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 "ROOT/RVersion.hxx"
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) {
269 char *_path = gSystem->ExpandPathName(name);
270 TString pathName = TString(_path);
271 delete[] _path;
272 if (!gSystem->AccessPathName(pathName)) {
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);
277 RooJSONFactoryWSTool tool(*get<RooWorkspace>());
280 if (!tool.importJSON(pathName.Data())) {
281 Error("xRooNode", "Error reading json workspace %s", name);
282 fComp.reset();
283 }
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>())) {
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
388 RooArgSet _allGlobs;
389 for (auto &[k, v] : GETWSSETS(_ws)) {
390 if (k == "globalObservables" || TString(k).EndsWith("_GlobalObservables")) {
391 for (auto &s : v) {
392 _allGlobs.add(*s);
393 s->setAttribute("obs");
394 s->setAttribute("global");
395 }
396 } else if (TString(k).EndsWith("_Observables")) {
397 const_cast<RooArgSet &>(v).setAttribAll("obs");
398 } else if (TString(k).EndsWith("_POI")) {
399 for (auto &s : v) {
400 s->setAttribute("poi");
401 auto _v = dynamic_cast<RooRealVar *>(s);
402 if (!_v)
403 continue;
404 // if (!_v->hasRange("physical")) {
405 // _v->setRange("physical", 0, std::numeric_limits<double>::infinity());
406 // // ensure range of poi is also straddling 0
407 // if (_v->getMin() >= 0)
408 // _v->setMin(-1e-5);
409 // }
410 }
411 } else if (TString(k).EndsWith("_NuisParams")) {
412 const_cast<RooArgSet &>(v).setAttribAll("np");
413 }
414 }
415 if (!_allGlobs.empty() && GETWSSETS(_ws).count("globalObservables") == 0) {
416 _ws->defineSet("globalObservables", _allGlobs);
417 }
418
419 // now check if any pars don't have errors defined (not same as error=0) ... if so, use the first pdf (if there is
420 // one) to try setting values from
421 if (!_ws->allPdfs().empty()) {
422 std::set<RooRealVar *> noErrorPars;
423 std::string parNames;
424 for (auto &p : np()) { // infer errors on all floating non-poi parameters
425 auto v = p->get<RooRealVar>();
426 if (!v)
427 continue;
428 if (!v->hasError()) {
429 noErrorPars.insert(v);
430 if (!parNames.empty())
431 parNames += ",";
432 parNames += v->GetName();
433 }
434 }
435 if (!noErrorPars.empty()) {
436 Warning("xRooNode",
437 "Inferring initial errors of %d parameters (%s%s) (give all nuisance parameters an error to avoid "
438 "this msg)",
439 int(noErrorPars.size()), (*noErrorPars.begin())->GetName(), (noErrorPars.size() > 1) ? ",..." : "");
440 if (gEnv->GetValue("XRooFit.SkipInitParErrorInference", false)) {
441 Warning("xRooNode", "Skipping because XRooFit.SkipInitParErrorInference=true. This is expert-only, you "
442 "should fix your workspaces!");
443 } else {
444 // get the first top-level pdf
445 browse();
446 for (auto &a : *this) {
447 if (noErrorPars.empty()) {
448 break;
449 }
450 if (a->fFolder == "!pdfs") {
451 try {
452 auto fr = a->floats().reduced(parNames).fitResult("prefit");
453 if (auto _fr = fr.get<RooFitResult>(); _fr) {
454 std::set<RooRealVar *> foundPars;
455 for (auto &v : noErrorPars) {
456 if (auto arg = dynamic_cast<RooRealVar *>(_fr->floatParsFinal().find(v->GetName()));
457 arg && arg->hasError()) {
458 v->setError(arg->getError());
459 foundPars.insert(v);
460 }
461 }
462 for (auto &v : foundPars) {
463 noErrorPars.erase(v);
464 }
465 }
466 } catch (...) {
467 }
468 }
469 }
470 }
471 }
472 }
473 }
474
475 if (strlen(GetTitle()) == 0) {
476 if (fComp) {
477 TNamed::SetTitle(fComp->GetTitle());
478 } else {
480 }
481 }
482}
483
484xRooNode::xRooNode(const TObject &comp, const std::shared_ptr<xRooNode> &parent)
485 : xRooNode(/*[](const TObject& c) {
486c.InheritsFrom("RooAbsArg");
487if (s) {
488return (s->getStringAttribute("alias")) ? s->getStringAttribute("alias") : c.GetName();
489}
490return c.GetName();
491}(comp)*/
492 (comp.InheritsFrom("RooAbsArg") && dynamic_cast<const RooAbsArg *>(&comp)->getStringAttribute("alias"))
493 ? dynamic_cast<const RooAbsArg *>(&comp)->getStringAttribute("alias")
494 : comp.GetName(),
495 std::shared_ptr<TObject>(const_cast<TObject *>(&comp), [](TObject *) {}), parent)
496{
497}
498
499xRooNode::xRooNode(const std::shared_ptr<TObject> &comp, const std::shared_ptr<xRooNode> &parent)
500 : xRooNode(
501 [&]() {
502 if (auto a = std::dynamic_pointer_cast<RooAbsArg>(comp); a && a->getStringAttribute("alias"))
503 return a->getStringAttribute("alias");
504 if (comp)
505 return comp->GetName();
506 return "";
507 }(),
508 comp, parent)
509{
510}
511
513
514void xRooNode::_SetAttribute_(const char *name, const char *value)
515{
516 TString v(value);
517 v.ToUpper();
518 bool isBool = (v == "TRUE" || v == "FALSE");
519 if (auto a = get<RooAbsArg>(); a) {
520 if (value == nullptr || v == "NULLPTR") {
521 if (a->getAttribute(name))
522 a->setAttribute(name, false);
523 else if (a->getStringAttribute(name))
524 a->setStringAttribute(name, nullptr);
525 } else {
526 if (isBool)
527 a->setAttribute(name, (v == "TRUE"));
528 else
529 a->setStringAttribute(name, value);
530 }
531 } else {
532 RooArgList l = argList();
533 for (auto a2 : l) {
534 xRooNode(*a2)._SetAttribute_(name, value);
535 }
536 }
537 // should update this node's state in any browsers ...
538 for (auto a : *gROOT->GetListOfBrowsers()) {
539 TBrowser *b = dynamic_cast<TBrowser *>(a);
540 if (b && GetTreeItem(b)) {
541 if (auto bi = dynamic_cast<TRootBrowser *>(b->GetBrowserImp())) {
542 if (auto fb = dynamic_cast<TGFileBrowser *>(bi->GetActBrowser())) {
543 fb->DoubleClicked(GetTreeItem(b), 0);
544 }
545 }
546 }
547 }
548}
549
550void xRooNode::Checked(TObject *obj, bool val)
551{
552 if (obj != this)
553 return;
554
555 // cycle through states:
556 // unhidden and selected: tick, no uline
557 // hidden and unselected: notick, uline
558 // unhidden and unselected: tick, uline
559 if (auto o = get<RooAbsReal>(); o) {
560 if (o->isSelectedComp() && !val) {
561 // deselecting and hiding
562 o->selectComp(val);
563 o->setAttribute("hidden");
564 } else if (!o->isSelectedComp() && !val) {
565 // selecting
566 o->selectComp(!val);
567 } else if (val) {
568 // unhiding but keeping unselected
569 o->setAttribute("hidden", false);
570 }
571 auto item = GetTreeItem(nullptr);
572 item->CheckItem(!o->getAttribute("hidden"));
573 if (o->isSelectedComp()) {
574 item->ClearColor();
575 } else {
576 item->SetColor(kGray);
577 }
578 return;
579 }
580
581 if (auto o = get(); o) {
582 // if (o->TestBit(1<<20)==val) return; // do nothing
583 o->SetBit(1 << 20, val); // TODO: check is 20th bit ok to play with?
584 if (auto fr = get<RooFitResult>(); fr) {
585 if (auto _ws = ws(); _ws) {
586 if (val) {
587 // ensure fit result is in genericObjects list or snapshots ... if not, add a copy ...
588 if (fr->numStatusHistory() && !_ws->genobj(fr->GetName())) {
589 _ws->import(*fr);
590 if (auto wfr = dynamic_cast<RooFitResult *>(_ws->genobj(fr->GetName()))) {
591 fr = wfr;
592 }
593 }
594 RooArgSet _allVars = _ws->allVars();
595 _allVars = fr->floatParsFinal();
596 _allVars = fr->constPars();
597 for (auto &i : fr->floatParsInit()) {
598 auto v = dynamic_cast<RooRealVar *>(_allVars.find(i->GetName()));
599 if (v)
600 v->setStringAttribute("initVal", TString::Format("%f", dynamic_cast<RooRealVar *>(i)->getVal()));
601 }
602 // uncheck all other fit results
603 for (auto oo : _ws->allGenericObjects()) {
604 if (auto ffr = dynamic_cast<RooFitResult *>(oo); ffr && ffr != fr) {
605 ffr->ResetBit(1 << 20);
606 }
607 }
608 } else
609 _ws->allVars() = fr->floatParsInit();
610 }
611
612 TBrowser *b = nullptr;
613 for (auto a : *gROOT->GetListOfBrowsers()) {
614 b = dynamic_cast<TBrowser *>(a);
615 if (b && GetTreeItem(b)) {
616 break;
617 }
618 }
619 if (b) {
620 auto p = GetTreeItem(b);
621
622 if (p) {
623 // update check marks on siblings
624 if (auto first = p->GetParent()->GetFirstChild()) {
625 do {
626 if (first->HasCheckBox()) {
627 auto _obj = static_cast<xRooNode *>(first->GetUserData());
628 first->CheckItem(_obj->get() && _obj->get()->TestBit(1 << 20));
629 }
630 } while ((first = first->GetNextSibling()));
631 }
632 }
633
634 // also since const status of pars could have changed, refresh all 'poi' and 'np' open nodes
635 if (auto bi = dynamic_cast<TRootBrowser *>(b->GetBrowserImp())) {
636 if (auto fb = dynamic_cast<TGFileBrowser *>(bi->GetActBrowser())) {
637 while (p) {
638 if (TString(p->GetText()).BeginsWith("RooWorkspace::")) {
639 std::function<void(TGListTreeItem *)> rfunc;
640
641 rfunc = [&](TGListTreeItem *i) {
642 if (auto first = i->GetFirstChild()) {
643 do {
644 if (first->IsOpen() &&
645 (TString(first->GetText()) == "poi" || TString(first->GetText()) == "np")) {
646 fb->DoubleClicked(first, 0);
647 } else
648 rfunc(first);
649 } while ((first = first->GetNextSibling()));
650 }
651 };
652 rfunc(p);
653 break;
654 } else {
655 p = p->GetParent();
656 }
657 }
658 }
659 }
660 }
661 }
662 }
663}
664
666{
667 static bool blockBrowse = false;
668 if (blockBrowse)
669 return;
670 if (b == nullptr) {
671 auto b2 = dynamic_cast<TBrowser *>(gROOT->GetListOfBrowsers()->Last());
672 if (!b2 || !b2->GetBrowserImp()) { // no browser imp if browser was closed
673 blockBrowse = true;
674 gEnv->SetValue("X11.UseXft", "no"); // for faster x11
675 gEnv->SetValue("X11.Sync", "no");
676 gEnv->SetValue("X11.FindBestVisual", "no");
677 gEnv->SetValue("Browser.Name", "TRootBrowser"); // forces classic root browser (in 6.26 onwards)
678 gEnv->SetValue("Canvas.Name", "TRootCanvas");
679 b2 = new TBrowser("nodeBrowser", this, "RooFit Browser");
680 blockBrowse = false;
681 } else if (strcmp(b2->GetName(), "nodeBrowser") == 0) {
682 blockBrowse = true;
683 b2->BrowseObject(this);
684 blockBrowse = false;
685 } else {
686 auto _b = dynamic_cast<TGFileBrowser *>(GETACTBROWSER(dynamic_cast<TRootBrowser *>(b2->GetBrowserImp())));
687 if (_b) {
688 _b->AddFSDirectory("Workspaces", nullptr, "SetRootDir");
689 _b->GotoDir(nullptr);
690 _b->Add(this, GetName());
691 }
692 }
693 return;
694 }
695
696 if (auto item = GetTreeItem(b); item) {
697 if (!item->IsOpen() && IsFolder())
698 return; // no need to rebrowse if closing
699 // update check marks on any child items
700 if (auto first = item->GetFirstChild()) {
701 do {
702 if (first->HasCheckBox()) {
703 auto _obj = static_cast<xRooNode *>(first->GetUserData());
704 first->CheckItem(_obj->get() &&
705 (_obj->get()->TestBit(1 << 20) ||
706 (_obj->get<RooAbsArg>() && !_obj->get<RooAbsArg>()->getAttribute("hidden"))));
707 }
708 } while ((first = first->GetNextSibling()));
709 }
710 }
711
712 browse();
713
714 // for top-level pdfs default to having the .vars browsable too
715 if (get<RooAbsPdf>() && fFolder == "!pdfs" && !_IsShowVars_()) {
716 fBrowsables.push_back(std::make_shared<xRooNode>(vars()));
717 }
718
719 if (auto _fr = get<RooFitResult>(); _fr && fBrowsables.empty()) {
720 // have some common drawing options
721 fBrowsables.push_back(std::make_shared<xRooNode>(".Draw(\"pull\")", nullptr, *this));
722 fBrowsables.push_back(std::make_shared<xRooNode>(".Draw(\"corr10colztext\")", nullptr, *this));
723 if (std::unique_ptr<RooAbsCollection>(_fr->floatParsFinal().selectByAttrib("poi", true))->size() == 1) {
724 fBrowsables.push_back(std::make_shared<xRooNode>(".Draw(\"impact\")", nullptr, *this));
725 }
726 }
727
728 if (empty() && fBrowsables.empty()) {
729 try {
730 if (auto s = get<TStyle>()) {
731 s->SetFillAttributes();
732 if (auto ed = dynamic_cast<TGedEditor *>(TVirtualPadEditor::GetPadEditor())) {
733 ed->SetModel(gPad, s, kButton1Down, true);
734 }
735 } else if (TString(GetName()).BeginsWith(".Draw(\"") && fParent) {
736 fParent->Draw(TString(TString(GetName())(7, strlen(GetName()) - 9)) + b->GetDrawOption());
737 } else {
738 Draw(b->GetDrawOption());
739 }
740 } catch (const std::exception &e) {
741 new TGMsgBox(
742 gClient->GetRoot(),
743 (gROOT->GetListOfBrowsers()->At(0))
744 ? dynamic_cast<TGWindow *>(static_cast<TBrowser *>(gROOT->GetListOfBrowsers()->At(0))->GetBrowserImp())
745 : gClient->GetRoot(),
746 "Exception", e.what(),
747 kMBIconExclamation); // deletes self on dismiss?
748 }
749 }
750
751 bool hasFolders = false;
752 if (strlen(GetName()) > 0 && GetName()[0] != '!') { // folders don't have folders
753 for (auto &c : *this) {
754 if (!c->fFolder.empty()) {
755 hasFolders = true;
756 break;
757 }
758 }
759 }
760 // auto _ws = get<RooWorkspace>();
761 if (/*_ws*/ hasFolders) {
762 // organize in folders
763 auto _folders = find(".folders");
764 if (!_folders) {
765 _folders = emplace_back(std::make_shared<xRooNode>(".folders", nullptr, *this));
766 }
767 // ensure entry in folders for every folder type ...
768 for (auto &v : *this) {
769 if (!v->fFolder.empty() && !_folders->find(v->fFolder, false)) {
770 _folders->emplace_back(std::make_shared<xRooNode>(v->fFolder.c_str(), nullptr, *this));
771 }
772 }
773 // now just add all the folders
774 for (auto &v : *_folders) {
775 TString _name = v->GetName();
776 if (_name.BeginsWith('!'))
777 _name = _name(1, _name.Length()); // strip ! from display
778 b->Add(v.get(), _name);
779 }
780 }
781
782 for (auto &v : *this) {
783 if (hasFolders && !v->fFolder.empty())
784 continue; // in the folders
785 if (strcmp(v->GetName(), ".folders") == 0)
786 continue; // never 'browse' the folders property
787 auto _fr = v->get<RooFitResult>();
788 int _checked = (v->get<RooAbsData>() || _fr) ? v->get()->TestBit(1 << 20) : -1;
789 if (_fr && ((_fr->status() == 0 && _fr->numStatusHistory() == 0) || (_fr->floatParsFinal().empty()))) {
790 // this is a "PARTIAL" fit result ... don't allow it to be selected
791 _checked = -1;
792 }
793 if (v->get<RooAbsPdf>() && get<RooSimultaneous>())
794 _checked = !v->get<RooAbsArg>()->getAttribute("hidden");
795 TString _name = v->GetName();
796 if (v->get() && _name.BeginsWith(TString(v->get()->ClassName()) + "::")) {
797 _name = _name(strlen(v->get()->ClassName()) + 2, _name.Length());
798 }
799 if (_name.BeginsWith(".")) {
800 // property node -- display the name of the contained object
801 if (v->get()) {
802 _name = TString::Format("%s: %s::%s", _name.Data(), v->get()->ClassName(),
803 (v->get<RooAbsArg>() && v->get<RooAbsArg>()->getStringAttribute("alias"))
804 ? v->get<RooAbsArg>()->getStringAttribute("alias")
805 : v->get()->GetName());
806 }
807 } else if (v->get() && !v->get<TFile>() && !TString(v->GetName()).BeginsWith('/'))
808 _name = TString::Format("%s::%s", v->get()->ClassName(), _name.Data());
809 if (auto _type = v->GetNodeType(); strlen(_type)) {
810 // decided not to show const values until figure out how to update if value changes
811 /*if (TString(_type)=="Const") _name += TString::Format(" [%s=%g]",_type,v->get<RooConstVar>()->getVal());
812 else*/
813 _name += TString::Format(" [%s]", _type);
814 }
815 if (auto fv = v->get<RooFormulaVar>()) {
816 TString formu = TString::Format(" [%s]", fv->expression());
817 for (size_t i = 0; i < fv->dependents().size(); i++) {
818 formu.ReplaceAll(TString::Format("x[%zu]", i), fv->dependents()[i].GetName());
819 }
820 _name += formu;
821 } else if (auto gv = v->get<RooGenericPdf>()) {
822 TString formu = TString::Format(" [%s]", gv->expression());
823 for (size_t i = 0; i < gv->dependents().size(); i++) {
824 formu.ReplaceAll(TString::Format("x[%zu]", i), gv->dependents()[i].GetName());
825 }
826 _name += formu;
827 } else if (auto pi = v->get<PiecewiseInterpolation>()) {
828 // check if all interpCodes are the same.
829 std::set<int> interpCodes;
830 for (auto &c : pi->interpolationCodes())
831 interpCodes.insert(c);
832 if (interpCodes.size() == 1) {
833 _name += TString::Format(" [InterpCode=%d]", *interpCodes.begin());
834 }
835 } else if (auto fiv = v->get<RooStats::HistFactory::FlexibleInterpVar>()) {
836 // check if all interpCodes are the same.
837 std::set<int> interpCodes;
838 for (auto &c : fiv->interpolationCodes())
839 interpCodes.insert(c == 4 ? 5 : c); // in definition of FlexibleInterpVar 4 gets replaced with 5
840 if (interpCodes.size() == 1) {
841 _name += TString::Format(" [InterpCode=%d]", *interpCodes.begin());
842 }
843 }
844 // tool tip defaults to displaying name and title, so temporarily set name to obj name if has one
845 // and set title to the object type
846 TString nameSave(v->TNamed::GetName());
847 TString titleSave(v->TNamed::GetTitle());
848 if (auto o = v->get(); o)
849 v->TNamed::SetNameTitle(o->GetName(), o->ClassName());
850 b->Add(v.get(), _name, _checked);
851 if (auto o = v->get(); o)
852 v->TNamed::SetNameTitle(nameSave, titleSave);
853 if (_checked != -1) {
854 dynamic_cast<TQObject *>(b->GetBrowserImp())
855 ->Connect("Checked(TObject *, bool)", ClassName(), v.get(), "Checked(TObject *, bool)");
856 }
857 if (_fr) {
858 if (_fr->status() || _fr->covQual() != 3) { // snapshots or bad fits
859 v->GetTreeItem(b)->SetColor((_fr->numStatusHistory() && !_fr->floatParsFinal().empty()) ? kRed : kBlue);
860 } else if (_fr->numStatusHistory() == 0) { // partial fit result ..
861 v->GetTreeItem(b)->SetColor(kGray);
862 }
863 }
864 if ((v->fFolder == "!np" || v->fFolder == "!poi")) {
865 if (v->get<RooAbsArg>()->getAttribute("Constant")) {
866 v->GetTreeItem(b)->SetColor(kGray);
867 } else
868 v->GetTreeItem(b)->ClearColor();
869 }
870 if (auto _htr = v->get<RooStats::HypoTestResult>(); _htr) {
871 // check for fit statuses
872 if (auto fits = _htr->GetFitInfo()) {
873 for (int i = 0; i < fits->numEntries(); i++) {
874 // if any fit (other than a genFit) is bad, flag point as bad
875 if (fits->get(i)->getCatIndex("type") != 5 && fits->get(i)->getRealValue("status") != 0) {
876 v->GetTreeItem(b)->SetColor(kRed);
877 break;
878 }
879 }
880 } else {
881 v->GetTreeItem(b)->SetColor(kBlue); // unknown fit status
882 }
883 }
884
885 // v.fBrowsers.insert(b);
886 }
887
888 // for pdfs, check for datasets too and add to list
889 /*if (get<RooAbsPdf>()) {
890 auto dsets = datasets();
891 if (!dsets.empty()) {
892 // check if already have .datasets() in browsables
893 bool found(false);
894 for(auto& p : fBrowsables) {
895 if (TString(p->GetName())==".datasets()") {found=true;
896 // add
897 break;
898 }
899 }
900 if (!found) {
901 fBrowsables.push_back(std::make_shared<xRooNode>(dsets));
902 }
903 }
904 }*/
905 // browse the browsables too
906 for (auto &v : fBrowsables) {
907 TString _name = v->GetName();
908 if (_name == ".memory")
909 continue; // hide the memory from browsing, if put in browsables
910 TString nameSave(v->TNamed::GetName());
911 TString titleSave(v->TNamed::GetTitle());
912 if (auto o = v->get(); o)
913 v->TNamed::SetNameTitle(o->GetName(), o->ClassName());
914 b->Add(v.get(), _name, -1);
915 if (auto o = v->get(); o)
916 v->TNamed::SetNameTitle(nameSave, titleSave);
917 }
918
919 b->SetSelected(this);
920}
921
923{
924 if (!set) {
925 // can't remove as causes a crash, need to remove from the browser first
926 /*for(auto itr = fBrowsables.begin(); itr != fBrowsables.end(); ++itr) {
927 if (strcmp((*itr)->GetName(),".vars")==0) {
928 fBrowsables.erase(itr);
929 }
930 }*/
931 } else {
932 auto v = std::make_shared<xRooNode>(vars());
933 fBrowsables.push_back(v);
934 if (auto l = GetListTree(nullptr)) {
935 l->AddItem(GetTreeItem(nullptr), v->GetName(), v.get());
936 }
937 }
938}
939
941{
942 for (auto &b : fBrowsables) {
943 if (strcmp(b->GetName(), ".vars") == 0)
944 return true;
945 }
946 return false;
947}
948
950{
951 if (strlen(GetName()) > 0 && GetName()[0] == '!')
952 return true;
953 if (strlen(GetName()) > 0 && GetName()[0] == '.' && !(TString(GetName()).BeginsWith(".Draw(\"")))
954 return true;
955 if (empty())
956 const_cast<xRooNode *>(this)->browse();
957 return !empty();
958}
959
960class Axis2 : public TAxis {
961
962public:
963 using TAxis::TAxis;
964 double GetBinWidth(Int_t bin) const override
965 {
966 if (auto v = var(); v)
967 return v->getBinWidth(bin - 1, GetName());
968 return 1;
969 }
970 double GetBinLowEdge(Int_t bin) const override
971 {
972 if (auto v = rvar(); v) {
973 return (bin == v->getBinning(GetName()).numBins() + 1) ? v->getBinning(GetName()).binHigh(bin - 2)
974 : v->getBinning(GetName()).binLow(bin - 1);
975 }
976 return bin - 1;
977 }
978 double GetBinUpEdge(Int_t bin) const override
979 {
980 if (auto v = rvar(); v)
981 return (bin == 0) ? v->getBinning(GetName()).binLow(bin) : v->getBinning(GetName()).binHigh(bin - 1);
982 return bin;
983 }
984
985 const char *GetTitle() const override
986 {
987 return (binning() && strlen(binning()->GetTitle())) ? binning()->GetTitle() : GetParent()->GetTitle();
988 }
989 void SetTitle(const char *title) override
990 {
991 if (binning()) {
992 const_cast<RooAbsBinning *>(binning())->SetTitle(title);
993 } else {
994 dynamic_cast<TNamed *>(GetParent())->SetTitle(title);
995 }
996 }
997
998 void Set(Int_t nbins, const double *xbins) override
999 {
1000 if (auto v = dynamic_cast<RooRealVar *>(rvar()))
1001 v->setBinning(RooBinning(nbins, xbins), GetName());
1002 TAxis::Set(nbins, xbins);
1003 }
1004 void Set(Int_t nbins, const float *xbins) override
1005 {
1006 std::vector<double> bins(nbins + 1);
1007 for (int i = 0; i <= nbins; i++)
1008 bins.at(i) = xbins[i];
1009 return Set(nbins, &bins[0]);
1010 }
1011 void Set(Int_t nbins, double xmin, double xmax) override
1012 {
1013 if (auto v = dynamic_cast<RooRealVar *>(rvar()))
1014 v->setBinning(RooUniformBinning(xmin, xmax, nbins), GetName());
1015 TAxis::Set(nbins, xmin, xmax);
1016 }
1017
1018 const RooAbsBinning *binning() const { return var()->getBinningPtr(GetName()); }
1019
1020 Int_t FindFixBin(const char *label) const override { return TAxis::FindFixBin(label); }
1021 Int_t FindFixBin(double x) const override { return (binning()) ? (binning()->binNumber(x) + 1) : x; }
1022
1023private:
1024 RooAbsLValue *var() const { return dynamic_cast<RooAbsLValue *>(GetParent()); }
1025 RooAbsRealLValue *rvar() const { return dynamic_cast<RooAbsRealLValue *>(GetParent()); }
1026};
1027
1028std::shared_ptr<TObject> xRooNode::getObject(const std::string &name, const std::string &type) const
1029{
1030 // if (fParent) return fParent->getObject(name);
1031
1032 if (auto _owned = find(".memory"); _owned) {
1033 for (auto &o : *_owned) {
1034 if (name == o->GetName()) {
1035 if (type.empty() || o->get()->InheritsFrom(type.c_str()))
1036 return o->fComp;
1037 }
1038 }
1039 }
1040
1041 // see if have a provider
1042 auto _provider = fProvider;
1043 auto _parent = fParent;
1044 while (!_provider && _parent) {
1045 _provider = _parent->fProvider;
1046 _parent = _parent->fParent;
1047 }
1048 if (_provider)
1049 return _provider->getObject(name, type);
1050
1051 if (ws()) {
1052 std::shared_ptr<TObject> out;
1053 if (auto arg = ws()->arg(name.c_str()); arg) {
1054 auto _tmp = std::shared_ptr<TObject>(arg, [](TObject *) {});
1055 if (!type.empty() && arg->InheritsFrom(type.c_str()))
1056 return _tmp;
1057 if (!out)
1058 out = _tmp;
1059 }
1060 if (auto arg = ws()->data(name.c_str()); arg) {
1061 auto _tmp = std::shared_ptr<TObject>(arg, [](TObject *) {});
1062 if (!type.empty() && arg->InheritsFrom(type.c_str()))
1063 return _tmp;
1064 if (!out)
1065 out = _tmp;
1066 }
1067 if (auto arg = ws()->genobj(name.c_str()); arg) {
1068 auto _tmp = std::shared_ptr<TObject>(arg, [](TObject *) {});
1069 if (!type.empty() && arg->InheritsFrom(type.c_str()))
1070 return _tmp;
1071 if (!out)
1072 out = _tmp;
1073 }
1074 if (auto arg = ws()->embeddedData(name.c_str()); arg) {
1075 auto _tmp = std::shared_ptr<TObject>(arg, [](TObject *) {});
1076 if (!type.empty() && arg->InheritsFrom(type.c_str()))
1077 return _tmp;
1078 if (!out)
1079 out = _tmp;
1080 }
1081 if (auto arg = GETWSSNAPSHOTS(ws()).find(name.c_str()); arg) {
1082 auto _tmp = std::shared_ptr<TObject>(arg, [](TObject *) {});
1083 if (!type.empty() && arg->InheritsFrom(type.c_str()))
1084 return _tmp;
1085 if (!out)
1086 out = _tmp;
1087 }
1088 return out;
1089 }
1090 if (auto arg = get<RooAbsArg>()) {
1091 // can try all nodes
1092 RooArgSet nodes;
1093 arg->treeNodeServerList(&nodes);
1094 if (auto server = nodes.find(name.c_str())) {
1095 return std::shared_ptr<TObject>(server, [](TObject *) {});
1096 }
1097 }
1098 return nullptr;
1099}
1100
1102{
1103 if (fXAxis) {
1104 // check if num bins needs update or not
1105 if (auto cat = dynamic_cast<RooAbsCategory *>(fXAxis->GetParent());
1106 cat && cat->numTypes() != fXAxis->GetNbins()) {
1107 fXAxis.reset();
1108 } else {
1109 return fXAxis.get();
1110 }
1111 }
1112 RooAbsLValue *x = nullptr;
1113 if (auto a = get<RooAbsArg>(); a && a->isFundamental())
1114 x = dynamic_cast<RooAbsLValue *>(a); // self-axis
1115
1116 auto _parentX = (!x && fParent && !fParent->get<RooSimultaneous>()) ? fParent->GetXaxis() : nullptr;
1117
1118 auto o = get<RooAbsReal>();
1119 if (!o)
1120 return _parentX;
1121
1122 if (auto xName = o->getStringAttribute("xvar"); xName) {
1123 x = dynamic_cast<RooAbsLValue *>(getObject(xName).get());
1124 }
1125
1126 // if xvar has become set equal to an arg and this is a pdf, we will allow a do-over
1127 if (!x) {
1128 // need to choose from dependent fundamentals, in following order:
1129 // parentX (if not a glob), robs, globs, vars, args
1130
1131 if (_parentX && !dynamic_cast<RooAbsArg *>(_parentX->GetParent())->getAttribute("global") &&
1132 (o->dependsOn(*dynamic_cast<RooAbsArg *>(_parentX->GetParent())) || vars().empty())) {
1133 x = dynamic_cast<RooAbsLValue *>(_parentX->GetParent());
1134 } else if (auto _obs = obs(); !_obs.empty()) {
1135 for (auto &v : _obs) {
1136 if (!v->get<RooAbsArg>()->getAttribute("global")) {
1137 x = v->get<RooAbsLValue>();
1138 if (x)
1139 break;
1140 } else if (!x) {
1141 x = v->get<RooAbsLValue>();
1142 }
1143 }
1144 } else if (auto _pars = pars(); !_pars.empty()) {
1145 for (auto &v : _pars) {
1146 if (!v->get<RooAbsArg>()->getAttribute("Constant")) {
1147 x = v->get<RooAbsLValue>();
1148 if (x)
1149 break;
1150 } else if (!x) {
1151 x = v->get<RooAbsLValue>();
1152 }
1153 }
1154 }
1155
1156 if (!x) {
1157 return nullptr;
1158 }
1159 }
1160
1161 /* no longer 'remembering' xvars when call GetXaxis(), as was causing incorrect obs to go onto node
1162 * when e.g. adding histogram with bin errors to a channel - the statFactor and constraint terms were
1163 * created first, causing the channel's xaxis to become equal to a globs at some point because of a GetXaxis call
1164 * Alternatively we could clear the xvar attribute of all client nodes whenever we 'modify' something
1165 * Should do that if processing large workspaces becomes slow
1166 if (o != dynamic_cast<TObject *>(x)) {
1167 o->setStringAttribute("xvar", dynamic_cast<TObject *>(x)->GetName());
1168 } */
1169
1170 // decide binning to use
1171 TString binningName = o->getStringAttribute("binning");
1172 auto _bnames = x->getBinningNames();
1173 bool hasBinning = false;
1174 for (auto &b : _bnames) {
1175 if (b == binningName) {
1176 hasBinning = true;
1177 break;
1178 }
1179 }
1180 if (!hasBinning) {
1181 // doesn't have binning, so clear binning attribute
1182 // this can happen after Combine of models because binning don't get combined yet (should fix this)
1183 Warning("GetXaxis", "Binning %s not defined on %s - clearing", binningName.Data(),
1184 dynamic_cast<TObject *>(x)->GetName());
1185 o->setStringAttribute("binning", nullptr);
1186 binningName = "";
1187 }
1188
1189 if (binningName == "" && o != dynamic_cast<TObject *>(x)) {
1190 // has var has a binning matching this nodes name then use that
1191 auto __bnames = x->getBinningNames();
1192 for (auto &b : __bnames) {
1193 if (b == GetName())
1194 binningName = GetName();
1195 if (b == o->GetName()) {
1196 binningName = o->GetName();
1197 break;
1198 } // best match
1199 }
1200 if (binningName == "") {
1201 // if we are binned in this var then will define that as a binning
1202 if (/*o->isBinnedDistribution(*dynamic_cast<RooAbsArg *>(x))*/
1203 auto bins = _or_func(
1204 /*o->plotSamplingHint(*dynamic_cast<RooAbsRealLValue
1205 *>(x),-std::numeric_limits<double>::infinity(),std::numeric_limits<double>::infinity())*/
1206 (std::list<double> *)(nullptr),
1207 (dynamic_cast<RooAbsRealLValue *>(x))
1208 ? o->binBoundaries(*dynamic_cast<RooAbsRealLValue *>(x), -std::numeric_limits<double>::infinity(),
1209 std::numeric_limits<double>::infinity())
1210 : nullptr);
1211 bins) {
1212 std::vector<double> _bins;
1213 for (auto &b : *bins) {
1214 if (_bins.empty() || std::abs(_bins.back() - b) > 1e-5 * _bins.back())
1215 _bins.push_back(b);
1216 }
1217 fXAxis = std::make_shared<Axis2>(_bins.size() - 1, &_bins[0]);
1218 // add this binning to the var to avoid recalling ...
1219 if (auto _v = dynamic_cast<RooRealVar *>(x); _v) {
1220 _v->setBinning(RooBinning(_bins.size() - 1, &_bins[0], o->GetName()), o->GetName());
1221 _v->getBinning(o->GetName())
1222 .SetTitle(""); // indicates to use the current var title when building histograms etc
1223 //_v->getBinning(o->GetName()).SetTitle(strlen(dynamic_cast<TObject*>(x)->GetTitle()) ?
1224 // dynamic_cast<TObject*>(x)->GetTitle() : dynamic_cast<TObject*>(x)->GetName());
1225 }
1226 binningName = o->GetName();
1227 delete bins;
1228 } else if (_parentX) {
1229 // use parent axis binning if defined, otherwise we will default
1230 binningName = _parentX->GetName();
1231 }
1232 }
1233 }
1234
1235 if (!fXAxis) {
1236 if (auto r = dynamic_cast<RooAbsRealLValue *>(x); r) {
1237 if (r->getBinning(binningName).isUniform()) {
1238 fXAxis = std::make_shared<Axis2>(x->numBins(binningName), r->getMin(binningName), r->getMax(binningName));
1239 } else {
1240 fXAxis = std::make_shared<Axis2>(x->numBins(binningName), r->getBinning(binningName).array());
1241 }
1242 } else if (auto cat = dynamic_cast<RooCategory *>(x)) {
1243 std::vector<double> bins = {};
1244 for (int i = 0; i <= x->numBins(binningName); i++)
1245 bins.push_back(i);
1246 fXAxis = std::make_shared<Axis2>(x->numBins(binningName), &bins[0]);
1247 // TODO have to load current state of bin labels if was a category (sadly not a virtual method)
1248 int i = 1;
1249 std::map<int, std::string> cats; // fill into a map to preserve index ordering
1250 for (auto &c : *cat) {
1251 if (cat->isStateInRange(binningName, c.first.c_str())) {
1252 cats[c.second] = c.first;
1253 }
1254 }
1255 for (auto &[_, label] : cats) {
1256 fXAxis->SetBinLabel(i++, label.c_str());
1257 }
1258 }
1259 }
1260
1261 fXAxis->SetName(binningName);
1262 fXAxis->SetParent(dynamic_cast<TObject *>(x));
1263 return fXAxis.get();
1264}
1265
1266const char *xRooNode::GetIconName() const
1267{
1268 if (auto o = get(); o) {
1269 if (o->InheritsFrom("RooWorkspace"))
1270 return "TFile";
1271 if (o->InheritsFrom("RooAbsData"))
1272 return "TProfile";
1273 if (o->InheritsFrom("RooSimultaneous"))
1274 return "TH3D";
1275
1276 if (o->InheritsFrom("RooProdPdf"))
1277 return "a.C"; // or nullptr for folder
1278 if (o->InheritsFrom("RooRealSumPdf") || o->InheritsFrom("RooAddPdf"))
1279 return "TH2D";
1280 // if(o->InheritsFrom("RooProduct")) return "TH1D";
1281 if (o->InheritsFrom("RooFitResult")) {
1282 if (!gClient->GetMimeTypeList()->GetIcon("xRooFitRooFitResult", true)) {
1283 gClient->GetMimeTypeList()->AddType("xRooFitRooFitResult", "xRooFitRooFitResult", "package.xpm",
1284 "package.xpm", "->Browse()");
1285 }
1286 return "xRooFitRooFitResult";
1287 }
1288 if (o->InheritsFrom("RooRealVar") || o->InheritsFrom("RooCategory")) {
1289 if (get<RooAbsArg>()->getAttribute("obs")) {
1290 if (!gClient->GetMimeTypeList()->GetIcon("xRooFitObs", true)) {
1291 gClient->GetMimeTypeList()->AddType("xRooFitObs", "xRooFitObs", "x_pic.xpm", "x_pic.xpm", "->Browse()");
1292 }
1293 if (!gClient->GetMimeTypeList()->GetIcon("xRooFitGlobs", true)) {
1294 gClient->GetMimeTypeList()->AddType("xRooFitGlobs", "xRooFitGlobs", "z_pic.xpm", "z_pic.xpm",
1295 "->Browse()");
1296 }
1297 return (get<RooAbsArg>()->getAttribute("global") ? "xRooFitGlobs" : "xRooFitObs");
1298 }
1299 return "TLeaf";
1300 }
1301 if (o->InheritsFrom("TStyle")) {
1302 if (!gClient->GetMimeTypeList()->GetIcon("xRooFitTStyle", true)) {
1303 gClient->GetMimeTypeList()->AddType("xRooFitTStyle", "xRooFitTStyle", "bld_colorselect.xpm",
1304 "bld_colorselect.xpm", "->Browse()");
1305 }
1306 return "xRooFitTStyle";
1307 }
1308 if (o->InheritsFrom("RooConstVar")) {
1309 /*if (!gClient->GetMimeTypeList()->GetIcon("xRooFitRooConstVar",true)) {
1310 gClient->GetMimeTypeList()->AddType("xRooFitRooConstVar", "xRooFitRooConstVar", "stop_t.xpm", "stop_t.xpm",
1311 "->Browse()");
1312 }
1313 return "xRooFitRooConstVar";*/
1314 return "TMethodBrowsable-leaf";
1315 }
1316 if (o->InheritsFrom("RooStats::HypoTestInverterResult")) {
1317 if (!gClient->GetMimeTypeList()->GetIcon("xRooFitScanStyle", true)) {
1318 gClient->GetMimeTypeList()->AddType("xRooFitScanStyle", "xRooFitScanStyle", "f2_s.xpm", "f2_s.xpm",
1319 "->Browse()");
1320 }
1321 return "xRooFitScanStyle";
1322 }
1323 if (o->InheritsFrom("RooStats::HypoTestResult")) {
1324 if (!gClient->GetMimeTypeList()->GetIcon("xRooFitTestStyle", true)) {
1325 gClient->GetMimeTypeList()->AddType("xRooFitTestStyle", "xRooFitTestStyle", "diamond.xpm", "diamond.xpm",
1326 "->Browse()");
1327 }
1328 return "xRooFitTestStyle";
1329 }
1330 if (o->InheritsFrom("RooStats::HistFactory::FlexibleInterpVar"))
1331 return "TBranchElement-folder";
1332 if (o->InheritsFrom("RooAbsPdf")) {
1333 if (!gClient->GetMimeTypeList()->GetIcon("xRooFitPDFStyle", true)) {
1334 gClient->GetMimeTypeList()->AddType("xRooFitPDFStyle", "xRooFitPDFStyle", "pdf.xpm", "pdf.xpm",
1335 "->Browse()");
1336 }
1337 return "xRooFitPDFStyle";
1338 }
1339 if (o->InheritsFrom("RooStats::ModelConfig")) {
1340 if (!gClient->GetMimeTypeList()->GetIcon("xRooFitMCStyle", true)) {
1341 gClient->GetMimeTypeList()->AddType("xRooFitMCStyle", "xRooFitMCStyle", "app_t.xpm", "app_t.xpm",
1342 "->Browse()");
1343 }
1344 return "xRooFitMCStyle";
1345 }
1346 if (auto a = dynamic_cast<RooAbsReal *>(o); a) {
1347 if (auto _ax = GetXaxis();
1348 _ax && (a->isBinnedDistribution(*dynamic_cast<RooAbsArg *>(_ax->GetParent())) ||
1349 (dynamic_cast<RooAbsRealLValue *>(_ax->GetParent()) &&
1350 std::unique_ptr<std::list<double>>(a->binBoundaries(
1351 *dynamic_cast<RooAbsRealLValue *>(_ax->GetParent()), -std::numeric_limits<double>::infinity(),
1352 std::numeric_limits<double>::infinity()))))) {
1353 return "TH1D";
1354 }
1355 return "TF1";
1356 }
1357 return o->ClassName();
1358 }
1359 if (!IsFolder()) {
1360 return "Unknown";
1361 }
1362 return nullptr;
1363}
1364
1365const char *xRooNode::GetNodeType() const
1366{
1367 if (auto rrs = get<RooRealSumPdf>(); rrs) {
1368 // if is BinnedLikelihood show that option
1369 if (rrs->getAttribute("BinnedLikelihood"))
1370 return "BinnedLikelihood";
1371 }
1372 if (auto o = get(); o && fParent && (fParent->get<RooProduct>() || fParent->get<RooRealSumPdf>())) {
1373 if (o->InheritsFrom("RooStats::HistFactory::FlexibleInterpVar"))
1374 return "Overall";
1375 if (o->InheritsFrom("PiecewiseInterpolation")) {
1376 // check if children are all RooHistFunc ... if so, it's a HistoFactor, otherwise it's a Varied
1377 bool isHisto = true;
1378 for (auto c : const_cast<xRooNode *>(this)->browse()) {
1379 if (!c->get<RooHistFunc>()) {
1380 isHisto = false;
1381 break;
1382 }
1383 }
1384 if (isHisto) {
1385 return (dynamic_cast<RooAbsArg *>(o)->getAttribute("density")) ? "HistoDensity" : "Histo";
1386 } else {
1387 return (dynamic_cast<RooAbsArg *>(o)->getAttribute("density")) ? "VariedDensity" : "Varied";
1388 }
1389 }
1390 if (o->InheritsFrom("RooHistFunc"))
1391 return (dynamic_cast<RooAbsArg *>(o)->getAttribute("density")) ? "SimpleDensity" : "Simple";
1392 if (o->InheritsFrom("RooBinWidthFunction"))
1393 return "Density";
1394 if (o->InheritsFrom("ParamHistFunc"))
1395 return "Shape";
1396 if (o->InheritsFrom("RooRealVar"))
1397 return "Norm";
1398 if (o->InheritsFrom("RooConstVar"))
1399 return "Const";
1400 }
1401 return "";
1402}
1403
1404xRooNode xRooNode::coords(bool setVals) const
1405{
1406 xRooNode out(".coords", nullptr, *this);
1407 // go up through parents looking for slice obs
1408 auto _p = std::shared_ptr<xRooNode>(const_cast<xRooNode *>(this), [](xRooNode *) {});
1409 while (_p) {
1410 TString pName(_p->GetName());
1411 // following is commented out while still considering, but idea is to include category in coords
1412 /*if (auto s = _p->get<RooSimultaneous>(); s && s->indexCat().InheritsFrom("RooCategory") &&
1413 !out.find(s->indexCat().GetName())) { auto cat = const_cast<RooCategory*>(dynamic_cast<const
1414 RooCategory*>(&s->indexCat()));
1415 // check if we have a pdf for every category ... if not then add to cut
1416 cat->clearRange("coordRange",true);
1417 bool hasMissing = false;
1418 std::string includedStates;
1419 for (auto state : *cat) {
1420 if (!s->getPdf(state.first.c_str())) {
1421 hasMissing = true;
1422 } else {
1423 if (!includedStates.empty()) {
1424 includedStates += ",";
1425 }
1426 includedStates += state.first;
1427 }
1428 }
1429 if (hasMissing) {
1430 if(includedStates.find(",") != std::string::npos) {
1431 cat->addToRange("coordRange",includedStates.c_str());
1432 } else {
1433 cat->setLabel(includedStates);
1434 }
1435 out.emplace_back(std::make_shared<xRooNode>(cat->GetName(),_p->getObject<RooAbsArg>(cat->GetName()),_p));
1436 }
1437 } else*/
1438 if (auto pos = pName.Index('='); pos != -1) {
1439 if (pos > 0 && pName(pos - 1) == '<') {
1440 // should be a range on a real lvalue, of form low<=name<high
1441 double low = TString(pName(0, pos - 1)).Atof();
1442 pName = pName(pos + 1, pName.Length());
1443 double high = TString(pName(pName.Index('<') + 1, pName.Length())).Atof();
1444 pName = pName(0, pName.Index('<'));
1445 if (auto _obs = _p->getObject<RooAbsRealLValue>(pName.Data()); _obs) {
1446 if (setVals) {
1447 _obs->setVal((high + low) / 2.);
1448 static_cast<RooRealVar *>(_obs.get())->setRange("coordRange", low, high);
1449 _obs->setStringAttribute(
1450 "coordRange", "coordRange"); // will need if we allow multi disconnected regions, need comma list
1451 }
1452 out.emplace_back(std::make_shared<xRooNode>(_obs->GetName(), _obs, _p));
1453 } else {
1454 throw std::runtime_error(TString::Format("Unknown observable: %s", pName.Data()));
1455 }
1456
1457 } else if (auto _obs = _p->getObject<RooAbsArg>(pName(0, pos)); _obs) {
1458 if (setVals) {
1459 if (auto _cat = dynamic_cast<RooAbsCategoryLValue *>(_obs.get()); _cat) {
1460 _cat->setLabel(pName(pos + 1, pName.Length()));
1461 } else if (auto _var = dynamic_cast<RooAbsRealLValue *>(_obs.get()); _var) {
1462 _var->setVal(TString(pName(pos + 1, pName.Length())).Atof());
1463 }
1464 }
1465 out.emplace_back(std::make_shared<xRooNode>(_obs->GetName(), _obs, _p));
1466 } else {
1467 throw std::runtime_error("Unknown observable, could not find");
1468 }
1469 }
1470 _p = _p->fParent;
1471 }
1472 return out;
1473}
1474
1475void xRooNode::_Add_(const char *name, const char *opt)
1476{
1477 try {
1478 Add(name, opt);
1479 } catch (const std::exception &e) {
1480 new TGMsgBox(gClient->GetRoot(), gClient->GetRoot(), "Exception", e.what(),
1481 kMBIconExclamation); // deletes self on dismiss?
1482 }
1483}
1484void xRooNode::_Vary_(const char *what)
1485{
1486 try {
1487 Vary(what);
1488 } catch (const std::exception &e) {
1489 new TGMsgBox(gClient->GetRoot(), gClient->GetRoot(), "Exception", e.what(),
1490 kMBIconExclamation); // deletes self on dismiss?
1491 }
1492}
1493
1495{
1496
1497 if (strcmp(GetName(), ".poi") == 0) {
1498 // demote a parameter from being a poi
1499 auto toRemove =
1500 (child.get<RooAbsArg>() || !find(child.GetName())) ? child : xRooNode(find(child.GetName())->fComp);
1501 if (toRemove) {
1502 if (!toRemove.get<RooAbsArg>()->getAttribute("poi")) {
1503 throw std::runtime_error(TString::Format("%s is not a poi", toRemove.GetName()));
1504 }
1505 toRemove.get<RooAbsArg>()->setAttribute("poi", false);
1506 return toRemove;
1507 }
1508 } else if (strcmp(GetName(), ".factors") == 0 || strcmp(GetName(), ".constraints") == 0 ||
1509 strcmp(GetName(), ".components") == 0) {
1510 auto toRemove =
1511 (child.get<RooAbsArg>() || !find(child.GetName())) ? child : xRooNode(find(child.GetName())->fComp);
1512 if (auto p = fParent->get<RooProdPdf>(); p) {
1513 auto pdf = toRemove.get<RooAbsArg>();
1514 if (!pdf)
1515 pdf = p->pdfList().find(child.GetName());
1516 if (!pdf)
1517 throw std::runtime_error(TString::Format("Cannot find %s in %s", child.GetName(), fParent->GetName()));
1518 auto i = p->pdfList().index(*pdf);
1519 if (i >= 0) {
1520#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
1521 const_cast<RooArgList &>(p->pdfList()).remove(*pdf);
1522#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 26, 00)
1523 p->_pdfNSetList.erase(p->_pdfNSetList.begin() + i);
1524#else
1525 auto nset = p->_pdfNSetList.At(i);
1526 p->_pdfNSetList.Remove(nset);
1527 delete nset; // I don't think the RooLinkedList owned it so must delete ourself
1528#endif
1529 if (p->_extendedIndex == i)
1530 p->_extendedIndex = -1;
1531 else if (p->_extendedIndex > i)
1532 p->_extendedIndex--;
1533#else
1534 p->removePdfs(RooArgSet(*pdf));
1535#endif
1536 sterilize();
1537 return xRooNode(*pdf);
1538 } else {
1539 throw std::runtime_error(TString::Format("Cannot find %s in %s", child.GetName(), fParent->GetName()));
1540 }
1541 } else if (auto p2 = fParent->get<RooProduct>(); p2) {
1542 auto arg = toRemove.get<RooAbsArg>();
1543 if (!arg)
1544 arg = p2->components().find(child.GetName());
1545 if (!arg)
1546 throw std::runtime_error(TString::Format("Cannot find %s in %s", child.GetName(), fParent->GetName()));
1547 // remove server ... doesn't seem to trigger removal from proxy
1548#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
1549 p2->_compRSet.remove(*arg);
1550#else
1551 const_cast<RooArgList &>(p2->realComponents()).remove(*arg);
1552#endif
1553 p2->removeServer(*arg, true);
1554 sterilize();
1555 return xRooNode(*arg);
1556 } else if (fParent->get<RooSimultaneous>()) {
1557 // remove from all channels
1558 bool removed = false;
1559 for (auto &c : fParent->bins()) {
1560 try {
1561 c->constraints().Remove(toRemove);
1562 removed = true;
1563 } catch (std::runtime_error &) { /* wasn't a constraint in channel */
1564 }
1565 }
1566 sterilize();
1567 if (!removed)
1568 throw std::runtime_error(TString::Format("Cannot find %s in %s", child.GetName(), fParent->GetName()));
1569 return toRemove;
1570 } else if (auto p4 = fParent->get<RooRealSumPdf>(); p4) {
1571 auto arg = toRemove.get<RooAbsArg>();
1572 if (!arg)
1573 arg = p4->funcList().find(child.GetName());
1574 if (!arg)
1575 throw std::runtime_error(TString::Format("Cannot find %s in %s", child.GetName(), fParent->GetName()));
1576 // remove, including coef removal ....
1577 auto idx = p4->funcList().index(arg);
1578
1579 if (idx != -1) {
1580
1581 const_cast<RooArgList &>(p4->funcList()).remove(*arg);
1582 p4->removeServer(*arg, true);
1583 // have to be careful removing coef because if shared will end up removing them all!!
1584 std::vector<RooAbsArg *> _coefs;
1585 for (size_t ii = 0; ii < const_cast<RooArgList &>(p4->coefList()).size(); ii++) {
1586 if (ii != size_t(idx))
1587 _coefs.push_back(const_cast<RooArgList &>(p4->coefList()).at(ii));
1588 }
1589 const_cast<RooArgList &>(p4->coefList()).removeAll();
1590 for (auto &a : _coefs)
1591 const_cast<RooArgList &>(p4->coefList()).add(*a);
1592
1593 sterilize();
1594 } else {
1595 throw std::runtime_error(TString::Format("Cannot find %s in %s", child.GetName(), fParent->GetName()));
1596 }
1597 return xRooNode(*arg);
1598 } else if (auto p5 = fParent->get<RooAddPdf>(); p5) {
1599 auto arg = toRemove.get<RooAbsArg>();
1600 if (!arg)
1601 arg = p5->pdfList().find(child.GetName());
1602 if (!arg)
1603 throw std::runtime_error(TString::Format("Cannot find %s in %s", child.GetName(), fParent->GetName()));
1604 // remove, including coef removal ....
1605 auto idx = p5->pdfList().index(arg);
1606
1607 if (idx != -1) {
1608
1609 const_cast<RooArgList &>(p5->pdfList()).remove(*arg);
1610 p5->removeServer(*arg, true);
1611 // have to be careful removing coef because if shared will end up removing them all!!
1612 std::vector<RooAbsArg *> _coefs;
1613 for (size_t ii = 0; ii < const_cast<RooArgList &>(p5->coefList()).size(); ii++) {
1614 if (ii != size_t(idx))
1615 _coefs.push_back(const_cast<RooArgList &>(p5->coefList()).at(ii));
1616 }
1617 const_cast<RooArgList &>(p5->coefList()).removeAll();
1618 for (auto &a : _coefs)
1619 const_cast<RooArgList &>(p5->coefList()).add(*a);
1620
1621 sterilize();
1622 } else {
1623 throw std::runtime_error(TString::Format("Cannot find %s in %s", child.GetName(), fParent->GetName()));
1624 }
1625 return xRooNode(*arg);
1626 } else if (auto p6 = fParent->get<RooAddition>(); p6) {
1627 auto arg = toRemove.get<RooAbsArg>();
1628 if (!arg)
1629 arg = p6->list().find(child.GetName());
1630 if (!arg)
1631 throw std::runtime_error(TString::Format("Cannot find %s in %s", child.GetName(), fParent->GetName()));
1632 // remove server ... doesn't seem to trigger removal from proxy
1633 const_cast<RooArgList &>(p6->list()).remove(*arg);
1634 p6->removeServer(*arg, true);
1635 sterilize();
1636 return xRooNode(*arg);
1637 }
1638 }
1639
1640 if (auto w = get<RooWorkspace>(); w) {
1641 xRooNode out(child.GetName());
1642 auto arg = w->components().find(child.GetName());
1643 if (!arg)
1644 arg = operator[](child.GetName())->get<RooAbsArg>();
1645 if (!arg) {
1646 throw std::runtime_error(TString::Format("Cannot find %s in workspace %s", child.GetName(), GetName()));
1647 }
1648 // check has no clients ... if so, cannot delete
1649 if (arg->hasClients()) {
1650 throw std::runtime_error(
1651 TString::Format("Cannot remove %s from workspace %s, because it has dependencies - first remove from those",
1652 child.GetName(), GetName()));
1653 }
1654 const_cast<RooArgSet &>(w->components()).remove(*arg); // deletes arg
1655 Info("Remove", "Deleted %s from workspace %s", out.GetName(), GetName());
1656 return out;
1657 } else if (get<RooProduct>() || get<RooProdPdf>()) {
1658 return factors().Remove(child);
1659 } else if (get<RooRealSumPdf>() || get<RooAddPdf>() || get<RooAddition>()) {
1660 return components().Remove(child);
1661 }
1662
1663 throw std::runtime_error("Removal not implemented for object type " +
1664 std::string(get() ? get()->ClassName() : "null"));
1665}
1666
1668{
1669
1670 class AutoUpdater {
1671 public:
1672 AutoUpdater(xRooNode &_n) : n(_n) {}
1673 ~AutoUpdater() { n.browse(); }
1674 xRooNode &n;
1675 };
1676 AutoUpdater xxx(*this);
1677
1678 TString sOpt(opt);
1679 bool considerType(sOpt == "+");
1680
1681 if (strlen(GetName()) > 0 && GetName()[0] == '!' && fParent) {
1682 // folder .. pass onto parent and add folder to child folder list
1683 const_cast<xRooNode &>(child).fFolder += GetName();
1684 return fParent->Add(child, opt);
1685 }
1686 // this is how to get the first real parent ... may be useful at some point?
1687 /*auto realParent = fParent;
1688 while(!realParent->get()) {
1689 realParent = realParent->fParent;
1690 if (!realParent) throw std::runtime_error("No parentage");
1691 }*/
1692
1693 // adding to a collection node will incorporate the child into the parent of the collection
1694 // in the appropriate way
1695 if (strcmp(GetName(), ".factors") == 0) {
1696 // multiply the parent
1697 return fParent->Multiply(child, opt);
1698 } else if (strcmp(GetName(), ".components") == 0) {
1699 // add to the parent
1700 return fParent->Add(child, opt);
1701 } else if (strcmp(GetName(), ".variations") == 0) {
1702 // vary the parent
1703 return fParent->Vary(child);
1704 } else if (strcmp(GetName(), ".constraints") == 0) {
1705 // constrain the parent
1706 return fParent->Constrain(child);
1707 } else if (strcmp(GetName(), ".bins") == 0 && fParent->get<RooSimultaneous>()) {
1708 // adding a channel (should adding a 'bin' be an 'Extend' operation?)
1709 return fParent->Vary(child);
1710 } else if ((strcmp(GetName(), ".globs") == 0)) {
1711 if (child.get<RooAbsArg>() || (!child.fComp && getObject<RooAbsArg>(child.GetName()))) {
1712 auto out = (child.get<RooAbsArg>()) ? child.get<RooAbsArg>() : getObject<RooAbsArg>(child.GetName()).get();
1713 out->setAttribute("obs");
1714 out->setAttribute("global");
1715 return xRooNode(*out, *this);
1716 }
1717 throw std::runtime_error("Failed to add global observable");
1718 } else if ((strcmp(GetName(), ".poi") == 0)) {
1719 if (child.get<RooAbsLValue>() || (!child.fComp && getObject<RooAbsLValue>(child.GetName()))) {
1720 auto out = (child.get<RooAbsArg>()) ? child.get<RooAbsArg>() : getObject<RooAbsArg>(child.GetName()).get();
1721 out->setAttribute("poi");
1722 return xRooNode(*out, *this);
1723 } else if (!child.get() && fParent->get<RooWorkspace>()) {
1724 // may be creating poi at same time as adding, try add to parent
1725 auto res = fParent->Add(child);
1726 if (res.get<RooAbsLValue>())
1727 return Add(res);
1728 }
1729 throw std::runtime_error("Failed to add parameter of interest");
1730 } else if ((strcmp(GetName(), ".pars") == 0 || strcmp(GetName(), ".vars") == 0) && fParent->get<RooWorkspace>()) {
1731 // adding a parameter, interpret as factory string unless no "[" then create RooRealVar
1732 TString fac(child.GetName());
1733 if (!fac.Contains("[") && !fac.Contains("("))
1734 fac += "[1]";
1735 return xRooNode(*fParent->get<RooWorkspace>()->factory(fac), fParent);
1736 } else if (strcmp(GetName(), ".datasets()") == 0) {
1737
1738 if (auto _data = child.get<RooAbsData>(); _data) {
1739 if (find(_data->GetName())) {
1740 throw std::runtime_error(TString::Format("Cannot add dataset %s, already exists for %s. If intending to "
1741 "combine datasets, please add directly to dataset",
1742 child->GetName(), GetName()));
1743 }
1744 return fParent->Add(child); // add the dataset to the parent
1745 }
1746
1747 // create a dataset - only allowed for pdfs or workspaces
1748 if (auto _ws = ws(); _ws && fParent) {
1749 sOpt.ToLower();
1750 if (!fParent->get<RooAbsPdf>() && (!fParent->get<RooWorkspace>() || sOpt == "asimov")) {
1751 throw std::runtime_error(
1752 "Datasets can only be created for pdfs or workspaces (except if generated dataset, then must be pdf)");
1753 }
1754
1755 if (sOpt == "asimov" || sOpt == "toy") {
1756 // generate expected dataset - note that globs will be frozen at this time
1757 auto _fr = fParent->fitResult();
1758 if (strlen(_fr->GetName()) == 0) { // ensure fit result has a name so that name is saved inside dataset
1759 _fr.get<RooFitResult>()->SetName(TUUID().AsString());
1760 }
1761 auto ds = fParent->generate(_fr, sOpt == "asimov");
1762 if (strlen(child.GetName())) {
1763 ds.SetName(child.GetName());
1764 ds.get<TNamed>()->SetName(child.GetName());
1765 }
1766 if (auto _ds = ds.get<RooAbsData>()) {
1767 _ws->import(*_ds);
1768 }
1769 if (_fr.get<RooFitResult>()->numStatusHistory() == 0) {
1770 if (!GETWSSNAPSHOTS(_ws).find(_fr->GetName())) {
1771 const_cast<RooLinkedList &>(GETWSSNAPSHOTS(_ws)).Add(_fr->Clone());
1772 }
1773 } else if (!_ws->obj(_fr->GetName())) {
1774 _ws->import((*_fr.get<RooFitResult>()));
1775 } // save fr to workspace, for later retrieval
1776 return xRooNode(*_ws->data(ds.GetName()), fParent);
1777 }
1778
1779 auto parentObs = fParent->obs(); // may own globs so keep alive
1780 auto _obs = parentObs.argList();
1781 // put globs in a snapshot
1782 std::unique_ptr<RooAbsCollection> _globs(_obs.selectByAttrib("global", true));
1783 // RooArgSet _tmp; _tmp.add(*_globs);_ws->saveSnapshot(child.GetName(),_tmp);
1784 _obs.remove(*_globs);
1785
1786 // include any coords
1787 _obs.add(coords(false).argList(), true);
1788 // include axis var too, provided it's an observable
1789 if (auto ax = GetXaxis(); ax && dynamic_cast<RooAbsArg *>(ax->GetParent())->getAttribute("obs")) {
1790 _obs.add(*dynamic_cast<RooAbsArg *>(ax->GetParent()));
1791 }
1792 // check if ws already has a dataset with this name, if it does we may need to extend columns
1793 if (auto _d = _ws->data(child.GetName()); _d) {
1794 // add any missing obs
1795 RooArgSet l(_obs);
1796 l.remove(*_d->get(), true, true);
1797 if (!l.empty()) {
1798 auto _dd = dynamic_cast<RooDataSet *>(_d);
1799 if (!_dd)
1800 throw std::runtime_error("Cannot extend dataset with new columns");
1801 for (auto &x : l) {
1802 _dd->addColumn(*x);
1803 }
1804 }
1805 } else {
1806 RooRealVar w("weightVar", "weightVar", 1);
1807 _obs.add(w);
1808 RooDataSet d(child.GetName(), child.GetTitle(), _obs, RooFit::WeightVar("weightVar"));
1809 _ws->import(d);
1810 // seems have to set bits after importing, not before
1811 if (auto __d = _ws->data(child.GetName()))
1812 __d->SetBit(1 << 20, _ws->allData().size() == 1); // sets as selected if is only ds
1813 }
1814 /*if(!_ws->data(child.GetName())) {
1815 RooRealVar w("weightVar", "weightVar", 1);
1816 RooArgSet _obs; _obs.add(w);
1817 RooDataSet d(child.GetName(), child.GetTitle(), _obs, "weightVar");
1818 _ws->import(d);
1819 }*/
1820 auto out = std::shared_ptr<TObject>(_ws->data(child.GetName()), [](TObject *) {});
1821
1822 if (out) {
1823 xRooNode o(out, fParent);
1824 if (child.get<TH1>())
1825 o = *child.get();
1826 return o;
1827 }
1828 }
1829 throw std::runtime_error("Cannot create dataset");
1830 }
1831
1832 if (!get()) {
1833 if (!fParent)
1834 throw std::runtime_error("Cannot add to null object with no parentage");
1835
1836 auto _ref = emplace_back(std::shared_ptr<xRooNode>(&const_cast<xRooNode &>(child), [](TObject *) {}));
1837 try {
1838 fComp = fParent->Add(*this, "+").fComp;
1839 } catch (...) {
1840 resize(size() - 1);
1841 std::rethrow_exception(std::current_exception());
1842 }
1843 resize(size() - 1); // remove the temporarily added node
1844
1845 if (!fComp) {
1846 throw std::runtime_error("No object");
1847 }
1848 }
1849
1850 if (auto p = get<RooAbsData>(); p) {
1851 if (auto bb = getBrowsable(".sourceds"))
1852 bb->Add(child, opt);
1853 if (auto _data = child.get<RooDataSet>()) {
1854 auto ds = dynamic_cast<RooDataSet *>(p);
1855 if (!ds) {
1856 throw std::runtime_error("Can only add datasets to a dataset");
1857 }
1858
1859 // append any missing globs, and check any existing globs have matching values
1860 RooArgList globsToAdd;
1861 auto _globs = globs();
1862 for (auto &glob : child.globs()) {
1863 if (auto g = _globs.find(glob->GetName()); !g) {
1864 globsToAdd.addClone(*glob->get<RooAbsArg>());
1865 } else if (g->GetContent() != glob->GetContent()) {
1866 Warning("Add", "Global observable %s=%g in dataset %s mismatches %s value %g ... ignoring latter",
1867 g->GetName(), g->GetContent(), GetName(), child.GetName(), glob->GetContent());
1868 }
1869 }
1870 // add any existing globs to list then set the list
1871 if (auto _dglobs = p->getGlobalObservables()) {
1872 globsToAdd.addClone(*_dglobs);
1873 } else {
1874 for (auto g : _globs)
1875 globsToAdd.addClone(*g->get<RooAbsArg>());
1876 }
1877 p->setGlobalObservables(globsToAdd);
1878
1879 // append any missing observables to our dataset, then append the dataset
1880
1881 std::set<std::pair<RooAbsCategory *, RooCategory *>> cats;
1882
1883 for (auto col : *_data->get()) {
1884 if (!p->get()->contains(*col)) {
1885 ds->addColumn(*col);
1886 } else if (auto c = dynamic_cast<RooAbsCategory *>(col)) {
1887 // check if any of the states of c have different index to c2
1888 auto c2 = dynamic_cast<RooCategory *>(p->get()->find(*col));
1889 if (!c2) {
1890 throw std::runtime_error(
1891 TString::Format("unexpected type for regular observable: %s", col->GetName()));
1892 }
1893 bool iMatches = true;
1894 for (const auto &nameIdx : *c) {
1895 if (!c2->hasLabel(nameIdx.first) && !c2->hasIndex(nameIdx.second)) {
1896 // can define the state
1897 c2->defineType(nameIdx.first, nameIdx.second);
1898 } else if (c2->lookupIndex(nameIdx.first) != nameIdx.second) {
1899 iMatches = false;
1900 break; // state exists, but with different index!
1901 }
1902 }
1903 if (!iMatches)
1904 cats.insert({c, c2});
1905 }
1906 }
1907 if (cats.empty()) {
1908 ds->append(*_data);
1909 } else {
1910 // cannot use append, because if categoricals use same idx for different states, will not do correct thing
1911 for (int i = 0; i < _data->numEntries(); i++) {
1912 auto row = _data->get(i);
1913 auto w = _data->weight();
1914 ds->get()->assign(*row);
1915 for (auto [c, c2] : cats) {
1916 c2->setLabel(row->getCatLabel(c->GetName()));
1917 }
1918 ds->add(*ds->get(), w);
1919 }
1920 }
1921
1922 ds->SetTitle(TString(ds->GetTitle()) + " + " + _data->GetTitle());
1923 SetTitle(TString(GetTitle()) + " + " + child.GetTitle());
1924 return *this;
1925 }
1926 auto _arg = child.get<RooAbsArg>();
1927 if (auto _ds = dynamic_cast<RooDataSet *>(p); _arg && _ds) {
1928 // can add var or function of existing obs to dataset as a column
1929 _ds->addColumn(*_arg);
1930 _arg->setAttribute("obs");
1931 return xRooNode(*_arg, *this);
1932 }
1933 auto _h = child.get<TH1>();
1934 if (!_h) {
1935 throw std::runtime_error("Can only add histogram or var/expr or dataset to data");
1936 }
1937 auto _pdf = parentPdf();
1938 if (!_pdf)
1939 throw std::runtime_error("Could not find pdf");
1940 auto _ax = _pdf->GetXaxis();
1941 if (!_ax) {
1942 throw std::runtime_error("Cannot determine binning to add data");
1943 }
1944
1945 RooArgSet obs;
1946 obs.add(*dynamic_cast<RooAbsArg *>(_ax->GetParent()));
1947 obs.add(coords().argList()); // will also move obs to coords
1948
1949 // add any missing obs
1950 RooArgSet l(obs);
1951 l.remove(*p->get(), true, true);
1952 if (!l.empty()) {
1953 auto _d = dynamic_cast<RooDataSet *>(p);
1954 if (!_d)
1955 throw std::runtime_error("Cannot extend dataset with new columns");
1956 for (auto &x : l) {
1957 _d->addColumn(*x);
1958 }
1959 }
1960
1961 // before adding, ensure range is good to cover
1962 for (auto &o : obs) {
1963 if (auto v = dynamic_cast<RooRealVar *>(o); v) {
1964 if (auto dv = dynamic_cast<RooRealVar *>(p->get()->find(v->GetName())); dv) {
1965 if (v->getMin() < dv->getMin())
1966 dv->setMin(v->getMin());
1967 if (v->getMax() > dv->getMax())
1968 dv->setMax(v->getMax());
1969 }
1970 } else if (auto c = dynamic_cast<RooCategory *>(o); c) {
1971 if (auto dc = dynamic_cast<RooCategory *>(p->get()->find(c->GetName())); dc) {
1972 for (const auto &nameIdx : *c) {
1973 if (!dc->hasLabel(nameIdx.first)) {
1974 dc->defineType(nameIdx.first, nameIdx.second);
1975 }
1976 }
1977 }
1978 }
1979 }
1980
1981 for (int i = 1; i <= _h->GetNbinsX(); i++) {
1982 if (auto cat = dynamic_cast<RooAbsCategoryLValue *>(_ax->GetParent())) {
1983 if (!_h->GetXaxis()->GetBinLabel(i)) {
1984 throw std::runtime_error(
1985 TString::Format("Categorical observable %s requires bin labels", _ax->GetParent()->GetName()));
1986 } else if (!cat->hasLabel(_h->GetXaxis()->GetBinLabel(i))) {
1987 throw std::runtime_error(TString::Format("Categorical observable %s does not have label %s",
1988 _ax->GetParent()->GetName(), _h->GetXaxis()->GetBinLabel(i)));
1989 } else {
1990 cat->setLabel(_h->GetXaxis()->GetBinLabel(i));
1991 }
1992 } else {
1993 dynamic_cast<RooAbsRealLValue *>(_ax->GetParent())->setVal(_h->GetBinCenter(i));
1994 }
1995 p->add(obs, _h->GetBinContent(i));
1996 }
1997
1998 return *this;
1999 }
2000
2001 if (auto p = get<RooAddPdf>(); p) {
2002 auto cc = child.fComp;
2003 // bool isConverted = (cc != child.convertForAcquisition(*this, sOpt));
2004 child.convertForAcquisition(*this, sOpt);
2005 if ((child.get<RooAbsPdf>() || (!child.fComp && getObject<RooAbsPdf>(child.GetName())))) {
2006 auto out = (child.fComp) ? acquire(child.fComp) : getObject<RooAbsArg>(child.GetName());
2007 // don't add a coef if in 'all-extended' mode and this pdf is extendable
2008 auto _pdf = std::dynamic_pointer_cast<RooAbsPdf>(out);
2009 if (!_pdf) {
2010 throw std::runtime_error("Something went wrong with pdf acquisition");
2011 }
2012
2013 if (auto _ax = GetXaxis(); _ax && dynamic_cast<RooAbsRealLValue *>(_ax->GetParent()) &&
2014 _pdf->dependsOn(*static_cast<RooAbsArg *>(_ax->GetParent()))) {
2015 auto _p = _pdf;
2016
2017 if (auto _boundaries = std::unique_ptr<std::list<double>>(_p->binBoundaries(
2018 *dynamic_cast<RooAbsRealLValue *>(_ax->GetParent()), -std::numeric_limits<double>::infinity(),
2019 std::numeric_limits<double>::infinity()));
2020 !_boundaries && _ax->GetNbins() > 0) {
2021#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 24, 00)
2022 Warning("Add", "Adding unbinned pdf %s to binned %s - will wrap with RooBinSamplingPdf(...)",
2023 _p->GetName(), GetName());
2024 _p = acquireNew<RooBinSamplingPdf>(TString::Format("%s_binned", _p->GetName()), _p->GetTitle(),
2025 *dynamic_cast<RooAbsRealLValue *>(_ax->GetParent()), *_p);
2026 _p->setStringAttribute("alias", std::dynamic_pointer_cast<RooAbsArg>(out)->getStringAttribute("alias"));
2027 if (!_p->getStringAttribute("alias"))
2028 _p->setStringAttribute("alias", out->GetName());
2029#else
2030 throw std::runtime_error(
2031 "unsupported addition of unbinned pdf to binned model - please upgrade to at least ROOT 6.24");
2032#endif
2033 _pdf = _p;
2034 }
2035 }
2036
2037 if (!(_pdf->canBeExtended() && p->coefList().empty())) {
2038 // if extended, use an extended binding as the coef
2039 // otherwise e.g. if adding a RooRealSumPdf the stacked histograms will be above the
2040 // actual pdf histogram because the pdf histogram is just normalized down
2041 if (_pdf->canBeExtended()) {
2042 // FIXME: ExtendedBinding needs the obs list passing to it ... should be fixed in RooFit
2043 std::cout << " warning " << _pdf->GetName() << " wont be correctly normalized" << std::endl;
2044 // until then, this will return "1" and so the pdf's histograms wont be normalized properly in relation
2045 // to stacks of its comps
2046 const_cast<RooArgList &>(p->coefList())
2047 .add(*acquireNew<RooExtendedBinding>(TString::Format("%s_extBind", _pdf->GetName()),
2048 TString::Format("Expected Events of %s", _pdf->GetTitle()),
2049 *_pdf));
2050 } else {
2051
2052 // need to create a coefficient for each existing pdf first, like above
2053 for (auto i = p->coefList().size(); i < p->pdfList().size(); i++) {
2054 const_cast<RooArgList &>(p->coefList())
2055 .add(*acquireNew<RooExtendedBinding>(
2056 TString::Format("%s_extBind", p->pdfList().at(i)->GetName()),
2057 TString::Format("Expected Events of %s", p->pdfList().at(i)->GetTitle()),
2058 *static_cast<RooAbsPdf *>(p->pdfList().at(i))));
2059 }
2060
2061 const_cast<RooArgList &>(p->coefList()).add(*acquire2<RooAbsArg, RooRealVar>("1", "1", 1));
2062 }
2063 // ensure not in no-coef mode any more
2064 *reinterpret_cast<bool *>(reinterpret_cast<unsigned char *>(p) +
2065 p->Class()->GetDataMemberOffset("_allExtendable")) = false;
2066 *reinterpret_cast<bool *>(reinterpret_cast<unsigned char *>(p) +
2067 p->Class()->GetDataMemberOffset("_haveLastCoef")) = true;
2068 }
2069 const_cast<RooArgList &>(p->pdfList()).add(*_pdf);
2070 sterilize();
2071 return xRooNode(*_pdf, *this);
2072 } else if ((child.get<TH1>() || child.get<RooAbsReal>() ||
2073 (!child.get() && getObject<RooAbsReal>(child.GetName()))) &&
2074 !child.get<RooAbsPdf>()) {
2075 RooRealSumPdf *_pdf = nullptr;
2076 bool tooMany(false);
2077 for (auto &pp : factors()) {
2078 if (auto _p = pp->get<RooRealSumPdf>(); _p) {
2079 if (_pdf) {
2080 _pdf = nullptr;
2081 tooMany = true;
2082 break;
2083 } // more than one!
2084 _pdf = _p;
2085 }
2086 }
2087 if (_pdf) {
2088 return xRooNode(*_pdf, *this).Add(child);
2089 } else if (!tooMany) {
2090 // create a RooRealSumPdf to hold the child
2091 auto _sumpdf = Add(*acquireNew<RooRealSumPdf>(TString::Format("%s_samples", p->GetName()),
2092 TString::Format("%s samples", GetTitle()), RooArgList(),
2093 RooArgList(), true));
2094 _sumpdf.get<RooAbsArg>()->setStringAttribute("alias", "samples");
2095 return _sumpdf.Add(child);
2096 }
2097 }
2098 }
2099
2100 if (auto p = get<RooRealSumPdf>(); p) {
2101 std::shared_ptr<TObject> out;
2102 auto cc = child.fComp;
2103 bool isConverted = (cc != child.convertForAcquisition(*this, sOpt));
2104 if (child.get<RooAbsReal>()) {
2105 out = acquire(child.fComp);
2106 if (std::dynamic_pointer_cast<TH1>(cc) && !TString(cc->GetOption()).Contains("nostyle")) {
2107 xRooNode(out, *this).styles(cc.get()); // transfer style if adding a histogram
2108 }
2109 }
2110 if (!child.fComp && getObject<RooAbsReal>(child.GetName())) {
2111 Info("Add", "Adding existing function %s to %s", child.GetName(), p->GetName());
2112 out = getObject<RooAbsReal>(child.GetName());
2113 }
2114
2115 if (!out && !child.fComp) {
2116 std::shared_ptr<RooAbsArg> _func;
2117 // a null node .. so create either a new RooProduct or RooHistFunc if has observables (or no deps but has
2118 // x-axis)
2119 auto _obs = robs();
2120 if (!_obs.empty() || GetXaxis()) {
2121 if (_obs.empty()) {
2122 // using X axis to construct hist
2123 auto _ax = dynamic_cast<Axis2 *>(GetXaxis());
2124 std::unique_ptr<TH1D> h;
2125 {
2126 TDirectory::TContext ctx{nullptr}; // No self-registration to directories
2127 h = std::make_unique<TH1D>(child.GetName(), child.GetTitle(), _ax->GetNbins(),
2128 _ax->binning()->array());
2129 }
2130 h->GetXaxis()->SetName(TString::Format("%s;%s", _ax->GetParent()->GetName(), _ax->GetName()));
2131 // technically convertForAcquisition has already acquired so no need to re-acquire but should be harmless
2132 _func = std::dynamic_pointer_cast<RooAbsArg>(acquire(xRooNode(*h).convertForAcquisition(*this)));
2133 } else if (_obs.size() == 1) {
2134 // use the single obs to make a TH1D
2135 auto _x = _obs.at(0)->get<RooAbsLValue>();
2136 auto _bnames = _x->getBinningNames();
2137 TString binningName = p->getStringAttribute("binning");
2138 for (auto &b : _bnames) {
2139 if (b == p->GetName()) {
2140 binningName = p->GetName();
2141 break;
2142 }
2143 }
2144 std::unique_ptr<TH1D> h;
2145 {
2146 TDirectory::TContext ctx{nullptr}; // No self-registration to directories
2147 h = std::make_unique<TH1D>(child.GetName(), child.GetTitle(), _x->numBins(binningName),
2148 _x->getBinningPtr(binningName)->array());
2149 }
2150 h->GetXaxis()->SetName(
2151 TString::Format("%s;%s", dynamic_cast<TObject *>(_x)->GetName(), binningName.Data()));
2152 // technically convertForAcquisition has already acquired so no need to re-acquire but should be harmless
2153 _func = std::dynamic_pointer_cast<RooAbsArg>(acquire(xRooNode(*h).convertForAcquisition(*this)));
2154 Info("Add", "Created SimpleDensity factor %s (xaxis=%s) for %s", _func->GetName(), _obs.at(0)->GetName(),
2155 p->GetName());
2156 } else {
2157 throw std::runtime_error("Unsupported creation of new component in SumPdf for this many obs");
2158 }
2159 } else {
2160 _func = acquireNew<RooProduct>(TString::Format("%s_%s", p->GetName(), child.GetName()), child.GetTitle(),
2161 RooArgList());
2162 }
2163 _func->setStringAttribute("alias", child.GetName());
2164 out = _func;
2165 }
2166
2167 if (auto _f = std::dynamic_pointer_cast<RooHistFunc>(
2168 (child.get<RooProduct>()) ? child.factors()[child.GetName()]->fComp : out);
2169 _f) {
2170 // adding a histfunc directly to a sumpdf, should be a density
2171 _f->setAttribute("density");
2172 if (_f->getAttribute("autodensity")) {
2173 // need to divide by bin widths first
2174 for (int i = 0; i < _f->dataHist().numEntries(); i++) {
2175 auto bin_pars = _f->dataHist().get(i);
2176 _f->dataHist().set(*bin_pars, _f->dataHist().weight(i) / _f->dataHist().binVolume(*bin_pars));
2177 }
2178 _f->setAttribute("autodensity", false);
2179 _f->setValueDirty();
2180 }
2181
2182 // promote the axis vars to observables
2183 // can't use original child as might refer to unacquired deps
2184 for (auto &x : xRooNode("tmp", _f).vars()) {
2185 x->get<RooAbsArg>()->setAttribute("obs");
2186 }
2187 if (isConverted) {
2188 Info("Add", "Created %s factor RooHistFunc::%s for %s",
2189 _f->getAttribute("density") ? "SimpleDensity" : "Simple", _f->GetName(), p->GetName());
2190 }
2191 }
2192
2193 if (auto _p = std::dynamic_pointer_cast<RooAbsPdf>(out); _p) {
2194 // adding a pdf to a RooRealSumPdf will replace it with a RooAddPdf and put the RooRealSumPdf inside that
2195 // if pdf is extended will use in the "no coefficients" state, where the expectedEvents are taking from
2196 // the pdf integrals
2197 TString newName(p->GetName());
2198 newName.ReplaceAll("_samples", "");
2199 newName += "_components";
2200 Warning("Add", "converting samples to components");
2201
2202 if (auto _ax = GetXaxis(); _ax && dynamic_cast<RooAbsRealLValue *>(_ax->GetParent()) &&
2203 _p->dependsOn(*static_cast<RooAbsArg *>(_ax->GetParent()))) {
2204
2205 if (auto _boundaries = std::unique_ptr<std::list<double>>(_p->binBoundaries(
2206 *dynamic_cast<RooAbsRealLValue *>(_ax->GetParent()), -std::numeric_limits<double>::infinity(),
2207 std::numeric_limits<double>::infinity()));
2208 !_boundaries && _ax->GetNbins() > 0) {
2209#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 24, 00)
2210 Warning("Add", "Adding unbinned pdf %s to binned %s - will wrap with RooBinSamplingPdf(...)",
2211 _p->GetName(), GetName());
2212 _p = acquireNew<RooBinSamplingPdf>(TString::Format("%s_binned", _p->GetName()), _p->GetTitle(),
2213 *dynamic_cast<RooAbsRealLValue *>(_ax->GetParent()), *_p);
2214 _p->setStringAttribute("alias", std::dynamic_pointer_cast<RooAbsArg>(out)->getStringAttribute("alias"));
2215 if (!_p->getStringAttribute("alias"))
2216 _p->setStringAttribute("alias", out->GetName());
2217#else
2218 throw std::runtime_error(
2219 "unsupported addition of unbinned pdf to binned model - please upgrade to at least ROOT 6.24");
2220#endif
2221 }
2222 }
2223
2224 // require to be extended to be in coefficient-free mode ...
2225 // otherwise would lose the integral of the sumPdf (can't think of way to have a coef be the integral)
2226 if (!_p->canBeExtended()) {
2227 _p = acquireNew<RooExtendPdf>(TString::Format("%s_extended", _p->GetName()), _p->GetTitle(), *_p,
2228 *acquire2<RooAbsReal, RooRealVar>("1", "1", 1));
2229 }
2230
2231 return *(Replace(*acquireNew<RooAddPdf>(newName, _p->GetTitle(), RooArgList(*p, *_p)))
2232 .browse()[1]); // returns second node.
2233 }
2234
2235 if (auto _f = std::dynamic_pointer_cast<RooAbsReal>(out); _f) {
2236
2237 // todo: if adding a pdf, should actually replace RooRealSumPdf with a RooAddPdf and put
2238 // the sumPdf and *this* pdf inside that pdf
2239 // only exception is the binSamplingPdf below to integrate unbinned functions across bins
2240
2241 if (auto _ax = GetXaxis(); _ax && dynamic_cast<RooAbsRealLValue *>(_ax->GetParent()) &&
2242 _f->dependsOn(*static_cast<RooAbsArg *>(_ax->GetParent()))) {
2243
2244 if (auto _boundaries = std::unique_ptr<std::list<double>>(_f->binBoundaries(
2245 *dynamic_cast<RooAbsRealLValue *>(_ax->GetParent()), -std::numeric_limits<double>::infinity(),
2246 std::numeric_limits<double>::infinity()));
2247 !_boundaries && _ax->GetNbins() > 0) {
2248#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 24, 00)
2249 Warning(
2250 "Add",
2251 "Adding unbinned function %s to binned %s - will wrap with RooRealSumPdf(RooBinSamplingPdf(...))",
2252 _f->GetName(), GetName());
2253 auto sumPdf = acquireNew<RooRealSumPdf>(TString::Format("%s_pdfWrapper", _f->GetName()), _f->GetTitle(),
2254 *_f, *acquire2<RooAbsArg, RooRealVar>("1", "1", 1), true);
2255 sumPdf->setStringAttribute("alias", _f->getStringAttribute("alias"));
2256 if (!sumPdf->getStringAttribute("alias"))
2257 sumPdf->setStringAttribute("alias", out->GetName());
2258 _f = acquireNew<RooBinSamplingPdf>(TString::Format("%s_binned", _f->GetName()), _f->GetTitle(),
2259 *dynamic_cast<RooAbsRealLValue *>(_ax->GetParent()), *sumPdf);
2260 _f->setStringAttribute("alias", std::dynamic_pointer_cast<RooAbsArg>(out)->getStringAttribute("alias"));
2261 if (!_f->getStringAttribute("alias"))
2262 _f->setStringAttribute("alias", out->GetName());
2263#else
2264 throw std::runtime_error(
2265 "unsupported addition of unbinned function to binned model - please upgrade to at least ROOT 6.24");
2266#endif
2267 }
2268 }
2269
2270 const_cast<RooArgList &>(p->coefList()).add(*acquire2<RooAbsArg, RooRealVar>("1", "1", 1));
2271 const_cast<RooArgList &>(p->funcList()).add(*_f);
2272 // inherit binning if we dont have one yet
2273 if (!p->getStringAttribute("binning"))
2274 p->setStringAttribute("binning", _f->getStringAttribute("binning"));
2275
2276 xRooNode _out(_f, *this);
2277 if (auto gf = p->getStringAttribute("global_factors"); gf) {
2278 TStringToken pattern(gf, ";");
2279 while (pattern.NextToken()) {
2280 auto fac = getObject<RooAbsReal>(pattern.Data());
2281 if (!fac) {
2282 throw std::runtime_error(TString::Format("Could not find global factor %s", pattern.Data()));
2283 }
2284 _out.Multiply(fac);
2285 }
2286 }
2287 sterilize();
2288 // clear children for reload and update shared axis
2289 clear();
2290 fXAxis.reset();
2291 p->setStringAttribute("xvar", nullptr);
2292 browse();
2293 return _out;
2294 }
2295 } else if (auto p2 = get<RooProdPdf>(); p2) {
2296 // can "add" to a RooProdPdf provided trying to add a RooAbsReal not a RooAbsPdf and have a zero or 1
2297 // RooRealSumPdf child.convertForAcquisition(*this); - don't convert here because want generated objects named
2298 // after roorealsumpdf
2299 // would like exception is if child is a factory string! - TODO
2300 if (child.get<RooAbsPdf>() || (!child.get() && getObject<RooAbsPdf>(child.GetName()))) {
2301 // can add if 0 or 1 RooAddPdf ....
2302 RooAddPdf *_pdf = nullptr;
2303 bool tooMany(false);
2304 for (auto &pp : factors()) {
2305 if (auto _p = pp->get<RooAddPdf>(); _p) {
2306 if (_pdf) {
2307 _pdf = nullptr;
2308 tooMany = true;
2309 break;
2310 } // more than one!
2311 _pdf = _p;
2312 }
2313 }
2314 if (_pdf) {
2315 return xRooNode(*_pdf, *this).Add(child);
2316 } else if (!tooMany) {
2317 auto out = this->operator[]("components")->Add(child);
2318 return out;
2319 }
2320 } else if ((child.get<TH1>() || child.get<RooAbsReal>() ||
2321 (!child.get() && getObject<RooAbsReal>(child.GetName()))) &&
2322 !child.get<RooAbsPdf>()) {
2323 RooRealSumPdf *_pdf = nullptr;
2324 RooAddPdf *_backup = nullptr;
2325 bool tooMany(false);
2326 for (auto &pp : factors()) {
2327 if (auto _p = pp->get<RooRealSumPdf>(); _p) {
2328 if (_pdf) {
2329 _pdf = nullptr;
2330 tooMany = true;
2331 break;
2332 } // more than one!
2333 _pdf = _p;
2334 } else if (auto _p2 = pp->get<RooAddPdf>(); _p2) {
2335 _backup = _p2;
2336 for (auto &_pdfa : pp->components()) {
2337 if (auto _p3 = _pdfa->get<RooRealSumPdf>(); _p3) {
2338 if (_pdf) {
2339 _pdf = nullptr;
2340 tooMany = true;
2341 break;
2342 } // more than one!
2343 _pdf = _p3;
2344 }
2345 }
2346 }
2347 }
2348 if (_pdf) {
2349 return xRooNode(*_pdf, *this).Add(child);
2350 } else if (_backup) {
2351 // added *INSIDE* the addPdf -- will create a RooRealSumPdf to hold it
2352 return xRooNode(*_backup, *this).Add(child);
2353 } else if (!tooMany) {
2354 auto out = this->operator[]("samples")->Add(child);
2355 // clear our x-axis to re-evaluate
2356 fXAxis.reset();
2357 p2->setStringAttribute("xvar", nullptr);
2358 return out;
2359 }
2360 }
2361 } else if (auto s = get<RooSimultaneous>(); s) {
2362
2363 // adding to a simultaneous means adding a bin
2364 return bins().Add(child);
2365
2366 // if the child is a RooAbsPdf can just add it as a new channel using name of pdf as the channel name
2367 // if child is a histogram, will create a RooProdPdf
2368
2369 } else if (auto w = get<RooWorkspace>(); w) {
2370 child.convertForAcquisition(
2371 *this, child.get() ? "" : "func" /* if child is a string, allow it to be passed to factory */);
2372 if (child.get()) {
2373 if (auto _d = child.get<RooAbsData>()) {
2374 // don't use acquire method to import, because that adds datasets as Embeddded
2375 if (!w->import(*_d)) {
2376 // should upgrade vars with any obs from the dataset
2377 if (_d->get()) {
2378 std::unique_ptr<RooAbsCollection>(w->allVars().selectCommon(*_d->get()))->setAttribAll("obs");
2379 }
2380 if (_d->getGlobalObservables()) {
2381 std::unique_ptr<RooAbsCollection> globs(w->allVars().selectCommon(*_d->getGlobalObservables()));
2382 globs->setAttribAll("obs");
2383 globs->setAttribAll("global");
2384 }
2385 return xRooNode(child.GetName(), *w->data(child.GetName()), *this);
2386 } else {
2387 throw std::runtime_error(
2388 TString::Format("Could not import dataset %s into workspace %s", child.GetName(), w->GetName())
2389 .Data());
2390 }
2391 } else {
2392 auto out = acquire(child.fComp);
2393 if (out)
2394 return xRooNode(child.GetName(), out, *this);
2395 }
2396 }
2397
2398 if (!child.empty() || child.fFolder == "!pdfs") {
2399 // create a RooSimultaneous using the children as the channels
2400 // children either have "=" in name if specifying channel cat name or otherwise assume
2401 std::string catName = "channelCat";
2402 if (!child.empty()) {
2403 if (TString ss = child.at(0)->GetName(); ss.Contains("=")) {
2404 catName = ss(0, ss.Index('='));
2405 }
2406 }
2407 auto _cat = acquire<RooCategory>(catName.c_str(), catName.c_str());
2408 _cat->setAttribute("obs");
2409 auto out = acquireNew<RooSimultaneous>(child.GetName(), child.GetTitle(), *_cat);
2410 Info("Add", "Created pdf RooSimultaneous::%s in workspace %s", out->GetName(), w->GetName());
2411 return xRooNode(out, *this);
2412 }
2413 } else if (auto coll = get<RooAbsCollection>(); coll && child.get<RooAbsArg>()) {
2414 if (coll->isOwning()) {
2415 coll->addOwned(*static_cast<RooAbsArg *>(child.get<RooAbsArg>()->Clone()));
2416 } else if (child.ws() != ws()) {
2417 coll->add(*static_cast<RooAbsArg *>(acquire(child.fComp).get()));
2418 } else {
2419 coll->add(*child.get<RooAbsArg>());
2420 }
2421 return xRooNode(child.GetName(), *coll->find(child.GetName()), *this);
2422 }
2423
2424 if (sOpt == "pdf") {
2425 // can only add a pdf to a workspace
2426 if (get<RooWorkspace>()) {
2427 const_cast<xRooNode &>(child).fFolder = "!pdfs";
2428 return Add(child);
2429 }
2430 } else if (sOpt == "channel") {
2431 // can add to a model or to a workspace (creates a RooProdPdf either way)
2432 if (get<RooSimultaneous>()) {
2433 return Vary(child);
2434 } else if (get<RooWorkspace>()) {
2435 std::shared_ptr<TObject> out;
2436 child.convertForAcquisition(*this);
2437 if (child.get<RooAbsPdf>()) {
2438 out = acquire(child.fComp);
2439 } else if (!child.fComp) {
2440 out = acquireNew<RooProdPdf>(child.GetName(),
2441 (strlen(child.GetTitle())) ? child.GetTitle() : child.GetName(), RooArgList());
2442 Info("Add", "Created channel RooProdPdf::%s in workspace %s", out->GetName(), get()->GetName());
2443 }
2444 return xRooNode(out, *this);
2445 }
2446 } else if (sOpt == "sample" || sOpt == "func") {
2447 if (get<RooProdPdf>()) {
2448 auto _mainChild = mainChild();
2449 if (_mainChild.get<RooRealSumPdf>()) {
2450 return _mainChild.Add(child, sOpt == "func" ? "func" : "");
2451 } else {
2452 return (*this)["samples"]->Add(child, sOpt == "func" ? "func" : "");
2453 }
2454 }
2455 } else if (sOpt == "dataset") {
2456 if (get<RooWorkspace>()) {
2457 // const_cast<xRooNode&>(child).fFolder = "!datasets";return Add(child);
2458 return (*this).datasets().Add(child);
2459 }
2460 }
2461
2462 if (considerType) {
2463
2464 // interpret 'adding' here as dependent on the object type ...
2465 if (get<RooSimultaneous>()) {
2466 return bins().Add(child);
2467 } else if (TString(child.GetName()).Contains('=')) {
2468 return variations().Add(child);
2469 } else if (get<RooProduct>() || get<RooProdPdf>()) {
2470 return factors().Add(child);
2471 }
2472 }
2473
2474 // Nov 2022 - removed ability to add placeholders ... could bring back if rediscover need for them
2475 // if (!child.get() && child.empty() && strlen(child.GetName())) {
2476 // // can add a 'placeholder' node, note it will be deleted at the next browse
2477 // xRooNode out(child.GetName(),nullptr,*this);
2478 // out.SetTitle(child.GetTitle());
2479 // emplace_back(std::make_shared<xRooNode>(out));
2480 // // update the parent in the out node so that it's copy of the parent knows it has itself in it
2481 // // actually maybe not want this :-/
2482 // //out.fParent = std::make_shared<Node2>(*this);
2483 // for(auto o : *gROOT->GetListOfBrowsers()) {
2484 // if(auto b = dynamic_cast<TBrowser*>(o); b && b->GetBrowserImp()){
2485 // if(auto _b = dynamic_cast<TGFileBrowser*>(
2486 // dynamic_cast<TRootBrowser*>(b->GetBrowserImp())->fActBrowser ); _b) {
2487 // auto _root = _b->fRootDir;
2488 // if (!_root) _root = _b->fListTree->GetFirstItem();
2489 // if (auto item = _b->fListTree->FindItemByObj(_root,this); item) {
2490 // _b->fListTree->AddItem(item,back()->GetName(),back().get());
2491 // }
2492 // }
2493 // }
2494 // }
2495 // return out;
2496 // }
2497
2498 throw std::runtime_error(TString::Format("Cannot add %s to %s", child.GetName(), GetName()));
2499}
2500
2501std::string xRooNode::GetPath() const
2502{
2503 if (!fParent)
2504 return GetName();
2505 return fParent->GetPath() + "/" + GetName();
2506}
2507
2509{
2510 // std::cout << "deleting " << GetPath() << std::endl;
2511}
2512
2514{
2515 if (auto a = get<RooAbsArg>()) {
2516 a->setAttribute("hidden", set);
2517 // if(auto item = GetTreeItem(nullptr); item) {
2518 // if(set) item->SetColor(kRed);
2519 // else item->ClearColor();
2520 // }
2521 }
2522}
2524{
2525 auto a = get<RooAbsArg>();
2526 if (a)
2527 return a->getAttribute("hidden");
2528 return false;
2529}
2530
2531xRooNode xRooNode::Combine(const xRooNode &rhs, bool silent)
2532{
2533
2534 if (get() == rhs.get()) {
2535 // nothing to do because objects are identical
2536 return *this;
2537 }
2538
2539 if (auto lhsa = get<RooAbsArg>(), rhsa = rhs.get<RooAbsArg>(); lhsa && rhsa && lhsa->isIdentical(*rhsa)) {
2540 return *this;
2541 }
2542
2543 if (get<RooWorkspace>() && rhs.get<RooWorkspace>()) {
2544
2545 // report which top-level pdfs will be combined
2546 std::set<std::string> pdfs;
2547 for (auto &c : rhs.components()) {
2548 if ((*this)["pdfs"]->find(c->GetName())) {
2549 pdfs.insert(c->GetName());
2550 }
2551 }
2552 if (pdfs.empty()) {
2553 Warning("Combine", "No pdfs will be combined. Please check and/or rename pdfs to match");
2554 } else {
2555 std::stringstream s;
2556 for (auto &p : pdfs)
2557 s << p << ",";
2558 Info("Combine", "pdfs that will be combined: %s", s.str().c_str());
2559 }
2560
2561 std::set<std::string> _np;
2562 auto mynp = np();
2563 for (auto &c : rhs.np()) {
2564 if (mynp.find(c->GetName())) {
2565 _np.insert(c->GetName());
2566 }
2567 }
2568 if (_np.empty()) {
2569 Warning("Combine", "No correlated np");
2570 } else {
2571 std::stringstream s;
2572 for (auto &p : _np)
2573 s << p << ",";
2574 Info("Combine", "np that will be shared (correlated): %s", s.str().c_str());
2575 }
2576 std::set<std::string> _poi;
2577 auto mypoi = poi();
2578 for (auto &c : rhs.poi()) {
2579 if (mypoi.find(c->GetName())) {
2580 _poi.insert(c->GetName());
2581 }
2582 }
2583 if (_poi.empty()) {
2584 Warning("Combine", "No correlated poi");
2585 } else {
2586 std::stringstream s;
2587 for (auto &p : _poi)
2588 s << p << ",";
2589 Info("Combine", "poi that will be shared (correlated): %s", s.str().c_str());
2590 }
2591
2592 // TODO: Check for derived components that have matching names and aren't top-level pdfs
2593 //
2594 // const auto comps = get<RooWorkspace>()->components();
2595 //
2596 // std::set<std::string> leafs;
2597 //
2598 // for(auto& c : rhs.get<RooWorkspace>()->components()) {
2599 // if(comps.find(c->GetName())) {
2600 //
2601 // }
2602 // }
2603 }
2604
2605 // combine components, factors, and variations ... when there is a name clash will combine on that object
2606 for (auto &c : rhs.components()) {
2607 if (get<RooWorkspace>() &&
2608 (c->fFolder == "!scratch" || c->fFolder == "!sets" || c->fFolder == "!snapshots" || c->fFolder == "!models"))
2609 continue;
2610 if (auto _c = components().find(c->GetName()); _c) {
2611 if (!silent) {
2612 Info("Combine", "Combining %s into %s", c->GetPath().c_str(), _c->GetPath().c_str());
2613 }
2614 _c->Combine(*c, true);
2615 } else {
2616 try {
2617 if (!silent) {
2618 Info("Combine", "Adding %s into %s", c->GetPath().c_str(), GetPath().c_str());
2619 }
2620 Add(*c);
2621 } catch (std::exception &e) {
2622 Warning("Combine", "Could not combine %s into %s", c->GetPath().c_str(), GetPath().c_str());
2623 }
2624 }
2625 }
2626
2627 if (!get<RooWorkspace>()) { // don't combine factors of a workspace
2628 for (auto &f : rhs.factors()) {
2629 if (auto _f = factors().find(f->GetName()); _f) {
2630 if (!silent) {
2631 Info("Combine", "Combining %s into %s", f->GetPath().c_str(), _f->GetPath().c_str());
2632 }
2633 _f->Combine(*f, true);
2634 } else {
2635 if (!silent) {
2636 Info("Combine", "Multiplying %s into %s", f->GetPath().c_str(), GetPath().c_str());
2637 }
2638 Multiply(*f);
2639 }
2640 }
2641 } else {
2642 // todo: go back through components, doing sets, snapshots, models, ....
2643 // do after import of pdfs etc so that can acquire the copies from the workspace
2644 for (auto &c : rhs.components()) {
2645 if (c->fFolder == "!sets") {
2646 if (components().find(c->GetName())) {
2647 Info("Combine", "Extending set %s", c->GetName());
2648 get<RooWorkspace>()->extendSet(c->GetName(), c->get<RooAbsCollection>()->contentsString().c_str());
2649 } else {
2650 Info("Combine", "Defining set %s", c->GetName());
2651 get<RooWorkspace>()->defineSet(c->GetName(), c->get<RooAbsCollection>()->contentsString().c_str());
2652 }
2653 }
2654 }
2655
2656 // also transfer datasets
2657 for (auto &ds : rhs.datasets()) {
2658 if (auto _ds = datasets().find(ds->GetName()); _ds) {
2659 if (!silent) {
2660 Info("Combine", "Combining %s into %s", ds->GetPath().c_str(), _ds->GetPath().c_str());
2661 }
2662 _ds->Add(*ds);
2663 } else {
2664 if (!silent) {
2665 Info("Combine", "Adding %s into %s", ds->GetPath().c_str(), GetPath().c_str());
2666 }
2667 datasets().Add(*ds);
2668 }
2669 }
2670 }
2671
2672 for (auto &v : rhs.variations()) {
2673 if (auto _v = variations().find(v->GetName()); _v) {
2674 if (!silent) {
2675 Info("Combine", "Combining variation %s into %s", v->GetPath().c_str(), _v->GetPath().c_str());
2676 }
2677 _v->Combine(*v, true);
2678 } else {
2679 if (!silent) {
2680 Info("Combine", "Varying %s into %s", v->GetPath().c_str(), GetPath().c_str());
2681 }
2682 Vary(*v);
2683 }
2684 }
2685
2686 if (get<RooSimultaneous>()) {
2687 // combine bins (channels) ... special case, never done silently
2688 for (auto &b : rhs.bins()) {
2689 if (auto _b = bins().find(b->GetName()); _b) {
2690 Info("Combine", "Combining %s into %s", b->GetPath().c_str(), _b->GetPath().c_str());
2691 _b->Combine(*b, true);
2692 } else {
2693 Info("Combine", "Extending with %s into %s", b->GetPath().c_str(), GetPath().c_str());
2694 Vary(*b); // extending channels currently done through Vary method
2695 }
2696 }
2697 }
2698
2699 // todo: Should also transfer over binnings of observables
2700
2701 return *this;
2702}
2703
2704xRooNode xRooNode::shallowCopy(const std::string &name, std::shared_ptr<xRooNode> parent)
2705{
2706 xRooNode out(name.c_str(), nullptr,
2707 parent /*? parent : fParent -- was passing fParent for getObject benefit before fProvider concept*/);
2708 // if(!parent) out.fAcquirer = true;
2709 if (!parent)
2710 out.fProvider = fParent;
2711
2712 auto o = get();
2713 if (!o) {
2714 return out;
2715 }
2716
2717 if (auto s = get<RooSimultaneous>(); s) {
2718 auto chans = bins();
2719 if (!chans.empty()) {
2720 // create a new RooSimultaneous with shallow copies of each channel
2721
2722 std::shared_ptr<RooSimultaneous> pdf = out.acquire<RooSimultaneous>(
2723 name.c_str(), o->GetTitle(), const_cast<RooAbsCategoryLValue &>(s->indexCat()));
2724
2725 for (auto &c : chans) {
2726 TString cName(c->GetName());
2727 cName = cName(cName.Index('=') + 1, cName.Length());
2728 // by passing out as the parent, will ensure out acquires everything created
2729 auto c_copy =
2730 c->shallowCopy(name + "_" + c->get()->GetName(), std::shared_ptr<xRooNode>(&out, [](xRooNode *) {}));
2731 pdf->addPdf(*dynamic_cast<RooAbsPdf *>(c_copy.get()), cName);
2732 }
2733 out.fComp = pdf;
2734 return out;
2735 }
2736 } else if (auto p = dynamic_cast<RooProdPdf *>(o); p) {
2737 // main pdf will be copied too
2738 std::shared_ptr<RooProdPdf> pdf =
2739 std::dynamic_pointer_cast<RooProdPdf>(out.acquire(std::shared_ptr<TObject>(p->Clone(/*name.c_str()*/)), false,
2740 true)); // use clone to copy all attributes etc too
2741 auto main = mainChild();
2742 if (main) {
2743 auto newMain =
2744 std::dynamic_pointer_cast<RooAbsArg>(out.acquire(std::shared_ptr<TObject>(main->Clone()), false, true));
2745 std::cout << newMain << " " << newMain->GetName() << std::endl;
2746 // pdf->replaceServer(*pdf->pdfList().find(main->GetName()), *newMain, true, true);
2747 // const_cast<RooArgList&>(pdf->pdfList()).replace(*pdf->pdfList().find(main->GetName()), *newMain);
2748 pdf->redirectServers(RooArgList(*newMain));
2749 }
2750 out.fComp = pdf;
2751 out.sterilize();
2752 return out;
2753 }
2754
2755 return out;
2756}
2757
2759{
2760 static std::unique_ptr<cout_redirect> capture;
2761 std::string captureStr;
2762 bool doCapture = false;
2763 if (!capture && gROOT->FromPopUp()) { // FromPopUp means user executed from the context menu
2764 capture = std::make_unique<cout_redirect>(captureStr);
2765 doCapture = true;
2766 }
2767
2768 TString sOpt(opt);
2769 int depth = 0;
2770 if (sOpt.Contains("depth=")) {
2771 depth = TString(sOpt(sOpt.Index("depth=") + 6, sOpt.Length())).Atoi();
2772 sOpt.ReplaceAll(TString::Format("depth=%d", depth), "");
2773 }
2774 int indent = 0;
2775 if (sOpt.Contains("indent=")) {
2776 indent = TString(sOpt(sOpt.Index("indent=") + 7, sOpt.Length())).Atoi();
2777 sOpt.ReplaceAll(TString::Format("indent=%d", indent), "");
2778 }
2779 bool _more = sOpt.Contains("m");
2780 if (_more)
2781 sOpt.Replace(sOpt.Index("m"), 1, "");
2782 if (sOpt != "")
2783 _more = true;
2784
2785 if (indent == 0) { // only print self if not indenting (will already be printed above if tree traverse)
2786 std::cout << GetPath();
2787 if (get() && get() != this) {
2788 std::cout << ": ";
2789 if (_more || (get<RooAbsArg>() && get<RooAbsArg>()->isFundamental()) || get<RooConstVar>() ||
2790 get<RooAbsData>() || get<RooProduct>() || get<RooFitResult>()) {
2791 auto _deps = coords(false).argList(); // want to revert coords after print
2792 auto _snap = std::unique_ptr<RooAbsCollection>(_deps.snapshot());
2793 coords(); // move to coords before printing (in case this matters)
2794 get()->Print(sOpt);
2795 if (auto _fr = get<RooFitResult>(); _fr && dynamic_cast<RooStringVar *>(_fr->constPars().find(".log"))) {
2796 std::cout << "Minimization Logs:" << std::endl;
2797 std::cout << dynamic_cast<RooStringVar *>(_fr->constPars().find(".log"))->getVal() << std::endl;
2798 }
2799 _deps.assignValueOnly(*_snap);
2800 // std::cout << std::endl;
2801 } else {
2802 TString _suffix = "";
2803 if (auto _type = GetNodeType(); strlen(_type)) {
2804 // decided not to show const values until figure out how to update if value changes
2805 /*if (TString(_type)=="Const") _name += TString::Format("
2806 [%s=%g]",_type,v->get<RooConstVar>()->getVal()); else*/
2807 _suffix += TString::Format(" [%s]", _type);
2808 }
2809 if (auto fv = get<RooFormulaVar>()) {
2810 TString formu = TString::Format(" [%s]", fv->expression());
2811 for (size_t i = 0; i < fv->dependents().size(); i++) {
2812 formu.ReplaceAll(TString::Format("x[%zu]", i), fv->dependents()[i].GetName());
2813 }
2814 _suffix += formu;
2815 } else if (auto gv = get<RooGenericPdf>()) {
2816 TString formu = TString::Format(" [%s]", gv->expression());
2817 for (size_t i = 0; i < gv->dependents().size(); i++) {
2818 formu.ReplaceAll(TString::Format("x[%zu]", i), gv->dependents()[i].GetName());
2819 }
2820 _suffix += formu;
2821 } else if (auto pi = get<PiecewiseInterpolation>()) {
2822 // check if all interpCodes are the same. Will include in the NodeType
2823 std::set<int> interpCodes;
2824 for (auto &c : pi->interpolationCodes())
2825 interpCodes.insert(c);
2826 if (interpCodes.size() == 1) {
2827 _suffix += TString::Format(" [InterpCode=%d]", *interpCodes.begin());
2828 }
2829 } else if (auto fiv = get<RooStats::HistFactory::FlexibleInterpVar>()) {
2830 // check if all interpCodes are the same.
2831 std::set<int> interpCodes;
2832 for (auto &c : fiv->interpolationCodes())
2833 interpCodes.insert(c == 4 ? 5 : c); // in definition of FlexibleInterpVar 4 gets replaced with 5
2834 if (interpCodes.size() == 1) {
2835 _suffix += TString::Format(" [InterpCode=%d]", *interpCodes.begin());
2836 }
2837 }
2838 std::cout << get()->ClassName() << "::" << get()->GetName() << _suffix.Data() << std::endl;
2839 }
2840
2841 } else if (!get()) {
2842 std::cout << std::endl;
2843 }
2844 }
2845 const_cast<xRooNode *>(this)->browse();
2846 std::vector<std::string> folderNames;
2847 for (auto &k : *this) {
2848 if (std::find(folderNames.begin(), folderNames.end(), k->fFolder) == folderNames.end()) {
2849 folderNames.push_back(k->fFolder);
2850 }
2851 }
2852 for (auto &f : folderNames) {
2853 int i = 0;
2854 int iindent = indent;
2855 if (!f.empty()) {
2856 for (int j = 0; j < indent; j++)
2857 std::cout << " ";
2858 std::cout << f << std::endl;
2859 iindent += 1;
2860 }
2861 for (auto &k : *this) {
2862 if (k->fFolder != f) {
2863 i++;
2864 continue;
2865 }
2866 for (int j = 0; j < iindent; j++)
2867 std::cout << " ";
2868 std::cout << i++ << ") " << k->GetName() << " : ";
2869 if (k->get()) {
2870 if (_more || (k->get<RooAbsArg>() && k->get<RooAbsArg>()->isFundamental()) || k->get<RooConstVar>() ||
2871 k->get<RooAbsData>() /*|| k->get<RooProduct>()*/) {
2872 auto _deps = k->coords(false).argList();
2873 auto _snap = std::unique_ptr<RooAbsCollection>(_deps.snapshot());
2874 k->coords(); // move to coords before printing (in case this matters)
2875 k->get()->Print(sOpt); // assumes finishes with an endl
2876 _deps.assignValueOnly(*_snap);
2877 } else {
2878 TString _suffix = "";
2879 if (auto _type = k->GetNodeType(); strlen(_type)) {
2880 // decided not to show const values until figure out how to update if value changes
2881 /*if (TString(_type)=="Const") _name += TString::Format("
2882 [%s=%g]",_type,v->get<RooConstVar>()->getVal()); else*/
2883 _suffix += TString::Format(" [%s]", _type);
2884 }
2885 if (auto fv = k->get<RooFormulaVar>()) {
2886 TString formu = TString::Format(" [%s]", fv->expression());
2887 for (size_t j = 0; j < fv->dependents().size(); j++) {
2888 formu.ReplaceAll(TString::Format("x[%zu]", j), fv->dependents()[j].GetName());
2889 }
2890 _suffix += formu;
2891 } else if (auto gv = k->get<RooGenericPdf>()) {
2892 TString formu = TString::Format(" [%s]", gv->expression());
2893 for (size_t j = 0; j < gv->dependents().size(); j++) {
2894 formu.ReplaceAll(TString::Format("x[%zu]", j), gv->dependents()[j].GetName());
2895 }
2896 _suffix += formu;
2897 } else if (auto pi = k->get<PiecewiseInterpolation>()) {
2898 // check if all interpCodes are the same. Will include in the NodeType
2899 std::set<int> interpCodes;
2900 for (auto &c : pi->interpolationCodes())
2901 interpCodes.insert(c);
2902 if (interpCodes.size() == 1) {
2903 _suffix += TString::Format(" [InterpCode=%d]", *interpCodes.begin());
2904 }
2905 } else if (auto fiv = k->get<RooStats::HistFactory::FlexibleInterpVar>()) {
2906 // check if all interpCodes are the same.
2907 std::set<int> interpCodes;
2908 for (auto &c : fiv->interpolationCodes())
2909 interpCodes.insert(c == 4 ? 5 : c); // in definition of FlexibleInterpVar 4 gets replaced with 5
2910 if (interpCodes.size() == 1) {
2911 _suffix += TString::Format(" [InterpCode=%d]", *interpCodes.begin());
2912 }
2913 }
2914 std::cout << k->get()->ClassName() << "::" << k->get()->GetName() << _suffix.Data() << std::endl;
2915 }
2916 if (depth != 0) {
2917 k->Print(sOpt + TString::Format("depth=%dindent=%d", depth - 1, iindent + 1));
2918 }
2919 } else
2920 std::cout << " NULL " << std::endl;
2921 }
2922 }
2923 if (doCapture) {
2924 capture.reset(); // no captureStr has the string to display
2925 // inject line breaks to avoid msgbox being too wide
2926 size_t lastBreak = 0;
2927 std::string captureStrWithBreaks;
2928 for (size_t i = 0; i < captureStr.size(); i++) {
2929 captureStrWithBreaks += captureStr[i];
2930 if (captureStr[i] == '\n') {
2931 lastBreak = i;
2932 }
2933 if (i - lastBreak > 150) {
2934 captureStrWithBreaks += '\n';
2935 lastBreak = i;
2936 }
2937 }
2938 const TGWindow *w =
2939 (gROOT->GetListOfBrowsers()->At(0))
2940 ? dynamic_cast<TGWindow *>(static_cast<TBrowser *>(gROOT->GetListOfBrowsers()->At(0))->GetBrowserImp())
2941 : gClient->GetRoot();
2942 new TGMsgBox(gClient->GetRoot(), w, GetName(),
2943 captureStrWithBreaks.c_str()); //,nullptr,kMBDismiss,nullptr,kVerticalFrame,kTextLeft|kTextCenterY);
2944 }
2945}
2946
2948{
2949 if (!child.get()) {
2950
2951 if (auto v = get<RooRealVar>(); v) {
2952
2953 TString constrType = child.GetName();
2954 double mean = std::numeric_limits<double>::quiet_NaN();
2955 double sigma = mean;
2956 if (constrType.BeginsWith("gaussian(")) {
2957 // extract the mean and stddev parameters
2958 // if only one given, it is the stddev
2959 if (constrType.Contains(",")) {
2960 mean = TString(constrType(9, constrType.Index(',') - 9)).Atof();
2961 sigma = TString(constrType(constrType.Index(',') + 1, constrType.Index(')') - constrType.Index(',') + 1))
2962 .Atof();
2963 } else {
2964 mean = std::numeric_limits<double>::quiet_NaN(); // will use the var current value below to set mean
2965 sigma = TString(constrType(9, constrType.Index(')') - 9)).Atof();
2966 }
2967 constrType = "normal";
2968 } else if (constrType == "normal") {
2969 mean = 0;
2970 sigma = 1;
2971 } else if (constrType == "gaussian") {
2972 // extract parameters from the variable
2973 // use current value and error on v as constraint
2974 if (!v->hasError())
2975 throw std::runtime_error("No error on parameter for gaussian constraint");
2976 sigma = v->getError();
2977 mean = v->getVal();
2978 constrType = "normal";
2979 } else if (constrType == "poisson") {
2980 if (!v->hasError())
2981 throw std::runtime_error("No error on parameter for poisson constraint");
2982 mean = 1;
2983 sigma = pow(v->getVal() / v->getError(), 2);
2984 }
2985
2986 if (constrType == "poisson") {
2987 // use current value and error on v as constraint
2988 double tau_val = sigma;
2989 auto globs = acquire<RooRealVar>(Form("globs_%s", v->GetName()), Form("globs_%s", v->GetName()),
2990 v->getVal() * tau_val, (v->getVal() - 5 * v->getError()) * tau_val,
2991 (v->getVal() + 5 * v->getError()) * tau_val);
2992 globs->setConstant();
2993 globs->setAttribute("obs");
2994 globs->setAttribute("global");
2995 globs->setStringAttribute("nominal", TString::Format("%f", tau_val));
2996 auto tau = acquireNew<RooConstVar>(TString::Format("tau_%s", v->GetName()), "", tau_val);
2997 auto constr = acquireNew<RooPoisson>(
2998 Form("pois_%s", v->GetName()), TString::Format("Poisson Constraint of %s", v->GetTitle()), *globs,
2999 *acquireNew<RooProduct>(TString::Format("mean_%s", v->GetName()),
3000 TString::Format("Poisson Constraint of %s", globs->GetTitle()),
3001 RooArgList(*v, *tau)),
3002 true /* no rounding */);
3003
3004 auto out = Constrain(xRooNode(Form("pois_%s", GetName()), constr));
3005 if (!v->hasError())
3006 v->setError(mean / sqrt(tau_val)); // if v doesnt have an uncert, will put one on it now
3007 Info("Constrain", "Added poisson constraint pdf RooPoisson::%s (tau=%g) for %s", out->GetName(), tau_val,
3008 GetName());
3009 return out;
3010 } else if (constrType == "normal") {
3011
3012 auto globs = acquire<RooRealVar>(Form("globs_%s", v->GetName()), Form("globs_%s", v->GetName()), mean,
3013 mean - 10 * sigma, mean + 10 * sigma);
3014 globs->setAttribute("obs");
3015 globs->setAttribute("global");
3016 globs->setConstant();
3017
3018 globs->setStringAttribute("nominal", TString::Format("%f", mean));
3019 auto constr = acquireNew<RooGaussian>(
3020 Form("gaus_%s", v->GetName()), TString::Format("Gaussian Constraint of %s", v->GetTitle()), *globs, *v,
3021 *acquireNew<RooConstVar>(TString::Format("sigma_%s", v->GetName()), "", sigma));
3022 auto out = Constrain(xRooNode(Form("gaus_%s", GetName()), constr));
3023 if (!v->hasError())
3024 v->setError(sigma); // if v doesnt have an uncert, will put one on it now
3025 Info("Constrain", "Added gaussian constraint pdf RooGaussian::%s (mean=%g,sigma=%g) for %s", out->GetName(),
3026 mean, sigma, GetName());
3027 return out;
3028 }
3029 }
3030 } else if (auto p = child.get<RooAbsPdf>(); p) {
3031
3032 auto _me = get<RooAbsArg>();
3033 if (!_me) {
3034 throw std::runtime_error("Cannot constrain non arg");
3035 }
3036
3037 if (!p->dependsOn(*_me)) {
3038 throw std::runtime_error("Constraint does not depend on constrainee");
3039 }
3040
3041 // find a parent that can swallow this pdf ... either a RooProdPdf or a RooWorkspace
3042 auto x = fParent;
3043 while (x && !x->get<RooProdPdf>() && !x->get<RooSimultaneous>() && !x->get<RooWorkspace>()) {
3044 x = x->fParent;
3045 }
3046 if (!x) {
3047 throw std::runtime_error("Nowhere to put constraint");
3048 }
3049 // get datasets of the swallower, and add glob to any globs lists
3050 auto childGlobs = child.globs();
3051 if (!childGlobs.empty()) {
3052 for (auto d : x->datasets()) {
3053 if (auto globs = d->get<RooAbsData>()->getGlobalObservables()) {
3054 RooArgSet newGlobs(*globs);
3055 newGlobs.add(*childGlobs.get<RooArgList>());
3056 d->get<RooAbsData>()->setGlobalObservables(newGlobs);
3057 }
3058 }
3059 // also add to the workspaces globalObservables lists
3060 if (x->ws()) {
3061 for (auto &[k, v] : GETWSSETS(x->ws())) {
3062 if (k == "globalObservables" || TString(k).EndsWith("_GlobalObservables")) {
3063 const_cast<RooArgSet &>(v).add(*childGlobs.get<RooArgList>());
3064 }
3065 }
3066 }
3067 }
3068 if (auto s = x->get<RooSimultaneous>(); s) {
3069 // put into every channel that features parameter
3070 x->browse();
3071 for (auto &c : *x) {
3072 if (auto a = c->get<RooAbsArg>(); a->dependsOn(*_me))
3073 c->Multiply(child);
3074 }
3075 return child;
3076 } else if (x->get<RooProdPdf>()) {
3077 return x->Multiply(child);
3078 } else {
3079 return x->Add(child, "+");
3080 }
3081 }
3082
3083 throw std::runtime_error(TString::Format("Cannot constrain %s", GetName()));
3084}
3085
3087{
3088
3089 class AutoUpdater {
3090 public:
3091 AutoUpdater(xRooNode &_n) : n(_n) {}
3092 ~AutoUpdater() { n.browse(); }
3093 xRooNode &n;
3094 };
3095 AutoUpdater xxx(*this);
3096
3097 if (fBinNumber != -1) {
3098 // scaling a bin ...
3099 if (child.get<RooAbsReal>()) { // if not child then let fall through to create a child and call self again below
3100 // doing a bin-multiplication .. the parent should have a ParamHistFunc called binFactors
3101 // if it doesn't then create one
3102 auto o = std::dynamic_pointer_cast<RooAbsReal>(acquire(child.fComp));
3103
3104 // get binFactor unless parent is a ParamHistFunc already ...
3105
3106 auto binFactors = (fParent->get<ParamHistFunc>()) ? fParent : fParent->factors().find("binFactors");
3107
3108 // it can happen in a loop over bins() that another node has moved fParent inside a product
3109 // so check for fParent having a client with the ORIGNAME:<name> attribute
3110 if (!binFactors && fParent->get<RooAbsArg>()) {
3111 for (auto c : fParent->get<RooAbsArg>()->clients()) {
3112 if (c->IsA() == RooProduct::Class() &&
3113 c->getAttribute(TString::Format("ORIGNAME:%s", fParent->get()->GetName()))) {
3114 // try getting binFactors out of this
3115 binFactors = xRooNode(*c).factors().find("binFactors");
3116 break;
3117 }
3118 }
3119 }
3120
3121 if (!binFactors) {
3122 fParent
3123 ->Multiply(TString::Format("%s_binFactors",
3124 (fParent->mainChild().get())
3125 ? fParent->mainChild()->GetName()
3126 : (fParent->get() ? fParent->get()->GetName() : fParent->GetName()))
3127 .Data(),
3128 "blankshape")
3129 .SetName("binFactors"); // creates ParamHistFunc with all pars = 1 (shared const)
3130 binFactors = fParent->factors().find("binFactors");
3131 if (!binFactors) {
3132 throw std::runtime_error(
3133 TString::Format("Could not create binFactors in parent %s", fParent->GetName()));
3134 }
3135 // auto phf = binFactors->get<ParamHistFunc>();
3136
3137 // create RooProducts for all the bins ... so that added factors don't affect selves
3138 int i = 1;
3139 for (auto &b : binFactors->bins()) {
3140 auto p = acquireNew<RooProduct>(TString::Format("%s_bin%d", binFactors->get()->GetName(), i),
3141 TString::Format("binFactors of bin %d", i), RooArgList());
3142 p->setStringAttribute("alias", TString::Format("%s=%g", binFactors->GetXaxis()->GetParent()->GetName(),
3143 binFactors->GetXaxis()->GetBinCenter(i)));
3144 b->Multiply(*p);
3145 i++;
3146 }
3147 }
3148 // then scale the relevant bin ... if the relevant bin is a "1" then just drop in our factor (inside a
3149 // RooProduct though, to avoid it getting modified by subsequent multiplies)
3150 auto _bin = binFactors->bins().at(fBinNumber - 1);
3151 if (auto phf = binFactors->get<ParamHistFunc>(); phf && _bin) {
3152#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
3153 RooArgList &pSet = phf->_paramSet;
3154#else
3155 RooArgList &pSet = const_cast<RooArgList &>(phf->paramList());
3156#endif
3157 if (strcmp(_bin->GetName(), "1") == 0) {
3158 RooArgList all;
3159 for (std::size_t i = 0; i < pSet.size(); i++) {
3160 if (int(i) != fBinNumber - 1) {
3161 all.add(*pSet.at(i));
3162 } else {
3163 all.add(*o);
3164 }
3165 }
3166 pSet.removeAll();
3167 pSet.add(all);
3168 } else {
3169 _bin->fBinNumber = -1; // to avoid infinite loop
3170 return _bin->Multiply(child, opt);
3171 }
3172 // } else {else if(_bin->get<RooProduct>()) {
3173 // // multiply the element which will just add it as a factor in the rooproduct
3174 // return _bin->Multiply(child,opt);
3175 // } else {
3176 // // not a rooproduct in this bin yet ... so need to replace with a rooproduct and
3177 // multiply that
3178 // // this avoids the undesired behaviour of shared binFactors getting all impacted by
3179 // mulitplies RooArgList all; auto new_p =
3180 // acquireNew<RooProduct>(TString::Format("%s_bin%d",binFactors->get()->GetName(),fBinNumber),TString::Format("binFactors
3181 // of bin %d",fBinNumber),RooArgList(*_bin->get<RooAbsArg>()));
3182 // new_p->setStringAttribute("alias","")
3183 // for (int i = 0; i < phf->_paramSet.size(); i++) {
3184 // if (i != fBinNumber - 1) all.add(*phf->_paramSet.at(i));
3185 // else all.add(*new_p);
3186 // }
3187 // phf->_paramSet.removeAll();
3188 // phf->_paramSet.add(all);
3189 // // now multiply that bin having converted it to RooProduct
3190 // return binFactors->bins().at(fBinNumber - 1)->Multiply(child,opt);
3191 // }
3192 }
3193 return xRooNode(*o, binFactors);
3194 }
3195 } else if (!get() && fParent) {
3196 // try to 'create' object based on parentage
3197 // add child as a temporary child to help with decision making
3198 auto _ref = emplace_back(std::shared_ptr<xRooNode>(&const_cast<xRooNode &>(child), [](TObject *) {}));
3199 try {
3200 fComp = fParent->Add(*this, "+").fComp;
3201 } catch (...) {
3202 resize(size() - 1);
3203 std::rethrow_exception(std::current_exception());
3204 }
3205 resize(size() - 1); // remove the temporarily added node
3206 }
3207
3208 if (!child.get()) {
3209 TString sOpt(opt);
3210 sOpt.ToLower();
3211 if (auto o = getObject<RooAbsReal>(child.GetName())) {
3212 auto out = Multiply(xRooNode(o, child.fParent));
3213 // have to protect bin case where get() is null (could change but then must change logic above too)
3214 if (get()) {
3215 Info("Multiply", "Scaled %s by existing factor %s::%s",
3216 mainChild().get() ? mainChild().get()->GetName() : get()->GetName(), o->ClassName(), o->GetName());
3217 }
3218 return out;
3219 } else if (sOpt == "const") {
3220 auto out = Multiply(RooConstVar(child.GetName(), child.GetTitle(), 1));
3221 if (get()) {
3222 Info("Multiply", "Scaled %s by new const factor %s",
3223 mainChild().get() ? mainChild().get()->GetName() : get()->GetName(), out->GetName());
3224 }
3225 return out;
3226 } else if (sOpt == "norm") {
3227 if (TString(child.GetName()).Contains("[") && ws()) {
3228 // assume factory method wanted
3229 auto arg = ws()->factory(child.GetName());
3230 if (arg) {
3231 auto out = Multiply(*arg);
3232 if (get()) {
3233 Info("Multiply", "Scaled %s by new norm factor %s",
3234 mainChild().get() ? mainChild().get()->GetName() : get()->GetName(), out->GetName());
3235 }
3236 return out;
3237 }
3238 throw std::runtime_error(TString::Format("Failed to create new normFactor %s", child.GetName()));
3239 }
3240 auto out = Multiply(RooRealVar(child.GetName(), child.GetTitle(), 1, -1e-5, 100));
3241 if (get()) {
3242 Info("Multiply", "Scaled %s by new norm factor %s",
3243 mainChild().get() ? mainChild().get()->GetName() : get()->GetName(), out->GetName());
3244 }
3245 return out;
3246 } else if (sOpt == "shape" || sOpt == "simple" || sOpt == "blankshape") {
3247 // needs axis defined
3248 if (auto ax = GetXaxis(); ax) {
3249 auto h = std::shared_ptr<TH1>(BuildHistogram(dynamic_cast<RooAbsLValue *>(ax->GetParent()), true));
3250 h->Reset();
3251 for (int i = 1; i <= h->GetNbinsX(); i++) {
3252 h->SetBinContent(i, 1);
3253 }
3254 h->SetMinimum(0);
3255 h->SetMaximum(100);
3256 h->SetName(TString::Format(";%s", child.GetName())); // ; char indicates don't "rename" this thing
3257 h->SetTitle(child.GetTitle());
3258 if (sOpt.Contains("shape"))
3259 h->SetOption(sOpt);
3260 auto out = Multiply(*h);
3261 if (get()) {
3262 Info("Multiply", "Scaled %s by new %s factor %s",
3263 mainChild().get() ? mainChild().get()->GetName() : get()->GetName(), sOpt.Data(), out->GetName());
3264 }
3265 return out;
3266 }
3267 } else if (sOpt == "overall") {
3268 auto out = Multiply(acquireNew<RooStats::HistFactory::FlexibleInterpVar>(
3269 child.GetName(), child.GetTitle(), RooArgList(), 1, std::vector<double>(), std::vector<double>()));
3270 if (get() /* can happen this is null if on a bin node with no shapeFactors*/) {
3271 Info("Multiply", "Scaled %s by new overall factor %s",
3272 mainChild().get() ? mainChild().get()->GetName() : get()->GetName(), out->GetName());
3273 }
3274 return out;
3275 } else if (sOpt == "func" && ws()) {
3276 // need to get way to get dependencies .. can't pass all as causes circular dependencies issues.
3277 if (auto arg = ws()->factory(TString("expr::") + child.GetName())) {
3278 auto out = Multiply(*arg);
3279 if (get() /* can happen this is null if on a bin node with no shapeFactors*/) {
3280 Info("Multiply", "Scaled %s by new func factor %s",
3281 mainChild().get() ? mainChild().get()->GetName() : get()->GetName(), out->GetName());
3282 }
3283 return out;
3284 }
3285 }
3286 }
3287 if (auto h = child.get<TH1>(); h && strlen(h->GetOption()) == 0 && strlen(opt) > 0) {
3288 // put the option in the hist
3289 h->SetOption(opt);
3290 }
3291 if (auto w = get<RooWorkspace>(); w) {
3292 // just acquire
3293 std::shared_ptr<TObject> out;
3294 child.convertForAcquisition(*this);
3295 if (child.get<RooAbsReal>())
3296 out = acquire(child.fComp);
3297 return out;
3298 }
3299
3300 if (strcmp(GetName(), ".coef") == 0) { // covers both .coef and .coefs
3301 // need to add this into the relevant coef ... if its not a RooProduct, replace it with one first
3302 if (auto p = fParent->fParent->get<RooAddPdf>()) {
3303 // may be in no-coef mode ... in which case must create coefs (use "ExtendedBindings" but note that these need
3304 // obs list passing to them
3305 if (p->coefList().empty() && !p->pdfList().empty()) {
3306 for (auto _pdf : p->pdfList()) {
3307 const_cast<RooArgList &>(p->coefList())
3308 .add(*acquireNew<RooExtendedBinding>(TString::Format("%s_extBind", _pdf->GetName()),
3309 TString::Format("Expected Events of %s", _pdf->GetTitle()),
3310 *static_cast<RooAbsPdf *>(_pdf)));
3311 }
3312 Info("Multiply", "Created RooExtendedBinding coefficients for all pdfs of %s so that can multiply coef",
3313 p->GetName());
3314 *reinterpret_cast<bool *>(reinterpret_cast<unsigned char *>(p) +
3315 p->Class()->GetDataMemberOffset("_allExtendable")) = false;
3316 *reinterpret_cast<bool *>(reinterpret_cast<unsigned char *>(p) +
3317 p->Class()->GetDataMemberOffset("_haveLastCoef")) = true;
3318 }
3319 for (size_t i = 0; i < p->pdfList().size(); i++) {
3320 if (p->pdfList().at(i) == fParent->get<RooAbsArg>()) {
3321 auto coefs = p->coefList().at(i);
3322 if (!coefs->InheritsFrom("RooProduct")) {
3323 RooArgList oldCoef;
3324 if (!(strcmp(coefs->GetName(), "1") == 0 || strcmp(coefs->GetName(), "ONE") == 0))
3325 oldCoef.add(*coefs);
3326 auto newCoefs = fParent->acquireNew<RooProduct>(
3327 TString::Format("coefs_%s", fParent->GetName()),
3328 TString::Format("coefficients for %s", fParent->GetName()), oldCoef);
3329 RooArgList oldCoefs;
3330 for (size_t j = 0; j < p->coefList().size(); j++) {
3331 if (i == j) {
3332 oldCoefs.add(*newCoefs);
3333 } else {
3334 oldCoefs.add(*p->coefList().at(j));
3335 }
3336 }
3337 const_cast<RooArgList &>(p->coefList()).removeAll();
3338 const_cast<RooArgList &>(p->coefList()).add(oldCoefs);
3339 coefs = newCoefs.get();
3340 }
3341 return xRooNode(*coefs, fParent).Multiply(child);
3342 }
3343 }
3344 } else if (auto p2 = fParent->fParent->get<RooRealSumPdf>()) {
3345 // find our function in the funcList, and then update the coefs of it
3346
3347 for (size_t i = 0; i < p2->funcList().size(); i++) {
3348 if (p2->funcList().at(i) == fParent->get<RooAbsArg>()) {
3349 auto coefs = p2->coefList().at(i);
3350 if (!coefs->InheritsFrom("RooProduct")) {
3351 RooArgList oldCoef;
3352 if (!(strcmp(coefs->GetName(), "1") == 0 || strcmp(coefs->GetName(), "ONE") == 0))
3353 oldCoef.add(*coefs);
3354 auto newCoefs = fParent->acquireNew<RooProduct>(
3355 TString::Format("coefs_%s", fParent->GetName()),
3356 TString::Format("coefficients for %s", fParent->GetName()), oldCoef);
3357 RooArgList oldCoefs;
3358 for (size_t j = 0; j < p2->coefList().size(); j++) {
3359 if (i == j) {
3360 oldCoefs.add(*newCoefs);
3361 } else {
3362 oldCoefs.add(*p2->coefList().at(j));
3363 }
3364 }
3365 const_cast<RooArgList &>(p2->coefList()).removeAll();
3366 const_cast<RooArgList &>(p2->coefList()).add(oldCoefs);
3367 coefs = newCoefs.get();
3368 }
3369 return xRooNode(*coefs, fParent).Multiply(child);
3370 }
3371 }
3372 }
3373 throw std::runtime_error("this coefs case is not supported");
3374 }
3375
3376 if (auto p = get<RooProduct>(); p) {
3377 std::shared_ptr<TObject> out;
3378 auto cc = child.fComp;
3379 bool isConverted = (child.convertForAcquisition(*this) != cc);
3380 if (child.get<RooAbsReal>())
3381 out = acquire(child.fComp);
3382
3383 // child may be a histfunc or a rooproduct of a histfunc and a paramhist if has stat errors
3384 if (auto _f = std::dynamic_pointer_cast<RooHistFunc>(
3385 (child.get<RooProduct>()) ? child.factors()[child.GetName()]->fComp : out);
3386 _f && _f->getAttribute("autodensity")) {
3387 // should we flag this as a density? yes if there's no other term marked as the density
3388 bool hasDensity = false;
3389 for (auto &f : factors()) {
3390 if (f->get<RooAbsArg>()->getAttribute("density")) {
3391 hasDensity = true;
3392 break;
3393 }
3394 }
3395 _f->setAttribute("density", !hasDensity && fParent && fParent->get<RooRealSumPdf>());
3396 if (_f->getAttribute("density")) {
3397
3398 // need to divide by bin widths first
3399 for (int i = 0; i < _f->dataHist().numEntries(); i++) {
3400 auto bin_pars = _f->dataHist().get(i);
3401 _f->dataHist().set(*bin_pars, _f->dataHist().weight(i) / _f->dataHist().binVolume(*bin_pars));
3402 }
3403 _f->setValueDirty();
3404
3405 // promote the axis vars to observables
3406 for (auto &x : xRooNode("tmp", _f).vars()) {
3407 x->get<RooAbsArg>()->setAttribute("obs");
3408 }
3409 }
3410 _f->setAttribute("autodensity", false);
3411 }
3412
3413 if (isConverted && child.get<RooHistFunc>()) {
3414 Info("Multiply", "Created %s factor %s in %s",
3415 child.get<RooAbsArg>()->getAttribute("density") ? "SimpleDensity" : "Simple", child->GetName(),
3416 p->GetName());
3417 } else if (isConverted && child.get<ParamHistFunc>()) {
3418 Info("Multiply", "Created Shape factor %s in %s", child->GetName(), p->GetName());
3419 }
3420
3421 if (auto _f = std::dynamic_pointer_cast<RooAbsReal>(out); _f) {
3422#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
3423 p->_compRSet.add(*_f);
3424#else
3425 const_cast<RooArgList &>(p->realComponents()).add(*_f);
3426#endif
3427 p->setValueDirty();
3428
3429 browse();
3430 xRooNode _out(_f, *this);
3431 for (auto &_par : _out.pars()) {
3432 if (auto s = _par->get<RooAbsArg>()->getStringAttribute("boundConstraint"); s) {
3433 bool found = false;
3434 for (auto &_constr : _par->constraints()) {
3435 if (strcmp(s, _constr->get()->GetName()) == 0) {
3436 // constraint is already included
3437 found = true;
3438 break;
3439 }
3440 }
3441 if (!found) {
3442 Info("Multiply", "Pulling in %s boundConstraint: %s", _par->GetName(), s);
3443 auto _pdf = getObject<RooAbsPdf>(s);
3444 if (!_pdf) {
3445 throw std::runtime_error("Couldn't find boundConstraint");
3446 }
3447 _par->Constrain(_pdf);
3448 }
3449 }
3450 }
3451 sterilize();
3452 return _out;
3453 }
3454 } else if (auto p2 = get<RooProdPdf>(); p2) {
3455
3456 std::shared_ptr<TObject> out;
3457 child.convertForAcquisition(*this);
3458 if (child.get<RooAbsPdf>()) {
3459 out = acquire(child.fComp);
3460 } else if (child.get<RooAbsReal>() && mainChild().get<RooRealSumPdf>()) {
3461 // cannot multiply a RooProdPdf by a non pdf
3462 throw std::runtime_error(TString::Format("Cannot multiply %s by non-pdf %s", GetName(), child.GetName()));
3463 // return mainChild().Add(child); - nov 2022 - used to do this but now replaced with exception above
3464 } else if (!child.get() || child.get<RooAbsReal>()) {
3465 // need to create or hide inside a sumpdf or rooadpdf
3466 std::shared_ptr<RooAbsPdf> _pdf;
3467 if (!child.get() && strcmp(child.GetName(), "components") == 0) {
3468 auto _sumpdf = acquireNew<RooAddPdf>(
3469 Form("%s_%s", p2->GetName(), child.GetName()),
3470 (strlen(child.GetTitle()) && strcmp(child.GetTitle(), child.GetName())) ? child.GetTitle()
3471 : p2->GetTitle(),
3472 RooArgList() /*, RooArgList() forces coef-mode if we specify this list */);
3473 _pdf = _sumpdf;
3474 } else {
3475 auto _sumpdf = acquireNew<RooRealSumPdf>(
3476 Form("%s_%s", p2->GetName(), child.GetName()),
3477 (strlen(child.GetTitle()) && strcmp(child.GetTitle(), child.GetName())) ? child.GetTitle()
3478 : p2->GetTitle(),
3479 RooArgList(), RooArgList(), true);
3480 _sumpdf->setFloor(true);
3481 _pdf = _sumpdf;
3482 }
3483 _pdf->setStringAttribute("alias", child.GetName());
3484 // transfer axis attributes if present (TODO: should GetXaxis look beyond the immediate parent?)
3485 _pdf->setStringAttribute("xvar", p2->getStringAttribute("xvar"));
3486 _pdf->setStringAttribute("binning", p2->getStringAttribute("binning"));
3487 out = _pdf;
3488 Info("Multiply", "Created %s::%s in channel %s", _pdf->ClassName(), _pdf->GetName(), p2->GetName());
3489 if (child.get<RooAbsReal>())
3490 xRooNode(*out, *this).Add(child);
3491 }
3492
3493 if (auto _pdf = std::dynamic_pointer_cast<RooAbsPdf>(out); _pdf) {
3494#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
3495 const_cast<RooArgList &>(p2->pdfList()).add(*_pdf);
3496#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 26, 00)
3497 p2->_pdfNSetList.emplace_back(std::make_unique<RooArgSet>("nset"));
3498#else
3499 p->_pdfNSetList.Add(new RooArgSet("nset"));
3500#endif
3501 if (!p2->canBeExtended() && _pdf->canBeExtended()) {
3502 p2->_extendedIndex = p2->_pdfList.size() - 1;
3503 }
3504#else
3505 p2->addPdfs(RooArgSet(*_pdf));
3506#endif
3507 sterilize();
3508 browse();
3509 return xRooNode(_pdf, *this);
3510 }
3511 } else if (auto p3 = get<RooRealSumPdf>(); p3) {
3512 // multiplying all current and future components
3513 std::shared_ptr<TObject> out;
3514 child.convertForAcquisition(*this);
3515 if (child.get<RooAbsReal>()) {
3516 out = acquire(child.fComp);
3517 for (auto &c : components()) {
3518 c->Multiply(out);
3519 }
3520 TString s = p3->getStringAttribute("global_factors");
3521 if (s != "")
3522 s += ";";
3523 s += out->GetName();
3524 p3->setStringAttribute("global_factors", s);
3525 Info(
3526 "Multiply",
3527 "Flagged %s as a global factor in channel %s (is applied to all current and future samples in the channel)",
3528 out->GetName(), p3->GetName());
3529 return xRooNode(out, *this);
3530 }
3531
3532 } else if (auto p4 = get<RooAbsPdf>(); p4 && !(fParent && fParent->get<RooRealSumPdf>())) {
3533 // multiply the coefs (if this isn't part of a RooAddPdf or RooRealSumPdf then we will eventually throw exception
3534 return coefs().Multiply(child);
3535 } else if (auto p5 = get<RooAbsReal>(); p5 && (!get<RooAbsPdf>() || (fParent && fParent->get<RooRealSumPdf>()))) {
3536 // replace this obj with a RooProduct to allow for multiplication
3537
3538 // get the list of clients BEFORE creating the new interpolation ... seems list of clients is inaccurate after
3539 std::set<RooAbsArg *> cl;
3540 for (auto &arg : p5->clients()) {
3541 cl.insert(arg);
3542 }
3543
3544 // if multiple clients, see if only one client is in parentage route
3545 // if so, then assume thats the only client we should replace in
3546 if (cl.size() > 1) {
3547 if (cl.count(fParent->get<RooAbsArg>()) > 0) {
3548 cl.clear();
3549 cl.insert(fParent->get<RooAbsArg>());
3550 } else {
3551 Warning("Multiply", "Scaling %s that has multiple clients", p5->GetName());
3552 }
3553 }
3554
3555 auto new_p = acquireNew<RooProduct>(TString::Format("prod_%s", p5->GetName()), p5->GetTitle(), RooArgList(*p5));
3556 // copy attributes over
3557 for (auto &a : p5->attributes())
3558 new_p->setAttribute(a.c_str());
3559 for (auto &a : p5->stringAttributes())
3560 new_p->setStringAttribute(a.first.c_str(), a.second.c_str());
3561 if (!new_p->getStringAttribute("alias"))
3562 new_p->setStringAttribute("alias", p5->GetName());
3563 auto old_p = p5;
3564 new_p->setAttribute(Form("ORIGNAME:%s", old_p->GetName())); // used in redirectServers to say what this replaces
3565 for (auto arg : cl) {
3566 arg->redirectServers(RooArgSet(*new_p), false, true);
3567 }
3568
3569 fComp = new_p;
3570 return Multiply(child);
3571 }
3572
3573 // before giving up here, assume user wanted a norm factor type if child is just a name
3574 if (!child.get() && strlen(opt) == 0)
3575 return Multiply(child, "norm");
3576
3577 throw std::runtime_error(
3578 TString::Format("Cannot multiply %s by %s%s", GetPath().c_str(), child.GetName(),
3579 (!child.get() && strlen(opt) == 0) ? " (forgot to specify factor type?)" : ""));
3580}
3581
3583{
3584
3585 auto p5 = get<RooAbsArg>();
3586 if (!p5) {
3587 throw std::runtime_error("Only replacement of RooAbsArg is supported");
3588 }
3589 node.convertForAcquisition(*this, "func");
3590
3591 auto new_p = node.get<RooAbsArg>();
3592 if (!new_p) {
3593 throw std::runtime_error(TString::Format("Cannot replace with %s", node.GetName()));
3594 }
3595 auto out = acquire(node.fComp);
3596 new_p = std::dynamic_pointer_cast<RooAbsArg>(out).get();
3597
3598 std::set<RooAbsArg *> cl;
3599 for (auto &arg : p5->clients()) {
3600 if (arg == new_p)
3601 continue; // do not replace in self ... although redirectServers will prevent that anyway
3602 cl.insert(arg);
3603 }
3604
3605 // if multiple clients, see if only one client is in parentage route
3606 // if so, then assume thats the only client we should replace in
3607 if (cl.size() > 1) {
3608 if (fParent && fParent->get<RooAbsArg>() && cl.count(fParent->get<RooAbsArg>()) > 0) {
3609 cl.clear();
3610 cl.insert(fParent->get<RooAbsArg>());
3611 } else {
3612 std::stringstream clientList;
3613 for (auto c : cl)
3614 clientList << c->GetName() << ",";
3615 Warning("Replace", "Replacing %s in all clients: %s", p5->GetName(), clientList.str().c_str());
3616 }
3617 }
3618
3619 new_p->setAttribute(Form("ORIGNAME:%s", p5->GetName())); // used in redirectServers to say what this replaces
3620 for (auto arg : cl) {
3621 // if RooFormulaVar need to ensure the internal formula has been "constructed" otherwise will try to construct
3622 // it from the original expression that may have old parameter in it.
3623 if (auto p = dynamic_cast<RooFormulaVar *>(arg))
3624 p->ok(); // triggers creation of RooFormula
3625 arg->redirectServers(RooArgSet(*new_p), false, true);
3626 }
3627 return node;
3628}
3629
3631{
3632
3633 class AutoUpdater {
3634 public:
3635 AutoUpdater(xRooNode &_n) : n(_n) {}
3636 ~AutoUpdater() { n.browse(); }
3637 xRooNode &n;
3638 };
3639 AutoUpdater xxx(*this);
3640
3641 if (!get() && fParent) {
3642 // try to 'create' object based on parentage
3643 // add child as a temporary child to help with decision making
3644 auto _ref = emplace_back(std::shared_ptr<xRooNode>(&const_cast<xRooNode &>(child), [](TObject *) {}));
3645 try {
3646 fComp = fParent->Add(*this, "+").fComp;
3647 } catch (...) {
3648 resize(size() - 1);
3649 std::rethrow_exception(std::current_exception());
3650 }
3651 resize(size() - 1); // remove the temporarily added node
3652 }
3653
3654 if (auto p = mainChild(); p) {
3655 // variations applied to the main child if has one
3656 return p.Vary(child);
3657 }
3658
3659 if (auto s = get<RooSimultaneous>(); s && s->indexCat().IsA() == RooCategory::Class()) {
3660 // name is used as cat label
3661 std::string label = child.GetName();
3662 if (auto pos = label.find('='); pos != std::string::npos)
3663 label = label.substr(pos + 1);
3664 if (!s->indexCat().hasLabel(label)) {
3665 // auto idx = static_cast<const RooCategory &>(s->indexCat()).nextAvailableStateIndex(); - can't access,
3666 // protected method ... will have to just assume indices stay in sync
3667 // ensure added to category in any of our datasets too
3668 for (auto _ds : datasets()) {
3669 if (auto bb = _ds->getBrowsable(".sourceds")) {
3670 _ds = bb;
3671 } // shouldn't happen
3672 auto dsCat = _ds->robs()[s->indexCat().GetName()]->get<RooCategory>();
3673 if (!dsCat) {
3674 throw std::runtime_error(TString::Format("Failed to find %s regular observable in %s dataset",
3675 s->indexCat().GetName(), _ds->GetName()));
3676 }
3677 dsCat->defineType(label.c_str());
3678 }
3679 // adding to the index cat after, so that we don't need to generate subdatasets in the call to datasets() above
3680 // (missing cat will trigger cut)
3681 static_cast<RooCategory &>(const_cast<RooAbsCategoryLValue &>(s->indexCat())).defineType(label.c_str());
3682 }
3683 std::shared_ptr<TObject> out;
3684 child.convertForAcquisition(*this);
3685 if (child.get<RooAbsPdf>()) {
3686 out = acquire(child.fComp); // may create a channel from a histogram
3687 } else if (!child.fComp) {
3688 out = acquireNew<RooProdPdf>(TString::Format("%s_%s", s->GetName(), label.c_str()),
3689 (strlen(child.GetTitle())) ? child.GetTitle() : label.c_str(), RooArgList());
3690 Info("Vary", "Created channel RooProdPdf::%s in model %s", out->GetName(), s->GetName());
3691 }
3692
3693 if (auto _pdf = std::dynamic_pointer_cast<RooAbsPdf>(out); _pdf) {
3694 // before adding the channel, we need to see if we are about to add any globs, and if necessary we must update
3695 // the dataset globs
3696 std::set<RooAbsData *> dsToUpdate;
3697 for (auto _ds : datasets()) {
3698 if (auto bb = _ds->getBrowsable(".sourceds")) {
3699 _ds = bb;
3700 } // shouldn't happen
3701 if (_ds->get<RooAbsData>()->getGlobalObservables()) {
3702 dsToUpdate.insert(_ds->get<RooAbsData>());
3703 }
3704 }
3705 if (!dsToUpdate.empty()) {
3706 RooArgSet leafs;
3707 _pdf->leafNodeServerList(&leafs);
3708 std::unique_ptr<RooAbsCollection> globals(leafs.selectByAttrib("global", true));
3709 for (auto _ds : dsToUpdate) {
3710 std::string alist;
3712 globs.addClone(*_ds->getGlobalObservables());
3713 for (auto &aa : *globals) {
3714 if (!globs.contains(*aa)) {
3715 globs.addClone(*aa);
3716 alist += std::string(aa->GetName()) + ",";
3717 }
3718 }
3719 if (!alist.empty()) {
3720 Warning("Vary", "Adding %s to global observables of %s", alist.c_str(), _ds->GetName());
3721 _ds->setGlobalObservables(globs);
3722 }
3723 }
3724 }
3725
3726 s->addPdf(*_pdf, label.c_str());
3727 sterilize();
3728 // clear children for reload and update shared axis
3729 clear();
3730 fXAxis.reset();
3731 browse();
3732 return xRooNode(TString::Format("%s=%s", s->indexCat().GetName(), label.data()), _pdf, *this);
3733 }
3734
3735 } else if (auto p = get<RooStats::HistFactory::FlexibleInterpVar>(); p) {
3736
3737 // child needs to be a constvar ...
3738 child.convertForAcquisition(*this);
3739 auto _c = child.get<RooConstVar>();
3740 if (!_c && child.get()) {
3741 throw std::runtime_error("Only pure consts can be set as variations of a flexible interpvar");
3742 }
3743#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
3744 double value = (_c ? _c->getVal() : p->_nominal);
3745 double nomVal = p->_nominal;
3746#else
3747 double value = (_c ? _c->getVal() : p->nominal());
3748 double nomVal = p->nominal();
3749#endif
3750
3751 TString cName(child.GetName());
3752 if (cName == "nominal") {
3753 p->setNominal(value);
3754 return *(this->variations().at(cName.Data()));
3755 }
3756 if (cName.CountChar('=') != 1) {
3757 throw std::runtime_error("unsupported variation form");
3758 }
3759 std::string parName = cName(0, cName.Index('='));
3760 double parVal = TString(cName(cName.Index('=') + 1, cName.Length())).Atof();
3761 if (parVal != 1 && parVal != -1) {
3762 throw std::runtime_error("unsupported variation magnitude");
3763 }
3764 bool high = parVal > 0;
3765
3766 if (parName.empty()) {
3767 p->setNominal(value);
3768 } else {
3769 auto v = fParent->getObject<RooRealVar>(parName);
3770 if (!v)
3771 v = fParent->acquire<RooRealVar>(parName.c_str(), parName.c_str(), -5, 5);
3772 if (!v->hasError())
3773 v->setError(1);
3774
3775 if (!p->findServer(*v)) {
3776#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
3777 p->_paramList.add(*v);
3778 p->_low.push_back(0);
3779 p->_high.push_back(0);
3780 p->_interpCode.push_back(4);
3781#else
3782 const_cast<RooListProxy &>(p->variables()).add(*v);
3783 const_cast<std::vector<double> &>(p->low()).push_back(0);
3784 const_cast<std::vector<double> &>(p->high()).push_back(0);
3785 const_cast<std::vector<int> &>(p->interpolationCodes()).push_back(4);
3786#endif
3787 v->setAttribute(Form("SYMMETRIC%s_%s", high ? "+" : "-", GetName())); // flag for symmetrized
3788 }
3789
3790 if (high) {
3791 p->setHigh(*v, value);
3792 if (v->getAttribute(Form("SYMMETRIC+_%s", GetName()))) {
3793 p->setLow(*v, 2 * nomVal - value);
3794 }
3795 v->setAttribute(Form("SYMMETRIC-_%s", GetName()), false);
3796 } else {
3797 p->setLow(*v, value);
3798 if (v->getAttribute(Form("SYMMETRIC-_%s", GetName()))) {
3799 p->setHigh(*v, 2 * nomVal - value);
3800 }
3801 v->setAttribute(Form("SYMMETRIC+_%s", GetName()), false);
3802 }
3803
3804 /*if (!unconstrained && fParent->pars()[v->GetName()].constraints().empty()) {
3805 fParent->pars()[v->GetName()].constraints().add("normal");
3806 }*/
3807 }
3808 return *(this->variations().at(cName.Data()));
3809 } else if (auto p2 = get<PiecewiseInterpolation>(); p2) {
3810 TString cName(child.GetName());
3811 if (cName.CountChar('=') != 1) {
3812 throw std::runtime_error("unsupported variation form");
3813 }
3814 TString parName = cName(0, cName.Index('='));
3815 double parVal = TString(cName(cName.Index('=') + 1, cName.Length())).Atof();
3816 if (parVal != 1 && parVal != -1) {
3817 throw std::runtime_error("unsupported variation magnitude");
3818 }
3819#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
3820 RooHistFunc *f = dynamic_cast<RooHistFunc *>(p2->_nominal.absArg());
3821 if (!f) {
3822 throw std::runtime_error(
3823 TString::Format("Interpolating %s instead of RooHistFunc", p2->_nominal.absArg()->ClassName()));
3824 }
3825#else
3826 RooHistFunc *f = dynamic_cast<RooHistFunc *>(const_cast<RooAbsReal *>(p2->nominalHist()));
3827 if (!f) {
3828 throw std::runtime_error(
3829 TString::Format("Interpolating %s instead of RooHistFunc", p2->nominalHist()->ClassName()));
3830 }
3831#endif
3832 RooHistFunc *nomf = f;
3833 RooHistFunc *otherf = nullptr;
3834 size_t i = 0;
3835 for (auto par : p2->paramList()) {
3836 if (parName == par->GetName()) {
3837 f = dynamic_cast<RooHistFunc *>((parVal > 0 ? p2->highList() : p2->lowList()).at(i));
3838 otherf = dynamic_cast<RooHistFunc *>((parVal > 0 ? p2->lowList() : p2->highList()).at(i));
3839 break;
3840 }
3841 i++;
3842 }
3843 if (i == p2->paramList().size() && !child.get<RooAbsReal>()) {
3844
3845 // need to add the parameter
3846 auto v = acquire<RooRealVar>(parName, parName, -5, 5);
3847 if (!v->hasError())
3848 v->setError(1);
3849
3850 std::shared_ptr<RooHistFunc> up(
3851 static_cast<RooHistFunc *>(f->Clone(Form("%s_%s_up", f->GetName(), parName.Data()))));
3852 std::shared_ptr<RooHistFunc> down(
3853 static_cast<RooHistFunc *>(f->Clone(Form("%s_%s_down", f->GetName(), parName.Data()))));
3854 // RooHistFunc doesn't clone it's data hist ... do it ourself (will be cloned again if imported into a ws)
3855#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
3856 std::unique_ptr<RooDataHist> h1(
3857 static_cast<RooDataHist *>(f->dataHist().Clone(Form("hist_%s", up->GetName()))));
3858 std::unique_ptr<RooDataHist> h2(
3859 static_cast<RooDataHist *>(f->dataHist().Clone(Form("hist_%s", down->GetName()))));
3860 up->_dataHist = dynamic_cast<RooDataHist *>(f->dataHist().Clone(Form("hist_%s", up->GetName())));
3861 down->_dataHist = dynamic_cast<RooDataHist *>(f->dataHist().Clone(Form("hist_%s", down->GetName())));
3862#else
3863 up->cloneAndOwnDataHist(TString::Format("hist_%s", up->GetName()));
3864 down->cloneAndOwnDataHist(TString::Format("hist_%s", down->GetName()));
3865#endif
3866 auto ups = std::dynamic_pointer_cast<RooHistFunc>(acquire(up, false, true));
3867 auto downs = std::dynamic_pointer_cast<RooHistFunc>(acquire(down, false, true));
3868#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
3869 p2->_highSet.add(*ups.get());
3870 p2->_lowSet.add(*downs.get());
3871 p2->_interpCode.push_back(4);
3872 p2->_paramSet.add(*v);
3873#else
3874 const_cast<RooArgList &>(p2->highList()).add(*ups);
3875 const_cast<RooArgList &>(p2->lowList()).add(*downs);
3876 const_cast<std::vector<int> &>(p2->interpolationCodes()).push_back(4);
3877 const_cast<RooArgList &>(p2->paramList()).add(*v);
3878#endif
3879 p2->setValueDirty();
3880 f = ((parVal > 0) ? ups : downs).get();
3881 otherf = ((parVal > 0) ? downs : ups).get();
3882 // start off with everything being symmetric
3883 f->setStringAttribute("symmetrizes", otherf->GetName());
3884 f->setStringAttribute("symmetrize_nominal", nomf->GetName());
3885 otherf->setStringAttribute("symmetrized_by", f->GetName());
3886
3887 // constrain par if required
3888 /*if (!unconstrained && fParent->pars()[v->GetName()].constraints().empty()) {
3889 fParent->pars()[v->GetName()].constraints().add("normal");
3890 }*/
3891 }
3892
3893 // child.convertForAcquisition(*this);
3894 if (f) {
3895 if (child.get())
3896 xRooNode("tmp", *f, *this) = *child.get();
3897 f->setValueDirty();
3898 xRooNode out(*f, *this);
3899 out.sterilize();
3900 return out;
3901 }
3902
3903#if ROOT_VERSION_CODE > ROOT_VERSION(6, 37, 00)
3904 } else if (auto pmr = get<RooMultiReal>(); pmr) {
3905 TString cName(child.GetName());
3906 if (cName.CountChar('=') != 1) {
3907 throw std::runtime_error("unsupported variation form");
3908 }
3909 TString parName = cName(0, cName.Index('='));
3910 TString parVal = TString(cName(cName.Index('=') + 1, cName.Length()));
3911
3912 // add parVal to categorical if not already there
3913 if (!pmr->indexCategory()->hasLabel(parVal.Data())) {
3914 dynamic_cast<RooCategory &>(*(pmr->indexCategory())).defineType(parVal);
3915 }
3916 auto idx = pmr->indexCategory()->lookupIndex(parVal.Data());
3917 if (idx < 0) {
3918 throw std::runtime_error("Invalid index");
3919 }
3920 // add child to list ... use copy of "nominal" (0th) if child is empty
3921 child.convertForAcquisition(*this);
3922 auto _c = child.get<RooAbsReal>();
3923 if (!_c) {
3924 if (pmr->getModelList().empty()) {
3925 throw std::runtime_error("No real function given for variation, and no nominal function to clone");
3926 }
3927 _c = std::dynamic_pointer_cast<RooAbsReal>(
3928 acquire(std::shared_ptr<TObject>(pmr->getModelList().at(0)->Clone(
3929 TString::Format("%s_%s", get()->GetName(), child.GetName()))),
3930 false, true))
3931 .get();
3932 _c->setStringAttribute("alias", child.GetName());
3933 }
3934 const_cast<RooListProxy &>(pmr->getModelList()).add(*_c);
3935 return xRooNode(*_c, *this);
3936
3937#endif
3938
3939 } else if (auto p3 = get<RooConstVar>(); p3) {
3940
3941 // never vary the universal consts ... its too dangerous
3942 if (p3->getAttribute("RooRealConstant_Factory_Object")) {
3943 throw std::runtime_error("Cannot vary pure constants");
3944 }
3945
3946 // inject a FlexibleInterpVar ...
3947
3948 // get the list of clients BEFORE creating the new interpolation ... seems list of clients is inaccurate after
3949 std::set<RooAbsArg *> cl;
3950 for (auto &arg : p3->clients()) {
3951 cl.insert(arg);
3952 }
3953 // if multiple clients, see if only one client is in parentage route
3954 // if so, then assume thats the only client we should replace in
3955 if (cl.size() > 1) {
3956 if (cl.count(fParent->get<RooAbsArg>()) > 0) {
3957 cl.clear();
3958 cl.insert(fParent->get<RooAbsArg>());
3959 } else {
3960 Warning("Vary", "Varying %s that has multiple clients", p3->GetName());
3961 }
3962 }
3963 p3->setStringAttribute("origName", p3->GetName());
3964 TString n = p3->GetName();
3965 p3->SetName(Form("%s_nominal", p3->GetName())); // if problems should perhaps not rename here
3966
3967 auto new_p = acquireNew<RooStats::HistFactory::FlexibleInterpVar>(n, p3->GetTitle(), RooArgList(), p3->getVal(),
3968 std::vector<double>(), std::vector<double>());
3969
3970 // copy attributes over
3971 for (auto &a : p3->attributes())
3972 new_p->setAttribute(a.c_str());
3973 for (auto &a : p3->stringAttributes())
3974 new_p->setStringAttribute(a.first.c_str(), a.second.c_str());
3975 // if (!new_p->getStringAttribute("alias")) new_p->setStringAttribute("alias",p->GetName());
3976 auto old_p = p3;
3977 new_p->setAttribute(Form("ORIGNAME:%s", old_p->GetName())); // used in redirectServers to say what this replaces
3978 for (auto arg : cl) {
3979 arg->redirectServers(RooArgSet(*new_p), false, true);
3980 }
3981
3982 fComp = new_p;
3983 return Vary(child);
3984
3985 } else if (auto p4 = get<RooAbsReal>(); p4) {
3986 // inject an interpolation node
3987
3988 // get the list of clients BEFORE creating the new interpolation ... seems list of clients is inaccurate after
3989 std::set<RooAbsArg *> cl;
3990 for (auto &arg : p4->clients()) {
3991 cl.insert(arg);
3992 }
3993 // if multiple clients, see if only one client is in parentage route
3994 // if so, then assume thats the only client we should replace in
3995 if (cl.size() > 1) {
3996 if (cl.count(fParent->get<RooAbsArg>()) > 0) {
3997 cl.clear();
3998 cl.insert(fParent->get<RooAbsArg>());
3999 } else {
4000 Warning("Vary", "Varying %s that has multiple clients", p4->GetName());
4001 }
4002 }
4003 p4->setStringAttribute("origName", p4->GetName());
4004 TString n = p4->GetName();
4005 p4->SetName(Form("%s_nominal", p4->GetName())); // if problems should perhaps not rename here
4006
4007#if ROOT_VERSION_CODE > ROOT_VERSION(6, 37, 00)
4008 std::shared_ptr<RooAbsArg> new_p;
4009 // if alphanumeric variation name is not a 1 or -1, inject a RooMultiReal not a PiecewiseInterpolation ...
4010 TString cName(child.GetName());
4011 if (cName.CountChar('=') == 1) {
4012 TString parName = cName(0, cName.Index('='));
4013 double parVal = TString(cName(cName.Index('=') + 1, cName.Length())).Atof();
4014 if (parVal != 1 && parVal != -1) {
4015 // get the idxCat ...
4016 auto idxCat = acquire<RooCategory>(parName, parName);
4017 if (!idxCat) {
4018 throw std::runtime_error("Failed to acquire categorical index for RooMultiReal");
4019 }
4020 // add an index if none already
4021 if (idxCat->numTypes() == 0) {
4022 idxCat->defineType("nominal");
4023 }
4024 Info("Vary", "Creating a RooMultiReal with category %s", idxCat->GetName());
4025 auto newFunc = acquireNew<RooMultiReal>(
4026 n, p4->GetTitle(), *idxCat,
4027 RooArgList()); // can't pass nominal model as that will force defining types on category
4028 const_cast<RooListProxy &>(newFunc->getModelList()).add(*p4);
4029 new_p = newFunc;
4030 }
4031 }
4032 if (!new_p) {
4033 new_p = acquireNew<PiecewiseInterpolation>(n, p4->GetTitle(), *p4, RooArgList(), RooArgList(), RooArgList());
4034 }
4035
4036#else
4037 auto new_p = acquireNew<PiecewiseInterpolation>(n, p4->GetTitle(), *p4, RooArgList(), RooArgList(), RooArgList());
4038#endif
4039
4040 // copy attributes over
4041 for (auto &a : p4->attributes())
4042 new_p->setAttribute(a.c_str());
4043 for (auto &a : p4->stringAttributes())
4044 new_p->setStringAttribute(a.first.c_str(), a.second.c_str());
4045 // if (!new_p->getStringAttribute("alias")) new_p->setStringAttribute("alias",p->GetName());
4046 auto old_p = p4;
4047 new_p->setAttribute(Form("ORIGNAME:%s", old_p->GetName())); // used in redirectServers to say what this replaces
4048 for (auto arg : cl) {
4049 arg->redirectServers(RooArgSet(*new_p), false, true);
4050 }
4051
4052 fComp = new_p;
4053 return Vary(child);
4054 }
4055
4056 Print();
4057 throw std::runtime_error(TString::Format("Cannot vary %s with %s", GetName(), child.GetName()));
4058}
4059
4061{
4063}
4064
4065bool xRooNode::SetContent(double value, const char *par, double val)
4066{
4067 return SetContents(RooConstVar(GetName(), GetTitle(), value), par, val);
4068}
4069
4072 {
4073 if (x && b)
4074 x->setBinning(*b);
4075 if (b)
4076 delete b;
4077 }
4078 RooRealVar *x = nullptr;
4079 RooAbsBinning *b = nullptr;
4080};
4081
4083{
4084
4085 if (!get()) {
4086 fComp = std::shared_ptr<TObject>(const_cast<TObject *>(&o), [](TObject *) {});
4087 if (fParent && !fParent->find(GetName())) {
4088 // either a temporary or a placeholder so need to try genuinely adding
4089 fComp = fParent->Add(*this, "+").fComp;
4090 if (auto a = get<RooAbsArg>(); a && strcmp(a->GetName(), GetName()) && !a->getStringAttribute("alias")) {
4091 a->setStringAttribute("alias", GetName());
4092 }
4093 if (!fComp)
4094 throw std::runtime_error("Cannot determine type");
4095 return *this;
4096 }
4097 }
4098
4099 if (auto h = dynamic_cast<const TH1 *>(&o); h) {
4100 /*auto f = get<RooHistFunc>();
4101 if (!f) {
4102 // if it's a RooProduct locate child with the same name
4103 if (get<RooProduct>()) {
4104 f = factors()[GetName()]->get<RooHistFunc>();
4105 }
4106
4107
4108
4109 }*/
4110 bool _isData = get<RooAbsData>();
4111 BinningRestorer _b;
4112 if (_isData) {
4113 // need to ensure x-axis matches this h
4114 auto ax = GetXaxis();
4115 if (!ax)
4116 throw std::runtime_error("no xaxis");
4117 auto _v = dynamic_cast<RooRealVar *>(ax->GetParent());
4118 if (_v) {
4119 _b.x = _v;
4120 _b.b = dynamic_cast<RooAbsBinning *>(_v->getBinningPtr(nullptr)->Clone());
4121 if (h->GetXaxis()->IsVariableBinSize()) {
4122 _v->setBinning(RooBinning(h->GetNbinsX(), h->GetXaxis()->GetXbins()->GetArray()));
4123 } else {
4124 _v->setBinning(RooUniformBinning(h->GetXaxis()->GetXmin(), h->GetXaxis()->GetXmax(), h->GetNbinsX()));
4125 }
4126 }
4127 }
4128
4129 if (true) {
4130 for (int bin = 1; bin <= h->GetNbinsX(); bin++) {
4131 SetBinContent(bin, h->GetBinContent(bin));
4132 /*double value = h->GetBinContent(bin);
4133 auto bin_pars = f->dataHist().get(bin - 1);
4134 if (f->getAttribute("density")) {
4135 value /= f->dataHist().binVolume(*bin_pars);
4136 }
4137 f->dataHist().set(*bin_pars, value);*/
4138 if (!_isData && h->GetSumw2N() && !SetBinError(bin, h->GetBinError(bin)))
4139 throw std::runtime_error("Failed setting stat error");
4140 }
4141 return *this;
4142 }
4143 } else if (auto _c = dynamic_cast<const RooConstVar *>(&o); _c) {
4144
4145 if (auto a = get<RooAbsArg>();
4146 (a && a->isFundamental()) || get<RooConstVar>() || get<RooStats::HistFactory::FlexibleInterpVar>()) {
4147 SetBinContent(1, _c->getVal());
4148 return *this;
4149 } else if (get<RooAbsData>()) { // try to do assignment to a dataset (usually setting a bin content)
4150 SetBinContent(0, _c->getVal());
4151 return *this;
4152 }
4153 }
4154
4155 throw std::runtime_error("Assignment failed");
4156
4157 /*
4158
4159 if (fParent && !fParent->mk()) {
4160 throw std::runtime_error("mk failure");
4161 }
4162
4163 if (fComp) return *this;
4164
4165 if (o.InheritsFrom("RooAbsArg")) {
4166 fComp = acquire(std::shared_ptr<TObject>(const_cast<TObject*>(&o),[](TObject* o){}));
4167 std::dynamic_pointer_cast<RooAbsArg>(fComp)->setStringAttribute("alias",GetName());
4168 }
4169
4170 if (fComp && fParent) {
4171 fParent->incorporate(fComp);
4172 }
4173
4174
4175 return *this;
4176 */
4177}
4178
4179void xRooNode::_fit_(const char *constParValues, const char *options)
4180{
4181 try {
4182 // re-float all poi and np before fitting
4183 np().get<RooArgList>()->setAttribAll("Constant", false);
4184 poi().get<RooArgList>()->setAttribAll("Constant", false);
4185 auto _pars = pars();
4186 // std::unique_ptr<RooAbsCollection> snap(_pars.argList().snapshot());
4187 TStringToken pattern(constParValues, ",");
4188 std::map<RooAbsRealLValue *, double> valsToSet;
4189 while (pattern.NextToken()) {
4190 auto idx = pattern.Index('=');
4191 TString pat = (idx == -1) ? TString(pattern) : TString(pattern(0, idx));
4192 double val =
4193 (idx == -1) ? std::numeric_limits<double>::quiet_NaN() : TString(pattern(idx + 1, pattern.Length())).Atof();
4194 bool foundArg = false;
4195 for (auto p : _pars.argList()) {
4196 if (TString(p->GetName()).Contains(TRegexp(pat, true))) {
4197 foundArg = true;
4198 p->setAttribute("Constant", true);
4199 if (!std::isnan(val)) {
4200 valsToSet[dynamic_cast<RooAbsRealLValue *>(p)] = val;
4201 // dynamic_cast<RooAbsRealLValue *>(p)->setVal(val); // don't set yet, to allow for asimov dataset
4202 // creation based on current values
4203 }
4204 }
4205 }
4206 if (!foundArg) {
4207 throw std::runtime_error(std::string("Unrecognised parameter: ") + pat.Data());
4208 }
4209 }
4210
4211 // parse options
4212 TStringToken pattern2(options, ",");
4213 auto defaultOpts = xRooFit::createNLLOptions();
4214 while (pattern2.NextToken()) {
4215 auto idx = pattern2.Index('=');
4216 TString pat = (idx == -1) ? TString(pattern2) : TString(pattern2(0, idx));
4217 TString val = TString(pattern2(idx + 1, pattern2.Length()));
4218 if (auto o = defaultOpts->FindObject(pat)) {
4219 defaultOpts->Remove(o);
4220 delete o;
4221 }
4222 defaultOpts->Add(new RooCmdArg(pat, val.IsDec() ? val.Atoi() : 0, 0, val.IsFloat() ? val.Atof() : 0., 0.,
4223 val.IsAlpha() ? val : nullptr));
4224 }
4225
4226 // use the first selected dataset
4227 auto _dsets = datasets();
4228 TString dsetName = "";
4229 for (auto &d : _dsets) {
4230 if (d->get()->TestBit(1 << 20)) {
4231 dsetName = d->get()->GetName();
4232 break;
4233 }
4234 }
4235 auto _nll = nll(dsetName.Data(), *defaultOpts);
4236 // can now set the values
4237 for (auto [p, v] : valsToSet) {
4238 p->setVal(v);
4239 }
4240 _nll.fitConfigOptions()->SetValue("LogSize", 65536);
4241 _nll.fitConfig()->MinimizerOptions().SetPrintLevel(0);
4242 auto fr = _nll.minimize();
4243 //_pars.argList() = *snap; // restore values - irrelevant as SetFitResult will restore values
4244 if (!fr.get())
4245 throw std::runtime_error("Fit Failed");
4246 SetFitResult(fr.get());
4247 TString statusCodes;
4248 for (unsigned int i = 0; i < fr->numStatusHistory(); i++) {
4249 statusCodes += TString::Format("\n%s = %d", fr->statusLabelHistory(i), fr->statusCodeHistory(i));
4250 }
4251 // add poi to list of statusCodes too if was good fit
4252 if(fr->status() == 0) {
4253 auto _poi = poi();
4254 if (!_poi.empty()) {
4255 statusCodes += "\n\nPOI Post Fit Values\n-------------";
4256 for (auto p : _poi) {
4257 auto arg = dynamic_cast<RooRealVar *>(fr->floatParsFinal().find(p->GetName()));
4258 if (!arg)
4259 continue; // must have been held constant
4260 statusCodes += TString::Format("\n%s : %g +/- %g", arg->GetName(), arg->getVal(), arg->getError());
4261 }
4262 }
4263 }
4264 const TGWindow *w =
4265 (gROOT->GetListOfBrowsers()->At(0))
4266 ? dynamic_cast<TGWindow *>(static_cast<TBrowser *>(gROOT->GetListOfBrowsers()->At(0))->GetBrowserImp())
4267 : gClient->GetRoot();
4268 TString gofResult = "";
4269 if (_nll.fOpts->find("GoF")) {
4270 gofResult = TString::Format("GoF p-value = %g (mainTerm = %g)\n", fr->constPars().getRealValue(".pgof"),
4271 fr->constPars().getRealValue(".mainterm_pgof"));
4272 }
4273 if (fr->status() != 0) {
4274 new TGMsgBox(gClient->GetRoot(), w, "Fit Finished with Bad Status Code",
4275 TString::Format("%s\nData = %s\nFit Status Code = %d\nCov Quality = %d\n%s-------------%s",
4276 fr->GetName(), dsetName.Data(), fr->status(), fr->covQual(), gofResult.Data(),
4277 statusCodes.Data()),
4279 } else if (fr->covQual() != 3 && _nll.fitConfig()->ParabErrors()) {
4280 new TGMsgBox(gClient->GetRoot(), w, "Fit Finished with Bad Covariance Quality",
4281 TString::Format("%s\nData = %s\nFit Status Code = %d\nCov Quality = %d\n%s-------------%s",
4282 fr->GetName(), dsetName.Data(), fr->status(), fr->covQual(), gofResult.Data(),
4283 statusCodes.Data()),
4285 } else {
4286 new TGMsgBox(gClient->GetRoot(), w, "Fit Finished Successfully",
4287 TString::Format("%s\nData = %s\nFit Status Code = %d\nCov Quality = %d\n%s-------------%s",
4288 fr->GetName(), dsetName.Data(), fr->status(), fr->covQual(), gofResult.Data(),
4289 statusCodes.Data()));
4290 }
4291 TBrowser *b = nullptr;
4292 for (auto a : *gROOT->GetListOfBrowsers()) {
4293 b = dynamic_cast<TBrowser *>(a);
4294 if (b && GetTreeItem(b)) {
4295 break;
4296 }
4297 }
4298 if (b) {
4299 auto p = GetTreeItem(b);
4300 while (p) {
4301 if (TString(p->GetText()).BeginsWith("RooWorkspace::")) {
4302 // found the workspace ... refresh this node, and if there's a fits node, refresh that
4303 if (auto bi = dynamic_cast<TRootBrowser *>(b->GetBrowserImp())) {
4304 if (auto fb = dynamic_cast<TGFileBrowser *>(bi->GetActBrowser())) {
4305 fb->DoubleClicked(p, 0);
4306 if (auto first = p->GetFirstChild()) {
4307 do {
4308 if (first->IsOpen() && TString(first->GetText()) == "fits") {
4309 fb->DoubleClicked(first, 0);
4310 }
4311 } while ((first = first->GetNextSibling()));
4312 }
4313 }
4314 }
4315 break;
4316 } else {
4317 p = p->GetParent();
4318 }
4319 }
4320 }
4321 } catch (const std::exception &e) {
4322 new TGMsgBox(
4323 gClient->GetRoot(),
4324 (gROOT->GetListOfBrowsers()->At(0))
4325 ? dynamic_cast<TGWindow *>(static_cast<TBrowser *>(gROOT->GetListOfBrowsers()->At(0))->GetBrowserImp())
4326 : gClient->GetRoot(),
4327 "Exception", e.what(), kMBIconExclamation, kMBOk); // deletes self on dismiss?
4328 }
4329}
4330
4331void xRooNode::_generate_(const char *datasetName, bool expected)
4332{
4333 try {
4334 datasets().Add(datasetName, expected ? "asimov" : "toy");
4335 // refresh datasets folder of workspace
4336 TBrowser *b = nullptr;
4337 for (auto a : *gROOT->GetListOfBrowsers()) {
4338 b = dynamic_cast<TBrowser *>(a);
4339 if (b && GetTreeItem(b)) {
4340 break;
4341 }
4342 }
4343 if (b) {
4344 auto p = GetTreeItem(b);
4345 while (p) {
4346 if (TString(p->GetText()).BeginsWith("RooWorkspace::")) {
4347 // found the workspace ... refresh this node, and if there's a datasets node, refresh that
4348 if (auto bi = dynamic_cast<TRootBrowser *>(b->GetBrowserImp())) {
4349 if (auto fb = dynamic_cast<TGFileBrowser *>(bi->GetActBrowser())) {
4350 fb->DoubleClicked(p, 0);
4351 if (auto first = p->GetFirstChild()) {
4352 do {
4353 if (first->IsOpen() && TString(first->GetText()) == "datasets") {
4354 fb->DoubleClicked(first, 0);
4355 }
4356 } while ((first = first->GetNextSibling()));
4357 }
4358 }
4359 }
4360 break;
4361 } else {
4362 p = p->GetParent();
4363 }
4364 }
4365 }
4366 } catch (const std::exception &e) {
4367 new TGMsgBox(
4368 gClient->GetRoot(),
4369 (gROOT->GetListOfBrowsers()->At(0))
4370 ? dynamic_cast<TGWindow *>(static_cast<TBrowser *>(gROOT->GetListOfBrowsers()->At(0))->GetBrowserImp())
4371 : gClient->GetRoot(),
4372 "Exception", e.what(),
4373 kMBIconExclamation); // deletes self on dismiss?
4374 }
4375}
4376
4377void xRooNode::_scan_(const char *what, double nToys, const char *xvar, int nBinsX, double lowX,
4378 double highX /*, const char*, int, double, double*/, const char *constParValues,
4379 const char *options)
4380{
4381 try {
4382 TString sXvar(xvar);
4383 TString sWhat(what);
4384
4385 // use the first selected dataset
4386 auto _dsets = datasets();
4387 TString dsetName = "";
4388 for (auto &d : _dsets) {
4389 if (d->get()->TestBit(1 << 20)) {
4390 dsetName = d->get()->GetName();
4391 break;
4392 }
4393 }
4394 auto _pars = pars();
4395 std::unique_ptr<RooAbsCollection> snap(_pars.argList().snapshot());
4396 TStringToken pattern(constParValues, ",");
4397 while (pattern.NextToken()) {
4398 auto idx = pattern.Index('=');
4399 TString pat = (idx == -1) ? TString(pattern) : TString(pattern(0, idx));
4400 double val =
4401 (idx == -1) ? std::numeric_limits<double>::quiet_NaN() : TString(pattern(idx + 1, pattern.Length())).Atof();
4402 bool foundArg = false;
4403 for (auto par : _pars.argList()) {
4404 if (TString(par->GetName()).Contains(TRegexp(pat, true))) {
4405 foundArg = true;
4406 par->setAttribute("Constant", true);
4407 if (!std::isnan(val)) {
4408 dynamic_cast<RooAbsRealLValue *>(par)->setVal(val);
4409 }
4410 }
4411 }
4412 if (!foundArg) {
4413 throw std::runtime_error(std::string("Unrecognised parameter: ") + pat.Data());
4414 }
4415 }
4416
4417 // parse options
4418 TStringToken pattern2(options, ",");
4419 auto defaultOpts = xRooFit::createNLLOptions();
4420 while (pattern2.NextToken()) {
4421 auto idx = pattern2.Index('=');
4422 TString pat = (idx == -1) ? TString(pattern2) : TString(pattern2(0, idx));
4423 TString val = TString(pattern2(idx + 1, pattern2.Length()));
4424 if (auto o = defaultOpts->FindObject(pat)) {
4425 defaultOpts->Remove(o);
4426 delete o;
4427 }
4428 defaultOpts->Add(new RooCmdArg(pat, val.IsDec() ? val.Atoi() : 0, 0, val.IsFloat() ? val.Atof() : 0., 0.,
4429 val.IsAlpha() ? val : nullptr));
4430 }
4431
4432 auto hs = nll(dsetName.Data(), *defaultOpts).hypoSpace(sXvar);
4433 hs.SetName(TUUID().AsString());
4434 if (nToys) {
4435 sWhat += " toys";
4436 if (nToys > 0) {
4437 sWhat += TString::Format("=%g", nToys);
4438 }
4439 }
4440 hs.SetTitle(sWhat + " scan" + ((dsetName != "") ? TString::Format(" [data=%s]", dsetName.Data()) : ""));
4441 int scanStatus = hs.scan(sWhat + " visualize", nBinsX, lowX, highX);
4442 if (scanStatus != 0) {
4443 new TGMsgBox(
4444 gClient->GetRoot(),
4445 (gROOT->GetListOfBrowsers()->At(0))
4446 ? dynamic_cast<TGWindow *>(static_cast<TBrowser *>(gROOT->GetListOfBrowsers()->At(0))->GetBrowserImp())
4447 : gClient->GetRoot(),
4448 "Scan Finished with Bad Status Code",
4449 TString::Format("%s\nData = %s\nScan Status Code = %d", hs.GetName(), dsetName.Data(), scanStatus),
4451 }
4452 if (ws()) {
4453 if (auto res = hs.result())
4454 ws()->import(*res);
4455 }
4456
4457 _pars.argList() = *snap; // restore pars
4458
4459 TBrowser *b = nullptr;
4460 for (auto a : *gROOT->GetListOfBrowsers()) {
4461 b = dynamic_cast<TBrowser *>(a);
4462 if (b && GetTreeItem(b)) {
4463 break;
4464 }
4465 }
4466 if (b) {
4467 auto p = GetTreeItem(b);
4468 while (p) {
4469 if (TString(p->GetText()).BeginsWith("RooWorkspace::")) {
4470 // found the workspace ... refresh this node, and if there's a scans node, refresh that
4471 if (auto bi = dynamic_cast<TRootBrowser *>(b->GetBrowserImp())) {
4472 if (auto fb = dynamic_cast<TGFileBrowser *>(bi->GetActBrowser())) {
4473 fb->DoubleClicked(p, 0);
4474 if (auto first = p->GetFirstChild()) {
4475 do {
4476 if (first->IsOpen() && TString(first->GetText()) == "scans") {
4477 fb->DoubleClicked(first, 0);
4478 }
4479 } while ((first = first->GetNextSibling()));
4480 }
4481 }
4482 }
4483 break;
4484 } else {
4485 p = p->GetParent();
4486 }
4487 }
4488 }
4489
4490 } catch (const std::exception &e) {
4491 new TGMsgBox(
4492 gClient->GetRoot(),
4493 (gROOT->GetListOfBrowsers()->At(0))
4494 ? dynamic_cast<TGWindow *>(static_cast<TBrowser *>(gROOT->GetListOfBrowsers()->At(0))->GetBrowserImp())
4495 : gClient->GetRoot(),
4496 "Exception", e.what(), kMBIconExclamation);
4497 }
4498}
4499
4500void xRooNode::_SetBinContent_(int bin, double value, const char *par, double parVal)
4501{
4502 try {
4503 SetBinContent(bin, value, strlen(par) > 0 ? par : nullptr, parVal);
4504 } catch (const std::exception &e) {
4505 new TGMsgBox(gClient->GetRoot(), gClient->GetRoot(), "Exception", e.what(),
4506 kMBIconExclamation); // deletes self on dismiss?
4507 }
4508}
4509
4511{
4512 try {
4513#if ROOT_VERSION_CODE > ROOT_VERSION(6, 29, 00)
4514 // if this is a collection of values, populate a TF1 and display as a dialog
4515 if (!get() && TString(GetName()).BeginsWith("!")) {
4516 browse();
4518 for (auto a : *this) {
4519 if (auto arg = a->get<RooRealVar>())
4520 args.add(*arg);
4521 }
4522 TF1 f(GetName(), 0.0, 1.0, std::min(int(args.size()), 10));
4523 int i = 0;
4524 int j = 0;
4525 for (auto c : args) {
4526 j++;
4527 if (j < value) {
4528 continue;
4529 }
4530 auto v = dynamic_cast<RooRealVar *>(c);
4531 f.SetParName(i, c->GetName());
4532 if (v) {
4533 f.SetParLimits(i, v->getMin(), v->getMax());
4534 if (v->isConstant())
4535 f.FixParameter(i, v->getVal());
4536 else {
4537 f.SetParameter(i, v->getVal());
4538 f.SetParError(i, v->getError());
4539 }
4540 }
4541 i++;
4542 if (i == 10) {
4543 break; // max 10 pars shown
4544 }
4545 }
4546 int ret = 0;
4548 gClient->GetDefaultRoot(),
4549 (gROOT->GetListOfBrowsers()->At(0))
4550 ? dynamic_cast<TGWindow *>(static_cast<TBrowser *>(gROOT->GetListOfBrowsers()->At(0))->GetBrowserImp())
4551 : gClient->GetDefaultRoot(),
4552 &f, nullptr, &ret);
4553 if (ret) {
4554 // user has changed parameter values etc, propagate back to parameters
4555 for (i = 0; i < f.GetNpar(); i++) {
4556 auto c = args.find(f.GetParName(i));
4557 auto v = dynamic_cast<RooRealVar *>(c);
4558 if (v) {
4559 v->setVal(f.GetParameter(i));
4560 double low, high;
4561 f.GetParLimits(i, low, high);
4562 if (low == high) {
4563 v->setConstant(low); // if low==high==0 then is not marked constant
4564 } else {
4565 v->setRange(low, high);
4566 }
4567 }
4568 }
4569 }
4570 return;
4571 }
4572#endif
4573
4574 if (!SetContent(value))
4575 throw std::runtime_error("Failed to SetContent");
4576 } catch (const std::exception &e) {
4577 new TGMsgBox(gClient->GetRoot(), gClient->GetRoot(), "Exception", e.what(),
4578 kMBIconExclamation); // deletes self on dismiss?
4579 }
4580}
4581
4582bool xRooNode::SetBinContent(int bin, double value, const char *par, double parVal)
4583{
4584
4585 // create if needed
4586 if (!get()) {
4587 if (fParent && !find(GetName())) {
4588 // if have a binning we create a histogram to match it
4589 if (auto ax = GetXaxis(); ax) {
4590 std::shared_ptr<TH1D> h;
4591 auto _b = dynamic_cast<Axis2 *>(ax)->binning();
4592 if (_b->isUniform()) {
4593 TDirectory::TContext ctx{nullptr}; // No self-registration to directories
4594 h.reset(new TH1D(GetName(), GetTitle(), _b->numBins(), _b->lowBound(), _b->highBound()));
4595 } else {
4596 TDirectory::TContext ctx{nullptr}; // No self-registration to directories
4597 h.reset(new TH1D(GetName(), GetTitle(), _b->numBins(), _b->array()));
4598 }
4599 h->SetOption("nostyle"); // don't transfer style when added
4600 h->GetXaxis()->SetName(TString::Format("%s;%s", ax->GetParent()->GetName(), ax->GetName()));
4601 fComp = h;
4602 }
4603 fComp = fParent->Add(*this, "sample").fComp;
4604 }
4605 }
4606
4607 // if it's a RooProduct locate child with the same name
4608 if (get<RooProduct>()) {
4609 return factors()[GetName()]->SetBinContent(bin, value, par, parVal);
4610 }
4611
4612 if (get<RooAbsData>()) {
4613 if (auto _data = get<RooDataSet>(); _data) {
4614 auto _ax = (bin) ? GetXaxis() : nullptr;
4615 if (!_ax && bin) {
4616 throw std::runtime_error("Cannot determine binning to fill data");
4617 }
4618 if (_ax && _ax->GetNbins() < bin) {
4619 throw std::out_of_range(TString::Format("%s range %s only has %d bins", _ax->GetParent()->GetName(),
4620 _ax->GetName(), _ax->GetNbins()));
4621 }
4622 RooArgSet obs;
4623
4624 TString cut = "";
4625
4626 for (auto _c : coords()) { // coords() moves vars to their respective coordinates too
4627 if (auto _cat = _c->get<RooAbsCategoryLValue>(); _cat) {
4628 if (cut != "")
4629 cut += " && ";
4630 cut += TString::Format("%s==%d", _cat->GetName(), _cat->getCurrentIndex());
4631 obs.add(*_cat); // note: if we ever changed coords to return clones, would need to keep coords alive
4632 } else if (auto _rv = _c->get<RooAbsRealLValue>(); _rv) {
4633 // todo: check coordRange is a single range rather than multirange
4634 if (cut != "")
4635 cut += " && ";
4636 cut +=
4637 TString::Format("%s>=%f&&%s<%f", _rv->GetName(), _rv->getMin(_rv->getStringAttribute("coordRange")),
4638 _rv->GetName(), _rv->getMax(_rv->getStringAttribute("coordRange")));
4639 obs.add(*_rv); // note: if we ever changed coords to return clones, would need to keep coords alive
4640 } else {
4641 throw std::runtime_error("SetBinContent of data: Unsupported coordinate type");
4642 }
4643 }
4644
4645 RooFormulaVar cutFormula("cut1", cut, obs); // doing this to avoid complaints about unused vars
4646 RooFormulaVar icutFormula("icut1", TString::Format("!(%s)", cut.Data()), obs);
4647
4648 TString cut2;
4649 if (_ax) {
4650 cut2 = TString::Format("%s >= %f && %s < %f", _ax->GetParent()->GetName(), _ax->GetBinLowEdge(bin),
4651 _ax->GetParent()->GetName(), _ax->GetBinUpEdge(bin));
4652 obs.add(*dynamic_cast<RooAbsArg *>(_ax->GetParent()));
4653 } else {
4654 cut2 = "1==1";
4655 }
4656 RooFormulaVar cutFormula2("cut2", cut + " && " + cut2, obs);
4657 RooFormulaVar icutFormula2("icut2", TString::Format("!(%s && %s)", cut.Data(), cut2.Data()), obs);
4658
4659 // // go up through parents looking for slice obs
4660 // auto _p = fParent;
4661 // while(_p) {
4662 // TString pName(_p->GetName());
4663 // if (auto pos = pName.Index('='); pos != -1) {
4664 // if(auto _obs = _p->getObject<RooAbsLValue>(pName(0,pos)); _obs) {
4665 // if(auto _cat = dynamic_cast<RooAbsCategoryLValue*>(_obs.get()); _cat) {
4666 // _cat->setLabel(pName(pos+1,pName.Length()));
4667 // cut += TString::Format("%s%s==%d", (cut=="")?"":" && ",_cat->GetName(),
4668 // _cat->getCurrentIndex());
4669 // } else if(auto _var = dynamic_cast<RooAbsRealLValue*>(_obs.get()); _var) {
4670 // _var->setVal(TString(pName(pos+1,pName.Length())).Atof());
4671 // // TODO: Cut for this!!
4672 // }
4673 // obs.add(*dynamic_cast<RooAbsArg*>(_obs.get()));
4674 // } else {
4675 // throw std::runtime_error("Unknown observable, could not find");
4676 // }
4677 // }
4678 // _p = _p->fParent;
4679 // }
4680
4681 // add observables to dataset if necessary
4682 RooArgSet l(obs);
4683 l.remove(*_data->get(), true, true);
4684 if (!l.empty()) {
4685 // addColumns method is buggy: https://github.com/root-project/root/issues/8787
4686 // incredibly though, addColumn works??
4687 for (auto &x : l) {
4688 _data->addColumn(*x);
4689 }
4690 // instead create a copy dataset and merge it into current
4691 // cant use merge because it drops weightVar
4692 /*RooDataSet tmp("tmp","tmp",l);
4693 for(int i=0;i<_data->numEntries();i++) tmp.add(l);
4694 _data->merge(&tmp);*/
4695 // delete _data->addColumns(l);
4696 }
4697 // before adding, ensure range is good to cover
4698 for (auto &o : obs) {
4699 if (auto v = dynamic_cast<RooRealVar *>(o); v) {
4700 if (auto dv = dynamic_cast<RooRealVar *>(_data->get()->find(v->GetName())); dv) {
4701 if (v->getMin() < dv->getMin())
4702 dv->setMin(v->getMin());
4703 if (v->getMax() > dv->getMax())
4704 dv->setMax(v->getMax());
4705 }
4706 } else if (auto c = dynamic_cast<RooCategory *>(o); c) {
4707 if (auto dc = dynamic_cast<RooCategory *>(_data->get()->find(c->GetName())); dc) {
4708 if (!dc->hasLabel(c->getCurrentLabel())) {
4709 dc->defineType(c->getCurrentLabel(), c->getCurrentIndex());
4710 }
4711 }
4712 }
4713 }
4714
4715 // using SetBinContent means dataset must take on a binned form at these coordinates
4716 // if number of entries doesnt match number of bins then will 'bin' the data
4717 if (bin) {
4718 if (auto _nentries = std::unique_ptr<RooAbsData>(_data->reduce(cutFormula))->numEntries();
4719 _nentries != _ax->GetNbins()) {
4720 auto _contents = GetBinContents(1, _ax->GetNbins());
4721
4722 if (_nentries > 0) {
4723 Info("SetBinContent", "Binning %s in channel: %s", GetName(), cut.Data());
4724 auto _reduced = std::unique_ptr<RooAbsData>(_data->reduce(icutFormula));
4725 _data->reset();
4726 for (int j = 0; j < _reduced->numEntries(); j++) {
4727 auto _obs = _reduced->get(j);
4728 _data->add(*_obs, _reduced->weight());
4729 }
4730 }
4731 for (int i = 1; i <= _ax->GetNbins(); i++) {
4732 // can skip over the bin we will be setting to save a reduce step below
4733 if (i == bin)
4734 continue;
4735 dynamic_cast<RooAbsLValue *>(_ax->GetParent())->setBin(i - 1, _ax->GetName());
4736 _data->add(obs, _contents.at(i - 1));
4737 }
4738 }
4739 }
4740 // remove existing entries
4741 if (std::unique_ptr<RooAbsData>(_data->reduce(cutFormula2))->numEntries() > 0) {
4742 auto _reduced = std::unique_ptr<RooAbsData>(_data->reduce(icutFormula2));
4743 _data->reset();
4744 for (int j = 0; j < _reduced->numEntries(); j++) {
4745 auto _obs = _reduced->get(j);
4746 _data->add(*_obs, _reduced->weight());
4747 }
4748 }
4749 if (_ax)
4750 dynamic_cast<RooAbsLValue *>(_ax->GetParent())->setBin(bin - 1, _ax->GetName());
4751 _data->add(obs, value);
4752 if (auto bb = getBrowsable(".sourceds"))
4753 return bb->SetBinContent(bin, value, par, parVal); // apply to source ds if we have one
4754 return true;
4755
4756 } else if (get<RooDataHist>()) {
4757 throw std::runtime_error("RooDataHist not supported yet");
4758 }
4759 }
4760
4761 if (auto _varies = variations(); !_varies.empty() || (par && strlen(par))) {
4762 if (!par || strlen(par) == 0) {
4763 return _varies["nominal"]->SetBinContent(bin, value, par, parVal);
4764 } else if (auto it = _varies.find(Form("%s=%g", par, parVal)); it) {
4765 return it->SetBinContent(bin, value);
4766 } else {
4767 // need to create the variation : note - if no variations existed up to now this will inject a new node
4768 // so we should redirect ourself to the new node
4769 // TODO: Do we need to redirect parents?
4770 TString s = Form("%s=%g", par, parVal);
4771 return Vary(s.Data()).SetBinContent(bin, value);
4772 }
4773 }
4774
4775 auto o = get();
4776 if (auto p = dynamic_cast<RooRealVar *>(o); p) {
4777 if (!par || strlen(par) == 0) {
4778 if (p->getMax() < value)
4779 p->setMax(value);
4780 if (p->getMin() > value)
4781 p->setMin(value);
4782 p->setVal(value);
4783 sterilize();
4784 return true;
4785 }
4786
4787 } else if (auto c = dynamic_cast<RooConstVar *>(o); c) {
4788
4789 // if parent is a FlexibleInterpVar, change the value in that .
4790 if (strcmp(c->GetName(), Form("%g", c->getVal())) == 0) {
4791 c->SetNameTitle(Form("%g", value), Form("%g", value));
4792 }
4793#if ROOT_VERSION_CODE < ROOT_VERSION(6, 24, 00)
4794 c->_value = value; // in future ROOT versions there is a changeVal method!
4795#else
4796 c->changeVal(value);
4797#endif
4798
4800 fParent->Vary(*this);
4801 }
4802
4803 sterilize();
4804 return true;
4805 } else if (auto f = dynamic_cast<RooHistFunc *>(o); f) {
4806 auto bin_pars = f->dataHist().get(bin - 1);
4807 if (f->getAttribute("density")) {
4808 value /= f->dataHist().binVolume(*bin_pars);
4809 }
4810 f->dataHist().set(*bin_pars, value);
4811 f->setValueDirty();
4812
4813 if (auto otherfName = f->getStringAttribute("symmetrized_by"); otherfName) {
4814 // broken symmetry, so update flags ...
4815 f->setStringAttribute("symmetrized_by", nullptr);
4816 if (auto x = getObject<RooAbsArg>(otherfName); x) {
4817 x->setStringAttribute("symmetrizes", nullptr);
4818 x->setStringAttribute("symmetrize_nominal", nullptr);
4819 }
4820 } else if (auto otherfName2 = f->getStringAttribute("symmetrizes"); otherfName2) {
4821 auto nomf = getObject<RooHistFunc>(f->getStringAttribute("symmetrize_nominal"));
4822 auto otherf = getObject<RooHistFunc>(otherfName2);
4823 if (nomf && otherf) {
4824 otherf->dataHist().set(*bin_pars, 2 * nomf->dataHist().weight(bin - 1) - value);
4825 otherf->setValueDirty();
4826 }
4827 }
4828 sterilize();
4829 return true;
4830 } else if (auto f2 = dynamic_cast<RooStats::HistFactory::FlexibleInterpVar *>(o); f2) {
4831 // changing nominal value
4832 f2->setNominal(value);
4833 }
4834 throw std::runtime_error(TString::Format("unable to set bin content of %s", GetPath().c_str()));
4835}
4836
4837bool xRooNode::SetBinData(int bin, double value, const xRooNode &data)
4838{
4839 if (data.get<RooAbsData>()) {
4840 // attach as a child before calling datasets(), so that is included in the list
4841 push_back(std::make_shared<xRooNode>(data));
4842 }
4843 auto node = datasets()[data.GetName()];
4844 if (data.get<RooAbsData>()) {
4845 // remove the child we attached
4846 resize(size() - 1);
4847 }
4848 return node->SetBinContent(bin, value);
4849}
4850
4851bool xRooNode::SetData(const TObject &obj, const xRooNode &data)
4852{
4853 if (data.get<RooAbsData>()) {
4854 // attach as a child before calling datasets(), so that is included in the list
4855 push_back(std::make_shared<xRooNode>(data));
4856 }
4857 auto node = datasets()[data.GetName()];
4858 if (data.get<RooAbsData>()) {
4859 // remove the child we attached
4860 resize(size() - 1);
4861 }
4862 return node->SetContents(obj);
4863}
4864
4865bool xRooNode::SetBinError(int bin, double value)
4866{
4867
4868 // if it's a RooProduct locate child with the same name
4869 if (get<RooProduct>()) {
4870 return factors()[GetName()]->SetBinError(bin, value);
4871 }
4872
4873 if (auto _varies = variations(); !_varies.empty()) {
4874 return _varies["nominal"]->SetBinError(bin, value);
4875 }
4876
4877 auto o = get();
4878
4879 if (auto f = dynamic_cast<RooHistFunc *>(o); f) {
4880
4881 // if (f->getAttribute("density")) { value /= f->dataHist().binVolume(*bin_pars); } - commented out because DON'T
4882 // convert .. sumw and sumw2 attributes will be stored not as densities
4883
4884 // NOTE: Can only do this because factors() makes parents of its children it's own parent (it isn't the parent)
4885 // If ever make factors etc part of the parentage then this would need tweaking to get to the true parent
4886 // find first parent that is a RooProduct, that is where the statFactor would live
4887 // stop as soon as we reach pdf object
4888 auto _prodParent = fParent;
4889 while (_prodParent && !_prodParent->get<RooProduct>() && !_prodParent->get<RooAbsPdf>()) {
4890 if (_prodParent->get<PiecewiseInterpolation>() && strcmp(GetName(), "nominal")) {
4891 _prodParent.reset();
4892 break; // only the 'nominal' variation can look for a statFactor outside the variation container
4893 }
4894 _prodParent = _prodParent->fParent;
4895 }
4896 auto _f_stat =
4897 (_prodParent && !_prodParent->get<RooAbsPdf>()) ? _prodParent->factors().find("statFactor") : nullptr;
4898 auto f_stat = (_f_stat) ? _f_stat->get<ParamHistFunc>() : nullptr;
4899 if (_f_stat && _f_stat->get() && !f_stat) {
4900 throw std::runtime_error("stat factor must be a paramhistfunc");
4901 }
4902
4903 // stat uncertainty lives in the "statFactor" factor, each sample has its own one,
4904 // but they can share parameters
4905 if (!f_stat) {
4906 if (value == 0)
4907 return true;
4908 TString parNames;
4909 for (auto &p : xRooNode("tmp", *f, std::shared_ptr<xRooNode>(nullptr)).vars()) {
4910 if (parNames != "")
4911 parNames += ",";
4912 parNames += p->get()->GetName();
4913 }
4914 auto h = std::unique_ptr<TH1>(f->dataHist().createHistogram(parNames
4915#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 27, 00)
4916 ,
4918#endif
4919 ));
4920 h->Reset();
4921 h->SetName("statFactor");
4922 h->SetTitle(TString::Format("StatFactor of %s", f->GetTitle()));
4923 h->SetOption("blankshape");
4924
4925 // multiply parent if is nominal
4926 auto toMultiply = this;
4927 if (strcmp(GetName(), "nominal") == 0 && fParent && fParent->get<PiecewiseInterpolation>())
4928 toMultiply = fParent.get();
4929
4930 f_stat = dynamic_cast<ParamHistFunc *>(toMultiply->Multiply(*h).get());
4931 if (!f_stat) {
4932 throw std::runtime_error("Failed creating stat shapeFactor");
4933 }
4934 }
4935
4936 auto phf = f_stat;
4937
4938 TString prefix = f->getStringAttribute("statPrefix");
4939 if (value && prefix == "") {
4940 // find the first parent that can hold components (RooAddPdf, RooRealSumPdf, RooAddition, RooWorkspace) ... use
4941 // that name for the stat factor
4942 auto _p = fParent;
4943 while (_p && !(_p->get()->InheritsFrom("RooRealSumPdf") || _p->get()->InheritsFrom("RooAddPdf") ||
4944 _p->get()->InheritsFrom("RooWorkspace") || _p->get()->InheritsFrom("RooAddition"))) {
4945 _p = _p->fParent;
4946 }
4947 prefix = TString::Format("stat_%s", (_p && _p->get<RooAbsReal>()) ? _p->get()->GetName() : f->GetName());
4948 }
4949 auto newVar = (value == 0) ? getObject<RooRealVar>("1")
4950 : acquire<RooRealVar>(Form("%s_bin%d", prefix.Data(), bin),
4951 Form("#gamma^{%s}_{%d}", prefix.Data(), bin), 1);
4952#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
4953 RooArgList &pSet = phf->_paramSet;
4954#else
4955 RooArgList &pSet = const_cast<RooArgList &>(phf->paramList());
4956#endif
4957 auto var = dynamic_cast<RooRealVar *>(&pSet[bin - 1]);
4958
4959 if (newVar.get() != var) {
4960 // need to swap out var for newVar
4961 // replace ith element in list with new func, or inject into RooProduct
4962 RooArgList all;
4963 for (std::size_t i = 0; i < pSet.size(); i++) {
4964 if (int(i) != bin - 1) {
4965 all.add(*pSet.at(i));
4966 } else {
4967 all.add(*newVar);
4968 }
4969 }
4970 pSet.removeAll();
4971 pSet.add(all);
4972 }
4973
4974 xRooNode v((value == 0) ? *var : *newVar, *this);
4975 auto rrv = dynamic_cast<RooRealVar *>(v.get());
4976 if (strcmp(rrv->GetName(), "1") != 0) {
4977 TString origName = (f->getStringAttribute("origName")) ? f->getStringAttribute("origName") : GetName();
4978 rrv->setStringAttribute(Form("sumw2_%s", origName.Data()), TString::Format("%f", pow(value, 2)));
4979 auto bin_pars = f->dataHist().get(bin - 1);
4980 auto _binContent = f->dataHist().weight(bin - 1);
4981 if (f->getAttribute("density")) {
4982 _binContent *= f->dataHist().binVolume(*bin_pars);
4983 }
4984 rrv->setStringAttribute(Form("sumw_%s", origName.Data()), TString::Format("%f", _binContent));
4985 double sumw2 = 0;
4986 double sumw = 0;
4987 for (auto &[s, sv] : rrv->stringAttributes()) {
4988 if (s.find("sumw_") == 0) {
4989 sumw += TString(sv).Atof();
4990 } else if (s.find("sumw2_") == 0) {
4991 sumw2 += TString(sv).Atof();
4992 }
4993 }
4994 if (sumw2 && sumw2 != std::numeric_limits<double>::infinity()) {
4995 double tau = pow(sumw, 2) / sumw2;
4996 rrv->setError((tau < 1e-15) ? 1e15 : (/*rrv->getVal()*/ 1. / sqrt(tau))); // not sure why was rrv->getVal()?
4997 rrv->setConstant(false);
4998 // parameter must be constrained
4999 auto _constr = v.constraints();
5000 // std::cout << " setting constraint " << v.GetName() << " nomin=" << tau << std::endl;
5001 if (_constr.empty()) {
5002 rrv->setStringAttribute("boundConstraint", _constr.Add("poisson").get()->GetName());
5003 } else {
5004 auto _glob = _constr.at(0)->obs().at(0)->get<RooRealVar>();
5005 // TODO: Update any globs snapshots that are designed to match the nominal
5006 _glob->setStringAttribute("nominal", TString::Format("%f", tau));
5007 double _min = tau * (1. - 5. * sqrt(1. / tau));
5008 double _max = tau * (1. + 5. * sqrt(1. / tau));
5009 _glob->setRange(_min, _max);
5010 _glob->setVal(tau);
5011 _constr.at(0)->pp().at(0)->SetBinContent(0, tau);
5012 rrv->setStringAttribute("boundConstraint", _constr.at(0)->get()->GetName());
5013 }
5014 rrv->setRange(std::max((1. - 5. * sqrt(1. / tau)), 1e-15), 1. + 5. * sqrt(1. / tau));
5015 } else {
5016 // remove constraint
5017 if (auto _constr = v.constraints(); !_constr.empty()) {
5018 v.constraints().Remove(*_constr.at(0));
5019 }
5020 // set const if sumw2 is 0 (i.e. no error)
5021 rrv->setVal(1);
5022 rrv->setError(0);
5023 rrv->setConstant(sumw2 == 0);
5024 }
5025 }
5026
5027 return true;
5028 }
5029
5030 throw std::runtime_error(TString::Format("%s SetBinError failed", GetName()));
5031}
5032
5033std::shared_ptr<xRooNode> xRooNode::at(const std::string &name, bool browseResult) const
5034{
5035 auto res = find(name, browseResult);
5036 if (res == nullptr)
5037 throw std::out_of_range(name + " does not exist");
5038 return res;
5039}
5040
5041////////////////////////////////////////////////////////////////////////////////
5042/// The RooWorkspace this node belong to, if any
5043
5045{
5046 if (auto _w = get<RooWorkspace>(); _w)
5047 return _w;
5048 if (auto a = get<RooAbsArg>(); a && GETWS(a)) {
5049 return GETWS(a);
5050 }
5051 if (fParent)
5052 return fParent->ws();
5053 return nullptr;
5054}
5055
5057{
5058
5059 xRooNode out(".constraints", nullptr, *this);
5060
5061 std::function<RooAbsPdf *(const xRooNode &n, RooAbsArg &par, std::set<RooAbsPdf *> ignore)> getConstraint;
5062 getConstraint = [&](const xRooNode &n, RooAbsArg &par, std::set<RooAbsPdf *> ignore) {
5063 if (auto _pdf = n.get<RooAbsPdf>()) {
5064 if (ignore.count(_pdf))
5065 return (RooAbsPdf *)nullptr;
5066 ignore.insert(_pdf);
5067 }
5068 auto o = n.get<RooProdPdf>();
5069 if (!o) {
5070 if (n.get<RooSimultaneous>()) {
5071 // check all channels for a constraint if is simultaneous
5072 for (auto &c : n.bins()) {
5073 if (auto oo = getConstraint(*c, par, ignore); oo) {
5074 return oo;
5075 }
5076 }
5077 return (RooAbsPdf *)nullptr;
5078 } else if (n.get<RooAbsPdf>() && n.fParent && n.fParent->get<RooWorkspace>()) {
5079 // reached top-level pdf, which wasn't a simultaneous, so stop here
5080 return (RooAbsPdf *)nullptr;
5081 } else if (auto _ws = n.get<RooWorkspace>(); _ws) {
5082 // reached a workspace, check for any pdf depending on parameter that isnt the ignore
5083 for (auto p : _ws->allPdfs()) {
5084 if (ignore.count(static_cast<RooAbsPdf *>(p)))
5085 continue;
5086 if (p->dependsOn(par)) {
5087 out.emplace_back(std::make_shared<xRooNode>(par.GetName(), *p, *this));
5088 }
5089 }
5090 }
5091 if (!n.fParent)
5092 return (RooAbsPdf *)nullptr;
5093 return getConstraint(*n.fParent, par, ignore);
5094 }
5095 for (auto p : o->pdfList()) {
5096 if (ignore.count(static_cast<RooAbsPdf *>(p)))
5097 continue;
5098 if (p->dependsOn(par)) {
5099 out.emplace_back(std::make_shared<xRooNode>(par.GetName(), *p, *this));
5100 }
5101 }
5102 return (RooAbsPdf *)nullptr;
5103 };
5104
5105 for (auto &p : vars()) {
5106 auto v = dynamic_cast<RooAbsReal *>(p->get());
5107 if (!v)
5108 continue;
5109 if (v->getAttribute("Constant") && v != get<RooAbsReal>())
5110 continue; // skip constants unless we are getting the constraints of a parameter itself
5111 if (v->getAttribute("obs"))
5112 continue; // skip observables ... constraints constrain pars not obs
5113 getConstraint(*this, *v, {get<RooAbsPdf>()});
5114 /*if (auto c = ; c) {
5115 out.emplace_back(std::make_shared<Node2>(p->GetName(), *c, *this));
5116 }*/
5117 }
5118
5119 // finish by removing any constraint that contains another constraint for the same par
5120 // and consolidate common pars
5121 auto it = out.std::vector<std::shared_ptr<xRooNode>>::begin();
5122 while (it != out.std::vector<std::shared_ptr<xRooNode>>::end()) {
5123 bool removeIt = false;
5124 for (auto &c : out) {
5125 if (c.get() == it->get())
5126 continue;
5127 if ((*it)->get<RooAbsArg>()->dependsOn(*c->get<RooAbsArg>())) {
5128 removeIt = true;
5129 std::set<std::string> parNames;
5130 std::string _cName = c->GetName();
5131 do {
5132 parNames.insert(_cName.substr(0, _cName.find(';')));
5133 _cName = _cName.substr(_cName.find(';') + 1);
5134 } while (_cName.find(';') != std::string::npos);
5135 parNames.insert(_cName);
5136 _cName = it->get()->GetName();
5137 do {
5138 parNames.insert(_cName.substr(0, _cName.find(';')));
5139 _cName = _cName.substr(_cName.find(';') + 1);
5140 } while (_cName.find(';') != std::string::npos);
5141 parNames.insert(_cName);
5142 _cName = "";
5143 for (auto &x : parNames) {
5144 if (!_cName.empty())
5145 _cName += ";";
5146 _cName += x;
5147 }
5148 c->TNamed::SetName(_cName.c_str());
5149 break;
5150 }
5151 }
5152 if (removeIt) {
5153 it = out.erase(it);
5154 } else {
5155 ++it;
5156 }
5157 }
5158
5159 // if getting constraints of a fundamental then use the constraint names instead of the par name (because would be
5160 // all same otherwise)
5161 if (get<RooAbsArg>() && get<RooAbsArg>()->isFundamental()) {
5162 for (auto &o : out) {
5163 o->TNamed::SetName(o->get()->GetName());
5164 }
5165 }
5166
5167 return out;
5168}
5169
5170std::shared_ptr<TObject> xRooNode::convertForAcquisition(xRooNode &acquirer, const char *opt) const
5171{
5172
5173 TString sOpt(opt);
5174 sOpt.ToLower();
5175 TString sName(GetName());
5176 if (sOpt == "func")
5177 sName = TString("factory:") + sName;
5178
5179 // if arg is a histogram, will acquire it as a RooHistFunc unless no conversion
5180 // todo: could flag not to convert
5181 if (auto h = get<TH1>(); h) {
5182 TString sOpt2(h->GetOption());
5183 std::map<std::string, std::string> stringAttrs;
5184 while (sOpt2.Contains("=")) {
5185 auto pos = sOpt2.Index("=");
5186 auto start = sOpt2.Index(";") + 1;
5187 if (start > pos)
5188 start = 0;
5189 auto end = sOpt2.Index(";", pos);
5190 if (end == -1)
5191 end = sOpt2.Length();
5192 stringAttrs[sOpt2(start, pos - start)] = sOpt2(pos + 1, end - pos - 1);
5193 sOpt2 = TString(sOpt2(0, start)) + TString(sOpt2(end + 1, sOpt2.Length()));
5194 }
5195 TString newObjName = GetName();
5196 TString origName = GetName();
5197 if (origName.BeginsWith(';'))
5198 origName = origName(1, origName.Length());
5199 if (newObjName.BeginsWith(';')) {
5200 newObjName =
5201 newObjName(1, newObjName.Length()); // special case if starts with ';' then don't create a fancy name
5202 } else if (acquirer.get() && !acquirer.get<RooWorkspace>()) {
5203 newObjName = TString::Format(
5204 "%s_%s", (acquirer.mainChild().get()) ? acquirer.mainChild()->GetName() : acquirer->GetName(),
5205 newObjName.Data());
5206 }
5207 // can convert to a RooHistFunc, or RooParamHist if option contains 'shape'
5208 TString varName = h->GetXaxis()->GetName();
5209 std::string binningName = newObjName.Data();
5210 if (auto pos = varName.Index(';'); pos != -1) {
5211 binningName = varName(pos + 1, varName.Length());
5212 varName = varName(0, pos);
5213 }
5214
5215 if (varName == "xaxis" &&
5216 !acquirer.get<RooSimultaneous>()) { // default case, try to take axis var and binning from the acquirer
5217 if (auto ax = acquirer.GetXaxis(); ax) {
5218 varName = ax->GetParent()->GetName();
5219 // TODO: check the binning is consistent before using - at least will check nBins below
5220 binningName = ax->GetName();
5221 } else if (acquirer.obs().size() == 1)
5222 varName = acquirer.obs().at(0)->get()->GetName(); // TODO what if no obs but Xaxis var is defined?
5223 }
5224 auto x = acquirer.acquire<RooRealVar>(varName, h->GetXaxis()->GetTitle(), h->GetXaxis()->GetXmin(),
5225 h->GetXaxis()->GetXmax());
5226 if (x->getMin() > h->GetXaxis()->GetXmin())
5227 x->setMin(h->GetXaxis()->GetXmin());
5228 if (x->getMax() < h->GetXaxis()->GetXmax())
5229 x->setMax(h->GetXaxis()->GetXmax());
5230 if (!x->hasBinning(binningName.c_str())) {
5231 if (h->GetXaxis()->IsVariableBinSize()) {
5232 x->setBinning(RooBinning(h->GetNbinsX(), h->GetXaxis()->GetXbins()->GetArray()), binningName.c_str());
5233 } else {
5234 x->setBinning(
5235 RooUniformBinning(h->GetXaxis()->GetXmin(), h->GetXaxis()->GetXmax(), h->GetXaxis()->GetNbins()),
5236 binningName.c_str());
5237 }
5238 x->getBinning(binningName.c_str()).SetTitle(h->GetXaxis()->GetTitle());
5239 if (x->getBinningNames().size() == 2) {
5240 // this was the first binning, so copy it over to be the default binning too
5241 x->setBinning(x->getBinning(binningName.c_str()));
5242 }
5243 } else {
5244 // TODO check binning is compatible with histogram
5245 if (x->getBinning(binningName.c_str()).numBins() != h->GetNbinsX()) {
5246 throw std::runtime_error(
5247 TString::Format("binning mismatch for binning %s of %s", binningName.c_str(), x->GetName()));
5248 }
5249 }
5250
5251 std::shared_ptr<RooAbsArg> _f;
5252
5253 // if acquirer is a RooSimultaneous, will use histogram to define a channel
5254 if (acquirer.get<RooSimultaneous>()) {
5255 _f = acquirer.acquireNew<RooProdPdf>(newObjName, (strlen(h->GetTitle())) ? h->GetTitle() : h->GetName(),
5256 RooArgList());
5257 for (auto &[k, v] : stringAttrs) {
5258 _f->setStringAttribute(k.c_str(), v.c_str());
5259 }
5260 x->setAttribute("obs", true);
5261 } else if (sOpt2.Contains("shape")) {
5262 RooArgList list;
5263 for (int i = 0; i < x->getBinning(binningName.c_str()).numBins(); i++) {
5264 std::shared_ptr<RooAbsArg> arg;
5265 if (sOpt2.Contains("blankshape")) {
5266 arg = acquirer.acquire2<RooAbsArg, RooRealVar>("1", "1", 1);
5267 } else {
5268 if (!h) {
5269 arg = acquirer.acquireNew<RooRealVar>(TString::Format("%s_bin%d", newObjName.Data(), i + 1), "", 1);
5270 } else if (h->GetMinimumStored() != -1111 || h->GetMaximumStored() != -1111) {
5271 arg = acquirer.acquireNew<RooRealVar>(TString::Format("%s_bin%d", newObjName.Data(), i + 1),
5272 TString::Format("%s_{%d}", h->GetTitle(), i + 1),
5273 h->GetBinContent(i + 1), h->GetMinimumStored(),
5274 h->GetMaximumStored());
5275 } else {
5276 arg = acquirer.acquireNew<RooRealVar>(TString::Format("%s_bin%d", newObjName.Data(), i + 1),
5277 TString::Format("%s_{%d}", h->GetTitle(), i + 1),
5278 h->GetBinContent(i + 1));
5279 }
5280 }
5281 list.add(*arg);
5282 }
5283 // paramhistfunc requires the binnings to be loaded as default at construction time
5284 // so load binning temporarily
5285 auto tmp = dynamic_cast<RooAbsBinning *>(x->getBinningPtr(nullptr)->Clone());
5286 x->setBinning(x->getBinning(binningName.c_str()));
5287 _f = acquirer.acquireNew<ParamHistFunc>(newObjName, h->GetTitle(), *x, list);
5288#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
5289 dynamic_cast<ParamHistFunc *>(_f.get())->_paramSet.setName("paramSet"); // so can see when print
5290#else
5291 const_cast<RooArgList &>(dynamic_cast<ParamHistFunc *>(_f.get())->paramList())
5292 .setName("paramSet"); // so can see when print
5293#endif
5294 x->setBinning(*tmp); // restore binning
5295 delete tmp;
5296 for (auto &[k, v] : stringAttrs) {
5297 _f->setStringAttribute(k.c_str(), v.c_str());
5298 }
5299 } else {
5300 auto dh = acquirer.acquireNew<RooDataHist>(Form("hist_%s", newObjName.Data()), h->GetTitle(), *x,
5301 binningName.c_str() /* binning name*/);
5302 if (!dh) {
5303 throw std::runtime_error("Couldn't make data hist");
5304 }
5305 auto f = acquirer.acquireNew<RooHistFunc>(newObjName, h->GetTitle(), *x, *dh,
5306 0 /*interpolation order between bins*/);
5307 f->forceNumInt();
5308 f->setAttribute("autodensity"); // where it gets inserted will determine if this is a density or not
5309 _f = f;
5310
5311 for (auto &[k, v] : stringAttrs) {
5312 _f->setStringAttribute(k.c_str(), v.c_str());
5313 }
5314
5315 // need to do these settings here because used in the assignment step
5316 _f->setStringAttribute("xvar", x->GetName());
5317 _f->setStringAttribute("binning", binningName.c_str());
5318 if (strcmp(_f->GetName(), origName.Data()) && !_f->getStringAttribute("alias"))
5319 _f->setStringAttribute("alias", origName);
5320
5321 // copy values over using the assignment operator - may convert to a RooProduct if there are stat uncerts
5322 xRooNode tmp(h->GetName(), _f, acquirer);
5323 tmp = *h;
5324 _f = std::dynamic_pointer_cast<RooAbsArg>(tmp.fComp); // in case got upgrade to a RooProduct
5325 }
5326
5327 _f->setStringAttribute("xvar", x->GetName());
5328 _f->setStringAttribute("binning", binningName.c_str());
5329 // style(h); // will transfer styling to object if necessary - not doing because this method used with plane hists
5330 // frequently
5331 if (strcmp(_f->GetName(), origName.Data()) && !_f->getStringAttribute("alias"))
5332 _f->setStringAttribute("alias", origName);
5333
5334 fComp = _f;
5335 return _f;
5336 } else if (!get() && (sName.BeginsWith("factory:") || sName.Contains("::")) && acquirer.ws()) {
5337 TString s(sName);
5338 if (sName.BeginsWith("factory:"))
5339 s = TString(s(8, s.Length()));
5340 fComp.reset(acquirer.ws()->factory(s), [](TObject *) {});
5341 if (fComp) {
5342 const_cast<xRooNode *>(this)->TNamed::SetName(fComp->GetName());
5343 }
5344 return fComp;
5345 }
5346
5347 return fComp;
5348}
5349
5350std::shared_ptr<TStyle> xRooNode::style(TObject *initObject, bool autoCreate) const
5351{
5352 return std::dynamic_pointer_cast<TStyle>(styles(initObject, autoCreate).fComp);
5353}
5354
5355xRooNode xRooNode::styles(TObject *initObject, bool autoCreate) const
5356{
5357 TString t = GetTitle();
5358
5359 auto arg = get<RooAbsArg>();
5360 if (!initObject && !arg && !gROOT->GetStyle(t)) {
5361 return nullptr;
5362 }
5363
5364 std::unique_ptr<TObject> argInitObject;
5365
5366 if (initObject) {
5367 t = (strlen(initObject->GetTitle())) ? initObject->GetTitle() : initObject->GetName();
5368 } else if (arg) {
5369 if (arg->getStringAttribute("style")) {
5370 t = arg->getStringAttribute("style");
5371 } else if (autoCreate) {
5372 // args will default to histo's object styling, whatever that currently may be
5373 argInitObject = std::make_unique<TH1D>(GetName(), GetTitle(), 1, 0, 1);
5374 initObject = argInitObject.get();
5375 } else {
5376 return nullptr;
5377 }
5378 }
5379
5380 std::shared_ptr<TStyle> style; // use to keep alive for access from GetStyle below, in case getObject has decided to
5381 // return the owning ptr (for some reason)
5382 if (!gROOT->GetStyle(t)) {
5383 if ((style = getObject<TStyle>(t.Data()))) {
5384 // loaded style (from workspace?) so put in list and use that
5385 gROOT->GetListOfStyles()->Add(style.get());
5386 } else {
5387 if (!autoCreate)
5388 return nullptr;
5389 // create new style - gets put in style list automatically so don't have to delete
5390 // acquire them so saved to workspaces for auto reload ...
5391 style = const_cast<xRooNode &>(*this).acquireNew<TStyle>(t.Data(),
5392 TString::Format("Style for %s component", t.Data()));
5393 if (auto x = dynamic_cast<TAttLine *>(initObject))
5394 ((TAttLine &)*style) = *x;
5395 if (auto x = dynamic_cast<TAttFill *>(initObject))
5396 ((TAttFill &)*style) = *x;
5397 if (auto x = dynamic_cast<TAttMarker *>(initObject))
5398 ((TAttMarker &)*style) = *x;
5399 gROOT->GetListOfStyles()->Add(style.get());
5400 }
5401 } else {
5402 style = std::shared_ptr<TStyle>(gROOT->GetStyle(t), [](TStyle *) {});
5403 }
5404
5405 if (arg && !arg->getStringAttribute("style")) {
5406 arg->setStringAttribute("style", style->GetName());
5407 }
5408
5409 return xRooNode(style, *this);
5410}
5411
5412std::shared_ptr<TObject> xRooNode::acquire(const std::shared_ptr<TObject> &arg, bool checkFactory, bool mustBeNew)
5413{
5414 if (!arg)
5415 return nullptr;
5416 if (!fAcquirer && !get<RooWorkspace>() && fParent)
5417 return fParent->acquire(arg, checkFactory, mustBeNew);
5418
5419 // if has a workspace and our object is the workspace or is in the workspace then add this object to workspace
5420 auto _ws = (fAcquirer) ? nullptr : ws();
5421 if (_ws && (get() == _ws || _ws->arg(GetName()) || (arg && strcmp(arg->GetName(), GetName()) == 0))) {
5424 if (auto a = dynamic_cast<RooAbsArg *>(arg.get()); a) {
5425 auto out_arg = _ws->arg(a->GetName());
5426 TString aName = arg->GetName();
5427 int ii = 1;
5428 while (out_arg && mustBeNew) {
5429 a->SetName(TString::Format("%s_%d", aName.Data(), ii++));
5430 out_arg = _ws->arg(a->GetName());
5431 }
5432 if (aName != a->GetName())
5433 Warning("acquire", "Renaming to %s", a->GetName());
5434 if (!out_arg) {
5435 bool done = false;
5436 if (checkFactory) {
5437 if (auto res = _ws->factory(arg->GetName()); res) {
5438 a = res;
5439 done = true;
5440 }
5441 }
5442 if (!done && _ws->import(*a, RooFit::RecycleConflictNodes())) {
5443 if (GETWS(a) != _ws) {
5444 Info("acquire", "A copy of %s has been added to workspace %s", a->GetName(), _ws->GetName());
5445 }
5447 return nullptr;
5448 }
5449 // sanitizeWS(); // clears the caches that might exist up to now, as well interfere with getParameters calls
5450 std::set<std::string> setNames;
5451 for (auto &aa : GETWSSETS(_ws)) {
5452 if (TString(aa.first.c_str()).BeginsWith("CACHE_")) {
5453 setNames.insert(aa.first);
5454 }
5455 }
5456 for (auto &aa : setNames)
5457 ws()->removeSet(aa.c_str());
5458 out_arg = _ws->arg(a->GetName());
5459 if (GETWS(out_arg) != _ws) { // seems that when objects imported their ws isn't set
5460 out_arg->setWorkspace(*_ws);
5461 }
5462 // if any of the leaf nodes of the imported object have "global" label on them, ensure propagate to
5463 // "globalObservables" list if ws has one
5464 if (auto globs = const_cast<RooArgSet *>(ws()->set("globalObservables")); globs) {
5465 RooArgSet leafs;
5466 out_arg->leafNodeServerList(&leafs);
5467 std::unique_ptr<RooAbsCollection> globals(leafs.selectByAttrib("global", true));
5468 for (auto &aa : *globals) {
5469 if (!globs->contains(*aa)) {
5470 globs->add(*aa);
5471 }
5472 }
5473 }
5474 }
5476 return std::shared_ptr<TObject>(out_arg, [](TObject *) {});
5477 } else if (auto a2 = dynamic_cast<RooAbsData *>(arg.get()); a2) {
5478 if (_ws->import(*a2, RooFit::Embedded())) {
5480 return nullptr;
5481 }
5483 return std::shared_ptr<TObject>(_ws->embeddedData(arg->GetName()), [](TObject *) {});
5484 } else if (arg->InheritsFrom("TNamed")) { // can add any TNamed to a workspace
5485 TObject *out_arg = nullptr;
5486 if (auto fr = dynamic_cast<RooFitResult *>(&*arg); fr && fr->numStatusHistory() == 0) {
5487 // fit results without a status history are treated as snapshots
5488 out_arg = fr->Clone();
5489 const_cast<RooLinkedList &>(GETWSSNAPSHOTS(_ws)).Add(out_arg);
5490 } else {
5491 // ensure will have a unique name for import if must be new
5492 TNamed *aNamed = dynamic_cast<TNamed *>(arg.get());
5493 TString aName = arg->GetName();
5494 out_arg = _ws->genobj(arg->GetName());
5495 int ii = 1;
5496 while (aNamed && out_arg && mustBeNew) {
5497 aNamed->SetName(TString::Format("%s;%d", aName.Data(), ii++));
5498 out_arg = _ws->genobj(aNamed->GetName());
5499 }
5500 if (!out_arg) {
5501 if (aName != arg->GetName()) {
5502 Warning("acquire", "Renaming to %s", arg->GetName());
5503 }
5504 if (_ws->import(*arg, false /*replace existing*/)) {
5506 return nullptr;
5507 }
5508 out_arg = _ws->genobj(arg->GetName());
5509 }
5510 }
5512
5513 return std::shared_ptr<TObject>(out_arg, [](TObject *) {});
5514 }
5516 // Warning("acquire","Not implemented acquisition of object %s",arg->GetName());
5517 // return nullptr;
5518 }
5519 if (!mustBeNew && fProvider) {
5520 auto out = fProvider->getObject(arg->GetName(), arg->ClassName());
5521 if (out)
5522 return out;
5523 }
5524 auto _owned = find(".memory");
5525 if (!_owned) {
5526 _owned = emplace_back(std::make_shared<xRooNode>(".memory", nullptr, *this));
5527 }
5528 // look for exact name, dont use 'find' because doesnt work if trying to find "1" and it doesn't exist, will get back
5529 // idx 1 instead
5530 if (!mustBeNew) {
5531 for (auto &r : *_owned) {
5532 if (strcmp(r->GetName(), arg->GetName()) == 0 && strcmp(r->get()->ClassName(), arg->ClassName()) == 0) {
5533 return r->fComp;
5534 }
5535 }
5536 }
5537 if (!fProvider)
5538 std::cout << GetName() << " taking over " << arg->ClassName() << "::" << arg->GetName() << std::endl;
5539 /*emplace_back(std::make_shared<Node2>(".memory",nullptr,*this))*/
5540 return _owned->emplace_back(std::make_shared<xRooNode>(arg->GetName(), arg, *this))->fComp;
5541 // return arg;
5542}
5543
5544bool xRooNode::SetXaxis(const char *name, const char *title, int nbins, double low, double high)
5545{
5546 RooUniformBinning b(low, high, nbins, name);
5547 b.SetTitle(title);
5548 return SetXaxis(b);
5549}
5550
5551bool xRooNode::SetXaxis(const char *name, const char *title, int nbins, const double *bins)
5552{
5553 RooBinning b(nbins, bins, name);
5554 b.SetTitle(title);
5555 return SetXaxis(b);
5556}
5557
5559{
5560
5561 auto name = binning.GetName();
5562 double high = binning.highBound();
5563 double low = binning.lowBound();
5564 // int nbins = binning.numBins();
5565 auto title = binning.GetTitle();
5566
5567 // if have any dependents and name isn't one of them then stop
5568 auto _deps = vars();
5569 /*if(!_deps.empty() && !_deps.find(name)) {
5570 throw std::runtime_error(TString::Format("%s Does not depend on %s",GetName(),name));
5571 }*/
5572
5573 // object will need to exist
5574 if (!get()) {
5575 if (fParent && !find(GetName())) {
5576 fComp = fParent->Add(*this, "+").fComp;
5577 }
5578 }
5579
5580 auto a = get<RooAbsArg>();
5581 if (!a)
5582 throw std::runtime_error("Cannot SetXaxis of non-arg");
5583
5584 auto _x = acquire<RooRealVar>(name, title, low, high);
5585 _x->setBinning(binning, a->GetName());
5586 _x->getBinning(a->GetName()).SetTitle(title);
5587 if (_x->getBinningNames().size() == 2) {
5588 // this was the first binning, so copy it over to be the default binning too
5589 _x->setBinning(_x->getBinning(a->GetName()));
5590 } else {
5591 // ensure the default binning is wide enough to cover this range
5592 // the alternative to this would be to ensure setNormRange of all pdfs
5593 // are set to correct range (then default can be narrower than some of the named binnings)
5594 if (_x->getMax() < high)
5595 _x->setMax(high);
5596 if (_x->getMin() > low)
5597 _x->setMin(low);
5598 }
5599
5600 if (!_deps.find(name) && get<RooAbsPdf>()) {
5601 // creating a variable for a pdf we will assume it should be an observable
5602 _x->setAttribute("obs");
5603 }
5604
5605 a->setStringAttribute("xvar", _x->GetName());
5606 a->setStringAttribute("binning", a->GetName());
5607 fXAxis.reset(); // remove any existing xaxis
5608
5609 return true;
5610}
5611
5613{
5614 if (!ax)
5615 return false;
5616 if (ax->IsVariableBinSize()) {
5617 return SetXaxis(ax->GetName(), ax->GetTitle(), ax->GetNbins(), ax->GetXbins()->GetArray());
5618 } else {
5619 return SetXaxis(ax->GetName(), ax->GetTitle(), ax->GetNbins(), ax->GetXmin(), ax->GetXmax());
5620 }
5621}
5622
5623bool xRooNode::contains(const std::string &name) const
5624{
5625 return find(name, false) != nullptr;
5626}
5627
5628std::shared_ptr<xRooNode> xRooNode::find(const std::string &name, bool browseResult) const
5629{
5630 std::string partname = (name.find('/') != std::string::npos) ? name.substr(0, name.find('/')) : name;
5631 auto _s = (!get() && fParent) ? fParent->get<RooSimultaneous>()
5632 : get<RooSimultaneous>(); // makes work if doing simPdf.bins()["blah"]
5633 std::string extra = (_s) ? _s->indexCat().GetName() : "";
5634 for (auto &child : *this) {
5635 if (auto _obj = child->get(); name == child->GetName() || partname == child->GetName() ||
5636 (_obj && name == _obj->GetName()) || (_obj && partname == _obj->GetName()) ||
5637 (!extra.empty() && ((extra + "=" + name) == child->GetName() ||
5638 (extra + "=" + partname) == child->GetName()))) {
5639 if (browseResult)
5640 child->browse(); // needed so can go at()->at()->at()...
5641 if (partname != name && name != child->GetName()) {
5642 return child->at(name.substr(partname.length() + 1));
5643 }
5644 return child;
5645 }
5646 if (partname.find('.') != 0) { // do not allow mainChild browsing if trying to find a "." child ... as is done in
5647 // getObject for ".memory"
5648 if (auto x = mainChild(); x && strcmp(child->GetName(), x.GetName()) == 0) {
5649 // can browse directly into main children as if their children were our children
5650 for (auto &child2 : x.browse()) {
5651 if (auto _obj = child2->get(); name == child2->GetName() || partname == child2->GetName() ||
5652 (_obj && name == _obj->GetName()) ||
5653 (_obj && partname == _obj->GetName())) {
5654 if (browseResult)
5655 child2->browse(); // needed for onward read (or is it? there's a browse above too??)
5656 if (partname != name && name != child2->GetName()) {
5657 return child2->at(name.substr(partname.length() + 1));
5658 }
5659 return child2;
5660 }
5661 }
5662 }
5663 }
5664 }
5665 // before giving up see if partName is numeric and indexes within the range
5666 if (TString s(partname); s.IsDec() && size_t(s.Atoi()) < size()) {
5667 auto child2 = at(s.Atoi());
5668 if (partname != name) {
5669 return child2->at(name.substr(partname.length() + 1));
5670 }
5671 return child2;
5672 }
5673 // allow calling of find on a RooWorkspace to access getObject objects ...
5674 if (get<RooWorkspace>() && name != ".memory") {
5675 if (auto obj = getObject(name)) {
5676 auto out = std::make_shared<xRooNode>(obj, *this);
5677 if (browseResult)
5678 out->browse();
5679 return out;
5680 }
5681 }
5682 return nullptr;
5683}
5684
5685std::shared_ptr<xRooNode> xRooNode::operator[](const std::string &name)
5686{
5687 std::string partname = (name.find('/') != std::string::npos) ? name.substr(0, name.find('/')) : name;
5688 browse();
5689 auto _s = (!get() && fParent) ? fParent->get<RooSimultaneous>()
5690 : get<RooSimultaneous>(); // makes work if doing simPdf.bins()["blah"]
5691 std::string extra = (_s) ? _s->indexCat().GetName() : "";
5692 std::shared_ptr<xRooNode> folderNode;
5693 for (auto &child : *this) {
5694 if (name == child->GetName() || partname == child->GetName() ||
5695 (!extra.empty() &&
5696 ((extra + "=" + name) == child->GetName() || (extra + "=" + partname) == child->GetName()))) {
5697 child->browse(); // needed for onward read (or is it? there's a browse above too??)
5698 if (partname != name && name != child->GetName()) {
5699 return child->operator[](name.substr(partname.length() + 1));
5700 }
5701 return child;
5702 }
5703 if (auto x = mainChild(); strcmp(child->GetName(), x.GetName()) == 0) {
5704 // can browse directly into main children as if their children were our children
5705 for (auto &child2 : x.browse()) {
5706 if (name == child2->GetName() || partname == child2->GetName()) {
5707 child2->browse(); // needed for onward read (or is it? there's a browse above too??)
5708 if (partname != name && name != child2->GetName()) {
5709 return child2->operator[](name.substr(partname.length() + 1));
5710 }
5711 return child2;
5712 }
5713 }
5714 }
5715 if (child->fFolder == (std::string("!") + partname)) {
5716 if (!folderNode)
5717 folderNode = std::make_shared<xRooNode>(child->fFolder.c_str(), nullptr, *this);
5718 folderNode->push_back(child);
5719 }
5720 }
5721 if (folderNode) {
5722 if (partname != name) {
5723 return folderNode->operator[](name.substr(partname.length() + 1));
5724 }
5725 return folderNode;
5726 }
5727 // before giving up see if partName is numeric and indexes within the range
5728 if (TString s(partname); s.IsDec() && size_t(s.Atoi()) < size()) {
5729 auto child2 = at(s.Atoi());
5730 if (partname != name) {
5731 return child2->operator[](name.substr(partname.length() + 1));
5732 }
5733 return child2;
5734 }
5735 auto out = std::make_shared<xRooNode>(partname.c_str(), nullptr, *this); // not adding as child yeeet
5736 // special case, if creating a node in the workspace with a specific name, it's a folder node ...
5737 if (get<RooWorkspace>() && partname == "pdfs") {
5738 out->SetName("!pdfs");
5739 }
5740 if (partname != name) {
5741 return out->operator[](name.substr(partname.length() + 1));
5742 }
5743 return out;
5744}
5745
5747{
5748 if (!b) {
5749 for (auto o : *gROOT->GetListOfBrowsers()) {
5750 b = dynamic_cast<TBrowser *>(o);
5751 if (!b || !b->GetBrowserImp())
5752 continue;
5753 if (auto out = GetTreeItem(b); out)
5754 return out;
5755 }
5756 return nullptr;
5757 }
5758 if (!b->GetBrowserImp())
5759 return nullptr;
5760 if (auto _b = dynamic_cast<TGFileBrowser *>(GETACTBROWSER(dynamic_cast<TRootBrowser *>(b->GetBrowserImp()))); _b) {
5761 auto _root = GETROOTDIR(_b);
5762 ;
5763 if (!_root)
5764 _root = GETLISTTREE(_b)->GetFirstItem();
5766 return GETLISTTREE(_b)->FindItemByObj(_root, const_cast<xRooNode *>(this));
5767 }
5768 return nullptr;
5769}
5770
5772{
5773 if (!b) {
5774 for (auto o : *gROOT->GetListOfBrowsers()) {
5775 b = dynamic_cast<TBrowser *>(o);
5776 if (!b || !b->GetBrowserImp())
5777 continue;
5778 if (auto out = GetListTree(b); out)
5779 return out;
5780 }
5781 return nullptr;
5782 }
5783 if (b->GetBrowserImp()) {
5784 if (auto _b = dynamic_cast<TGFileBrowser *>(GETACTBROWSER(dynamic_cast<TRootBrowser *>(b->GetBrowserImp())));
5785 _b) {
5786 auto _root = GETROOTDIR(_b);
5787 if (!_root)
5788 _root = GETLISTTREE(_b)->GetFirstItem();
5789 if (auto item = GETLISTTREE(_b)->FindItemByObj(_root, const_cast<xRooNode *>(this)); item) {
5790 return GETLISTTREE(_b);
5791 }
5792 }
5793 }
5794 return nullptr;
5795}
5796
5797void xRooNode::SetName(const char *name)
5798{
5800 if (auto a = get<RooAbsArg>(); a)
5801 a->setStringAttribute("alias", name);
5802 for (auto o : *gROOT->GetListOfBrowsers()) {
5803 if (auto b = dynamic_cast<TBrowser *>(o); b) {
5804 if (auto item = GetTreeItem(b); item) {
5805 item->SetText(name);
5806 }
5807 }
5808 }
5809}
5810
5811void xRooNode::SetTitle(const char *title)
5812{
5813 if (auto o = (get<TNamed>()); o) {
5814 if (auto c = mainChild(); c.get()) {
5815 c.SetTitle(title);
5816 }
5817 o->SetTitle(title);
5818 }
5819 TNamed::SetTitle(title);
5820}
5821
5823{
5824 if (get<RooArgList>() || (!get() && !(strlen(GetName()) > 0 && (GetName()[0] == '!')) && !fBrowseOperation))
5825 return *this; // nothing to browse - 'collection' nodes should already be populated except for folders
5826 // alternative could have been to mandate that the 'components' of a collection node are the children it has.
5827
5828 auto findByObj = [&](const std::shared_ptr<xRooNode> &n) {
5829 std::vector<std::shared_ptr<xRooNode>> &nn = *this;
5830 for (auto &c : nn) {
5831 if (c->get() == n->get() && strcmp(n->GetName(), c->GetName()) == 0)
5832 return c;
5833 }
5834 return std::shared_ptr<xRooNode>(nullptr);
5835 };
5836
5837 auto appendChildren = [&](const xRooNode &n) {
5838 size_t out = 0;
5839 const std::vector<std::shared_ptr<xRooNode>> &nn(n);
5840 for (auto &c : nn) {
5841 if (auto existing = findByObj(c); existing) {
5842 existing->fTimes++;
5843 existing->fFolder = c->fFolder; // transfer folder assignment
5844 } else {
5845 emplace_back(c);
5846 }
5847 if (!TString(c->GetName()).BeginsWith(".coef"))
5848 out++; // don't count .coef as a child, as technically part of parent
5849 }
5850 return out;
5851 };
5852
5853 const std::vector<std::shared_ptr<xRooNode>> &nn2(*this);
5854 for (auto &c : nn2) {
5855 if (strlen(c->GetName()) > 0 && (c->GetName()[0] == '.')) {
5856 c->fTimes = 1;
5857 continue;
5858 } // never auto-cleanup property children
5859 if (strcmp(c->GetName(), "!.pars") == 0) {
5860 c->fTimes = 1;
5861 continue;
5862 } // special collection, also not cleaned up
5863 if (c->get<RooWorkspace>() || c->get<TFile>()) {
5864 c->fTimes = 1;
5865 continue;
5866 } // workspaces and files not cleaned up: TODO have a nocleanup flag instead
5867 c->fTimes = 0;
5868 }
5869
5870 size_t addedChildren = 0;
5871 if (fBrowseOperation) {
5872 addedChildren += appendChildren(fBrowseOperation(this));
5873 } else {
5874 if (get<RooWorkspace>()) {
5875 addedChildren += appendChildren(datasets());
5876 }
5877
5878 // if (get<RooAbsPdf>() && ((fParent && fParent->get<RooWorkspace>()) || !fParent)) {
5879 // // top-level pdfs will also list the ".vars" property for -- should make this updateable
5880 // //if (auto x = find("!.vars"); !x) { // this is slower because it triggers a browse of !.vars
5881 // if(!contains("!.vars")) {
5882 // emplace_back(std::make_shared<Node2>("!.vars",nullptr,*this));
5883 // } /*else {
5884 // x->fTimes++;
5885 // }*/
5886 // }
5887
5888 // go through components factors and variations, adding all as children if required
5889 addedChildren += appendChildren(components());
5890 if (!get<RooWorkspace>())
5891 addedChildren += appendChildren(factors());
5892 // include coefs if any
5893 auto _coefs = coefs();
5894 if (_coefs.get() && strcmp(_coefs->GetName(), "1") != 0 && strcmp(_coefs->GetName(), "ONE") != 0) {
5895 if (_coefs.size() == 1 && _coefs.get<RooAddition>()) {
5896 if (strcmp(_coefs.at(0)->GetName(), "1") != 0 &&
5897 strcmp(_coefs.at(0)->GetName(), "ONE") != 0) { // don't add the "1"
5898 auto coef = std::make_shared<xRooNode>(".coef", *_coefs.at(0)->get(), *this);
5899 if (auto existing = findByObj(coef); existing) {
5900 existing->fTimes++;
5901 existing->fFolder = _coefs.at(0)->fFolder; // transfer folder assignment
5902 } else {
5903 emplace_back(coef);
5904 }
5905 }
5906 } else {
5907 if (auto existing = find(_coefs.GetName()); existing) {
5908 existing->fTimes++;
5909 existing->fFolder = _coefs.fFolder; // transfer folder assignment
5910 } else {
5911 emplace_back(std::make_shared<xRooNode>(_coefs));
5912 }
5913 }
5914 }
5915 addedChildren += appendChildren(variations());
5916 if (get<ParamHistFunc>() || get<RooSimultaneous>())
5917 addedChildren += appendChildren(bins());
5918 if (get<RooAbsData>())
5919 addedChildren += appendChildren(obs());
5920 }
5921 // if has no children and is a RooAbsArg, add all the proxies
5922 if (auto arg = get<RooAbsArg>(); arg && addedChildren == 0) {
5923 for (int i = 0; i < arg->numProxies(); i++) {
5924 auto _proxy = arg->getProxy(i);
5925 if (auto a = dynamic_cast<RooArgProxy *>(_proxy)) {
5926 auto c = std::make_shared<xRooNode>(TString::Format(".%s", _proxy->name()), *(a->absArg()), *this);
5927 if (auto existing = findByObj(c); existing) {
5928 existing->fTimes++;
5929 existing->fFolder = c->fFolder; // transfer folder assignment
5930 } else {
5931 // mark any existing children with the same name for cleanup - this happens e.g. if did a Replace on one
5932 // of these nodes note that the child nodes will still become reordered (the old node will be deleted,
5933 // new node will appear at end)
5934 for (auto &child : *this) {
5935 if (strcmp(child->GetName(), c->GetName()) == 0) {
5936 child->fTimes = 0;
5937 }
5938 }
5939 emplace_back(c);
5940 }
5941 } else if (auto s = dynamic_cast<RooAbsCollection *>(_proxy)) {
5942 for (auto a2 : *s) {
5943 auto c = std::make_shared<xRooNode>(*a2, *this);
5944 if (arg->numProxies() != 1) {
5945 c->fFolder = std::string("!.") +
5946 _proxy->name(); // don't put in a folder if there's just 1 proxy (the collection)
5947 }
5948 if (auto existing = findByObj(c); existing) {
5949 existing->fTimes++;
5950 existing->fFolder = c->fFolder; // transfer folder assignment
5951 } else {
5952 emplace_back(c);
5953 }
5954 }
5955 }
5956 }
5957 /*for(auto& s : arg->servers()) {
5958 auto c = std::make_shared<xRooNode>(*s,*this);
5959 if (auto existing = findByObj(c); existing) {
5960 existing->fTimes++;
5961 existing->fFolder = c->fFolder; // transfer folder assignment
5962 } else {
5963 emplace_back(c);
5964 }
5965 }*/
5966 } else if (auto ir = get<RooStats::HypoTestInverterResult>()) {
5967 // check if we already have a hypoSpace in our memory
5968 bool hasHS = false;
5969 for (auto &c : fBrowsables) {
5970 if (strcmp(c->GetName(), ".memory") == 0 && c->get<xRooHypoSpace>()) {
5971 hasHS = true;
5972 break;
5973 }
5974 }
5975 if (!hasHS) {
5976 // add the HS
5977 auto hs =
5978 fBrowsables.emplace_back(std::make_shared<xRooNode>(".memory", std::make_shared<xRooHypoSpace>(ir), *this));
5979 // add the hypoPoints first so they appear first
5980 auto _axes = hs->get<xRooHypoSpace>()->axes();
5981
5982 int i = 0;
5983 for (auto &hp : *hs->get<xRooHypoSpace>()) {
5984 TString coordString;
5985 for (auto a : _axes) {
5986 if (a != _axes.first())
5987 coordString += ",";
5988 coordString +=
5989 TString::Format("%s=%g", a->GetName(), hp.coords->getRealValue(a->GetName(), ir->GetXValue(i)));
5990 }
5991 auto hpn = emplace_back(std::make_shared<xRooNode>(coordString, hp.hypoTestResult, hs));
5992 hpn->fTimes++;
5993 hpn->fBrowsables.emplace_back(std::make_shared<xRooNode>(
5994 ".memory", std::shared_ptr<xRooNLLVar::xRooHypoPoint>(&hp, [](xRooNLLVar::xRooHypoPoint *) {}), hpn));
5995 i++;
5996 }
5997 } else {
5998 // ensure all hypoTestResults are flagged as keep-alive
5999 std::vector<std::shared_ptr<xRooNode>> &nn = *this;
6000 for (auto &c : nn) {
6001 if (c->get<RooStats::HypoTestResult>())
6002 c->fTimes++;
6003 }
6004 }
6005 // xRooNode tests;
6006 // for(int i=0;i<ir->ArraySize();i++) {
6007 // tests.push_back(std::make_shared<xRooNode>(TString::Format("%g",ir->GetXValue(i)),*ir->GetResult(i),*this));
6008 // }
6009 // appendChildren(tests);
6010 } else if (get<RooStats::HypoTestResult>()) {
6011
6012 // create the xRooHypoPoint if necessary
6013 xRooNLLVar::xRooHypoPoint *hp = nullptr;
6014 for (auto &c : fBrowsables) {
6015 if (strcmp(c->GetName(), ".memory") == 0 && c->get<xRooNLLVar::xRooHypoPoint>()) {
6016 hp = c->get<xRooNLLVar::xRooHypoPoint>();
6017 c->fTimes++; // keep it alive
6018 break;
6019 }
6020 }
6021 if (!hp) {
6022 auto shp =
6023 std::make_shared<xRooNLLVar::xRooHypoPoint>(std::dynamic_pointer_cast<RooStats::HypoTestResult>(fComp));
6024 fBrowsables.emplace_back(std::make_shared<xRooNode>(".memory", shp, *this));
6025 hp = shp.get();
6026 }
6027
6028 xRooNode fits;
6029
6030 if (auto fit = hp->ufit()) {
6031 fits.emplace_back(std::make_shared<xRooNode>(fit, *this))->TNamed::SetName("ufit");
6032 }
6033 if (auto fit = hp->cfit_null()) {
6034 fits.emplace_back(std::make_shared<xRooNode>(fit, *this))->TNamed::SetName("cfit_null");
6035 }
6036 if (auto fit = hp->cfit_alt()) {
6037 fits.emplace_back(std::make_shared<xRooNode>(fit, *this))->TNamed::SetName("cfit_alt");
6038 }
6039 if (auto fit = hp->gfit()) {
6040 fits.emplace_back(std::make_shared<xRooNode>(fit, *this))->TNamed::SetName("gfit");
6041 }
6042 if (auto asi = hp->asimov()) {
6043 auto asiP = fits.emplace_back(std::make_shared<xRooNode>(
6044 asi->hypoTestResult ? asi->hypoTestResult : std::make_shared<RooStats::HypoTestResult>(asi->result()),
6045 *this));
6046 asiP->TNamed::SetName("asimov");
6047 asiP->fBrowsables.emplace_back(std::make_shared<xRooNode>(".memory", asi, asiP));
6048 }
6049 appendChildren(fits);
6050 }
6051
6052 // clear anything that has fTimes = 0 still
6053 auto it = std::vector<std::shared_ptr<xRooNode>>::begin();
6054 while (it != std::vector<std::shared_ptr<xRooNode>>::end()) {
6055 if (it->get()->fTimes == 0) {
6056 for (auto o : *gROOT->GetListOfBrowsers()) {
6057 auto b = dynamic_cast<TBrowser *>(o);
6058 if (b && b->GetBrowserImp()) { // browserImp is null if browser was closed
6059 // std::cout << GetPath() << " Removing " << it->get()->GetPath() << std::endl;
6060
6061 if (auto _b =
6062 dynamic_cast<TGFileBrowser *>(GETACTBROWSER(dynamic_cast<TRootBrowser *>(b->GetBrowserImp())));
6063 _b) {
6064 auto _root = GETROOTDIR(_b);
6065 if (!_root)
6066 _root = GETLISTTREE(_b)->GetFirstItem();
6067 if (auto item = GETLISTTREE(_b)->FindItemByObj(_root, this); item) {
6068 GETLISTTREE(_b)->OpenItem(item);
6069 }
6070 }
6071
6072 b->RecursiveRemove(
6073 it->get()); // problem: if obj is living in a collapsed node it wont actually get deleted
6074 /*auto _b = dynamic_cast<TGFileBrowser*>( dynamic_cast<TRootBrowser*>(b->GetBrowserImp())->fActBrowser );
6075 if (_b) {
6076 std::cout << _b->fRootDir->GetText() << std::endl;
6077 if (auto item = _b->fListTree->FindItemByObj(_b->fRootDir,it->get()); item) {
6078 std::cout << "Found obj: " << item << " " << item->GetText() << std::endl;
6079 _b->fListTree->RecursiveDeleteItem(_b->fRootDir,it->get());
6080 }
6081
6082 //b->RecursiveRemove(it->get());
6083 if (auto item = _b->fListTree->FindItemByObj(_b->fRootDir,it->get()); item) {
6084 std::cout << "Still Found obj: " << item << std::endl;
6085 }
6086 _b->fListTree->ClearViewPort();
6087
6088 }*/
6089 }
6090 }
6091 /*it->get()->ResetBit(TObject::kNotDeleted); ++it;*/ it = erase(it);
6092 } else {
6093 ++it;
6094 }
6095 }
6096
6097 return *this;
6098}
6099
6100////////////////////////////////////////////////////////////////////////////////
6101/// List of observables (global and regular) of this node.
6102
6104{
6105 xRooNode out(".obs", std::make_shared<RooArgList>(), *this);
6106 out.get<RooArgList>()->setName((GetPath() + ".obs").c_str());
6107 for (auto o : vars()) {
6108 if (o->get<RooAbsArg>()->getAttribute("obs")) {
6109 out.get<RooArgList>()->add(*o->get<RooAbsArg>());
6110 out.emplace_back(o);
6111 }
6112 }
6113 return out;
6114}
6115
6116////////////////////////////////////////////////////////////////////////////////
6117/// List of global observables of this node.
6118
6120{
6121 xRooNode out(".globs", std::make_shared<RooArgList>(), *this);
6122 out.get<RooArgList>()->setName((GetPath() + ".globs").c_str());
6123 for (auto o : obs()) {
6124 if (o->get<RooAbsArg>()->getAttribute("global")) {
6125 out.get<RooArgList>()->add(*o->get<RooAbsArg>());
6126 out.emplace_back(o);
6127 }
6128 }
6129 return out;
6130}
6131
6132////////////////////////////////////////////////////////////////////////////////
6133/// List of regular observables of this node.
6134
6136{
6137 xRooNode out(".robs", std::make_shared<RooArgList>(), *this);
6138 out.get<RooArgList>()->setName((GetPath() + ".robs").c_str());
6139 for (auto o : obs()) {
6140 if (!o->get<RooAbsArg>()->getAttribute("global")) {
6141 out.get<RooArgList>()->add(*o->get<RooAbsArg>());
6142 out.emplace_back(o);
6143 }
6144 }
6145 return out;
6146}
6147
6148////////////////////////////////////////////////////////////////////////////////
6149/// List of parameters (non-observables) of this node.
6150
6152{
6153 if (strcmp(GetName(), ".bins") == 0 && fParent) {
6154 // return pars of the parent - this method is used by covariances() if e.g. do node.bins().covariances()
6155 return fParent->pars();
6156 }
6157 xRooNode out(".pars", std::make_shared<RooArgList>(), *this);
6158 out.get<RooArgList>()->setName((GetPath() + ".pars").c_str());
6159 for (auto o : vars()) {
6160 if (!o->get<RooAbsArg>()->getAttribute("obs")) {
6161 out.get<RooArgList>()->add(*(o->get<RooAbsArg>()));
6162 out.emplace_back(o);
6163 }
6164 }
6165 return out;
6166}
6167
6168////////////////////////////////////////////////////////////////////////////////
6169/// List of parameters that are currently constant
6170
6172{
6173 xRooNode out(".consts", std::make_shared<RooArgList>(), *this);
6174 out.get<RooArgList>()->setName((GetPath() + ".consts").c_str());
6175 for (auto o : pars()) {
6176 if (o->get<RooAbsArg>()->getAttribute("Constant") || o->get<RooConstVar>()) {
6177 out.get<RooArgList>()->add(*o->get<RooAbsArg>());
6178 out.emplace_back(o);
6179 }
6180 }
6181 return out;
6182}
6183
6184////////////////////////////////////////////////////////////////////////////////
6185/// List of parameters that are currently non-constant
6186/// These parameters do not have the "Constant" attribute
6187
6189{
6190 xRooNode out(".floats", std::make_shared<RooArgList>(), *this);
6191 out.get<RooArgList>()->setName((GetPath() + ".floats").c_str());
6192 for (auto o : pars()) {
6193 if (!o->get<RooAbsArg>()->getAttribute("Constant") && !o->get<RooConstVar>()) {
6194 out.get<RooArgList>()->add(*o->get<RooAbsArg>());
6195 out.emplace_back(o);
6196 }
6197 }
6198 return out;
6199}
6200
6201////////////////////////////////////////////////////////////////////////////////
6202/// List of parameters of interest: parameters marked as "of interest"
6203/// These parameters have the "poi" attribute
6204
6206{
6207 xRooNode out(".poi", std::make_shared<RooArgList>(), *this);
6208 out.get<RooArgList>()->setName((GetPath() + ".poi").c_str());
6209 for (auto o : pars()) {
6210 if (o->get<RooAbsArg>()->getAttribute("poi")) {
6211 out.get<RooArgList>()->add(*o->get<RooAbsArg>());
6212 out.emplace_back(o);
6213 }
6214 }
6215 return out;
6216}
6217
6218////////////////////////////////////////////////////////////////////////////////
6219/// List of nuisance parameters: non-constant parameters that are not marked of interest,
6220/// as well as any parameters that have been marked by the "np" attribute
6221
6223{
6224 xRooNode out(".np", std::make_shared<RooArgList>(), *this);
6225 out.get<RooArgList>()->setName((GetPath() + ".np").c_str());
6226 for (auto o : pars()) {
6227 if (o->get<RooAbsArg>()->getAttribute("np") ||
6228 (!o->get<RooAbsArg>()->getAttribute("Constant") && !o->get<RooAbsArg>()->getAttribute("poi") &&
6229 !o->get<RooConstVar>())) {
6230 out.get<RooArgList>()->add(*o->get<RooAbsArg>());
6231 out.emplace_back(o);
6232 }
6233 }
6234 return out;
6235}
6236
6237////////////////////////////////////////////////////////////////////////////////
6238/// List of prespecified parameters: non-floatable parameters
6239
6241{
6242 xRooNode out(".pp", std::make_shared<RooArgList>(), *this);
6243 out.get<RooArgList>()->setName((GetPath() + ".pp").c_str());
6244 for (auto o : pars()) {
6245 if (!o->get<RooAbsArg>()->getAttribute("np") && !o->get<RooAbsArg>()->getAttribute("poi") &&
6246 (o->get<RooAbsArg>()->getAttribute("Constant") || o->get<RooConstVar>())) {
6247 out.get<RooArgList>()->add(*o->get<RooAbsArg>());
6248 out.emplace_back(o);
6249 }
6250 }
6251 return out;
6252}
6253
6254////////////////////////////////////////////////////////////////////////////////
6255/// List of variables (observables and parameters) of this node
6256
6258{
6259 xRooNode out(".vars", std::make_shared<RooArgList>(), *this);
6260 out.get<RooArgList>()->setName((GetPath() + ".vars").c_str());
6261 if (auto coll = get<RooAbsCollection>(); coll) {
6262 for (auto &x : *this) {
6263 for (auto &y : x->vars()) {
6264 out.push_back(y);
6265 }
6266 }
6267 return out;
6268 }
6269 if (auto p = get<RooAbsArg>(); p) {
6270 // also need to get all constPars so use leafNodeServerList .. will include self if is fundamental, which is what
6271 // we want
6272 // ensure all globs appear after robs, as we rely on this ordering for picking "x" var in "reduced" method
6273 xRooNode _globs;
6274 RooArgSet allLeaves;
6275 p->leafNodeServerList(&allLeaves);
6276 for (auto &c : allLeaves) {
6277 if (c->isFundamental() || (dynamic_cast<RooConstVar *>(c) && !TString(c->GetName()).IsFloat())) {
6278 if (!c->getAttribute("global")) {
6279 out.get<RooArgList>()->add(*c);
6280 out.emplace_back(std::make_shared<xRooNode>(*c, *this));
6281 }
6282 if (c->getAttribute("global")) {
6283 _globs.emplace_back(std::make_shared<xRooNode>(*c, *this));
6284 _globs.back()->fFolder = "!globs";
6285 } else if (c->getAttribute("obs")) {
6286 out.back()->fFolder = "!robs";
6287 } else if (c->getAttribute("poi")) {
6288 out.back()->fFolder = "!poi";
6289 } else if (c->getAttribute("np") ||
6290 (!c->getAttribute("Constant") && !c->getAttribute("poi") && c->IsA() != RooConstVar::Class())) {
6291 out.back()->fFolder = "!np";
6292 } else if (!c->getAttribute("Constant") && c->IsA() != RooConstVar::Class()) {
6293 out.back()->fFolder = "!floats";
6294 } else {
6295 out.back()->fFolder = "!pp";
6296 }
6297 }
6298 }
6299 for (auto g : _globs) {
6300 out.get<RooArgList>()->add(*g->get<RooAbsArg>());
6301 out.emplace_back(g);
6302 }
6303 } else if (auto p2 = get<RooAbsData>(); p2) {
6304 for (auto a : *p2->get()) {
6305 a->setAttribute("obs");
6306 out.emplace_back(std::make_shared<xRooNode>(*a, *this));
6307 out.get<RooArgList>()->add(*a);
6308 }
6309 if (auto _dglobs = p2->getGlobalObservables()) {
6310 for (auto &a : *_dglobs) {
6311 a->setAttribute("obs");
6312 a->setAttribute("global");
6313 out.emplace_back(std::make_shared<xRooNode>(*a, *this));
6314 out.get<RooArgList>()->add(*a);
6315 }
6316 } else if (auto _globs = find(".globs"); _globs && _globs->get<RooAbsCollection>()) {
6317 for (auto &a : *_globs->get<RooAbsCollection>()) {
6318 a->setAttribute("obs");
6319 a->setAttribute("global");
6320 out.emplace_back(std::make_shared<xRooNode>(*a, *this));
6321 out.get<RooArgList>()->add(*a);
6322 }
6323 } else if (auto _ws = ws(); _ws) {
6324 if (auto _globs2 = dynamic_cast<RooArgSet *>(GETWSSNAPSHOTS(_ws).find(p2->GetName())); _globs2) {
6325 for (auto a : *_globs2) {
6326 a->setAttribute("obs");
6327 a->setAttribute("global");
6328 out.emplace_back(std::make_shared<xRooNode>(*a, *this));
6329 out.get<RooArgList>()->add(*a);
6330 }
6331 } else if (auto _gl = GETWSSETS(_ws).find("globalObservables"); _gl != GETWSSETS(_ws).end()) {
6332 for (auto &_g : _gl->second) {
6333 auto _clone = std::shared_ptr<RooAbsArg>(dynamic_cast<RooAbsArg *>(_g->Clone(_g->GetName())));
6334 if (auto v = std::dynamic_pointer_cast<RooAbsRealLValue>(_clone); v && _g->getStringAttribute("nominal"))
6335 v->setVal(TString(_g->getStringAttribute("nominal")).Atof());
6336 out.emplace_back(std::make_shared<xRooNode>(_clone, *this));
6337 out.get<RooArgList>()->add(*_clone);
6338 }
6339 } else if (fParent) {
6340 // note: this is slow in large workspaces ... too many obs to look through?
6341 std::unique_ptr<RooAbsCollection> _globs3(fParent->obs().get<RooArgList>()->selectByAttrib("global", true));
6342 // std::unique_ptr<RooAbsCollection> _globs(_ws->allVars().selectByAttrib("global",true)); - tried this to
6343 // be quicker but it wasn't
6344 for (auto &_g : *_globs3) {
6345 auto _clone = std::shared_ptr<RooAbsArg>(dynamic_cast<RooAbsArg *>(_g->Clone(_g->GetName())));
6346 if (auto v = std::dynamic_pointer_cast<RooAbsRealLValue>(_clone); v && _g->getStringAttribute("nominal"))
6347 v->setVal(TString(_g->getStringAttribute("nominal")).Atof());
6348 out.emplace_back(std::make_shared<xRooNode>(_clone, *this));
6349 out.get<RooArgList>()->add(*_clone);
6350 }
6351 }
6352 }
6353 } else if (auto w = get<RooWorkspace>(); w) {
6354 for (auto a : w->components()) {
6355 if (a->InheritsFrom(RooRealVar::Class()) || a->InheritsFrom(RooCategory::Class()) ||
6356 a->InheritsFrom(RooConstVar::Class())) {
6357 out.emplace_back(std::make_shared<xRooNode>(*a, *this));
6358 }
6359 }
6360 }
6361 return out;
6362}
6363
6365{
6366 xRooNode out(".components", nullptr, *this);
6367
6368 if (auto p = get<RooAddPdf>(); p) {
6369 // only add each pdf once (the coefs will be accumulated in coefs() method) ...
6370 std::set<RooAbsArg *> donePdfs;
6371 for (auto &o : p->pdfList()) {
6372 if (donePdfs.count(o))
6373 continue;
6374 out.emplace_back(std::make_shared<xRooNode>(*o, *this));
6375 donePdfs.insert(o);
6376 }
6377 } else if (auto p2 = get<RooRealSumPdf>(); p2) {
6378 // check for common prefixes and suffixes, will use to define aliases to shorten names
6379 // if have more than 1 function
6380 // TString commonPrefix=""; TString commonSuffix="";
6381 // if (p->funcList().size() > 1) {
6382 // bool checked=false;
6383 // for(auto& o : p->funcList()) {
6384 // if (!checked) {
6385 // commonPrefix = o->GetName(); commonSuffix = o->GetName(); checked=true;
6386 // } else {
6387 //
6388 // }
6389 // }
6390 // }
6391 std::set<RooAbsArg *> doneFuncs;
6392 for (auto &o : p2->funcList()) {
6393 if (doneFuncs.count(o))
6394 continue;
6395 out.emplace_back(std::make_shared<xRooNode>(*o, *this));
6396 doneFuncs.insert(o);
6397 }
6398 } else if (auto p3 = get<RooAddition>(); p3) {
6399 for (auto &o : p3->list()) {
6400 out.emplace_back(std::make_shared<xRooNode>(*o, *this));
6401 }
6402 } else if (auto p4 = get<RooAbsCollection>(); p4) {
6403 for (auto &a : *p4) {
6404 out.emplace_back(std::make_shared<xRooNode>(*a, *this));
6405 }
6406 } else if (auto p5 = get<RooWorkspace>(); p5) {
6407 for (auto &o : p5->components()) {
6408 // only top-level nodes (only clients are integrals or things that aren't part of the workspace)
6409 // if (o->hasClients()) continue;
6410 bool hasClients = false;
6411 for (auto &c : o->clients()) {
6412 if (!c->InheritsFrom("RooRealIntegral") && p5 == GETWS(c)) {
6413 hasClients = true;
6414 break;
6415 }
6416 }
6417 if (hasClients)
6418 continue;
6419 out.emplace_back(std::make_shared<xRooNode>(*o, *this));
6420 if (o->InheritsFrom("RooAbsPdf")) {
6421 out.back()->fFolder = "!pdfs";
6422 } else {
6423 out.back()->fFolder = "!scratch";
6424 }
6425 }
6426 for (auto &o : p5->allGenericObjects()) {
6427 if (auto fr = dynamic_cast<RooFitResult *>(o); fr) {
6428 TString s(fr->GetTitle());
6429 if (s.Contains(';'))
6430 s = s(0, s.Index(';'));
6431 if (auto _pdf = out.find(s.Data()); _pdf) {
6432 // std::cout << " type = " << _pdf->get()->ClassName() << std::endl;
6433 out.emplace_back(std::make_shared<xRooNode>(fr->GetName(), *fr, _pdf));
6434 // for a while, this node's parent pointed to something of type Node2!!
6435 // how to fix??? - I fxied it with a new constructor to avoid the shared_ptr<Node2> calling the const
6436 // Node2& constructor via getting wrapped in a Node2(shared_ptr<TObject>) call
6437 // out.back()->fParent = _pdf;
6438 // std::cout << " type2 = " << out.back()->fParent->get()->ClassName() << std::endl;
6439 } else {
6440 out.emplace_back(std::make_shared<xRooNode>(fr->GetName(), *fr, *this));
6441 }
6442 out.back()->fFolder = "!fits";
6443 } else {
6444 out.emplace_back(std::make_shared<xRooNode>(*o, *this));
6445 if (strcmp(out.back()->get()->ClassName(), "TStyle") == 0) {
6446 out.back()->fFolder = "!styles";
6447 } else if (strcmp(out.back()->get()->ClassName(), "RooStats::HypoTestInverterResult") == 0) {
6448 out.back()->fFolder = "!scans";
6449 } else if (strcmp(out.back()->get()->ClassName(), "RooStats::ModelConfig") == 0) {
6450 out.back()->fFolder = "!models";
6451 } else {
6452 out.back()->fFolder = "!objects";
6453 }
6454 }
6455 }
6456 for (auto &[k, v] : GETWSSETS(p5)) {
6457 // skip 'CACHE' sets because they are auto-removed when sanitizing workspaces, which will invalidate these
6458 // children
6459 if (k.find("CACHE_") == 0)
6460 continue;
6461 out.emplace_back(std::make_shared<xRooNode>(k.c_str(), v, *this));
6462 out.back()->fFolder = "!sets";
6463 }
6464
6465 RooLinkedList snaps = GETWSSNAPSHOTS(p5);
6466 std::unique_ptr<TIterator> iter(snaps.MakeIterator());
6467 TObject *snap;
6468 while ((snap = iter->Next())) {
6469 out.emplace_back(std::make_shared<xRooNode>(*snap, *this));
6470 out.back()->fFolder = "!snapshots";
6471 }
6472 } else if (auto mc = get<RooStats::ModelConfig>()) {
6473 // add the pdf as a child, and the external constraints set if its there
6474 if (mc->GetPdf()) {
6475 out.emplace_back(std::make_shared<xRooNode>(".pdf", *mc->GetPdf(), *this));
6476 }
6477 if (mc->GetExternalConstraints()) {
6478 out.emplace_back(std::make_shared<xRooNode>(".extCons", *mc->GetExternalConstraints(), *this));
6479 }
6480 } else if (strlen(GetName()) > 0 && GetName()[0] == '!' && fParent) {
6481 // special case of dynamic property
6482 if (TString(GetName()) == "!.pars") {
6483 for (auto &c : fParent->pars()) {
6484 out.emplace_back(c);
6485 }
6486 } else {
6487 // the components of a folder are the children of the parent (after browsing) that live in this folder
6488 fParent->browse();
6489 for (auto &c : *fParent) {
6490 if (c->fFolder == GetName()) {
6491 out.emplace_back(c);
6492 }
6493 }
6494 }
6495 }
6496
6497 return out;
6498}
6499
6500////////////////////////////////////////////////////////////////////////////////
6501/// bins of a channel or sample, or channels of a multi-channel pdf
6502
6504{
6505 xRooNode out(".bins", nullptr, *this);
6506
6507 if (auto p = get<RooSimultaneous>(); p) {
6508 std::map<int, std::shared_ptr<xRooNode>> cats; // fill into a map to preserve index ordering
6509 for (auto &c : p->indexCat()) { // is alphabetical in labels
6510 auto pp = p->getPdf(c.first.c_str());
6511 if (!pp)
6512 continue;
6513 cats[c.second] =
6514 std::make_shared<xRooNode>(TString::Format("%s=%s", p->indexCat().GetName(), c.first.c_str()), *pp, *this);
6515 }
6516 for (auto &[_, n] : cats)
6517 out.emplace_back(n);
6518 } else if (auto phf = get<ParamHistFunc>(); phf) {
6519 int i = 1;
6520#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
6521 auto &pSet = phf->_paramSet;
6522#else
6523 auto &pSet = phf->paramList();
6524#endif
6525 for (auto par : pSet) {
6526 out.emplace_back(std::make_shared<xRooNode>(*par, *this));
6527 out.back()->fBinNumber = i;
6528 i++;
6529 }
6530 } else if (auto ax = GetXaxis(); ax) {
6531 for (int i = 1; i <= ax->GetNbins(); i++) {
6532 // create a RooProduct of all bin-specific factors of all shapeFactors
6533 std::vector<RooAbsArg *> _factors;
6534 for (auto f : factors()) {
6535 if (f->get<ParamHistFunc>()) {
6536 if (f->bins()[i - 1]->get<RooProduct>()) {
6537 for (auto &ss : f->bins()[i - 1]->factors())
6538 _factors.push_back(ss->get<RooAbsArg>());
6539 } else {
6540 _factors.push_back(f->bins()[i - 1]->get<RooAbsArg>());
6541 }
6542 }
6543 }
6544 out.emplace_back(std::make_shared<xRooNode>(
6545 TString::Format("%g<=%s<%g", ax->GetBinLowEdge(i), ax->GetParent()->GetName(), ax->GetBinLowEdge(i + 1)),
6546 _factors.empty() ? nullptr
6547 : std::make_shared<RooProduct>(TString::Format("%s.binFactors.bin%d", GetName(), i),
6548 "binFactors", RooArgList()),
6549 *this));
6550 for (auto f : _factors) {
6551#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
6552 out.back()->get<RooProduct>()->_compRSet.add(*f);
6553#else
6554 const_cast<RooArgList &>(out.back()->get<RooProduct>()->realComponents()).add(*f);
6555#endif
6556 }
6557 out.back()->fBinNumber = i;
6558 }
6559 }
6560
6561 return out;
6562}
6563
6564xRooNode xRooNode::coefs(bool recurse) const
6565{
6567
6568 if (recurse && fParent) {
6569 // get our coefs and multiply it by the parents coefs ...
6570 auto ourCoefs = xRooNode::coefs(false);
6571 auto parentCoefs = fParent->coefs(true);
6572 if (!parentCoefs.get<RooAbsReal>()) {
6573 // no coefs to include, just return our coefs
6574 return ourCoefs;
6575 }
6576 if (!ourCoefs.get<RooAbsReal>()) {
6577 // just return the parent's coefs
6578 return parentCoefs;
6579 }
6580 // if got here, must combine parentCoefs and outCoefs into a RooProduct
6581 xRooNode out(".recursiveCoefs",
6582 std::make_shared<RooProduct>(".recursiveCoefs",
6583 TString::Format("Recursive Coefficients of %s", GetName()),
6584 *ourCoefs.get<RooAbsReal>(), *parentCoefs.get<RooAbsReal>()),
6585 *this);
6586 // keep alive the two coef nodes by adding to out's memory
6587 auto mem = out.emplace_back(std::make_shared<xRooNode>(".memory", nullptr, *this));
6588 mem->emplace_back(std::make_shared<xRooNode>(ourCoefs));
6589 mem->emplace_back(std::make_shared<xRooNode>(parentCoefs));
6590 return out;
6591 }
6592
6593 bool isResidual = false;
6594
6595 // if parent is a sumpdf or addpdf then include the coefs
6596 // if func appears multiple times then coefs must be combined into a RooAddition temporary
6597 if (fParent) {
6598 // handle case where filters are applied .. need to pass through these
6599 // do this by iterating while fComp is null
6600 auto parent = fParent;
6601 if (!parent->fComp) {
6602 while (!parent->fComp && parent->fParent) {
6603 parent = parent->fParent;
6604 }
6605 // parent should now be node above the filters ... need parent of that
6606 parent = parent->fParent;
6607 if (!parent)
6608 parent = fParent; // revert t original parent in case something went wrong
6609 }
6610 if (auto p = parent->get<RooRealSumPdf>(); p) {
6611 std::size_t i = 0;
6612 for (auto &o : p->funcList()) {
6613 if (o == get()) {
6614 if (i >= p->coefList().size()) {
6615 isResidual = true;
6616 coefs.add(p->coefList());
6617 } else {
6618 coefs.add(*p->coefList().at(i));
6619 }
6620 }
6621 i++;
6622 }
6623 } else if (auto p2 = parent->get<RooAddPdf>(); p2) {
6624 std::size_t i = 0;
6625 if (p2->coefList().empty()) {
6626 // this can happen if all pdfs are extended then the coef is effectively the
6627 // expected number of events
6628 // TODO: test behaviour of xRooNode under this scenario (are histograms correct?)
6629 } else {
6630 for (auto &o : p2->pdfList()) {
6631 if (o == get()) {
6632 if (i >= p2->coefList().size()) {
6633 isResidual = true;
6634 coefs.add(p2->coefList());
6635 } else {
6636 coefs.add(*p2->coefList().at(i));
6637 }
6638 }
6639 i++;
6640 }
6641 }
6642 }
6643 }
6644 if (isResidual) {
6645 // return a node representing 1.-sumOfCoefs
6646 // involves creating sumOfCoefs unless there is only 1 coef, then just use that
6647 auto coefSum = coefs.empty()
6648 ? nullptr
6649 : (coefs.size() == 1 ? std::shared_ptr<RooAbsArg>(coefs.at(0), [](RooAbsArg *) {})
6650 : std::make_shared<RooAddition>((isResidual) ? ".sumOfCoefs" : ".coefs",
6651 "Coefficients of", coefs));
6652 xRooNode out(".coef", coefSum ? std::dynamic_pointer_cast<RooAbsArg>(std::make_shared<RooFormulaVar>(
6653 ".coef", "1-sum(otherCoefs)", "1. - @0", *coefSum))
6654 : nullptr /* should we return a "1." instead? */);
6655 if (coefSum && coefs.size() != 1) {
6656 out.emplace_back(std::make_shared<xRooNode>(".memory", nullptr, *this))
6657 ->emplace_back(
6658 std::make_shared<xRooNode>(".sumOfCoefs", coefSum, out)); // added to keep the sum alive! with the node
6659 }
6660 if (!coefs.empty()) {
6661 out.browse();
6662 }
6663 return out;
6664 } else if (coefs.size() == 1) {
6665 xRooNode out(".coef", std::shared_ptr<RooAbsArg>(coefs.at(0), [](RooAbsArg *) {}), *this);
6666 if (!coefs.empty()) {
6667 out.browse();
6668 }
6669 return out;
6670 } else {
6671 auto coefSum =
6672 coefs.empty()
6673 ? nullptr
6674 : std::make_shared<RooAddition>(".coefs", TString::Format("Coefficients of %s", GetName()), coefs);
6675 xRooNode out(".coefs", coefSum, *this);
6676 if (!coefs.empty())
6677 out.browse();
6678
6679 return out;
6680 }
6681}
6682
6684{
6685 xRooNode out(".factors", nullptr, *this);
6686
6687 if (auto p = get<RooProdPdf>(); p) {
6688 auto _main = mainChild();
6689 if (auto a = _main.get<RooRealSumPdf>(); a && !a->getStringAttribute("alias")) {
6690 a->setStringAttribute("alias", "samples");
6691 } else if (auto a2 = _main.get<RooAddPdf>(); a2 && !a2->getStringAttribute("alias")) {
6692 a2->setStringAttribute("alias", "components");
6693 }
6694 int _npdfs = p->pdfList().size();
6695 for (auto &o : p->pdfList()) {
6696 out.emplace_back(std::make_shared<xRooNode>(*o, *this));
6697 if (_npdfs > 5 && o != _main.get() && out.back()->robs().size() == 0) // constraints have no robs in them
6698 out.back()->fFolder = "!constraints";
6699 }
6700 } else if (auto p2 = get<RooProduct>(); p2) {
6701 for (auto &o : p2->components()) {
6702 if (o->InheritsFrom("RooProduct")) {
6703 // get factors of this term
6704 auto x = xRooNode("tmp", *o, *this).factors();
6705 for (auto &n : x) {
6706 out.emplace_back(std::make_shared<xRooNode>(n->GetName(), n->fComp, *this));
6707 }
6708 } else {
6709 out.emplace_back(std::make_shared<xRooNode>(*o, *this));
6710 }
6711 }
6712 } else if (auto w = get<RooWorkspace>(); w) {
6713 // if workspace, return all functions (not pdfs) that have a RooProduct as one of their clients
6714 // or not clients
6715 // exclude obs and globs
6716 auto oo = obs(); // need to keep alive as may contain owning globs
6717 auto &_obs = *(oo.get<RooArgList>());
6718 for (auto a : w->allFunctions()) {
6719 if (_obs.contains(*a))
6720 continue;
6721 bool show(true);
6722 for (auto c : a->clients()) {
6723 show = false;
6724 if (c->InheritsFrom("RooProduct")) {
6725 show = true;
6726 break;
6727 }
6728 }
6729 if (show)
6730 out.emplace_back(std::make_shared<xRooNode>(*a, *this));
6731 }
6732 }
6733
6734 /*
6735 // if parent is a sumpdf or addpdf then include the coefs
6736 // if func appears multiple times then coefs must be combined into a RooAddition temporary
6737 if (fParent) {
6738 RooArgList coefs;
6739 if(auto p = fParent->get<RooRealSumPdf>();p) {
6740 int i=0;
6741 for(auto& o : p->funcList()) {
6742 if (o == get()) {
6743 coefs.add( *p->coefList().at(i) );
6744 }
6745 i++;
6746 }
6747 } else if(auto p = fParent->get<RooAddPdf>(); p) {
6748 int i=0;
6749 for(auto& o : p->pdfList()) {
6750 if (o == get()) {
6751 coefs.add( *p->coefList().at(i) );
6752 }
6753 i++;
6754 }
6755 }
6756 if (!coefs.empty()) {
6757 if (coefs.size() == 1) {
6758 if (strcmp(coefs.at(0)->GetName(),"1")) { // don't add the "1"
6759 out.emplace_back(std::make_shared<Node2>(".coef", *coefs.at(0), *this));
6760 }
6761 } else {
6762 out.emplace_back(std::make_shared<Node2>(".coefs",
6763 std::make_shared<RooAddition>(".coefs", "Coefficients of",
6764 coefs), *this));
6765 }
6766 }
6767 }
6768 */
6769 return out;
6770}
6771
6773{
6774 xRooNode out(".variations", nullptr, *this);
6775
6776 // if (auto p = get<RooSimultaneous>(); p) {
6777 // for (auto &c : p->indexCat()) {
6778 // auto pp = p->getPdf(c.first.c_str());
6779 // if (!pp)
6780 // continue;
6781 // out.emplace_back(
6782 // std::make_shared<xRooNode>(TString::Format("%s=%s", p->indexCat().GetName(), c.first.c_str()), *pp,
6783 // *this));
6784 // }
6785 // } else
6786 if (auto p2 = get<PiecewiseInterpolation>(); p2) {
6787#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
6788 out.emplace_back(std::make_shared<xRooNode>("nominal", p2->_nominal.arg(), *this));
6789#else
6790 out.emplace_back(std::make_shared<xRooNode>("nominal", *(p2->nominalHist()), *this));
6791#endif
6792 for (size_t i = 0; i < p2->paramList().size(); i++) {
6793 // TODO: should we only return one if we find they are symmetrized?
6794 out.emplace_back(std::make_shared<xRooNode>(TString::Format("%s=1", p2->paramList().at(i)->GetName()),
6795 *p2->highList().at(i), *this));
6796 out.emplace_back(std::make_shared<xRooNode>(TString::Format("%s=-1", p2->paramList().at(i)->GetName()),
6797 *p2->lowList().at(i), *this));
6798 }
6799 } else if (auto p3 = get<RooStats::HistFactory::FlexibleInterpVar>(); p3) {
6800#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
6801 out.emplace_back(std::make_shared<xRooNode>("nominal", RooFit::RooConst(p3->_nominal), *this));
6802 for (size_t i = 0; i < p3->_paramList.size(); i++) {
6803 out.emplace_back(std::make_shared<xRooNode>(TString::Format("%s=1", p3->_paramList.at(i)->GetName()),
6804 RooFit::RooConst(p3->_high.at(i)), *this));
6805 out.emplace_back(std::make_shared<xRooNode>(TString::Format("%s=-1", p3->_paramList.at(i)->GetName()),
6806 RooFit::RooConst(p3->_low.at(i)), *this));
6807 }
6808#else
6809 out.emplace_back(std::make_shared<xRooNode>("nominal", RooFit::RooConst(p3->nominal()), *this));
6810 for (size_t i = 0; i < p3->variables().size(); i++) {
6811 out.emplace_back(std::make_shared<xRooNode>(TString::Format("%s=1", p3->variables().at(i)->GetName()),
6812 RooFit::RooConst(p3->high().at(i)), *this));
6813 out.emplace_back(std::make_shared<xRooNode>(TString::Format("%s=-1", p3->variables().at(i)->GetName()),
6814 RooFit::RooConst(p3->low().at(i)), *this));
6815 }
6816#endif
6817
6818 } else if (auto p4 = get<ParamHistFunc>(); p4) {
6819 // I *think* I put this here so that can browse into a ParamHistFunc
6820 // int i = 0;
6821 // for (auto par : p4->_paramSet) {
6822 // TString _name = par->GetName();
6823 // // if(auto _v = dynamic_cast<RooRealVar*>(p->_dataSet.get(i)->first()); _v) {
6824 // // _name = TString::Format("%s=%g",_v->GetName(),_v->getVal());
6825 // // }
6826 // // out.emplace_back(std::make_shared<xRooNode>(_name,*par,*this)); -- -removed cos now have bin()
6827 // method i++;
6828 // }
6829 }
6830 return out;
6831}
6832
6834{
6835 RooArgList out;
6836 out.setName(GetName());
6837 for (auto &k : *this) {
6838 if (auto o = k->get<RooAbsArg>(); o)
6839 out.add(*o);
6840 }
6841 return out;
6842}
6843
6845{
6846 xRooNode out(".datasets()", nullptr, *this);
6847 // removed the browse operation since no longer showing '.datasets()' in browser
6848 // and otherwise this means dataset reduction operation will be called every time we 'browse()' the datasets node
6849 // out.fBrowseOperation = [](xRooNode *f) { return f->fParent->datasets(); };
6850
6851 if (auto _ws = get<RooWorkspace>(); _ws) {
6852 for (auto &d : _ws->allData()) {
6853 out.emplace_back(std::make_shared<xRooNode>(*d, *this));
6854 out.back()->fFolder = "!datasets";
6855 }
6856 } else if (get<RooAbsPdf>() ||
6857 (!get() && fParent &&
6858 fParent->get<RooAbsPdf>())) { // second condition handles 'bins' nodes of pdf, which have null ptr
6859 // only add datasets that have observables that cover all our observables
6860 auto oo = obs(); // must keep alive in case is owning the globs
6861 RooArgSet _obs(*oo.get<RooArgList>());
6862 //_obs.add(coords(true).argList(), true); // include coord observables too, and current xaxis if there's one -
6863 // added in loop below
6864
6865 TString cut;
6866 RooArgSet cutobs;
6867 for (auto _c : coords()) { // coords() moves vars to their respective coordinates too
6868 if (auto _cat = _c->get<RooAbsCategoryLValue>(); _cat) {
6869 if (cut != "")
6870 cut += " && ";
6871 cut += TString::Format("%s==%d", _cat->GetName(), _cat->getCurrentIndex());
6872 _obs.add(*_cat,
6873 true); // note: if we ever changed coords to return clones, would need to keep coords alive
6874 cutobs.add(*_cat);
6875 } else if (auto _rv = _c->get<RooAbsRealLValue>(); _rv) {
6876 // todo: check coordRange is a single range rather than multirange
6877 if (cut != "")
6878 cut += " && ";
6879 cut += TString::Format("%s>=%f&&%s<%f", _rv->GetName(), _rv->getMin(_rv->getStringAttribute("coordRange")),
6880 _rv->GetName(), _rv->getMax(_rv->getStringAttribute("coordRange")));
6881 _obs.add(*_rv,
6882 true); // note: if we ever changed coords to return clones, would need to keep coords alive
6883 cutobs.add(*_rv);
6884 } else {
6885 throw std::runtime_error("datasets(): Unsupported coordinate type");
6886 }
6887 }
6888 if (auto s = get<RooSimultaneous>()) {
6889 // check if we have a pdf for every category ... if not then add to cut
6890 bool hasMissing = false;
6891 TString extraCut = "";
6892 for (auto cat : s->indexCat()) {
6893 if (!s->getPdf(cat.first.c_str())) {
6894 hasMissing = true;
6895 } else {
6896 if (extraCut != "")
6897 extraCut += " || ";
6898 extraCut += TString::Format("%s==%d", s->indexCat().GetName(), cat.second);
6899 }
6900 }
6901 if (hasMissing) {
6902 if (cut != "")
6903 cut += " && ";
6904 cut += "(" + extraCut + ")";
6905 cutobs.add(s->indexCat());
6906 }
6907 }
6908
6909 if (auto ax = GetXaxis(); ax && dynamic_cast<RooAbsArg *>(ax->GetParent())->getAttribute("obs")) {
6910 auto a = dynamic_cast<RooAbsArg *>(ax->GetParent());
6911 _obs.add(*a, true);
6912 }
6913 xRooNode _datasets; // will be any child datasets, along with datasets of the workspace
6914 for (auto &child : *this) {
6915 if (child->get<RooAbsData>())
6916 _datasets.push_back(child);
6917 }
6918 if (auto __ws = ws(); __ws) {
6919 xRooNode _wsNode(*__ws, *this);
6920 for (auto &d : _wsNode.datasets()) {
6921 _datasets.push_back(d);
6922 }
6923 }
6924
6925 for (auto &d : _datasets) {
6926 if (std::unique_ptr<RooAbsCollection>(d->obs().argList().selectCommon(_obs))->size() == _obs.size()) {
6927 // all obs present .. include
6928
6929 if (cut != "") {
6930 RooFormulaVar cutFormula("cut1", cut, cutobs); // doing this to avoid complaints about unused vars
6931 // TODO: Could consider using a 'filter' node (see filter() method) applied to the dataset instead
6932 // of creating and using a reduced dataset here
6933 out.emplace_back(std::make_shared<xRooNode>(
6934 std::shared_ptr<RooAbsData>(d->get<RooAbsData>()->reduce(
6935 *std::unique_ptr<RooAbsCollection>(d->robs().get<RooArgList>()->selectCommon(_obs)), cutFormula)),
6936 *this));
6937 // put a subset of the globs in the returned dataset too
6938 out.back()->get<RooAbsData>()->setGlobalObservables(*std::unique_ptr<RooAbsCollection>(
6939 d->globs().get<RooArgList>()->selectCommon(*globs().get<RooArgList>())));
6940 if (d->get()->TestBit(1 << 20))
6941 out.back()->get()->SetBit(1 << 20);
6942 // need to attach the original dataset so that things like SetBinContent can interact with it
6943 out.back()->fBrowsables.emplace_back(std::make_shared<xRooNode>(".sourceds", d->fComp, *this));
6944 } else {
6945 out.emplace_back(std::make_shared<xRooNode>(d->fComp, *this));
6946 }
6947 }
6948 }
6949 /*else if(auto p = get<RooFitResult>(); p) {
6950 // look for datasets in workspace that match the fit result name after hashing
6951 for(auto& _d : xRooNode(*_ws,*this).datasets()) {
6952 auto _hash = RooAbsTree::nameToHash(_d->get()->GetName());
6953 if (TString::Format("%d;%d",_hash.first,_hash.second) == p->GetTitle()) {
6954 out.emplace_back(std::make_shared<xRooNode>(_d->fComp, *this));
6955 }
6956 }
6957 }*/
6958 } else if (auto mc = get<RooStats::ModelConfig>()) {
6959 return xRooNode(*mc->GetPdf(), fParent).datasets();
6960 }
6961
6962 return out;
6963}
6964
6966{
6967 xRooNode out(".parents", nullptr, *this);
6968 if (auto a = get<RooAbsArg>()) {
6969 for (auto c : a->clients()) {
6970 out.push_back(std::make_shared<xRooNode>(*c, *this));
6971 }
6972 }
6973 return out;
6974}
6975
6977{
6978 if (auto w = get<RooWorkspace>()) {
6979 xRooNode out(".args", w->components(), *this);
6980 out.browse(); // populate
6981 return out;
6982 } else if (auto a = get<RooAbsArg>()) {
6983 xRooNode out(".args", std::make_shared<RooArgList>(), *this);
6984 out.get<RooArgList>()->setName((GetPath() + ".args").c_str());
6985 a->treeNodeServerList(out.get<RooArgList>());
6986 out.browse(); // populate
6987 return out;
6988 }
6989 return nullptr;
6990}
6991
6992std::shared_ptr<xRooNode> xRooNode::getBrowsable(const char *name) const
6993{
6994 for (auto b : fBrowsables) {
6995 if (b && strcmp(b->GetName(), name) == 0)
6996 return b;
6997 }
6998 return nullptr;
6999}
7000
7001TGraph *xRooNode::BuildGraph(RooAbsLValue *v, bool includeZeros, TVirtualPad *fromPad) const
7002{
7003
7004 if (auto fr = get<RooFitResult>(); fr) {
7005 return nullptr;
7006 }
7007
7008 if (auto theData = get<RooDataSet>(); theData) {
7009
7010 TH1 *theHist = nullptr;
7011
7012 if (fromPad) {
7013 // find first histogram in pad
7014 for (auto o : *fromPad->GetListOfPrimitives()) {
7015 theHist = dynamic_cast<TH1 *>(o);
7016 if (theHist) {
7017 theHist = static_cast<TH1 *>(theHist->Clone());
7018 theHist->Reset();
7019 break;
7020 } // clone because theHist gets deleted below
7021 }
7022 }
7023
7024 if (!theHist) {
7025 auto _parentPdf = parentPdf();
7026 if (!_parentPdf) {
7027 // can still build graph if v is an obs ... will use v binning
7028 auto vo = dynamic_cast<TObject *>(v);
7029 if (v && obs().find(vo->GetName())) {
7030 if (auto cat = dynamic_cast<RooAbsCategoryLValue *>(v)) {
7031 theHist = new TH1D(
7032 TString::Format("%s_%s", GetName(), vo->GetName()),
7033 TString::Format("my temp hist;%s", strlen(vo->GetTitle()) ? vo->GetTitle() : vo->GetName()),
7034 cat->numTypes(), 0, cat->numTypes());
7035 int i = 1;
7036 std::map<int, std::string> cats; // fill into a map to preserve index ordering
7037 for (auto &c : *cat) {
7038 cats[c.second] = c.first;
7039 }
7040 for (auto &[_, label] : cats) {
7041 theHist->GetXaxis()->SetBinLabel(i++, label.c_str());
7042 }
7043 } else {
7044 auto _binning = v->getBinningPtr(nullptr);
7045 if (_binning->isUniform()) {
7046 theHist = new TH1D(
7047 TString::Format("%s_%s", GetName(), vo->GetName()),
7048 TString::Format("my temp hist;%s", strlen(vo->GetTitle()) ? vo->GetTitle() : vo->GetName()),
7049 v->numBins(), _binning->lowBound(), _binning->highBound());
7050 } else {
7051 theHist = new TH1D(
7052 TString::Format("%s_%s", GetName(), vo->GetName()),
7053 TString::Format("my temp hist;%s", strlen(vo->GetTitle()) ? vo->GetTitle() : vo->GetName()),
7054 v->numBins(), _binning->array());
7055 }
7056 }
7057 } else {
7058 throw std::runtime_error("Cannot draw dataset without parent PDF");
7059 }
7060 } else {
7061 theHist = _parentPdf->BuildHistogram(v, true);
7062 }
7063 }
7064 if (!theHist)
7065 return nullptr;
7066 // this hist will get filled with w*x to track weighted x position per bin
7067 TH1 *xPos = static_cast<TH1 *>(theHist->Clone("xPos"));
7068 xPos->Reset();
7069 TH1 *xPos2 = static_cast<TH1 *>(theHist->Clone("xPos2"));
7070 xPos2->Reset();
7071 auto nHist = std::unique_ptr<TH1>(static_cast<TH1 *>(theHist->Clone("nEntries")));
7072 nHist->Reset();
7073
7074 auto dataGraph = new TGraphAsymmErrors;
7075 dataGraph->SetEditable(false);
7076 dataGraph->SetName(GetName());
7077 dataGraph->SetTitle(strlen(theData->GetTitle()) ? theData->GetTitle() : theData->GetName());
7078 // next line triggers creation of the histogram inside the graph, in root 6.22 that isn't protected from being
7079 // added to gDirectory
7080 dataGraph->SetTitle(TString::Format("%s;%s;Events", dataGraph->GetTitle(), theHist->GetXaxis()->GetTitle()));
7081 *static_cast<TAttMarker *>(dataGraph) = *static_cast<TAttMarker *>(theHist);
7082 *static_cast<TAttLine *>(dataGraph) = *static_cast<TAttLine *>(theHist);
7083
7084 // default style based on if generated or not
7085
7086 if (auto w = theData->weightVar(); w && w->getStringAttribute("fitResult")) {
7087 // is generated
7088 dataGraph->SetLineColor(kGreen + 2);
7089 if (w->getAttribute("expected")) {
7090 // is asimov
7091 dataGraph->SetLineColor(kBlue);
7092 }
7093 } else {
7094 dataGraph->SetLineColor(kBlack);
7095 }
7096 dataGraph->SetMarkerStyle(20);
7097 dataGraph->SetMarkerColor(dataGraph->GetLineColor());
7098 dataGraph->SetMarkerSize(gStyle->GetMarkerSize());
7099
7100 auto _obs = obs();
7101
7102 // auto x = theData->get()->find((v) ? dynamic_cast<TObject*>(v)->GetName() : theHist->GetXaxis()->GetName());
7103 // const RooAbsReal* xvar = (x) ? dynamic_cast<RooAbsReal*>(x) : nullptr;
7104 // const RooAbsCategory* xcat = (x && !xvar) ? dynamic_cast<RooAbsCategory*>(x) : nullptr;
7105 auto x = _obs.find((v) ? dynamic_cast<TObject *>(v)->GetName()
7106 : (theHist->GetXaxis()->IsAlphanumeric() ? theHist->GetXaxis()->GetTimeFormatOnly()
7107 : theHist->GetXaxis()->GetName()));
7108 if (x && x->get<RooAbsArg>()->getAttribute("global")) {
7109 // is global observable ...
7110 dataGraph->SetPoint(0, x->get<RooAbsReal>()->getVal(), 1e-15);
7111 dataGraph->SetTitle(TString::Format("%s = %f", dataGraph->GetTitle(), dataGraph->GetPointX(0)));
7112 delete xPos;
7113 delete xPos2;
7114 delete theHist;
7115 return dataGraph;
7116 }
7117
7118 const RooAbsReal *xvar = (x) ? x->get<RooAbsReal>() : nullptr;
7119 const RooAbsCategory *xcat = (x && !xvar) ? x->get<RooAbsCategory>() : nullptr;
7120
7121 auto _coords = coords();
7122
7123 TString pName((fromPad) ? fromPad->GetName() : "");
7124 auto _pos = pName.Index('=');
7125
7126 int nevent = theData->numEntries();
7127 for (int i = 0; i < nevent; i++) {
7128 theData->get(i);
7129 bool _skip = false;
7130 for (auto _c : _coords) {
7131 if (auto cat = _c->get<RooAbsCategoryLValue>(); cat) {
7132 if (cat->getIndex() != theData->get()->getCatIndex(cat->GetName())) {
7133 _skip = true;
7134 break;
7135 }
7136 } else if (auto rv = _c->get<RooAbsRealLValue>(); rv) {
7137 // must be in range
7138 if (!rv->inRange(theData->get()->getRealValue(rv->GetName()), rv->getStringAttribute("coordRange"))) {
7139 _skip = true;
7140 break;
7141 }
7142 }
7143 }
7144 if (_pos != -1) {
7145 if (auto cat = dynamic_cast<RooAbsCategory *>(theData->get()->find(TString(pName(0, _pos))));
7146 cat && cat->getLabel() != pName(_pos + 1, pName.Length())) {
7147 _skip = true;
7148 }
7149 }
7150 if (_skip)
7151 continue;
7152
7153 if (xvar) {
7154 xPos->Fill(xvar->getVal(), xvar->getVal() * theData->weight());
7155 xPos2->Fill(xvar->getVal(), pow(xvar->getVal(), 2) * theData->weight());
7156 }
7157
7158 if (xcat) {
7159 theHist->Fill(xcat->getLabel(), theData->weight());
7160 nHist->Fill(xcat->getLabel(), 1);
7161 } else {
7162 theHist->Fill((x) ? xvar->getVal() : 0.5, theData->weight());
7163 nHist->Fill((x) ? xvar->getVal() : 0.5, 1);
7164 }
7165 }
7166
7167 xPos->Divide(theHist);
7168 xPos2->Divide(theHist);
7169
7170 // update the x positions to the means for each bin and use poisson asymmetric errors for data ..
7171 for (int i = 0; i < theHist->GetNbinsX(); i++) {
7172 if (includeZeros || nHist->GetBinContent(i + 1)) {
7173 double val = theHist->GetBinContent(i + 1);
7174
7175 dataGraph->SetPoint(dataGraph->GetN(),
7176 (xvar && val) ? xPos->GetBinContent(i + 1) : theHist->GetBinCenter(i + 1), val);
7177
7178 // x-error will be the (weighted) standard deviation of the x values ...
7179 double xErr = xPos2->GetBinContent(i + 1) - pow(xPos->GetBinContent(i + 1), 2);
7180 xErr = (xErr <= 0) ? 0. : sqrt(xErr); // protects against floating point rounding effects
7181
7182 if (xErr || val) {
7183 dataGraph->SetPointError(dataGraph->GetN() - 1, xErr, xErr,
7184 val - 0.5 * TMath::ChisquareQuantile(TMath::Prob(1, 1) / 2., 2. * (val)),
7185 0.5 * TMath::ChisquareQuantile(1. - TMath::Prob(1, 1) / 2., 2. * (val + 1)) -
7186 val);
7187 }
7188 }
7189 }
7190
7191 // transfer limits from theHist to dataGraph hist
7192 dataGraph->GetHistogram()->GetXaxis()->SetLimits(theHist->GetXaxis()->GetXmin(), theHist->GetXaxis()->GetXmax());
7193 // and bin labels, if any
7194 if (xcat) {
7195 dataGraph->GetHistogram()->GetXaxis()->Set(theHist->GetNbinsX(), 0, theHist->GetNbinsX());
7196 for (int i = 1; i <= theHist->GetNbinsX(); i++)
7197 dataGraph->GetHistogram()->GetXaxis()->SetBinLabel(i, theHist->GetXaxis()->GetBinLabel(i));
7198 }
7199
7200 delete xPos;
7201 delete xPos2;
7202 delete theHist;
7203
7204 // std::shared_ptr<TStyle> style; // use to keep alive for access from GetStyle below, in case getObject
7205 // has decided to return the owning ptr (for some reason) std::string _title =
7206 // strlen(dataGraph->GetTitle()) ? dataGraph->GetTitle() : GetName(); if (!gROOT->GetStyle(_title.c_str()))
7207 // {
7208 // if ( (style = getObject<TStyle>(_title)) ) {
7209 // // loaded style (from workspace?) so put in list and use that
7210 // gROOT->GetListOfStyles()->Add(style.get());
7211 // } else {
7212 // // create new style - gets put in style list automatically so don't have to delete
7213 // // acquire them so saved to workspaces for auto reload ...
7214 // style = const_cast<xRooNode&>(*this).acquireNew<TStyle>(_title.c_str(),
7215 // TString::Format("Style for %s component", _title.c_str()));
7216 // (TAttLine &) (*style) = *dynamic_cast<TAttLine *>(dataGraph);
7217 // (TAttFill &) (*style) = *dynamic_cast<TAttFill *>(dataGraph);
7218 // (TAttMarker &) (*style) = *dynamic_cast<TAttMarker *>(dataGraph);
7219 // gROOT->GetListOfStyles()->Add(style.get());
7220 // }
7221 // }
7222 auto _styleNode = styles(dataGraph);
7223 if (auto _style = _styleNode.get<TStyle>()) {
7224 *dynamic_cast<TAttLine *>(dataGraph) = *_style;
7225 *dynamic_cast<TAttFill *>(dataGraph) = *_style;
7226 *dynamic_cast<TAttMarker *>(dataGraph) = *_style;
7227 }
7228 return dataGraph;
7229 }
7230
7231 throw std::runtime_error("Cannot build graph");
7232}
7233
7235{
7236 if (fr) {
7237 if (auto _w = ws(); _w) {
7238 auto res = acquire(std::shared_ptr<RooFitResult>(const_cast<RooFitResult *>(fr), [](RooFitResult *) {}));
7239 for (auto o : _w->allGenericObjects()) {
7240 if (auto _fr = dynamic_cast<RooFitResult *>(o); _fr) {
7241 _fr->ResetBit(1 << 20);
7242 }
7243 }
7244 res->SetBit(1 << 20);
7245 // assign values
7246 auto allVars = _w->allVars();
7247 allVars = fr->floatParsFinal();
7248 allVars = fr->constPars();
7249 } else {
7250 // need to add to memory as a specific name
7251 throw std::runtime_error("Not supported yet"); // complication is how to replace an existing fitResult in
7252 // .memory auto _clone = std::make_shared<RooFitResult>(*fr);
7253 //_clone->SetName("fitResult");
7254 }
7255 } else {
7256 SetFitResult(fitResult("prefit").get<RooFitResult>());
7257 }
7258}
7259
7261{
7262 if (auto _fr = fr.get<const RooFitResult>()) {
7263 SetFitResult(_fr);
7264 } else
7265 throw std::runtime_error("Not a RooFitResult");
7266}
7267
7268xRooNode xRooNode::fitResult(const char *opt) const
7269{
7270
7271 if (get<RooFitResult>())
7272 return *this;
7273 if (get<RooAbsData>()) {
7274 if (auto _fr = find(".fitResult"); _fr)
7275 return _fr;
7276#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 26, 00)
7277 // check if weightVar of RooAbsData has fitResult attribute on it, will be the generation fit result
7278 if (get<RooDataSet>() && get<RooDataSet>()->weightVar() &&
7279 get<RooDataSet>()->weightVar()->getStringAttribute("fitResult")) {
7280 return xRooNode(getObject<const RooFitResult>(get<RooDataSet>()->weightVar()->getStringAttribute("fitResult")),
7281 *this);
7282 }
7283#endif
7284 return xRooNode();
7285 }
7286
7287 TString sOpt(opt);
7288 if (sOpt == "prefit") {
7289 // build a fitResult using nominal values and infer errors from constraints
7290 // that aren't the 'main' constraints
7291 // Warning("fitResult","Building prefitResult by examining pdf. Consider setting an explicit prefitResult
7292 // (SetFitResult(fr)) where fr name is prefitResult");
7293
7294 // ensure coefs are included if there are any
7295 auto _coefs = coefs();
7296 if (_coefs.get()) {
7297 return xRooNode(RooProduct("tmp", "tmp", RooArgList(*get<RooAbsArg>(), *_coefs.get<RooAbsReal>())))
7298 .fitResult(opt);
7299 }
7300
7301 std::unique_ptr<RooArgList> _pars(dynamic_cast<RooArgList *>(pars().argList().selectByAttrib("Constant", false)));
7302 auto fr = std::make_shared<RooFitResult>("prefitResult", "Prefit");
7303 fr->setFinalParList(*_pars);
7304 for (auto &p : fr->floatParsFinal()) {
7305 auto _v = dynamic_cast<RooRealVar *>(p);
7306 if (!_v)
7307 continue;
7308 if (auto s = _v->getStringAttribute("nominal"); s)
7309 _v->setVal(TString(s).Atof());
7310 auto _constr = xRooNode(fParent->getObject<RooRealVar>(p->GetName()), *this).constraints();
7311 std::shared_ptr<xRooNode> pConstr;
7312 for (auto &c : _constr) {
7313 if (c->get<RooPoisson>() || c->get<RooGaussian>()) {
7314 // require parameter to be a direct server of the constraint pdf to count
7315 bool isServer = true;
7316 if (c->get<RooGaussian>()) {
7317 isServer = false;
7318 for (auto s : c->get<RooAbsArg>()->servers()) {
7319 if (strcmp(s->GetName(), p->GetName()) == 0) {
7320 isServer = true;
7321 break;
7322 }
7323 }
7324 }
7325 if (isServer) {
7326 pConstr = c;
7327 break;
7328 }
7329 }
7330 }
7331 if (pConstr) {
7332 // there will be 3 deps, one will be this par, the other two are the mean and error (or error^2 in case of
7333 // poisson use the one that's a ConstVar as the error to break a tie ...
7334 double prefitVal = 0;
7335 double prefitError = 0;
7336 for (auto &_d : pConstr->vars()) {
7337 if (strcmp(p->GetName(), _d->get()->GetName()) == 0)
7338 continue;
7339 if (auto _c = _d->get<RooConstVar>(); _c && _c->getVal() != 0) {
7340 if (prefitError)
7341 prefitVal = prefitError; // loading val into error already, so move it over
7342 prefitError = _c->getVal();
7343 } else if (prefitError == 0) {
7344 prefitError = _d->get<RooAbsReal>()->getVal();
7345 } else {
7346 prefitVal = _d->get<RooAbsReal>()->getVal();
7347 }
7348 }
7349
7350 if (pConstr->get<RooGaussian>() && pConstr->browse().find(".sigma")) {
7351 prefitError = pConstr->find(".sigma")->get<RooAbsReal>()->getVal();
7352 }
7353 // std::cout << p->GetName() << " extracted " << prefitVal << " " << prefitError << " from ";
7354 // pConstr->deps().Print();
7355 if (pConstr->get<RooPoisson>()) {
7356 // prefitVal will be the global observable value, need to divide that by tau
7357 prefitVal /= prefitError;
7358 // prefiterror will be tau ... need 1/sqrt(tau) for error
7359 prefitError = 1. / sqrt(prefitError);
7360 }
7361 if (!_v->getStringAttribute("nominal"))
7362 _v->setVal(prefitVal);
7363 _v->setError(prefitError);
7364 } else {
7365 // unconstrained, remove error
7366 _v->removeError();
7367 }
7368 }
7369 auto _args = consts().argList();
7370 _args.add(pp().argList());
7371 // global obs are added to constPars list too
7372 auto _globs = globs(); // keep alive as may own glob
7373 _args.add(_globs.argList());
7374 fr->setConstParList(_args);
7375 std::unique_ptr<RooArgList> _snap(dynamic_cast<RooArgList *>(_pars->snapshot()));
7376 for (auto &p : *_snap) {
7377 if (auto atr = p->getStringAttribute("initVal"); atr && dynamic_cast<RooRealVar *>(p))
7378 dynamic_cast<RooRealVar *>(p)->setVal(TString(atr).Atof());
7379 }
7380 fr->setInitParList(*_snap);
7381 return xRooNode(fr, *this);
7382 }
7383
7384 // return first checked fit result present in the workspace
7385 if (auto _w = ws(); _w) {
7386 auto checkFr = [&](TObject *o) {
7387 if (auto _fr = dynamic_cast<RooFitResult *>(o); _fr && _fr->TestBit(1 << 20)) {
7388 // check all pars match final/const values ... if mismatch need to create a new RooFitResult
7389 RooArgList oldFloats;
7390 RooArgList newFloats;
7391 RooArgList newConsts;
7392 for (auto p : pars()) {
7393 if (p->get<RooAbsArg>()->getAttribute("Constant") || p->get<RooConstVar>()) {
7394 // par must not be in the float list or have different value to what is in constPars (if it is there)
7395 if (_fr->floatParsFinal().find(p->GetName()) ||
7396 (p->get<RooAbsReal>() &&
7397 std::abs(_fr->constPars().getRealValue(p->GetName(), std::numeric_limits<double>::quiet_NaN()) -
7398 p->get<RooAbsReal>()->getVal()) > 1e-15) ||
7399 (p->get<RooAbsCategory>() &&
7400 p->get<RooAbsCategory>()->getCurrentIndex() !=
7401 _fr->constPars().getCatIndex(p->GetName(), std::numeric_limits<int>().max()))) {
7402 newConsts.add(*p->get<RooAbsArg>());
7403 }
7404 } else {
7405 // floating par must be present in the floatPars list with the same value
7406 if (!_fr->floatParsFinal().find(p->GetName())) {
7407 newFloats.add(*p->get<RooAbsArg>());
7408 } else if ((p->get<RooAbsReal>() &&
7409 std::abs(_fr->floatParsFinal().getRealValue(p->GetName(),
7410 std::numeric_limits<double>::quiet_NaN()) -
7411 p->get<RooAbsReal>()->getVal()) > 1e-15) ||
7412 (p->get<RooAbsCategory>() &&
7413 p->get<RooAbsCategory>()->getCurrentIndex() !=
7414 _fr->floatParsFinal().getCatIndex(p->GetName(), std::numeric_limits<int>().max()))) {
7415 // value of existing float changed
7416 oldFloats.add(*p->get<RooAbsArg>());
7417 }
7418 }
7419 }
7420 if (!oldFloats.empty() || !newFloats.empty() || !newConsts.empty()) {
7421 // create new fit result using covariance from the fit result
7422 // remove any new consts from the list before extracting covariance matrix
7423 RooArgList existingFloats(_fr->floatParsFinal());
7424 existingFloats.remove(newConsts, true, true /* match name*/);
7425 auto cov = _fr->reducedCovarianceMatrix(existingFloats);
7426 if (!newFloats.empty()) {
7427 // extend the covariance matrix and add variances using current parameter errors
7428 size_t oldSize = existingFloats.size();
7429 cov.ResizeTo(oldSize + newFloats.size(), oldSize + newFloats.size());
7430 for (size_t i = 0; i < newFloats.size(); i++) {
7431 existingFloats.add(*newFloats.at(i));
7432 auto v = dynamic_cast<RooRealVar *>(newFloats.at(i));
7433 if (v)
7434 cov(oldSize + i, oldSize + i) = std::pow(v->getError(), 2);
7435 }
7436 }
7437 RooArgList existingConsts(_fr->constPars());
7438 existingConsts.remove(newFloats, true, true);
7439 existingConsts.add(newConsts);
7440
7441 // do we need to add our remaining const pars to the const par list? or the globs?
7442 // for speed we wont bother
7443 // note that generating datasets needs the globs in the const pars list so the robs can be determined
7444 // at the moment this check is done in the generate() method (along with check for missing pars)
7445
7446 auto fr = std::make_shared<RooFitResult>(TString::Format("%s-dirty", _fr->GetName()));
7447 fr->setFinalParList(existingFloats);
7448 fr->setConstParList(existingConsts);
7449 fr->setCovarianceMatrix(cov);
7450 fr->setInitParList(_fr->floatParsInit()); // will only be the pars that were actually float for the fit
7451
7452 return xRooNode(fr, *this);
7453 } else {
7454 // all matching, can return the fit result as-is
7455 return xRooNode(*_fr, std::make_shared<xRooNode>(*_w, std::make_shared<xRooNode>()));
7456 }
7457 }
7458 return xRooNode();
7459 };
7460 for (auto o : _w->allGenericObjects()) {
7461 auto out = checkFr(o);
7462 if (out)
7463 return out;
7464 }
7465 for (auto o : GETWSSNAPSHOTS(_w)) {
7466 auto out = checkFr(o);
7467 if (out)
7468 return out;
7469 }
7470 } else {
7471 // objects not in workspaces are allowed to have a fitResult set in their memory
7472 // use getObject to get it
7473 if (auto fr = getObject<RooFitResult>(".fitResult"); fr) {
7474 return xRooNode(fr, *this);
7475 }
7476 }
7477
7478 // ensure coefs are included if there are any
7479 auto _coefs = coefs();
7480 if (_coefs.get()) {
7481 return xRooNode(RooProduct("tmp", "tmp", RooArgList(*get<RooAbsArg>(), *_coefs.get<RooAbsReal>())))
7482 .fitResult(opt);
7483 }
7484
7485 std::unique_ptr<RooArgList> _pars(dynamic_cast<RooArgList *>(pars().argList().selectByAttrib("Constant", false)));
7486 auto fr = std::make_shared<RooFitResult>(TUUID().AsString());
7487 fr->SetTitle(TString::Format("%s uncorrelated parameter snapshot", GetName()));
7488 fr->setFinalParList(*_pars);
7489 fr->setStatus(-1);
7490
7491 TMatrixDSym cov(fr->floatParsFinal().size());
7492 TMatrixTSym<double> *prevCov = static_cast<TMatrixTSym<double> *>(GETDMP(fr.get(), _VM));
7493 if (prevCov) {
7494 for (int i = 0; i < prevCov->GetNcols(); i++) {
7495 for (int j = 0; j < prevCov->GetNrows(); j++) {
7496 cov(i, j) = (*prevCov)(i, j);
7497 }
7498 }
7499 }
7500 int i = 0;
7501 for (auto &p : fr->floatParsFinal()) {
7502 if (!prevCov || i >= prevCov->GetNcols()) {
7503 if (auto v = dynamic_cast<RooRealVar *>(p)) {
7504 cov(i, i) = pow(v->getError(), 2);
7505 } else {
7506 cov(i, i) = 0;
7507 }
7508 }
7509 i++;
7510 }
7511 int covQualBackup = fr->covQual();
7512 fr->setCovarianceMatrix(cov);
7513 fr->setCovQual(covQualBackup);
7514
7515 auto _args = consts().argList();
7516 _args.add(pp().argList());
7517 // global obs are added to constPars list too
7518 auto _globs = globs(); // keep alive as may own glob
7519 _args.add(_globs.argList());
7520 fr->setConstParList(_args);
7521 std::unique_ptr<RooArgList> _snap(dynamic_cast<RooArgList *>(_pars->snapshot()));
7522 for (auto &p : *_snap) {
7523 if (auto atr = p->getStringAttribute("initVal"); atr && dynamic_cast<RooRealVar *>(p))
7524 dynamic_cast<RooRealVar *>(p)->setVal(TString(atr).Atof());
7525 }
7526 fr->setInitParList(*_snap);
7527
7528 // return *const_cast<Node2*>(this)->emplace_back(std::make_shared<Node2>(".fitResult",fr,*this));
7529 return xRooNode(fr, *this);
7530}
7531
7532// xRooNode xRooNode::fitTo_(const char* datasetName) const {
7533// try {
7534// return fitTo(datasetName);
7535// } catch(const std::exception& e) {
7536// new TGMsgBox(gClient->GetRoot(), gClient->GetRoot(), "Exception", e.what(),kMBIconExclamation); // deletes
7537// self on dismiss? return xRooNode();
7538// }
7539// }
7540//
7541// xRooNode xRooNode::fitTo(const char* datasetName) const {
7542// return fitTo(*datasets().at(datasetName));
7543// }
7544
7545void xRooNode::SetRange(const char *range, double low, double high)
7546{
7547 if (!std::isnan(low) && !std::isnan(high) && get<RooRealVar>()) {
7548 if (range && strlen(range)) {
7549 get<RooRealVar>()->setRange(range, low, high);
7550 } else {
7551 get<RooRealVar>()->setRange(low, high);
7552 }
7553 return;
7554 }
7555 if (auto o = get<RooAbsArg>(); o)
7556 o->setStringAttribute("range", range);
7557 // todo: clear the range attribute on all servers
7558 // could make this controlled by a flag but probably easiest to enforce so you must set range
7559 // in children after if you wanted to override
7560}
7561const char *xRooNode::GetRange() const
7562{
7563 std::string &out = fRange;
7564 if (auto o = get<RooAbsArg>(); o && o->getStringAttribute("range"))
7565 out = o->getStringAttribute("range");
7566 auto _parent = fParent;
7567 while (out.empty() && _parent) {
7568 if (auto o = _parent->get<RooAbsArg>(); o && o->getStringAttribute("range"))
7569 out = o->getStringAttribute("range");
7570 _parent = _parent->fParent;
7571 }
7572 return out.c_str();
7573}
7574
7575xRooNLLVar xRooNode::nll(const char *_data, std::initializer_list<RooCmdArg> nllOpts) const
7576{
7577 return nll(xRooNode(_data), nllOpts);
7578}
7579
7581{
7582 return nll(_data, *xRooFit::createNLLOptions());
7583}
7584
7585xRooNLLVar xRooNode::nll(const xRooNode &_data, std::initializer_list<RooCmdArg> nllOpts) const
7586{
7587 auto defaultOpts = xRooFit::createNLLOptions(); // smart pointer will cleanup the list
7588 // add user-specified options to list ... if already existing in default list, override and warn
7590 for (auto opt : *defaultOpts) {
7591 l.Add(opt);
7592 }
7593 for (auto &i : nllOpts) {
7594 if (auto o = l.FindObject(i.GetName())) {
7595 Info("nll", "Overriding NLL Option: %s", o->GetName());
7596 l.Remove(o);
7597 }
7598 l.Add(const_cast<RooCmdArg *>(&i));
7599 }
7600
7601 return nll(_data, l);
7602}
7603
7604xRooNode xRooNode::generate(const xRooNode &fr, bool expected, int seed)
7605{
7606 if (auto mc = get<RooStats::ModelConfig>()) {
7607 return xRooNode(*mc->GetPdf(), fParent).generate(fr, expected, seed);
7608 }
7609
7610 if (!get<RooAbsPdf>()) {
7611 // before giving up, if this is a workspace we can proceed if we only have one model
7612 if (get<RooWorkspace>()) {
7613 std::shared_ptr<xRooNode> mainModel;
7614 for (auto &c : const_cast<xRooNode *>(this)->browse()) {
7615 if (c->get<RooAbsPdf>()) {
7616 if (!mainModel) {
7617 mainModel = c;
7618 } else {
7619 throw std::runtime_error(TString::Format("Workspace has multiple models, you must specify which to "
7620 "generate with (found at least %s and %s)",
7621 mainModel->GetName(), c->GetName()));
7622 }
7623 }
7624 }
7625 if (mainModel)
7626 return mainModel->generate(fr, expected, seed);
7627 }
7628 throw std::runtime_error(TString::Format("%s is not a pdf", GetName()));
7629 }
7630
7631 // when generating, will only include channels that are selected
7632 // any unselected but not hidden channel will have data from the only selected dataset added to it
7633 if (get<RooSimultaneous>()) {
7634 std::string selected;
7635 std::string fromds; // list of channels to take from selected ds
7636 bool hasDeselected = false;
7637 for (auto c : bins()) {
7638 TString cName(c->GetName());
7639 cName = cName(cName.Index('=') + 1, cName.Length());
7640 if (!c->get<RooAbsReal>()->isSelectedComp()) {
7641 hasDeselected = true;
7642 if (!c->get<RooAbsArg>()->getAttribute("hidden")) {
7643 if (!fromds.empty())
7644 fromds += ",";
7645 fromds += cName.Data();
7646 }
7647 } else {
7648 if (!selected.empty())
7649 selected += ",";
7650 selected += cName.Data();
7651 }
7652 }
7653 if (hasDeselected) {
7654 std::string dsetName = "";
7655 if (!fromds.empty()) {
7656 // use the first selected dataset as protodata
7657 auto _dsets = datasets();
7658 for (auto &d : _dsets) {
7659 if (d->get()->TestBit(1 << 20)) {
7660 dsetName = d->get()->GetName();
7661 break;
7662 }
7663 }
7664 if (dsetName.empty()) {
7665 throw std::runtime_error(
7666 "Need at least one dataset selected (SetChecked) to use for deselected regions");
7667 }
7668 }
7669 auto result = reduced(selected).generate(fr, expected, seed);
7670 if (!fromds.empty()) {
7671 auto ds = reduced(fromds).datasets()[dsetName];
7672 result.Add(*ds);
7673 result.SetName(TString(result.GetName()) + "_and_" + dsetName.c_str());
7674 }
7675 return result;
7676 }
7677 }
7678
7679 auto _fr = fr.get<RooFitResult>();
7680 xRooNode fr2;
7681 if (!_fr) {
7682 fr2 = fitResult();
7683 _fr = fr2.get<RooFitResult>();
7684 }
7685
7686 // must ensure fr has all the globs in its constPars list ... any missing must be added
7687 // otherwise generateFrom method wont determine globs properly
7688 // same for any missing pars
7689 auto _globs = globs();
7690 bool missingGlobs(false);
7691 for (auto glob : _globs) {
7692 if (!_fr->constPars().find(*glob->get<RooAbsArg>())) {
7693 missingGlobs = true;
7694 break;
7695 }
7696 }
7697
7698 std::unique_ptr<RooFitResult> newFr;
7699 if (missingGlobs) {
7700 newFr = std::make_unique<RooFitResult>(*_fr);
7701 for (auto glob : _globs) {
7702 if (!newFr->constPars().find(*glob->get<RooAbsArg>())) {
7703 const_cast<RooArgList &>(newFr->constPars()).addClone(*glob->get<RooAbsArg>());
7704 }
7705 }
7706 _fr = newFr.get();
7707 }
7708
7709 // check for missing fundamental pars (consts are not fundamentals)
7710 auto _pars = pars();
7711 bool missingPars(false);
7712 for (auto par : _pars) {
7713 if (!par->get<RooAbsArg>()->isFundamental())
7714 continue;
7715 if (!_fr->constPars().find(*par->get<RooAbsArg>()) && !_fr->floatParsFinal().find(*par->get<RooAbsArg>())) {
7716 missingPars = true;
7717 break;
7718 }
7719 }
7720
7721 if (missingPars) {
7722 newFr = std::make_unique<RooFitResult>(*_fr);
7723 for (auto par : _pars) {
7724 if (!par->get<RooAbsArg>()->isFundamental())
7725 continue;
7726 if (!newFr->constPars().find(*par->get<RooAbsArg>()) &&
7727 !newFr->floatParsFinal().find(*par->get<RooAbsArg>())) {
7728 const_cast<RooArgList &>(newFr->constPars()).addClone(*par->get<RooAbsArg>());
7729 }
7730 }
7731 _fr = newFr.get();
7732 }
7733
7734 return xRooNode(xRooFit::generateFrom(*get<RooAbsPdf>(), *_fr, expected, seed).first, *this);
7735
7736 // should add coords to the dataset too?
7737 // e.g. in the case of generating a dataset for a single channel, include the channelCat
7738 // this will allow datasets to then be combined.
7739 // could just say users must use 'reduced' on the simPdf, even if reducing to a single channel
7740}
7741
7742xRooNLLVar xRooNode::nll(const xRooNode &_data, const RooLinkedList &opts) const
7743{
7744 if (auto mc = get<RooStats::ModelConfig>()) {
7745 if (mc->GetExternalConstraints()) {
7746 RooLinkedList optsWithConstraints;
7747 for (auto o : opts) {
7748 optsWithConstraints.Add(o->Clone(nullptr));
7749 }
7750 optsWithConstraints.Add(RooFit::ExternalConstraints(*mc->GetExternalConstraints()).Clone(nullptr));
7751 return xRooNode(*mc->GetPdf(), fParent).nll(_data, optsWithConstraints);
7752 } else {
7753 return xRooNode(*mc->GetPdf(), fParent).nll(_data, opts);
7754 }
7755 }
7756
7757 if (!get<RooAbsPdf>()) {
7758 // before giving up, if this is a workspace we can proceed if we only have one model or pdf
7759 if (get<RooWorkspace>()) {
7760 std::shared_ptr<xRooNode> mainPdf, mainModel, otherPdf;
7761 for (auto &c : const_cast<xRooNode *>(this)->browse()) {
7762 if (c->get<RooAbsPdf>()) {
7763 if (!mainPdf) {
7764 mainPdf = c;
7765 } else {
7766 otherPdf = c;
7767 }
7768 } else if (c->get<RooStats::ModelConfig>()) {
7769 if (!mainModel) {
7770 mainModel = c;
7771 } else {
7772 throw std::runtime_error(TString::Format("Workspace has multiple models, you must specify which to "
7773 "build nll with (found at least %s and %s)",
7774 mainModel->GetName(), c->GetName()));
7775 }
7776 }
7777 }
7778 if (mainModel)
7779 return mainModel->nll(_data, opts);
7780 if (mainPdf) {
7781 if (otherPdf) {
7782 throw std::runtime_error(TString::Format("Workspace has multiple pdfs, you must specify which to "
7783 "build nll with (found at least %s and %s)",
7784 mainPdf->GetName(), otherPdf->GetName()));
7785 }
7786 return mainPdf->nll(_data, opts);
7787 }
7788 }
7789 throw std::runtime_error(TString::Format("%s is not a pdf", GetName()));
7790 }
7791
7792 // if simultaneous and any channels deselected then reduce and return
7793 if (get<RooSimultaneous>()) {
7794 std::string selected;
7795 bool hasDeselected = false;
7796 for (auto c : bins()) {
7797 if (!c->get<RooAbsReal>()->isSelectedComp()) {
7798 hasDeselected = true;
7799 } else {
7800 TString cName(c->GetName());
7801 cName = cName(cName.Index('=') + 1, cName.Length());
7802 if (!selected.empty())
7803 selected += ",";
7804 selected += cName.Data();
7805 }
7806 }
7807 if (hasDeselected)
7808 return reduced(selected).nll(_data, opts);
7809 }
7810
7811 if (!_data.get<RooAbsData>()) {
7812 // use node name to find dataset and recall
7813 auto _d = strlen(_data.GetName()) ? datasets().find(_data.GetName()) : nullptr;
7814 if (strlen(_data.GetName()) == 0) {
7815 // create the EXPECTED (asimov) dataset with the observables
7816 auto asi = xRooFit::generateFrom(*get<RooAbsPdf>(), *(fitResult().get<RooFitResult>()), true);
7817 _d = std::make_shared<xRooNode>(asi.first, *this);
7818 if (asi.second) {
7819 _d->emplace_back(
7820 std::make_shared<xRooNode>(".globs", std::const_pointer_cast<RooAbsCollection>(asi.second), *_d));
7821 }
7822 } else if (!_d) {
7823 throw std::runtime_error(TString::Format("Cannot find dataset %s", _data.GetName()));
7824 }
7825 return nll(*_d, opts);
7826 } else if (!_data.fParent || _data.fParent->fComp != fComp) {
7827 // dataset is not parented by this node ... meaning it may need to be reduced,
7828 // do this via the datasets() method by attaching and detaching the dataset
7829 xRooNode me(*this); // since we are in a const method, need to create a copy node.
7830 me.push_back(std::make_shared<xRooNode>(_data));
7831 return nll(*me.datasets().at(_data.GetName()), opts);
7832 }
7833
7834 auto _globs = _data.globs(); // keep alive because may own the globs
7835
7836 auto _opts = std::shared_ptr<RooLinkedList>(new RooLinkedList, [](RooLinkedList *l) {
7837 if (l)
7838 l->Delete();
7839 delete l;
7840 });
7841 RooArgSet _globsSet(_globs.argList());
7842 _opts->Add(RooFit::GlobalObservables(_globsSet).Clone());
7843 if (GetRange() && strlen(GetRange()))
7844 _opts->Add(RooFit::Range(GetRange()).Clone());
7845
7846 // copy over opts ... need to clone each so can safely delete when _opts destroyed
7847 for (int i = 0; i < opts.GetSize(); i++) {
7848 if (strlen(opts.At(i)->GetName()) == 0)
7849 continue; // skipping "none" cmds
7850 if (strcmp(opts.At(i)->GetName(), "GlobalObservables") == 0) {
7851 // maybe warn here?
7852 } else {
7853 _opts->Add(opts.At(i)->Clone(nullptr)); // nullptr needed because accessing Clone via TObject base class puts
7854 // "" instead, so doesnt copy names
7855 }
7856 }
7857
7858 // use shared_ptr method so NLLVar will take ownership of datasets etc if created above
7859 // snapshots the globs out of the nllOpts (see specific constructor of xRooNLLVar)
7860 auto out = xRooFit::createNLL(std::dynamic_pointer_cast<RooAbsPdf>(fComp),
7861 std::dynamic_pointer_cast<RooAbsData>(_data.fComp), *_opts);
7862 return out;
7863}
7864
7865// xRooNode xRooNode::fitTo(const xRooNode& _data) const {
7866//
7867//
7868// auto _pdf = get<RooAbsPdf>();
7869// if (!_pdf) throw std::runtime_error("Not a pdf");
7870//
7871// auto _globs = _data.globs(); // keep alive because may own the globs
7872// RooArgSet globsSet(_globs.argList());
7873//
7874// std::shared_ptr<RooSimultaneous> newPdf;
7875// if(auto s = get<RooSimultaneous>(); s) {
7876// auto rangeName = GetRange();
7877// if (rangeName) {
7878// // need to reduce the RooSimultaneous until fix: https://github.com/root-project/root/issues/8231
7879// std::vector<TString> chanPatterns;
7880// TStringToken pattern(rangeName, ",");
7881// while (pattern.NextToken()) {
7882// chanPatterns.emplace_back(pattern);
7883// }
7884// auto& _cat = const_cast<RooAbsCategoryLValue&>(s->indexCat());
7885// newPdf = std::make_shared<RooSimultaneous>(TString::Format("%s_reduced",GetName()),"Reduced model",_cat);
7886// for(auto& c : variations()) {
7887// TString cName(c->GetName());
7888// cName = cName(cName.Index('=')+1,cName.Length());
7889// _cat.setLabel(cName);
7890// bool matchAny=false;
7891// for(auto& p : chanPatterns) {
7892// if (cName.Contains(TRegexp(p,true))) { matchAny=true; break; }
7893// if (_cat.hasRange(p) && _cat.inRange(p)) { matchAny=true; break; }
7894// }
7895// if(matchAny) {
7896// newPdf->addPdf( *c->get<RooAbsPdf>(), cName );
7897// }
7898// }
7899// RooFitResultTree t(newPdf->GetName(),"",*newPdf);
7900// auto _fr = std::const_pointer_cast<RooFitResult>(t.fitTo(_data.get<RooAbsData>(), &globsSet));
7901// xRooNode parent(_data.GetName(),nullptr,*this);
7902// xRooNode out(_fr->GetName(),/*acquire(_fr)*/ _fr,parent);
7903// // do full propagation by 'checking' the fr ...
7904// out.Checked(&out,true);
7905// return out;
7906// }
7907// }
7908//
7909//
7910//
7911// std::string treeName = TString::Format("fits_%s",GetName()).Data();
7912//
7913// auto _frt = getObject<TTree>(treeName); // get existing frt
7914//
7915// std::shared_ptr<RooFitResultTree> t;
7916// if (_frt) {
7917// t = std::make_shared<RooFitResultTree>(_frt.get());
7918// } else {
7919// t = std::make_shared<RooFitResultTree>(treeName.c_str(),"",*_pdf);
7920// }
7921// //t->SetProgress(true);
7922// auto _fr = std::const_pointer_cast<RooFitResult>(t->fitTo(_data.get<RooAbsData>(), &globsSet));
7923//
7924//
7925//
7926// /*
7927// obs().argList() = s; // sets global observables to their values
7928// auto _fr =
7929// std::shared_ptr<RooFitResult>(_pdf->fitTo(*_data->get<RooAbsData>(),RooFit::GlobalObservables(s),RooFit::Offset(true),RooFit::Save()));
7930// _fr->SetName(TUUID().AsString());
7931// // restore parameters before returning
7932// *std::unique_ptr<RooArgSet>(_pdf->getDependents(_fr->floatParsFinal())) = _fr->floatParsInit();
7933// */
7934//
7935// //_fr->SetTitle(TString::Format("%s;%s",GetName(),datasetName));
7936// if (!_frt) {
7937// t =
7938// std::make_shared<RooFitResultTree>(std::dynamic_pointer_cast<TTree>(const_cast<xRooNode*>(this)->acquire(t->fTree)).get());
7939// }
7940// xRooNode parent(_data.GetName(),nullptr,xRooNode(t,*this));
7941// xRooNode out(_fr->GetName(),/*acquire(_fr)*/ _fr,parent);
7942// // do full propagation by 'checking' the fr ...
7943// out.Checked(&out,true);
7944// return out;
7945// }
7946
7947std::shared_ptr<xRooNode> xRooNode::parentPdf() const
7948{
7949 // find first parent that is a pdf
7950 auto out = fParent;
7951 while (out && !out->get<RooAbsPdf>()) {
7952 out = out->fParent;
7953 }
7954 return out;
7955}
7956
7957xRooNode xRooNode::reduced(const std::string &_range, bool invert) const
7958{
7959 auto rangeName = (_range.empty()) ? GetRange() : _range;
7960 if (!rangeName.empty()) {
7961 std::vector<TString> patterns;
7962 TStringToken pattern(rangeName, ",");
7963 while (pattern.NextToken()) {
7964 patterns.emplace_back(pattern);
7965 }
7966 if (auto s = get<RooSimultaneous>(); s) {
7967 // need to reduce the RooSimultaneous until fix: https://github.com/root-project/root/issues/8231
7968 auto &_cat = const_cast<RooAbsCategoryLValue &>(s->indexCat());
7969 auto newPdf =
7970 std::make_shared<RooSimultaneous>(TString::Format("%s_reduced", GetName()), "Reduced model", _cat);
7971 for (auto &c : bins()) {
7972 TString cName(c->GetName());
7973 cName = cName(cName.Index('=') + 1, cName.Length());
7974 _cat.setLabel(cName);
7975 bool matchAny = false;
7976 for (auto &p : patterns) {
7977 TString pNoCatName(p);
7978 if (pNoCatName.Contains('='))
7979 pNoCatName = pNoCatName(pNoCatName.Index('=') + 1, pNoCatName.Length());
7980 if (cName.Contains(TRegexp(p, true)) || cName.Contains(TRegexp(pNoCatName, true))) {
7981 matchAny = true;
7982 break;
7983 }
7984 if (_cat.hasRange(p) && _cat.inRange(p)) {
7985 matchAny = true;
7986 break;
7987 }
7988 }
7989 if ((matchAny && !invert) || (!matchAny && invert)) {
7990 newPdf->addPdf(*c->get<RooAbsPdf>(), cName);
7991 }
7992 }
7993 return xRooNode(newPdf, fParent);
7994 } else if (get() && !get<RooAbsCollection>() && !components().empty()) {
7995 // create a new obj and remove non-matching components
7996 xRooNode out(std::shared_ptr<TObject>(get()->Clone(TString::Format("%s_reduced", get()->GetName()))), fParent);
7997 // go through components and remove any that don't match pattern
7998 std::vector<TObject *> funcs; // to be removed
7999 for (auto &c : out.components()) {
8000 bool matchAny = false;
8001 for (auto &p : patterns) {
8002 if (TString(c->GetName()).Contains(TRegexp(p, true))) {
8003 matchAny = true;
8004 break;
8005 }
8006 }
8007 if (!((matchAny && !invert) || (!matchAny && invert)))
8008 funcs.push_back(c->get());
8009 }
8010 for (auto &c : funcs)
8011 out.Remove(*c);
8012 if (!funcs.empty()) {
8013 if (auto _pdf = out.get<RooRealSumPdf>(); _pdf) {
8014 _pdf->setFloor(false); // remove floor if removed some functions, which allows evaluation of negative
8015 // valued components
8016 }
8017 }
8018 out.browse();
8019 return out;
8020 } else if (auto fr = get<RooFitResult>()) {
8021 // reduce the fit result by moving unselected float pars into the constPars list and dropping their covariances
8022 xRooNode out(std::shared_ptr<TObject>(fr->Clone(TString::Format("%s_reduced", fr->GetName()))), fParent);
8023 fr = out.get<RooFitResult>();
8024 RooArgList _pars = fr->floatParsFinal();
8025 RooArgList _remPars;
8026 for (auto c : _pars) {
8027 bool matchAny = false;
8028 for (auto &p : patterns) {
8029 if (TString(c->GetName()).Contains(TRegexp(p, true))) {
8030 matchAny = true;
8031 break;
8032 }
8033 }
8034 if (!((matchAny && !invert) || (!matchAny && invert))) {
8035 _remPars.add(*c);
8036 }
8037 }
8038 _pars.remove(_remPars, true);
8039
8040 auto _tmp = fr->reducedCovarianceMatrix(_pars);
8041 int covQualBackup = fr->covQual();
8042 fr->setCovarianceMatrix(_tmp);
8043 fr->setCovQual(covQualBackup);
8044 const_cast<RooArgList &>(fr->floatParsFinal())
8045 .remove(_remPars, true); // is this a memory leak ... should delete the remPars?
8046 return out;
8047
8048 } else if (!get() || get<RooAbsCollection>()) {
8049 // filter the children .... handle special case of filtering ".vars" with "x" option too
8050 xRooNode out(std::make_shared<RooArgList>(), fParent);
8051 out.SetName(TString(GetName()) + "_reduced");
8052 size_t nobs = 0;
8053 bool notAllArgs = false;
8054 bool isVars = (strcmp(GetName(), ".vars") == 0);
8055 for (auto c : *this) {
8056 nobs += (c->fFolder == "!robs" || c->fFolder == "!globs");
8057 bool matchAny = false;
8058 for (auto &p : patterns) {
8059 if (TString(c->GetName()).Contains(TRegexp(p, true)) ||
8060 (isVars && p == "x" && (c->fFolder == "!robs" || c->fFolder == "!globs") && nobs == 1)) {
8061 matchAny = true;
8062 break;
8063 }
8064 }
8065 if ((matchAny && !invert) || (!matchAny && invert)) {
8066 out.push_back(c);
8067 if (auto a = c->get<RooAbsArg>()) {
8068 out.get<RooArgList>()->add(*a);
8069 } else {
8070 notAllArgs = true;
8071 }
8072 }
8073 }
8074 if (notAllArgs) {
8075 out.fComp.reset();
8076 }
8077 return out;
8078 }
8079 }
8080
8081 return get<RooArgList>() ? xRooNode(std::make_shared<RooArgList>(), fParent) : *this;
8082}
8083
8084xRooNode xRooNode::reduced(const std::function<bool(const xRooNode &)> selector) const
8085{
8086 if (empty()) {
8087 const_cast<xRooNode &>(*this).browse();
8088 }
8089 // build a list of children to keep
8090 std::string noName = "___"; // assume this is never a name
8091 std::string childNames;
8092 for (auto &c : *this) {
8093 if (selector(*c)) {
8094 if (!childNames.empty())
8095 childNames += ",";
8096 childNames += c->GetName();
8097 }
8098 }
8099 if (childNames.empty())
8100 childNames = noName; // if childNames was blank it would return the full list;
8101 return reduced(childNames); // calls main method above ... this will ensure we construct a reduced version of ourself
8102}
8103
8104// xRooNode xRooNode::generate(bool expected) const {
8105//
8106// auto fr = fitResult();
8107// auto _fr = fr.get<RooFitResult>();
8108//
8109// auto _pdf = (get<RooAbsPdf>()) ? std::shared_ptr<const xRooNode>(this, [](const xRooNode*){}) : parentPdf();
8110// if (!_pdf) {
8111// throw std::runtime_error("Could not find pdf");
8112// }
8113//
8114// std::shared_ptr<RooDataTree> t;
8115//
8116// std::shared_ptr<RooSimultaneous> newPdf;
8117// if(auto s = _pdf->get<RooSimultaneous>(); s) {
8118// auto rangeName = GetRange();
8119// if (rangeName) {
8120// // need to reduce the RooSimultaneous until fix: https://github.com/root-project/root/issues/8231
8121// std::vector<TString> chanPatterns;
8122// TStringToken pattern(rangeName, ",");
8123// while (pattern.NextToken()) {
8124// chanPatterns.emplace_back(pattern);
8125// }
8126// auto& _cat = const_cast<RooAbsCategoryLValue&>(s->indexCat());
8127// newPdf = std::make_shared<RooSimultaneous>(TString::Format("%s_reduced",GetName()),"Reduced model",_cat);
8128// for(auto& c : _pdf->variations()) {
8129// TString cName(c->GetName());
8130// cName = cName(cName.Index('=')+1,cName.Length());
8131// _cat.setLabel(cName);
8132// bool matchAny=false;
8133// for(auto& p : chanPatterns) {
8134// if (cName.Contains(TRegexp(p,true))) { matchAny=true; break; }
8135// if (_cat.hasRange(p) && _cat.inRange(p)) { matchAny=true; break; }
8136// }
8137// if(matchAny) {
8138// newPdf->addPdf( *c->get<RooAbsPdf>(), cName );
8139// }
8140// }
8141// t = std::make_shared<RooDataTree>(newPdf->GetName(),"",*newPdf);
8142// RooArgSet s1(_pdf->obs().argList());
8143// RooArgSet s2(_pdf->globs().argList());s1.remove(s2);
8144// t->SetObservables(&s1,&s2);
8145// auto _data = t->generate(_fr,expected);
8146//
8147// xRooNode parent(_fr ? _fr->GetName() : "unknown",nullptr,xRooNode(t,*this));
8148// xRooNode out(_data.first->GetName(),/*acquire(_fr)*/ _data.first,parent);
8149// out.emplace_back(std::make_shared<xRooNode>(".globs",std::const_pointer_cast<RooArgSet>(_data.second),out));
8150// return out;
8151// }
8152// }
8153//
8154//
8155// std::string treeName = TString::Format("gen_%s",_pdf->GetName()).Data();
8156//
8157// auto _frt = getObject<TTree>(treeName); // get existing frt
8158//
8159//
8160// if (_frt) {
8161// t = std::make_shared<RooDataTree>(_frt.get());
8162// } else {
8163// t = std::make_shared<RooDataTree>(treeName.c_str(),"",*_pdf->get<RooAbsPdf>());
8164// RooArgSet s1(_pdf->obs().argList());
8165// RooArgSet s2(_pdf->globs().argList());s1.remove(s2);
8166// t->SetObservables(&s1,&s2);
8167// }
8168// auto _data = t->generate(_fr,expected);
8169// if (!_frt) {
8170// t =
8171// std::make_shared<RooDataTree>(std::dynamic_pointer_cast<TTree>(const_cast<xRooNode*>(this)->acquire(t->fTree)).get());
8172// }
8173// xRooNode parent(_fr ? _fr->GetName() : "unknown",nullptr,xRooNode(t,*this));
8174// xRooNode out(_data.first->GetName(),/*acquire(_fr)*/ _data.first,parent);
8175// out.emplace_back(std::make_shared<xRooNode>(".globs",std::const_pointer_cast<RooArgSet>(_data.second),out));
8176// return out;
8177// }
8178
8180public:
8182 double expectedEvents(const RooArgSet *nset) const override
8183 {
8184 return static_cast<RooAbsPdf *>(intpdf.absArg())->expectedEvents(nset);
8185 }
8186 ExtendMode extendMode() const override { return static_cast<RooAbsPdf *>(intpdf.absArg())->extendMode(); }
8187 TObject *clone(const char *newname) const override { return new xRooProjectedPdf(*this, newname); }
8188
8189protected:
8190 double evaluate() const override
8191 {
8192 int code;
8193 return getProjection(&intobs, _normSet, (_normRange.Length() > 0 ? _normRange.Data() : nullptr), code)->getVal();
8194 }
8195};
8196
8197std::vector<double> new_getPropagatedErrors(const RooAbsReal &f, const RooFitResult &fr, const RooArgSet &nset,
8198 RooArgList **pars, bool asymHi, bool asymLo, const std::vector<int> &bins,
8199 const std::function<void(int)> &setBin)
8200{
8201 const size_t nBins = bins.size();
8202 std::vector<double> out(nBins, 0.);
8203 if (nBins == 0)
8204 return out;
8205
8206 // Build the list of parameters f depends on that have a non-zero error.
8207 // The result is cached in *pars so it is only computed once across repeated
8208 // calls (e.g. the separate Hi and Lo passes of an asymmetric error band).
8209 RooArgList *_pars = (pars) ? *pars : nullptr;
8210
8211 if (!_pars) {
8212
8213 RooArgSet allParamsInAbsReal;
8214 f.getParameters(&nset, allParamsInAbsReal);
8215
8216 _pars = new RooArgList;
8217 for (auto *rrvFitRes : static_range_cast<RooRealVar *>(fr.floatParsFinal())) {
8218
8219 auto rrvInAbsReal = static_cast<RooRealVar const *>(allParamsInAbsReal.find(*rrvFitRes));
8220
8221 // Strip out parameters with zero error
8222 if (rrvFitRes->getError() <= std::abs(rrvFitRes->getVal()) * std::numeric_limits<double>::epsilon())
8223 continue;
8224
8225 // Ignore parameters in the fit result that this RooAbsReal doesn't depend on
8226 if (!rrvInAbsReal)
8227 continue;
8228
8229 // Checking for float equality is a bad. We check if the values are
8230 // negligibly far away from each other, relative to the uncertainty.
8231 if (std::abs(rrvInAbsReal->getVal() - rrvFitRes->getVal()) > 0.01 * rrvFitRes->getError()) {
8232 std::stringstream errMsg;
8233 errMsg << "RooAbsReal::getPropagatedError(): the parameters of the RooAbsReal don't have"
8234 << " the same values as in the fit result! The logic of getPropagatedError is broken in this case."
8235 << " \n " << rrvInAbsReal->GetName() << " : " << rrvInAbsReal->getVal() << " vs "
8236 << rrvFitRes->getVal();
8237
8238 throw std::runtime_error(errMsg.str());
8239 }
8240
8241 _pars->add(*rrvInAbsReal);
8242 }
8243 }
8244
8245 const size_t nPars = _pars->size();
8246
8247 // Covariance and correlation matrices depend only on the fit result and the
8248 // parameter set, so build them (including the matrix reduction) once for all
8249 // bins rather than once per bin.
8250 // TODO: if _pars includes pars not in fr, need to extend matrix with uncorrelated errors of those pars
8251 // (as built above, _pars is a subset of fr.floatParsFinal(), so such pars are currently dropped silently).
8252 TMatrixDSym V(nPars == fr.floatParsFinal().size() ? fr.covarianceMatrix() : fr.reducedCovarianceMatrix(*_pars));
8253
8254 TMatrixDSym C(nPars);
8255 for (size_t i = 0; i < nPars; i++) {
8256 for (size_t j = i; j < nPars; j++) {
8257 C(i, j) = V(i, j) / std::sqrt(V(i, i) * V(j, j));
8258 C(j, i) = C(i, j);
8259 }
8260 }
8261
8262 // Nominal value of each bin (only needed to symmetrize asymmetric errors).
8263 std::vector<double> nomVals;
8264 if (asymHi || asymLo) {
8265 nomVals.resize(nBins);
8266 for (size_t k = 0; k < nBins; k++) {
8267 setBin(bins[k]);
8268 nomVals[k] = f.getVal(nset);
8269 }
8270 }
8271
8272 // F[k] holds, for bin k, the vector of (signed) variations w.r.t. each
8273 // parameter - i.e. the column of the Jacobian times the parameter error.
8274 std::vector<TVectorD> F(nBins, TVectorD(nPars));
8275
8276 std::vector<double> plusVar(nBins), minusVar(nBins);
8277
8278 double ERROR_FACTOR = 1.;
8279 for (size_t ivar = 0; ivar < nPars; ivar++) {
8280
8281 auto &rrv = static_cast<RooRealVar &>((*_pars)[ivar]);
8282 auto *frrrv = static_cast<RooRealVar *>(fr.floatParsFinal().find(rrv));
8283
8284 double cenVal = rrv.getVal();
8285 ERROR_FACTOR = 1;//(1e-2)*std::max(1.,std::abs(cenVal));
8286
8287 if (asymHi || asymLo) {
8288 double errValHi = frrrv->getErrorHi();
8289 rrv.setVal(errValHi > 0 ? std::min(cenVal + errValHi*ERROR_FACTOR, rrv.getMax())
8290 : std::max(cenVal + errValHi*ERROR_FACTOR, rrv.getMin()));
8291 for (size_t k = 0; k < nBins; k++) {
8292 setBin(bins[k]);
8293 plusVar[k] = f.getVal(nset);
8294 }
8295 double errValLo = frrrv->getErrorLo();
8296 rrv.setVal(errValLo > 0 ? std::min(cenVal + errValLo*ERROR_FACTOR, rrv.getMax())
8297 : std::max(cenVal + errValLo*ERROR_FACTOR, rrv.getMin()));
8298 for (size_t k = 0; k < nBins; k++) {
8299 setBin(bins[k]);
8300 minusVar[k] = f.getVal(nset);
8301 }
8302 rrv.setVal(cenVal);
8303 for (size_t k = 0; k < nBins; k++) {
8304 double hi = plusVar[k];
8305 double lo = minusVar[k];
8306 if (asymHi) {
8307 // pick the one that moved result 'up' most
8308 hi = std::max(plusVar[k], minusVar[k]);
8309 lo = 2 * nomVals[k] - hi; // symmetrizes
8310 } else {
8311 // pick the one that moved result 'down' most
8312 lo = std::min(plusVar[k], minusVar[k]);
8313 hi = 2 * nomVals[k] - lo; // symmetrizes
8314 }
8315 F[k][ivar] = (hi - lo) * 0.5 / ERROR_FACTOR;
8316 }
8317 } else {
8318 double errVal = sqrt(V(ivar, ivar));//frrrv->getError(); //sqrt(V(ivar, ivar));
8319 // Make Plus variation
8320 rrv.setVal(std::min(cenVal + errVal*ERROR_FACTOR, rrv.getMax()));
8321 for (size_t k = 0; k < nBins; k++) {
8322 setBin(bins[k]);
8323 plusVar[k] = f.getVal(nset);
8324 }
8325 // Make Minus variation
8326 rrv.setVal(std::max(cenVal - errVal*ERROR_FACTOR, rrv.getMin()));
8327 for (size_t k = 0; k < nBins; k++) {
8328 setBin(bins[k]);
8329 minusVar[k] = f.getVal(nset);
8330 }
8331 rrv.setVal(cenVal);
8332 for (size_t k = 0; k < nBins; k++) {
8333 F[k][ivar] = (plusVar[k] - minusVar[k]) * 0.5 / ERROR_FACTOR;
8334 }
8335 }
8336 }
8337
8338 // Re-evaluate f with the central parameters just to be extra-safe that this
8339 // call doesn't leave any state changed. It should not be necessary because
8340 // thanks to the dirty flag propagation f is re-evaluated anyway the next
8341 // time getVal() is called, but there are imaginable corner cases where that
8342 // would not be triggered (e.g. if the caller changes the RooFit operation
8343 // mode after the error propagation).
8344 setBin(bins[nBins - 1]);
8345 f.getVal(nset);
8346
8347 // Calculate error in linear approximation from variations and correlation
8348 // coefficients - this is pure arithmetic (no function evaluations).
8349 for (size_t k = 0; k < nBins; k++) {
8350 out[k] = std::sqrt(F[k] * (C * F[k]));
8351 }
8352
8353 if (!pars) {
8354 delete _pars;
8355 } else {
8356 *pars = _pars;
8357 }
8358
8359 return out;
8360}
8361
8362class PdfWrapper : public RooAbsPdf {
8363public:
8364 // need expPdf option while RooProjectedPdf doesn't support keeping things extended
8365 PdfWrapper(RooAbsReal &f, RooAbsReal *coef, bool expEvMode = false, RooAbsPdf *expPdf = nullptr)
8366 : RooAbsPdf(Form("exp_%s", f.GetName())),
8367 fFunc("func", "func", this, f),
8368 fCoef("coef", "coef", this),
8369 fExpPdf("expPdf", "expPdf", this)
8370 {
8371 // don't treat pdf as extended if it has a coefficient and is RooAddPdf: RooAddPdf doesn't extend them unless no
8372 // coefs for any (and all are extendable)
8373 if (coef) {
8374 fCoef.setArg(*coef);
8375 }
8376 if (expPdf && expPdf->canBeExtended() && !(coef && dynamic_cast<RooAddPdf *>(expPdf))) {
8377 fExpPdf.setArg(*expPdf);
8378 } else if (auto _p = dynamic_cast<RooAbsPdf *>(&f);
8379 _p && _p->canBeExtended() && !(coef && dynamic_cast<RooAddPdf *>(_p))) {
8380 fExpPdf.setArg(f); // using self for expectation
8381 }
8382 fExpectedEventsMode = expEvMode;
8383 }
8384 ~PdfWrapper() override {};
8385 PdfWrapper(const PdfWrapper &other, const char *name = nullptr)
8386 : RooAbsPdf(other, name),
8387 fFunc("func", this, other.fFunc),
8388 fCoef("coef", this, other.fCoef),
8389 fExpPdf("expPdf", this, other.fExpPdf),
8390 fExpectedEventsMode(other.fExpectedEventsMode)
8391 {
8392 }
8393 TObject *clone(const char *newname) const override { return new PdfWrapper(*this, newname); }
8394 bool isBinnedDistribution(const RooArgSet &obs) const override { return fFunc->isBinnedDistribution(obs); }
8395 std::list<double> *binBoundaries(RooAbsRealLValue &obs, double xlo, double xhi) const override
8396 {
8397 return fFunc->binBoundaries(obs, xlo, xhi);
8398 }
8399
8400 double evaluate() const override
8401 {
8402 return (fExpectedEventsMode ? 1. : fFunc) *
8403 ((fExpPdf.absArg()) ? static_cast<RooAbsPdf *>(fExpPdf.absArg())->expectedEvents(_normSet) : 1.) *
8404 (fCoef.absArg() ? fCoef : 1.);
8405 }
8406
8407 bool selfNormalized() const override
8408 {
8409 return true;
8410 } // so that doesn't try to do an integral because we are passing integration onto fFunc in evaluate
8411
8412 // faster than full evaluation because doesnt make the integral dependent on the full expression
8413 double getSimplePropagatedError(const RooFitResult &fr, const RooArgSet &nset_in) const
8414 {
8415#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 28, 00)
8416 double oo = getPropagatedError(fr, nset_in); // method was improved in 6.28 so use this instead
8417 if (std::isnan(oo)) {
8418 // may be consequence of zero uncerts
8419 // Calling getParameters() might be costly, but necessary to get the right
8420 // parameters in the RooAbsReal. The RooFitResult only stores snapshots.
8421 RooArgSet allParamsInAbsReal;
8422 getParameters(&nset_in, allParamsInAbsReal);
8423
8424 RooArgList paramList;
8425 for (auto *rrvFitRes : static_range_cast<RooRealVar *>(fr.floatParsFinal())) {
8426
8427 auto rrvInAbsReal = static_cast<RooRealVar const *>(allParamsInAbsReal.find(*rrvFitRes));
8428
8429 // If this RooAbsReal is a RooRealVar in the fit result, we don't need to
8430 // propagate anything and can just return the error in the fit result
8431 if (rrvFitRes->namePtr() == namePtr())
8432 return rrvFitRes->getError();
8433
8434 // Strip out parameters with zero error
8435 if (!rrvFitRes->hasError() ||
8436 rrvFitRes->getError() <= std::abs(rrvFitRes->getVal()) * std::numeric_limits<double>::epsilon())
8437 continue;
8438
8439 // Ignore parameters in the fit result that this RooAbsReal doesn't depend on
8440 if (!rrvInAbsReal)
8441 continue;
8442
8443 // Checking for float equality is a bad. We check if the values are
8444 // negligibly far away from each other, relative to the uncertainty.
8445 if (std::abs(rrvInAbsReal->getVal() - rrvFitRes->getVal()) > 0.01 * rrvFitRes->getError()) {
8446 std::stringstream errMsg;
8447 errMsg
8448 << "RooAbsReal::getPropagatedError(): the parameters of the RooAbsReal don't have"
8449 << " the same values as in the fit result! The logic of getPropagatedError is broken in this case.";
8450
8451 throw std::runtime_error(errMsg.str());
8452 }
8453
8454 paramList.add(*rrvInAbsReal);
8455 }
8456 if (paramList.empty())
8457 return 0.;
8458
8459 std::vector<double> plusVar;
8460 std::vector<double> minusVar;
8461 plusVar.reserve(paramList.size());
8462 minusVar.reserve(paramList.size());
8463
8464 // Create std::vector of plus,minus variations for each parameter
8465 TMatrixDSym V(paramList.size() == fr.floatParsFinal().size() ? fr.covarianceMatrix()
8466 : fr.reducedCovarianceMatrix(paramList));
8467
8468 for (std::size_t ivar = 0; ivar < paramList.size(); ivar++) {
8469
8470 auto &rrv = static_cast<RooRealVar &>(paramList[ivar]);
8471
8472 double cenVal = rrv.getVal();
8473 double errVal = sqrt(V(ivar, ivar));
8474
8475 // this next thing happens if the par has errors but the covariance matrix is empty
8476 // this only happens if the fit was dodgy, so perhaps best to not even try to recover from this
8477 // screwup ... hence I've commented out this fixup here and will let the errors be nan
8478 // if(errVal==0) {
8479 // Warning("getPropagatedError","Missing variance for %s",rrv.GetName());
8480 // errVal = rrv.getError();
8481 // V(ivar,ivar) = errVal*errVal;
8482 // }
8483
8484 // Make Plus variation
8485 rrv.setVal(cenVal + errVal);
8486 plusVar.push_back(getVal(nset_in));
8487
8488 // Make Minus variation
8489 rrv.setVal(cenVal - errVal);
8490 minusVar.push_back(getVal(nset_in));
8491#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
8492 // can try to recover nans ... this stopped being possible in 6.27 onwards because NaNPacker made private
8493 if (std::isnan(plusVar.back()) && RooNaNPacker::isNaNWithPayload(plusVar.back())) {
8494 plusVar.back() = -RooNaNPacker::unpackNaN(plusVar.back());
8495 }
8496 if (std::isnan(minusVar.back()) && RooNaNPacker::isNaNWithPayload(minusVar.back())) {
8497 minusVar.back() = -RooNaNPacker::unpackNaN(minusVar.back());
8498 }
8499#endif
8500 // std::cout << plusVar.back() << " and " << minusVar.back() << std::endl;
8501
8502 rrv.setVal(cenVal);
8503 }
8504
8505 // Re-evaluate this RooAbsReal with the central parameters just to be
8506 // extra-safe that a call to `getPropagatedError()` doesn't change any state.
8507 // It should not be necessary because thanks to the dirty flag propagation
8508 // the RooAbsReal is re-evaluated anyway the next time getVal() is called.
8509 // Still there are imaginable corner cases where it would not be triggered,
8510 // for example if the user changes the RooFit operation more after the error
8511 // propagation.
8512 getVal(nset_in);
8513
8514 TMatrixDSym C(paramList.size());
8515 std::vector<double> errVec(paramList.size());
8516 for (std::size_t i = 0; i < paramList.size(); i++) {
8517 errVec[i] = std::sqrt(V(i, i));
8518 for (std::size_t j = i; j < paramList.size(); j++) {
8519 C(i, j) = V(i, j) / std::sqrt(V(i, i) * V(j, j));
8520 C(j, i) = C(i, j);
8521 }
8522 }
8523
8524 // Make std::vector of variations
8525 TVectorD F(plusVar.size());
8526 for (unsigned int j = 0; j < plusVar.size(); j++) {
8527 F[j] = (plusVar[j] - minusVar[j]) / 2;
8528 }
8529
8530 // Calculate error in linear approximation from variations and correlation coefficient
8531 double sum = F * (C * F);
8532
8533 return sqrt(sum);
8534 }
8535 return oo;
8536#else
8537
8538 // Strip out parameters with zero error
8539 RooArgList fpf_stripped;
8540 for (auto *frv : static_range_cast<RooRealVar *>(fr.floatParsFinal())) {
8541 if (frv->getError() > 1e-20) {
8542 fpf_stripped.add(*frv);
8543 }
8544 }
8545
8546 // Clone self for internal use
8547 RooAbsReal *cloneFunc = const_cast<PdfWrapper *>(this); // (RooAbsReal *)fFunc.absArg()->cloneTree();
8548 // RooAbsPdf *clonePdf = dynamic_cast<RooAbsPdf *>(cloneFunc);
8549 RooArgSet *errorParams = cloneFunc->getObservables(fpf_stripped);
8550
8551 RooArgSet *nset =
8552 nset_in.size() == 0 ? cloneFunc->getParameters(*errorParams) : cloneFunc->getObservables(nset_in);
8553
8554 // Make list of parameter instances of cloneFunc in order of error matrix
8555 RooArgList paramList;
8556 const RooArgList &fpf = fpf_stripped;
8557 std::vector<int> fpf_idx;
8558 for (Int_t i = 0; i < fpf.size(); i++) {
8559 RooAbsArg *par = errorParams->find(fpf[i].GetName());
8560 if (par) {
8561 paramList.add(*par);
8562 fpf_idx.push_back(i);
8563 }
8564 }
8565
8566 std::vector<double> plusVar, minusVar;
8567
8568 // Create vector of plus,minus variations for each parameter
8569 TMatrixDSym V(paramList.size() == fr.floatParsFinal().size() ? fr.covarianceMatrix()
8570 : fr.reducedCovarianceMatrix(paramList));
8571
8572 for (Int_t ivar = 0; ivar < paramList.size(); ivar++) {
8573
8574 RooRealVar &rrv = (RooRealVar &)fpf[fpf_idx[ivar]];
8575
8576 double cenVal = rrv.getVal();
8577 double errVal = sqrt(V(ivar, ivar));
8578
8579 // Make Plus variation
8580 ((RooRealVar *)paramList.at(ivar))->setVal(cenVal + errVal);
8581 // plusVar.push_back((fExpectedEventsMode ? 1. : cloneFunc->getVal(nset)) *
8582 // (clonePdf ? clonePdf->expectedEvents(nset) : 1.));
8583 plusVar.push_back(cloneFunc->getVal(nset));
8584
8585 // Make Minus variation
8586 ((RooRealVar *)paramList.at(ivar))->setVal(cenVal - errVal);
8587 // minusVar.push_back((fExpectedEventsMode ? 1. : cloneFunc->getVal(nset)) *
8588 // (clonePdf ? clonePdf->expectedEvents(nset) : 1.));
8589 minusVar.push_back(cloneFunc->getVal(nset));
8590
8591 ((RooRealVar *)paramList.at(ivar))->setVal(cenVal);
8592 }
8593
8594 getVal(nset); // reset state
8595
8596 TMatrixDSym C(paramList.size());
8597 std::vector<double> errVec(paramList.size());
8598 for (int i = 0; i < paramList.size(); i++) {
8599 errVec[i] = sqrt(V(i, i));
8600 for (int j = i; j < paramList.size(); j++) {
8601 C(i, j) = V(i, j) / sqrt(V(i, i) * V(j, j));
8602 C(j, i) = C(i, j);
8603 }
8604 }
8605
8606 // Make vector of variations
8607 TVectorD F(plusVar.size());
8608 for (unsigned int j = 0; j < plusVar.size(); j++) {
8609 F[j] = (plusVar[j] - minusVar[j]) / 2;
8610 }
8611
8612 // Calculate error in linear approximation from variations and correlation coefficient
8613 double sum = F * (C * F);
8614
8615 // delete cloneFunc;
8616 delete errorParams;
8617 delete nset;
8618
8619 return sqrt(sum);
8620#endif
8621 }
8622
8623private:
8627 bool fExpectedEventsMode = false;
8628};
8629
8630const xRooNode *runningNode = nullptr;
8632
8634{
8635 std::cout << "Got signal " << signum << std::endl;
8636 if (signum == SIGINT) {
8637 std::cout << "Keyboard interrupt while building histogram" << std::endl;
8638 // TODO: create a global mutex for this
8639 runningNode->fInterrupted = true;
8640 } else {
8641 gOldHandlerr(signum);
8642 }
8643}
8644
8646{
8647 auto _doSterilize = [](RooAbsArg *obj) {
8648 if (!obj)
8649 return;
8650 for (int i = 0; i < obj->numCaches(); i++) {
8651 if (auto cache = dynamic_cast<RooObjCacheManager *>(obj->getCache(i))) {
8652 cache->reset();
8653 }
8654 }
8655 if (RooAbsPdf *p = dynamic_cast<RooAbsPdf *>(obj); p) {
8656 p->setNormRange(p->normRange());
8657 }
8658#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
8659 if (RooAbsReal *p = dynamic_cast<RooAbsReal *>(obj); p) {
8660 // need to forget about any normSet that was passed to getVal(...)
8661 // doesn't seem necessary in 6.28
8662
8663 p->setProxyNormSet(nullptr);
8664 p->_lastNSet = nullptr;
8665 }
8666#endif
8667 obj->setValueDirty();
8668 };
8669 if (auto w = get<RooWorkspace>(); w) {
8670 // sterilizing all nodes
8671 for (auto &c : w->components()) {
8672 _doSterilize(c);
8673 }
8674 return;
8675 }
8676 // recursive through all clients and sterlize their normalization caches
8677 std::function<void(RooAbsArg *)> func;
8678 func = [&](RooAbsArg *a) {
8679 if (!a) {
8680 return;
8681 }
8682 _doSterilize(a); // sterilize first so that cache elements don't appear in the client list
8683 // safety net in case sterilizing one client deletes another one of our clients
8684 // monitor for change in clients list size
8685 // found this was only case in 6.26 (valgrind shows invalid read), in 6.28 these went away
8686 // might be in 6.28 the client list iterator became able to handle in-loop edits but didn't see
8687 // in test case that client count changed so just resterilizing if that's the case.
8688 size_t nClients;
8689 do {
8690 nClients = a->clients().size();
8691 for (auto obj : a->clients()) {
8692 func(dynamic_cast<RooAbsArg *>(obj));
8693 if (a->clients().size() != nClients) {
8694 break; // means sterilizing a client changed our clients, so don't trust the client iterator at this
8695 // point
8696 }
8697 }
8698 } while (a->clients().size() != nClients);
8699 };
8700 func(get<RooAbsArg>());
8701}
8702
8703// observables not in the axisVars are automatically projected over
8704xRooNode xRooNode::histo(const xRooNode &vars, const xRooNode &fr, bool content, bool errors, bool stack, bool errorsHi,
8705 bool errorsLo, int nErrorToys) const
8706{
8707
8708 if (!vars.fComp && strlen(vars.GetName())) {
8709 return histo(xRooNode::vars().reduced(vars.GetName()), fr, content, errors,stack,errorsHi,errorsLo,nErrorToys);
8710 }
8711
8712 xRooNode out(TString::Format("%s.histo", GetName()), nullptr, *this);
8713
8714 RooAbsLValue *v = nullptr;
8715 if (vars.empty()) {
8716 // does an integral
8717 out.fComp = std::shared_ptr<TH1>(
8718 BuildHistogram(nullptr, !content, errors, -1, -1, fr, errorsHi, errorsLo, nErrorToys, nullptr, !stack, false));
8719 } else if (vars.size() == 1) {
8720 v = vars.at(0)->get<RooAbsLValue>();
8721 out.fComp = std::shared_ptr<TH1>(
8722 BuildHistogram(v, !content, errors, 1, 0, fr, errorsHi, errorsLo, nErrorToys, nullptr, !stack, true));
8723 } else {
8724 throw std::runtime_error("multi-dim histo not yet supported");
8725 }
8726
8727 return out;
8728}
8729
8731{
8732 return xRooNode(fComp, xRooNode(range.GetName(), nullptr, *this));
8733}
8734
8735TH1 *xRooNode::BuildHistogram(RooAbsLValue *v, bool empty, bool errors, int binStart, int binEnd, const xRooNode &_fr,
8736 bool errorsHi, bool errorsLo, int nErrorToys, TH1 *templateHist, bool nostack,
8737 bool setInterp) const
8738{
8739 auto rar = get<RooAbsReal>();
8740 if (!rar) {
8741 if (get<TH1>()) {
8742 return dynamic_cast<TH1 *>(get()->Clone());
8743 }
8744 return nullptr;
8745 }
8746
8747 TObject *vv = rar;
8748
8749 TH1 *h = nullptr;
8750 if (!v) {
8751 if (binStart != -1 || binEnd != -1) { // allow v to stay nullptr if doing integral (binStart=binEnd=-1)
8752 if (auto _ax = GetXaxis())
8753 v = dynamic_cast<RooAbsLValue *>(_ax->GetParent());
8754 } else {
8755 // don't need to integrate if doing a self-histogram
8756 v = dynamic_cast<RooRealVar *>(rar);
8757 }
8758 if (v) {
8759 vv = dynamic_cast<TObject *>(v);
8760 } else {
8761 // make a single-bin histogram of just this value
8762 TDirectory::TContext ctx{nullptr}; // No self-registration to directories
8763 h = new TH1D(rar->GetName(), rar->GetTitle(), 1, 0, 1);
8764 h->GetXaxis()->SetBinLabel(1, rar->GetName());
8765 h->GetXaxis()->SetTimeFormat(rar->GetName());
8766 }
8767 }
8768
8769 auto x = dynamic_cast<RooRealVar *>(v);
8770 bool setTitle = false;
8771 if (templateHist) {
8772 // using template hist for the binning
8773 {
8774 TDirectory::TContext ctx{nullptr}; // No self-registration to directories
8775 h = static_cast<TH1 *>(templateHist->Clone(rar->GetName()));
8776 }
8777 h->SetTitle(rar->GetTitle());
8778 h->Reset();
8779 } else if (x) {
8780 if (x == rar) {
8781 // self histogram ...
8782 {
8783 TDirectory::TContext ctx{nullptr}; // No self-registration to directories
8784 h = new TH1D(rar->GetName(), rar->GetTitle(), 1, 0, 1);
8785 }
8786 h->Sumw2();
8787 h->GetXaxis()->SetBinLabel(1, rar->GetName());
8788 h->SetBinContent(1, rar->getVal());
8789 if (x->getError()) {
8790 h->SetBinError(1, x->getError());
8791 h->SetFillStyle(3005);
8792 h->SetFillColor(h->GetLineColor());
8793 }
8794 h->SetMaximum(x->hasMax() ? x->getMax()
8795 : (h->GetBinContent(1) + std::max(std::abs(h->GetBinContent(1) * 0.1), 50.)));
8796 h->SetMinimum(x->hasMin() ? x->getMin()
8797 : (h->GetBinContent(1) - std::max(std::abs(h->GetBinContent(1) * 0.1), 50.)));
8798 h->GetXaxis()->SetName(dynamic_cast<TObject *>(v)->GetName());
8799 h->SetOption("e2");
8800 h->SetMarkerSize(0);
8801 h->SetMarkerStyle(0);
8802
8803 return h;
8804 }
8805 auto _ax = GetXaxis();
8806 TString binningName = (_ax && _ax->GetParent() == x) ? _ax->GetName() : rar->getStringAttribute("binning");
8807 if (binningName == "")
8808 binningName = rar->GetName();
8809 if (x->hasBinning(binningName)) {
8810 if (x->getBinning(binningName).isUniform()) {
8811 TDirectory::TContext ctx{nullptr}; // No self-registration to directories
8812 h = new TH1D(rar->GetName(), rar->GetTitle(), x->numBins(binningName) <= 0 ? 100 : x->numBins(binningName),
8813 x->getMin(binningName), x->getMax(binningName));
8814 } else {
8815 TDirectory::TContext ctx{nullptr}; // No self-registration to directories
8816 h = new TH1D(rar->GetName(), rar->GetTitle(), x->numBins(binningName), x->getBinning(binningName).array());
8817 }
8818 h->GetXaxis()->SetTitle(x->getBinning(binningName).GetTitle());
8819 setTitle = true;
8820 } else if (auto _boundaries =
8821 _or_func(/*rar->plotSamplingHint(*x,x->getMin(),x->getMax())*/ (std::list<double> *)(nullptr),
8822 rar->binBoundaries(*x, -std::numeric_limits<double>::infinity(),
8823 std::numeric_limits<double>::infinity()));
8824 _boundaries) {
8825 std::vector<double> _bins;
8826 for (auto &b : *_boundaries) {
8827 if (_bins.empty() || std::abs(_bins.back() - b) > 1e-5 * _bins.back())
8828 _bins.push_back(b);
8829 } // found sometimes get virtual duplicates in the binning
8830 TDirectory::TContext ctx{nullptr}; // No self-registration to directories
8831 h = new TH1D(rar->GetName(), rar->GetTitle(), _bins.size() - 1, &_bins[0]);
8832 delete _boundaries;
8833 } else if (!x->hasMax() || !x->hasMin()) {
8834 // use current value of x to estimate range with
8835 TDirectory::TContext ctx{nullptr}; // No self-registration to directories
8836 h = new TH1D(rar->GetName(), rar->GetTitle(), v->numBins(), x->getVal() * 0.2, x->getVal() * 5);
8837 } else {
8838 TDirectory::TContext ctx{nullptr}; // No self-registration to directories
8839 h = new TH1D(rar->GetName(), rar->GetTitle(), v->numBins(), x->getBinning().array());
8840 }
8841 h->Sumw2();
8842 } else if (!h) {
8843 {
8844 TDirectory::TContext ctx{nullptr}; // No self-registration to directories
8845 h = new TH1D(rar->GetName(), rar->GetTitle(), v->numBins(rar->GetName()), 0, v->numBins(rar->GetName()));
8846 }
8847 if (auto cat = dynamic_cast<RooAbsCategoryLValue *>(v)) {
8848 int i = 1;
8849 std::map<int, std::string> cats; // fill into a map to preserve index ordering
8850 for (auto &c : *cat) {
8851 cats[c.second] = c.first;
8852 }
8853 for (auto &[_, label] : cats) {
8854 h->GetXaxis()->SetBinLabel(i++, label.c_str());
8855 }
8856 }
8857 h->Sumw2();
8858 }
8859 if (auto o = dynamic_cast<TObject *>(v); o && !setTitle) {
8860 h->GetXaxis()->SetTitle(o->GetTitle());
8861 }
8862
8863 if (v) {
8864 if (h->GetXaxis()->IsAlphanumeric()) {
8865 // store the variable name in the TimeFormat property as well, b.c. alphanumeric requires axis name to be
8866 // "xaxis"
8867 h->GetXaxis()->SetTimeFormat(dynamic_cast<TObject *>(v)->GetName());
8868 } else {
8869 h->GetXaxis()->SetName(dynamic_cast<TObject *>(v)->GetName()); // WARNING: messes up display of bin labels
8870 }
8871 }
8872
8873 if (auto s = styles(nullptr, false); s) {
8874 auto _style = s.get<TStyle>();
8875 static_cast<TAttLine &>(*h) = *_style;
8876 static_cast<TAttFill &>(*h) = *_style;
8877 static_cast<TAttMarker &>(*h) = *_style;
8878 }
8879 if (strlen(h->GetXaxis()->GetTitle()) == 0)
8880 h->GetXaxis()->SetTitle(vv->GetTitle());
8881 auto p = dynamic_cast<RooAbsPdf *>(rar);
8882
8883 // possible speed improvement:
8884 // if(auto spdf = dynamic_cast<RooRealSumPdf*>(p); spdf && spdf->canBeExtended()) {
8885 // p = nullptr; // faster to evaluate sumpdf as a function not a pdf
8886 // }
8887
8888 if (empty && !errors) {
8889 return h;
8890 }
8891
8892 // if (!empty) {
8893
8894 auto _coefs = coefs();
8895
8896 RooFitResult *fr = nullptr;
8897 if (errors) {
8898 // must ensure the fit result we obtain includes pars from coefficients if present
8899 if (_fr.get<RooFitResult>()) {
8900 fr = static_cast<RooFitResult *>(_fr.get<RooFitResult>()->Clone());
8901 } else {
8902 auto frn =
8903 (!_coefs.get() ? *this : xRooNode(RooProduct("tmp", "tmp", RooArgList(*rar, *_coefs.get<RooAbsReal>()))))
8904 .fitResult();
8905 if (strlen(_fr.GetName()))
8906 frn = frn.reduced(_fr.GetName());
8907 fr = dynamic_cast<RooFitResult *>(frn->Clone());
8908 }
8909 if (!GETDMP(fr, _finalPars)) {
8911 }
8912
8913 /// Oct2022: No longer doing this because want to allow fitResult to be used to get partial error
8914 // // need to add any floating parameters not included somewhere already in the fit result ...
8915 // now done in the fitResult() method, where a fit result from the workspace can be automatically extended
8916
8917 TMatrixTSym<double> *prevCov = static_cast<TMatrixTSym<double> *>(GETDMP(fr, _VM));
8918
8919 if (!prevCov || size_t(fr->covarianceMatrix().GetNcols()) < fr->floatParsFinal().size()) {
8920 TMatrixDSym cov(fr->floatParsFinal().size());
8921 if (prevCov) {
8922 for (int i = 0; i < prevCov->GetNcols(); i++) {
8923 for (int j = 0; j < prevCov->GetNrows(); j++) {
8924 cov(i, j) = (*prevCov)(i, j);
8925 }
8926 }
8927 }
8928 int i = 0;
8929 for (auto &p2 : fr->floatParsFinal()) {
8930 if (!prevCov || i >= prevCov->GetNcols()) {
8931 cov(i, i) = pow(dynamic_cast<RooRealVar *>(p2)->getError(), 2);
8932 }
8933 i++;
8934 }
8935 int covQualBackup = fr->covQual();
8936 fr->setCovarianceMatrix(cov);
8937 fr->setCovQual(covQualBackup);
8938 }
8939
8940 if (v) {
8941 // need to remove v from result as we are plotting as function of v
8942 if (auto _p = fr->floatParsFinal().find(dynamic_cast<TObject *>(v)->GetName()); _p) {
8943 RooArgList _pars = fr->floatParsFinal();
8944 _pars.remove(*_p, true);
8945 auto _tmp = fr->reducedCovarianceMatrix(_pars);
8946 int covQualBackup = fr->covQual();
8947 fr->setCovarianceMatrix(_tmp);
8948 fr->setCovQual(covQualBackup);
8949 const_cast<RooArgList &>(fr->floatParsFinal())
8950 .remove(*_p, true); // NOTE: I think this might be a memory leak, should delete _p after removal
8951 }
8952 }
8953 // finally check at least one float has errors defined (might not be cause if in prefit state)
8954 bool hasErrors = false;
8955 for (auto pp : fr->floatParsFinal()) {
8956 if (dynamic_cast<RooRealVar *>(pp)->hasError()) {
8957 hasErrors = true;
8958 break;
8959 }
8960 }
8961 if (!hasErrors) {
8962 errors = false;
8963 delete fr;
8964 }
8965 }
8966
8967 RooArgSet normSet;
8968 if (v)
8969 normSet.add(*dynamic_cast<RooAbsArg *>(v));
8970
8971 if (binEnd == 0)
8972 binEnd = h->GetNbinsX();
8973
8974 bool needBinWidth = false;
8975 // may have MULTIPLE coefficients for the same pdf!
8976
8977 if (x && (p || _coefs.get() || rar->getAttribute("density"))) {
8978 // pdfs of samples embedded in a sumpdf (aka have a coef) will convert their density value to a content
8979 needBinWidth = true;
8980 }
8981
8982 if (auto spdf = dynamic_cast<RooRealSumPdf *>(p);
8983 spdf && spdf->canBeExtended() && !spdf->getFloor() && !_coefs.get()) {
8984 p = nullptr; // if pdf has no floor, will evaluate it as a function to allow it to be negative - evaluation should
8985 // also be faster (no integral)
8986 // exception is if RooRealSumPdf is embedded in a RooAddPdf (detected by presence of coefs) ... then it must be
8987 // evaluated as a pdf technically should check parent is a RooAddPdf, because if was inside a RooRealSumPdf then
8988 // would be evaluated as a function!
8989 }
8990
8991 // check if we need to do any projecting of other observables
8992 RooAbsReal *oldrar = nullptr;
8993 auto _obs = obs();
8994
8995 for (auto o : _obs) {
8996 if (auto rr = o->get<RooRealVar>(); rr && rr->hasRange("coordRange")) {
8997 rr->removeMin();
8998 rr->removeMax(); // rr->removeRange("coordRange"); // doesn't actually remove, just sets to
8999 // -inf->+inf
9000 rr->setStringAttribute("coordRange", nullptr); // removes the attribute
9001 }
9002 }
9003 // probably should also remove any range on the x-axis variable too, if there is one
9004 if (auto rr = dynamic_cast<RooRealVar *>(v); rr && rr->hasRange("coordRange")) {
9005 rr->removeMin();
9006 rr->removeMax(); // rr->removeRange("coordRange"); // doesn't actually remove, just sets to
9007 // -inf->+inf
9008 rr->setStringAttribute("coordRange", nullptr); // removes the attribute
9009 }
9010 coords(); // loads current coordinates and populates coordRange, if any
9011
9012 if (auto a = dynamic_cast<RooAbsArg *>(v))
9013 _obs.get<RooArgList>()->remove(*a);
9014 if (!_obs.get<RooArgList>()->empty()) {
9015 oldrar = rar;
9016 normSet.add(*_obs.get<RooArgList>());
9017 // check if any obs are restricted range
9018 bool hasRange = false;
9019 for (auto o : normSet) {
9020 if (auto rr = dynamic_cast<RooRealVar *>(o);
9021 rr && (rr->getStringAttribute("coordRange")) && strlen(rr->getStringAttribute("coordRange"))) {
9022 hasRange = true;
9023 break;
9024 }
9025 }
9026 if (p) {
9027 // need to handle special case of RooSimultaneous ... each pdf needs individually projecting over just its
9028 // dependent obs
9029 if (auto s = dynamic_cast<RooSimultaneous *>(p)) {
9030 auto newrar = new RooSimultaneous("projSim", "projSim", const_cast<RooAbsCategoryLValue &>(s->indexCat()));
9031 for (auto pdf : bins()) {
9032 // auto _pdf =
9033 // pdf->get<RooAbsPdf>()->createProjection(*pdf->get<RooAbsPdf>()->getObservables(*_obs.get<RooArgList>()));
9034 std::unique_ptr<RooArgSet> projObs{pdf->get<RooAbsPdf>()->getObservables(*_obs.get<RooArgList>())};
9035 auto _pdf = new xRooProjectedPdf(TString::Format("%s_projection", pdf->GetName()), "",
9036 *pdf->get<RooAbsPdf>(), *projObs);
9037 if (hasRange) {
9038 dynamic_cast<RooAbsPdf *>(_pdf)->setNormRange("coordRange");
9039 }
9040 newrar->addPdf(*_pdf, pdf->coords()[s->indexCat().GetName()]->get<RooCategory>()->getLabel());
9041 }
9042 rar = newrar;
9043 } else {
9044 rar = p->createProjection(
9045 *_obs.get<RooArgList>()); // TODO should use xRooProjectedPdf here too, because not fixed range and
9046 // extend behaviour of RooProjectedPdf in ROOT yet
9047 if (hasRange) {
9048 dynamic_cast<RooAbsPdf *>(rar)->setNormRange("coordRange");
9049 }
9050 }
9051 if (hasRange)
9052 p->setNormRange("coordRange"); // should get cleared when we sterilize
9053 } else {
9054 if (hasRange) {
9055 // commented out passing of normset so that getVal of non-pdf is always a 'raw' value (needed for raw eval
9056 // of RooRealSumPdf)
9057 rar = std::unique_ptr<RooAbsReal>{rar->createIntegral(
9058 *_obs.get<RooArgList>(),
9059 /*RooFit::NormSet(normSet),*/ RooFit::Range("coordRange"))}
9060 .release();
9061 } else {
9062 rar =
9063 std::unique_ptr<RooAbsReal>{rar->createIntegral(*_obs.get<RooArgList>() /*, RooFit::NormSet(normSet)*/)}
9064 .release();
9065 }
9066 }
9067 }
9068
9069 bool scaleExpected = (p && p->canBeExtended() && !_coefs.get());
9070 // Note about above: if pdf has coefficients then its embedded in a RooAddPdf that has coefs defined ...
9071 // in this case we should *not* scale by expected, since the coefs become the scaling instead
9072 // we should also not build a stack for this (may be a RooRealSumPdf inside a RooAddPdf, but the
9073 // samples of the RooRealSumPdf wont be correctly scaled to line up with overall RooRealSumPdf
9074 // which will be normalized to its coefficient
9075 if (!nostack && p && p->canBeExtended() && _coefs.get()) {
9076 nostack = true;
9077 // if wanted to still hve a stack, would need to scale the stack subcomponents by
9078 // coefs-value / p_integral(raw) ... since raw p-integral will be what stack integrates to
9079 }
9080
9081 std::unique_ptr<RooArgSet> snap(normSet.snapshot());
9082 TStopwatch timeIt;
9083 std::vector<double> lapTimes;
9084 bool warned = false;
9085 if (binStart == -1 && binEnd == -1) {
9086 binEnd = 1;
9087 }
9088 auto cat = (!x) ? dynamic_cast<RooAbsCategoryLValue *>(v) : nullptr;
9089 RooArgList *errorPars = nullptr;
9090 std::unique_ptr<RooAbsCollection> errorParsSnap;
9091
9092 if (!v) {
9093 setInterp = false;
9094 }
9095
9096 if (setInterp) {
9097 RooAbsArg *vvv = dynamic_cast<RooAbsArg *>(v);
9098 // determining if histogram should have interpolation drawing options set on it
9099 // need to strip namespace to discount the "HistFactory" namespace classes from all being treated as binned
9100 TString clNameNoNamespace = rar->ClassName();
9101 clNameNoNamespace = clNameNoNamespace(clNameNoNamespace.Last(':') + 1, clNameNoNamespace.Length());
9102 setInterp = (clNameNoNamespace.Contains("Hist") || vvv->isCategory() || rar->isBinnedDistribution(*vvv) ||
9103 h->GetNbinsX() == 1 || rar->getAttribute("BinnedLikelihood") ||
9104 (dynamic_cast<RooAbsRealLValue *>(vvv) &&
9105 std::unique_ptr<std::list<double>>(rar->binBoundaries(*dynamic_cast<RooAbsRealLValue *>(vvv),
9106 -std::numeric_limits<double>::infinity(),
9107 std::numeric_limits<double>::infinity()))))
9108 ? false
9109 : true;
9110 if (auto d = dynamic_cast<RooHistFunc *>(rar); d && !d->isBinnedDistribution(*vvv) && h->GetNbinsX() != 1) {
9111 setInterp = true; // hist func is interpolated, so draw it as such
9112 }
9113 if (setInterp && !components().empty()) {
9114 // check if all components of dOpt are "Hist" type (CMS model support)
9115 // if so then don't interp;
9116 bool allHist = true;
9117 for (auto &s : components()) {
9118 TString _clName = s->get()->ClassName();
9119 _clName = _clName(_clName.Last(':') + 1, _clName.Length());
9120 if (!(s->get() && _clName.Contains("Hist"))) {
9121 allHist = false;
9122 break;
9123 }
9124 }
9125 if (allHist)
9126 setInterp = false;
9127 }
9128 if (setInterp) {
9129 h->SetOption("l"); // does linear interpolation between points
9130 }
9131 }
9132
9133 if (errors) {
9134 // may be computing potentially asymmetric errors
9135 // the main histogram will be the error band, and the nominal histogram will be added as a function
9136 // so that it is drawn over the top of the error band
9137 // note that this means GetBinContent on returned histogram will return midpoint of the up and down error
9138 auto l = static_cast<TH1 *>(h->Clone("nominal"));
9139 l->SetDirectory(0);
9140 l->SetFillStyle(0);
9141 h->GetListOfFunctions()->Add(l, (setInterp) ? "lsame" : "histsame");
9142 h->SetOption(setInterp ? "e3" : "e2"); // default draw option E2 or E3 so error band shown .. could have used
9143 // 'EX0' to draw "classic style"
9144 // could take this from the 'band' style object if we create one in future?
9145 h->SetMarkerSize(0);
9146 h->SetFillStyle(3005);
9147 h->SetFillColor(h->GetLineColor());
9148 }
9149
9150 if (nErrorToys > 0) {
9151 errors = false; // wont evaluate error on each toy, will estimate for std.dev or normiles of toys
9152 // need list of errorPars
9153 auto allPars =
9154 (!_coefs.get() ? *this : xRooNode(RooProduct("tmp", "tmp", RooArgList(*rar, *_coefs.get<RooAbsReal>()))))
9155 .pars();
9156 errorPars = new RooArgList; // will be in same order as appear in fr.
9157 for (auto a : fr->floatParsFinal()) {
9158 if (auto par = allPars.get<RooArgList>()->find(*a)) {
9159 errorPars->add(*par);
9160 }
9161 }
9162 errorParsSnap.reset(errorPars->snapshot());
9163 auto l = static_cast<TH1 *>(h->Clone("toys"));
9164 l->Reset(); // removes any functions
9165 l->SetDirectory(0);
9166 h->GetListOfFunctions()->Add(
9167 l, "histsame"); // ensures just this empty hist will be drawn, and not each individual toy
9168
9169 if (errorsLo || errorsHi)
9170 empty = false; // must not be empty b.c. calculation of error relies on knowing nominal (see after loop)
9171 }
9172
9173 std::vector<int> errorBins; // bins (in-range) for which to compute the propagated error band, filled below
9174 for (int toy = 0; toy < (nErrorToys + 1); toy++) {
9175
9176 TH1 *main_h = h;
9177 if (toy > 0) {
9178 h = static_cast<TH1 *>(main_h->Clone(TString::Format("toy_%d", toy)));
9179 h->SetDirectory(0);
9180 h->Reset();
9181 static_cast<TH1 *>(main_h->GetListOfFunctions()->FindObject("toys"))->GetListOfFunctions()->Add(h);
9182 // randomize the parameter values according to the fr's covariance matrix
9183 errorPars->assignValueOnly(fr->randomizePars());
9184 // if any par has 0 error, randomizePars can end up assigning a nan, so replace
9185 // all zero errors with value
9186 for (auto pp : fr->floatParsFinal()) {
9187 auto _vv = dynamic_cast<RooRealVar *>(pp);
9188 if (!_vv)
9189 continue;
9190 if (_vv->getError() == 0)
9191 errorPars->setRealValue(pp->GetName(), _vv->getVal());
9192 }
9193 }
9194
9195 for (int i = std::max(1, binStart); i <= std::min(h->GetNbinsX(), binEnd); i++) {
9196 timeIt.Start(true);
9197 if (x) {
9198 x->setVal(h->GetBinCenter(i));
9199 } else if (cat) {
9200 cat->setLabel(h->GetXaxis()->GetBinLabel(i)); // because order might not match "binning" order
9201 } else if (v) {
9202 v->setBin(i - 1);
9203 }
9204 if (x && !x->inRange("coordRange"))
9205 continue;
9206
9207 double r = 0;
9208 if (!empty || toy > 0) {
9209 r = /*(p && p->selfNormalized())*/ rar->getVal(p ? &normSet : nullptr);
9210#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
9211 if (std::isnan(r) && RooNaNPacker::isNaNWithPayload(r)) {
9213 }
9214#endif
9215 if (r && _coefs.get()) {
9216 r *= _coefs.get<RooAbsReal>()->getVal(normSet);
9217 }
9218 if (needBinWidth) {
9219 r *= h->GetBinWidth(i);
9220 }
9221 if (scaleExpected) {
9222 // std::cout << r << " exp = " << p->expectedEvents(normSet) << " for normRange " << (p->normRange() ?
9223 // p->normRange() : "null") << std::endl; p->Print();rar->Print();
9224 r *= (p->expectedEvents(normSet));
9225 } // do in here in case dependency on var
9226 }
9227 h->SetBinContent(i, r);
9228
9229 if (errors) {
9230 static_cast<TH1 *>(h->FindObject("nominal"))->SetBinContent(i, r); // transfer nominal to nominal hist
9231 // Record for which bins to compute the error later.
9232 if (toy == 0) {
9233 errorBins.push_back(i);
9234 }
9235 }
9236 timeIt.Stop();
9237 lapTimes.push_back(timeIt.RealTime());
9238 double time_estimate =
9239 (lapTimes.size() > 1)
9240 ? (h->GetNbinsX() * (std::accumulate(lapTimes.begin() + 1, lapTimes.end(), 0.) / (lapTimes.size() - 1)))
9241 : 0.;
9242 if (!warned && (lapTimes.at(0) > 10 || (lapTimes.size() > 2 && time_estimate > 60.))) {
9243 TTimeStamp t2;
9244 t2.Add(time_estimate);
9245 Warning("BuildHistogram", "Building this histogram will take until %s", t2.AsString());
9246 if (errors) {
9247 // install interrupt handler
9248 runningNode = this;
9249 gOldHandlerr = signal(SIGINT, buildHistogramInterrupt);
9250 }
9251 warned = true;
9252 }
9253 if (fInterrupted) {
9254 if (errors) {
9255 Warning("BuildHistogram", "Skipping errors for remaining bins");
9256 errors = false;
9257 }
9258 fInterrupted = false;
9259 }
9260 }
9261 if (toy > 0) {
9262 h = main_h;
9263 }
9264 }
9265
9266 if (errors && !errorBins.empty()) {
9267 // Batched linear error propagation over all (in-range) bins recorded above. By varying each parameter only
9268 // once and sweeping the observable inside that variation, the normalization / expected-events integrals are
9269 // reused across bins instead of being recomputed for every bin (see new_getPropagatedErrors).
9270 std::unique_ptr<RooAbsReal> errFunc;
9271 RooArgSet emptyNormSet;
9272 if (p) {
9273 errFunc =
9274 std::make_unique<PdfWrapper>((oldrar) ? *rar : *p, _coefs.get<RooAbsReal>(), !v, oldrar ? p : nullptr);
9275 } else {
9276 // especially important not to pass a normSet in the case we are evaluating a RooRealSumPdf as a function!
9277 // otherwise the error will be wrong
9278 errFunc = std::make_unique<RooProduct>(
9279 "errorEval", "errorEval",
9280 RooArgList(*rar, !_coefs.get() ? RooFit::RooConst(1) : *_coefs.get<RooAbsReal>()));
9281 }
9282 const RooArgSet &errNormSet = p ? normSet : emptyNormSet;
9283
9284 auto setBin = [&](int i) {
9285 if (x) {
9286 x->setVal(h->GetBinCenter(i));
9287 } else if (cat) {
9288 cat->setLabel(h->GetXaxis()->GetBinLabel(i));
9289 } else if (v) {
9290 v->setBin(i - 1);
9291 }
9292 };
9293
9294 bool doAsym = (errorsHi && errorsLo);
9295 // For asymmetric bands we need both the Lo and Hi propagated errors; otherwise a single (possibly one-sided)
9296 // pass. errorPars is reused across both passes so the parameter list is only built once.
9297 std::vector<double> errLo = new_getPropagatedErrors(
9298 *errFunc, *fr, errNormSet, &errorPars, doAsym ? false : errorsHi, doAsym ? true : errorsLo, errorBins, setBin);
9299 std::vector<double> errHi;
9300 if (doAsym) {
9301 errHi = new_getPropagatedErrors(*errFunc, *fr, errNormSet, &errorPars, true, false, errorBins, setBin);
9302 }
9303#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
9304 if (p) {
9305 // improved normSet invalidity checking, so assuming no longer need this in 6.28 onwards
9306 p->_normSet = nullptr;
9307 }
9308#endif
9309
9310 for (size_t k = 0; k < errorBins.size(); k++) {
9311 int i = errorBins[k];
9312 double resLo = errLo[k];
9313 if (needBinWidth)
9314 resLo *= h->GetBinWidth(i);
9315 h->SetBinError(i, resLo);
9316 if (doAsym) {
9317 double resHi = errHi[k];
9318 if (needBinWidth)
9319 resHi *= h->GetBinWidth(i);
9320 // lowVal = content - resLo, highVal = content + resHi
9321 // => band/2 = (resHi+resLo)/2 and band-mid = content + (resHi-resLo)/2
9322 h->SetBinContent(i, h->GetBinContent(i) + (resHi - resLo) * 0.5);
9323 h->SetBinError(i, (resHi + resLo) * 0.5);
9324 }
9325 }
9326 }
9327
9328 if (gOldHandlerr) {
9329 signal(SIGINT, gOldHandlerr);
9330 gOldHandlerr = nullptr;
9331 }
9332 normSet = *snap;
9333
9334 if (errorPars) {
9335 if (errorParsSnap)
9336 *errorPars = *errorParsSnap;
9337 delete errorPars;
9338 }
9339 if (nErrorToys) {
9340 // compute main histogram error bar from toys
9341 // if not doing asymmetric, then will display std.dev
9342 // otherwise will copy main to nominal and make main error bar s.t. it shows +/-1sigma vals
9343 if (errorsLo && errorsHi) {
9344 auto nomHist = static_cast<TH1 *>(h->FindObject("nominal"));
9345 nomHist->Add(h);
9346 }
9347 for (int i = 1; i <= h->GetNbinsX(); i++) {
9348 std::vector<double> vals;
9349 vals.reserve(nErrorToys);
9350 for (int j = 1; j < (nErrorToys + 1); j++) {
9351 vals.push_back(
9352 static_cast<TH1 *>(h->FindObject("toys")->FindObject(TString::Format("toy_%d", j)))->GetBinContent(i));
9353 }
9354 double upVal, downVal;
9355 if (errorsLo || errorsHi) {
9356 std::sort(vals.begin(), vals.end());
9357 upVal = vals.at(std::floor(vals.size() * ROOT::Math::gaussian_cdf(1)));
9358 downVal = vals.at(std::floor(vals.size() * ROOT::Math::gaussian_cdf(-1)));
9359 if (!errorsLo)
9360 downVal = 2. * h->GetBinContent(i) - upVal;
9361 if (!errorsHi)
9362 upVal = 2. * h->GetBinContent(i) - downVal;
9363 } else {
9364 double err = TMath::StdDev(vals.begin(), vals.end());
9365 upVal = h->GetBinContent(i) + err;
9366 downVal = h->GetBinContent(i) - err;
9367 }
9368 h->SetBinContent(i, (upVal + downVal) * 0.5);
9369 h->SetBinError(i, (upVal - downVal) * 0.5);
9370 }
9371 }
9372#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 41, 00)
9373 // We evaluated the above with `normSet`, which lives on this function's stack.
9374 // RooFit proxies only cache a bare pointer to the last normSet and re-use it
9375 // later (e.g. RooProduct::evaluate reads back _compRSet.nset()), so before it
9376 // goes out of scope we must clear it everywhere it could have been cached,
9377 // otherwise a later evaluation dereferences freed stack ("use of uninitialised
9378 // value" in valgrind). sterilize() only walks clients, but getVal() propagates
9379 // the normSet to the servers of the evaluated function, so reset those here.
9380 // setProxyNormSet made public in 6.41
9381
9382 {
9383 RooArgSet evaluatedNodes;
9384 for (RooAbsArg *treeRoot : {static_cast<RooAbsArg *>(rar), static_cast<RooAbsArg *>(p),
9385 static_cast<RooAbsArg *>(oldrar), _coefs.get<RooAbsArg>()}) {
9386 if (treeRoot)
9387 treeRoot->treeNodeServerList(&evaluatedNodes);
9388 }
9389 for (RooAbsArg *node : evaluatedNodes) {
9390 node->setProxyNormSet(nullptr);
9391 }
9392 }
9393#endif
9394 if (oldrar) {
9395 std::vector<RooAbsArg *> extra;
9396 if (auto s = dynamic_cast<RooSimultaneous *>(rar)) {
9397 // need to delete all the subpdfs we created too
9398 for (auto _pdf : s->servers()) {
9399 if (dynamic_cast<RooAbsPdf *>(_pdf))
9400 extra.push_back(_pdf);
9401 }
9402 }
9403 extra.push_back(rar);
9404 rar = oldrar;
9405 xRooNode(*rar).sterilize(); // need to clear the cache of the created integral - do this before deleting things!
9406 for (auto a : extra)
9407 delete a;
9408 } else {
9409 sterilize(); // needed to forget about the normSet that was passed to getVal()
9410 }
9411
9412 if (!p && !rar->getAttribute("density") && !needBinWidth) {
9413 h->GetYaxis()->SetTitle(rar->getStringAttribute("units"));
9414 } else if ((p && p->canBeExtended()) || (!p && needBinWidth)) {
9415 h->GetYaxis()->SetTitle("Events");
9416 } else {
9417 h->GetYaxis()->SetTitle("Probability Mass");
9418 }
9419 h->GetYaxis()->SetMaxDigits(3);
9420
9421 if (errors) {
9422 delete fr;
9423 }
9424
9425 // build a stack unless not requested
9426 if (!nostack) {
9427 // need to draw copy of hist so shown over the stack
9428 auto hCopy = static_cast<TH1 *>(h->Clone("copy"));
9429 hCopy->Reset();
9430 hCopy->Add(h); // use Reset and Add to clear the function list (dont clear directly as may double-delete if same
9431 // object added twice)
9432 hCopy->SetStats(false);
9433 h->GetListOfFunctions()->Add(hCopy, TString(h->GetOption()) + "same");
9434 h->GetListOfFunctions()->Add(hCopy, "axissame"); // prevents stack covering axis
9435 TString dOpt = (setInterp) ? "LF2" : ""; // should become lf2 if interpolation of histogram is appropriate
9436
9437 const xRooNode *rarNode = this;
9438 RooAbsReal *sf = nullptr;
9439 if (get()->InheritsFrom("RooExtendPdf")) {
9440 const_cast<xRooNode *>(this)->browse();
9441 rarNode = find(".pdf").get();
9442 // rar = rarNode->get<RooAbsReal>();
9443 sf = find(".n")->get<RooAbsReal>();
9444 }
9445
9446 THStack *stack = new THStack("stack", TString::Format("%s;%s", rar->GetTitle(), h->GetXaxis()->GetTitle()));
9447 int count = 0;
9448 std::map<std::string, int> colorByTitle; // TODO: should fill from any existing legend
9449 std::set<std::string> allTitles;
9450 bool titleMatchName = true;
9451 std::map<std::string, TH1 *> histGroups;
9452 std::vector<TH1 *> hhs;
9453 std::set<std::pair<size_t, TH1 *>> ordered_hhs;
9454 std::set<TH1 *> histsWithBadTitles; // these histograms will have their titles autoFormatted
9455
9456 // support for CMS model case where has single component containing many coeffs
9457 // will build stack by setting each coeff equal to 0 in turn, rebuilding the histogram
9458 // the difference from the "full" histogram will be the component
9459 RooArgList cms_coefs;
9460 if (!rarNode->components().empty()) {
9461 auto comps = rarNode->components()[0];
9462 for (auto &c : *comps) {
9463 if (c->fFolder == "!.coeffs" || c->fFolder == "!.coeffpars")
9464 cms_coefs.add(*c->get<RooAbsArg>());
9465 }
9466 }
9467 if (!cms_coefs.empty()) {
9468 RooRealVar zero("zero", "", 0);
9469 std::shared_ptr<TH1> prevHist(static_cast<TH1 *>(h->Clone()));
9470 prevHist->Reset();
9471 prevHist->Add(h);
9472 // Nov25: discovered cms CMSHistSum has really heavy caching, that wont clear until I clone
9473 // i.e. I must first copy the function, then redirect the server, then copy that function
9474 // the copy of the copy will give the correct results, not the copy
9475 // This wasn't necessary for the CMSHistErrorPropagator class that I had before.
9476 std::unique_ptr<RooAbsReal> forig(
9477 dynamic_cast<RooAbsReal *>(rarNode->components()[0]->get()->Clone("tmpCopy0")));
9478 for (auto c : cms_coefs) {
9479 // seems I have to remake the function each time, as haven't figured out what cache needs clearing?
9480
9481 zero.setAttribute(Form("ORIGNAME:%s", c->GetName())); // used in redirectServers to say what this replaces
9482 forig->redirectServers(RooArgSet(zero), false, true); // each time will replace one additional coef
9483 std::unique_ptr<RooAbsReal> f(dynamic_cast<RooAbsReal *>(forig->Clone("tmpCopy")));
9484
9485 // zero.setAttribute(Form("ORIGNAME:%s",c->GetName()),false); (commented out so that on next iteration
9486 // will still replace all prev)
9487 auto hh = xRooNode(*f, *this).BuildHistogram(v);
9488 hh->SetName(c->GetName());
9489 if (sf)
9490 hh->Scale(sf->getVal());
9491 if (strlen(hh->GetTitle()) == 0) {
9492 hh->SetTitle(c->GetName()); // ensure all hists has titles
9493 // special case for CMS ... if find "_proc_" in the title, take whatever is after that
9494 auto idx = TString(hh->GetTitle()).Index("_proc_");
9495 if (idx >= 0) {
9496 hh->SetTitle(TString(TString(hh->GetTitle())(idx + 6, strlen(hh->GetTitle()))));
9497 }
9498 histsWithBadTitles.insert(hh);
9499 } else if (strcmp(hh->GetName(), hh->GetTitle()) == 0) {
9500 histsWithBadTitles.insert(hh);
9501 }
9502 titleMatchName &= (TString(c->GetName()) == hh->GetTitle() ||
9503 TString(hh->GetTitle()).BeginsWith(TString(c->GetName()) + "_"));
9504 std::shared_ptr<TH1> nextHist(static_cast<TH1 *>(hh->Clone()));
9505 hh->Add(prevHist.get(), -1.);
9506 hh->Scale(-1.);
9507 // remove the errors ... the above lines will have introduced errors
9508 hh->TH1::Reset("ICE"); // calling the base class method explicitly will only clear errors
9509 ordered_hhs.insert(std::pair(ordered_hhs.size(), hh));
9510 prevHist = nextHist;
9511 }
9512 } else if (get<RooSimultaneous>()) {
9513 // need to create a histogram for each sample across all the channels - will rely on functionality below to
9514 // merge them based on titles
9515
9516 for (auto &chan : bins()) {
9517 TString chanName(chan->GetName());
9518 chanName = chanName(chanName.Index("=") + 1, chanName.Length());
9519 auto samps = chan->mainChild();
9520 if (!samps)
9521 samps = *chan;
9522 for (auto &samp : samps.components()) {
9523 auto hh = static_cast<TH1 *>(h->Clone(samp->GetName()));
9524 hh->Reset();
9525 hh->SetTitle(samp->GetTitle());
9526 if (strlen(hh->GetTitle()) == 0) {
9527 hh->SetTitle(samp->GetName());
9528 histsWithBadTitles.insert(hh);
9529 } else if (strcmp(hh->GetName(), hh->GetTitle()) == 0) {
9530 histsWithBadTitles.insert(hh);
9531 }
9532 hh->SetTitle(TString(hh->GetTitle())
9533 .ReplaceAll(TString(chan->get()->GetName()) + "_",
9534 "")); // remove occurance of channelname_ in title (usually prefix)
9535 titleMatchName &= (TString(samp->GetName()) == hh->GetTitle() ||
9536 TString(hh->GetTitle()).BeginsWith(TString(samp->GetName()) + "_"));
9537 hh->SetBinContent(hh->GetXaxis()->FindFixBin(chanName), samp->GetContent());
9538 ordered_hhs.insert(std::pair(ordered_hhs.size(), hh));
9539 }
9540 }
9541 } else {
9542 for (auto &samp : rarNode->components()) {
9543 auto hh = samp->BuildHistogram(
9544 v, empty, false /* no errors for stack*/, binStart, binEnd, _fr, false, false, 0, h, true,
9545 setInterp); // passing h to ensure binning is the same for all subcomponent hists
9546 hh->SetName(samp->GetName());
9547 if (sf)
9548 hh->Scale(sf->getVal());
9549 ordered_hhs.insert(
9550 std::pair((samp->get<RooAbsArg>() && samp->get<RooAbsArg>()->getStringAttribute("StackOrder"))
9551 ? TString(samp->get<RooAbsArg>()->getStringAttribute("StackOrder")).Atoi()
9552 : ordered_hhs.size(),
9553 hh));
9554 if (strlen(hh->GetTitle()) == 0) {
9555 hh->SetTitle(samp->GetName()); // ensure all hists has titles
9556 histsWithBadTitles.insert(hh);
9557 } else if (strcmp(hh->GetName(), hh->GetTitle()) == 0) {
9558 histsWithBadTitles.insert(hh);
9559 }
9560 titleMatchName &= (TString(samp->GetName()) == hh->GetTitle() ||
9561 TString(hh->GetTitle()).BeginsWith(TString(samp->GetName()) + "_"));
9562 }
9563 }
9564
9565 // pull histograms in their order
9566 for (auto &[_, hh] : ordered_hhs) {
9567 hhs.push_back(hh);
9568 }
9569
9570 if (!hhs.empty()) {
9571 for (auto &hh : hhs) {
9572 allTitles.insert(hh->GetTitle());
9573 }
9574
9575 // get common prefix to strip off only if all titles match names and
9576 // any title is longer than 10 chars
9577 size_t e = std::min(allTitles.begin()->size(), allTitles.rbegin()->size());
9578 size_t ii = 0;
9579 bool goodPrefix = false;
9580 std::string commonSuffix;
9581 if (titleMatchName && hhs.size() > 1) {
9582 while (ii < e - 1 && allTitles.begin()->at(ii) == allTitles.rbegin()->at(ii)) {
9583 ii++;
9584 if (allTitles.begin()->at(ii) == '_' || allTitles.begin()->at(ii) == ' ')
9585 goodPrefix = true;
9586 }
9587
9588 // find common suffix if there is one .. must start with a "_"
9589 bool stop = false;
9590 while (!stop && commonSuffix.size() < size_t(e - 1)) {
9591 commonSuffix = allTitles.begin()->substr(allTitles.begin()->length() - commonSuffix.length() - 1);
9592 for (auto &tt : allTitles) {
9593 if (!TString(tt).EndsWith(commonSuffix.c_str())) {
9594 commonSuffix = commonSuffix.substr(1);
9595 stop = true;
9596 break;
9597 }
9598 }
9599 }
9600 if (commonSuffix.find('_') == std::string::npos) {
9601 commonSuffix = "";
9602 } else {
9603 commonSuffix = commonSuffix.substr(commonSuffix.find('_'));
9604 }
9605 }
9606 if (!goodPrefix)
9607 ii = 0;
9608
9609 // also find how many characters are needed to distinguish all entries (that dont have the same name)
9610 // then carry on up to first space or underscore
9611 size_t jj = 0;
9612 std::map<std::string, std::string> reducedTitles;
9613 while (reducedTitles.size() != allTitles.size()) {
9614 jj++;
9615 std::map<std::string, int> titlesMap;
9616 for (auto &s : allTitles) {
9617 if (reducedTitles.count(s))
9618 continue;
9619 titlesMap[s.substr(0, jj)]++;
9620 }
9621 for (auto &s : allTitles) {
9622 if (titlesMap[s.substr(0, jj)] == 1 && (jj >= s.length() || s.at(jj) == ' ' || s.at(jj) == '_')) {
9623 reducedTitles[s] = s.substr(0, jj);
9624 }
9625 }
9626 }
9627
9628 // strip common prefix and suffix before adding
9629 for (auto ritr = hhs.rbegin(); ritr != hhs.rend(); ++ritr) { // go in reverse order
9630 if (!histsWithBadTitles.count((*ritr))) {
9631 continue;
9632 }
9633 auto _title = (hhs.size() > 5) ? reducedTitles[(*ritr)->GetTitle()] : (*ritr)->GetTitle();
9634 _title = _title.substr(ii < _title.size() ? ii : 0);
9635 if (!commonSuffix.empty() && TString(_title).EndsWith(commonSuffix.c_str()))
9636 _title = _title.substr(0, _title.length() - commonSuffix.length());
9637 (*ritr)->SetTitle(_title.c_str());
9638 }
9639 }
9640
9641 for (auto &hh : hhs) {
9642 // automatically group hists that all have the same title
9643 if (histGroups.find(hh->GetTitle()) == histGroups.end()) {
9644 histGroups[hh->GetTitle()] = hh;
9645 } else {
9646 // add it into this group
9647 histGroups[hh->GetTitle()]->Add(hh);
9648 delete hh;
9649 hh = nullptr;
9650 continue;
9651 }
9652 auto hhMin = (hh->GetMinimum() == 0) ? hh->GetMinimum(1e-9) : hh->GetMinimum();
9653 if (!stack->GetHists() && h->GetMinimum() > hhMin) {
9654 auto newMin = hhMin - (h->GetMaximum() - hhMin) * gStyle->GetHistTopMargin();
9655 if (hhMin >= 0 && newMin < 0)
9656 newMin = hhMin * 0.99;
9657 // adjustYRange(newMin, h->GetMaximum());
9658 }
9659
9660 /*if(stack->GetHists() && stack->GetHists()->GetEntries()>0) {
9661 // to remove rounding effects on bin boundaries, see if binnings compatible
9662 auto _h1 = dynamic_cast<TH1*>(stack->GetHists()->At(0));
9663 if(_h1->GetNbinsX()==hh->GetNbinsX()) TODO ... finish dealing with silly rounding effects
9664 }*/
9665 TString thisOpt = TString(hh->GetOption()) == "l" ? "LF2" : ""; // need LF2 to get smooth line with fill
9666 // uncomment next line to blend continuous with discrete components .. get some unpleasant "poke through"
9667 // effects though
9668 // if(auto s = samp->get<RooAbsReal>(); s) thisOpt = s->isBinnedDistribution(*dynamic_cast<RooAbsArg*>(v)) ?
9669 // "" : "LF2";
9670 stack->Add(hh, thisOpt);
9671 }
9672 // stack->SetBit(kCanDelete); // should delete its sub histograms
9673 h->GetListOfFunctions()->AddFirst(stack, "noclear same");
9674 // stack->Draw("noclear same");
9675 // h->Draw(
9676 // dOpt + sOpt +
9677 // "same"); // overlay again .. if stack would cover original hist (negative components) we still see
9678 // integral
9679 // h->Draw("axissame"); // redraws axis
9680
9681 TList *ll = stack->GetHists();
9682 if (ll && ll->GetEntries()) {
9683
9684 // finally, ensure all hists are styled
9685 for (auto ho : *ll) {
9686 TH1 *hh = dynamic_cast<TH1 *>(ho);
9687 if (!hh)
9688 continue;
9689 bool createdStyle = (xRooNode(*hh, *this).styles(nullptr, false).get<TStyle>() == nullptr);
9690
9691 if (createdStyle) {
9692 // give hist a color, that isn't the same as any other hists color
9693 hh->SetFillStyle(1001); // solid fill style
9694 bool used = false;
9695 do {
9696 hh->SetFillColor(gEnv->GetValue("XRooFit.MinFillColor", kP10Blue /* was previously 2*/) + (count++));
9697 if (!gROOT->GetColor(hh->GetFillColor())) {
9698 // color doesn't exist, default it to transparent?
9699 hh->SetFillColor(0);
9700 }
9701 // check not already used this color
9702 used = false;
9703 for (auto ho2 : *ll) {
9704 TH1 *hh2 = dynamic_cast<TH1 *>(ho2);
9705 if (!hh2)
9706 continue;
9707 auto _styleNode = xRooNode(*hh2, *this).styles(hh2, false);
9708 auto _style = _styleNode.get<TStyle>();
9709 if (hh != hh2 && _style && _style->GetFillColor() == hh->GetFillColor()) {
9710 used = true;
9711 break;
9712 }
9713 }
9714 } while (used);
9715 }
9716
9717 auto _styleNode = xRooNode(*hh, *this).styles(hh);
9718 if (auto _style = _styleNode.get<TStyle>()) {
9719 *dynamic_cast<TAttLine *>(hh) = *_style;
9720 *dynamic_cast<TAttFill *>(hh) = *_style;
9721 *dynamic_cast<TAttMarker *>(hh) = *_style;
9722 }
9723 // for stacks, fill color of white should be color 10 unless fill style is 0
9724 if (hh->GetFillColor() == kWhite && hh->GetFillStyle() != 0) {
9725 // kWhite means 'transparent' in ROOT ... should really use a FillStyle of 0 for that
9726 // so assume user wanted actual white, which is color 10
9727 hh->SetFillColor(10);
9728 }
9729 }
9730 }
9731 }
9732
9733 return h;
9734}
9735
9736double xRooNode::GetBinData(int bin, const xRooNode &data)
9737{
9738 if (data.get<RooAbsData>()) {
9739 // attach as a child before calling datasets(), so that is included in the list
9740 push_back(std::make_shared<xRooNode>(data));
9741 }
9742 auto node = datasets().find(data.GetName());
9743 if (data.get<RooAbsData>()) {
9744 // remove the child we attached
9745 resize(size() - 1);
9746 }
9747 if (!node)
9748 return std::numeric_limits<double>::quiet_NaN();
9749 return node->GetBinContent(bin);
9750}
9751
9752std::vector<double> xRooNode::GetBinContents(int binStart, int binEnd) const
9753{
9754 if (fBinNumber != -1) {
9755 if (binStart != binEnd || !fParent) {
9756 throw std::runtime_error(TString::Format("%s is a bin - only has one value", GetName()));
9757 }
9758 return fParent->GetBinContents(fBinNumber, fBinNumber);
9759 }
9760 std::vector<double> out;
9761 if (get<RooAbsData>()) {
9762 auto g = BuildGraph(
9763 nullptr,
9764 (binStart != -1 ||
9765 binEnd != -1) /*include points for zeros unless we are asking for a single point with start=end=-1*/);
9766 if (!g) {
9767 return out;
9768 }
9769 if (binStart == binEnd && binStart == -1) {
9770 // integral over all bins if getting bin content -1
9771 double integral(0);
9772 for (int i = 0; i < g->GetN(); i++)
9773 integral += g->GetPointY(i);
9774 out.push_back(integral);
9775 delete g;
9776 return out;
9777 }
9778 for (int i = binStart - 1; i < g->GetN() && (binEnd == 0 || i < binEnd); i++) {
9779 out.push_back(g->GetPointY(i));
9780 }
9781 delete g;
9782 return out;
9783 }
9784
9785 bool doIntegral = false;
9786 if (binStart == binEnd && binStart == -1) {
9787 binStart = -1;
9788 binEnd = -1;
9789 doIntegral = true;
9790 } // return integral if request bin -1
9791 auto h = BuildHistogram(nullptr, false, false, binStart, binEnd);
9792 if (!h) {
9793 throw std::runtime_error(TString::Format("%s has no content", GetName()));
9794 }
9795 if (binEnd == 0) {
9796 binEnd = h->GetNbinsX();
9797 }
9798 if (doIntegral) {
9799 double tot = 0;
9800 for (int i = 1; i <= h->GetNbinsX(); i++) {
9801 tot += h->GetBinContent(i);
9802 }
9803 out.push_back(tot);
9804 } else {
9805 for (int i = binStart; i <= binEnd; i++) {
9806 out.push_back(h->GetBinContent(i));
9807 }
9808 }
9809 delete h;
9810 return out;
9811}
9812
9814{
9815 if (auto a = get<RooAbsArg>(); a) {
9816 // go through servers looking for 'main' thing
9817 for (auto &l : a->servers()) {
9818 if (l->getAttribute("MAIN_MEASUREMENT") || l->InheritsFrom("RooRealSumPdf") || l->InheritsFrom("RooAddPdf")) {
9819 return xRooNode(*l, *this);
9820 }
9821 }
9822 // the main child of a RooProduct is one that has the same name (/alias) as the product (except if is a bin
9823 // factor)
9824 if (a->IsA() == RooProduct::Class() && fBinNumber == -1) {
9825 for (auto &l : factors()) {
9826 if (strcmp(l->GetName(), GetName()) == 0) {
9827 return *l;
9828 }
9829 }
9830 }
9831 }
9832 return xRooNode();
9833}
9834
9836{
9837 if (auto o = get(); o) {
9838 o->Inspect();
9839 } else {
9841 }
9842}
9843
9844bool TopRightPlaceBox(TPad *p, TObject *o, double w, double h, double &xl, double &yb)
9845{
9846#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
9847 // reinitialize collide grid because the filling depends on fUxmin and fUxmax (and ymin ymax too)
9848 // and these aren't filled on the first time we do the placement (they init to 0 and 1), but will be filled
9849 // subsequently
9850 for (int i = 0; i < p->fCGnx; i++) {
9851 for (int j = 0; j < p->fCGny; j++) {
9852 p->fCollideGrid[i + j * p->fCGnx] = true;
9853 }
9854 }
9855 p->FillCollideGrid(o);
9856 Int_t iw = (int)(p->fCGnx * w);
9857 Int_t ih = (int)(p->fCGny * h);
9858
9859 Int_t nxmax = p->fCGnx - iw - 1 - p->fCGnx * p->GetRightMargin();
9860 Int_t nymax = p->fCGny - ih - 1 - p->fCGny * p->GetTopMargin();
9861
9862 for (Int_t j = nymax; j >= 0; j--) {
9863 for (Int_t i = nxmax; i >= 0; i--) {
9864 if (p->Collide(i, j, iw, ih)) {
9865 continue;
9866 } else {
9867 xl = (double)(i) / (double)(p->fCGnx);
9868 yb = (double)(j) / (double)(p->fCGny);
9869 return true;
9870 }
9871 }
9872 }
9873 return false;
9874#else
9875 return p->PlaceBox(o, w, h, xl, yb, "trw");
9876#endif
9877}
9878
9879TPaveText *getPave(const char *name = "labels", bool create = true, bool doPaint = false)
9880{
9881 if (auto p = dynamic_cast<TPaveText *>(gPad->GetPrimitive(name)); p) {
9882 if (doPaint)
9883 gPad->PaintModified(); //-- slows down x11 so trying to avoid
9884 return p;
9885 }
9886 if (!create) {
9887 return nullptr;
9888 }
9889 auto l = new TPaveText(gPad->GetLeftMargin() + 0.02, 1. - gPad->GetTopMargin() - 0.08, 0.6,
9890 1. - gPad->GetTopMargin() - 0.08);
9891 l->SetBorderSize(0);
9892 if (l->GetTextSize() == 0)
9893 l->SetTextSize(gStyle->GetTitleYSize());
9894
9896 // l->SetMargin(0);
9897 l->SetFillStyle(0);
9898 l->SetName(name);
9899 l->Draw();
9900 l->ConvertNDCtoPad();
9901 return l;
9902}
9903
9904TLegend *getLegend(bool create = true, bool doPaint = false)
9905{
9906 if (auto p = dynamic_cast<TLegend *>(gPad->GetPrimitive("legend")); p) {
9907 double x;
9908 double y;
9909 double w = p->GetX2NDC() - p->GetX1NDC();
9910 double h = p->GetY2NDC() - p->GetY1NDC();
9911 if (doPaint)
9912 gPad->PaintModified(); //-- slows down x11 so trying to avoid
9913 if (TopRightPlaceBox(dynamic_cast<TPad *>(gPad), p, w, h, x, y)) {
9914 // squash inside the frame ..
9915 // std::cout << gPad->GetName() << ":" << x << " , " << y << " , " << w << " , " << h << std::endl;
9916 x = std::max(x, (gPad->GetLeftMargin() + 0.02));
9917 y = std::max(y, (gPad->GetBottomMargin() + 0.02));
9918 x = std::min(x, (1. - gPad->GetRightMargin() - 0.02) - w);
9919 y = std::min(y, (1. - gPad->GetTopMargin() - 0.02) - h);
9920 h = std::min(h, (1. - gPad->GetTopMargin() - 0.02) - y);
9921 w = std::min(w, (1. - gPad->GetRightMargin() - 0.02) - x);
9922 // std::cout << gPad->GetName() << ":" << x << " , " << y << " , " << h << " , " << w << std::endl;
9923 p->SetX1NDC(x);
9924 p->SetY1NDC(y);
9925 p->SetX2NDC(x + w);
9926 p->SetY2NDC(y + h);
9927 gPad->Modified();
9928 }
9929 return p;
9930 }
9931 // look for a parent pad called 'legend' and create it there if existing
9932 auto p = gPad;
9933 while ((p != p->GetMother()) && (p = p->GetMother())) {
9934 if (auto q = dynamic_cast<TVirtualPad *>(p->GetPrimitive("legend")); q) {
9935 q->Modified();
9936 p = q;
9937 break;
9938 }
9939 }
9940 auto tmpPad = gPad;
9941 TLegend *l = nullptr;
9942 if (p && strcmp(p->GetName(), "legend") == 0) {
9943 if (l = dynamic_cast<TLegend *>(p->GetPrimitive("legend")); l || !create)
9944 return l;
9945 p->cd();
9946 l = new TLegend(gPad->GetLeftMargin(), 1. - gPad->GetTopMargin(), 1. - gPad->GetRightMargin(),
9947 gPad->GetBottomMargin());
9948 l->SetBorderSize(1); // ensure has a border
9949 } else {
9950 if (!create)
9951 return nullptr;
9952 l = new TLegend(0.6, 1. - gPad->GetTopMargin() - 0.08, 0.75, 1. - gPad->GetTopMargin() - 0.08);
9953 l->SetBorderSize(0);
9954 // legend text will be required to match y-axis
9955 if (l->GetTextSize() == 0) {
9956 l->SetTextSize(gStyle->GetTitleYSize());
9957 l->SetTextFont(gStyle->GetTitleFont("Y"));
9958 }
9959 }
9961 // l->SetMargin(0);
9962 l->SetFillStyle(0);
9963 l->SetName("legend");
9964 l->Draw();
9965 l->ConvertNDCtoPad();
9966 tmpPad->cd();
9967 return l;
9968}
9969
9970std::string formatLegendString(const std::string &s)
9971{
9972 auto i = s.find("\n");
9973 if (i == std::string::npos) {
9974 return s;
9975 }
9976 return std::string("#splitline{") + s.substr(0, i) + "}{" + formatLegendString(s.substr(i + 1)) + "}";
9977}
9978
9979void addLegendEntry(TObject *o, const char *title, const char *opt)
9980{
9981 auto l = getLegend();
9982 if (!l)
9983 return;
9984 // check for entry already existing with same title
9985 for (auto a : *l->GetListOfPrimitives()) {
9986 if (formatLegendString(title) == dynamic_cast<TLegendEntry *>(a)->GetLabel())
9987 return;
9988 }
9989 if (l->GetListOfPrimitives()->GetEntries() > 20)
9990 return; // todo: create an 'other' entry?
9991
9992 auto e = l->AddEntry(o, formatLegendString(title).c_str(), opt);
9993 // move to top of the legend (we add things in at the top)
9994 l->GetListOfPrimitives()->RemoveLast();
9995 l->GetListOfPrimitives()->AddFirst(e);
9996 if (auto nObj = l->GetListOfPrimitives()->GetEntries(); nObj > 0) {
9997 // each entry takes up 0.05 ... maximum of N*(N+4) (where N is # cols) before next column
9998 int nn = l->GetNColumns();
9999 nn *= (nn + 4);
10000 if (nObj > 1 && (nObj % nn) == 1) {
10001 l->SetNColumns(l->GetNColumns() + 1);
10002 if (l->GetBorderSize() == 0) {
10003 l->SetX1NDC(l->GetX2NDC() - 0.15 * l->GetNColumns());
10004 }
10005 }
10006 if (l->GetBorderSize() == 0) {
10007 l->SetY1NDC(l->GetY2NDC() - 0.05 * gPad->GetHNDC() * std::ceil((double(nObj) / l->GetNColumns())));
10008 }
10009 }
10010
10011 getLegend(); // to mark modified
10012}
10013
10014// this exists to avoid calling update excessively because it slows down x11 ... but still
10015// need to call update twice if have a legend drawn in order to relocate it.
10017public:
10018 PadRefresher(TVirtualPad *p) : fPad(p) { nExisting++; }
10020 {
10021 if (fPad) {
10022 getLegend(false, true);
10023 fPad->GetCanvas()->Paint();
10024 fPad->GetCanvas()->Update();
10025#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 30, 00)
10026 fPad->GetCanvas()->ResetUpdated(); // stops previous canvas being replaced in a jupyter notebook
10027#endif
10028 fPad->cd();
10029 }
10030 nExisting--;
10031 }
10032 TVirtualPad *fPad = nullptr;
10033 static int nExisting;
10034};
10035
10037
10039{
10040 // in order to catch exceptions to prevent crash of GUI, do this:
10041 if (gROOT->FromPopUp()) {
10042 gROOT->SetFromPopUp(false);
10043 try {
10044 Draw(opt);
10045 } catch (const std::exception &e) {
10046 new TGMsgBox(
10047 gClient->GetRoot(),
10048 (gROOT->GetListOfBrowsers()->At(0))
10049 ? dynamic_cast<TGWindow *>(static_cast<TBrowser *>(gROOT->GetListOfBrowsers()->At(0))->GetBrowserImp())
10050 : gClient->GetRoot(),
10051 "Exception", e.what(),
10052 kMBIconExclamation); // deletes self on dismiss?
10053 }
10054 gROOT->SetFromPopUp(true);
10055 return;
10056 }
10057
10058 TString sOpt2(opt);
10059 sOpt2.ToLower();
10060 if (!get() && !IsFolder() && !sOpt2.Contains("x="))
10061 return;
10062
10063 if (auto mc = get<RooStats::ModelConfig>()) {
10064 xRooNode(*mc->GetPdf(), fParent).Draw(opt); // draw the pdf of the config
10065 } else if (auto ir = get<RooStats::HypoTestInverterResult>()) {
10066 xRooHypoSpace(ir).Draw(opt);
10068 return;
10069 } else if (get<RooStats::HypoTestResult>()) {
10070 if (gPad)
10071 gPad->Clear();
10072 xRooNLLVar::xRooHypoPoint(std::dynamic_pointer_cast<RooStats::HypoTestResult>(fComp)).Draw(opt);
10073 {
10074 PadRefresher p(gPad); // refreshes the pad
10075 }
10077 return;
10078 }
10079
10080 if (sOpt2 == "pcls" && get<RooRealVar>() && fParent && fParent->get<RooAbsPdf>()) {
10081 // use the first selected dataset
10082 auto _dsets = fParent->datasets();
10083 // bool _drawn=false;
10084 TString dsetName = "";
10085 for (auto &d : _dsets) {
10086 if (d->get()->TestBit(1 << 20)) {
10087 dsetName = d->get()->GetName();
10088 break;
10089 }
10090 }
10091 auto hs = fParent->nll(dsetName.Data()).hypoSpace(get<RooRealVar>()->GetName());
10092 hs.limits("cls visualize");
10093 hs.SetName(TUUID().AsString());
10094 if (ws()) {
10095 ws()->import(*hs.result());
10096 }
10097 return;
10098 }
10099
10100 if (auxFunctions.empty()) {
10101 // add the defaults: Ratio and Signif
10103 "Ratio",
10104 [](double a, double b, double) {
10105 if (a == 0)
10106 return 0.;
10107 if (b == 0 && a == 0)
10108 return 1.;
10109 return a / b;
10110 },
10111 true);
10113 "Signif",
10114 [](double n, double b, double sigma) {
10115 double t0 = 0;
10116 if (sigma <= 0.) {
10117 // use simplified expression ...
10118 t0 = 2. * (((n == 0) ? 0 : n * log(n / b)) - (n - b));
10119 } else {
10120 double sigma2 = sigma * sigma;
10121 double b_hathat = 0.5 * (b - sigma2 + sqrt(pow(b - sigma2, 2) + 4 * n * sigma2));
10122 // double s_hat = n - m;
10123 // double b_hat = m;
10124 t0 = 2. * (((n == 0) ? 0 : n * log(n / b_hathat)) + b_hathat - n + pow(b - b_hathat, 2) / (2. * sigma2));
10125 }
10126 if (t0 < 0)
10127 return 0.; // can happen from numerical precision
10128 return (n >= b) ? sqrt(t0) : -sqrt(t0);
10129 },
10130 false);
10131 }
10132
10133 TString sOpt(opt);
10134
10135 RooAbsLValue *v = nullptr;
10136 std::vector<double> xPoints;
10137 if (sOpt2.Contains("x=")) {
10138 // specifying a particular var to scan over ...
10139 int _idx = sOpt2.Index("x=");
10140 int _eidx = sOpt2.Index(';', _idx);
10141 TString varPart = sOpt(_idx + 2, (_eidx < 0 ? sOpt2.Length() : _eidx) - (_idx + 2));
10142 TString varName = varPart;
10143 // if varName is of form str(num,num,num) then can infer scan points
10144 if (auto _idx2 = varPart.Index("("); _idx2 > 0) {
10145 varName = varPart(0, _idx2);
10146 TStringToken pattern(TString(varPart(_idx2 + 1, varPart.Length() - _idx2 - 2)), ",");
10147 double min(0);
10148 double max(0);
10149 int nBins = 0;
10150 int ii = 0;
10151 while (pattern.NextToken()) {
10152 TString s = pattern;
10153 if (ii == 0) {
10154 nBins = s.Atoi();
10155 } else if (ii == 1) {
10156 min = s.Atof();
10157 } else if (ii == 2) {
10158 max = s.Atof();
10159 }
10160 ii++;
10161 }
10162 if (nBins > 100)
10163 nBins = 100; // limit scanning to 100 points
10164 if (nBins > 1) {
10165 for (double x = min; x <= max; x += (max - min) / (nBins - 1)) {
10166 xPoints.push_back(x);
10167 }
10168 } else if (nBins == 1)
10169 xPoints.push_back((min + max) / 2.);
10170 }
10171 v = getObject<RooAbsLValue>(varName.Data()).get();
10172 if (!v) {
10173 throw std::runtime_error(TString::Format("Could not find variable %s", varName.Data()));
10174 }
10175 if (xPoints.empty() && !obs().find(varName.Data()) &&
10176 dynamic_cast<RooAbsRealLValue *>(v)) { // will draw obs as regular (e.g. hist)
10177 double tmp = static_cast<RooAbsRealLValue *>(v)->getVal();
10178 for (int i = 0; i < v->numBins(GetName()); i++) {
10179 v->setBin(i, GetName());
10180 xPoints.push_back(static_cast<RooAbsRealLValue *>(v)->getVal());
10181 }
10182 static_cast<RooAbsRealLValue *>(v)->setVal(tmp);
10183 }
10184 sOpt2 = TString(sOpt2(0, _idx)) + sOpt2(_idx + 2 + varPart.Length() + 1, sOpt2.Length());
10185 sOpt = TString(sOpt(0, _idx)) + sOpt(_idx + 2 + varPart.Length() + 1, sOpt.Length());
10186 }
10187 TString forceNames = "";
10188 if (sOpt2.Contains("force")) {
10189 // force plots show how much NLL changes wrt to a change of variables
10190 if (get<RooRealVar>() && fParent && fParent->get<RooAbsPdf>()) {
10191 // assume want force of this parameter from the parent pdf
10192 TString ff = sOpt(sOpt2.Index("force"), sOpt2.Index("force") + 5);
10193 sOpt.ReplaceAll(ff, TString::Format("force%s", get()->GetName()));
10194 fParent->Draw(sOpt);
10195 return;
10196 } else if (get<RooAbsPdf>()) {
10197 // extract the parameter(s) to calculate force for
10198 forceNames = sOpt(sOpt2.Index("force") + 5, sOpt2.Length());
10199 sOpt = sOpt(0, sOpt2.Index("force"));
10200 sOpt2 = sOpt2(0, sOpt2.Index("force"));
10201 } else {
10202 Error("Draw", "Can only compute forces with PDFs");
10203 return; // don't throw because will cause browser to exit if done from there
10204 }
10205 }
10206 bool hasOverlay = sOpt2.Contains("overlay");
10207 TString overlayName = "";
10208 if (hasOverlay) {
10209 // whatever follows overlay is the variation name
10210 overlayName = sOpt(sOpt2.Index("overlay") + 7, sOpt2.Length());
10211 sOpt = sOpt(0, sOpt2.Index("overlay"));
10212 sOpt2 = sOpt2(0, sOpt2.Index("overlay"));
10213 }
10214 if (sOpt2.Contains("ratio") && !sOpt2.Contains("auxratio"))
10215 sOpt += "auxRatio";
10216 if (sOpt2.Contains("significance") && !sOpt2.Contains("auxsignif"))
10217 sOpt += "auxSignif";
10218
10219 std::string auxPlotTitle;
10220 for (auto &[k, _] : auxFunctions) {
10221 if (sOpt.Contains(TString::Format("aux%s", k.c_str()))) {
10222 auxPlotTitle = k;
10223 }
10224 sOpt.ReplaceAll(TString::Format("aux%s", k.c_str()), "");
10225 }
10226
10227 sOpt.ToLower();
10228 sOpt.ReplaceAll("ratio", "");
10229 sOpt.ReplaceAll("significance", ""); // remove old option if still given
10230 bool nostack = sOpt.Contains("nostack");
10231 sOpt.ReplaceAll("nostack", "");
10232 bool hasSame = sOpt.Contains("same");
10233 sOpt.ReplaceAll("same", "");
10234 bool hasGoff = sOpt.Contains("goff");
10235 sOpt.ReplaceAll("goff", "");
10236 bool hasFR = sOpt.Contains("pull") && !get<RooFitResult>();
10237 sOpt.ReplaceAll("pull", "");
10238 bool hasText = sOpt.Contains("text");
10239 bool hasTexte = sOpt.Contains("texte");
10240 bool hasErrorOpt = sOpt.Contains("e");
10241 sOpt.ReplaceAll("e", "");
10242 if (hasTexte) {
10243 sOpt.ReplaceAll("txt", "texte");
10244 } else if (hasText) {
10245 sOpt.ReplaceAll("txt", "text");
10246 }
10247 if (auxPlotTitle == "Signif")
10248 hasErrorOpt = true; // must calculate error to calculate significance
10249 if (hasOverlay)
10250 hasSame = true; // when overlaying must be putting on same
10251
10252 TVirtualPad *pad = gPad;
10253
10254 TH1 *hAxis = nullptr;
10255
10256 auto clearPad = []() {
10257 gPad->Clear();
10258 if (gPad->GetNumber() == 0) {
10259 gPad->SetBottomMargin(gStyle->GetPadBottomMargin());
10260 gPad->SetTopMargin(gStyle->GetPadTopMargin());
10261 gPad->SetLeftMargin(gStyle->GetPadLeftMargin());
10262 gPad->SetRightMargin(gStyle->GetPadRightMargin());
10263 }
10264 // if (gPad == gPad->GetCanvas()) {
10265 // gPad->GetCanvas()->SetCanvasSize( gPad->GetCanvas()->GetWindowWidth() - 4,
10266 // gPad->GetCanvas()->GetWindowHeight() - 28 );
10267 // }
10268 };
10269
10270 if (!hasSame || !pad) {
10271 if (!pad) {
10273 pad = gPad;
10274 }
10275
10276 } else {
10277 // get the histogram representing the axes
10278 hAxis = dynamic_cast<TH1 *>(pad->GetPrimitive("axis"));
10279 if (!hAxis) {
10280 for (auto o : *pad->GetListOfPrimitives()) {
10281 if (hAxis = dynamic_cast<TH1 *>(o); hAxis)
10282 break;
10283 }
10284 }
10285 if (hAxis && !v) {
10286 v = getObject<RooAbsLValue>(hAxis->GetXaxis()->IsAlphanumeric() ? hAxis->GetXaxis()->GetTimeFormatOnly()
10287 : hAxis->GetXaxis()->GetName())
10288 .get();
10289 }
10290 }
10291
10292 if (!hasSame) {
10293 if (gPad != gPad->GetCanvas()) {
10294 gPad->SetName(GetName()); // only rename the pad if its not the parent canvas
10295 }
10296 gPad->SetTitle(GetTitle());
10297 }
10298
10299 PadRefresher padRefresh(((!hasSame || hasOverlay || PadRefresher::nExisting == 0) && !hasGoff) ? gPad : nullptr);
10300
10301 auto adjustYRange = [&](double min, double max, TH1 *hh = nullptr, bool symmetrize = false) {
10302 if (!hh)
10303 hh = hAxis;
10304 // give max and min a buffer ...
10305 max += gStyle->GetHistTopMargin() * (max - min);
10306 if (min > 0)
10307 min = std::max(min * 0.9, min - gStyle->GetHistTopMargin() * (max - min));
10308 if (hh) {
10309 double ymin = hh->GetMinimum();
10310 double ymax = hh->GetMaximum();
10311 if (hh->GetMaximumStored() == -1111)
10312 ymax += gStyle->GetHistTopMargin() * (ymax - ymin);
10313 if (hh->GetMinimumStored() == -1111) {
10314 if (gStyle->GetHistMinimumZero() && ymax >= 0) {
10315 ymin = 0;
10316 } else if (ymin < 0) {
10317 ymin -= gStyle->GetHistTopMargin() * (ymax - ymin);
10318 } else {
10319 ymin = std::max(ymin * 0.9, ymin - gStyle->GetHistTopMargin() * (ymax - ymin));
10320 }
10321 // see TGLPlotPainter to complete the mimic, but we leave off here truncating @ 0 if ymax>0
10322 }
10323 // make ymax at least 3x bigger than biggest error if has error
10324 if (hh->GetSumw2()) {
10325 double smallestErrDown3 = -std::numeric_limits<double>::infinity();
10326 double smallestErrUp3 = std::numeric_limits<double>::infinity();
10327 for (int i = 1; i <= hh->GetNbinsX(); i++) {
10328 smallestErrDown3 = std::max(smallestErrDown3, hh->GetBinContent(i) - 3 * hh->GetBinError(i));
10329 smallestErrUp3 = std::min(smallestErrUp3, hh->GetBinContent(i) + 3 * hh->GetBinError(i));
10330 }
10331 max = std::max(max, smallestErrUp3);
10332 min = std::min(min, smallestErrDown3);
10333 }
10334 bool change = false;
10335 if (min < ymin) {
10336 ymin = min;
10337 change = true;
10338 }
10339 if (max > ymax) {
10340 ymax = max;
10341 change = true;
10342 }
10343 if (change) {
10344 // note: unfortunately when user 'unzooms' y axis it resets stored minimum to -1111, so lose range
10345 if (symmetrize) {
10346 double down = hh->GetBinContent(1) - ymin;
10347 double up = ymax - hh->GetBinContent(1);
10348 if (down > up) {
10349 ymax = hh->GetBinContent(1) + down;
10350 } else {
10351 ymin = hh->GetBinContent(1) - up;
10352 }
10353 }
10354 if (hh == hAxis && pad && !pad->GetLogy() && ymin > 0 && (log10(ymax) - log10(max)) >= 3) {
10355 // auto-log the pad
10356 pad->SetLogy();
10357 }
10358 if (hh == hAxis && pad && ymin == 0 && pad->GetLogy()) {
10359 ymin = 1e-2;
10360 }
10361 if (ymin == 0 && ymax > 10)
10362 ymin = 0.1; // adjust min so if user activates log scale it isn't bad
10363 hh->SetMinimum(ymin);
10364 hh->SetMaximum(ymax);
10365 hh->GetYaxis()->Set(1, ymin, ymax);
10366 hh->SetAxisRange(ymin, ymax, "Y");
10367 }
10368 }
10369 };
10370
10371 auto graphMinMax = [](TGraphAsymmErrors *gr) {
10372 double ymax = -std::numeric_limits<double>::infinity();
10373 double ymin = std::numeric_limits<double>::infinity();
10374 for (int i = 0; i < gr->GetN(); i++) {
10375 ymax = std::max(ymax, gr->GetPointY(i) + gr->GetErrorYhigh(i));
10376 ymin = std::min(ymin, gr->GetPointY(i) - gr->GetErrorYlow(i));
10377 }
10378 return std::make_pair(ymin, ymax);
10379 };
10380
10381 if (!xPoints.empty()) {
10382 // create a graph using GetContent
10384 out->SetName(GetName());
10385 out->SetTitle(GetTitle());
10386 out->SetFillColor(out->GetLineColor());
10387 out->SetMarkerStyle(0);
10388 out->SetFillStyle(hasErrorOpt ? 3005 : 0);
10389 double tmp = static_cast<RooAbsRealLValue *>(v)->getVal();
10390 for (auto &x : xPoints) {
10391 static_cast<RooAbsRealLValue *>(v)->setVal(x);
10392 out->AddPoint(x, GetContent());
10393 if (hasErrorOpt) {
10394 out->SetPointEYlow(out->GetN() - 1, GetError());
10395 out->SetPointEYhigh(out->GetN() - 1, out->GetErrorYlow(out->GetN() - 1)); // symmetric error for now
10396 }
10397 }
10398 static_cast<RooAbsRealLValue *>(v)->setVal(tmp);
10399 out->GetHistogram()->GetXaxis()->SetTitle(static_cast<RooAbsRealLValue *>(v)->GetTitle());
10400 out->SetBit(kCanDelete);
10401 out->Draw(TString(hasSame ? "L" : "AL") + (hasErrorOpt ? "3" : ""));
10402 return;
10403 }
10404
10405 if (hasFR) {
10406 // drawing the fitresult as a pull plot on a subpad, and rest of the draw elsewhere
10407 clearPad();
10408 pad->Divide(1, 2, 1e-9, 1e-9); //,0,0);
10409 pad->GetPad(1)->SetPad(0, 0.2, 1, 1);
10410 pad->GetPad(2)->SetPad(0, 0, 1, 0.2);
10411 TString optNoFR(opt);
10412 optNoFR.ReplaceAll("pull", "");
10413 pad->cd(1);
10414 Draw(optNoFR);
10415 pad->cd(2);
10416 auto _fr = fitResult();
10417 _fr.Draw();
10418 // switch into subpad
10419 gPad->cd(1);
10420 gPad->SetFillColor(kGray);
10421 gPad->GetFrame()->SetFillColor(kWhite);
10422 gPad->GetFrame()->SetFillStyle(1001);
10423 gPad->SetTopMargin(0);
10424 gPad->SetBottomMargin(0);
10425 gPad->SetName("pull");
10426 // split the pull graph into individual points -- for benefit of GUI status bar
10427 auto pullGraph = dynamic_cast<TGraphAsymmErrors *>(gPad->GetPrimitive("pulls"));
10428 if (!pullGraph) {
10429 Error("Draw", "Couldn't find pull graph");
10430 return;
10431 }
10432 pullGraph->SetName("nominal");
10433 TMultiGraph *mg = new TMultiGraph;
10434 mg->SetName("editables");
10435
10436 auto scaleHist = static_cast<TH1 *>(pullGraph->FindObject("scales"));
10437 if (!scaleHist)
10438 throw std::runtime_error("Could not find scales in fit result");
10439
10440 for (auto i = 0; i < pullGraph->GetN(); i++) {
10441 auto g = new TGraphAsymmErrors;
10442 g->SetName(scaleHist->GetXaxis()->GetBinLabel(i + 1));
10443 auto _p = dynamic_cast<RooRealVar *>(_fr.get<RooFitResult>()->floatParsFinal().find(g->GetName()));
10444 if (!_p) {
10445 Warning("Draw", "Found a non-var in the floatParsFinal list: %s - this shouldn't happen", g->GetName());
10446 continue;
10447 }
10448 g->SetTitle(TString::Format(
10449 "%s=%g +/- %s [%g,%g]", strlen(_p->GetTitle()) ? _p->GetTitle() : _p->GetName(), _p->getVal(),
10450 _p->hasAsymError() ? TString::Format("(%g,%g)", _p->getAsymErrorHi(), _p->getAsymErrorLo()).Data()
10451 : TString::Format("%g", _p->getError()).Data(),
10452 scaleHist->GetBinContent(i + 1), scaleHist->GetBinError(i + 1)));
10453 g->SetPoint(0, pullGraph->GetPointX(i), pullGraph->GetPointY(i));
10454 g->SetPointEYhigh(0, pullGraph->GetErrorYhigh(i));
10455 g->SetPointEYlow(0, pullGraph->GetErrorYlow(i));
10456 g->SetEditable(true);
10457 g->SetHighlight(true);
10458 g->SetMarkerStyle(20);
10459 g->SetMarkerSize(0.5);
10460 mg->Add(g);
10461 }
10462 // gPad->GetListOfPrimitives()->Remove(pullGraph); delete pullGraph;
10463 mg->Draw("z0p");
10464 mg->SetBit(kCanDelete);
10465 auto _thisClone = new xRooNode("node", fComp, fParent);
10466 _thisClone->SetBit(kCanDelete);
10467 _thisClone->AppendPad();
10468
10469 // ensure statusbar visible for interactive plot
10470 // turned this off for now ... as not needed if doing through browser, status bar already there
10471 // if (gPad->GetCanvas() && !gPad->GetCanvas()->TestBit(TCanvas::kShowEventStatus)) {
10472 // gPad->GetCanvas()->ToggleEventStatus();
10473 // }
10474 gPad->AddExec("interactivePull", TString::Format("%s::Interactive_Pull()", ClassName()));
10475
10476 pad->cd();
10477 return;
10478 }
10479
10480 if (auto _simPdf = get<RooSimultaneous>();
10481 _simPdf && !(v && strcmp(_simPdf->indexCat().GetName(), dynamic_cast<TObject *>(v)->GetName()) == 0)) {
10482 auto _channels = bins();
10483 int _size = 0;
10484 for (auto &_v : _channels) {
10485 if (!_v->IsHidden())
10486 _size++;
10487 }
10488 if (!hasSame) {
10489 if (_size > 4) {
10490 // add a pad for the common legends
10491 _size++;
10492 }
10493 clearPad();
10494 pad->SetBorderSize(0);
10495 // if (pad->GetCanvas() == pad) {
10496 // if(_size>4) {
10497 // int n = _size;
10498 // Int_t w = 1, h = 1;
10499 // if (pad->GetCanvas()->GetWindowWidth() > pad->GetCanvas()->GetWindowHeight()) {
10500 // w = TMath::Ceil(TMath::Sqrt(n));
10501 // h = TMath::Floor(TMath::Sqrt(n));
10502 // if (w*h < n) w++;
10503 // } else {
10504 // h = TMath::Ceil(TMath::Sqrt(n));
10505 // w = TMath::Floor(TMath::Sqrt(n));
10506 // if (w*h < n) h++;
10507 // }
10508 // // adjust the window size to display only 4 in the window, with scroll bars
10509 // pad->GetCanvas()->SetCanvasSize( w*((pad->GetCanvas()->GetWindowWidth()-4)/2.) -16
10510 // ,h*((pad->GetCanvas()->GetWindowHeight()-28)/2.) - 16 );
10511 // } else {
10512 // //pad->GetCanvas()->Set(
10513 // w*(pad->GetCanvas()->GetWindowWidth()/2.),h*(pad->GetCanvas()->GetWindowHeight()/2.)) )
10514 // }
10515 // }
10516 int ncols = _simPdf->getStringAttribute("ncols") ? TString(_simPdf->getStringAttribute("ncols")).Atoi() : 0;
10517 if (ncols > 0) {
10518 dynamic_cast<TPad *>(pad)->Divide(ncols, std::ceil(double(_size) / ncols), 1e-9, 1e-9);
10519 } else {
10520 dynamic_cast<TPad *>(pad)->DivideSquare(_size, 1e-9, 1e-9);
10521 }
10522 if (_size > 5) {
10523 auto _pad = pad->GetPad(_size); // will use as the legend pad
10524 _pad->SetName("legend");
10525 // stretch the pad all the way to the left
10526 _pad->SetPad(_pad->GetXlowNDC(), _pad->GetYlowNDC(), 1.0, _pad->GetYlowNDC() + _pad->GetHNDC());
10527 // and make all the remaining pads transparent
10528 int x = _size;
10529 while (pad->GetPad(x + 1)) {
10530 pad->GetPad(x + 1)->SetFillStyle(0);
10531 x++;
10532 }
10533 }
10534 }
10535 int i = 0;
10536 auto &chanVar = const_cast<RooAbsCategoryLValue &>(_simPdf->indexCat());
10537 // auto _idx = chanVar.getIndex();
10538 auto _range = GetRange();
10539 std::vector<TString> chanPatterns;
10540 if (_range && strlen(_range)) {
10541 TStringToken pattern(_range, ",");
10542 while (pattern.NextToken()) {
10543 chanPatterns.emplace_back(pattern);
10544 }
10545 }
10546 for (auto &_v : _channels) {
10547 if (_v->IsHidden())
10548 continue;
10549 TString s(_v->GetName());
10550 pad->cd(++i);
10551 gPad->SetName(s);
10552 TString cName = s(s.Index('=') + 1, s.Length());
10553 chanVar.setLabel(cName);
10554 bool inRange = chanPatterns.empty();
10555 for (auto &p : chanPatterns) {
10556 if (chanVar.inRange(p)) {
10557 inRange = true;
10558 break;
10559 }
10560 }
10561 if (!inRange || !_v->get<RooAbsReal>()->isSelectedComp())
10562 gPad->SetFillColor(kGray);
10563 if (!hasSame && _size > 1 && (gStyle->GetTitleFont("Y") % 10) == 3)
10564 gPad->SetLeftMargin(std::min(gPad->GetLeftMargin() * (1. / gPad->GetWNDC()), 0.3));
10565 _v->Draw(opt);
10567 }
10568 pad->cd(0);
10569 gPad->Modified();
10570 // gPad->Update();
10571 return;
10572 }
10573
10574 if (!get() || get<RooArgList>()) {
10575 // is a group draw all the submembers
10576 browse();
10577 int _size = 0;
10578 // int _size = _channels.size(); // size(); if (find("!.vars")) _size--;
10579 for (auto &_v : *this) {
10580 if (_v->IsHidden())
10581 continue;
10582 if (strcmp(GetName(), ".vars") == 0) {
10583 // auto hide obs and "1" and const var
10584 if (_v->get<RooAbsArg>()->getAttribute("obs"))
10585 continue;
10586 if (strcmp(_v->get()->GetName(), "1") == 0 || strcmp(_v->get()->GetName(), "ONE") == 0 ||
10587 TString(_v->get()->GetName()).BeginsWith("binWidth_"))
10588 continue;
10589 if (_v->get()->InheritsFrom("RooConstVar"))
10590 continue;
10591 }
10592 TString s(_v->GetName());
10593 if (s.BeginsWith(".") || s.BeginsWith("!"))
10594 continue;
10595 _size++;
10596 }
10597 if (!hasSame) {
10598 clearPad();
10599 pad->SetBorderSize(0);
10600 dynamic_cast<TPad *>(pad)->DivideSquare(_size, 1e-9, 1e-9);
10601 }
10602 int i = 0;
10603 for (auto &_v : *this) {
10604 if (_v->IsHidden())
10605 continue;
10606 if (strcmp(GetName(), ".vars") == 0) {
10607 // auto hide obs and "1" and const var
10608 if (_v->get<RooAbsArg>()->getAttribute("obs"))
10609 continue;
10610 if (strcmp(_v->get()->GetName(), "1") == 0 || strcmp(_v->get()->GetName(), "ONE") == 0 ||
10611 TString(_v->get()->GetName()).BeginsWith("binWidth_"))
10612 continue;
10613 if (_v->get()->InheritsFrom("RooConstVar"))
10614 continue;
10615 }
10616 TString s(_v->GetName());
10617 if (s.BeginsWith(".") || s.BeginsWith("!"))
10618 continue;
10619 pad->cd(++i);
10620 gPad->SetName(s);
10621 if (!hasSame && _size > 1 && (gStyle->GetTitleFont("Y") % 10) == 3)
10622 gPad->SetLeftMargin(std::min(gPad->GetLeftMargin() * (1. / gPad->GetWNDC()), 0.3));
10623 _v->Draw(opt);
10624 // pad->Modified();//pad->Update();
10626 }
10627 pad->cd(0);
10628 gPad->Modified();
10629 // gPad->Update();
10630 return;
10631 }
10632
10633 if (get()->InheritsFrom("RooProdPdf")) {
10634 // draw the main pdf, if there is one...
10635 auto _mainChild = mainChild();
10636 if (_mainChild) {
10637 _mainChild.Draw(opt);
10638 gPad->SetName(GetName());
10639 return;
10640 }
10641 }
10642
10643 if (auto fr = get<RooFitResult>(); fr) {
10644 if (sOpt.Contains("corr")) {
10645 // do correlation matrix
10646 // if a number follows 'corr', reduce the correlation matrix to show only the most extreme correlations
10647 int numCorrs = TString(sOpt(sOpt.Index("corr") + 4, sOpt.Length())).Atoi();
10648 if (numCorrs == 0)
10649 numCorrs = fr->correlationMatrix().GetNcols();
10650
10651 TH2 *hist = nullptr;
10652 if (numCorrs < fr->correlationMatrix().GetNcols()) {
10653 // need to reduce
10654 std::set<std::pair<double, size_t>> maxCorrs;
10655 for (int i = 0; i < fr->correlationMatrix().GetNcols(); i++) {
10656 double maxCorr = 0;
10657 for (int j = 0; j < fr->correlationMatrix().GetNcols(); j++) {
10658 if (j == i)
10659 continue;
10660 maxCorr = std::max(std::abs(fr->correlationMatrix()(i, j)), maxCorr);
10661 }
10662 maxCorrs.insert({maxCorr, i});
10663 }
10664 std::vector<size_t> topN;
10665 int c = 0;
10666 for (auto itr = maxCorrs.rbegin(); itr != maxCorrs.rend(); ++itr) {
10667 topN.push_back(itr->second);
10668 c++;
10669 if (c == numCorrs)
10670 break;
10671 }
10672 hist = new TH2D(fr->GetName(), TString::Format("%s - Top %d correlations", fr->GetTitle(), numCorrs),
10673 numCorrs, 0, numCorrs, numCorrs, 0, numCorrs);
10674 for (size_t i = 0; i < topN.size(); i++) {
10675 hist->GetXaxis()->SetBinLabel(i + 1, fr->floatParsFinal().at(topN.at(i))->GetTitle());
10676 hist->GetYaxis()->SetBinLabel(numCorrs - i, fr->floatParsFinal().at(topN.at(i))->GetTitle());
10677 for (size_t j = 0; j < topN.size(); j++) {
10678 hist->Fill(i + 0.5, numCorrs - j - 0.5, fr->correlationMatrix()(topN.at(i), topN.at(j)));
10679 }
10680 }
10681 hist->SetMinimum(-1);
10682 hist->SetMaximum(1);
10683
10684 } else {
10685 hist = fr->correlationHist(fr->GetName());
10686 hist->SetTitle(fr->GetTitle());
10687 }
10688
10689 hist->SetBit(kCanDelete);
10690 hist->Scale(100);
10691 hist->SetStats(false);
10692 hist->SetDirectory(nullptr);
10694 gStyle->SetPaintTextFormat(".1f");
10695 hist->GetXaxis()->SetTickSize(0);
10696 hist->GetYaxis()->SetTickSize(0);
10697 hist->SetMinimum(-100);
10698 hist->Draw(sOpt);
10700 gPad->SetGrid(1, 1);
10701 gPad->SetLogy(0);
10702 gPad->SetLogx(0);
10703 return;
10704 }
10705
10706 if (sOpt.Contains("brakdown")) { // e will have been removed above
10707
10708 // breakdown is quadrature difference between total error and conditional error
10709 // group by 'group' attribute
10710
10711 std::string poiName;
10712 if (sOpt.Contains("brakdown:")) {
10713 TString sOpt3(opt);
10714 poiName = sOpt3(sOpt3.Index("breakdown:") + 10, sOpt3.Length());
10715 } else {
10716 std::unique_ptr<RooAbsCollection> _poi(fr->floatParsFinal().selectByAttrib("poi", true));
10717 if (_poi->empty()) {
10718 throw std::runtime_error("No floating poi in the fit");
10719 } else if (_poi->size() != 1) {
10720 throw std::runtime_error("Multiple poi in the fit");
10721 }
10722 poiName = _poi->first()->GetName();
10723 }
10724 RooRealVar *poi = dynamic_cast<RooRealVar *>(fr->floatParsFinal().find(poiName.c_str()));
10725 if (!poi) {
10726 throw std::runtime_error(TString::Format("Cannot find parameter %s", poiName.c_str()));
10727 }
10728 std::set<std::string> groups;
10729 for (auto p : fr->floatParsFinal()) {
10730 if (p == poi) {
10731 continue;
10732 } else if (p->getStringAttribute("group")) {
10733 groups.insert(p->getStringAttribute("group"));
10734 } else {
10735 groups.insert(p->GetTitle());
10736 }
10737 }
10738
10739 auto roundedVal = xRooFit::matchPrecision(std::pair(poi->getVal(), poi->getError()));
10740
10741 TPie *pie = new TPie(TString::Format("breakdown:%s", poi->GetName()),
10742 TString::Format("%s: %g #pm %g", poi->GetTitle(), roundedVal.first, roundedVal.second),
10743 groups.size() + 1);
10744
10745 // for display of errors will go to one extra dp ...
10746 roundedVal.second *= .1;
10747
10748 // do breakdown by removing parameters in blocks according to groups and seeing impact on variance
10749 // this will give the correct sum but will be order-dependent if there are correlations between
10750 // groups. therefore we will stick with group-by-group
10751 // RooArgList pars(fr->floatParsFinal()); // pars to not condition on
10752 // double variance = pow(dynamic_cast<RooRealVar*>(poi)->getError(),2);
10753 int i = 0;
10754 for (auto group : groups) {
10755 RooArgList pars(fr->floatParsFinal()); // pars to not condition on
10756 double variance = pow(dynamic_cast<RooRealVar *>(poi)->getError(), 2);
10757 for (auto p : fr->floatParsFinal()) {
10758 if (p == poi) {
10759 continue;
10760 } else if ((p->getStringAttribute("group") && group == p->getStringAttribute("group")) ||
10761 (!p->getStringAttribute("group") && group == p->GetTitle())) {
10762 // conditioning on this parameter ... remove from pars list
10763 pars.remove(*p);
10764 }
10765 }
10766 int idx = pars.index(poiName.c_str());
10767 double reducedVar = fr->conditionalCovarianceMatrix(pars)(idx, idx);
10768 if (reducedVar > variance) {
10769 Warning("Draw", "breakdown group %s variance bigger than preceding?", group.c_str());
10770 pie->SetEntryVal(i, 0);
10771 pie->SetEntryLabel(i, TString::Format("%s: NaN", group.c_str()));
10772 } else {
10773 pie->SetEntryVal(i, variance - reducedVar);
10775 std::pair(sqrt(variance - reducedVar), roundedVal.second)); // r.first will be the rounded error
10776 if (r.first > 0) {
10777 pie->SetEntryLabel(i, TString::Format("%s: %g", group.c_str(), r.first));
10778 } else {
10779 pie->SetEntryLabel(i, group.c_str()); // suppress labels for negligible errors.
10780 }
10781 }
10783 // variance = reducedVar;
10784 i++;
10785 }
10786 // remaining variance is statistical=
10787 double variance = fr->conditionalCovarianceMatrix(*poi)(0, 0);
10788 auto r =
10789 xRooFit::matchPrecision(std::pair(sqrt(variance), roundedVal.second)); // r.first will be the rounded error
10790 pie->SetEntryVal(i, variance);
10791 pie->SetEntryLabel(i, TString::Format("stat: %g", r.first));
10793 pie->SetBit(kCanDelete);
10794 pie->SetRadius(0.17);
10796 pie->Draw("NOL");
10797 return;
10798 }
10799
10800 // plot pull or impact
10802 out->SetName(TString::Format("%s_pull", fr->GetName()));
10803 out->SetTitle("Fit Result Pulls");
10804 std::vector<TString> graphLabels;
10806 ugraph->SetName(TString::Format("%s_pull_unconstrained", fr->GetName()));
10807 ugraph->SetTitle("Fit Result Pulls");
10808 std::vector<TString> ugraphLabels;
10809 std::map<std::string, double> scale;
10810 std::map<std::string, double> offset;
10811 for (auto &p : fr->floatParsFinal()) {
10812 auto _v = dynamic_cast<RooRealVar *>(p);
10813 if (!_v)
10814 continue;
10815
10816 if (std::isnan(_v->getErrorHi()) || std::isnan(_v->getErrorLo())) {
10817 Warning("Draw", "%s error is invalid", _v->GetName());
10818 }
10819
10820 // need to get constraint mean and error parameters ....
10821 // look for normal gaussian and poisson cases
10822 double prefitError = 0;
10823 double prefitVal = 0;
10824 double customScale = 0;
10825 if (auto ip =
10826 dynamic_cast<RooRealVar *>(fr->floatParsInit().find(p->GetName()))) { // handles if no prefit available
10827 prefitError = ip->getError();
10828 prefitVal = ip->getVal();
10829 };
10830
10831 std::shared_ptr<xRooNode> pConstr;
10832 if (fParent && fParent->getObject<RooRealVar>(p->GetName())) {
10833 auto _vv = fParent->getObject<RooRealVar>(p->GetName());
10834 if (_vv->hasRange("pullScale")) {
10835 customScale = (_vv->getMax("pullScale") - _vv->getMin("pullScale")) / 2.;
10836 }
10837 auto _constr = xRooNode(_vv, *this).constraints();
10838 for (auto &c : _constr) {
10839 if (c->get<RooPoisson>() || c->get<RooGaussian>()) {
10840 // require parameter to be a direct server of the constraint pdf to count if its a gaussian
10841 bool isServer = true;
10842 if (c->get<RooGaussian>()) {
10843 isServer = false;
10844 for (auto s : c->get<RooAbsArg>()->servers()) {
10845 if (strcmp(s->GetName(), p->GetName()) == 0) {
10846 isServer = true;
10847 break;
10848 }
10849 }
10850 }
10851 if (isServer) {
10852 pConstr = c;
10853 break;
10854 }
10855 }
10856 }
10857 }
10858 if (pConstr) {
10859
10860 // there will be 3 deps, one will be this par, the other two are the mean and error (or error^2 in case of
10861 // poisson
10862
10863 // std::cout << p->GetName() << " extracted " << prefitVal << " " << prefitError << " from ";
10864 // pConstr->deps().Print();
10865 pConstr->browse();
10866 if (pConstr->get<RooPoisson>() && pConstr->find(".x")) {
10867 std::string xName = pConstr->find(".x")->get()->GetName();
10868 prefitVal = pConstr->find(".x")->get<RooAbsReal>()->getVal();
10869 for (auto &_d : pConstr->vars()) {
10870 if (strcmp(p->GetName(), _d->get()->GetName()) == 0)
10871 continue;
10872 if (xName == _d->get()->GetName())
10873 continue;
10874 if (_d->get<RooAbsReal>()->getVal())
10875 prefitError = _d->get<RooAbsReal>()->getVal();
10876 }
10877 if (fr->constPars().find(pConstr->find(".x")->get()->GetName())) {
10878 // globs was saved to fr, use that instead of current value
10879 prefitVal = fr->constPars().getRealValue(pConstr->find(".x")->get()->GetName());
10880 }
10881 // prefitVal will be the global observable value, need to divide that by tau
10882 prefitVal /= prefitError;
10883 // prefiterror will be tau ... need 1/sqrt(tau) for error
10884 prefitError = 1. / sqrt(prefitError);
10885 } else if (auto _g = pConstr->get<RooGaussian>(); _g) {
10886 prefitError =
10887 (pConstr->find(".sigma")) ? pConstr->find(".sigma")->get<RooAbsReal>()->getVal() : prefitError;
10888 prefitVal =
10889 (pConstr->find(".x")) ? pConstr->find(".x")->get<RooAbsReal>()->getVal() : 0; // usually the globs
10890 if (pConstr->find(".x") && fr->constPars().find(pConstr->find(".x")->get()->GetName())) {
10891 // globs was saved to fr, use that instead of current value
10892 prefitVal = fr->constPars().getRealValue(pConstr->find(".x")->get()->GetName());
10893 }
10894 if (pConstr->find(".x") &&
10895 strcmp(p->GetName(), pConstr->find(".x")->get<RooAbsReal>()->GetName()) == 0) {
10896 // hybrid construction case,
10897 prefitVal = pConstr->find(".mean")->get<RooAbsReal>()->getVal();
10898 if (fr->constPars().find(pConstr->find(".mean")->get()->GetName())) {
10899 // globs was saved to fr, use that instead of current value
10900 prefitVal = fr->constPars().getRealValue(pConstr->find(".mean")->get()->GetName());
10901 }
10902 }
10903 }
10904
10905 if (customScale)
10906 prefitError = customScale;
10907 if (prefitError == 0) {
10908 Warning("Draw", "failed to determine prefit error of %s, using post-fit error", p->GetName());
10909 prefitError = _v->getError();
10910 }
10911 out->SetPoint(out->GetN(), out->GetN(), (_v->getVal() - prefitVal) / prefitError);
10912 out->SetPointError(out->GetN() - 1, 0, 0, (-_v->getErrorLo()) / prefitError,
10913 (_v->getErrorHi()) / prefitError);
10914 graphLabels.push_back(p->GetName());
10915 scale[p->GetName()] = prefitError;
10916 offset[p->GetName()] = prefitVal;
10917 } else if (!fParent) {
10918 // no parent to determine constraints from ... prefitError=0 will be the unconstrained ones
10919 if (customScale)
10920 prefitError = customScale;
10921 if (prefitError == 0) {
10922 // uses range of var
10923 prefitError = (std::max({_v->getMax() - _v->getVal(), _v->getVal() - _v->getMin(), 4.}) / 4);
10924 ugraph->SetPoint(ugraph->GetN(), ugraph->GetN(), (_v->getVal() - prefitVal) / prefitError);
10925 ugraph->SetPointError(ugraph->GetN() - 1, 0, 0, (-_v->getErrorLo()) / prefitError,
10926 (_v->getErrorHi()) / prefitError);
10927 ugraphLabels.push_back(p->GetName());
10928 } else {
10929 out->SetPoint(out->GetN(), out->GetN(), (_v->getVal() - prefitVal) / prefitError);
10930 out->SetPointError(out->GetN() - 1, 0, 0, (-_v->getErrorLo()) / prefitError,
10931 (_v->getErrorHi()) / prefitError);
10932 graphLabels.push_back(p->GetName());
10933 }
10934 scale[p->GetName()] = prefitError;
10935 offset[p->GetName()] = prefitVal;
10936
10937 } else {
10938 // unconstrained (or at least couldn't determine constraint) ... use par range if no prefit error
10939 if (customScale)
10940 prefitError = customScale;
10941 if (prefitError == 0) {
10942 prefitError = (std::max({_v->getMax() - _v->getVal(), _v->getVal() - _v->getMin(), 4.}) / 4);
10943 }
10944 ugraph->SetPoint(ugraph->GetN(), ugraph->GetN(), (_v->getVal() - prefitVal) / prefitError);
10945 ugraph->SetPointError(ugraph->GetN() - 1, 0, 0, (-_v->getErrorLo()) / prefitError,
10946 (_v->getErrorHi()) / prefitError);
10947 ugraphLabels.push_back(p->GetName());
10948 scale[p->GetName()] = prefitError;
10949 offset[p->GetName()] = prefitVal;
10950 }
10951 }
10952 auto graph = out;
10953
10954 // append ugraph points to end of graph
10955 for (int i = 0; i < ugraph->GetN(); i++)
10956 ugraph->SetPointX(i, i + graph->GetN());
10957 int nUnconstrained = ugraph->GetN();
10958 TList tmpList;
10959 tmpList.SetName("tmpList");
10960 tmpList.Add(ugraph);
10961 graph->Merge(&tmpList);
10962 tmpList.RemoveAll();
10963 delete ugraph;
10964 for (auto &l : ugraphLabels) {
10965 graphLabels.push_back(l);
10966 }
10967
10968 graph->SetBit(kCanDelete);
10969 graph->SetMarkerStyle(20);
10970 graph->SetMarkerSize(0.5);
10971
10972 graph->SetMaximum(4);
10973 graph->SetMinimum(-4);
10974
10975 bool doHorizontal =
10976 (!sOpt.Contains("impact") && sOpt.Contains("v")) || (sOpt.Contains("impact") && !sOpt.Contains("himpact"));
10977
10978 std::vector<std::pair<double, std::string>> covariances;
10979 /*double poiError = 0;*/ std::string poiName;
10980 double maxImpact = 0;
10981 if (sOpt.Contains("impact")) {
10982 if (sOpt.Contains("impact:")) {
10983 TString sOpt3(opt);
10984 poiName = sOpt3(sOpt3.Index("impact:") + 7, sOpt3.Length());
10985 } else {
10986 std::unique_ptr<RooAbsCollection> _poi(fr->floatParsFinal().selectByAttrib("poi", true));
10987 if (_poi->empty()) {
10988 throw std::runtime_error("No floating poi in the fit");
10989 } else if (_poi->size() != 1) {
10990 throw std::runtime_error("Multiple poi in the fit");
10991 }
10992 poiName = _poi->first()->GetName();
10993 }
10994 RooAbsArg *poi = fr->floatParsFinal().find(poiName.c_str());
10995 if (!poi) {
10996 throw std::runtime_error(TString::Format("Cannot find parameter %s", poiName.c_str()));
10997 }
10998 size_t poiIdx = fr->floatParsFinal().index(*poi);
10999 // put parameters in order of impact on the poi
11000
11001 // impact is regression coefficient * npError
11002 // relevant regression coefficient is cov / (npVariance)
11003 // i.e. DeltaX/sigmaX = [cov(X,Y)/(sigmaXsigmaY)]DeltaY/sigmaY
11004 // ... DeltaX = [cov(X,Y)/(sigmaY^2)]DeltaY
11005 // if DeltaY is just sigmaY then DeltaX = cov(X,Y)/sigmaY
11006
11007 for (auto &label : graphLabels) {
11008 covariances.emplace_back(fr->covarianceMatrix()(poiIdx, fr->floatParsFinal().index(label)) /
11009 dynamic_cast<RooRealVar *>(fr->floatParsFinal().find(label))->getError(),
11010 label);
11011 }
11012 std::sort(covariances.begin(), covariances.end(),
11013 [&](std::pair<double, std::string> i, std::pair<double, std::string> j) {
11014 return doHorizontal ? (std::abs(i.first) < std::abs(j.first))
11015 : (std::abs(i.first) > std::abs(j.first));
11016 });
11017
11018 TGraphAsymmErrors sortedGraph;
11019 std::vector<TString> sortedLabels;
11020 maxImpact = (doHorizontal) ? covariances.back().first
11021 : covariances.front().first; // note: max impact is likely to be self variance
11022 for (auto &c : covariances) {
11023 if (c.second == poi->GetName()) {
11024 // poiError = sqrt(c.first);
11025 continue; // skip self
11026 }
11027 c.first *= 4. / (maxImpact * 1.2);
11028 sortedLabels.push_back(c.second);
11029 size_t i = 0;
11030 for (; i < graphLabels.size(); i++) {
11031 if (graphLabels[i] == c.second) {
11032 break;
11033 }
11034 }
11035 sortedGraph.AddPoint(sortedGraph.GetN(), graph->GetPointY(i));
11036 sortedGraph.SetPointError(sortedGraph.GetN() - 1, 0, 0, graph->GetErrorYlow(i), graph->GetErrorYhigh(i));
11037 }
11038 graph->Set(0);
11039 TList tmpList2;
11040 tmpList2.SetName("tmpList");
11041 tmpList2.Add(&sortedGraph);
11042 graph->Merge(&tmpList2);
11043 tmpList2.RemoveAll();
11044 graphLabels = sortedLabels;
11045 graph->SetTitle("Fit Result Impact");
11046 }
11047
11048 // create a framing histogram
11049 TH2D *hist;
11050 if (doHorizontal) {
11051 hist = new TH2D(GetName(), fr->GetTitle(), 100, -4, 4, std::max(graph->GetN(), 1), -0.5,
11052 std::max(graph->GetN(), 1) - 0.5);
11053 int i = 1;
11054 for (auto &l : graphLabels) {
11055 hist->GetYaxis()->SetBinLabel(i++, l);
11056 }
11057 if (!graphLabels.empty())
11058 hist->GetYaxis()->LabelsOption("v");
11059 hist->GetXaxis()->SetTitle("(#hat{#theta}-#theta_{i})/#sigma_{i}");
11060 } else {
11061 hist = new TH2D(GetName(), fr->GetTitle(), std::max(graph->GetN(), 1), -0.5, std::max(graph->GetN(), 1) - 0.5,
11062 100, -4, 4);
11063 int i = 1;
11064 for (auto &l : graphLabels) {
11065 hist->GetXaxis()->SetBinLabel(i++, l);
11066 }
11067 if (!graphLabels.empty())
11068 hist->GetXaxis()->LabelsOption("v");
11069 hist->GetYaxis()->SetNdivisions(8, 0, 0);
11070 hist->GetYaxis()->SetTitle("(#hat{#theta}-#theta_{i})/#sigma_{i}");
11071 }
11072 hist->SetStats(false);
11073 hist->SetDirectory(nullptr);
11074 hist->SetBit(kCanDelete);
11075 auto histCopy = dynamic_cast<TH1 *>(hist->Clone(".axis"));
11076 histCopy->SetDirectory(nullptr);
11077 histCopy->SetBit(kCanDelete);
11078 auto _axis = (doHorizontal ? histCopy->GetYaxis() : histCopy->GetXaxis());
11079
11080 graph->GetHistogram()->GetXaxis()->Set(std::max(graph->GetN(), 1), -0.5, std::max(graph->GetN(), 1) - 0.5);
11081 for (int ii = 1; ii <= _axis->GetNbins(); ii++) {
11082 graph->GetHistogram()->GetXaxis()->SetBinLabel(ii, _axis->GetBinLabel(ii));
11083 }
11084 // int i = 1;
11085 // for (auto &l : graphLabels) {
11086 // hist->GetXaxis()->SetBinLabel(i++, l);
11087 // }
11088 // hist->SetMaximum(4);
11089 // hist->SetMinimum(-4);
11090 // if (graph->GetN())
11091 // hist->GetXaxis()->LabelsOption("v");
11092 // hist->GetYaxis()->SetNdivisions(8, 0, 0);
11093 // hist->GetYaxis()->SetTitle("(#hat{#theta}-#theta_{i})/#sigma_{i}");
11094 clearPad();
11095 // create a new pad because adjust the margins ...
11096 auto oldPad = gPad;
11097 gPad->Divide(1, 1, 1e-9, 1e-9);
11098 gPad->cd(1);
11099
11100 if (doHorizontal) {
11101 gPad->SetLeftMargin(0.4);
11102 } else {
11103 gPad->SetBottomMargin(0.4);
11104 }
11105
11106 auto pNamesHist = dynamic_cast<TH1F *>(graph->GetHistogram()->Clone("scales")); // used by interactive "pull" plot
11107 pNamesHist->Sumw2();
11108 pNamesHist->SetDirectory(nullptr);
11109
11110 for (int ii = 1; ii <= graph->GetN(); ii++) { // use graph->GetN() to protect against the 0 pars case
11111 auto _p = fr->floatParsFinal().find(_axis->GetBinLabel(ii));
11112 pNamesHist->SetBinContent(ii, offset[_p->GetName()]);
11113 pNamesHist->SetBinError(ii, scale[_p->GetName()]);
11114 _axis->SetBinLabel(ii, strlen(_p->GetTitle()) ? _p->GetTitle() : _p->GetName());
11115 }
11116
11117 // hist->Draw(); -- now just draw the graph
11118
11119 if (!sOpt.Contains("impact")) {
11120 for (int ii = 2; ii >= 1; ii--) {
11121 auto pullBox = new TGraphErrors;
11122 pullBox->SetName(TString::Format("%dsigmaBand", ii));
11123 pullBox->SetBit(kCanDelete);
11124 pullBox->SetPoint(0, (doHorizontal) ? -ii : -0.5, (doHorizontal) ? -0.5 : 0);
11125 pullBox->SetPoint(1, (doHorizontal) ? ii : (_axis->GetNbins() - 0.5 - nUnconstrained),
11126 (doHorizontal) ? -0.5 : 0);
11127 pullBox->SetPointError(0, 0, (doHorizontal) ? (_axis->GetNbins() - nUnconstrained) : ii);
11128 pullBox->SetPointError(1, 0, (doHorizontal) ? (_axis->GetNbins() - nUnconstrained) : ii);
11129 pullBox->SetFillColor((ii == 2) ? kYellow : kGreen);
11130 hist->GetListOfFunctions()->Add(pullBox, "3"); // pullBox->Draw("3");
11131 }
11132 auto pullLine = new TGraph;
11133 pullLine->SetName("0sigmaLine");
11134 pullLine->SetBit(kCanDelete);
11135 pullLine->SetPoint(0, -0.5, 0);
11136 pullLine->SetPoint(1, _axis->GetNbins() - 0.5, 0);
11137 pullLine->SetLineStyle(2);
11138 pullLine->SetEditable(false);
11139 hist->GetListOfFunctions()->Add(pullLine, "l"); // pullLine->Draw("l");
11140
11141 // also draw vertical line separating constrained from unconstrained, if necessary
11142 if (nUnconstrained > 0) {
11143 pullLine = new TGraph;
11144 pullLine->SetName("dividerLine");
11145 pullLine->SetBit(kCanDelete);
11146 pullLine->SetPoint(0, graph->GetN() - 0.5 - nUnconstrained, -100);
11147 pullLine->SetPoint(1, graph->GetN() - 0.5 - nUnconstrained, 100);
11148 pullLine->SetLineStyle(2);
11149 pullLine->SetEditable(false);
11150 hist->GetListOfFunctions()->Add(pullLine, "l"); // pullLine->Draw("l");
11151 }
11152
11153 // and draw a pave with fr status info
11154 TPaveText *pave =
11155 new TPaveText(gPad->GetLeftMargin(), 1. - gPad->GetTopMargin(), 1. - gPad->GetRightMargin(), 0.98, "NDCNB");
11156 pave->SetFillStyle(0);
11157 pave->SetBorderSize(0);
11158 pave->SetMargin(0.);
11159 pave->SetName("status");
11160 pave->SetTextAlign(31);
11161 pave->AddText(TString::Format("minNLL: %g edm: %g", fr->minNll(), fr->edm()))
11162 ->SetTextColor((fr->status() == 3) ? kRed : kBlack);
11163 std::string covQualTxt;
11164 switch (fr->covQual()) {
11165 case -1: covQualTxt = "Unknown"; break;
11166 case 0: covQualTxt = "Not calculated"; break;
11167 case 1: covQualTxt = "Approximate"; break;
11168 case 2: covQualTxt = "Forced Positive-Definite"; break;
11169 case 3: covQualTxt = "Accurate"; break;
11170 }
11171 pave->AddText(TString::Format("Cov. Quality: %d (%s)", fr->covQual(), covQualTxt.c_str()))
11172 ->SetTextColor((fr->covQual() == 3) ? kBlack : kRed);
11173
11174 std::string statusCodes;
11175 for (unsigned int i = 0; i < fr->numStatusHistory(); i++) {
11176 statusCodes += TString::Format(" %s = %d", fr->statusLabelHistory(i), fr->statusCodeHistory(i));
11177 }
11178 pave->AddText(statusCodes.c_str())->SetTextColor(fr->status() == 0 ? kBlack : kRed);
11179
11180 hist->GetListOfFunctions()->Add(pave);
11181
11182 } else {
11183 gPad->SetTicks(0, 0); // ensure mirrored ticks aren't drawn in this pad
11184
11185 if (doHorizontal) {
11186 // ensure canvas height big enough
11187 if (int(gPad->GetCanvas()->GetWh()) < pNamesHist->GetNbinsX() * 15) {
11188 gPad->GetCanvas()->SetCanvasSize(gPad->GetCanvas()->GetWw(), pNamesHist->GetNbinsX() * 15);
11189 }
11190 }
11191
11192 double factor = 475. / gPad->GetCanvas()->GetWh(); // Wh is the full canvas height, not window height
11193 gPad->SetTopMargin(gStyle->GetPadTopMargin() * factor); // fixed margin height
11194 gPad->SetBottomMargin(gStyle->GetPadBottomMargin() * factor); // fixed margin height
11195
11196 TGaxis *axis =
11197 new TGaxis(_axis->GetXmin(), -4, _axis->GetXmin(), 4, -1.2 * maxImpact, 1.2 * maxImpact, 510, "-S");
11198
11199 if (doHorizontal) {
11200 // _axis->SetLabelSize(
11201 // (_axis->GetLabelFont() % 10 > 2)
11202 // ? (20 / factor)
11203 // : ((gPad->AbsPixeltoY(0) - gPad->AbsPixeltoY(20 / factor)) / (gPad->GetY2() -
11204 // gPad->GetY1())));
11205 // histCopy->GetXaxis()->SetTickLength(histCopy->GetXaxis()->GetTickLength() * factor);
11206 // hist->GetXaxis()->SetTickLength(hist->GetXaxis()->GetTickLength() * factor);
11207 // histCopy->GetYaxis()->SetTickLength(histCopy->GetYaxis()->GetTickLength() * factor);
11208 // hist->GetYaxis()->SetTickLength(hist->GetYaxis()->GetTickLength() * factor);
11209 // histCopy->GetXaxis()->SetTitleOffset(histCopy->GetXaxis()->GetTitleOffset() * factor);
11210 // histCopy->GetXaxis()->SetLabelOffset(histCopy->GetXaxis()->GetLabelOffset() * factor);
11211 // hist->GetXaxis()->SetTitleOffset(hist->GetXaxis()->GetTitleOffset() * factor);
11212 // hist->GetXaxis()->SetLabelOffset(hist->GetXaxis()->GetLabelOffset() * factor);
11213 // histCopy->GetXaxis()->SetTitleOffset(histCopy->GetXaxis()->GetTitleOffset() * factor);
11214 // histCopy->GetXaxis()->SetLabelOffset(histCopy->GetXaxis()->GetLabelOffset() * factor);
11215 }
11216 // copy attributes from TAxis to TGaxis
11217 axis->ImportAxisAttributes((doHorizontal) ? histCopy->GetXaxis() : histCopy->GetYaxis());
11218 axis->SetTitle(TString::Format("#Delta %s", fr->floatParsFinal().find(poiName.c_str())->GetTitle()));
11219
11220 // create impact bar charts
11221 for (int tt = 0; tt < 2; tt++) {
11222 auto impact = static_cast<TH1 *>(
11223 graph->GetHistogram()->Clone(TString::Format("%s_impact+", tt == 0 ? "prefit" : "postfit")));
11224 impact->SetDirectory(nullptr);
11225 impact->GetYaxis()->SetTitle(TString::Format("#Delta%s/#sigma", poiName.c_str()));
11226 impact->SetBarWidth(0.9);
11227 impact->SetBarOffset(0.05);
11228 impact->SetLineColor(kBlack);
11229 impact->SetFillColor(kAzure - 4);
11230 impact->SetFillStyle(tt == 0 ? 3013 : 1001);
11231 auto impact2 =
11232 static_cast<TH1 *>(impact->Clone(TString::Format("%s_impact-", tt == 0 ? "prefit" : "postfit")));
11233 impact2->SetDirectory(nullptr);
11234 impact2->SetFillColor(kCyan);
11235 for (int ii = 1; ii <= pNamesHist->GetNbinsX(); ii++) {
11236 for (auto &c : covariances) {
11237 if (c.second != pNamesHist->GetXaxis()->GetBinLabel(ii))
11238 continue;
11239 auto vv = dynamic_cast<RooRealVar *>(fr->floatParsFinal().find(c.second.c_str()));
11240 auto vv_init = dynamic_cast<RooRealVar *>(fr->floatParsInit().find(c.second.c_str()));
11241 impact->SetBinContent(ii, ((tt == 0 && !vv_init->hasError()) || !vv->hasError())
11242 ? 0.
11243 : c.first * vv->getError() / vv->getErrorHi() *
11244 (tt == 0 ? (vv_init->getErrorHi() / vv->getErrorHi()) : 1.));
11245 impact2->SetBinContent(ii, ((tt == 0 && !vv_init->hasError()) || !vv->hasError())
11246 ? 0.
11247 : c.first * vv->getError() / vv->getErrorLo() *
11248 (tt == 0 ? (vv_init->getErrorLo() / vv->getErrorLo()) : 1.));
11249 }
11250 }
11251 hist->GetListOfFunctions()->Add(impact, (doHorizontal) ? "hbarsamemin0" : "bsamey+");
11252 hist->GetListOfFunctions()->Add(impact2, (doHorizontal) ? "hbarsamemin0" : "bsamey+");
11253 }
11254 // add three lines
11255 for (int ii = -1; ii <= 1; ii++) {
11256 auto pullLine = new TGraph;
11257 pullLine->SetName(TString::Format("%dsigmaLine", ii));
11258 pullLine->SetBit(kCanDelete);
11259 pullLine->SetPoint(0, -0.5, ii);
11260 pullLine->SetPoint(1, hist->GetNbinsY() - 0.5, ii);
11261 pullLine->SetLineStyle(2);
11262 pullLine->SetEditable(false);
11263 hist->GetListOfFunctions()->Add(pullLine, "l");
11264 }
11265 hist->GetListOfFunctions()->Add(axis); // draw axis last
11266 TLegend *leg1 =
11267 new TLegend(0.02, doHorizontal ? (1. - 0.22 * factor) : 0.02, 0.27, (doHorizontal ? 1. : 0.24));
11268 leg1->SetFillStyle(0);
11269 leg1->SetBorderSize(0);
11270 leg1->SetMargin(0.25);
11271 leg1->SetNColumns(2);
11272
11273 leg1->SetTextSize(_axis->GetLabelSize());
11274 leg1->SetTextFont(_axis->GetLabelFont());
11275 leg1->AddEntry((TObject *)nullptr, "Hessian Pre-fit", "");
11276 leg1->AddEntry((TObject *)nullptr, "Impact:", "");
11277 leg1->AddEntry(hist->FindObject("prefit_impact+"), "#theta = #hat{#theta}+#Delta#theta", "f");
11278 leg1->AddEntry(hist->FindObject("prefit_impact-"), "#theta = #hat{#theta}-#Delta#theta", "f");
11279 leg1->AddEntry((TObject *)nullptr, "Hessian Post-fit", "");
11280 leg1->AddEntry((TObject *)nullptr, "Impact:", "");
11281 leg1->AddEntry(hist->FindObject("postfit_impact+"), "#theta = #hat{#theta}+#Delta#theta", "f");
11282 leg1->AddEntry(hist->FindObject("postfit_impact-"), "#theta = #hat{#theta}-#Delta#theta", "f");
11283
11284 hist->GetListOfFunctions()->Add(leg1);
11285 if (gStyle->GetOptTitle()) {
11286 histCopy->SetBit(TH1::kNoTitle);
11287 TPaveText *title =
11288 new TPaveText(gPad->GetLeftMargin(), 1. - gPad->AbsPixeltoY(14), 1. - gPad->GetRightMargin(), 1., "NDC");
11289 title->ConvertNDCtoPad();
11290 title->SetY1NDC(1. - gPad->GetTopMargin() * 0.6);
11291 title->SetY2NDC(1);
11292 title->SetTextSize(
11293 (title->GetTextFont() % 10 > 2)
11294 ? (14 / factor)
11295 : ((gPad->AbsPixeltoY(0) - gPad->AbsPixeltoY(10 / factor)) / (gPad->GetY2() - gPad->GetY1())));
11296 title->SetFillStyle(0);
11297 title->SetBorderSize(0);
11298 title->AddText(histCopy->GetTitle());
11299 hist->GetListOfFunctions()->Add(title);
11300 }
11301 }
11302
11303 graph->SetEditable(false);
11304 pNamesHist->SetLineWidth(0);
11305 pNamesHist->SetMarkerSize(0);
11306 pNamesHist->SetMarkerStyle(0);
11307 graph->GetListOfFunctions()->Add(pNamesHist, "same"); // graph->SetHistogram(pNamesHist);
11308 if (doHorizontal) {
11309
11310 // flip the graph and contained graphs
11311 for (int p = 0; p < graph->GetN(); p++) {
11312 graph->SetPoint(p, graph->GetPointY(p), graph->GetPointX(p));
11313 graph->SetPointError(p, graph->GetErrorYlow(p), graph->GetErrorYhigh(p), graph->GetErrorXlow(p),
11314 graph->GetErrorXhigh(p));
11315 }
11316 for (auto f : *hist->GetListOfFunctions()) {
11317 if (f->InheritsFrom("TH1")) {
11318 // f->Draw("hbarsamemin0");
11319 } /*else if (auto g2 = dynamic_cast<TGraphErrors *>(f)) {
11320 for (int p = 0; p < g2->GetN(); p++) {
11321 g2->SetPoint(p, g2->GetPointY(p), g2->GetPointX(p));
11322 g2->SetPointError(p, g2->GetErrorY(p), _axis->GetNbins());
11323 }
11324 //g2->Draw("3");
11325 } */
11326 else if (auto g = dynamic_cast<TGraph *>(f)) {
11327 for (int p = 0; p < g->GetN(); p++) {
11328 g->SetPoint(p, g->GetPointY(p), g->GetPointX(p));
11329 }
11330 // g->Draw("l");
11331 } else if (auto l = dynamic_cast<TLine *>(f)) {
11332 l->SetX1(l->GetY1());
11333 l->SetX2(l->GetY2());
11334 l->SetY1(_axis->GetXmax());
11335 l->SetY2(_axis->GetXmax());
11336 // l->Draw();
11337 }
11338 }
11339 }
11340
11341 if (!sOpt.Contains("impact")) {
11342 // add labels to graph for unconstrained parameters
11343 for (size_t i = 0; i < ugraphLabels.size(); i++) {
11344 int bin = pNamesHist->GetNbinsX() - ugraphLabels.size() + i + 1;
11345 auto p = dynamic_cast<RooRealVar *>(fr->floatParsFinal().find(pNamesHist->GetXaxis()->GetBinLabel(bin)));
11346 if (!p)
11347 continue;
11348 auto x = graph->GetPointX(graph->GetN() - ugraphLabels.size() + i);
11349 auto y = graph->GetPointY(graph->GetN() - ugraphLabels.size() + i) +
11350 graph->GetErrorYhigh(graph->GetN() - ugraphLabels.size() + i);
11351 auto l = xRooFit::matchPrecision({p->getVal(), p->getError()});
11352 auto t = new TLatex(x, y, TString::Format("%g #pm %g", l.first, l.second));
11353 t->SetBit(kCanDelete);
11354 t->SetTextSize(0.025);
11355 t->SetTextAngle(90);
11356 graph->GetListOfFunctions()->Add(t);
11357 }
11358 }
11359
11360 graph->SetName("pulls");
11361 hist->GetListOfFunctions()->Add(graph, "z0p");
11362 // hist->GetListOfFunctions()->Add(histCopy->Clone(".axis"),(sOpt.Contains("impact") &&
11363 // !doHorizontal)?"axissamey+":"axissame"); // doesn't display right when zoom the axis
11364 if (!hasSame) {
11365 histCopy->Draw((sOpt.Contains("impact") && !doHorizontal)
11366 ? "axisy+"
11367 : "axis"); // draws the axis, called ".axis" for easy access
11368 }
11369 hist->Draw("same");
11370 //
11371 // if(sOpt.Contains("impact")) {
11372 // // make main object the histogram
11373 // auto h = (TH1*)graph->GetHistogram()->Clone("impact");
11374 // graph->GetListOfFunctions()->RemoveAll();
11375 // for(int ii=1;ii<=h->GetNbinsX();ii++) h->SetBinContent(ii,-4);
11376 // h->GetListOfFunctions()->Add(graph,"z0p");
11377 // h->Draw("hbar");
11378 // } else {
11379 // graph->Draw(sOpt.Contains("impact") ? "az0py+" : "az0p");
11380 // }
11381 auto hh = dynamic_cast<TH1 *>(histCopy->Clone(".axiscopy"));
11382 hh->SetDirectory(nullptr);
11383 hh->SetBit(kCanDelete);
11384 hh->Draw(
11385 (sOpt.Contains("impact") && !doHorizontal)
11386 ? "axissamey+"
11387 : "axissame"); // overlay axis again -- important is last so can remove if don't pad->Update before reclear
11388 gPad->Modified();
11389 oldPad->cd();
11390 // gPad->Update();
11391 return;
11392 }
11393
11394 if (get()->InheritsFrom("RooAbsData")) {
11395 auto s = parentPdf();
11396 if (s && s->get<RooSimultaneous>()) {
11397 // drawing dataset associated to a simultaneous means must find subpads with variation names
11398 // may not have subpads if drawning a "Yield" plot ...
11399 bool doneDraw = false;
11400 // in the case of hybrid datasets, the parentPdf will be a reducedPdf ...
11401 // but if the dataset has been added to, then there may be additional entries
11402 // in other channels ... so loop over all labels of the categorical
11403 // and for ones we don't have a channel for, just draw directly on
11404
11405 for (auto [catName, catVal] : s->get<RooSimultaneous>()->indexCat()) {
11406 auto _pad = dynamic_cast<TPad *>(gPad->GetPrimitive(
11407 TString::Format("%s=%s", s->get<RooSimultaneous>()->indexCat().GetName(), catName.c_str())));
11408 if (!_pad)
11409 continue; // channel was hidden?
11410 // attach as a child before calling datasets(), so that if this dataset is external to workspace it is
11411 // included still attaching the dataset ensures dataset reduction for the channel is applied
11412 auto tmp = gPad;
11413 _pad->cd();
11414 if (auto c = s->bins().find(catName)) {
11415 c->push_back(std::make_shared<xRooNode>(*this));
11416 auto ds = c->datasets().find(GetName());
11417 c->resize(c->size() - 1); // remove the child we attached
11418 if (!ds) {
11419 std::cout << " no ds " << GetName() << " - this should never happen!" << std::endl;
11420 continue;
11421 }
11422 ds->Draw(opt);
11423 } else {
11424 Draw(opt);
11425 }
11426 doneDraw = true;
11427 tmp->cd();
11428 }
11429 if (doneDraw) {
11430 gPad->Modified();
11431 return;
11432 }
11433 }
11434
11435 if (!s && hasSame) {
11436 // draw onto all subpads with = in the name
11437 // if has no such subpads, draw onto this pad
11438 bool doneDraw = false;
11439 for (auto o : *gPad->GetListOfPrimitives()) {
11440 if (auto p = dynamic_cast<TPad *>(o); p && TString(p->GetName()).Contains('=')) {
11441 auto _tmp = gPad;
11442 p->cd();
11443 Draw(opt);
11444 _tmp->cd();
11445 doneDraw = true;
11446 }
11447 }
11448 if (doneDraw) {
11449 gPad->Modified();
11450 return;
11451 }
11452 }
11453
11454 auto dataGraph = BuildGraph(v, false, (!s && hasSame) ? gPad : nullptr);
11455 if (!dataGraph)
11456 return;
11457
11458 dataGraph->SetBit(kCanDelete); // will be be deleted when pad is cleared
11459 dataGraph->SetMarkerSize(dataGraph->GetMarkerSize() * gPad->GetWNDC()); // scale marker sizes to pad size
11460
11461 if (s && !s->get<RooAbsPdf>()->canBeExtended()) {
11462 // normalize dataGraph to 1
11463 double tot = 0;
11464 for (int i = 0; i < dataGraph->GetN(); i++)
11465 tot += dataGraph->GetPointY(i);
11466 dataGraph->Scale(1. / tot);
11467 }
11468
11469 if (!hasSame) {
11470 clearPad();
11471 dataGraph->Draw("Az0p");
11472 addLegendEntry(dataGraph, strlen(dataGraph->GetTitle()) ? dataGraph->GetTitle() : GetName(), "pEX0");
11473 gPad->Modified();
11474 // gPad->Update();
11475 return;
11476 }
11477
11478 bool noPoint = false;
11479 if (v && dynamic_cast<RooAbsArg *>(v)->getAttribute("global") && dataGraph->GetN() == 1) {
11480 // global observable ... if graph has only 1 data point line it up on the histogram value
11481 for (auto o : *gPad->GetListOfPrimitives()) {
11482 if (auto h = dynamic_cast<TH1 *>(o);
11483 h && strcmp(h->GetXaxis()->GetName(), dynamic_cast<TObject *>(v)->GetName()) == 0) {
11484 dataGraph->SetPointY(0, h->Interpolate(dataGraph->GetPointX(0)));
11485 noPoint = true;
11486 break;
11487 }
11488 }
11489 }
11490
11491 if (auto _pad = dynamic_cast<TPad *>(gPad->FindObject("auxPad")); _pad) {
11492 if (auto h = dynamic_cast<TH1 *>(_pad->GetPrimitive("auxHist")); h) {
11493 TString histName = h->GetTitle(); // split it by | char
11494 TString histType = histName(histName.Index('|') + 1, histName.Length());
11495 histName = histName(0, histName.Index('|'));
11496 if (auto mainHist = dynamic_cast<TH1 *>(gPad->GetPrimitive(histName));
11497 mainHist && auxFunctions.find(h->GetYaxis()->GetTitle()) != auxFunctions.end()) {
11498 // decide what to do based on title of auxHist (previously used name of y-axis but that changed axis
11499 // behaviour) use title instead
11500 auto ratioGraph = dynamic_cast<TGraphAsymmErrors *>(dataGraph->Clone(dataGraph->GetName()));
11501 ratioGraph->SetBit(kCanDelete);
11502 for (int i = 0; i < ratioGraph->GetN(); i++) {
11503 double val = ratioGraph->GetPointY(i);
11504 int binNum = mainHist->FindFixBin(ratioGraph->GetPointX(i));
11505 double nom = mainHist->GetBinContent(binNum);
11506 double nomerr = mainHist->GetBinError(binNum);
11507 double yval =
11508 std::get<0>(auxFunctions[h->GetYaxis()->GetTitle()])(ratioGraph->GetPointY(i), nom, nomerr);
11509 double yup = std::get<0>(auxFunctions[h->GetYaxis()->GetTitle()])(val + ratioGraph->GetErrorYhigh(i),
11510 nom, nomerr) -
11511 yval;
11512 double ydown = yval - std::get<0>(auxFunctions[h->GetYaxis()->GetTitle()])(
11513 val - ratioGraph->GetErrorYlow(i), nom, nomerr);
11514 if (!std::isnan(yval)) {
11515 ratioGraph->SetPointY(i, yval);
11516 if (!std::isnan(yup))
11517 ratioGraph->SetPointEYhigh(i, yup);
11518 if (!std::isnan(ydown))
11519 ratioGraph->SetPointEYlow(i, ydown);
11520 }
11521 }
11522 // remove the zero points
11523 int i = 0;
11524 while (i < ratioGraph->GetN()) {
11525 if (ratioGraph->GetPointY(i) == 0 && ratioGraph->GetErrorYhigh(i) == 0 &&
11526 ratioGraph->GetErrorYlow(i) == 0) {
11527 ratioGraph->RemovePoint(i);
11528 } else {
11529 i++;
11530 }
11531 }
11532 auto _tmpPad = gPad;
11533 _pad->cd();
11534 ratioGraph->Draw("z0psame");
11535 auto minMax = graphMinMax(ratioGraph);
11536 adjustYRange(minMax.first, minMax.second, h, std::get<1>(auxFunctions[h->GetYaxis()->GetTitle()]));
11537 _tmpPad->cd();
11538 }
11539 }
11540 }
11541
11542 dataGraph->Draw("z0p same");
11543 addLegendEntry((noPoint) ? nullptr : dataGraph, strlen(dataGraph->GetTitle()) ? dataGraph->GetTitle() : GetName(),
11544 noPoint ? "" : "pEX0");
11545
11546 auto minMax = graphMinMax(dynamic_cast<TGraphAsymmErrors *>(dataGraph));
11547 adjustYRange(minMax.first, minMax.second);
11548
11549 gPad->Modified();
11550 // gPad->Update();
11551 return;
11552 }
11553
11554 // auto _ax = GetXaxis();
11555 // auto v = (_ax) ? dynamic_cast<RooRealVar*>(/*possibleObs.first()*/_ax->GetParent()) : nullptr;
11556 // if (!v) { v = get<RooRealVar>(); } // self-axis
11557 // if (!v) return;
11558
11559 if (auto lv = get<RooAbsLValue>(); lv && fParent && fParent->get<RooAbsData>()) {
11560 // drawing an observable from a dataset ... build graph, and exit
11561 auto gr = fParent->BuildGraph(lv, true);
11563 gr->Draw(hasSame ? "P" : "AP");
11564 return;
11565 }
11566
11567 if (forceNames != "") {
11568 // drawing a force plot ... build nll and fill a histogram with force terms
11569 auto _dsets = datasets();
11570 bool _drawn = false;
11571 auto _coords = coords();
11572 auto _fr = fitResult();
11573 auto initPar = dynamic_cast<RooRealVar *>(_fr.get<RooFitResult>()->floatParsInit().find(forceNames));
11574 if (!initPar)
11575 return;
11576 std::vector<double> valuesToDo = {initPar->getVal()};
11577 if (initPar->hasError() || initPar->hasAsymError()) {
11578 valuesToDo.push_back(initPar->getVal() + initPar->getErrorLo());
11579 valuesToDo.push_back(initPar->getVal() + initPar->getErrorHi());
11580 }
11581 int ii = 0;
11582 for (auto valueToDo : valuesToDo) {
11583 ii++;
11584 for (auto &d : _dsets) {
11585 if (!d->get()->TestBit(1 << 20))
11586 continue;
11587 auto emptyHist = BuildHistogram(v, true);
11588 emptyHist->SetBit(kCanDelete);
11589 auto _obs = d->obs();
11590 auto x = _obs.find((v) ? dynamic_cast<TObject *>(v)->GetName() : emptyHist->GetXaxis()->GetName());
11591 auto _nll = nll(d);
11592 auto theData = d->get<RooAbsData>();
11593 int nevent = theData->numEntries();
11594 for (int i = 0; i < nevent; i++) {
11595 theData->get(i);
11596 bool _skip = false;
11597 for (const auto &_c : _coords) {
11598 if (auto cat = _c->get<RooAbsCategoryLValue>(); cat) {
11599 if (cat->getIndex() != theData->get()->getCatIndex(cat->GetName())) {
11600 _skip = true;
11601 break;
11602 }
11603 }
11604 }
11605 if (_skip)
11606 continue;
11607
11608 if (x) {
11609 auto val = _nll.pars()->getRealValue(initPar->GetName());
11610 if (ii > 1)
11611 _nll.pars()->setRealValue(initPar->GetName(), valueToDo);
11612 auto nllVal = _nll.getEntryVal(i);
11613 _nll.pars()->setRealValue(initPar->GetName(), initPar->getVal());
11614 auto nllVal2 = _nll.getEntryVal(i);
11615 _nll.pars()->setRealValue(initPar->GetName(), val);
11616 emptyHist->Fill(x->get<RooAbsReal>()->getVal(), (nllVal2 - nllVal));
11617 }
11618 }
11619 // include the extendedTerm, distributed evenly over the bins
11620 // probably should be somehow dependent on data density though (i.e. bins with more data get more of it?)
11621 auto val = _nll.pars()->getRealValue(initPar->GetName());
11622 if (ii > 1)
11623 _nll.pars()->setRealValue(initPar->GetName(), valueToDo);
11624 auto _extTerm = _nll.extendedTermVal();
11625 _nll.pars()->setRealValue(initPar->GetName(), initPar->getVal());
11626 auto _extTerm2 = _nll.extendedTermVal();
11627 _nll.pars()->setRealValue(initPar->GetName(), val);
11628 for (int i = 1; i <= emptyHist->GetNbinsX(); i++) {
11629 emptyHist->SetBinContent(i,
11630 emptyHist->GetBinContent(i) + (_extTerm2 - _extTerm) / emptyHist->GetNbinsX());
11631 emptyHist->SetBinError(i, 0);
11632 }
11633 emptyHist->GetYaxis()->SetTitle("log (L(#theta)/L(#theta_{0}))");
11634 emptyHist->SetTitle(TString::Format("#theta = %g", (ii > 1) ? valueToDo : val));
11635 if (ii == 1)
11636 emptyHist->SetLineColor(kBlack);
11637 if (ii == 2) {
11638 emptyHist->SetLineColor(kRed);
11639 } else if (ii == 3) {
11640 emptyHist->SetLineColor(kBlue);
11641 }
11642 emptyHist->Draw(_drawn ? "same" : "");
11643 _drawn = true;
11644 }
11645 }
11646 return;
11647 }
11648
11649 auto rar = get<RooAbsReal>();
11650 const xRooNode *rarNode = this;
11651 if (!rar) {
11652 // draw a deleteable clone of the object we wrap (since we might own the object)
11653 get()->DrawClone(opt);
11654 return;
11655 }
11656 // RooAbsReal *sf = nullptr;
11657 if (get()->InheritsFrom("RooExtendPdf")) {
11658 browse();
11659 rarNode = find(".pdf").get();
11660 // rar = rarNode->get<RooAbsReal>();
11661 // sf = find(".n")->get<RooAbsReal>();
11662 }
11663
11664 if (!nostack && !hasOverlay &&
11665 (rarNode->get()->InheritsFrom("RooRealSumPdf") || rarNode->get()->InheritsFrom("RooAddPdf") ||
11666 (v && rarNode->get()->InheritsFrom("RooSimultaneous") &&
11667 strcmp(dynamic_cast<TObject *>(v)->GetName(), rarNode->get<RooSimultaneous>()->indexCat().GetName()) == 0))) {
11668 nostack = false;
11669 } else {
11670 // in all other cases, we do not build a stack
11671 nostack = true;
11672 }
11673
11674 auto h = BuildHistogram(v, false, hasErrorOpt, 1, 0, "", false, false, 0, nullptr, nostack, true /*setInterp*/);
11675 if (!h) {
11676 if (get()) {
11677 // draw a deleteable clone of the object we wrap (since we might own the object)
11678 get()->DrawClone(opt);
11679 }
11680 return;
11681 }
11683
11684 if (!v)
11685 v = getObject<RooAbsLValue>(h->GetXaxis()->IsAlphanumeric() ? h->GetXaxis()->GetTimeFormatOnly()
11686 : h->GetXaxis()->GetName())
11687 .get();
11688 RooAbsArg *vv = (v) ? dynamic_cast<RooAbsArg *>(v) : rar;
11689 if (h->GetXaxis()->IsAlphanumeric()) {
11690 // do this to get bin labels
11691 h->GetXaxis()->SetName("xaxis"); // WARNING -- this messes up anywhere we GetXaxis()->GetName()
11692 }
11693
11694 // get style now, before we mess with histogram title
11695 // auto _styleNode = styles(h);
11696
11697 if (rar->InheritsFrom("RooAbsPdf") && !(rar->InheritsFrom("RooRealSumPdf") || rar->InheritsFrom("RooAddPdf") ||
11698 rar->InheritsFrom("RooSimultaneous"))) {
11699 // append parameter values to title if has such
11700 RooArgSet s;
11701 rar->leafNodeServerList(&s);
11702 if (v)
11703 s.remove(*dynamic_cast<RooAbsArg *>(v));
11704 if (!s.empty()) {
11705 TString ss = h->GetTitle();
11706 ss += " [";
11707 bool first = true;
11708 for (auto _p : s) {
11709 auto _v = dynamic_cast<RooRealVar *>(_p);
11710 if (!_v)
11711 continue;
11712 if (!first)
11713 ss += ",";
11714 first = false;
11715 ss += TString::Format("%s=%g", strlen(_p->GetTitle()) ? _p->GetTitle() : _p->GetName(), _v->getVal());
11716 if (_v->hasError()) {
11717 ss += TString::Format("#pm %g", _v->getError());
11718 }
11719 }
11720 ss += "]";
11721 h->SetTitle(ss);
11722 }
11723 }
11724
11725 if (!hasSame) {
11726 if (obs().find(vv->GetName())) {
11727 gPad->SetGrid(0, 0);
11728 } else {
11729 gPad->SetGrid(1, 1);
11730 }
11731 }
11732 TString dOpt = h->GetOption();
11733 if (dOpt == "l")
11734 h->SetFillStyle(0);
11735 // // need to strip namespace to discount the "HistFactory" namespace classes from all being treated as binned
11736 // TString clNameNoNamespace = rar->ClassName();
11737 // clNameNoNamespace = clNameNoNamespace(clNameNoNamespace.Last(':') + 1, clNameNoNamespace.Length());
11738 // TString dOpt = (clNameNoNamespace.Contains("Hist") || vv->isCategory() || rar->isBinnedDistribution(*vv) ||
11739 // h->GetNbinsX() == 1 || rar->getAttribute("BinnedLikelihood") ||
11740 // (dynamic_cast<RooAbsRealLValue *>(vv) &&
11741 // std::unique_ptr<std::list<double>>(rar->binBoundaries(*dynamic_cast<RooAbsRealLValue *>(vv),
11742 // -std::numeric_limits<double>::infinity(),
11743 // std::numeric_limits<double>::infinity()))))
11744 // ? ""
11745 // : "LF2";
11746 // if (auto d = dynamic_cast<RooHistFunc *>(rar); d && !d->isBinnedDistribution(*vv) && h->GetNbinsX() != 1) {
11747 // dOpt = "LF2"; // hist func is interpolated, so draw it as such
11748 // }
11749 // if (dOpt == "LF2" && !components().empty()) {
11750 // // check if all components of dOpt are "Hist" type (CMS model support)
11751 // // if so then dOpt="";
11752 // bool allHist = true;
11753 // for (auto &s : components()) {
11754 // TString _clName = s->get()->ClassName();
11755 // _clName = _clName(_clName.Last(':') + 1, _clName.Length());
11756 // if (!(s->get() && _clName.Contains("Hist"))) {
11757 // allHist = false;
11758 // break;
11759 // }
11760 // }
11761 // if (allHist)
11762 // dOpt = "";
11763 // }
11764 //
11765 // if(dOpt=="LF2") {
11766 // // ensure any sub hists have lf2 option
11767 // TObjLink *lnk = h->GetListOfFunctions()->FirstLink();
11768 // while (lnk) {
11769 // if(auto hh = dynamic_cast<TH1*>(lnk->GetObject())) {
11770 // if(TString(hh->GetName())=="band" && TString(lnk->GetOption())=="e2same") {
11771 // lnk->SetOption("LF2 e3same");
11772 // } else if(TString(hh->GetName())=="nominal") {
11773 // lnk->SetOption("L same");
11774 // }
11775 // }
11776 // lnk = lnk->Next();
11777 // }
11778 // }
11779
11780 if (rar == vv && rar->IsA() == RooRealVar::Class()) {
11781 dOpt += "TEXT";
11782 // add a TExec to the histogram so that when edited it will propagate to var
11783 gROOT->SetEditHistograms(true);
11784 } else {
11785 gROOT->SetEditHistograms(false);
11786 }
11787
11788 if (hasSame) {
11789 dOpt += " same";
11790 } else {
11791 hAxis = h;
11792 }
11793
11794 if (dOpt.Contains("TEXT") || sOpt.Contains("text")) {
11795 // adjust marker size so text is good
11796 h->SetMarkerSize(gStyle->GetLabelSize("Z") / (0.02 * gPad->GetHNDC()));
11797 }
11798
11799 bool hasError(false);
11800 for (int i = 0; i < h->GetSumw2N(); i++) {
11801 if (h->GetSumw2()->At(i)) {
11802 hasError = true;
11803 break;
11804 }
11805 }
11806
11807 /** This doesn't seem necessary in at least 6.26 any more - pads seem adjusted on their own
11808 if (!hasSame && h->GetYaxis()->GetTitleFont()%10 == 2) {
11809 h->GetYaxis()->SetTitleOffset( gPad->GetLeftMargin() / gStyle->GetPadLeftMargin() );
11810 } */
11811 // don't this instead - dont want to leave as zero (auto) in case show aux plot
11812 if (!hasSame && h->GetYaxis()->GetTitleFont() % 10 == 2) {
11813 h->GetYaxis()->SetTitleOffset(1.);
11814 }
11815
11816 TH1 *errHist = nullptr;
11817
11818 if (!hasSame)
11819 clearPad();
11820
11821 if (rar->getAttribute("Logy")) {
11822 gPad->SetLogy(1);
11823 }
11824
11825 if (rar == vv && rar->IsA() == RooRealVar::Class()) {
11826 // add a TExec to the histogram so that when edited it will propagate to var
11827 // h->GetListOfFunctions()->Add(h->Clone("self"),"TEXTHIST");
11828 dOpt = "TEXT";
11829 auto node = new xRooNode(*this);
11830 auto _hist = (errHist) ? errHist : h;
11831 auto hCopy = (errHist) ? nullptr : dynamic_cast<TH1 *>(h->Clone());
11832 if (hCopy) {
11833 hCopy->Reset();
11834 hCopy->Add(_hist);
11835 hCopy->SetDirectory(nullptr);
11836 }
11837 _hist->GetListOfFunctions()->Add(node);
11838 _hist->GetListOfFunctions()->Add(new TExec(
11839 ".update",
11841 "gROOT->SetEditHistograms(true);auto h = dynamic_cast<TH1*>(gPad->GetPrimitive(\"%s\")); if(h) { double "
11842 "range= h->GetMaximum()-h->GetMinimum(); if(auto n "
11843 "= dynamic_cast<xRooNode*>(h->GetListOfFunctions()->FindObject(\"%s\")); n && "
11844 "n->TestBit(TObject::kNotDeleted) && n->get<RooRealVar>()->getVal() != h->GetBinContent(1)) {"
11845 "h->SetBinContent(1, "
11846 "TString::Format(\"%%.2g\",int(h->GetBinContent(1)/(range*0.01))*range*0.01).Atof());n->SetContent( "
11847 "h->GetBinContent(1) ); for(auto pp : *h->GetListOfFunctions()) if(auto hh = "
11848 "dynamic_cast<TH1*>(pp))hh->SetBinContent(1,h->GetBinContent(1));} if(h->GetBinContent(1)==0.) "
11849 "h->SetBinContent(1,range*0.005); gPad->Modified();gPad->Update(); }",
11850 _hist->GetName(), node->GetName())));
11851 if (errHist) {
11852 errHist->GetListOfFunctions()->Add(h, "TEXT HIST same");
11853 errHist->SetFillColor(h->GetLineColor());
11854 } else {
11855 hCopy->SetBit(kCanDelete);
11856 hCopy->SetFillStyle(0);
11857 _hist->GetListOfFunctions()->Add(hCopy, "TEXT HIST same");
11858 //_hist->SetBinError(1, 0);
11859 }
11860 _hist->SetStats(false);
11861 // if (_hist->GetBinContent(1)==0.) _hist->SetBinContent(1,(_hist->GetMaximum()-_hist->GetMinimum())*0.005);
11862 _hist->Draw(); //_hist->Draw(((hasError) ? "e2" : ""));
11863 gPad->Modified();
11864 return;
11865 }
11866
11867 bool overlayExisted = false;
11868 if (hasOverlay) {
11869 h->SetName(TString::Format("%s%s", h->GetName(), overlayName.Data()));
11870 if (auto existing = dynamic_cast<TH1 *>(gPad->GetPrimitive(h->GetName())); existing) {
11871 existing->Reset();
11872 existing->Add(h);
11873 delete h;
11874 h = existing;
11875 overlayExisted = true;
11876 } else {
11877 TString oldStyle = (rar && rar->getStringAttribute("style")) ? rar->getStringAttribute("style") : "";
11878 h->SetTitle(overlayName);
11879 // for overlays will take style from current gStyle before overriding with personal style
11880 // this ensures initial style will be whatever gStyle is, rather than whatever ours is
11881 static_cast<TAttLine &>(*h) = *gStyle;
11882 static_cast<TAttFill &>(*h) = *gStyle;
11883 static_cast<TAttMarker &>(*h) = *gStyle;
11884 h->SetFillStyle(0); // explicit default for overlays will be transparent fill
11885
11886 // std::shared_ptr<TStyle> style; // use to keep alive for access from GetStyle below, in case
11887 // getObject has decided to return the owning ptr (for some reason) if
11888 // (!gROOT->GetStyle(h->GetTitle())) {
11889 // if ( (style = getObject<TStyle>(h->GetTitle())) ) {
11890 // // loaded style (from workspace?) so put in list and use that
11891 // gROOT->GetListOfStyles()->Add(style.get());
11892 // } else {
11893 // // create new style - gets put in style list automatically so don't have to delete
11894 // // acquire them so saved to workspaces for auto reload ...
11895 // style = acquireNew<TStyle>(h->GetTitle(),
11896 // TString::Format("Style for %s component", h->GetTitle()));
11897 // (TAttLine &) (*style) = *dynamic_cast<TAttLine *>(h);
11898 // (TAttFill &) (*style) = *dynamic_cast<TAttFill *>(h);
11899 // (TAttMarker &) (*style) = *dynamic_cast<TAttMarker *>(h);
11900 // gROOT->GetListOfStyles()->Add(style.get());
11901 // }
11902 // }
11903 // (TAttLine&)(*h) = *(gROOT->GetStyle(h->GetTitle()) ? gROOT->GetStyle(h->GetTitle()) : gStyle);
11904 // (TAttFill&)(*h) = *(gROOT->GetStyle(h->GetTitle()) ? gROOT->GetStyle(h->GetTitle()) : gStyle);
11905 // (TAttMarker&)(*h) = *(gROOT->GetStyle(h->GetTitle()) ? gROOT->GetStyle(h->GetTitle()) : gStyle);
11906 auto _styleNode = styles(h);
11907 rar->setStringAttribute("style", oldStyle == "" ? nullptr : oldStyle.Data()); // restores old style
11908 if (auto _style = _styleNode.get<TStyle>()) {
11909 (TAttLine &)(*h) = *_style;
11910 (TAttFill &)(*h) = *_style;
11911 (TAttMarker &)(*h) = *_style;
11912 }
11913 h->Draw(dOpt == "LF2" ? "e3" : dOpt);
11914 if (errHist) {
11915 errHist->SetTitle(overlayName);
11916 (TAttLine &)(*errHist) = *h;
11917 errHist->SetFillColor(h->GetLineColor());
11918 }
11919 }
11920 } else {
11921 // if (auto _style = _styleNode.get<TStyle>()) {
11922 // (TAttLine &)(*h) = *_style;
11923 // (TAttFill &)(*h) = *_style;
11924 // (TAttMarker &)(*h) = *_style;
11925 // if (errHist) {
11926 // (TAttLine &)(*errHist) = *h;
11927 // errHist->SetFillColor(h->GetLineColor());
11928 // }
11929 // }
11930 h->Draw(dOpt);
11931 }
11932
11933 if (!hasOverlay && (rarNode->get()->InheritsFrom("RooRealSumPdf") || rarNode->get()->InheritsFrom("RooAddPdf") ||
11934 (rarNode->get()->InheritsFrom("RooSimultaneous") &&
11935 strcmp(vv->GetName(), rarNode->get<RooSimultaneous>()->indexCat().GetName()) == 0))) {
11936 if (auto stack = dynamic_cast<THStack *>(h->FindObject("stack"))) {
11937 // access the stack and set draw options, adjust ranges etc
11938 TObjLink *lnk = stack->GetHists()->FirstLink();
11939 while (lnk) {
11940 TH1 *hh = static_cast<TH1 *>(lnk->GetObject());
11941 // lnk->SetOption(dOpt); - not needed
11942 auto hhMin = (hh->GetMinimum() == 0) ? hh->GetMinimum(1e-9) : hh->GetMinimum();
11943 if (lnk == stack->GetHists()->FirstLink() && h->GetMinimum() > hhMin) {
11944 auto newMin = hhMin - (h->GetMaximum() - hhMin) * gStyle->GetHistTopMargin();
11945 if (hhMin >= 0 && newMin < 0)
11946 newMin = hhMin * 0.99;
11947 adjustYRange(newMin, h->GetMaximum());
11948 }
11949 addLegendEntry(hh, hh->GetTitle(), "f");
11950 lnk = lnk->Next();
11951 }
11952 }
11953
11954 // // build a stack unless not requested
11955 // if (!nostack) {
11956 // THStack *stack = new THStack(TString::Format("%s_stack", rar->GetName()),
11957 // TString::Format("%s;%s", rar->GetTitle(), h->GetXaxis()->GetTitle()));
11958 // int count = 2;
11959 // std::map<std::string, int> colorByTitle; // TODO: should fill from any existing legend
11960 // std::set<std::string> allTitles;
11961 // bool titleMatchName = true;
11962 // std::map<std::string, TH1 *> histGroups;
11963 // std::vector<TH1 *> hhs;
11964 // std::set<TH1 *> histsWithBadTitles; // these histograms will have their titles autoFormatted
11965 //
11966 // // support for CMS model case where has single component containing many coeffs
11967 // // will build stack by setting each coeff equal to 0 in turn, rebuilding the histogram
11968 // // the difference from the "full" histogram will be the component
11969 // RooArgList cms_coefs;
11970 // if (!rarNode->components().empty()) {
11971 // auto comps = rarNode->components()[0];
11972 // for (auto &c : *comps) {
11973 // if (c->fFolder == "!.coeffs")
11974 // cms_coefs.add(*c->get<RooAbsArg>());
11975 // }
11976 // }
11977 // if (!cms_coefs.empty()) {
11978 // RooRealVar zero("zero", "", 0);
11979 // std::shared_ptr<TH1> prevHist(static_cast<TH1 *>(h->Clone()));
11980 // for (auto c : cms_coefs) {
11981 // // seems I have to remake the function each time, as haven't figured out what cache needs
11982 // clearing? std::unique_ptr<RooAbsReal> f(
11983 // dynamic_cast<RooAbsReal *>(rarNode->components()[0]->get()->Clone("tmpCopy")));
11984 // zero.setAttribute(
11985 // Form("ORIGNAME:%s", c->GetName())); // used in redirectServers to say what this
11986 // replaces
11987 // f->redirectServers(RooArgSet(zero), false, true); // each time will replace one additional coef
11988 // // zero.setAttribute(Form("ORIGNAME:%s",c->GetName()),false); (commented out so that on next
11989 // iteration
11990 // // will still replace all prev)
11991 // auto hh = xRooNode(*f, *this).BuildHistogram(v);
11992 // hh->SetName(c->GetName());
11993 // if (sf)
11994 // hh->Scale(sf->getVal());
11995 // if (strlen(hh->GetTitle()) == 0) {
11996 // hh->SetTitle(c->GetName()); // ensure all hists has titles
11997 // histsWithBadTitles.insert(hh);
11998 // } else if (strcmp(hh->GetName(), hh->GetTitle()) == 0) {
11999 // histsWithBadTitles.insert(hh);
12000 // }
12001 // titleMatchName &= (TString(c->GetName()) == hh->GetTitle() ||
12002 // TString(hh->GetTitle()).BeginsWith(TString(c->GetName()) + "_"));
12003 // std::shared_ptr<TH1> nextHist(static_cast<TH1 *>(hh->Clone()));
12004 // hh->Add(prevHist.get(), -1.);
12005 // hh->Scale(-1.);
12006 // hhs.push_back(hh);
12007 // prevHist = nextHist;
12008 // }
12009 // } else if (get<RooSimultaneous>()) {
12010 // // need to create a histogram for each sample across all the channels - will rely on functionality
12011 // below to
12012 // // merge them based on titles
12013 //
12014 // for (auto &chan : bins()) {
12015 // TString chanName(chan->GetName());
12016 // chanName = chanName(chanName.Index("=") + 1, chanName.Length());
12017 // auto samps = chan->mainChild();
12018 // if (!samps)
12019 // samps = *chan;
12020 // for (auto &samp : samps.components()) {
12021 // auto hh = static_cast<TH1 *>(h->Clone(samp->GetName()));
12022 // hh->Reset();
12023 // hh->SetTitle(samp->GetTitle());
12024 // if (strlen(hh->GetTitle()) == 0) {
12025 // hh->SetTitle(samp->GetName());
12026 // histsWithBadTitles.insert(hh);
12027 // } else if (strcmp(hh->GetName(), hh->GetTitle()) == 0) {
12028 // histsWithBadTitles.insert(hh);
12029 // }
12030 // hh->SetTitle(TString(hh->GetTitle())
12031 // .ReplaceAll(TString(chan->get()->GetName()) + "_",
12032 // "")); // remove occurance of channelname_ in title (usually
12033 // prefix)
12034 // titleMatchName &= (TString(samp->GetName()) == hh->GetTitle() ||
12035 // TString(hh->GetTitle()).BeginsWith(TString(samp->GetName()) + "_"));
12036 // hh->SetBinContent(hh->GetXaxis()->FindFixBin(chanName), samp->GetContent());
12037 // hhs.push_back(hh);
12038 // }
12039 // }
12040 // } else {
12041 // for (auto &samp : rarNode->components()) {
12042 // auto hh = samp->BuildHistogram(v,false,false,1,0,"",false,false,0,h); // passing h to ensure
12043 // binning is the same for all subcomponent hists if (sf)
12044 // hh->Scale(sf->getVal());
12045 // hhs.push_back(hh);
12046 // if (strlen(hh->GetTitle()) == 0) {
12047 // hh->SetTitle(samp->GetName()); // ensure all hists has titles
12048 // histsWithBadTitles.insert(hh);
12049 // } else if (strcmp(hh->GetName(), hh->GetTitle()) == 0) {
12050 // histsWithBadTitles.insert(hh);
12051 // }
12052 // titleMatchName &= (TString(samp->GetName()) == hh->GetTitle() ||
12053 // TString(hh->GetTitle()).BeginsWith(TString(samp->GetName()) + "_"));
12054 // }
12055 // }
12056 //
12057 // if (!hhs.empty()) {
12058 // for (auto &hh : hhs) {
12059 // allTitles.insert(hh->GetTitle());
12060 // }
12061 //
12062 // // get common prefix to strip off only if all titles match names and
12063 // // any title is longer than 10 chars
12064 // size_t e = std::min(allTitles.begin()->size(), allTitles.rbegin()->size());
12065 // size_t ii = 0;
12066 // bool goodPrefix = false;
12067 // std::string commonSuffix;
12068 // if (titleMatchName && hhs.size() > 1) {
12069 // while (ii < e - 1 && allTitles.begin()->at(ii) == allTitles.rbegin()->at(ii)) {
12070 // ii++;
12071 // if (allTitles.begin()->at(ii) == '_' || allTitles.begin()->at(ii) == ' ')
12072 // goodPrefix = true;
12073 // }
12074 //
12075 // // find common suffix if there is one .. must start with a "_"
12076 // bool stop = false;
12077 // while (!stop && commonSuffix.size() < size_t(e - 1)) {
12078 // commonSuffix = allTitles.begin()->substr(allTitles.begin()->length() - commonSuffix.length() -
12079 // 1); for (auto &t : allTitles) {
12080 // if (!TString(t).EndsWith(commonSuffix.c_str())) {
12081 // commonSuffix = commonSuffix.substr(1);
12082 // stop = true;
12083 // break;
12084 // }
12085 // }
12086 // }
12087 // if (commonSuffix.find('_') == std::string::npos) {
12088 // commonSuffix = "";
12089 // } else {
12090 // commonSuffix = commonSuffix.substr(commonSuffix.find('_'));
12091 // }
12092 // }
12093 // if (!goodPrefix)
12094 // ii = 0;
12095 //
12096 // // also find how many characters are needed to distinguish all entries (that dont have the same
12097 // name)
12098 // // then carry on up to first space or underscore
12099 // size_t jj = 0;
12100 // std::map<std::string, std::string> reducedTitles;
12101 // while (reducedTitles.size() != allTitles.size()) {
12102 // jj++;
12103 // std::map<std::string, int> titlesMap;
12104 // for (auto &s : allTitles) {
12105 // if (reducedTitles.count(s))
12106 // continue;
12107 // titlesMap[s.substr(0, jj)]++;
12108 // }
12109 // for (auto &s : allTitles) {
12110 // if (titlesMap[s.substr(0, jj)] == 1 && (jj >= s.length() || s.at(jj) == ' ' || s.at(jj) ==
12111 // '_')) {
12112 // reducedTitles[s] = s.substr(0, jj);
12113 // }
12114 // }
12115 // }
12116 //
12117 // // strip common prefix and suffix before adding
12118 // for (auto ritr = hhs.rbegin(); ritr != hhs.rend(); ++ritr) { // go in reverse order
12119 // if (!histsWithBadTitles.count((*ritr))) {
12120 // continue;
12121 // }
12122 // auto _title = (hhs.size() > 5) ? reducedTitles[(*ritr)->GetTitle()] : (*ritr)->GetTitle();
12123 // _title = _title.substr(ii < _title.size() ? ii : 0);
12124 // if (!commonSuffix.empty() && TString(_title).EndsWith(commonSuffix.c_str()))
12125 // _title = _title.substr(0, _title.length() - commonSuffix.length());
12126 // (*ritr)->SetTitle(_title.c_str());
12127 // }
12128 // }
12129 //
12130 // for (auto &hh : hhs) {
12131 // // automatically group hists that all have the same title
12132 // if (histGroups.find(hh->GetTitle()) == histGroups.end()) {
12133 // histGroups[hh->GetTitle()] = hh;
12134 // } else {
12135 // // add it into this group
12136 // histGroups[hh->GetTitle()]->Add(hh);
12137 // delete hh;
12138 // hh = nullptr;
12139 // continue;
12140 // }
12141 // auto hhMin = (hh->GetMinimum() == 0) ? hh->GetMinimum(1e-9) : hh->GetMinimum();
12142 // if (!stack->GetHists() && h->GetMinimum() > hhMin) {
12143 // auto newMin = hhMin - (h->GetMaximum() - hhMin) * gStyle->GetHistTopMargin();
12144 // if (hhMin >= 0 && newMin < 0)
12145 // newMin = hhMin * 0.99;
12146 // adjustYRange(newMin, h->GetMaximum());
12147 // }
12148 //
12149 // /*if(stack->GetHists() && stack->GetHists()->GetEntries()>0) {
12150 // // to remove rounding effects on bin boundaries, see if binnings compatible
12151 // auto _h1 = dynamic_cast<TH1*>(stack->GetHists()->At(0));
12152 // if(_h1->GetNbinsX()==hh->GetNbinsX()) TODO ... finish dealing with silly rounding effects
12153 // }*/
12154 // TString thisOpt = dOpt;
12155 // // uncomment next line to blend continuous with discrete components .. get some unpleasant "poke
12156 // through"
12157 // // effects though
12158 // // if(auto s = samp->get<RooAbsReal>(); s) thisOpt =
12159 // s->isBinnedDistribution(*dynamic_cast<RooAbsArg*>(v)) ?
12160 // // "" : "LF2";
12161 // stack->Add(hh, thisOpt);
12162 // }
12163 // stack->SetBit(kCanDelete); // should delete its sub histograms
12164 // stack->Draw("noclear same");
12165 // h->Draw(
12166 // dOpt + sOpt +
12167 // "same"); // overlay again .. if stack would cover original hist (negative components) we still see
12168 // integral
12169 // h->Draw("axissame"); // redraws axis
12170 //
12171 // TList *ll = stack->GetHists();
12172 // if (ll && ll->GetEntries()) {
12173 //
12174 // // finally, ensure all hists are styled
12175 // for (auto ho : *ll) {
12176 // TH1 *hh = dynamic_cast<TH1 *>(ho);
12177 // if (!hh)
12178 // continue;
12179 // bool createdStyle = (xRooNode(*hh, *this).styles(nullptr, false).get<TStyle>() == nullptr);
12180 //
12181 // if (createdStyle) {
12182 // // give hist a color, that isn't the same as any other hists color
12183 // hh->SetFillStyle(1001); // solid fill style
12184 // bool used = false;
12185 // do {
12186 // hh->SetFillColor((count++));
12187 // // check not already used this color
12188 // used = false;
12189 // for (auto ho2 : *ll) {
12190 // TH1 *hh2 = dynamic_cast<TH1 *>(ho2);
12191 // if (!hh2)
12192 // continue;
12193 // auto _styleNode = xRooNode(*hh2, *this).styles(hh2, false);
12194 // auto _style = _styleNode.get<TStyle>();
12195 // if (hh != hh2 && _style && _style->GetFillColor() == hh->GetFillColor()) {
12196 // used = true;
12197 // break;
12198 // }
12199 // }
12200 // } while (used);
12201 // }
12202 //
12203 // auto _styleNode = xRooNode(*hh, *this).styles(hh);
12204 // if (auto _style = _styleNode.get<TStyle>()) {
12205 // *dynamic_cast<TAttLine *>(hh) = *_style;
12206 // *dynamic_cast<TAttFill *>(hh) = *_style;
12207 // *dynamic_cast<TAttMarker *>(hh) = *_style;
12208 // }
12209 // // for stacks, fill color of white should be color 10 unless fill style is 0
12210 // if (hh->GetFillColor() == kWhite && hh->GetFillStyle() != 0) {
12211 // // kWhite means 'transparent' in ROOT ... should really use a FillStyle of 0 for that
12212 // // so assume user wanted actual white, which is color 10
12213 // hh->SetFillColor(10);
12214 // }
12215 // addLegendEntry(hh, hh->GetTitle(), "f");
12216 // }
12217 // }
12218 // }
12219 } else if (!overlayExisted) {
12220
12221 if (errHist) {
12222 addLegendEntry(errHist, strlen(errHist->GetTitle()) ? errHist->GetTitle() : GetName(), "fl");
12223 } else {
12224 addLegendEntry(h, strlen(h->GetTitle()) ? h->GetTitle() : GetName(), (hasError) ? "fl" : "l");
12225 }
12226 }
12227
12228 if (errHist) {
12229 dOpt.ReplaceAll("TEXT", "");
12230 errHist->Draw(dOpt + (dOpt.Contains("LF2") ? "e3same" : "e2same"));
12231 double ymax = -std::numeric_limits<double>::infinity();
12232 double ymin = std::numeric_limits<double>::infinity();
12233 for (int i = 1; i <= errHist->GetNbinsX(); i++) {
12234 ymax = std::max(ymax, errHist->GetBinContent(i) + errHist->GetBinError(i));
12235 ymin = std::min(ymin, errHist->GetBinContent(i) - errHist->GetBinError(i));
12236 }
12237 adjustYRange(ymin, ymax);
12238 } else {
12239 adjustYRange(h->GetMinimum() * 0.9, h->GetMaximum() * 1.1);
12240 }
12241
12242 if ((!auxPlotTitle.empty()) && !hasSame) {
12243 // create a pad for the ratio ... shift the bottom margin of this pad to make space for it
12244 double padFrac = 0.3;
12245 auto _tmpPad = gPad;
12246 gPad->SetBottomMargin(padFrac);
12247 auto ratioPad = new TPad("auxPad", "aux plot", 0, 0, 1, padFrac);
12248 ratioPad->SetFillColor(_tmpPad->GetFillColor());
12249 ratioPad->SetNumber(1);
12250 ratioPad->SetBottomMargin(ratioPad->GetBottomMargin() / padFrac);
12251 ratioPad->SetTopMargin(0.04);
12252 ratioPad->SetLeftMargin(gPad->GetLeftMargin());
12253 ratioPad->SetRightMargin(gPad->GetRightMargin());
12254 ratioPad->cd();
12255 TH1 *ratioHist = dynamic_cast<TH1 *>((errHist) ? errHist->Clone("auxHist") : h->Clone("auxHist"));
12256 ratioHist->Reset();
12257 ratioHist->Add(h); // removes function list
12258 ratioHist->SetDirectory(nullptr);
12259 ratioHist->SetTitle((errHist) ? errHist->GetName()
12260 : h->GetName()); // abuse the title string to hold the name of the main hist
12261
12262 ratioHist->GetYaxis()->SetNdivisions(5, 0, 0);
12263 ratioHist->GetYaxis()->SetTitle(auxPlotTitle.c_str());
12264 ratioHist->SetTitle(
12265 TString::Format("%s|%s", ratioHist->GetTitle(),
12266 auxPlotTitle.c_str())); // used when plotting data (above) to decide what to calculate
12267 ratioHist->SetMaximum();
12268 ratioHist->SetMinimum(); // resets min and max
12269 ratioPad->SetGridy();
12270
12271 for (int i = 1; i <= ratioHist->GetNbinsX(); i++) {
12272 double val = ratioHist->GetBinContent(i);
12273 double err = ratioHist->GetBinError(i);
12274 ratioHist->SetBinContent(i, std::get<0>(auxFunctions[auxPlotTitle])(val, val, err));
12275 ratioHist->SetBinError(i, std::get<0>(auxFunctions[auxPlotTitle])(val + err, val, err) -
12276 ratioHist->GetBinContent(i));
12277 }
12278
12279 double rHeight = (1. - padFrac) / padFrac; //(_tmpPad->GetWNDC())/(gPad->GetHNDC());
12280 if (ratioHist->GetYaxis()->GetTitleFont() % 10 == 2) {
12281 ratioHist->GetYaxis()->SetTitleSize(ratioHist->GetYaxis()->GetTitleSize() * rHeight);
12282 ratioHist->GetYaxis()->SetLabelSize(ratioHist->GetYaxis()->GetLabelSize() * rHeight);
12283 ratioHist->GetXaxis()->SetTitleSize(ratioHist->GetXaxis()->GetTitleSize() * rHeight);
12284 ratioHist->GetXaxis()->SetLabelSize(ratioHist->GetXaxis()->GetLabelSize() * rHeight);
12285 ratioHist->GetYaxis()->SetTitleOffset(ratioHist->GetYaxis()->GetTitleOffset() / rHeight);
12286 } else {
12287#if ROOT_VERSION_CODE < ROOT_VERSION(6, 26, 00)
12288 ratioHist->GetYaxis()->SetTitleOffset(ratioHist->GetYaxis()->GetTitleOffset() / rHeight);
12289#endif
12290 }
12291 ratioHist->GetXaxis()->SetTickLength(ratioHist->GetXaxis()->GetTickLength() * rHeight);
12292 ratioHist->SetStats(false);
12293 ratioHist->SetBit(TH1::kNoTitle);
12294 ratioHist->SetBit(kCanDelete);
12295 if (errHist) {
12296 auto _h = dynamic_cast<TH1 *>(ratioHist->Clone("auxHist_clone"));
12297 _h->SetDirectory(nullptr);
12298 _h->SetFillColor(0);
12299 ratioHist->GetListOfFunctions()->Add(_h, "histsame");
12300 //_h->Draw("histsame");
12301 }
12302 ratioHist->GetListOfFunctions()->Add(new TExec(
12303 ".updateAxis",
12304 TString::Format("auto h1 = (TH1*)%p; auto h2 = (TH1*)%p; if(h2->GetXaxis()->GetFirst() != "
12305 "h1->GetXaxis()->GetFirst() || h1->GetXaxis()->GetLast()!=h2->GetXaxis()->GetLast()) "
12306 "{h2->GetXaxis()->SetRange(h1->GetXaxis()->GetFirst(),h1->GetXaxis()->GetLast());if(gPad) "
12307 "{gPad->GetCanvas()->Paint();gPad->GetCanvas()->Update();}}",
12308 (void *)ratioHist, (void *)(h))));
12309 ratioHist->Draw((errHist ? "e2" : ""));
12310
12311 _tmpPad->cd();
12312 ratioPad->Draw();
12313 } else if (auto ratioPad = dynamic_cast<TPad *>(gPad->GetPrimitive("auxPad")); hasSame && ratioPad) {
12314 // need to draw histogram in the ratio pad ...
12315 // if doing overlay need to update histogram
12316
12317 if (auto hr = dynamic_cast<TH1 *>(ratioPad->GetPrimitive("auxHist"));
12318 hr && auxFunctions.find(hr->GetYaxis()->GetTitle()) != auxFunctions.end()) {
12319 TString histName = hr->GetTitle(); // split it by | char
12320 TString histType = histName(histName.Index('|') + 1, histName.Length());
12321 histName = histName(0, histName.Index('|'));
12322
12323 if (auto hnom = dynamic_cast<TH1 *>(gPad->GetPrimitive(histName)); hnom) {
12324 h = dynamic_cast<TH1 *>(h->Clone(h->GetName()));
12325 h->SetDirectory(nullptr);
12326 h->SetBit(kCanDelete);
12327 for (int i = 1; i <= hnom->GetNbinsX(); i++) {
12328 double val = h->GetBinContent(i);
12329 double err = h->GetBinError(i);
12330 h->SetBinContent(i, std::get<0>(auxFunctions[hr->GetYaxis()->GetTitle()])(
12331 h->GetBinContent(i), hnom->GetBinContent(i), hnom->GetBinError(i)));
12332 h->SetBinError(i, std::get<0>(auxFunctions[hr->GetYaxis()->GetTitle()])(
12333 val + err, hnom->GetBinContent(i), hnom->GetBinError(i)) -
12334 h->GetBinContent(i));
12335 }
12336 auto _tmpPad = gPad;
12337 ratioPad->cd();
12338 if (hasOverlay) {
12339 if (auto existing = dynamic_cast<TH1 *>(ratioPad->GetPrimitive(h->GetName())); existing) {
12340 existing->Reset();
12341 existing->Add(h);
12342 delete h;
12343 h = existing;
12344 overlayExisted = true;
12345 } else {
12346 h->Draw(dOpt);
12347 }
12348 } else {
12349 h->Draw(dOpt);
12350 }
12351 double ymax = -std::numeric_limits<double>::infinity();
12352 double ymin = std::numeric_limits<double>::infinity();
12353 for (int i = 1; i <= h->GetNbinsX(); i++) {
12354 ymax = std::max(ymax, h->GetBinContent(i) + h->GetBinError(i));
12355 ymin = std::min(ymin, h->GetBinContent(i) - h->GetBinError(i));
12356 }
12357 adjustYRange(ymin, ymax, hr, std::get<1>(auxFunctions[hr->GetYaxis()->GetTitle()]));
12358 // adjustYRange(h->GetMinimum() * (h->GetMinimum()<0 ? 1.1 : 0.9), h->GetMaximum() * (h->GetMinimum()<0 ?
12359 // 0.9 : 1.1), hr, std::get<1>(auxFunctions[hr->GetYaxis()->GetTitle()]));
12360 gPad->Modified();
12361 _tmpPad->cd();
12362 }
12363 }
12364 }
12365
12366 // see if it's in a simultaneous so need to select a cat
12367 /*auto _parent = fParent;
12368 auto _me = rar;
12369 while(_parent) {
12370 if (auto s = _parent->get<RooSimultaneous>(); s) {
12371 for (auto c : s->indexCat()) {
12372 if (auto p = s->getPdf(c.first.c_str());_me==p) {
12373 gPad->SetName(c.first.c_str());
12374 break;
12375 }
12376 }
12377 break;
12378 }
12379 _me = _parent->get<RooAbsReal>();
12380 _parent = _parent->fParent;
12381 }*/
12382
12383 // now draw selected datasets on top if this was a pdf
12384 if (auto _pdf = get<RooAbsPdf>();
12385 !hasSame && _pdf /*&& (_pdf->canBeExtended() || robs().empty())*/ && coefs(true).empty()) {
12386 auto _dsets = datasets();
12387 // bool _drawn=false;
12388 for (auto &d : _dsets) {
12389 if (d->get()->TestBit(1 << 20)) {
12390 d->Draw("same");
12391 //_drawn=true;
12392 }
12393 }
12394 // if (!_drawn && !_dsets.empty()) _dsets[0]->Draw("same"); // always draw if has a dataset
12395 }
12396
12397 gPad->Modified();
12398 // gPad->Update();
12399 getLegend();
12400 gPad->Modified();
12401 // gPad->Update();
12402}
12403
12404void xRooNode::SaveAs(const char *filename, Option_t *option) const
12405{
12406 TString sOpt(option);
12407 sOpt.ToLower();
12408 TString sFilename(filename);
12409 TString objName = GetName();
12410 if (sFilename.Contains(".root:")) {
12411 objName = TString(sFilename(sFilename.Index(".root:") + 6, sFilename.Length()));
12412 sFilename = sFilename(0, sFilename.Index(".root:") + 5);
12413 }
12414
12415 if (auto pdf = get<RooAbsPdf>(); pdf) {
12416 // if saving a pdf, will put it inside a workspace, with its datasets, and a modelconfig
12417 // then save the workspace
12418 RooWorkspace w(objName, TString::Format("Workspace of %s", GetTitle()));
12419 xRooNode ws(w);
12420 auto addedPdf = ws.Add(*this);
12421 for (auto ds : datasets()) {
12422 ws.Add(*ds);
12423 }
12424 RooStats::ModelConfig mc("ModelConfig", GetTitle(), &w);
12425 mc.SetPdf(addedPdf->GetName());
12426 mc.SetObservables(*addedPdf.robs().get<RooArgList>());
12427 mc.SetGlobalObservables(*addedPdf.globs().get<RooArgList>());
12428 mc.SetNuisanceParameters(*addedPdf.np().get<RooArgList>());
12429 mc.SetParametersOfInterest(*addedPdf.poi().get<RooArgList>());
12430 ws.Add(mc);
12431
12432 // save the workspace
12434
12435 } else if (auto w = get<RooWorkspace>(); w) {
12436 // ensure the current color set is saved in the workspace
12437 w->import(*gROOT->GetListOfColors(), true);
12438
12439 if (sFilename.EndsWith(".json")) {
12440#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 26, 00)
12441 // stream with json tool
12442 RooJSONFactoryWSTool tool(*w);
12443 if (tool.exportJSON(sFilename.Data())) {
12444 Info("SaveAs", "%s saved to %s", w->GetName(), sFilename.Data());
12445 } else {
12446 Error("SaveAs", "Unable to save to %s", sFilename.Data());
12447 }
12448#else
12449 Error("SaveAs", "json format workspaces only in ROOT 6.26 onwards");
12450#endif
12451 return;
12452 }
12453
12454#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
12455 // before saving, clear the eocache of all owned nodes
12456 // because causes memory leak when read back in (workspace streamer immediately overwrites the caches)
12457 // fixed in: https://github.com/root-project/root/pull/12024
12458 for (auto &c : w->components()) {
12459 c->_eocache = nullptr;
12460 }
12461#endif
12462 // const_cast<Node2*>(this)->sterilize(); - tried this to reduce mem leak on readback but no improve
12463#if ROOT_VERSION_CODE < ROOT_VERSION(6, 38, 00)
12464 if (!w->writeToFile(sFilename, sOpt != "update")) {
12465#else
12466 if (w->writeToFile(sFilename, sOpt != "update")) {
12467#endif
12468 Info("SaveAs", "%s saved to %s", w->GetName(), sFilename.Data());
12469 // save any fitDatabase that is loaded in memory too
12470 // TODO: We should do this as well for SaveAs on a scan object
12471 std::function<void(TDirectory *, TDirectory *)> CopyDir;
12472
12473 CopyDir = [&](TDirectory *source, TDirectory *dest) {
12474 auto dir = dest->GetDirectory(source->GetName());
12475 if (!dir) {
12476 dir = dest->mkdir(source->GetName());
12477 }
12478 for (auto k : *source->GetListOfKeys()) {
12479 auto key = dynamic_cast<TKey *>(k);
12480 const char *classname = key->GetClassName();
12481 TClass *cl = gROOT->GetClass(classname);
12482 // std::cout << "processing " << key->GetName() << " " << classname << std::endl;
12483 if (!cl) {
12484 continue;
12485 } else if (cl->InheritsFrom(TDirectory::Class())) {
12486 CopyDir(source->GetDirectory(key->GetName()), dir);
12487 } else {
12488 // don't write object if it already exists
12489 if (dir->FindKey(key->GetName()))
12490 continue;
12491 // support FitConfigs ....
12492 if (strcmp(classname, "ROOT::Fit::FitConfig") == 0) {
12493 auto fc = key->ReadObject<ROOT::Fit::FitConfig>();
12494 dir->WriteObject(fc, key->GetName());
12495 delete fc;
12496 } else {
12497 TObject *obj = key->ReadObj();
12498 if (obj) {
12499 dir->WriteTObject(obj, key->GetName());
12500 delete obj;
12501 }
12502 }
12503 }
12504 }
12505 };
12506 if (gROOT->GetListOfFiles()) {
12507 for (auto key : *gROOT->GetListOfFiles()) {
12508 if (auto fitDb = dynamic_cast<TMemFile *>(key);
12509 fitDb /*&& TString(key->GetName()).BeginsWith("fitDatabase_")*/) {
12510 CopyDir(fitDb, std::make_unique<TFile>(sFilename, "UPDATE").get());
12511 Info("SaveAs", "Saved %s to %s", fitDb->GetName(), sFilename.Data());
12512 }
12513 }
12514 }
12515 } else {
12516 Error("SaveAs", "Unable to save to %s", sFilename.Data());
12517 }
12518#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
12519 // restore the cache to every node
12520 for (auto &c : w->components()) {
12521 c->setExpensiveObjectCache(w->expensiveObjectCache());
12522 }
12523#endif
12524 }
12525}
12526
12527double xRooNode::GetBinError(int bin, const xRooNode &fr, int nToys, bool errorsHi, bool errorsLo) const
12528{
12529 auto res = GetBinErrors(bin, bin, fr, nToys, errorsHi, errorsLo);
12530 if (res.empty())
12531 return std::numeric_limits<double>::quiet_NaN();
12532 return res.at(0);
12533}
12534
12535std::vector<double> xRooNode::contents() const
12536{
12537 std::vector<double> out;
12538 out.reserve(size());
12539 for (auto child : *this) {
12540 out.push_back(child->GetContent());
12541 }
12542 return out;
12543}
12544
12546{
12547
12548 auto _fr = fr.get<RooFitResult>();
12549
12550 if (!_fr) {
12551 return covariances(fitResult());
12552 }
12553
12554 auto rho = _fr->correlationMatrix();
12555
12556 TMatrixDSym out(size());
12557
12558 auto _pars = pars();
12559
12560 // 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]
12561 // - nu[theta_n_down]) consistent with propagatedError formula
12562
12563 for (int m = 0; m < rho.GetNrows(); m++) {
12564 auto p_m = dynamic_cast<RooRealVar *>(_fr->floatParsFinal().at(m));
12565 if (!p_m)
12566 continue; // skip categoricals
12567 auto _p = dynamic_cast<RooAbsRealLValue *>(_pars.get<RooArgList>()->find(p_m->GetName()));
12568 if (!_p)
12569 continue;
12570 auto tmp = _p->getVal();
12571 _p->setVal(p_m->getVal() + p_m->getErrorHi());
12572 auto nu_m = contents();
12573 _p->setVal(p_m->getVal() + p_m->getErrorLo());
12574 auto nu_m2 = contents();
12575 _p->setVal(tmp);
12576 for (int n = 0; n < rho.GetNrows(); n++) {
12577 auto p_n = dynamic_cast<RooRealVar *>(_fr->floatParsFinal().at(n));
12578 if (!p_n)
12579 continue; // skip categoricals
12580 auto _p2 = dynamic_cast<RooAbsRealLValue *>(_pars.get<RooArgList>()->find(p_n->GetName()));
12581 if (!_p2)
12582 continue;
12583 auto tmp2 = _p2->getVal();
12584 _p2->setVal(p_n->getVal() + p_n->getErrorHi());
12585 auto nu_n = (p_n == p_m) ? nu_m : contents();
12586 _p2->setVal(p_n->getVal() + p_n->getErrorLo());
12587 auto nu_n2 = (p_n == p_m) ? nu_m2 : contents();
12588 _p2->setVal(tmp2);
12589 for (int i = 0; i < out.GetNrows(); i++) {
12590 for (int j = 0; j < out.GetNrows(); j++) {
12591 out(i, j) += 0.25 * (nu_m[i] - nu_m2[i]) * rho(m, n) * (nu_n[j] - nu_n2[j]);
12592 }
12593 }
12594 }
12595 }
12596 return out;
12597}
12598
12599std::pair<double, double>
12600xRooNode::IntegralAndError(const xRooNode &fr, const char *rangeName, int nToys, bool errorsHi, bool errorsLo) const
12601{
12602 double out = 1.;
12603 double err = std::numeric_limits<double>::quiet_NaN();
12604
12605 std::unique_ptr<RooAbsCollection> _snap;
12606 RooArgList _pars;
12607 if (auto _fr = fr.get<RooFitResult>()) {
12608 _pars.add(pars().argList());
12609 _snap.reset(_pars.snapshot());
12610 _pars = _fr->floatParsFinal();
12611 _pars = _fr->constPars();
12612 }
12613
12614 auto _obs = obs();
12615 RooArgSet sobs(*_obs.get<RooArgList>()); // need to make explicit RooArgSet for ROOT 6.36 onwards
12616 auto _coefs = coefs(); // need here to keep alive owned RooProduct
12617 if (auto c = _coefs.get<RooAbsReal>(); c) {
12618 out = c->getVal(sobs); // assumes independent of observables!
12619 }
12620
12621 if (auto p = dynamic_cast<RooAbsPdf *>(get()); p) {
12622 // prefer to use expectedEvents for integrals of RooAbsPdf e.g. for RooProdPdf wont include constraint terms
12623 if (rangeName)
12624 p->setNormRange(rangeName);
12625#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 27, 00)
12627#endif
12628 out *= p->expectedEvents(*_obs.get<RooArgList>());
12629#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
12630 // improved normSet invalidity checking, so assuming no longer need this in 6.28 onwards
12631 p->_normSet = nullptr;
12632#endif
12633 err = GetBinError(-1, fr, nToys, errorsHi, errorsLo);
12634 if (rangeName)
12635 p->setNormRange(nullptr);
12636 } else if (auto p2 = dynamic_cast<RooAbsReal *>(get()); p2) {
12637 // only integrate over observables we actually depend on
12638 auto f = std::shared_ptr<RooAbsReal>(
12639 p2->createIntegral(*std::unique_ptr<RooArgSet>(p2->getObservables(*_obs.get<RooArgList>())),
12640 rangeName)); // did use x here before using obs
12641 RooProduct pr("int_x_coef", "int_x_coef",
12642 RooArgList(*f, !_coefs.get() ? RooFit::RooConst(1) : *_coefs.get<RooAbsReal>()));
12643 out *= f->getVal();
12644 err = xRooNode(pr, *this).GetBinError(-1, fr, nToys, errorsHi, errorsLo);
12645 sterilize(); // needed so that we can forget properly about the integral we just created (and are deleting)
12646 } else if (get<RooAbsData>()) {
12647 out = 0;
12648 auto vals = GetBinContents(1, 0); // returns all bins
12649 auto ax = (rangeName) ? GetXaxis() : nullptr;
12650 auto rv = (ax) ? dynamic_cast<RooRealVar *>(ax->GetParent()) : nullptr;
12651 auto cv = (ax && !rv) ? dynamic_cast<RooCategory *>(ax->GetParent()) : nullptr;
12652 int i = 0;
12653 for (auto &v : vals) {
12654 i++;
12655 if (rangeName) {
12656 if (rv && !rv->inRange(ax->GetBinCenter(i), rangeName))
12657 continue;
12658 if (cv && !cv->isStateInRange(rangeName, ax->GetBinLabel(i)))
12659 continue;
12660 }
12661 out += v;
12662 }
12663 err = 0; // should this be sqrt(sum(v^2)) or something similar
12664 } else {
12665 out = std::numeric_limits<double>::quiet_NaN();
12666 }
12667 if (_snap) {
12668 _pars.RooAbsCollection::operator=(*_snap);
12669 }
12670 return std::make_pair(out, err);
12671}
12672
12673std::vector<double>
12674xRooNode::GetBinErrors(int binStart, int binEnd, const xRooNode &_fr, int nToys, bool errorHi, bool errorLo) const
12675{
12676 // note: so far this method is inconsistent with the BuildHistogram in ways:
12677 // no projection over other variables
12678 // July2023: made RooRealSumPdf evaluate as a function if doesn't have a floor
12679 // but this method will still evaluate it as a pdf (uses PdfWrapper)
12680 // but can get away with it while added NaN recovery to getSimplePropagatedError to pickup raw values
12681
12682 if (fBinNumber != -1) {
12683 if (binStart != binEnd || !fParent) {
12684 throw std::runtime_error(TString::Format("%s is a bin - only has one value", GetName()));
12685 }
12686 return fParent->GetBinErrors(fBinNumber, fBinNumber, _fr, nToys, errorHi, errorLo);
12687 }
12688
12689 std::vector<double> out;
12690
12691 auto _hist = BuildHistogram(nullptr, true, true, binStart, binEnd, _fr, errorHi, errorLo, nToys);
12692 if (!_hist)
12693 return out;
12694 if (binEnd == 0) {
12695 binEnd = _hist->GetNbinsX();
12696 } else if (binEnd == binStart && binEnd == -1) {
12697 binStart = 1;
12698 binEnd = 1; // done an integral, so histogram has only 1 bin
12699 }
12700 for (int bin = binStart; bin <= binEnd; bin++) {
12701 out.push_back(((errorLo && !errorHi) ? (-1.) : 1.) *
12702 _hist->GetBinError(bin)); // using same convention as RooFit that Lo errors are negative
12703 }
12704 delete _hist;
12705 return out;
12706
12707 // auto o = dynamic_cast<RooAbsReal *>(get());
12708 // if (!o)
12709 // return out;
12710 //
12711 // std::shared_ptr<RooFitResult> fr = std::dynamic_pointer_cast<RooFitResult>(_fr.fComp);
12712 // //= dynamic_cast<RooFitResult*>( _fr.get<RooFitResult>() ? _fr->Clone() : fitResult()->Clone());
12713 //
12714 // auto _coefs = coefs();
12715 //
12716 // if (!fr) {
12717 // // need to ensure coefs, if any, are included in fit result retrieval so all pars are loaded
12718 // auto frn = (!_coefs.get() ? *this : xRooNode(RooProduct("tmp", "tmp", RooArgList(*o,
12719 // *_coefs.get<RooAbsReal>()))))
12720 // .fitResult();
12721 // if (strlen(_fr.GetName()))
12722 // frn = frn.reduced(_fr.GetName());
12723 //
12724 // // use name to reduce the fit result, if one given
12725 // fr = std::dynamic_pointer_cast<RooFitResult>(frn.fComp);
12726 // }
12727 //
12728 // if (!GETDMP(fr.get(), _finalPars)) {
12729 // fr->setFinalParList(RooArgList());
12730 // }
12731 //
12732 // /// Oct2022: No longer doing this because want to allow fitResult to be used to get partial error
12733 // // // need to add any floating parameters not included somewhere already in the fit result ...
12734 // // RooArgList l;
12735 // // for(auto& p : pars()) {
12736 // // auto v = p->get<RooRealVar>();
12737 // // if (!v) continue;
12738 // // if (v->isConstant()) continue;
12739 // // if (fr->floatParsFinal().find(v->GetName())) continue;
12740 // // if (fr->_constPars && fr->_constPars->find(v->GetName())) continue;
12741 // // l.add(*v);
12742 // // }
12743 // //
12744 // // if (!l.empty()) {
12745 // // RooArgList l2; l2.addClone(fr->floatParsFinal());
12746 // // l2.addClone(l);
12747 // // fr->setFinalParList(l2);
12748 // // }
12749 //
12750 // TMatrixTSym<double> *prevCov = static_cast<TMatrixTSym<double> *>(GETDMP(fr.get(), _VM));
12751 //
12752 // if (!prevCov || size_t(prevCov->GetNcols()) < fr->floatParsFinal().size()) {
12753 // TMatrixDSym cov(fr->floatParsFinal().size());
12754 // if (prevCov) {
12755 // for (int i = 0; i < prevCov->GetNcols(); i++) {
12756 // for (int j = 0; j < prevCov->GetNrows(); j++) {
12757 // cov(i, j) = (*prevCov)(i, j);
12758 // }
12759 // }
12760 // }
12761 // int i = 0;
12762 // for (auto &p : fr->floatParsFinal()) {
12763 // if (!prevCov || i >= prevCov->GetNcols()) {
12764 // cov(i, i) = pow(dynamic_cast<RooRealVar *>(p)->getError(), 2);
12765 // }
12766 // i++;
12767 // }
12768 // int covQualBackup = fr->covQual();
12769 // fr->setCovarianceMatrix(cov);
12770 // fr->setCovQual(covQualBackup);
12771 // }
12772 //
12773 // bool doBinWidth = false;
12774 // auto ax = (binStart == -1 && binEnd == -1) ? nullptr : GetXaxis();
12775 //
12776 // auto _obs = obs(); // may own an obs so keep alive here
12777 // RooArgList normSet = _obs.argList();
12778 // // to give consistency with BuildHistogram method, should be only the axis var if defined
12779 // if (ax) {
12780 // normSet.clear();
12781 // normSet.add(*dynamic_cast<RooAbsArg *>(ax->GetParent()));
12782 // }
12783 //
12784 // if (auto p = dynamic_cast<RooAbsPdf *>(o); ax && (p || _coefs.get() || o->getAttribute("density"))) {
12785 // // pdfs of samples embedded in a sumpdf (aka have a coef) will convert their density value to a content
12786 // doBinWidth = true;
12787 // }
12788 // if (binEnd == 0) {
12789 // if (ax) {
12790 // binEnd = ax->GetNbins();
12791 // } else {
12792 // binEnd = binStart;
12793 // }
12794 // }
12795 // for (int bin = binStart; bin <= binEnd; bin++) {
12796 // if (ax)
12797 // dynamic_cast<RooAbsLValue *>(ax->GetParent())->setBin(bin - 1, ax->GetName());
12798 // // if (!SetBin(bin)) { return out; }
12799 //
12800 // double res;
12801 // if (auto p = dynamic_cast<RooAbsPdf *>(o); p) {
12802 // // fr->covarianceMatrix().Print();
12803 // res = PdfWrapper(*p, _coefs.get<RooAbsReal>(), !ax).getSimplePropagatedError(*fr, normSet);
12804 // #if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
12805 // // improved normSet invalidity checking, so assuming no longer need this in 6.28 onwards
12806 // p->_normSet = nullptr;
12807 // #endif
12808 // } else {
12809 // // res = o->getPropagatedError(*fr, normSet);
12810 // // // TODO: What if coef has error? - probably need a FuncWrapper class
12811 // // if (auto c = _coefs.get<RooAbsReal>(); c) {
12812 // // res *= c->getVal(normSet);
12813 // // }
12814 // res = RooProduct("errorEval", "errorEval",
12815 // RooArgList(*o, !_coefs.get() ? RooFit::RooConst(1) : *_coefs.get<RooAbsReal>()))
12816 // .getPropagatedError(*fr, normSet);
12817 // }
12818 // if (doBinWidth) {
12819 // res *= ax->GetBinWidth(bin);
12820 // }
12821 // out.push_back(res);
12822 // }
12823 //
12824 // return out;
12825}
12826
12828
12829std::string cling::printValue(const XROOFIT_NAMESPACE_NAME::xRooNode *v)
12830{
12831 if (!v)
12832 return "nullptr\n";
12833 if (!v->empty()) {
12834 std::string out;
12835 size_t left = v->size();
12836 for (auto n : *v) {
12837 left--;
12838 if (!out.empty())
12839 out += ",";
12840 else
12841 out += "{";
12842 out += n->GetName();
12843 if (out.length() > 100 && left > 0) {
12844 out += TString::Format(",... and %lu more", left);
12845 break;
12846 }
12847 }
12848 out += "}\n";
12849 out = std::string(Form("<%s> %s", v->get() ? v->get()->ClassName() : "nullptr", v->GetName())) + out;
12850 return out;
12851 }
12852 std::string out;
12853 if (!(*v)) {
12854 return "<nullptr>";
12855 } else {
12856 return Form("<%s> %s", v->get() ? v->get()->ClassName() : "nullptr", v->GetName());
12857 }
12858
12859 return out;
12860}
@ kButton1Down
Definition Buttons.h:17
#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:26
#define ROOT_VERSION_CODE
Definition RVersion.hxx:27
size_t size(const MatrixT &matrix)
retrieve the size of a square matrix
const char Option_t
Option string (const char)
Definition RtypesCore.h:81
@ kGray
Definition Rtypes.h:65
@ kRed
Definition Rtypes.h:66
@ kBlack
Definition Rtypes.h:65
@ kGreen
Definition Rtypes.h:66
@ kWhite
Definition Rtypes.h:65
@ kCyan
Definition Rtypes.h:66
@ kBlue
Definition Rtypes.h:66
@ kAzure
Definition Rtypes.h:67
@ kYellow
Definition Rtypes.h:66
@ kP10Blue
Definition Rtypes.h:71
static void indent(ostringstream &buf, int indent_level)
R__EXTERN TEnv * gEnv
Definition TEnv.h:126
void Warning(const char *location, const char *msgfmt,...)
Use this function in warning situations.
Definition TError.cxx:252
#define gClient
Definition TGClient.h:157
@ kMBOk
Definition TGMsgBox.h:33
@ kMBIconExclamation
Definition TGMsgBox.h:24
winID h TVirtualViewer3D TVirtualGLPainter p
winID h TVirtualViewer3D vv
Option_t Option_t option
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void data
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t dest
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t winding char text const char depth char const char Int_t count const char ColorStruct_t color const char filename
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t winding char text const char depth char const char Int_t count const char ColorStruct_t color const char Pixmap_t Pixmap_t PictureAttributes_t attr const char char ret_data h unsigned char height h offset
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t np
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t r
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t result
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t child
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void value
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void funcs
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t winding char text const char depth char const char Int_t count const char ColorStruct_t color const char Pixmap_t Pixmap_t PictureAttributes_t attr const char char ret_data h unsigned char height h Atom_t Int_t ULong_t ULong_t unsigned char prop_list Atom_t Atom_t Atom_t Time_t type
Option_t Option_t style
char name[80]
Definition TGX11.cxx:142
float xmin
#define hi
float * q
float ymin
float xmax
float ymax
@ kCanDelete
Definition TObject.h:375
#define gROOT
Definition TROOT.h:417
char * Form(const char *fmt,...)
Formats a string in a circular formatting buffer.
Definition TString.cxx:2571
R__EXTERN TStyle * gStyle
Definition TStyle.h:442
R__EXTERN TSystem * gSystem
Definition TSystem.h:582
TVectorT< Double_t > TVectorD
Definition TVectorDfwd.h:23
#define gPad
#define _(A, B)
Definition cfortran.h:112
double GetBinLowEdge(Int_t bin) const override
Return low edge of bin.
Definition xRooNode.cxx:970
double GetBinUpEdge(Int_t bin) const override
Return up edge of bin.
Definition xRooNode.cxx:978
Int_t FindFixBin(double x) const override
Find bin number corresponding to abscissa x
void Set(Int_t nbins, const double *xbins) override
Initialize axis with variable bins.
Definition xRooNode.cxx:998
RooAbsRealLValue * rvar() const
void SetTitle(const char *title) override
Set the title of the TNamed.
Definition xRooNode.cxx:989
void Set(Int_t nbins, double xmin, double xmax) override
Initialize axis with fix bins.
Int_t FindFixBin(const char *label) const override
Find bin number with label.
RooAbsLValue * var() const
const RooAbsBinning * binning() const
void Set(Int_t nbins, const float *xbins) override
Initialize axis with variable bins.
const char * GetTitle() const override
Returns title of object.
Definition xRooNode.cxx:985
double GetBinWidth(Int_t bin) const override
Return bin width.
Definition xRooNode.cxx:964
PadRefresher(TVirtualPad *p)
static int nExisting
A class which maps the current values of a RooRealVar (or a set of RooRealVars) to one of a number of...
bool isBinnedDistribution(const RooArgSet &obs) const override
Tests if the distribution is binned. Unless overridden by derived classes, this always returns false.
bool selfNormalized() const override
Shows if a PDF is self-normalized, which means that no attempt is made to add a normalization term.
~PdfWrapper() override
double getSimplePropagatedError(const RooFitResult &fr, const RooArgSet &nset_in) const
RooRealProxy fCoef
double evaluate() const override
Evaluate this PDF / function / constant. Needs to be overridden by all derived classes.
TObject * clone(const char *newname) const override
std::list< double > * binBoundaries(RooAbsRealLValue &obs, double xlo, double xhi) const override
Retrieve bin boundaries if this distribution is binned in obs.
PdfWrapper(const PdfWrapper &other, const char *name=nullptr)
RooRealProxy fExpPdf
PdfWrapper(RooAbsReal &f, RooAbsReal *coef, bool expEvMode=false, RooAbsPdf *expPdf=nullptr)
RooRealProxy fFunc
The PiecewiseInterpolation is a class that can morph distributions into each other,...
static std::shared_ptr< RooLinkedList > createNLLOptions()
Definition xRooFit.cxx:468
static std::pair< std::shared_ptr< RooAbsData >, std::shared_ptr< const RooAbsCollection > > generateFrom(RooAbsPdf &pdf, const RooFitResult &fr, bool expected=false, int seed=0)
Definition xRooFit.cxx:154
static xRooNLLVar createNLL(const std::shared_ptr< RooAbsPdf > pdf, const std::shared_ptr< RooAbsData > data, const RooLinkedList &nllOpts)
Definition xRooFit.cxx:105
static std::pair< double, double > matchPrecision(const std::pair< double, double > &in)
Definition xRooFit.cxx:2096
void Draw(Option_t *opt="") override
Default Draw method for all objects.
std::shared_ptr< xRooHypoPoint > asimov(bool readOnly=false)
std::shared_ptr< const RooFitResult > ufit(bool readOnly=false)
std::shared_ptr< const RooFitResult > cfit_null(bool readOnly=false)
std::shared_ptr< const RooFitResult > cfit_alt(bool readOnly=false)
std::shared_ptr< const RooFitResult > gfit()
Definition xRooNLLVar.h:179
void Draw(Option_t *opt="") override
Default Draw method for all objects.
This xRooNLLVar object has several special methods, e.g.
Definition xRooNLLVar.h:59
xRooHypoSpace hypoSpace(const char *parName, int nPoints, double low, double high, double alt_value=std::numeric_limits< double >::quiet_NaN(), const xRooFit::Asymptotics::PLLType &pllType=xRooFit::Asymptotics::Unknown, int tsType=0)
The xRooNode class is designed to wrap over a TObject and provide functionality to aid with interacti...
Definition xRooNode.h:52
void _Add_(const char *name, const char *opt)
TH1 * BuildHistogram(RooAbsLValue *v=nullptr, bool empty=false, bool errors=false, int binStart=1, int binEnd=0, const xRooNode &fr="", bool errorsHi=false, bool errorsLo=false, int nErrorToys=0, TH1 *templateHist=nullptr, bool nostack=true, bool setInterp=false) const
xRooNode filter(const xRooNode &range) const
std::vector< std::shared_ptr< xRooNode > > fBrowsables
Definition xRooNode.h:551
xRooNLLVar nll(const xRooNode &_data, std::initializer_list< RooCmdArg > nllOpts) const
xRooNode poi() const
List of parameters of interest: parameters marked as "of interest" These parameters have the "poi" at...
void SetName(const char *name) override
Set the name of the TNamed.
TGListTreeItem * GetTreeItem(TBrowser *b) const
void SetTitle(const char *title) override
Set the title of the TNamed.
xRooNode Multiply(const xRooNode &child, Option_t *opt="")
xRooNode Replace(const xRooNode &node)
bool SetData(const TObject &obj, const xRooNode &data="obsData")
xRooNode Remove(const xRooNode &child)
xRooNode globs() const
List of global observables of this node.
xRooNode Add(const xRooNode &child, Option_t *opt="")
xRooNode(const char *type, const char *name, const char *title="")
const char * GetIconName() const override
Returns mime type name of object.
void Draw(Option_t *opt="") override
Default Draw method for all objects.
TGraph * BuildGraph(RooAbsLValue *v=nullptr, bool includeZeros=false, TVirtualPad *fromPad=nullptr) const
const std::shared_ptr< xRooNode > & at(size_t idx, bool browseResult=true) const
Definition xRooNode.h:147
std::string fRange
! only here so can have char* GetRange return so can return nullptr for no range set !...
Definition xRooNode.h:535
xRooNode reduced(const std::string &range="", bool invert=false) const
void _fit_(const char *constParValues="", const char *options="GoF")
bool SetBinContent(int bin, double value, const char *par=nullptr, double parVal=1)
std::shared_ptr< xRooNode > operator[](size_t idx)
Definition xRooNode.h:164
void Checked(TObject *obj, bool val)
Definition xRooNode.cxx:550
void Print(Option_t *opt="") const override
Print TNamed name and title.
std::shared_ptr< T > acquireNew(Args &&...args)
Definition xRooNode.h:290
std::shared_ptr< T > acquire2(Args &&...args)
Definition xRooNode.h:284
void SaveAs(const char *filename="", Option_t *option="") const override
Save this object in the file specified by filename.
xRooNode bins() const
bins of a channel or sample, or channels of a multi-channel pdf
std::shared_ptr< xRooNode > find(const std::string &name, bool browseResult=true) const
TGListTree * GetListTree(TBrowser *b) const
xRooNode styles(TObject *initObject=nullptr, bool autoCreate=true) const
xRooNode generate(const xRooNode &fr="", bool expected=false, int seed=0)
void Inspect() const override
Dump contents of this object in a graphics canvas.
std::shared_ptr< xRooNode > getBrowsable(const char *name) const
bool SetXaxis(const RooAbsBinning &binning)
double GetBinError(int bin, const xRooNode &fr="", int nToys=0, bool errorsHi=false, bool errorsLo=false) const
bool SetBinData(int bin, double value, const xRooNode &data="obsData")
std::vector< double > GetBinContents(int binStart=1, int binEnd=0) const
xRooNode pp() const
List of prespecified parameters: non-floatable parameters.
double GetError(const xRooNode &fr="", int nToys=0, bool errorsHi=false, bool errorsLo=false) const
Definition xRooNode.h:439
std::shared_ptr< xRooNode > fProvider
! like a parent but only for use by getObject
Definition xRooNode.h:544
std::shared_ptr< TStyle > style(TObject *initObject=nullptr, bool autoCreate=true) const
xRooNode fitResult(const char *opt="") const
std::shared_ptr< TAxis > fXAxis
! appears that if was fXaxis then dialog box for SetXaxis will take as current value
Definition xRooNode.h:539
xRooNode coords(bool setVals=true) const
xRooNode Vary(const xRooNode &child)
xRooNode Constrain(const xRooNode &child)
static std::map< std::string, std::tuple< std::function< double(double, double, double)>, bool > > auxFunctions
Definition xRooNode.h:58
xRooNode pars() const
List of parameters (non-observables) of this node.
void SetFitResult(const RooFitResult *fr=nullptr)
xRooNode coefs(bool recurse=false) const
bool IsFolder() const override
Returns kTRUE in case object contains browsable objects (like containers or lists of other objects).
Definition xRooNode.cxx:949
double GetBinData(int bin, const xRooNode &data="obsData")
bool SetBinError(int bin, double value)
bool SetContents(const TObject &obj)
Definition xRooNode.h:369
std::shared_ptr< TObject > fComp
!
Definition xRooNode.h:526
std::pair< double, double > IntegralAndError(const xRooNode &fr="", const char *rangeName=nullptr, int nToys=0, bool errorsHi=false, bool errorsLo=false) const
std::vector< double > contents() const
RooWorkspace * ws() const
The RooWorkspace this node belong to, if any.
xRooNode shallowCopy(const std::string &name, std::shared_ptr< xRooNode > parent=nullptr)
void _generate_(const char *name="", bool expected=false)
std::shared_ptr< TObject > getObject(const std::string &name, const std::string &type="") const
xRooNode np() const
List of nuisance parameters: non-constant parameters that are not marked of interest,...
xRooNode Combine(const xRooNode &rhs, bool silent=false)
xRooNode floats() const
List of parameters that are currently non-constant These parameters do not have the "Constant" attrib...
bool contains(const std::string &name) const
double GetBinContent(int bin) const
Definition xRooNode.h:411
std::vector< double > GetBinErrors(int binStart=1, int binEnd=0, const xRooNode &fr="", int nToys=0, bool errorsHi=false, bool errorsLo=false) const
auto begin() const -> xRooNodeIterator
Definition xRooNode.h:200
std::shared_ptr< xRooNode > parentPdf() const
void _scan_(const char *what="plr", double nToys=0, const char *xvar="", int nPointsX=0, double lowX=0, double highX=0, const char *constParValues="", const char *options="")
std::shared_ptr< xRooNode > fParent
!
Definition xRooNode.h:529
xRooNode robs() const
List of regular observables of this node.
TClass * IsA() const override
Definition xRooNode.h:557
xRooNode & operator=(const TObject &o)
auto end() const -> xRooNodeIterator
Definition xRooNode.h:217
xRooNode consts() const
List of parameters that are currently constant.
void _SetBinContent_(int bin, double value, const char *par="", double parVal=1)
std::shared_ptr< TObject > acquire(const std::shared_ptr< TObject > &arg, bool checkFactory=false, bool mustBeNew=false)
std::function< xRooNode(xRooNode *) fBrowseOperation)
Definition xRooNode.h:552
void _SetAttribute_(const char *name, const char *value=nullptr)
Definition xRooNode.cxx:514
xRooNode histo(const xRooNode &vars="x", const xRooNode &fr="", bool content=true, bool errors=true, bool stack=true, bool errorsHi=false, bool errorsLo=false, int nErrorToys=0) const
void Browse(TBrowser *b=nullptr) override
Browse object. May be overridden for another default action.
Definition xRooNode.cxx:665
xRooNode obs() const
List of observables (global and regular) of this node.
void SetRange(const char *range, double low=std::numeric_limits< double >::quiet_NaN(), double high=std::numeric_limits< double >::quiet_NaN())
static void SetAuxFunction(const char *title, const std::function< double(double, double, double)> &func, bool symmetrize=false)
Definition xRooNode.cxx:222
std::shared_ptr< TObject > convertForAcquisition(xRooNode &acquirer, const char *opt="") const
xRooNode vars() const
List of variables (observables and parameters) of this node.
TMatrixDSym covariances(const xRooNode &fr="") const
Class describing the configuration of the fit, options and parameter settings using the ROOT::Fit::Pa...
Definition FitConfig.h:49
Common abstract base class for objects that represent a value and a "shape" in RooFit.
Definition RooAbsArg.h:77
bool dependsOn(const RooAbsCollection &serverList, const RooAbsArg *ignoreArg=nullptr, bool valueOnly=false) const
Test whether we depend on (ie, are served by) any object in the specified collection.
virtual bool isCategory() const
Definition RooAbsArg.h:502
void setStringAttribute(const Text_t *key, const Text_t *value)
Associate string 'value' to this object under key 'key'.
RooFit::OwningPtr< RooArgSet > getParameters(const RooAbsData *data, bool stripDisconnected=true) const
Create a list of leaf nodes in the arg tree starting with ourself as top node that don't match any of...
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.
void setAttribute(const Text_t *name, bool value=true)
Set (default) or clear a named boolean attribute of this object.
TObject * Clone(const char *newname=nullptr) const override
Make a clone of an object using the Streamer facility.
Definition RooAbsArg.h:89
virtual bool isFundamental() const
Is this object a fundamental type that can be added to a dataset? Fundamental-type subclasses overrid...
Definition RooAbsArg.h:176
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...
double getRealValue(const char *name, double defVal=0.0, bool verbose=false) const
Get value of a RooAbsReal stored in set with given name.
virtual void removeAll()
Remove all arguments from our set, deleting them if we own them.
virtual bool remove(const RooAbsArg &var, bool silent=false, bool matchByNameOnly=false)
Remove the specified argument from our list.
RooAbsCollection & assignValueOnly(const RooAbsCollection &other, bool forceIfSizeOne=false)
Sets the value of any argument in our set that also appears in the other set.
RooAbsCollection * snapshot(bool deepCopy=true) const
Take a snap shot of current collection contents.
virtual bool add(const RooAbsArg &var, bool silent=false)
Add the specified argument to list.
Int_t index(const RooAbsArg *arg) const
Returns index of given arg, or -1 if arg is not in the collection.
Storage_t::size_type size() const
bool setRealValue(const char *name, double newVal=0.0, bool verbose=false)
Set value of a RooAbsRealLValue stored in set with given name to newVal No error messages are printed...
virtual RooAbsArg * addClone(const RooAbsArg &var, bool silent=false)
Add a clone of the specified argument to list.
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:55
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.
virtual Int_t numEntries() const
Return number of entries in dataset, i.e., count unweighted entries.
RooArgSet const * getGlobalObservables() const
Returns snapshot of global observables stored in this data.
Definition RooAbsData.h:272
Abstract base class for objects that are lvalues, i.e.
virtual std::list< std::string > getBinningNames() const =0
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...
bool hasRange(const char *name) const override
Check if variable has a binning with given name.
Context to temporarily change the error logging mode as long as the context is alive.
Definition RooAbsReal.h:317
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:32
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
RooArgSet * snapshot(bool deepCopy=true) const
Use RooAbsCollection::snapshot(), but return as RooArgSet.
Definition RooArgSet.h:159
RooArgSet * selectByAttrib(const char *name, bool value) const
Use RooAbsCollection::selectByAttrib(), but return as RooArgSet.
Definition RooArgSet.h:144
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:48
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:29
bool isBinnedDistribution(const RooArgSet &) const override
Tests if the distribution is binned. Unless overridden by derived classes, this always returns false.
Definition RooHistFunc.h:93
When using RooFit, statistical models can be conveniently handled and stored as a RooWorkspace.
bool importJSON(std::string const &filename)
Imports a JSON file from the given filename to the workspace.
bool exportJSON(std::string const &fileName)
Export the workspace to JSON format and write to the specified file.
Collection class for internal use, storing a collection of RooAbsArg pointers in a doubly linked list...
Int_t GetSize() const
TObject * At(int index) const
Return object stored in sequential position given by index.
virtual void Add(TObject *arg)
TIterator * MakeIterator(bool forward=true) const
Create a TIterator for this list.
static RooMsgService & instance()
Return reference to singleton instance.
StreamConfig & getStream(Int_t id)
void setGlobalKillBelow(RooFit::MsgLevel level)
RooFit::MsgLevel globalKillBelow() const
Implementation of a RooCacheManager<RooAbsCacheElement> that specializes in the storage of cache elem...
Poisson pdf.
Definition RooPoisson.h:18
Efficient implementation of a product of PDFs of the form.
Definition RooProdPdf.h:35
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()
double getError() const
Definition RooRealVar.h:59
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
void SetParametersOfInterest(const RooArgSet &set)
Specify parameters of interest.
void SetObservables(const RooArgSet &set)
Specify the observables.
void SetPdf(const RooAbsPdf &pdf)
Set the Pdf, add to the workspace if not already there.
Definition ModelConfig.h:87
void SetNuisanceParameters(const RooArgSet &set)
Specify the nuisance parameters (parameters that are not POI).
void SetGlobalObservables(const RooArgSet &set)
Specify the global observables.
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.
const std::map< std::string, RooArgSet > & sets() const
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.
const Double_t * GetArray() const
Definition TArrayD.h:43
virtual void SetTitleOffset(Float_t offset=1)
Set distance between the axis and the axis title.
Definition TAttAxis.cxx:279
virtual Style_t GetTitleFont() const
Definition TAttAxis.h:48
virtual void SetLabelSize(Float_t size=0.04)
Set size of axis labels.
Definition TAttAxis.cxx:184
virtual void SetTitleSize(Float_t size=0.04)
Set size of axis title.
Definition TAttAxis.cxx:290
virtual Float_t GetTitleSize() const
Definition TAttAxis.h:45
virtual void SetTickSize(Float_t size=0.03)
Definition TAttAxis.h:60
virtual Float_t GetLabelSize() const
Definition TAttAxis.h:42
virtual Float_t GetTickLength() const
Definition TAttAxis.h:46
virtual Float_t GetTitleOffset() const
Definition TAttAxis.h:44
virtual void SetTickLength(Float_t length=0.03)
Set tick mark length.
Definition TAttAxis.cxx:265
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 Color_t GetFillColor() const
Return the fill area color.
Definition TAttFill.h:32
virtual Style_t GetFillStyle() const
Return the fill area style.
Definition TAttFill.h:33
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
virtual void SetLineColor(Color_t lcolor)
Set the line color.
Definition TAttLine.h:44
Marker Attributes class.
Definition TAttMarker.h:22
virtual Size_t GetMarkerSize() const
Return the marker size.
Definition TAttMarker.h:36
virtual void SetMarkerStyle(Style_t mstyle=1)
Set the marker style.
virtual void SetMarkerSize(Size_t msize=1)
Set the marker size.
virtual void SetTextAlign(Short_t align=11)
Set the text alignment.
Definition TAttText.h:48
virtual Font_t GetTextFont() const
Return the text font.
Definition TAttText.h:38
virtual void SetTextColor(Color_t tcolor=1)
Set the text color.
Definition TAttText.h:50
virtual void SetTextFont(Font_t tfont=62)
Set the text font.
Definition TAttText.h:52
virtual void SetTextSize(Float_t tsize=1)
Set the text size.
Definition TAttText.h:53
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:674
virtual void SetBinLabel(Int_t bin, const char *label)
Set label for bin.
Definition TAxis.cxx:898
Bool_t IsAlphanumeric() const
Definition TAxis.h:90
Bool_t IsVariableBinSize() const
Definition TAxis.h:144
const char * GetTitle() const override
Returns title of object.
Definition TAxis.h:137
TAxis()
Default constructor.
Definition TAxis.cxx:50
const TArrayD * GetXbins() const
Definition TAxis.h:138
Double_t GetXmax() const
Definition TAxis.h:142
const char * GetBinLabel(Int_t bin) const
Return label for bin.
Definition TAxis.cxx:444
virtual void Set(Int_t nbins, Double_t xmin, Double_t xmax)
Initialize axis with fix bins.
Definition TAxis.cxx:797
virtual Int_t FindFixBin(Double_t x) const
Find bin number corresponding to abscissa x
Definition TAxis.cxx:422
virtual const char * GetTimeFormatOnly() const
Return only the time format from the string fTimeFormat.
Definition TAxis.cxx:611
Double_t GetXmin() const
Definition TAxis.h:141
Int_t GetNbins() const
Definition TAxis.h:127
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:1536
TClass instances represent classes, structs and namespaces in the ROOT type system.
Definition TClass.h:84
Longptr_t GetDataMemberOffset(const char *membername) const
return offset for member name.
Definition TClass.cxx:3544
Bool_t InheritsFrom(const char *cl) const override
Return kTRUE if this class inherits from a class with name "classname".
Definition TClass.cxx:4995
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:2999
void SetName(const char *name)
virtual Int_t GetEntries() const
virtual void RemoveAll(TCollection *col)
Remove all objects in collection col from this collection.
static Int_t GetColorPalette(Int_t i)
Static function returning the color number i in current palette.
Definition TColor.cxx:1522
static void InitializeColors()
Initialize colors used by the TCanvas based graphics (via TColor objects).
Definition TColor.cxx:1185
static Int_t GetNumberOfColors()
Static function returning number of colors in the color palette.
Definition TColor.cxx:1542
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 TDirectory * GetDirectory(const char *namecycle, Bool_t printError=false, const char *funcname="GetDirectory")
Find a directory using apath.
virtual TList * GetListOfKeys() const
Definition TDirectory.h:224
virtual Int_t GetValue(const char *name, Int_t dflt) const
Returns the integer value for a resource.
Definition TEnv.cxx:511
virtual void SetValue(const char *name, const char *value, EEnvLevel level=kEnvChange, const char *type=nullptr)
Set the value of a resource or create a new resource.
Definition TEnv.cxx:753
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.
void AddFSDirectory(const char *entry, const char *path=nullptr, Option_t *opt="")
Add file system directory in the list tree.
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:2910
virtual void ImportAxisAttributes(TAxis *axis)
Internal method to import TAxis attributes to this TGaxis.
Definition TGaxis.cxx:953
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:2386
Int_t GetN() const
Definition TGraph.h:131
void SetName(const char *name="") override
Set graph name.
Definition TGraph.cxx:2425
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:1457
virtual Double_t GetPointY(Int_t i) const
Get y value for point i.
Definition TGraph.cxx:1584
void SetTitle(const char *title="") override
Change (i.e.
Definition TGraph.cxx:2441
virtual void SetPointX(Int_t i, Double_t x)
Set x value for point i.
Definition TGraph.cxx:2410
virtual void SetEditable(Bool_t editable=kTRUE)
if editable=kFALSE, the graph cannot be modified with the mouse by default a TGraph is editable
Definition TGraph.cxx:2338
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:9170
virtual Double_t GetBinCenter(Int_t bin) const
Return bin center for 1D histogram.
Definition TH1.cxx:9371
void SetTitle(const char *title) override
Change/set the title.
Definition TH1.cxx:6932
virtual Int_t GetNbinsY() const
Definition TH1.h:542
virtual Double_t GetBinError(Int_t bin) const
Return value of error associated to bin number bin.
Definition TH1.cxx:9293
@ kNoTitle
Don't draw the histogram title.
Definition TH1.h:408
virtual void Reset(Option_t *option="")
Reset this histogram: contents, errors, etc.
Definition TH1.cxx:7324
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:4008
virtual Int_t GetNbinsX() const
Definition TH1.h:541
virtual void SetMaximum(Double_t maximum=-1111)
Definition TH1.h:652
virtual Bool_t Add(TF1 *h1, Double_t c1=1, Option_t *option="")
Performs the operation: this = this + c1*f1 if errors are defined (see TH1::Sumw2),...
Definition TH1.cxx:852
virtual void SetBinError(Int_t bin, Double_t error)
Set the bin Error Note that this resets the bin eror option to be of Normal Type and for the non-empt...
Definition TH1.cxx:9436
virtual Int_t Fill(Double_t x)
Increment bin with abscissa X by 1.
Definition TH1.cxx:3489
TAxis * GetYaxis()
Definition TH1.h:572
void Draw(Option_t *option="") override
Draw this histogram with options.
Definition TH1.cxx:3193
virtual void SetMinimum(Double_t minimum=-1111)
Definition TH1.h:653
virtual void SetBinContent(Int_t bin, Double_t content)
Set bin content see convention for numbering bins in TH1::GetBin In case the bin number is greater th...
Definition TH1.cxx:9452
TList * GetListOfFunctions() const
Definition TH1.h:488
virtual Double_t GetBinContent(Int_t bin) const
Return content of bin number bin.
Definition TH1.cxx:5239
virtual void Scale(Double_t c1=1, Option_t *option="")
Multiply this histogram by a constant c1.
Definition TH1.cxx:6815
TObject * Clone(const char *newname="") const override
Make a complete copy of the underlying object.
Definition TH1.cxx:2882
virtual Bool_t Divide(TF1 *f1, Double_t c1=1)
Performs the operation: this = this/(c1*f1) if errors are defined (see TH1::Sumw2),...
Definition TH1.cxx:2969
virtual Double_t GetMinimum(Double_t minval=-FLT_MAX) const
Return minimum value larger than minval of bins in the range, unless the value has been overridden by...
Definition TH1.cxx:8868
virtual void Sumw2(Bool_t flag=kTRUE)
Create structure to store sum of squares of weights.
Definition TH1.cxx:9253
virtual void SetStats(Bool_t stats=kTRUE)
Set statistics option on/off.
Definition TH1.cxx:9223
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:368
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
TLegendEntry * AddEntry(const TObject *obj, const char *label="", Option_t *option="lpf")
Add a new entry to this legend.
Definition TLegend.cxx:319
void SetNColumns(Int_t nColumns)
Set the number of columns for the legend.
Definition TLegend.cxx:605
void SetMargin(Float_t margin)
Definition TLegend.h:69
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
TObject * FindObject(const char *name) const override
Find an object in this list using its name.
Definition TList.cxx:708
void Add(TObject *obj) override
Definition TList.h:81
Int_t GetNrows() const
Int_t GetNcols() 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
void Clear(Option_t *option="") override
Set name and title to empty strings ("").
Definition TNamed.cxx:63
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:569
virtual const char * GetName() const
Returns name of object.
Definition TObject.cxx:461
R__ALWAYS_INLINE Bool_t TestBit(UInt_t f) const
Definition TObject.h:204
virtual TObject * Clone(const char *newname="") const
Make a clone of an object using the Streamer facility.
Definition TObject.cxx:242
virtual const char * ClassName() const
Returns name of class to which the object belongs.
Definition TObject.cxx:226
virtual void Warning(const char *method, const char *msgfmt,...) const
Issue warning message.
Definition TObject.cxx:1082
virtual TObject * DrawClone(Option_t *option="") const
Draw a clone of this object in the current selected pad with: gROOT->SetSelectedPad(c1).
Definition TObject.cxx:318
virtual TObject * FindObject(const char *name) const
Must be redefined in derived classes.
Definition TObject.cxx:424
virtual void SaveAs(const char *filename="", Option_t *option="") const
Save this object in the file specified by filename.
Definition TObject.cxx:707
virtual void Delete(Option_t *option="")
Delete this object.
Definition TObject.cxx:267
void SetBit(UInt_t f, Bool_t set)
Set or unset the user status bits as specified in f.
Definition TObject.cxx:886
virtual Bool_t InheritsFrom(const char *classname) const
Returns kTRUE if object inherits from class "classname".
Definition TObject.cxx:548
virtual void Error(const char *method, const char *msgfmt,...) const
Issue error message.
Definition TObject.cxx:1096
virtual const char * GetTitle() const
Returns title of object.
Definition TObject.cxx:506
virtual void Draw(Option_t *option="")
Default Draw method for all objects.
Definition TObject.cxx:292
virtual void Print(Option_t *option="") const
This method must be overridden when a class wants to print itself.
Definition TObject.cxx:660
@ 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:1070
The most important graphics class in the ROOT system.
Definition TPad.h:32
TVirtualPad * cd(Int_t subpadnumber=0) override
Set Current pad.
Definition TPad.cxx:757
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 SetMargin(Float_t margin=0.05)
Definition TPaveText.h:62
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 SetName(const char *name="")
Definition TPave.h:81
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:26
void SetEntryVal(Int_t, Double_t)
Set the value of a slice.
Definition TPie.cxx:1180
Int_t GetEntries() const
Definition TPie.h:92
void SetEntryFillColor(Int_t, Int_t)
Set the color for the slice "i".
Definition TPie.cxx:1156
void SetRadius(Double_t)
Set the pie chart's radius' value.
Definition TPie.cxx:1262
void SetEntryLabel(Int_t, const char *text="Slice")
Set slice number "i" label.
Definition TPie.cxx:1123
void Draw(Option_t *option="l") override
Draw the pie chart.
Definition TPie.cxx:295
/**
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
Double_t RealTime()
Stop the stopwatch (if it is running) and return the realtime (in seconds) passed between the start a...
void Start(Bool_t reset=kTRUE)
Start the stopwatch.
void Stop()
Stop the stopwatch.
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:137
Ssiz_t Length() const
Definition TString.h:426
Bool_t IsDec() const
Returns true if all characters in string are decimal digits (0-9).
Definition TString.cxx:2021
void ToLower()
Change string to lower-case.
Definition TString.cxx:1190
Int_t Atoi() const
Return integer value of string.
Definition TString.cxx:2069
Bool_t EndsWith(const char *pat, ECaseCompare cmp=kExact) const
Return true if string ends with the specified string.
Definition TString.cxx:2325
Double_t Atof() const
Return floating-point value contained in string.
Definition TString.cxx:2135
Bool_t IsFloat() const
Returns kTRUE if string contains a floating point or integer number.
Definition TString.cxx:1939
TString & Replace(Ssiz_t pos, Ssiz_t n, const char *s)
Definition TString.h:704
const char * Data() const
Definition TString.h:385
TString & ReplaceAll(const TString &s1, const TString &s2)
Definition TString.h:714
Bool_t IsAlpha() const
Returns true if all characters in string are alphabetic.
Definition TString.cxx:1879
Ssiz_t Last(char c) const
Find last occurrence of a character c.
Definition TString.cxx:939
Bool_t BeginsWith(const char *s, ECaseCompare cmp=kExact) const
Definition TString.h:633
Int_t CountChar(Int_t c) const
Return number of times character c occurs in the string.
Definition TString.cxx:523
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:2460
Bool_t Contains(const char *pat, ECaseCompare cmp=kExact) const
Definition TString.h:642
Ssiz_t Index(const char *pat, Ssiz_t i=0, ECaseCompare cmp=kExact) const
Definition TString.h:661
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:1296
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:1318
virtual Bool_t ProcessEvents()
Process pending events (GUI, timers, sockets).
Definition TSystem.cxx:419
The TTimeStamp encapsulates seconds and ns since EPOCH.
Definition TTimeStamp.h:45
void Add(const TTimeStamp &offset)
Add "offset" as a delta time.
const char * AsString(const Option_t *option="") const
Return the date & time as a string.
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:53
virtual void Modified(Bool_t flag=1)=0
virtual TList * GetListOfPrimitives() const =0
virtual void SetPad(const char *name, const char *title, Double_t xlow, Double_t ylow, Double_t xup, Double_t yup, Color_t color=35, Short_t bordersize=5, Short_t bordermode=-1)=0
virtual TVirtualPad * cd(Int_t subpadnumber=0)=0
const char * GetName() const override=0
Returns name of object.
virtual TVirtualPad * GetPad(Int_t subpadnumber) const =0
virtual void Divide(Int_t nx=1, Int_t ny=1, Float_t xmargin=0.01, Float_t ymargin=0.01, Int_t color=0)=0
virtual Int_t GetLogy() const =0
virtual void SetLogy(Int_t value=1)=0
virtual TObject * GetPrimitive(const char *name) const =0
virtual void SetBorderSize(Short_t bordersize)=0
virtual void SetName(const char *name)=0
static TClass * Class()
TClass * IsA() const override
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
int main()
Definition main.cpp:18
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:683
The namespace RooFit contains mostly switches that change the behaviour of functions of PDFs (or othe...
Definition CodegenImpl.h:73
static constexpr auto NumIntegration
Alias of MsgLevel::NumericIntegration for backwards compatibility.
MsgLevel
Verbosity level for RooMsgService::StreamConfig in RooMsgService.
@ NumericIntegration
Double_t Prob(Double_t chi2, Int_t ndf)
Computation of the probability for a certain Chi-squared (chi2) and number of degrees of freedom (ndf...
Definition TMath.cxx:637
Double_t ChisquareQuantile(Double_t p, Double_t ndf)
Evaluate the quantiles of the chi-squared probability distribution function.
Definition TMath.cxx:2196
Double_t StdDev(Long64_t n, const T *a, const Double_t *w=nullptr)
Same as RMS.
Definition TMath.h:536
std::string GetName(TCppScope_t)
#define BEGIN_XROOFIT_NAMESPACE
Definition Config.h:24
#define END_XROOFIT_NAMESPACE
Definition Config.h:25
static const char * what
Definition stlLoader.cc:5
RooAbsBinning * b
RooRealVar * x
void removeTopic(RooFit::MsgTopic oldTopic)
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:2335
#define GETWS(a)
#define GETWSSETS(w)
void(* gOldHandlerr)(int)
void addLegendEntry(TObject *o, const char *title, const char *opt)
void buildHistogramInterrupt(int signum)
auto GETACTBROWSER(TRootBrowser *b)
Definition xRooNode.cxx:111
auto & GETWSSNAPSHOTS(RooWorkspace *w)
Definition xRooNode.cxx:107
bool TopRightPlaceBox(TPad *p, TObject *o, double w, double h, double &xl, double &yb)
#define GETDMP(o, m)
Definition xRooNode.cxx:123
TPaveText * getPave(const char *name="labels", bool create=true, bool doPaint=false)
std::string formatLegendString(const std::string &s)
auto GETROOTDIR(TGFileBrowser *b)
Definition xRooNode.cxx:115
const xRooNode * runningNode
auto GETLISTTREE(TGFileBrowser *b)
Definition xRooNode.cxx:119
const T & _or_func(const T &a, const T &b)
Definition xRooNode.cxx:229
std::vector< double > new_getPropagatedErrors(const RooAbsReal &f, const RooFitResult &fr, const RooArgSet &nset, RooArgList **pars, bool asymHi, bool asymLo, const std::vector< int > &bins, const std::function< void(int)> &setBin)
TLegend * getLegend(bool create=true, bool doPaint=false)