Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
xRooNode.cxx
Go to the documentation of this file.
1/*
2 * Project: xRooFit
3 * Author:
4 * Will Buttinger, RAL 2022
5 *
6 * Copyright (c) 2022, CERN
7 *
8 * Redistribution and use in source and binary forms,
9 * with or without modification, are permitted according to the terms
10 * listed in LICENSE (http://roofit.sourceforge.net/license.txt)
11 */
12
13/** \class ROOT::Experimental::XRooFit::xRooNode
14\ingroup xroofit
15
16The xRooNode class is designed to wrap over a TObject and provide functionality to aid with interacting with that
17object, particularly in the case where the object is a RooFit class instance. It is a smart pointer to the object, so
18you have access to all the methods of the object too.
19
20xRooNode is designed to work in both python and C++, but examples below are given in python because that is imagined
21 be the most common way to use the xRooFit API.
22
23-# [Exploring workspaces](\ref exploring-workspaces)
24
25\anchor exploring-workspaces
26## Exploring workspaces
27
28An existing workspace file (either a ROOT file containing a RooWorkspace, or a json HS3 file) can be opened using
29 xRooNode like this:
30
31\code{.py}
32from ROOT.Experimental import XRooFit
33w = XRooFit.xRooNode("workspace.root") # or can use workspace.json for HS3
34\endcode
35
36 You can explore the content of the workspace somewhat like you would a file system: each node contains sub-nodes,
37 which you can interact with to explore ever deeper. The most relevant methods for navigating the workspace and
38exploring the content are:
39
40
41
42
43 */
44
45#include "RVersion.h"
46
47#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
48
49#define protected public
50#include "TRootBrowser.h"
52#define private public
53#include "RooAbsArg.h"
54#include "RooWorkspace.h"
57#include "RooProdPdf.h"
58#include "TGFileBrowser.h"
59#include "RooFitResult.h"
60#include "TPad.h"
61#undef private
62#include "RooAddPdf.h"
63#include "RooRealSumPdf.h"
64#include "RooProduct.h"
65#include "RooHistFunc.h"
66#include "RooConstVar.h"
67#include "RooSimultaneous.h"
68#undef protected
69
70#define GETWS(a) a->_myws
71#define GETWSSETS(w) w->_namedSets
72#define GETWSSNAPSHOTS(w) w->_snapshots
73#define GETACTBROWSER(b) b->fActBrowser
74#define GETROOTDIR(b) b->fRootDir
75#define GETLISTTREE(b) b->fListTree
76#define GETDMP(o, m) o->m
77
78#else
79
80#include "RooAbsArg.h"
81#include "RooWorkspace.h"
82#include "RooFitResult.h"
83#include "RooConstVar.h"
84#include "RooHistFunc.h"
85#include "RooRealSumPdf.h"
86#include "RooSimultaneous.h"
87#include "RooAddPdf.h"
88#include "RooProduct.h"
89#include "TPad.h"
93#include "RooProdPdf.h"
94#include "TRootBrowser.h"
95#include "TGFileBrowser.h"
96#include "TF1.h"
98
100{
101 return a->workspace();
102}
104{
105 return w->sets();
106}
108{
109 return w->getSnapshots();
110}
112{
113 return b->GetActBrowser();
114}
116{
117 return b->GetRootDir();
118}
120{
121 return b->GetListTree();
122}
123#define GETDMP(o, m) \
124 *reinterpret_cast<void **>(reinterpret_cast<unsigned char *>(o) + o->Class()->GetDataMemberOffset(#m))
125
126#endif
127
128#include "RooAddition.h"
129
130#include "RooCategory.h"
131#include "RooRealVar.h"
132#include "RooStringVar.h"
133#include "RooBinning.h"
134#include "RooUniformBinning.h"
135
136#include "RooAbsData.h"
137#include "RooDataHist.h"
138#include "RooDataSet.h"
139
140#include "xRooFit/xRooNode.h"
141#include "xRooFit/xRooFit.h"
142
143#include "TH1.h"
144#include "TBrowser.h"
145#include "TROOT.h"
146#include "TQObject.h"
147#include "TAxis.h"
148#include "TGraphAsymmErrors.h"
149#include "TMath.h"
150#include "TPRegexp.h"
151#include "TRegexp.h"
152#include "TExec.h"
153#include "TPaveText.h"
154
155#include "TGListTree.h"
156#include "TGMsgBox.h"
157#include "TGedEditor.h"
158#include "TGMimeTypes.h"
159#include "TH2.h"
160#include "RooExtendPdf.h"
161#include "RooExtendedBinding.h"
162
164
165#include "coutCapture.h"
166
167// #include "RooFitTrees/RooFitResultTree.h"
168// #include "RooFitTrees/RooDataTree.h"
169#include "TFile.h"
170#include "TSystem.h"
171#include "TKey.h"
172#include "TEnv.h"
173#include "TStyle.h"
174
175#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 26, 00)
177#endif
178
179#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 24, 00)
180#include "RooBinSamplingPdf.h"
181#endif
182
183#include "RooPoisson.h"
184#include "RooGaussian.h"
185#include "RooFormulaVar.h"
186#include "RooGenericPdf.h"
187#include "TVectorD.h"
188#include "TStopwatch.h"
189#include "TTimeStamp.h"
190
191#include <csignal>
192
193#include "TCanvas.h"
194#include "THStack.h"
195
196#include "TLegend.h"
197#include "TLegendEntry.h"
198#include "TGraphErrors.h"
199#include "TMultiGraph.h"
200#include "TFrame.h"
201#include "RooProjectedPdf.h"
202#include "TMemFile.h"
203#include "TGaxis.h"
204#include "TPie.h"
205// #include <thread>
206// #include <future>
207
208#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
209#include "RooNaNPacker.h"
210#endif
211
213
214xRooNode::InteractiveObject *xRooNode::gIntObj = nullptr;
215std::map<std::string, std::tuple<std::function<double(double, double, double)>, bool>> xRooNode::auxFunctions;
216void xRooNode::SetAuxFunction(const char *title, const std::function<double(double, double, double)> &func,
217 bool symmetrize)
218{
219 auxFunctions[title] = std::make_tuple(func, symmetrize);
220}
221
222template <typename T>
223const T &_or_func(const T &a, const T &b)
224{
225 if (a)
226 return a;
227 return b;
228}
229
230////////////////////////////////////////////////////////////////////////////////
231/// Create new object of type classname, with given name and title, and own-wrap it
232/// i.e. the xRooNode will delete the object when the node (and any that reference it) is destroyed
233///
234/// \param classname : the type of the object to create
235/// \param name : the name to give the object
236/// \param title : the title to give the object
237
238xRooNode::xRooNode(const char *classname, const char *name, const char *title)
239 : xRooNode(name, std::shared_ptr<TObject>(TClass::GetClass(classname)
240 ? reinterpret_cast<TObject *>(TClass::GetClass(classname)->New())
241 : nullptr,
242 [](TObject *o) {
243 if (auto w = dynamic_cast<RooWorkspace *>(o); w) {
244#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
245 w->_embeddedDataList.Delete();
246#endif
247 xRooNode(*w, std::make_shared<xRooNode>()).sterilize();
248 }
249 if (o)
250 delete o;
251 }))
252{
253 if (auto a = get<TNamed>(); a)
254 a->SetName(name);
255 SetTitle(title);
256}
257
258xRooNode::xRooNode(const char *name, const std::shared_ptr<TObject> &comp, const std::shared_ptr<xRooNode> &parent)
259 : TNamed(name, ""), fComp(comp), fParent(parent)
260{
261
262 if (!fComp && !fParent && name && strlen(name) > 0) {
263 char *_path = gSystem->ExpandPathName(name);
264 TString pathName = TString(_path);
265 delete[] _path;
266 if (!gSystem->AccessPathName(pathName)) {
267 // if file is json can try to read
268 if (pathName.EndsWith(".json")) {
269#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 26, 00)
270 fComp = std::make_shared<RooWorkspace>("workspace", name);
271 RooJSONFactoryWSTool tool(*get<RooWorkspace>());
274 if (!tool.importJSON(pathName.Data())) {
275 Error("xRooNode", "Error reading json workspace %s", name);
276 fComp.reset();
277 }
279#else
280 Error("xRooNode", "json format workspaces available only in ROOT 6.26 onwards");
281#endif
282 } else {
283
284 // using acquire in the constructor seems to cause a mem leak according to valgrind ... possibly because
285 // (*this) gets called on it before the node is fully constructed
286 auto _file = std::make_shared<TFile>(
287 pathName); // acquire<TFile>(name); // acquire file to ensure stays open while we have the workspace
288 // actually it appears we don't need to keep the file open once we've loaded the workspace, but should be
289 // no harm doing so
290 // otherwise the workspace doesn't saveas
291 auto keys = _file->GetListOfKeys();
292 if (keys) {
293 for (auto &&k : *keys) {
294 auto cl = TClass::GetClass((static_cast<TKey *>(k))->GetClassName());
295 if (cl == RooWorkspace::Class() || cl->InheritsFrom("RooWorkspace")) {
296 fComp.reset(_file->Get<RooWorkspace>(k->GetName()), [](TObject *ws) {
297 // memory leak in workspace, some RooLinkedLists aren't cleared, fixed in ROOT 6.28
298 if (ws) {
299#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
300 dynamic_cast<RooWorkspace *>(ws)->_embeddedDataList.Delete();
301#endif
302 xRooNode(*ws, std::make_shared<xRooNode>()).sterilize();
303 delete ws;
304 }
305 });
306 if (fComp) {
307 TNamed::SetNameTitle(fComp->GetName(), fComp->GetTitle());
308 fParent = std::make_shared<xRooNode>(
309 _file); // keep file alive - seems necessary to save workspace again in some cases
310 break;
311 }
312 }
313 }
314 }
315 }
316 } else if (pathName.EndsWith(".root") || pathName.EndsWith(".json")) {
317 throw std::runtime_error(TString::Format("%s does not exist", name));
318 }
319 }
320
321 if (auto _ws = get<RooWorkspace>(); _ws && (!parent || parent->get<TFile>())) {
324 .removeTopic(RooFit::NumIntegration); // stop info message every time
325
326 // check if any of the open files have version numbers greater than our major version
327 // may not read correctly
328 for (auto f : *gROOT->GetListOfFiles()) {
329 if ((dynamic_cast<TFile *>(f)->GetVersion() / 100) > (gROOT->GetVersionInt() / 100)) {
330 Warning("xRooNode", "There is file open with version %d > current version %d ... results may be wrong",
331 dynamic_cast<TFile *>(f)->GetVersion(), gROOT->GetVersionInt());
332 }
333 }
334
335 // use the datasets if any to 'mark' observables
336 int checkCount = 0;
337 for (auto &d : _ws->allData()) {
338 for (auto &a : *d->get()) {
339 if (auto v = _ws->var(a->GetName()); v) {
340 v->setAttribute("obs");
341 } else if (auto c = _ws->cat(a->GetName()); c) {
342 c->setAttribute("obs");
343 }
344 }
345 // count how many ds are checked ... if none are checked will check the first
346 checkCount += d->TestBit(1 << 20);
347 }
348
349 if (checkCount == 0 && !_ws->allData().empty())
350 _ws->allData().back()->SetBit(1 << 20, true);
351
352 if (auto _set = dynamic_cast<RooArgSet *>(GETWSSNAPSHOTS(_ws).find("NominalParamValues")); _set) {
353 for (auto s : *_set) {
354 if (auto v = dynamic_cast<RooRealVar *>(s); v) {
355 _ws->var(s->GetName())->setStringAttribute("nominal", TString::Format("%f", v->getVal()));
356 }
357 }
358 }
359
360 // also flag global observables ... relies on ModelConfig existences
361 RooArgSet _allGlobs;
362 for (auto &[k, v] : GETWSSETS(_ws)) {
363 if (k == "globalObservables" || TString(k).EndsWith("_GlobalObservables")) {
364 for (auto &s : v) {
365 _allGlobs.add(*s);
366 s->setAttribute("obs");
367 s->setAttribute("global");
368 }
369 } else if (TString(k).EndsWith("_Observables")) {
370 const_cast<RooArgSet &>(v).setAttribAll("obs");
371 } else if (TString(k).EndsWith("_POI")) {
372 for (auto &s : v) {
373 s->setAttribute("poi");
374 auto _v = dynamic_cast<RooRealVar *>(s);
375 if (!_v)
376 continue;
377 // if (!_v->hasRange("physical")) {
378 // _v->setRange("physical", 0, std::numeric_limits<double>::infinity());
379 // // ensure range of poi is also straddling 0
380 // if (_v->getMin() >= 0)
381 // _v->setMin(-1e-5);
382 // }
383 }
384 } else if (TString(k).EndsWith("_NuisParams")) {
385 const_cast<RooArgSet &>(v).setAttribAll("np");
386 }
387 }
388 if (!_allGlobs.empty() && GETWSSETS(_ws).count("globalObservables") == 0) {
389 _ws->defineSet("globalObservables", _allGlobs);
390 }
391
392 // now check if any pars don't have errors defined (not same as error=0) ... if so, use the first pdf (if there is
393 // one) to try setting values from
394 if (!_ws->allPdfs().empty()) {
395 std::set<RooRealVar *> noErrorPars;
396 std::string parNames;
397 for (auto &p : np()) { // infer errors on all floating non-poi parameters
398 auto v = p->get<RooRealVar>();
399 if (!v)
400 continue;
401 if (!v->hasError()) {
402 noErrorPars.insert(v);
403 if (!parNames.empty())
404 parNames += ",";
405 parNames += v->GetName();
406 }
407 }
408 if (!noErrorPars.empty()) {
409 Warning("xRooNode",
410 "Inferring initial errors of %d parameters (%s%s) (give all nuisance parameters an error to avoid "
411 "this msg)",
412 int(noErrorPars.size()), (*noErrorPars.begin())->GetName(), (noErrorPars.size() > 1) ? ",..." : "");
413 // get the first top-level pdf
414 browse();
415 for (auto &a : *this) {
416 if (noErrorPars.empty()) {
417 break;
418 }
419 if (a->fFolder == "!pdfs") {
420 try {
421 auto fr = a->floats().reduced(parNames).fitResult("prefit");
422 if (auto _fr = fr.get<RooFitResult>(); _fr) {
423 std::set<RooRealVar *> foundPars;
424 for (auto &v : noErrorPars) {
425 if (auto arg = dynamic_cast<RooRealVar *>(_fr->floatParsFinal().find(v->GetName()));
426 arg && arg->hasError()) {
427 v->setError(arg->getError());
428 foundPars.insert(v);
429 }
430 }
431 for (auto &v : foundPars) {
432 noErrorPars.erase(v);
433 }
434 }
435 } catch (...) {
436 }
437 }
438 }
439 }
440 }
441 }
442
443 if (strlen(GetTitle()) == 0) {
444 if (fComp) {
445 TNamed::SetTitle(fComp->GetTitle());
446 } else {
447 TNamed::SetTitle(GetName());
448 }
449 }
450}
451
452xRooNode::xRooNode(const TObject &comp, const std::shared_ptr<xRooNode> &parent)
453 : xRooNode(/*[](const TObject& c) {
454c.InheritsFrom("RooAbsArg");
455if (s) {
456return (s->getStringAttribute("alias")) ? s->getStringAttribute("alias") : c.GetName();
457}
458return c.GetName();
459}(comp)*/
460 (comp.InheritsFrom("RooAbsArg") && dynamic_cast<const RooAbsArg *>(&comp)->getStringAttribute("alias"))
461 ? dynamic_cast<const RooAbsArg *>(&comp)->getStringAttribute("alias")
462 : comp.GetName(),
463 std::shared_ptr<TObject>(const_cast<TObject *>(&comp), [](TObject *) {}), parent)
464{
465}
466
467xRooNode::xRooNode(const std::shared_ptr<TObject> &comp, const std::shared_ptr<xRooNode> &parent)
468 : xRooNode(
469 [&]() {
470 if (auto a = std::dynamic_pointer_cast<RooAbsArg>(comp); a && a->getStringAttribute("alias"))
471 return a->getStringAttribute("alias");
472 if (comp)
473 return comp->GetName();
474 return "";
475 }(),
476 comp, parent)
477{
478}
479
481
482void xRooNode::Checked(TObject *obj, bool val)
483{
484 if (obj != this)
485 return;
486
487 // cycle through states:
488 // unhidden and selected: tick, no uline
489 // hidden and unselected: notick, uline
490 // unhidden and unselected: tick, uline
491 if (auto o = get<RooAbsReal>(); o) {
492 if (o->isSelectedComp() && !val) {
493 // deselecting and hiding
494 o->selectComp(val);
495 o->setAttribute("hidden");
496 } else if (!o->isSelectedComp() && !val) {
497 // selecting
498 o->selectComp(!val);
499 } else if (val) {
500 // unhiding but keeping unselected
501 o->setAttribute("hidden", false);
502 }
503 auto item = GetTreeItem(nullptr);
504 item->CheckItem(!o->getAttribute("hidden"));
505 if (o->isSelectedComp()) {
506 item->ClearColor();
507 } else {
508 item->SetColor(kGray);
509 }
510 return;
511 }
512
513 if (auto o = get(); o) {
514 // if (o->TestBit(1<<20)==val) return; // do nothing
515 o->SetBit(1 << 20, val); // TODO: check is 20th bit ok to play with?
516 if (auto fr = get<RooFitResult>(); fr) {
517 if (auto _ws = ws(); _ws) {
518 if (val) {
519 // ensure fit result is in genericObjects list ... if not, add a copy ...
520 if (!_ws->genobj(fr->GetName())) {
521 _ws->import(*fr);
522 if (auto wfr = dynamic_cast<RooFitResult *>(_ws->genobj(fr->GetName()))) {
523 fr = wfr;
524 }
525 }
526 RooArgSet _allVars = _ws->allVars();
527 _allVars = fr->floatParsFinal();
528 _allVars = fr->constPars();
529 for (auto &i : fr->floatParsInit()) {
530 auto v = dynamic_cast<RooRealVar *>(_allVars.find(i->GetName()));
531 if (v)
532 v->setStringAttribute("initVal", TString::Format("%f", dynamic_cast<RooRealVar *>(i)->getVal()));
533 }
534 // uncheck all other fit results
535 for (auto oo : _ws->allGenericObjects()) {
536 if (auto ffr = dynamic_cast<RooFitResult *>(oo); ffr && ffr != fr) {
537 ffr->ResetBit(1 << 20);
538 }
539 }
540 } else
541 _ws->allVars() = fr->floatParsInit();
542 }
543 if (auto item = GetTreeItem(nullptr); item) {
544 // update check marks on siblings
545 if (auto first = item->GetParent()->GetFirstChild()) {
546 do {
547 if (first->HasCheckBox()) {
548 auto _obj = static_cast<xRooNode *>(first->GetUserData());
549 first->CheckItem(_obj->get() && _obj->get()->TestBit(1 << 20));
550 }
551 } while ((first = first->GetNextSibling()));
552 }
553 }
554 }
555 }
556}
557
559{
560 static bool blockBrowse = false;
561 if (blockBrowse)
562 return;
563 if (b == nullptr) {
564 auto b2 = dynamic_cast<TBrowser *>(gROOT->GetListOfBrowsers()->Last());
565 if (!b2 || !b2->GetBrowserImp()) { // no browser imp if browser was closed
566 blockBrowse = true;
567 gEnv->SetValue("X11.UseXft", "no"); // for faster x11
568 gEnv->SetValue("X11.Sync", "no");
569 gEnv->SetValue("X11.FindBestVisual", "no");
570 gEnv->SetValue("Browser.Name", "TRootBrowser"); // forces classic root browser (in 6.26 onwards)
571 gEnv->SetValue("Canvas.Name", "TRootCanvas");
572 b2 = new TBrowser("nodeBrowser", this, "RooFit Browser");
573 blockBrowse = false;
574 } else if (strcmp(b2->GetName(), "nodeBrowser") == 0) {
575 blockBrowse = true;
576 b2->BrowseObject(this);
577 blockBrowse = false;
578 } else {
579 auto _b = dynamic_cast<TGFileBrowser *>(GETACTBROWSER(dynamic_cast<TRootBrowser *>(b2->GetBrowserImp())));
580 if (_b)
581 _b->AddFSDirectory("Workspaces", nullptr, "SetRootDir");
582 /*auto l = Node2::Class()->GetMenuList();
583 auto o = new CustomClassMenuItem(TClassMenuItem::kPopupUserFunction,Node2::Class(),
584 "blah blah blah","BlahBlah",0,"Option_t*",-1,true);
585 //o->SetCall(o,"BlahBlah","Option_t*",-1);
586 l->AddFirst(o);*/
587 // b->BrowseObject(this);
588 _b->GotoDir(nullptr);
589 _b->Add(this, GetName());
590 // b->Add(this);
591 }
592 return;
593 }
594
595 if (auto item = GetTreeItem(b); item) {
596 if (!item->IsOpen() && IsFolder())
597 return; // no need to rebrowse if closing
598 // update check marks on any child items
599 if (auto first = item->GetFirstChild()) {
600 do {
601 if (first->HasCheckBox()) {
602 auto _obj = static_cast<xRooNode *>(first->GetUserData());
603 first->CheckItem(_obj->get() &&
604 (_obj->get()->TestBit(1 << 20) ||
605 (_obj->get<RooAbsArg>() && !_obj->get<RooAbsArg>()->getAttribute("hidden"))));
606 }
607 } while ((first = first->GetNextSibling()));
608 }
609 }
610
611 browse();
612
613 // for top-level pdfs default to having the .vars browsable too
614 if (get<RooAbsPdf>() && fFolder == "!pdfs" && !_IsShowVars_()) {
615 fBrowsables.push_back(std::make_shared<xRooNode>(vars()));
616 }
617
618 if (auto _fr = get<RooFitResult>(); _fr && fBrowsables.empty()) {
619 // have some common drawing options
620 fBrowsables.push_back(std::make_shared<xRooNode>(".Draw(\"pull\")", nullptr, *this));
621 fBrowsables.push_back(std::make_shared<xRooNode>(".Draw(\"corrcolztext\")", nullptr, *this));
622 if (std::unique_ptr<RooAbsCollection>(_fr->floatParsFinal().selectByAttrib("poi", true))->size() == 1) {
623 fBrowsables.push_back(std::make_shared<xRooNode>(".Draw(\"impact\")", nullptr, *this));
624 }
625 }
626
627 if (empty() && fBrowsables.empty()) {
628 try {
629 if (auto s = get<TStyle>()) {
630 s->SetFillAttributes();
631 if (auto ed = dynamic_cast<TGedEditor *>(TVirtualPadEditor::GetPadEditor())) {
632 ed->SetModel(gPad, s, kButton1Down, true);
633 }
634 } else if (TString(GetName()).BeginsWith(".Draw(\"") && fParent) {
635 fParent->Draw(TString(TString(GetName())(7, strlen(GetName()) - 9)) + b->GetDrawOption());
636 } else {
637 Draw(b->GetDrawOption());
638 }
639 } catch (const std::exception &e) {
640 new TGMsgBox(
641 gClient->GetRoot(),
642 (gROOT->GetListOfBrowsers()->At(0))
643 ? dynamic_cast<TGWindow *>(static_cast<TBrowser *>(gROOT->GetListOfBrowsers()->At(0))->GetBrowserImp())
644 : gClient->GetRoot(),
645 "Exception", e.what(),
646 kMBIconExclamation); // deletes self on dismiss?
647 }
648 }
649
650 bool hasFolders = false;
651 if (strlen(GetName()) > 0 && GetName()[0] != '!') { // folders don't have folders
652 for (auto &c : *this) {
653 if (!c->fFolder.empty()) {
654 hasFolders = true;
655 break;
656 }
657 }
658 }
659 // auto _ws = get<RooWorkspace>();
660 if (/*_ws*/ hasFolders) {
661 // organize in folders
662 auto _folders = find(".folders");
663 if (!_folders) {
664 _folders = emplace_back(std::make_shared<xRooNode>(".folders", nullptr, *this));
665 }
666 // ensure entry in folders for every folder type ...
667 for (auto &v : *this) {
668 if (!v->fFolder.empty() && !_folders->find(v->fFolder, false)) {
669 _folders->emplace_back(std::make_shared<xRooNode>(v->fFolder.c_str(), nullptr, *this));
670 }
671 }
672 // now just add all the folders
673 for (auto &v : *_folders) {
674 TString _name = v->GetName();
675 if (_name.BeginsWith('!'))
676 _name = _name(1, _name.Length()); // strip ! from display
677 b->Add(v.get(), _name);
678 }
679 }
680
681 for (auto &v : *this) {
682 if (hasFolders && !v->fFolder.empty())
683 continue; // in the folders
684 if (strcmp(v->GetName(), ".folders") == 0)
685 continue; // never 'browse' the folders property
686 auto _fr = v->get<RooFitResult>();
687 int _checked = (v->get<RooAbsData>() || _fr) ? v->get()->TestBit(1 << 20) : -1;
688 if (_fr && ((_fr->status() == 0 && _fr->numStatusHistory() == 0) || (_fr->floatParsFinal().empty()))) {
689 // this is a "PARTIAL" fit result ... don't allow it to be selected
690 _checked = -1;
691 }
692 if (v->get<RooAbsPdf>() && get<RooSimultaneous>())
693 _checked = !v->get<RooAbsArg>()->getAttribute("hidden");
694 TString _name = v->GetName();
695 if (v->get() && _name.BeginsWith(TString(v->get()->ClassName()) + "::")) {
696 _name = _name(strlen(v->get()->ClassName()) + 2, _name.Length());
697 }
698 if (_name.BeginsWith(".")) {
699 // property node -- display the name of the contained object
700 if (v->get()) {
701 _name = TString::Format("%s: %s::%s", _name.Data(), v->get()->ClassName(),
702 (v->get<RooAbsArg>() && v->get<RooAbsArg>()->getStringAttribute("alias"))
703 ? v->get<RooAbsArg>()->getStringAttribute("alias")
704 : v->get()->GetName());
705 }
706 } else if (v->get() && !v->get<TFile>() && !TString(v->GetName()).BeginsWith('/'))
707 _name = TString::Format("%s::%s", v->get()->ClassName(), _name.Data());
708 if (auto _type = v->GetNodeType(); strlen(_type)) {
709 // decided not to show const values until figure out how to update if value changes
710 /*if (TString(_type)=="Const") _name += TString::Format(" [%s=%g]",_type,v->get<RooConstVar>()->getVal());
711 else*/
712 _name += TString::Format(" [%s]", _type);
713 }
714 if (auto fv = v->get<RooFormulaVar>()) {
715 TString formu = TString::Format(" [%s]", fv->expression());
716 for (size_t i = 0; i < fv->dependents().size(); i++) {
717 formu.ReplaceAll(TString::Format("x[%zu]", i), fv->dependents()[i].GetName());
718 }
719 _name += formu;
720 } else if (auto gv = v->get<RooGenericPdf>()) {
721 TString formu = TString::Format(" [%s]", gv->expression());
722 for (size_t i = 0; i < gv->dependents().size(); i++) {
723 formu.ReplaceAll(TString::Format("x[%zu]", i), gv->dependents()[i].GetName());
724 }
725 _name += formu;
726 }
727 // tool tip defaults to displaying name and title, so temporarily set name to obj name if has one
728 // and set title to the object type
729 TString nameSave(v->TNamed::GetName());
730 TString titleSave(v->TNamed::GetTitle());
731 if (auto o = v->get(); o)
732 v->TNamed::SetNameTitle(o->GetName(), o->ClassName());
733 b->Add(v.get(), _name, _checked);
734 if (auto o = v->get(); o)
735 v->TNamed::SetNameTitle(nameSave, titleSave);
736 if (_checked != -1) {
737 dynamic_cast<TQObject *>(b->GetBrowserImp())
738 ->Connect("Checked(TObject *, bool)", ClassName(), v.get(), "Checked(TObject *, bool)");
739 }
740 if (_fr) {
741 if (_fr->status() || _fr->covQual() != 3) { // snapshots or bad fits
742 v->GetTreeItem(b)->SetColor((_fr->numStatusHistory() || _fr->floatParsFinal().empty()) ? kRed : kBlue);
743 } else if (_fr->numStatusHistory() == 0) { // partial fit result ..
744 v->GetTreeItem(b)->SetColor(kGray);
745 }
746 }
747 if ((v->fFolder == "!np" || v->fFolder == "!poi")) {
748 if (v->get<RooAbsArg>()->getAttribute("Constant")) {
749 v->GetTreeItem(b)->SetColor(kGray);
750 } else
751 v->GetTreeItem(b)->ClearColor();
752 }
753 if (auto _htr = v->get<RooStats::HypoTestResult>(); _htr) {
754 // check for fit statuses
755 if (auto fits = _htr->GetFitInfo()) {
756 for (int i = 0; i < fits->numEntries(); i++) {
757 // if any fit (other than a genFit) is bad, flag point as bad
758 if (fits->get(i)->getCatIndex("type") != 5 && fits->get(i)->getRealValue("status") != 0) {
759 v->GetTreeItem(b)->SetColor(kRed);
760 break;
761 }
762 }
763 } else {
764 v->GetTreeItem(b)->SetColor(kBlue); // unknown fit status
765 }
766 }
767
768 // v.fBrowsers.insert(b);
769 }
770
771 // for pdfs, check for datasets too and add to list
772 /*if (get<RooAbsPdf>()) {
773 auto dsets = datasets();
774 if (!dsets.empty()) {
775 // check if already have .datasets() in browsables
776 bool found(false);
777 for(auto& p : fBrowsables) {
778 if (TString(p->GetName())==".datasets()") {found=true;
779 // add
780 break;
781 }
782 }
783 if (!found) {
784 fBrowsables.push_back(std::make_shared<xRooNode>(dsets));
785 }
786 }
787 }*/
788 // browse the browsables too
789 for (auto &v : fBrowsables) {
790 TString _name = v->GetName();
791 if (_name == ".memory")
792 continue; // hide the memory from browsing, if put in browsables
793 TString nameSave(v->TNamed::GetName());
794 TString titleSave(v->TNamed::GetTitle());
795 if (auto o = v->get(); o)
796 v->TNamed::SetNameTitle(o->GetName(), o->ClassName());
797 b->Add(v.get(), _name, -1);
798 if (auto o = v->get(); o)
799 v->TNamed::SetNameTitle(nameSave, titleSave);
800 }
801
802 b->SetSelected(this);
803}
804
806{
807 if (!set) {
808 // can't remove as causes a crash, need to remove from the browser first
809 /*for(auto itr = fBrowsables.begin(); itr != fBrowsables.end(); ++itr) {
810 if (strcmp((*itr)->GetName(),".vars")==0) {
811 fBrowsables.erase(itr);
812 }
813 }*/
814 } else {
815 auto v = std::make_shared<xRooNode>(vars());
816 fBrowsables.push_back(v);
817 if (auto l = GetListTree(nullptr)) {
818 l->AddItem(GetTreeItem(nullptr), v->GetName(), v.get());
819 }
820 }
821}
822
824{
825 for (auto &b : fBrowsables) {
826 if (strcmp(b->GetName(), ".vars") == 0)
827 return true;
828 }
829 return false;
830}
831
833{
834 if (strlen(GetName()) > 0 && GetName()[0] == '!')
835 return true;
836 if (strlen(GetName()) > 0 && GetName()[0] == '.' && !(TString(GetName()).BeginsWith(".Draw(\"")))
837 return true;
838 if (empty())
839 const_cast<xRooNode *>(this)->browse();
840 return !empty();
841}
842
843class Axis2 : public TAxis {
844
845public:
846 using TAxis::TAxis;
847 double GetBinWidth(Int_t bin) const override
848 {
849 if (auto v = var(); v)
850 return v->getBinWidth(bin - 1, GetName());
851 return 1;
852 }
853 double GetBinLowEdge(Int_t bin) const override
854 {
855 if (auto v = rvar(); v) {
856 return (bin == v->getBinning(GetName()).numBins() + 1) ? v->getBinning(GetName()).binHigh(bin - 2)
857 : v->getBinning(GetName()).binLow(bin - 1);
858 }
859 return bin - 1;
860 }
861 double GetBinUpEdge(Int_t bin) const override
862 {
863 if (auto v = rvar(); v)
864 return (bin == 0) ? v->getBinning(GetName()).binLow(bin) : v->getBinning(GetName()).binHigh(bin - 1);
865 return bin;
866 }
867
868 const char *GetTitle() const override
869 {
870 return (binning() && strlen(binning()->GetTitle())) ? binning()->GetTitle() : GetParent()->GetTitle();
871 }
872 void SetTitle(const char *title) override
873 {
874 if (binning()) {
875 const_cast<RooAbsBinning *>(binning())->SetTitle(title);
876 } else {
877 dynamic_cast<TNamed *>(GetParent())->SetTitle(title);
878 }
879 }
880
881 void Set(Int_t nbins, const double *xbins) override
882 {
883 if (auto v = dynamic_cast<RooRealVar *>(rvar()))
884 v->setBinning(RooBinning(nbins, xbins), GetName());
885 TAxis::Set(nbins, xbins);
886 }
887 void Set(Int_t nbins, const float *xbins) override
888 {
889 std::vector<double> bins(nbins + 1);
890 for (int i = 0; i <= nbins; i++)
891 bins.at(i) = xbins[i];
892 return Set(nbins, &bins[0]);
893 }
894 void Set(Int_t nbins, double xmin, double xmax) override
895 {
896 if (auto v = dynamic_cast<RooRealVar *>(rvar()))
897 v->setBinning(RooUniformBinning(xmin, xmax, nbins), GetName());
898 TAxis::Set(nbins, xmin, xmax);
899 }
900
901 const RooAbsBinning *binning() const { return var()->getBinningPtr(GetName()); }
902
903 Int_t FindFixBin(const char *label) const override { return TAxis::FindFixBin(label); }
904 Int_t FindFixBin(double x) const override { return (binning()) ? (binning()->binNumber(x) + 1) : x; }
905
906private:
907 RooAbsLValue *var() const { return dynamic_cast<RooAbsLValue *>(GetParent()); }
908 RooAbsRealLValue *rvar() const { return dynamic_cast<RooAbsRealLValue *>(GetParent()); }
909};
910
911std::shared_ptr<TObject> xRooNode::getObject(const std::string &name, const std::string &type) const
912{
913 // if (fParent) return fParent->getObject(name);
914
915 if (auto _owned = find(".memory"); _owned) {
916 for (auto &o : *_owned) {
917 if (name == o->GetName()) {
918 if (type.empty() || o->get()->InheritsFrom(type.c_str()))
919 return o->fComp;
920 }
921 }
922 }
923
924 // see if have a provider
925 auto _provider = fProvider;
926 auto _parent = fParent;
927 while (!_provider && _parent) {
928 _provider = _parent->fProvider;
929 _parent = _parent->fParent;
930 }
931 if (_provider)
932 return _provider->getObject(name, type);
933
934 if (ws()) {
935 std::shared_ptr<TObject> out;
936 if (auto arg = ws()->arg(name.c_str()); arg) {
937 auto _tmp = std::shared_ptr<TObject>(arg, [](TObject *) {});
938 if (!type.empty() && arg->InheritsFrom(type.c_str()))
939 return _tmp;
940 if (!out)
941 out = _tmp;
942 }
943 if (auto arg = ws()->data(name.c_str()); arg) {
944 auto _tmp = std::shared_ptr<TObject>(arg, [](TObject *) {});
945 if (!type.empty() && arg->InheritsFrom(type.c_str()))
946 return _tmp;
947 if (!out)
948 out = _tmp;
949 }
950 if (auto arg = ws()->genobj(name.c_str()); arg) {
951 auto _tmp = std::shared_ptr<TObject>(arg, [](TObject *) {});
952 if (!type.empty() && arg->InheritsFrom(type.c_str()))
953 return _tmp;
954 if (!out)
955 out = _tmp;
956 }
957 if (auto arg = ws()->embeddedData(name.c_str()); arg) {
958 auto _tmp = std::shared_ptr<TObject>(arg, [](TObject *) {});
959 if (!type.empty() && arg->InheritsFrom(type.c_str()))
960 return _tmp;
961 if (!out)
962 out = _tmp;
963 }
964 if (auto arg = GETWSSNAPSHOTS(ws()).find(name.c_str()); arg) {
965 auto _tmp = std::shared_ptr<TObject>(arg, [](TObject *) {});
966 if (!type.empty() && arg->InheritsFrom(type.c_str()))
967 return _tmp;
968 if (!out)
969 out = _tmp;
970 }
971 return out;
972 }
973 if (auto arg = get<RooAbsArg>()) {
974 // can try all nodes
975 RooArgSet nodes;
976 arg->treeNodeServerList(&nodes);
977 if (auto server = nodes.find(name.c_str())) {
978 return std::shared_ptr<TObject>(server, [](TObject *) {});
979 }
980 }
981 return nullptr;
982}
983
985{
986 if (fXAxis) {
987 // check if num bins needs update or not
988 if (auto cat = dynamic_cast<RooAbsCategory *>(fXAxis->GetParent());
989 cat && cat->numTypes() != fXAxis->GetNbins()) {
990 fXAxis.reset();
991 } else {
992 return fXAxis.get();
993 }
994 }
995 RooAbsLValue *x = nullptr;
996 if (auto a = get<RooAbsArg>(); a && a->isFundamental())
997 x = dynamic_cast<RooAbsLValue *>(a); // self-axis
998
999 auto _parentX = (!x && fParent && !fParent->get<RooSimultaneous>()) ? fParent->GetXaxis() : nullptr;
1000
1001 auto o = get<RooAbsReal>();
1002 if (!o)
1003 return _parentX;
1004
1005 if (auto xName = o->getStringAttribute("xvar"); xName) {
1006 x = dynamic_cast<RooAbsLValue *>(getObject(xName).get());
1007 }
1008
1009 // if xvar has become set equal to an arg and this is a pdf, we will allow a do-over
1010 if (!x) {
1011 // need to choose from dependent fundamentals, in following order:
1012 // parentX (if not a glob), robs, globs, vars, args
1013
1014 if (_parentX && !dynamic_cast<RooAbsArg *>(_parentX->GetParent())->getAttribute("global") &&
1015 (o->dependsOn(*dynamic_cast<RooAbsArg *>(_parentX->GetParent())) || vars().empty())) {
1016 x = dynamic_cast<RooAbsLValue *>(_parentX->GetParent());
1017 } else if (auto _obs = obs(); !_obs.empty()) {
1018 for (auto &v : _obs) {
1019 if (!v->get<RooAbsArg>()->getAttribute("global")) {
1020 x = v->get<RooAbsLValue>();
1021 if (x)
1022 break;
1023 } else if (!x) {
1024 x = v->get<RooAbsLValue>();
1025 }
1026 }
1027 } else if (auto _pars = pars(); !_pars.empty()) {
1028 for (auto &v : _pars) {
1029 if (!v->get<RooAbsArg>()->getAttribute("Constant")) {
1030 x = v->get<RooAbsLValue>();
1031 if (x)
1032 break;
1033 } else if (!x) {
1034 x = v->get<RooAbsLValue>();
1035 }
1036 }
1037 }
1038
1039 if (!x) {
1040 return nullptr;
1041 }
1042 }
1043
1044 if (o != dynamic_cast<TObject *>(x)) {
1045 o->setStringAttribute("xvar", dynamic_cast<TObject *>(x)->GetName());
1046 }
1047
1048 // decide binning to use
1049 TString binningName = o->getStringAttribute("binning");
1050 auto _bnames = x->getBinningNames();
1051 bool hasBinning = false;
1052 for (auto &b : _bnames) {
1053 if (b == binningName) {
1054 hasBinning = true;
1055 break;
1056 }
1057 }
1058 if (!hasBinning) {
1059 // doesn't have binning, so clear binning attribute
1060 // this can happen after Combine of models because binning don't get combined yet (should fix this)
1061 Warning("GetXaxis", "Binning %s not defined on %s - clearing", binningName.Data(),
1062 dynamic_cast<TObject *>(x)->GetName());
1063 o->setStringAttribute("binning", nullptr);
1064 binningName = "";
1065 }
1066
1067 if (binningName == "" && o != dynamic_cast<TObject *>(x)) {
1068 // has var has a binning matching this nodes name then use that
1069 auto __bnames = x->getBinningNames();
1070 for (auto &b : __bnames) {
1071 if (b == GetName())
1072 binningName = GetName();
1073 if (b == o->GetName()) {
1074 binningName = o->GetName();
1075 break;
1076 } // best match
1077 }
1078 if (binningName == "") {
1079 // if we are binned in this var then will define that as a binning
1080 if (/*o->isBinnedDistribution(*dynamic_cast<RooAbsArg *>(x))*/
1081 auto bins = _or_func(
1082 /*o->plotSamplingHint(*dynamic_cast<RooAbsRealLValue
1083 *>(x),-std::numeric_limits<double>::infinity(),std::numeric_limits<double>::infinity())*/
1084 (std::list<double> *)(nullptr),
1085 o->binBoundaries(*dynamic_cast<RooAbsRealLValue *>(x), -std::numeric_limits<double>::infinity(),
1086 std::numeric_limits<double>::infinity()));
1087 bins) {
1088 std::vector<double> _bins;
1089 for (auto &b : *bins) {
1090 if (_bins.empty() || std::abs(_bins.back() - b) > 1e-5 * _bins.back())
1091 _bins.push_back(b);
1092 }
1093 fXAxis = std::make_shared<Axis2>(_bins.size() - 1, &_bins[0]);
1094 // add this binning to the var to avoid recalling ...
1095 if (auto _v = dynamic_cast<RooRealVar *>(x); _v) {
1096 _v->setBinning(RooBinning(_bins.size() - 1, &_bins[0], o->GetName()), o->GetName());
1097 _v->getBinning(o->GetName())
1098 .SetTitle(""); // indicates to use the current var title when building histograms etc
1099 //_v->getBinning(o->GetName()).SetTitle(strlen(dynamic_cast<TObject*>(x)->GetTitle()) ?
1100 // dynamic_cast<TObject*>(x)->GetTitle() : dynamic_cast<TObject*>(x)->GetName());
1101 }
1102 binningName = o->GetName();
1103 delete bins;
1104 } else if (_parentX) {
1105 // use parent axis binning if defined, otherwise we will default
1106 binningName = _parentX->GetName();
1107 }
1108 }
1109 }
1110
1111 if (!fXAxis) {
1112 if (auto r = dynamic_cast<RooAbsRealLValue *>(x); r) {
1113 if (r->getBinning(binningName).isUniform()) {
1114 fXAxis = std::make_shared<Axis2>(x->numBins(binningName), r->getMin(binningName), r->getMax(binningName));
1115 } else {
1116 fXAxis = std::make_shared<Axis2>(x->numBins(binningName), r->getBinning(binningName).array());
1117 }
1118 } else if (auto cat = dynamic_cast<RooCategory *>(x)) {
1119 std::vector<double> bins = {};
1120 for (int i = 0; i <= x->numBins(binningName); i++)
1121 bins.push_back(i);
1122 fXAxis = std::make_shared<Axis2>(x->numBins(binningName), &bins[0]);
1123 // TODO have to load current state of bin labels if was a category (sadly not a virtual method)
1124 int i = 1;
1125 std::map<int, std::string> cats; // fill into a map to preserve index ordering
1126 for (auto &c : *cat) {
1127 if (cat->isStateInRange(binningName, c.first.c_str())) {
1128 cats[c.second] = c.first;
1129 }
1130 }
1131 for (auto &[_, label] : cats) {
1132 fXAxis->SetBinLabel(i++, label.c_str());
1133 }
1134 }
1135 }
1136
1137 fXAxis->SetName(binningName);
1138 fXAxis->SetParent(dynamic_cast<TObject *>(x));
1139 return fXAxis.get();
1140}
1141
1142const char *xRooNode::GetIconName() const
1143{
1144 if (auto o = get(); o) {
1145 if (o->InheritsFrom("RooWorkspace"))
1146 return "TFile";
1147 if (o->InheritsFrom("RooAbsData"))
1148 return "TProfile";
1149 if (o->InheritsFrom("RooSimultaneous"))
1150 return "TH3D";
1151
1152 if (o->InheritsFrom("RooProdPdf"))
1153 return "a.C"; // or nullptr for folder
1154 if (o->InheritsFrom("RooRealSumPdf") || o->InheritsFrom("RooAddPdf"))
1155 return "TH2D";
1156 // if(o->InheritsFrom("RooProduct")) return "TH1D";
1157 if (o->InheritsFrom("RooFitResult")) {
1158 if (!gClient->GetMimeTypeList()->GetIcon("xRooFitRooFitResult", true)) {
1159 gClient->GetMimeTypeList()->AddType("xRooFitRooFitResult", "xRooFitRooFitResult", "package.xpm",
1160 "package.xpm", "->Browse()");
1161 }
1162 return "xRooFitRooFitResult";
1163 }
1164 if (o->InheritsFrom("RooRealVar") || o->InheritsFrom("RooCategory")) {
1165 if (get<RooAbsArg>()->getAttribute("obs")) {
1166 if (!gClient->GetMimeTypeList()->GetIcon("xRooFitObs", true)) {
1167 gClient->GetMimeTypeList()->AddType("xRooFitObs", "xRooFitObs", "x_pic.xpm", "x_pic.xpm", "->Browse()");
1168 }
1169 if (!gClient->GetMimeTypeList()->GetIcon("xRooFitGlobs", true)) {
1170 gClient->GetMimeTypeList()->AddType("xRooFitGlobs", "xRooFitGlobs", "z_pic.xpm", "z_pic.xpm",
1171 "->Browse()");
1172 }
1173 return (get<RooAbsArg>()->getAttribute("global") ? "xRooFitGlobs" : "xRooFitObs");
1174 }
1175 return "TLeaf";
1176 }
1177 if (o->InheritsFrom("TStyle")) {
1178 if (!gClient->GetMimeTypeList()->GetIcon("xRooFitTStyle", true)) {
1179 gClient->GetMimeTypeList()->AddType("xRooFitTStyle", "xRooFitTStyle", "bld_colorselect.xpm",
1180 "bld_colorselect.xpm", "->Browse()");
1181 }
1182 return "xRooFitTStyle";
1183 }
1184 if (o->InheritsFrom("RooConstVar")) {
1185 /*if (!gClient->GetMimeTypeList()->GetIcon("xRooFitRooConstVar",true)) {
1186 gClient->GetMimeTypeList()->AddType("xRooFitRooConstVar", "xRooFitRooConstVar", "stop_t.xpm", "stop_t.xpm",
1187 "->Browse()");
1188 }
1189 return "xRooFitRooConstVar";*/
1190 return "TMethodBrowsable-leaf";
1191 }
1192 if (o->InheritsFrom("RooStats::HypoTestInverterResult")) {
1193 if (!gClient->GetMimeTypeList()->GetIcon("xRooFitScanStyle", true)) {
1194 gClient->GetMimeTypeList()->AddType("xRooFitScanStyle", "xRooFitScanStyle", "f2_s.xpm", "f2_s.xpm",
1195 "->Browse()");
1196 }
1197 return "xRooFitScanStyle";
1198 }
1199 if (o->InheritsFrom("RooStats::HypoTestResult")) {
1200 if (!gClient->GetMimeTypeList()->GetIcon("xRooFitTestStyle", true)) {
1201 gClient->GetMimeTypeList()->AddType("xRooFitTestStyle", "xRooFitTestStyle", "diamond.xpm", "diamond.xpm",
1202 "->Browse()");
1203 }
1204 return "xRooFitTestStyle";
1205 }
1206 if (o->InheritsFrom("RooStats::HistFactory::FlexibleInterpVar"))
1207 return "TBranchElement-folder";
1208 if (o->InheritsFrom("RooAbsPdf")) {
1209 if (!gClient->GetMimeTypeList()->GetIcon("xRooFitPDFStyle", true)) {
1210 gClient->GetMimeTypeList()->AddType("xRooFitPDFStyle", "xRooFitPDFStyle", "pdf.xpm", "pdf.xpm",
1211 "->Browse()");
1212 }
1213 return "xRooFitPDFStyle";
1214 }
1215 if (auto a = dynamic_cast<RooAbsReal *>(o); a) {
1216 if (auto _ax = GetXaxis();
1217 _ax && (a->isBinnedDistribution(*dynamic_cast<RooAbsArg *>(_ax->GetParent())) ||
1218 (dynamic_cast<RooAbsRealLValue *>(_ax->GetParent()) &&
1219 std::unique_ptr<std::list<double>>(a->binBoundaries(
1220 *dynamic_cast<RooAbsRealLValue *>(_ax->GetParent()), -std::numeric_limits<double>::infinity(),
1221 std::numeric_limits<double>::infinity()))))) {
1222 return "TH1D";
1223 }
1224 return "TF1";
1225 }
1226 return o->ClassName();
1227 }
1228 if (!IsFolder()) {
1229 return "Unknown";
1230 }
1231 return nullptr;
1232}
1233
1234const char *xRooNode::GetNodeType() const
1235{
1236 if (auto o = get(); o && fParent && (fParent->get<RooProduct>() || fParent->get<RooRealSumPdf>())) {
1237 if (o->InheritsFrom("RooStats::HistFactory::FlexibleInterpVar"))
1238 return "Overall";
1239 if (o->InheritsFrom("PiecewiseInterpolation"))
1240 return (dynamic_cast<RooAbsArg *>(o)->getAttribute("density")) ? "DensityHisto" : "Histo";
1241 if (o->InheritsFrom("RooHistFunc"))
1242 return (dynamic_cast<RooAbsArg *>(o)->getAttribute("density")) ? "ConstDensityHisto" : "ConstHisto";
1243 if (o->InheritsFrom("RooBinWidthFunction"))
1244 return "Density";
1245 if (o->InheritsFrom("ParamHistFunc"))
1246 return "Shape";
1247 if (o->InheritsFrom("RooRealVar"))
1248 return "Norm";
1249 if (o->InheritsFrom("RooConstVar"))
1250 return "Const";
1251 }
1252 return "";
1253}
1254
1255xRooNode xRooNode::coords(bool setVals) const
1256{
1257 xRooNode out(".coords", nullptr, *this);
1258 // go up through parents looking for slice obs
1259 auto _p = std::shared_ptr<xRooNode>(const_cast<xRooNode *>(this), [](xRooNode *) {});
1260 while (_p) {
1261 TString pName(_p->GetName());
1262 // following is commented out while still considering, but idea is to include category in coords
1263 /*if (auto s = _p->get<RooSimultaneous>(); s && s->indexCat().InheritsFrom("RooCategory") &&
1264 !out.find(s->indexCat().GetName())) { auto cat = const_cast<RooCategory*>(dynamic_cast<const
1265 RooCategory*>(&s->indexCat()));
1266 // check if we have a pdf for every category ... if not then add to cut
1267 cat->clearRange("coordRange",true);
1268 bool hasMissing = false;
1269 std::string includedStates;
1270 for (auto state : *cat) {
1271 if (!s->getPdf(state.first.c_str())) {
1272 hasMissing = true;
1273 } else {
1274 if (!includedStates.empty()) {
1275 includedStates += ",";
1276 }
1277 includedStates += state.first;
1278 }
1279 }
1280 if (hasMissing) {
1281 if(includedStates.find(",") != std::string::npos) {
1282 cat->addToRange("coordRange",includedStates.c_str());
1283 } else {
1284 cat->setLabel(includedStates);
1285 }
1286 out.emplace_back(std::make_shared<xRooNode>(cat->GetName(),_p->getObject<RooAbsArg>(cat->GetName()),_p));
1287 }
1288 } else*/
1289 if (auto pos = pName.Index('='); pos != -1) {
1290 if (pos > 0 && pName(pos - 1) == '<') {
1291 // should be a range on a real lvalue, of form low<=name<high
1292 double low = TString(pName(0, pos - 1)).Atof();
1293 pName = pName(pos + 1, pName.Length());
1294 double high = TString(pName(pName.Index('<') + 1, pName.Length())).Atof();
1295 pName = pName(0, pName.Index('<'));
1296 if (auto _obs = _p->getObject<RooAbsRealLValue>(pName.Data()); _obs) {
1297 if (setVals) {
1298 _obs->setVal((high + low) / 2.);
1299 static_cast<RooRealVar *>(_obs.get())->setRange("coordRange", low, high);
1300 _obs->setStringAttribute(
1301 "coordRange", "coordRange"); // will need if we allow multi disconnected regions, need comma list
1302 }
1303 out.emplace_back(std::make_shared<xRooNode>(_obs->GetName(), _obs, _p));
1304 } else {
1305 throw std::runtime_error(TString::Format("Unknown observable: %s", pName.Data()));
1306 }
1307
1308 } else if (auto _obs = _p->getObject<RooAbsArg>(pName(0, pos)); _obs) {
1309 if (setVals) {
1310 if (auto _cat = dynamic_cast<RooAbsCategoryLValue *>(_obs.get()); _cat) {
1311 _cat->setLabel(pName(pos + 1, pName.Length()));
1312 } else if (auto _var = dynamic_cast<RooAbsRealLValue *>(_obs.get()); _var) {
1313 _var->setVal(TString(pName(pos + 1, pName.Length())).Atof());
1314 }
1315 }
1316 out.emplace_back(std::make_shared<xRooNode>(_obs->GetName(), _obs, _p));
1317 } else {
1318 throw std::runtime_error("Unknown observable, could not find");
1319 }
1320 }
1321 _p = _p->fParent;
1322 }
1323 return out;
1324}
1325
1326void xRooNode::_Add_(const char *name, const char *opt)
1327{
1328 try {
1329 Add(name, opt);
1330 } catch (const std::exception &e) {
1331 new TGMsgBox(gClient->GetRoot(), gClient->GetRoot(), "Exception", e.what(),
1332 kMBIconExclamation); // deletes self on dismiss?
1333 }
1334}
1335void xRooNode::_Vary_(const char *what)
1336{
1337 try {
1338 Vary(what);
1339 } catch (const std::exception &e) {
1340 new TGMsgBox(gClient->GetRoot(), gClient->GetRoot(), "Exception", e.what(),
1341 kMBIconExclamation); // deletes self on dismiss?
1342 }
1343}
1344
1346{
1347
1348 if (strcmp(GetName(), ".poi") == 0) {
1349 // demote a parameter from being a poi
1350 auto toRemove =
1351 (child.get<RooAbsArg>() || !find(child.GetName())) ? child : xRooNode(find(child.GetName())->fComp);
1352 if (toRemove) {
1353 if (!toRemove.get<RooAbsArg>()->getAttribute("poi")) {
1354 throw std::runtime_error(TString::Format("%s is not a poi", toRemove.GetName()));
1355 }
1356 toRemove.get<RooAbsArg>()->setAttribute("poi", false);
1357 return toRemove;
1358 }
1359 } else if (strcmp(GetName(), ".factors") == 0 || strcmp(GetName(), ".constraints") == 0 ||
1360 strcmp(GetName(), ".components") == 0) {
1361 auto toRemove =
1362 (child.get<RooAbsArg>() || !find(child.GetName())) ? child : xRooNode(find(child.GetName())->fComp);
1363 if (auto p = fParent->get<RooProdPdf>(); p) {
1364 auto pdf = toRemove.get<RooAbsArg>();
1365 if (!pdf)
1366 pdf = p->pdfList().find(child.GetName());
1367 if (!pdf)
1368 throw std::runtime_error(TString::Format("Cannot find %s in %s", child.GetName(), fParent->GetName()));
1369 auto i = p->pdfList().index(*pdf);
1370 if (i >= 0) {
1371#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
1372 const_cast<RooArgList &>(p->pdfList()).remove(*pdf);
1373#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 26, 00)
1374 p->_pdfNSetList.erase(p->_pdfNSetList.begin() + i);
1375#else
1376 auto nset = p->_pdfNSetList.At(i);
1377 p->_pdfNSetList.Remove(nset);
1378 delete nset; // I don't think the RooLinkedList owned it so must delete ourself
1379#endif
1380 if (p->_extendedIndex == i)
1381 p->_extendedIndex = -1;
1382 else if (p->_extendedIndex > i)
1383 p->_extendedIndex--;
1384#else
1385 p->removePdfs(RooArgSet(*pdf));
1386#endif
1387 sterilize();
1388 return xRooNode(*pdf);
1389 } else {
1390 throw std::runtime_error(TString::Format("Cannot find %s in %s", child.GetName(), fParent->GetName()));
1391 }
1392 } else if (auto p2 = fParent->get<RooProduct>(); p2) {
1393 auto arg = toRemove.get<RooAbsArg>();
1394 if (!arg)
1395 arg = p2->components().find(child.GetName());
1396 if (!arg)
1397 throw std::runtime_error(TString::Format("Cannot find %s in %s", child.GetName(), fParent->GetName()));
1398 // remove server ... doesn't seem to trigger removal from proxy
1399#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
1400 p2->_compRSet.remove(*arg);
1401#else
1402 const_cast<RooArgList &>(p2->realComponents()).remove(*arg);
1403#endif
1404 p2->removeServer(*arg, true);
1405 sterilize();
1406 return xRooNode(*arg);
1407 } else if (fParent->get<RooSimultaneous>()) {
1408 // remove from all channels
1409 bool removed = false;
1410 for (auto &c : fParent->bins()) {
1411 try {
1412 c->constraints().Remove(toRemove);
1413 removed = true;
1414 } catch (std::runtime_error &) { /* wasn't a constraint in channel */
1415 }
1416 }
1417 sterilize();
1418 if (!removed)
1419 throw std::runtime_error(TString::Format("Cannot find %s in %s", child.GetName(), fParent->GetName()));
1420 return toRemove;
1421 } else if (auto p4 = fParent->get<RooRealSumPdf>(); p4) {
1422 auto arg = toRemove.get<RooAbsArg>();
1423 if (!arg)
1424 arg = p4->funcList().find(child.GetName());
1425 if (!arg)
1426 throw std::runtime_error(TString::Format("Cannot find %s in %s", child.GetName(), fParent->GetName()));
1427 // remove, including coef removal ....
1428 auto idx = p4->funcList().index(arg);
1429
1430 if (idx != -1) {
1431
1432 const_cast<RooArgList &>(p4->funcList()).remove(*arg);
1433 p4->removeServer(*arg, true);
1434 // have to be careful removing coef because if shared will end up removing them all!!
1435 std::vector<RooAbsArg *> _coefs;
1436 for (size_t ii = 0; ii < const_cast<RooArgList &>(p4->coefList()).size(); ii++) {
1437 if (ii != size_t(idx))
1438 _coefs.push_back(const_cast<RooArgList &>(p4->coefList()).at(ii));
1439 }
1440 const_cast<RooArgList &>(p4->coefList()).removeAll();
1441 for (auto &a : _coefs)
1442 const_cast<RooArgList &>(p4->coefList()).add(*a);
1443
1444 sterilize();
1445 } else {
1446 throw std::runtime_error(TString::Format("Cannot find %s in %s", child.GetName(), fParent->GetName()));
1447 }
1448 return xRooNode(*arg);
1449 } // todo: add support for RooAddPdf and RooAddition
1450 }
1451
1452 if (auto w = get<RooWorkspace>(); w) {
1453 xRooNode out(child.GetName());
1454 auto arg = w->components().find(child.GetName());
1455 if (!arg)
1456 arg = operator[](child.GetName())->get<RooAbsArg>();
1457 if (!arg) {
1458 throw std::runtime_error(TString::Format("Cannot find %s in workspace %s", child.GetName(), GetName()));
1459 }
1460 // check has no clients ... if so, cannot delete
1461 if (arg->hasClients()) {
1462 throw std::runtime_error(
1463 TString::Format("Cannot remove %s from workspace %s, because it has dependencies - first remove from those",
1464 child.GetName(), GetName()));
1465 }
1466 const_cast<RooArgSet &>(w->components()).remove(*arg); // deletes arg
1467 Info("Remove", "Deleted %s from workspace %s", out.GetName(), GetName());
1468 return out;
1469 } else if (get<RooProduct>() || get<RooProdPdf>()) {
1470 return factors().Remove(child);
1471 } else if (get<RooRealSumPdf>()) {
1472 return components().Remove(child);
1473 }
1474
1475 throw std::runtime_error("Removal not implemented for this type of object");
1476}
1477
1479{
1480
1481 class AutoUpdater {
1482 public:
1483 AutoUpdater(xRooNode &_n) : n(_n) {}
1484 ~AutoUpdater() { n.browse(); }
1485 xRooNode &n;
1486 };
1487 AutoUpdater xxx(*this);
1488
1489 TString sOpt(opt);
1490 bool considerType(sOpt == "+");
1491
1492 if (strlen(GetName()) > 0 && GetName()[0] == '!' && fParent) {
1493 // folder .. pass onto parent and add folder to child folder list
1494 const_cast<xRooNode &>(child).fFolder += GetName();
1495 return fParent->Add(child, opt);
1496 }
1497 // this is how to get the first real parent ... may be useful at some point?
1498 /*auto realParent = fParent;
1499 while(!realParent->get()) {
1500 realParent = realParent->fParent;
1501 if (!realParent) throw std::runtime_error("No parentage");
1502 }*/
1503
1504 // adding to a collection node will incorporate the child into the parent of the collection
1505 // in the appropriate way
1506 if (strcmp(GetName(), ".factors") == 0) {
1507 // multiply the parent
1508 return fParent->Multiply(child, opt);
1509 } else if (strcmp(GetName(), ".components") == 0) {
1510 // add to the parent
1511 return fParent->Add(child, opt);
1512 } else if (strcmp(GetName(), ".variations") == 0) {
1513 // vary the parent
1514 return fParent->Vary(child);
1515 } else if (strcmp(GetName(), ".constraints") == 0) {
1516 // constrain the parent
1517 return fParent->Constrain(child);
1518 } else if (strcmp(GetName(), ".bins") == 0 && fParent->get<RooSimultaneous>()) {
1519 // adding a channel (should adding a 'bin' be an 'Extend' operation?)
1520 return fParent->Vary(child);
1521 } else if ((strcmp(GetName(), ".globs") == 0)) {
1522 if (child.get<RooAbsArg>() || (!child.fComp && getObject<RooAbsArg>(child.GetName()))) {
1523 auto out = (child.get<RooAbsArg>()) ? child.get<RooAbsArg>() : getObject<RooAbsArg>(child.GetName()).get();
1524 out->setAttribute("obs");
1525 out->setAttribute("global");
1526 return xRooNode(*out, *this);
1527 }
1528 throw std::runtime_error("Failed to add global observable");
1529 } else if ((strcmp(GetName(), ".poi") == 0)) {
1530 if (child.get<RooAbsLValue>() || (!child.fComp && getObject<RooAbsLValue>(child.GetName()))) {
1531 auto out = (child.get<RooAbsArg>()) ? child.get<RooAbsArg>() : getObject<RooAbsArg>(child.GetName()).get();
1532 out->setAttribute("poi");
1533 return xRooNode(*out, *this);
1534 }
1535 throw std::runtime_error("Failed to add parameter of interest");
1536 } else if ((strcmp(GetName(), ".pars") == 0 || strcmp(GetName(), ".vars") == 0) && fParent->get<RooWorkspace>()) {
1537 // adding a parameter, interpret as factory string unless no "[" then create RooRealVar
1538 TString fac(child.GetName());
1539 if (!fac.Contains("["))
1540 fac += "[1]";
1541 return xRooNode(*fParent->get<RooWorkspace>()->factory(fac), fParent);
1542 } else if (strcmp(GetName(), ".datasets()") == 0) {
1543 // create a dataset - only allowed for pdfs or workspaces
1544 if (auto _ws = ws(); _ws && fParent) {
1545 sOpt.ToLower();
1546 if (!fParent->get<RooAbsPdf>() && (!fParent->get<RooWorkspace>() || sOpt == "asimov")) {
1547 throw std::runtime_error(
1548 "Datasets can only be created for pdfs or workspaces (except if generated dataset, then must be pdf)");
1549 }
1550
1551 if (sOpt == "asimov" || sOpt == "toy") {
1552 // generate expected dataset - note that globs will be frozen at this time
1553 auto _fr = fParent->fitResult();
1554 if (strlen(_fr->GetName()) == 0) { // ensure fit result has a name so that name is saved inside dataset
1555 _fr.get<RooFitResult>()->SetName(TUUID().AsString());
1556 }
1557 auto ds = fParent->generate(_fr, sOpt == "asimov");
1558 if (strlen(child.GetName())) {
1559 ds.SetName(child.GetName());
1560 ds.get<TNamed>()->SetName(child.GetName());
1561 }
1562 if (auto _ds = ds.get<RooAbsData>()) {
1563 _ws->import(*_ds);
1564 }
1565 if (_fr.get<RooFitResult>()->numStatusHistory() == 0) {
1566 if (!GETWSSNAPSHOTS(_ws).find(_fr->GetName())) {
1567 const_cast<RooLinkedList &>(GETWSSNAPSHOTS(_ws)).Add(_fr->Clone());
1568 }
1569 } else if (!_ws->obj(_fr->GetName())) {
1570 _ws->import((*_fr.get<RooFitResult>()));
1571 } // save fr to workspace, for later retrieval
1572 return xRooNode(*_ws->data(ds.GetName()), fParent);
1573 }
1574
1575 auto parentObs = fParent->obs(); // may own globs so keep alive
1576 auto _obs = parentObs.argList();
1577 // put globs in a snapshot
1578 std::unique_ptr<RooAbsCollection> _globs(_obs.selectByAttrib("global", true));
1579 // RooArgSet _tmp; _tmp.add(*_globs);_ws->saveSnapshot(child.GetName(),_tmp);
1580 _obs.remove(*_globs);
1581
1582 // include any coords
1583 _obs.add(coords(false).argList(), true);
1584 // include axis var too, provided it's an observable
1585 if (auto ax = GetXaxis(); ax && dynamic_cast<RooAbsArg *>(ax->GetParent())->getAttribute("obs")) {
1586 _obs.add(*dynamic_cast<RooAbsArg *>(ax->GetParent()));
1587 }
1588 // check if ws already has a dataset with this name, if it does we may need to extend columns
1589 if (auto _d = _ws->data(child.GetName()); _d) {
1590 // add any missing obs
1591 RooArgSet l(_obs);
1592 l.remove(*_d->get(), true, true);
1593 if (!l.empty()) {
1594 auto _dd = dynamic_cast<RooDataSet *>(_d);
1595 if (!_dd)
1596 throw std::runtime_error("Cannot extend dataset with new columns");
1597 for (auto &x : l) {
1598 _dd->addColumn(*x);
1599 }
1600 }
1601 } else {
1602 RooRealVar w("weightVar", "weightVar", 1);
1603 _obs.add(w);
1604 RooDataSet d(child.GetName(), child.GetTitle(), _obs, RooFit::WeightVar("weightVar"));
1605 _ws->import(d);
1606 // seems have to set bits after importing, not before
1607 if (auto __d = _ws->data(child.GetName()))
1608 __d->SetBit(1 << 20, _ws->allData().size() == 1); // sets as selected if is only ds
1609 }
1610 /*if(!_ws->data(child.GetName())) {
1611 RooRealVar w("weightVar", "weightVar", 1);
1612 RooArgSet _obs; _obs.add(w);
1613 RooDataSet d(child.GetName(), child.GetTitle(), _obs, "weightVar");
1614 _ws->import(d);
1615 }*/
1616 auto out = std::shared_ptr<TObject>(_ws->data(child.GetName()), [](TObject *) {});
1617
1618 if (out) {
1619 xRooNode o(out, fParent);
1620 if (child.get<TH1>())
1621 o = *child.get();
1622 return o;
1623 }
1624 }
1625 throw std::runtime_error("Cannot create dataset");
1626 }
1627
1628 if (!get()) {
1629 if (!fParent)
1630 throw std::runtime_error("Cannot add to null object with no parentage");
1631
1632 auto _ref = emplace_back(std::shared_ptr<xRooNode>(&const_cast<xRooNode &>(child), [](TObject *) {}));
1633 try {
1634 fComp = fParent->Add(*this, "+").fComp;
1635 } catch (...) {
1636 resize(size() - 1);
1637 std::rethrow_exception(std::current_exception());
1638 }
1639 resize(size() - 1); // remove the temporarily added node
1640
1641 if (!fComp) {
1642 throw std::runtime_error("No object");
1643 }
1644 }
1645
1646 if (auto p = get<RooAbsData>(); p) {
1647 if (auto bb = getBrowsable(".sourceds"))
1648 bb->Add(child, opt);
1649 if (auto _data = child.get<RooDataSet>()) {
1650 auto ds = dynamic_cast<RooDataSet *>(p);
1651 if (!ds) {
1652 throw std::runtime_error("Can only add datasets to a dataset");
1653 }
1654
1655 // append any missing globs, and check any existing globs have matching values
1656 RooArgList globsToAdd;
1657 auto _globs = globs();
1658 for (auto &glob : child.globs()) {
1659 if (auto g = _globs.find(glob->GetName()); !g) {
1660 globsToAdd.addClone(*glob->get<RooAbsArg>());
1661 } else if (g->GetContent() != glob->GetContent()) {
1662 Warning("Add", "Global observable %s=%g in dataset %s mismatches %s value %g ... ignoring latter",
1663 g->GetName(), g->GetContent(), GetName(), child.GetName(), glob->GetContent());
1664 }
1665 }
1666 // add any existing globs to list then set the list
1667 if (auto _dglobs = p->getGlobalObservables()) {
1668 globsToAdd.addClone(*_dglobs);
1669 } else {
1670 for (auto g : _globs)
1671 globsToAdd.addClone(*g->get<RooAbsArg>());
1672 }
1673 p->setGlobalObservables(globsToAdd);
1674
1675 // append any missing observables to our dataset, then append the dataset
1676
1677 for (auto col : *_data->get()) {
1678 if (!p->get()->contains(*col)) {
1679 ds->addColumn(*col);
1680 }
1681 }
1682 ds->append(*_data);
1683 ds->SetTitle(TString(ds->GetTitle()) + " + " + _data->GetTitle());
1684 SetTitle(TString(GetTitle()) + " + " + child.GetTitle());
1685 return *this;
1686 }
1687 auto _h = child.get<TH1>();
1688 if (!_h) {
1689 throw std::runtime_error("Can only add histogram or dataset to data");
1690 }
1691 auto _pdf = parentPdf();
1692 if (!_pdf)
1693 throw std::runtime_error("Could not find pdf");
1694 auto _ax = _pdf->GetXaxis();
1695 if (!_ax) {
1696 throw std::runtime_error("Cannot determine binning to add data");
1697 }
1698
1699 RooArgSet obs;
1700 obs.add(*dynamic_cast<RooAbsArg *>(_ax->GetParent()));
1701 obs.add(coords().argList()); // will also move obs to coords
1702
1703 // add any missing obs
1704 RooArgSet l(obs);
1705 l.remove(*p->get(), true, true);
1706 if (!l.empty()) {
1707 auto _d = dynamic_cast<RooDataSet *>(p);
1708 if (!_d)
1709 throw std::runtime_error("Cannot extend dataset with new columns");
1710 for (auto &x : l) {
1711 _d->addColumn(*x);
1712 }
1713 }
1714
1715 // before adding, ensure range is good to cover
1716 for (auto &o : obs) {
1717 if (auto v = dynamic_cast<RooRealVar *>(o); v) {
1718 if (auto dv = dynamic_cast<RooRealVar *>(p->get()->find(v->GetName())); dv) {
1719 if (v->getMin() < dv->getMin())
1720 dv->setMin(v->getMin());
1721 if (v->getMax() > dv->getMax())
1722 dv->setMax(v->getMax());
1723 }
1724 } else if (auto c = dynamic_cast<RooCategory *>(o); c) {
1725 if (auto dc = dynamic_cast<RooCategory *>(p->get()->find(c->GetName())); dc) {
1726 if (!dc->hasLabel(c->getCurrentLabel())) {
1727 dc->defineType(c->getCurrentLabel(), c->getCurrentIndex());
1728 }
1729 }
1730 }
1731 }
1732
1733 for (int i = 1; i <= _h->GetNbinsX(); i++) {
1734 if (auto cat = dynamic_cast<RooAbsCategoryLValue *>(_ax->GetParent())) {
1735 if (!_h->GetXaxis()->GetBinLabel(i)) {
1736 throw std::runtime_error(
1737 TString::Format("Categorical observable %s requires bin labels", _ax->GetParent()->GetName()));
1738 } else if (!cat->hasLabel(_h->GetXaxis()->GetBinLabel(i))) {
1739 throw std::runtime_error(TString::Format("Categorical observable %s does not have label %s",
1740 _ax->GetParent()->GetName(), _h->GetXaxis()->GetBinLabel(i)));
1741 } else {
1742 cat->setLabel(_h->GetXaxis()->GetBinLabel(i));
1743 }
1744 } else {
1745 dynamic_cast<RooAbsRealLValue *>(_ax->GetParent())->setVal(_h->GetBinCenter(i));
1746 }
1747 p->add(obs, _h->GetBinContent(i));
1748 }
1749
1750 return *this;
1751 }
1752
1753 if (auto p = get<RooAddPdf>(); p) {
1754 if ((child.get<RooAbsPdf>() || (!child.fComp && getObject<RooAbsPdf>(child.GetName())))) {
1755 auto out = (child.fComp) ? acquire(child.fComp) : getObject<RooAbsArg>(child.GetName());
1756 // don't add a coef if in 'all-extended' mode and this pdf is extendable
1757 auto _pdf = std::dynamic_pointer_cast<RooAbsPdf>(out);
1758 if (!_pdf) {
1759 throw std::runtime_error("Something went wrong with pdf acquisition");
1760 }
1761
1762 if (auto _ax = GetXaxis(); _ax && dynamic_cast<RooAbsRealLValue *>(_ax->GetParent()) &&
1763 _pdf->dependsOn(*static_cast<RooAbsArg *>(_ax->GetParent()))) {
1764 auto _p = _pdf;
1765
1766 if (auto _boundaries = std::unique_ptr<std::list<double>>(_p->binBoundaries(
1767 *dynamic_cast<RooAbsRealLValue *>(_ax->GetParent()), -std::numeric_limits<double>::infinity(),
1768 std::numeric_limits<double>::infinity()));
1769 !_boundaries && _ax->GetNbins() > 0) {
1770#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 24, 00)
1771 Warning("Add", "Adding unbinned pdf %s to binned %s - will wrap with RooBinSamplingPdf(...)",
1772 _p->GetName(), GetName());
1773 _p = acquireNew<RooBinSamplingPdf>(TString::Format("%s_binned", _p->GetName()), _p->GetTitle(),
1774 *dynamic_cast<RooAbsRealLValue *>(_ax->GetParent()), *_p);
1775 _p->setStringAttribute("alias", std::dynamic_pointer_cast<RooAbsArg>(out)->getStringAttribute("alias"));
1776 if (!_p->getStringAttribute("alias"))
1777 _p->setStringAttribute("alias", out->GetName());
1778#else
1779 throw std::runtime_error(
1780 "unsupported addition of unbinned pdf to binned model - please upgrade to at least ROOT 6.24");
1781#endif
1782 _pdf = _p;
1783 }
1784 }
1785
1786 if (!(_pdf->canBeExtended() && p->coefList().empty())) {
1787 // if extended, use an extended binding as the coef
1788 // otherwise e.g. if adding a RooRealSumPdf the stacked histograms will be above the
1789 // actual pdf histogram because the pdf histogram is just normalized down
1790 if (_pdf->canBeExtended()) {
1791 // FIXME: ExtendedBinding needs the obs list passing to it ... should be fixed in RooFit
1792 // until then, this will return "1" and so the pdf's histograms wont be normalized properly in relation
1793 // to stacks of its comps
1794 const_cast<RooArgList &>(p->coefList())
1795 .add(*acquireNew<RooExtendedBinding>(TString::Format("%s_extBind", _pdf->GetName()),
1796 TString::Format("Expected Events of %s", _pdf->GetTitle()),
1797 *_pdf));
1798 } else {
1799 const_cast<RooArgList &>(p->coefList()).add(*acquire2<RooAbsArg, RooRealVar>("1", "1", 1));
1800 }
1801 }
1802 const_cast<RooArgList &>(p->pdfList()).add(*_pdf);
1803 sterilize();
1804 return xRooNode(*_pdf, *this);
1805 } else if ((child.get<TH1>() || child.get<RooAbsReal>() ||
1806 (!child.get() && getObject<RooAbsReal>(child.GetName()))) &&
1807 !child.get<RooAbsPdf>()) {
1808 RooRealSumPdf *_pdf = nullptr;
1809 bool tooMany(false);
1810 for (auto &pp : factors()) {
1811 if (auto _p = pp->get<RooRealSumPdf>(); _p) {
1812 if (_pdf) {
1813 _pdf = nullptr;
1814 tooMany = true;
1815 break;
1816 } // more than one!
1817 _pdf = _p;
1818 }
1819 }
1820 if (_pdf) {
1821 return xRooNode(*_pdf, *this).Add(child);
1822 } else if (!tooMany) {
1823 // create a RooRealSumPdf to hold the child
1824 auto _sumpdf = Add(*acquireNew<RooRealSumPdf>(TString::Format("%s_samples", p->GetName()),
1825 TString::Format("%s samples", GetTitle()), RooArgList(),
1826 RooArgList(), true));
1827 _sumpdf.get<RooAbsArg>()->setStringAttribute("alias", "samples");
1828 return _sumpdf.Add(child);
1829 }
1830 }
1831 }
1832
1833 if (auto p = get<RooRealSumPdf>(); p) {
1834 std::shared_ptr<TObject> out;
1835 auto cc = child.fComp;
1836 bool isConverted = (cc != child.convertForAcquisition(*this, sOpt));
1837 if (child.get<RooAbsReal>()) {
1838 out = acquire(child.fComp);
1839 if (std::dynamic_pointer_cast<TH1>(cc) && !TString(cc->GetOption()).Contains("nostyle")) {
1840 xRooNode(out, *this).style(cc.get()); // transfer style if adding a histogram
1841 }
1842 }
1843 if (!child.fComp && getObject<RooAbsReal>(child.GetName())) {
1844 Info("Add", "Adding existing function %s to %s", child.GetName(), p->GetName());
1845 out = getObject<RooAbsReal>(child.GetName());
1846 }
1847
1848 if (!out && !child.fComp) {
1849 std::shared_ptr<RooAbsArg> _func;
1850 // a null node .. so create either a new RooProduct or RooHistFunc if has observables (or no deps but has
1851 // x-axis)
1852 auto _obs = robs();
1853 if (!_obs.empty() || GetXaxis()) {
1854 if (_obs.empty()) {
1855 // using X axis to construct hist
1856 auto _ax = dynamic_cast<Axis2 *>(GetXaxis());
1857 auto t = TH1::AddDirectoryStatus();
1858 TH1::AddDirectory(false);
1859 auto h =
1860 std::make_unique<TH1D>(child.GetName(), child.GetTitle(), _ax->GetNbins(), _ax->binning()->array());
1862 h->GetXaxis()->SetName(TString::Format("%s;%s", _ax->GetParent()->GetName(), _ax->GetName()));
1863 // technically convertForAcquisition has already acquired so no need to re-acquire but should be harmless
1864 _func = std::dynamic_pointer_cast<RooAbsArg>(acquire(xRooNode(*h).convertForAcquisition(*this)));
1865 } else if (_obs.size() == 1) {
1866 // use the single obs to make a TH1D
1867 auto _x = _obs.at(0)->get<RooAbsLValue>();
1868 auto _bnames = _x->getBinningNames();
1869 TString binningName = p->getStringAttribute("binning");
1870 for (auto &b : _bnames) {
1871 if (b == p->GetName()) {
1872 binningName = p->GetName();
1873 break;
1874 }
1875 }
1876 auto t = TH1::AddDirectoryStatus();
1877 TH1::AddDirectory(false);
1878 auto h = std::make_unique<TH1D>(child.GetName(), child.GetTitle(), _x->numBins(binningName),
1879 _x->getBinningPtr(binningName)->array());
1881 h->GetXaxis()->SetName(
1882 TString::Format("%s;%s", dynamic_cast<TObject *>(_x)->GetName(), binningName.Data()));
1883 // technically convertForAcquisition has already acquired so no need to re-acquire but should be harmless
1884 _func = std::dynamic_pointer_cast<RooAbsArg>(acquire(xRooNode(*h).convertForAcquisition(*this)));
1885 Info("Add", "Created densityhisto factor %s (xaxis=%s) for %s", _func->GetName(), _obs.at(0)->GetName(),
1886 p->GetName());
1887 } else {
1888 throw std::runtime_error("Unsupported creation of new component in SumPdf for this many obs");
1889 }
1890 } else {
1891 _func = acquireNew<RooProduct>(TString::Format("%s_%s", p->GetName(), child.GetName()), child.GetTitle(),
1892 RooArgList());
1893 }
1894 _func->setStringAttribute("alias", child.GetName());
1895 out = _func;
1896 }
1897
1898 if (auto _f = std::dynamic_pointer_cast<RooHistFunc>(
1899 (child.get<RooProduct>()) ? child.factors()[child.GetName()]->fComp : out);
1900 _f) {
1901 // adding a histfunc directly to a sumpdf, should be a density
1902 _f->setAttribute("density");
1903 if (_f->getAttribute("autodensity")) {
1904 // need to divide by bin widths first
1905 for (int i = 0; i < _f->dataHist().numEntries(); i++) {
1906 auto bin_pars = _f->dataHist().get(i);
1907 _f->dataHist().set(*bin_pars, _f->dataHist().weight() / _f->dataHist().binVolume(*bin_pars));
1908 }
1909 _f->setAttribute("autodensity", false);
1910 _f->setValueDirty();
1911 }
1912
1913 // promote the axis vars to observables
1914 // can't use original child as might refer to unacquired deps
1915 for (auto &x : xRooNode("tmp", _f).vars()) {
1916 x->get<RooAbsArg>()->setAttribute("obs");
1917 }
1918 if (isConverted) {
1919 Info("Add", "Created %s factor RooHistFunc::%s for %s",
1920 _f->getAttribute("density") ? "densityhisto" : "histo", _f->GetName(), p->GetName());
1921 }
1922 }
1923
1924 if (auto _p = std::dynamic_pointer_cast<RooAbsPdf>(out); _p) {
1925 // adding a pdf to a RooRealSumPdf will replace it with a RooAddPdf and put the RooRealSumPdf inside that
1926 // if pdf is extended will use in the "no coefficients" state, where the expectedEvents are taking from
1927 // the pdf integrals
1928 TString newName(_p->GetName());
1929 newName.ReplaceAll("_samples", "");
1930 newName += "_components";
1931 Warning("Add", "converting samples to components");
1932
1933 if (auto _ax = GetXaxis(); _ax && dynamic_cast<RooAbsRealLValue *>(_ax->GetParent()) &&
1934 _p->dependsOn(*static_cast<RooAbsArg *>(_ax->GetParent()))) {
1935
1936 if (auto _boundaries = std::unique_ptr<std::list<double>>(_p->binBoundaries(
1937 *dynamic_cast<RooAbsRealLValue *>(_ax->GetParent()), -std::numeric_limits<double>::infinity(),
1938 std::numeric_limits<double>::infinity()));
1939 !_boundaries && _ax->GetNbins() > 0) {
1940#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 24, 00)
1941 Warning("Add", "Adding unbinned pdf %s to binned %s - will wrap with RooBinSamplingPdf(...)",
1942 _p->GetName(), GetName());
1943 _p = acquireNew<RooBinSamplingPdf>(TString::Format("%s_binned", _p->GetName()), _p->GetTitle(),
1944 *dynamic_cast<RooAbsRealLValue *>(_ax->GetParent()), *_p);
1945 _p->setStringAttribute("alias", std::dynamic_pointer_cast<RooAbsArg>(out)->getStringAttribute("alias"));
1946 if (!_p->getStringAttribute("alias"))
1947 _p->setStringAttribute("alias", out->GetName());
1948#else
1949 throw std::runtime_error(
1950 "unsupported addition of unbinned pdf to binned model - please upgrade to at least ROOT 6.24");
1951#endif
1952 }
1953 }
1954
1955 // require to be extended to be in coefficient-free mode ...
1956 // otherwise would lose the integral of the sumPdf (can't think of way to have a coef be the integral)
1957 if (!_p->canBeExtended()) {
1958 _p = acquireNew<RooExtendPdf>(TString::Format("%s_extended", _p->GetName()), _p->GetTitle(), *_p,
1959 *acquire2<RooAbsReal, RooRealVar>("1", "1", 1));
1960 }
1961
1962 return *(Replace(*acquireNew<RooAddPdf>(newName, _p->GetTitle(), RooArgList(*p, *_p)))
1963 .browse()[1]); // returns second node.
1964 }
1965
1966 if (auto _f = std::dynamic_pointer_cast<RooAbsReal>(out); _f) {
1967
1968 // todo: if adding a pdf, should actually replace RooRealSumPdf with a RooAddPdf and put
1969 // the sumPdf and *this* pdf inside that pdf
1970 // only exception is the binSamplingPdf below to integrate unbinned functions across bins
1971
1972 if (auto _ax = GetXaxis(); _ax && dynamic_cast<RooAbsRealLValue *>(_ax->GetParent()) &&
1973 _f->dependsOn(*static_cast<RooAbsArg *>(_ax->GetParent()))) {
1974
1975 if (auto _boundaries = std::unique_ptr<std::list<double>>(_f->binBoundaries(
1976 *dynamic_cast<RooAbsRealLValue *>(_ax->GetParent()), -std::numeric_limits<double>::infinity(),
1977 std::numeric_limits<double>::infinity()));
1978 !_boundaries && _ax->GetNbins() > 0) {
1979#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 24, 00)
1980 Warning(
1981 "Add",
1982 "Adding unbinned function %s to binned %s - will wrap with RooRealSumPdf(RooBinSamplingPdf(...))",
1983 _f->GetName(), GetName());
1984 auto sumPdf = acquireNew<RooRealSumPdf>(TString::Format("%s_pdfWrapper", _f->GetName()), _f->GetTitle(),
1985 *_f, *acquire2<RooAbsArg, RooRealVar>("1", "1", 1), true);
1986 sumPdf->setStringAttribute("alias", _f->getStringAttribute("alias"));
1987 if (!sumPdf->getStringAttribute("alias"))
1988 sumPdf->setStringAttribute("alias", out->GetName());
1989 _f = acquireNew<RooBinSamplingPdf>(TString::Format("%s_binned", _f->GetName()), _f->GetTitle(),
1990 *dynamic_cast<RooAbsRealLValue *>(_ax->GetParent()), *sumPdf);
1991 _f->setStringAttribute("alias", std::dynamic_pointer_cast<RooAbsArg>(out)->getStringAttribute("alias"));
1992 if (!_f->getStringAttribute("alias"))
1993 _f->setStringAttribute("alias", out->GetName());
1994#else
1995 throw std::runtime_error(
1996 "unsupported addition of unbinned function to binned model - please upgrade to at least ROOT 6.24");
1997#endif
1998 }
1999 }
2000
2001 const_cast<RooArgList &>(p->coefList()).add(*acquire2<RooAbsArg, RooRealVar>("1", "1", 1));
2002 const_cast<RooArgList &>(p->funcList()).add(*_f);
2003 // inherit binning if we dont have one yet
2004 if (!p->getStringAttribute("binning"))
2005 p->setStringAttribute("binning", _f->getStringAttribute("binning"));
2006
2007 xRooNode _out(_f, *this);
2008 if (auto gf = p->getStringAttribute("global_factors"); gf) {
2009 TStringToken pattern(gf, ";");
2010 while (pattern.NextToken()) {
2011 auto fac = getObject<RooAbsReal>(pattern.Data());
2012 if (!fac) {
2013 throw std::runtime_error(TString::Format("Could not find global factor %s", pattern.Data()));
2014 }
2015 _out.Multiply(fac);
2016 }
2017 }
2018 sterilize();
2019 // clear children for reload and update shared axis
2020 clear();
2021 fXAxis.reset();
2022 p->setStringAttribute("xvar", nullptr);
2023 browse();
2024 return _out;
2025 }
2026 } else if (auto p2 = get<RooProdPdf>(); p2) {
2027 // can "add" to a RooProdPdf provided trying to add a RooAbsReal not a RooAbsPdf and have a zero or 1
2028 // RooRealSumPdf child.convertForAcquisition(*this); - don't convert here because want generated objects named
2029 // after roorealsumpdf
2030 if (child.get<RooAbsPdf>() || (!child.get() && getObject<RooAbsPdf>(child.GetName()))) {
2031 // can add if 0 or 1 RooAddPdf ....
2032 RooAddPdf *_pdf = nullptr;
2033 bool tooMany(false);
2034 for (auto &pp : factors()) {
2035 if (auto _p = pp->get<RooAddPdf>(); _p) {
2036 if (_pdf) {
2037 _pdf = nullptr;
2038 tooMany = true;
2039 break;
2040 } // more than one!
2041 _pdf = _p;
2042 }
2043 }
2044 if (_pdf) {
2045 return xRooNode(*_pdf, *this).Add(child);
2046 } else if (!tooMany) {
2047 auto out = this->operator[]("components")->Add(child);
2048 return out;
2049 }
2050 } else if ((child.get<TH1>() || child.get<RooAbsReal>() ||
2051 (!child.get() && getObject<RooAbsReal>(child.GetName()))) &&
2052 !child.get<RooAbsPdf>()) {
2053 RooRealSumPdf *_pdf = nullptr;
2054 RooAddPdf *_backup = nullptr;
2055 bool tooMany(false);
2056 for (auto &pp : factors()) {
2057 if (auto _p = pp->get<RooRealSumPdf>(); _p) {
2058 if (_pdf) {
2059 _pdf = nullptr;
2060 tooMany = true;
2061 break;
2062 } // more than one!
2063 _pdf = _p;
2064 } else if (auto _p2 = pp->get<RooAddPdf>(); _p2) {
2065 _backup = _p2;
2066 for (auto &_pdfa : pp->components()) {
2067 if (auto _p3 = _pdfa->get<RooRealSumPdf>(); _p3) {
2068 if (_pdf) {
2069 _pdf = nullptr;
2070 tooMany = true;
2071 break;
2072 } // more than one!
2073 _pdf = _p3;
2074 }
2075 }
2076 }
2077 }
2078 if (_pdf) {
2079 return xRooNode(*_pdf, *this).Add(child);
2080 } else if (_backup) {
2081 // added *INSIDE* the addPdf -- will create a RooRealSumPdf to hold it
2082 return xRooNode(*_backup, *this).Add(child);
2083 } else if (!tooMany) {
2084 auto out = this->operator[]("samples")->Add(child);
2085 // clear our x-axis to re-evaluate
2086 fXAxis.reset();
2087 p2->setStringAttribute("xvar", nullptr);
2088 return out;
2089 }
2090 }
2091 } else if (auto s = get<RooSimultaneous>(); s) {
2092
2093 // adding to a simultaneous means adding a bin
2094 return bins().Add(child);
2095
2096 // if the child is a RooAbsPdf can just add it as a new channel using name of pdf as the channel name
2097 // if child is a histogram, will create a RooProdPdf
2098
2099 } else if (auto w = get<RooWorkspace>(); w) {
2100 child.convertForAcquisition(*this);
2101 if (child.get()) {
2102 if (auto _d = child.get<RooAbsData>()) {
2103 // don't use acquire method to import, because that adds datasets as Embedded
2104 if (!w->import(*_d)) {
2105 return xRooNode(child.GetName(), *w->data(child.GetName()), *this);
2106 } else {
2107 throw std::runtime_error(
2108 TString::Format("Could not import dataset %s into workspace %s", child.GetName(), w->GetName())
2109 .Data());
2110 }
2111 } else {
2112 auto out = acquire(child.fComp);
2113 if (out)
2114 return xRooNode(child.GetName(), out, *this);
2115 }
2116 }
2117
2118 if (!child.empty() || child.fFolder == "!pdfs") {
2119 // create a RooSimultaneous using the children as the channels
2120 // children either have "=" in name if specifying channel cat name or otherwise assume
2121 std::string catName = "channelCat";
2122 if (!child.empty()) {
2123 if (TString ss = child.at(0)->GetName(); ss.Contains("=")) {
2124 catName = ss(0, ss.Index('='));
2125 }
2126 }
2127 auto _cat = acquire<RooCategory>(catName.c_str(), catName.c_str());
2128 _cat->setAttribute("obs");
2129 auto out = acquireNew<RooSimultaneous>(child.GetName(), child.GetTitle(), *_cat);
2130 Info("Add", "Created model RooSimultaneous::%s in workspace %s", out->GetName(), w->GetName());
2131 return xRooNode(out, *this);
2132 }
2133 }
2134
2135 if (sOpt == "pdf") {
2136 // can only add a pdf to a workspace
2137 if (get<RooWorkspace>()) {
2138 const_cast<xRooNode &>(child).fFolder = "!pdfs";
2139 return Add(child);
2140 }
2141 } else if (sOpt == "channel") {
2142 // can add to a model or to a workspace (creates a RooProdPdf either way)
2143 if (get<RooSimultaneous>()) {
2144 return Vary(child);
2145 } else if (get<RooWorkspace>()) {
2146 std::shared_ptr<TObject> out;
2147 child.convertForAcquisition(*this);
2148 if (child.get<RooAbsPdf>()) {
2149 out = acquire(child.fComp);
2150 } else if (!child.fComp) {
2151 out = acquireNew<RooProdPdf>(child.GetName(),
2152 (strlen(child.GetTitle())) ? child.GetTitle() : child.GetName(), RooArgList());
2153 Info("Add", "Created channel RooProdPdf::%s in workspace %s", out->GetName(), get()->GetName());
2154 }
2155 return xRooNode(out, *this);
2156 }
2157 } else if (sOpt == "sample" || sOpt == "func") {
2158 if (get<RooProdPdf>()) {
2159 auto _mainChild = mainChild();
2160 if (_mainChild.get<RooRealSumPdf>()) {
2161 return _mainChild.Add(child, sOpt == "func" ? "func" : "");
2162 } else {
2163 return (*this)["samples"]->Add(child, sOpt == "func" ? "func" : "");
2164 }
2165 }
2166 } else if (sOpt == "dataset") {
2167 if (get<RooWorkspace>()) {
2168 // const_cast<xRooNode&>(child).fFolder = "!datasets";return Add(child);
2169 return (*this).datasets().Add(child);
2170 }
2171 }
2172
2173 if (considerType) {
2174
2175 // interpret 'adding' here as dependent on the object type ...
2176 if (get<RooSimultaneous>()) {
2177 return bins().Add(child);
2178 } else if (TString(child.GetName()).Contains('=')) {
2179 return variations().Add(child);
2180 } else if (get<RooProduct>() || get<RooProdPdf>()) {
2181 return factors().Add(child);
2182 }
2183 }
2184
2185 // Nov 2022 - removed ability to add placeholders ... could bring back if rediscover need for them
2186 // if (!child.get() && child.empty() && strlen(child.GetName())) {
2187 // // can add a 'placeholder' node, note it will be deleted at the next browse
2188 // xRooNode out(child.GetName(),nullptr,*this);
2189 // out.SetTitle(child.GetTitle());
2190 // emplace_back(std::make_shared<xRooNode>(out));
2191 // // update the parent in the out node so that it's copy of the parent knows it has itself in it
2192 // // actually maybe not want this :-/
2193 // //out.fParent = std::make_shared<Node2>(*this);
2194 // for(auto o : *gROOT->GetListOfBrowsers()) {
2195 // if(auto b = dynamic_cast<TBrowser*>(o); b && b->GetBrowserImp()){
2196 // if(auto _b = dynamic_cast<TGFileBrowser*>(
2197 // dynamic_cast<TRootBrowser*>(b->GetBrowserImp())->fActBrowser ); _b) {
2198 // auto _root = _b->fRootDir;
2199 // if (!_root) _root = _b->fListTree->GetFirstItem();
2200 // if (auto item = _b->fListTree->FindItemByObj(_root,this); item) {
2201 // _b->fListTree->AddItem(item,back()->GetName(),back().get());
2202 // }
2203 // }
2204 // }
2205 // }
2206 // return out;
2207 // }
2208
2209 throw std::runtime_error(TString::Format("Cannot add %s to %s", child.GetName(), GetName()));
2210}
2211
2212std::string xRooNode::GetPath() const
2213{
2214 if (!fParent)
2215 return GetName();
2216 return fParent->GetPath() + "/" + GetName();
2217}
2218
2220{
2221 // std::cout << "deleting " << GetPath() << std::endl;
2222}
2223
2225{
2226 if (auto a = get<RooAbsArg>()) {
2227 a->setAttribute("hidden", set);
2228 // if(auto item = GetTreeItem(nullptr); item) {
2229 // if(set) item->SetColor(kRed);
2230 // else item->ClearColor();
2231 // }
2232 }
2233}
2235{
2236 auto a = get<RooAbsArg>();
2237 if (a)
2238 return a->getAttribute("hidden");
2239 return false;
2240}
2241
2243{
2244
2245 if (get() == rhs.get()) {
2246 // nothing to do because objects are identical
2247 return *this;
2248 }
2249
2250 // Info("Combine","Combining %s into %s",rhs.GetPath().c_str(),GetPath().c_str());
2251
2252 // combine components, factors, and variations ... when there is a name clash will combine on that object
2253 for (auto &c : rhs.components()) {
2254 if (auto _c = components().find(c->GetName()); _c) {
2255 _c->Combine(*c);
2256 } else {
2257 Add(*c);
2258 }
2259 }
2260
2261 for (auto &f : rhs.factors()) {
2262 if (auto _f = factors().find(f->GetName()); _f) {
2263 _f->Combine(*f);
2264 } else {
2265 Multiply(*f);
2266 }
2267 }
2268
2269 for (auto &v : rhs.variations()) {
2270 if (auto _v = variations().find(v->GetName()); _v) {
2271 _v->Combine(*v);
2272 } else {
2273 Vary(*v);
2274 }
2275 }
2276
2277 // todo: Should also transfer over binnings of observables
2278
2279 return *this;
2280}
2281
2282xRooNode xRooNode::shallowCopy(const std::string &name, std::shared_ptr<xRooNode> parent)
2283{
2284 xRooNode out(name.c_str(), nullptr,
2285 parent /*? parent : fParent -- was passing fParent for getObject benefit before fProvider concept*/);
2286 // if(!parent) out.fAcquirer = true;
2287 if (!parent)
2288 out.fProvider = fParent;
2289
2290 auto o = get();
2291 if (!o) {
2292 return out;
2293 }
2294
2295 if (auto s = get<RooSimultaneous>(); s) {
2296 auto chans = bins();
2297 if (!chans.empty()) {
2298 // create a new RooSimultaneous with shallow copies of each channel
2299
2300 std::shared_ptr<RooSimultaneous> pdf = out.acquire<RooSimultaneous>(
2301 name.c_str(), o->GetTitle(), const_cast<RooAbsCategoryLValue &>(s->indexCat()));
2302
2303 for (auto &c : chans) {
2304 TString cName(c->GetName());
2305 cName = cName(cName.Index('=') + 1, cName.Length());
2306 // by passing out as the parent, will ensure out acquires everything created
2307 auto c_copy =
2308 c->shallowCopy(name + "_" + c->get()->GetName(), std::shared_ptr<xRooNode>(&out, [](xRooNode *) {}));
2309 pdf->addPdf(*dynamic_cast<RooAbsPdf *>(c_copy.get()), cName);
2310 }
2311 out.fComp = pdf;
2312 return out;
2313 }
2314 } else if (auto p = dynamic_cast<RooProdPdf *>(o); p) {
2315 // main pdf will be copied too
2316 std::shared_ptr<RooProdPdf> pdf =
2317 std::dynamic_pointer_cast<RooProdPdf>(out.acquire(std::shared_ptr<TObject>(p->Clone(/*name.c_str()*/)), false,
2318 true)); // use clone to copy all attributes etc too
2319 auto main = mainChild();
2320 if (main) {
2321 auto newMain =
2322 std::dynamic_pointer_cast<RooAbsArg>(out.acquire(std::shared_ptr<TObject>(main->Clone()), false, true));
2323 std::cout << newMain << " " << newMain->GetName() << std::endl;
2324 // pdf->replaceServer(*pdf->pdfList().find(main->GetName()), *newMain, true, true);
2325 // const_cast<RooArgList&>(pdf->pdfList()).replace(*pdf->pdfList().find(main->GetName()), *newMain);
2326 pdf->redirectServers(RooArgList(*newMain));
2327 }
2328 out.fComp = pdf;
2329 out.sterilize();
2330 return out;
2331 }
2332
2333 return out;
2334}
2335
2337{
2338 static std::unique_ptr<cout_redirect> capture;
2339 std::string captureStr;
2340 bool doCapture = false;
2341 if (!capture && gROOT->FromPopUp()) { // FromPopUp means user executed from the context menu
2342 capture = std::make_unique<cout_redirect>(captureStr);
2343 doCapture = true;
2344 }
2345
2346 TString sOpt(opt);
2347 int depth = 0;
2348 if (sOpt.Contains("depth=")) {
2349 depth = TString(sOpt(sOpt.Index("depth=") + 6, sOpt.Length())).Atoi();
2350 sOpt.ReplaceAll(TString::Format("depth=%d", depth), "");
2351 }
2352 int indent = 0;
2353 if (sOpt.Contains("indent=")) {
2354 indent = TString(sOpt(sOpt.Index("indent=") + 7, sOpt.Length())).Atoi();
2355 sOpt.ReplaceAll(TString::Format("indent=%d", indent), "");
2356 }
2357 bool _more = sOpt.Contains("m");
2358 if (_more)
2359 sOpt.Replace(sOpt.Index("m"), 1, "");
2360 if (sOpt != "")
2361 _more = true;
2362
2363 if (indent == 0) { // only print self if not indenting (will already be printed above if tree traverse)
2364 std::cout << GetPath();
2365 if (get() && get() != this) {
2366 std::cout << ": ";
2367 if (_more || (get<RooAbsArg>() && get<RooAbsArg>()->isFundamental()) || get<RooConstVar>() ||
2368 get<RooAbsData>() || get<RooProduct>() || get<RooFitResult>()) {
2369 auto _deps = coords(false).argList(); // want to revert coords after print
2370 auto _snap = std::unique_ptr<RooAbsCollection>(_deps.snapshot());
2371 coords(); // move to coords before printing (in case this matters)
2372 get()->Print(sOpt);
2373 if (auto _fr = get<RooFitResult>(); _fr && dynamic_cast<RooStringVar *>(_fr->constPars().find(".log"))) {
2374 std::cout << "Minimization Logs:" << std::endl;
2375 std::cout << dynamic_cast<RooStringVar *>(_fr->constPars().find(".log"))->getVal() << std::endl;
2376 }
2377 _deps.assignValueOnly(*_snap);
2378 // std::cout << std::endl;
2379 } else {
2380 TString _suffix = "";
2381 if (auto _type = GetNodeType(); strlen(_type)) {
2382 // decided not to show const values until figure out how to update if value changes
2383 /*if (TString(_type)=="Const") _name += TString::Format("
2384 [%s=%g]",_type,v->get<RooConstVar>()->getVal()); else*/
2385 _suffix += TString::Format(" [%s]", _type);
2386 }
2387 if (auto fv = get<RooFormulaVar>()) {
2388 TString formu = TString::Format(" [%s]", fv->expression());
2389 for (size_t i = 0; i < fv->dependents().size(); i++) {
2390 formu.ReplaceAll(TString::Format("x[%zu]", i), fv->dependents()[i].GetName());
2391 }
2392 _suffix += formu;
2393 } else if (auto gv = get<RooGenericPdf>()) {
2394 TString formu = TString::Format(" [%s]", gv->expression());
2395 for (size_t i = 0; i < gv->dependents().size(); i++) {
2396 formu.ReplaceAll(TString::Format("x[%zu]", i), gv->dependents()[i].GetName());
2397 }
2398 _suffix += formu;
2399 }
2400 std::cout << get()->ClassName() << "::" << get()->GetName() << _suffix.Data() << std::endl;
2401 }
2402
2403 } else if (!get()) {
2404 std::cout << std::endl;
2405 }
2406 }
2407 const_cast<xRooNode *>(this)->browse();
2408 std::vector<std::string> folderNames;
2409 for (auto &k : *this) {
2410 if (std::find(folderNames.begin(), folderNames.end(), k->fFolder) == folderNames.end()) {
2411 folderNames.push_back(k->fFolder);
2412 }
2413 }
2414 for (auto &f : folderNames) {
2415 int i = 0;
2416 int iindent = indent;
2417 if (!f.empty()) {
2418 for (int j = 0; j < indent; j++)
2419 std::cout << " ";
2420 std::cout << f << std::endl;
2421 iindent += 1;
2422 }
2423 for (auto &k : *this) {
2424 if (k->fFolder != f) {
2425 i++;
2426 continue;
2427 }
2428 for (int j = 0; j < iindent; j++)
2429 std::cout << " ";
2430 std::cout << i++ << ") " << k->GetName() << " : ";
2431 if (k->get()) {
2432 if (_more || (k->get<RooAbsArg>() && k->get<RooAbsArg>()->isFundamental()) || k->get<RooConstVar>() ||
2433 k->get<RooAbsData>() /*|| k->get<RooProduct>()*/) {
2434 auto _deps = k->coords(false).argList();
2435 auto _snap = std::unique_ptr<RooAbsCollection>(_deps.snapshot());
2436 k->coords(); // move to coords before printing (in case this matters)
2437 k->get()->Print(sOpt); // assumes finishes with an endl
2438 _deps.assignValueOnly(*_snap);
2439 } else {
2440 TString _suffix = "";
2441 if (auto _type = k->GetNodeType(); strlen(_type)) {
2442 // decided not to show const values until figure out how to update if value changes
2443 /*if (TString(_type)=="Const") _name += TString::Format("
2444 [%s=%g]",_type,v->get<RooConstVar>()->getVal()); else*/
2445 _suffix += TString::Format(" [%s]", _type);
2446 }
2447 if (auto fv = k->get<RooFormulaVar>()) {
2448 TString formu = TString::Format(" [%s]", fv->expression());
2449 for (size_t j = 0; j < fv->dependents().size(); j++) {
2450 formu.ReplaceAll(TString::Format("x[%zu]", j), fv->dependents()[j].GetName());
2451 }
2452 _suffix += formu;
2453 } else if (auto gv = k->get<RooGenericPdf>()) {
2454 TString formu = TString::Format(" [%s]", gv->expression());
2455 for (size_t j = 0; j < gv->dependents().size(); j++) {
2456 formu.ReplaceAll(TString::Format("x[%zu]", j), gv->dependents()[j].GetName());
2457 }
2458 _suffix += formu;
2459 }
2460 std::cout << k->get()->ClassName() << "::" << k->get()->GetName() << _suffix.Data() << std::endl;
2461 }
2462 if (depth != 0) {
2463 k->Print(sOpt + TString::Format("depth=%dindent=%d", depth - 1, iindent + 1));
2464 }
2465 } else
2466 std::cout << " NULL " << std::endl;
2467 }
2468 }
2469 if (doCapture) {
2470 capture.reset(); // no captureStr has the string to display
2471 // inject line breaks to avoid msgbox being too wide
2472 size_t lastBreak = 0;
2473 std::string captureStrWithBreaks;
2474 for (size_t i = 0; i < captureStr.size(); i++) {
2475 captureStrWithBreaks += captureStr[i];
2476 if (captureStr[i] == '\n') {
2477 lastBreak = i;
2478 }
2479 if (i - lastBreak > 150) {
2480 captureStrWithBreaks += '\n';
2481 lastBreak = i;
2482 }
2483 }
2484 const TGWindow *w =
2485 (gROOT->GetListOfBrowsers()->At(0))
2486 ? dynamic_cast<TGWindow *>(static_cast<TBrowser *>(gROOT->GetListOfBrowsers()->At(0))->GetBrowserImp())
2487 : gClient->GetRoot();
2488 new TGMsgBox(gClient->GetRoot(), w, GetName(),
2489 captureStrWithBreaks.c_str()); //,nullptr,kMBDismiss,nullptr,kVerticalFrame,kTextLeft|kTextCenterY);
2490 }
2491}
2492
2494{
2495 if (!child.get()) {
2496
2497 if (auto v = get<RooRealVar>(); v) {
2498
2499 TString constrType = child.GetName();
2500 double mean = std::numeric_limits<double>::quiet_NaN();
2501 double sigma = mean;
2502 if (constrType.BeginsWith("gaussian(")) {
2503 // extract the mean and stddev parameters
2504 // if only one given, it is the stddev
2505 if (constrType.Contains(",")) {
2506 mean = TString(constrType(9, constrType.Index(',') - 9)).Atof();
2507 sigma = TString(constrType(constrType.Index(',') + 1, constrType.Index(')') - constrType.Index(',') + 1))
2508 .Atof();
2509 } else {
2510 mean = std::numeric_limits<double>::quiet_NaN(); // will use the var current value below to set mean
2511 sigma = TString(constrType(9, constrType.Index(')') - 9)).Atof();
2512 }
2513 constrType = "normal";
2514 } else if (constrType == "normal") {
2515 mean = 0;
2516 sigma = 1;
2517 } else if (constrType == "gaussian") {
2518 // extract parameters from the variable
2519 // use current value and error on v as constraint
2520 if (!v->hasError())
2521 throw std::runtime_error("No error on parameter for gaussian constraint");
2522 sigma = v->getError();
2523 mean = v->getVal();
2524 constrType = "normal";
2525 } else if (constrType == "poisson") {
2526 if (!v->hasError())
2527 throw std::runtime_error("No error on parameter for poisson constraint");
2528 mean = 1;
2529 sigma = pow(v->getVal() / v->getError(), 2);
2530 }
2531
2532 if (constrType == "poisson") {
2533 // use current value and error on v as constraint
2534 double tau_val = sigma;
2535 auto globs = acquire<RooRealVar>(Form("globs_%s", v->GetName()), Form("globs_%s", v->GetName()),
2536 v->getVal() * tau_val, (v->getVal() - 5 * v->getError()) * tau_val,
2537 (v->getVal() + 5 * v->getError()) * tau_val);
2538 globs->setConstant();
2539 globs->setAttribute("obs");
2540 globs->setAttribute("global");
2541 globs->setStringAttribute("nominal", TString::Format("%f", tau_val));
2542 auto tau = acquireNew<RooConstVar>(TString::Format("tau_%s", v->GetName()), "", tau_val);
2543 auto constr = acquireNew<RooPoisson>(
2544 Form("pois_%s", v->GetName()), TString::Format("Poisson Constraint of %s", v->GetTitle()), *globs,
2545 *acquireNew<RooProduct>(TString::Format("mean_%s", v->GetName()),
2546 TString::Format("Poisson Constraint of %s", globs->GetTitle()),
2547 RooArgList(*v, *tau)),
2548 true /* no rounding */);
2549
2550 auto out = Constrain(xRooNode(Form("pois_%s", GetName()), constr));
2551 if (!v->hasError())
2552 v->setError(mean / sqrt(tau_val)); // if v doesnt have an uncert, will put one on it now
2553 Info("Constrain", "Added poisson constraint pdf RooPoisson::%s (tau=%g) for %s", out->GetName(), tau_val,
2554 GetName());
2555 return out;
2556 } else if (constrType == "normal") {
2557
2558 auto globs = acquire<RooRealVar>(Form("globs_%s", v->GetName()), Form("globs_%s", v->GetName()), mean,
2559 mean - 10 * sigma, mean + 10 * sigma);
2560 globs->setAttribute("obs");
2561 globs->setAttribute("global");
2562 globs->setConstant();
2563
2564 globs->setStringAttribute("nominal", TString::Format("%f", mean));
2565 auto constr = acquireNew<RooGaussian>(
2566 Form("gaus_%s", v->GetName()), TString::Format("Gaussian Constraint of %s", v->GetTitle()), *globs, *v,
2567 *acquireNew<RooConstVar>(TString::Format("sigma_%s", v->GetName()), "", sigma));
2568 auto out = Constrain(xRooNode(Form("gaus_%s", GetName()), constr));
2569 if (!v->hasError())
2570 v->setError(sigma); // if v doesnt have an uncert, will put one on it now
2571 Info("Constrain", "Added gaussian constraint pdf RooGaussian::%s (mean=%g,sigma=%g) for %s", out->GetName(),
2572 mean, sigma, GetName());
2573 return out;
2574 }
2575 }
2576 } else if (auto p = child.get<RooAbsPdf>(); p) {
2577
2578 auto _me = get<RooAbsArg>();
2579 if (!_me) {
2580 throw std::runtime_error("Cannot constrain non arg");
2581 }
2582
2583 if (!p->dependsOn(*_me)) {
2584 throw std::runtime_error("Constraint does not depend on constrainee");
2585 }
2586
2587 // find a parent that can swallow this pdf ... either a RooProdPdf or a RooWorkspace
2588 auto x = fParent;
2589 while (x && !x->get<RooProdPdf>() && !x->get<RooSimultaneous>() && !x->get<RooWorkspace>()) {
2590 x = x->fParent;
2591 }
2592 if (!x) {
2593 throw std::runtime_error("Nowhere to put constraint");
2594 }
2595
2596 if (auto s = x->get<RooSimultaneous>(); s) {
2597 // put into every channel that features parameter
2598 x->browse();
2599 for (auto &c : *x) {
2600 if (auto a = c->get<RooAbsArg>(); a->dependsOn(*_me))
2601 c->Multiply(child);
2602 }
2603 return child;
2604 } else if (x->get<RooProdPdf>()) {
2605 return x->Multiply(child);
2606 } else {
2607 return x->Add(child, "+");
2608 }
2609 }
2610
2611 throw std::runtime_error(TString::Format("Cannot constrain %s", GetName()));
2612}
2613
2615{
2616
2617 class AutoUpdater {
2618 public:
2619 AutoUpdater(xRooNode &_n) : n(_n) {}
2620 ~AutoUpdater() { n.browse(); }
2621 xRooNode &n;
2622 };
2623 AutoUpdater xxx(*this);
2624
2625 if (fBinNumber != -1) {
2626 // scaling a bin ...
2627 if (child.get<RooAbsReal>()) { // if not child then let fall through to create a child and call self again below
2628 // doing a bin-multiplication .. the parent should have a ParamHistFunc called binFactors
2629 // if it doesn't then create one
2630 auto o = std::dynamic_pointer_cast<RooAbsReal>(acquire(child.fComp));
2631
2632 // get binFactor unless parent is a ParamHistFunc already ...
2633
2634 auto binFactors = (fParent->get<ParamHistFunc>()) ? fParent : fParent->factors().find("binFactors");
2635
2636 // it can happen in a loop over bins() that another node has moved fParent inside a product
2637 // so check for fParent having a client with the ORIGNAME:<name> attribute
2638 if (!binFactors && fParent->get<RooAbsArg>()) {
2639 for (auto c : fParent->get<RooAbsArg>()->clients()) {
2640 if (c->IsA() == RooProduct::Class() &&
2641 c->getAttribute(TString::Format("ORIGNAME:%s", fParent->get()->GetName()))) {
2642 // try getting binFactors out of this
2643 binFactors = xRooNode(*c).factors().find("binFactors");
2644 break;
2645 }
2646 }
2647 }
2648
2649 if (!binFactors) {
2650 fParent
2651 ->Multiply(TString::Format("%s_binFactors",
2652 (fParent->mainChild().get())
2653 ? fParent->mainChild()->GetName()
2654 : (fParent->get() ? fParent->get()->GetName() : fParent->GetName()))
2655 .Data(),
2656 "blankshape")
2657 .SetName("binFactors"); // creates ParamHistFunc with all pars = 1 (shared const)
2658 binFactors = fParent->factors().find("binFactors");
2659 if (!binFactors) {
2660 throw std::runtime_error(
2661 TString::Format("Could not create binFactors in parent %s", fParent->GetName()));
2662 }
2663 // auto phf = binFactors->get<ParamHistFunc>();
2664
2665 // create RooProducts for all the bins ... so that added factors don't affect selves
2666 int i = 1;
2667 for (auto &b : binFactors->bins()) {
2668 auto p = acquireNew<RooProduct>(TString::Format("%s_bin%d", binFactors->get()->GetName(), i),
2669 TString::Format("binFactors of bin %d", i), RooArgList());
2670 p->setStringAttribute("alias", TString::Format("%s=%g", binFactors->GetXaxis()->GetParent()->GetName(),
2671 binFactors->GetXaxis()->GetBinCenter(i)));
2672 b->Multiply(*p);
2673 i++;
2674 }
2675 }
2676 // then scale the relevant bin ... if the relevant bin is a "1" then just drop in our factor (inside a
2677 // RooProduct though, to avoid it getting modified by subsequent multiplies)
2678 auto _bin = binFactors->bins().at(fBinNumber - 1);
2679 if (auto phf = binFactors->get<ParamHistFunc>(); phf && _bin) {
2680#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
2681 RooArgList &pSet = phf->_paramSet;
2682#else
2683 RooArgList &pSet = const_cast<RooArgList &>(phf->paramList());
2684#endif
2685 if (strcmp(_bin->GetName(), "1") == 0) {
2686 RooArgList all;
2687 for (std::size_t i = 0; i < pSet.size(); i++) {
2688 if (int(i) != fBinNumber - 1) {
2689 all.add(*pSet.at(i));
2690 } else {
2691 all.add(*o);
2692 }
2693 }
2694 pSet.removeAll();
2695 pSet.add(all);
2696 } else {
2697 _bin->fBinNumber = -1; // to avoid infinite loop
2698 return _bin->Multiply(child, opt);
2699 }
2700 // } else {else if(_bin->get<RooProduct>()) {
2701 // // multiply the element which will just add it as a factor in the rooproduct
2702 // return _bin->Multiply(child,opt);
2703 // } else {
2704 // // not a rooproduct in this bin yet ... so need to replace with a rooproduct and
2705 // multiply that
2706 // // this avoids the undesired behaviour of shared binFactors getting all impacted by
2707 // mulitplies RooArgList all; auto new_p =
2708 // acquireNew<RooProduct>(TString::Format("%s_bin%d",binFactors->get()->GetName(),fBinNumber),TString::Format("binFactors
2709 // of bin %d",fBinNumber),RooArgList(*_bin->get<RooAbsArg>()));
2710 // new_p->setStringAttribute("alias","")
2711 // for (int i = 0; i < phf->_paramSet.size(); i++) {
2712 // if (i != fBinNumber - 1) all.add(*phf->_paramSet.at(i));
2713 // else all.add(*new_p);
2714 // }
2715 // phf->_paramSet.removeAll();
2716 // phf->_paramSet.add(all);
2717 // // now multiply that bin having converted it to RooProduct
2718 // return binFactors->bins().at(fBinNumber - 1)->Multiply(child,opt);
2719 // }
2720 }
2721 return xRooNode(*o, binFactors);
2722 }
2723 } else if (!get() && fParent) {
2724 // try to 'create' object based on parentage
2725 // add child as a temporary child to help with decision making
2726 auto _ref = emplace_back(std::shared_ptr<xRooNode>(&const_cast<xRooNode &>(child), [](TObject *) {}));
2727 try {
2728 fComp = fParent->Add(*this, "+").fComp;
2729 } catch (...) {
2730 resize(size() - 1);
2731 std::rethrow_exception(std::current_exception());
2732 }
2733 resize(size() - 1); // remove the temporarily added node
2734 }
2735
2736 if (!child.get()) {
2737 TString sOpt(opt);
2738 sOpt.ToLower();
2739 if (auto o = getObject<RooAbsReal>(child.GetName())) {
2740 auto out = Multiply(xRooNode(o, child.fParent));
2741 // have to protect bin case where get() is null (could change but then must change logic above too)
2742 if (get()) {
2743 Info("Multiply", "Scaled %s by existing factor %s::%s",
2744 mainChild().get() ? mainChild().get()->GetName() : get()->GetName(), o->ClassName(), o->GetName());
2745 }
2746 return out;
2747 } else if (sOpt == "norm") {
2748 if (TString(child.GetName()).Contains("[") && ws()) {
2749 // assume factory method wanted
2750 auto arg = ws()->factory(child.GetName());
2751 if (arg) {
2752 auto out = Multiply(*arg);
2753 if (get()) {
2754 Info("Multiply", "Scaled %s by new norm factor %s",
2755 mainChild().get() ? mainChild().get()->GetName() : get()->GetName(), out->GetName());
2756 }
2757 return out;
2758 }
2759 throw std::runtime_error(TString::Format("Failed to create new normFactor %s", child.GetName()));
2760 }
2761 auto out = Multiply(RooRealVar(child.GetName(), child.GetTitle(), 1, -1e-5, 100));
2762 if (get()) {
2763 Info("Multiply", "Scaled %s by new norm factor %s",
2764 mainChild().get() ? mainChild().get()->GetName() : get()->GetName(), out->GetName());
2765 }
2766 return out;
2767 } else if (sOpt == "shape" || sOpt == "histo" || sOpt == "blankshape") {
2768 // needs axis defined
2769 if (auto ax = GetXaxis(); ax) {
2770 auto h = std::shared_ptr<TH1>(BuildHistogram(dynamic_cast<RooAbsLValue *>(ax->GetParent()), true));
2771 h->Reset();
2772 for (int i = 1; i <= h->GetNbinsX(); i++) {
2773 h->SetBinContent(i, 1);
2774 }
2775 h->SetMinimum(0);
2776 h->SetMaximum(100);
2777 h->SetName(TString::Format(";%s", child.GetName())); // ; char indicates don't "rename" this thing
2778 h->SetTitle(child.GetTitle());
2779 if (sOpt.Contains("shape"))
2780 h->SetOption(sOpt);
2781 auto out = Multiply(*h);
2782 if (get()) {
2783 Info("Multiply", "Scaled %s by new %s factor %s",
2784 mainChild().get() ? mainChild().get()->GetName() : get()->GetName(), sOpt.Data(), out->GetName());
2785 }
2786 return out;
2787 }
2788 } else if (sOpt == "overall") {
2789 auto out = Multiply(acquireNew<RooStats::HistFactory::FlexibleInterpVar>(
2790 child.GetName(), child.GetTitle(), RooArgList(), 1, std::vector<double>(), std::vector<double>()));
2791 if (get() /* can happen this is null if on a bin node with no shapeFactors*/) {
2792 Info("Multiply", "Scaled %s by new overall factor %s",
2793 mainChild().get() ? mainChild().get()->GetName() : get()->GetName(), out->GetName());
2794 }
2795 return out;
2796 } else if (sOpt == "func" && ws()) {
2797 // need to get way to get dependencies .. can't pass all as causes circular dependencies issues.
2798 if (auto arg = ws()->factory(TString("expr::") + child.GetName())) {
2799 auto out = Multiply(*arg);
2800 if (get() /* can happen this is null if on a bin node with no shapeFactors*/) {
2801 Info("Multiply", "Scaled %s by new func factor %s",
2802 mainChild().get() ? mainChild().get()->GetName() : get()->GetName(), out->GetName());
2803 }
2804 return out;
2805 }
2806 }
2807 }
2808 if (auto h = child.get<TH1>(); h && strlen(h->GetOption()) == 0 && strlen(opt) > 0) {
2809 // put the option in the hist
2810 h->SetOption(opt);
2811 }
2812 if (auto w = get<RooWorkspace>(); w) {
2813 // just acquire
2814 std::shared_ptr<TObject> out;
2815 child.convertForAcquisition(*this);
2816 if (child.get<RooAbsReal>())
2817 out = acquire(child.fComp);
2818 return out;
2819 }
2820
2821 if (strcmp(GetName(), ".coef") == 0) { // covers both .coef and .coefs
2822 // need to add this into the relevant coef ... if its not a RooProduct, replace it with one first
2823 if (auto p = fParent->fParent->get<RooAddPdf>()) {
2824 for (size_t i = 0; i < p->pdfList().size(); i++) {
2825 if (p->pdfList().at(i) == fParent->get<RooAbsArg>()) {
2826 auto coefs = p->coefList().at(i);
2827 if (!coefs->InheritsFrom("RooProduct")) {
2828 RooArgList oldCoef;
2829 if (!(strcmp(coefs->GetName(), "1") == 0 || strcmp(coefs->GetName(), "ONE") == 0))
2830 oldCoef.add(*coefs);
2831 auto newCoefs = fParent->acquireNew<RooProduct>(
2832 TString::Format("coefs_%s", fParent->GetName()),
2833 TString::Format("coefficients for %s", fParent->GetName()), oldCoef);
2834 RooArgList oldCoefs;
2835 for (size_t j = 0; j < p->coefList().size(); j++) {
2836 if (i == j) {
2837 oldCoefs.add(*newCoefs);
2838 } else {
2839 oldCoefs.add(*p->coefList().at(j));
2840 }
2841 }
2842 const_cast<RooArgList &>(p->coefList()).removeAll();
2843 const_cast<RooArgList &>(p->coefList()).add(oldCoefs);
2844 coefs = newCoefs.get();
2845 }
2846 return xRooNode(*coefs, fParent).Multiply(child);
2847 }
2848 }
2849 }
2850 throw std::runtime_error("this coefs case is not supported");
2851 }
2852
2853 if (auto p = get<RooProduct>(); p) {
2854 std::shared_ptr<TObject> out;
2855 auto cc = child.fComp;
2856 bool isConverted = (child.convertForAcquisition(*this) != cc);
2857 if (child.get<RooAbsReal>())
2858 out = acquire(child.fComp);
2859
2860 // child may be a histfunc or a rooproduct of a histfunc and a paramhist if has stat errors
2861 if (auto _f = std::dynamic_pointer_cast<RooHistFunc>(
2862 (child.get<RooProduct>()) ? child.factors()[child.GetName()]->fComp : out);
2863 _f && _f->getAttribute("autodensity")) {
2864 // should we flag this as a density? yes if there's no other term marked as the density
2865 bool hasDensity = false;
2866 for (auto &f : factors()) {
2867 if (f->get<RooAbsArg>()->getAttribute("density")) {
2868 hasDensity = true;
2869 break;
2870 }
2871 }
2872 _f->setAttribute("density", !hasDensity && fParent && fParent->get<RooRealSumPdf>());
2873 if (_f->getAttribute("density")) {
2874
2875 // need to divide by bin widths first
2876 for (int i = 0; i < _f->dataHist().numEntries(); i++) {
2877 auto bin_pars = _f->dataHist().get(i);
2878 _f->dataHist().set(*bin_pars, _f->dataHist().weight() / _f->dataHist().binVolume(*bin_pars));
2879 }
2880 _f->setValueDirty();
2881
2882 // promote the axis vars to observables
2883 for (auto &x : xRooNode("tmp", _f).vars()) {
2884 x->get<RooAbsArg>()->setAttribute("obs");
2885 }
2886 }
2887 _f->setAttribute("autodensity", false);
2888 }
2889
2890 if (isConverted && child.get<RooHistFunc>()) {
2891 Info("Multiply", "Created %s factor %s in %s",
2892 child.get<RooAbsArg>()->getAttribute("density") ? "densityhisto" : "histo", child->GetName(),
2893 p->GetName());
2894 } else if (isConverted && child.get<ParamHistFunc>()) {
2895 Info("Multiply", "Created shape factor %s in %s", child->GetName(), p->GetName());
2896 }
2897
2898 if (auto _f = std::dynamic_pointer_cast<RooAbsReal>(out); _f) {
2899#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
2900 p->_compRSet.add(*_f);
2901#else
2902 const_cast<RooArgList &>(p->realComponents()).add(*_f);
2903#endif
2904 p->setValueDirty();
2905
2906 browse();
2907 xRooNode _out(_f, *this);
2908 for (auto &_par : _out.pars()) {
2909 if (auto s = _par->get<RooAbsArg>()->getStringAttribute("boundConstraint"); s) {
2910 bool found = false;
2911 for (auto &_constr : _par->constraints()) {
2912 if (strcmp(s, _constr->get()->GetName()) == 0) {
2913 // constraint is already included
2914 found = true;
2915 break;
2916 }
2917 }
2918 if (!found) {
2919 Info("Multiply", "Pulling in %s boundConstraint: %s", _par->GetName(), s);
2920 auto _pdf = getObject<RooAbsPdf>(s);
2921 if (!_pdf) {
2922 throw std::runtime_error("Couldn't find boundConstraint");
2923 }
2924 _par->Constrain(_pdf);
2925 }
2926 }
2927 }
2928 sterilize();
2929 return _out;
2930 }
2931 } else if (auto p2 = get<RooProdPdf>(); p2) {
2932
2933 std::shared_ptr<TObject> out;
2934 child.convertForAcquisition(*this);
2935 if (child.get<RooAbsPdf>()) {
2936 out = acquire(child.fComp);
2937 } else if (child.get<RooAbsReal>() && mainChild().get<RooRealSumPdf>()) {
2938 // cannot multiply a RooProdPdf by a non pdf
2939 throw std::runtime_error(TString::Format("Cannot multiply %s by non-pdf %s", GetName(), child.GetName()));
2940 // return mainChild().Add(child); - nov 2022 - used to do this but now replaced with exception above
2941 } else if (!child.get() || child.get<RooAbsReal>()) {
2942 // need to create or hide inside a sumpdf or rooadpdf
2943 std::shared_ptr<RooAbsPdf> _pdf;
2944 if (!child.get() && strcmp(child.GetName(), "components") == 0) {
2945 auto _sumpdf = acquireNew<RooAddPdf>(Form("%s_%s", p2->GetName(), child.GetName()),
2946 (strlen(child.GetTitle()) && strcmp(child.GetTitle(), child.GetName()))
2947 ? child.GetTitle()
2948 : p2->GetTitle(),
2949 RooArgList(), RooArgList());
2950 _pdf = _sumpdf;
2951 } else {
2952 auto _sumpdf = acquireNew<RooRealSumPdf>(
2953 Form("%s_%s", p2->GetName(), child.GetName()),
2954 (strlen(child.GetTitle()) && strcmp(child.GetTitle(), child.GetName())) ? child.GetTitle()
2955 : p2->GetTitle(),
2956 RooArgList(), RooArgList(), true);
2957 _sumpdf->setFloor(true);
2958 _pdf = _sumpdf;
2959 }
2960 _pdf->setStringAttribute("alias", child.GetName());
2961 // transfer axis attributes if present (TODO: should GetXaxis look beyond the immediate parent?)
2962 _pdf->setStringAttribute("xvar", p2->getStringAttribute("xvar"));
2963 _pdf->setStringAttribute("binning", p2->getStringAttribute("binning"));
2964 out = _pdf;
2965 Info("Multiply", "Created %s::%s in channel %s", _pdf->ClassName(), _pdf->GetName(), p2->GetName());
2966 if (child.get<RooAbsReal>())
2967 xRooNode(*out, *this).Add(child);
2968 }
2969
2970 if (auto _pdf = std::dynamic_pointer_cast<RooAbsPdf>(out); _pdf) {
2971#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
2972 const_cast<RooArgList &>(p2->pdfList()).add(*_pdf);
2973#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 26, 00)
2974 p2->_pdfNSetList.emplace_back(std::make_unique<RooArgSet>("nset"));
2975#else
2976 p->_pdfNSetList.Add(new RooArgSet("nset"));
2977#endif
2978 if (!p2->canBeExtended() && _pdf->canBeExtended()) {
2979 p2->_extendedIndex = p2->_pdfList.size() - 1;
2980 }
2981#else
2982 p2->addPdfs(RooArgSet(*_pdf));
2983#endif
2984 sterilize();
2985 browse();
2986 return xRooNode(_pdf, *this);
2987 }
2988 } else if (auto p3 = get<RooRealSumPdf>(); p3) {
2989 // multiplying all current and future components
2990 std::shared_ptr<TObject> out;
2991 child.convertForAcquisition(*this);
2992 if (child.get<RooAbsReal>()) {
2993 out = acquire(child.fComp);
2994 for (auto &c : components()) {
2995 c->Multiply(out);
2996 }
2997 TString s = p3->getStringAttribute("global_factors");
2998 if (s != "")
2999 s += ";";
3000 s += out->GetName();
3001 p3->setStringAttribute("global_factors", s);
3002 Info(
3003 "Multiply",
3004 "Flagged %s as a global factor in channel %s (is applied to all current and future samples in the channel)",
3005 out->GetName(), p3->GetName());
3006 return xRooNode(out, *this);
3007 }
3008
3009 } else if (auto p4 = get<RooAbsPdf>(); p4 && !(fParent && fParent->get<RooRealSumPdf>())) {
3010 // multiply the coefs (if this isn't part of a RooAddPdf or RooRealSumPdf then we will eventually throw exception
3011 return coefs().Multiply(child);
3012 } else if (auto p5 = get<RooAbsReal>(); p5 && (!get<RooAbsPdf>() || (fParent && fParent->get<RooRealSumPdf>()))) {
3013 // replace this obj with a RooProduct to allow for multiplication
3014
3015 // get the list of clients BEFORE creating the new interpolation ... seems list of clients is inaccurate after
3016 std::set<RooAbsArg *> cl;
3017 for (auto &arg : p5->clients()) {
3018 cl.insert(arg);
3019 }
3020
3021 // if multiple clients, see if only one client is in parentage route
3022 // if so, then assume thats the only client we should replace in
3023 if (cl.size() > 1) {
3024 if (cl.count(fParent->get<RooAbsArg>()) > 0) {
3025 cl.clear();
3026 cl.insert(fParent->get<RooAbsArg>());
3027 } else {
3028 Warning("Multiply", "Scaling %s that has multiple clients", p5->GetName());
3029 }
3030 }
3031
3032 auto new_p = acquireNew<RooProduct>(TString::Format("prod_%s", p5->GetName()), p5->GetTitle(), RooArgList(*p5));
3033 // copy attributes over
3034 for (auto &a : p5->attributes())
3035 new_p->setAttribute(a.c_str());
3036 for (auto &a : p5->stringAttributes())
3037 new_p->setStringAttribute(a.first.c_str(), a.second.c_str());
3038 if (!new_p->getStringAttribute("alias"))
3039 new_p->setStringAttribute("alias", p5->GetName());
3040 auto old_p = p5;
3041 new_p->setAttribute(Form("ORIGNAME:%s", old_p->GetName())); // used in redirectServers to say what this replaces
3042 for (auto arg : cl) {
3043 arg->redirectServers(RooArgSet(*new_p), false, true);
3044 }
3045
3046 fComp = new_p;
3047 return Multiply(child);
3048 }
3049
3050 // before giving up here, assume user wanted a norm factor type if child is just a name
3051 if (!child.get() && strlen(opt) == 0)
3052 return Multiply(child, "norm");
3053
3054 throw std::runtime_error(
3055 TString::Format("Cannot multiply %s by %s%s", GetPath().c_str(), child.GetName(),
3056 (!child.get() && strlen(opt) == 0) ? " (forgot to specify factor type?)" : ""));
3057}
3058
3060{
3061
3062 auto p5 = get<RooAbsArg>();
3063 if (!p5) {
3064 throw std::runtime_error("Only replacement of RooAbsArg is supported");
3065 }
3066 node.convertForAcquisition(*this, "func");
3067
3068 auto new_p = node.get<RooAbsArg>();
3069 if (!new_p) {
3070 throw std::runtime_error(TString::Format("Cannot replace with %s", node.GetName()));
3071 }
3072 auto out = acquire(node.fComp);
3073 new_p = std::dynamic_pointer_cast<RooAbsArg>(out).get();
3074
3075 std::set<RooAbsArg *> cl;
3076 for (auto &arg : p5->clients()) {
3077 if (arg == new_p)
3078 continue; // do not replace in self ... although redirectServers will prevent that anyway
3079 cl.insert(arg);
3080 }
3081
3082 // if multiple clients, see if only one client is in parentage route
3083 // if so, then assume thats the only client we should replace in
3084 if (cl.size() > 1) {
3085 if (fParent && fParent->get<RooAbsArg>() && cl.count(fParent->get<RooAbsArg>()) > 0) {
3086 cl.clear();
3087 cl.insert(fParent->get<RooAbsArg>());
3088 } else {
3089 std::stringstream clientList;
3090 for (auto c : cl)
3091 clientList << c->GetName() << ",";
3092 Warning("Replace", "Replacing %s in all clients: %s", p5->GetName(), clientList.str().c_str());
3093 }
3094 }
3095
3096 new_p->setAttribute(Form("ORIGNAME:%s", p5->GetName())); // used in redirectServers to say what this replaces
3097 for (auto arg : cl) {
3098 // if RooFormulaVar need to ensure the internal formula has been "constructed" otherwise will try to construct
3099 // it from the original expression that may have old parameter in it.
3100 if (auto p = dynamic_cast<RooFormulaVar *>(arg))
3101 p->ok(); // triggers creation of RooFormula
3102 arg->redirectServers(RooArgSet(*new_p), false, true);
3103 }
3104 return node;
3105}
3106
3108{
3109
3110 class AutoUpdater {
3111 public:
3112 AutoUpdater(xRooNode &_n) : n(_n) {}
3113 ~AutoUpdater() { n.browse(); }
3114 xRooNode &n;
3115 };
3116 AutoUpdater xxx(*this);
3117
3118 if (!get() && fParent) {
3119 // try to 'create' object based on parentage
3120 // add child as a temporary child to help with decision making
3121 auto _ref = emplace_back(std::shared_ptr<xRooNode>(&const_cast<xRooNode &>(child), [](TObject *) {}));
3122 try {
3123 fComp = fParent->Add(*this, "+").fComp;
3124 } catch (...) {
3125 resize(size() - 1);
3126 std::rethrow_exception(std::current_exception());
3127 }
3128 resize(size() - 1); // remove the temporarily added node
3129 }
3130
3131 if (auto p = mainChild(); p) {
3132 // variations applied to the main child if has one
3133 return p.Vary(child);
3134 }
3135
3136 if (auto s = get<RooSimultaneous>(); s && s->indexCat().IsA() == RooCategory::Class()) {
3137 // name is used as cat label
3138 std::string label = child.GetName();
3139 if (auto pos = label.find('='); pos != std::string::npos)
3140 label = label.substr(pos + 1);
3141 if (!s->indexCat().hasLabel(label)) {
3142 static_cast<RooCategory &>(const_cast<RooAbsCategoryLValue &>(s->indexCat())).defineType(label.c_str());
3143 }
3144 std::shared_ptr<TObject> out;
3145 child.convertForAcquisition(*this);
3146 if (child.get<RooAbsPdf>()) {
3147 out = acquire(child.fComp); // may create a channel from a histogram
3148 } else if (!child.fComp) {
3149 out = acquireNew<RooProdPdf>(TString::Format("%s_%s", s->GetName(), label.c_str()),
3150 (strlen(child.GetTitle())) ? child.GetTitle() : label.c_str(), RooArgList());
3151 Info("Vary", "Created channel RooProdPdf::%s in model %s", out->GetName(), s->GetName());
3152 }
3153
3154 if (auto _pdf = std::dynamic_pointer_cast<RooAbsPdf>(out); _pdf) {
3155 s->addPdf(*_pdf, label.c_str());
3156 sterilize();
3157 // clear children for reload and update shared axis
3158 clear();
3159 fXAxis.reset();
3160 browse();
3161 return xRooNode(TString::Format("%s=%s", s->indexCat().GetName(), label.data()), _pdf, *this);
3162 }
3163
3164 } else if (auto p = get<RooStats::HistFactory::FlexibleInterpVar>(); p) {
3165
3166 // child needs to be a constvar ...
3167 child.convertForAcquisition(*this);
3168 auto _c = child.get<RooConstVar>();
3169 if (!_c && child.get()) {
3170 throw std::runtime_error("Only pure consts can be set as variations of a flexible interpvar");
3171 }
3172#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
3173 double value = (_c ? _c->getVal() : p->_nominal);
3174 double nomVal = p->_nominal;
3175#else
3176 double value = (_c ? _c->getVal() : p->nominal());
3177 double nomVal = p->nominal();
3178#endif
3179
3180 TString cName(child.GetName());
3181 if (cName == "nominal") {
3182 p->setNominal(value);
3183 return *(this->variations().at(cName.Data()));
3184 }
3185 if (cName.CountChar('=') != 1) {
3186 throw std::runtime_error("unsupported variation form");
3187 }
3188 std::string parName = cName(0, cName.Index('='));
3189 double parVal = TString(cName(cName.Index('=') + 1, cName.Length())).Atof();
3190 if (parVal != 1 && parVal != -1) {
3191 throw std::runtime_error("unsupported variation magnitude");
3192 }
3193 bool high = parVal > 0;
3194
3195 if (parName.empty()) {
3196 p->setNominal(value);
3197 } else {
3198 auto v = fParent->getObject<RooRealVar>(parName);
3199 if (!v)
3200 v = fParent->acquire<RooRealVar>(parName.c_str(), parName.c_str(), -5, 5);
3201 if (!v->hasError())
3202 v->setError(1);
3203
3204 if (!p->findServer(*v)) {
3205#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
3206 p->_paramList.add(*v);
3207 p->_low.push_back(0);
3208 p->_high.push_back(0);
3209 p->_interpCode.push_back(4);
3210#else
3211 const_cast<RooListProxy &>(p->variables()).add(*v);
3212 const_cast<std::vector<double> &>(p->low()).push_back(0);
3213 const_cast<std::vector<double> &>(p->high()).push_back(0);
3214 const_cast<std::vector<int> &>(p->interpolationCodes()).push_back(4);
3215#endif
3216 v->setAttribute(Form("SYMMETRIC%s_%s", high ? "+" : "-", GetName())); // flag for symmetrized
3217 }
3218
3219 if (high) {
3220 p->setHigh(*v, value);
3221 if (v->getAttribute(Form("SYMMETRIC+_%s", GetName()))) {
3222 p->setLow(*v, 2 * nomVal - value);
3223 }
3224 v->setAttribute(Form("SYMMETRIC-_%s", GetName()), false);
3225 } else {
3226 p->setLow(*v, value);
3227 if (v->getAttribute(Form("SYMMETRIC-_%s", GetName()))) {
3228 p->setHigh(*v, 2 * nomVal - value);
3229 }
3230 v->setAttribute(Form("SYMMETRIC+_%s", GetName()), false);
3231 }
3232
3233 /*if (!unconstrained && fParent->pars()[v->GetName()].constraints().empty()) {
3234 fParent->pars()[v->GetName()].constraints().add("normal");
3235 }*/
3236 }
3237 return *(this->variations().at(cName.Data()));
3238 } else if (auto p2 = get<PiecewiseInterpolation>(); p2) {
3239 TString cName(child.GetName());
3240 if (cName.CountChar('=') != 1) {
3241 throw std::runtime_error("unsupported variation form");
3242 }
3243 TString parName = cName(0, cName.Index('='));
3244 double parVal = TString(cName(cName.Index('=') + 1, cName.Length())).Atof();
3245 if (parVal != 1 && parVal != -1) {
3246 throw std::runtime_error("unsupported variation magnitude");
3247 }
3248#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
3249 RooHistFunc *f = dynamic_cast<RooHistFunc *>(p2->_nominal.absArg());
3250 if (!f) {
3251 throw std::runtime_error(
3252 TString::Format("Interpolating %s instead of RooHistFunc", p2->_nominal.absArg()->ClassName()));
3253 }
3254#else
3255 RooHistFunc *f = dynamic_cast<RooHistFunc *>(const_cast<RooAbsReal *>(p2->nominalHist()));
3256 if (!f) {
3257 throw std::runtime_error(
3258 TString::Format("Interpolating %s instead of RooHistFunc", p2->nominalHist()->ClassName()));
3259 }
3260#endif
3261 RooHistFunc *nomf = f;
3262 RooHistFunc *otherf = nullptr;
3263 size_t i = 0;
3264 for (auto par : p2->paramList()) {
3265 if (parName == par->GetName()) {
3266 f = dynamic_cast<RooHistFunc *>((parVal > 0 ? p2->highList() : p2->lowList()).at(i));
3267 otherf = dynamic_cast<RooHistFunc *>((parVal > 0 ? p2->lowList() : p2->highList()).at(i));
3268 break;
3269 }
3270 i++;
3271 }
3272 if (i == p2->paramList().size() && !child.get<RooAbsReal>()) {
3273
3274 // need to add the parameter
3275 auto v = acquire<RooRealVar>(parName, parName, -5, 5);
3276 if (!v->hasError())
3277 v->setError(1);
3278
3279 std::shared_ptr<RooHistFunc> up(
3280 static_cast<RooHistFunc *>(f->Clone(Form("%s_%s_up", f->GetName(), parName.Data()))));
3281 std::shared_ptr<RooHistFunc> down(
3282 static_cast<RooHistFunc *>(f->Clone(Form("%s_%s_down", f->GetName(), parName.Data()))));
3283 // RooHistFunc doesn't clone it's data hist ... do it ourself (will be cloned again if imported into a ws)
3284#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
3285 std::unique_ptr<RooDataHist> h1(
3286 static_cast<RooDataHist *>(f->dataHist().Clone(Form("hist_%s", up->GetName()))));
3287 std::unique_ptr<RooDataHist> h2(
3288 static_cast<RooDataHist *>(f->dataHist().Clone(Form("hist_%s", down->GetName()))));
3289 up->_dataHist = dynamic_cast<RooDataHist *>(f->dataHist().Clone(Form("hist_%s", up->GetName())));
3290 down->_dataHist = dynamic_cast<RooDataHist *>(f->dataHist().Clone(Form("hist_%s", down->GetName())));
3291#else
3292 up->cloneAndOwnDataHist(TString::Format("hist_%s", up->GetName()));
3293 down->cloneAndOwnDataHist(TString::Format("hist_%s", down->GetName()));
3294#endif
3295 auto ups = std::dynamic_pointer_cast<RooHistFunc>(acquire(up, false, true));
3296 auto downs = std::dynamic_pointer_cast<RooHistFunc>(acquire(down, false, true));
3297#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
3298 p2->_highSet.add(*ups.get());
3299 p2->_lowSet.add(*downs.get());
3300 p2->_interpCode.push_back(4);
3301 p2->_paramSet.add(*v);
3302#else
3303 const_cast<RooArgList &>(p2->highList()).add(*ups);
3304 const_cast<RooArgList &>(p2->lowList()).add(*downs);
3305 const_cast<std::vector<int> &>(p2->interpolationCodes()).push_back(4);
3306 const_cast<RooArgList &>(p2->paramList()).add(*v);
3307#endif
3308 p2->setValueDirty();
3309 f = ((parVal > 0) ? ups : downs).get();
3310 otherf = ((parVal > 0) ? downs : ups).get();
3311 // start off with everything being symmetric
3312 f->setStringAttribute("symmetrizes", otherf->GetName());
3313 f->setStringAttribute("symmetrize_nominal", nomf->GetName());
3314 otherf->setStringAttribute("symmetrized_by", f->GetName());
3315
3316 // constrain par if required
3317 /*if (!unconstrained && fParent->pars()[v->GetName()].constraints().empty()) {
3318 fParent->pars()[v->GetName()].constraints().add("normal");
3319 }*/
3320 }
3321
3322 // child.convertForAcquisition(*this);
3323 if (f) {
3324 if (child.get())
3325 xRooNode("tmp", *f, *this) = *child.get();
3326 f->setValueDirty();
3327 xRooNode out(*f, *this);
3328 out.sterilize();
3329 return out;
3330 }
3331
3332 } else if (auto p3 = get<RooConstVar>(); p3) {
3333
3334 // never vary the universal consts ... its too dangerous
3335 if (p3->getAttribute("RooRealConstant_Factory_Object")) {
3336 throw std::runtime_error("Cannot vary pure constants");
3337 }
3338
3339 // inject a FlexibleInterpVar ...
3340
3341 // get the list of clients BEFORE creating the new interpolation ... seems list of clients is inaccurate after
3342 std::set<RooAbsArg *> cl;
3343 for (auto &arg : p3->clients()) {
3344 cl.insert(arg);
3345 }
3346 // if multiple clients, see if only one client is in parentage route
3347 // if so, then assume thats the only client we should replace in
3348 if (cl.size() > 1) {
3349 if (cl.count(fParent->get<RooAbsArg>()) > 0) {
3350 cl.clear();
3351 cl.insert(fParent->get<RooAbsArg>());
3352 } else {
3353 Warning("Vary", "Varying %s that has multiple clients", p3->GetName());
3354 }
3355 }
3356 p3->setStringAttribute("origName", p3->GetName());
3357 TString n = p3->GetName();
3358 p3->SetName(Form("%s_nominal", p3->GetName())); // if problems should perhaps not rename here
3359
3360 auto new_p = acquireNew<RooStats::HistFactory::FlexibleInterpVar>(n, p3->GetTitle(), RooArgList(), p3->getVal(),
3361 std::vector<double>(), std::vector<double>());
3362
3363 // copy attributes over
3364 for (auto &a : p3->attributes())
3365 new_p->setAttribute(a.c_str());
3366 for (auto &a : p3->stringAttributes())
3367 new_p->setStringAttribute(a.first.c_str(), a.second.c_str());
3368 // if (!new_p->getStringAttribute("alias")) new_p->setStringAttribute("alias",p->GetName());
3369 auto old_p = p3;
3370 new_p->setAttribute(Form("ORIGNAME:%s", old_p->GetName())); // used in redirectServers to say what this replaces
3371 for (auto arg : cl) {
3372 arg->redirectServers(RooArgSet(*new_p), false, true);
3373 }
3374
3375 fComp = new_p;
3376 return Vary(child);
3377
3378 } else if (auto p4 = get<RooAbsReal>(); p4) {
3379 // inject an interpolation node
3380
3381 // get the list of clients BEFORE creating the new interpolation ... seems list of clients is inaccurate after
3382 std::set<RooAbsArg *> cl;
3383 for (auto &arg : p4->clients()) {
3384 cl.insert(arg);
3385 }
3386 // if multiple clients, see if only one client is in parentage route
3387 // if so, then assume thats the only client we should replace in
3388 if (cl.size() > 1) {
3389 if (cl.count(fParent->get<RooAbsArg>()) > 0) {
3390 cl.clear();
3391 cl.insert(fParent->get<RooAbsArg>());
3392 } else {
3393 Warning("Vary", "Varying %s that has multiple clients", p4->GetName());
3394 }
3395 }
3396 p4->setStringAttribute("origName", p4->GetName());
3397 TString n = p4->GetName();
3398 p4->SetName(Form("%s_nominal", p4->GetName())); // if problems should perhaps not rename here
3399
3400 auto new_p = acquireNew<PiecewiseInterpolation>(n, p4->GetTitle(), *p4, RooArgList(), RooArgList(), RooArgList());
3401
3402 // copy attributes over
3403 for (auto &a : p4->attributes())
3404 new_p->setAttribute(a.c_str());
3405 for (auto &a : p4->stringAttributes())
3406 new_p->setStringAttribute(a.first.c_str(), a.second.c_str());
3407 // if (!new_p->getStringAttribute("alias")) new_p->setStringAttribute("alias",p->GetName());
3408 auto old_p = p4;
3409 new_p->setAttribute(Form("ORIGNAME:%s", old_p->GetName())); // used in redirectServers to say what this replaces
3410 for (auto arg : cl) {
3411 arg->redirectServers(RooArgSet(*new_p), false, true);
3412 }
3413
3414 fComp = new_p;
3415 return Vary(child);
3416 }
3417
3418 Print();
3419 throw std::runtime_error(TString::Format("Cannot vary %s with %s", GetName(), child.GetName()));
3420}
3421
3423{
3425}
3426
3427bool xRooNode::SetContent(double value, const char *par, double val)
3428{
3429 return SetContents(RooConstVar(GetName(), GetTitle(), value), par, val);
3430}
3431
3434 {
3435 if (x && b)
3436 x->setBinning(*b);
3437 if (b)
3438 delete b;
3439 }
3440 RooRealVar *x = nullptr;
3441 RooAbsBinning *b = nullptr;
3442};
3443
3445{
3446
3447 if (!get()) {
3448 fComp = std::shared_ptr<TObject>(const_cast<TObject *>(&o), [](TObject *) {});
3449 if (fParent && !fParent->find(GetName())) {
3450 // either a temporary or a placeholder so need to try genuinely adding
3451 fComp = fParent->Add(*this, "+").fComp;
3452 if (auto a = get<RooAbsArg>(); a && strcmp(a->GetName(), GetName()) && !a->getStringAttribute("alias")) {
3453 a->setStringAttribute("alias", GetName());
3454 }
3455 if (!fComp)
3456 throw std::runtime_error("Cannot determine type");
3457 return *this;
3458 }
3459 }
3460
3461 if (auto h = dynamic_cast<const TH1 *>(&o); h) {
3462 /*auto f = get<RooHistFunc>();
3463 if (!f) {
3464 // if it's a RooProduct locate child with the same name
3465 if (get<RooProduct>()) {
3466 f = factors()[GetName()]->get<RooHistFunc>();
3467 }
3468
3469
3470
3471 }*/
3472 bool _isData = get<RooAbsData>();
3473 BinningRestorer _b;
3474 if (_isData) {
3475 // need to ensure x-axis matches this h
3476 auto ax = GetXaxis();
3477 if (!ax)
3478 throw std::runtime_error("no xaxis");
3479 auto _v = dynamic_cast<RooRealVar *>(ax->GetParent());
3480 if (_v) {
3481 _b.x = _v;
3482 _b.b = dynamic_cast<RooAbsBinning *>(_v->getBinningPtr(nullptr)->Clone());
3483 if (h->GetXaxis()->IsVariableBinSize()) {
3484 _v->setBinning(RooBinning(h->GetNbinsX(), h->GetXaxis()->GetXbins()->GetArray()));
3485 } else {
3486 _v->setBinning(RooUniformBinning(h->GetXaxis()->GetXmin(), h->GetXaxis()->GetXmax(), h->GetNbinsX()));
3487 }
3488 }
3489 }
3490
3491 if (true) {
3492 for (int bin = 1; bin <= h->GetNbinsX(); bin++) {
3493 SetBinContent(bin, h->GetBinContent(bin));
3494 /*double value = h->GetBinContent(bin);
3495 auto bin_pars = f->dataHist().get(bin - 1);
3496 if (f->getAttribute("density")) {
3497 value /= f->dataHist().binVolume(*bin_pars);
3498 }
3499 f->dataHist().set(*bin_pars, value);*/
3500 if (!_isData && h->GetSumw2N() && !SetBinError(bin, h->GetBinError(bin)))
3501 throw std::runtime_error("Failed setting stat error");
3502 }
3503 return *this;
3504 }
3505 } else if (auto _c = dynamic_cast<const RooConstVar *>(&o); _c) {
3506
3507 if (auto a = get<RooAbsArg>();
3508 (a && a->isFundamental()) || get<RooConstVar>() || get<RooStats::HistFactory::FlexibleInterpVar>()) {
3509 SetBinContent(1, _c->getVal());
3510 return *this;
3511 } else if (get<RooAbsData>()) { // try to do assignment to a dataset (usually setting a bin content)
3512 SetBinContent(0, _c->getVal());
3513 return *this;
3514 }
3515 }
3516
3517 throw std::runtime_error("Assignment failed");
3518
3519 /*
3520
3521 if (fParent && !fParent->mk()) {
3522 throw std::runtime_error("mk failure");
3523 }
3524
3525 if (fComp) return *this;
3526
3527 if (o.InheritsFrom("RooAbsArg")) {
3528 fComp = acquire(std::shared_ptr<TObject>(const_cast<TObject*>(&o),[](TObject* o){}));
3529 std::dynamic_pointer_cast<RooAbsArg>(fComp)->setStringAttribute("alias",GetName());
3530 }
3531
3532 if (fComp && fParent) {
3533 fParent->incorporate(fComp);
3534 }
3535
3536
3537 return *this;
3538 */
3539}
3540
3541void xRooNode::_fit_(const char *constParValues)
3542{
3543 try {
3544 auto _pars = pars();
3545 // std::unique_ptr<RooAbsCollection> snap(_pars.argList().snapshot());
3546 TStringToken pattern(constParValues, ",");
3547 std::map<RooAbsRealLValue *, double> valsToSet;
3548 while (pattern.NextToken()) {
3549 auto idx = pattern.Index('=');
3550 TString pat = (idx == -1) ? TString(pattern) : TString(pattern(0, idx));
3551 double val =
3552 (idx == -1) ? std::numeric_limits<double>::quiet_NaN() : TString(pattern(idx + 1, pattern.Length())).Atof();
3553 for (auto p : _pars.argList()) {
3554 if (TString(p->GetName()).Contains(TRegexp(pat, true))) {
3555 p->setAttribute("Constant", true);
3556 if (!std::isnan(val)) {
3557 valsToSet[dynamic_cast<RooAbsRealLValue *>(p)] = val;
3558 // dynamic_cast<RooAbsRealLValue *>(p)->setVal(val); // don't set yet, to allow for asimov dataset
3559 // creation based on current values
3560 }
3561 }
3562 }
3563 }
3564 // use the first selected dataset
3565 auto _dsets = datasets();
3566 TString dsetName = "";
3567 for (auto &d : _dsets) {
3568 if (d->get()->TestBit(1 << 20)) {
3569 dsetName = d->get()->GetName();
3570 break;
3571 }
3572 }
3573 auto _nll = nll(dsetName.Data());
3574 // can now set the values
3575 for (auto [p, v] : valsToSet) {
3576 p->setVal(v);
3577 }
3578 _nll.fitConfigOptions()->SetValue("LogSize", 65536);
3579 _nll.fitConfig()->MinimizerOptions().SetPrintLevel(0);
3580 auto fr = _nll.minimize();
3581 //_pars.argList() = *snap; // restore values - irrelevant as SetFitResult will restore values
3582 if (!fr.get())
3583 throw std::runtime_error("Fit Failed");
3584 SetFitResult(fr.get());
3585 TString statusCodes;
3586 for (unsigned int i = 0; i < fr->numStatusHistory(); i++) {
3587 statusCodes += TString::Format("\n%s = %d", fr->statusLabelHistory(i), fr->statusCodeHistory(i));
3588 }
3589 const TGWindow *w =
3590 (gROOT->GetListOfBrowsers()->At(0))
3591 ? dynamic_cast<TGWindow *>(static_cast<TBrowser *>(gROOT->GetListOfBrowsers()->At(0))->GetBrowserImp())
3592 : gClient->GetRoot();
3593 if (fr->status() != 0) {
3594 new TGMsgBox(gClient->GetRoot(), w, "Fit Finished with Bad Status Code",
3595 TString::Format("%s\nData = %s\nFit Status Code = %d\nCov Quality = %d\n-------------%s",
3596 fr->GetName(), dsetName.Data(), fr->status(), fr->covQual(), statusCodes.Data()),
3598 } else if (fr->covQual() != 3 && _nll.fitConfig()->ParabErrors()) {
3599 new TGMsgBox(gClient->GetRoot(), w, "Fit Finished with Bad Covariance Quality",
3600 TString::Format("%s\nData = %s\nFit Status Code = %d\nCov Quality = %d\n-------------%s",
3601 fr->GetName(), dsetName.Data(), fr->status(), fr->covQual(), statusCodes.Data()),
3603 } else {
3604 new TGMsgBox(gClient->GetRoot(), w, "Fit Finished Successfully",
3605 TString::Format("%s\nData = %s\nFit Status Code = %d\nCov Quality = %d\n-------------%s",
3606 fr->GetName(), dsetName.Data(), fr->status(), fr->covQual(), statusCodes.Data()));
3607 }
3608 } catch (const std::exception &e) {
3609 new TGMsgBox(
3610 gClient->GetRoot(),
3611 (gROOT->GetListOfBrowsers()->At(0))
3612 ? dynamic_cast<TGWindow *>(static_cast<TBrowser *>(gROOT->GetListOfBrowsers()->At(0))->GetBrowserImp())
3613 : gClient->GetRoot(),
3614 "Exception", e.what(), kMBIconExclamation, kMBOk); // deletes self on dismiss?
3615 }
3616}
3617
3618void xRooNode::_generate_(const char *datasetName, bool expected)
3619{
3620 try {
3621 datasets().Add(datasetName, expected ? "asimov" : "toy");
3622 } catch (const std::exception &e) {
3623 new TGMsgBox(
3624 gClient->GetRoot(),
3625 (gROOT->GetListOfBrowsers()->At(0))
3626 ? dynamic_cast<TGWindow *>(static_cast<TBrowser *>(gROOT->GetListOfBrowsers()->At(0))->GetBrowserImp())
3627 : gClient->GetRoot(),
3628 "Exception", e.what(),
3629 kMBIconExclamation); // deletes self on dismiss?
3630 }
3631}
3632
3633void xRooNode::_scan_(const char *what, double nToys, const char *xvar, int nBinsX, double lowX,
3634 double highX /*, const char*, int, double, double*/, const char *constParValues)
3635{
3636 try {
3637 TString sXvar(xvar);
3638 TString sWhat(what);
3639
3640 // use the first selected dataset
3641 auto _dsets = datasets();
3642 TString dsetName = "";
3643 for (auto &d : _dsets) {
3644 if (d->get()->TestBit(1 << 20)) {
3645 dsetName = d->get()->GetName();
3646 break;
3647 }
3648 }
3649 auto _pars = pars();
3650 std::unique_ptr<RooAbsCollection> snap(_pars.argList().snapshot());
3651 TStringToken pattern(constParValues, ",");
3652 while (pattern.NextToken()) {
3653 auto idx = pattern.Index('=');
3654 TString pat = (idx == -1) ? TString(pattern) : TString(pattern(0, idx));
3655 double val =
3656 (idx == -1) ? std::numeric_limits<double>::quiet_NaN() : TString(pattern(idx + 1, pattern.Length())).Atof();
3657 for (auto par : _pars.argList()) {
3658 if (TString(par->GetName()).Contains(TRegexp(pat, true))) {
3659 par->setAttribute("Constant", true);
3660 if (!std::isnan(val)) {
3661 dynamic_cast<RooAbsRealLValue *>(par)->setVal(val);
3662 }
3663 }
3664 }
3665 }
3666 auto hs = nll(dsetName.Data()).hypoSpace(sXvar);
3667 if (nToys) {
3668 sWhat += " toys";
3669 if (nToys > 0) {
3670 sWhat += TString::Format("=%g", nToys);
3671 }
3672 }
3673 hs.SetTitle(sWhat + " scan" + ((dsetName != "") ? TString::Format(" [data=%s]", dsetName.Data()) : ""));
3674 int scanStatus = hs.scan(sWhat + " visualize", nBinsX, lowX, highX);
3675 if (scanStatus != 0) {
3676 new TGMsgBox(
3677 gClient->GetRoot(),
3678 (gROOT->GetListOfBrowsers()->At(0))
3679 ? dynamic_cast<TGWindow *>(static_cast<TBrowser *>(gROOT->GetListOfBrowsers()->At(0))->GetBrowserImp())
3680 : gClient->GetRoot(),
3681 "Scan Finished with Bad Status Code",
3682 TString::Format("%s\nData = %s\nScan Status Code = %d", hs.GetName(), dsetName.Data(), scanStatus),
3684 }
3685 hs.SetName(TUUID().AsString());
3686 if (ws()) {
3687 if (auto res = hs.result())
3688 ws()->import(*res);
3689 }
3690
3691 _pars.argList() = *snap; // restore pars
3692
3693 } catch (const std::exception &e) {
3694 new TGMsgBox(
3695 gClient->GetRoot(),
3696 (gROOT->GetListOfBrowsers()->At(0))
3697 ? dynamic_cast<TGWindow *>(static_cast<TBrowser *>(gROOT->GetListOfBrowsers()->At(0))->GetBrowserImp())
3698 : gClient->GetRoot(),
3699 "Exception", e.what(), kMBIconExclamation);
3700 }
3701}
3702
3703void xRooNode::_SetBinContent_(int bin, double value, const char *par, double parVal)
3704{
3705 try {
3706 SetBinContent(bin, value, strlen(par) > 0 ? par : nullptr, parVal);
3707 } catch (const std::exception &e) {
3708 new TGMsgBox(gClient->GetRoot(), gClient->GetRoot(), "Exception", e.what(),
3709 kMBIconExclamation); // deletes self on dismiss?
3710 }
3711}
3712
3714{
3715 try {
3716#if ROOT_VERSION_CODE > ROOT_VERSION(6, 29, 00)
3717 // if this is a collection of values, populate a TF1 and display as a dialog
3718 if (!get() && TString(GetName()).BeginsWith("!")) {
3719 browse();
3720 RooArgList args;
3721 for (auto a : *this) {
3722 if (auto arg = a->get<RooRealVar>())
3723 args.add(*arg);
3724 }
3725 TF1 f(GetName(), 0.0, 1.0, std::min(int(args.size()), 10));
3726 int i = 0;
3727 int j = 0;
3728 for (auto c : args) {
3729 j++;
3730 if (j < value) {
3731 continue;
3732 }
3733 auto v = dynamic_cast<RooRealVar *>(c);
3734 f.SetParName(i, c->GetName());
3735 if (v) {
3736 f.SetParLimits(i, v->getMin(), v->getMax());
3737 if (v->isConstant())
3738 f.FixParameter(i, v->getVal());
3739 else {
3740 f.SetParameter(i, v->getVal());
3741 f.SetParError(i, v->getError());
3742 }
3743 }
3744 i++;
3745 if (i == 10) {
3746 break; // max 10 pars shown
3747 }
3748 }
3749 int ret = 0;
3751 gClient->GetDefaultRoot(),
3752 (gROOT->GetListOfBrowsers()->At(0))
3753 ? dynamic_cast<TGWindow *>(static_cast<TBrowser *>(gROOT->GetListOfBrowsers()->At(0))->GetBrowserImp())
3754 : gClient->GetDefaultRoot(),
3755 &f, nullptr, &ret);
3756 if (ret) {
3757 // user has changed parameter values etc, propagate back to parameters
3758 for (i = 0; i < f.GetNpar(); i++) {
3759 auto c = args.find(f.GetParName(i));
3760 auto v = dynamic_cast<RooRealVar *>(c);
3761 if (v) {
3762 v->setVal(f.GetParameter(i));
3763 double low, high;
3764 f.GetParLimits(i, low, high);
3765 if (low == high) {
3766 v->setConstant(low); // if low==high==0 then is not marked constant
3767 } else {
3768 v->setRange(low, high);
3769 }
3770 }
3771 }
3772 }
3773 return;
3774 }
3775#endif
3776
3777 if (!SetContent(value))
3778 throw std::runtime_error("Failed to SetContent");
3779 } catch (const std::exception &e) {
3780 new TGMsgBox(gClient->GetRoot(), gClient->GetRoot(), "Exception", e.what(),
3781 kMBIconExclamation); // deletes self on dismiss?
3782 }
3783}
3784
3785bool xRooNode::SetBinContent(int bin, double value, const char *par, double parVal)
3786{
3787
3788 // create if needed
3789 if (!get()) {
3790 if (fParent && !find(GetName())) {
3791 // if have a binning we create a histogram to match it
3792 if (auto ax = GetXaxis(); ax) {
3793 std::shared_ptr<TH1D> h;
3794 auto _b = dynamic_cast<Axis2 *>(ax)->binning();
3795 auto t = TH1::AddDirectoryStatus();
3796 TH1::AddDirectory(false);
3797 if (_b->isUniform()) {
3798 h.reset(new TH1D(GetName(), GetTitle(), _b->numBins(), _b->lowBound(), _b->highBound()));
3799 } else {
3800 h.reset(new TH1D(GetName(), GetTitle(), _b->numBins(), _b->array()));
3801 }
3802 h->SetOption("nostyle"); // don't transfer style when added
3803 h->SetDirectory(nullptr);
3805 h->GetXaxis()->SetName(TString::Format("%s;%s", ax->GetParent()->GetName(), ax->GetName()));
3806 fComp = h;
3807 }
3808 fComp = fParent->Add(*this, "sample").fComp;
3809 }
3810 }
3811
3812 // if it's a RooProduct locate child with the same name
3813 if (get<RooProduct>()) {
3814 return factors()[GetName()]->SetBinContent(bin, value, par, parVal);
3815 }
3816
3817 if (get<RooAbsData>()) {
3818 if (auto _data = get<RooDataSet>(); _data) {
3819 auto _ax = (bin) ? GetXaxis() : nullptr;
3820 if (!_ax && bin) {
3821 throw std::runtime_error("Cannot determine binning to fill data");
3822 }
3823 if (_ax && _ax->GetNbins() < bin) {
3824 throw std::out_of_range(TString::Format("%s range %s only has %d bins", _ax->GetParent()->GetName(),
3825 _ax->GetName(), _ax->GetNbins()));
3826 }
3827 RooArgSet obs;
3828
3829 TString cut = "";
3830
3831 for (auto _c : coords()) { // coords() moves vars to their respective coordinates too
3832 if (auto _cat = _c->get<RooAbsCategoryLValue>(); _cat) {
3833 if (cut != "")
3834 cut += " && ";
3835 cut += TString::Format("%s==%d", _cat->GetName(), _cat->getCurrentIndex());
3836 obs.add(*_cat); // note: if we ever changed coords to return clones, would need to keep coords alive
3837 } else if (auto _rv = _c->get<RooAbsRealLValue>(); _rv) {
3838 // todo: check coordRange is a single range rather than multirange
3839 if (cut != "")
3840 cut += " && ";
3841 cut +=
3842 TString::Format("%s>=%f&&%s<%f", _rv->GetName(), _rv->getMin(_rv->getStringAttribute("coordRange")),
3843 _rv->GetName(), _rv->getMax(_rv->getStringAttribute("coordRange")));
3844 obs.add(*_rv); // note: if we ever changed coords to return clones, would need to keep coords alive
3845 } else {
3846 throw std::runtime_error("SetBinContent of data: Unsupported coordinate type");
3847 }
3848 }
3849
3850 RooFormulaVar cutFormula("cut1", cut, obs); // doing this to avoid complaints about unused vars
3851 RooFormulaVar icutFormula("icut1", TString::Format("!(%s)", cut.Data()), obs);
3852
3853 TString cut2;
3854 if (_ax) {
3855 cut2 = TString::Format("%s >= %f && %s < %f", _ax->GetParent()->GetName(), _ax->GetBinLowEdge(bin),
3856 _ax->GetParent()->GetName(), _ax->GetBinUpEdge(bin));
3857 obs.add(*dynamic_cast<RooAbsArg *>(_ax->GetParent()));
3858 } else {
3859 cut2 = "1==1";
3860 }
3861 RooFormulaVar cutFormula2("cut2", cut + " && " + cut2, obs);
3862 RooFormulaVar icutFormula2("icut2", TString::Format("!(%s && %s)", cut.Data(), cut2.Data()), obs);
3863
3864 // // go up through parents looking for slice obs
3865 // auto _p = fParent;
3866 // while(_p) {
3867 // TString pName(_p->GetName());
3868 // if (auto pos = pName.Index('='); pos != -1) {
3869 // if(auto _obs = _p->getObject<RooAbsLValue>(pName(0,pos)); _obs) {
3870 // if(auto _cat = dynamic_cast<RooAbsCategoryLValue*>(_obs.get()); _cat) {
3871 // _cat->setLabel(pName(pos+1,pName.Length()));
3872 // cut += TString::Format("%s%s==%d", (cut=="")?"":" && ",_cat->GetName(),
3873 // _cat->getCurrentIndex());
3874 // } else if(auto _var = dynamic_cast<RooAbsRealLValue*>(_obs.get()); _var) {
3875 // _var->setVal(TString(pName(pos+1,pName.Length())).Atof());
3876 // // TODO: Cut for this!!
3877 // }
3878 // obs.add(*dynamic_cast<RooAbsArg*>(_obs.get()));
3879 // } else {
3880 // throw std::runtime_error("Unknown observable, could not find");
3881 // }
3882 // }
3883 // _p = _p->fParent;
3884 // }
3885
3886 // add observables to dataset if necessary
3887 RooArgSet l(obs);
3888 l.remove(*_data->get(), true, true);
3889 if (!l.empty()) {
3890 // addColumns method is buggy: https://github.com/root-project/root/issues/8787
3891 // incredibly though, addColumn works??
3892 for (auto &x : l) {
3893 _data->addColumn(*x);
3894 }
3895 // instead create a copy dataset and merge it into current
3896 // cant use merge because it drops weightVar
3897 /*RooDataSet tmp("tmp","tmp",l);
3898 for(int i=0;i<_data->numEntries();i++) tmp.add(l);
3899 _data->merge(&tmp);*/
3900 // delete _data->addColumns(l);
3901 }
3902 // before adding, ensure range is good to cover
3903 for (auto &o : obs) {
3904 if (auto v = dynamic_cast<RooRealVar *>(o); v) {
3905 if (auto dv = dynamic_cast<RooRealVar *>(_data->get()->find(v->GetName())); dv) {
3906 if (v->getMin() < dv->getMin())
3907 dv->setMin(v->getMin());
3908 if (v->getMax() > dv->getMax())
3909 dv->setMax(v->getMax());
3910 }
3911 } else if (auto c = dynamic_cast<RooCategory *>(o); c) {
3912 if (auto dc = dynamic_cast<RooCategory *>(_data->get()->find(c->GetName())); dc) {
3913 if (!dc->hasLabel(c->getCurrentLabel())) {
3914 dc->defineType(c->getCurrentLabel(), c->getCurrentIndex());
3915 }
3916 }
3917 }
3918 }
3919
3920 // using SetBinContent means dataset must take on a binned form at these coordinates
3921 // if number of entries doesnt match number of bins then will 'bin' the data
3922 if (bin) {
3923 if (auto _nentries = std::unique_ptr<RooAbsData>(_data->reduce(cutFormula))->numEntries();
3924 _nentries != _ax->GetNbins()) {
3925 auto _contents = GetBinContents(1, _ax->GetNbins());
3926
3927 if (_nentries > 0) {
3928 Info("SetBinContent", "Binning %s in channel: %s", GetName(), cut.Data());
3929 auto _reduced = std::unique_ptr<RooAbsData>(_data->reduce(icutFormula));
3930 _data->reset();
3931 for (int j = 0; j < _reduced->numEntries(); j++) {
3932 auto _obs = _reduced->get(j);
3933 _data->add(*_obs, _reduced->weight());
3934 }
3935 }
3936 for (int i = 1; i <= _ax->GetNbins(); i++) {
3937 // can skip over the bin we will be setting to save a reduce step below
3938 if (i == bin)
3939 continue;
3940 dynamic_cast<RooAbsLValue *>(_ax->GetParent())->setBin(i - 1, _ax->GetName());
3941 _data->add(obs, _contents.at(i - 1));
3942 }
3943 }
3944 }
3945 // remove existing entries
3946 if (std::unique_ptr<RooAbsData>(_data->reduce(cutFormula2))->numEntries() > 0) {
3947 auto _reduced = std::unique_ptr<RooAbsData>(_data->reduce(icutFormula2));
3948 _data->reset();
3949 for (int j = 0; j < _reduced->numEntries(); j++) {
3950 auto _obs = _reduced->get(j);
3951 _data->add(*_obs, _reduced->weight());
3952 }
3953 }
3954 if (_ax)
3955 dynamic_cast<RooAbsLValue *>(_ax->GetParent())->setBin(bin - 1, _ax->GetName());
3956 _data->add(obs, value);
3957 if (auto bb = getBrowsable(".sourceds"))
3958 return bb->SetBinContent(bin, value, par, parVal); // apply to source ds if we have one
3959 return true;
3960
3961 } else if (get<RooDataHist>()) {
3962 throw std::runtime_error("RooDataHist not supported yet");
3963 }
3964 }
3965
3966 if (auto _varies = variations(); !_varies.empty() || (par && strlen(par))) {
3967 if (!par || strlen(par) == 0) {
3968 return _varies["nominal"]->SetBinContent(bin, value, par, parVal);
3969 } else if (auto it = _varies.find(Form("%s=%g", par, parVal)); it) {
3970 return it->SetBinContent(bin, value);
3971 } else {
3972 // need to create the variation : note - if no variations existed up to now this will inject a new node
3973 // so we should redirect ourself to the new node
3974 // TODO: Do we need to redirect parents?
3975 TString s = Form("%s=%g", par, parVal);
3976 return Vary(s.Data()).SetBinContent(bin, value);
3977 }
3978 }
3979
3980 auto o = get();
3981 if (auto p = dynamic_cast<RooRealVar *>(o); p) {
3982 if (!par || strlen(par) == 0) {
3983 if (p->getMax() < value)
3984 p->setMax(value);
3985 if (p->getMin() > value)
3986 p->setMin(value);
3987 p->setVal(value);
3988 sterilize();
3989 return true;
3990 }
3991
3992 } else if (auto c = dynamic_cast<RooConstVar *>(o); c) {
3993
3994 // if parent is a FlexibleInterpVar, change the value in that .
3995 if (strcmp(c->GetName(), Form("%g", c->getVal())) == 0) {
3996 c->SetNameTitle(Form("%g", value), Form("%g", value));
3997 }
3998#if ROOT_VERSION_CODE < ROOT_VERSION(6, 24, 00)
3999 c->_value = value; // in future ROOT versions there is a changeVal method!
4000#else
4001 c->changeVal(value);
4002#endif
4003
4005 fParent->Vary(*this);
4006 }
4007
4008 sterilize();
4009 return true;
4010 } else if (auto f = dynamic_cast<RooHistFunc *>(o); f) {
4011 auto bin_pars = f->dataHist().get(bin - 1);
4012 if (f->getAttribute("density")) {
4013 value /= f->dataHist().binVolume(*bin_pars);
4014 }
4015 f->dataHist().set(*bin_pars, value);
4016 f->setValueDirty();
4017
4018 if (auto otherfName = f->getStringAttribute("symmetrized_by"); otherfName) {
4019 // broken symmetry, so update flags ...
4020 f->setStringAttribute("symmetrized_by", nullptr);
4021 if (auto x = getObject<RooAbsArg>(otherfName); x) {
4022 x->setStringAttribute("symmetrizes", nullptr);
4023 x->setStringAttribute("symmetrize_nominal", nullptr);
4024 }
4025 } else if (auto otherfName2 = f->getStringAttribute("symmetrizes"); otherfName2) {
4026 auto nomf = getObject<RooHistFunc>(f->getStringAttribute("symmetrize_nominal"));
4027 auto otherf = getObject<RooHistFunc>(otherfName2);
4028 if (nomf && otherf) {
4029 otherf->dataHist().set(*bin_pars, 2 * nomf->dataHist().weight(bin - 1) - value);
4030 otherf->setValueDirty();
4031 }
4032 }
4033 sterilize();
4034 return true;
4035 } else if (auto f2 = dynamic_cast<RooStats::HistFactory::FlexibleInterpVar *>(o); f2) {
4036 // changing nominal value
4037 f2->setNominal(value);
4038 }
4039 throw std::runtime_error(TString::Format("unable to set bin content of %s", GetPath().c_str()));
4040}
4041
4042bool xRooNode::SetBinData(int bin, double value, const xRooNode &data)
4043{
4044 if (data.get<RooAbsData>()) {
4045 // attach as a child before calling datasets(), so that is included in the list
4046 push_back(std::make_shared<xRooNode>(data));
4047 }
4048 auto node = datasets()[data.GetName()];
4049 if (data.get<RooAbsData>()) {
4050 // remove the child we attached
4051 resize(size() - 1);
4052 }
4053 return node->SetBinContent(bin, value);
4054}
4055
4056bool xRooNode::SetData(const TObject &obj, const xRooNode &data)
4057{
4058 if (data.get<RooAbsData>()) {
4059 // attach as a child before calling datasets(), so that is included in the list
4060 push_back(std::make_shared<xRooNode>(data));
4061 }
4062 auto node = datasets()[data.GetName()];
4063 if (data.get<RooAbsData>()) {
4064 // remove the child we attached
4065 resize(size() - 1);
4066 }
4067 return node->SetContents(obj);
4068}
4069
4070bool xRooNode::SetBinError(int bin, double value)
4071{
4072
4073 // if it's a RooProduct locate child with the same name
4074 if (get<RooProduct>()) {
4075 return factors()[GetName()]->SetBinError(bin, value);
4076 }
4077
4078 if (auto _varies = variations(); !_varies.empty()) {
4079 return _varies["nominal"]->SetBinError(bin, value);
4080 }
4081
4082 auto o = get();
4083
4084 if (auto f = dynamic_cast<RooHistFunc *>(o); f) {
4085
4086 // if (f->getAttribute("density")) { value /= f->dataHist().binVolume(*bin_pars); } - commented out because DON'T
4087 // convert .. sumw and sumw2 attributes will be stored not as densities
4088
4089 // NOTE: Can only do this because factors() makes parents of its children it's own parent (it isn't the parent)
4090 // If ever make factors etc part of the parentage then this would need tweaking to get to the true parent
4091 // find first parent that is a RooProduct, that is where the statFactor would live
4092 // stop as soon as we reach pdf object
4093 auto _prodParent = fParent;
4094 while (_prodParent && !_prodParent->get<RooProduct>() && !_prodParent->get<RooAbsPdf>()) {
4095 if (_prodParent->get<PiecewiseInterpolation>() && strcmp(GetName(), "nominal")) {
4096 _prodParent.reset();
4097 break; // only the 'nominal' variation can look for a statFactor outside the variation container
4098 }
4099 _prodParent = _prodParent->fParent;
4100 }
4101 auto _f_stat =
4102 (_prodParent && !_prodParent->get<RooAbsPdf>()) ? _prodParent->factors().find("statFactor") : nullptr;
4103 auto f_stat = (_f_stat) ? _f_stat->get<ParamHistFunc>() : nullptr;
4104 if (_f_stat && _f_stat->get() && !f_stat) {
4105 throw std::runtime_error("stat factor must be a paramhistfunc");
4106 }
4107
4108 // stat uncertainty lives in the "statFactor" factor, each sample has its own one,
4109 // but they can share parameters
4110 if (!f_stat) {
4111 if (value == 0)
4112 return true;
4113 TString parNames;
4114 for (auto &p : xRooNode("tmp", *f, std::shared_ptr<xRooNode>(nullptr)).vars()) {
4115 if (parNames != "")
4116 parNames += ",";
4117 parNames += p->get()->GetName();
4118 }
4119 auto h = std::unique_ptr<TH1>(f->dataHist().createHistogram(parNames
4120#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 27, 00)
4121 ,
4123#endif
4124 ));
4125 h->Reset();
4126 h->SetName("statFactor");
4127 h->SetTitle(TString::Format("StatFactor of %s", f->GetTitle()));
4128 h->SetOption("blankshape");
4129
4130 // multiply parent if is nominal
4131 auto toMultiply = this;
4132 if (strcmp(GetName(), "nominal") == 0 && fParent && fParent->get<PiecewiseInterpolation>())
4133 toMultiply = fParent.get();
4134
4135 f_stat = dynamic_cast<ParamHistFunc *>(toMultiply->Multiply(*h).get());
4136 if (!f_stat) {
4137 throw std::runtime_error("Failed creating stat shapeFactor");
4138 }
4139 }
4140
4141 auto phf = f_stat;
4142
4143 TString prefix = f->getStringAttribute("statPrefix");
4144 if (value && prefix == "") {
4145 // find the first parent that can hold components (RooAddPdf, RooRealSumPdf, RooAddition, RooWorkspace) ... use
4146 // that name for the stat factor
4147 auto _p = fParent;
4148 while (_p && !(_p->get()->InheritsFrom("RooRealSumPdf") || _p->get()->InheritsFrom("RooAddPdf") ||
4149 _p->get()->InheritsFrom("RooWorkspace") || _p->get()->InheritsFrom("RooAddition"))) {
4150 _p = _p->fParent;
4151 }
4152 prefix = TString::Format("stat_%s", (_p && _p->get<RooAbsReal>()) ? _p->get()->GetName() : f->GetName());
4153 }
4154 auto newVar = (value == 0) ? getObject<RooRealVar>("1")
4155 : acquire<RooRealVar>(Form("%s_bin%d", prefix.Data(), bin),
4156 Form("%s_bin%d", prefix.Data(), bin), 1);
4157#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
4158 RooArgList &pSet = phf->_paramSet;
4159#else
4160 RooArgList &pSet = const_cast<RooArgList &>(phf->paramList());
4161#endif
4162 auto var = dynamic_cast<RooRealVar *>(&pSet[bin - 1]);
4163
4164 if (newVar.get() != var) {
4165 // need to swap out var for newVar
4166 // replace ith element in list with new func, or inject into RooProduct
4167 RooArgList all;
4168 for (std::size_t i = 0; i < pSet.size(); i++) {
4169 if (int(i) != bin - 1) {
4170 all.add(*pSet.at(i));
4171 } else {
4172 all.add(*newVar);
4173 }
4174 }
4175 pSet.removeAll();
4176 pSet.add(all);
4177 }
4178
4179 xRooNode v((value == 0) ? *var : *newVar, *this);
4180 auto rrv = dynamic_cast<RooRealVar *>(v.get());
4181 if (strcmp(rrv->GetName(), "1") != 0) {
4182 TString origName = (f->getStringAttribute("origName")) ? f->getStringAttribute("origName") : GetName();
4183 rrv->setStringAttribute(Form("sumw2_%s", origName.Data()), TString::Format("%f", pow(value, 2)));
4184 auto bin_pars = f->dataHist().get(bin - 1);
4185 auto _binContent = f->dataHist().weight();
4186 if (f->getAttribute("density")) {
4187 _binContent *= f->dataHist().binVolume(*bin_pars);
4188 }
4189 rrv->setStringAttribute(Form("sumw_%s", origName.Data()), TString::Format("%f", _binContent));
4190 double sumw2 = 0;
4191 double sumw = 0;
4192 for (auto &[s, sv] : rrv->stringAttributes()) {
4193 if (s.find("sumw_") == 0) {
4194 sumw += TString(sv).Atof();
4195 } else if (s.find("sumw2_") == 0) {
4196 sumw2 += TString(sv).Atof();
4197 }
4198 }
4199 if (sumw2 && sumw2 != std::numeric_limits<double>::infinity()) {
4200 double tau = pow(sumw, 2) / sumw2;
4201 rrv->setError((tau < 1e-15) ? 1e15 : (/*rrv->getVal()*/ 1. / sqrt(tau))); // not sure why was rrv->getVal()?
4202 rrv->setConstant(false);
4203 // parameter must be constrained
4204 auto _constr = v.constraints();
4205 // std::cout << " setting constraint " << v.GetName() << " nomin=" << tau << std::endl;
4206 if (_constr.empty()) {
4207 rrv->setStringAttribute("boundConstraint", _constr.Add("poisson").get()->GetName());
4208 } else {
4209 auto _glob = _constr.at(0)->obs().at(0)->get<RooRealVar>();
4210 // TODO: Update any globs snapshots that are designed to match the nominal
4211 _glob->setStringAttribute("nominal", TString::Format("%f", tau));
4212 double _min = tau * (1. - 5. * sqrt(1. / tau));
4213 double _max = tau * (1. + 5. * sqrt(1. / tau));
4214 _glob->setRange(_min, _max);
4215 _glob->setVal(tau);
4216 _constr.at(0)->pp().at(0)->SetBinContent(0, tau);
4217 rrv->setStringAttribute("boundConstraint", _constr.at(0)->get()->GetName());
4218 }
4219 rrv->setRange(std::max((1. - 5. * sqrt(1. / tau)), 1e-15), 1. + 5. * sqrt(1. / tau));
4220 } else {
4221 // remove constraint
4222 if (auto _constr = v.constraints(); !_constr.empty()) {
4223 v.constraints().Remove(*_constr.at(0));
4224 }
4225 // set const if sumw2 is 0 (i.e. no error)
4226 rrv->setVal(1);
4227 rrv->setError(0);
4228 rrv->setConstant(sumw2 == 0);
4229 }
4230 }
4231
4232 return true;
4233 }
4234
4235 throw std::runtime_error(TString::Format("%s SetBinError failed", GetName()));
4236}
4237
4238std::shared_ptr<xRooNode> xRooNode::at(const std::string &name, bool browseResult) const
4239{
4240 auto res = find(name, browseResult);
4241 if (res == nullptr)
4242 throw std::out_of_range(name + " does not exist");
4243 return res;
4244}
4245
4246////////////////////////////////////////////////////////////////////////////////
4247/// The RooWorkspace this node belong to, if any
4248
4250{
4251 if (auto _w = get<RooWorkspace>(); _w)
4252 return _w;
4253 if (auto a = get<RooAbsArg>(); a && GETWS(a)) {
4254 return GETWS(a);
4255 }
4256 if (fParent)
4257 return fParent->ws();
4258 return nullptr;
4259}
4260
4262{
4263
4264 xRooNode out(".constraints", nullptr, *this);
4265
4266 std::function<RooAbsPdf *(const xRooNode &n, RooAbsArg &par, std::set<RooAbsPdf *> ignore)> getConstraint;
4267 getConstraint = [&](const xRooNode &n, RooAbsArg &par, std::set<RooAbsPdf *> ignore) {
4268 if (auto _pdf = n.get<RooAbsPdf>()) {
4269 if (ignore.count(_pdf))
4270 return (RooAbsPdf *)nullptr;
4271 ignore.insert(_pdf);
4272 }
4273 auto o = n.get<RooProdPdf>();
4274 if (!o) {
4275 if (n.get<RooSimultaneous>()) {
4276 // check all channels for a constraint if is simultaneous
4277 for (auto &c : n.bins()) {
4278 if (auto oo = getConstraint(*c, par, ignore); oo) {
4279 return oo;
4280 }
4281 }
4282 return (RooAbsPdf *)nullptr;
4283 } else if (n.get<RooAbsPdf>() && n.fParent && n.fParent->get<RooWorkspace>()) {
4284 // reached top-level pdf, which wasn't a simultaneous, so stop here
4285 return (RooAbsPdf *)nullptr;
4286 } else if (auto _ws = n.get<RooWorkspace>(); _ws) {
4287 // reached a workspace, check for any pdf depending on parameter that isnt the ignore
4288 for (auto p : _ws->allPdfs()) {
4289 if (ignore.count(static_cast<RooAbsPdf *>(p)))
4290 continue;
4291 if (p->dependsOn(par)) {
4292 out.emplace_back(std::make_shared<xRooNode>(par.GetName(), *p, *this));
4293 }
4294 }
4295 }
4296 if (!n.fParent)
4297 return (RooAbsPdf *)nullptr;
4298 return getConstraint(*n.fParent, par, ignore);
4299 }
4300 for (auto p : o->pdfList()) {
4301 if (ignore.count(static_cast<RooAbsPdf *>(p)))
4302 continue;
4303 if (p->dependsOn(par)) {
4304 out.emplace_back(std::make_shared<xRooNode>(par.GetName(), *p, *this));
4305 }
4306 }
4307 return (RooAbsPdf *)nullptr;
4308 };
4309
4310 for (auto &p : vars()) {
4311 auto v = dynamic_cast<RooAbsReal *>(p->get());
4312 if (!v)
4313 continue;
4314 if (v->getAttribute("Constant") && v != get<RooAbsReal>())
4315 continue; // skip constants unless we are getting the constraints of a parameter itself
4316 if (v->getAttribute("obs"))
4317 continue; // skip observables ... constraints constrain pars not obs
4318 getConstraint(*this, *v, {get<RooAbsPdf>()});
4319 /*if (auto c = ; c) {
4320 out.emplace_back(std::make_shared<Node2>(p->GetName(), *c, *this));
4321 }*/
4322 }
4323
4324 // finish by removing any constraint that contains another constraint for the same par
4325 // and consolidate common pars
4326 auto it = out.std::vector<std::shared_ptr<xRooNode>>::begin();
4327 while (it != out.std::vector<std::shared_ptr<xRooNode>>::end()) {
4328 bool removeIt = false;
4329 for (auto &c : out) {
4330 if (c.get() == it->get())
4331 continue;
4332 if ((*it)->get<RooAbsArg>()->dependsOn(*c->get<RooAbsArg>())) {
4333 removeIt = true;
4334 std::set<std::string> parNames;
4335 std::string _cName = c->GetName();
4336 do {
4337 parNames.insert(_cName.substr(0, _cName.find(';')));
4338 _cName = _cName.substr(_cName.find(';') + 1);
4339 } while (_cName.find(';') != std::string::npos);
4340 parNames.insert(_cName);
4341 _cName = it->get()->GetName();
4342 do {
4343 parNames.insert(_cName.substr(0, _cName.find(';')));
4344 _cName = _cName.substr(_cName.find(';') + 1);
4345 } while (_cName.find(';') != std::string::npos);
4346 parNames.insert(_cName);
4347 _cName = "";
4348 for (auto &x : parNames) {
4349 if (!_cName.empty())
4350 _cName += ";";
4351 _cName += x;
4352 }
4353 c->TNamed::SetName(_cName.c_str());
4354 break;
4355 }
4356 }
4357 if (removeIt) {
4358 it = out.erase(it);
4359 } else {
4360 ++it;
4361 }
4362 }
4363
4364 // if getting constraints of a fundamental then use the constraint names instead of the par name (because would be
4365 // all same otherwise)
4366 if (get<RooAbsArg>() && get<RooAbsArg>()->isFundamental()) {
4367 for (auto &o : out) {
4368 o->TNamed::SetName(o->get()->GetName());
4369 }
4370 }
4371
4372 return out;
4373}
4374
4375std::shared_ptr<TObject> xRooNode::convertForAcquisition(xRooNode &acquirer, const char *opt) const
4376{
4377
4378 TString sOpt(opt);
4379 sOpt.ToLower();
4380 TString sName(GetName());
4381 if (sOpt == "func")
4382 sName = TString("factory:") + sName;
4383
4384 // if arg is a histogram, will acquire it as a RooHistFunc unless no conversion
4385 // todo: could flag not to convert
4386 if (auto h = get<TH1>(); h) {
4387 TString sOpt2(h->GetOption());
4388 std::map<std::string, std::string> stringAttrs;
4389 while (sOpt2.Contains("=")) {
4390 auto pos = sOpt2.Index("=");
4391 auto start = sOpt2.Index(";") + 1;
4392 if (start > pos)
4393 start = 0;
4394 auto end = sOpt2.Index(";", pos);
4395 if (end == -1)
4396 end = sOpt2.Length();
4397 stringAttrs[sOpt2(start, pos - start)] = sOpt2(pos + 1, end - pos - 1);
4398 sOpt2 = TString(sOpt2(0, start)) + TString(sOpt2(end + 1, sOpt2.Length()));
4399 }
4400 TString newObjName = GetName();
4401 TString origName = GetName();
4402 if (origName.BeginsWith(';'))
4403 origName = origName(1, origName.Length());
4404 if (newObjName.BeginsWith(';')) {
4405 newObjName =
4406 newObjName(1, newObjName.Length()); // special case if starts with ';' then don't create a fancy name
4407 } else if (acquirer.get() && !acquirer.get<RooWorkspace>()) {
4408 newObjName = TString::Format(
4409 "%s_%s", (acquirer.mainChild().get()) ? acquirer.mainChild()->GetName() : acquirer->GetName(),
4410 newObjName.Data());
4411 }
4412 // can convert to a RooHistFunc, or RooParamHist if option contains 'shape'
4413 TString varName = h->GetXaxis()->GetName();
4414 std::string binningName = newObjName.Data();
4415 if (auto pos = varName.Index(';'); pos != -1) {
4416 binningName = varName(pos + 1, varName.Length());
4417 varName = varName(0, pos);
4418 }
4419
4420 if (varName == "xaxis" &&
4421 !acquirer.get<RooSimultaneous>()) { // default case, try to take axis var and binning from the acquirer
4422 if (auto ax = acquirer.GetXaxis(); ax) {
4423 varName = ax->GetParent()->GetName();
4424 // TODO: check the binning is consistent before using - at least will check nBins below
4425 binningName = ax->GetName();
4426 } else if (acquirer.obs().size() == 1)
4427 varName = acquirer.obs().at(0)->get()->GetName(); // TODO what if no obs but Xaxis var is defined?
4428 }
4429 auto x = acquirer.acquire<RooRealVar>(varName, h->GetXaxis()->GetTitle(), h->GetXaxis()->GetXmin(),
4430 h->GetXaxis()->GetXmax());
4431 if (x->getMin() > h->GetXaxis()->GetXmin())
4432 x->setMin(h->GetXaxis()->GetXmin());
4433 if (x->getMax() < h->GetXaxis()->GetXmax())
4434 x->setMax(h->GetXaxis()->GetXmax());
4435 if (!x->hasBinning(binningName.c_str())) {
4436 if (h->GetXaxis()->IsVariableBinSize()) {
4437 x->setBinning(RooBinning(h->GetNbinsX(), h->GetXaxis()->GetXbins()->GetArray()), binningName.c_str());
4438 } else {
4439 x->setBinning(
4440 RooUniformBinning(h->GetXaxis()->GetXmin(), h->GetXaxis()->GetXmax(), h->GetXaxis()->GetNbins()),
4441 binningName.c_str());
4442 }
4443 x->getBinning(binningName.c_str()).SetTitle(h->GetXaxis()->GetTitle());
4444 if (x->getBinningNames().size() == 2) {
4445 // this was the first binning, so copy it over to be the default binning too
4446 x->setBinning(x->getBinning(binningName.c_str()));
4447 }
4448 } else {
4449 // TODO check binning is compatible with histogram
4450 if (x->getBinning(binningName.c_str()).numBins() != h->GetNbinsX()) {
4451 throw std::runtime_error(
4452 TString::Format("binning mismatch for binning %s of %s", binningName.c_str(), x->GetName()));
4453 }
4454 }
4455
4456 std::shared_ptr<RooAbsArg> _f;
4457
4458 // if acquirer is a RooSimultaneous, will use histogram to define a channel
4459 if (acquirer.get<RooSimultaneous>()) {
4460 _f = acquirer.acquireNew<RooProdPdf>(newObjName, (strlen(h->GetTitle())) ? h->GetTitle() : h->GetName(),
4461 RooArgList());
4462 for (auto &[k, v] : stringAttrs) {
4463 _f->setStringAttribute(k.c_str(), v.c_str());
4464 }
4465 x->setAttribute("obs", true);
4466 } else if (sOpt2.Contains("shape")) {
4467 RooArgList list;
4468 for (int i = 0; i < x->getBinning(binningName.c_str()).numBins(); i++) {
4469 std::shared_ptr<RooAbsArg> arg;
4470 if (sOpt2.Contains("blankshape")) {
4471 arg = acquirer.acquire2<RooAbsArg, RooRealVar>("1", "1", 1);
4472 } else {
4473 if (!h) {
4474 arg = acquirer.acquireNew<RooRealVar>(TString::Format("%s_bin%d", newObjName.Data(), i + 1), "", 1);
4475 }
4476 if (h->GetMinimumStored() != -1111 || h->GetMaximumStored() != -1111) {
4477 arg = acquirer.acquireNew<RooRealVar>(TString::Format("%s_bin%d", newObjName.Data(), i + 1), "",
4478 h->GetBinContent(i + 1), h->GetMinimumStored(),
4479 h->GetMaximumStored());
4480 } else {
4481 arg = acquirer.acquireNew<RooRealVar>(TString::Format("%s_bin%d", newObjName.Data(), i + 1), "",
4482 h->GetBinContent(i + 1));
4483 }
4484 }
4485 list.add(*arg);
4486 }
4487 // paramhistfunc requires the binnings to be loaded as default at construction time
4488 // so load binning temporarily
4489 auto tmp = dynamic_cast<RooAbsBinning *>(x->getBinningPtr(nullptr)->Clone());
4490 x->setBinning(x->getBinning(binningName.c_str()));
4491 _f = acquirer.acquireNew<ParamHistFunc>(newObjName, h->GetTitle(), *x, list);
4492#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
4493 dynamic_cast<ParamHistFunc *>(_f.get())->_paramSet.setName("paramSet"); // so can see when print
4494#else
4495 const_cast<RooArgList &>(dynamic_cast<ParamHistFunc *>(_f.get())->paramList())
4496 .setName("paramSet"); // so can see when print
4497#endif
4498 x->setBinning(*tmp); // restore binning
4499 delete tmp;
4500 for (auto &[k, v] : stringAttrs) {
4501 _f->setStringAttribute(k.c_str(), v.c_str());
4502 }
4503 } else {
4504 auto dh = acquirer.acquireNew<RooDataHist>(Form("hist_%s", newObjName.Data()), h->GetTitle(), *x,
4505 binningName.c_str() /* binning name*/);
4506 if (!dh) {
4507 throw std::runtime_error("Couldn't make data hist");
4508 }
4509 auto f = acquirer.acquireNew<RooHistFunc>(newObjName, h->GetTitle(), *x, *dh,
4510 0 /*interpolation order between bins*/);
4511 f->forceNumInt();
4512 f->setAttribute("autodensity"); // where it gets inserted will determine if this is a density or not
4513 _f = f;
4514
4515 for (auto &[k, v] : stringAttrs) {
4516 _f->setStringAttribute(k.c_str(), v.c_str());
4517 }
4518
4519 // need to do these settings here because used in the assignment step
4520 _f->setStringAttribute("xvar", x->GetName());
4521 _f->setStringAttribute("binning", binningName.c_str());
4522 if (strcmp(_f->GetName(), origName.Data()) && !_f->getStringAttribute("alias"))
4523 _f->setStringAttribute("alias", origName);
4524
4525 // copy values over using the assignment operator - may convert to a RooProduct if there are stat uncerts
4526 xRooNode tmp(h->GetName(), _f, acquirer);
4527 tmp = *h;
4528 _f = std::dynamic_pointer_cast<RooAbsArg>(tmp.fComp); // in case got upgrade to a RooProduct
4529 }
4530
4531 _f->setStringAttribute("xvar", x->GetName());
4532 _f->setStringAttribute("binning", binningName.c_str());
4533 // style(h); // will transfer styling to object if necessary - not doing because this method used with plane hists
4534 // frequently
4535 if (strcmp(_f->GetName(), origName.Data()) && !_f->getStringAttribute("alias"))
4536 _f->setStringAttribute("alias", origName);
4537
4538 fComp = _f;
4539 return _f;
4540 } else if (!get() && sName.BeginsWith("factory:") && acquirer.ws()) {
4541 TString s(sName);
4542 s = TString(s(8, s.Length()));
4543 fComp.reset(acquirer.ws()->factory(s), [](TObject *) {});
4544 return fComp;
4545 }
4546
4547 return fComp;
4548}
4549
4550std::shared_ptr<TStyle> xRooNode::style(TObject *initObject, bool autoCreate) const
4551{
4552 TString t = GetTitle();
4553
4554 auto arg = get<RooAbsArg>();
4555 if (!initObject && !arg && !gROOT->GetStyle(t)) {
4556 return nullptr;
4557 }
4558
4559 std::unique_ptr<TObject> argInitObject;
4560
4561 if (initObject) {
4562 t = (strlen(initObject->GetTitle())) ? initObject->GetTitle() : initObject->GetName();
4563 } else if (arg) {
4564 if (arg->getStringAttribute("style")) {
4565 t = arg->getStringAttribute("style");
4566 } else if (autoCreate) {
4567 // args will default to histo's object styling, whatever that currently may be
4568 argInitObject = std::make_unique<TH1D>(GetName(), GetTitle(), 1, 0, 1);
4569 initObject = argInitObject.get();
4570 } else {
4571 return nullptr;
4572 }
4573 }
4574
4575 std::shared_ptr<TStyle> style; // use to keep alive for access from GetStyle below, in case getObject has decided to
4576 // return the owning ptr (for some reason)
4577 if (!gROOT->GetStyle(t)) {
4578 if ((style = getObject<TStyle>(t.Data()))) {
4579 // loaded style (from workspace?) so put in list and use that
4580 gROOT->GetListOfStyles()->Add(style.get());
4581 } else {
4582 if (!autoCreate)
4583 return nullptr;
4584 // create new style - gets put in style list automatically so don't have to delete
4585 // acquire them so saved to workspaces for auto reload ...
4586 style = const_cast<xRooNode &>(*this).acquireNew<TStyle>(t.Data(),
4587 TString::Format("Style for %s component", t.Data()));
4588 if (auto x = dynamic_cast<TAttLine *>(initObject))
4589 ((TAttLine &)*style) = *x;
4590 if (auto x = dynamic_cast<TAttFill *>(initObject))
4591 ((TAttFill &)*style) = *x;
4592 if (auto x = dynamic_cast<TAttMarker *>(initObject))
4593 ((TAttMarker &)*style) = *x;
4594 gROOT->GetListOfStyles()->Add(style.get());
4595 }
4596 } else {
4597 style = std::shared_ptr<TStyle>(gROOT->GetStyle(t), [](TStyle *) {});
4598 }
4599
4600 if (arg && !arg->getStringAttribute("style")) {
4601 arg->setStringAttribute("style", style->GetName());
4602 }
4603
4604 return style;
4605}
4606
4607std::shared_ptr<TObject> xRooNode::acquire(const std::shared_ptr<TObject> &arg, bool checkFactory, bool mustBeNew)
4608{
4609 if (!arg)
4610 return nullptr;
4611 if (!fAcquirer && !get<RooWorkspace>() && fParent)
4612 return fParent->acquire(arg, checkFactory, mustBeNew);
4613
4614 // if has a workspace and our object is the workspace or is in the workspace then add this object to workspace
4615 auto _ws = (fAcquirer) ? nullptr : ws();
4616 if (_ws && (get() == _ws || _ws->arg(GetName()) || (arg && strcmp(arg->GetName(), GetName()) == 0))) {
4619 if (auto a = dynamic_cast<RooAbsArg *>(arg.get()); a) {
4620 auto out_arg = _ws->arg(a->GetName());
4621 TString aName = arg->GetName();
4622 int ii = 1;
4623 while (out_arg && mustBeNew) {
4624 a->SetName(TString::Format("%s_%d", aName.Data(), ii++));
4625 out_arg = _ws->arg(a->GetName());
4626 }
4627 if (aName != a->GetName())
4628 Warning("acquire", "Renaming to %s", a->GetName());
4629 if (!out_arg) {
4630 bool done = false;
4631 if (checkFactory) {
4632 if (auto res = _ws->factory(arg->GetName()); res) {
4633 a = res;
4634 done = true;
4635 }
4636 }
4637 if (!done && _ws->import(*a, RooFit::RecycleConflictNodes())) {
4638 if (GETWS(a) != _ws) {
4639 Info("acquire", "A copy of %s has been added to workspace %s", a->GetName(), _ws->GetName());
4640 }
4642 return nullptr;
4643 }
4644 // sanitizeWS(); // clears the caches that might exist up to now, as well interfere with getParameters calls
4645 std::set<std::string> setNames;
4646 for (auto &aa : GETWSSETS(_ws)) {
4647 if (TString(aa.first.c_str()).BeginsWith("CACHE_")) {
4648 setNames.insert(aa.first);
4649 }
4650 }
4651 for (auto &aa : setNames)
4652 ws()->removeSet(aa.c_str());
4653 out_arg = _ws->arg(a->GetName());
4654 if (GETWS(out_arg) != _ws) { // seems that when objects imported their ws isn't set
4655 out_arg->setWorkspace(*_ws);
4656 }
4657 }
4659 return std::shared_ptr<TObject>(out_arg, [](TObject *) {});
4660 } else if (auto a2 = dynamic_cast<RooAbsData *>(arg.get()); a2) {
4661 if (_ws->import(*a2, RooFit::Embedded())) {
4663 return nullptr;
4664 }
4666 return std::shared_ptr<TObject>(_ws->embeddedData(arg->GetName()), [](TObject *) {});
4667 } else if (arg->InheritsFrom("RooFitResult") || arg->InheritsFrom("TTree") || arg->IsA() == TStyle::Class() ||
4668 arg->InheritsFrom("RooStats::HypoTestInverterResult") ||
4669 arg->InheritsFrom("RooStats::HypoTestResult")) {
4670 // ensure will have a unique name for import if must be new
4671 TNamed *aNamed = dynamic_cast<TNamed *>(arg.get());
4672 TString aName = arg->GetName();
4673 TObject *out_arg = _ws->genobj(arg->GetName());
4674 int ii = 1;
4675 while (aNamed && out_arg && mustBeNew) {
4676 aNamed->SetName(TString::Format("%s;%d", aName.Data(), ii++));
4677 out_arg = _ws->genobj(aNamed->GetName());
4678 }
4679 if (!out_arg) {
4680 if (aName != arg->GetName()) {
4681 Warning("acquire", "Renaming to %s", arg->GetName());
4682 }
4683 if (_ws->import(*arg, false /*replace existing*/)) {
4685 return nullptr;
4686 }
4687 out_arg = _ws->genobj(arg->GetName());
4688 }
4690 /* this doesnt work because caller has its own version of fParent, not the one in the browser
4691 for(auto o : *gROOT->GetListOfBrowsers()) {
4692 if(auto b = dynamic_cast<TBrowser*>(o); b){
4693 if(auto _b = dynamic_cast<TGFileBrowser*>( dynamic_cast<TRootBrowser*>(b->GetBrowserImp())->fActBrowser
4694 ); _b) { if (auto item = _b->fListTree->FindItemByObj(_b->fRootDir,this); item) { auto _tmp = _b->fListLevel;
4695 _b->fListLevel = item;
4696 bool _tmp2 = item->IsOpen();
4697 item->SetOpen(false);
4698 this->Browse(b);
4699 item->SetOpen(_tmp2);
4700 _b->fListLevel = _tmp;
4701 }
4702 }
4703 }
4704 }*/
4705 return std::shared_ptr<TObject>(out_arg, [](TObject *) {});
4706 }
4708 // Warning("acquire","Not implemented acquisition of object %s",arg->GetName());
4709 // return nullptr;
4710 }
4711 if (!mustBeNew && fProvider) {
4712 auto out = fProvider->getObject(arg->GetName(), arg->ClassName());
4713 if (out)
4714 return out;
4715 }
4716 auto _owned = find(".memory");
4717 if (!_owned) {
4718 _owned = emplace_back(std::make_shared<xRooNode>(".memory", nullptr, *this));
4719 }
4720 // look for exact name, dont use 'find' because doesnt work if trying to find "1" and it doesn't exist, will get back
4721 // idx 1 instead
4722 if (!mustBeNew) {
4723 for (auto &r : *_owned) {
4724 if (strcmp(r->GetName(), arg->GetName()) == 0 && strcmp(r->get()->ClassName(), arg->ClassName()) == 0) {
4725 return r->fComp;
4726 }
4727 }
4728 }
4729 if (!fProvider)
4730 std::cout << GetName() << " taking over " << arg->ClassName() << "::" << arg->GetName() << std::endl;
4731 /*emplace_back(std::make_shared<Node2>(".memory",nullptr,*this))*/
4732 return _owned->emplace_back(std::make_shared<xRooNode>(arg->GetName(), arg, *this))->fComp;
4733 // return arg;
4734}
4735
4736bool xRooNode::SetXaxis(const char *name, const char *title, int nbins, double low, double high)
4737{
4738 RooUniformBinning b(low, high, nbins, name);
4739 b.SetTitle(title);
4740 return SetXaxis(b);
4741}
4742
4743bool xRooNode::SetXaxis(const char *name, const char *title, int nbins, const double *bins)
4744{
4745 RooBinning b(nbins, bins, name);
4746 b.SetTitle(title);
4747 return SetXaxis(b);
4748}
4749
4751{
4752
4753 auto name = binning.GetName();
4754 double high = binning.highBound();
4755 double low = binning.lowBound();
4756 // int nbins = binning.numBins();
4757 auto title = binning.GetTitle();
4758
4759 // if have any dependents and name isn't one of them then stop
4760 auto _deps = vars();
4761 /*if(!_deps.empty() && !_deps.find(name)) {
4762 throw std::runtime_error(TString::Format("%s Does not depend on %s",GetName(),name));
4763 }*/
4764
4765 // object will need to exist
4766 if (!get()) {
4767 if (fParent && !find(GetName())) {
4768 fComp = fParent->Add(*this, "+").fComp;
4769 }
4770 }
4771
4772 auto a = get<RooAbsArg>();
4773 if (!a)
4774 throw std::runtime_error("Cannot SetXaxis of non-arg");
4775
4776 auto _x = acquire<RooRealVar>(name, title, low, high);
4777 _x->setBinning(binning, a->GetName());
4778 _x->getBinning(a->GetName()).SetTitle(title);
4779 if (_x->getBinningNames().size() == 2) {
4780 // this was the first binning, so copy it over to be the default binning too
4781 _x->setBinning(_x->getBinning(a->GetName()));
4782 } else {
4783 // ensure the default binning is wide enough to cover this range
4784 // the alternative to this would be to ensure setNormRange of all pdfs
4785 // are set to correct range (then default can be narrower than some of the named binnings)
4786 if (_x->getMax() < high)
4787 _x->setMax(high);
4788 if (_x->getMin() > low)
4789 _x->setMin(low);
4790 }
4791
4792 if (!_deps.find(name) && get<RooAbsPdf>()) {
4793 // creating a variable for a pdf we will assume it should be an observable
4794 _x->setAttribute("obs");
4795 }
4796
4797 a->setStringAttribute("xvar", _x->GetName());
4798 a->setStringAttribute("binning", a->GetName());
4799 fXAxis.reset(); // remove any existing xaxis
4800
4801 return true;
4802}
4803
4805{
4806 if (!ax)
4807 return false;
4808 if (ax->IsVariableBinSize()) {
4809 return SetXaxis(ax->GetName(), ax->GetTitle(), ax->GetNbins(), ax->GetXbins()->GetArray());
4810 } else {
4811 return SetXaxis(ax->GetName(), ax->GetTitle(), ax->GetNbins(), ax->GetXmin(), ax->GetXmax());
4812 }
4813}
4814
4815bool xRooNode::contains(const std::string &name) const
4816{
4817 return find(name, false) != nullptr;
4818}
4819
4820std::shared_ptr<xRooNode> xRooNode::find(const std::string &name, bool browseResult) const
4821{
4822 std::string partname = (name.find('/') != std::string::npos) ? name.substr(0, name.find('/')) : name;
4823 auto _s = (!get() && fParent) ? fParent->get<RooSimultaneous>()
4824 : get<RooSimultaneous>(); // makes work if doing simPdf.bins()["blah"]
4825 std::string extra = (_s) ? _s->indexCat().GetName() : "";
4826 for (auto &child : *this) {
4827 if (auto _obj = child->get(); name == child->GetName() || partname == child->GetName() ||
4828 (_obj && name == _obj->GetName()) || (_obj && partname == _obj->GetName()) ||
4829 (!extra.empty() && ((extra + "=" + name) == child->GetName() ||
4830 (extra + "=" + partname) == child->GetName()))) {
4831 if (browseResult)
4832 child->browse(); // needed so can go at()->at()->at()...
4833 if (partname != name && name != child->GetName()) {
4834 return child->at(name.substr(partname.length() + 1));
4835 }
4836 return child;
4837 }
4838 if (auto x = mainChild(); x && strcmp(child->GetName(), x.GetName()) == 0) {
4839 // can browse directly into main children as if their children were our children
4840 for (auto &child2 : x.browse()) {
4841 if (auto _obj = child2->get(); name == child2->GetName() || partname == child2->GetName() ||
4842 (_obj && name == _obj->GetName()) || (_obj && partname == _obj->GetName())) {
4843 if (browseResult)
4844 child2->browse(); // needed for onward read (or is it? there's a browse above too??)
4845 if (partname != name && name != child2->GetName()) {
4846 return child2->at(name.substr(partname.length() + 1));
4847 }
4848 return child2;
4849 }
4850 }
4851 }
4852 }
4853 // before giving up see if partName is numeric and indexes within the range
4854 if (TString s(partname); s.IsDec() && size_t(s.Atoi()) < size()) {
4855 auto child2 = at(s.Atoi());
4856 if (partname != name) {
4857 return child2->at(name.substr(partname.length() + 1));
4858 }
4859 return child2;
4860 }
4861 return nullptr;
4862}
4863
4864std::shared_ptr<xRooNode> xRooNode::operator[](const std::string &name)
4865{
4866 std::string partname = (name.find('/') != std::string::npos) ? name.substr(0, name.find('/')) : name;
4867 browse();
4868 auto _s = (!get() && fParent) ? fParent->get<RooSimultaneous>()
4869 : get<RooSimultaneous>(); // makes work if doing simPdf.bins()["blah"]
4870 std::string extra = (_s) ? _s->indexCat().GetName() : "";
4871 std::shared_ptr<xRooNode> folderNode;
4872 for (auto &child : *this) {
4873 if (name == child->GetName() || partname == child->GetName() ||
4874 (!extra.empty() &&
4875 ((extra + "=" + name) == child->GetName() || (extra + "=" + partname) == child->GetName()))) {
4876 child->browse(); // needed for onward read (or is it? there's a browse above too??)
4877 if (partname != name && name != child->GetName()) {
4878 return child->operator[](name.substr(partname.length() + 1));
4879 }
4880 return child;
4881 }
4882 if (auto x = mainChild(); strcmp(child->GetName(), x.GetName()) == 0) {
4883 // can browse directly into main children as if their children were our children
4884 for (auto &child2 : x.browse()) {
4885 if (name == child2->GetName() || partname == child2->GetName()) {
4886 child2->browse(); // needed for onward read (or is it? there's a browse above too??)
4887 if (partname != name && name != child2->GetName()) {
4888 return child2->operator[](name.substr(partname.length() + 1));
4889 }
4890 return child2;
4891 }
4892 }
4893 }
4894 if (child->fFolder == (std::string("!") + partname)) {
4895 if (!folderNode)
4896 folderNode = std::make_shared<xRooNode>(child->fFolder.c_str(), nullptr, *this);
4897 folderNode->push_back(child);
4898 }
4899 }
4900 if (folderNode) {
4901 if (partname != name) {
4902 return folderNode->operator[](name.substr(partname.length() + 1));
4903 }
4904 return folderNode;
4905 }
4906 // before giving up see if partName is numeric and indexes within the range
4907 if (TString s(partname); s.IsDec() && size_t(s.Atoi()) < size()) {
4908 auto child2 = at(s.Atoi());
4909 if (partname != name) {
4910 return child2->operator[](name.substr(partname.length() + 1));
4911 }
4912 return child2;
4913 }
4914 auto out = std::make_shared<xRooNode>(partname.c_str(), nullptr, *this); // not adding as child yeeet
4915 if (partname != name) {
4916 return out->operator[](name.substr(partname.length() + 1));
4917 }
4918 return out;
4919}
4920
4922{
4923 if (!b) {
4924 for (auto o : *gROOT->GetListOfBrowsers()) {
4925 b = dynamic_cast<TBrowser *>(o);
4926 if (!b || !b->GetBrowserImp())
4927 continue;
4928 if (auto out = GetTreeItem(b); out)
4929 return out;
4930 }
4931 return nullptr;
4932 }
4933 if (!b->GetBrowserImp())
4934 return nullptr;
4935 if (auto _b = dynamic_cast<TGFileBrowser *>(GETACTBROWSER(dynamic_cast<TRootBrowser *>(b->GetBrowserImp()))); _b) {
4936 auto _root = GETROOTDIR(_b);
4937 ;
4938 if (!_root)
4939 _root = GETLISTTREE(_b)->GetFirstItem();
4941 return GETLISTTREE(_b)->FindItemByObj(_root, const_cast<xRooNode *>(this));
4942 }
4943 return nullptr;
4944}
4945
4947{
4948 if (!b) {
4949 for (auto o : *gROOT->GetListOfBrowsers()) {
4950 b = dynamic_cast<TBrowser *>(o);
4951 if (!b || !b->GetBrowserImp())
4952 continue;
4953 if (auto out = GetListTree(b); out)
4954 return out;
4955 }
4956 return nullptr;
4957 }
4958 if (b->GetBrowserImp()) {
4959 if (auto _b = dynamic_cast<TGFileBrowser *>(GETACTBROWSER(dynamic_cast<TRootBrowser *>(b->GetBrowserImp())));
4960 _b) {
4961 auto _root = GETROOTDIR(_b);
4962 if (!_root)
4963 _root = GETLISTTREE(_b)->GetFirstItem();
4964 if (auto item = GETLISTTREE(_b)->FindItemByObj(_root, const_cast<xRooNode *>(this)); item) {
4965 return GETLISTTREE(_b);
4966 }
4967 }
4968 }
4969 return nullptr;
4970}
4971
4972void xRooNode::SetName(const char *name)
4973{
4975 if (auto a = get<RooAbsArg>(); a)
4976 a->setStringAttribute("alias", name);
4977 for (auto o : *gROOT->GetListOfBrowsers()) {
4978 if (auto b = dynamic_cast<TBrowser *>(o); b) {
4979 if (auto item = GetTreeItem(b); item) {
4980 item->SetText(name);
4981 }
4982 }
4983 }
4984}
4985
4986void xRooNode::SetTitle(const char *title)
4987{
4988 if (auto o = (get<TNamed>()); o) {
4989 if (auto c = mainChild(); c.get()) {
4990 c.SetTitle(title);
4991 }
4992 o->SetTitle(title);
4993 }
4994 TNamed::SetTitle(title);
4995}
4996
4998{
4999 if (get<RooArgList>() || (!get() && !(strlen(GetName()) > 0 && (GetName()[0] == '!')) && !fBrowseOperation))
5000 return *this; // nothing to browse - 'collection' nodes should already be populated except for folders
5001 // alternative could have been to mandate that the 'components' of a collection node are the children it has.
5002
5003 auto findByObj = [&](const std::shared_ptr<xRooNode> &n) {
5004 std::vector<std::shared_ptr<xRooNode>> &nn = *this;
5005 for (auto &c : nn) {
5006 if (c->get() == n->get() && strcmp(n->GetName(), c->GetName()) == 0)
5007 return c;
5008 }
5009 return std::shared_ptr<xRooNode>(nullptr);
5010 };
5011
5012 auto appendChildren = [&](const xRooNode &n) {
5013 size_t out = 0;
5014 const std::vector<std::shared_ptr<xRooNode>> &nn(n);
5015 for (auto &c : nn) {
5016 if (auto existing = findByObj(c); existing) {
5017 existing->fTimes++;
5018 existing->fFolder = c->fFolder; // transfer folder assignment
5019 } else {
5020 emplace_back(c);
5021 }
5022 if (!TString(c->GetName()).BeginsWith(".coef"))
5023 out++; // don't count .coef as a child, as technically part of parent
5024 }
5025 return out;
5026 };
5027
5028 const std::vector<std::shared_ptr<xRooNode>> &nn2(*this);
5029 for (auto &c : nn2) {
5030 if (strlen(c->GetName()) > 0 && (c->GetName()[0] == '.')) {
5031 c->fTimes = 1;
5032 continue;
5033 } // never auto-cleanup property children
5034 if (strcmp(c->GetName(), "!.pars") == 0) {
5035 c->fTimes = 1;
5036 continue;
5037 } // special collection, also not cleaned up
5038 if (c->get<RooWorkspace>() || c->get<TFile>()) {
5039 c->fTimes = 1;
5040 continue;
5041 } // workspaces and files not cleaned up: TODO have a nocleanup flag instead
5042 c->fTimes = 0;
5043 }
5044
5045 size_t addedChildren = 0;
5046 if (fBrowseOperation) {
5047 addedChildren += appendChildren(fBrowseOperation(this));
5048 } else {
5049 if (get<RooWorkspace>()) {
5050 addedChildren += appendChildren(datasets());
5051 }
5052
5053 // if (get<RooAbsPdf>() && ((fParent && fParent->get<RooWorkspace>()) || !fParent)) {
5054 // // top-level pdfs will also list the ".vars" property for -- should make this updateable
5055 // //if (auto x = find("!.vars"); !x) { // this is slower because it triggers a browse of !.vars
5056 // if(!contains("!.vars")) {
5057 // emplace_back(std::make_shared<Node2>("!.vars",nullptr,*this));
5058 // } /*else {
5059 // x->fTimes++;
5060 // }*/
5061 // }
5062
5063 // go through components factors and variations, adding all as children if required
5064 addedChildren += appendChildren(components());
5065 if (!get<RooWorkspace>())
5066 addedChildren += appendChildren(factors());
5067 // include coefs if any
5068 auto _coefs = coefs();
5069 if (_coefs.get() && strcmp(_coefs->GetName(), "1") != 0 && strcmp(_coefs->GetName(), "ONE") != 0) {
5070 if (_coefs.size() == 1 && _coefs.get<RooAddition>()) {
5071 if (strcmp(_coefs.at(0)->GetName(), "1") != 0 &&
5072 strcmp(_coefs.at(0)->GetName(), "ONE") != 0) { // don't add the "1"
5073 auto coef = std::make_shared<xRooNode>(".coef", *_coefs.at(0)->get(), *this);
5074 if (auto existing = findByObj(coef); existing) {
5075 existing->fTimes++;
5076 existing->fFolder = _coefs.at(0)->fFolder; // transfer folder assignment
5077 } else {
5078 emplace_back(coef);
5079 }
5080 }
5081 } else {
5082 if (auto existing = find(_coefs.GetName()); existing) {
5083 existing->fTimes++;
5084 existing->fFolder = _coefs.fFolder; // transfer folder assignment
5085 } else {
5086 emplace_back(std::make_shared<xRooNode>(_coefs));
5087 }
5088 }
5089 }
5090 addedChildren += appendChildren(variations());
5091 if (get<ParamHistFunc>() || get<RooSimultaneous>())
5092 addedChildren += appendChildren(bins());
5093 if (get<RooAbsData>())
5094 addedChildren += appendChildren(obs());
5095 }
5096 // if has no children and is a RooAbsArg, add all the proxies
5097 if (auto arg = get<RooAbsArg>(); arg && addedChildren == 0) {
5098 for (int i = 0; i < arg->numProxies(); i++) {
5099 auto _proxy = arg->getProxy(i);
5100 if (auto a = dynamic_cast<RooArgProxy *>(_proxy)) {
5101 auto c = std::make_shared<xRooNode>(TString::Format(".%s", _proxy->name()), *(a->absArg()), *this);
5102 if (auto existing = findByObj(c); existing) {
5103 existing->fTimes++;
5104 existing->fFolder = c->fFolder; // transfer folder assignment
5105 } else {
5106 emplace_back(c);
5107 }
5108 } else if (auto s = dynamic_cast<RooAbsCollection *>(_proxy)) {
5109 for (auto a2 : *s) {
5110 auto c = std::make_shared<xRooNode>(*a2, *this);
5111 if (arg->numProxies() != 1) {
5112 c->fFolder = std::string("!.") +
5113 _proxy->name(); // don't put in a folder if there's just 1 proxy (the collection)
5114 }
5115 if (auto existing = findByObj(c); existing) {
5116 existing->fTimes++;
5117 existing->fFolder = c->fFolder; // transfer folder assignment
5118 } else {
5119 emplace_back(c);
5120 }
5121 }
5122 }
5123 }
5124 /*for(auto& s : arg->servers()) {
5125 auto c = std::make_shared<xRooNode>(*s,*this);
5126 if (auto existing = findByObj(c); existing) {
5127 existing->fTimes++;
5128 existing->fFolder = c->fFolder; // transfer folder assignment
5129 } else {
5130 emplace_back(c);
5131 }
5132 }*/
5133 } else if (auto ir = get<RooStats::HypoTestInverterResult>()) {
5134 // check if we already have a hypoSpace in our memory
5135 bool hasHS = false;
5136 for (auto &c : fBrowsables) {
5137 if (strcmp(c->GetName(), ".memory") == 0 && c->get<xRooHypoSpace>()) {
5138 hasHS = true;
5139 break;
5140 }
5141 }
5142 if (!hasHS) {
5143 // add the HS
5144 auto hs =
5145 fBrowsables.emplace_back(std::make_shared<xRooNode>(".memory", std::make_shared<xRooHypoSpace>(ir), *this));
5146 // add the hypoPoints first so they appear first
5147 auto _axes = hs->get<xRooHypoSpace>()->axes();
5148
5149 int i = 0;
5150 for (auto &hp : *hs->get<xRooHypoSpace>()) {
5151 TString coordString;
5152 for (auto a : _axes) {
5153 if (a != _axes.first())
5154 coordString += ",";
5155 coordString +=
5156 TString::Format("%s=%g", a->GetName(), hp.coords->getRealValue(a->GetName(), ir->GetXValue(i)));
5157 }
5158 auto hpn = emplace_back(std::make_shared<xRooNode>(coordString, hp.hypoTestResult, hs));
5159 hpn->fTimes++;
5160 hpn->fBrowsables.emplace_back(std::make_shared<xRooNode>(
5161 ".memory", std::shared_ptr<xRooNLLVar::xRooHypoPoint>(&hp, [](xRooNLLVar::xRooHypoPoint *) {}), hpn));
5162 i++;
5163 }
5164 } else {
5165 // ensure all hypoTestResults are flagged as keep-alive
5166 std::vector<std::shared_ptr<xRooNode>> &nn = *this;
5167 for (auto &c : nn) {
5168 if (c->get<RooStats::HypoTestResult>())
5169 c->fTimes++;
5170 }
5171 }
5172 // xRooNode tests;
5173 // for(int i=0;i<ir->ArraySize();i++) {
5174 // tests.push_back(std::make_shared<xRooNode>(TString::Format("%g",ir->GetXValue(i)),*ir->GetResult(i),*this));
5175 // }
5176 // appendChildren(tests);
5177 } else if (get<RooStats::HypoTestResult>()) {
5178
5179 // create the xRooHypoPoint if necessary
5180 xRooNLLVar::xRooHypoPoint *hp = nullptr;
5181 for (auto &c : fBrowsables) {
5182 if (strcmp(c->GetName(), ".memory") == 0 && c->get<xRooNLLVar::xRooHypoPoint>()) {
5183 hp = c->get<xRooNLLVar::xRooHypoPoint>();
5184 c->fTimes++; // keep it alive
5185 break;
5186 }
5187 }
5188 if (!hp) {
5189 auto shp =
5190 std::make_shared<xRooNLLVar::xRooHypoPoint>(std::dynamic_pointer_cast<RooStats::HypoTestResult>(fComp));
5191 fBrowsables.emplace_back(std::make_shared<xRooNode>(".memory", shp, *this));
5192 hp = shp.get();
5193 }
5194
5195 xRooNode fits;
5196
5197 if (auto fit = hp->ufit()) {
5198 fits.emplace_back(std::make_shared<xRooNode>(fit, *this))->TNamed::SetName("ufit");
5199 }
5200 if (auto fit = hp->cfit_null()) {
5201 fits.emplace_back(std::make_shared<xRooNode>(fit, *this))->TNamed::SetName("cfit_null");
5202 }
5203 if (auto fit = hp->cfit_alt()) {
5204 fits.emplace_back(std::make_shared<xRooNode>(fit, *this))->TNamed::SetName("cfit_alt");
5205 }
5206 if (auto fit = hp->gfit()) {
5207 fits.emplace_back(std::make_shared<xRooNode>(fit, *this))->TNamed::SetName("gfit");
5208 }
5209 if (auto asi = hp->asimov()) {
5210 auto asiP = fits.emplace_back(std::make_shared<xRooNode>(
5211 asi->hypoTestResult ? asi->hypoTestResult : std::make_shared<RooStats::HypoTestResult>(asi->result()),
5212 *this));
5213 asiP->TNamed::SetName("asimov");
5214 asiP->fBrowsables.emplace_back(std::make_shared<xRooNode>(".memory", asi, asiP));
5215 }
5216 appendChildren(fits);
5217 }
5218
5219 // clear anything that has fTimes = 0 still
5220 auto it = std::vector<std::shared_ptr<xRooNode>>::begin();
5221 while (it != std::vector<std::shared_ptr<xRooNode>>::end()) {
5222 if (it->get()->fTimes == 0) {
5223 for (auto o : *gROOT->GetListOfBrowsers()) {
5224 auto b = dynamic_cast<TBrowser *>(o);
5225 if (b && b->GetBrowserImp()) { // browserImp is null if browser was closed
5226 // std::cout << GetPath() << " Removing " << it->get()->GetPath() << std::endl;
5227
5228 if (auto _b =
5229 dynamic_cast<TGFileBrowser *>(GETACTBROWSER(dynamic_cast<TRootBrowser *>(b->GetBrowserImp())));
5230 _b) {
5231 auto _root = GETROOTDIR(_b);
5232 if (!_root)
5233 _root = GETLISTTREE(_b)->GetFirstItem();
5234 if (auto item = GETLISTTREE(_b)->FindItemByObj(_root, this); item) {
5235 GETLISTTREE(_b)->OpenItem(item);
5236 }
5237 }
5238
5239 b->RecursiveRemove(
5240 it->get()); // problem: if obj is living in a collapsed node it wont actually get deleted
5241 /*auto _b = dynamic_cast<TGFileBrowser*>( dynamic_cast<TRootBrowser*>(b->GetBrowserImp())->fActBrowser );
5242 if (_b) {
5243 std::cout << _b->fRootDir->GetText() << std::endl;
5244 if (auto item = _b->fListTree->FindItemByObj(_b->fRootDir,it->get()); item) {
5245 std::cout << "Found obj: " << item << " " << item->GetText() << std::endl;
5246 _b->fListTree->RecursiveDeleteItem(_b->fRootDir,it->get());
5247 }
5248
5249 //b->RecursiveRemove(it->get());
5250 if (auto item = _b->fListTree->FindItemByObj(_b->fRootDir,it->get()); item) {
5251 std::cout << "Still Found obj: " << item << std::endl;
5252 }
5253 _b->fListTree->ClearViewPort();
5254
5255 }*/
5256 }
5257 }
5258 /*it->get()->ResetBit(TObject::kNotDeleted); ++it;*/ it = erase(it);
5259 } else {
5260 ++it;
5261 }
5262 }
5263
5264 return *this;
5265}
5266
5267////////////////////////////////////////////////////////////////////////////////
5268/// List of observables (global and regular) of this node.
5269
5271{
5272 xRooNode out(".obs", std::make_shared<RooArgList>(), *this);
5273 out.get<RooArgList>()->setName((GetPath() + ".obs").c_str());
5274 for (auto o : vars()) {
5275 if (o->get<RooAbsArg>()->getAttribute("obs")) {
5276 out.get<RooArgList>()->add(*o->get<RooAbsArg>());
5277 out.emplace_back(o);
5278 }
5279 }
5280 return out;
5281}
5282
5283////////////////////////////////////////////////////////////////////////////////
5284/// List of global observables of this node.
5285
5287{
5288 xRooNode out(".globs", std::make_shared<RooArgList>(), *this);
5289 out.get<RooArgList>()->setName((GetPath() + ".globs").c_str());
5290 for (auto o : obs()) {
5291 if (o->get<RooAbsArg>()->getAttribute("global")) {
5292 out.get<RooArgList>()->add(*o->get<RooAbsArg>());
5293 out.emplace_back(o);
5294 }
5295 }
5296 return out;
5297}
5298
5299////////////////////////////////////////////////////////////////////////////////
5300/// List of regular observables of this node.
5301
5303{
5304 xRooNode out(".robs", std::make_shared<RooArgList>(), *this);
5305 out.get<RooArgList>()->setName((GetPath() + ".robs").c_str());
5306 for (auto o : obs()) {
5307 if (!o->get<RooAbsArg>()->getAttribute("global")) {
5308 out.get<RooArgList>()->add(*o->get<RooAbsArg>());
5309 out.emplace_back(o);
5310 }
5311 }
5312 return out;
5313}
5314
5315////////////////////////////////////////////////////////////////////////////////
5316/// List of parameters (non-observables) of this node.
5317
5319{
5320 if (strcmp(GetName(), ".bins") == 0 && fParent) {
5321 // return pars of the parent - this method is used by covariances() if e.g. do node.bins().covariances()
5322 return fParent->pars();
5323 }
5324 xRooNode out(".pars", std::make_shared<RooArgList>(), *this);
5325 out.get<RooArgList>()->setName((GetPath() + ".pars").c_str());
5326 for (auto o : vars()) {
5327 if (!o->get<RooAbsArg>()->getAttribute("obs")) {
5328 out.get<RooArgList>()->add(*(o->get<RooAbsArg>()));
5329 out.emplace_back(o);
5330 }
5331 }
5332 return out;
5333}
5334
5335////////////////////////////////////////////////////////////////////////////////
5336/// List of parameters that are currently constant
5337
5339{
5340 xRooNode out(".consts", std::make_shared<RooArgList>(), *this);
5341 out.get<RooArgList>()->setName((GetPath() + ".consts").c_str());
5342 for (auto o : pars()) {
5343 if (o->get<RooAbsArg>()->getAttribute("Constant") || o->get<RooConstVar>()) {
5344 out.get<RooArgList>()->add(*o->get<RooAbsArg>());
5345 out.emplace_back(o);
5346 }
5347 }
5348 return out;
5349}
5350
5351////////////////////////////////////////////////////////////////////////////////
5352/// List of parameters that are currently non-constant
5353/// These parameters do not have the "Constant" attribute
5354
5356{
5357 xRooNode out(".floats", std::make_shared<RooArgList>(), *this);
5358 out.get<RooArgList>()->setName((GetPath() + ".floats").c_str());
5359 for (auto o : pars()) {
5360 if (!o->get<RooAbsArg>()->getAttribute("Constant") && !o->get<RooConstVar>()) {
5361 out.get<RooArgList>()->add(*o->get<RooAbsArg>());
5362 out.emplace_back(o);
5363 }
5364 }
5365 return out;
5366}
5367
5368////////////////////////////////////////////////////////////////////////////////
5369/// List of parameters of interest: parameters marked as "of interest"
5370/// These parameters have the "poi" attribute
5371
5373{
5374 xRooNode out(".poi", std::make_shared<RooArgList>(), *this);
5375 out.get<RooArgList>()->setName((GetPath() + ".poi").c_str());
5376 for (auto o : pars()) {
5377 if (o->get<RooAbsArg>()->getAttribute("poi")) {
5378 out.get<RooArgList>()->add(*o->get<RooAbsArg>());
5379 out.emplace_back(o);
5380 }
5381 }
5382 return out;
5383}
5384
5385////////////////////////////////////////////////////////////////////////////////
5386/// List of nuisance parameters: non-constant parameters that are not marked of interest,
5387/// as well as any parameters that have been marked by the "np" attribute
5388
5390{
5391 xRooNode out(".np", std::make_shared<RooArgList>(), *this);
5392 out.get<RooArgList>()->setName((GetPath() + ".np").c_str());
5393 for (auto o : pars()) {
5394 if (o->get<RooAbsArg>()->getAttribute("np") ||
5395 (!o->get<RooAbsArg>()->getAttribute("Constant") && !o->get<RooAbsArg>()->getAttribute("poi") &&
5396 !o->get<RooConstVar>())) {
5397 out.get<RooArgList>()->add(*o->get<RooAbsArg>());
5398 out.emplace_back(o);
5399 }
5400 }
5401 return out;
5402}
5403
5404////////////////////////////////////////////////////////////////////////////////
5405/// List of prespecified parameters: non-floatable parameters
5406
5408{
5409 xRooNode out(".pp", std::make_shared<RooArgList>(), *this);
5410 out.get<RooArgList>()->setName((GetPath() + ".pp").c_str());
5411 for (auto o : pars()) {
5412 if (!o->get<RooAbsArg>()->getAttribute("np") && !o->get<RooAbsArg>()->getAttribute("poi") &&
5413 (o->get<RooAbsArg>()->getAttribute("Constant") || o->get<RooConstVar>())) {
5414 out.get<RooArgList>()->add(*o->get<RooAbsArg>());
5415 out.emplace_back(o);
5416 }
5417 }
5418 return out;
5419}
5420
5421////////////////////////////////////////////////////////////////////////////////
5422/// List of variables (observables and parameters) of this node
5423
5425{
5426 xRooNode out(".vars", std::make_shared<RooArgList>(), *this);
5427 out.get<RooArgList>()->setName((GetPath() + ".vars").c_str());
5428 if (auto coll = get<RooAbsCollection>(); coll) {
5429 for (auto &x : *this) {
5430 for (auto &y : x->vars()) {
5431 out.push_back(y);
5432 }
5433 }
5434 return out;
5435 }
5436 if (auto p = get<RooAbsArg>(); p) {
5437 // also need to get all constPars so use leafNodeServerList .. will include self if is fundamental, which is what
5438 // we want
5439 // ensure all globs appear after robs, as we rely on this ordering for picking "x" var in "reduced" method
5440 xRooNode _globs;
5441 RooArgSet allLeaves;
5442 p->leafNodeServerList(&allLeaves);
5443 for (auto &c : allLeaves) {
5444 if (c->isFundamental() || (dynamic_cast<RooConstVar *>(c) && !TString(c->GetName()).IsFloat())) {
5445 if (!c->getAttribute("global")) {
5446 out.get<RooArgList>()->add(*c);
5447 out.emplace_back(std::make_shared<xRooNode>(*c, *this));
5448 }
5449 if (c->getAttribute("global")) {
5450 _globs.emplace_back(std::make_shared<xRooNode>(*c, *this));
5451 _globs.back()->fFolder = "!globs";
5452 } else if (c->getAttribute("obs")) {
5453 out.back()->fFolder = "!robs";
5454 } else if (c->getAttribute("poi")) {
5455 out.back()->fFolder = "!poi";
5456 } else if (c->getAttribute("np") ||
5457 (!c->getAttribute("Constant") && !c->getAttribute("poi") && c->IsA() != RooConstVar::Class())) {
5458 out.back()->fFolder = "!np";
5459 } else if (!c->getAttribute("Constant") && c->IsA() != RooConstVar::Class()) {
5460 out.back()->fFolder = "!floats";
5461 } else {
5462 out.back()->fFolder = "!pp";
5463 }
5464 }
5465 }
5466 for (auto g : _globs) {
5467 out.get<RooArgList>()->add(*g->get<RooAbsArg>());
5468 out.emplace_back(g);
5469 }
5470 } else if (auto p2 = get<RooAbsData>(); p2) {
5471 for (auto a : *p2->get()) {
5472 a->setAttribute("obs");
5473 out.emplace_back(std::make_shared<xRooNode>(*a, *this));
5474 out.get<RooArgList>()->add(*a);
5475 }
5476 if (auto _dglobs = p2->getGlobalObservables()) {
5477 for (auto &a : *_dglobs) {
5478 a->setAttribute("obs");
5479 a->setAttribute("global");
5480 out.emplace_back(std::make_shared<xRooNode>(*a, *this));
5481 out.get<RooArgList>()->add(*a);
5482 }
5483 } else if (auto _globs = find(".globs"); _globs && _globs->get<RooAbsCollection>()) {
5484 for (auto &a : *_globs->get<RooAbsCollection>()) {
5485 a->setAttribute("obs");
5486 a->setAttribute("global");
5487 out.emplace_back(std::make_shared<xRooNode>(*a, *this));
5488 out.get<RooArgList>()->add(*a);
5489 }
5490 } else if (auto _ws = ws(); _ws) {
5491 if (auto _globs2 = dynamic_cast<RooArgSet *>(GETWSSNAPSHOTS(_ws).find(p2->GetName())); _globs2) {
5492 for (auto a : *_globs2) {
5493 a->setAttribute("obs");
5494 a->setAttribute("global");
5495 out.emplace_back(std::make_shared<xRooNode>(*a, *this));
5496 out.get<RooArgList>()->add(*a);
5497 }
5498 } else if (auto _gl = GETWSSETS(_ws).find("globalObservables"); _gl != GETWSSETS(_ws).end()) {
5499 for (auto &_g : _gl->second) {
5500 auto _clone = std::shared_ptr<RooAbsArg>(dynamic_cast<RooAbsArg *>(_g->Clone(_g->GetName())));
5501 if (auto v = std::dynamic_pointer_cast<RooAbsRealLValue>(_clone); v && _g->getStringAttribute("nominal"))
5502 v->setVal(TString(_g->getStringAttribute("nominal")).Atof());
5503 out.emplace_back(std::make_shared<xRooNode>(_clone, *this));
5504 out.get<RooArgList>()->add(*_clone);
5505 }
5506 } else if (fParent) {
5507 // note: this is slow in large workspaces ... too many obs to look through?
5508 std::unique_ptr<RooAbsCollection> _globs3(fParent->obs().get<RooArgList>()->selectByAttrib("global", true));
5509 // std::unique_ptr<RooAbsCollection> _globs(_ws->allVars().selectByAttrib("global",true)); - tried this to
5510 // be quicker but it wasn't
5511 for (auto &_g : *_globs3) {
5512 auto _clone = std::shared_ptr<RooAbsArg>(dynamic_cast<RooAbsArg *>(_g->Clone(_g->GetName())));
5513 if (auto v = std::dynamic_pointer_cast<RooAbsRealLValue>(_clone); v && _g->getStringAttribute("nominal"))
5514 v->setVal(TString(_g->getStringAttribute("nominal")).Atof());
5515 out.emplace_back(std::make_shared<xRooNode>(_clone, *this));
5516 out.get<RooArgList>()->add(*_clone);
5517 }
5518 }
5519 }
5520 } else if (auto w = get<RooWorkspace>(); w) {
5521 for (auto a : w->allVars()) {
5522 out.emplace_back(std::make_shared<xRooNode>(*a, *this));
5523 out.get<RooArgList>()->add(*a);
5524 }
5525 // add all cats as well
5526 for (auto a : w->allCats()) {
5527 out.emplace_back(std::make_shared<xRooNode>(*a, *this));
5528 out.get<RooArgList>()->add(*a);
5529 }
5530 }
5531 return out;
5532}
5533
5535{
5536 xRooNode out(".components", nullptr, *this);
5537
5538 if (auto p = get<RooAddPdf>(); p) {
5539 // only add each pdf once (the coefs will be accumulated in coefs() method) ...
5540 std::set<RooAbsArg *> donePdfs;
5541 for (auto &o : p->pdfList()) {
5542 if (donePdfs.count(o))
5543 continue;
5544 out.emplace_back(std::make_shared<xRooNode>(*o, *this));
5545 donePdfs.insert(o);
5546 }
5547 } else if (auto p2 = get<RooRealSumPdf>(); p2) {
5548 // check for common prefixes and suffixes, will use to define aliases to shorten names
5549 // if have more than 1 function
5550 // TString commonPrefix=""; TString commonSuffix="";
5551 // if (p->funcList().size() > 1) {
5552 // bool checked=false;
5553 // for(auto& o : p->funcList()) {
5554 // if (!checked) {
5555 // commonPrefix = o->GetName(); commonSuffix = o->GetName(); checked=true;
5556 // } else {
5557 //
5558 // }
5559 // }
5560 // }
5561 std::set<RooAbsArg *> doneFuncs;
5562 for (auto &o : p2->funcList()) {
5563 if (doneFuncs.count(o))
5564 continue;
5565 out.emplace_back(std::make_shared<xRooNode>(*o, *this));
5566 doneFuncs.insert(o);
5567 }
5568 } else if (auto p3 = get<RooAddition>(); p3) {
5569 for (auto &o : p3->list()) {
5570 out.emplace_back(std::make_shared<xRooNode>(*o, *this));
5571 }
5572 } else if (auto p4 = get<RooAbsCollection>(); p4) {
5573 for (auto &a : *p4) {
5574 out.emplace_back(std::make_shared<xRooNode>(*a, *this));
5575 }
5576 } else if (auto p5 = get<RooWorkspace>(); p5) {
5577 for (auto &o : p5->components()) {
5578 // only top-level nodes (only clients are integrals or things that aren't part of the workspace)
5579 // if (o->hasClients()) continue;
5580 bool hasClients = false;
5581 for (auto &c : o->clients()) {
5582 if (!c->InheritsFrom("RooRealIntegral") && p5 == GETWS(c)) {
5583 hasClients = true;
5584 break;
5585 }
5586 }
5587 if (hasClients)
5588 continue;
5589 out.emplace_back(std::make_shared<xRooNode>(*o, *this));
5590 if (o->InheritsFrom("RooAbsPdf")) {
5591 out.back()->fFolder = "!pdfs";
5592 } else {
5593 out.back()->fFolder = "!scratch";
5594 }
5595 }
5596 for (auto &o : p5->allGenericObjects()) {
5597 if (auto fr = dynamic_cast<RooFitResult *>(o); fr) {
5598 TString s(fr->GetTitle());
5599 if (s.Contains(';'))
5600 s = s(0, s.Index(';'));
5601 if (auto _pdf = out.find(s.Data()); _pdf) {
5602 // std::cout << " type = " << _pdf->get()->ClassName() << std::endl;
5603 out.emplace_back(std::make_shared<xRooNode>(fr->GetName(), *fr, _pdf));
5604 // for a while, this node's parent pointed to something of type Node2!!
5605 // how to fix??? - I fxied it with a new constructor to avoid the shared_ptr<Node2> calling the const
5606 // Node2& constructor via getting wrapped in a Node2(shared_ptr<TObject>) call
5607 // out.back()->fParent = _pdf;
5608 // std::cout << " type2 = " << out.back()->fParent->get()->ClassName() << std::endl;
5609 } else {
5610 out.emplace_back(std::make_shared<xRooNode>(fr->GetName(), *fr, *this));
5611 }
5612 out.back()->fFolder = "!fits";
5613 } else {
5614 out.emplace_back(std::make_shared<xRooNode>(*o, *this));
5615 if (strcmp(out.back()->get()->ClassName(), "TStyle") == 0) {
5616 out.back()->fFolder = "!styles";
5617 } else if (strcmp(out.back()->get()->ClassName(), "RooStats::HypoTestInverterResult") == 0) {
5618 out.back()->fFolder = "!scans";
5619 } else {
5620 out.back()->fFolder = "!objects";
5621 }
5622 }
5623 }
5624 for (auto &[k, v] : GETWSSETS(p5)) {
5625 // skip 'CACHE' sets because they are auto-removed when sanitizing workspaces, which will invalidate these
5626 // children
5627 if (k.find("CACHE_") == 0)
5628 continue;
5629 out.emplace_back(std::make_shared<xRooNode>(k.c_str(), v, *this));
5630 out.back()->fFolder = "!sets";
5631 }
5632
5633 RooLinkedList snaps = GETWSSNAPSHOTS(p5);
5634 std::unique_ptr<TIterator> iter(snaps.MakeIterator());
5635 TObject *snap;
5636 while ((snap = iter->Next())) {
5637 out.emplace_back(std::make_shared<xRooNode>(*snap, *this));
5638 out.back()->fFolder = "!snapshots";
5639 }
5640 } else if (strlen(GetName()) > 0 && GetName()[0] == '!' && fParent) {
5641 // special case of dynamic property
5642 if (TString(GetName()) == "!.pars") {
5643 for (auto &c : fParent->pars()) {
5644 out.emplace_back(c);
5645 }
5646 } else {
5647 // the components of a folder are the children of the parent (after browsing) that live in this folder
5648 fParent->browse();
5649 for (auto &c : *fParent) {
5650 if (c->fFolder == GetName()) {
5651 out.emplace_back(c);
5652 }
5653 }
5654 }
5655 }
5656
5657 return out;
5658}
5659
5660////////////////////////////////////////////////////////////////////////////////
5661/// bins of a channel or sample, or channels of a multi-channel pdf
5662
5664{
5665 xRooNode out(".bins", nullptr, *this);
5666
5667 if (auto p = get<RooSimultaneous>(); p) {
5668 std::map<int, std::shared_ptr<xRooNode>> cats; // fill into a map to preserve index ordering
5669 for (auto &c : p->indexCat()) { // is alphabetical in labels
5670 auto pp = p->getPdf(c.first.c_str());
5671 if (!pp)
5672 continue;
5673 cats[c.second] =
5674 std::make_shared<xRooNode>(TString::Format("%s=%s", p->indexCat().GetName(), c.first.c_str()), *pp, *this);
5675 }
5676 for (auto &[_, n] : cats)
5677 out.emplace_back(n);
5678 } else if (auto phf = get<ParamHistFunc>(); phf) {
5679 int i = 1;
5680#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
5681 auto &pSet = phf->_paramSet;
5682#else
5683 auto &pSet = phf->paramList();
5684#endif
5685 for (auto par : pSet) {
5686 out.emplace_back(std::make_shared<xRooNode>(*par, *this));
5687 out.back()->fBinNumber = i;
5688 i++;
5689 }
5690 } else if (auto ax = GetXaxis(); ax) {
5691 for (int i = 1; i <= ax->GetNbins(); i++) {
5692 // create a RooProduct of all bin-specific factors of all shapeFactors
5693 std::vector<RooAbsArg *> _factors;
5694 for (auto f : factors()) {
5695 if (f->get<ParamHistFunc>()) {
5696 if (f->bins()[i - 1]->get<RooProduct>()) {
5697 for (auto &ss : f->bins()[i - 1]->factors())
5698 _factors.push_back(ss->get<RooAbsArg>());
5699 } else {
5700 _factors.push_back(f->bins()[i - 1]->get<RooAbsArg>());
5701 }
5702 }
5703 }
5704 out.emplace_back(std::make_shared<xRooNode>(
5705 TString::Format("%g<=%s<%g", ax->GetBinLowEdge(i), ax->GetParent()->GetName(), ax->GetBinLowEdge(i + 1)),
5706 _factors.empty() ? nullptr
5707 : std::make_shared<RooProduct>(TString::Format("%s.binFactors.bin%d", GetName(), i),
5708 "binFactors", RooArgList()),
5709 *this));
5710 for (auto f : _factors) {
5711#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
5712 out.back()->get<RooProduct>()->_compRSet.add(*f);
5713#else
5714 const_cast<RooArgList &>(out.back()->get<RooProduct>()->realComponents()).add(*f);
5715#endif
5716 }
5717 out.back()->fBinNumber = i;
5718 }
5719 }
5720
5721 return out;
5722}
5723
5725{
5727 bool isResidual = false;
5728
5729 // if parent is a sumpdf or addpdf then include the coefs
5730 // if func appears multiple times then coefs must be combined into a RooAddition temporary
5731 if (fParent) {
5732 // handle case where filters are applied .. need to pass through these
5733 // do this by iterating while fComp is null
5734 auto parent = fParent;
5735 if (!parent->fComp) {
5736 while (!parent->fComp && parent->fParent) {
5737 parent = parent->fParent;
5738 }
5739 // parent should now be node above the filters ... need parent of that
5740 parent = parent->fParent;
5741 if (!parent)
5742 parent = fParent; // revert t original parent in case something went wrong
5743 }
5744 if (auto p = parent->get<RooRealSumPdf>(); p) {
5745 std::size_t i = 0;
5746 for (auto &o : p->funcList()) {
5747 if (o == get()) {
5748 if (i >= p->coefList().size()) {
5749 isResidual = true;
5750 coefs.add(p->coefList());
5751 } else {
5752 coefs.add(*p->coefList().at(i));
5753 }
5754 }
5755 i++;
5756 }
5757 } else if (auto p2 = parent->get<RooAddPdf>(); p2) {
5758 std::size_t i = 0;
5759 if (p2->coefList().empty()) {
5760 // this can happen if all pdfs are extended then the coef is effectively the
5761 // expected number of events
5762 // TODO: test behaviour of xRooNode under this scenario (are histograms correct?)
5763 } else {
5764 for (auto &o : p2->pdfList()) {
5765 if (o == get()) {
5766 if (i >= p2->coefList().size()) {
5767 isResidual = true;
5768 coefs.add(p2->coefList());
5769 } else {
5770 coefs.add(*p2->coefList().at(i));
5771 }
5772 }
5773 i++;
5774 }
5775 }
5776 }
5777 }
5778 if (isResidual) {
5779 // return a node representing 1.-sumOfCoefs
5780 // involves creating sumOfCoefs unless there is only 1 coef, then just use that
5781 auto coefSum = coefs.empty()
5782 ? nullptr
5783 : (coefs.size() == 1 ? std::shared_ptr<RooAbsArg>(coefs.at(0), [](RooAbsArg *) {})
5784 : std::make_shared<RooAddition>((isResidual) ? ".sumOfCoefs" : ".coefs",
5785 "Coefficients of", coefs));
5786 xRooNode out(".coef", coefSum ? std::dynamic_pointer_cast<RooAbsArg>(std::make_shared<RooFormulaVar>(
5787 ".coef", "1-sum(otherCoefs)", "1. - @0", *coefSum))
5788 : nullptr /* should we return a "1." instead? */);
5789 if (coefSum && coefs.size() != 1) {
5790 out.emplace_back(std::make_shared<xRooNode>(".memory", nullptr, *this))
5791 ->emplace_back(
5792 std::make_shared<xRooNode>(".sumOfCoefs", coefSum, out)); // added to keep the sum alive! with the node
5793 }
5794 if (!coefs.empty()) {
5795 out.browse();
5796 }
5797 return out;
5798 } else if (coefs.size() == 1) {
5799 xRooNode out(".coef", std::shared_ptr<RooAbsArg>(coefs.at(0), [](RooAbsArg *) {}), *this);
5800 if (!coefs.empty()) {
5801 out.browse();
5802 }
5803 return out;
5804 } else {
5805 auto coefSum =
5806 coefs.empty()
5807 ? nullptr
5808 : std::make_shared<RooAddition>(".coefs", TString::Format("Coefficients of %s", GetName()), coefs);
5809 xRooNode out(".coefs", coefSum, *this);
5810 if (!coefs.empty())
5811 out.browse();
5812
5813 return out;
5814 }
5815}
5816
5818{
5819 xRooNode out(".factors", nullptr, *this);
5820
5821 if (auto p = get<RooProdPdf>(); p) {
5822 auto _main = mainChild();
5823 if (auto a = _main.get<RooRealSumPdf>(); a && !a->getStringAttribute("alias")) {
5824 a->setStringAttribute("alias", "samples");
5825 } else if (auto a2 = _main.get<RooAddPdf>(); a2 && !a2->getStringAttribute("alias")) {
5826 a2->setStringAttribute("alias", "components");
5827 }
5828 int _npdfs = p->pdfList().size();
5829 for (auto &o : p->pdfList()) {
5830 out.emplace_back(std::make_shared<xRooNode>(*o, *this));
5831 if (_npdfs > 5 && o != _main.get())
5832 out.back()->fFolder = "!constraints";
5833 }
5834 } else if (auto p2 = get<RooProduct>(); p2) {
5835 for (auto &o : p2->components()) {
5836 if (o->InheritsFrom("RooProduct")) {
5837 // get factors of this term
5838 auto x = xRooNode("tmp", *o, *this).factors();
5839 for (auto &n : x) {
5840 out.emplace_back(std::make_shared<xRooNode>(n->GetName(), n->fComp, *this));
5841 }
5842 } else {
5843 out.emplace_back(std::make_shared<xRooNode>(*o, *this));
5844 }
5845 }
5846 } else if (auto w = get<RooWorkspace>(); w) {
5847 // if workspace, return all functions (not pdfs) that have a RooProduct as one of their clients
5848 // or not clients
5849 // exclude obs and globs
5850 auto oo = obs(); // need to keep alive as may contain owning globs
5851 auto &_obs = *(oo.get<RooArgList>());
5852 for (auto a : w->allFunctions()) {
5853 if (_obs.contains(*a))
5854 continue;
5855 bool show(true);
5856 for (auto c : a->clients()) {
5857 show = false;
5858 if (c->InheritsFrom("RooProduct"))
5859 show = true;
5860 }
5861 if (show)
5862 out.emplace_back(std::make_shared<xRooNode>(*a, *this));
5863 }
5864 }
5865
5866 /*
5867 // if parent is a sumpdf or addpdf then include the coefs
5868 // if func appears multiple times then coefs must be combined into a RooAddition temporary
5869 if (fParent) {
5870 RooArgList coefs;
5871 if(auto p = fParent->get<RooRealSumPdf>();p) {
5872 int i=0;
5873 for(auto& o : p->funcList()) {
5874 if (o == get()) {
5875 coefs.add( *p->coefList().at(i) );
5876 }
5877 i++;
5878 }
5879 } else if(auto p = fParent->get<RooAddPdf>(); p) {
5880 int i=0;
5881 for(auto& o : p->pdfList()) {
5882 if (o == get()) {
5883 coefs.add( *p->coefList().at(i) );
5884 }
5885 i++;
5886 }
5887 }
5888 if (!coefs.empty()) {
5889 if (coefs.size() == 1) {
5890 if (strcmp(coefs.at(0)->GetName(),"1")) { // don't add the "1"
5891 out.emplace_back(std::make_shared<Node2>(".coef", *coefs.at(0), *this));
5892 }
5893 } else {
5894 out.emplace_back(std::make_shared<Node2>(".coefs",
5895 std::make_shared<RooAddition>(".coefs", "Coefficients of",
5896 coefs), *this));
5897 }
5898 }
5899 }
5900 */
5901 return out;
5902}
5903
5905{
5906 xRooNode out(".variations", nullptr, *this);
5907
5908 // if (auto p = get<RooSimultaneous>(); p) {
5909 // for (auto &c : p->indexCat()) {
5910 // auto pp = p->getPdf(c.first.c_str());
5911 // if (!pp)
5912 // continue;
5913 // out.emplace_back(
5914 // std::make_shared<xRooNode>(TString::Format("%s=%s", p->indexCat().GetName(), c.first.c_str()), *pp,
5915 // *this));
5916 // }
5917 // } else
5918 if (auto p2 = get<PiecewiseInterpolation>(); p2) {
5919#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
5920 out.emplace_back(std::make_shared<xRooNode>("nominal", p2->_nominal.arg(), *this));
5921#else
5922 out.emplace_back(std::make_shared<xRooNode>("nominal", *(p2->nominalHist()), *this));
5923#endif
5924 for (size_t i = 0; i < p2->paramList().size(); i++) {
5925 // TODO: should we only return one if we find they are symmetrized?
5926 out.emplace_back(std::make_shared<xRooNode>(TString::Format("%s=1", p2->paramList().at(i)->GetName()),
5927 *p2->highList().at(i), *this));
5928 out.emplace_back(std::make_shared<xRooNode>(TString::Format("%s=-1", p2->paramList().at(i)->GetName()),
5929 *p2->lowList().at(i), *this));
5930 }
5931 } else if (auto p3 = get<RooStats::HistFactory::FlexibleInterpVar>(); p3) {
5932#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
5933 out.emplace_back(std::make_shared<xRooNode>("nominal", RooFit::RooConst(p3->_nominal), *this));
5934 for (size_t i = 0; i < p3->_paramList.size(); i++) {
5935 out.emplace_back(std::make_shared<xRooNode>(TString::Format("%s=1", p3->_paramList.at(i)->GetName()),
5936 RooFit::RooConst(p3->_high.at(i)), *this));
5937 out.emplace_back(std::make_shared<xRooNode>(TString::Format("%s=-1", p3->_paramList.at(i)->GetName()),
5938 RooFit::RooConst(p3->_low.at(i)), *this));
5939 }
5940#else
5941 out.emplace_back(std::make_shared<xRooNode>("nominal", RooFit::RooConst(p3->nominal()), *this));
5942 for (size_t i = 0; i < p3->variables().size(); i++) {
5943 out.emplace_back(std::make_shared<xRooNode>(TString::Format("%s=1", p3->variables().at(i)->GetName()),
5944 RooFit::RooConst(p3->high().at(i)), *this));
5945 out.emplace_back(std::make_shared<xRooNode>(TString::Format("%s=-1", p3->variables().at(i)->GetName()),
5946 RooFit::RooConst(p3->low().at(i)), *this));
5947 }
5948#endif
5949
5950 } else if (auto p4 = get<ParamHistFunc>(); p4) {
5951 // I *think* I put this here so that can browse into a ParamHistFunc
5952 // int i = 0;
5953 // for (auto par : p4->_paramSet) {
5954 // TString _name = par->GetName();
5955 // // if(auto _v = dynamic_cast<RooRealVar*>(p->_dataSet.get(i)->first()); _v) {
5956 // // _name = TString::Format("%s=%g",_v->GetName(),_v->getVal());
5957 // // }
5958 // // out.emplace_back(std::make_shared<xRooNode>(_name,*par,*this)); -- -removed cos now have bin()
5959 // method i++;
5960 // }
5961 }
5962 return out;
5963}
5964
5966{
5967 RooArgList out;
5968 out.setName(GetName());
5969 for (auto &k : *this) {
5970 if (auto o = k->get<RooAbsArg>(); o)
5971 out.add(*o);
5972 }
5973 return out;
5974}
5975
5977{
5978 xRooNode out(".datasets()", nullptr, *this);
5979 // removed the browse operation since no longer showing '.datasets()' in browser
5980 // and otherwise this means dataset reduction operation will be called every time we 'browse()' the datasets node
5981 // out.fBrowseOperation = [](xRooNode *f) { return f->fParent->datasets(); };
5982
5983 if (auto _ws = get<RooWorkspace>(); _ws) {
5984 for (auto &d : _ws->allData()) {
5985 out.emplace_back(std::make_shared<xRooNode>(*d, *this));
5986 out.back()->fFolder = "!datasets";
5987 }
5988 } else if (get<RooAbsPdf>() ||
5989 (!get() && fParent &&
5990 fParent->get<RooAbsPdf>())) { // second condition handles 'bins' nodes of pdf, which have null ptr
5991 // only add datasets that have observables that cover all our observables
5992 auto oo = obs(); // must keep alive in case is owning the globs
5993 RooArgSet _obs(*oo.get<RooArgList>());
5994 //_obs.add(coords(true).argList(), true); // include coord observables too, and current xaxis if there's one -
5995 // added in loop below
5996
5997 TString cut;
5998 RooArgSet cutobs;
5999 for (auto _c : coords()) { // coords() moves vars to their respective coordinates too
6000 if (auto _cat = _c->get<RooAbsCategoryLValue>(); _cat) {
6001 if (cut != "")
6002 cut += " && ";
6003 cut += TString::Format("%s==%d", _cat->GetName(), _cat->getCurrentIndex());
6004 _obs.add(*_cat,
6005 true); // note: if we ever changed coords to return clones, would need to keep coords alive
6006 cutobs.add(*_cat);
6007 } else if (auto _rv = _c->get<RooAbsRealLValue>(); _rv) {
6008 // todo: check coordRange is a single range rather than multirange
6009 if (cut != "")
6010 cut += " && ";
6011 cut += TString::Format("%s>=%f&&%s<%f", _rv->GetName(), _rv->getMin(_rv->getStringAttribute("coordRange")),
6012 _rv->GetName(), _rv->getMax(_rv->getStringAttribute("coordRange")));
6013 _obs.add(*_rv,
6014 true); // note: if we ever changed coords to return clones, would need to keep coords alive
6015 cutobs.add(*_rv);
6016 } else {
6017 throw std::runtime_error("datasets(): Unsupported coordinate type");
6018 }
6019 }
6020 if (auto s = get<RooSimultaneous>()) {
6021 // check if we have a pdf for every category ... if not then add to cut
6022 bool hasMissing = false;
6023 TString extraCut = "";
6024 for (auto cat : s->indexCat()) {
6025 if (!s->getPdf(cat.first.c_str())) {
6026 hasMissing = true;
6027 } else {
6028 if (extraCut != "")
6029 extraCut += " || ";
6030 extraCut += TString::Format("%s==%d", s->indexCat().GetName(), cat.second);
6031 }
6032 }
6033 if (hasMissing) {
6034 if (cut != "")
6035 cut += " && ";
6036 cut += "(" + extraCut + ")";
6037 cutobs.add(s->indexCat());
6038 }
6039 }
6040
6041 if (auto ax = GetXaxis(); ax && dynamic_cast<RooAbsArg *>(ax->GetParent())->getAttribute("obs")) {
6042 auto a = dynamic_cast<RooAbsArg *>(ax->GetParent());
6043 _obs.add(*a, true);
6044 }
6045 xRooNode _datasets; // will be any child datasets, along with datasets of the workspace
6046 for (auto &child : *this) {
6047 if (child->get<RooAbsData>())
6048 _datasets.push_back(child);
6049 }
6050 if (auto __ws = ws(); __ws) {
6051 xRooNode _wsNode(*__ws, *this);
6052 for (auto &d : _wsNode.datasets()) {
6053 _datasets.push_back(d);
6054 }
6055 }
6056
6057 for (auto &d : _datasets) {
6058 if (std::unique_ptr<RooAbsCollection>(d->obs().argList().selectCommon(_obs))->size() == _obs.size()) {
6059 // all obs present .. include
6060
6061 if (cut != "") {
6062 RooFormulaVar cutFormula("cut1", cut, cutobs); // doing this to avoid complaints about unused vars
6063 // TODO: Could consider using a 'filter' node (see filter() method) applied to the dataset instead
6064 // of creating and using a reduced dataset here
6065 out.emplace_back(std::make_shared<xRooNode>(
6066 std::shared_ptr<RooAbsData>(d->get<RooAbsData>()->reduce(
6067 *std::unique_ptr<RooAbsCollection>(d->robs().get<RooArgList>()->selectCommon(_obs)), cutFormula)),
6068 *this));
6069 // put a subset of the globs in the returned dataset too
6070 out.back()->get<RooAbsData>()->setGlobalObservables(*std::unique_ptr<RooAbsCollection>(
6071 d->globs().get<RooArgList>()->selectCommon(*globs().get<RooArgList>())));
6072 if (d->get()->TestBit(1 << 20))
6073 out.back()->get()->SetBit(1 << 20);
6074 // need to attach the original dataset so that things like SetBinContent can interact with it
6075 out.back()->fBrowsables.emplace_back(std::make_shared<xRooNode>(".sourceds", d->fComp, *this));
6076 } else {
6077 out.emplace_back(std::make_shared<xRooNode>(d->fComp, *this));
6078 }
6079 }
6080 }
6081 /*else if(auto p = get<RooFitResult>(); p) {
6082 // look for datasets in workspace that match the fit result name after hashing
6083 for(auto& _d : xRooNode(*_ws,*this).datasets()) {
6084 auto _hash = RooAbsTree::nameToHash(_d->get()->GetName());
6085 if (TString::Format("%d;%d",_hash.first,_hash.second) == p->GetTitle()) {
6086 out.emplace_back(std::make_shared<xRooNode>(_d->fComp, *this));
6087 }
6088 }
6089 }*/
6090 }
6091
6092 return out;
6093}
6094
6095std::shared_ptr<xRooNode> xRooNode::getBrowsable(const char *name) const
6096{
6097 for (auto b : fBrowsables) {
6098 if (b && strcmp(b->GetName(), name) == 0)
6099 return b;
6100 }
6101 return nullptr;
6102}
6103
6104TGraph *xRooNode::BuildGraph(RooAbsLValue *v, bool includeZeros, TVirtualPad *fromPad) const
6105{
6106
6107 if (auto fr = get<RooFitResult>(); fr) {
6108 return nullptr;
6109 }
6110
6111 if (auto theData = get<RooDataSet>(); theData) {
6112
6113 TH1 *theHist = nullptr;
6114
6115 if (fromPad) {
6116 // find first histogram in pad
6117 for (auto o : *fromPad->GetListOfPrimitives()) {
6118 theHist = dynamic_cast<TH1 *>(o);
6119 if (theHist) {
6120 theHist = static_cast<TH1 *>(theHist->Clone());
6121 theHist->Reset();
6122 break;
6123 } // clone because theHist gets deleted below
6124 }
6125 }
6126
6127 if (!theHist) {
6128 auto _parentPdf = parentPdf();
6129 if (!_parentPdf) {
6130 // can still build graph if v is an obs ... will use v binning
6131 auto vo = dynamic_cast<TObject *>(v);
6132 if (v && obs().find(vo->GetName())) {
6133 if (auto cat = dynamic_cast<RooAbsCategoryLValue *>(v)) {
6134 theHist = new TH1D(
6135 TString::Format("%s_%s", GetName(), vo->GetName()),
6136 TString::Format("my temp hist;%s", strlen(vo->GetTitle()) ? vo->GetTitle() : vo->GetName()),
6137 cat->numTypes(), 0, cat->numTypes());
6138 int i = 1;
6139 std::map<int, std::string> cats; // fill into a map to preserve index ordering
6140 for (auto &c : *cat) {
6141 cats[c.second] = c.first;
6142 }
6143 for (auto &[_, label] : cats) {
6144 theHist->GetXaxis()->SetBinLabel(i++, label.c_str());
6145 }
6146 } else {
6147 auto _binning = v->getBinningPtr(nullptr);
6148 if (_binning->isUniform()) {
6149 theHist = new TH1D(
6150 TString::Format("%s_%s", GetName(), vo->GetName()),
6151 TString::Format("my temp hist;%s", strlen(vo->GetTitle()) ? vo->GetTitle() : vo->GetName()),
6152 v->numBins(), _binning->lowBound(), _binning->highBound());
6153 } else {
6154 theHist = new TH1D(
6155 TString::Format("%s_%s", GetName(), vo->GetName()),
6156 TString::Format("my temp hist;%s", strlen(vo->GetTitle()) ? vo->GetTitle() : vo->GetName()),
6157 v->numBins(), _binning->array());
6158 }
6159 }
6160 } else {
6161 throw std::runtime_error("Cannot draw dataset without parent PDF");
6162 }
6163 } else {
6164 theHist = _parentPdf->BuildHistogram(v, true);
6165 }
6166 }
6167 if (!theHist)
6168 return nullptr;
6169 // this hist will get filled with w*x to track weighted x position per bin
6170 TH1 *xPos = static_cast<TH1 *>(theHist->Clone("xPos"));
6171 xPos->Reset();
6172 TH1 *xPos2 = static_cast<TH1 *>(theHist->Clone("xPos2"));
6173 xPos2->Reset();
6174 auto nHist = std::unique_ptr<TH1>(static_cast<TH1 *>(theHist->Clone("nEntries")));
6175 nHist->Reset();
6176
6177 auto dataGraph = new TGraphAsymmErrors;
6178 dataGraph->SetEditable(false);
6179 dataGraph->SetName(GetName());
6180 dataGraph->SetTitle(strlen(theData->GetTitle()) ? theData->GetTitle() : theData->GetName());
6181 // next line triggers creation of the histogram inside the graph, in root 6.22 that isn't protected from being
6182 // added to gDirectory
6183 dataGraph->SetTitle(TString::Format("%s;%s;Events", dataGraph->GetTitle(), theHist->GetXaxis()->GetTitle()));
6184 *static_cast<TAttMarker *>(dataGraph) = *static_cast<TAttMarker *>(theHist);
6185 *static_cast<TAttLine *>(dataGraph) = *static_cast<TAttLine *>(theHist);
6186 dataGraph->SetMarkerStyle(20);
6187 dataGraph->SetLineColor(kBlack);
6188
6189 auto _obs = obs();
6190
6191 // auto x = theData->get()->find((v) ? dynamic_cast<TObject*>(v)->GetName() : theHist->GetXaxis()->GetName());
6192 // const RooAbsReal* xvar = (x) ? dynamic_cast<RooAbsReal*>(x) : nullptr;
6193 // const RooAbsCategory* xcat = (x && !xvar) ? dynamic_cast<RooAbsCategory*>(x) : nullptr;
6194 auto x = _obs.find((v) ? dynamic_cast<TObject *>(v)->GetName() : theHist->GetXaxis()->GetName());
6195 if (x && x->get<RooAbsArg>()->getAttribute("global")) {
6196 // is global observable ...
6197 dataGraph->SetPoint(0, x->get<RooAbsReal>()->getVal(), 1e-15);
6198 dataGraph->SetTitle(TString::Format("%s = %f", dataGraph->GetTitle(), dataGraph->GetPointX(0)));
6199 delete xPos;
6200 delete xPos2;
6201 delete theHist;
6202 return dataGraph;
6203 }
6204
6205 const RooAbsReal *xvar = (x) ? x->get<RooAbsReal>() : nullptr;
6206 const RooAbsCategory *xcat = (x && !xvar) ? x->get<RooAbsCategory>() : nullptr;
6207
6208 auto _coords = coords();
6209
6210 TString pName((fromPad) ? fromPad->GetName() : "");
6211 auto _pos = pName.Index('=');
6212
6213 int nevent = theData->numEntries();
6214 for (int i = 0; i < nevent; i++) {
6215 theData->get(i);
6216 bool _skip = false;
6217 for (auto _c : _coords) {
6218 if (auto cat = _c->get<RooAbsCategoryLValue>(); cat) {
6219 if (cat->getIndex() != theData->get()->getCatIndex(cat->GetName())) {
6220 _skip = true;
6221 break;
6222 }
6223 } else if (auto rv = _c->get<RooAbsRealLValue>(); rv) {
6224 // must be in range
6225 if (!rv->inRange(theData->get()->getRealValue(rv->GetName()), rv->getStringAttribute("coordRange"))) {
6226 _skip = true;
6227 break;
6228 }
6229 }
6230 }
6231 if (_pos != -1) {
6232 if (auto cat = dynamic_cast<RooAbsCategory *>(theData->get()->find(TString(pName(0, _pos))));
6233 cat && cat->getLabel() != pName(_pos + 1, pName.Length())) {
6234 _skip = true;
6235 }
6236 }
6237 if (_skip)
6238 continue;
6239
6240 if (xvar) {
6241 xPos->Fill(xvar->getVal(), xvar->getVal() * theData->weight());
6242 xPos2->Fill(xvar->getVal(), pow(xvar->getVal(), 2) * theData->weight());
6243 }
6244
6245 if (xcat) {
6246 theHist->Fill(xcat->getLabel(), theData->weight());
6247 nHist->Fill(xcat->getLabel(), 1);
6248 } else {
6249 theHist->Fill((x) ? xvar->getVal() : 0.5, theData->weight());
6250 nHist->Fill((x) ? xvar->getVal() : 0.5, 1);
6251 }
6252 }
6253
6254 xPos->Divide(theHist);
6255 xPos2->Divide(theHist);
6256
6257 // update the x positions to the means for each bin and use poisson asymmetric errors for data ..
6258 for (int i = 0; i < theHist->GetNbinsX(); i++) {
6259 if (includeZeros || nHist->GetBinContent(i + 1)) {
6260 double val = theHist->GetBinContent(i + 1);
6261
6262 dataGraph->SetPoint(dataGraph->GetN(),
6263 (xvar && val) ? xPos->GetBinContent(i + 1) : theHist->GetBinCenter(i + 1), val);
6264
6265 // x-error will be the (weighted) standard deviation of the x values ...
6266 double xErr = xPos2->GetBinContent(i + 1) - pow(xPos->GetBinContent(i + 1), 2);
6267 xErr = (xErr <= 0) ? 0. : sqrt(xErr); // protects against floating point rounding effects
6268
6269 if (xErr || val) {
6270 dataGraph->SetPointError(dataGraph->GetN() - 1, xErr, xErr,
6271 val - 0.5 * TMath::ChisquareQuantile(TMath::Prob(1, 1) / 2., 2. * (val)),
6272 0.5 * TMath::ChisquareQuantile(1. - TMath::Prob(1, 1) / 2., 2. * (val + 1)) -
6273 val);
6274 }
6275 }
6276 }
6277
6278 // transfer limits from theHist to dataGraph hist
6279 dataGraph->GetHistogram()->GetXaxis()->SetLimits(theHist->GetXaxis()->GetXmin(), theHist->GetXaxis()->GetXmax());
6280 // and bin labels, if any
6281 if (xcat) {
6282 dataGraph->GetHistogram()->GetXaxis()->Set(theHist->GetNbinsX(), 0, theHist->GetNbinsX());
6283 for (int i = 1; i <= theHist->GetNbinsX(); i++)
6284 dataGraph->GetHistogram()->GetXaxis()->SetBinLabel(i, theHist->GetXaxis()->GetBinLabel(i));
6285 }
6286
6287 delete xPos;
6288 delete xPos2;
6289 delete theHist;
6290
6291 // std::shared_ptr<TStyle> style; // use to keep alive for access from GetStyle below, in case getObject
6292 // has decided to return the owning ptr (for some reason) std::string _title =
6293 // strlen(dataGraph->GetTitle()) ? dataGraph->GetTitle() : GetName(); if (!gROOT->GetStyle(_title.c_str()))
6294 // {
6295 // if ( (style = getObject<TStyle>(_title)) ) {
6296 // // loaded style (from workspace?) so put in list and use that
6297 // gROOT->GetListOfStyles()->Add(style.get());
6298 // } else {
6299 // // create new style - gets put in style list automatically so don't have to delete
6300 // // acquire them so saved to workspaces for auto reload ...
6301 // style = const_cast<xRooNode&>(*this).acquireNew<TStyle>(_title.c_str(),
6302 // TString::Format("Style for %s component", _title.c_str()));
6303 // (TAttLine &) (*style) = *dynamic_cast<TAttLine *>(dataGraph);
6304 // (TAttFill &) (*style) = *dynamic_cast<TAttFill *>(dataGraph);
6305 // (TAttMarker &) (*style) = *dynamic_cast<TAttMarker *>(dataGraph);
6306 // gROOT->GetListOfStyles()->Add(style.get());
6307 // }
6308 // }
6309 auto _style = style(dataGraph);
6310 if (_style) {
6311 *dynamic_cast<TAttLine *>(dataGraph) = *_style;
6312 *dynamic_cast<TAttFill *>(dataGraph) = *_style;
6313 *dynamic_cast<TAttMarker *>(dataGraph) = *_style;
6314 }
6315 return dataGraph;
6316 }
6317
6318 throw std::runtime_error("Cannot build graph");
6319}
6320
6322{
6323 if (fr) {
6324 if (auto _w = ws(); _w) {
6325 auto res = acquire(std::shared_ptr<RooFitResult>(const_cast<RooFitResult *>(fr), [](RooFitResult *) {}));
6326 for (auto o : _w->allGenericObjects()) {
6327 if (auto _fr = dynamic_cast<RooFitResult *>(o); _fr) {
6328 _fr->ResetBit(1 << 20);
6329 }
6330 }
6331 res->SetBit(1 << 20);
6332 // assign values
6333 auto allVars = _w->allVars();
6334 allVars = fr->floatParsFinal();
6335 allVars = fr->constPars();
6336 } else {
6337 // need to add to memory as a specific name
6338 throw std::runtime_error("Not supported yet"); // complication is how to replace an existing fitResult in
6339 // .memory auto _clone = std::make_shared<RooFitResult>(*fr);
6340 //_clone->SetName("fitResult");
6341 }
6342 } else {
6343 SetFitResult(fitResult("prefit").get<RooFitResult>());
6344 }
6345}
6346
6348{
6349 if (auto _fr = fr.get<const RooFitResult>()) {
6350 SetFitResult(_fr);
6351 } else
6352 throw std::runtime_error("Not a RooFitResult");
6353}
6354
6355xRooNode xRooNode::fitResult(const char *opt) const
6356{
6357
6358 if (get<RooFitResult>())
6359 return *this;
6360 if (get<RooAbsData>()) {
6361 if (auto _fr = find(".fitResult"); _fr)
6362 return _fr;
6363#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 26, 00)
6364 // check if weightVar of RooAbsData has fitResult attribute on it, will be the generation fit result
6365 if (get<RooDataSet>() && get<RooDataSet>()->weightVar() &&
6366 get<RooDataSet>()->weightVar()->getStringAttribute("fitResult")) {
6367 return xRooNode(getObject<const RooFitResult>(get<RooDataSet>()->weightVar()->getStringAttribute("fitResult")),
6368 *this);
6369 }
6370#endif
6371 return xRooNode();
6372 }
6373
6374 TString sOpt(opt);
6375 if (sOpt == "prefit") {
6376 // build a fitResult using nominal values and infer errors from constraints
6377 // that aren't the 'main' constraints
6378 // Warning("fitResult","Building prefitResult by examining pdf. Consider setting an explicit prefitResult
6379 // (SetFitResult(fr)) where fr name is prefitResult");
6380
6381 // ensure coefs are included if there are any
6382 auto _coefs = coefs();
6383 if (_coefs.get()) {
6384 return xRooNode(RooProduct("tmp", "tmp", RooArgList(*get<RooAbsArg>(), *_coefs.get<RooAbsReal>())))
6385 .fitResult(opt);
6386 }
6387
6388 std::unique_ptr<RooArgList> _pars(dynamic_cast<RooArgList *>(pars().argList().selectByAttrib("Constant", false)));
6389 auto fr = std::make_shared<RooFitResult>("prefitResult", "Prefit");
6390 fr->setFinalParList(*_pars);
6391 for (auto &p : fr->floatParsFinal()) {
6392 auto _v = dynamic_cast<RooRealVar *>(p);
6393 if (!_v)
6394 continue;
6395 if (auto s = _v->getStringAttribute("nominal"); s)
6396 _v->setVal(TString(s).Atof());
6397 auto _constr = xRooNode(fParent->getObject<RooRealVar>(p->GetName()), *this).constraints();
6398 std::shared_ptr<xRooNode> pConstr;
6399 for (auto &c : _constr) {
6400 if (c->get<RooPoisson>() || c->get<RooGaussian>()) {
6401 // require parameter to be a direct server of the constraint pdf to count
6402 bool isServer = true;
6403 if (c->get<RooGaussian>()) {
6404 isServer = false;
6405 for (auto s : c->get<RooAbsArg>()->servers()) {
6406 if (strcmp(s->GetName(), p->GetName()) == 0) {
6407 isServer = true;
6408 break;
6409 }
6410 }
6411 }
6412 if (isServer) {
6413 pConstr = c;
6414 break;
6415 }
6416 }
6417 }
6418 if (pConstr) {
6419 // there will be 3 deps, one will be this par, the other two are the mean and error (or error^2 in case of
6420 // poisson use the one that's a ConstVar as the error to break a tie ...
6421 double prefitVal = 0;
6422 double prefitError = 0;
6423 for (auto &_d : pConstr->vars()) {
6424 if (strcmp(p->GetName(), _d->get()->GetName()) == 0)
6425 continue;
6426 if (auto _c = _d->get<RooConstVar>(); _c && _c->getVal() != 0) {
6427 if (prefitError)
6428 prefitVal = prefitError; // loading val into error already, so move it over
6429 prefitError = _c->getVal();
6430 } else if (prefitError == 0) {
6431 prefitError = _d->get<RooAbsReal>()->getVal();
6432 } else {
6433 prefitVal = _d->get<RooAbsReal>()->getVal();
6434 }
6435 }
6436
6437 if (pConstr->get<RooGaussian>() && pConstr->browse().find(".sigma")) {
6438 prefitError = pConstr->find(".sigma")->get<RooAbsReal>()->getVal();
6439 }
6440 // std::cout << p->GetName() << " extracted " << prefitVal << " " << prefitError << " from ";
6441 // pConstr->deps().Print();
6442 if (pConstr->get<RooPoisson>()) {
6443 // prefitVal will be the global observable value, need to divide that by tau
6444 prefitVal /= prefitError;
6445 // prefiterror will be tau ... need 1/sqrt(tau) for error
6446 prefitError = 1. / sqrt(prefitError);
6447 }
6448 if (!_v->getStringAttribute("nominal"))
6449 _v->setVal(prefitVal);
6450 _v->setError(prefitError);
6451 } else {
6452 // unconstrained, remove error
6453 _v->removeError();
6454 }
6455 }
6456 auto _args = consts().argList();
6457 _args.add(pp().argList());
6458 // global obs are added to constPars list too
6459 auto _globs = globs(); // keep alive as may own glob
6460 _args.add(_globs.argList());
6461 fr->setConstParList(_args);
6462 std::unique_ptr<RooArgList> _snap(dynamic_cast<RooArgList *>(_pars->snapshot()));
6463 for (auto &p : *_snap) {
6464 if (auto atr = p->getStringAttribute("initVal"); atr && dynamic_cast<RooRealVar *>(p))
6465 dynamic_cast<RooRealVar *>(p)->setVal(TString(atr).Atof());
6466 }
6467 fr->setInitParList(*_snap);
6468 return xRooNode(fr, *this);
6469 }
6470
6471 // return first checked fit result present in the workspace
6472 if (auto _w = ws(); _w) {
6473 auto checkFr = [&](TObject *o) {
6474 if (auto _fr = dynamic_cast<RooFitResult *>(o); _fr && _fr->TestBit(1 << 20)) {
6475 // check all pars match final/const values ... if mismatch need to create a new RooFitResult
6476 bool match = true;
6477 for (auto p : pars()) {
6478 if (!p->get<RooAbsReal>()) {
6479 if (auto cat = p->get<RooAbsCategory>();
6480 cat && cat->getCurrentIndex() ==
6481 _fr->floatParsFinal().getCatIndex(cat->GetName(), std::numeric_limits<int>().max())) {
6482 match = false;
6483 break;
6484 }
6485 } else if (p->get<RooAbsArg>()->getAttribute("Constant")) {
6486 if (_fr->floatParsFinal().find(p->GetName()) ||
6487 std::abs(_fr->constPars().getRealValue(p->GetName(), std::numeric_limits<double>::quiet_NaN()) -
6488 p->get<RooAbsReal>()->getVal()) > 1e-15) {
6489 match = false;
6490 break;
6491 }
6492 } else {
6493 if (_fr->constPars().find(p->GetName()) ||
6494 std::abs(
6495 _fr->floatParsFinal().getRealValue(p->GetName(), std::numeric_limits<double>::quiet_NaN()) -
6496 p->get<RooAbsReal>()->getVal()) > 1e-15) {
6497 match = false;
6498 break;
6499 }
6500 }
6501 }
6502 if (!match) {
6503 // create new fit result using covariances from this fit result
6504 std::unique_ptr<RooArgList> _pars(
6505 dynamic_cast<RooArgList *>(pars().argList().selectByAttrib("Constant", false)));
6506 auto fr = std::make_shared<RooFitResult>(TString::Format("%s-dirty", _fr->GetName()));
6507 fr->SetTitle(TString::Format("%s parameter snapshot", GetName()));
6508 fr->setFinalParList(*_pars);
6509 TMatrixTSym<double> *prevCov = static_cast<TMatrixTSym<double> *>(GETDMP(_fr, _VM));
6510 if (prevCov) {
6511 auto cov = _fr->reducedCovarianceMatrix(*_pars);
6512 // make the diagonals all the current error values
6513 for (size_t i = 0; i < _pars->size(); i++) {
6514 if (auto v = dynamic_cast<RooRealVar *>(_pars->at(i))) {
6515 cov(i, i) = pow(v->getError(), 2);
6516 } else {
6517 cov(i, i) = 0;
6518 }
6519 }
6520 fr->setCovarianceMatrix(cov);
6521 }
6522
6523 auto _args = consts().argList();
6524 _args.add(pp().argList());
6525 // global obs are added to constPars list too
6526 auto _globs = globs(); // keep alive as may own glob
6527 _args.add(_globs.argList());
6528 fr->setConstParList(_args);
6529 std::unique_ptr<RooArgList> _snap(dynamic_cast<RooArgList *>(_pars->snapshot()));
6530 for (auto &p : *_snap) {
6531 if (auto atr = p->getStringAttribute("initVal"); atr && dynamic_cast<RooRealVar *>(p))
6532 dynamic_cast<RooRealVar *>(p)->setVal(TString(atr).Atof());
6533 }
6534 fr->setInitParList(*_snap);
6535 return xRooNode(fr, *this);
6536 }
6537 return xRooNode(*_fr, std::make_shared<xRooNode>(*_w, std::make_shared<xRooNode>()));
6538 }
6539 return xRooNode();
6540 };
6541 for (auto o : _w->allGenericObjects()) {
6542 auto out = checkFr(o);
6543 if (out)
6544 return out;
6545 }
6546 for (auto o : GETWSSNAPSHOTS(_w)) {
6547 auto out = checkFr(o);
6548 if (out)
6549 return out;
6550 }
6551 } else {
6552 // objects not in workspaces are allowed to have a fitResult set in their memory
6553 // use getObject to get it
6554 if (auto fr = getObject<RooFitResult>(".fitResult"); fr) {
6555 return xRooNode(fr, *this);
6556 }
6557 }
6558
6559 // ensure coefs are included if there are any
6560 auto _coefs = coefs();
6561 if (_coefs.get()) {
6562 return xRooNode(RooProduct("tmp", "tmp", RooArgList(*get<RooAbsArg>(), *_coefs.get<RooAbsReal>())))
6563 .fitResult(opt);
6564 }
6565
6566 std::unique_ptr<RooArgList> _pars(dynamic_cast<RooArgList *>(pars().argList().selectByAttrib("Constant", false)));
6567 auto fr = std::make_shared<RooFitResult>(TUUID().AsString());
6568 fr->SetTitle(TString::Format("%s uncorrelated parameter snapshot", GetName()));
6569 fr->setFinalParList(*_pars);
6570 fr->setStatus(-1);
6571
6572 TMatrixDSym cov(fr->floatParsFinal().size());
6573 TMatrixTSym<double> *prevCov = static_cast<TMatrixTSym<double> *>(GETDMP(fr.get(), _VM));
6574 if (prevCov) {
6575 for (int i = 0; i < prevCov->GetNcols(); i++) {
6576 for (int j = 0; j < prevCov->GetNrows(); j++) {
6577 cov(i, j) = (*prevCov)(i, j);
6578 }
6579 }
6580 }
6581 int i = 0;
6582 for (auto &p : fr->floatParsFinal()) {
6583 if (!prevCov || i >= prevCov->GetNcols()) {
6584 if (auto v = dynamic_cast<RooRealVar *>(p)) {
6585 cov(i, i) = pow(v->getError(), 2);
6586 } else {
6587 cov(i, i) = 0;
6588 }
6589 }
6590 i++;
6591 }
6592 int covQualBackup = fr->covQual();
6593 fr->setCovarianceMatrix(cov);
6594 fr->setCovQual(covQualBackup);
6595
6596 auto _args = consts().argList();
6597 _args.add(pp().argList());
6598 // global obs are added to constPars list too
6599 auto _globs = globs(); // keep alive as may own glob
6600 _args.add(_globs.argList());
6601 fr->setConstParList(_args);
6602 std::unique_ptr<RooArgList> _snap(dynamic_cast<RooArgList *>(_pars->snapshot()));
6603 for (auto &p : *_snap) {
6604 if (auto atr = p->getStringAttribute("initVal"); atr && dynamic_cast<RooRealVar *>(p))
6605 dynamic_cast<RooRealVar *>(p)->setVal(TString(atr).Atof());
6606 }
6607 fr->setInitParList(*_snap);
6608
6609 // return *const_cast<Node2*>(this)->emplace_back(std::make_shared<Node2>(".fitResult",fr,*this));
6610 return xRooNode(fr, *this);
6611}
6612
6613// xRooNode xRooNode::fitTo_(const char* datasetName) const {
6614// try {
6615// return fitTo(datasetName);
6616// } catch(const std::exception& e) {
6617// new TGMsgBox(gClient->GetRoot(), gClient->GetRoot(), "Exception", e.what(),kMBIconExclamation); // deletes
6618// self on dismiss? return xRooNode();
6619// }
6620// }
6621//
6622// xRooNode xRooNode::fitTo(const char* datasetName) const {
6623// return fitTo(*datasets().at(datasetName));
6624// }
6625
6626void xRooNode::SetRange(const char *range, double low, double high)
6627{
6628 if (!std::isnan(low) && !std::isnan(high) && get<RooRealVar>()) {
6629 if (range && strlen(range)) {
6630 get<RooRealVar>()->setRange(range, low, high);
6631 } else {
6632 get<RooRealVar>()->setRange(low, high);
6633 }
6634 return;
6635 }
6636 if (auto o = get<RooAbsArg>(); o)
6637 o->setStringAttribute("range", range);
6638 // todo: clear the range attribute on all servers
6639 // could make this controlled by a flag but probably easiest to enforce so you must set range
6640 // in children after if you wanted to override
6641}
6642const char *xRooNode::GetRange() const
6643{
6644 std::string &out = fRange;
6645 if (auto o = get<RooAbsArg>(); o && o->getStringAttribute("range"))
6646 out = o->getStringAttribute("range");
6647 auto _parent = fParent;
6648 while (out.empty() && _parent) {
6649 if (auto o = _parent->get<RooAbsArg>(); o && o->getStringAttribute("range"))
6650 out = o->getStringAttribute("range");
6651 _parent = _parent->fParent;
6652 }
6653 return out.c_str();
6654}
6655
6657{
6659}
6660
6661xRooNLLVar xRooNode::nll(const xRooNode &_data, std::initializer_list<RooCmdArg> nllOpts) const
6662{
6663 auto defaultOpts = xRooFit::createNLLOptions(); // smart pointer will cleanup the list
6664 // add user-specified options to list ... if already existing in default list, override and warn
6666 for (auto opt : *defaultOpts) {
6667 l.Add(opt);
6668 }
6669 for (auto &i : nllOpts) {
6670 if (auto o = l.FindObject(i.GetName())) {
6671 Info("nll", "Overriding NLL Option: %s", o->GetName());
6672 l.Remove(o);
6673 }
6674 l.Add(const_cast<RooCmdArg *>(&i));
6675 }
6676
6677 return nll(_data, l);
6678}
6679
6680xRooNode xRooNode::generate(const xRooNode &fr, bool expected, int seed)
6681{
6682 if (!get<RooAbsPdf>()) {
6683 // before giving up, if this is a workspace we can proceed if we only have one model
6684 if (get<RooWorkspace>()) {
6685 std::shared_ptr<xRooNode> mainModel;
6686 for (auto &c : const_cast<xRooNode *>(this)->browse()) {
6687 if (c->get<RooAbsPdf>()) {
6688 if (!mainModel) {
6689 mainModel = c;
6690 } else {
6691 throw std::runtime_error(TString::Format("Workspace has multiple models, you must specify which to "
6692 "generate with (found at least %s and %s)",
6693 mainModel->GetName(), c->GetName()));
6694 }
6695 }
6696 }
6697 if (mainModel)
6698 return mainModel->generate(fr, expected, seed);
6699 }
6700 throw std::runtime_error(TString::Format("%s is not a pdf", GetName()));
6701 }
6702 auto _fr = fr.get<RooFitResult>();
6703
6704 // when generating, will only include channels that are selected
6705 // any unselected but not hidden channel will have data from the only selected dataset added to it
6706 if (get<RooSimultaneous>()) {
6707 std::string selected;
6708 std::string fromds; // list of channels to take from selected ds
6709 bool hasDeselected = false;
6710 for (auto c : bins()) {
6711 TString cName(c->GetName());
6712 cName = cName(cName.Index('=') + 1, cName.Length());
6713 if (!c->get<RooAbsReal>()->isSelectedComp()) {
6714 hasDeselected = true;
6715 if (!c->get<RooAbsArg>()->getAttribute("hidden")) {
6716 if (!fromds.empty())
6717 fromds += ",";
6718 fromds += cName.Data();
6719 }
6720 } else {
6721 if (!selected.empty())
6722 selected += ",";
6723 selected += cName.Data();
6724 }
6725 }
6726 if (hasDeselected) {
6727 std::string dsetName = "";
6728 if (!fromds.empty()) {
6729 // use the first selected dataset as protodata
6730 auto _dsets = datasets();
6731 for (auto &d : _dsets) {
6732 if (d->get()->TestBit(1 << 20)) {
6733 dsetName = d->get()->GetName();
6734 break;
6735 }
6736 }
6737 if (dsetName.empty()) {
6738 throw std::runtime_error(
6739 "Need at least one dataset selected (SetChecked) to use for deselected regions");
6740 }
6741 }
6742 auto result = reduced(selected).generate(fr, expected, seed);
6743 if (!fromds.empty()) {
6744 auto ds = reduced(fromds).datasets()[dsetName];
6745 result.Add(*ds);
6746 result.SetName(TString(result.GetName()) + "_and_" + dsetName.c_str());
6747 }
6748 return result;
6749 }
6750 }
6751
6752 return xRooNode(
6753 xRooFit::generateFrom(*get<RooAbsPdf>(), (_fr ? *_fr : *(fitResult().get<RooFitResult>())), expected, seed).first,
6754 *this);
6755}
6756
6757xRooNLLVar xRooNode::nll(const xRooNode &_data, const RooLinkedList &opts) const
6758{
6759
6760 if (!get<RooAbsPdf>()) {
6761 // before giving up, if this is a workspace we can proceed if we only have one model
6762 if (get<RooWorkspace>()) {
6763 std::shared_ptr<xRooNode> mainModel;
6764 for (auto &c : const_cast<xRooNode *>(this)->browse()) {
6765 if (c->get<RooAbsPdf>()) {
6766 if (!mainModel) {
6767 mainModel = c;
6768 } else {
6769 throw std::runtime_error(TString::Format("Workspace has multiple models, you must specify which to "
6770 "build nll with (found at least %s and %s)",
6771 mainModel->GetName(), c->GetName()));
6772 }
6773 }
6774 }
6775 if (mainModel)
6776 return mainModel->nll(_data, opts);
6777 }
6778 throw std::runtime_error(TString::Format("%s is not a pdf", GetName()));
6779 }
6780
6781 // if simultaneous and any channels deselected then reduce and return
6782 if (get<RooSimultaneous>()) {
6783 std::string selected;
6784 bool hasDeselected = false;
6785 for (auto c : bins()) {
6786 if (!c->get<RooAbsReal>()->isSelectedComp()) {
6787 hasDeselected = true;
6788 } else {
6789 TString cName(c->GetName());
6790 cName = cName(cName.Index('=') + 1, cName.Length());
6791 if (!selected.empty())
6792 selected += ",";
6793 selected += cName.Data();
6794 }
6795 }
6796 if (hasDeselected)
6797 return reduced(selected).nll(_data, opts);
6798 }
6799
6800 if (!_data.get<RooAbsData>()) {
6801 // use node name to find dataset and recall
6802 auto _d = strlen(_data.GetName()) ? datasets().find(_data.GetName()) : nullptr;
6803 if (strlen(_data.GetName()) == 0) {
6804 // create the EXPECTED (asimov) dataset with the observables
6805 auto asi = xRooFit::generateFrom(*get<RooAbsPdf>(), *(fitResult().get<RooFitResult>()), true);
6806 _d = std::make_shared<xRooNode>(asi.first, *this);
6807 if (asi.second) {
6808 _d->emplace_back(
6809 std::make_shared<xRooNode>(".globs", std::const_pointer_cast<RooAbsCollection>(asi.second), *_d));
6810 }
6811 } else if (!_d) {
6812 throw std::runtime_error(TString::Format("Cannot find dataset %s", _data.GetName()));
6813 }
6814 return nll(*_d, opts);
6815 } else if (!_data.fParent || _data.fParent->fComp != fComp) {
6816 // dataset is not parented by this node ... meaning it may need to be reduced,
6817 // do this via the datasets() method by attaching and detaching the dataset
6818 xRooNode me(*this); // since we are in a const method, need to create a copy node.
6819 me.push_back(std::make_shared<xRooNode>(_data));
6820 return nll(*me.datasets().at(_data.GetName()), opts);
6821 }
6822
6823 auto _globs = _data.globs(); // keep alive because may own the globs
6824
6825 auto _opts = std::shared_ptr<RooLinkedList>(new RooLinkedList, [](RooLinkedList *l) {
6826 if (l)
6827 l->Delete();
6828 delete l;
6829 });
6830 RooArgSet _globsSet(_globs.argList());
6831 _opts->Add(RooFit::GlobalObservables(_globsSet).Clone());
6832 if (GetRange() && strlen(GetRange()))
6833 _opts->Add(RooFit::Range(GetRange()).Clone());
6834
6835 // copy over opts ... need to clone each so can safely delete when _opts destroyed
6836 for (int i = 0; i < opts.GetSize(); i++) {
6837 if (strlen(opts.At(i)->GetName()) == 0)
6838 continue; // skipping "none" cmds
6839 if (strcmp(opts.At(i)->GetName(), "GlobalObservables") == 0) {
6840 // maybe warn here?
6841 } else {
6842 _opts->Add(opts.At(i)->Clone(nullptr)); // nullptr needed because accessing Clone via TObject base class puts
6843 // "" instead, so doesnt copy names
6844 }
6845 }
6846
6847 // use shared_ptr method so NLLVar will take ownership of datasets etc if created above
6848 // snapshots the globs out of the nllOpts (see specific constructor of xRooNLLVar)
6849 auto out = xRooFit::createNLL(std::dynamic_pointer_cast<RooAbsPdf>(fComp),
6850 std::dynamic_pointer_cast<RooAbsData>(_data.fComp), *_opts);
6851 return out;
6852}
6853
6854// xRooNode xRooNode::fitTo(const xRooNode& _data) const {
6855//
6856//
6857// auto _pdf = get<RooAbsPdf>();
6858// if (!_pdf) throw std::runtime_error("Not a pdf");
6859//
6860// auto _globs = _data.globs(); // keep alive because may own the globs
6861// RooArgSet globsSet(_globs.argList());
6862//
6863// std::shared_ptr<RooSimultaneous> newPdf;
6864// if(auto s = get<RooSimultaneous>(); s) {
6865// auto rangeName = GetRange();
6866// if (rangeName) {
6867// // need to reduce the RooSimultaneous until fix: https://github.com/root-project/root/issues/8231
6868// std::vector<TString> chanPatterns;
6869// TStringToken pattern(rangeName, ",");
6870// while (pattern.NextToken()) {
6871// chanPatterns.emplace_back(pattern);
6872// }
6873// auto& _cat = const_cast<RooAbsCategoryLValue&>(s->indexCat());
6874// newPdf = std::make_shared<RooSimultaneous>(TString::Format("%s_reduced",GetName()),"Reduced model",_cat);
6875// for(auto& c : variations()) {
6876// TString cName(c->GetName());
6877// cName = cName(cName.Index('=')+1,cName.Length());
6878// _cat.setLabel(cName);
6879// bool matchAny=false;
6880// for(auto& p : chanPatterns) {
6881// if (cName.Contains(TRegexp(p,true))) { matchAny=true; break; }
6882// if (_cat.hasRange(p) && _cat.inRange(p)) { matchAny=true; break; }
6883// }
6884// if(matchAny) {
6885// newPdf->addPdf( *c->get<RooAbsPdf>(), cName );
6886// }
6887// }
6888// RooFitResultTree t(newPdf->GetName(),"",*newPdf);
6889// auto _fr = std::const_pointer_cast<RooFitResult>(t.fitTo(_data.get<RooAbsData>(), &globsSet));
6890// xRooNode parent(_data.GetName(),nullptr,*this);
6891// xRooNode out(_fr->GetName(),/*acquire(_fr)*/ _fr,parent);
6892// // do full propagation by 'checking' the fr ...
6893// out.Checked(&out,true);
6894// return out;
6895// }
6896// }
6897//
6898//
6899//
6900// std::string treeName = TString::Format("fits_%s",GetName()).Data();
6901//
6902// auto _frt = getObject<TTree>(treeName); // get existing frt
6903//
6904// std::shared_ptr<RooFitResultTree> t;
6905// if (_frt) {
6906// t = std::make_shared<RooFitResultTree>(_frt.get());
6907// } else {
6908// t = std::make_shared<RooFitResultTree>(treeName.c_str(),"",*_pdf);
6909// }
6910// //t->SetProgress(true);
6911// auto _fr = std::const_pointer_cast<RooFitResult>(t->fitTo(_data.get<RooAbsData>(), &globsSet));
6912//
6913//
6914//
6915// /*
6916// obs().argList() = s; // sets global observables to their values
6917// auto _fr =
6918// std::shared_ptr<RooFitResult>(_pdf->fitTo(*_data->get<RooAbsData>(),RooFit::GlobalObservables(s),RooFit::Offset(true),RooFit::Save()));
6919// _fr->SetName(TUUID().AsString());
6920// // restore parameters before returning
6921// *std::unique_ptr<RooArgSet>(_pdf->getDependents(_fr->floatParsFinal())) = _fr->floatParsInit();
6922// */
6923//
6924// //_fr->SetTitle(TString::Format("%s;%s",GetName(),datasetName));
6925// if (!_frt) {
6926// t =
6927// std::make_shared<RooFitResultTree>(std::dynamic_pointer_cast<TTree>(const_cast<xRooNode*>(this)->acquire(t->fTree)).get());
6928// }
6929// xRooNode parent(_data.GetName(),nullptr,xRooNode(t,*this));
6930// xRooNode out(_fr->GetName(),/*acquire(_fr)*/ _fr,parent);
6931// // do full propagation by 'checking' the fr ...
6932// out.Checked(&out,true);
6933// return out;
6934// }
6935
6936std::shared_ptr<xRooNode> xRooNode::parentPdf() const
6937{
6938 // find first parent that is a pdf
6939 auto out = fParent;
6940 while (out && !out->get<RooAbsPdf>()) {
6941 out = out->fParent;
6942 }
6943 return out;
6944}
6945
6946xRooNode xRooNode::reduced(const std::string &_range, bool invert) const
6947{
6948 auto rangeName = (_range.empty()) ? GetRange() : _range;
6949 if (!rangeName.empty()) {
6950 std::vector<TString> patterns;
6951 TStringToken pattern(rangeName, ",");
6952 while (pattern.NextToken()) {
6953 patterns.emplace_back(pattern);
6954 }
6955 if (auto s = get<RooSimultaneous>(); s) {
6956 // need to reduce the RooSimultaneous until fix: https://github.com/root-project/root/issues/8231
6957 auto &_cat = const_cast<RooAbsCategoryLValue &>(s->indexCat());
6958 auto newPdf =
6959 std::make_shared<RooSimultaneous>(TString::Format("%s_reduced", GetName()), "Reduced model", _cat);
6960 for (auto &c : bins()) {
6961 TString cName(c->GetName());
6962 cName = cName(cName.Index('=') + 1, cName.Length());
6963 _cat.setLabel(cName);
6964 bool matchAny = false;
6965 for (auto &p : patterns) {
6966 if (cName.Contains(TRegexp(p, true))) {
6967 matchAny = true;
6968 break;
6969 }
6970 if (_cat.hasRange(p) && _cat.inRange(p)) {
6971 matchAny = true;
6972 break;
6973 }
6974 }
6975 if ((matchAny && !invert) || (!matchAny && invert)) {
6976 newPdf->addPdf(*c->get<RooAbsPdf>(), cName);
6977 }
6978 }
6979 return xRooNode(newPdf, fParent);
6980 } else if (get() && !get<RooAbsCollection>() && !components().empty()) {
6981 // create a new obj and remove non-matching components
6982 xRooNode out(std::shared_ptr<TObject>(get()->Clone(TString::Format("%s_reduced", get()->GetName()))), fParent);
6983 // go through components and remove any that don't match pattern
6984 std::vector<TObject *> funcs; // to be removed
6985 for (auto &c : out.components()) {
6986 bool matchAny = false;
6987 for (auto &p : patterns) {
6988 if (TString(c->GetName()).Contains(TRegexp(p, true))) {
6989 matchAny = true;
6990 break;
6991 }
6992 }
6993 if (!((matchAny && !invert) || (!matchAny && invert)))
6994 funcs.push_back(c->get());
6995 }
6996 for (auto &c : funcs)
6997 out.Remove(*c);
6998 if (!funcs.empty()) {
6999 if (auto _pdf = out.get<RooRealSumPdf>(); _pdf) {
7000 _pdf->setFloor(false); // remove floor if removed some functions, which allows evaluation of negative
7001 // valued components
7002 }
7003 }
7004 out.browse();
7005 return out;
7006 } else if (auto fr = get<RooFitResult>()) {
7007 // reduce the fit result by moving unselected float pars into the constPars list and dropping their covariances
7008 xRooNode out(std::shared_ptr<TObject>(fr->Clone(TString::Format("%s_reduced", fr->GetName()))), fParent);
7009 fr = out.get<RooFitResult>();
7010 RooArgList _pars = fr->floatParsFinal();
7011 RooArgList _remPars;
7012 for (auto c : _pars) {
7013 bool matchAny = false;
7014 for (auto &p : patterns) {
7015 if (TString(c->GetName()).Contains(TRegexp(p, true))) {
7016 matchAny = true;
7017 break;
7018 }
7019 }
7020 if (!((matchAny && !invert) || (!matchAny && invert))) {
7021 _remPars.add(*c);
7022 }
7023 }
7024 _pars.remove(_remPars, true);
7025
7026 auto _tmp = fr->reducedCovarianceMatrix(_pars);
7027 int covQualBackup = fr->covQual();
7028 fr->setCovarianceMatrix(_tmp);
7029 fr->setCovQual(covQualBackup);
7030 const_cast<RooArgList &>(fr->floatParsFinal())
7031 .remove(_remPars, true); // is this a memory leak ... should delete the remPars?
7032 return out;
7033
7034 } else if (!get() || get<RooAbsCollection>()) {
7035 // filter the children .... handle special case of filtering ".vars" with "x" option too
7036 xRooNode out(std::make_shared<RooArgList>(), fParent);
7037 size_t nobs = 0;
7038 bool notAllArgs = false;
7039 bool isVars = (strcmp(GetName(), ".vars") == 0);
7040 for (auto c : *this) {
7041 nobs += (c->fFolder == "!robs" || c->fFolder == "!globs");
7042 bool matchAny = false;
7043 for (auto &p : patterns) {
7044 if (TString(c->GetName()).Contains(TRegexp(p, true)) ||
7045 (isVars && p == "x" && (c->fFolder == "!robs" || c->fFolder == "!globs") && nobs == 1)) {
7046 matchAny = true;
7047 break;
7048 }
7049 }
7050 if ((matchAny && !invert) || (!matchAny && invert)) {
7051 out.push_back(c);
7052 if (auto a = c->get<RooAbsArg>()) {
7053 out.get<RooArgList>()->add(*a);
7054 } else {
7055 notAllArgs = true;
7056 }
7057 }
7058 }
7059 if (notAllArgs) {
7060 out.fComp.reset();
7061 }
7062 return out;
7063 }
7064 }
7065
7066 return get<RooArgList>() ? xRooNode(std::make_shared<RooArgList>(), fParent) : *this;
7067}
7068
7069// xRooNode xRooNode::generate(bool expected) const {
7070//
7071// auto fr = fitResult();
7072// auto _fr = fr.get<RooFitResult>();
7073//
7074// auto _pdf = (get<RooAbsPdf>()) ? std::shared_ptr<const xRooNode>(this, [](const xRooNode*){}) : parentPdf();
7075// if (!_pdf) {
7076// throw std::runtime_error("Could not find pdf");
7077// }
7078//
7079// std::shared_ptr<RooDataTree> t;
7080//
7081// std::shared_ptr<RooSimultaneous> newPdf;
7082// if(auto s = _pdf->get<RooSimultaneous>(); s) {
7083// auto rangeName = GetRange();
7084// if (rangeName) {
7085// // need to reduce the RooSimultaneous until fix: https://github.com/root-project/root/issues/8231
7086// std::vector<TString> chanPatterns;
7087// TStringToken pattern(rangeName, ",");
7088// while (pattern.NextToken()) {
7089// chanPatterns.emplace_back(pattern);
7090// }
7091// auto& _cat = const_cast<RooAbsCategoryLValue&>(s->indexCat());
7092// newPdf = std::make_shared<RooSimultaneous>(TString::Format("%s_reduced",GetName()),"Reduced model",_cat);
7093// for(auto& c : _pdf->variations()) {
7094// TString cName(c->GetName());
7095// cName = cName(cName.Index('=')+1,cName.Length());
7096// _cat.setLabel(cName);
7097// bool matchAny=false;
7098// for(auto& p : chanPatterns) {
7099// if (cName.Contains(TRegexp(p,true))) { matchAny=true; break; }
7100// if (_cat.hasRange(p) && _cat.inRange(p)) { matchAny=true; break; }
7101// }
7102// if(matchAny) {
7103// newPdf->addPdf( *c->get<RooAbsPdf>(), cName );
7104// }
7105// }
7106// t = std::make_shared<RooDataTree>(newPdf->GetName(),"",*newPdf);
7107// RooArgSet s1(_pdf->obs().argList());
7108// RooArgSet s2(_pdf->globs().argList());s1.remove(s2);
7109// t->SetObservables(&s1,&s2);
7110// auto _data = t->generate(_fr,expected);
7111//
7112// xRooNode parent(_fr ? _fr->GetName() : "unknown",nullptr,xRooNode(t,*this));
7113// xRooNode out(_data.first->GetName(),/*acquire(_fr)*/ _data.first,parent);
7114// out.emplace_back(std::make_shared<xRooNode>(".globs",std::const_pointer_cast<RooArgSet>(_data.second),out));
7115// return out;
7116// }
7117// }
7118//
7119//
7120// std::string treeName = TString::Format("gen_%s",_pdf->GetName()).Data();
7121//
7122// auto _frt = getObject<TTree>(treeName); // get existing frt
7123//
7124//
7125// if (_frt) {
7126// t = std::make_shared<RooDataTree>(_frt.get());
7127// } else {
7128// t = std::make_shared<RooDataTree>(treeName.c_str(),"",*_pdf->get<RooAbsPdf>());
7129// RooArgSet s1(_pdf->obs().argList());
7130// RooArgSet s2(_pdf->globs().argList());s1.remove(s2);
7131// t->SetObservables(&s1,&s2);
7132// }
7133// auto _data = t->generate(_fr,expected);
7134// if (!_frt) {
7135// t =
7136// std::make_shared<RooDataTree>(std::dynamic_pointer_cast<TTree>(const_cast<xRooNode*>(this)->acquire(t->fTree)).get());
7137// }
7138// xRooNode parent(_fr ? _fr->GetName() : "unknown",nullptr,xRooNode(t,*this));
7139// xRooNode out(_data.first->GetName(),/*acquire(_fr)*/ _data.first,parent);
7140// out.emplace_back(std::make_shared<xRooNode>(".globs",std::const_pointer_cast<RooArgSet>(_data.second),out));
7141// return out;
7142// }
7143
7145public:
7147 double expectedEvents(const RooArgSet *nset) const override
7148 {
7149 return static_cast<RooAbsPdf *>(intpdf.absArg())->expectedEvents(nset);
7150 }
7151 ExtendMode extendMode() const override { return static_cast<RooAbsPdf *>(intpdf.absArg())->extendMode(); }
7152 TObject *clone(const char *newname) const override { return new xRooProjectedPdf(*this, newname); }
7153
7154protected:
7155 double evaluate() const override
7156 {
7157 int code;
7158 return getProjection(&intobs, _normSet, (_normRange.Length() > 0 ? _normRange.Data() : nullptr), code)->getVal();
7159 }
7160};
7161
7162class PdfWrapper : public RooAbsPdf {
7163public:
7164 // need expPdf option while RooProjectedPdf doesn't support keeping things extended
7165 PdfWrapper(RooAbsReal &f, RooAbsReal *coef, bool expEvMode = false, RooAbsPdf *expPdf = nullptr)
7166 : RooAbsPdf(Form("exp_%s", f.GetName())),
7167 fFunc("func", "func", this, f),
7168 fCoef("coef", "coef", this),
7169 fExpPdf("expPdf", "expPdf", this)
7170 {
7171 // don't treat pdf as extended if it has a coefficient and is RooAddPdf: RooAddPdf doesn't extend them unless no
7172 // coefs for any (and all are extendable)
7173 if (coef) {
7174 fCoef.setArg(*coef);
7175 }
7176 if (expPdf && expPdf->canBeExtended() && !(coef && dynamic_cast<RooAddPdf *>(expPdf))) {
7177 fExpPdf.setArg(*expPdf);
7178 } else if (auto _p = dynamic_cast<RooAbsPdf *>(&f);
7179 _p && _p->canBeExtended() && !(coef && dynamic_cast<RooAddPdf *>(_p))) {
7180 fExpPdf.setArg(f); // using self for expectation
7181 }
7182 fExpectedEventsMode = expEvMode;
7183 }
7184 ~PdfWrapper() override{};
7185 PdfWrapper(const PdfWrapper &other, const char *name = nullptr)
7186 : RooAbsPdf(other, name),
7187 fFunc("func", this, other.fFunc),
7188 fCoef("coef", this, other.fCoef),
7189 fExpPdf("expPdf", this, other.fExpPdf),
7190 fExpectedEventsMode(other.fExpectedEventsMode)
7191 {
7192 }
7193 TObject *clone(const char *newname) const override { return new PdfWrapper(*this, newname); }
7194 bool isBinnedDistribution(const RooArgSet &obs) const override { return fFunc->isBinnedDistribution(obs); }
7195 std::list<double> *binBoundaries(RooAbsRealLValue &obs, double xlo, double xhi) const override
7196 {
7197 return fFunc->binBoundaries(obs, xlo, xhi);
7198 }
7199
7200 double evaluate() const override
7201 {
7202 return (fExpectedEventsMode ? 1. : fFunc) *
7203 ((fExpPdf.absArg()) ? static_cast<RooAbsPdf *>(fExpPdf.absArg())->expectedEvents(_normSet) : 1.) *
7204 (fCoef.absArg() ? fCoef : 1.);
7205 }
7206
7207 bool selfNormalized() const override
7208 {
7209 return true;
7210 } // so that doesn't try to do an integral because we are passing integration onto fFunc in evaluate
7211
7212 // faster than full evaluation because doesnt make the integral dependent on the full expression
7213 double getSimplePropagatedError(const RooFitResult &fr, const RooArgSet &nset_in) const
7214 {
7215#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 28, 00)
7216 double oo = getPropagatedError(fr, nset_in); // method was improved in 6.28 so use this instead
7217 if (std::isnan(oo)) {
7218 // may be consequence of zero uncerts
7219 // Calling getParameters() might be costly, but necessary to get the right
7220 // parameters in the RooAbsReal. The RooFitResult only stores snapshots.
7221 RooArgSet allParamsInAbsReal;
7222 getParameters(&nset_in, allParamsInAbsReal);
7223
7224 RooArgList paramList;
7225 for (auto *rrvFitRes : static_range_cast<RooRealVar *>(fr.floatParsFinal())) {
7226
7227 auto rrvInAbsReal = static_cast<RooRealVar const *>(allParamsInAbsReal.find(*rrvFitRes));
7228
7229 // If this RooAbsReal is a RooRealVar in the fit result, we don't need to
7230 // propagate anything and can just return the error in the fit result
7231 if (rrvFitRes->namePtr() == namePtr())
7232 return rrvFitRes->getError();
7233
7234 // Strip out parameters with zero error
7235 if (!rrvFitRes->hasError() ||
7236 rrvFitRes->getError() <= std::abs(rrvFitRes->getVal()) * std::numeric_limits<double>::epsilon())
7237 continue;
7238
7239 // Ignore parameters in the fit result that this RooAbsReal doesn't depend on
7240 if (!rrvInAbsReal)
7241 continue;
7242
7243 // Checking for float equality is a bad. We check if the values are
7244 // negligibly far away from each other, relative to the uncertainty.
7245 if (std::abs(rrvInAbsReal->getVal() - rrvFitRes->getVal()) > 0.01 * rrvFitRes->getError()) {
7246 std::stringstream errMsg;
7247 errMsg
7248 << "RooAbsReal::getPropagatedError(): the parameters of the RooAbsReal don't have"
7249 << " the same values as in the fit result! The logic of getPropagatedError is broken in this case.";
7250
7251 throw std::runtime_error(errMsg.str());
7252 }
7253
7254 paramList.add(*rrvInAbsReal);
7255 }
7256 if (paramList.empty())
7257 return 0.;
7258
7259 std::vector<double> plusVar;
7260 std::vector<double> minusVar;
7261 plusVar.reserve(paramList.size());
7262 minusVar.reserve(paramList.size());
7263
7264 // Create std::vector of plus,minus variations for each parameter
7265 TMatrixDSym V(paramList.size() == fr.floatParsFinal().size() ? fr.covarianceMatrix()
7266 : fr.reducedCovarianceMatrix(paramList));
7267
7268 for (std::size_t ivar = 0; ivar < paramList.size(); ivar++) {
7269
7270 auto &rrv = static_cast<RooRealVar &>(paramList[ivar]);
7271
7272 double cenVal = rrv.getVal();
7273 double errVal = sqrt(V(ivar, ivar));
7274
7275 // this next thing happens if the par has errors but the covariance matrix is empty
7276 // this only happens if the fit was dodgy, so perhaps best to not even try to recover from this
7277 // screwup ... hence I've commented out this fixup here and will let the errors be nan
7278 // if(errVal==0) {
7279 // Warning("getPropagatedError","Missing variance for %s",rrv.GetName());
7280 // errVal = rrv.getError();
7281 // V(ivar,ivar) = errVal*errVal;
7282 // }
7283
7284 // Make Plus variation
7285 rrv.setVal(cenVal + errVal);
7286 plusVar.push_back(getVal(nset_in));
7287
7288 // Make Minus variation
7289 rrv.setVal(cenVal - errVal);
7290 minusVar.push_back(getVal(nset_in));
7291#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
7292 // can try to recover nans ... this stopped being possible in 6.27 onwards because NaNPacker made private
7293 if (std::isnan(plusVar.back()) && RooNaNPacker::isNaNWithPayload(plusVar.back())) {
7294 plusVar.back() = -RooNaNPacker::unpackNaN(plusVar.back());
7295 }
7296 if (std::isnan(minusVar.back()) && RooNaNPacker::isNaNWithPayload(minusVar.back())) {
7297 minusVar.back() = -RooNaNPacker::unpackNaN(minusVar.back());
7298 }
7299#endif
7300 // std::cout << plusVar.back() << " and " << minusVar.back() << std::endl;
7301
7302 rrv.setVal(cenVal);
7303 }
7304
7305 // Re-evaluate this RooAbsReal with the central parameters just to be
7306 // extra-safe that a call to `getPropagatedError()` doesn't change any state.
7307 // It should not be necessary because thanks to the dirty flag propagation
7308 // the RooAbsReal is re-evaluated anyway the next time getVal() is called.
7309 // Still there are imaginable corner cases where it would not be triggered,
7310 // for example if the user changes the RooFit operation more after the error
7311 // propagation.
7312 getVal(nset_in);
7313
7314 TMatrixDSym C(paramList.size());
7315 std::vector<double> errVec(paramList.size());
7316 for (std::size_t i = 0; i < paramList.size(); i++) {
7317 errVec[i] = std::sqrt(V(i, i));
7318 for (std::size_t j = i; j < paramList.size(); j++) {
7319 C(i, j) = V(i, j) / std::sqrt(V(i, i) * V(j, j));
7320 C(j, i) = C(i, j);
7321 }
7322 }
7323
7324 // Make std::vector of variations
7325 TVectorD F(plusVar.size());
7326 for (unsigned int j = 0; j < plusVar.size(); j++) {
7327 F[j] = (plusVar[j] - minusVar[j]) / 2;
7328 }
7329
7330 // Calculate error in linear approximation from variations and correlation coefficient
7331 double sum = F * (C * F);
7332
7333 return sqrt(sum);
7334 }
7335 return oo;
7336#else
7337
7338 // Strip out parameters with zero error
7339 RooArgList fpf_stripped;
7340 for (auto *frv : static_range_cast<RooRealVar *>(fr.floatParsFinal())) {
7341 if (frv->getError() > 1e-20) {
7342 fpf_stripped.add(*frv);
7343 }
7344 }
7345
7346 // Clone self for internal use
7347 RooAbsReal *cloneFunc = const_cast<PdfWrapper *>(this); // (RooAbsReal *)fFunc.absArg()->cloneTree();
7348 // RooAbsPdf *clonePdf = dynamic_cast<RooAbsPdf *>(cloneFunc);
7349 RooArgSet *errorParams = cloneFunc->getObservables(fpf_stripped);
7350
7351 RooArgSet *nset =
7352 nset_in.size() == 0 ? cloneFunc->getParameters(*errorParams) : cloneFunc->getObservables(nset_in);
7353
7354 // Make list of parameter instances of cloneFunc in order of error matrix
7355 RooArgList paramList;
7356 const RooArgList &fpf = fpf_stripped;
7357 std::vector<int> fpf_idx;
7358 for (Int_t i = 0; i < fpf.size(); i++) {
7359 RooAbsArg *par = errorParams->find(fpf[i].GetName());
7360 if (par) {
7361 paramList.add(*par);
7362 fpf_idx.push_back(i);
7363 }
7364 }
7365
7366 std::vector<double> plusVar, minusVar;
7367
7368 // Create vector of plus,minus variations for each parameter
7369 TMatrixDSym V(paramList.size() == fr.floatParsFinal().size() ? fr.covarianceMatrix()
7370 : fr.reducedCovarianceMatrix(paramList));
7371
7372 for (Int_t ivar = 0; ivar < paramList.size(); ivar++) {
7373
7374 RooRealVar &rrv = (RooRealVar &)fpf[fpf_idx[ivar]];
7375
7376 double cenVal = rrv.getVal();
7377 double errVal = sqrt(V(ivar, ivar));
7378
7379 // Make Plus variation
7380 ((RooRealVar *)paramList.at(ivar))->setVal(cenVal + errVal);
7381 // plusVar.push_back((fExpectedEventsMode ? 1. : cloneFunc->getVal(nset)) *
7382 // (clonePdf ? clonePdf->expectedEvents(nset) : 1.));
7383 plusVar.push_back(cloneFunc->getVal(nset));
7384
7385 // Make Minus variation
7386 ((RooRealVar *)paramList.at(ivar))->setVal(cenVal - errVal);
7387 // minusVar.push_back((fExpectedEventsMode ? 1. : cloneFunc->getVal(nset)) *
7388 // (clonePdf ? clonePdf->expectedEvents(nset) : 1.));
7389 minusVar.push_back(cloneFunc->getVal(nset));
7390
7391 ((RooRealVar *)paramList.at(ivar))->setVal(cenVal);
7392 }
7393
7394 getVal(nset); // reset state
7395
7396 TMatrixDSym C(paramList.size());
7397 std::vector<double> errVec(paramList.size());
7398 for (int i = 0; i < paramList.size(); i++) {
7399 errVec[i] = sqrt(V(i, i));
7400 for (int j = i; j < paramList.size(); j++) {
7401 C(i, j) = V(i, j) / sqrt(V(i, i) * V(j, j));
7402 C(j, i) = C(i, j);
7403 }
7404 }
7405
7406 // Make vector of variations
7407 TVectorD F(plusVar.size());
7408 for (unsigned int j = 0; j < plusVar.size(); j++) {
7409 F[j] = (plusVar[j] - minusVar[j]) / 2;
7410 }
7411
7412 // Calculate error in linear approximation from variations and correlation coefficient
7413 double sum = F * (C * F);
7414
7415 // delete cloneFunc;
7416 delete errorParams;
7417 delete nset;
7418
7419 return sqrt(sum);
7420#endif
7421 }
7422
7423private:
7427 bool fExpectedEventsMode = false;
7428};
7429
7430const xRooNode *runningNode = nullptr;
7432
7434{
7435 std::cout << "Got signal " << signum << std::endl;
7436 if (signum == SIGINT) {
7437 std::cout << "Keyboard interrupt while building histogram" << std::endl;
7438 // TODO: create a global mutex for this
7439 runningNode->fInterrupted = true;
7440 } else {
7441 gOldHandlerr(signum);
7442 }
7443}
7444
7446{
7447 auto _doSterilize = [](RooAbsArg *obj) {
7448 if (!obj)
7449 return;
7450 for (int i = 0; i < obj->numCaches(); i++) {
7451 if (auto cache = dynamic_cast<RooObjCacheManager *>(obj->getCache(i))) {
7452 cache->reset();
7453 }
7454 }
7455 if (RooAbsPdf *p = dynamic_cast<RooAbsPdf *>(obj); p) {
7456 p->setNormRange(p->normRange());
7457 }
7458#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
7459 if (RooAbsReal *p = dynamic_cast<RooAbsReal *>(obj); p) {
7460 // need to forget about any normSet that was passed to getVal(...)
7461 // doesn't seem necessary in 6.28
7462
7463 p->setProxyNormSet(nullptr);
7464 p->_lastNSet = nullptr;
7465 }
7466#endif
7467 obj->setValueDirty();
7468 };
7469 if (auto w = get<RooWorkspace>(); w) {
7470 // sterilizing all nodes
7471 for (auto &c : w->components()) {
7472 _doSterilize(c);
7473 }
7474 return;
7475 }
7476 // recursive through all clients and sterlize their normalization caches
7477 std::function<void(RooAbsArg *)> func;
7478 func = [&](RooAbsArg *a) {
7479 if (!a) {
7480 return;
7481 }
7482 _doSterilize(a); // sterilize first so that cache elements don't appear in the client list
7483 // safety net in case sterilizing one client deletes another one of our clients
7484 // monitor for change in clients list size
7485 // found this was only case in 6.26 (valgrind shows invalid read), in 6.28 these went away
7486 // might be in 6.28 the client list iterator became able to handle in-loop edits but didn't see
7487 // in test case that client count changed so just resterilizing if that's the case.
7488 size_t nClients;
7489 do {
7490 nClients = a->clients().size();
7491 for (auto obj : a->clients()) {
7492 func(dynamic_cast<RooAbsArg *>(obj));
7493 if (a->clients().size() != nClients) {
7494 break; // means sterilizing a client changed our clients, so don't trust the client iterator at this
7495 // point
7496 }
7497 }
7498 } while (a->clients().size() != nClients);
7499 };
7500 func(get<RooAbsArg>());
7501}
7502
7503// observables not in the axisVars are automatically projected over
7504xRooNode xRooNode::histo(const xRooNode &vars, const xRooNode &fr, bool content, bool errors) const
7505{
7506
7507 if (!vars.fComp && strlen(vars.GetName())) {
7508 return histo(xRooNode::vars().reduced(vars.GetName()), fr, content, errors);
7509 }
7510
7511 xRooNode out(TString::Format("%s.histo", GetName()), nullptr, *this);
7512
7513 RooAbsLValue *v = nullptr;
7514 if (vars.empty()) {
7515 out.fComp = std::shared_ptr<TH1>(BuildHistogram(nullptr, !content, errors, -1, -1, fr));
7516 } else if (vars.size() == 1) {
7517 v = vars.at(0)->get<RooAbsLValue>();
7518 out.fComp = std::shared_ptr<TH1>(BuildHistogram(v, !content, errors, 1, 0, fr));
7519 } else {
7520 throw std::runtime_error("multi-dim histo not yet supported");
7521 }
7522
7523 if (auto h = out.get<TH1>()) {
7524 if (h->GetXaxis()->IsAlphanumeric()) {
7525 // do this to get bin labels
7526 h->GetXaxis()->SetName("xaxis"); // WARNING -- this messes up anywhere we GetXaxis()->GetName()
7527 }
7528 h->SetStats(false);
7529 h->SetName(GetName());
7530 auto hCopy = static_cast<TH1 *>(h->Clone("nominal"));
7531
7532 if (content && !components().empty()) {
7533 RooAbsReal *sf = nullptr; // TODO - support case of RooExtendPdf drawing (see ::Draw)
7534 // build a stack
7535 THStack *stack = new THStack("stack", "");
7536 int count = 2;
7537 std::map<std::string, int> colorByTitle; // TODO: should fill from any existing legend
7538 std::set<std::string> allTitles;
7539 bool titleMatchName = true;
7540 std::map<std::string, TH1 *> histGroups;
7541 std::vector<TH1 *> hhs;
7542
7543 // support for CMS model case where has single component containing many coeffs
7544 // will build stack by setting each coeff equal to 0 in turn, rebuilding the histogram
7545 // the difference from the "full" histogram will be the component
7546 RooArgList cms_coefs;
7547 if (!components().empty()) {
7548 auto comps = components()[0];
7549 for (auto &c : *comps) {
7550 if (c->fFolder == "!.coeffs")
7551 cms_coefs.add(*c->get<RooAbsArg>());
7552 }
7553 }
7554
7555 if (!cms_coefs.empty()) {
7556 RooRealVar zero("zero", "", 0);
7557 std::shared_ptr<TH1> prevHist(static_cast<TH1 *>(h->Clone()));
7558 for (auto c : cms_coefs) {
7559 // seems I have to remake the function each time, as haven't figured out what cache needs clearing?
7560 std::unique_ptr<RooAbsReal> f(dynamic_cast<RooAbsReal *>(components()[0]->get()->Clone("tmpCopy")));
7561 zero.setAttribute(
7562 Form("ORIGNAME:%s", c->GetName())); // used in redirectServers to say what this replaces
7563 f->redirectServers(RooArgSet(zero), false, true); // each time will replace one additional coef
7564 // zero.setAttribute(Form("ORIGNAME:%s",c->GetName()),false); (commented out so that on next iteration
7565 // will still replace all prev)
7566 auto hh = xRooNode(*f, *this).BuildHistogram(v, false, false, !v ? -1 : 1, !v ? -1 : 0, fr);
7567 if (sf)
7568 hh->Scale(sf->getVal());
7569 if (strlen(hh->GetTitle()) == 0)
7570 hh->SetTitle(c->GetName()); // ensure all hists has titles
7571 titleMatchName &= (TString(c->GetName()) == hh->GetTitle() ||
7572 TString(hh->GetTitle()).BeginsWith(TString(c->GetName()) + "_"));
7573 std::shared_ptr<TH1> nextHist(static_cast<TH1 *>(hh->Clone()));
7574 hh->Add(prevHist.get(), -1.);
7575 hh->Scale(-1.);
7576 hhs.push_back(hh);
7577 prevHist = nextHist;
7578 }
7579 } else {
7580 for (auto &samp : components()) {
7581 auto hh = samp->BuildHistogram(v, false, false, !v ? -1 : 1, !v ? -1 : 0, fr);
7582 if (sf)
7583 hh->Scale(sf->getVal());
7584 hhs.push_back(hh);
7585 if (strlen(hh->GetTitle()) == 0)
7586 hh->SetTitle(samp->GetName()); // ensure all hists has titles
7587 titleMatchName &= (TString(samp->GetName()) == hh->GetTitle() ||
7588 TString(hh->GetTitle()).BeginsWith(TString(samp->GetName()) + "_"));
7589 }
7590 }
7591 for (auto &hh : hhs) {
7592 if (h->GetXaxis()->IsAlphanumeric()) {
7593 // must ensure bin labels match for stack
7594 hh->GetXaxis()->SetName("xaxis");
7595 for (int i = 1; i <= hh->GetNbinsX(); i++)
7596 hh->GetXaxis()->SetBinLabel(i, h->GetXaxis()->GetBinLabel(i));
7597 }
7598 // automatically group hists that all have the same title
7599 if (histGroups.find(hh->GetTitle()) == histGroups.end()) {
7600 histGroups[hh->GetTitle()] = hh;
7601 } else {
7602 // add it into this group
7603 histGroups[hh->GetTitle()]->Add(hh);
7604 delete hh;
7605 continue;
7606 }
7607 auto hhMin = (hh->GetMinimum() == 0) ? hh->GetMinimum(1e-9) : hh->GetMinimum();
7608 if (!stack->GetHists() && h->GetMinimum() > hhMin) {
7609 auto newMin = hhMin - (h->GetMaximum() - hhMin) * gStyle->GetHistTopMargin();
7610 if (hhMin >= 0 && newMin < 0)
7611 newMin = hhMin * 0.99;
7612 h->SetMinimum(newMin); /// adjustYRange(newMin, h->GetMaximum());
7613 }
7614 if (auto it = colorByTitle.find(hh->GetTitle()); it != colorByTitle.end()) {
7615 hh->SetFillColor(it->second);
7616 } else {
7617 bool used = false;
7618 do {
7619 hh->SetFillColor((count++) % 100);
7620 // check not already used this color
7621 used = false;
7622 for (auto hh2 : hhs) {
7623 if (hh != hh2 && hh2->GetFillColor() == hh->GetFillColor()) {
7624 used = true;
7625 break;
7626 }
7627 }
7628 } while (used);
7629 colorByTitle[hh->GetTitle()] = hh->GetFillColor();
7630 }
7631 /*if(stack->GetHists() && stack->GetHists()->GetEntries()>0) {
7632 // to remove rounding effects on bin boundaries, see if binnings compatible
7633 auto _h1 = dynamic_cast<TH1*>(stack->GetHists()->At(0));
7634 if(_h1->GetNbinsX()==hh->GetNbinsX()) TODO ... finish dealing with silly rounding effects
7635 }*/
7636 TString thisOpt = ""; /// dOpt;
7637 // uncomment next line to blend continuous with discrete components .. get some unpleasant "poke through"
7638 // effects though
7639 // if(auto s = samp->get<RooAbsReal>(); s) thisOpt = s->isBinnedDistribution(*dynamic_cast<RooAbsArg*>(v)) ?
7640 // "" : "LF2";
7641 stack->Add(hh, thisOpt);
7642 allTitles.insert(hh->GetTitle());
7643 }
7644
7645 TList *ll = stack->GetHists();
7646 if (ll && ll->GetEntries()) {
7647
7648 // get common prefix to strip off only if all titles match names and
7649 // any title is longer than 10 chars
7650 size_t e = std::min(allTitles.begin()->size(), allTitles.rbegin()->size());
7651 size_t ii = 0;
7652 bool goodPrefix = false;
7653 std::string commonSuffix;
7654 if (titleMatchName && ll->GetEntries() > 1) {
7655 while (ii < e - 1 && allTitles.begin()->at(ii) == allTitles.rbegin()->at(ii)) {
7656 ii++;
7657 if (allTitles.begin()->at(ii) == '_' || allTitles.begin()->at(ii) == ' ')
7658 goodPrefix = true;
7659 }
7660
7661 // find common suffix if there is one .. must start with a "_"
7662 bool stop = false;
7663 while (!stop && commonSuffix.size() < size_t(e - 1)) {
7664 commonSuffix = allTitles.begin()->substr(allTitles.begin()->length() - commonSuffix.length() - 1);
7665 for (auto &t : allTitles) {
7666 if (!TString(t).EndsWith(commonSuffix.c_str())) {
7667 commonSuffix = commonSuffix.substr(1);
7668 stop = true;
7669 break;
7670 }
7671 }
7672 }
7673 if (commonSuffix.find('_') == std::string::npos) {
7674 commonSuffix = "";
7675 } else {
7676 commonSuffix = commonSuffix.substr(commonSuffix.find('_'));
7677 }
7678 }
7679 if (!goodPrefix)
7680 ii = 0;
7681
7682 // also find how many characters are needed to distinguish all entries (that dont have the same name)
7683 // then carry on up to first space or underscore
7684 size_t jj = 0;
7685 std::map<std::string, std::string> reducedTitles;
7686 while (reducedTitles.size() != allTitles.size()) {
7687 jj++;
7688 std::map<std::string, int> titlesMap;
7689 for (auto &s : allTitles) {
7690 if (reducedTitles.count(s))
7691 continue;
7692 titlesMap[s.substr(0, jj)]++;
7693 }
7694 for (auto &s : allTitles) {
7695 if (titlesMap[s.substr(0, jj)] == 1 && (jj >= s.length() || s.at(jj) == ' ' || s.at(jj) == '_')) {
7696 reducedTitles[s] = s.substr(0, jj);
7697 }
7698 }
7699 }
7700
7701 // strip common prefix and suffix before adding
7702 for (int i = ll->GetEntries() - 1; i >= 0; i--) { // go in reverse order
7703 auto _title = (ll->GetEntries() > 5) ? reducedTitles[ll->At(i)->GetTitle()] : ll->At(i)->GetTitle();
7704 _title = _title.substr(ii < _title.size() ? ii : 0);
7705 if (!commonSuffix.empty() && TString(_title).EndsWith(commonSuffix.c_str()))
7706 _title = _title.substr(0, _title.length() - commonSuffix.length());
7707
7708 dynamic_cast<TNamed *>(ll->At(i))->SetTitle(_title.c_str());
7709
7710 // style hists according to available styles ... creating if necessary
7711 auto _style = xRooNode(*ll->At(i), *this).style(ll->At(i));
7712 if (_style) {
7713 *dynamic_cast<TAttLine *>(ll->At(i)) = *_style;
7714 *dynamic_cast<TAttFill *>(ll->At(i)) = *_style;
7715 *dynamic_cast<TAttMarker *>(ll->At(i)) = *_style;
7716 }
7717 // for stacks, fill color of white should be color 10 unless fill style is 0
7718 if (dynamic_cast<TAttFill *>(ll->At(i))->GetFillColor() == kWhite &&
7719 dynamic_cast<TAttFill *>(ll->At(i))->GetFillStyle() != 0) {
7720 // kWhite means 'transparent' in ROOT ... should really use a FillStyle of 0 for that
7721 // so assume user wanted actual white, which is color 10
7722 dynamic_cast<TAttFill *>(ll->At(i))->SetFillColor(10);
7723 }
7724 }
7725 }
7726 h->GetListOfFunctions()->Add(stack, "noclearsame");
7727 if (h->GetSumw2() && h->GetSumw2()->GetSum()) {
7728 hCopy->SetFillStyle(3005);
7729 hCopy->SetFillColor(h->GetLineColor());
7730 hCopy->SetMarkerStyle(0);
7731 h->GetListOfFunctions()->Add(hCopy->Clone(".copy"), "e2same");
7732 *static_cast<TAttFill *>(hCopy) = *h;
7733 }
7734 }
7735
7736 h->GetListOfFunctions()->Add(hCopy, "histsame");
7737 if (h->GetSumw2() && h->GetSumw2()->GetSum()) {
7738 h->SetFillStyle(3005);
7739 h->SetFillColor(h->GetLineColor());
7740 h->SetMarkerStyle(0);
7741 }
7742 }
7743
7744 return out;
7745}
7746
7748{
7749 return xRooNode(fComp, xRooNode(range.GetName(), nullptr, *this));
7750}
7751
7752TH1 *xRooNode::BuildHistogram(RooAbsLValue *v, bool empty, bool errors, int binStart, int binEnd,
7753 const xRooNode &_fr) const
7754{
7755 auto rar = get<RooAbsReal>();
7756 if (!rar)
7757 return nullptr;
7758
7759 TObject *vv = rar;
7760
7761 auto t = TH1::AddDirectoryStatus();
7762 TH1::AddDirectory(false);
7763 TH1 *h = nullptr;
7764 if (!v) {
7765 if (binStart != -1 || binEnd != -1) { // allow v to stay nullptr if doing integral (binStart=binEnd=-1)
7766 if (auto _ax = GetXaxis())
7767 v = dynamic_cast<RooAbsLValue *>(_ax->GetParent());
7768 } else {
7769 // don't need to integrate if doing a self-histogram
7770 v = dynamic_cast<RooRealVar *>(rar);
7771 }
7772 if (v) {
7773 vv = dynamic_cast<TObject *>(v);
7774 } else {
7775 // make a single-bin histogram of just this value
7776 h = new TH1D(rar->GetName(), rar->GetTitle(), 1, 0, 1);
7777 h->GetXaxis()->SetBinLabel(1, rar->GetName());
7778 h->GetXaxis()->SetName(rar->GetName());
7779 }
7780 }
7781
7782 auto x = dynamic_cast<RooRealVar *>(v);
7783 bool setTitle = false;
7784 if (x) {
7785 if (x == rar) {
7786 // self histogram ...
7787 h = new TH1D(rar->GetName(), rar->GetTitle(), 1, 0, 1);
7788 h->Sumw2();
7789 h->GetXaxis()->SetBinLabel(1, rar->GetName());
7790 h->SetBinContent(1, rar->getVal());
7791 if (x->hasError())
7792 h->SetBinError(1, x->getError());
7793 h->SetMaximum(x->hasMax() ? x->getMax()
7794 : (h->GetBinContent(1) + std::max(std::abs(h->GetBinContent(1) * 0.1), 50.)));
7795 h->SetMinimum(x->hasMin() ? x->getMin()
7796 : (h->GetBinContent(1) - std::max(std::abs(h->GetBinContent(1) * 0.1), 50.)));
7797 h->GetXaxis()->SetName(dynamic_cast<TObject *>(v)->GetName());
7798 return h;
7799 }
7800 auto _ax = GetXaxis();
7801 TString binningName = (_ax && _ax->GetParent() == x) ? _ax->GetName() : rar->getStringAttribute("binning");
7802 if (binningName == "")
7803 binningName = rar->GetName();
7804 if (x->hasBinning(binningName)) {
7805 if (x->getBinning(binningName).isUniform()) {
7806 h = new TH1D(rar->GetName(), rar->GetTitle(), x->numBins(binningName) <= 0 ? 100 : x->numBins(binningName),
7807 x->getMin(binningName), x->getMax(binningName));
7808 } else {
7809 h = new TH1D(rar->GetName(), rar->GetTitle(), x->numBins(binningName), x->getBinning(binningName).array());
7810 }
7811 h->GetXaxis()->SetTitle(x->getBinning(binningName).GetTitle());
7812 setTitle = true;
7813 } else if (auto _boundaries =
7814 _or_func(/*rar->plotSamplingHint(*x,x->getMin(),x->getMax())*/ (std::list<double> *)(nullptr),
7815 rar->binBoundaries(*x, -std::numeric_limits<double>::infinity(),
7816 std::numeric_limits<double>::infinity()));
7817 _boundaries) {
7818 std::vector<double> _bins;
7819 for (auto &b : *_boundaries) {
7820 if (_bins.empty() || std::abs(_bins.back() - b) > 1e-5 * _bins.back())
7821 _bins.push_back(b);
7822 } // found sometimes get virtual duplicates in the binning
7823 h = new TH1D(rar->GetName(), rar->GetTitle(), _bins.size() - 1, &_bins[0]);
7824 delete _boundaries;
7825 } else if (!x->hasMax() || !x->hasMin()) {
7826 // use current value of x to estimate range with
7827 h = new TH1D(rar->GetName(), rar->GetTitle(), v->numBins(), x->getVal() * 0.2, x->getVal() * 5);
7828 } else {
7829 h = new TH1D(rar->GetName(), rar->GetTitle(), v->numBins(), x->getBinning().array());
7830 }
7831
7832 } else if (!h) {
7833 h = new TH1D(rar->GetName(), rar->GetTitle(), v->numBins(rar->GetName()), 0, v->numBins(rar->GetName()));
7834 if (auto cat = dynamic_cast<RooAbsCategoryLValue *>(v)) {
7835 int i = 1;
7836 std::map<int, std::string> cats; // fill into a map to preserve index ordering
7837 for (auto &c : *cat) {
7838 cats[c.second] = c.first;
7839 }
7840 for (auto &[_, label] : cats) {
7841 h->GetXaxis()->SetBinLabel(i++, label.c_str());
7842 }
7843 }
7844 }
7845 if (auto o = dynamic_cast<TObject *>(v); o && !setTitle) {
7846 h->GetXaxis()->SetTitle(o->GetTitle());
7847 }
7849 h->Sumw2();
7850 if (v) {
7851 if (h->GetXaxis()->IsAlphanumeric()) {
7852 // store the variable name in the TimeFormat property as well, b.c. alphanumeric requires axis name to be
7853 // "xaxis"
7854 h->GetXaxis()->SetTimeFormat(dynamic_cast<TObject *>(v)->GetName());
7855 }
7856 h->GetXaxis()->SetName(dynamic_cast<TObject *>(v)->GetName()); // WARNING: messes up display of bin labels
7857 }
7858
7859 if (auto s = style(nullptr, false); s) {
7860 static_cast<TAttLine &>(*h) = *s;
7861 static_cast<TAttFill &>(*h) = *s;
7862 static_cast<TAttMarker &>(*h) = *s;
7863 }
7864 if (strlen(h->GetXaxis()->GetTitle()) == 0)
7865 h->GetXaxis()->SetTitle(vv->GetTitle());
7866 auto p = dynamic_cast<RooAbsPdf *>(rar);
7867
7868 // possible speed improvement:
7869 // if(auto spdf = dynamic_cast<RooRealSumPdf*>(p); spdf && spdf->canBeExtended()) {
7870 // p = nullptr; // faster to evaluate sumpdf as a function not a pdf
7871 // }
7872
7873 if (empty && !errors) {
7874 return h;
7875 }
7876
7877 // if (!empty) {
7878
7879 auto _coefs = coefs();
7880
7881 RooFitResult *fr = nullptr;
7882 if (errors) {
7883 // must ensure the fit result we obtain includes pars from coefficients if present
7884 if (_fr.get<RooFitResult>()) {
7885 fr = static_cast<RooFitResult *>(_fr.get<RooFitResult>()->Clone());
7886 } else {
7887 auto frn =
7888 (!_coefs.get() ? *this : xRooNode(RooProduct("tmp", "tmp", RooArgList(*rar, *_coefs.get<RooAbsReal>()))))
7889 .fitResult();
7890 if (strlen(_fr.GetName()))
7891 frn = frn.reduced(_fr.GetName());
7892 fr = dynamic_cast<RooFitResult *>(frn->Clone());
7893 }
7894 if (!GETDMP(fr, _finalPars)) {
7896 }
7897
7898 /// Oct2022: No longer doing this because want to allow fitResult to be used to get partial error
7899 // // need to add any floating parameters not included somewhere already in the fit result ...
7900 // RooArgList l;
7901 // for(auto& p : pars()) {
7902 // auto vv = p->get<RooRealVar>();
7903 // if (!vv) continue;
7904 // if (vv == dynamic_cast<RooRealVar*>(v)) continue;
7905 // if (vv->isConstant()) continue;
7906 // if (fr->floatParsFinal().find(vv->GetName())) continue;
7907 // if (fr->_constPars && fr->_constPars->find(vv->GetName())) continue;
7908 // l.add(*vv);
7909 // }
7910 //
7911 // if (!l.empty()) {
7912 // RooArgList l2; l2.addClone(fr->floatParsFinal());
7913 // l2.addClone(l);
7914 // fr->setFinalParList(l2);
7915 // }
7916
7917 TMatrixTSym<double> *prevCov = static_cast<TMatrixTSym<double> *>(GETDMP(fr, _VM));
7918
7919 if (!prevCov || size_t(fr->covarianceMatrix().GetNcols()) < fr->floatParsFinal().size()) {
7920 TMatrixDSym cov(fr->floatParsFinal().size());
7921 if (prevCov) {
7922 for (int i = 0; i < prevCov->GetNcols(); i++) {
7923 for (int j = 0; j < prevCov->GetNrows(); j++) {
7924 cov(i, j) = (*prevCov)(i, j);
7925 }
7926 }
7927 }
7928 int i = 0;
7929 for (auto &p2 : fr->floatParsFinal()) {
7930 if (!prevCov || i >= prevCov->GetNcols()) {
7931 cov(i, i) = pow(dynamic_cast<RooRealVar *>(p2)->getError(), 2);
7932 }
7933 i++;
7934 }
7935 int covQualBackup = fr->covQual();
7936 fr->setCovarianceMatrix(cov);
7937 fr->setCovQual(covQualBackup);
7938 }
7939
7940 if (v) {
7941 // need to remove v from result as we are plotting as function of v
7942 if (auto _p = fr->floatParsFinal().find(dynamic_cast<TObject *>(v)->GetName()); _p) {
7943 RooArgList _pars = fr->floatParsFinal();
7944 _pars.remove(*_p, true);
7945 auto _tmp = fr->reducedCovarianceMatrix(_pars);
7946 int covQualBackup = fr->covQual();
7947 fr->setCovarianceMatrix(_tmp);
7948 fr->setCovQual(covQualBackup);
7949 const_cast<RooArgList &>(fr->floatParsFinal())
7950 .remove(*_p, true); // NOTE: I think this might be a memory leak, should delete _p after removal
7951 }
7952 }
7953 // finally check at least one float has errors defined (might not be cause if in prefit state)
7954 bool hasErrors = false;
7955 for (auto pp : fr->floatParsFinal()) {
7956 if (dynamic_cast<RooRealVar *>(pp)->hasError()) {
7957 hasErrors = true;
7958 break;
7959 }
7960 }
7961 if (!hasErrors) {
7962 errors = false;
7963 delete fr;
7964 }
7965 }
7966
7967 RooArgSet normSet;
7968 if (v)
7969 normSet.add(*dynamic_cast<RooAbsArg *>(v));
7970
7971 if (binEnd == 0)
7972 binEnd = h->GetNbinsX();
7973
7974 bool needBinWidth = false;
7975 // may have MULTIPLE coefficients for the same pdf!
7976
7977 if (x && (p || _coefs.get() || rar->getAttribute("density"))) {
7978 // pdfs of samples embedded in a sumpdf (aka have a coef) will convert their density value to a content
7979 needBinWidth = true;
7980 }
7981
7982 if (auto spdf = dynamic_cast<RooRealSumPdf *>(p);
7983 spdf && spdf->canBeExtended() && !spdf->getFloor() && !_coefs.get()) {
7984 p = nullptr; // if pdf has no floor, will evaluate it as a function to allow it to be negative - evaluation should
7985 // also be faster (no integral)
7986 // exception is if RooRealSumPdf is embedded in a RooAddPdf (detected by presence of coefs) ... then it must be
7987 // evaluated as a pdf technically should check parent is a RooAddPdf, because if was inside a RooRealSumPdf then
7988 // would be evaluated as a function!
7989 }
7990
7991 // check if we need to do any projecting of other observables
7992 RooAbsReal *oldrar = nullptr;
7993 auto _obs = obs();
7994
7995 for (auto o : _obs) {
7996 if (auto rr = o->get<RooRealVar>(); rr && rr->hasRange("coordRange")) {
7997 rr->removeRange("coordRange"); // doesn't actually remove, just sets to -inf->+inf
7998 rr->setStringAttribute("coordRange", nullptr); // removes the attribute
7999 }
8000 }
8001 // probably should also remove any range on the x-axis variable too, if there is one
8002 if (auto rr = dynamic_cast<RooRealVar *>(v); rr && rr->hasRange("coordRange")) {
8003 rr->removeRange("coordRange"); // doesn't actually remove, just sets to -inf->+inf
8004 rr->setStringAttribute("coordRange", nullptr); // removes the attribute
8005 }
8006 coords(); // loads current coordinates and populates coordRange, if any
8007
8008 if (auto a = dynamic_cast<RooAbsArg *>(v))
8009 _obs.get<RooArgList>()->remove(*a);
8010 if (!_obs.get<RooArgList>()->empty()) {
8011 oldrar = rar;
8012 normSet.add(*_obs.get<RooArgList>());
8013 // check if any obs are restricted range
8014 bool hasRange = false;
8015 for (auto o : normSet) {
8016 if (auto rr = dynamic_cast<RooRealVar *>(o);
8017 rr && (rr->getStringAttribute("coordRange")) && strlen(rr->getStringAttribute("coordRange"))) {
8018 hasRange = true;
8019 break;
8020 }
8021 }
8022 if (p) {
8023 // need to handle special case of RooSimultaneous ... each pdf needs individually projecting over just its
8024 // dependent obs
8025 if (auto s = dynamic_cast<RooSimultaneous *>(p)) {
8026 auto newrar = new RooSimultaneous("projSim", "projSim", const_cast<RooAbsCategoryLValue &>(s->indexCat()));
8027 for (auto pdf : bins()) {
8028 // auto _pdf =
8029 // pdf->get<RooAbsPdf>()->createProjection(*pdf->get<RooAbsPdf>()->getObservables(*_obs.get<RooArgList>()));
8030 auto _pdf =
8031 new xRooProjectedPdf(TString::Format("%s_projection", pdf->GetName()), "", *pdf->get<RooAbsPdf>(),
8032 *pdf->get<RooAbsPdf>()->getObservables(*_obs.get<RooArgList>()));
8033 if (hasRange) {
8034 dynamic_cast<RooAbsPdf *>(_pdf)->setNormRange("coordRange");
8035 }
8036 newrar->addPdf(*_pdf, pdf->coords()[s->indexCat().GetName()]->get<RooCategory>()->getLabel());
8037 }
8038 rar = newrar;
8039 } else {
8040 rar = p->createProjection(
8041 *_obs.get<RooArgList>()); // TODO should use xRooProjectedPdf here too, because not fixed range and
8042 // extend behaviour of RooProjectedPdf in ROOT yet
8043 if (hasRange) {
8044 dynamic_cast<RooAbsPdf *>(rar)->setNormRange("coordRange");
8045 }
8046 }
8047 if (hasRange)
8048 p->setNormRange("coordRange"); // should get cleared when we sterilize
8049 } else {
8050 if (hasRange) {
8051 // commented out passing of normset so that getVal of non-pdf is always a 'raw' value (needed for raw eval
8052 // of RooRealSumPdf)
8053 rar = std::unique_ptr<RooAbsReal>{rar->createIntegral(
8054 *_obs.get<RooArgList>(),
8055 /*RooFit::NormSet(normSet),*/ RooFit::Range("coordRange"))}
8056 .release();
8057 } else {
8058 rar =
8059 std::unique_ptr<RooAbsReal>{rar->createIntegral(*_obs.get<RooArgList>() /*, RooFit::NormSet(normSet)*/)}
8060 .release();
8061 }
8062 }
8063 }
8064
8065 bool scaleExpected = (p && p->canBeExtended() && !_coefs.get());
8066 // Note about above: if pdf has coefficients then its embedded in a RooAddPdf that has coefs defined ...
8067 // in this case we should *not* scale by expected, since the coefs become the scaling instead
8068
8069 std::unique_ptr<RooArgSet> snap(normSet.snapshot());
8070 TStopwatch timeIt;
8071 std::vector<double> lapTimes;
8072 bool warned = false;
8073 if (binStart == -1 && binEnd == -1) {
8074 binEnd = 1;
8075 }
8076 auto cat = (!x) ? dynamic_cast<RooAbsCategoryLValue *>(v) : nullptr;
8077 for (int i = std::max(1, binStart); i <= std::min(h->GetNbinsX(), binEnd); i++) {
8078 timeIt.Start(true);
8079 if (x) {
8080 x->setVal(h->GetBinCenter(i));
8081 } else if (cat) {
8082 cat->setLabel(h->GetXaxis()->GetBinLabel(i)); // because order might not match "binning" order
8083 } else if (v) {
8084 v->setBin(i - 1);
8085 }
8086 if (x && !x->inRange("coordRange"))
8087 continue;
8088
8089 double r = 0;
8090 if (!empty) {
8091 r = /*(p && p->selfNormalized())*/ rar->getVal(p ? &normSet : nullptr);
8092#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
8093 if (std::isnan(r) && RooNaNPacker::isNaNWithPayload(r)) {
8095 }
8096#endif
8097 if (r && _coefs.get()) {
8098 r *= _coefs.get<RooAbsReal>()->getVal(normSet);
8099 }
8100 if (needBinWidth) {
8101 r *= h->GetBinWidth(i);
8102 }
8103 if (scaleExpected) {
8104 // std::cout << r << " exp = " << p->expectedEvents(normSet) << " for normRange " << (p->normRange() ?
8105 // p->normRange() : "null") << std::endl; p->Print();rar->Print();
8106 r *= (p->expectedEvents(normSet));
8107 } // do in here in case dependency on var
8108 }
8109 h->SetBinContent(i, r);
8110
8111 if (errors) {
8112 double res;
8113 if (p) {
8114 // std::cout << "computing error of :" << h->GetBinCenter(i) << std::endl;
8115 // //fr->floatParsFinal().Print(); fr->covarianceMatrix().Print();
8116 res = PdfWrapper((oldrar) ? *rar : *p, _coefs.get<RooAbsReal>(), !v, oldrar ? p : nullptr)
8117 .getSimplePropagatedError(*fr, normSet);
8118#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
8119 // improved normSet invalidity checking, so assuming no longer need this in 6.28 onwards
8120 p->_normSet = nullptr;
8121#endif
8122 } else {
8123 res = RooProduct("errorEval", "errorEval",
8124 RooArgList(*rar, !_coefs.get() ? RooFit::RooConst(1) : *_coefs.get<RooAbsReal>()))
8125 .getPropagatedError(
8126 *fr /*, normSet*/); // should be no need to pass a normSet to a non-pdf (but not verified this)
8127 // especially important not to pass in the case we are evaluated RooRealSumPdf as a function! otherwise
8128 // error will be wrong
8129 }
8130 if (needBinWidth) {
8131 res *= h->GetBinWidth(i);
8132 }
8133 h->SetBinError(i, res);
8134 }
8135 timeIt.Stop();
8136 lapTimes.push_back(timeIt.RealTime());
8137 double time_estimate =
8138 (lapTimes.size() > 1)
8139 ? (h->GetNbinsX() * (std::accumulate(lapTimes.begin() + 1, lapTimes.end(), 0.) / (lapTimes.size() - 1)))
8140 : 0.;
8141 if (!warned && (lapTimes.at(0) > 10 || (lapTimes.size() > 2 && time_estimate > 60.))) {
8142 TTimeStamp t2;
8143 t2.Add(time_estimate);
8144 Warning("BuildHistogram", "Building this histogram will take until %s", t2.AsString());
8145 if (errors) {
8146 // install interrupt handler
8147 runningNode = this;
8148 gOldHandlerr = signal(SIGINT, buildHistogramInterrupt);
8149 }
8150 warned = true;
8151 }
8152 if (fInterrupted) {
8153 if (errors) {
8154 Warning("BuildHistogram", "Skipping errors for remaining bins");
8155 errors = false;
8156 }
8157 fInterrupted = false;
8158 }
8159 }
8160 if (gOldHandlerr) {
8161 signal(SIGINT, gOldHandlerr);
8162 gOldHandlerr = nullptr;
8163 }
8164 normSet = *snap;
8165
8166 if (oldrar) {
8167 std::vector<RooAbsArg *> extra;
8168 if (auto s = dynamic_cast<RooSimultaneous *>(rar)) {
8169 // need to delete all the subpdfs we created too
8170 for (auto _pdf : s->servers()) {
8171 if (dynamic_cast<RooAbsPdf *>(_pdf))
8172 extra.push_back(_pdf);
8173 }
8174 }
8175 extra.push_back(rar);
8176 rar = oldrar;
8177 xRooNode(*rar).sterilize(); // need to clear the cache of the created integral - do this before deleting things!
8178 for (auto a : extra)
8179 delete a;
8180 } else {
8181 sterilize(); // needed to forget about the normSet that was passed to getVal()
8182 }
8183
8184 if (!p && !rar->getAttribute("density") && !needBinWidth) {
8185 h->GetYaxis()->SetTitle(rar->getStringAttribute("units"));
8186 } else if ((p && p->canBeExtended()) || (!p && needBinWidth)) {
8187 h->GetYaxis()->SetTitle("Events");
8188 } else {
8189 h->GetYaxis()->SetTitle("Probability Mass");
8190 }
8191 h->GetYaxis()->SetMaxDigits(3);
8192
8193 if (errors) {
8194 delete fr;
8195 }
8196 //}
8197 return h;
8198}
8199
8200double xRooNode::GetBinData(int bin, const xRooNode &data)
8201{
8202 if (data.get<RooAbsData>()) {
8203 // attach as a child before calling datasets(), so that is included in the list
8204 push_back(std::make_shared<xRooNode>(data));
8205 }
8206 auto node = datasets().find(data.GetName());
8207 if (data.get<RooAbsData>()) {
8208 // remove the child we attached
8209 resize(size() - 1);
8210 }
8211 if (!node)
8212 return std::numeric_limits<double>::quiet_NaN();
8213 return node->GetBinContent(bin);
8214}
8215
8216std::vector<double> xRooNode::GetBinContents(int binStart, int binEnd) const
8217{
8218 if (fBinNumber != -1) {
8219 if (binStart != binEnd || !fParent) {
8220 throw std::runtime_error(TString::Format("%s is a bin - only has one value", GetName()));
8221 }
8222 return fParent->GetBinContents(fBinNumber, fBinNumber);
8223 }
8224 std::vector<double> out;
8225 if (get<RooAbsData>()) {
8226 auto g = BuildGraph(
8227 nullptr,
8228 (binStart != -1 ||
8229 binEnd != -1) /*include points for zeros unless we are asking for a single point with start=end=-1*/);
8230 if (!g) {
8231 return out;
8232 }
8233 if (binStart == binEnd && binStart == -1) {
8234 // integral over all bins if getting bin content -1
8235 double integral(0);
8236 for (int i = 0; i < g->GetN(); i++)
8237 integral += g->GetPointY(i);
8238 out.push_back(integral);
8239 delete g;
8240 return out;
8241 }
8242 for (int i = binStart - 1; i < g->GetN() && (binEnd == 0 || i < binEnd); i++) {
8243 out.push_back(g->GetPointY(i));
8244 }
8245 delete g;
8246 return out;
8247 }
8248
8249 bool doIntegral = false;
8250 if (binStart == binEnd && binStart == -1) {
8251 binStart = -1;
8252 binEnd = -1;
8253 doIntegral = true;
8254 } // return integral if request bin -1
8255 auto h = BuildHistogram(nullptr, false, false, binStart, binEnd);
8256 if (!h) {
8257 throw std::runtime_error(TString::Format("%s has no content", GetName()));
8258 }
8259 if (binEnd == 0) {
8260 binEnd = h->GetNbinsX();
8261 }
8262 if (doIntegral) {
8263 double tot = 0;
8264 for (int i = 1; i <= h->GetNbinsX(); i++) {
8265 tot += h->GetBinContent(i);
8266 }
8267 out.push_back(tot);
8268 } else {
8269 for (int i = binStart; i <= binEnd; i++) {
8270 out.push_back(h->GetBinContent(i));
8271 }
8272 }
8273 delete h;
8274 return out;
8275}
8276
8278{
8279 if (auto a = get<RooAbsArg>(); a) {
8280 // go through servers looking for 'main' thing
8281 for (auto &l : a->servers()) {
8282 if (l->getAttribute("MAIN_MEASUREMENT") || l->InheritsFrom("RooRealSumPdf") || l->InheritsFrom("RooAddPdf")) {
8283 return xRooNode(*l, *this);
8284 }
8285 }
8286 // the main child of a RooProduct is one that has the same name (/alias) as the product (except if is a bin
8287 // factor)
8288 if (a->IsA() == RooProduct::Class() && fBinNumber == -1) {
8289 for (auto &l : factors()) {
8290 if (strcmp(l->GetName(), GetName()) == 0) {
8291 return *l;
8292 }
8293 }
8294 }
8295 }
8296 return xRooNode();
8297}
8298
8300{
8301 if (auto o = get(); o) {
8302 o->Inspect();
8303 } else {
8305 }
8306}
8307
8308bool TopRightPlaceBox(TPad *p, TObject *o, double w, double h, double &xl, double &yb)
8309{
8310#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
8311 // reinitialize collide grid because the filling depends on fUxmin and fUxmax (and ymin ymax too)
8312 // and these aren't filled on the first time we do the placement (they init to 0 and 1), but will be filled
8313 // subsequently
8314 for (int i = 0; i < p->fCGnx; i++) {
8315 for (int j = 0; j < p->fCGny; j++) {
8316 p->fCollideGrid[i + j * p->fCGnx] = true;
8317 }
8318 }
8319 p->FillCollideGrid(o);
8320 Int_t iw = (int)(p->fCGnx * w);
8321 Int_t ih = (int)(p->fCGny * h);
8322
8323 Int_t nxmax = p->fCGnx - iw - 1 - p->fCGnx * p->GetRightMargin();
8324 Int_t nymax = p->fCGny - ih - 1 - p->fCGny * p->GetTopMargin();
8325
8326 for (Int_t j = nymax; j >= 0; j--) {
8327 for (Int_t i = nxmax; i >= 0; i--) {
8328 if (p->Collide(i, j, iw, ih)) {
8329 continue;
8330 } else {
8331 xl = (double)(i) / (double)(p->fCGnx);
8332 yb = (double)(j) / (double)(p->fCGny);
8333 return true;
8334 }
8335 }
8336 }
8337 return false;
8338#else
8339 return p->PlaceBox(o, w, h, xl, yb, "trw");
8340#endif
8341}
8342
8343TPaveText *getPave(const char *name = "labels", bool create = true, bool doPaint = false)
8344{
8345 if (auto p = dynamic_cast<TPaveText *>(gPad->GetPrimitive(name)); p) {
8346 if (doPaint)
8347 gPad->PaintModified(); //-- slows down x11 so trying to avoid
8348 return p;
8349 }
8350 if (!create) {
8351 return nullptr;
8352 }
8353 auto l = new TPaveText(gPad->GetLeftMargin() + 0.02, 1. - gPad->GetTopMargin() - 0.08, 0.6,
8354 1. - gPad->GetTopMargin() - 0.08);
8355 l->SetBorderSize(0);
8356 if (l->GetTextSize() == 0)
8357 l->SetTextSize(gStyle->GetTitleYSize());
8358
8360 // l->SetMargin(0);
8361 l->SetFillStyle(0);
8362 l->SetName(name);
8363 l->Draw();
8364 l->ConvertNDCtoPad();
8365 return l;
8366}
8367
8368TLegend *getLegend(bool create = true, bool doPaint = false)
8369{
8370 if (auto p = dynamic_cast<TLegend *>(gPad->GetPrimitive("legend")); p) {
8371 double x;
8372 double y;
8373 double w = p->GetX2NDC() - p->GetX1NDC();
8374 double h = p->GetY2NDC() - p->GetY1NDC();
8375 if (doPaint)
8376 gPad->PaintModified(); //-- slows down x11 so trying to avoid
8377 if (TopRightPlaceBox(dynamic_cast<TPad *>(gPad), p, w, h, x, y)) {
8378 // squash inside the frame ..
8379 // std::cout << gPad->GetName() << ":" << x << " , " << y << " , " << w << " , " << h << std::endl;
8380 x = std::max(x, (gPad->GetLeftMargin() + 0.02));
8381 y = std::max(y, (gPad->GetBottomMargin() + 0.02));
8382 x = std::min(x, (1. - gPad->GetRightMargin() - 0.02) - w);
8383 y = std::min(y, (1. - gPad->GetTopMargin() - 0.02) - h);
8384 h = std::min(h, (1. - gPad->GetTopMargin() - 0.02) - y);
8385 w = std::min(w, (1. - gPad->GetRightMargin() - 0.02) - x);
8386 // std::cout << gPad->GetName() << ":" << x << " , " << y << " , " << h << " , " << w << std::endl;
8387 p->SetX1NDC(x);
8388 p->SetY1NDC(y);
8389 p->SetX2NDC(x + w);
8390 p->SetY2NDC(y + h);
8391 gPad->Modified();
8392 }
8393 return p;
8394 }
8395 // look for a parent pad called 'legend' and create it there if existing
8396 auto p = gPad;
8397 while ((p != p->GetMother()) && (p = p->GetMother())) {
8398 if (auto q = dynamic_cast<TVirtualPad *>(p->GetPrimitive("legend")); q) {
8399 q->Modified();
8400 p = q;
8401 break;
8402 }
8403 }
8404 auto tmpPad = gPad;
8405 TLegend *l = nullptr;
8406 if (p && strcmp(p->GetName(), "legend") == 0) {
8407 if (l = dynamic_cast<TLegend *>(p->GetPrimitive("legend")); l || !create)
8408 return l;
8409 p->cd();
8410 l = new TLegend(gPad->GetLeftMargin(), 1. - gPad->GetTopMargin(), 1. - gPad->GetRightMargin(),
8411 gPad->GetBottomMargin());
8412 l->SetBorderSize(1); // ensure has a border
8413 } else {
8414 if (!create)
8415 return nullptr;
8416 l = new TLegend(0.6, 1. - gPad->GetTopMargin() - 0.08, 0.75, 1. - gPad->GetTopMargin() - 0.08);
8417 l->SetBorderSize(0);
8418 // legend text will be required to match y-axis
8419 if (l->GetTextSize() == 0) {
8420 l->SetTextSize(gStyle->GetTitleYSize());
8421 l->SetTextFont(gStyle->GetTitleFont("Y"));
8422 }
8423 }
8425 // l->SetMargin(0);
8426 l->SetFillStyle(0);
8427 l->SetName("legend");
8428 l->Draw();
8429 l->ConvertNDCtoPad();
8430 tmpPad->cd();
8431 return l;
8432}
8433
8434std::string formatLegendString(const std::string &s)
8435{
8436 auto i = s.find("\n");
8437 if (i == std::string::npos) {
8438 return s;
8439 }
8440 return std::string("#splitline{") + s.substr(0, i) + "}{" + formatLegendString(s.substr(i + 1)) + "}";
8441}
8442
8443void addLegendEntry(TObject *o, const char *title, const char *opt)
8444{
8445 auto l = getLegend();
8446 if (!l)
8447 return;
8448 // check for entry already existing with same title
8449 for (auto a : *l->GetListOfPrimitives()) {
8450 if (formatLegendString(title) == dynamic_cast<TLegendEntry *>(a)->GetLabel())
8451 return;
8452 }
8453 if (l->GetListOfPrimitives()->GetEntries() > 20)
8454 return; // todo: create an 'other' entry?
8455
8456 l->AddEntry(o, formatLegendString(title).c_str(), opt);
8457 if (auto nObj = l->GetListOfPrimitives()->GetEntries(); nObj > 0) {
8458 // each entry takes up 0.05 ... maximum of N*(N+4) (where N is # cols) before next column
8459 int nn = l->GetNColumns();
8460 nn *= (nn + 4);
8461 if (nObj > 1 && (nObj % nn) == 1) {
8462 l->SetNColumns(l->GetNColumns() + 1);
8463 if (l->GetBorderSize() == 0) {
8464 l->SetX1NDC(l->GetX2NDC() - 0.15 * l->GetNColumns());
8465 }
8466 }
8467 if (l->GetBorderSize() == 0) {
8468 l->SetY1NDC(l->GetY2NDC() - 0.05 * gPad->GetHNDC() * std::ceil((double(nObj) / l->GetNColumns())));
8469 }
8470 }
8471
8472 getLegend(); // to mark modified
8473}
8474
8475// this exists to avoid calling update excessively because it slows down x11 ... but still
8476// need to call update twice if have a legend drawn in order to relocate it.
8478public:
8479 PadRefresher(TVirtualPad *p) : fPad(p) { nExisting++; }
8481 {
8482 if (fPad) {
8483 getLegend(false, true);
8484 fPad->GetCanvas()->Paint();
8485 fPad->GetCanvas()->Update();
8486#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 30, 00)
8487 fPad->GetCanvas()->ResetUpdated(); // stops previous canvas being replaced in a jupyter notebook
8488#endif
8489 fPad->cd();
8490 }
8491 nExisting--;
8492 }
8493 TVirtualPad *fPad = nullptr;
8494 static int nExisting;
8495};
8496
8498
8500{
8501 // in order to catch exceptions to prevent crash of GUI, do this:
8502 if (gROOT->FromPopUp()) {
8503 gROOT->SetFromPopUp(false);
8504 try {
8505 Draw(opt);
8506 } catch (const std::exception &e) {
8507 new TGMsgBox(
8508 gClient->GetRoot(),
8509 (gROOT->GetListOfBrowsers()->At(0))
8510 ? dynamic_cast<TGWindow *>(static_cast<TBrowser *>(gROOT->GetListOfBrowsers()->At(0))->GetBrowserImp())
8511 : gClient->GetRoot(),
8512 "Exception", e.what(),
8513 kMBIconExclamation); // deletes self on dismiss?
8514 }
8515 gROOT->SetFromPopUp(true);
8516 return;
8517 }
8518
8519 TString sOpt2(opt);
8520 sOpt2.ToLower();
8521 if (!get() && !IsFolder() && !sOpt2.Contains("x="))
8522 return;
8523
8524 if (auto ir = get<RooStats::HypoTestInverterResult>()) {
8525 xRooHypoSpace(ir).Draw(opt);
8527 return;
8528 } else if (get<RooStats::HypoTestResult>()) {
8529 if (gPad)
8530 gPad->Clear();
8531 xRooNLLVar::xRooHypoPoint(std::dynamic_pointer_cast<RooStats::HypoTestResult>(fComp)).Draw(opt);
8532 {
8533 PadRefresher p(gPad); // refreshes the pad
8534 }
8536 return;
8537 }
8538
8539 if (sOpt2 == "pcls" && get<RooRealVar>() && fParent && fParent->get<RooAbsPdf>()) {
8540 // use the first selected dataset
8541 auto _dsets = fParent->datasets();
8542 // bool _drawn=false;
8543 TString dsetName = "";
8544 for (auto &d : _dsets) {
8545 if (d->get()->TestBit(1 << 20)) {
8546 dsetName = d->get()->GetName();
8547 break;
8548 }
8549 }
8550 auto hs = fParent->nll(dsetName.Data()).hypoSpace(get<RooRealVar>()->GetName());
8551 hs.limits("cls visualize");
8552 hs.SetName(TUUID().AsString());
8553 if (ws()) {
8554 ws()->import(*hs.result());
8555 }
8556 return;
8557 }
8558
8559 if (auxFunctions.empty()) {
8560 // add the defaults: Ratio and Signif
8562 "Ratio",
8563 [](double a, double b, double) {
8564 if (a == 0)
8565 return 0.;
8566 if (b == 0 && a == 0)
8567 return 1.;
8568 return a / b;
8569 },
8570 true);
8572 "Signif",
8573 [](double n, double b, double sigma) {
8574 double t0 = 0;
8575 if (sigma <= 0.) {
8576 // use simplified expression ...
8577 t0 = 2. * (((n == 0) ? 0 : n * log(n / b)) - (n - b));
8578 } else {
8579 double sigma2 = sigma * sigma;
8580 double b_hathat = 0.5 * (b - sigma2 + sqrt(pow(b - sigma2, 2) + 4 * n * sigma2));
8581 // double s_hat = n - m;
8582 // double b_hat = m;
8583 t0 = 2. * (((n == 0) ? 0 : n * log(n / b_hathat)) + b_hathat - n + pow(b - b_hathat, 2) / (2. * sigma2));
8584 }
8585 if (t0 < 0)
8586 return 0.; // can happen from numerical precision
8587 return (n >= b) ? sqrt(t0) : -sqrt(t0);
8588 },
8589 false);
8590 }
8591
8592 TString sOpt(opt);
8593
8594 RooAbsLValue *v = nullptr;
8595 std::vector<double> xPoints;
8596 if (sOpt2.Contains("x=")) {
8597 // specifying a particular var to scan over ...
8598 int _idx = sOpt2.Index("x=");
8599 int _eidx = sOpt2.Index(';', _idx);
8600 TString varPart = sOpt(_idx + 2, (_eidx < 0 ? sOpt2.Length() : _eidx) - (_idx + 2));
8601 TString varName = varPart;
8602 // if varName is of form str(num,num,num) then can infer scan points
8603 if (auto _idx2 = varPart.Index("("); _idx2 > 0) {
8604 varName = varPart(0, _idx2);
8605 TStringToken pattern(TString(varPart(_idx2 + 1, varPart.Length() - _idx2 - 2)), ",");
8606 double min(0);
8607 double max(0);
8608 int nBins = 0;
8609 int ii = 0;
8610 while (pattern.NextToken()) {
8611 TString s = pattern;
8612 if (ii == 0) {
8613 nBins = s.Atoi();
8614 } else if (ii == 1) {
8615 min = s.Atof();
8616 } else if (ii == 2) {
8617 max = s.Atof();
8618 }
8619 ii++;
8620 }
8621 if (nBins > 100)
8622 nBins = 100; // limit scanning to 100 points
8623 if (nBins > 1) {
8624 for (double x = min; x <= max; x += (max - min) / (nBins - 1)) {
8625 xPoints.push_back(x);
8626 }
8627 } else if (nBins == 1)
8628 xPoints.push_back((min + max) / 2.);
8629 }
8630 v = getObject<RooAbsLValue>(varName.Data()).get();
8631 if (!v) {
8632 throw std::runtime_error(TString::Format("Could not find variable %s", varName.Data()));
8633 }
8634 if (xPoints.empty() && !obs().find(varName.Data()) &&
8635 dynamic_cast<RooAbsRealLValue *>(v)) { // will draw obs as regular (e.g. hist)
8636 double tmp = static_cast<RooAbsRealLValue *>(v)->getVal();
8637 for (int i = 0; i < v->numBins(GetName()); i++) {
8638 v->setBin(i, GetName());
8639 xPoints.push_back(static_cast<RooAbsRealLValue *>(v)->getVal());
8640 }
8641 static_cast<RooAbsRealLValue *>(v)->setVal(tmp);
8642 }
8643 sOpt2 = TString(sOpt2(0, _idx)) + sOpt2(_idx + 2 + varPart.Length() + 1, sOpt2.Length());
8644 sOpt = TString(sOpt(0, _idx)) + sOpt(_idx + 2 + varPart.Length() + 1, sOpt.Length());
8645 }
8646 TString forceNames = "";
8647 if (sOpt2.Contains("force")) {
8648 // force plots show how much NLL changes wrt to a change of variables
8649 if (get<RooRealVar>() && fParent && fParent->get<RooAbsPdf>()) {
8650 // assume want force of this parameter from the parent pdf
8651 TString ff = sOpt(sOpt2.Index("force"), sOpt2.Index("force") + 5);
8652 sOpt.ReplaceAll(ff, TString::Format("force%s", get()->GetName()));
8653 fParent->Draw(sOpt);
8654 return;
8655 } else if (get<RooAbsPdf>()) {
8656 // extract the parameter(s) to calculate force for
8657 forceNames = sOpt(sOpt2.Index("force") + 5, sOpt2.Length());
8658 sOpt = sOpt(0, sOpt2.Index("force"));
8659 sOpt2 = sOpt2(0, sOpt2.Index("force"));
8660 } else {
8661 Error("Draw", "Can only compute forces with PDFs");
8662 return; // don't throw because will cause browser to exit if done from there
8663 }
8664 }
8665 bool hasOverlay = sOpt2.Contains("overlay");
8666 TString overlayName = "";
8667 if (hasOverlay) {
8668 // whatever follows overlay is the variation name
8669 overlayName = sOpt(sOpt2.Index("overlay") + 7, sOpt2.Length());
8670 sOpt = sOpt(0, sOpt2.Index("overlay"));
8671 sOpt2 = sOpt2(0, sOpt2.Index("overlay"));
8672 }
8673 if (sOpt2.Contains("ratio") && !sOpt2.Contains("auxratio"))
8674 sOpt += "auxRatio";
8675 if (sOpt2.Contains("significance") && !sOpt2.Contains("auxsignif"))
8676 sOpt += "auxSignif";
8677
8678 std::string auxPlotTitle;
8679 for (auto &[k, _] : auxFunctions) {
8680 if (sOpt.Contains(TString::Format("aux%s", k.c_str()))) {
8681 auxPlotTitle = k;
8682 }
8683 sOpt.ReplaceAll(TString::Format("aux%s", k.c_str()), "");
8684 }
8685
8686 sOpt.ToLower();
8687 sOpt.ReplaceAll("ratio", "");
8688 sOpt.ReplaceAll("significance", ""); // remove old option if still given
8689 bool nostack = sOpt.Contains("nostack");
8690 sOpt.ReplaceAll("nostack", "");
8691 bool hasSame = sOpt.Contains("same");
8692 sOpt.ReplaceAll("same", "");
8693 bool hasGoff = sOpt.Contains("goff");
8694 sOpt.ReplaceAll("goff", "");
8695 bool hasFR = sOpt.Contains("pull") && !get<RooFitResult>();
8696 sOpt.ReplaceAll("pull", "");
8697 bool hasText = sOpt.Contains("text");
8698 bool hasTexte = sOpt.Contains("texte");
8699 bool hasErrorOpt = sOpt.Contains("e");
8700 sOpt.ReplaceAll("e", "");
8701 if (hasTexte) {
8702 sOpt.ReplaceAll("txt", "texte");
8703 } else if (hasText) {
8704 sOpt.ReplaceAll("txt", "text");
8705 }
8706 if (auxPlotTitle == "Signif")
8707 hasErrorOpt = true; // must calculate error to calculate significance
8708 if (hasOverlay)
8709 hasSame = true; // when overlaying must be putting on same
8710
8711 TVirtualPad *pad = gPad;
8712
8713 TH1 *hAxis = nullptr;
8714
8715 auto clearPad = []() {
8716 gPad->Clear();
8717 if (gPad->GetNumber() == 0) {
8718 gPad->SetBottomMargin(gStyle->GetPadBottomMargin());
8719 gPad->SetTopMargin(gStyle->GetPadTopMargin());
8720 gPad->SetLeftMargin(gStyle->GetPadLeftMargin());
8721 gPad->SetRightMargin(gStyle->GetPadRightMargin());
8722 }
8723 // if (gPad == gPad->GetCanvas()) {
8724 // gPad->GetCanvas()->SetCanvasSize( gPad->GetCanvas()->GetWindowWidth() - 4,
8725 // gPad->GetCanvas()->GetWindowHeight() - 28 );
8726 // }
8727 };
8728
8729 if (!hasSame || !pad) {
8730 if (!pad) {
8732 pad = gPad;
8733 }
8734
8735 } else {
8736 // get the histogram representing the axes
8737 hAxis = dynamic_cast<TH1 *>(pad->GetPrimitive("axis"));
8738 if (!hAxis) {
8739 for (auto o : *pad->GetListOfPrimitives()) {
8740 if (hAxis = dynamic_cast<TH1 *>(o); hAxis)
8741 break;
8742 }
8743 }
8744 if (hAxis && !v) {
8745 v = getObject<RooAbsLValue>(hAxis->GetXaxis()->IsAlphanumeric() ? hAxis->GetXaxis()->GetTimeFormatOnly()
8746 : hAxis->GetXaxis()->GetName())
8747 .get();
8748 }
8749 }
8750
8751 if (!hasSame) {
8752 if (gPad != gPad->GetCanvas()) {
8753 gPad->SetName(GetName()); // only rename the pad if its not the parent canvas
8754 }
8755 gPad->SetTitle(GetTitle());
8756 }
8757
8758 PadRefresher padRefresh(((!hasSame || hasOverlay || PadRefresher::nExisting == 0) && !hasGoff) ? gPad : nullptr);
8759
8760 auto adjustYRange = [&](double min, double max, TH1 *hh = nullptr, bool symmetrize = false) {
8761 if (!hh)
8762 hh = hAxis;
8763 // give max and min a buffer ...
8764 max += gStyle->GetHistTopMargin() * (max - min);
8765 if (min > 0)
8766 min = std::max(min * 0.9, min - gStyle->GetHistTopMargin() * (max - min));
8767 if (hh) {
8768 double ymin = hh->GetMinimum();
8769 double ymax = hh->GetMaximum();
8770 if (hh->GetMaximumStored() == -1111)
8771 ymax += gStyle->GetHistTopMargin() * (ymax - ymin);
8772 if (hh->GetMinimumStored() == -1111) {
8773 if (gStyle->GetHistMinimumZero() && ymax >= 0) {
8774 ymin = 0;
8775 } else if (ymin < 0) {
8776 ymin -= gStyle->GetHistTopMargin() * (ymax - ymin);
8777 } else {
8778 ymin = std::max(ymin * 0.9, ymin - gStyle->GetHistTopMargin() * (ymax - ymin));
8779 }
8780 // see TGLPlotPainter to complete the mimic, but we leave off here truncating @ 0 if ymax>0
8781 }
8782 // make ymax at least 3x bigger than biggest error if has error
8783 if (hh->GetSumw2()) {
8784 double smallestErrDown3 = -std::numeric_limits<double>::infinity();
8785 double smallestErrUp3 = std::numeric_limits<double>::infinity();
8786 for (int i = 1; i <= hh->GetNbinsX(); i++) {
8787 smallestErrDown3 = std::max(smallestErrDown3, hh->GetBinContent(i) - 3 * hh->GetBinError(i));
8788 smallestErrUp3 = std::min(smallestErrUp3, hh->GetBinContent(i) + 3 * hh->GetBinError(i));
8789 }
8790 max = std::max(max, smallestErrUp3);
8791 min = std::min(min, smallestErrDown3);
8792 }
8793 bool change = false;
8794 if (min < ymin) {
8795 ymin = min;
8796 change = true;
8797 }
8798 if (max > ymax) {
8799 ymax = max;
8800 change = true;
8801 }
8802 if (change) {
8803 // note: unfortunately when user 'unzooms' y axis it resets stored minimum to -1111, so lose range
8804 if (symmetrize) {
8805 double down = hh->GetBinContent(1) - ymin;
8806 double up = ymax - hh->GetBinContent(1);
8807 if (down > up) {
8808 ymax = hh->GetBinContent(1) + down;
8809 } else {
8810 ymin = hh->GetBinContent(1) - up;
8811 }
8812 }
8813 if (hh == hAxis && pad && !pad->GetLogy() && ymin > 0 && (log10(ymax) - log10(max)) >= 3) {
8814 // auto-log the pad
8815 pad->SetLogy();
8816 }
8817 if (hh == hAxis && pad && ymin == 0 && pad->GetLogy()) {
8818 ymin = 1e-2;
8819 }
8820 if (ymin == 0 && ymax > 10)
8821 ymin = 0.1; // adjust min so if user activates log scale it isn't bad
8822 hh->SetMinimum(ymin);
8823 hh->SetMaximum(ymax);
8824 hh->GetYaxis()->Set(1, ymin, ymax);
8825 hh->SetAxisRange(ymin, ymax, "Y");
8826 }
8827 }
8828 };
8829
8830 auto graphMinMax = [](TGraphAsymmErrors *gr) {
8831 double ymax = -std::numeric_limits<double>::infinity();
8832 double ymin = std::numeric_limits<double>::infinity();
8833 for (int i = 0; i < gr->GetN(); i++) {
8834 ymax = std::max(ymax, gr->GetPointY(i) + gr->GetErrorYhigh(i));
8835 ymin = std::min(ymin, gr->GetPointY(i) - gr->GetErrorYlow(i));
8836 }
8837 return std::make_pair(ymin, ymax);
8838 };
8839
8840 if (!xPoints.empty()) {
8841 // create a graph using GetContent
8843 out->SetName(GetName());
8844 out->SetTitle(GetTitle());
8845 out->SetFillColor(out->GetLineColor());
8846 out->SetMarkerStyle(0);
8847 out->SetFillStyle(hasErrorOpt ? 3005 : 0);
8848 double tmp = static_cast<RooAbsRealLValue *>(v)->getVal();
8849 for (auto &x : xPoints) {
8850 static_cast<RooAbsRealLValue *>(v)->setVal(x);
8851 out->AddPoint(x, GetContent());
8852 if (hasErrorOpt) {
8853 out->SetPointEYlow(out->GetN() - 1, GetError());
8854 out->SetPointEYhigh(out->GetN() - 1, out->GetErrorYlow(out->GetN() - 1)); // symmetric error for now
8855 }
8856 }
8857 static_cast<RooAbsRealLValue *>(v)->setVal(tmp);
8858 out->GetHistogram()->GetXaxis()->SetTitle(static_cast<RooAbsRealLValue *>(v)->GetTitle());
8859 out->SetBit(kCanDelete);
8860 out->Draw(TString(hasSame ? "L" : "AL") + (hasErrorOpt ? "3" : ""));
8861 return;
8862 }
8863
8864 if (hasFR) {
8865 // drawing the fitresult as a pull plot on a subpad, and rest of the draw elsewhere
8866 clearPad();
8867 pad->Divide(1, 2, 1e-9, 1e-9); //,0,0);
8868 pad->GetPad(1)->SetPad(0, 0.2, 1, 1);
8869 pad->GetPad(2)->SetPad(0, 0, 1, 0.2);
8870 TString optNoFR(opt);
8871 optNoFR.ReplaceAll("pull", "");
8872 pad->cd(1);
8873 Draw(optNoFR);
8874 pad->cd(2);
8875 auto _fr = fitResult();
8876 _fr.Draw();
8877 // switch into subpad
8878 gPad->cd(1);
8879 gPad->SetFillColor(kGray);
8880 gPad->GetFrame()->SetFillColor(kWhite);
8881 gPad->GetFrame()->SetFillStyle(1001);
8882 gPad->SetTopMargin(0);
8883 gPad->SetBottomMargin(0);
8884 gPad->SetName("pull");
8885 // split the pull graph into individual points -- for benefit of GUI status bar
8886 auto pullGraph = dynamic_cast<TGraphAsymmErrors *>(gPad->GetPrimitive("pulls"));
8887 if (!pullGraph) {
8888 Error("Draw", "Couldn't find pull graph");
8889 return;
8890 }
8891 pullGraph->SetName("nominal");
8892 TMultiGraph *mg = new TMultiGraph;
8893 mg->SetName("editables");
8894
8895 auto scaleHist = static_cast<TH1 *>(pullGraph->FindObject("scales"));
8896 if (!scaleHist)
8897 throw std::runtime_error("Could not find scales in fit result");
8898
8899 for (auto i = 0; i < pullGraph->GetN(); i++) {
8900 auto g = new TGraphAsymmErrors;
8901 g->SetName(scaleHist->GetXaxis()->GetBinLabel(i + 1));
8902 auto _p = dynamic_cast<RooRealVar *>(_fr.get<RooFitResult>()->floatParsFinal().find(g->GetName()));
8903 if (!_p) {
8904 Warning("Draw", "Found a non-var in the floatParsFinal list: %s - this shouldn't happen", g->GetName());
8905 continue;
8906 }
8907 g->SetTitle(TString::Format(
8908 "%s=%g +/- %s [%g,%g]", strlen(_p->GetTitle()) ? _p->GetTitle() : _p->GetName(), _p->getVal(),
8909 _p->hasAsymError() ? TString::Format("(%g,%g)", _p->getAsymErrorHi(), _p->getAsymErrorLo()).Data()
8910 : TString::Format("%g", _p->getError()).Data(),
8911 scaleHist->GetBinContent(i + 1), scaleHist->GetBinError(i + 1)));
8912 g->SetPoint(0, pullGraph->GetPointX(i), pullGraph->GetPointY(i));
8913 g->SetPointEYhigh(0, pullGraph->GetErrorYhigh(i));
8914 g->SetPointEYlow(0, pullGraph->GetErrorYlow(i));
8915 g->SetEditable(true);
8916 g->SetHighlight(true);
8917 g->SetMarkerStyle(20);
8918 g->SetMarkerSize(0.5);
8919 mg->Add(g);
8920 }
8921 // gPad->GetListOfPrimitives()->Remove(pullGraph); delete pullGraph;
8922 mg->Draw("z0p");
8923 mg->SetBit(kCanDelete);
8924 auto _thisClone = new xRooNode("node", fComp, fParent);
8925 _thisClone->SetBit(kCanDelete);
8926 _thisClone->AppendPad();
8927
8928 // ensure statusbar visible for interactive plot
8929 // turned this off for now ... as not needed if doing through browser, status bar already there
8930 // if (gPad->GetCanvas() && !gPad->GetCanvas()->TestBit(TCanvas::kShowEventStatus)) {
8931 // gPad->GetCanvas()->ToggleEventStatus();
8932 // }
8933 gPad->AddExec("interactivePull", TString::Format("%s::Interactive_Pull()", ClassName()));
8934
8935 pad->cd();
8936 return;
8937 }
8938
8939 if (auto _simPdf = get<RooSimultaneous>();
8940 _simPdf && !(v && strcmp(_simPdf->indexCat().GetName(), dynamic_cast<TObject *>(v)->GetName()) == 0)) {
8941 auto _channels = bins();
8942 int _size = 0;
8943 for (auto &_v : _channels) {
8944 if (!_v->IsHidden())
8945 _size++;
8946 }
8947 if (!hasSame) {
8948 if (_size > 2) {
8949 // add a pad for the common legends
8950 _size++;
8951 }
8952 clearPad();
8953 pad->SetBorderSize(0);
8954 // if (pad->GetCanvas() == pad) {
8955 // if(_size>4) {
8956 // int n = _size;
8957 // Int_t w = 1, h = 1;
8958 // if (pad->GetCanvas()->GetWindowWidth() > pad->GetCanvas()->GetWindowHeight()) {
8959 // w = std::ceil(std::sqrt(n));
8960 // h = std::floor(std::sqrt(n));
8961 // if (w*h < n) w++;
8962 // } else {
8963 // h = std::ceil(std::sqrt(n));
8964 // w = std::floor(std::sqrt(n));
8965 // if (w*h < n) h++;
8966 // }
8967 // // adjust the window size to display only 4 in the window, with scroll bars
8968 // pad->GetCanvas()->SetCanvasSize( w*((pad->GetCanvas()->GetWindowWidth()-4)/2.) -16
8969 // ,h*((pad->GetCanvas()->GetWindowHeight()-28)/2.) - 16 );
8970 // } else {
8971 // //pad->GetCanvas()->Set(
8972 // w*(pad->GetCanvas()->GetWindowWidth()/2.),h*(pad->GetCanvas()->GetWindowHeight()/2.)) )
8973 // }
8974 // }
8975 dynamic_cast<TPad *>(pad)->DivideSquare(_size, 1e-9, 1e-9);
8976 if (_size > 3) {
8977 auto _pad = pad->GetPad(_size); // will use as the legend pad
8978 _pad->SetName("legend");
8979 // stretch the pad all the way to the left
8980 _pad->SetPad(_pad->GetXlowNDC(), _pad->GetYlowNDC(), 1.0, _pad->GetYlowNDC() + _pad->GetHNDC());
8981 // and make all the remaining pads transparent
8982 int x = _size;
8983 while (pad->GetPad(x + 1)) {
8984 pad->GetPad(x + 1)->SetFillStyle(0);
8985 x++;
8986 }
8987 }
8988 }
8989 int i = 0;
8990 auto &chanVar = const_cast<RooAbsCategoryLValue &>(_simPdf->indexCat());
8991 // auto _idx = chanVar.getIndex();
8992 auto _range = GetRange();
8993 std::vector<TString> chanPatterns;
8994 if (_range && strlen(_range)) {
8995 TStringToken pattern(_range, ",");
8996 while (pattern.NextToken()) {
8997 chanPatterns.emplace_back(pattern);
8998 }
8999 }
9000 for (auto &_v : _channels) {
9001 if (_v->IsHidden())
9002 continue;
9003 TString s(_v->GetName());
9004 pad->cd(++i);
9005 gPad->SetName(s);
9006 TString cName = s(s.Index('=') + 1, s.Length());
9007 chanVar.setLabel(cName);
9008 bool inRange = chanPatterns.empty();
9009 for (auto &p : chanPatterns) {
9010 if (chanVar.inRange(p)) {
9011 inRange = true;
9012 break;
9013 }
9014 }
9015 if (!inRange || !_v->get<RooAbsReal>()->isSelectedComp())
9016 gPad->SetFillColor(kGray);
9017 if (!hasSame && _size > 1 && (gStyle->GetTitleFont("Y") % 10) == 3)
9018 gPad->SetLeftMargin(std::min(gPad->GetLeftMargin() * (1. / gPad->GetWNDC()), 0.3));
9019 _v->Draw(opt);
9021 }
9022 pad->cd(0);
9023 gPad->Modified();
9024 // gPad->Update();
9025 return;
9026 }
9027
9028 if (!get() || get<RooArgList>()) {
9029 // is a group draw all the submembers
9030 browse();
9031 int _size = 0;
9032 // int _size = _channels.size(); // size(); if (find("!.vars")) _size--;
9033 for (auto &_v : *this) {
9034 if (_v->IsHidden())
9035 continue;
9036 if (strcmp(GetName(), ".vars") == 0) {
9037 // auto hide obs and "1" and const var
9038 if (_v->get<RooAbsArg>()->getAttribute("obs"))
9039 continue;
9040 if (strcmp(_v->get()->GetName(), "1") == 0 || strcmp(_v->get()->GetName(), "ONE") == 0 ||
9041 TString(_v->get()->GetName()).BeginsWith("binWidth_"))
9042 continue;
9043 if (_v->get()->InheritsFrom("RooConstVar"))
9044 continue;
9045 }
9046 TString s(_v->GetName());
9047 if (s.BeginsWith(".") || s.BeginsWith("!"))
9048 continue;
9049 _size++;
9050 }
9051 if (!hasSame) {
9052 clearPad();
9053 pad->SetBorderSize(0);
9054 dynamic_cast<TPad *>(pad)->DivideSquare(_size, 1e-9, 1e-9);
9055 }
9056 int i = 0;
9057 for (auto &_v : *this) {
9058 if (_v->IsHidden())
9059 continue;
9060 if (strcmp(GetName(), ".vars") == 0) {
9061 // auto hide obs and "1" and const var
9062 if (_v->get<RooAbsArg>()->getAttribute("obs"))
9063 continue;
9064 if (strcmp(_v->get()->GetName(), "1") == 0 || strcmp(_v->get()->GetName(), "ONE") == 0 ||
9065 TString(_v->get()->GetName()).BeginsWith("binWidth_"))
9066 continue;
9067 if (_v->get()->InheritsFrom("RooConstVar"))
9068 continue;
9069 }
9070 TString s(_v->GetName());
9071 if (s.BeginsWith(".") || s.BeginsWith("!"))
9072 continue;
9073 pad->cd(++i);
9074 gPad->SetName(s);
9075 if (!hasSame && _size > 1 && (gStyle->GetTitleFont("Y") % 10) == 3)
9076 gPad->SetLeftMargin(std::min(gPad->GetLeftMargin() * (1. / gPad->GetWNDC()), 0.3));
9077 _v->Draw(opt);
9078 // pad->Modified();//pad->Update();
9080 }
9081 pad->cd(0);
9082 gPad->Modified();
9083 // gPad->Update();
9084 return;
9085 }
9086
9087 if (get()->InheritsFrom("RooProdPdf")) {
9088 // draw the main pdf ...
9089 mainChild().Draw(opt);
9090 gPad->SetName(GetName());
9091 return;
9092 }
9093
9094 if (auto fr = get<RooFitResult>(); fr) {
9095 if (sOpt.Contains("corr")) {
9096 // do correlation matrix
9097
9098 auto hist = fr->correlationHist(fr->GetName());
9099 hist->SetTitle(fr->GetTitle());
9100 hist->SetBit(kCanDelete);
9101 hist->Scale(100);
9102 hist->SetStats(false);
9103 hist->SetDirectory(nullptr);
9105 gStyle->SetPaintTextFormat(".1f");
9106 hist->GetXaxis()->SetTickSize(0);
9107 hist->GetYaxis()->SetTickSize(0);
9108 hist->SetMinimum(-100);
9109 hist->Draw(sOpt);
9111 gPad->SetGrid(1, 1);
9112 gPad->SetLogy(0);
9113 gPad->SetLogx(0);
9114 return;
9115 }
9116
9117 if (sOpt.Contains("brakdown")) { // e will have been removed above
9118
9119 // breakdown is quadrature difference between total error and conditional error
9120 // group by 'group' attribute
9121
9122 std::string poiName;
9123 if (sOpt.Contains("brakdown:")) {
9124 TString sOpt3(opt);
9125 poiName = sOpt3(sOpt3.Index("breakdown:") + 10, sOpt3.Length());
9126 } else {
9127 std::unique_ptr<RooAbsCollection> _poi(fr->floatParsFinal().selectByAttrib("poi", true));
9128 if (_poi->empty()) {
9129 throw std::runtime_error("No floating poi in the fit");
9130 } else if (_poi->size() != 1) {
9131 throw std::runtime_error("Multiple poi in the fit");
9132 }
9133 poiName = _poi->first()->GetName();
9134 }
9135 RooRealVar *poi = dynamic_cast<RooRealVar *>(fr->floatParsFinal().find(poiName.c_str()));
9136 if (!poi) {
9137 throw std::runtime_error(TString::Format("Cannot find parameter %s", poiName.c_str()));
9138 }
9139 std::set<std::string> groups;
9140 for (auto p : fr->floatParsFinal()) {
9141 if (p == poi) {
9142 continue;
9143 } else if (p->getStringAttribute("group")) {
9144 groups.insert(p->getStringAttribute("group"));
9145 } else {
9146 groups.insert(p->GetTitle());
9147 }
9148 }
9149
9150 auto roundedVal = xRooFit::matchPrecision(std::pair(poi->getVal(), poi->getError()));
9151
9152 TPie *pie = new TPie(TString::Format("breakdown:%s", poi->GetName()),
9153 TString::Format("%s: %g #pm %g", poi->GetTitle(), roundedVal.first, roundedVal.second),
9154 groups.size() + 1);
9155
9156 // for display of errors will go to one extra dp ...
9157 roundedVal.second *= .1;
9158
9159 // do breakdown by removing parameters in blocks according to groups and seeing impact on variance
9160 // this will give the correct sum but will be order-dependent if there are correlations between
9161 // groups. therefore we will stick with group-by-group
9162 // RooArgList pars(fr->floatParsFinal()); // pars to not condition on
9163 // double variance = pow(dynamic_cast<RooRealVar*>(poi)->getError(),2);
9164 int i = 0;
9165 for (auto group : groups) {
9166 RooArgList pars(fr->floatParsFinal()); // pars to not condition on
9167 double variance = pow(dynamic_cast<RooRealVar *>(poi)->getError(), 2);
9168 for (auto p : fr->floatParsFinal()) {
9169 if (p == poi) {
9170 continue;
9171 } else if ((p->getStringAttribute("group") && group == p->getStringAttribute("group")) ||
9172 (!p->getStringAttribute("group") && group == p->GetTitle())) {
9173 // conditioning on this parameter ... remove from pars list
9174 pars.remove(*p);
9175 }
9176 }
9177 int idx = pars.index(poiName.c_str());
9178 double reducedVar = fr->conditionalCovarianceMatrix(pars)(idx, idx);
9179 if (reducedVar > variance) {
9180 Warning("Draw", "breakdown group %s variance bigger than preceding?", group.c_str());
9181 pie->SetEntryVal(i, 0);
9182 pie->SetEntryLabel(i, TString::Format("%s: NaN", group.c_str()));
9183 } else {
9184 pie->SetEntryVal(i, variance - reducedVar);
9186 std::pair(sqrt(variance - reducedVar), roundedVal.second)); // r.first will be the rounded error
9187 if (r.first > 0) {
9188 pie->SetEntryLabel(i, TString::Format("%s: %g", group.c_str(), r.first));
9189 } else {
9190 pie->SetEntryLabel(i, group.c_str()); // suppress labels for negligible errors.
9191 }
9192 }
9194 // variance = reducedVar;
9195 i++;
9196 }
9197 // remaining variance is statistical=
9198 double variance = fr->conditionalCovarianceMatrix(*poi)(0, 0);
9199 auto r =
9200 xRooFit::matchPrecision(std::pair(sqrt(variance), roundedVal.second)); // r.first will be the rounded error
9201 pie->SetEntryVal(i, variance);
9202 pie->SetEntryLabel(i, TString::Format("stat: %g", r.first));
9204 pie->SetBit(kCanDelete);
9205 pie->SetRadius(0.17);
9207 pie->Draw("NOL");
9208 return;
9209 }
9210
9211 // plot pull or impact
9213 out->SetName(TString::Format("%s_pull", fr->GetName()));
9214 out->SetTitle("Fit Result Pulls");
9215 std::vector<TString> graphLabels;
9217 ugraph->SetName(TString::Format("%s_pull_unconstrained", fr->GetName()));
9218 ugraph->SetTitle("Fit Result Pulls");
9219 std::vector<TString> ugraphLabels;
9220 std::map<std::string, double> scale;
9221 std::map<std::string, double> offset;
9222 for (auto &p : fr->floatParsFinal()) {
9223 auto _v = dynamic_cast<RooRealVar *>(p);
9224 if (!_v)
9225 continue;
9226
9227 if (std::isnan(_v->getErrorHi()) || std::isnan(_v->getErrorLo())) {
9228 Warning("Draw", "%s error is invalid", _v->GetName());
9229 }
9230
9231 // need to get constraint mean and error parameters ....
9232 // look for normal gaussian and poisson cases
9233 double prefitError = 0;
9234 double prefitVal = 0;
9235 double customScale = 0;
9236 if (auto ip =
9237 dynamic_cast<RooRealVar *>(fr->floatParsInit().find(p->GetName()))) { // handles if no prefit available
9238 prefitError = ip->getError();
9239 prefitVal = ip->getVal();
9240 };
9241
9242 std::shared_ptr<xRooNode> pConstr;
9243 if (fParent && fParent->getObject<RooRealVar>(p->GetName())) {
9244 auto _vv = fParent->getObject<RooRealVar>(p->GetName());
9245 if (_vv->hasRange("pullScale")) {
9246 customScale = (_vv->getMax("pullScale") - _vv->getMin("pullScale")) / 2.;
9247 }
9248 auto _constr = xRooNode(_vv, *this).constraints();
9249 for (auto &c : _constr) {
9250 if (c->get<RooPoisson>() || c->get<RooGaussian>()) {
9251 // require parameter to be a direct server of the constraint pdf to count if its a gaussian
9252 bool isServer = true;
9253 if (c->get<RooGaussian>()) {
9254 isServer = false;
9255 for (auto s : c->get<RooAbsArg>()->servers()) {
9256 if (strcmp(s->GetName(), p->GetName()) == 0) {
9257 isServer = true;
9258 break;
9259 }
9260 }
9261 }
9262 if (isServer) {
9263 pConstr = c;
9264 break;
9265 }
9266 }
9267 }
9268 }
9269 if (pConstr) {
9270
9271 // there will be 3 deps, one will be this par, the other two are the mean and error (or error^2 in case of
9272 // poisson
9273
9274 // std::cout << p->GetName() << " extracted " << prefitVal << " " << prefitError << " from ";
9275 // pConstr->deps().Print();
9276 pConstr->browse();
9277 if (pConstr->get<RooPoisson>() && pConstr->find(".x")) {
9278 std::string xName = pConstr->find(".x")->get()->GetName();
9279 prefitVal = pConstr->find(".x")->get<RooAbsReal>()->getVal();
9280 for (auto &_d : pConstr->vars()) {
9281 if (strcmp(p->GetName(), _d->get()->GetName()) == 0)
9282 continue;
9283 if (xName == _d->get()->GetName())
9284 continue;
9285 if (_d->get<RooAbsReal>()->getVal())
9286 prefitError = _d->get<RooAbsReal>()->getVal();
9287 }
9288 // prefitVal will be the global observable value, need to divide that by tau
9289 prefitVal /= prefitError;
9290 // prefiterror will be tau ... need 1/sqrt(tau) for error
9291 prefitError = 1. / sqrt(prefitError);
9292 } else if (auto _g = pConstr->get<RooGaussian>(); _g) {
9293 prefitError =
9294 (pConstr->find(".sigma")) ? pConstr->find(".sigma")->get<RooAbsReal>()->getVal() : prefitError;
9295 prefitVal =
9296 (pConstr->find(".x")) ? pConstr->find(".x")->get<RooAbsReal>()->getVal() : 0; // usually the globs
9297 if (pConstr->find(".x") &&
9298 strcmp(p->GetName(), pConstr->find(".x")->get<RooAbsReal>()->GetName()) == 0) {
9299 // hybrid construction case,
9300 prefitVal = pConstr->find(".mean")->get<RooAbsReal>()->getVal();
9301 }
9302 }
9303
9304 if (customScale)
9305 prefitError = customScale;
9306 if (prefitError == 0) {
9307 Warning("Draw", "failed to determine prefit error of %s, using post-fit error", p->GetName());
9308 prefitError = _v->getError();
9309 }
9310 out->SetPoint(out->GetN(), out->GetN(), (_v->getVal() - prefitVal) / prefitError);
9311 out->SetPointError(out->GetN() - 1, 0, 0, (-_v->getErrorLo()) / prefitError,
9312 (_v->getErrorHi()) / prefitError);
9313 graphLabels.push_back(p->GetName());
9314 scale[p->GetName()] = prefitError;
9315 offset[p->GetName()] = prefitVal;
9316 } else if (!fParent) {
9317 // no parent to determine constraints from ... prefitError=0 will be the unconstrained ones
9318 if (customScale)
9319 prefitError = customScale;
9320 if (prefitError == 0) {
9321 // uses range of var
9322 prefitError = (std::max({_v->getMax() - _v->getVal(), _v->getVal() - _v->getMin(), 4.}) / 4);
9323 ugraph->SetPoint(ugraph->GetN(), ugraph->GetN(), (_v->getVal() - prefitVal) / prefitError);
9324 ugraph->SetPointError(ugraph->GetN() - 1, 0, 0, (-_v->getErrorLo()) / prefitError,
9325 (_v->getErrorHi()) / prefitError);
9326 ugraphLabels.push_back(p->GetName());
9327 } else {
9328 out->SetPoint(out->GetN(), out->GetN(), (_v->getVal() - prefitVal) / prefitError);
9329 out->SetPointError(out->GetN() - 1, 0, 0, (-_v->getErrorLo()) / prefitError,
9330 (_v->getErrorHi()) / prefitError);
9331 graphLabels.push_back(p->GetName());
9332 }
9333 scale[p->GetName()] = prefitError;
9334 offset[p->GetName()] = prefitVal;
9335
9336 } else {
9337 // unconstrained (or at least couldn't determine constraint) ... use par range if no prefit error
9338 if (customScale)
9339 prefitError = customScale;
9340 if (prefitError == 0) {
9341 prefitError = (std::max({_v->getMax() - _v->getVal(), _v->getVal() - _v->getMin(), 4.}) / 4);
9342 }
9343 ugraph->SetPoint(ugraph->GetN(), ugraph->GetN(), (_v->getVal() - prefitVal) / prefitError);
9344 ugraph->SetPointError(ugraph->GetN() - 1, 0, 0, (-_v->getErrorLo()) / prefitError,
9345 (_v->getErrorHi()) / prefitError);
9346 ugraphLabels.push_back(p->GetName());
9347 scale[p->GetName()] = prefitError;
9348 offset[p->GetName()] = prefitVal;
9349 }
9350 }
9351 auto graph = out;
9352
9353 // append ugraph points to end of graph
9354 for (int i = 0; i < ugraph->GetN(); i++)
9355 ugraph->SetPointX(i, i + graph->GetN());
9356 int nUnconstrained = ugraph->GetN();
9357 TList tmpList;
9358 tmpList.SetName("tmpList");
9359 tmpList.Add(ugraph);
9360 graph->Merge(&tmpList);
9361 tmpList.RemoveAll();
9362 delete ugraph;
9363 for (auto &l : ugraphLabels) {
9364 graphLabels.push_back(l);
9365 }
9366
9367 graph->SetBit(kCanDelete);
9368 graph->SetMarkerStyle(20);
9369 graph->SetMarkerSize(0.5);
9370
9371 graph->SetMaximum(4);
9372 graph->SetMinimum(-4);
9373
9374 bool doHorizontal =
9375 (!sOpt.Contains("impact") && sOpt.Contains("v")) || (sOpt.Contains("impact") && !sOpt.Contains("himpact"));
9376
9377 std::vector<std::pair<double, std::string>> covariances;
9378 /*double poiError = 0;*/ std::string poiName;
9379 double maxImpact = 0;
9380 if (sOpt.Contains("impact")) {
9381 if (sOpt.Contains("impact:")) {
9382 TString sOpt3(opt);
9383 poiName = sOpt3(sOpt3.Index("impact:") + 7, sOpt3.Length());
9384 } else {
9385 std::unique_ptr<RooAbsCollection> _poi(fr->floatParsFinal().selectByAttrib("poi", true));
9386 if (_poi->empty()) {
9387 throw std::runtime_error("No floating poi in the fit");
9388 } else if (_poi->size() != 1) {
9389 throw std::runtime_error("Multiple poi in the fit");
9390 }
9391 poiName = _poi->first()->GetName();
9392 }
9393 RooAbsArg *poi = fr->floatParsFinal().find(poiName.c_str());
9394 if (!poi) {
9395 throw std::runtime_error(TString::Format("Cannot find parameter %s", poiName.c_str()));
9396 }
9397 size_t poiIdx = fr->floatParsFinal().index(*poi);
9398 // put parameters in order of impact on the poi
9399
9400 // impact is regression coefficient * npError
9401 // relevant regression coefficient is cov / (npVariance)
9402 // i.e. DeltaX/sigmaX = [cov(X,Y)/(sigmaXsigmaY)]DeltaY/sigmaY
9403 // ... DeltaX = [cov(X,Y)/(sigmaY^2)]DeltaY
9404 // if DeltaY is just sigmaY then DeltaX = cov(X,Y)/sigmaY
9405
9406 for (auto &label : graphLabels) {
9407 covariances.emplace_back(fr->covarianceMatrix()(poiIdx, fr->floatParsFinal().index(label)) /
9408 dynamic_cast<RooRealVar *>(fr->floatParsFinal().find(label))->getError(),
9409 label);
9410 }
9411 std::sort(covariances.begin(), covariances.end(),
9412 [&](std::pair<double, std::string> i, std::pair<double, std::string> j) {
9413 return doHorizontal ? (std::abs(i.first) < std::abs(j.first))
9414 : (std::abs(i.first) > std::abs(j.first));
9415 });
9416
9417 TGraphAsymmErrors sortedGraph;
9418 std::vector<TString> sortedLabels;
9419 maxImpact = (doHorizontal) ? covariances.back().first
9420 : covariances.front().first; // note: max impact is likely to be self variance
9421 for (auto &c : covariances) {
9422 if (c.second == poi->GetName()) {
9423 // poiError = sqrt(c.first);
9424 continue; // skip self
9425 }
9426 c.first *= 4. / (maxImpact * 1.2);
9427 sortedLabels.push_back(c.second);
9428 size_t i = 0;
9429 for (; i < graphLabels.size(); i++) {
9430 if (graphLabels[i] == c.second) {
9431 break;
9432 }
9433 }
9434 sortedGraph.AddPoint(sortedGraph.GetN(), graph->GetPointY(i));
9435 sortedGraph.SetPointError(sortedGraph.GetN() - 1, 0, 0, graph->GetErrorYlow(i), graph->GetErrorYhigh(i));
9436 }
9437 graph->Set(0);
9438 TList tmpList2;
9439 tmpList2.SetName("tmpList");
9440 tmpList2.Add(&sortedGraph);
9441 graph->Merge(&tmpList2);
9442 tmpList2.RemoveAll();
9443 graphLabels = sortedLabels;
9444 graph->SetTitle("Fit Result Impact");
9445 }
9446
9447 // create a framing histogram
9448 TH2D *hist;
9449 if (doHorizontal) {
9450 hist = new TH2D(GetName(), fr->GetTitle(), 100, -4, 4, std::max(graph->GetN(), 1), -0.5,
9451 std::max(graph->GetN(), 1) - 0.5);
9452 int i = 1;
9453 for (auto &l : graphLabels) {
9454 hist->GetYaxis()->SetBinLabel(i++, l);
9455 }
9456 if (!graphLabels.empty())
9457 hist->GetYaxis()->LabelsOption("v");
9458 hist->GetXaxis()->SetTitle("(#hat{#theta}-#theta_{i})/#sigma_{i}");
9459 } else {
9460 hist = new TH2D(GetName(), fr->GetTitle(), std::max(graph->GetN(), 1), -0.5, std::max(graph->GetN(), 1) - 0.5,
9461 100, -4, 4);
9462 int i = 1;
9463 for (auto &l : graphLabels) {
9464 hist->GetXaxis()->SetBinLabel(i++, l);
9465 }
9466 if (!graphLabels.empty())
9467 hist->GetXaxis()->LabelsOption("v");
9468 hist->GetYaxis()->SetNdivisions(8, 0, 0);
9469 hist->GetYaxis()->SetTitle("(#hat{#theta}-#theta_{i})/#sigma_{i}");
9470 }
9471 hist->SetStats(false);
9472 hist->SetDirectory(nullptr);
9473 hist->SetBit(kCanDelete);
9474 auto histCopy = dynamic_cast<TH1 *>(hist->Clone(".axis"));
9475 histCopy->SetDirectory(nullptr);
9476 histCopy->SetBit(kCanDelete);
9477 auto _axis = (doHorizontal ? histCopy->GetYaxis() : histCopy->GetXaxis());
9478
9479 /*
9480 auto t = TH1::AddDirectoryStatus();
9481 TH1::AddDirectory(false);
9482 auto hist = new TH1F(TString::Format(".%s_pullFrame", GetName()), fr->GetTitle(), std::max(graph->GetN(),
9483 1), -0.5, std::max(graph->GetN(), 1) - 0.5); hist->SetStats(false); TH1::AddDirectory(t);
9484 hist->SetBit(kCanDelete);
9485 */
9486 // auto hist = graph->GetHistogram();
9487 graph->GetHistogram()->GetXaxis()->Set(std::max(graph->GetN(), 1), -0.5, std::max(graph->GetN(), 1) - 0.5);
9488 for (int ii = 1; ii <= _axis->GetNbins(); ii++) {
9489 graph->GetHistogram()->GetXaxis()->SetBinLabel(ii, _axis->GetBinLabel(ii));
9490 }
9491 // int i = 1;
9492 // for (auto &l : graphLabels) {
9493 // hist->GetXaxis()->SetBinLabel(i++, l);
9494 // }
9495 // hist->SetMaximum(4);
9496 // hist->SetMinimum(-4);
9497 // if (graph->GetN())
9498 // hist->GetXaxis()->LabelsOption("v");
9499 // hist->GetYaxis()->SetNdivisions(8, 0, 0);
9500 // hist->GetYaxis()->SetTitle("(#hat{#theta}-#theta_{i})/#sigma_{i}");
9501 clearPad();
9502 // create a new pad because adjust the margins ...
9503 auto oldPad = gPad;
9504 gPad->Divide(1, 1, 1e-9, 1e-9);
9505 gPad->cd(1);
9506
9507 if (doHorizontal) {
9508 gPad->SetLeftMargin(0.4);
9509 } else {
9510 gPad->SetBottomMargin(0.4);
9511 }
9512
9513 auto pNamesHist = dynamic_cast<TH1F *>(graph->GetHistogram()->Clone("scales")); // used by interactive "pull" plot
9514 pNamesHist->Sumw2();
9515 pNamesHist->SetDirectory(nullptr);
9516
9517 for (int ii = 1; ii <= graph->GetN(); ii++) { // use graph->GetN() to protect against the 0 pars case
9518 auto _p = fr->floatParsFinal().find(_axis->GetBinLabel(ii));
9519 pNamesHist->SetBinContent(ii, offset[_p->GetName()]);
9520 pNamesHist->SetBinError(ii, scale[_p->GetName()]);
9521 _axis->SetBinLabel(ii, strlen(_p->GetTitle()) ? _p->GetTitle() : _p->GetName());
9522 }
9523
9524 // hist->Draw(); -- now just draw the graph
9525
9526 if (!sOpt.Contains("impact")) {
9527 for (int ii = 2; ii >= 1; ii--) {
9528 auto pullBox = new TGraphErrors;
9529 pullBox->SetName(TString::Format("%dsigmaBand", ii));
9530 pullBox->SetBit(kCanDelete);
9531 pullBox->SetPoint(0, (doHorizontal) ? -ii : -0.5, (doHorizontal) ? -0.5 : 0);
9532 pullBox->SetPoint(1, (doHorizontal) ? ii : (_axis->GetNbins() - 0.5 - nUnconstrained),
9533 (doHorizontal) ? -0.5 : 0);
9534 pullBox->SetPointError(0, 0, (doHorizontal) ? (_axis->GetNbins() - nUnconstrained) : ii);
9535 pullBox->SetPointError(1, 0, (doHorizontal) ? (_axis->GetNbins() - nUnconstrained) : ii);
9536 pullBox->SetFillColor((ii == 2) ? kYellow : kGreen);
9537 hist->GetListOfFunctions()->Add(pullBox, "3"); // pullBox->Draw("3");
9538 }
9539 auto pullLine = new TGraph;
9540 pullLine->SetName("0sigmaLine");
9541 pullLine->SetBit(kCanDelete);
9542 pullLine->SetPoint(0, -0.5, 0);
9543 pullLine->SetPoint(1, _axis->GetNbins() - 0.5, 0);
9544 pullLine->SetLineStyle(2);
9545 pullLine->SetEditable(false);
9546 hist->GetListOfFunctions()->Add(pullLine, "l"); // pullLine->Draw("l");
9547
9548 // also draw vertical line separating constrained from unconstrained, if necessary
9549 if (nUnconstrained > 0) {
9550 pullLine = new TGraph;
9551 pullLine->SetName("dividerLine");
9552 pullLine->SetBit(kCanDelete);
9553 pullLine->SetPoint(0, graph->GetN() - 0.5 - nUnconstrained, -100);
9554 pullLine->SetPoint(1, graph->GetN() - 0.5 - nUnconstrained, 100);
9555 pullLine->SetLineStyle(2);
9556 pullLine->SetEditable(false);
9557 hist->GetListOfFunctions()->Add(pullLine, "l"); // pullLine->Draw("l");
9558 }
9559
9560 // and draw a pave with fr status info
9561 TPaveText *pave =
9562 new TPaveText(gPad->GetLeftMargin(), 1. - gPad->GetTopMargin(), 1. - gPad->GetRightMargin(), 0.98, "NDCNB");
9563 pave->SetFillStyle(0);
9564 pave->SetBorderSize(0);
9565 pave->SetMargin(0.);
9566 pave->SetName("status");
9567 pave->SetTextAlign(31);
9568 pave->AddText(TString::Format("minNLL: %g edm: %g", fr->minNll(), fr->edm()));
9569 std::string covQualTxt;
9570 switch (fr->covQual()) {
9571 case -1: covQualTxt = "Unknown"; break;
9572 case 0: covQualTxt = "Not calculated"; break;
9573 case 1: covQualTxt = "Approximate"; break;
9574 case 2: covQualTxt = "Forced Positive-Definite"; break;
9575 case 3: covQualTxt = "Accurate"; break;
9576 }
9577 pave->AddText(TString::Format("Cov. Quality: %d (%s)", fr->covQual(), covQualTxt.c_str()))
9578 ->SetTextColor((fr->covQual() == 3) ? kBlack : kRed);
9579
9580 std::string statusCodes;
9581 for (unsigned int i = 0; i < fr->numStatusHistory(); i++) {
9582 statusCodes += TString::Format(" %s = %d", fr->statusLabelHistory(i), fr->statusCodeHistory(i));
9583 }
9584 pave->AddText(statusCodes.c_str())->SetTextColor(fr->status() == 0 ? kBlack : kRed);
9585
9586 hist->GetListOfFunctions()->Add(pave);
9587
9588 } else {
9589 gPad->SetTicks(0, 0); // ensure mirrored ticks aren't drawn in this pad
9590
9591 if (doHorizontal) {
9592 // ensure canvas height big enough
9593 if (int(gPad->GetCanvas()->GetWh()) < pNamesHist->GetNbinsX() * 15) {
9594 gPad->GetCanvas()->SetCanvasSize(gPad->GetCanvas()->GetWw(), pNamesHist->GetNbinsX() * 15);
9595 }
9596 }
9597
9598 double factor = 475. / gPad->GetCanvas()->GetWh(); // Wh is the full canvas height, not window height
9599 gPad->SetTopMargin(gStyle->GetPadTopMargin() * factor); // fixed margin height
9600 gPad->SetBottomMargin(gStyle->GetPadBottomMargin() * factor); // fixed margin height
9601
9602 TGaxis *axis =
9603 new TGaxis(_axis->GetXmin(), -4, _axis->GetXmin(), 4, -1.2 * maxImpact, 1.2 * maxImpact, 510, "-S");
9604
9605 if (doHorizontal) {
9606 // _axis->SetLabelSize(
9607 // (_axis->GetLabelFont() % 10 > 2)
9608 // ? (20 / factor)
9609 // : ((gPad->AbsPixeltoY(0) - gPad->AbsPixeltoY(20 / factor)) / (gPad->GetY2() -
9610 // gPad->GetY1())));
9611 // histCopy->GetXaxis()->SetTickLength(histCopy->GetXaxis()->GetTickLength() * factor);
9612 // hist->GetXaxis()->SetTickLength(hist->GetXaxis()->GetTickLength() * factor);
9613 // histCopy->GetYaxis()->SetTickLength(histCopy->GetYaxis()->GetTickLength() * factor);
9614 // hist->GetYaxis()->SetTickLength(hist->GetYaxis()->GetTickLength() * factor);
9615 // histCopy->GetXaxis()->SetTitleOffset(histCopy->GetXaxis()->GetTitleOffset() * factor);
9616 // histCopy->GetXaxis()->SetLabelOffset(histCopy->GetXaxis()->GetLabelOffset() * factor);
9617 // hist->GetXaxis()->SetTitleOffset(hist->GetXaxis()->GetTitleOffset() * factor);
9618 // hist->GetXaxis()->SetLabelOffset(hist->GetXaxis()->GetLabelOffset() * factor);
9619 // histCopy->GetXaxis()->SetTitleOffset(histCopy->GetXaxis()->GetTitleOffset() * factor);
9620 // histCopy->GetXaxis()->SetLabelOffset(histCopy->GetXaxis()->GetLabelOffset() * factor);
9621 }
9622 // copy attributes from TAxis to TGaxis
9623 axis->ImportAxisAttributes((doHorizontal) ? histCopy->GetXaxis() : histCopy->GetYaxis());
9624 axis->SetTitle(TString::Format("#Delta %s", fr->floatParsFinal().find(poiName.c_str())->GetTitle()));
9625
9626 // create impact bar charts
9627 for (int tt = 0; tt < 2; tt++) {
9628 auto impact = static_cast<TH1 *>(
9629 graph->GetHistogram()->Clone(TString::Format("%s_impact+", tt == 0 ? "prefit" : "postfit")));
9630 impact->SetDirectory(nullptr);
9631 impact->GetYaxis()->SetTitle(TString::Format("#Delta%s/#sigma", poiName.c_str()));
9632 impact->SetBarWidth(0.9);
9633 impact->SetBarOffset(0.05);
9634 impact->SetLineColor(kBlack);
9635 impact->SetFillColor(kAzure - 4);
9636 impact->SetFillStyle(tt == 0 ? 3013 : 1001);
9637 auto impact2 =
9638 static_cast<TH1 *>(impact->Clone(TString::Format("%s_impact-", tt == 0 ? "prefit" : "postfit")));
9639 impact2->SetDirectory(nullptr);
9640 impact2->SetFillColor(kCyan);
9641 for (int ii = 1; ii <= pNamesHist->GetNbinsX(); ii++) {
9642 for (auto &c : covariances) {
9643 if (c.second != pNamesHist->GetXaxis()->GetBinLabel(ii))
9644 continue;
9645 auto vv = dynamic_cast<RooRealVar *>(fr->floatParsFinal().find(c.second.c_str()));
9646 auto vv_init = dynamic_cast<RooRealVar *>(fr->floatParsInit().find(c.second.c_str()));
9647 impact->SetBinContent(ii, ((tt == 0 && !vv_init->hasError()) || !vv->hasError())
9648 ? 0.
9649 : c.first * vv->getError() / vv->getErrorHi() *
9650 (tt == 0 ? (vv_init->getErrorHi() / vv->getErrorHi()) : 1.));
9651 impact2->SetBinContent(ii, ((tt == 0 && !vv_init->hasError()) || !vv->hasError())
9652 ? 0.
9653 : c.first * vv->getError() / vv->getErrorLo() *
9654 (tt == 0 ? (vv_init->getErrorLo() / vv->getErrorLo()) : 1.));
9655 }
9656 }
9657 hist->GetListOfFunctions()->Add(impact, (doHorizontal) ? "hbarsamemin0" : "bsamey+");
9658 hist->GetListOfFunctions()->Add(impact2, (doHorizontal) ? "hbarsamemin0" : "bsamey+");
9659 }
9660 // add three lines
9661 for (int ii = -1; ii <= 1; ii++) {
9662 auto pullLine = new TGraph;
9663 pullLine->SetName(TString::Format("%dsigmaLine", ii));
9664 pullLine->SetBit(kCanDelete);
9665 pullLine->SetPoint(0, -0.5, ii);
9666 pullLine->SetPoint(1, hist->GetNbinsY() - 0.5, ii);
9667 pullLine->SetLineStyle(2);
9668 pullLine->SetEditable(false);
9669 hist->GetListOfFunctions()->Add(pullLine, "l");
9670 }
9671 hist->GetListOfFunctions()->Add(axis); // draw axis last
9672 TLegend *leg1 =
9673 new TLegend(0.02, doHorizontal ? (1. - 0.22 * factor) : 0.02, 0.27, (doHorizontal ? 1. : 0.24));
9674 leg1->SetFillStyle(0);
9675 leg1->SetBorderSize(0);
9676 leg1->SetMargin(0.25);
9677 leg1->SetNColumns(2);
9678
9679 leg1->SetTextSize(_axis->GetLabelSize());
9680 leg1->SetTextFont(_axis->GetLabelFont());
9681 leg1->AddEntry((TObject *)nullptr, "Hessian Pre-fit", "");
9682 leg1->AddEntry((TObject *)nullptr, "Impact:", "");
9683 leg1->AddEntry(hist->FindObject("prefit_impact+"), "#theta = #hat{#theta}+#Delta#theta", "f");
9684 leg1->AddEntry(hist->FindObject("prefit_impact-"), "#theta = #hat{#theta}-#Delta#theta", "f");
9685 leg1->AddEntry((TObject *)nullptr, "Hessian Post-fit", "");
9686 leg1->AddEntry((TObject *)nullptr, "Impact:", "");
9687 leg1->AddEntry(hist->FindObject("postfit_impact+"), "#theta = #hat{#theta}+#Delta#theta", "f");
9688 leg1->AddEntry(hist->FindObject("postfit_impact-"), "#theta = #hat{#theta}-#Delta#theta", "f");
9689
9690 hist->GetListOfFunctions()->Add(leg1);
9691 if (gStyle->GetOptTitle()) {
9692 histCopy->SetBit(TH1::kNoTitle);
9693 TPaveText *title =
9694 new TPaveText(gPad->GetLeftMargin(), 1. - gPad->AbsPixeltoY(14), 1. - gPad->GetRightMargin(), 1., "NDC");
9695 title->ConvertNDCtoPad();
9696 title->SetY1NDC(1. - gPad->GetTopMargin() * 0.6);
9697 title->SetY2NDC(1);
9698 title->SetTextSize(
9699 (title->GetTextFont() % 10 > 2)
9700 ? (14 / factor)
9701 : ((gPad->AbsPixeltoY(0) - gPad->AbsPixeltoY(10 / factor)) / (gPad->GetY2() - gPad->GetY1())));
9702 title->SetFillStyle(0);
9703 title->SetBorderSize(0);
9704 title->AddText(histCopy->GetTitle());
9705 hist->GetListOfFunctions()->Add(title);
9706 }
9707 }
9708
9709 graph->SetEditable(false);
9710 pNamesHist->SetLineWidth(0);
9711 pNamesHist->SetMarkerSize(0);
9712 graph->GetListOfFunctions()->Add(pNamesHist, "same"); // graph->SetHistogram(pNamesHist);
9713 if (doHorizontal) {
9714
9715 // flip the graph and contained graphs
9716 for (int p = 0; p < graph->GetN(); p++) {
9717 graph->SetPoint(p, graph->GetPointY(p), graph->GetPointX(p));
9718 graph->SetPointError(p, graph->GetErrorYlow(p), graph->GetErrorYhigh(p), graph->GetErrorXlow(p),
9719 graph->GetErrorXhigh(p));
9720 }
9721 for (auto f : *hist->GetListOfFunctions()) {
9722 if (f->InheritsFrom("TH1")) {
9723 // f->Draw("hbarsamemin0");
9724 } /*else if (auto g2 = dynamic_cast<TGraphErrors *>(f)) {
9725 for (int p = 0; p < g2->GetN(); p++) {
9726 g2->SetPoint(p, g2->GetPointY(p), g2->GetPointX(p));
9727 g2->SetPointError(p, g2->GetErrorY(p), _axis->GetNbins());
9728 }
9729 //g2->Draw("3");
9730 } */
9731 else if (auto g = dynamic_cast<TGraph *>(f)) {
9732 for (int p = 0; p < g->GetN(); p++) {
9733 g->SetPoint(p, g->GetPointY(p), g->GetPointX(p));
9734 }
9735 // g->Draw("l");
9736 } else if (auto l = dynamic_cast<TLine *>(f)) {
9737 l->SetX1(l->GetY1());
9738 l->SetX2(l->GetY2());
9739 l->SetY1(_axis->GetXmax());
9740 l->SetY2(_axis->GetXmax());
9741 // l->Draw();
9742 }
9743 }
9744 }
9745
9746 graph->SetName("pulls");
9747 hist->GetListOfFunctions()->Add(graph, "z0p");
9748 // hist->GetListOfFunctions()->Add(histCopy->Clone(".axis"),(sOpt.Contains("impact") &&
9749 // !doHorizontal)?"axissamey+":"axissame"); // doesn't display right when zoom the axis
9750 if (!hasSame) {
9751 histCopy->Draw((sOpt.Contains("impact") && !doHorizontal)
9752 ? "axisy+"
9753 : "axis"); // draws the axis, called ".axis" for easy access
9754 }
9755 hist->Draw("same");
9756 //
9757 // if(sOpt.Contains("impact")) {
9758 // // make main object the histogram
9759 // auto h = (TH1*)graph->GetHistogram()->Clone("impact");
9760 // graph->GetListOfFunctions()->RemoveAll();
9761 // for(int ii=1;ii<=h->GetNbinsX();ii++) h->SetBinContent(ii,-4);
9762 // h->GetListOfFunctions()->Add(graph,"z0p");
9763 // h->Draw("hbar");
9764 // } else {
9765 // graph->Draw(sOpt.Contains("impact") ? "az0py+" : "az0p");
9766 // }
9767 auto hh = dynamic_cast<TH1 *>(histCopy->Clone(".axiscopy"));
9768 hh->SetDirectory(nullptr);
9769 hh->SetBit(kCanDelete);
9770 hh->Draw(
9771 (sOpt.Contains("impact") && !doHorizontal)
9772 ? "axissamey+"
9773 : "axissame"); // overlay axis again -- important is last so can remove if don't pad->Update before reclear
9774 gPad->Modified();
9775 oldPad->cd();
9776 // gPad->Update();
9777 return;
9778 }
9779
9780 if (get()->InheritsFrom("RooAbsData")) {
9781 auto s = parentPdf();
9782 if (s && s->get<RooSimultaneous>()) {
9783 // drawing dataset associated to a simultaneous means must find subpads with variation names
9784 // may not have subpads if drawning a "Yield" plot ...
9785 bool doneDraw = false;
9786 for (auto c : s->bins()) {
9787 auto _pad = dynamic_cast<TPad *>(gPad->GetPrimitive(c->GetName()));
9788 if (!_pad)
9789 continue; // channel was hidden?
9790 // attach as a child before calling datasets(), so that if this dataset is external to workspace it is
9791 // included still attaching the dataset ensures dataset reduction for the channel is applied
9792 c->push_back(std::make_shared<xRooNode>(*this));
9793 auto ds = c->datasets().find(GetName());
9794 c->resize(c->size() - 1); // remove the child we attached
9795 if (!ds) {
9796 std::cout << " no ds " << GetName() << " - this should never happen!" << std::endl;
9797 continue;
9798 }
9799 auto tmp = gPad;
9800 _pad->cd();
9801 ds->Draw(opt);
9802 doneDraw = true;
9803 tmp->cd();
9804 }
9805 if (doneDraw) {
9806 gPad->Modified();
9807 return;
9808 }
9809 }
9810
9811 if (!s && hasSame) {
9812 // draw onto all subpads with = in the name
9813 // if has no such subpads, draw onto this pad
9814 bool doneDraw = false;
9815 for (auto o : *gPad->GetListOfPrimitives()) {
9816 if (auto p = dynamic_cast<TPad *>(o); p && TString(p->GetName()).Contains('=')) {
9817 auto _tmp = gPad;
9818 p->cd();
9819 Draw(opt);
9820 _tmp->cd();
9821 doneDraw = true;
9822 }
9823 }
9824 if (doneDraw) {
9825 gPad->Modified();
9826 return;
9827 }
9828 }
9829
9830 auto dataGraph = BuildGraph(v, false, (!s && hasSame) ? gPad : nullptr);
9831 if (!dataGraph)
9832 return;
9833
9834 dataGraph->SetBit(kCanDelete); // will be be deleted when pad is cleared
9835 dataGraph->SetMarkerSize(dataGraph->GetMarkerSize() * gPad->GetWNDC()); // scale marker sizes to pad size
9836
9837 if (s && !s->get<RooAbsPdf>()->canBeExtended()) {
9838 // normalize dataGraph to 1
9839 double tot = 0;
9840 for (int i = 0; i < dataGraph->GetN(); i++)
9841 tot += dataGraph->GetPointY(i);
9842 dataGraph->Scale(1. / tot);
9843 }
9844
9845 if (!hasSame) {
9846 clearPad();
9847 dataGraph->Draw("Az0p");
9848 addLegendEntry(dataGraph, strlen(dataGraph->GetTitle()) ? dataGraph->GetTitle() : GetName(), "pEX0");
9849 gPad->Modified();
9850 // gPad->Update();
9851 return;
9852 }
9853
9854 bool noPoint = false;
9855 if (v && dynamic_cast<RooAbsArg *>(v)->getAttribute("global") && dataGraph->GetN() == 1) {
9856 // global observable ... if graph has only 1 data point line it up on the histogram value
9857 for (auto o : *gPad->GetListOfPrimitives()) {
9858 if (auto h = dynamic_cast<TH1 *>(o);
9859 h && strcmp(h->GetXaxis()->GetName(), dynamic_cast<TObject *>(v)->GetName()) == 0) {
9860 dataGraph->SetPointY(0, h->Interpolate(dataGraph->GetPointX(0)));
9861 noPoint = true;
9862 break;
9863 }
9864 }
9865 }
9866
9867 if (auto _pad = dynamic_cast<TPad *>(gPad->FindObject("auxPad")); _pad) {
9868 if (auto h = dynamic_cast<TH1 *>(_pad->GetPrimitive("auxHist")); h) {
9869 TString histName = h->GetTitle(); // split it by | char
9870 TString histType = histName(histName.Index('|') + 1, histName.Length());
9871 histName = histName(0, histName.Index('|'));
9872 if (auto mainHist = dynamic_cast<TH1 *>(gPad->GetPrimitive(histName));
9873 mainHist && auxFunctions.find(h->GetYaxis()->GetTitle()) != auxFunctions.end()) {
9874 // decide what to do based on title of auxHist (previously used name of y-axis but that changed axis
9875 // behaviour) use title instead
9876 auto ratioGraph = dynamic_cast<TGraphAsymmErrors *>(dataGraph->Clone(dataGraph->GetName()));
9877 ratioGraph->SetBit(kCanDelete);
9878 for (int i = 0; i < ratioGraph->GetN(); i++) {
9879 double val = ratioGraph->GetPointY(i);
9880 int binNum = mainHist->FindFixBin(ratioGraph->GetPointX(i));
9881 double nom = mainHist->GetBinContent(binNum);
9882 double nomerr = mainHist->GetBinError(binNum);
9883 double yval =
9884 std::get<0>(auxFunctions[h->GetYaxis()->GetTitle()])(ratioGraph->GetPointY(i), nom, nomerr);
9885 double yup = std::get<0>(auxFunctions[h->GetYaxis()->GetTitle()])(val + ratioGraph->GetErrorYhigh(i),
9886 nom, nomerr) -
9887 yval;
9888 double ydown = yval - std::get<0>(auxFunctions[h->GetYaxis()->GetTitle()])(
9889 val - ratioGraph->GetErrorYlow(i), nom, nomerr);
9890 if (!std::isnan(yval)) {
9891 ratioGraph->SetPointY(i, yval);
9892 if (!std::isnan(yup))
9893 ratioGraph->SetPointEYhigh(i, yup);
9894 if (!std::isnan(ydown))
9895 ratioGraph->SetPointEYlow(i, ydown);
9896 }
9897 }
9898 // remove the zero points
9899 int i = 0;
9900 while (i < ratioGraph->GetN()) {
9901 if (ratioGraph->GetPointY(i) == 0 && ratioGraph->GetErrorYhigh(i) == 0 &&
9902 ratioGraph->GetErrorYlow(i) == 0) {
9903 ratioGraph->RemovePoint(i);
9904 } else {
9905 i++;
9906 }
9907 }
9908 auto _tmpPad = gPad;
9909 _pad->cd();
9910 ratioGraph->Draw("z0psame");
9911 auto minMax = graphMinMax(ratioGraph);
9912 adjustYRange(minMax.first, minMax.second, h, std::get<1>(auxFunctions[h->GetYaxis()->GetTitle()]));
9913 _tmpPad->cd();
9914 }
9915 }
9916 }
9917
9918 dataGraph->Draw("z0p same");
9919 addLegendEntry((noPoint) ? nullptr : dataGraph, strlen(dataGraph->GetTitle()) ? dataGraph->GetTitle() : GetName(),
9920 noPoint ? "" : "pEX0");
9921
9922 auto minMax = graphMinMax(dynamic_cast<TGraphAsymmErrors *>(dataGraph));
9923 adjustYRange(minMax.first, minMax.second);
9924
9925 gPad->Modified();
9926 // gPad->Update();
9927 return;
9928 }
9929
9930 // auto _ax = GetXaxis();
9931 // auto v = (_ax) ? dynamic_cast<RooRealVar*>(/*possibleObs.first()*/_ax->GetParent()) : nullptr;
9932 // if (!v) { v = get<RooRealVar>(); } // self-axis
9933 // if (!v) return;
9934
9935 if (auto lv = get<RooAbsLValue>(); lv && fParent && fParent->get<RooAbsData>()) {
9936 // drawing an observable from a dataset ... build graph, and exit
9937 auto gr = fParent->BuildGraph(lv, true);
9939 gr->Draw(hasSame ? "P" : "AP");
9940 return;
9941 }
9942
9943 if (forceNames != "") {
9944 // drawing a force plot ... build nll and fill a histogram with force terms
9945 auto _dsets = datasets();
9946 bool _drawn = false;
9947 auto _coords = coords();
9948 auto _fr = fitResult();
9949 auto initPar = dynamic_cast<RooRealVar *>(_fr.get<RooFitResult>()->floatParsInit().find(forceNames));
9950 if (!initPar)
9951 return;
9952 std::vector<double> valuesToDo = {initPar->getVal()};
9953 if (initPar->hasError() || initPar->hasAsymError()) {
9954 valuesToDo.push_back(initPar->getVal() + initPar->getErrorLo());
9955 valuesToDo.push_back(initPar->getVal() + initPar->getErrorHi());
9956 }
9957 int ii = 0;
9958 for (auto valueToDo : valuesToDo) {
9959 ii++;
9960 for (auto &d : _dsets) {
9961 if (!d->get()->TestBit(1 << 20))
9962 continue;
9963 auto emptyHist = BuildHistogram(v, true);
9964 emptyHist->SetBit(kCanDelete);
9965 auto _obs = d->obs();
9966 auto x = _obs.find((v) ? dynamic_cast<TObject *>(v)->GetName() : emptyHist->GetXaxis()->GetName());
9967 auto _nll = nll(d);
9968 auto theData = d->get<RooAbsData>();
9969 int nevent = theData->numEntries();
9970 for (int i = 0; i < nevent; i++) {
9971 theData->get(i);
9972 bool _skip = false;
9973 for (const auto &_c : _coords) {
9974 if (auto cat = _c->get<RooAbsCategoryLValue>(); cat) {
9975 if (cat->getIndex() != theData->get()->getCatIndex(cat->GetName())) {
9976 _skip = true;
9977 break;
9978 }
9979 }
9980 }
9981 if (_skip)
9982 continue;
9983
9984 if (x) {
9985 auto val = _nll.pars()->getRealValue(initPar->GetName());
9986 if (ii > 1)
9987 _nll.pars()->setRealValue(initPar->GetName(), valueToDo);
9988 auto nllVal = _nll.getEntryVal(i);
9989 _nll.pars()->setRealValue(initPar->GetName(), initPar->getVal());
9990 auto nllVal2 = _nll.getEntryVal(i);
9991 _nll.pars()->setRealValue(initPar->GetName(), val);
9992 emptyHist->Fill(x->get<RooAbsReal>()->getVal(), (nllVal2 - nllVal));
9993 }
9994 }
9995 // include the extendedTerm, distributed evenly over the bins
9996 // probably should be somehow dependent on data density though (i.e. bins with more data get more of it?)
9997 auto val = _nll.pars()->getRealValue(initPar->GetName());
9998 if (ii > 1)
9999 _nll.pars()->setRealValue(initPar->GetName(), valueToDo);
10000 auto _extTerm = _nll.extendedTerm();
10001 _nll.pars()->setRealValue(initPar->GetName(), initPar->getVal());
10002 auto _extTerm2 = _nll.extendedTerm();
10003 _nll.pars()->setRealValue(initPar->GetName(), val);
10004 for (int i = 1; i <= emptyHist->GetNbinsX(); i++) {
10005 emptyHist->SetBinContent(i,
10006 emptyHist->GetBinContent(i) + (_extTerm2 - _extTerm) / emptyHist->GetNbinsX());
10007 emptyHist->SetBinError(i, 0);
10008 }
10009 emptyHist->GetYaxis()->SetTitle("log (L(#theta)/L(#theta_{0}))");
10010 emptyHist->SetTitle(TString::Format("#theta = %g", (ii > 1) ? valueToDo : val));
10011 if (ii == 1)
10012 emptyHist->SetLineColor(kBlack);
10013 if (ii == 2) {
10014 emptyHist->SetLineColor(kRed);
10015 } else if (ii == 3) {
10016 emptyHist->SetLineColor(kBlue);
10017 }
10018 emptyHist->Draw(_drawn ? "same" : "");
10019 _drawn = true;
10020 }
10021 }
10022 return;
10023 }
10024
10025 auto rar = get<RooAbsReal>();
10026 const xRooNode *rarNode = this;
10027 if (!rar) {
10028 get()->Draw();
10029 return;
10030 }
10031 RooAbsReal *sf = nullptr;
10032 if (get()->InheritsFrom("RooExtendPdf")) {
10033 browse();
10034 rarNode = find(".pdf").get();
10035 // rar = rarNode->get<RooAbsReal>();
10036 sf = find(".n")->get<RooAbsReal>();
10037 }
10038
10039 auto h = BuildHistogram(v, false, hasErrorOpt);
10040 if (!h)
10041 return;
10042 h->SetBit(kCanDelete);
10043
10044 if (!v)
10045 v = getObject<RooAbsLValue>(h->GetXaxis()->IsAlphanumeric() ? h->GetXaxis()->GetTimeFormatOnly()
10046 : h->GetXaxis()->GetName())
10047 .get();
10048 RooAbsArg *vv = (v) ? dynamic_cast<RooAbsArg *>(v) : rar;
10049 if (h->GetXaxis()->IsAlphanumeric()) {
10050 // do this to get bin labels
10051 h->GetXaxis()->SetName("xaxis"); // WARNING -- this messes up anywhere we GetXaxis()->GetName()
10052 }
10053
10054 if (rar->InheritsFrom("RooAbsPdf") && !(rar->InheritsFrom("RooRealSumPdf") || rar->InheritsFrom("RooAddPdf") ||
10055 rar->InheritsFrom("RooSimultaneous"))) {
10056 // append parameter values to title if has such
10057 RooArgSet s;
10058 rar->leafNodeServerList(&s);
10059 if (v)
10060 s.remove(*dynamic_cast<RooAbsArg *>(v));
10061 if (!s.empty()) {
10062 TString ss = h->GetTitle();
10063 ss += " [";
10064 bool first = true;
10065 for (auto _p : s) {
10066 auto _v = dynamic_cast<RooRealVar *>(_p);
10067 if (!_v)
10068 continue;
10069 if (!first)
10070 ss += ",";
10071 first = false;
10072 ss += TString::Format("%s=%g", strlen(_p->GetTitle()) ? _p->GetTitle() : _p->GetName(), _v->getVal());
10073 if (_v->hasError()) {
10074 ss += TString::Format("#pm %g", _v->getError());
10075 }
10076 }
10077 ss += "]";
10078 h->SetTitle(ss);
10079 }
10080 }
10081
10082 if (!hasSame) {
10083 if (obs().find(vv->GetName())) {
10084 gPad->SetGrid(0, 0);
10085 } else {
10086 gPad->SetGrid(1, 1);
10087 }
10088 }
10089 // need to strip namespace to discount the "HistFactory" namespace classes from all being treated as binned
10090 TString clNameNoNamespace = rar->ClassName();
10091 clNameNoNamespace = clNameNoNamespace(clNameNoNamespace.Last(':') + 1, clNameNoNamespace.Length());
10092 TString dOpt = (clNameNoNamespace.Contains("Hist") || vv->isCategory() || rar->isBinnedDistribution(*vv) ||
10093 h->GetNbinsX() == 1 || rar->getAttribute("BinnedLikelihood") ||
10094 (dynamic_cast<RooAbsRealLValue *>(vv) &&
10095 std::unique_ptr<std::list<double>>(rar->binBoundaries(*dynamic_cast<RooAbsRealLValue *>(vv),
10096 -std::numeric_limits<double>::infinity(),
10097 std::numeric_limits<double>::infinity()))))
10098 ? ""
10099 : "LF2";
10100 if (auto d = dynamic_cast<RooHistFunc *>(rar); d && !d->isBinnedDistribution(*vv) && h->GetNbinsX() != 1) {
10101 dOpt = "LF2"; // hist func is interpolated, so draw it as such
10102 }
10103 if (dOpt == "LF2" && !components().empty()) {
10104 // check if all components of dOpt are "Hist" type (CMS model support)
10105 // if so then dOpt="";
10106 bool allHist = true;
10107 for (auto &s : components()) {
10108 TString _clName = s->get()->ClassName();
10109 _clName = _clName(_clName.Last(':') + 1, _clName.Length());
10110 if (!(s->get() && _clName.Contains("Hist"))) {
10111 allHist = false;
10112 break;
10113 }
10114 }
10115 if (allHist)
10116 dOpt = "";
10117 }
10118
10119 if (rar == vv && rar->IsA() == RooRealVar::Class()) {
10120 dOpt += "TEXT";
10121 // add a TExec to the histogram so that when edited it will propagate to var
10122 gROOT->SetEditHistograms(true);
10123 } else {
10124 gROOT->SetEditHistograms(false);
10125 }
10126
10127 if (hasSame) {
10128 dOpt += " same";
10129 } else {
10130 hAxis = h;
10131 }
10132
10133 if (dOpt.Contains("TEXT") || sOpt.Contains("text")) {
10134 // adjust marker size so text is good
10135 h->SetMarkerSize(gStyle->GetLabelSize("Z") / (0.02 * gPad->GetHNDC()));
10136 }
10137
10138 bool hasError(false);
10139 for (int i = 0; i < h->GetSumw2N(); i++) {
10140 if (h->GetSumw2()->At(i)) {
10141 hasError = true;
10142 break;
10143 }
10144 }
10145
10146 /** This doesn't seem necessary in at least 6.26 any more - pads seem adjusted on their own
10147 if (!hasSame && h->GetYaxis()->GetTitleFont()%10 == 2) {
10148 h->GetYaxis()->SetTitleOffset( gPad->GetLeftMargin() / gStyle->GetPadLeftMargin() );
10149 } */
10150 // don't this instead - dont want to leave as zero (auto) in case show aux plot
10151 if (!hasSame && h->GetYaxis()->GetTitleFont() % 10 == 2) {
10152 h->GetYaxis()->SetTitleOffset(1.);
10153 }
10154
10155 TH1 *errHist = nullptr;
10156 if (hasError) {
10157 h->SetFillStyle(hasError ? 3005 : 0);
10158 h->SetFillColor(h->GetLineColor());
10159 h->SetMarkerStyle(0);
10160 errHist = dynamic_cast<TH1 *>(h->Clone(Form("%s_err", h->GetName())));
10161 errHist->SetBit(kCanDelete);
10162 errHist->SetDirectory(nullptr);
10163 h->SetFillStyle(0);
10164 for (int i = 1; i <= h->GetNbinsX(); i++) {
10165 h->SetBinError(i, 0);
10166 }
10167 }
10168
10169 if (!hasSame)
10170 clearPad();
10171
10172 if (rar == vv && rar->IsA() == RooRealVar::Class()) {
10173 // add a TExec to the histogram so that when edited it will propagate to var
10174 // h->GetListOfFunctions()->Add(h->Clone("self"),"TEXTHIST");
10175 dOpt = "TEXT";
10176 auto node = new xRooNode(*this);
10177 auto _hist = (errHist) ? errHist : h;
10178 auto hCopy = (errHist) ? nullptr : dynamic_cast<TH1 *>(h->Clone());
10179 if (hCopy)
10180 hCopy->SetDirectory(nullptr);
10181 _hist->GetListOfFunctions()->Add(node);
10182 _hist->GetListOfFunctions()->Add(new TExec(
10183 ".update",
10185 "gROOT->SetEditHistograms(true);auto h = dynamic_cast<TH1*>(gPad->GetPrimitive(\"%s\")); if(h) { double "
10186 "range= h->GetMaximum()-h->GetMinimum(); if(auto n "
10187 "= dynamic_cast<xRooNode*>(h->GetListOfFunctions()->FindObject(\"%s\")); n && "
10188 "n->TestBit(TObject::kNotDeleted) && n->get<RooRealVar>()->getVal() != h->GetBinContent(1)) {"
10189 "h->SetBinContent(1, "
10190 "TString::Format(\"%%.2g\",int(h->GetBinContent(1)/(range*0.01))*range*0.01).Atof());n->SetContent( "
10191 "h->GetBinContent(1) ); for(auto pp : *h->GetListOfFunctions()) if(auto hh = "
10192 "dynamic_cast<TH1*>(pp))hh->SetBinContent(1,h->GetBinContent(1));} if(h->GetBinContent(1)==0.) "
10193 "h->SetBinContent(1,range*0.005); gPad->Modified();gPad->Update(); }",
10194 _hist->GetName(), node->GetName())));
10195 if (errHist) {
10196 errHist->GetListOfFunctions()->Add(h, "TEXT HIST same");
10197 errHist->SetFillColor(h->GetLineColor());
10198 } else {
10199 hCopy->SetBit(kCanDelete);
10200 _hist->GetListOfFunctions()->Add(hCopy, "TEXT HIST same");
10201 _hist->SetBinError(1, 0);
10202 }
10203 _hist->SetStats(false);
10204 // if (_hist->GetBinContent(1)==0.) _hist->SetBinContent(1,(_hist->GetMaximum()-_hist->GetMinimum())*0.005);
10205 _hist->Draw(((errHist) ? "e2" : ""));
10206 gPad->Modified();
10207 return;
10208 }
10209
10210 bool overlayExisted = false;
10211 if (hasOverlay) {
10212 h->SetName(TString::Format("%s%s", h->GetName(), overlayName.Data()));
10213 if (auto existing = dynamic_cast<TH1 *>(gPad->GetPrimitive(h->GetName())); existing) {
10214 existing->Reset();
10215 existing->Add(h);
10216 delete h;
10217 h = existing;
10218 overlayExisted = true;
10219 } else {
10220 TString oldStyle = (rar && rar->getStringAttribute("style")) ? rar->getStringAttribute("style") : "";
10221 h->SetTitle(overlayName);
10222 // for overlays will take style from current gStyle before overriding with personal style
10223 // this ensures initial style will be whatever gStyle is, rather than whatever ours is
10224 (TAttLine &)(*h) = *gStyle;
10225
10226 // std::shared_ptr<TStyle> style; // use to keep alive for access from GetStyle below, in case
10227 // getObject has decided to return the owning ptr (for some reason) if
10228 // (!gROOT->GetStyle(h->GetTitle())) {
10229 // if ( (style = getObject<TStyle>(h->GetTitle())) ) {
10230 // // loaded style (from workspace?) so put in list and use that
10231 // gROOT->GetListOfStyles()->Add(style.get());
10232 // } else {
10233 // // create new style - gets put in style list automatically so don't have to delete
10234 // // acquire them so saved to workspaces for auto reload ...
10235 // style = acquireNew<TStyle>(h->GetTitle(),
10236 // TString::Format("Style for %s component", h->GetTitle()));
10237 // (TAttLine &) (*style) = *dynamic_cast<TAttLine *>(h);
10238 // (TAttFill &) (*style) = *dynamic_cast<TAttFill *>(h);
10239 // (TAttMarker &) (*style) = *dynamic_cast<TAttMarker *>(h);
10240 // gROOT->GetListOfStyles()->Add(style.get());
10241 // }
10242 // }
10243 // (TAttLine&)(*h) = *(gROOT->GetStyle(h->GetTitle()) ? gROOT->GetStyle(h->GetTitle()) : gStyle);
10244 // (TAttFill&)(*h) = *(gROOT->GetStyle(h->GetTitle()) ? gROOT->GetStyle(h->GetTitle()) : gStyle);
10245 // (TAttMarker&)(*h) = *(gROOT->GetStyle(h->GetTitle()) ? gROOT->GetStyle(h->GetTitle()) : gStyle);
10246 auto _style = style(h);
10247 rar->setStringAttribute("style", oldStyle == "" ? nullptr : oldStyle.Data()); // restores old style
10248 if (_style) {
10249 (TAttLine &)(*h) = *_style;
10250 (TAttFill &)(*h) = *_style;
10251 (TAttMarker &)(*h) = *_style;
10252 }
10253 h->Draw(dOpt);
10254 if (errHist) {
10255 errHist->SetTitle(overlayName);
10256 (TAttLine &)(*errHist) = *h;
10257 errHist->SetFillColor(h->GetLineColor());
10258 }
10259 }
10260 } else {
10261 auto _style = style(h);
10262 if (_style) {
10263 (TAttLine &)(*h) = *_style;
10264 (TAttFill &)(*h) = *_style;
10265 (TAttMarker &)(*h) = *_style;
10266 if (errHist) {
10267 (TAttLine &)(*errHist) = *h;
10268 errHist->SetFillColor(h->GetLineColor());
10269 }
10270 }
10271 h->Draw(dOpt + sOpt);
10272 }
10273
10274 if (!hasOverlay && (rarNode->get()->InheritsFrom("RooRealSumPdf") || rarNode->get()->InheritsFrom("RooAddPdf") ||
10275 (rarNode->get()->InheritsFrom("RooSimultaneous") &&
10276 strcmp(vv->GetName(), rarNode->get<RooSimultaneous>()->indexCat().GetName()) == 0))) {
10277 // build a stack unless not requested
10278 if (!nostack) {
10279 THStack *stack = new THStack(TString::Format("%s_stack", rar->GetName()),
10280 TString::Format("%s;%s", rar->GetTitle(), h->GetXaxis()->GetTitle()));
10281 int count = 2;
10282 std::map<std::string, int> colorByTitle; // TODO: should fill from any existing legend
10283 std::set<std::string> allTitles;
10284 bool titleMatchName = true;
10285 std::map<std::string, TH1 *> histGroups;
10286 std::vector<TH1 *> hhs;
10287 std::set<TH1 *> histsWithBadTitles; // these histograms will have their titles autoFormatted
10288
10289 // support for CMS model case where has single component containing many coeffs
10290 // will build stack by setting each coeff equal to 0 in turn, rebuilding the histogram
10291 // the difference from the "full" histogram will be the component
10292 RooArgList cms_coefs;
10293 if (!rarNode->components().empty()) {
10294 auto comps = rarNode->components()[0];
10295 for (auto &c : *comps) {
10296 if (c->fFolder == "!.coeffs")
10297 cms_coefs.add(*c->get<RooAbsArg>());
10298 }
10299 }
10300 if (!cms_coefs.empty()) {
10301 RooRealVar zero("zero", "", 0);
10302 std::shared_ptr<TH1> prevHist(static_cast<TH1 *>(h->Clone()));
10303 for (auto c : cms_coefs) {
10304 // seems I have to remake the function each time, as haven't figured out what cache needs clearing?
10305 std::unique_ptr<RooAbsReal> f(
10306 dynamic_cast<RooAbsReal *>(rarNode->components()[0]->get()->Clone("tmpCopy")));
10307 zero.setAttribute(
10308 Form("ORIGNAME:%s", c->GetName())); // used in redirectServers to say what this replaces
10309 f->redirectServers(RooArgSet(zero), false, true); // each time will replace one additional coef
10310 // zero.setAttribute(Form("ORIGNAME:%s",c->GetName()),false); (commented out so that on next iteration
10311 // will still replace all prev)
10312 auto hh = xRooNode(*f, *this).BuildHistogram(v);
10313 hh->SetName(c->GetName());
10314 if (sf)
10315 hh->Scale(sf->getVal());
10316 if (strlen(hh->GetTitle()) == 0) {
10317 hh->SetTitle(c->GetName()); // ensure all hists has titles
10318 histsWithBadTitles.insert(hh);
10319 } else if (strcmp(hh->GetName(), hh->GetTitle()) == 0) {
10320 histsWithBadTitles.insert(hh);
10321 }
10322 titleMatchName &= (TString(c->GetName()) == hh->GetTitle() ||
10323 TString(hh->GetTitle()).BeginsWith(TString(c->GetName()) + "_"));
10324 std::shared_ptr<TH1> nextHist(static_cast<TH1 *>(hh->Clone()));
10325 hh->Add(prevHist.get(), -1.);
10326 hh->Scale(-1.);
10327 hhs.push_back(hh);
10328 prevHist = nextHist;
10329 }
10330 } else if (get<RooSimultaneous>()) {
10331 // need to create a histogram for each sample across all the channels - will rely on functionality below to
10332 // merge them based on titles
10333
10334 for (auto &chan : bins()) {
10335 TString chanName(chan->GetName());
10336 chanName = chanName(chanName.Index("=") + 1, chanName.Length());
10337 auto samps = chan->mainChild();
10338 if (!samps)
10339 samps = *chan;
10340 for (auto &samp : samps.components()) {
10341 auto hh = static_cast<TH1 *>(h->Clone(samp->GetName()));
10342 hh->Reset();
10343 hh->SetTitle(samp->GetTitle());
10344 if (strlen(hh->GetTitle()) == 0) {
10345 hh->SetTitle(samp->GetName());
10346 histsWithBadTitles.insert(hh);
10347 } else if (strcmp(hh->GetName(), hh->GetTitle()) == 0) {
10348 histsWithBadTitles.insert(hh);
10349 }
10350 hh->SetTitle(TString(hh->GetTitle())
10351 .ReplaceAll(TString(chan->get()->GetName()) + "_",
10352 "")); // remove occurrence of channelname_ in title (usually prefix)
10353 titleMatchName &= (TString(samp->GetName()) == hh->GetTitle() ||
10354 TString(hh->GetTitle()).BeginsWith(TString(samp->GetName()) + "_"));
10355 hh->SetBinContent(hh->GetXaxis()->FindFixBin(chanName), samp->GetContent());
10356 hhs.push_back(hh);
10357 }
10358 }
10359 } else {
10360 for (auto &samp : rarNode->components()) {
10361 auto hh = samp->BuildHistogram(v);
10362 if (sf)
10363 hh->Scale(sf->getVal());
10364 hhs.push_back(hh);
10365 if (strlen(hh->GetTitle()) == 0) {
10366 hh->SetTitle(samp->GetName()); // ensure all hists has titles
10367 histsWithBadTitles.insert(hh);
10368 } else if (strcmp(hh->GetName(), hh->GetTitle()) == 0) {
10369 histsWithBadTitles.insert(hh);
10370 }
10371 titleMatchName &= (TString(samp->GetName()) == hh->GetTitle() ||
10372 TString(hh->GetTitle()).BeginsWith(TString(samp->GetName()) + "_"));
10373 }
10374 }
10375
10376 if (!hhs.empty()) {
10377 for (auto &hh : hhs) {
10378 allTitles.insert(hh->GetTitle());
10379 }
10380
10381 // get common prefix to strip off only if all titles match names and
10382 // any title is longer than 10 chars
10383 size_t e = std::min(allTitles.begin()->size(), allTitles.rbegin()->size());
10384 size_t ii = 0;
10385 bool goodPrefix = false;
10386 std::string commonSuffix;
10387 if (titleMatchName && hhs.size() > 1) {
10388 while (ii < e - 1 && allTitles.begin()->at(ii) == allTitles.rbegin()->at(ii)) {
10389 ii++;
10390 if (allTitles.begin()->at(ii) == '_' || allTitles.begin()->at(ii) == ' ')
10391 goodPrefix = true;
10392 }
10393
10394 // find common suffix if there is one .. must start with a "_"
10395 bool stop = false;
10396 while (!stop && commonSuffix.size() < size_t(e - 1)) {
10397 commonSuffix = allTitles.begin()->substr(allTitles.begin()->length() - commonSuffix.length() - 1);
10398 for (auto &t : allTitles) {
10399 if (!TString(t).EndsWith(commonSuffix.c_str())) {
10400 commonSuffix = commonSuffix.substr(1);
10401 stop = true;
10402 break;
10403 }
10404 }
10405 }
10406 if (commonSuffix.find('_') == std::string::npos) {
10407 commonSuffix = "";
10408 } else {
10409 commonSuffix = commonSuffix.substr(commonSuffix.find('_'));
10410 }
10411 }
10412 if (!goodPrefix)
10413 ii = 0;
10414
10415 // also find how many characters are needed to distinguish all entries (that dont have the same name)
10416 // then carry on up to first space or underscore
10417 size_t jj = 0;
10418 std::map<std::string, std::string> reducedTitles;
10419 while (reducedTitles.size() != allTitles.size()) {
10420 jj++;
10421 std::map<std::string, int> titlesMap;
10422 for (auto &s : allTitles) {
10423 if (reducedTitles.count(s))
10424 continue;
10425 titlesMap[s.substr(0, jj)]++;
10426 }
10427 for (auto &s : allTitles) {
10428 if (titlesMap[s.substr(0, jj)] == 1 && (jj >= s.length() || s.at(jj) == ' ' || s.at(jj) == '_')) {
10429 reducedTitles[s] = s.substr(0, jj);
10430 }
10431 }
10432 }
10433
10434 // strip common prefix and suffix before adding
10435 for (auto ritr = hhs.rbegin(); ritr != hhs.rend(); ++ritr) { // go in reverse order
10436 if (!histsWithBadTitles.count((*ritr))) {
10437 continue;
10438 }
10439 auto _title = (hhs.size() > 5) ? reducedTitles[(*ritr)->GetTitle()] : (*ritr)->GetTitle();
10440 _title = _title.substr(ii < _title.size() ? ii : 0);
10441 if (!commonSuffix.empty() && TString(_title).EndsWith(commonSuffix.c_str()))
10442 _title = _title.substr(0, _title.length() - commonSuffix.length());
10443 (*ritr)->SetTitle(_title.c_str());
10444 }
10445 }
10446
10447 for (auto &hh : hhs) {
10448 // automatically group hists that all have the same title
10449 if (histGroups.find(hh->GetTitle()) == histGroups.end()) {
10450 histGroups[hh->GetTitle()] = hh;
10451 } else {
10452 // add it into this group
10453 histGroups[hh->GetTitle()]->Add(hh);
10454 delete hh;
10455 hh = nullptr;
10456 continue;
10457 }
10458 auto hhMin = (hh->GetMinimum() == 0) ? hh->GetMinimum(1e-9) : hh->GetMinimum();
10459 if (!stack->GetHists() && h->GetMinimum() > hhMin) {
10460 auto newMin = hhMin - (h->GetMaximum() - hhMin) * gStyle->GetHistTopMargin();
10461 if (hhMin >= 0 && newMin < 0)
10462 newMin = hhMin * 0.99;
10463 adjustYRange(newMin, h->GetMaximum());
10464 }
10465
10466 /*if(stack->GetHists() && stack->GetHists()->GetEntries()>0) {
10467 // to remove rounding effects on bin boundaries, see if binnings compatible
10468 auto _h1 = dynamic_cast<TH1*>(stack->GetHists()->At(0));
10469 if(_h1->GetNbinsX()==hh->GetNbinsX()) TODO ... finish dealing with silly rounding effects
10470 }*/
10471 TString thisOpt = dOpt;
10472 // uncomment next line to blend continuous with discrete components .. get some unpleasant "poke through"
10473 // effects though
10474 // if(auto s = samp->get<RooAbsReal>(); s) thisOpt = s->isBinnedDistribution(*dynamic_cast<RooAbsArg*>(v)) ?
10475 // "" : "LF2";
10476 stack->Add(hh, thisOpt);
10477 }
10478 stack->SetBit(kCanDelete); // should delete its sub histograms
10479 stack->Draw("noclear same");
10480 h->Draw(
10481 dOpt + sOpt +
10482 "same"); // overlay again .. if stack would cover original hist (negative components) we still see integral
10483 h->Draw("axissame"); // redraws axis
10484
10485 TList *ll = stack->GetHists();
10486 if (ll && ll->GetEntries()) {
10487
10488 // finally, ensure all hists are styled
10489 for (auto ho : *ll) {
10490 TH1 *hh = dynamic_cast<TH1 *>(ho);
10491 if (!hh)
10492 continue;
10493 bool createdStyle = (xRooNode(*hh, *this).style(nullptr, false) == nullptr);
10494
10495 if (createdStyle) {
10496 // give hist a color, that isn't the same as any other hists color
10497 hh->SetFillStyle(1001); // solid fill style
10498 bool used = false;
10499 do {
10500 hh->SetFillColor((count++));
10501 // check not already used this color
10502 used = false;
10503 for (auto ho2 : *ll) {
10504 TH1 *hh2 = dynamic_cast<TH1 *>(ho2);
10505 if (!hh2)
10506 continue;
10507 auto _style = xRooNode(*hh2, *this).style(hh2, false);
10508 if (hh != hh2 && _style && _style->GetFillColor() == hh->GetFillColor()) {
10509 used = true;
10510 break;
10511 }
10512 }
10513 } while (used);
10514 }
10515
10516 auto _style = xRooNode(*hh, *this).style(hh);
10517 if (_style) {
10518 *dynamic_cast<TAttLine *>(hh) = *_style;
10519 *dynamic_cast<TAttFill *>(hh) = *_style;
10520 *dynamic_cast<TAttMarker *>(hh) = *_style;
10521 }
10522 // for stacks, fill color of white should be color 10 unless fill style is 0
10523 if (hh->GetFillColor() == kWhite && hh->GetFillStyle() != 0) {
10524 // kWhite means 'transparent' in ROOT ... should really use a FillStyle of 0 for that
10525 // so assume user wanted actual white, which is color 10
10526 hh->SetFillColor(10);
10527 }
10528 addLegendEntry(hh, hh->GetTitle(), "f");
10529 }
10530 }
10531 }
10532 } else if (!overlayExisted) {
10533
10534 if (errHist) {
10535 addLegendEntry(errHist, strlen(errHist->GetTitle()) ? errHist->GetTitle() : GetName(), "fl");
10536 } else {
10537 addLegendEntry(h, strlen(h->GetTitle()) ? h->GetTitle() : GetName(), "l");
10538 }
10539 }
10540
10541 if (errHist) {
10542 dOpt.ReplaceAll("TEXT", "");
10543 errHist->Draw(dOpt + (dOpt.Contains("LF2") ? "e3same" : "e2same"));
10544 double ymax = -std::numeric_limits<double>::infinity();
10545 double ymin = std::numeric_limits<double>::infinity();
10546 for (int i = 1; i <= errHist->GetNbinsX(); i++) {
10547 ymax = std::max(ymax, errHist->GetBinContent(i) + errHist->GetBinError(i));
10548 ymin = std::min(ymin, errHist->GetBinContent(i) - errHist->GetBinError(i));
10549 }
10550 adjustYRange(ymin, ymax);
10551 } else {
10552 adjustYRange(h->GetMinimum() * 0.9, h->GetMaximum() * 1.1);
10553 }
10554
10555 if ((!auxPlotTitle.empty()) && !hasSame) {
10556 // create a pad for the ratio ... shift the bottom margin of this pad to make space for it
10557 double padFrac = 0.3;
10558 auto _tmpPad = gPad;
10559 gPad->SetBottomMargin(padFrac);
10560 auto ratioPad = new TPad("auxPad", "aux plot", 0, 0, 1, padFrac);
10561 ratioPad->SetFillColor(_tmpPad->GetFillColor());
10562 ratioPad->SetNumber(1);
10563 ratioPad->SetBottomMargin(ratioPad->GetBottomMargin() / padFrac);
10564 ratioPad->SetTopMargin(0.04);
10565 ratioPad->SetLeftMargin(gPad->GetLeftMargin());
10566 ratioPad->SetRightMargin(gPad->GetRightMargin());
10567 ratioPad->cd();
10568 TH1 *ratioHist = dynamic_cast<TH1 *>((errHist) ? errHist->Clone("auxHist") : h->Clone("auxHist"));
10569 ratioHist->SetDirectory(nullptr);
10570 ratioHist->SetTitle((errHist) ? errHist->GetName()
10571 : h->GetName()); // abuse the title string to hold the name of the main hist
10572
10573 ratioHist->GetYaxis()->SetNdivisions(5, 0, 0);
10574 ratioHist->GetYaxis()->SetTitle(auxPlotTitle.c_str());
10575 ratioHist->SetTitle(
10576 TString::Format("%s|%s", ratioHist->GetTitle(),
10577 auxPlotTitle.c_str())); // used when plotting data (above) to decide what to calculate
10578 ratioHist->SetMaximum();
10579 ratioHist->SetMinimum(); // resets min and max
10580 ratioPad->SetGridy();
10581
10582 for (int i = 1; i <= ratioHist->GetNbinsX(); i++) {
10583 double val = ratioHist->GetBinContent(i);
10584 double err = ratioHist->GetBinError(i);
10585 ratioHist->SetBinContent(i, std::get<0>(auxFunctions[auxPlotTitle])(val, val, err));
10586 ratioHist->SetBinError(i, std::get<0>(auxFunctions[auxPlotTitle])(val + err, val, err) -
10587 ratioHist->GetBinContent(i));
10588 }
10589
10590 double rHeight = 1. / padFrac; //(_tmpPad->GetWNDC())/(gPad->GetHNDC());
10591 if (ratioHist->GetYaxis()->GetTitleFont() % 10 == 2) {
10592 ratioHist->GetYaxis()->SetTitleSize(ratioHist->GetYaxis()->GetTitleSize() * rHeight);
10593 ratioHist->GetYaxis()->SetLabelSize(ratioHist->GetYaxis()->GetLabelSize() * rHeight);
10594 ratioHist->GetXaxis()->SetTitleSize(ratioHist->GetXaxis()->GetTitleSize() * rHeight);
10595 ratioHist->GetXaxis()->SetLabelSize(ratioHist->GetXaxis()->GetLabelSize() * rHeight);
10596 ratioHist->GetYaxis()->SetTitleOffset(ratioHist->GetYaxis()->GetTitleOffset() / rHeight);
10597 } else {
10598#if ROOT_VERSION_CODE < ROOT_VERSION(6, 26, 00)
10599 ratioHist->GetYaxis()->SetTitleOffset(ratioHist->GetYaxis()->GetTitleOffset() / rHeight);
10600#endif
10601 }
10602 ratioHist->GetXaxis()->SetTickLength(ratioHist->GetXaxis()->GetTickLength() * rHeight);
10603 ratioHist->SetStats(false);
10604 ratioHist->SetBit(TH1::kNoTitle);
10605 ratioHist->SetBit(kCanDelete);
10606 if (errHist) {
10607 auto _h = dynamic_cast<TH1 *>(ratioHist->Clone("auxHist_clone"));
10608 _h->SetDirectory(nullptr);
10609 _h->SetFillColor(0);
10610 ratioHist->GetListOfFunctions()->Add(_h, "histsame");
10611 //_h->Draw("histsame");
10612 }
10613 ratioHist->GetListOfFunctions()->Add(new TExec(
10614 ".updateAxis",
10615 TString::Format("auto h1 = (TH1*)%p; auto h2 = (TH1*)%p; if(h2->GetXaxis()->GetFirst() != "
10616 "h1->GetXaxis()->GetFirst() || h1->GetXaxis()->GetLast()!=h2->GetXaxis()->GetLast()) "
10617 "{h2->GetXaxis()->SetRange(h1->GetXaxis()->GetFirst(),h1->GetXaxis()->GetLast());if(gPad) "
10618 "{gPad->GetCanvas()->Paint();gPad->GetCanvas()->Update();}}",
10619 (void *)ratioHist, (void *)(h))));
10620 ratioHist->Draw((errHist ? "e2" : ""));
10621
10622 _tmpPad->cd();
10623 ratioPad->Draw();
10624 } else if (auto ratioPad = dynamic_cast<TPad *>(gPad->GetPrimitive("auxPad")); hasSame && ratioPad) {
10625 // need to draw histogram in the ratio pad ...
10626 // if doing overlay need to update histogram
10627
10628 if (auto hr = dynamic_cast<TH1 *>(ratioPad->GetPrimitive("auxHist"));
10629 hr && auxFunctions.find(hr->GetYaxis()->GetTitle()) != auxFunctions.end()) {
10630 TString histName = hr->GetTitle(); // split it by | char
10631 TString histType = histName(histName.Index('|') + 1, histName.Length());
10632 histName = histName(0, histName.Index('|'));
10633
10634 if (auto hnom = dynamic_cast<TH1 *>(gPad->GetPrimitive(histName)); hnom) {
10635 h = dynamic_cast<TH1 *>(h->Clone(h->GetName()));
10636 h->SetDirectory(nullptr);
10637 h->SetBit(kCanDelete);
10638 for (int i = 1; i <= hnom->GetNbinsX(); i++) {
10639 double val = h->GetBinContent(i);
10640 double err = h->GetBinError(i);
10641 h->SetBinContent(i, std::get<0>(auxFunctions[hr->GetYaxis()->GetTitle()])(
10642 h->GetBinContent(i), hnom->GetBinContent(i), hnom->GetBinError(i)));
10643 h->SetBinError(i, std::get<0>(auxFunctions[hr->GetYaxis()->GetTitle()])(
10644 val + err, hnom->GetBinContent(i), hnom->GetBinError(i)) -
10645 h->GetBinContent(i));
10646 }
10647 auto _tmpPad = gPad;
10648 ratioPad->cd();
10649 if (hasOverlay) {
10650 if (auto existing = dynamic_cast<TH1 *>(ratioPad->GetPrimitive(h->GetName())); existing) {
10651 existing->Reset();
10652 existing->Add(h);
10653 delete h;
10654 h = existing;
10655 overlayExisted = true;
10656 } else {
10657 h->Draw(dOpt);
10658 }
10659 } else {
10660 h->Draw(dOpt);
10661 }
10662 double ymax = -std::numeric_limits<double>::infinity();
10663 double ymin = std::numeric_limits<double>::infinity();
10664 for (int i = 1; i <= h->GetNbinsX(); i++) {
10665 ymax = std::max(ymax, h->GetBinContent(i) + h->GetBinError(i));
10666 ymin = std::min(ymin, h->GetBinContent(i) - h->GetBinError(i));
10667 }
10668 adjustYRange(ymin, ymax, hr, std::get<1>(auxFunctions[hr->GetYaxis()->GetTitle()]));
10669 // adjustYRange(h->GetMinimum() * (h->GetMinimum()<0 ? 1.1 : 0.9), h->GetMaximum() * (h->GetMinimum()<0 ?
10670 // 0.9 : 1.1), hr, std::get<1>(auxFunctions[hr->GetYaxis()->GetTitle()]));
10671 gPad->Modified();
10672 _tmpPad->cd();
10673 }
10674 }
10675 }
10676
10677 // see if it's in a simultaneous so need to select a cat
10678 /*auto _parent = fParent;
10679 auto _me = rar;
10680 while(_parent) {
10681 if (auto s = _parent->get<RooSimultaneous>(); s) {
10682 for (auto c : s->indexCat()) {
10683 if (auto p = s->getPdf(c.first.c_str());_me==p) {
10684 gPad->SetName(c.first.c_str());
10685 break;
10686 }
10687 }
10688 break;
10689 }
10690 _me = _parent->get<RooAbsReal>();
10691 _parent = _parent->fParent;
10692 }*/
10693
10694 // now draw selected datasets on top if this was a pdf
10695 if (auto _pdf = get<RooAbsPdf>();
10696 !hasSame && _pdf /*&& (_pdf->canBeExtended() || robs().empty())*/ && coefs().empty()) {
10697 auto _dsets = datasets();
10698 // bool _drawn=false;
10699 for (auto &d : _dsets) {
10700 if (d->get()->TestBit(1 << 20)) {
10701 d->Draw("same");
10702 //_drawn=true;
10703 }
10704 }
10705 // if (!_drawn && !_dsets.empty()) _dsets[0]->Draw("same"); // always draw if has a dataset
10706 }
10707
10708 gPad->Modified();
10709 // gPad->Update();
10710 getLegend();
10711 gPad->Modified();
10712 // gPad->Update();
10713}
10714
10715void xRooNode::SaveAs(const char *filename, Option_t *option) const
10716{
10717 TString sOpt(option);
10718 sOpt.ToLower();
10719 if (auto w = get<RooWorkspace>(); w) {
10720
10721 if (TString(filename).EndsWith(".json")) {
10722#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 26, 00)
10723 // stream with json tool
10724 RooJSONFactoryWSTool tool(*w);
10725 if (tool.exportJSON(filename)) {
10726 Info("SaveAs", "%s saved to %s", w->GetName(), filename);
10727 } else {
10728 Error("SaveAs", "Unable to save to %s", filename);
10729 }
10730#else
10731 Error("SaveAs", "json format workspaces only in ROOT 6.26 onwards");
10732#endif
10733 return;
10734 }
10735
10736#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
10737 // before saving, clear the eocache of all owned nodes
10738 // because causes memory leak when read back in (workspace streamer immediately overwrites the caches)
10739 // fixed in: https://github.com/root-project/root/pull/12024
10740 for (auto &c : w->components()) {
10741 c->_eocache = nullptr;
10742 }
10743#endif
10744 // const_cast<Node2*>(this)->sterilize(); - tried this to reduce mem leak on readback but no improve
10745 if (!w->writeToFile(filename, sOpt != "update")) {
10746 Info("SaveAs", "%s saved to %s", w->GetName(), filename);
10747 // save any fitDatabase that is loaded in memory too
10748 // TODO: We should do this as well for SaveAs on a scan object
10749 if (auto fitDb = dynamic_cast<TFile *>(gROOT->GetListOfFiles()->FindObject("fitDatabase"))) {
10750
10751 std::function<void(TDirectory *, TDirectory *)> CopyDir;
10752
10753 CopyDir = [&](TDirectory *source, TDirectory *dest) {
10754 auto dir = dest->GetDirectory(source->GetName());
10755 if (!dir) {
10756 dir = dest->mkdir(source->GetName());
10757 }
10758 for (auto k : *source->GetListOfKeys()) {
10759 auto key = dynamic_cast<TKey *>(k);
10760 const char *classname = key->GetClassName();
10761 TClass *cl = gROOT->GetClass(classname);
10762 // std::cout << "processing " << key->GetName() << " " << classname << std::endl;
10763 if (!cl) {
10764 continue;
10765 } else if (cl->InheritsFrom(TDirectory::Class())) {
10766 CopyDir(source->GetDirectory(key->GetName()), dir);
10767 } else {
10768 // don't write object if it already exists
10769 if (dir->FindKey(key->GetName()))
10770 continue;
10771 // support FitConfigs ....
10772 if (strcmp(classname, "ROOT::Fit::FitConfig") == 0) {
10773 auto fc = key->ReadObject<ROOT::Fit::FitConfig>();
10774 dir->WriteObject(fc, key->GetName());
10775 delete fc;
10776 } else {
10777 TObject *obj = key->ReadObj();
10778 if (obj) {
10779 dir->WriteTObject(obj, key->GetName());
10780 delete obj;
10781 }
10782 }
10783 }
10784 }
10785 };
10786 CopyDir(fitDb, std::make_unique<TFile>(filename, "UPDATE").get());
10787 Info("SaveAs", "Saved fitDatabase to %s", filename);
10788 }
10789
10790 } else {
10791 Error("SaveAs", "Unable to save to %s", filename);
10792 }
10793#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
10794 // restore the cache to every node
10795 for (auto &c : w->components()) {
10796 c->setExpensiveObjectCache(w->expensiveObjectCache());
10797 }
10798#endif
10799 }
10800}
10801
10802double xRooNode::GetBinError(int bin, const xRooNode &fr) const
10803{
10804 auto res = GetBinErrors(bin, bin, fr);
10805 if (res.empty())
10806 return std::numeric_limits<double>::quiet_NaN();
10807 return res.at(0);
10808}
10809
10810std::vector<double> xRooNode::contents() const
10811{
10812 std::vector<double> out;
10813 out.reserve(size());
10814 for (auto child : *this) {
10815 out.push_back(child->GetContent());
10816 }
10817 return out;
10818}
10819
10821{
10822
10823 auto _fr = fr.get<RooFitResult>();
10824
10825 if (!_fr) {
10826 return covariances(fitResult());
10827 }
10828
10829 auto rho = _fr->correlationMatrix();
10830
10831 TMatrixDSym out(size());
10832
10833 auto _pars = pars();
10834
10835 // 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]
10836 // - nu[theta_n_down]) consistent with propagatedError formula
10837
10838 for (int m = 0; m < rho.GetNrows(); m++) {
10839 auto p_m = dynamic_cast<RooRealVar *>(_fr->floatParsFinal().at(m));
10840 if (!p_m)
10841 continue; // skip categoricals
10842 auto _p = dynamic_cast<RooAbsRealLValue *>(_pars.get<RooArgList>()->find(p_m->GetName()));
10843 if (!_p)
10844 continue;
10845 auto tmp = _p->getVal();
10846 _p->setVal(p_m->getVal() + p_m->getErrorHi());
10847 auto nu_m = contents();
10848 _p->setVal(p_m->getVal() + p_m->getErrorLo());
10849 auto nu_m2 = contents();
10850 _p->setVal(tmp);
10851 for (int n = 0; n < rho.GetNrows(); n++) {
10852 auto p_n = dynamic_cast<RooRealVar *>(_fr->floatParsFinal().at(n));
10853 if (!p_n)
10854 continue; // skip categoricals
10855 auto _p2 = dynamic_cast<RooAbsRealLValue *>(_pars.get<RooArgList>()->find(p_n->GetName()));
10856 if (!_p2)
10857 continue;
10858 auto tmp2 = _p2->getVal();
10859 _p2->setVal(p_n->getVal() + p_n->getErrorHi());
10860 auto nu_n = (p_n == p_m) ? nu_m : contents();
10861 _p2->setVal(p_n->getVal() + p_n->getErrorLo());
10862 auto nu_n2 = (p_n == p_m) ? nu_m2 : contents();
10863 _p2->setVal(tmp2);
10864 for (int i = 0; i < out.GetNrows(); i++) {
10865 for (int j = 0; j < out.GetNrows(); j++) {
10866 out(i, j) += 0.25 * (nu_m[i] - nu_m2[i]) * rho(m, n) * (nu_n[j] - nu_n2[j]);
10867 }
10868 }
10869 }
10870 }
10871 return out;
10872}
10873
10874std::pair<double, double> xRooNode::IntegralAndError(const xRooNode &fr, const char *rangeName) const
10875{
10876 double out = 1.;
10877 double err = std::numeric_limits<double>::quiet_NaN();
10878
10879 std::unique_ptr<RooAbsCollection> _snap;
10880 RooArgList _pars;
10881 if (auto _fr = fr.get<RooFitResult>()) {
10882 _pars.add(pars().argList());
10883 _snap.reset(_pars.snapshot());
10884 _pars = _fr->floatParsFinal();
10885 _pars = _fr->constPars();
10886 }
10887
10888 auto _obs = obs();
10889 auto _coefs = coefs(); // need here to keep alive owned RooProduct
10890 if (auto c = _coefs.get<RooAbsReal>(); c) {
10891 out = c->getVal(*_obs.get<RooArgList>()); // assumes independent of observables!
10892 }
10893
10894 if (auto p = dynamic_cast<RooAbsPdf *>(get()); p) {
10895 // prefer to use expectedEvents for integrals of RooAbsPdf e.g. for RooProdPdf wont include constraint terms
10896 if (rangeName)
10897 p->setNormRange(rangeName);
10898#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 27, 00)
10900#endif
10901 out *= p->expectedEvents(*_obs.get<RooArgList>());
10902#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
10903 // improved normSet invalidity checking, so assuming no longer need this in 6.28 onwards
10904 p->_normSet = nullptr;
10905#endif
10906 err = GetBinError(-1, fr);
10907 if (rangeName)
10908 p->setNormRange(nullptr);
10909 } else if (auto p2 = dynamic_cast<RooAbsReal *>(get()); p2) {
10910 // only integrate over observables we actually depend on
10911 auto f = std::shared_ptr<RooAbsReal>(
10912 p2->createIntegral(*std::unique_ptr<RooArgSet>(p2->getObservables(*_obs.get<RooArgList>())),
10913 rangeName)); // did use x here before using obs
10914 RooProduct pr("int_x_coef", "int_x_coef",
10915 RooArgList(*f, !_coefs.get() ? RooFit::RooConst(1) : *_coefs.get<RooAbsReal>()));
10916 out *= f->getVal();
10917 err = xRooNode(pr, *this).GetBinError(-1, fr);
10918 sterilize(); // needed so that we can forget properly about the integral we just created (and are deleting)
10919 } else if (get<RooAbsData>()) {
10920 out = 0;
10921 auto vals = GetBinContents(1, 0); // returns all bins
10922 auto ax = (rangeName) ? GetXaxis() : nullptr;
10923 auto rv = (ax) ? dynamic_cast<RooRealVar *>(ax->GetParent()) : nullptr;
10924 auto cv = (ax && !rv) ? dynamic_cast<RooCategory *>(ax->GetParent()) : nullptr;
10925 int i = 0;
10926 for (auto &v : vals) {
10927 i++;
10928 if (rangeName) {
10929 if (rv && !rv->inRange(ax->GetBinCenter(i), rangeName))
10930 continue;
10931 if (cv && !cv->isStateInRange(rangeName, ax->GetBinLabel(i)))
10932 continue;
10933 }
10934 out += v;
10935 }
10936 err = 0; // should this be sqrt(sum(v^2)) or something similar
10937 } else {
10938 out = std::numeric_limits<double>::quiet_NaN();
10939 }
10940 if (_snap) {
10941 _pars.RooAbsCollection::operator=(*_snap);
10942 }
10943 return std::make_pair(out, err);
10944}
10945
10946std::vector<double> xRooNode::GetBinErrors(int binStart, int binEnd, const xRooNode &_fr) const
10947{
10948 // note: so far this method is inconsistent with the BuildHistogram in ways:
10949 // no projection over other variables
10950 // July2023: made RooRealSumPdf evaluate as a function if doesn't have a floor
10951 // but this method will still evaluate it as a pdf (uses PdfWrapper)
10952 // but can get away with it while added NaN recovery to getSimplePropagatedError to pickup raw values
10953
10954 if (fBinNumber != -1) {
10955 if (binStart != binEnd || !fParent) {
10956 throw std::runtime_error(TString::Format("%s is a bin - only has one value", GetName()));
10957 }
10958 return fParent->GetBinErrors(fBinNumber, fBinNumber);
10959 }
10960
10961 std::vector<double> out;
10962
10963 auto o = dynamic_cast<RooAbsReal *>(get());
10964 if (!o)
10965 return out;
10966
10967 std::shared_ptr<RooFitResult> fr = std::dynamic_pointer_cast<RooFitResult>(_fr.fComp);
10968 //= dynamic_cast<RooFitResult*>( _fr.get<RooFitResult>() ? _fr->Clone() : fitResult()->Clone());
10969
10970 auto _coefs = coefs();
10971
10972 if (!fr) {
10973 // need to ensure coefs, if any, are included in fit result retrieval so all pars are loaded
10974 auto frn = (!_coefs.get() ? *this : xRooNode(RooProduct("tmp", "tmp", RooArgList(*o, *_coefs.get<RooAbsReal>()))))
10975 .fitResult();
10976 if (strlen(_fr.GetName()))
10977 frn = frn.reduced(_fr.GetName());
10978
10979 // use name to reduce the fit result, if one given
10980 fr = std::dynamic_pointer_cast<RooFitResult>(frn.fComp);
10981 }
10982
10983 if (!GETDMP(fr.get(), _finalPars)) {
10984 fr->setFinalParList(RooArgList());
10985 }
10986
10987 /// Oct2022: No longer doing this because want to allow fitResult to be used to get partial error
10988 // // need to add any floating parameters not included somewhere already in the fit result ...
10989 // RooArgList l;
10990 // for(auto& p : pars()) {
10991 // auto v = p->get<RooRealVar>();
10992 // if (!v) continue;
10993 // if (v->isConstant()) continue;
10994 // if (fr->floatParsFinal().find(v->GetName())) continue;
10995 // if (fr->_constPars && fr->_constPars->find(v->GetName())) continue;
10996 // l.add(*v);
10997 // }
10998 //
10999 // if (!l.empty()) {
11000 // RooArgList l2; l2.addClone(fr->floatParsFinal());
11001 // l2.addClone(l);
11002 // fr->setFinalParList(l2);
11003 // }
11004
11005 TMatrixTSym<double> *prevCov = static_cast<TMatrixTSym<double> *>(GETDMP(fr.get(), _VM));
11006
11007 if (!prevCov || size_t(prevCov->GetNcols()) < fr->floatParsFinal().size()) {
11008 TMatrixDSym cov(fr->floatParsFinal().size());
11009 if (prevCov) {
11010 for (int i = 0; i < prevCov->GetNcols(); i++) {
11011 for (int j = 0; j < prevCov->GetNrows(); j++) {
11012 cov(i, j) = (*prevCov)(i, j);
11013 }
11014 }
11015 }
11016 int i = 0;
11017 for (auto &p : fr->floatParsFinal()) {
11018 if (!prevCov || i >= prevCov->GetNcols()) {
11019 cov(i, i) = pow(dynamic_cast<RooRealVar *>(p)->getError(), 2);
11020 }
11021 i++;
11022 }
11023 int covQualBackup = fr->covQual();
11024 fr->setCovarianceMatrix(cov);
11025 fr->setCovQual(covQualBackup);
11026 }
11027
11028 bool doBinWidth = false;
11029 auto ax = (binStart == -1 && binEnd == -1) ? nullptr : GetXaxis();
11030
11031 auto _obs = obs(); // may own an obs so keep alive here
11032 RooArgList normSet = _obs.argList();
11033 // to give consistency with BuildHistogram method, should be only the axis var if defined
11034 if (ax) {
11035 normSet.clear();
11036 normSet.add(*dynamic_cast<RooAbsArg *>(ax->GetParent()));
11037 }
11038
11039 if (auto p = dynamic_cast<RooAbsPdf *>(o); ax && (p || _coefs.get() || o->getAttribute("density"))) {
11040 // pdfs of samples embedded in a sumpdf (aka have a coef) will convert their density value to a content
11041 doBinWidth = true;
11042 }
11043 if (binEnd == 0) {
11044 if (ax) {
11045 binEnd = ax->GetNbins();
11046 } else {
11047 binEnd = binStart;
11048 }
11049 }
11050 for (int bin = binStart; bin <= binEnd; bin++) {
11051 if (ax)
11052 dynamic_cast<RooAbsLValue *>(ax->GetParent())->setBin(bin - 1, ax->GetName());
11053 // if (!SetBin(bin)) { return out; }
11054
11055 double res;
11056 if (auto p = dynamic_cast<RooAbsPdf *>(o); p) {
11057 // fr->covarianceMatrix().Print();
11058 res = PdfWrapper(*p, _coefs.get<RooAbsReal>(), !ax).getSimplePropagatedError(*fr, normSet);
11059#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
11060 // improved normSet invalidity checking, so assuming no longer need this in 6.28 onwards
11061 p->_normSet = nullptr;
11062#endif
11063 } else {
11064 // res = o->getPropagatedError(*fr, normSet);
11065 // // TODO: What if coef has error? - probably need a FuncWrapper class
11066 // if (auto c = _coefs.get<RooAbsReal>(); c) {
11067 // res *= c->getVal(normSet);
11068 // }
11069 res = RooProduct("errorEval", "errorEval",
11070 RooArgList(*o, !_coefs.get() ? RooFit::RooConst(1) : *_coefs.get<RooAbsReal>()))
11071 .getPropagatedError(*fr, normSet);
11072 }
11073 if (doBinWidth) {
11074 res *= ax->GetBinWidth(bin);
11075 }
11076 out.push_back(res);
11077 }
11078
11079 return out;
11080}
11081
11082std::string cling::printValue(const xRooNode *v)
11083{
11084 if (!v)
11085 return "nullptr\n";
11086 if (!v->empty()) {
11087 std::string out;
11088 size_t left = v->size();
11089 for (auto n : *v) {
11090 left--;
11091 if (!out.empty())
11092 out += ",";
11093 else
11094 out += "{";
11095 out += n->GetName();
11096 if (out.length() > 100 && left > 0) {
11097 out += TString::Format(",... and %zu more", left);
11098 break;
11099 }
11100 }
11101 out += "}\n";
11102 out = std::string(Form("<%s> %s", v->get() ? v->get()->ClassName() : "nullptr", v->GetName())) + out;
11103 return out;
11104 }
11105 std::string out;
11106 if (!(*v)) {
11107 return "<nullptr>";
11108 } else {
11109 return Form("<%s> %s", v->get() ? v->get()->ClassName() : "nullptr", v->GetName());
11110 }
11111
11112 return out;
11113}
11114
@ kButton1Down
Definition Buttons.h:17
int main()
Definition Prototype.cxx:12
#define d(i)
Definition RSha256.hxx:102
#define b(i)
Definition RSha256.hxx:100
#define f(i)
Definition RSha256.hxx:104
#define c(i)
Definition RSha256.hxx:101
#define g(i)
Definition RSha256.hxx:105
#define a(i)
Definition RSha256.hxx:99
#define h(i)
Definition RSha256.hxx:106
#define e(i)
Definition RSha256.hxx:103
#define ROOT_VERSION(a, b, c)
Definition RVersion.hxx:23
#define ROOT_VERSION_CODE
Definition RVersion.hxx:24
RooArgSet * _normSet
Pointer to set with observables used for normalization.
RooAbsData * _data
Pointer to original input dataset.
RooSetProxy _paramSet
Parameters of the test statistic (=parameters of the input function)
RooAbsReal * _func
Pointer to original input function.
virtual RooAbsTestStatistic * create(const char *name, const char *title, RooAbsReal &real, RooAbsData &data, const RooArgSet &projDeps, Configuration const &cfg)=0
size_t size(const MatrixT &matrix)
retrieve the size of a square matrix
const char Option_t
Definition RtypesCore.h:66
@ 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
static void indent(ostringstream &buf, int indent_level)
R__EXTERN TEnv * gEnv
Definition TEnv.h:170
void Warning(const char *location, const char *msgfmt,...)
Use this function in warning situations.
Definition TError.cxx:229
#define gClient
Definition TGClient.h:156
@ 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 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 data
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 SetFillColor
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t winding char text const char depth char const char Int_t count const char ColorStruct_t color const char Pixmap_t Pixmap_t PictureAttributes_t attr const char char ret_data h unsigned char height h Atom_t Int_t ULong_t ULong_t unsigned char prop_list Atom_t Atom_t Atom_t Time_t type
Option_t Option_t style
char name[80]
Definition TGX11.cxx:110
float xmin
float * q
float ymin
float xmax
float ymax
@ kCanDelete
Definition TObject.h:367
#define gROOT
Definition TROOT.h:407
char * Form(const char *fmt,...)
Formats a string in a circular formatting buffer.
Definition TString.cxx:2489
R__EXTERN TStyle * gStyle
Definition TStyle.h:436
R__EXTERN TSystem * gSystem
Definition TSystem.h:555
#define gPad
#define _(A, B)
Definition cfortran.h:108
double GetBinLowEdge(Int_t bin) const override
Return low edge of bin.
Definition xRooNode.cxx:853
double GetBinUpEdge(Int_t bin) const override
Return up edge of bin.
Definition xRooNode.cxx:861
Int_t FindFixBin(double x) const override
Find bin number corresponding to abscissa x.
Definition xRooNode.cxx:904
void Set(Int_t nbins, const double *xbins) override
Initialize axis with variable bins.
Definition xRooNode.cxx:881
RooAbsRealLValue * rvar() const
Definition xRooNode.cxx:908
void SetTitle(const char *title) override
Set the title of the TNamed.
Definition xRooNode.cxx:872
void Set(Int_t nbins, double xmin, double xmax) override
Initialize axis with fix bins.
Definition xRooNode.cxx:894
Int_t FindFixBin(const char *label) const override
Find bin number with label.
Definition xRooNode.cxx:903
RooAbsLValue * var() const
Definition xRooNode.cxx:907
const RooAbsBinning * binning() const
Definition xRooNode.cxx:901
void Set(Int_t nbins, const float *xbins) override
Initialize axis with variable bins.
Definition xRooNode.cxx:887
const char * GetTitle() const override
Returns title of object.
Definition xRooNode.cxx:868
double GetBinWidth(Int_t bin) const override
Return bin width.
Definition xRooNode.cxx:847
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:456
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:146
static xRooNLLVar createNLL(const std::shared_ptr< RooAbsPdf > pdf, const std::shared_ptr< RooAbsData > data, const RooLinkedList &nllOpts)
Definition xRooFit.cxx:97
static std::pair< double, double > matchPrecision(const std::pair< double, double > &in)
Definition xRooFit.cxx:1983
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:176
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)
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)
xRooNode filter(const xRooNode &range) 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="")
std::vector< std::shared_ptr< xRooNode > > fBrowsables
Definition xRooNode.h:497
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 Combine(const xRooNode &rhs)
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.
double GetBinError(int bin, const xRooNode &fr="") const
std::vector< double > GetBinErrors(int binStart=1, int binEnd=0, const xRooNode &fr="") const
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
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:482
void Print(Option_t *opt="") const override
Print TNamed name and title.
std::shared_ptr< T > acquireNew(Args &&...args)
Definition xRooNode.h:274
std::shared_ptr< T > acquire2(Args &&...args)
Definition xRooNode.h:268
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
bool fInterrupted
appears that if was fXaxis then dialog box for SetXaxis will take as current value
Definition xRooNode.h:487
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)
bool SetBinData(int bin, double value, const xRooNode &data="obsData")
std::function< xRooNode(xRooNode *)> fBrowseOperation
Definition xRooNode.h:498
std::vector< double > GetBinContents(int binStart=1, int binEnd=0) const
xRooNode pp() const
List of prespecified parameters: non-floatable parameters.
std::shared_ptr< xRooNode > fProvider
Definition xRooNode.h:490
std::shared_ptr< TStyle > style(TObject *initObject=nullptr, bool autoCreate=true) const
xRooNode fitResult(const char *opt="") const
std::shared_ptr< TAxis > fXAxis
only here so can have char* GetRange return so can return nullptr for no range set (required for RooC...
Definition xRooNode.h:485
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)
bool IsFolder() const override
Returns kTRUE in case object contains browsable objects (like containers or lists of other objects).
Definition xRooNode.cxx:832
double GetBinData(int bin, const xRooNode &data="obsData")
bool SetBinError(int bin, double value)
xRooNode histo(const xRooNode &vars="x", const xRooNode &fr="", bool content=true, bool errors=true) const
bool SetContents(const TObject &obj)
Definition xRooNode.h:347
std::shared_ptr< TObject > fComp
Definition xRooNode.h:472
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)
void _fit_(const char *constParValues="")
std::shared_ptr< TObject > getObject(const std::string &name, const std::string &type="") const
Definition xRooNode.cxx:911
xRooNode np() const
List of nuisance parameters: non-constant parameters that are not marked of interest,...
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
xRooNode reduced(const std::string &range="", bool invert=false) const
auto begin() const -> xRooNodeIterator
Definition xRooNode.h:200
std::shared_ptr< xRooNode > parentPdf() const
like a parent but only for use by getObject
std::shared_ptr< xRooNode > fParent
Definition xRooNode.h:475
std::pair< double, double > IntegralAndError(const xRooNode &fr="", const char *rangeName=nullptr) const
xRooNode robs() const
List of regular observables of this node.
TClass * IsA() const override
Definition xRooNode.h:503
xRooNode & operator=(const TObject &o)
auto end() const -> xRooNodeIterator
Definition xRooNode.h:201
TH1 * BuildHistogram(RooAbsLValue *v=nullptr, bool empty=false, bool errors=false, int binStart=1, int binEnd=0, const xRooNode &fr="") const
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)
void Browse(TBrowser *b=nullptr) override
Browse object. May be overridden for another default action.
Definition xRooNode.cxx:558
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:216
std::shared_ptr< TObject > convertForAcquisition(xRooNode &acquirer, const char *opt="") const
double GetError(const xRooNode &fr="") const
Definition xRooNode.h:395
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:47
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.
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.
virtual bool isFundamental() const
Is this object a fundamental type that can be added to a dataset? Fundamental-type subclasses overrid...
Definition RooAbsArg.h:249
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.
virtual value_type getCurrentIndex() const
Return index number of current state.
const char * getLabel() const
Retrieve current label. Use getCurrentLabel() for more clarity.
Abstract container object that can hold multiple RooAbsArg objects.
RooAbsCollection * selectByAttrib(const char *name, bool value) const
Create a subset of the current collection, consisting only of those elements with the specified attri...
virtual 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 * 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.
Storage_t::size_type size() const
void clear()
Clear contents. If the collection is owning, it will also delete the contents.
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...
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:57
virtual const RooArgSet * get() const
Definition RooAbsData.h:101
virtual void reset()
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={})
Create a reduced copy of this dataset.
virtual Int_t numEntries() const
Return number of entries in dataset, i.e., count unweighted entries.
virtual void add(const RooArgSet &row, double weight=1)=0
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:40
bool canBeExtended() const
If true, PDF can provide extended likelihood term.
Definition RooAbsPdf.h:219
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:324
Abstract base class for objects that represent a real value and implements functionality common to al...
Definition RooAbsReal.h:59
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:103
virtual void forceNumInt(bool flag=true)
Definition RooAbsReal.h:169
Efficient implementation of a sum of PDFs of the form.
Definition RooAddPdf.h:33
Calculates the sum of a set of RooAbsReal terms, or when constructed with two sets,...
Definition RooAddition.h:27
RooArgList is a container object that can hold multiple RooAbsArg objects.
Definition RooArgList.h:22
RooAbsArg * at(Int_t idx) const
Return object at given index, or nullptr if index is out of range.
Definition RooArgList.h:110
Abstract interface for RooAbsArg proxy classes.
Definition RooArgProxy.h:24
RooArgSet is a container object that can hold multiple RooAbsArg objects.
Definition RooArgSet.h:55
RooArgSet * snapshot(bool deepCopy=true) const
Use RooAbsCollection::snapshot(), but return as RooArgSet.
Definition RooArgSet.h:178
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
Represents a constant real-valued object.
Definition RooConstVar.h:23
static TClass * Class()
Container class to hold N-dimensional binned data.
Definition RooDataHist.h:39
Container class to hold unbinned data.
Definition RooDataSet.h:57
RooFitResult is a container class to hold the input and output of a PDF fit to a dataset.
void setCovQual(Int_t val)
const TMatrixDSym & covarianceMatrix() const
Return covariance matrix.
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...
void setCovarianceMatrix(TMatrixDSym &V)
Store externally provided correlation matrix in this RooFitResult ;.
const RooArgList & constPars() const
Return list of constant parameters.
void SetName(const char *name) override
Change name of RooFitResult object.
Int_t covQual() const
Return MINUIT quality code of covariance matrix.
const RooArgList & floatParsFinal() const
Return list of floating parameters after fit.
void setFinalParList(const RooArgList &list)
Fill the list of final values of the floating parameters.
UInt_t numStatusHistory() const
A RooFormulaVar is a generic implementation of a real-valued object, which takes a RooArgList of serv...
Plain Gaussian p.d.f.
Definition RooGaussian.h:24
Implementation of a probability density function that takes a RooArgList of servers and a C++ express...
A real-valued function sampled from a multidimensional histogram.
Definition RooHistFunc.h:31
bool isBinnedDistribution(const RooArgSet &) const override
Tests if the distribution is binned. Unless overridden by derived classes, this always returns false.
Definition RooHistFunc.h:95
When using RooFit, statistical models can be conveniently handled and stored as a RooWorkspace.
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.
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:19
Efficient implementation of a product of PDFs of the form.
Definition RooProdPdf.h:33
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:60
static TClass * Class()
double getError() const
Definition RooRealVar.h:58
bool hasError(bool allowZero=true) const
Definition RooRealVar.h:59
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 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 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.
bool removeSet(const char *name)
Remove a named set from the workspace.
static TClass * Class()
RooFactoryWSTool & factory()
Return instance to factory tool.
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:298
virtual Style_t GetTitleFont() const
Definition TAttAxis.h:47
virtual void SetLabelSize(Float_t size=0.04)
Set size of axis labels.
Definition TAttAxis.cxx:203
virtual void SetTitleSize(Float_t size=0.04)
Set size of axis title.
Definition TAttAxis.cxx:309
virtual Float_t GetTitleSize() const
Definition TAttAxis.h:44
virtual Float_t GetLabelSize() const
Definition TAttAxis.h:41
virtual Float_t GetTickLength() const
Definition TAttAxis.h:45
virtual Float_t GetTitleOffset() const
Definition TAttAxis.h:43
virtual void SetTickLength(Float_t length=0.03)
Set tick mark length.
Definition TAttAxis.cxx:284
virtual void SetNdivisions(Int_t n=510, Bool_t optim=kTRUE)
Set the number of divisions for this axis.
Definition TAttAxis.cxx:233
Fill Area Attributes class.
Definition TAttFill.h:19
virtual Color_t GetFillColor() const
Return the fill area color.
Definition TAttFill.h:30
virtual Style_t GetFillStyle() const
Return the fill area style.
Definition TAttFill.h:31
virtual void SetFillColor(Color_t fcolor)
Set the fill area color.
Definition TAttFill.h:37
virtual void SetFillStyle(Style_t fstyle)
Set the fill area style.
Definition TAttFill.h:39
Line Attributes class.
Definition TAttLine.h:18
virtual void SetLineColor(Color_t lcolor)
Set the line color.
Definition TAttLine.h:40
Marker Attributes class.
Definition TAttMarker.h:19
virtual void SetMarkerSize(Size_t msize=1)
Set the marker size.
Definition TAttMarker.h:45
virtual void SetTextAlign(Short_t align=11)
Set the text alignment.
Definition TAttText.h:42
virtual Font_t GetTextFont() const
Return the text font.
Definition TAttText.h:35
virtual void SetTextColor(Color_t tcolor=1)
Set the text color.
Definition TAttText.h:44
virtual void SetTextFont(Font_t tfont=62)
Set the text font.
Definition TAttText.h:46
virtual void SetTextSize(Float_t tsize=1)
Set the text size.
Definition TAttText.h:47
Class to manage histogram axis.
Definition TAxis.h:31
virtual void LabelsOption(Option_t *option="h")
Set option(s) to draw axis with labels option can be:
Definition TAxis.cxx:662
virtual void SetBinLabel(Int_t bin, const char *label)
Set label for bin.
Definition TAxis.cxx:886
Bool_t IsAlphanumeric() const
Definition TAxis.h:88
Bool_t IsVariableBinSize() const
Definition TAxis.h:142
const char * GetTitle() const override
Returns title of object.
Definition TAxis.h:135
TAxis()
Default constructor.
Definition TAxis.cxx:50
const TArrayD * GetXbins() const
Definition TAxis.h:136
Double_t GetXmax() const
Definition TAxis.h:140
const char * GetBinLabel(Int_t bin) const
Return label for bin.
Definition TAxis.cxx:440
virtual void Set(Int_t nbins, Double_t xmin, Double_t xmax)
Initialize axis with fix bins.
Definition TAxis.cxx:794
virtual Int_t FindFixBin(Double_t x) const
Find bin number corresponding to abscissa x.
Definition TAxis.cxx:419
virtual const char * GetTimeFormatOnly() const
Return only the time format from the string fTimeFormat.
Definition TAxis.cxx:599
Double_t GetXmin() const
Definition TAxis.h:139
Int_t GetNbins() const
Definition TAxis.h:125
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:1516
TClass instances represent classes, structs and namespaces in the ROOT type system.
Definition TClass.h:81
Bool_t InheritsFrom(const char *cl) const override
Return kTRUE if this class inherits from a class with name "classname".
Definition TClass.cxx:4874
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:2968
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:1455
static Int_t GetNumberOfColors()
Static function returning number of colors in the color palette.
Definition TColor.cxx:1475
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:223
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:736
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:233
A ROOT file is an on-disk file, usually with extension .root, that stores objects in a file-system-li...
Definition TFile.h:53
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:195
ROOT GUI Window base class.
Definition TGWindow.h:23
virtual void SetName(const char *name)
Definition TGWindow.h:121
The axis painter class.
Definition TGaxis.h:24
virtual void SetTitle(const char *title="")
Change the title of the axis.
Definition TGaxis.cxx:2942
virtual void ImportAxisAttributes(TAxis *axis)
Internal method to import TAxis attributes to this TGaxis.
Definition TGaxis.cxx:955
TGraph with asymmetric error bars.
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.
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:98
virtual void SetPoint(Int_t i, Double_t x, Double_t y)
Set x and y values for point number i.
Definition TGraph.cxx:2325
Int_t GetN() const
Definition TGraph.h:131
void SetName(const char *name="") override
Set graph name.
Definition TGraph.cxx:2364
void Draw(Option_t *chopt="") override
Draw this graph with its current attributes.
Definition TGraph.cxx:814
virtual Double_t GetPointY(Int_t i) const
Get y value for point i.
Definition TGraph.cxx:1538
void SetTitle(const char *title="") override
Change (i.e.
Definition TGraph.cxx:2380
virtual void SetPointX(Int_t i, Double_t x)
Set x value for point i.
Definition TGraph.cxx:2349
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:2284
1-D histogram with a double per channel (see TH1 documentation)
Definition TH1.h:670
1-D histogram with a float per channel (see TH1 documentation)
Definition TH1.h:622
TH1 is the base class of all histogram classes in ROOT.
Definition TH1.h:59
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:8928
virtual Double_t GetBinCenter(Int_t bin) const
Return bin center for 1D histogram.
Definition TH1.cxx:9132
void SetTitle(const char *title) override
Change/set the title.
Definition TH1.cxx:6709
virtual Int_t GetNbinsY() const
Definition TH1.h:298
virtual Double_t GetBinError(Int_t bin) const
Return value of error associated to bin number bin.
Definition TH1.cxx:9054
static void AddDirectory(Bool_t add=kTRUE)
Sets the flag controlling the automatic add of histograms in memory.
Definition TH1.cxx:1294
@ kNoTitle
Don't draw the histogram title.
Definition TH1.h:170
virtual void Reset(Option_t *option="")
Reset this histogram: contents, errors, etc.
Definition TH1.cxx:7094
TAxis * GetXaxis()
Definition TH1.h:324
TObject * FindObject(const char *name) const override
Search object named name in the list of functions.
Definition TH1.cxx:3857
virtual Int_t GetNbinsX() const
Definition TH1.h:297
virtual void SetMaximum(Double_t maximum=-1111)
Definition TH1.h:404
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:9197
virtual Int_t Fill(Double_t x)
Increment bin with abscissa X by 1.
Definition TH1.cxx:3344
TAxis * GetYaxis()
Definition TH1.h:325
void Draw(Option_t *option="") override
Draw this histogram with options.
Definition TH1.cxx:3066
virtual void SetMinimum(Double_t minimum=-1111)
Definition TH1.h:405
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:9213
TList * GetListOfFunctions() const
Definition TH1.h:244
void SetName(const char *name) override
Change the name of this histogram.
Definition TH1.cxx:8951
virtual Double_t GetBinContent(Int_t bin) const
Return content of bin number bin.
Definition TH1.cxx:5052
virtual void Scale(Double_t c1=1, Option_t *option="")
Multiply this histogram by a constant c1.
Definition TH1.cxx:6595
TObject * Clone(const char *newname="") const override
Make a complete copy of the underlying object.
Definition TH1.cxx:2752
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:2840
virtual void Sumw2(Bool_t flag=kTRUE)
Create structure to store sum of squares of weights.
Definition TH1.cxx:9011
static Bool_t AddDirectoryStatus()
Static function: cannot be inlined on Windows/NT.
Definition TH1.cxx:754
virtual void SetStats(Bool_t stats=kTRUE)
Set statistics option on/off.
Definition TH1.cxx:8981
2-D histogram with a double per channel (see TH1 documentation)
Definition TH2.h:357
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:366
void Draw(Option_t *chopt="") override
Draw this stack with its current attributes.
Definition THStack.cxx:453
Book space in a file, create I/O buffers, to fill them, (un)compress them.
Definition TKey.h:28
virtual const char * GetClassName() const
Definition TKey.h:75
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:320
void SetNColumns(Int_t nColumns)
Set the number of columns for the legend.
Definition TLegend.cxx:606
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:68
virtual void SetX2(Double_t x2)
Definition TLine.h:66
Double_t GetY1() const
Definition TLine.h:52
virtual void SetX1(Double_t x1)
Definition TLine.h:65
virtual void SetY1(Double_t y1)
Definition TLine.h:67
Double_t GetY2() const
Definition TLine.h:53
A doubly linked list.
Definition TList.h:38
void Add(TObject *obj) override
Definition TList.h:83
TObject * At(Int_t idx) const override
Returns the object at position idx. Returns 0 if idx is out of range.
Definition TList.cxx:355
Int_t GetNrows() const
Int_t GetNcols() const
TMatrixTSym.
Definition TMatrixTSym.h:36
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:74
virtual void SetTitle(const char *title="")
Set the title of the TNamed.
Definition TNamed.cxx:164
const char * GetName() const override
Returns name of object.
Definition TNamed.h:47
const char * GetTitle() const override
Returns title of object.
Definition TNamed.h:48
void Clear(Option_t *option="") override
Set name and title to empty strings ("").
Definition TNamed.cxx:64
virtual void SetName(const char *name)
Set the name of the TNamed.
Definition TNamed.cxx:140
virtual void SetNameTitle(const char *name, const char *title)
Set all the TNamed parameters (name and title).
Definition TNamed.cxx:154
Mother of all ROOT objects.
Definition TObject.h:41
virtual void Inspect() const
Dump contents of this object in a graphics canvas.
Definition TObject.cxx:545
virtual const char * GetName() const
Returns name of object.
Definition TObject.cxx:438
R__ALWAYS_INLINE Bool_t TestBit(UInt_t f) const
Definition TObject.h:199
virtual TObject * Clone(const char *newname="") const
Make a clone of an object using the Streamer facility.
Definition TObject.cxx:223
virtual const char * ClassName() const
Returns name of class to which the object belongs.
Definition TObject.cxx:207
virtual void Warning(const char *method, const char *msgfmt,...) const
Issue warning message.
Definition TObject.cxx:973
virtual TObject * FindObject(const char *name) const
Must be redefined in derived classes.
Definition TObject.cxx:402
virtual void Delete(Option_t *option="")
Delete this object.
Definition TObject.cxx:248
void SetBit(UInt_t f, Bool_t set)
Set or unset the user status bits as specified in f.
Definition TObject.cxx:780
virtual Bool_t InheritsFrom(const char *classname) const
Returns kTRUE if object inherits from class "classname".
Definition TObject.cxx:524
virtual void Error(const char *method, const char *msgfmt,...) const
Issue error message.
Definition TObject.cxx:987
virtual const char * GetTitle() const
Returns title of object.
Definition TObject.cxx:482
virtual void Draw(Option_t *option="")
Default Draw method for all objects.
Definition TObject.cxx:274
virtual void Print(Option_t *option="") const
This method must be overridden when a class wants to print itself.
Definition TObject.cxx:636
@ kCanDelete
if object in a list can be deleted
Definition TObject.h:62
virtual void Info(const char *method, const char *msgfmt,...) const
Issue info message.
Definition TObject.cxx:961
The most important graphics class in the ROOT system.
Definition TPad.h:28
A Pave (see TPave) with text, lines or/and boxes inside.
Definition TPaveText.h:21
virtual TText * AddText(Double_t x1, Double_t y1, const char *label)
Add a new Text line to this pavetext at given coordinates.
virtual void SetMargin(Float_t margin=0.05)
Definition TPaveText.h:62
virtual void SetY1NDC(Double_t y1)
Definition TPave.h:84
virtual void ConvertNDCtoPad()
Convert pave coordinates from NDC to Pad coordinates.
Definition TPave.cxx:139
virtual void SetName(const char *name="")
Definition TPave.h:79
virtual void SetBorderSize(Int_t bordersize=4)
Sets the border size of the TPave box and shadow.
Definition TPave.h:77
virtual void SetY2NDC(Double_t y2)
Definition TPave.h:85
Draw a Pie Chart,.
Definition TPie.h:23
void SetEntryVal(Int_t, Double_t)
Set the value of a slice.
Definition TPie.cxx:1250
Int_t GetEntries()
Definition TPie.h:76
void SetEntryFillColor(Int_t, Int_t)
Set the color for the slice "i".
Definition TPie.cxx:1226
void SetRadius(Double_t)
Set the pie chart's radius' value.
Definition TPie.cxx:1341
void SetEntryLabel(Int_t, const char *text="Slice")
Set slice number "i" label.
Definition TPie.cxx:1193
void Draw(Option_t *option="l") override
Draw the pie chart.
Definition TPie.cxx:277
This is the ROOT implementation of the Qt object communication mechanism (see also http://www....
Definition TQObject.h:48
Regular expression class.
Definition TRegexp.h:31
This class creates a ROOT object browser, constituted by three main tabs.
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:139
Ssiz_t Length() const
Definition TString.h:417
Bool_t IsDec() const
Returns true if all characters in string are decimal digits (0-9).
Definition TString.cxx:1940
void ToLower()
Change string to lower-case.
Definition TString.cxx:1182
Int_t Atoi() const
Return integer value of string.
Definition TString.cxx:1988
Bool_t EndsWith(const char *pat, ECaseCompare cmp=kExact) const
Return true if string ends with the specified string.
Definition TString.cxx:2244
Double_t Atof() const
Return floating-point value contained in string.
Definition TString.cxx:2054
TString & Replace(Ssiz_t pos, Ssiz_t n, const char *s)
Definition TString.h:694
const char * Data() const
Definition TString.h:376
TString & ReplaceAll(const TString &s1, const TString &s2)
Definition TString.h:704
Ssiz_t Last(char c) const
Find last occurrence of a character c.
Definition TString.cxx:931
Bool_t BeginsWith(const char *s, ECaseCompare cmp=kExact) const
Definition TString.h:623
Int_t CountChar(Int_t c) const
Return number of times character c occurs in the string.
Definition TString.cxx:515
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:2378
Bool_t Contains(const char *pat, ECaseCompare cmp=kExact) const
Definition TString.h:632
Ssiz_t Index(const char *pat, Ssiz_t i=0, ECaseCompare cmp=kExact) const
Definition TString.h:651
TStyle objects may be created to define special styles.
Definition TStyle.h:29
void SetPaintTextFormat(const char *format="g")
Definition TStyle.h:386
Int_t GetOptTitle() const
Definition TStyle.h:246
Float_t GetLabelSize(Option_t *axis="X") const
Return label size.
Definition TStyle.cxx:1145
Float_t GetPadRightMargin() const
Definition TStyle.h:214
Style_t GetTitleFont(Option_t *axis="X") const
Return title font.
Definition TStyle.cxx:1216
Bool_t GetHistMinimumZero() const
Definition TStyle.h:237
Float_t GetPadLeftMargin() const
Definition TStyle.h:213
Float_t GetTitleYSize() const
Definition TStyle.h:279
static TClass * Class()
Double_t GetHistTopMargin() const
Definition TStyle.h:238
Float_t GetPadBottomMargin() const
Definition TStyle.h:211
const char * GetPaintTextFormat() const
Definition TStyle.h:250
Float_t GetPadTopMargin() const
Definition TStyle.h:212
virtual Bool_t ExpandPathName(TString &path)
Expand a pathname getting rid of special shell characters like ~.
Definition TSystem.cxx:1274
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:1296
virtual Bool_t ProcessEvents()
Process pending events (GUI, timers, sockets).
Definition TSystem.cxx:416
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:571
static TVirtualPadEditor * GetPadEditor(Bool_t load=kTRUE)
Returns the pad editor dialog. Static method.
TVirtualPad is an abstract base class for the Pad and Canvas classes.
Definition TVirtualPad.h:51
virtual 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
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.
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 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
#define F(x, y, z)
bool EndsWith(const std::string &theString, const std::string &theSubstring)
TClass * GetClass(T *)
Definition TClass.h:663
The namespace RooFit contains mostly switches that change the behaviour of functions of PDFs (or othe...
Definition JSONIO.h:26
MsgLevel
Verbosity level for RooMsgService::StreamConfig in RooMsgService.
@ NumIntegration
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:2193
Definition graph.py:1
#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:2345
#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:223
TLegend * getLegend(bool create=true, bool doPaint=false)