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 exploring
38 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
98{
99 return a->workspace();
100}
102{
103 return w->sets();
104}
106{
107 return w->getSnapshots();
108}
110{
111 return b->GetActBrowser();
112}
114{
115 return b->GetRootDir();
116}
118{
119 return b->GetListTree();
120}
121#define GETDMP(o, m) \
122 *reinterpret_cast<void **>(reinterpret_cast<unsigned char *>(o) + o->Class()->GetDataMemberOffset(#m))
123
124#endif
125
126#include "RooAddition.h"
127
128#include "RooCategory.h"
129#include "RooRealVar.h"
130#include "RooStringVar.h"
131#include "RooBinning.h"
132#include "RooUniformBinning.h"
133
134#include "RooAbsData.h"
135#include "RooDataHist.h"
136#include "RooDataSet.h"
137
138#include "xRooFit/xRooNode.h"
139#include "xRooFit/xRooFit.h"
140
141#include "TH1.h"
142#include "TBrowser.h"
143#include "TROOT.h"
144#include "TQObject.h"
145#include "TAxis.h"
146#include "TGraphAsymmErrors.h"
147#include "TMath.h"
148#include "TPRegexp.h"
149#include "TRegexp.h"
150#include "TExec.h"
151#include "TPaveText.h"
152
153#include "TGListTree.h"
154#include "TGMsgBox.h"
155#include "TGedEditor.h"
156#include "TGMimeTypes.h"
157#include "TH2.h"
158#include "RooExtendPdf.h"
159#include "RooExtendedBinding.h"
160
162
163#include "coutCapture.h"
164
165// #include "RooFitTrees/RooFitResultTree.h"
166// #include "RooFitTrees/RooDataTree.h"
167#include "TFile.h"
168#include "TSystem.h"
169#include "TKey.h"
170#include "TEnv.h"
171#include "TStyle.h"
172
173#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 26, 00)
175#endif
176
177#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 24, 00)
178#include "RooBinSamplingPdf.h"
179#endif
180
181#include "RooPoisson.h"
182#include "RooGaussian.h"
183#include "RooFormulaVar.h"
184#include "RooGenericPdf.h"
185#include "TVectorD.h"
186#include "TStopwatch.h"
187#include "TTimeStamp.h"
188
189#include <csignal>
190
191#include "TCanvas.h"
192#include "THStack.h"
193
194#include "TLegend.h"
195#include "TLegendEntry.h"
196#include "TGraphErrors.h"
197#include "TMultiGraph.h"
198#include "TFrame.h"
199#include "RooProjectedPdf.h"
200#include "TMemFile.h"
201#include "TGaxis.h"
202#include "TPie.h"
203// #include <thread>
204// #include <future>
205
206#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
207#include "RooNaNPacker.h"
208#endif
209
210
211
213
214xRooNode::InteractiveObject *xRooNode::gIntObj = nullptr;
215std::map<std::string, std::tuple<std::function<double(double, double, double)>, bool>> xRooNode::auxFunctions;
216void xRooNode::SetAuxFunction(const char *title, const std::function<double(double, double, double)> &func,
217 bool symmetrize)
218{
219 auxFunctions[title] = std::make_tuple(func, symmetrize);
220}
221
222template <typename T>
223const T &_or_func(const T &a, const T &b)
224{
225 if (a)
226 return a;
227 return b;
228}
229
230////////////////////////////////////////////////////////////////////////////////
231/// Create new object of type classname, with given name and title, and own-wrap it
232/// i.e. the xRooNode will delete the object when the node (and any that reference it) is destroyed
233///
234/// \param classname : the type of the object to create
235/// \param name : the name to give the object
236/// \param title : the title to give the object
237
238xRooNode::xRooNode(const char *classname, const char *name, const char *title)
239 : xRooNode(name, std::shared_ptr<TObject>(TClass::GetClass(classname)
240 ? reinterpret_cast<TObject *>(TClass::GetClass(classname)->New())
241 : nullptr,
242 [](TObject *o) {
243 if (auto w = dynamic_cast<RooWorkspace *>(o); w) {
244#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
245 w->_embeddedDataList.Delete();
246#endif
247 xRooNode(*w, std::make_shared<xRooNode>()).sterilize();
248 }
249 if (o)
250 delete o;
251 }))
252{
253 if (auto a = get<TNamed>(); a)
254 a->SetName(name);
255 SetTitle(title);
256}
257
258xRooNode::xRooNode(const char *name, const std::shared_ptr<TObject> &comp, const std::shared_ptr<xRooNode> &parent)
259 : TNamed(name, ""), fComp(comp), fParent(parent)
260{
261
262 if (!fComp && !fParent && name && strlen(name) > 0) {
263 char *_path = gSystem->ExpandPathName(name);
264 TString pathName = TString(_path);
265 delete[] _path;
266 if (!gSystem->AccessPathName(pathName)) {
267 // if file is json can try to read
268 if (pathName.EndsWith(".json")) {
269#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 26, 00)
270 fComp = std::make_shared<RooWorkspace>("workspace", name);
271 RooJSONFactoryWSTool tool(*get<RooWorkspace>());
274 if (!tool.importJSON(pathName.Data())) {
275 Error("xRooNode", "Error reading json workspace %s", name);
276 fComp.reset();
277 }
279#else
280 Error("xRooNode", "json format workspaces available only in ROOT 6.26 onwards");
281#endif
282 } else {
283
284 // using acquire in the constructor seems to cause a mem leak according to valgrind ... possibly because
285 // (*this) gets called on it before the node is fully constructed
286 auto _file = std::make_shared<TFile>(
287 pathName); // acquire<TFile>(name); // acquire file to ensure stays open while we have the workspace
288 // actually it appears we don't need to keep the file open once we've loaded the workspace, but should be
289 // no harm doing so
290 // otherwise the workspace doesn't saveas
291 auto keys = _file->GetListOfKeys();
292 if (keys) {
293 for (auto &&k : *keys) {
294 auto cl = TClass::GetClass(((TKey *)k)->GetClassName());
295 if (cl == RooWorkspace::Class() || cl->InheritsFrom("RooWorkspace")) {
296 fComp.reset(_file->Get<RooWorkspace>(k->GetName()), [](TObject *ws) {
297 // memory leak in workspace, some RooLinkedLists aren't cleared, fixed in ROOT 6.28
298 if (ws) {
299#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
300 dynamic_cast<RooWorkspace *>(ws)->_embeddedDataList.Delete();
301#endif
302 xRooNode(*ws, std::make_shared<xRooNode>()).sterilize();
303 delete ws;
304 }
305 });
306 if (fComp) {
307 TNamed::SetNameTitle(fComp->GetName(), fComp->GetTitle());
308 fParent = std::make_shared<xRooNode>(
309 _file); // keep file alive - seems necessary to save workspace again in some cases
310 break;
311 }
312 }
313 }
314 }
315 }
316 } else if (pathName.EndsWith(".root") || pathName.EndsWith(".json")) {
317 throw std::runtime_error(TString::Format("%s does not exist", name));
318 }
319 }
320
321 if (auto _ws = get<RooWorkspace>(); _ws && (!parent || parent->get<TFile>())) {
324 .removeTopic(RooFit::NumIntegration); // stop info message every time
325
326 // check if any of the open files have version numbers greater than our major version
327 // may not read correctly
328 for (auto f : *gROOT->GetListOfFiles()) {
329 if ((dynamic_cast<TFile *>(f)->GetVersion() / 100) > (gROOT->GetVersionInt() / 100)) {
330 Warning("xRooNode", "There is file open with version %d > current version %d ... results may be wrong",
331 dynamic_cast<TFile *>(f)->GetVersion(), gROOT->GetVersionInt());
332 }
333 }
334
335 // use the datasets if any to 'mark' observables
336 int checkCount = 0;
337 for (auto &d : _ws->allData()) {
338 for (auto &a : *d->get()) {
339 if (auto v = _ws->var(a->GetName()); v)
340 v->setAttribute("obs");
341 else if (auto c = _ws->cat(a->GetName()); c)
342 c->setAttribute("obs");
343 }
344 // count how many ds are checked ... if none are checked will check the first
345 checkCount += d->TestBit(1 << 20);
346 }
347
348 if (checkCount == 0 && !_ws->allData().empty())
349 _ws->allData().back()->SetBit(1 << 20, true);
350
351 if (auto _set = dynamic_cast<RooArgSet *>(GETWSSNAPSHOTS(_ws).find("NominalParamValues")); _set) {
352 for (auto s : *_set) {
353 if (auto v = dynamic_cast<RooRealVar *>(s); v) {
354 _ws->var(s->GetName())->setStringAttribute("nominal", TString::Format("%f", v->getVal()));
355 }
356 }
357 }
358
359 // also flag global observables ... relies on ModelConfig existences
360 RooArgSet _allGlobs;
361 for (auto &[k, v] : GETWSSETS(_ws)) {
362 if (k == "globalObservables" || TString(k).EndsWith("_GlobalObservables")) {
363 for (auto &s : v) {
364 _allGlobs.add(*s);
365 s->setAttribute("obs");
366 s->setAttribute("global");
367 }
368 } else if (TString(k).EndsWith("_Observables")) {
369 const_cast<RooArgSet &>(v).setAttribAll("obs");
370 } else if (TString(k).EndsWith("_POI")) {
371 for (auto &s : v) {
372 s->setAttribute("poi");
373 auto _v = dynamic_cast<RooRealVar *>(s);
374 if (!_v)
375 continue;
376 // if (!_v->hasRange("physical")) {
377 // _v->setRange("physical", 0, std::numeric_limits<double>::infinity());
378 // // ensure range of poi is also straddling 0
379 // if (_v->getMin() >= 0)
380 // _v->setMin(-1e-5);
381 // }
382 }
383 } else if (TString(k).EndsWith("_NuisParams")) {
384 const_cast<RooArgSet &>(v).setAttribAll("np");
385 }
386 }
387 if (!_allGlobs.empty() && GETWSSETS(_ws).count("globalObservables") == 0) {
388 _ws->defineSet("globalObservables", _allGlobs);
389 }
390
391 // now check if any pars don't have errors defined (not same as error=0) ... if so, use the first pdf (if there is
392 // one) to try setting values from
393 if (!_ws->allPdfs().empty()) {
394 std::set<RooRealVar *> noErrorPars;
395 std::string parNames;
396 for (auto &p : np()) { // infer errors on all floating non-poi parameters
397 auto v = p->get<RooRealVar>();
398 if (!v)
399 continue;
400 if (!v->hasError()) {
401 noErrorPars.insert(v);
402 if (!parNames.empty())
403 parNames += ",";
404 parNames += v->GetName();
405 }
406 }
407 if (!noErrorPars.empty()) {
408 Warning(
409 "xRooNode",
410 "Inferring initial errors of %d parameters (give all nuisance parameters an error to avoid this msg)",
411 int(noErrorPars.size()));
412 // get the first top-level pdf
413 browse();
414 for (auto &a : *this) {
415 if (a->fFolder == "!models") {
416 try {
417 auto fr = a->floats().reduced(parNames).fitResult("prefit");
418 if (auto _fr = fr.get<RooFitResult>(); _fr) {
419 for (auto &v : noErrorPars) {
420 if (auto arg = dynamic_cast<RooRealVar *>(_fr->floatParsFinal().find(v->GetName()));
421 arg && arg->hasError()) {
422 v->setError(arg->getError());
423 }
424 }
425 }
426 } catch (...) {
427 }
428 }
429 }
430 }
431 }
432 }
433
434 if (strlen(GetTitle()) == 0) {
435 if (fComp)
436 TNamed::SetTitle(fComp->GetTitle());
437 else
438 TNamed::SetTitle(GetName());
439 }
440}
441
442xRooNode::xRooNode(const TObject &comp, const std::shared_ptr<xRooNode> &parent)
443 : xRooNode(/*[](const TObject& c) {
444c.InheritsFrom("RooAbsArg");
445if (s) {
446return (s->getStringAttribute("alias")) ? s->getStringAttribute("alias") : c.GetName();
447}
448return c.GetName();
449}(comp)*/
450 (comp.InheritsFrom("RooAbsArg") && dynamic_cast<const RooAbsArg *>(&comp)->getStringAttribute("alias"))
451 ? dynamic_cast<const RooAbsArg *>(&comp)->getStringAttribute("alias")
452 : comp.GetName(),
453 std::shared_ptr<TObject>(const_cast<TObject *>(&comp), [](TObject *) {}), parent)
454{
455}
456
457xRooNode::xRooNode(const std::shared_ptr<TObject> &comp, const std::shared_ptr<xRooNode> &parent)
458 : xRooNode(
459 [&]() {
460 if (auto a = std::dynamic_pointer_cast<RooAbsArg>(comp); a && a->getStringAttribute("alias"))
461 return a->getStringAttribute("alias");
462 if (comp)
463 return comp->GetName();
464 return "";
465 }(),
466 comp, parent)
467{
468}
469
471
472void xRooNode::Checked(TObject *obj, bool val)
473{
474 if (obj != this)
475 return;
476
477 // cycle through states:
478 // unhidden and selected: tick, no uline
479 // hidden and unselected: notick, uline
480 // unhidden and unselected: tick, uline
481 if (auto o = get<RooAbsReal>(); o) {
482 if (o->isSelectedComp() && !val) {
483 // deselecting and hiding
484 o->selectComp(val);
485 o->setAttribute("hidden");
486 } else if (!o->isSelectedComp() && !val) {
487 // selecting
488 o->selectComp(!val);
489 } else if (val) {
490 // unhiding but keeping unselected
491 o->setAttribute("hidden", false);
492 }
493 auto item = GetTreeItem(nullptr);
494 item->CheckItem(!o->getAttribute("hidden"));
495 if (o->isSelectedComp())
496 item->ClearColor();
497 else
498 item->SetColor(kGray);
499 return;
500 }
501
502 if (auto o = get(); o) {
503 // if (o->TestBit(1<<20)==val) return; // do nothing
504 o->SetBit(1 << 20, val); // TODO: check is 20th bit ok to play with?
505 if (auto fr = get<RooFitResult>(); fr) {
506 if (auto _ws = ws(); _ws) {
507 if (val) {
508 // ensure fit result is in genericObjects list ... if not, add a copy ...
509 if (!_ws->genobj(fr->GetName())) {
510 _ws->import(*fr);
511 if (auto wfr = dynamic_cast<RooFitResult *>(_ws->genobj(fr->GetName()))) {
512 fr = wfr;
513 }
514 }
515 RooArgSet _allVars = _ws->allVars();
516 _allVars = fr->floatParsFinal();
517 _allVars = fr->constPars();
518 for (auto &i : fr->floatParsInit()) {
519 auto v = dynamic_cast<RooRealVar *>(_allVars.find(i->GetName()));
520 if (v)
521 v->setStringAttribute("initVal", TString::Format("%f", dynamic_cast<RooRealVar *>(i)->getVal()));
522 }
523 // uncheck all other fit results
524 for (auto oo : _ws->allGenericObjects()) {
525 if (auto ffr = dynamic_cast<RooFitResult *>(oo); ffr && ffr != fr) {
526 ffr->ResetBit(1 << 20);
527 }
528 }
529 } else
530 _ws->allVars() = fr->floatParsInit();
531 }
532 if (auto item = GetTreeItem(nullptr); item) {
533 // update check marks on siblings
534 if (auto first = item->GetParent()->GetFirstChild()) {
535 do {
536 if (first->HasCheckBox()) {
537 auto _obj = static_cast<xRooNode *>(first->GetUserData());
538 first->CheckItem(_obj->get() && _obj->get()->TestBit(1 << 20));
539 }
540 } while ((first = first->GetNextSibling()));
541 }
542 }
543 }
544 }
545}
546
548{
549 static bool blockBrowse = false;
550 if (blockBrowse)
551 return;
552 if (b == 0) {
553 auto b2 = dynamic_cast<TBrowser *>(gROOT->GetListOfBrowsers()->Last());
554 if (!b2 || !b2->GetBrowserImp()) { // no browser imp if browser was closed
555 blockBrowse = true;
556 gEnv->SetValue("X11.UseXft", "no"); // for faster x11
557 gEnv->SetValue("X11.Sync", "no");
558 gEnv->SetValue("X11.FindBestVisual", "no");
559 gEnv->SetValue("Browser.Name", "TRootBrowser"); // forces classic root browser (in 6.26 onwards)
560 gEnv->SetValue("Canvas.Name", "TRootCanvas");
561 b2 = new TBrowser("nodeBrowser", this, "RooFit Browser");
562 blockBrowse = false;
563 } else if (strcmp(b2->GetName(), "nodeBrowser") == 0) {
564 blockBrowse = true;
565 b2->BrowseObject(this);
566 blockBrowse = false;
567 } else {
568 auto _b = dynamic_cast<TGFileBrowser *>(GETACTBROWSER(dynamic_cast<TRootBrowser *>(b2->GetBrowserImp())));
569 if (_b)
570 _b->AddFSDirectory("Workspaces", 0, "SetRootDir");
571 /*auto l = Node2::Class()->GetMenuList();
572 auto o = new CustomClassMenuItem(TClassMenuItem::kPopupUserFunction,Node2::Class(),
573 "blah blah blah","BlahBlah",0,"Option_t*",-1,true);
574 //o->SetCall(o,"BlahBlah","Option_t*",-1);
575 l->AddFirst(o);*/
576 // b->BrowseObject(this);
577 _b->GotoDir(0);
578 _b->Add(this, GetName());
579 // b->Add(this);
580 }
581 return;
582 }
583
584 if (auto item = GetTreeItem(b); item) {
585 if (!item->IsOpen() && IsFolder())
586 return; // no need to rebrowse if closing
587 // update check marks on any child items
588 if (auto first = item->GetFirstChild()) {
589 do {
590 if (first->HasCheckBox()) {
591 auto _obj = static_cast<xRooNode *>(first->GetUserData());
592 first->CheckItem(_obj->get() &&
593 (_obj->get()->TestBit(1 << 20) ||
594 (_obj->get<RooAbsArg>() && !_obj->get<RooAbsArg>()->getAttribute("hidden"))));
595 }
596 } while ((first = first->GetNextSibling()));
597 }
598 }
599
600 browse();
601
602 // for top-level pdfs default to having the .vars browsable too
603 if (get<RooAbsPdf>() && fFolder == "!models" && !_IsShowVars_()) {
604 fBrowsables.push_back(std::make_shared<xRooNode>(vars()));
605 }
606
607 if (auto _fr = get<RooFitResult>(); _fr && fBrowsables.empty()) {
608 // have some common drawing options
609 fBrowsables.push_back(std::make_shared<xRooNode>(".Draw(\"pull\")", nullptr, *this));
610 fBrowsables.push_back(std::make_shared<xRooNode>(".Draw(\"corrcolztext\")", nullptr, *this));
611 if (std::unique_ptr<RooAbsCollection>(_fr->floatParsFinal().selectByAttrib("poi", true))->size() == 1) {
612 fBrowsables.push_back(std::make_shared<xRooNode>(".Draw(\"impact\")", nullptr, *this));
613 }
614 }
615
616 if (empty() && fBrowsables.empty()) {
617 try {
618 if (auto s = get<TStyle>()) {
619 s->SetFillAttributes();
620 if (auto ed = dynamic_cast<TGedEditor *>(TVirtualPadEditor::GetPadEditor())) {
621 ed->SetModel(gPad, s, kButton1Down, true);
622 }
623 } else if (TString(GetName()).BeginsWith(".Draw(\"") && fParent) {
624 fParent->Draw(TString(TString(GetName())(7, strlen(GetName()) - 9)) + b->GetDrawOption());
625 } else {
626 Draw(b->GetDrawOption());
627 }
628 } catch (const std::exception &e) {
629 new TGMsgBox(
630 gClient->GetRoot(),
631 (gROOT->GetListOfBrowsers()->At(0))
632 ? dynamic_cast<TGWindow *>(static_cast<TBrowser *>(gROOT->GetListOfBrowsers()->At(0))->GetBrowserImp())
633 : gClient->GetRoot(),
634 "Exception", e.what(),
635 kMBIconExclamation); // deletes self on dismiss?
636 }
637 }
638
639 bool hasFolders = false;
640 if (strlen(GetName()) > 0 && GetName()[0] != '!') { // folders don't have folders
641 for (auto &c : *this) {
642 if (!c->fFolder.empty()) {
643 hasFolders = true;
644 break;
645 }
646 }
647 }
648 // auto _ws = get<RooWorkspace>();
649 if (/*_ws*/ hasFolders) {
650 // organize in folders
651 auto _folders = find(".folders");
652 if (!_folders) {
653 _folders = emplace_back(std::make_shared<xRooNode>(".folders", nullptr, *this));
654 }
655 // ensure entry in folders for every folder type ...
656 for (auto &v : *this) {
657 if (v->fFolder != "" && !_folders->find(v->fFolder, false)) {
658 _folders->emplace_back(std::make_shared<xRooNode>(v->fFolder.c_str(), nullptr, *this));
659 }
660 }
661 // now just add all the folders
662 for (auto &v : *_folders) {
663 TString _name = v->GetName();
664 if (_name.BeginsWith('!'))
665 _name = _name(1, _name.Length()); // strip ! from display
666 b->Add(v.get(), _name);
667 }
668 }
669
670 for (auto &v : *this) {
671 if (hasFolders && !v->fFolder.empty())
672 continue; // in the folders
673 if (strcmp(v->GetName(), ".folders") == 0)
674 continue; // never 'browse' the folders property
675 auto _fr = v->get<RooFitResult>();
676 int _checked = (v->get<RooAbsData>() || _fr) ? v->get()->TestBit(1 << 20) : -1;
677 if (_fr && ((_fr->status() == 0 && _fr->numStatusHistory() == 0) || (_fr->floatParsFinal().empty()))) {
678 // this is a "PARTIAL" fit result ... don't allow it to be selected
679 _checked = -1;
680 }
681 if (v->get<RooAbsPdf>() && get<RooSimultaneous>())
682 _checked = !v->get<RooAbsArg>()->getAttribute("hidden");
683 TString _name = v->GetName();
684 if (v->get() && _name.BeginsWith(TString(v->get()->ClassName()) + "::")) {
685 _name = _name(strlen(v->get()->ClassName()) + 2, _name.Length());
686 }
687 if (_name.BeginsWith(".")) {
688 // property node -- display the name of the contained object
689 if (v->get())
690 _name = TString::Format("%s: %s::%s", _name.Data(), v->get()->ClassName(),
691 (v->get<RooAbsArg>() && v->get<RooAbsArg>()->getStringAttribute("alias"))
692 ? v->get<RooAbsArg>()->getStringAttribute("alias")
693 : v->get()->GetName());
694 } else if (v->get() && !v->get<TFile>() && !TString(v->GetName()).BeginsWith('/'))
695 _name = TString::Format("%s::%s", v->get()->ClassName(), _name.Data());
696 if (auto _type = v->GetNodeType(); strlen(_type)) {
697 // decided not to show const values until figure out how to update if value changes
698 /*if (TString(_type)=="Const") _name += TString::Format(" [%s=%g]",_type,v->get<RooConstVar>()->getVal());
699 else*/
700 _name += TString::Format(" [%s]", _type);
701 }
702 if (auto fv = v->get<RooFormulaVar>()) {
703 TString formu = TString::Format(" [%s]", fv->expression());
704 for (size_t i = 0; i < fv->dependents().size(); i++) {
705 formu.ReplaceAll(TString::Format("x[%zu]", i), fv->dependents()[i].GetName());
706 }
707 _name += formu;
708 } else if (auto gv = v->get<RooGenericPdf>()) {
709 TString formu = TString::Format(" [%s]", gv->expression());
710 for (size_t i = 0; i < gv->dependents().size(); i++) {
711 formu.ReplaceAll(TString::Format("x[%zu]", i), gv->dependents()[i].GetName());
712 }
713 _name += formu;
714 }
715 // tool tip defaults to displaying name and title, so temporarily set name to obj name if has one
716 // and set title to the object type
717 TString nameSave(v->TNamed::GetName());
718 TString titleSave(v->TNamed::GetTitle());
719 if (auto o = v->get(); o)
720 v->TNamed::SetNameTitle(o->GetName(), o->ClassName());
721 b->Add(v.get(), _name, _checked);
722 if (auto o = v->get(); o)
723 v->TNamed::SetNameTitle(nameSave, titleSave);
724 if (_checked != -1) {
725 dynamic_cast<TQObject *>(b->GetBrowserImp())
726 ->Connect("Checked(TObject *, bool)", ClassName(), v.get(), "Checked(TObject *, bool)");
727 }
728 if (_fr) {
729 if (_fr->status() || _fr->covQual() != 3) { // snapshots or bad fits
730 v->GetTreeItem(b)->SetColor((_fr->numStatusHistory() || _fr->floatParsFinal().empty()) ? kRed : kBlue);
731 } else if (_fr->numStatusHistory() == 0) { // partial fit result ..
732 v->GetTreeItem(b)->SetColor(kGray);
733 }
734 }
735 if ((v->fFolder == "!np" || v->fFolder == "!poi")) {
736 if (v->get<RooAbsArg>()->getAttribute("Constant")) {
737 v->GetTreeItem(b)->SetColor(kGray);
738 } else
739 v->GetTreeItem(b)->ClearColor();
740 }
741 if (auto _htr = v->get<RooStats::HypoTestResult>(); _htr) {
742 // check for fit statuses
743 if (auto fits = _htr->GetFitInfo()) {
744 for (int i = 0; i < fits->numEntries(); i++) {
745 // if any fit (other than a genFit) is bad, flag point as bad
746 if (fits->get(i)->getCatIndex("type") != 5 && fits->get(i)->getRealValue("status") != 0) {
747 v->GetTreeItem(b)->SetColor(kRed);
748 break;
749 }
750 }
751 } else {
752 v->GetTreeItem(b)->SetColor(kBlue); // unknown fit status
753 }
754 }
755
756 // v.fBrowsers.insert(b);
757 }
758
759 // for pdfs, check for datasets too and add to list
760 /*if (get<RooAbsPdf>()) {
761 auto dsets = datasets();
762 if (!dsets.empty()) {
763 // check if already have .datasets() in browsables
764 bool found(false);
765 for(auto& p : fBrowsables) {
766 if (TString(p->GetName())==".datasets()") {found=true;
767 // add
768 break;
769 }
770 }
771 if (!found) {
772 fBrowsables.push_back(std::make_shared<xRooNode>(dsets));
773 }
774 }
775 }*/
776 // browse the browsables too
777 for (auto &v : fBrowsables) {
778 TString _name = v->GetName();
779 if (_name == ".memory")
780 continue; // hide the memory from browsing, if put in browsables
781 TString nameSave(v->TNamed::GetName());
782 TString titleSave(v->TNamed::GetTitle());
783 if (auto o = v->get(); o)
784 v->TNamed::SetNameTitle(o->GetName(), o->ClassName());
785 b->Add(v.get(), _name, -1);
786 if (auto o = v->get(); o)
787 v->TNamed::SetNameTitle(nameSave, titleSave);
788 }
789
790 b->SetSelected(this);
791}
792
794{
795 if (!set) {
796 // can't remove as causes a crash, need to remove from the browser first
797 /*for(auto itr = fBrowsables.begin(); itr != fBrowsables.end(); ++itr) {
798 if (strcmp((*itr)->GetName(),".vars")==0) {
799 fBrowsables.erase(itr);
800 }
801 }*/
802 } else {
803 auto v = std::make_shared<xRooNode>(vars());
804 fBrowsables.push_back(v);
805 if (auto l = GetListTree(nullptr)) {
806 l->AddItem(GetTreeItem(nullptr), v->GetName(), v.get());
807 }
808 }
809}
810
812{
813 for (auto &b : fBrowsables) {
814 if (strcmp(b->GetName(), ".vars") == 0)
815 return true;
816 }
817 return false;
818}
819
821{
822 if (strlen(GetName()) > 0 && GetName()[0] == '!')
823 return true;
824 if (strlen(GetName()) > 0 && GetName()[0] == '.' && !(TString(GetName()).BeginsWith(".Draw(\"")))
825 return true;
826 if (empty())
827 const_cast<xRooNode *>(this)->browse();
828 return !empty();
829}
830
831class Axis2 : public TAxis {
832
833public:
834 using TAxis::TAxis;
835 double GetBinWidth(Int_t bin) const override
836 {
837 if (auto v = var(); v)
838 return v->getBinWidth(bin - 1, GetName());
839 return 1;
840 }
841 double GetBinLowEdge(Int_t bin) const override
842 {
843 if (auto v = rvar(); v)
844 return (bin == v->getBinning(GetName()).numBins() + 1) ? v->getBinning(GetName()).binHigh(bin - 2)
845 : v->getBinning(GetName()).binLow(bin - 1);
846 return bin - 1;
847 }
848 double GetBinUpEdge(Int_t bin) const override
849 {
850 if (auto v = rvar(); v)
851 return (bin == 0) ? v->getBinning(GetName()).binLow(bin) : v->getBinning(GetName()).binHigh(bin - 1);
852 return bin;
853 }
854
855 const char *GetTitle() const override
856 {
857 return (binning() && strlen(binning()->GetTitle())) ? binning()->GetTitle() : GetParent()->GetTitle();
858 }
859 void SetTitle(const char *title) override
860 {
861 if (binning())
862 const_cast<RooAbsBinning *>(binning())->SetTitle(title);
863 else
864 dynamic_cast<TNamed *>(GetParent())->SetTitle(title);
865 }
866
867 void Set(Int_t nbins, const double *xbins) override
868 {
869 if (auto v = dynamic_cast<RooRealVar *>(rvar()))
870 v->setBinning(RooBinning(nbins, xbins), GetName());
871 TAxis::Set(nbins, xbins);
872 }
873 void Set(Int_t nbins, const float *xbins) override
874 {
875 std::vector<double> bins(nbins + 1);
876 for (int i = 0; i <= nbins; i++)
877 bins.at(i) = xbins[i];
878 return Set(nbins, &bins[0]);
879 }
880 void Set(Int_t nbins, double xmin, double xmax) override
881 {
882 if (auto v = dynamic_cast<RooRealVar *>(rvar()))
883 v->setBinning(RooUniformBinning(xmin, xmax, nbins), GetName());
884 TAxis::Set(nbins, xmin, xmax);
885 }
886
887 const RooAbsBinning *binning() const { return var()->getBinningPtr(GetName()); }
888
889 Int_t FindFixBin(const char *label) const override { return TAxis::FindFixBin(label); }
890 Int_t FindFixBin(double x) const override { return (binning()) ? (binning()->binNumber(x) + 1) : x; }
891
892private:
893 RooAbsLValue *var() const { return dynamic_cast<RooAbsLValue *>(GetParent()); }
894 RooAbsRealLValue *rvar() const { return dynamic_cast<RooAbsRealLValue *>(GetParent()); }
895};
896
897std::shared_ptr<TObject> xRooNode::getObject(const std::string &name, const std::string &type) const
898{
899 // if (fParent) return fParent->getObject(name);
900
901 if (auto _owned = find(".memory"); _owned) {
902 for (auto &o : *_owned) {
903 if (name == o->GetName()) {
904 if (type.empty() || o->get()->InheritsFrom(type.c_str()))
905 return o->fComp;
906 }
907 }
908 }
909
910 // see if have a provider
911 auto _provider = fProvider;
912 auto _parent = fParent;
913 while (!_provider && _parent) {
914 _provider = _parent->fProvider;
915 _parent = _parent->fParent;
916 }
917 if (_provider)
918 return _provider->getObject(name, type);
919
920 if (ws()) {
921 std::shared_ptr<TObject> out;
922 if (auto arg = ws()->arg(name.c_str()); arg) {
923 auto _tmp = std::shared_ptr<TObject>(arg, [](TObject *) {});
924 if (!type.empty() && arg->InheritsFrom(type.c_str()))
925 return _tmp;
926 if (!out)
927 out = _tmp;
928 }
929 if (auto arg = ws()->data(name.c_str()); arg) {
930 auto _tmp = std::shared_ptr<TObject>(arg, [](TObject *) {});
931 if (!type.empty() && arg->InheritsFrom(type.c_str()))
932 return _tmp;
933 if (!out)
934 out = _tmp;
935 }
936 if (auto arg = ws()->genobj(name.c_str()); arg) {
937 auto _tmp = std::shared_ptr<TObject>(arg, [](TObject *) {});
938 if (!type.empty() && arg->InheritsFrom(type.c_str()))
939 return _tmp;
940 if (!out)
941 out = _tmp;
942 }
943 if (auto arg = ws()->embeddedData(name.c_str()); arg) {
944 auto _tmp = std::shared_ptr<TObject>(arg, [](TObject *) {});
945 if (!type.empty() && arg->InheritsFrom(type.c_str()))
946 return _tmp;
947 if (!out)
948 out = _tmp;
949 }
950 if (auto arg = GETWSSNAPSHOTS(ws()).find(name.c_str()); arg) {
951 auto _tmp = std::shared_ptr<TObject>(arg, [](TObject *) {});
952 if (!type.empty() && arg->InheritsFrom(type.c_str()))
953 return _tmp;
954 if (!out)
955 out = _tmp;
956 }
957 return out;
958 }
959 return nullptr;
960}
961
963{
964 if (fXAxis) {
965 // check if num bins needs update or not
966 if (auto cat = dynamic_cast<RooAbsCategory *>(fXAxis->GetParent());
967 cat && cat->numTypes() != fXAxis->GetNbins()) {
968 fXAxis.reset();
969 } else {
970 return fXAxis.get();
971 }
972 }
973 RooAbsLValue *x = nullptr;
974 if (auto a = get<RooAbsArg>(); a && a->isFundamental())
975 x = dynamic_cast<RooAbsLValue *>(a); // self-axis
976
977 auto _parentX = (!x && fParent && !fParent->get<RooSimultaneous>()) ? fParent->GetXaxis() : nullptr;
978
979 auto o = get<RooAbsReal>();
980 if (!o)
981 return _parentX;
982
983 if (auto xName = o->getStringAttribute("xvar"); xName) {
984 x = dynamic_cast<RooAbsLValue *>(getObject(xName).get());
985 }
986
987 // if xvar has become set equal to an arg and this is a pdf, we will allow a do-over
988 if (!x) {
989 // need to choose from dependent fundamentals, in following order:
990 // parentX (if not a glob), robs, globs, vars, args
991
992 if (_parentX && !dynamic_cast<RooAbsArg *>(_parentX->GetParent())->getAttribute("global") &&
993 (o->dependsOn(*dynamic_cast<RooAbsArg *>(_parentX->GetParent())) || vars().size() == 0)) {
994 x = dynamic_cast<RooAbsLValue *>(_parentX->GetParent());
995 } else if (auto _obs = obs(); !_obs.empty()) {
996 for (auto &v : _obs) {
997 if (!v->get<RooAbsArg>()->getAttribute("global")) {
998 x = v->get<RooAbsLValue>();
999 if (x)
1000 break;
1001 } else if (!x) {
1002 x = v->get<RooAbsLValue>();
1003 }
1004 }
1005 } else if (auto _pars = pars(); !_pars.empty()) {
1006 for (auto &v : _pars) {
1007 if (!v->get<RooAbsArg>()->getAttribute("Constant")) {
1008 x = v->get<RooAbsLValue>();
1009 if (x)
1010 break;
1011 } else if (!x) {
1012 x = v->get<RooAbsLValue>();
1013 }
1014 }
1015 }
1016
1017 if (!x) {
1018 return nullptr;
1019 }
1020 }
1021
1022 if (o != dynamic_cast<TObject *>(x)) {
1023 o->setStringAttribute("xvar", dynamic_cast<TObject *>(x)->GetName());
1024 }
1025
1026 // decide binning to use
1027 TString binningName = o->getStringAttribute("binning");
1028 auto _bnames = x->getBinningNames();
1029 bool hasBinning = false;
1030 for (auto &b : _bnames) {
1031 if (b == binningName) {
1032 hasBinning = true;
1033 break;
1034 }
1035 }
1036 if (!hasBinning) {
1037 // doesn't have binning, so clear binning attribute
1038 // this can happen after Combine of models because binning don't get combined yet (should fix this)
1039 Warning("GetXaxis", "Binning %s not defined on %s - clearing", binningName.Data(),
1040 dynamic_cast<TObject *>(x)->GetName());
1041 o->setStringAttribute("binning", nullptr);
1042 binningName = "";
1043 }
1044
1045 if (binningName == "" && o != dynamic_cast<TObject *>(x)) {
1046 // has var has a binning matching this nodes name then use that
1047 auto __bnames = x->getBinningNames();
1048 for (auto &b : __bnames) {
1049 if (b == GetName())
1050 binningName = GetName();
1051 if (b == o->GetName()) {
1052 binningName = o->GetName();
1053 break;
1054 } // best match
1055 }
1056 if (binningName == "") {
1057 // if we are binned in this var then will define that as a binning
1058 if (/*o->isBinnedDistribution(*dynamic_cast<RooAbsArg *>(x))*/
1059 auto bins = _or_func(
1060 /*o->plotSamplingHint(*dynamic_cast<RooAbsRealLValue
1061 *>(x),-std::numeric_limits<double>::infinity(),std::numeric_limits<double>::infinity())*/
1062 (std::list<double> *)(nullptr),
1063 o->binBoundaries(*dynamic_cast<RooAbsRealLValue *>(x), -std::numeric_limits<double>::infinity(),
1064 std::numeric_limits<double>::infinity()));
1065 bins) {
1066 std::vector<double> _bins;
1067 for (auto &b : *bins) {
1068 if (_bins.empty() || std::abs(_bins.back() - b) > 1e-5 * _bins.back())
1069 _bins.push_back(b);
1070 }
1071 fXAxis = std::make_shared<Axis2>(_bins.size() - 1, &_bins[0]);
1072 // add this binning to the var to avoid recalling ...
1073 if (auto _v = dynamic_cast<RooRealVar *>(x); _v) {
1074 _v->setBinning(RooBinning(_bins.size() - 1, &_bins[0], o->GetName()), o->GetName());
1075 _v->getBinning(o->GetName())
1076 .SetTitle(""); // indicates to use the current var title when building histograms etc
1077 //_v->getBinning(o->GetName()).SetTitle(strlen(dynamic_cast<TObject*>(x)->GetTitle()) ?
1078 // dynamic_cast<TObject*>(x)->GetTitle() : dynamic_cast<TObject*>(x)->GetName());
1079 }
1080 binningName = o->GetName();
1081 delete bins;
1082 } else if (_parentX) {
1083 // use parent axis binning if defined, otherwise we will default
1084 binningName = _parentX->GetName();
1085 }
1086 }
1087 }
1088
1089 if (!fXAxis) {
1090 if (auto r = dynamic_cast<RooAbsRealLValue *>(x); r) {
1091 if (r->getBinning(binningName).isUniform()) {
1092 fXAxis = std::make_shared<Axis2>(x->numBins(binningName), r->getMin(binningName), r->getMax(binningName));
1093 } else {
1094 fXAxis = std::make_shared<Axis2>(x->numBins(binningName), r->getBinning(binningName).array());
1095 }
1096 } else if (dynamic_cast<RooAbsCategory *>(x)) {
1097 std::vector<double> bins = {};
1098 for (int i = 0; i <= x->numBins(binningName); i++)
1099 bins.push_back(i);
1100 fXAxis = std::make_shared<Axis2>(x->numBins(binningName), &bins[0]);
1101 // TODO have to load current state of bin labels if was a category (sadly not a virtual method)
1102 for (int i = 0; i < x->numBins(binningName); i++) {
1103 fXAxis->SetBinLabel(i + 1, dynamic_cast<RooAbsCategory *>(x)->lookupName(i).c_str());
1104 }
1105 }
1106 }
1107
1108 fXAxis->SetName(binningName);
1109 fXAxis->SetParent(dynamic_cast<TObject *>(x));
1110 return fXAxis.get();
1111}
1112
1113const char *xRooNode::GetIconName() const
1114{
1115 if (auto o = get(); o) {
1116 if (o->InheritsFrom("RooWorkspace"))
1117 return "TFile";
1118 if (o->InheritsFrom("RooAbsData"))
1119 return "TProfile";
1120 if (o->InheritsFrom("RooSimultaneous"))
1121 return "TH3D";
1122
1123 if (o->InheritsFrom("RooProdPdf"))
1124 return "a.C"; // or nullptr for folder
1125 if (o->InheritsFrom("RooRealSumPdf") || o->InheritsFrom("RooAddPdf"))
1126 return "TH2D";
1127 // if(o->InheritsFrom("RooProduct")) return "TH1D";
1128 if (o->InheritsFrom("RooFitResult")) {
1129 if (!gClient->GetMimeTypeList()->GetIcon("xRooFitRooFitResult", true)) {
1130 gClient->GetMimeTypeList()->AddType("xRooFitRooFitResult", "xRooFitRooFitResult", "package.xpm",
1131 "package.xpm", "->Browse()");
1132 }
1133 return "xRooFitRooFitResult";
1134 }
1135 if (o->InheritsFrom("RooRealVar") || o->InheritsFrom("RooCategory")) {
1136 if (get<RooAbsArg>()->getAttribute("obs")) {
1137 if (!gClient->GetMimeTypeList()->GetIcon("xRooFitObs", true)) {
1138 gClient->GetMimeTypeList()->AddType("xRooFitObs", "xRooFitObs", "x_pic.xpm", "x_pic.xpm", "->Browse()");
1139 }
1140 if (!gClient->GetMimeTypeList()->GetIcon("xRooFitGlobs", true)) {
1141 gClient->GetMimeTypeList()->AddType("xRooFitGlobs", "xRooFitGlobs", "z_pic.xpm", "z_pic.xpm",
1142 "->Browse()");
1143 }
1144 return (get<RooAbsArg>()->getAttribute("global") ? "xRooFitGlobs" : "xRooFitObs");
1145 }
1146 return "TLeaf";
1147 }
1148 if (o->InheritsFrom("TStyle")) {
1149 if (!gClient->GetMimeTypeList()->GetIcon("xRooFitTStyle", true)) {
1150 gClient->GetMimeTypeList()->AddType("xRooFitTStyle", "xRooFitTStyle", "bld_colorselect.xpm",
1151 "bld_colorselect.xpm", "->Browse()");
1152 }
1153 return "xRooFitTStyle";
1154 }
1155 if (o->InheritsFrom("RooConstVar")) {
1156 /*if (!gClient->GetMimeTypeList()->GetIcon("xRooFitRooConstVar",true)) {
1157 gClient->GetMimeTypeList()->AddType("xRooFitRooConstVar", "xRooFitRooConstVar", "stop_t.xpm", "stop_t.xpm",
1158 "->Browse()");
1159 }
1160 return "xRooFitRooConstVar";*/
1161 return "TMethodBrowsable-leaf";
1162 }
1163 if (o->InheritsFrom("RooStats::HypoTestInverterResult")) {
1164 if (!gClient->GetMimeTypeList()->GetIcon("xRooFitScanStyle", true)) {
1165 gClient->GetMimeTypeList()->AddType("xRooFitScanStyle", "xRooFitScanStyle", "f2_s.xpm", "f2_s.xpm",
1166 "->Browse()");
1167 }
1168 return "xRooFitScanStyle";
1169 }
1170 if (o->InheritsFrom("RooStats::HypoTestResult")) {
1171 if (!gClient->GetMimeTypeList()->GetIcon("xRooFitTestStyle", true)) {
1172 gClient->GetMimeTypeList()->AddType("xRooFitTestStyle", "xRooFitTestStyle", "diamond.xpm", "diamond.xpm",
1173 "->Browse()");
1174 }
1175 return "xRooFitTestStyle";
1176 }
1177 if (o->InheritsFrom("RooStats::HistFactory::FlexibleInterpVar"))
1178 return "TBranchElement-folder";
1179 if (o->InheritsFrom("RooAbsPdf")) {
1180 if (!gClient->GetMimeTypeList()->GetIcon("xRooFitPDFStyle", true)) {
1181 gClient->GetMimeTypeList()->AddType("xRooFitPDFStyle", "xRooFitPDFStyle", "pdf.xpm", "pdf.xpm",
1182 "->Browse()");
1183 }
1184 return "xRooFitPDFStyle";
1185 }
1186 if (auto a = dynamic_cast<RooAbsReal *>(o); a) {
1187 if (auto _ax = GetXaxis();
1188 _ax && (a->isBinnedDistribution(*dynamic_cast<RooAbsArg *>(_ax->GetParent())) ||
1189 (dynamic_cast<RooAbsRealLValue *>(_ax->GetParent()) &&
1190 std::unique_ptr<std::list<double>>(a->binBoundaries(
1191 *dynamic_cast<RooAbsRealLValue *>(_ax->GetParent()), -std::numeric_limits<double>::infinity(),
1192 std::numeric_limits<double>::infinity()))))) {
1193 return "TH1D";
1194 }
1195 return "TF1";
1196 }
1197 return o->ClassName();
1198 }
1199 if (!IsFolder()) {
1200 return "Unknown";
1201 }
1202 return nullptr;
1203}
1204
1205const char *xRooNode::GetNodeType() const
1206{
1207 if (auto o = get(); o && fParent && (fParent->get<RooProduct>() || fParent->get<RooRealSumPdf>())) {
1208 if (o->InheritsFrom("RooStats::HistFactory::FlexibleInterpVar"))
1209 return "Overall";
1210 if (o->InheritsFrom("PiecewiseInterpolation"))
1211 return (dynamic_cast<RooAbsArg *>(o)->getAttribute("density")) ? "DensityHisto" : "Histo";
1212 if (o->InheritsFrom("RooHistFunc"))
1213 return (dynamic_cast<RooAbsArg *>(o)->getAttribute("density")) ? "ConstDensityHisto" : "ConstHisto";
1214 if (o->InheritsFrom("RooBinWidthFunction"))
1215 return "Density";
1216 if (o->InheritsFrom("ParamHistFunc"))
1217 return "Shape";
1218 if (o->InheritsFrom("RooRealVar"))
1219 return "Norm";
1220 if (o->InheritsFrom("RooConstVar"))
1221 return "Const";
1222 }
1223 return "";
1224}
1225
1226xRooNode xRooNode::coords(bool setVals) const
1227{
1228 xRooNode out(".coords", nullptr, *this);
1229 // go up through parents looking for slice obs
1230 auto _p = std::shared_ptr<xRooNode>(const_cast<xRooNode *>(this), [](xRooNode *) {});
1231 while (_p) {
1232 TString pName(_p->GetName());
1233 if (auto pos = pName.Index('='); pos != -1) {
1234 if (pos > 0 && pName(pos - 1) == '<') {
1235 // should be a range on a real lvalue, of form low<=name<high
1236 double low = TString(pName(0, pos - 1)).Atof();
1237 pName = pName(pos + 1, pName.Length());
1238 double high = TString(pName(pName.Index('<') + 1, pName.Length())).Atof();
1239 pName = pName(0, pName.Index('<'));
1240 if (auto _obs = _p->getObject<RooAbsRealLValue>(pName.Data()); _obs) {
1241 if (setVals) {
1242 _obs->setVal((high + low) / 2.);
1243 static_cast<RooRealVar *>(_obs.get())->setRange("coordRange", low, high);
1244 _obs->setStringAttribute(
1245 "coordRange", "coordRange"); // will need if we allow multi disconnected regions, need comma list
1246 }
1247 out.emplace_back(std::make_shared<xRooNode>(_obs->GetName(), _obs, _p));
1248 } else {
1249 throw std::runtime_error(TString::Format("Unknown observable: %s", pName.Data()));
1250 }
1251
1252 } else if (auto _obs = _p->getObject<RooAbsArg>(pName(0, pos)); _obs) {
1253 if (setVals) {
1254 if (auto _cat = dynamic_cast<RooAbsCategoryLValue *>(_obs.get()); _cat) {
1255 _cat->setLabel(pName(pos + 1, pName.Length()));
1256 } else if (auto _var = dynamic_cast<RooAbsRealLValue *>(_obs.get()); _var) {
1257 _var->setVal(TString(pName(pos + 1, pName.Length())).Atof());
1258 }
1259 }
1260 out.emplace_back(std::make_shared<xRooNode>(_obs->GetName(), _obs, _p));
1261 } else {
1262 throw std::runtime_error("Unknown observable, could not find");
1263 }
1264 }
1265 _p = _p->fParent;
1266 }
1267 return out;
1268}
1269
1270void xRooNode::_Add_(const char *name, const char *opt)
1271{
1272 try {
1273 Add(name, opt);
1274 } catch (const std::exception &e) {
1275 new TGMsgBox(gClient->GetRoot(), gClient->GetRoot(), "Exception", e.what(),
1276 kMBIconExclamation); // deletes self on dismiss?
1277 }
1278}
1279void xRooNode::_Vary_(const char *what)
1280{
1281 try {
1282 Vary(what);
1283 } catch (const std::exception &e) {
1284 new TGMsgBox(gClient->GetRoot(), gClient->GetRoot(), "Exception", e.what(),
1285 kMBIconExclamation); // deletes self on dismiss?
1286 }
1287}
1288
1290{
1291
1292 if (strcmp(GetName(), ".poi") == 0) {
1293 // demote a parameter from being a poi
1294 auto toRemove =
1295 (child.get<RooAbsArg>() || !find(child.GetName())) ? child : xRooNode(find(child.GetName())->fComp);
1296 if (toRemove) {
1297 if (!toRemove.get<RooAbsArg>()->getAttribute("poi")) {
1298 throw std::runtime_error(TString::Format("%s is not a poi", toRemove.GetName()));
1299 }
1300 toRemove.get<RooAbsArg>()->setAttribute("poi", false);
1301 return toRemove;
1302 }
1303 } else if (strcmp(GetName(), ".factors") == 0 || strcmp(GetName(), ".constraints") == 0 ||
1304 strcmp(GetName(), ".components") == 0) {
1305 auto toRemove =
1306 (child.get<RooAbsArg>() || !find(child.GetName())) ? child : xRooNode(find(child.GetName())->fComp);
1307 if (auto p = fParent->get<RooProdPdf>(); p) {
1308 auto pdf = toRemove.get<RooAbsArg>();
1309 if (!pdf)
1310 pdf = p->pdfList().find(child.GetName());
1311 if (!pdf)
1312 throw std::runtime_error(TString::Format("Cannot find %s in %s", child.GetName(), fParent->GetName()));
1313 auto i = p->pdfList().index(*pdf);
1314 if (i >= 0) {
1315#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
1316 const_cast<RooArgList &>(p->pdfList()).remove(*pdf);
1317#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 26, 00)
1318 p->_pdfNSetList.erase(p->_pdfNSetList.begin() + i);
1319#else
1320 auto nset = p->_pdfNSetList.At(i);
1321 p->_pdfNSetList.Remove(nset);
1322 delete nset; // I don't think the RooLinkedList owned it so must delete ourself
1323#endif
1324 if (p->_extendedIndex == i)
1325 p->_extendedIndex = -1;
1326 else if (p->_extendedIndex > i)
1327 p->_extendedIndex--;
1328#else
1329 p->removePdfs(RooArgSet(*pdf));
1330#endif
1331 sterilize();
1332 return xRooNode(*pdf);
1333 } else {
1334 throw std::runtime_error(TString::Format("Cannot find %s in %s", child.GetName(), fParent->GetName()));
1335 }
1336 } else if (auto p2 = fParent->get<RooProduct>(); p2) {
1337 auto arg = toRemove.get<RooAbsArg>();
1338 if (!arg)
1339 arg = p2->components().find(child.GetName());
1340 if (!arg)
1341 throw std::runtime_error(TString::Format("Cannot find %s in %s", child.GetName(), fParent->GetName()));
1342 // remove server ... doesn't seem to trigger removal from proxy
1343#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
1344 p2->_compRSet.remove(*arg);
1345#else
1346 const_cast<RooArgList &>(p2->realComponents()).remove(*arg);
1347#endif
1348 p2->removeServer(*arg, true);
1349 sterilize();
1350 return xRooNode(*arg);
1351 } else if (fParent->get<RooSimultaneous>()) {
1352 // remove from all channels
1353 bool removed = false;
1354 for (auto &c : fParent->bins()) {
1355 try {
1356 c->constraints().Remove(toRemove);
1357 removed = true;
1358 } catch (std::runtime_error &) { /* wasn't a constraint in channel */
1359 }
1360 }
1361 sterilize();
1362 if (!removed)
1363 throw std::runtime_error(TString::Format("Cannot find %s in %s", child.GetName(), fParent->GetName()));
1364 return toRemove;
1365 } else if (auto p4 = fParent->get<RooRealSumPdf>(); p4) {
1366 auto arg = toRemove.get<RooAbsArg>();
1367 if (!arg)
1368 arg = p4->funcList().find(child.GetName());
1369 if (!arg)
1370 throw std::runtime_error(TString::Format("Cannot find %s in %s", child.GetName(), fParent->GetName()));
1371 // remove, including coef removal ....
1372 auto idx = p4->funcList().index(arg);
1373
1374 if (idx != -1) {
1375
1376 const_cast<RooArgList &>(p4->funcList()).remove(*arg);
1377 p4->removeServer(*arg, true);
1378 // have to be careful removing coef because if shared will end up removing them all!!
1379 std::vector<RooAbsArg *> _coefs;
1380 for (size_t ii = 0; ii < const_cast<RooArgList &>(p4->coefList()).size(); ii++) {
1381 if (ii != size_t(idx))
1382 _coefs.push_back(const_cast<RooArgList &>(p4->coefList()).at(ii));
1383 }
1384 const_cast<RooArgList &>(p4->coefList()).removeAll();
1385 for (auto &a : _coefs)
1386 const_cast<RooArgList &>(p4->coefList()).add(*a);
1387
1388 sterilize();
1389 } else {
1390 throw std::runtime_error(TString::Format("Cannot find %s in %s", child.GetName(), fParent->GetName()));
1391 }
1392 return xRooNode(*arg);
1393 } // todo: add support for RooAddPdf and RooAddition
1394 }
1395
1396 if (auto w = get<RooWorkspace>(); w) {
1397 xRooNode out(child.GetName());
1398 auto arg = w->components().find(child.GetName());
1399 if (!arg)
1400 arg = operator[](child.GetName())->get<RooAbsArg>();
1401 if (!arg) {
1402 throw std::runtime_error(TString::Format("Cannot find %s in workspace %s", child.GetName(), GetName()));
1403 }
1404 // check has no clients ... if so, cannot delete
1405 if (arg->hasClients()) {
1406 throw std::runtime_error(
1407 TString::Format("Cannot remove %s from workspace %s, because it has dependencies - first remove from those",
1408 child.GetName(), GetName()));
1409 }
1410 const_cast<RooArgSet &>(w->components()).remove(*arg); // deletes arg
1411 Info("Remove", "Deleted %s from workspace %s", out.GetName(), GetName());
1412 return out;
1413 } else if (get<RooProduct>() || get<RooProdPdf>()) {
1414 return factors().Remove(child);
1415 } else if (get<RooRealSumPdf>()) {
1416 return components().Remove(child);
1417 }
1418
1419 throw std::runtime_error("Removal not implemented for this type of object");
1420}
1421
1423{
1424
1425 class AutoUpdater {
1426 public:
1427 AutoUpdater(xRooNode &_n) : n(_n) {}
1428 ~AutoUpdater() { n.browse(); }
1429 xRooNode &n;
1430 };
1431 AutoUpdater xxx(*this);
1432
1433 TString sOpt(opt);
1434 bool considerType(sOpt == "+");
1435
1436 if (strlen(GetName()) > 0 && GetName()[0] == '!' && fParent) {
1437 // folder .. pass onto parent and add folder to child folder list
1438 const_cast<xRooNode &>(child).fFolder += GetName();
1439 return fParent->Add(child, opt);
1440 }
1441 // this is how to get the first real parent ... may be useful at some point?
1442 /*auto realParent = fParent;
1443 while(!realParent->get()) {
1444 realParent = realParent->fParent;
1445 if (!realParent) throw std::runtime_error("No parentage");
1446 }*/
1447
1448 // adding to a collection node will incorporate the child into the parent of the collection
1449 // in the appropriate way
1450 if (strcmp(GetName(), ".factors") == 0) {
1451 // multiply the parent
1452 return fParent->Multiply(child, opt);
1453 } else if (strcmp(GetName(), ".components") == 0) {
1454 // add to the parent
1455 return fParent->Add(child, opt);
1456 } else if (strcmp(GetName(), ".variations") == 0) {
1457 // vary the parent
1458 return fParent->Vary(child);
1459 } else if (strcmp(GetName(), ".constraints") == 0) {
1460 // constrain the parent
1461 return fParent->Constrain(child);
1462 } else if (strcmp(GetName(), ".bins") == 0 && fParent->get<RooSimultaneous>()) {
1463 // adding a channel (should adding a 'bin' be an 'Extend' operation?)
1464 return fParent->Vary(child);
1465 } else if ((strcmp(GetName(), ".globs") == 0)) {
1466 if (child.get<RooAbsArg>() || (!child.fComp && getObject<RooAbsArg>(child.GetName()))) {
1467 auto out = (child.get<RooAbsArg>()) ? child.get<RooAbsArg>() : getObject<RooAbsArg>(child.GetName()).get();
1468 out->setAttribute("obs");
1469 out->setAttribute("global");
1470 return xRooNode(*out, *this);
1471 }
1472 throw std::runtime_error("Failed to add global observable");
1473 } else if ((strcmp(GetName(), ".poi") == 0)) {
1474 if (child.get<RooAbsLValue>() || (!child.fComp && getObject<RooAbsLValue>(child.GetName()))) {
1475 auto out = (child.get<RooAbsArg>()) ? child.get<RooAbsArg>() : getObject<RooAbsArg>(child.GetName()).get();
1476 out->setAttribute("poi");
1477 return xRooNode(*out, *this);
1478 }
1479 throw std::runtime_error("Failed to add parameter of interest");
1480 } else if ((strcmp(GetName(), ".pars") == 0 || strcmp(GetName(), ".vars") == 0) && fParent->get<RooWorkspace>()) {
1481 // adding a parameter, interpret as factory string unless no "[" then create RooRealVar
1482 TString fac(child.GetName());
1483 if (!fac.Contains("["))
1484 fac += "[1]";
1485 return xRooNode(*fParent->get<RooWorkspace>()->factory(fac), fParent);
1486 } else if (strcmp(GetName(), ".datasets()") == 0) {
1487 // create a dataset - only allowed for pdfs or workspaces
1488 if (auto _ws = ws(); _ws && fParent) {
1489 sOpt.ToLower();
1490 if (!fParent->get<RooAbsPdf>() && (!fParent->get<RooWorkspace>() || sOpt == "asimov")) {
1491 throw std::runtime_error(
1492 "Datasets can only be created for pdfs or workspaces (except if generated dataset, then must be pdf)");
1493 }
1494
1495 if (sOpt == "asimov" || sOpt == "toy") {
1496 // generate expected dataset - note that globs will be frozen at this time
1497 auto _fr = std::dynamic_pointer_cast<const RooFitResult>(fParent->fitResult().fComp);
1498 if (strlen(_fr->GetName()) == 0)
1499 std::const_pointer_cast<RooFitResult>(_fr)->SetName(TUUID().AsString());
1500 auto asi = xRooFit::generateFrom(*fParent->get<RooAbsPdf>(), *_fr, sOpt == "asimov");
1501 if (strlen(child.GetName()))
1502 asi.first->SetName(child.GetName());
1503 if (asi.first) {
1504 _ws->import(*asi.first);
1505 }
1506 if (_fr->numStatusHistory() == 0) {
1507 if (!GETWSSNAPSHOTS(_ws).find(_fr->GetName())) {
1508 const_cast<RooLinkedList &>(GETWSSNAPSHOTS(_ws)).Add(_fr->Clone());
1509 }
1510 } else if (!_ws->obj(_fr->GetName())) {
1511 _ws->import(const_cast<RooFitResult &>(*_fr));
1512 } // save fr to workspace, for later retrieval
1513 if (asi.second) {
1514#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 26, 00)
1515 _ws->saveSnapshot(asi.first->GetName(), *asi.second,
1516 true); // TODO: Migrate to using globs inside datasets
1517#else
1518 RooArgSet _tmp;
1519 _tmp.add(*asi.second);
1520 _ws->saveSnapshot(asi.first->GetName(), _tmp, true);
1521#endif
1522 }
1523 return xRooNode(*_ws->data(asi.first->GetName()), fParent);
1524 }
1525
1526 auto parentObs = fParent->obs(); // may own globs so keep alive
1527 auto _obs = parentObs.argList();
1528 // put globs in a snapshot
1529 std::unique_ptr<RooAbsCollection> _globs(_obs.selectByAttrib("global", true));
1530 // RooArgSet _tmp; _tmp.add(*_globs);_ws->saveSnapshot(child.GetName(),_tmp);
1531 _obs.remove(*_globs);
1532
1533 // include any coords
1534 _obs.add(coords(false).argList(), true);
1535 // include axis var too, provided it's an observable
1536 if (auto ax = GetXaxis(); ax && dynamic_cast<RooAbsArg *>(ax->GetParent())->getAttribute("obs")) {
1537 _obs.add(*dynamic_cast<RooAbsArg *>(ax->GetParent()));
1538 }
1539 // check if ws already has a dataset with this name, if it does we may need to extend columns
1540 if (auto _d = _ws->data(child.GetName()); _d) {
1541 // add any missing obs
1542 RooArgSet l(_obs);
1543 l.remove(*_d->get(), true, true);
1544 if (!l.empty()) {
1545 auto _dd = dynamic_cast<RooDataSet *>(_d);
1546 if (!_dd)
1547 throw std::runtime_error("Cannot extend dataset with new columns");
1548 for (auto &x : l) {
1549 _dd->addColumn(*x);
1550 }
1551 }
1552 } else {
1553 RooRealVar w("weightVar", "weightVar", 1);
1554 _obs.add(w);
1555 RooDataSet d(child.GetName(), child.GetTitle(), _obs, "weightVar");
1556 _ws->import(d);
1557 // seems have to set bits after importing, not before
1558 if (auto __d = _ws->data(child.GetName()))
1559 __d->SetBit(1 << 20, _ws->allData().size() == 1); // sets as selected if is only ds
1560 }
1561 /*if(!_ws->data(child.GetName())) {
1562 RooRealVar w("weightVar", "weightVar", 1);
1563 RooArgSet _obs; _obs.add(w);
1564 RooDataSet d(child.GetName(), child.GetTitle(), _obs, "weightVar");
1565 _ws->import(d);
1566 }*/
1567 auto out = std::shared_ptr<TObject>(_ws->data(child.GetName()), [](TObject *) {});
1568
1569 if (out) {
1570 xRooNode o(out, fParent);
1571 if (child.get<TH1>())
1572 o = *child.get();
1573 return o;
1574 }
1575 }
1576 throw std::runtime_error("Cannot create dataset");
1577 }
1578
1579 if (!get()) {
1580 if (!fParent)
1581 throw std::runtime_error("Cannot add to null object with no parentage");
1582
1583 auto _ref = emplace_back(std::shared_ptr<xRooNode>(&const_cast<xRooNode &>(child), [](TObject *) {}));
1584 try {
1585 fComp = fParent->Add(*this, "+").fComp;
1586 } catch (...) {
1587 resize(size() - 1);
1588 std::rethrow_exception(std::current_exception());
1589 }
1590 resize(size() - 1); // remove the temporarily added node
1591
1592 if (!fComp) {
1593 throw std::runtime_error("No object");
1594 }
1595 }
1596
1597 if (auto p = get<RooAbsData>(); p) {
1598 if (auto bb = getBrowsable(".sourceds"))
1599 bb->Add(child, opt);
1600 if (auto _data = child.get<RooDataSet>()) {
1601 auto ds = dynamic_cast<RooDataSet *>(p);
1602 if (!ds) {
1603 throw std::runtime_error("Can only add datasets to a dataset");
1604 }
1605
1606 // append any missing globs, and check any existing globs have matching values
1607 RooArgList globsToAdd;
1608 auto _globs = globs();
1609 for (auto &glob : child.globs()) {
1610 if (auto g = _globs.find(glob->GetName()); !g) {
1611 globsToAdd.addClone(*glob->get<RooAbsArg>());
1612 } else if (g->GetContent() != glob->GetContent()) {
1613 Warning("Add", "Global observable %s=%g in dataset mismatches child value %g ... ignoring child",
1614 g->GetName(), g->GetContent(), glob->GetContent());
1615 }
1616 }
1617 // add any existing globs to list then set the list
1618 if (auto _dglobs = p->getGlobalObservables()) {
1619 globsToAdd.addClone(*_dglobs);
1620 } else {
1621 for (auto g : _globs)
1622 globsToAdd.addClone(*g->get<RooAbsArg>());
1623 }
1624 p->setGlobalObservables(globsToAdd);
1625
1626 // append any missing observables to our dataset, then append the dataset
1627
1628 for (auto col : *_data->get()) {
1629 if (!p->get()->contains(*col)) {
1630 ds->addColumn(*col);
1631 }
1632 }
1633 ds->append(*_data);
1634 return *this;
1635 }
1636 auto _h = child.get<TH1>();
1637 if (!_h) {
1638 throw std::runtime_error("Can only add histogram or dataset to data");
1639 }
1640 auto _pdf = parentPdf();
1641 if (!_pdf)
1642 throw std::runtime_error("Could not find pdf");
1643 auto _ax = _pdf->GetXaxis();
1644 if (!_ax) {
1645 throw std::runtime_error("Cannot determine binning to add data");
1646 }
1647
1648 RooArgSet obs;
1649 obs.add(*dynamic_cast<RooAbsArg *>(_ax->GetParent()));
1650 obs.add(coords().argList()); // will also move obs to coords
1651
1652 // add any missing obs
1653 RooArgSet l(obs);
1654 l.remove(*p->get(), true, true);
1655 if (!l.empty()) {
1656 auto _d = dynamic_cast<RooDataSet *>(p);
1657 if (!_d)
1658 throw std::runtime_error("Cannot extend dataset with new columns");
1659 for (auto &x : l) {
1660 _d->addColumn(*x);
1661 }
1662 }
1663
1664 // before adding, ensure range is good to cover
1665 for (auto &o : obs) {
1666 if (auto v = dynamic_cast<RooRealVar *>(o); v) {
1667 if (auto dv = dynamic_cast<RooRealVar *>(p->get()->find(v->GetName())); dv) {
1668 if (v->getMin() < dv->getMin())
1669 dv->setMin(v->getMin());
1670 if (v->getMax() > dv->getMax())
1671 dv->setMax(v->getMax());
1672 }
1673 } else if (auto c = dynamic_cast<RooCategory *>(o); c) {
1674 if (auto dc = dynamic_cast<RooCategory *>(p->get()->find(c->GetName())); dc) {
1675 if (!dc->hasLabel(c->getCurrentLabel())) {
1676 dc->defineType(c->getCurrentLabel(), c->getCurrentIndex());
1677 }
1678 }
1679 }
1680 }
1681
1682 for (int i = 1; i <= _h->GetNbinsX(); i++) {
1683 if (auto cat = dynamic_cast<RooAbsCategoryLValue *>(_ax->GetParent())) {
1684 if (!_h->GetXaxis()->GetBinLabel(i)) {
1685 throw std::runtime_error(
1686 TString::Format("Categorical observable %s requires bin labels", _ax->GetParent()->GetName()));
1687 } else if (!cat->hasLabel(_h->GetXaxis()->GetBinLabel(i))) {
1688 throw std::runtime_error(TString::Format("Categorical observable %s does not have label %s",
1689 _ax->GetParent()->GetName(), _h->GetXaxis()->GetBinLabel(i)));
1690 } else {
1691 cat->setLabel(_h->GetXaxis()->GetBinLabel(i));
1692 }
1693 } else {
1694 dynamic_cast<RooAbsRealLValue *>(_ax->GetParent())->setVal(_h->GetBinCenter(i));
1695 }
1696 p->add(obs, _h->GetBinContent(i));
1697 }
1698
1699 return *this;
1700 }
1701
1702 if (auto p = get<RooAddPdf>(); p) {
1703 if ((child.get<RooAbsPdf>() || (!child.fComp && getObject<RooAbsPdf>(child.GetName())))) {
1704 auto out = (child.fComp) ? acquire(child.fComp) : getObject<RooAbsArg>(child.GetName());
1705 // don't add a coef if in 'all-extended' mode and this pdf is extendable
1706 auto _pdf = std::dynamic_pointer_cast<RooAbsPdf>(out);
1707 if (!_pdf) {
1708 throw std::runtime_error("Something went wrong with pdf acquisition");
1709 }
1710
1711 if (auto _ax = GetXaxis(); _ax && dynamic_cast<RooAbsRealLValue *>(_ax->GetParent()) &&
1712 _pdf->dependsOn(*static_cast<RooAbsArg *>(_ax->GetParent()))) {
1713 auto _p = _pdf;
1714
1715 if (auto _boundaries = std::unique_ptr<std::list<double>>(_p->binBoundaries(
1716 *dynamic_cast<RooAbsRealLValue *>(_ax->GetParent()), -std::numeric_limits<double>::infinity(),
1717 std::numeric_limits<double>::infinity()));
1718 !_boundaries && _ax->GetNbins() > 0) {
1719#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 24, 00)
1720 Warning("Add", "Adding unbinned pdf %s to binned %s - will wrap with RooBinSamplingPdf(...)",
1721 _p->GetName(), GetName());
1722 _p = acquireNew<RooBinSamplingPdf>(TString::Format("%s_binned", _p->GetName()), _p->GetTitle(),
1723 *dynamic_cast<RooAbsRealLValue *>(_ax->GetParent()), *_p);
1724 _p->setStringAttribute("alias", std::dynamic_pointer_cast<RooAbsArg>(out)->getStringAttribute("alias"));
1725 if (!_p->getStringAttribute("alias"))
1726 _p->setStringAttribute("alias", out->GetName());
1727#else
1728 throw std::runtime_error(
1729 "unsupported addition of unbinned pdf to binned model - please upgrade to at least ROOT 6.24");
1730#endif
1731 _pdf = _p;
1732 }
1733 }
1734
1735 if (!(_pdf->canBeExtended() && p->coefList().empty())) {
1736 // if extended, use an extended binding as the coef
1737 // otherwise e.g. if adding a RooRealSumPdf the stacked histograms will be above the
1738 // actual pdf histogram because the pdf histogram is just normalized down
1739 if (_pdf->canBeExtended()) {
1740 // FIXME: ExtendedBinding needs the obs list passing to it ... should be fixed in RooFit
1741 // until then, this will return "1" and so the pdf's histograms wont be normalized properly in relation
1742 // to stacks of its comps
1743 const_cast<RooArgList &>(p->coefList())
1744 .add(*acquireNew<RooExtendedBinding>(TString::Format("%s_extBind", _pdf->GetName()),
1745 TString::Format("Expected Events of %s", _pdf->GetTitle()),
1746 *_pdf));
1747 } else {
1748 const_cast<RooArgList &>(p->coefList()).add(*acquire2<RooAbsArg, RooRealVar>("1", "1", 1));
1749 }
1750 }
1751 const_cast<RooArgList &>(p->pdfList()).add(*_pdf);
1752 sterilize();
1753 return xRooNode(*_pdf, *this);
1754 } else if ((child.get<TH1>() || child.get<RooAbsReal>() ||
1755 (!child.get() && getObject<RooAbsReal>(child.GetName()))) &&
1756 !child.get<RooAbsPdf>()) {
1757 RooRealSumPdf *_pdf = nullptr;
1758 bool tooMany(false);
1759 for (auto &pp : factors()) {
1760 if (auto _p = pp->get<RooRealSumPdf>(); _p) {
1761 if (_pdf) {
1762 _pdf = nullptr;
1763 tooMany = true;
1764 break;
1765 } // more than one!
1766 _pdf = _p;
1767 }
1768 }
1769 if (_pdf) {
1770 return xRooNode(*_pdf, *this).Add(child);
1771 } else if (!tooMany) {
1772 // create a RooRealSumPdf to hold the child
1773 auto _sumpdf = Add(*acquireNew<RooRealSumPdf>(TString::Format("%s_samples", p->GetName()),
1774 TString::Format("%s samples", GetTitle()), RooArgList(),
1775 RooArgList(), true));
1776 _sumpdf.get<RooAbsArg>()->setStringAttribute("alias", "samples");
1777 return _sumpdf.Add(child);
1778 }
1779 }
1780 }
1781
1782 if (auto p = get<RooRealSumPdf>(); p) {
1783 std::shared_ptr<TObject> out;
1784 auto cc = child.fComp;
1785 bool isConverted = (cc != child.convertForAcquisition(*this, sOpt));
1786 if (child.get<RooAbsReal>())
1787 out = acquire(child.fComp);
1788 if (!child.fComp && getObject<RooAbsReal>(child.GetName())) {
1789 Info("Add", "Adding existing function %s to %s", child.GetName(), p->GetName());
1790 out = getObject<RooAbsReal>(child.GetName());
1791 }
1792
1793 if (!out && !child.fComp) {
1794 std::shared_ptr<RooAbsArg> _func;
1795 // a null node .. so create either a new RooProduct or RooHistFunc if has observables (or no deps but has
1796 // x-axis)
1797 auto _obs = robs();
1798 if (!_obs.empty() || GetXaxis()) {
1799 if (_obs.empty()) {
1800 // using X axis to construct hist
1801 auto _ax = dynamic_cast<Axis2 *>(GetXaxis());
1802 auto t = TH1::AddDirectoryStatus();
1803 TH1::AddDirectory(false);
1804 auto h =
1805 std::make_unique<TH1D>(child.GetName(), child.GetTitle(), _ax->GetNbins(), _ax->binning()->array());
1807 h->GetXaxis()->SetName(TString::Format("%s;%s", _ax->GetParent()->GetName(), _ax->GetName()));
1808 // technically convertForAcquisition has already acquired so no need to re-acquire but should be harmless
1809 _func = std::dynamic_pointer_cast<RooAbsArg>(acquire(xRooNode(*h).convertForAcquisition(*this)));
1810 } else if (_obs.size() == 1) {
1811 // use the single obs to make a TH1D
1812 auto _x = _obs.at(0)->get<RooAbsLValue>();
1813 auto _bnames = _x->getBinningNames();
1814 TString binningName = p->getStringAttribute("binning");
1815 for (auto &b : _bnames) {
1816 if (b == p->GetName()) {
1817 binningName = p->GetName();
1818 break;
1819 }
1820 }
1821 auto t = TH1::AddDirectoryStatus();
1822 TH1::AddDirectory(false);
1823 auto h = std::make_unique<TH1D>(child.GetName(), child.GetTitle(), _x->numBins(binningName),
1824 _x->getBinningPtr(binningName)->array());
1826 h->GetXaxis()->SetName(
1827 TString::Format("%s;%s", dynamic_cast<TObject *>(_x)->GetName(), binningName.Data()));
1828 // technically convertForAcquisition has already acquired so no need to re-acquire but should be harmless
1829 _func = std::dynamic_pointer_cast<RooAbsArg>(acquire(xRooNode(*h).convertForAcquisition(*this)));
1830 Info("Add", "Created densityhisto factor %s (xaxis=%s) for %s", _func->GetName(), _obs.at(0)->GetName(),
1831 p->GetName());
1832 } else {
1833 throw std::runtime_error("Unsupported creation of new component in SumPdf for this many obs");
1834 }
1835 } else {
1836 _func = acquireNew<RooProduct>(TString::Format("%s_%s", p->GetName(), child.GetName()), child.GetTitle(),
1837 RooArgList());
1838 }
1839 _func->setStringAttribute("alias", child.GetName());
1840 out = _func;
1841 }
1842
1843 if (auto _f = std::dynamic_pointer_cast<RooHistFunc>(
1844 (child.get<RooProduct>()) ? child.factors()[child.GetName()]->fComp : out);
1845 _f) {
1846 // adding a histfunc directly to a sumpdf, should be a density
1847 _f->setAttribute("density");
1848 if (_f->getAttribute("autodensity")) {
1849 // need to divide by bin widths first
1850 for (int i = 0; i < _f->dataHist().numEntries(); i++) {
1851 auto bin_pars = _f->dataHist().get(i);
1852 _f->dataHist().set(*bin_pars, _f->dataHist().weight() / _f->dataHist().binVolume(*bin_pars));
1853 }
1854 _f->setAttribute("autodensity", false);
1855 _f->setValueDirty();
1856 }
1857
1858 // promote the axis vars to observables
1859 // can't use original child as might refer to unacquired deps
1860 for (auto &x : xRooNode("tmp", _f).vars()) {
1861 x->get<RooAbsArg>()->setAttribute("obs");
1862 }
1863 if (isConverted)
1864 Info("Add", "Created %s factor RooHistFunc::%s for %s",
1865 _f->getAttribute("density") ? "densityhisto" : "histo", _f->GetName(), p->GetName());
1866 }
1867
1868 if (auto _p = std::dynamic_pointer_cast<RooAbsPdf>(out); _p) {
1869 // adding a pdf to a RooRealSumPdf will replace it with a RooAddPdf and put the RooRealSumPdf inside that
1870 // if pdf is extended will use in the "no coefficients" state, where the expectedEvents are taking from
1871 // the pdf integrals
1872 TString newName(_p->GetName());
1873 newName.ReplaceAll("_samples", "");
1874 newName += "_components";
1875 Warning("Add", "converting samples to components");
1876
1877 if (auto _ax = GetXaxis(); _ax && dynamic_cast<RooAbsRealLValue *>(_ax->GetParent()) &&
1878 _p->dependsOn(*static_cast<RooAbsArg *>(_ax->GetParent()))) {
1879
1880 if (auto _boundaries = std::unique_ptr<std::list<double>>(_p->binBoundaries(
1881 *dynamic_cast<RooAbsRealLValue *>(_ax->GetParent()), -std::numeric_limits<double>::infinity(),
1882 std::numeric_limits<double>::infinity()));
1883 !_boundaries && _ax->GetNbins() > 0) {
1884#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 24, 00)
1885 Warning("Add", "Adding unbinned pdf %s to binned %s - will wrap with RooBinSamplingPdf(...)",
1886 _p->GetName(), GetName());
1887 _p = acquireNew<RooBinSamplingPdf>(TString::Format("%s_binned", _p->GetName()), _p->GetTitle(),
1888 *dynamic_cast<RooAbsRealLValue *>(_ax->GetParent()), *_p);
1889 _p->setStringAttribute("alias", std::dynamic_pointer_cast<RooAbsArg>(out)->getStringAttribute("alias"));
1890 if (!_p->getStringAttribute("alias"))
1891 _p->setStringAttribute("alias", out->GetName());
1892#else
1893 throw std::runtime_error(
1894 "unsupported addition of unbinned pdf to binned model - please upgrade to at least ROOT 6.24");
1895#endif
1896 }
1897 }
1898
1899 // require to be extended to be in coefficient-free mode ...
1900 // otherwise would lose the integral of the sumPdf (can't think of way to have a coef be the integral)
1901 if (!_p->canBeExtended()) {
1902 _p = acquireNew<RooExtendPdf>(TString::Format("%s_extended", _p->GetName()), _p->GetTitle(), *_p,
1903 *acquire2<RooAbsReal, RooRealVar>("1", "1", 1));
1904 }
1905
1906 return *(Replace(*acquireNew<RooAddPdf>(newName, _p->GetTitle(), RooArgList(*p, *_p)))
1907 .browse()[1]); // returns second node.
1908 }
1909
1910 if (auto _f = std::dynamic_pointer_cast<RooAbsReal>(out); _f) {
1911
1912 // todo: if adding a pdf, should actually replace RooRealSumPdf with a RooAddPdf and put
1913 // the sumPdf and *this* pdf inside that pdf
1914 // only exception is the binSamplingPdf below to integrate unbinned functions across bins
1915
1916 if (auto _ax = GetXaxis(); _ax && dynamic_cast<RooAbsRealLValue *>(_ax->GetParent()) &&
1917 _f->dependsOn(*static_cast<RooAbsArg *>(_ax->GetParent()))) {
1918
1919 if (auto _boundaries = std::unique_ptr<std::list<double>>(_f->binBoundaries(
1920 *dynamic_cast<RooAbsRealLValue *>(_ax->GetParent()), -std::numeric_limits<double>::infinity(),
1921 std::numeric_limits<double>::infinity()));
1922 !_boundaries && _ax->GetNbins() > 0) {
1923#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 24, 00)
1924 Warning(
1925 "Add",
1926 "Adding unbinned function %s to binned %s - will wrap with RooRealSumPdf(RooBinSamplingPdf(...))",
1927 _f->GetName(), GetName());
1928 auto sumPdf = acquireNew<RooRealSumPdf>(TString::Format("%s_pdfWrapper", _f->GetName()), _f->GetTitle(),
1929 *_f, *acquire2<RooAbsArg, RooRealVar>("1", "1", 1), true);
1930 sumPdf->setStringAttribute("alias", _f->getStringAttribute("alias"));
1931 if (!sumPdf->getStringAttribute("alias"))
1932 sumPdf->setStringAttribute("alias", out->GetName());
1933 _f = acquireNew<RooBinSamplingPdf>(TString::Format("%s_binned", _f->GetName()), _f->GetTitle(),
1934 *dynamic_cast<RooAbsRealLValue *>(_ax->GetParent()), *sumPdf);
1935 _f->setStringAttribute("alias", std::dynamic_pointer_cast<RooAbsArg>(out)->getStringAttribute("alias"));
1936 if (!_f->getStringAttribute("alias"))
1937 _f->setStringAttribute("alias", out->GetName());
1938#else
1939 throw std::runtime_error(
1940 "unsupported addition of unbinned function to binned model - please upgrade to at least ROOT 6.24");
1941#endif
1942 }
1943 }
1944
1945 const_cast<RooArgList &>(p->coefList()).add(*acquire2<RooAbsArg, RooRealVar>("1", "1", 1));
1946 const_cast<RooArgList &>(p->funcList()).add(*_f);
1947 // inherit binning if we dont have one yet
1948 if (!p->getStringAttribute("binning"))
1949 p->setStringAttribute("binning", _f->getStringAttribute("binning"));
1950
1951 xRooNode _out(_f, *this);
1952 if (auto gf = p->getStringAttribute("global_factors"); gf) {
1953 TStringToken pattern(gf, ";");
1954 while (pattern.NextToken()) {
1955 auto fac = getObject<RooAbsReal>(pattern.Data());
1956 if (!fac) {
1957 throw std::runtime_error(TString::Format("Could not find global factor %s", pattern.Data()));
1958 }
1959 _out.Multiply(fac);
1960 }
1961 }
1962 sterilize();
1963 // clear children for reload and update shared axis
1964 clear();
1965 fXAxis.reset();
1966 p->setStringAttribute("xvar", nullptr);
1967 browse();
1968 return _out;
1969 }
1970 } else if (auto p2 = get<RooProdPdf>(); p2) {
1971 // can "add" to a RooProdPdf provided trying to add a RooAbsReal not a RooAbsPdf and have a zero or 1
1972 // RooRealSumPdf child.convertForAcquisition(*this); - don't convert here because want generated objects named
1973 // after roorealsumpdf
1974 if (child.get<RooAbsPdf>() || (!child.get() && getObject<RooAbsPdf>(child.GetName()))) {
1975 // can add if 0 or 1 RooAddPdf ....
1976 RooAddPdf *_pdf = nullptr;
1977 bool tooMany(false);
1978 for (auto &pp : factors()) {
1979 if (auto _p = pp->get<RooAddPdf>(); _p) {
1980 if (_pdf) {
1981 _pdf = nullptr;
1982 tooMany = true;
1983 break;
1984 } // more than one!
1985 _pdf = _p;
1986 }
1987 }
1988 if (_pdf) {
1989 return xRooNode(*_pdf, *this).Add(child);
1990 } else if (!tooMany) {
1991 auto out = this->operator[]("components")->Add(child);
1992 return out;
1993 }
1994 } else if ((child.get<TH1>() || child.get<RooAbsReal>() ||
1995 (!child.get() && getObject<RooAbsReal>(child.GetName()))) &&
1996 !child.get<RooAbsPdf>()) {
1997 RooRealSumPdf *_pdf = nullptr;
1998 RooAddPdf *_backup = nullptr;
1999 bool tooMany(false);
2000 for (auto &pp : factors()) {
2001 if (auto _p = pp->get<RooRealSumPdf>(); _p) {
2002 if (_pdf) {
2003 _pdf = nullptr;
2004 tooMany = true;
2005 break;
2006 } // more than one!
2007 _pdf = _p;
2008 } else if (auto _p2 = pp->get<RooAddPdf>(); _p2) {
2009 _backup = _p2;
2010 for (auto &_pdfa : pp->components()) {
2011 if (auto _p3 = _pdfa->get<RooRealSumPdf>(); _p3) {
2012 if (_pdf) {
2013 _pdf = nullptr;
2014 tooMany = true;
2015 break;
2016 } // more than one!
2017 _pdf = _p3;
2018 }
2019 }
2020 }
2021 }
2022 if (_pdf) {
2023 return xRooNode(*_pdf, *this).Add(child);
2024 } else if (_backup) {
2025 // added *INSIDE* the addPdf -- will create a RooRealSumPdf to hold it
2026 return xRooNode(*_backup, *this).Add(child);
2027 } else if (!tooMany) {
2028 auto out = this->operator[]("samples")->Add(child);
2029 // clear our x-axis to re-evaluate
2030 fXAxis.reset();
2031 p2->setStringAttribute("xvar", nullptr);
2032 return out;
2033 }
2034 }
2035 } else if (auto s = get<RooSimultaneous>(); s) {
2036
2037 // adding to a simultaneous means adding a bin
2038 return bins().Add(child);
2039
2040 // if the child is a RooAbsPdf can just add it as a new channel using name of pdf as the channel name
2041 // if child is a histogram, will create a RooProdPdf
2042
2043 } else if (auto w = get<RooWorkspace>(); w) {
2044 child.convertForAcquisition(*this);
2045 if (child.get()) {
2046 if(auto _d = child.get<RooAbsData>()) {
2047 // don't use acquire method to import, because that adds datasets as Embeddded
2048 if (!w->import(*_d)) {
2049 return xRooNode(child.GetName(), *w->data(child.GetName()),*this);
2050 } else {
2051 throw std::runtime_error(TString::Format("Could not import dataset %s into workspace %s",child.GetName(),w->GetName()).Data());
2052 }
2053 } else {
2054 auto out = acquire(child.fComp);
2055 if (out)
2056 return xRooNode(child.GetName(), out, *this);
2057 }
2058 }
2059
2060 if (!child.empty() || child.fFolder == "!models") {
2061 // create a RooSimultaneous using the children as the channels
2062 // children either have "=" in name if specifying channel cat name or otherwise assume
2063 std::string catName = "channelCat";
2064 if (!child.empty()) {
2065 if (TString ss = child.at(0)->GetName(); ss.Contains("=")) {
2066 catName = ss(0, ss.Index('='));
2067 }
2068 }
2069 auto _cat = acquire<RooCategory>(catName.c_str(), catName.c_str());
2070 _cat->setAttribute("obs");
2071 auto out = acquireNew<RooSimultaneous>(child.GetName(), child.GetTitle(), *_cat);
2072 Info("Add", "Created model RooSimultaneous::%s in workspace %s", out->GetName(), w->GetName());
2073 return xRooNode(out, *this);
2074 }
2075 }
2076
2077 if (sOpt == "model") {
2078 // can only add a model to a workspace
2079 if (get<RooWorkspace>()) {
2080 const_cast<xRooNode &>(child).fFolder = "!models";
2081 return Add(child);
2082 }
2083 } else if (sOpt == "channel") {
2084 // can add to a model or to a workspace (creates a RooProdPdf either way)
2085 if (get<RooSimultaneous>()) {
2086 return Vary(child);
2087 } else if (get<RooWorkspace>()) {
2088 std::shared_ptr<TObject> out;
2089 child.convertForAcquisition(*this);
2090 if (child.get<RooAbsPdf>())
2091 out = acquire(child.fComp);
2092 else if (!child.fComp) {
2093 out = acquireNew<RooProdPdf>(child.GetName(),
2094 (strlen(child.GetTitle())) ? child.GetTitle() : child.GetName(), RooArgList());
2095 Info("Add", "Created channel RooProdPdf::%s in workspace %s", out->GetName(), get()->GetName());
2096 }
2097 return xRooNode(out, *this);
2098 }
2099 } else if (sOpt == "sample" || sOpt == "func") {
2100 if (get<RooProdPdf>()) {
2101 auto _mainChild = mainChild();
2102 if (_mainChild.get<RooRealSumPdf>()) {
2103 return _mainChild.Add(child, sOpt == "func" ? "func" : "");
2104 } else {
2105 return (*this)["samples"]->Add(child, sOpt == "func" ? "func" : "");
2106 }
2107 }
2108 } else if (sOpt == "dataset") {
2109 if (get<RooWorkspace>()) {
2110 // const_cast<xRooNode&>(child).fFolder = "!datasets";return Add(child);
2111 return (*this).datasets().Add(child);
2112 }
2113 }
2114
2115 if (considerType) {
2116
2117 // interpret 'adding' here as dependent on the object type ...
2118 if (get<RooSimultaneous>()) {
2119 return bins().Add(child);
2120 } else if (TString(child.GetName()).Contains('=')) {
2121 return variations().Add(child);
2122 } else if (get<RooProduct>() || get<RooProdPdf>()) {
2123 return factors().Add(child);
2124 }
2125 }
2126
2127 // Nov 2022 - removed ability to add placeholders ... could bring back if rediscover need for them
2128 // if (!child.get() && child.empty() && strlen(child.GetName())) {
2129 // // can add a 'placeholder' node, note it will be deleted at the next browse
2130 // xRooNode out(child.GetName(),nullptr,*this);
2131 // out.SetTitle(child.GetTitle());
2132 // emplace_back(std::make_shared<xRooNode>(out));
2133 // // update the parent in the out node so that it's copy of the parent knows it has itself in it
2134 // // actually maybe not want this :-/
2135 // //out.fParent = std::make_shared<Node2>(*this);
2136 // for(auto o : *gROOT->GetListOfBrowsers()) {
2137 // if(auto b = dynamic_cast<TBrowser*>(o); b && b->GetBrowserImp()){
2138 // if(auto _b = dynamic_cast<TGFileBrowser*>(
2139 // dynamic_cast<TRootBrowser*>(b->GetBrowserImp())->fActBrowser ); _b) {
2140 // auto _root = _b->fRootDir;
2141 // if (!_root) _root = _b->fListTree->GetFirstItem();
2142 // if (auto item = _b->fListTree->FindItemByObj(_root,this); item) {
2143 // _b->fListTree->AddItem(item,back()->GetName(),back().get());
2144 // }
2145 // }
2146 // }
2147 // }
2148 // return out;
2149 // }
2150
2151 throw std::runtime_error(TString::Format("Cannot add %s to %s", child.GetName(), GetName()));
2152}
2153
2154std::string xRooNode::GetPath() const
2155{
2156 if (!fParent)
2157 return GetName();
2158 return fParent->GetPath() + "/" + GetName();
2159}
2160
2162{
2163 // std::cout << "deleting " << GetPath() << std::endl;
2164}
2165
2167{
2168 if (auto a = get<RooAbsArg>()) {
2169 a->setAttribute("hidden", set);
2170 // if(auto item = GetTreeItem(nullptr); item) {
2171 // if(set) item->SetColor(kRed);
2172 // else item->ClearColor();
2173 // }
2174 }
2175}
2177{
2178 auto a = get<RooAbsArg>();
2179 if (a)
2180 return a->getAttribute("hidden");
2181 return false;
2182}
2183
2185{
2186
2187 if (get() == rhs.get()) {
2188 // nothing to do because objects are identical
2189 return *this;
2190 }
2191
2192 // Info("Combine","Combining %s into %s",rhs.GetPath().c_str(),GetPath().c_str());
2193
2194 // combine components, factors, and variations ... when there is a name clash will combine on that object
2195 for (auto &c : rhs.components()) {
2196 if (auto _c = components().find(c->GetName()); _c) {
2197 _c->Combine(*c);
2198 } else {
2199 Add(*c);
2200 }
2201 }
2202
2203 for (auto &f : rhs.factors()) {
2204 if (auto _f = factors().find(f->GetName()); _f) {
2205 _f->Combine(*f);
2206 } else {
2207 Multiply(*f);
2208 }
2209 }
2210
2211 for (auto &v : rhs.variations()) {
2212 if (auto _v = variations().find(v->GetName()); _v) {
2213 _v->Combine(*v);
2214 } else {
2215 Vary(*v);
2216 }
2217 }
2218
2219 // todo: Should also transfer over binnings of observables
2220
2221 return *this;
2222}
2223
2224xRooNode xRooNode::shallowCopy(const std::string &name, std::shared_ptr<xRooNode> parent)
2225{
2226 xRooNode out(name.c_str(), nullptr,
2227 parent /*? parent : fParent -- was passing fParent for getObject benefit before fProvider concept*/);
2228 // if(!parent) out.fAcquirer = true;
2229 if (!parent)
2230 out.fProvider = fParent;
2231
2232 auto o = get();
2233 if (!o) {
2234 return out;
2235 }
2236
2237 if (auto s = get<RooSimultaneous>(); s) {
2238 auto chans = bins();
2239 if (!chans.empty()) {
2240 // create a new RooSimultaneous with shallow copies of each channel
2241
2242 std::shared_ptr<RooSimultaneous> pdf = out.acquire<RooSimultaneous>(
2243 name.c_str(), o->GetTitle(), const_cast<RooAbsCategoryLValue &>(s->indexCat()));
2244
2245 for (auto &c : chans) {
2246 TString cName(c->GetName());
2247 cName = cName(cName.Index('=') + 1, cName.Length());
2248 // by passing out as the parent, will ensure out acquires everything created
2249 auto c_copy =
2250 c->shallowCopy(name + "_" + c->get()->GetName(), std::shared_ptr<xRooNode>(&out, [](xRooNode *) {}));
2251 pdf->addPdf(*dynamic_cast<RooAbsPdf *>(c_copy.get()), cName);
2252 }
2253 out.fComp = pdf;
2254 return out;
2255 }
2256 } else if (auto p = dynamic_cast<RooProdPdf *>(o); p) {
2257 // main pdf will be copied too
2258 std::shared_ptr<RooProdPdf> pdf =
2259 std::dynamic_pointer_cast<RooProdPdf>(out.acquire(std::shared_ptr<TObject>(p->Clone(/*name.c_str()*/)), false,
2260 true)); // use clone to copy all attributes etc too
2261 auto main = mainChild();
2262 if (main) {
2263 auto newMain =
2264 std::dynamic_pointer_cast<RooAbsArg>(out.acquire(std::shared_ptr<TObject>(main->Clone()), false, true));
2265 std::cout << newMain << " " << newMain->GetName() << std::endl;
2266 // pdf->replaceServer(*pdf->pdfList().find(main->GetName()), *newMain, true, true);
2267 // const_cast<RooArgList&>(pdf->pdfList()).replace(*pdf->pdfList().find(main->GetName()), *newMain);
2268 pdf->redirectServers(RooArgList(*newMain));
2269 }
2270 out.fComp = pdf;
2271 out.sterilize();
2272 return out;
2273 }
2274
2275 return out;
2276}
2277
2279{
2280 static std::unique_ptr<cout_redirect> capture;
2281 std::string captureStr;
2282 bool doCapture = false;
2283 if (!capture && gROOT->FromPopUp()) { // FromPopUp means user executed from the context menu
2284 capture = std::make_unique<cout_redirect>(captureStr);
2285 doCapture = true;
2286 }
2287
2288 TString sOpt(opt);
2289 int depth = 0;
2290 if (sOpt.Contains("depth=")) {
2291 depth = TString(sOpt(sOpt.Index("depth=") + 6, sOpt.Length())).Atoi();
2292 sOpt.ReplaceAll(TString::Format("depth=%d", depth), "");
2293 }
2294 int indent = 0;
2295 if (sOpt.Contains("indent=")) {
2296 indent = TString(sOpt(sOpt.Index("indent=") + 7, sOpt.Length())).Atoi();
2297 sOpt.ReplaceAll(TString::Format("indent=%d", indent), "");
2298 }
2299 bool _more = sOpt.Contains("m");
2300 if (_more)
2301 sOpt.Replace(sOpt.Index("m"), 1, "");
2302 if (sOpt != "")
2303 _more = true;
2304
2305 if (indent == 0) { // only print self if not indenting (will already be printed above if tree traverse)
2306 std::cout << GetPath();
2307 if (get() && get() != this) {
2308 std::cout << ": ";
2309 if (_more || (get<RooAbsArg>() && get<RooAbsArg>()->isFundamental()) || get<RooConstVar>() ||
2310 get<RooAbsData>() || get<RooProduct>() || get<RooFitResult>()) {
2311 auto _deps = coords(false).argList(); // want to revert coords after print
2312 auto _snap = std::unique_ptr<RooAbsCollection>(_deps.snapshot());
2313 coords(); // move to coords before printing (in case this matters)
2314 get()->Print(sOpt);
2315 if (auto _fr = get<RooFitResult>(); _fr && dynamic_cast<RooStringVar *>(_fr->constPars().find(".log"))) {
2316 std::cout << "Minimization Logs:" << std::endl;
2317 std::cout << dynamic_cast<RooStringVar *>(_fr->constPars().find(".log"))->getVal() << std::endl;
2318 }
2319 _deps.assignValueOnly(*_snap);
2320 // std::cout << std::endl;
2321 } else {
2322 TString _suffix = "";
2323 if (auto _type = GetNodeType(); strlen(_type)) {
2324 // decided not to show const values until figure out how to update if value changes
2325 /*if (TString(_type)=="Const") _name += TString::Format("
2326 [%s=%g]",_type,v->get<RooConstVar>()->getVal()); else*/
2327 _suffix += TString::Format(" [%s]", _type);
2328 }
2329 if (auto fv = get<RooFormulaVar>()) {
2330 TString formu = TString::Format(" [%s]", fv->expression());
2331 for (size_t i = 0; i < fv->dependents().size(); i++) {
2332 formu.ReplaceAll(TString::Format("x[%zu]", i), fv->dependents()[i].GetName());
2333 }
2334 _suffix += formu;
2335 } else if (auto gv = get<RooGenericPdf>()) {
2336 TString formu = TString::Format(" [%s]", gv->expression());
2337 for (size_t i = 0; i < gv->dependents().size(); i++) {
2338 formu.ReplaceAll(TString::Format("x[%zu]", i), gv->dependents()[i].GetName());
2339 }
2340 _suffix += formu;
2341 }
2342 std::cout << get()->ClassName() << "::" << get()->GetName() << _suffix.Data() << std::endl;
2343 }
2344
2345 } else if (!get()) {
2346 std::cout << std::endl;
2347 }
2348 }
2349 const_cast<xRooNode *>(this)->browse();
2350 std::vector<std::string> folderNames;
2351 for (auto &k : *this) {
2352 if (std::find(folderNames.begin(), folderNames.end(), k->fFolder) == folderNames.end()) {
2353 folderNames.push_back(k->fFolder);
2354 }
2355 }
2356 for (auto &f : folderNames) {
2357 int i = 0;
2358 int iindent = indent;
2359 if (!f.empty()) {
2360 for (int j = 0; j < indent; j++)
2361 std::cout << " ";
2362 std::cout << f << std::endl;
2363 iindent += 1;
2364 }
2365 for (auto &k : *this) {
2366 if (k->fFolder != f) {
2367 i++;
2368 continue;
2369 }
2370 for (int j = 0; j < iindent; j++)
2371 std::cout << " ";
2372 std::cout << i++ << ") " << k->GetName() << " : ";
2373 if (k->get()) {
2374 if (_more || (k->get<RooAbsArg>() && k->get<RooAbsArg>()->isFundamental()) || k->get<RooConstVar>() ||
2375 k->get<RooAbsData>() /*|| k->get<RooProduct>()*/) {
2376 auto _deps = k->coords(false).argList();
2377 auto _snap = std::unique_ptr<RooAbsCollection>(_deps.snapshot());
2378 k->coords(); // move to coords before printing (in case this matters)
2379 k->get()->Print(sOpt); // assumes finishes with an endl
2380 _deps.assignValueOnly(*_snap);
2381 } else {
2382 TString _suffix = "";
2383 if (auto _type = k->GetNodeType(); strlen(_type)) {
2384 // decided not to show const values until figure out how to update if value changes
2385 /*if (TString(_type)=="Const") _name += TString::Format("
2386 [%s=%g]",_type,v->get<RooConstVar>()->getVal()); else*/
2387 _suffix += TString::Format(" [%s]", _type);
2388 }
2389 if (auto fv = k->get<RooFormulaVar>()) {
2390 TString formu = TString::Format(" [%s]", fv->expression());
2391 for (size_t j = 0; j < fv->dependents().size(); j++) {
2392 formu.ReplaceAll(TString::Format("x[%zu]", j), fv->dependents()[j].GetName());
2393 }
2394 _suffix += formu;
2395 } else if (auto gv = k->get<RooGenericPdf>()) {
2396 TString formu = TString::Format(" [%s]", gv->expression());
2397 for (size_t j = 0; j < gv->dependents().size(); j++) {
2398 formu.ReplaceAll(TString::Format("x[%zu]", j), gv->dependents()[j].GetName());
2399 }
2400 _suffix += formu;
2401 }
2402 std::cout << k->get()->ClassName() << "::" << k->get()->GetName() << _suffix.Data() << std::endl;
2403 }
2404 if (depth != 0) {
2405 k->Print(sOpt + TString::Format("depth=%dindent=%d", depth - 1, iindent + 1));
2406 }
2407 } else
2408 std::cout << " NULL " << std::endl;
2409 }
2410 }
2411 if (doCapture) {
2412 capture.reset(); // no captureStr has the string to display
2413 // inject line breaks to avoid msgbox being too wide
2414 size_t lastBreak = 0;
2415 std::string captureStrWithBreaks;
2416 for (size_t i = 0; i < captureStr.size(); i++) {
2417 captureStrWithBreaks += captureStr[i];
2418 if (captureStr[i] == '\n') {
2419 lastBreak = i;
2420 }
2421 if (i - lastBreak > 150) {
2422 captureStrWithBreaks += '\n';
2423 lastBreak = i;
2424 }
2425 }
2426 const TGWindow *w =
2427 (gROOT->GetListOfBrowsers()->At(0))
2428 ? dynamic_cast<TGWindow *>(static_cast<TBrowser *>(gROOT->GetListOfBrowsers()->At(0))->GetBrowserImp())
2429 : gClient->GetRoot();
2430 new TGMsgBox(gClient->GetRoot(), w, GetName(),
2431 captureStrWithBreaks.c_str()); //,nullptr,kMBDismiss,nullptr,kVerticalFrame,kTextLeft|kTextCenterY);
2432 }
2433}
2434
2436{
2437 if (!child.get()) {
2438
2439 if (auto v = get<RooRealVar>(); v) {
2440
2441 TString constrType = child.GetName();
2442 double mean = std::numeric_limits<double>::quiet_NaN();
2443 double sigma = mean;
2444 if (constrType.BeginsWith("gaussian(")) {
2445 // extract the mean and stddev parameters
2446 // if only one given, it is the stddev
2447 if (constrType.Contains(",")) {
2448 mean = TString(constrType(9, constrType.Index(',') - 9)).Atof();
2449 sigma = TString(constrType(constrType.Index(',') + 1, constrType.Index(')') - constrType.Index(',') + 1))
2450 .Atof();
2451 } else {
2452 mean = std::numeric_limits<double>::quiet_NaN(); // will use the var current value below to set mean
2453 sigma = TString(constrType(9, constrType.Index(')') - 9)).Atof();
2454 }
2455 constrType = "normal";
2456 } else if (constrType == "normal") {
2457 mean = 0;
2458 sigma = 1;
2459 } else if (constrType == "gaussian") {
2460 // extract parameters from the variable
2461 // use current value and error on v as constraint
2462 if (!v->hasError())
2463 throw std::runtime_error("No error on parameter for gaussian constraint");
2464 sigma = v->getError();
2465 mean = v->getVal();
2466 constrType = "normal";
2467 } else if (constrType == "poisson") {
2468 if (!v->hasError())
2469 throw std::runtime_error("No error on parameter for poisson constraint");
2470 mean = 1;
2471 sigma = pow(v->getVal() / v->getError(), 2);
2472 }
2473
2474 if (constrType == "poisson") {
2475 // use current value and error on v as constraint
2476 double tau_val = sigma;
2477 auto globs = acquire<RooRealVar>(Form("globs_%s", v->GetName()), Form("globs_%s", v->GetName()),
2478 v->getVal() * tau_val, (v->getVal() - 5 * v->getError()) * tau_val,
2479 (v->getVal() + 5 * v->getError()) * tau_val);
2480 globs->setConstant();
2481 globs->setAttribute("obs");
2482 globs->setAttribute("global");
2483 globs->setStringAttribute("nominal", TString::Format("%f", tau_val));
2484 auto tau = acquireNew<RooConstVar>(TString::Format("tau_%s", v->GetName()), "", tau_val);
2485 auto constr = acquireNew<RooPoisson>(
2486 Form("pois_%s", v->GetName()), TString::Format("Poisson Constraint of %s", v->GetTitle()), *globs,
2487 *acquireNew<RooProduct>(TString::Format("mean_%s", v->GetName()),
2488 TString::Format("Poisson Constraint of %s", globs->GetTitle()),
2489 RooArgList(*v, *tau)),
2490 true /* no rounding */);
2491
2492 auto out = Constrain(xRooNode(Form("pois_%s", GetName()), constr));
2493 if (!v->hasError())
2494 v->setError(mean / sqrt(tau_val)); // if v doesnt have an uncert, will put one on it now
2495 Info("Constrain", "Added poisson constraint pdf RooPoisson::%s (tau=%g) for %s", out->GetName(), tau_val,
2496 GetName());
2497 return out;
2498 } else if (constrType == "normal") {
2499
2500 auto globs = acquire<RooRealVar>(Form("globs_%s", v->GetName()), Form("globs_%s", v->GetName()), mean,
2501 mean - 10 * sigma, mean + 10 * sigma);
2502 globs->setAttribute("obs");
2503 globs->setAttribute("global");
2504 globs->setConstant();
2505
2506 globs->setStringAttribute("nominal", TString::Format("%f", mean));
2507 auto constr = acquireNew<RooGaussian>(
2508 Form("gaus_%s", v->GetName()), TString::Format("Gaussian Constraint of %s", v->GetTitle()), *globs, *v,
2509 *acquireNew<RooConstVar>(TString::Format("sigma_%s", v->GetName()), "", sigma));
2510 auto out = Constrain(xRooNode(Form("gaus_%s", GetName()), constr));
2511 if (!v->hasError())
2512 v->setError(sigma); // if v doesnt have an uncert, will put one on it now
2513 Info("Constrain", "Added gaussian constraint pdf RooGaussian::%s (mean=%g,sigma=%g) for %s", out->GetName(),
2514 mean, sigma, GetName());
2515 return out;
2516 }
2517 }
2518 } else if (auto p = child.get<RooAbsPdf>(); p) {
2519
2520 auto _me = get<RooAbsArg>();
2521 if (!_me) {
2522 throw std::runtime_error("Cannot constrain non arg");
2523 }
2524
2525 if (!p->dependsOn(*_me)) {
2526 throw std::runtime_error("Constraint does not depend on constrainee");
2527 }
2528
2529 // find a parent that can swallow this pdf ... either a RooProdPdf or a RooWorkspace
2530 auto x = fParent;
2531 while (x && !x->get<RooProdPdf>() && !x->get<RooSimultaneous>() && !x->get<RooWorkspace>()) {
2532 x = x->fParent;
2533 }
2534 if (!x) {
2535 throw std::runtime_error("Nowhere to put constraint");
2536 }
2537
2538 if (auto s = x->get<RooSimultaneous>(); s) {
2539 // put into every channel that features parameter
2540 x->browse();
2541 for (auto &c : *x) {
2542 if (auto a = c->get<RooAbsArg>(); a->dependsOn(*_me))
2543 c->Multiply(child);
2544 }
2545 return child;
2546 } else if (x->get<RooProdPdf>()) {
2547 return x->Multiply(child);
2548 } else {
2549 return x->Add(child, "+");
2550 }
2551 }
2552
2553 throw std::runtime_error(TString::Format("Cannot constrain %s", GetName()));
2554}
2555
2557{
2558
2559 class AutoUpdater {
2560 public:
2561 AutoUpdater(xRooNode &_n) : n(_n) {}
2562 ~AutoUpdater() { n.browse(); }
2563 xRooNode &n;
2564 };
2565 AutoUpdater xxx(*this);
2566
2567 if (fBinNumber != -1) {
2568 // scaling a bin ...
2569 if (child.get<RooAbsReal>()) { // if not child then let fall through to create a child and call self again below
2570 // doing a bin-multiplication .. the parent should have a ParamHistFunc called binFactors
2571 // if it doesn't then create one
2572 auto o = std::dynamic_pointer_cast<RooAbsReal>(acquire(child.fComp));
2573
2574 // get binFactor unless parent is a ParamHistFunc already ...
2575
2576 auto binFactors = (fParent->get<ParamHistFunc>()) ? fParent : fParent->factors().find("binFactors");
2577
2578 // it can happen in a loop over bins() that another node has moved fParent inside a product
2579 // so check for fParent having a client with the ORIGNAME:<name> attribute
2580 if (!binFactors && fParent->get<RooAbsArg>()) {
2581 for (auto c : fParent->get<RooAbsArg>()->clients()) {
2582 if (c->IsA() == RooProduct::Class() &&
2583 c->getAttribute(TString::Format("ORIGNAME:%s", fParent->get()->GetName()))) {
2584 // try getting binFactors out of this
2585 binFactors = xRooNode(*c).factors().find("binFactors");
2586 break;
2587 }
2588 }
2589 }
2590
2591 if (!binFactors) {
2592 fParent
2593 ->Multiply(TString::Format("%s_binFactors",
2594 (fParent->mainChild().get())
2595 ? fParent->mainChild()->GetName()
2596 : (fParent->get() ? fParent->get()->GetName() : fParent->GetName()))
2597 .Data(),
2598 "blankshape")
2599 .SetName("binFactors"); // creates ParamHistFunc with all pars = 1 (shared const)
2600 binFactors = fParent->factors().find("binFactors");
2601 if (!binFactors) {
2602 throw std::runtime_error(
2603 TString::Format("Could not create binFactors in parent %s", fParent->GetName()));
2604 }
2605 // auto phf = binFactors->get<ParamHistFunc>();
2606
2607 // create RooProducts for all the bins ... so that added factors don't affect selves
2608 int i = 1;
2609 for (auto &b : binFactors->bins()) {
2610 auto p = acquireNew<RooProduct>(TString::Format("%s_bin%d", binFactors->get()->GetName(), i),
2611 TString::Format("binFactors of bin %d", i), RooArgList());
2612 p->setStringAttribute("alias", TString::Format("%s=%g", binFactors->GetXaxis()->GetParent()->GetName(),
2613 binFactors->GetXaxis()->GetBinCenter(i)));
2614 b->Multiply(*p);
2615 i++;
2616 }
2617 }
2618 // then scale the relevant bin ... if the relevant bin is a "1" then just drop in our factor (inside a
2619 // RooProduct though, to avoid it getting modified by subsequent multiplies)
2620 auto _bin = binFactors->bins().at(fBinNumber - 1);
2621 if (auto phf = binFactors->get<ParamHistFunc>(); phf && _bin) {
2622#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
2623 RooArgList &pSet = phf->_paramSet;
2624#else
2625 RooArgList &pSet = const_cast<RooArgList &>(phf->paramList());
2626#endif
2627 if (strcmp(_bin->GetName(), "1") == 0) {
2628 RooArgList all;
2629 for (int i = 0; i < pSet.getSize(); i++) {
2630 if (i != fBinNumber - 1)
2631 all.add(*pSet.at(i));
2632 else
2633 all.add(*o);
2634 }
2635 pSet.removeAll();
2636 pSet.add(all);
2637 } else {
2638 _bin->fBinNumber = -1; // to avoid infinite loop
2639 return _bin->Multiply(child, opt);
2640 }
2641 // } else {else if(_bin->get<RooProduct>()) {
2642 // // multiply the element which will just add it as a factor in the rooproduct
2643 // return _bin->Multiply(child,opt);
2644 // } else {
2645 // // not a rooproduct in this bin yet ... so need to replace with a rooproduct and
2646 // multiply that
2647 // // this avoids the undesired behaviour of shared binFactors getting all impacted by
2648 // mulitplies RooArgList all; auto new_p =
2649 // acquireNew<RooProduct>(TString::Format("%s_bin%d",binFactors->get()->GetName(),fBinNumber),TString::Format("binFactors
2650 // of bin %d",fBinNumber),RooArgList(*_bin->get<RooAbsArg>()));
2651 // new_p->setStringAttribute("alias","")
2652 // for (int i = 0; i < phf->_paramSet.getSize(); i++) {
2653 // if (i != fBinNumber - 1) all.add(*phf->_paramSet.at(i));
2654 // else all.add(*new_p);
2655 // }
2656 // phf->_paramSet.removeAll();
2657 // phf->_paramSet.add(all);
2658 // // now multiply that bin having converted it to RooProduct
2659 // return binFactors->bins().at(fBinNumber - 1)->Multiply(child,opt);
2660 // }
2661 }
2662 return xRooNode(*o, binFactors);
2663 }
2664 } else if (!get() && fParent) {
2665 // try to 'create' object based on parentage
2666 // add child as a temporary child to help with decision making
2667 auto _ref = emplace_back(std::shared_ptr<xRooNode>(&const_cast<xRooNode &>(child), [](TObject *) {}));
2668 try {
2669 fComp = fParent->Add(*this, "+").fComp;
2670 } catch (...) {
2671 resize(size() - 1);
2672 std::rethrow_exception(std::current_exception());
2673 }
2674 resize(size() - 1); // remove the temporarily added node
2675 }
2676
2677 if (!child.get()) {
2678 TString sOpt(opt);
2679 sOpt.ToLower();
2680 if (auto o = getObject<RooAbsReal>(child.GetName())) {
2681 auto out = Multiply(xRooNode(o, child.fParent));
2682 // have to protect bin case where get() is null (could change but then must change logic above too)
2683 if (get())
2684 Info("Multiply", "Scaled %s by existing factor %s::%s",
2685 mainChild().get() ? mainChild().get()->GetName() : get()->GetName(), o->ClassName(), o->GetName());
2686 return out;
2687 } else if (sOpt == "norm") {
2688 if (TString(child.GetName()).Contains("[") && ws()) {
2689 // assume factory method wanted
2690 auto arg = ws()->factory(child.GetName());
2691 if (arg) {
2692 auto out = Multiply(*arg);
2693 if (get())
2694 Info("Multiply", "Scaled %s by new norm factor %s",
2695 mainChild().get() ? mainChild().get()->GetName() : get()->GetName(), out->GetName());
2696 return out;
2697 }
2698 throw std::runtime_error(TString::Format("Failed to create new normFactor %s", child.GetName()));
2699 }
2700 auto out = Multiply(RooRealVar(child.GetName(), child.GetTitle(), 1, -1e-5, 100));
2701 if (get())
2702 Info("Multiply", "Scaled %s by new norm factor %s",
2703 mainChild().get() ? mainChild().get()->GetName() : get()->GetName(), out->GetName());
2704 return out;
2705 } else if (sOpt == "shape" || sOpt == "histo" || sOpt == "blankshape") {
2706 // needs axis defined
2707 if (auto ax = GetXaxis(); ax) {
2708 auto h = std::shared_ptr<TH1>(BuildHistogram(dynamic_cast<RooAbsLValue *>(ax->GetParent()), true));
2709 h->Reset();
2710 for (int i = 1; i <= h->GetNbinsX(); i++) {
2711 h->SetBinContent(i, 1);
2712 }
2713 h->SetMinimum(0);
2714 h->SetMaximum(100);
2715 h->SetName(TString::Format(";%s", child.GetName())); // ; char indicates don't "rename" this thing
2716 h->SetTitle(child.GetTitle());
2717 if (sOpt.Contains("shape"))
2718 h->SetOption(sOpt);
2719 auto out = Multiply(*h);
2720 if (get())
2721 Info("Multiply", "Scaled %s by new %s factor %s",
2722 mainChild().get() ? mainChild().get()->GetName() : get()->GetName(), sOpt.Data(), out->GetName());
2723 return out;
2724 }
2725 } else if (sOpt == "overall") {
2726 auto out = Multiply(acquireNew<RooStats::HistFactory::FlexibleInterpVar>(
2727 child.GetName(), child.GetTitle(), RooArgList(), 1, std::vector<double>(), std::vector<double>()));
2728 if (get() /* can happen this is null if on a bin node with no shapeFactors*/)
2729 Info("Multiply", "Scaled %s by new overall factor %s",
2730 mainChild().get() ? mainChild().get()->GetName() : get()->GetName(), out->GetName());
2731 return out;
2732 } else if (sOpt == "func" && ws()) {
2733 // need to get way to get dependencies .. can't pass all as causes circular dependencies issues.
2734 if (auto arg = ws()->factory(TString("expr::") + child.GetName())) {
2735 auto out = Multiply(*arg);
2736 if (get() /* can happen this is null if on a bin node with no shapeFactors*/)
2737 Info("Multiply", "Scaled %s by new func factor %s",
2738 mainChild().get() ? mainChild().get()->GetName() : get()->GetName(), out->GetName());
2739 return out;
2740 }
2741 }
2742 }
2743 if (auto h = child.get<TH1>(); h && strlen(h->GetOption()) == 0 && strlen(opt) > 0) {
2744 // put the option in the hist
2745 h->SetOption(opt);
2746 }
2747 if (auto w = get<RooWorkspace>(); w) {
2748 // just acquire
2749 std::shared_ptr<TObject> out;
2750 child.convertForAcquisition(*this);
2751 if (child.get<RooAbsReal>())
2752 out = acquire(child.fComp);
2753 return out;
2754 }
2755
2756 if (strcmp(GetName(), ".coef") == 0) { // covers both .coef and .coefs
2757 // need to add this into the relevant coef ... if its not a RooProduct, replace it with one first
2758 if (auto p = fParent->fParent->get<RooAddPdf>()) {
2759 for (size_t i = 0; i < p->pdfList().size(); i++) {
2760 if (p->pdfList().at(i) == fParent->get<RooAbsArg>()) {
2761 auto coefs = p->coefList().at(i);
2762 if (!coefs->InheritsFrom("RooProduct")) {
2763 RooArgList oldCoef;
2764 if (!(strcmp(coefs->GetName(), "1") == 0 || strcmp(coefs->GetName(), "ONE") == 0))
2765 oldCoef.add(*coefs);
2766 auto newCoefs = fParent->acquireNew<RooProduct>(
2767 TString::Format("coefs_%s", fParent->GetName()),
2768 TString::Format("coefficients for %s", fParent->GetName()), oldCoef);
2769 RooArgList oldCoefs;
2770 for (size_t j = 0; j < p->coefList().size(); j++) {
2771 if (i == j)
2772 oldCoefs.add(*newCoefs);
2773 else
2774 oldCoefs.add(*p->coefList().at(j));
2775 }
2776 const_cast<RooArgList &>(p->coefList()).removeAll();
2777 const_cast<RooArgList &>(p->coefList()).add(oldCoefs);
2778 coefs = newCoefs.get();
2779 }
2780 return xRooNode(*coefs, fParent).Multiply(child);
2781 }
2782 }
2783 }
2784 throw std::runtime_error("this coefs case is not supported");
2785 }
2786
2787 if (auto p = get<RooProduct>(); p) {
2788 std::shared_ptr<TObject> out;
2789 auto cc = child.fComp;
2790 bool isConverted = (child.convertForAcquisition(*this) != cc);
2791 if (child.get<RooAbsReal>())
2792 out = acquire(child.fComp);
2793
2794 // child may be a histfunc or a rooproduct of a histfunc and a paramhist if has stat errors
2795 if (auto _f = std::dynamic_pointer_cast<RooHistFunc>(
2796 (child.get<RooProduct>()) ? child.factors()[child.GetName()]->fComp : out);
2797 _f && _f->getAttribute("autodensity")) {
2798 // should we flag this as a density? yes if there's no other term marked as the density
2799 bool hasDensity = false;
2800 for (auto &f : factors()) {
2801 if (f->get<RooAbsArg>()->getAttribute("density")) {
2802 hasDensity = true;
2803 break;
2804 }
2805 }
2806 _f->setAttribute("density", !hasDensity && fParent && fParent->get<RooRealSumPdf>());
2807 if (_f->getAttribute("density")) {
2808
2809 // need to divide by bin widths first
2810 for (int i = 0; i < _f->dataHist().numEntries(); i++) {
2811 auto bin_pars = _f->dataHist().get(i);
2812 _f->dataHist().set(*bin_pars, _f->dataHist().weight() / _f->dataHist().binVolume(*bin_pars));
2813 }
2814 _f->setValueDirty();
2815
2816 // promote the axis vars to observables
2817 for (auto &x : xRooNode("tmp", _f).vars()) {
2818 x->get<RooAbsArg>()->setAttribute("obs");
2819 }
2820 }
2821 _f->setAttribute("autodensity", false);
2822 }
2823
2824 if (isConverted && child.get<RooHistFunc>()) {
2825 Info("Multiply", "Created %s factor %s in %s",
2826 child.get<RooAbsArg>()->getAttribute("density") ? "densityhisto" : "histo", child->GetName(),
2827 p->GetName());
2828 } else if (isConverted && child.get<ParamHistFunc>()) {
2829 Info("Multiply", "Created shape factor %s in %s", child->GetName(), p->GetName());
2830 }
2831
2832 if (auto _f = std::dynamic_pointer_cast<RooAbsReal>(out); _f) {
2833#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
2834 p->_compRSet.add(*_f);
2835#else
2836 const_cast<RooArgList &>(p->realComponents()).add(*_f);
2837#endif
2838 p->setValueDirty();
2839
2840 browse();
2841 xRooNode _out(_f, *this);
2842 for (auto &_par : _out.pars()) {
2843 if (auto s = _par->get<RooAbsArg>()->getStringAttribute("boundConstraint"); s) {
2844 bool found = false;
2845 for (auto &_constr : _par->constraints()) {
2846 if (strcmp(s, _constr->get()->GetName()) == 0) {
2847 // constraint is already included
2848 found = true;
2849 break;
2850 }
2851 }
2852 if (!found) {
2853 Info("Multiply", "Pulling in %s boundConstraint: %s", _par->GetName(), s);
2854 auto _pdf = getObject<RooAbsPdf>(s);
2855 if (!_pdf) {
2856 throw std::runtime_error("Couldn't find boundConstraint");
2857 }
2858 _par->Constrain(_pdf);
2859 }
2860 }
2861 }
2862 sterilize();
2863 return _out;
2864 }
2865 } else if (auto p2 = get<RooProdPdf>(); p2) {
2866
2867 std::shared_ptr<TObject> out;
2868 child.convertForAcquisition(*this);
2869 if (child.get<RooAbsPdf>())
2870 out = acquire(child.fComp);
2871 else if (child.get<RooAbsReal>() && mainChild().get<RooRealSumPdf>()) {
2872 // cannot multiply a RooProdPdf by a non pdf
2873 throw std::runtime_error(TString::Format("Cannot multiply %s by non-pdf %s", GetName(), child.GetName()));
2874 // return mainChild().Add(child); - nov 2022 - used to do this but now replaced with exception above
2875 } else if (!child.get() || child.get<RooAbsReal>()) {
2876 // need to create or hide inside a sumpdf or rooadpdf
2877 std::shared_ptr<RooAbsPdf> _pdf;
2878 if (!child.get() && strcmp(child.GetName(), "components") == 0) {
2879 auto _sumpdf = acquireNew<RooAddPdf>(Form("%s_%s", p2->GetName(), child.GetName()),
2880 (strlen(child.GetTitle()) && strcmp(child.GetTitle(), child.GetName()))
2881 ? child.GetTitle()
2882 : p2->GetTitle(),
2883 RooArgList(), RooArgList());
2884 _pdf = _sumpdf;
2885 } else {
2886 auto _sumpdf = acquireNew<RooRealSumPdf>(
2887 Form("%s_%s", p2->GetName(), child.GetName()),
2888 (strlen(child.GetTitle()) && strcmp(child.GetTitle(), child.GetName())) ? child.GetTitle()
2889 : p2->GetTitle(),
2890 RooArgList(), RooArgList(), true);
2891 _sumpdf->setFloor(true);
2892 _pdf = _sumpdf;
2893 }
2894 _pdf->setStringAttribute("alias", child.GetName());
2895 // transfer axis attributes if present (TODO: should GetXaxis look beyond the immediate parent?)
2896 _pdf->setStringAttribute("xvar", p2->getStringAttribute("xvar"));
2897 _pdf->setStringAttribute("binning", p2->getStringAttribute("binning"));
2898 out = _pdf;
2899 Info("Multiply", "Created %s::%s in channel %s", _pdf->ClassName(), _pdf->GetName(), p2->GetName());
2900 if (child.get<RooAbsReal>())
2901 xRooNode(*out, *this).Add(child);
2902 }
2903
2904 if (auto _pdf = std::dynamic_pointer_cast<RooAbsPdf>(out); _pdf) {
2905#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
2906 const_cast<RooArgList &>(p2->pdfList()).add(*_pdf);
2907#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 26, 00)
2908 p2->_pdfNSetList.emplace_back(std::make_unique<RooArgSet>("nset"));
2909#else
2910 p->_pdfNSetList.Add(new RooArgSet("nset"));
2911#endif
2912 if (!p2->canBeExtended() && _pdf->canBeExtended()) {
2913 p2->_extendedIndex = p2->_pdfList.size() - 1;
2914 }
2915#else
2916 p2->addPdfs(RooArgSet(*_pdf));
2917#endif
2918 sterilize();
2919 browse();
2920 return xRooNode(_pdf, *this);
2921 }
2922 } else if (auto p3 = get<RooRealSumPdf>(); p3) {
2923 // multiplying all current and future components
2924 std::shared_ptr<TObject> out;
2925 child.convertForAcquisition(*this);
2926 if (child.get<RooAbsReal>()) {
2927 out = acquire(child.fComp);
2928 for (auto &c : components()) {
2929 c->Multiply(out);
2930 }
2931 TString s = p3->getStringAttribute("global_factors");
2932 if (s != "")
2933 s += ";";
2934 s += out->GetName();
2935 p3->setStringAttribute("global_factors", s);
2936 Info(
2937 "Multiply",
2938 "Flagged %s as a global factor in channel %s (is applied to all current and future samples in the channel)",
2939 out->GetName(), p3->GetName());
2940 return xRooNode(out, *this);
2941 }
2942
2943 } else if (auto p4 = get<RooAbsPdf>(); p4 && !(fParent && fParent->get<RooRealSumPdf>())) {
2944 // multiply the coefs (if this isn't part of a RooAddPdf or RooRealSumPdf then we will eventually throw exception
2945 return coefs().Multiply(child);
2946 } else if (auto p5 = get<RooAbsReal>(); p5 && (!get<RooAbsPdf>() || (fParent && fParent->get<RooRealSumPdf>()))) {
2947 // replace this obj with a RooProduct to allow for multiplication
2948
2949 // get the list of clients BEFORE creating the new interpolation ... seems list of clients is inaccurate after
2950 std::set<RooAbsArg *> cl;
2951 for (auto &arg : p5->clients()) {
2952 cl.insert(arg);
2953 }
2954
2955 // if multiple clients, see if only one client is in parentage route
2956 // if so, then assume thats the only client we should replace in
2957 if (cl.size() > 1) {
2958 if (cl.count(fParent->get<RooAbsArg>()) > 0) {
2959 cl.clear();
2960 cl.insert(fParent->get<RooAbsArg>());
2961 } else {
2962 Warning("Multiply", "Scaling %s that has multiple clients", p5->GetName());
2963 }
2964 }
2965
2966 auto new_p = acquireNew<RooProduct>(TString::Format("prod_%s", p5->GetName()), p5->GetTitle(), RooArgList(*p5));
2967 // copy attributes over
2968 for (auto &a : p5->attributes())
2969 new_p->setAttribute(a.c_str());
2970 for (auto &a : p5->stringAttributes())
2971 new_p->setStringAttribute(a.first.c_str(), a.second.c_str());
2972 if (!new_p->getStringAttribute("alias"))
2973 new_p->setStringAttribute("alias", p5->GetName());
2974 auto old_p = p5;
2975 new_p->setAttribute(Form("ORIGNAME:%s", old_p->GetName())); // used in redirectServers to say what this replaces
2976 for (auto arg : cl) {
2977 arg->redirectServers(RooArgSet(*new_p), false, true);
2978 }
2979
2980 fComp = new_p;
2981 return Multiply(child);
2982 }
2983
2984 // before giving up here, assume user wanted a norm factor type if child is just a name
2985 if (!child.get() && strlen(opt) == 0)
2986 return Multiply(child, "norm");
2987
2988 throw std::runtime_error(
2989 TString::Format("Cannot multiply %s by %s%s", GetPath().c_str(), child.GetName(),
2990 (!child.get() && strlen(opt) == 0) ? " (forgot to specify factor type?)" : ""));
2991}
2992
2994{
2995
2996 auto p5 = get<RooAbsArg>();
2997 if (!p5) {
2998 throw std::runtime_error("Only replacement of RooAbsArg is supported");
2999 }
3000 node.convertForAcquisition(*this, "func");
3001
3002 auto new_p = node.get<RooAbsArg>();
3003 if (!new_p) {
3004 throw std::runtime_error(TString::Format("Cannot replace with %s", node.GetName()));
3005 }
3006 auto out = acquire(node.fComp);
3007 new_p = std::dynamic_pointer_cast<RooAbsArg>(out).get();
3008
3009 std::set<RooAbsArg *> cl;
3010 for (auto &arg : p5->clients()) {
3011 if (arg == new_p)
3012 continue; // do not replace in self ... although redirectServers will prevent that anyway
3013 cl.insert(arg);
3014 }
3015
3016 // if multiple clients, see if only one client is in parentage route
3017 // if so, then assume thats the only client we should replace in
3018 if (cl.size() > 1) {
3019 if (fParent && fParent->get<RooAbsArg>() && cl.count(fParent->get<RooAbsArg>()) > 0) {
3020 cl.clear();
3021 cl.insert(fParent->get<RooAbsArg>());
3022 } else {
3023 std::stringstream clientList;
3024 for (auto c : cl)
3025 clientList << c->GetName() << ",";
3026 Warning("Replace", "Replacing %s in all clients: %s", p5->GetName(), clientList.str().c_str());
3027 }
3028 }
3029
3030 new_p->setAttribute(Form("ORIGNAME:%s", p5->GetName())); // used in redirectServers to say what this replaces
3031 for (auto arg : cl) {
3032 // if RooFormulaVar need to ensure the internal formula has been "constructed" otherwise will try to construct
3033 // it from the original expression that may have old parameter in it.
3034 if (auto p = dynamic_cast<RooFormulaVar *>(arg))
3035 p->ok(); // triggers creation of RooFormula
3036 arg->redirectServers(RooArgSet(*new_p), false, true);
3037 }
3038 return node;
3039}
3040
3042{
3043
3044 class AutoUpdater {
3045 public:
3046 AutoUpdater(xRooNode &_n) : n(_n) {}
3047 ~AutoUpdater() { n.browse(); }
3048 xRooNode &n;
3049 };
3050 AutoUpdater xxx(*this);
3051
3052 if (!get() && fParent) {
3053 // try to 'create' object based on parentage
3054 // add child as a temporary child to help with decision making
3055 auto _ref = emplace_back(std::shared_ptr<xRooNode>(&const_cast<xRooNode &>(child), [](TObject *) {}));
3056 try {
3057 fComp = fParent->Add(*this, "+").fComp;
3058 } catch (...) {
3059 resize(size() - 1);
3060 std::rethrow_exception(std::current_exception());
3061 }
3062 resize(size() - 1); // remove the temporarily added node
3063 }
3064
3065 if (auto p = mainChild(); p) {
3066 // variations applied to the main child if has one
3067 return p.Vary(child);
3068 }
3069
3070 if (auto s = get<RooSimultaneous>(); s && s->indexCat().IsA() == RooCategory::Class()) {
3071 // name is used as cat label
3072 std::string label = child.GetName();
3073 if (auto pos = label.find("="); pos != std::string::npos)
3074 label = label.substr(pos + 1);
3075 if (!s->indexCat().hasLabel(label)) {
3076 static_cast<RooCategory &>(const_cast<RooAbsCategoryLValue &>(s->indexCat())).defineType(label.c_str());
3077 }
3078 std::shared_ptr<TObject> out;
3079 child.convertForAcquisition(*this);
3080 if (child.get<RooAbsPdf>())
3081 out = acquire(child.fComp); // may create a channel from a histogram
3082 else if (!child.fComp) {
3083 out = acquireNew<RooProdPdf>(TString::Format("%s_%s", s->GetName(), label.c_str()),
3084 (strlen(child.GetTitle())) ? child.GetTitle() : label.c_str(), RooArgList());
3085 Info("Vary", "Created channel RooProdPdf::%s in model %s", out->GetName(), s->GetName());
3086 }
3087
3088 if (auto _pdf = std::dynamic_pointer_cast<RooAbsPdf>(out); _pdf) {
3089 s->addPdf(*_pdf, label.c_str());
3090 sterilize();
3091 // clear children for reload and update shared axis
3092 clear();
3093 fXAxis.reset();
3094 browse();
3095 return xRooNode(TString::Format("%s=%s", s->indexCat().GetName(), label.data()), _pdf, *this);
3096 }
3097
3098 } else if (auto p = get<RooStats::HistFactory::FlexibleInterpVar>(); p) {
3099
3100 // child needs to be a constvar ...
3101 child.convertForAcquisition(*this);
3102 auto _c = child.get<RooConstVar>();
3103 if (!_c && child.get()) {
3104 throw std::runtime_error("Only pure consts can be set as variations of a flexible interpvar");
3105 }
3106#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
3107 double value = (_c ? _c->getVal() : p->_nominal);
3108 double nomVal = p->_nominal;
3109#else
3110 double value = (_c ? _c->getVal() : p->nominal());
3111 double nomVal = p->nominal();
3112#endif
3113
3114 TString cName(child.GetName());
3115 if (cName == "nominal") {
3116 p->setNominal(value);
3117 return *(this->variations().at(cName.Data()));
3118 }
3119 if (cName.CountChar('=') != 1) {
3120 throw std::runtime_error("unsupported variation form");
3121 }
3122 std::string parName = cName(0, cName.Index('='));
3123 double parVal = TString(cName(cName.Index('=') + 1, cName.Length())).Atof();
3124 if (parVal != 1 && parVal != -1) {
3125 throw std::runtime_error("unsupported variation magnitude");
3126 }
3127 bool high = parVal > 0;
3128
3129 if (parName.empty()) {
3130 p->setNominal(value);
3131 } else {
3132 auto v = fParent->getObject<RooRealVar>(parName);
3133 if (!v)
3134 v = fParent->acquire<RooRealVar>(parName.c_str(), parName.c_str(), -5, 5);
3135 if (!v->hasError())
3136 v->setError(1);
3137
3138 if (!p->findServer(*v)) {
3139#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
3140 p->_paramList.add(*v);
3141 p->_low.push_back(0);
3142 p->_high.push_back(0);
3143 p->_interpCode.push_back(4);
3144#else
3145 const_cast<RooListProxy &>(p->variables()).add(*v);
3146 const_cast<std::vector<double> &>(p->low()).push_back(0);
3147 const_cast<std::vector<double> &>(p->high()).push_back(0);
3148 const_cast<std::vector<int> &>(p->interpolationCodes()).push_back(4);
3149#endif
3150 v->setAttribute(Form("SYMMETRIC%s_%s", high ? "+" : "-", GetName())); // flag for symmetrized
3151 }
3152
3153 if (high) {
3154 p->setHigh(*v, value);
3155 if (v->getAttribute(Form("SYMMETRIC+_%s", GetName()))) {
3156 p->setLow(*v, 2 * nomVal - value);
3157 }
3158 v->setAttribute(Form("SYMMETRIC-_%s", GetName()), false);
3159 } else {
3160 p->setLow(*v, value);
3161 if (v->getAttribute(Form("SYMMETRIC-_%s", GetName()))) {
3162 p->setHigh(*v, 2 * nomVal - value);
3163 }
3164 v->setAttribute(Form("SYMMETRIC+_%s", GetName()), false);
3165 }
3166
3167 /*if (!unconstrained && fParent->pars()[v->GetName()].constraints().empty()) {
3168 fParent->pars()[v->GetName()].constraints().add("normal");
3169 }*/
3170 }
3171 return *(this->variations().at(cName.Data()));
3172 } else if (auto p2 = get<PiecewiseInterpolation>(); p2) {
3173 TString cName(child.GetName());
3174 if (cName.CountChar('=') != 1) {
3175 throw std::runtime_error("unsupported variation form");
3176 }
3177 TString parName = cName(0, cName.Index('='));
3178 double parVal = TString(cName(cName.Index('=') + 1, cName.Length())).Atof();
3179 if (parVal != 1 && parVal != -1) {
3180 throw std::runtime_error("unsupported variation magnitude");
3181 }
3182#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
3183 RooHistFunc *f = dynamic_cast<RooHistFunc *>(p2->_nominal.absArg());
3184 if (!f) {
3185 throw std::runtime_error(
3186 TString::Format("Interpolating %s instead of RooHistFunc", p2->_nominal.absArg()->ClassName()));
3187 }
3188#else
3189 RooHistFunc *f = dynamic_cast<RooHistFunc *>(const_cast<RooAbsReal *>(p2->nominalHist()));
3190 if (!f) {
3191 throw std::runtime_error(
3192 TString::Format("Interpolating %s instead of RooHistFunc", p2->nominalHist()->ClassName()));
3193 }
3194#endif
3195 RooHistFunc *nomf = f;
3196 RooHistFunc *otherf = nullptr;
3197 size_t i = 0;
3198 for (auto par : p2->paramList()) {
3199 if (parName == par->GetName()) {
3200 f = dynamic_cast<RooHistFunc *>((parVal > 0 ? p2->highList() : p2->lowList()).at(i));
3201 otherf = dynamic_cast<RooHistFunc *>((parVal > 0 ? p2->lowList() : p2->highList()).at(i));
3202 break;
3203 }
3204 i++;
3205 }
3206 if (i == p2->paramList().size() && !child.get<RooAbsReal>()) {
3207
3208 // need to add the parameter
3209 auto v = acquire<RooRealVar>(parName, parName, -5, 5);
3210 if (!v->hasError())
3211 v->setError(1);
3212
3213 std::shared_ptr<RooHistFunc> up(
3214 static_cast<RooHistFunc *>(f->Clone(Form("%s_%s_up", f->GetName(), parName.Data()))));
3215 std::shared_ptr<RooHistFunc> down(
3216 static_cast<RooHistFunc *>(f->Clone(Form("%s_%s_down", f->GetName(), parName.Data()))));
3217 // RooHistFunc doesn't clone it's data hist ... do it ourself (will be cloned again if imported into a ws)
3218#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
3219 std::unique_ptr<RooDataHist> h1(
3220 static_cast<RooDataHist *>(f->dataHist().Clone(Form("hist_%s", up->GetName()))));
3221 std::unique_ptr<RooDataHist> h2(
3222 static_cast<RooDataHist *>(f->dataHist().Clone(Form("hist_%s", down->GetName()))));
3223 up->_dataHist = dynamic_cast<RooDataHist *>(f->dataHist().Clone(Form("hist_%s", up->GetName())));
3224 down->_dataHist = dynamic_cast<RooDataHist *>(f->dataHist().Clone(Form("hist_%s", down->GetName())));
3225#else
3226 up->cloneAndOwnDataHist(TString::Format("hist_%s", up->GetName()));
3227 down->cloneAndOwnDataHist(TString::Format("hist_%s", down->GetName()));
3228#endif
3229 auto ups = std::dynamic_pointer_cast<RooHistFunc>(acquire(up, false, true));
3230 auto downs = std::dynamic_pointer_cast<RooHistFunc>(acquire(down, false, true));
3231#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
3232 p2->_highSet.add(*ups.get());
3233 p2->_lowSet.add(*downs.get());
3234 p2->_interpCode.push_back(4);
3235 p2->_paramSet.add(*v);
3236#else
3237 const_cast<RooArgList &>(p2->highList()).add(*ups.get());
3238 const_cast<RooArgList &>(p2->lowList()).add(*downs.get());
3239 const_cast<std::vector<int> &>(p2->interpolationCodes()).push_back(4);
3240 const_cast<RooArgList &>(p2->paramList()).add(*v);
3241#endif
3242 p2->setValueDirty();
3243 f = ((parVal > 0) ? ups : downs).get();
3244 otherf = ((parVal > 0) ? downs : ups).get();
3245 // start off with everything being symmetric
3246 f->setStringAttribute("symmetrizes", otherf->GetName());
3247 f->setStringAttribute("symmetrize_nominal", nomf->GetName());
3248 otherf->setStringAttribute("symmetrized_by", f->GetName());
3249
3250 // constrain par if required
3251 /*if (!unconstrained && fParent->pars()[v->GetName()].constraints().empty()) {
3252 fParent->pars()[v->GetName()].constraints().add("normal");
3253 }*/
3254 }
3255
3256 // child.convertForAcquisition(*this);
3257 if (f) {
3258 if (child.get())
3259 xRooNode("tmp", *f, *this) = *child.get();
3260 f->setValueDirty();
3261 xRooNode out(*f, *this);
3262 out.sterilize();
3263 return out;
3264 }
3265
3266 } else if (auto p3 = get<RooConstVar>(); p3) {
3267
3268 // never vary the universal consts ... its too dangerous
3269 if (p3->getAttribute("RooRealConstant_Factory_Object")) {
3270 throw std::runtime_error("Cannot vary pure constants");
3271 }
3272
3273 // inject a FlexibleInterpVar ...
3274
3275 // get the list of clients BEFORE creating the new interpolation ... seems list of clients is inaccurate after
3276 std::set<RooAbsArg *> cl;
3277 for (auto &arg : p3->clients()) {
3278 cl.insert(arg);
3279 }
3280 // if multiple clients, see if only one client is in parentage route
3281 // if so, then assume thats the only client we should replace in
3282 if (cl.size() > 1) {
3283 if (cl.count(fParent->get<RooAbsArg>()) > 0) {
3284 cl.clear();
3285 cl.insert(fParent->get<RooAbsArg>());
3286 } else {
3287 Warning("Vary", "Varying %s that has multiple clients", p3->GetName());
3288 }
3289 }
3290 p3->setStringAttribute("origName", p3->GetName());
3291 TString n = p3->GetName();
3292 p3->SetName(Form("%s_nominal", p3->GetName())); // if problems should perhaps not rename here
3293
3294 auto new_p = acquireNew<RooStats::HistFactory::FlexibleInterpVar>(n, p3->GetTitle(), RooArgList(), p3->getVal(),
3295 std::vector<double>(), std::vector<double>());
3296
3297 // copy attributes over
3298 for (auto &a : p3->attributes())
3299 new_p->setAttribute(a.c_str());
3300 for (auto &a : p3->stringAttributes())
3301 new_p->setStringAttribute(a.first.c_str(), a.second.c_str());
3302 // if (!new_p->getStringAttribute("alias")) new_p->setStringAttribute("alias",p->GetName());
3303 auto old_p = p3;
3304 new_p->setAttribute(Form("ORIGNAME:%s", old_p->GetName())); // used in redirectServers to say what this replaces
3305 for (auto arg : cl) {
3306 arg->redirectServers(RooArgSet(*new_p), false, true);
3307 }
3308
3309 fComp = new_p;
3310 return Vary(child);
3311
3312 } else if (auto p4 = get<RooAbsReal>(); p4) {
3313 // inject an interpolation node
3314
3315 // get the list of clients BEFORE creating the new interpolation ... seems list of clients is inaccurate after
3316 std::set<RooAbsArg *> cl;
3317 for (auto &arg : p4->clients()) {
3318 cl.insert(arg);
3319 }
3320 // if multiple clients, see if only one client is in parentage route
3321 // if so, then assume thats the only client we should replace in
3322 if (cl.size() > 1) {
3323 if (cl.count(fParent->get<RooAbsArg>()) > 0) {
3324 cl.clear();
3325 cl.insert(fParent->get<RooAbsArg>());
3326 } else {
3327 Warning("Vary", "Varying %s that has multiple clients", p4->GetName());
3328 }
3329 }
3330 p4->setStringAttribute("origName", p4->GetName());
3331 TString n = p4->GetName();
3332 p4->SetName(Form("%s_nominal", p4->GetName())); // if problems should perhaps not rename here
3333
3334 auto new_p = acquireNew<PiecewiseInterpolation>(n, p4->GetTitle(), *p4, RooArgList(), RooArgList(), RooArgList());
3335
3336 // copy attributes over
3337 for (auto &a : p4->attributes())
3338 new_p->setAttribute(a.c_str());
3339 for (auto &a : p4->stringAttributes())
3340 new_p->setStringAttribute(a.first.c_str(), a.second.c_str());
3341 // if (!new_p->getStringAttribute("alias")) new_p->setStringAttribute("alias",p->GetName());
3342 auto old_p = p4;
3343 new_p->setAttribute(Form("ORIGNAME:%s", old_p->GetName())); // used in redirectServers to say what this replaces
3344 for (auto arg : cl) {
3345 arg->redirectServers(RooArgSet(*new_p), false, true);
3346 }
3347
3348 fComp = new_p;
3349 return Vary(child);
3350 }
3351
3352 Print();
3353 throw std::runtime_error(TString::Format("Cannot vary %s with %s", GetName(), child.GetName()));
3354}
3355
3357{
3359}
3360
3361bool xRooNode::SetContent(double value, const char *par, double val)
3362{
3363 return SetContents(RooConstVar(GetName(), GetTitle(), value), par, val);
3364}
3365
3368 {
3369 if (x && b)
3370 x->setBinning(*b);
3371 if (b)
3372 delete b;
3373 }
3374 RooRealVar *x = nullptr;
3375 RooAbsBinning *b = nullptr;
3376};
3377
3379{
3380
3381 if (!get()) {
3382 fComp = std::shared_ptr<TObject>(const_cast<TObject *>(&o), [](TObject *) {});
3383 if (fParent && !fParent->find(GetName())) {
3384 // either a temporary or a placeholder so need to try genuinely adding
3385 fComp = fParent->Add(*this, "+").fComp;
3386 if (auto a = get<RooAbsArg>(); a && strcmp(a->GetName(), GetName()) && !a->getStringAttribute("alias")) {
3387 a->setStringAttribute("alias", GetName());
3388 }
3389 if (!fComp)
3390 throw std::runtime_error("Cannot determine type");
3391 return *this;
3392 }
3393 }
3394
3395 if (auto h = dynamic_cast<const TH1 *>(&o); h) {
3396 /*auto f = get<RooHistFunc>();
3397 if (!f) {
3398 // if it's a RooProduct locate child with the same name
3399 if (get<RooProduct>()) {
3400 f = factors()[GetName()]->get<RooHistFunc>();
3401 }
3402
3403
3404
3405 }*/
3406 bool _isData = get<RooAbsData>();
3407 BinningRestorer _b;
3408 if (_isData) {
3409 // need to ensure x-axis matches this h
3410 auto ax = GetXaxis();
3411 if (!ax)
3412 throw std::runtime_error("no xaxis");
3413 auto _v = dynamic_cast<RooRealVar *>(ax->GetParent());
3414 if (_v) {
3415 _b.x = _v;
3416 _b.b = dynamic_cast<RooAbsBinning *>(_v->getBinningPtr(0)->Clone());
3417 if (h->GetXaxis()->IsVariableBinSize()) {
3418 _v->setBinning(RooBinning(h->GetNbinsX(), h->GetXaxis()->GetXbins()->GetArray()));
3419 } else {
3420 _v->setBinning(RooUniformBinning(h->GetXaxis()->GetXmin(), h->GetXaxis()->GetXmax(), h->GetNbinsX()));
3421 }
3422 }
3423 }
3424
3425 if (true) {
3426 for (int bin = 1; bin <= h->GetNbinsX(); bin++) {
3427 SetBinContent(bin, h->GetBinContent(bin));
3428 /*double value = h->GetBinContent(bin);
3429 auto bin_pars = f->dataHist().get(bin - 1);
3430 if (f->getAttribute("density")) {
3431 value /= f->dataHist().binVolume(*bin_pars);
3432 }
3433 f->dataHist().set(*bin_pars, value);*/
3434 if (!_isData && h->GetSumw2N() && !SetBinError(bin, h->GetBinError(bin)))
3435 throw std::runtime_error("Failed setting stat error");
3436 }
3437 return *this;
3438 }
3439 } else if (auto _c = dynamic_cast<const RooConstVar *>(&o); _c) {
3440
3441 if (auto a = get<RooAbsArg>();
3442 (a && a->isFundamental()) || get<RooConstVar>() || get<RooStats::HistFactory::FlexibleInterpVar>()) {
3443 SetBinContent(1, _c->getVal());
3444 return *this;
3445 } else if (get<RooAbsData>()) { // try to do assignment to a dataset (usually setting a bin content)
3446 SetBinContent(0, _c->getVal());
3447 return *this;
3448 }
3449 }
3450
3451 throw std::runtime_error("Assignment failed");
3452
3453 /*
3454
3455 if (fParent && !fParent->mk()) {
3456 throw std::runtime_error("mk failure");
3457 }
3458
3459 if (fComp) return *this;
3460
3461 if (o.InheritsFrom("RooAbsArg")) {
3462 fComp = acquire(std::shared_ptr<TObject>(const_cast<TObject*>(&o),[](TObject* o){}));
3463 std::dynamic_pointer_cast<RooAbsArg>(fComp)->setStringAttribute("alias",GetName());
3464 }
3465
3466 if (fComp && fParent) {
3467 fParent->incorporate(fComp);
3468 }
3469
3470
3471 return *this;
3472 */
3473}
3474
3475void xRooNode::_fit_(const char *constParValues)
3476{
3477 try {
3478 auto _pars = pars();
3479 // std::unique_ptr<RooAbsCollection> snap(_pars.argList().snapshot());
3480 TStringToken pattern(constParValues, ",");
3481 while (pattern.NextToken()) {
3482 auto idx = pattern.Index('=');
3483 TString pat = (idx == -1) ? TString(pattern) : TString(pattern(0, idx));
3484 double val =
3485 (idx == -1) ? std::numeric_limits<double>::quiet_NaN() : TString(pattern(idx + 1, pattern.Length())).Atof();
3486 for (auto p : _pars.argList()) {
3487 if (TString(p->GetName()).Contains(TRegexp(pat, true))) {
3488 p->setAttribute("Constant", true);
3489 if (!std::isnan(val)) {
3490 dynamic_cast<RooAbsRealLValue *>(p)->setVal(val);
3491 }
3492 }
3493 }
3494 }
3495 // use the first selected dataset
3496 auto _dsets = datasets();
3497 TString dsetName = "";
3498 for (auto &d : _dsets) {
3499 if (d->get()->TestBit(1 << 20)) {
3500 dsetName = d->get()->GetName();
3501 break;
3502 }
3503 }
3504 auto _nll = nll(dsetName.Data());
3505 _nll.fitConfigOptions()->SetValue("LogSize", 65536);
3506 _nll.fitConfig()->MinimizerOptions().SetPrintLevel(0);
3507 auto fr = _nll.minimize();
3508 //_pars.argList() = *snap; // restore values - irrelevant as SetFitResult will restore values
3509 if (!fr.get())
3510 throw std::runtime_error("Fit Failed");
3511 SetFitResult(fr.get());
3512 TString statusCodes;
3513 for (unsigned int i = 0; i < fr->numStatusHistory(); i++) {
3514 statusCodes += TString::Format("\n%s = %d", fr->statusLabelHistory(i), fr->statusCodeHistory(i));
3515 }
3516 const TGWindow *w =
3517 (gROOT->GetListOfBrowsers()->At(0))
3518 ? dynamic_cast<TGWindow *>(static_cast<TBrowser *>(gROOT->GetListOfBrowsers()->At(0))->GetBrowserImp())
3519 : gClient->GetRoot();
3520 if (fr->status() != 0) {
3521 new TGMsgBox(gClient->GetRoot(), w, "Fit Finished with Bad Status Code",
3522 TString::Format("%s\nData = %s\nFit Status Code = %d\nCov Quality = %d\n-------------%s",
3523 fr->GetName(), dsetName.Data(), fr->status(), fr->covQual(), statusCodes.Data()),
3525 } else if (fr->covQual() != 3 && _nll.fitConfig()->ParabErrors()) {
3526 new TGMsgBox(gClient->GetRoot(), w, "Fit Finished with Bad Covariance Quality",
3527 TString::Format("%s\nData = %s\nFit Status Code = %d\nCov Quality = %d\n-------------%s",
3528 fr->GetName(), dsetName.Data(), fr->status(), fr->covQual(), statusCodes.Data()),
3530 } else {
3531 new TGMsgBox(gClient->GetRoot(), w, "Fit Finished Successfully",
3532 TString::Format("%s\nData = %s\nFit Status Code = %d\nCov Quality = %d\n-------------%s",
3533 fr->GetName(), dsetName.Data(), fr->status(), fr->covQual(), statusCodes.Data()));
3534 }
3535 } catch (const std::exception &e) {
3536 new TGMsgBox(
3537 gClient->GetRoot(),
3538 (gROOT->GetListOfBrowsers()->At(0))
3539 ? dynamic_cast<TGWindow *>(static_cast<TBrowser *>(gROOT->GetListOfBrowsers()->At(0))->GetBrowserImp())
3540 : gClient->GetRoot(),
3541 "Exception", e.what(), kMBIconExclamation, kMBOk); // deletes self on dismiss?
3542 }
3543}
3544
3545void xRooNode::_generate_(const char *datasetName, bool expected)
3546{
3547 try {
3548 datasets().Add(datasetName, expected ? "asimov" : "toy");
3549 } catch (const std::exception &e) {
3550 new TGMsgBox(
3551 gClient->GetRoot(),
3552 (gROOT->GetListOfBrowsers()->At(0))
3553 ? dynamic_cast<TGWindow *>(static_cast<TBrowser *>(gROOT->GetListOfBrowsers()->At(0))->GetBrowserImp())
3554 : gClient->GetRoot(),
3555 "Exception", e.what(),
3556 kMBIconExclamation); // deletes self on dismiss?
3557 }
3558}
3559
3560void xRooNode::_scan_(const char *what, double nToys, const char *xvar, int nBinsX, double lowX,
3561 double highX /*, const char*, int, double, double*/, const char *constParValues)
3562{
3563 try {
3564 TString sXvar(xvar);
3565 TString sWhat(what);
3566
3567 // use the first selected dataset
3568 auto _dsets = datasets();
3569 TString dsetName = "";
3570 for (auto &d : _dsets) {
3571 if (d->get()->TestBit(1 << 20)) {
3572 dsetName = d->get()->GetName();
3573 break;
3574 }
3575 }
3576 auto _pars = pars();
3577 std::unique_ptr<RooAbsCollection> snap(_pars.argList().snapshot());
3578 TStringToken pattern(constParValues, ",");
3579 while (pattern.NextToken()) {
3580 auto idx = pattern.Index('=');
3581 TString pat = (idx == -1) ? TString(pattern) : TString(pattern(0, idx));
3582 double val =
3583 (idx == -1) ? std::numeric_limits<double>::quiet_NaN() : TString(pattern(idx + 1, pattern.Length())).Atof();
3584 for (auto par : _pars.argList()) {
3585 if (TString(par->GetName()).Contains(TRegexp(pat, true))) {
3586 par->setAttribute("Constant", true);
3587 if (!std::isnan(val)) {
3588 dynamic_cast<RooAbsRealLValue *>(par)->setVal(val);
3589 }
3590 }
3591 }
3592 }
3593 auto hs = nll(dsetName.Data()).hypoSpace(sXvar);
3594 if (nToys) {
3595 sWhat += " toys";
3596 if (nToys > 0) {
3597 sWhat += TString::Format("=%g", nToys);
3598 }
3599 }
3600 hs.SetTitle(sWhat + " scan" + ((dsetName != "") ? TString::Format(" [data=%s]", dsetName.Data()) : ""));
3601 int scanStatus = hs.scan(sWhat + " visualize", nBinsX, lowX, highX);
3602 if (scanStatus != 0) {
3603 new TGMsgBox(
3604 gClient->GetRoot(),
3605 (gROOT->GetListOfBrowsers()->At(0))
3606 ? dynamic_cast<TGWindow *>(static_cast<TBrowser *>(gROOT->GetListOfBrowsers()->At(0))->GetBrowserImp())
3607 : gClient->GetRoot(),
3608 "Scan Finished with Bad Status Code",
3609 TString::Format("%s\nData = %s\nScan Status Code = %d", hs.GetName(), dsetName.Data(), scanStatus),
3611 }
3612 hs.SetName(TUUID().AsString());
3613 if (ws()) {
3614 if (auto res = hs.result())
3615 ws()->import(*res);
3616 }
3617
3618 _pars.argList() = *snap; // restore pars
3619
3620 } catch (const std::exception &e) {
3621 new TGMsgBox(
3622 gClient->GetRoot(),
3623 (gROOT->GetListOfBrowsers()->At(0))
3624 ? dynamic_cast<TGWindow *>(static_cast<TBrowser *>(gROOT->GetListOfBrowsers()->At(0))->GetBrowserImp())
3625 : gClient->GetRoot(),
3626 "Exception", e.what(), kMBIconExclamation);
3627 }
3628}
3629
3630void xRooNode::_SetBinContent_(int bin, double value, const char *par, double parVal)
3631{
3632 try {
3633 SetBinContent(bin, value, strlen(par) > 0 ? par : nullptr, parVal);
3634 } catch (const std::exception &e) {
3635 new TGMsgBox(gClient->GetRoot(), gClient->GetRoot(), "Exception", e.what(),
3636 kMBIconExclamation); // deletes self on dismiss?
3637 }
3638}
3639
3641{
3642 try {
3643 if (!SetContent(value))
3644 throw std::runtime_error("Failed to SetContent");
3645 } catch (const std::exception &e) {
3646 new TGMsgBox(gClient->GetRoot(), gClient->GetRoot(), "Exception", e.what(),
3647 kMBIconExclamation); // deletes self on dismiss?
3648 }
3649}
3650
3651bool xRooNode::SetBinContent(int bin, double value, const char *par, double parVal)
3652{
3653
3654 // create if needed
3655 if (!get()) {
3656 if (fParent && !find(GetName())) {
3657 // if have a binning we create a histogram to match it
3658 if (auto ax = GetXaxis(); ax) {
3659 std::shared_ptr<TH1D> h;
3660 auto _b = dynamic_cast<Axis2 *>(ax)->binning();
3661 auto t = TH1::AddDirectoryStatus();
3662 TH1::AddDirectory(false);
3663 if (_b->isUniform()) {
3664 h.reset(new TH1D(GetName(), GetTitle(), _b->numBins(), _b->lowBound(), _b->highBound()));
3665 } else {
3666 h.reset(new TH1D(GetName(), GetTitle(), _b->numBins(), _b->array()));
3667 }
3668 h->SetDirectory(0);
3670 h->GetXaxis()->SetName(TString::Format("%s;%s", ax->GetParent()->GetName(), ax->GetName()));
3671 fComp = h;
3672 }
3673 fComp = fParent->Add(*this, "sample").fComp;
3674 }
3675 }
3676
3677 // if it's a RooProduct locate child with the same name
3678 if (get<RooProduct>()) {
3679 return factors()[GetName()]->SetBinContent(bin, value, par, parVal);
3680 }
3681
3682 if (get<RooAbsData>()) {
3683 if (auto _data = get<RooDataSet>(); _data) {
3684 auto _ax = (bin) ? GetXaxis() : nullptr;
3685 if (!_ax && bin) {
3686 throw std::runtime_error("Cannot determine binning to fill data");
3687 }
3688 if (_ax && _ax->GetNbins() < bin)
3689 throw std::out_of_range(TString::Format("%s range %s only has %d bins", _ax->GetParent()->GetName(),
3690 _ax->GetName(), _ax->GetNbins()));
3691 RooArgSet obs;
3692
3693 TString cut = "";
3694
3695 for (auto _c : coords()) { // coords() moves vars to their respective coordinates too
3696 if (auto _cat = _c->get<RooAbsCategoryLValue>(); _cat) {
3697 if (cut != "")
3698 cut += " && ";
3699 cut += TString::Format("%s==%d", _cat->GetName(), _cat->getCurrentIndex());
3700 obs.add(*_cat); // note: if we ever changed coords to return clones, would need to keep coords alive
3701 } else if (auto _rv = _c->get<RooAbsRealLValue>(); _rv) {
3702 // todo: check coordRange is a single range rather than multirange
3703 if (cut != "")
3704 cut += " && ";
3705 cut +=
3706 TString::Format("%s>=%f&&%s<%f", _rv->GetName(), _rv->getMin(_rv->getStringAttribute("coordRange")),
3707 _rv->GetName(), _rv->getMax(_rv->getStringAttribute("coordRange")));
3708 obs.add(*_rv); // note: if we ever changed coords to return clones, would need to keep coords alive
3709 } else {
3710 throw std::runtime_error("SetBinContent of data: Unsupported coordinate type");
3711 }
3712 }
3713
3714 RooFormulaVar cutFormula("cut1", cut, obs); // doing this to avoid complaints about unused vars
3715 RooFormulaVar icutFormula("icut1", TString::Format("!(%s)", cut.Data()), obs);
3716
3717 TString cut2;
3718 if (_ax) {
3719 cut2 = TString::Format("%s >= %f && %s < %f", _ax->GetParent()->GetName(), _ax->GetBinLowEdge(bin),
3720 _ax->GetParent()->GetName(), _ax->GetBinUpEdge(bin));
3721 obs.add(*dynamic_cast<RooAbsArg *>(_ax->GetParent()));
3722 } else {
3723 cut2 = "1==1";
3724 }
3725 RooFormulaVar cutFormula2("cut2", cut + " && " + cut2, obs);
3726 RooFormulaVar icutFormula2("icut2", TString::Format("!(%s && %s)", cut.Data(), cut2.Data()), obs);
3727
3728 // // go up through parents looking for slice obs
3729 // auto _p = fParent;
3730 // while(_p) {
3731 // TString pName(_p->GetName());
3732 // if (auto pos = pName.Index('='); pos != -1) {
3733 // if(auto _obs = _p->getObject<RooAbsLValue>(pName(0,pos)); _obs) {
3734 // if(auto _cat = dynamic_cast<RooAbsCategoryLValue*>(_obs.get()); _cat) {
3735 // _cat->setLabel(pName(pos+1,pName.Length()));
3736 // cut += TString::Format("%s%s==%d", (cut=="")?"":" && ",_cat->GetName(),
3737 // _cat->getCurrentIndex());
3738 // } else if(auto _var = dynamic_cast<RooAbsRealLValue*>(_obs.get()); _var) {
3739 // _var->setVal(TString(pName(pos+1,pName.Length())).Atof());
3740 // // TODO: Cut for this!!
3741 // }
3742 // obs.add(*dynamic_cast<RooAbsArg*>(_obs.get()));
3743 // } else {
3744 // throw std::runtime_error("Unknown observable, could not find");
3745 // }
3746 // }
3747 // _p = _p->fParent;
3748 // }
3749
3750 // add observables to dataset if necessary
3751 RooArgSet l(obs);
3752 l.remove(*_data->get(), true, true);
3753 if (!l.empty()) {
3754 // addColumns method is buggy: https://github.com/root-project/root/issues/8787
3755 // incredibly though, addColumn works??
3756 for (auto &x : l) {
3757 _data->addColumn(*x);
3758 }
3759 // instead create a copy dataset and merge it into current
3760 // cant use merge because it drops weightVar
3761 /*RooDataSet tmp("tmp","tmp",l);
3762 for(int i=0;i<_data->numEntries();i++) tmp.add(l);
3763 _data->merge(&tmp);*/
3764 // delete _data->addColumns(l);
3765 }
3766 // before adding, ensure range is good to cover
3767 for (auto &o : obs) {
3768 if (auto v = dynamic_cast<RooRealVar *>(o); v) {
3769 if (auto dv = dynamic_cast<RooRealVar *>(_data->get()->find(v->GetName())); dv) {
3770 if (v->getMin() < dv->getMin())
3771 dv->setMin(v->getMin());
3772 if (v->getMax() > dv->getMax())
3773 dv->setMax(v->getMax());
3774 }
3775 } else if (auto c = dynamic_cast<RooCategory *>(o); c) {
3776 if (auto dc = dynamic_cast<RooCategory *>(_data->get()->find(c->GetName())); dc) {
3777 if (!dc->hasLabel(c->getCurrentLabel())) {
3778 dc->defineType(c->getCurrentLabel(), c->getCurrentIndex());
3779 }
3780 }
3781 }
3782 }
3783
3784 // using SetBinContent means dataset must take on a binned form at these coordinates
3785 // if number of entries doesnt match number of bins then will 'bin' the data
3786 if (bin) {
3787 if (auto _nentries = std::unique_ptr<RooAbsData>(_data->reduce(cutFormula))->numEntries();
3788 _nentries != _ax->GetNbins()) {
3789 auto _contents = GetBinContents(1, _ax->GetNbins());
3790
3791 if (_nentries > 0) {
3792 Info("SetBinContent", "Binning %s in channel: %s", GetName(), cut.Data());
3793 auto _reduced = std::unique_ptr<RooAbsData>(_data->reduce(icutFormula));
3794 _data->reset();
3795 for (int j = 0; j < _reduced->numEntries(); j++) {
3796 auto _obs = _reduced->get(j);
3797 _data->add(*_obs, _reduced->weight());
3798 }
3799 }
3800 for (int i = 1; i <= _ax->GetNbins(); i++) {
3801 // can skip over the bin we will be setting to save a reduce step below
3802 if (i == bin)
3803 continue;
3804 dynamic_cast<RooAbsLValue *>(_ax->GetParent())->setBin(i - 1, _ax->GetName());
3805 _data->add(obs, _contents.at(i - 1));
3806 }
3807 }
3808 }
3809 // remove existing entries
3810 if (std::unique_ptr<RooAbsData>(_data->reduce(cutFormula2))->numEntries() > 0) {
3811 auto _reduced = std::unique_ptr<RooAbsData>(_data->reduce(icutFormula2));
3812 _data->reset();
3813 for (int j = 0; j < _reduced->numEntries(); j++) {
3814 auto _obs = _reduced->get(j);
3815 _data->add(*_obs, _reduced->weight());
3816 }
3817 }
3818 if (_ax)
3819 dynamic_cast<RooAbsLValue *>(_ax->GetParent())->setBin(bin - 1, _ax->GetName());
3820 _data->add(obs, value);
3821 if (auto bb = getBrowsable(".sourceds"))
3822 return bb->SetBinContent(bin, value, par, parVal); // apply to source ds if we have one
3823 return true;
3824
3825 } else if (get<RooDataHist>()) {
3826 throw std::runtime_error("RooDataHist not supported yet");
3827 }
3828 }
3829
3830 if (auto _varies = variations(); !_varies.empty() || (par && strlen(par))) {
3831 if (!par || strlen(par) == 0) {
3832 return _varies["nominal"]->SetBinContent(bin, value, par, parVal);
3833 } else if (auto it = _varies.find(Form("%s=%g", par, parVal)); it) {
3834 return it->SetBinContent(bin, value);
3835 } else {
3836 // need to create the variation : note - if no variations existed up to now this will inject a new node
3837 // so we should redirect ourself to the new node
3838 // TODO: Do we need to redirect parents?
3839 TString s = Form("%s=%g", par, parVal);
3840 return Vary(s.Data()).SetBinContent(bin, value);
3841 }
3842 }
3843
3844 auto o = get();
3845 if (auto p = dynamic_cast<RooRealVar *>(o); p) {
3846 if (!par || strlen(par) == 0) {
3847 if (p->getMax() < value)
3848 p->setMax(value);
3849 if (p->getMin() > value)
3850 p->setMin(value);
3851 p->setVal(value);
3852 sterilize();
3853 return true;
3854 }
3855
3856 } else if (auto c = dynamic_cast<RooConstVar *>(o); c) {
3857
3858 // if parent is a FlexibleInterpVar, change the value in that .
3859 if (strcmp(c->GetName(), Form("%g", c->getVal())) == 0) {
3860 c->SetNameTitle(Form("%g", value), Form("%g", value));
3861 }
3862#if ROOT_VERSION_CODE < ROOT_VERSION(6, 24, 00)
3863 c->_value = value; // in future ROOT versions there is a changeVal method!
3864#else
3865 c->changeVal(value);
3866#endif
3867
3869 fParent->Vary(*this);
3870 }
3871
3872 sterilize();
3873 return true;
3874 } else if (auto f = dynamic_cast<RooHistFunc *>(o); f) {
3875 auto bin_pars = f->dataHist().get(bin - 1);
3876 if (f->getAttribute("density")) {
3877 value /= f->dataHist().binVolume(*bin_pars);
3878 }
3879 f->dataHist().set(*bin_pars, value);
3880 f->setValueDirty();
3881
3882 if (auto otherfName = f->getStringAttribute("symmetrized_by"); otherfName) {
3883 // broken symmetry, so update flags ...
3884 f->setStringAttribute("symmetrized_by", nullptr);
3885 if (auto x = getObject<RooAbsArg>(otherfName); x) {
3886 x->setStringAttribute("symmetrizes", nullptr);
3887 x->setStringAttribute("symmetrize_nominal", nullptr);
3888 }
3889 } else if (auto otherfName2 = f->getStringAttribute("symmetrizes"); otherfName2) {
3890 auto nomf = getObject<RooHistFunc>(f->getStringAttribute("symmetrize_nominal"));
3891 auto otherf = getObject<RooHistFunc>(otherfName2);
3892 if (nomf && otherf) {
3893 otherf->dataHist().set(*bin_pars, 2 * nomf->dataHist().weight(bin - 1) - value);
3894 otherf->setValueDirty();
3895 }
3896 }
3897 sterilize();
3898 return true;
3899 } else if (auto f2 = dynamic_cast<RooStats::HistFactory::FlexibleInterpVar *>(o); f2) {
3900 // changing nominal value
3901 f2->setNominal(value);
3902 }
3903 throw std::runtime_error(TString::Format("unable to set bin content of %s", GetPath().c_str()));
3904}
3905
3906bool xRooNode::SetBinData(int bin, double value, const char *dataName)
3907{
3908 return datasets()[dataName]->SetBinContent(bin, value);
3909}
3910
3911bool xRooNode::SetData(const TObject &obj, const char *dataName)
3912{
3913 return datasets()[dataName]->SetContents(obj);
3914}
3915
3916bool xRooNode::SetBinError(int bin, double value)
3917{
3918
3919 // if it's a RooProduct locate child with the same name
3920 if (get<RooProduct>()) {
3921 return factors()[GetName()]->SetBinError(bin, value);
3922 }
3923
3924 if (auto _varies = variations(); !_varies.empty()) {
3925 return _varies["nominal"]->SetBinError(bin, value);
3926 }
3927
3928 auto o = get();
3929
3930 if (auto f = dynamic_cast<RooHistFunc *>(o); f) {
3931
3932 // if (f->getAttribute("density")) { value /= f->dataHist().binVolume(*bin_pars); } - commented out because DON'T
3933 // convert .. sumw and sumw2 attributes will be stored not as densities
3934
3935 // NOTE: Can only do this because factors() makes parents of its children it's own parent (it isn't the parent)
3936 // If ever make factors etc part of the parentage then this would need tweaking to get to the true parent
3937 // find first parent that is a RooProduct, that is where the statFactor would live
3938 // stop as soon as we reach pdf object
3939 auto _prodParent = fParent;
3940 while (_prodParent && !_prodParent->get<RooProduct>() && !_prodParent->get<RooAbsPdf>()) {
3941 if (_prodParent->get<PiecewiseInterpolation>() && strcmp(GetName(), "nominal")) {
3942 _prodParent.reset();
3943 break; // only the 'nominal' variation can look for a statFactor outside the variation container
3944 }
3945 _prodParent = _prodParent->fParent;
3946 }
3947 auto _f_stat =
3948 (_prodParent && !_prodParent->get<RooAbsPdf>()) ? _prodParent->factors().find("statFactor") : nullptr;
3949 auto f_stat = (_f_stat) ? _f_stat->get<ParamHistFunc>() : nullptr;
3950 if (_f_stat && _f_stat->get() && !f_stat) {
3951 throw std::runtime_error("stat factor must be a paramhistfunc");
3952 }
3953
3954 // stat uncertainty lives in the "statFactor" factor, each sample has its own one,
3955 // but they can share parameters
3956 if (!f_stat) {
3957 if (value == 0)
3958 return true;
3959 TString parNames;
3960 for (auto &p : xRooNode("tmp", *f, std::shared_ptr<xRooNode>(nullptr)).vars()) {
3961 if (parNames != "")
3962 parNames += ",";
3963 parNames += p->get()->GetName();
3964 }
3965 auto h = std::unique_ptr<TH1>(f->dataHist().createHistogram(parNames
3966#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 27, 00)
3967 ,
3969#endif
3970 ));
3971 h->Reset();
3972 h->SetName("statFactor");
3973 h->SetTitle(TString::Format("StatFactor of %s", f->GetTitle()));
3974 h->SetOption("blankshape");
3975
3976 // multiply parent if is nominal
3977 auto toMultiply = this;
3978 if (strcmp(GetName(), "nominal") == 0 && fParent && fParent->get<PiecewiseInterpolation>())
3979 toMultiply = fParent.get();
3980
3981 f_stat = dynamic_cast<ParamHistFunc *>(toMultiply->Multiply(*h).get());
3982 if (!f_stat) {
3983 throw std::runtime_error("Failed creating stat shapeFactor");
3984 }
3985 }
3986
3987 auto phf = f_stat;
3988
3989 TString prefix = f->getStringAttribute("statPrefix");
3990 if (value && prefix == "") {
3991 // find the first parent that can hold components (RooAddPdf, RooRealSumPdf, RooAddition, RooWorkspace) ... use
3992 // that name for the stat factor
3993 auto _p = fParent;
3994 while (_p && !(_p->get()->InheritsFrom("RooRealSumPdf") || _p->get()->InheritsFrom("RooAddPdf") ||
3995 _p->get()->InheritsFrom("RooWorkspace") || _p->get()->InheritsFrom("RooAddition"))) {
3996 _p = _p->fParent;
3997 }
3998 prefix = TString::Format("stat_%s", (_p && _p->get<RooAbsReal>()) ? _p->get()->GetName() : f->GetName());
3999 }
4000 auto newVar = (value == 0) ? getObject<RooRealVar>("1")
4001 : acquire<RooRealVar>(Form("%s_bin%d", prefix.Data(), bin),
4002 Form("%s_bin%d", prefix.Data(), bin), 1);
4003#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
4004 RooArgList &pSet = phf->_paramSet;
4005#else
4006 RooArgList &pSet = const_cast<RooArgList &>(phf->paramList());
4007#endif
4008 auto var = dynamic_cast<RooRealVar *>(&pSet[bin - 1]);
4009
4010 if (newVar.get() != var) {
4011 // need to swap out var for newVar
4012 // replace ith element in list with new func, or inject into RooProduct
4013 RooArgList all;
4014 for (int i = 0; i < pSet.getSize(); i++) {
4015 if (i != bin - 1)
4016 all.add(*pSet.at(i));
4017 else {
4018 all.add(*newVar);
4019 }
4020 }
4021 pSet.removeAll();
4022 pSet.add(all);
4023 }
4024
4025 xRooNode v((value == 0) ? *var : *newVar, *this);
4026 auto rrv = dynamic_cast<RooRealVar *>(v.get());
4027 if (strcmp(rrv->GetName(), "1") != 0) {
4028 TString origName = (f->getStringAttribute("origName")) ? f->getStringAttribute("origName") : GetName();
4029 rrv->setStringAttribute(Form("sumw2_%s", origName.Data()), TString::Format("%f", pow(value, 2)));
4030 auto bin_pars = f->dataHist().get(bin - 1);
4031 auto _binContent = f->dataHist().weight();
4032 if (f->getAttribute("density")) {
4033 _binContent *= f->dataHist().binVolume(*bin_pars);
4034 }
4035 rrv->setStringAttribute(Form("sumw_%s", origName.Data()), TString::Format("%f", _binContent));
4036 double sumw2 = 0;
4037 double sumw = 0;
4038 for (auto &[s, sv] : rrv->stringAttributes()) {
4039 if (s.find("sumw_") == 0) {
4040 sumw += TString(sv).Atof();
4041 } else if (s.find("sumw2_") == 0) {
4042 sumw2 += TString(sv).Atof();
4043 }
4044 }
4045 if (sumw2 && sumw2 != std::numeric_limits<double>::infinity()) {
4046 double tau = pow(sumw, 2) / sumw2;
4047 rrv->setError((tau < 1e-15) ? 1e15 : (/*rrv->getVal()*/ 1. / sqrt(tau))); // not sure why was rrv->getVal()?
4048 rrv->setConstant(false);
4049 // parameter must be constrained
4050 auto _constr = v.constraints();
4051 // std::cout << " setting constraint " << v.GetName() << " nomin=" << tau << std::endl;
4052 if (_constr.empty()) {
4053 rrv->setStringAttribute("boundConstraint", _constr.Add("poisson").get()->GetName());
4054 } else {
4055 auto _glob = _constr.at(0)->obs().at(0)->get<RooRealVar>();
4056 // TODO: Update any globs snapshots that are designed to match the nominal
4057 _glob->setStringAttribute("nominal", TString::Format("%f", tau));
4058 double _min = tau * (1. - 5. * sqrt(1. / tau));
4059 double _max = tau * (1. + 5. * sqrt(1. / tau));
4060 _glob->setRange(_min, _max);
4061 _glob->setVal(tau);
4062 _constr.at(0)->pp().at(0)->SetBinContent(0, tau);
4063 rrv->setStringAttribute("boundConstraint", _constr.at(0)->get()->GetName());
4064 }
4065 rrv->setRange(std::max((1. - 5. * sqrt(1. / tau)), 1e-15), 1. + 5. * sqrt(1. / tau));
4066 } else {
4067 // remove constraint
4068 if (auto _constr = v.constraints(); !_constr.empty()) {
4069 v.constraints().Remove(*_constr.at(0));
4070 }
4071 // set const if sumw2 is 0 (i.e. no error)
4072 rrv->setVal(1);
4073 rrv->setError(0);
4074 rrv->setConstant(sumw2 == 0);
4075 }
4076 }
4077
4078 return true;
4079 }
4080
4081 throw std::runtime_error(TString::Format("%s SetBinError failed", GetName()));
4082}
4083
4084std::shared_ptr<xRooNode> xRooNode::at(const std::string &name, bool browseResult) const
4085{
4086 auto res = find(name, browseResult);
4087 if (res == nullptr)
4088 throw std::out_of_range(name + " does not exist");
4089 return res;
4090}
4091
4092////////////////////////////////////////////////////////////////////////////////
4093/// The RooWorkspace this node belong to, if any
4094
4096{
4097 if (auto _w = get<RooWorkspace>(); _w)
4098 return _w;
4099 if (auto a = get<RooAbsArg>(); a && GETWS(a)) {
4100 return GETWS(a);
4101 }
4102 if (fParent)
4103 return fParent->ws();
4104 return nullptr;
4105}
4106
4108{
4109
4110 xRooNode out(".constraints", nullptr, *this);
4111
4112 std::function<RooAbsPdf *(const xRooNode &n, RooAbsArg &par, std::set<RooAbsPdf *> ignore)> getConstraint;
4113 getConstraint = [&](const xRooNode &n, RooAbsArg &par, std::set<RooAbsPdf *> ignore) {
4114 if (auto _pdf = n.get<RooAbsPdf>()) {
4115 if (ignore.count(_pdf))
4116 return (RooAbsPdf *)nullptr;
4117 ignore.insert(_pdf);
4118 }
4119 auto o = n.get<RooProdPdf>();
4120 if (!o) {
4121 if (n.get<RooSimultaneous>()) {
4122 // check all channels for a constraint if is simultaneous
4123 for (auto &c : n.bins()) {
4124 if (auto oo = getConstraint(*c.get(), par, ignore); oo) {
4125 return oo;
4126 }
4127 }
4128 return (RooAbsPdf *)nullptr;
4129 } else if (n.get<RooAbsPdf>() && n.fParent && n.fParent->get<RooWorkspace>()) {
4130 // reached top-level pdf, which wasn't a simultaneous, so stop here
4131 return (RooAbsPdf *)nullptr;
4132 } else if (auto _ws = n.get<RooWorkspace>(); _ws) {
4133 // reached a workspace, check for any pdf depending on parameter that isnt the ignore
4134 for (auto p : _ws->allPdfs()) {
4135 if (ignore.count(static_cast<RooAbsPdf *>(p)))
4136 continue;
4137 if (p->dependsOn(par)) {
4138 out.emplace_back(std::make_shared<xRooNode>(par.GetName(), *p, *this));
4139 }
4140 }
4141 }
4142 if (!n.fParent)
4143 return (RooAbsPdf *)nullptr;
4144 return getConstraint(*n.fParent.get(), par, ignore);
4145 }
4146 for (auto p : o->pdfList()) {
4147 if (ignore.count(static_cast<RooAbsPdf *>(p)))
4148 continue;
4149 if (p->dependsOn(par)) {
4150 out.emplace_back(std::make_shared<xRooNode>(par.GetName(), *p, *this));
4151 }
4152 }
4153 return (RooAbsPdf *)nullptr;
4154 };
4155
4156 for (auto &p : vars()) {
4157 auto v = dynamic_cast<RooAbsReal *>(p->get());
4158 if (!v)
4159 continue;
4160 if (v->getAttribute("Constant") && v != get<RooAbsReal>())
4161 continue; // skip constants unless we are getting the constraints of a parameter itself
4162 if (v->getAttribute("obs"))
4163 continue; // skip observables ... constraints constrain pars not obs
4164 getConstraint(*this, *v, {get<RooAbsPdf>()});
4165 /*if (auto c = ; c) {
4166 out.emplace_back(std::make_shared<Node2>(p->GetName(), *c, *this));
4167 }*/
4168 }
4169
4170 // finish by removing any constraint that contains another constraint for the same par
4171 // and consolidate common pars
4172 auto it = out.std::vector<std::shared_ptr<xRooNode>>::begin();
4173 while (it != out.std::vector<std::shared_ptr<xRooNode>>::end()) {
4174 bool removeIt = false;
4175 for (auto &c : out) {
4176 if (c.get() == it->get())
4177 continue;
4178 if ((*it)->get<RooAbsArg>()->dependsOn(*c->get<RooAbsArg>())) {
4179 removeIt = true;
4180 std::set<std::string> parNames;
4181 std::string _cName = c->GetName();
4182 do {
4183 parNames.insert(_cName.substr(0, _cName.find(';')));
4184 _cName = _cName.substr(_cName.find(';') + 1);
4185 } while (_cName.find(';') != std::string::npos);
4186 parNames.insert(_cName);
4187 _cName = it->get()->GetName();
4188 do {
4189 parNames.insert(_cName.substr(0, _cName.find(';')));
4190 _cName = _cName.substr(_cName.find(';') + 1);
4191 } while (_cName.find(';') != std::string::npos);
4192 parNames.insert(_cName);
4193 _cName = "";
4194 for (auto &x : parNames) {
4195 if (!_cName.empty())
4196 _cName += ";";
4197 _cName += x;
4198 }
4199 c->TNamed::SetName(_cName.c_str());
4200 break;
4201 }
4202 }
4203 if (removeIt)
4204 it = out.erase(it);
4205 else
4206 ++it;
4207 }
4208
4209 // if getting constraints of a fundamental then use the constraint names instead of the par name (because would be
4210 // all same otherwise)
4211 if (get<RooAbsArg>() && get<RooAbsArg>()->isFundamental()) {
4212 for (auto &o : out) {
4213 o->TNamed::SetName(o->get()->GetName());
4214 }
4215 }
4216
4217 return out;
4218}
4219
4220std::shared_ptr<TObject> xRooNode::convertForAcquisition(xRooNode &acquirer, const char *opt) const
4221{
4222
4223 TString sOpt(opt);
4224 sOpt.ToLower();
4225 TString sName(GetName());
4226 if (sOpt == "func")
4227 sName = TString("factory:") + sName;
4228
4229 // if arg is a histogram, will acquire it as a RooHistFunc unless no conversion
4230 // todo: could flag not to convert
4231 if (auto h = get<TH1>(); h) {
4232 TString sOpt2(h->GetOption());
4233 std::map<std::string, std::string> stringAttrs;
4234 while (sOpt2.Contains("=")) {
4235 auto pos = sOpt2.Index("=");
4236 auto start = sOpt2.Index(";") + 1;
4237 if (start > pos)
4238 start = 0;
4239 auto end = sOpt2.Index(";", pos);
4240 if (end == -1)
4241 end = sOpt2.Length();
4242 stringAttrs[sOpt2(start, pos - start)] = sOpt2(pos + 1, end - pos - 1);
4243 sOpt2 = TString(sOpt2(0, start)) + TString(sOpt2(end + 1, sOpt2.Length()));
4244 }
4245 TString newObjName = GetName();
4246 TString origName = GetName();
4247 if (origName.BeginsWith(';'))
4248 origName = origName(1, origName.Length());
4249 if (newObjName.BeginsWith(';'))
4250 newObjName =
4251 newObjName(1, newObjName.Length()); // special case if starts with ';' then don't create a fancy name
4252 else if (acquirer.get() && !acquirer.get<RooWorkspace>())
4253 newObjName = TString::Format(
4254 "%s_%s", (acquirer.mainChild().get()) ? acquirer.mainChild()->GetName() : acquirer->GetName(),
4255 newObjName.Data());
4256 // can convert to a RooHistFunc, or RooParamHist if option contains 'shape'
4257 TString varName = h->GetXaxis()->GetName();
4258 std::string binningName = newObjName.Data();
4259 if (auto pos = varName.Index(';'); pos != -1) {
4260 binningName = varName(pos + 1, varName.Length());
4261 varName = varName(0, pos);
4262 }
4263
4264 if (varName == "xaxis" &&
4265 !acquirer.get<RooSimultaneous>()) { // default case, try to take axis var and binning from the acquirer
4266 if (auto ax = acquirer.GetXaxis(); ax) {
4267 varName = ax->GetParent()->GetName();
4268 // TODO: check the binning is consistent before using - at least will check nBins below
4269 binningName = ax->GetName();
4270 } else if (acquirer.obs().size() == 1)
4271 varName = acquirer.obs().at(0)->get()->GetName(); // TODO what if no obs but Xaxis var is defined?
4272 }
4273 auto x = acquirer.acquire<RooRealVar>(varName, h->GetXaxis()->GetTitle(), h->GetXaxis()->GetXmin(),
4274 h->GetXaxis()->GetXmax());
4275 if (x->getMin() > h->GetXaxis()->GetXmin())
4276 x->setMin(h->GetXaxis()->GetXmin());
4277 if (x->getMax() < h->GetXaxis()->GetXmax())
4278 x->setMax(h->GetXaxis()->GetXmax());
4279 if (!x->hasBinning(binningName.c_str())) {
4280 if (h->GetXaxis()->IsVariableBinSize()) {
4281 x->setBinning(RooBinning(h->GetNbinsX(), h->GetXaxis()->GetXbins()->GetArray()), binningName.c_str());
4282 } else {
4283 x->setBinning(
4284 RooUniformBinning(h->GetXaxis()->GetXmin(), h->GetXaxis()->GetXmax(), h->GetXaxis()->GetNbins()),
4285 binningName.c_str());
4286 }
4287 x->getBinning(binningName.c_str()).SetTitle(h->GetXaxis()->GetTitle());
4288 if (x->getBinningNames().size() == 2) {
4289 // this was the first binning, so copy it over to be the default binning too
4290 x->setBinning(x->getBinning(binningName.c_str()));
4291 }
4292 } else {
4293 // TODO check binning is compatible with histogram
4294 if (x->getBinning(binningName.c_str()).numBins() != h->GetNbinsX()) {
4295 throw std::runtime_error(
4296 TString::Format("binning mismatch for binning %s of %s", binningName.c_str(), x->GetName()));
4297 }
4298 }
4299
4300 std::shared_ptr<RooAbsArg> _f;
4301
4302 // if acquirer is a RooSimultaneous, will use histogram to define a channel
4303 if (acquirer.get<RooSimultaneous>()) {
4304 _f = acquirer.acquireNew<RooProdPdf>(newObjName, (strlen(h->GetTitle())) ? h->GetTitle() : h->GetName(),
4305 RooArgList());
4306 for (auto &[k, v] : stringAttrs) {
4307 _f->setStringAttribute(k.c_str(), v.c_str());
4308 }
4309 x->setAttribute("obs", true);
4310 } else if (sOpt2.Contains("shape")) {
4311 RooArgList list;
4312 for (int i = 0; i < x->getBinning(binningName.c_str()).numBins(); i++) {
4313 std::shared_ptr<RooAbsArg> arg;
4314 if (sOpt2.Contains("blankshape")) {
4315 arg = acquirer.acquire2<RooAbsArg, RooRealVar>("1", "1", 1);
4316 } else {
4317 if (!h) {
4318 arg = acquirer.acquireNew<RooRealVar>(TString::Format("%s_bin%d", newObjName.Data(), i + 1), "", 1);
4319 }
4320 if (h->GetMinimumStored() != -1111 || h->GetMaximumStored() != -1111) {
4321 arg = acquirer.acquireNew<RooRealVar>(TString::Format("%s_bin%d", newObjName.Data(), i + 1), "",
4322 h->GetBinContent(i + 1), h->GetMinimumStored(),
4323 h->GetMaximumStored());
4324 } else {
4325 arg = acquirer.acquireNew<RooRealVar>(TString::Format("%s_bin%d", newObjName.Data(), i + 1), "",
4326 h->GetBinContent(i + 1));
4327 }
4328 }
4329 list.add(*arg);
4330 }
4331 // paramhistfunc requires the binnings to be loaded as default at construction time
4332 // so load binning temporarily
4333 auto tmp = dynamic_cast<RooAbsBinning *>(x->getBinningPtr(0)->Clone());
4334 x->setBinning(x->getBinning(binningName.c_str()));
4335 _f = acquirer.acquireNew<ParamHistFunc>(newObjName, h->GetTitle(), *x, list);
4336#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
4337 dynamic_cast<ParamHistFunc *>(_f.get())->_paramSet.setName("paramSet"); // so can see when print
4338#else
4339 const_cast<RooArgList &>(dynamic_cast<ParamHistFunc *>(_f.get())->paramList())
4340 .setName("paramSet"); // so can see when print
4341#endif
4342 x->setBinning(*tmp); // restore binning
4343 delete tmp;
4344 for (auto &[k, v] : stringAttrs) {
4345 _f->setStringAttribute(k.c_str(), v.c_str());
4346 }
4347 } else {
4348 auto dh = acquirer.acquireNew<RooDataHist>(Form("hist_%s", newObjName.Data()), h->GetTitle(), *x,
4349 binningName.c_str() /* binning name*/);
4350 if (!dh) {
4351 throw std::runtime_error("Couldn't make data hist");
4352 }
4353 auto f = acquirer.acquireNew<RooHistFunc>(newObjName, h->GetTitle(), *x, *dh,
4354 0 /*interpolation order between bins*/);
4355 f->forceNumInt();
4356 f->setAttribute("autodensity"); // where it gets inserted will determine if this is a density or not
4357 _f = f;
4358
4359 for (auto &[k, v] : stringAttrs) {
4360 _f->setStringAttribute(k.c_str(), v.c_str());
4361 }
4362
4363 // need to do these settings here because used in the assignment step
4364 _f->setStringAttribute("xvar", x->GetName());
4365 _f->setStringAttribute("binning", binningName.c_str());
4366 if (strcmp(_f->GetName(), origName.Data()) && !_f->getStringAttribute("alias"))
4367 _f->setStringAttribute("alias", origName);
4368
4369 // copy values over using the assignment operator - may convert to a RooProduct if there are stat uncerts
4370 xRooNode tmp(h->GetName(), _f, acquirer);
4371 tmp = *h;
4372 _f = std::dynamic_pointer_cast<RooAbsArg>(tmp.fComp); // in case got upgrade to a RooProduct
4373 }
4374
4375 _f->setStringAttribute("xvar", x->GetName());
4376 _f->setStringAttribute("binning", binningName.c_str());
4377 // style(h); // will transfer styling to object if necessary - not doing because this method used with plane hists
4378 // frequently
4379 if (strcmp(_f->GetName(), origName.Data()) && !_f->getStringAttribute("alias"))
4380 _f->setStringAttribute("alias", origName);
4381
4382 fComp = _f;
4383 return _f;
4384 } else if (!get() && sName.BeginsWith("factory:") && acquirer.ws()) {
4385 TString s(sName);
4386 s = TString(s(8, s.Length()));
4387 fComp.reset(acquirer.ws()->factory(s), [](TObject *) {});
4388 return fComp;
4389 }
4390
4391 return fComp;
4392}
4393
4394std::shared_ptr<TStyle> xRooNode::style(TObject *initObject, bool autoCreate) const
4395{
4396
4397 auto arg = get<RooAbsArg>();
4398 if (!initObject && !arg) {
4399 return nullptr;
4400 }
4401
4402 std::unique_ptr<TObject> argInitObject;
4403
4404 TString t = GetTitle();
4405 if (initObject) {
4406 t = (strlen(initObject->GetTitle())) ? initObject->GetTitle() : initObject->GetName();
4407 } else if (arg) {
4408 if (arg->getStringAttribute("style"))
4409 t = arg->getStringAttribute("style");
4410 else if (autoCreate) {
4411 // args will default to histo's object styling, whatever that currently may be
4412 argInitObject = std::make_unique<TH1D>(GetName(), GetTitle(), 1, 0, 1);
4413 initObject = argInitObject.get();
4414 } else {
4415 return nullptr;
4416 }
4417 }
4418
4419 std::shared_ptr<TStyle> style; // use to keep alive for access from GetStyle below, in case getObject has decided to
4420 // return the owning ptr (for some reason)
4421 if (!gROOT->GetStyle(t)) {
4422 if ((style = getObject<TStyle>(t.Data()))) {
4423 // loaded style (from workspace?) so put in list and use that
4424 gROOT->GetListOfStyles()->Add(style.get());
4425 } else {
4426 if (!autoCreate)
4427 return nullptr;
4428 // create new style - gets put in style list automatically so don't have to delete
4429 // acquire them so saved to workspaces for auto reload ...
4430 style = const_cast<xRooNode &>(*this).acquireNew<TStyle>(t.Data(),
4431 TString::Format("Style for %s component", t.Data()));
4432 if (auto x = dynamic_cast<TAttLine *>(initObject))
4433 ((TAttLine &)*style) = *x;
4434 if (auto x = dynamic_cast<TAttFill *>(initObject))
4435 ((TAttFill &)*style) = *x;
4436 if (auto x = dynamic_cast<TAttMarker *>(initObject))
4437 ((TAttMarker &)*style) = *x;
4438 gROOT->GetListOfStyles()->Add(style.get());
4439 }
4440 } else {
4441 style = std::shared_ptr<TStyle>(gROOT->GetStyle(t), [](TStyle *) {});
4442 }
4443
4444 if (arg && !arg->getStringAttribute("style")) {
4445 arg->setStringAttribute("style", style->GetName());
4446 }
4447
4448 return style;
4449}
4450
4451std::shared_ptr<TObject> xRooNode::acquire(const std::shared_ptr<TObject> &arg, bool checkFactory, bool mustBeNew)
4452{
4453 if (!arg)
4454 return nullptr;
4455 if (!fAcquirer && !get<RooWorkspace>() && fParent)
4456 return fParent->acquire(arg, checkFactory, mustBeNew);
4457
4458 // if has a workspace and our object is the workspace or is in the workspace then add this object to workspace
4459 auto _ws = (fAcquirer) ? nullptr : ws();
4460 if (_ws && (get() == _ws || _ws->arg(GetName()) || (arg && strcmp(arg->GetName(), GetName()) == 0))) {
4463 if (auto a = dynamic_cast<RooAbsArg *>(arg.get()); a) {
4464 auto out_arg = _ws->arg(a->GetName());
4465 TString aName = arg->GetName();
4466 int ii = 1;
4467 while (out_arg && mustBeNew) {
4468 a->SetName(TString::Format("%s_%d", aName.Data(), ii++));
4469 out_arg = _ws->arg(a->GetName());
4470 }
4471 if (aName != a->GetName())
4472 Warning("acquire", "Renaming to %s", a->GetName());
4473 if (!out_arg) {
4474 bool done = false;
4475 if (checkFactory) {
4476 if (auto res = _ws->factory(arg->GetName()); res) {
4477 a = res;
4478 done = true;
4479 }
4480 }
4481 if (!done && _ws->import(*a, RooFit::RecycleConflictNodes())) {
4482 if (GETWS(a) != _ws) {
4483 Info("acquire", "A copy of %s has been added to workspace %s", a->GetName(), _ws->GetName());
4484 }
4486 return nullptr;
4487 }
4488 // sanitizeWS(); // clears the caches that might exist up to now, as well interfere with getParameters calls
4489 std::set<std::string> setNames;
4490 for (auto &aa : GETWSSETS(_ws)) {
4491 if (TString(aa.first.c_str()).BeginsWith("CACHE_")) {
4492 setNames.insert(aa.first);
4493 }
4494 }
4495 for (auto &aa : setNames)
4496 ws()->removeSet(aa.c_str());
4497 out_arg = _ws->arg(a->GetName());
4498 if (GETWS(out_arg) != _ws) { // seems that when objects imported their ws isn't set
4499 out_arg->setWorkspace(*_ws);
4500 }
4501 }
4503 return std::shared_ptr<TObject>(out_arg, [](TObject *) {});
4504 } else if (auto a2 = dynamic_cast<RooAbsData *>(arg.get()); a2) {
4505 if (_ws->import(*a2, RooFit::Embedded())) {
4507 return nullptr;
4508 }
4510 return std::shared_ptr<TObject>(_ws->embeddedData(arg->GetName()), [](TObject *) {});
4511 } else if (arg->InheritsFrom("RooFitResult") || arg->InheritsFrom("TTree") || arg->IsA() == TStyle::Class()) {
4512 // ensure will have a unique name for import if must be new
4513 TNamed *aNamed = dynamic_cast<TNamed *>(arg.get());
4514 TString aName = arg->GetName();
4515 TObject *out_arg = _ws->genobj(arg->GetName());
4516 int ii = 1;
4517 while (aNamed && out_arg && mustBeNew) {
4518 aNamed->SetName(TString::Format("%s;%d", aName.Data(), ii++));
4519 out_arg = _ws->genobj(aNamed->GetName());
4520 }
4521 if (!out_arg) {
4522 if (aName != arg->GetName()) {
4523 Warning("acquire", "Renaming to %s", arg->GetName());
4524 }
4525 if (_ws->import(*arg.get(), false /*replace existing*/)) {
4527 return nullptr;
4528 }
4529 out_arg = _ws->genobj(arg->GetName());
4530 }
4532 /* this doesnt work because caller has its own version of fParent, not the one in the browser
4533 for(auto o : *gROOT->GetListOfBrowsers()) {
4534 if(auto b = dynamic_cast<TBrowser*>(o); b){
4535 if(auto _b = dynamic_cast<TGFileBrowser*>( dynamic_cast<TRootBrowser*>(b->GetBrowserImp())->fActBrowser
4536 ); _b) { if (auto item = _b->fListTree->FindItemByObj(_b->fRootDir,this); item) { auto _tmp = _b->fListLevel;
4537 _b->fListLevel = item;
4538 bool _tmp2 = item->IsOpen();
4539 item->SetOpen(false);
4540 this->Browse(b);
4541 item->SetOpen(_tmp2);
4542 _b->fListLevel = _tmp;
4543 }
4544 }
4545 }
4546 }*/
4547 return std::shared_ptr<TObject>(out_arg, [](TObject *) {});
4548 }
4550 // Warning("acquire","Not implemented acquisition of object %s",arg->GetName());
4551 // return nullptr;
4552 }
4553 if (!mustBeNew && fProvider) {
4554 auto out = fProvider->getObject(arg->GetName(), arg->ClassName());
4555 if (out)
4556 return out;
4557 }
4558 auto _owned = find(".memory");
4559 if (!_owned) {
4560 _owned = emplace_back(std::make_shared<xRooNode>(".memory", nullptr, *this));
4561 }
4562 // look for exact name, dont use 'find' because doesnt work if trying to find "1" and it doesn't exist, will get back
4563 // idx 1 instead
4564 if (!mustBeNew) {
4565 for (auto &r : *_owned) {
4566 if (strcmp(r->GetName(), arg->GetName()) == 0 && strcmp(r->get()->ClassName(), arg->ClassName()) == 0) {
4567 return r->fComp;
4568 }
4569 }
4570 }
4571 if (!fProvider)
4572 std::cout << GetName() << " taking over " << arg->ClassName() << "::" << arg->GetName() << std::endl;
4573 /*emplace_back(std::make_shared<Node2>(".memory",nullptr,*this))*/
4574 return _owned->emplace_back(std::make_shared<xRooNode>(arg->GetName(), arg, *this))->fComp;
4575 // return arg;
4576}
4577
4578bool xRooNode::SetXaxis(const char *name, const char *title, int nbins, double low, double high)
4579{
4580 RooUniformBinning b(low, high, nbins, name);
4581 b.SetTitle(title);
4582 return SetXaxis(b);
4583}
4584
4585bool xRooNode::SetXaxis(const char *name, const char *title, int nbins, const double *bins)
4586{
4587 RooBinning b(nbins, bins, name);
4588 b.SetTitle(title);
4589 return SetXaxis(b);
4590}
4591
4593{
4594
4595 auto name = binning.GetName();
4596 double high = binning.highBound();
4597 double low = binning.lowBound();
4598 // int nbins = binning.numBins();
4599 auto title = binning.GetTitle();
4600
4601 // if have any dependents and name isn't one of them then stop
4602 auto _deps = vars();
4603 /*if(!_deps.empty() && !_deps.find(name)) {
4604 throw std::runtime_error(TString::Format("%s Does not depend on %s",GetName(),name));
4605 }*/
4606
4607 // object will need to exist
4608 if (!get()) {
4609 if (fParent && !find(GetName())) {
4610 fComp = fParent->Add(*this, "+").fComp;
4611 }
4612 }
4613
4614 auto a = get<RooAbsArg>();
4615 if (!a)
4616 throw std::runtime_error("Cannot SetXaxis of non-arg");
4617
4618 auto _x = acquire<RooRealVar>(name, title, low, high);
4619 _x->setBinning(binning, a->GetName());
4620 _x->getBinning(a->GetName()).SetTitle(title);
4621 if (_x->getBinningNames().size() == 2) {
4622 // this was the first binning, so copy it over to be the default binning too
4623 _x->setBinning(_x->getBinning(a->GetName()));
4624 } else {
4625 // ensure the default binning is wide enough to cover this range
4626 // the alternative to this would be to ensure setNormRange of all pdfs
4627 // are set to correct range (then default can be narrower than some of the named binnings)
4628 if (_x->getMax() < high)
4629 _x->setMax(high);
4630 if (_x->getMin() > low)
4631 _x->setMin(low);
4632 }
4633
4634 if (!_deps.find(name) && get<RooAbsPdf>()) {
4635 // creating a variable for a pdf we will assume it should be an observable
4636 _x->setAttribute("obs");
4637 }
4638
4639 a->setStringAttribute("xvar", _x->GetName());
4640 a->setStringAttribute("binning", a->GetName());
4641 fXAxis.reset(); // remove any existing xaxis
4642
4643 return true;
4644}
4645
4647{
4648 if (!ax)
4649 return false;
4650 if (ax->IsVariableBinSize()) {
4651 return SetXaxis(ax->GetName(), ax->GetTitle(), ax->GetNbins(), ax->GetXbins()->GetArray());
4652 } else {
4653 return SetXaxis(ax->GetName(), ax->GetTitle(), ax->GetNbins(), ax->GetXmin(), ax->GetXmax());
4654 }
4655}
4656
4657bool xRooNode::contains(const std::string &name) const
4658{
4659 return find(name, false) != nullptr;
4660}
4661
4662std::shared_ptr<xRooNode> xRooNode::find(const std::string &name, bool browseResult) const
4663{
4664 std::string partname = (name.find('/') != std::string::npos) ? name.substr(0, name.find('/')) : name;
4665 auto _s = (!get() && fParent) ? fParent->get<RooSimultaneous>()
4666 : get<RooSimultaneous>(); // makes work if doing simPdf.bins()["blah"]
4667 std::string extra = (_s) ? _s->indexCat().GetName() : "";
4668 for (auto &child : *this) {
4669 if (auto _obj = child->get(); name == child->GetName() || partname == child->GetName() ||
4670 (_obj && name == _obj->GetName()) || (_obj && partname == _obj->GetName()) ||
4671 (!extra.empty() && ((extra + "=" + name) == child->GetName() ||
4672 (extra + "=" + partname) == child->GetName()))) {
4673 if (browseResult)
4674 child->browse(); // needed so can go at()->at()->at()...
4675 if (partname != name && name != child->GetName()) {
4676 return child->at(name.substr(partname.length() + 1));
4677 }
4678 return child;
4679 }
4680 if (auto x = mainChild(); x && strcmp(child->GetName(), x.GetName()) == 0) {
4681 // can browse directly into main children as if their children were our children
4682 for (auto &child2 : x.browse()) {
4683 if (auto _obj = child2->get(); name == child2->GetName() || partname == child2->GetName() ||
4684 (_obj && name == _obj->GetName()) || (_obj && partname == _obj->GetName())) {
4685 if (browseResult)
4686 child2->browse(); // needed for onward read (or is it? there's a browse above too??)
4687 if (partname != name && name != child2->GetName()) {
4688 return child2->at(name.substr(partname.length() + 1));
4689 }
4690 return child2;
4691 }
4692 }
4693 }
4694 }
4695 // before giving up see if partName is numeric and indexes within the range
4696 if (TString s(partname); s.IsDec() && size_t(s.Atoi()) < size()) {
4697 auto child2 = at(s.Atoi());
4698 if (partname != name) {
4699 return child2->at(name.substr(partname.length() + 1));
4700 }
4701 return child2;
4702 }
4703 return nullptr;
4704}
4705
4706std::shared_ptr<xRooNode> xRooNode::operator[](const std::string &name)
4707{
4708 std::string partname = (name.find('/') != std::string::npos) ? name.substr(0, name.find('/')) : name;
4709 browse();
4710 auto _s = (!get() && fParent) ? fParent->get<RooSimultaneous>()
4711 : get<RooSimultaneous>(); // makes work if doing simPdf.bins()["blah"]
4712 std::string extra = (_s) ? _s->indexCat().GetName() : "";
4713 std::shared_ptr<xRooNode> folderNode;
4714 for (auto &child : *this) {
4715 if (name == child->GetName() || partname == child->GetName() ||
4716 (!extra.empty() &&
4717 ((extra + "=" + name) == child->GetName() || (extra + "=" + partname) == child->GetName()))) {
4718 child->browse(); // needed for onward read (or is it? there's a browse above too??)
4719 if (partname != name && name != child->GetName()) {
4720 return child->operator[](name.substr(partname.length() + 1));
4721 }
4722 return child;
4723 }
4724 if (auto x = mainChild(); strcmp(child->GetName(), x.GetName()) == 0) {
4725 // can browse directly into main children as if their children were our children
4726 for (auto &child2 : x.browse()) {
4727 if (name == child2->GetName() || partname == child2->GetName()) {
4728 child2->browse(); // needed for onward read (or is it? there's a browse above too??)
4729 if (partname != name && name != child2->GetName()) {
4730 return child2->operator[](name.substr(partname.length() + 1));
4731 }
4732 return child2;
4733 }
4734 }
4735 }
4736 if (child->fFolder == (std::string("!") + partname)) {
4737 if (!folderNode)
4738 folderNode = std::make_shared<xRooNode>(child->fFolder.c_str(), nullptr, *this);
4739 folderNode->push_back(child);
4740 }
4741 }
4742 if (folderNode) {
4743 if (partname != name) {
4744 return folderNode->operator[](name.substr(partname.length() + 1));
4745 }
4746 return folderNode;
4747 }
4748 // before giving up see if partName is numeric and indexes within the range
4749 if (TString s(partname); s.IsDec() && size_t(s.Atoi()) < size()) {
4750 auto child2 = at(s.Atoi());
4751 if (partname != name) {
4752 return child2->operator[](name.substr(partname.length() + 1));
4753 }
4754 return child2;
4755 }
4756 auto out = std::make_shared<xRooNode>(partname.c_str(), nullptr, *this); // not adding as child yeeet
4757 if (partname != name) {
4758 return out->operator[](name.substr(partname.length() + 1));
4759 }
4760 return out;
4761}
4762
4764{
4765 if (!b) {
4766 for (auto o : *gROOT->GetListOfBrowsers()) {
4767 b = dynamic_cast<TBrowser *>(o);
4768 if (!b || !b->GetBrowserImp())
4769 continue;
4770 if (auto out = GetTreeItem(b); out)
4771 return out;
4772 }
4773 return nullptr;
4774 }
4775 if (!b->GetBrowserImp())
4776 return nullptr;
4777 if (auto _b = dynamic_cast<TGFileBrowser *>(GETACTBROWSER(dynamic_cast<TRootBrowser *>(b->GetBrowserImp()))); _b) {
4778 auto _root = GETROOTDIR(_b);
4779 ;
4780 if (!_root)
4781 _root = GETLISTTREE(_b)->GetFirstItem();
4783 return GETLISTTREE(_b)->FindItemByObj(_root, const_cast<xRooNode *>(this));
4784 }
4785 return nullptr;
4786}
4787
4789{
4790 if (!b) {
4791 for (auto o : *gROOT->GetListOfBrowsers()) {
4792 b = dynamic_cast<TBrowser *>(o);
4793 if (!b || !b->GetBrowserImp())
4794 continue;
4795 if (auto out = GetListTree(b); out)
4796 return out;
4797 }
4798 return nullptr;
4799 }
4800 if (b->GetBrowserImp()) {
4801 if (auto _b = dynamic_cast<TGFileBrowser *>(GETACTBROWSER(dynamic_cast<TRootBrowser *>(b->GetBrowserImp())));
4802 _b) {
4803 auto _root = GETROOTDIR(_b);
4804 if (!_root)
4805 _root = GETLISTTREE(_b)->GetFirstItem();
4806 if (auto item = GETLISTTREE(_b)->FindItemByObj(_root, const_cast<xRooNode *>(this)); item) {
4807 return GETLISTTREE(_b);
4808 }
4809 }
4810 }
4811 return nullptr;
4812}
4813
4814void xRooNode::SetName(const char *name)
4815{
4817 if (auto a = get<RooAbsArg>(); a)
4818 a->setStringAttribute("alias", name);
4819 for (auto o : *gROOT->GetListOfBrowsers()) {
4820 if (auto b = dynamic_cast<TBrowser *>(o); b) {
4821 if (auto item = GetTreeItem(b); item) {
4822 item->SetText(name);
4823 }
4824 }
4825 }
4826}
4827
4828void xRooNode::SetTitle(const char *title)
4829{
4830 if (auto o = (get<TNamed>()); o) {
4831 if (auto c = mainChild(); c.get()) {
4832 c.SetTitle(title);
4833 }
4834 o->SetTitle(title);
4835 }
4836 TNamed::SetTitle(title);
4837}
4838
4840{
4841 if (get<RooArgList>() || (!get() && !(strlen(GetName()) > 0 && (GetName()[0] == '!')) && !fBrowseOperation))
4842 return *this; // nothing to browse - 'collection' nodes should already be populated except for folders
4843 // alternative could have been to mandate that the 'components' of a collection node are the children it has.
4844
4845 auto findByObj = [&](const std::shared_ptr<xRooNode> &n) {
4846 std::vector<std::shared_ptr<xRooNode>> &nn = *this;
4847 for (auto &c : nn) {
4848 if (c->get() == n->get() && strcmp(n->GetName(), c->GetName()) == 0)
4849 return c;
4850 }
4851 return std::shared_ptr<xRooNode>(nullptr);
4852 };
4853
4854 auto appendChildren = [&](const xRooNode &n) {
4855 size_t out = 0;
4856 const std::vector<std::shared_ptr<xRooNode>> &nn(n);
4857 for (auto &c : nn) {
4858 if (auto existing = findByObj(c); existing) {
4859 existing->fTimes++;
4860 existing->fFolder = c->fFolder; // transfer folder assignment
4861 } else {
4862 emplace_back(c);
4863 }
4864 if (!TString(c->GetName()).BeginsWith(".coef"))
4865 out++; // don't count .coef as a child, as technically part of parent
4866 }
4867 return out;
4868 };
4869
4870 const std::vector<std::shared_ptr<xRooNode>> &nn2(*this);
4871 for (auto &c : nn2) {
4872 if (strlen(c->GetName()) > 0 && (c->GetName()[0] == '.')) {
4873 c->fTimes = 1;
4874 continue;
4875 } // never auto-cleanup property children
4876 if (strcmp(c->GetName(), "!.pars") == 0) {
4877 c->fTimes = 1;
4878 continue;
4879 } // special collection, also not cleaned up
4880 if (c->get<RooWorkspace>() || c->get<TFile>()) {
4881 c->fTimes = 1;
4882 continue;
4883 } // workspaces and files not cleaned up: TODO have a nocleanup flag instead
4884 c->fTimes = 0;
4885 }
4886
4887 size_t addedChildren = 0;
4888 if (fBrowseOperation) {
4889 addedChildren += appendChildren(fBrowseOperation(this));
4890 } else {
4891 if (get<RooWorkspace>()) {
4892 addedChildren += appendChildren(datasets());
4893 }
4894
4895 // if (get<RooAbsPdf>() && ((fParent && fParent->get<RooWorkspace>()) || !fParent)) {
4896 // // top-level pdfs will also list the ".vars" property for -- should make this updateable
4897 // //if (auto x = find("!.vars"); !x) { // this is slower because it triggers a browse of !.vars
4898 // if(!contains("!.vars")) {
4899 // emplace_back(std::make_shared<Node2>("!.vars",nullptr,*this));
4900 // } /*else {
4901 // x->fTimes++;
4902 // }*/
4903 // }
4904
4905 // go through components factors and variations, adding all as children if required
4906 addedChildren += appendChildren(components());
4907 if (!get<RooWorkspace>())
4908 addedChildren += appendChildren(factors());
4909 // include coefs if any
4910 auto _coefs = coefs();
4911 if (_coefs.get() && strcmp(_coefs->GetName(), "1") != 0 && strcmp(_coefs->GetName(), "ONE") != 0) {
4912 if (_coefs.size() == 1 && _coefs.get<RooAddition>()) {
4913 if (strcmp(_coefs.at(0)->GetName(), "1") != 0 &&
4914 strcmp(_coefs.at(0)->GetName(), "ONE") != 0) { // don't add the "1"
4915 auto coef = std::make_shared<xRooNode>(".coef", *_coefs.at(0)->get(), *this);
4916 if (auto existing = findByObj(coef); existing) {
4917 existing->fTimes++;
4918 existing->fFolder = _coefs.at(0)->fFolder; // transfer folder assignment
4919 } else {
4920 emplace_back(coef);
4921 }
4922 }
4923 } else {
4924 if (auto existing = find(_coefs.GetName()); existing) {
4925 existing->fTimes++;
4926 existing->fFolder = _coefs.fFolder; // transfer folder assignment
4927 } else {
4928 emplace_back(std::make_shared<xRooNode>(_coefs));
4929 }
4930 }
4931 }
4932 addedChildren += appendChildren(variations());
4933 if (get<ParamHistFunc>() || get<RooSimultaneous>())
4934 addedChildren += appendChildren(bins());
4935 if (get<RooAbsData>())
4936 addedChildren += appendChildren(obs());
4937 }
4938 // if has no children and is a RooAbsArg, add all the proxies
4939 if (auto arg = get<RooAbsArg>(); arg && addedChildren == 0) {
4940 for (int i = 0; i < arg->numProxies(); i++) {
4941 auto _proxy = arg->getProxy(i);
4942 if (auto a = dynamic_cast<RooArgProxy *>(_proxy)) {
4943 auto c = std::make_shared<xRooNode>(TString::Format(".%s", _proxy->name()), *(a->absArg()), *this);
4944 if (auto existing = findByObj(c); existing) {
4945 existing->fTimes++;
4946 existing->fFolder = c->fFolder; // transfer folder assignment
4947 } else {
4948 emplace_back(c);
4949 }
4950 } else if (auto s = dynamic_cast<RooAbsCollection *>(_proxy)) {
4951 for (auto a2 : *s) {
4952 auto c = std::make_shared<xRooNode>(*a2, *this);
4953 if (arg->numProxies() != 1)
4954 c->fFolder = std::string("!.") +
4955 _proxy->name(); // don't put in a folder if there's just 1 proxy (the collection)
4956 if (auto existing = findByObj(c); existing) {
4957 existing->fTimes++;
4958 existing->fFolder = c->fFolder; // transfer folder assignment
4959 } else {
4960 emplace_back(c);
4961 }
4962 }
4963 }
4964 }
4965 /*for(auto& s : arg->servers()) {
4966 auto c = std::make_shared<xRooNode>(*s,*this);
4967 if (auto existing = findByObj(c); existing) {
4968 existing->fTimes++;
4969 existing->fFolder = c->fFolder; // transfer folder assignment
4970 } else {
4971 emplace_back(c);
4972 }
4973 }*/
4974 } else if (auto ir = get<RooStats::HypoTestInverterResult>()) {
4975 // check if we already have a hypoSpace in our memory
4976 bool hasHS = false;
4977 for (auto &c : fBrowsables) {
4978 if (strcmp(c->GetName(), ".memory") == 0 && c->get<xRooHypoSpace>()) {
4979 hasHS = true;
4980 break;
4981 }
4982 }
4983 if (!hasHS) {
4984 // add the HS
4985 auto hs =
4986 fBrowsables.emplace_back(std::make_shared<xRooNode>(".memory", std::make_shared<xRooHypoSpace>(ir), *this));
4987 // add the hypoPoints first so they appear first
4988 auto _axes = hs->get<xRooHypoSpace>()->axes();
4989
4990 int i = 0;
4991 for (auto &hp : *hs->get<xRooHypoSpace>()) {
4992 TString coordString;
4993 for (auto a : _axes) {
4994 if (a != _axes.first())
4995 coordString += ",";
4996 coordString +=
4997 TString::Format("%s=%g", a->GetName(), hp.coords->getRealValue(a->GetName(), ir->GetXValue(i)));
4998 }
4999 auto hpn = emplace_back(std::make_shared<xRooNode>(coordString, hp.hypoTestResult, hs));
5000 hpn->fTimes++;
5001 hpn->fBrowsables.emplace_back(std::make_shared<xRooNode>(
5002 ".memory", std::shared_ptr<xRooNLLVar::xRooHypoPoint>(&hp, [](xRooNLLVar::xRooHypoPoint *) {}), hpn));
5003 i++;
5004 }
5005 } else {
5006 // ensure all hypoTestResults are flagged as keep-alive
5007 std::vector<std::shared_ptr<xRooNode>> &nn = *this;
5008 for (auto &c : nn) {
5009 if (c->get<RooStats::HypoTestResult>())
5010 c->fTimes++;
5011 }
5012 }
5013 // xRooNode tests;
5014 // for(int i=0;i<ir->ArraySize();i++) {
5015 // tests.push_back(std::make_shared<xRooNode>(TString::Format("%g",ir->GetXValue(i)),*ir->GetResult(i),*this));
5016 // }
5017 // appendChildren(tests);
5018 } else if (get<RooStats::HypoTestResult>()) {
5019
5020 // create the xRooHypoPoint if necessary
5021 xRooNLLVar::xRooHypoPoint *hp = nullptr;
5022 for (auto &c : fBrowsables) {
5023 if (strcmp(c->GetName(), ".memory") == 0 && c->get<xRooNLLVar::xRooHypoPoint>()) {
5024 hp = c->get<xRooNLLVar::xRooHypoPoint>();
5025 c->fTimes++; // keep it alive
5026 break;
5027 }
5028 }
5029 if (!hp) {
5030 auto shp =
5031 std::make_shared<xRooNLLVar::xRooHypoPoint>(std::dynamic_pointer_cast<RooStats::HypoTestResult>(fComp));
5032 fBrowsables.emplace_back(std::make_shared<xRooNode>(".memory", shp, *this));
5033 hp = shp.get();
5034 }
5035
5036 xRooNode fits;
5037
5038 if (auto fit = hp->ufit()) {
5039 fits.emplace_back(std::make_shared<xRooNode>(fit, *this))->TNamed::SetName("ufit");
5040 }
5041 if (auto fit = hp->cfit_null()) {
5042 fits.emplace_back(std::make_shared<xRooNode>(fit, *this))->TNamed::SetName("cfit_null");
5043 }
5044 if (auto fit = hp->cfit_alt()) {
5045 fits.emplace_back(std::make_shared<xRooNode>(fit, *this))->TNamed::SetName("cfit_alt");
5046 }
5047 if (auto fit = hp->gfit()) {
5048 fits.emplace_back(std::make_shared<xRooNode>(fit, *this))->TNamed::SetName("gfit");
5049 }
5050 if (auto asi = hp->asimov()) {
5051 auto asiP = fits.emplace_back(std::make_shared<xRooNode>(
5052 asi->hypoTestResult ? asi->hypoTestResult : std::make_shared<RooStats::HypoTestResult>(asi->result()),
5053 *this));
5054 asiP->TNamed::SetName("asimov");
5055 asiP->fBrowsables.emplace_back(std::make_shared<xRooNode>(".memory", asi, asiP));
5056 }
5057 appendChildren(fits);
5058 }
5059
5060 // clear anything that has fTimes = 0 still
5061 auto it = std::vector<std::shared_ptr<xRooNode>>::begin();
5062 while (it != std::vector<std::shared_ptr<xRooNode>>::end()) {
5063 if (it->get()->fTimes == 0) {
5064 for (auto o : *gROOT->GetListOfBrowsers()) {
5065 auto b = dynamic_cast<TBrowser *>(o);
5066 if (b && b->GetBrowserImp()) { // browserImp is null if browser was closed
5067 // std::cout << GetPath() << " Removing " << it->get()->GetPath() << std::endl;
5068
5069 if (auto _b =
5070 dynamic_cast<TGFileBrowser *>(GETACTBROWSER(dynamic_cast<TRootBrowser *>(b->GetBrowserImp())));
5071 _b) {
5072 auto _root = GETROOTDIR(_b);
5073 if (!_root)
5074 _root = GETLISTTREE(_b)->GetFirstItem();
5075 if (auto item = GETLISTTREE(_b)->FindItemByObj(_root, this); item) {
5076 GETLISTTREE(_b)->OpenItem(item);
5077 }
5078 }
5079
5080 b->RecursiveRemove(
5081 it->get()); // problem: if obj is living in a collapsed node it wont actually get deleted
5082 /*auto _b = dynamic_cast<TGFileBrowser*>( dynamic_cast<TRootBrowser*>(b->GetBrowserImp())->fActBrowser );
5083 if (_b) {
5084 std::cout << _b->fRootDir->GetText() << std::endl;
5085 if (auto item = _b->fListTree->FindItemByObj(_b->fRootDir,it->get()); item) {
5086 std::cout << "Found obj: " << item << " " << item->GetText() << std::endl;
5087 _b->fListTree->RecursiveDeleteItem(_b->fRootDir,it->get());
5088 }
5089
5090 //b->RecursiveRemove(it->get());
5091 if (auto item = _b->fListTree->FindItemByObj(_b->fRootDir,it->get()); item) {
5092 std::cout << "Still Found obj: " << item << std::endl;
5093 }
5094 _b->fListTree->ClearViewPort();
5095
5096 }*/
5097 }
5098 }
5099 /*it->get()->ResetBit(TObject::kNotDeleted); ++it;*/ it = erase(it);
5100 } else {
5101 ++it;
5102 }
5103 }
5104
5105 return *this;
5106}
5107
5108////////////////////////////////////////////////////////////////////////////////
5109/// List of observables (global and regular) of this node.
5110
5112{
5113 xRooNode out(".obs", std::make_shared<RooArgList>(), *this);
5114 out.get<RooArgList>()->setName((GetPath() + ".obs").c_str());
5115 for (auto o : vars()) {
5116 if (o->get<RooAbsArg>()->getAttribute("obs")) {
5117 out.get<RooArgList>()->add(*o->get<RooAbsArg>());
5118 out.emplace_back(o);
5119 }
5120 }
5121 return out;
5122}
5123
5124////////////////////////////////////////////////////////////////////////////////
5125/// List of global observables of this node.
5126
5128{
5129 xRooNode out(".globs", std::make_shared<RooArgList>(), *this);
5130 out.get<RooArgList>()->setName((GetPath() + ".globs").c_str());
5131 for (auto o : obs()) {
5132 if (o->get<RooAbsArg>()->getAttribute("global")) {
5133 out.get<RooArgList>()->add(*o->get<RooAbsArg>());
5134 out.emplace_back(o);
5135 }
5136 }
5137 return out;
5138}
5139
5140////////////////////////////////////////////////////////////////////////////////
5141/// List of regular observables of this node.
5142
5144{
5145 xRooNode out(".robs", std::make_shared<RooArgList>(), *this);
5146 out.get<RooArgList>()->setName((GetPath() + ".robs").c_str());
5147 for (auto o : obs()) {
5148 if (!o->get<RooAbsArg>()->getAttribute("global")) {
5149 out.get<RooArgList>()->add(*o->get<RooAbsArg>());
5150 out.emplace_back(o);
5151 }
5152 }
5153 return out;
5154}
5155
5156////////////////////////////////////////////////////////////////////////////////
5157/// List of parameters (non-observables) of this node.
5158
5160{
5161 xRooNode out(".pars", std::make_shared<RooArgList>(), *this);
5162 out.get<RooArgList>()->setName((GetPath() + ".pars").c_str());
5163 for (auto o : vars()) {
5164 if (!o->get<RooAbsArg>()->getAttribute("obs")) {
5165 out.get<RooArgList>()->add(*(o->get<RooAbsArg>()));
5166 out.emplace_back(o);
5167 }
5168 }
5169 return out;
5170}
5171
5172////////////////////////////////////////////////////////////////////////////////
5173/// List of parameters that are currently constant
5174
5176{
5177 xRooNode out(".consts", std::make_shared<RooArgList>(), *this);
5178 out.get<RooArgList>()->setName((GetPath() + ".consts").c_str());
5179 for (auto o : pars()) {
5180 if (o->get<RooAbsArg>()->getAttribute("Constant") || o->get<RooConstVar>()) {
5181 out.get<RooArgList>()->add(*o->get<RooAbsArg>());
5182 out.emplace_back(o);
5183 }
5184 }
5185 return out;
5186}
5187
5188////////////////////////////////////////////////////////////////////////////////
5189/// List of parameters that are currently non-constant
5190/// These parameters do not have the "Constant" attribute
5191
5193{
5194 xRooNode out(".floats", std::make_shared<RooArgList>(), *this);
5195 out.get<RooArgList>()->setName((GetPath() + ".floats").c_str());
5196 for (auto o : pars()) {
5197 if (!o->get<RooAbsArg>()->getAttribute("Constant") && !o->get<RooConstVar>()) {
5198 out.get<RooArgList>()->add(*o->get<RooAbsArg>());
5199 out.emplace_back(o);
5200 }
5201 }
5202 return out;
5203}
5204
5205////////////////////////////////////////////////////////////////////////////////
5206/// List of parameters of interest: parameters marked as "of interest"
5207/// These parameters have the "poi" attribute
5208
5210{
5211 xRooNode out(".poi", std::make_shared<RooArgList>(), *this);
5212 out.get<RooArgList>()->setName((GetPath() + ".poi").c_str());
5213 for (auto o : pars()) {
5214 if (o->get<RooAbsArg>()->getAttribute("poi")) {
5215 out.get<RooArgList>()->add(*o->get<RooAbsArg>());
5216 out.emplace_back(o);
5217 }
5218 }
5219 return out;
5220}
5221
5222////////////////////////////////////////////////////////////////////////////////
5223/// List of nuisance parameters: non-constant parameters that are not marked of interest,
5224/// as well as any parameters that have been marked by the "np" attribute
5225
5227{
5228 xRooNode out(".np", std::make_shared<RooArgList>(), *this);
5229 out.get<RooArgList>()->setName((GetPath() + ".np").c_str());
5230 for (auto o : pars()) {
5231 if (o->get<RooAbsArg>()->getAttribute("np") ||
5232 (!o->get<RooAbsArg>()->getAttribute("Constant") && !o->get<RooAbsArg>()->getAttribute("poi") &&
5233 !o->get<RooConstVar>())) {
5234 out.get<RooArgList>()->add(*o->get<RooAbsArg>());
5235 out.emplace_back(o);
5236 }
5237 }
5238 return out;
5239}
5240
5241////////////////////////////////////////////////////////////////////////////////
5242/// List of prespecified parameters: non-floatable parameters
5243
5245{
5246 xRooNode out(".pp", std::make_shared<RooArgList>(), *this);
5247 out.get<RooArgList>()->setName((GetPath() + ".pp").c_str());
5248 for (auto o : pars()) {
5249 if (!o->get<RooAbsArg>()->getAttribute("np") && !o->get<RooAbsArg>()->getAttribute("poi") &&
5250 (o->get<RooAbsArg>()->getAttribute("Constant") || o->get<RooConstVar>())) {
5251 out.get<RooArgList>()->add(*o->get<RooAbsArg>());
5252 out.emplace_back(o);
5253 }
5254 }
5255 return out;
5256}
5257
5258////////////////////////////////////////////////////////////////////////////////
5259/// List of variables (observables and parameters) of this node
5260
5262{
5263 xRooNode out(".vars", std::make_shared<RooArgList>(), *this);
5264 out.get<RooArgList>()->setName((GetPath() + ".vars").c_str());
5265 if (auto coll = get<RooAbsCollection>(); coll) {
5266 for (auto &x : *this) {
5267 for (auto &y : x->vars()) {
5268 out.push_back(y);
5269 }
5270 }
5271 return out;
5272 }
5273 if (auto p = get<RooAbsArg>(); p) {
5274 // also need to get all constPars so use leafNodeServerList .. will include self if is fundamental, which is what
5275 // we want
5276 // ensure all globs appear after robs, as we rely on this ordering for picking "x" var in "reduced" method
5277 xRooNode _globs;
5278 RooArgSet allLeafs;
5279 p->leafNodeServerList(&allLeafs);
5280 for (auto &c : allLeafs) {
5281 if (c->isFundamental() || (dynamic_cast<RooConstVar *>(c) && !TString(c->GetName()).IsFloat())) {
5282 if (!c->getAttribute("global")) {
5283 out.get<RooArgList>()->add(*c);
5284 out.emplace_back(std::make_shared<xRooNode>(*c, *this));
5285 }
5286 if (c->getAttribute("global")) {
5287 _globs.emplace_back(std::make_shared<xRooNode>(*c, *this));
5288 _globs.back()->fFolder = "!globs";
5289 } else if (c->getAttribute("obs"))
5290 out.back()->fFolder = "!robs";
5291 else if (c->getAttribute("poi"))
5292 out.back()->fFolder = "!poi";
5293 else if (c->getAttribute("np") ||
5294 (!c->getAttribute("Constant") && !c->getAttribute("poi") && c->IsA() != RooConstVar::Class()))
5295 out.back()->fFolder = "!np";
5296 else if (!c->getAttribute("Constant") && c->IsA() != RooConstVar::Class())
5297 out.back()->fFolder = "!floats";
5298 else
5299 out.back()->fFolder = "!pp";
5300 }
5301 }
5302 for (auto g : _globs) {
5303 out.get<RooArgList>()->add(*g->get<RooAbsArg>());
5304 out.emplace_back(g);
5305 }
5306 } else if (auto p2 = get<RooAbsData>(); p2) {
5307 for (auto a : *p2->get()) {
5308 a->setAttribute("obs");
5309 out.emplace_back(std::make_shared<xRooNode>(*a, *this));
5310 out.get<RooArgList>()->add(*a);
5311 }
5312 if (auto _dglobs = p2->getGlobalObservables()) {
5313 for (auto &a : *_dglobs) {
5314 a->setAttribute("obs");
5315 a->setAttribute("global");
5316 out.emplace_back(std::make_shared<xRooNode>(*a, *this));
5317 out.get<RooArgList>()->add(*a);
5318 }
5319 } else if (auto _globs = find(".globs"); _globs && _globs->get<RooAbsCollection>()) {
5320 for (auto &a : *_globs->get<RooAbsCollection>()) {
5321 a->setAttribute("obs");
5322 a->setAttribute("global");
5323 out.emplace_back(std::make_shared<xRooNode>(*a, *this));
5324 out.get<RooArgList>()->add(*a);
5325 }
5326 } else if (auto _ws = ws(); _ws) {
5327 if (auto _globs2 = dynamic_cast<RooArgSet *>(GETWSSNAPSHOTS(_ws).find(p2->GetName())); _globs2) {
5328 for (auto a : *_globs2) {
5329 a->setAttribute("obs");
5330 a->setAttribute("global");
5331 out.emplace_back(std::make_shared<xRooNode>(*a, *this));
5332 out.get<RooArgList>()->add(*a);
5333 }
5334 } else if (auto _gl = GETWSSETS(_ws).find("globalObservables"); _gl != GETWSSETS(_ws).end()) {
5335 for (auto &_g : _gl->second) {
5336 auto _clone = std::shared_ptr<RooAbsArg>(dynamic_cast<RooAbsArg *>(_g->Clone(_g->GetName())));
5337 if (auto v = std::dynamic_pointer_cast<RooAbsRealLValue>(_clone); v && _g->getStringAttribute("nominal"))
5338 v->setVal(TString(_g->getStringAttribute("nominal")).Atof());
5339 out.emplace_back(std::make_shared<xRooNode>(_clone, *this));
5340 out.get<RooArgList>()->add(*_clone);
5341 }
5342 } else if (fParent) {
5343 // note: this is slow in large workspaces ... too many obs to look through?
5344 std::unique_ptr<RooAbsCollection> _globs3(fParent->obs().get<RooArgList>()->selectByAttrib("global", true));
5345 // std::unique_ptr<RooAbsCollection> _globs(_ws->allVars().selectByAttrib("global",true)); - tried this to
5346 // be quicker but it wasn't
5347 for (auto &_g : *_globs3) {
5348 auto _clone = std::shared_ptr<RooAbsArg>(dynamic_cast<RooAbsArg *>(_g->Clone(_g->GetName())));
5349 if (auto v = std::dynamic_pointer_cast<RooAbsRealLValue>(_clone); v && _g->getStringAttribute("nominal"))
5350 v->setVal(TString(_g->getStringAttribute("nominal")).Atof());
5351 out.emplace_back(std::make_shared<xRooNode>(_clone, *this));
5352 out.get<RooArgList>()->add(*_clone);
5353 }
5354 }
5355 }
5356 } else if (auto w = get<RooWorkspace>(); w) {
5357 for (auto a : w->allVars()) {
5358 out.emplace_back(std::make_shared<xRooNode>(*a, *this));
5359 out.get<RooArgList>()->add(*a);
5360 }
5361 // add all cats as well
5362 for (auto a : w->allCats()) {
5363 out.emplace_back(std::make_shared<xRooNode>(*a, *this));
5364 out.get<RooArgList>()->add(*a);
5365 }
5366 }
5367 return out;
5368}
5369
5371{
5372 xRooNode out(".components", nullptr, *this);
5373
5374 if (auto p = get<RooAddPdf>(); p) {
5375 // only add each pdf once (the coefs will be accumulated in coefs() method) ...
5376 std::set<RooAbsArg *> donePdfs;
5377 for (auto &o : p->pdfList()) {
5378 if (donePdfs.count(o))
5379 continue;
5380 out.emplace_back(std::make_shared<xRooNode>(*o, *this));
5381 donePdfs.insert(o);
5382 }
5383 } else if (auto p2 = get<RooRealSumPdf>(); p2) {
5384 // check for common prefixes and suffixes, will use to define aliases to shorten names
5385 // if have more than 1 function
5386 // TString commonPrefix=""; TString commonSuffix="";
5387 // if (p->funcList().size() > 1) {
5388 // bool checked=false;
5389 // for(auto& o : p->funcList()) {
5390 // if (!checked) {
5391 // commonPrefix = o->GetName(); commonSuffix = o->GetName(); checked=true;
5392 // } else {
5393 //
5394 // }
5395 // }
5396 // }
5397 std::set<RooAbsArg *> doneFuncs;
5398 for (auto &o : p2->funcList()) {
5399 if (doneFuncs.count(o))
5400 continue;
5401 out.emplace_back(std::make_shared<xRooNode>(*o, *this));
5402 doneFuncs.insert(o);
5403 }
5404 } else if (auto p3 = get<RooAddition>(); p3) {
5405 for (auto &o : p3->list()) {
5406 out.emplace_back(std::make_shared<xRooNode>(*o, *this));
5407 }
5408 } else if (auto p4 = get<RooAbsCollection>(); p4) {
5409 for (auto &a : *p4) {
5410 out.emplace_back(std::make_shared<xRooNode>(*a, *this));
5411 }
5412 } else if (auto p5 = get<RooWorkspace>(); p5) {
5413 for (auto &o : p5->components()) {
5414 // only top-level nodes (only clients are integrals or things that aren't part of the workspace)
5415 // if (o->hasClients()) continue;
5416 bool hasClients = false;
5417 for (auto &c : o->clients())
5418 if (!c->InheritsFrom("RooRealIntegral") && p5 == GETWS(o)) {
5419 hasClients = true;
5420 break;
5421 }
5422 if (hasClients)
5423 continue;
5424 out.emplace_back(std::make_shared<xRooNode>(*o, *this));
5425 if (o->InheritsFrom("RooAbsPdf"))
5426 out.back()->fFolder = "!models";
5427 else
5428 out.back()->fFolder = "!scratch";
5429 }
5430 for (auto &o : p5->allGenericObjects()) {
5431 if (auto fr = dynamic_cast<RooFitResult *>(o); fr) {
5432 TString s(fr->GetTitle());
5433 if (s.Contains(';'))
5434 s = s(0, s.Index(';'));
5435 if (auto _pdf = out.find(s.Data()); _pdf) {
5436 // std::cout << " type = " << _pdf->get()->ClassName() << std::endl;
5437 out.emplace_back(std::make_shared<xRooNode>(fr->GetName(), *fr, _pdf));
5438 // for a while, this node's parent pointed to something of type Node2!!
5439 // how to fix??? - I fxied it with a new constructor to avoid the shared_ptr<Node2> calling the const
5440 // Node2& constructor via getting wrapped in a Node2(shared_ptr<TObject>) call
5441 // out.back()->fParent = _pdf;
5442 // std::cout << " type2 = " << out.back()->fParent->get()->ClassName() << std::endl;
5443 } else {
5444 out.emplace_back(std::make_shared<xRooNode>(fr->GetName(), *fr, *this));
5445 }
5446 out.back()->fFolder = "!fits";
5447 } else {
5448 out.emplace_back(std::make_shared<xRooNode>(*o, *this));
5449 if (strcmp(out.back()->get()->ClassName(), "TStyle") == 0) {
5450 out.back()->fFolder = "!styles";
5451 } else if (strcmp(out.back()->get()->ClassName(), "RooStats::HypoTestInverterResult") == 0) {
5452 out.back()->fFolder = "!scans";
5453 } else {
5454 out.back()->fFolder = "!objects";
5455 }
5456 }
5457 }
5458 for (auto &[k, v] : GETWSSETS(p5)) {
5459 // skip 'CACHE' sets because they are auto-removed when sanitizing workspaces, which will invalidate these
5460 // children
5461 if (k.find("CACHE_") == 0)
5462 continue;
5463 out.emplace_back(std::make_shared<xRooNode>(k.c_str(), v, *this));
5464 out.back()->fFolder = "!sets";
5465 }
5466
5467 RooLinkedList snaps = GETWSSNAPSHOTS(p5);
5468 std::unique_ptr<TIterator> iter(snaps.MakeIterator());
5469 TObject *snap;
5470 while ((snap = iter->Next())) {
5471 out.emplace_back(std::make_shared<xRooNode>(*snap, *this));
5472 out.back()->fFolder = "!snapshots";
5473 }
5474 } else if (strlen(GetName()) > 0 && GetName()[0] == '!' && fParent) {
5475 // special case of dynamic property
5476 if (TString(GetName()) == "!.pars") {
5477 for (auto &c : fParent->pars()) {
5478 out.emplace_back(c);
5479 }
5480 } else {
5481 // the components of a folder are the children of the parent (after browsing) that live in this folder
5482 fParent->browse();
5483 for (auto &c : *fParent) {
5484 if (c->fFolder == GetName()) {
5485 out.emplace_back(c);
5486 }
5487 }
5488 }
5489 }
5490
5491 return out;
5492}
5493
5494////////////////////////////////////////////////////////////////////////////////
5495/// bins of a channel or sample, or channels of a multi-channel pdf
5496
5498{
5499 xRooNode out(".bins", nullptr, *this);
5500
5501 if (auto p = get<RooSimultaneous>(); p) {
5502 std::map<int, std::shared_ptr<xRooNode>> cats; // fill into a map to preserve index ordering
5503 for (auto &c : p->indexCat()) { // is alphabetical in labels
5504 auto pp = p->getPdf(c.first.c_str());
5505 if (!pp)
5506 continue;
5507 cats[c.second] =
5508 std::make_shared<xRooNode>(TString::Format("%s=%s", p->indexCat().GetName(), c.first.c_str()), *pp, *this);
5509 }
5510 for (auto &[_, n] : cats)
5511 out.emplace_back(n);
5512 } else if (auto phf = get<ParamHistFunc>(); phf) {
5513 int i = 1;
5514#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
5515 auto &pSet = phf->_paramSet;
5516#else
5517 auto &pSet = phf->paramList();
5518#endif
5519 for (auto par : pSet) {
5520 out.emplace_back(std::make_shared<xRooNode>(*par, *this));
5521 out.back()->fBinNumber = i;
5522 i++;
5523 }
5524 } else if (auto ax = GetXaxis(); ax) {
5525 for (int i = 1; i <= ax->GetNbins(); i++) {
5526 // create a RooProduct of all bin-specific factors of all shapeFactors
5527 std::vector<RooAbsArg *> _factors;
5528 for (auto f : factors()) {
5529 if (f->get<ParamHistFunc>()) {
5530 if (f->bins()[i - 1]->get<RooProduct>())
5531 for (auto &ss : f->bins()[i - 1]->factors())
5532 _factors.push_back(ss->get<RooAbsArg>());
5533 else
5534 _factors.push_back(f->bins()[i - 1]->get<RooAbsArg>());
5535 }
5536 }
5537 out.emplace_back(std::make_shared<xRooNode>(
5538 TString::Format("%g<=%s<%g", ax->GetBinLowEdge(i), ax->GetParent()->GetName(), ax->GetBinLowEdge(i + 1)),
5539 _factors.empty() ? nullptr
5540 : std::make_shared<RooProduct>(TString::Format("%s.binFactors.bin%d", GetName(), i),
5541 "binFactors", RooArgList()),
5542 *this));
5543 for (auto f : _factors) {
5544#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
5545 out.back()->get<RooProduct>()->_compRSet.add(*f);
5546#else
5547 const_cast<RooArgList &>(out.back()->get<RooProduct>()->realComponents()).add(*f);
5548#endif
5549 }
5550 out.back()->fBinNumber = i;
5551 }
5552 }
5553
5554 return out;
5555}
5556
5558{
5560 bool isResidual = false;
5561
5562 // if parent is a sumpdf or addpdf then include the coefs
5563 // if func appears multiple times then coefs must be combined into a RooAddition temporary
5564 if (fParent) {
5565 // handle case where filters are applied .. need to pass through these
5566 // do this by iterating while fComp is null
5567 auto parent = fParent;
5568 if (!parent->fComp) {
5569 while (!parent->fComp && parent->fParent) {
5570 parent = parent->fParent;
5571 }
5572 // parent should now be node above the filters ... need parent of that
5573 parent = parent->fParent;
5574 if (!parent)
5575 parent = fParent; // revert t original parent in case something went wrong
5576 }
5577 if (auto p = parent->get<RooRealSumPdf>(); p) {
5578 std::size_t i = 0;
5579 for (auto &o : p->funcList()) {
5580 if (o == get()) {
5581 if (i >= p->coefList().size()) {
5582 isResidual = true;
5583 coefs.add(p->coefList());
5584 } else {
5585 coefs.add(*p->coefList().at(i));
5586 }
5587 }
5588 i++;
5589 }
5590 } else if (auto p2 = parent->get<RooAddPdf>(); p2) {
5591 std::size_t i = 0;
5592 if (p2->coefList().empty()) {
5593 // this can happen if all pdfs are extended then the coef is effectively the
5594 // expected number of events
5595 // TODO: test behaviour of xRooNode under this scenario (are histograms correct?)
5596 } else {
5597 for (auto &o : p2->pdfList()) {
5598 if (o == get()) {
5599 if (i >= p2->coefList().size()) {
5600 isResidual = true;
5601 coefs.add(p2->coefList());
5602 } else {
5603 coefs.add(*p2->coefList().at(i));
5604 }
5605 }
5606 i++;
5607 }
5608 }
5609 }
5610 }
5611 if (isResidual) {
5612 // return a node representing 1.-sumOfCoefs
5613 // involves creating sumOfCoefs unless there is only 1 coef, then just use that
5614 auto coefSum = coefs.empty()
5615 ? nullptr
5616 : (coefs.size() == 1 ? std::shared_ptr<RooAbsArg>(coefs.at(0), [](RooAbsArg *) {})
5617 : std::make_shared<RooAddition>((isResidual) ? ".sumOfCoefs" : ".coefs",
5618 "Coefficients of", coefs));
5619 xRooNode out(".coef", coefSum ? std::dynamic_pointer_cast<RooAbsArg>(std::make_shared<RooFormulaVar>(
5620 ".coef", "1-sum(otherCoefs)", "1. - @0", *coefSum))
5621 : nullptr /* should we return a "1." instead? */);
5622 if (coefSum && coefs.size() != 1) {
5623 out.emplace_back(std::make_shared<xRooNode>(".memory", nullptr, *this))
5624 ->emplace_back(
5625 std::make_shared<xRooNode>(".sumOfCoefs", coefSum, out)); // added to keep the sum alive! with the node
5626 }
5627 if (!coefs.empty()) {
5628 out.browse();
5629 }
5630 return out;
5631 } else if (coefs.size() == 1) {
5632 xRooNode out(".coef", std::shared_ptr<RooAbsArg>(coefs.at(0), [](RooAbsArg *) {}), *this);
5633 if (!coefs.empty()) {
5634 out.browse();
5635 }
5636 return out;
5637 } else {
5638 auto coefSum =
5639 coefs.empty()
5640 ? nullptr
5641 : std::make_shared<RooAddition>(".coefs", TString::Format("Coefficients of %s", GetName()), coefs);
5642 xRooNode out(".coefs", coefSum, *this);
5643 if (!coefs.empty())
5644 out.browse();
5645
5646 return out;
5647 }
5648}
5649
5651{
5652 xRooNode out(".factors", nullptr, *this);
5653
5654 if (auto p = get<RooProdPdf>(); p) {
5655 auto _main = mainChild();
5656 if (auto a = _main.get<RooRealSumPdf>(); a && !a->getStringAttribute("alias"))
5657 a->setStringAttribute("alias", "samples");
5658 else if (auto a2 = _main.get<RooAddPdf>(); a2 && !a2->getStringAttribute("alias"))
5659 a2->setStringAttribute("alias", "components");
5660 int _npdfs = p->pdfList().size();
5661 for (auto &o : p->pdfList()) {
5662 out.emplace_back(std::make_shared<xRooNode>(*o, *this));
5663 if (_npdfs > 5 && o != _main.get())
5664 out.back()->fFolder = "!constraints";
5665 }
5666 } else if (auto p2 = get<RooProduct>(); p2) {
5667 for (auto &o : p2->components()) {
5668 if (o->InheritsFrom("RooProduct")) {
5669 // get factors of this term
5670 auto x = xRooNode("tmp", *o, *this).factors();
5671 for (auto &n : x) {
5672 out.emplace_back(std::make_shared<xRooNode>(n->GetName(), n->fComp, *this));
5673 }
5674 } else {
5675 out.emplace_back(std::make_shared<xRooNode>(*o, *this));
5676 }
5677 }
5678 } else if (auto w = get<RooWorkspace>(); w) {
5679 // if workspace, return all functions (not pdfs) that have a RooProduct as one of their clients
5680 // or not clients
5681 // exclude obs and globs
5682 auto oo = obs(); // need to keep alive as may contain owning globs
5683 auto& _obs = *(oo.get<RooArgList>());
5684 for (auto a : w->allFunctions()) {
5685 if (_obs.contains(*a))
5686 continue;
5687 bool show(true);
5688 for (auto c : a->clients()) {
5689 show = false;
5690 if (c->InheritsFrom("RooProduct"))
5691 show = true;
5692 }
5693 if (show)
5694 out.emplace_back(std::make_shared<xRooNode>(*a, *this));
5695 }
5696 }
5697
5698 /*
5699 // if parent is a sumpdf or addpdf then include the coefs
5700 // if func appears multiple times then coefs must be combined into a RooAddition temporary
5701 if (fParent) {
5702 RooArgList coefs;
5703 if(auto p = fParent->get<RooRealSumPdf>();p) {
5704 int i=0;
5705 for(auto& o : p->funcList()) {
5706 if (o == get()) {
5707 coefs.add( *p->coefList().at(i) );
5708 }
5709 i++;
5710 }
5711 } else if(auto p = fParent->get<RooAddPdf>(); p) {
5712 int i=0;
5713 for(auto& o : p->pdfList()) {
5714 if (o == get()) {
5715 coefs.add( *p->coefList().at(i) );
5716 }
5717 i++;
5718 }
5719 }
5720 if (!coefs.empty()) {
5721 if (coefs.size() == 1) {
5722 if (strcmp(coefs.at(0)->GetName(),"1")) { // don't add the "1"
5723 out.emplace_back(std::make_shared<Node2>(".coef", *coefs.at(0), *this));
5724 }
5725 } else {
5726 out.emplace_back(std::make_shared<Node2>(".coefs",
5727 std::make_shared<RooAddition>(".coefs", "Coefficients of",
5728 coefs), *this));
5729 }
5730 }
5731 }
5732 */
5733 return out;
5734}
5735
5737{
5738 xRooNode out(".variations", nullptr, *this);
5739
5740 // if (auto p = get<RooSimultaneous>(); p) {
5741 // for (auto &c : p->indexCat()) {
5742 // auto pp = p->getPdf(c.first.c_str());
5743 // if (!pp)
5744 // continue;
5745 // out.emplace_back(
5746 // std::make_shared<xRooNode>(TString::Format("%s=%s", p->indexCat().GetName(), c.first.c_str()), *pp,
5747 // *this));
5748 // }
5749 // } else
5750 if (auto p2 = get<PiecewiseInterpolation>(); p2) {
5751#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
5752 out.emplace_back(std::make_shared<xRooNode>("nominal", p2->_nominal.arg(), *this));
5753#else
5754 out.emplace_back(std::make_shared<xRooNode>("nominal", *(p2->nominalHist()), *this));
5755#endif
5756 for (size_t i = 0; i < p2->paramList().size(); i++) {
5757 // TODO: should we only return one if we find they are symmetrized?
5758 out.emplace_back(std::make_shared<xRooNode>(TString::Format("%s=1", p2->paramList().at(i)->GetName()),
5759 *p2->highList().at(i), *this));
5760 out.emplace_back(std::make_shared<xRooNode>(TString::Format("%s=-1", p2->paramList().at(i)->GetName()),
5761 *p2->lowList().at(i), *this));
5762 }
5763 } else if (auto p3 = get<RooStats::HistFactory::FlexibleInterpVar>(); p3) {
5764#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
5765 out.emplace_back(std::make_shared<xRooNode>("nominal", RooFit::RooConst(p3->_nominal), *this));
5766 for (size_t i = 0; i < p3->_paramList.size(); i++) {
5767 out.emplace_back(std::make_shared<xRooNode>(TString::Format("%s=1", p3->_paramList.at(i)->GetName()),
5768 RooFit::RooConst(p3->_high.at(i)), *this));
5769 out.emplace_back(std::make_shared<xRooNode>(TString::Format("%s=-1", p3->_paramList.at(i)->GetName()),
5770 RooFit::RooConst(p3->_low.at(i)), *this));
5771 }
5772#else
5773 out.emplace_back(std::make_shared<xRooNode>("nominal", RooFit::RooConst(p3->nominal()), *this));
5774 for (size_t i = 0; i < p3->variables().size(); i++) {
5775 out.emplace_back(std::make_shared<xRooNode>(TString::Format("%s=1", p3->variables().at(i)->GetName()),
5776 RooFit::RooConst(p3->high().at(i)), *this));
5777 out.emplace_back(std::make_shared<xRooNode>(TString::Format("%s=-1", p3->variables().at(i)->GetName()),
5778 RooFit::RooConst(p3->low().at(i)), *this));
5779 }
5780#endif
5781
5782 } else if (auto p4 = get<ParamHistFunc>(); p4) {
5783 // I *think* I put this here so that can browse into a ParamHistFunc
5784 // int i = 0;
5785 // for (auto par : p4->_paramSet) {
5786 // TString _name = par->GetName();
5787 // // if(auto _v = dynamic_cast<RooRealVar*>(p->_dataSet.get(i)->first()); _v) {
5788 // // _name = TString::Format("%s=%g",_v->GetName(),_v->getVal());
5789 // // }
5790 // // out.emplace_back(std::make_shared<xRooNode>(_name,*par,*this)); -- -removed cos now have bin()
5791 // method i++;
5792 // }
5793 }
5794 return out;
5795}
5796
5798{
5799 RooArgList out;
5800 out.setName(GetName());
5801 for (auto &k : *this) {
5802 if (auto o = k->get<RooAbsArg>(); o)
5803 out.add(*o);
5804 }
5805 return out;
5806}
5807
5809{
5810 xRooNode out(".datasets()", nullptr, *this);
5811 out.fBrowseOperation = [](xRooNode *f) { return f->fParent->datasets(); };
5812
5813 if (auto _ws = get<RooWorkspace>(); _ws) {
5814 for (auto &d : _ws->allData()) {
5815 out.emplace_back(std::make_shared<xRooNode>(*d, *this));
5816 out.back()->fFolder = "!datasets";
5817 }
5818 } else if (auto __ws = ws(); __ws) {
5819 if (get<RooAbsPdf>() ||
5820 (!get() && fParent &&
5821 fParent->get<RooAbsPdf>())) { // second condition handles 'bins' nodes of pdf, which have null ptr
5822 // only add datasets that have observables that cover all our observables
5823 auto oo = obs(); // must keep alive in case is owning the globs
5824 RooArgSet _obs(*oo.get<RooArgList>());
5825 //_obs.add(coords(true).argList(), true); // include coord observables too, and current xaxis if there's one -
5826 // added in loop below
5827
5828 TString cut;
5829 RooArgSet cutobs;
5830 for (auto _c : coords()) { // coords() moves vars to their respective coordinates too
5831 if (auto _cat = _c->get<RooAbsCategoryLValue>(); _cat) {
5832 if (cut != "")
5833 cut += " && ";
5834 cut += TString::Format("%s==%d", _cat->GetName(), _cat->getCurrentIndex());
5835 _obs.add(*_cat,
5836 true); // note: if we ever changed coords to return clones, would need to keep coords alive
5837 cutobs.add(*_cat);
5838 } else if (auto _rv = _c->get<RooAbsRealLValue>(); _rv) {
5839 // todo: check coordRange is a single range rather than multirange
5840 if (cut != "")
5841 cut += " && ";
5842 cut +=
5843 TString::Format("%s>=%f&&%s<%f", _rv->GetName(), _rv->getMin(_rv->getStringAttribute("coordRange")),
5844 _rv->GetName(), _rv->getMax(_rv->getStringAttribute("coordRange")));
5845 _obs.add(*_rv,
5846 true); // note: if we ever changed coords to return clones, would need to keep coords alive
5847 cutobs.add(*_rv);
5848 } else {
5849 throw std::runtime_error("datasets(): Unsupported coordinate type");
5850 }
5851 }
5852 if (auto s = get<RooSimultaneous>()) {
5853 // check if we have a pdf for every category ... if not then add to cut
5854 bool hasMissing = false;
5855 TString extraCut = "";
5856 for (auto cat : s->indexCat()) {
5857 if (!s->getPdf(cat.first.c_str())) {
5858 hasMissing = true;
5859 } else {
5860 if (extraCut != "")
5861 extraCut += " || ";
5862 extraCut += TString::Format("%s==%d", s->indexCat().GetName(), cat.second);
5863 }
5864 }
5865 if (hasMissing) {
5866 if (cut != "")
5867 cut += " && ";
5868 cut += "(" + extraCut + ")";
5869 cutobs.add(s->indexCat());
5870 }
5871 }
5872
5873 if (auto ax = GetXaxis(); ax && dynamic_cast<RooAbsArg *>(ax->GetParent())->getAttribute("obs")) {
5874 auto a = dynamic_cast<RooAbsArg *>(ax->GetParent());
5875 _obs.add(*a, true);
5876 }
5877 xRooNode _wsNode(*__ws, *this);
5878 for (auto &d : _wsNode.datasets()) {
5879 if (std::unique_ptr<RooAbsCollection>(d->obs().argList().selectCommon(_obs))->size() == _obs.size()) {
5880 // all obs present .. include
5881
5882 if (cut != "") {
5883 RooFormulaVar cutFormula("cut1", cut, cutobs); // doing this to avoid complaints about unused vars
5884 // TODO: Could consider using a 'filter' node (see filter() method) applied to the dataset instead
5885 // of creating and using a reduced dataset here
5886 out.emplace_back(std::make_shared<xRooNode>(
5887 std::shared_ptr<RooAbsData>(d->get<RooAbsData>()->reduce(*std::unique_ptr<RooAbsCollection>(d->robs().get<RooArgList>()->selectCommon(_obs)),cutFormula)), *this));
5888 // put a subset of the globs in the returned dataset too
5889 out.back()->get<RooAbsData>()->setGlobalObservables(*std::unique_ptr<RooAbsCollection>(d->globs().get<RooArgList>()->selectCommon(*globs().get<RooArgList>())));
5890 if (d->get()->TestBit(1 << 20))
5891 out.back()->get()->SetBit(1 << 20);
5892 // need to attach the original dataset so that things like SetBinContent can interact with it
5893 out.back()->fBrowsables.emplace_back(std::make_shared<xRooNode>(".sourceds", d->fComp, *this));
5894 } else {
5895 out.emplace_back(std::make_shared<xRooNode>(d->fComp, *this));
5896 }
5897 }
5898 }
5899 } /*else if(auto p = get<RooFitResult>(); p) {
5900 // look for datasets in workspace that match the fit result name after hashing
5901 for(auto& _d : xRooNode(*_ws,*this).datasets()) {
5902 auto _hash = RooAbsTree::nameToHash(_d->get()->GetName());
5903 if (TString::Format("%d;%d",_hash.first,_hash.second) == p->GetTitle()) {
5904 out.emplace_back(std::make_shared<xRooNode>(_d->fComp, *this));
5905 }
5906 }
5907 }*/
5908 }
5909
5910 return out;
5911}
5912
5913std::shared_ptr<xRooNode> xRooNode::getBrowsable(const char *name) const
5914{
5915 for (auto b : fBrowsables)
5916 if (b && strcmp(b->GetName(), name) == 0)
5917 return b;
5918 return nullptr;
5919}
5920
5921TGraph *xRooNode::BuildGraph(RooAbsLValue *v, bool includeZeros, TVirtualPad *fromPad) const
5922{
5923
5924 if (auto fr = get<RooFitResult>(); fr) {
5925 return nullptr;
5926 }
5927
5928 if (auto theData = get<RooDataSet>(); theData) {
5929
5930 TH1 *theHist = nullptr;
5931
5932 if (fromPad) {
5933 // find first histogram in pad
5934 for (auto o : *fromPad->GetListOfPrimitives()) {
5935 theHist = dynamic_cast<TH1 *>(o);
5936 if (theHist) {
5937 theHist = (TH1 *)theHist->Clone();
5938 theHist->Reset();
5939 break;
5940 } // clone because theHist gets deleted below
5941 }
5942 }
5943
5944 if (!theHist) {
5945 auto _parentPdf = parentPdf();
5946 if (!_parentPdf) {
5947 // can still build graph if v is an obs ... will use v binning
5948 auto vo = dynamic_cast<TObject *>(v);
5949 if (v && obs().find(vo->GetName())) {
5950 if (auto cat = dynamic_cast<RooAbsCategoryLValue *>(v)) {
5951 theHist = new TH1D(
5952 TString::Format("%s_%s", GetName(), vo->GetName()),
5953 TString::Format("my temp hist;%s", strlen(vo->GetTitle()) ? vo->GetTitle() : vo->GetName()),
5954 cat->numTypes(), 0, cat->numTypes());
5955 for (int i = 0; i < cat->numTypes(); i++) {
5956 cat->setBin(i);
5957 theHist->GetXaxis()->SetBinLabel(i + 1, cat->getLabel());
5958 }
5959 } else {
5960 auto _binning = v->getBinningPtr(nullptr);
5961 if(_binning->isUniform()) {
5962 theHist = new TH1D(
5963 TString::Format("%s_%s", GetName(), vo->GetName()),
5964 TString::Format("my temp hist;%s", strlen(vo->GetTitle()) ? vo->GetTitle() : vo->GetName()),
5965 v->numBins(), _binning->lowBound(), _binning->highBound());
5966 } else {
5967 theHist = new TH1D(
5968 TString::Format("%s_%s", GetName(), vo->GetName()),
5969 TString::Format("my temp hist;%s", strlen(vo->GetTitle()) ? vo->GetTitle() : vo->GetName()),
5970 v->numBins(), _binning->array());
5971 }
5972 }
5973 } else {
5974 throw std::runtime_error("Cannot draw dataset without parent PDF");
5975 }
5976 } else {
5977 theHist = _parentPdf->BuildHistogram(v, true);
5978 }
5979 }
5980 if (!theHist)
5981 return nullptr;
5982 // this hist will get filled with w*x to track weighted x position per bin
5983 TH1 *xPos = (TH1 *)theHist->Clone("xPos");
5984 xPos->Reset();
5985 TH1 *xPos2 = (TH1 *)theHist->Clone("xPos2");
5986 xPos2->Reset();
5987 auto nHist = std::unique_ptr<TH1>((TH1 *)theHist->Clone("nEntries"));
5988 nHist->Reset();
5989
5990 auto dataGraph = new TGraphAsymmErrors;
5991 dataGraph->SetEditable(false);
5992 dataGraph->SetName(GetName());
5993 dataGraph->SetTitle(strlen(theData->GetTitle()) ? theData->GetTitle() : theData->GetName());
5994 // next line triggers creation of the histogram inside the graph, in root 6.22 that isn't protected from being
5995 // added to gDirectory
5996 dataGraph->SetTitle(TString::Format("%s;%s;Events", dataGraph->GetTitle(), theHist->GetXaxis()->GetTitle()));
5997 *static_cast<TAttMarker *>(dataGraph) = *static_cast<TAttMarker *>(theHist);
5998 *static_cast<TAttLine *>(dataGraph) = *static_cast<TAttLine *>(theHist);
5999 dataGraph->SetMarkerStyle(20);
6000 dataGraph->SetLineColor(kBlack);
6001
6002 auto _obs = obs();
6003
6004 // auto x = theData->get()->find((v) ? dynamic_cast<TObject*>(v)->GetName() : theHist->GetXaxis()->GetName());
6005 // const RooAbsReal* xvar = (x) ? dynamic_cast<RooAbsReal*>(x) : nullptr;
6006 // const RooAbsCategory* xcat = (x && !xvar) ? dynamic_cast<RooAbsCategory*>(x) : nullptr;
6007 auto x = _obs.find((v) ? dynamic_cast<TObject *>(v)->GetName() : theHist->GetXaxis()->GetName());
6008 if (x && x->get<RooAbsArg>()->getAttribute("global")) {
6009 // is global observable ...
6010 dataGraph->SetPoint(0, x->get<RooAbsReal>()->getVal(), 1e-15);
6011 dataGraph->SetTitle(TString::Format("%s = %f", dataGraph->GetTitle(), dataGraph->GetPointX(0)));
6012 delete xPos;
6013 delete xPos2;
6014 delete theHist;
6015 return dataGraph;
6016 }
6017
6018 const RooAbsReal *xvar = (x) ? x->get<RooAbsReal>() : nullptr;
6019 const RooAbsCategory *xcat = (x && !xvar) ? x->get<RooAbsCategory>() : nullptr;
6020
6021 auto _coords = coords();
6022
6023 TString pName((fromPad) ? fromPad->GetName() : "");
6024 auto _pos = pName.Index('=');
6025
6026 int nevent = theData->numEntries();
6027 for (int i = 0; i < nevent; i++) {
6028 theData->get(i);
6029 bool _skip = false;
6030 for (auto _c : _coords) {
6031 if (auto cat = _c->get<RooAbsCategoryLValue>(); cat) {
6032 if (cat->getIndex() != theData->get()->getCatIndex(cat->GetName())) {
6033 _skip = true;
6034 break;
6035 }
6036 } else if (auto rv = _c->get<RooAbsRealLValue>(); rv) {
6037 // must be in range
6038 if (!rv->inRange(theData->get()->getRealValue(rv->GetName()), rv->getStringAttribute("coordRange"))) {
6039 _skip = true;
6040 break;
6041 }
6042 }
6043 }
6044 if (_pos != -1) {
6045 if (auto cat = dynamic_cast<RooAbsCategory *>(theData->get()->find(TString(pName(0, _pos))));
6046 cat && cat->getLabel() != pName(_pos + 1, pName.Length())) {
6047 _skip = true;
6048 }
6049 }
6050 if (_skip)
6051 continue;
6052
6053 if (xvar) {
6054 xPos->Fill(xvar->getVal(), xvar->getVal() * theData->weight());
6055 xPos2->Fill(xvar->getVal(), pow(xvar->getVal(), 2) * theData->weight());
6056 }
6057
6058 if (xcat) {
6059 theHist->Fill(xcat->getLabel(), theData->weight());
6060 nHist->Fill(xcat->getLabel(), 1);
6061 } else {
6062 theHist->Fill((x) ? xvar->getVal() : 0.5, theData->weight());
6063 nHist->Fill((x) ? xvar->getVal() : 0.5, 1);
6064 }
6065 }
6066
6067 xPos->Divide(theHist);
6068 xPos2->Divide(theHist);
6069
6070 // update the x positions to the means for each bin and use poisson asymmetric errors for data ..
6071 for (int i = 0; i < theHist->GetNbinsX(); i++) {
6072 if (includeZeros || nHist->GetBinContent(i + 1)) {
6073 double val = theHist->GetBinContent(i + 1);
6074
6075 dataGraph->SetPoint(dataGraph->GetN(),
6076 (xvar && val) ? xPos->GetBinContent(i + 1) : theHist->GetBinCenter(i + 1), val);
6077
6078 // x-error will be the (weighted) standard deviation of the x values ...
6079 double xErr = xPos2->GetBinContent(i + 1) - pow(xPos->GetBinContent(i + 1), 2);
6080 xErr = (xErr <= 0) ? 0. : sqrt(xErr); // protects against floating point rounding effects
6081
6082 if (xErr || val)
6083 dataGraph->SetPointError(dataGraph->GetN() - 1, xErr, xErr,
6084 val - 0.5 * TMath::ChisquareQuantile(TMath::Prob(1, 1) / 2., 2. * (val)),
6085 0.5 * TMath::ChisquareQuantile(1. - TMath::Prob(1, 1) / 2., 2. * (val + 1)) -
6086 val);
6087 }
6088 }
6089
6090 // transfer limits from theHist to dataGraph hist
6091 dataGraph->GetHistogram()->GetXaxis()->SetLimits(theHist->GetXaxis()->GetXmin(), theHist->GetXaxis()->GetXmax());
6092 // and bin labels, if any
6093 if (xcat) {
6094 dataGraph->GetHistogram()->GetXaxis()->Set(theHist->GetNbinsX(), 0, theHist->GetNbinsX());
6095 for (int i = 1; i <= theHist->GetNbinsX(); i++)
6096 dataGraph->GetHistogram()->GetXaxis()->SetBinLabel(i, theHist->GetXaxis()->GetBinLabel(i));
6097 }
6098
6099 delete xPos;
6100 delete xPos2;
6101 delete theHist;
6102
6103 // std::shared_ptr<TStyle> style; // use to keep alive for access from GetStyle below, in case getObject
6104 // has decided to return the owning ptr (for some reason) std::string _title =
6105 // strlen(dataGraph->GetTitle()) ? dataGraph->GetTitle() : GetName(); if (!gROOT->GetStyle(_title.c_str()))
6106 // {
6107 // if ( (style = getObject<TStyle>(_title)) ) {
6108 // // loaded style (from workspace?) so put in list and use that
6109 // gROOT->GetListOfStyles()->Add(style.get());
6110 // } else {
6111 // // create new style - gets put in style list automatically so don't have to delete
6112 // // acquire them so saved to workspaces for auto reload ...
6113 // style = const_cast<xRooNode&>(*this).acquireNew<TStyle>(_title.c_str(),
6114 // TString::Format("Style for %s component", _title.c_str()));
6115 // (TAttLine &) (*style) = *dynamic_cast<TAttLine *>(dataGraph);
6116 // (TAttFill &) (*style) = *dynamic_cast<TAttFill *>(dataGraph);
6117 // (TAttMarker &) (*style) = *dynamic_cast<TAttMarker *>(dataGraph);
6118 // gROOT->GetListOfStyles()->Add(style.get());
6119 // }
6120 // }
6121 auto _style = style(dataGraph);
6122 if (_style) {
6123 *dynamic_cast<TAttLine *>(dataGraph) = *_style;
6124 *dynamic_cast<TAttFill *>(dataGraph) = *_style;
6125 *dynamic_cast<TAttMarker *>(dataGraph) = *_style;
6126 }
6127 return dataGraph;
6128 }
6129
6130 throw std::runtime_error("Cannot build graph");
6131}
6132
6134{
6135 if (fr) {
6136 if (auto _w = ws(); _w) {
6137 auto res = acquire(std::shared_ptr<RooFitResult>(const_cast<RooFitResult *>(fr), [](RooFitResult *) {}));
6138 for (auto o : _w->allGenericObjects()) {
6139 if (auto _fr = dynamic_cast<RooFitResult *>(o); _fr) {
6140 _fr->ResetBit(1 << 20);
6141 }
6142 }
6143 res->SetBit(1 << 20);
6144 // assign values
6145 auto allVars = _w->allVars();
6146 allVars = fr->floatParsFinal();
6147 allVars = fr->constPars();
6148 } else {
6149 // need to add to memory as a specific name
6150 throw std::runtime_error("Not supported yet"); // complication is how to replace an existing fitResult in
6151 // .memory auto _clone = std::make_shared<RooFitResult>(*fr);
6152 //_clone->SetName("fitResult");
6153 }
6154 } else {
6155 SetFitResult(fitResult("prefit").get<RooFitResult>());
6156 }
6157}
6158
6160{
6161 if (auto _fr = fr.get<const RooFitResult>()) {
6162 SetFitResult(_fr);
6163 } else
6164 throw std::runtime_error("Not a RooFitResult");
6165}
6166
6167xRooNode xRooNode::fitResult(const char *opt) const
6168{
6169
6170 if (get<RooFitResult>())
6171 return *this;
6172 if (get<RooAbsData>()) {
6173 if (auto _fr = find(".fitResult"); _fr)
6174 return _fr;
6175#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 26, 00)
6176 // check if weightVar of RooAbsData has fitResult attribute on it, will be the generation fit result
6177 if (get<RooDataSet>() && get<RooDataSet>()->weightVar() &&
6178 get<RooDataSet>()->weightVar()->getStringAttribute("fitResult")) {
6179 return xRooNode(getObject<const RooFitResult>(get<RooDataSet>()->weightVar()->getStringAttribute("fitResult")),
6180 *this);
6181 }
6182#endif
6183 return xRooNode();
6184 }
6185
6186 TString sOpt(opt);
6187 if (sOpt == "prefit") {
6188 // build a fitResult using nominal values and infer errors from constraints
6189 // that aren't the 'main' constraints
6190 // Warning("fitResult","Building prefitResult by examining pdf. Consider setting an explicit prefitResult
6191 // (SetFitResult(fr)) where fr name is prefitResult");
6192
6193 // ensure coefs are included if there are any
6194 auto _coefs = coefs();
6195 if (_coefs.get()) {
6196 return xRooNode(RooProduct("tmp", "tmp", RooArgList(*get<RooAbsArg>(), *_coefs.get<RooAbsReal>())))
6197 .fitResult(opt);
6198 }
6199
6200 std::unique_ptr<RooArgList> _pars(dynamic_cast<RooArgList *>(pars().argList().selectByAttrib("Constant", false)));
6201 auto fr = std::make_shared<RooFitResult>("prefitResult", "Prefit");
6202 fr->setFinalParList(*_pars);
6203 for (auto &p : fr->floatParsFinal()) {
6204 auto _v = dynamic_cast<RooRealVar *>(p);
6205 if (!_v)
6206 continue;
6207 if (auto s = _v->getStringAttribute("nominal"); s)
6208 _v->setVal(TString(s).Atof());
6209 auto _constr = xRooNode(fParent->getObject<RooRealVar>(p->GetName()), *this).constraints();
6210 std::shared_ptr<xRooNode> pConstr;
6211 for (auto &c : _constr) {
6212 if (c->get<RooPoisson>() || c->get<RooGaussian>()) {
6213 // require parameter to be a direct server of the constraint pdf to count
6214 bool isServer = true;
6215 if (c->get<RooGaussian>()) {
6216 isServer = false;
6217 for (auto s : c->get<RooAbsArg>()->servers()) {
6218 if (strcmp(s->GetName(), p->GetName()) == 0) {
6219 isServer = true;
6220 break;
6221 }
6222 }
6223 }
6224 if (isServer) {
6225 pConstr = c;
6226 break;
6227 }
6228 }
6229 }
6230 if (pConstr) {
6231 // there will be 3 deps, one will be this par, the other two are the mean and error (or error^2 in case of
6232 // poisson use the one that's a ConstVar as the error to break a tie ...
6233 double prefitVal = 0, prefitError = 0;
6234 for (auto &_d : pConstr->vars()) {
6235 if (strcmp(p->GetName(), _d->get()->GetName()) == 0)
6236 continue;
6237 if (auto _c = _d->get<RooConstVar>(); _c && _c->getVal() != 0) {
6238 if (prefitError)
6239 prefitVal = prefitError; // loading val into error already, so move it over
6240 prefitError = _c->getVal();
6241 } else if (prefitError == 0)
6242 prefitError = _d->get<RooAbsReal>()->getVal();
6243 else
6244 prefitVal = _d->get<RooAbsReal>()->getVal();
6245 }
6246
6247 if (pConstr->get<RooGaussian>() && pConstr->browse().find(".sigma")) {
6248 prefitError = pConstr->find(".sigma")->get<RooAbsReal>()->getVal();
6249 }
6250 // std::cout << p->GetName() << " extracted " << prefitVal << " " << prefitError << " from ";
6251 // pConstr->deps().Print();
6252 if (pConstr->get<RooPoisson>()) {
6253 // prefitVal will be the global observable value, need to divide that by tau
6254 prefitVal /= prefitError;
6255 // prefiterror will be tau ... need 1/sqrt(tau) for error
6256 prefitError = 1. / sqrt(prefitError);
6257 }
6258 if (!_v->getStringAttribute("nominal"))
6259 _v->setVal(prefitVal);
6260 _v->setError(prefitError);
6261 } else {
6262 // unconstrained, remove error
6263 _v->removeError();
6264 }
6265 }
6266 auto _args = consts().argList();
6267 _args.add(pp().argList());
6268 // global obs are added to constPars list too
6269 auto _globs = globs(); // keep alive as may own glob
6270 _args.add(_globs.argList());
6271 fr->setConstParList(_args);
6272 std::unique_ptr<RooArgList> _snap(dynamic_cast<RooArgList *>(_pars->snapshot()));
6273 for (auto &p : *_snap) {
6274 if (auto atr = p->getStringAttribute("initVal"); atr && dynamic_cast<RooRealVar *>(p))
6275 dynamic_cast<RooRealVar *>(p)->setVal(TString(atr).Atof());
6276 }
6277 fr->setInitParList(*_snap);
6278 return xRooNode(fr, *this);
6279 }
6280
6281 // return first checked fit result present in the workspace
6282 if (auto _w = ws(); _w) {
6283 auto checkFr = [&](TObject *o) {
6284 if (auto _fr = dynamic_cast<RooFitResult *>(o); _fr && _fr->TestBit(1 << 20)) {
6285 // check all pars match final/const values ... if mismatch need to create a new RooFitResult
6286 bool match = true;
6287 for (auto p : pars()) {
6288 if (!p->get<RooAbsReal>())
6289 continue;
6290 if (p->get<RooAbsArg>()->getAttribute("Constant")) {
6291 if (_fr->floatParsFinal().find(p->GetName()) ||
6292 std::abs(_fr->constPars().getRealValue(p->GetName(), std::numeric_limits<double>::quiet_NaN()) -
6293 p->get<RooAbsReal>()->getVal()) > 1e-15) {
6294 match = false;
6295 break;
6296 }
6297 } else {
6298 if (_fr->constPars().find(p->GetName()) ||
6299 std::abs(
6300 _fr->floatParsFinal().getRealValue(p->GetName(), std::numeric_limits<double>::quiet_NaN()) -
6301 p->get<RooAbsReal>()->getVal()) > 1e-15) {
6302 match = false;
6303 break;
6304 }
6305 }
6306 }
6307 if (!match) {
6308 // create new fit result using covariances from this fit result
6309 std::unique_ptr<RooArgList> _pars(
6310 dynamic_cast<RooArgList *>(pars().argList().selectByAttrib("Constant", false)));
6311 auto fr = std::make_shared<RooFitResult>(TString::Format("%s-dirty", _fr->GetName()));
6312 fr->SetTitle(TString::Format("%s parameter snapshot", GetName()));
6313 fr->setFinalParList(*_pars);
6314 TMatrixTSym<double> *prevCov = static_cast<TMatrixTSym<double> *>(GETDMP(_fr, _VM));
6315 if (prevCov) {
6316 auto cov = _fr->reducedCovarianceMatrix(*_pars);
6317 // make the diagonals all the current error values
6318 for (size_t i = 0; i < _pars->size(); i++) {
6319 if (auto v = dynamic_cast<RooRealVar *>(_pars->at(i))) {
6320 cov(i, i) = pow(v->getError(), 2);
6321 } else {
6322 cov(i, i) = 0;
6323 }
6324 }
6325 fr->setCovarianceMatrix(cov);
6326 }
6327
6328 auto _args = consts().argList();
6329 _args.add(pp().argList());
6330 // global obs are added to constPars list too
6331 auto _globs = globs(); // keep alive as may own glob
6332 _args.add(_globs.argList());
6333 fr->setConstParList(_args);
6334 std::unique_ptr<RooArgList> _snap(dynamic_cast<RooArgList *>(_pars->snapshot()));
6335 for (auto &p : *_snap) {
6336 if (auto atr = p->getStringAttribute("initVal"); atr && dynamic_cast<RooRealVar *>(p))
6337 dynamic_cast<RooRealVar *>(p)->setVal(TString(atr).Atof());
6338 }
6339 fr->setInitParList(*_snap);
6340 return xRooNode(fr, *this);
6341 }
6342 return xRooNode(*_fr, std::make_shared<xRooNode>(*_w, std::make_shared<xRooNode>()));
6343 }
6344 return xRooNode();
6345 };
6346 for (auto o : _w->allGenericObjects()) {
6347 auto out = checkFr(o);
6348 if (out)
6349 return out;
6350 }
6351 for (auto o : GETWSSNAPSHOTS(_w)) {
6352 auto out = checkFr(o);
6353 if (out)
6354 return out;
6355 }
6356 } else {
6357 // objects not in workspaces are allowed to have a fitResult set in their memory
6358 // use getObject to get it
6359 if (auto fr = getObject<RooFitResult>(".fitResult"); fr) {
6360 return xRooNode(fr, *this);
6361 }
6362 }
6363
6364 // ensure coefs are included if there are any
6365 auto _coefs = coefs();
6366 if (_coefs.get()) {
6367 return xRooNode(RooProduct("tmp", "tmp", RooArgList(*get<RooAbsArg>(), *_coefs.get<RooAbsReal>())))
6368 .fitResult(opt);
6369 }
6370
6371 std::unique_ptr<RooArgList> _pars(dynamic_cast<RooArgList *>(pars().argList().selectByAttrib("Constant", false)));
6372 auto fr = std::make_shared<RooFitResult>(TUUID().AsString());
6373 fr->SetTitle(TString::Format("%s uncorrelated parameter snapshot", GetName()));
6374 fr->setFinalParList(*_pars);
6375 fr->setStatus(-1);
6376
6377 TMatrixDSym cov(fr->floatParsFinal().getSize());
6378 TMatrixTSym<double> *prevCov = static_cast<TMatrixTSym<double> *>(GETDMP(fr.get(), _VM));
6379 if (prevCov) {
6380 for (int i = 0; i < prevCov->GetNcols(); i++) {
6381 for (int j = 0; j < prevCov->GetNrows(); j++) {
6382 cov(i, j) = (*prevCov)(i, j);
6383 }
6384 }
6385 }
6386 int i = 0;
6387 for (auto &p : fr->floatParsFinal()) {
6388 if (!prevCov || i >= prevCov->GetNcols()) {
6389 if (auto v = dynamic_cast<RooRealVar *>(p)) {
6390 cov(i, i) = pow(v->getError(), 2);
6391 } else {
6392 cov(i, i) = 0;
6393 }
6394 }
6395 i++;
6396 }
6397 int covQualBackup = fr->covQual();
6398 fr->setCovarianceMatrix(cov);
6399 fr->setCovQual(covQualBackup);
6400
6401 auto _args = consts().argList();
6402 _args.add(pp().argList());
6403 // global obs are added to constPars list too
6404 auto _globs = globs(); // keep alive as may own glob
6405 _args.add(_globs.argList());
6406 fr->setConstParList(_args);
6407 std::unique_ptr<RooArgList> _snap(dynamic_cast<RooArgList *>(_pars->snapshot()));
6408 for (auto &p : *_snap) {
6409 if (auto atr = p->getStringAttribute("initVal"); atr && dynamic_cast<RooRealVar *>(p))
6410 dynamic_cast<RooRealVar *>(p)->setVal(TString(atr).Atof());
6411 }
6412 fr->setInitParList(*_snap);
6413
6414 // return *const_cast<Node2*>(this)->emplace_back(std::make_shared<Node2>(".fitResult",fr,*this));
6415 return xRooNode(fr, *this);
6416}
6417
6418// xRooNode xRooNode::fitTo_(const char* datasetName) const {
6419// try {
6420// return fitTo(datasetName);
6421// } catch(const std::exception& e) {
6422// new TGMsgBox(gClient->GetRoot(), gClient->GetRoot(), "Exception", e.what(),kMBIconExclamation); // deletes
6423// self on dismiss? return xRooNode();
6424// }
6425// }
6426//
6427// xRooNode xRooNode::fitTo(const char* datasetName) const {
6428// return fitTo(*datasets().at(datasetName));
6429// }
6430
6431void xRooNode::SetRange(const char *range, double low, double high)
6432{
6433 if (!std::isnan(low) && !std::isnan(high) && get<RooRealVar>()) {
6434 if (range && strlen(range))
6435 get<RooRealVar>()->setRange(range, low, high);
6436 else
6437 get<RooRealVar>()->setRange(low, high);
6438 return;
6439 }
6440 if (auto o = get<RooAbsArg>(); o)
6441 o->setStringAttribute("range", range);
6442 // todo: clear the range attribute on all servers
6443 // could make this controlled by a flag but probably easiest to enforce so you must set range
6444 // in children after if you wanted to override
6445}
6446const char *xRooNode::GetRange() const
6447{
6448 std::string &out = fRange;
6449 if (auto o = get<RooAbsArg>(); o && o->getStringAttribute("range"))
6450 out = o->getStringAttribute("range");
6451 auto _parent = fParent;
6452 while (out.empty() && _parent) {
6453 if (auto o = _parent->get<RooAbsArg>(); o && o->getStringAttribute("range"))
6454 out = o->getStringAttribute("range");
6455 _parent = _parent->fParent;
6456 }
6457 return out.c_str();
6458}
6459
6461{
6462 return nll(_data, *xRooFit::createNLLOptions());
6463}
6464
6465xRooNLLVar xRooNode::nll(const xRooNode &_data, std::initializer_list<RooCmdArg> nllOpts) const
6466{
6467 auto defaultOpts = xRooFit::createNLLOptions(); // smart pointer will cleanup the list
6468 // add user-specified options to list ... if already existing in default list, override and warn
6470 for (auto opt : *defaultOpts) {
6471 l.Add(opt);
6472 }
6473 for (auto &i : nllOpts) {
6474 if (auto o = l.FindObject(i.GetName())) {
6475 Info("nll", "Overriding NLL Option: %s", o->GetName());
6476 l.Remove(o);
6477 }
6478 l.Add(const_cast<RooCmdArg *>(&i));
6479 }
6480
6481 return nll(_data, l);
6482}
6483
6484xRooNode xRooNode::generate(const xRooNode &fr, bool expected, int seed)
6485{
6486 if (!get<RooAbsPdf>()) {
6487 // before giving up, if this is a workspace we can proceed if we only have one model
6488 if (get<RooWorkspace>()) {
6489 std::shared_ptr<xRooNode> mainModel;
6490 for (auto &c : const_cast<xRooNode *>(this)->browse()) {
6491 if (c->get<RooAbsPdf>()) {
6492 if (!mainModel) {
6493 mainModel = c;
6494 } else {
6495 throw std::runtime_error(TString::Format("Workspace has multiple models, you must specify which to "
6496 "generate with (found at least %s and %s)",
6497 mainModel->GetName(), c->GetName()));
6498 }
6499 }
6500 }
6501 if (mainModel)
6502 return mainModel->generate(fr, expected, seed);
6503 }
6504 throw std::runtime_error(TString::Format("%s is not a pdf", GetName()));
6505 }
6506 auto _fr = fr.get<RooFitResult>();
6507 return xRooNode(
6508 xRooFit::generateFrom(*get<RooAbsPdf>(), (_fr ? *_fr : *(fitResult().get<RooFitResult>())), expected, seed).first,
6509 *this);
6510}
6511
6512xRooNLLVar xRooNode::nll(const xRooNode &_data, const RooLinkedList &opts) const
6513{
6514
6515 if (!get<RooAbsPdf>()) {
6516 // before giving up, if this is a workspace we can proceed if we only have one model
6517 if (get<RooWorkspace>()) {
6518 std::shared_ptr<xRooNode> mainModel;
6519 for (auto &c : const_cast<xRooNode *>(this)->browse()) {
6520 if (c->get<RooAbsPdf>()) {
6521 if (!mainModel) {
6522 mainModel = c;
6523 } else {
6524 throw std::runtime_error(TString::Format("Workspace has multiple models, you must specify which to "
6525 "build nll with (found at least %s and %s)",
6526 mainModel->GetName(), c->GetName()));
6527 }
6528 }
6529 }
6530 if (mainModel)
6531 return mainModel->nll(_data, opts);
6532 }
6533 throw std::runtime_error(TString::Format("%s is not a pdf", GetName()));
6534 }
6535
6536 // if simultaneous and any channels deselected then reduce and return
6537 if (get<RooSimultaneous>()) {
6538 std::string selected;
6539 bool hasDeselected = false;
6540 for (auto c : bins()) {
6541 if (!c->get<RooAbsReal>()->isSelectedComp()) {
6542 hasDeselected = true;
6543 } else {
6544 TString cName(c->GetName());
6545 cName = cName(cName.Index('=') + 1, cName.Length());
6546 if (!selected.empty())
6547 selected += ",";
6548 selected += cName.Data();
6549 }
6550 }
6551 if (hasDeselected)
6552 return reduced(selected).nll(_data, opts);
6553 }
6554
6555 if (!_data.get<RooAbsData>()) {
6556 // use node name to find dataset and recall
6557 auto _d = strlen(_data.GetName()) ? datasets().find(_data.GetName()) : nullptr;
6558 if (strlen(_data.GetName()) == 0) {
6559 // create the EXPECTED (asimov) dataset with the observables
6560 auto asi = xRooFit::generateFrom(*get<RooAbsPdf>(), *(fitResult().get<RooFitResult>()), true);
6561 _d = std::make_shared<xRooNode>(asi.first, *this);
6562 if (asi.second) {
6563 _d->emplace_back(
6564 std::make_shared<xRooNode>(".globs", std::const_pointer_cast<RooAbsCollection>(asi.second), *_d));
6565 }
6566 } else if (!_d) {
6567 throw std::runtime_error(TString::Format("Cannot find dataset %s", _data.GetName()));
6568 }
6569 return nll(*_d, opts);
6570 }
6571
6572 auto _globs = _data.globs(); // keep alive because may own the globs
6573
6574 auto _opts = std::shared_ptr<RooLinkedList>(new RooLinkedList, [](RooLinkedList *l) {
6575 if (l)
6576 l->Delete();
6577 delete l;
6578 });
6579 RooArgSet _globsSet(_globs.argList());
6580 _opts->Add(RooFit::GlobalObservables(_globsSet).Clone());
6581 if (GetRange() && strlen(GetRange()))
6582 _opts->Add(RooFit::Range(GetRange()).Clone());
6583
6584 // copy over opts ... need to clone each so can safely delete when _opts destroyed
6585 for (int i = 0; i < opts.GetSize(); i++) {
6586 if (strlen(opts.At(i)->GetName()) == 0)
6587 continue; // skipping "none" cmds
6588 if (strcmp(opts.At(i)->GetName(), "GlobalObservables") == 0) {
6589 // maybe warn here?
6590 } else {
6591 _opts->Add(opts.At(i)->Clone(nullptr)); // nullptr needed because accessing Clone via TObject base class puts
6592 // "" instead, so doesnt copy names
6593 }
6594 }
6595
6596 // use shared_ptr method so NLLVar will take ownership of datasets etc if created above
6597 // snapshots the globs out of the nllOpts (see specific constructor of xRooNLLVar)
6598 auto out = xRooFit::createNLL(std::dynamic_pointer_cast<RooAbsPdf>(fComp),
6599 std::dynamic_pointer_cast<RooAbsData>(_data.fComp), *_opts);
6600 return out;
6601}
6602
6603// xRooNode xRooNode::fitTo(const xRooNode& _data) const {
6604//
6605//
6606// auto _pdf = get<RooAbsPdf>();
6607// if (!_pdf) throw std::runtime_error("Not a pdf");
6608//
6609// auto _globs = _data.globs(); // keep alive because may own the globs
6610// RooArgSet globsSet(_globs.argList());
6611//
6612// std::shared_ptr<RooSimultaneous> newPdf;
6613// if(auto s = get<RooSimultaneous>(); s) {
6614// auto rangeName = GetRange();
6615// if (rangeName) {
6616// // need to reduce the RooSimultaneous until fix: https://github.com/root-project/root/issues/8231
6617// std::vector<TString> chanPatterns;
6618// TStringToken pattern(rangeName, ",");
6619// while (pattern.NextToken()) {
6620// chanPatterns.emplace_back(pattern);
6621// }
6622// auto& _cat = const_cast<RooAbsCategoryLValue&>(s->indexCat());
6623// newPdf = std::make_shared<RooSimultaneous>(TString::Format("%s_reduced",GetName()),"Reduced model",_cat);
6624// for(auto& c : variations()) {
6625// TString cName(c->GetName());
6626// cName = cName(cName.Index('=')+1,cName.Length());
6627// _cat.setLabel(cName);
6628// bool matchAny=false;
6629// for(auto& p : chanPatterns) {
6630// if (cName.Contains(TRegexp(p,true))) { matchAny=true; break; }
6631// if (_cat.hasRange(p) && _cat.inRange(p)) { matchAny=true; break; }
6632// }
6633// if(matchAny) {
6634// newPdf->addPdf( *c->get<RooAbsPdf>(), cName );
6635// }
6636// }
6637// RooFitResultTree t(newPdf->GetName(),"",*newPdf);
6638// auto _fr = std::const_pointer_cast<RooFitResult>(t.fitTo(_data.get<RooAbsData>(), &globsSet));
6639// xRooNode parent(_data.GetName(),nullptr,*this);
6640// xRooNode out(_fr->GetName(),/*acquire(_fr)*/ _fr,parent);
6641// // do full propagation by 'checking' the fr ...
6642// out.Checked(&out,true);
6643// return out;
6644// }
6645// }
6646//
6647//
6648//
6649// std::string treeName = TString::Format("fits_%s",GetName()).Data();
6650//
6651// auto _frt = getObject<TTree>(treeName); // get existing frt
6652//
6653// std::shared_ptr<RooFitResultTree> t;
6654// if (_frt) {
6655// t = std::make_shared<RooFitResultTree>(_frt.get());
6656// } else {
6657// t = std::make_shared<RooFitResultTree>(treeName.c_str(),"",*_pdf);
6658// }
6659// //t->SetProgress(true);
6660// auto _fr = std::const_pointer_cast<RooFitResult>(t->fitTo(_data.get<RooAbsData>(), &globsSet));
6661//
6662//
6663//
6664// /*
6665// obs().argList() = s; // sets global observables to their values
6666// auto _fr =
6667// std::shared_ptr<RooFitResult>(_pdf->fitTo(*_data->get<RooAbsData>(),RooFit::GlobalObservables(s),RooFit::Offset(true),RooFit::Save()));
6668// _fr->SetName(TUUID().AsString());
6669// // restore parameters before returning
6670// *std::unique_ptr<RooArgSet>(_pdf->getDependents(_fr->floatParsFinal())) = _fr->floatParsInit();
6671// */
6672//
6673// //_fr->SetTitle(TString::Format("%s;%s",GetName(),datasetName));
6674// if (!_frt) {
6675// t =
6676// std::make_shared<RooFitResultTree>(std::dynamic_pointer_cast<TTree>(const_cast<xRooNode*>(this)->acquire(t->fTree)).get());
6677// }
6678// xRooNode parent(_data.GetName(),nullptr,xRooNode(t,*this));
6679// xRooNode out(_fr->GetName(),/*acquire(_fr)*/ _fr,parent);
6680// // do full propagation by 'checking' the fr ...
6681// out.Checked(&out,true);
6682// return out;
6683// }
6684
6685std::shared_ptr<xRooNode> xRooNode::parentPdf() const
6686{
6687 // find first parent that is a pdf
6688 auto out = fParent;
6689 while (out && !out->get<RooAbsPdf>()) {
6690 out = out->fParent;
6691 }
6692 return out;
6693}
6694
6695xRooNode xRooNode::reduced(const std::string &_range, bool invert) const
6696{
6697 auto rangeName = (_range.empty()) ? GetRange() : _range;
6698 if (!rangeName.empty()) {
6699 std::vector<TString> patterns;
6700 TStringToken pattern(rangeName, ",");
6701 while (pattern.NextToken()) {
6702 patterns.emplace_back(pattern);
6703 }
6704 if (auto s = get<RooSimultaneous>(); s) {
6705 // need to reduce the RooSimultaneous until fix: https://github.com/root-project/root/issues/8231
6706 auto &_cat = const_cast<RooAbsCategoryLValue &>(s->indexCat());
6707 auto newPdf =
6708 std::make_shared<RooSimultaneous>(TString::Format("%s_reduced", GetName()), "Reduced model", _cat);
6709 for (auto &c : bins()) {
6710 TString cName(c->GetName());
6711 cName = cName(cName.Index('=') + 1, cName.Length());
6712 _cat.setLabel(cName);
6713 bool matchAny = false;
6714 for (auto &p : patterns) {
6715 if (cName.Contains(TRegexp(p, true))) {
6716 matchAny = true;
6717 break;
6718 }
6719 if (_cat.hasRange(p) && _cat.inRange(p)) {
6720 matchAny = true;
6721 break;
6722 }
6723 }
6724 if ((matchAny && !invert) || (!matchAny && invert)) {
6725 newPdf->addPdf(*c->get<RooAbsPdf>(), cName);
6726 }
6727 }
6728 return xRooNode(newPdf, fParent);
6729 } else if (get() && !get<RooAbsCollection>() && !components().empty()) {
6730 // create a new obj and remove non-matching components
6731 xRooNode out(std::shared_ptr<TObject>(get()->Clone(TString::Format("%s_reduced", get()->GetName()))), fParent);
6732 // go through components and remove any that don't match pattern
6733 std::vector<TObject *> funcs; // to be removed
6734 for (auto &c : out.components()) {
6735 bool matchAny = false;
6736 for (auto &p : patterns) {
6737 if (TString(c->GetName()).Contains(TRegexp(p, true))) {
6738 matchAny = true;
6739 break;
6740 }
6741 }
6742 if (!((matchAny && !invert) || (!matchAny && invert)))
6743 funcs.push_back(c->get());
6744 }
6745 for (auto &c : funcs)
6746 out.Remove(*c);
6747 if (!funcs.empty()) {
6748 if (auto _pdf = out.get<RooRealSumPdf>(); _pdf) {
6749 _pdf->setFloor(false); // remove floor if removed some functions, which allows evaluation of negative
6750 // valued components
6751 }
6752 }
6753 out.browse();
6754 return out;
6755 } else if (auto fr = get<RooFitResult>()) {
6756 // reduce the fit result by moving unselected float pars into the constPars list and dropping their covariances
6757 xRooNode out(std::shared_ptr<TObject>(fr->Clone(TString::Format("%s_reduced", fr->GetName()))), fParent);
6758 fr = out.get<RooFitResult>();
6759 RooArgList _pars = fr->floatParsFinal();
6760 RooArgList _remPars;
6761 for (auto c : _pars) {
6762 bool matchAny = false;
6763 for (auto &p : patterns) {
6764 if (TString(c->GetName()).Contains(TRegexp(p, true))) {
6765 matchAny = true;
6766 break;
6767 }
6768 }
6769 if (!((matchAny && !invert) || (!matchAny && invert))) {
6770 _remPars.add(*c);
6771 }
6772 }
6773 _pars.remove(_remPars, true);
6774
6775 auto _tmp = fr->reducedCovarianceMatrix(_pars);
6776 int covQualBackup = fr->covQual();
6777 fr->setCovarianceMatrix(_tmp);
6778 fr->setCovQual(covQualBackup);
6779 const_cast<RooArgList &>(fr->floatParsFinal())
6780 .remove(_remPars, true); // is this a memory leak ... should delete the remPars?
6781 return out;
6782
6783 } else if (!get() || get<RooAbsCollection>()) {
6784 // filter the children .... handle special case of filtering ".vars" with "x" option too
6785 xRooNode out(std::make_shared<RooArgList>(), fParent);
6786 size_t nobs = 0;
6787 bool notAllArgs = false;
6788 bool isVars = (strcmp(GetName(), ".vars") == 0);
6789 for (auto c : *this) {
6790 nobs += (c->fFolder == "!robs" || c->fFolder == "!globs");
6791 bool matchAny = false;
6792 for (auto &p : patterns) {
6793 if (TString(c->GetName()).Contains(TRegexp(p, true)) ||
6794 (isVars && p == "x" && (c->fFolder == "!robs" || c->fFolder == "!globs") && nobs == 1)) {
6795 matchAny = true;
6796 break;
6797 }
6798 }
6799 if ((matchAny && !invert) || (!matchAny && invert)) {
6800 out.push_back(c);
6801 if (auto a = c->get<RooAbsArg>()) {
6802 out.get<RooArgList>()->add(*a);
6803 } else {
6804 notAllArgs = true;
6805 }
6806 }
6807 }
6808 if (notAllArgs) {
6809 out.fComp.reset();
6810 }
6811 return out;
6812 }
6813 }
6814
6815 return get<RooArgList>() ? xRooNode(std::make_shared<RooArgList>(), fParent) : *this;
6816}
6817
6818// xRooNode xRooNode::generate(bool expected) const {
6819//
6820// auto fr = fitResult();
6821// auto _fr = fr.get<RooFitResult>();
6822//
6823// auto _pdf = (get<RooAbsPdf>()) ? std::shared_ptr<const xRooNode>(this, [](const xRooNode*){}) : parentPdf();
6824// if (!_pdf) {
6825// throw std::runtime_error("Could not find pdf");
6826// }
6827//
6828// std::shared_ptr<RooDataTree> t;
6829//
6830// std::shared_ptr<RooSimultaneous> newPdf;
6831// if(auto s = _pdf->get<RooSimultaneous>(); s) {
6832// auto rangeName = GetRange();
6833// if (rangeName) {
6834// // need to reduce the RooSimultaneous until fix: https://github.com/root-project/root/issues/8231
6835// std::vector<TString> chanPatterns;
6836// TStringToken pattern(rangeName, ",");
6837// while (pattern.NextToken()) {
6838// chanPatterns.emplace_back(pattern);
6839// }
6840// auto& _cat = const_cast<RooAbsCategoryLValue&>(s->indexCat());
6841// newPdf = std::make_shared<RooSimultaneous>(TString::Format("%s_reduced",GetName()),"Reduced model",_cat);
6842// for(auto& c : _pdf->variations()) {
6843// TString cName(c->GetName());
6844// cName = cName(cName.Index('=')+1,cName.Length());
6845// _cat.setLabel(cName);
6846// bool matchAny=false;
6847// for(auto& p : chanPatterns) {
6848// if (cName.Contains(TRegexp(p,true))) { matchAny=true; break; }
6849// if (_cat.hasRange(p) && _cat.inRange(p)) { matchAny=true; break; }
6850// }
6851// if(matchAny) {
6852// newPdf->addPdf( *c->get<RooAbsPdf>(), cName );
6853// }
6854// }
6855// t = std::make_shared<RooDataTree>(newPdf->GetName(),"",*newPdf);
6856// RooArgSet s1(_pdf->obs().argList());
6857// RooArgSet s2(_pdf->globs().argList());s1.remove(s2);
6858// t->SetObservables(&s1,&s2);
6859// auto _data = t->generate(_fr,expected);
6860//
6861// xRooNode parent(_fr ? _fr->GetName() : "unknown",nullptr,xRooNode(t,*this));
6862// xRooNode out(_data.first->GetName(),/*acquire(_fr)*/ _data.first,parent);
6863// out.emplace_back(std::make_shared<xRooNode>(".globs",std::const_pointer_cast<RooArgSet>(_data.second),out));
6864// return out;
6865// }
6866// }
6867//
6868//
6869// std::string treeName = TString::Format("gen_%s",_pdf->GetName()).Data();
6870//
6871// auto _frt = getObject<TTree>(treeName); // get existing frt
6872//
6873//
6874// if (_frt) {
6875// t = std::make_shared<RooDataTree>(_frt.get());
6876// } else {
6877// t = std::make_shared<RooDataTree>(treeName.c_str(),"",*_pdf->get<RooAbsPdf>());
6878// RooArgSet s1(_pdf->obs().argList());
6879// RooArgSet s2(_pdf->globs().argList());s1.remove(s2);
6880// t->SetObservables(&s1,&s2);
6881// }
6882// auto _data = t->generate(_fr,expected);
6883// if (!_frt) {
6884// t =
6885// std::make_shared<RooDataTree>(std::dynamic_pointer_cast<TTree>(const_cast<xRooNode*>(this)->acquire(t->fTree)).get());
6886// }
6887// xRooNode parent(_fr ? _fr->GetName() : "unknown",nullptr,xRooNode(t,*this));
6888// xRooNode out(_data.first->GetName(),/*acquire(_fr)*/ _data.first,parent);
6889// out.emplace_back(std::make_shared<xRooNode>(".globs",std::const_pointer_cast<RooArgSet>(_data.second),out));
6890// return out;
6891// }
6892
6894public:
6896 double expectedEvents(const RooArgSet *nset) const override
6897 {
6898 return static_cast<RooAbsPdf *>(intpdf.absArg())->expectedEvents(nset);
6899 }
6900 ExtendMode extendMode() const override { return static_cast<RooAbsPdf *>(intpdf.absArg())->extendMode(); }
6901 virtual TObject *clone(const char *newname) const override { return new xRooProjectedPdf(*this, newname); }
6902
6903protected:
6904 double evaluate() const override
6905 {
6906 int code;
6907 return getProjection(&intobs, _normSet, (_normRange.Length() > 0 ? _normRange.Data() : 0), code)->getVal();
6908 }
6909};
6910
6911class PdfWrapper : public RooAbsPdf {
6912public:
6913 // need expPdf option while RooProjectedPdf doesn't support keeping things extended
6914 PdfWrapper(RooAbsReal &f, RooAbsReal *coef, bool expEvMode = false, RooAbsPdf *expPdf = nullptr)
6915 : RooAbsPdf(Form("exp_%s", f.GetName())),
6916 fFunc("func", "func", this, f),
6917 fCoef("coef", "coef", this),
6918 fExpPdf("expPdf", "expPdf", this)
6919 {
6920 // don't treat pdf as extended if it has a coefficient and is RooAddPdf: RooAddPdf doesn't extend them unless no
6921 // coefs for any (and all are extendable)
6922 if (coef) {
6923 fCoef.setArg(*coef);
6924 }
6925 if (expPdf && expPdf->canBeExtended() && !(coef && dynamic_cast<RooAddPdf *>(expPdf))) {
6926 fExpPdf.setArg(*expPdf);
6927 } else if (auto _p = dynamic_cast<RooAbsPdf *>(&f);
6928 _p && _p->canBeExtended() && !(coef && dynamic_cast<RooAddPdf *>(_p))) {
6929 fExpPdf.setArg(f); // using self for expectation
6930 }
6931 fExpectedEventsMode = expEvMode;
6932 }
6933 virtual ~PdfWrapper(){};
6934 PdfWrapper(const PdfWrapper &other, const char *name = 0)
6935 : RooAbsPdf(other, name),
6936 fFunc("func", this, other.fFunc),
6937 fCoef("coef", this, other.fCoef),
6938 fExpPdf("expPdf", this, other.fExpPdf),
6939 fExpectedEventsMode(other.fExpectedEventsMode)
6940 {
6941 }
6942 virtual TObject *clone(const char *newname) const override { return new PdfWrapper(*this, newname); }
6943 bool isBinnedDistribution(const RooArgSet &obs) const override { return fFunc->isBinnedDistribution(obs); }
6944 std::list<double> *binBoundaries(RooAbsRealLValue &obs, double xlo, double xhi) const override
6945 {
6946 return fFunc->binBoundaries(obs, xlo, xhi);
6947 }
6948
6949 double evaluate() const override
6950 {
6951 return (fExpectedEventsMode ? 1. : fFunc) *
6952 ((fExpPdf.absArg()) ? static_cast<RooAbsPdf *>(fExpPdf.absArg())->expectedEvents(_normSet) : 1.) *
6953 (fCoef.absArg() ? fCoef : 1.);
6954 }
6955
6956 bool selfNormalized() const override
6957 {
6958 return true;
6959 } // so that doesn't try to do an integral because we are passing integration onto fFunc in evaluate
6960
6961 // faster than full evaluation because doesnt make the integral dependent on the full expression
6962 double getSimplePropagatedError(const RooFitResult &fr, const RooArgSet &nset_in) const
6963 {
6964#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 28, 00)
6965 double oo = getPropagatedError(fr, nset_in); // method was improved in 6.28 so use this instead
6966 if (std::isnan(oo)) {
6967 // may be consequence of zero uncerts
6968 // Calling getParameters() might be costly, but necessary to get the right
6969 // parameters in the RooAbsReal. The RooFitResult only stores snapshots.
6970 RooArgSet allParamsInAbsReal;
6971 getParameters(&nset_in, allParamsInAbsReal);
6972
6973 RooArgList paramList;
6974 for (auto *rrvFitRes : static_range_cast<RooRealVar *>(fr.floatParsFinal())) {
6975
6976 auto rrvInAbsReal = static_cast<RooRealVar const *>(allParamsInAbsReal.find(*rrvFitRes));
6977
6978 // If this RooAbsReal is a RooRealVar in the fit result, we don't need to
6979 // propagate anything and can just return the error in the fit result
6980 if (rrvFitRes->namePtr() == namePtr())
6981 return rrvFitRes->getError();
6982
6983 // Strip out parameters with zero error
6984 if (!rrvFitRes->hasError() ||
6985 rrvFitRes->getError() <= std::abs(rrvFitRes->getVal()) * std::numeric_limits<double>::epsilon())
6986 continue;
6987
6988 // Ignore parameters in the fit result that this RooAbsReal doesn't depend on
6989 if (!rrvInAbsReal)
6990 continue;
6991
6992 // Checking for float equality is a bad. We check if the values are
6993 // negligibly far away from each other, relative to the uncertainty.
6994 if (std::abs(rrvInAbsReal->getVal() - rrvFitRes->getVal()) > 0.01 * rrvFitRes->getError()) {
6995 std::stringstream errMsg;
6996 errMsg
6997 << "RooAbsReal::getPropagatedError(): the parameters of the RooAbsReal don't have"
6998 << " the same values as in the fit result! The logic of getPropagatedError is broken in this case.";
6999
7000 throw std::runtime_error(errMsg.str());
7001 }
7002
7003 paramList.add(*rrvInAbsReal);
7004 }
7005 if (paramList.empty())
7006 return 0.;
7007
7008 std::vector<double> plusVar;
7009 std::vector<double> minusVar;
7010 plusVar.reserve(paramList.size());
7011 minusVar.reserve(paramList.size());
7012
7013 // Create std::vector of plus,minus variations for each parameter
7014 TMatrixDSym V(paramList.size() == fr.floatParsFinal().size() ? fr.covarianceMatrix()
7015 : fr.reducedCovarianceMatrix(paramList));
7016
7017 for (Int_t ivar = 0; ivar < paramList.getSize(); ivar++) {
7018
7019 auto &rrv = static_cast<RooRealVar &>(paramList[ivar]);
7020
7021 double cenVal = rrv.getVal();
7022 double errVal = sqrt(V(ivar, ivar));
7023
7024 // this next thing happens if the par has errors but the covariance matrix is empty
7025 // this only happens if the fit was dodgy, so perhaps best to not even try to recover from this
7026 // screwup ... hence I've commented out this fixup here and will let the errors be nan
7027 // if(errVal==0) {
7028 // Warning("getPropagatedError","Missing variance for %s",rrv.GetName());
7029 // errVal = rrv.getError();
7030 // V(ivar,ivar) = errVal*errVal;
7031 // }
7032
7033 // Make Plus variation
7034 rrv.setVal(cenVal + errVal);
7035 plusVar.push_back(getVal(nset_in));
7036
7037 // Make Minus variation
7038 rrv.setVal(cenVal - errVal);
7039 minusVar.push_back(getVal(nset_in));
7040#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
7041 // can try to recover nans ... this stopped being possible in 6.27 onwards because NaNPacker made private
7042 if (std::isnan(plusVar.back()) && RooNaNPacker::isNaNWithPayload(plusVar.back())) {
7043 plusVar.back() = -RooNaNPacker::unpackNaN(plusVar.back());
7044 }
7045 if (std::isnan(minusVar.back()) && RooNaNPacker::isNaNWithPayload(minusVar.back())) {
7046 minusVar.back() = -RooNaNPacker::unpackNaN(minusVar.back());
7047 }
7048#endif
7049 // std::cout << plusVar.back() << " and " << minusVar.back() << std::endl;
7050
7051 rrv.setVal(cenVal);
7052 }
7053
7054 // Re-evaluate this RooAbsReal with the central parameters just to be
7055 // extra-safe that a call to `getPropagatedError()` doesn't change any state.
7056 // It should not be necessary because thanks to the dirty flag propagation
7057 // the RooAbsReal is re-evaluated anyway the next time getVal() is called.
7058 // Still there are imaginable corner cases where it would not be triggered,
7059 // for example if the user changes the RooFit operation more after the error
7060 // propagation.
7061 getVal(nset_in);
7062
7063 TMatrixDSym C(paramList.getSize());
7064 std::vector<double> errVec(paramList.size());
7065 for (int i = 0; i < paramList.getSize(); i++) {
7066 errVec[i] = std::sqrt(V(i, i));
7067 for (int j = i; j < paramList.getSize(); j++) {
7068 C(i, j) = V(i, j) / std::sqrt(V(i, i) * V(j, j));
7069 C(j, i) = C(i, j);
7070 }
7071 }
7072
7073 // Make std::vector of variations
7074 TVectorD F(plusVar.size());
7075 for (unsigned int j = 0; j < plusVar.size(); j++) {
7076 F[j] = (plusVar[j] - minusVar[j]) / 2;
7077 }
7078
7079 // Calculate error in linear approximation from variations and correlation coefficient
7080 double sum = F * (C * F);
7081
7082 return sqrt(sum);
7083 }
7084 return oo;
7085#else
7086
7087 // Strip out parameters with zero error
7088 RooArgList fpf_stripped;
7089 for (auto *frv : static_range_cast<RooRealVar *>(fi.floatParsFinal())) {
7090 if (frv->getError() > 1e-20) {
7091 fpf_stripped.add(*frv);
7092 }
7093 }
7094
7095 // Clone self for internal use
7096 RooAbsReal *cloneFunc = const_cast<PdfWrapper *>(this); // (RooAbsReal *)fFunc.absArg()->cloneTree();
7097 // RooAbsPdf *clonePdf = dynamic_cast<RooAbsPdf *>(cloneFunc);
7098 RooArgSet *errorParams = cloneFunc->getObservables(fpf_stripped);
7099
7100 RooArgSet *nset =
7101 nset_in.getSize() == 0 ? cloneFunc->getParameters(*errorParams) : cloneFunc->getObservables(nset_in);
7102
7103 // Make list of parameter instances of cloneFunc in order of error matrix
7104 RooArgList paramList;
7105 const RooArgList &fpf = fpf_stripped;
7106 std::vector<int> fpf_idx;
7107 for (Int_t i = 0; i < fpf.getSize(); i++) {
7108 RooAbsArg *par = errorParams->find(fpf[i].GetName());
7109 if (par) {
7110 paramList.add(*par);
7111 fpf_idx.push_back(i);
7112 }
7113 }
7114
7115 std::vector<double> plusVar, minusVar;
7116
7117 // Create vector of plus,minus variations for each parameter
7118 TMatrixDSym V(paramList.size() == fr.floatParsFinal().size() ? fr.covarianceMatrix()
7119 : fr.reducedCovarianceMatrix(paramList));
7120
7121 for (Int_t ivar = 0; ivar < paramList.getSize(); ivar++) {
7122
7123 RooRealVar &rrv = (RooRealVar &)fpf[fpf_idx[ivar]];
7124
7125 double cenVal = rrv.getVal();
7126 double errVal = sqrt(V(ivar, ivar));
7127
7128 // Make Plus variation
7129 ((RooRealVar *)paramList.at(ivar))->setVal(cenVal + errVal);
7130 // plusVar.push_back((fExpectedEventsMode ? 1. : cloneFunc->getVal(nset)) *
7131 // (clonePdf ? clonePdf->expectedEvents(nset) : 1.));
7132 plusVar.push_back(cloneFunc->getVal(nset));
7133
7134 // Make Minus variation
7135 ((RooRealVar *)paramList.at(ivar))->setVal(cenVal - errVal);
7136 // minusVar.push_back((fExpectedEventsMode ? 1. : cloneFunc->getVal(nset)) *
7137 // (clonePdf ? clonePdf->expectedEvents(nset) : 1.));
7138 minusVar.push_back(cloneFunc->getVal(nset));
7139
7140 ((RooRealVar *)paramList.at(ivar))->setVal(cenVal);
7141 }
7142
7143 getVal(nset); // reset state
7144
7145 TMatrixDSym C(paramList.getSize());
7146 std::vector<double> errVec(paramList.getSize());
7147 for (int i = 0; i < paramList.getSize(); i++) {
7148 errVec[i] = sqrt(V(i, i));
7149 for (int j = i; j < paramList.getSize(); j++) {
7150 C(i, j) = V(i, j) / sqrt(V(i, i) * V(j, j));
7151 C(j, i) = C(i, j);
7152 }
7153 }
7154
7155 // Make vector of variations
7156 TVectorD F(plusVar.size());
7157 for (unsigned int j = 0; j < plusVar.size(); j++) {
7158 F[j] = (plusVar[j] - minusVar[j]) / 2;
7159 }
7160
7161 // Calculate error in linear approximation from variations and correlation coefficient
7162 double sum = F * (C * F);
7163
7164 // delete cloneFunc;
7165 delete errorParams;
7166 delete nset;
7167
7168 return sqrt(sum);
7169#endif
7170 }
7171
7172private:
7176 bool fExpectedEventsMode = false;
7177};
7178
7179const xRooNode *runningNode = nullptr;
7181
7183{
7184 std::cout << "Got signal " << signum << std::endl;
7185 if (signum == SIGINT) {
7186 std::cout << "Keyboard interrupt while building histogram" << std::endl;
7187 // TODO: create a global mutex for this
7188 runningNode->fInterrupted = true;
7189 } else {
7190 gOldHandlerr(signum);
7191 }
7192}
7193
7195{
7196 auto _doSterilize = [](RooAbsArg *obj) {
7197 if (!obj)
7198 return;
7199 for (int i = 0; i < obj->numCaches(); i++) {
7200 if (auto cache = dynamic_cast<RooObjCacheManager *>(obj->getCache(i))) {
7201 cache->reset();
7202 }
7203 }
7204 if (RooAbsPdf *p = dynamic_cast<RooAbsPdf *>(obj); p) {
7205 p->setNormRange(p->normRange());
7206 }
7207#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
7208 if (RooAbsReal *p = dynamic_cast<RooAbsReal *>(obj); p) {
7209 // need to forget about any normSet that was passed to getVal(...)
7210 // doesn't seem necessary in 6.28
7211
7212 p->setProxyNormSet(nullptr);
7213 p->_lastNSet = nullptr;
7214 }
7215#endif
7216 obj->setValueDirty();
7217 };
7218 if (auto w = get<RooWorkspace>(); w) {
7219 // sterilizing all nodes
7220 for (auto &c : w->components()) {
7221 _doSterilize(c);
7222 }
7223 return;
7224 }
7225 // recursive through all clients and sterlize their normalization caches
7226 std::function<void(RooAbsArg *)> func;
7227 func = [&](RooAbsArg *a) {
7228 if (!a) {
7229 return;
7230 }
7231 _doSterilize(a); // sterilize first so that cache elements don't appear in the client list
7232 // safety net in case sterilizing one client deletes another one of our clients
7233 // monitor for change in clients list size
7234 // found this was only case in 6.26 (valgrind shows invalid read), in 6.28 these went away
7235 // might be in 6.28 the client list iterator became able to handle in-loop edits but didn't see
7236 // in test case that client count changed so just resterilizing if that's the case.
7237 size_t nClients;
7238 do {
7239 nClients = a->clients().size();
7240 for (auto obj : a->clients()) {
7241 func(dynamic_cast<RooAbsArg *>(obj));
7242 if (a->clients().size() != nClients) {
7243 break; // means sterilizing a client changed our clients, so don't trust the client iterator at this
7244 // point
7245 }
7246 }
7247 } while (a->clients().size() != nClients);
7248 };
7249 func(get<RooAbsArg>());
7250}
7251
7252// observables not in the axisVars are automatically projected over
7253xRooNode xRooNode::histo(const xRooNode &vars, const xRooNode &fr, bool content, bool errors) const
7254{
7255
7256 if (!vars.fComp && strlen(vars.GetName())) {
7257 return histo(xRooNode::vars().reduced(vars.GetName()), fr, content, errors);
7258 }
7259
7260 xRooNode out(TString::Format("%s.histo", GetName()), nullptr, *this);
7261
7262 RooAbsLValue *v = nullptr;
7263 if (vars.empty()) {
7264 out.fComp = std::shared_ptr<TH1>(BuildHistogram(nullptr, !content, errors, -1, -1, fr));
7265 } else if (vars.size() == 1) {
7266 v = vars.at(0)->get<RooAbsLValue>();
7267 out.fComp = std::shared_ptr<TH1>(BuildHistogram(v, !content, errors, 1, 0, fr));
7268 } else {
7269 throw std::runtime_error("multi-dim histo not yet supported");
7270 }
7271
7272 if (auto h = out.get<TH1>()) {
7273 if (h->GetXaxis()->IsAlphanumeric()) {
7274 // do this to get bin labels
7275 h->GetXaxis()->SetName("xaxis"); // WARNING -- this messes up anywhere we GetXaxis()->GetName()
7276 }
7277 h->SetStats(false);
7278 h->SetName(GetName());
7279 auto hCopy = static_cast<TH1 *>(h->Clone("nominal"));
7280
7281 if (content && !components().empty()) {
7282 RooAbsReal *sf = nullptr; // TODO - support case of RooExtendPdf drawing (see ::Draw)
7283 // build a stack
7284 THStack *stack = new THStack("stack", "");
7285 int count = 2;
7286 std::map<std::string, int> colorByTitle; // TODO: should fill from any existing legend
7287 std::set<std::string> allTitles;
7288 bool titleMatchName = true;
7289 std::map<std::string, TH1 *> histGroups;
7290 std::vector<TH1 *> hhs;
7291
7292 // support for CMS model case where has single component containing many coeffs
7293 // will build stack by setting each coeff equal to 0 in turn, rebuilding the histogram
7294 // the difference from the "full" histogram will be the component
7295 RooArgList cms_coefs;
7296 if (!components().empty()) {
7297 auto comps = components()[0];
7298 for (auto &c : *comps) {
7299 if (c->fFolder == "!.coeffs")
7300 cms_coefs.add(*c->get<RooAbsArg>());
7301 }
7302 }
7303
7304 if (!cms_coefs.empty()) {
7305 RooRealVar zero("zero", "", 0);
7306 std::shared_ptr<TH1> prevHist((TH1 *)h->Clone());
7307 for (auto c : cms_coefs) {
7308 // seems I have to remake the function each time, as haven't figured out what cache needs clearing?
7309 std::unique_ptr<RooAbsReal> f(dynamic_cast<RooAbsReal *>(components()[0]->get()->Clone("tmpCopy")));
7310 zero.setAttribute(
7311 Form("ORIGNAME:%s", c->GetName())); // used in redirectServers to say what this replaces
7312 f->redirectServers(RooArgSet(zero), false, true); // each time will replace one additional coef
7313 // zero.setAttribute(Form("ORIGNAME:%s",c->GetName()),false); (commented out so that on next iteration
7314 // will still replace all prev)
7315 auto hh = xRooNode(*f, *this).BuildHistogram(v, false, false, !v ? -1 : 1, !v ? -1 : 0, fr);
7316 if (sf)
7317 hh->Scale(sf->getVal());
7318 if (strlen(hh->GetTitle()) == 0)
7319 hh->SetTitle(c->GetName()); // ensure all hists has titles
7320 titleMatchName &= (TString(c->GetName()) == hh->GetTitle() ||
7321 TString(hh->GetTitle()).BeginsWith(TString(c->GetName()) + "_"));
7322 std::shared_ptr<TH1> nextHist((TH1 *)hh->Clone());
7323 hh->Add(prevHist.get(), -1.);
7324 hh->Scale(-1.);
7325 hhs.push_back(hh);
7326 prevHist = nextHist;
7327 }
7328 } else {
7329 for (auto &samp : components()) {
7330 auto hh = samp->BuildHistogram(v, false, false, !v ? -1 : 1, !v ? -1 : 0, fr);
7331 if (sf)
7332 hh->Scale(sf->getVal());
7333 hhs.push_back(hh);
7334 if (strlen(hh->GetTitle()) == 0)
7335 hh->SetTitle(samp->GetName()); // ensure all hists has titles
7336 titleMatchName &= (TString(samp->GetName()) == hh->GetTitle() ||
7337 TString(hh->GetTitle()).BeginsWith(TString(samp->GetName()) + "_"));
7338 }
7339 }
7340 for (auto &hh : hhs) {
7341 if (h->GetXaxis()->IsAlphanumeric()) {
7342 // must ensure bin labels match for stack
7343 hh->GetXaxis()->SetName("xaxis");
7344 for (int i = 1; i <= hh->GetNbinsX(); i++)
7345 hh->GetXaxis()->SetBinLabel(i, h->GetXaxis()->GetBinLabel(i));
7346 }
7347 // automatically group hists that all have the same title
7348 if (histGroups.find(hh->GetTitle()) == histGroups.end()) {
7349 histGroups[hh->GetTitle()] = hh;
7350 } else {
7351 // add it into this group
7352 histGroups[hh->GetTitle()]->Add(hh);
7353 delete hh;
7354 continue;
7355 }
7356 auto hhMin = (hh->GetMinimum() == 0) ? hh->GetMinimum(1e-9) : hh->GetMinimum();
7357 if (!stack->GetHists() && h->GetMinimum() > hhMin) {
7358 auto newMin = hhMin - (h->GetMaximum() - hhMin) * gStyle->GetHistTopMargin();
7359 if (hhMin >= 0 && newMin < 0)
7360 newMin = hhMin * 0.99;
7361 h->SetMinimum(newMin); /// adjustYRange(newMin, h->GetMaximum());
7362 }
7363 if (auto it = colorByTitle.find(hh->GetTitle()); it != colorByTitle.end()) {
7364 hh->SetFillColor(it->second);
7365 } else {
7366 bool used = false;
7367 do {
7368 hh->SetFillColor((count++) % 100);
7369 // check not already used this color
7370 used = false;
7371 for (auto hh2 : hhs) {
7372 if (hh != hh2 && hh2->GetFillColor() == hh->GetFillColor()) {
7373 used = true;
7374 break;
7375 }
7376 }
7377 } while (used);
7378 colorByTitle[hh->GetTitle()] = hh->GetFillColor();
7379 }
7380 /*if(stack->GetHists() && stack->GetHists()->GetEntries()>0) {
7381 // to remove rounding effects on bin boundaries, see if binnings compatible
7382 auto _h1 = dynamic_cast<TH1*>(stack->GetHists()->At(0));
7383 if(_h1->GetNbinsX()==hh->GetNbinsX()) TODO ... finish dealing with silly rounding effects
7384 }*/
7385 TString thisOpt = ""; /// dOpt;
7386 // uncomment next line to blend continuous with discrete components .. get some unpleasant "poke through"
7387 // effects though
7388 // if(auto s = samp->get<RooAbsReal>(); s) thisOpt = s->isBinnedDistribution(*dynamic_cast<RooAbsArg*>(v)) ?
7389 // "" : "LF2";
7390 stack->Add(hh, thisOpt);
7391 allTitles.insert(hh->GetTitle());
7392 }
7393
7394 TList *ll = stack->GetHists();
7395 if (ll && ll->GetEntries()) {
7396
7397 // get common prefix to strip off only if all titles match names and
7398 // any title is longer than 10 chars
7399 size_t e = std::min(allTitles.begin()->size(), allTitles.rbegin()->size());
7400 size_t ii = 0;
7401 bool goodPrefix = false;
7402 std::string commonSuffix;
7403 if (titleMatchName && ll->GetEntries() > 1) {
7404 while (ii < e - 1 && allTitles.begin()->at(ii) == allTitles.rbegin()->at(ii)) {
7405 ii++;
7406 if (allTitles.begin()->at(ii) == '_' || allTitles.begin()->at(ii) == ' ')
7407 goodPrefix = true;
7408 }
7409
7410 // find common suffix if there is one .. must start with a "_"
7411 bool stop = false;
7412 while (!stop && commonSuffix.size() < size_t(e - 1)) {
7413 commonSuffix = allTitles.begin()->substr(allTitles.begin()->length() - commonSuffix.length() - 1);
7414 for (auto &t : allTitles) {
7415 if (!TString(t).EndsWith(commonSuffix.c_str())) {
7416 commonSuffix = commonSuffix.substr(1);
7417 stop = true;
7418 break;
7419 }
7420 }
7421 }
7422 if (commonSuffix.find('_') == std::string::npos)
7423 commonSuffix = "";
7424 else
7425 commonSuffix = commonSuffix.substr(commonSuffix.find('_'));
7426 }
7427 if (!goodPrefix)
7428 ii = 0;
7429
7430 // also find how many characters are needed to distinguish all entries (that dont have the same name)
7431 // then carry on up to first space or underscore
7432 size_t jj = 0;
7433 std::map<std::string, std::string> reducedTitles;
7434 while (reducedTitles.size() != allTitles.size()) {
7435 jj++;
7436 std::map<std::string, int> titlesMap;
7437 for (auto &s : allTitles) {
7438 if (reducedTitles.count(s))
7439 continue;
7440 titlesMap[s.substr(0, jj)]++;
7441 }
7442 for (auto &s : allTitles) {
7443 if (titlesMap[s.substr(0, jj)] == 1 && (jj >= s.length() || s.at(jj) == ' ' || s.at(jj) == '_')) {
7444 reducedTitles[s] = s.substr(0, jj);
7445 }
7446 }
7447 }
7448
7449 // strip common prefix and suffix before adding
7450 for (int i = ll->GetEntries() - 1; i >= 0; i--) { // go in reverse order
7451 auto _title = (ll->GetEntries() > 5) ? reducedTitles[ll->At(i)->GetTitle()] : ll->At(i)->GetTitle();
7452 _title = _title.substr(ii < _title.size() ? ii : 0);
7453 if (!commonSuffix.empty() && TString(_title).EndsWith(commonSuffix.c_str()))
7454 _title = _title.substr(0, _title.length() - commonSuffix.length());
7455
7456 dynamic_cast<TNamed *>(ll->At(i))->SetTitle(_title.c_str());
7457
7458 // style hists according to available styles ... creating if necessary
7459 auto _style = xRooNode(*ll->At(i), *this).style(ll->At(i));
7460 if (_style) {
7461 *dynamic_cast<TAttLine *>(ll->At(i)) = *_style;
7462 *dynamic_cast<TAttFill *>(ll->At(i)) = *_style;
7463 *dynamic_cast<TAttMarker *>(ll->At(i)) = *_style;
7464 }
7465 }
7466 }
7467 h->GetListOfFunctions()->Add(stack, "noclearsame");
7468 if (h->GetSumw2() && h->GetSumw2()->GetSum()) {
7469 hCopy->SetFillStyle(3005);
7470 hCopy->SetFillColor(h->GetLineColor());
7471 hCopy->SetMarkerStyle(0);
7472 h->GetListOfFunctions()->Add(hCopy->Clone(".copy"), "e2same");
7473 *static_cast<TAttFill *>(hCopy) = *h;
7474 }
7475 }
7476
7477 h->GetListOfFunctions()->Add(hCopy, "histsame");
7478 if (h->GetSumw2() && h->GetSumw2()->GetSum()) {
7479 h->SetFillStyle(3005);
7480 h->SetFillColor(h->GetLineColor());
7481 h->SetMarkerStyle(0);
7482 }
7483 }
7484
7485 return out;
7486}
7487
7489{
7490 return xRooNode(fComp, xRooNode(range.GetName(), nullptr, *this));
7491}
7492
7493TH1 *xRooNode::BuildHistogram(RooAbsLValue *v, bool empty, bool errors, int binStart, int binEnd,
7494 const xRooNode &_fr) const
7495{
7496 auto rar = get<RooAbsReal>();
7497 if (!rar)
7498 return nullptr;
7499
7500 TObject *vv = rar;
7501
7502 auto t = TH1::AddDirectoryStatus();
7503 TH1::AddDirectory(false);
7504 TH1 *h = nullptr;
7505 if (!v) {
7506 if (binStart != -1 || binEnd != -1) { // allow v to stay nullptr if doing integral (binStart=binEnd=-1)
7507 if (auto _ax = GetXaxis())
7508 v = dynamic_cast<RooAbsLValue *>(_ax->GetParent());
7509 } else {
7510 // don't need to integrate if doing a self-histogram
7511 v = dynamic_cast<RooRealVar *>(rar);
7512 }
7513 if (v)
7514 vv = dynamic_cast<TObject *>(v);
7515 else {
7516 // make a single-bin histogram of just this value
7517 h = new TH1D(rar->GetName(), rar->GetTitle(), 1, 0, 1);
7518 h->GetXaxis()->SetBinLabel(1, rar->GetName());
7519 h->GetXaxis()->SetName(rar->GetName());
7520 }
7521 }
7522
7523 auto x = dynamic_cast<RooRealVar *>(v);
7524 bool setTitle = false;
7525 if (x) {
7526 if (x == rar) {
7527 // self histogram ...
7528 h = new TH1D(rar->GetName(), rar->GetTitle(), 1, 0, 1);
7529 h->Sumw2();
7530 h->GetXaxis()->SetBinLabel(1, rar->GetName());
7531 h->SetBinContent(1, rar->getVal());
7532 if (x->hasError())
7533 h->SetBinError(1, x->getError());
7534 h->SetMaximum(x->hasMax() ? x->getMax()
7535 : (h->GetBinContent(1) + std::max(std::abs(h->GetBinContent(1) * 0.1), 50.)));
7536 h->SetMinimum(x->hasMin() ? x->getMin()
7537 : (h->GetBinContent(1) - std::max(std::abs(h->GetBinContent(1) * 0.1), 50.)));
7538 h->GetXaxis()->SetName(dynamic_cast<TObject *>(v)->GetName());
7539 return h;
7540 }
7541 auto _ax = GetXaxis();
7542 TString binningName = (_ax && _ax->GetParent() == x) ? _ax->GetName() : rar->getStringAttribute("binning");
7543 if (binningName == "")
7544 binningName = rar->GetName();
7545 if (x->hasBinning(binningName)) {
7546 if (x->getBinning(binningName).isUniform()) {
7547 h = new TH1D(rar->GetName(), rar->GetTitle(), x->numBins(binningName) <= 0 ? 100 : x->numBins(binningName),
7548 x->getMin(binningName), x->getMax(binningName));
7549 } else {
7550 h = new TH1D(rar->GetName(), rar->GetTitle(), x->numBins(binningName), x->getBinning(binningName).array());
7551 }
7552 h->GetXaxis()->SetTitle(x->getBinning(binningName).GetTitle());
7553 setTitle = true;
7554 } else if (auto _boundaries =
7555 _or_func(/*rar->plotSamplingHint(*x,x->getMin(),x->getMax())*/ (std::list<double> *)(nullptr),
7556 rar->binBoundaries(*x, -std::numeric_limits<double>::infinity(),
7557 std::numeric_limits<double>::infinity()));
7558 _boundaries) {
7559 std::vector<double> _bins;
7560 for (auto &b : *_boundaries) {
7561 if (_bins.empty() || std::abs(_bins.back() - b) > 1e-5 * _bins.back())
7562 _bins.push_back(b);
7563 } // found sometimes get virtual duplicates in the binning
7564 h = new TH1D(rar->GetName(), rar->GetTitle(), _bins.size() - 1, &_bins[0]);
7565 delete _boundaries;
7566 } else if (!x->hasMax() || !x->hasMin()) {
7567 // use current value of x to estimate range with
7568 h = new TH1D(rar->GetName(), rar->GetTitle(), v->numBins(), x->getVal() * 0.2, x->getVal() * 5);
7569 } else {
7570 h = new TH1D(rar->GetName(), rar->GetTitle(), v->numBins(), x->getBinning().array());
7571 }
7572
7573 } else if (!h) {
7574 h = new TH1D(rar->GetName(), rar->GetTitle(), v->numBins(rar->GetName()), 0, v->numBins(rar->GetName()));
7575 if (auto cat = dynamic_cast<RooAbsCategoryLValue *>(v)) {
7576 for (int i = 0; i < cat->numTypes(); i++) {
7577 cat->setBin(i);
7578 h->GetXaxis()->SetBinLabel(i + 1, cat->getLabel());
7579 }
7580 }
7581 }
7582 if (auto o = dynamic_cast<TObject *>(v); o && !setTitle) {
7583 h->GetXaxis()->SetTitle(o->GetTitle());
7584 }
7586 h->Sumw2();
7587 if (v)
7588 h->GetXaxis()->SetName(dynamic_cast<TObject *>(v)->GetName()); // WARNING: messes up display of bin labels
7589 if (auto s = style(nullptr, false); s) {
7590 static_cast<TAttLine &>(*h) = *s;
7591 static_cast<TAttFill &>(*h) = *s;
7592 static_cast<TAttMarker &>(*h) = *s;
7593 }
7594 if (strlen(h->GetXaxis()->GetTitle()) == 0)
7595 h->GetXaxis()->SetTitle(vv->GetTitle());
7596 auto p = dynamic_cast<RooAbsPdf *>(rar);
7597
7598 // possible speed improvement:
7599 // if(auto spdf = dynamic_cast<RooRealSumPdf*>(p); spdf && spdf->canBeExtended()) {
7600 // p = nullptr; // faster to evaluate sumpdf as a function not a pdf
7601 // }
7602
7603 if (empty && !errors) {
7604 return h;
7605 }
7606
7607 // if (!empty) {
7608
7609 auto _coefs = coefs();
7610
7611 RooFitResult *fr = nullptr;
7612 if (errors) {
7613 // must ensure the fit result we obtain includes pars from coefficients if present
7614 if (_fr.get<RooFitResult>()) {
7615 fr = static_cast<RooFitResult *>(_fr.get<RooFitResult>()->Clone());
7616 } else {
7617 auto frn =
7618 (!_coefs.get() ? *this : xRooNode(RooProduct("tmp", "tmp", RooArgList(*rar, *_coefs.get<RooAbsReal>()))))
7619 .fitResult();
7620 if (strlen(_fr.GetName()))
7621 frn = frn.reduced(_fr.GetName());
7622 fr = dynamic_cast<RooFitResult *>(frn->Clone());
7623 }
7624 if (!GETDMP(fr, _finalPars)) {
7626 }
7627
7628 /// Oct2022: No longer doing this because want to allow fitResult to be used to get partial error
7629 // // need to add any floating parameters not included somewhere already in the fit result ...
7630 // RooArgList l;
7631 // for(auto& p : pars()) {
7632 // auto vv = p->get<RooRealVar>();
7633 // if (!vv) continue;
7634 // if (vv == dynamic_cast<RooRealVar*>(v)) continue;
7635 // if (vv->isConstant()) continue;
7636 // if (fr->floatParsFinal().find(vv->GetName())) continue;
7637 // if (fr->_constPars && fr->_constPars->find(vv->GetName())) continue;
7638 // l.add(*vv);
7639 // }
7640 //
7641 // if (!l.empty()) {
7642 // RooArgList l2; l2.addClone(fr->floatParsFinal());
7643 // l2.addClone(l);
7644 // fr->setFinalParList(l2);
7645 // }
7646
7647 TMatrixTSym<double> *prevCov = static_cast<TMatrixTSym<double> *>(GETDMP(fr, _VM));
7648
7649 if (!prevCov || size_t(fr->covarianceMatrix().GetNcols()) < fr->floatParsFinal().size()) {
7650 TMatrixDSym cov(fr->floatParsFinal().getSize());
7651 if (prevCov) {
7652 for (int i = 0; i < prevCov->GetNcols(); i++) {
7653 for (int j = 0; j < prevCov->GetNrows(); j++) {
7654 cov(i, j) = (*prevCov)(i, j);
7655 }
7656 }
7657 }
7658 int i = 0;
7659 for (auto &p2 : fr->floatParsFinal()) {
7660 if (!prevCov || i >= prevCov->GetNcols()) {
7661 cov(i, i) = pow(dynamic_cast<RooRealVar *>(p2)->getError(), 2);
7662 }
7663 i++;
7664 }
7665 int covQualBackup = fr->covQual();
7666 fr->setCovarianceMatrix(cov);
7667 fr->setCovQual(covQualBackup);
7668 }
7669
7670 if (v) {
7671 // need to remove v from result as we are plotting as function of v
7672 if (auto _p = fr->floatParsFinal().find(dynamic_cast<TObject *>(v)->GetName()); _p) {
7673 RooArgList _pars = fr->floatParsFinal();
7674 _pars.remove(*_p, true);
7675 auto _tmp = fr->reducedCovarianceMatrix(_pars);
7676 int covQualBackup = fr->covQual();
7677 fr->setCovarianceMatrix(_tmp);
7678 fr->setCovQual(covQualBackup);
7679 const_cast<RooArgList &>(fr->floatParsFinal())
7680 .remove(*_p, true); // NOTE: I think this might be a memory leak, should delete _p after removal
7681 }
7682 }
7683 // finally check at least one float has errors defined (might not be cause if in prefit state)
7684 bool hasErrors = false;
7685 for (auto pp : fr->floatParsFinal()) {
7686 if (dynamic_cast<RooRealVar *>(pp)->hasError()) {
7687 hasErrors = true;
7688 break;
7689 }
7690 }
7691 if (!hasErrors) {
7692 errors = false;
7693 delete fr;
7694 }
7695 }
7696
7697 RooArgSet normSet;
7698 if (v)
7699 normSet.add(*dynamic_cast<RooAbsArg *>(v));
7700
7701 if (binEnd == 0)
7702 binEnd = h->GetNbinsX();
7703
7704 bool needBinWidth = false;
7705 // may have MULTIPLE coefficients for the same pdf!
7706
7707 if (x && (p || _coefs.get() || rar->getAttribute("density"))) {
7708 // pdfs of samples embedded in a sumpdf (aka have a coef) will convert their density value to a content
7709 needBinWidth = true;
7710 }
7711
7712 if (auto spdf = dynamic_cast<RooRealSumPdf *>(p);
7713 spdf && spdf->canBeExtended() && !spdf->getFloor() && !_coefs.get()) {
7714 p = nullptr; // if pdf has no floor, will evaluate it as a function to allow it to be negative - evaluation should
7715 // also be faster (no integral)
7716 // exception is if RooRealSumPdf is embedded in a RooAddPdf (detected by presence of coefs) ... then it must be
7717 // evaluated as a pdf technically should check parent is a RooAddPdf, because if was inside a RooRealSumPdf then
7718 // would be evaluated as a function!
7719 }
7720
7721 // check if we need to do any projecting of other observables
7722 RooAbsReal *oldrar = nullptr;
7723 auto _obs = obs();
7724
7725 for (auto o : _obs) {
7726 if (auto rr = o->get<RooRealVar>(); rr && rr->hasRange("coordRange")) {
7727 rr->removeRange("coordRange");
7728 }
7729 }
7730 coords(); // loads current coordinates and populates coordRange, if any
7731
7732 if (auto a = dynamic_cast<RooAbsArg *>(v))
7733 _obs.get<RooArgList>()->remove(*a);
7734 if (!_obs.get<RooArgList>()->empty()) {
7735 oldrar = rar;
7736 normSet.add(*_obs.get<RooArgList>());
7737 // check if any obs are restricted range
7738 bool hasRange = false;
7739 for (auto o : normSet) {
7740 if (auto rr = dynamic_cast<RooRealVar *>(o); rr && rr->hasRange("coordRange")) {
7741 hasRange = true;
7742 break;
7743 }
7744 }
7745 if (p) {
7746 // need to handle special case of RooSimultaneous ... each pdf needs individually projecting over just its
7747 // dependent obs
7748 if (auto s = dynamic_cast<RooSimultaneous *>(p)) {
7749 auto newrar = new RooSimultaneous("projSim", "projSim", const_cast<RooAbsCategoryLValue &>(s->indexCat()));
7750 for (auto pdf : bins()) {
7751 // auto _pdf =
7752 // pdf->get<RooAbsPdf>()->createProjection(*pdf->get<RooAbsPdf>()->getObservables(*_obs.get<RooArgList>()));
7753 auto _pdf =
7754 new xRooProjectedPdf(TString::Format("%s_projection", pdf->GetName()), "", *pdf->get<RooAbsPdf>(),
7755 *pdf->get<RooAbsPdf>()->getObservables(*_obs.get<RooArgList>()));
7756 if (hasRange) {
7757 dynamic_cast<RooAbsPdf *>(_pdf)->setNormRange("coordRange");
7758 }
7759 newrar->addPdf(*_pdf, pdf->coords()["channelCat"]->get<RooCategory>()->getLabel());
7760 }
7761 rar = newrar;
7762 } else {
7763 rar = p->createProjection(
7764 *_obs.get<RooArgList>()); // TODO should use xRooProjectedPdf here too, because not fixed range and
7765 // extend behaviour of RooProjectedPdf in ROOT yet
7766 if (hasRange) {
7767 dynamic_cast<RooAbsPdf *>(rar)->setNormRange("coordRange");
7768 }
7769 }
7770 if (hasRange)
7771 p->setNormRange("coordRange"); // should get cleared when we sterilize
7772 } else {
7773 if (hasRange) {
7774 // commented out passing of normset so that getVal of non-pdf is always a 'raw' value (needed for raw eval
7775 // of RooRealSumPdf)
7776 rar = std::unique_ptr<RooAbsReal>{rar->createIntegral(
7777 *_obs.get<RooArgList>(),
7778 /*RooFit::NormSet(normSet),*/ RooFit::Range("coordRange"))}
7779 .release();
7780 } else {
7781 rar =
7782 std::unique_ptr<RooAbsReal>{rar->createIntegral(*_obs.get<RooArgList>() /*, RooFit::NormSet(normSet)*/)}
7783 .release();
7784 }
7785 }
7786 }
7787
7788 bool scaleExpected = (p && p->canBeExtended() && !_coefs.get());
7789 // Note about above: if pdf has coefficients then its embedded in a RooAddPdf that has coefs defined ...
7790 // in this case we should *not* scale by expected, since the coefs become the scaling instead
7791
7792 std::unique_ptr<RooArgSet> snap(normSet.snapshot());
7793 TStopwatch timeIt;
7794 std::vector<double> lapTimes;
7795 bool warned = false;
7796 if (binStart == -1 && binEnd == -1) {
7797 binEnd = 1;
7798 }
7799 for (int i = std::max(1, binStart); i <= std::min(h->GetNbinsX(), binEnd); i++) {
7800 timeIt.Start(true);
7801 if (x)
7802 x->setVal(h->GetBinCenter(i));
7803 else if (v)
7804 v->setBin(i - 1);
7805 if (x && !x->inRange("coordRange"))
7806 continue;
7807
7808 double r = 0;
7809 if (!empty) {
7810 r = /*(p && p->selfNormalized())*/ rar->getVal(p ? &normSet : nullptr);
7811#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
7812 if (std::isnan(r) && RooNaNPacker::isNaNWithPayload(r)) {
7814 }
7815#endif
7816 if (r && _coefs.get()) {
7817 r *= _coefs.get<RooAbsReal>()->getVal(normSet);
7818 }
7819 if (needBinWidth) {
7820 r *= h->GetBinWidth(i);
7821 }
7822 if (scaleExpected) {
7823 // std::cout << r << " exp = " << p->expectedEvents(normSet) << " for normRange " << (p->normRange() ?
7824 // p->normRange() : "null") << std::endl; p->Print();rar->Print();
7825 r *= (p->expectedEvents(normSet));
7826 } // do in here in case dependency on var
7827 }
7828 h->SetBinContent(i, r);
7829
7830 if (errors) {
7831 double res;
7832 if (p) {
7833 // std::cout << "computing error of :" << h->GetBinCenter(i) << std::endl;
7834 // //fr->floatParsFinal().Print(); fr->covarianceMatrix().Print();
7835 res = PdfWrapper((oldrar) ? *rar : *p, _coefs.get<RooAbsReal>(), !v, oldrar ? p : nullptr)
7836 .getSimplePropagatedError(*fr, normSet);
7837#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
7838 // improved normSet invalidity checking, so assuming no longer need this in 6.28 onwards
7839 p->_normSet = nullptr;
7840#endif
7841 } else {
7842 res = RooProduct("errorEval", "errorEval",
7843 RooArgList(*rar, !_coefs.get() ? RooFit::RooConst(1) : *_coefs.get<RooAbsReal>()))
7844 .getPropagatedError(
7845 *fr /*, normSet*/); // should be no need to pass a normSet to a non-pdf (but not verified this)
7846 // especially important not to pass in the case we are evaluated RooRealSumPdf as a function! otherwise
7847 // error will be wrong
7848 }
7849 if (needBinWidth) {
7850 res *= h->GetBinWidth(i);
7851 }
7852 h->SetBinError(i, res);
7853 }
7854 timeIt.Stop();
7855 lapTimes.push_back(timeIt.RealTime());
7856 double time_estimate =
7857 (lapTimes.size() > 1)
7858 ? (h->GetNbinsX() * (std::accumulate(lapTimes.begin() + 1, lapTimes.end(), 0.) / (lapTimes.size() - 1)))
7859 : 0.;
7860 if (!warned && (lapTimes.at(0) > 10 || (lapTimes.size() > 2 && time_estimate > 60.))) {
7861 TTimeStamp t2;
7862 t2.Add(time_estimate);
7863 Warning("BuildHistogram", "Building this histogram will take until %s", t2.AsString());
7864 if (errors) {
7865 // install interrupt handler
7866 runningNode = this;
7867 gOldHandlerr = signal(SIGINT, buildHistogramInterrupt);
7868 }
7869 warned = true;
7870 }
7871 if (fInterrupted) {
7872 if (errors) {
7873 Warning("BuildHistogram", "Skipping errors for remaining bins");
7874 errors = false;
7875 }
7876 fInterrupted = false;
7877 }
7878 }
7879 if (gOldHandlerr) {
7880 signal(SIGINT, gOldHandlerr);
7881 gOldHandlerr = 0;
7882 }
7883 normSet = *snap;
7884
7885 if (oldrar) {
7886 std::vector<RooAbsArg *> extra;
7887 if (auto s = dynamic_cast<RooSimultaneous *>(rar)) {
7888 // need to delete all the subpdfs we created too
7889 for (auto _pdf : s->servers())
7890 if (dynamic_cast<RooAbsPdf *>(_pdf))
7891 extra.push_back(_pdf);
7892 }
7893 extra.push_back(rar);
7894 rar = oldrar;
7895 xRooNode(*rar).sterilize(); // need to clear the cache of the created integral - do this before deleting things!
7896 for (auto a : extra)
7897 delete a;
7898 } else {
7899 sterilize(); // needed to forget about the normSet that was passed to getVal()
7900 }
7901
7902 if (!p && !rar->getAttribute("density") && !needBinWidth) {
7903 h->GetYaxis()->SetTitle(rar->getStringAttribute("units"));
7904 } else if ((p && p->canBeExtended()) || (!p && needBinWidth)) {
7905 h->GetYaxis()->SetTitle("Events");
7906 } else {
7907 h->GetYaxis()->SetTitle("Probability Mass");
7908 }
7909 h->GetYaxis()->SetMaxDigits(3);
7910
7911 if (errors) {
7912 delete fr;
7913 }
7914 //}
7915 return h;
7916}
7917
7918double xRooNode::GetBinData(int bin, const char *dataName)
7919{
7920 auto node = datasets().find(dataName);
7921 if (!node)
7922 return std::numeric_limits<double>::quiet_NaN();
7923 return node->GetBinContent(bin);
7924}
7925
7926std::vector<double> xRooNode::GetBinContents(int binStart, int binEnd) const
7927{
7928 if (fBinNumber != -1) {
7929 if (binStart != binEnd || !fParent) {
7930 throw std::runtime_error(TString::Format("%s is a bin - only has one value", GetName()));
7931 }
7932 return fParent->GetBinContents(fBinNumber, fBinNumber);
7933 }
7934 std::vector<double> out;
7935 if (get<RooAbsData>()) {
7936 auto g = BuildGraph(
7937 nullptr,
7938 (binStart != -1 ||
7939 binEnd != -1) /*include points for zeros unless we are asking for a single point with start=end=-1*/);
7940 if (!g) {
7941 return out;
7942 }
7943 if (binStart == binEnd && binStart == -1) {
7944 // integral over all bins if getting bin content -1
7945 double integral(0);
7946 for (int i = 0; i < g->GetN(); i++)
7947 integral += g->GetPointY(i);
7948 out.push_back(integral);
7949 delete g;
7950 return out;
7951 }
7952 for (int i = binStart - 1; i < g->GetN() && (binEnd == 0 || i < binEnd); i++) {
7953 out.push_back(g->GetPointY(i));
7954 }
7955 delete g;
7956 return out;
7957 }
7958
7959 bool doIntegral = false;
7960 if (binStart == binEnd && binStart == -1) {
7961 binStart = -1;
7962 binEnd = -1;
7963 doIntegral = true;
7964 } // return integral if request bin -1
7965 auto h = BuildHistogram(nullptr, false, false, binStart, binEnd);
7966 if (!h) {
7967 throw std::runtime_error(TString::Format("%s has no content", GetName()));
7968 }
7969 if (binEnd == 0) {
7970 binEnd = h->GetNbinsX();
7971 }
7972 if (doIntegral) {
7973 double tot = 0;
7974 for (int i = 1; i <= h->GetNbinsX(); i++) {
7975 tot += h->GetBinContent(i);
7976 }
7977 out.push_back(tot);
7978 } else {
7979 for (int i = binStart; i <= binEnd; i++) {
7980 out.push_back(h->GetBinContent(i));
7981 }
7982 }
7983 delete h;
7984 return out;
7985}
7986
7988{
7989 if (auto a = get<RooAbsArg>(); a) {
7990 // go through servers looking for 'main' thing
7991 for (auto &l : a->servers()) {
7992 if (l->getAttribute("MAIN_MEASUREMENT") || l->InheritsFrom("RooRealSumPdf") || l->InheritsFrom("RooAddPdf")) {
7993 return xRooNode(*l, *this);
7994 }
7995 }
7996 // the main child of a RooProduct is one that has the same name (/alias) as the product (except if is a bin
7997 // factor)
7998 if (a->IsA() == RooProduct::Class() && fBinNumber == -1) {
7999 for (auto &l : factors()) {
8000 if (strcmp(l->GetName(), GetName()) == 0) {
8001 return *l;
8002 }
8003 }
8004 }
8005 }
8006 return xRooNode();
8007}
8008
8010{
8011 if (auto o = get(); o)
8012 o->Inspect();
8013 else
8015}
8016
8017bool TopRightPlaceBox(TPad *p, TObject *o, double w, double h, double &xl, double &yb)
8018{
8019#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
8020 // reinitialize collide grid because the filling depends on fUxmin and fUxmax (and ymin ymax too)
8021 // and these aren't filled on the first time we do the placement (they init to 0 and 1), but will be filled
8022 // subsequently
8023 for (int i = 0; i < p->fCGnx; i++) {
8024 for (int j = 0; j < p->fCGny; j++) {
8025 p->fCollideGrid[i + j * p->fCGnx] = true;
8026 }
8027 }
8028 p->FillCollideGrid(o);
8029 Int_t iw = (int)(p->fCGnx * w);
8030 Int_t ih = (int)(p->fCGny * h);
8031
8032 Int_t nxmax = p->fCGnx - iw - 1 - p->fCGnx * p->GetRightMargin();
8033 Int_t nymax = p->fCGny - ih - 1 - p->fCGny * p->GetTopMargin();
8034
8035 for (Int_t j = nymax; j >= 0; j--) {
8036 for (Int_t i = nxmax; i >= 0; i--) {
8037 if (p->Collide(i, j, iw, ih)) {
8038 continue;
8039 } else {
8040 xl = (double)(i) / (double)(p->fCGnx);
8041 yb = (double)(j) / (double)(p->fCGny);
8042 return true;
8043 }
8044 }
8045 }
8046 return false;
8047#else
8048 return p->PlaceBox(o, w, h, xl, yb, "trw");
8049#endif
8050}
8051
8052TPaveText *getPave(const char *name = "labels", bool create = true, bool doPaint = false)
8053{
8054 if (auto p = dynamic_cast<TPaveText *>(gPad->GetPrimitive(name)); p) {
8055 if (doPaint)
8056 gPad->PaintModified(); //-- slows down x11 so trying to avoid
8057 return p;
8058 }
8059 if (!create) {
8060 return nullptr;
8061 }
8062 auto l = new TPaveText(gPad->GetLeftMargin() + 0.02, 1. - gPad->GetTopMargin() - 0.08, 0.6,
8063 1. - gPad->GetTopMargin() - 0.08);
8064 l->SetBorderSize(0);
8065 if (l->GetTextSize() == 0)
8066 l->SetTextSize(gStyle->GetTitleYSize());
8067
8069 // l->SetMargin(0);
8070 l->SetFillStyle(0);
8071 l->SetName(name);
8072 l->Draw();
8073 l->ConvertNDCtoPad();
8074 return l;
8075}
8076
8077TLegend *getLegend(bool create = true, bool doPaint = false)
8078{
8079 if (auto p = dynamic_cast<TLegend *>(gPad->GetPrimitive("legend")); p) {
8080 double x, y;
8081 double w = p->GetX2NDC() - p->GetX1NDC(), h = p->GetY2NDC() - p->GetY1NDC();
8082 if (doPaint)
8083 gPad->PaintModified(); //-- slows down x11 so trying to avoid
8084 if (TopRightPlaceBox(dynamic_cast<TPad *>(gPad), p, w, h, x, y)) {
8085 // squash inside the frame ..
8086 // std::cout << gPad->GetName() << ":" << x << " , " << y << " , " << w << " , " << h << std::endl;
8087 x = std::max(x, (gPad->GetLeftMargin() + 0.02));
8088 y = std::max(y, (gPad->GetBottomMargin() + 0.02));
8089 x = std::min(x, (1. - gPad->GetRightMargin() - 0.02) - w);
8090 y = std::min(y, (1. - gPad->GetTopMargin() - 0.02) - h);
8091 h = std::min(h, (1. - gPad->GetTopMargin() - 0.02) - y);
8092 w = std::min(w, (1. - gPad->GetRightMargin() - 0.02) - x);
8093 // std::cout << gPad->GetName() << ":" << x << " , " << y << " , " << h << " , " << w << std::endl;
8094 p->SetX1NDC(x);
8095 p->SetY1NDC(y);
8096 p->SetX2NDC(x + w);
8097 p->SetY2NDC(y + h);
8098 gPad->Modified();
8099 }
8100 return p;
8101 }
8102 // look for a parent pad called 'legend' and create it there if existing
8103 auto p = gPad;
8104 while ((p != p->GetMother()) && (p = p->GetMother())) {
8105 if (auto q = dynamic_cast<TVirtualPad *>(p->GetPrimitive("legend")); q) {
8106 q->Modified();
8107 p = q;
8108 break;
8109 }
8110 }
8111 auto tmpPad = gPad;
8112 TLegend *l = nullptr;
8113 if (p && strcmp(p->GetName(), "legend") == 0) {
8114 if (l = dynamic_cast<TLegend *>(p->GetPrimitive("legend")); l || !create)
8115 return l;
8116 p->cd();
8117 l = new TLegend(gPad->GetLeftMargin(), 1. - gPad->GetTopMargin(), 1. - gPad->GetRightMargin(),
8118 gPad->GetBottomMargin());
8119 } else {
8120 if (!create)
8121 return nullptr;
8122 l = new TLegend(0.6, 1. - gPad->GetTopMargin() - 0.08, 0.75, 1. - gPad->GetTopMargin() - 0.08);
8123 l->SetBorderSize(0);
8124 if (l->GetTextSize() == 0)
8125 l->SetTextSize(gStyle->GetTitleYSize());
8126 }
8128 // l->SetMargin(0);
8129 l->SetFillStyle(0);
8130 l->SetName("legend");
8131 l->Draw();
8132 l->ConvertNDCtoPad();
8133 tmpPad->cd();
8134 return l;
8135};
8136
8137void addLegendEntry(TObject *o, const char *title, const char *opt)
8138{
8139 auto l = getLegend();
8140 if (!l)
8141 return;
8142 // check for entry already existing with same title
8143 for (auto a : *l->GetListOfPrimitives()) {
8144 if (!strcmp(dynamic_cast<TLegendEntry *>(a)->GetLabel(), title))
8145 return;
8146 }
8147 if (l->GetListOfPrimitives()->GetEntries() > 20)
8148 return; // todo: create an 'other' entry?
8149
8150 l->AddEntry(o, title, opt);
8151 if (auto nObj = l->GetListOfPrimitives()->GetEntries(); nObj > 0) {
8152 // each entry takes up 0.05 ... maximum of N*(N+4) (where N is # cols) before next column
8153 int nn = l->GetNColumns();
8154 nn *= (nn + 4);
8155 if (nObj > 1 && (nObj % nn) == 1) {
8156 l->SetNColumns(l->GetNColumns() + 1);
8157 l->SetX1NDC(l->GetX2NDC() - 0.15 * l->GetNColumns());
8158 }
8159 l->SetY1NDC(l->GetY2NDC() - 0.05 * gPad->GetHNDC() * std::ceil((double(nObj) / l->GetNColumns())));
8160 }
8161
8162 getLegend(); // to mark modified
8163}
8164
8165// this exists to avoid calling update excessively because it slows down x11 ... but still
8166// need to call update twice if have a legend drawn in order to relocate it.
8168public:
8169 PadRefresher(TVirtualPad *p) : fPad(p) { nExisting++; }
8171 {
8172 if (fPad) {
8173 getLegend(false, true);
8174 fPad->GetCanvas()->Paint();
8175 fPad->GetCanvas()->Update();
8176#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 30, 00)
8177 fPad->GetCanvas()->ResetUpdated(); // stops previous canvas being replaced in a jupyter notebook
8178#endif
8179 fPad->cd();
8180 }
8181 nExisting--;
8182 }
8183 TVirtualPad *fPad = nullptr;
8184 static int nExisting;
8185};
8186
8188
8190{
8191 // in order to catch exceptions to prevent crash of GUI, do this:
8192 if (gROOT->FromPopUp()) {
8193 gROOT->SetFromPopUp(false);
8194 try {
8195 Draw(opt);
8196 } catch (const std::exception &e) {
8197 new TGMsgBox(
8198 gClient->GetRoot(),
8199 (gROOT->GetListOfBrowsers()->At(0))
8200 ? dynamic_cast<TGWindow *>(static_cast<TBrowser *>(gROOT->GetListOfBrowsers()->At(0))->GetBrowserImp())
8201 : gClient->GetRoot(),
8202 "Exception", e.what(),
8203 kMBIconExclamation); // deletes self on dismiss?
8204 }
8205 gROOT->SetFromPopUp(true);
8206 return;
8207 }
8208
8209 TString sOpt2(opt);
8210 sOpt2.ToLower();
8211 if (!get() && !IsFolder() && !sOpt2.Contains("x="))
8212 return;
8213
8214 if (auto ir = get<RooStats::HypoTestInverterResult>()) {
8215 xRooHypoSpace(ir).Draw(opt);
8217 return;
8218 } else if (get<RooStats::HypoTestResult>()) {
8219 if (gPad)
8220 gPad->Clear();
8221 xRooNLLVar::xRooHypoPoint(std::dynamic_pointer_cast<RooStats::HypoTestResult>(fComp)).Draw(opt);
8222 {
8223 PadRefresher p(gPad); // refreshes the pad
8224 }
8226 return;
8227 }
8228
8229 if (sOpt2 == "pcls" && get<RooRealVar>() && fParent && fParent->get<RooAbsPdf>()) {
8230 // use the first selected dataset
8231 auto _dsets = fParent->datasets();
8232 // bool _drawn=false;
8233 TString dsetName = "";
8234 for (auto &d : _dsets) {
8235 if (d->get()->TestBit(1 << 20)) {
8236 dsetName = d->get()->GetName();
8237 break;
8238 }
8239 }
8240 auto hs = fParent->nll(dsetName.Data()).hypoSpace(get<RooRealVar>()->GetName());
8241 hs.limits("cls visualize");
8242 hs.SetName(TUUID().AsString());
8243 if (ws()) {
8244 ws()->import(*hs.result());
8245 }
8246 return;
8247 }
8248
8249 if (auxFunctions.empty()) {
8250 // add the defaults: Ratio and Signif
8252 "Ratio",
8253 [](double a, double b, double) {
8254 if (a == 0)
8255 return 0.;
8256 if (b == 0 && a == 0)
8257 return 1.;
8258 return a / b;
8259 },
8260 true);
8262 "Signif",
8263 [](double n, double b, double sigma) {
8264 double t0 = 0;
8265 if (sigma <= 0.) {
8266 // use simplified expression ...
8267 t0 = 2. * (((n == 0) ? 0 : n * log(n / b)) - (n - b));
8268 } else {
8269 double sigma2 = sigma * sigma;
8270 double b_hathat = 0.5 * (b - sigma2 + sqrt(pow(b - sigma2, 2) + 4 * n * sigma2));
8271 // double s_hat = n - m;
8272 // double b_hat = m;
8273 t0 = 2. * (((n == 0) ? 0 : n * log(n / b_hathat)) + b_hathat - n + pow(b - b_hathat, 2) / (2. * sigma2));
8274 }
8275 if (t0 < 0)
8276 return 0.; // can happen from numerical precision
8277 return (n >= b) ? sqrt(t0) : -sqrt(t0);
8278 },
8279 false);
8280 }
8281
8282 TString sOpt(opt);
8283
8284 RooAbsLValue *v = nullptr;
8285 std::vector<double> xPoints;
8286 if (sOpt2.Contains("x=")) {
8287 // specifying a particular var to scan over ...
8288 int _idx = sOpt2.Index("x=");
8289 int _eidx = sOpt2.Index(';', _idx);
8290 TString varPart = sOpt(_idx + 2, (_eidx < 0 ? sOpt2.Length() : _eidx) - (_idx + 2));
8291 TString varName = varPart;
8292 // if varName is of form str(num,num,num) then can infer scan points
8293 if (auto _idx2 = varPart.Index("("); _idx2 > 0) {
8294 varName = varPart(0, _idx2);
8295 TStringToken pattern(TString(varPart(_idx2 + 1, varPart.Length() - _idx2 - 2)), ",");
8296 double min(0), max(0);
8297 int nBins = 0;
8298 int ii = 0;
8299 while (pattern.NextToken()) {
8300 TString s = pattern;
8301 if (ii == 0)
8302 nBins = s.Atoi();
8303 else if (ii == 1)
8304 min = s.Atof();
8305 else if (ii == 2)
8306 max = s.Atof();
8307 ii++;
8308 }
8309 if (nBins > 100)
8310 nBins = 100; // limit scanning to 100 points
8311 if (nBins > 1) {
8312 for (double x = min; x <= max; x += (max - min) / (nBins - 1)) {
8313 xPoints.push_back(x);
8314 }
8315 } else if (nBins == 1)
8316 xPoints.push_back((min + max) / 2.);
8317 }
8318 v = getObject<RooAbsRealLValue>(varName.Data()).get();
8319 if (!v) {
8320 Error("Draw", "Could not find variable %s", varName.Data());
8321 return; // don't throw because if happens in browser will cause ROOT to exit
8322 }
8323 if (xPoints.empty() && !obs().find(varName.Data())) { // will draw obs as regular (e.g. hist)
8324 double tmp = static_cast<RooAbsRealLValue *>(v)->getVal();
8325 for (int i = 0; i < v->numBins(GetName()); i++) {
8326 v->setBin(i, GetName());
8327 xPoints.push_back(static_cast<RooAbsRealLValue *>(v)->getVal());
8328 }
8329 static_cast<RooAbsRealLValue *>(v)->setVal(tmp);
8330 }
8331 sOpt2 = TString(sOpt2(0, _idx)) + sOpt2(_idx + 2 + varPart.Length() + 1, sOpt2.Length());
8332 sOpt = TString(sOpt(0, _idx)) + sOpt(_idx + 2 + varPart.Length() + 1, sOpt.Length());
8333 }
8334 TString forceNames = "";
8335 if (sOpt2.Contains("force")) {
8336 // force plots show how much NLL changes wrt to a change of variables
8337 if (get<RooRealVar>() && fParent && fParent->get<RooAbsPdf>()) {
8338 // assume want force of this parameter from the parent pdf
8339 TString ff = sOpt(sOpt2.Index("force"), sOpt2.Index("force") + 5);
8340 sOpt.ReplaceAll(ff, TString::Format("force%s", get()->GetName()));
8341 fParent->Draw(sOpt);
8342 return;
8343 } else if (get<RooAbsPdf>()) {
8344 // extract the parameter(s) to calculate force for
8345 forceNames = sOpt(sOpt2.Index("force") + 5, sOpt2.Length());
8346 sOpt = sOpt(0, sOpt2.Index("force"));
8347 sOpt2 = sOpt2(0, sOpt2.Index("force"));
8348 } else {
8349 Error("Draw", "Can only compute forces with PDFs");
8350 return; // don't throw because will cause browser to exit if done from there
8351 }
8352 }
8353 bool hasOverlay = sOpt2.Contains("overlay");
8354 TString overlayName = "";
8355 if (hasOverlay) {
8356 // whatever follows overlay is the variation name
8357 overlayName = sOpt(sOpt2.Index("overlay") + 7, sOpt2.Length());
8358 sOpt = sOpt(0, sOpt2.Index("overlay"));
8359 sOpt2 = sOpt2(0, sOpt2.Index("overlay"));
8360 }
8361 if (sOpt2.Contains("ratio") && !sOpt2.Contains("auxratio"))
8362 sOpt += "auxRatio";
8363 if (sOpt2.Contains("significance") && !sOpt2.Contains("auxsignif"))
8364 sOpt += "auxSignif";
8365
8366 std::string auxPlotTitle = "";
8367 for (auto &[k, _] : auxFunctions) {
8368 if (sOpt.Contains(TString::Format("aux%s", k.c_str()))) {
8369 auxPlotTitle = k;
8370 }
8371 sOpt.ReplaceAll(TString::Format("aux%s", k.c_str()), "");
8372 }
8373
8374 sOpt.ToLower();
8375 sOpt.ReplaceAll("ratio", "");
8376 sOpt.ReplaceAll("significance", ""); // remove old option if still given
8377 bool nostack = sOpt.Contains("nostack");
8378 sOpt.ReplaceAll("nostack", "");
8379 bool hasSame = sOpt.Contains("same");
8380 sOpt.ReplaceAll("same", "");
8381 bool hasGoff = sOpt.Contains("goff");
8382 sOpt.ReplaceAll("goff", "");
8383 bool hasFR = sOpt.Contains("pull") && !get<RooFitResult>();
8384 sOpt.ReplaceAll("pull", "");
8385 bool hasText = sOpt.Contains("text");
8386 bool hasErrorOpt = sOpt.Contains("e");
8387 sOpt.ReplaceAll("e", "");
8388 if (hasText)
8389 sOpt.ReplaceAll("txt", "text");
8390 if (auxPlotTitle == "Signif")
8391 hasErrorOpt = true; // must calculate error to calculate significance
8392 if (hasOverlay)
8393 hasSame = true; // when overlaying must be putting on same
8394
8395 TVirtualPad *pad = gPad;
8396
8397 TH1 *hAxis = nullptr;
8398
8399 auto clearPad = []() {
8400 gPad->Clear();
8401 if (gPad->GetNumber() == 0) {
8402 gPad->SetBottomMargin(gStyle->GetPadBottomMargin());
8403 gPad->SetTopMargin(gStyle->GetPadTopMargin());
8404 gPad->SetLeftMargin(gStyle->GetPadLeftMargin());
8405 gPad->SetRightMargin(gStyle->GetPadRightMargin());
8406 }
8407 // if (gPad == gPad->GetCanvas()) {
8408 // gPad->GetCanvas()->SetCanvasSize( gPad->GetCanvas()->GetWindowWidth() - 4,
8409 // gPad->GetCanvas()->GetWindowHeight() - 28 );
8410 // }
8411 };
8412
8413 if (!hasSame || !pad) {
8414 if (!pad) {
8416 pad = gPad;
8417 }
8418
8419 } else {
8420 // get the histogram representing the axes
8421 hAxis = dynamic_cast<TH1 *>(pad->GetPrimitive("axis"));
8422 if (!hAxis) {
8423 for (auto o : *pad->GetListOfPrimitives()) {
8424 if (hAxis = dynamic_cast<TH1 *>(o); hAxis)
8425 break;
8426 }
8427 }
8428 if (hAxis && !v) {
8429 v = getObject<RooAbsLValue>(hAxis->GetXaxis()->GetName()).get();
8430 }
8431 }
8432
8433 if (!hasSame) {
8434 gPad->SetName(GetName());
8435 gPad->SetTitle(GetTitle());
8436 }
8437
8438 PadRefresher padRefresh(((!hasSame || hasOverlay || PadRefresher::nExisting == 0) && !hasGoff) ? gPad : nullptr);
8439
8440 auto adjustYRange = [&](double min, double max, TH1 *hh = nullptr, bool symmetrize = false) {
8441 if (!hh)
8442 hh = hAxis;
8443 // give max and min a buffer ...
8444 max += gStyle->GetHistTopMargin() * (max - min);
8445 if (min > 0)
8446 min = std::max(min * 0.9, min - gStyle->GetHistTopMargin() * (max - min));
8447 if (hh) {
8448 double ymin = hh->GetMinimum();
8449 double ymax = hh->GetMaximum();
8450 if (hh->GetMaximumStored() == -1111)
8451 ymax += gStyle->GetHistTopMargin() * (ymax - ymin);
8452 if (hh->GetMinimumStored() == -1111) {
8453 if (gStyle->GetHistMinimumZero() && ymax >= 0)
8454 ymin = 0;
8455 else if (ymin < 0)
8456 ymin -= gStyle->GetHistTopMargin() * (ymax - ymin);
8457 else
8458 ymin = std::max(ymin * 0.9, ymin - gStyle->GetHistTopMargin() * (ymax - ymin));
8459 // see TGLPlotPainter to complete the mimic, but we leave off here truncating @ 0 if ymax>0
8460 }
8461 // make ymax at least 3x bigger than biggest error if has error
8462 if (hh->GetSumw2()) {
8463 double smallestErrDown3 = -std::numeric_limits<double>::infinity();
8464 double smallestErrUp3 = std::numeric_limits<double>::infinity();
8465 for (int i = 1; i <= hh->GetNbinsX(); i++) {
8466 smallestErrDown3 = std::max(smallestErrDown3, hh->GetBinContent(i) - 3 * hh->GetBinError(i));
8467 smallestErrUp3 = std::min(smallestErrUp3, hh->GetBinContent(i) + 3 * hh->GetBinError(i));
8468 }
8469 max = std::max(max, smallestErrUp3);
8470 min = std::min(min, smallestErrDown3);
8471 }
8472 bool change = false;
8473 if (min < ymin) {
8474 ymin = min;
8475 change = true;
8476 }
8477 if (max > ymax) {
8478 ymax = max;
8479 change = true;
8480 }
8481 if (change) {
8482 // note: unfortunately when user 'unzooms' y axis it resets stored minimum to -1111, so lose range
8483 if (symmetrize) {
8484 double down = hh->GetBinContent(1) - ymin;
8485 double up = ymax - hh->GetBinContent(1);
8486 if (down > up)
8487 ymax = hh->GetBinContent(1) + down;
8488 else
8489 ymin = hh->GetBinContent(1) - up;
8490 }
8491 if (hh == hAxis && pad && !pad->GetLogy() && ymin > 0 && (log10(ymax) - log10(max)) >= 3) {
8492 // auto-log the pad
8493 pad->SetLogy();
8494 }
8495 if (hh == hAxis && pad && ymin == 0 && pad->GetLogy()) {
8496 ymin = 1e-2;
8497 }
8498 if (ymin == 0 && ymax > 10)
8499 ymin = 0.1; // adjust min so if user activates log scale it isn't bad
8500 hh->SetMinimum(ymin);
8501 hh->SetMaximum(ymax);
8502 hh->GetYaxis()->Set(1, ymin, ymax);
8503 hh->SetAxisRange(ymin, ymax, "Y");
8504 }
8505 }
8506 };
8507
8508 auto graphMinMax = [](TGraphAsymmErrors *gr) {
8509 double ymax = -std::numeric_limits<double>::infinity();
8510 double ymin = std::numeric_limits<double>::infinity();
8511 for (int i = 0; i < gr->GetN(); i++) {
8512 ymax = std::max(ymax, gr->GetPointY(i) + gr->GetErrorYhigh(i));
8513 ymin = std::min(ymin, gr->GetPointY(i) - gr->GetErrorYlow(i));
8514 }
8515 return std::make_pair(ymin, ymax);
8516 };
8517
8518 if (!xPoints.empty()) {
8519 // create a graph using GetContent
8521 out->SetName(GetName());
8522 out->SetTitle(GetTitle());
8523 out->SetFillColor(out->GetLineColor());
8524 out->SetMarkerStyle(0);
8525 out->SetFillStyle(hasErrorOpt ? 3005 : 0);
8526 double tmp = static_cast<RooAbsRealLValue *>(v)->getVal();
8527 for (auto &x : xPoints) {
8528 static_cast<RooAbsRealLValue *>(v)->setVal(x);
8529 out->AddPoint(x, GetContent());
8530 if (hasErrorOpt) {
8531 out->SetPointEYlow(out->GetN() - 1, GetError());
8532 out->SetPointEYhigh(out->GetN() - 1, out->GetErrorYlow(out->GetN() - 1)); // symmetric error for now
8533 }
8534 }
8535 static_cast<RooAbsRealLValue *>(v)->setVal(tmp);
8536 out->GetHistogram()->GetXaxis()->SetTitle(static_cast<RooAbsRealLValue *>(v)->GetTitle());
8537 out->SetBit(kCanDelete);
8538 out->Draw(TString(hasSame ? "L" : "AL") + (hasErrorOpt ? "3" : ""));
8539 return;
8540 }
8541
8542 if (hasFR) {
8543 // drawing the fitresult as a pull plot on a subpad, and rest of the draw elsewhere
8544 clearPad();
8545 pad->Divide(1, 2, 1e-9, 1e-9); //,0,0);
8546 pad->GetPad(1)->SetPad(0, 0.2, 1, 1);
8547 pad->GetPad(2)->SetPad(0, 0, 1, 0.2);
8548 TString optNoFR(opt);
8549 optNoFR.ReplaceAll("pull", "");
8550 pad->cd(1);
8551 Draw(optNoFR);
8552 pad->cd(2);
8553 auto _fr = fitResult();
8554 _fr.Draw();
8555 // switch into subpad
8556 gPad->cd(1);
8557 gPad->SetFillColor(kGray);
8558 gPad->GetFrame()->SetFillColor(kWhite);
8559 gPad->GetFrame()->SetFillStyle(1001);
8560 gPad->SetTopMargin(0);
8561 gPad->SetBottomMargin(0);
8562 gPad->SetName("pull");
8563 // split the pull graph into individual points -- for benefit of GUI status bar
8564 auto pullGraph = dynamic_cast<TGraphAsymmErrors *>(gPad->GetPrimitive("pulls"));
8565 if (!pullGraph) {
8566 Error("Draw", "Couldn't find pull graph");
8567 return;
8568 }
8569 pullGraph->SetName("nominal");
8570 TMultiGraph *mg = new TMultiGraph;
8571 mg->SetName("editables");
8572
8573 auto scaleHist = static_cast<TH1 *>(pullGraph->FindObject("scales"));
8574 if (!scaleHist)
8575 throw std::runtime_error("Could not find scales in fit result");
8576
8577 for (auto i = 0; i < pullGraph->GetN(); i++) {
8578 auto g = new TGraphAsymmErrors;
8579 g->SetName(scaleHist->GetXaxis()->GetBinLabel(i + 1));
8580 auto _p = dynamic_cast<RooRealVar *>(_fr.get<RooFitResult>()->floatParsFinal().find(g->GetName()));
8581 if (!_p) {
8582 Warning("Draw", "Found a non-var in the floatParsFinal list: %s - this shouldn't happen", g->GetName());
8583 continue;
8584 }
8585 g->SetTitle(TString::Format(
8586 "%s=%g +/- %s [%g,%g]", strlen(_p->GetTitle()) ? _p->GetTitle() : _p->GetName(), _p->getVal(),
8587 _p->hasAsymError() ? TString::Format("(%g,%g)", _p->getAsymErrorHi(), _p->getAsymErrorLo()).Data()
8588 : TString::Format("%g", _p->getError()).Data(),
8589 scaleHist->GetBinContent(i + 1), scaleHist->GetBinError(i + 1)));
8590 g->SetPoint(0, pullGraph->GetPointX(i), pullGraph->GetPointY(i));
8591 g->SetPointEYhigh(0, pullGraph->GetErrorYhigh(i));
8592 g->SetPointEYlow(0, pullGraph->GetErrorYlow(i));
8593 g->SetEditable(true);
8594 g->SetHighlight(true);
8595 g->SetMarkerStyle(20);
8596 g->SetMarkerSize(0.5);
8597 mg->Add(g);
8598 }
8599 // gPad->GetListOfPrimitives()->Remove(pullGraph); delete pullGraph;
8600 mg->Draw("z0p");
8601 mg->SetBit(kCanDelete);
8602 auto _thisClone = new xRooNode("node", fComp, fParent);
8603 _thisClone->SetBit(kCanDelete);
8604 _thisClone->AppendPad();
8605
8606 // ensure statusbar visible for interactive plot
8607 // turned this off for now ... as not needed if doing through browser, status bar already there
8608 // if (gPad->GetCanvas() && !gPad->GetCanvas()->TestBit(TCanvas::kShowEventStatus)) {
8609 // gPad->GetCanvas()->ToggleEventStatus();
8610 // }
8611 gPad->AddExec("interactivePull", TString::Format("%s::Interactive_Pull()", ClassName()));
8612
8613 pad->cd();
8614 return;
8615 }
8616
8617 if (auto _simPdf = get<RooSimultaneous>(); _simPdf) {
8618 int _size = 0;
8619 auto _channels = bins();
8620 for (auto &_v : _channels) {
8621 if (!_v->IsHidden())
8622 _size++;
8623 }
8624 if (!hasSame) {
8625 clearPad();
8626 pad->SetBorderSize(0);
8627 // if (pad->GetCanvas() == pad) {
8628 // if(_size>4) {
8629 // int n = _size;
8630 // Int_t w = 1, h = 1;
8631 // if (pad->GetCanvas()->GetWindowWidth() > pad->GetCanvas()->GetWindowHeight()) {
8632 // w = TMath::Ceil(TMath::Sqrt(n));
8633 // h = TMath::Floor(TMath::Sqrt(n));
8634 // if (w*h < n) w++;
8635 // } else {
8636 // h = TMath::Ceil(TMath::Sqrt(n));
8637 // w = TMath::Floor(TMath::Sqrt(n));
8638 // if (w*h < n) h++;
8639 // }
8640 // // adjust the window size to display only 4 in the window, with scroll bars
8641 // pad->GetCanvas()->SetCanvasSize( w*((pad->GetCanvas()->GetWindowWidth()-4)/2.) -16
8642 // ,h*((pad->GetCanvas()->GetWindowHeight()-28)/2.) - 16 );
8643 // } else {
8644 // //pad->GetCanvas()->Set(
8645 // w*(pad->GetCanvas()->GetWindowWidth()/2.),h*(pad->GetCanvas()->GetWindowHeight()/2.)) )
8646 // }
8647 // }
8648 dynamic_cast<TPad *>(pad)->DivideSquare(_size, 1e-9, 1e-9);
8649 }
8650 int i = 0;
8651 auto &chanVar = const_cast<RooAbsCategoryLValue &>(_simPdf->indexCat());
8652 // auto _idx = chanVar.getIndex();
8653 auto _range = GetRange();
8654 std::vector<TString> chanPatterns;
8655 if (_range && strlen(_range)) {
8656 TStringToken pattern(_range, ",");
8657 while (pattern.NextToken()) {
8658 chanPatterns.emplace_back(pattern);
8659 }
8660 }
8661 for (auto &_v : _channels) {
8662 if (_v->IsHidden())
8663 continue;
8664 TString s(_v->GetName());
8665 pad->cd(++i);
8666 gPad->SetName(s);
8667 TString cName = s(s.Index('=') + 1, s.Length());
8668 chanVar.setLabel(cName);
8669 bool inRange = chanPatterns.empty();
8670 for (auto &p : chanPatterns)
8671 if (chanVar.inRange(p)) {
8672 inRange = true;
8673 break;
8674 }
8675 if (!inRange || !_v->get<RooAbsReal>()->isSelectedComp())
8676 gPad->SetFillColor(kGray);
8677 if (!hasSame && _size > 1 && (gStyle->GetTitleFont("Y") % 10) == 3)
8678 gPad->SetLeftMargin(std::min(gPad->GetLeftMargin() * (1. / gPad->GetWNDC()), 0.3));
8679 _v->Draw(opt);
8681 }
8682 pad->cd(0);
8683 gPad->Modified();
8684 // gPad->Update();
8685 return;
8686 }
8687
8688 if (!get() || get<RooArgList>()) {
8689 // is a group draw all the submembers
8690 browse();
8691 int _size = 0;
8692 // int _size = _channels.size(); // size(); if (find("!.vars")) _size--;
8693 for (auto &_v : *this) {
8694 if (_v->IsHidden())
8695 continue;
8696 if (strcmp(GetName(), ".vars") == 0) {
8697 // auto hide obs and "1" and const var
8698 if (_v->get<RooAbsArg>()->getAttribute("obs"))
8699 continue;
8700 if (strcmp(_v->get()->GetName(), "1") == 0 || strcmp(_v->get()->GetName(), "ONE") == 0 ||
8701 TString(_v->get()->GetName()).BeginsWith("binWidth_"))
8702 continue;
8703 if (_v->get()->InheritsFrom("RooConstVar"))
8704 continue;
8705 }
8706 TString s(_v->GetName());
8707 if (s.BeginsWith(".") || s.BeginsWith("!"))
8708 continue;
8709 _size++;
8710 }
8711 if (!hasSame) {
8712 clearPad();
8713 pad->SetBorderSize(0);
8714 dynamic_cast<TPad *>(pad)->DivideSquare(_size, 1e-9, 1e-9);
8715 }
8716 int i = 0;
8717 for (auto &_v : *this) {
8718 if (_v->IsHidden())
8719 continue;
8720 if (strcmp(GetName(), ".vars") == 0) {
8721 // auto hide obs and "1" and const var
8722 if (_v->get<RooAbsArg>()->getAttribute("obs"))
8723 continue;
8724 if (strcmp(_v->get()->GetName(), "1") == 0 || strcmp(_v->get()->GetName(), "ONE") == 0 ||
8725 TString(_v->get()->GetName()).BeginsWith("binWidth_"))
8726 continue;
8727 if (_v->get()->InheritsFrom("RooConstVar"))
8728 continue;
8729 }
8730 TString s(_v->GetName());
8731 if (s.BeginsWith(".") || s.BeginsWith("!"))
8732 continue;
8733 pad->cd(++i);
8734 gPad->SetName(s);
8735 if (!hasSame && _size > 1 && (gStyle->GetTitleFont("Y") % 10) == 3)
8736 gPad->SetLeftMargin(std::min(gPad->GetLeftMargin() * (1. / gPad->GetWNDC()), 0.3));
8737 _v->Draw(opt);
8738 // pad->Modified();//pad->Update();
8740 }
8741 pad->cd(0);
8742 gPad->Modified();
8743 // gPad->Update();
8744 return;
8745 }
8746
8747 if (get()->InheritsFrom("RooProdPdf")) {
8748 // draw the main pdf ...
8749 mainChild().Draw(opt);
8750 gPad->SetName(GetName());
8751 return;
8752 }
8753
8754 if (auto fr = get<RooFitResult>(); fr) {
8755 if (sOpt.Contains("corr")) {
8756 // do correlation matrix
8757
8758 auto hist = fr->correlationHist(fr->GetName());
8759 hist->SetTitle(fr->GetTitle());
8760 hist->SetBit(kCanDelete);
8761 hist->Scale(100);
8762 hist->SetStats(false);
8763 hist->SetDirectory(nullptr);
8765 gStyle->SetPaintTextFormat(".1f");
8766 hist->GetXaxis()->SetTickSize(0);
8767 hist->GetYaxis()->SetTickSize(0);
8768 hist->SetMinimum(-100);
8769 hist->Draw(sOpt);
8771 gPad->SetGrid(1, 1);
8772 gPad->SetLogy(0);
8773 gPad->SetLogx(0);
8774 return;
8775 }
8776
8777 if (sOpt.Contains("brakdown")) { // e will have been removed above
8778
8779 // breakdown is quadrature difference between total error and conditional error
8780 // group by 'group' attribute
8781
8782 std::string poiName;
8783 if (sOpt.Contains("brakdown:")) {
8784 TString sOpt3(opt);
8785 poiName = sOpt3(sOpt3.Index("breakdown:") + 10, sOpt3.Length());
8786 } else {
8787 std::unique_ptr<RooAbsCollection> _poi(fr->floatParsFinal().selectByAttrib("poi", true));
8788 if (_poi->empty()) {
8789 throw std::runtime_error("No floating poi in the fit");
8790 } else if (_poi->size() != 1) {
8791 throw std::runtime_error("Multiple poi in the fit");
8792 }
8793 poiName = _poi->first()->GetName();
8794 }
8795 RooRealVar *poi = dynamic_cast<RooRealVar *>(fr->floatParsFinal().find(poiName.c_str()));
8796 if (!poi) {
8797 throw std::runtime_error(TString::Format("Cannot find parameter %s", poiName.c_str()));
8798 }
8799 std::set<std::string> groups;
8800 for (auto p : fr->floatParsFinal()) {
8801 if (p == poi)
8802 continue;
8803 else if (p->getStringAttribute("group"))
8804 groups.insert(p->getStringAttribute("group"));
8805 else
8806 groups.insert(p->GetTitle());
8807 }
8808
8809 auto roundedVal = xRooFit::matchPrecision(std::pair(poi->getVal(), poi->getError()));
8810
8811 TPie *pie = new TPie(TString::Format("breakdown:%s", poi->GetName()),
8812 TString::Format("%s: %g #pm %g", poi->GetTitle(), roundedVal.first, roundedVal.second),
8813 groups.size() + 1);
8814
8815 // for display of errors will go to one extra dp ...
8816 roundedVal.second *= .1;
8817
8818 // do breakdown by removing parameters in blocks according to groups and seeing impact on variance
8819 // this will give the correct sum but will be order-dependent if there are correlations between
8820 // groups. therefore we will stick with group-by-group
8821 // RooArgList pars(fr->floatParsFinal()); // pars to not condition on
8822 // double variance = pow(dynamic_cast<RooRealVar*>(poi)->getError(),2);
8823 int i = 0;
8824 for (auto group : groups) {
8825 RooArgList pars(fr->floatParsFinal()); // pars to not condition on
8826 double variance = pow(dynamic_cast<RooRealVar *>(poi)->getError(), 2);
8827 for (auto p : fr->floatParsFinal()) {
8828 if (p == poi)
8829 continue;
8830 else if ((p->getStringAttribute("group") && group == p->getStringAttribute("group")) ||
8831 (!p->getStringAttribute("group") && group == p->GetTitle())) {
8832 // conditioning on this parameter ... remove from pars list
8833 pars.remove(*p);
8834 }
8835 }
8836 int idx = pars.index(poiName.c_str());
8837 double reducedVar = fr->conditionalCovarianceMatrix(pars)(idx, idx);
8838 if (reducedVar > variance) {
8839 Warning("Draw", "breakdown group %s variance bigger than preceding?", group.c_str());
8840 pie->SetEntryVal(i, 0);
8841 pie->SetEntryLabel(i, TString::Format("%s: NaN", group.c_str()));
8842 } else {
8843 pie->SetEntryVal(i, variance - reducedVar);
8845 std::pair(sqrt(variance - reducedVar), roundedVal.second)); // r.first will be the rounded error
8846 if (r.first > 0) {
8847 pie->SetEntryLabel(i, TString::Format("%s: %g", group.c_str(), r.first));
8848 } else {
8849 pie->SetEntryLabel(i, group.c_str()); // suppress labels for negligible errors.
8850 }
8851 }
8853 // variance = reducedVar;
8854 i++;
8855 }
8856 // remaining variance is statistical=
8857 double variance = fr->conditionalCovarianceMatrix(*poi)(0, 0);
8858 auto r =
8859 xRooFit::matchPrecision(std::pair(sqrt(variance), roundedVal.second)); // r.first will be the rounded error
8860 pie->SetEntryVal(i, variance);
8861 pie->SetEntryLabel(i, TString::Format("stat: %g", r.first));
8863 pie->SetBit(kCanDelete);
8864 pie->SetRadius(0.17);
8866 pie->Draw("NOL");
8867 return;
8868 }
8869
8870 // plot pull or impact
8872 out->SetName(TString::Format("%s_pull", fr->GetName()));
8873 out->SetTitle("Fit Result Pulls");
8874 std::vector<TString> graphLabels;
8876 ugraph->SetName(TString::Format("%s_pull_unconstrained", fr->GetName()));
8877 ugraph->SetTitle("Fit Result Pulls");
8878 std::vector<TString> ugraphLabels;
8879 std::map<std::string, double> scale;
8880 std::map<std::string, double> offset;
8881 for (auto &p : fr->floatParsFinal()) {
8882 auto _v = dynamic_cast<RooRealVar *>(p);
8883 if (!_v)
8884 continue;
8885
8886 if (std::isnan(_v->getErrorHi()) || std::isnan(_v->getErrorLo())) {
8887 Warning("Draw", "%s error is invalid", _v->GetName());
8888 }
8889
8890 // need to get constraint mean and error parameters ....
8891 // look for normal gaussian and poisson cases
8892 double prefitError = 0;
8893 double prefitVal = 0;
8894 double customScale = 0;
8895 if (auto ip =
8896 dynamic_cast<RooRealVar *>(fr->floatParsInit().find(p->GetName()))) { // handles if no prefit available
8897 prefitError = ip->getError();
8898 prefitVal = ip->getVal();
8899 };
8900
8901 std::shared_ptr<xRooNode> pConstr;
8902 if (fParent && fParent->getObject<RooRealVar>(p->GetName())) {
8903 auto _vv = fParent->getObject<RooRealVar>(p->GetName());
8904 if (_vv->hasRange("pullScale")) {
8905 customScale = (_vv->getMax("pullScale") - _vv->getMin("pullScale")) / 2.;
8906 }
8907 auto _constr = xRooNode(_vv, *this).constraints();
8908 for (auto &c : _constr) {
8909 if (c->get<RooPoisson>() || c->get<RooGaussian>()) {
8910 // require parameter to be a direct server of the constraint pdf to count if its a gaussian
8911 bool isServer = true;
8912 if (c->get<RooGaussian>()) {
8913 isServer = false;
8914 for (auto s : c->get<RooAbsArg>()->servers()) {
8915 if (strcmp(s->GetName(), p->GetName()) == 0) {
8916 isServer = true;
8917 break;
8918 }
8919 }
8920 }
8921 if (isServer) {
8922 pConstr = c;
8923 break;
8924 }
8925 }
8926 }
8927 }
8928 if (pConstr) {
8929
8930 // there will be 3 deps, one will be this par, the other two are the mean and error (or error^2 in case of
8931 // poisson
8932
8933 // std::cout << p->GetName() << " extracted " << prefitVal << " " << prefitError << " from ";
8934 // pConstr->deps().Print();
8935 pConstr->browse();
8936 if (pConstr->get<RooPoisson>() && pConstr->find(".x")) {
8937 std::string xName = pConstr->find(".x")->get()->GetName();
8938 prefitVal = pConstr->find(".x")->get<RooAbsReal>()->getVal();
8939 for (auto &_d : pConstr->vars()) {
8940 if (strcmp(p->GetName(), _d->get()->GetName()) == 0)
8941 continue;
8942 if (xName == _d->get()->GetName())
8943 continue;
8944 if (_d->get<RooAbsReal>()->getVal())
8945 prefitError = _d->get<RooAbsReal>()->getVal();
8946 }
8947 // prefitVal will be the global observable value, need to divide that by tau
8948 prefitVal /= prefitError;
8949 // prefiterror will be tau ... need 1/sqrt(tau) for error
8950 prefitError = 1. / sqrt(prefitError);
8951 } else if (auto _g = pConstr->get<RooGaussian>(); _g) {
8952 prefitError =
8953 (pConstr->find(".sigma")) ? pConstr->find(".sigma")->get<RooAbsReal>()->getVal() : prefitError;
8954 prefitVal =
8955 (pConstr->find(".x")) ? pConstr->find(".x")->get<RooAbsReal>()->getVal() : 0; // usually the globs
8956 if (pConstr->find(".x") &&
8957 strcmp(p->GetName(), pConstr->find(".x")->get<RooAbsReal>()->GetName()) == 0) {
8958 // hybrid construction case,
8959 prefitVal = pConstr->find(".mean")->get<RooAbsReal>()->getVal();
8960 }
8961 }
8962
8963 if (customScale)
8964 prefitError = customScale;
8965 if (prefitError == 0) {
8966 Warning("Draw", "failed to determine prefit error of %s, using post-fit error", p->GetName());
8967 prefitError = _v->getError();
8968 }
8969 out->SetPoint(out->GetN(), out->GetN(), (_v->getVal() - prefitVal) / prefitError);
8970 out->SetPointError(out->GetN() - 1, 0, 0, (-_v->getErrorLo()) / prefitError,
8971 (_v->getErrorHi()) / prefitError);
8972 graphLabels.push_back(p->GetName());
8973 scale[p->GetName()] = prefitError;
8974 offset[p->GetName()] = prefitVal;
8975 } else if (!fParent) {
8976 // no parent to determine constraints from ... prefitError=0 will be the unconstrained ones
8977 if (customScale)
8978 prefitError = customScale;
8979 if (prefitError == 0) {
8980 // uses range of var
8981 prefitError = (std::max({_v->getMax() - _v->getVal(), _v->getVal() - _v->getMin(), 4.}) / 4);
8982 ugraph->SetPoint(ugraph->GetN(), ugraph->GetN(), (_v->getVal() - prefitVal) / prefitError);
8983 ugraph->SetPointError(ugraph->GetN() - 1, 0, 0, (-_v->getErrorLo()) / prefitError,
8984 (_v->getErrorHi()) / prefitError);
8985 ugraphLabels.push_back(p->GetName());
8986 } else {
8987 out->SetPoint(out->GetN(), out->GetN(), (_v->getVal() - prefitVal) / prefitError);
8988 out->SetPointError(out->GetN() - 1, 0, 0, (-_v->getErrorLo()) / prefitError,
8989 (_v->getErrorHi()) / prefitError);
8990 graphLabels.push_back(p->GetName());
8991 }
8992 scale[p->GetName()] = prefitError;
8993 offset[p->GetName()] = prefitVal;
8994
8995 } else {
8996 // unconstrained (or at least couldn't determine constraint) ... use par range if no prefit error
8997 if (customScale)
8998 prefitError = customScale;
8999 if (prefitError == 0) {
9000 prefitError = (std::max({_v->getMax() - _v->getVal(), _v->getVal() - _v->getMin(), 4.}) / 4);
9001 }
9002 ugraph->SetPoint(ugraph->GetN(), ugraph->GetN(), (_v->getVal() - prefitVal) / prefitError);
9003 ugraph->SetPointError(ugraph->GetN() - 1, 0, 0, (-_v->getErrorLo()) / prefitError,
9004 (_v->getErrorHi()) / prefitError);
9005 ugraphLabels.push_back(p->GetName());
9006 scale[p->GetName()] = prefitError;
9007 offset[p->GetName()] = prefitVal;
9008 }
9009 }
9010 auto graph = out;
9011
9012 // append ugraph points to end of graph
9013 for (int i = 0; i < ugraph->GetN(); i++)
9014 ugraph->SetPointX(i, i + graph->GetN());
9015 int nUnconstrained = ugraph->GetN();
9016 TList tmpList;
9017 tmpList.SetName("tmpList");
9018 tmpList.Add(ugraph);
9019 graph->Merge(&tmpList);
9020 tmpList.RemoveAll();
9021 delete ugraph;
9022 for (auto &l : ugraphLabels) {
9023 graphLabels.push_back(l);
9024 }
9025
9026 graph->SetBit(kCanDelete);
9027 graph->SetMarkerStyle(20);
9028 graph->SetMarkerSize(0.5);
9029
9030 graph->SetMaximum(4);
9031 graph->SetMinimum(-4);
9032
9033 bool doHorizontal =
9034 (!sOpt.Contains("impact") && sOpt.Contains("v")) || (sOpt.Contains("impact") && !sOpt.Contains("himpact"));
9035
9036 std::vector<std::pair<double, std::string>> covariances;
9037 /*double poiError = 0;*/ std::string poiName;
9038 double maxImpact = 0;
9039 if (sOpt.Contains("impact")) {
9040 if (sOpt.Contains("impact:")) {
9041 TString sOpt3(opt);
9042 poiName = sOpt3(sOpt3.Index("impact:") + 7, sOpt3.Length());
9043 } else {
9044 std::unique_ptr<RooAbsCollection> _poi(fr->floatParsFinal().selectByAttrib("poi", true));
9045 if (_poi->empty()) {
9046 throw std::runtime_error("No floating poi in the fit");
9047 } else if (_poi->size() != 1) {
9048 throw std::runtime_error("Multiple poi in the fit");
9049 }
9050 poiName = _poi->first()->GetName();
9051 }
9052 RooAbsArg *poi = fr->floatParsFinal().find(poiName.c_str());
9053 if (!poi) {
9054 throw std::runtime_error(TString::Format("Cannot find parameter %s", poiName.c_str()));
9055 }
9056 size_t poiIdx = fr->floatParsFinal().index(*poi);
9057 // put parameters in order of impact on the poi
9058
9059 // impact is regression coefficient * npError
9060 // relevant regression coefficient is cov / (npVariance)
9061 // i.e. DeltaX/sigmaX = [cov(X,Y)/(sigmaXsigmaY)]DeltaY/sigmaY
9062 // ... DeltaX = [cov(X,Y)/(sigmaY^2)]DeltaY
9063 // if DeltaY is just sigmaY then DeltaX = cov(X,Y)/sigmaY
9064
9065 for (auto &label : graphLabels) {
9066 covariances.emplace_back(fr->covarianceMatrix()(poiIdx, fr->floatParsFinal().index(label)) /
9067 dynamic_cast<RooRealVar *>(fr->floatParsFinal().find(label))->getError(),
9068 label);
9069 }
9070 std::sort(covariances.begin(), covariances.end(),
9071 [&](std::pair<double, std::string> i, std::pair<double, std::string> j) {
9072 return doHorizontal ? (std::abs(i.first) < std::abs(j.first))
9073 : (std::abs(i.first) > std::abs(j.first));
9074 });
9075
9076 TGraphAsymmErrors sortedGraph;
9077 std::vector<TString> sortedLabels;
9078 maxImpact = (doHorizontal) ? covariances.back().first
9079 : covariances.front().first; // note: max impact is likely to be self variance
9080 for (auto &c : covariances) {
9081 if (c.second == poi->GetName()) {
9082 // poiError = sqrt(c.first);
9083 continue; // skip self
9084 }
9085 c.first *= 4. / (maxImpact * 1.2);
9086 sortedLabels.push_back(c.second);
9087 size_t i = 0;
9088 for (; i < graphLabels.size(); i++) {
9089 if (graphLabels[i] == c.second) {
9090 break;
9091 }
9092 }
9093 sortedGraph.AddPoint(sortedGraph.GetN(), graph->GetPointY(i));
9094 sortedGraph.SetPointError(sortedGraph.GetN() - 1, 0, 0, graph->GetErrorYlow(i), graph->GetErrorYhigh(i));
9095 }
9096 graph->Set(0);
9097 TList tmpList2;
9098 tmpList2.SetName("tmpList");
9099 tmpList2.Add(&sortedGraph);
9100 graph->Merge(&tmpList2);
9101 tmpList2.RemoveAll();
9102 graphLabels = sortedLabels;
9103 graph->SetTitle("Fit Result Impact");
9104 }
9105
9106 // create a framing histogram
9107 TH2D *hist;
9108 if (doHorizontal) {
9109 hist = new TH2D(GetName(), fr->GetTitle(), 100, -4, 4, std::max(graph->GetN(), 1), -0.5,
9110 std::max(graph->GetN(), 1) - 0.5);
9111 int i = 1;
9112 for (auto &l : graphLabels) {
9113 hist->GetYaxis()->SetBinLabel(i++, l);
9114 }
9115 if (!graphLabels.empty())
9116 hist->GetYaxis()->LabelsOption("v");
9117 hist->GetXaxis()->SetTitle("(#hat{#theta}-#theta_{i})/#sigma_{i}");
9118 } else {
9119 hist = new TH2D(GetName(), fr->GetTitle(), std::max(graph->GetN(), 1), -0.5, std::max(graph->GetN(), 1) - 0.5,
9120 100, -4, 4);
9121 int i = 1;
9122 for (auto &l : graphLabels) {
9123 hist->GetXaxis()->SetBinLabel(i++, l);
9124 }
9125 if (!graphLabels.empty())
9126 hist->GetXaxis()->LabelsOption("v");
9127 hist->GetYaxis()->SetNdivisions(8, 0, 0);
9128 hist->GetYaxis()->SetTitle("(#hat{#theta}-#theta_{i})/#sigma_{i}");
9129 }
9130 hist->SetStats(false);
9131 hist->SetDirectory(nullptr);
9132 hist->SetBit(kCanDelete);
9133 auto histCopy = dynamic_cast<TH1 *>(hist->Clone(".axis"));
9134 histCopy->SetDirectory(0);
9135 histCopy->SetBit(kCanDelete);
9136 auto _axis = (doHorizontal ? histCopy->GetYaxis() : histCopy->GetXaxis());
9137
9138 /*
9139 auto t = TH1::AddDirectoryStatus();
9140 TH1::AddDirectory(false);
9141 auto hist = new TH1F(TString::Format(".%s_pullFrame", GetName()), fr->GetTitle(), std::max(graph->GetN(),
9142 1), -0.5, std::max(graph->GetN(), 1) - 0.5); hist->SetStats(false); TH1::AddDirectory(t);
9143 hist->SetBit(kCanDelete);
9144 */
9145 // auto hist = graph->GetHistogram();
9146 graph->GetHistogram()->GetXaxis()->Set(std::max(graph->GetN(), 1), -0.5, std::max(graph->GetN(), 1) - 0.5);
9147 for (int ii = 1; ii <= _axis->GetNbins(); ii++) {
9148 graph->GetHistogram()->GetXaxis()->SetBinLabel(ii, _axis->GetBinLabel(ii));
9149 }
9150 // int i = 1;
9151 // for (auto &l : graphLabels) {
9152 // hist->GetXaxis()->SetBinLabel(i++, l);
9153 // }
9154 // hist->SetMaximum(4);
9155 // hist->SetMinimum(-4);
9156 // if (graph->GetN())
9157 // hist->GetXaxis()->LabelsOption("v");
9158 // hist->GetYaxis()->SetNdivisions(8, 0, 0);
9159 // hist->GetYaxis()->SetTitle("(#hat{#theta}-#theta_{i})/#sigma_{i}");
9160 clearPad();
9161 // create a new pad because adjust the margins ...
9162 auto oldPad = gPad;
9163 gPad->Divide(1, 1, 1e-9, 1e-9);
9164 gPad->cd(1);
9165
9166 if (doHorizontal) {
9167 gPad->SetLeftMargin(0.4);
9168 } else {
9169 gPad->SetBottomMargin(0.4);
9170 }
9171
9172 auto pNamesHist = dynamic_cast<TH1F *>(graph->GetHistogram()->Clone("scales")); // used by interactive "pull" plot
9173 pNamesHist->Sumw2();
9174 pNamesHist->SetDirectory(0);
9175
9176 for (int ii = 1; ii <= graph->GetN(); ii++) { // use graph->GetN() to protect against the 0 pars case
9177 auto _p = fr->floatParsFinal().find(_axis->GetBinLabel(ii));
9178 pNamesHist->SetBinContent(ii, offset[_p->GetName()]);
9179 pNamesHist->SetBinError(ii, scale[_p->GetName()]);
9180 _axis->SetBinLabel(ii, strlen(_p->GetTitle()) ? _p->GetTitle() : _p->GetName());
9181 }
9182
9183 // hist->Draw(); -- now just draw the graph
9184
9185 if (!sOpt.Contains("impact")) {
9186 for (int ii = 2; ii >= 1; ii--) {
9187 auto pullBox = new TGraphErrors;
9188 pullBox->SetName(TString::Format("%dsigmaBand", ii));
9189 pullBox->SetBit(kCanDelete);
9190 pullBox->SetPoint(0, (doHorizontal) ? -ii : -0.5, (doHorizontal) ? -0.5 : 0);
9191 pullBox->SetPoint(1, (doHorizontal) ? ii : (_axis->GetNbins() - 0.5 - nUnconstrained),
9192 (doHorizontal) ? -0.5 : 0);
9193 pullBox->SetPointError(0, 0, (doHorizontal) ? (_axis->GetNbins() - nUnconstrained) : ii);
9194 pullBox->SetPointError(1, 0, (doHorizontal) ? (_axis->GetNbins() - nUnconstrained) : ii);
9195 pullBox->SetFillColor((ii == 2) ? kYellow : kGreen);
9196 hist->GetListOfFunctions()->Add(pullBox, "3"); // pullBox->Draw("3");
9197 }
9198 auto pullLine = new TGraph;
9199 pullLine->SetName("0sigmaLine");
9200 pullLine->SetBit(kCanDelete);
9201 pullLine->SetPoint(0, -0.5, 0);
9202 pullLine->SetPoint(1, _axis->GetNbins() - 0.5, 0);
9203 pullLine->SetLineStyle(2);
9204 pullLine->SetEditable(false);
9205 hist->GetListOfFunctions()->Add(pullLine, "l"); // pullLine->Draw("l");
9206
9207 // also draw vertical line separating constrained from unconstrained, if necessary
9208 if (nUnconstrained > 0) {
9209 pullLine = new TGraph;
9210 pullLine->SetName("dividerLine");
9211 pullLine->SetBit(kCanDelete);
9212 pullLine->SetPoint(0, graph->GetN() - 0.5 - nUnconstrained, -100);
9213 pullLine->SetPoint(1, graph->GetN() - 0.5 - nUnconstrained, 100);
9214 pullLine->SetLineStyle(2);
9215 pullLine->SetEditable(false);
9216 hist->GetListOfFunctions()->Add(pullLine, "l"); // pullLine->Draw("l");
9217 }
9218
9219 // and draw a pave with fr status info
9220 TPaveText *pave =
9221 new TPaveText(gPad->GetLeftMargin(), 1. - gPad->GetTopMargin(), 1. - gPad->GetRightMargin(), 0.98, "NDCNB");
9222 pave->SetFillStyle(0);
9223 pave->SetBorderSize(0);
9224 pave->SetMargin(0.);
9225 pave->SetName("status");
9226 pave->SetTextAlign(31);
9227 pave->AddText(TString::Format("minNLL: %g edm: %g", fr->minNll(), fr->edm()));
9228 std::string covQualTxt;
9229 switch (fr->covQual()) {
9230 case -1: covQualTxt = "Unknown"; break;
9231 case 0: covQualTxt = "Not calculated"; break;
9232 case 1: covQualTxt = "Approximate"; break;
9233 case 2: covQualTxt = "Forced Positive-Definite"; break;
9234 case 3: covQualTxt = "Accurate"; break;
9235 }
9236 pave->AddText(TString::Format("Cov. Quality: %d (%s)", fr->covQual(), covQualTxt.c_str()))
9237 ->SetTextColor((fr->covQual() == 3) ? kBlack : kRed);
9238
9239 std::string statusCodes;
9240 for (unsigned int i = 0; i < fr->numStatusHistory(); i++) {
9241 statusCodes += TString::Format(" %s = %d", fr->statusLabelHistory(i), fr->statusCodeHistory(i));
9242 }
9243 pave->AddText(statusCodes.c_str())->SetTextColor(fr->status() == 0 ? kBlack : kRed);
9244
9245 hist->GetListOfFunctions()->Add(pave);
9246
9247 } else {
9248 gPad->SetTicks(0, 0); // ensure mirrored ticks aren't drawn in this pad
9249
9250 if (doHorizontal) {
9251 // ensure canvas height big enough
9252 if (int(gPad->GetCanvas()->GetWh()) < pNamesHist->GetNbinsX() * 15) {
9253 gPad->GetCanvas()->SetCanvasSize(gPad->GetCanvas()->GetWw(), pNamesHist->GetNbinsX() * 15);
9254 }
9255 }
9256
9257 double factor = 475. / gPad->GetCanvas()->GetWh(); // Wh is the full canvas height, not window height
9258 gPad->SetTopMargin(gStyle->GetPadTopMargin() * factor); // fixed margin height
9259 gPad->SetBottomMargin(gStyle->GetPadBottomMargin() * factor); // fixed margin height
9260
9261 TGaxis *axis =
9262 new TGaxis(_axis->GetXmin(), -4, _axis->GetXmin(), 4, -1.2 * maxImpact, 1.2 * maxImpact, 510, "-S");
9263 axis->SetTextFont(_axis->GetTitleFont());
9264 axis->SetLabelFont(_axis->GetLabelFont());
9265 axis->SetTextSize((axis->GetTextFont() % 10 > 2) ? (10 / factor)
9266 : ((gPad->AbsPixeltoY(0) - gPad->AbsPixeltoY(10 / factor)) /
9267 (gPad->GetY2() - gPad->GetY1())));
9268 axis->SetTitle(TString::Format("#Delta %s", fr->floatParsFinal().find(poiName.c_str())->GetTitle()));
9269 axis->SetTickSize(axis->GetTickSize() * factor);
9270
9271 if (doHorizontal) {
9272 axis->SetLabelSize(
9273 (axis->GetLabelFont() % 10 > 2)
9274 ? (10 / factor)
9275 : ((gPad->AbsPixeltoY(0) - gPad->AbsPixeltoY(10 / factor)) / (gPad->GetY2() - gPad->GetY1())));
9276 // axis->SetTextSize(axis->GetTextSize()*factor);
9277 axis->SetTitleSize(
9278 (axis->GetTextFont() % 10 > 2)
9279 ? (10 / factor)
9280 : ((gPad->AbsPixeltoY(0) - gPad->AbsPixeltoY(10 / factor)) / (gPad->GetY2() - gPad->GetY1())));
9281 axis->SetTitleOffset(axis->GetTitleOffset() * factor);
9282 // axis->SetLabelOffset(axis->GetLabelOffset()*factor);
9283 _axis->SetLabelSize(
9284 (_axis->GetLabelFont() % 10 > 2)
9285 ? (10 / factor)
9286 : ((gPad->AbsPixeltoY(0) - gPad->AbsPixeltoY(10 / factor)) / (gPad->GetY2() - gPad->GetY1())));
9287 histCopy->GetXaxis()->SetTickLength(histCopy->GetXaxis()->GetTickLength() * factor);
9288 hist->GetXaxis()->SetTickLength(hist->GetXaxis()->GetTickLength() * factor);
9289 histCopy->GetYaxis()->SetTickLength(histCopy->GetYaxis()->GetTickLength() * factor);
9290 hist->GetYaxis()->SetTickLength(hist->GetYaxis()->GetTickLength() * factor);
9291 histCopy->GetXaxis()->SetTitleOffset(histCopy->GetXaxis()->GetTitleOffset() * factor);
9292 histCopy->GetXaxis()->SetLabelOffset(histCopy->GetXaxis()->GetLabelOffset() * factor);
9293 hist->GetXaxis()->SetTitleOffset(hist->GetXaxis()->GetTitleOffset() * factor);
9294 hist->GetXaxis()->SetLabelOffset(hist->GetXaxis()->GetLabelOffset() * factor);
9295 histCopy->GetXaxis()->SetTitleOffset(histCopy->GetXaxis()->GetTitleOffset() * factor);
9296 histCopy->GetXaxis()->SetLabelOffset(histCopy->GetXaxis()->GetLabelOffset() * factor);
9297 }
9298
9299 // create impact bar charts
9300 for (int tt = 0; tt < 2; tt++) {
9301 auto impact = static_cast<TH1 *>(
9302 graph->GetHistogram()->Clone(TString::Format("%s_impact+", tt == 0 ? "prefit" : "postfit")));
9303 impact->SetDirectory(0);
9304 impact->GetYaxis()->SetTitle(TString::Format("#Delta%s/#sigma", poiName.c_str()));
9305 impact->SetBarWidth(0.9);
9306 impact->SetBarOffset(0.05);
9307 impact->SetLineColor(kBlack);
9308 impact->SetFillColor(kAzure - 4);
9309 impact->SetFillStyle(tt == 0 ? 3013 : 1001);
9310 auto impact2 =
9311 static_cast<TH1 *>(impact->Clone(TString::Format("%s_impact-", tt == 0 ? "prefit" : "postfit")));
9312 impact2->SetDirectory(0);
9313 impact2->SetFillColor(kCyan);
9314 for (int ii = 1; ii <= pNamesHist->GetNbinsX(); ii++) {
9315 for (auto &c : covariances) {
9316 if (c.second != pNamesHist->GetXaxis()->GetBinLabel(ii))
9317 continue;
9318 auto vv = dynamic_cast<RooRealVar *>(fr->floatParsFinal().find(c.second.c_str()));
9319 auto vv_init = dynamic_cast<RooRealVar *>(fr->floatParsInit().find(c.second.c_str()));
9320 impact->SetBinContent(ii, ((tt == 0 && !vv_init->hasError()) || !vv->hasError())
9321 ? 0.
9322 : c.first * vv->getError() / vv->getErrorHi() *
9323 (tt == 0 ? (vv_init->getErrorHi() / vv->getErrorHi()) : 1.));
9324 impact2->SetBinContent(ii, ((tt == 0 && !vv_init->hasError()) || !vv->hasError())
9325 ? 0.
9326 : c.first * vv->getError() / vv->getErrorLo() *
9327 (tt == 0 ? (vv_init->getErrorLo() / vv->getErrorLo()) : 1.));
9328 }
9329 }
9330 hist->GetListOfFunctions()->Add(impact, (doHorizontal) ? "hbarsamemin0" : "bsamey+");
9331 hist->GetListOfFunctions()->Add(impact2, (doHorizontal) ? "hbarsamemin0" : "bsamey+");
9332 }
9333 // add three lines
9334 for (int ii = -1; ii <= 1; ii++) {
9335 auto pullLine = new TGraph;
9336 pullLine->SetName(TString::Format("%dsigmaLine", ii));
9337 pullLine->SetBit(kCanDelete);
9338 pullLine->SetPoint(0, -0.5, ii);
9339 pullLine->SetPoint(1, hist->GetNbinsY() - 0.5, ii);
9340 pullLine->SetLineStyle(2);
9341 pullLine->SetEditable(false);
9342 hist->GetListOfFunctions()->Add(pullLine, "l");
9343 }
9344 hist->GetListOfFunctions()->Add(axis); // draw axis last
9345 TLegend *leg1 =
9346 new TLegend(0.02, doHorizontal ? (1. - 0.22 * factor) : 0.02, 0.27, (doHorizontal ? 1. : 0.24));
9347 leg1->SetFillStyle(0);
9348 leg1->SetBorderSize(0);
9349 leg1->SetMargin(0.25);
9350 leg1->SetNColumns(2);
9351
9352 leg1->SetTextSize((leg1->GetTextFont() % 10 > 2) ? (10 / factor)
9353 : ((gPad->AbsPixeltoY(0) - gPad->AbsPixeltoY(10 / factor)) /
9354 (gPad->GetY2() - gPad->GetY1())));
9355 // leg1.SetTextFont(gStyle->GetTextFont());
9356 // leg1.SetTextSize(gStyle->GetTextSize());
9357 leg1->AddEntry((TObject *)nullptr, "Hessian Pre-fit", "");
9358 leg1->AddEntry((TObject *)nullptr, "Impact:", "");
9359 leg1->AddEntry(hist->FindObject("prefit_impact+"), "#theta = #hat{#theta}+#Delta#theta", "f");
9360 leg1->AddEntry(hist->FindObject("prefit_impact-"), "#theta = #hat{#theta}-#Delta#theta", "f");
9361 leg1->AddEntry((TObject *)nullptr, "Hessian Post-fit", "");
9362 leg1->AddEntry((TObject *)nullptr, "Impact:", "");
9363 leg1->AddEntry(hist->FindObject("postfit_impact+"), "#theta = #hat{#theta}+#Delta#theta", "f");
9364 leg1->AddEntry(hist->FindObject("postfit_impact-"), "#theta = #hat{#theta}-#Delta#theta", "f");
9365
9366 hist->GetListOfFunctions()->Add(leg1);
9367 if (gStyle->GetOptTitle()) {
9368 histCopy->SetBit(TH1::kNoTitle);
9369 TPaveText *title =
9370 new TPaveText(gPad->GetLeftMargin(), 1. - gPad->AbsPixeltoY(14), 1. - gPad->GetRightMargin(), 1., "NDC");
9371 title->ConvertNDCtoPad();
9372 title->SetY1NDC(1. - gPad->GetTopMargin() * 0.6);
9373 title->SetY2NDC(1);
9374 title->SetTextSize(
9375 (title->GetTextFont() % 10 > 2)
9376 ? (14 / factor)
9377 : ((gPad->AbsPixeltoY(0) - gPad->AbsPixeltoY(10 / factor)) / (gPad->GetY2() - gPad->GetY1())));
9378 title->SetFillStyle(0);
9379 title->SetBorderSize(0);
9380 title->AddText(histCopy->GetTitle());
9381 hist->GetListOfFunctions()->Add(title);
9382 }
9383 }
9384
9385 graph->SetEditable(false);
9386 pNamesHist->SetLineWidth(0);
9387 pNamesHist->SetMarkerSize(0);
9388 graph->GetListOfFunctions()->Add(pNamesHist, "same"); // graph->SetHistogram(pNamesHist);
9389 if (doHorizontal) {
9390
9391 // flip the graph and contained graphs
9392 for (int p = 0; p < graph->GetN(); p++) {
9393 graph->SetPoint(p, graph->GetPointY(p), graph->GetPointX(p));
9394 graph->SetPointError(p, graph->GetErrorYlow(p), graph->GetErrorYhigh(p), graph->GetErrorXlow(p),
9395 graph->GetErrorXhigh(p));
9396 }
9397 for (auto f : *hist->GetListOfFunctions()) {
9398 if (f->InheritsFrom("TH1")) {
9399 // f->Draw("hbarsamemin0");
9400 } /*else if (auto g2 = dynamic_cast<TGraphErrors *>(f)) {
9401 for (int p = 0; p < g2->GetN(); p++) {
9402 g2->SetPoint(p, g2->GetPointY(p), g2->GetPointX(p));
9403 g2->SetPointError(p, g2->GetErrorY(p), _axis->GetNbins());
9404 }
9405 //g2->Draw("3");
9406 } */
9407 else if (auto g = dynamic_cast<TGraph *>(f)) {
9408 for (int p = 0; p < g->GetN(); p++) {
9409 g->SetPoint(p, g->GetPointY(p), g->GetPointX(p));
9410 }
9411 // g->Draw("l");
9412 } else if (auto l = dynamic_cast<TLine *>(f)) {
9413 l->SetX1(l->GetY1());
9414 l->SetX2(l->GetY2());
9415 l->SetY1(_axis->GetXmax());
9416 l->SetY2(_axis->GetXmax());
9417 // l->Draw();
9418 }
9419 }
9420 }
9421
9422 graph->SetName("pulls");
9423 hist->GetListOfFunctions()->Add(graph, "z0p");
9424 // hist->GetListOfFunctions()->Add(histCopy->Clone(".axis"),(sOpt.Contains("impact") &&
9425 // !doHorizontal)?"axissamey+":"axissame"); // doesn't display right when zoom the axis
9426 if (!hasSame)
9427 histCopy->Draw((sOpt.Contains("impact") && !doHorizontal)
9428 ? "axisy+"
9429 : "axis"); // draws the axis, called ".axis" for easy access
9430 hist->Draw("same");
9431 //
9432 // if(sOpt.Contains("impact")) {
9433 // // make main object the histogram
9434 // auto h = (TH1*)graph->GetHistogram()->Clone("impact");
9435 // graph->GetListOfFunctions()->RemoveAll();
9436 // for(int ii=1;ii<=h->GetNbinsX();ii++) h->SetBinContent(ii,-4);
9437 // h->GetListOfFunctions()->Add(graph,"z0p");
9438 // h->Draw("hbar");
9439 // } else {
9440 // graph->Draw(sOpt.Contains("impact") ? "az0py+" : "az0p");
9441 // }
9442 auto hh = dynamic_cast<TH1 *>(histCopy->Clone(".axiscopy"));
9443 hh->SetDirectory(0);
9444 hh->SetBit(kCanDelete);
9445 hh->Draw(
9446 (sOpt.Contains("impact") && !doHorizontal)
9447 ? "axissamey+"
9448 : "axissame"); // overlay axis again -- important is last so can remove if don't pad->Update before reclear
9449 gPad->Modified();
9450 oldPad->cd();
9451 // gPad->Update();
9452 return;
9453 }
9454
9455 if (get()->InheritsFrom("RooAbsData")) {
9456 auto s = parentPdf();
9457 if (s && s->get<RooSimultaneous>()) {
9458 // drawing dataset associated to a simultaneous means must find subpads with variation names
9459 for (auto c : s->bins()) {
9460 auto _pad = dynamic_cast<TPad *>(gPad->GetPrimitive(c->GetName()));
9461 if (!_pad)
9462 continue; // channel was hidden?
9463 auto ds = c->datasets().find(GetName());
9464 if (!ds) {
9465 std::cout << " no ds " << GetName() << std::endl;
9466 continue;
9467 }
9468 auto tmp = gPad;
9469 _pad->cd();
9470 ds->Draw(opt);
9471 tmp->cd();
9472 }
9473 gPad->Modified();
9474 return;
9475 }
9476
9477 if (!s && hasSame) {
9478 // draw onto all subpads with = in the name
9479 // if has no such subpads, draw onto this pad
9480 bool doneDraw = false;
9481 for (auto o : *gPad->GetListOfPrimitives()) {
9482 if (auto p = dynamic_cast<TPad *>(o); p && TString(p->GetName()).Contains('=')) {
9483 auto _tmp = gPad;
9484 p->cd();
9485 Draw(opt);
9486 _tmp->cd();
9487 doneDraw = true;
9488 }
9489 }
9490 if (doneDraw) {
9491 gPad->Modified();
9492 return;
9493 }
9494 }
9495
9496 auto dataGraph = BuildGraph(v, false, (!s && hasSame) ? gPad : nullptr);
9497 if (!dataGraph)
9498 return;
9499
9500 dataGraph->SetBit(kCanDelete); // will be be deleted when pad is cleared
9501 dataGraph->SetMarkerSize(dataGraph->GetMarkerSize() * gPad->GetWNDC()); // scale marker sizes to pad size
9502
9503 if (s && !s->get<RooAbsPdf>()->canBeExtended()) {
9504 // normalize dataGraph to 1
9505 double tot = 0;
9506 for (int i = 0; i < dataGraph->GetN(); i++)
9507 tot += dataGraph->GetPointY(i);
9508 dataGraph->Scale(1. / tot);
9509 }
9510
9511 if (!hasSame) {
9512 clearPad();
9513 dataGraph->Draw("Az0p");
9514 addLegendEntry(dataGraph, strlen(dataGraph->GetTitle()) ? dataGraph->GetTitle() : GetName(), "pEX0");
9515 gPad->Modified();
9516 // gPad->Update();
9517 return;
9518 }
9519
9520 bool noPoint = false;
9521 if (v && dynamic_cast<RooAbsArg *>(v)->getAttribute("global") && dataGraph->GetN() == 1) {
9522 // global observable ... if graph has only 1 data point line it up on the histogram value
9523 for (auto o : *gPad->GetListOfPrimitives()) {
9524 if (auto h = dynamic_cast<TH1 *>(o);
9525 h && strcmp(h->GetXaxis()->GetName(), dynamic_cast<TObject *>(v)->GetName()) == 0) {
9526 dataGraph->SetPointY(0, h->Interpolate(dataGraph->GetPointX(0)));
9527 noPoint = true;
9528 break;
9529 }
9530 }
9531 }
9532
9533 if (auto _pad = dynamic_cast<TPad *>(gPad->FindObject("auxPad")); _pad) {
9534 if (auto h = dynamic_cast<TH1 *>(_pad->GetPrimitive("auxHist")); h) {
9535 TString histName = h->GetTitle(); // split it by | char
9536 TString histType = histName(histName.Index('|') + 1, histName.Length());
9537 histName = histName(0, histName.Index('|'));
9538 if (auto mainHist = dynamic_cast<TH1 *>(gPad->GetPrimitive(histName));
9539 mainHist && auxFunctions.find(h->GetYaxis()->GetTitle()) != auxFunctions.end()) {
9540 // decide what to do based on title of auxHist (previously used name of y-axis but that changed axis
9541 // behaviour) use title instead
9542 auto ratioGraph = dynamic_cast<TGraphAsymmErrors *>(dataGraph->Clone(dataGraph->GetName()));
9543 ratioGraph->SetBit(kCanDelete);
9544 for (int i = 0; i < ratioGraph->GetN(); i++) {
9545 double val = ratioGraph->GetPointY(i);
9546 int binNum = mainHist->FindFixBin(ratioGraph->GetPointX(i));
9547 double nom = mainHist->GetBinContent(binNum);
9548 double nomerr = mainHist->GetBinError(binNum);
9549 double yval =
9550 std::get<0>(auxFunctions[h->GetYaxis()->GetTitle()])(ratioGraph->GetPointY(i), nom, nomerr);
9551 double yup = std::get<0>(auxFunctions[h->GetYaxis()->GetTitle()])(val + ratioGraph->GetErrorYhigh(i),
9552 nom, nomerr) -
9553 yval;
9554 double ydown = yval - std::get<0>(auxFunctions[h->GetYaxis()->GetTitle()])(
9555 val - ratioGraph->GetErrorYlow(i), nom, nomerr);
9556 if (!std::isnan(yval)) {
9557 ratioGraph->SetPointY(i, yval);
9558 if (!std::isnan(yup))
9559 ratioGraph->SetPointEYhigh(i, yup);
9560 if (!std::isnan(ydown))
9561 ratioGraph->SetPointEYlow(i, ydown);
9562 }
9563 }
9564 // remove the zero points
9565 int i = 0;
9566 while (i < ratioGraph->GetN()) {
9567 if (ratioGraph->GetPointY(i) == 0 && ratioGraph->GetErrorYhigh(i) == 0 &&
9568 ratioGraph->GetErrorYlow(i) == 0)
9569 ratioGraph->RemovePoint(i);
9570 else
9571 i++;
9572 }
9573 auto _tmpPad = gPad;
9574 _pad->cd();
9575 ratioGraph->Draw("z0psame");
9576 auto minMax = graphMinMax(ratioGraph);
9577 adjustYRange(minMax.first, minMax.second, h, std::get<1>(auxFunctions[h->GetYaxis()->GetTitle()]));
9578 _tmpPad->cd();
9579 }
9580 }
9581 }
9582
9583 dataGraph->Draw("z0p same");
9584 addLegendEntry((noPoint) ? nullptr : dataGraph, strlen(dataGraph->GetTitle()) ? dataGraph->GetTitle() : GetName(),
9585 noPoint ? "" : "pEX0");
9586
9587 auto minMax = graphMinMax(dynamic_cast<TGraphAsymmErrors *>(dataGraph));
9588 adjustYRange(minMax.first, minMax.second);
9589
9590 gPad->Modified();
9591 // gPad->Update();
9592 return;
9593 }
9594
9595 // auto _ax = GetXaxis();
9596 // auto v = (_ax) ? dynamic_cast<RooRealVar*>(/*possibleObs.first()*/_ax->GetParent()) : nullptr;
9597 // if (!v) { v = get<RooRealVar>(); } // self-axis
9598 // if (!v) return;
9599
9600 if (auto lv = get<RooAbsLValue>(); lv && fParent && fParent->get<RooAbsData>()) {
9601 // drawing an observable from a dataset ... build graph, and exit
9602 auto gr = fParent->BuildGraph(lv, true);
9604 gr->Draw(hasSame ? "P" : "AP");
9605 return;
9606 }
9607
9608 if (forceNames != "") {
9609 // drawing a force plot ... build nll and fill a histogram with force terms
9610 auto _dsets = datasets();
9611 bool _drawn = false;
9612 auto _coords = coords();
9613 auto _fr = fitResult();
9614 auto initPar = dynamic_cast<RooRealVar *>(_fr.get<RooFitResult>()->floatParsInit().find(forceNames));
9615 if (!initPar)
9616 return;
9617 std::vector<double> valuesToDo = {initPar->getVal()};
9618 if (initPar->hasError() || initPar->hasAsymError()) {
9619 valuesToDo.push_back(initPar->getVal() + initPar->getErrorLo());
9620 valuesToDo.push_back(initPar->getVal() + initPar->getErrorHi());
9621 }
9622 int ii = 0;
9623 for (auto valueToDo : valuesToDo) {
9624 ii++;
9625 for (auto &d : _dsets) {
9626 if (!d->get()->TestBit(1 << 20))
9627 continue;
9628 auto emptyHist = BuildHistogram(v, true);
9629 emptyHist->SetBit(kCanDelete);
9630 auto _obs = d->obs();
9631 auto x = _obs.find((v) ? dynamic_cast<TObject *>(v)->GetName() : emptyHist->GetXaxis()->GetName());
9632 auto _nll = nll(d);
9633 auto theData = d->get<RooAbsData>();
9634 int nevent = theData->numEntries();
9635 for (int i = 0; i < nevent; i++) {
9636 theData->get(i);
9637 bool _skip = false;
9638 for (const auto &_c : _coords) {
9639 if (auto cat = _c->get<RooAbsCategoryLValue>(); cat) {
9640 if (cat->getIndex() != theData->get()->getCatIndex(cat->GetName())) {
9641 _skip = true;
9642 break;
9643 }
9644 }
9645 }
9646 if (_skip)
9647 continue;
9648
9649 if (x) {
9650 auto val = _nll.pars()->getRealValue(initPar->GetName());
9651 if (ii > 1)
9652 _nll.pars()->setRealValue(initPar->GetName(), valueToDo);
9653 auto nllVal = _nll.getEntryVal(i);
9654 _nll.pars()->setRealValue(initPar->GetName(), initPar->getVal());
9655 auto nllVal2 = _nll.getEntryVal(i);
9656 _nll.pars()->setRealValue(initPar->GetName(), val);
9657 emptyHist->Fill(x->get<RooAbsReal>()->getVal(), (nllVal2 - nllVal));
9658 }
9659 }
9660 // include the extendedTerm, distributed evenly over the bins
9661 // probably should be somehow dependent on data density though (i.e. bins with more data get more of it?)
9662 auto val = _nll.pars()->getRealValue(initPar->GetName());
9663 if (ii > 1)
9664 _nll.pars()->setRealValue(initPar->GetName(), valueToDo);
9665 auto _extTerm = _nll.extendedTerm();
9666 _nll.pars()->setRealValue(initPar->GetName(), initPar->getVal());
9667 auto _extTerm2 = _nll.extendedTerm();
9668 _nll.pars()->setRealValue(initPar->GetName(), val);
9669 for (int i = 1; i <= emptyHist->GetNbinsX(); i++) {
9670 emptyHist->SetBinContent(i,
9671 emptyHist->GetBinContent(i) + (_extTerm2 - _extTerm) / emptyHist->GetNbinsX());
9672 emptyHist->SetBinError(i, 0);
9673 }
9674 emptyHist->GetYaxis()->SetTitle("log (L(#theta)/L(#theta_{0}))");
9675 emptyHist->SetTitle(TString::Format("#theta = %g", (ii > 1) ? valueToDo : val));
9676 if (ii == 1)
9677 emptyHist->SetLineColor(kBlack);
9678 if (ii == 2)
9679 emptyHist->SetLineColor(kRed);
9680 else if (ii == 3)
9681 emptyHist->SetLineColor(kBlue);
9682 emptyHist->Draw(_drawn ? "same" : "");
9683 _drawn = true;
9684 }
9685 }
9686 return;
9687 }
9688
9689 auto rar = get<RooAbsReal>();
9690 const xRooNode *rarNode = this;
9691 if (!rar) {
9692 get()->Draw();
9693 return;
9694 }
9695 RooAbsReal *sf = nullptr;
9696 if (get()->InheritsFrom("RooExtendPdf")) {
9697 browse();
9698 rarNode = find(".pdf").get();
9699 // rar = rarNode->get<RooAbsReal>();
9700 sf = find(".n")->get<RooAbsReal>();
9701 }
9702
9703 auto h = BuildHistogram(v, false, hasErrorOpt);
9704 if (!h)
9705 return;
9706 h->SetBit(kCanDelete);
9707
9708 if (!v)
9709 v = getObject<RooAbsLValue>(h->GetXaxis()->GetName()).get();
9710 RooAbsArg *vv = (v) ? dynamic_cast<RooAbsArg *>(v) : rar;
9711 if (h->GetXaxis()->IsAlphanumeric()) {
9712 // do this to get bin labels
9713 h->GetXaxis()->SetName("xaxis"); // WARNING -- this messes up anywhere we GetXaxis()->GetName()
9714 }
9715
9716 if (rar->InheritsFrom("RooAbsPdf") && !(rar->InheritsFrom("RooRealSumPdf") || rar->InheritsFrom("RooAddPdf"))) {
9717 // append parameter values to title if has such
9718 RooArgSet s;
9719 rar->leafNodeServerList(&s);
9720 if (v)
9721 s.remove(*dynamic_cast<RooAbsArg *>(v));
9722 if (!s.empty()) {
9723 TString ss = h->GetTitle();
9724 ss += " [";
9725 bool first = true;
9726 for (auto _p : s) {
9727 auto _v = dynamic_cast<RooRealVar *>(_p);
9728 if (!_v)
9729 continue;
9730 if (!first)
9731 ss += ",";
9732 first = false;
9733 ss += TString::Format("%s=%g", strlen(_p->GetTitle()) ? _p->GetTitle() : _p->GetName(), _v->getVal());
9734 if (_v->hasError()) {
9735 ss += TString::Format("#pm %g", _v->getError());
9736 }
9737 }
9738 ss += "]";
9739 h->SetTitle(ss);
9740 }
9741 }
9742
9743 if (!hasSame) {
9744 if (obs().find(vv->GetName())) {
9745 gPad->SetGrid(0, 0);
9746 } else {
9747 gPad->SetGrid(1, 1);
9748 }
9749 }
9750 TString dOpt = (TString(rar->ClassName()).Contains("Hist") || rar->isBinnedDistribution(*vv) ||
9751 h->GetNbinsX() == 1 || rar->getAttribute("BinnedLikelihood") ||
9752 (dynamic_cast<RooAbsRealLValue *>(vv) &&
9753 std::unique_ptr<std::list<double>>(rar->binBoundaries(*dynamic_cast<RooAbsRealLValue *>(vv),
9754 -std::numeric_limits<double>::infinity(),
9755 std::numeric_limits<double>::infinity()))))
9756 ? ""
9757 : "LF2";
9758 if (auto d = dynamic_cast<RooHistFunc *>(rar); d && !d->isBinnedDistribution(*vv) && h->GetNbinsX() != 1) {
9759 dOpt = "LF2"; // hist func is interpolated, so draw it as such
9760 }
9761 if (dOpt == "LF2" && !components().empty()) {
9762 // check if all components of dOpt are "Hist" type (CMS model support)
9763 // if so then dOpt="";
9764 bool allHist = true;
9765 for (auto &s : components()) {
9766 if (!(s->get() && TString(s->get()->ClassName()).Contains("Hist"))) {
9767 allHist = false;
9768 break;
9769 }
9770 }
9771 if (allHist)
9772 dOpt = "";
9773 }
9774
9775 if (rar == vv && rar->IsA() == RooRealVar::Class()) {
9776 dOpt += "TEXT";
9777 // add a TExec to the histogram so that when edited it will propagate to var
9778 gROOT->SetEditHistograms(true);
9779 } else {
9780 gROOT->SetEditHistograms(false);
9781 }
9782
9783 if (hasSame)
9784 dOpt += " same";
9785 else
9786 hAxis = h;
9787
9788 if (dOpt.Contains("TEXT") || sOpt.Contains("text")) {
9789 // adjust marker size so text is good
9790 h->SetMarkerSize(gStyle->GetLabelSize("Z") / (0.02 * gPad->GetHNDC()));
9791 }
9792
9793 bool hasError(false);
9794 for (int i = 0; i < h->GetSumw2N(); i++) {
9795 if (h->GetSumw2()->At(i)) {
9796 hasError = true;
9797 break;
9798 }
9799 }
9800
9801 /** This doesn't seem necessary in at least 6.26 any more - pads seem adjusted on their own
9802 if (!hasSame && h->GetYaxis()->GetTitleFont()%10 == 2) {
9803 h->GetYaxis()->SetTitleOffset( gPad->GetLeftMargin() / gStyle->GetPadLeftMargin() );
9804 } */
9805 // don't this instead - dont want to leave as zero (auto) in case show aux plot
9806 if (!hasSame && h->GetYaxis()->GetTitleFont() % 10 == 2) {
9807 h->GetYaxis()->SetTitleOffset(1.);
9808 }
9809
9810 TH1 *errHist = nullptr;
9811 if (hasError) {
9812 h->SetFillStyle(hasError ? 3005 : 0);
9813 h->SetFillColor(h->GetLineColor());
9814 h->SetMarkerStyle(0);
9815 errHist = dynamic_cast<TH1 *>(h->Clone(Form("%s_err", h->GetName())));
9816 errHist->SetBit(kCanDelete);
9817 errHist->SetDirectory(0);
9818 h->SetFillStyle(0);
9819 for (int i = 1; i <= h->GetNbinsX(); i++) {
9820 h->SetBinError(i, 0);
9821 }
9822 }
9823
9824 if (!hasSame)
9825 clearPad();
9826
9827 if (rar == vv && rar->IsA() == RooRealVar::Class()) {
9828 // add a TExec to the histogram so that when edited it will propagate to var
9829 // h->GetListOfFunctions()->Add(h->Clone("self"),"TEXTHIST");
9830 dOpt = "TEXT";
9831 auto node = new xRooNode(*this);
9832 auto _hist = (errHist) ? errHist : h;
9833 auto hCopy = (errHist) ? nullptr : dynamic_cast<TH1 *>(h->Clone());
9834 if (hCopy)
9835 hCopy->SetDirectory(0);
9836 _hist->GetListOfFunctions()->Add(node);
9837 _hist->GetListOfFunctions()->Add(new TExec(
9838 ".update",
9840 "gROOT->SetEditHistograms(true);auto h = dynamic_cast<TH1*>(gPad->GetPrimitive(\"%s\")); if(h) { double "
9841 "range= h->GetMaximum()-h->GetMinimum(); if(auto n "
9842 "= dynamic_cast<xRooNode*>(h->GetListOfFunctions()->FindObject(\"%s\")); n && "
9843 "n->TestBit(TObject::kNotDeleted) && n->get<RooRealVar>()->getVal() != h->GetBinContent(1)) {"
9844 "h->SetBinContent(1, "
9845 "TString::Format(\"%%.2g\",int(h->GetBinContent(1)/(range*0.01))*range*0.01).Atof());n->SetContent( "
9846 "h->GetBinContent(1) ); for(auto pp : *h->GetListOfFunctions()) if(auto hh = "
9847 "dynamic_cast<TH1*>(pp))hh->SetBinContent(1,h->GetBinContent(1));} if(h->GetBinContent(1)==0.) "
9848 "h->SetBinContent(1,range*0.005); gPad->Modified();gPad->Update(); }",
9849 _hist->GetName(), node->GetName())));
9850 if (errHist) {
9851 errHist->GetListOfFunctions()->Add(h, "TEXT HIST same");
9852 errHist->SetFillColor(h->GetLineColor());
9853 } else {
9854 hCopy->SetBit(kCanDelete);
9855 _hist->GetListOfFunctions()->Add(hCopy, "TEXT HIST same");
9856 _hist->SetBinError(1, 0);
9857 }
9858 _hist->SetStats(false);
9859 // if (_hist->GetBinContent(1)==0.) _hist->SetBinContent(1,(_hist->GetMaximum()-_hist->GetMinimum())*0.005);
9860 _hist->Draw(((errHist) ? "e2" : ""));
9861 gPad->Modified();
9862 return;
9863 }
9864
9865 bool overlayExisted = false;
9866 if (hasOverlay) {
9867 h->SetName(TString::Format("%s%s", h->GetName(), overlayName.Data()));
9868 if (auto existing = dynamic_cast<TH1 *>(gPad->GetPrimitive(h->GetName())); existing) {
9869 existing->Reset();
9870 existing->Add(h);
9871 delete h;
9872 h = existing;
9873 overlayExisted = true;
9874 } else {
9875 TString oldStyle = (rar && rar->getStringAttribute("style")) ? rar->getStringAttribute("style") : "";
9876 h->SetTitle(overlayName);
9877 // for overlays will take style from current gStyle before overriding with personal style
9878 // this ensures initial style will be whatever gStyle is, rather than whatever ours is
9879 (TAttLine &)(*h) = *gStyle;
9880
9881 // std::shared_ptr<TStyle> style; // use to keep alive for access from GetStyle below, in case
9882 // getObject has decided to return the owning ptr (for some reason) if
9883 // (!gROOT->GetStyle(h->GetTitle())) {
9884 // if ( (style = getObject<TStyle>(h->GetTitle())) ) {
9885 // // loaded style (from workspace?) so put in list and use that
9886 // gROOT->GetListOfStyles()->Add(style.get());
9887 // } else {
9888 // // create new style - gets put in style list automatically so don't have to delete
9889 // // acquire them so saved to workspaces for auto reload ...
9890 // style = acquireNew<TStyle>(h->GetTitle(),
9891 // TString::Format("Style for %s component", h->GetTitle()));
9892 // (TAttLine &) (*style) = *dynamic_cast<TAttLine *>(h);
9893 // (TAttFill &) (*style) = *dynamic_cast<TAttFill *>(h);
9894 // (TAttMarker &) (*style) = *dynamic_cast<TAttMarker *>(h);
9895 // gROOT->GetListOfStyles()->Add(style.get());
9896 // }
9897 // }
9898 // (TAttLine&)(*h) = *(gROOT->GetStyle(h->GetTitle()) ? gROOT->GetStyle(h->GetTitle()) : gStyle);
9899 // (TAttFill&)(*h) = *(gROOT->GetStyle(h->GetTitle()) ? gROOT->GetStyle(h->GetTitle()) : gStyle);
9900 // (TAttMarker&)(*h) = *(gROOT->GetStyle(h->GetTitle()) ? gROOT->GetStyle(h->GetTitle()) : gStyle);
9901 auto _style = style(h);
9902 rar->setStringAttribute("style", oldStyle == "" ? nullptr : oldStyle.Data()); // restores old style
9903 if (_style) {
9904 (TAttLine &)(*h) = *_style;
9905 (TAttFill &)(*h) = *_style;
9906 (TAttMarker &)(*h) = *_style;
9907 }
9908 h->Draw(dOpt);
9909 if (errHist) {
9910 errHist->SetTitle(overlayName);
9911 (TAttLine &)(*errHist) = *h;
9912 errHist->SetFillColor(h->GetLineColor());
9913 }
9914 }
9915 } else {
9916 auto _style = style(h);
9917 if (_style) {
9918 (TAttLine &)(*h) = *_style;
9919 (TAttFill &)(*h) = *_style;
9920 (TAttMarker &)(*h) = *_style;
9921 if (errHist) {
9922 (TAttLine &)(*errHist) = *h;
9923 errHist->SetFillColor(h->GetLineColor());
9924 }
9925 }
9926 h->Draw(dOpt + sOpt);
9927 }
9928
9929 if (!hasOverlay && (rarNode->get()->InheritsFrom("RooRealSumPdf") || rarNode->get()->InheritsFrom("RooAddPdf"))) {
9930 // build a stack unless not requested
9931 if (!nostack) {
9932 THStack *stack = new THStack(TString::Format("%s_stack", rar->GetName()),
9933 TString::Format("%s;%s", rar->GetTitle(), h->GetXaxis()->GetTitle()));
9934 int count = 2;
9935 std::map<std::string, int> colorByTitle; // TODO: should fill from any existing legend
9936 std::set<std::string> allTitles;
9937 bool titleMatchName = true;
9938 std::map<std::string, TH1 *> histGroups;
9939 std::vector<TH1 *> hhs;
9940
9941 // support for CMS model case where has single component containing many coeffs
9942 // will build stack by setting each coeff equal to 0 in turn, rebuilding the histogram
9943 // the difference from the "full" histogram will be the component
9944 RooArgList cms_coefs;
9945 if (!rarNode->components().empty()) {
9946 auto comps = rarNode->components()[0];
9947 for (auto &c : *comps) {
9948 if (c->fFolder == "!.coeffs")
9949 cms_coefs.add(*c->get<RooAbsArg>());
9950 }
9951 }
9952 if (!cms_coefs.empty()) {
9953 RooRealVar zero("zero", "", 0);
9954 std::shared_ptr<TH1> prevHist((TH1 *)h->Clone());
9955 for (auto c : cms_coefs) {
9956 // seems I have to remake the function each time, as haven't figured out what cache needs clearing?
9957 std::unique_ptr<RooAbsReal> f(
9958 dynamic_cast<RooAbsReal *>(rarNode->components()[0]->get()->Clone("tmpCopy")));
9959 zero.setAttribute(
9960 Form("ORIGNAME:%s", c->GetName())); // used in redirectServers to say what this replaces
9961 f->redirectServers(RooArgSet(zero), false, true); // each time will replace one additional coef
9962 // zero.setAttribute(Form("ORIGNAME:%s",c->GetName()),false); (commented out so that on next iteration
9963 // will still replace all prev)
9964 auto hh = xRooNode(*f, *this).BuildHistogram(v);
9965 if (sf)
9966 hh->Scale(sf->getVal());
9967 if (strlen(hh->GetTitle()) == 0)
9968 hh->SetTitle(c->GetName()); // ensure all hists has titles
9969 titleMatchName &= (TString(c->GetName()) == hh->GetTitle() ||
9970 TString(hh->GetTitle()).BeginsWith(TString(c->GetName()) + "_"));
9971 std::shared_ptr<TH1> nextHist((TH1 *)hh->Clone());
9972 hh->Add(prevHist.get(), -1.);
9973 hh->Scale(-1.);
9974 hhs.push_back(hh);
9975 prevHist = nextHist;
9976 }
9977 } else {
9978 for (auto &samp : rarNode->components()) {
9979 auto hh = samp->BuildHistogram(v);
9980 if (sf)
9981 hh->Scale(sf->getVal());
9982 hhs.push_back(hh);
9983 if (strlen(hh->GetTitle()) == 0)
9984 hh->SetTitle(samp->GetName()); // ensure all hists has titles
9985 titleMatchName &= (TString(samp->GetName()) == hh->GetTitle() ||
9986 TString(hh->GetTitle()).BeginsWith(TString(samp->GetName()) + "_"));
9987 }
9988 }
9989 for (auto &hh : hhs) {
9990 // automatically group hists that all have the same title
9991 if (histGroups.find(hh->GetTitle()) == histGroups.end()) {
9992 histGroups[hh->GetTitle()] = hh;
9993 } else {
9994 // add it into this group
9995 histGroups[hh->GetTitle()]->Add(hh);
9996 delete hh;
9997 continue;
9998 }
9999 auto hhMin = (hh->GetMinimum() == 0) ? hh->GetMinimum(1e-9) : hh->GetMinimum();
10000 if (!stack->GetHists() && h->GetMinimum() > hhMin) {
10001 auto newMin = hhMin - (h->GetMaximum() - hhMin) * gStyle->GetHistTopMargin();
10002 if (hhMin >= 0 && newMin < 0)
10003 newMin = hhMin * 0.99;
10004 adjustYRange(newMin, h->GetMaximum());
10005 }
10006
10007 /*if(stack->GetHists() && stack->GetHists()->GetEntries()>0) {
10008 // to remove rounding effects on bin boundaries, see if binnings compatible
10009 auto _h1 = dynamic_cast<TH1*>(stack->GetHists()->At(0));
10010 if(_h1->GetNbinsX()==hh->GetNbinsX()) TODO ... finish dealing with silly rounding effects
10011 }*/
10012 TString thisOpt = dOpt;
10013 // uncomment next line to blend continuous with discrete components .. get some unpleasant "poke through"
10014 // effects though
10015 // if(auto s = samp->get<RooAbsReal>(); s) thisOpt = s->isBinnedDistribution(*dynamic_cast<RooAbsArg*>(v)) ?
10016 // "" : "LF2";
10017 stack->Add(hh, thisOpt);
10018 allTitles.insert(hh->GetTitle());
10019 }
10020 stack->SetBit(kCanDelete); // should delete its sub histograms
10021 stack->Draw("noclear same");
10022 h->Draw(
10023 dOpt + sOpt +
10024 "same"); // overlay again .. if stack would cover original hist (negative components) we still see integral
10025 h->Draw("axissame"); // redraws axis
10026
10027 TList *ll = stack->GetHists();
10028 if (ll && ll->GetEntries()) {
10029
10030 // get common prefix to strip off only if all titles match names and
10031 // any title is longer than 10 chars
10032 size_t e = std::min(allTitles.begin()->size(), allTitles.rbegin()->size());
10033 size_t ii = 0;
10034 bool goodPrefix = false;
10035 std::string commonSuffix;
10036 if (titleMatchName && ll->GetEntries() > 1) {
10037 while (ii < e - 1 && allTitles.begin()->at(ii) == allTitles.rbegin()->at(ii)) {
10038 ii++;
10039 if (allTitles.begin()->at(ii) == '_' || allTitles.begin()->at(ii) == ' ')
10040 goodPrefix = true;
10041 }
10042
10043 // find common suffix if there is one .. must start with a "_"
10044 bool stop = false;
10045 while (!stop && commonSuffix.size() < size_t(e - 1)) {
10046 commonSuffix = allTitles.begin()->substr(allTitles.begin()->length() - commonSuffix.length() - 1);
10047 for (auto &t : allTitles) {
10048 if (!TString(t).EndsWith(commonSuffix.c_str())) {
10049 commonSuffix = commonSuffix.substr(1);
10050 stop = true;
10051 break;
10052 }
10053 }
10054 }
10055 if (commonSuffix.find('_') == std::string::npos)
10056 commonSuffix = "";
10057 else
10058 commonSuffix = commonSuffix.substr(commonSuffix.find('_'));
10059 }
10060 if (!goodPrefix)
10061 ii = 0;
10062
10063 // also find how many characters are needed to distinguish all entries (that dont have the same name)
10064 // then carry on up to first space or underscore
10065 size_t jj = 0;
10066 std::map<std::string, std::string> reducedTitles;
10067 while (reducedTitles.size() != allTitles.size()) {
10068 jj++;
10069 std::map<std::string, int> titlesMap;
10070 for (auto &s : allTitles) {
10071 if (reducedTitles.count(s))
10072 continue;
10073 titlesMap[s.substr(0, jj)]++;
10074 }
10075 for (auto &s : allTitles) {
10076 if (titlesMap[s.substr(0, jj)] == 1 && (jj >= s.length() || s.at(jj) == ' ' || s.at(jj) == '_')) {
10077 reducedTitles[s] = s.substr(0, jj);
10078 }
10079 }
10080 }
10081
10082 // strip common prefix and suffix before adding
10083 for (int i = ll->GetEntries() - 1; i >= 0; i--) { // go in reverse order
10084 auto _title = (ll->GetEntries() > 5) ? reducedTitles[ll->At(i)->GetTitle()] : ll->At(i)->GetTitle();
10085 _title = _title.substr(ii < _title.size() ? ii : 0);
10086 if (!commonSuffix.empty() && TString(_title).EndsWith(commonSuffix.c_str()))
10087 _title = _title.substr(0, _title.length() - commonSuffix.length());
10088
10089 // style hists according to available styles ... creating if necessary
10090 dynamic_cast<TNamed *>(ll->At(i))->SetTitle(_title.c_str());
10091 addLegendEntry(ll->At(i), _title.c_str(), "f");
10092 }
10093 // finally, ensure all hists are styled
10094 for (auto ho : *ll) {
10095 TH1 *hh = dynamic_cast<TH1 *>(ho);
10096 if (!hh)
10097 continue;
10098 bool createdStyle = (xRooNode(*hh, *this).style(nullptr, false) == nullptr);
10099
10100 if (createdStyle) {
10101 // give hist a color, that isn't the same as any other hists color
10102 bool used = false;
10103 do {
10104 hh->SetFillColor((count++) % 100);
10105 // check not already used this color
10106 used = false;
10107 for (auto ho2 : *ll) {
10108 TH1 *hh2 = dynamic_cast<TH1 *>(ho2);
10109 if (!hh2)
10110 continue;
10111 auto _style = xRooNode(*hh2, *this).style(hh2, false);
10112 if (hh != hh2 && _style && _style->GetFillColor() == hh->GetFillColor()) {
10113 used = true;
10114 break;
10115 }
10116 }
10117 } while (used);
10118 }
10119
10120 auto _style = xRooNode(*hh, *this).style(hh);
10121 if (_style) {
10122 *dynamic_cast<TAttLine *>(hh) = *_style;
10123 *dynamic_cast<TAttFill *>(hh) = *_style;
10124 *dynamic_cast<TAttMarker *>(hh) = *_style;
10125 }
10126 }
10127 }
10128 }
10129 } else if (!overlayExisted) {
10130
10131 if (errHist) {
10132 addLegendEntry(errHist, strlen(errHist->GetTitle()) ? errHist->GetTitle() : GetName(), "fl");
10133 } else {
10134 addLegendEntry(h, strlen(h->GetTitle()) ? h->GetTitle() : GetName(), "l");
10135 }
10136 }
10137
10138 if (errHist) {
10139 dOpt.ReplaceAll("TEXT", "");
10140 errHist->Draw(dOpt + (dOpt.Contains("LF2") ? "e3same" : "e2same"));
10141 double ymax = -std::numeric_limits<double>::infinity();
10142 double ymin = std::numeric_limits<double>::infinity();
10143 for (int i = 1; i <= errHist->GetNbinsX(); i++) {
10144 ymax = std::max(ymax, errHist->GetBinContent(i) + errHist->GetBinError(i));
10145 ymin = std::min(ymin, errHist->GetBinContent(i) - errHist->GetBinError(i));
10146 }
10147 adjustYRange(ymin, ymax);
10148 } else {
10149 adjustYRange(h->GetMinimum() * 0.9, h->GetMaximum() * 1.1);
10150 }
10151
10152 if ((!auxPlotTitle.empty()) && !hasSame) {
10153 // create a pad for the ratio ... shift the bottom margin of this pad to make space for it
10154 double padFrac = 0.3;
10155 auto _tmpPad = gPad;
10156 gPad->SetBottomMargin(padFrac);
10157 auto ratioPad = new TPad("auxPad", "aux plot", 0, 0, 1, padFrac);
10158 ratioPad->SetFillColor(_tmpPad->GetFillColor());
10159 ratioPad->SetNumber(1);
10160 ratioPad->SetBottomMargin(ratioPad->GetBottomMargin() / padFrac);
10161 ratioPad->SetTopMargin(0.04);
10162 ratioPad->SetLeftMargin(gPad->GetLeftMargin());
10163 ratioPad->SetRightMargin(gPad->GetRightMargin());
10164 ratioPad->cd();
10165 TH1 *ratioHist = dynamic_cast<TH1 *>((errHist) ? errHist->Clone("auxHist") : h->Clone("auxHist"));
10166 ratioHist->SetDirectory(0);
10167 ratioHist->SetTitle((errHist) ? errHist->GetName()
10168 : h->GetName()); // abuse the title string to hold the name of the main hist
10169
10170 ratioHist->GetYaxis()->SetNdivisions(5, 0, 0);
10171 ratioHist->GetYaxis()->SetTitle(auxPlotTitle.c_str());
10172 ratioHist->SetTitle(
10173 TString::Format("%s|%s", ratioHist->GetTitle(),
10174 auxPlotTitle.c_str())); // used when plotting data (above) to decide what to calculate
10175 ratioHist->SetMaximum();
10176 ratioHist->SetMinimum(); // resets min and max
10177 ratioPad->SetGridy();
10178
10179 for (int i = 1; i <= ratioHist->GetNbinsX(); i++) {
10180 double val = ratioHist->GetBinContent(i);
10181 double err = ratioHist->GetBinError(i);
10182 ratioHist->SetBinContent(i, std::get<0>(auxFunctions[auxPlotTitle])(val, val, err));
10183 ratioHist->SetBinError(i, std::get<0>(auxFunctions[auxPlotTitle])(val + err, val, err) -
10184 ratioHist->GetBinContent(i));
10185 }
10186
10187 double rHeight = 1. / padFrac; //(_tmpPad->GetWNDC())/(gPad->GetHNDC());
10188 if (ratioHist->GetYaxis()->GetTitleFont() % 10 == 2) {
10189 ratioHist->GetYaxis()->SetTitleSize(ratioHist->GetYaxis()->GetTitleSize() * rHeight);
10190 ratioHist->GetYaxis()->SetLabelSize(ratioHist->GetYaxis()->GetLabelSize() * rHeight);
10191 ratioHist->GetXaxis()->SetTitleSize(ratioHist->GetXaxis()->GetTitleSize() * rHeight);
10192 ratioHist->GetXaxis()->SetLabelSize(ratioHist->GetXaxis()->GetLabelSize() * rHeight);
10193 ratioHist->GetYaxis()->SetTitleOffset(ratioHist->GetYaxis()->GetTitleOffset() / rHeight);
10194 } else {
10195#if ROOT_VERSION_CODE < ROOT_VERSION(6, 26, 00)
10196 ratioHist->GetYaxis()->SetTitleOffset(ratioHist->GetYaxis()->GetTitleOffset() / rHeight);
10197#endif
10198 }
10199 ratioHist->GetXaxis()->SetTickLength(ratioHist->GetXaxis()->GetTickLength() * rHeight);
10200 ratioHist->SetStats(false);
10201 ratioHist->SetBit(TH1::kNoTitle);
10202 ratioHist->SetBit(kCanDelete);
10203 if (errHist) {
10204 auto _h = dynamic_cast<TH1 *>(ratioHist->Clone("auxHist_clone"));
10205 _h->SetDirectory(0);
10206 _h->SetFillColor(0);
10207 ratioHist->GetListOfFunctions()->Add(_h, "histsame");
10208 //_h->Draw("histsame");
10209 }
10210 ratioHist->GetListOfFunctions()->Add(new TExec(
10211 ".updateAxis",
10212 TString::Format("auto h1 = (TH1*)%p; auto h2 = (TH1*)%p; if(h2->GetXaxis()->GetFirst() != "
10213 "h1->GetXaxis()->GetFirst() || h1->GetXaxis()->GetLast()!=h2->GetXaxis()->GetLast()) "
10214 "{h2->GetXaxis()->SetRange(h1->GetXaxis()->GetFirst(),h1->GetXaxis()->GetLast());if(gPad) "
10215 "{gPad->GetCanvas()->Paint();gPad->GetCanvas()->Update();}}",
10216 (void *)ratioHist, (void *)(h))));
10217 ratioHist->Draw((errHist ? "e2" : ""));
10218
10219 _tmpPad->cd();
10220 ratioPad->Draw();
10221 } else if (auto ratioPad = dynamic_cast<TPad *>(gPad->GetPrimitive("auxPad")); hasSame && ratioPad) {
10222 // need to draw histogram in the ratio pad ...
10223 // if doing overlay need to update histogram
10224
10225 if (auto hr = dynamic_cast<TH1 *>(ratioPad->GetPrimitive("auxHist"));
10226 hr && auxFunctions.find(hr->GetYaxis()->GetTitle()) != auxFunctions.end()) {
10227 TString histName = hr->GetTitle(); // split it by | char
10228 TString histType = histName(histName.Index('|') + 1, histName.Length());
10229 histName = histName(0, histName.Index('|'));
10230
10231 if (auto hnom = dynamic_cast<TH1 *>(gPad->GetPrimitive(histName)); hnom) {
10232 h = dynamic_cast<TH1 *>(h->Clone(h->GetName()));
10233 h->SetDirectory(0);
10234 h->SetBit(kCanDelete);
10235 for (int i = 1; i <= hnom->GetNbinsX(); i++) {
10236 double val = h->GetBinContent(i);
10237 double err = h->GetBinError(i);
10238 h->SetBinContent(i, std::get<0>(auxFunctions[hr->GetYaxis()->GetTitle()])(
10239 h->GetBinContent(i), hnom->GetBinContent(i), hnom->GetBinError(i)));
10240 h->SetBinError(i, std::get<0>(auxFunctions[hr->GetYaxis()->GetTitle()])(
10241 val + err, hnom->GetBinContent(i), hnom->GetBinError(i)) -
10242 h->GetBinContent(i));
10243 }
10244 auto _tmpPad = gPad;
10245 ratioPad->cd();
10246 if (hasOverlay) {
10247 if (auto existing = dynamic_cast<TH1 *>(ratioPad->GetPrimitive(h->GetName())); existing) {
10248 existing->Reset();
10249 existing->Add(h);
10250 delete h;
10251 h = existing;
10252 overlayExisted = true;
10253 } else {
10254 h->Draw(dOpt);
10255 }
10256 } else {
10257 h->Draw(dOpt);
10258 }
10259 double ymax = -std::numeric_limits<double>::infinity();
10260 double ymin = std::numeric_limits<double>::infinity();
10261 for (int i = 1; i <= h->GetNbinsX(); i++) {
10262 ymax = std::max(ymax, h->GetBinContent(i) + h->GetBinError(i));
10263 ymin = std::min(ymin, h->GetBinContent(i) - h->GetBinError(i));
10264 }
10265 adjustYRange(ymin, ymax, hr, std::get<1>(auxFunctions[hr->GetYaxis()->GetTitle()]));
10266 // adjustYRange(h->GetMinimum() * (h->GetMinimum()<0 ? 1.1 : 0.9), h->GetMaximum() * (h->GetMinimum()<0 ?
10267 // 0.9 : 1.1), hr, std::get<1>(auxFunctions[hr->GetYaxis()->GetTitle()]));
10268 gPad->Modified();
10269 _tmpPad->cd();
10270 }
10271 }
10272 }
10273
10274 // see if it's in a simultaneous so need to select a cat
10275 /*auto _parent = fParent;
10276 auto _me = rar;
10277 while(_parent) {
10278 if (auto s = _parent->get<RooSimultaneous>(); s) {
10279 for (auto c : s->indexCat()) {
10280 if (auto p = s->getPdf(c.first.c_str());_me==p) {
10281 gPad->SetName(c.first.c_str());
10282 break;
10283 }
10284 }
10285 break;
10286 }
10287 _me = _parent->get<RooAbsReal>();
10288 _parent = _parent->fParent;
10289 }*/
10290
10291 // now draw selected datasets on top if this was a pdf
10292 if (auto _pdf = get<RooAbsPdf>();
10293 !hasSame && _pdf /*&& (_pdf->canBeExtended() || robs().empty())*/ && coefs().empty()) {
10294 auto _dsets = datasets();
10295 // bool _drawn=false;
10296 for (auto &d : _dsets) {
10297 if (d->get()->TestBit(1 << 20)) {
10298 d->Draw("same");
10299 //_drawn=true;
10300 }
10301 }
10302 // if (!_drawn && !_dsets.empty()) _dsets[0]->Draw("same"); // always draw if has a dataset
10303 }
10304
10305 gPad->Modified();
10306 // gPad->Update();
10307 getLegend();
10308 gPad->Modified();
10309 // gPad->Update();
10310}
10311
10312void xRooNode::SaveAs(const char *filename, Option_t *option) const
10313{
10314 TString sOpt(option);
10315 sOpt.ToLower();
10316 if (auto w = get<RooWorkspace>(); w) {
10317
10318 if (TString(filename).EndsWith(".json")) {
10319#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 26, 00)
10320 // stream with json tool
10321 RooJSONFactoryWSTool tool(*w);
10322 if (tool.exportJSON(filename)) {
10323 Info("SaveAs", "%s saved to %s", w->GetName(), filename);
10324 } else {
10325 Error("SaveAs", "Unable to save to %s", filename);
10326 }
10327#else
10328 Error("SaveAs", "json format workspaces only in ROOT 6.26 onwards");
10329#endif
10330 return;
10331 }
10332
10333#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
10334 // before saving, clear the eocache of all owned nodes
10335 // because causes memory leak when read back in (workspace streamer immediately overwrites the caches)
10336 // fixed in: https://github.com/root-project/root/pull/12024
10337 for (auto &c : w->components()) {
10338 c->_eocache = nullptr;
10339 }
10340#endif
10341 // const_cast<Node2*>(this)->sterilize(); - tried this to reduce mem leak on readback but no improve
10342 if (!w->writeToFile(filename, sOpt != "update")) {
10343 Info("SaveAs", "%s saved to %s", w->GetName(), filename);
10344 // save any fitDatabase that is loaded in memory too
10345 // TODO: We should do this as well for SaveAs on a scan object
10346 if (auto fitDb = dynamic_cast<TFile *>(gROOT->GetListOfFiles()->FindObject("fitDatabase"))) {
10347
10348 std::function<void(TDirectory *, TDirectory *)> CopyDir;
10349
10350 CopyDir = [&](TDirectory *source, TDirectory *dest) {
10351 auto dir = dest->GetDirectory(source->GetName());
10352 if (!dir) {
10353 dir = dest->mkdir(source->GetName());
10354 }
10355 for (auto k : *source->GetListOfKeys()) {
10356 auto key = dynamic_cast<TKey *>(k);
10357 const char *classname = key->GetClassName();
10358 TClass *cl = gROOT->GetClass(classname);
10359 // std::cout << "processing " << key->GetName() << " " << classname << std::endl;
10360 if (!cl) {
10361 continue;
10362 } else if (cl->InheritsFrom(TDirectory::Class())) {
10363 CopyDir(source->GetDirectory(key->GetName()), dir);
10364 } else {
10365 // don't write object if it already exists
10366 if (dir->FindKey(key->GetName()))
10367 continue;
10368 // support FitConfigs ....
10369 if (strcmp(classname, "ROOT::Fit::FitConfig") == 0) {
10370 auto fc = key->ReadObject<ROOT::Fit::FitConfig>();
10371 dir->WriteObject(fc, key->GetName());
10372 delete fc;
10373 } else {
10374 TObject *obj = key->ReadObj();
10375 if (obj) {
10376 dir->WriteTObject(obj, key->GetName());
10377 delete obj;
10378 }
10379 }
10380 }
10381 }
10382 };
10383 CopyDir(fitDb, std::make_unique<TFile>(filename, "UPDATE").get());
10384 Info("SaveAs", "Saved fitDatabase to %s", filename);
10385 }
10386
10387 } else {
10388 Error("SaveAs", "Unable to save to %s", filename);
10389 }
10390#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
10391 // restore the cache to every node
10392 for (auto &c : w->components()) {
10393 c->setExpensiveObjectCache(w->expensiveObjectCache());
10394 }
10395#endif
10396 }
10397}
10398
10399double xRooNode::GetBinError(int bin, const xRooNode &fr) const
10400{
10401 auto res = GetBinErrors(bin, bin, fr);
10402 if (res.empty())
10403 return std::numeric_limits<double>::quiet_NaN();
10404 return res.at(0);
10405}
10406
10407std::pair<double, double> xRooNode::IntegralAndError(const xRooNode &fr, const char *rangeName) const
10408{
10409 double out = 1.;
10410 double err = std::numeric_limits<double>::quiet_NaN();
10411
10412 std::unique_ptr<RooAbsCollection> _snap;
10413 RooArgList _pars;
10414 if (auto _fr = fr.get<RooFitResult>()) {
10415 _pars.add(pars().argList());
10416 _snap.reset(_pars.snapshot());
10417 _pars = _fr->floatParsFinal();
10418 _pars = _fr->constPars();
10419 }
10420
10421 auto _obs = obs();
10422 auto _coefs = coefs(); // need here to keep alive owned RooProduct
10423 if (auto c = _coefs.get<RooAbsReal>(); c) {
10424 out = c->getVal(*_obs.get<RooArgList>()); // assumes independent of observables!
10425 }
10426
10427 if (auto p = dynamic_cast<RooAbsPdf *>(get()); p) {
10428 // prefer to use expectedEvents for integrals of RooAbsPdf e.g. for RooProdPdf wont include constraint terms
10429 if (rangeName)
10430 p->setNormRange(rangeName);
10431#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 27, 00)
10433#endif
10434 out *= p->expectedEvents(*_obs.get<RooArgList>());
10435#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
10436 // improved normSet invalidity checking, so assuming no longer need this in 6.28 onwards
10437 p->_normSet = nullptr;
10438#endif
10439 err = GetBinError(-1, fr);
10440 if (rangeName)
10441 p->setNormRange(nullptr);
10442 } else if (auto p2 = dynamic_cast<RooAbsReal *>(get()); p2) {
10443 // only integrate over observables we actually depend on
10444 auto f = std::shared_ptr<RooAbsReal>(
10445 p2->createIntegral(*std::unique_ptr<RooArgSet>(p2->getObservables(*_obs.get<RooArgList>())),
10446 rangeName)); // did use x here before using obs
10447 RooProduct pr("int_x_coef", "int_x_coef",
10448 RooArgList(*f, !_coefs.get() ? RooFit::RooConst(1) : *_coefs.get<RooAbsReal>()));
10449 out *= f->getVal();
10450 err = xRooNode(pr, *this).GetBinError(-1, fr);
10451 sterilize(); // needed so that we can forget properly about the integral we just created (and are deleting)
10452 } else if (get<RooAbsData>()) {
10453 out = 0;
10454 auto vals = GetBinContents(1, 0); // returns all bins
10455 auto ax = (rangeName) ? GetXaxis() : nullptr;
10456 auto rv = (ax) ? dynamic_cast<RooRealVar *>(ax->GetParent()) : nullptr;
10457 auto cv = (ax && !rv) ? dynamic_cast<RooCategory *>(ax->GetParent()) : nullptr;
10458 int i = 0;
10459 for (auto &v : vals) {
10460 i++;
10461 if (rangeName) {
10462 if (rv && !rv->inRange(ax->GetBinCenter(i), rangeName))
10463 continue;
10464 if (cv && !cv->isStateInRange(rangeName, ax->GetBinLabel(i)))
10465 continue;
10466 }
10467 out += v;
10468 }
10469 err = 0; // should this be sqrt(sum(v^2)) or something similar
10470 } else {
10471 out = std::numeric_limits<double>::quiet_NaN();
10472 }
10473 if (_snap) {
10474 _pars.RooAbsCollection::operator=(*_snap);
10475 }
10476 return std::make_pair(out, err);
10477}
10478
10479std::vector<double> xRooNode::GetBinErrors(int binStart, int binEnd, const xRooNode &_fr) const
10480{
10481 // note: so far this method is inconsistent with the BuildHistogram in ways:
10482 // no projection over other variables
10483 // July2023: made RooRealSumPdf evaluate as a function if doesn't have a floor
10484 // but this method will still evaluate it as a pdf (uses PdfWrapper)
10485 // but can get away with it while added NaN recovery to getSimplePropagatedError to pickup raw values
10486
10487 if (fBinNumber != -1) {
10488 if (binStart != binEnd || !fParent) {
10489 throw std::runtime_error(TString::Format("%s is a bin - only has one value", GetName()));
10490 }
10491 return fParent->GetBinErrors(fBinNumber, fBinNumber);
10492 }
10493
10494 std::vector<double> out;
10495
10496 auto o = dynamic_cast<RooAbsReal *>(get());
10497 if (!o)
10498 return out;
10499
10500 std::shared_ptr<RooFitResult> fr = std::dynamic_pointer_cast<RooFitResult>(_fr.fComp);
10501 //= dynamic_cast<RooFitResult*>( _fr.get<RooFitResult>() ? _fr->Clone() : fitResult()->Clone());
10502
10503 auto _coefs = coefs();
10504
10505 if (!fr) {
10506 // need to ensure coefs, if any, are included in fit result retrieval so all pars are loaded
10507 auto frn = (!_coefs.get() ? *this : xRooNode(RooProduct("tmp", "tmp", RooArgList(*o, *_coefs.get<RooAbsReal>()))))
10508 .fitResult();
10509 if (strlen(_fr.GetName()))
10510 frn = frn.reduced(_fr.GetName());
10511
10512 // use name to reduce the fit result, if one given
10513 fr = std::dynamic_pointer_cast<RooFitResult>(frn.fComp);
10514 }
10515
10516 if (!GETDMP(fr.get(), _finalPars)) {
10517 fr->setFinalParList(RooArgList());
10518 }
10519
10520 /// Oct2022: No longer doing this because want to allow fitResult to be used to get partial error
10521 // // need to add any floating parameters not included somewhere already in the fit result ...
10522 // RooArgList l;
10523 // for(auto& p : pars()) {
10524 // auto v = p->get<RooRealVar>();
10525 // if (!v) continue;
10526 // if (v->isConstant()) continue;
10527 // if (fr->floatParsFinal().find(v->GetName())) continue;
10528 // if (fr->_constPars && fr->_constPars->find(v->GetName())) continue;
10529 // l.add(*v);
10530 // }
10531 //
10532 // if (!l.empty()) {
10533 // RooArgList l2; l2.addClone(fr->floatParsFinal());
10534 // l2.addClone(l);
10535 // fr->setFinalParList(l2);
10536 // }
10537
10538 TMatrixTSym<double> *prevCov = static_cast<TMatrixTSym<double> *>(GETDMP(fr.get(), _VM));
10539
10540 if (!prevCov || size_t(prevCov->GetNcols()) < fr->floatParsFinal().size()) {
10541 TMatrixDSym cov(fr->floatParsFinal().getSize());
10542 if (prevCov) {
10543 for (int i = 0; i < prevCov->GetNcols(); i++) {
10544 for (int j = 0; j < prevCov->GetNrows(); j++) {
10545 cov(i, j) = (*prevCov)(i, j);
10546 }
10547 }
10548 }
10549 int i = 0;
10550 for (auto &p : fr->floatParsFinal()) {
10551 if (!prevCov || i >= prevCov->GetNcols()) {
10552 cov(i, i) = pow(dynamic_cast<RooRealVar *>(p)->getError(), 2);
10553 }
10554 i++;
10555 }
10556 int covQualBackup = fr->covQual();
10557 fr->setCovarianceMatrix(cov);
10558 fr->setCovQual(covQualBackup);
10559 }
10560
10561 bool doBinWidth = false;
10562 auto ax = (binStart == -1 && binEnd == -1) ? nullptr : GetXaxis();
10563
10564 auto _obs = obs(); // may own an obs so keep alive here
10565 RooArgList normSet = _obs.argList();
10566 // to give consistency with BuildHistogram method, should be only the axis var if defined
10567 if (ax) {
10568 normSet.clear();
10569 normSet.add(*dynamic_cast<RooAbsArg *>(ax->GetParent()));
10570 }
10571
10572 if (auto p = dynamic_cast<RooAbsPdf *>(o); ax && (p || _coefs.get() || o->getAttribute("density"))) {
10573 // pdfs of samples embedded in a sumpdf (aka have a coef) will convert their density value to a content
10574 doBinWidth = true;
10575 }
10576 if (binEnd == 0) {
10577 if (ax)
10578 binEnd = ax->GetNbins();
10579 else
10580 binEnd = binStart;
10581 }
10582 for (int bin = binStart; bin <= binEnd; bin++) {
10583 if (ax)
10584 dynamic_cast<RooAbsLValue *>(ax->GetParent())->setBin(bin - 1, ax->GetName());
10585 // if (!SetBin(bin)) { return out; }
10586
10587 double res;
10588 if (auto p = dynamic_cast<RooAbsPdf *>(o); p) {
10589 // fr->covarianceMatrix().Print();
10590 res = PdfWrapper(*p, _coefs.get<RooAbsReal>(), !ax).getSimplePropagatedError(*fr, normSet);
10591#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
10592 // improved normSet invalidity checking, so assuming no longer need this in 6.28 onwards
10593 p->_normSet = nullptr;
10594#endif
10595 } else {
10596 // res = o->getPropagatedError(*fr, normSet);
10597 // // TODO: What if coef has error? - probably need a FuncWrapper class
10598 // if (auto c = _coefs.get<RooAbsReal>(); c) {
10599 // res *= c->getVal(normSet);
10600 // }
10601 res = RooProduct("errorEval", "errorEval",
10602 RooArgList(*o, !_coefs.get() ? RooFit::RooConst(1) : *_coefs.get<RooAbsReal>()))
10603 .getPropagatedError(*fr, normSet);
10604 }
10605 if (doBinWidth) {
10606 res *= ax->GetBinWidth(bin);
10607 }
10608 out.push_back(res);
10609 }
10610
10611 return out;
10612}
10613
10614
10615 std::string cling::printValue( const xRooNode *v ) {
10616 if(!v) return "nullptr\n";
10617 if(!v->empty()) {
10618 std::string out;
10619 size_t left = v->size();
10620 for(auto n : *v) {
10621 left--;
10622 if(!out.empty()) out += ","; else out += "{";
10623 out += n->GetName();
10624 if(out.length()>100 && left > 0) {
10625 out += TString::Format(",... and %lu more",left);
10626 break;
10627 }
10628 }
10629 out += "}\n";
10630 return out;
10631 }
10632 std::string out;
10633 if(!(*v)) {
10634 return "<empty node>";
10635 } else {
10636 return Form("Name: %s",v->GetName());
10637 }
10638
10639 return out;
10640}
10641
@ 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
RooAbsTestStatistic * create(const char *name, const char *title, RooAbsReal &real, RooAbsData &adata, const RooArgSet &projDeps, RooAbsTestStatistic::Configuration const &cfg) override
size_t size(const MatrixT &matrix)
retrieve the size of a square matrix
const char Option_t
Definition RtypesCore.h:66
@ kGray
Definition Rtypes.h:65
@ kRed
Definition Rtypes.h:66
@ kBlack
Definition Rtypes.h:65
@ kGreen
Definition Rtypes.h:66
@ kWhite
Definition Rtypes.h:65
@ kCyan
Definition Rtypes.h:66
@ kBlue
Definition Rtypes.h:66
@ kAzure
Definition Rtypes.h:67
@ kYellow
Definition Rtypes.h:66
static void indent(ostringstream &buf, int indent_level)
R__EXTERN TEnv * gEnv
Definition TEnv.h:170
void Warning(const char *location, const char *msgfmt,...)
Use this function in warning situations.
Definition TError.cxx:229
#define gClient
Definition TGClient.h:157
@ kMBOk
Definition TGMsgBox.h:33
@ kMBIconExclamation
Definition TGMsgBox.h:24
winID h TVirtualViewer3D TVirtualGLPainter p
winID h TVirtualViewer3D vv
Option_t Option_t option
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void data
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t 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 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:407
char * Form(const char *fmt,...)
Formats a string in a circular formatting buffer.
Definition TString.cxx:2467
R__EXTERN TStyle * gStyle
Definition TStyle.h:433
R__EXTERN TSystem * gSystem
Definition TSystem.h:560
#define gPad
#define _(A, B)
Definition cfortran.h:108
double GetBinLowEdge(Int_t bin) const override
Return low edge of bin.
Definition xRooNode.cxx:841
double GetBinUpEdge(Int_t bin) const override
Return up edge of bin.
Definition xRooNode.cxx:848
Int_t FindFixBin(double x) const override
Find bin number corresponding to abscissa x.
Definition xRooNode.cxx:890
void Set(Int_t nbins, const double *xbins) override
Initialize axis with variable bins.
Definition xRooNode.cxx:867
RooAbsRealLValue * rvar() const
Definition xRooNode.cxx:894
void SetTitle(const char *title) override
Set the title of the TNamed.
Definition xRooNode.cxx:859
void Set(Int_t nbins, double xmin, double xmax) override
Initialize axis with fix bins.
Definition xRooNode.cxx:880
Int_t FindFixBin(const char *label) const override
Find bin number with label.
Definition xRooNode.cxx:889
RooAbsLValue * var() const
Definition xRooNode.cxx:893
const RooAbsBinning * binning() const
Definition xRooNode.cxx:887
void Set(Int_t nbins, const float *xbins) override
Initialize axis with variable bins.
Definition xRooNode.cxx:873
const char * GetTitle() const override
Returns title of object.
Definition xRooNode.cxx:855
double GetBinWidth(Int_t bin) const override
Return bin width.
Definition xRooNode.cxx:835
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.
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.
std::list< double > * binBoundaries(RooAbsRealLValue &obs, double xlo, double xhi) const override
Retrieve bin boundaries if this distribution is binned in obs.
RooRealProxy fExpPdf
PdfWrapper(const PdfWrapper &other, const char *name=0)
virtual ~PdfWrapper()
PdfWrapper(RooAbsReal &f, RooAbsReal *coef, bool expEvMode=false, RooAbsPdf *expPdf=nullptr)
virtual TObject * clone(const char *newname) const override
RooRealProxy fFunc
The PiecewiseInterpolation is a class that can morph distributions into each other,...
static std::shared_ptr< RooLinkedList > createNLLOptions()
Definition xRooFit.cxx:443
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:135
static xRooNLLVar createNLL(const std::shared_ptr< RooAbsPdf > pdf, const std::shared_ptr< RooAbsData > data, const RooLinkedList &nllOpts)
Definition xRooFit.cxx:86
static std::pair< double, double > matchPrecision(const std::pair< double, double > &in)
Definition xRooFit.cxx:1857
void Draw(Option_t *opt="") override
Default Draw method for all objects.
std::shared_ptr< xRooHypoPoint > asimov(bool readOnly=false)
std::shared_ptr< const RooFitResult > ufit(bool readOnly=false)
std::shared_ptr< const RooFitResult > cfit_null(bool readOnly=false)
std::shared_ptr< const RooFitResult > cfit_alt(bool readOnly=false)
std::shared_ptr< const RooFitResult > gfit()
Definition xRooNLLVar.h:179
void Draw(Option_t *opt="") override
Default Draw method for all objects.
This xRooNLLVar object has several special methods, e.g.
Definition xRooNLLVar.h:59
xRooHypoSpace hypoSpace(const char *parName, int nPoints, double low, double high, double alt_value=std::numeric_limits< double >::quiet_NaN(), const xRooFit::Asymptotics::PLLType &pllType=xRooFit::Asymptotics::Unknown)
The xRooNode class is designed to wrap over a TObject and provide functionality to aid with interacti...
Definition xRooNode.h:51
void _Add_(const char *name, const char *opt)
xRooNode filter(const xRooNode &range) const
void _scan_(const char *what="plr", double nToys=0, const char *xvar="", int nPointsX=0, double lowX=0, double highX=0, const char *constParValues="")
std::vector< std::shared_ptr< xRooNode > > fBrowsables
Definition xRooNode.h:492
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)
xRooNode Remove(const xRooNode &child)
xRooNode globs() const
List of global observables of this node.
xRooNode Combine(const xRooNode &rhs)
bool SetBinData(int bin, double value, const char *dataName="obsData")
xRooNode Add(const xRooNode &child, Option_t *opt="")
xRooNode(const char *type, const char *name, const char *title="")
const char * GetIconName() const override
Returns mime type name of object.
void Draw(Option_t *opt="") override
Default Draw method for all objects.
double GetBinError(int bin, const xRooNode &fr="") const
std::vector< double > GetBinErrors(int binStart=1, int binEnd=0, const xRooNode &fr="") const
TGraph * BuildGraph(RooAbsLValue *v=nullptr, bool includeZeros=false, TVirtualPad *fromPad=nullptr) const
const std::shared_ptr< xRooNode > & at(size_t idx, bool browseResult=true) const
Definition xRooNode.h:146
bool SetBinContent(int bin, double value, const char *par=nullptr, double parVal=1)
std::shared_ptr< xRooNode > operator[](size_t idx)
Definition xRooNode.h:163
void Checked(TObject *obj, bool val)
Definition xRooNode.cxx:472
void Print(Option_t *opt="") const override
Print TNamed name and title.
std::shared_ptr< T > acquireNew(Args &&...args)
Definition xRooNode.h:273
std::shared_ptr< T > acquire2(Args &&...args)
Definition xRooNode.h:267
void SaveAs(const char *filename="", Option_t *option="") const override
Save this object in the file specified by filename.
xRooNode bins() const
bins of a channel or sample, or channels of a multi-channel pdf
std::shared_ptr< xRooNode > find(const std::string &name, bool browseResult=true) const
TGListTree * GetListTree(TBrowser *b) const
bool fInterrupted
appears that if was fXaxis then dialog box for SetXaxis will take as current value
Definition xRooNode.h:482
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)
std::function< xRooNode(xRooNode *)> fBrowseOperation
Definition xRooNode.h:493
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:485
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:480
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:57
xRooNode pars() const
List of parameters (non-observables) of this node.
void SetFitResult(const RooFitResult *fr=nullptr)
bool IsFolder() const override
Returns kTRUE in case object contains browsable objects (like containers or lists of other objects).
Definition xRooNode.cxx:820
bool SetBinError(int bin, double value)
xRooNode histo(const xRooNode &vars="x", const xRooNode &fr="", bool content=true, bool errors=true) const
bool SetContents(const TObject &obj)
Definition xRooNode.h:346
std::shared_ptr< TObject > fComp
Definition xRooNode.h:467
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:897
xRooNode np() const
List of nuisance parameters: non-constant parameters that are not marked of interest,...
xRooNode floats() const
List of parameters that are currently non-constant These parameters do not have the "Constant" attrib...
bool contains(const std::string &name) const
xRooNode reduced(const std::string &range="", bool invert=false) const
auto begin() const -> xRooNodeIterator
Definition xRooNode.h:199
std::shared_ptr< xRooNode > parentPdf() const
like a parent but only for use by getObject
std::shared_ptr< xRooNode > fParent
Definition xRooNode.h:470
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:498
xRooNode & operator=(const TObject &o)
auto end() const -> xRooNodeIterator
Definition xRooNode.h:200
TH1 * BuildHistogram(RooAbsLValue *v=nullptr, bool empty=false, bool errors=false, int binStart=1, int binEnd=0, const xRooNode &fr="") const
xRooNode consts() const
List of parameters that are currently constant.
void _SetBinContent_(int bin, double value, const char *par="", double parVal=1)
std::shared_ptr< TObject > acquire(const std::shared_ptr< TObject > &arg, bool checkFactory=false, bool mustBeNew=false)
void Browse(TBrowser *b=nullptr) override
Browse object. May be overridden for another default action.
Definition xRooNode.cxx:547
double GetBinData(int bin, const char *dataName="obsData")
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())
bool SetData(const TObject &obj, const char *dataName="obsData")
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:394
xRooNode vars() const
List of variables (observables and parameters) of this node.
Class describing the configuration of the fit, options and parameter settings using the ROOT::Fit::Pa...
Definition FitConfig.h:47
Common abstract base class for objects that represent a value and a "shape" in RooFit.
Definition RooAbsArg.h: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 > getParameters(const RooAbsData *data, bool stripDisconnected=true) const
Create a list of leaf nodes in the arg tree starting with ourself as top node that don't match any of...
RooFit::OwningPtr< RooArgSet > getObservables(const RooArgSet &set, bool valueOnly=true) const
Given a set of possible observables, return the observables that this PDF depends on.
void SetName(const char *name) override
Set the name of the TNamed.
const Text_t * getStringAttribute(const Text_t *key) const
Get string attribute mapped under key 'key'.
bool getAttribute(const Text_t *name) const
Check if a named attribute is set. By default, all attributes are unset.
void setAttribute(const Text_t *name, bool value=true)
Set (default) or clear a named boolean attribute of this object.
virtual bool isFundamental() const
Is this object a fundamental type that can be added to a dataset? Fundamental-type subclasses overrid...
Definition RooAbsArg.h:251
RooAbsBinning is the abstract base class for RooRealVar binning definitions.
virtual double highBound() const =0
virtual double lowBound() const =0
Abstract base class for objects that represent a discrete value that can be set from the outside,...
A space to attach TBranches.
const char * getLabel() const
Retrieve current label. Use getCurrentLabel() for more clarity.
Abstract container object that can hold multiple RooAbsArg objects.
RooAbsCollection * selectByAttrib(const char *name, bool value) const
Create a subset of the current collection, consisting only of those elements with the specified attri...
virtual void removeAll()
Remove all arguments from our set, deleting them if we own them.
virtual bool remove(const RooAbsArg &var, bool silent=false, bool matchByNameOnly=false)
Remove the specified argument from our list.
RooAbsCollection * snapshot(bool deepCopy=true) const
Take a snap shot of current collection contents.
Int_t getSize() const
Return the number of elements in the collection.
virtual bool add(const RooAbsArg &var, bool silent=false)
Add the specified argument to list.
Storage_t::size_type size() const
void clear()
Clear contents. If the collection is owning, it will also delete the contents.
virtual RooAbsArg * addClone(const RooAbsArg &var, bool silent=false)
Add a clone of the specified argument to list.
bool selectCommon(const RooAbsCollection &refColl, RooAbsCollection &outColl) const
Create a subset of the current collection, consisting only of those elements that are contained as we...
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={})
Create a reduced copy of this dataset.
virtual Int_t numEntries() const
Return number of entries in dataset, i.e., count unweighted entries.
Abstract base class for objects that are lvalues, i.e.
virtual std::list< std::string > getBinningNames() const =0
Abstract interface for all probability density functions.
Definition RooAbsPdf.h:40
bool canBeExtended() const
If true, PDF can provide extended likelihood term.
Definition RooAbsPdf.h:218
RooAbsRealLValue is the common abstract base class for objects that represent a real value that may a...
void setBin(Int_t ibin, const char *rangeName=nullptr) override
Set value to center of bin 'ibin' of binning 'rangeName' (or of default binning if no range is specif...
bool hasRange(const char *name) const override
Check if variable has a binning with given name.
Context to temporarily change the error logging mode as long as the context is alive.
Definition RooAbsReal.h:324
Abstract base class for objects that represent a real value and implements functionality common to al...
Definition RooAbsReal.h:59
bool isSelectedComp() const
If true, the current pdf is a selected component (for use in plotting)
double getVal(const RooArgSet *normalisationSet=nullptr) const
Evaluate object.
Definition RooAbsReal.h:103
virtual void forceNumInt(bool flag=true)
Definition RooAbsReal.h:169
Efficient implementation of a sum of PDFs of the form.
Definition RooAddPdf.h:33
RooAddition calculates the sum of a set of RooAbsReal terms, or when constructed with two sets,...
Definition RooAddition.h:27
RooArgList is a container object that can hold multiple RooAbsArg objects.
Definition RooArgList.h:22
RooAbsArg * at(Int_t idx) const
Return object at given index, or nullptr if index is out of range.
Definition RooArgList.h:110
Abstract interface for RooAbsArg proxy classes.
Definition RooArgProxy.h:24
RooArgSet is a container object that can hold multiple RooAbsArg objects.
Definition RooArgSet.h:55
RooArgSet * snapshot(bool deepCopy=true) const
Use RooAbsCollection::snapshot(), but return as RooArgSet.
Definition RooArgSet.h:178
Class RooBinning is an implements RooAbsBinning in terms of an array of boundary values,...
Definition RooBinning.h:27
Object to represent discrete states.
Definition RooCategory.h:28
bool defineType(const std::string &label)
Define a state with given name.
static TClass * Class()
Named container for two doubles, two integers two object points and three string pointers that can be...
Definition RooCmdArg.h:26
static const RooCmdArg & none()
Return reference to null argument.
Definition RooCmdArg.cxx:51
RooConstVar represent a constant real-valued object.
Definition RooConstVar.h:23
static TClass * Class()
The RooDataHist is a container class to hold N-dimensional binned data.
Definition RooDataHist.h:39
RooDataSet is a container class to hold unbinned data.
Definition RooDataSet.h:57
RooFitResult is a container class to hold the input and output of a PDF fit to a dataset.
void setCovQual(Int_t val)
const TMatrixDSym & covarianceMatrix() const
Return covariance matrix.
TMatrixDSym reducedCovarianceMatrix(const RooArgList &params) const
Return a reduced covariance matrix (Note that Vred is a simple sub-matrix of V, row/columns are order...
void setCovarianceMatrix(TMatrixDSym &V)
Store externally provided correlation matrix in this RooFitResult ;.
const RooArgList & constPars() const
Return list of constant parameters.
Int_t covQual() const
Return MINUIT quality code of covariance matrix.
const RooArgList & floatParsFinal() const
Return list of floating parameters after fit.
void setFinalParList(const RooArgList &list)
Fill the list of final values of the floating parameters.
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
RooGenericPdf is a concrete implementation of a probability density function, which takes a RooArgLis...
RooHistFunc implements a real-valued function sampled from a multidimensional histogram.
Definition RooHistFunc.h:31
bool isBinnedDistribution(const RooArgSet &) const override
Tests if the distribution is binned. Unless overridden by derived classes, this always returns false.
Definition RooHistFunc.h:95
When using RooFit, statistical models can be conveniently handled and stored as a RooWorkspace.
bool importJSON(std::string const &filename)
Imports a JSON file from the given filename to the workspace.
bool exportJSON(std::string const &fileName)
Export the workspace to JSON format and write to the specified file.
RooLinkedList is an collection class for internal use, storing a collection of RooAbsArg pointers in ...
Int_t GetSize() const
TObject * At(int index) const
Return object stored in sequential position given by index.
TIterator * MakeIterator(bool forward=true) const
Create a TIterator for this list.
static RooMsgService & instance()
Return reference to singleton instance.
StreamConfig & getStream(Int_t id)
void setGlobalKillBelow(RooFit::MsgLevel level)
RooFit::MsgLevel globalKillBelow() const
Class RooObjCacheManager is an implementation of class RooCacheManager<RooAbsCacheElement> and specia...
Poisson pdf.
Definition RooPoisson.h:19
Efficient implementation of a product of PDFs of the form.
Definition RooProdPdf.h:33
A RooProduct represents the product of a given set of RooAbsReal objects.
Definition RooProduct.h:29
static TClass * Class()
Class RooProjectedPdf is a RooAbsPdf implementation that represent a projection of a given input p....
RooProjectedPdf()
Default constructor.
Implements a PDF constructed from a sum of functions:
RooRealVar represents a variable that can be changed from the outside.
Definition RooRealVar.h:37
void setVal(double value) override
Set value of variable to 'value'.
void setError(double value)
Definition RooRealVar.h:60
static TClass * Class()
double getError() const
Definition RooRealVar.h:58
bool hasError(bool allowZero=true) const
Definition RooRealVar.h:59
Facilitates simultaneous fitting of multiple PDFs to subsets of a given dataset.
HypoTestResult is a base class for results from hypothesis tests.
RooStringVar is a RooAbsArg implementing string values.
RooUniformBinning is an implementation of RooAbsBinning that provides a uniform binning in 'n' bins b...
Persistable container for RooFit projects.
const std::map< std::string, RooArgSet > & sets() const
bool import(const RooAbsArg &arg, const RooCmdArg &arg1={}, const RooCmdArg &arg2={}, const RooCmdArg &arg3={}, const RooCmdArg &arg4={}, const RooCmdArg &arg5={}, const RooCmdArg &arg6={}, const RooCmdArg &arg7={}, const RooCmdArg &arg8={}, const RooCmdArg &arg9={})
Import a RooAbsArg object, e.g.
bool removeSet(const char *name)
Remove a named set from the workspace.
static TClass * Class()
RooFactoryWSTool & factory()
Return instance to factory tool.
const Double_t * GetArray() const
Definition TArrayD.h:43
virtual void SetTitleOffset(Float_t offset=1)
Set distance between the axis and the axis title.
Definition TAttAxis.cxx:298
virtual Style_t GetTitleFont() const
Definition TAttAxis.h:47
virtual Float_t GetLabelOffset() const
Definition TAttAxis.h:40
virtual void SetLabelSize(Float_t size=0.04)
Set size of axis labels.
Definition TAttAxis.cxx:203
virtual void SetLabelOffset(Float_t offset=0.005)
Set distance between the axis and the labels.
Definition TAttAxis.cxx:191
virtual void SetTitleSize(Float_t size=0.04)
Set size of axis title.
Definition TAttAxis.cxx:309
virtual Float_t GetTitleSize() const
Definition TAttAxis.h:44
virtual Float_t GetLabelSize() const
Definition TAttAxis.h:41
virtual Float_t GetTickLength() const
Definition TAttAxis.h:45
virtual Float_t GetTitleOffset() const
Definition TAttAxis.h:43
virtual void SetTickLength(Float_t length=0.03)
Set tick mark length.
Definition TAttAxis.cxx:284
virtual void SetNdivisions(Int_t n=510, Bool_t optim=kTRUE)
Set the number of divisions for this axis.
Definition TAttAxis.cxx:233
Fill Area Attributes class.
Definition TAttFill.h:19
virtual Color_t GetFillColor() const
Return the fill area color.
Definition TAttFill.h:30
virtual void SetFillColor(Color_t fcolor)
Set the fill area color.
Definition TAttFill.h:37
virtual void SetFillStyle(Style_t fstyle)
Set the fill area style.
Definition TAttFill.h:39
Line Attributes class.
Definition TAttLine.h:18
virtual void SetLineColor(Color_t lcolor)
Set the line color.
Definition TAttLine.h:40
Marker Attributes class.
Definition TAttMarker.h:19
virtual void SetMarkerStyle(Style_t mstyle=1)
Set the marker style.
Definition TAttMarker.h:40
virtual void SetMarkerSize(Size_t msize=1)
Set the marker size.
Definition TAttMarker.h:45
virtual void SetTextAlign(Short_t align=11)
Set the text alignment.
Definition TAttText.h:42
virtual Font_t GetTextFont() const
Return the text font.
Definition TAttText.h:35
virtual void SetTextColor(Color_t tcolor=1)
Set the text color.
Definition TAttText.h:44
virtual void SetTextFont(Font_t tfont=62)
Set the text font.
Definition TAttText.h:46
virtual void SetTextSize(Float_t tsize=1)
Set the text size.
Definition TAttText.h:47
Class to manage histogram axis.
Definition TAxis.h:31
virtual void LabelsOption(Option_t *option="h")
Set option(s) to draw axis with labels option can be:
Definition TAxis.cxx:662
virtual void SetBinLabel(Int_t bin, const char *label)
Set label for bin.
Definition TAxis.cxx:886
Bool_t IsVariableBinSize() const
Definition TAxis.h:142
const char * GetTitle() const override
Returns title of object.
Definition TAxis.h:135
TAxis()
Default constructor.
Definition TAxis.cxx:50
const TArrayD * GetXbins() const
Definition TAxis.h:136
Double_t GetXmax() const
Definition TAxis.h:140
const char * GetBinLabel(Int_t bin) const
Return label for bin.
Definition TAxis.cxx:440
virtual void Set(Int_t nbins, Double_t xmin, Double_t xmax)
Initialize axis with fix bins.
Definition TAxis.cxx:794
virtual Int_t FindFixBin(Double_t x) const
Find bin number corresponding to abscissa x.
Definition TAxis.cxx:419
Double_t GetXmin() const
Definition TAxis.h:139
Int_t GetNbins() const
Definition TAxis.h:125
Using a TBrowser one can browse all ROOT objects.
Definition TBrowser.h:37
TBrowserImp * GetBrowserImp() const
Definition TBrowser.h:94
static TCanvas * MakeDefCanvas()
Static function to build a default canvas.
Definition TCanvas.cxx:1514
TClass instances represent classes, structs and namespaces in the ROOT type system.
Definition TClass.h:81
Bool_t InheritsFrom(const char *cl) const override
Return kTRUE if this class inherits from a class with name "classname".
Definition TClass.cxx:4874
static TClass * GetClass(const char *name, Bool_t load=kTRUE, Bool_t silent=kFALSE)
Static method returning pointer to TClass of the specified class name.
Definition TClass.cxx:2968
void SetName(const char *name)
virtual Int_t GetEntries() const
virtual void RemoveAll(TCollection *col)
Remove all objects in collection col from this collection.
static Int_t GetColorPalette(Int_t i)
Static function returning the color number i in current palette.
Definition TColor.cxx:1450
static Int_t GetNumberOfColors()
Static function returning number of colors in the color palette.
Definition TColor.cxx:1470
Describe directory structure in memory.
Definition TDirectory.h:45
static TClass * Class()
virtual TDirectory * GetDirectory(const char *namecycle, Bool_t printError=false, const char *funcname="GetDirectory")
Find a directory using apath.
virtual TList * GetListOfKeys() const
Definition TDirectory.h:223
virtual void SetValue(const char *name, const char *value, EEnvLevel level=kEnvChange, const char *type=nullptr)
Set the value of a resource or create a new resource.
Definition TEnv.cxx:736
TExec is a utility class that can be used to execute a C++ command when some event happens in a pad.
Definition TExec.h:26
A ROOT file is composed of a header, followed by consecutive data records (TKey instances) with a wel...
Definition TFile.h:53
System file browser, used as TRootBrowser plug-in.
void AddFSDirectory(const char *entry, const char *path=nullptr, Option_t *opt="")
Add file system directory in the list tree.
A list tree is a widget that can contain a number of items arranged in a tree structure.
Definition TGListTree.h:195
ROOT GUI Window base class.
Definition TGWindow.h:23
virtual void SetName(const char *name)
Definition TGWindow.h:121
The axis painter class.
Definition TGaxis.h:24
void SetTitleOffset(Float_t titleoffset=1)
Definition TGaxis.h:128
void SetLabelFont(Int_t labelfont)
Definition TGaxis.h:105
void SetTitleSize(Float_t titlesize)
Definition TGaxis.h:129
virtual void SetTitle(const char *title="")
Change the title of the axis.
Definition TGaxis.cxx:2942
Int_t GetLabelFont() const
Definition TGaxis.h:79
Float_t GetTitleOffset() const
Definition TGaxis.h:82
Float_t GetTickSize() const
Definition TGaxis.h:91
void SetTickSize(Float_t ticksize)
Definition TGaxis.h:122
void SetLabelSize(Float_t labelsize)
Definition TGaxis.h:107
TGraph with asymmetric error bars.
virtual void SetPointError(Double_t exl, Double_t exh, Double_t eyl, Double_t eyh)
Set ex and ey values for point pointed by the mouse.
A TGraphErrors is a TGraph with error bars.
Double_t GetErrorYlow(Int_t bin) const override
It returns the error along Y at point i.
Double_t GetErrorYhigh(Int_t bin) const override
It returns the error along Y at point i.
A TGraph is an object made of two arrays X and Y with npoints each.
Definition TGraph.h:41
virtual void AddPoint(Double_t x, Double_t y)
Append a new point to the graph.
Definition TGraph.h:97
virtual void SetPoint(Int_t i, Double_t x, Double_t y)
Set x and y values for point number i.
Definition TGraph.cxx:2315
Int_t GetN() const
Definition TGraph.h:130
void SetName(const char *name="") override
Set graph name.
Definition TGraph.cxx:2354
void Draw(Option_t *chopt="") override
Draw this graph with its current attributes.
Definition TGraph.cxx:809
virtual Double_t GetPointY(Int_t i) const
Get y value for point i.
Definition TGraph.cxx:1529
void SetTitle(const char *title="") override
Change (i.e.
Definition TGraph.cxx:2370
virtual void SetPointX(Int_t i, Double_t x)
Set x value for point i.
Definition TGraph.cxx:2339
virtual void SetEditable(Bool_t editable=kTRUE)
if editable=kFALSE, the graph cannot be modified with the mouse by default a TGraph is editable
Definition TGraph.cxx:2274
1-D histogram with a double per channel (see TH1 documentation)}
Definition TH1.h:620
1-D histogram with a float per channel (see TH1 documentation)}
Definition TH1.h:577
TH1 is the base class of all histogram classes in ROOT.
Definition TH1.h:58
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:8854
virtual Double_t GetBinCenter(Int_t bin) const
Return bin center for 1D histogram.
Definition TH1.cxx:9058
void SetTitle(const char *title) override
Change/set the title.
Definition TH1.cxx:6707
virtual Int_t GetNbinsY() const
Definition TH1.h:296
virtual Double_t GetBinError(Int_t bin) const
Return value of error associated to bin number bin.
Definition TH1.cxx:8980
static void AddDirectory(Bool_t add=kTRUE)
Sets the flag controlling the automatic add of histograms in memory.
Definition TH1.cxx:1267
@ kNoTitle
Don't draw the histogram title.
Definition TH1.h:168
virtual void Reset(Option_t *option="")
Reset this histogram: contents, errors, etc.
Definition TH1.cxx:7098
TAxis * GetXaxis()
Definition TH1.h:322
TObject * FindObject(const char *name) const override
Search object named name in the list of functions.
Definition TH1.cxx:3860
virtual Int_t GetNbinsX() const
Definition TH1.h:295
virtual void SetMaximum(Double_t maximum=-1111)
Definition TH1.h:400
virtual void SetBinError(Int_t bin, Double_t error)
Set the bin Error Note that this resets the bin eror option to be of Normal Type and for the non-empt...
Definition TH1.cxx:9123
virtual Int_t Fill(Double_t x)
Increment bin with abscissa X by 1.
Definition TH1.cxx:3345
TAxis * GetYaxis()
Definition TH1.h:323
void Draw(Option_t *option="") override
Draw this histogram with options.
Definition TH1.cxx:3067
virtual void SetMinimum(Double_t minimum=-1111)
Definition TH1.h:401
virtual void SetBinContent(Int_t bin, Double_t content)
Set bin content see convention for numbering bins in TH1::GetBin In case the bin number is greater th...
Definition TH1.cxx:9139
TList * GetListOfFunctions() const
Definition TH1.h:242
void SetName(const char *name) override
Change the name of this histogram.
Definition TH1.cxx:8877
virtual Double_t GetBinContent(Int_t bin) const
Return content of bin number bin.
Definition TH1.cxx:5032
virtual void Scale(Double_t c1=1, Option_t *option="")
Multiply this histogram by a constant c1.
Definition TH1.cxx:6593
TObject * Clone(const char *newname="") const override
Make a complete copy of the underlying object.
Definition TH1.cxx:2734
virtual Bool_t Divide(TF1 *f1, Double_t c1=1)
Performs the operation: this = this/(c1*f1) if errors are defined (see TH1::Sumw2),...
Definition TH1.cxx:2822
virtual void Sumw2(Bool_t flag=kTRUE)
Create structure to store sum of squares of weights.
Definition TH1.cxx:8937
static Bool_t AddDirectoryStatus()
Static function: cannot be inlined on Windows/NT.
Definition TH1.cxx:735
virtual void SetStats(Bool_t stats=kTRUE)
Set statistics option on/off.
Definition TH1.cxx:8907
2-D histogram with a double per channel (see TH1 documentation)}
Definition TH2.h:301
The Histogram stack class.
Definition THStack.h:38
TList * GetHists() const
Definition THStack.h:70
virtual void Add(TH1 *h, Option_t *option="")
add a new histogram to the list Only 1-d and 2-d histograms currently supported.
Definition THStack.cxx:364
void Draw(Option_t *chopt="") override
Draw this multihist with its current attributes.
Definition THStack.cxx:449
Book space in a file, create I/O buffers, to fill them, (un)compress them.
Definition TKey.h:28
virtual const char * GetClassName() const
Definition TKey.h:75
Storage class for one entry of a TLegend.
This class displays a legend box (TPaveText) containing several legend entries.
Definition TLegend.h:23
TLegendEntry * AddEntry(const TObject *obj, const char *label="", Option_t *option="lpf")
Add a new entry to this legend.
Definition TLegend.cxx:317
void SetNColumns(Int_t nColumns)
Set the number of columns for the legend.
Definition TLegend.cxx:603
void SetMargin(Float_t margin)
Definition TLegend.h:69
Use the TLine constructor to create a simple line.
Definition TLine.h:22
virtual void SetY2(Double_t y2)
Definition TLine.h:68
virtual void SetX2(Double_t x2)
Definition TLine.h:66
Double_t GetY1() const
Definition TLine.h:52
virtual void SetX1(Double_t x1)
Definition TLine.h:65
virtual void SetY1(Double_t y1)
Definition TLine.h:67
Double_t GetY2() const
Definition TLine.h:53
A doubly linked list.
Definition TList.h:38
void Add(TObject *obj) override
Definition TList.h:81
TObject * At(Int_t idx) const override
Returns the object at position idx. Returns 0 if idx is out of range.
Definition TList.cxx:357
Int_t GetNrows() const
Int_t GetNcols() const
TMatrixTSym.
Definition TMatrixTSym.h:34
A TMultiGraph is a collection of TGraph (or derived) objects.
Definition TMultiGraph.h:34
virtual void Add(TGraph *graph, Option_t *chopt="")
Add a new graph to the list of graphs.
void Draw(Option_t *chopt="") override
Draw this multigraph with its current attributes.
The TNamed class is the base class for all named ROOT classes.
Definition TNamed.h:29
TObject * Clone(const char *newname="") const override
Make a clone of an object using the Streamer facility.
Definition TNamed.cxx:74
virtual void SetTitle(const char *title="")
Set the title of the TNamed.
Definition TNamed.cxx:164
const char * GetName() const override
Returns name of object.
Definition TNamed.h:47
const char * GetTitle() const override
Returns title of object.
Definition TNamed.h:48
void Clear(Option_t *option="") override
Set name and title to empty strings ("").
Definition TNamed.cxx:64
virtual void SetName(const char *name)
Set the name of the TNamed.
Definition TNamed.cxx:140
virtual void SetNameTitle(const char *name, const char *title)
Set all the TNamed parameters (name and title).
Definition TNamed.cxx:154
Mother of all ROOT objects.
Definition TObject.h:41
virtual void Inspect() const
Dump contents of this object in a graphics canvas.
Definition TObject.cxx:546
virtual const char * GetName() const
Returns name of object.
Definition TObject.cxx:439
R__ALWAYS_INLINE Bool_t TestBit(UInt_t f) const
Definition TObject.h:199
virtual TObject * Clone(const char *newname="") const
Make a clone of an object using the Streamer facility.
Definition TObject.cxx:223
virtual const char * ClassName() const
Returns name of class to which the object belongs.
Definition TObject.cxx:207
virtual void Warning(const char *method, const char *msgfmt,...) const
Issue warning message.
Definition TObject.cxx:973
virtual TObject * FindObject(const char *name) const
Must be redefined in derived classes.
Definition TObject.cxx:403
virtual void Delete(Option_t *option="")
Delete this object.
Definition TObject.cxx:248
void SetBit(UInt_t f, Bool_t set)
Set or unset the user status bits as specified in f.
Definition TObject.cxx:780
virtual Bool_t InheritsFrom(const char *classname) const
Returns kTRUE if object inherits from class "classname".
Definition TObject.cxx:525
virtual void Error(const char *method, const char *msgfmt,...) const
Issue error message.
Definition TObject.cxx:987
virtual const char * GetTitle() const
Returns title of object.
Definition TObject.cxx:483
virtual void Draw(Option_t *option="")
Default Draw method for all objects.
Definition TObject.cxx:274
virtual void Print(Option_t *option="") const
This method must be overridden when a class wants to print itself.
Definition TObject.cxx:636
@ kCanDelete
if object in a list can be deleted
Definition TObject.h:62
virtual void Info(const char *method, const char *msgfmt,...) const
Issue info message.
Definition TObject.cxx:961
The most important graphics class in the ROOT system.
Definition TPad.h:28
TVirtualPad * cd(Int_t subpadnumber=0) override
Set Current pad.
Definition TPad.cxx:597
A Pave (see TPave) with text, lines or/and boxes inside.
Definition TPaveText.h:21
virtual TText * AddText(Double_t x1, Double_t y1, const char *label)
Add a new Text line to this pavetext at given coordinates.
virtual void SetMargin(Float_t margin=0.05)
Definition TPaveText.h:62
virtual void SetY1NDC(Double_t y1)
Definition TPave.h:80
virtual void ConvertNDCtoPad()
Convert pave coordinates from NDC to Pad coordinates.
Definition TPave.cxx:139
virtual void SetName(const char *name="")
Definition TPave.h:75
virtual void SetBorderSize(Int_t bordersize=4)
Definition TPave.h:73
virtual void SetY2NDC(Double_t y2)
Definition TPave.h:81
Draw a Pie Chart,.
Definition TPie.h:23
void SetEntryVal(Int_t, Double_t)
Set the value of a slice.
Definition TPie.cxx:1250
Int_t GetEntries()
Definition TPie.h:76
void SetEntryFillColor(Int_t, Int_t)
Set the color for the slice "i".
Definition TPie.cxx:1226
void SetRadius(Double_t)
Set the pie chart's radius' value.
Definition TPie.cxx:1341
void SetEntryLabel(Int_t, const char *text="Slice")
Set slice number "i" label.
Definition TPie.cxx:1193
void Draw(Option_t *option="l") override
Draw the pie chart.
Definition TPie.cxx:277
This is the ROOT implementation of the Qt object communication mechanism (see also http://www....
Definition TQObject.h:48
Regular expression class.
Definition TRegexp.h:31
This class creates a ROOT object browser, constituted by three main tabs.
Stopwatch class.
Definition TStopwatch.h:28
Double_t RealTime()
Stop the stopwatch (if it is running) and return the realtime (in seconds) passed between the start a...
void Start(Bool_t reset=kTRUE)
Start the stopwatch.
void Stop()
Stop the stopwatch.
Provides iteration through tokens of a given string.
Definition TPRegexp.h:143
Bool_t NextToken()
Get the next token, it is stored in this TString.
Definition TPRegexp.cxx:975
Basic string class.
Definition TString.h:139
Ssiz_t Length() const
Definition TString.h:421
Bool_t IsDec() const
Returns true if all characters in string are decimal digits (0-9).
Definition TString.cxx:1918
void ToLower()
Change string to lower-case.
Definition TString.cxx:1170
Int_t Atoi() const
Return integer value of string.
Definition TString.cxx:1966
Bool_t EndsWith(const char *pat, ECaseCompare cmp=kExact) const
Return true if string ends with the specified string.
Definition TString.cxx:2222
Double_t Atof() const
Return floating-point value contained in string.
Definition TString.cxx:2032
TString & Replace(Ssiz_t pos, Ssiz_t n, const char *s)
Definition TString.h:694
const char * Data() const
Definition TString.h:380
TString & ReplaceAll(const TString &s1, const TString &s2)
Definition TString.h:704
Bool_t BeginsWith(const char *s, ECaseCompare cmp=kExact) const
Definition TString.h:627
Int_t CountChar(Int_t c) const
Return number of times character c occurs in the string.
Definition TString.cxx:508
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:2356
Bool_t Contains(const char *pat, ECaseCompare cmp=kExact) const
Definition TString.h:636
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:383
Int_t GetOptTitle() const
Definition TStyle.h:244
Float_t GetLabelSize(Option_t *axis="X") const
Return label size.
Definition TStyle.cxx:1141
Float_t GetPadRightMargin() const
Definition TStyle.h:212
Style_t GetTitleFont(Option_t *axis="X") const
Return title font.
Definition TStyle.cxx:1212
Bool_t GetHistMinimumZero() const
Definition TStyle.h:235
Float_t GetPadLeftMargin() const
Definition TStyle.h:211
Float_t GetTitleYSize() const
Definition TStyle.h:277
static TClass * Class()
Double_t GetHistTopMargin() const
Definition TStyle.h:236
Float_t GetPadBottomMargin() const
Definition TStyle.h:209
const char * GetPaintTextFormat() const
Definition TStyle.h:248
Float_t GetPadTopMargin() const
Definition TStyle.h:210
virtual Bool_t ExpandPathName(TString &path)
Expand a pathname getting rid of special shell characters like ~.
Definition TSystem.cxx:1261
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:1283
virtual Bool_t ProcessEvents()
Process pending events (GUI, timers, sockets).
Definition TSystem.cxx:403
The TTimeStamp encapsulates seconds and ns since EPOCH.
Definition TTimeStamp.h:45
void Add(const TTimeStamp &offset)
Add "offset" as a delta time.
const char * AsString(const Option_t *option="") const
Return the date & time as a string.
This class defines a UUID (Universally Unique IDentifier), also known as GUIDs (Globally Unique IDent...
Definition TUUID.h:42
const char * AsString() const
Return UUID as string. Copy string immediately since it will be reused.
Definition TUUID.cxx:571
static TVirtualPadEditor * GetPadEditor(Bool_t load=kTRUE)
Returns the pad editor dialog. Static method.
TVirtualPad is an abstract base class for the Pad and Canvas classes.
Definition TVirtualPad.h:51
virtual void Modified(Bool_t flag=1)=0
virtual TList * GetListOfPrimitives() const =0
virtual void SetPad(const char *name, const char *title, Double_t xlow, Double_t ylow, Double_t xup, Double_t yup, Color_t color=35, Short_t bordersize=5, Short_t bordermode=-1)=0
virtual TVirtualPad * cd(Int_t subpadnumber=0)=0
void Draw(Option_t *option="") override=0
Default Draw method for all objects.
const char * GetName() const override=0
Returns name of object.
virtual TVirtualPad * GetPad(Int_t subpadnumber) const =0
virtual void Divide(Int_t nx=1, Int_t ny=1, Float_t xmargin=0.01, Float_t ymargin=0.01, Int_t color=0)=0
virtual Int_t GetLogy() const =0
virtual void SetLogy(Int_t value=1)=0
virtual TObject * GetPrimitive(const char *name) const =0
virtual void SetBorderSize(Short_t bordersize)=0
virtual void SetName(const char *name)=0
TClass * IsA() const override
double evaluate() const override
Evaluate projected p.d.f.
virtual 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 GlobalObservables(Args_t &&... argsOrArgSet)
RooCmdArg Range(const char *rangeName, bool adjustNorm=true)
const Double_t sigma
Double_t y[n]
Definition legend1.C:17
Double_t x[n]
Definition legend1.C:17
const Int_t n
Definition legend1.C:16
TGraphErrors * gr
Definition legend1.C:25
TH1F * h1
Definition legend1.C:5
#define F(x, y, z)
bool EndsWith(const std::string &theString, const std::string &theSubstring)
TClass * GetClass(T *)
Definition TClass.h:663
The namespace RooFit contains mostly switches that change the behaviour of functions of PDFs (or othe...
Definition JSONIO.h:26
MsgLevel
Verbosity level for RooMsgService::StreamConfig in RooMsgService.
@ NumIntegration
Double_t Prob(Double_t chi2, Int_t ndf)
Computation of the probability for a certain Chi-squared (chi2) and number of degrees of freedom (ndf...
Definition TMath.cxx:637
Double_t ChisquareQuantile(Double_t p, Double_t ndf)
Evaluate the quantiles of the chi-squared probability distribution function.
Definition TMath.cxx:2189
Definition first.py:1
Definition graph.py:1
#define END_XROOFIT_NAMESPACE
Definition Config.h:25
static const char * what
Definition stlLoader.cc:6
RooAbsBinning * b
RooRealVar * x
void removeTopic(RooFit::MsgTopic oldTopic)
bool isNaNWithPayload() const
Test if this struct has a float packed into its mantissa.
static float unpackNaN(double val)
If val is NaN and a this NaN has been tagged as containing a payload, unpack the float from the manti...
th1 Draw()
TLine lv
Definition textalign.C:5
TLine l
Definition textangle.C:4
auto * tt
Definition textangle.C:16
static uint64_t sum(uint64_t i)
Definition Factory.cxx:2345
#define dest(otri, vertexptr)
Definition triangle.c:1041
#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:109
auto & GETWSSNAPSHOTS(RooWorkspace *w)
Definition xRooNode.cxx:105
bool TopRightPlaceBox(TPad *p, TObject *o, double w, double h, double &xl, double &yb)
#define GETDMP(o, m)
Definition xRooNode.cxx:121
BEGIN_XROOFIT_NAMESPACE
Definition xRooNode.cxx:212
TPaveText * getPave(const char *name="labels", bool create=true, bool doPaint=false)
auto GETROOTDIR(TGFileBrowser *b)
Definition xRooNode.cxx:113
const xRooNode * runningNode
auto GETLISTTREE(TGFileBrowser *b)
Definition xRooNode.cxx:117
const T & _or_func(const T &a, const T &b)
Definition xRooNode.cxx:223
TLegend * getLegend(bool create=true, bool doPaint=false)