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) {
265 delete[] _path;
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);
273 RooMsgService::instance().setGlobalKillBelow(RooFit::WARNING);
274 if (!tool.importJSON(pathName.Data())) {
275 Error("xRooNode", "Error reading json workspace %s", name);
276 fComp.reset();
277 }
278 RooMsgService::instance().setGlobalKillBelow(msglevel);
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>())) {
323 .getStream(RooFit::INFO)
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 // load list of colors if there is one
336 if (auto colors = dynamic_cast<TSeqCollection *>(_ws->obj(gROOT->GetListOfColors()->GetName()))) {
337 gROOT->GetListOfColors()->Clear();
338 for (auto col : *colors) {
339 gROOT->GetListOfColors()->Add(col);
340 }
341 }
342
343 // use the datasets if any to 'mark' observables
344 int checkCount = 0;
345 for (auto &d : _ws->allData()) {
346 for (auto &a : *d->get()) {
347 if (auto v = _ws->var(a->GetName()); v) {
348 v->setAttribute("obs");
349 } else if (auto c = _ws->cat(a->GetName()); c) {
350 c->setAttribute("obs");
351 }
352 }
353 // count how many ds are checked ... if none are checked will check the first
354 checkCount += d->TestBit(1 << 20);
355 }
356
357 if (checkCount == 0 && !_ws->allData().empty())
358 _ws->allData().back()->SetBit(1 << 20, true);
359
360 if (auto _set = dynamic_cast<RooArgSet *>(GETWSSNAPSHOTS(_ws).find("NominalParamValues")); _set) {
361 for (auto s : *_set) {
362 if (auto v = dynamic_cast<RooRealVar *>(s); v) {
363 _ws->var(s->GetName())->setStringAttribute("nominal", TString::Format("%f", v->getVal()));
364 }
365 }
366 }
367
368 // also flag global observables ... relies on ModelConfig existences
370 for (auto &[k, v] : GETWSSETS(_ws)) {
371 if (k == "globalObservables" || TString(k).EndsWith("_GlobalObservables")) {
372 for (auto &s : v) {
373 _allGlobs.add(*s);
374 s->setAttribute("obs");
375 s->setAttribute("global");
376 }
377 } else if (TString(k).EndsWith("_Observables")) {
378 const_cast<RooArgSet &>(v).setAttribAll("obs");
379 } else if (TString(k).EndsWith("_POI")) {
380 for (auto &s : v) {
381 s->setAttribute("poi");
382 auto _v = dynamic_cast<RooRealVar *>(s);
383 if (!_v)
384 continue;
385 // if (!_v->hasRange("physical")) {
386 // _v->setRange("physical", 0, std::numeric_limits<double>::infinity());
387 // // ensure range of poi is also straddling 0
388 // if (_v->getMin() >= 0)
389 // _v->setMin(-1e-5);
390 // }
391 }
392 } else if (TString(k).EndsWith("_NuisParams")) {
393 const_cast<RooArgSet &>(v).setAttribAll("np");
394 }
395 }
396 if (!_allGlobs.empty() && GETWSSETS(_ws).count("globalObservables") == 0) {
397 _ws->defineSet("globalObservables", _allGlobs);
398 }
399
400 // now check if any pars don't have errors defined (not same as error=0) ... if so, use the first pdf (if there is
401 // one) to try setting values from
402 if (!_ws->allPdfs().empty()) {
403 std::set<RooRealVar *> noErrorPars;
404 std::string parNames;
405 for (auto &p : np()) { // infer errors on all floating non-poi parameters
406 auto v = p->get<RooRealVar>();
407 if (!v)
408 continue;
409 if (!v->hasError()) {
410 noErrorPars.insert(v);
411 if (!parNames.empty())
412 parNames += ",";
413 parNames += v->GetName();
414 }
415 }
416 if (!noErrorPars.empty()) {
417 Warning("xRooNode",
418 "Inferring initial errors of %d parameters (%s%s) (give all nuisance parameters an error to avoid "
419 "this msg)",
420 int(noErrorPars.size()), (*noErrorPars.begin())->GetName(), (noErrorPars.size() > 1) ? ",..." : "");
421 // get the first top-level pdf
422 browse();
423 for (auto &a : *this) {
424 if (noErrorPars.empty()) {
425 break;
426 }
427 if (a->fFolder == "!pdfs") {
428 try {
429 auto fr = a->floats().reduced(parNames).fitResult("prefit");
430 if (auto _fr = fr.get<RooFitResult>(); _fr) {
431 std::set<RooRealVar *> foundPars;
432 for (auto &v : noErrorPars) {
433 if (auto arg = dynamic_cast<RooRealVar *>(_fr->floatParsFinal().find(v->GetName()));
434 arg && arg->hasError()) {
435 v->setError(arg->getError());
436 foundPars.insert(v);
437 }
438 }
439 for (auto &v : foundPars) {
440 noErrorPars.erase(v);
441 }
442 }
443 } catch (...) {
444 }
445 }
446 }
447 }
448 }
449 }
450
451 if (strlen(GetTitle()) == 0) {
452 if (fComp) {
453 TNamed::SetTitle(fComp->GetTitle());
454 } else {
455 TNamed::SetTitle(GetName());
456 }
457 }
458}
459
460xRooNode::xRooNode(const TObject &comp, const std::shared_ptr<xRooNode> &parent)
461 : xRooNode(/*[](const TObject& c) {
462c.InheritsFrom("RooAbsArg");
463if (s) {
464return (s->getStringAttribute("alias")) ? s->getStringAttribute("alias") : c.GetName();
465}
466return c.GetName();
467}(comp)*/
468 (comp.InheritsFrom("RooAbsArg") && dynamic_cast<const RooAbsArg *>(&comp)->getStringAttribute("alias"))
469 ? dynamic_cast<const RooAbsArg *>(&comp)->getStringAttribute("alias")
470 : comp.GetName(),
471 std::shared_ptr<TObject>(const_cast<TObject *>(&comp), [](TObject *) {}), parent)
472{
473}
474
475xRooNode::xRooNode(const std::shared_ptr<TObject> &comp, const std::shared_ptr<xRooNode> &parent)
476 : xRooNode(
477 [&]() {
478 if (auto a = std::dynamic_pointer_cast<RooAbsArg>(comp); a && a->getStringAttribute("alias"))
479 return a->getStringAttribute("alias");
480 if (comp)
481 return comp->GetName();
482 return "";
483 }(),
484 comp, parent)
485{
486}
487
489
490void xRooNode::Checked(TObject *obj, bool val)
491{
492 if (obj != this)
493 return;
494
495 // cycle through states:
496 // unhidden and selected: tick, no uline
497 // hidden and unselected: notick, uline
498 // unhidden and unselected: tick, uline
499 if (auto o = get<RooAbsReal>(); o) {
500 if (o->isSelectedComp() && !val) {
501 // deselecting and hiding
502 o->selectComp(val);
503 o->setAttribute("hidden");
504 } else if (!o->isSelectedComp() && !val) {
505 // selecting
506 o->selectComp(!val);
507 } else if (val) {
508 // unhiding but keeping unselected
509 o->setAttribute("hidden", false);
510 }
511 auto item = GetTreeItem(nullptr);
512 item->CheckItem(!o->getAttribute("hidden"));
513 if (o->isSelectedComp()) {
514 item->ClearColor();
515 } else {
516 item->SetColor(kGray);
517 }
518 return;
519 }
520
521 if (auto o = get(); o) {
522 // if (o->TestBit(1<<20)==val) return; // do nothing
523 o->SetBit(1 << 20, val); // TODO: check is 20th bit ok to play with?
524 if (auto fr = get<RooFitResult>(); fr) {
525 if (auto _ws = ws(); _ws) {
526 if (val) {
527 // ensure fit result is in genericObjects list ... if not, add a copy ...
528 if (!_ws->genobj(fr->GetName())) {
529 _ws->import(*fr);
530 if (auto wfr = dynamic_cast<RooFitResult *>(_ws->genobj(fr->GetName()))) {
531 fr = wfr;
532 }
533 }
534 RooArgSet _allVars = _ws->allVars();
535 _allVars = fr->floatParsFinal();
536 _allVars = fr->constPars();
537 for (auto &i : fr->floatParsInit()) {
538 auto v = dynamic_cast<RooRealVar *>(_allVars.find(i->GetName()));
539 if (v)
540 v->setStringAttribute("initVal", TString::Format("%f", dynamic_cast<RooRealVar *>(i)->getVal()));
541 }
542 // uncheck all other fit results
543 for (auto oo : _ws->allGenericObjects()) {
544 if (auto ffr = dynamic_cast<RooFitResult *>(oo); ffr && ffr != fr) {
545 ffr->ResetBit(1 << 20);
546 }
547 }
548 } else
549 _ws->allVars() = fr->floatParsInit();
550 }
551 if (auto item = GetTreeItem(nullptr); item) {
552 // update check marks on siblings
553 if (auto first = item->GetParent()->GetFirstChild()) {
554 do {
555 if (first->HasCheckBox()) {
556 auto _obj = static_cast<xRooNode *>(first->GetUserData());
557 first->CheckItem(_obj->get() && _obj->get()->TestBit(1 << 20));
558 }
559 } while ((first = first->GetNextSibling()));
560 }
561 }
562 }
563 }
564}
565
567{
568 static bool blockBrowse = false;
569 if (blockBrowse)
570 return;
571 if (b == nullptr) {
572 auto b2 = dynamic_cast<TBrowser *>(gROOT->GetListOfBrowsers()->Last());
573 if (!b2 || !b2->GetBrowserImp()) { // no browser imp if browser was closed
574 blockBrowse = true;
575 gEnv->SetValue("X11.UseXft", "no"); // for faster x11
576 gEnv->SetValue("X11.Sync", "no");
577 gEnv->SetValue("X11.FindBestVisual", "no");
578 gEnv->SetValue("Browser.Name", "TRootBrowser"); // forces classic root browser (in 6.26 onwards)
579 gEnv->SetValue("Canvas.Name", "TRootCanvas");
580 b2 = new TBrowser("nodeBrowser", this, "RooFit Browser");
581 blockBrowse = false;
582 } else if (strcmp(b2->GetName(), "nodeBrowser") == 0) {
583 blockBrowse = true;
584 b2->BrowseObject(this);
585 blockBrowse = false;
586 } else {
587 auto _b = dynamic_cast<TGFileBrowser *>(GETACTBROWSER(dynamic_cast<TRootBrowser *>(b2->GetBrowserImp())));
588 if (_b)
589 _b->AddFSDirectory("Workspaces", nullptr, "SetRootDir");
590 /*auto l = Node2::Class()->GetMenuList();
591 auto o = new CustomClassMenuItem(TClassMenuItem::kPopupUserFunction,Node2::Class(),
592 "blah blah blah","BlahBlah",0,"Option_t*",-1,true);
593 //o->SetCall(o,"BlahBlah","Option_t*",-1);
594 l->AddFirst(o);*/
595 // b->BrowseObject(this);
596 _b->GotoDir(nullptr);
597 _b->Add(this, GetName());
598 // b->Add(this);
599 }
600 return;
601 }
602
603 if (auto item = GetTreeItem(b); item) {
604 if (!item->IsOpen() && IsFolder())
605 return; // no need to rebrowse if closing
606 // update check marks on any child items
607 if (auto first = item->GetFirstChild()) {
608 do {
609 if (first->HasCheckBox()) {
610 auto _obj = static_cast<xRooNode *>(first->GetUserData());
611 first->CheckItem(_obj->get() &&
612 (_obj->get()->TestBit(1 << 20) ||
613 (_obj->get<RooAbsArg>() && !_obj->get<RooAbsArg>()->getAttribute("hidden"))));
614 }
615 } while ((first = first->GetNextSibling()));
616 }
617 }
618
619 browse();
620
621 // for top-level pdfs default to having the .vars browsable too
622 if (get<RooAbsPdf>() && fFolder == "!pdfs" && !_IsShowVars_()) {
623 fBrowsables.push_back(std::make_shared<xRooNode>(vars()));
624 }
625
626 if (auto _fr = get<RooFitResult>(); _fr && fBrowsables.empty()) {
627 // have some common drawing options
628 fBrowsables.push_back(std::make_shared<xRooNode>(".Draw(\"pull\")", nullptr, *this));
629 fBrowsables.push_back(std::make_shared<xRooNode>(".Draw(\"corrcolztext\")", nullptr, *this));
630 if (std::unique_ptr<RooAbsCollection>(_fr->floatParsFinal().selectByAttrib("poi", true))->size() == 1) {
631 fBrowsables.push_back(std::make_shared<xRooNode>(".Draw(\"impact\")", nullptr, *this));
632 }
633 }
634
635 if (empty() && fBrowsables.empty()) {
636 try {
637 if (auto s = get<TStyle>()) {
638 s->SetFillAttributes();
639 if (auto ed = dynamic_cast<TGedEditor *>(TVirtualPadEditor::GetPadEditor())) {
640 ed->SetModel(gPad, s, kButton1Down, true);
641 }
642 } else if (TString(GetName()).BeginsWith(".Draw(\"") && fParent) {
643 fParent->Draw(TString(TString(GetName())(7, strlen(GetName()) - 9)) + b->GetDrawOption());
644 } else {
645 Draw(b->GetDrawOption());
646 }
647 } catch (const std::exception &e) {
648 new TGMsgBox(
649 gClient->GetRoot(),
650 (gROOT->GetListOfBrowsers()->At(0))
651 ? dynamic_cast<TGWindow *>(static_cast<TBrowser *>(gROOT->GetListOfBrowsers()->At(0))->GetBrowserImp())
652 : gClient->GetRoot(),
653 "Exception", e.what(),
654 kMBIconExclamation); // deletes self on dismiss?
655 }
656 }
657
658 bool hasFolders = false;
659 if (strlen(GetName()) > 0 && GetName()[0] != '!') { // folders don't have folders
660 for (auto &c : *this) {
661 if (!c->fFolder.empty()) {
662 hasFolders = true;
663 break;
664 }
665 }
666 }
667 // auto _ws = get<RooWorkspace>();
668 if (/*_ws*/ hasFolders) {
669 // organize in folders
670 auto _folders = find(".folders");
671 if (!_folders) {
672 _folders = emplace_back(std::make_shared<xRooNode>(".folders", nullptr, *this));
673 }
674 // ensure entry in folders for every folder type ...
675 for (auto &v : *this) {
676 if (!v->fFolder.empty() && !_folders->find(v->fFolder, false)) {
677 _folders->emplace_back(std::make_shared<xRooNode>(v->fFolder.c_str(), nullptr, *this));
678 }
679 }
680 // now just add all the folders
681 for (auto &v : *_folders) {
682 TString _name = v->GetName();
683 if (_name.BeginsWith('!'))
684 _name = _name(1, _name.Length()); // strip ! from display
685 b->Add(v.get(), _name);
686 }
687 }
688
689 for (auto &v : *this) {
690 if (hasFolders && !v->fFolder.empty())
691 continue; // in the folders
692 if (strcmp(v->GetName(), ".folders") == 0)
693 continue; // never 'browse' the folders property
694 auto _fr = v->get<RooFitResult>();
695 int _checked = (v->get<RooAbsData>() || _fr) ? v->get()->TestBit(1 << 20) : -1;
696 if (_fr && ((_fr->status() == 0 && _fr->numStatusHistory() == 0) || (_fr->floatParsFinal().empty()))) {
697 // this is a "PARTIAL" fit result ... don't allow it to be selected
698 _checked = -1;
699 }
700 if (v->get<RooAbsPdf>() && get<RooSimultaneous>())
701 _checked = !v->get<RooAbsArg>()->getAttribute("hidden");
702 TString _name = v->GetName();
703 if (v->get() && _name.BeginsWith(TString(v->get()->ClassName()) + "::")) {
704 _name = _name(strlen(v->get()->ClassName()) + 2, _name.Length());
705 }
706 if (_name.BeginsWith(".")) {
707 // property node -- display the name of the contained object
708 if (v->get()) {
709 _name = TString::Format("%s: %s::%s", _name.Data(), v->get()->ClassName(),
710 (v->get<RooAbsArg>() && v->get<RooAbsArg>()->getStringAttribute("alias"))
711 ? v->get<RooAbsArg>()->getStringAttribute("alias")
712 : v->get()->GetName());
713 }
714 } else if (v->get() && !v->get<TFile>() && !TString(v->GetName()).BeginsWith('/'))
715 _name = TString::Format("%s::%s", v->get()->ClassName(), _name.Data());
716 if (auto _type = v->GetNodeType(); strlen(_type)) {
717 // decided not to show const values until figure out how to update if value changes
718 /*if (TString(_type)=="Const") _name += TString::Format(" [%s=%g]",_type,v->get<RooConstVar>()->getVal());
719 else*/
720 _name += TString::Format(" [%s]", _type);
721 }
722 if (auto fv = v->get<RooFormulaVar>()) {
723 TString formu = TString::Format(" [%s]", fv->expression());
724 for (size_t i = 0; i < fv->dependents().size(); i++) {
725 formu.ReplaceAll(TString::Format("x[%zu]", i), fv->dependents()[i].GetName());
726 }
727 _name += formu;
728 } else if (auto gv = v->get<RooGenericPdf>()) {
729 TString formu = TString::Format(" [%s]", gv->expression());
730 for (size_t i = 0; i < gv->dependents().size(); i++) {
731 formu.ReplaceAll(TString::Format("x[%zu]", i), gv->dependents()[i].GetName());
732 }
733 _name += formu;
734 }
735 // tool tip defaults to displaying name and title, so temporarily set name to obj name if has one
736 // and set title to the object type
737 TString nameSave(v->TNamed::GetName());
738 TString titleSave(v->TNamed::GetTitle());
739 if (auto o = v->get(); o)
740 v->TNamed::SetNameTitle(o->GetName(), o->ClassName());
741 b->Add(v.get(), _name, _checked);
742 if (auto o = v->get(); o)
743 v->TNamed::SetNameTitle(nameSave, titleSave);
744 if (_checked != -1) {
745 dynamic_cast<TQObject *>(b->GetBrowserImp())
746 ->Connect("Checked(TObject *, bool)", ClassName(), v.get(), "Checked(TObject *, bool)");
747 }
748 if (_fr) {
749 if (_fr->status() || _fr->covQual() != 3) { // snapshots or bad fits
750 v->GetTreeItem(b)->SetColor((_fr->numStatusHistory() || _fr->floatParsFinal().empty()) ? kRed : kBlue);
751 } else if (_fr->numStatusHistory() == 0) { // partial fit result ..
752 v->GetTreeItem(b)->SetColor(kGray);
753 }
754 }
755 if ((v->fFolder == "!np" || v->fFolder == "!poi")) {
756 if (v->get<RooAbsArg>()->getAttribute("Constant")) {
757 v->GetTreeItem(b)->SetColor(kGray);
758 } else
759 v->GetTreeItem(b)->ClearColor();
760 }
761 if (auto _htr = v->get<RooStats::HypoTestResult>(); _htr) {
762 // check for fit statuses
763 if (auto fits = _htr->GetFitInfo()) {
764 for (int i = 0; i < fits->numEntries(); i++) {
765 // if any fit (other than a genFit) is bad, flag point as bad
766 if (fits->get(i)->getCatIndex("type") != 5 && fits->get(i)->getRealValue("status") != 0) {
767 v->GetTreeItem(b)->SetColor(kRed);
768 break;
769 }
770 }
771 } else {
772 v->GetTreeItem(b)->SetColor(kBlue); // unknown fit status
773 }
774 }
775
776 // v.fBrowsers.insert(b);
777 }
778
779 // for pdfs, check for datasets too and add to list
780 /*if (get<RooAbsPdf>()) {
781 auto dsets = datasets();
782 if (!dsets.empty()) {
783 // check if already have .datasets() in browsables
784 bool found(false);
785 for(auto& p : fBrowsables) {
786 if (TString(p->GetName())==".datasets()") {found=true;
787 // add
788 break;
789 }
790 }
791 if (!found) {
792 fBrowsables.push_back(std::make_shared<xRooNode>(dsets));
793 }
794 }
795 }*/
796 // browse the browsables too
797 for (auto &v : fBrowsables) {
798 TString _name = v->GetName();
799 if (_name == ".memory")
800 continue; // hide the memory from browsing, if put in browsables
801 TString nameSave(v->TNamed::GetName());
802 TString titleSave(v->TNamed::GetTitle());
803 if (auto o = v->get(); o)
804 v->TNamed::SetNameTitle(o->GetName(), o->ClassName());
805 b->Add(v.get(), _name, -1);
806 if (auto o = v->get(); o)
807 v->TNamed::SetNameTitle(nameSave, titleSave);
808 }
809
810 b->SetSelected(this);
811}
812
814{
815 if (!set) {
816 // can't remove as causes a crash, need to remove from the browser first
817 /*for(auto itr = fBrowsables.begin(); itr != fBrowsables.end(); ++itr) {
818 if (strcmp((*itr)->GetName(),".vars")==0) {
819 fBrowsables.erase(itr);
820 }
821 }*/
822 } else {
823 auto v = std::make_shared<xRooNode>(vars());
824 fBrowsables.push_back(v);
825 if (auto l = GetListTree(nullptr)) {
826 l->AddItem(GetTreeItem(nullptr), v->GetName(), v.get());
827 }
828 }
829}
830
832{
833 for (auto &b : fBrowsables) {
834 if (strcmp(b->GetName(), ".vars") == 0)
835 return true;
836 }
837 return false;
838}
839
841{
842 if (strlen(GetName()) > 0 && GetName()[0] == '!')
843 return true;
844 if (strlen(GetName()) > 0 && GetName()[0] == '.' && !(TString(GetName()).BeginsWith(".Draw(\"")))
845 return true;
846 if (empty())
847 const_cast<xRooNode *>(this)->browse();
848 return !empty();
849}
850
851class Axis2 : public TAxis {
852
853public:
854 using TAxis::TAxis;
855 double GetBinWidth(Int_t bin) const override
856 {
857 if (auto v = var(); v)
858 return v->getBinWidth(bin - 1, GetName());
859 return 1;
860 }
861 double GetBinLowEdge(Int_t bin) const override
862 {
863 if (auto v = rvar(); v) {
864 return (bin == v->getBinning(GetName()).numBins() + 1) ? v->getBinning(GetName()).binHigh(bin - 2)
865 : v->getBinning(GetName()).binLow(bin - 1);
866 }
867 return bin - 1;
868 }
869 double GetBinUpEdge(Int_t bin) const override
870 {
871 if (auto v = rvar(); v)
872 return (bin == 0) ? v->getBinning(GetName()).binLow(bin) : v->getBinning(GetName()).binHigh(bin - 1);
873 return bin;
874 }
875
876 const char *GetTitle() const override
877 {
878 return (binning() && strlen(binning()->GetTitle())) ? binning()->GetTitle() : GetParent()->GetTitle();
879 }
880 void SetTitle(const char *title) override
881 {
882 if (binning()) {
883 const_cast<RooAbsBinning *>(binning())->SetTitle(title);
884 } else {
885 dynamic_cast<TNamed *>(GetParent())->SetTitle(title);
886 }
887 }
888
889 void Set(Int_t nbins, const double *xbins) override
890 {
891 if (auto v = dynamic_cast<RooRealVar *>(rvar()))
892 v->setBinning(RooBinning(nbins, xbins), GetName());
893 TAxis::Set(nbins, xbins);
894 }
895 void Set(Int_t nbins, const float *xbins) override
896 {
897 std::vector<double> bins(nbins + 1);
898 for (int i = 0; i <= nbins; i++)
899 bins.at(i) = xbins[i];
900 return Set(nbins, &bins[0]);
901 }
902 void Set(Int_t nbins, double xmin, double xmax) override
903 {
904 if (auto v = dynamic_cast<RooRealVar *>(rvar()))
905 v->setBinning(RooUniformBinning(xmin, xmax, nbins), GetName());
906 TAxis::Set(nbins, xmin, xmax);
907 }
908
909 const RooAbsBinning *binning() const { return var()->getBinningPtr(GetName()); }
910
911 Int_t FindFixBin(const char *label) const override { return TAxis::FindFixBin(label); }
912 Int_t FindFixBin(double x) const override { return (binning()) ? (binning()->binNumber(x) + 1) : x; }
913
914private:
915 RooAbsLValue *var() const { return dynamic_cast<RooAbsLValue *>(GetParent()); }
916 RooAbsRealLValue *rvar() const { return dynamic_cast<RooAbsRealLValue *>(GetParent()); }
917};
918
919std::shared_ptr<TObject> xRooNode::getObject(const std::string &name, const std::string &type) const
920{
921 // if (fParent) return fParent->getObject(name);
922
923 if (auto _owned = find(".memory"); _owned) {
924 for (auto &o : *_owned) {
925 if (name == o->GetName()) {
926 if (type.empty() || o->get()->InheritsFrom(type.c_str()))
927 return o->fComp;
928 }
929 }
930 }
931
932 // see if have a provider
933 auto _provider = fProvider;
934 auto _parent = fParent;
935 while (!_provider && _parent) {
936 _provider = _parent->fProvider;
937 _parent = _parent->fParent;
938 }
939 if (_provider)
940 return _provider->getObject(name, type);
941
942 if (ws()) {
943 std::shared_ptr<TObject> out;
944 if (auto arg = ws()->arg(name.c_str()); arg) {
945 auto _tmp = std::shared_ptr<TObject>(arg, [](TObject *) {});
946 if (!type.empty() && arg->InheritsFrom(type.c_str()))
947 return _tmp;
948 if (!out)
949 out = _tmp;
950 }
951 if (auto arg = ws()->data(name.c_str()); arg) {
952 auto _tmp = std::shared_ptr<TObject>(arg, [](TObject *) {});
953 if (!type.empty() && arg->InheritsFrom(type.c_str()))
954 return _tmp;
955 if (!out)
956 out = _tmp;
957 }
958 if (auto arg = ws()->genobj(name.c_str()); arg) {
959 auto _tmp = std::shared_ptr<TObject>(arg, [](TObject *) {});
960 if (!type.empty() && arg->InheritsFrom(type.c_str()))
961 return _tmp;
962 if (!out)
963 out = _tmp;
964 }
965 if (auto arg = ws()->embeddedData(name.c_str()); arg) {
966 auto _tmp = std::shared_ptr<TObject>(arg, [](TObject *) {});
967 if (!type.empty() && arg->InheritsFrom(type.c_str()))
968 return _tmp;
969 if (!out)
970 out = _tmp;
971 }
972 if (auto arg = GETWSSNAPSHOTS(ws()).find(name.c_str()); arg) {
973 auto _tmp = std::shared_ptr<TObject>(arg, [](TObject *) {});
974 if (!type.empty() && arg->InheritsFrom(type.c_str()))
975 return _tmp;
976 if (!out)
977 out = _tmp;
978 }
979 return out;
980 }
981 if (auto arg = get<RooAbsArg>()) {
982 // can try all nodes
983 RooArgSet nodes;
984 arg->treeNodeServerList(&nodes);
985 if (auto server = nodes.find(name.c_str())) {
986 return std::shared_ptr<TObject>(server, [](TObject *) {});
987 }
988 }
989 return nullptr;
990}
991
993{
994 if (fXAxis) {
995 // check if num bins needs update or not
996 if (auto cat = dynamic_cast<RooAbsCategory *>(fXAxis->GetParent());
997 cat && cat->numTypes() != fXAxis->GetNbins()) {
998 fXAxis.reset();
999 } else {
1000 return fXAxis.get();
1001 }
1002 }
1003 RooAbsLValue *x = nullptr;
1004 if (auto a = get<RooAbsArg>(); a && a->isFundamental())
1005 x = dynamic_cast<RooAbsLValue *>(a); // self-axis
1006
1007 auto _parentX = (!x && fParent && !fParent->get<RooSimultaneous>()) ? fParent->GetXaxis() : nullptr;
1008
1009 auto o = get<RooAbsReal>();
1010 if (!o)
1011 return _parentX;
1012
1013 if (auto xName = o->getStringAttribute("xvar"); xName) {
1014 x = dynamic_cast<RooAbsLValue *>(getObject(xName).get());
1015 }
1016
1017 // if xvar has become set equal to an arg and this is a pdf, we will allow a do-over
1018 if (!x) {
1019 // need to choose from dependent fundamentals, in following order:
1020 // parentX (if not a glob), robs, globs, vars, args
1021
1022 if (_parentX && !dynamic_cast<RooAbsArg *>(_parentX->GetParent())->getAttribute("global") &&
1023 (o->dependsOn(*dynamic_cast<RooAbsArg *>(_parentX->GetParent())) || vars().empty())) {
1024 x = dynamic_cast<RooAbsLValue *>(_parentX->GetParent());
1025 } else if (auto _obs = obs(); !_obs.empty()) {
1026 for (auto &v : _obs) {
1027 if (!v->get<RooAbsArg>()->getAttribute("global")) {
1028 x = v->get<RooAbsLValue>();
1029 if (x)
1030 break;
1031 } else if (!x) {
1032 x = v->get<RooAbsLValue>();
1033 }
1034 }
1035 } else if (auto _pars = pars(); !_pars.empty()) {
1036 for (auto &v : _pars) {
1037 if (!v->get<RooAbsArg>()->getAttribute("Constant")) {
1038 x = v->get<RooAbsLValue>();
1039 if (x)
1040 break;
1041 } else if (!x) {
1042 x = v->get<RooAbsLValue>();
1043 }
1044 }
1045 }
1046
1047 if (!x) {
1048 return nullptr;
1049 }
1050 }
1051
1052 if (o != dynamic_cast<TObject *>(x)) {
1053 o->setStringAttribute("xvar", dynamic_cast<TObject *>(x)->GetName());
1054 }
1055
1056 // decide binning to use
1057 TString binningName = o->getStringAttribute("binning");
1058 auto _bnames = x->getBinningNames();
1059 bool hasBinning = false;
1060 for (auto &b : _bnames) {
1061 if (b == binningName) {
1062 hasBinning = true;
1063 break;
1064 }
1065 }
1066 if (!hasBinning) {
1067 // doesn't have binning, so clear binning attribute
1068 // this can happen after Combine of models because binning don't get combined yet (should fix this)
1069 Warning("GetXaxis", "Binning %s not defined on %s - clearing", binningName.Data(),
1070 dynamic_cast<TObject *>(x)->GetName());
1071 o->setStringAttribute("binning", nullptr);
1072 binningName = "";
1073 }
1074
1075 if (binningName == "" && o != dynamic_cast<TObject *>(x)) {
1076 // has var has a binning matching this nodes name then use that
1077 auto __bnames = x->getBinningNames();
1078 for (auto &b : __bnames) {
1079 if (b == GetName())
1080 binningName = GetName();
1081 if (b == o->GetName()) {
1082 binningName = o->GetName();
1083 break;
1084 } // best match
1085 }
1086 if (binningName == "") {
1087 // if we are binned in this var then will define that as a binning
1088 if (/*o->isBinnedDistribution(*dynamic_cast<RooAbsArg *>(x))*/
1089 auto bins = _or_func(
1090 /*o->plotSamplingHint(*dynamic_cast<RooAbsRealLValue
1091 *>(x),-std::numeric_limits<double>::infinity(),std::numeric_limits<double>::infinity())*/
1092 (std::list<double> *)(nullptr),
1093 o->binBoundaries(*dynamic_cast<RooAbsRealLValue *>(x), -std::numeric_limits<double>::infinity(),
1094 std::numeric_limits<double>::infinity()));
1095 bins) {
1096 std::vector<double> _bins;
1097 for (auto &b : *bins) {
1098 if (_bins.empty() || std::abs(_bins.back() - b) > 1e-5 * _bins.back())
1099 _bins.push_back(b);
1100 }
1101 fXAxis = std::make_shared<Axis2>(_bins.size() - 1, &_bins[0]);
1102 // add this binning to the var to avoid recalling ...
1103 if (auto _v = dynamic_cast<RooRealVar *>(x); _v) {
1104 _v->setBinning(RooBinning(_bins.size() - 1, &_bins[0], o->GetName()), o->GetName());
1105 _v->getBinning(o->GetName())
1106 .SetTitle(""); // indicates to use the current var title when building histograms etc
1107 //_v->getBinning(o->GetName()).SetTitle(strlen(dynamic_cast<TObject*>(x)->GetTitle()) ?
1108 // dynamic_cast<TObject*>(x)->GetTitle() : dynamic_cast<TObject*>(x)->GetName());
1109 }
1110 binningName = o->GetName();
1111 delete bins;
1112 } else if (_parentX) {
1113 // use parent axis binning if defined, otherwise we will default
1114 binningName = _parentX->GetName();
1115 }
1116 }
1117 }
1118
1119 if (!fXAxis) {
1120 if (auto r = dynamic_cast<RooAbsRealLValue *>(x); r) {
1121 if (r->getBinning(binningName).isUniform()) {
1122 fXAxis = std::make_shared<Axis2>(x->numBins(binningName), r->getMin(binningName), r->getMax(binningName));
1123 } else {
1124 fXAxis = std::make_shared<Axis2>(x->numBins(binningName), r->getBinning(binningName).array());
1125 }
1126 } else if (auto cat = dynamic_cast<RooCategory *>(x)) {
1127 std::vector<double> bins = {};
1128 for (int i = 0; i <= x->numBins(binningName); i++)
1129 bins.push_back(i);
1130 fXAxis = std::make_shared<Axis2>(x->numBins(binningName), &bins[0]);
1131 // TODO have to load current state of bin labels if was a category (sadly not a virtual method)
1132 int i = 1;
1133 std::map<int, std::string> cats; // fill into a map to preserve index ordering
1134 for (auto &c : *cat) {
1135 if (cat->isStateInRange(binningName, c.first.c_str())) {
1136 cats[c.second] = c.first;
1137 }
1138 }
1139 for (auto &[_, label] : cats) {
1140 fXAxis->SetBinLabel(i++, label.c_str());
1141 }
1142 }
1143 }
1144
1145 fXAxis->SetName(binningName);
1146 fXAxis->SetParent(dynamic_cast<TObject *>(x));
1147 return fXAxis.get();
1148}
1149
1150const char *xRooNode::GetIconName() const
1151{
1152 if (auto o = get(); o) {
1153 if (o->InheritsFrom("RooWorkspace"))
1154 return "TFile";
1155 if (o->InheritsFrom("RooAbsData"))
1156 return "TProfile";
1157 if (o->InheritsFrom("RooSimultaneous"))
1158 return "TH3D";
1159
1160 if (o->InheritsFrom("RooProdPdf"))
1161 return "a.C"; // or nullptr for folder
1162 if (o->InheritsFrom("RooRealSumPdf") || o->InheritsFrom("RooAddPdf"))
1163 return "TH2D";
1164 // if(o->InheritsFrom("RooProduct")) return "TH1D";
1165 if (o->InheritsFrom("RooFitResult")) {
1166 if (!gClient->GetMimeTypeList()->GetIcon("xRooFitRooFitResult", true)) {
1167 gClient->GetMimeTypeList()->AddType("xRooFitRooFitResult", "xRooFitRooFitResult", "package.xpm",
1168 "package.xpm", "->Browse()");
1169 }
1170 return "xRooFitRooFitResult";
1171 }
1172 if (o->InheritsFrom("RooRealVar") || o->InheritsFrom("RooCategory")) {
1173 if (get<RooAbsArg>()->getAttribute("obs")) {
1174 if (!gClient->GetMimeTypeList()->GetIcon("xRooFitObs", true)) {
1175 gClient->GetMimeTypeList()->AddType("xRooFitObs", "xRooFitObs", "x_pic.xpm", "x_pic.xpm", "->Browse()");
1176 }
1177 if (!gClient->GetMimeTypeList()->GetIcon("xRooFitGlobs", true)) {
1178 gClient->GetMimeTypeList()->AddType("xRooFitGlobs", "xRooFitGlobs", "z_pic.xpm", "z_pic.xpm",
1179 "->Browse()");
1180 }
1181 return (get<RooAbsArg>()->getAttribute("global") ? "xRooFitGlobs" : "xRooFitObs");
1182 }
1183 return "TLeaf";
1184 }
1185 if (o->InheritsFrom("TStyle")) {
1186 if (!gClient->GetMimeTypeList()->GetIcon("xRooFitTStyle", true)) {
1187 gClient->GetMimeTypeList()->AddType("xRooFitTStyle", "xRooFitTStyle", "bld_colorselect.xpm",
1188 "bld_colorselect.xpm", "->Browse()");
1189 }
1190 return "xRooFitTStyle";
1191 }
1192 if (o->InheritsFrom("RooConstVar")) {
1193 /*if (!gClient->GetMimeTypeList()->GetIcon("xRooFitRooConstVar",true)) {
1194 gClient->GetMimeTypeList()->AddType("xRooFitRooConstVar", "xRooFitRooConstVar", "stop_t.xpm", "stop_t.xpm",
1195 "->Browse()");
1196 }
1197 return "xRooFitRooConstVar";*/
1198 return "TMethodBrowsable-leaf";
1199 }
1200 if (o->InheritsFrom("RooStats::HypoTestInverterResult")) {
1201 if (!gClient->GetMimeTypeList()->GetIcon("xRooFitScanStyle", true)) {
1202 gClient->GetMimeTypeList()->AddType("xRooFitScanStyle", "xRooFitScanStyle", "f2_s.xpm", "f2_s.xpm",
1203 "->Browse()");
1204 }
1205 return "xRooFitScanStyle";
1206 }
1207 if (o->InheritsFrom("RooStats::HypoTestResult")) {
1208 if (!gClient->GetMimeTypeList()->GetIcon("xRooFitTestStyle", true)) {
1209 gClient->GetMimeTypeList()->AddType("xRooFitTestStyle", "xRooFitTestStyle", "diamond.xpm", "diamond.xpm",
1210 "->Browse()");
1211 }
1212 return "xRooFitTestStyle";
1213 }
1214 if (o->InheritsFrom("RooStats::HistFactory::FlexibleInterpVar"))
1215 return "TBranchElement-folder";
1216 if (o->InheritsFrom("RooAbsPdf")) {
1217 if (!gClient->GetMimeTypeList()->GetIcon("xRooFitPDFStyle", true)) {
1218 gClient->GetMimeTypeList()->AddType("xRooFitPDFStyle", "xRooFitPDFStyle", "pdf.xpm", "pdf.xpm",
1219 "->Browse()");
1220 }
1221 return "xRooFitPDFStyle";
1222 }
1223 if (o->InheritsFrom("RooStats::ModelConfig")) {
1224 if (!gClient->GetMimeTypeList()->GetIcon("xRooFitMCStyle", true)) {
1225 gClient->GetMimeTypeList()->AddType("xRooFitMCStyle", "xRooFitMCStyle", "app_t.xpm", "app_t.xpm",
1226 "->Browse()");
1227 }
1228 return "xRooFitMCStyle";
1229 }
1230 if (auto a = dynamic_cast<RooAbsReal *>(o); a) {
1231 if (auto _ax = GetXaxis();
1232 _ax && (a->isBinnedDistribution(*dynamic_cast<RooAbsArg *>(_ax->GetParent())) ||
1233 (dynamic_cast<RooAbsRealLValue *>(_ax->GetParent()) &&
1234 std::unique_ptr<std::list<double>>(a->binBoundaries(
1235 *dynamic_cast<RooAbsRealLValue *>(_ax->GetParent()), -std::numeric_limits<double>::infinity(),
1236 std::numeric_limits<double>::infinity()))))) {
1237 return "TH1D";
1238 }
1239 return "TF1";
1240 }
1241 return o->ClassName();
1242 }
1243 if (!IsFolder()) {
1244 return "Unknown";
1245 }
1246 return nullptr;
1247}
1248
1249const char *xRooNode::GetNodeType() const
1250{
1251 if (auto o = get(); o && fParent && (fParent->get<RooProduct>() || fParent->get<RooRealSumPdf>())) {
1252 if (o->InheritsFrom("RooStats::HistFactory::FlexibleInterpVar"))
1253 return "Overall";
1254 if (o->InheritsFrom("PiecewiseInterpolation"))
1255 return (dynamic_cast<RooAbsArg *>(o)->getAttribute("density")) ? "DensityHisto" : "Histo";
1256 if (o->InheritsFrom("RooHistFunc"))
1257 return (dynamic_cast<RooAbsArg *>(o)->getAttribute("density")) ? "ConstDensityHisto" : "ConstHisto";
1258 if (o->InheritsFrom("RooBinWidthFunction"))
1259 return "Density";
1260 if (o->InheritsFrom("ParamHistFunc"))
1261 return "Shape";
1262 if (o->InheritsFrom("RooRealVar"))
1263 return "Norm";
1264 if (o->InheritsFrom("RooConstVar"))
1265 return "Const";
1266 }
1267 return "";
1268}
1269
1271{
1272 xRooNode out(".coords", nullptr, *this);
1273 // go up through parents looking for slice obs
1274 auto _p = std::shared_ptr<xRooNode>(const_cast<xRooNode *>(this), [](xRooNode *) {});
1275 while (_p) {
1276 TString pName(_p->GetName());
1277 // following is commented out while still considering, but idea is to include category in coords
1278 /*if (auto s = _p->get<RooSimultaneous>(); s && s->indexCat().InheritsFrom("RooCategory") &&
1279 !out.find(s->indexCat().GetName())) { auto cat = const_cast<RooCategory*>(dynamic_cast<const
1280 RooCategory*>(&s->indexCat()));
1281 // check if we have a pdf for every category ... if not then add to cut
1282 cat->clearRange("coordRange",true);
1283 bool hasMissing = false;
1284 std::string includedStates;
1285 for (auto state : *cat) {
1286 if (!s->getPdf(state.first.c_str())) {
1287 hasMissing = true;
1288 } else {
1289 if (!includedStates.empty()) {
1290 includedStates += ",";
1291 }
1292 includedStates += state.first;
1293 }
1294 }
1295 if (hasMissing) {
1296 if(includedStates.find(",") != std::string::npos) {
1297 cat->addToRange("coordRange",includedStates.c_str());
1298 } else {
1299 cat->setLabel(includedStates);
1300 }
1301 out.emplace_back(std::make_shared<xRooNode>(cat->GetName(),_p->getObject<RooAbsArg>(cat->GetName()),_p));
1302 }
1303 } else*/
1304 if (auto pos = pName.Index('='); pos != -1) {
1305 if (pos > 0 && pName(pos - 1) == '<') {
1306 // should be a range on a real lvalue, of form low<=name<high
1307 double low = TString(pName(0, pos - 1)).Atof();
1308 pName = pName(pos + 1, pName.Length());
1309 double high = TString(pName(pName.Index('<') + 1, pName.Length())).Atof();
1310 pName = pName(0, pName.Index('<'));
1311 if (auto _obs = _p->getObject<RooAbsRealLValue>(pName.Data()); _obs) {
1312 if (setVals) {
1313 _obs->setVal((high + low) / 2.);
1314 static_cast<RooRealVar *>(_obs.get())->setRange("coordRange", low, high);
1315 _obs->setStringAttribute(
1316 "coordRange", "coordRange"); // will need if we allow multi disconnected regions, need comma list
1317 }
1318 out.emplace_back(std::make_shared<xRooNode>(_obs->GetName(), _obs, _p));
1319 } else {
1320 throw std::runtime_error(TString::Format("Unknown observable: %s", pName.Data()));
1321 }
1322
1323 } else if (auto _obs = _p->getObject<RooAbsArg>(pName(0, pos)); _obs) {
1324 if (setVals) {
1325 if (auto _cat = dynamic_cast<RooAbsCategoryLValue *>(_obs.get()); _cat) {
1326 _cat->setLabel(pName(pos + 1, pName.Length()));
1327 } else if (auto _var = dynamic_cast<RooAbsRealLValue *>(_obs.get()); _var) {
1328 _var->setVal(TString(pName(pos + 1, pName.Length())).Atof());
1329 }
1330 }
1331 out.emplace_back(std::make_shared<xRooNode>(_obs->GetName(), _obs, _p));
1332 } else {
1333 throw std::runtime_error("Unknown observable, could not find");
1334 }
1335 }
1336 _p = _p->fParent;
1337 }
1338 return out;
1339}
1340
1341void xRooNode::_Add_(const char *name, const char *opt)
1342{
1343 try {
1344 Add(name, opt);
1345 } catch (const std::exception &e) {
1346 new TGMsgBox(gClient->GetRoot(), gClient->GetRoot(), "Exception", e.what(),
1347 kMBIconExclamation); // deletes self on dismiss?
1348 }
1349}
1350void xRooNode::_Vary_(const char *what)
1351{
1352 try {
1353 Vary(what);
1354 } catch (const std::exception &e) {
1355 new TGMsgBox(gClient->GetRoot(), gClient->GetRoot(), "Exception", e.what(),
1356 kMBIconExclamation); // deletes self on dismiss?
1357 }
1358}
1359
1361{
1362
1363 if (strcmp(GetName(), ".poi") == 0) {
1364 // demote a parameter from being a poi
1365 auto toRemove =
1366 (child.get<RooAbsArg>() || !find(child.GetName())) ? child : xRooNode(find(child.GetName())->fComp);
1367 if (toRemove) {
1368 if (!toRemove.get<RooAbsArg>()->getAttribute("poi")) {
1369 throw std::runtime_error(TString::Format("%s is not a poi", toRemove.GetName()));
1370 }
1371 toRemove.get<RooAbsArg>()->setAttribute("poi", false);
1372 return toRemove;
1373 }
1374 } else if (strcmp(GetName(), ".factors") == 0 || strcmp(GetName(), ".constraints") == 0 ||
1375 strcmp(GetName(), ".components") == 0) {
1376 auto toRemove =
1377 (child.get<RooAbsArg>() || !find(child.GetName())) ? child : xRooNode(find(child.GetName())->fComp);
1378 if (auto p = fParent->get<RooProdPdf>(); p) {
1379 auto pdf = toRemove.get<RooAbsArg>();
1380 if (!pdf)
1381 pdf = p->pdfList().find(child.GetName());
1382 if (!pdf)
1383 throw std::runtime_error(TString::Format("Cannot find %s in %s", child.GetName(), fParent->GetName()));
1384 auto i = p->pdfList().index(*pdf);
1385 if (i >= 0) {
1386#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
1387 const_cast<RooArgList &>(p->pdfList()).remove(*pdf);
1388#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 26, 00)
1389 p->_pdfNSetList.erase(p->_pdfNSetList.begin() + i);
1390#else
1391 auto nset = p->_pdfNSetList.At(i);
1392 p->_pdfNSetList.Remove(nset);
1393 delete nset; // I don't think the RooLinkedList owned it so must delete ourself
1394#endif
1395 if (p->_extendedIndex == i)
1396 p->_extendedIndex = -1;
1397 else if (p->_extendedIndex > i)
1398 p->_extendedIndex--;
1399#else
1400 p->removePdfs(RooArgSet(*pdf));
1401#endif
1402 sterilize();
1403 return xRooNode(*pdf);
1404 } else {
1405 throw std::runtime_error(TString::Format("Cannot find %s in %s", child.GetName(), fParent->GetName()));
1406 }
1407 } else if (auto p2 = fParent->get<RooProduct>(); p2) {
1408 auto arg = toRemove.get<RooAbsArg>();
1409 if (!arg)
1410 arg = p2->components().find(child.GetName());
1411 if (!arg)
1412 throw std::runtime_error(TString::Format("Cannot find %s in %s", child.GetName(), fParent->GetName()));
1413 // remove server ... doesn't seem to trigger removal from proxy
1414#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
1415 p2->_compRSet.remove(*arg);
1416#else
1417 const_cast<RooArgList &>(p2->realComponents()).remove(*arg);
1418#endif
1419 p2->removeServer(*arg, true);
1420 sterilize();
1421 return xRooNode(*arg);
1422 } else if (fParent->get<RooSimultaneous>()) {
1423 // remove from all channels
1424 bool removed = false;
1425 for (auto &c : fParent->bins()) {
1426 try {
1427 c->constraints().Remove(toRemove);
1428 removed = true;
1429 } catch (std::runtime_error &) { /* wasn't a constraint in channel */
1430 }
1431 }
1432 sterilize();
1433 if (!removed)
1434 throw std::runtime_error(TString::Format("Cannot find %s in %s", child.GetName(), fParent->GetName()));
1435 return toRemove;
1436 } else if (auto p4 = fParent->get<RooRealSumPdf>(); p4) {
1437 auto arg = toRemove.get<RooAbsArg>();
1438 if (!arg)
1439 arg = p4->funcList().find(child.GetName());
1440 if (!arg)
1441 throw std::runtime_error(TString::Format("Cannot find %s in %s", child.GetName(), fParent->GetName()));
1442 // remove, including coef removal ....
1443 auto idx = p4->funcList().index(arg);
1444
1445 if (idx != -1) {
1446
1447 const_cast<RooArgList &>(p4->funcList()).remove(*arg);
1448 p4->removeServer(*arg, true);
1449 // have to be careful removing coef because if shared will end up removing them all!!
1450 std::vector<RooAbsArg *> _coefs;
1451 for (size_t ii = 0; ii < const_cast<RooArgList &>(p4->coefList()).size(); ii++) {
1452 if (ii != size_t(idx))
1453 _coefs.push_back(const_cast<RooArgList &>(p4->coefList()).at(ii));
1454 }
1455 const_cast<RooArgList &>(p4->coefList()).removeAll();
1456 for (auto &a : _coefs)
1457 const_cast<RooArgList &>(p4->coefList()).add(*a);
1458
1459 sterilize();
1460 } else {
1461 throw std::runtime_error(TString::Format("Cannot find %s in %s", child.GetName(), fParent->GetName()));
1462 }
1463 return xRooNode(*arg);
1464 } else if (auto p5 = fParent->get<RooAddPdf>(); p5) {
1465 auto arg = toRemove.get<RooAbsArg>();
1466 if (!arg)
1467 arg = p5->pdfList().find(child.GetName());
1468 if (!arg)
1469 throw std::runtime_error(TString::Format("Cannot find %s in %s", child.GetName(), fParent->GetName()));
1470 // remove, including coef removal ....
1471 auto idx = p5->pdfList().index(arg);
1472
1473 if (idx != -1) {
1474
1475 const_cast<RooArgList &>(p5->pdfList()).remove(*arg);
1476 p5->removeServer(*arg, true);
1477 // have to be careful removing coef because if shared will end up removing them all!!
1478 std::vector<RooAbsArg *> _coefs;
1479 for (size_t ii = 0; ii < const_cast<RooArgList &>(p5->coefList()).size(); ii++) {
1480 if (ii != size_t(idx))
1481 _coefs.push_back(const_cast<RooArgList &>(p5->coefList()).at(ii));
1482 }
1483 const_cast<RooArgList &>(p5->coefList()).removeAll();
1484 for (auto &a : _coefs)
1485 const_cast<RooArgList &>(p5->coefList()).add(*a);
1486
1487 sterilize();
1488 } else {
1489 throw std::runtime_error(TString::Format("Cannot find %s in %s", child.GetName(), fParent->GetName()));
1490 }
1491 return xRooNode(*arg);
1492 } else if (auto p6 = fParent->get<RooAddition>(); p6) {
1493 auto arg = toRemove.get<RooAbsArg>();
1494 if (!arg)
1495 arg = p6->list().find(child.GetName());
1496 if (!arg)
1497 throw std::runtime_error(TString::Format("Cannot find %s in %s", child.GetName(), fParent->GetName()));
1498 // remove server ... doesn't seem to trigger removal from proxy
1499 const_cast<RooArgList &>(p6->list()).remove(*arg);
1500 p6->removeServer(*arg, true);
1501 sterilize();
1502 return xRooNode(*arg);
1503 }
1504 }
1505
1506 if (auto w = get<RooWorkspace>(); w) {
1507 xRooNode out(child.GetName());
1508 auto arg = w->components().find(child.GetName());
1509 if (!arg)
1510 arg = operator[](child.GetName())->get<RooAbsArg>();
1511 if (!arg) {
1512 throw std::runtime_error(TString::Format("Cannot find %s in workspace %s", child.GetName(), GetName()));
1513 }
1514 // check has no clients ... if so, cannot delete
1515 if (arg->hasClients()) {
1516 throw std::runtime_error(
1517 TString::Format("Cannot remove %s from workspace %s, because it has dependencies - first remove from those",
1518 child.GetName(), GetName()));
1519 }
1520 const_cast<RooArgSet &>(w->components()).remove(*arg); // deletes arg
1521 Info("Remove", "Deleted %s from workspace %s", out.GetName(), GetName());
1522 return out;
1523 } else if (get<RooProduct>() || get<RooProdPdf>()) {
1524 return factors().Remove(child);
1525 } else if (get<RooRealSumPdf>() || get<RooAddPdf>() || get<RooAddition>()) {
1526 return components().Remove(child);
1527 }
1528
1529 throw std::runtime_error("Removal not implemented for object type " +
1530 std::string(get() ? get()->ClassName() : "null"));
1531}
1532
1534{
1535
1536 class AutoUpdater {
1537 public:
1538 AutoUpdater(xRooNode &_n) : n(_n) {}
1539 ~AutoUpdater() { n.browse(); }
1540 xRooNode &n;
1541 };
1542 AutoUpdater xxx(*this);
1543
1544 TString sOpt(opt);
1545 bool considerType(sOpt == "+");
1546
1547 if (strlen(GetName()) > 0 && GetName()[0] == '!' && fParent) {
1548 // folder .. pass onto parent and add folder to child folder list
1549 const_cast<xRooNode &>(child).fFolder += GetName();
1550 return fParent->Add(child, opt);
1551 }
1552 // this is how to get the first real parent ... may be useful at some point?
1553 /*auto realParent = fParent;
1554 while(!realParent->get()) {
1555 realParent = realParent->fParent;
1556 if (!realParent) throw std::runtime_error("No parentage");
1557 }*/
1558
1559 // adding to a collection node will incorporate the child into the parent of the collection
1560 // in the appropriate way
1561 if (strcmp(GetName(), ".factors") == 0) {
1562 // multiply the parent
1563 return fParent->Multiply(child, opt);
1564 } else if (strcmp(GetName(), ".components") == 0) {
1565 // add to the parent
1566 return fParent->Add(child, opt);
1567 } else if (strcmp(GetName(), ".variations") == 0) {
1568 // vary the parent
1569 return fParent->Vary(child);
1570 } else if (strcmp(GetName(), ".constraints") == 0) {
1571 // constrain the parent
1572 return fParent->Constrain(child);
1573 } else if (strcmp(GetName(), ".bins") == 0 && fParent->get<RooSimultaneous>()) {
1574 // adding a channel (should adding a 'bin' be an 'Extend' operation?)
1575 return fParent->Vary(child);
1576 } else if ((strcmp(GetName(), ".globs") == 0)) {
1577 if (child.get<RooAbsArg>() || (!child.fComp && getObject<RooAbsArg>(child.GetName()))) {
1578 auto out = (child.get<RooAbsArg>()) ? child.get<RooAbsArg>() : getObject<RooAbsArg>(child.GetName()).get();
1579 out->setAttribute("obs");
1580 out->setAttribute("global");
1581 return xRooNode(*out, *this);
1582 }
1583 throw std::runtime_error("Failed to add global observable");
1584 } else if ((strcmp(GetName(), ".poi") == 0)) {
1585 if (child.get<RooAbsLValue>() || (!child.fComp && getObject<RooAbsLValue>(child.GetName()))) {
1586 auto out = (child.get<RooAbsArg>()) ? child.get<RooAbsArg>() : getObject<RooAbsArg>(child.GetName()).get();
1587 out->setAttribute("poi");
1588 return xRooNode(*out, *this);
1589 } else if (!child.get() && fParent->get<RooWorkspace>()) {
1590 // may be creating poi at same time as adding, try add to parent
1591 auto res = fParent->Add(child);
1592 if (res.get<RooAbsLValue>())
1593 return Add(res);
1594 }
1595 throw std::runtime_error("Failed to add parameter of interest");
1596 } else if ((strcmp(GetName(), ".pars") == 0 || strcmp(GetName(), ".vars") == 0) && fParent->get<RooWorkspace>()) {
1597 // adding a parameter, interpret as factory string unless no "[" then create RooRealVar
1598 TString fac(child.GetName());
1599 if (!fac.Contains("["))
1600 fac += "[1]";
1601 return xRooNode(*fParent->get<RooWorkspace>()->factory(fac), fParent);
1602 } else if (strcmp(GetName(), ".datasets()") == 0) {
1603 // create a dataset - only allowed for pdfs or workspaces
1604 if (auto _ws = ws(); _ws && fParent) {
1605 sOpt.ToLower();
1606 if (!fParent->get<RooAbsPdf>() && (!fParent->get<RooWorkspace>() || sOpt == "asimov")) {
1607 throw std::runtime_error(
1608 "Datasets can only be created for pdfs or workspaces (except if generated dataset, then must be pdf)");
1609 }
1610
1611 if (sOpt == "asimov" || sOpt == "toy") {
1612 // generate expected dataset - note that globs will be frozen at this time
1613 auto _fr = fParent->fitResult();
1614 if (strlen(_fr->GetName()) == 0) { // ensure fit result has a name so that name is saved inside dataset
1615 _fr.get<RooFitResult>()->SetName(TUUID().AsString());
1616 }
1617 auto ds = fParent->generate(_fr, sOpt == "asimov");
1618 if (strlen(child.GetName())) {
1619 ds.SetName(child.GetName());
1620 ds.get<TNamed>()->SetName(child.GetName());
1621 }
1622 if (auto _ds = ds.get<RooAbsData>()) {
1623 _ws->import(*_ds);
1624 }
1625 if (_fr.get<RooFitResult>()->numStatusHistory() == 0) {
1626 if (!GETWSSNAPSHOTS(_ws).find(_fr->GetName())) {
1627 const_cast<RooLinkedList &>(GETWSSNAPSHOTS(_ws)).Add(_fr->Clone());
1628 }
1629 } else if (!_ws->obj(_fr->GetName())) {
1630 _ws->import((*_fr.get<RooFitResult>()));
1631 } // save fr to workspace, for later retrieval
1632 return xRooNode(*_ws->data(ds.GetName()), fParent);
1633 }
1634
1635 auto parentObs = fParent->obs(); // may own globs so keep alive
1636 auto _obs = parentObs.argList();
1637 // put globs in a snapshot
1638 std::unique_ptr<RooAbsCollection> _globs(_obs.selectByAttrib("global", true));
1639 // RooArgSet _tmp; _tmp.add(*_globs);_ws->saveSnapshot(child.GetName(),_tmp);
1640 _obs.remove(*_globs);
1641
1642 // include any coords
1643 _obs.add(coords(false).argList(), true);
1644 // include axis var too, provided it's an observable
1645 if (auto ax = GetXaxis(); ax && dynamic_cast<RooAbsArg *>(ax->GetParent())->getAttribute("obs")) {
1646 _obs.add(*dynamic_cast<RooAbsArg *>(ax->GetParent()));
1647 }
1648 // check if ws already has a dataset with this name, if it does we may need to extend columns
1649 if (auto _d = _ws->data(child.GetName()); _d) {
1650 // add any missing obs
1651 RooArgSet l(_obs);
1652 l.remove(*_d->get(), true, true);
1653 if (!l.empty()) {
1654 auto _dd = dynamic_cast<RooDataSet *>(_d);
1655 if (!_dd)
1656 throw std::runtime_error("Cannot extend dataset with new columns");
1657 for (auto &x : l) {
1658 _dd->addColumn(*x);
1659 }
1660 }
1661 } else {
1662 RooRealVar w("weightVar", "weightVar", 1);
1663 _obs.add(w);
1664 RooDataSet d(child.GetName(), child.GetTitle(), _obs, RooFit::WeightVar("weightVar"));
1665 _ws->import(d);
1666 // seems have to set bits after importing, not before
1667 if (auto __d = _ws->data(child.GetName()))
1668 __d->SetBit(1 << 20, _ws->allData().size() == 1); // sets as selected if is only ds
1669 }
1670 /*if(!_ws->data(child.GetName())) {
1671 RooRealVar w("weightVar", "weightVar", 1);
1672 RooArgSet _obs; _obs.add(w);
1673 RooDataSet d(child.GetName(), child.GetTitle(), _obs, "weightVar");
1674 _ws->import(d);
1675 }*/
1676 auto out = std::shared_ptr<TObject>(_ws->data(child.GetName()), [](TObject *) {});
1677
1678 if (out) {
1679 xRooNode o(out, fParent);
1680 if (child.get<TH1>())
1681 o = *child.get();
1682 return o;
1683 }
1684 }
1685 throw std::runtime_error("Cannot create dataset");
1686 }
1687
1688 if (!get()) {
1689 if (!fParent)
1690 throw std::runtime_error("Cannot add to null object with no parentage");
1691
1692 auto _ref = emplace_back(std::shared_ptr<xRooNode>(&const_cast<xRooNode &>(child), [](TObject *) {}));
1693 try {
1694 fComp = fParent->Add(*this, "+").fComp;
1695 } catch (...) {
1696 resize(size() - 1);
1697 std::rethrow_exception(std::current_exception());
1698 }
1699 resize(size() - 1); // remove the temporarily added node
1700
1701 if (!fComp) {
1702 throw std::runtime_error("No object");
1703 }
1704 }
1705
1706 if (auto p = get<RooAbsData>(); p) {
1707 if (auto bb = getBrowsable(".sourceds"))
1708 bb->Add(child, opt);
1709 if (auto _data = child.get<RooDataSet>()) {
1710 auto ds = dynamic_cast<RooDataSet *>(p);
1711 if (!ds) {
1712 throw std::runtime_error("Can only add datasets to a dataset");
1713 }
1714
1715 // append any missing globs, and check any existing globs have matching values
1717 auto _globs = globs();
1718 for (auto &glob : child.globs()) {
1719 if (auto g = _globs.find(glob->GetName()); !g) {
1720 globsToAdd.addClone(*glob->get<RooAbsArg>());
1721 } else if (g->GetContent() != glob->GetContent()) {
1722 Warning("Add", "Global observable %s=%g in dataset %s mismatches %s value %g ... ignoring latter",
1723 g->GetName(), g->GetContent(), GetName(), child.GetName(), glob->GetContent());
1724 }
1725 }
1726 // add any existing globs to list then set the list
1727 if (auto _dglobs = p->getGlobalObservables()) {
1728 globsToAdd.addClone(*_dglobs);
1729 } else {
1730 for (auto g : _globs)
1731 globsToAdd.addClone(*g->get<RooAbsArg>());
1732 }
1733 p->setGlobalObservables(globsToAdd);
1734
1735 // append any missing observables to our dataset, then append the dataset
1736
1737 for (auto col : *_data->get()) {
1738 if (!p->get()->contains(*col)) {
1739 ds->addColumn(*col);
1740 }
1741 }
1742 ds->append(*_data);
1743 ds->SetTitle(TString(ds->GetTitle()) + " + " + _data->GetTitle());
1744 SetTitle(TString(GetTitle()) + " + " + child.GetTitle());
1745 return *this;
1746 }
1747 auto _h = child.get<TH1>();
1748 if (!_h) {
1749 throw std::runtime_error("Can only add histogram or dataset to data");
1750 }
1751 auto _pdf = parentPdf();
1752 if (!_pdf)
1753 throw std::runtime_error("Could not find pdf");
1754 auto _ax = _pdf->GetXaxis();
1755 if (!_ax) {
1756 throw std::runtime_error("Cannot determine binning to add data");
1757 }
1758
1759 RooArgSet obs;
1760 obs.add(*dynamic_cast<RooAbsArg *>(_ax->GetParent()));
1761 obs.add(coords().argList()); // will also move obs to coords
1762
1763 // add any missing obs
1764 RooArgSet l(obs);
1765 l.remove(*p->get(), true, true);
1766 if (!l.empty()) {
1767 auto _d = dynamic_cast<RooDataSet *>(p);
1768 if (!_d)
1769 throw std::runtime_error("Cannot extend dataset with new columns");
1770 for (auto &x : l) {
1771 _d->addColumn(*x);
1772 }
1773 }
1774
1775 // before adding, ensure range is good to cover
1776 for (auto &o : obs) {
1777 if (auto v = dynamic_cast<RooRealVar *>(o); v) {
1778 if (auto dv = dynamic_cast<RooRealVar *>(p->get()->find(v->GetName())); dv) {
1779 if (v->getMin() < dv->getMin())
1780 dv->setMin(v->getMin());
1781 if (v->getMax() > dv->getMax())
1782 dv->setMax(v->getMax());
1783 }
1784 } else if (auto c = dynamic_cast<RooCategory *>(o); c) {
1785 if (auto dc = dynamic_cast<RooCategory *>(p->get()->find(c->GetName())); dc) {
1786 if (!dc->hasLabel(c->getCurrentLabel())) {
1787 dc->defineType(c->getCurrentLabel(), c->getCurrentIndex());
1788 }
1789 }
1790 }
1791 }
1792
1793 for (int i = 1; i <= _h->GetNbinsX(); i++) {
1794 if (auto cat = dynamic_cast<RooAbsCategoryLValue *>(_ax->GetParent())) {
1795 if (!_h->GetXaxis()->GetBinLabel(i)) {
1796 throw std::runtime_error(
1797 TString::Format("Categorical observable %s requires bin labels", _ax->GetParent()->GetName()));
1798 } else if (!cat->hasLabel(_h->GetXaxis()->GetBinLabel(i))) {
1799 throw std::runtime_error(TString::Format("Categorical observable %s does not have label %s",
1800 _ax->GetParent()->GetName(), _h->GetXaxis()->GetBinLabel(i)));
1801 } else {
1802 cat->setLabel(_h->GetXaxis()->GetBinLabel(i));
1803 }
1804 } else {
1805 dynamic_cast<RooAbsRealLValue *>(_ax->GetParent())->setVal(_h->GetBinCenter(i));
1806 }
1807 p->add(obs, _h->GetBinContent(i));
1808 }
1809
1810 return *this;
1811 }
1812
1813 if (auto p = get<RooAddPdf>(); p) {
1814 if ((child.get<RooAbsPdf>() || (!child.fComp && getObject<RooAbsPdf>(child.GetName())))) {
1815 auto out = (child.fComp) ? acquire(child.fComp) : getObject<RooAbsArg>(child.GetName());
1816 // don't add a coef if in 'all-extended' mode and this pdf is extendable
1817 auto _pdf = std::dynamic_pointer_cast<RooAbsPdf>(out);
1818 if (!_pdf) {
1819 throw std::runtime_error("Something went wrong with pdf acquisition");
1820 }
1821
1822 if (auto _ax = GetXaxis(); _ax && dynamic_cast<RooAbsRealLValue *>(_ax->GetParent()) &&
1823 _pdf->dependsOn(*static_cast<RooAbsArg *>(_ax->GetParent()))) {
1824 auto _p = _pdf;
1825
1826 if (auto _boundaries = std::unique_ptr<std::list<double>>(_p->binBoundaries(
1827 *dynamic_cast<RooAbsRealLValue *>(_ax->GetParent()), -std::numeric_limits<double>::infinity(),
1828 std::numeric_limits<double>::infinity()));
1829 !_boundaries && _ax->GetNbins() > 0) {
1830#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 24, 00)
1831 Warning("Add", "Adding unbinned pdf %s to binned %s - will wrap with RooBinSamplingPdf(...)",
1832 _p->GetName(), GetName());
1833 _p = acquireNew<RooBinSamplingPdf>(TString::Format("%s_binned", _p->GetName()), _p->GetTitle(),
1834 *dynamic_cast<RooAbsRealLValue *>(_ax->GetParent()), *_p);
1835 _p->setStringAttribute("alias", std::dynamic_pointer_cast<RooAbsArg>(out)->getStringAttribute("alias"));
1836 if (!_p->getStringAttribute("alias"))
1837 _p->setStringAttribute("alias", out->GetName());
1838#else
1839 throw std::runtime_error(
1840 "unsupported addition of unbinned pdf to binned model - please upgrade to at least ROOT 6.24");
1841#endif
1842 _pdf = _p;
1843 }
1844 }
1845
1846 if (!(_pdf->canBeExtended() && p->coefList().empty())) {
1847 // if extended, use an extended binding as the coef
1848 // otherwise e.g. if adding a RooRealSumPdf the stacked histograms will be above the
1849 // actual pdf histogram because the pdf histogram is just normalized down
1850 if (_pdf->canBeExtended()) {
1851 // FIXME: ExtendedBinding needs the obs list passing to it ... should be fixed in RooFit
1852 // until then, this will return "1" and so the pdf's histograms wont be normalized properly in relation
1853 // to stacks of its comps
1854 const_cast<RooArgList &>(p->coefList())
1855 .add(*acquireNew<RooExtendedBinding>(TString::Format("%s_extBind", _pdf->GetName()),
1856 TString::Format("Expected Events of %s", _pdf->GetTitle()),
1857 *_pdf));
1858 } else {
1859
1860 // need to create a coefficient for each existing pdf first, like above
1861 for (auto i = p->coefList().size(); i < p->pdfList().size(); i++) {
1862 const_cast<RooArgList &>(p->coefList())
1864 TString::Format("%s_extBind", p->pdfList().at(i)->GetName()),
1865 TString::Format("Expected Events of %s", p->pdfList().at(i)->GetTitle()),
1866 *static_cast<RooAbsPdf *>(p->pdfList().at(i))));
1867 }
1868
1869 const_cast<RooArgList &>(p->coefList()).add(*acquire2<RooAbsArg, RooRealVar>("1", "1", 1));
1870 }
1871 // ensure not in no-coef mode any more
1872 *reinterpret_cast<bool *>(reinterpret_cast<unsigned char *>(p) +
1873 p->Class()->GetDataMemberOffset("_allExtendable")) = false;
1874 *reinterpret_cast<bool *>(reinterpret_cast<unsigned char *>(p) +
1875 p->Class()->GetDataMemberOffset("_haveLastCoef")) = true;
1876 }
1877 const_cast<RooArgList &>(p->pdfList()).add(*_pdf);
1878 sterilize();
1879 return xRooNode(*_pdf, *this);
1880 } else if ((child.get<TH1>() || child.get<RooAbsReal>() ||
1881 (!child.get() && getObject<RooAbsReal>(child.GetName()))) &&
1882 !child.get<RooAbsPdf>()) {
1883 RooRealSumPdf *_pdf = nullptr;
1884 bool tooMany(false);
1885 for (auto &pp : factors()) {
1886 if (auto _p = pp->get<RooRealSumPdf>(); _p) {
1887 if (_pdf) {
1888 _pdf = nullptr;
1889 tooMany = true;
1890 break;
1891 } // more than one!
1892 _pdf = _p;
1893 }
1894 }
1895 if (_pdf) {
1896 return xRooNode(*_pdf, *this).Add(child);
1897 } else if (!tooMany) {
1898 // create a RooRealSumPdf to hold the child
1899 auto _sumpdf = Add(*acquireNew<RooRealSumPdf>(TString::Format("%s_samples", p->GetName()),
1900 TString::Format("%s samples", GetTitle()), RooArgList(),
1901 RooArgList(), true));
1902 _sumpdf.get<RooAbsArg>()->setStringAttribute("alias", "samples");
1903 return _sumpdf.Add(child);
1904 }
1905 }
1906 }
1907
1908 if (auto p = get<RooRealSumPdf>(); p) {
1909 std::shared_ptr<TObject> out;
1910 auto cc = child.fComp;
1911 bool isConverted = (cc != child.convertForAcquisition(*this, sOpt));
1912 if (child.get<RooAbsReal>()) {
1913 out = acquire(child.fComp);
1914 if (std::dynamic_pointer_cast<TH1>(cc) && !TString(cc->GetOption()).Contains("nostyle")) {
1915 xRooNode(out, *this).styles(cc.get()); // transfer style if adding a histogram
1916 }
1917 }
1918 if (!child.fComp && getObject<RooAbsReal>(child.GetName())) {
1919 Info("Add", "Adding existing function %s to %s", child.GetName(), p->GetName());
1920 out = getObject<RooAbsReal>(child.GetName());
1921 }
1922
1923 if (!out && !child.fComp) {
1924 std::shared_ptr<RooAbsArg> _func;
1925 // a null node .. so create either a new RooProduct or RooHistFunc if has observables (or no deps but has
1926 // x-axis)
1927 auto _obs = robs();
1928 if (!_obs.empty() || GetXaxis()) {
1929 if (_obs.empty()) {
1930 // using X axis to construct hist
1931 auto _ax = dynamic_cast<Axis2 *>(GetXaxis());
1932 auto t = TH1::AddDirectoryStatus();
1933 TH1::AddDirectory(false);
1934 auto h =
1935 std::make_unique<TH1D>(child.GetName(), child.GetTitle(), _ax->GetNbins(), _ax->binning()->array());
1937 h->GetXaxis()->SetName(TString::Format("%s;%s", _ax->GetParent()->GetName(), _ax->GetName()));
1938 // technically convertForAcquisition has already acquired so no need to re-acquire but should be harmless
1939 _func = std::dynamic_pointer_cast<RooAbsArg>(acquire(xRooNode(*h).convertForAcquisition(*this)));
1940 } else if (_obs.size() == 1) {
1941 // use the single obs to make a TH1D
1942 auto _x = _obs.at(0)->get<RooAbsLValue>();
1943 auto _bnames = _x->getBinningNames();
1944 TString binningName = p->getStringAttribute("binning");
1945 for (auto &b : _bnames) {
1946 if (b == p->GetName()) {
1947 binningName = p->GetName();
1948 break;
1949 }
1950 }
1951 auto t = TH1::AddDirectoryStatus();
1952 TH1::AddDirectory(false);
1953 auto h = std::make_unique<TH1D>(child.GetName(), child.GetTitle(), _x->numBins(binningName),
1954 _x->getBinningPtr(binningName)->array());
1956 h->GetXaxis()->SetName(
1957 TString::Format("%s;%s", dynamic_cast<TObject *>(_x)->GetName(), binningName.Data()));
1958 // technically convertForAcquisition has already acquired so no need to re-acquire but should be harmless
1959 _func = std::dynamic_pointer_cast<RooAbsArg>(acquire(xRooNode(*h).convertForAcquisition(*this)));
1960 Info("Add", "Created densityhisto factor %s (xaxis=%s) for %s", _func->GetName(), _obs.at(0)->GetName(),
1961 p->GetName());
1962 } else {
1963 throw std::runtime_error("Unsupported creation of new component in SumPdf for this many obs");
1964 }
1965 } else {
1966 _func = acquireNew<RooProduct>(TString::Format("%s_%s", p->GetName(), child.GetName()), child.GetTitle(),
1967 RooArgList());
1968 }
1969 _func->setStringAttribute("alias", child.GetName());
1970 out = _func;
1971 }
1972
1973 if (auto _f = std::dynamic_pointer_cast<RooHistFunc>(
1974 (child.get<RooProduct>()) ? child.factors()[child.GetName()]->fComp : out);
1975 _f) {
1976 // adding a histfunc directly to a sumpdf, should be a density
1977 _f->setAttribute("density");
1978 if (_f->getAttribute("autodensity")) {
1979 // need to divide by bin widths first
1980 for (int i = 0; i < _f->dataHist().numEntries(); i++) {
1981 auto bin_pars = _f->dataHist().get(i);
1982 _f->dataHist().set(*bin_pars, _f->dataHist().weight() / _f->dataHist().binVolume(*bin_pars));
1983 }
1984 _f->setAttribute("autodensity", false);
1985 _f->setValueDirty();
1986 }
1987
1988 // promote the axis vars to observables
1989 // can't use original child as might refer to unacquired deps
1990 for (auto &x : xRooNode("tmp", _f).vars()) {
1991 x->get<RooAbsArg>()->setAttribute("obs");
1992 }
1993 if (isConverted) {
1994 Info("Add", "Created %s factor RooHistFunc::%s for %s",
1995 _f->getAttribute("density") ? "densityhisto" : "histo", _f->GetName(), p->GetName());
1996 }
1997 }
1998
1999 if (auto _p = std::dynamic_pointer_cast<RooAbsPdf>(out); _p) {
2000 // adding a pdf to a RooRealSumPdf will replace it with a RooAddPdf and put the RooRealSumPdf inside that
2001 // if pdf is extended will use in the "no coefficients" state, where the expectedEvents are taking from
2002 // the pdf integrals
2003 TString newName(_p->GetName());
2004 newName.ReplaceAll("_samples", "");
2005 newName += "_components";
2006 Warning("Add", "converting samples to components");
2007
2008 if (auto _ax = GetXaxis(); _ax && dynamic_cast<RooAbsRealLValue *>(_ax->GetParent()) &&
2009 _p->dependsOn(*static_cast<RooAbsArg *>(_ax->GetParent()))) {
2010
2011 if (auto _boundaries = std::unique_ptr<std::list<double>>(_p->binBoundaries(
2012 *dynamic_cast<RooAbsRealLValue *>(_ax->GetParent()), -std::numeric_limits<double>::infinity(),
2013 std::numeric_limits<double>::infinity()));
2014 !_boundaries && _ax->GetNbins() > 0) {
2015#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 24, 00)
2016 Warning("Add", "Adding unbinned pdf %s to binned %s - will wrap with RooBinSamplingPdf(...)",
2017 _p->GetName(), GetName());
2018 _p = acquireNew<RooBinSamplingPdf>(TString::Format("%s_binned", _p->GetName()), _p->GetTitle(),
2019 *dynamic_cast<RooAbsRealLValue *>(_ax->GetParent()), *_p);
2020 _p->setStringAttribute("alias", std::dynamic_pointer_cast<RooAbsArg>(out)->getStringAttribute("alias"));
2021 if (!_p->getStringAttribute("alias"))
2022 _p->setStringAttribute("alias", out->GetName());
2023#else
2024 throw std::runtime_error(
2025 "unsupported addition of unbinned pdf to binned model - please upgrade to at least ROOT 6.24");
2026#endif
2027 }
2028 }
2029
2030 // require to be extended to be in coefficient-free mode ...
2031 // otherwise would lose the integral of the sumPdf (can't think of way to have a coef be the integral)
2032 if (!_p->canBeExtended()) {
2033 _p = acquireNew<RooExtendPdf>(TString::Format("%s_extended", _p->GetName()), _p->GetTitle(), *_p,
2034 *acquire2<RooAbsReal, RooRealVar>("1", "1", 1));
2035 }
2036
2037 return *(Replace(*acquireNew<RooAddPdf>(newName, _p->GetTitle(), RooArgList(*p, *_p)))
2038 .browse()[1]); // returns second node.
2039 }
2040
2041 if (auto _f = std::dynamic_pointer_cast<RooAbsReal>(out); _f) {
2042
2043 // todo: if adding a pdf, should actually replace RooRealSumPdf with a RooAddPdf and put
2044 // the sumPdf and *this* pdf inside that pdf
2045 // only exception is the binSamplingPdf below to integrate unbinned functions across bins
2046
2047 if (auto _ax = GetXaxis(); _ax && dynamic_cast<RooAbsRealLValue *>(_ax->GetParent()) &&
2048 _f->dependsOn(*static_cast<RooAbsArg *>(_ax->GetParent()))) {
2049
2050 if (auto _boundaries = std::unique_ptr<std::list<double>>(_f->binBoundaries(
2051 *dynamic_cast<RooAbsRealLValue *>(_ax->GetParent()), -std::numeric_limits<double>::infinity(),
2052 std::numeric_limits<double>::infinity()));
2053 !_boundaries && _ax->GetNbins() > 0) {
2054#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 24, 00)
2055 Warning(
2056 "Add",
2057 "Adding unbinned function %s to binned %s - will wrap with RooRealSumPdf(RooBinSamplingPdf(...))",
2058 _f->GetName(), GetName());
2059 auto sumPdf = acquireNew<RooRealSumPdf>(TString::Format("%s_pdfWrapper", _f->GetName()), _f->GetTitle(),
2060 *_f, *acquire2<RooAbsArg, RooRealVar>("1", "1", 1), true);
2061 sumPdf->setStringAttribute("alias", _f->getStringAttribute("alias"));
2062 if (!sumPdf->getStringAttribute("alias"))
2063 sumPdf->setStringAttribute("alias", out->GetName());
2064 _f = acquireNew<RooBinSamplingPdf>(TString::Format("%s_binned", _f->GetName()), _f->GetTitle(),
2065 *dynamic_cast<RooAbsRealLValue *>(_ax->GetParent()), *sumPdf);
2066 _f->setStringAttribute("alias", std::dynamic_pointer_cast<RooAbsArg>(out)->getStringAttribute("alias"));
2067 if (!_f->getStringAttribute("alias"))
2068 _f->setStringAttribute("alias", out->GetName());
2069#else
2070 throw std::runtime_error(
2071 "unsupported addition of unbinned function to binned model - please upgrade to at least ROOT 6.24");
2072#endif
2073 }
2074 }
2075
2076 const_cast<RooArgList &>(p->coefList()).add(*acquire2<RooAbsArg, RooRealVar>("1", "1", 1));
2077 const_cast<RooArgList &>(p->funcList()).add(*_f);
2078 // inherit binning if we dont have one yet
2079 if (!p->getStringAttribute("binning"))
2080 p->setStringAttribute("binning", _f->getStringAttribute("binning"));
2081
2082 xRooNode _out(_f, *this);
2083 if (auto gf = p->getStringAttribute("global_factors"); gf) {
2084 TStringToken pattern(gf, ";");
2085 while (pattern.NextToken()) {
2086 auto fac = getObject<RooAbsReal>(pattern.Data());
2087 if (!fac) {
2088 throw std::runtime_error(TString::Format("Could not find global factor %s", pattern.Data()));
2089 }
2090 _out.Multiply(fac);
2091 }
2092 }
2093 sterilize();
2094 // clear children for reload and update shared axis
2095 clear();
2096 fXAxis.reset();
2097 p->setStringAttribute("xvar", nullptr);
2098 browse();
2099 return _out;
2100 }
2101 } else if (auto p2 = get<RooProdPdf>(); p2) {
2102 // can "add" to a RooProdPdf provided trying to add a RooAbsReal not a RooAbsPdf and have a zero or 1
2103 // RooRealSumPdf child.convertForAcquisition(*this); - don't convert here because want generated objects named
2104 // after roorealsumpdf
2105 if (child.get<RooAbsPdf>() || (!child.get() && getObject<RooAbsPdf>(child.GetName()))) {
2106 // can add if 0 or 1 RooAddPdf ....
2107 RooAddPdf *_pdf = nullptr;
2108 bool tooMany(false);
2109 for (auto &pp : factors()) {
2110 if (auto _p = pp->get<RooAddPdf>(); _p) {
2111 if (_pdf) {
2112 _pdf = nullptr;
2113 tooMany = true;
2114 break;
2115 } // more than one!
2116 _pdf = _p;
2117 }
2118 }
2119 if (_pdf) {
2120 return xRooNode(*_pdf, *this).Add(child);
2121 } else if (!tooMany) {
2122 auto out = this->operator[]("components")->Add(child);
2123 return out;
2124 }
2125 } else if ((child.get<TH1>() || child.get<RooAbsReal>() ||
2126 (!child.get() && getObject<RooAbsReal>(child.GetName()))) &&
2127 !child.get<RooAbsPdf>()) {
2128 RooRealSumPdf *_pdf = nullptr;
2129 RooAddPdf *_backup = nullptr;
2130 bool tooMany(false);
2131 for (auto &pp : factors()) {
2132 if (auto _p = pp->get<RooRealSumPdf>(); _p) {
2133 if (_pdf) {
2134 _pdf = nullptr;
2135 tooMany = true;
2136 break;
2137 } // more than one!
2138 _pdf = _p;
2139 } else if (auto _p2 = pp->get<RooAddPdf>(); _p2) {
2140 _backup = _p2;
2141 for (auto &_pdfa : pp->components()) {
2142 if (auto _p3 = _pdfa->get<RooRealSumPdf>(); _p3) {
2143 if (_pdf) {
2144 _pdf = nullptr;
2145 tooMany = true;
2146 break;
2147 } // more than one!
2148 _pdf = _p3;
2149 }
2150 }
2151 }
2152 }
2153 if (_pdf) {
2154 return xRooNode(*_pdf, *this).Add(child);
2155 } else if (_backup) {
2156 // added *INSIDE* the addPdf -- will create a RooRealSumPdf to hold it
2157 return xRooNode(*_backup, *this).Add(child);
2158 } else if (!tooMany) {
2159 auto out = this->operator[]("samples")->Add(child);
2160 // clear our x-axis to re-evaluate
2161 fXAxis.reset();
2162 p2->setStringAttribute("xvar", nullptr);
2163 return out;
2164 }
2165 }
2166 } else if (auto s = get<RooSimultaneous>(); s) {
2167
2168 // adding to a simultaneous means adding a bin
2169 return bins().Add(child);
2170
2171 // if the child is a RooAbsPdf can just add it as a new channel using name of pdf as the channel name
2172 // if child is a histogram, will create a RooProdPdf
2173
2174 } else if (auto w = get<RooWorkspace>(); w) {
2175 child.convertForAcquisition(
2176 *this, child.get() ? "" : "func" /* if child is a string, allow it to be passed to factory */);
2177 if (child.get()) {
2178 if (auto _d = child.get<RooAbsData>()) {
2179 // don't use acquire method to import, because that adds datasets as Embeddded
2180 if (!w->import(*_d)) {
2181 // should upgrade vars with any obs from the dataset
2182 if (_d->get()) {
2183 std::unique_ptr<RooAbsCollection>(w->allVars().selectCommon(*_d->get()))->setAttribAll("obs");
2184 }
2185 if (_d->getGlobalObservables()) {
2186 std::unique_ptr<RooAbsCollection> globs(w->allVars().selectCommon(*_d->get()));
2187 globs->setAttribAll("obs");
2188 globs->setAttribAll("global");
2189 }
2190 return xRooNode(child.GetName(), *w->data(child.GetName()), *this);
2191 } else {
2192 throw std::runtime_error(
2193 TString::Format("Could not import dataset %s into workspace %s", child.GetName(), w->GetName())
2194 .Data());
2195 }
2196 } else {
2197 auto out = acquire(child.fComp);
2198 if (out)
2199 return xRooNode(child.GetName(), out, *this);
2200 }
2201 }
2202
2203 if (!child.empty() || child.fFolder == "!pdfs") {
2204 // create a RooSimultaneous using the children as the channels
2205 // children either have "=" in name if specifying channel cat name or otherwise assume
2206 std::string catName = "channelCat";
2207 if (!child.empty()) {
2208 if (TString ss = child.at(0)->GetName(); ss.Contains("=")) {
2209 catName = ss(0, ss.Index('='));
2210 }
2211 }
2212 auto _cat = acquire<RooCategory>(catName.c_str(), catName.c_str());
2213 _cat->setAttribute("obs");
2214 auto out = acquireNew<RooSimultaneous>(child.GetName(), child.GetTitle(), *_cat);
2215 Info("Add", "Created pdf RooSimultaneous::%s in workspace %s", out->GetName(), w->GetName());
2216 return xRooNode(out, *this);
2217 }
2218 }
2219
2220 if (sOpt == "pdf") {
2221 // can only add a pdf to a workspace
2222 if (get<RooWorkspace>()) {
2223 const_cast<xRooNode &>(child).fFolder = "!pdfs";
2224 return Add(child);
2225 }
2226 } else if (sOpt == "channel") {
2227 // can add to a model or to a workspace (creates a RooProdPdf either way)
2228 if (get<RooSimultaneous>()) {
2229 return Vary(child);
2230 } else if (get<RooWorkspace>()) {
2231 std::shared_ptr<TObject> out;
2232 child.convertForAcquisition(*this);
2233 if (child.get<RooAbsPdf>()) {
2234 out = acquire(child.fComp);
2235 } else if (!child.fComp) {
2236 out = acquireNew<RooProdPdf>(child.GetName(),
2237 (strlen(child.GetTitle())) ? child.GetTitle() : child.GetName(), RooArgList());
2238 Info("Add", "Created channel RooProdPdf::%s in workspace %s", out->GetName(), get()->GetName());
2239 }
2240 return xRooNode(out, *this);
2241 }
2242 } else if (sOpt == "sample" || sOpt == "func") {
2243 if (get<RooProdPdf>()) {
2244 auto _mainChild = mainChild();
2245 if (_mainChild.get<RooRealSumPdf>()) {
2246 return _mainChild.Add(child, sOpt == "func" ? "func" : "");
2247 } else {
2248 return (*this)["samples"]->Add(child, sOpt == "func" ? "func" : "");
2249 }
2250 }
2251 } else if (sOpt == "dataset") {
2252 if (get<RooWorkspace>()) {
2253 // const_cast<xRooNode&>(child).fFolder = "!datasets";return Add(child);
2254 return (*this).datasets().Add(child);
2255 }
2256 }
2257
2258 if (considerType) {
2259
2260 // interpret 'adding' here as dependent on the object type ...
2261 if (get<RooSimultaneous>()) {
2262 return bins().Add(child);
2263 } else if (TString(child.GetName()).Contains('=')) {
2264 return variations().Add(child);
2265 } else if (get<RooProduct>() || get<RooProdPdf>()) {
2266 return factors().Add(child);
2267 }
2268 }
2269
2270 // Nov 2022 - removed ability to add placeholders ... could bring back if rediscover need for them
2271 // if (!child.get() && child.empty() && strlen(child.GetName())) {
2272 // // can add a 'placeholder' node, note it will be deleted at the next browse
2273 // xRooNode out(child.GetName(),nullptr,*this);
2274 // out.SetTitle(child.GetTitle());
2275 // emplace_back(std::make_shared<xRooNode>(out));
2276 // // update the parent in the out node so that it's copy of the parent knows it has itself in it
2277 // // actually maybe not want this :-/
2278 // //out.fParent = std::make_shared<Node2>(*this);
2279 // for(auto o : *gROOT->GetListOfBrowsers()) {
2280 // if(auto b = dynamic_cast<TBrowser*>(o); b && b->GetBrowserImp()){
2281 // if(auto _b = dynamic_cast<TGFileBrowser*>(
2282 // dynamic_cast<TRootBrowser*>(b->GetBrowserImp())->fActBrowser ); _b) {
2283 // auto _root = _b->fRootDir;
2284 // if (!_root) _root = _b->fListTree->GetFirstItem();
2285 // if (auto item = _b->fListTree->FindItemByObj(_root,this); item) {
2286 // _b->fListTree->AddItem(item,back()->GetName(),back().get());
2287 // }
2288 // }
2289 // }
2290 // }
2291 // return out;
2292 // }
2293
2294 throw std::runtime_error(TString::Format("Cannot add %s to %s", child.GetName(), GetName()));
2295}
2296
2297std::string xRooNode::GetPath() const
2298{
2299 if (!fParent)
2300 return GetName();
2301 return fParent->GetPath() + "/" + GetName();
2302}
2303
2305{
2306 // std::cout << "deleting " << GetPath() << std::endl;
2307}
2308
2310{
2311 if (auto a = get<RooAbsArg>()) {
2312 a->setAttribute("hidden", set);
2313 // if(auto item = GetTreeItem(nullptr); item) {
2314 // if(set) item->SetColor(kRed);
2315 // else item->ClearColor();
2316 // }
2317 }
2318}
2320{
2321 auto a = get<RooAbsArg>();
2322 if (a)
2323 return a->getAttribute("hidden");
2324 return false;
2325}
2326
2328{
2329
2330 if (get() == rhs.get()) {
2331 // nothing to do because objects are identical
2332 return *this;
2333 }
2334
2335 // Info("Combine","Combining %s into %s",rhs.GetPath().c_str(),GetPath().c_str());
2336
2337 // combine components, factors, and variations ... when there is a name clash will combine on that object
2338 for (auto &c : rhs.components()) {
2339 if (auto _c = components().find(c->GetName()); _c) {
2340 _c->Combine(*c);
2341 } else {
2342 Add(*c);
2343 }
2344 }
2345
2346 for (auto &f : rhs.factors()) {
2347 if (auto _f = factors().find(f->GetName()); _f) {
2348 _f->Combine(*f);
2349 } else {
2350 Multiply(*f);
2351 }
2352 }
2353
2354 for (auto &v : rhs.variations()) {
2355 if (auto _v = variations().find(v->GetName()); _v) {
2356 _v->Combine(*v);
2357 } else {
2358 Vary(*v);
2359 }
2360 }
2361
2362 // todo: Should also transfer over binnings of observables
2363
2364 return *this;
2365}
2366
2367xRooNode xRooNode::shallowCopy(const std::string &name, std::shared_ptr<xRooNode> parent)
2368{
2369 xRooNode out(name.c_str(), nullptr,
2370 parent /*? parent : fParent -- was passing fParent for getObject benefit before fProvider concept*/);
2371 // if(!parent) out.fAcquirer = true;
2372 if (!parent)
2373 out.fProvider = fParent;
2374
2375 auto o = get();
2376 if (!o) {
2377 return out;
2378 }
2379
2380 if (auto s = get<RooSimultaneous>(); s) {
2381 auto chans = bins();
2382 if (!chans.empty()) {
2383 // create a new RooSimultaneous with shallow copies of each channel
2384
2385 std::shared_ptr<RooSimultaneous> pdf = out.acquire<RooSimultaneous>(
2386 name.c_str(), o->GetTitle(), const_cast<RooAbsCategoryLValue &>(s->indexCat()));
2387
2388 for (auto &c : chans) {
2389 TString cName(c->GetName());
2390 cName = cName(cName.Index('=') + 1, cName.Length());
2391 // by passing out as the parent, will ensure out acquires everything created
2392 auto c_copy =
2393 c->shallowCopy(name + "_" + c->get()->GetName(), std::shared_ptr<xRooNode>(&out, [](xRooNode *) {}));
2394 pdf->addPdf(*dynamic_cast<RooAbsPdf *>(c_copy.get()), cName);
2395 }
2396 out.fComp = pdf;
2397 return out;
2398 }
2399 } else if (auto p = dynamic_cast<RooProdPdf *>(o); p) {
2400 // main pdf will be copied too
2401 std::shared_ptr<RooProdPdf> pdf =
2402 std::dynamic_pointer_cast<RooProdPdf>(out.acquire(std::shared_ptr<TObject>(p->Clone(/*name.c_str()*/)), false,
2403 true)); // use clone to copy all attributes etc too
2404 auto main = mainChild();
2405 if (main) {
2406 auto newMain =
2407 std::dynamic_pointer_cast<RooAbsArg>(out.acquire(std::shared_ptr<TObject>(main->Clone()), false, true));
2408 std::cout << newMain << " " << newMain->GetName() << std::endl;
2409 // pdf->replaceServer(*pdf->pdfList().find(main->GetName()), *newMain, true, true);
2410 // const_cast<RooArgList&>(pdf->pdfList()).replace(*pdf->pdfList().find(main->GetName()), *newMain);
2411 pdf->redirectServers(RooArgList(*newMain));
2412 }
2413 out.fComp = pdf;
2414 out.sterilize();
2415 return out;
2416 }
2417
2418 return out;
2419}
2420
2422{
2423 static std::unique_ptr<cout_redirect> capture;
2424 std::string captureStr;
2425 bool doCapture = false;
2426 if (!capture && gROOT->FromPopUp()) { // FromPopUp means user executed from the context menu
2427 capture = std::make_unique<cout_redirect>(captureStr);
2428 doCapture = true;
2429 }
2430
2431 TString sOpt(opt);
2432 int depth = 0;
2433 if (sOpt.Contains("depth=")) {
2434 depth = TString(sOpt(sOpt.Index("depth=") + 6, sOpt.Length())).Atoi();
2435 sOpt.ReplaceAll(TString::Format("depth=%d", depth), "");
2436 }
2437 int indent = 0;
2438 if (sOpt.Contains("indent=")) {
2439 indent = TString(sOpt(sOpt.Index("indent=") + 7, sOpt.Length())).Atoi();
2440 sOpt.ReplaceAll(TString::Format("indent=%d", indent), "");
2441 }
2442 bool _more = sOpt.Contains("m");
2443 if (_more)
2444 sOpt.Replace(sOpt.Index("m"), 1, "");
2445 if (sOpt != "")
2446 _more = true;
2447
2448 if (indent == 0) { // only print self if not indenting (will already be printed above if tree traverse)
2449 std::cout << GetPath();
2450 if (get() && get() != this) {
2451 std::cout << ": ";
2452 if (_more || (get<RooAbsArg>() && get<RooAbsArg>()->isFundamental()) || get<RooConstVar>() ||
2454 auto _deps = coords(false).argList(); // want to revert coords after print
2455 auto _snap = std::unique_ptr<RooAbsCollection>(_deps.snapshot());
2456 coords(); // move to coords before printing (in case this matters)
2457 get()->Print(sOpt);
2458 if (auto _fr = get<RooFitResult>(); _fr && dynamic_cast<RooStringVar *>(_fr->constPars().find(".log"))) {
2459 std::cout << "Minimization Logs:" << std::endl;
2460 std::cout << dynamic_cast<RooStringVar *>(_fr->constPars().find(".log"))->getVal() << std::endl;
2461 }
2462 _deps.assignValueOnly(*_snap);
2463 // std::cout << std::endl;
2464 } else {
2465 TString _suffix = "";
2466 if (auto _type = GetNodeType(); strlen(_type)) {
2467 // decided not to show const values until figure out how to update if value changes
2468 /*if (TString(_type)=="Const") _name += TString::Format("
2469 [%s=%g]",_type,v->get<RooConstVar>()->getVal()); else*/
2470 _suffix += TString::Format(" [%s]", _type);
2471 }
2472 if (auto fv = get<RooFormulaVar>()) {
2473 TString formu = TString::Format(" [%s]", fv->expression());
2474 for (size_t i = 0; i < fv->dependents().size(); i++) {
2475 formu.ReplaceAll(TString::Format("x[%zu]", i), fv->dependents()[i].GetName());
2476 }
2477 _suffix += formu;
2478 } else if (auto gv = get<RooGenericPdf>()) {
2479 TString formu = TString::Format(" [%s]", gv->expression());
2480 for (size_t i = 0; i < gv->dependents().size(); i++) {
2481 formu.ReplaceAll(TString::Format("x[%zu]", i), gv->dependents()[i].GetName());
2482 }
2483 _suffix += formu;
2484 }
2485 std::cout << get()->ClassName() << "::" << get()->GetName() << _suffix.Data() << std::endl;
2486 }
2487
2488 } else if (!get()) {
2489 std::cout << std::endl;
2490 }
2491 }
2492 const_cast<xRooNode *>(this)->browse();
2493 std::vector<std::string> folderNames;
2494 for (auto &k : *this) {
2495 if (std::find(folderNames.begin(), folderNames.end(), k->fFolder) == folderNames.end()) {
2496 folderNames.push_back(k->fFolder);
2497 }
2498 }
2499 for (auto &f : folderNames) {
2500 int i = 0;
2501 int iindent = indent;
2502 if (!f.empty()) {
2503 for (int j = 0; j < indent; j++)
2504 std::cout << " ";
2505 std::cout << f << std::endl;
2506 iindent += 1;
2507 }
2508 for (auto &k : *this) {
2509 if (k->fFolder != f) {
2510 i++;
2511 continue;
2512 }
2513 for (int j = 0; j < iindent; j++)
2514 std::cout << " ";
2515 std::cout << i++ << ") " << k->GetName() << " : ";
2516 if (k->get()) {
2517 if (_more || (k->get<RooAbsArg>() && k->get<RooAbsArg>()->isFundamental()) || k->get<RooConstVar>() ||
2518 k->get<RooAbsData>() /*|| k->get<RooProduct>()*/) {
2519 auto _deps = k->coords(false).argList();
2520 auto _snap = std::unique_ptr<RooAbsCollection>(_deps.snapshot());
2521 k->coords(); // move to coords before printing (in case this matters)
2522 k->get()->Print(sOpt); // assumes finishes with an endl
2523 _deps.assignValueOnly(*_snap);
2524 } else {
2525 TString _suffix = "";
2526 if (auto _type = k->GetNodeType(); strlen(_type)) {
2527 // decided not to show const values until figure out how to update if value changes
2528 /*if (TString(_type)=="Const") _name += TString::Format("
2529 [%s=%g]",_type,v->get<RooConstVar>()->getVal()); else*/
2530 _suffix += TString::Format(" [%s]", _type);
2531 }
2532 if (auto fv = k->get<RooFormulaVar>()) {
2533 TString formu = TString::Format(" [%s]", fv->expression());
2534 for (size_t j = 0; j < fv->dependents().size(); j++) {
2535 formu.ReplaceAll(TString::Format("x[%zu]", j), fv->dependents()[j].GetName());
2536 }
2537 _suffix += formu;
2538 } else if (auto gv = k->get<RooGenericPdf>()) {
2539 TString formu = TString::Format(" [%s]", gv->expression());
2540 for (size_t j = 0; j < gv->dependents().size(); j++) {
2541 formu.ReplaceAll(TString::Format("x[%zu]", j), gv->dependents()[j].GetName());
2542 }
2543 _suffix += formu;
2544 }
2545 std::cout << k->get()->ClassName() << "::" << k->get()->GetName() << _suffix.Data() << std::endl;
2546 }
2547 if (depth != 0) {
2548 k->Print(sOpt + TString::Format("depth=%dindent=%d", depth - 1, iindent + 1));
2549 }
2550 } else
2551 std::cout << " NULL " << std::endl;
2552 }
2553 }
2554 if (doCapture) {
2555 capture.reset(); // no captureStr has the string to display
2556 // inject line breaks to avoid msgbox being too wide
2557 size_t lastBreak = 0;
2558 std::string captureStrWithBreaks;
2559 for (size_t i = 0; i < captureStr.size(); i++) {
2561 if (captureStr[i] == '\n') {
2562 lastBreak = i;
2563 }
2564 if (i - lastBreak > 150) {
2565 captureStrWithBreaks += '\n';
2566 lastBreak = i;
2567 }
2568 }
2569 const TGWindow *w =
2570 (gROOT->GetListOfBrowsers()->At(0))
2571 ? dynamic_cast<TGWindow *>(static_cast<TBrowser *>(gROOT->GetListOfBrowsers()->At(0))->GetBrowserImp())
2572 : gClient->GetRoot();
2573 new TGMsgBox(gClient->GetRoot(), w, GetName(),
2574 captureStrWithBreaks.c_str()); //,nullptr,kMBDismiss,nullptr,kVerticalFrame,kTextLeft|kTextCenterY);
2575 }
2576}
2577
2579{
2580 if (!child.get()) {
2581
2582 if (auto v = get<RooRealVar>(); v) {
2583
2584 TString constrType = child.GetName();
2585 double mean = std::numeric_limits<double>::quiet_NaN();
2586 double sigma = mean;
2587 if (constrType.BeginsWith("gaussian(")) {
2588 // extract the mean and stddev parameters
2589 // if only one given, it is the stddev
2590 if (constrType.Contains(",")) {
2591 mean = TString(constrType(9, constrType.Index(',') - 9)).Atof();
2592 sigma = TString(constrType(constrType.Index(',') + 1, constrType.Index(')') - constrType.Index(',') + 1))
2593 .Atof();
2594 } else {
2595 mean = std::numeric_limits<double>::quiet_NaN(); // will use the var current value below to set mean
2596 sigma = TString(constrType(9, constrType.Index(')') - 9)).Atof();
2597 }
2598 constrType = "normal";
2599 } else if (constrType == "normal") {
2600 mean = 0;
2601 sigma = 1;
2602 } else if (constrType == "gaussian") {
2603 // extract parameters from the variable
2604 // use current value and error on v as constraint
2605 if (!v->hasError())
2606 throw std::runtime_error("No error on parameter for gaussian constraint");
2607 sigma = v->getError();
2608 mean = v->getVal();
2609 constrType = "normal";
2610 } else if (constrType == "poisson") {
2611 if (!v->hasError())
2612 throw std::runtime_error("No error on parameter for poisson constraint");
2613 mean = 1;
2614 sigma = pow(v->getVal() / v->getError(), 2);
2615 }
2616
2617 if (constrType == "poisson") {
2618 // use current value and error on v as constraint
2619 double tau_val = sigma;
2620 auto globs = acquire<RooRealVar>(Form("globs_%s", v->GetName()), Form("globs_%s", v->GetName()),
2621 v->getVal() * tau_val, (v->getVal() - 5 * v->getError()) * tau_val,
2622 (v->getVal() + 5 * v->getError()) * tau_val);
2623 globs->setConstant();
2624 globs->setAttribute("obs");
2625 globs->setAttribute("global");
2626 globs->setStringAttribute("nominal", TString::Format("%f", tau_val));
2627 auto tau = acquireNew<RooConstVar>(TString::Format("tau_%s", v->GetName()), "", tau_val);
2629 Form("pois_%s", v->GetName()), TString::Format("Poisson Constraint of %s", v->GetTitle()), *globs,
2630 *acquireNew<RooProduct>(TString::Format("mean_%s", v->GetName()),
2631 TString::Format("Poisson Constraint of %s", globs->GetTitle()),
2632 RooArgList(*v, *tau)),
2633 true /* no rounding */);
2634
2635 auto out = Constrain(xRooNode(Form("pois_%s", GetName()), constr));
2636 if (!v->hasError())
2637 v->setError(mean / sqrt(tau_val)); // if v doesnt have an uncert, will put one on it now
2638 Info("Constrain", "Added poisson constraint pdf RooPoisson::%s (tau=%g) for %s", out->GetName(), tau_val,
2639 GetName());
2640 return out;
2641 } else if (constrType == "normal") {
2642
2643 auto globs = acquire<RooRealVar>(Form("globs_%s", v->GetName()), Form("globs_%s", v->GetName()), mean,
2644 mean - 10 * sigma, mean + 10 * sigma);
2645 globs->setAttribute("obs");
2646 globs->setAttribute("global");
2647 globs->setConstant();
2648
2649 globs->setStringAttribute("nominal", TString::Format("%f", mean));
2651 Form("gaus_%s", v->GetName()), TString::Format("Gaussian Constraint of %s", v->GetTitle()), *globs, *v,
2652 *acquireNew<RooConstVar>(TString::Format("sigma_%s", v->GetName()), "", sigma));
2653 auto out = Constrain(xRooNode(Form("gaus_%s", GetName()), constr));
2654 if (!v->hasError())
2655 v->setError(sigma); // if v doesnt have an uncert, will put one on it now
2656 Info("Constrain", "Added gaussian constraint pdf RooGaussian::%s (mean=%g,sigma=%g) for %s", out->GetName(),
2657 mean, sigma, GetName());
2658 return out;
2659 }
2660 }
2661 } else if (auto p = child.get<RooAbsPdf>(); p) {
2662
2663 auto _me = get<RooAbsArg>();
2664 if (!_me) {
2665 throw std::runtime_error("Cannot constrain non arg");
2666 }
2667
2668 if (!p->dependsOn(*_me)) {
2669 throw std::runtime_error("Constraint does not depend on constrainee");
2670 }
2671
2672 // find a parent that can swallow this pdf ... either a RooProdPdf or a RooWorkspace
2673 auto x = fParent;
2674 while (x && !x->get<RooProdPdf>() && !x->get<RooSimultaneous>() && !x->get<RooWorkspace>()) {
2675 x = x->fParent;
2676 }
2677 if (!x) {
2678 throw std::runtime_error("Nowhere to put constraint");
2679 }
2680 // get datasets of the swallower, and add glob to any globs lists
2681 auto childGlobs = child.globs();
2682 if (!childGlobs.empty()) {
2683 for (auto d : x->datasets()) {
2684 if (auto globs = d->get<RooAbsData>()->getGlobalObservables()) {
2686 newGlobs.add(*childGlobs.get<RooArgList>());
2687 d->get<RooAbsData>()->setGlobalObservables(newGlobs);
2688 }
2689 }
2690 // also add to the workspaces globalObservables lists
2691 if (x->ws()) {
2692 for (auto &[k, v] : GETWSSETS(x->ws())) {
2693 if (k == "globalObservables" || TString(k).EndsWith("_GlobalObservables")) {
2694 const_cast<RooArgSet &>(v).add(*childGlobs.get<RooArgList>());
2695 }
2696 }
2697 }
2698 }
2699 if (auto s = x->get<RooSimultaneous>(); s) {
2700 // put into every channel that features parameter
2701 x->browse();
2702 for (auto &c : *x) {
2703 if (auto a = c->get<RooAbsArg>(); a->dependsOn(*_me))
2704 c->Multiply(child);
2705 }
2706 return child;
2707 } else if (x->get<RooProdPdf>()) {
2708 return x->Multiply(child);
2709 } else {
2710 return x->Add(child, "+");
2711 }
2712 }
2713
2714 throw std::runtime_error(TString::Format("Cannot constrain %s", GetName()));
2715}
2716
2718{
2719
2720 class AutoUpdater {
2721 public:
2722 AutoUpdater(xRooNode &_n) : n(_n) {}
2723 ~AutoUpdater() { n.browse(); }
2724 xRooNode &n;
2725 };
2726 AutoUpdater xxx(*this);
2727
2728 if (fBinNumber != -1) {
2729 // scaling a bin ...
2730 if (child.get<RooAbsReal>()) { // if not child then let fall through to create a child and call self again below
2731 // doing a bin-multiplication .. the parent should have a ParamHistFunc called binFactors
2732 // if it doesn't then create one
2733 auto o = std::dynamic_pointer_cast<RooAbsReal>(acquire(child.fComp));
2734
2735 // get binFactor unless parent is a ParamHistFunc already ...
2736
2737 auto binFactors = (fParent->get<ParamHistFunc>()) ? fParent : fParent->factors().find("binFactors");
2738
2739 // it can happen in a loop over bins() that another node has moved fParent inside a product
2740 // so check for fParent having a client with the ORIGNAME:<name> attribute
2741 if (!binFactors && fParent->get<RooAbsArg>()) {
2742 for (auto c : fParent->get<RooAbsArg>()->clients()) {
2743 if (c->IsA() == RooProduct::Class() &&
2744 c->getAttribute(TString::Format("ORIGNAME:%s", fParent->get()->GetName()))) {
2745 // try getting binFactors out of this
2746 binFactors = xRooNode(*c).factors().find("binFactors");
2747 break;
2748 }
2749 }
2750 }
2751
2752 if (!binFactors) {
2753 fParent
2754 ->Multiply(TString::Format("%s_binFactors",
2755 (fParent->mainChild().get())
2756 ? fParent->mainChild()->GetName()
2757 : (fParent->get() ? fParent->get()->GetName() : fParent->GetName()))
2758 .Data(),
2759 "blankshape")
2760 .SetName("binFactors"); // creates ParamHistFunc with all pars = 1 (shared const)
2761 binFactors = fParent->factors().find("binFactors");
2762 if (!binFactors) {
2763 throw std::runtime_error(
2764 TString::Format("Could not create binFactors in parent %s", fParent->GetName()));
2765 }
2766 // auto phf = binFactors->get<ParamHistFunc>();
2767
2768 // create RooProducts for all the bins ... so that added factors don't affect selves
2769 int i = 1;
2770 for (auto &b : binFactors->bins()) {
2771 auto p = acquireNew<RooProduct>(TString::Format("%s_bin%d", binFactors->get()->GetName(), i),
2772 TString::Format("binFactors of bin %d", i), RooArgList());
2773 p->setStringAttribute("alias", TString::Format("%s=%g", binFactors->GetXaxis()->GetParent()->GetName(),
2774 binFactors->GetXaxis()->GetBinCenter(i)));
2775 b->Multiply(*p);
2776 i++;
2777 }
2778 }
2779 // then scale the relevant bin ... if the relevant bin is a "1" then just drop in our factor (inside a
2780 // RooProduct though, to avoid it getting modified by subsequent multiplies)
2781 auto _bin = binFactors->bins().at(fBinNumber - 1);
2782 if (auto phf = binFactors->get<ParamHistFunc>(); phf && _bin) {
2783#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
2784 RooArgList &pSet = phf->_paramSet;
2785#else
2786 RooArgList &pSet = const_cast<RooArgList &>(phf->paramList());
2787#endif
2788 if (strcmp(_bin->GetName(), "1") == 0) {
2790 for (std::size_t i = 0; i < pSet.size(); i++) {
2791 if (int(i) != fBinNumber - 1) {
2792 all.add(*pSet.at(i));
2793 } else {
2794 all.add(*o);
2795 }
2796 }
2797 pSet.removeAll();
2798 pSet.add(all);
2799 } else {
2800 _bin->fBinNumber = -1; // to avoid infinite loop
2801 return _bin->Multiply(child, opt);
2802 }
2803 // } else {else if(_bin->get<RooProduct>()) {
2804 // // multiply the element which will just add it as a factor in the rooproduct
2805 // return _bin->Multiply(child,opt);
2806 // } else {
2807 // // not a rooproduct in this bin yet ... so need to replace with a rooproduct and
2808 // multiply that
2809 // // this avoids the undesired behaviour of shared binFactors getting all impacted by
2810 // mulitplies RooArgList all; auto new_p =
2811 // acquireNew<RooProduct>(TString::Format("%s_bin%d",binFactors->get()->GetName(),fBinNumber),TString::Format("binFactors
2812 // of bin %d",fBinNumber),RooArgList(*_bin->get<RooAbsArg>()));
2813 // new_p->setStringAttribute("alias","")
2814 // for (int i = 0; i < phf->_paramSet.size(); i++) {
2815 // if (i != fBinNumber - 1) all.add(*phf->_paramSet.at(i));
2816 // else all.add(*new_p);
2817 // }
2818 // phf->_paramSet.removeAll();
2819 // phf->_paramSet.add(all);
2820 // // now multiply that bin having converted it to RooProduct
2821 // return binFactors->bins().at(fBinNumber - 1)->Multiply(child,opt);
2822 // }
2823 }
2824 return xRooNode(*o, binFactors);
2825 }
2826 } else if (!get() && fParent) {
2827 // try to 'create' object based on parentage
2828 // add child as a temporary child to help with decision making
2829 auto _ref = emplace_back(std::shared_ptr<xRooNode>(&const_cast<xRooNode &>(child), [](TObject *) {}));
2830 try {
2831 fComp = fParent->Add(*this, "+").fComp;
2832 } catch (...) {
2833 resize(size() - 1);
2834 std::rethrow_exception(std::current_exception());
2835 }
2836 resize(size() - 1); // remove the temporarily added node
2837 }
2838
2839 if (!child.get()) {
2840 TString sOpt(opt);
2841 sOpt.ToLower();
2842 if (auto o = getObject<RooAbsReal>(child.GetName())) {
2843 auto out = Multiply(xRooNode(o, child.fParent));
2844 // have to protect bin case where get() is null (could change but then must change logic above too)
2845 if (get()) {
2846 Info("Multiply", "Scaled %s by existing factor %s::%s",
2847 mainChild().get() ? mainChild().get()->GetName() : get()->GetName(), o->ClassName(), o->GetName());
2848 }
2849 return out;
2850 } else if (sOpt == "norm") {
2851 if (TString(child.GetName()).Contains("[") && ws()) {
2852 // assume factory method wanted
2853 auto arg = ws()->factory(child.GetName());
2854 if (arg) {
2855 auto out = Multiply(*arg);
2856 if (get()) {
2857 Info("Multiply", "Scaled %s by new norm factor %s",
2858 mainChild().get() ? mainChild().get()->GetName() : get()->GetName(), out->GetName());
2859 }
2860 return out;
2861 }
2862 throw std::runtime_error(TString::Format("Failed to create new normFactor %s", child.GetName()));
2863 }
2864 auto out = Multiply(RooRealVar(child.GetName(), child.GetTitle(), 1, -1e-5, 100));
2865 if (get()) {
2866 Info("Multiply", "Scaled %s by new norm factor %s",
2867 mainChild().get() ? mainChild().get()->GetName() : get()->GetName(), out->GetName());
2868 }
2869 return out;
2870 } else if (sOpt == "shape" || sOpt == "histo" || sOpt == "blankshape") {
2871 // needs axis defined
2872 if (auto ax = GetXaxis(); ax) {
2873 auto h = std::shared_ptr<TH1>(BuildHistogram(dynamic_cast<RooAbsLValue *>(ax->GetParent()), true));
2874 h->Reset();
2875 for (int i = 1; i <= h->GetNbinsX(); i++) {
2876 h->SetBinContent(i, 1);
2877 }
2878 h->SetMinimum(0);
2879 h->SetMaximum(100);
2880 h->SetName(TString::Format(";%s", child.GetName())); // ; char indicates don't "rename" this thing
2881 h->SetTitle(child.GetTitle());
2882 if (sOpt.Contains("shape"))
2883 h->SetOption(sOpt);
2884 auto out = Multiply(*h);
2885 if (get()) {
2886 Info("Multiply", "Scaled %s by new %s factor %s",
2887 mainChild().get() ? mainChild().get()->GetName() : get()->GetName(), sOpt.Data(), out->GetName());
2888 }
2889 return out;
2890 }
2891 } else if (sOpt == "overall") {
2893 child.GetName(), child.GetTitle(), RooArgList(), 1, std::vector<double>(), std::vector<double>()));
2894 if (get() /* can happen this is null if on a bin node with no shapeFactors*/) {
2895 Info("Multiply", "Scaled %s by new overall factor %s",
2896 mainChild().get() ? mainChild().get()->GetName() : get()->GetName(), out->GetName());
2897 }
2898 return out;
2899 } else if (sOpt == "func" && ws()) {
2900 // need to get way to get dependencies .. can't pass all as causes circular dependencies issues.
2901 if (auto arg = ws()->factory(TString("expr::") + child.GetName())) {
2902 auto out = Multiply(*arg);
2903 if (get() /* can happen this is null if on a bin node with no shapeFactors*/) {
2904 Info("Multiply", "Scaled %s by new func factor %s",
2905 mainChild().get() ? mainChild().get()->GetName() : get()->GetName(), out->GetName());
2906 }
2907 return out;
2908 }
2909 }
2910 }
2911 if (auto h = child.get<TH1>(); h && strlen(h->GetOption()) == 0 && strlen(opt) > 0) {
2912 // put the option in the hist
2913 h->SetOption(opt);
2914 }
2915 if (auto w = get<RooWorkspace>(); w) {
2916 // just acquire
2917 std::shared_ptr<TObject> out;
2918 child.convertForAcquisition(*this);
2919 if (child.get<RooAbsReal>())
2920 out = acquire(child.fComp);
2921 return out;
2922 }
2923
2924 if (strcmp(GetName(), ".coef") == 0) { // covers both .coef and .coefs
2925 // need to add this into the relevant coef ... if its not a RooProduct, replace it with one first
2926 if (auto p = fParent->fParent->get<RooAddPdf>()) {
2927 // may be in no-coef mode ... in which case must create coefs (use "ExtendedBindings" but note that these need
2928 // obs list passing to them
2929 if (p->coefList().empty() && !p->pdfList().empty()) {
2930 for (auto _pdf : p->pdfList()) {
2931 const_cast<RooArgList &>(p->coefList())
2932 .add(*acquireNew<RooExtendedBinding>(TString::Format("%s_extBind", _pdf->GetName()),
2933 TString::Format("Expected Events of %s", _pdf->GetTitle()),
2934 *static_cast<RooAbsPdf *>(_pdf)));
2935 }
2936 Info("Multiply", "Created RooExtendedBinding coefficients for all pdfs of %s so that can multiply coef",
2937 p->GetName());
2938 *reinterpret_cast<bool *>(reinterpret_cast<unsigned char *>(p) +
2939 p->Class()->GetDataMemberOffset("_allExtendable")) = false;
2940 *reinterpret_cast<bool *>(reinterpret_cast<unsigned char *>(p) +
2941 p->Class()->GetDataMemberOffset("_haveLastCoef")) = true;
2942 }
2943 for (size_t i = 0; i < p->pdfList().size(); i++) {
2944 if (p->pdfList().at(i) == fParent->get<RooAbsArg>()) {
2945 auto coefs = p->coefList().at(i);
2946 if (!coefs->InheritsFrom("RooProduct")) {
2948 if (!(strcmp(coefs->GetName(), "1") == 0 || strcmp(coefs->GetName(), "ONE") == 0))
2949 oldCoef.add(*coefs);
2950 auto newCoefs = fParent->acquireNew<RooProduct>(
2951 TString::Format("coefs_%s", fParent->GetName()),
2952 TString::Format("coefficients for %s", fParent->GetName()), oldCoef);
2954 for (size_t j = 0; j < p->coefList().size(); j++) {
2955 if (i == j) {
2956 oldCoefs.add(*newCoefs);
2957 } else {
2958 oldCoefs.add(*p->coefList().at(j));
2959 }
2960 }
2961 const_cast<RooArgList &>(p->coefList()).removeAll();
2962 const_cast<RooArgList &>(p->coefList()).add(oldCoefs);
2963 coefs = newCoefs.get();
2964 }
2965 return xRooNode(*coefs, fParent).Multiply(child);
2966 }
2967 }
2968 }
2969 throw std::runtime_error("this coefs case is not supported");
2970 }
2971
2972 if (auto p = get<RooProduct>(); p) {
2973 std::shared_ptr<TObject> out;
2974 auto cc = child.fComp;
2975 bool isConverted = (child.convertForAcquisition(*this) != cc);
2976 if (child.get<RooAbsReal>())
2977 out = acquire(child.fComp);
2978
2979 // child may be a histfunc or a rooproduct of a histfunc and a paramhist if has stat errors
2980 if (auto _f = std::dynamic_pointer_cast<RooHistFunc>(
2981 (child.get<RooProduct>()) ? child.factors()[child.GetName()]->fComp : out);
2982 _f && _f->getAttribute("autodensity")) {
2983 // should we flag this as a density? yes if there's no other term marked as the density
2984 bool hasDensity = false;
2985 for (auto &f : factors()) {
2986 if (f->get<RooAbsArg>()->getAttribute("density")) {
2987 hasDensity = true;
2988 break;
2989 }
2990 }
2991 _f->setAttribute("density", !hasDensity && fParent && fParent->get<RooRealSumPdf>());
2992 if (_f->getAttribute("density")) {
2993
2994 // need to divide by bin widths first
2995 for (int i = 0; i < _f->dataHist().numEntries(); i++) {
2996 auto bin_pars = _f->dataHist().get(i);
2997 _f->dataHist().set(*bin_pars, _f->dataHist().weight() / _f->dataHist().binVolume(*bin_pars));
2998 }
2999 _f->setValueDirty();
3000
3001 // promote the axis vars to observables
3002 for (auto &x : xRooNode("tmp", _f).vars()) {
3003 x->get<RooAbsArg>()->setAttribute("obs");
3004 }
3005 }
3006 _f->setAttribute("autodensity", false);
3007 }
3008
3009 if (isConverted && child.get<RooHistFunc>()) {
3010 Info("Multiply", "Created %s factor %s in %s",
3011 child.get<RooAbsArg>()->getAttribute("density") ? "densityhisto" : "histo", child->GetName(),
3012 p->GetName());
3013 } else if (isConverted && child.get<ParamHistFunc>()) {
3014 Info("Multiply", "Created shape factor %s in %s", child->GetName(), p->GetName());
3015 }
3016
3017 if (auto _f = std::dynamic_pointer_cast<RooAbsReal>(out); _f) {
3018#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
3019 p->_compRSet.add(*_f);
3020#else
3021 const_cast<RooArgList &>(p->realComponents()).add(*_f);
3022#endif
3023 p->setValueDirty();
3024
3025 browse();
3026 xRooNode _out(_f, *this);
3027 for (auto &_par : _out.pars()) {
3028 if (auto s = _par->get<RooAbsArg>()->getStringAttribute("boundConstraint"); s) {
3029 bool found = false;
3030 for (auto &_constr : _par->constraints()) {
3031 if (strcmp(s, _constr->get()->GetName()) == 0) {
3032 // constraint is already included
3033 found = true;
3034 break;
3035 }
3036 }
3037 if (!found) {
3038 Info("Multiply", "Pulling in %s boundConstraint: %s", _par->GetName(), s);
3039 auto _pdf = getObject<RooAbsPdf>(s);
3040 if (!_pdf) {
3041 throw std::runtime_error("Couldn't find boundConstraint");
3042 }
3043 _par->Constrain(_pdf);
3044 }
3045 }
3046 }
3047 sterilize();
3048 return _out;
3049 }
3050 } else if (auto p2 = get<RooProdPdf>(); p2) {
3051
3052 std::shared_ptr<TObject> out;
3053 child.convertForAcquisition(*this);
3054 if (child.get<RooAbsPdf>()) {
3055 out = acquire(child.fComp);
3056 } else if (child.get<RooAbsReal>() && mainChild().get<RooRealSumPdf>()) {
3057 // cannot multiply a RooProdPdf by a non pdf
3058 throw std::runtime_error(TString::Format("Cannot multiply %s by non-pdf %s", GetName(), child.GetName()));
3059 // return mainChild().Add(child); - nov 2022 - used to do this but now replaced with exception above
3060 } else if (!child.get() || child.get<RooAbsReal>()) {
3061 // need to create or hide inside a sumpdf or rooadpdf
3062 std::shared_ptr<RooAbsPdf> _pdf;
3063 if (!child.get() && strcmp(child.GetName(), "components") == 0) {
3065 Form("%s_%s", p2->GetName(), child.GetName()),
3066 (strlen(child.GetTitle()) && strcmp(child.GetTitle(), child.GetName())) ? child.GetTitle()
3067 : p2->GetTitle(),
3068 RooArgList() /*, RooArgList() forces coef-mode if we specify this list */);
3069 _pdf = _sumpdf;
3070 } else {
3072 Form("%s_%s", p2->GetName(), child.GetName()),
3073 (strlen(child.GetTitle()) && strcmp(child.GetTitle(), child.GetName())) ? child.GetTitle()
3074 : p2->GetTitle(),
3075 RooArgList(), RooArgList(), true);
3076 _sumpdf->setFloor(true);
3077 _pdf = _sumpdf;
3078 }
3079 _pdf->setStringAttribute("alias", child.GetName());
3080 // transfer axis attributes if present (TODO: should GetXaxis look beyond the immediate parent?)
3081 _pdf->setStringAttribute("xvar", p2->getStringAttribute("xvar"));
3082 _pdf->setStringAttribute("binning", p2->getStringAttribute("binning"));
3083 out = _pdf;
3084 Info("Multiply", "Created %s::%s in channel %s", _pdf->ClassName(), _pdf->GetName(), p2->GetName());
3085 if (child.get<RooAbsReal>())
3086 xRooNode(*out, *this).Add(child);
3087 }
3088
3089 if (auto _pdf = std::dynamic_pointer_cast<RooAbsPdf>(out); _pdf) {
3090#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
3091 const_cast<RooArgList &>(p2->pdfList()).add(*_pdf);
3092#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 26, 00)
3093 p2->_pdfNSetList.emplace_back(std::make_unique<RooArgSet>("nset"));
3094#else
3095 p->_pdfNSetList.Add(new RooArgSet("nset"));
3096#endif
3097 if (!p2->canBeExtended() && _pdf->canBeExtended()) {
3098 p2->_extendedIndex = p2->_pdfList.size() - 1;
3099 }
3100#else
3101 p2->addPdfs(RooArgSet(*_pdf));
3102#endif
3103 sterilize();
3104 browse();
3105 return xRooNode(_pdf, *this);
3106 }
3107 } else if (auto p3 = get<RooRealSumPdf>(); p3) {
3108 // multiplying all current and future components
3109 std::shared_ptr<TObject> out;
3110 child.convertForAcquisition(*this);
3111 if (child.get<RooAbsReal>()) {
3112 out = acquire(child.fComp);
3113 for (auto &c : components()) {
3114 c->Multiply(out);
3115 }
3116 TString s = p3->getStringAttribute("global_factors");
3117 if (s != "")
3118 s += ";";
3119 s += out->GetName();
3120 p3->setStringAttribute("global_factors", s);
3121 Info(
3122 "Multiply",
3123 "Flagged %s as a global factor in channel %s (is applied to all current and future samples in the channel)",
3124 out->GetName(), p3->GetName());
3125 return xRooNode(out, *this);
3126 }
3127
3128 } else if (auto p4 = get<RooAbsPdf>(); p4 && !(fParent && fParent->get<RooRealSumPdf>())) {
3129 // multiply the coefs (if this isn't part of a RooAddPdf or RooRealSumPdf then we will eventually throw exception
3130 return coefs().Multiply(child);
3131 } else if (auto p5 = get<RooAbsReal>(); p5 && (!get<RooAbsPdf>() || (fParent && fParent->get<RooRealSumPdf>()))) {
3132 // replace this obj with a RooProduct to allow for multiplication
3133
3134 // get the list of clients BEFORE creating the new interpolation ... seems list of clients is inaccurate after
3135 std::set<RooAbsArg *> cl;
3136 for (auto &arg : p5->clients()) {
3137 cl.insert(arg);
3138 }
3139
3140 // if multiple clients, see if only one client is in parentage route
3141 // if so, then assume thats the only client we should replace in
3142 if (cl.size() > 1) {
3143 if (cl.count(fParent->get<RooAbsArg>()) > 0) {
3144 cl.clear();
3145 cl.insert(fParent->get<RooAbsArg>());
3146 } else {
3147 Warning("Multiply", "Scaling %s that has multiple clients", p5->GetName());
3148 }
3149 }
3150
3151 auto new_p = acquireNew<RooProduct>(TString::Format("prod_%s", p5->GetName()), p5->GetTitle(), RooArgList(*p5));
3152 // copy attributes over
3153 for (auto &a : p5->attributes())
3154 new_p->setAttribute(a.c_str());
3155 for (auto &a : p5->stringAttributes())
3156 new_p->setStringAttribute(a.first.c_str(), a.second.c_str());
3157 if (!new_p->getStringAttribute("alias"))
3158 new_p->setStringAttribute("alias", p5->GetName());
3159 auto old_p = p5;
3160 new_p->setAttribute(Form("ORIGNAME:%s", old_p->GetName())); // used in redirectServers to say what this replaces
3161 for (auto arg : cl) {
3162 arg->redirectServers(RooArgSet(*new_p), false, true);
3163 }
3164
3165 fComp = new_p;
3166 return Multiply(child);
3167 }
3168
3169 // before giving up here, assume user wanted a norm factor type if child is just a name
3170 if (!child.get() && strlen(opt) == 0)
3171 return Multiply(child, "norm");
3172
3173 throw std::runtime_error(
3174 TString::Format("Cannot multiply %s by %s%s", GetPath().c_str(), child.GetName(),
3175 (!child.get() && strlen(opt) == 0) ? " (forgot to specify factor type?)" : ""));
3176}
3177
3179{
3180
3181 auto p5 = get<RooAbsArg>();
3182 if (!p5) {
3183 throw std::runtime_error("Only replacement of RooAbsArg is supported");
3184 }
3185 node.convertForAcquisition(*this, "func");
3186
3187 auto new_p = node.get<RooAbsArg>();
3188 if (!new_p) {
3189 throw std::runtime_error(TString::Format("Cannot replace with %s", node.GetName()));
3190 }
3191 auto out = acquire(node.fComp);
3192 new_p = std::dynamic_pointer_cast<RooAbsArg>(out).get();
3193
3194 std::set<RooAbsArg *> cl;
3195 for (auto &arg : p5->clients()) {
3196 if (arg == new_p)
3197 continue; // do not replace in self ... although redirectServers will prevent that anyway
3198 cl.insert(arg);
3199 }
3200
3201 // if multiple clients, see if only one client is in parentage route
3202 // if so, then assume thats the only client we should replace in
3203 if (cl.size() > 1) {
3204 if (fParent && fParent->get<RooAbsArg>() && cl.count(fParent->get<RooAbsArg>()) > 0) {
3205 cl.clear();
3206 cl.insert(fParent->get<RooAbsArg>());
3207 } else {
3208 std::stringstream clientList;
3209 for (auto c : cl)
3210 clientList << c->GetName() << ",";
3211 Warning("Replace", "Replacing %s in all clients: %s", p5->GetName(), clientList.str().c_str());
3212 }
3213 }
3214
3215 new_p->setAttribute(Form("ORIGNAME:%s", p5->GetName())); // used in redirectServers to say what this replaces
3216 for (auto arg : cl) {
3217 // if RooFormulaVar need to ensure the internal formula has been "constructed" otherwise will try to construct
3218 // it from the original expression that may have old parameter in it.
3219 if (auto p = dynamic_cast<RooFormulaVar *>(arg))
3220 p->ok(); // triggers creation of RooFormula
3221 arg->redirectServers(RooArgSet(*new_p), false, true);
3222 }
3223 return node;
3224}
3225
3227{
3228
3229 class AutoUpdater {
3230 public:
3231 AutoUpdater(xRooNode &_n) : n(_n) {}
3232 ~AutoUpdater() { n.browse(); }
3233 xRooNode &n;
3234 };
3235 AutoUpdater xxx(*this);
3236
3237 if (!get() && fParent) {
3238 // try to 'create' object based on parentage
3239 // add child as a temporary child to help with decision making
3240 auto _ref = emplace_back(std::shared_ptr<xRooNode>(&const_cast<xRooNode &>(child), [](TObject *) {}));
3241 try {
3242 fComp = fParent->Add(*this, "+").fComp;
3243 } catch (...) {
3244 resize(size() - 1);
3245 std::rethrow_exception(std::current_exception());
3246 }
3247 resize(size() - 1); // remove the temporarily added node
3248 }
3249
3250 if (auto p = mainChild(); p) {
3251 // variations applied to the main child if has one
3252 return p.Vary(child);
3253 }
3254
3255 if (auto s = get<RooSimultaneous>(); s && s->indexCat().IsA() == RooCategory::Class()) {
3256 // name is used as cat label
3257 std::string label = child.GetName();
3258 if (auto pos = label.find('='); pos != std::string::npos)
3259 label = label.substr(pos + 1);
3260 if (!s->indexCat().hasLabel(label)) {
3261 static_cast<RooCategory &>(const_cast<RooAbsCategoryLValue &>(s->indexCat())).defineType(label.c_str());
3262 }
3263 std::shared_ptr<TObject> out;
3264 child.convertForAcquisition(*this);
3265 if (child.get<RooAbsPdf>()) {
3266 out = acquire(child.fComp); // may create a channel from a histogram
3267 } else if (!child.fComp) {
3268 out = acquireNew<RooProdPdf>(TString::Format("%s_%s", s->GetName(), label.c_str()),
3269 (strlen(child.GetTitle())) ? child.GetTitle() : label.c_str(), RooArgList());
3270 Info("Vary", "Created channel RooProdPdf::%s in model %s", out->GetName(), s->GetName());
3271 }
3272
3273 if (auto _pdf = std::dynamic_pointer_cast<RooAbsPdf>(out); _pdf) {
3274 s->addPdf(*_pdf, label.c_str());
3275 sterilize();
3276 // clear children for reload and update shared axis
3277 clear();
3278 fXAxis.reset();
3279 browse();
3280 return xRooNode(TString::Format("%s=%s", s->indexCat().GetName(), label.data()), _pdf, *this);
3281 }
3282
3283 } else if (auto p = get<RooStats::HistFactory::FlexibleInterpVar>(); p) {
3284
3285 // child needs to be a constvar ...
3286 child.convertForAcquisition(*this);
3287 auto _c = child.get<RooConstVar>();
3288 if (!_c && child.get()) {
3289 throw std::runtime_error("Only pure consts can be set as variations of a flexible interpvar");
3290 }
3291#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
3292 double value = (_c ? _c->getVal() : p->_nominal);
3293 double nomVal = p->_nominal;
3294#else
3295 double value = (_c ? _c->getVal() : p->nominal());
3296 double nomVal = p->nominal();
3297#endif
3298
3299 TString cName(child.GetName());
3300 if (cName == "nominal") {
3301 p->setNominal(value);
3302 return *(this->variations().at(cName.Data()));
3303 }
3304 if (cName.CountChar('=') != 1) {
3305 throw std::runtime_error("unsupported variation form");
3306 }
3307 std::string parName = cName(0, cName.Index('='));
3308 double parVal = TString(cName(cName.Index('=') + 1, cName.Length())).Atof();
3309 if (parVal != 1 && parVal != -1) {
3310 throw std::runtime_error("unsupported variation magnitude");
3311 }
3312 bool high = parVal > 0;
3313
3314 if (parName.empty()) {
3315 p->setNominal(value);
3316 } else {
3317 auto v = fParent->getObject<RooRealVar>(parName);
3318 if (!v)
3319 v = fParent->acquire<RooRealVar>(parName.c_str(), parName.c_str(), -5, 5);
3320 if (!v->hasError())
3321 v->setError(1);
3322
3323 if (!p->findServer(*v)) {
3324#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
3325 p->_paramList.add(*v);
3326 p->_low.push_back(0);
3327 p->_high.push_back(0);
3328 p->_interpCode.push_back(4);
3329#else
3330 const_cast<RooListProxy &>(p->variables()).add(*v);
3331 const_cast<std::vector<double> &>(p->low()).push_back(0);
3332 const_cast<std::vector<double> &>(p->high()).push_back(0);
3333 const_cast<std::vector<int> &>(p->interpolationCodes()).push_back(4);
3334#endif
3335 v->setAttribute(Form("SYMMETRIC%s_%s", high ? "+" : "-", GetName())); // flag for symmetrized
3336 }
3337
3338 if (high) {
3339 p->setHigh(*v, value);
3340 if (v->getAttribute(Form("SYMMETRIC+_%s", GetName()))) {
3341 p->setLow(*v, 2 * nomVal - value);
3342 }
3343 v->setAttribute(Form("SYMMETRIC-_%s", GetName()), false);
3344 } else {
3345 p->setLow(*v, value);
3346 if (v->getAttribute(Form("SYMMETRIC-_%s", GetName()))) {
3347 p->setHigh(*v, 2 * nomVal - value);
3348 }
3349 v->setAttribute(Form("SYMMETRIC+_%s", GetName()), false);
3350 }
3351
3352 /*if (!unconstrained && fParent->pars()[v->GetName()].constraints().empty()) {
3353 fParent->pars()[v->GetName()].constraints().add("normal");
3354 }*/
3355 }
3356 return *(this->variations().at(cName.Data()));
3357 } else if (auto p2 = get<PiecewiseInterpolation>(); p2) {
3358 TString cName(child.GetName());
3359 if (cName.CountChar('=') != 1) {
3360 throw std::runtime_error("unsupported variation form");
3361 }
3362 TString parName = cName(0, cName.Index('='));
3363 double parVal = TString(cName(cName.Index('=') + 1, cName.Length())).Atof();
3364 if (parVal != 1 && parVal != -1) {
3365 throw std::runtime_error("unsupported variation magnitude");
3366 }
3367#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
3368 RooHistFunc *f = dynamic_cast<RooHistFunc *>(p2->_nominal.absArg());
3369 if (!f) {
3370 throw std::runtime_error(
3371 TString::Format("Interpolating %s instead of RooHistFunc", p2->_nominal.absArg()->ClassName()));
3372 }
3373#else
3374 RooHistFunc *f = dynamic_cast<RooHistFunc *>(const_cast<RooAbsReal *>(p2->nominalHist()));
3375 if (!f) {
3376 throw std::runtime_error(
3377 TString::Format("Interpolating %s instead of RooHistFunc", p2->nominalHist()->ClassName()));
3378 }
3379#endif
3380 RooHistFunc *nomf = f;
3381 RooHistFunc *otherf = nullptr;
3382 size_t i = 0;
3383 for (auto par : p2->paramList()) {
3384 if (parName == par->GetName()) {
3385 f = dynamic_cast<RooHistFunc *>((parVal > 0 ? p2->highList() : p2->lowList()).at(i));
3386 otherf = dynamic_cast<RooHistFunc *>((parVal > 0 ? p2->lowList() : p2->highList()).at(i));
3387 break;
3388 }
3389 i++;
3390 }
3391 if (i == p2->paramList().size() && !child.get<RooAbsReal>()) {
3392
3393 // need to add the parameter
3394 auto v = acquire<RooRealVar>(parName, parName, -5, 5);
3395 if (!v->hasError())
3396 v->setError(1);
3397
3398 std::shared_ptr<RooHistFunc> up(
3399 static_cast<RooHistFunc *>(f->Clone(Form("%s_%s_up", f->GetName(), parName.Data()))));
3400 std::shared_ptr<RooHistFunc> down(
3401 static_cast<RooHistFunc *>(f->Clone(Form("%s_%s_down", f->GetName(), parName.Data()))));
3402 // RooHistFunc doesn't clone it's data hist ... do it ourself (will be cloned again if imported into a ws)
3403#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
3404 std::unique_ptr<RooDataHist> h1(
3405 static_cast<RooDataHist *>(f->dataHist().Clone(Form("hist_%s", up->GetName()))));
3406 std::unique_ptr<RooDataHist> h2(
3407 static_cast<RooDataHist *>(f->dataHist().Clone(Form("hist_%s", down->GetName()))));
3408 up->_dataHist = dynamic_cast<RooDataHist *>(f->dataHist().Clone(Form("hist_%s", up->GetName())));
3409 down->_dataHist = dynamic_cast<RooDataHist *>(f->dataHist().Clone(Form("hist_%s", down->GetName())));
3410#else
3411 up->cloneAndOwnDataHist(TString::Format("hist_%s", up->GetName()));
3412 down->cloneAndOwnDataHist(TString::Format("hist_%s", down->GetName()));
3413#endif
3414 auto ups = std::dynamic_pointer_cast<RooHistFunc>(acquire(up, false, true));
3415 auto downs = std::dynamic_pointer_cast<RooHistFunc>(acquire(down, false, true));
3416#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
3417 p2->_highSet.add(*ups.get());
3418 p2->_lowSet.add(*downs.get());
3419 p2->_interpCode.push_back(4);
3420 p2->_paramSet.add(*v);
3421#else
3422 const_cast<RooArgList &>(p2->highList()).add(*ups);
3423 const_cast<RooArgList &>(p2->lowList()).add(*downs);
3424 const_cast<std::vector<int> &>(p2->interpolationCodes()).push_back(4);
3425 const_cast<RooArgList &>(p2->paramList()).add(*v);
3426#endif
3427 p2->setValueDirty();
3428 f = ((parVal > 0) ? ups : downs).get();
3429 otherf = ((parVal > 0) ? downs : ups).get();
3430 // start off with everything being symmetric
3431 f->setStringAttribute("symmetrizes", otherf->GetName());
3432 f->setStringAttribute("symmetrize_nominal", nomf->GetName());
3433 otherf->setStringAttribute("symmetrized_by", f->GetName());
3434
3435 // constrain par if required
3436 /*if (!unconstrained && fParent->pars()[v->GetName()].constraints().empty()) {
3437 fParent->pars()[v->GetName()].constraints().add("normal");
3438 }*/
3439 }
3440
3441 // child.convertForAcquisition(*this);
3442 if (f) {
3443 if (child.get())
3444 xRooNode("tmp", *f, *this) = *child.get();
3445 f->setValueDirty();
3446 xRooNode out(*f, *this);
3447 out.sterilize();
3448 return out;
3449 }
3450
3451 } else if (auto p3 = get<RooConstVar>(); p3) {
3452
3453 // never vary the universal consts ... its too dangerous
3454 if (p3->getAttribute("RooRealConstant_Factory_Object")) {
3455 throw std::runtime_error("Cannot vary pure constants");
3456 }
3457
3458 // inject a FlexibleInterpVar ...
3459
3460 // get the list of clients BEFORE creating the new interpolation ... seems list of clients is inaccurate after
3461 std::set<RooAbsArg *> cl;
3462 for (auto &arg : p3->clients()) {
3463 cl.insert(arg);
3464 }
3465 // if multiple clients, see if only one client is in parentage route
3466 // if so, then assume thats the only client we should replace in
3467 if (cl.size() > 1) {
3468 if (cl.count(fParent->get<RooAbsArg>()) > 0) {
3469 cl.clear();
3470 cl.insert(fParent->get<RooAbsArg>());
3471 } else {
3472 Warning("Vary", "Varying %s that has multiple clients", p3->GetName());
3473 }
3474 }
3475 p3->setStringAttribute("origName", p3->GetName());
3476 TString n = p3->GetName();
3477 p3->SetName(Form("%s_nominal", p3->GetName())); // if problems should perhaps not rename here
3478
3480 std::vector<double>(), std::vector<double>());
3481
3482 // copy attributes over
3483 for (auto &a : p3->attributes())
3484 new_p->setAttribute(a.c_str());
3485 for (auto &a : p3->stringAttributes())
3486 new_p->setStringAttribute(a.first.c_str(), a.second.c_str());
3487 // if (!new_p->getStringAttribute("alias")) new_p->setStringAttribute("alias",p->GetName());
3488 auto old_p = p3;
3489 new_p->setAttribute(Form("ORIGNAME:%s", old_p->GetName())); // used in redirectServers to say what this replaces
3490 for (auto arg : cl) {
3491 arg->redirectServers(RooArgSet(*new_p), false, true);
3492 }
3493
3494 fComp = new_p;
3495 return Vary(child);
3496
3497 } else if (auto p4 = get<RooAbsReal>(); p4) {
3498 // inject an interpolation node
3499
3500 // get the list of clients BEFORE creating the new interpolation ... seems list of clients is inaccurate after
3501 std::set<RooAbsArg *> cl;
3502 for (auto &arg : p4->clients()) {
3503 cl.insert(arg);
3504 }
3505 // if multiple clients, see if only one client is in parentage route
3506 // if so, then assume thats the only client we should replace in
3507 if (cl.size() > 1) {
3508 if (cl.count(fParent->get<RooAbsArg>()) > 0) {
3509 cl.clear();
3510 cl.insert(fParent->get<RooAbsArg>());
3511 } else {
3512 Warning("Vary", "Varying %s that has multiple clients", p4->GetName());
3513 }
3514 }
3515 p4->setStringAttribute("origName", p4->GetName());
3516 TString n = p4->GetName();
3517 p4->SetName(Form("%s_nominal", p4->GetName())); // if problems should perhaps not rename here
3518
3520
3521 // copy attributes over
3522 for (auto &a : p4->attributes())
3523 new_p->setAttribute(a.c_str());
3524 for (auto &a : p4->stringAttributes())
3525 new_p->setStringAttribute(a.first.c_str(), a.second.c_str());
3526 // if (!new_p->getStringAttribute("alias")) new_p->setStringAttribute("alias",p->GetName());
3527 auto old_p = p4;
3528 new_p->setAttribute(Form("ORIGNAME:%s", old_p->GetName())); // used in redirectServers to say what this replaces
3529 for (auto arg : cl) {
3530 arg->redirectServers(RooArgSet(*new_p), false, true);
3531 }
3532
3533 fComp = new_p;
3534 return Vary(child);
3535 }
3536
3537 Print();
3538 throw std::runtime_error(TString::Format("Cannot vary %s with %s", GetName(), child.GetName()));
3539}
3540
3542{
3544}
3545
3546bool xRooNode::SetContent(double value, const char *par, double val)
3547{
3548 return SetContents(RooConstVar(GetName(), GetTitle(), value), par, val);
3549}
3550
3553 {
3554 if (x && b)
3555 x->setBinning(*b);
3556 if (b)
3557 delete b;
3558 }
3559 RooRealVar *x = nullptr;
3560 RooAbsBinning *b = nullptr;
3561};
3562
3564{
3565
3566 if (!get()) {
3567 fComp = std::shared_ptr<TObject>(const_cast<TObject *>(&o), [](TObject *) {});
3568 if (fParent && !fParent->find(GetName())) {
3569 // either a temporary or a placeholder so need to try genuinely adding
3570 fComp = fParent->Add(*this, "+").fComp;
3571 if (auto a = get<RooAbsArg>(); a && strcmp(a->GetName(), GetName()) && !a->getStringAttribute("alias")) {
3572 a->setStringAttribute("alias", GetName());
3573 }
3574 if (!fComp)
3575 throw std::runtime_error("Cannot determine type");
3576 return *this;
3577 }
3578 }
3579
3580 if (auto h = dynamic_cast<const TH1 *>(&o); h) {
3581 /*auto f = get<RooHistFunc>();
3582 if (!f) {
3583 // if it's a RooProduct locate child with the same name
3584 if (get<RooProduct>()) {
3585 f = factors()[GetName()]->get<RooHistFunc>();
3586 }
3587
3588
3589
3590 }*/
3591 bool _isData = get<RooAbsData>();
3593 if (_isData) {
3594 // need to ensure x-axis matches this h
3595 auto ax = GetXaxis();
3596 if (!ax)
3597 throw std::runtime_error("no xaxis");
3598 auto _v = dynamic_cast<RooRealVar *>(ax->GetParent());
3599 if (_v) {
3600 _b.x = _v;
3601 _b.b = dynamic_cast<RooAbsBinning *>(_v->getBinningPtr(nullptr)->Clone());
3602 if (h->GetXaxis()->IsVariableBinSize()) {
3603 _v->setBinning(RooBinning(h->GetNbinsX(), h->GetXaxis()->GetXbins()->GetArray()));
3604 } else {
3605 _v->setBinning(RooUniformBinning(h->GetXaxis()->GetXmin(), h->GetXaxis()->GetXmax(), h->GetNbinsX()));
3606 }
3607 }
3608 }
3609
3610 if (true) {
3611 for (int bin = 1; bin <= h->GetNbinsX(); bin++) {
3612 SetBinContent(bin, h->GetBinContent(bin));
3613 /*double value = h->GetBinContent(bin);
3614 auto bin_pars = f->dataHist().get(bin - 1);
3615 if (f->getAttribute("density")) {
3616 value /= f->dataHist().binVolume(*bin_pars);
3617 }
3618 f->dataHist().set(*bin_pars, value);*/
3619 if (!_isData && h->GetSumw2N() && !SetBinError(bin, h->GetBinError(bin)))
3620 throw std::runtime_error("Failed setting stat error");
3621 }
3622 return *this;
3623 }
3624 } else if (auto _c = dynamic_cast<const RooConstVar *>(&o); _c) {
3625
3626 if (auto a = get<RooAbsArg>();
3627 (a && a->isFundamental()) || get<RooConstVar>() || get<RooStats::HistFactory::FlexibleInterpVar>()) {
3628 SetBinContent(1, _c->getVal());
3629 return *this;
3630 } else if (get<RooAbsData>()) { // try to do assignment to a dataset (usually setting a bin content)
3631 SetBinContent(0, _c->getVal());
3632 return *this;
3633 }
3634 }
3635
3636 throw std::runtime_error("Assignment failed");
3637
3638 /*
3639
3640 if (fParent && !fParent->mk()) {
3641 throw std::runtime_error("mk failure");
3642 }
3643
3644 if (fComp) return *this;
3645
3646 if (o.InheritsFrom("RooAbsArg")) {
3647 fComp = acquire(std::shared_ptr<TObject>(const_cast<TObject*>(&o),[](TObject* o){}));
3648 std::dynamic_pointer_cast<RooAbsArg>(fComp)->setStringAttribute("alias",GetName());
3649 }
3650
3651 if (fComp && fParent) {
3652 fParent->incorporate(fComp);
3653 }
3654
3655
3656 return *this;
3657 */
3658}
3659
3661{
3662 try {
3663 auto _pars = pars();
3664 // std::unique_ptr<RooAbsCollection> snap(_pars.argList().snapshot());
3665 TStringToken pattern(constParValues, ",");
3666 std::map<RooAbsRealLValue *, double> valsToSet;
3667 while (pattern.NextToken()) {
3668 auto idx = pattern.Index('=');
3669 TString pat = (idx == -1) ? TString(pattern) : TString(pattern(0, idx));
3670 double val =
3671 (idx == -1) ? std::numeric_limits<double>::quiet_NaN() : TString(pattern(idx + 1, pattern.Length())).Atof();
3672 for (auto p : _pars.argList()) {
3673 if (TString(p->GetName()).Contains(TRegexp(pat, true))) {
3674 p->setAttribute("Constant", true);
3675 if (!std::isnan(val)) {
3676 valsToSet[dynamic_cast<RooAbsRealLValue *>(p)] = val;
3677 // dynamic_cast<RooAbsRealLValue *>(p)->setVal(val); // don't set yet, to allow for asimov dataset
3678 // creation based on current values
3679 }
3680 }
3681 }
3682 }
3683 // use the first selected dataset
3684 auto _dsets = datasets();
3685 TString dsetName = "";
3686 for (auto &d : _dsets) {
3687 if (d->get()->TestBit(1 << 20)) {
3688 dsetName = d->get()->GetName();
3689 break;
3690 }
3691 }
3692 auto _nll = nll(dsetName.Data());
3693 // can now set the values
3694 for (auto [p, v] : valsToSet) {
3695 p->setVal(v);
3696 }
3697 _nll.fitConfigOptions()->SetValue("LogSize", 65536);
3698 _nll.fitConfig()->MinimizerOptions().SetPrintLevel(0);
3699 auto fr = _nll.minimize();
3700 //_pars.argList() = *snap; // restore values - irrelevant as SetFitResult will restore values
3701 if (!fr.get())
3702 throw std::runtime_error("Fit Failed");
3703 SetFitResult(fr.get());
3705 for (unsigned int i = 0; i < fr->numStatusHistory(); i++) {
3706 statusCodes += TString::Format("\n%s = %d", fr->statusLabelHistory(i), fr->statusCodeHistory(i));
3707 }
3708 const TGWindow *w =
3709 (gROOT->GetListOfBrowsers()->At(0))
3710 ? dynamic_cast<TGWindow *>(static_cast<TBrowser *>(gROOT->GetListOfBrowsers()->At(0))->GetBrowserImp())
3711 : gClient->GetRoot();
3712 if (fr->status() != 0) {
3713 new TGMsgBox(gClient->GetRoot(), w, "Fit Finished with Bad Status Code",
3714 TString::Format("%s\nData = %s\nFit Status Code = %d\nCov Quality = %d\n-------------%s",
3715 fr->GetName(), dsetName.Data(), fr->status(), fr->covQual(), statusCodes.Data()),
3717 } else if (fr->covQual() != 3 && _nll.fitConfig()->ParabErrors()) {
3718 new TGMsgBox(gClient->GetRoot(), w, "Fit Finished with Bad Covariance Quality",
3719 TString::Format("%s\nData = %s\nFit Status Code = %d\nCov Quality = %d\n-------------%s",
3720 fr->GetName(), dsetName.Data(), fr->status(), fr->covQual(), statusCodes.Data()),
3722 } else {
3723 new TGMsgBox(gClient->GetRoot(), w, "Fit Finished Successfully",
3724 TString::Format("%s\nData = %s\nFit Status Code = %d\nCov Quality = %d\n-------------%s",
3725 fr->GetName(), dsetName.Data(), fr->status(), fr->covQual(), statusCodes.Data()));
3726 }
3727 } catch (const std::exception &e) {
3728 new TGMsgBox(
3729 gClient->GetRoot(),
3730 (gROOT->GetListOfBrowsers()->At(0))
3731 ? dynamic_cast<TGWindow *>(static_cast<TBrowser *>(gROOT->GetListOfBrowsers()->At(0))->GetBrowserImp())
3732 : gClient->GetRoot(),
3733 "Exception", e.what(), kMBIconExclamation, kMBOk); // deletes self on dismiss?
3734 }
3735}
3736
3738{
3739 try {
3740 datasets().Add(datasetName, expected ? "asimov" : "toy");
3741 } catch (const std::exception &e) {
3742 new TGMsgBox(
3743 gClient->GetRoot(),
3744 (gROOT->GetListOfBrowsers()->At(0))
3745 ? dynamic_cast<TGWindow *>(static_cast<TBrowser *>(gROOT->GetListOfBrowsers()->At(0))->GetBrowserImp())
3746 : gClient->GetRoot(),
3747 "Exception", e.what(),
3748 kMBIconExclamation); // deletes self on dismiss?
3749 }
3750}
3751
3752void xRooNode::_scan_(const char *what, double nToys, const char *xvar, int nBinsX, double lowX,
3753 double highX /*, const char*, int, double, double*/, const char *constParValues)
3754{
3755 try {
3758
3759 // use the first selected dataset
3760 auto _dsets = datasets();
3761 TString dsetName = "";
3762 for (auto &d : _dsets) {
3763 if (d->get()->TestBit(1 << 20)) {
3764 dsetName = d->get()->GetName();
3765 break;
3766 }
3767 }
3768 auto _pars = pars();
3769 std::unique_ptr<RooAbsCollection> snap(_pars.argList().snapshot());
3770 TStringToken pattern(constParValues, ",");
3771 while (pattern.NextToken()) {
3772 auto idx = pattern.Index('=');
3773 TString pat = (idx == -1) ? TString(pattern) : TString(pattern(0, idx));
3774 double val =
3775 (idx == -1) ? std::numeric_limits<double>::quiet_NaN() : TString(pattern(idx + 1, pattern.Length())).Atof();
3776 for (auto par : _pars.argList()) {
3777 if (TString(par->GetName()).Contains(TRegexp(pat, true))) {
3778 par->setAttribute("Constant", true);
3779 if (!std::isnan(val)) {
3780 dynamic_cast<RooAbsRealLValue *>(par)->setVal(val);
3781 }
3782 }
3783 }
3784 }
3785 auto hs = nll(dsetName.Data()).hypoSpace(sXvar);
3786 if (nToys) {
3787 sWhat += " toys";
3788 if (nToys > 0) {
3789 sWhat += TString::Format("=%g", nToys);
3790 }
3791 }
3792 hs.SetTitle(sWhat + " scan" + ((dsetName != "") ? TString::Format(" [data=%s]", dsetName.Data()) : ""));
3793 int scanStatus = hs.scan(sWhat + " visualize", nBinsX, lowX, highX);
3794 if (scanStatus != 0) {
3795 new TGMsgBox(
3796 gClient->GetRoot(),
3797 (gROOT->GetListOfBrowsers()->At(0))
3798 ? dynamic_cast<TGWindow *>(static_cast<TBrowser *>(gROOT->GetListOfBrowsers()->At(0))->GetBrowserImp())
3799 : gClient->GetRoot(),
3800 "Scan Finished with Bad Status Code",
3801 TString::Format("%s\nData = %s\nScan Status Code = %d", hs.GetName(), dsetName.Data(), scanStatus),
3803 }
3804 hs.SetName(TUUID().AsString());
3805 if (ws()) {
3806 if (auto res = hs.result())
3807 ws()->import(*res);
3808 }
3809
3810 _pars.argList() = *snap; // restore pars
3811
3812 } catch (const std::exception &e) {
3813 new TGMsgBox(
3814 gClient->GetRoot(),
3815 (gROOT->GetListOfBrowsers()->At(0))
3816 ? dynamic_cast<TGWindow *>(static_cast<TBrowser *>(gROOT->GetListOfBrowsers()->At(0))->GetBrowserImp())
3817 : gClient->GetRoot(),
3818 "Exception", e.what(), kMBIconExclamation);
3819 }
3820}
3821
3822void xRooNode::_SetBinContent_(int bin, double value, const char *par, double parVal)
3823{
3824 try {
3825 SetBinContent(bin, value, strlen(par) > 0 ? par : nullptr, parVal);
3826 } catch (const std::exception &e) {
3827 new TGMsgBox(gClient->GetRoot(), gClient->GetRoot(), "Exception", e.what(),
3828 kMBIconExclamation); // deletes self on dismiss?
3829 }
3830}
3831
3833{
3834 try {
3835#if ROOT_VERSION_CODE > ROOT_VERSION(6, 29, 00)
3836 // if this is a collection of values, populate a TF1 and display as a dialog
3837 if (!get() && TString(GetName()).BeginsWith("!")) {
3838 browse();
3839 RooArgList args;
3840 for (auto a : *this) {
3841 if (auto arg = a->get<RooRealVar>())
3842 args.add(*arg);
3843 }
3844 TF1 f(GetName(), 0.0, 1.0, std::min(int(args.size()), 10));
3845 int i = 0;
3846 int j = 0;
3847 for (auto c : args) {
3848 j++;
3849 if (j < value) {
3850 continue;
3851 }
3852 auto v = dynamic_cast<RooRealVar *>(c);
3853 f.SetParName(i, c->GetName());
3854 if (v) {
3855 f.SetParLimits(i, v->getMin(), v->getMax());
3856 if (v->isConstant())
3857 f.FixParameter(i, v->getVal());
3858 else {
3859 f.SetParameter(i, v->getVal());
3860 f.SetParError(i, v->getError());
3861 }
3862 }
3863 i++;
3864 if (i == 10) {
3865 break; // max 10 pars shown
3866 }
3867 }
3868 int ret = 0;
3870 gClient->GetDefaultRoot(),
3871 (gROOT->GetListOfBrowsers()->At(0))
3872 ? dynamic_cast<TGWindow *>(static_cast<TBrowser *>(gROOT->GetListOfBrowsers()->At(0))->GetBrowserImp())
3873 : gClient->GetDefaultRoot(),
3874 &f, nullptr, &ret);
3875 if (ret) {
3876 // user has changed parameter values etc, propagate back to parameters
3877 for (i = 0; i < f.GetNpar(); i++) {
3878 auto c = args.find(f.GetParName(i));
3879 auto v = dynamic_cast<RooRealVar *>(c);
3880 if (v) {
3881 v->setVal(f.GetParameter(i));
3882 double low, high;
3883 f.GetParLimits(i, low, high);
3884 if (low == high) {
3885 v->setConstant(low); // if low==high==0 then is not marked constant
3886 } else {
3887 v->setRange(low, high);
3888 }
3889 }
3890 }
3891 }
3892 return;
3893 }
3894#endif
3895
3896 if (!SetContent(value))
3897 throw std::runtime_error("Failed to SetContent");
3898 } catch (const std::exception &e) {
3899 new TGMsgBox(gClient->GetRoot(), gClient->GetRoot(), "Exception", e.what(),
3900 kMBIconExclamation); // deletes self on dismiss?
3901 }
3902}
3903
3904bool xRooNode::SetBinContent(int bin, double value, const char *par, double parVal)
3905{
3906
3907 // create if needed
3908 if (!get()) {
3909 if (fParent && !find(GetName())) {
3910 // if have a binning we create a histogram to match it
3911 if (auto ax = GetXaxis(); ax) {
3912 std::shared_ptr<TH1D> h;
3913 auto _b = dynamic_cast<Axis2 *>(ax)->binning();
3914 auto t = TH1::AddDirectoryStatus();
3915 TH1::AddDirectory(false);
3916 if (_b->isUniform()) {
3917 h.reset(new TH1D(GetName(), GetTitle(), _b->numBins(), _b->lowBound(), _b->highBound()));
3918 } else {
3919 h.reset(new TH1D(GetName(), GetTitle(), _b->numBins(), _b->array()));
3920 }
3921 h->SetOption("nostyle"); // don't transfer style when added
3922 h->SetDirectory(nullptr);
3924 h->GetXaxis()->SetName(TString::Format("%s;%s", ax->GetParent()->GetName(), ax->GetName()));
3925 fComp = h;
3926 }
3927 fComp = fParent->Add(*this, "sample").fComp;
3928 }
3929 }
3930
3931 // if it's a RooProduct locate child with the same name
3932 if (get<RooProduct>()) {
3933 return factors()[GetName()]->SetBinContent(bin, value, par, parVal);
3934 }
3935
3936 if (get<RooAbsData>()) {
3937 if (auto _data = get<RooDataSet>(); _data) {
3938 auto _ax = (bin) ? GetXaxis() : nullptr;
3939 if (!_ax && bin) {
3940 throw std::runtime_error("Cannot determine binning to fill data");
3941 }
3942 if (_ax && _ax->GetNbins() < bin) {
3943 throw std::out_of_range(TString::Format("%s range %s only has %d bins", _ax->GetParent()->GetName(),
3944 _ax->GetName(), _ax->GetNbins()));
3945 }
3946 RooArgSet obs;
3947
3948 TString cut = "";
3949
3950 for (auto _c : coords()) { // coords() moves vars to their respective coordinates too
3951 if (auto _cat = _c->get<RooAbsCategoryLValue>(); _cat) {
3952 if (cut != "")
3953 cut += " && ";
3954 cut += TString::Format("%s==%d", _cat->GetName(), _cat->getCurrentIndex());
3955 obs.add(*_cat); // note: if we ever changed coords to return clones, would need to keep coords alive
3956 } else if (auto _rv = _c->get<RooAbsRealLValue>(); _rv) {
3957 // todo: check coordRange is a single range rather than multirange
3958 if (cut != "")
3959 cut += " && ";
3960 cut +=
3961 TString::Format("%s>=%f&&%s<%f", _rv->GetName(), _rv->getMin(_rv->getStringAttribute("coordRange")),
3962 _rv->GetName(), _rv->getMax(_rv->getStringAttribute("coordRange")));
3963 obs.add(*_rv); // note: if we ever changed coords to return clones, would need to keep coords alive
3964 } else {
3965 throw std::runtime_error("SetBinContent of data: Unsupported coordinate type");
3966 }
3967 }
3968
3969 RooFormulaVar cutFormula("cut1", cut, obs); // doing this to avoid complaints about unused vars
3970 RooFormulaVar icutFormula("icut1", TString::Format("!(%s)", cut.Data()), obs);
3971
3972 TString cut2;
3973 if (_ax) {
3974 cut2 = TString::Format("%s >= %f && %s < %f", _ax->GetParent()->GetName(), _ax->GetBinLowEdge(bin),
3975 _ax->GetParent()->GetName(), _ax->GetBinUpEdge(bin));
3976 obs.add(*dynamic_cast<RooAbsArg *>(_ax->GetParent()));
3977 } else {
3978 cut2 = "1==1";
3979 }
3980 RooFormulaVar cutFormula2("cut2", cut + " && " + cut2, obs);
3981 RooFormulaVar icutFormula2("icut2", TString::Format("!(%s && %s)", cut.Data(), cut2.Data()), obs);
3982
3983 // // go up through parents looking for slice obs
3984 // auto _p = fParent;
3985 // while(_p) {
3986 // TString pName(_p->GetName());
3987 // if (auto pos = pName.Index('='); pos != -1) {
3988 // if(auto _obs = _p->getObject<RooAbsLValue>(pName(0,pos)); _obs) {
3989 // if(auto _cat = dynamic_cast<RooAbsCategoryLValue*>(_obs.get()); _cat) {
3990 // _cat->setLabel(pName(pos+1,pName.Length()));
3991 // cut += TString::Format("%s%s==%d", (cut=="")?"":" && ",_cat->GetName(),
3992 // _cat->getCurrentIndex());
3993 // } else if(auto _var = dynamic_cast<RooAbsRealLValue*>(_obs.get()); _var) {
3994 // _var->setVal(TString(pName(pos+1,pName.Length())).Atof());
3995 // // TODO: Cut for this!!
3996 // }
3997 // obs.add(*dynamic_cast<RooAbsArg*>(_obs.get()));
3998 // } else {
3999 // throw std::runtime_error("Unknown observable, could not find");
4000 // }
4001 // }
4002 // _p = _p->fParent;
4003 // }
4004
4005 // add observables to dataset if necessary
4006 RooArgSet l(obs);
4007 l.remove(*_data->get(), true, true);
4008 if (!l.empty()) {
4009 // addColumns method is buggy: https://github.com/root-project/root/issues/8787
4010 // incredibly though, addColumn works??
4011 for (auto &x : l) {
4012 _data->addColumn(*x);
4013 }
4014 // instead create a copy dataset and merge it into current
4015 // cant use merge because it drops weightVar
4016 /*RooDataSet tmp("tmp","tmp",l);
4017 for(int i=0;i<_data->numEntries();i++) tmp.add(l);
4018 _data->merge(&tmp);*/
4019 // delete _data->addColumns(l);
4020 }
4021 // before adding, ensure range is good to cover
4022 for (auto &o : obs) {
4023 if (auto v = dynamic_cast<RooRealVar *>(o); v) {
4024 if (auto dv = dynamic_cast<RooRealVar *>(_data->get()->find(v->GetName())); dv) {
4025 if (v->getMin() < dv->getMin())
4026 dv->setMin(v->getMin());
4027 if (v->getMax() > dv->getMax())
4028 dv->setMax(v->getMax());
4029 }
4030 } else if (auto c = dynamic_cast<RooCategory *>(o); c) {
4031 if (auto dc = dynamic_cast<RooCategory *>(_data->get()->find(c->GetName())); dc) {
4032 if (!dc->hasLabel(c->getCurrentLabel())) {
4033 dc->defineType(c->getCurrentLabel(), c->getCurrentIndex());
4034 }
4035 }
4036 }
4037 }
4038
4039 // using SetBinContent means dataset must take on a binned form at these coordinates
4040 // if number of entries doesnt match number of bins then will 'bin' the data
4041 if (bin) {
4042 if (auto _nentries = std::unique_ptr<RooAbsData>(_data->reduce(cutFormula))->numEntries();
4043 _nentries != _ax->GetNbins()) {
4044 auto _contents = GetBinContents(1, _ax->GetNbins());
4045
4046 if (_nentries > 0) {
4047 Info("SetBinContent", "Binning %s in channel: %s", GetName(), cut.Data());
4048 auto _reduced = std::unique_ptr<RooAbsData>(_data->reduce(icutFormula));
4049 _data->reset();
4050 for (int j = 0; j < _reduced->numEntries(); j++) {
4051 auto _obs = _reduced->get(j);
4052 _data->add(*_obs, _reduced->weight());
4053 }
4054 }
4055 for (int i = 1; i <= _ax->GetNbins(); i++) {
4056 // can skip over the bin we will be setting to save a reduce step below
4057 if (i == bin)
4058 continue;
4059 dynamic_cast<RooAbsLValue *>(_ax->GetParent())->setBin(i - 1, _ax->GetName());
4060 _data->add(obs, _contents.at(i - 1));
4061 }
4062 }
4063 }
4064 // remove existing entries
4065 if (std::unique_ptr<RooAbsData>(_data->reduce(cutFormula2))->numEntries() > 0) {
4066 auto _reduced = std::unique_ptr<RooAbsData>(_data->reduce(icutFormula2));
4067 _data->reset();
4068 for (int j = 0; j < _reduced->numEntries(); j++) {
4069 auto _obs = _reduced->get(j);
4070 _data->add(*_obs, _reduced->weight());
4071 }
4072 }
4073 if (_ax)
4074 dynamic_cast<RooAbsLValue *>(_ax->GetParent())->setBin(bin - 1, _ax->GetName());
4075 _data->add(obs, value);
4076 if (auto bb = getBrowsable(".sourceds"))
4077 return bb->SetBinContent(bin, value, par, parVal); // apply to source ds if we have one
4078 return true;
4079
4080 } else if (get<RooDataHist>()) {
4081 throw std::runtime_error("RooDataHist not supported yet");
4082 }
4083 }
4084
4085 if (auto _varies = variations(); !_varies.empty() || (par && strlen(par))) {
4086 if (!par || strlen(par) == 0) {
4087 return _varies["nominal"]->SetBinContent(bin, value, par, parVal);
4088 } else if (auto it = _varies.find(Form("%s=%g", par, parVal)); it) {
4089 return it->SetBinContent(bin, value);
4090 } else {
4091 // need to create the variation : note - if no variations existed up to now this will inject a new node
4092 // so we should redirect ourself to the new node
4093 // TODO: Do we need to redirect parents?
4094 TString s = Form("%s=%g", par, parVal);
4095 return Vary(s.Data()).SetBinContent(bin, value);
4096 }
4097 }
4098
4099 auto o = get();
4100 if (auto p = dynamic_cast<RooRealVar *>(o); p) {
4101 if (!par || strlen(par) == 0) {
4102 if (p->getMax() < value)
4103 p->setMax(value);
4104 if (p->getMin() > value)
4105 p->setMin(value);
4106 p->setVal(value);
4107 sterilize();
4108 return true;
4109 }
4110
4111 } else if (auto c = dynamic_cast<RooConstVar *>(o); c) {
4112
4113 // if parent is a FlexibleInterpVar, change the value in that .
4114 if (strcmp(c->GetName(), Form("%g", c->getVal())) == 0) {
4115 c->SetNameTitle(Form("%g", value), Form("%g", value));
4116 }
4117#if ROOT_VERSION_CODE < ROOT_VERSION(6, 24, 00)
4118 c->_value = value; // in future ROOT versions there is a changeVal method!
4119#else
4120 c->changeVal(value);
4121#endif
4122
4124 fParent->Vary(*this);
4125 }
4126
4127 sterilize();
4128 return true;
4129 } else if (auto f = dynamic_cast<RooHistFunc *>(o); f) {
4130 auto bin_pars = f->dataHist().get(bin - 1);
4131 if (f->getAttribute("density")) {
4132 value /= f->dataHist().binVolume(*bin_pars);
4133 }
4134 f->dataHist().set(*bin_pars, value);
4135 f->setValueDirty();
4136
4137 if (auto otherfName = f->getStringAttribute("symmetrized_by"); otherfName) {
4138 // broken symmetry, so update flags ...
4139 f->setStringAttribute("symmetrized_by", nullptr);
4140 if (auto x = getObject<RooAbsArg>(otherfName); x) {
4141 x->setStringAttribute("symmetrizes", nullptr);
4142 x->setStringAttribute("symmetrize_nominal", nullptr);
4143 }
4144 } else if (auto otherfName2 = f->getStringAttribute("symmetrizes"); otherfName2) {
4145 auto nomf = getObject<RooHistFunc>(f->getStringAttribute("symmetrize_nominal"));
4147 if (nomf && otherf) {
4148 otherf->dataHist().set(*bin_pars, 2 * nomf->dataHist().weight(bin - 1) - value);
4149 otherf->setValueDirty();
4150 }
4151 }
4152 sterilize();
4153 return true;
4154 } else if (auto f2 = dynamic_cast<RooStats::HistFactory::FlexibleInterpVar *>(o); f2) {
4155 // changing nominal value
4156 f2->setNominal(value);
4157 }
4158 throw std::runtime_error(TString::Format("unable to set bin content of %s", GetPath().c_str()));
4159}
4160
4161bool xRooNode::SetBinData(int bin, double value, const xRooNode &data)
4162{
4163 if (data.get<RooAbsData>()) {
4164 // attach as a child before calling datasets(), so that is included in the list
4165 push_back(std::make_shared<xRooNode>(data));
4166 }
4167 auto node = datasets()[data.GetName()];
4168 if (data.get<RooAbsData>()) {
4169 // remove the child we attached
4170 resize(size() - 1);
4171 }
4172 return node->SetBinContent(bin, value);
4173}
4174
4175bool xRooNode::SetData(const TObject &obj, const xRooNode &data)
4176{
4177 if (data.get<RooAbsData>()) {
4178 // attach as a child before calling datasets(), so that is included in the list
4179 push_back(std::make_shared<xRooNode>(data));
4180 }
4181 auto node = datasets()[data.GetName()];
4182 if (data.get<RooAbsData>()) {
4183 // remove the child we attached
4184 resize(size() - 1);
4185 }
4186 return node->SetContents(obj);
4187}
4188
4189bool xRooNode::SetBinError(int bin, double value)
4190{
4191
4192 // if it's a RooProduct locate child with the same name
4193 if (get<RooProduct>()) {
4194 return factors()[GetName()]->SetBinError(bin, value);
4195 }
4196
4197 if (auto _varies = variations(); !_varies.empty()) {
4198 return _varies["nominal"]->SetBinError(bin, value);
4199 }
4200
4201 auto o = get();
4202
4203 if (auto f = dynamic_cast<RooHistFunc *>(o); f) {
4204
4205 // if (f->getAttribute("density")) { value /= f->dataHist().binVolume(*bin_pars); } - commented out because DON'T
4206 // convert .. sumw and sumw2 attributes will be stored not as densities
4207
4208 // NOTE: Can only do this because factors() makes parents of its children it's own parent (it isn't the parent)
4209 // If ever make factors etc part of the parentage then this would need tweaking to get to the true parent
4210 // find first parent that is a RooProduct, that is where the statFactor would live
4211 // stop as soon as we reach pdf object
4212 auto _prodParent = fParent;
4213 while (_prodParent && !_prodParent->get<RooProduct>() && !_prodParent->get<RooAbsPdf>()) {
4214 if (_prodParent->get<PiecewiseInterpolation>() && strcmp(GetName(), "nominal")) {
4215 _prodParent.reset();
4216 break; // only the 'nominal' variation can look for a statFactor outside the variation container
4217 }
4218 _prodParent = _prodParent->fParent;
4219 }
4220 auto _f_stat =
4221 (_prodParent && !_prodParent->get<RooAbsPdf>()) ? _prodParent->factors().find("statFactor") : nullptr;
4222 auto f_stat = (_f_stat) ? _f_stat->get<ParamHistFunc>() : nullptr;
4223 if (_f_stat && _f_stat->get() && !f_stat) {
4224 throw std::runtime_error("stat factor must be a paramhistfunc");
4225 }
4226
4227 // stat uncertainty lives in the "statFactor" factor, each sample has its own one,
4228 // but they can share parameters
4229 if (!f_stat) {
4230 if (value == 0)
4231 return true;
4233 for (auto &p : xRooNode("tmp", *f, std::shared_ptr<xRooNode>(nullptr)).vars()) {
4234 if (parNames != "")
4235 parNames += ",";
4236 parNames += p->get()->GetName();
4237 }
4238 auto h = std::unique_ptr<TH1>(f->dataHist().createHistogram(parNames
4239#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 27, 00)
4240 ,
4242#endif
4243 ));
4244 h->Reset();
4245 h->SetName("statFactor");
4246 h->SetTitle(TString::Format("StatFactor of %s", f->GetTitle()));
4247 h->SetOption("blankshape");
4248
4249 // multiply parent if is nominal
4250 auto toMultiply = this;
4251 if (strcmp(GetName(), "nominal") == 0 && fParent && fParent->get<PiecewiseInterpolation>())
4252 toMultiply = fParent.get();
4253
4254 f_stat = dynamic_cast<ParamHistFunc *>(toMultiply->Multiply(*h).get());
4255 if (!f_stat) {
4256 throw std::runtime_error("Failed creating stat shapeFactor");
4257 }
4258 }
4259
4260 auto phf = f_stat;
4261
4262 TString prefix = f->getStringAttribute("statPrefix");
4263 if (value && prefix == "") {
4264 // find the first parent that can hold components (RooAddPdf, RooRealSumPdf, RooAddition, RooWorkspace) ... use
4265 // that name for the stat factor
4266 auto _p = fParent;
4267 while (_p && !(_p->get()->InheritsFrom("RooRealSumPdf") || _p->get()->InheritsFrom("RooAddPdf") ||
4268 _p->get()->InheritsFrom("RooWorkspace") || _p->get()->InheritsFrom("RooAddition"))) {
4269 _p = _p->fParent;
4270 }
4271 prefix = TString::Format("stat_%s", (_p && _p->get<RooAbsReal>()) ? _p->get()->GetName() : f->GetName());
4272 }
4273 auto newVar = (value == 0) ? getObject<RooRealVar>("1")
4274 : acquire<RooRealVar>(Form("%s_bin%d", prefix.Data(), bin),
4275 Form("%s_bin%d", prefix.Data(), bin), 1);
4276#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
4277 RooArgList &pSet = phf->_paramSet;
4278#else
4279 RooArgList &pSet = const_cast<RooArgList &>(phf->paramList());
4280#endif
4281 auto var = dynamic_cast<RooRealVar *>(&pSet[bin - 1]);
4282
4283 if (newVar.get() != var) {
4284 // need to swap out var for newVar
4285 // replace ith element in list with new func, or inject into RooProduct
4287 for (std::size_t i = 0; i < pSet.size(); i++) {
4288 if (int(i) != bin - 1) {
4289 all.add(*pSet.at(i));
4290 } else {
4291 all.add(*newVar);
4292 }
4293 }
4294 pSet.removeAll();
4295 pSet.add(all);
4296 }
4297
4298 xRooNode v((value == 0) ? *var : *newVar, *this);
4299 auto rrv = dynamic_cast<RooRealVar *>(v.get());
4300 if (strcmp(rrv->GetName(), "1") != 0) {
4301 TString origName = (f->getStringAttribute("origName")) ? f->getStringAttribute("origName") : GetName();
4302 rrv->setStringAttribute(Form("sumw2_%s", origName.Data()), TString::Format("%f", pow(value, 2)));
4303 auto bin_pars = f->dataHist().get(bin - 1);
4304 auto _binContent = f->dataHist().weight();
4305 if (f->getAttribute("density")) {
4306 _binContent *= f->dataHist().binVolume(*bin_pars);
4307 }
4308 rrv->setStringAttribute(Form("sumw_%s", origName.Data()), TString::Format("%f", _binContent));
4309 double sumw2 = 0;
4310 double sumw = 0;
4311 for (auto &[s, sv] : rrv->stringAttributes()) {
4312 if (s.find("sumw_") == 0) {
4313 sumw += TString(sv).Atof();
4314 } else if (s.find("sumw2_") == 0) {
4315 sumw2 += TString(sv).Atof();
4316 }
4317 }
4318 if (sumw2 && sumw2 != std::numeric_limits<double>::infinity()) {
4319 double tau = pow(sumw, 2) / sumw2;
4320 rrv->setError((tau < 1e-15) ? 1e15 : (/*rrv->getVal()*/ 1. / sqrt(tau))); // not sure why was rrv->getVal()?
4321 rrv->setConstant(false);
4322 // parameter must be constrained
4323 auto _constr = v.constraints();
4324 // std::cout << " setting constraint " << v.GetName() << " nomin=" << tau << std::endl;
4325 if (_constr.empty()) {
4326 rrv->setStringAttribute("boundConstraint", _constr.Add("poisson").get()->GetName());
4327 } else {
4328 auto _glob = _constr.at(0)->obs().at(0)->get<RooRealVar>();
4329 // TODO: Update any globs snapshots that are designed to match the nominal
4330 _glob->setStringAttribute("nominal", TString::Format("%f", tau));
4331 double _min = tau * (1. - 5. * sqrt(1. / tau));
4332 double _max = tau * (1. + 5. * sqrt(1. / tau));
4333 _glob->setRange(_min, _max);
4334 _glob->setVal(tau);
4335 _constr.at(0)->pp().at(0)->SetBinContent(0, tau);
4336 rrv->setStringAttribute("boundConstraint", _constr.at(0)->get()->GetName());
4337 }
4338 rrv->setRange(std::max((1. - 5. * sqrt(1. / tau)), 1e-15), 1. + 5. * sqrt(1. / tau));
4339 } else {
4340 // remove constraint
4341 if (auto _constr = v.constraints(); !_constr.empty()) {
4342 v.constraints().Remove(*_constr.at(0));
4343 }
4344 // set const if sumw2 is 0 (i.e. no error)
4345 rrv->setVal(1);
4346 rrv->setError(0);
4347 rrv->setConstant(sumw2 == 0);
4348 }
4349 }
4350
4351 return true;
4352 }
4353
4354 throw std::runtime_error(TString::Format("%s SetBinError failed", GetName()));
4355}
4356
4357std::shared_ptr<xRooNode> xRooNode::at(const std::string &name, bool browseResult) const
4358{
4359 auto res = find(name, browseResult);
4360 if (res == nullptr)
4361 throw std::out_of_range(name + " does not exist");
4362 return res;
4363}
4364
4365////////////////////////////////////////////////////////////////////////////////
4366/// The RooWorkspace this node belong to, if any
4367
4369{
4370 if (auto _w = get<RooWorkspace>(); _w)
4371 return _w;
4372 if (auto a = get<RooAbsArg>(); a && GETWS(a)) {
4373 return GETWS(a);
4374 }
4375 if (fParent)
4376 return fParent->ws();
4377 return nullptr;
4378}
4379
4381{
4382
4383 xRooNode out(".constraints", nullptr, *this);
4384
4385 std::function<RooAbsPdf *(const xRooNode &n, RooAbsArg &par, std::set<RooAbsPdf *> ignore)> getConstraint;
4386 getConstraint = [&](const xRooNode &n, RooAbsArg &par, std::set<RooAbsPdf *> ignore) {
4387 if (auto _pdf = n.get<RooAbsPdf>()) {
4388 if (ignore.count(_pdf))
4389 return (RooAbsPdf *)nullptr;
4390 ignore.insert(_pdf);
4391 }
4392 auto o = n.get<RooProdPdf>();
4393 if (!o) {
4394 if (n.get<RooSimultaneous>()) {
4395 // check all channels for a constraint if is simultaneous
4396 for (auto &c : n.bins()) {
4397 if (auto oo = getConstraint(*c, par, ignore); oo) {
4398 return oo;
4399 }
4400 }
4401 return (RooAbsPdf *)nullptr;
4402 } else if (n.get<RooAbsPdf>() && n.fParent && n.fParent->get<RooWorkspace>()) {
4403 // reached top-level pdf, which wasn't a simultaneous, so stop here
4404 return (RooAbsPdf *)nullptr;
4405 } else if (auto _ws = n.get<RooWorkspace>(); _ws) {
4406 // reached a workspace, check for any pdf depending on parameter that isnt the ignore
4407 for (auto p : _ws->allPdfs()) {
4408 if (ignore.count(static_cast<RooAbsPdf *>(p)))
4409 continue;
4410 if (p->dependsOn(par)) {
4411 out.emplace_back(std::make_shared<xRooNode>(par.GetName(), *p, *this));
4412 }
4413 }
4414 }
4415 if (!n.fParent)
4416 return (RooAbsPdf *)nullptr;
4417 return getConstraint(*n.fParent, par, ignore);
4418 }
4419 for (auto p : o->pdfList()) {
4420 if (ignore.count(static_cast<RooAbsPdf *>(p)))
4421 continue;
4422 if (p->dependsOn(par)) {
4423 out.emplace_back(std::make_shared<xRooNode>(par.GetName(), *p, *this));
4424 }
4425 }
4426 return (RooAbsPdf *)nullptr;
4427 };
4428
4429 for (auto &p : vars()) {
4430 auto v = dynamic_cast<RooAbsReal *>(p->get());
4431 if (!v)
4432 continue;
4433 if (v->getAttribute("Constant") && v != get<RooAbsReal>())
4434 continue; // skip constants unless we are getting the constraints of a parameter itself
4435 if (v->getAttribute("obs"))
4436 continue; // skip observables ... constraints constrain pars not obs
4437 getConstraint(*this, *v, {get<RooAbsPdf>()});
4438 /*if (auto c = ; c) {
4439 out.emplace_back(std::make_shared<Node2>(p->GetName(), *c, *this));
4440 }*/
4441 }
4442
4443 // finish by removing any constraint that contains another constraint for the same par
4444 // and consolidate common pars
4445 auto it = out.std::vector<std::shared_ptr<xRooNode>>::begin();
4446 while (it != out.std::vector<std::shared_ptr<xRooNode>>::end()) {
4447 bool removeIt = false;
4448 for (auto &c : out) {
4449 if (c.get() == it->get())
4450 continue;
4451 if ((*it)->get<RooAbsArg>()->dependsOn(*c->get<RooAbsArg>())) {
4452 removeIt = true;
4453 std::set<std::string> parNames;
4454 std::string _cName = c->GetName();
4455 do {
4456 parNames.insert(_cName.substr(0, _cName.find(';')));
4457 _cName = _cName.substr(_cName.find(';') + 1);
4458 } while (_cName.find(';') != std::string::npos);
4459 parNames.insert(_cName);
4460 _cName = it->get()->GetName();
4461 do {
4462 parNames.insert(_cName.substr(0, _cName.find(';')));
4463 _cName = _cName.substr(_cName.find(';') + 1);
4464 } while (_cName.find(';') != std::string::npos);
4465 parNames.insert(_cName);
4466 _cName = "";
4467 for (auto &x : parNames) {
4468 if (!_cName.empty())
4469 _cName += ";";
4470 _cName += x;
4471 }
4472 c->TNamed::SetName(_cName.c_str());
4473 break;
4474 }
4475 }
4476 if (removeIt) {
4477 it = out.erase(it);
4478 } else {
4479 ++it;
4480 }
4481 }
4482
4483 // if getting constraints of a fundamental then use the constraint names instead of the par name (because would be
4484 // all same otherwise)
4485 if (get<RooAbsArg>() && get<RooAbsArg>()->isFundamental()) {
4486 for (auto &o : out) {
4487 o->TNamed::SetName(o->get()->GetName());
4488 }
4489 }
4490
4491 return out;
4492}
4493
4494std::shared_ptr<TObject> xRooNode::convertForAcquisition(xRooNode &acquirer, const char *opt) const
4495{
4496
4497 TString sOpt(opt);
4498 sOpt.ToLower();
4500 if (sOpt == "func")
4501 sName = TString("factory:") + sName;
4502
4503 // if arg is a histogram, will acquire it as a RooHistFunc unless no conversion
4504 // todo: could flag not to convert
4505 if (auto h = get<TH1>(); h) {
4506 TString sOpt2(h->GetOption());
4507 std::map<std::string, std::string> stringAttrs;
4508 while (sOpt2.Contains("=")) {
4509 auto pos = sOpt2.Index("=");
4510 auto start = sOpt2.Index(";") + 1;
4511 if (start > pos)
4512 start = 0;
4513 auto end = sOpt2.Index(";", pos);
4514 if (end == -1)
4515 end = sOpt2.Length();
4516 stringAttrs[sOpt2(start, pos - start)] = sOpt2(pos + 1, end - pos - 1);
4517 sOpt2 = TString(sOpt2(0, start)) + TString(sOpt2(end + 1, sOpt2.Length()));
4518 }
4521 if (origName.BeginsWith(';'))
4522 origName = origName(1, origName.Length());
4523 if (newObjName.BeginsWith(';')) {
4524 newObjName =
4525 newObjName(1, newObjName.Length()); // special case if starts with ';' then don't create a fancy name
4526 } else if (acquirer.get() && !acquirer.get<RooWorkspace>()) {
4528 "%s_%s", (acquirer.mainChild().get()) ? acquirer.mainChild()->GetName() : acquirer->GetName(),
4529 newObjName.Data());
4530 }
4531 // can convert to a RooHistFunc, or RooParamHist if option contains 'shape'
4532 TString varName = h->GetXaxis()->GetName();
4533 std::string binningName = newObjName.Data();
4534 if (auto pos = varName.Index(';'); pos != -1) {
4535 binningName = varName(pos + 1, varName.Length());
4536 varName = varName(0, pos);
4537 }
4538
4539 if (varName == "xaxis" &&
4540 !acquirer.get<RooSimultaneous>()) { // default case, try to take axis var and binning from the acquirer
4541 if (auto ax = acquirer.GetXaxis(); ax) {
4542 varName = ax->GetParent()->GetName();
4543 // TODO: check the binning is consistent before using - at least will check nBins below
4544 binningName = ax->GetName();
4545 } else if (acquirer.obs().size() == 1)
4546 varName = acquirer.obs().at(0)->get()->GetName(); // TODO what if no obs but Xaxis var is defined?
4547 }
4548 auto x = acquirer.acquire<RooRealVar>(varName, h->GetXaxis()->GetTitle(), h->GetXaxis()->GetXmin(),
4549 h->GetXaxis()->GetXmax());
4550 if (x->getMin() > h->GetXaxis()->GetXmin())
4551 x->setMin(h->GetXaxis()->GetXmin());
4552 if (x->getMax() < h->GetXaxis()->GetXmax())
4553 x->setMax(h->GetXaxis()->GetXmax());
4554 if (!x->hasBinning(binningName.c_str())) {
4555 if (h->GetXaxis()->IsVariableBinSize()) {
4556 x->setBinning(RooBinning(h->GetNbinsX(), h->GetXaxis()->GetXbins()->GetArray()), binningName.c_str());
4557 } else {
4558 x->setBinning(
4559 RooUniformBinning(h->GetXaxis()->GetXmin(), h->GetXaxis()->GetXmax(), h->GetXaxis()->GetNbins()),
4560 binningName.c_str());
4561 }
4562 x->getBinning(binningName.c_str()).SetTitle(h->GetXaxis()->GetTitle());
4563 if (x->getBinningNames().size() == 2) {
4564 // this was the first binning, so copy it over to be the default binning too
4565 x->setBinning(x->getBinning(binningName.c_str()));
4566 }
4567 } else {
4568 // TODO check binning is compatible with histogram
4569 if (x->getBinning(binningName.c_str()).numBins() != h->GetNbinsX()) {
4570 throw std::runtime_error(
4571 TString::Format("binning mismatch for binning %s of %s", binningName.c_str(), x->GetName()));
4572 }
4573 }
4574
4575 std::shared_ptr<RooAbsArg> _f;
4576
4577 // if acquirer is a RooSimultaneous, will use histogram to define a channel
4578 if (acquirer.get<RooSimultaneous>()) {
4579 _f = acquirer.acquireNew<RooProdPdf>(newObjName, (strlen(h->GetTitle())) ? h->GetTitle() : h->GetName(),
4580 RooArgList());
4581 for (auto &[k, v] : stringAttrs) {
4582 _f->setStringAttribute(k.c_str(), v.c_str());
4583 }
4584 x->setAttribute("obs", true);
4585 } else if (sOpt2.Contains("shape")) {
4586 RooArgList list;
4587 for (int i = 0; i < x->getBinning(binningName.c_str()).numBins(); i++) {
4588 std::shared_ptr<RooAbsArg> arg;
4589 if (sOpt2.Contains("blankshape")) {
4590 arg = acquirer.acquire2<RooAbsArg, RooRealVar>("1", "1", 1);
4591 } else {
4592 if (!h) {
4593 arg = acquirer.acquireNew<RooRealVar>(TString::Format("%s_bin%d", newObjName.Data(), i + 1), "", 1);
4594 }
4595 if (h->GetMinimumStored() != -1111 || h->GetMaximumStored() != -1111) {
4596 arg = acquirer.acquireNew<RooRealVar>(TString::Format("%s_bin%d", newObjName.Data(), i + 1), "",
4597 h->GetBinContent(i + 1), h->GetMinimumStored(),
4598 h->GetMaximumStored());
4599 } else {
4600 arg = acquirer.acquireNew<RooRealVar>(TString::Format("%s_bin%d", newObjName.Data(), i + 1), "",
4601 h->GetBinContent(i + 1));
4602 }
4603 }
4604 list.add(*arg);
4605 }
4606 // paramhistfunc requires the binnings to be loaded as default at construction time
4607 // so load binning temporarily
4608 auto tmp = dynamic_cast<RooAbsBinning *>(x->getBinningPtr(nullptr)->Clone());
4609 x->setBinning(x->getBinning(binningName.c_str()));
4610 _f = acquirer.acquireNew<ParamHistFunc>(newObjName, h->GetTitle(), *x, list);
4611#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
4612 dynamic_cast<ParamHistFunc *>(_f.get())->_paramSet.setName("paramSet"); // so can see when print
4613#else
4614 const_cast<RooArgList &>(dynamic_cast<ParamHistFunc *>(_f.get())->paramList())
4615 .setName("paramSet"); // so can see when print
4616#endif
4617 x->setBinning(*tmp); // restore binning
4618 delete tmp;
4619 for (auto &[k, v] : stringAttrs) {
4620 _f->setStringAttribute(k.c_str(), v.c_str());
4621 }
4622 } else {
4623 auto dh = acquirer.acquireNew<RooDataHist>(Form("hist_%s", newObjName.Data()), h->GetTitle(), *x,
4624 binningName.c_str() /* binning name*/);
4625 if (!dh) {
4626 throw std::runtime_error("Couldn't make data hist");
4627 }
4628 auto f = acquirer.acquireNew<RooHistFunc>(newObjName, h->GetTitle(), *x, *dh,
4629 0 /*interpolation order between bins*/);
4630 f->forceNumInt();
4631 f->setAttribute("autodensity"); // where it gets inserted will determine if this is a density or not
4632 _f = f;
4633
4634 for (auto &[k, v] : stringAttrs) {
4635 _f->setStringAttribute(k.c_str(), v.c_str());
4636 }
4637
4638 // need to do these settings here because used in the assignment step
4639 _f->setStringAttribute("xvar", x->GetName());
4640 _f->setStringAttribute("binning", binningName.c_str());
4641 if (strcmp(_f->GetName(), origName.Data()) && !_f->getStringAttribute("alias"))
4642 _f->setStringAttribute("alias", origName);
4643
4644 // copy values over using the assignment operator - may convert to a RooProduct if there are stat uncerts
4645 xRooNode tmp(h->GetName(), _f, acquirer);
4646 tmp = *h;
4647 _f = std::dynamic_pointer_cast<RooAbsArg>(tmp.fComp); // in case got upgrade to a RooProduct
4648 }
4649
4650 _f->setStringAttribute("xvar", x->GetName());
4651 _f->setStringAttribute("binning", binningName.c_str());
4652 // style(h); // will transfer styling to object if necessary - not doing because this method used with plane hists
4653 // frequently
4654 if (strcmp(_f->GetName(), origName.Data()) && !_f->getStringAttribute("alias"))
4655 _f->setStringAttribute("alias", origName);
4656
4657 fComp = _f;
4658 return _f;
4659 } else if (!get() && sName.BeginsWith("factory:") && acquirer.ws()) {
4660 TString s(sName);
4661 s = TString(s(8, s.Length()));
4662 fComp.reset(acquirer.ws()->factory(s), [](TObject *) {});
4663 if (fComp) {
4664 const_cast<xRooNode *>(this)->TNamed::SetName(fComp->GetName());
4665 }
4666 return fComp;
4667 }
4668
4669 return fComp;
4670}
4671
4672std::shared_ptr<TStyle> xRooNode::style(TObject *initObject, bool autoCreate) const
4673{
4674 return std::dynamic_pointer_cast<TStyle>(styles(initObject, autoCreate).fComp);
4675}
4676
4678{
4679 TString t = GetTitle();
4680
4681 auto arg = get<RooAbsArg>();
4682 if (!initObject && !arg && !gROOT->GetStyle(t)) {
4683 return nullptr;
4684 }
4685
4686 std::unique_ptr<TObject> argInitObject;
4687
4688 if (initObject) {
4689 t = (strlen(initObject->GetTitle())) ? initObject->GetTitle() : initObject->GetName();
4690 } else if (arg) {
4691 if (arg->getStringAttribute("style")) {
4692 t = arg->getStringAttribute("style");
4693 } else if (autoCreate) {
4694 // args will default to histo's object styling, whatever that currently may be
4695 argInitObject = std::make_unique<TH1D>(GetName(), GetTitle(), 1, 0, 1);
4696 initObject = argInitObject.get();
4697 } else {
4698 return nullptr;
4699 }
4700 }
4701
4702 std::shared_ptr<TStyle> style; // use to keep alive for access from GetStyle below, in case getObject has decided to
4703 // return the owning ptr (for some reason)
4704 if (!gROOT->GetStyle(t)) {
4705 if ((style = getObject<TStyle>(t.Data()))) {
4706 // loaded style (from workspace?) so put in list and use that
4707 gROOT->GetListOfStyles()->Add(style.get());
4708 } else {
4709 if (!autoCreate)
4710 return nullptr;
4711 // create new style - gets put in style list automatically so don't have to delete
4712 // acquire them so saved to workspaces for auto reload ...
4713 style = const_cast<xRooNode &>(*this).acquireNew<TStyle>(t.Data(),
4714 TString::Format("Style for %s component", t.Data()));
4715 if (auto x = dynamic_cast<TAttLine *>(initObject))
4716 ((TAttLine &)*style) = *x;
4717 if (auto x = dynamic_cast<TAttFill *>(initObject))
4718 ((TAttFill &)*style) = *x;
4719 if (auto x = dynamic_cast<TAttMarker *>(initObject))
4720 ((TAttMarker &)*style) = *x;
4721 gROOT->GetListOfStyles()->Add(style.get());
4722 }
4723 } else {
4724 style = std::shared_ptr<TStyle>(gROOT->GetStyle(t), [](TStyle *) {});
4725 }
4726
4727 if (arg && !arg->getStringAttribute("style")) {
4728 arg->setStringAttribute("style", style->GetName());
4729 }
4730
4731 return xRooNode(style, *this);
4732}
4733
4734std::shared_ptr<TObject> xRooNode::acquire(const std::shared_ptr<TObject> &arg, bool checkFactory, bool mustBeNew)
4735{
4736 if (!arg)
4737 return nullptr;
4738 if (!fAcquirer && !get<RooWorkspace>() && fParent)
4739 return fParent->acquire(arg, checkFactory, mustBeNew);
4740
4741 // if has a workspace and our object is the workspace or is in the workspace then add this object to workspace
4742 auto _ws = (fAcquirer) ? nullptr : ws();
4743 if (_ws && (get() == _ws || _ws->arg(GetName()) || (arg && strcmp(arg->GetName(), GetName()) == 0))) {
4745 RooMsgService::instance().setGlobalKillBelow(RooFit::WARNING);
4746 if (auto a = dynamic_cast<RooAbsArg *>(arg.get()); a) {
4747 auto out_arg = _ws->arg(a->GetName());
4748 TString aName = arg->GetName();
4749 int ii = 1;
4750 while (out_arg && mustBeNew) {
4751 a->SetName(TString::Format("%s_%d", aName.Data(), ii++));
4752 out_arg = _ws->arg(a->GetName());
4753 }
4754 if (aName != a->GetName())
4755 Warning("acquire", "Renaming to %s", a->GetName());
4756 if (!out_arg) {
4757 bool done = false;
4758 if (checkFactory) {
4759 if (auto res = _ws->factory(arg->GetName()); res) {
4760 a = res;
4761 done = true;
4762 }
4763 }
4764 if (!done && _ws->import(*a, RooFit::RecycleConflictNodes())) {
4765 if (GETWS(a) != _ws) {
4766 Info("acquire", "A copy of %s has been added to workspace %s", a->GetName(), _ws->GetName());
4767 }
4768 RooMsgService::instance().setGlobalKillBelow(msglevel);
4769 return nullptr;
4770 }
4771 // sanitizeWS(); // clears the caches that might exist up to now, as well interfere with getParameters calls
4772 std::set<std::string> setNames;
4773 for (auto &aa : GETWSSETS(_ws)) {
4774 if (TString(aa.first.c_str()).BeginsWith("CACHE_")) {
4775 setNames.insert(aa.first);
4776 }
4777 }
4778 for (auto &aa : setNames)
4779 ws()->removeSet(aa.c_str());
4780 out_arg = _ws->arg(a->GetName());
4781 if (GETWS(out_arg) != _ws) { // seems that when objects imported their ws isn't set
4782 out_arg->setWorkspace(*_ws);
4783 }
4784 }
4785 RooMsgService::instance().setGlobalKillBelow(msglevel);
4786 return std::shared_ptr<TObject>(out_arg, [](TObject *) {});
4787 } else if (auto a2 = dynamic_cast<RooAbsData *>(arg.get()); a2) {
4788 if (_ws->import(*a2, RooFit::Embedded())) {
4789 RooMsgService::instance().setGlobalKillBelow(msglevel);
4790 return nullptr;
4791 }
4792 RooMsgService::instance().setGlobalKillBelow(msglevel);
4793 return std::shared_ptr<TObject>(_ws->embeddedData(arg->GetName()), [](TObject *) {});
4794 } else if (arg->InheritsFrom("RooFitResult") || arg->InheritsFrom("TTree") || arg->IsA() == TStyle::Class() ||
4795 arg->InheritsFrom("RooStats::HypoTestInverterResult") ||
4796 arg->InheritsFrom("RooStats::HypoTestResult")) {
4797 // ensure will have a unique name for import if must be new
4798 TNamed *aNamed = dynamic_cast<TNamed *>(arg.get());
4799 TString aName = arg->GetName();
4800 TObject *out_arg = _ws->genobj(arg->GetName());
4801 int ii = 1;
4802 while (aNamed && out_arg && mustBeNew) {
4803 aNamed->SetName(TString::Format("%s;%d", aName.Data(), ii++));
4804 out_arg = _ws->genobj(aNamed->GetName());
4805 }
4806 if (!out_arg) {
4807 if (aName != arg->GetName()) {
4808 Warning("acquire", "Renaming to %s", arg->GetName());
4809 }
4810 if (_ws->import(*arg, false /*replace existing*/)) {
4811 RooMsgService::instance().setGlobalKillBelow(msglevel);
4812 return nullptr;
4813 }
4814 out_arg = _ws->genobj(arg->GetName());
4815 }
4816 RooMsgService::instance().setGlobalKillBelow(msglevel);
4817 /* this doesnt work because caller has its own version of fParent, not the one in the browser
4818 for(auto o : *gROOT->GetListOfBrowsers()) {
4819 if(auto b = dynamic_cast<TBrowser*>(o); b){
4820 if(auto _b = dynamic_cast<TGFileBrowser*>( dynamic_cast<TRootBrowser*>(b->GetBrowserImp())->fActBrowser
4821 ); _b) { if (auto item = _b->fListTree->FindItemByObj(_b->fRootDir,this); item) { auto _tmp = _b->fListLevel;
4822 _b->fListLevel = item;
4823 bool _tmp2 = item->IsOpen();
4824 item->SetOpen(false);
4825 this->Browse(b);
4826 item->SetOpen(_tmp2);
4827 _b->fListLevel = _tmp;
4828 }
4829 }
4830 }
4831 }*/
4832 return std::shared_ptr<TObject>(out_arg, [](TObject *) {});
4833 }
4834 RooMsgService::instance().setGlobalKillBelow(msglevel);
4835 // Warning("acquire","Not implemented acquisition of object %s",arg->GetName());
4836 // return nullptr;
4837 }
4838 if (!mustBeNew && fProvider) {
4839 auto out = fProvider->getObject(arg->GetName(), arg->ClassName());
4840 if (out)
4841 return out;
4842 }
4843 auto _owned = find(".memory");
4844 if (!_owned) {
4845 _owned = emplace_back(std::make_shared<xRooNode>(".memory", nullptr, *this));
4846 }
4847 // look for exact name, dont use 'find' because doesnt work if trying to find "1" and it doesn't exist, will get back
4848 // idx 1 instead
4849 if (!mustBeNew) {
4850 for (auto &r : *_owned) {
4851 if (strcmp(r->GetName(), arg->GetName()) == 0 && strcmp(r->get()->ClassName(), arg->ClassName()) == 0) {
4852 return r->fComp;
4853 }
4854 }
4855 }
4856 if (!fProvider)
4857 std::cout << GetName() << " taking over " << arg->ClassName() << "::" << arg->GetName() << std::endl;
4858 /*emplace_back(std::make_shared<Node2>(".memory",nullptr,*this))*/
4859 return _owned->emplace_back(std::make_shared<xRooNode>(arg->GetName(), arg, *this))->fComp;
4860 // return arg;
4861}
4862
4863bool xRooNode::SetXaxis(const char *name, const char *title, int nbins, double low, double high)
4864{
4865 RooUniformBinning b(low, high, nbins, name);
4866 b.SetTitle(title);
4867 return SetXaxis(b);
4868}
4869
4870bool xRooNode::SetXaxis(const char *name, const char *title, int nbins, const double *bins)
4871{
4872 RooBinning b(nbins, bins, name);
4873 b.SetTitle(title);
4874 return SetXaxis(b);
4875}
4876
4878{
4879
4880 auto name = binning.GetName();
4881 double high = binning.highBound();
4882 double low = binning.lowBound();
4883 // int nbins = binning.numBins();
4884 auto title = binning.GetTitle();
4885
4886 // if have any dependents and name isn't one of them then stop
4887 auto _deps = vars();
4888 /*if(!_deps.empty() && !_deps.find(name)) {
4889 throw std::runtime_error(TString::Format("%s Does not depend on %s",GetName(),name));
4890 }*/
4891
4892 // object will need to exist
4893 if (!get()) {
4894 if (fParent && !find(GetName())) {
4895 fComp = fParent->Add(*this, "+").fComp;
4896 }
4897 }
4898
4899 auto a = get<RooAbsArg>();
4900 if (!a)
4901 throw std::runtime_error("Cannot SetXaxis of non-arg");
4902
4903 auto _x = acquire<RooRealVar>(name, title, low, high);
4904 _x->setBinning(binning, a->GetName());
4905 _x->getBinning(a->GetName()).SetTitle(title);
4906 if (_x->getBinningNames().size() == 2) {
4907 // this was the first binning, so copy it over to be the default binning too
4908 _x->setBinning(_x->getBinning(a->GetName()));
4909 } else {
4910 // ensure the default binning is wide enough to cover this range
4911 // the alternative to this would be to ensure setNormRange of all pdfs
4912 // are set to correct range (then default can be narrower than some of the named binnings)
4913 if (_x->getMax() < high)
4914 _x->setMax(high);
4915 if (_x->getMin() > low)
4916 _x->setMin(low);
4917 }
4918
4919 if (!_deps.find(name) && get<RooAbsPdf>()) {
4920 // creating a variable for a pdf we will assume it should be an observable
4921 _x->setAttribute("obs");
4922 }
4923
4924 a->setStringAttribute("xvar", _x->GetName());
4925 a->setStringAttribute("binning", a->GetName());
4926 fXAxis.reset(); // remove any existing xaxis
4927
4928 return true;
4929}
4930
4932{
4933 if (!ax)
4934 return false;
4935 if (ax->IsVariableBinSize()) {
4936 return SetXaxis(ax->GetName(), ax->GetTitle(), ax->GetNbins(), ax->GetXbins()->GetArray());
4937 } else {
4938 return SetXaxis(ax->GetName(), ax->GetTitle(), ax->GetNbins(), ax->GetXmin(), ax->GetXmax());
4939 }
4940}
4941
4942bool xRooNode::contains(const std::string &name) const
4943{
4944 return find(name, false) != nullptr;
4945}
4946
4947std::shared_ptr<xRooNode> xRooNode::find(const std::string &name, bool browseResult) const
4948{
4949 std::string partname = (name.find('/') != std::string::npos) ? name.substr(0, name.find('/')) : name;
4950 auto _s = (!get() && fParent) ? fParent->get<RooSimultaneous>()
4951 : get<RooSimultaneous>(); // makes work if doing simPdf.bins()["blah"]
4952 std::string extra = (_s) ? _s->indexCat().GetName() : "";
4953 for (auto &child : *this) {
4954 if (auto _obj = child->get(); name == child->GetName() || partname == child->GetName() ||
4955 (_obj && name == _obj->GetName()) || (_obj && partname == _obj->GetName()) ||
4956 (!extra.empty() && ((extra + "=" + name) == child->GetName() ||
4957 (extra + "=" + partname) == child->GetName()))) {
4958 if (browseResult)
4959 child->browse(); // needed so can go at()->at()->at()...
4960 if (partname != name && name != child->GetName()) {
4961 return child->at(name.substr(partname.length() + 1));
4962 }
4963 return child;
4964 }
4965 if (partname.find('.') != 0) { // do not allow mainChild browsing if trying to find a "." child ... as is done in
4966 // getObject for ".memory"
4967 if (auto x = mainChild(); x && strcmp(child->GetName(), x.GetName()) == 0) {
4968 // can browse directly into main children as if their children were our children
4969 for (auto &child2 : x.browse()) {
4970 if (auto _obj = child2->get(); name == child2->GetName() || partname == child2->GetName() ||
4971 (_obj && name == _obj->GetName()) ||
4972 (_obj && partname == _obj->GetName())) {
4973 if (browseResult)
4974 child2->browse(); // needed for onward read (or is it? there's a browse above too??)
4975 if (partname != name && name != child2->GetName()) {
4976 return child2->at(name.substr(partname.length() + 1));
4977 }
4978 return child2;
4979 }
4980 }
4981 }
4982 }
4983 }
4984 // before giving up see if partName is numeric and indexes within the range
4985 if (TString s(partname); s.IsDec() && size_t(s.Atoi()) < size()) {
4986 auto child2 = at(s.Atoi());
4987 if (partname != name) {
4988 return child2->at(name.substr(partname.length() + 1));
4989 }
4990 return child2;
4991 }
4992 // allow calling of find on a RooWorkspace to access getObject objects ...
4993 if (get<RooWorkspace>() && name != ".memory") {
4994 if (auto obj = getObject(name)) {
4995 auto out = std::make_shared<xRooNode>(obj, *this);
4996 if (browseResult)
4997 out->browse();
4998 return out;
4999 }
5000 }
5001 return nullptr;
5002}
5003
5004std::shared_ptr<xRooNode> xRooNode::operator[](const std::string &name)
5005{
5006 std::string partname = (name.find('/') != std::string::npos) ? name.substr(0, name.find('/')) : name;
5007 browse();
5008 auto _s = (!get() && fParent) ? fParent->get<RooSimultaneous>()
5009 : get<RooSimultaneous>(); // makes work if doing simPdf.bins()["blah"]
5010 std::string extra = (_s) ? _s->indexCat().GetName() : "";
5011 std::shared_ptr<xRooNode> folderNode;
5012 for (auto &child : *this) {
5013 if (name == child->GetName() || partname == child->GetName() ||
5014 (!extra.empty() &&
5015 ((extra + "=" + name) == child->GetName() || (extra + "=" + partname) == child->GetName()))) {
5016 child->browse(); // needed for onward read (or is it? there's a browse above too??)
5017 if (partname != name && name != child->GetName()) {
5018 return child->operator[](name.substr(partname.length() + 1));
5019 }
5020 return child;
5021 }
5022 if (auto x = mainChild(); strcmp(child->GetName(), x.GetName()) == 0) {
5023 // can browse directly into main children as if their children were our children
5024 for (auto &child2 : x.browse()) {
5025 if (name == child2->GetName() || partname == child2->GetName()) {
5026 child2->browse(); // needed for onward read (or is it? there's a browse above too??)
5027 if (partname != name && name != child2->GetName()) {
5028 return child2->operator[](name.substr(partname.length() + 1));
5029 }
5030 return child2;
5031 }
5032 }
5033 }
5034 if (child->fFolder == (std::string("!") + partname)) {
5035 if (!folderNode)
5036 folderNode = std::make_shared<xRooNode>(child->fFolder.c_str(), nullptr, *this);
5037 folderNode->push_back(child);
5038 }
5039 }
5040 if (folderNode) {
5041 if (partname != name) {
5042 return folderNode->operator[](name.substr(partname.length() + 1));
5043 }
5044 return folderNode;
5045 }
5046 // before giving up see if partName is numeric and indexes within the range
5047 if (TString s(partname); s.IsDec() && size_t(s.Atoi()) < size()) {
5048 auto child2 = at(s.Atoi());
5049 if (partname != name) {
5050 return child2->operator[](name.substr(partname.length() + 1));
5051 }
5052 return child2;
5053 }
5054 auto out = std::make_shared<xRooNode>(partname.c_str(), nullptr, *this); // not adding as child yeeet
5055 // special case, if creating a node in the workspace with a specific name, it's a folder node ...
5056 if (get<RooWorkspace>() && partname == "pdfs") {
5057 out->SetName("!pdfs");
5058 }
5059 if (partname != name) {
5060 return out->operator[](name.substr(partname.length() + 1));
5061 }
5062 return out;
5063}
5064
5066{
5067 if (!b) {
5068 for (auto o : *gROOT->GetListOfBrowsers()) {
5069 b = dynamic_cast<TBrowser *>(o);
5070 if (!b || !b->GetBrowserImp())
5071 continue;
5072 if (auto out = GetTreeItem(b); out)
5073 return out;
5074 }
5075 return nullptr;
5076 }
5077 if (!b->GetBrowserImp())
5078 return nullptr;
5079 if (auto _b = dynamic_cast<TGFileBrowser *>(GETACTBROWSER(dynamic_cast<TRootBrowser *>(b->GetBrowserImp()))); _b) {
5080 auto _root = GETROOTDIR(_b);
5081 ;
5082 if (!_root)
5083 _root = GETLISTTREE(_b)->GetFirstItem();
5085 return GETLISTTREE(_b)->FindItemByObj(_root, const_cast<xRooNode *>(this));
5086 }
5087 return nullptr;
5088}
5089
5091{
5092 if (!b) {
5093 for (auto o : *gROOT->GetListOfBrowsers()) {
5094 b = dynamic_cast<TBrowser *>(o);
5095 if (!b || !b->GetBrowserImp())
5096 continue;
5097 if (auto out = GetListTree(b); out)
5098 return out;
5099 }
5100 return nullptr;
5101 }
5102 if (b->GetBrowserImp()) {
5103 if (auto _b = dynamic_cast<TGFileBrowser *>(GETACTBROWSER(dynamic_cast<TRootBrowser *>(b->GetBrowserImp())));
5104 _b) {
5105 auto _root = GETROOTDIR(_b);
5106 if (!_root)
5107 _root = GETLISTTREE(_b)->GetFirstItem();
5108 if (auto item = GETLISTTREE(_b)->FindItemByObj(_root, const_cast<xRooNode *>(this)); item) {
5109 return GETLISTTREE(_b);
5110 }
5111 }
5112 }
5113 return nullptr;
5114}
5115
5116void xRooNode::SetName(const char *name)
5117{
5119 if (auto a = get<RooAbsArg>(); a)
5120 a->setStringAttribute("alias", name);
5121 for (auto o : *gROOT->GetListOfBrowsers()) {
5122 if (auto b = dynamic_cast<TBrowser *>(o); b) {
5123 if (auto item = GetTreeItem(b); item) {
5124 item->SetText(name);
5125 }
5126 }
5127 }
5128}
5129
5130void xRooNode::SetTitle(const char *title)
5131{
5132 if (auto o = (get<TNamed>()); o) {
5133 if (auto c = mainChild(); c.get()) {
5134 c.SetTitle(title);
5135 }
5136 o->SetTitle(title);
5137 }
5138 TNamed::SetTitle(title);
5139}
5140
5142{
5143 if (get<RooArgList>() || (!get() && !(strlen(GetName()) > 0 && (GetName()[0] == '!')) && !fBrowseOperation))
5144 return *this; // nothing to browse - 'collection' nodes should already be populated except for folders
5145 // alternative could have been to mandate that the 'components' of a collection node are the children it has.
5146
5147 auto findByObj = [&](const std::shared_ptr<xRooNode> &n) {
5148 std::vector<std::shared_ptr<xRooNode>> &nn = *this;
5149 for (auto &c : nn) {
5150 if (c->get() == n->get() && strcmp(n->GetName(), c->GetName()) == 0)
5151 return c;
5152 }
5153 return std::shared_ptr<xRooNode>(nullptr);
5154 };
5155
5156 auto appendChildren = [&](const xRooNode &n) {
5157 size_t out = 0;
5158 const std::vector<std::shared_ptr<xRooNode>> &nn(n);
5159 for (auto &c : nn) {
5160 if (auto existing = findByObj(c); existing) {
5161 existing->fTimes++;
5162 existing->fFolder = c->fFolder; // transfer folder assignment
5163 } else {
5164 emplace_back(c);
5165 }
5166 if (!TString(c->GetName()).BeginsWith(".coef"))
5167 out++; // don't count .coef as a child, as technically part of parent
5168 }
5169 return out;
5170 };
5171
5172 const std::vector<std::shared_ptr<xRooNode>> &nn2(*this);
5173 for (auto &c : nn2) {
5174 if (strlen(c->GetName()) > 0 && (c->GetName()[0] == '.')) {
5175 c->fTimes = 1;
5176 continue;
5177 } // never auto-cleanup property children
5178 if (strcmp(c->GetName(), "!.pars") == 0) {
5179 c->fTimes = 1;
5180 continue;
5181 } // special collection, also not cleaned up
5182 if (c->get<RooWorkspace>() || c->get<TFile>()) {
5183 c->fTimes = 1;
5184 continue;
5185 } // workspaces and files not cleaned up: TODO have a nocleanup flag instead
5186 c->fTimes = 0;
5187 }
5188
5189 size_t addedChildren = 0;
5190 if (fBrowseOperation) {
5192 } else {
5193 if (get<RooWorkspace>()) {
5195 }
5196
5197 // if (get<RooAbsPdf>() && ((fParent && fParent->get<RooWorkspace>()) || !fParent)) {
5198 // // top-level pdfs will also list the ".vars" property for -- should make this updateable
5199 // //if (auto x = find("!.vars"); !x) { // this is slower because it triggers a browse of !.vars
5200 // if(!contains("!.vars")) {
5201 // emplace_back(std::make_shared<Node2>("!.vars",nullptr,*this));
5202 // } /*else {
5203 // x->fTimes++;
5204 // }*/
5205 // }
5206
5207 // go through components factors and variations, adding all as children if required
5209 if (!get<RooWorkspace>())
5211 // include coefs if any
5212 auto _coefs = coefs();
5213 if (_coefs.get() && strcmp(_coefs->GetName(), "1") != 0 && strcmp(_coefs->GetName(), "ONE") != 0) {
5214 if (_coefs.size() == 1 && _coefs.get<RooAddition>()) {
5215 if (strcmp(_coefs.at(0)->GetName(), "1") != 0 &&
5216 strcmp(_coefs.at(0)->GetName(), "ONE") != 0) { // don't add the "1"
5217 auto coef = std::make_shared<xRooNode>(".coef", *_coefs.at(0)->get(), *this);
5218 if (auto existing = findByObj(coef); existing) {
5219 existing->fTimes++;
5220 existing->fFolder = _coefs.at(0)->fFolder; // transfer folder assignment
5221 } else {
5222 emplace_back(coef);
5223 }
5224 }
5225 } else {
5226 if (auto existing = find(_coefs.GetName()); existing) {
5227 existing->fTimes++;
5228 existing->fFolder = _coefs.fFolder; // transfer folder assignment
5229 } else {
5230 emplace_back(std::make_shared<xRooNode>(_coefs));
5231 }
5232 }
5233 }
5237 if (get<RooAbsData>())
5239 }
5240 // if has no children and is a RooAbsArg, add all the proxies
5241 if (auto arg = get<RooAbsArg>(); arg && addedChildren == 0) {
5242 for (int i = 0; i < arg->numProxies(); i++) {
5243 auto _proxy = arg->getProxy(i);
5244 if (auto a = dynamic_cast<RooArgProxy *>(_proxy)) {
5245 auto c = std::make_shared<xRooNode>(TString::Format(".%s", _proxy->name()), *(a->absArg()), *this);
5246 if (auto existing = findByObj(c); existing) {
5247 existing->fTimes++;
5248 existing->fFolder = c->fFolder; // transfer folder assignment
5249 } else {
5250 // mark any existing children with the same name for cleanup - this happens e.g. if did a Replace on one
5251 // of these nodes note that the child nodes will still become reordered (the old node will be deleted,
5252 // new node will appear at end)
5253 for (auto &child : *this) {
5254 if (strcmp(child->GetName(), c->GetName()) == 0) {
5255 child->fTimes = 0;
5256 }
5257 }
5258 emplace_back(c);
5259 }
5260 } else if (auto s = dynamic_cast<RooAbsCollection *>(_proxy)) {
5261 for (auto a2 : *s) {
5262 auto c = std::make_shared<xRooNode>(*a2, *this);
5263 if (arg->numProxies() != 1) {
5264 c->fFolder = std::string("!.") +
5265 _proxy->name(); // don't put in a folder if there's just 1 proxy (the collection)
5266 }
5267 if (auto existing = findByObj(c); existing) {
5268 existing->fTimes++;
5269 existing->fFolder = c->fFolder; // transfer folder assignment
5270 } else {
5271 emplace_back(c);
5272 }
5273 }
5274 }
5275 }
5276 /*for(auto& s : arg->servers()) {
5277 auto c = std::make_shared<xRooNode>(*s,*this);
5278 if (auto existing = findByObj(c); existing) {
5279 existing->fTimes++;
5280 existing->fFolder = c->fFolder; // transfer folder assignment
5281 } else {
5282 emplace_back(c);
5283 }
5284 }*/
5285 } else if (auto ir = get<RooStats::HypoTestInverterResult>()) {
5286 // check if we already have a hypoSpace in our memory
5287 bool hasHS = false;
5288 for (auto &c : fBrowsables) {
5289 if (strcmp(c->GetName(), ".memory") == 0 && c->get<xRooHypoSpace>()) {
5290 hasHS = true;
5291 break;
5292 }
5293 }
5294 if (!hasHS) {
5295 // add the HS
5296 auto hs =
5297 fBrowsables.emplace_back(std::make_shared<xRooNode>(".memory", std::make_shared<xRooHypoSpace>(ir), *this));
5298 // add the hypoPoints first so they appear first
5299 auto _axes = hs->get<xRooHypoSpace>()->axes();
5300
5301 int i = 0;
5302 for (auto &hp : *hs->get<xRooHypoSpace>()) {
5304 for (auto a : _axes) {
5305 if (a != _axes.first())
5306 coordString += ",";
5307 coordString +=
5308 TString::Format("%s=%g", a->GetName(), hp.coords->getRealValue(a->GetName(), ir->GetXValue(i)));
5309 }
5310 auto hpn = emplace_back(std::make_shared<xRooNode>(coordString, hp.hypoTestResult, hs));
5311 hpn->fTimes++;
5312 hpn->fBrowsables.emplace_back(std::make_shared<xRooNode>(
5313 ".memory", std::shared_ptr<xRooNLLVar::xRooHypoPoint>(&hp, [](xRooNLLVar::xRooHypoPoint *) {}), hpn));
5314 i++;
5315 }
5316 } else {
5317 // ensure all hypoTestResults are flagged as keep-alive
5318 std::vector<std::shared_ptr<xRooNode>> &nn = *this;
5319 for (auto &c : nn) {
5320 if (c->get<RooStats::HypoTestResult>())
5321 c->fTimes++;
5322 }
5323 }
5324 // xRooNode tests;
5325 // for(int i=0;i<ir->ArraySize();i++) {
5326 // tests.push_back(std::make_shared<xRooNode>(TString::Format("%g",ir->GetXValue(i)),*ir->GetResult(i),*this));
5327 // }
5328 // appendChildren(tests);
5329 } else if (get<RooStats::HypoTestResult>()) {
5330
5331 // create the xRooHypoPoint if necessary
5332 xRooNLLVar::xRooHypoPoint *hp = nullptr;
5333 for (auto &c : fBrowsables) {
5334 if (strcmp(c->GetName(), ".memory") == 0 && c->get<xRooNLLVar::xRooHypoPoint>()) {
5335 hp = c->get<xRooNLLVar::xRooHypoPoint>();
5336 c->fTimes++; // keep it alive
5337 break;
5338 }
5339 }
5340 if (!hp) {
5341 auto shp =
5342 std::make_shared<xRooNLLVar::xRooHypoPoint>(std::dynamic_pointer_cast<RooStats::HypoTestResult>(fComp));
5343 fBrowsables.emplace_back(std::make_shared<xRooNode>(".memory", shp, *this));
5344 hp = shp.get();
5345 }
5346
5347 xRooNode fits;
5348
5349 if (auto fit = hp->ufit()) {
5350 fits.emplace_back(std::make_shared<xRooNode>(fit, *this))->TNamed::SetName("ufit");
5351 }
5352 if (auto fit = hp->cfit_null()) {
5353 fits.emplace_back(std::make_shared<xRooNode>(fit, *this))->TNamed::SetName("cfit_null");
5354 }
5355 if (auto fit = hp->cfit_alt()) {
5356 fits.emplace_back(std::make_shared<xRooNode>(fit, *this))->TNamed::SetName("cfit_alt");
5357 }
5358 if (auto fit = hp->gfit()) {
5359 fits.emplace_back(std::make_shared<xRooNode>(fit, *this))->TNamed::SetName("gfit");
5360 }
5361 if (auto asi = hp->asimov()) {
5362 auto asiP = fits.emplace_back(std::make_shared<xRooNode>(
5363 asi->hypoTestResult ? asi->hypoTestResult : std::make_shared<RooStats::HypoTestResult>(asi->result()),
5364 *this));
5365 asiP->TNamed::SetName("asimov");
5366 asiP->fBrowsables.emplace_back(std::make_shared<xRooNode>(".memory", asi, asiP));
5367 }
5369 }
5370
5371 // clear anything that has fTimes = 0 still
5372 auto it = std::vector<std::shared_ptr<xRooNode>>::begin();
5373 while (it != std::vector<std::shared_ptr<xRooNode>>::end()) {
5374 if (it->get()->fTimes == 0) {
5375 for (auto o : *gROOT->GetListOfBrowsers()) {
5376 auto b = dynamic_cast<TBrowser *>(o);
5377 if (b && b->GetBrowserImp()) { // browserImp is null if browser was closed
5378 // std::cout << GetPath() << " Removing " << it->get()->GetPath() << std::endl;
5379
5380 if (auto _b =
5381 dynamic_cast<TGFileBrowser *>(GETACTBROWSER(dynamic_cast<TRootBrowser *>(b->GetBrowserImp())));
5382 _b) {
5383 auto _root = GETROOTDIR(_b);
5384 if (!_root)
5385 _root = GETLISTTREE(_b)->GetFirstItem();
5386 if (auto item = GETLISTTREE(_b)->FindItemByObj(_root, this); item) {
5387 GETLISTTREE(_b)->OpenItem(item);
5388 }
5389 }
5390
5391 b->RecursiveRemove(
5392 it->get()); // problem: if obj is living in a collapsed node it wont actually get deleted
5393 /*auto _b = dynamic_cast<TGFileBrowser*>( dynamic_cast<TRootBrowser*>(b->GetBrowserImp())->fActBrowser );
5394 if (_b) {
5395 std::cout << _b->fRootDir->GetText() << std::endl;
5396 if (auto item = _b->fListTree->FindItemByObj(_b->fRootDir,it->get()); item) {
5397 std::cout << "Found obj: " << item << " " << item->GetText() << std::endl;
5398 _b->fListTree->RecursiveDeleteItem(_b->fRootDir,it->get());
5399 }
5400
5401 //b->RecursiveRemove(it->get());
5402 if (auto item = _b->fListTree->FindItemByObj(_b->fRootDir,it->get()); item) {
5403 std::cout << "Still Found obj: " << item << std::endl;
5404 }
5405 _b->fListTree->ClearViewPort();
5406
5407 }*/
5408 }
5409 }
5410 /*it->get()->ResetBit(TObject::kNotDeleted); ++it;*/ it = erase(it);
5411 } else {
5412 ++it;
5413 }
5414 }
5415
5416 return *this;
5417}
5418
5419////////////////////////////////////////////////////////////////////////////////
5420/// List of observables (global and regular) of this node.
5421
5423{
5424 xRooNode out(".obs", std::make_shared<RooArgList>(), *this);
5425 out.get<RooArgList>()->setName((GetPath() + ".obs").c_str());
5426 for (auto o : vars()) {
5427 if (o->get<RooAbsArg>()->getAttribute("obs")) {
5428 out.get<RooArgList>()->add(*o->get<RooAbsArg>());
5429 out.emplace_back(o);
5430 }
5431 }
5432 return out;
5433}
5434
5435////////////////////////////////////////////////////////////////////////////////
5436/// List of global observables of this node.
5437
5439{
5440 xRooNode out(".globs", std::make_shared<RooArgList>(), *this);
5441 out.get<RooArgList>()->setName((GetPath() + ".globs").c_str());
5442 for (auto o : obs()) {
5443 if (o->get<RooAbsArg>()->getAttribute("global")) {
5444 out.get<RooArgList>()->add(*o->get<RooAbsArg>());
5445 out.emplace_back(o);
5446 }
5447 }
5448 return out;
5449}
5450
5451////////////////////////////////////////////////////////////////////////////////
5452/// List of regular observables of this node.
5453
5455{
5456 xRooNode out(".robs", std::make_shared<RooArgList>(), *this);
5457 out.get<RooArgList>()->setName((GetPath() + ".robs").c_str());
5458 for (auto o : obs()) {
5459 if (!o->get<RooAbsArg>()->getAttribute("global")) {
5460 out.get<RooArgList>()->add(*o->get<RooAbsArg>());
5461 out.emplace_back(o);
5462 }
5463 }
5464 return out;
5465}
5466
5467////////////////////////////////////////////////////////////////////////////////
5468/// List of parameters (non-observables) of this node.
5469
5471{
5472 if (strcmp(GetName(), ".bins") == 0 && fParent) {
5473 // return pars of the parent - this method is used by covariances() if e.g. do node.bins().covariances()
5474 return fParent->pars();
5475 }
5476 xRooNode out(".pars", std::make_shared<RooArgList>(), *this);
5477 out.get<RooArgList>()->setName((GetPath() + ".pars").c_str());
5478 for (auto o : vars()) {
5479 if (!o->get<RooAbsArg>()->getAttribute("obs")) {
5480 out.get<RooArgList>()->add(*(o->get<RooAbsArg>()));
5481 out.emplace_back(o);
5482 }
5483 }
5484 return out;
5485}
5486
5487////////////////////////////////////////////////////////////////////////////////
5488/// List of parameters that are currently constant
5489
5491{
5492 xRooNode out(".consts", std::make_shared<RooArgList>(), *this);
5493 out.get<RooArgList>()->setName((GetPath() + ".consts").c_str());
5494 for (auto o : pars()) {
5495 if (o->get<RooAbsArg>()->getAttribute("Constant") || o->get<RooConstVar>()) {
5496 out.get<RooArgList>()->add(*o->get<RooAbsArg>());
5497 out.emplace_back(o);
5498 }
5499 }
5500 return out;
5501}
5502
5503////////////////////////////////////////////////////////////////////////////////
5504/// List of parameters that are currently non-constant
5505/// These parameters do not have the "Constant" attribute
5506
5508{
5509 xRooNode out(".floats", std::make_shared<RooArgList>(), *this);
5510 out.get<RooArgList>()->setName((GetPath() + ".floats").c_str());
5511 for (auto o : pars()) {
5512 if (!o->get<RooAbsArg>()->getAttribute("Constant") && !o->get<RooConstVar>()) {
5513 out.get<RooArgList>()->add(*o->get<RooAbsArg>());
5514 out.emplace_back(o);
5515 }
5516 }
5517 return out;
5518}
5519
5520////////////////////////////////////////////////////////////////////////////////
5521/// List of parameters of interest: parameters marked as "of interest"
5522/// These parameters have the "poi" attribute
5523
5525{
5526 xRooNode out(".poi", std::make_shared<RooArgList>(), *this);
5527 out.get<RooArgList>()->setName((GetPath() + ".poi").c_str());
5528 for (auto o : pars()) {
5529 if (o->get<RooAbsArg>()->getAttribute("poi")) {
5530 out.get<RooArgList>()->add(*o->get<RooAbsArg>());
5531 out.emplace_back(o);
5532 }
5533 }
5534 return out;
5535}
5536
5537////////////////////////////////////////////////////////////////////////////////
5538/// List of nuisance parameters: non-constant parameters that are not marked of interest,
5539/// as well as any parameters that have been marked by the "np" attribute
5540
5542{
5543 xRooNode out(".np", std::make_shared<RooArgList>(), *this);
5544 out.get<RooArgList>()->setName((GetPath() + ".np").c_str());
5545 for (auto o : pars()) {
5546 if (o->get<RooAbsArg>()->getAttribute("np") ||
5547 (!o->get<RooAbsArg>()->getAttribute("Constant") && !o->get<RooAbsArg>()->getAttribute("poi") &&
5548 !o->get<RooConstVar>())) {
5549 out.get<RooArgList>()->add(*o->get<RooAbsArg>());
5550 out.emplace_back(o);
5551 }
5552 }
5553 return out;
5554}
5555
5556////////////////////////////////////////////////////////////////////////////////
5557/// List of prespecified parameters: non-floatable parameters
5558
5560{
5561 xRooNode out(".pp", std::make_shared<RooArgList>(), *this);
5562 out.get<RooArgList>()->setName((GetPath() + ".pp").c_str());
5563 for (auto o : pars()) {
5564 if (!o->get<RooAbsArg>()->getAttribute("np") && !o->get<RooAbsArg>()->getAttribute("poi") &&
5565 (o->get<RooAbsArg>()->getAttribute("Constant") || o->get<RooConstVar>())) {
5566 out.get<RooArgList>()->add(*o->get<RooAbsArg>());
5567 out.emplace_back(o);
5568 }
5569 }
5570 return out;
5571}
5572
5573////////////////////////////////////////////////////////////////////////////////
5574/// List of variables (observables and parameters) of this node
5575
5577{
5578 xRooNode out(".vars", std::make_shared<RooArgList>(), *this);
5579 out.get<RooArgList>()->setName((GetPath() + ".vars").c_str());
5580 if (auto coll = get<RooAbsCollection>(); coll) {
5581 for (auto &x : *this) {
5582 for (auto &y : x->vars()) {
5583 out.push_back(y);
5584 }
5585 }
5586 return out;
5587 }
5588 if (auto p = get<RooAbsArg>(); p) {
5589 // also need to get all constPars so use leafNodeServerList .. will include self if is fundamental, which is what
5590 // we want
5591 // ensure all globs appear after robs, as we rely on this ordering for picking "x" var in "reduced" method
5594 p->leafNodeServerList(&allLeaves);
5595 for (auto &c : allLeaves) {
5596 if (c->isFundamental() || (dynamic_cast<RooConstVar *>(c) && !TString(c->GetName()).IsFloat())) {
5597 if (!c->getAttribute("global")) {
5598 out.get<RooArgList>()->add(*c);
5599 out.emplace_back(std::make_shared<xRooNode>(*c, *this));
5600 }
5601 if (c->getAttribute("global")) {
5602 _globs.emplace_back(std::make_shared<xRooNode>(*c, *this));
5603 _globs.back()->fFolder = "!globs";
5604 } else if (c->getAttribute("obs")) {
5605 out.back()->fFolder = "!robs";
5606 } else if (c->getAttribute("poi")) {
5607 out.back()->fFolder = "!poi";
5608 } else if (c->getAttribute("np") ||
5609 (!c->getAttribute("Constant") && !c->getAttribute("poi") && c->IsA() != RooConstVar::Class())) {
5610 out.back()->fFolder = "!np";
5611 } else if (!c->getAttribute("Constant") && c->IsA() != RooConstVar::Class()) {
5612 out.back()->fFolder = "!floats";
5613 } else {
5614 out.back()->fFolder = "!pp";
5615 }
5616 }
5617 }
5618 for (auto g : _globs) {
5619 out.get<RooArgList>()->add(*g->get<RooAbsArg>());
5620 out.emplace_back(g);
5621 }
5622 } else if (auto p2 = get<RooAbsData>(); p2) {
5623 for (auto a : *p2->get()) {
5624 a->setAttribute("obs");
5625 out.emplace_back(std::make_shared<xRooNode>(*a, *this));
5626 out.get<RooArgList>()->add(*a);
5627 }
5628 if (auto _dglobs = p2->getGlobalObservables()) {
5629 for (auto &a : *_dglobs) {
5630 a->setAttribute("obs");
5631 a->setAttribute("global");
5632 out.emplace_back(std::make_shared<xRooNode>(*a, *this));
5633 out.get<RooArgList>()->add(*a);
5634 }
5635 } else if (auto _globs = find(".globs"); _globs && _globs->get<RooAbsCollection>()) {
5636 for (auto &a : *_globs->get<RooAbsCollection>()) {
5637 a->setAttribute("obs");
5638 a->setAttribute("global");
5639 out.emplace_back(std::make_shared<xRooNode>(*a, *this));
5640 out.get<RooArgList>()->add(*a);
5641 }
5642 } else if (auto _ws = ws(); _ws) {
5643 if (auto _globs2 = dynamic_cast<RooArgSet *>(GETWSSNAPSHOTS(_ws).find(p2->GetName())); _globs2) {
5644 for (auto a : *_globs2) {
5645 a->setAttribute("obs");
5646 a->setAttribute("global");
5647 out.emplace_back(std::make_shared<xRooNode>(*a, *this));
5648 out.get<RooArgList>()->add(*a);
5649 }
5650 } else if (auto _gl = GETWSSETS(_ws).find("globalObservables"); _gl != GETWSSETS(_ws).end()) {
5651 for (auto &_g : _gl->second) {
5652 auto _clone = std::shared_ptr<RooAbsArg>(dynamic_cast<RooAbsArg *>(_g->Clone(_g->GetName())));
5653 if (auto v = std::dynamic_pointer_cast<RooAbsRealLValue>(_clone); v && _g->getStringAttribute("nominal"))
5654 v->setVal(TString(_g->getStringAttribute("nominal")).Atof());
5655 out.emplace_back(std::make_shared<xRooNode>(_clone, *this));
5656 out.get<RooArgList>()->add(*_clone);
5657 }
5658 } else if (fParent) {
5659 // note: this is slow in large workspaces ... too many obs to look through?
5660 std::unique_ptr<RooAbsCollection> _globs3(fParent->obs().get<RooArgList>()->selectByAttrib("global", true));
5661 // std::unique_ptr<RooAbsCollection> _globs(_ws->allVars().selectByAttrib("global",true)); - tried this to
5662 // be quicker but it wasn't
5663 for (auto &_g : *_globs3) {
5664 auto _clone = std::shared_ptr<RooAbsArg>(dynamic_cast<RooAbsArg *>(_g->Clone(_g->GetName())));
5665 if (auto v = std::dynamic_pointer_cast<RooAbsRealLValue>(_clone); v && _g->getStringAttribute("nominal"))
5666 v->setVal(TString(_g->getStringAttribute("nominal")).Atof());
5667 out.emplace_back(std::make_shared<xRooNode>(_clone, *this));
5668 out.get<RooArgList>()->add(*_clone);
5669 }
5670 }
5671 }
5672 } else if (auto w = get<RooWorkspace>(); w) {
5673 for (auto a : w->allVars()) {
5674 out.emplace_back(std::make_shared<xRooNode>(*a, *this));
5675 out.get<RooArgList>()->add(*a);
5676 }
5677 // add all cats as well
5678 for (auto a : w->allCats()) {
5679 out.emplace_back(std::make_shared<xRooNode>(*a, *this));
5680 out.get<RooArgList>()->add(*a);
5681 }
5682 }
5683 return out;
5684}
5685
5687{
5688 xRooNode out(".components", nullptr, *this);
5689
5690 if (auto p = get<RooAddPdf>(); p) {
5691 // only add each pdf once (the coefs will be accumulated in coefs() method) ...
5692 std::set<RooAbsArg *> donePdfs;
5693 for (auto &o : p->pdfList()) {
5694 if (donePdfs.count(o))
5695 continue;
5696 out.emplace_back(std::make_shared<xRooNode>(*o, *this));
5697 donePdfs.insert(o);
5698 }
5699 } else if (auto p2 = get<RooRealSumPdf>(); p2) {
5700 // check for common prefixes and suffixes, will use to define aliases to shorten names
5701 // if have more than 1 function
5702 // TString commonPrefix=""; TString commonSuffix="";
5703 // if (p->funcList().size() > 1) {
5704 // bool checked=false;
5705 // for(auto& o : p->funcList()) {
5706 // if (!checked) {
5707 // commonPrefix = o->GetName(); commonSuffix = o->GetName(); checked=true;
5708 // } else {
5709 //
5710 // }
5711 // }
5712 // }
5713 std::set<RooAbsArg *> doneFuncs;
5714 for (auto &o : p2->funcList()) {
5715 if (doneFuncs.count(o))
5716 continue;
5717 out.emplace_back(std::make_shared<xRooNode>(*o, *this));
5718 doneFuncs.insert(o);
5719 }
5720 } else if (auto p3 = get<RooAddition>(); p3) {
5721 for (auto &o : p3->list()) {
5722 out.emplace_back(std::make_shared<xRooNode>(*o, *this));
5723 }
5724 } else if (auto p4 = get<RooAbsCollection>(); p4) {
5725 for (auto &a : *p4) {
5726 out.emplace_back(std::make_shared<xRooNode>(*a, *this));
5727 }
5728 } else if (auto p5 = get<RooWorkspace>(); p5) {
5729 for (auto &o : p5->components()) {
5730 // only top-level nodes (only clients are integrals or things that aren't part of the workspace)
5731 // if (o->hasClients()) continue;
5732 bool hasClients = false;
5733 for (auto &c : o->clients()) {
5734 if (!c->InheritsFrom("RooRealIntegral") && p5 == GETWS(c)) {
5735 hasClients = true;
5736 break;
5737 }
5738 }
5739 if (hasClients)
5740 continue;
5741 out.emplace_back(std::make_shared<xRooNode>(*o, *this));
5742 if (o->InheritsFrom("RooAbsPdf")) {
5743 out.back()->fFolder = "!pdfs";
5744 } else {
5745 out.back()->fFolder = "!scratch";
5746 }
5747 }
5748 for (auto &o : p5->allGenericObjects()) {
5749 if (auto fr = dynamic_cast<RooFitResult *>(o); fr) {
5750 TString s(fr->GetTitle());
5751 if (s.Contains(';'))
5752 s = s(0, s.Index(';'));
5753 if (auto _pdf = out.find(s.Data()); _pdf) {
5754 // std::cout << " type = " << _pdf->get()->ClassName() << std::endl;
5755 out.emplace_back(std::make_shared<xRooNode>(fr->GetName(), *fr, _pdf));
5756 // for a while, this node's parent pointed to something of type Node2!!
5757 // how to fix??? - I fxied it with a new constructor to avoid the shared_ptr<Node2> calling the const
5758 // Node2& constructor via getting wrapped in a Node2(shared_ptr<TObject>) call
5759 // out.back()->fParent = _pdf;
5760 // std::cout << " type2 = " << out.back()->fParent->get()->ClassName() << std::endl;
5761 } else {
5762 out.emplace_back(std::make_shared<xRooNode>(fr->GetName(), *fr, *this));
5763 }
5764 out.back()->fFolder = "!fits";
5765 } else {
5766 out.emplace_back(std::make_shared<xRooNode>(*o, *this));
5767 if (strcmp(out.back()->get()->ClassName(), "TStyle") == 0) {
5768 out.back()->fFolder = "!styles";
5769 } else if (strcmp(out.back()->get()->ClassName(), "RooStats::HypoTestInverterResult") == 0) {
5770 out.back()->fFolder = "!scans";
5771 } else if (strcmp(out.back()->get()->ClassName(), "RooStats::ModelConfig") == 0) {
5772 out.back()->fFolder = "!models";
5773 } else {
5774 out.back()->fFolder = "!objects";
5775 }
5776 }
5777 }
5778 for (auto &[k, v] : GETWSSETS(p5)) {
5779 // skip 'CACHE' sets because they are auto-removed when sanitizing workspaces, which will invalidate these
5780 // children
5781 if (k.find("CACHE_") == 0)
5782 continue;
5783 out.emplace_back(std::make_shared<xRooNode>(k.c_str(), v, *this));
5784 out.back()->fFolder = "!sets";
5785 }
5786
5788 std::unique_ptr<TIterator> iter(snaps.MakeIterator());
5789 TObject *snap;
5790 while ((snap = iter->Next())) {
5791 out.emplace_back(std::make_shared<xRooNode>(*snap, *this));
5792 out.back()->fFolder = "!snapshots";
5793 }
5794 } else if (auto mc = get<RooStats::ModelConfig>()) {
5795 // add the pdf as a child, and the external constraints set if its there
5796 if (mc->GetPdf()) {
5797 out.emplace_back(std::make_shared<xRooNode>(".pdf", *mc->GetPdf(), *this));
5798 }
5799 if (mc->GetExternalConstraints()) {
5800 out.emplace_back(std::make_shared<xRooNode>(".extCons", *mc->GetExternalConstraints(), *this));
5801 }
5802 } else if (strlen(GetName()) > 0 && GetName()[0] == '!' && fParent) {
5803 // special case of dynamic property
5804 if (TString(GetName()) == "!.pars") {
5805 for (auto &c : fParent->pars()) {
5806 out.emplace_back(c);
5807 }
5808 } else {
5809 // the components of a folder are the children of the parent (after browsing) that live in this folder
5810 fParent->browse();
5811 for (auto &c : *fParent) {
5812 if (c->fFolder == GetName()) {
5813 out.emplace_back(c);
5814 }
5815 }
5816 }
5817 }
5818
5819 return out;
5820}
5821
5822////////////////////////////////////////////////////////////////////////////////
5823/// bins of a channel or sample, or channels of a multi-channel pdf
5824
5826{
5827 xRooNode out(".bins", nullptr, *this);
5828
5829 if (auto p = get<RooSimultaneous>(); p) {
5830 std::map<int, std::shared_ptr<xRooNode>> cats; // fill into a map to preserve index ordering
5831 for (auto &c : p->indexCat()) { // is alphabetical in labels
5832 auto pp = p->getPdf(c.first.c_str());
5833 if (!pp)
5834 continue;
5835 cats[c.second] =
5836 std::make_shared<xRooNode>(TString::Format("%s=%s", p->indexCat().GetName(), c.first.c_str()), *pp, *this);
5837 }
5838 for (auto &[_, n] : cats)
5839 out.emplace_back(n);
5840 } else if (auto phf = get<ParamHistFunc>(); phf) {
5841 int i = 1;
5842#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
5843 auto &pSet = phf->_paramSet;
5844#else
5845 auto &pSet = phf->paramList();
5846#endif
5847 for (auto par : pSet) {
5848 out.emplace_back(std::make_shared<xRooNode>(*par, *this));
5849 out.back()->fBinNumber = i;
5850 i++;
5851 }
5852 } else if (auto ax = GetXaxis(); ax) {
5853 for (int i = 1; i <= ax->GetNbins(); i++) {
5854 // create a RooProduct of all bin-specific factors of all shapeFactors
5855 std::vector<RooAbsArg *> _factors;
5856 for (auto f : factors()) {
5857 if (f->get<ParamHistFunc>()) {
5858 if (f->bins()[i - 1]->get<RooProduct>()) {
5859 for (auto &ss : f->bins()[i - 1]->factors())
5860 _factors.push_back(ss->get<RooAbsArg>());
5861 } else {
5862 _factors.push_back(f->bins()[i - 1]->get<RooAbsArg>());
5863 }
5864 }
5865 }
5866 out.emplace_back(std::make_shared<xRooNode>(
5867 TString::Format("%g<=%s<%g", ax->GetBinLowEdge(i), ax->GetParent()->GetName(), ax->GetBinLowEdge(i + 1)),
5868 _factors.empty() ? nullptr
5869 : std::make_shared<RooProduct>(TString::Format("%s.binFactors.bin%d", GetName(), i),
5870 "binFactors", RooArgList()),
5871 *this));
5872 for (auto f : _factors) {
5873#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
5874 out.back()->get<RooProduct>()->_compRSet.add(*f);
5875#else
5876 const_cast<RooArgList &>(out.back()->get<RooProduct>()->realComponents()).add(*f);
5877#endif
5878 }
5879 out.back()->fBinNumber = i;
5880 }
5881 }
5882
5883 return out;
5884}
5885
5887{
5889
5890 if (recurse && fParent) {
5891 // get our coefs and multiply it by the parents coefs ...
5892 auto ourCoefs = xRooNode::coefs(false);
5893 auto parentCoefs = fParent->coefs(true);
5894 if (!parentCoefs.get<RooAbsReal>()) {
5895 // no coefs to include, just return our coefs
5896 return ourCoefs;
5897 }
5898 if (!ourCoefs.get<RooAbsReal>()) {
5899 // just return the parent's coefs
5900 return parentCoefs;
5901 }
5902 // if got here, must combine parentCoefs and outCoefs into a RooProduct
5903 xRooNode out(".recursiveCoefs",
5904 std::make_shared<RooProduct>(".recursiveCoefs",
5905 TString::Format("Recursive Coefficients of %s", GetName()),
5906 *ourCoefs.get<RooAbsReal>(), *parentCoefs.get<RooAbsReal>()),
5907 *this);
5908 // keep alive the two coef nodes by adding to out's memory
5909 auto mem = out.emplace_back(std::make_shared<xRooNode>(".memory", nullptr, *this));
5910 mem->emplace_back(std::make_shared<xRooNode>(ourCoefs));
5911 mem->emplace_back(std::make_shared<xRooNode>(parentCoefs));
5912 return out;
5913 }
5914
5915 bool isResidual = false;
5916
5917 // if parent is a sumpdf or addpdf then include the coefs
5918 // if func appears multiple times then coefs must be combined into a RooAddition temporary
5919 if (fParent) {
5920 // handle case where filters are applied .. need to pass through these
5921 // do this by iterating while fComp is null
5922 auto parent = fParent;
5923 if (!parent->fComp) {
5924 while (!parent->fComp && parent->fParent) {
5925 parent = parent->fParent;
5926 }
5927 // parent should now be node above the filters ... need parent of that
5928 parent = parent->fParent;
5929 if (!parent)
5930 parent = fParent; // revert t original parent in case something went wrong
5931 }
5932 if (auto p = parent->get<RooRealSumPdf>(); p) {
5933 std::size_t i = 0;
5934 for (auto &o : p->funcList()) {
5935 if (o == get()) {
5936 if (i >= p->coefList().size()) {
5937 isResidual = true;
5938 coefs.add(p->coefList());
5939 } else {
5940 coefs.add(*p->coefList().at(i));
5941 }
5942 }
5943 i++;
5944 }
5945 } else if (auto p2 = parent->get<RooAddPdf>(); p2) {
5946 std::size_t i = 0;
5947 if (p2->coefList().empty()) {
5948 // this can happen if all pdfs are extended then the coef is effectively the
5949 // expected number of events
5950 // TODO: test behaviour of xRooNode under this scenario (are histograms correct?)
5951 } else {
5952 for (auto &o : p2->pdfList()) {
5953 if (o == get()) {
5954 if (i >= p2->coefList().size()) {
5955 isResidual = true;
5956 coefs.add(p2->coefList());
5957 } else {
5958 coefs.add(*p2->coefList().at(i));
5959 }
5960 }
5961 i++;
5962 }
5963 }
5964 }
5965 }
5966 if (isResidual) {
5967 // return a node representing 1.-sumOfCoefs
5968 // involves creating sumOfCoefs unless there is only 1 coef, then just use that
5969 auto coefSum = coefs.empty()
5970 ? nullptr
5971 : (coefs.size() == 1 ? std::shared_ptr<RooAbsArg>(coefs.at(0), [](RooAbsArg *) {})
5972 : std::make_shared<RooAddition>((isResidual) ? ".sumOfCoefs" : ".coefs",
5973 "Coefficients of", coefs));
5974 xRooNode out(".coef", coefSum ? std::dynamic_pointer_cast<RooAbsArg>(std::make_shared<RooFormulaVar>(
5975 ".coef", "1-sum(otherCoefs)", "1. - @0", *coefSum))
5976 : nullptr /* should we return a "1." instead? */);
5977 if (coefSum && coefs.size() != 1) {
5978 out.emplace_back(std::make_shared<xRooNode>(".memory", nullptr, *this))
5979 ->emplace_back(
5980 std::make_shared<xRooNode>(".sumOfCoefs", coefSum, out)); // added to keep the sum alive! with the node
5981 }
5982 if (!coefs.empty()) {
5983 out.browse();
5984 }
5985 return out;
5986 } else if (coefs.size() == 1) {
5987 xRooNode out(".coef", std::shared_ptr<RooAbsArg>(coefs.at(0), [](RooAbsArg *) {}), *this);
5988 if (!coefs.empty()) {
5989 out.browse();
5990 }
5991 return out;
5992 } else {
5993 auto coefSum =
5994 coefs.empty()
5995 ? nullptr
5996 : std::make_shared<RooAddition>(".coefs", TString::Format("Coefficients of %s", GetName()), coefs);
5997 xRooNode out(".coefs", coefSum, *this);
5998 if (!coefs.empty())
5999 out.browse();
6000
6001 return out;
6002 }
6003}
6004
6006{
6007 xRooNode out(".factors", nullptr, *this);
6008
6009 if (auto p = get<RooProdPdf>(); p) {
6010 auto _main = mainChild();
6011 if (auto a = _main.get<RooRealSumPdf>(); a && !a->getStringAttribute("alias")) {
6012 a->setStringAttribute("alias", "samples");
6013 } else if (auto a2 = _main.get<RooAddPdf>(); a2 && !a2->getStringAttribute("alias")) {
6014 a2->setStringAttribute("alias", "components");
6015 }
6016 int _npdfs = p->pdfList().size();
6017 for (auto &o : p->pdfList()) {
6018 out.emplace_back(std::make_shared<xRooNode>(*o, *this));
6019 if (_npdfs > 5 && o != _main.get())
6020 out.back()->fFolder = "!constraints";
6021 }
6022 } else if (auto p2 = get<RooProduct>(); p2) {
6023 for (auto &o : p2->components()) {
6024 if (o->InheritsFrom("RooProduct")) {
6025 // get factors of this term
6026 auto x = xRooNode("tmp", *o, *this).factors();
6027 for (auto &n : x) {
6028 out.emplace_back(std::make_shared<xRooNode>(n->GetName(), n->fComp, *this));
6029 }
6030 } else {
6031 out.emplace_back(std::make_shared<xRooNode>(*o, *this));
6032 }
6033 }
6034 } else if (auto w = get<RooWorkspace>(); w) {
6035 // if workspace, return all functions (not pdfs) that have a RooProduct as one of their clients
6036 // or not clients
6037 // exclude obs and globs
6038 auto oo = obs(); // need to keep alive as may contain owning globs
6039 auto &_obs = *(oo.get<RooArgList>());
6040 for (auto a : w->allFunctions()) {
6041 if (_obs.contains(*a))
6042 continue;
6043 bool show(true);
6044 for (auto c : a->clients()) {
6045 show = false;
6046 if (c->InheritsFrom("RooProduct")) {
6047 show = true;
6048 break;
6049 }
6050 }
6051 if (show)
6052 out.emplace_back(std::make_shared<xRooNode>(*a, *this));
6053 }
6054 }
6055
6056 /*
6057 // if parent is a sumpdf or addpdf then include the coefs
6058 // if func appears multiple times then coefs must be combined into a RooAddition temporary
6059 if (fParent) {
6060 RooArgList coefs;
6061 if(auto p = fParent->get<RooRealSumPdf>();p) {
6062 int i=0;
6063 for(auto& o : p->funcList()) {
6064 if (o == get()) {
6065 coefs.add( *p->coefList().at(i) );
6066 }
6067 i++;
6068 }
6069 } else if(auto p = fParent->get<RooAddPdf>(); p) {
6070 int i=0;
6071 for(auto& o : p->pdfList()) {
6072 if (o == get()) {
6073 coefs.add( *p->coefList().at(i) );
6074 }
6075 i++;
6076 }
6077 }
6078 if (!coefs.empty()) {
6079 if (coefs.size() == 1) {
6080 if (strcmp(coefs.at(0)->GetName(),"1")) { // don't add the "1"
6081 out.emplace_back(std::make_shared<Node2>(".coef", *coefs.at(0), *this));
6082 }
6083 } else {
6084 out.emplace_back(std::make_shared<Node2>(".coefs",
6085 std::make_shared<RooAddition>(".coefs", "Coefficients of",
6086 coefs), *this));
6087 }
6088 }
6089 }
6090 */
6091 return out;
6092}
6093
6095{
6096 xRooNode out(".variations", nullptr, *this);
6097
6098 // if (auto p = get<RooSimultaneous>(); p) {
6099 // for (auto &c : p->indexCat()) {
6100 // auto pp = p->getPdf(c.first.c_str());
6101 // if (!pp)
6102 // continue;
6103 // out.emplace_back(
6104 // std::make_shared<xRooNode>(TString::Format("%s=%s", p->indexCat().GetName(), c.first.c_str()), *pp,
6105 // *this));
6106 // }
6107 // } else
6108 if (auto p2 = get<PiecewiseInterpolation>(); p2) {
6109#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
6110 out.emplace_back(std::make_shared<xRooNode>("nominal", p2->_nominal.arg(), *this));
6111#else
6112 out.emplace_back(std::make_shared<xRooNode>("nominal", *(p2->nominalHist()), *this));
6113#endif
6114 for (size_t i = 0; i < p2->paramList().size(); i++) {
6115 // TODO: should we only return one if we find they are symmetrized?
6116 out.emplace_back(std::make_shared<xRooNode>(TString::Format("%s=1", p2->paramList().at(i)->GetName()),
6117 *p2->highList().at(i), *this));
6118 out.emplace_back(std::make_shared<xRooNode>(TString::Format("%s=-1", p2->paramList().at(i)->GetName()),
6119 *p2->lowList().at(i), *this));
6120 }
6122#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
6123 out.emplace_back(std::make_shared<xRooNode>("nominal", RooFit::RooConst(p3->_nominal), *this));
6124 for (size_t i = 0; i < p3->_paramList.size(); i++) {
6125 out.emplace_back(std::make_shared<xRooNode>(TString::Format("%s=1", p3->_paramList.at(i)->GetName()),
6126 RooFit::RooConst(p3->_high.at(i)), *this));
6127 out.emplace_back(std::make_shared<xRooNode>(TString::Format("%s=-1", p3->_paramList.at(i)->GetName()),
6128 RooFit::RooConst(p3->_low.at(i)), *this));
6129 }
6130#else
6131 out.emplace_back(std::make_shared<xRooNode>("nominal", RooFit::RooConst(p3->nominal()), *this));
6132 for (size_t i = 0; i < p3->variables().size(); i++) {
6133 out.emplace_back(std::make_shared<xRooNode>(TString::Format("%s=1", p3->variables().at(i)->GetName()),
6134 RooFit::RooConst(p3->high().at(i)), *this));
6135 out.emplace_back(std::make_shared<xRooNode>(TString::Format("%s=-1", p3->variables().at(i)->GetName()),
6136 RooFit::RooConst(p3->low().at(i)), *this));
6137 }
6138#endif
6139
6140 } else if (auto p4 = get<ParamHistFunc>(); p4) {
6141 // I *think* I put this here so that can browse into a ParamHistFunc
6142 // int i = 0;
6143 // for (auto par : p4->_paramSet) {
6144 // TString _name = par->GetName();
6145 // // if(auto _v = dynamic_cast<RooRealVar*>(p->_dataSet.get(i)->first()); _v) {
6146 // // _name = TString::Format("%s=%g",_v->GetName(),_v->getVal());
6147 // // }
6148 // // out.emplace_back(std::make_shared<xRooNode>(_name,*par,*this)); -- -removed cos now have bin()
6149 // method i++;
6150 // }
6151 }
6152 return out;
6153}
6154
6156{
6157 RooArgList out;
6158 out.setName(GetName());
6159 for (auto &k : *this) {
6160 if (auto o = k->get<RooAbsArg>(); o)
6161 out.add(*o);
6162 }
6163 return out;
6164}
6165
6167{
6168 xRooNode out(".datasets()", nullptr, *this);
6169 // removed the browse operation since no longer showing '.datasets()' in browser
6170 // and otherwise this means dataset reduction operation will be called every time we 'browse()' the datasets node
6171 // out.fBrowseOperation = [](xRooNode *f) { return f->fParent->datasets(); };
6172
6173 if (auto _ws = get<RooWorkspace>(); _ws) {
6174 for (auto &d : _ws->allData()) {
6175 out.emplace_back(std::make_shared<xRooNode>(*d, *this));
6176 out.back()->fFolder = "!datasets";
6177 }
6178 } else if (get<RooAbsPdf>() ||
6179 (!get() && fParent &&
6180 fParent->get<RooAbsPdf>())) { // second condition handles 'bins' nodes of pdf, which have null ptr
6181 // only add datasets that have observables that cover all our observables
6182 auto oo = obs(); // must keep alive in case is owning the globs
6183 RooArgSet _obs(*oo.get<RooArgList>());
6184 //_obs.add(coords(true).argList(), true); // include coord observables too, and current xaxis if there's one -
6185 // added in loop below
6186
6187 TString cut;
6189 for (auto _c : coords()) { // coords() moves vars to their respective coordinates too
6190 if (auto _cat = _c->get<RooAbsCategoryLValue>(); _cat) {
6191 if (cut != "")
6192 cut += " && ";
6193 cut += TString::Format("%s==%d", _cat->GetName(), _cat->getCurrentIndex());
6194 _obs.add(*_cat,
6195 true); // note: if we ever changed coords to return clones, would need to keep coords alive
6196 cutobs.add(*_cat);
6197 } else if (auto _rv = _c->get<RooAbsRealLValue>(); _rv) {
6198 // todo: check coordRange is a single range rather than multirange
6199 if (cut != "")
6200 cut += " && ";
6201 cut += TString::Format("%s>=%f&&%s<%f", _rv->GetName(), _rv->getMin(_rv->getStringAttribute("coordRange")),
6202 _rv->GetName(), _rv->getMax(_rv->getStringAttribute("coordRange")));
6203 _obs.add(*_rv,
6204 true); // note: if we ever changed coords to return clones, would need to keep coords alive
6205 cutobs.add(*_rv);
6206 } else {
6207 throw std::runtime_error("datasets(): Unsupported coordinate type");
6208 }
6209 }
6210 if (auto s = get<RooSimultaneous>()) {
6211 // check if we have a pdf for every category ... if not then add to cut
6212 bool hasMissing = false;
6213 TString extraCut = "";
6214 for (auto cat : s->indexCat()) {
6215 if (!s->getPdf(cat.first.c_str())) {
6216 hasMissing = true;
6217 } else {
6218 if (extraCut != "")
6219 extraCut += " || ";
6220 extraCut += TString::Format("%s==%d", s->indexCat().GetName(), cat.second);
6221 }
6222 }
6223 if (hasMissing) {
6224 if (cut != "")
6225 cut += " && ";
6226 cut += "(" + extraCut + ")";
6227 cutobs.add(s->indexCat());
6228 }
6229 }
6230
6231 if (auto ax = GetXaxis(); ax && dynamic_cast<RooAbsArg *>(ax->GetParent())->getAttribute("obs")) {
6232 auto a = dynamic_cast<RooAbsArg *>(ax->GetParent());
6233 _obs.add(*a, true);
6234 }
6235 xRooNode _datasets; // will be any child datasets, along with datasets of the workspace
6236 for (auto &child : *this) {
6237 if (child->get<RooAbsData>())
6238 _datasets.push_back(child);
6239 }
6240 if (auto __ws = ws(); __ws) {
6241 xRooNode _wsNode(*__ws, *this);
6242 for (auto &d : _wsNode.datasets()) {
6243 _datasets.push_back(d);
6244 }
6245 }
6246
6247 for (auto &d : _datasets) {
6248 if (std::unique_ptr<RooAbsCollection>(d->obs().argList().selectCommon(_obs))->size() == _obs.size()) {
6249 // all obs present .. include
6250
6251 if (cut != "") {
6252 RooFormulaVar cutFormula("cut1", cut, cutobs); // doing this to avoid complaints about unused vars
6253 // TODO: Could consider using a 'filter' node (see filter() method) applied to the dataset instead
6254 // of creating and using a reduced dataset here
6255 out.emplace_back(std::make_shared<xRooNode>(
6256 std::shared_ptr<RooAbsData>(d->get<RooAbsData>()->reduce(
6257 *std::unique_ptr<RooAbsCollection>(d->robs().get<RooArgList>()->selectCommon(_obs)), cutFormula)),
6258 *this));
6259 // put a subset of the globs in the returned dataset too
6260 out.back()->get<RooAbsData>()->setGlobalObservables(*std::unique_ptr<RooAbsCollection>(
6261 d->globs().get<RooArgList>()->selectCommon(*globs().get<RooArgList>())));
6262 if (d->get()->TestBit(1 << 20))
6263 out.back()->get()->SetBit(1 << 20);
6264 // need to attach the original dataset so that things like SetBinContent can interact with it
6265 out.back()->fBrowsables.emplace_back(std::make_shared<xRooNode>(".sourceds", d->fComp, *this));
6266 } else {
6267 out.emplace_back(std::make_shared<xRooNode>(d->fComp, *this));
6268 }
6269 }
6270 }
6271 /*else if(auto p = get<RooFitResult>(); p) {
6272 // look for datasets in workspace that match the fit result name after hashing
6273 for(auto& _d : xRooNode(*_ws,*this).datasets()) {
6274 auto _hash = RooAbsTree::nameToHash(_d->get()->GetName());
6275 if (TString::Format("%d;%d",_hash.first,_hash.second) == p->GetTitle()) {
6276 out.emplace_back(std::make_shared<xRooNode>(_d->fComp, *this));
6277 }
6278 }
6279 }*/
6280 } else if (auto mc = get<RooStats::ModelConfig>()) {
6281 return xRooNode(*mc->GetPdf(), fParent).datasets();
6282 }
6283
6284 return out;
6285}
6286
6287std::shared_ptr<xRooNode> xRooNode::getBrowsable(const char *name) const
6288{
6289 for (auto b : fBrowsables) {
6290 if (b && strcmp(b->GetName(), name) == 0)
6291 return b;
6292 }
6293 return nullptr;
6294}
6295
6297{
6298
6299 if (auto fr = get<RooFitResult>(); fr) {
6300 return nullptr;
6301 }
6302
6303 if (auto theData = get<RooDataSet>(); theData) {
6304
6305 TH1 *theHist = nullptr;
6306
6307 if (fromPad) {
6308 // find first histogram in pad
6309 for (auto o : *fromPad->GetListOfPrimitives()) {
6310 theHist = dynamic_cast<TH1 *>(o);
6311 if (theHist) {
6312 theHist = static_cast<TH1 *>(theHist->Clone());
6313 theHist->Reset();
6314 break;
6315 } // clone because theHist gets deleted below
6316 }
6317 }
6318
6319 if (!theHist) {
6320 auto _parentPdf = parentPdf();
6321 if (!_parentPdf) {
6322 // can still build graph if v is an obs ... will use v binning
6323 auto vo = dynamic_cast<TObject *>(v);
6324 if (v && obs().find(vo->GetName())) {
6325 if (auto cat = dynamic_cast<RooAbsCategoryLValue *>(v)) {
6326 theHist = new TH1D(
6327 TString::Format("%s_%s", GetName(), vo->GetName()),
6328 TString::Format("my temp hist;%s", strlen(vo->GetTitle()) ? vo->GetTitle() : vo->GetName()),
6329 cat->numTypes(), 0, cat->numTypes());
6330 int i = 1;
6331 std::map<int, std::string> cats; // fill into a map to preserve index ordering
6332 for (auto &c : *cat) {
6333 cats[c.second] = c.first;
6334 }
6335 for (auto &[_, label] : cats) {
6336 theHist->GetXaxis()->SetBinLabel(i++, label.c_str());
6337 }
6338 } else {
6339 auto _binning = v->getBinningPtr(nullptr);
6340 if (_binning->isUniform()) {
6341 theHist = new TH1D(
6342 TString::Format("%s_%s", GetName(), vo->GetName()),
6343 TString::Format("my temp hist;%s", strlen(vo->GetTitle()) ? vo->GetTitle() : vo->GetName()),
6344 v->numBins(), _binning->lowBound(), _binning->highBound());
6345 } else {
6346 theHist = new TH1D(
6347 TString::Format("%s_%s", GetName(), vo->GetName()),
6348 TString::Format("my temp hist;%s", strlen(vo->GetTitle()) ? vo->GetTitle() : vo->GetName()),
6349 v->numBins(), _binning->array());
6350 }
6351 }
6352 } else {
6353 throw std::runtime_error("Cannot draw dataset without parent PDF");
6354 }
6355 } else {
6356 theHist = _parentPdf->BuildHistogram(v, true);
6357 }
6358 }
6359 if (!theHist)
6360 return nullptr;
6361 // this hist will get filled with w*x to track weighted x position per bin
6362 TH1 *xPos = static_cast<TH1 *>(theHist->Clone("xPos"));
6363 xPos->Reset();
6364 TH1 *xPos2 = static_cast<TH1 *>(theHist->Clone("xPos2"));
6365 xPos2->Reset();
6366 auto nHist = std::unique_ptr<TH1>(static_cast<TH1 *>(theHist->Clone("nEntries")));
6367 nHist->Reset();
6368
6369 auto dataGraph = new TGraphAsymmErrors;
6370 dataGraph->SetEditable(false);
6371 dataGraph->SetName(GetName());
6372 dataGraph->SetTitle(strlen(theData->GetTitle()) ? theData->GetTitle() : theData->GetName());
6373 // next line triggers creation of the histogram inside the graph, in root 6.22 that isn't protected from being
6374 // added to gDirectory
6375 dataGraph->SetTitle(TString::Format("%s;%s;Events", dataGraph->GetTitle(), theHist->GetXaxis()->GetTitle()));
6376 *static_cast<TAttMarker *>(dataGraph) = *static_cast<TAttMarker *>(theHist);
6377 *static_cast<TAttLine *>(dataGraph) = *static_cast<TAttLine *>(theHist);
6378 dataGraph->SetMarkerStyle(20);
6379 dataGraph->SetLineColor(kBlack);
6380
6381 auto _obs = obs();
6382
6383 // auto x = theData->get()->find((v) ? dynamic_cast<TObject*>(v)->GetName() : theHist->GetXaxis()->GetName());
6384 // const RooAbsReal* xvar = (x) ? dynamic_cast<RooAbsReal*>(x) : nullptr;
6385 // const RooAbsCategory* xcat = (x && !xvar) ? dynamic_cast<RooAbsCategory*>(x) : nullptr;
6386 auto x = _obs.find((v) ? dynamic_cast<TObject *>(v)->GetName()
6387 : (theHist->GetXaxis()->IsAlphanumeric() ? theHist->GetXaxis()->GetTimeFormatOnly()
6388 : theHist->GetXaxis()->GetName()));
6389 if (x && x->get<RooAbsArg>()->getAttribute("global")) {
6390 // is global observable ...
6391 dataGraph->SetPoint(0, x->get<RooAbsReal>()->getVal(), 1e-15);
6392 dataGraph->SetTitle(TString::Format("%s = %f", dataGraph->GetTitle(), dataGraph->GetPointX(0)));
6393 delete xPos;
6394 delete xPos2;
6395 delete theHist;
6396 return dataGraph;
6397 }
6398
6399 const RooAbsReal *xvar = (x) ? x->get<RooAbsReal>() : nullptr;
6400 const RooAbsCategory *xcat = (x && !xvar) ? x->get<RooAbsCategory>() : nullptr;
6401
6402 auto _coords = coords();
6403
6404 TString pName((fromPad) ? fromPad->GetName() : "");
6405 auto _pos = pName.Index('=');
6406
6407 int nevent = theData->numEntries();
6408 for (int i = 0; i < nevent; i++) {
6409 theData->get(i);
6410 bool _skip = false;
6411 for (auto _c : _coords) {
6412 if (auto cat = _c->get<RooAbsCategoryLValue>(); cat) {
6413 if (cat->getIndex() != theData->get()->getCatIndex(cat->GetName())) {
6414 _skip = true;
6415 break;
6416 }
6417 } else if (auto rv = _c->get<RooAbsRealLValue>(); rv) {
6418 // must be in range
6419 if (!rv->inRange(theData->get()->getRealValue(rv->GetName()), rv->getStringAttribute("coordRange"))) {
6420 _skip = true;
6421 break;
6422 }
6423 }
6424 }
6425 if (_pos != -1) {
6426 if (auto cat = dynamic_cast<RooAbsCategory *>(theData->get()->find(TString(pName(0, _pos))));
6427 cat && cat->getLabel() != pName(_pos + 1, pName.Length())) {
6428 _skip = true;
6429 }
6430 }
6431 if (_skip)
6432 continue;
6433
6434 if (xvar) {
6435 xPos->Fill(xvar->getVal(), xvar->getVal() * theData->weight());
6436 xPos2->Fill(xvar->getVal(), pow(xvar->getVal(), 2) * theData->weight());
6437 }
6438
6439 if (xcat) {
6440 theHist->Fill(xcat->getLabel(), theData->weight());
6441 nHist->Fill(xcat->getLabel(), 1);
6442 } else {
6443 theHist->Fill((x) ? xvar->getVal() : 0.5, theData->weight());
6444 nHist->Fill((x) ? xvar->getVal() : 0.5, 1);
6445 }
6446 }
6447
6448 xPos->Divide(theHist);
6449 xPos2->Divide(theHist);
6450
6451 // update the x positions to the means for each bin and use poisson asymmetric errors for data ..
6452 for (int i = 0; i < theHist->GetNbinsX(); i++) {
6453 if (includeZeros || nHist->GetBinContent(i + 1)) {
6454 double val = theHist->GetBinContent(i + 1);
6455
6456 dataGraph->SetPoint(dataGraph->GetN(),
6457 (xvar && val) ? xPos->GetBinContent(i + 1) : theHist->GetBinCenter(i + 1), val);
6458
6459 // x-error will be the (weighted) standard deviation of the x values ...
6460 double xErr = xPos2->GetBinContent(i + 1) - pow(xPos->GetBinContent(i + 1), 2);
6461 xErr = (xErr <= 0) ? 0. : sqrt(xErr); // protects against floating point rounding effects
6462
6463 if (xErr || val) {
6464 dataGraph->SetPointError(dataGraph->GetN() - 1, xErr, xErr,
6465 val - 0.5 * TMath::ChisquareQuantile(TMath::Prob(1, 1) / 2., 2. * (val)),
6466 0.5 * TMath::ChisquareQuantile(1. - TMath::Prob(1, 1) / 2., 2. * (val + 1)) -
6467 val);
6468 }
6469 }
6470 }
6471
6472 // transfer limits from theHist to dataGraph hist
6473 dataGraph->GetHistogram()->GetXaxis()->SetLimits(theHist->GetXaxis()->GetXmin(), theHist->GetXaxis()->GetXmax());
6474 // and bin labels, if any
6475 if (xcat) {
6476 dataGraph->GetHistogram()->GetXaxis()->Set(theHist->GetNbinsX(), 0, theHist->GetNbinsX());
6477 for (int i = 1; i <= theHist->GetNbinsX(); i++)
6478 dataGraph->GetHistogram()->GetXaxis()->SetBinLabel(i, theHist->GetXaxis()->GetBinLabel(i));
6479 }
6480
6481 delete xPos;
6482 delete xPos2;
6483 delete theHist;
6484
6485 // std::shared_ptr<TStyle> style; // use to keep alive for access from GetStyle below, in case getObject
6486 // has decided to return the owning ptr (for some reason) std::string _title =
6487 // strlen(dataGraph->GetTitle()) ? dataGraph->GetTitle() : GetName(); if (!gROOT->GetStyle(_title.c_str()))
6488 // {
6489 // if ( (style = getObject<TStyle>(_title)) ) {
6490 // // loaded style (from workspace?) so put in list and use that
6491 // gROOT->GetListOfStyles()->Add(style.get());
6492 // } else {
6493 // // create new style - gets put in style list automatically so don't have to delete
6494 // // acquire them so saved to workspaces for auto reload ...
6495 // style = const_cast<xRooNode&>(*this).acquireNew<TStyle>(_title.c_str(),
6496 // TString::Format("Style for %s component", _title.c_str()));
6497 // (TAttLine &) (*style) = *dynamic_cast<TAttLine *>(dataGraph);
6498 // (TAttFill &) (*style) = *dynamic_cast<TAttFill *>(dataGraph);
6499 // (TAttMarker &) (*style) = *dynamic_cast<TAttMarker *>(dataGraph);
6500 // gROOT->GetListOfStyles()->Add(style.get());
6501 // }
6502 // }
6503 auto _styleNode = styles(dataGraph);
6504 if (auto _style = _styleNode.get<TStyle>()) {
6505 *dynamic_cast<TAttLine *>(dataGraph) = *_style;
6506 *dynamic_cast<TAttFill *>(dataGraph) = *_style;
6507 *dynamic_cast<TAttMarker *>(dataGraph) = *_style;
6508 }
6509 return dataGraph;
6510 }
6511
6512 throw std::runtime_error("Cannot build graph");
6513}
6514
6516{
6517 if (fr) {
6518 if (auto _w = ws(); _w) {
6519 auto res = acquire(std::shared_ptr<RooFitResult>(const_cast<RooFitResult *>(fr), [](RooFitResult *) {}));
6520 for (auto o : _w->allGenericObjects()) {
6521 if (auto _fr = dynamic_cast<RooFitResult *>(o); _fr) {
6522 _fr->ResetBit(1 << 20);
6523 }
6524 }
6525 res->SetBit(1 << 20);
6526 // assign values
6527 auto allVars = _w->allVars();
6528 allVars = fr->floatParsFinal();
6529 allVars = fr->constPars();
6530 } else {
6531 // need to add to memory as a specific name
6532 throw std::runtime_error("Not supported yet"); // complication is how to replace an existing fitResult in
6533 // .memory auto _clone = std::make_shared<RooFitResult>(*fr);
6534 //_clone->SetName("fitResult");
6535 }
6536 } else {
6538 }
6539}
6540
6542{
6543 if (auto _fr = fr.get<const RooFitResult>()) {
6545 } else
6546 throw std::runtime_error("Not a RooFitResult");
6547}
6548
6549xRooNode xRooNode::fitResult(const char *opt) const
6550{
6551
6552 if (get<RooFitResult>())
6553 return *this;
6554 if (get<RooAbsData>()) {
6555 if (auto _fr = find(".fitResult"); _fr)
6556 return _fr;
6557#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 26, 00)
6558 // check if weightVar of RooAbsData has fitResult attribute on it, will be the generation fit result
6559 if (get<RooDataSet>() && get<RooDataSet>()->weightVar() &&
6560 get<RooDataSet>()->weightVar()->getStringAttribute("fitResult")) {
6561 return xRooNode(getObject<const RooFitResult>(get<RooDataSet>()->weightVar()->getStringAttribute("fitResult")),
6562 *this);
6563 }
6564#endif
6565 return xRooNode();
6566 }
6567
6568 TString sOpt(opt);
6569 if (sOpt == "prefit") {
6570 // build a fitResult using nominal values and infer errors from constraints
6571 // that aren't the 'main' constraints
6572 // Warning("fitResult","Building prefitResult by examining pdf. Consider setting an explicit prefitResult
6573 // (SetFitResult(fr)) where fr name is prefitResult");
6574
6575 // ensure coefs are included if there are any
6576 auto _coefs = coefs();
6577 if (_coefs.get()) {
6578 return xRooNode(RooProduct("tmp", "tmp", RooArgList(*get<RooAbsArg>(), *_coefs.get<RooAbsReal>())))
6579 .fitResult(opt);
6580 }
6581
6582 std::unique_ptr<RooArgList> _pars(dynamic_cast<RooArgList *>(pars().argList().selectByAttrib("Constant", false)));
6583 auto fr = std::make_shared<RooFitResult>("prefitResult", "Prefit");
6584 fr->setFinalParList(*_pars);
6585 for (auto &p : fr->floatParsFinal()) {
6586 auto _v = dynamic_cast<RooRealVar *>(p);
6587 if (!_v)
6588 continue;
6589 if (auto s = _v->getStringAttribute("nominal"); s)
6590 _v->setVal(TString(s).Atof());
6591 auto _constr = xRooNode(fParent->getObject<RooRealVar>(p->GetName()), *this).constraints();
6592 std::shared_ptr<xRooNode> pConstr;
6593 for (auto &c : _constr) {
6594 if (c->get<RooPoisson>() || c->get<RooGaussian>()) {
6595 // require parameter to be a direct server of the constraint pdf to count
6596 bool isServer = true;
6597 if (c->get<RooGaussian>()) {
6598 isServer = false;
6599 for (auto s : c->get<RooAbsArg>()->servers()) {
6600 if (strcmp(s->GetName(), p->GetName()) == 0) {
6601 isServer = true;
6602 break;
6603 }
6604 }
6605 }
6606 if (isServer) {
6607 pConstr = c;
6608 break;
6609 }
6610 }
6611 }
6612 if (pConstr) {
6613 // there will be 3 deps, one will be this par, the other two are the mean and error (or error^2 in case of
6614 // poisson use the one that's a ConstVar as the error to break a tie ...
6615 double prefitVal = 0;
6616 double prefitError = 0;
6617 for (auto &_d : pConstr->vars()) {
6618 if (strcmp(p->GetName(), _d->get()->GetName()) == 0)
6619 continue;
6620 if (auto _c = _d->get<RooConstVar>(); _c && _c->getVal() != 0) {
6621 if (prefitError)
6622 prefitVal = prefitError; // loading val into error already, so move it over
6623 prefitError = _c->getVal();
6624 } else if (prefitError == 0) {
6625 prefitError = _d->get<RooAbsReal>()->getVal();
6626 } else {
6627 prefitVal = _d->get<RooAbsReal>()->getVal();
6628 }
6629 }
6630
6631 if (pConstr->get<RooGaussian>() && pConstr->browse().find(".sigma")) {
6632 prefitError = pConstr->find(".sigma")->get<RooAbsReal>()->getVal();
6633 }
6634 // std::cout << p->GetName() << " extracted " << prefitVal << " " << prefitError << " from ";
6635 // pConstr->deps().Print();
6636 if (pConstr->get<RooPoisson>()) {
6637 // prefitVal will be the global observable value, need to divide that by tau
6639 // prefiterror will be tau ... need 1/sqrt(tau) for error
6640 prefitError = 1. / sqrt(prefitError);
6641 }
6642 if (!_v->getStringAttribute("nominal"))
6643 _v->setVal(prefitVal);
6644 _v->setError(prefitError);
6645 } else {
6646 // unconstrained, remove error
6647 _v->removeError();
6648 }
6649 }
6650 auto _args = consts().argList();
6651 _args.add(pp().argList());
6652 // global obs are added to constPars list too
6653 auto _globs = globs(); // keep alive as may own glob
6654 _args.add(_globs.argList());
6655 fr->setConstParList(_args);
6656 std::unique_ptr<RooArgList> _snap(dynamic_cast<RooArgList *>(_pars->snapshot()));
6657 for (auto &p : *_snap) {
6658 if (auto atr = p->getStringAttribute("initVal"); atr && dynamic_cast<RooRealVar *>(p))
6659 dynamic_cast<RooRealVar *>(p)->setVal(TString(atr).Atof());
6660 }
6661 fr->setInitParList(*_snap);
6662 return xRooNode(fr, *this);
6663 }
6664
6665 // return first checked fit result present in the workspace
6666 if (auto _w = ws(); _w) {
6667 auto checkFr = [&](TObject *o) {
6668 if (auto _fr = dynamic_cast<RooFitResult *>(o); _fr && _fr->TestBit(1 << 20)) {
6669 // check all pars match final/const values ... if mismatch need to create a new RooFitResult
6670 bool match = true;
6671 for (auto p : pars()) {
6672 if (!p->get<RooAbsReal>()) {
6673 if (auto cat = p->get<RooAbsCategory>();
6674 cat && cat->getCurrentIndex() ==
6675 _fr->floatParsFinal().getCatIndex(cat->GetName(), std::numeric_limits<int>().max())) {
6676 match = false;
6677 break;
6678 }
6679 } else if (p->get<RooAbsArg>()->getAttribute("Constant")) {
6680 if (_fr->floatParsFinal().find(p->GetName()) ||
6681 std::abs(_fr->constPars().getRealValue(p->GetName(), std::numeric_limits<double>::quiet_NaN()) -
6682 p->get<RooAbsReal>()->getVal()) > 1e-15) {
6683 match = false;
6684 break;
6685 }
6686 } else {
6687 if (_fr->constPars().find(p->GetName()) ||
6688 std::abs(
6689 _fr->floatParsFinal().getRealValue(p->GetName(), std::numeric_limits<double>::quiet_NaN()) -
6690 p->get<RooAbsReal>()->getVal()) > 1e-15) {
6691 match = false;
6692 break;
6693 }
6694 }
6695 }
6696 if (!match) {
6697 // create new fit result using covariances from this fit result
6698 std::unique_ptr<RooArgList> _pars(
6699 dynamic_cast<RooArgList *>(pars().argList().selectByAttrib("Constant", false)));
6700 auto fr = std::make_shared<RooFitResult>(TString::Format("%s-dirty", _fr->GetName()));
6701 fr->SetTitle(TString::Format("%s parameter snapshot", GetName()));
6702 fr->setFinalParList(*_pars);
6703 TMatrixTSym<double> *prevCov = static_cast<TMatrixTSym<double> *>(GETDMP(_fr, _VM));
6704 if (prevCov) {
6705 auto cov = _fr->reducedCovarianceMatrix(*_pars);
6706 // make the diagonals all the current error values
6707 for (size_t i = 0; i < _pars->size(); i++) {
6708 if (auto v = dynamic_cast<RooRealVar *>(_pars->at(i))) {
6709 cov(i, i) = pow(v->getError(), 2);
6710 } else {
6711 cov(i, i) = 0;
6712 }
6713 }
6714 fr->setCovarianceMatrix(cov);
6715 }
6716
6717 auto _args = consts().argList();
6718 _args.add(pp().argList());
6719 // global obs are added to constPars list too
6720 auto _globs = globs(); // keep alive as may own glob
6721 _args.add(_globs.argList());
6722 fr->setConstParList(_args);
6723 std::unique_ptr<RooArgList> _snap(dynamic_cast<RooArgList *>(_pars->snapshot()));
6724 for (auto &p : *_snap) {
6725 if (auto atr = p->getStringAttribute("initVal"); atr && dynamic_cast<RooRealVar *>(p))
6726 dynamic_cast<RooRealVar *>(p)->setVal(TString(atr).Atof());
6727 }
6728 fr->setInitParList(*_snap);
6729 return xRooNode(fr, *this);
6730 }
6731 return xRooNode(*_fr, std::make_shared<xRooNode>(*_w, std::make_shared<xRooNode>()));
6732 }
6733 return xRooNode();
6734 };
6735 for (auto o : _w->allGenericObjects()) {
6736 auto out = checkFr(o);
6737 if (out)
6738 return out;
6739 }
6740 for (auto o : GETWSSNAPSHOTS(_w)) {
6741 auto out = checkFr(o);
6742 if (out)
6743 return out;
6744 }
6745 } else {
6746 // objects not in workspaces are allowed to have a fitResult set in their memory
6747 // use getObject to get it
6748 if (auto fr = getObject<RooFitResult>(".fitResult"); fr) {
6749 return xRooNode(fr, *this);
6750 }
6751 }
6752
6753 // ensure coefs are included if there are any
6754 auto _coefs = coefs();
6755 if (_coefs.get()) {
6756 return xRooNode(RooProduct("tmp", "tmp", RooArgList(*get<RooAbsArg>(), *_coefs.get<RooAbsReal>())))
6757 .fitResult(opt);
6758 }
6759
6760 std::unique_ptr<RooArgList> _pars(dynamic_cast<RooArgList *>(pars().argList().selectByAttrib("Constant", false)));
6761 auto fr = std::make_shared<RooFitResult>(TUUID().AsString());
6762 fr->SetTitle(TString::Format("%s uncorrelated parameter snapshot", GetName()));
6763 fr->setFinalParList(*_pars);
6764 fr->setStatus(-1);
6765
6766 TMatrixDSym cov(fr->floatParsFinal().size());
6767 TMatrixTSym<double> *prevCov = static_cast<TMatrixTSym<double> *>(GETDMP(fr.get(), _VM));
6768 if (prevCov) {
6769 for (int i = 0; i < prevCov->GetNcols(); i++) {
6770 for (int j = 0; j < prevCov->GetNrows(); j++) {
6771 cov(i, j) = (*prevCov)(i, j);
6772 }
6773 }
6774 }
6775 int i = 0;
6776 for (auto &p : fr->floatParsFinal()) {
6777 if (!prevCov || i >= prevCov->GetNcols()) {
6778 if (auto v = dynamic_cast<RooRealVar *>(p)) {
6779 cov(i, i) = pow(v->getError(), 2);
6780 } else {
6781 cov(i, i) = 0;
6782 }
6783 }
6784 i++;
6785 }
6786 int covQualBackup = fr->covQual();
6787 fr->setCovarianceMatrix(cov);
6788 fr->setCovQual(covQualBackup);
6789
6790 auto _args = consts().argList();
6791 _args.add(pp().argList());
6792 // global obs are added to constPars list too
6793 auto _globs = globs(); // keep alive as may own glob
6794 _args.add(_globs.argList());
6795 fr->setConstParList(_args);
6796 std::unique_ptr<RooArgList> _snap(dynamic_cast<RooArgList *>(_pars->snapshot()));
6797 for (auto &p : *_snap) {
6798 if (auto atr = p->getStringAttribute("initVal"); atr && dynamic_cast<RooRealVar *>(p))
6799 dynamic_cast<RooRealVar *>(p)->setVal(TString(atr).Atof());
6800 }
6801 fr->setInitParList(*_snap);
6802
6803 // return *const_cast<Node2*>(this)->emplace_back(std::make_shared<Node2>(".fitResult",fr,*this));
6804 return xRooNode(fr, *this);
6805}
6806
6807// xRooNode xRooNode::fitTo_(const char* datasetName) const {
6808// try {
6809// return fitTo(datasetName);
6810// } catch(const std::exception& e) {
6811// new TGMsgBox(gClient->GetRoot(), gClient->GetRoot(), "Exception", e.what(),kMBIconExclamation); // deletes
6812// self on dismiss? return xRooNode();
6813// }
6814// }
6815//
6816// xRooNode xRooNode::fitTo(const char* datasetName) const {
6817// return fitTo(*datasets().at(datasetName));
6818// }
6819
6820void xRooNode::SetRange(const char *range, double low, double high)
6821{
6822 if (!std::isnan(low) && !std::isnan(high) && get<RooRealVar>()) {
6823 if (range && strlen(range)) {
6824 get<RooRealVar>()->setRange(range, low, high);
6825 } else {
6826 get<RooRealVar>()->setRange(low, high);
6827 }
6828 return;
6829 }
6830 if (auto o = get<RooAbsArg>(); o)
6831 o->setStringAttribute("range", range);
6832 // todo: clear the range attribute on all servers
6833 // could make this controlled by a flag but probably easiest to enforce so you must set range
6834 // in children after if you wanted to override
6835}
6836const char *xRooNode::GetRange() const
6837{
6838 std::string &out = fRange;
6839 if (auto o = get<RooAbsArg>(); o && o->getStringAttribute("range"))
6840 out = o->getStringAttribute("range");
6841 auto _parent = fParent;
6842 while (out.empty() && _parent) {
6843 if (auto o = _parent->get<RooAbsArg>(); o && o->getStringAttribute("range"))
6844 out = o->getStringAttribute("range");
6845 _parent = _parent->fParent;
6846 }
6847 return out.c_str();
6848}
6849
6850xRooNLLVar xRooNode::nll(const char *_data, std::initializer_list<RooCmdArg> nllOpts) const
6851{
6852 return nll(xRooNode(_data), nllOpts);
6853}
6854
6856{
6857 return nll(_data, *xRooFit::createNLLOptions());
6858}
6859
6860xRooNLLVar xRooNode::nll(const xRooNode &_data, std::initializer_list<RooCmdArg> nllOpts) const
6861{
6862 auto defaultOpts = xRooFit::createNLLOptions(); // smart pointer will cleanup the list
6863 // add user-specified options to list ... if already existing in default list, override and warn
6865 for (auto opt : *defaultOpts) {
6866 l.Add(opt);
6867 }
6868 for (auto &i : nllOpts) {
6869 if (auto o = l.FindObject(i.GetName())) {
6870 Info("nll", "Overriding NLL Option: %s", o->GetName());
6871 l.Remove(o);
6872 }
6873 l.Add(const_cast<RooCmdArg *>(&i));
6874 }
6875
6876 return nll(_data, l);
6877}
6878
6880{
6881 if (auto mc = get<RooStats::ModelConfig>()) {
6882 return xRooNode(*mc->GetPdf(), fParent).generate(fr, expected, seed);
6883 }
6884
6885 if (!get<RooAbsPdf>()) {
6886 // before giving up, if this is a workspace we can proceed if we only have one model
6887 if (get<RooWorkspace>()) {
6888 std::shared_ptr<xRooNode> mainModel;
6889 for (auto &c : const_cast<xRooNode *>(this)->browse()) {
6890 if (c->get<RooAbsPdf>()) {
6891 if (!mainModel) {
6892 mainModel = c;
6893 } else {
6894 throw std::runtime_error(TString::Format("Workspace has multiple models, you must specify which to "
6895 "generate with (found at least %s and %s)",
6896 mainModel->GetName(), c->GetName()));
6897 }
6898 }
6899 }
6900 if (mainModel)
6901 return mainModel->generate(fr, expected, seed);
6902 }
6903 throw std::runtime_error(TString::Format("%s is not a pdf", GetName()));
6904 }
6905 auto _fr = fr.get<RooFitResult>();
6906
6907 // when generating, will only include channels that are selected
6908 // any unselected but not hidden channel will have data from the only selected dataset added to it
6909 if (get<RooSimultaneous>()) {
6910 std::string selected;
6911 std::string fromds; // list of channels to take from selected ds
6912 bool hasDeselected = false;
6913 for (auto c : bins()) {
6914 TString cName(c->GetName());
6915 cName = cName(cName.Index('=') + 1, cName.Length());
6916 if (!c->get<RooAbsReal>()->isSelectedComp()) {
6917 hasDeselected = true;
6918 if (!c->get<RooAbsArg>()->getAttribute("hidden")) {
6919 if (!fromds.empty())
6920 fromds += ",";
6921 fromds += cName.Data();
6922 }
6923 } else {
6924 if (!selected.empty())
6925 selected += ",";
6926 selected += cName.Data();
6927 }
6928 }
6929 if (hasDeselected) {
6930 std::string dsetName = "";
6931 if (!fromds.empty()) {
6932 // use the first selected dataset as protodata
6933 auto _dsets = datasets();
6934 for (auto &d : _dsets) {
6935 if (d->get()->TestBit(1 << 20)) {
6936 dsetName = d->get()->GetName();
6937 break;
6938 }
6939 }
6940 if (dsetName.empty()) {
6941 throw std::runtime_error(
6942 "Need at least one dataset selected (SetChecked) to use for deselected regions");
6943 }
6944 }
6945 auto result = reduced(selected).generate(fr, expected, seed);
6946 if (!fromds.empty()) {
6947 auto ds = reduced(fromds).datasets()[dsetName];
6948 result.Add(*ds);
6949 result.SetName(TString(result.GetName()) + "_and_" + dsetName.c_str());
6950 }
6951 return result;
6952 }
6953 }
6954
6955 return xRooNode(
6957 *this);
6958}
6959
6960xRooNLLVar xRooNode::nll(const xRooNode &_data, const RooLinkedList &opts) const
6961{
6962 if (auto mc = get<RooStats::ModelConfig>()) {
6963 if (mc->GetExternalConstraints()) {
6965 for (auto o : opts) {
6966 optsWithConstraints.Add(o->Clone(nullptr));
6967 }
6968 optsWithConstraints.Add(RooFit::ExternalConstraints(*mc->GetExternalConstraints()).Clone(nullptr));
6969 return xRooNode(*mc->GetPdf(), fParent).nll(_data, optsWithConstraints);
6970 } else {
6971 return xRooNode(*mc->GetPdf(), fParent).nll(_data, opts);
6972 }
6973 }
6974
6975 if (!get<RooAbsPdf>()) {
6976 // before giving up, if this is a workspace we can proceed if we only have one model or pdf
6977 if (get<RooWorkspace>()) {
6978 std::shared_ptr<xRooNode> mainPdf, mainModel, otherPdf;
6979 for (auto &c : const_cast<xRooNode *>(this)->browse()) {
6980 if (c->get<RooAbsPdf>()) {
6981 if (!mainPdf) {
6982 mainPdf = c;
6983 } else {
6984 otherPdf = c;
6985 }
6986 } else if (c->get<RooStats::ModelConfig>()) {
6987 if (!mainModel) {
6988 mainModel = c;
6989 } else {
6990 throw std::runtime_error(TString::Format("Workspace has multiple models, you must specify which to "
6991 "build nll with (found at least %s and %s)",
6992 mainModel->GetName(), c->GetName()));
6993 }
6994 }
6995 }
6996 if (mainModel)
6997 return mainModel->nll(_data, opts);
6998 if (mainPdf) {
6999 if (otherPdf) {
7000 throw std::runtime_error(TString::Format("Workspace has multiple pdfs, you must specify which to "
7001 "build nll with (found at least %s and %s)",
7002 mainPdf->GetName(), otherPdf->GetName()));
7003 }
7004 return mainPdf->nll(_data, opts);
7005 }
7006 }
7007 throw std::runtime_error(TString::Format("%s is not a pdf", GetName()));
7008 }
7009
7010 // if simultaneous and any channels deselected then reduce and return
7011 if (get<RooSimultaneous>()) {
7012 std::string selected;
7013 bool hasDeselected = false;
7014 for (auto c : bins()) {
7015 if (!c->get<RooAbsReal>()->isSelectedComp()) {
7016 hasDeselected = true;
7017 } else {
7018 TString cName(c->GetName());
7019 cName = cName(cName.Index('=') + 1, cName.Length());
7020 if (!selected.empty())
7021 selected += ",";
7022 selected += cName.Data();
7023 }
7024 }
7025 if (hasDeselected)
7026 return reduced(selected).nll(_data, opts);
7027 }
7028
7029 if (!_data.get<RooAbsData>()) {
7030 // use node name to find dataset and recall
7031 auto _d = strlen(_data.GetName()) ? datasets().find(_data.GetName()) : nullptr;
7032 if (strlen(_data.GetName()) == 0) {
7033 // create the EXPECTED (asimov) dataset with the observables
7035 _d = std::make_shared<xRooNode>(asi.first, *this);
7036 if (asi.second) {
7037 _d->emplace_back(
7038 std::make_shared<xRooNode>(".globs", std::const_pointer_cast<RooAbsCollection>(asi.second), *_d));
7039 }
7040 } else if (!_d) {
7041 throw std::runtime_error(TString::Format("Cannot find dataset %s", _data.GetName()));
7042 }
7043 return nll(*_d, opts);
7044 } else if (!_data.fParent || _data.fParent->fComp != fComp) {
7045 // dataset is not parented by this node ... meaning it may need to be reduced,
7046 // do this via the datasets() method by attaching and detaching the dataset
7047 xRooNode me(*this); // since we are in a const method, need to create a copy node.
7048 me.push_back(std::make_shared<xRooNode>(_data));
7049 return nll(*me.datasets().at(_data.GetName()), opts);
7050 }
7051
7052 auto _globs = _data.globs(); // keep alive because may own the globs
7053
7054 auto _opts = std::shared_ptr<RooLinkedList>(new RooLinkedList, [](RooLinkedList *l) {
7055 if (l)
7056 l->Delete();
7057 delete l;
7058 });
7059 RooArgSet _globsSet(_globs.argList());
7061 if (GetRange() && strlen(GetRange()))
7062 _opts->Add(RooFit::Range(GetRange()).Clone());
7063
7064 // copy over opts ... need to clone each so can safely delete when _opts destroyed
7065 for (int i = 0; i < opts.GetSize(); i++) {
7066 if (strlen(opts.At(i)->GetName()) == 0)
7067 continue; // skipping "none" cmds
7068 if (strcmp(opts.At(i)->GetName(), "GlobalObservables") == 0) {
7069 // maybe warn here?
7070 } else {
7071 _opts->Add(opts.At(i)->Clone(nullptr)); // nullptr needed because accessing Clone via TObject base class puts
7072 // "" instead, so doesnt copy names
7073 }
7074 }
7075
7076 // use shared_ptr method so NLLVar will take ownership of datasets etc if created above
7077 // snapshots the globs out of the nllOpts (see specific constructor of xRooNLLVar)
7078 auto out = xRooFit::createNLL(std::dynamic_pointer_cast<RooAbsPdf>(fComp),
7079 std::dynamic_pointer_cast<RooAbsData>(_data.fComp), *_opts);
7080 return out;
7081}
7082
7083// xRooNode xRooNode::fitTo(const xRooNode& _data) const {
7084//
7085//
7086// auto _pdf = get<RooAbsPdf>();
7087// if (!_pdf) throw std::runtime_error("Not a pdf");
7088//
7089// auto _globs = _data.globs(); // keep alive because may own the globs
7090// RooArgSet globsSet(_globs.argList());
7091//
7092// std::shared_ptr<RooSimultaneous> newPdf;
7093// if(auto s = get<RooSimultaneous>(); s) {
7094// auto rangeName = GetRange();
7095// if (rangeName) {
7096// // need to reduce the RooSimultaneous until fix: https://github.com/root-project/root/issues/8231
7097// std::vector<TString> chanPatterns;
7098// TStringToken pattern(rangeName, ",");
7099// while (pattern.NextToken()) {
7100// chanPatterns.emplace_back(pattern);
7101// }
7102// auto& _cat = const_cast<RooAbsCategoryLValue&>(s->indexCat());
7103// newPdf = std::make_shared<RooSimultaneous>(TString::Format("%s_reduced",GetName()),"Reduced model",_cat);
7104// for(auto& c : variations()) {
7105// TString cName(c->GetName());
7106// cName = cName(cName.Index('=')+1,cName.Length());
7107// _cat.setLabel(cName);
7108// bool matchAny=false;
7109// for(auto& p : chanPatterns) {
7110// if (cName.Contains(TRegexp(p,true))) { matchAny=true; break; }
7111// if (_cat.hasRange(p) && _cat.inRange(p)) { matchAny=true; break; }
7112// }
7113// if(matchAny) {
7114// newPdf->addPdf( *c->get<RooAbsPdf>(), cName );
7115// }
7116// }
7117// RooFitResultTree t(newPdf->GetName(),"",*newPdf);
7118// auto _fr = std::const_pointer_cast<RooFitResult>(t.fitTo(_data.get<RooAbsData>(), &globsSet));
7119// xRooNode parent(_data.GetName(),nullptr,*this);
7120// xRooNode out(_fr->GetName(),/*acquire(_fr)*/ _fr,parent);
7121// // do full propagation by 'checking' the fr ...
7122// out.Checked(&out,true);
7123// return out;
7124// }
7125// }
7126//
7127//
7128//
7129// std::string treeName = TString::Format("fits_%s",GetName()).Data();
7130//
7131// auto _frt = getObject<TTree>(treeName); // get existing frt
7132//
7133// std::shared_ptr<RooFitResultTree> t;
7134// if (_frt) {
7135// t = std::make_shared<RooFitResultTree>(_frt.get());
7136// } else {
7137// t = std::make_shared<RooFitResultTree>(treeName.c_str(),"",*_pdf);
7138// }
7139// //t->SetProgress(true);
7140// auto _fr = std::const_pointer_cast<RooFitResult>(t->fitTo(_data.get<RooAbsData>(), &globsSet));
7141//
7142//
7143//
7144// /*
7145// obs().argList() = s; // sets global observables to their values
7146// auto _fr =
7147// std::shared_ptr<RooFitResult>(_pdf->fitTo(*_data->get<RooAbsData>(),RooFit::GlobalObservables(s),RooFit::Offset(true),RooFit::Save()));
7148// _fr->SetName(TUUID().AsString());
7149// // restore parameters before returning
7150// *std::unique_ptr<RooArgSet>(_pdf->getDependents(_fr->floatParsFinal())) = _fr->floatParsInit();
7151// */
7152//
7153// //_fr->SetTitle(TString::Format("%s;%s",GetName(),datasetName));
7154// if (!_frt) {
7155// t =
7156// std::make_shared<RooFitResultTree>(std::dynamic_pointer_cast<TTree>(const_cast<xRooNode*>(this)->acquire(t->fTree)).get());
7157// }
7158// xRooNode parent(_data.GetName(),nullptr,xRooNode(t,*this));
7159// xRooNode out(_fr->GetName(),/*acquire(_fr)*/ _fr,parent);
7160// // do full propagation by 'checking' the fr ...
7161// out.Checked(&out,true);
7162// return out;
7163// }
7164
7165std::shared_ptr<xRooNode> xRooNode::parentPdf() const
7166{
7167 // find first parent that is a pdf
7168 auto out = fParent;
7169 while (out && !out->get<RooAbsPdf>()) {
7170 out = out->fParent;
7171 }
7172 return out;
7173}
7174
7175xRooNode xRooNode::reduced(const std::string &_range, bool invert) const
7176{
7177 auto rangeName = (_range.empty()) ? GetRange() : _range;
7178 if (!rangeName.empty()) {
7179 std::vector<TString> patterns;
7180 TStringToken pattern(rangeName, ",");
7181 while (pattern.NextToken()) {
7182 patterns.emplace_back(pattern);
7183 }
7184 if (auto s = get<RooSimultaneous>(); s) {
7185 // need to reduce the RooSimultaneous until fix: https://github.com/root-project/root/issues/8231
7186 auto &_cat = const_cast<RooAbsCategoryLValue &>(s->indexCat());
7187 auto newPdf =
7188 std::make_shared<RooSimultaneous>(TString::Format("%s_reduced", GetName()), "Reduced model", _cat);
7189 for (auto &c : bins()) {
7190 TString cName(c->GetName());
7191 cName = cName(cName.Index('=') + 1, cName.Length());
7192 _cat.setLabel(cName);
7193 bool matchAny = false;
7194 for (auto &p : patterns) {
7195 if (cName.Contains(TRegexp(p, true))) {
7196 matchAny = true;
7197 break;
7198 }
7199 if (_cat.hasRange(p) && _cat.inRange(p)) {
7200 matchAny = true;
7201 break;
7202 }
7203 }
7204 if ((matchAny && !invert) || (!matchAny && invert)) {
7205 newPdf->addPdf(*c->get<RooAbsPdf>(), cName);
7206 }
7207 }
7208 return xRooNode(newPdf, fParent);
7209 } else if (get() && !get<RooAbsCollection>() && !components().empty()) {
7210 // create a new obj and remove non-matching components
7211 xRooNode out(std::shared_ptr<TObject>(get()->Clone(TString::Format("%s_reduced", get()->GetName()))), fParent);
7212 // go through components and remove any that don't match pattern
7213 std::vector<TObject *> funcs; // to be removed
7214 for (auto &c : out.components()) {
7215 bool matchAny = false;
7216 for (auto &p : patterns) {
7217 if (TString(c->GetName()).Contains(TRegexp(p, true))) {
7218 matchAny = true;
7219 break;
7220 }
7221 }
7222 if (!((matchAny && !invert) || (!matchAny && invert)))
7223 funcs.push_back(c->get());
7224 }
7225 for (auto &c : funcs)
7226 out.Remove(*c);
7227 if (!funcs.empty()) {
7228 if (auto _pdf = out.get<RooRealSumPdf>(); _pdf) {
7229 _pdf->setFloor(false); // remove floor if removed some functions, which allows evaluation of negative
7230 // valued components
7231 }
7232 }
7233 out.browse();
7234 return out;
7235 } else if (auto fr = get<RooFitResult>()) {
7236 // reduce the fit result by moving unselected float pars into the constPars list and dropping their covariances
7237 xRooNode out(std::shared_ptr<TObject>(fr->Clone(TString::Format("%s_reduced", fr->GetName()))), fParent);
7238 fr = out.get<RooFitResult>();
7239 RooArgList _pars = fr->floatParsFinal();
7241 for (auto c : _pars) {
7242 bool matchAny = false;
7243 for (auto &p : patterns) {
7244 if (TString(c->GetName()).Contains(TRegexp(p, true))) {
7245 matchAny = true;
7246 break;
7247 }
7248 }
7249 if (!((matchAny && !invert) || (!matchAny && invert))) {
7250 _remPars.add(*c);
7251 }
7252 }
7253 _pars.remove(_remPars, true);
7254
7255 auto _tmp = fr->reducedCovarianceMatrix(_pars);
7256 int covQualBackup = fr->covQual();
7257 fr->setCovarianceMatrix(_tmp);
7258 fr->setCovQual(covQualBackup);
7259 const_cast<RooArgList &>(fr->floatParsFinal())
7260 .remove(_remPars, true); // is this a memory leak ... should delete the remPars?
7261 return out;
7262
7263 } else if (!get() || get<RooAbsCollection>()) {
7264 // filter the children .... handle special case of filtering ".vars" with "x" option too
7265 xRooNode out(std::make_shared<RooArgList>(), fParent);
7266 out.SetName(TString(GetName()) + "_reduced");
7267 size_t nobs = 0;
7268 bool notAllArgs = false;
7269 bool isVars = (strcmp(GetName(), ".vars") == 0);
7270 for (auto c : *this) {
7271 nobs += (c->fFolder == "!robs" || c->fFolder == "!globs");
7272 bool matchAny = false;
7273 for (auto &p : patterns) {
7274 if (TString(c->GetName()).Contains(TRegexp(p, true)) ||
7275 (isVars && p == "x" && (c->fFolder == "!robs" || c->fFolder == "!globs") && nobs == 1)) {
7276 matchAny = true;
7277 break;
7278 }
7279 }
7280 if ((matchAny && !invert) || (!matchAny && invert)) {
7281 out.push_back(c);
7282 if (auto a = c->get<RooAbsArg>()) {
7283 out.get<RooArgList>()->add(*a);
7284 } else {
7285 notAllArgs = true;
7286 }
7287 }
7288 }
7289 if (notAllArgs) {
7290 out.fComp.reset();
7291 }
7292 return out;
7293 }
7294 }
7295
7296 return get<RooArgList>() ? xRooNode(std::make_shared<RooArgList>(), fParent) : *this;
7297}
7298
7299// xRooNode xRooNode::generate(bool expected) const {
7300//
7301// auto fr = fitResult();
7302// auto _fr = fr.get<RooFitResult>();
7303//
7304// auto _pdf = (get<RooAbsPdf>()) ? std::shared_ptr<const xRooNode>(this, [](const xRooNode*){}) : parentPdf();
7305// if (!_pdf) {
7306// throw std::runtime_error("Could not find pdf");
7307// }
7308//
7309// std::shared_ptr<RooDataTree> t;
7310//
7311// std::shared_ptr<RooSimultaneous> newPdf;
7312// if(auto s = _pdf->get<RooSimultaneous>(); s) {
7313// auto rangeName = GetRange();
7314// if (rangeName) {
7315// // need to reduce the RooSimultaneous until fix: https://github.com/root-project/root/issues/8231
7316// std::vector<TString> chanPatterns;
7317// TStringToken pattern(rangeName, ",");
7318// while (pattern.NextToken()) {
7319// chanPatterns.emplace_back(pattern);
7320// }
7321// auto& _cat = const_cast<RooAbsCategoryLValue&>(s->indexCat());
7322// newPdf = std::make_shared<RooSimultaneous>(TString::Format("%s_reduced",GetName()),"Reduced model",_cat);
7323// for(auto& c : _pdf->variations()) {
7324// TString cName(c->GetName());
7325// cName = cName(cName.Index('=')+1,cName.Length());
7326// _cat.setLabel(cName);
7327// bool matchAny=false;
7328// for(auto& p : chanPatterns) {
7329// if (cName.Contains(TRegexp(p,true))) { matchAny=true; break; }
7330// if (_cat.hasRange(p) && _cat.inRange(p)) { matchAny=true; break; }
7331// }
7332// if(matchAny) {
7333// newPdf->addPdf( *c->get<RooAbsPdf>(), cName );
7334// }
7335// }
7336// t = std::make_shared<RooDataTree>(newPdf->GetName(),"",*newPdf);
7337// RooArgSet s1(_pdf->obs().argList());
7338// RooArgSet s2(_pdf->globs().argList());s1.remove(s2);
7339// t->SetObservables(&s1,&s2);
7340// auto _data = t->generate(_fr,expected);
7341//
7342// xRooNode parent(_fr ? _fr->GetName() : "unknown",nullptr,xRooNode(t,*this));
7343// xRooNode out(_data.first->GetName(),/*acquire(_fr)*/ _data.first,parent);
7344// out.emplace_back(std::make_shared<xRooNode>(".globs",std::const_pointer_cast<RooArgSet>(_data.second),out));
7345// return out;
7346// }
7347// }
7348//
7349//
7350// std::string treeName = TString::Format("gen_%s",_pdf->GetName()).Data();
7351//
7352// auto _frt = getObject<TTree>(treeName); // get existing frt
7353//
7354//
7355// if (_frt) {
7356// t = std::make_shared<RooDataTree>(_frt.get());
7357// } else {
7358// t = std::make_shared<RooDataTree>(treeName.c_str(),"",*_pdf->get<RooAbsPdf>());
7359// RooArgSet s1(_pdf->obs().argList());
7360// RooArgSet s2(_pdf->globs().argList());s1.remove(s2);
7361// t->SetObservables(&s1,&s2);
7362// }
7363// auto _data = t->generate(_fr,expected);
7364// if (!_frt) {
7365// t =
7366// std::make_shared<RooDataTree>(std::dynamic_pointer_cast<TTree>(const_cast<xRooNode*>(this)->acquire(t->fTree)).get());
7367// }
7368// xRooNode parent(_fr ? _fr->GetName() : "unknown",nullptr,xRooNode(t,*this));
7369// xRooNode out(_data.first->GetName(),/*acquire(_fr)*/ _data.first,parent);
7370// out.emplace_back(std::make_shared<xRooNode>(".globs",std::const_pointer_cast<RooArgSet>(_data.second),out));
7371// return out;
7372// }
7373
7375public:
7377 double expectedEvents(const RooArgSet *nset) const override
7378 {
7379 return static_cast<RooAbsPdf *>(intpdf.absArg())->expectedEvents(nset);
7380 }
7381 ExtendMode extendMode() const override { return static_cast<RooAbsPdf *>(intpdf.absArg())->extendMode(); }
7382 TObject *clone(const char *newname) const override { return new xRooProjectedPdf(*this, newname); }
7383
7384protected:
7385 double evaluate() const override
7386 {
7387 int code;
7388 return getProjection(&intobs, _normSet, (_normRange.Length() > 0 ? _normRange.Data() : nullptr), code)->getVal();
7389 }
7390};
7391
7392double new_getPropagatedError(const RooAbsReal &f, const RooFitResult &fr, const RooArgSet &nset = {},
7393 RooArgList **pars = nullptr, bool asymHi = false, bool asymLo = false)
7394{
7395 // Calling getParameters() might be costly, but necessary to get the right
7396 // parameters in the RooAbsReal. The RooFitResult only stores snapshots.
7397
7398 // handle simple case that function is a RooRealVar
7399 if (auto rrv = dynamic_cast<const RooRealVar *>(&f); rrv) {
7400 if (auto frrrv = dynamic_cast<RooRealVar *>(fr.floatParsFinal().find(*rrv)); frrrv) {
7401 rrv = frrrv; // use value from fit result
7402 }
7403 if (asymHi) {
7404 return rrv->getErrorHi();
7405 } else if (asymLo) {
7406 return rrv->getErrorLo();
7407 } else {
7408 return rrv->getError();
7409 }
7410 }
7411
7412 RooArgList *_pars = (pars) ? *pars : nullptr;
7413
7414 if (!_pars) {
7415
7417 f.getParameters(&nset, allParamsInAbsReal);
7418
7419 _pars = new RooArgList;
7420 for (auto *rrvFitRes : static_range_cast<RooRealVar *>(fr.floatParsFinal())) {
7421
7422 auto rrvInAbsReal = static_cast<RooRealVar const *>(allParamsInAbsReal.find(*rrvFitRes));
7423
7424 // Strip out parameters with zero error
7425 if (rrvFitRes->getError() <= std::abs(rrvFitRes->getVal()) * std::numeric_limits<double>::epsilon())
7426 continue;
7427
7428 // Ignore parameters in the fit result that this RooAbsReal doesn't depend on
7429 if (!rrvInAbsReal)
7430 continue;
7431
7432 // Checking for float equality is a bad. We check if the values are
7433 // negligibly far away from each other, relative to the uncertainty.
7434 if (std::abs(rrvInAbsReal->getVal() - rrvFitRes->getVal()) > 0.01 * rrvFitRes->getError()) {
7435 std::stringstream errMsg;
7436 errMsg << "RooAbsReal::getPropagatedError(): the parameters of the RooAbsReal don't have"
7437 << " the same values as in the fit result! The logic of getPropagatedError is broken in this case.";
7438
7439 throw std::runtime_error(errMsg.str());
7440 }
7441
7442 _pars->add(*rrvInAbsReal);
7443 }
7444 }
7445
7446 // Make std::vector of variations
7447 TVectorD F(_pars->size());
7448
7449 // Create std::vector of plus,minus variations for each parameter
7450 TMatrixDSym V(_pars->size() == fr.floatParsFinal().size() ? fr.covarianceMatrix()
7451 : fr.reducedCovarianceMatrix(*_pars));
7452
7453 // TODO: if _pars includes pars not in fr, need to extend matrix with uncorrelated errors of those pars
7454
7455 double nomVal = f.getVal(nset);
7456
7457 for (std::size_t ivar = 0; ivar < _pars->size(); ivar++) {
7458
7459 auto &rrv = static_cast<RooRealVar &>((*_pars)[ivar]);
7460 auto *frrrv = static_cast<RooRealVar *>(fr.floatParsFinal().find(rrv));
7461
7462 double cenVal = rrv.getVal();
7463 double plusVar, minusVar, errVal;
7464
7465 if (asymHi || asymLo) {
7466 errVal = frrrv->getErrorHi();
7467 rrv.setVal(cenVal + errVal);
7468 plusVar = f.getVal(nset);
7469 errVal = frrrv->getErrorLo();
7470 rrv.setVal(cenVal + errVal);
7471 minusVar = f.getVal(nset);
7472 if (asymHi) {
7473 // pick the one that moved result 'up' most
7474 plusVar = std::max(plusVar, minusVar);
7475 minusVar = 2 * nomVal - plusVar; // symmetrizes
7476 } else {
7477 // pick the one that moved result 'down' most
7478 minusVar = std::min(plusVar, minusVar);
7479 plusVar = 2 * nomVal - minusVar; // symmetrizes
7480 }
7481 } else {
7482 errVal = sqrt(V(ivar, ivar));
7483 // Make Plus variation
7484 rrv.setVal(cenVal + errVal);
7485 plusVar = f.getVal(nset);
7486 // Make Minus variation
7487 rrv.setVal(cenVal - errVal);
7488 minusVar = f.getVal(nset);
7489 }
7490 F[ivar] = (plusVar - minusVar) * 0.5;
7491 rrv.setVal(cenVal);
7492 }
7493
7494 // Re-evaluate this RooAbsReal with the central parameters just to be
7495 // extra-safe that a call to `getPropagatedError()` doesn't change any state.
7496 // It should not be necessary because thanks to the dirty flag propagation
7497 // the RooAbsReal is re-evaluated anyway the next time getVal() is called.
7498 // Still there are imaginable corner cases where it would not be triggered,
7499 // for example if the user changes the RooFit operation more after the error
7500 // propagation.
7501 f.getVal(nset);
7502
7503 TMatrixDSym C(_pars->size());
7504 std::vector<double> errVec(_pars->size());
7505 for (std::size_t i = 0; i < _pars->size(); i++) {
7506 errVec[i] = std::sqrt(V(i, i));
7507 for (std::size_t j = i; j < _pars->size(); j++) {
7508 C(i, j) = V(i, j) / std::sqrt(V(i, i) * V(j, j));
7509 C(j, i) = C(i, j);
7510 }
7511 }
7512
7513 // Calculate error in linear approximation from variations and correlation coefficient
7514 double sum = F * (C * F);
7515
7516 if (!pars) {
7517 delete _pars;
7518 } else {
7519 *pars = _pars;
7520 }
7521
7522 return sqrt(sum);
7523}
7524
7525class PdfWrapper : public RooAbsPdf {
7526public:
7527 // need expPdf option while RooProjectedPdf doesn't support keeping things extended
7528 PdfWrapper(RooAbsReal &f, RooAbsReal *coef, bool expEvMode = false, RooAbsPdf *expPdf = nullptr)
7529 : RooAbsPdf(Form("exp_%s", f.GetName())),
7530 fFunc("func", "func", this, f),
7531 fCoef("coef", "coef", this),
7532 fExpPdf("expPdf", "expPdf", this)
7533 {
7534 // don't treat pdf as extended if it has a coefficient and is RooAddPdf: RooAddPdf doesn't extend them unless no
7535 // coefs for any (and all are extendable)
7536 if (coef) {
7537 fCoef.setArg(*coef);
7538 }
7539 if (expPdf && expPdf->canBeExtended() && !(coef && dynamic_cast<RooAddPdf *>(expPdf))) {
7540 fExpPdf.setArg(*expPdf);
7541 } else if (auto _p = dynamic_cast<RooAbsPdf *>(&f);
7542 _p && _p->canBeExtended() && !(coef && dynamic_cast<RooAddPdf *>(_p))) {
7543 fExpPdf.setArg(f); // using self for expectation
7544 }
7545 fExpectedEventsMode = expEvMode;
7546 }
7547 ~PdfWrapper() override{};
7548 PdfWrapper(const PdfWrapper &other, const char *name = nullptr)
7549 : RooAbsPdf(other, name),
7550 fFunc("func", this, other.fFunc),
7551 fCoef("coef", this, other.fCoef),
7552 fExpPdf("expPdf", this, other.fExpPdf),
7553 fExpectedEventsMode(other.fExpectedEventsMode)
7554 {
7555 }
7556 TObject *clone(const char *newname) const override { return new PdfWrapper(*this, newname); }
7557 bool isBinnedDistribution(const RooArgSet &obs) const override { return fFunc->isBinnedDistribution(obs); }
7558 std::list<double> *binBoundaries(RooAbsRealLValue &obs, double xlo, double xhi) const override
7559 {
7560 return fFunc->binBoundaries(obs, xlo, xhi);
7561 }
7562
7563 double evaluate() const override
7564 {
7565 return (fExpectedEventsMode ? 1. : fFunc) *
7566 ((fExpPdf.absArg()) ? static_cast<RooAbsPdf *>(fExpPdf.absArg())->expectedEvents(_normSet) : 1.) *
7567 (fCoef.absArg() ? fCoef : 1.);
7568 }
7569
7570 bool selfNormalized() const override
7571 {
7572 return true;
7573 } // so that doesn't try to do an integral because we are passing integration onto fFunc in evaluate
7574
7575 // faster than full evaluation because doesnt make the integral dependent on the full expression
7577 {
7578#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 28, 00)
7579 double oo = getPropagatedError(fr, nset_in); // method was improved in 6.28 so use this instead
7580 if (std::isnan(oo)) {
7581 // may be consequence of zero uncerts
7582 // Calling getParameters() might be costly, but necessary to get the right
7583 // parameters in the RooAbsReal. The RooFitResult only stores snapshots.
7585 getParameters(&nset_in, allParamsInAbsReal);
7586
7587 RooArgList paramList;
7589
7590 auto rrvInAbsReal = static_cast<RooRealVar const *>(allParamsInAbsReal.find(*rrvFitRes));
7591
7592 // If this RooAbsReal is a RooRealVar in the fit result, we don't need to
7593 // propagate anything and can just return the error in the fit result
7594 if (rrvFitRes->namePtr() == namePtr())
7595 return rrvFitRes->getError();
7596
7597 // Strip out parameters with zero error
7598 if (!rrvFitRes->hasError() ||
7599 rrvFitRes->getError() <= std::abs(rrvFitRes->getVal()) * std::numeric_limits<double>::epsilon())
7600 continue;
7601
7602 // Ignore parameters in the fit result that this RooAbsReal doesn't depend on
7603 if (!rrvInAbsReal)
7604 continue;
7605
7606 // Checking for float equality is a bad. We check if the values are
7607 // negligibly far away from each other, relative to the uncertainty.
7608 if (std::abs(rrvInAbsReal->getVal() - rrvFitRes->getVal()) > 0.01 * rrvFitRes->getError()) {
7609 std::stringstream errMsg;
7610 errMsg
7611 << "RooAbsReal::getPropagatedError(): the parameters of the RooAbsReal don't have"
7612 << " the same values as in the fit result! The logic of getPropagatedError is broken in this case.";
7613
7614 throw std::runtime_error(errMsg.str());
7615 }
7616
7617 paramList.add(*rrvInAbsReal);
7618 }
7619 if (paramList.empty())
7620 return 0.;
7621
7622 std::vector<double> plusVar;
7623 std::vector<double> minusVar;
7624 plusVar.reserve(paramList.size());
7625 minusVar.reserve(paramList.size());
7626
7627 // Create std::vector of plus,minus variations for each parameter
7628 TMatrixDSym V(paramList.size() == fr.floatParsFinal().size() ? fr.covarianceMatrix()
7629 : fr.reducedCovarianceMatrix(paramList));
7630
7631 for (std::size_t ivar = 0; ivar < paramList.size(); ivar++) {
7632
7633 auto &rrv = static_cast<RooRealVar &>(paramList[ivar]);
7634
7635 double cenVal = rrv.getVal();
7636 double errVal = sqrt(V(ivar, ivar));
7637
7638 // this next thing happens if the par has errors but the covariance matrix is empty
7639 // this only happens if the fit was dodgy, so perhaps best to not even try to recover from this
7640 // screwup ... hence I've commented out this fixup here and will let the errors be nan
7641 // if(errVal==0) {
7642 // Warning("getPropagatedError","Missing variance for %s",rrv.GetName());
7643 // errVal = rrv.getError();
7644 // V(ivar,ivar) = errVal*errVal;
7645 // }
7646
7647 // Make Plus variation
7648 rrv.setVal(cenVal + errVal);
7649 plusVar.push_back(getVal(nset_in));
7650
7651 // Make Minus variation
7652 rrv.setVal(cenVal - errVal);
7653 minusVar.push_back(getVal(nset_in));
7654#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
7655 // can try to recover nans ... this stopped being possible in 6.27 onwards because NaNPacker made private
7656 if (std::isnan(plusVar.back()) && RooNaNPacker::isNaNWithPayload(plusVar.back())) {
7657 plusVar.back() = -RooNaNPacker::unpackNaN(plusVar.back());
7658 }
7659 if (std::isnan(minusVar.back()) && RooNaNPacker::isNaNWithPayload(minusVar.back())) {
7660 minusVar.back() = -RooNaNPacker::unpackNaN(minusVar.back());
7661 }
7662#endif
7663 // std::cout << plusVar.back() << " and " << minusVar.back() << std::endl;
7664
7665 rrv.setVal(cenVal);
7666 }
7667
7668 // Re-evaluate this RooAbsReal with the central parameters just to be
7669 // extra-safe that a call to `getPropagatedError()` doesn't change any state.
7670 // It should not be necessary because thanks to the dirty flag propagation
7671 // the RooAbsReal is re-evaluated anyway the next time getVal() is called.
7672 // Still there are imaginable corner cases where it would not be triggered,
7673 // for example if the user changes the RooFit operation more after the error
7674 // propagation.
7675 getVal(nset_in);
7676
7677 TMatrixDSym C(paramList.size());
7678 std::vector<double> errVec(paramList.size());
7679 for (std::size_t i = 0; i < paramList.size(); i++) {
7680 errVec[i] = std::sqrt(V(i, i));
7681 for (std::size_t j = i; j < paramList.size(); j++) {
7682 C(i, j) = V(i, j) / std::sqrt(V(i, i) * V(j, j));
7683 C(j, i) = C(i, j);
7684 }
7685 }
7686
7687 // Make std::vector of variations
7688 TVectorD F(plusVar.size());
7689 for (unsigned int j = 0; j < plusVar.size(); j++) {
7690 F[j] = (plusVar[j] - minusVar[j]) / 2;
7691 }
7692
7693 // Calculate error in linear approximation from variations and correlation coefficient
7694 double sum = F * (C * F);
7695
7696 return sqrt(sum);
7697 }
7698 return oo;
7699#else
7700
7701 // Strip out parameters with zero error
7704 if (frv->getError() > 1e-20) {
7705 fpf_stripped.add(*frv);
7706 }
7707 }
7708
7709 // Clone self for internal use
7710 RooAbsReal *cloneFunc = const_cast<PdfWrapper *>(this); // (RooAbsReal *)fFunc.absArg()->cloneTree();
7711 // RooAbsPdf *clonePdf = dynamic_cast<RooAbsPdf *>(cloneFunc);
7712 RooArgSet *errorParams = cloneFunc->getObservables(fpf_stripped);
7713
7714 RooArgSet *nset =
7715 nset_in.size() == 0 ? cloneFunc->getParameters(*errorParams) : cloneFunc->getObservables(nset_in);
7716
7717 // Make list of parameter instances of cloneFunc in order of error matrix
7718 RooArgList paramList;
7719 const RooArgList &fpf = fpf_stripped;
7720 std::vector<int> fpf_idx;
7721 for (Int_t i = 0; i < fpf.size(); i++) {
7722 RooAbsArg *par = errorParams->find(fpf[i].GetName());
7723 if (par) {
7724 paramList.add(*par);
7725 fpf_idx.push_back(i);
7726 }
7727 }
7728
7729 std::vector<double> plusVar, minusVar;
7730
7731 // Create vector of plus,minus variations for each parameter
7732 TMatrixDSym V(paramList.size() == fr.floatParsFinal().size() ? fr.covarianceMatrix()
7733 : fr.reducedCovarianceMatrix(paramList));
7734
7735 for (Int_t ivar = 0; ivar < paramList.size(); ivar++) {
7736
7738
7739 double cenVal = rrv.getVal();
7740 double errVal = sqrt(V(ivar, ivar));
7741
7742 // Make Plus variation
7743 ((RooRealVar *)paramList.at(ivar))->setVal(cenVal + errVal);
7744 // plusVar.push_back((fExpectedEventsMode ? 1. : cloneFunc->getVal(nset)) *
7745 // (clonePdf ? clonePdf->expectedEvents(nset) : 1.));
7746 plusVar.push_back(cloneFunc->getVal(nset));
7747
7748 // Make Minus variation
7749 ((RooRealVar *)paramList.at(ivar))->setVal(cenVal - errVal);
7750 // minusVar.push_back((fExpectedEventsMode ? 1. : cloneFunc->getVal(nset)) *
7751 // (clonePdf ? clonePdf->expectedEvents(nset) : 1.));
7752 minusVar.push_back(cloneFunc->getVal(nset));
7753
7754 ((RooRealVar *)paramList.at(ivar))->setVal(cenVal);
7755 }
7756
7757 getVal(nset); // reset state
7758
7759 TMatrixDSym C(paramList.size());
7760 std::vector<double> errVec(paramList.size());
7761 for (int i = 0; i < paramList.size(); i++) {
7762 errVec[i] = sqrt(V(i, i));
7763 for (int j = i; j < paramList.size(); j++) {
7764 C(i, j) = V(i, j) / sqrt(V(i, i) * V(j, j));
7765 C(j, i) = C(i, j);
7766 }
7767 }
7768
7769 // Make vector of variations
7770 TVectorD F(plusVar.size());
7771 for (unsigned int j = 0; j < plusVar.size(); j++) {
7772 F[j] = (plusVar[j] - minusVar[j]) / 2;
7773 }
7774
7775 // Calculate error in linear approximation from variations and correlation coefficient
7776 double sum = F * (C * F);
7777
7778 // delete cloneFunc;
7779 delete errorParams;
7780 delete nset;
7781
7782 return sqrt(sum);
7783#endif
7784 }
7785
7786private:
7790 bool fExpectedEventsMode = false;
7791};
7792
7793const xRooNode *runningNode = nullptr;
7795
7797{
7798 std::cout << "Got signal " << signum << std::endl;
7799 if (signum == SIGINT) {
7800 std::cout << "Keyboard interrupt while building histogram" << std::endl;
7801 // TODO: create a global mutex for this
7802 runningNode->fInterrupted = true;
7803 } else {
7805 }
7806}
7807
7809{
7810 auto _doSterilize = [](RooAbsArg *obj) {
7811 if (!obj)
7812 return;
7813 for (int i = 0; i < obj->numCaches(); i++) {
7814 if (auto cache = dynamic_cast<RooObjCacheManager *>(obj->getCache(i))) {
7815 cache->reset();
7816 }
7817 }
7818 if (RooAbsPdf *p = dynamic_cast<RooAbsPdf *>(obj); p) {
7819 p->setNormRange(p->normRange());
7820 }
7821#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
7822 if (RooAbsReal *p = dynamic_cast<RooAbsReal *>(obj); p) {
7823 // need to forget about any normSet that was passed to getVal(...)
7824 // doesn't seem necessary in 6.28
7825
7826 p->setProxyNormSet(nullptr);
7827 p->_lastNSet = nullptr;
7828 }
7829#endif
7830 obj->setValueDirty();
7831 };
7832 if (auto w = get<RooWorkspace>(); w) {
7833 // sterilizing all nodes
7834 for (auto &c : w->components()) {
7835 _doSterilize(c);
7836 }
7837 return;
7838 }
7839 // recursive through all clients and sterlize their normalization caches
7840 std::function<void(RooAbsArg *)> func;
7841 func = [&](RooAbsArg *a) {
7842 if (!a) {
7843 return;
7844 }
7845 _doSterilize(a); // sterilize first so that cache elements don't appear in the client list
7846 // safety net in case sterilizing one client deletes another one of our clients
7847 // monitor for change in clients list size
7848 // found this was only case in 6.26 (valgrind shows invalid read), in 6.28 these went away
7849 // might be in 6.28 the client list iterator became able to handle in-loop edits but didn't see
7850 // in test case that client count changed so just resterilizing if that's the case.
7851 size_t nClients;
7852 do {
7853 nClients = a->clients().size();
7854 for (auto obj : a->clients()) {
7855 func(dynamic_cast<RooAbsArg *>(obj));
7856 if (a->clients().size() != nClients) {
7857 break; // means sterilizing a client changed our clients, so don't trust the client iterator at this
7858 // point
7859 }
7860 }
7861 } while (a->clients().size() != nClients);
7862 };
7863 func(get<RooAbsArg>());
7864}
7865
7866// observables not in the axisVars are automatically projected over
7867xRooNode xRooNode::histo(const xRooNode &vars, const xRooNode &fr, bool content, bool errors, bool stack, bool errorsHi,
7868 bool errorsLo, int nErrorToys) const
7869{
7870
7871 if (!vars.fComp && strlen(vars.GetName())) {
7873 }
7874
7875 xRooNode out(TString::Format("%s.histo", GetName()), nullptr, *this);
7876
7877 RooAbsLValue *v = nullptr;
7878 if (vars.empty()) {
7879 // does an integral
7880 out.fComp = std::shared_ptr<TH1>(
7881 BuildHistogram(nullptr, !content, errors, -1, -1, fr, errorsHi, errorsLo, nErrorToys, nullptr, !stack, false));
7882 } else if (vars.size() == 1) {
7883 v = vars.at(0)->get<RooAbsLValue>();
7884 out.fComp = std::shared_ptr<TH1>(
7885 BuildHistogram(v, !content, errors, 1, 0, fr, errorsHi, errorsLo, nErrorToys, nullptr, !stack, true));
7886 } else {
7887 throw std::runtime_error("multi-dim histo not yet supported");
7888 }
7889
7890 return out;
7891}
7892
7894{
7895 return xRooNode(fComp, xRooNode(range.GetName(), nullptr, *this));
7896}
7897
7899 bool errorsHi, bool errorsLo, int nErrorToys, TH1 *templateHist, bool nostack,
7900 bool setInterp) const
7901{
7902 auto rar = get<RooAbsReal>();
7903 if (!rar)
7904 return nullptr;
7905
7906 TObject *vv = rar;
7907
7908 auto t = TH1::AddDirectoryStatus();
7909 TH1::AddDirectory(false);
7910 TH1 *h = nullptr;
7911 if (!v) {
7912 if (binStart != -1 || binEnd != -1) { // allow v to stay nullptr if doing integral (binStart=binEnd=-1)
7913 if (auto _ax = GetXaxis())
7914 v = dynamic_cast<RooAbsLValue *>(_ax->GetParent());
7915 } else {
7916 // don't need to integrate if doing a self-histogram
7917 v = dynamic_cast<RooRealVar *>(rar);
7918 }
7919 if (v) {
7920 vv = dynamic_cast<TObject *>(v);
7921 } else {
7922 // make a single-bin histogram of just this value
7923 h = new TH1D(rar->GetName(), rar->GetTitle(), 1, 0, 1);
7924 h->GetXaxis()->SetBinLabel(1, rar->GetName());
7925 h->GetXaxis()->SetTimeFormat(rar->GetName());
7926 }
7927 }
7928
7929 auto x = dynamic_cast<RooRealVar *>(v);
7930 bool setTitle = false;
7931 if (templateHist) {
7932 // using template hist for the binning
7933 h = static_cast<TH1 *>(templateHist->Clone(rar->GetName()));
7934 if (h->GetListOfFunctions())
7936 h->SetDirectory(0);
7937 h->SetTitle(rar->GetTitle());
7938 h->Reset();
7939 } else if (x) {
7940 if (x == rar) {
7941 // self histogram ...
7942 h = new TH1D(rar->GetName(), rar->GetTitle(), 1, 0, 1);
7943 h->Sumw2();
7944 h->GetXaxis()->SetBinLabel(1, rar->GetName());
7945 h->SetBinContent(1, rar->getVal());
7946 if (x->getError()) {
7947 h->SetBinError(1, x->getError());
7948 h->SetFillStyle(3005);
7949 h->SetFillColor(h->GetLineColor());
7950 }
7951 h->SetMaximum(x->hasMax() ? x->getMax()
7952 : (h->GetBinContent(1) + std::max(std::abs(h->GetBinContent(1) * 0.1), 50.)));
7953 h->SetMinimum(x->hasMin() ? x->getMin()
7954 : (h->GetBinContent(1) - std::max(std::abs(h->GetBinContent(1) * 0.1), 50.)));
7955 h->GetXaxis()->SetName(dynamic_cast<TObject *>(v)->GetName());
7956 h->SetOption("e2");
7957 h->SetMarkerSize(0);
7958 h->SetMarkerStyle(0);
7959
7960 return h;
7961 }
7962 auto _ax = GetXaxis();
7963 TString binningName = (_ax && _ax->GetParent() == x) ? _ax->GetName() : rar->getStringAttribute("binning");
7964 if (binningName == "")
7965 binningName = rar->GetName();
7966 if (x->hasBinning(binningName)) {
7967 if (x->getBinning(binningName).isUniform()) {
7968 h = new TH1D(rar->GetName(), rar->GetTitle(), x->numBins(binningName) <= 0 ? 100 : x->numBins(binningName),
7969 x->getMin(binningName), x->getMax(binningName));
7970 } else {
7971 h = new TH1D(rar->GetName(), rar->GetTitle(), x->numBins(binningName), x->getBinning(binningName).array());
7972 }
7973 h->GetXaxis()->SetTitle(x->getBinning(binningName).GetTitle());
7974 setTitle = true;
7975 } else if (auto _boundaries =
7976 _or_func(/*rar->plotSamplingHint(*x,x->getMin(),x->getMax())*/ (std::list<double> *)(nullptr),
7977 rar->binBoundaries(*x, -std::numeric_limits<double>::infinity(),
7978 std::numeric_limits<double>::infinity()));
7979 _boundaries) {
7980 std::vector<double> _bins;
7981 for (auto &b : *_boundaries) {
7982 if (_bins.empty() || std::abs(_bins.back() - b) > 1e-5 * _bins.back())
7983 _bins.push_back(b);
7984 } // found sometimes get virtual duplicates in the binning
7985 h = new TH1D(rar->GetName(), rar->GetTitle(), _bins.size() - 1, &_bins[0]);
7986 delete _boundaries;
7987 } else if (!x->hasMax() || !x->hasMin()) {
7988 // use current value of x to estimate range with
7989 h = new TH1D(rar->GetName(), rar->GetTitle(), v->numBins(), x->getVal() * 0.2, x->getVal() * 5);
7990 } else {
7991 h = new TH1D(rar->GetName(), rar->GetTitle(), v->numBins(), x->getBinning().array());
7992 }
7993 h->Sumw2();
7994 } else if (!h) {
7995 h = new TH1D(rar->GetName(), rar->GetTitle(), v->numBins(rar->GetName()), 0, v->numBins(rar->GetName()));
7996 if (auto cat = dynamic_cast<RooAbsCategoryLValue *>(v)) {
7997 int i = 1;
7998 std::map<int, std::string> cats; // fill into a map to preserve index ordering
7999 for (auto &c : *cat) {
8000 cats[c.second] = c.first;
8001 }
8002 for (auto &[_, label] : cats) {
8003 h->GetXaxis()->SetBinLabel(i++, label.c_str());
8004 }
8005 }
8006 h->Sumw2();
8007 }
8008 if (auto o = dynamic_cast<TObject *>(v); o && !setTitle) {
8009 h->GetXaxis()->SetTitle(o->GetTitle());
8010 }
8012 if (v) {
8013 if (h->GetXaxis()->IsAlphanumeric()) {
8014 // store the variable name in the TimeFormat property as well, b.c. alphanumeric requires axis name to be
8015 // "xaxis"
8016 h->GetXaxis()->SetTimeFormat(dynamic_cast<TObject *>(v)->GetName());
8017 } else {
8018 h->GetXaxis()->SetName(dynamic_cast<TObject *>(v)->GetName()); // WARNING: messes up display of bin labels
8019 }
8020 }
8021
8022 if (auto s = styles(nullptr, false); s) {
8023 auto _style = s.get<TStyle>();
8024 static_cast<TAttLine &>(*h) = *_style;
8025 static_cast<TAttFill &>(*h) = *_style;
8026 static_cast<TAttMarker &>(*h) = *_style;
8027 }
8028 if (strlen(h->GetXaxis()->GetTitle()) == 0)
8029 h->GetXaxis()->SetTitle(vv->GetTitle());
8030 auto p = dynamic_cast<RooAbsPdf *>(rar);
8031
8032 // possible speed improvement:
8033 // if(auto spdf = dynamic_cast<RooRealSumPdf*>(p); spdf && spdf->canBeExtended()) {
8034 // p = nullptr; // faster to evaluate sumpdf as a function not a pdf
8035 // }
8036
8037 if (empty && !errors) {
8038 return h;
8039 }
8040
8041 // if (!empty) {
8042
8043 auto _coefs = coefs();
8044
8045 RooFitResult *fr = nullptr;
8046 if (errors) {
8047 // must ensure the fit result we obtain includes pars from coefficients if present
8048 if (_fr.get<RooFitResult>()) {
8049 fr = static_cast<RooFitResult *>(_fr.get<RooFitResult>()->Clone());
8050 } else {
8051 auto frn =
8052 (!_coefs.get() ? *this : xRooNode(RooProduct("tmp", "tmp", RooArgList(*rar, *_coefs.get<RooAbsReal>()))))
8053 .fitResult();
8054 if (strlen(_fr.GetName()))
8055 frn = frn.reduced(_fr.GetName());
8056 fr = dynamic_cast<RooFitResult *>(frn->Clone());
8057 }
8058 if (!GETDMP(fr, _finalPars)) {
8060 }
8061
8062 /// Oct2022: No longer doing this because want to allow fitResult to be used to get partial error
8063 // // need to add any floating parameters not included somewhere already in the fit result ...
8064 // RooArgList l;
8065 // for(auto& p : pars()) {
8066 // auto vv = p->get<RooRealVar>();
8067 // if (!vv) continue;
8068 // if (vv == dynamic_cast<RooRealVar*>(v)) continue;
8069 // if (vv->isConstant()) continue;
8070 // if (fr->floatParsFinal().find(vv->GetName())) continue;
8071 // if (fr->_constPars && fr->_constPars->find(vv->GetName())) continue;
8072 // l.add(*vv);
8073 // }
8074 //
8075 // if (!l.empty()) {
8076 // RooArgList l2; l2.addClone(fr->floatParsFinal());
8077 // l2.addClone(l);
8078 // fr->setFinalParList(l2);
8079 // }
8080
8081 TMatrixTSym<double> *prevCov = static_cast<TMatrixTSym<double> *>(GETDMP(fr, _VM));
8082
8083 if (!prevCov || size_t(fr->covarianceMatrix().GetNcols()) < fr->floatParsFinal().size()) {
8085 if (prevCov) {
8086 for (int i = 0; i < prevCov->GetNcols(); i++) {
8087 for (int j = 0; j < prevCov->GetNrows(); j++) {
8088 cov(i, j) = (*prevCov)(i, j);
8089 }
8090 }
8091 }
8092 int i = 0;
8093 for (auto &p2 : fr->floatParsFinal()) {
8094 if (!prevCov || i >= prevCov->GetNcols()) {
8095 cov(i, i) = pow(dynamic_cast<RooRealVar *>(p2)->getError(), 2);
8096 }
8097 i++;
8098 }
8099 int covQualBackup = fr->covQual();
8102 }
8103
8104 if (v) {
8105 // need to remove v from result as we are plotting as function of v
8106 if (auto _p = fr->floatParsFinal().find(dynamic_cast<TObject *>(v)->GetName()); _p) {
8108 _pars.remove(*_p, true);
8110 int covQualBackup = fr->covQual();
8113 const_cast<RooArgList &>(fr->floatParsFinal())
8114 .remove(*_p, true); // NOTE: I think this might be a memory leak, should delete _p after removal
8115 }
8116 }
8117 // finally check at least one float has errors defined (might not be cause if in prefit state)
8118 bool hasErrors = false;
8119 for (auto pp : fr->floatParsFinal()) {
8120 if (dynamic_cast<RooRealVar *>(pp)->hasError()) {
8121 hasErrors = true;
8122 break;
8123 }
8124 }
8125 if (!hasErrors) {
8126 errors = false;
8127 delete fr;
8128 }
8129 }
8130
8132 if (v)
8133 normSet.add(*dynamic_cast<RooAbsArg *>(v));
8134
8135 if (binEnd == 0)
8136 binEnd = h->GetNbinsX();
8137
8138 bool needBinWidth = false;
8139 // may have MULTIPLE coefficients for the same pdf!
8140
8141 if (x && (p || _coefs.get() || rar->getAttribute("density"))) {
8142 // pdfs of samples embedded in a sumpdf (aka have a coef) will convert their density value to a content
8143 needBinWidth = true;
8144 }
8145
8146 if (auto spdf = dynamic_cast<RooRealSumPdf *>(p);
8147 spdf && spdf->canBeExtended() && !spdf->getFloor() && !_coefs.get()) {
8148 p = nullptr; // if pdf has no floor, will evaluate it as a function to allow it to be negative - evaluation should
8149 // also be faster (no integral)
8150 // exception is if RooRealSumPdf is embedded in a RooAddPdf (detected by presence of coefs) ... then it must be
8151 // evaluated as a pdf technically should check parent is a RooAddPdf, because if was inside a RooRealSumPdf then
8152 // would be evaluated as a function!
8153 }
8154
8155 // check if we need to do any projecting of other observables
8156 RooAbsReal *oldrar = nullptr;
8157 auto _obs = obs();
8158
8159 for (auto o : _obs) {
8160 if (auto rr = o->get<RooRealVar>(); rr && rr->hasRange("coordRange")) {
8161 rr->removeRange("coordRange"); // doesn't actually remove, just sets to -inf->+inf
8162 rr->setStringAttribute("coordRange", nullptr); // removes the attribute
8163 }
8164 }
8165 // probably should also remove any range on the x-axis variable too, if there is one
8166 if (auto rr = dynamic_cast<RooRealVar *>(v); rr && rr->hasRange("coordRange")) {
8167 rr->removeRange("coordRange"); // doesn't actually remove, just sets to -inf->+inf
8168 rr->setStringAttribute("coordRange", nullptr); // removes the attribute
8169 }
8170 coords(); // loads current coordinates and populates coordRange, if any
8171
8172 if (auto a = dynamic_cast<RooAbsArg *>(v))
8173 _obs.get<RooArgList>()->remove(*a);
8174 if (!_obs.get<RooArgList>()->empty()) {
8175 oldrar = rar;
8176 normSet.add(*_obs.get<RooArgList>());
8177 // check if any obs are restricted range
8178 bool hasRange = false;
8179 for (auto o : normSet) {
8180 if (auto rr = dynamic_cast<RooRealVar *>(o);
8181 rr && (rr->getStringAttribute("coordRange")) && strlen(rr->getStringAttribute("coordRange"))) {
8182 hasRange = true;
8183 break;
8184 }
8185 }
8186 if (p) {
8187 // need to handle special case of RooSimultaneous ... each pdf needs individually projecting over just its
8188 // dependent obs
8189 if (auto s = dynamic_cast<RooSimultaneous *>(p)) {
8190 auto newrar = new RooSimultaneous("projSim", "projSim", const_cast<RooAbsCategoryLValue &>(s->indexCat()));
8191 for (auto pdf : bins()) {
8192 // auto _pdf =
8193 // pdf->get<RooAbsPdf>()->createProjection(*pdf->get<RooAbsPdf>()->getObservables(*_obs.get<RooArgList>()));
8194 auto _pdf =
8195 new xRooProjectedPdf(TString::Format("%s_projection", pdf->GetName()), "", *pdf->get<RooAbsPdf>(),
8196 *pdf->get<RooAbsPdf>()->getObservables(*_obs.get<RooArgList>()));
8197 if (hasRange) {
8198 dynamic_cast<RooAbsPdf *>(_pdf)->setNormRange("coordRange");
8199 }
8200 newrar->addPdf(*_pdf, pdf->coords()[s->indexCat().GetName()]->get<RooCategory>()->getLabel());
8201 }
8202 rar = newrar;
8203 } else {
8204 rar = p->createProjection(
8205 *_obs.get<RooArgList>()); // TODO should use xRooProjectedPdf here too, because not fixed range and
8206 // extend behaviour of RooProjectedPdf in ROOT yet
8207 if (hasRange) {
8208 dynamic_cast<RooAbsPdf *>(rar)->setNormRange("coordRange");
8209 }
8210 }
8211 if (hasRange)
8212 p->setNormRange("coordRange"); // should get cleared when we sterilize
8213 } else {
8214 if (hasRange) {
8215 // commented out passing of normset so that getVal of non-pdf is always a 'raw' value (needed for raw eval
8216 // of RooRealSumPdf)
8217 rar = std::unique_ptr<RooAbsReal>{rar->createIntegral(
8218 *_obs.get<RooArgList>(),
8219 /*RooFit::NormSet(normSet),*/ RooFit::Range("coordRange"))}
8220 .release();
8221 } else {
8222 rar =
8223 std::unique_ptr<RooAbsReal>{rar->createIntegral(*_obs.get<RooArgList>() /*, RooFit::NormSet(normSet)*/)}
8224 .release();
8225 }
8226 }
8227 }
8228
8229 bool scaleExpected = (p && p->canBeExtended() && !_coefs.get());
8230 // Note about above: if pdf has coefficients then its embedded in a RooAddPdf that has coefs defined ...
8231 // in this case we should *not* scale by expected, since the coefs become the scaling instead
8232 // we should also not build a stack for this (may be a RooRealSumPdf inside a RooAddPdf, but the
8233 // samples of the RooRealSumPdf wont be correctly scaled to line up with overall RooRealSumPdf
8234 // which will be normalized to its coefficient
8235 if (!nostack && p && p->canBeExtended() && _coefs.get()) {
8236 nostack = true;
8237 // if wanted to still hve a stack, would need to scale the stack subcomponents by
8238 // coefs-value / p_integral(raw) ... since raw p-integral will be what stack integrates to
8239 }
8240
8241 std::unique_ptr<RooArgSet> snap(normSet.snapshot());
8243 std::vector<double> lapTimes;
8244 bool warned = false;
8245 if (binStart == -1 && binEnd == -1) {
8246 binEnd = 1;
8247 }
8248 auto cat = (!x) ? dynamic_cast<RooAbsCategoryLValue *>(v) : nullptr;
8249 RooArgList *errorPars = nullptr;
8250 std::unique_ptr<RooAbsCollection> errorParsSnap;
8251
8252 if (!v) {
8253 setInterp = false;
8254 }
8255
8256 if (setInterp) {
8257 RooAbsArg *vvv = dynamic_cast<RooAbsArg *>(v);
8258 // determining if histogram should have interpolation drawing options set on it
8259 // need to strip namespace to discount the "HistFactory" namespace classes from all being treated as binned
8260 TString clNameNoNamespace = rar->ClassName();
8262 setInterp = (clNameNoNamespace.Contains("Hist") || vvv->isCategory() || rar->isBinnedDistribution(*vvv) ||
8263 h->GetNbinsX() == 1 || rar->getAttribute("BinnedLikelihood") ||
8264 (dynamic_cast<RooAbsRealLValue *>(vvv) &&
8265 std::unique_ptr<std::list<double>>(rar->binBoundaries(*dynamic_cast<RooAbsRealLValue *>(vvv),
8266 -std::numeric_limits<double>::infinity(),
8267 std::numeric_limits<double>::infinity()))))
8268 ? false
8269 : true;
8270 if (auto d = dynamic_cast<RooHistFunc *>(rar); d && !d->isBinnedDistribution(*vvv) && h->GetNbinsX() != 1) {
8271 setInterp = true; // hist func is interpolated, so draw it as such
8272 }
8273 if (setInterp && !components().empty()) {
8274 // check if all components of dOpt are "Hist" type (CMS model support)
8275 // if so then don't interp;
8276 bool allHist = true;
8277 for (auto &s : components()) {
8278 TString _clName = s->get()->ClassName();
8279 _clName = _clName(_clName.Last(':') + 1, _clName.Length());
8280 if (!(s->get() && _clName.Contains("Hist"))) {
8281 allHist = false;
8282 break;
8283 }
8284 }
8285 if (allHist)
8286 setInterp = false;
8287 }
8288 if (setInterp) {
8289 h->SetOption("l"); // does linear interpolation between points
8290 }
8291 }
8292
8293 if (errors) {
8294 // may be computing potentially asymmetric errors
8295 // the main histogram will be the error band, and the nominal histogram will be added as a function
8296 // so that it is drawn over the top of the error band
8297 // note that this means GetBinContent on returned histogram will return midpoint of the up and down error
8298 auto l = static_cast<TH1 *>(h->Clone("nominal"));
8299 l->SetDirectory(0);
8300 l->SetFillStyle(0);
8301 h->GetListOfFunctions()->Add(l, (setInterp) ? "lsame" : "histsame");
8302 h->SetOption(setInterp ? "e3" : "e2"); // default draw option E2 or E3 so error band shown .. could have used
8303 // 'EX0' to draw "classic style"
8304 // could take this from the 'band' style object if we create one in future?
8305 h->SetMarkerSize(0);
8306 h->SetFillStyle(3005);
8307 h->SetFillColor(h->GetLineColor());
8308 }
8309
8310 if (nErrorToys > 0) {
8311 errors = false; // wont evaluate error on each toy, will estimate for std.dev or normiles of toys
8312 // need list of errorPars
8313 auto allPars =
8314 (!_coefs.get() ? *this : xRooNode(RooProduct("tmp", "tmp", RooArgList(*rar, *_coefs.get<RooAbsReal>()))))
8315 .pars();
8316 errorPars = new RooArgList; // will be in same order as appear in fr.
8317 for (auto a : fr->floatParsFinal()) {
8318 if (auto par = allPars.get<RooArgList>()->find(*a)) {
8319 errorPars->add(*par);
8320 }
8321 }
8322 errorParsSnap.reset(errorPars->snapshot());
8323 auto l = static_cast<TH1 *>(h->Clone("toys"));
8324 l->Reset(); // removes any functions
8325 l->SetDirectory(0);
8326 h->GetListOfFunctions()->Add(
8327 l, "histsame"); // ensures just this empty hist will be drawn, and not each individual toy
8328
8329 if (errorsLo || errorsHi)
8330 empty = false; // must not be empty b.c. calculation of error relies on knowing nominal (see after loop)
8331 }
8332
8333 for (int toy = 0; toy < (nErrorToys + 1); toy++) {
8334
8335 TH1 *main_h = h;
8336 if (toy > 0) {
8337 h = static_cast<TH1 *>(main_h->Clone(TString::Format("toy_%d", toy)));
8338 h->SetDirectory(0);
8339 h->Reset();
8340 static_cast<TH1 *>(main_h->GetListOfFunctions()->FindObject("toys"))->GetListOfFunctions()->Add(h);
8341 // randomize the parameter values according to the fr's covariance matrix
8342 errorPars->assignValueOnly(fr->randomizePars());
8343 }
8344
8345 for (int i = std::max(1, binStart); i <= std::min(h->GetNbinsX(), binEnd); i++) {
8346 timeIt.Start(true);
8347 if (x) {
8348 x->setVal(h->GetBinCenter(i));
8349 } else if (cat) {
8350 cat->setLabel(h->GetXaxis()->GetBinLabel(i)); // because order might not match "binning" order
8351 } else if (v) {
8352 v->setBin(i - 1);
8353 }
8354 if (x && !x->inRange("coordRange"))
8355 continue;
8356
8357 double r = 0;
8358 if (!empty || toy > 0) {
8359 r = /*(p && p->selfNormalized())*/ rar->getVal(p ? &normSet : nullptr);
8360#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
8361 if (std::isnan(r) && RooNaNPacker::isNaNWithPayload(r)) {
8363 }
8364#endif
8365 if (r && _coefs.get()) {
8366 r *= _coefs.get<RooAbsReal>()->getVal(normSet);
8367 }
8368 if (needBinWidth) {
8369 r *= h->GetBinWidth(i);
8370 }
8371 if (scaleExpected) {
8372 // std::cout << r << " exp = " << p->expectedEvents(normSet) << " for normRange " << (p->normRange() ?
8373 // p->normRange() : "null") << std::endl; p->Print();rar->Print();
8374 r *= (p->expectedEvents(normSet));
8375 } // do in here in case dependency on var
8376 }
8377 h->SetBinContent(i, r);
8378
8379 if (errors) {
8380 static_cast<TH1 *>(h->FindObject("nominal"))->SetBinContent(i, r); // transfer nominal to nominal hist
8381 double res;
8382 bool doAsym = (errorsHi && errorsLo);
8383 if (doAsym) {
8384 errorsHi = false;
8385 }
8386 if (p) {
8387 // std::cout << "computing error of :" << h->GetBinCenter(i) << std::endl;
8388 // //fr->floatParsFinal().Print(); fr->covarianceMatrix().Print();
8389 // res = PdfWrapper((oldrar) ? *rar : *p, _coefs.get<RooAbsReal>(), !v, oldrar ? p : nullptr)
8390 // .getSimplePropagatedError(*fr, normSet);
8392 PdfWrapper((oldrar) ? *rar : *p, _coefs.get<RooAbsReal>(), !v, oldrar ? p : nullptr), *fr, normSet,
8394#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
8395 // improved normSet invalidity checking, so assuming no longer need this in 6.28 onwards
8396 p->_normSet = nullptr;
8397#endif
8398 } else {
8399 // res = RooProduct("errorEval", "errorEval",
8400 // RooArgList(*rar, !_coefs.get() ? RooFit::RooConst(1) :
8401 // *_coefs.get<RooAbsReal>()))
8402 // .getPropagatedError(
8403 // *fr /*, normSet*/); // should be no need to pass a normSet to a non-pdf (but
8404 // not verified this)
8406 RooProduct("errorEval", "errorEval",
8407 RooArgList(*rar, !_coefs.get() ? RooFit::RooConst(1) : *_coefs.get<RooAbsReal>())),
8408 *fr, {}, &errorPars, errorsHi,
8409 errorsLo); // should be no need to pass a normSet to a non-pdf (but not verified this)
8410 // especially important not to pass in the case we are evaluated RooRealSumPdf as a function! otherwise
8411 // error will be wrong
8412 }
8413 if (needBinWidth) {
8414 res *= h->GetBinWidth(i);
8415 }
8416 h->SetBinError(i, res);
8417 if (doAsym) {
8418 // compute Hi error
8419 errorsHi = true;
8420 errorsLo = false;
8421 if (p) {
8423 PdfWrapper((oldrar) ? *rar : *p, _coefs.get<RooAbsReal>(), !v, oldrar ? p : nullptr), *fr, normSet,
8425 } else {
8427 RooProduct("errorEval", "errorEval",
8428 RooArgList(*rar, !_coefs.get() ? RooFit::RooConst(1) : *_coefs.get<RooAbsReal>())),
8429 *fr, {}, &errorPars, errorsHi, errorsLo);
8430 }
8431 if (needBinWidth) {
8432 res *= h->GetBinWidth(i);
8433 }
8434 errorsLo = true;
8435 // lowVal = content - error, highVal = content + res
8436 // => band/2 = (res+error)/2 and band-mid = (2*content+res-error)/2
8437 h->SetBinContent(i, h->GetBinContent(i) + (res - h->GetBinError(i)) * 0.5);
8438 h->SetBinError(i, (res + h->GetBinError(i)) * 0.5);
8439 }
8440 }
8441 timeIt.Stop();
8442 lapTimes.push_back(timeIt.RealTime());
8443 double time_estimate =
8444 (lapTimes.size() > 1)
8445 ? (h->GetNbinsX() * (std::accumulate(lapTimes.begin() + 1, lapTimes.end(), 0.) / (lapTimes.size() - 1)))
8446 : 0.;
8447 if (!warned && (lapTimes.at(0) > 10 || (lapTimes.size() > 2 && time_estimate > 60.))) {
8448 TTimeStamp t2;
8449 t2.Add(time_estimate);
8450 Warning("BuildHistogram", "Building this histogram will take until %s", t2.AsString());
8451 if (errors) {
8452 // install interrupt handler
8453 runningNode = this;
8455 }
8456 warned = true;
8457 }
8458 if (fInterrupted) {
8459 if (errors) {
8460 Warning("BuildHistogram", "Skipping errors for remaining bins");
8461 errors = false;
8462 }
8463 fInterrupted = false;
8464 }
8465 }
8466 if (toy > 0) {
8467 h = main_h;
8468 }
8469 }
8470 if (gOldHandlerr) {
8472 gOldHandlerr = nullptr;
8473 }
8474 normSet = *snap;
8475
8476 if (errorPars) {
8477 if (errorParsSnap)
8479 delete errorPars;
8480 }
8481 if (nErrorToys) {
8482 // compute main histogram error bar from toys
8483 // if not doing asymmetric, then will display std.dev
8484 // otherwise will copy main to nominal and make main error bar s.t. it shows +/-1sigma vals
8485 if (errorsLo && errorsHi) {
8486 auto nomHist = static_cast<TH1 *>(h->FindObject("nominal"));
8487 nomHist->Add(h);
8488 }
8489 for (int i = 1; i <= h->GetNbinsX(); i++) {
8490 std::vector<double> vals;
8491 vals.reserve(nErrorToys);
8492 for (int j = 1; j < (nErrorToys + 1); j++) {
8493 vals.push_back(
8494 static_cast<TH1 *>(h->FindObject("toys")->FindObject(TString::Format("toy_%d", j)))->GetBinContent(i));
8495 }
8496 double upVal, downVal;
8497 if (errorsLo || errorsHi) {
8498 std::sort(vals.begin(), vals.end());
8499 upVal = vals.at(std::round(vals.size() * ROOT::Math::gaussian_cdf(1)));
8500 downVal = vals.at(std::round(vals.size() * ROOT::Math::gaussian_cdf(-1)));
8501 if (!errorsLo)
8502 downVal = 2. * h->GetBinContent(i) - upVal;
8503 if (!errorsHi)
8504 upVal = 2. * h->GetBinContent(i) - downVal;
8505 } else {
8506 double err = TMath::StdDev(vals.begin(), vals.end());
8507 upVal = h->GetBinContent(i) + err;
8508 downVal = h->GetBinContent(i) - err;
8509 }
8510 h->SetBinContent(i, (upVal + downVal) * 0.5);
8511 h->SetBinError(i, (upVal - downVal) * 0.5);
8512 }
8513 }
8514
8515 if (oldrar) {
8516 std::vector<RooAbsArg *> extra;
8517 if (auto s = dynamic_cast<RooSimultaneous *>(rar)) {
8518 // need to delete all the subpdfs we created too
8519 for (auto _pdf : s->servers()) {
8520 if (dynamic_cast<RooAbsPdf *>(_pdf))
8521 extra.push_back(_pdf);
8522 }
8523 }
8524 extra.push_back(rar);
8525 rar = oldrar;
8526 xRooNode(*rar).sterilize(); // need to clear the cache of the created integral - do this before deleting things!
8527 for (auto a : extra)
8528 delete a;
8529 } else {
8530 sterilize(); // needed to forget about the normSet that was passed to getVal()
8531 }
8532
8533 if (!p && !rar->getAttribute("density") && !needBinWidth) {
8534 h->GetYaxis()->SetTitle(rar->getStringAttribute("units"));
8535 } else if ((p && p->canBeExtended()) || (!p && needBinWidth)) {
8536 h->GetYaxis()->SetTitle("Events");
8537 } else {
8538 h->GetYaxis()->SetTitle("Probability Mass");
8539 }
8540 h->GetYaxis()->SetMaxDigits(3);
8541
8542 if (errors) {
8543 delete fr;
8544 }
8545
8546 // build a stack unless not requested
8547 if (!nostack) {
8548 // need to draw copy of hist so shown over the stack
8549 auto hCopy = static_cast<TH1 *>(h->Clone("copy"));
8550 hCopy->Reset();
8551 hCopy->Add(h); // use Reset and Add to clear the function list (dont clear directly as may double-delete if same
8552 // object added twice)
8553 hCopy->SetStats(false);
8554 h->GetListOfFunctions()->Add(hCopy, TString(h->GetOption()) + "same");
8555 h->GetListOfFunctions()->Add(hCopy, "axissame"); // prevents stack covering axis
8556 TString dOpt = (setInterp) ? "LF2" : ""; // should become lf2 if interpolation of histogram is appropriate
8557
8558 const xRooNode *rarNode = this;
8559 RooAbsReal *sf = nullptr;
8560 if (get()->InheritsFrom("RooExtendPdf")) {
8561 const_cast<xRooNode *>(this)->browse();
8562 rarNode = find(".pdf").get();
8563 // rar = rarNode->get<RooAbsReal>();
8564 sf = find(".n")->get<RooAbsReal>();
8565 }
8566
8567 THStack *stack = new THStack("stack", TString::Format("%s;%s", rar->GetTitle(), h->GetXaxis()->GetTitle()));
8568 int count = 2;
8569 std::map<std::string, int> colorByTitle; // TODO: should fill from any existing legend
8570 std::set<std::string> allTitles;
8571 bool titleMatchName = true;
8572 std::map<std::string, TH1 *> histGroups;
8573 std::vector<TH1 *> hhs;
8574 std::set<TH1 *> histsWithBadTitles; // these histograms will have their titles autoFormatted
8575
8576 // support for CMS model case where has single component containing many coeffs
8577 // will build stack by setting each coeff equal to 0 in turn, rebuilding the histogram
8578 // the difference from the "full" histogram will be the component
8580 if (!rarNode->components().empty()) {
8581 auto comps = rarNode->components()[0];
8582 for (auto &c : *comps) {
8583 if (c->fFolder == "!.coeffs")
8584 cms_coefs.add(*c->get<RooAbsArg>());
8585 }
8586 }
8587 if (!cms_coefs.empty()) {
8588 RooRealVar zero("zero", "", 0);
8589 std::shared_ptr<TH1> prevHist(static_cast<TH1 *>(h->Clone()));
8590 prevHist->Reset();
8591 prevHist->Add(h);
8592 for (auto c : cms_coefs) {
8593 // seems I have to remake the function each time, as haven't figured out what cache needs clearing?
8594 std::unique_ptr<RooAbsReal> f(
8595 dynamic_cast<RooAbsReal *>(rarNode->components()[0]->get()->Clone("tmpCopy")));
8596 zero.setAttribute(Form("ORIGNAME:%s", c->GetName())); // used in redirectServers to say what this replaces
8597 f->redirectServers(RooArgSet(zero), false, true); // each time will replace one additional coef
8598 // zero.setAttribute(Form("ORIGNAME:%s",c->GetName()),false); (commented out so that on next iteration
8599 // will still replace all prev)
8600 auto hh = xRooNode(*f, *this).BuildHistogram(v);
8601 hh->SetName(c->GetName());
8602 if (sf)
8603 hh->Scale(sf->getVal());
8604 if (strlen(hh->GetTitle()) == 0) {
8605 hh->SetTitle(c->GetName()); // ensure all hists has titles
8606 histsWithBadTitles.insert(hh);
8607 } else if (strcmp(hh->GetName(), hh->GetTitle()) == 0) {
8608 histsWithBadTitles.insert(hh);
8609 }
8610 titleMatchName &= (TString(c->GetName()) == hh->GetTitle() ||
8611 TString(hh->GetTitle()).BeginsWith(TString(c->GetName()) + "_"));
8612 std::shared_ptr<TH1> nextHist(static_cast<TH1 *>(hh->Clone()));
8613 hh->Add(prevHist.get(), -1.);
8614 hh->Scale(-1.);
8615 hhs.push_back(hh);
8617 }
8618 } else if (get<RooSimultaneous>()) {
8619 // need to create a histogram for each sample across all the channels - will rely on functionality below to
8620 // merge them based on titles
8621
8622 for (auto &chan : bins()) {
8623 TString chanName(chan->GetName());
8624 chanName = chanName(chanName.Index("=") + 1, chanName.Length());
8625 auto samps = chan->mainChild();
8626 if (!samps)
8627 samps = *chan;
8628 for (auto &samp : samps.components()) {
8629 auto hh = static_cast<TH1 *>(h->Clone(samp->GetName()));
8630 hh->Reset();
8631 hh->SetTitle(samp->GetTitle());
8632 if (strlen(hh->GetTitle()) == 0) {
8633 hh->SetTitle(samp->GetName());
8634 histsWithBadTitles.insert(hh);
8635 } else if (strcmp(hh->GetName(), hh->GetTitle()) == 0) {
8636 histsWithBadTitles.insert(hh);
8637 }
8638 hh->SetTitle(TString(hh->GetTitle())
8639 .ReplaceAll(TString(chan->get()->GetName()) + "_",
8640 "")); // remove occurance of channelname_ in title (usually prefix)
8641 titleMatchName &= (TString(samp->GetName()) == hh->GetTitle() ||
8642 TString(hh->GetTitle()).BeginsWith(TString(samp->GetName()) + "_"));
8643 hh->SetBinContent(hh->GetXaxis()->FindFixBin(chanName), samp->GetContent());
8644 hhs.push_back(hh);
8645 }
8646 }
8647 } else {
8648 for (auto &samp : rarNode->components()) {
8649 auto hh = samp->BuildHistogram(
8650 v, empty, false /* no errors for stack*/, binStart, binEnd, _fr, false, false, 0, h, true,
8651 setInterp); // passing h to ensure binning is the same for all subcomponent hists
8652 hh->SetName(samp->GetName());
8653 if (sf)
8654 hh->Scale(sf->getVal());
8655 hhs.push_back(hh);
8656 if (strlen(hh->GetTitle()) == 0) {
8657 hh->SetTitle(samp->GetName()); // ensure all hists has titles
8658 histsWithBadTitles.insert(hh);
8659 } else if (strcmp(hh->GetName(), hh->GetTitle()) == 0) {
8660 histsWithBadTitles.insert(hh);
8661 }
8662 titleMatchName &= (TString(samp->GetName()) == hh->GetTitle() ||
8663 TString(hh->GetTitle()).BeginsWith(TString(samp->GetName()) + "_"));
8664 }
8665 }
8666
8667 if (!hhs.empty()) {
8668 for (auto &hh : hhs) {
8669 allTitles.insert(hh->GetTitle());
8670 }
8671
8672 // get common prefix to strip off only if all titles match names and
8673 // any title is longer than 10 chars
8674 size_t e = std::min(allTitles.begin()->size(), allTitles.rbegin()->size());
8675 size_t ii = 0;
8676 bool goodPrefix = false;
8677 std::string commonSuffix;
8678 if (titleMatchName && hhs.size() > 1) {
8679 while (ii < e - 1 && allTitles.begin()->at(ii) == allTitles.rbegin()->at(ii)) {
8680 ii++;
8681 if (allTitles.begin()->at(ii) == '_' || allTitles.begin()->at(ii) == ' ')
8682 goodPrefix = true;
8683 }
8684
8685 // find common suffix if there is one .. must start with a "_"
8686 bool stop = false;
8687 while (!stop && commonSuffix.size() < size_t(e - 1)) {
8688 commonSuffix = allTitles.begin()->substr(allTitles.begin()->length() - commonSuffix.length() - 1);
8689 for (auto &tt : allTitles) {
8690 if (!TString(tt).EndsWith(commonSuffix.c_str())) {
8691 commonSuffix = commonSuffix.substr(1);
8692 stop = true;
8693 break;
8694 }
8695 }
8696 }
8697 if (commonSuffix.find('_') == std::string::npos) {
8698 commonSuffix = "";
8699 } else {
8700 commonSuffix = commonSuffix.substr(commonSuffix.find('_'));
8701 }
8702 }
8703 if (!goodPrefix)
8704 ii = 0;
8705
8706 // also find how many characters are needed to distinguish all entries (that dont have the same name)
8707 // then carry on up to first space or underscore
8708 size_t jj = 0;
8709 std::map<std::string, std::string> reducedTitles;
8710 while (reducedTitles.size() != allTitles.size()) {
8711 jj++;
8712 std::map<std::string, int> titlesMap;
8713 for (auto &s : allTitles) {
8714 if (reducedTitles.count(s))
8715 continue;
8716 titlesMap[s.substr(0, jj)]++;
8717 }
8718 for (auto &s : allTitles) {
8719 if (titlesMap[s.substr(0, jj)] == 1 && (jj >= s.length() || s.at(jj) == ' ' || s.at(jj) == '_')) {
8720 reducedTitles[s] = s.substr(0, jj);
8721 }
8722 }
8723 }
8724
8725 // strip common prefix and suffix before adding
8726 for (auto ritr = hhs.rbegin(); ritr != hhs.rend(); ++ritr) { // go in reverse order
8727 if (!histsWithBadTitles.count((*ritr))) {
8728 continue;
8729 }
8730 auto _title = (hhs.size() > 5) ? reducedTitles[(*ritr)->GetTitle()] : (*ritr)->GetTitle();
8731 _title = _title.substr(ii < _title.size() ? ii : 0);
8732 if (!commonSuffix.empty() && TString(_title).EndsWith(commonSuffix.c_str()))
8733 _title = _title.substr(0, _title.length() - commonSuffix.length());
8734 (*ritr)->SetTitle(_title.c_str());
8735 }
8736 }
8737
8738 for (auto &hh : hhs) {
8739 // automatically group hists that all have the same title
8740 if (histGroups.find(hh->GetTitle()) == histGroups.end()) {
8741 histGroups[hh->GetTitle()] = hh;
8742 } else {
8743 // add it into this group
8744 histGroups[hh->GetTitle()]->Add(hh);
8745 delete hh;
8746 hh = nullptr;
8747 continue;
8748 }
8749 auto hhMin = (hh->GetMinimum() == 0) ? hh->GetMinimum(1e-9) : hh->GetMinimum();
8750 if (!stack->GetHists() && h->GetMinimum() > hhMin) {
8751 auto newMin = hhMin - (h->GetMaximum() - hhMin) * gStyle->GetHistTopMargin();
8752 if (hhMin >= 0 && newMin < 0)
8753 newMin = hhMin * 0.99;
8754 // adjustYRange(newMin, h->GetMaximum());
8755 }
8756
8757 /*if(stack->GetHists() && stack->GetHists()->GetEntries()>0) {
8758 // to remove rounding effects on bin boundaries, see if binnings compatible
8759 auto _h1 = dynamic_cast<TH1*>(stack->GetHists()->At(0));
8760 if(_h1->GetNbinsX()==hh->GetNbinsX()) TODO ... finish dealing with silly rounding effects
8761 }*/
8762 TString thisOpt = TString(hh->GetOption()) == "l" ? "LF2" : ""; // need LF2 to get smooth line with fill
8763 // uncomment next line to blend continuous with discrete components .. get some unpleasant "poke through"
8764 // effects though
8765 // if(auto s = samp->get<RooAbsReal>(); s) thisOpt = s->isBinnedDistribution(*dynamic_cast<RooAbsArg*>(v)) ?
8766 // "" : "LF2";
8767 stack->Add(hh, thisOpt);
8768 }
8769 // stack->SetBit(kCanDelete); // should delete its sub histograms
8770 h->GetListOfFunctions()->AddFirst(stack, "noclear same");
8771 // stack->Draw("noclear same");
8772 // h->Draw(
8773 // dOpt + sOpt +
8774 // "same"); // overlay again .. if stack would cover original hist (negative components) we still see
8775 // integral
8776 // h->Draw("axissame"); // redraws axis
8777
8778 TList *ll = stack->GetHists();
8779 if (ll && ll->GetEntries()) {
8780
8781 // finally, ensure all hists are styled
8782 for (auto ho : *ll) {
8783 TH1 *hh = dynamic_cast<TH1 *>(ho);
8784 if (!hh)
8785 continue;
8786 bool createdStyle = (xRooNode(*hh, *this).styles(nullptr, false).get<TStyle>() == nullptr);
8787
8788 if (createdStyle) {
8789 // give hist a color, that isn't the same as any other hists color
8790 hh->SetFillStyle(1001); // solid fill style
8791 bool used = false;
8792 do {
8793 hh->SetFillColor((count++));
8794 // check not already used this color
8795 used = false;
8796 for (auto ho2 : *ll) {
8797 TH1 *hh2 = dynamic_cast<TH1 *>(ho2);
8798 if (!hh2)
8799 continue;
8800 auto _styleNode = xRooNode(*hh2, *this).styles(hh2, false);
8801 auto _style = _styleNode.get<TStyle>();
8802 if (hh != hh2 && _style && _style->GetFillColor() == hh->GetFillColor()) {
8803 used = true;
8804 break;
8805 }
8806 }
8807 } while (used);
8808 }
8809
8810 auto _styleNode = xRooNode(*hh, *this).styles(hh);
8811 if (auto _style = _styleNode.get<TStyle>()) {
8812 *dynamic_cast<TAttLine *>(hh) = *_style;
8813 *dynamic_cast<TAttFill *>(hh) = *_style;
8814 *dynamic_cast<TAttMarker *>(hh) = *_style;
8815 }
8816 // for stacks, fill color of white should be color 10 unless fill style is 0
8817 if (hh->GetFillColor() == kWhite && hh->GetFillStyle() != 0) {
8818 // kWhite means 'transparent' in ROOT ... should really use a FillStyle of 0 for that
8819 // so assume user wanted actual white, which is color 10
8820 hh->SetFillColor(10);
8821 }
8822 }
8823 }
8824 }
8825
8826 return h;
8827}
8828
8829double xRooNode::GetBinData(int bin, const xRooNode &data)
8830{
8831 if (data.get<RooAbsData>()) {
8832 // attach as a child before calling datasets(), so that is included in the list
8833 push_back(std::make_shared<xRooNode>(data));
8834 }
8835 auto node = datasets().find(data.GetName());
8836 if (data.get<RooAbsData>()) {
8837 // remove the child we attached
8838 resize(size() - 1);
8839 }
8840 if (!node)
8841 return std::numeric_limits<double>::quiet_NaN();
8842 return node->GetBinContent(bin);
8843}
8844
8845std::vector<double> xRooNode::GetBinContents(int binStart, int binEnd) const
8846{
8847 if (fBinNumber != -1) {
8848 if (binStart != binEnd || !fParent) {
8849 throw std::runtime_error(TString::Format("%s is a bin - only has one value", GetName()));
8850 }
8851 return fParent->GetBinContents(fBinNumber, fBinNumber);
8852 }
8853 std::vector<double> out;
8854 if (get<RooAbsData>()) {
8855 auto g = BuildGraph(
8856 nullptr,
8857 (binStart != -1 ||
8858 binEnd != -1) /*include points for zeros unless we are asking for a single point with start=end=-1*/);
8859 if (!g) {
8860 return out;
8861 }
8862 if (binStart == binEnd && binStart == -1) {
8863 // integral over all bins if getting bin content -1
8864 double integral(0);
8865 for (int i = 0; i < g->GetN(); i++)
8866 integral += g->GetPointY(i);
8867 out.push_back(integral);
8868 delete g;
8869 return out;
8870 }
8871 for (int i = binStart - 1; i < g->GetN() && (binEnd == 0 || i < binEnd); i++) {
8872 out.push_back(g->GetPointY(i));
8873 }
8874 delete g;
8875 return out;
8876 }
8877
8878 bool doIntegral = false;
8879 if (binStart == binEnd && binStart == -1) {
8880 binStart = -1;
8881 binEnd = -1;
8882 doIntegral = true;
8883 } // return integral if request bin -1
8884 auto h = BuildHistogram(nullptr, false, false, binStart, binEnd);
8885 if (!h) {
8886 throw std::runtime_error(TString::Format("%s has no content", GetName()));
8887 }
8888 if (binEnd == 0) {
8889 binEnd = h->GetNbinsX();
8890 }
8891 if (doIntegral) {
8892 double tot = 0;
8893 for (int i = 1; i <= h->GetNbinsX(); i++) {
8894 tot += h->GetBinContent(i);
8895 }
8896 out.push_back(tot);
8897 } else {
8898 for (int i = binStart; i <= binEnd; i++) {
8899 out.push_back(h->GetBinContent(i));
8900 }
8901 }
8902 delete h;
8903 return out;
8904}
8905
8907{
8908 if (auto a = get<RooAbsArg>(); a) {
8909 // go through servers looking for 'main' thing
8910 for (auto &l : a->servers()) {
8911 if (l->getAttribute("MAIN_MEASUREMENT") || l->InheritsFrom("RooRealSumPdf") || l->InheritsFrom("RooAddPdf")) {
8912 return xRooNode(*l, *this);
8913 }
8914 }
8915 // the main child of a RooProduct is one that has the same name (/alias) as the product (except if is a bin
8916 // factor)
8917 if (a->IsA() == RooProduct::Class() && fBinNumber == -1) {
8918 for (auto &l : factors()) {
8919 if (strcmp(l->GetName(), GetName()) == 0) {
8920 return *l;
8921 }
8922 }
8923 }
8924 }
8925 return xRooNode();
8926}
8927
8929{
8930 if (auto o = get(); o) {
8931 o->Inspect();
8932 } else {
8934 }
8935}
8936
8937bool TopRightPlaceBox(TPad *p, TObject *o, double w, double h, double &xl, double &yb)
8938{
8939#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
8940 // reinitialize collide grid because the filling depends on fUxmin and fUxmax (and ymin ymax too)
8941 // and these aren't filled on the first time we do the placement (they init to 0 and 1), but will be filled
8942 // subsequently
8943 for (int i = 0; i < p->fCGnx; i++) {
8944 for (int j = 0; j < p->fCGny; j++) {
8945 p->fCollideGrid[i + j * p->fCGnx] = true;
8946 }
8947 }
8948 p->FillCollideGrid(o);
8949 Int_t iw = (int)(p->fCGnx * w);
8950 Int_t ih = (int)(p->fCGny * h);
8951
8952 Int_t nxmax = p->fCGnx - iw - 1 - p->fCGnx * p->GetRightMargin();
8953 Int_t nymax = p->fCGny - ih - 1 - p->fCGny * p->GetTopMargin();
8954
8955 for (Int_t j = nymax; j >= 0; j--) {
8956 for (Int_t i = nxmax; i >= 0; i--) {
8957 if (p->Collide(i, j, iw, ih)) {
8958 continue;
8959 } else {
8960 xl = (double)(i) / (double)(p->fCGnx);
8961 yb = (double)(j) / (double)(p->fCGny);
8962 return true;
8963 }
8964 }
8965 }
8966 return false;
8967#else
8968 return p->PlaceBox(o, w, h, xl, yb, "trw");
8969#endif
8970}
8971
8972TPaveText *getPave(const char *name = "labels", bool create = true, bool doPaint = false)
8973{
8974 if (auto p = dynamic_cast<TPaveText *>(gPad->GetPrimitive(name)); p) {
8975 if (doPaint)
8976 gPad->PaintModified(); //-- slows down x11 so trying to avoid
8977 return p;
8978 }
8979 if (!create) {
8980 return nullptr;
8981 }
8982 auto l = new TPaveText(gPad->GetLeftMargin() + 0.02, 1. - gPad->GetTopMargin() - 0.08, 0.6,
8983 1. - gPad->GetTopMargin() - 0.08);
8984 l->SetBorderSize(0);
8985 if (l->GetTextSize() == 0)
8986 l->SetTextSize(gStyle->GetTitleYSize());
8987
8989 // l->SetMargin(0);
8990 l->SetFillStyle(0);
8991 l->SetName(name);
8992 l->Draw();
8993 l->ConvertNDCtoPad();
8994 return l;
8995}
8996
8997TLegend *getLegend(bool create = true, bool doPaint = false)
8998{
8999 if (auto p = dynamic_cast<TLegend *>(gPad->GetPrimitive("legend")); p) {
9000 double x;
9001 double y;
9002 double w = p->GetX2NDC() - p->GetX1NDC();
9003 double h = p->GetY2NDC() - p->GetY1NDC();
9004 if (doPaint)
9005 gPad->PaintModified(); //-- slows down x11 so trying to avoid
9006 if (TopRightPlaceBox(dynamic_cast<TPad *>(gPad), p, w, h, x, y)) {
9007 // squash inside the frame ..
9008 // std::cout << gPad->GetName() << ":" << x << " , " << y << " , " << w << " , " << h << std::endl;
9009 x = std::max(x, (gPad->GetLeftMargin() + 0.02));
9010 y = std::max(y, (gPad->GetBottomMargin() + 0.02));
9011 x = std::min(x, (1. - gPad->GetRightMargin() - 0.02) - w);
9012 y = std::min(y, (1. - gPad->GetTopMargin() - 0.02) - h);
9013 h = std::min(h, (1. - gPad->GetTopMargin() - 0.02) - y);
9014 w = std::min(w, (1. - gPad->GetRightMargin() - 0.02) - x);
9015 // std::cout << gPad->GetName() << ":" << x << " , " << y << " , " << h << " , " << w << std::endl;
9016 p->SetX1NDC(x);
9017 p->SetY1NDC(y);
9018 p->SetX2NDC(x + w);
9019 p->SetY2NDC(y + h);
9020 gPad->Modified();
9021 }
9022 return p;
9023 }
9024 // look for a parent pad called 'legend' and create it there if existing
9025 auto p = gPad;
9026 while ((p != p->GetMother()) && (p = p->GetMother())) {
9027 if (auto q = dynamic_cast<TVirtualPad *>(p->GetPrimitive("legend")); q) {
9028 q->Modified();
9029 p = q;
9030 break;
9031 }
9032 }
9033 auto tmpPad = gPad;
9034 TLegend *l = nullptr;
9035 if (p && strcmp(p->GetName(), "legend") == 0) {
9036 if (l = dynamic_cast<TLegend *>(p->GetPrimitive("legend")); l || !create)
9037 return l;
9038 p->cd();
9039 l = new TLegend(gPad->GetLeftMargin(), 1. - gPad->GetTopMargin(), 1. - gPad->GetRightMargin(),
9040 gPad->GetBottomMargin());
9041 l->SetBorderSize(1); // ensure has a border
9042 } else {
9043 if (!create)
9044 return nullptr;
9045 l = new TLegend(0.6, 1. - gPad->GetTopMargin() - 0.08, 0.75, 1. - gPad->GetTopMargin() - 0.08);
9046 l->SetBorderSize(0);
9047 // legend text will be required to match y-axis
9048 if (l->GetTextSize() == 0) {
9049 l->SetTextSize(gStyle->GetTitleYSize());
9050 l->SetTextFont(gStyle->GetTitleFont("Y"));
9051 }
9052 }
9054 // l->SetMargin(0);
9055 l->SetFillStyle(0);
9056 l->SetName("legend");
9057 l->Draw();
9058 l->ConvertNDCtoPad();
9059 tmpPad->cd();
9060 return l;
9061}
9062
9063std::string formatLegendString(const std::string &s)
9064{
9065 auto i = s.find("\n");
9066 if (i == std::string::npos) {
9067 return s;
9068 }
9069 return std::string("#splitline{") + s.substr(0, i) + "}{" + formatLegendString(s.substr(i + 1)) + "}";
9070}
9071
9072void addLegendEntry(TObject *o, const char *title, const char *opt)
9073{
9074 auto l = getLegend();
9075 if (!l)
9076 return;
9077 // check for entry already existing with same title
9078 for (auto a : *l->GetListOfPrimitives()) {
9079 if (formatLegendString(title) == dynamic_cast<TLegendEntry *>(a)->GetLabel())
9080 return;
9081 }
9082 if (l->GetListOfPrimitives()->GetEntries() > 20)
9083 return; // todo: create an 'other' entry?
9084
9085 l->AddEntry(o, formatLegendString(title).c_str(), opt);
9086 if (auto nObj = l->GetListOfPrimitives()->GetEntries(); nObj > 0) {
9087 // each entry takes up 0.05 ... maximum of N*(N+4) (where N is # cols) before next column
9088 int nn = l->GetNColumns();
9089 nn *= (nn + 4);
9090 if (nObj > 1 && (nObj % nn) == 1) {
9091 l->SetNColumns(l->GetNColumns() + 1);
9092 if (l->GetBorderSize() == 0) {
9093 l->SetX1NDC(l->GetX2NDC() - 0.15 * l->GetNColumns());
9094 }
9095 }
9096 if (l->GetBorderSize() == 0) {
9097 l->SetY1NDC(l->GetY2NDC() - 0.05 * gPad->GetHNDC() * std::ceil((double(nObj) / l->GetNColumns())));
9098 }
9099 }
9100
9101 getLegend(); // to mark modified
9102}
9103
9104// this exists to avoid calling update excessively because it slows down x11 ... but still
9105// need to call update twice if have a legend drawn in order to relocate it.
9107public:
9108 PadRefresher(TVirtualPad *p) : fPad(p) { nExisting++; }
9110 {
9111 if (fPad) {
9112 getLegend(false, true);
9113 fPad->GetCanvas()->Paint();
9114 fPad->GetCanvas()->Update();
9115#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 30, 00)
9116 fPad->GetCanvas()->ResetUpdated(); // stops previous canvas being replaced in a jupyter notebook
9117#endif
9118 fPad->cd();
9119 }
9120 nExisting--;
9121 }
9122 TVirtualPad *fPad = nullptr;
9123 static int nExisting;
9124};
9125
9127
9129{
9130 // in order to catch exceptions to prevent crash of GUI, do this:
9131 if (gROOT->FromPopUp()) {
9132 gROOT->SetFromPopUp(false);
9133 try {
9134 Draw(opt);
9135 } catch (const std::exception &e) {
9136 new TGMsgBox(
9137 gClient->GetRoot(),
9138 (gROOT->GetListOfBrowsers()->At(0))
9139 ? dynamic_cast<TGWindow *>(static_cast<TBrowser *>(gROOT->GetListOfBrowsers()->At(0))->GetBrowserImp())
9140 : gClient->GetRoot(),
9141 "Exception", e.what(),
9142 kMBIconExclamation); // deletes self on dismiss?
9143 }
9144 gROOT->SetFromPopUp(true);
9145 return;
9146 }
9147
9148 TString sOpt2(opt);
9149 sOpt2.ToLower();
9150 if (!get() && !IsFolder() && !sOpt2.Contains("x="))
9151 return;
9152
9153 if (auto mc = get<RooStats::ModelConfig>()) {
9154 xRooNode(*mc->GetPdf(), fParent).Draw(opt); // draw the pdf of the config
9155 } else if (auto ir = get<RooStats::HypoTestInverterResult>()) {
9156 xRooHypoSpace(ir).Draw(opt);
9158 return;
9159 } else if (get<RooStats::HypoTestResult>()) {
9160 if (gPad)
9161 gPad->Clear();
9162 xRooNLLVar::xRooHypoPoint(std::dynamic_pointer_cast<RooStats::HypoTestResult>(fComp)).Draw(opt);
9163 {
9164 PadRefresher p(gPad); // refreshes the pad
9165 }
9167 return;
9168 }
9169
9170 if (sOpt2 == "pcls" && get<RooRealVar>() && fParent && fParent->get<RooAbsPdf>()) {
9171 // use the first selected dataset
9172 auto _dsets = fParent->datasets();
9173 // bool _drawn=false;
9174 TString dsetName = "";
9175 for (auto &d : _dsets) {
9176 if (d->get()->TestBit(1 << 20)) {
9177 dsetName = d->get()->GetName();
9178 break;
9179 }
9180 }
9181 auto hs = fParent->nll(dsetName.Data()).hypoSpace(get<RooRealVar>()->GetName());
9182 hs.limits("cls visualize");
9183 hs.SetName(TUUID().AsString());
9184 if (ws()) {
9185 ws()->import(*hs.result());
9186 }
9187 return;
9188 }
9189
9190 if (auxFunctions.empty()) {
9191 // add the defaults: Ratio and Signif
9193 "Ratio",
9194 [](double a, double b, double) {
9195 if (a == 0)
9196 return 0.;
9197 if (b == 0 && a == 0)
9198 return 1.;
9199 return a / b;
9200 },
9201 true);
9203 "Signif",
9204 [](double n, double b, double sigma) {
9205 double t0 = 0;
9206 if (sigma <= 0.) {
9207 // use simplified expression ...
9208 t0 = 2. * (((n == 0) ? 0 : n * log(n / b)) - (n - b));
9209 } else {
9210 double sigma2 = sigma * sigma;
9211 double b_hathat = 0.5 * (b - sigma2 + sqrt(pow(b - sigma2, 2) + 4 * n * sigma2));
9212 // double s_hat = n - m;
9213 // double b_hat = m;
9214 t0 = 2. * (((n == 0) ? 0 : n * log(n / b_hathat)) + b_hathat - n + pow(b - b_hathat, 2) / (2. * sigma2));
9215 }
9216 if (t0 < 0)
9217 return 0.; // can happen from numerical precision
9218 return (n >= b) ? sqrt(t0) : -sqrt(t0);
9219 },
9220 false);
9221 }
9222
9223 TString sOpt(opt);
9224
9225 RooAbsLValue *v = nullptr;
9226 std::vector<double> xPoints;
9227 if (sOpt2.Contains("x=")) {
9228 // specifying a particular var to scan over ...
9229 int _idx = sOpt2.Index("x=");
9230 int _eidx = sOpt2.Index(';', _idx);
9231 TString varPart = sOpt(_idx + 2, (_eidx < 0 ? sOpt2.Length() : _eidx) - (_idx + 2));
9233 // if varName is of form str(num,num,num) then can infer scan points
9234 if (auto _idx2 = varPart.Index("("); _idx2 > 0) {
9235 varName = varPart(0, _idx2);
9236 TStringToken pattern(TString(varPart(_idx2 + 1, varPart.Length() - _idx2 - 2)), ",");
9237 double min(0);
9238 double max(0);
9239 int nBins = 0;
9240 int ii = 0;
9241 while (pattern.NextToken()) {
9242 TString s = pattern;
9243 if (ii == 0) {
9244 nBins = s.Atoi();
9245 } else if (ii == 1) {
9246 min = s.Atof();
9247 } else if (ii == 2) {
9248 max = s.Atof();
9249 }
9250 ii++;
9251 }
9252 if (nBins > 100)
9253 nBins = 100; // limit scanning to 100 points
9254 if (nBins > 1) {
9255 for (double x = min; x <= max; x += (max - min) / (nBins - 1)) {
9256 xPoints.push_back(x);
9257 }
9258 } else if (nBins == 1)
9259 xPoints.push_back((min + max) / 2.);
9260 }
9261 v = getObject<RooAbsLValue>(varName.Data()).get();
9262 if (!v) {
9263 throw std::runtime_error(TString::Format("Could not find variable %s", varName.Data()));
9264 }
9265 if (xPoints.empty() && !obs().find(varName.Data()) &&
9266 dynamic_cast<RooAbsRealLValue *>(v)) { // will draw obs as regular (e.g. hist)
9267 double tmp = static_cast<RooAbsRealLValue *>(v)->getVal();
9268 for (int i = 0; i < v->numBins(GetName()); i++) {
9269 v->setBin(i, GetName());
9270 xPoints.push_back(static_cast<RooAbsRealLValue *>(v)->getVal());
9271 }
9272 static_cast<RooAbsRealLValue *>(v)->setVal(tmp);
9273 }
9274 sOpt2 = TString(sOpt2(0, _idx)) + sOpt2(_idx + 2 + varPart.Length() + 1, sOpt2.Length());
9275 sOpt = TString(sOpt(0, _idx)) + sOpt(_idx + 2 + varPart.Length() + 1, sOpt.Length());
9276 }
9277 TString forceNames = "";
9278 if (sOpt2.Contains("force")) {
9279 // force plots show how much NLL changes wrt to a change of variables
9280 if (get<RooRealVar>() && fParent && fParent->get<RooAbsPdf>()) {
9281 // assume want force of this parameter from the parent pdf
9282 TString ff = sOpt(sOpt2.Index("force"), sOpt2.Index("force") + 5);
9283 sOpt.ReplaceAll(ff, TString::Format("force%s", get()->GetName()));
9284 fParent->Draw(sOpt);
9285 return;
9286 } else if (get<RooAbsPdf>()) {
9287 // extract the parameter(s) to calculate force for
9288 forceNames = sOpt(sOpt2.Index("force") + 5, sOpt2.Length());
9289 sOpt = sOpt(0, sOpt2.Index("force"));
9290 sOpt2 = sOpt2(0, sOpt2.Index("force"));
9291 } else {
9292 Error("Draw", "Can only compute forces with PDFs");
9293 return; // don't throw because will cause browser to exit if done from there
9294 }
9295 }
9296 bool hasOverlay = sOpt2.Contains("overlay");
9297 TString overlayName = "";
9298 if (hasOverlay) {
9299 // whatever follows overlay is the variation name
9300 overlayName = sOpt(sOpt2.Index("overlay") + 7, sOpt2.Length());
9301 sOpt = sOpt(0, sOpt2.Index("overlay"));
9302 sOpt2 = sOpt2(0, sOpt2.Index("overlay"));
9303 }
9304 if (sOpt2.Contains("ratio") && !sOpt2.Contains("auxratio"))
9305 sOpt += "auxRatio";
9306 if (sOpt2.Contains("significance") && !sOpt2.Contains("auxsignif"))
9307 sOpt += "auxSignif";
9308
9309 std::string auxPlotTitle;
9310 for (auto &[k, _] : auxFunctions) {
9311 if (sOpt.Contains(TString::Format("aux%s", k.c_str()))) {
9312 auxPlotTitle = k;
9313 }
9314 sOpt.ReplaceAll(TString::Format("aux%s", k.c_str()), "");
9315 }
9316
9317 sOpt.ToLower();
9318 sOpt.ReplaceAll("ratio", "");
9319 sOpt.ReplaceAll("significance", ""); // remove old option if still given
9320 bool nostack = sOpt.Contains("nostack");
9321 sOpt.ReplaceAll("nostack", "");
9322 bool hasSame = sOpt.Contains("same");
9323 sOpt.ReplaceAll("same", "");
9324 bool hasGoff = sOpt.Contains("goff");
9325 sOpt.ReplaceAll("goff", "");
9326 bool hasFR = sOpt.Contains("pull") && !get<RooFitResult>();
9327 sOpt.ReplaceAll("pull", "");
9328 bool hasText = sOpt.Contains("text");
9329 bool hasTexte = sOpt.Contains("texte");
9330 bool hasErrorOpt = sOpt.Contains("e");
9331 sOpt.ReplaceAll("e", "");
9332 if (hasTexte) {
9333 sOpt.ReplaceAll("txt", "texte");
9334 } else if (hasText) {
9335 sOpt.ReplaceAll("txt", "text");
9336 }
9337 if (auxPlotTitle == "Signif")
9338 hasErrorOpt = true; // must calculate error to calculate significance
9339 if (hasOverlay)
9340 hasSame = true; // when overlaying must be putting on same
9341
9342 TVirtualPad *pad = gPad;
9343
9344 TH1 *hAxis = nullptr;
9345
9346 auto clearPad = []() {
9347 gPad->Clear();
9348 if (gPad->GetNumber() == 0) {
9349 gPad->SetBottomMargin(gStyle->GetPadBottomMargin());
9350 gPad->SetTopMargin(gStyle->GetPadTopMargin());
9351 gPad->SetLeftMargin(gStyle->GetPadLeftMargin());
9352 gPad->SetRightMargin(gStyle->GetPadRightMargin());
9353 }
9354 // if (gPad == gPad->GetCanvas()) {
9355 // gPad->GetCanvas()->SetCanvasSize( gPad->GetCanvas()->GetWindowWidth() - 4,
9356 // gPad->GetCanvas()->GetWindowHeight() - 28 );
9357 // }
9358 };
9359
9360 if (!hasSame || !pad) {
9361 if (!pad) {
9363 pad = gPad;
9364 }
9365
9366 } else {
9367 // get the histogram representing the axes
9368 hAxis = dynamic_cast<TH1 *>(pad->GetPrimitive("axis"));
9369 if (!hAxis) {
9370 for (auto o : *pad->GetListOfPrimitives()) {
9371 if (hAxis = dynamic_cast<TH1 *>(o); hAxis)
9372 break;
9373 }
9374 }
9375 if (hAxis && !v) {
9376 v = getObject<RooAbsLValue>(hAxis->GetXaxis()->IsAlphanumeric() ? hAxis->GetXaxis()->GetTimeFormatOnly()
9377 : hAxis->GetXaxis()->GetName())
9378 .get();
9379 }
9380 }
9381
9382 if (!hasSame) {
9383 if (gPad != gPad->GetCanvas()) {
9384 gPad->SetName(GetName()); // only rename the pad if its not the parent canvas
9385 }
9386 gPad->SetTitle(GetTitle());
9387 }
9388
9390
9391 auto adjustYRange = [&](double min, double max, TH1 *hh = nullptr, bool symmetrize = false) {
9392 if (!hh)
9393 hh = hAxis;
9394 // give max and min a buffer ...
9395 max += gStyle->GetHistTopMargin() * (max - min);
9396 if (min > 0)
9397 min = std::max(min * 0.9, min - gStyle->GetHistTopMargin() * (max - min));
9398 if (hh) {
9399 double ymin = hh->GetMinimum();
9400 double ymax = hh->GetMaximum();
9401 if (hh->GetMaximumStored() == -1111)
9402 ymax += gStyle->GetHistTopMargin() * (ymax - ymin);
9403 if (hh->GetMinimumStored() == -1111) {
9404 if (gStyle->GetHistMinimumZero() && ymax >= 0) {
9405 ymin = 0;
9406 } else if (ymin < 0) {
9407 ymin -= gStyle->GetHistTopMargin() * (ymax - ymin);
9408 } else {
9409 ymin = std::max(ymin * 0.9, ymin - gStyle->GetHistTopMargin() * (ymax - ymin));
9410 }
9411 // see TGLPlotPainter to complete the mimic, but we leave off here truncating @ 0 if ymax>0
9412 }
9413 // make ymax at least 3x bigger than biggest error if has error
9414 if (hh->GetSumw2()) {
9415 double smallestErrDown3 = -std::numeric_limits<double>::infinity();
9416 double smallestErrUp3 = std::numeric_limits<double>::infinity();
9417 for (int i = 1; i <= hh->GetNbinsX(); i++) {
9418 smallestErrDown3 = std::max(smallestErrDown3, hh->GetBinContent(i) - 3 * hh->GetBinError(i));
9419 smallestErrUp3 = std::min(smallestErrUp3, hh->GetBinContent(i) + 3 * hh->GetBinError(i));
9420 }
9421 max = std::max(max, smallestErrUp3);
9422 min = std::min(min, smallestErrDown3);
9423 }
9424 bool change = false;
9425 if (min < ymin) {
9426 ymin = min;
9427 change = true;
9428 }
9429 if (max > ymax) {
9430 ymax = max;
9431 change = true;
9432 }
9433 if (change) {
9434 // note: unfortunately when user 'unzooms' y axis it resets stored minimum to -1111, so lose range
9435 if (symmetrize) {
9436 double down = hh->GetBinContent(1) - ymin;
9437 double up = ymax - hh->GetBinContent(1);
9438 if (down > up) {
9439 ymax = hh->GetBinContent(1) + down;
9440 } else {
9441 ymin = hh->GetBinContent(1) - up;
9442 }
9443 }
9444 if (hh == hAxis && pad && !pad->GetLogy() && ymin > 0 && (log10(ymax) - log10(max)) >= 3) {
9445 // auto-log the pad
9446 pad->SetLogy();
9447 }
9448 if (hh == hAxis && pad && ymin == 0 && pad->GetLogy()) {
9449 ymin = 1e-2;
9450 }
9451 if (ymin == 0 && ymax > 10)
9452 ymin = 0.1; // adjust min so if user activates log scale it isn't bad
9453 hh->SetMinimum(ymin);
9454 hh->SetMaximum(ymax);
9455 hh->GetYaxis()->Set(1, ymin, ymax);
9456 hh->SetAxisRange(ymin, ymax, "Y");
9457 }
9458 }
9459 };
9460
9461 auto graphMinMax = [](TGraphAsymmErrors *gr) {
9462 double ymax = -std::numeric_limits<double>::infinity();
9463 double ymin = std::numeric_limits<double>::infinity();
9464 for (int i = 0; i < gr->GetN(); i++) {
9465 ymax = std::max(ymax, gr->GetPointY(i) + gr->GetErrorYhigh(i));
9466 ymin = std::min(ymin, gr->GetPointY(i) - gr->GetErrorYlow(i));
9467 }
9468 return std::make_pair(ymin, ymax);
9469 };
9470
9471 if (!xPoints.empty()) {
9472 // create a graph using GetContent
9474 out->SetName(GetName());
9475 out->SetTitle(GetTitle());
9476 out->SetFillColor(out->GetLineColor());
9477 out->SetMarkerStyle(0);
9478 out->SetFillStyle(hasErrorOpt ? 3005 : 0);
9479 double tmp = static_cast<RooAbsRealLValue *>(v)->getVal();
9480 for (auto &x : xPoints) {
9481 static_cast<RooAbsRealLValue *>(v)->setVal(x);
9482 out->AddPoint(x, GetContent());
9483 if (hasErrorOpt) {
9484 out->SetPointEYlow(out->GetN() - 1, GetError());
9485 out->SetPointEYhigh(out->GetN() - 1, out->GetErrorYlow(out->GetN() - 1)); // symmetric error for now
9486 }
9487 }
9488 static_cast<RooAbsRealLValue *>(v)->setVal(tmp);
9489 out->GetHistogram()->GetXaxis()->SetTitle(static_cast<RooAbsRealLValue *>(v)->GetTitle());
9490 out->SetBit(kCanDelete);
9491 out->Draw(TString(hasSame ? "L" : "AL") + (hasErrorOpt ? "3" : ""));
9492 return;
9493 }
9494
9495 if (hasFR) {
9496 // drawing the fitresult as a pull plot on a subpad, and rest of the draw elsewhere
9497 clearPad();
9498 pad->Divide(1, 2, 1e-9, 1e-9); //,0,0);
9499 pad->GetPad(1)->SetPad(0, 0.2, 1, 1);
9500 pad->GetPad(2)->SetPad(0, 0, 1, 0.2);
9501 TString optNoFR(opt);
9502 optNoFR.ReplaceAll("pull", "");
9503 pad->cd(1);
9504 Draw(optNoFR);
9505 pad->cd(2);
9506 auto _fr = fitResult();
9507 _fr.Draw();
9508 // switch into subpad
9509 gPad->cd(1);
9510 gPad->SetFillColor(kGray);
9511 gPad->GetFrame()->SetFillColor(kWhite);
9512 gPad->GetFrame()->SetFillStyle(1001);
9513 gPad->SetTopMargin(0);
9514 gPad->SetBottomMargin(0);
9515 gPad->SetName("pull");
9516 // split the pull graph into individual points -- for benefit of GUI status bar
9517 auto pullGraph = dynamic_cast<TGraphAsymmErrors *>(gPad->GetPrimitive("pulls"));
9518 if (!pullGraph) {
9519 Error("Draw", "Couldn't find pull graph");
9520 return;
9521 }
9522 pullGraph->SetName("nominal");
9523 TMultiGraph *mg = new TMultiGraph;
9524 mg->SetName("editables");
9525
9526 auto scaleHist = static_cast<TH1 *>(pullGraph->FindObject("scales"));
9527 if (!scaleHist)
9528 throw std::runtime_error("Could not find scales in fit result");
9529
9530 for (auto i = 0; i < pullGraph->GetN(); i++) {
9531 auto g = new TGraphAsymmErrors;
9532 g->SetName(scaleHist->GetXaxis()->GetBinLabel(i + 1));
9533 auto _p = dynamic_cast<RooRealVar *>(_fr.get<RooFitResult>()->floatParsFinal().find(g->GetName()));
9534 if (!_p) {
9535 Warning("Draw", "Found a non-var in the floatParsFinal list: %s - this shouldn't happen", g->GetName());
9536 continue;
9537 }
9538 g->SetTitle(TString::Format(
9539 "%s=%g +/- %s [%g,%g]", strlen(_p->GetTitle()) ? _p->GetTitle() : _p->GetName(), _p->getVal(),
9540 _p->hasAsymError() ? TString::Format("(%g,%g)", _p->getAsymErrorHi(), _p->getAsymErrorLo()).Data()
9541 : TString::Format("%g", _p->getError()).Data(),
9542 scaleHist->GetBinContent(i + 1), scaleHist->GetBinError(i + 1)));
9543 g->SetPoint(0, pullGraph->GetPointX(i), pullGraph->GetPointY(i));
9544 g->SetPointEYhigh(0, pullGraph->GetErrorYhigh(i));
9545 g->SetPointEYlow(0, pullGraph->GetErrorYlow(i));
9546 g->SetEditable(true);
9547 g->SetHighlight(true);
9548 g->SetMarkerStyle(20);
9549 g->SetMarkerSize(0.5);
9550 mg->Add(g);
9551 }
9552 // gPad->GetListOfPrimitives()->Remove(pullGraph); delete pullGraph;
9553 mg->Draw("z0p");
9554 mg->SetBit(kCanDelete);
9555 auto _thisClone = new xRooNode("node", fComp, fParent);
9556 _thisClone->SetBit(kCanDelete);
9557 _thisClone->AppendPad();
9558
9559 // ensure statusbar visible for interactive plot
9560 // turned this off for now ... as not needed if doing through browser, status bar already there
9561 // if (gPad->GetCanvas() && !gPad->GetCanvas()->TestBit(TCanvas::kShowEventStatus)) {
9562 // gPad->GetCanvas()->ToggleEventStatus();
9563 // }
9564 gPad->AddExec("interactivePull", TString::Format("%s::Interactive_Pull()", ClassName()));
9565
9566 pad->cd();
9567 return;
9568 }
9569
9570 if (auto _simPdf = get<RooSimultaneous>();
9571 _simPdf && !(v && strcmp(_simPdf->indexCat().GetName(), dynamic_cast<TObject *>(v)->GetName()) == 0)) {
9572 auto _channels = bins();
9573 int _size = 0;
9574 for (auto &_v : _channels) {
9575 if (!_v->IsHidden())
9576 _size++;
9577 }
9578 if (!hasSame) {
9579 if (_size > 2) {
9580 // add a pad for the common legends
9581 _size++;
9582 }
9583 clearPad();
9584 pad->SetBorderSize(0);
9585 // if (pad->GetCanvas() == pad) {
9586 // if(_size>4) {
9587 // int n = _size;
9588 // Int_t w = 1, h = 1;
9589 // if (pad->GetCanvas()->GetWindowWidth() > pad->GetCanvas()->GetWindowHeight()) {
9590 // w = TMath::Ceil(TMath::Sqrt(n));
9591 // h = TMath::Floor(TMath::Sqrt(n));
9592 // if (w*h < n) w++;
9593 // } else {
9594 // h = TMath::Ceil(TMath::Sqrt(n));
9595 // w = TMath::Floor(TMath::Sqrt(n));
9596 // if (w*h < n) h++;
9597 // }
9598 // // adjust the window size to display only 4 in the window, with scroll bars
9599 // pad->GetCanvas()->SetCanvasSize( w*((pad->GetCanvas()->GetWindowWidth()-4)/2.) -16
9600 // ,h*((pad->GetCanvas()->GetWindowHeight()-28)/2.) - 16 );
9601 // } else {
9602 // //pad->GetCanvas()->Set(
9603 // w*(pad->GetCanvas()->GetWindowWidth()/2.),h*(pad->GetCanvas()->GetWindowHeight()/2.)) )
9604 // }
9605 // }
9606 dynamic_cast<TPad *>(pad)->DivideSquare(_size, 1e-9, 1e-9);
9607 if (_size > 3) {
9608 auto _pad = pad->GetPad(_size); // will use as the legend pad
9609 _pad->SetName("legend");
9610 // stretch the pad all the way to the left
9611 _pad->SetPad(_pad->GetXlowNDC(), _pad->GetYlowNDC(), 1.0, _pad->GetYlowNDC() + _pad->GetHNDC());
9612 // and make all the remaining pads transparent
9613 int x = _size;
9614 while (pad->GetPad(x + 1)) {
9615 pad->GetPad(x + 1)->SetFillStyle(0);
9616 x++;
9617 }
9618 }
9619 }
9620 int i = 0;
9621 auto &chanVar = const_cast<RooAbsCategoryLValue &>(_simPdf->indexCat());
9622 // auto _idx = chanVar.getIndex();
9623 auto _range = GetRange();
9624 std::vector<TString> chanPatterns;
9625 if (_range && strlen(_range)) {
9626 TStringToken pattern(_range, ",");
9627 while (pattern.NextToken()) {
9628 chanPatterns.emplace_back(pattern);
9629 }
9630 }
9631 for (auto &_v : _channels) {
9632 if (_v->IsHidden())
9633 continue;
9634 TString s(_v->GetName());
9635 pad->cd(++i);
9636 gPad->SetName(s);
9637 TString cName = s(s.Index('=') + 1, s.Length());
9638 chanVar.setLabel(cName);
9639 bool inRange = chanPatterns.empty();
9640 for (auto &p : chanPatterns) {
9641 if (chanVar.inRange(p)) {
9642 inRange = true;
9643 break;
9644 }
9645 }
9646 if (!inRange || !_v->get<RooAbsReal>()->isSelectedComp())
9647 gPad->SetFillColor(kGray);
9648 if (!hasSame && _size > 1 && (gStyle->GetTitleFont("Y") % 10) == 3)
9649 gPad->SetLeftMargin(std::min(gPad->GetLeftMargin() * (1. / gPad->GetWNDC()), 0.3));
9650 _v->Draw(opt);
9652 }
9653 pad->cd(0);
9654 gPad->Modified();
9655 // gPad->Update();
9656 return;
9657 }
9658
9659 if (!get() || get<RooArgList>()) {
9660 // is a group draw all the submembers
9661 browse();
9662 int _size = 0;
9663 // int _size = _channels.size(); // size(); if (find("!.vars")) _size--;
9664 for (auto &_v : *this) {
9665 if (_v->IsHidden())
9666 continue;
9667 if (strcmp(GetName(), ".vars") == 0) {
9668 // auto hide obs and "1" and const var
9669 if (_v->get<RooAbsArg>()->getAttribute("obs"))
9670 continue;
9671 if (strcmp(_v->get()->GetName(), "1") == 0 || strcmp(_v->get()->GetName(), "ONE") == 0 ||
9672 TString(_v->get()->GetName()).BeginsWith("binWidth_"))
9673 continue;
9674 if (_v->get()->InheritsFrom("RooConstVar"))
9675 continue;
9676 }
9677 TString s(_v->GetName());
9678 if (s.BeginsWith(".") || s.BeginsWith("!"))
9679 continue;
9680 _size++;
9681 }
9682 if (!hasSame) {
9683 clearPad();
9684 pad->SetBorderSize(0);
9685 dynamic_cast<TPad *>(pad)->DivideSquare(_size, 1e-9, 1e-9);
9686 }
9687 int i = 0;
9688 for (auto &_v : *this) {
9689 if (_v->IsHidden())
9690 continue;
9691 if (strcmp(GetName(), ".vars") == 0) {
9692 // auto hide obs and "1" and const var
9693 if (_v->get<RooAbsArg>()->getAttribute("obs"))
9694 continue;
9695 if (strcmp(_v->get()->GetName(), "1") == 0 || strcmp(_v->get()->GetName(), "ONE") == 0 ||
9696 TString(_v->get()->GetName()).BeginsWith("binWidth_"))
9697 continue;
9698 if (_v->get()->InheritsFrom("RooConstVar"))
9699 continue;
9700 }
9701 TString s(_v->GetName());
9702 if (s.BeginsWith(".") || s.BeginsWith("!"))
9703 continue;
9704 pad->cd(++i);
9705 gPad->SetName(s);
9706 if (!hasSame && _size > 1 && (gStyle->GetTitleFont("Y") % 10) == 3)
9707 gPad->SetLeftMargin(std::min(gPad->GetLeftMargin() * (1. / gPad->GetWNDC()), 0.3));
9708 _v->Draw(opt);
9709 // pad->Modified();//pad->Update();
9711 }
9712 pad->cd(0);
9713 gPad->Modified();
9714 // gPad->Update();
9715 return;
9716 }
9717
9718 if (get()->InheritsFrom("RooProdPdf")) {
9719 // draw the main pdf, if there is one...
9720 auto _mainChild = mainChild();
9721 if (_mainChild) {
9722 _mainChild.Draw(opt);
9723 gPad->SetName(GetName());
9724 return;
9725 }
9726 }
9727
9728 if (auto fr = get<RooFitResult>(); fr) {
9729 if (sOpt.Contains("corr")) {
9730 // do correlation matrix
9731
9732 auto hist = fr->correlationHist(fr->GetName());
9733 hist->SetTitle(fr->GetTitle());
9734 hist->SetBit(kCanDelete);
9735 hist->Scale(100);
9736 hist->SetStats(false);
9737 hist->SetDirectory(nullptr);
9739 gStyle->SetPaintTextFormat(".1f");
9740 hist->GetXaxis()->SetTickSize(0);
9741 hist->GetYaxis()->SetTickSize(0);
9742 hist->SetMinimum(-100);
9743 hist->Draw(sOpt);
9745 gPad->SetGrid(1, 1);
9746 gPad->SetLogy(0);
9747 gPad->SetLogx(0);
9748 return;
9749 }
9750
9751 if (sOpt.Contains("brakdown")) { // e will have been removed above
9752
9753 // breakdown is quadrature difference between total error and conditional error
9754 // group by 'group' attribute
9755
9756 std::string poiName;
9757 if (sOpt.Contains("brakdown:")) {
9758 TString sOpt3(opt);
9759 poiName = sOpt3(sOpt3.Index("breakdown:") + 10, sOpt3.Length());
9760 } else {
9761 std::unique_ptr<RooAbsCollection> _poi(fr->floatParsFinal().selectByAttrib("poi", true));
9762 if (_poi->empty()) {
9763 throw std::runtime_error("No floating poi in the fit");
9764 } else if (_poi->size() != 1) {
9765 throw std::runtime_error("Multiple poi in the fit");
9766 }
9767 poiName = _poi->first()->GetName();
9768 }
9769 RooRealVar *poi = dynamic_cast<RooRealVar *>(fr->floatParsFinal().find(poiName.c_str()));
9770 if (!poi) {
9771 throw std::runtime_error(TString::Format("Cannot find parameter %s", poiName.c_str()));
9772 }
9773 std::set<std::string> groups;
9774 for (auto p : fr->floatParsFinal()) {
9775 if (p == poi) {
9776 continue;
9777 } else if (p->getStringAttribute("group")) {
9778 groups.insert(p->getStringAttribute("group"));
9779 } else {
9780 groups.insert(p->GetTitle());
9781 }
9782 }
9783
9784 auto roundedVal = xRooFit::matchPrecision(std::pair(poi->getVal(), poi->getError()));
9785
9786 TPie *pie = new TPie(TString::Format("breakdown:%s", poi->GetName()),
9787 TString::Format("%s: %g #pm %g", poi->GetTitle(), roundedVal.first, roundedVal.second),
9788 groups.size() + 1);
9789
9790 // for display of errors will go to one extra dp ...
9791 roundedVal.second *= .1;
9792
9793 // do breakdown by removing parameters in blocks according to groups and seeing impact on variance
9794 // this will give the correct sum but will be order-dependent if there are correlations between
9795 // groups. therefore we will stick with group-by-group
9796 // RooArgList pars(fr->floatParsFinal()); // pars to not condition on
9797 // double variance = pow(dynamic_cast<RooRealVar*>(poi)->getError(),2);
9798 int i = 0;
9799 for (auto group : groups) {
9800 RooArgList pars(fr->floatParsFinal()); // pars to not condition on
9801 double variance = pow(dynamic_cast<RooRealVar *>(poi)->getError(), 2);
9802 for (auto p : fr->floatParsFinal()) {
9803 if (p == poi) {
9804 continue;
9805 } else if ((p->getStringAttribute("group") && group == p->getStringAttribute("group")) ||
9806 (!p->getStringAttribute("group") && group == p->GetTitle())) {
9807 // conditioning on this parameter ... remove from pars list
9808 pars.remove(*p);
9809 }
9810 }
9811 int idx = pars.index(poiName.c_str());
9812 double reducedVar = fr->conditionalCovarianceMatrix(pars)(idx, idx);
9813 if (reducedVar > variance) {
9814 Warning("Draw", "breakdown group %s variance bigger than preceding?", group.c_str());
9815 pie->SetEntryVal(i, 0);
9816 pie->SetEntryLabel(i, TString::Format("%s: NaN", group.c_str()));
9817 } else {
9818 pie->SetEntryVal(i, variance - reducedVar);
9820 std::pair(sqrt(variance - reducedVar), roundedVal.second)); // r.first will be the rounded error
9821 if (r.first > 0) {
9822 pie->SetEntryLabel(i, TString::Format("%s: %g", group.c_str(), r.first));
9823 } else {
9824 pie->SetEntryLabel(i, group.c_str()); // suppress labels for negligible errors.
9825 }
9826 }
9827 pie->SetEntryFillColor(i, TColor::GetColorPalette(TColor::GetNumberOfColors() * i / pie->GetEntries()));
9828 // variance = reducedVar;
9829 i++;
9830 }
9831 // remaining variance is statistical=
9832 double variance = fr->conditionalCovarianceMatrix(*poi)(0, 0);
9833 auto r =
9834 xRooFit::matchPrecision(std::pair(sqrt(variance), roundedVal.second)); // r.first will be the rounded error
9835 pie->SetEntryVal(i, variance);
9836 pie->SetEntryLabel(i, TString::Format("stat: %g", r.first));
9837 pie->SetEntryFillColor(i, TColor::GetColorPalette(TColor::GetNumberOfColors() * i / pie->GetEntries()));
9838 pie->SetBit(kCanDelete);
9839 pie->SetRadius(0.17);
9840 pie->SetTextSize(gStyle->GetTitleYSize());
9841 pie->Draw("NOL");
9842 return;
9843 }
9844
9845 // plot pull or impact
9847 out->SetName(TString::Format("%s_pull", fr->GetName()));
9848 out->SetTitle("Fit Result Pulls");
9849 std::vector<TString> graphLabels;
9851 ugraph->SetName(TString::Format("%s_pull_unconstrained", fr->GetName()));
9852 ugraph->SetTitle("Fit Result Pulls");
9853 std::vector<TString> ugraphLabels;
9854 std::map<std::string, double> scale;
9855 std::map<std::string, double> offset;
9856 for (auto &p : fr->floatParsFinal()) {
9857 auto _v = dynamic_cast<RooRealVar *>(p);
9858 if (!_v)
9859 continue;
9860
9861 if (std::isnan(_v->getErrorHi()) || std::isnan(_v->getErrorLo())) {
9862 Warning("Draw", "%s error is invalid", _v->GetName());
9863 }
9864
9865 // need to get constraint mean and error parameters ....
9866 // look for normal gaussian and poisson cases
9867 double prefitError = 0;
9868 double prefitVal = 0;
9869 double customScale = 0;
9870 if (auto ip =
9871 dynamic_cast<RooRealVar *>(fr->floatParsInit().find(p->GetName()))) { // handles if no prefit available
9872 prefitError = ip->getError();
9873 prefitVal = ip->getVal();
9874 };
9875
9876 std::shared_ptr<xRooNode> pConstr;
9877 if (fParent && fParent->getObject<RooRealVar>(p->GetName())) {
9878 auto _vv = fParent->getObject<RooRealVar>(p->GetName());
9879 if (_vv->hasRange("pullScale")) {
9880 customScale = (_vv->getMax("pullScale") - _vv->getMin("pullScale")) / 2.;
9881 }
9882 auto _constr = xRooNode(_vv, *this).constraints();
9883 for (auto &c : _constr) {
9884 if (c->get<RooPoisson>() || c->get<RooGaussian>()) {
9885 // require parameter to be a direct server of the constraint pdf to count if its a gaussian
9886 bool isServer = true;
9887 if (c->get<RooGaussian>()) {
9888 isServer = false;
9889 for (auto s : c->get<RooAbsArg>()->servers()) {
9890 if (strcmp(s->GetName(), p->GetName()) == 0) {
9891 isServer = true;
9892 break;
9893 }
9894 }
9895 }
9896 if (isServer) {
9897 pConstr = c;
9898 break;
9899 }
9900 }
9901 }
9902 }
9903 if (pConstr) {
9904
9905 // there will be 3 deps, one will be this par, the other two are the mean and error (or error^2 in case of
9906 // poisson
9907
9908 // std::cout << p->GetName() << " extracted " << prefitVal << " " << prefitError << " from ";
9909 // pConstr->deps().Print();
9910 pConstr->browse();
9911 if (pConstr->get<RooPoisson>() && pConstr->find(".x")) {
9912 std::string xName = pConstr->find(".x")->get()->GetName();
9913 prefitVal = pConstr->find(".x")->get<RooAbsReal>()->getVal();
9914 for (auto &_d : pConstr->vars()) {
9915 if (strcmp(p->GetName(), _d->get()->GetName()) == 0)
9916 continue;
9917 if (xName == _d->get()->GetName())
9918 continue;
9919 if (_d->get<RooAbsReal>()->getVal())
9920 prefitError = _d->get<RooAbsReal>()->getVal();
9921 }
9922 // prefitVal will be the global observable value, need to divide that by tau
9924 // prefiterror will be tau ... need 1/sqrt(tau) for error
9925 prefitError = 1. / sqrt(prefitError);
9926 } else if (auto _g = pConstr->get<RooGaussian>(); _g) {
9927 prefitError =
9928 (pConstr->find(".sigma")) ? pConstr->find(".sigma")->get<RooAbsReal>()->getVal() : prefitError;
9929 prefitVal =
9930 (pConstr->find(".x")) ? pConstr->find(".x")->get<RooAbsReal>()->getVal() : 0; // usually the globs
9931 if (pConstr->find(".x") &&
9932 strcmp(p->GetName(), pConstr->find(".x")->get<RooAbsReal>()->GetName()) == 0) {
9933 // hybrid construction case,
9934 prefitVal = pConstr->find(".mean")->get<RooAbsReal>()->getVal();
9935 }
9936 }
9937
9938 if (customScale)
9940 if (prefitError == 0) {
9941 Warning("Draw", "failed to determine prefit error of %s, using post-fit error", p->GetName());
9942 prefitError = _v->getError();
9943 }
9944 out->SetPoint(out->GetN(), out->GetN(), (_v->getVal() - prefitVal) / prefitError);
9945 out->SetPointError(out->GetN() - 1, 0, 0, (-_v->getErrorLo()) / prefitError,
9946 (_v->getErrorHi()) / prefitError);
9947 graphLabels.push_back(p->GetName());
9948 scale[p->GetName()] = prefitError;
9949 offset[p->GetName()] = prefitVal;
9950 } else if (!fParent) {
9951 // no parent to determine constraints from ... prefitError=0 will be the unconstrained ones
9952 if (customScale)
9954 if (prefitError == 0) {
9955 // uses range of var
9956 prefitError = (std::max({_v->getMax() - _v->getVal(), _v->getVal() - _v->getMin(), 4.}) / 4);
9957 ugraph->SetPoint(ugraph->GetN(), ugraph->GetN(), (_v->getVal() - prefitVal) / prefitError);
9958 ugraph->SetPointError(ugraph->GetN() - 1, 0, 0, (-_v->getErrorLo()) / prefitError,
9959 (_v->getErrorHi()) / prefitError);
9960 ugraphLabels.push_back(p->GetName());
9961 } else {
9962 out->SetPoint(out->GetN(), out->GetN(), (_v->getVal() - prefitVal) / prefitError);
9963 out->SetPointError(out->GetN() - 1, 0, 0, (-_v->getErrorLo()) / prefitError,
9964 (_v->getErrorHi()) / prefitError);
9965 graphLabels.push_back(p->GetName());
9966 }
9967 scale[p->GetName()] = prefitError;
9968 offset[p->GetName()] = prefitVal;
9969
9970 } else {
9971 // unconstrained (or at least couldn't determine constraint) ... use par range if no prefit error
9972 if (customScale)
9974 if (prefitError == 0) {
9975 prefitError = (std::max({_v->getMax() - _v->getVal(), _v->getVal() - _v->getMin(), 4.}) / 4);
9976 }
9977 ugraph->SetPoint(ugraph->GetN(), ugraph->GetN(), (_v->getVal() - prefitVal) / prefitError);
9978 ugraph->SetPointError(ugraph->GetN() - 1, 0, 0, (-_v->getErrorLo()) / prefitError,
9979 (_v->getErrorHi()) / prefitError);
9980 ugraphLabels.push_back(p->GetName());
9981 scale[p->GetName()] = prefitError;
9982 offset[p->GetName()] = prefitVal;
9983 }
9984 }
9985 auto graph = out;
9986
9987 // append ugraph points to end of graph
9988 for (int i = 0; i < ugraph->GetN(); i++)
9989 ugraph->SetPointX(i, i + graph->GetN());
9990 int nUnconstrained = ugraph->GetN();
9991 TList tmpList;
9992 tmpList.SetName("tmpList");
9993 tmpList.Add(ugraph);
9994 graph->Merge(&tmpList);
9995 tmpList.RemoveAll();
9996 delete ugraph;
9997 for (auto &l : ugraphLabels) {
9998 graphLabels.push_back(l);
9999 }
10000
10001 graph->SetBit(kCanDelete);
10002 graph->SetMarkerStyle(20);
10003 graph->SetMarkerSize(0.5);
10004
10005 graph->SetMaximum(4);
10006 graph->SetMinimum(-4);
10007
10008 bool doHorizontal =
10009 (!sOpt.Contains("impact") && sOpt.Contains("v")) || (sOpt.Contains("impact") && !sOpt.Contains("himpact"));
10010
10011 std::vector<std::pair<double, std::string>> covariances;
10012 /*double poiError = 0;*/ std::string poiName;
10013 double maxImpact = 0;
10014 if (sOpt.Contains("impact")) {
10015 if (sOpt.Contains("impact:")) {
10016 TString sOpt3(opt);
10017 poiName = sOpt3(sOpt3.Index("impact:") + 7, sOpt3.Length());
10018 } else {
10019 std::unique_ptr<RooAbsCollection> _poi(fr->floatParsFinal().selectByAttrib("poi", true));
10020 if (_poi->empty()) {
10021 throw std::runtime_error("No floating poi in the fit");
10022 } else if (_poi->size() != 1) {
10023 throw std::runtime_error("Multiple poi in the fit");
10024 }
10025 poiName = _poi->first()->GetName();
10026 }
10027 RooAbsArg *poi = fr->floatParsFinal().find(poiName.c_str());
10028 if (!poi) {
10029 throw std::runtime_error(TString::Format("Cannot find parameter %s", poiName.c_str()));
10030 }
10031 size_t poiIdx = fr->floatParsFinal().index(*poi);
10032 // put parameters in order of impact on the poi
10033
10034 // impact is regression coefficient * npError
10035 // relevant regression coefficient is cov / (npVariance)
10036 // i.e. DeltaX/sigmaX = [cov(X,Y)/(sigmaXsigmaY)]DeltaY/sigmaY
10037 // ... DeltaX = [cov(X,Y)/(sigmaY^2)]DeltaY
10038 // if DeltaY is just sigmaY then DeltaX = cov(X,Y)/sigmaY
10039
10040 for (auto &label : graphLabels) {
10041 covariances.emplace_back(fr->covarianceMatrix()(poiIdx, fr->floatParsFinal().index(label)) /
10042 dynamic_cast<RooRealVar *>(fr->floatParsFinal().find(label))->getError(),
10043 label);
10044 }
10045 std::sort(covariances.begin(), covariances.end(),
10046 [&](std::pair<double, std::string> i, std::pair<double, std::string> j) {
10047 return doHorizontal ? (std::abs(i.first) < std::abs(j.first))
10048 : (std::abs(i.first) > std::abs(j.first));
10049 });
10050
10052 std::vector<TString> sortedLabels;
10053 maxImpact = (doHorizontal) ? covariances.back().first
10054 : covariances.front().first; // note: max impact is likely to be self variance
10055 for (auto &c : covariances) {
10056 if (c.second == poi->GetName()) {
10057 // poiError = sqrt(c.first);
10058 continue; // skip self
10059 }
10060 c.first *= 4. / (maxImpact * 1.2);
10061 sortedLabels.push_back(c.second);
10062 size_t i = 0;
10063 for (; i < graphLabels.size(); i++) {
10064 if (graphLabels[i] == c.second) {
10065 break;
10066 }
10067 }
10068 sortedGraph.AddPoint(sortedGraph.GetN(), graph->GetPointY(i));
10069 sortedGraph.SetPointError(sortedGraph.GetN() - 1, 0, 0, graph->GetErrorYlow(i), graph->GetErrorYhigh(i));
10070 }
10071 graph->Set(0);
10073 tmpList2.SetName("tmpList");
10074 tmpList2.Add(&sortedGraph);
10075 graph->Merge(&tmpList2);
10076 tmpList2.RemoveAll();
10078 graph->SetTitle("Fit Result Impact");
10079 }
10080
10081 // create a framing histogram
10082 TH2D *hist;
10083 if (doHorizontal) {
10084 hist = new TH2D(GetName(), fr->GetTitle(), 100, -4, 4, std::max(graph->GetN(), 1), -0.5,
10085 std::max(graph->GetN(), 1) - 0.5);
10086 int i = 1;
10087 for (auto &l : graphLabels) {
10088 hist->GetYaxis()->SetBinLabel(i++, l);
10089 }
10090 if (!graphLabels.empty())
10091 hist->GetYaxis()->LabelsOption("v");
10092 hist->GetXaxis()->SetTitle("(#hat{#theta}-#theta_{i})/#sigma_{i}");
10093 } else {
10094 hist = new TH2D(GetName(), fr->GetTitle(), std::max(graph->GetN(), 1), -0.5, std::max(graph->GetN(), 1) - 0.5,
10095 100, -4, 4);
10096 int i = 1;
10097 for (auto &l : graphLabels) {
10098 hist->GetXaxis()->SetBinLabel(i++, l);
10099 }
10100 if (!graphLabels.empty())
10101 hist->GetXaxis()->LabelsOption("v");
10102 hist->GetYaxis()->SetNdivisions(8, 0, 0);
10103 hist->GetYaxis()->SetTitle("(#hat{#theta}-#theta_{i})/#sigma_{i}");
10104 }
10105 hist->SetStats(false);
10106 hist->SetDirectory(nullptr);
10107 hist->SetBit(kCanDelete);
10108 auto histCopy = dynamic_cast<TH1 *>(hist->Clone(".axis"));
10109 histCopy->SetDirectory(nullptr);
10110 histCopy->SetBit(kCanDelete);
10111 auto _axis = (doHorizontal ? histCopy->GetYaxis() : histCopy->GetXaxis());
10112
10113 /*
10114 auto t = TH1::AddDirectoryStatus();
10115 TH1::AddDirectory(false);
10116 auto hist = new TH1F(TString::Format(".%s_pullFrame", GetName()), fr->GetTitle(), std::max(graph->GetN(),
10117 1), -0.5, std::max(graph->GetN(), 1) - 0.5); hist->SetStats(false); TH1::AddDirectory(t);
10118 hist->SetBit(kCanDelete);
10119 */
10120 // auto hist = graph->GetHistogram();
10121 graph->GetHistogram()->GetXaxis()->Set(std::max(graph->GetN(), 1), -0.5, std::max(graph->GetN(), 1) - 0.5);
10122 for (int ii = 1; ii <= _axis->GetNbins(); ii++) {
10123 graph->GetHistogram()->GetXaxis()->SetBinLabel(ii, _axis->GetBinLabel(ii));
10124 }
10125 // int i = 1;
10126 // for (auto &l : graphLabels) {
10127 // hist->GetXaxis()->SetBinLabel(i++, l);
10128 // }
10129 // hist->SetMaximum(4);
10130 // hist->SetMinimum(-4);
10131 // if (graph->GetN())
10132 // hist->GetXaxis()->LabelsOption("v");
10133 // hist->GetYaxis()->SetNdivisions(8, 0, 0);
10134 // hist->GetYaxis()->SetTitle("(#hat{#theta}-#theta_{i})/#sigma_{i}");
10135 clearPad();
10136 // create a new pad because adjust the margins ...
10137 auto oldPad = gPad;
10138 gPad->Divide(1, 1, 1e-9, 1e-9);
10139 gPad->cd(1);
10140
10141 if (doHorizontal) {
10142 gPad->SetLeftMargin(0.4);
10143 } else {
10144 gPad->SetBottomMargin(0.4);
10145 }
10146
10147 auto pNamesHist = dynamic_cast<TH1F *>(graph->GetHistogram()->Clone("scales")); // used by interactive "pull" plot
10148 pNamesHist->Sumw2();
10149 pNamesHist->SetDirectory(nullptr);
10150
10151 for (int ii = 1; ii <= graph->GetN(); ii++) { // use graph->GetN() to protect against the 0 pars case
10152 auto _p = fr->floatParsFinal().find(_axis->GetBinLabel(ii));
10153 pNamesHist->SetBinContent(ii, offset[_p->GetName()]);
10154 pNamesHist->SetBinError(ii, scale[_p->GetName()]);
10155 _axis->SetBinLabel(ii, strlen(_p->GetTitle()) ? _p->GetTitle() : _p->GetName());
10156 }
10157
10158 // hist->Draw(); -- now just draw the graph
10159
10160 if (!sOpt.Contains("impact")) {
10161 for (int ii = 2; ii >= 1; ii--) {
10162 auto pullBox = new TGraphErrors;
10163 pullBox->SetName(TString::Format("%dsigmaBand", ii));
10164 pullBox->SetBit(kCanDelete);
10165 pullBox->SetPoint(0, (doHorizontal) ? -ii : -0.5, (doHorizontal) ? -0.5 : 0);
10166 pullBox->SetPoint(1, (doHorizontal) ? ii : (_axis->GetNbins() - 0.5 - nUnconstrained),
10167 (doHorizontal) ? -0.5 : 0);
10168 pullBox->SetPointError(0, 0, (doHorizontal) ? (_axis->GetNbins() - nUnconstrained) : ii);
10169 pullBox->SetPointError(1, 0, (doHorizontal) ? (_axis->GetNbins() - nUnconstrained) : ii);
10170 pullBox->SetFillColor((ii == 2) ? kYellow : kGreen);
10171 hist->GetListOfFunctions()->Add(pullBox, "3"); // pullBox->Draw("3");
10172 }
10173 auto pullLine = new TGraph;
10174 pullLine->SetName("0sigmaLine");
10175 pullLine->SetBit(kCanDelete);
10176 pullLine->SetPoint(0, -0.5, 0);
10177 pullLine->SetPoint(1, _axis->GetNbins() - 0.5, 0);
10178 pullLine->SetLineStyle(2);
10179 pullLine->SetEditable(false);
10180 hist->GetListOfFunctions()->Add(pullLine, "l"); // pullLine->Draw("l");
10181
10182 // also draw vertical line separating constrained from unconstrained, if necessary
10183 if (nUnconstrained > 0) {
10184 pullLine = new TGraph;
10185 pullLine->SetName("dividerLine");
10186 pullLine->SetBit(kCanDelete);
10187 pullLine->SetPoint(0, graph->GetN() - 0.5 - nUnconstrained, -100);
10188 pullLine->SetPoint(1, graph->GetN() - 0.5 - nUnconstrained, 100);
10189 pullLine->SetLineStyle(2);
10190 pullLine->SetEditable(false);
10191 hist->GetListOfFunctions()->Add(pullLine, "l"); // pullLine->Draw("l");
10192 }
10193
10194 // and draw a pave with fr status info
10195 TPaveText *pave =
10196 new TPaveText(gPad->GetLeftMargin(), 1. - gPad->GetTopMargin(), 1. - gPad->GetRightMargin(), 0.98, "NDCNB");
10197 pave->SetFillStyle(0);
10198 pave->SetBorderSize(0);
10199 pave->SetMargin(0.);
10200 pave->SetName("status");
10201 pave->SetTextAlign(31);
10202 pave->AddText(TString::Format("minNLL: %g edm: %g", fr->minNll(), fr->edm()));
10203 std::string covQualTxt;
10204 switch (fr->covQual()) {
10205 case -1: covQualTxt = "Unknown"; break;
10206 case 0: covQualTxt = "Not calculated"; break;
10207 case 1: covQualTxt = "Approximate"; break;
10208 case 2: covQualTxt = "Forced Positive-Definite"; break;
10209 case 3: covQualTxt = "Accurate"; break;
10210 }
10211 pave->AddText(TString::Format("Cov. Quality: %d (%s)", fr->covQual(), covQualTxt.c_str()))
10212 ->SetTextColor((fr->covQual() == 3) ? kBlack : kRed);
10213
10214 std::string statusCodes;
10215 for (unsigned int i = 0; i < fr->numStatusHistory(); i++) {
10216 statusCodes += TString::Format(" %s = %d", fr->statusLabelHistory(i), fr->statusCodeHistory(i));
10217 }
10218 pave->AddText(statusCodes.c_str())->SetTextColor(fr->status() == 0 ? kBlack : kRed);
10219
10220 hist->GetListOfFunctions()->Add(pave);
10221
10222 } else {
10223 gPad->SetTicks(0, 0); // ensure mirrored ticks aren't drawn in this pad
10224
10225 if (doHorizontal) {
10226 // ensure canvas height big enough
10227 if (int(gPad->GetCanvas()->GetWh()) < pNamesHist->GetNbinsX() * 15) {
10228 gPad->GetCanvas()->SetCanvasSize(gPad->GetCanvas()->GetWw(), pNamesHist->GetNbinsX() * 15);
10229 }
10230 }
10231
10232 double factor = 475. / gPad->GetCanvas()->GetWh(); // Wh is the full canvas height, not window height
10233 gPad->SetTopMargin(gStyle->GetPadTopMargin() * factor); // fixed margin height
10234 gPad->SetBottomMargin(gStyle->GetPadBottomMargin() * factor); // fixed margin height
10235
10236 TGaxis *axis =
10237 new TGaxis(_axis->GetXmin(), -4, _axis->GetXmin(), 4, -1.2 * maxImpact, 1.2 * maxImpact, 510, "-S");
10238
10239 if (doHorizontal) {
10240 // _axis->SetLabelSize(
10241 // (_axis->GetLabelFont() % 10 > 2)
10242 // ? (20 / factor)
10243 // : ((gPad->AbsPixeltoY(0) - gPad->AbsPixeltoY(20 / factor)) / (gPad->GetY2() -
10244 // gPad->GetY1())));
10245 // histCopy->GetXaxis()->SetTickLength(histCopy->GetXaxis()->GetTickLength() * factor);
10246 // hist->GetXaxis()->SetTickLength(hist->GetXaxis()->GetTickLength() * factor);
10247 // histCopy->GetYaxis()->SetTickLength(histCopy->GetYaxis()->GetTickLength() * factor);
10248 // hist->GetYaxis()->SetTickLength(hist->GetYaxis()->GetTickLength() * factor);
10249 // histCopy->GetXaxis()->SetTitleOffset(histCopy->GetXaxis()->GetTitleOffset() * factor);
10250 // histCopy->GetXaxis()->SetLabelOffset(histCopy->GetXaxis()->GetLabelOffset() * factor);
10251 // hist->GetXaxis()->SetTitleOffset(hist->GetXaxis()->GetTitleOffset() * factor);
10252 // hist->GetXaxis()->SetLabelOffset(hist->GetXaxis()->GetLabelOffset() * factor);
10253 // histCopy->GetXaxis()->SetTitleOffset(histCopy->GetXaxis()->GetTitleOffset() * factor);
10254 // histCopy->GetXaxis()->SetLabelOffset(histCopy->GetXaxis()->GetLabelOffset() * factor);
10255 }
10256 // copy attributes from TAxis to TGaxis
10257 axis->ImportAxisAttributes((doHorizontal) ? histCopy->GetXaxis() : histCopy->GetYaxis());
10258 axis->SetTitle(TString::Format("#Delta %s", fr->floatParsFinal().find(poiName.c_str())->GetTitle()));
10259
10260 // create impact bar charts
10261 for (int tt = 0; tt < 2; tt++) {
10262 auto impact = static_cast<TH1 *>(
10263 graph->GetHistogram()->Clone(TString::Format("%s_impact+", tt == 0 ? "prefit" : "postfit")));
10264 impact->SetDirectory(nullptr);
10265 impact->GetYaxis()->SetTitle(TString::Format("#Delta%s/#sigma", poiName.c_str()));
10266 impact->SetBarWidth(0.9);
10267 impact->SetBarOffset(0.05);
10268 impact->SetLineColor(kBlack);
10269 impact->SetFillColor(kAzure - 4);
10270 impact->SetFillStyle(tt == 0 ? 3013 : 1001);
10271 auto impact2 =
10272 static_cast<TH1 *>(impact->Clone(TString::Format("%s_impact-", tt == 0 ? "prefit" : "postfit")));
10273 impact2->SetDirectory(nullptr);
10274 impact2->SetFillColor(kCyan);
10275 for (int ii = 1; ii <= pNamesHist->GetNbinsX(); ii++) {
10276 for (auto &c : covariances) {
10277 if (c.second != pNamesHist->GetXaxis()->GetBinLabel(ii))
10278 continue;
10279 auto vv = dynamic_cast<RooRealVar *>(fr->floatParsFinal().find(c.second.c_str()));
10280 auto vv_init = dynamic_cast<RooRealVar *>(fr->floatParsInit().find(c.second.c_str()));
10281 impact->SetBinContent(ii, ((tt == 0 && !vv_init->hasError()) || !vv->hasError())
10282 ? 0.
10283 : c.first * vv->getError() / vv->getErrorHi() *
10284 (tt == 0 ? (vv_init->getErrorHi() / vv->getErrorHi()) : 1.));
10285 impact2->SetBinContent(ii, ((tt == 0 && !vv_init->hasError()) || !vv->hasError())
10286 ? 0.
10287 : c.first * vv->getError() / vv->getErrorLo() *
10288 (tt == 0 ? (vv_init->getErrorLo() / vv->getErrorLo()) : 1.));
10289 }
10290 }
10291 hist->GetListOfFunctions()->Add(impact, (doHorizontal) ? "hbarsamemin0" : "bsamey+");
10292 hist->GetListOfFunctions()->Add(impact2, (doHorizontal) ? "hbarsamemin0" : "bsamey+");
10293 }
10294 // add three lines
10295 for (int ii = -1; ii <= 1; ii++) {
10296 auto pullLine = new TGraph;
10297 pullLine->SetName(TString::Format("%dsigmaLine", ii));
10298 pullLine->SetBit(kCanDelete);
10299 pullLine->SetPoint(0, -0.5, ii);
10300 pullLine->SetPoint(1, hist->GetNbinsY() - 0.5, ii);
10301 pullLine->SetLineStyle(2);
10302 pullLine->SetEditable(false);
10303 hist->GetListOfFunctions()->Add(pullLine, "l");
10304 }
10305 hist->GetListOfFunctions()->Add(axis); // draw axis last
10306 TLegend *leg1 =
10307 new TLegend(0.02, doHorizontal ? (1. - 0.22 * factor) : 0.02, 0.27, (doHorizontal ? 1. : 0.24));
10308 leg1->SetFillStyle(0);
10309 leg1->SetBorderSize(0);
10310 leg1->SetMargin(0.25);
10311 leg1->SetNColumns(2);
10312
10313 leg1->SetTextSize(_axis->GetLabelSize());
10314 leg1->SetTextFont(_axis->GetLabelFont());
10315 leg1->AddEntry((TObject *)nullptr, "Hessian Pre-fit", "");
10316 leg1->AddEntry((TObject *)nullptr, "Impact:", "");
10317 leg1->AddEntry(hist->FindObject("prefit_impact+"), "#theta = #hat{#theta}+#Delta#theta", "f");
10318 leg1->AddEntry(hist->FindObject("prefit_impact-"), "#theta = #hat{#theta}-#Delta#theta", "f");
10319 leg1->AddEntry((TObject *)nullptr, "Hessian Post-fit", "");
10320 leg1->AddEntry((TObject *)nullptr, "Impact:", "");
10321 leg1->AddEntry(hist->FindObject("postfit_impact+"), "#theta = #hat{#theta}+#Delta#theta", "f");
10322 leg1->AddEntry(hist->FindObject("postfit_impact-"), "#theta = #hat{#theta}-#Delta#theta", "f");
10323
10324 hist->GetListOfFunctions()->Add(leg1);
10325 if (gStyle->GetOptTitle()) {
10326 histCopy->SetBit(TH1::kNoTitle);
10327 TPaveText *title =
10328 new TPaveText(gPad->GetLeftMargin(), 1. - gPad->AbsPixeltoY(14), 1. - gPad->GetRightMargin(), 1., "NDC");
10329 title->ConvertNDCtoPad();
10330 title->SetY1NDC(1. - gPad->GetTopMargin() * 0.6);
10331 title->SetY2NDC(1);
10332 title->SetTextSize(
10333 (title->GetTextFont() % 10 > 2)
10334 ? (14 / factor)
10335 : ((gPad->AbsPixeltoY(0) - gPad->AbsPixeltoY(10 / factor)) / (gPad->GetY2() - gPad->GetY1())));
10336 title->SetFillStyle(0);
10337 title->SetBorderSize(0);
10338 title->AddText(histCopy->GetTitle());
10339 hist->GetListOfFunctions()->Add(title);
10340 }
10341 }
10342
10343 graph->SetEditable(false);
10344 pNamesHist->SetLineWidth(0);
10345 pNamesHist->SetMarkerSize(0);
10346 pNamesHist->SetMarkerStyle(0);
10347 graph->GetListOfFunctions()->Add(pNamesHist, "same"); // graph->SetHistogram(pNamesHist);
10348 if (doHorizontal) {
10349
10350 // flip the graph and contained graphs
10351 for (int p = 0; p < graph->GetN(); p++) {
10352 graph->SetPoint(p, graph->GetPointY(p), graph->GetPointX(p));
10353 graph->SetPointError(p, graph->GetErrorYlow(p), graph->GetErrorYhigh(p), graph->GetErrorXlow(p),
10354 graph->GetErrorXhigh(p));
10355 }
10356 for (auto f : *hist->GetListOfFunctions()) {
10357 if (f->InheritsFrom("TH1")) {
10358 // f->Draw("hbarsamemin0");
10359 } /*else if (auto g2 = dynamic_cast<TGraphErrors *>(f)) {
10360 for (int p = 0; p < g2->GetN(); p++) {
10361 g2->SetPoint(p, g2->GetPointY(p), g2->GetPointX(p));
10362 g2->SetPointError(p, g2->GetErrorY(p), _axis->GetNbins());
10363 }
10364 //g2->Draw("3");
10365 } */
10366 else if (auto g = dynamic_cast<TGraph *>(f)) {
10367 for (int p = 0; p < g->GetN(); p++) {
10368 g->SetPoint(p, g->GetPointY(p), g->GetPointX(p));
10369 }
10370 // g->Draw("l");
10371 } else if (auto l = dynamic_cast<TLine *>(f)) {
10372 l->SetX1(l->GetY1());
10373 l->SetX2(l->GetY2());
10374 l->SetY1(_axis->GetXmax());
10375 l->SetY2(_axis->GetXmax());
10376 // l->Draw();
10377 }
10378 }
10379 }
10380
10381 graph->SetName("pulls");
10382 hist->GetListOfFunctions()->Add(graph, "z0p");
10383 // hist->GetListOfFunctions()->Add(histCopy->Clone(".axis"),(sOpt.Contains("impact") &&
10384 // !doHorizontal)?"axissamey+":"axissame"); // doesn't display right when zoom the axis
10385 if (!hasSame) {
10386 histCopy->Draw((sOpt.Contains("impact") && !doHorizontal)
10387 ? "axisy+"
10388 : "axis"); // draws the axis, called ".axis" for easy access
10389 }
10390 hist->Draw("same");
10391 //
10392 // if(sOpt.Contains("impact")) {
10393 // // make main object the histogram
10394 // auto h = (TH1*)graph->GetHistogram()->Clone("impact");
10395 // graph->GetListOfFunctions()->RemoveAll();
10396 // for(int ii=1;ii<=h->GetNbinsX();ii++) h->SetBinContent(ii,-4);
10397 // h->GetListOfFunctions()->Add(graph,"z0p");
10398 // h->Draw("hbar");
10399 // } else {
10400 // graph->Draw(sOpt.Contains("impact") ? "az0py+" : "az0p");
10401 // }
10402 auto hh = dynamic_cast<TH1 *>(histCopy->Clone(".axiscopy"));
10403 hh->SetDirectory(nullptr);
10404 hh->SetBit(kCanDelete);
10405 hh->Draw(
10406 (sOpt.Contains("impact") && !doHorizontal)
10407 ? "axissamey+"
10408 : "axissame"); // overlay axis again -- important is last so can remove if don't pad->Update before reclear
10409 gPad->Modified();
10410 oldPad->cd();
10411 // gPad->Update();
10412 return;
10413 }
10414
10415 if (get()->InheritsFrom("RooAbsData")) {
10416 auto s = parentPdf();
10417 if (s && s->get<RooSimultaneous>()) {
10418 // drawing dataset associated to a simultaneous means must find subpads with variation names
10419 // may not have subpads if drawning a "Yield" plot ...
10420 bool doneDraw = false;
10421 for (auto c : s->bins()) {
10422 auto _pad = dynamic_cast<TPad *>(gPad->GetPrimitive(c->GetName()));
10423 if (!_pad)
10424 continue; // channel was hidden?
10425 // attach as a child before calling datasets(), so that if this dataset is external to workspace it is
10426 // included still attaching the dataset ensures dataset reduction for the channel is applied
10427 c->push_back(std::make_shared<xRooNode>(*this));
10428 auto ds = c->datasets().find(GetName());
10429 c->resize(c->size() - 1); // remove the child we attached
10430 if (!ds) {
10431 std::cout << " no ds " << GetName() << " - this should never happen!" << std::endl;
10432 continue;
10433 }
10434 auto tmp = gPad;
10435 _pad->cd();
10436 ds->Draw(opt);
10437 doneDraw = true;
10438 tmp->cd();
10439 }
10440 if (doneDraw) {
10441 gPad->Modified();
10442 return;
10443 }
10444 }
10445
10446 if (!s && hasSame) {
10447 // draw onto all subpads with = in the name
10448 // if has no such subpads, draw onto this pad
10449 bool doneDraw = false;
10450 for (auto o : *gPad->GetListOfPrimitives()) {
10451 if (auto p = dynamic_cast<TPad *>(o); p && TString(p->GetName()).Contains('=')) {
10452 auto _tmp = gPad;
10453 p->cd();
10454 Draw(opt);
10455 _tmp->cd();
10456 doneDraw = true;
10457 }
10458 }
10459 if (doneDraw) {
10460 gPad->Modified();
10461 return;
10462 }
10463 }
10464
10465 auto dataGraph = BuildGraph(v, false, (!s && hasSame) ? gPad : nullptr);
10466 if (!dataGraph)
10467 return;
10468
10469 dataGraph->SetBit(kCanDelete); // will be be deleted when pad is cleared
10470 dataGraph->SetMarkerSize(dataGraph->GetMarkerSize() * gPad->GetWNDC()); // scale marker sizes to pad size
10471
10472 if (s && !s->get<RooAbsPdf>()->canBeExtended()) {
10473 // normalize dataGraph to 1
10474 double tot = 0;
10475 for (int i = 0; i < dataGraph->GetN(); i++)
10476 tot += dataGraph->GetPointY(i);
10477 dataGraph->Scale(1. / tot);
10478 }
10479
10480 if (!hasSame) {
10481 clearPad();
10482 dataGraph->Draw("Az0p");
10483 addLegendEntry(dataGraph, strlen(dataGraph->GetTitle()) ? dataGraph->GetTitle() : GetName(), "pEX0");
10484 gPad->Modified();
10485 // gPad->Update();
10486 return;
10487 }
10488
10489 bool noPoint = false;
10490 if (v && dynamic_cast<RooAbsArg *>(v)->getAttribute("global") && dataGraph->GetN() == 1) {
10491 // global observable ... if graph has only 1 data point line it up on the histogram value
10492 for (auto o : *gPad->GetListOfPrimitives()) {
10493 if (auto h = dynamic_cast<TH1 *>(o);
10494 h && strcmp(h->GetXaxis()->GetName(), dynamic_cast<TObject *>(v)->GetName()) == 0) {
10495 dataGraph->SetPointY(0, h->Interpolate(dataGraph->GetPointX(0)));
10496 noPoint = true;
10497 break;
10498 }
10499 }
10500 }
10501
10502 if (auto _pad = dynamic_cast<TPad *>(gPad->FindObject("auxPad")); _pad) {
10503 if (auto h = dynamic_cast<TH1 *>(_pad->GetPrimitive("auxHist")); h) {
10504 TString histName = h->GetTitle(); // split it by | char
10505 TString histType = histName(histName.Index('|') + 1, histName.Length());
10506 histName = histName(0, histName.Index('|'));
10507 if (auto mainHist = dynamic_cast<TH1 *>(gPad->GetPrimitive(histName));
10508 mainHist && auxFunctions.find(h->GetYaxis()->GetTitle()) != auxFunctions.end()) {
10509 // decide what to do based on title of auxHist (previously used name of y-axis but that changed axis
10510 // behaviour) use title instead
10511 auto ratioGraph = dynamic_cast<TGraphAsymmErrors *>(dataGraph->Clone(dataGraph->GetName()));
10512 ratioGraph->SetBit(kCanDelete);
10513 for (int i = 0; i < ratioGraph->GetN(); i++) {
10514 double val = ratioGraph->GetPointY(i);
10515 int binNum = mainHist->FindFixBin(ratioGraph->GetPointX(i));
10516 double nom = mainHist->GetBinContent(binNum);
10517 double nomerr = mainHist->GetBinError(binNum);
10518 double yval =
10519 std::get<0>(auxFunctions[h->GetYaxis()->GetTitle()])(ratioGraph->GetPointY(i), nom, nomerr);
10520 double yup = std::get<0>(auxFunctions[h->GetYaxis()->GetTitle()])(val + ratioGraph->GetErrorYhigh(i),
10521 nom, nomerr) -
10522 yval;
10523 double ydown = yval - std::get<0>(auxFunctions[h->GetYaxis()->GetTitle()])(
10524 val - ratioGraph->GetErrorYlow(i), nom, nomerr);
10525 if (!std::isnan(yval)) {
10526 ratioGraph->SetPointY(i, yval);
10527 if (!std::isnan(yup))
10528 ratioGraph->SetPointEYhigh(i, yup);
10529 if (!std::isnan(ydown))
10530 ratioGraph->SetPointEYlow(i, ydown);
10531 }
10532 }
10533 // remove the zero points
10534 int i = 0;
10535 while (i < ratioGraph->GetN()) {
10536 if (ratioGraph->GetPointY(i) == 0 && ratioGraph->GetErrorYhigh(i) == 0 &&
10537 ratioGraph->GetErrorYlow(i) == 0) {
10538 ratioGraph->RemovePoint(i);
10539 } else {
10540 i++;
10541 }
10542 }
10543 auto _tmpPad = gPad;
10544 _pad->cd();
10545 ratioGraph->Draw("z0psame");
10547 adjustYRange(minMax.first, minMax.second, h, std::get<1>(auxFunctions[h->GetYaxis()->GetTitle()]));
10548 _tmpPad->cd();
10549 }
10550 }
10551 }
10552
10553 dataGraph->Draw("z0p same");
10554 addLegendEntry((noPoint) ? nullptr : dataGraph, strlen(dataGraph->GetTitle()) ? dataGraph->GetTitle() : GetName(),
10555 noPoint ? "" : "pEX0");
10556
10557 auto minMax = graphMinMax(dynamic_cast<TGraphAsymmErrors *>(dataGraph));
10558 adjustYRange(minMax.first, minMax.second);
10559
10560 gPad->Modified();
10561 // gPad->Update();
10562 return;
10563 }
10564
10565 // auto _ax = GetXaxis();
10566 // auto v = (_ax) ? dynamic_cast<RooRealVar*>(/*possibleObs.first()*/_ax->GetParent()) : nullptr;
10567 // if (!v) { v = get<RooRealVar>(); } // self-axis
10568 // if (!v) return;
10569
10570 if (auto lv = get<RooAbsLValue>(); lv && fParent && fParent->get<RooAbsData>()) {
10571 // drawing an observable from a dataset ... build graph, and exit
10572 auto gr = fParent->BuildGraph(lv, true);
10574 gr->Draw(hasSame ? "P" : "AP");
10575 return;
10576 }
10577
10578 if (forceNames != "") {
10579 // drawing a force plot ... build nll and fill a histogram with force terms
10580 auto _dsets = datasets();
10581 bool _drawn = false;
10582 auto _coords = coords();
10583 auto _fr = fitResult();
10584 auto initPar = dynamic_cast<RooRealVar *>(_fr.get<RooFitResult>()->floatParsInit().find(forceNames));
10585 if (!initPar)
10586 return;
10587 std::vector<double> valuesToDo = {initPar->getVal()};
10588 if (initPar->hasError() || initPar->hasAsymError()) {
10589 valuesToDo.push_back(initPar->getVal() + initPar->getErrorLo());
10590 valuesToDo.push_back(initPar->getVal() + initPar->getErrorHi());
10591 }
10592 int ii = 0;
10593 for (auto valueToDo : valuesToDo) {
10594 ii++;
10595 for (auto &d : _dsets) {
10596 if (!d->get()->TestBit(1 << 20))
10597 continue;
10598 auto emptyHist = BuildHistogram(v, true);
10599 emptyHist->SetBit(kCanDelete);
10600 auto _obs = d->obs();
10601 auto x = _obs.find((v) ? dynamic_cast<TObject *>(v)->GetName() : emptyHist->GetXaxis()->GetName());
10602 auto _nll = nll(d);
10603 auto theData = d->get<RooAbsData>();
10604 int nevent = theData->numEntries();
10605 for (int i = 0; i < nevent; i++) {
10606 theData->get(i);
10607 bool _skip = false;
10608 for (const auto &_c : _coords) {
10609 if (auto cat = _c->get<RooAbsCategoryLValue>(); cat) {
10610 if (cat->getIndex() != theData->get()->getCatIndex(cat->GetName())) {
10611 _skip = true;
10612 break;
10613 }
10614 }
10615 }
10616 if (_skip)
10617 continue;
10618
10619 if (x) {
10620 auto val = _nll.pars()->getRealValue(initPar->GetName());
10621 if (ii > 1)
10622 _nll.pars()->setRealValue(initPar->GetName(), valueToDo);
10623 auto nllVal = _nll.getEntryVal(i);
10624 _nll.pars()->setRealValue(initPar->GetName(), initPar->getVal());
10625 auto nllVal2 = _nll.getEntryVal(i);
10626 _nll.pars()->setRealValue(initPar->GetName(), val);
10627 emptyHist->Fill(x->get<RooAbsReal>()->getVal(), (nllVal2 - nllVal));
10628 }
10629 }
10630 // include the extendedTerm, distributed evenly over the bins
10631 // probably should be somehow dependent on data density though (i.e. bins with more data get more of it?)
10632 auto val = _nll.pars()->getRealValue(initPar->GetName());
10633 if (ii > 1)
10634 _nll.pars()->setRealValue(initPar->GetName(), valueToDo);
10635 auto _extTerm = _nll.extendedTermVal();
10636 _nll.pars()->setRealValue(initPar->GetName(), initPar->getVal());
10637 auto _extTerm2 = _nll.extendedTermVal();
10638 _nll.pars()->setRealValue(initPar->GetName(), val);
10639 for (int i = 1; i <= emptyHist->GetNbinsX(); i++) {
10640 emptyHist->SetBinContent(i,
10641 emptyHist->GetBinContent(i) + (_extTerm2 - _extTerm) / emptyHist->GetNbinsX());
10642 emptyHist->SetBinError(i, 0);
10643 }
10644 emptyHist->GetYaxis()->SetTitle("log (L(#theta)/L(#theta_{0}))");
10645 emptyHist->SetTitle(TString::Format("#theta = %g", (ii > 1) ? valueToDo : val));
10646 if (ii == 1)
10647 emptyHist->SetLineColor(kBlack);
10648 if (ii == 2) {
10649 emptyHist->SetLineColor(kRed);
10650 } else if (ii == 3) {
10651 emptyHist->SetLineColor(kBlue);
10652 }
10653 emptyHist->Draw(_drawn ? "same" : "");
10654 _drawn = true;
10655 }
10656 }
10657 return;
10658 }
10659
10660 auto rar = get<RooAbsReal>();
10661 const xRooNode *rarNode = this;
10662 if (!rar) {
10663 // draw a deleteable clone of the object we wrap (since we might own the object)
10664 get()->DrawClone(opt);
10665 return;
10666 }
10667 // RooAbsReal *sf = nullptr;
10668 if (get()->InheritsFrom("RooExtendPdf")) {
10669 browse();
10670 rarNode = find(".pdf").get();
10671 // rar = rarNode->get<RooAbsReal>();
10672 // sf = find(".n")->get<RooAbsReal>();
10673 }
10674
10675 if (!nostack && !hasOverlay &&
10676 (rarNode->get()->InheritsFrom("RooRealSumPdf") || rarNode->get()->InheritsFrom("RooAddPdf") ||
10677 (v && rarNode->get()->InheritsFrom("RooSimultaneous") &&
10678 strcmp(dynamic_cast<TObject *>(v)->GetName(), rarNode->get<RooSimultaneous>()->indexCat().GetName()) == 0))) {
10679 nostack = false;
10680 } else {
10681 // in all other cases, we do not build a stack
10682 nostack = true;
10683 }
10684
10685 auto h = BuildHistogram(v, false, hasErrorOpt, 1, 0, "", false, false, 0, nullptr, nostack, true /*setInterp*/);
10686 if (!h) {
10687 if (get()) {
10688 // draw a deleteable clone of the object we wrap (since we might own the object)
10689 get()->DrawClone(opt);
10690 }
10691 return;
10692 }
10693 h->SetBit(kCanDelete);
10694
10695 if (!v)
10696 v = getObject<RooAbsLValue>(h->GetXaxis()->IsAlphanumeric() ? h->GetXaxis()->GetTimeFormatOnly()
10697 : h->GetXaxis()->GetName())
10698 .get();
10699 RooAbsArg *vv = (v) ? dynamic_cast<RooAbsArg *>(v) : rar;
10700 if (h->GetXaxis()->IsAlphanumeric()) {
10701 // do this to get bin labels
10702 h->GetXaxis()->SetName("xaxis"); // WARNING -- this messes up anywhere we GetXaxis()->GetName()
10703 }
10704
10705 // get style now, before we mess with histogram title
10706 // auto _styleNode = styles(h);
10707
10708 if (rar->InheritsFrom("RooAbsPdf") && !(rar->InheritsFrom("RooRealSumPdf") || rar->InheritsFrom("RooAddPdf") ||
10709 rar->InheritsFrom("RooSimultaneous"))) {
10710 // append parameter values to title if has such
10711 RooArgSet s;
10712 rar->leafNodeServerList(&s);
10713 if (v)
10714 s.remove(*dynamic_cast<RooAbsArg *>(v));
10715 if (!s.empty()) {
10716 TString ss = h->GetTitle();
10717 ss += " [";
10718 bool first = true;
10719 for (auto _p : s) {
10720 auto _v = dynamic_cast<RooRealVar *>(_p);
10721 if (!_v)
10722 continue;
10723 if (!first)
10724 ss += ",";
10725 first = false;
10726 ss += TString::Format("%s=%g", strlen(_p->GetTitle()) ? _p->GetTitle() : _p->GetName(), _v->getVal());
10727 if (_v->hasError()) {
10728 ss += TString::Format("#pm %g", _v->getError());
10729 }
10730 }
10731 ss += "]";
10732 h->SetTitle(ss);
10733 }
10734 }
10735
10736 if (!hasSame) {
10737 if (obs().find(vv->GetName())) {
10738 gPad->SetGrid(0, 0);
10739 } else {
10740 gPad->SetGrid(1, 1);
10741 }
10742 }
10743 TString dOpt = h->GetOption();
10744 if (dOpt == "l")
10745 h->SetFillStyle(0);
10746 // // need to strip namespace to discount the "HistFactory" namespace classes from all being treated as binned
10747 // TString clNameNoNamespace = rar->ClassName();
10748 // clNameNoNamespace = clNameNoNamespace(clNameNoNamespace.Last(':') + 1, clNameNoNamespace.Length());
10749 // TString dOpt = (clNameNoNamespace.Contains("Hist") || vv->isCategory() || rar->isBinnedDistribution(*vv) ||
10750 // h->GetNbinsX() == 1 || rar->getAttribute("BinnedLikelihood") ||
10751 // (dynamic_cast<RooAbsRealLValue *>(vv) &&
10752 // std::unique_ptr<std::list<double>>(rar->binBoundaries(*dynamic_cast<RooAbsRealLValue *>(vv),
10753 // -std::numeric_limits<double>::infinity(),
10754 // std::numeric_limits<double>::infinity()))))
10755 // ? ""
10756 // : "LF2";
10757 // if (auto d = dynamic_cast<RooHistFunc *>(rar); d && !d->isBinnedDistribution(*vv) && h->GetNbinsX() != 1) {
10758 // dOpt = "LF2"; // hist func is interpolated, so draw it as such
10759 // }
10760 // if (dOpt == "LF2" && !components().empty()) {
10761 // // check if all components of dOpt are "Hist" type (CMS model support)
10762 // // if so then dOpt="";
10763 // bool allHist = true;
10764 // for (auto &s : components()) {
10765 // TString _clName = s->get()->ClassName();
10766 // _clName = _clName(_clName.Last(':') + 1, _clName.Length());
10767 // if (!(s->get() && _clName.Contains("Hist"))) {
10768 // allHist = false;
10769 // break;
10770 // }
10771 // }
10772 // if (allHist)
10773 // dOpt = "";
10774 // }
10775 //
10776 // if(dOpt=="LF2") {
10777 // // ensure any sub hists have lf2 option
10778 // TObjLink *lnk = h->GetListOfFunctions()->FirstLink();
10779 // while (lnk) {
10780 // if(auto hh = dynamic_cast<TH1*>(lnk->GetObject())) {
10781 // if(TString(hh->GetName())=="band" && TString(lnk->GetOption())=="e2same") {
10782 // lnk->SetOption("LF2 e3same");
10783 // } else if(TString(hh->GetName())=="nominal") {
10784 // lnk->SetOption("L same");
10785 // }
10786 // }
10787 // lnk = lnk->Next();
10788 // }
10789 // }
10790
10791 if (rar == vv && rar->IsA() == RooRealVar::Class()) {
10792 dOpt += "TEXT";
10793 // add a TExec to the histogram so that when edited it will propagate to var
10794 gROOT->SetEditHistograms(true);
10795 } else {
10796 gROOT->SetEditHistograms(false);
10797 }
10798
10799 if (hasSame) {
10800 dOpt += " same";
10801 } else {
10802 hAxis = h;
10803 }
10804
10805 if (dOpt.Contains("TEXT") || sOpt.Contains("text")) {
10806 // adjust marker size so text is good
10807 h->SetMarkerSize(gStyle->GetLabelSize("Z") / (0.02 * gPad->GetHNDC()));
10808 }
10809
10810 bool hasError(false);
10811 for (int i = 0; i < h->GetSumw2N(); i++) {
10812 if (h->GetSumw2()->At(i)) {
10813 hasError = true;
10814 break;
10815 }
10816 }
10817
10818 /** This doesn't seem necessary in at least 6.26 any more - pads seem adjusted on their own
10819 if (!hasSame && h->GetYaxis()->GetTitleFont()%10 == 2) {
10820 h->GetYaxis()->SetTitleOffset( gPad->GetLeftMargin() / gStyle->GetPadLeftMargin() );
10821 } */
10822 // don't this instead - dont want to leave as zero (auto) in case show aux plot
10823 if (!hasSame && h->GetYaxis()->GetTitleFont() % 10 == 2) {
10824 h->GetYaxis()->SetTitleOffset(1.);
10825 }
10826
10827 TH1 *errHist = nullptr;
10828
10829 if (!hasSame)
10830 clearPad();
10831
10832 if (rar == vv && rar->IsA() == RooRealVar::Class()) {
10833 // add a TExec to the histogram so that when edited it will propagate to var
10834 // h->GetListOfFunctions()->Add(h->Clone("self"),"TEXTHIST");
10835 dOpt = "TEXT";
10836 auto node = new xRooNode(*this);
10837 auto _hist = (errHist) ? errHist : h;
10838 auto hCopy = (errHist) ? nullptr : dynamic_cast<TH1 *>(h->Clone());
10839 if (hCopy) {
10840 hCopy->Reset();
10841 hCopy->Add(_hist);
10842 hCopy->SetDirectory(nullptr);
10843 }
10844 _hist->GetListOfFunctions()->Add(node);
10845 _hist->GetListOfFunctions()->Add(new TExec(
10846 ".update",
10848 "gROOT->SetEditHistograms(true);auto h = dynamic_cast<TH1*>(gPad->GetPrimitive(\"%s\")); if(h) { double "
10849 "range= h->GetMaximum()-h->GetMinimum(); if(auto n "
10850 "= dynamic_cast<xRooNode*>(h->GetListOfFunctions()->FindObject(\"%s\")); n && "
10851 "n->TestBit(TObject::kNotDeleted) && n->get<RooRealVar>()->getVal() != h->GetBinContent(1)) {"
10852 "h->SetBinContent(1, "
10853 "TString::Format(\"%%.2g\",int(h->GetBinContent(1)/(range*0.01))*range*0.01).Atof());n->SetContent( "
10854 "h->GetBinContent(1) ); for(auto pp : *h->GetListOfFunctions()) if(auto hh = "
10855 "dynamic_cast<TH1*>(pp))hh->SetBinContent(1,h->GetBinContent(1));} if(h->GetBinContent(1)==0.) "
10856 "h->SetBinContent(1,range*0.005); gPad->Modified();gPad->Update(); }",
10857 _hist->GetName(), node->GetName())));
10858 if (errHist) {
10859 errHist->GetListOfFunctions()->Add(h, "TEXT HIST same");
10860 errHist->SetFillColor(h->GetLineColor());
10861 } else {
10862 hCopy->SetBit(kCanDelete);
10863 hCopy->SetFillStyle(0);
10864 _hist->GetListOfFunctions()->Add(hCopy, "TEXT HIST same");
10865 //_hist->SetBinError(1, 0);
10866 }
10867 _hist->SetStats(false);
10868 // if (_hist->GetBinContent(1)==0.) _hist->SetBinContent(1,(_hist->GetMaximum()-_hist->GetMinimum())*0.005);
10869 _hist->Draw(); //_hist->Draw(((hasError) ? "e2" : ""));
10870 gPad->Modified();
10871 return;
10872 }
10873
10874 bool overlayExisted = false;
10875 if (hasOverlay) {
10876 h->SetName(TString::Format("%s%s", h->GetName(), overlayName.Data()));
10877 if (auto existing = dynamic_cast<TH1 *>(gPad->GetPrimitive(h->GetName())); existing) {
10878 existing->Reset();
10879 existing->Add(h);
10880 delete h;
10881 h = existing;
10882 overlayExisted = true;
10883 } else {
10884 TString oldStyle = (rar && rar->getStringAttribute("style")) ? rar->getStringAttribute("style") : "";
10885 h->SetTitle(overlayName);
10886 // for overlays will take style from current gStyle before overriding with personal style
10887 // this ensures initial style will be whatever gStyle is, rather than whatever ours is
10888 static_cast<TAttLine &>(*h) = *gStyle;
10889 static_cast<TAttFill &>(*h) = *gStyle;
10890 static_cast<TAttMarker &>(*h) = *gStyle;
10891 h->SetFillStyle(0); // explicit default for overlays will be transparent fill
10892
10893 // std::shared_ptr<TStyle> style; // use to keep alive for access from GetStyle below, in case
10894 // getObject has decided to return the owning ptr (for some reason) if
10895 // (!gROOT->GetStyle(h->GetTitle())) {
10896 // if ( (style = getObject<TStyle>(h->GetTitle())) ) {
10897 // // loaded style (from workspace?) so put in list and use that
10898 // gROOT->GetListOfStyles()->Add(style.get());
10899 // } else {
10900 // // create new style - gets put in style list automatically so don't have to delete
10901 // // acquire them so saved to workspaces for auto reload ...
10902 // style = acquireNew<TStyle>(h->GetTitle(),
10903 // TString::Format("Style for %s component", h->GetTitle()));
10904 // (TAttLine &) (*style) = *dynamic_cast<TAttLine *>(h);
10905 // (TAttFill &) (*style) = *dynamic_cast<TAttFill *>(h);
10906 // (TAttMarker &) (*style) = *dynamic_cast<TAttMarker *>(h);
10907 // gROOT->GetListOfStyles()->Add(style.get());
10908 // }
10909 // }
10910 // (TAttLine&)(*h) = *(gROOT->GetStyle(h->GetTitle()) ? gROOT->GetStyle(h->GetTitle()) : gStyle);
10911 // (TAttFill&)(*h) = *(gROOT->GetStyle(h->GetTitle()) ? gROOT->GetStyle(h->GetTitle()) : gStyle);
10912 // (TAttMarker&)(*h) = *(gROOT->GetStyle(h->GetTitle()) ? gROOT->GetStyle(h->GetTitle()) : gStyle);
10913 auto _styleNode = styles(h);
10914 rar->setStringAttribute("style", oldStyle == "" ? nullptr : oldStyle.Data()); // restores old style
10915 if (auto _style = _styleNode.get<TStyle>()) {
10916 (TAttLine &)(*h) = *_style;
10917 (TAttFill &)(*h) = *_style;
10918 (TAttMarker &)(*h) = *_style;
10919 }
10920 h->Draw(dOpt == "LF2" ? "e3" : dOpt);
10921 if (errHist) {
10922 errHist->SetTitle(overlayName);
10923 (TAttLine &)(*errHist) = *h;
10924 errHist->SetFillColor(h->GetLineColor());
10925 }
10926 }
10927 } else {
10928 // if (auto _style = _styleNode.get<TStyle>()) {
10929 // (TAttLine &)(*h) = *_style;
10930 // (TAttFill &)(*h) = *_style;
10931 // (TAttMarker &)(*h) = *_style;
10932 // if (errHist) {
10933 // (TAttLine &)(*errHist) = *h;
10934 // errHist->SetFillColor(h->GetLineColor());
10935 // }
10936 // }
10937 h->Draw(dOpt);
10938 }
10939
10940 if (!hasOverlay && (rarNode->get()->InheritsFrom("RooRealSumPdf") || rarNode->get()->InheritsFrom("RooAddPdf") ||
10941 (rarNode->get()->InheritsFrom("RooSimultaneous") &&
10942 strcmp(vv->GetName(), rarNode->get<RooSimultaneous>()->indexCat().GetName()) == 0))) {
10943 if (auto stack = dynamic_cast<THStack *>(h->FindObject("stack"))) {
10944 // access the stack and set draw options, adjust ranges etc
10945 TObjLink *lnk = stack->GetHists()->FirstLink();
10946 while (lnk) {
10947 TH1 *hh = static_cast<TH1 *>(lnk->GetObject());
10948 // lnk->SetOption(dOpt); - not needed
10949 auto hhMin = (hh->GetMinimum() == 0) ? hh->GetMinimum(1e-9) : hh->GetMinimum();
10950 if (lnk == stack->GetHists()->FirstLink() && h->GetMinimum() > hhMin) {
10951 auto newMin = hhMin - (h->GetMaximum() - hhMin) * gStyle->GetHistTopMargin();
10952 if (hhMin >= 0 && newMin < 0)
10953 newMin = hhMin * 0.99;
10954 adjustYRange(newMin, h->GetMaximum());
10955 }
10956 addLegendEntry(hh, hh->GetTitle(), "f");
10957 lnk = lnk->Next();
10958 }
10959 }
10960
10961 // // build a stack unless not requested
10962 // if (!nostack) {
10963 // THStack *stack = new THStack(TString::Format("%s_stack", rar->GetName()),
10964 // TString::Format("%s;%s", rar->GetTitle(), h->GetXaxis()->GetTitle()));
10965 // int count = 2;
10966 // std::map<std::string, int> colorByTitle; // TODO: should fill from any existing legend
10967 // std::set<std::string> allTitles;
10968 // bool titleMatchName = true;
10969 // std::map<std::string, TH1 *> histGroups;
10970 // std::vector<TH1 *> hhs;
10971 // std::set<TH1 *> histsWithBadTitles; // these histograms will have their titles autoFormatted
10972 //
10973 // // support for CMS model case where has single component containing many coeffs
10974 // // will build stack by setting each coeff equal to 0 in turn, rebuilding the histogram
10975 // // the difference from the "full" histogram will be the component
10976 // RooArgList cms_coefs;
10977 // if (!rarNode->components().empty()) {
10978 // auto comps = rarNode->components()[0];
10979 // for (auto &c : *comps) {
10980 // if (c->fFolder == "!.coeffs")
10981 // cms_coefs.add(*c->get<RooAbsArg>());
10982 // }
10983 // }
10984 // if (!cms_coefs.empty()) {
10985 // RooRealVar zero("zero", "", 0);
10986 // std::shared_ptr<TH1> prevHist(static_cast<TH1 *>(h->Clone()));
10987 // for (auto c : cms_coefs) {
10988 // // seems I have to remake the function each time, as haven't figured out what cache needs
10989 // clearing? std::unique_ptr<RooAbsReal> f(
10990 // dynamic_cast<RooAbsReal *>(rarNode->components()[0]->get()->Clone("tmpCopy")));
10991 // zero.setAttribute(
10992 // Form("ORIGNAME:%s", c->GetName())); // used in redirectServers to say what this
10993 // replaces
10994 // f->redirectServers(RooArgSet(zero), false, true); // each time will replace one additional coef
10995 // // zero.setAttribute(Form("ORIGNAME:%s",c->GetName()),false); (commented out so that on next
10996 // iteration
10997 // // will still replace all prev)
10998 // auto hh = xRooNode(*f, *this).BuildHistogram(v);
10999 // hh->SetName(c->GetName());
11000 // if (sf)
11001 // hh->Scale(sf->getVal());
11002 // if (strlen(hh->GetTitle()) == 0) {
11003 // hh->SetTitle(c->GetName()); // ensure all hists has titles
11004 // histsWithBadTitles.insert(hh);
11005 // } else if (strcmp(hh->GetName(), hh->GetTitle()) == 0) {
11006 // histsWithBadTitles.insert(hh);
11007 // }
11008 // titleMatchName &= (TString(c->GetName()) == hh->GetTitle() ||
11009 // TString(hh->GetTitle()).BeginsWith(TString(c->GetName()) + "_"));
11010 // std::shared_ptr<TH1> nextHist(static_cast<TH1 *>(hh->Clone()));
11011 // hh->Add(prevHist.get(), -1.);
11012 // hh->Scale(-1.);
11013 // hhs.push_back(hh);
11014 // prevHist = nextHist;
11015 // }
11016 // } else if (get<RooSimultaneous>()) {
11017 // // need to create a histogram for each sample across all the channels - will rely on functionality
11018 // below to
11019 // // merge them based on titles
11020 //
11021 // for (auto &chan : bins()) {
11022 // TString chanName(chan->GetName());
11023 // chanName = chanName(chanName.Index("=") + 1, chanName.Length());
11024 // auto samps = chan->mainChild();
11025 // if (!samps)
11026 // samps = *chan;
11027 // for (auto &samp : samps.components()) {
11028 // auto hh = static_cast<TH1 *>(h->Clone(samp->GetName()));
11029 // hh->Reset();
11030 // hh->SetTitle(samp->GetTitle());
11031 // if (strlen(hh->GetTitle()) == 0) {
11032 // hh->SetTitle(samp->GetName());
11033 // histsWithBadTitles.insert(hh);
11034 // } else if (strcmp(hh->GetName(), hh->GetTitle()) == 0) {
11035 // histsWithBadTitles.insert(hh);
11036 // }
11037 // hh->SetTitle(TString(hh->GetTitle())
11038 // .ReplaceAll(TString(chan->get()->GetName()) + "_",
11039 // "")); // remove occurance of channelname_ in title (usually
11040 // prefix)
11041 // titleMatchName &= (TString(samp->GetName()) == hh->GetTitle() ||
11042 // TString(hh->GetTitle()).BeginsWith(TString(samp->GetName()) + "_"));
11043 // hh->SetBinContent(hh->GetXaxis()->FindFixBin(chanName), samp->GetContent());
11044 // hhs.push_back(hh);
11045 // }
11046 // }
11047 // } else {
11048 // for (auto &samp : rarNode->components()) {
11049 // auto hh = samp->BuildHistogram(v,false,false,1,0,"",false,false,0,h); // passing h to ensure
11050 // binning is the same for all subcomponent hists if (sf)
11051 // hh->Scale(sf->getVal());
11052 // hhs.push_back(hh);
11053 // if (strlen(hh->GetTitle()) == 0) {
11054 // hh->SetTitle(samp->GetName()); // ensure all hists has titles
11055 // histsWithBadTitles.insert(hh);
11056 // } else if (strcmp(hh->GetName(), hh->GetTitle()) == 0) {
11057 // histsWithBadTitles.insert(hh);
11058 // }
11059 // titleMatchName &= (TString(samp->GetName()) == hh->GetTitle() ||
11060 // TString(hh->GetTitle()).BeginsWith(TString(samp->GetName()) + "_"));
11061 // }
11062 // }
11063 //
11064 // if (!hhs.empty()) {
11065 // for (auto &hh : hhs) {
11066 // allTitles.insert(hh->GetTitle());
11067 // }
11068 //
11069 // // get common prefix to strip off only if all titles match names and
11070 // // any title is longer than 10 chars
11071 // size_t e = std::min(allTitles.begin()->size(), allTitles.rbegin()->size());
11072 // size_t ii = 0;
11073 // bool goodPrefix = false;
11074 // std::string commonSuffix;
11075 // if (titleMatchName && hhs.size() > 1) {
11076 // while (ii < e - 1 && allTitles.begin()->at(ii) == allTitles.rbegin()->at(ii)) {
11077 // ii++;
11078 // if (allTitles.begin()->at(ii) == '_' || allTitles.begin()->at(ii) == ' ')
11079 // goodPrefix = true;
11080 // }
11081 //
11082 // // find common suffix if there is one .. must start with a "_"
11083 // bool stop = false;
11084 // while (!stop && commonSuffix.size() < size_t(e - 1)) {
11085 // commonSuffix = allTitles.begin()->substr(allTitles.begin()->length() - commonSuffix.length() -
11086 // 1); for (auto &t : allTitles) {
11087 // if (!TString(t).EndsWith(commonSuffix.c_str())) {
11088 // commonSuffix = commonSuffix.substr(1);
11089 // stop = true;
11090 // break;
11091 // }
11092 // }
11093 // }
11094 // if (commonSuffix.find('_') == std::string::npos) {
11095 // commonSuffix = "";
11096 // } else {
11097 // commonSuffix = commonSuffix.substr(commonSuffix.find('_'));
11098 // }
11099 // }
11100 // if (!goodPrefix)
11101 // ii = 0;
11102 //
11103 // // also find how many characters are needed to distinguish all entries (that dont have the same
11104 // name)
11105 // // then carry on up to first space or underscore
11106 // size_t jj = 0;
11107 // std::map<std::string, std::string> reducedTitles;
11108 // while (reducedTitles.size() != allTitles.size()) {
11109 // jj++;
11110 // std::map<std::string, int> titlesMap;
11111 // for (auto &s : allTitles) {
11112 // if (reducedTitles.count(s))
11113 // continue;
11114 // titlesMap[s.substr(0, jj)]++;
11115 // }
11116 // for (auto &s : allTitles) {
11117 // if (titlesMap[s.substr(0, jj)] == 1 && (jj >= s.length() || s.at(jj) == ' ' || s.at(jj) ==
11118 // '_')) {
11119 // reducedTitles[s] = s.substr(0, jj);
11120 // }
11121 // }
11122 // }
11123 //
11124 // // strip common prefix and suffix before adding
11125 // for (auto ritr = hhs.rbegin(); ritr != hhs.rend(); ++ritr) { // go in reverse order
11126 // if (!histsWithBadTitles.count((*ritr))) {
11127 // continue;
11128 // }
11129 // auto _title = (hhs.size() > 5) ? reducedTitles[(*ritr)->GetTitle()] : (*ritr)->GetTitle();
11130 // _title = _title.substr(ii < _title.size() ? ii : 0);
11131 // if (!commonSuffix.empty() && TString(_title).EndsWith(commonSuffix.c_str()))
11132 // _title = _title.substr(0, _title.length() - commonSuffix.length());
11133 // (*ritr)->SetTitle(_title.c_str());
11134 // }
11135 // }
11136 //
11137 // for (auto &hh : hhs) {
11138 // // automatically group hists that all have the same title
11139 // if (histGroups.find(hh->GetTitle()) == histGroups.end()) {
11140 // histGroups[hh->GetTitle()] = hh;
11141 // } else {
11142 // // add it into this group
11143 // histGroups[hh->GetTitle()]->Add(hh);
11144 // delete hh;
11145 // hh = nullptr;
11146 // continue;
11147 // }
11148 // auto hhMin = (hh->GetMinimum() == 0) ? hh->GetMinimum(1e-9) : hh->GetMinimum();
11149 // if (!stack->GetHists() && h->GetMinimum() > hhMin) {
11150 // auto newMin = hhMin - (h->GetMaximum() - hhMin) * gStyle->GetHistTopMargin();
11151 // if (hhMin >= 0 && newMin < 0)
11152 // newMin = hhMin * 0.99;
11153 // adjustYRange(newMin, h->GetMaximum());
11154 // }
11155 //
11156 // /*if(stack->GetHists() && stack->GetHists()->GetEntries()>0) {
11157 // // to remove rounding effects on bin boundaries, see if binnings compatible
11158 // auto _h1 = dynamic_cast<TH1*>(stack->GetHists()->At(0));
11159 // if(_h1->GetNbinsX()==hh->GetNbinsX()) TODO ... finish dealing with silly rounding effects
11160 // }*/
11161 // TString thisOpt = dOpt;
11162 // // uncomment next line to blend continuous with discrete components .. get some unpleasant "poke
11163 // through"
11164 // // effects though
11165 // // if(auto s = samp->get<RooAbsReal>(); s) thisOpt =
11166 // s->isBinnedDistribution(*dynamic_cast<RooAbsArg*>(v)) ?
11167 // // "" : "LF2";
11168 // stack->Add(hh, thisOpt);
11169 // }
11170 // stack->SetBit(kCanDelete); // should delete its sub histograms
11171 // stack->Draw("noclear same");
11172 // h->Draw(
11173 // dOpt + sOpt +
11174 // "same"); // overlay again .. if stack would cover original hist (negative components) we still see
11175 // integral
11176 // h->Draw("axissame"); // redraws axis
11177 //
11178 // TList *ll = stack->GetHists();
11179 // if (ll && ll->GetEntries()) {
11180 //
11181 // // finally, ensure all hists are styled
11182 // for (auto ho : *ll) {
11183 // TH1 *hh = dynamic_cast<TH1 *>(ho);
11184 // if (!hh)
11185 // continue;
11186 // bool createdStyle = (xRooNode(*hh, *this).styles(nullptr, false).get<TStyle>() == nullptr);
11187 //
11188 // if (createdStyle) {
11189 // // give hist a color, that isn't the same as any other hists color
11190 // hh->SetFillStyle(1001); // solid fill style
11191 // bool used = false;
11192 // do {
11193 // hh->SetFillColor((count++));
11194 // // check not already used this color
11195 // used = false;
11196 // for (auto ho2 : *ll) {
11197 // TH1 *hh2 = dynamic_cast<TH1 *>(ho2);
11198 // if (!hh2)
11199 // continue;
11200 // auto _styleNode = xRooNode(*hh2, *this).styles(hh2, false);
11201 // auto _style = _styleNode.get<TStyle>();
11202 // if (hh != hh2 && _style && _style->GetFillColor() == hh->GetFillColor()) {
11203 // used = true;
11204 // break;
11205 // }
11206 // }
11207 // } while (used);
11208 // }
11209 //
11210 // auto _styleNode = xRooNode(*hh, *this).styles(hh);
11211 // if (auto _style = _styleNode.get<TStyle>()) {
11212 // *dynamic_cast<TAttLine *>(hh) = *_style;
11213 // *dynamic_cast<TAttFill *>(hh) = *_style;
11214 // *dynamic_cast<TAttMarker *>(hh) = *_style;
11215 // }
11216 // // for stacks, fill color of white should be color 10 unless fill style is 0
11217 // if (hh->GetFillColor() == kWhite && hh->GetFillStyle() != 0) {
11218 // // kWhite means 'transparent' in ROOT ... should really use a FillStyle of 0 for that
11219 // // so assume user wanted actual white, which is color 10
11220 // hh->SetFillColor(10);
11221 // }
11222 // addLegendEntry(hh, hh->GetTitle(), "f");
11223 // }
11224 // }
11225 // }
11226 } else if (!overlayExisted) {
11227
11228 if (errHist) {
11229 addLegendEntry(errHist, strlen(errHist->GetTitle()) ? errHist->GetTitle() : GetName(), "fl");
11230 } else {
11231 addLegendEntry(h, strlen(h->GetTitle()) ? h->GetTitle() : GetName(), (hasError) ? "fl" : "l");
11232 }
11233 }
11234
11235 if (errHist) {
11236 dOpt.ReplaceAll("TEXT", "");
11237 errHist->Draw(dOpt + (dOpt.Contains("LF2") ? "e3same" : "e2same"));
11238 double ymax = -std::numeric_limits<double>::infinity();
11239 double ymin = std::numeric_limits<double>::infinity();
11240 for (int i = 1; i <= errHist->GetNbinsX(); i++) {
11241 ymax = std::max(ymax, errHist->GetBinContent(i) + errHist->GetBinError(i));
11242 ymin = std::min(ymin, errHist->GetBinContent(i) - errHist->GetBinError(i));
11243 }
11245 } else {
11246 adjustYRange(h->GetMinimum() * 0.9, h->GetMaximum() * 1.1);
11247 }
11248
11249 if ((!auxPlotTitle.empty()) && !hasSame) {
11250 // create a pad for the ratio ... shift the bottom margin of this pad to make space for it
11251 double padFrac = 0.3;
11252 auto _tmpPad = gPad;
11253 gPad->SetBottomMargin(padFrac);
11254 auto ratioPad = new TPad("auxPad", "aux plot", 0, 0, 1, padFrac);
11255 ratioPad->SetFillColor(_tmpPad->GetFillColor());
11256 ratioPad->SetNumber(1);
11257 ratioPad->SetBottomMargin(ratioPad->GetBottomMargin() / padFrac);
11258 ratioPad->SetTopMargin(0.04);
11259 ratioPad->SetLeftMargin(gPad->GetLeftMargin());
11260 ratioPad->SetRightMargin(gPad->GetRightMargin());
11261 ratioPad->cd();
11262 TH1 *ratioHist = dynamic_cast<TH1 *>((errHist) ? errHist->Clone("auxHist") : h->Clone("auxHist"));
11263 ratioHist->Reset();
11264 ratioHist->Add(h); // removes function list
11265 ratioHist->SetDirectory(nullptr);
11266 ratioHist->SetTitle((errHist) ? errHist->GetName()
11267 : h->GetName()); // abuse the title string to hold the name of the main hist
11268
11269 ratioHist->GetYaxis()->SetNdivisions(5, 0, 0);
11270 ratioHist->GetYaxis()->SetTitle(auxPlotTitle.c_str());
11271 ratioHist->SetTitle(
11272 TString::Format("%s|%s", ratioHist->GetTitle(),
11273 auxPlotTitle.c_str())); // used when plotting data (above) to decide what to calculate
11274 ratioHist->SetMaximum();
11275 ratioHist->SetMinimum(); // resets min and max
11276 ratioPad->SetGridy();
11277
11278 for (int i = 1; i <= ratioHist->GetNbinsX(); i++) {
11279 double val = ratioHist->GetBinContent(i);
11280 double err = ratioHist->GetBinError(i);
11281 ratioHist->SetBinContent(i, std::get<0>(auxFunctions[auxPlotTitle])(val, val, err));
11282 ratioHist->SetBinError(i, std::get<0>(auxFunctions[auxPlotTitle])(val + err, val, err) -
11283 ratioHist->GetBinContent(i));
11284 }
11285
11286 double rHeight = 1. / padFrac; //(_tmpPad->GetWNDC())/(gPad->GetHNDC());
11287 if (ratioHist->GetYaxis()->GetTitleFont() % 10 == 2) {
11288 ratioHist->GetYaxis()->SetTitleSize(ratioHist->GetYaxis()->GetTitleSize() * rHeight);
11289 ratioHist->GetYaxis()->SetLabelSize(ratioHist->GetYaxis()->GetLabelSize() * rHeight);
11290 ratioHist->GetXaxis()->SetTitleSize(ratioHist->GetXaxis()->GetTitleSize() * rHeight);
11291 ratioHist->GetXaxis()->SetLabelSize(ratioHist->GetXaxis()->GetLabelSize() * rHeight);
11292 ratioHist->GetYaxis()->SetTitleOffset(ratioHist->GetYaxis()->GetTitleOffset() / rHeight);
11293 } else {
11294#if ROOT_VERSION_CODE < ROOT_VERSION(6, 26, 00)
11295 ratioHist->GetYaxis()->SetTitleOffset(ratioHist->GetYaxis()->GetTitleOffset() / rHeight);
11296#endif
11297 }
11298 ratioHist->GetXaxis()->SetTickLength(ratioHist->GetXaxis()->GetTickLength() * rHeight);
11299 ratioHist->SetStats(false);
11300 ratioHist->SetBit(TH1::kNoTitle);
11301 ratioHist->SetBit(kCanDelete);
11302 if (errHist) {
11303 auto _h = dynamic_cast<TH1 *>(ratioHist->Clone("auxHist_clone"));
11304 _h->SetDirectory(nullptr);
11305 _h->SetFillColor(0);
11306 ratioHist->GetListOfFunctions()->Add(_h, "histsame");
11307 //_h->Draw("histsame");
11308 }
11309 ratioHist->GetListOfFunctions()->Add(new TExec(
11310 ".updateAxis",
11311 TString::Format("auto h1 = (TH1*)%p; auto h2 = (TH1*)%p; if(h2->GetXaxis()->GetFirst() != "
11312 "h1->GetXaxis()->GetFirst() || h1->GetXaxis()->GetLast()!=h2->GetXaxis()->GetLast()) "
11313 "{h2->GetXaxis()->SetRange(h1->GetXaxis()->GetFirst(),h1->GetXaxis()->GetLast());if(gPad) "
11314 "{gPad->GetCanvas()->Paint();gPad->GetCanvas()->Update();}}",
11315 (void *)ratioHist, (void *)(h))));
11316 ratioHist->Draw((errHist ? "e2" : ""));
11317
11318 _tmpPad->cd();
11319 ratioPad->Draw();
11320 } else if (auto ratioPad = dynamic_cast<TPad *>(gPad->GetPrimitive("auxPad")); hasSame && ratioPad) {
11321 // need to draw histogram in the ratio pad ...
11322 // if doing overlay need to update histogram
11323
11324 if (auto hr = dynamic_cast<TH1 *>(ratioPad->GetPrimitive("auxHist"));
11325 hr && auxFunctions.find(hr->GetYaxis()->GetTitle()) != auxFunctions.end()) {
11326 TString histName = hr->GetTitle(); // split it by | char
11327 TString histType = histName(histName.Index('|') + 1, histName.Length());
11328 histName = histName(0, histName.Index('|'));
11329
11330 if (auto hnom = dynamic_cast<TH1 *>(gPad->GetPrimitive(histName)); hnom) {
11331 h = dynamic_cast<TH1 *>(h->Clone(h->GetName()));
11332 h->SetDirectory(nullptr);
11333 h->SetBit(kCanDelete);
11334 for (int i = 1; i <= hnom->GetNbinsX(); i++) {
11335 double val = h->GetBinContent(i);
11336 double err = h->GetBinError(i);
11337 h->SetBinContent(i, std::get<0>(auxFunctions[hr->GetYaxis()->GetTitle()])(
11338 h->GetBinContent(i), hnom->GetBinContent(i), hnom->GetBinError(i)));
11339 h->SetBinError(i, std::get<0>(auxFunctions[hr->GetYaxis()->GetTitle()])(
11340 val + err, hnom->GetBinContent(i), hnom->GetBinError(i)) -
11341 h->GetBinContent(i));
11342 }
11343 auto _tmpPad = gPad;
11344 ratioPad->cd();
11345 if (hasOverlay) {
11346 if (auto existing = dynamic_cast<TH1 *>(ratioPad->GetPrimitive(h->GetName())); existing) {
11347 existing->Reset();
11348 existing->Add(h);
11349 delete h;
11350 h = existing;
11351 overlayExisted = true;
11352 } else {
11353 h->Draw(dOpt);
11354 }
11355 } else {
11356 h->Draw(dOpt);
11357 }
11358 double ymax = -std::numeric_limits<double>::infinity();
11359 double ymin = std::numeric_limits<double>::infinity();
11360 for (int i = 1; i <= h->GetNbinsX(); i++) {
11361 ymax = std::max(ymax, h->GetBinContent(i) + h->GetBinError(i));
11362 ymin = std::min(ymin, h->GetBinContent(i) - h->GetBinError(i));
11363 }
11364 adjustYRange(ymin, ymax, hr, std::get<1>(auxFunctions[hr->GetYaxis()->GetTitle()]));
11365 // adjustYRange(h->GetMinimum() * (h->GetMinimum()<0 ? 1.1 : 0.9), h->GetMaximum() * (h->GetMinimum()<0 ?
11366 // 0.9 : 1.1), hr, std::get<1>(auxFunctions[hr->GetYaxis()->GetTitle()]));
11367 gPad->Modified();
11368 _tmpPad->cd();
11369 }
11370 }
11371 }
11372
11373 // see if it's in a simultaneous so need to select a cat
11374 /*auto _parent = fParent;
11375 auto _me = rar;
11376 while(_parent) {
11377 if (auto s = _parent->get<RooSimultaneous>(); s) {
11378 for (auto c : s->indexCat()) {
11379 if (auto p = s->getPdf(c.first.c_str());_me==p) {
11380 gPad->SetName(c.first.c_str());
11381 break;
11382 }
11383 }
11384 break;
11385 }
11386 _me = _parent->get<RooAbsReal>();
11387 _parent = _parent->fParent;
11388 }*/
11389
11390 // now draw selected datasets on top if this was a pdf
11391 if (auto _pdf = get<RooAbsPdf>();
11392 !hasSame && _pdf /*&& (_pdf->canBeExtended() || robs().empty())*/ && coefs(true).empty()) {
11393 auto _dsets = datasets();
11394 // bool _drawn=false;
11395 for (auto &d : _dsets) {
11396 if (d->get()->TestBit(1 << 20)) {
11397 d->Draw("same");
11398 //_drawn=true;
11399 }
11400 }
11401 // if (!_drawn && !_dsets.empty()) _dsets[0]->Draw("same"); // always draw if has a dataset
11402 }
11403
11404 gPad->Modified();
11405 // gPad->Update();
11406 getLegend();
11407 gPad->Modified();
11408 // gPad->Update();
11409}
11410
11411void xRooNode::SaveAs(const char *filename, Option_t *option) const
11412{
11414 sOpt.ToLower();
11415 if (auto w = get<RooWorkspace>(); w) {
11416 // ensure the current color set is saved in the workspace
11417 w->import(*gROOT->GetListOfColors(), true);
11418
11419 if (TString(filename).EndsWith(".json")) {
11420#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 26, 00)
11421 // stream with json tool
11423 if (tool.exportJSON(filename)) {
11424 Info("SaveAs", "%s saved to %s", w->GetName(), filename);
11425 } else {
11426 Error("SaveAs", "Unable to save to %s", filename);
11427 }
11428#else
11429 Error("SaveAs", "json format workspaces only in ROOT 6.26 onwards");
11430#endif
11431 return;
11432 }
11433
11434#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
11435 // before saving, clear the eocache of all owned nodes
11436 // because causes memory leak when read back in (workspace streamer immediately overwrites the caches)
11437 // fixed in: https://github.com/root-project/root/pull/12024
11438 for (auto &c : w->components()) {
11439 c->_eocache = nullptr;
11440 }
11441#endif
11442 // const_cast<Node2*>(this)->sterilize(); - tried this to reduce mem leak on readback but no improve
11443 if (!w->writeToFile(filename, sOpt != "update")) {
11444 Info("SaveAs", "%s saved to %s", w->GetName(), filename);
11445 // save any fitDatabase that is loaded in memory too
11446 // TODO: We should do this as well for SaveAs on a scan object
11447 if (auto fitDb = dynamic_cast<TFile *>(gROOT->GetListOfFiles()->FindObject("fitDatabase"))) {
11448
11449 std::function<void(TDirectory *, TDirectory *)> CopyDir;
11450
11452 auto dir = dest->GetDirectory(source->GetName());
11453 if (!dir) {
11454 dir = dest->mkdir(source->GetName());
11455 }
11456 for (auto k : *source->GetListOfKeys()) {
11457 auto key = dynamic_cast<TKey *>(k);
11458 const char *classname = key->GetClassName();
11459 TClass *cl = gROOT->GetClass(classname);
11460 // std::cout << "processing " << key->GetName() << " " << classname << std::endl;
11461 if (!cl) {
11462 continue;
11463 } else if (cl->InheritsFrom(TDirectory::Class())) {
11464 CopyDir(source->GetDirectory(key->GetName()), dir);
11465 } else {
11466 // don't write object if it already exists
11467 if (dir->FindKey(key->GetName()))
11468 continue;
11469 // support FitConfigs ....
11470 if (strcmp(classname, "ROOT::Fit::FitConfig") == 0) {
11471 auto fc = key->ReadObject<ROOT::Fit::FitConfig>();
11472 dir->WriteObject(fc, key->GetName());
11473 delete fc;
11474 } else {
11475 TObject *obj = key->ReadObj();
11476 if (obj) {
11477 dir->WriteTObject(obj, key->GetName());
11478 delete obj;
11479 }
11480 }
11481 }
11482 }
11483 };
11484 CopyDir(fitDb, std::make_unique<TFile>(filename, "UPDATE").get());
11485 Info("SaveAs", "Saved fitDatabase to %s", filename);
11486 }
11487
11488 } else {
11489 Error("SaveAs", "Unable to save to %s", filename);
11490 }
11491#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
11492 // restore the cache to every node
11493 for (auto &c : w->components()) {
11494 c->setExpensiveObjectCache(w->expensiveObjectCache());
11495 }
11496#endif
11497 }
11498}
11499
11500double xRooNode::GetBinError(int bin, const xRooNode &fr, int nToys, bool errorsHi, bool errorsLo) const
11501{
11502 auto res = GetBinErrors(bin, bin, fr, nToys, errorsHi, errorsLo);
11503 if (res.empty())
11504 return std::numeric_limits<double>::quiet_NaN();
11505 return res.at(0);
11506}
11507
11508std::vector<double> xRooNode::contents() const
11509{
11510 std::vector<double> out;
11511 out.reserve(size());
11512 for (auto child : *this) {
11513 out.push_back(child->GetContent());
11514 }
11515 return out;
11516}
11517
11519{
11520
11521 auto _fr = fr.get<RooFitResult>();
11522
11523 if (!_fr) {
11524 return covariances(fitResult());
11525 }
11526
11527 auto rho = _fr->correlationMatrix();
11528
11529 TMatrixDSym out(size());
11530
11531 auto _pars = pars();
11532
11533 // 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]
11534 // - nu[theta_n_down]) consistent with propagatedError formula
11535
11536 for (int m = 0; m < rho.GetNrows(); m++) {
11537 auto p_m = dynamic_cast<RooRealVar *>(_fr->floatParsFinal().at(m));
11538 if (!p_m)
11539 continue; // skip categoricals
11540 auto _p = dynamic_cast<RooAbsRealLValue *>(_pars.get<RooArgList>()->find(p_m->GetName()));
11541 if (!_p)
11542 continue;
11543 auto tmp = _p->getVal();
11544 _p->setVal(p_m->getVal() + p_m->getErrorHi());
11545 auto nu_m = contents();
11546 _p->setVal(p_m->getVal() + p_m->getErrorLo());
11547 auto nu_m2 = contents();
11548 _p->setVal(tmp);
11549 for (int n = 0; n < rho.GetNrows(); n++) {
11550 auto p_n = dynamic_cast<RooRealVar *>(_fr->floatParsFinal().at(n));
11551 if (!p_n)
11552 continue; // skip categoricals
11553 auto _p2 = dynamic_cast<RooAbsRealLValue *>(_pars.get<RooArgList>()->find(p_n->GetName()));
11554 if (!_p2)
11555 continue;
11556 auto tmp2 = _p2->getVal();
11557 _p2->setVal(p_n->getVal() + p_n->getErrorHi());
11558 auto nu_n = (p_n == p_m) ? nu_m : contents();
11559 _p2->setVal(p_n->getVal() + p_n->getErrorLo());
11560 auto nu_n2 = (p_n == p_m) ? nu_m2 : contents();
11561 _p2->setVal(tmp2);
11562 for (int i = 0; i < out.GetNrows(); i++) {
11563 for (int j = 0; j < out.GetNrows(); j++) {
11564 out(i, j) += 0.25 * (nu_m[i] - nu_m2[i]) * rho(m, n) * (nu_n[j] - nu_n2[j]);
11565 }
11566 }
11567 }
11568 }
11569 return out;
11570}
11571
11572std::pair<double, double> xRooNode::IntegralAndError(const xRooNode &fr, const char *rangeName) const
11573{
11574 double out = 1.;
11575 double err = std::numeric_limits<double>::quiet_NaN();
11576
11577 std::unique_ptr<RooAbsCollection> _snap;
11579 if (auto _fr = fr.get<RooFitResult>()) {
11580 _pars.add(pars().argList());
11581 _snap.reset(_pars.snapshot());
11582 _pars = _fr->floatParsFinal();
11583 _pars = _fr->constPars();
11584 }
11585
11586 auto _obs = obs();
11587 auto _coefs = coefs(); // need here to keep alive owned RooProduct
11588 if (auto c = _coefs.get<RooAbsReal>(); c) {
11589 out = c->getVal(*_obs.get<RooArgList>()); // assumes independent of observables!
11590 }
11591
11592 if (auto p = dynamic_cast<RooAbsPdf *>(get()); p) {
11593 // prefer to use expectedEvents for integrals of RooAbsPdf e.g. for RooProdPdf wont include constraint terms
11594 if (rangeName)
11595 p->setNormRange(rangeName);
11596#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 27, 00)
11598#endif
11599 out *= p->expectedEvents(*_obs.get<RooArgList>());
11600#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
11601 // improved normSet invalidity checking, so assuming no longer need this in 6.28 onwards
11602 p->_normSet = nullptr;
11603#endif
11604 err = GetBinError(-1, fr);
11605 if (rangeName)
11606 p->setNormRange(nullptr);
11607 } else if (auto p2 = dynamic_cast<RooAbsReal *>(get()); p2) {
11608 // only integrate over observables we actually depend on
11609 auto f = std::shared_ptr<RooAbsReal>(
11610 p2->createIntegral(*std::unique_ptr<RooArgSet>(p2->getObservables(*_obs.get<RooArgList>())),
11611 rangeName)); // did use x here before using obs
11612 RooProduct pr("int_x_coef", "int_x_coef",
11613 RooArgList(*f, !_coefs.get() ? RooFit::RooConst(1) : *_coefs.get<RooAbsReal>()));
11614 out *= f->getVal();
11615 err = xRooNode(pr, *this).GetBinError(-1, fr);
11616 sterilize(); // needed so that we can forget properly about the integral we just created (and are deleting)
11617 } else if (get<RooAbsData>()) {
11618 out = 0;
11619 auto vals = GetBinContents(1, 0); // returns all bins
11620 auto ax = (rangeName) ? GetXaxis() : nullptr;
11621 auto rv = (ax) ? dynamic_cast<RooRealVar *>(ax->GetParent()) : nullptr;
11622 auto cv = (ax && !rv) ? dynamic_cast<RooCategory *>(ax->GetParent()) : nullptr;
11623 int i = 0;
11624 for (auto &v : vals) {
11625 i++;
11626 if (rangeName) {
11627 if (rv && !rv->inRange(ax->GetBinCenter(i), rangeName))
11628 continue;
11629 if (cv && !cv->isStateInRange(rangeName, ax->GetBinLabel(i)))
11630 continue;
11631 }
11632 out += v;
11633 }
11634 err = 0; // should this be sqrt(sum(v^2)) or something similar
11635 } else {
11636 out = std::numeric_limits<double>::quiet_NaN();
11637 }
11638 if (_snap) {
11639 _pars.RooAbsCollection::operator=(*_snap);
11640 }
11641 return std::make_pair(out, err);
11642}
11643
11644std::vector<double>
11645xRooNode::GetBinErrors(int binStart, int binEnd, const xRooNode &_fr, int nToys, bool errorHi, bool errorLo) const
11646{
11647 // note: so far this method is inconsistent with the BuildHistogram in ways:
11648 // no projection over other variables
11649 // July2023: made RooRealSumPdf evaluate as a function if doesn't have a floor
11650 // but this method will still evaluate it as a pdf (uses PdfWrapper)
11651 // but can get away with it while added NaN recovery to getSimplePropagatedError to pickup raw values
11652
11653 if (fBinNumber != -1) {
11654 if (binStart != binEnd || !fParent) {
11655 throw std::runtime_error(TString::Format("%s is a bin - only has one value", GetName()));
11656 }
11657 return fParent->GetBinErrors(fBinNumber, fBinNumber, _fr);
11658 }
11659
11660 std::vector<double> out;
11661
11662 auto _hist = BuildHistogram(nullptr, true, true, binStart, binEnd, _fr, errorHi, errorLo, nToys);
11663 if (!_hist)
11664 return out;
11665 if (binEnd == 0) {
11666 binEnd = _hist->GetNbinsX();
11667 } else if (binEnd == binStart && binEnd == -1) {
11668 binStart = 1;
11669 binEnd = 1; // done an integral, so histogram has only 1 bin
11670 }
11671 for (int bin = binStart; bin <= binEnd; bin++) {
11672 out.push_back(((errorLo && !errorHi) ? (-1.) : 1.) *
11673 _hist->GetBinError(bin)); // using same convention as RooFit that Lo errors are negative
11674 }
11675 delete _hist;
11676 return out;
11677
11678 // auto o = dynamic_cast<RooAbsReal *>(get());
11679 // if (!o)
11680 // return out;
11681 //
11682 // std::shared_ptr<RooFitResult> fr = std::dynamic_pointer_cast<RooFitResult>(_fr.fComp);
11683 // //= dynamic_cast<RooFitResult*>( _fr.get<RooFitResult>() ? _fr->Clone() : fitResult()->Clone());
11684 //
11685 // auto _coefs = coefs();
11686 //
11687 // if (!fr) {
11688 // // need to ensure coefs, if any, are included in fit result retrieval so all pars are loaded
11689 // auto frn = (!_coefs.get() ? *this : xRooNode(RooProduct("tmp", "tmp", RooArgList(*o,
11690 // *_coefs.get<RooAbsReal>()))))
11691 // .fitResult();
11692 // if (strlen(_fr.GetName()))
11693 // frn = frn.reduced(_fr.GetName());
11694 //
11695 // // use name to reduce the fit result, if one given
11696 // fr = std::dynamic_pointer_cast<RooFitResult>(frn.fComp);
11697 // }
11698 //
11699 // if (!GETDMP(fr.get(), _finalPars)) {
11700 // fr->setFinalParList(RooArgList());
11701 // }
11702 //
11703 // /// Oct2022: No longer doing this because want to allow fitResult to be used to get partial error
11704 // // // need to add any floating parameters not included somewhere already in the fit result ...
11705 // // RooArgList l;
11706 // // for(auto& p : pars()) {
11707 // // auto v = p->get<RooRealVar>();
11708 // // if (!v) continue;
11709 // // if (v->isConstant()) continue;
11710 // // if (fr->floatParsFinal().find(v->GetName())) continue;
11711 // // if (fr->_constPars && fr->_constPars->find(v->GetName())) continue;
11712 // // l.add(*v);
11713 // // }
11714 // //
11715 // // if (!l.empty()) {
11716 // // RooArgList l2; l2.addClone(fr->floatParsFinal());
11717 // // l2.addClone(l);
11718 // // fr->setFinalParList(l2);
11719 // // }
11720 //
11721 // TMatrixTSym<double> *prevCov = static_cast<TMatrixTSym<double> *>(GETDMP(fr.get(), _VM));
11722 //
11723 // if (!prevCov || size_t(prevCov->GetNcols()) < fr->floatParsFinal().size()) {
11724 // TMatrixDSym cov(fr->floatParsFinal().size());
11725 // if (prevCov) {
11726 // for (int i = 0; i < prevCov->GetNcols(); i++) {
11727 // for (int j = 0; j < prevCov->GetNrows(); j++) {
11728 // cov(i, j) = (*prevCov)(i, j);
11729 // }
11730 // }
11731 // }
11732 // int i = 0;
11733 // for (auto &p : fr->floatParsFinal()) {
11734 // if (!prevCov || i >= prevCov->GetNcols()) {
11735 // cov(i, i) = pow(dynamic_cast<RooRealVar *>(p)->getError(), 2);
11736 // }
11737 // i++;
11738 // }
11739 // int covQualBackup = fr->covQual();
11740 // fr->setCovarianceMatrix(cov);
11741 // fr->setCovQual(covQualBackup);
11742 // }
11743 //
11744 // bool doBinWidth = false;
11745 // auto ax = (binStart == -1 && binEnd == -1) ? nullptr : GetXaxis();
11746 //
11747 // auto _obs = obs(); // may own an obs so keep alive here
11748 // RooArgList normSet = _obs.argList();
11749 // // to give consistency with BuildHistogram method, should be only the axis var if defined
11750 // if (ax) {
11751 // normSet.clear();
11752 // normSet.add(*dynamic_cast<RooAbsArg *>(ax->GetParent()));
11753 // }
11754 //
11755 // if (auto p = dynamic_cast<RooAbsPdf *>(o); ax && (p || _coefs.get() || o->getAttribute("density"))) {
11756 // // pdfs of samples embedded in a sumpdf (aka have a coef) will convert their density value to a content
11757 // doBinWidth = true;
11758 // }
11759 // if (binEnd == 0) {
11760 // if (ax) {
11761 // binEnd = ax->GetNbins();
11762 // } else {
11763 // binEnd = binStart;
11764 // }
11765 // }
11766 // for (int bin = binStart; bin <= binEnd; bin++) {
11767 // if (ax)
11768 // dynamic_cast<RooAbsLValue *>(ax->GetParent())->setBin(bin - 1, ax->GetName());
11769 // // if (!SetBin(bin)) { return out; }
11770 //
11771 // double res;
11772 // if (auto p = dynamic_cast<RooAbsPdf *>(o); p) {
11773 // // fr->covarianceMatrix().Print();
11774 // res = PdfWrapper(*p, _coefs.get<RooAbsReal>(), !ax).getSimplePropagatedError(*fr, normSet);
11775 // #if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
11776 // // improved normSet invalidity checking, so assuming no longer need this in 6.28 onwards
11777 // p->_normSet = nullptr;
11778 // #endif
11779 // } else {
11780 // // res = o->getPropagatedError(*fr, normSet);
11781 // // // TODO: What if coef has error? - probably need a FuncWrapper class
11782 // // if (auto c = _coefs.get<RooAbsReal>(); c) {
11783 // // res *= c->getVal(normSet);
11784 // // }
11785 // res = RooProduct("errorEval", "errorEval",
11786 // RooArgList(*o, !_coefs.get() ? RooFit::RooConst(1) : *_coefs.get<RooAbsReal>()))
11787 // .getPropagatedError(*fr, normSet);
11788 // }
11789 // if (doBinWidth) {
11790 // res *= ax->GetBinWidth(bin);
11791 // }
11792 // out.push_back(res);
11793 // }
11794 //
11795 // return out;
11796}
11797
11798std::string cling::printValue(const xRooNode *v)
11799{
11800 if (!v)
11801 return "nullptr\n";
11802 if (!v->empty()) {
11803 std::string out;
11804 size_t left = v->size();
11805 for (auto n : *v) {
11806 left--;
11807 if (!out.empty())
11808 out += ",";
11809 else
11810 out += "{";
11811 out += n->GetName();
11812 if (out.length() > 100 && left > 0) {
11813 out += TString::Format(",... and %lu more", left);
11814 break;
11815 }
11816 }
11817 out += "}\n";
11818 out = std::string(Form("<%s> %s", v->get() ? v->get()->ClassName() : "nullptr", v->GetName())) + out;
11819 return out;
11820 }
11821 std::string out;
11822 if (!(*v)) {
11823 return "<nullptr>";
11824 } else {
11825 return Form("<%s> %s", v->get() ? v->get()->ClassName() : "nullptr", v->GetName());
11826 }
11827
11828 return out;
11829}
11830
@ kButton1Down
Definition Buttons.h:17
int main()
Definition Prototype.cxx:12
#define d(i)
Definition RSha256.hxx:102
#define b(i)
Definition RSha256.hxx:100
#define f(i)
Definition RSha256.hxx:104
#define c(i)
Definition RSha256.hxx:101
#define g(i)
Definition RSha256.hxx:105
#define a(i)
Definition RSha256.hxx:99
#define h(i)
Definition RSha256.hxx:106
#define e(i)
Definition RSha256.hxx:103
#define ROOT_VERSION(a, b, c)
Definition RVersion.hxx:23
#define ROOT_VERSION_CODE
Definition RVersion.hxx:24
ROOT::RRangeCast< T, false, Range_t > static_range_cast(Range_t &&coll)
size_t size(const MatrixT &matrix)
retrieve the size of a square matrix
const char Option_t
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)
ROOT::Detail::TRangeCast< T, true > TRangeDynCast
TRangeDynCast is an adapter class that allows the typed iteration through a TCollection.
R__EXTERN TEnv * gEnv
Definition TEnv.h:170
void Warning(const char *location, const char *msgfmt,...)
Use this function in warning situations.
Definition TError.cxx: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 data
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t dest
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t winding char text const char depth char const char Int_t count const char ColorStruct_t color const char filename
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t winding char text const char depth char const char Int_t count const char ColorStruct_t color const char Pixmap_t Pixmap_t PictureAttributes_t attr const char char ret_data h unsigned char height h offset
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t np
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t r
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t result
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t child
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void value
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void funcs
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t winding char text const char depth char const char Int_t count const char ColorStruct_t color const char Pixmap_t Pixmap_t PictureAttributes_t attr const char char ret_data h unsigned char height h Atom_t Int_t ULong_t ULong_t unsigned char prop_list Atom_t Atom_t Atom_t Time_t type
Option_t Option_t style
char name[80]
Definition TGX11.cxx:110
float xmin
float * q
float ymin
float xmax
float ymax
@ kCanDelete
Definition TObject.h:367
#define gROOT
Definition TROOT.h:406
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:561
#define gPad
Color * colors
Definition X3DBuffer.c:21
#define _(A, B)
Definition cfortran.h:108
double GetBinLowEdge(Int_t bin) const override
Return low edge of bin.
Definition xRooNode.cxx:861
double GetBinUpEdge(Int_t bin) const override
Return up edge of bin.
Definition xRooNode.cxx:869
Int_t FindFixBin(double x) const override
Find bin number corresponding to abscissa x.
Definition xRooNode.cxx:912
void Set(Int_t nbins, const double *xbins) override
Initialize axis with variable bins.
Definition xRooNode.cxx:889
RooAbsRealLValue * rvar() const
Definition xRooNode.cxx:916
void SetTitle(const char *title) override
Set the title of the TNamed.
Definition xRooNode.cxx:880
void Set(Int_t nbins, double xmin, double xmax) override
Initialize axis with fix bins.
Definition xRooNode.cxx:902
Int_t FindFixBin(const char *label) const override
Find bin number with label.
Definition xRooNode.cxx:911
RooAbsLValue * var() const
Definition xRooNode.cxx:915
const RooAbsBinning * binning() const
Definition xRooNode.cxx:909
void Set(Int_t nbins, const float *xbins) override
Initialize axis with variable bins.
Definition xRooNode.cxx:895
const char * GetTitle() const override
Returns title of object.
Definition xRooNode.cxx:876
double GetBinWidth(Int_t bin) const override
Return bin width.
Definition xRooNode.cxx:855
PadRefresher(TVirtualPad *p)
static int nExisting
A class which maps the current values of a RooRealVar (or a set of RooRealVars) to one of a number of...
bool isBinnedDistribution(const RooArgSet &obs) const override
Tests if the distribution is binned. Unless overridden by derived classes, this always returns false.
bool selfNormalized() const override
Shows if a PDF is self-normalized, which means that no attempt is made to add a normalization term.
~PdfWrapper() override
double getSimplePropagatedError(const RooFitResult &fr, const RooArgSet &nset_in) const
RooRealProxy fCoef
double evaluate() const override
Evaluate this PDF / function / constant. Needs to be overridden by all derived classes.
TObject * clone(const char *newname) const override
std::list< double > * binBoundaries(RooAbsRealLValue &obs, double xlo, double xhi) const override
Retrieve bin boundaries if this distribution is binned in obs.
PdfWrapper(const PdfWrapper &other, const char *name=nullptr)
RooRealProxy fExpPdf
PdfWrapper(RooAbsReal &f, RooAbsReal *coef, bool expEvMode=false, RooAbsPdf *expPdf=nullptr)
RooRealProxy fFunc
The PiecewiseInterpolation is a class that can morph distributions into each other,...
static std::shared_ptr< RooLinkedList > createNLLOptions()
Definition xRooFit.cxx:460
static std::pair< std::shared_ptr< RooAbsData >, std::shared_ptr< const RooAbsCollection > > generateFrom(RooAbsPdf &pdf, const RooFitResult &fr, bool expected=false, int seed=0)
Definition xRooFit.cxx:150
static xRooNLLVar createNLL(const std::shared_ptr< RooAbsPdf > pdf, const std::shared_ptr< RooAbsData > data, const RooLinkedList &nllOpts)
Definition xRooFit.cxx:101
static std::pair< double, double > matchPrecision(const std::pair< double, double > &in)
Definition xRooFit.cxx:2000
void Draw(Option_t *opt="") override
Default Draw method for all objects.
void Draw(Option_t *opt="") override
Default Draw method for all objects.
This xRooNLLVar object has several special methods, e.g.
Definition xRooNLLVar.h:58
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)
TH1 * BuildHistogram(RooAbsLValue *v=nullptr, bool empty=false, bool errors=false, int binStart=1, int binEnd=0, const xRooNode &fr="", bool errorsHi=false, bool errorsLo=false, int nErrorToys=0, TH1 *templateHist=nullptr, bool nostack=true, bool setInterp=false) const
xRooNode filter(const xRooNode &range) const
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:525
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.
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:490
void Print(Option_t *opt="") const override
Print TNamed name and title.
std::shared_ptr< T > acquireNew(Args &&...args)
Definition xRooNode.h:274
void SaveAs(const char *filename="", Option_t *option="") const override
Save this object in the file specified by filename.
xRooNode bins() const
bins of a channel or sample, or channels of a multi-channel pdf
std::shared_ptr< xRooNode > find(const std::string &name, bool browseResult=true) const
TGListTree * GetListTree(TBrowser *b) const
xRooNode styles(TObject *initObject=nullptr, bool autoCreate=true) const
bool fInterrupted
appears that if was fXaxis then dialog box for SetXaxis will take as current value
Definition xRooNode.h:515
xRooNode generate(const xRooNode &fr="", bool expected=false, int seed=0)
void Inspect() const override
Dump contents of this object in a graphics canvas.
std::shared_ptr< xRooNode > getBrowsable(const char *name) const
bool SetXaxis(const RooAbsBinning &binning)
double GetBinError(int bin, const xRooNode &fr="", int nToys=0, bool errorsHi=false, bool errorsLo=false) const
bool SetBinData(int bin, double value, const xRooNode &data="obsData")
std::vector< double > GetBinContents(int binStart=1, int binEnd=0) const
xRooNode pp() const
List of prespecified parameters: non-floatable parameters.
std::shared_ptr< xRooNode > fProvider
Definition xRooNode.h:518
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:513
xRooNode coords(bool setVals=true) const
xRooNode Vary(const xRooNode &child)
xRooNode Constrain(const xRooNode &child)
static std::map< std::string, std::tuple< std::function< double(double, double, double)>, bool > > auxFunctions
Definition xRooNode.h:58
xRooNode pars() const
List of parameters (non-observables) of this node.
void SetFitResult(const RooFitResult *fr=nullptr)
xRooNode coefs(bool recurse=false) const
bool IsFolder() const override
Returns kTRUE in case object contains browsable objects (like containers or lists of other objects).
Definition xRooNode.cxx:840
double GetBinData(int bin, const xRooNode &data="obsData")
bool SetBinError(int bin, double value)
bool SetContents(const TObject &obj)
Definition xRooNode.h:347
std::shared_ptr< TObject > fComp
Definition xRooNode.h:500
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:919
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
double GetBinContent(int bin) const
Definition xRooNode.h:389
xRooNode reduced(const std::string &range="", bool invert=false) const
std::vector< double > GetBinErrors(int binStart=1, int binEnd=0, const xRooNode &fr="", int nToys=0, bool errorsHi=false, bool errorsLo=false) const
auto begin() const -> xRooNodeIterator
Definition xRooNode.h:200
std::shared_ptr< xRooNode > parentPdf() const
like a parent but only for use by getObject
std::shared_ptr< xRooNode > fParent
Definition xRooNode.h:503
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:531
xRooNode & operator=(const TObject &o)
auto end() const -> xRooNodeIterator
Definition xRooNode.h:201
xRooNode consts() const
List of parameters that are currently constant.
void _SetBinContent_(int bin, double value, const char *par="", double parVal=1)
std::shared_ptr< TObject > acquire(const std::shared_ptr< TObject > &arg, bool checkFactory=false, bool mustBeNew=false)
std::function< xRooNode(xRooNode *) fBrowseOperation)
Definition xRooNode.h:526
xRooNode histo(const xRooNode &vars="x", const xRooNode &fr="", bool content=true, bool errors=true, bool stack=true, bool errorsHi=false, bool errorsLo=false, int nErrorToys=0) const
void Browse(TBrowser *b=nullptr) override
Browse object. May be overridden for another default action.
Definition xRooNode.cxx:566
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:416
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
const_iterator begin() const
const_iterator end() const
Common abstract base class for objects that represent a value and a "shape" in RooFit.
Definition RooAbsArg.h:79
bool dependsOn(const RooAbsCollection &serverList, const RooAbsArg *ignoreArg=nullptr, bool valueOnly=false) const
Test whether we depend on (ie, are served by) any object in the specified collection.
void setStringAttribute(const Text_t *key, const Text_t *value)
Associate string 'value' to this object under key 'key'.
RooFit::OwningPtr< RooArgSet > getObservables(const RooArgSet &set, bool valueOnly=true) const
Given a set of possible observables, return the observables that this PDF depends on.
void SetName(const char *name) override
Set the name of the TNamed.
const Text_t * getStringAttribute(const Text_t *key) const
Get string attribute mapped under key 'key'.
bool getAttribute(const Text_t *name) const
Check if a named attribute is set. By default, all attributes are unset.
virtual bool isFundamental() const
Is this object a fundamental type that can be added to a dataset? Fundamental-type subclasses overrid...
Definition RooAbsArg.h:223
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 bool remove(const RooAbsArg &var, bool silent=false, bool matchByNameOnly=false)
Remove the specified argument from our list.
virtual bool add(const RooAbsArg &var, bool silent=false)
Add the specified argument to list.
Int_t index(const RooAbsArg *arg) const
Returns index of given arg, or -1 if arg is not in the collection.
Storage_t::size_type size() const
bool 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
RooFit::OwningPtr< RooAbsData > reduce(const RooCmdArg &arg1, const RooCmdArg &arg2={}, const RooCmdArg &arg3={}, const RooCmdArg &arg4={}, const RooCmdArg &arg5={}, const RooCmdArg &arg6={}, const RooCmdArg &arg7={}, const RooCmdArg &arg8={}) const
Create a reduced copy of this dataset.
RooArgSet const * getGlobalObservables() const
Returns snapshot of global observables stored in this data.
Definition RooAbsData.h:288
Abstract base class for objects that are lvalues, i.e.
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:218
Abstract base class for objects that represent a real value that may appear on the left hand side of ...
void setBin(Int_t ibin, const char *rangeName=nullptr) override
Set value to center of bin 'ibin' of binning 'rangeName' (or of default binning if no range is specif...
Context to temporarily change the error logging mode as long as the context is alive.
Definition RooAbsReal.h:320
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:24
Implements a RooAbsBinning in terms of an array of boundary values, posing no constraints on the choi...
Definition RooBinning.h:27
Object to represent discrete states.
Definition RooCategory.h:28
bool defineType(const std::string &label)
Define a state with given name.
static TClass * Class()
Named container for two doubles, two integers two object points and three string pointers that can be...
Definition RooCmdArg.h:26
static const RooCmdArg & none()
Return reference to null argument.
Definition RooCmdArg.cxx:52
TObject * Clone(const char *newName=nullptr) const override
Make a clone of an object using the Streamer facility.
Definition RooCmdArg.h:58
Represents a constant real-valued object.
Definition RooConstVar.h:23
static TClass * Class()
Container class to hold N-dimensional binned data.
Definition RooDataHist.h:40
Container class to hold unbinned data.
Definition RooDataSet.h:33
RooFitResult is a container class to hold the input and output of a PDF fit to a dataset.
TMatrixDSym conditionalCovarianceMatrix(const RooArgList &params) const
Return a reduced covariance matrix, which is calculated as.
void setCovQual(Int_t val)
const TMatrixDSym & covarianceMatrix() const
Return covariance matrix.
Int_t statusCodeHistory(UInt_t icycle) const
const RooArgList & floatParsInit() const
Return list of floating parameters before fit.
TMatrixDSym reducedCovarianceMatrix(const RooArgList &params) const
Return a reduced covariance matrix (Note that Vred is a simple sub-matrix of V, row/columns are order...
const RooArgList & randomizePars() const
Generate random perturbations of the final parameters using the covariance matrix.
void setCovarianceMatrix(TMatrixDSym &V)
Store externally provided correlation matrix in this RooFitResult ;.
double edm() const
Return estimated distance to minimum.
const RooArgList & constPars() const
Return list of constant parameters.
const char * statusLabelHistory(UInt_t icycle) const
Int_t covQual() const
Return MINUIT quality code of covariance matrix.
TH2 * correlationHist(const char *name="correlation_matrix") const
Return TH2D of correlation matrix.
const RooArgList & floatParsFinal() const
Return list of floating parameters after fit.
Int_t status() const
Return MINUIT status code.
void setFinalParList(const RooArgList &list)
Fill the list of final values of the floating parameters.
UInt_t numStatusHistory() const
double minNll() const
Return minimized -log(L) value.
A RooFormulaVar is a generic implementation of a real-valued object, which takes a RooArgList of serv...
Plain Gaussian p.d.f.
Definition RooGaussian.h:24
Implementation of a probability density function that takes a RooArgList of servers and a C++ express...
A real-valued function sampled from a multidimensional histogram.
Definition RooHistFunc.h:31
bool isBinnedDistribution(const RooArgSet &) const override
Tests if the distribution is binned. Unless overridden by derived classes, this always returns false.
Definition RooHistFunc.h:95
When using RooFit, statistical models can be conveniently handled and stored as a RooWorkspace.
Collection class for internal use, storing a collection of RooAbsArg pointers in a doubly linked list...
static RooMsgService & instance()
Return reference to singleton instance.
Implementation of a RooCacheManager<RooAbsCacheElement> that specializes in the storage of cache elem...
Poisson pdf.
Definition RooPoisson.h:19
Efficient implementation of a product of PDFs of the form.
Definition RooProdPdf.h: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()
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.
ModelConfig is a simple class that holds configuration information specifying how a model should be u...
Definition ModelConfig.h:35
A RooAbsArg implementing string values.
Implementation of RooAbsBinning that provides a uniform binning in 'n' bins between the range end poi...
Persistable container for RooFit projects.
bool removeSet(const char *name)
Remove a named set from the workspace.
static TClass * Class()
RooFactoryWSTool & factory()
Return instance to factory tool.
bool import(const RooAbsArg &arg, const RooCmdArg &arg1={}, const RooCmdArg &arg2={}, const RooCmdArg &arg3={}, const RooCmdArg &arg4={}, const RooCmdArg &arg5={}, const RooCmdArg &arg6={}, const RooCmdArg &arg7={}, const RooCmdArg &arg8={}, const RooCmdArg &arg9={})
Import a RooAbsArg object, e.g.
virtual void 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 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 Color_t GetLineColor() const
Return the line color.
Definition TAttLine.h:33
Marker Attributes class.
Definition TAttMarker.h:19
virtual void SetMarkerStyle(Style_t mstyle=1)
Set the marker style.
Definition TAttMarker.h:40
virtual Font_t GetTextFont() const
Return the text font.
Definition TAttText.h:35
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
TAxis()
Default constructor.
Definition TAxis.cxx:50
virtual void Set(Int_t nbins, Double_t xmin, Double_t xmax)
Initialize axis with fix bins.
Definition TAxis.cxx:794
virtual Int_t FindFixBin(Double_t x) const
Find bin number corresponding to abscissa x.
Definition TAxis.cxx:419
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:4943
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:3037
virtual Int_t GetEntries() const
static Int_t GetColorPalette(Int_t i)
Static function returning the color number i in current palette.
Definition TColor.cxx:1504
static Int_t GetNumberOfColors()
Static function returning number of colors in the color palette.
Definition TColor.cxx:1524
Describe directory structure in memory.
Definition TDirectory.h:45
static TClass * Class()
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.
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
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 SetPointEYlow(Int_t i, Double_t eyl)
Set EYlow for point i.
virtual void SetPointError(Double_t exl, Double_t exh, Double_t eyl, Double_t eyh)
Set ex and ey values for point pointed by the mouse.
virtual void SetPointEYhigh(Int_t i, Double_t eyh)
Set EYhigh for point i.
Double_t GetErrorYlow(Int_t i) const override
Get low error on Y.
A TGraphErrors is a TGraph with error bars.
Double_t GetErrorYlow(Int_t bin) const override
It returns the error along Y at point i.
Double_t GetErrorYhigh(Int_t bin) const override
It returns the error along Y at point i.
A TGraph is an object made of two arrays X and Y with npoints each.
Definition TGraph.h:41
virtual void AddPoint(Double_t x, Double_t y)
Append a new point to the graph.
Definition TGraph.h: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:2342
Int_t GetN() const
Definition TGraph.h:132
void SetName(const char *name="") override
Set graph name.
Definition TGraph.cxx:2381
void Draw(Option_t *chopt="") override
Draw this graph with its current attributes.
Definition TGraph.cxx:831
virtual TH1F * GetHistogram() const
Returns a pointer to the histogram used to draw the axis Takes into account the two following cases.
Definition TGraph.cxx:1428
virtual Double_t GetPointY(Int_t i) const
Get y value for point i.
Definition TGraph.cxx:1555
void SetTitle(const char *title="") override
Change (i.e.
Definition TGraph.cxx:2397
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:8958
void SetTitle(const char *title) override
Change/set the title.
Definition TH1.cxx:6739
virtual Int_t GetNbinsY() const
Definition TH1.h:298
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
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 Bool_t Add(TF1 *h1, Double_t c1=1, Option_t *option="")
Performs the operation: this = this + c1*f1 if errors are defined (see TH1::Sumw2),...
Definition TH1.cxx:826
TAxis * GetYaxis()
Definition TH1.h:325
void Draw(Option_t *option="") override
Draw this histogram with options.
Definition TH1.cxx:3066
TList * GetListOfFunctions() const
Definition TH1.h:244
TObject * Clone(const char *newname="") const override
Make a complete copy of the underlying object.
Definition TH1.cxx:2752
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:9011
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
Book space in a file, create I/O buffers, to fill them, (un)compress them.
Definition TKey.h:28
Storage class for one entry of a TLegend.
This class displays a legend box (TPaveText) containing several legend entries.
Definition TLegend.h:23
Use the TLine constructor to create a simple line.
Definition TLine.h:22
virtual void SetY2(Double_t y2)
Definition TLine.h: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 Clear(Option_t *option="") override
Remove all objects from the list.
Definition TList.cxx:400
void Add(TObject *obj) override
Definition TList.h:81
Int_t GetNrows() const
Int_t GetNcols() const
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
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:551
virtual const char * GetName() const
Returns name of object.
Definition TObject.cxx:444
virtual const char * ClassName() const
Returns name of class to which the object belongs.
Definition TObject.cxx:213
virtual void Warning(const char *method, const char *msgfmt,...) const
Issue warning message.
Definition TObject.cxx:979
virtual TObject * DrawClone(Option_t *option="") const
Draw a clone of this object in the current selected pad with: gROOT->SetSelectedPad(c1).
Definition TObject.cxx:305
virtual TObject * FindObject(const char *name) const
Must be redefined in derived classes.
Definition TObject.cxx:408
virtual void Delete(Option_t *option="")
Delete this object.
Definition TObject.cxx:254
void SetBit(UInt_t f, Bool_t set)
Set or unset the user status bits as specified in f.
Definition TObject.cxx:786
virtual Bool_t InheritsFrom(const char *classname) const
Returns kTRUE if object inherits from class "classname".
Definition TObject.cxx:530
virtual void Error(const char *method, const char *msgfmt,...) const
Issue error message.
Definition TObject.cxx:993
virtual const char * GetTitle() const
Returns title of object.
Definition TObject.cxx:488
virtual void Draw(Option_t *option="")
Default Draw method for all objects.
Definition TObject.cxx:280
virtual void Print(Option_t *option="") const
This method must be overridden when a class wants to print itself.
Definition TObject.cxx:642
@ 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:967
The most important graphics class in the ROOT system.
Definition TPad.h:28
A Pave (see TPave) with text, lines or/and boxes inside.
Definition TPaveText.h:21
virtual TText * AddText(Double_t x1, Double_t y1, const char *label)
Add a new Text line to this pavetext at given coordinates.
virtual void SetY1NDC(Double_t y1)
Definition TPave.h:84
virtual void ConvertNDCtoPad()
Convert pave coordinates from NDC to Pad coordinates.
Definition TPave.cxx:139
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
This is the ROOT implementation of the Qt object communication mechanism (see also http://www....
Definition TQObject.h:48
Regular expression class.
Definition TRegexp.h:31
This class creates a ROOT object browser, constituted by three main tabs.
Sequenceable collection abstract base class.
Stopwatch class.
Definition TStopwatch.h:28
Provides iteration through tokens of a given string.
Definition TPRegexp.h:143
Bool_t NextToken()
Get the next token, it is stored in this TString.
Basic string class.
Definition TString.h: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
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
const char * Data() const
Definition TString.h:376
Bool_t BeginsWith(const char *s, ECaseCompare cmp=kExact) const
Definition TString.h:623
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
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
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 ExternalConstraints(const RooArgSet &constraintPdfs)
RooCmdArg Range(const char *rangeName, bool adjustNorm=true)
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)
double gaussian_cdf(double x, double sigma=1, double x0=0)
Alternative name for same function.
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
constexpr Double_t C()
Velocity of light in .
Definition TMath.h:114
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
Double_t StdDev(Long64_t n, const T *a, const Double_t *w=nullptr)
Definition TMath.h:527
#define BEGIN_XROOFIT_NAMESPACE
Definition Config.h:24
#define END_XROOFIT_NAMESPACE
Definition Config.h:25
static const char * what
Definition stlLoader.cc:5
bool isNaNWithPayload() const
Test if this struct has a float packed into its mantissa.
static float unpackNaN(double val)
If val is NaN and a this NaN has been tagged as containing a payload, unpack the float from the manti...
th1 Draw()
TLine lv
Definition textalign.C:5
TMarker m
Definition textangle.C:8
TLine l
Definition textangle.C:4
auto * tt
Definition textangle.C:16
static uint64_t sum(uint64_t i)
Definition Factory.cxx: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
double new_getPropagatedError(const RooAbsReal &f, const RooFitResult &fr, const RooArgSet &nset={}, RooArgList **pars=nullptr, bool asymHi=false, bool asymLo=false)
TLegend * getLegend(bool create=true, bool doPaint=false)