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((static_cast<TKey *>(k))->GetClassName());
295 if (cl == RooWorkspace::Class() || cl->InheritsFrom("RooWorkspace")) {
296 fComp.reset(_file->Get<RooWorkspace>(k->GetName()), [](TObject *ws) {
297 // memory leak in workspace, some RooLinkedLists aren't cleared, fixed in ROOT 6.28
298 if (ws) {
299#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
300 dynamic_cast<RooWorkspace *>(ws)->_embeddedDataList.Delete();
301#endif
302 xRooNode(*ws, std::make_shared<xRooNode>()).sterilize();
303 delete ws;
304 }
305 });
306 if (fComp) {
307 TNamed::SetNameTitle(fComp->GetName(), fComp->GetTitle());
308 fParent = std::make_shared<xRooNode>(
309 _file); // keep file alive - seems necessary to save workspace again in some cases
310 break;
311 }
312 }
313 }
314 }
315 }
316 } else if (pathName.EndsWith(".root") || pathName.EndsWith(".json")) {
317 throw std::runtime_error(TString::Format("%s does not exist", name));
318 }
319 }
320
321 if (auto _ws = get<RooWorkspace>(); _ws && (!parent || parent->get<TFile>())) {
324 .removeTopic(RooFit::NumIntegration); // stop info message every time
325
326 // check if any of the open files have version numbers greater than our major version
327 // may not read correctly
328 for (auto f : *gROOT->GetListOfFiles()) {
329 if ((dynamic_cast<TFile *>(f)->GetVersion() / 100) > (gROOT->GetVersionInt() / 100)) {
330 Warning("xRooNode", "There is file open with version %d > current version %d ... results may be wrong",
331 dynamic_cast<TFile *>(f)->GetVersion(), gROOT->GetVersionInt());
332 }
333 }
334
335 // use the datasets if any to 'mark' observables
336 int checkCount = 0;
337 for (auto &d : _ws->allData()) {
338 for (auto &a : *d->get()) {
339 if (auto v = _ws->var(a->GetName()); v) {
340 v->setAttribute("obs");
341 } else if (auto c = _ws->cat(a->GetName()); c) {
342 c->setAttribute("obs");
343 }
344 }
345 // count how many ds are checked ... if none are checked will check the first
346 checkCount += d->TestBit(1 << 20);
347 }
348
349 if (checkCount == 0 && !_ws->allData().empty())
350 _ws->allData().back()->SetBit(1 << 20, true);
351
352 if (auto _set = dynamic_cast<RooArgSet *>(GETWSSNAPSHOTS(_ws).find("NominalParamValues")); _set) {
353 for (auto s : *_set) {
354 if (auto v = dynamic_cast<RooRealVar *>(s); v) {
355 _ws->var(s->GetName())->setStringAttribute("nominal", TString::Format("%f", v->getVal()));
356 }
357 }
358 }
359
360 // also flag global observables ... relies on ModelConfig existences
361 RooArgSet _allGlobs;
362 for (auto &[k, v] : GETWSSETS(_ws)) {
363 if (k == "globalObservables" || TString(k).EndsWith("_GlobalObservables")) {
364 for (auto &s : v) {
365 _allGlobs.add(*s);
366 s->setAttribute("obs");
367 s->setAttribute("global");
368 }
369 } else if (TString(k).EndsWith("_Observables")) {
370 const_cast<RooArgSet &>(v).setAttribAll("obs");
371 } else if (TString(k).EndsWith("_POI")) {
372 for (auto &s : v) {
373 s->setAttribute("poi");
374 auto _v = dynamic_cast<RooRealVar *>(s);
375 if (!_v)
376 continue;
377 // if (!_v->hasRange("physical")) {
378 // _v->setRange("physical", 0, std::numeric_limits<double>::infinity());
379 // // ensure range of poi is also straddling 0
380 // if (_v->getMin() >= 0)
381 // _v->setMin(-1e-5);
382 // }
383 }
384 } else if (TString(k).EndsWith("_NuisParams")) {
385 const_cast<RooArgSet &>(v).setAttribAll("np");
386 }
387 }
388 if (!_allGlobs.empty() && GETWSSETS(_ws).count("globalObservables") == 0) {
389 _ws->defineSet("globalObservables", _allGlobs);
390 }
391
392 // now check if any pars don't have errors defined (not same as error=0) ... if so, use the first pdf (if there is
393 // one) to try setting values from
394 if (!_ws->allPdfs().empty()) {
395 std::set<RooRealVar *> noErrorPars;
396 std::string parNames;
397 for (auto &p : np()) { // infer errors on all floating non-poi parameters
398 auto v = p->get<RooRealVar>();
399 if (!v)
400 continue;
401 if (!v->hasError()) {
402 noErrorPars.insert(v);
403 if (!parNames.empty())
404 parNames += ",";
405 parNames += v->GetName();
406 }
407 }
408 if (!noErrorPars.empty()) {
409 Warning(
410 "xRooNode",
411 "Inferring initial errors of %d parameters (give all nuisance parameters an error to avoid this msg)",
412 int(noErrorPars.size()));
413 // get the first top-level pdf
414 browse();
415 for (auto &a : *this) {
416 if (a->fFolder == "!models") {
417 try {
418 auto fr = a->floats().reduced(parNames).fitResult("prefit");
419 if (auto _fr = fr.get<RooFitResult>(); _fr) {
420 for (auto &v : noErrorPars) {
421 if (auto arg = dynamic_cast<RooRealVar *>(_fr->floatParsFinal().find(v->GetName()));
422 arg && arg->hasError()) {
423 v->setError(arg->getError());
424 }
425 }
426 }
427 } catch (...) {
428 }
429 }
430 }
431 }
432 }
433 }
434
435 if (strlen(GetTitle()) == 0) {
436 if (fComp) {
437 TNamed::SetTitle(fComp->GetTitle());
438 } else {
439 TNamed::SetTitle(GetName());
440 }
441 }
442}
443
444xRooNode::xRooNode(const TObject &comp, const std::shared_ptr<xRooNode> &parent)
445 : xRooNode(/*[](const TObject& c) {
446c.InheritsFrom("RooAbsArg");
447if (s) {
448return (s->getStringAttribute("alias")) ? s->getStringAttribute("alias") : c.GetName();
449}
450return c.GetName();
451}(comp)*/
452 (comp.InheritsFrom("RooAbsArg") && dynamic_cast<const RooAbsArg *>(&comp)->getStringAttribute("alias"))
453 ? dynamic_cast<const RooAbsArg *>(&comp)->getStringAttribute("alias")
454 : comp.GetName(),
455 std::shared_ptr<TObject>(const_cast<TObject *>(&comp), [](TObject *) {}), parent)
456{
457}
458
459xRooNode::xRooNode(const std::shared_ptr<TObject> &comp, const std::shared_ptr<xRooNode> &parent)
460 : xRooNode(
461 [&]() {
462 if (auto a = std::dynamic_pointer_cast<RooAbsArg>(comp); a && a->getStringAttribute("alias"))
463 return a->getStringAttribute("alias");
464 if (comp)
465 return comp->GetName();
466 return "";
467 }(),
468 comp, parent)
469{
470}
471
473
474void xRooNode::Checked(TObject *obj, bool val)
475{
476 if (obj != this)
477 return;
478
479 // cycle through states:
480 // unhidden and selected: tick, no uline
481 // hidden and unselected: notick, uline
482 // unhidden and unselected: tick, uline
483 if (auto o = get<RooAbsReal>(); o) {
484 if (o->isSelectedComp() && !val) {
485 // deselecting and hiding
486 o->selectComp(val);
487 o->setAttribute("hidden");
488 } else if (!o->isSelectedComp() && !val) {
489 // selecting
490 o->selectComp(!val);
491 } else if (val) {
492 // unhiding but keeping unselected
493 o->setAttribute("hidden", false);
494 }
495 auto item = GetTreeItem(nullptr);
496 item->CheckItem(!o->getAttribute("hidden"));
497 if (o->isSelectedComp()) {
498 item->ClearColor();
499 } else {
500 item->SetColor(kGray);
501 }
502 return;
503 }
504
505 if (auto o = get(); o) {
506 // if (o->TestBit(1<<20)==val) return; // do nothing
507 o->SetBit(1 << 20, val); // TODO: check is 20th bit ok to play with?
508 if (auto fr = get<RooFitResult>(); fr) {
509 if (auto _ws = ws(); _ws) {
510 if (val) {
511 // ensure fit result is in genericObjects list ... if not, add a copy ...
512 if (!_ws->genobj(fr->GetName())) {
513 _ws->import(*fr);
514 if (auto wfr = dynamic_cast<RooFitResult *>(_ws->genobj(fr->GetName()))) {
515 fr = wfr;
516 }
517 }
518 RooArgSet _allVars = _ws->allVars();
519 _allVars = fr->floatParsFinal();
520 _allVars = fr->constPars();
521 for (auto &i : fr->floatParsInit()) {
522 auto v = dynamic_cast<RooRealVar *>(_allVars.find(i->GetName()));
523 if (v)
524 v->setStringAttribute("initVal", TString::Format("%f", dynamic_cast<RooRealVar *>(i)->getVal()));
525 }
526 // uncheck all other fit results
527 for (auto oo : _ws->allGenericObjects()) {
528 if (auto ffr = dynamic_cast<RooFitResult *>(oo); ffr && ffr != fr) {
529 ffr->ResetBit(1 << 20);
530 }
531 }
532 } else
533 _ws->allVars() = fr->floatParsInit();
534 }
535 if (auto item = GetTreeItem(nullptr); item) {
536 // update check marks on siblings
537 if (auto first = item->GetParent()->GetFirstChild()) {
538 do {
539 if (first->HasCheckBox()) {
540 auto _obj = static_cast<xRooNode *>(first->GetUserData());
541 first->CheckItem(_obj->get() && _obj->get()->TestBit(1 << 20));
542 }
543 } while ((first = first->GetNextSibling()));
544 }
545 }
546 }
547 }
548}
549
551{
552 static bool blockBrowse = false;
553 if (blockBrowse)
554 return;
555 if (b == nullptr) {
556 auto b2 = dynamic_cast<TBrowser *>(gROOT->GetListOfBrowsers()->Last());
557 if (!b2 || !b2->GetBrowserImp()) { // no browser imp if browser was closed
558 blockBrowse = true;
559 gEnv->SetValue("X11.UseXft", "no"); // for faster x11
560 gEnv->SetValue("X11.Sync", "no");
561 gEnv->SetValue("X11.FindBestVisual", "no");
562 gEnv->SetValue("Browser.Name", "TRootBrowser"); // forces classic root browser (in 6.26 onwards)
563 gEnv->SetValue("Canvas.Name", "TRootCanvas");
564 b2 = new TBrowser("nodeBrowser", this, "RooFit Browser");
565 blockBrowse = false;
566 } else if (strcmp(b2->GetName(), "nodeBrowser") == 0) {
567 blockBrowse = true;
568 b2->BrowseObject(this);
569 blockBrowse = false;
570 } else {
571 auto _b = dynamic_cast<TGFileBrowser *>(GETACTBROWSER(dynamic_cast<TRootBrowser *>(b2->GetBrowserImp())));
572 if (_b)
573 _b->AddFSDirectory("Workspaces", nullptr, "SetRootDir");
574 /*auto l = Node2::Class()->GetMenuList();
575 auto o = new CustomClassMenuItem(TClassMenuItem::kPopupUserFunction,Node2::Class(),
576 "blah blah blah","BlahBlah",0,"Option_t*",-1,true);
577 //o->SetCall(o,"BlahBlah","Option_t*",-1);
578 l->AddFirst(o);*/
579 // b->BrowseObject(this);
580 _b->GotoDir(nullptr);
581 _b->Add(this, GetName());
582 // b->Add(this);
583 }
584 return;
585 }
586
587 if (auto item = GetTreeItem(b); item) {
588 if (!item->IsOpen() && IsFolder())
589 return; // no need to rebrowse if closing
590 // update check marks on any child items
591 if (auto first = item->GetFirstChild()) {
592 do {
593 if (first->HasCheckBox()) {
594 auto _obj = static_cast<xRooNode *>(first->GetUserData());
595 first->CheckItem(_obj->get() &&
596 (_obj->get()->TestBit(1 << 20) ||
597 (_obj->get<RooAbsArg>() && !_obj->get<RooAbsArg>()->getAttribute("hidden"))));
598 }
599 } while ((first = first->GetNextSibling()));
600 }
601 }
602
603 browse();
604
605 // for top-level pdfs default to having the .vars browsable too
606 if (get<RooAbsPdf>() && fFolder == "!models" && !_IsShowVars_()) {
607 fBrowsables.push_back(std::make_shared<xRooNode>(vars()));
608 }
609
610 if (auto _fr = get<RooFitResult>(); _fr && fBrowsables.empty()) {
611 // have some common drawing options
612 fBrowsables.push_back(std::make_shared<xRooNode>(".Draw(\"pull\")", nullptr, *this));
613 fBrowsables.push_back(std::make_shared<xRooNode>(".Draw(\"corrcolztext\")", nullptr, *this));
614 if (std::unique_ptr<RooAbsCollection>(_fr->floatParsFinal().selectByAttrib("poi", true))->size() == 1) {
615 fBrowsables.push_back(std::make_shared<xRooNode>(".Draw(\"impact\")", nullptr, *this));
616 }
617 }
618
619 if (empty() && fBrowsables.empty()) {
620 try {
621 if (auto s = get<TStyle>()) {
622 s->SetFillAttributes();
623 if (auto ed = dynamic_cast<TGedEditor *>(TVirtualPadEditor::GetPadEditor())) {
624 ed->SetModel(gPad, s, kButton1Down, true);
625 }
626 } else if (TString(GetName()).BeginsWith(".Draw(\"") && fParent) {
627 fParent->Draw(TString(TString(GetName())(7, strlen(GetName()) - 9)) + b->GetDrawOption());
628 } else {
629 Draw(b->GetDrawOption());
630 }
631 } catch (const std::exception &e) {
632 new TGMsgBox(
633 gClient->GetRoot(),
634 (gROOT->GetListOfBrowsers()->At(0))
635 ? dynamic_cast<TGWindow *>(static_cast<TBrowser *>(gROOT->GetListOfBrowsers()->At(0))->GetBrowserImp())
636 : gClient->GetRoot(),
637 "Exception", e.what(),
638 kMBIconExclamation); // deletes self on dismiss?
639 }
640 }
641
642 bool hasFolders = false;
643 if (strlen(GetName()) > 0 && GetName()[0] != '!') { // folders don't have folders
644 for (auto &c : *this) {
645 if (!c->fFolder.empty()) {
646 hasFolders = true;
647 break;
648 }
649 }
650 }
651 // auto _ws = get<RooWorkspace>();
652 if (/*_ws*/ hasFolders) {
653 // organize in folders
654 auto _folders = find(".folders");
655 if (!_folders) {
656 _folders = emplace_back(std::make_shared<xRooNode>(".folders", nullptr, *this));
657 }
658 // ensure entry in folders for every folder type ...
659 for (auto &v : *this) {
660 if (!v->fFolder.empty() && !_folders->find(v->fFolder, false)) {
661 _folders->emplace_back(std::make_shared<xRooNode>(v->fFolder.c_str(), nullptr, *this));
662 }
663 }
664 // now just add all the folders
665 for (auto &v : *_folders) {
666 TString _name = v->GetName();
667 if (_name.BeginsWith('!'))
668 _name = _name(1, _name.Length()); // strip ! from display
669 b->Add(v.get(), _name);
670 }
671 }
672
673 for (auto &v : *this) {
674 if (hasFolders && !v->fFolder.empty())
675 continue; // in the folders
676 if (strcmp(v->GetName(), ".folders") == 0)
677 continue; // never 'browse' the folders property
678 auto _fr = v->get<RooFitResult>();
679 int _checked = (v->get<RooAbsData>() || _fr) ? v->get()->TestBit(1 << 20) : -1;
680 if (_fr && ((_fr->status() == 0 && _fr->numStatusHistory() == 0) || (_fr->floatParsFinal().empty()))) {
681 // this is a "PARTIAL" fit result ... don't allow it to be selected
682 _checked = -1;
683 }
684 if (v->get<RooAbsPdf>() && get<RooSimultaneous>())
685 _checked = !v->get<RooAbsArg>()->getAttribute("hidden");
686 TString _name = v->GetName();
687 if (v->get() && _name.BeginsWith(TString(v->get()->ClassName()) + "::")) {
688 _name = _name(strlen(v->get()->ClassName()) + 2, _name.Length());
689 }
690 if (_name.BeginsWith(".")) {
691 // property node -- display the name of the contained object
692 if (v->get()) {
693 _name = TString::Format("%s: %s::%s", _name.Data(), v->get()->ClassName(),
694 (v->get<RooAbsArg>() && v->get<RooAbsArg>()->getStringAttribute("alias"))
695 ? v->get<RooAbsArg>()->getStringAttribute("alias")
696 : v->get()->GetName());
697 }
698 } else if (v->get() && !v->get<TFile>() && !TString(v->GetName()).BeginsWith('/'))
699 _name = TString::Format("%s::%s", v->get()->ClassName(), _name.Data());
700 if (auto _type = v->GetNodeType(); strlen(_type)) {
701 // decided not to show const values until figure out how to update if value changes
702 /*if (TString(_type)=="Const") _name += TString::Format(" [%s=%g]",_type,v->get<RooConstVar>()->getVal());
703 else*/
704 _name += TString::Format(" [%s]", _type);
705 }
706 if (auto fv = v->get<RooFormulaVar>()) {
707 TString formu = TString::Format(" [%s]", fv->expression());
708 for (size_t i = 0; i < fv->dependents().size(); i++) {
709 formu.ReplaceAll(TString::Format("x[%zu]", i), fv->dependents()[i].GetName());
710 }
711 _name += formu;
712 } else if (auto gv = v->get<RooGenericPdf>()) {
713 TString formu = TString::Format(" [%s]", gv->expression());
714 for (size_t i = 0; i < gv->dependents().size(); i++) {
715 formu.ReplaceAll(TString::Format("x[%zu]", i), gv->dependents()[i].GetName());
716 }
717 _name += formu;
718 }
719 // tool tip defaults to displaying name and title, so temporarily set name to obj name if has one
720 // and set title to the object type
721 TString nameSave(v->TNamed::GetName());
722 TString titleSave(v->TNamed::GetTitle());
723 if (auto o = v->get(); o)
724 v->TNamed::SetNameTitle(o->GetName(), o->ClassName());
725 b->Add(v.get(), _name, _checked);
726 if (auto o = v->get(); o)
727 v->TNamed::SetNameTitle(nameSave, titleSave);
728 if (_checked != -1) {
729 dynamic_cast<TQObject *>(b->GetBrowserImp())
730 ->Connect("Checked(TObject *, bool)", ClassName(), v.get(), "Checked(TObject *, bool)");
731 }
732 if (_fr) {
733 if (_fr->status() || _fr->covQual() != 3) { // snapshots or bad fits
734 v->GetTreeItem(b)->SetColor((_fr->numStatusHistory() || _fr->floatParsFinal().empty()) ? kRed : kBlue);
735 } else if (_fr->numStatusHistory() == 0) { // partial fit result ..
736 v->GetTreeItem(b)->SetColor(kGray);
737 }
738 }
739 if ((v->fFolder == "!np" || v->fFolder == "!poi")) {
740 if (v->get<RooAbsArg>()->getAttribute("Constant")) {
741 v->GetTreeItem(b)->SetColor(kGray);
742 } else
743 v->GetTreeItem(b)->ClearColor();
744 }
745 if (auto _htr = v->get<RooStats::HypoTestResult>(); _htr) {
746 // check for fit statuses
747 if (auto fits = _htr->GetFitInfo()) {
748 for (int i = 0; i < fits->numEntries(); i++) {
749 // if any fit (other than a genFit) is bad, flag point as bad
750 if (fits->get(i)->getCatIndex("type") != 5 && fits->get(i)->getRealValue("status") != 0) {
751 v->GetTreeItem(b)->SetColor(kRed);
752 break;
753 }
754 }
755 } else {
756 v->GetTreeItem(b)->SetColor(kBlue); // unknown fit status
757 }
758 }
759
760 // v.fBrowsers.insert(b);
761 }
762
763 // for pdfs, check for datasets too and add to list
764 /*if (get<RooAbsPdf>()) {
765 auto dsets = datasets();
766 if (!dsets.empty()) {
767 // check if already have .datasets() in browsables
768 bool found(false);
769 for(auto& p : fBrowsables) {
770 if (TString(p->GetName())==".datasets()") {found=true;
771 // add
772 break;
773 }
774 }
775 if (!found) {
776 fBrowsables.push_back(std::make_shared<xRooNode>(dsets));
777 }
778 }
779 }*/
780 // browse the browsables too
781 for (auto &v : fBrowsables) {
782 TString _name = v->GetName();
783 if (_name == ".memory")
784 continue; // hide the memory from browsing, if put in browsables
785 TString nameSave(v->TNamed::GetName());
786 TString titleSave(v->TNamed::GetTitle());
787 if (auto o = v->get(); o)
788 v->TNamed::SetNameTitle(o->GetName(), o->ClassName());
789 b->Add(v.get(), _name, -1);
790 if (auto o = v->get(); o)
791 v->TNamed::SetNameTitle(nameSave, titleSave);
792 }
793
794 b->SetSelected(this);
795}
796
798{
799 if (!set) {
800 // can't remove as causes a crash, need to remove from the browser first
801 /*for(auto itr = fBrowsables.begin(); itr != fBrowsables.end(); ++itr) {
802 if (strcmp((*itr)->GetName(),".vars")==0) {
803 fBrowsables.erase(itr);
804 }
805 }*/
806 } else {
807 auto v = std::make_shared<xRooNode>(vars());
808 fBrowsables.push_back(v);
809 if (auto l = GetListTree(nullptr)) {
810 l->AddItem(GetTreeItem(nullptr), v->GetName(), v.get());
811 }
812 }
813}
814
816{
817 for (auto &b : fBrowsables) {
818 if (strcmp(b->GetName(), ".vars") == 0)
819 return true;
820 }
821 return false;
822}
823
825{
826 if (strlen(GetName()) > 0 && GetName()[0] == '!')
827 return true;
828 if (strlen(GetName()) > 0 && GetName()[0] == '.' && !(TString(GetName()).BeginsWith(".Draw(\"")))
829 return true;
830 if (empty())
831 const_cast<xRooNode *>(this)->browse();
832 return !empty();
833}
834
835class Axis2 : public TAxis {
836
837public:
838 using TAxis::TAxis;
839 double GetBinWidth(Int_t bin) const override
840 {
841 if (auto v = var(); v)
842 return v->getBinWidth(bin - 1, GetName());
843 return 1;
844 }
845 double GetBinLowEdge(Int_t bin) const override
846 {
847 if (auto v = rvar(); v) {
848 return (bin == v->getBinning(GetName()).numBins() + 1) ? v->getBinning(GetName()).binHigh(bin - 2)
849 : v->getBinning(GetName()).binLow(bin - 1);
850 }
851 return bin - 1;
852 }
853 double GetBinUpEdge(Int_t bin) const override
854 {
855 if (auto v = rvar(); v)
856 return (bin == 0) ? v->getBinning(GetName()).binLow(bin) : v->getBinning(GetName()).binHigh(bin - 1);
857 return bin;
858 }
859
860 const char *GetTitle() const override
861 {
862 return (binning() && strlen(binning()->GetTitle())) ? binning()->GetTitle() : GetParent()->GetTitle();
863 }
864 void SetTitle(const char *title) override
865 {
866 if (binning()) {
867 const_cast<RooAbsBinning *>(binning())->SetTitle(title);
868 } else {
869 dynamic_cast<TNamed *>(GetParent())->SetTitle(title);
870 }
871 }
872
873 void Set(Int_t nbins, const double *xbins) override
874 {
875 if (auto v = dynamic_cast<RooRealVar *>(rvar()))
876 v->setBinning(RooBinning(nbins, xbins), GetName());
877 TAxis::Set(nbins, xbins);
878 }
879 void Set(Int_t nbins, const float *xbins) override
880 {
881 std::vector<double> bins(nbins + 1);
882 for (int i = 0; i <= nbins; i++)
883 bins.at(i) = xbins[i];
884 return Set(nbins, &bins[0]);
885 }
886 void Set(Int_t nbins, double xmin, double xmax) override
887 {
888 if (auto v = dynamic_cast<RooRealVar *>(rvar()))
889 v->setBinning(RooUniformBinning(xmin, xmax, nbins), GetName());
890 TAxis::Set(nbins, xmin, xmax);
891 }
892
893 const RooAbsBinning *binning() const { return var()->getBinningPtr(GetName()); }
894
895 Int_t FindFixBin(const char *label) const override { return TAxis::FindFixBin(label); }
896 Int_t FindFixBin(double x) const override { return (binning()) ? (binning()->binNumber(x) + 1) : x; }
897
898private:
899 RooAbsLValue *var() const { return dynamic_cast<RooAbsLValue *>(GetParent()); }
900 RooAbsRealLValue *rvar() const { return dynamic_cast<RooAbsRealLValue *>(GetParent()); }
901};
902
903std::shared_ptr<TObject> xRooNode::getObject(const std::string &name, const std::string &type) const
904{
905 // if (fParent) return fParent->getObject(name);
906
907 if (auto _owned = find(".memory"); _owned) {
908 for (auto &o : *_owned) {
909 if (name == o->GetName()) {
910 if (type.empty() || o->get()->InheritsFrom(type.c_str()))
911 return o->fComp;
912 }
913 }
914 }
915
916 // see if have a provider
917 auto _provider = fProvider;
918 auto _parent = fParent;
919 while (!_provider && _parent) {
920 _provider = _parent->fProvider;
921 _parent = _parent->fParent;
922 }
923 if (_provider)
924 return _provider->getObject(name, type);
925
926 if (ws()) {
927 std::shared_ptr<TObject> out;
928 if (auto arg = ws()->arg(name.c_str()); arg) {
929 auto _tmp = std::shared_ptr<TObject>(arg, [](TObject *) {});
930 if (!type.empty() && arg->InheritsFrom(type.c_str()))
931 return _tmp;
932 if (!out)
933 out = _tmp;
934 }
935 if (auto arg = ws()->data(name.c_str()); arg) {
936 auto _tmp = std::shared_ptr<TObject>(arg, [](TObject *) {});
937 if (!type.empty() && arg->InheritsFrom(type.c_str()))
938 return _tmp;
939 if (!out)
940 out = _tmp;
941 }
942 if (auto arg = ws()->genobj(name.c_str()); arg) {
943 auto _tmp = std::shared_ptr<TObject>(arg, [](TObject *) {});
944 if (!type.empty() && arg->InheritsFrom(type.c_str()))
945 return _tmp;
946 if (!out)
947 out = _tmp;
948 }
949 if (auto arg = ws()->embeddedData(name.c_str()); arg) {
950 auto _tmp = std::shared_ptr<TObject>(arg, [](TObject *) {});
951 if (!type.empty() && arg->InheritsFrom(type.c_str()))
952 return _tmp;
953 if (!out)
954 out = _tmp;
955 }
956 if (auto arg = GETWSSNAPSHOTS(ws()).find(name.c_str()); arg) {
957 auto _tmp = std::shared_ptr<TObject>(arg, [](TObject *) {});
958 if (!type.empty() && arg->InheritsFrom(type.c_str()))
959 return _tmp;
960 if (!out)
961 out = _tmp;
962 }
963 return out;
964 }
965 return nullptr;
966}
967
969{
970 if (fXAxis) {
971 // check if num bins needs update or not
972 if (auto cat = dynamic_cast<RooAbsCategory *>(fXAxis->GetParent());
973 cat && cat->numTypes() != fXAxis->GetNbins()) {
974 fXAxis.reset();
975 } else {
976 return fXAxis.get();
977 }
978 }
979 RooAbsLValue *x = nullptr;
980 if (auto a = get<RooAbsArg>(); a && a->isFundamental())
981 x = dynamic_cast<RooAbsLValue *>(a); // self-axis
982
983 auto _parentX = (!x && fParent && !fParent->get<RooSimultaneous>()) ? fParent->GetXaxis() : nullptr;
984
985 auto o = get<RooAbsReal>();
986 if (!o)
987 return _parentX;
988
989 if (auto xName = o->getStringAttribute("xvar"); xName) {
990 x = dynamic_cast<RooAbsLValue *>(getObject(xName).get());
991 }
992
993 // if xvar has become set equal to an arg and this is a pdf, we will allow a do-over
994 if (!x) {
995 // need to choose from dependent fundamentals, in following order:
996 // parentX (if not a glob), robs, globs, vars, args
997
998 if (_parentX && !dynamic_cast<RooAbsArg *>(_parentX->GetParent())->getAttribute("global") &&
999 (o->dependsOn(*dynamic_cast<RooAbsArg *>(_parentX->GetParent())) || vars().empty())) {
1000 x = dynamic_cast<RooAbsLValue *>(_parentX->GetParent());
1001 } else if (auto _obs = obs(); !_obs.empty()) {
1002 for (auto &v : _obs) {
1003 if (!v->get<RooAbsArg>()->getAttribute("global")) {
1004 x = v->get<RooAbsLValue>();
1005 if (x)
1006 break;
1007 } else if (!x) {
1008 x = v->get<RooAbsLValue>();
1009 }
1010 }
1011 } else if (auto _pars = pars(); !_pars.empty()) {
1012 for (auto &v : _pars) {
1013 if (!v->get<RooAbsArg>()->getAttribute("Constant")) {
1014 x = v->get<RooAbsLValue>();
1015 if (x)
1016 break;
1017 } else if (!x) {
1018 x = v->get<RooAbsLValue>();
1019 }
1020 }
1021 }
1022
1023 if (!x) {
1024 return nullptr;
1025 }
1026 }
1027
1028 if (o != dynamic_cast<TObject *>(x)) {
1029 o->setStringAttribute("xvar", dynamic_cast<TObject *>(x)->GetName());
1030 }
1031
1032 // decide binning to use
1033 TString binningName = o->getStringAttribute("binning");
1034 auto _bnames = x->getBinningNames();
1035 bool hasBinning = false;
1036 for (auto &b : _bnames) {
1037 if (b == binningName) {
1038 hasBinning = true;
1039 break;
1040 }
1041 }
1042 if (!hasBinning) {
1043 // doesn't have binning, so clear binning attribute
1044 // this can happen after Combine of models because binning don't get combined yet (should fix this)
1045 Warning("GetXaxis", "Binning %s not defined on %s - clearing", binningName.Data(),
1046 dynamic_cast<TObject *>(x)->GetName());
1047 o->setStringAttribute("binning", nullptr);
1048 binningName = "";
1049 }
1050
1051 if (binningName == "" && o != dynamic_cast<TObject *>(x)) {
1052 // has var has a binning matching this nodes name then use that
1053 auto __bnames = x->getBinningNames();
1054 for (auto &b : __bnames) {
1055 if (b == GetName())
1056 binningName = GetName();
1057 if (b == o->GetName()) {
1058 binningName = o->GetName();
1059 break;
1060 } // best match
1061 }
1062 if (binningName == "") {
1063 // if we are binned in this var then will define that as a binning
1064 if (/*o->isBinnedDistribution(*dynamic_cast<RooAbsArg *>(x))*/
1065 auto bins = _or_func(
1066 /*o->plotSamplingHint(*dynamic_cast<RooAbsRealLValue
1067 *>(x),-std::numeric_limits<double>::infinity(),std::numeric_limits<double>::infinity())*/
1068 (std::list<double> *)(nullptr),
1069 o->binBoundaries(*dynamic_cast<RooAbsRealLValue *>(x), -std::numeric_limits<double>::infinity(),
1070 std::numeric_limits<double>::infinity()));
1071 bins) {
1072 std::vector<double> _bins;
1073 for (auto &b : *bins) {
1074 if (_bins.empty() || std::abs(_bins.back() - b) > 1e-5 * _bins.back())
1075 _bins.push_back(b);
1076 }
1077 fXAxis = std::make_shared<Axis2>(_bins.size() - 1, &_bins[0]);
1078 // add this binning to the var to avoid recalling ...
1079 if (auto _v = dynamic_cast<RooRealVar *>(x); _v) {
1080 _v->setBinning(RooBinning(_bins.size() - 1, &_bins[0], o->GetName()), o->GetName());
1081 _v->getBinning(o->GetName())
1082 .SetTitle(""); // indicates to use the current var title when building histograms etc
1083 //_v->getBinning(o->GetName()).SetTitle(strlen(dynamic_cast<TObject*>(x)->GetTitle()) ?
1084 // dynamic_cast<TObject*>(x)->GetTitle() : dynamic_cast<TObject*>(x)->GetName());
1085 }
1086 binningName = o->GetName();
1087 delete bins;
1088 } else if (_parentX) {
1089 // use parent axis binning if defined, otherwise we will default
1090 binningName = _parentX->GetName();
1091 }
1092 }
1093 }
1094
1095 if (!fXAxis) {
1096 if (auto r = dynamic_cast<RooAbsRealLValue *>(x); r) {
1097 if (r->getBinning(binningName).isUniform()) {
1098 fXAxis = std::make_shared<Axis2>(x->numBins(binningName), r->getMin(binningName), r->getMax(binningName));
1099 } else {
1100 fXAxis = std::make_shared<Axis2>(x->numBins(binningName), r->getBinning(binningName).array());
1101 }
1102 } else if (dynamic_cast<RooAbsCategory *>(x)) {
1103 std::vector<double> bins = {};
1104 for (int i = 0; i <= x->numBins(binningName); i++)
1105 bins.push_back(i);
1106 fXAxis = std::make_shared<Axis2>(x->numBins(binningName), &bins[0]);
1107 // TODO have to load current state of bin labels if was a category (sadly not a virtual method)
1108 for (int i = 0; i < x->numBins(binningName); i++) {
1109 fXAxis->SetBinLabel(i + 1, dynamic_cast<RooAbsCategory *>(x)->lookupName(i).c_str());
1110 }
1111 }
1112 }
1113
1114 fXAxis->SetName(binningName);
1115 fXAxis->SetParent(dynamic_cast<TObject *>(x));
1116 return fXAxis.get();
1117}
1118
1119const char *xRooNode::GetIconName() const
1120{
1121 if (auto o = get(); o) {
1122 if (o->InheritsFrom("RooWorkspace"))
1123 return "TFile";
1124 if (o->InheritsFrom("RooAbsData"))
1125 return "TProfile";
1126 if (o->InheritsFrom("RooSimultaneous"))
1127 return "TH3D";
1128
1129 if (o->InheritsFrom("RooProdPdf"))
1130 return "a.C"; // or nullptr for folder
1131 if (o->InheritsFrom("RooRealSumPdf") || o->InheritsFrom("RooAddPdf"))
1132 return "TH2D";
1133 // if(o->InheritsFrom("RooProduct")) return "TH1D";
1134 if (o->InheritsFrom("RooFitResult")) {
1135 if (!gClient->GetMimeTypeList()->GetIcon("xRooFitRooFitResult", true)) {
1136 gClient->GetMimeTypeList()->AddType("xRooFitRooFitResult", "xRooFitRooFitResult", "package.xpm",
1137 "package.xpm", "->Browse()");
1138 }
1139 return "xRooFitRooFitResult";
1140 }
1141 if (o->InheritsFrom("RooRealVar") || o->InheritsFrom("RooCategory")) {
1142 if (get<RooAbsArg>()->getAttribute("obs")) {
1143 if (!gClient->GetMimeTypeList()->GetIcon("xRooFitObs", true)) {
1144 gClient->GetMimeTypeList()->AddType("xRooFitObs", "xRooFitObs", "x_pic.xpm", "x_pic.xpm", "->Browse()");
1145 }
1146 if (!gClient->GetMimeTypeList()->GetIcon("xRooFitGlobs", true)) {
1147 gClient->GetMimeTypeList()->AddType("xRooFitGlobs", "xRooFitGlobs", "z_pic.xpm", "z_pic.xpm",
1148 "->Browse()");
1149 }
1150 return (get<RooAbsArg>()->getAttribute("global") ? "xRooFitGlobs" : "xRooFitObs");
1151 }
1152 return "TLeaf";
1153 }
1154 if (o->InheritsFrom("TStyle")) {
1155 if (!gClient->GetMimeTypeList()->GetIcon("xRooFitTStyle", true)) {
1156 gClient->GetMimeTypeList()->AddType("xRooFitTStyle", "xRooFitTStyle", "bld_colorselect.xpm",
1157 "bld_colorselect.xpm", "->Browse()");
1158 }
1159 return "xRooFitTStyle";
1160 }
1161 if (o->InheritsFrom("RooConstVar")) {
1162 /*if (!gClient->GetMimeTypeList()->GetIcon("xRooFitRooConstVar",true)) {
1163 gClient->GetMimeTypeList()->AddType("xRooFitRooConstVar", "xRooFitRooConstVar", "stop_t.xpm", "stop_t.xpm",
1164 "->Browse()");
1165 }
1166 return "xRooFitRooConstVar";*/
1167 return "TMethodBrowsable-leaf";
1168 }
1169 if (o->InheritsFrom("RooStats::HypoTestInverterResult")) {
1170 if (!gClient->GetMimeTypeList()->GetIcon("xRooFitScanStyle", true)) {
1171 gClient->GetMimeTypeList()->AddType("xRooFitScanStyle", "xRooFitScanStyle", "f2_s.xpm", "f2_s.xpm",
1172 "->Browse()");
1173 }
1174 return "xRooFitScanStyle";
1175 }
1176 if (o->InheritsFrom("RooStats::HypoTestResult")) {
1177 if (!gClient->GetMimeTypeList()->GetIcon("xRooFitTestStyle", true)) {
1178 gClient->GetMimeTypeList()->AddType("xRooFitTestStyle", "xRooFitTestStyle", "diamond.xpm", "diamond.xpm",
1179 "->Browse()");
1180 }
1181 return "xRooFitTestStyle";
1182 }
1183 if (o->InheritsFrom("RooStats::HistFactory::FlexibleInterpVar"))
1184 return "TBranchElement-folder";
1185 if (o->InheritsFrom("RooAbsPdf")) {
1186 if (!gClient->GetMimeTypeList()->GetIcon("xRooFitPDFStyle", true)) {
1187 gClient->GetMimeTypeList()->AddType("xRooFitPDFStyle", "xRooFitPDFStyle", "pdf.xpm", "pdf.xpm",
1188 "->Browse()");
1189 }
1190 return "xRooFitPDFStyle";
1191 }
1192 if (auto a = dynamic_cast<RooAbsReal *>(o); a) {
1193 if (auto _ax = GetXaxis();
1194 _ax && (a->isBinnedDistribution(*dynamic_cast<RooAbsArg *>(_ax->GetParent())) ||
1195 (dynamic_cast<RooAbsRealLValue *>(_ax->GetParent()) &&
1196 std::unique_ptr<std::list<double>>(a->binBoundaries(
1197 *dynamic_cast<RooAbsRealLValue *>(_ax->GetParent()), -std::numeric_limits<double>::infinity(),
1198 std::numeric_limits<double>::infinity()))))) {
1199 return "TH1D";
1200 }
1201 return "TF1";
1202 }
1203 return o->ClassName();
1204 }
1205 if (!IsFolder()) {
1206 return "Unknown";
1207 }
1208 return nullptr;
1209}
1210
1211const char *xRooNode::GetNodeType() const
1212{
1213 if (auto o = get(); o && fParent && (fParent->get<RooProduct>() || fParent->get<RooRealSumPdf>())) {
1214 if (o->InheritsFrom("RooStats::HistFactory::FlexibleInterpVar"))
1215 return "Overall";
1216 if (o->InheritsFrom("PiecewiseInterpolation"))
1217 return (dynamic_cast<RooAbsArg *>(o)->getAttribute("density")) ? "DensityHisto" : "Histo";
1218 if (o->InheritsFrom("RooHistFunc"))
1219 return (dynamic_cast<RooAbsArg *>(o)->getAttribute("density")) ? "ConstDensityHisto" : "ConstHisto";
1220 if (o->InheritsFrom("RooBinWidthFunction"))
1221 return "Density";
1222 if (o->InheritsFrom("ParamHistFunc"))
1223 return "Shape";
1224 if (o->InheritsFrom("RooRealVar"))
1225 return "Norm";
1226 if (o->InheritsFrom("RooConstVar"))
1227 return "Const";
1228 }
1229 return "";
1230}
1231
1232xRooNode xRooNode::coords(bool setVals) const
1233{
1234 xRooNode out(".coords", nullptr, *this);
1235 // go up through parents looking for slice obs
1236 auto _p = std::shared_ptr<xRooNode>(const_cast<xRooNode *>(this), [](xRooNode *) {});
1237 while (_p) {
1238 TString pName(_p->GetName());
1239 if (auto pos = pName.Index('='); pos != -1) {
1240 if (pos > 0 && pName(pos - 1) == '<') {
1241 // should be a range on a real lvalue, of form low<=name<high
1242 double low = TString(pName(0, pos - 1)).Atof();
1243 pName = pName(pos + 1, pName.Length());
1244 double high = TString(pName(pName.Index('<') + 1, pName.Length())).Atof();
1245 pName = pName(0, pName.Index('<'));
1246 if (auto _obs = _p->getObject<RooAbsRealLValue>(pName.Data()); _obs) {
1247 if (setVals) {
1248 _obs->setVal((high + low) / 2.);
1249 static_cast<RooRealVar *>(_obs.get())->setRange("coordRange", low, high);
1250 _obs->setStringAttribute(
1251 "coordRange", "coordRange"); // will need if we allow multi disconnected regions, need comma list
1252 }
1253 out.emplace_back(std::make_shared<xRooNode>(_obs->GetName(), _obs, _p));
1254 } else {
1255 throw std::runtime_error(TString::Format("Unknown observable: %s", pName.Data()));
1256 }
1257
1258 } else if (auto _obs = _p->getObject<RooAbsArg>(pName(0, pos)); _obs) {
1259 if (setVals) {
1260 if (auto _cat = dynamic_cast<RooAbsCategoryLValue *>(_obs.get()); _cat) {
1261 _cat->setLabel(pName(pos + 1, pName.Length()));
1262 } else if (auto _var = dynamic_cast<RooAbsRealLValue *>(_obs.get()); _var) {
1263 _var->setVal(TString(pName(pos + 1, pName.Length())).Atof());
1264 }
1265 }
1266 out.emplace_back(std::make_shared<xRooNode>(_obs->GetName(), _obs, _p));
1267 } else {
1268 throw std::runtime_error("Unknown observable, could not find");
1269 }
1270 }
1271 _p = _p->fParent;
1272 }
1273 return out;
1274}
1275
1276void xRooNode::_Add_(const char *name, const char *opt)
1277{
1278 try {
1279 Add(name, opt);
1280 } catch (const std::exception &e) {
1281 new TGMsgBox(gClient->GetRoot(), gClient->GetRoot(), "Exception", e.what(),
1282 kMBIconExclamation); // deletes self on dismiss?
1283 }
1284}
1285void xRooNode::_Vary_(const char *what)
1286{
1287 try {
1288 Vary(what);
1289 } catch (const std::exception &e) {
1290 new TGMsgBox(gClient->GetRoot(), gClient->GetRoot(), "Exception", e.what(),
1291 kMBIconExclamation); // deletes self on dismiss?
1292 }
1293}
1294
1296{
1297
1298 if (strcmp(GetName(), ".poi") == 0) {
1299 // demote a parameter from being a poi
1300 auto toRemove =
1301 (child.get<RooAbsArg>() || !find(child.GetName())) ? child : xRooNode(find(child.GetName())->fComp);
1302 if (toRemove) {
1303 if (!toRemove.get<RooAbsArg>()->getAttribute("poi")) {
1304 throw std::runtime_error(TString::Format("%s is not a poi", toRemove.GetName()));
1305 }
1306 toRemove.get<RooAbsArg>()->setAttribute("poi", false);
1307 return toRemove;
1308 }
1309 } else if (strcmp(GetName(), ".factors") == 0 || strcmp(GetName(), ".constraints") == 0 ||
1310 strcmp(GetName(), ".components") == 0) {
1311 auto toRemove =
1312 (child.get<RooAbsArg>() || !find(child.GetName())) ? child : xRooNode(find(child.GetName())->fComp);
1313 if (auto p = fParent->get<RooProdPdf>(); p) {
1314 auto pdf = toRemove.get<RooAbsArg>();
1315 if (!pdf)
1316 pdf = p->pdfList().find(child.GetName());
1317 if (!pdf)
1318 throw std::runtime_error(TString::Format("Cannot find %s in %s", child.GetName(), fParent->GetName()));
1319 auto i = p->pdfList().index(*pdf);
1320 if (i >= 0) {
1321#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
1322 const_cast<RooArgList &>(p->pdfList()).remove(*pdf);
1323#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 26, 00)
1324 p->_pdfNSetList.erase(p->_pdfNSetList.begin() + i);
1325#else
1326 auto nset = p->_pdfNSetList.At(i);
1327 p->_pdfNSetList.Remove(nset);
1328 delete nset; // I don't think the RooLinkedList owned it so must delete ourself
1329#endif
1330 if (p->_extendedIndex == i)
1331 p->_extendedIndex = -1;
1332 else if (p->_extendedIndex > i)
1333 p->_extendedIndex--;
1334#else
1335 p->removePdfs(RooArgSet(*pdf));
1336#endif
1337 sterilize();
1338 return xRooNode(*pdf);
1339 } else {
1340 throw std::runtime_error(TString::Format("Cannot find %s in %s", child.GetName(), fParent->GetName()));
1341 }
1342 } else if (auto p2 = fParent->get<RooProduct>(); p2) {
1343 auto arg = toRemove.get<RooAbsArg>();
1344 if (!arg)
1345 arg = p2->components().find(child.GetName());
1346 if (!arg)
1347 throw std::runtime_error(TString::Format("Cannot find %s in %s", child.GetName(), fParent->GetName()));
1348 // remove server ... doesn't seem to trigger removal from proxy
1349#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
1350 p2->_compRSet.remove(*arg);
1351#else
1352 const_cast<RooArgList &>(p2->realComponents()).remove(*arg);
1353#endif
1354 p2->removeServer(*arg, true);
1355 sterilize();
1356 return xRooNode(*arg);
1357 } else if (fParent->get<RooSimultaneous>()) {
1358 // remove from all channels
1359 bool removed = false;
1360 for (auto &c : fParent->bins()) {
1361 try {
1362 c->constraints().Remove(toRemove);
1363 removed = true;
1364 } catch (std::runtime_error &) { /* wasn't a constraint in channel */
1365 }
1366 }
1367 sterilize();
1368 if (!removed)
1369 throw std::runtime_error(TString::Format("Cannot find %s in %s", child.GetName(), fParent->GetName()));
1370 return toRemove;
1371 } else if (auto p4 = fParent->get<RooRealSumPdf>(); p4) {
1372 auto arg = toRemove.get<RooAbsArg>();
1373 if (!arg)
1374 arg = p4->funcList().find(child.GetName());
1375 if (!arg)
1376 throw std::runtime_error(TString::Format("Cannot find %s in %s", child.GetName(), fParent->GetName()));
1377 // remove, including coef removal ....
1378 auto idx = p4->funcList().index(arg);
1379
1380 if (idx != -1) {
1381
1382 const_cast<RooArgList &>(p4->funcList()).remove(*arg);
1383 p4->removeServer(*arg, true);
1384 // have to be careful removing coef because if shared will end up removing them all!!
1385 std::vector<RooAbsArg *> _coefs;
1386 for (size_t ii = 0; ii < const_cast<RooArgList &>(p4->coefList()).size(); ii++) {
1387 if (ii != size_t(idx))
1388 _coefs.push_back(const_cast<RooArgList &>(p4->coefList()).at(ii));
1389 }
1390 const_cast<RooArgList &>(p4->coefList()).removeAll();
1391 for (auto &a : _coefs)
1392 const_cast<RooArgList &>(p4->coefList()).add(*a);
1393
1394 sterilize();
1395 } else {
1396 throw std::runtime_error(TString::Format("Cannot find %s in %s", child.GetName(), fParent->GetName()));
1397 }
1398 return xRooNode(*arg);
1399 } // todo: add support for RooAddPdf and RooAddition
1400 }
1401
1402 if (auto w = get<RooWorkspace>(); w) {
1403 xRooNode out(child.GetName());
1404 auto arg = w->components().find(child.GetName());
1405 if (!arg)
1406 arg = operator[](child.GetName())->get<RooAbsArg>();
1407 if (!arg) {
1408 throw std::runtime_error(TString::Format("Cannot find %s in workspace %s", child.GetName(), GetName()));
1409 }
1410 // check has no clients ... if so, cannot delete
1411 if (arg->hasClients()) {
1412 throw std::runtime_error(
1413 TString::Format("Cannot remove %s from workspace %s, because it has dependencies - first remove from those",
1414 child.GetName(), GetName()));
1415 }
1416 const_cast<RooArgSet &>(w->components()).remove(*arg); // deletes arg
1417 Info("Remove", "Deleted %s from workspace %s", out.GetName(), GetName());
1418 return out;
1419 } else if (get<RooProduct>() || get<RooProdPdf>()) {
1420 return factors().Remove(child);
1421 } else if (get<RooRealSumPdf>()) {
1422 return components().Remove(child);
1423 }
1424
1425 throw std::runtime_error("Removal not implemented for this type of object");
1426}
1427
1429{
1430
1431 class AutoUpdater {
1432 public:
1433 AutoUpdater(xRooNode &_n) : n(_n) {}
1434 ~AutoUpdater() { n.browse(); }
1435 xRooNode &n;
1436 };
1437 AutoUpdater xxx(*this);
1438
1439 TString sOpt(opt);
1440 bool considerType(sOpt == "+");
1441
1442 if (strlen(GetName()) > 0 && GetName()[0] == '!' && fParent) {
1443 // folder .. pass onto parent and add folder to child folder list
1444 const_cast<xRooNode &>(child).fFolder += GetName();
1445 return fParent->Add(child, opt);
1446 }
1447 // this is how to get the first real parent ... may be useful at some point?
1448 /*auto realParent = fParent;
1449 while(!realParent->get()) {
1450 realParent = realParent->fParent;
1451 if (!realParent) throw std::runtime_error("No parentage");
1452 }*/
1453
1454 // adding to a collection node will incorporate the child into the parent of the collection
1455 // in the appropriate way
1456 if (strcmp(GetName(), ".factors") == 0) {
1457 // multiply the parent
1458 return fParent->Multiply(child, opt);
1459 } else if (strcmp(GetName(), ".components") == 0) {
1460 // add to the parent
1461 return fParent->Add(child, opt);
1462 } else if (strcmp(GetName(), ".variations") == 0) {
1463 // vary the parent
1464 return fParent->Vary(child);
1465 } else if (strcmp(GetName(), ".constraints") == 0) {
1466 // constrain the parent
1467 return fParent->Constrain(child);
1468 } else if (strcmp(GetName(), ".bins") == 0 && fParent->get<RooSimultaneous>()) {
1469 // adding a channel (should adding a 'bin' be an 'Extend' operation?)
1470 return fParent->Vary(child);
1471 } else if ((strcmp(GetName(), ".globs") == 0)) {
1472 if (child.get<RooAbsArg>() || (!child.fComp && getObject<RooAbsArg>(child.GetName()))) {
1473 auto out = (child.get<RooAbsArg>()) ? child.get<RooAbsArg>() : getObject<RooAbsArg>(child.GetName()).get();
1474 out->setAttribute("obs");
1475 out->setAttribute("global");
1476 return xRooNode(*out, *this);
1477 }
1478 throw std::runtime_error("Failed to add global observable");
1479 } else if ((strcmp(GetName(), ".poi") == 0)) {
1480 if (child.get<RooAbsLValue>() || (!child.fComp && getObject<RooAbsLValue>(child.GetName()))) {
1481 auto out = (child.get<RooAbsArg>()) ? child.get<RooAbsArg>() : getObject<RooAbsArg>(child.GetName()).get();
1482 out->setAttribute("poi");
1483 return xRooNode(*out, *this);
1484 }
1485 throw std::runtime_error("Failed to add parameter of interest");
1486 } else if ((strcmp(GetName(), ".pars") == 0 || strcmp(GetName(), ".vars") == 0) && fParent->get<RooWorkspace>()) {
1487 // adding a parameter, interpret as factory string unless no "[" then create RooRealVar
1488 TString fac(child.GetName());
1489 if (!fac.Contains("["))
1490 fac += "[1]";
1491 return xRooNode(*fParent->get<RooWorkspace>()->factory(fac), fParent);
1492 } else if (strcmp(GetName(), ".datasets()") == 0) {
1493 // create a dataset - only allowed for pdfs or workspaces
1494 if (auto _ws = ws(); _ws && fParent) {
1495 sOpt.ToLower();
1496 if (!fParent->get<RooAbsPdf>() && (!fParent->get<RooWorkspace>() || sOpt == "asimov")) {
1497 throw std::runtime_error(
1498 "Datasets can only be created for pdfs or workspaces (except if generated dataset, then must be pdf)");
1499 }
1500
1501 if (sOpt == "asimov" || sOpt == "toy") {
1502 // generate expected dataset - note that globs will be frozen at this time
1503 auto _fr = std::dynamic_pointer_cast<const RooFitResult>(fParent->fitResult().fComp);
1504 if (strlen(_fr->GetName()) == 0)
1505 std::const_pointer_cast<RooFitResult>(_fr)->SetName(TUUID().AsString());
1506 auto asi = xRooFit::generateFrom(*fParent->get<RooAbsPdf>(), *_fr, sOpt == "asimov");
1507 if (strlen(child.GetName()))
1508 asi.first->SetName(child.GetName());
1509 if (asi.first) {
1510 _ws->import(*asi.first);
1511 }
1512 if (_fr->numStatusHistory() == 0) {
1513 if (!GETWSSNAPSHOTS(_ws).find(_fr->GetName())) {
1514 const_cast<RooLinkedList &>(GETWSSNAPSHOTS(_ws)).Add(_fr->Clone());
1515 }
1516 } else if (!_ws->obj(_fr->GetName())) {
1517 _ws->import(const_cast<RooFitResult &>(*_fr));
1518 } // save fr to workspace, for later retrieval
1519 if (asi.second) {
1520#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 26, 00)
1521 _ws->saveSnapshot(asi.first->GetName(), *asi.second,
1522 true); // TODO: Migrate to using globs inside datasets
1523#else
1524 RooArgSet _tmp;
1525 _tmp.add(*asi.second);
1526 _ws->saveSnapshot(asi.first->GetName(), _tmp, true);
1527#endif
1528 }
1529 return xRooNode(*_ws->data(asi.first->GetName()), fParent);
1530 }
1531
1532 auto parentObs = fParent->obs(); // may own globs so keep alive
1533 auto _obs = parentObs.argList();
1534 // put globs in a snapshot
1535 std::unique_ptr<RooAbsCollection> _globs(_obs.selectByAttrib("global", true));
1536 // RooArgSet _tmp; _tmp.add(*_globs);_ws->saveSnapshot(child.GetName(),_tmp);
1537 _obs.remove(*_globs);
1538
1539 // include any coords
1540 _obs.add(coords(false).argList(), true);
1541 // include axis var too, provided it's an observable
1542 if (auto ax = GetXaxis(); ax && dynamic_cast<RooAbsArg *>(ax->GetParent())->getAttribute("obs")) {
1543 _obs.add(*dynamic_cast<RooAbsArg *>(ax->GetParent()));
1544 }
1545 // check if ws already has a dataset with this name, if it does we may need to extend columns
1546 if (auto _d = _ws->data(child.GetName()); _d) {
1547 // add any missing obs
1548 RooArgSet l(_obs);
1549 l.remove(*_d->get(), true, true);
1550 if (!l.empty()) {
1551 auto _dd = dynamic_cast<RooDataSet *>(_d);
1552 if (!_dd)
1553 throw std::runtime_error("Cannot extend dataset with new columns");
1554 for (auto &x : l) {
1555 _dd->addColumn(*x);
1556 }
1557 }
1558 } else {
1559 RooRealVar w("weightVar", "weightVar", 1);
1560 _obs.add(w);
1561 RooDataSet d(child.GetName(), child.GetTitle(), _obs, RooFit::WeightVar("weightVar"));
1562 _ws->import(d);
1563 // seems have to set bits after importing, not before
1564 if (auto __d = _ws->data(child.GetName()))
1565 __d->SetBit(1 << 20, _ws->allData().size() == 1); // sets as selected if is only ds
1566 }
1567 /*if(!_ws->data(child.GetName())) {
1568 RooRealVar w("weightVar", "weightVar", 1);
1569 RooArgSet _obs; _obs.add(w);
1570 RooDataSet d(child.GetName(), child.GetTitle(), _obs, "weightVar");
1571 _ws->import(d);
1572 }*/
1573 auto out = std::shared_ptr<TObject>(_ws->data(child.GetName()), [](TObject *) {});
1574
1575 if (out) {
1576 xRooNode o(out, fParent);
1577 if (child.get<TH1>())
1578 o = *child.get();
1579 return o;
1580 }
1581 }
1582 throw std::runtime_error("Cannot create dataset");
1583 }
1584
1585 if (!get()) {
1586 if (!fParent)
1587 throw std::runtime_error("Cannot add to null object with no parentage");
1588
1589 auto _ref = emplace_back(std::shared_ptr<xRooNode>(&const_cast<xRooNode &>(child), [](TObject *) {}));
1590 try {
1591 fComp = fParent->Add(*this, "+").fComp;
1592 } catch (...) {
1593 resize(size() - 1);
1594 std::rethrow_exception(std::current_exception());
1595 }
1596 resize(size() - 1); // remove the temporarily added node
1597
1598 if (!fComp) {
1599 throw std::runtime_error("No object");
1600 }
1601 }
1602
1603 if (auto p = get<RooAbsData>(); p) {
1604 if (auto bb = getBrowsable(".sourceds"))
1605 bb->Add(child, opt);
1606 if (auto _data = child.get<RooDataSet>()) {
1607 auto ds = dynamic_cast<RooDataSet *>(p);
1608 if (!ds) {
1609 throw std::runtime_error("Can only add datasets to a dataset");
1610 }
1611
1612 // append any missing globs, and check any existing globs have matching values
1613 RooArgList globsToAdd;
1614 auto _globs = globs();
1615 for (auto &glob : child.globs()) {
1616 if (auto g = _globs.find(glob->GetName()); !g) {
1617 globsToAdd.addClone(*glob->get<RooAbsArg>());
1618 } else if (g->GetContent() != glob->GetContent()) {
1619 Warning("Add", "Global observable %s=%g in dataset mismatches child value %g ... ignoring child",
1620 g->GetName(), g->GetContent(), glob->GetContent());
1621 }
1622 }
1623 // add any existing globs to list then set the list
1624 if (auto _dglobs = p->getGlobalObservables()) {
1625 globsToAdd.addClone(*_dglobs);
1626 } else {
1627 for (auto g : _globs)
1628 globsToAdd.addClone(*g->get<RooAbsArg>());
1629 }
1630 p->setGlobalObservables(globsToAdd);
1631
1632 // append any missing observables to our dataset, then append the dataset
1633
1634 for (auto col : *_data->get()) {
1635 if (!p->get()->contains(*col)) {
1636 ds->addColumn(*col);
1637 }
1638 }
1639 ds->append(*_data);
1640 return *this;
1641 }
1642 auto _h = child.get<TH1>();
1643 if (!_h) {
1644 throw std::runtime_error("Can only add histogram or dataset to data");
1645 }
1646 auto _pdf = parentPdf();
1647 if (!_pdf)
1648 throw std::runtime_error("Could not find pdf");
1649 auto _ax = _pdf->GetXaxis();
1650 if (!_ax) {
1651 throw std::runtime_error("Cannot determine binning to add data");
1652 }
1653
1654 RooArgSet obs;
1655 obs.add(*dynamic_cast<RooAbsArg *>(_ax->GetParent()));
1656 obs.add(coords().argList()); // will also move obs to coords
1657
1658 // add any missing obs
1659 RooArgSet l(obs);
1660 l.remove(*p->get(), true, true);
1661 if (!l.empty()) {
1662 auto _d = dynamic_cast<RooDataSet *>(p);
1663 if (!_d)
1664 throw std::runtime_error("Cannot extend dataset with new columns");
1665 for (auto &x : l) {
1666 _d->addColumn(*x);
1667 }
1668 }
1669
1670 // before adding, ensure range is good to cover
1671 for (auto &o : obs) {
1672 if (auto v = dynamic_cast<RooRealVar *>(o); v) {
1673 if (auto dv = dynamic_cast<RooRealVar *>(p->get()->find(v->GetName())); dv) {
1674 if (v->getMin() < dv->getMin())
1675 dv->setMin(v->getMin());
1676 if (v->getMax() > dv->getMax())
1677 dv->setMax(v->getMax());
1678 }
1679 } else if (auto c = dynamic_cast<RooCategory *>(o); c) {
1680 if (auto dc = dynamic_cast<RooCategory *>(p->get()->find(c->GetName())); dc) {
1681 if (!dc->hasLabel(c->getCurrentLabel())) {
1682 dc->defineType(c->getCurrentLabel(), c->getCurrentIndex());
1683 }
1684 }
1685 }
1686 }
1687
1688 for (int i = 1; i <= _h->GetNbinsX(); i++) {
1689 if (auto cat = dynamic_cast<RooAbsCategoryLValue *>(_ax->GetParent())) {
1690 if (!_h->GetXaxis()->GetBinLabel(i)) {
1691 throw std::runtime_error(
1692 TString::Format("Categorical observable %s requires bin labels", _ax->GetParent()->GetName()));
1693 } else if (!cat->hasLabel(_h->GetXaxis()->GetBinLabel(i))) {
1694 throw std::runtime_error(TString::Format("Categorical observable %s does not have label %s",
1695 _ax->GetParent()->GetName(), _h->GetXaxis()->GetBinLabel(i)));
1696 } else {
1697 cat->setLabel(_h->GetXaxis()->GetBinLabel(i));
1698 }
1699 } else {
1700 dynamic_cast<RooAbsRealLValue *>(_ax->GetParent())->setVal(_h->GetBinCenter(i));
1701 }
1702 p->add(obs, _h->GetBinContent(i));
1703 }
1704
1705 return *this;
1706 }
1707
1708 if (auto p = get<RooAddPdf>(); p) {
1709 if ((child.get<RooAbsPdf>() || (!child.fComp && getObject<RooAbsPdf>(child.GetName())))) {
1710 auto out = (child.fComp) ? acquire(child.fComp) : getObject<RooAbsArg>(child.GetName());
1711 // don't add a coef if in 'all-extended' mode and this pdf is extendable
1712 auto _pdf = std::dynamic_pointer_cast<RooAbsPdf>(out);
1713 if (!_pdf) {
1714 throw std::runtime_error("Something went wrong with pdf acquisition");
1715 }
1716
1717 if (auto _ax = GetXaxis(); _ax && dynamic_cast<RooAbsRealLValue *>(_ax->GetParent()) &&
1718 _pdf->dependsOn(*static_cast<RooAbsArg *>(_ax->GetParent()))) {
1719 auto _p = _pdf;
1720
1721 if (auto _boundaries = std::unique_ptr<std::list<double>>(_p->binBoundaries(
1722 *dynamic_cast<RooAbsRealLValue *>(_ax->GetParent()), -std::numeric_limits<double>::infinity(),
1723 std::numeric_limits<double>::infinity()));
1724 !_boundaries && _ax->GetNbins() > 0) {
1725#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 24, 00)
1726 Warning("Add", "Adding unbinned pdf %s to binned %s - will wrap with RooBinSamplingPdf(...)",
1727 _p->GetName(), GetName());
1728 _p = acquireNew<RooBinSamplingPdf>(TString::Format("%s_binned", _p->GetName()), _p->GetTitle(),
1729 *dynamic_cast<RooAbsRealLValue *>(_ax->GetParent()), *_p);
1730 _p->setStringAttribute("alias", std::dynamic_pointer_cast<RooAbsArg>(out)->getStringAttribute("alias"));
1731 if (!_p->getStringAttribute("alias"))
1732 _p->setStringAttribute("alias", out->GetName());
1733#else
1734 throw std::runtime_error(
1735 "unsupported addition of unbinned pdf to binned model - please upgrade to at least ROOT 6.24");
1736#endif
1737 _pdf = _p;
1738 }
1739 }
1740
1741 if (!(_pdf->canBeExtended() && p->coefList().empty())) {
1742 // if extended, use an extended binding as the coef
1743 // otherwise e.g. if adding a RooRealSumPdf the stacked histograms will be above the
1744 // actual pdf histogram because the pdf histogram is just normalized down
1745 if (_pdf->canBeExtended()) {
1746 // FIXME: ExtendedBinding needs the obs list passing to it ... should be fixed in RooFit
1747 // until then, this will return "1" and so the pdf's histograms wont be normalized properly in relation
1748 // to stacks of its comps
1749 const_cast<RooArgList &>(p->coefList())
1750 .add(*acquireNew<RooExtendedBinding>(TString::Format("%s_extBind", _pdf->GetName()),
1751 TString::Format("Expected Events of %s", _pdf->GetTitle()),
1752 *_pdf));
1753 } else {
1754 const_cast<RooArgList &>(p->coefList()).add(*acquire2<RooAbsArg, RooRealVar>("1", "1", 1));
1755 }
1756 }
1757 const_cast<RooArgList &>(p->pdfList()).add(*_pdf);
1758 sterilize();
1759 return xRooNode(*_pdf, *this);
1760 } else if ((child.get<TH1>() || child.get<RooAbsReal>() ||
1761 (!child.get() && getObject<RooAbsReal>(child.GetName()))) &&
1762 !child.get<RooAbsPdf>()) {
1763 RooRealSumPdf *_pdf = nullptr;
1764 bool tooMany(false);
1765 for (auto &pp : factors()) {
1766 if (auto _p = pp->get<RooRealSumPdf>(); _p) {
1767 if (_pdf) {
1768 _pdf = nullptr;
1769 tooMany = true;
1770 break;
1771 } // more than one!
1772 _pdf = _p;
1773 }
1774 }
1775 if (_pdf) {
1776 return xRooNode(*_pdf, *this).Add(child);
1777 } else if (!tooMany) {
1778 // create a RooRealSumPdf to hold the child
1779 auto _sumpdf = Add(*acquireNew<RooRealSumPdf>(TString::Format("%s_samples", p->GetName()),
1780 TString::Format("%s samples", GetTitle()), RooArgList(),
1781 RooArgList(), true));
1782 _sumpdf.get<RooAbsArg>()->setStringAttribute("alias", "samples");
1783 return _sumpdf.Add(child);
1784 }
1785 }
1786 }
1787
1788 if (auto p = get<RooRealSumPdf>(); p) {
1789 std::shared_ptr<TObject> out;
1790 auto cc = child.fComp;
1791 bool isConverted = (cc != child.convertForAcquisition(*this, sOpt));
1792 if (child.get<RooAbsReal>())
1793 out = acquire(child.fComp);
1794 if (!child.fComp && getObject<RooAbsReal>(child.GetName())) {
1795 Info("Add", "Adding existing function %s to %s", child.GetName(), p->GetName());
1796 out = getObject<RooAbsReal>(child.GetName());
1797 }
1798
1799 if (!out && !child.fComp) {
1800 std::shared_ptr<RooAbsArg> _func;
1801 // a null node .. so create either a new RooProduct or RooHistFunc if has observables (or no deps but has
1802 // x-axis)
1803 auto _obs = robs();
1804 if (!_obs.empty() || GetXaxis()) {
1805 if (_obs.empty()) {
1806 // using X axis to construct hist
1807 auto _ax = dynamic_cast<Axis2 *>(GetXaxis());
1808 auto t = TH1::AddDirectoryStatus();
1809 TH1::AddDirectory(false);
1810 auto h =
1811 std::make_unique<TH1D>(child.GetName(), child.GetTitle(), _ax->GetNbins(), _ax->binning()->array());
1813 h->GetXaxis()->SetName(TString::Format("%s;%s", _ax->GetParent()->GetName(), _ax->GetName()));
1814 // technically convertForAcquisition has already acquired so no need to re-acquire but should be harmless
1815 _func = std::dynamic_pointer_cast<RooAbsArg>(acquire(xRooNode(*h).convertForAcquisition(*this)));
1816 } else if (_obs.size() == 1) {
1817 // use the single obs to make a TH1D
1818 auto _x = _obs.at(0)->get<RooAbsLValue>();
1819 auto _bnames = _x->getBinningNames();
1820 TString binningName = p->getStringAttribute("binning");
1821 for (auto &b : _bnames) {
1822 if (b == p->GetName()) {
1823 binningName = p->GetName();
1824 break;
1825 }
1826 }
1827 auto t = TH1::AddDirectoryStatus();
1828 TH1::AddDirectory(false);
1829 auto h = std::make_unique<TH1D>(child.GetName(), child.GetTitle(), _x->numBins(binningName),
1830 _x->getBinningPtr(binningName)->array());
1832 h->GetXaxis()->SetName(
1833 TString::Format("%s;%s", dynamic_cast<TObject *>(_x)->GetName(), binningName.Data()));
1834 // technically convertForAcquisition has already acquired so no need to re-acquire but should be harmless
1835 _func = std::dynamic_pointer_cast<RooAbsArg>(acquire(xRooNode(*h).convertForAcquisition(*this)));
1836 Info("Add", "Created densityhisto factor %s (xaxis=%s) for %s", _func->GetName(), _obs.at(0)->GetName(),
1837 p->GetName());
1838 } else {
1839 throw std::runtime_error("Unsupported creation of new component in SumPdf for this many obs");
1840 }
1841 } else {
1842 _func = acquireNew<RooProduct>(TString::Format("%s_%s", p->GetName(), child.GetName()), child.GetTitle(),
1843 RooArgList());
1844 }
1845 _func->setStringAttribute("alias", child.GetName());
1846 out = _func;
1847 }
1848
1849 if (auto _f = std::dynamic_pointer_cast<RooHistFunc>(
1850 (child.get<RooProduct>()) ? child.factors()[child.GetName()]->fComp : out);
1851 _f) {
1852 // adding a histfunc directly to a sumpdf, should be a density
1853 _f->setAttribute("density");
1854 if (_f->getAttribute("autodensity")) {
1855 // need to divide by bin widths first
1856 for (int i = 0; i < _f->dataHist().numEntries(); i++) {
1857 auto bin_pars = _f->dataHist().get(i);
1858 _f->dataHist().set(*bin_pars, _f->dataHist().weight() / _f->dataHist().binVolume(*bin_pars));
1859 }
1860 _f->setAttribute("autodensity", false);
1861 _f->setValueDirty();
1862 }
1863
1864 // promote the axis vars to observables
1865 // can't use original child as might refer to unacquired deps
1866 for (auto &x : xRooNode("tmp", _f).vars()) {
1867 x->get<RooAbsArg>()->setAttribute("obs");
1868 }
1869 if (isConverted) {
1870 Info("Add", "Created %s factor RooHistFunc::%s for %s",
1871 _f->getAttribute("density") ? "densityhisto" : "histo", _f->GetName(), p->GetName());
1872 }
1873 }
1874
1875 if (auto _p = std::dynamic_pointer_cast<RooAbsPdf>(out); _p) {
1876 // adding a pdf to a RooRealSumPdf will replace it with a RooAddPdf and put the RooRealSumPdf inside that
1877 // if pdf is extended will use in the "no coefficients" state, where the expectedEvents are taking from
1878 // the pdf integrals
1879 TString newName(_p->GetName());
1880 newName.ReplaceAll("_samples", "");
1881 newName += "_components";
1882 Warning("Add", "converting samples to components");
1883
1884 if (auto _ax = GetXaxis(); _ax && dynamic_cast<RooAbsRealLValue *>(_ax->GetParent()) &&
1885 _p->dependsOn(*static_cast<RooAbsArg *>(_ax->GetParent()))) {
1886
1887 if (auto _boundaries = std::unique_ptr<std::list<double>>(_p->binBoundaries(
1888 *dynamic_cast<RooAbsRealLValue *>(_ax->GetParent()), -std::numeric_limits<double>::infinity(),
1889 std::numeric_limits<double>::infinity()));
1890 !_boundaries && _ax->GetNbins() > 0) {
1891#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 24, 00)
1892 Warning("Add", "Adding unbinned pdf %s to binned %s - will wrap with RooBinSamplingPdf(...)",
1893 _p->GetName(), GetName());
1894 _p = acquireNew<RooBinSamplingPdf>(TString::Format("%s_binned", _p->GetName()), _p->GetTitle(),
1895 *dynamic_cast<RooAbsRealLValue *>(_ax->GetParent()), *_p);
1896 _p->setStringAttribute("alias", std::dynamic_pointer_cast<RooAbsArg>(out)->getStringAttribute("alias"));
1897 if (!_p->getStringAttribute("alias"))
1898 _p->setStringAttribute("alias", out->GetName());
1899#else
1900 throw std::runtime_error(
1901 "unsupported addition of unbinned pdf to binned model - please upgrade to at least ROOT 6.24");
1902#endif
1903 }
1904 }
1905
1906 // require to be extended to be in coefficient-free mode ...
1907 // otherwise would lose the integral of the sumPdf (can't think of way to have a coef be the integral)
1908 if (!_p->canBeExtended()) {
1909 _p = acquireNew<RooExtendPdf>(TString::Format("%s_extended", _p->GetName()), _p->GetTitle(), *_p,
1910 *acquire2<RooAbsReal, RooRealVar>("1", "1", 1));
1911 }
1912
1913 return *(Replace(*acquireNew<RooAddPdf>(newName, _p->GetTitle(), RooArgList(*p, *_p)))
1914 .browse()[1]); // returns second node.
1915 }
1916
1917 if (auto _f = std::dynamic_pointer_cast<RooAbsReal>(out); _f) {
1918
1919 // todo: if adding a pdf, should actually replace RooRealSumPdf with a RooAddPdf and put
1920 // the sumPdf and *this* pdf inside that pdf
1921 // only exception is the binSamplingPdf below to integrate unbinned functions across bins
1922
1923 if (auto _ax = GetXaxis(); _ax && dynamic_cast<RooAbsRealLValue *>(_ax->GetParent()) &&
1924 _f->dependsOn(*static_cast<RooAbsArg *>(_ax->GetParent()))) {
1925
1926 if (auto _boundaries = std::unique_ptr<std::list<double>>(_f->binBoundaries(
1927 *dynamic_cast<RooAbsRealLValue *>(_ax->GetParent()), -std::numeric_limits<double>::infinity(),
1928 std::numeric_limits<double>::infinity()));
1929 !_boundaries && _ax->GetNbins() > 0) {
1930#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 24, 00)
1931 Warning(
1932 "Add",
1933 "Adding unbinned function %s to binned %s - will wrap with RooRealSumPdf(RooBinSamplingPdf(...))",
1934 _f->GetName(), GetName());
1935 auto sumPdf = acquireNew<RooRealSumPdf>(TString::Format("%s_pdfWrapper", _f->GetName()), _f->GetTitle(),
1936 *_f, *acquire2<RooAbsArg, RooRealVar>("1", "1", 1), true);
1937 sumPdf->setStringAttribute("alias", _f->getStringAttribute("alias"));
1938 if (!sumPdf->getStringAttribute("alias"))
1939 sumPdf->setStringAttribute("alias", out->GetName());
1940 _f = acquireNew<RooBinSamplingPdf>(TString::Format("%s_binned", _f->GetName()), _f->GetTitle(),
1941 *dynamic_cast<RooAbsRealLValue *>(_ax->GetParent()), *sumPdf);
1942 _f->setStringAttribute("alias", std::dynamic_pointer_cast<RooAbsArg>(out)->getStringAttribute("alias"));
1943 if (!_f->getStringAttribute("alias"))
1944 _f->setStringAttribute("alias", out->GetName());
1945#else
1946 throw std::runtime_error(
1947 "unsupported addition of unbinned function to binned model - please upgrade to at least ROOT 6.24");
1948#endif
1949 }
1950 }
1951
1952 const_cast<RooArgList &>(p->coefList()).add(*acquire2<RooAbsArg, RooRealVar>("1", "1", 1));
1953 const_cast<RooArgList &>(p->funcList()).add(*_f);
1954 // inherit binning if we dont have one yet
1955 if (!p->getStringAttribute("binning"))
1956 p->setStringAttribute("binning", _f->getStringAttribute("binning"));
1957
1958 xRooNode _out(_f, *this);
1959 if (auto gf = p->getStringAttribute("global_factors"); gf) {
1960 TStringToken pattern(gf, ";");
1961 while (pattern.NextToken()) {
1962 auto fac = getObject<RooAbsReal>(pattern.Data());
1963 if (!fac) {
1964 throw std::runtime_error(TString::Format("Could not find global factor %s", pattern.Data()));
1965 }
1966 _out.Multiply(fac);
1967 }
1968 }
1969 sterilize();
1970 // clear children for reload and update shared axis
1971 clear();
1972 fXAxis.reset();
1973 p->setStringAttribute("xvar", nullptr);
1974 browse();
1975 return _out;
1976 }
1977 } else if (auto p2 = get<RooProdPdf>(); p2) {
1978 // can "add" to a RooProdPdf provided trying to add a RooAbsReal not a RooAbsPdf and have a zero or 1
1979 // RooRealSumPdf child.convertForAcquisition(*this); - don't convert here because want generated objects named
1980 // after roorealsumpdf
1981 if (child.get<RooAbsPdf>() || (!child.get() && getObject<RooAbsPdf>(child.GetName()))) {
1982 // can add if 0 or 1 RooAddPdf ....
1983 RooAddPdf *_pdf = nullptr;
1984 bool tooMany(false);
1985 for (auto &pp : factors()) {
1986 if (auto _p = pp->get<RooAddPdf>(); _p) {
1987 if (_pdf) {
1988 _pdf = nullptr;
1989 tooMany = true;
1990 break;
1991 } // more than one!
1992 _pdf = _p;
1993 }
1994 }
1995 if (_pdf) {
1996 return xRooNode(*_pdf, *this).Add(child);
1997 } else if (!tooMany) {
1998 auto out = this->operator[]("components")->Add(child);
1999 return out;
2000 }
2001 } else if ((child.get<TH1>() || child.get<RooAbsReal>() ||
2002 (!child.get() && getObject<RooAbsReal>(child.GetName()))) &&
2003 !child.get<RooAbsPdf>()) {
2004 RooRealSumPdf *_pdf = nullptr;
2005 RooAddPdf *_backup = nullptr;
2006 bool tooMany(false);
2007 for (auto &pp : factors()) {
2008 if (auto _p = pp->get<RooRealSumPdf>(); _p) {
2009 if (_pdf) {
2010 _pdf = nullptr;
2011 tooMany = true;
2012 break;
2013 } // more than one!
2014 _pdf = _p;
2015 } else if (auto _p2 = pp->get<RooAddPdf>(); _p2) {
2016 _backup = _p2;
2017 for (auto &_pdfa : pp->components()) {
2018 if (auto _p3 = _pdfa->get<RooRealSumPdf>(); _p3) {
2019 if (_pdf) {
2020 _pdf = nullptr;
2021 tooMany = true;
2022 break;
2023 } // more than one!
2024 _pdf = _p3;
2025 }
2026 }
2027 }
2028 }
2029 if (_pdf) {
2030 return xRooNode(*_pdf, *this).Add(child);
2031 } else if (_backup) {
2032 // added *INSIDE* the addPdf -- will create a RooRealSumPdf to hold it
2033 return xRooNode(*_backup, *this).Add(child);
2034 } else if (!tooMany) {
2035 auto out = this->operator[]("samples")->Add(child);
2036 // clear our x-axis to re-evaluate
2037 fXAxis.reset();
2038 p2->setStringAttribute("xvar", nullptr);
2039 return out;
2040 }
2041 }
2042 } else if (auto s = get<RooSimultaneous>(); s) {
2043
2044 // adding to a simultaneous means adding a bin
2045 return bins().Add(child);
2046
2047 // if the child is a RooAbsPdf can just add it as a new channel using name of pdf as the channel name
2048 // if child is a histogram, will create a RooProdPdf
2049
2050 } else if (auto w = get<RooWorkspace>(); w) {
2051 child.convertForAcquisition(*this);
2052 if (child.get()) {
2053 if(auto _d = child.get<RooAbsData>()) {
2054 // don't use acquire method to import, because that adds datasets as Embeddded
2055 if (!w->import(*_d)) {
2056 return xRooNode(child.GetName(), *w->data(child.GetName()),*this);
2057 } else {
2058 throw std::runtime_error(TString::Format("Could not import dataset %s into workspace %s",child.GetName(),w->GetName()).Data());
2059 }
2060 } else {
2061 auto out = acquire(child.fComp);
2062 if (out)
2063 return xRooNode(child.GetName(), out, *this);
2064 }
2065 }
2066
2067 if (!child.empty() || child.fFolder == "!models") {
2068 // create a RooSimultaneous using the children as the channels
2069 // children either have "=" in name if specifying channel cat name or otherwise assume
2070 std::string catName = "channelCat";
2071 if (!child.empty()) {
2072 if (TString ss = child.at(0)->GetName(); ss.Contains("=")) {
2073 catName = ss(0, ss.Index('='));
2074 }
2075 }
2076 auto _cat = acquire<RooCategory>(catName.c_str(), catName.c_str());
2077 _cat->setAttribute("obs");
2078 auto out = acquireNew<RooSimultaneous>(child.GetName(), child.GetTitle(), *_cat);
2079 Info("Add", "Created model RooSimultaneous::%s in workspace %s", out->GetName(), w->GetName());
2080 return xRooNode(out, *this);
2081 }
2082 }
2083
2084 if (sOpt == "model") {
2085 // can only add a model to a workspace
2086 if (get<RooWorkspace>()) {
2087 const_cast<xRooNode &>(child).fFolder = "!models";
2088 return Add(child);
2089 }
2090 } else if (sOpt == "channel") {
2091 // can add to a model or to a workspace (creates a RooProdPdf either way)
2092 if (get<RooSimultaneous>()) {
2093 return Vary(child);
2094 } else if (get<RooWorkspace>()) {
2095 std::shared_ptr<TObject> out;
2096 child.convertForAcquisition(*this);
2097 if (child.get<RooAbsPdf>()) {
2098 out = acquire(child.fComp);
2099 } else if (!child.fComp) {
2100 out = acquireNew<RooProdPdf>(child.GetName(),
2101 (strlen(child.GetTitle())) ? child.GetTitle() : child.GetName(), RooArgList());
2102 Info("Add", "Created channel RooProdPdf::%s in workspace %s", out->GetName(), get()->GetName());
2103 }
2104 return xRooNode(out, *this);
2105 }
2106 } else if (sOpt == "sample" || sOpt == "func") {
2107 if (get<RooProdPdf>()) {
2108 auto _mainChild = mainChild();
2109 if (_mainChild.get<RooRealSumPdf>()) {
2110 return _mainChild.Add(child, sOpt == "func" ? "func" : "");
2111 } else {
2112 return (*this)["samples"]->Add(child, sOpt == "func" ? "func" : "");
2113 }
2114 }
2115 } else if (sOpt == "dataset") {
2116 if (get<RooWorkspace>()) {
2117 // const_cast<xRooNode&>(child).fFolder = "!datasets";return Add(child);
2118 return (*this).datasets().Add(child);
2119 }
2120 }
2121
2122 if (considerType) {
2123
2124 // interpret 'adding' here as dependent on the object type ...
2125 if (get<RooSimultaneous>()) {
2126 return bins().Add(child);
2127 } else if (TString(child.GetName()).Contains('=')) {
2128 return variations().Add(child);
2129 } else if (get<RooProduct>() || get<RooProdPdf>()) {
2130 return factors().Add(child);
2131 }
2132 }
2133
2134 // Nov 2022 - removed ability to add placeholders ... could bring back if rediscover need for them
2135 // if (!child.get() && child.empty() && strlen(child.GetName())) {
2136 // // can add a 'placeholder' node, note it will be deleted at the next browse
2137 // xRooNode out(child.GetName(),nullptr,*this);
2138 // out.SetTitle(child.GetTitle());
2139 // emplace_back(std::make_shared<xRooNode>(out));
2140 // // update the parent in the out node so that it's copy of the parent knows it has itself in it
2141 // // actually maybe not want this :-/
2142 // //out.fParent = std::make_shared<Node2>(*this);
2143 // for(auto o : *gROOT->GetListOfBrowsers()) {
2144 // if(auto b = dynamic_cast<TBrowser*>(o); b && b->GetBrowserImp()){
2145 // if(auto _b = dynamic_cast<TGFileBrowser*>(
2146 // dynamic_cast<TRootBrowser*>(b->GetBrowserImp())->fActBrowser ); _b) {
2147 // auto _root = _b->fRootDir;
2148 // if (!_root) _root = _b->fListTree->GetFirstItem();
2149 // if (auto item = _b->fListTree->FindItemByObj(_root,this); item) {
2150 // _b->fListTree->AddItem(item,back()->GetName(),back().get());
2151 // }
2152 // }
2153 // }
2154 // }
2155 // return out;
2156 // }
2157
2158 throw std::runtime_error(TString::Format("Cannot add %s to %s", child.GetName(), GetName()));
2159}
2160
2161std::string xRooNode::GetPath() const
2162{
2163 if (!fParent)
2164 return GetName();
2165 return fParent->GetPath() + "/" + GetName();
2166}
2167
2169{
2170 // std::cout << "deleting " << GetPath() << std::endl;
2171}
2172
2174{
2175 if (auto a = get<RooAbsArg>()) {
2176 a->setAttribute("hidden", set);
2177 // if(auto item = GetTreeItem(nullptr); item) {
2178 // if(set) item->SetColor(kRed);
2179 // else item->ClearColor();
2180 // }
2181 }
2182}
2184{
2185 auto a = get<RooAbsArg>();
2186 if (a)
2187 return a->getAttribute("hidden");
2188 return false;
2189}
2190
2192{
2193
2194 if (get() == rhs.get()) {
2195 // nothing to do because objects are identical
2196 return *this;
2197 }
2198
2199 // Info("Combine","Combining %s into %s",rhs.GetPath().c_str(),GetPath().c_str());
2200
2201 // combine components, factors, and variations ... when there is a name clash will combine on that object
2202 for (auto &c : rhs.components()) {
2203 if (auto _c = components().find(c->GetName()); _c) {
2204 _c->Combine(*c);
2205 } else {
2206 Add(*c);
2207 }
2208 }
2209
2210 for (auto &f : rhs.factors()) {
2211 if (auto _f = factors().find(f->GetName()); _f) {
2212 _f->Combine(*f);
2213 } else {
2214 Multiply(*f);
2215 }
2216 }
2217
2218 for (auto &v : rhs.variations()) {
2219 if (auto _v = variations().find(v->GetName()); _v) {
2220 _v->Combine(*v);
2221 } else {
2222 Vary(*v);
2223 }
2224 }
2225
2226 // todo: Should also transfer over binnings of observables
2227
2228 return *this;
2229}
2230
2231xRooNode xRooNode::shallowCopy(const std::string &name, std::shared_ptr<xRooNode> parent)
2232{
2233 xRooNode out(name.c_str(), nullptr,
2234 parent /*? parent : fParent -- was passing fParent for getObject benefit before fProvider concept*/);
2235 // if(!parent) out.fAcquirer = true;
2236 if (!parent)
2237 out.fProvider = fParent;
2238
2239 auto o = get();
2240 if (!o) {
2241 return out;
2242 }
2243
2244 if (auto s = get<RooSimultaneous>(); s) {
2245 auto chans = bins();
2246 if (!chans.empty()) {
2247 // create a new RooSimultaneous with shallow copies of each channel
2248
2249 std::shared_ptr<RooSimultaneous> pdf = out.acquire<RooSimultaneous>(
2250 name.c_str(), o->GetTitle(), const_cast<RooAbsCategoryLValue &>(s->indexCat()));
2251
2252 for (auto &c : chans) {
2253 TString cName(c->GetName());
2254 cName = cName(cName.Index('=') + 1, cName.Length());
2255 // by passing out as the parent, will ensure out acquires everything created
2256 auto c_copy =
2257 c->shallowCopy(name + "_" + c->get()->GetName(), std::shared_ptr<xRooNode>(&out, [](xRooNode *) {}));
2258 pdf->addPdf(*dynamic_cast<RooAbsPdf *>(c_copy.get()), cName);
2259 }
2260 out.fComp = pdf;
2261 return out;
2262 }
2263 } else if (auto p = dynamic_cast<RooProdPdf *>(o); p) {
2264 // main pdf will be copied too
2265 std::shared_ptr<RooProdPdf> pdf =
2266 std::dynamic_pointer_cast<RooProdPdf>(out.acquire(std::shared_ptr<TObject>(p->Clone(/*name.c_str()*/)), false,
2267 true)); // use clone to copy all attributes etc too
2268 auto main = mainChild();
2269 if (main) {
2270 auto newMain =
2271 std::dynamic_pointer_cast<RooAbsArg>(out.acquire(std::shared_ptr<TObject>(main->Clone()), false, true));
2272 std::cout << newMain << " " << newMain->GetName() << std::endl;
2273 // pdf->replaceServer(*pdf->pdfList().find(main->GetName()), *newMain, true, true);
2274 // const_cast<RooArgList&>(pdf->pdfList()).replace(*pdf->pdfList().find(main->GetName()), *newMain);
2275 pdf->redirectServers(RooArgList(*newMain));
2276 }
2277 out.fComp = pdf;
2278 out.sterilize();
2279 return out;
2280 }
2281
2282 return out;
2283}
2284
2286{
2287 static std::unique_ptr<cout_redirect> capture;
2288 std::string captureStr;
2289 bool doCapture = false;
2290 if (!capture && gROOT->FromPopUp()) { // FromPopUp means user executed from the context menu
2291 capture = std::make_unique<cout_redirect>(captureStr);
2292 doCapture = true;
2293 }
2294
2295 TString sOpt(opt);
2296 int depth = 0;
2297 if (sOpt.Contains("depth=")) {
2298 depth = TString(sOpt(sOpt.Index("depth=") + 6, sOpt.Length())).Atoi();
2299 sOpt.ReplaceAll(TString::Format("depth=%d", depth), "");
2300 }
2301 int indent = 0;
2302 if (sOpt.Contains("indent=")) {
2303 indent = TString(sOpt(sOpt.Index("indent=") + 7, sOpt.Length())).Atoi();
2304 sOpt.ReplaceAll(TString::Format("indent=%d", indent), "");
2305 }
2306 bool _more = sOpt.Contains("m");
2307 if (_more)
2308 sOpt.Replace(sOpt.Index("m"), 1, "");
2309 if (sOpt != "")
2310 _more = true;
2311
2312 if (indent == 0) { // only print self if not indenting (will already be printed above if tree traverse)
2313 std::cout << GetPath();
2314 if (get() && get() != this) {
2315 std::cout << ": ";
2316 if (_more || (get<RooAbsArg>() && get<RooAbsArg>()->isFundamental()) || get<RooConstVar>() ||
2317 get<RooAbsData>() || get<RooProduct>() || get<RooFitResult>()) {
2318 auto _deps = coords(false).argList(); // want to revert coords after print
2319 auto _snap = std::unique_ptr<RooAbsCollection>(_deps.snapshot());
2320 coords(); // move to coords before printing (in case this matters)
2321 get()->Print(sOpt);
2322 if (auto _fr = get<RooFitResult>(); _fr && dynamic_cast<RooStringVar *>(_fr->constPars().find(".log"))) {
2323 std::cout << "Minimization Logs:" << std::endl;
2324 std::cout << dynamic_cast<RooStringVar *>(_fr->constPars().find(".log"))->getVal() << std::endl;
2325 }
2326 _deps.assignValueOnly(*_snap);
2327 // std::cout << std::endl;
2328 } else {
2329 TString _suffix = "";
2330 if (auto _type = GetNodeType(); strlen(_type)) {
2331 // decided not to show const values until figure out how to update if value changes
2332 /*if (TString(_type)=="Const") _name += TString::Format("
2333 [%s=%g]",_type,v->get<RooConstVar>()->getVal()); else*/
2334 _suffix += TString::Format(" [%s]", _type);
2335 }
2336 if (auto fv = get<RooFormulaVar>()) {
2337 TString formu = TString::Format(" [%s]", fv->expression());
2338 for (size_t i = 0; i < fv->dependents().size(); i++) {
2339 formu.ReplaceAll(TString::Format("x[%zu]", i), fv->dependents()[i].GetName());
2340 }
2341 _suffix += formu;
2342 } else if (auto gv = get<RooGenericPdf>()) {
2343 TString formu = TString::Format(" [%s]", gv->expression());
2344 for (size_t i = 0; i < gv->dependents().size(); i++) {
2345 formu.ReplaceAll(TString::Format("x[%zu]", i), gv->dependents()[i].GetName());
2346 }
2347 _suffix += formu;
2348 }
2349 std::cout << get()->ClassName() << "::" << get()->GetName() << _suffix.Data() << std::endl;
2350 }
2351
2352 } else if (!get()) {
2353 std::cout << std::endl;
2354 }
2355 }
2356 const_cast<xRooNode *>(this)->browse();
2357 std::vector<std::string> folderNames;
2358 for (auto &k : *this) {
2359 if (std::find(folderNames.begin(), folderNames.end(), k->fFolder) == folderNames.end()) {
2360 folderNames.push_back(k->fFolder);
2361 }
2362 }
2363 for (auto &f : folderNames) {
2364 int i = 0;
2365 int iindent = indent;
2366 if (!f.empty()) {
2367 for (int j = 0; j < indent; j++)
2368 std::cout << " ";
2369 std::cout << f << std::endl;
2370 iindent += 1;
2371 }
2372 for (auto &k : *this) {
2373 if (k->fFolder != f) {
2374 i++;
2375 continue;
2376 }
2377 for (int j = 0; j < iindent; j++)
2378 std::cout << " ";
2379 std::cout << i++ << ") " << k->GetName() << " : ";
2380 if (k->get()) {
2381 if (_more || (k->get<RooAbsArg>() && k->get<RooAbsArg>()->isFundamental()) || k->get<RooConstVar>() ||
2382 k->get<RooAbsData>() /*|| k->get<RooProduct>()*/) {
2383 auto _deps = k->coords(false).argList();
2384 auto _snap = std::unique_ptr<RooAbsCollection>(_deps.snapshot());
2385 k->coords(); // move to coords before printing (in case this matters)
2386 k->get()->Print(sOpt); // assumes finishes with an endl
2387 _deps.assignValueOnly(*_snap);
2388 } else {
2389 TString _suffix = "";
2390 if (auto _type = k->GetNodeType(); strlen(_type)) {
2391 // decided not to show const values until figure out how to update if value changes
2392 /*if (TString(_type)=="Const") _name += TString::Format("
2393 [%s=%g]",_type,v->get<RooConstVar>()->getVal()); else*/
2394 _suffix += TString::Format(" [%s]", _type);
2395 }
2396 if (auto fv = k->get<RooFormulaVar>()) {
2397 TString formu = TString::Format(" [%s]", fv->expression());
2398 for (size_t j = 0; j < fv->dependents().size(); j++) {
2399 formu.ReplaceAll(TString::Format("x[%zu]", j), fv->dependents()[j].GetName());
2400 }
2401 _suffix += formu;
2402 } else if (auto gv = k->get<RooGenericPdf>()) {
2403 TString formu = TString::Format(" [%s]", gv->expression());
2404 for (size_t j = 0; j < gv->dependents().size(); j++) {
2405 formu.ReplaceAll(TString::Format("x[%zu]", j), gv->dependents()[j].GetName());
2406 }
2407 _suffix += formu;
2408 }
2409 std::cout << k->get()->ClassName() << "::" << k->get()->GetName() << _suffix.Data() << std::endl;
2410 }
2411 if (depth != 0) {
2412 k->Print(sOpt + TString::Format("depth=%dindent=%d", depth - 1, iindent + 1));
2413 }
2414 } else
2415 std::cout << " NULL " << std::endl;
2416 }
2417 }
2418 if (doCapture) {
2419 capture.reset(); // no captureStr has the string to display
2420 // inject line breaks to avoid msgbox being too wide
2421 size_t lastBreak = 0;
2422 std::string captureStrWithBreaks;
2423 for (size_t i = 0; i < captureStr.size(); i++) {
2424 captureStrWithBreaks += captureStr[i];
2425 if (captureStr[i] == '\n') {
2426 lastBreak = i;
2427 }
2428 if (i - lastBreak > 150) {
2429 captureStrWithBreaks += '\n';
2430 lastBreak = i;
2431 }
2432 }
2433 const TGWindow *w =
2434 (gROOT->GetListOfBrowsers()->At(0))
2435 ? dynamic_cast<TGWindow *>(static_cast<TBrowser *>(gROOT->GetListOfBrowsers()->At(0))->GetBrowserImp())
2436 : gClient->GetRoot();
2437 new TGMsgBox(gClient->GetRoot(), w, GetName(),
2438 captureStrWithBreaks.c_str()); //,nullptr,kMBDismiss,nullptr,kVerticalFrame,kTextLeft|kTextCenterY);
2439 }
2440}
2441
2443{
2444 if (!child.get()) {
2445
2446 if (auto v = get<RooRealVar>(); v) {
2447
2448 TString constrType = child.GetName();
2449 double mean = std::numeric_limits<double>::quiet_NaN();
2450 double sigma = mean;
2451 if (constrType.BeginsWith("gaussian(")) {
2452 // extract the mean and stddev parameters
2453 // if only one given, it is the stddev
2454 if (constrType.Contains(",")) {
2455 mean = TString(constrType(9, constrType.Index(',') - 9)).Atof();
2456 sigma = TString(constrType(constrType.Index(',') + 1, constrType.Index(')') - constrType.Index(',') + 1))
2457 .Atof();
2458 } else {
2459 mean = std::numeric_limits<double>::quiet_NaN(); // will use the var current value below to set mean
2460 sigma = TString(constrType(9, constrType.Index(')') - 9)).Atof();
2461 }
2462 constrType = "normal";
2463 } else if (constrType == "normal") {
2464 mean = 0;
2465 sigma = 1;
2466 } else if (constrType == "gaussian") {
2467 // extract parameters from the variable
2468 // use current value and error on v as constraint
2469 if (!v->hasError())
2470 throw std::runtime_error("No error on parameter for gaussian constraint");
2471 sigma = v->getError();
2472 mean = v->getVal();
2473 constrType = "normal";
2474 } else if (constrType == "poisson") {
2475 if (!v->hasError())
2476 throw std::runtime_error("No error on parameter for poisson constraint");
2477 mean = 1;
2478 sigma = pow(v->getVal() / v->getError(), 2);
2479 }
2480
2481 if (constrType == "poisson") {
2482 // use current value and error on v as constraint
2483 double tau_val = sigma;
2484 auto globs = acquire<RooRealVar>(Form("globs_%s", v->GetName()), Form("globs_%s", v->GetName()),
2485 v->getVal() * tau_val, (v->getVal() - 5 * v->getError()) * tau_val,
2486 (v->getVal() + 5 * v->getError()) * tau_val);
2487 globs->setConstant();
2488 globs->setAttribute("obs");
2489 globs->setAttribute("global");
2490 globs->setStringAttribute("nominal", TString::Format("%f", tau_val));
2491 auto tau = acquireNew<RooConstVar>(TString::Format("tau_%s", v->GetName()), "", tau_val);
2492 auto constr = acquireNew<RooPoisson>(
2493 Form("pois_%s", v->GetName()), TString::Format("Poisson Constraint of %s", v->GetTitle()), *globs,
2494 *acquireNew<RooProduct>(TString::Format("mean_%s", v->GetName()),
2495 TString::Format("Poisson Constraint of %s", globs->GetTitle()),
2496 RooArgList(*v, *tau)),
2497 true /* no rounding */);
2498
2499 auto out = Constrain(xRooNode(Form("pois_%s", GetName()), constr));
2500 if (!v->hasError())
2501 v->setError(mean / sqrt(tau_val)); // if v doesnt have an uncert, will put one on it now
2502 Info("Constrain", "Added poisson constraint pdf RooPoisson::%s (tau=%g) for %s", out->GetName(), tau_val,
2503 GetName());
2504 return out;
2505 } else if (constrType == "normal") {
2506
2507 auto globs = acquire<RooRealVar>(Form("globs_%s", v->GetName()), Form("globs_%s", v->GetName()), mean,
2508 mean - 10 * sigma, mean + 10 * sigma);
2509 globs->setAttribute("obs");
2510 globs->setAttribute("global");
2511 globs->setConstant();
2512
2513 globs->setStringAttribute("nominal", TString::Format("%f", mean));
2514 auto constr = acquireNew<RooGaussian>(
2515 Form("gaus_%s", v->GetName()), TString::Format("Gaussian Constraint of %s", v->GetTitle()), *globs, *v,
2516 *acquireNew<RooConstVar>(TString::Format("sigma_%s", v->GetName()), "", sigma));
2517 auto out = Constrain(xRooNode(Form("gaus_%s", GetName()), constr));
2518 if (!v->hasError())
2519 v->setError(sigma); // if v doesnt have an uncert, will put one on it now
2520 Info("Constrain", "Added gaussian constraint pdf RooGaussian::%s (mean=%g,sigma=%g) for %s", out->GetName(),
2521 mean, sigma, GetName());
2522 return out;
2523 }
2524 }
2525 } else if (auto p = child.get<RooAbsPdf>(); p) {
2526
2527 auto _me = get<RooAbsArg>();
2528 if (!_me) {
2529 throw std::runtime_error("Cannot constrain non arg");
2530 }
2531
2532 if (!p->dependsOn(*_me)) {
2533 throw std::runtime_error("Constraint does not depend on constrainee");
2534 }
2535
2536 // find a parent that can swallow this pdf ... either a RooProdPdf or a RooWorkspace
2537 auto x = fParent;
2538 while (x && !x->get<RooProdPdf>() && !x->get<RooSimultaneous>() && !x->get<RooWorkspace>()) {
2539 x = x->fParent;
2540 }
2541 if (!x) {
2542 throw std::runtime_error("Nowhere to put constraint");
2543 }
2544
2545 if (auto s = x->get<RooSimultaneous>(); s) {
2546 // put into every channel that features parameter
2547 x->browse();
2548 for (auto &c : *x) {
2549 if (auto a = c->get<RooAbsArg>(); a->dependsOn(*_me))
2550 c->Multiply(child);
2551 }
2552 return child;
2553 } else if (x->get<RooProdPdf>()) {
2554 return x->Multiply(child);
2555 } else {
2556 return x->Add(child, "+");
2557 }
2558 }
2559
2560 throw std::runtime_error(TString::Format("Cannot constrain %s", GetName()));
2561}
2562
2564{
2565
2566 class AutoUpdater {
2567 public:
2568 AutoUpdater(xRooNode &_n) : n(_n) {}
2569 ~AutoUpdater() { n.browse(); }
2570 xRooNode &n;
2571 };
2572 AutoUpdater xxx(*this);
2573
2574 if (fBinNumber != -1) {
2575 // scaling a bin ...
2576 if (child.get<RooAbsReal>()) { // if not child then let fall through to create a child and call self again below
2577 // doing a bin-multiplication .. the parent should have a ParamHistFunc called binFactors
2578 // if it doesn't then create one
2579 auto o = std::dynamic_pointer_cast<RooAbsReal>(acquire(child.fComp));
2580
2581 // get binFactor unless parent is a ParamHistFunc already ...
2582
2583 auto binFactors = (fParent->get<ParamHistFunc>()) ? fParent : fParent->factors().find("binFactors");
2584
2585 // it can happen in a loop over bins() that another node has moved fParent inside a product
2586 // so check for fParent having a client with the ORIGNAME:<name> attribute
2587 if (!binFactors && fParent->get<RooAbsArg>()) {
2588 for (auto c : fParent->get<RooAbsArg>()->clients()) {
2589 if (c->IsA() == RooProduct::Class() &&
2590 c->getAttribute(TString::Format("ORIGNAME:%s", fParent->get()->GetName()))) {
2591 // try getting binFactors out of this
2592 binFactors = xRooNode(*c).factors().find("binFactors");
2593 break;
2594 }
2595 }
2596 }
2597
2598 if (!binFactors) {
2599 fParent
2600 ->Multiply(TString::Format("%s_binFactors",
2601 (fParent->mainChild().get())
2602 ? fParent->mainChild()->GetName()
2603 : (fParent->get() ? fParent->get()->GetName() : fParent->GetName()))
2604 .Data(),
2605 "blankshape")
2606 .SetName("binFactors"); // creates ParamHistFunc with all pars = 1 (shared const)
2607 binFactors = fParent->factors().find("binFactors");
2608 if (!binFactors) {
2609 throw std::runtime_error(
2610 TString::Format("Could not create binFactors in parent %s", fParent->GetName()));
2611 }
2612 // auto phf = binFactors->get<ParamHistFunc>();
2613
2614 // create RooProducts for all the bins ... so that added factors don't affect selves
2615 int i = 1;
2616 for (auto &b : binFactors->bins()) {
2617 auto p = acquireNew<RooProduct>(TString::Format("%s_bin%d", binFactors->get()->GetName(), i),
2618 TString::Format("binFactors of bin %d", i), RooArgList());
2619 p->setStringAttribute("alias", TString::Format("%s=%g", binFactors->GetXaxis()->GetParent()->GetName(),
2620 binFactors->GetXaxis()->GetBinCenter(i)));
2621 b->Multiply(*p);
2622 i++;
2623 }
2624 }
2625 // then scale the relevant bin ... if the relevant bin is a "1" then just drop in our factor (inside a
2626 // RooProduct though, to avoid it getting modified by subsequent multiplies)
2627 auto _bin = binFactors->bins().at(fBinNumber - 1);
2628 if (auto phf = binFactors->get<ParamHistFunc>(); phf && _bin) {
2629#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
2630 RooArgList &pSet = phf->_paramSet;
2631#else
2632 RooArgList &pSet = const_cast<RooArgList &>(phf->paramList());
2633#endif
2634 if (strcmp(_bin->GetName(), "1") == 0) {
2635 RooArgList all;
2636 for (std::size_t i = 0; i < pSet.size(); i++) {
2637 if (int(i) != fBinNumber - 1) {
2638 all.add(*pSet.at(i));
2639 } else {
2640 all.add(*o);
2641 }
2642 }
2643 pSet.removeAll();
2644 pSet.add(all);
2645 } else {
2646 _bin->fBinNumber = -1; // to avoid infinite loop
2647 return _bin->Multiply(child, opt);
2648 }
2649 // } else {else if(_bin->get<RooProduct>()) {
2650 // // multiply the element which will just add it as a factor in the rooproduct
2651 // return _bin->Multiply(child,opt);
2652 // } else {
2653 // // not a rooproduct in this bin yet ... so need to replace with a rooproduct and
2654 // multiply that
2655 // // this avoids the undesired behaviour of shared binFactors getting all impacted by
2656 // mulitplies RooArgList all; auto new_p =
2657 // acquireNew<RooProduct>(TString::Format("%s_bin%d",binFactors->get()->GetName(),fBinNumber),TString::Format("binFactors
2658 // of bin %d",fBinNumber),RooArgList(*_bin->get<RooAbsArg>()));
2659 // new_p->setStringAttribute("alias","")
2660 // for (int i = 0; i < phf->_paramSet.size(); i++) {
2661 // if (i != fBinNumber - 1) all.add(*phf->_paramSet.at(i));
2662 // else all.add(*new_p);
2663 // }
2664 // phf->_paramSet.removeAll();
2665 // phf->_paramSet.add(all);
2666 // // now multiply that bin having converted it to RooProduct
2667 // return binFactors->bins().at(fBinNumber - 1)->Multiply(child,opt);
2668 // }
2669 }
2670 return xRooNode(*o, binFactors);
2671 }
2672 } else if (!get() && fParent) {
2673 // try to 'create' object based on parentage
2674 // add child as a temporary child to help with decision making
2675 auto _ref = emplace_back(std::shared_ptr<xRooNode>(&const_cast<xRooNode &>(child), [](TObject *) {}));
2676 try {
2677 fComp = fParent->Add(*this, "+").fComp;
2678 } catch (...) {
2679 resize(size() - 1);
2680 std::rethrow_exception(std::current_exception());
2681 }
2682 resize(size() - 1); // remove the temporarily added node
2683 }
2684
2685 if (!child.get()) {
2686 TString sOpt(opt);
2687 sOpt.ToLower();
2688 if (auto o = getObject<RooAbsReal>(child.GetName())) {
2689 auto out = Multiply(xRooNode(o, child.fParent));
2690 // have to protect bin case where get() is null (could change but then must change logic above too)
2691 if (get()) {
2692 Info("Multiply", "Scaled %s by existing factor %s::%s",
2693 mainChild().get() ? mainChild().get()->GetName() : get()->GetName(), o->ClassName(), o->GetName());
2694 }
2695 return out;
2696 } else if (sOpt == "norm") {
2697 if (TString(child.GetName()).Contains("[") && ws()) {
2698 // assume factory method wanted
2699 auto arg = ws()->factory(child.GetName());
2700 if (arg) {
2701 auto out = Multiply(*arg);
2702 if (get()) {
2703 Info("Multiply", "Scaled %s by new norm factor %s",
2704 mainChild().get() ? mainChild().get()->GetName() : get()->GetName(), out->GetName());
2705 }
2706 return out;
2707 }
2708 throw std::runtime_error(TString::Format("Failed to create new normFactor %s", child.GetName()));
2709 }
2710 auto out = Multiply(RooRealVar(child.GetName(), child.GetTitle(), 1, -1e-5, 100));
2711 if (get()) {
2712 Info("Multiply", "Scaled %s by new norm factor %s",
2713 mainChild().get() ? mainChild().get()->GetName() : get()->GetName(), out->GetName());
2714 }
2715 return out;
2716 } else if (sOpt == "shape" || sOpt == "histo" || sOpt == "blankshape") {
2717 // needs axis defined
2718 if (auto ax = GetXaxis(); ax) {
2719 auto h = std::shared_ptr<TH1>(BuildHistogram(dynamic_cast<RooAbsLValue *>(ax->GetParent()), true));
2720 h->Reset();
2721 for (int i = 1; i <= h->GetNbinsX(); i++) {
2722 h->SetBinContent(i, 1);
2723 }
2724 h->SetMinimum(0);
2725 h->SetMaximum(100);
2726 h->SetName(TString::Format(";%s", child.GetName())); // ; char indicates don't "rename" this thing
2727 h->SetTitle(child.GetTitle());
2728 if (sOpt.Contains("shape"))
2729 h->SetOption(sOpt);
2730 auto out = Multiply(*h);
2731 if (get()) {
2732 Info("Multiply", "Scaled %s by new %s factor %s",
2733 mainChild().get() ? mainChild().get()->GetName() : get()->GetName(), sOpt.Data(), out->GetName());
2734 }
2735 return out;
2736 }
2737 } else if (sOpt == "overall") {
2738 auto out = Multiply(acquireNew<RooStats::HistFactory::FlexibleInterpVar>(
2739 child.GetName(), child.GetTitle(), RooArgList(), 1, std::vector<double>(), std::vector<double>()));
2740 if (get() /* can happen this is null if on a bin node with no shapeFactors*/) {
2741 Info("Multiply", "Scaled %s by new overall factor %s",
2742 mainChild().get() ? mainChild().get()->GetName() : get()->GetName(), out->GetName());
2743 }
2744 return out;
2745 } else if (sOpt == "func" && ws()) {
2746 // need to get way to get dependencies .. can't pass all as causes circular dependencies issues.
2747 if (auto arg = ws()->factory(TString("expr::") + child.GetName())) {
2748 auto out = Multiply(*arg);
2749 if (get() /* can happen this is null if on a bin node with no shapeFactors*/) {
2750 Info("Multiply", "Scaled %s by new func factor %s",
2751 mainChild().get() ? mainChild().get()->GetName() : get()->GetName(), out->GetName());
2752 }
2753 return out;
2754 }
2755 }
2756 }
2757 if (auto h = child.get<TH1>(); h && strlen(h->GetOption()) == 0 && strlen(opt) > 0) {
2758 // put the option in the hist
2759 h->SetOption(opt);
2760 }
2761 if (auto w = get<RooWorkspace>(); w) {
2762 // just acquire
2763 std::shared_ptr<TObject> out;
2764 child.convertForAcquisition(*this);
2765 if (child.get<RooAbsReal>())
2766 out = acquire(child.fComp);
2767 return out;
2768 }
2769
2770 if (strcmp(GetName(), ".coef") == 0) { // covers both .coef and .coefs
2771 // need to add this into the relevant coef ... if its not a RooProduct, replace it with one first
2772 if (auto p = fParent->fParent->get<RooAddPdf>()) {
2773 for (size_t i = 0; i < p->pdfList().size(); i++) {
2774 if (p->pdfList().at(i) == fParent->get<RooAbsArg>()) {
2775 auto coefs = p->coefList().at(i);
2776 if (!coefs->InheritsFrom("RooProduct")) {
2777 RooArgList oldCoef;
2778 if (!(strcmp(coefs->GetName(), "1") == 0 || strcmp(coefs->GetName(), "ONE") == 0))
2779 oldCoef.add(*coefs);
2780 auto newCoefs = fParent->acquireNew<RooProduct>(
2781 TString::Format("coefs_%s", fParent->GetName()),
2782 TString::Format("coefficients for %s", fParent->GetName()), oldCoef);
2783 RooArgList oldCoefs;
2784 for (size_t j = 0; j < p->coefList().size(); j++) {
2785 if (i == j) {
2786 oldCoefs.add(*newCoefs);
2787 } else {
2788 oldCoefs.add(*p->coefList().at(j));
2789 }
2790 }
2791 const_cast<RooArgList &>(p->coefList()).removeAll();
2792 const_cast<RooArgList &>(p->coefList()).add(oldCoefs);
2793 coefs = newCoefs.get();
2794 }
2795 return xRooNode(*coefs, fParent).Multiply(child);
2796 }
2797 }
2798 }
2799 throw std::runtime_error("this coefs case is not supported");
2800 }
2801
2802 if (auto p = get<RooProduct>(); p) {
2803 std::shared_ptr<TObject> out;
2804 auto cc = child.fComp;
2805 bool isConverted = (child.convertForAcquisition(*this) != cc);
2806 if (child.get<RooAbsReal>())
2807 out = acquire(child.fComp);
2808
2809 // child may be a histfunc or a rooproduct of a histfunc and a paramhist if has stat errors
2810 if (auto _f = std::dynamic_pointer_cast<RooHistFunc>(
2811 (child.get<RooProduct>()) ? child.factors()[child.GetName()]->fComp : out);
2812 _f && _f->getAttribute("autodensity")) {
2813 // should we flag this as a density? yes if there's no other term marked as the density
2814 bool hasDensity = false;
2815 for (auto &f : factors()) {
2816 if (f->get<RooAbsArg>()->getAttribute("density")) {
2817 hasDensity = true;
2818 break;
2819 }
2820 }
2821 _f->setAttribute("density", !hasDensity && fParent && fParent->get<RooRealSumPdf>());
2822 if (_f->getAttribute("density")) {
2823
2824 // need to divide by bin widths first
2825 for (int i = 0; i < _f->dataHist().numEntries(); i++) {
2826 auto bin_pars = _f->dataHist().get(i);
2827 _f->dataHist().set(*bin_pars, _f->dataHist().weight() / _f->dataHist().binVolume(*bin_pars));
2828 }
2829 _f->setValueDirty();
2830
2831 // promote the axis vars to observables
2832 for (auto &x : xRooNode("tmp", _f).vars()) {
2833 x->get<RooAbsArg>()->setAttribute("obs");
2834 }
2835 }
2836 _f->setAttribute("autodensity", false);
2837 }
2838
2839 if (isConverted && child.get<RooHistFunc>()) {
2840 Info("Multiply", "Created %s factor %s in %s",
2841 child.get<RooAbsArg>()->getAttribute("density") ? "densityhisto" : "histo", child->GetName(),
2842 p->GetName());
2843 } else if (isConverted && child.get<ParamHistFunc>()) {
2844 Info("Multiply", "Created shape factor %s in %s", child->GetName(), p->GetName());
2845 }
2846
2847 if (auto _f = std::dynamic_pointer_cast<RooAbsReal>(out); _f) {
2848#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
2849 p->_compRSet.add(*_f);
2850#else
2851 const_cast<RooArgList &>(p->realComponents()).add(*_f);
2852#endif
2853 p->setValueDirty();
2854
2855 browse();
2856 xRooNode _out(_f, *this);
2857 for (auto &_par : _out.pars()) {
2858 if (auto s = _par->get<RooAbsArg>()->getStringAttribute("boundConstraint"); s) {
2859 bool found = false;
2860 for (auto &_constr : _par->constraints()) {
2861 if (strcmp(s, _constr->get()->GetName()) == 0) {
2862 // constraint is already included
2863 found = true;
2864 break;
2865 }
2866 }
2867 if (!found) {
2868 Info("Multiply", "Pulling in %s boundConstraint: %s", _par->GetName(), s);
2869 auto _pdf = getObject<RooAbsPdf>(s);
2870 if (!_pdf) {
2871 throw std::runtime_error("Couldn't find boundConstraint");
2872 }
2873 _par->Constrain(_pdf);
2874 }
2875 }
2876 }
2877 sterilize();
2878 return _out;
2879 }
2880 } else if (auto p2 = get<RooProdPdf>(); p2) {
2881
2882 std::shared_ptr<TObject> out;
2883 child.convertForAcquisition(*this);
2884 if (child.get<RooAbsPdf>()) {
2885 out = acquire(child.fComp);
2886 } else if (child.get<RooAbsReal>() && mainChild().get<RooRealSumPdf>()) {
2887 // cannot multiply a RooProdPdf by a non pdf
2888 throw std::runtime_error(TString::Format("Cannot multiply %s by non-pdf %s", GetName(), child.GetName()));
2889 // return mainChild().Add(child); - nov 2022 - used to do this but now replaced with exception above
2890 } else if (!child.get() || child.get<RooAbsReal>()) {
2891 // need to create or hide inside a sumpdf or rooadpdf
2892 std::shared_ptr<RooAbsPdf> _pdf;
2893 if (!child.get() && strcmp(child.GetName(), "components") == 0) {
2894 auto _sumpdf = acquireNew<RooAddPdf>(Form("%s_%s", p2->GetName(), child.GetName()),
2895 (strlen(child.GetTitle()) && strcmp(child.GetTitle(), child.GetName()))
2896 ? child.GetTitle()
2897 : p2->GetTitle(),
2898 RooArgList(), RooArgList());
2899 _pdf = _sumpdf;
2900 } else {
2901 auto _sumpdf = acquireNew<RooRealSumPdf>(
2902 Form("%s_%s", p2->GetName(), child.GetName()),
2903 (strlen(child.GetTitle()) && strcmp(child.GetTitle(), child.GetName())) ? child.GetTitle()
2904 : p2->GetTitle(),
2905 RooArgList(), RooArgList(), true);
2906 _sumpdf->setFloor(true);
2907 _pdf = _sumpdf;
2908 }
2909 _pdf->setStringAttribute("alias", child.GetName());
2910 // transfer axis attributes if present (TODO: should GetXaxis look beyond the immediate parent?)
2911 _pdf->setStringAttribute("xvar", p2->getStringAttribute("xvar"));
2912 _pdf->setStringAttribute("binning", p2->getStringAttribute("binning"));
2913 out = _pdf;
2914 Info("Multiply", "Created %s::%s in channel %s", _pdf->ClassName(), _pdf->GetName(), p2->GetName());
2915 if (child.get<RooAbsReal>())
2916 xRooNode(*out, *this).Add(child);
2917 }
2918
2919 if (auto _pdf = std::dynamic_pointer_cast<RooAbsPdf>(out); _pdf) {
2920#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
2921 const_cast<RooArgList &>(p2->pdfList()).add(*_pdf);
2922#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 26, 00)
2923 p2->_pdfNSetList.emplace_back(std::make_unique<RooArgSet>("nset"));
2924#else
2925 p->_pdfNSetList.Add(new RooArgSet("nset"));
2926#endif
2927 if (!p2->canBeExtended() && _pdf->canBeExtended()) {
2928 p2->_extendedIndex = p2->_pdfList.size() - 1;
2929 }
2930#else
2931 p2->addPdfs(RooArgSet(*_pdf));
2932#endif
2933 sterilize();
2934 browse();
2935 return xRooNode(_pdf, *this);
2936 }
2937 } else if (auto p3 = get<RooRealSumPdf>(); p3) {
2938 // multiplying all current and future components
2939 std::shared_ptr<TObject> out;
2940 child.convertForAcquisition(*this);
2941 if (child.get<RooAbsReal>()) {
2942 out = acquire(child.fComp);
2943 for (auto &c : components()) {
2944 c->Multiply(out);
2945 }
2946 TString s = p3->getStringAttribute("global_factors");
2947 if (s != "")
2948 s += ";";
2949 s += out->GetName();
2950 p3->setStringAttribute("global_factors", s);
2951 Info(
2952 "Multiply",
2953 "Flagged %s as a global factor in channel %s (is applied to all current and future samples in the channel)",
2954 out->GetName(), p3->GetName());
2955 return xRooNode(out, *this);
2956 }
2957
2958 } else if (auto p4 = get<RooAbsPdf>(); p4 && !(fParent && fParent->get<RooRealSumPdf>())) {
2959 // multiply the coefs (if this isn't part of a RooAddPdf or RooRealSumPdf then we will eventually throw exception
2960 return coefs().Multiply(child);
2961 } else if (auto p5 = get<RooAbsReal>(); p5 && (!get<RooAbsPdf>() || (fParent && fParent->get<RooRealSumPdf>()))) {
2962 // replace this obj with a RooProduct to allow for multiplication
2963
2964 // get the list of clients BEFORE creating the new interpolation ... seems list of clients is inaccurate after
2965 std::set<RooAbsArg *> cl;
2966 for (auto &arg : p5->clients()) {
2967 cl.insert(arg);
2968 }
2969
2970 // if multiple clients, see if only one client is in parentage route
2971 // if so, then assume thats the only client we should replace in
2972 if (cl.size() > 1) {
2973 if (cl.count(fParent->get<RooAbsArg>()) > 0) {
2974 cl.clear();
2975 cl.insert(fParent->get<RooAbsArg>());
2976 } else {
2977 Warning("Multiply", "Scaling %s that has multiple clients", p5->GetName());
2978 }
2979 }
2980
2981 auto new_p = acquireNew<RooProduct>(TString::Format("prod_%s", p5->GetName()), p5->GetTitle(), RooArgList(*p5));
2982 // copy attributes over
2983 for (auto &a : p5->attributes())
2984 new_p->setAttribute(a.c_str());
2985 for (auto &a : p5->stringAttributes())
2986 new_p->setStringAttribute(a.first.c_str(), a.second.c_str());
2987 if (!new_p->getStringAttribute("alias"))
2988 new_p->setStringAttribute("alias", p5->GetName());
2989 auto old_p = p5;
2990 new_p->setAttribute(Form("ORIGNAME:%s", old_p->GetName())); // used in redirectServers to say what this replaces
2991 for (auto arg : cl) {
2992 arg->redirectServers(RooArgSet(*new_p), false, true);
2993 }
2994
2995 fComp = new_p;
2996 return Multiply(child);
2997 }
2998
2999 // before giving up here, assume user wanted a norm factor type if child is just a name
3000 if (!child.get() && strlen(opt) == 0)
3001 return Multiply(child, "norm");
3002
3003 throw std::runtime_error(
3004 TString::Format("Cannot multiply %s by %s%s", GetPath().c_str(), child.GetName(),
3005 (!child.get() && strlen(opt) == 0) ? " (forgot to specify factor type?)" : ""));
3006}
3007
3009{
3010
3011 auto p5 = get<RooAbsArg>();
3012 if (!p5) {
3013 throw std::runtime_error("Only replacement of RooAbsArg is supported");
3014 }
3015 node.convertForAcquisition(*this, "func");
3016
3017 auto new_p = node.get<RooAbsArg>();
3018 if (!new_p) {
3019 throw std::runtime_error(TString::Format("Cannot replace with %s", node.GetName()));
3020 }
3021 auto out = acquire(node.fComp);
3022 new_p = std::dynamic_pointer_cast<RooAbsArg>(out).get();
3023
3024 std::set<RooAbsArg *> cl;
3025 for (auto &arg : p5->clients()) {
3026 if (arg == new_p)
3027 continue; // do not replace in self ... although redirectServers will prevent that anyway
3028 cl.insert(arg);
3029 }
3030
3031 // if multiple clients, see if only one client is in parentage route
3032 // if so, then assume thats the only client we should replace in
3033 if (cl.size() > 1) {
3034 if (fParent && fParent->get<RooAbsArg>() && cl.count(fParent->get<RooAbsArg>()) > 0) {
3035 cl.clear();
3036 cl.insert(fParent->get<RooAbsArg>());
3037 } else {
3038 std::stringstream clientList;
3039 for (auto c : cl)
3040 clientList << c->GetName() << ",";
3041 Warning("Replace", "Replacing %s in all clients: %s", p5->GetName(), clientList.str().c_str());
3042 }
3043 }
3044
3045 new_p->setAttribute(Form("ORIGNAME:%s", p5->GetName())); // used in redirectServers to say what this replaces
3046 for (auto arg : cl) {
3047 // if RooFormulaVar need to ensure the internal formula has been "constructed" otherwise will try to construct
3048 // it from the original expression that may have old parameter in it.
3049 if (auto p = dynamic_cast<RooFormulaVar *>(arg))
3050 p->ok(); // triggers creation of RooFormula
3051 arg->redirectServers(RooArgSet(*new_p), false, true);
3052 }
3053 return node;
3054}
3055
3057{
3058
3059 class AutoUpdater {
3060 public:
3061 AutoUpdater(xRooNode &_n) : n(_n) {}
3062 ~AutoUpdater() { n.browse(); }
3063 xRooNode &n;
3064 };
3065 AutoUpdater xxx(*this);
3066
3067 if (!get() && fParent) {
3068 // try to 'create' object based on parentage
3069 // add child as a temporary child to help with decision making
3070 auto _ref = emplace_back(std::shared_ptr<xRooNode>(&const_cast<xRooNode &>(child), [](TObject *) {}));
3071 try {
3072 fComp = fParent->Add(*this, "+").fComp;
3073 } catch (...) {
3074 resize(size() - 1);
3075 std::rethrow_exception(std::current_exception());
3076 }
3077 resize(size() - 1); // remove the temporarily added node
3078 }
3079
3080 if (auto p = mainChild(); p) {
3081 // variations applied to the main child if has one
3082 return p.Vary(child);
3083 }
3084
3085 if (auto s = get<RooSimultaneous>(); s && s->indexCat().IsA() == RooCategory::Class()) {
3086 // name is used as cat label
3087 std::string label = child.GetName();
3088 if (auto pos = label.find('='); pos != std::string::npos)
3089 label = label.substr(pos + 1);
3090 if (!s->indexCat().hasLabel(label)) {
3091 static_cast<RooCategory &>(const_cast<RooAbsCategoryLValue &>(s->indexCat())).defineType(label.c_str());
3092 }
3093 std::shared_ptr<TObject> out;
3094 child.convertForAcquisition(*this);
3095 if (child.get<RooAbsPdf>()) {
3096 out = acquire(child.fComp); // may create a channel from a histogram
3097 } else if (!child.fComp) {
3098 out = acquireNew<RooProdPdf>(TString::Format("%s_%s", s->GetName(), label.c_str()),
3099 (strlen(child.GetTitle())) ? child.GetTitle() : label.c_str(), RooArgList());
3100 Info("Vary", "Created channel RooProdPdf::%s in model %s", out->GetName(), s->GetName());
3101 }
3102
3103 if (auto _pdf = std::dynamic_pointer_cast<RooAbsPdf>(out); _pdf) {
3104 s->addPdf(*_pdf, label.c_str());
3105 sterilize();
3106 // clear children for reload and update shared axis
3107 clear();
3108 fXAxis.reset();
3109 browse();
3110 return xRooNode(TString::Format("%s=%s", s->indexCat().GetName(), label.data()), _pdf, *this);
3111 }
3112
3113 } else if (auto p = get<RooStats::HistFactory::FlexibleInterpVar>(); p) {
3114
3115 // child needs to be a constvar ...
3116 child.convertForAcquisition(*this);
3117 auto _c = child.get<RooConstVar>();
3118 if (!_c && child.get()) {
3119 throw std::runtime_error("Only pure consts can be set as variations of a flexible interpvar");
3120 }
3121#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
3122 double value = (_c ? _c->getVal() : p->_nominal);
3123 double nomVal = p->_nominal;
3124#else
3125 double value = (_c ? _c->getVal() : p->nominal());
3126 double nomVal = p->nominal();
3127#endif
3128
3129 TString cName(child.GetName());
3130 if (cName == "nominal") {
3131 p->setNominal(value);
3132 return *(this->variations().at(cName.Data()));
3133 }
3134 if (cName.CountChar('=') != 1) {
3135 throw std::runtime_error("unsupported variation form");
3136 }
3137 std::string parName = cName(0, cName.Index('='));
3138 double parVal = TString(cName(cName.Index('=') + 1, cName.Length())).Atof();
3139 if (parVal != 1 && parVal != -1) {
3140 throw std::runtime_error("unsupported variation magnitude");
3141 }
3142 bool high = parVal > 0;
3143
3144 if (parName.empty()) {
3145 p->setNominal(value);
3146 } else {
3147 auto v = fParent->getObject<RooRealVar>(parName);
3148 if (!v)
3149 v = fParent->acquire<RooRealVar>(parName.c_str(), parName.c_str(), -5, 5);
3150 if (!v->hasError())
3151 v->setError(1);
3152
3153 if (!p->findServer(*v)) {
3154#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
3155 p->_paramList.add(*v);
3156 p->_low.push_back(0);
3157 p->_high.push_back(0);
3158 p->_interpCode.push_back(4);
3159#else
3160 const_cast<RooListProxy &>(p->variables()).add(*v);
3161 const_cast<std::vector<double> &>(p->low()).push_back(0);
3162 const_cast<std::vector<double> &>(p->high()).push_back(0);
3163 const_cast<std::vector<int> &>(p->interpolationCodes()).push_back(4);
3164#endif
3165 v->setAttribute(Form("SYMMETRIC%s_%s", high ? "+" : "-", GetName())); // flag for symmetrized
3166 }
3167
3168 if (high) {
3169 p->setHigh(*v, value);
3170 if (v->getAttribute(Form("SYMMETRIC+_%s", GetName()))) {
3171 p->setLow(*v, 2 * nomVal - value);
3172 }
3173 v->setAttribute(Form("SYMMETRIC-_%s", GetName()), false);
3174 } else {
3175 p->setLow(*v, value);
3176 if (v->getAttribute(Form("SYMMETRIC-_%s", GetName()))) {
3177 p->setHigh(*v, 2 * nomVal - value);
3178 }
3179 v->setAttribute(Form("SYMMETRIC+_%s", GetName()), false);
3180 }
3181
3182 /*if (!unconstrained && fParent->pars()[v->GetName()].constraints().empty()) {
3183 fParent->pars()[v->GetName()].constraints().add("normal");
3184 }*/
3185 }
3186 return *(this->variations().at(cName.Data()));
3187 } else if (auto p2 = get<PiecewiseInterpolation>(); p2) {
3188 TString cName(child.GetName());
3189 if (cName.CountChar('=') != 1) {
3190 throw std::runtime_error("unsupported variation form");
3191 }
3192 TString parName = cName(0, cName.Index('='));
3193 double parVal = TString(cName(cName.Index('=') + 1, cName.Length())).Atof();
3194 if (parVal != 1 && parVal != -1) {
3195 throw std::runtime_error("unsupported variation magnitude");
3196 }
3197#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
3198 RooHistFunc *f = dynamic_cast<RooHistFunc *>(p2->_nominal.absArg());
3199 if (!f) {
3200 throw std::runtime_error(
3201 TString::Format("Interpolating %s instead of RooHistFunc", p2->_nominal.absArg()->ClassName()));
3202 }
3203#else
3204 RooHistFunc *f = dynamic_cast<RooHistFunc *>(const_cast<RooAbsReal *>(p2->nominalHist()));
3205 if (!f) {
3206 throw std::runtime_error(
3207 TString::Format("Interpolating %s instead of RooHistFunc", p2->nominalHist()->ClassName()));
3208 }
3209#endif
3210 RooHistFunc *nomf = f;
3211 RooHistFunc *otherf = nullptr;
3212 size_t i = 0;
3213 for (auto par : p2->paramList()) {
3214 if (parName == par->GetName()) {
3215 f = dynamic_cast<RooHistFunc *>((parVal > 0 ? p2->highList() : p2->lowList()).at(i));
3216 otherf = dynamic_cast<RooHistFunc *>((parVal > 0 ? p2->lowList() : p2->highList()).at(i));
3217 break;
3218 }
3219 i++;
3220 }
3221 if (i == p2->paramList().size() && !child.get<RooAbsReal>()) {
3222
3223 // need to add the parameter
3224 auto v = acquire<RooRealVar>(parName, parName, -5, 5);
3225 if (!v->hasError())
3226 v->setError(1);
3227
3228 std::shared_ptr<RooHistFunc> up(
3229 static_cast<RooHistFunc *>(f->Clone(Form("%s_%s_up", f->GetName(), parName.Data()))));
3230 std::shared_ptr<RooHistFunc> down(
3231 static_cast<RooHistFunc *>(f->Clone(Form("%s_%s_down", f->GetName(), parName.Data()))));
3232 // RooHistFunc doesn't clone it's data hist ... do it ourself (will be cloned again if imported into a ws)
3233#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
3234 std::unique_ptr<RooDataHist> h1(
3235 static_cast<RooDataHist *>(f->dataHist().Clone(Form("hist_%s", up->GetName()))));
3236 std::unique_ptr<RooDataHist> h2(
3237 static_cast<RooDataHist *>(f->dataHist().Clone(Form("hist_%s", down->GetName()))));
3238 up->_dataHist = dynamic_cast<RooDataHist *>(f->dataHist().Clone(Form("hist_%s", up->GetName())));
3239 down->_dataHist = dynamic_cast<RooDataHist *>(f->dataHist().Clone(Form("hist_%s", down->GetName())));
3240#else
3241 up->cloneAndOwnDataHist(TString::Format("hist_%s", up->GetName()));
3242 down->cloneAndOwnDataHist(TString::Format("hist_%s", down->GetName()));
3243#endif
3244 auto ups = std::dynamic_pointer_cast<RooHistFunc>(acquire(up, false, true));
3245 auto downs = std::dynamic_pointer_cast<RooHistFunc>(acquire(down, false, true));
3246#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
3247 p2->_highSet.add(*ups.get());
3248 p2->_lowSet.add(*downs.get());
3249 p2->_interpCode.push_back(4);
3250 p2->_paramSet.add(*v);
3251#else
3252 const_cast<RooArgList &>(p2->highList()).add(*ups);
3253 const_cast<RooArgList &>(p2->lowList()).add(*downs);
3254 const_cast<std::vector<int> &>(p2->interpolationCodes()).push_back(4);
3255 const_cast<RooArgList &>(p2->paramList()).add(*v);
3256#endif
3257 p2->setValueDirty();
3258 f = ((parVal > 0) ? ups : downs).get();
3259 otherf = ((parVal > 0) ? downs : ups).get();
3260 // start off with everything being symmetric
3261 f->setStringAttribute("symmetrizes", otherf->GetName());
3262 f->setStringAttribute("symmetrize_nominal", nomf->GetName());
3263 otherf->setStringAttribute("symmetrized_by", f->GetName());
3264
3265 // constrain par if required
3266 /*if (!unconstrained && fParent->pars()[v->GetName()].constraints().empty()) {
3267 fParent->pars()[v->GetName()].constraints().add("normal");
3268 }*/
3269 }
3270
3271 // child.convertForAcquisition(*this);
3272 if (f) {
3273 if (child.get())
3274 xRooNode("tmp", *f, *this) = *child.get();
3275 f->setValueDirty();
3276 xRooNode out(*f, *this);
3277 out.sterilize();
3278 return out;
3279 }
3280
3281 } else if (auto p3 = get<RooConstVar>(); p3) {
3282
3283 // never vary the universal consts ... its too dangerous
3284 if (p3->getAttribute("RooRealConstant_Factory_Object")) {
3285 throw std::runtime_error("Cannot vary pure constants");
3286 }
3287
3288 // inject a FlexibleInterpVar ...
3289
3290 // get the list of clients BEFORE creating the new interpolation ... seems list of clients is inaccurate after
3291 std::set<RooAbsArg *> cl;
3292 for (auto &arg : p3->clients()) {
3293 cl.insert(arg);
3294 }
3295 // if multiple clients, see if only one client is in parentage route
3296 // if so, then assume thats the only client we should replace in
3297 if (cl.size() > 1) {
3298 if (cl.count(fParent->get<RooAbsArg>()) > 0) {
3299 cl.clear();
3300 cl.insert(fParent->get<RooAbsArg>());
3301 } else {
3302 Warning("Vary", "Varying %s that has multiple clients", p3->GetName());
3303 }
3304 }
3305 p3->setStringAttribute("origName", p3->GetName());
3306 TString n = p3->GetName();
3307 p3->SetName(Form("%s_nominal", p3->GetName())); // if problems should perhaps not rename here
3308
3309 auto new_p = acquireNew<RooStats::HistFactory::FlexibleInterpVar>(n, p3->GetTitle(), RooArgList(), p3->getVal(),
3310 std::vector<double>(), std::vector<double>());
3311
3312 // copy attributes over
3313 for (auto &a : p3->attributes())
3314 new_p->setAttribute(a.c_str());
3315 for (auto &a : p3->stringAttributes())
3316 new_p->setStringAttribute(a.first.c_str(), a.second.c_str());
3317 // if (!new_p->getStringAttribute("alias")) new_p->setStringAttribute("alias",p->GetName());
3318 auto old_p = p3;
3319 new_p->setAttribute(Form("ORIGNAME:%s", old_p->GetName())); // used in redirectServers to say what this replaces
3320 for (auto arg : cl) {
3321 arg->redirectServers(RooArgSet(*new_p), false, true);
3322 }
3323
3324 fComp = new_p;
3325 return Vary(child);
3326
3327 } else if (auto p4 = get<RooAbsReal>(); p4) {
3328 // inject an interpolation node
3329
3330 // get the list of clients BEFORE creating the new interpolation ... seems list of clients is inaccurate after
3331 std::set<RooAbsArg *> cl;
3332 for (auto &arg : p4->clients()) {
3333 cl.insert(arg);
3334 }
3335 // if multiple clients, see if only one client is in parentage route
3336 // if so, then assume thats the only client we should replace in
3337 if (cl.size() > 1) {
3338 if (cl.count(fParent->get<RooAbsArg>()) > 0) {
3339 cl.clear();
3340 cl.insert(fParent->get<RooAbsArg>());
3341 } else {
3342 Warning("Vary", "Varying %s that has multiple clients", p4->GetName());
3343 }
3344 }
3345 p4->setStringAttribute("origName", p4->GetName());
3346 TString n = p4->GetName();
3347 p4->SetName(Form("%s_nominal", p4->GetName())); // if problems should perhaps not rename here
3348
3349 auto new_p = acquireNew<PiecewiseInterpolation>(n, p4->GetTitle(), *p4, RooArgList(), RooArgList(), RooArgList());
3350
3351 // copy attributes over
3352 for (auto &a : p4->attributes())
3353 new_p->setAttribute(a.c_str());
3354 for (auto &a : p4->stringAttributes())
3355 new_p->setStringAttribute(a.first.c_str(), a.second.c_str());
3356 // if (!new_p->getStringAttribute("alias")) new_p->setStringAttribute("alias",p->GetName());
3357 auto old_p = p4;
3358 new_p->setAttribute(Form("ORIGNAME:%s", old_p->GetName())); // used in redirectServers to say what this replaces
3359 for (auto arg : cl) {
3360 arg->redirectServers(RooArgSet(*new_p), false, true);
3361 }
3362
3363 fComp = new_p;
3364 return Vary(child);
3365 }
3366
3367 Print();
3368 throw std::runtime_error(TString::Format("Cannot vary %s with %s", GetName(), child.GetName()));
3369}
3370
3372{
3374}
3375
3376bool xRooNode::SetContent(double value, const char *par, double val)
3377{
3378 return SetContents(RooConstVar(GetName(), GetTitle(), value), par, val);
3379}
3380
3383 {
3384 if (x && b)
3385 x->setBinning(*b);
3386 if (b)
3387 delete b;
3388 }
3389 RooRealVar *x = nullptr;
3390 RooAbsBinning *b = nullptr;
3391};
3392
3394{
3395
3396 if (!get()) {
3397 fComp = std::shared_ptr<TObject>(const_cast<TObject *>(&o), [](TObject *) {});
3398 if (fParent && !fParent->find(GetName())) {
3399 // either a temporary or a placeholder so need to try genuinely adding
3400 fComp = fParent->Add(*this, "+").fComp;
3401 if (auto a = get<RooAbsArg>(); a && strcmp(a->GetName(), GetName()) && !a->getStringAttribute("alias")) {
3402 a->setStringAttribute("alias", GetName());
3403 }
3404 if (!fComp)
3405 throw std::runtime_error("Cannot determine type");
3406 return *this;
3407 }
3408 }
3409
3410 if (auto h = dynamic_cast<const TH1 *>(&o); h) {
3411 /*auto f = get<RooHistFunc>();
3412 if (!f) {
3413 // if it's a RooProduct locate child with the same name
3414 if (get<RooProduct>()) {
3415 f = factors()[GetName()]->get<RooHistFunc>();
3416 }
3417
3418
3419
3420 }*/
3421 bool _isData = get<RooAbsData>();
3422 BinningRestorer _b;
3423 if (_isData) {
3424 // need to ensure x-axis matches this h
3425 auto ax = GetXaxis();
3426 if (!ax)
3427 throw std::runtime_error("no xaxis");
3428 auto _v = dynamic_cast<RooRealVar *>(ax->GetParent());
3429 if (_v) {
3430 _b.x = _v;
3431 _b.b = dynamic_cast<RooAbsBinning *>(_v->getBinningPtr(nullptr)->Clone());
3432 if (h->GetXaxis()->IsVariableBinSize()) {
3433 _v->setBinning(RooBinning(h->GetNbinsX(), h->GetXaxis()->GetXbins()->GetArray()));
3434 } else {
3435 _v->setBinning(RooUniformBinning(h->GetXaxis()->GetXmin(), h->GetXaxis()->GetXmax(), h->GetNbinsX()));
3436 }
3437 }
3438 }
3439
3440 if (true) {
3441 for (int bin = 1; bin <= h->GetNbinsX(); bin++) {
3442 SetBinContent(bin, h->GetBinContent(bin));
3443 /*double value = h->GetBinContent(bin);
3444 auto bin_pars = f->dataHist().get(bin - 1);
3445 if (f->getAttribute("density")) {
3446 value /= f->dataHist().binVolume(*bin_pars);
3447 }
3448 f->dataHist().set(*bin_pars, value);*/
3449 if (!_isData && h->GetSumw2N() && !SetBinError(bin, h->GetBinError(bin)))
3450 throw std::runtime_error("Failed setting stat error");
3451 }
3452 return *this;
3453 }
3454 } else if (auto _c = dynamic_cast<const RooConstVar *>(&o); _c) {
3455
3456 if (auto a = get<RooAbsArg>();
3457 (a && a->isFundamental()) || get<RooConstVar>() || get<RooStats::HistFactory::FlexibleInterpVar>()) {
3458 SetBinContent(1, _c->getVal());
3459 return *this;
3460 } else if (get<RooAbsData>()) { // try to do assignment to a dataset (usually setting a bin content)
3461 SetBinContent(0, _c->getVal());
3462 return *this;
3463 }
3464 }
3465
3466 throw std::runtime_error("Assignment failed");
3467
3468 /*
3469
3470 if (fParent && !fParent->mk()) {
3471 throw std::runtime_error("mk failure");
3472 }
3473
3474 if (fComp) return *this;
3475
3476 if (o.InheritsFrom("RooAbsArg")) {
3477 fComp = acquire(std::shared_ptr<TObject>(const_cast<TObject*>(&o),[](TObject* o){}));
3478 std::dynamic_pointer_cast<RooAbsArg>(fComp)->setStringAttribute("alias",GetName());
3479 }
3480
3481 if (fComp && fParent) {
3482 fParent->incorporate(fComp);
3483 }
3484
3485
3486 return *this;
3487 */
3488}
3489
3490void xRooNode::_fit_(const char *constParValues)
3491{
3492 try {
3493 auto _pars = pars();
3494 // std::unique_ptr<RooAbsCollection> snap(_pars.argList().snapshot());
3495 TStringToken pattern(constParValues, ",");
3496 while (pattern.NextToken()) {
3497 auto idx = pattern.Index('=');
3498 TString pat = (idx == -1) ? TString(pattern) : TString(pattern(0, idx));
3499 double val =
3500 (idx == -1) ? std::numeric_limits<double>::quiet_NaN() : TString(pattern(idx + 1, pattern.Length())).Atof();
3501 for (auto p : _pars.argList()) {
3502 if (TString(p->GetName()).Contains(TRegexp(pat, true))) {
3503 p->setAttribute("Constant", true);
3504 if (!std::isnan(val)) {
3505 dynamic_cast<RooAbsRealLValue *>(p)->setVal(val);
3506 }
3507 }
3508 }
3509 }
3510 // use the first selected dataset
3511 auto _dsets = datasets();
3512 TString dsetName = "";
3513 for (auto &d : _dsets) {
3514 if (d->get()->TestBit(1 << 20)) {
3515 dsetName = d->get()->GetName();
3516 break;
3517 }
3518 }
3519 auto _nll = nll(dsetName.Data());
3520 _nll.fitConfigOptions()->SetValue("LogSize", 65536);
3521 _nll.fitConfig()->MinimizerOptions().SetPrintLevel(0);
3522 auto fr = _nll.minimize();
3523 //_pars.argList() = *snap; // restore values - irrelevant as SetFitResult will restore values
3524 if (!fr.get())
3525 throw std::runtime_error("Fit Failed");
3526 SetFitResult(fr.get());
3527 TString statusCodes;
3528 for (unsigned int i = 0; i < fr->numStatusHistory(); i++) {
3529 statusCodes += TString::Format("\n%s = %d", fr->statusLabelHistory(i), fr->statusCodeHistory(i));
3530 }
3531 const TGWindow *w =
3532 (gROOT->GetListOfBrowsers()->At(0))
3533 ? dynamic_cast<TGWindow *>(static_cast<TBrowser *>(gROOT->GetListOfBrowsers()->At(0))->GetBrowserImp())
3534 : gClient->GetRoot();
3535 if (fr->status() != 0) {
3536 new TGMsgBox(gClient->GetRoot(), w, "Fit Finished with Bad Status Code",
3537 TString::Format("%s\nData = %s\nFit Status Code = %d\nCov Quality = %d\n-------------%s",
3538 fr->GetName(), dsetName.Data(), fr->status(), fr->covQual(), statusCodes.Data()),
3540 } else if (fr->covQual() != 3 && _nll.fitConfig()->ParabErrors()) {
3541 new TGMsgBox(gClient->GetRoot(), w, "Fit Finished with Bad Covariance Quality",
3542 TString::Format("%s\nData = %s\nFit Status Code = %d\nCov Quality = %d\n-------------%s",
3543 fr->GetName(), dsetName.Data(), fr->status(), fr->covQual(), statusCodes.Data()),
3545 } else {
3546 new TGMsgBox(gClient->GetRoot(), w, "Fit Finished Successfully",
3547 TString::Format("%s\nData = %s\nFit Status Code = %d\nCov Quality = %d\n-------------%s",
3548 fr->GetName(), dsetName.Data(), fr->status(), fr->covQual(), statusCodes.Data()));
3549 }
3550 } catch (const std::exception &e) {
3551 new TGMsgBox(
3552 gClient->GetRoot(),
3553 (gROOT->GetListOfBrowsers()->At(0))
3554 ? dynamic_cast<TGWindow *>(static_cast<TBrowser *>(gROOT->GetListOfBrowsers()->At(0))->GetBrowserImp())
3555 : gClient->GetRoot(),
3556 "Exception", e.what(), kMBIconExclamation, kMBOk); // deletes self on dismiss?
3557 }
3558}
3559
3560void xRooNode::_generate_(const char *datasetName, bool expected)
3561{
3562 try {
3563 datasets().Add(datasetName, expected ? "asimov" : "toy");
3564 } catch (const std::exception &e) {
3565 new TGMsgBox(
3566 gClient->GetRoot(),
3567 (gROOT->GetListOfBrowsers()->At(0))
3568 ? dynamic_cast<TGWindow *>(static_cast<TBrowser *>(gROOT->GetListOfBrowsers()->At(0))->GetBrowserImp())
3569 : gClient->GetRoot(),
3570 "Exception", e.what(),
3571 kMBIconExclamation); // deletes self on dismiss?
3572 }
3573}
3574
3575void xRooNode::_scan_(const char *what, double nToys, const char *xvar, int nBinsX, double lowX,
3576 double highX /*, const char*, int, double, double*/, const char *constParValues)
3577{
3578 try {
3579 TString sXvar(xvar);
3580 TString sWhat(what);
3581
3582 // use the first selected dataset
3583 auto _dsets = datasets();
3584 TString dsetName = "";
3585 for (auto &d : _dsets) {
3586 if (d->get()->TestBit(1 << 20)) {
3587 dsetName = d->get()->GetName();
3588 break;
3589 }
3590 }
3591 auto _pars = pars();
3592 std::unique_ptr<RooAbsCollection> snap(_pars.argList().snapshot());
3593 TStringToken pattern(constParValues, ",");
3594 while (pattern.NextToken()) {
3595 auto idx = pattern.Index('=');
3596 TString pat = (idx == -1) ? TString(pattern) : TString(pattern(0, idx));
3597 double val =
3598 (idx == -1) ? std::numeric_limits<double>::quiet_NaN() : TString(pattern(idx + 1, pattern.Length())).Atof();
3599 for (auto par : _pars.argList()) {
3600 if (TString(par->GetName()).Contains(TRegexp(pat, true))) {
3601 par->setAttribute("Constant", true);
3602 if (!std::isnan(val)) {
3603 dynamic_cast<RooAbsRealLValue *>(par)->setVal(val);
3604 }
3605 }
3606 }
3607 }
3608 auto hs = nll(dsetName.Data()).hypoSpace(sXvar);
3609 if (nToys) {
3610 sWhat += " toys";
3611 if (nToys > 0) {
3612 sWhat += TString::Format("=%g", nToys);
3613 }
3614 }
3615 hs.SetTitle(sWhat + " scan" + ((dsetName != "") ? TString::Format(" [data=%s]", dsetName.Data()) : ""));
3616 int scanStatus = hs.scan(sWhat + " visualize", nBinsX, lowX, highX);
3617 if (scanStatus != 0) {
3618 new TGMsgBox(
3619 gClient->GetRoot(),
3620 (gROOT->GetListOfBrowsers()->At(0))
3621 ? dynamic_cast<TGWindow *>(static_cast<TBrowser *>(gROOT->GetListOfBrowsers()->At(0))->GetBrowserImp())
3622 : gClient->GetRoot(),
3623 "Scan Finished with Bad Status Code",
3624 TString::Format("%s\nData = %s\nScan Status Code = %d", hs.GetName(), dsetName.Data(), scanStatus),
3626 }
3627 hs.SetName(TUUID().AsString());
3628 if (ws()) {
3629 if (auto res = hs.result())
3630 ws()->import(*res);
3631 }
3632
3633 _pars.argList() = *snap; // restore pars
3634
3635 } catch (const std::exception &e) {
3636 new TGMsgBox(
3637 gClient->GetRoot(),
3638 (gROOT->GetListOfBrowsers()->At(0))
3639 ? dynamic_cast<TGWindow *>(static_cast<TBrowser *>(gROOT->GetListOfBrowsers()->At(0))->GetBrowserImp())
3640 : gClient->GetRoot(),
3641 "Exception", e.what(), kMBIconExclamation);
3642 }
3643}
3644
3645void xRooNode::_SetBinContent_(int bin, double value, const char *par, double parVal)
3646{
3647 try {
3648 SetBinContent(bin, value, strlen(par) > 0 ? par : nullptr, parVal);
3649 } catch (const std::exception &e) {
3650 new TGMsgBox(gClient->GetRoot(), gClient->GetRoot(), "Exception", e.what(),
3651 kMBIconExclamation); // deletes self on dismiss?
3652 }
3653}
3654
3656{
3657 try {
3658 if (!SetContent(value))
3659 throw std::runtime_error("Failed to SetContent");
3660 } catch (const std::exception &e) {
3661 new TGMsgBox(gClient->GetRoot(), gClient->GetRoot(), "Exception", e.what(),
3662 kMBIconExclamation); // deletes self on dismiss?
3663 }
3664}
3665
3666bool xRooNode::SetBinContent(int bin, double value, const char *par, double parVal)
3667{
3668
3669 // create if needed
3670 if (!get()) {
3671 if (fParent && !find(GetName())) {
3672 // if have a binning we create a histogram to match it
3673 if (auto ax = GetXaxis(); ax) {
3674 std::shared_ptr<TH1D> h;
3675 auto _b = dynamic_cast<Axis2 *>(ax)->binning();
3676 auto t = TH1::AddDirectoryStatus();
3677 TH1::AddDirectory(false);
3678 if (_b->isUniform()) {
3679 h.reset(new TH1D(GetName(), GetTitle(), _b->numBins(), _b->lowBound(), _b->highBound()));
3680 } else {
3681 h.reset(new TH1D(GetName(), GetTitle(), _b->numBins(), _b->array()));
3682 }
3683 h->SetDirectory(nullptr);
3685 h->GetXaxis()->SetName(TString::Format("%s;%s", ax->GetParent()->GetName(), ax->GetName()));
3686 fComp = h;
3687 }
3688 fComp = fParent->Add(*this, "sample").fComp;
3689 }
3690 }
3691
3692 // if it's a RooProduct locate child with the same name
3693 if (get<RooProduct>()) {
3694 return factors()[GetName()]->SetBinContent(bin, value, par, parVal);
3695 }
3696
3697 if (get<RooAbsData>()) {
3698 if (auto _data = get<RooDataSet>(); _data) {
3699 auto _ax = (bin) ? GetXaxis() : nullptr;
3700 if (!_ax && bin) {
3701 throw std::runtime_error("Cannot determine binning to fill data");
3702 }
3703 if (_ax && _ax->GetNbins() < bin) {
3704 throw std::out_of_range(TString::Format("%s range %s only has %d bins", _ax->GetParent()->GetName(),
3705 _ax->GetName(), _ax->GetNbins()));
3706 }
3707 RooArgSet obs;
3708
3709 TString cut = "";
3710
3711 for (auto _c : coords()) { // coords() moves vars to their respective coordinates too
3712 if (auto _cat = _c->get<RooAbsCategoryLValue>(); _cat) {
3713 if (cut != "")
3714 cut += " && ";
3715 cut += TString::Format("%s==%d", _cat->GetName(), _cat->getCurrentIndex());
3716 obs.add(*_cat); // note: if we ever changed coords to return clones, would need to keep coords alive
3717 } else if (auto _rv = _c->get<RooAbsRealLValue>(); _rv) {
3718 // todo: check coordRange is a single range rather than multirange
3719 if (cut != "")
3720 cut += " && ";
3721 cut +=
3722 TString::Format("%s>=%f&&%s<%f", _rv->GetName(), _rv->getMin(_rv->getStringAttribute("coordRange")),
3723 _rv->GetName(), _rv->getMax(_rv->getStringAttribute("coordRange")));
3724 obs.add(*_rv); // note: if we ever changed coords to return clones, would need to keep coords alive
3725 } else {
3726 throw std::runtime_error("SetBinContent of data: Unsupported coordinate type");
3727 }
3728 }
3729
3730 RooFormulaVar cutFormula("cut1", cut, obs); // doing this to avoid complaints about unused vars
3731 RooFormulaVar icutFormula("icut1", TString::Format("!(%s)", cut.Data()), obs);
3732
3733 TString cut2;
3734 if (_ax) {
3735 cut2 = TString::Format("%s >= %f && %s < %f", _ax->GetParent()->GetName(), _ax->GetBinLowEdge(bin),
3736 _ax->GetParent()->GetName(), _ax->GetBinUpEdge(bin));
3737 obs.add(*dynamic_cast<RooAbsArg *>(_ax->GetParent()));
3738 } else {
3739 cut2 = "1==1";
3740 }
3741 RooFormulaVar cutFormula2("cut2", cut + " && " + cut2, obs);
3742 RooFormulaVar icutFormula2("icut2", TString::Format("!(%s && %s)", cut.Data(), cut2.Data()), obs);
3743
3744 // // go up through parents looking for slice obs
3745 // auto _p = fParent;
3746 // while(_p) {
3747 // TString pName(_p->GetName());
3748 // if (auto pos = pName.Index('='); pos != -1) {
3749 // if(auto _obs = _p->getObject<RooAbsLValue>(pName(0,pos)); _obs) {
3750 // if(auto _cat = dynamic_cast<RooAbsCategoryLValue*>(_obs.get()); _cat) {
3751 // _cat->setLabel(pName(pos+1,pName.Length()));
3752 // cut += TString::Format("%s%s==%d", (cut=="")?"":" && ",_cat->GetName(),
3753 // _cat->getCurrentIndex());
3754 // } else if(auto _var = dynamic_cast<RooAbsRealLValue*>(_obs.get()); _var) {
3755 // _var->setVal(TString(pName(pos+1,pName.Length())).Atof());
3756 // // TODO: Cut for this!!
3757 // }
3758 // obs.add(*dynamic_cast<RooAbsArg*>(_obs.get()));
3759 // } else {
3760 // throw std::runtime_error("Unknown observable, could not find");
3761 // }
3762 // }
3763 // _p = _p->fParent;
3764 // }
3765
3766 // add observables to dataset if necessary
3767 RooArgSet l(obs);
3768 l.remove(*_data->get(), true, true);
3769 if (!l.empty()) {
3770 // addColumns method is buggy: https://github.com/root-project/root/issues/8787
3771 // incredibly though, addColumn works??
3772 for (auto &x : l) {
3773 _data->addColumn(*x);
3774 }
3775 // instead create a copy dataset and merge it into current
3776 // cant use merge because it drops weightVar
3777 /*RooDataSet tmp("tmp","tmp",l);
3778 for(int i=0;i<_data->numEntries();i++) tmp.add(l);
3779 _data->merge(&tmp);*/
3780 // delete _data->addColumns(l);
3781 }
3782 // before adding, ensure range is good to cover
3783 for (auto &o : obs) {
3784 if (auto v = dynamic_cast<RooRealVar *>(o); v) {
3785 if (auto dv = dynamic_cast<RooRealVar *>(_data->get()->find(v->GetName())); dv) {
3786 if (v->getMin() < dv->getMin())
3787 dv->setMin(v->getMin());
3788 if (v->getMax() > dv->getMax())
3789 dv->setMax(v->getMax());
3790 }
3791 } else if (auto c = dynamic_cast<RooCategory *>(o); c) {
3792 if (auto dc = dynamic_cast<RooCategory *>(_data->get()->find(c->GetName())); dc) {
3793 if (!dc->hasLabel(c->getCurrentLabel())) {
3794 dc->defineType(c->getCurrentLabel(), c->getCurrentIndex());
3795 }
3796 }
3797 }
3798 }
3799
3800 // using SetBinContent means dataset must take on a binned form at these coordinates
3801 // if number of entries doesnt match number of bins then will 'bin' the data
3802 if (bin) {
3803 if (auto _nentries = std::unique_ptr<RooAbsData>(_data->reduce(cutFormula))->numEntries();
3804 _nentries != _ax->GetNbins()) {
3805 auto _contents = GetBinContents(1, _ax->GetNbins());
3806
3807 if (_nentries > 0) {
3808 Info("SetBinContent", "Binning %s in channel: %s", GetName(), cut.Data());
3809 auto _reduced = std::unique_ptr<RooAbsData>(_data->reduce(icutFormula));
3810 _data->reset();
3811 for (int j = 0; j < _reduced->numEntries(); j++) {
3812 auto _obs = _reduced->get(j);
3813 _data->add(*_obs, _reduced->weight());
3814 }
3815 }
3816 for (int i = 1; i <= _ax->GetNbins(); i++) {
3817 // can skip over the bin we will be setting to save a reduce step below
3818 if (i == bin)
3819 continue;
3820 dynamic_cast<RooAbsLValue *>(_ax->GetParent())->setBin(i - 1, _ax->GetName());
3821 _data->add(obs, _contents.at(i - 1));
3822 }
3823 }
3824 }
3825 // remove existing entries
3826 if (std::unique_ptr<RooAbsData>(_data->reduce(cutFormula2))->numEntries() > 0) {
3827 auto _reduced = std::unique_ptr<RooAbsData>(_data->reduce(icutFormula2));
3828 _data->reset();
3829 for (int j = 0; j < _reduced->numEntries(); j++) {
3830 auto _obs = _reduced->get(j);
3831 _data->add(*_obs, _reduced->weight());
3832 }
3833 }
3834 if (_ax)
3835 dynamic_cast<RooAbsLValue *>(_ax->GetParent())->setBin(bin - 1, _ax->GetName());
3836 _data->add(obs, value);
3837 if (auto bb = getBrowsable(".sourceds"))
3838 return bb->SetBinContent(bin, value, par, parVal); // apply to source ds if we have one
3839 return true;
3840
3841 } else if (get<RooDataHist>()) {
3842 throw std::runtime_error("RooDataHist not supported yet");
3843 }
3844 }
3845
3846 if (auto _varies = variations(); !_varies.empty() || (par && strlen(par))) {
3847 if (!par || strlen(par) == 0) {
3848 return _varies["nominal"]->SetBinContent(bin, value, par, parVal);
3849 } else if (auto it = _varies.find(Form("%s=%g", par, parVal)); it) {
3850 return it->SetBinContent(bin, value);
3851 } else {
3852 // need to create the variation : note - if no variations existed up to now this will inject a new node
3853 // so we should redirect ourself to the new node
3854 // TODO: Do we need to redirect parents?
3855 TString s = Form("%s=%g", par, parVal);
3856 return Vary(s.Data()).SetBinContent(bin, value);
3857 }
3858 }
3859
3860 auto o = get();
3861 if (auto p = dynamic_cast<RooRealVar *>(o); p) {
3862 if (!par || strlen(par) == 0) {
3863 if (p->getMax() < value)
3864 p->setMax(value);
3865 if (p->getMin() > value)
3866 p->setMin(value);
3867 p->setVal(value);
3868 sterilize();
3869 return true;
3870 }
3871
3872 } else if (auto c = dynamic_cast<RooConstVar *>(o); c) {
3873
3874 // if parent is a FlexibleInterpVar, change the value in that .
3875 if (strcmp(c->GetName(), Form("%g", c->getVal())) == 0) {
3876 c->SetNameTitle(Form("%g", value), Form("%g", value));
3877 }
3878#if ROOT_VERSION_CODE < ROOT_VERSION(6, 24, 00)
3879 c->_value = value; // in future ROOT versions there is a changeVal method!
3880#else
3881 c->changeVal(value);
3882#endif
3883
3885 fParent->Vary(*this);
3886 }
3887
3888 sterilize();
3889 return true;
3890 } else if (auto f = dynamic_cast<RooHistFunc *>(o); f) {
3891 auto bin_pars = f->dataHist().get(bin - 1);
3892 if (f->getAttribute("density")) {
3893 value /= f->dataHist().binVolume(*bin_pars);
3894 }
3895 f->dataHist().set(*bin_pars, value);
3896 f->setValueDirty();
3897
3898 if (auto otherfName = f->getStringAttribute("symmetrized_by"); otherfName) {
3899 // broken symmetry, so update flags ...
3900 f->setStringAttribute("symmetrized_by", nullptr);
3901 if (auto x = getObject<RooAbsArg>(otherfName); x) {
3902 x->setStringAttribute("symmetrizes", nullptr);
3903 x->setStringAttribute("symmetrize_nominal", nullptr);
3904 }
3905 } else if (auto otherfName2 = f->getStringAttribute("symmetrizes"); otherfName2) {
3906 auto nomf = getObject<RooHistFunc>(f->getStringAttribute("symmetrize_nominal"));
3907 auto otherf = getObject<RooHistFunc>(otherfName2);
3908 if (nomf && otherf) {
3909 otherf->dataHist().set(*bin_pars, 2 * nomf->dataHist().weight(bin - 1) - value);
3910 otherf->setValueDirty();
3911 }
3912 }
3913 sterilize();
3914 return true;
3915 } else if (auto f2 = dynamic_cast<RooStats::HistFactory::FlexibleInterpVar *>(o); f2) {
3916 // changing nominal value
3917 f2->setNominal(value);
3918 }
3919 throw std::runtime_error(TString::Format("unable to set bin content of %s", GetPath().c_str()));
3920}
3921
3922bool xRooNode::SetBinData(int bin, double value, const char *dataName)
3923{
3924 return datasets()[dataName]->SetBinContent(bin, value);
3925}
3926
3927bool xRooNode::SetData(const TObject &obj, const char *dataName)
3928{
3929 return datasets()[dataName]->SetContents(obj);
3930}
3931
3932bool xRooNode::SetBinError(int bin, double value)
3933{
3934
3935 // if it's a RooProduct locate child with the same name
3936 if (get<RooProduct>()) {
3937 return factors()[GetName()]->SetBinError(bin, value);
3938 }
3939
3940 if (auto _varies = variations(); !_varies.empty()) {
3941 return _varies["nominal"]->SetBinError(bin, value);
3942 }
3943
3944 auto o = get();
3945
3946 if (auto f = dynamic_cast<RooHistFunc *>(o); f) {
3947
3948 // if (f->getAttribute("density")) { value /= f->dataHist().binVolume(*bin_pars); } - commented out because DON'T
3949 // convert .. sumw and sumw2 attributes will be stored not as densities
3950
3951 // NOTE: Can only do this because factors() makes parents of its children it's own parent (it isn't the parent)
3952 // If ever make factors etc part of the parentage then this would need tweaking to get to the true parent
3953 // find first parent that is a RooProduct, that is where the statFactor would live
3954 // stop as soon as we reach pdf object
3955 auto _prodParent = fParent;
3956 while (_prodParent && !_prodParent->get<RooProduct>() && !_prodParent->get<RooAbsPdf>()) {
3957 if (_prodParent->get<PiecewiseInterpolation>() && strcmp(GetName(), "nominal")) {
3958 _prodParent.reset();
3959 break; // only the 'nominal' variation can look for a statFactor outside the variation container
3960 }
3961 _prodParent = _prodParent->fParent;
3962 }
3963 auto _f_stat =
3964 (_prodParent && !_prodParent->get<RooAbsPdf>()) ? _prodParent->factors().find("statFactor") : nullptr;
3965 auto f_stat = (_f_stat) ? _f_stat->get<ParamHistFunc>() : nullptr;
3966 if (_f_stat && _f_stat->get() && !f_stat) {
3967 throw std::runtime_error("stat factor must be a paramhistfunc");
3968 }
3969
3970 // stat uncertainty lives in the "statFactor" factor, each sample has its own one,
3971 // but they can share parameters
3972 if (!f_stat) {
3973 if (value == 0)
3974 return true;
3975 TString parNames;
3976 for (auto &p : xRooNode("tmp", *f, std::shared_ptr<xRooNode>(nullptr)).vars()) {
3977 if (parNames != "")
3978 parNames += ",";
3979 parNames += p->get()->GetName();
3980 }
3981 auto h = std::unique_ptr<TH1>(f->dataHist().createHistogram(parNames
3982#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 27, 00)
3983 ,
3985#endif
3986 ));
3987 h->Reset();
3988 h->SetName("statFactor");
3989 h->SetTitle(TString::Format("StatFactor of %s", f->GetTitle()));
3990 h->SetOption("blankshape");
3991
3992 // multiply parent if is nominal
3993 auto toMultiply = this;
3994 if (strcmp(GetName(), "nominal") == 0 && fParent && fParent->get<PiecewiseInterpolation>())
3995 toMultiply = fParent.get();
3996
3997 f_stat = dynamic_cast<ParamHistFunc *>(toMultiply->Multiply(*h).get());
3998 if (!f_stat) {
3999 throw std::runtime_error("Failed creating stat shapeFactor");
4000 }
4001 }
4002
4003 auto phf = f_stat;
4004
4005 TString prefix = f->getStringAttribute("statPrefix");
4006 if (value && prefix == "") {
4007 // find the first parent that can hold components (RooAddPdf, RooRealSumPdf, RooAddition, RooWorkspace) ... use
4008 // that name for the stat factor
4009 auto _p = fParent;
4010 while (_p && !(_p->get()->InheritsFrom("RooRealSumPdf") || _p->get()->InheritsFrom("RooAddPdf") ||
4011 _p->get()->InheritsFrom("RooWorkspace") || _p->get()->InheritsFrom("RooAddition"))) {
4012 _p = _p->fParent;
4013 }
4014 prefix = TString::Format("stat_%s", (_p && _p->get<RooAbsReal>()) ? _p->get()->GetName() : f->GetName());
4015 }
4016 auto newVar = (value == 0) ? getObject<RooRealVar>("1")
4017 : acquire<RooRealVar>(Form("%s_bin%d", prefix.Data(), bin),
4018 Form("%s_bin%d", prefix.Data(), bin), 1);
4019#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
4020 RooArgList &pSet = phf->_paramSet;
4021#else
4022 RooArgList &pSet = const_cast<RooArgList &>(phf->paramList());
4023#endif
4024 auto var = dynamic_cast<RooRealVar *>(&pSet[bin - 1]);
4025
4026 if (newVar.get() != var) {
4027 // need to swap out var for newVar
4028 // replace ith element in list with new func, or inject into RooProduct
4029 RooArgList all;
4030 for (std::size_t i = 0; i < pSet.size(); i++) {
4031 if (int(i) != bin - 1) {
4032 all.add(*pSet.at(i));
4033 } else {
4034 all.add(*newVar);
4035 }
4036 }
4037 pSet.removeAll();
4038 pSet.add(all);
4039 }
4040
4041 xRooNode v((value == 0) ? *var : *newVar, *this);
4042 auto rrv = dynamic_cast<RooRealVar *>(v.get());
4043 if (strcmp(rrv->GetName(), "1") != 0) {
4044 TString origName = (f->getStringAttribute("origName")) ? f->getStringAttribute("origName") : GetName();
4045 rrv->setStringAttribute(Form("sumw2_%s", origName.Data()), TString::Format("%f", pow(value, 2)));
4046 auto bin_pars = f->dataHist().get(bin - 1);
4047 auto _binContent = f->dataHist().weight();
4048 if (f->getAttribute("density")) {
4049 _binContent *= f->dataHist().binVolume(*bin_pars);
4050 }
4051 rrv->setStringAttribute(Form("sumw_%s", origName.Data()), TString::Format("%f", _binContent));
4052 double sumw2 = 0;
4053 double sumw = 0;
4054 for (auto &[s, sv] : rrv->stringAttributes()) {
4055 if (s.find("sumw_") == 0) {
4056 sumw += TString(sv).Atof();
4057 } else if (s.find("sumw2_") == 0) {
4058 sumw2 += TString(sv).Atof();
4059 }
4060 }
4061 if (sumw2 && sumw2 != std::numeric_limits<double>::infinity()) {
4062 double tau = pow(sumw, 2) / sumw2;
4063 rrv->setError((tau < 1e-15) ? 1e15 : (/*rrv->getVal()*/ 1. / sqrt(tau))); // not sure why was rrv->getVal()?
4064 rrv->setConstant(false);
4065 // parameter must be constrained
4066 auto _constr = v.constraints();
4067 // std::cout << " setting constraint " << v.GetName() << " nomin=" << tau << std::endl;
4068 if (_constr.empty()) {
4069 rrv->setStringAttribute("boundConstraint", _constr.Add("poisson").get()->GetName());
4070 } else {
4071 auto _glob = _constr.at(0)->obs().at(0)->get<RooRealVar>();
4072 // TODO: Update any globs snapshots that are designed to match the nominal
4073 _glob->setStringAttribute("nominal", TString::Format("%f", tau));
4074 double _min = tau * (1. - 5. * sqrt(1. / tau));
4075 double _max = tau * (1. + 5. * sqrt(1. / tau));
4076 _glob->setRange(_min, _max);
4077 _glob->setVal(tau);
4078 _constr.at(0)->pp().at(0)->SetBinContent(0, tau);
4079 rrv->setStringAttribute("boundConstraint", _constr.at(0)->get()->GetName());
4080 }
4081 rrv->setRange(std::max((1. - 5. * sqrt(1. / tau)), 1e-15), 1. + 5. * sqrt(1. / tau));
4082 } else {
4083 // remove constraint
4084 if (auto _constr = v.constraints(); !_constr.empty()) {
4085 v.constraints().Remove(*_constr.at(0));
4086 }
4087 // set const if sumw2 is 0 (i.e. no error)
4088 rrv->setVal(1);
4089 rrv->setError(0);
4090 rrv->setConstant(sumw2 == 0);
4091 }
4092 }
4093
4094 return true;
4095 }
4096
4097 throw std::runtime_error(TString::Format("%s SetBinError failed", GetName()));
4098}
4099
4100std::shared_ptr<xRooNode> xRooNode::at(const std::string &name, bool browseResult) const
4101{
4102 auto res = find(name, browseResult);
4103 if (res == nullptr)
4104 throw std::out_of_range(name + " does not exist");
4105 return res;
4106}
4107
4108////////////////////////////////////////////////////////////////////////////////
4109/// The RooWorkspace this node belong to, if any
4110
4112{
4113 if (auto _w = get<RooWorkspace>(); _w)
4114 return _w;
4115 if (auto a = get<RooAbsArg>(); a && GETWS(a)) {
4116 return GETWS(a);
4117 }
4118 if (fParent)
4119 return fParent->ws();
4120 return nullptr;
4121}
4122
4124{
4125
4126 xRooNode out(".constraints", nullptr, *this);
4127
4128 std::function<RooAbsPdf *(const xRooNode &n, RooAbsArg &par, std::set<RooAbsPdf *> ignore)> getConstraint;
4129 getConstraint = [&](const xRooNode &n, RooAbsArg &par, std::set<RooAbsPdf *> ignore) {
4130 if (auto _pdf = n.get<RooAbsPdf>()) {
4131 if (ignore.count(_pdf))
4132 return (RooAbsPdf *)nullptr;
4133 ignore.insert(_pdf);
4134 }
4135 auto o = n.get<RooProdPdf>();
4136 if (!o) {
4137 if (n.get<RooSimultaneous>()) {
4138 // check all channels for a constraint if is simultaneous
4139 for (auto &c : n.bins()) {
4140 if (auto oo = getConstraint(*c, par, ignore); oo) {
4141 return oo;
4142 }
4143 }
4144 return (RooAbsPdf *)nullptr;
4145 } else if (n.get<RooAbsPdf>() && n.fParent && n.fParent->get<RooWorkspace>()) {
4146 // reached top-level pdf, which wasn't a simultaneous, so stop here
4147 return (RooAbsPdf *)nullptr;
4148 } else if (auto _ws = n.get<RooWorkspace>(); _ws) {
4149 // reached a workspace, check for any pdf depending on parameter that isnt the ignore
4150 for (auto p : _ws->allPdfs()) {
4151 if (ignore.count(static_cast<RooAbsPdf *>(p)))
4152 continue;
4153 if (p->dependsOn(par)) {
4154 out.emplace_back(std::make_shared<xRooNode>(par.GetName(), *p, *this));
4155 }
4156 }
4157 }
4158 if (!n.fParent)
4159 return (RooAbsPdf *)nullptr;
4160 return getConstraint(*n.fParent, par, ignore);
4161 }
4162 for (auto p : o->pdfList()) {
4163 if (ignore.count(static_cast<RooAbsPdf *>(p)))
4164 continue;
4165 if (p->dependsOn(par)) {
4166 out.emplace_back(std::make_shared<xRooNode>(par.GetName(), *p, *this));
4167 }
4168 }
4169 return (RooAbsPdf *)nullptr;
4170 };
4171
4172 for (auto &p : vars()) {
4173 auto v = dynamic_cast<RooAbsReal *>(p->get());
4174 if (!v)
4175 continue;
4176 if (v->getAttribute("Constant") && v != get<RooAbsReal>())
4177 continue; // skip constants unless we are getting the constraints of a parameter itself
4178 if (v->getAttribute("obs"))
4179 continue; // skip observables ... constraints constrain pars not obs
4180 getConstraint(*this, *v, {get<RooAbsPdf>()});
4181 /*if (auto c = ; c) {
4182 out.emplace_back(std::make_shared<Node2>(p->GetName(), *c, *this));
4183 }*/
4184 }
4185
4186 // finish by removing any constraint that contains another constraint for the same par
4187 // and consolidate common pars
4188 auto it = out.std::vector<std::shared_ptr<xRooNode>>::begin();
4189 while (it != out.std::vector<std::shared_ptr<xRooNode>>::end()) {
4190 bool removeIt = false;
4191 for (auto &c : out) {
4192 if (c.get() == it->get())
4193 continue;
4194 if ((*it)->get<RooAbsArg>()->dependsOn(*c->get<RooAbsArg>())) {
4195 removeIt = true;
4196 std::set<std::string> parNames;
4197 std::string _cName = c->GetName();
4198 do {
4199 parNames.insert(_cName.substr(0, _cName.find(';')));
4200 _cName = _cName.substr(_cName.find(';') + 1);
4201 } while (_cName.find(';') != std::string::npos);
4202 parNames.insert(_cName);
4203 _cName = it->get()->GetName();
4204 do {
4205 parNames.insert(_cName.substr(0, _cName.find(';')));
4206 _cName = _cName.substr(_cName.find(';') + 1);
4207 } while (_cName.find(';') != std::string::npos);
4208 parNames.insert(_cName);
4209 _cName = "";
4210 for (auto &x : parNames) {
4211 if (!_cName.empty())
4212 _cName += ";";
4213 _cName += x;
4214 }
4215 c->TNamed::SetName(_cName.c_str());
4216 break;
4217 }
4218 }
4219 if (removeIt) {
4220 it = out.erase(it);
4221 } else {
4222 ++it;
4223 }
4224 }
4225
4226 // if getting constraints of a fundamental then use the constraint names instead of the par name (because would be
4227 // all same otherwise)
4228 if (get<RooAbsArg>() && get<RooAbsArg>()->isFundamental()) {
4229 for (auto &o : out) {
4230 o->TNamed::SetName(o->get()->GetName());
4231 }
4232 }
4233
4234 return out;
4235}
4236
4237std::shared_ptr<TObject> xRooNode::convertForAcquisition(xRooNode &acquirer, const char *opt) const
4238{
4239
4240 TString sOpt(opt);
4241 sOpt.ToLower();
4242 TString sName(GetName());
4243 if (sOpt == "func")
4244 sName = TString("factory:") + sName;
4245
4246 // if arg is a histogram, will acquire it as a RooHistFunc unless no conversion
4247 // todo: could flag not to convert
4248 if (auto h = get<TH1>(); h) {
4249 TString sOpt2(h->GetOption());
4250 std::map<std::string, std::string> stringAttrs;
4251 while (sOpt2.Contains("=")) {
4252 auto pos = sOpt2.Index("=");
4253 auto start = sOpt2.Index(";") + 1;
4254 if (start > pos)
4255 start = 0;
4256 auto end = sOpt2.Index(";", pos);
4257 if (end == -1)
4258 end = sOpt2.Length();
4259 stringAttrs[sOpt2(start, pos - start)] = sOpt2(pos + 1, end - pos - 1);
4260 sOpt2 = TString(sOpt2(0, start)) + TString(sOpt2(end + 1, sOpt2.Length()));
4261 }
4262 TString newObjName = GetName();
4263 TString origName = GetName();
4264 if (origName.BeginsWith(';'))
4265 origName = origName(1, origName.Length());
4266 if (newObjName.BeginsWith(';')) {
4267 newObjName =
4268 newObjName(1, newObjName.Length()); // special case if starts with ';' then don't create a fancy name
4269 } else if (acquirer.get() && !acquirer.get<RooWorkspace>()) {
4270 newObjName = TString::Format(
4271 "%s_%s", (acquirer.mainChild().get()) ? acquirer.mainChild()->GetName() : acquirer->GetName(),
4272 newObjName.Data());
4273 }
4274 // can convert to a RooHistFunc, or RooParamHist if option contains 'shape'
4275 TString varName = h->GetXaxis()->GetName();
4276 std::string binningName = newObjName.Data();
4277 if (auto pos = varName.Index(';'); pos != -1) {
4278 binningName = varName(pos + 1, varName.Length());
4279 varName = varName(0, pos);
4280 }
4281
4282 if (varName == "xaxis" &&
4283 !acquirer.get<RooSimultaneous>()) { // default case, try to take axis var and binning from the acquirer
4284 if (auto ax = acquirer.GetXaxis(); ax) {
4285 varName = ax->GetParent()->GetName();
4286 // TODO: check the binning is consistent before using - at least will check nBins below
4287 binningName = ax->GetName();
4288 } else if (acquirer.obs().size() == 1)
4289 varName = acquirer.obs().at(0)->get()->GetName(); // TODO what if no obs but Xaxis var is defined?
4290 }
4291 auto x = acquirer.acquire<RooRealVar>(varName, h->GetXaxis()->GetTitle(), h->GetXaxis()->GetXmin(),
4292 h->GetXaxis()->GetXmax());
4293 if (x->getMin() > h->GetXaxis()->GetXmin())
4294 x->setMin(h->GetXaxis()->GetXmin());
4295 if (x->getMax() < h->GetXaxis()->GetXmax())
4296 x->setMax(h->GetXaxis()->GetXmax());
4297 if (!x->hasBinning(binningName.c_str())) {
4298 if (h->GetXaxis()->IsVariableBinSize()) {
4299 x->setBinning(RooBinning(h->GetNbinsX(), h->GetXaxis()->GetXbins()->GetArray()), binningName.c_str());
4300 } else {
4301 x->setBinning(
4302 RooUniformBinning(h->GetXaxis()->GetXmin(), h->GetXaxis()->GetXmax(), h->GetXaxis()->GetNbins()),
4303 binningName.c_str());
4304 }
4305 x->getBinning(binningName.c_str()).SetTitle(h->GetXaxis()->GetTitle());
4306 if (x->getBinningNames().size() == 2) {
4307 // this was the first binning, so copy it over to be the default binning too
4308 x->setBinning(x->getBinning(binningName.c_str()));
4309 }
4310 } else {
4311 // TODO check binning is compatible with histogram
4312 if (x->getBinning(binningName.c_str()).numBins() != h->GetNbinsX()) {
4313 throw std::runtime_error(
4314 TString::Format("binning mismatch for binning %s of %s", binningName.c_str(), x->GetName()));
4315 }
4316 }
4317
4318 std::shared_ptr<RooAbsArg> _f;
4319
4320 // if acquirer is a RooSimultaneous, will use histogram to define a channel
4321 if (acquirer.get<RooSimultaneous>()) {
4322 _f = acquirer.acquireNew<RooProdPdf>(newObjName, (strlen(h->GetTitle())) ? h->GetTitle() : h->GetName(),
4323 RooArgList());
4324 for (auto &[k, v] : stringAttrs) {
4325 _f->setStringAttribute(k.c_str(), v.c_str());
4326 }
4327 x->setAttribute("obs", true);
4328 } else if (sOpt2.Contains("shape")) {
4329 RooArgList list;
4330 for (int i = 0; i < x->getBinning(binningName.c_str()).numBins(); i++) {
4331 std::shared_ptr<RooAbsArg> arg;
4332 if (sOpt2.Contains("blankshape")) {
4333 arg = acquirer.acquire2<RooAbsArg, RooRealVar>("1", "1", 1);
4334 } else {
4335 if (!h) {
4336 arg = acquirer.acquireNew<RooRealVar>(TString::Format("%s_bin%d", newObjName.Data(), i + 1), "", 1);
4337 }
4338 if (h->GetMinimumStored() != -1111 || h->GetMaximumStored() != -1111) {
4339 arg = acquirer.acquireNew<RooRealVar>(TString::Format("%s_bin%d", newObjName.Data(), i + 1), "",
4340 h->GetBinContent(i + 1), h->GetMinimumStored(),
4341 h->GetMaximumStored());
4342 } else {
4343 arg = acquirer.acquireNew<RooRealVar>(TString::Format("%s_bin%d", newObjName.Data(), i + 1), "",
4344 h->GetBinContent(i + 1));
4345 }
4346 }
4347 list.add(*arg);
4348 }
4349 // paramhistfunc requires the binnings to be loaded as default at construction time
4350 // so load binning temporarily
4351 auto tmp = dynamic_cast<RooAbsBinning *>(x->getBinningPtr(nullptr)->Clone());
4352 x->setBinning(x->getBinning(binningName.c_str()));
4353 _f = acquirer.acquireNew<ParamHistFunc>(newObjName, h->GetTitle(), *x, list);
4354#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
4355 dynamic_cast<ParamHistFunc *>(_f.get())->_paramSet.setName("paramSet"); // so can see when print
4356#else
4357 const_cast<RooArgList &>(dynamic_cast<ParamHistFunc *>(_f.get())->paramList())
4358 .setName("paramSet"); // so can see when print
4359#endif
4360 x->setBinning(*tmp); // restore binning
4361 delete tmp;
4362 for (auto &[k, v] : stringAttrs) {
4363 _f->setStringAttribute(k.c_str(), v.c_str());
4364 }
4365 } else {
4366 auto dh = acquirer.acquireNew<RooDataHist>(Form("hist_%s", newObjName.Data()), h->GetTitle(), *x,
4367 binningName.c_str() /* binning name*/);
4368 if (!dh) {
4369 throw std::runtime_error("Couldn't make data hist");
4370 }
4371 auto f = acquirer.acquireNew<RooHistFunc>(newObjName, h->GetTitle(), *x, *dh,
4372 0 /*interpolation order between bins*/);
4373 f->forceNumInt();
4374 f->setAttribute("autodensity"); // where it gets inserted will determine if this is a density or not
4375 _f = f;
4376
4377 for (auto &[k, v] : stringAttrs) {
4378 _f->setStringAttribute(k.c_str(), v.c_str());
4379 }
4380
4381 // need to do these settings here because used in the assignment step
4382 _f->setStringAttribute("xvar", x->GetName());
4383 _f->setStringAttribute("binning", binningName.c_str());
4384 if (strcmp(_f->GetName(), origName.Data()) && !_f->getStringAttribute("alias"))
4385 _f->setStringAttribute("alias", origName);
4386
4387 // copy values over using the assignment operator - may convert to a RooProduct if there are stat uncerts
4388 xRooNode tmp(h->GetName(), _f, acquirer);
4389 tmp = *h;
4390 _f = std::dynamic_pointer_cast<RooAbsArg>(tmp.fComp); // in case got upgrade to a RooProduct
4391 }
4392
4393 _f->setStringAttribute("xvar", x->GetName());
4394 _f->setStringAttribute("binning", binningName.c_str());
4395 // style(h); // will transfer styling to object if necessary - not doing because this method used with plane hists
4396 // frequently
4397 if (strcmp(_f->GetName(), origName.Data()) && !_f->getStringAttribute("alias"))
4398 _f->setStringAttribute("alias", origName);
4399
4400 fComp = _f;
4401 return _f;
4402 } else if (!get() && sName.BeginsWith("factory:") && acquirer.ws()) {
4403 TString s(sName);
4404 s = TString(s(8, s.Length()));
4405 fComp.reset(acquirer.ws()->factory(s), [](TObject *) {});
4406 return fComp;
4407 }
4408
4409 return fComp;
4410}
4411
4412std::shared_ptr<TStyle> xRooNode::style(TObject *initObject, bool autoCreate) const
4413{
4414
4415 auto arg = get<RooAbsArg>();
4416 if (!initObject && !arg) {
4417 return nullptr;
4418 }
4419
4420 std::unique_ptr<TObject> argInitObject;
4421
4422 TString t = GetTitle();
4423 if (initObject) {
4424 t = (strlen(initObject->GetTitle())) ? initObject->GetTitle() : initObject->GetName();
4425 } else if (arg) {
4426 if (arg->getStringAttribute("style")) {
4427 t = arg->getStringAttribute("style");
4428 } else if (autoCreate) {
4429 // args will default to histo's object styling, whatever that currently may be
4430 argInitObject = std::make_unique<TH1D>(GetName(), GetTitle(), 1, 0, 1);
4431 initObject = argInitObject.get();
4432 } else {
4433 return nullptr;
4434 }
4435 }
4436
4437 std::shared_ptr<TStyle> style; // use to keep alive for access from GetStyle below, in case getObject has decided to
4438 // return the owning ptr (for some reason)
4439 if (!gROOT->GetStyle(t)) {
4440 if ((style = getObject<TStyle>(t.Data()))) {
4441 // loaded style (from workspace?) so put in list and use that
4442 gROOT->GetListOfStyles()->Add(style.get());
4443 } else {
4444 if (!autoCreate)
4445 return nullptr;
4446 // create new style - gets put in style list automatically so don't have to delete
4447 // acquire them so saved to workspaces for auto reload ...
4448 style = const_cast<xRooNode &>(*this).acquireNew<TStyle>(t.Data(),
4449 TString::Format("Style for %s component", t.Data()));
4450 if (auto x = dynamic_cast<TAttLine *>(initObject))
4451 ((TAttLine &)*style) = *x;
4452 if (auto x = dynamic_cast<TAttFill *>(initObject))
4453 ((TAttFill &)*style) = *x;
4454 if (auto x = dynamic_cast<TAttMarker *>(initObject))
4455 ((TAttMarker &)*style) = *x;
4456 gROOT->GetListOfStyles()->Add(style.get());
4457 }
4458 } else {
4459 style = std::shared_ptr<TStyle>(gROOT->GetStyle(t), [](TStyle *) {});
4460 }
4461
4462 if (arg && !arg->getStringAttribute("style")) {
4463 arg->setStringAttribute("style", style->GetName());
4464 }
4465
4466 return style;
4467}
4468
4469std::shared_ptr<TObject> xRooNode::acquire(const std::shared_ptr<TObject> &arg, bool checkFactory, bool mustBeNew)
4470{
4471 if (!arg)
4472 return nullptr;
4473 if (!fAcquirer && !get<RooWorkspace>() && fParent)
4474 return fParent->acquire(arg, checkFactory, mustBeNew);
4475
4476 // if has a workspace and our object is the workspace or is in the workspace then add this object to workspace
4477 auto _ws = (fAcquirer) ? nullptr : ws();
4478 if (_ws && (get() == _ws || _ws->arg(GetName()) || (arg && strcmp(arg->GetName(), GetName()) == 0))) {
4481 if (auto a = dynamic_cast<RooAbsArg *>(arg.get()); a) {
4482 auto out_arg = _ws->arg(a->GetName());
4483 TString aName = arg->GetName();
4484 int ii = 1;
4485 while (out_arg && mustBeNew) {
4486 a->SetName(TString::Format("%s_%d", aName.Data(), ii++));
4487 out_arg = _ws->arg(a->GetName());
4488 }
4489 if (aName != a->GetName())
4490 Warning("acquire", "Renaming to %s", a->GetName());
4491 if (!out_arg) {
4492 bool done = false;
4493 if (checkFactory) {
4494 if (auto res = _ws->factory(arg->GetName()); res) {
4495 a = res;
4496 done = true;
4497 }
4498 }
4499 if (!done && _ws->import(*a, RooFit::RecycleConflictNodes())) {
4500 if (GETWS(a) != _ws) {
4501 Info("acquire", "A copy of %s has been added to workspace %s", a->GetName(), _ws->GetName());
4502 }
4504 return nullptr;
4505 }
4506 // sanitizeWS(); // clears the caches that might exist up to now, as well interfere with getParameters calls
4507 std::set<std::string> setNames;
4508 for (auto &aa : GETWSSETS(_ws)) {
4509 if (TString(aa.first.c_str()).BeginsWith("CACHE_")) {
4510 setNames.insert(aa.first);
4511 }
4512 }
4513 for (auto &aa : setNames)
4514 ws()->removeSet(aa.c_str());
4515 out_arg = _ws->arg(a->GetName());
4516 if (GETWS(out_arg) != _ws) { // seems that when objects imported their ws isn't set
4517 out_arg->setWorkspace(*_ws);
4518 }
4519 }
4521 return std::shared_ptr<TObject>(out_arg, [](TObject *) {});
4522 } else if (auto a2 = dynamic_cast<RooAbsData *>(arg.get()); a2) {
4523 if (_ws->import(*a2, RooFit::Embedded())) {
4525 return nullptr;
4526 }
4528 return std::shared_ptr<TObject>(_ws->embeddedData(arg->GetName()), [](TObject *) {});
4529 } else if (arg->InheritsFrom("RooFitResult") || arg->InheritsFrom("TTree") || arg->IsA() == TStyle::Class()) {
4530 // ensure will have a unique name for import if must be new
4531 TNamed *aNamed = dynamic_cast<TNamed *>(arg.get());
4532 TString aName = arg->GetName();
4533 TObject *out_arg = _ws->genobj(arg->GetName());
4534 int ii = 1;
4535 while (aNamed && out_arg && mustBeNew) {
4536 aNamed->SetName(TString::Format("%s;%d", aName.Data(), ii++));
4537 out_arg = _ws->genobj(aNamed->GetName());
4538 }
4539 if (!out_arg) {
4540 if (aName != arg->GetName()) {
4541 Warning("acquire", "Renaming to %s", arg->GetName());
4542 }
4543 if (_ws->import(*arg, false /*replace existing*/)) {
4545 return nullptr;
4546 }
4547 out_arg = _ws->genobj(arg->GetName());
4548 }
4550 /* this doesnt work because caller has its own version of fParent, not the one in the browser
4551 for(auto o : *gROOT->GetListOfBrowsers()) {
4552 if(auto b = dynamic_cast<TBrowser*>(o); b){
4553 if(auto _b = dynamic_cast<TGFileBrowser*>( dynamic_cast<TRootBrowser*>(b->GetBrowserImp())->fActBrowser
4554 ); _b) { if (auto item = _b->fListTree->FindItemByObj(_b->fRootDir,this); item) { auto _tmp = _b->fListLevel;
4555 _b->fListLevel = item;
4556 bool _tmp2 = item->IsOpen();
4557 item->SetOpen(false);
4558 this->Browse(b);
4559 item->SetOpen(_tmp2);
4560 _b->fListLevel = _tmp;
4561 }
4562 }
4563 }
4564 }*/
4565 return std::shared_ptr<TObject>(out_arg, [](TObject *) {});
4566 }
4568 // Warning("acquire","Not implemented acquisition of object %s",arg->GetName());
4569 // return nullptr;
4570 }
4571 if (!mustBeNew && fProvider) {
4572 auto out = fProvider->getObject(arg->GetName(), arg->ClassName());
4573 if (out)
4574 return out;
4575 }
4576 auto _owned = find(".memory");
4577 if (!_owned) {
4578 _owned = emplace_back(std::make_shared<xRooNode>(".memory", nullptr, *this));
4579 }
4580 // look for exact name, dont use 'find' because doesnt work if trying to find "1" and it doesn't exist, will get back
4581 // idx 1 instead
4582 if (!mustBeNew) {
4583 for (auto &r : *_owned) {
4584 if (strcmp(r->GetName(), arg->GetName()) == 0 && strcmp(r->get()->ClassName(), arg->ClassName()) == 0) {
4585 return r->fComp;
4586 }
4587 }
4588 }
4589 if (!fProvider)
4590 std::cout << GetName() << " taking over " << arg->ClassName() << "::" << arg->GetName() << std::endl;
4591 /*emplace_back(std::make_shared<Node2>(".memory",nullptr,*this))*/
4592 return _owned->emplace_back(std::make_shared<xRooNode>(arg->GetName(), arg, *this))->fComp;
4593 // return arg;
4594}
4595
4596bool xRooNode::SetXaxis(const char *name, const char *title, int nbins, double low, double high)
4597{
4598 RooUniformBinning b(low, high, nbins, name);
4599 b.SetTitle(title);
4600 return SetXaxis(b);
4601}
4602
4603bool xRooNode::SetXaxis(const char *name, const char *title, int nbins, const double *bins)
4604{
4605 RooBinning b(nbins, bins, name);
4606 b.SetTitle(title);
4607 return SetXaxis(b);
4608}
4609
4611{
4612
4613 auto name = binning.GetName();
4614 double high = binning.highBound();
4615 double low = binning.lowBound();
4616 // int nbins = binning.numBins();
4617 auto title = binning.GetTitle();
4618
4619 // if have any dependents and name isn't one of them then stop
4620 auto _deps = vars();
4621 /*if(!_deps.empty() && !_deps.find(name)) {
4622 throw std::runtime_error(TString::Format("%s Does not depend on %s",GetName(),name));
4623 }*/
4624
4625 // object will need to exist
4626 if (!get()) {
4627 if (fParent && !find(GetName())) {
4628 fComp = fParent->Add(*this, "+").fComp;
4629 }
4630 }
4631
4632 auto a = get<RooAbsArg>();
4633 if (!a)
4634 throw std::runtime_error("Cannot SetXaxis of non-arg");
4635
4636 auto _x = acquire<RooRealVar>(name, title, low, high);
4637 _x->setBinning(binning, a->GetName());
4638 _x->getBinning(a->GetName()).SetTitle(title);
4639 if (_x->getBinningNames().size() == 2) {
4640 // this was the first binning, so copy it over to be the default binning too
4641 _x->setBinning(_x->getBinning(a->GetName()));
4642 } else {
4643 // ensure the default binning is wide enough to cover this range
4644 // the alternative to this would be to ensure setNormRange of all pdfs
4645 // are set to correct range (then default can be narrower than some of the named binnings)
4646 if (_x->getMax() < high)
4647 _x->setMax(high);
4648 if (_x->getMin() > low)
4649 _x->setMin(low);
4650 }
4651
4652 if (!_deps.find(name) && get<RooAbsPdf>()) {
4653 // creating a variable for a pdf we will assume it should be an observable
4654 _x->setAttribute("obs");
4655 }
4656
4657 a->setStringAttribute("xvar", _x->GetName());
4658 a->setStringAttribute("binning", a->GetName());
4659 fXAxis.reset(); // remove any existing xaxis
4660
4661 return true;
4662}
4663
4665{
4666 if (!ax)
4667 return false;
4668 if (ax->IsVariableBinSize()) {
4669 return SetXaxis(ax->GetName(), ax->GetTitle(), ax->GetNbins(), ax->GetXbins()->GetArray());
4670 } else {
4671 return SetXaxis(ax->GetName(), ax->GetTitle(), ax->GetNbins(), ax->GetXmin(), ax->GetXmax());
4672 }
4673}
4674
4675bool xRooNode::contains(const std::string &name) const
4676{
4677 return find(name, false) != nullptr;
4678}
4679
4680std::shared_ptr<xRooNode> xRooNode::find(const std::string &name, bool browseResult) const
4681{
4682 std::string partname = (name.find('/') != std::string::npos) ? name.substr(0, name.find('/')) : name;
4683 auto _s = (!get() && fParent) ? fParent->get<RooSimultaneous>()
4684 : get<RooSimultaneous>(); // makes work if doing simPdf.bins()["blah"]
4685 std::string extra = (_s) ? _s->indexCat().GetName() : "";
4686 for (auto &child : *this) {
4687 if (auto _obj = child->get(); name == child->GetName() || partname == child->GetName() ||
4688 (_obj && name == _obj->GetName()) || (_obj && partname == _obj->GetName()) ||
4689 (!extra.empty() && ((extra + "=" + name) == child->GetName() ||
4690 (extra + "=" + partname) == child->GetName()))) {
4691 if (browseResult)
4692 child->browse(); // needed so can go at()->at()->at()...
4693 if (partname != name && name != child->GetName()) {
4694 return child->at(name.substr(partname.length() + 1));
4695 }
4696 return child;
4697 }
4698 if (auto x = mainChild(); x && strcmp(child->GetName(), x.GetName()) == 0) {
4699 // can browse directly into main children as if their children were our children
4700 for (auto &child2 : x.browse()) {
4701 if (auto _obj = child2->get(); name == child2->GetName() || partname == child2->GetName() ||
4702 (_obj && name == _obj->GetName()) || (_obj && partname == _obj->GetName())) {
4703 if (browseResult)
4704 child2->browse(); // needed for onward read (or is it? there's a browse above too??)
4705 if (partname != name && name != child2->GetName()) {
4706 return child2->at(name.substr(partname.length() + 1));
4707 }
4708 return child2;
4709 }
4710 }
4711 }
4712 }
4713 // before giving up see if partName is numeric and indexes within the range
4714 if (TString s(partname); s.IsDec() && size_t(s.Atoi()) < size()) {
4715 auto child2 = at(s.Atoi());
4716 if (partname != name) {
4717 return child2->at(name.substr(partname.length() + 1));
4718 }
4719 return child2;
4720 }
4721 return nullptr;
4722}
4723
4724std::shared_ptr<xRooNode> xRooNode::operator[](const std::string &name)
4725{
4726 std::string partname = (name.find('/') != std::string::npos) ? name.substr(0, name.find('/')) : name;
4727 browse();
4728 auto _s = (!get() && fParent) ? fParent->get<RooSimultaneous>()
4729 : get<RooSimultaneous>(); // makes work if doing simPdf.bins()["blah"]
4730 std::string extra = (_s) ? _s->indexCat().GetName() : "";
4731 std::shared_ptr<xRooNode> folderNode;
4732 for (auto &child : *this) {
4733 if (name == child->GetName() || partname == child->GetName() ||
4734 (!extra.empty() &&
4735 ((extra + "=" + name) == child->GetName() || (extra + "=" + partname) == child->GetName()))) {
4736 child->browse(); // needed for onward read (or is it? there's a browse above too??)
4737 if (partname != name && name != child->GetName()) {
4738 return child->operator[](name.substr(partname.length() + 1));
4739 }
4740 return child;
4741 }
4742 if (auto x = mainChild(); strcmp(child->GetName(), x.GetName()) == 0) {
4743 // can browse directly into main children as if their children were our children
4744 for (auto &child2 : x.browse()) {
4745 if (name == child2->GetName() || partname == child2->GetName()) {
4746 child2->browse(); // needed for onward read (or is it? there's a browse above too??)
4747 if (partname != name && name != child2->GetName()) {
4748 return child2->operator[](name.substr(partname.length() + 1));
4749 }
4750 return child2;
4751 }
4752 }
4753 }
4754 if (child->fFolder == (std::string("!") + partname)) {
4755 if (!folderNode)
4756 folderNode = std::make_shared<xRooNode>(child->fFolder.c_str(), nullptr, *this);
4757 folderNode->push_back(child);
4758 }
4759 }
4760 if (folderNode) {
4761 if (partname != name) {
4762 return folderNode->operator[](name.substr(partname.length() + 1));
4763 }
4764 return folderNode;
4765 }
4766 // before giving up see if partName is numeric and indexes within the range
4767 if (TString s(partname); s.IsDec() && size_t(s.Atoi()) < size()) {
4768 auto child2 = at(s.Atoi());
4769 if (partname != name) {
4770 return child2->operator[](name.substr(partname.length() + 1));
4771 }
4772 return child2;
4773 }
4774 auto out = std::make_shared<xRooNode>(partname.c_str(), nullptr, *this); // not adding as child yeeet
4775 if (partname != name) {
4776 return out->operator[](name.substr(partname.length() + 1));
4777 }
4778 return out;
4779}
4780
4782{
4783 if (!b) {
4784 for (auto o : *gROOT->GetListOfBrowsers()) {
4785 b = dynamic_cast<TBrowser *>(o);
4786 if (!b || !b->GetBrowserImp())
4787 continue;
4788 if (auto out = GetTreeItem(b); out)
4789 return out;
4790 }
4791 return nullptr;
4792 }
4793 if (!b->GetBrowserImp())
4794 return nullptr;
4795 if (auto _b = dynamic_cast<TGFileBrowser *>(GETACTBROWSER(dynamic_cast<TRootBrowser *>(b->GetBrowserImp()))); _b) {
4796 auto _root = GETROOTDIR(_b);
4797 ;
4798 if (!_root)
4799 _root = GETLISTTREE(_b)->GetFirstItem();
4801 return GETLISTTREE(_b)->FindItemByObj(_root, const_cast<xRooNode *>(this));
4802 }
4803 return nullptr;
4804}
4805
4807{
4808 if (!b) {
4809 for (auto o : *gROOT->GetListOfBrowsers()) {
4810 b = dynamic_cast<TBrowser *>(o);
4811 if (!b || !b->GetBrowserImp())
4812 continue;
4813 if (auto out = GetListTree(b); out)
4814 return out;
4815 }
4816 return nullptr;
4817 }
4818 if (b->GetBrowserImp()) {
4819 if (auto _b = dynamic_cast<TGFileBrowser *>(GETACTBROWSER(dynamic_cast<TRootBrowser *>(b->GetBrowserImp())));
4820 _b) {
4821 auto _root = GETROOTDIR(_b);
4822 if (!_root)
4823 _root = GETLISTTREE(_b)->GetFirstItem();
4824 if (auto item = GETLISTTREE(_b)->FindItemByObj(_root, const_cast<xRooNode *>(this)); item) {
4825 return GETLISTTREE(_b);
4826 }
4827 }
4828 }
4829 return nullptr;
4830}
4831
4832void xRooNode::SetName(const char *name)
4833{
4835 if (auto a = get<RooAbsArg>(); a)
4836 a->setStringAttribute("alias", name);
4837 for (auto o : *gROOT->GetListOfBrowsers()) {
4838 if (auto b = dynamic_cast<TBrowser *>(o); b) {
4839 if (auto item = GetTreeItem(b); item) {
4840 item->SetText(name);
4841 }
4842 }
4843 }
4844}
4845
4846void xRooNode::SetTitle(const char *title)
4847{
4848 if (auto o = (get<TNamed>()); o) {
4849 if (auto c = mainChild(); c.get()) {
4850 c.SetTitle(title);
4851 }
4852 o->SetTitle(title);
4853 }
4854 TNamed::SetTitle(title);
4855}
4856
4858{
4859 if (get<RooArgList>() || (!get() && !(strlen(GetName()) > 0 && (GetName()[0] == '!')) && !fBrowseOperation))
4860 return *this; // nothing to browse - 'collection' nodes should already be populated except for folders
4861 // alternative could have been to mandate that the 'components' of a collection node are the children it has.
4862
4863 auto findByObj = [&](const std::shared_ptr<xRooNode> &n) {
4864 std::vector<std::shared_ptr<xRooNode>> &nn = *this;
4865 for (auto &c : nn) {
4866 if (c->get() == n->get() && strcmp(n->GetName(), c->GetName()) == 0)
4867 return c;
4868 }
4869 return std::shared_ptr<xRooNode>(nullptr);
4870 };
4871
4872 auto appendChildren = [&](const xRooNode &n) {
4873 size_t out = 0;
4874 const std::vector<std::shared_ptr<xRooNode>> &nn(n);
4875 for (auto &c : nn) {
4876 if (auto existing = findByObj(c); existing) {
4877 existing->fTimes++;
4878 existing->fFolder = c->fFolder; // transfer folder assignment
4879 } else {
4880 emplace_back(c);
4881 }
4882 if (!TString(c->GetName()).BeginsWith(".coef"))
4883 out++; // don't count .coef as a child, as technically part of parent
4884 }
4885 return out;
4886 };
4887
4888 const std::vector<std::shared_ptr<xRooNode>> &nn2(*this);
4889 for (auto &c : nn2) {
4890 if (strlen(c->GetName()) > 0 && (c->GetName()[0] == '.')) {
4891 c->fTimes = 1;
4892 continue;
4893 } // never auto-cleanup property children
4894 if (strcmp(c->GetName(), "!.pars") == 0) {
4895 c->fTimes = 1;
4896 continue;
4897 } // special collection, also not cleaned up
4898 if (c->get<RooWorkspace>() || c->get<TFile>()) {
4899 c->fTimes = 1;
4900 continue;
4901 } // workspaces and files not cleaned up: TODO have a nocleanup flag instead
4902 c->fTimes = 0;
4903 }
4904
4905 size_t addedChildren = 0;
4906 if (fBrowseOperation) {
4907 addedChildren += appendChildren(fBrowseOperation(this));
4908 } else {
4909 if (get<RooWorkspace>()) {
4910 addedChildren += appendChildren(datasets());
4911 }
4912
4913 // if (get<RooAbsPdf>() && ((fParent && fParent->get<RooWorkspace>()) || !fParent)) {
4914 // // top-level pdfs will also list the ".vars" property for -- should make this updateable
4915 // //if (auto x = find("!.vars"); !x) { // this is slower because it triggers a browse of !.vars
4916 // if(!contains("!.vars")) {
4917 // emplace_back(std::make_shared<Node2>("!.vars",nullptr,*this));
4918 // } /*else {
4919 // x->fTimes++;
4920 // }*/
4921 // }
4922
4923 // go through components factors and variations, adding all as children if required
4924 addedChildren += appendChildren(components());
4925 if (!get<RooWorkspace>())
4926 addedChildren += appendChildren(factors());
4927 // include coefs if any
4928 auto _coefs = coefs();
4929 if (_coefs.get() && strcmp(_coefs->GetName(), "1") != 0 && strcmp(_coefs->GetName(), "ONE") != 0) {
4930 if (_coefs.size() == 1 && _coefs.get<RooAddition>()) {
4931 if (strcmp(_coefs.at(0)->GetName(), "1") != 0 &&
4932 strcmp(_coefs.at(0)->GetName(), "ONE") != 0) { // don't add the "1"
4933 auto coef = std::make_shared<xRooNode>(".coef", *_coefs.at(0)->get(), *this);
4934 if (auto existing = findByObj(coef); existing) {
4935 existing->fTimes++;
4936 existing->fFolder = _coefs.at(0)->fFolder; // transfer folder assignment
4937 } else {
4938 emplace_back(coef);
4939 }
4940 }
4941 } else {
4942 if (auto existing = find(_coefs.GetName()); existing) {
4943 existing->fTimes++;
4944 existing->fFolder = _coefs.fFolder; // transfer folder assignment
4945 } else {
4946 emplace_back(std::make_shared<xRooNode>(_coefs));
4947 }
4948 }
4949 }
4950 addedChildren += appendChildren(variations());
4951 if (get<ParamHistFunc>() || get<RooSimultaneous>())
4952 addedChildren += appendChildren(bins());
4953 if (get<RooAbsData>())
4954 addedChildren += appendChildren(obs());
4955 }
4956 // if has no children and is a RooAbsArg, add all the proxies
4957 if (auto arg = get<RooAbsArg>(); arg && addedChildren == 0) {
4958 for (int i = 0; i < arg->numProxies(); i++) {
4959 auto _proxy = arg->getProxy(i);
4960 if (auto a = dynamic_cast<RooArgProxy *>(_proxy)) {
4961 auto c = std::make_shared<xRooNode>(TString::Format(".%s", _proxy->name()), *(a->absArg()), *this);
4962 if (auto existing = findByObj(c); existing) {
4963 existing->fTimes++;
4964 existing->fFolder = c->fFolder; // transfer folder assignment
4965 } else {
4966 emplace_back(c);
4967 }
4968 } else if (auto s = dynamic_cast<RooAbsCollection *>(_proxy)) {
4969 for (auto a2 : *s) {
4970 auto c = std::make_shared<xRooNode>(*a2, *this);
4971 if (arg->numProxies() != 1) {
4972 c->fFolder = std::string("!.") +
4973 _proxy->name(); // don't put in a folder if there's just 1 proxy (the collection)
4974 }
4975 if (auto existing = findByObj(c); existing) {
4976 existing->fTimes++;
4977 existing->fFolder = c->fFolder; // transfer folder assignment
4978 } else {
4979 emplace_back(c);
4980 }
4981 }
4982 }
4983 }
4984 /*for(auto& s : arg->servers()) {
4985 auto c = std::make_shared<xRooNode>(*s,*this);
4986 if (auto existing = findByObj(c); existing) {
4987 existing->fTimes++;
4988 existing->fFolder = c->fFolder; // transfer folder assignment
4989 } else {
4990 emplace_back(c);
4991 }
4992 }*/
4993 } else if (auto ir = get<RooStats::HypoTestInverterResult>()) {
4994 // check if we already have a hypoSpace in our memory
4995 bool hasHS = false;
4996 for (auto &c : fBrowsables) {
4997 if (strcmp(c->GetName(), ".memory") == 0 && c->get<xRooHypoSpace>()) {
4998 hasHS = true;
4999 break;
5000 }
5001 }
5002 if (!hasHS) {
5003 // add the HS
5004 auto hs =
5005 fBrowsables.emplace_back(std::make_shared<xRooNode>(".memory", std::make_shared<xRooHypoSpace>(ir), *this));
5006 // add the hypoPoints first so they appear first
5007 auto _axes = hs->get<xRooHypoSpace>()->axes();
5008
5009 int i = 0;
5010 for (auto &hp : *hs->get<xRooHypoSpace>()) {
5011 TString coordString;
5012 for (auto a : _axes) {
5013 if (a != _axes.first())
5014 coordString += ",";
5015 coordString +=
5016 TString::Format("%s=%g", a->GetName(), hp.coords->getRealValue(a->GetName(), ir->GetXValue(i)));
5017 }
5018 auto hpn = emplace_back(std::make_shared<xRooNode>(coordString, hp.hypoTestResult, hs));
5019 hpn->fTimes++;
5020 hpn->fBrowsables.emplace_back(std::make_shared<xRooNode>(
5021 ".memory", std::shared_ptr<xRooNLLVar::xRooHypoPoint>(&hp, [](xRooNLLVar::xRooHypoPoint *) {}), hpn));
5022 i++;
5023 }
5024 } else {
5025 // ensure all hypoTestResults are flagged as keep-alive
5026 std::vector<std::shared_ptr<xRooNode>> &nn = *this;
5027 for (auto &c : nn) {
5028 if (c->get<RooStats::HypoTestResult>())
5029 c->fTimes++;
5030 }
5031 }
5032 // xRooNode tests;
5033 // for(int i=0;i<ir->ArraySize();i++) {
5034 // tests.push_back(std::make_shared<xRooNode>(TString::Format("%g",ir->GetXValue(i)),*ir->GetResult(i),*this));
5035 // }
5036 // appendChildren(tests);
5037 } else if (get<RooStats::HypoTestResult>()) {
5038
5039 // create the xRooHypoPoint if necessary
5040 xRooNLLVar::xRooHypoPoint *hp = nullptr;
5041 for (auto &c : fBrowsables) {
5042 if (strcmp(c->GetName(), ".memory") == 0 && c->get<xRooNLLVar::xRooHypoPoint>()) {
5043 hp = c->get<xRooNLLVar::xRooHypoPoint>();
5044 c->fTimes++; // keep it alive
5045 break;
5046 }
5047 }
5048 if (!hp) {
5049 auto shp =
5050 std::make_shared<xRooNLLVar::xRooHypoPoint>(std::dynamic_pointer_cast<RooStats::HypoTestResult>(fComp));
5051 fBrowsables.emplace_back(std::make_shared<xRooNode>(".memory", shp, *this));
5052 hp = shp.get();
5053 }
5054
5055 xRooNode fits;
5056
5057 if (auto fit = hp->ufit()) {
5058 fits.emplace_back(std::make_shared<xRooNode>(fit, *this))->TNamed::SetName("ufit");
5059 }
5060 if (auto fit = hp->cfit_null()) {
5061 fits.emplace_back(std::make_shared<xRooNode>(fit, *this))->TNamed::SetName("cfit_null");
5062 }
5063 if (auto fit = hp->cfit_alt()) {
5064 fits.emplace_back(std::make_shared<xRooNode>(fit, *this))->TNamed::SetName("cfit_alt");
5065 }
5066 if (auto fit = hp->gfit()) {
5067 fits.emplace_back(std::make_shared<xRooNode>(fit, *this))->TNamed::SetName("gfit");
5068 }
5069 if (auto asi = hp->asimov()) {
5070 auto asiP = fits.emplace_back(std::make_shared<xRooNode>(
5071 asi->hypoTestResult ? asi->hypoTestResult : std::make_shared<RooStats::HypoTestResult>(asi->result()),
5072 *this));
5073 asiP->TNamed::SetName("asimov");
5074 asiP->fBrowsables.emplace_back(std::make_shared<xRooNode>(".memory", asi, asiP));
5075 }
5076 appendChildren(fits);
5077 }
5078
5079 // clear anything that has fTimes = 0 still
5080 auto it = std::vector<std::shared_ptr<xRooNode>>::begin();
5081 while (it != std::vector<std::shared_ptr<xRooNode>>::end()) {
5082 if (it->get()->fTimes == 0) {
5083 for (auto o : *gROOT->GetListOfBrowsers()) {
5084 auto b = dynamic_cast<TBrowser *>(o);
5085 if (b && b->GetBrowserImp()) { // browserImp is null if browser was closed
5086 // std::cout << GetPath() << " Removing " << it->get()->GetPath() << std::endl;
5087
5088 if (auto _b =
5089 dynamic_cast<TGFileBrowser *>(GETACTBROWSER(dynamic_cast<TRootBrowser *>(b->GetBrowserImp())));
5090 _b) {
5091 auto _root = GETROOTDIR(_b);
5092 if (!_root)
5093 _root = GETLISTTREE(_b)->GetFirstItem();
5094 if (auto item = GETLISTTREE(_b)->FindItemByObj(_root, this); item) {
5095 GETLISTTREE(_b)->OpenItem(item);
5096 }
5097 }
5098
5099 b->RecursiveRemove(
5100 it->get()); // problem: if obj is living in a collapsed node it wont actually get deleted
5101 /*auto _b = dynamic_cast<TGFileBrowser*>( dynamic_cast<TRootBrowser*>(b->GetBrowserImp())->fActBrowser );
5102 if (_b) {
5103 std::cout << _b->fRootDir->GetText() << std::endl;
5104 if (auto item = _b->fListTree->FindItemByObj(_b->fRootDir,it->get()); item) {
5105 std::cout << "Found obj: " << item << " " << item->GetText() << std::endl;
5106 _b->fListTree->RecursiveDeleteItem(_b->fRootDir,it->get());
5107 }
5108
5109 //b->RecursiveRemove(it->get());
5110 if (auto item = _b->fListTree->FindItemByObj(_b->fRootDir,it->get()); item) {
5111 std::cout << "Still Found obj: " << item << std::endl;
5112 }
5113 _b->fListTree->ClearViewPort();
5114
5115 }*/
5116 }
5117 }
5118 /*it->get()->ResetBit(TObject::kNotDeleted); ++it;*/ it = erase(it);
5119 } else {
5120 ++it;
5121 }
5122 }
5123
5124 return *this;
5125}
5126
5127////////////////////////////////////////////////////////////////////////////////
5128/// List of observables (global and regular) of this node.
5129
5131{
5132 xRooNode out(".obs", std::make_shared<RooArgList>(), *this);
5133 out.get<RooArgList>()->setName((GetPath() + ".obs").c_str());
5134 for (auto o : vars()) {
5135 if (o->get<RooAbsArg>()->getAttribute("obs")) {
5136 out.get<RooArgList>()->add(*o->get<RooAbsArg>());
5137 out.emplace_back(o);
5138 }
5139 }
5140 return out;
5141}
5142
5143////////////////////////////////////////////////////////////////////////////////
5144/// List of global observables of this node.
5145
5147{
5148 xRooNode out(".globs", std::make_shared<RooArgList>(), *this);
5149 out.get<RooArgList>()->setName((GetPath() + ".globs").c_str());
5150 for (auto o : obs()) {
5151 if (o->get<RooAbsArg>()->getAttribute("global")) {
5152 out.get<RooArgList>()->add(*o->get<RooAbsArg>());
5153 out.emplace_back(o);
5154 }
5155 }
5156 return out;
5157}
5158
5159////////////////////////////////////////////////////////////////////////////////
5160/// List of regular observables of this node.
5161
5163{
5164 xRooNode out(".robs", std::make_shared<RooArgList>(), *this);
5165 out.get<RooArgList>()->setName((GetPath() + ".robs").c_str());
5166 for (auto o : obs()) {
5167 if (!o->get<RooAbsArg>()->getAttribute("global")) {
5168 out.get<RooArgList>()->add(*o->get<RooAbsArg>());
5169 out.emplace_back(o);
5170 }
5171 }
5172 return out;
5173}
5174
5175////////////////////////////////////////////////////////////////////////////////
5176/// List of parameters (non-observables) of this node.
5177
5179{
5180 xRooNode out(".pars", std::make_shared<RooArgList>(), *this);
5181 out.get<RooArgList>()->setName((GetPath() + ".pars").c_str());
5182 for (auto o : vars()) {
5183 if (!o->get<RooAbsArg>()->getAttribute("obs")) {
5184 out.get<RooArgList>()->add(*(o->get<RooAbsArg>()));
5185 out.emplace_back(o);
5186 }
5187 }
5188 return out;
5189}
5190
5191////////////////////////////////////////////////////////////////////////////////
5192/// List of parameters that are currently constant
5193
5195{
5196 xRooNode out(".consts", std::make_shared<RooArgList>(), *this);
5197 out.get<RooArgList>()->setName((GetPath() + ".consts").c_str());
5198 for (auto o : pars()) {
5199 if (o->get<RooAbsArg>()->getAttribute("Constant") || o->get<RooConstVar>()) {
5200 out.get<RooArgList>()->add(*o->get<RooAbsArg>());
5201 out.emplace_back(o);
5202 }
5203 }
5204 return out;
5205}
5206
5207////////////////////////////////////////////////////////////////////////////////
5208/// List of parameters that are currently non-constant
5209/// These parameters do not have the "Constant" attribute
5210
5212{
5213 xRooNode out(".floats", std::make_shared<RooArgList>(), *this);
5214 out.get<RooArgList>()->setName((GetPath() + ".floats").c_str());
5215 for (auto o : pars()) {
5216 if (!o->get<RooAbsArg>()->getAttribute("Constant") && !o->get<RooConstVar>()) {
5217 out.get<RooArgList>()->add(*o->get<RooAbsArg>());
5218 out.emplace_back(o);
5219 }
5220 }
5221 return out;
5222}
5223
5224////////////////////////////////////////////////////////////////////////////////
5225/// List of parameters of interest: parameters marked as "of interest"
5226/// These parameters have the "poi" attribute
5227
5229{
5230 xRooNode out(".poi", std::make_shared<RooArgList>(), *this);
5231 out.get<RooArgList>()->setName((GetPath() + ".poi").c_str());
5232 for (auto o : pars()) {
5233 if (o->get<RooAbsArg>()->getAttribute("poi")) {
5234 out.get<RooArgList>()->add(*o->get<RooAbsArg>());
5235 out.emplace_back(o);
5236 }
5237 }
5238 return out;
5239}
5240
5241////////////////////////////////////////////////////////////////////////////////
5242/// List of nuisance parameters: non-constant parameters that are not marked of interest,
5243/// as well as any parameters that have been marked by the "np" attribute
5244
5246{
5247 xRooNode out(".np", std::make_shared<RooArgList>(), *this);
5248 out.get<RooArgList>()->setName((GetPath() + ".np").c_str());
5249 for (auto o : pars()) {
5250 if (o->get<RooAbsArg>()->getAttribute("np") ||
5251 (!o->get<RooAbsArg>()->getAttribute("Constant") && !o->get<RooAbsArg>()->getAttribute("poi") &&
5252 !o->get<RooConstVar>())) {
5253 out.get<RooArgList>()->add(*o->get<RooAbsArg>());
5254 out.emplace_back(o);
5255 }
5256 }
5257 return out;
5258}
5259
5260////////////////////////////////////////////////////////////////////////////////
5261/// List of prespecified parameters: non-floatable parameters
5262
5264{
5265 xRooNode out(".pp", std::make_shared<RooArgList>(), *this);
5266 out.get<RooArgList>()->setName((GetPath() + ".pp").c_str());
5267 for (auto o : pars()) {
5268 if (!o->get<RooAbsArg>()->getAttribute("np") && !o->get<RooAbsArg>()->getAttribute("poi") &&
5269 (o->get<RooAbsArg>()->getAttribute("Constant") || o->get<RooConstVar>())) {
5270 out.get<RooArgList>()->add(*o->get<RooAbsArg>());
5271 out.emplace_back(o);
5272 }
5273 }
5274 return out;
5275}
5276
5277////////////////////////////////////////////////////////////////////////////////
5278/// List of variables (observables and parameters) of this node
5279
5281{
5282 xRooNode out(".vars", std::make_shared<RooArgList>(), *this);
5283 out.get<RooArgList>()->setName((GetPath() + ".vars").c_str());
5284 if (auto coll = get<RooAbsCollection>(); coll) {
5285 for (auto &x : *this) {
5286 for (auto &y : x->vars()) {
5287 out.push_back(y);
5288 }
5289 }
5290 return out;
5291 }
5292 if (auto p = get<RooAbsArg>(); p) {
5293 // also need to get all constPars so use leafNodeServerList .. will include self if is fundamental, which is what
5294 // we want
5295 // ensure all globs appear after robs, as we rely on this ordering for picking "x" var in "reduced" method
5296 xRooNode _globs;
5297 RooArgSet allLeaves;
5298 p->leafNodeServerList(&allLeaves);
5299 for (auto &c : allLeaves) {
5300 if (c->isFundamental() || (dynamic_cast<RooConstVar *>(c) && !TString(c->GetName()).IsFloat())) {
5301 if (!c->getAttribute("global")) {
5302 out.get<RooArgList>()->add(*c);
5303 out.emplace_back(std::make_shared<xRooNode>(*c, *this));
5304 }
5305 if (c->getAttribute("global")) {
5306 _globs.emplace_back(std::make_shared<xRooNode>(*c, *this));
5307 _globs.back()->fFolder = "!globs";
5308 } else if (c->getAttribute("obs")) {
5309 out.back()->fFolder = "!robs";
5310 } else if (c->getAttribute("poi")) {
5311 out.back()->fFolder = "!poi";
5312 } else if (c->getAttribute("np") ||
5313 (!c->getAttribute("Constant") && !c->getAttribute("poi") && c->IsA() != RooConstVar::Class())) {
5314 out.back()->fFolder = "!np";
5315 } else if (!c->getAttribute("Constant") && c->IsA() != RooConstVar::Class()) {
5316 out.back()->fFolder = "!floats";
5317 } else {
5318 out.back()->fFolder = "!pp";
5319 }
5320 }
5321 }
5322 for (auto g : _globs) {
5323 out.get<RooArgList>()->add(*g->get<RooAbsArg>());
5324 out.emplace_back(g);
5325 }
5326 } else if (auto p2 = get<RooAbsData>(); p2) {
5327 for (auto a : *p2->get()) {
5328 a->setAttribute("obs");
5329 out.emplace_back(std::make_shared<xRooNode>(*a, *this));
5330 out.get<RooArgList>()->add(*a);
5331 }
5332 if (auto _dglobs = p2->getGlobalObservables()) {
5333 for (auto &a : *_dglobs) {
5334 a->setAttribute("obs");
5335 a->setAttribute("global");
5336 out.emplace_back(std::make_shared<xRooNode>(*a, *this));
5337 out.get<RooArgList>()->add(*a);
5338 }
5339 } else if (auto _globs = find(".globs"); _globs && _globs->get<RooAbsCollection>()) {
5340 for (auto &a : *_globs->get<RooAbsCollection>()) {
5341 a->setAttribute("obs");
5342 a->setAttribute("global");
5343 out.emplace_back(std::make_shared<xRooNode>(*a, *this));
5344 out.get<RooArgList>()->add(*a);
5345 }
5346 } else if (auto _ws = ws(); _ws) {
5347 if (auto _globs2 = dynamic_cast<RooArgSet *>(GETWSSNAPSHOTS(_ws).find(p2->GetName())); _globs2) {
5348 for (auto a : *_globs2) {
5349 a->setAttribute("obs");
5350 a->setAttribute("global");
5351 out.emplace_back(std::make_shared<xRooNode>(*a, *this));
5352 out.get<RooArgList>()->add(*a);
5353 }
5354 } else if (auto _gl = GETWSSETS(_ws).find("globalObservables"); _gl != GETWSSETS(_ws).end()) {
5355 for (auto &_g : _gl->second) {
5356 auto _clone = std::shared_ptr<RooAbsArg>(dynamic_cast<RooAbsArg *>(_g->Clone(_g->GetName())));
5357 if (auto v = std::dynamic_pointer_cast<RooAbsRealLValue>(_clone); v && _g->getStringAttribute("nominal"))
5358 v->setVal(TString(_g->getStringAttribute("nominal")).Atof());
5359 out.emplace_back(std::make_shared<xRooNode>(_clone, *this));
5360 out.get<RooArgList>()->add(*_clone);
5361 }
5362 } else if (fParent) {
5363 // note: this is slow in large workspaces ... too many obs to look through?
5364 std::unique_ptr<RooAbsCollection> _globs3(fParent->obs().get<RooArgList>()->selectByAttrib("global", true));
5365 // std::unique_ptr<RooAbsCollection> _globs(_ws->allVars().selectByAttrib("global",true)); - tried this to
5366 // be quicker but it wasn't
5367 for (auto &_g : *_globs3) {
5368 auto _clone = std::shared_ptr<RooAbsArg>(dynamic_cast<RooAbsArg *>(_g->Clone(_g->GetName())));
5369 if (auto v = std::dynamic_pointer_cast<RooAbsRealLValue>(_clone); v && _g->getStringAttribute("nominal"))
5370 v->setVal(TString(_g->getStringAttribute("nominal")).Atof());
5371 out.emplace_back(std::make_shared<xRooNode>(_clone, *this));
5372 out.get<RooArgList>()->add(*_clone);
5373 }
5374 }
5375 }
5376 } else if (auto w = get<RooWorkspace>(); w) {
5377 for (auto a : w->allVars()) {
5378 out.emplace_back(std::make_shared<xRooNode>(*a, *this));
5379 out.get<RooArgList>()->add(*a);
5380 }
5381 // add all cats as well
5382 for (auto a : w->allCats()) {
5383 out.emplace_back(std::make_shared<xRooNode>(*a, *this));
5384 out.get<RooArgList>()->add(*a);
5385 }
5386 }
5387 return out;
5388}
5389
5391{
5392 xRooNode out(".components", nullptr, *this);
5393
5394 if (auto p = get<RooAddPdf>(); p) {
5395 // only add each pdf once (the coefs will be accumulated in coefs() method) ...
5396 std::set<RooAbsArg *> donePdfs;
5397 for (auto &o : p->pdfList()) {
5398 if (donePdfs.count(o))
5399 continue;
5400 out.emplace_back(std::make_shared<xRooNode>(*o, *this));
5401 donePdfs.insert(o);
5402 }
5403 } else if (auto p2 = get<RooRealSumPdf>(); p2) {
5404 // check for common prefixes and suffixes, will use to define aliases to shorten names
5405 // if have more than 1 function
5406 // TString commonPrefix=""; TString commonSuffix="";
5407 // if (p->funcList().size() > 1) {
5408 // bool checked=false;
5409 // for(auto& o : p->funcList()) {
5410 // if (!checked) {
5411 // commonPrefix = o->GetName(); commonSuffix = o->GetName(); checked=true;
5412 // } else {
5413 //
5414 // }
5415 // }
5416 // }
5417 std::set<RooAbsArg *> doneFuncs;
5418 for (auto &o : p2->funcList()) {
5419 if (doneFuncs.count(o))
5420 continue;
5421 out.emplace_back(std::make_shared<xRooNode>(*o, *this));
5422 doneFuncs.insert(o);
5423 }
5424 } else if (auto p3 = get<RooAddition>(); p3) {
5425 for (auto &o : p3->list()) {
5426 out.emplace_back(std::make_shared<xRooNode>(*o, *this));
5427 }
5428 } else if (auto p4 = get<RooAbsCollection>(); p4) {
5429 for (auto &a : *p4) {
5430 out.emplace_back(std::make_shared<xRooNode>(*a, *this));
5431 }
5432 } else if (auto p5 = get<RooWorkspace>(); p5) {
5433 for (auto &o : p5->components()) {
5434 // only top-level nodes (only clients are integrals or things that aren't part of the workspace)
5435 // if (o->hasClients()) continue;
5436 bool hasClients = false;
5437 for (auto &c : o->clients()) {
5438 if (!c->InheritsFrom("RooRealIntegral") && p5 == GETWS(o)) {
5439 hasClients = true;
5440 break;
5441 }
5442 }
5443 if (hasClients)
5444 continue;
5445 out.emplace_back(std::make_shared<xRooNode>(*o, *this));
5446 if (o->InheritsFrom("RooAbsPdf")) {
5447 out.back()->fFolder = "!models";
5448 } else {
5449 out.back()->fFolder = "!scratch";
5450 }
5451 }
5452 for (auto &o : p5->allGenericObjects()) {
5453 if (auto fr = dynamic_cast<RooFitResult *>(o); fr) {
5454 TString s(fr->GetTitle());
5455 if (s.Contains(';'))
5456 s = s(0, s.Index(';'));
5457 if (auto _pdf = out.find(s.Data()); _pdf) {
5458 // std::cout << " type = " << _pdf->get()->ClassName() << std::endl;
5459 out.emplace_back(std::make_shared<xRooNode>(fr->GetName(), *fr, _pdf));
5460 // for a while, this node's parent pointed to something of type Node2!!
5461 // how to fix??? - I fxied it with a new constructor to avoid the shared_ptr<Node2> calling the const
5462 // Node2& constructor via getting wrapped in a Node2(shared_ptr<TObject>) call
5463 // out.back()->fParent = _pdf;
5464 // std::cout << " type2 = " << out.back()->fParent->get()->ClassName() << std::endl;
5465 } else {
5466 out.emplace_back(std::make_shared<xRooNode>(fr->GetName(), *fr, *this));
5467 }
5468 out.back()->fFolder = "!fits";
5469 } else {
5470 out.emplace_back(std::make_shared<xRooNode>(*o, *this));
5471 if (strcmp(out.back()->get()->ClassName(), "TStyle") == 0) {
5472 out.back()->fFolder = "!styles";
5473 } else if (strcmp(out.back()->get()->ClassName(), "RooStats::HypoTestInverterResult") == 0) {
5474 out.back()->fFolder = "!scans";
5475 } else {
5476 out.back()->fFolder = "!objects";
5477 }
5478 }
5479 }
5480 for (auto &[k, v] : GETWSSETS(p5)) {
5481 // skip 'CACHE' sets because they are auto-removed when sanitizing workspaces, which will invalidate these
5482 // children
5483 if (k.find("CACHE_") == 0)
5484 continue;
5485 out.emplace_back(std::make_shared<xRooNode>(k.c_str(), v, *this));
5486 out.back()->fFolder = "!sets";
5487 }
5488
5489 RooLinkedList snaps = GETWSSNAPSHOTS(p5);
5490 std::unique_ptr<TIterator> iter(snaps.MakeIterator());
5491 TObject *snap;
5492 while ((snap = iter->Next())) {
5493 out.emplace_back(std::make_shared<xRooNode>(*snap, *this));
5494 out.back()->fFolder = "!snapshots";
5495 }
5496 } else if (strlen(GetName()) > 0 && GetName()[0] == '!' && fParent) {
5497 // special case of dynamic property
5498 if (TString(GetName()) == "!.pars") {
5499 for (auto &c : fParent->pars()) {
5500 out.emplace_back(c);
5501 }
5502 } else {
5503 // the components of a folder are the children of the parent (after browsing) that live in this folder
5504 fParent->browse();
5505 for (auto &c : *fParent) {
5506 if (c->fFolder == GetName()) {
5507 out.emplace_back(c);
5508 }
5509 }
5510 }
5511 }
5512
5513 return out;
5514}
5515
5516////////////////////////////////////////////////////////////////////////////////
5517/// bins of a channel or sample, or channels of a multi-channel pdf
5518
5520{
5521 xRooNode out(".bins", nullptr, *this);
5522
5523 if (auto p = get<RooSimultaneous>(); p) {
5524 std::map<int, std::shared_ptr<xRooNode>> cats; // fill into a map to preserve index ordering
5525 for (auto &c : p->indexCat()) { // is alphabetical in labels
5526 auto pp = p->getPdf(c.first.c_str());
5527 if (!pp)
5528 continue;
5529 cats[c.second] =
5530 std::make_shared<xRooNode>(TString::Format("%s=%s", p->indexCat().GetName(), c.first.c_str()), *pp, *this);
5531 }
5532 for (auto &[_, n] : cats)
5533 out.emplace_back(n);
5534 } else if (auto phf = get<ParamHistFunc>(); phf) {
5535 int i = 1;
5536#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
5537 auto &pSet = phf->_paramSet;
5538#else
5539 auto &pSet = phf->paramList();
5540#endif
5541 for (auto par : pSet) {
5542 out.emplace_back(std::make_shared<xRooNode>(*par, *this));
5543 out.back()->fBinNumber = i;
5544 i++;
5545 }
5546 } else if (auto ax = GetXaxis(); ax) {
5547 for (int i = 1; i <= ax->GetNbins(); i++) {
5548 // create a RooProduct of all bin-specific factors of all shapeFactors
5549 std::vector<RooAbsArg *> _factors;
5550 for (auto f : factors()) {
5551 if (f->get<ParamHistFunc>()) {
5552 if (f->bins()[i - 1]->get<RooProduct>()) {
5553 for (auto &ss : f->bins()[i - 1]->factors())
5554 _factors.push_back(ss->get<RooAbsArg>());
5555 } else {
5556 _factors.push_back(f->bins()[i - 1]->get<RooAbsArg>());
5557 }
5558 }
5559 }
5560 out.emplace_back(std::make_shared<xRooNode>(
5561 TString::Format("%g<=%s<%g", ax->GetBinLowEdge(i), ax->GetParent()->GetName(), ax->GetBinLowEdge(i + 1)),
5562 _factors.empty() ? nullptr
5563 : std::make_shared<RooProduct>(TString::Format("%s.binFactors.bin%d", GetName(), i),
5564 "binFactors", RooArgList()),
5565 *this));
5566 for (auto f : _factors) {
5567#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
5568 out.back()->get<RooProduct>()->_compRSet.add(*f);
5569#else
5570 const_cast<RooArgList &>(out.back()->get<RooProduct>()->realComponents()).add(*f);
5571#endif
5572 }
5573 out.back()->fBinNumber = i;
5574 }
5575 }
5576
5577 return out;
5578}
5579
5581{
5583 bool isResidual = false;
5584
5585 // if parent is a sumpdf or addpdf then include the coefs
5586 // if func appears multiple times then coefs must be combined into a RooAddition temporary
5587 if (fParent) {
5588 // handle case where filters are applied .. need to pass through these
5589 // do this by iterating while fComp is null
5590 auto parent = fParent;
5591 if (!parent->fComp) {
5592 while (!parent->fComp && parent->fParent) {
5593 parent = parent->fParent;
5594 }
5595 // parent should now be node above the filters ... need parent of that
5596 parent = parent->fParent;
5597 if (!parent)
5598 parent = fParent; // revert t original parent in case something went wrong
5599 }
5600 if (auto p = parent->get<RooRealSumPdf>(); p) {
5601 std::size_t i = 0;
5602 for (auto &o : p->funcList()) {
5603 if (o == get()) {
5604 if (i >= p->coefList().size()) {
5605 isResidual = true;
5606 coefs.add(p->coefList());
5607 } else {
5608 coefs.add(*p->coefList().at(i));
5609 }
5610 }
5611 i++;
5612 }
5613 } else if (auto p2 = parent->get<RooAddPdf>(); p2) {
5614 std::size_t i = 0;
5615 if (p2->coefList().empty()) {
5616 // this can happen if all pdfs are extended then the coef is effectively the
5617 // expected number of events
5618 // TODO: test behaviour of xRooNode under this scenario (are histograms correct?)
5619 } else {
5620 for (auto &o : p2->pdfList()) {
5621 if (o == get()) {
5622 if (i >= p2->coefList().size()) {
5623 isResidual = true;
5624 coefs.add(p2->coefList());
5625 } else {
5626 coefs.add(*p2->coefList().at(i));
5627 }
5628 }
5629 i++;
5630 }
5631 }
5632 }
5633 }
5634 if (isResidual) {
5635 // return a node representing 1.-sumOfCoefs
5636 // involves creating sumOfCoefs unless there is only 1 coef, then just use that
5637 auto coefSum = coefs.empty()
5638 ? nullptr
5639 : (coefs.size() == 1 ? std::shared_ptr<RooAbsArg>(coefs.at(0), [](RooAbsArg *) {})
5640 : std::make_shared<RooAddition>((isResidual) ? ".sumOfCoefs" : ".coefs",
5641 "Coefficients of", coefs));
5642 xRooNode out(".coef", coefSum ? std::dynamic_pointer_cast<RooAbsArg>(std::make_shared<RooFormulaVar>(
5643 ".coef", "1-sum(otherCoefs)", "1. - @0", *coefSum))
5644 : nullptr /* should we return a "1." instead? */);
5645 if (coefSum && coefs.size() != 1) {
5646 out.emplace_back(std::make_shared<xRooNode>(".memory", nullptr, *this))
5647 ->emplace_back(
5648 std::make_shared<xRooNode>(".sumOfCoefs", coefSum, out)); // added to keep the sum alive! with the node
5649 }
5650 if (!coefs.empty()) {
5651 out.browse();
5652 }
5653 return out;
5654 } else if (coefs.size() == 1) {
5655 xRooNode out(".coef", std::shared_ptr<RooAbsArg>(coefs.at(0), [](RooAbsArg *) {}), *this);
5656 if (!coefs.empty()) {
5657 out.browse();
5658 }
5659 return out;
5660 } else {
5661 auto coefSum =
5662 coefs.empty()
5663 ? nullptr
5664 : std::make_shared<RooAddition>(".coefs", TString::Format("Coefficients of %s", GetName()), coefs);
5665 xRooNode out(".coefs", coefSum, *this);
5666 if (!coefs.empty())
5667 out.browse();
5668
5669 return out;
5670 }
5671}
5672
5674{
5675 xRooNode out(".factors", nullptr, *this);
5676
5677 if (auto p = get<RooProdPdf>(); p) {
5678 auto _main = mainChild();
5679 if (auto a = _main.get<RooRealSumPdf>(); a && !a->getStringAttribute("alias")) {
5680 a->setStringAttribute("alias", "samples");
5681 } else if (auto a2 = _main.get<RooAddPdf>(); a2 && !a2->getStringAttribute("alias")) {
5682 a2->setStringAttribute("alias", "components");
5683 }
5684 int _npdfs = p->pdfList().size();
5685 for (auto &o : p->pdfList()) {
5686 out.emplace_back(std::make_shared<xRooNode>(*o, *this));
5687 if (_npdfs > 5 && o != _main.get())
5688 out.back()->fFolder = "!constraints";
5689 }
5690 } else if (auto p2 = get<RooProduct>(); p2) {
5691 for (auto &o : p2->components()) {
5692 if (o->InheritsFrom("RooProduct")) {
5693 // get factors of this term
5694 auto x = xRooNode("tmp", *o, *this).factors();
5695 for (auto &n : x) {
5696 out.emplace_back(std::make_shared<xRooNode>(n->GetName(), n->fComp, *this));
5697 }
5698 } else {
5699 out.emplace_back(std::make_shared<xRooNode>(*o, *this));
5700 }
5701 }
5702 } else if (auto w = get<RooWorkspace>(); w) {
5703 // if workspace, return all functions (not pdfs) that have a RooProduct as one of their clients
5704 // or not clients
5705 // exclude obs and globs
5706 auto oo = obs(); // need to keep alive as may contain owning globs
5707 auto& _obs = *(oo.get<RooArgList>());
5708 for (auto a : w->allFunctions()) {
5709 if (_obs.contains(*a))
5710 continue;
5711 bool show(true);
5712 for (auto c : a->clients()) {
5713 show = false;
5714 if (c->InheritsFrom("RooProduct"))
5715 show = true;
5716 }
5717 if (show)
5718 out.emplace_back(std::make_shared<xRooNode>(*a, *this));
5719 }
5720 }
5721
5722 /*
5723 // if parent is a sumpdf or addpdf then include the coefs
5724 // if func appears multiple times then coefs must be combined into a RooAddition temporary
5725 if (fParent) {
5726 RooArgList coefs;
5727 if(auto p = fParent->get<RooRealSumPdf>();p) {
5728 int i=0;
5729 for(auto& o : p->funcList()) {
5730 if (o == get()) {
5731 coefs.add( *p->coefList().at(i) );
5732 }
5733 i++;
5734 }
5735 } else if(auto p = fParent->get<RooAddPdf>(); p) {
5736 int i=0;
5737 for(auto& o : p->pdfList()) {
5738 if (o == get()) {
5739 coefs.add( *p->coefList().at(i) );
5740 }
5741 i++;
5742 }
5743 }
5744 if (!coefs.empty()) {
5745 if (coefs.size() == 1) {
5746 if (strcmp(coefs.at(0)->GetName(),"1")) { // don't add the "1"
5747 out.emplace_back(std::make_shared<Node2>(".coef", *coefs.at(0), *this));
5748 }
5749 } else {
5750 out.emplace_back(std::make_shared<Node2>(".coefs",
5751 std::make_shared<RooAddition>(".coefs", "Coefficients of",
5752 coefs), *this));
5753 }
5754 }
5755 }
5756 */
5757 return out;
5758}
5759
5761{
5762 xRooNode out(".variations", nullptr, *this);
5763
5764 // if (auto p = get<RooSimultaneous>(); p) {
5765 // for (auto &c : p->indexCat()) {
5766 // auto pp = p->getPdf(c.first.c_str());
5767 // if (!pp)
5768 // continue;
5769 // out.emplace_back(
5770 // std::make_shared<xRooNode>(TString::Format("%s=%s", p->indexCat().GetName(), c.first.c_str()), *pp,
5771 // *this));
5772 // }
5773 // } else
5774 if (auto p2 = get<PiecewiseInterpolation>(); p2) {
5775#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
5776 out.emplace_back(std::make_shared<xRooNode>("nominal", p2->_nominal.arg(), *this));
5777#else
5778 out.emplace_back(std::make_shared<xRooNode>("nominal", *(p2->nominalHist()), *this));
5779#endif
5780 for (size_t i = 0; i < p2->paramList().size(); i++) {
5781 // TODO: should we only return one if we find they are symmetrized?
5782 out.emplace_back(std::make_shared<xRooNode>(TString::Format("%s=1", p2->paramList().at(i)->GetName()),
5783 *p2->highList().at(i), *this));
5784 out.emplace_back(std::make_shared<xRooNode>(TString::Format("%s=-1", p2->paramList().at(i)->GetName()),
5785 *p2->lowList().at(i), *this));
5786 }
5787 } else if (auto p3 = get<RooStats::HistFactory::FlexibleInterpVar>(); p3) {
5788#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
5789 out.emplace_back(std::make_shared<xRooNode>("nominal", RooFit::RooConst(p3->_nominal), *this));
5790 for (size_t i = 0; i < p3->_paramList.size(); i++) {
5791 out.emplace_back(std::make_shared<xRooNode>(TString::Format("%s=1", p3->_paramList.at(i)->GetName()),
5792 RooFit::RooConst(p3->_high.at(i)), *this));
5793 out.emplace_back(std::make_shared<xRooNode>(TString::Format("%s=-1", p3->_paramList.at(i)->GetName()),
5794 RooFit::RooConst(p3->_low.at(i)), *this));
5795 }
5796#else
5797 out.emplace_back(std::make_shared<xRooNode>("nominal", RooFit::RooConst(p3->nominal()), *this));
5798 for (size_t i = 0; i < p3->variables().size(); i++) {
5799 out.emplace_back(std::make_shared<xRooNode>(TString::Format("%s=1", p3->variables().at(i)->GetName()),
5800 RooFit::RooConst(p3->high().at(i)), *this));
5801 out.emplace_back(std::make_shared<xRooNode>(TString::Format("%s=-1", p3->variables().at(i)->GetName()),
5802 RooFit::RooConst(p3->low().at(i)), *this));
5803 }
5804#endif
5805
5806 } else if (auto p4 = get<ParamHistFunc>(); p4) {
5807 // I *think* I put this here so that can browse into a ParamHistFunc
5808 // int i = 0;
5809 // for (auto par : p4->_paramSet) {
5810 // TString _name = par->GetName();
5811 // // if(auto _v = dynamic_cast<RooRealVar*>(p->_dataSet.get(i)->first()); _v) {
5812 // // _name = TString::Format("%s=%g",_v->GetName(),_v->getVal());
5813 // // }
5814 // // out.emplace_back(std::make_shared<xRooNode>(_name,*par,*this)); -- -removed cos now have bin()
5815 // method i++;
5816 // }
5817 }
5818 return out;
5819}
5820
5822{
5823 RooArgList out;
5824 out.setName(GetName());
5825 for (auto &k : *this) {
5826 if (auto o = k->get<RooAbsArg>(); o)
5827 out.add(*o);
5828 }
5829 return out;
5830}
5831
5833{
5834 xRooNode out(".datasets()", nullptr, *this);
5835 out.fBrowseOperation = [](xRooNode *f) { return f->fParent->datasets(); };
5836
5837 if (auto _ws = get<RooWorkspace>(); _ws) {
5838 for (auto &d : _ws->allData()) {
5839 out.emplace_back(std::make_shared<xRooNode>(*d, *this));
5840 out.back()->fFolder = "!datasets";
5841 }
5842 } else if (auto __ws = ws(); __ws) {
5843 if (get<RooAbsPdf>() ||
5844 (!get() && fParent &&
5845 fParent->get<RooAbsPdf>())) { // second condition handles 'bins' nodes of pdf, which have null ptr
5846 // only add datasets that have observables that cover all our observables
5847 auto oo = obs(); // must keep alive in case is owning the globs
5848 RooArgSet _obs(*oo.get<RooArgList>());
5849 //_obs.add(coords(true).argList(), true); // include coord observables too, and current xaxis if there's one -
5850 // added in loop below
5851
5852 TString cut;
5853 RooArgSet cutobs;
5854 for (auto _c : coords()) { // coords() moves vars to their respective coordinates too
5855 if (auto _cat = _c->get<RooAbsCategoryLValue>(); _cat) {
5856 if (cut != "")
5857 cut += " && ";
5858 cut += TString::Format("%s==%d", _cat->GetName(), _cat->getCurrentIndex());
5859 _obs.add(*_cat,
5860 true); // note: if we ever changed coords to return clones, would need to keep coords alive
5861 cutobs.add(*_cat);
5862 } else if (auto _rv = _c->get<RooAbsRealLValue>(); _rv) {
5863 // todo: check coordRange is a single range rather than multirange
5864 if (cut != "")
5865 cut += " && ";
5866 cut +=
5867 TString::Format("%s>=%f&&%s<%f", _rv->GetName(), _rv->getMin(_rv->getStringAttribute("coordRange")),
5868 _rv->GetName(), _rv->getMax(_rv->getStringAttribute("coordRange")));
5869 _obs.add(*_rv,
5870 true); // note: if we ever changed coords to return clones, would need to keep coords alive
5871 cutobs.add(*_rv);
5872 } else {
5873 throw std::runtime_error("datasets(): Unsupported coordinate type");
5874 }
5875 }
5876 if (auto s = get<RooSimultaneous>()) {
5877 // check if we have a pdf for every category ... if not then add to cut
5878 bool hasMissing = false;
5879 TString extraCut = "";
5880 for (auto cat : s->indexCat()) {
5881 if (!s->getPdf(cat.first.c_str())) {
5882 hasMissing = true;
5883 } else {
5884 if (extraCut != "")
5885 extraCut += " || ";
5886 extraCut += TString::Format("%s==%d", s->indexCat().GetName(), cat.second);
5887 }
5888 }
5889 if (hasMissing) {
5890 if (cut != "")
5891 cut += " && ";
5892 cut += "(" + extraCut + ")";
5893 cutobs.add(s->indexCat());
5894 }
5895 }
5896
5897 if (auto ax = GetXaxis(); ax && dynamic_cast<RooAbsArg *>(ax->GetParent())->getAttribute("obs")) {
5898 auto a = dynamic_cast<RooAbsArg *>(ax->GetParent());
5899 _obs.add(*a, true);
5900 }
5901 xRooNode _wsNode(*__ws, *this);
5902 for (auto &d : _wsNode.datasets()) {
5903 if (std::unique_ptr<RooAbsCollection>(d->obs().argList().selectCommon(_obs))->size() == _obs.size()) {
5904 // all obs present .. include
5905
5906 if (cut != "") {
5907 RooFormulaVar cutFormula("cut1", cut, cutobs); // doing this to avoid complaints about unused vars
5908 // TODO: Could consider using a 'filter' node (see filter() method) applied to the dataset instead
5909 // of creating and using a reduced dataset here
5910 out.emplace_back(std::make_shared<xRooNode>(
5911 std::shared_ptr<RooAbsData>(d->get<RooAbsData>()->reduce(*std::unique_ptr<RooAbsCollection>(d->robs().get<RooArgList>()->selectCommon(_obs)),cutFormula)), *this));
5912 // put a subset of the globs in the returned dataset too
5913 out.back()->get<RooAbsData>()->setGlobalObservables(*std::unique_ptr<RooAbsCollection>(d->globs().get<RooArgList>()->selectCommon(*globs().get<RooArgList>())));
5914 if (d->get()->TestBit(1 << 20))
5915 out.back()->get()->SetBit(1 << 20);
5916 // need to attach the original dataset so that things like SetBinContent can interact with it
5917 out.back()->fBrowsables.emplace_back(std::make_shared<xRooNode>(".sourceds", d->fComp, *this));
5918 } else {
5919 out.emplace_back(std::make_shared<xRooNode>(d->fComp, *this));
5920 }
5921 }
5922 }
5923 } /*else if(auto p = get<RooFitResult>(); p) {
5924 // look for datasets in workspace that match the fit result name after hashing
5925 for(auto& _d : xRooNode(*_ws,*this).datasets()) {
5926 auto _hash = RooAbsTree::nameToHash(_d->get()->GetName());
5927 if (TString::Format("%d;%d",_hash.first,_hash.second) == p->GetTitle()) {
5928 out.emplace_back(std::make_shared<xRooNode>(_d->fComp, *this));
5929 }
5930 }
5931 }*/
5932 }
5933
5934 return out;
5935}
5936
5937std::shared_ptr<xRooNode> xRooNode::getBrowsable(const char *name) const
5938{
5939 for (auto b : fBrowsables) {
5940 if (b && strcmp(b->GetName(), name) == 0)
5941 return b;
5942 }
5943 return nullptr;
5944}
5945
5946TGraph *xRooNode::BuildGraph(RooAbsLValue *v, bool includeZeros, TVirtualPad *fromPad) const
5947{
5948
5949 if (auto fr = get<RooFitResult>(); fr) {
5950 return nullptr;
5951 }
5952
5953 if (auto theData = get<RooDataSet>(); theData) {
5954
5955 TH1 *theHist = nullptr;
5956
5957 if (fromPad) {
5958 // find first histogram in pad
5959 for (auto o : *fromPad->GetListOfPrimitives()) {
5960 theHist = dynamic_cast<TH1 *>(o);
5961 if (theHist) {
5962 theHist = static_cast<TH1 *>(theHist->Clone());
5963 theHist->Reset();
5964 break;
5965 } // clone because theHist gets deleted below
5966 }
5967 }
5968
5969 if (!theHist) {
5970 auto _parentPdf = parentPdf();
5971 if (!_parentPdf) {
5972 // can still build graph if v is an obs ... will use v binning
5973 auto vo = dynamic_cast<TObject *>(v);
5974 if (v && obs().find(vo->GetName())) {
5975 if (auto cat = dynamic_cast<RooAbsCategoryLValue *>(v)) {
5976 theHist = new TH1D(
5977 TString::Format("%s_%s", GetName(), vo->GetName()),
5978 TString::Format("my temp hist;%s", strlen(vo->GetTitle()) ? vo->GetTitle() : vo->GetName()),
5979 cat->numTypes(), 0, cat->numTypes());
5980 for (int i = 0; i < cat->numTypes(); i++) {
5981 cat->setBin(i);
5982 theHist->GetXaxis()->SetBinLabel(i + 1, cat->getLabel());
5983 }
5984 } else {
5985 auto _binning = v->getBinningPtr(nullptr);
5986 if(_binning->isUniform()) {
5987 theHist = new TH1D(
5988 TString::Format("%s_%s", GetName(), vo->GetName()),
5989 TString::Format("my temp hist;%s", strlen(vo->GetTitle()) ? vo->GetTitle() : vo->GetName()),
5990 v->numBins(), _binning->lowBound(), _binning->highBound());
5991 } else {
5992 theHist = new TH1D(
5993 TString::Format("%s_%s", GetName(), vo->GetName()),
5994 TString::Format("my temp hist;%s", strlen(vo->GetTitle()) ? vo->GetTitle() : vo->GetName()),
5995 v->numBins(), _binning->array());
5996 }
5997 }
5998 } else {
5999 throw std::runtime_error("Cannot draw dataset without parent PDF");
6000 }
6001 } else {
6002 theHist = _parentPdf->BuildHistogram(v, true);
6003 }
6004 }
6005 if (!theHist)
6006 return nullptr;
6007 // this hist will get filled with w*x to track weighted x position per bin
6008 TH1 *xPos = static_cast<TH1 *>(theHist->Clone("xPos"));
6009 xPos->Reset();
6010 TH1 *xPos2 = static_cast<TH1 *>(theHist->Clone("xPos2"));
6011 xPos2->Reset();
6012 auto nHist = std::unique_ptr<TH1>(static_cast<TH1 *>(theHist->Clone("nEntries")));
6013 nHist->Reset();
6014
6015 auto dataGraph = new TGraphAsymmErrors;
6016 dataGraph->SetEditable(false);
6017 dataGraph->SetName(GetName());
6018 dataGraph->SetTitle(strlen(theData->GetTitle()) ? theData->GetTitle() : theData->GetName());
6019 // next line triggers creation of the histogram inside the graph, in root 6.22 that isn't protected from being
6020 // added to gDirectory
6021 dataGraph->SetTitle(TString::Format("%s;%s;Events", dataGraph->GetTitle(), theHist->GetXaxis()->GetTitle()));
6022 *static_cast<TAttMarker *>(dataGraph) = *static_cast<TAttMarker *>(theHist);
6023 *static_cast<TAttLine *>(dataGraph) = *static_cast<TAttLine *>(theHist);
6024 dataGraph->SetMarkerStyle(20);
6025 dataGraph->SetLineColor(kBlack);
6026
6027 auto _obs = obs();
6028
6029 // auto x = theData->get()->find((v) ? dynamic_cast<TObject*>(v)->GetName() : theHist->GetXaxis()->GetName());
6030 // const RooAbsReal* xvar = (x) ? dynamic_cast<RooAbsReal*>(x) : nullptr;
6031 // const RooAbsCategory* xcat = (x && !xvar) ? dynamic_cast<RooAbsCategory*>(x) : nullptr;
6032 auto x = _obs.find((v) ? dynamic_cast<TObject *>(v)->GetName() : theHist->GetXaxis()->GetName());
6033 if (x && x->get<RooAbsArg>()->getAttribute("global")) {
6034 // is global observable ...
6035 dataGraph->SetPoint(0, x->get<RooAbsReal>()->getVal(), 1e-15);
6036 dataGraph->SetTitle(TString::Format("%s = %f", dataGraph->GetTitle(), dataGraph->GetPointX(0)));
6037 delete xPos;
6038 delete xPos2;
6039 delete theHist;
6040 return dataGraph;
6041 }
6042
6043 const RooAbsReal *xvar = (x) ? x->get<RooAbsReal>() : nullptr;
6044 const RooAbsCategory *xcat = (x && !xvar) ? x->get<RooAbsCategory>() : nullptr;
6045
6046 auto _coords = coords();
6047
6048 TString pName((fromPad) ? fromPad->GetName() : "");
6049 auto _pos = pName.Index('=');
6050
6051 int nevent = theData->numEntries();
6052 for (int i = 0; i < nevent; i++) {
6053 theData->get(i);
6054 bool _skip = false;
6055 for (auto _c : _coords) {
6056 if (auto cat = _c->get<RooAbsCategoryLValue>(); cat) {
6057 if (cat->getIndex() != theData->get()->getCatIndex(cat->GetName())) {
6058 _skip = true;
6059 break;
6060 }
6061 } else if (auto rv = _c->get<RooAbsRealLValue>(); rv) {
6062 // must be in range
6063 if (!rv->inRange(theData->get()->getRealValue(rv->GetName()), rv->getStringAttribute("coordRange"))) {
6064 _skip = true;
6065 break;
6066 }
6067 }
6068 }
6069 if (_pos != -1) {
6070 if (auto cat = dynamic_cast<RooAbsCategory *>(theData->get()->find(TString(pName(0, _pos))));
6071 cat && cat->getLabel() != pName(_pos + 1, pName.Length())) {
6072 _skip = true;
6073 }
6074 }
6075 if (_skip)
6076 continue;
6077
6078 if (xvar) {
6079 xPos->Fill(xvar->getVal(), xvar->getVal() * theData->weight());
6080 xPos2->Fill(xvar->getVal(), pow(xvar->getVal(), 2) * theData->weight());
6081 }
6082
6083 if (xcat) {
6084 theHist->Fill(xcat->getLabel(), theData->weight());
6085 nHist->Fill(xcat->getLabel(), 1);
6086 } else {
6087 theHist->Fill((x) ? xvar->getVal() : 0.5, theData->weight());
6088 nHist->Fill((x) ? xvar->getVal() : 0.5, 1);
6089 }
6090 }
6091
6092 xPos->Divide(theHist);
6093 xPos2->Divide(theHist);
6094
6095 // update the x positions to the means for each bin and use poisson asymmetric errors for data ..
6096 for (int i = 0; i < theHist->GetNbinsX(); i++) {
6097 if (includeZeros || nHist->GetBinContent(i + 1)) {
6098 double val = theHist->GetBinContent(i + 1);
6099
6100 dataGraph->SetPoint(dataGraph->GetN(),
6101 (xvar && val) ? xPos->GetBinContent(i + 1) : theHist->GetBinCenter(i + 1), val);
6102
6103 // x-error will be the (weighted) standard deviation of the x values ...
6104 double xErr = xPos2->GetBinContent(i + 1) - pow(xPos->GetBinContent(i + 1), 2);
6105 xErr = (xErr <= 0) ? 0. : sqrt(xErr); // protects against floating point rounding effects
6106
6107 if (xErr || val) {
6108 dataGraph->SetPointError(dataGraph->GetN() - 1, xErr, xErr,
6109 val - 0.5 * TMath::ChisquareQuantile(TMath::Prob(1, 1) / 2., 2. * (val)),
6110 0.5 * TMath::ChisquareQuantile(1. - TMath::Prob(1, 1) / 2., 2. * (val + 1)) -
6111 val);
6112 }
6113 }
6114 }
6115
6116 // transfer limits from theHist to dataGraph hist
6117 dataGraph->GetHistogram()->GetXaxis()->SetLimits(theHist->GetXaxis()->GetXmin(), theHist->GetXaxis()->GetXmax());
6118 // and bin labels, if any
6119 if (xcat) {
6120 dataGraph->GetHistogram()->GetXaxis()->Set(theHist->GetNbinsX(), 0, theHist->GetNbinsX());
6121 for (int i = 1; i <= theHist->GetNbinsX(); i++)
6122 dataGraph->GetHistogram()->GetXaxis()->SetBinLabel(i, theHist->GetXaxis()->GetBinLabel(i));
6123 }
6124
6125 delete xPos;
6126 delete xPos2;
6127 delete theHist;
6128
6129 // std::shared_ptr<TStyle> style; // use to keep alive for access from GetStyle below, in case getObject
6130 // has decided to return the owning ptr (for some reason) std::string _title =
6131 // strlen(dataGraph->GetTitle()) ? dataGraph->GetTitle() : GetName(); if (!gROOT->GetStyle(_title.c_str()))
6132 // {
6133 // if ( (style = getObject<TStyle>(_title)) ) {
6134 // // loaded style (from workspace?) so put in list and use that
6135 // gROOT->GetListOfStyles()->Add(style.get());
6136 // } else {
6137 // // create new style - gets put in style list automatically so don't have to delete
6138 // // acquire them so saved to workspaces for auto reload ...
6139 // style = const_cast<xRooNode&>(*this).acquireNew<TStyle>(_title.c_str(),
6140 // TString::Format("Style for %s component", _title.c_str()));
6141 // (TAttLine &) (*style) = *dynamic_cast<TAttLine *>(dataGraph);
6142 // (TAttFill &) (*style) = *dynamic_cast<TAttFill *>(dataGraph);
6143 // (TAttMarker &) (*style) = *dynamic_cast<TAttMarker *>(dataGraph);
6144 // gROOT->GetListOfStyles()->Add(style.get());
6145 // }
6146 // }
6147 auto _style = style(dataGraph);
6148 if (_style) {
6149 *dynamic_cast<TAttLine *>(dataGraph) = *_style;
6150 *dynamic_cast<TAttFill *>(dataGraph) = *_style;
6151 *dynamic_cast<TAttMarker *>(dataGraph) = *_style;
6152 }
6153 return dataGraph;
6154 }
6155
6156 throw std::runtime_error("Cannot build graph");
6157}
6158
6160{
6161 if (fr) {
6162 if (auto _w = ws(); _w) {
6163 auto res = acquire(std::shared_ptr<RooFitResult>(const_cast<RooFitResult *>(fr), [](RooFitResult *) {}));
6164 for (auto o : _w->allGenericObjects()) {
6165 if (auto _fr = dynamic_cast<RooFitResult *>(o); _fr) {
6166 _fr->ResetBit(1 << 20);
6167 }
6168 }
6169 res->SetBit(1 << 20);
6170 // assign values
6171 auto allVars = _w->allVars();
6172 allVars = fr->floatParsFinal();
6173 allVars = fr->constPars();
6174 } else {
6175 // need to add to memory as a specific name
6176 throw std::runtime_error("Not supported yet"); // complication is how to replace an existing fitResult in
6177 // .memory auto _clone = std::make_shared<RooFitResult>(*fr);
6178 //_clone->SetName("fitResult");
6179 }
6180 } else {
6181 SetFitResult(fitResult("prefit").get<RooFitResult>());
6182 }
6183}
6184
6186{
6187 if (auto _fr = fr.get<const RooFitResult>()) {
6188 SetFitResult(_fr);
6189 } else
6190 throw std::runtime_error("Not a RooFitResult");
6191}
6192
6193xRooNode xRooNode::fitResult(const char *opt) const
6194{
6195
6196 if (get<RooFitResult>())
6197 return *this;
6198 if (get<RooAbsData>()) {
6199 if (auto _fr = find(".fitResult"); _fr)
6200 return _fr;
6201#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 26, 00)
6202 // check if weightVar of RooAbsData has fitResult attribute on it, will be the generation fit result
6203 if (get<RooDataSet>() && get<RooDataSet>()->weightVar() &&
6204 get<RooDataSet>()->weightVar()->getStringAttribute("fitResult")) {
6205 return xRooNode(getObject<const RooFitResult>(get<RooDataSet>()->weightVar()->getStringAttribute("fitResult")),
6206 *this);
6207 }
6208#endif
6209 return xRooNode();
6210 }
6211
6212 TString sOpt(opt);
6213 if (sOpt == "prefit") {
6214 // build a fitResult using nominal values and infer errors from constraints
6215 // that aren't the 'main' constraints
6216 // Warning("fitResult","Building prefitResult by examining pdf. Consider setting an explicit prefitResult
6217 // (SetFitResult(fr)) where fr name is prefitResult");
6218
6219 // ensure coefs are included if there are any
6220 auto _coefs = coefs();
6221 if (_coefs.get()) {
6222 return xRooNode(RooProduct("tmp", "tmp", RooArgList(*get<RooAbsArg>(), *_coefs.get<RooAbsReal>())))
6223 .fitResult(opt);
6224 }
6225
6226 std::unique_ptr<RooArgList> _pars(dynamic_cast<RooArgList *>(pars().argList().selectByAttrib("Constant", false)));
6227 auto fr = std::make_shared<RooFitResult>("prefitResult", "Prefit");
6228 fr->setFinalParList(*_pars);
6229 for (auto &p : fr->floatParsFinal()) {
6230 auto _v = dynamic_cast<RooRealVar *>(p);
6231 if (!_v)
6232 continue;
6233 if (auto s = _v->getStringAttribute("nominal"); s)
6234 _v->setVal(TString(s).Atof());
6235 auto _constr = xRooNode(fParent->getObject<RooRealVar>(p->GetName()), *this).constraints();
6236 std::shared_ptr<xRooNode> pConstr;
6237 for (auto &c : _constr) {
6238 if (c->get<RooPoisson>() || c->get<RooGaussian>()) {
6239 // require parameter to be a direct server of the constraint pdf to count
6240 bool isServer = true;
6241 if (c->get<RooGaussian>()) {
6242 isServer = false;
6243 for (auto s : c->get<RooAbsArg>()->servers()) {
6244 if (strcmp(s->GetName(), p->GetName()) == 0) {
6245 isServer = true;
6246 break;
6247 }
6248 }
6249 }
6250 if (isServer) {
6251 pConstr = c;
6252 break;
6253 }
6254 }
6255 }
6256 if (pConstr) {
6257 // there will be 3 deps, one will be this par, the other two are the mean and error (or error^2 in case of
6258 // poisson use the one that's a ConstVar as the error to break a tie ...
6259 double prefitVal = 0;
6260 double prefitError = 0;
6261 for (auto &_d : pConstr->vars()) {
6262 if (strcmp(p->GetName(), _d->get()->GetName()) == 0)
6263 continue;
6264 if (auto _c = _d->get<RooConstVar>(); _c && _c->getVal() != 0) {
6265 if (prefitError)
6266 prefitVal = prefitError; // loading val into error already, so move it over
6267 prefitError = _c->getVal();
6268 } else if (prefitError == 0) {
6269 prefitError = _d->get<RooAbsReal>()->getVal();
6270 } else {
6271 prefitVal = _d->get<RooAbsReal>()->getVal();
6272 }
6273 }
6274
6275 if (pConstr->get<RooGaussian>() && pConstr->browse().find(".sigma")) {
6276 prefitError = pConstr->find(".sigma")->get<RooAbsReal>()->getVal();
6277 }
6278 // std::cout << p->GetName() << " extracted " << prefitVal << " " << prefitError << " from ";
6279 // pConstr->deps().Print();
6280 if (pConstr->get<RooPoisson>()) {
6281 // prefitVal will be the global observable value, need to divide that by tau
6282 prefitVal /= prefitError;
6283 // prefiterror will be tau ... need 1/sqrt(tau) for error
6284 prefitError = 1. / sqrt(prefitError);
6285 }
6286 if (!_v->getStringAttribute("nominal"))
6287 _v->setVal(prefitVal);
6288 _v->setError(prefitError);
6289 } else {
6290 // unconstrained, remove error
6291 _v->removeError();
6292 }
6293 }
6294 auto _args = consts().argList();
6295 _args.add(pp().argList());
6296 // global obs are added to constPars list too
6297 auto _globs = globs(); // keep alive as may own glob
6298 _args.add(_globs.argList());
6299 fr->setConstParList(_args);
6300 std::unique_ptr<RooArgList> _snap(dynamic_cast<RooArgList *>(_pars->snapshot()));
6301 for (auto &p : *_snap) {
6302 if (auto atr = p->getStringAttribute("initVal"); atr && dynamic_cast<RooRealVar *>(p))
6303 dynamic_cast<RooRealVar *>(p)->setVal(TString(atr).Atof());
6304 }
6305 fr->setInitParList(*_snap);
6306 return xRooNode(fr, *this);
6307 }
6308
6309 // return first checked fit result present in the workspace
6310 if (auto _w = ws(); _w) {
6311 auto checkFr = [&](TObject *o) {
6312 if (auto _fr = dynamic_cast<RooFitResult *>(o); _fr && _fr->TestBit(1 << 20)) {
6313 // check all pars match final/const values ... if mismatch need to create a new RooFitResult
6314 bool match = true;
6315 for (auto p : pars()) {
6316 if (!p->get<RooAbsReal>())
6317 continue;
6318 if (p->get<RooAbsArg>()->getAttribute("Constant")) {
6319 if (_fr->floatParsFinal().find(p->GetName()) ||
6320 std::abs(_fr->constPars().getRealValue(p->GetName(), std::numeric_limits<double>::quiet_NaN()) -
6321 p->get<RooAbsReal>()->getVal()) > 1e-15) {
6322 match = false;
6323 break;
6324 }
6325 } else {
6326 if (_fr->constPars().find(p->GetName()) ||
6327 std::abs(
6328 _fr->floatParsFinal().getRealValue(p->GetName(), std::numeric_limits<double>::quiet_NaN()) -
6329 p->get<RooAbsReal>()->getVal()) > 1e-15) {
6330 match = false;
6331 break;
6332 }
6333 }
6334 }
6335 if (!match) {
6336 // create new fit result using covariances from this fit result
6337 std::unique_ptr<RooArgList> _pars(
6338 dynamic_cast<RooArgList *>(pars().argList().selectByAttrib("Constant", false)));
6339 auto fr = std::make_shared<RooFitResult>(TString::Format("%s-dirty", _fr->GetName()));
6340 fr->SetTitle(TString::Format("%s parameter snapshot", GetName()));
6341 fr->setFinalParList(*_pars);
6342 TMatrixTSym<double> *prevCov = static_cast<TMatrixTSym<double> *>(GETDMP(_fr, _VM));
6343 if (prevCov) {
6344 auto cov = _fr->reducedCovarianceMatrix(*_pars);
6345 // make the diagonals all the current error values
6346 for (size_t i = 0; i < _pars->size(); i++) {
6347 if (auto v = dynamic_cast<RooRealVar *>(_pars->at(i))) {
6348 cov(i, i) = pow(v->getError(), 2);
6349 } else {
6350 cov(i, i) = 0;
6351 }
6352 }
6353 fr->setCovarianceMatrix(cov);
6354 }
6355
6356 auto _args = consts().argList();
6357 _args.add(pp().argList());
6358 // global obs are added to constPars list too
6359 auto _globs = globs(); // keep alive as may own glob
6360 _args.add(_globs.argList());
6361 fr->setConstParList(_args);
6362 std::unique_ptr<RooArgList> _snap(dynamic_cast<RooArgList *>(_pars->snapshot()));
6363 for (auto &p : *_snap) {
6364 if (auto atr = p->getStringAttribute("initVal"); atr && dynamic_cast<RooRealVar *>(p))
6365 dynamic_cast<RooRealVar *>(p)->setVal(TString(atr).Atof());
6366 }
6367 fr->setInitParList(*_snap);
6368 return xRooNode(fr, *this);
6369 }
6370 return xRooNode(*_fr, std::make_shared<xRooNode>(*_w, std::make_shared<xRooNode>()));
6371 }
6372 return xRooNode();
6373 };
6374 for (auto o : _w->allGenericObjects()) {
6375 auto out = checkFr(o);
6376 if (out)
6377 return out;
6378 }
6379 for (auto o : GETWSSNAPSHOTS(_w)) {
6380 auto out = checkFr(o);
6381 if (out)
6382 return out;
6383 }
6384 } else {
6385 // objects not in workspaces are allowed to have a fitResult set in their memory
6386 // use getObject to get it
6387 if (auto fr = getObject<RooFitResult>(".fitResult"); fr) {
6388 return xRooNode(fr, *this);
6389 }
6390 }
6391
6392 // ensure coefs are included if there are any
6393 auto _coefs = coefs();
6394 if (_coefs.get()) {
6395 return xRooNode(RooProduct("tmp", "tmp", RooArgList(*get<RooAbsArg>(), *_coefs.get<RooAbsReal>())))
6396 .fitResult(opt);
6397 }
6398
6399 std::unique_ptr<RooArgList> _pars(dynamic_cast<RooArgList *>(pars().argList().selectByAttrib("Constant", false)));
6400 auto fr = std::make_shared<RooFitResult>(TUUID().AsString());
6401 fr->SetTitle(TString::Format("%s uncorrelated parameter snapshot", GetName()));
6402 fr->setFinalParList(*_pars);
6403 fr->setStatus(-1);
6404
6405 TMatrixDSym cov(fr->floatParsFinal().size());
6406 TMatrixTSym<double> *prevCov = static_cast<TMatrixTSym<double> *>(GETDMP(fr.get(), _VM));
6407 if (prevCov) {
6408 for (int i = 0; i < prevCov->GetNcols(); i++) {
6409 for (int j = 0; j < prevCov->GetNrows(); j++) {
6410 cov(i, j) = (*prevCov)(i, j);
6411 }
6412 }
6413 }
6414 int i = 0;
6415 for (auto &p : fr->floatParsFinal()) {
6416 if (!prevCov || i >= prevCov->GetNcols()) {
6417 if (auto v = dynamic_cast<RooRealVar *>(p)) {
6418 cov(i, i) = pow(v->getError(), 2);
6419 } else {
6420 cov(i, i) = 0;
6421 }
6422 }
6423 i++;
6424 }
6425 int covQualBackup = fr->covQual();
6426 fr->setCovarianceMatrix(cov);
6427 fr->setCovQual(covQualBackup);
6428
6429 auto _args = consts().argList();
6430 _args.add(pp().argList());
6431 // global obs are added to constPars list too
6432 auto _globs = globs(); // keep alive as may own glob
6433 _args.add(_globs.argList());
6434 fr->setConstParList(_args);
6435 std::unique_ptr<RooArgList> _snap(dynamic_cast<RooArgList *>(_pars->snapshot()));
6436 for (auto &p : *_snap) {
6437 if (auto atr = p->getStringAttribute("initVal"); atr && dynamic_cast<RooRealVar *>(p))
6438 dynamic_cast<RooRealVar *>(p)->setVal(TString(atr).Atof());
6439 }
6440 fr->setInitParList(*_snap);
6441
6442 // return *const_cast<Node2*>(this)->emplace_back(std::make_shared<Node2>(".fitResult",fr,*this));
6443 return xRooNode(fr, *this);
6444}
6445
6446// xRooNode xRooNode::fitTo_(const char* datasetName) const {
6447// try {
6448// return fitTo(datasetName);
6449// } catch(const std::exception& e) {
6450// new TGMsgBox(gClient->GetRoot(), gClient->GetRoot(), "Exception", e.what(),kMBIconExclamation); // deletes
6451// self on dismiss? return xRooNode();
6452// }
6453// }
6454//
6455// xRooNode xRooNode::fitTo(const char* datasetName) const {
6456// return fitTo(*datasets().at(datasetName));
6457// }
6458
6459void xRooNode::SetRange(const char *range, double low, double high)
6460{
6461 if (!std::isnan(low) && !std::isnan(high) && get<RooRealVar>()) {
6462 if (range && strlen(range)) {
6463 get<RooRealVar>()->setRange(range, low, high);
6464 } else {
6465 get<RooRealVar>()->setRange(low, high);
6466 }
6467 return;
6468 }
6469 if (auto o = get<RooAbsArg>(); o)
6470 o->setStringAttribute("range", range);
6471 // todo: clear the range attribute on all servers
6472 // could make this controlled by a flag but probably easiest to enforce so you must set range
6473 // in children after if you wanted to override
6474}
6475const char *xRooNode::GetRange() const
6476{
6477 std::string &out = fRange;
6478 if (auto o = get<RooAbsArg>(); o && o->getStringAttribute("range"))
6479 out = o->getStringAttribute("range");
6480 auto _parent = fParent;
6481 while (out.empty() && _parent) {
6482 if (auto o = _parent->get<RooAbsArg>(); o && o->getStringAttribute("range"))
6483 out = o->getStringAttribute("range");
6484 _parent = _parent->fParent;
6485 }
6486 return out.c_str();
6487}
6488
6490{
6492}
6493
6494xRooNLLVar xRooNode::nll(const xRooNode &_data, std::initializer_list<RooCmdArg> nllOpts) const
6495{
6496 auto defaultOpts = xRooFit::createNLLOptions(); // smart pointer will cleanup the list
6497 // add user-specified options to list ... if already existing in default list, override and warn
6499 for (auto opt : *defaultOpts) {
6500 l.Add(opt);
6501 }
6502 for (auto &i : nllOpts) {
6503 if (auto o = l.FindObject(i.GetName())) {
6504 Info("nll", "Overriding NLL Option: %s", o->GetName());
6505 l.Remove(o);
6506 }
6507 l.Add(const_cast<RooCmdArg *>(&i));
6508 }
6509
6510 return nll(_data, l);
6511}
6512
6513xRooNode xRooNode::generate(const xRooNode &fr, bool expected, int seed)
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 "generate with (found at least %s and %s)",
6526 mainModel->GetName(), c->GetName()));
6527 }
6528 }
6529 }
6530 if (mainModel)
6531 return mainModel->generate(fr, expected, seed);
6532 }
6533 throw std::runtime_error(TString::Format("%s is not a pdf", GetName()));
6534 }
6535 auto _fr = fr.get<RooFitResult>();
6536 return xRooNode(
6537 xRooFit::generateFrom(*get<RooAbsPdf>(), (_fr ? *_fr : *(fitResult().get<RooFitResult>())), expected, seed).first,
6538 *this);
6539}
6540
6541xRooNLLVar xRooNode::nll(const xRooNode &_data, const RooLinkedList &opts) const
6542{
6543
6544 if (!get<RooAbsPdf>()) {
6545 // before giving up, if this is a workspace we can proceed if we only have one model
6546 if (get<RooWorkspace>()) {
6547 std::shared_ptr<xRooNode> mainModel;
6548 for (auto &c : const_cast<xRooNode *>(this)->browse()) {
6549 if (c->get<RooAbsPdf>()) {
6550 if (!mainModel) {
6551 mainModel = c;
6552 } else {
6553 throw std::runtime_error(TString::Format("Workspace has multiple models, you must specify which to "
6554 "build nll with (found at least %s and %s)",
6555 mainModel->GetName(), c->GetName()));
6556 }
6557 }
6558 }
6559 if (mainModel)
6560 return mainModel->nll(_data, opts);
6561 }
6562 throw std::runtime_error(TString::Format("%s is not a pdf", GetName()));
6563 }
6564
6565 // if simultaneous and any channels deselected then reduce and return
6566 if (get<RooSimultaneous>()) {
6567 std::string selected;
6568 bool hasDeselected = false;
6569 for (auto c : bins()) {
6570 if (!c->get<RooAbsReal>()->isSelectedComp()) {
6571 hasDeselected = true;
6572 } else {
6573 TString cName(c->GetName());
6574 cName = cName(cName.Index('=') + 1, cName.Length());
6575 if (!selected.empty())
6576 selected += ",";
6577 selected += cName.Data();
6578 }
6579 }
6580 if (hasDeselected)
6581 return reduced(selected).nll(_data, opts);
6582 }
6583
6584 if (!_data.get<RooAbsData>()) {
6585 // use node name to find dataset and recall
6586 auto _d = strlen(_data.GetName()) ? datasets().find(_data.GetName()) : nullptr;
6587 if (strlen(_data.GetName()) == 0) {
6588 // create the EXPECTED (asimov) dataset with the observables
6589 auto asi = xRooFit::generateFrom(*get<RooAbsPdf>(), *(fitResult().get<RooFitResult>()), true);
6590 _d = std::make_shared<xRooNode>(asi.first, *this);
6591 if (asi.second) {
6592 _d->emplace_back(
6593 std::make_shared<xRooNode>(".globs", std::const_pointer_cast<RooAbsCollection>(asi.second), *_d));
6594 }
6595 } else if (!_d) {
6596 throw std::runtime_error(TString::Format("Cannot find dataset %s", _data.GetName()));
6597 }
6598 return nll(*_d, opts);
6599 }
6600
6601 auto _globs = _data.globs(); // keep alive because may own the globs
6602
6603 auto _opts = std::shared_ptr<RooLinkedList>(new RooLinkedList, [](RooLinkedList *l) {
6604 if (l)
6605 l->Delete();
6606 delete l;
6607 });
6608 RooArgSet _globsSet(_globs.argList());
6609 _opts->Add(RooFit::GlobalObservables(_globsSet).Clone());
6610 if (GetRange() && strlen(GetRange()))
6611 _opts->Add(RooFit::Range(GetRange()).Clone());
6612
6613 // copy over opts ... need to clone each so can safely delete when _opts destroyed
6614 for (int i = 0; i < opts.GetSize(); i++) {
6615 if (strlen(opts.At(i)->GetName()) == 0)
6616 continue; // skipping "none" cmds
6617 if (strcmp(opts.At(i)->GetName(), "GlobalObservables") == 0) {
6618 // maybe warn here?
6619 } else {
6620 _opts->Add(opts.At(i)->Clone(nullptr)); // nullptr needed because accessing Clone via TObject base class puts
6621 // "" instead, so doesnt copy names
6622 }
6623 }
6624
6625 // use shared_ptr method so NLLVar will take ownership of datasets etc if created above
6626 // snapshots the globs out of the nllOpts (see specific constructor of xRooNLLVar)
6627 auto out = xRooFit::createNLL(std::dynamic_pointer_cast<RooAbsPdf>(fComp),
6628 std::dynamic_pointer_cast<RooAbsData>(_data.fComp), *_opts);
6629 return out;
6630}
6631
6632// xRooNode xRooNode::fitTo(const xRooNode& _data) const {
6633//
6634//
6635// auto _pdf = get<RooAbsPdf>();
6636// if (!_pdf) throw std::runtime_error("Not a pdf");
6637//
6638// auto _globs = _data.globs(); // keep alive because may own the globs
6639// RooArgSet globsSet(_globs.argList());
6640//
6641// std::shared_ptr<RooSimultaneous> newPdf;
6642// if(auto s = get<RooSimultaneous>(); s) {
6643// auto rangeName = GetRange();
6644// if (rangeName) {
6645// // need to reduce the RooSimultaneous until fix: https://github.com/root-project/root/issues/8231
6646// std::vector<TString> chanPatterns;
6647// TStringToken pattern(rangeName, ",");
6648// while (pattern.NextToken()) {
6649// chanPatterns.emplace_back(pattern);
6650// }
6651// auto& _cat = const_cast<RooAbsCategoryLValue&>(s->indexCat());
6652// newPdf = std::make_shared<RooSimultaneous>(TString::Format("%s_reduced",GetName()),"Reduced model",_cat);
6653// for(auto& c : variations()) {
6654// TString cName(c->GetName());
6655// cName = cName(cName.Index('=')+1,cName.Length());
6656// _cat.setLabel(cName);
6657// bool matchAny=false;
6658// for(auto& p : chanPatterns) {
6659// if (cName.Contains(TRegexp(p,true))) { matchAny=true; break; }
6660// if (_cat.hasRange(p) && _cat.inRange(p)) { matchAny=true; break; }
6661// }
6662// if(matchAny) {
6663// newPdf->addPdf( *c->get<RooAbsPdf>(), cName );
6664// }
6665// }
6666// RooFitResultTree t(newPdf->GetName(),"",*newPdf);
6667// auto _fr = std::const_pointer_cast<RooFitResult>(t.fitTo(_data.get<RooAbsData>(), &globsSet));
6668// xRooNode parent(_data.GetName(),nullptr,*this);
6669// xRooNode out(_fr->GetName(),/*acquire(_fr)*/ _fr,parent);
6670// // do full propagation by 'checking' the fr ...
6671// out.Checked(&out,true);
6672// return out;
6673// }
6674// }
6675//
6676//
6677//
6678// std::string treeName = TString::Format("fits_%s",GetName()).Data();
6679//
6680// auto _frt = getObject<TTree>(treeName); // get existing frt
6681//
6682// std::shared_ptr<RooFitResultTree> t;
6683// if (_frt) {
6684// t = std::make_shared<RooFitResultTree>(_frt.get());
6685// } else {
6686// t = std::make_shared<RooFitResultTree>(treeName.c_str(),"",*_pdf);
6687// }
6688// //t->SetProgress(true);
6689// auto _fr = std::const_pointer_cast<RooFitResult>(t->fitTo(_data.get<RooAbsData>(), &globsSet));
6690//
6691//
6692//
6693// /*
6694// obs().argList() = s; // sets global observables to their values
6695// auto _fr =
6696// std::shared_ptr<RooFitResult>(_pdf->fitTo(*_data->get<RooAbsData>(),RooFit::GlobalObservables(s),RooFit::Offset(true),RooFit::Save()));
6697// _fr->SetName(TUUID().AsString());
6698// // restore parameters before returning
6699// *std::unique_ptr<RooArgSet>(_pdf->getDependents(_fr->floatParsFinal())) = _fr->floatParsInit();
6700// */
6701//
6702// //_fr->SetTitle(TString::Format("%s;%s",GetName(),datasetName));
6703// if (!_frt) {
6704// t =
6705// std::make_shared<RooFitResultTree>(std::dynamic_pointer_cast<TTree>(const_cast<xRooNode*>(this)->acquire(t->fTree)).get());
6706// }
6707// xRooNode parent(_data.GetName(),nullptr,xRooNode(t,*this));
6708// xRooNode out(_fr->GetName(),/*acquire(_fr)*/ _fr,parent);
6709// // do full propagation by 'checking' the fr ...
6710// out.Checked(&out,true);
6711// return out;
6712// }
6713
6714std::shared_ptr<xRooNode> xRooNode::parentPdf() const
6715{
6716 // find first parent that is a pdf
6717 auto out = fParent;
6718 while (out && !out->get<RooAbsPdf>()) {
6719 out = out->fParent;
6720 }
6721 return out;
6722}
6723
6724xRooNode xRooNode::reduced(const std::string &_range, bool invert) const
6725{
6726 auto rangeName = (_range.empty()) ? GetRange() : _range;
6727 if (!rangeName.empty()) {
6728 std::vector<TString> patterns;
6729 TStringToken pattern(rangeName, ",");
6730 while (pattern.NextToken()) {
6731 patterns.emplace_back(pattern);
6732 }
6733 if (auto s = get<RooSimultaneous>(); s) {
6734 // need to reduce the RooSimultaneous until fix: https://github.com/root-project/root/issues/8231
6735 auto &_cat = const_cast<RooAbsCategoryLValue &>(s->indexCat());
6736 auto newPdf =
6737 std::make_shared<RooSimultaneous>(TString::Format("%s_reduced", GetName()), "Reduced model", _cat);
6738 for (auto &c : bins()) {
6739 TString cName(c->GetName());
6740 cName = cName(cName.Index('=') + 1, cName.Length());
6741 _cat.setLabel(cName);
6742 bool matchAny = false;
6743 for (auto &p : patterns) {
6744 if (cName.Contains(TRegexp(p, true))) {
6745 matchAny = true;
6746 break;
6747 }
6748 if (_cat.hasRange(p) && _cat.inRange(p)) {
6749 matchAny = true;
6750 break;
6751 }
6752 }
6753 if ((matchAny && !invert) || (!matchAny && invert)) {
6754 newPdf->addPdf(*c->get<RooAbsPdf>(), cName);
6755 }
6756 }
6757 return xRooNode(newPdf, fParent);
6758 } else if (get() && !get<RooAbsCollection>() && !components().empty()) {
6759 // create a new obj and remove non-matching components
6760 xRooNode out(std::shared_ptr<TObject>(get()->Clone(TString::Format("%s_reduced", get()->GetName()))), fParent);
6761 // go through components and remove any that don't match pattern
6762 std::vector<TObject *> funcs; // to be removed
6763 for (auto &c : out.components()) {
6764 bool matchAny = false;
6765 for (auto &p : patterns) {
6766 if (TString(c->GetName()).Contains(TRegexp(p, true))) {
6767 matchAny = true;
6768 break;
6769 }
6770 }
6771 if (!((matchAny && !invert) || (!matchAny && invert)))
6772 funcs.push_back(c->get());
6773 }
6774 for (auto &c : funcs)
6775 out.Remove(*c);
6776 if (!funcs.empty()) {
6777 if (auto _pdf = out.get<RooRealSumPdf>(); _pdf) {
6778 _pdf->setFloor(false); // remove floor if removed some functions, which allows evaluation of negative
6779 // valued components
6780 }
6781 }
6782 out.browse();
6783 return out;
6784 } else if (auto fr = get<RooFitResult>()) {
6785 // reduce the fit result by moving unselected float pars into the constPars list and dropping their covariances
6786 xRooNode out(std::shared_ptr<TObject>(fr->Clone(TString::Format("%s_reduced", fr->GetName()))), fParent);
6787 fr = out.get<RooFitResult>();
6788 RooArgList _pars = fr->floatParsFinal();
6789 RooArgList _remPars;
6790 for (auto c : _pars) {
6791 bool matchAny = false;
6792 for (auto &p : patterns) {
6793 if (TString(c->GetName()).Contains(TRegexp(p, true))) {
6794 matchAny = true;
6795 break;
6796 }
6797 }
6798 if (!((matchAny && !invert) || (!matchAny && invert))) {
6799 _remPars.add(*c);
6800 }
6801 }
6802 _pars.remove(_remPars, true);
6803
6804 auto _tmp = fr->reducedCovarianceMatrix(_pars);
6805 int covQualBackup = fr->covQual();
6806 fr->setCovarianceMatrix(_tmp);
6807 fr->setCovQual(covQualBackup);
6808 const_cast<RooArgList &>(fr->floatParsFinal())
6809 .remove(_remPars, true); // is this a memory leak ... should delete the remPars?
6810 return out;
6811
6812 } else if (!get() || get<RooAbsCollection>()) {
6813 // filter the children .... handle special case of filtering ".vars" with "x" option too
6814 xRooNode out(std::make_shared<RooArgList>(), fParent);
6815 size_t nobs = 0;
6816 bool notAllArgs = false;
6817 bool isVars = (strcmp(GetName(), ".vars") == 0);
6818 for (auto c : *this) {
6819 nobs += (c->fFolder == "!robs" || c->fFolder == "!globs");
6820 bool matchAny = false;
6821 for (auto &p : patterns) {
6822 if (TString(c->GetName()).Contains(TRegexp(p, true)) ||
6823 (isVars && p == "x" && (c->fFolder == "!robs" || c->fFolder == "!globs") && nobs == 1)) {
6824 matchAny = true;
6825 break;
6826 }
6827 }
6828 if ((matchAny && !invert) || (!matchAny && invert)) {
6829 out.push_back(c);
6830 if (auto a = c->get<RooAbsArg>()) {
6831 out.get<RooArgList>()->add(*a);
6832 } else {
6833 notAllArgs = true;
6834 }
6835 }
6836 }
6837 if (notAllArgs) {
6838 out.fComp.reset();
6839 }
6840 return out;
6841 }
6842 }
6843
6844 return get<RooArgList>() ? xRooNode(std::make_shared<RooArgList>(), fParent) : *this;
6845}
6846
6847// xRooNode xRooNode::generate(bool expected) const {
6848//
6849// auto fr = fitResult();
6850// auto _fr = fr.get<RooFitResult>();
6851//
6852// auto _pdf = (get<RooAbsPdf>()) ? std::shared_ptr<const xRooNode>(this, [](const xRooNode*){}) : parentPdf();
6853// if (!_pdf) {
6854// throw std::runtime_error("Could not find pdf");
6855// }
6856//
6857// std::shared_ptr<RooDataTree> t;
6858//
6859// std::shared_ptr<RooSimultaneous> newPdf;
6860// if(auto s = _pdf->get<RooSimultaneous>(); s) {
6861// auto rangeName = GetRange();
6862// if (rangeName) {
6863// // need to reduce the RooSimultaneous until fix: https://github.com/root-project/root/issues/8231
6864// std::vector<TString> chanPatterns;
6865// TStringToken pattern(rangeName, ",");
6866// while (pattern.NextToken()) {
6867// chanPatterns.emplace_back(pattern);
6868// }
6869// auto& _cat = const_cast<RooAbsCategoryLValue&>(s->indexCat());
6870// newPdf = std::make_shared<RooSimultaneous>(TString::Format("%s_reduced",GetName()),"Reduced model",_cat);
6871// for(auto& c : _pdf->variations()) {
6872// TString cName(c->GetName());
6873// cName = cName(cName.Index('=')+1,cName.Length());
6874// _cat.setLabel(cName);
6875// bool matchAny=false;
6876// for(auto& p : chanPatterns) {
6877// if (cName.Contains(TRegexp(p,true))) { matchAny=true; break; }
6878// if (_cat.hasRange(p) && _cat.inRange(p)) { matchAny=true; break; }
6879// }
6880// if(matchAny) {
6881// newPdf->addPdf( *c->get<RooAbsPdf>(), cName );
6882// }
6883// }
6884// t = std::make_shared<RooDataTree>(newPdf->GetName(),"",*newPdf);
6885// RooArgSet s1(_pdf->obs().argList());
6886// RooArgSet s2(_pdf->globs().argList());s1.remove(s2);
6887// t->SetObservables(&s1,&s2);
6888// auto _data = t->generate(_fr,expected);
6889//
6890// xRooNode parent(_fr ? _fr->GetName() : "unknown",nullptr,xRooNode(t,*this));
6891// xRooNode out(_data.first->GetName(),/*acquire(_fr)*/ _data.first,parent);
6892// out.emplace_back(std::make_shared<xRooNode>(".globs",std::const_pointer_cast<RooArgSet>(_data.second),out));
6893// return out;
6894// }
6895// }
6896//
6897//
6898// std::string treeName = TString::Format("gen_%s",_pdf->GetName()).Data();
6899//
6900// auto _frt = getObject<TTree>(treeName); // get existing frt
6901//
6902//
6903// if (_frt) {
6904// t = std::make_shared<RooDataTree>(_frt.get());
6905// } else {
6906// t = std::make_shared<RooDataTree>(treeName.c_str(),"",*_pdf->get<RooAbsPdf>());
6907// RooArgSet s1(_pdf->obs().argList());
6908// RooArgSet s2(_pdf->globs().argList());s1.remove(s2);
6909// t->SetObservables(&s1,&s2);
6910// }
6911// auto _data = t->generate(_fr,expected);
6912// if (!_frt) {
6913// t =
6914// std::make_shared<RooDataTree>(std::dynamic_pointer_cast<TTree>(const_cast<xRooNode*>(this)->acquire(t->fTree)).get());
6915// }
6916// xRooNode parent(_fr ? _fr->GetName() : "unknown",nullptr,xRooNode(t,*this));
6917// xRooNode out(_data.first->GetName(),/*acquire(_fr)*/ _data.first,parent);
6918// out.emplace_back(std::make_shared<xRooNode>(".globs",std::const_pointer_cast<RooArgSet>(_data.second),out));
6919// return out;
6920// }
6921
6923public:
6925 double expectedEvents(const RooArgSet *nset) const override
6926 {
6927 return static_cast<RooAbsPdf *>(intpdf.absArg())->expectedEvents(nset);
6928 }
6929 ExtendMode extendMode() const override { return static_cast<RooAbsPdf *>(intpdf.absArg())->extendMode(); }
6930 TObject *clone(const char *newname) const override { return new xRooProjectedPdf(*this, newname); }
6931
6932protected:
6933 double evaluate() const override
6934 {
6935 int code;
6936 return getProjection(&intobs, _normSet, (_normRange.Length() > 0 ? _normRange.Data() : nullptr), code)->getVal();
6937 }
6938};
6939
6940class PdfWrapper : public RooAbsPdf {
6941public:
6942 // need expPdf option while RooProjectedPdf doesn't support keeping things extended
6943 PdfWrapper(RooAbsReal &f, RooAbsReal *coef, bool expEvMode = false, RooAbsPdf *expPdf = nullptr)
6944 : RooAbsPdf(Form("exp_%s", f.GetName())),
6945 fFunc("func", "func", this, f),
6946 fCoef("coef", "coef", this),
6947 fExpPdf("expPdf", "expPdf", this)
6948 {
6949 // don't treat pdf as extended if it has a coefficient and is RooAddPdf: RooAddPdf doesn't extend them unless no
6950 // coefs for any (and all are extendable)
6951 if (coef) {
6952 fCoef.setArg(*coef);
6953 }
6954 if (expPdf && expPdf->canBeExtended() && !(coef && dynamic_cast<RooAddPdf *>(expPdf))) {
6955 fExpPdf.setArg(*expPdf);
6956 } else if (auto _p = dynamic_cast<RooAbsPdf *>(&f);
6957 _p && _p->canBeExtended() && !(coef && dynamic_cast<RooAddPdf *>(_p))) {
6958 fExpPdf.setArg(f); // using self for expectation
6959 }
6960 fExpectedEventsMode = expEvMode;
6961 }
6962 ~PdfWrapper() override{};
6963 PdfWrapper(const PdfWrapper &other, const char *name = nullptr)
6964 : RooAbsPdf(other, name),
6965 fFunc("func", this, other.fFunc),
6966 fCoef("coef", this, other.fCoef),
6967 fExpPdf("expPdf", this, other.fExpPdf),
6968 fExpectedEventsMode(other.fExpectedEventsMode)
6969 {
6970 }
6971 TObject *clone(const char *newname) const override { return new PdfWrapper(*this, newname); }
6972 bool isBinnedDistribution(const RooArgSet &obs) const override { return fFunc->isBinnedDistribution(obs); }
6973 std::list<double> *binBoundaries(RooAbsRealLValue &obs, double xlo, double xhi) const override
6974 {
6975 return fFunc->binBoundaries(obs, xlo, xhi);
6976 }
6977
6978 double evaluate() const override
6979 {
6980 return (fExpectedEventsMode ? 1. : fFunc) *
6981 ((fExpPdf.absArg()) ? static_cast<RooAbsPdf *>(fExpPdf.absArg())->expectedEvents(_normSet) : 1.) *
6982 (fCoef.absArg() ? fCoef : 1.);
6983 }
6984
6985 bool selfNormalized() const override
6986 {
6987 return true;
6988 } // so that doesn't try to do an integral because we are passing integration onto fFunc in evaluate
6989
6990 // faster than full evaluation because doesnt make the integral dependent on the full expression
6991 double getSimplePropagatedError(const RooFitResult &fr, const RooArgSet &nset_in) const
6992 {
6993#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 28, 00)
6994 double oo = getPropagatedError(fr, nset_in); // method was improved in 6.28 so use this instead
6995 if (std::isnan(oo)) {
6996 // may be consequence of zero uncerts
6997 // Calling getParameters() might be costly, but necessary to get the right
6998 // parameters in the RooAbsReal. The RooFitResult only stores snapshots.
6999 RooArgSet allParamsInAbsReal;
7000 getParameters(&nset_in, allParamsInAbsReal);
7001
7002 RooArgList paramList;
7003 for (auto *rrvFitRes : static_range_cast<RooRealVar *>(fr.floatParsFinal())) {
7004
7005 auto rrvInAbsReal = static_cast<RooRealVar const *>(allParamsInAbsReal.find(*rrvFitRes));
7006
7007 // If this RooAbsReal is a RooRealVar in the fit result, we don't need to
7008 // propagate anything and can just return the error in the fit result
7009 if (rrvFitRes->namePtr() == namePtr())
7010 return rrvFitRes->getError();
7011
7012 // Strip out parameters with zero error
7013 if (!rrvFitRes->hasError() ||
7014 rrvFitRes->getError() <= std::abs(rrvFitRes->getVal()) * std::numeric_limits<double>::epsilon())
7015 continue;
7016
7017 // Ignore parameters in the fit result that this RooAbsReal doesn't depend on
7018 if (!rrvInAbsReal)
7019 continue;
7020
7021 // Checking for float equality is a bad. We check if the values are
7022 // negligibly far away from each other, relative to the uncertainty.
7023 if (std::abs(rrvInAbsReal->getVal() - rrvFitRes->getVal()) > 0.01 * rrvFitRes->getError()) {
7024 std::stringstream errMsg;
7025 errMsg
7026 << "RooAbsReal::getPropagatedError(): the parameters of the RooAbsReal don't have"
7027 << " the same values as in the fit result! The logic of getPropagatedError is broken in this case.";
7028
7029 throw std::runtime_error(errMsg.str());
7030 }
7031
7032 paramList.add(*rrvInAbsReal);
7033 }
7034 if (paramList.empty())
7035 return 0.;
7036
7037 std::vector<double> plusVar;
7038 std::vector<double> minusVar;
7039 plusVar.reserve(paramList.size());
7040 minusVar.reserve(paramList.size());
7041
7042 // Create std::vector of plus,minus variations for each parameter
7043 TMatrixDSym V(paramList.size() == fr.floatParsFinal().size() ? fr.covarianceMatrix()
7044 : fr.reducedCovarianceMatrix(paramList));
7045
7046 for (std::size_t ivar = 0; ivar < paramList.size(); ivar++) {
7047
7048 auto &rrv = static_cast<RooRealVar &>(paramList[ivar]);
7049
7050 double cenVal = rrv.getVal();
7051 double errVal = sqrt(V(ivar, ivar));
7052
7053 // this next thing happens if the par has errors but the covariance matrix is empty
7054 // this only happens if the fit was dodgy, so perhaps best to not even try to recover from this
7055 // screwup ... hence I've commented out this fixup here and will let the errors be nan
7056 // if(errVal==0) {
7057 // Warning("getPropagatedError","Missing variance for %s",rrv.GetName());
7058 // errVal = rrv.getError();
7059 // V(ivar,ivar) = errVal*errVal;
7060 // }
7061
7062 // Make Plus variation
7063 rrv.setVal(cenVal + errVal);
7064 plusVar.push_back(getVal(nset_in));
7065
7066 // Make Minus variation
7067 rrv.setVal(cenVal - errVal);
7068 minusVar.push_back(getVal(nset_in));
7069#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
7070 // can try to recover nans ... this stopped being possible in 6.27 onwards because NaNPacker made private
7071 if (std::isnan(plusVar.back()) && RooNaNPacker::isNaNWithPayload(plusVar.back())) {
7072 plusVar.back() = -RooNaNPacker::unpackNaN(plusVar.back());
7073 }
7074 if (std::isnan(minusVar.back()) && RooNaNPacker::isNaNWithPayload(minusVar.back())) {
7075 minusVar.back() = -RooNaNPacker::unpackNaN(minusVar.back());
7076 }
7077#endif
7078 // std::cout << plusVar.back() << " and " << minusVar.back() << std::endl;
7079
7080 rrv.setVal(cenVal);
7081 }
7082
7083 // Re-evaluate this RooAbsReal with the central parameters just to be
7084 // extra-safe that a call to `getPropagatedError()` doesn't change any state.
7085 // It should not be necessary because thanks to the dirty flag propagation
7086 // the RooAbsReal is re-evaluated anyway the next time getVal() is called.
7087 // Still there are imaginable corner cases where it would not be triggered,
7088 // for example if the user changes the RooFit operation more after the error
7089 // propagation.
7090 getVal(nset_in);
7091
7092 TMatrixDSym C(paramList.size());
7093 std::vector<double> errVec(paramList.size());
7094 for (std::size_t i = 0; i < paramList.size(); i++) {
7095 errVec[i] = std::sqrt(V(i, i));
7096 for (std::size_t j = i; j < paramList.size(); j++) {
7097 C(i, j) = V(i, j) / std::sqrt(V(i, i) * V(j, j));
7098 C(j, i) = C(i, j);
7099 }
7100 }
7101
7102 // Make std::vector of variations
7103 TVectorD F(plusVar.size());
7104 for (unsigned int j = 0; j < plusVar.size(); j++) {
7105 F[j] = (plusVar[j] - minusVar[j]) / 2;
7106 }
7107
7108 // Calculate error in linear approximation from variations and correlation coefficient
7109 double sum = F * (C * F);
7110
7111 return sqrt(sum);
7112 }
7113 return oo;
7114#else
7115
7116 // Strip out parameters with zero error
7117 RooArgList fpf_stripped;
7118 for (auto *frv : static_range_cast<RooRealVar *>(fi.floatParsFinal())) {
7119 if (frv->getError() > 1e-20) {
7120 fpf_stripped.add(*frv);
7121 }
7122 }
7123
7124 // Clone self for internal use
7125 RooAbsReal *cloneFunc = const_cast<PdfWrapper *>(this); // (RooAbsReal *)fFunc.absArg()->cloneTree();
7126 // RooAbsPdf *clonePdf = dynamic_cast<RooAbsPdf *>(cloneFunc);
7127 RooArgSet *errorParams = cloneFunc->getObservables(fpf_stripped);
7128
7129 RooArgSet *nset =
7130 nset_in.size() == 0 ? cloneFunc->getParameters(*errorParams) : cloneFunc->getObservables(nset_in);
7131
7132 // Make list of parameter instances of cloneFunc in order of error matrix
7133 RooArgList paramList;
7134 const RooArgList &fpf = fpf_stripped;
7135 std::vector<int> fpf_idx;
7136 for (Int_t i = 0; i < fpf.size(); i++) {
7137 RooAbsArg *par = errorParams->find(fpf[i].GetName());
7138 if (par) {
7139 paramList.add(*par);
7140 fpf_idx.push_back(i);
7141 }
7142 }
7143
7144 std::vector<double> plusVar, minusVar;
7145
7146 // Create vector of plus,minus variations for each parameter
7147 TMatrixDSym V(paramList.size() == fr.floatParsFinal().size() ? fr.covarianceMatrix()
7148 : fr.reducedCovarianceMatrix(paramList));
7149
7150 for (Int_t ivar = 0; ivar < paramList.size(); ivar++) {
7151
7152 RooRealVar &rrv = (RooRealVar &)fpf[fpf_idx[ivar]];
7153
7154 double cenVal = rrv.getVal();
7155 double errVal = sqrt(V(ivar, ivar));
7156
7157 // Make Plus variation
7158 ((RooRealVar *)paramList.at(ivar))->setVal(cenVal + errVal);
7159 // plusVar.push_back((fExpectedEventsMode ? 1. : cloneFunc->getVal(nset)) *
7160 // (clonePdf ? clonePdf->expectedEvents(nset) : 1.));
7161 plusVar.push_back(cloneFunc->getVal(nset));
7162
7163 // Make Minus variation
7164 ((RooRealVar *)paramList.at(ivar))->setVal(cenVal - errVal);
7165 // minusVar.push_back((fExpectedEventsMode ? 1. : cloneFunc->getVal(nset)) *
7166 // (clonePdf ? clonePdf->expectedEvents(nset) : 1.));
7167 minusVar.push_back(cloneFunc->getVal(nset));
7168
7169 ((RooRealVar *)paramList.at(ivar))->setVal(cenVal);
7170 }
7171
7172 getVal(nset); // reset state
7173
7174 TMatrixDSym C(paramList.size());
7175 std::vector<double> errVec(paramList.size());
7176 for (int i = 0; i < paramList.size(); i++) {
7177 errVec[i] = sqrt(V(i, i));
7178 for (int j = i; j < paramList.size(); j++) {
7179 C(i, j) = V(i, j) / sqrt(V(i, i) * V(j, j));
7180 C(j, i) = C(i, j);
7181 }
7182 }
7183
7184 // Make vector of variations
7185 TVectorD F(plusVar.size());
7186 for (unsigned int j = 0; j < plusVar.size(); j++) {
7187 F[j] = (plusVar[j] - minusVar[j]) / 2;
7188 }
7189
7190 // Calculate error in linear approximation from variations and correlation coefficient
7191 double sum = F * (C * F);
7192
7193 // delete cloneFunc;
7194 delete errorParams;
7195 delete nset;
7196
7197 return sqrt(sum);
7198#endif
7199 }
7200
7201private:
7205 bool fExpectedEventsMode = false;
7206};
7207
7208const xRooNode *runningNode = nullptr;
7210
7212{
7213 std::cout << "Got signal " << signum << std::endl;
7214 if (signum == SIGINT) {
7215 std::cout << "Keyboard interrupt while building histogram" << std::endl;
7216 // TODO: create a global mutex for this
7217 runningNode->fInterrupted = true;
7218 } else {
7219 gOldHandlerr(signum);
7220 }
7221}
7222
7224{
7225 auto _doSterilize = [](RooAbsArg *obj) {
7226 if (!obj)
7227 return;
7228 for (int i = 0; i < obj->numCaches(); i++) {
7229 if (auto cache = dynamic_cast<RooObjCacheManager *>(obj->getCache(i))) {
7230 cache->reset();
7231 }
7232 }
7233 if (RooAbsPdf *p = dynamic_cast<RooAbsPdf *>(obj); p) {
7234 p->setNormRange(p->normRange());
7235 }
7236#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
7237 if (RooAbsReal *p = dynamic_cast<RooAbsReal *>(obj); p) {
7238 // need to forget about any normSet that was passed to getVal(...)
7239 // doesn't seem necessary in 6.28
7240
7241 p->setProxyNormSet(nullptr);
7242 p->_lastNSet = nullptr;
7243 }
7244#endif
7245 obj->setValueDirty();
7246 };
7247 if (auto w = get<RooWorkspace>(); w) {
7248 // sterilizing all nodes
7249 for (auto &c : w->components()) {
7250 _doSterilize(c);
7251 }
7252 return;
7253 }
7254 // recursive through all clients and sterlize their normalization caches
7255 std::function<void(RooAbsArg *)> func;
7256 func = [&](RooAbsArg *a) {
7257 if (!a) {
7258 return;
7259 }
7260 _doSterilize(a); // sterilize first so that cache elements don't appear in the client list
7261 // safety net in case sterilizing one client deletes another one of our clients
7262 // monitor for change in clients list size
7263 // found this was only case in 6.26 (valgrind shows invalid read), in 6.28 these went away
7264 // might be in 6.28 the client list iterator became able to handle in-loop edits but didn't see
7265 // in test case that client count changed so just resterilizing if that's the case.
7266 size_t nClients;
7267 do {
7268 nClients = a->clients().size();
7269 for (auto obj : a->clients()) {
7270 func(dynamic_cast<RooAbsArg *>(obj));
7271 if (a->clients().size() != nClients) {
7272 break; // means sterilizing a client changed our clients, so don't trust the client iterator at this
7273 // point
7274 }
7275 }
7276 } while (a->clients().size() != nClients);
7277 };
7278 func(get<RooAbsArg>());
7279}
7280
7281// observables not in the axisVars are automatically projected over
7282xRooNode xRooNode::histo(const xRooNode &vars, const xRooNode &fr, bool content, bool errors) const
7283{
7284
7285 if (!vars.fComp && strlen(vars.GetName())) {
7286 return histo(xRooNode::vars().reduced(vars.GetName()), fr, content, errors);
7287 }
7288
7289 xRooNode out(TString::Format("%s.histo", GetName()), nullptr, *this);
7290
7291 RooAbsLValue *v = nullptr;
7292 if (vars.empty()) {
7293 out.fComp = std::shared_ptr<TH1>(BuildHistogram(nullptr, !content, errors, -1, -1, fr));
7294 } else if (vars.size() == 1) {
7295 v = vars.at(0)->get<RooAbsLValue>();
7296 out.fComp = std::shared_ptr<TH1>(BuildHistogram(v, !content, errors, 1, 0, fr));
7297 } else {
7298 throw std::runtime_error("multi-dim histo not yet supported");
7299 }
7300
7301 if (auto h = out.get<TH1>()) {
7302 if (h->GetXaxis()->IsAlphanumeric()) {
7303 // do this to get bin labels
7304 h->GetXaxis()->SetName("xaxis"); // WARNING -- this messes up anywhere we GetXaxis()->GetName()
7305 }
7306 h->SetStats(false);
7307 h->SetName(GetName());
7308 auto hCopy = static_cast<TH1 *>(h->Clone("nominal"));
7309
7310 if (content && !components().empty()) {
7311 RooAbsReal *sf = nullptr; // TODO - support case of RooExtendPdf drawing (see ::Draw)
7312 // build a stack
7313 THStack *stack = new THStack("stack", "");
7314 int count = 2;
7315 std::map<std::string, int> colorByTitle; // TODO: should fill from any existing legend
7316 std::set<std::string> allTitles;
7317 bool titleMatchName = true;
7318 std::map<std::string, TH1 *> histGroups;
7319 std::vector<TH1 *> hhs;
7320
7321 // support for CMS model case where has single component containing many coeffs
7322 // will build stack by setting each coeff equal to 0 in turn, rebuilding the histogram
7323 // the difference from the "full" histogram will be the component
7324 RooArgList cms_coefs;
7325 if (!components().empty()) {
7326 auto comps = components()[0];
7327 for (auto &c : *comps) {
7328 if (c->fFolder == "!.coeffs")
7329 cms_coefs.add(*c->get<RooAbsArg>());
7330 }
7331 }
7332
7333 if (!cms_coefs.empty()) {
7334 RooRealVar zero("zero", "", 0);
7335 std::shared_ptr<TH1> prevHist(static_cast<TH1 *>(h->Clone()));
7336 for (auto c : cms_coefs) {
7337 // seems I have to remake the function each time, as haven't figured out what cache needs clearing?
7338 std::unique_ptr<RooAbsReal> f(dynamic_cast<RooAbsReal *>(components()[0]->get()->Clone("tmpCopy")));
7339 zero.setAttribute(
7340 Form("ORIGNAME:%s", c->GetName())); // used in redirectServers to say what this replaces
7341 f->redirectServers(RooArgSet(zero), false, true); // each time will replace one additional coef
7342 // zero.setAttribute(Form("ORIGNAME:%s",c->GetName()),false); (commented out so that on next iteration
7343 // will still replace all prev)
7344 auto hh = xRooNode(*f, *this).BuildHistogram(v, false, false, !v ? -1 : 1, !v ? -1 : 0, fr);
7345 if (sf)
7346 hh->Scale(sf->getVal());
7347 if (strlen(hh->GetTitle()) == 0)
7348 hh->SetTitle(c->GetName()); // ensure all hists has titles
7349 titleMatchName &= (TString(c->GetName()) == hh->GetTitle() ||
7350 TString(hh->GetTitle()).BeginsWith(TString(c->GetName()) + "_"));
7351 std::shared_ptr<TH1> nextHist(static_cast<TH1 *>(hh->Clone()));
7352 hh->Add(prevHist.get(), -1.);
7353 hh->Scale(-1.);
7354 hhs.push_back(hh);
7355 prevHist = nextHist;
7356 }
7357 } else {
7358 for (auto &samp : components()) {
7359 auto hh = samp->BuildHistogram(v, false, false, !v ? -1 : 1, !v ? -1 : 0, fr);
7360 if (sf)
7361 hh->Scale(sf->getVal());
7362 hhs.push_back(hh);
7363 if (strlen(hh->GetTitle()) == 0)
7364 hh->SetTitle(samp->GetName()); // ensure all hists has titles
7365 titleMatchName &= (TString(samp->GetName()) == hh->GetTitle() ||
7366 TString(hh->GetTitle()).BeginsWith(TString(samp->GetName()) + "_"));
7367 }
7368 }
7369 for (auto &hh : hhs) {
7370 if (h->GetXaxis()->IsAlphanumeric()) {
7371 // must ensure bin labels match for stack
7372 hh->GetXaxis()->SetName("xaxis");
7373 for (int i = 1; i <= hh->GetNbinsX(); i++)
7374 hh->GetXaxis()->SetBinLabel(i, h->GetXaxis()->GetBinLabel(i));
7375 }
7376 // automatically group hists that all have the same title
7377 if (histGroups.find(hh->GetTitle()) == histGroups.end()) {
7378 histGroups[hh->GetTitle()] = hh;
7379 } else {
7380 // add it into this group
7381 histGroups[hh->GetTitle()]->Add(hh);
7382 delete hh;
7383 continue;
7384 }
7385 auto hhMin = (hh->GetMinimum() == 0) ? hh->GetMinimum(1e-9) : hh->GetMinimum();
7386 if (!stack->GetHists() && h->GetMinimum() > hhMin) {
7387 auto newMin = hhMin - (h->GetMaximum() - hhMin) * gStyle->GetHistTopMargin();
7388 if (hhMin >= 0 && newMin < 0)
7389 newMin = hhMin * 0.99;
7390 h->SetMinimum(newMin); /// adjustYRange(newMin, h->GetMaximum());
7391 }
7392 if (auto it = colorByTitle.find(hh->GetTitle()); it != colorByTitle.end()) {
7393 hh->SetFillColor(it->second);
7394 } else {
7395 bool used = false;
7396 do {
7397 hh->SetFillColor((count++) % 100);
7398 // check not already used this color
7399 used = false;
7400 for (auto hh2 : hhs) {
7401 if (hh != hh2 && hh2->GetFillColor() == hh->GetFillColor()) {
7402 used = true;
7403 break;
7404 }
7405 }
7406 } while (used);
7407 colorByTitle[hh->GetTitle()] = hh->GetFillColor();
7408 }
7409 /*if(stack->GetHists() && stack->GetHists()->GetEntries()>0) {
7410 // to remove rounding effects on bin boundaries, see if binnings compatible
7411 auto _h1 = dynamic_cast<TH1*>(stack->GetHists()->At(0));
7412 if(_h1->GetNbinsX()==hh->GetNbinsX()) TODO ... finish dealing with silly rounding effects
7413 }*/
7414 TString thisOpt = ""; /// dOpt;
7415 // uncomment next line to blend continuous with discrete components .. get some unpleasant "poke through"
7416 // effects though
7417 // if(auto s = samp->get<RooAbsReal>(); s) thisOpt = s->isBinnedDistribution(*dynamic_cast<RooAbsArg*>(v)) ?
7418 // "" : "LF2";
7419 stack->Add(hh, thisOpt);
7420 allTitles.insert(hh->GetTitle());
7421 }
7422
7423 TList *ll = stack->GetHists();
7424 if (ll && ll->GetEntries()) {
7425
7426 // get common prefix to strip off only if all titles match names and
7427 // any title is longer than 10 chars
7428 size_t e = std::min(allTitles.begin()->size(), allTitles.rbegin()->size());
7429 size_t ii = 0;
7430 bool goodPrefix = false;
7431 std::string commonSuffix;
7432 if (titleMatchName && ll->GetEntries() > 1) {
7433 while (ii < e - 1 && allTitles.begin()->at(ii) == allTitles.rbegin()->at(ii)) {
7434 ii++;
7435 if (allTitles.begin()->at(ii) == '_' || allTitles.begin()->at(ii) == ' ')
7436 goodPrefix = true;
7437 }
7438
7439 // find common suffix if there is one .. must start with a "_"
7440 bool stop = false;
7441 while (!stop && commonSuffix.size() < size_t(e - 1)) {
7442 commonSuffix = allTitles.begin()->substr(allTitles.begin()->length() - commonSuffix.length() - 1);
7443 for (auto &t : allTitles) {
7444 if (!TString(t).EndsWith(commonSuffix.c_str())) {
7445 commonSuffix = commonSuffix.substr(1);
7446 stop = true;
7447 break;
7448 }
7449 }
7450 }
7451 if (commonSuffix.find('_') == std::string::npos) {
7452 commonSuffix = "";
7453 } else {
7454 commonSuffix = commonSuffix.substr(commonSuffix.find('_'));
7455 }
7456 }
7457 if (!goodPrefix)
7458 ii = 0;
7459
7460 // also find how many characters are needed to distinguish all entries (that dont have the same name)
7461 // then carry on up to first space or underscore
7462 size_t jj = 0;
7463 std::map<std::string, std::string> reducedTitles;
7464 while (reducedTitles.size() != allTitles.size()) {
7465 jj++;
7466 std::map<std::string, int> titlesMap;
7467 for (auto &s : allTitles) {
7468 if (reducedTitles.count(s))
7469 continue;
7470 titlesMap[s.substr(0, jj)]++;
7471 }
7472 for (auto &s : allTitles) {
7473 if (titlesMap[s.substr(0, jj)] == 1 && (jj >= s.length() || s.at(jj) == ' ' || s.at(jj) == '_')) {
7474 reducedTitles[s] = s.substr(0, jj);
7475 }
7476 }
7477 }
7478
7479 // strip common prefix and suffix before adding
7480 for (int i = ll->GetEntries() - 1; i >= 0; i--) { // go in reverse order
7481 auto _title = (ll->GetEntries() > 5) ? reducedTitles[ll->At(i)->GetTitle()] : ll->At(i)->GetTitle();
7482 _title = _title.substr(ii < _title.size() ? ii : 0);
7483 if (!commonSuffix.empty() && TString(_title).EndsWith(commonSuffix.c_str()))
7484 _title = _title.substr(0, _title.length() - commonSuffix.length());
7485
7486 dynamic_cast<TNamed *>(ll->At(i))->SetTitle(_title.c_str());
7487
7488 // style hists according to available styles ... creating if necessary
7489 auto _style = xRooNode(*ll->At(i), *this).style(ll->At(i));
7490 if (_style) {
7491 *dynamic_cast<TAttLine *>(ll->At(i)) = *_style;
7492 *dynamic_cast<TAttFill *>(ll->At(i)) = *_style;
7493 *dynamic_cast<TAttMarker *>(ll->At(i)) = *_style;
7494 }
7495 }
7496 }
7497 h->GetListOfFunctions()->Add(stack, "noclearsame");
7498 if (h->GetSumw2() && h->GetSumw2()->GetSum()) {
7499 hCopy->SetFillStyle(3005);
7500 hCopy->SetFillColor(h->GetLineColor());
7501 hCopy->SetMarkerStyle(0);
7502 h->GetListOfFunctions()->Add(hCopy->Clone(".copy"), "e2same");
7503 *static_cast<TAttFill *>(hCopy) = *h;
7504 }
7505 }
7506
7507 h->GetListOfFunctions()->Add(hCopy, "histsame");
7508 if (h->GetSumw2() && h->GetSumw2()->GetSum()) {
7509 h->SetFillStyle(3005);
7510 h->SetFillColor(h->GetLineColor());
7511 h->SetMarkerStyle(0);
7512 }
7513 }
7514
7515 return out;
7516}
7517
7519{
7520 return xRooNode(fComp, xRooNode(range.GetName(), nullptr, *this));
7521}
7522
7523TH1 *xRooNode::BuildHistogram(RooAbsLValue *v, bool empty, bool errors, int binStart, int binEnd,
7524 const xRooNode &_fr) const
7525{
7526 auto rar = get<RooAbsReal>();
7527 if (!rar)
7528 return nullptr;
7529
7530 TObject *vv = rar;
7531
7532 auto t = TH1::AddDirectoryStatus();
7533 TH1::AddDirectory(false);
7534 TH1 *h = nullptr;
7535 if (!v) {
7536 if (binStart != -1 || binEnd != -1) { // allow v to stay nullptr if doing integral (binStart=binEnd=-1)
7537 if (auto _ax = GetXaxis())
7538 v = dynamic_cast<RooAbsLValue *>(_ax->GetParent());
7539 } else {
7540 // don't need to integrate if doing a self-histogram
7541 v = dynamic_cast<RooRealVar *>(rar);
7542 }
7543 if (v) {
7544 vv = dynamic_cast<TObject *>(v);
7545 } else {
7546 // make a single-bin histogram of just this value
7547 h = new TH1D(rar->GetName(), rar->GetTitle(), 1, 0, 1);
7548 h->GetXaxis()->SetBinLabel(1, rar->GetName());
7549 h->GetXaxis()->SetName(rar->GetName());
7550 }
7551 }
7552
7553 auto x = dynamic_cast<RooRealVar *>(v);
7554 bool setTitle = false;
7555 if (x) {
7556 if (x == rar) {
7557 // self histogram ...
7558 h = new TH1D(rar->GetName(), rar->GetTitle(), 1, 0, 1);
7559 h->Sumw2();
7560 h->GetXaxis()->SetBinLabel(1, rar->GetName());
7561 h->SetBinContent(1, rar->getVal());
7562 if (x->hasError())
7563 h->SetBinError(1, x->getError());
7564 h->SetMaximum(x->hasMax() ? x->getMax()
7565 : (h->GetBinContent(1) + std::max(std::abs(h->GetBinContent(1) * 0.1), 50.)));
7566 h->SetMinimum(x->hasMin() ? x->getMin()
7567 : (h->GetBinContent(1) - std::max(std::abs(h->GetBinContent(1) * 0.1), 50.)));
7568 h->GetXaxis()->SetName(dynamic_cast<TObject *>(v)->GetName());
7569 return h;
7570 }
7571 auto _ax = GetXaxis();
7572 TString binningName = (_ax && _ax->GetParent() == x) ? _ax->GetName() : rar->getStringAttribute("binning");
7573 if (binningName == "")
7574 binningName = rar->GetName();
7575 if (x->hasBinning(binningName)) {
7576 if (x->getBinning(binningName).isUniform()) {
7577 h = new TH1D(rar->GetName(), rar->GetTitle(), x->numBins(binningName) <= 0 ? 100 : x->numBins(binningName),
7578 x->getMin(binningName), x->getMax(binningName));
7579 } else {
7580 h = new TH1D(rar->GetName(), rar->GetTitle(), x->numBins(binningName), x->getBinning(binningName).array());
7581 }
7582 h->GetXaxis()->SetTitle(x->getBinning(binningName).GetTitle());
7583 setTitle = true;
7584 } else if (auto _boundaries =
7585 _or_func(/*rar->plotSamplingHint(*x,x->getMin(),x->getMax())*/ (std::list<double> *)(nullptr),
7586 rar->binBoundaries(*x, -std::numeric_limits<double>::infinity(),
7587 std::numeric_limits<double>::infinity()));
7588 _boundaries) {
7589 std::vector<double> _bins;
7590 for (auto &b : *_boundaries) {
7591 if (_bins.empty() || std::abs(_bins.back() - b) > 1e-5 * _bins.back())
7592 _bins.push_back(b);
7593 } // found sometimes get virtual duplicates in the binning
7594 h = new TH1D(rar->GetName(), rar->GetTitle(), _bins.size() - 1, &_bins[0]);
7595 delete _boundaries;
7596 } else if (!x->hasMax() || !x->hasMin()) {
7597 // use current value of x to estimate range with
7598 h = new TH1D(rar->GetName(), rar->GetTitle(), v->numBins(), x->getVal() * 0.2, x->getVal() * 5);
7599 } else {
7600 h = new TH1D(rar->GetName(), rar->GetTitle(), v->numBins(), x->getBinning().array());
7601 }
7602
7603 } else if (!h) {
7604 h = new TH1D(rar->GetName(), rar->GetTitle(), v->numBins(rar->GetName()), 0, v->numBins(rar->GetName()));
7605 if (auto cat = dynamic_cast<RooAbsCategoryLValue *>(v)) {
7606 for (int i = 0; i < cat->numTypes(); i++) {
7607 cat->setBin(i);
7608 h->GetXaxis()->SetBinLabel(i + 1, cat->getLabel());
7609 }
7610 }
7611 }
7612 if (auto o = dynamic_cast<TObject *>(v); o && !setTitle) {
7613 h->GetXaxis()->SetTitle(o->GetTitle());
7614 }
7616 h->Sumw2();
7617 if (v)
7618 h->GetXaxis()->SetName(dynamic_cast<TObject *>(v)->GetName()); // WARNING: messes up display of bin labels
7619 if (auto s = style(nullptr, false); s) {
7620 static_cast<TAttLine &>(*h) = *s;
7621 static_cast<TAttFill &>(*h) = *s;
7622 static_cast<TAttMarker &>(*h) = *s;
7623 }
7624 if (strlen(h->GetXaxis()->GetTitle()) == 0)
7625 h->GetXaxis()->SetTitle(vv->GetTitle());
7626 auto p = dynamic_cast<RooAbsPdf *>(rar);
7627
7628 // possible speed improvement:
7629 // if(auto spdf = dynamic_cast<RooRealSumPdf*>(p); spdf && spdf->canBeExtended()) {
7630 // p = nullptr; // faster to evaluate sumpdf as a function not a pdf
7631 // }
7632
7633 if (empty && !errors) {
7634 return h;
7635 }
7636
7637 // if (!empty) {
7638
7639 auto _coefs = coefs();
7640
7641 RooFitResult *fr = nullptr;
7642 if (errors) {
7643 // must ensure the fit result we obtain includes pars from coefficients if present
7644 if (_fr.get<RooFitResult>()) {
7645 fr = static_cast<RooFitResult *>(_fr.get<RooFitResult>()->Clone());
7646 } else {
7647 auto frn =
7648 (!_coefs.get() ? *this : xRooNode(RooProduct("tmp", "tmp", RooArgList(*rar, *_coefs.get<RooAbsReal>()))))
7649 .fitResult();
7650 if (strlen(_fr.GetName()))
7651 frn = frn.reduced(_fr.GetName());
7652 fr = dynamic_cast<RooFitResult *>(frn->Clone());
7653 }
7654 if (!GETDMP(fr, _finalPars)) {
7656 }
7657
7658 /// Oct2022: No longer doing this because want to allow fitResult to be used to get partial error
7659 // // need to add any floating parameters not included somewhere already in the fit result ...
7660 // RooArgList l;
7661 // for(auto& p : pars()) {
7662 // auto vv = p->get<RooRealVar>();
7663 // if (!vv) continue;
7664 // if (vv == dynamic_cast<RooRealVar*>(v)) continue;
7665 // if (vv->isConstant()) continue;
7666 // if (fr->floatParsFinal().find(vv->GetName())) continue;
7667 // if (fr->_constPars && fr->_constPars->find(vv->GetName())) continue;
7668 // l.add(*vv);
7669 // }
7670 //
7671 // if (!l.empty()) {
7672 // RooArgList l2; l2.addClone(fr->floatParsFinal());
7673 // l2.addClone(l);
7674 // fr->setFinalParList(l2);
7675 // }
7676
7677 TMatrixTSym<double> *prevCov = static_cast<TMatrixTSym<double> *>(GETDMP(fr, _VM));
7678
7679 if (!prevCov || size_t(fr->covarianceMatrix().GetNcols()) < fr->floatParsFinal().size()) {
7680 TMatrixDSym cov(fr->floatParsFinal().size());
7681 if (prevCov) {
7682 for (int i = 0; i < prevCov->GetNcols(); i++) {
7683 for (int j = 0; j < prevCov->GetNrows(); j++) {
7684 cov(i, j) = (*prevCov)(i, j);
7685 }
7686 }
7687 }
7688 int i = 0;
7689 for (auto &p2 : fr->floatParsFinal()) {
7690 if (!prevCov || i >= prevCov->GetNcols()) {
7691 cov(i, i) = pow(dynamic_cast<RooRealVar *>(p2)->getError(), 2);
7692 }
7693 i++;
7694 }
7695 int covQualBackup = fr->covQual();
7696 fr->setCovarianceMatrix(cov);
7697 fr->setCovQual(covQualBackup);
7698 }
7699
7700 if (v) {
7701 // need to remove v from result as we are plotting as function of v
7702 if (auto _p = fr->floatParsFinal().find(dynamic_cast<TObject *>(v)->GetName()); _p) {
7703 RooArgList _pars = fr->floatParsFinal();
7704 _pars.remove(*_p, true);
7705 auto _tmp = fr->reducedCovarianceMatrix(_pars);
7706 int covQualBackup = fr->covQual();
7707 fr->setCovarianceMatrix(_tmp);
7708 fr->setCovQual(covQualBackup);
7709 const_cast<RooArgList &>(fr->floatParsFinal())
7710 .remove(*_p, true); // NOTE: I think this might be a memory leak, should delete _p after removal
7711 }
7712 }
7713 // finally check at least one float has errors defined (might not be cause if in prefit state)
7714 bool hasErrors = false;
7715 for (auto pp : fr->floatParsFinal()) {
7716 if (dynamic_cast<RooRealVar *>(pp)->hasError()) {
7717 hasErrors = true;
7718 break;
7719 }
7720 }
7721 if (!hasErrors) {
7722 errors = false;
7723 delete fr;
7724 }
7725 }
7726
7727 RooArgSet normSet;
7728 if (v)
7729 normSet.add(*dynamic_cast<RooAbsArg *>(v));
7730
7731 if (binEnd == 0)
7732 binEnd = h->GetNbinsX();
7733
7734 bool needBinWidth = false;
7735 // may have MULTIPLE coefficients for the same pdf!
7736
7737 if (x && (p || _coefs.get() || rar->getAttribute("density"))) {
7738 // pdfs of samples embedded in a sumpdf (aka have a coef) will convert their density value to a content
7739 needBinWidth = true;
7740 }
7741
7742 if (auto spdf = dynamic_cast<RooRealSumPdf *>(p);
7743 spdf && spdf->canBeExtended() && !spdf->getFloor() && !_coefs.get()) {
7744 p = nullptr; // if pdf has no floor, will evaluate it as a function to allow it to be negative - evaluation should
7745 // also be faster (no integral)
7746 // exception is if RooRealSumPdf is embedded in a RooAddPdf (detected by presence of coefs) ... then it must be
7747 // evaluated as a pdf technically should check parent is a RooAddPdf, because if was inside a RooRealSumPdf then
7748 // would be evaluated as a function!
7749 }
7750
7751 // check if we need to do any projecting of other observables
7752 RooAbsReal *oldrar = nullptr;
7753 auto _obs = obs();
7754
7755 for (auto o : _obs) {
7756 if (auto rr = o->get<RooRealVar>(); rr && rr->hasRange("coordRange")) {
7757 rr->removeRange("coordRange");
7758 }
7759 }
7760 coords(); // loads current coordinates and populates coordRange, if any
7761
7762 if (auto a = dynamic_cast<RooAbsArg *>(v))
7763 _obs.get<RooArgList>()->remove(*a);
7764 if (!_obs.get<RooArgList>()->empty()) {
7765 oldrar = rar;
7766 normSet.add(*_obs.get<RooArgList>());
7767 // check if any obs are restricted range
7768 bool hasRange = false;
7769 for (auto o : normSet) {
7770 if (auto rr = dynamic_cast<RooRealVar *>(o); rr && rr->hasRange("coordRange")) {
7771 hasRange = true;
7772 break;
7773 }
7774 }
7775 if (p) {
7776 // need to handle special case of RooSimultaneous ... each pdf needs individually projecting over just its
7777 // dependent obs
7778 if (auto s = dynamic_cast<RooSimultaneous *>(p)) {
7779 auto newrar = new RooSimultaneous("projSim", "projSim", const_cast<RooAbsCategoryLValue &>(s->indexCat()));
7780 for (auto pdf : bins()) {
7781 // auto _pdf =
7782 // pdf->get<RooAbsPdf>()->createProjection(*pdf->get<RooAbsPdf>()->getObservables(*_obs.get<RooArgList>()));
7783 auto _pdf =
7784 new xRooProjectedPdf(TString::Format("%s_projection", pdf->GetName()), "", *pdf->get<RooAbsPdf>(),
7785 *pdf->get<RooAbsPdf>()->getObservables(*_obs.get<RooArgList>()));
7786 if (hasRange) {
7787 dynamic_cast<RooAbsPdf *>(_pdf)->setNormRange("coordRange");
7788 }
7789 newrar->addPdf(*_pdf, pdf->coords()["channelCat"]->get<RooCategory>()->getLabel());
7790 }
7791 rar = newrar;
7792 } else {
7793 rar = p->createProjection(
7794 *_obs.get<RooArgList>()); // TODO should use xRooProjectedPdf here too, because not fixed range and
7795 // extend behaviour of RooProjectedPdf in ROOT yet
7796 if (hasRange) {
7797 dynamic_cast<RooAbsPdf *>(rar)->setNormRange("coordRange");
7798 }
7799 }
7800 if (hasRange)
7801 p->setNormRange("coordRange"); // should get cleared when we sterilize
7802 } else {
7803 if (hasRange) {
7804 // commented out passing of normset so that getVal of non-pdf is always a 'raw' value (needed for raw eval
7805 // of RooRealSumPdf)
7806 rar = std::unique_ptr<RooAbsReal>{rar->createIntegral(
7807 *_obs.get<RooArgList>(),
7808 /*RooFit::NormSet(normSet),*/ RooFit::Range("coordRange"))}
7809 .release();
7810 } else {
7811 rar =
7812 std::unique_ptr<RooAbsReal>{rar->createIntegral(*_obs.get<RooArgList>() /*, RooFit::NormSet(normSet)*/)}
7813 .release();
7814 }
7815 }
7816 }
7817
7818 bool scaleExpected = (p && p->canBeExtended() && !_coefs.get());
7819 // Note about above: if pdf has coefficients then its embedded in a RooAddPdf that has coefs defined ...
7820 // in this case we should *not* scale by expected, since the coefs become the scaling instead
7821
7822 std::unique_ptr<RooArgSet> snap(normSet.snapshot());
7823 TStopwatch timeIt;
7824 std::vector<double> lapTimes;
7825 bool warned = false;
7826 if (binStart == -1 && binEnd == -1) {
7827 binEnd = 1;
7828 }
7829 for (int i = std::max(1, binStart); i <= std::min(h->GetNbinsX(), binEnd); i++) {
7830 timeIt.Start(true);
7831 if (x) {
7832 x->setVal(h->GetBinCenter(i));
7833 } else if (v) {
7834 v->setBin(i - 1);
7835 }
7836 if (x && !x->inRange("coordRange"))
7837 continue;
7838
7839 double r = 0;
7840 if (!empty) {
7841 r = /*(p && p->selfNormalized())*/ rar->getVal(p ? &normSet : nullptr);
7842#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
7843 if (std::isnan(r) && RooNaNPacker::isNaNWithPayload(r)) {
7845 }
7846#endif
7847 if (r && _coefs.get()) {
7848 r *= _coefs.get<RooAbsReal>()->getVal(normSet);
7849 }
7850 if (needBinWidth) {
7851 r *= h->GetBinWidth(i);
7852 }
7853 if (scaleExpected) {
7854 // std::cout << r << " exp = " << p->expectedEvents(normSet) << " for normRange " << (p->normRange() ?
7855 // p->normRange() : "null") << std::endl; p->Print();rar->Print();
7856 r *= (p->expectedEvents(normSet));
7857 } // do in here in case dependency on var
7858 }
7859 h->SetBinContent(i, r);
7860
7861 if (errors) {
7862 double res;
7863 if (p) {
7864 // std::cout << "computing error of :" << h->GetBinCenter(i) << std::endl;
7865 // //fr->floatParsFinal().Print(); fr->covarianceMatrix().Print();
7866 res = PdfWrapper((oldrar) ? *rar : *p, _coefs.get<RooAbsReal>(), !v, oldrar ? p : nullptr)
7867 .getSimplePropagatedError(*fr, normSet);
7868#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
7869 // improved normSet invalidity checking, so assuming no longer need this in 6.28 onwards
7870 p->_normSet = nullptr;
7871#endif
7872 } else {
7873 res = RooProduct("errorEval", "errorEval",
7874 RooArgList(*rar, !_coefs.get() ? RooFit::RooConst(1) : *_coefs.get<RooAbsReal>()))
7875 .getPropagatedError(
7876 *fr /*, normSet*/); // should be no need to pass a normSet to a non-pdf (but not verified this)
7877 // especially important not to pass in the case we are evaluated RooRealSumPdf as a function! otherwise
7878 // error will be wrong
7879 }
7880 if (needBinWidth) {
7881 res *= h->GetBinWidth(i);
7882 }
7883 h->SetBinError(i, res);
7884 }
7885 timeIt.Stop();
7886 lapTimes.push_back(timeIt.RealTime());
7887 double time_estimate =
7888 (lapTimes.size() > 1)
7889 ? (h->GetNbinsX() * (std::accumulate(lapTimes.begin() + 1, lapTimes.end(), 0.) / (lapTimes.size() - 1)))
7890 : 0.;
7891 if (!warned && (lapTimes.at(0) > 10 || (lapTimes.size() > 2 && time_estimate > 60.))) {
7892 TTimeStamp t2;
7893 t2.Add(time_estimate);
7894 Warning("BuildHistogram", "Building this histogram will take until %s", t2.AsString());
7895 if (errors) {
7896 // install interrupt handler
7897 runningNode = this;
7898 gOldHandlerr = signal(SIGINT, buildHistogramInterrupt);
7899 }
7900 warned = true;
7901 }
7902 if (fInterrupted) {
7903 if (errors) {
7904 Warning("BuildHistogram", "Skipping errors for remaining bins");
7905 errors = false;
7906 }
7907 fInterrupted = false;
7908 }
7909 }
7910 if (gOldHandlerr) {
7911 signal(SIGINT, gOldHandlerr);
7912 gOldHandlerr = nullptr;
7913 }
7914 normSet = *snap;
7915
7916 if (oldrar) {
7917 std::vector<RooAbsArg *> extra;
7918 if (auto s = dynamic_cast<RooSimultaneous *>(rar)) {
7919 // need to delete all the subpdfs we created too
7920 for (auto _pdf : s->servers()) {
7921 if (dynamic_cast<RooAbsPdf *>(_pdf))
7922 extra.push_back(_pdf);
7923 }
7924 }
7925 extra.push_back(rar);
7926 rar = oldrar;
7927 xRooNode(*rar).sterilize(); // need to clear the cache of the created integral - do this before deleting things!
7928 for (auto a : extra)
7929 delete a;
7930 } else {
7931 sterilize(); // needed to forget about the normSet that was passed to getVal()
7932 }
7933
7934 if (!p && !rar->getAttribute("density") && !needBinWidth) {
7935 h->GetYaxis()->SetTitle(rar->getStringAttribute("units"));
7936 } else if ((p && p->canBeExtended()) || (!p && needBinWidth)) {
7937 h->GetYaxis()->SetTitle("Events");
7938 } else {
7939 h->GetYaxis()->SetTitle("Probability Mass");
7940 }
7941 h->GetYaxis()->SetMaxDigits(3);
7942
7943 if (errors) {
7944 delete fr;
7945 }
7946 //}
7947 return h;
7948}
7949
7950double xRooNode::GetBinData(int bin, const char *dataName)
7951{
7952 auto node = datasets().find(dataName);
7953 if (!node)
7954 return std::numeric_limits<double>::quiet_NaN();
7955 return node->GetBinContent(bin);
7956}
7957
7958std::vector<double> xRooNode::GetBinContents(int binStart, int binEnd) const
7959{
7960 if (fBinNumber != -1) {
7961 if (binStart != binEnd || !fParent) {
7962 throw std::runtime_error(TString::Format("%s is a bin - only has one value", GetName()));
7963 }
7964 return fParent->GetBinContents(fBinNumber, fBinNumber);
7965 }
7966 std::vector<double> out;
7967 if (get<RooAbsData>()) {
7968 auto g = BuildGraph(
7969 nullptr,
7970 (binStart != -1 ||
7971 binEnd != -1) /*include points for zeros unless we are asking for a single point with start=end=-1*/);
7972 if (!g) {
7973 return out;
7974 }
7975 if (binStart == binEnd && binStart == -1) {
7976 // integral over all bins if getting bin content -1
7977 double integral(0);
7978 for (int i = 0; i < g->GetN(); i++)
7979 integral += g->GetPointY(i);
7980 out.push_back(integral);
7981 delete g;
7982 return out;
7983 }
7984 for (int i = binStart - 1; i < g->GetN() && (binEnd == 0 || i < binEnd); i++) {
7985 out.push_back(g->GetPointY(i));
7986 }
7987 delete g;
7988 return out;
7989 }
7990
7991 bool doIntegral = false;
7992 if (binStart == binEnd && binStart == -1) {
7993 binStart = -1;
7994 binEnd = -1;
7995 doIntegral = true;
7996 } // return integral if request bin -1
7997 auto h = BuildHistogram(nullptr, false, false, binStart, binEnd);
7998 if (!h) {
7999 throw std::runtime_error(TString::Format("%s has no content", GetName()));
8000 }
8001 if (binEnd == 0) {
8002 binEnd = h->GetNbinsX();
8003 }
8004 if (doIntegral) {
8005 double tot = 0;
8006 for (int i = 1; i <= h->GetNbinsX(); i++) {
8007 tot += h->GetBinContent(i);
8008 }
8009 out.push_back(tot);
8010 } else {
8011 for (int i = binStart; i <= binEnd; i++) {
8012 out.push_back(h->GetBinContent(i));
8013 }
8014 }
8015 delete h;
8016 return out;
8017}
8018
8020{
8021 if (auto a = get<RooAbsArg>(); a) {
8022 // go through servers looking for 'main' thing
8023 for (auto &l : a->servers()) {
8024 if (l->getAttribute("MAIN_MEASUREMENT") || l->InheritsFrom("RooRealSumPdf") || l->InheritsFrom("RooAddPdf")) {
8025 return xRooNode(*l, *this);
8026 }
8027 }
8028 // the main child of a RooProduct is one that has the same name (/alias) as the product (except if is a bin
8029 // factor)
8030 if (a->IsA() == RooProduct::Class() && fBinNumber == -1) {
8031 for (auto &l : factors()) {
8032 if (strcmp(l->GetName(), GetName()) == 0) {
8033 return *l;
8034 }
8035 }
8036 }
8037 }
8038 return xRooNode();
8039}
8040
8042{
8043 if (auto o = get(); o) {
8044 o->Inspect();
8045 } else {
8047 }
8048}
8049
8050bool TopRightPlaceBox(TPad *p, TObject *o, double w, double h, double &xl, double &yb)
8051{
8052#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
8053 // reinitialize collide grid because the filling depends on fUxmin and fUxmax (and ymin ymax too)
8054 // and these aren't filled on the first time we do the placement (they init to 0 and 1), but will be filled
8055 // subsequently
8056 for (int i = 0; i < p->fCGnx; i++) {
8057 for (int j = 0; j < p->fCGny; j++) {
8058 p->fCollideGrid[i + j * p->fCGnx] = true;
8059 }
8060 }
8061 p->FillCollideGrid(o);
8062 Int_t iw = (int)(p->fCGnx * w);
8063 Int_t ih = (int)(p->fCGny * h);
8064
8065 Int_t nxmax = p->fCGnx - iw - 1 - p->fCGnx * p->GetRightMargin();
8066 Int_t nymax = p->fCGny - ih - 1 - p->fCGny * p->GetTopMargin();
8067
8068 for (Int_t j = nymax; j >= 0; j--) {
8069 for (Int_t i = nxmax; i >= 0; i--) {
8070 if (p->Collide(i, j, iw, ih)) {
8071 continue;
8072 } else {
8073 xl = (double)(i) / (double)(p->fCGnx);
8074 yb = (double)(j) / (double)(p->fCGny);
8075 return true;
8076 }
8077 }
8078 }
8079 return false;
8080#else
8081 return p->PlaceBox(o, w, h, xl, yb, "trw");
8082#endif
8083}
8084
8085TPaveText *getPave(const char *name = "labels", bool create = true, bool doPaint = false)
8086{
8087 if (auto p = dynamic_cast<TPaveText *>(gPad->GetPrimitive(name)); p) {
8088 if (doPaint)
8089 gPad->PaintModified(); //-- slows down x11 so trying to avoid
8090 return p;
8091 }
8092 if (!create) {
8093 return nullptr;
8094 }
8095 auto l = new TPaveText(gPad->GetLeftMargin() + 0.02, 1. - gPad->GetTopMargin() - 0.08, 0.6,
8096 1. - gPad->GetTopMargin() - 0.08);
8097 l->SetBorderSize(0);
8098 if (l->GetTextSize() == 0)
8099 l->SetTextSize(gStyle->GetTitleYSize());
8100
8102 // l->SetMargin(0);
8103 l->SetFillStyle(0);
8104 l->SetName(name);
8105 l->Draw();
8106 l->ConvertNDCtoPad();
8107 return l;
8108}
8109
8110TLegend *getLegend(bool create = true, bool doPaint = false)
8111{
8112 if (auto p = dynamic_cast<TLegend *>(gPad->GetPrimitive("legend")); p) {
8113 double x;
8114 double y;
8115 double w = p->GetX2NDC() - p->GetX1NDC();
8116 double h = p->GetY2NDC() - p->GetY1NDC();
8117 if (doPaint)
8118 gPad->PaintModified(); //-- slows down x11 so trying to avoid
8119 if (TopRightPlaceBox(dynamic_cast<TPad *>(gPad), p, w, h, x, y)) {
8120 // squash inside the frame ..
8121 // std::cout << gPad->GetName() << ":" << x << " , " << y << " , " << w << " , " << h << std::endl;
8122 x = std::max(x, (gPad->GetLeftMargin() + 0.02));
8123 y = std::max(y, (gPad->GetBottomMargin() + 0.02));
8124 x = std::min(x, (1. - gPad->GetRightMargin() - 0.02) - w);
8125 y = std::min(y, (1. - gPad->GetTopMargin() - 0.02) - h);
8126 h = std::min(h, (1. - gPad->GetTopMargin() - 0.02) - y);
8127 w = std::min(w, (1. - gPad->GetRightMargin() - 0.02) - x);
8128 // std::cout << gPad->GetName() << ":" << x << " , " << y << " , " << h << " , " << w << std::endl;
8129 p->SetX1NDC(x);
8130 p->SetY1NDC(y);
8131 p->SetX2NDC(x + w);
8132 p->SetY2NDC(y + h);
8133 gPad->Modified();
8134 }
8135 return p;
8136 }
8137 // look for a parent pad called 'legend' and create it there if existing
8138 auto p = gPad;
8139 while ((p != p->GetMother()) && (p = p->GetMother())) {
8140 if (auto q = dynamic_cast<TVirtualPad *>(p->GetPrimitive("legend")); q) {
8141 q->Modified();
8142 p = q;
8143 break;
8144 }
8145 }
8146 auto tmpPad = gPad;
8147 TLegend *l = nullptr;
8148 if (p && strcmp(p->GetName(), "legend") == 0) {
8149 if (l = dynamic_cast<TLegend *>(p->GetPrimitive("legend")); l || !create)
8150 return l;
8151 p->cd();
8152 l = new TLegend(gPad->GetLeftMargin(), 1. - gPad->GetTopMargin(), 1. - gPad->GetRightMargin(),
8153 gPad->GetBottomMargin());
8154 } else {
8155 if (!create)
8156 return nullptr;
8157 l = new TLegend(0.6, 1. - gPad->GetTopMargin() - 0.08, 0.75, 1. - gPad->GetTopMargin() - 0.08);
8158 l->SetBorderSize(0);
8159 if (l->GetTextSize() == 0)
8160 l->SetTextSize(gStyle->GetTitleYSize());
8161 }
8163 // l->SetMargin(0);
8164 l->SetFillStyle(0);
8165 l->SetName("legend");
8166 l->Draw();
8167 l->ConvertNDCtoPad();
8168 tmpPad->cd();
8169 return l;
8170};
8171
8172void addLegendEntry(TObject *o, const char *title, const char *opt)
8173{
8174 auto l = getLegend();
8175 if (!l)
8176 return;
8177 // check for entry already existing with same title
8178 for (auto a : *l->GetListOfPrimitives()) {
8179 if (!strcmp(dynamic_cast<TLegendEntry *>(a)->GetLabel(), title))
8180 return;
8181 }
8182 if (l->GetListOfPrimitives()->GetEntries() > 20)
8183 return; // todo: create an 'other' entry?
8184
8185 l->AddEntry(o, title, opt);
8186 if (auto nObj = l->GetListOfPrimitives()->GetEntries(); nObj > 0) {
8187 // each entry takes up 0.05 ... maximum of N*(N+4) (where N is # cols) before next column
8188 int nn = l->GetNColumns();
8189 nn *= (nn + 4);
8190 if (nObj > 1 && (nObj % nn) == 1) {
8191 l->SetNColumns(l->GetNColumns() + 1);
8192 l->SetX1NDC(l->GetX2NDC() - 0.15 * l->GetNColumns());
8193 }
8194 l->SetY1NDC(l->GetY2NDC() - 0.05 * gPad->GetHNDC() * std::ceil((double(nObj) / l->GetNColumns())));
8195 }
8196
8197 getLegend(); // to mark modified
8198}
8199
8200// this exists to avoid calling update excessively because it slows down x11 ... but still
8201// need to call update twice if have a legend drawn in order to relocate it.
8203public:
8204 PadRefresher(TVirtualPad *p) : fPad(p) { nExisting++; }
8206 {
8207 if (fPad) {
8208 getLegend(false, true);
8209 fPad->GetCanvas()->Paint();
8210 fPad->GetCanvas()->Update();
8211#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 30, 00)
8212 fPad->GetCanvas()->ResetUpdated(); // stops previous canvas being replaced in a jupyter notebook
8213#endif
8214 fPad->cd();
8215 }
8216 nExisting--;
8217 }
8218 TVirtualPad *fPad = nullptr;
8219 static int nExisting;
8220};
8221
8223
8225{
8226 // in order to catch exceptions to prevent crash of GUI, do this:
8227 if (gROOT->FromPopUp()) {
8228 gROOT->SetFromPopUp(false);
8229 try {
8230 Draw(opt);
8231 } catch (const std::exception &e) {
8232 new TGMsgBox(
8233 gClient->GetRoot(),
8234 (gROOT->GetListOfBrowsers()->At(0))
8235 ? dynamic_cast<TGWindow *>(static_cast<TBrowser *>(gROOT->GetListOfBrowsers()->At(0))->GetBrowserImp())
8236 : gClient->GetRoot(),
8237 "Exception", e.what(),
8238 kMBIconExclamation); // deletes self on dismiss?
8239 }
8240 gROOT->SetFromPopUp(true);
8241 return;
8242 }
8243
8244 TString sOpt2(opt);
8245 sOpt2.ToLower();
8246 if (!get() && !IsFolder() && !sOpt2.Contains("x="))
8247 return;
8248
8249 if (auto ir = get<RooStats::HypoTestInverterResult>()) {
8250 xRooHypoSpace(ir).Draw(opt);
8252 return;
8253 } else if (get<RooStats::HypoTestResult>()) {
8254 if (gPad)
8255 gPad->Clear();
8256 xRooNLLVar::xRooHypoPoint(std::dynamic_pointer_cast<RooStats::HypoTestResult>(fComp)).Draw(opt);
8257 {
8258 PadRefresher p(gPad); // refreshes the pad
8259 }
8261 return;
8262 }
8263
8264 if (sOpt2 == "pcls" && get<RooRealVar>() && fParent && fParent->get<RooAbsPdf>()) {
8265 // use the first selected dataset
8266 auto _dsets = fParent->datasets();
8267 // bool _drawn=false;
8268 TString dsetName = "";
8269 for (auto &d : _dsets) {
8270 if (d->get()->TestBit(1 << 20)) {
8271 dsetName = d->get()->GetName();
8272 break;
8273 }
8274 }
8275 auto hs = fParent->nll(dsetName.Data()).hypoSpace(get<RooRealVar>()->GetName());
8276 hs.limits("cls visualize");
8277 hs.SetName(TUUID().AsString());
8278 if (ws()) {
8279 ws()->import(*hs.result());
8280 }
8281 return;
8282 }
8283
8284 if (auxFunctions.empty()) {
8285 // add the defaults: Ratio and Signif
8287 "Ratio",
8288 [](double a, double b, double) {
8289 if (a == 0)
8290 return 0.;
8291 if (b == 0 && a == 0)
8292 return 1.;
8293 return a / b;
8294 },
8295 true);
8297 "Signif",
8298 [](double n, double b, double sigma) {
8299 double t0 = 0;
8300 if (sigma <= 0.) {
8301 // use simplified expression ...
8302 t0 = 2. * (((n == 0) ? 0 : n * log(n / b)) - (n - b));
8303 } else {
8304 double sigma2 = sigma * sigma;
8305 double b_hathat = 0.5 * (b - sigma2 + sqrt(pow(b - sigma2, 2) + 4 * n * sigma2));
8306 // double s_hat = n - m;
8307 // double b_hat = m;
8308 t0 = 2. * (((n == 0) ? 0 : n * log(n / b_hathat)) + b_hathat - n + pow(b - b_hathat, 2) / (2. * sigma2));
8309 }
8310 if (t0 < 0)
8311 return 0.; // can happen from numerical precision
8312 return (n >= b) ? sqrt(t0) : -sqrt(t0);
8313 },
8314 false);
8315 }
8316
8317 TString sOpt(opt);
8318
8319 RooAbsLValue *v = nullptr;
8320 std::vector<double> xPoints;
8321 if (sOpt2.Contains("x=")) {
8322 // specifying a particular var to scan over ...
8323 int _idx = sOpt2.Index("x=");
8324 int _eidx = sOpt2.Index(';', _idx);
8325 TString varPart = sOpt(_idx + 2, (_eidx < 0 ? sOpt2.Length() : _eidx) - (_idx + 2));
8326 TString varName = varPart;
8327 // if varName is of form str(num,num,num) then can infer scan points
8328 if (auto _idx2 = varPart.Index("("); _idx2 > 0) {
8329 varName = varPart(0, _idx2);
8330 TStringToken pattern(TString(varPart(_idx2 + 1, varPart.Length() - _idx2 - 2)), ",");
8331 double min(0);
8332 double max(0);
8333 int nBins = 0;
8334 int ii = 0;
8335 while (pattern.NextToken()) {
8336 TString s = pattern;
8337 if (ii == 0) {
8338 nBins = s.Atoi();
8339 } else if (ii == 1) {
8340 min = s.Atof();
8341 } else if (ii == 2) {
8342 max = s.Atof();
8343 }
8344 ii++;
8345 }
8346 if (nBins > 100)
8347 nBins = 100; // limit scanning to 100 points
8348 if (nBins > 1) {
8349 for (double x = min; x <= max; x += (max - min) / (nBins - 1)) {
8350 xPoints.push_back(x);
8351 }
8352 } else if (nBins == 1)
8353 xPoints.push_back((min + max) / 2.);
8354 }
8355 v = getObject<RooAbsRealLValue>(varName.Data()).get();
8356 if (!v) {
8357 Error("Draw", "Could not find variable %s", varName.Data());
8358 return; // don't throw because if happens in browser will cause ROOT to exit
8359 }
8360 if (xPoints.empty() && !obs().find(varName.Data())) { // will draw obs as regular (e.g. hist)
8361 double tmp = static_cast<RooAbsRealLValue *>(v)->getVal();
8362 for (int i = 0; i < v->numBins(GetName()); i++) {
8363 v->setBin(i, GetName());
8364 xPoints.push_back(static_cast<RooAbsRealLValue *>(v)->getVal());
8365 }
8366 static_cast<RooAbsRealLValue *>(v)->setVal(tmp);
8367 }
8368 sOpt2 = TString(sOpt2(0, _idx)) + sOpt2(_idx + 2 + varPart.Length() + 1, sOpt2.Length());
8369 sOpt = TString(sOpt(0, _idx)) + sOpt(_idx + 2 + varPart.Length() + 1, sOpt.Length());
8370 }
8371 TString forceNames = "";
8372 if (sOpt2.Contains("force")) {
8373 // force plots show how much NLL changes wrt to a change of variables
8374 if (get<RooRealVar>() && fParent && fParent->get<RooAbsPdf>()) {
8375 // assume want force of this parameter from the parent pdf
8376 TString ff = sOpt(sOpt2.Index("force"), sOpt2.Index("force") + 5);
8377 sOpt.ReplaceAll(ff, TString::Format("force%s", get()->GetName()));
8378 fParent->Draw(sOpt);
8379 return;
8380 } else if (get<RooAbsPdf>()) {
8381 // extract the parameter(s) to calculate force for
8382 forceNames = sOpt(sOpt2.Index("force") + 5, sOpt2.Length());
8383 sOpt = sOpt(0, sOpt2.Index("force"));
8384 sOpt2 = sOpt2(0, sOpt2.Index("force"));
8385 } else {
8386 Error("Draw", "Can only compute forces with PDFs");
8387 return; // don't throw because will cause browser to exit if done from there
8388 }
8389 }
8390 bool hasOverlay = sOpt2.Contains("overlay");
8391 TString overlayName = "";
8392 if (hasOverlay) {
8393 // whatever follows overlay is the variation name
8394 overlayName = sOpt(sOpt2.Index("overlay") + 7, sOpt2.Length());
8395 sOpt = sOpt(0, sOpt2.Index("overlay"));
8396 sOpt2 = sOpt2(0, sOpt2.Index("overlay"));
8397 }
8398 if (sOpt2.Contains("ratio") && !sOpt2.Contains("auxratio"))
8399 sOpt += "auxRatio";
8400 if (sOpt2.Contains("significance") && !sOpt2.Contains("auxsignif"))
8401 sOpt += "auxSignif";
8402
8403 std::string auxPlotTitle;
8404 for (auto &[k, _] : auxFunctions) {
8405 if (sOpt.Contains(TString::Format("aux%s", k.c_str()))) {
8406 auxPlotTitle = k;
8407 }
8408 sOpt.ReplaceAll(TString::Format("aux%s", k.c_str()), "");
8409 }
8410
8411 sOpt.ToLower();
8412 sOpt.ReplaceAll("ratio", "");
8413 sOpt.ReplaceAll("significance", ""); // remove old option if still given
8414 bool nostack = sOpt.Contains("nostack");
8415 sOpt.ReplaceAll("nostack", "");
8416 bool hasSame = sOpt.Contains("same");
8417 sOpt.ReplaceAll("same", "");
8418 bool hasGoff = sOpt.Contains("goff");
8419 sOpt.ReplaceAll("goff", "");
8420 bool hasFR = sOpt.Contains("pull") && !get<RooFitResult>();
8421 sOpt.ReplaceAll("pull", "");
8422 bool hasText = sOpt.Contains("text");
8423 bool hasErrorOpt = sOpt.Contains("e");
8424 sOpt.ReplaceAll("e", "");
8425 if (hasText)
8426 sOpt.ReplaceAll("txt", "text");
8427 if (auxPlotTitle == "Signif")
8428 hasErrorOpt = true; // must calculate error to calculate significance
8429 if (hasOverlay)
8430 hasSame = true; // when overlaying must be putting on same
8431
8432 TVirtualPad *pad = gPad;
8433
8434 TH1 *hAxis = nullptr;
8435
8436 auto clearPad = []() {
8437 gPad->Clear();
8438 if (gPad->GetNumber() == 0) {
8439 gPad->SetBottomMargin(gStyle->GetPadBottomMargin());
8440 gPad->SetTopMargin(gStyle->GetPadTopMargin());
8441 gPad->SetLeftMargin(gStyle->GetPadLeftMargin());
8442 gPad->SetRightMargin(gStyle->GetPadRightMargin());
8443 }
8444 // if (gPad == gPad->GetCanvas()) {
8445 // gPad->GetCanvas()->SetCanvasSize( gPad->GetCanvas()->GetWindowWidth() - 4,
8446 // gPad->GetCanvas()->GetWindowHeight() - 28 );
8447 // }
8448 };
8449
8450 if (!hasSame || !pad) {
8451 if (!pad) {
8453 pad = gPad;
8454 }
8455
8456 } else {
8457 // get the histogram representing the axes
8458 hAxis = dynamic_cast<TH1 *>(pad->GetPrimitive("axis"));
8459 if (!hAxis) {
8460 for (auto o : *pad->GetListOfPrimitives()) {
8461 if (hAxis = dynamic_cast<TH1 *>(o); hAxis)
8462 break;
8463 }
8464 }
8465 if (hAxis && !v) {
8466 v = getObject<RooAbsLValue>(hAxis->GetXaxis()->GetName()).get();
8467 }
8468 }
8469
8470 if (!hasSame) {
8471 gPad->SetName(GetName());
8472 gPad->SetTitle(GetTitle());
8473 }
8474
8475 PadRefresher padRefresh(((!hasSame || hasOverlay || PadRefresher::nExisting == 0) && !hasGoff) ? gPad : nullptr);
8476
8477 auto adjustYRange = [&](double min, double max, TH1 *hh = nullptr, bool symmetrize = false) {
8478 if (!hh)
8479 hh = hAxis;
8480 // give max and min a buffer ...
8481 max += gStyle->GetHistTopMargin() * (max - min);
8482 if (min > 0)
8483 min = std::max(min * 0.9, min - gStyle->GetHistTopMargin() * (max - min));
8484 if (hh) {
8485 double ymin = hh->GetMinimum();
8486 double ymax = hh->GetMaximum();
8487 if (hh->GetMaximumStored() == -1111)
8488 ymax += gStyle->GetHistTopMargin() * (ymax - ymin);
8489 if (hh->GetMinimumStored() == -1111) {
8490 if (gStyle->GetHistMinimumZero() && ymax >= 0) {
8491 ymin = 0;
8492 } else if (ymin < 0) {
8493 ymin -= gStyle->GetHistTopMargin() * (ymax - ymin);
8494 } else {
8495 ymin = std::max(ymin * 0.9, ymin - gStyle->GetHistTopMargin() * (ymax - ymin));
8496 }
8497 // see TGLPlotPainter to complete the mimic, but we leave off here truncating @ 0 if ymax>0
8498 }
8499 // make ymax at least 3x bigger than biggest error if has error
8500 if (hh->GetSumw2()) {
8501 double smallestErrDown3 = -std::numeric_limits<double>::infinity();
8502 double smallestErrUp3 = std::numeric_limits<double>::infinity();
8503 for (int i = 1; i <= hh->GetNbinsX(); i++) {
8504 smallestErrDown3 = std::max(smallestErrDown3, hh->GetBinContent(i) - 3 * hh->GetBinError(i));
8505 smallestErrUp3 = std::min(smallestErrUp3, hh->GetBinContent(i) + 3 * hh->GetBinError(i));
8506 }
8507 max = std::max(max, smallestErrUp3);
8508 min = std::min(min, smallestErrDown3);
8509 }
8510 bool change = false;
8511 if (min < ymin) {
8512 ymin = min;
8513 change = true;
8514 }
8515 if (max > ymax) {
8516 ymax = max;
8517 change = true;
8518 }
8519 if (change) {
8520 // note: unfortunately when user 'unzooms' y axis it resets stored minimum to -1111, so lose range
8521 if (symmetrize) {
8522 double down = hh->GetBinContent(1) - ymin;
8523 double up = ymax - hh->GetBinContent(1);
8524 if (down > up) {
8525 ymax = hh->GetBinContent(1) + down;
8526 } else {
8527 ymin = hh->GetBinContent(1) - up;
8528 }
8529 }
8530 if (hh == hAxis && pad && !pad->GetLogy() && ymin > 0 && (log10(ymax) - log10(max)) >= 3) {
8531 // auto-log the pad
8532 pad->SetLogy();
8533 }
8534 if (hh == hAxis && pad && ymin == 0 && pad->GetLogy()) {
8535 ymin = 1e-2;
8536 }
8537 if (ymin == 0 && ymax > 10)
8538 ymin = 0.1; // adjust min so if user activates log scale it isn't bad
8539 hh->SetMinimum(ymin);
8540 hh->SetMaximum(ymax);
8541 hh->GetYaxis()->Set(1, ymin, ymax);
8542 hh->SetAxisRange(ymin, ymax, "Y");
8543 }
8544 }
8545 };
8546
8547 auto graphMinMax = [](TGraphAsymmErrors *gr) {
8548 double ymax = -std::numeric_limits<double>::infinity();
8549 double ymin = std::numeric_limits<double>::infinity();
8550 for (int i = 0; i < gr->GetN(); i++) {
8551 ymax = std::max(ymax, gr->GetPointY(i) + gr->GetErrorYhigh(i));
8552 ymin = std::min(ymin, gr->GetPointY(i) - gr->GetErrorYlow(i));
8553 }
8554 return std::make_pair(ymin, ymax);
8555 };
8556
8557 if (!xPoints.empty()) {
8558 // create a graph using GetContent
8560 out->SetName(GetName());
8561 out->SetTitle(GetTitle());
8562 out->SetFillColor(out->GetLineColor());
8563 out->SetMarkerStyle(0);
8564 out->SetFillStyle(hasErrorOpt ? 3005 : 0);
8565 double tmp = static_cast<RooAbsRealLValue *>(v)->getVal();
8566 for (auto &x : xPoints) {
8567 static_cast<RooAbsRealLValue *>(v)->setVal(x);
8568 out->AddPoint(x, GetContent());
8569 if (hasErrorOpt) {
8570 out->SetPointEYlow(out->GetN() - 1, GetError());
8571 out->SetPointEYhigh(out->GetN() - 1, out->GetErrorYlow(out->GetN() - 1)); // symmetric error for now
8572 }
8573 }
8574 static_cast<RooAbsRealLValue *>(v)->setVal(tmp);
8575 out->GetHistogram()->GetXaxis()->SetTitle(static_cast<RooAbsRealLValue *>(v)->GetTitle());
8576 out->SetBit(kCanDelete);
8577 out->Draw(TString(hasSame ? "L" : "AL") + (hasErrorOpt ? "3" : ""));
8578 return;
8579 }
8580
8581 if (hasFR) {
8582 // drawing the fitresult as a pull plot on a subpad, and rest of the draw elsewhere
8583 clearPad();
8584 pad->Divide(1, 2, 1e-9, 1e-9); //,0,0);
8585 pad->GetPad(1)->SetPad(0, 0.2, 1, 1);
8586 pad->GetPad(2)->SetPad(0, 0, 1, 0.2);
8587 TString optNoFR(opt);
8588 optNoFR.ReplaceAll("pull", "");
8589 pad->cd(1);
8590 Draw(optNoFR);
8591 pad->cd(2);
8592 auto _fr = fitResult();
8593 _fr.Draw();
8594 // switch into subpad
8595 gPad->cd(1);
8596 gPad->SetFillColor(kGray);
8597 gPad->GetFrame()->SetFillColor(kWhite);
8598 gPad->GetFrame()->SetFillStyle(1001);
8599 gPad->SetTopMargin(0);
8600 gPad->SetBottomMargin(0);
8601 gPad->SetName("pull");
8602 // split the pull graph into individual points -- for benefit of GUI status bar
8603 auto pullGraph = dynamic_cast<TGraphAsymmErrors *>(gPad->GetPrimitive("pulls"));
8604 if (!pullGraph) {
8605 Error("Draw", "Couldn't find pull graph");
8606 return;
8607 }
8608 pullGraph->SetName("nominal");
8609 TMultiGraph *mg = new TMultiGraph;
8610 mg->SetName("editables");
8611
8612 auto scaleHist = static_cast<TH1 *>(pullGraph->FindObject("scales"));
8613 if (!scaleHist)
8614 throw std::runtime_error("Could not find scales in fit result");
8615
8616 for (auto i = 0; i < pullGraph->GetN(); i++) {
8617 auto g = new TGraphAsymmErrors;
8618 g->SetName(scaleHist->GetXaxis()->GetBinLabel(i + 1));
8619 auto _p = dynamic_cast<RooRealVar *>(_fr.get<RooFitResult>()->floatParsFinal().find(g->GetName()));
8620 if (!_p) {
8621 Warning("Draw", "Found a non-var in the floatParsFinal list: %s - this shouldn't happen", g->GetName());
8622 continue;
8623 }
8624 g->SetTitle(TString::Format(
8625 "%s=%g +/- %s [%g,%g]", strlen(_p->GetTitle()) ? _p->GetTitle() : _p->GetName(), _p->getVal(),
8626 _p->hasAsymError() ? TString::Format("(%g,%g)", _p->getAsymErrorHi(), _p->getAsymErrorLo()).Data()
8627 : TString::Format("%g", _p->getError()).Data(),
8628 scaleHist->GetBinContent(i + 1), scaleHist->GetBinError(i + 1)));
8629 g->SetPoint(0, pullGraph->GetPointX(i), pullGraph->GetPointY(i));
8630 g->SetPointEYhigh(0, pullGraph->GetErrorYhigh(i));
8631 g->SetPointEYlow(0, pullGraph->GetErrorYlow(i));
8632 g->SetEditable(true);
8633 g->SetHighlight(true);
8634 g->SetMarkerStyle(20);
8635 g->SetMarkerSize(0.5);
8636 mg->Add(g);
8637 }
8638 // gPad->GetListOfPrimitives()->Remove(pullGraph); delete pullGraph;
8639 mg->Draw("z0p");
8640 mg->SetBit(kCanDelete);
8641 auto _thisClone = new xRooNode("node", fComp, fParent);
8642 _thisClone->SetBit(kCanDelete);
8643 _thisClone->AppendPad();
8644
8645 // ensure statusbar visible for interactive plot
8646 // turned this off for now ... as not needed if doing through browser, status bar already there
8647 // if (gPad->GetCanvas() && !gPad->GetCanvas()->TestBit(TCanvas::kShowEventStatus)) {
8648 // gPad->GetCanvas()->ToggleEventStatus();
8649 // }
8650 gPad->AddExec("interactivePull", TString::Format("%s::Interactive_Pull()", ClassName()));
8651
8652 pad->cd();
8653 return;
8654 }
8655
8656 if (auto _simPdf = get<RooSimultaneous>(); _simPdf) {
8657 int _size = 0;
8658 auto _channels = bins();
8659 for (auto &_v : _channels) {
8660 if (!_v->IsHidden())
8661 _size++;
8662 }
8663 if (!hasSame) {
8664 clearPad();
8665 pad->SetBorderSize(0);
8666 // if (pad->GetCanvas() == pad) {
8667 // if(_size>4) {
8668 // int n = _size;
8669 // Int_t w = 1, h = 1;
8670 // if (pad->GetCanvas()->GetWindowWidth() > pad->GetCanvas()->GetWindowHeight()) {
8671 // w = TMath::Ceil(TMath::Sqrt(n));
8672 // h = TMath::Floor(TMath::Sqrt(n));
8673 // if (w*h < n) w++;
8674 // } else {
8675 // h = TMath::Ceil(TMath::Sqrt(n));
8676 // w = TMath::Floor(TMath::Sqrt(n));
8677 // if (w*h < n) h++;
8678 // }
8679 // // adjust the window size to display only 4 in the window, with scroll bars
8680 // pad->GetCanvas()->SetCanvasSize( w*((pad->GetCanvas()->GetWindowWidth()-4)/2.) -16
8681 // ,h*((pad->GetCanvas()->GetWindowHeight()-28)/2.) - 16 );
8682 // } else {
8683 // //pad->GetCanvas()->Set(
8684 // w*(pad->GetCanvas()->GetWindowWidth()/2.),h*(pad->GetCanvas()->GetWindowHeight()/2.)) )
8685 // }
8686 // }
8687 dynamic_cast<TPad *>(pad)->DivideSquare(_size, 1e-9, 1e-9);
8688 }
8689 int i = 0;
8690 auto &chanVar = const_cast<RooAbsCategoryLValue &>(_simPdf->indexCat());
8691 // auto _idx = chanVar.getIndex();
8692 auto _range = GetRange();
8693 std::vector<TString> chanPatterns;
8694 if (_range && strlen(_range)) {
8695 TStringToken pattern(_range, ",");
8696 while (pattern.NextToken()) {
8697 chanPatterns.emplace_back(pattern);
8698 }
8699 }
8700 for (auto &_v : _channels) {
8701 if (_v->IsHidden())
8702 continue;
8703 TString s(_v->GetName());
8704 pad->cd(++i);
8705 gPad->SetName(s);
8706 TString cName = s(s.Index('=') + 1, s.Length());
8707 chanVar.setLabel(cName);
8708 bool inRange = chanPatterns.empty();
8709 for (auto &p : chanPatterns) {
8710 if (chanVar.inRange(p)) {
8711 inRange = true;
8712 break;
8713 }
8714 }
8715 if (!inRange || !_v->get<RooAbsReal>()->isSelectedComp())
8716 gPad->SetFillColor(kGray);
8717 if (!hasSame && _size > 1 && (gStyle->GetTitleFont("Y") % 10) == 3)
8718 gPad->SetLeftMargin(std::min(gPad->GetLeftMargin() * (1. / gPad->GetWNDC()), 0.3));
8719 _v->Draw(opt);
8721 }
8722 pad->cd(0);
8723 gPad->Modified();
8724 // gPad->Update();
8725 return;
8726 }
8727
8728 if (!get() || get<RooArgList>()) {
8729 // is a group draw all the submembers
8730 browse();
8731 int _size = 0;
8732 // int _size = _channels.size(); // size(); if (find("!.vars")) _size--;
8733 for (auto &_v : *this) {
8734 if (_v->IsHidden())
8735 continue;
8736 if (strcmp(GetName(), ".vars") == 0) {
8737 // auto hide obs and "1" and const var
8738 if (_v->get<RooAbsArg>()->getAttribute("obs"))
8739 continue;
8740 if (strcmp(_v->get()->GetName(), "1") == 0 || strcmp(_v->get()->GetName(), "ONE") == 0 ||
8741 TString(_v->get()->GetName()).BeginsWith("binWidth_"))
8742 continue;
8743 if (_v->get()->InheritsFrom("RooConstVar"))
8744 continue;
8745 }
8746 TString s(_v->GetName());
8747 if (s.BeginsWith(".") || s.BeginsWith("!"))
8748 continue;
8749 _size++;
8750 }
8751 if (!hasSame) {
8752 clearPad();
8753 pad->SetBorderSize(0);
8754 dynamic_cast<TPad *>(pad)->DivideSquare(_size, 1e-9, 1e-9);
8755 }
8756 int i = 0;
8757 for (auto &_v : *this) {
8758 if (_v->IsHidden())
8759 continue;
8760 if (strcmp(GetName(), ".vars") == 0) {
8761 // auto hide obs and "1" and const var
8762 if (_v->get<RooAbsArg>()->getAttribute("obs"))
8763 continue;
8764 if (strcmp(_v->get()->GetName(), "1") == 0 || strcmp(_v->get()->GetName(), "ONE") == 0 ||
8765 TString(_v->get()->GetName()).BeginsWith("binWidth_"))
8766 continue;
8767 if (_v->get()->InheritsFrom("RooConstVar"))
8768 continue;
8769 }
8770 TString s(_v->GetName());
8771 if (s.BeginsWith(".") || s.BeginsWith("!"))
8772 continue;
8773 pad->cd(++i);
8774 gPad->SetName(s);
8775 if (!hasSame && _size > 1 && (gStyle->GetTitleFont("Y") % 10) == 3)
8776 gPad->SetLeftMargin(std::min(gPad->GetLeftMargin() * (1. / gPad->GetWNDC()), 0.3));
8777 _v->Draw(opt);
8778 // pad->Modified();//pad->Update();
8780 }
8781 pad->cd(0);
8782 gPad->Modified();
8783 // gPad->Update();
8784 return;
8785 }
8786
8787 if (get()->InheritsFrom("RooProdPdf")) {
8788 // draw the main pdf ...
8789 mainChild().Draw(opt);
8790 gPad->SetName(GetName());
8791 return;
8792 }
8793
8794 if (auto fr = get<RooFitResult>(); fr) {
8795 if (sOpt.Contains("corr")) {
8796 // do correlation matrix
8797
8798 auto hist = fr->correlationHist(fr->GetName());
8799 hist->SetTitle(fr->GetTitle());
8800 hist->SetBit(kCanDelete);
8801 hist->Scale(100);
8802 hist->SetStats(false);
8803 hist->SetDirectory(nullptr);
8805 gStyle->SetPaintTextFormat(".1f");
8806 hist->GetXaxis()->SetTickSize(0);
8807 hist->GetYaxis()->SetTickSize(0);
8808 hist->SetMinimum(-100);
8809 hist->Draw(sOpt);
8811 gPad->SetGrid(1, 1);
8812 gPad->SetLogy(0);
8813 gPad->SetLogx(0);
8814 return;
8815 }
8816
8817 if (sOpt.Contains("brakdown")) { // e will have been removed above
8818
8819 // breakdown is quadrature difference between total error and conditional error
8820 // group by 'group' attribute
8821
8822 std::string poiName;
8823 if (sOpt.Contains("brakdown:")) {
8824 TString sOpt3(opt);
8825 poiName = sOpt3(sOpt3.Index("breakdown:") + 10, sOpt3.Length());
8826 } else {
8827 std::unique_ptr<RooAbsCollection> _poi(fr->floatParsFinal().selectByAttrib("poi", true));
8828 if (_poi->empty()) {
8829 throw std::runtime_error("No floating poi in the fit");
8830 } else if (_poi->size() != 1) {
8831 throw std::runtime_error("Multiple poi in the fit");
8832 }
8833 poiName = _poi->first()->GetName();
8834 }
8835 RooRealVar *poi = dynamic_cast<RooRealVar *>(fr->floatParsFinal().find(poiName.c_str()));
8836 if (!poi) {
8837 throw std::runtime_error(TString::Format("Cannot find parameter %s", poiName.c_str()));
8838 }
8839 std::set<std::string> groups;
8840 for (auto p : fr->floatParsFinal()) {
8841 if (p == poi) {
8842 continue;
8843 } else if (p->getStringAttribute("group")) {
8844 groups.insert(p->getStringAttribute("group"));
8845 } else {
8846 groups.insert(p->GetTitle());
8847 }
8848 }
8849
8850 auto roundedVal = xRooFit::matchPrecision(std::pair(poi->getVal(), poi->getError()));
8851
8852 TPie *pie = new TPie(TString::Format("breakdown:%s", poi->GetName()),
8853 TString::Format("%s: %g #pm %g", poi->GetTitle(), roundedVal.first, roundedVal.second),
8854 groups.size() + 1);
8855
8856 // for display of errors will go to one extra dp ...
8857 roundedVal.second *= .1;
8858
8859 // do breakdown by removing parameters in blocks according to groups and seeing impact on variance
8860 // this will give the correct sum but will be order-dependent if there are correlations between
8861 // groups. therefore we will stick with group-by-group
8862 // RooArgList pars(fr->floatParsFinal()); // pars to not condition on
8863 // double variance = pow(dynamic_cast<RooRealVar*>(poi)->getError(),2);
8864 int i = 0;
8865 for (auto group : groups) {
8866 RooArgList pars(fr->floatParsFinal()); // pars to not condition on
8867 double variance = pow(dynamic_cast<RooRealVar *>(poi)->getError(), 2);
8868 for (auto p : fr->floatParsFinal()) {
8869 if (p == poi) {
8870 continue;
8871 } else if ((p->getStringAttribute("group") && group == p->getStringAttribute("group")) ||
8872 (!p->getStringAttribute("group") && group == p->GetTitle())) {
8873 // conditioning on this parameter ... remove from pars list
8874 pars.remove(*p);
8875 }
8876 }
8877 int idx = pars.index(poiName.c_str());
8878 double reducedVar = fr->conditionalCovarianceMatrix(pars)(idx, idx);
8879 if (reducedVar > variance) {
8880 Warning("Draw", "breakdown group %s variance bigger than preceding?", group.c_str());
8881 pie->SetEntryVal(i, 0);
8882 pie->SetEntryLabel(i, TString::Format("%s: NaN", group.c_str()));
8883 } else {
8884 pie->SetEntryVal(i, variance - reducedVar);
8886 std::pair(sqrt(variance - reducedVar), roundedVal.second)); // r.first will be the rounded error
8887 if (r.first > 0) {
8888 pie->SetEntryLabel(i, TString::Format("%s: %g", group.c_str(), r.first));
8889 } else {
8890 pie->SetEntryLabel(i, group.c_str()); // suppress labels for negligible errors.
8891 }
8892 }
8894 // variance = reducedVar;
8895 i++;
8896 }
8897 // remaining variance is statistical=
8898 double variance = fr->conditionalCovarianceMatrix(*poi)(0, 0);
8899 auto r =
8900 xRooFit::matchPrecision(std::pair(sqrt(variance), roundedVal.second)); // r.first will be the rounded error
8901 pie->SetEntryVal(i, variance);
8902 pie->SetEntryLabel(i, TString::Format("stat: %g", r.first));
8904 pie->SetBit(kCanDelete);
8905 pie->SetRadius(0.17);
8907 pie->Draw("NOL");
8908 return;
8909 }
8910
8911 // plot pull or impact
8913 out->SetName(TString::Format("%s_pull", fr->GetName()));
8914 out->SetTitle("Fit Result Pulls");
8915 std::vector<TString> graphLabels;
8917 ugraph->SetName(TString::Format("%s_pull_unconstrained", fr->GetName()));
8918 ugraph->SetTitle("Fit Result Pulls");
8919 std::vector<TString> ugraphLabels;
8920 std::map<std::string, double> scale;
8921 std::map<std::string, double> offset;
8922 for (auto &p : fr->floatParsFinal()) {
8923 auto _v = dynamic_cast<RooRealVar *>(p);
8924 if (!_v)
8925 continue;
8926
8927 if (std::isnan(_v->getErrorHi()) || std::isnan(_v->getErrorLo())) {
8928 Warning("Draw", "%s error is invalid", _v->GetName());
8929 }
8930
8931 // need to get constraint mean and error parameters ....
8932 // look for normal gaussian and poisson cases
8933 double prefitError = 0;
8934 double prefitVal = 0;
8935 double customScale = 0;
8936 if (auto ip =
8937 dynamic_cast<RooRealVar *>(fr->floatParsInit().find(p->GetName()))) { // handles if no prefit available
8938 prefitError = ip->getError();
8939 prefitVal = ip->getVal();
8940 };
8941
8942 std::shared_ptr<xRooNode> pConstr;
8943 if (fParent && fParent->getObject<RooRealVar>(p->GetName())) {
8944 auto _vv = fParent->getObject<RooRealVar>(p->GetName());
8945 if (_vv->hasRange("pullScale")) {
8946 customScale = (_vv->getMax("pullScale") - _vv->getMin("pullScale")) / 2.;
8947 }
8948 auto _constr = xRooNode(_vv, *this).constraints();
8949 for (auto &c : _constr) {
8950 if (c->get<RooPoisson>() || c->get<RooGaussian>()) {
8951 // require parameter to be a direct server of the constraint pdf to count if its a gaussian
8952 bool isServer = true;
8953 if (c->get<RooGaussian>()) {
8954 isServer = false;
8955 for (auto s : c->get<RooAbsArg>()->servers()) {
8956 if (strcmp(s->GetName(), p->GetName()) == 0) {
8957 isServer = true;
8958 break;
8959 }
8960 }
8961 }
8962 if (isServer) {
8963 pConstr = c;
8964 break;
8965 }
8966 }
8967 }
8968 }
8969 if (pConstr) {
8970
8971 // there will be 3 deps, one will be this par, the other two are the mean and error (or error^2 in case of
8972 // poisson
8973
8974 // std::cout << p->GetName() << " extracted " << prefitVal << " " << prefitError << " from ";
8975 // pConstr->deps().Print();
8976 pConstr->browse();
8977 if (pConstr->get<RooPoisson>() && pConstr->find(".x")) {
8978 std::string xName = pConstr->find(".x")->get()->GetName();
8979 prefitVal = pConstr->find(".x")->get<RooAbsReal>()->getVal();
8980 for (auto &_d : pConstr->vars()) {
8981 if (strcmp(p->GetName(), _d->get()->GetName()) == 0)
8982 continue;
8983 if (xName == _d->get()->GetName())
8984 continue;
8985 if (_d->get<RooAbsReal>()->getVal())
8986 prefitError = _d->get<RooAbsReal>()->getVal();
8987 }
8988 // prefitVal will be the global observable value, need to divide that by tau
8989 prefitVal /= prefitError;
8990 // prefiterror will be tau ... need 1/sqrt(tau) for error
8991 prefitError = 1. / sqrt(prefitError);
8992 } else if (auto _g = pConstr->get<RooGaussian>(); _g) {
8993 prefitError =
8994 (pConstr->find(".sigma")) ? pConstr->find(".sigma")->get<RooAbsReal>()->getVal() : prefitError;
8995 prefitVal =
8996 (pConstr->find(".x")) ? pConstr->find(".x")->get<RooAbsReal>()->getVal() : 0; // usually the globs
8997 if (pConstr->find(".x") &&
8998 strcmp(p->GetName(), pConstr->find(".x")->get<RooAbsReal>()->GetName()) == 0) {
8999 // hybrid construction case,
9000 prefitVal = pConstr->find(".mean")->get<RooAbsReal>()->getVal();
9001 }
9002 }
9003
9004 if (customScale)
9005 prefitError = customScale;
9006 if (prefitError == 0) {
9007 Warning("Draw", "failed to determine prefit error of %s, using post-fit error", p->GetName());
9008 prefitError = _v->getError();
9009 }
9010 out->SetPoint(out->GetN(), out->GetN(), (_v->getVal() - prefitVal) / prefitError);
9011 out->SetPointError(out->GetN() - 1, 0, 0, (-_v->getErrorLo()) / prefitError,
9012 (_v->getErrorHi()) / prefitError);
9013 graphLabels.push_back(p->GetName());
9014 scale[p->GetName()] = prefitError;
9015 offset[p->GetName()] = prefitVal;
9016 } else if (!fParent) {
9017 // no parent to determine constraints from ... prefitError=0 will be the unconstrained ones
9018 if (customScale)
9019 prefitError = customScale;
9020 if (prefitError == 0) {
9021 // uses range of var
9022 prefitError = (std::max({_v->getMax() - _v->getVal(), _v->getVal() - _v->getMin(), 4.}) / 4);
9023 ugraph->SetPoint(ugraph->GetN(), ugraph->GetN(), (_v->getVal() - prefitVal) / prefitError);
9024 ugraph->SetPointError(ugraph->GetN() - 1, 0, 0, (-_v->getErrorLo()) / prefitError,
9025 (_v->getErrorHi()) / prefitError);
9026 ugraphLabels.push_back(p->GetName());
9027 } else {
9028 out->SetPoint(out->GetN(), out->GetN(), (_v->getVal() - prefitVal) / prefitError);
9029 out->SetPointError(out->GetN() - 1, 0, 0, (-_v->getErrorLo()) / prefitError,
9030 (_v->getErrorHi()) / prefitError);
9031 graphLabels.push_back(p->GetName());
9032 }
9033 scale[p->GetName()] = prefitError;
9034 offset[p->GetName()] = prefitVal;
9035
9036 } else {
9037 // unconstrained (or at least couldn't determine constraint) ... use par range if no prefit error
9038 if (customScale)
9039 prefitError = customScale;
9040 if (prefitError == 0) {
9041 prefitError = (std::max({_v->getMax() - _v->getVal(), _v->getVal() - _v->getMin(), 4.}) / 4);
9042 }
9043 ugraph->SetPoint(ugraph->GetN(), ugraph->GetN(), (_v->getVal() - prefitVal) / prefitError);
9044 ugraph->SetPointError(ugraph->GetN() - 1, 0, 0, (-_v->getErrorLo()) / prefitError,
9045 (_v->getErrorHi()) / prefitError);
9046 ugraphLabels.push_back(p->GetName());
9047 scale[p->GetName()] = prefitError;
9048 offset[p->GetName()] = prefitVal;
9049 }
9050 }
9051 auto graph = out;
9052
9053 // append ugraph points to end of graph
9054 for (int i = 0; i < ugraph->GetN(); i++)
9055 ugraph->SetPointX(i, i + graph->GetN());
9056 int nUnconstrained = ugraph->GetN();
9057 TList tmpList;
9058 tmpList.SetName("tmpList");
9059 tmpList.Add(ugraph);
9060 graph->Merge(&tmpList);
9061 tmpList.RemoveAll();
9062 delete ugraph;
9063 for (auto &l : ugraphLabels) {
9064 graphLabels.push_back(l);
9065 }
9066
9067 graph->SetBit(kCanDelete);
9068 graph->SetMarkerStyle(20);
9069 graph->SetMarkerSize(0.5);
9070
9071 graph->SetMaximum(4);
9072 graph->SetMinimum(-4);
9073
9074 bool doHorizontal =
9075 (!sOpt.Contains("impact") && sOpt.Contains("v")) || (sOpt.Contains("impact") && !sOpt.Contains("himpact"));
9076
9077 std::vector<std::pair<double, std::string>> covariances;
9078 /*double poiError = 0;*/ std::string poiName;
9079 double maxImpact = 0;
9080 if (sOpt.Contains("impact")) {
9081 if (sOpt.Contains("impact:")) {
9082 TString sOpt3(opt);
9083 poiName = sOpt3(sOpt3.Index("impact:") + 7, sOpt3.Length());
9084 } else {
9085 std::unique_ptr<RooAbsCollection> _poi(fr->floatParsFinal().selectByAttrib("poi", true));
9086 if (_poi->empty()) {
9087 throw std::runtime_error("No floating poi in the fit");
9088 } else if (_poi->size() != 1) {
9089 throw std::runtime_error("Multiple poi in the fit");
9090 }
9091 poiName = _poi->first()->GetName();
9092 }
9093 RooAbsArg *poi = fr->floatParsFinal().find(poiName.c_str());
9094 if (!poi) {
9095 throw std::runtime_error(TString::Format("Cannot find parameter %s", poiName.c_str()));
9096 }
9097 size_t poiIdx = fr->floatParsFinal().index(*poi);
9098 // put parameters in order of impact on the poi
9099
9100 // impact is regression coefficient * npError
9101 // relevant regression coefficient is cov / (npVariance)
9102 // i.e. DeltaX/sigmaX = [cov(X,Y)/(sigmaXsigmaY)]DeltaY/sigmaY
9103 // ... DeltaX = [cov(X,Y)/(sigmaY^2)]DeltaY
9104 // if DeltaY is just sigmaY then DeltaX = cov(X,Y)/sigmaY
9105
9106 for (auto &label : graphLabels) {
9107 covariances.emplace_back(fr->covarianceMatrix()(poiIdx, fr->floatParsFinal().index(label)) /
9108 dynamic_cast<RooRealVar *>(fr->floatParsFinal().find(label))->getError(),
9109 label);
9110 }
9111 std::sort(covariances.begin(), covariances.end(),
9112 [&](std::pair<double, std::string> i, std::pair<double, std::string> j) {
9113 return doHorizontal ? (std::abs(i.first) < std::abs(j.first))
9114 : (std::abs(i.first) > std::abs(j.first));
9115 });
9116
9117 TGraphAsymmErrors sortedGraph;
9118 std::vector<TString> sortedLabels;
9119 maxImpact = (doHorizontal) ? covariances.back().first
9120 : covariances.front().first; // note: max impact is likely to be self variance
9121 for (auto &c : covariances) {
9122 if (c.second == poi->GetName()) {
9123 // poiError = sqrt(c.first);
9124 continue; // skip self
9125 }
9126 c.first *= 4. / (maxImpact * 1.2);
9127 sortedLabels.push_back(c.second);
9128 size_t i = 0;
9129 for (; i < graphLabels.size(); i++) {
9130 if (graphLabels[i] == c.second) {
9131 break;
9132 }
9133 }
9134 sortedGraph.AddPoint(sortedGraph.GetN(), graph->GetPointY(i));
9135 sortedGraph.SetPointError(sortedGraph.GetN() - 1, 0, 0, graph->GetErrorYlow(i), graph->GetErrorYhigh(i));
9136 }
9137 graph->Set(0);
9138 TList tmpList2;
9139 tmpList2.SetName("tmpList");
9140 tmpList2.Add(&sortedGraph);
9141 graph->Merge(&tmpList2);
9142 tmpList2.RemoveAll();
9143 graphLabels = sortedLabels;
9144 graph->SetTitle("Fit Result Impact");
9145 }
9146
9147 // create a framing histogram
9148 TH2D *hist;
9149 if (doHorizontal) {
9150 hist = new TH2D(GetName(), fr->GetTitle(), 100, -4, 4, std::max(graph->GetN(), 1), -0.5,
9151 std::max(graph->GetN(), 1) - 0.5);
9152 int i = 1;
9153 for (auto &l : graphLabels) {
9154 hist->GetYaxis()->SetBinLabel(i++, l);
9155 }
9156 if (!graphLabels.empty())
9157 hist->GetYaxis()->LabelsOption("v");
9158 hist->GetXaxis()->SetTitle("(#hat{#theta}-#theta_{i})/#sigma_{i}");
9159 } else {
9160 hist = new TH2D(GetName(), fr->GetTitle(), std::max(graph->GetN(), 1), -0.5, std::max(graph->GetN(), 1) - 0.5,
9161 100, -4, 4);
9162 int i = 1;
9163 for (auto &l : graphLabels) {
9164 hist->GetXaxis()->SetBinLabel(i++, l);
9165 }
9166 if (!graphLabels.empty())
9167 hist->GetXaxis()->LabelsOption("v");
9168 hist->GetYaxis()->SetNdivisions(8, 0, 0);
9169 hist->GetYaxis()->SetTitle("(#hat{#theta}-#theta_{i})/#sigma_{i}");
9170 }
9171 hist->SetStats(false);
9172 hist->SetDirectory(nullptr);
9173 hist->SetBit(kCanDelete);
9174 auto histCopy = dynamic_cast<TH1 *>(hist->Clone(".axis"));
9175 histCopy->SetDirectory(nullptr);
9176 histCopy->SetBit(kCanDelete);
9177 auto _axis = (doHorizontal ? histCopy->GetYaxis() : histCopy->GetXaxis());
9178
9179 /*
9180 auto t = TH1::AddDirectoryStatus();
9181 TH1::AddDirectory(false);
9182 auto hist = new TH1F(TString::Format(".%s_pullFrame", GetName()), fr->GetTitle(), std::max(graph->GetN(),
9183 1), -0.5, std::max(graph->GetN(), 1) - 0.5); hist->SetStats(false); TH1::AddDirectory(t);
9184 hist->SetBit(kCanDelete);
9185 */
9186 // auto hist = graph->GetHistogram();
9187 graph->GetHistogram()->GetXaxis()->Set(std::max(graph->GetN(), 1), -0.5, std::max(graph->GetN(), 1) - 0.5);
9188 for (int ii = 1; ii <= _axis->GetNbins(); ii++) {
9189 graph->GetHistogram()->GetXaxis()->SetBinLabel(ii, _axis->GetBinLabel(ii));
9190 }
9191 // int i = 1;
9192 // for (auto &l : graphLabels) {
9193 // hist->GetXaxis()->SetBinLabel(i++, l);
9194 // }
9195 // hist->SetMaximum(4);
9196 // hist->SetMinimum(-4);
9197 // if (graph->GetN())
9198 // hist->GetXaxis()->LabelsOption("v");
9199 // hist->GetYaxis()->SetNdivisions(8, 0, 0);
9200 // hist->GetYaxis()->SetTitle("(#hat{#theta}-#theta_{i})/#sigma_{i}");
9201 clearPad();
9202 // create a new pad because adjust the margins ...
9203 auto oldPad = gPad;
9204 gPad->Divide(1, 1, 1e-9, 1e-9);
9205 gPad->cd(1);
9206
9207 if (doHorizontal) {
9208 gPad->SetLeftMargin(0.4);
9209 } else {
9210 gPad->SetBottomMargin(0.4);
9211 }
9212
9213 auto pNamesHist = dynamic_cast<TH1F *>(graph->GetHistogram()->Clone("scales")); // used by interactive "pull" plot
9214 pNamesHist->Sumw2();
9215 pNamesHist->SetDirectory(nullptr);
9216
9217 for (int ii = 1; ii <= graph->GetN(); ii++) { // use graph->GetN() to protect against the 0 pars case
9218 auto _p = fr->floatParsFinal().find(_axis->GetBinLabel(ii));
9219 pNamesHist->SetBinContent(ii, offset[_p->GetName()]);
9220 pNamesHist->SetBinError(ii, scale[_p->GetName()]);
9221 _axis->SetBinLabel(ii, strlen(_p->GetTitle()) ? _p->GetTitle() : _p->GetName());
9222 }
9223
9224 // hist->Draw(); -- now just draw the graph
9225
9226 if (!sOpt.Contains("impact")) {
9227 for (int ii = 2; ii >= 1; ii--) {
9228 auto pullBox = new TGraphErrors;
9229 pullBox->SetName(TString::Format("%dsigmaBand", ii));
9230 pullBox->SetBit(kCanDelete);
9231 pullBox->SetPoint(0, (doHorizontal) ? -ii : -0.5, (doHorizontal) ? -0.5 : 0);
9232 pullBox->SetPoint(1, (doHorizontal) ? ii : (_axis->GetNbins() - 0.5 - nUnconstrained),
9233 (doHorizontal) ? -0.5 : 0);
9234 pullBox->SetPointError(0, 0, (doHorizontal) ? (_axis->GetNbins() - nUnconstrained) : ii);
9235 pullBox->SetPointError(1, 0, (doHorizontal) ? (_axis->GetNbins() - nUnconstrained) : ii);
9236 pullBox->SetFillColor((ii == 2) ? kYellow : kGreen);
9237 hist->GetListOfFunctions()->Add(pullBox, "3"); // pullBox->Draw("3");
9238 }
9239 auto pullLine = new TGraph;
9240 pullLine->SetName("0sigmaLine");
9241 pullLine->SetBit(kCanDelete);
9242 pullLine->SetPoint(0, -0.5, 0);
9243 pullLine->SetPoint(1, _axis->GetNbins() - 0.5, 0);
9244 pullLine->SetLineStyle(2);
9245 pullLine->SetEditable(false);
9246 hist->GetListOfFunctions()->Add(pullLine, "l"); // pullLine->Draw("l");
9247
9248 // also draw vertical line separating constrained from unconstrained, if necessary
9249 if (nUnconstrained > 0) {
9250 pullLine = new TGraph;
9251 pullLine->SetName("dividerLine");
9252 pullLine->SetBit(kCanDelete);
9253 pullLine->SetPoint(0, graph->GetN() - 0.5 - nUnconstrained, -100);
9254 pullLine->SetPoint(1, graph->GetN() - 0.5 - nUnconstrained, 100);
9255 pullLine->SetLineStyle(2);
9256 pullLine->SetEditable(false);
9257 hist->GetListOfFunctions()->Add(pullLine, "l"); // pullLine->Draw("l");
9258 }
9259
9260 // and draw a pave with fr status info
9261 TPaveText *pave =
9262 new TPaveText(gPad->GetLeftMargin(), 1. - gPad->GetTopMargin(), 1. - gPad->GetRightMargin(), 0.98, "NDCNB");
9263 pave->SetFillStyle(0);
9264 pave->SetBorderSize(0);
9265 pave->SetMargin(0.);
9266 pave->SetName("status");
9267 pave->SetTextAlign(31);
9268 pave->AddText(TString::Format("minNLL: %g edm: %g", fr->minNll(), fr->edm()));
9269 std::string covQualTxt;
9270 switch (fr->covQual()) {
9271 case -1: covQualTxt = "Unknown"; break;
9272 case 0: covQualTxt = "Not calculated"; break;
9273 case 1: covQualTxt = "Approximate"; break;
9274 case 2: covQualTxt = "Forced Positive-Definite"; break;
9275 case 3: covQualTxt = "Accurate"; break;
9276 }
9277 pave->AddText(TString::Format("Cov. Quality: %d (%s)", fr->covQual(), covQualTxt.c_str()))
9278 ->SetTextColor((fr->covQual() == 3) ? kBlack : kRed);
9279
9280 std::string statusCodes;
9281 for (unsigned int i = 0; i < fr->numStatusHistory(); i++) {
9282 statusCodes += TString::Format(" %s = %d", fr->statusLabelHistory(i), fr->statusCodeHistory(i));
9283 }
9284 pave->AddText(statusCodes.c_str())->SetTextColor(fr->status() == 0 ? kBlack : kRed);
9285
9286 hist->GetListOfFunctions()->Add(pave);
9287
9288 } else {
9289 gPad->SetTicks(0, 0); // ensure mirrored ticks aren't drawn in this pad
9290
9291 if (doHorizontal) {
9292 // ensure canvas height big enough
9293 if (int(gPad->GetCanvas()->GetWh()) < pNamesHist->GetNbinsX() * 15) {
9294 gPad->GetCanvas()->SetCanvasSize(gPad->GetCanvas()->GetWw(), pNamesHist->GetNbinsX() * 15);
9295 }
9296 }
9297
9298 double factor = 475. / gPad->GetCanvas()->GetWh(); // Wh is the full canvas height, not window height
9299 gPad->SetTopMargin(gStyle->GetPadTopMargin() * factor); // fixed margin height
9300 gPad->SetBottomMargin(gStyle->GetPadBottomMargin() * factor); // fixed margin height
9301
9302 TGaxis *axis =
9303 new TGaxis(_axis->GetXmin(), -4, _axis->GetXmin(), 4, -1.2 * maxImpact, 1.2 * maxImpact, 510, "-S");
9304 axis->SetTextFont(_axis->GetTitleFont());
9305 axis->SetLabelFont(_axis->GetLabelFont());
9306 axis->SetTextSize((axis->GetTextFont() % 10 > 2) ? (10 / factor)
9307 : ((gPad->AbsPixeltoY(0) - gPad->AbsPixeltoY(10 / factor)) /
9308 (gPad->GetY2() - gPad->GetY1())));
9309 axis->SetTitle(TString::Format("#Delta %s", fr->floatParsFinal().find(poiName.c_str())->GetTitle()));
9310 axis->SetTickSize(axis->GetTickSize() * factor);
9311
9312 if (doHorizontal) {
9313 axis->SetLabelSize(
9314 (axis->GetLabelFont() % 10 > 2)
9315 ? (10 / factor)
9316 : ((gPad->AbsPixeltoY(0) - gPad->AbsPixeltoY(10 / factor)) / (gPad->GetY2() - gPad->GetY1())));
9317 // axis->SetTextSize(axis->GetTextSize()*factor);
9318 axis->SetTitleSize(
9319 (axis->GetTextFont() % 10 > 2)
9320 ? (10 / factor)
9321 : ((gPad->AbsPixeltoY(0) - gPad->AbsPixeltoY(10 / factor)) / (gPad->GetY2() - gPad->GetY1())));
9322 axis->SetTitleOffset(axis->GetTitleOffset() * factor);
9323 // axis->SetLabelOffset(axis->GetLabelOffset()*factor);
9324 _axis->SetLabelSize(
9325 (_axis->GetLabelFont() % 10 > 2)
9326 ? (10 / factor)
9327 : ((gPad->AbsPixeltoY(0) - gPad->AbsPixeltoY(10 / factor)) / (gPad->GetY2() - gPad->GetY1())));
9328 histCopy->GetXaxis()->SetTickLength(histCopy->GetXaxis()->GetTickLength() * factor);
9329 hist->GetXaxis()->SetTickLength(hist->GetXaxis()->GetTickLength() * factor);
9330 histCopy->GetYaxis()->SetTickLength(histCopy->GetYaxis()->GetTickLength() * factor);
9331 hist->GetYaxis()->SetTickLength(hist->GetYaxis()->GetTickLength() * factor);
9332 histCopy->GetXaxis()->SetTitleOffset(histCopy->GetXaxis()->GetTitleOffset() * factor);
9333 histCopy->GetXaxis()->SetLabelOffset(histCopy->GetXaxis()->GetLabelOffset() * factor);
9334 hist->GetXaxis()->SetTitleOffset(hist->GetXaxis()->GetTitleOffset() * factor);
9335 hist->GetXaxis()->SetLabelOffset(hist->GetXaxis()->GetLabelOffset() * factor);
9336 histCopy->GetXaxis()->SetTitleOffset(histCopy->GetXaxis()->GetTitleOffset() * factor);
9337 histCopy->GetXaxis()->SetLabelOffset(histCopy->GetXaxis()->GetLabelOffset() * factor);
9338 }
9339
9340 // create impact bar charts
9341 for (int tt = 0; tt < 2; tt++) {
9342 auto impact = static_cast<TH1 *>(
9343 graph->GetHistogram()->Clone(TString::Format("%s_impact+", tt == 0 ? "prefit" : "postfit")));
9344 impact->SetDirectory(nullptr);
9345 impact->GetYaxis()->SetTitle(TString::Format("#Delta%s/#sigma", poiName.c_str()));
9346 impact->SetBarWidth(0.9);
9347 impact->SetBarOffset(0.05);
9348 impact->SetLineColor(kBlack);
9349 impact->SetFillColor(kAzure - 4);
9350 impact->SetFillStyle(tt == 0 ? 3013 : 1001);
9351 auto impact2 =
9352 static_cast<TH1 *>(impact->Clone(TString::Format("%s_impact-", tt == 0 ? "prefit" : "postfit")));
9353 impact2->SetDirectory(nullptr);
9354 impact2->SetFillColor(kCyan);
9355 for (int ii = 1; ii <= pNamesHist->GetNbinsX(); ii++) {
9356 for (auto &c : covariances) {
9357 if (c.second != pNamesHist->GetXaxis()->GetBinLabel(ii))
9358 continue;
9359 auto vv = dynamic_cast<RooRealVar *>(fr->floatParsFinal().find(c.second.c_str()));
9360 auto vv_init = dynamic_cast<RooRealVar *>(fr->floatParsInit().find(c.second.c_str()));
9361 impact->SetBinContent(ii, ((tt == 0 && !vv_init->hasError()) || !vv->hasError())
9362 ? 0.
9363 : c.first * vv->getError() / vv->getErrorHi() *
9364 (tt == 0 ? (vv_init->getErrorHi() / vv->getErrorHi()) : 1.));
9365 impact2->SetBinContent(ii, ((tt == 0 && !vv_init->hasError()) || !vv->hasError())
9366 ? 0.
9367 : c.first * vv->getError() / vv->getErrorLo() *
9368 (tt == 0 ? (vv_init->getErrorLo() / vv->getErrorLo()) : 1.));
9369 }
9370 }
9371 hist->GetListOfFunctions()->Add(impact, (doHorizontal) ? "hbarsamemin0" : "bsamey+");
9372 hist->GetListOfFunctions()->Add(impact2, (doHorizontal) ? "hbarsamemin0" : "bsamey+");
9373 }
9374 // add three lines
9375 for (int ii = -1; ii <= 1; ii++) {
9376 auto pullLine = new TGraph;
9377 pullLine->SetName(TString::Format("%dsigmaLine", ii));
9378 pullLine->SetBit(kCanDelete);
9379 pullLine->SetPoint(0, -0.5, ii);
9380 pullLine->SetPoint(1, hist->GetNbinsY() - 0.5, ii);
9381 pullLine->SetLineStyle(2);
9382 pullLine->SetEditable(false);
9383 hist->GetListOfFunctions()->Add(pullLine, "l");
9384 }
9385 hist->GetListOfFunctions()->Add(axis); // draw axis last
9386 TLegend *leg1 =
9387 new TLegend(0.02, doHorizontal ? (1. - 0.22 * factor) : 0.02, 0.27, (doHorizontal ? 1. : 0.24));
9388 leg1->SetFillStyle(0);
9389 leg1->SetBorderSize(0);
9390 leg1->SetMargin(0.25);
9391 leg1->SetNColumns(2);
9392
9393 leg1->SetTextSize((leg1->GetTextFont() % 10 > 2) ? (10 / factor)
9394 : ((gPad->AbsPixeltoY(0) - gPad->AbsPixeltoY(10 / factor)) /
9395 (gPad->GetY2() - gPad->GetY1())));
9396 // leg1.SetTextFont(gStyle->GetTextFont());
9397 // leg1.SetTextSize(gStyle->GetTextSize());
9398 leg1->AddEntry((TObject *)nullptr, "Hessian Pre-fit", "");
9399 leg1->AddEntry((TObject *)nullptr, "Impact:", "");
9400 leg1->AddEntry(hist->FindObject("prefit_impact+"), "#theta = #hat{#theta}+#Delta#theta", "f");
9401 leg1->AddEntry(hist->FindObject("prefit_impact-"), "#theta = #hat{#theta}-#Delta#theta", "f");
9402 leg1->AddEntry((TObject *)nullptr, "Hessian Post-fit", "");
9403 leg1->AddEntry((TObject *)nullptr, "Impact:", "");
9404 leg1->AddEntry(hist->FindObject("postfit_impact+"), "#theta = #hat{#theta}+#Delta#theta", "f");
9405 leg1->AddEntry(hist->FindObject("postfit_impact-"), "#theta = #hat{#theta}-#Delta#theta", "f");
9406
9407 hist->GetListOfFunctions()->Add(leg1);
9408 if (gStyle->GetOptTitle()) {
9409 histCopy->SetBit(TH1::kNoTitle);
9410 TPaveText *title =
9411 new TPaveText(gPad->GetLeftMargin(), 1. - gPad->AbsPixeltoY(14), 1. - gPad->GetRightMargin(), 1., "NDC");
9412 title->ConvertNDCtoPad();
9413 title->SetY1NDC(1. - gPad->GetTopMargin() * 0.6);
9414 title->SetY2NDC(1);
9415 title->SetTextSize(
9416 (title->GetTextFont() % 10 > 2)
9417 ? (14 / factor)
9418 : ((gPad->AbsPixeltoY(0) - gPad->AbsPixeltoY(10 / factor)) / (gPad->GetY2() - gPad->GetY1())));
9419 title->SetFillStyle(0);
9420 title->SetBorderSize(0);
9421 title->AddText(histCopy->GetTitle());
9422 hist->GetListOfFunctions()->Add(title);
9423 }
9424 }
9425
9426 graph->SetEditable(false);
9427 pNamesHist->SetLineWidth(0);
9428 pNamesHist->SetMarkerSize(0);
9429 graph->GetListOfFunctions()->Add(pNamesHist, "same"); // graph->SetHistogram(pNamesHist);
9430 if (doHorizontal) {
9431
9432 // flip the graph and contained graphs
9433 for (int p = 0; p < graph->GetN(); p++) {
9434 graph->SetPoint(p, graph->GetPointY(p), graph->GetPointX(p));
9435 graph->SetPointError(p, graph->GetErrorYlow(p), graph->GetErrorYhigh(p), graph->GetErrorXlow(p),
9436 graph->GetErrorXhigh(p));
9437 }
9438 for (auto f : *hist->GetListOfFunctions()) {
9439 if (f->InheritsFrom("TH1")) {
9440 // f->Draw("hbarsamemin0");
9441 } /*else if (auto g2 = dynamic_cast<TGraphErrors *>(f)) {
9442 for (int p = 0; p < g2->GetN(); p++) {
9443 g2->SetPoint(p, g2->GetPointY(p), g2->GetPointX(p));
9444 g2->SetPointError(p, g2->GetErrorY(p), _axis->GetNbins());
9445 }
9446 //g2->Draw("3");
9447 } */
9448 else if (auto g = dynamic_cast<TGraph *>(f)) {
9449 for (int p = 0; p < g->GetN(); p++) {
9450 g->SetPoint(p, g->GetPointY(p), g->GetPointX(p));
9451 }
9452 // g->Draw("l");
9453 } else if (auto l = dynamic_cast<TLine *>(f)) {
9454 l->SetX1(l->GetY1());
9455 l->SetX2(l->GetY2());
9456 l->SetY1(_axis->GetXmax());
9457 l->SetY2(_axis->GetXmax());
9458 // l->Draw();
9459 }
9460 }
9461 }
9462
9463 graph->SetName("pulls");
9464 hist->GetListOfFunctions()->Add(graph, "z0p");
9465 // hist->GetListOfFunctions()->Add(histCopy->Clone(".axis"),(sOpt.Contains("impact") &&
9466 // !doHorizontal)?"axissamey+":"axissame"); // doesn't display right when zoom the axis
9467 if (!hasSame) {
9468 histCopy->Draw((sOpt.Contains("impact") && !doHorizontal)
9469 ? "axisy+"
9470 : "axis"); // draws the axis, called ".axis" for easy access
9471 }
9472 hist->Draw("same");
9473 //
9474 // if(sOpt.Contains("impact")) {
9475 // // make main object the histogram
9476 // auto h = (TH1*)graph->GetHistogram()->Clone("impact");
9477 // graph->GetListOfFunctions()->RemoveAll();
9478 // for(int ii=1;ii<=h->GetNbinsX();ii++) h->SetBinContent(ii,-4);
9479 // h->GetListOfFunctions()->Add(graph,"z0p");
9480 // h->Draw("hbar");
9481 // } else {
9482 // graph->Draw(sOpt.Contains("impact") ? "az0py+" : "az0p");
9483 // }
9484 auto hh = dynamic_cast<TH1 *>(histCopy->Clone(".axiscopy"));
9485 hh->SetDirectory(nullptr);
9486 hh->SetBit(kCanDelete);
9487 hh->Draw(
9488 (sOpt.Contains("impact") && !doHorizontal)
9489 ? "axissamey+"
9490 : "axissame"); // overlay axis again -- important is last so can remove if don't pad->Update before reclear
9491 gPad->Modified();
9492 oldPad->cd();
9493 // gPad->Update();
9494 return;
9495 }
9496
9497 if (get()->InheritsFrom("RooAbsData")) {
9498 auto s = parentPdf();
9499 if (s && s->get<RooSimultaneous>()) {
9500 // drawing dataset associated to a simultaneous means must find subpads with variation names
9501 for (auto c : s->bins()) {
9502 auto _pad = dynamic_cast<TPad *>(gPad->GetPrimitive(c->GetName()));
9503 if (!_pad)
9504 continue; // channel was hidden?
9505 auto ds = c->datasets().find(GetName());
9506 if (!ds) {
9507 std::cout << " no ds " << GetName() << std::endl;
9508 continue;
9509 }
9510 auto tmp = gPad;
9511 _pad->cd();
9512 ds->Draw(opt);
9513 tmp->cd();
9514 }
9515 gPad->Modified();
9516 return;
9517 }
9518
9519 if (!s && hasSame) {
9520 // draw onto all subpads with = in the name
9521 // if has no such subpads, draw onto this pad
9522 bool doneDraw = false;
9523 for (auto o : *gPad->GetListOfPrimitives()) {
9524 if (auto p = dynamic_cast<TPad *>(o); p && TString(p->GetName()).Contains('=')) {
9525 auto _tmp = gPad;
9526 p->cd();
9527 Draw(opt);
9528 _tmp->cd();
9529 doneDraw = true;
9530 }
9531 }
9532 if (doneDraw) {
9533 gPad->Modified();
9534 return;
9535 }
9536 }
9537
9538 auto dataGraph = BuildGraph(v, false, (!s && hasSame) ? gPad : nullptr);
9539 if (!dataGraph)
9540 return;
9541
9542 dataGraph->SetBit(kCanDelete); // will be be deleted when pad is cleared
9543 dataGraph->SetMarkerSize(dataGraph->GetMarkerSize() * gPad->GetWNDC()); // scale marker sizes to pad size
9544
9545 if (s && !s->get<RooAbsPdf>()->canBeExtended()) {
9546 // normalize dataGraph to 1
9547 double tot = 0;
9548 for (int i = 0; i < dataGraph->GetN(); i++)
9549 tot += dataGraph->GetPointY(i);
9550 dataGraph->Scale(1. / tot);
9551 }
9552
9553 if (!hasSame) {
9554 clearPad();
9555 dataGraph->Draw("Az0p");
9556 addLegendEntry(dataGraph, strlen(dataGraph->GetTitle()) ? dataGraph->GetTitle() : GetName(), "pEX0");
9557 gPad->Modified();
9558 // gPad->Update();
9559 return;
9560 }
9561
9562 bool noPoint = false;
9563 if (v && dynamic_cast<RooAbsArg *>(v)->getAttribute("global") && dataGraph->GetN() == 1) {
9564 // global observable ... if graph has only 1 data point line it up on the histogram value
9565 for (auto o : *gPad->GetListOfPrimitives()) {
9566 if (auto h = dynamic_cast<TH1 *>(o);
9567 h && strcmp(h->GetXaxis()->GetName(), dynamic_cast<TObject *>(v)->GetName()) == 0) {
9568 dataGraph->SetPointY(0, h->Interpolate(dataGraph->GetPointX(0)));
9569 noPoint = true;
9570 break;
9571 }
9572 }
9573 }
9574
9575 if (auto _pad = dynamic_cast<TPad *>(gPad->FindObject("auxPad")); _pad) {
9576 if (auto h = dynamic_cast<TH1 *>(_pad->GetPrimitive("auxHist")); h) {
9577 TString histName = h->GetTitle(); // split it by | char
9578 TString histType = histName(histName.Index('|') + 1, histName.Length());
9579 histName = histName(0, histName.Index('|'));
9580 if (auto mainHist = dynamic_cast<TH1 *>(gPad->GetPrimitive(histName));
9581 mainHist && auxFunctions.find(h->GetYaxis()->GetTitle()) != auxFunctions.end()) {
9582 // decide what to do based on title of auxHist (previously used name of y-axis but that changed axis
9583 // behaviour) use title instead
9584 auto ratioGraph = dynamic_cast<TGraphAsymmErrors *>(dataGraph->Clone(dataGraph->GetName()));
9585 ratioGraph->SetBit(kCanDelete);
9586 for (int i = 0; i < ratioGraph->GetN(); i++) {
9587 double val = ratioGraph->GetPointY(i);
9588 int binNum = mainHist->FindFixBin(ratioGraph->GetPointX(i));
9589 double nom = mainHist->GetBinContent(binNum);
9590 double nomerr = mainHist->GetBinError(binNum);
9591 double yval =
9592 std::get<0>(auxFunctions[h->GetYaxis()->GetTitle()])(ratioGraph->GetPointY(i), nom, nomerr);
9593 double yup = std::get<0>(auxFunctions[h->GetYaxis()->GetTitle()])(val + ratioGraph->GetErrorYhigh(i),
9594 nom, nomerr) -
9595 yval;
9596 double ydown = yval - std::get<0>(auxFunctions[h->GetYaxis()->GetTitle()])(
9597 val - ratioGraph->GetErrorYlow(i), nom, nomerr);
9598 if (!std::isnan(yval)) {
9599 ratioGraph->SetPointY(i, yval);
9600 if (!std::isnan(yup))
9601 ratioGraph->SetPointEYhigh(i, yup);
9602 if (!std::isnan(ydown))
9603 ratioGraph->SetPointEYlow(i, ydown);
9604 }
9605 }
9606 // remove the zero points
9607 int i = 0;
9608 while (i < ratioGraph->GetN()) {
9609 if (ratioGraph->GetPointY(i) == 0 && ratioGraph->GetErrorYhigh(i) == 0 &&
9610 ratioGraph->GetErrorYlow(i) == 0) {
9611 ratioGraph->RemovePoint(i);
9612 } else {
9613 i++;
9614 }
9615 }
9616 auto _tmpPad = gPad;
9617 _pad->cd();
9618 ratioGraph->Draw("z0psame");
9619 auto minMax = graphMinMax(ratioGraph);
9620 adjustYRange(minMax.first, minMax.second, h, std::get<1>(auxFunctions[h->GetYaxis()->GetTitle()]));
9621 _tmpPad->cd();
9622 }
9623 }
9624 }
9625
9626 dataGraph->Draw("z0p same");
9627 addLegendEntry((noPoint) ? nullptr : dataGraph, strlen(dataGraph->GetTitle()) ? dataGraph->GetTitle() : GetName(),
9628 noPoint ? "" : "pEX0");
9629
9630 auto minMax = graphMinMax(dynamic_cast<TGraphAsymmErrors *>(dataGraph));
9631 adjustYRange(minMax.first, minMax.second);
9632
9633 gPad->Modified();
9634 // gPad->Update();
9635 return;
9636 }
9637
9638 // auto _ax = GetXaxis();
9639 // auto v = (_ax) ? dynamic_cast<RooRealVar*>(/*possibleObs.first()*/_ax->GetParent()) : nullptr;
9640 // if (!v) { v = get<RooRealVar>(); } // self-axis
9641 // if (!v) return;
9642
9643 if (auto lv = get<RooAbsLValue>(); lv && fParent && fParent->get<RooAbsData>()) {
9644 // drawing an observable from a dataset ... build graph, and exit
9645 auto gr = fParent->BuildGraph(lv, true);
9647 gr->Draw(hasSame ? "P" : "AP");
9648 return;
9649 }
9650
9651 if (forceNames != "") {
9652 // drawing a force plot ... build nll and fill a histogram with force terms
9653 auto _dsets = datasets();
9654 bool _drawn = false;
9655 auto _coords = coords();
9656 auto _fr = fitResult();
9657 auto initPar = dynamic_cast<RooRealVar *>(_fr.get<RooFitResult>()->floatParsInit().find(forceNames));
9658 if (!initPar)
9659 return;
9660 std::vector<double> valuesToDo = {initPar->getVal()};
9661 if (initPar->hasError() || initPar->hasAsymError()) {
9662 valuesToDo.push_back(initPar->getVal() + initPar->getErrorLo());
9663 valuesToDo.push_back(initPar->getVal() + initPar->getErrorHi());
9664 }
9665 int ii = 0;
9666 for (auto valueToDo : valuesToDo) {
9667 ii++;
9668 for (auto &d : _dsets) {
9669 if (!d->get()->TestBit(1 << 20))
9670 continue;
9671 auto emptyHist = BuildHistogram(v, true);
9672 emptyHist->SetBit(kCanDelete);
9673 auto _obs = d->obs();
9674 auto x = _obs.find((v) ? dynamic_cast<TObject *>(v)->GetName() : emptyHist->GetXaxis()->GetName());
9675 auto _nll = nll(d);
9676 auto theData = d->get<RooAbsData>();
9677 int nevent = theData->numEntries();
9678 for (int i = 0; i < nevent; i++) {
9679 theData->get(i);
9680 bool _skip = false;
9681 for (const auto &_c : _coords) {
9682 if (auto cat = _c->get<RooAbsCategoryLValue>(); cat) {
9683 if (cat->getIndex() != theData->get()->getCatIndex(cat->GetName())) {
9684 _skip = true;
9685 break;
9686 }
9687 }
9688 }
9689 if (_skip)
9690 continue;
9691
9692 if (x) {
9693 auto val = _nll.pars()->getRealValue(initPar->GetName());
9694 if (ii > 1)
9695 _nll.pars()->setRealValue(initPar->GetName(), valueToDo);
9696 auto nllVal = _nll.getEntryVal(i);
9697 _nll.pars()->setRealValue(initPar->GetName(), initPar->getVal());
9698 auto nllVal2 = _nll.getEntryVal(i);
9699 _nll.pars()->setRealValue(initPar->GetName(), val);
9700 emptyHist->Fill(x->get<RooAbsReal>()->getVal(), (nllVal2 - nllVal));
9701 }
9702 }
9703 // include the extendedTerm, distributed evenly over the bins
9704 // probably should be somehow dependent on data density though (i.e. bins with more data get more of it?)
9705 auto val = _nll.pars()->getRealValue(initPar->GetName());
9706 if (ii > 1)
9707 _nll.pars()->setRealValue(initPar->GetName(), valueToDo);
9708 auto _extTerm = _nll.extendedTerm();
9709 _nll.pars()->setRealValue(initPar->GetName(), initPar->getVal());
9710 auto _extTerm2 = _nll.extendedTerm();
9711 _nll.pars()->setRealValue(initPar->GetName(), val);
9712 for (int i = 1; i <= emptyHist->GetNbinsX(); i++) {
9713 emptyHist->SetBinContent(i,
9714 emptyHist->GetBinContent(i) + (_extTerm2 - _extTerm) / emptyHist->GetNbinsX());
9715 emptyHist->SetBinError(i, 0);
9716 }
9717 emptyHist->GetYaxis()->SetTitle("log (L(#theta)/L(#theta_{0}))");
9718 emptyHist->SetTitle(TString::Format("#theta = %g", (ii > 1) ? valueToDo : val));
9719 if (ii == 1)
9720 emptyHist->SetLineColor(kBlack);
9721 if (ii == 2) {
9722 emptyHist->SetLineColor(kRed);
9723 } else if (ii == 3) {
9724 emptyHist->SetLineColor(kBlue);
9725 }
9726 emptyHist->Draw(_drawn ? "same" : "");
9727 _drawn = true;
9728 }
9729 }
9730 return;
9731 }
9732
9733 auto rar = get<RooAbsReal>();
9734 const xRooNode *rarNode = this;
9735 if (!rar) {
9736 get()->Draw();
9737 return;
9738 }
9739 RooAbsReal *sf = nullptr;
9740 if (get()->InheritsFrom("RooExtendPdf")) {
9741 browse();
9742 rarNode = find(".pdf").get();
9743 // rar = rarNode->get<RooAbsReal>();
9744 sf = find(".n")->get<RooAbsReal>();
9745 }
9746
9747 auto h = BuildHistogram(v, false, hasErrorOpt);
9748 if (!h)
9749 return;
9750 h->SetBit(kCanDelete);
9751
9752 if (!v)
9753 v = getObject<RooAbsLValue>(h->GetXaxis()->GetName()).get();
9754 RooAbsArg *vv = (v) ? dynamic_cast<RooAbsArg *>(v) : rar;
9755 if (h->GetXaxis()->IsAlphanumeric()) {
9756 // do this to get bin labels
9757 h->GetXaxis()->SetName("xaxis"); // WARNING -- this messes up anywhere we GetXaxis()->GetName()
9758 }
9759
9760 if (rar->InheritsFrom("RooAbsPdf") && !(rar->InheritsFrom("RooRealSumPdf") || rar->InheritsFrom("RooAddPdf"))) {
9761 // append parameter values to title if has such
9762 RooArgSet s;
9763 rar->leafNodeServerList(&s);
9764 if (v)
9765 s.remove(*dynamic_cast<RooAbsArg *>(v));
9766 if (!s.empty()) {
9767 TString ss = h->GetTitle();
9768 ss += " [";
9769 bool first = true;
9770 for (auto _p : s) {
9771 auto _v = dynamic_cast<RooRealVar *>(_p);
9772 if (!_v)
9773 continue;
9774 if (!first)
9775 ss += ",";
9776 first = false;
9777 ss += TString::Format("%s=%g", strlen(_p->GetTitle()) ? _p->GetTitle() : _p->GetName(), _v->getVal());
9778 if (_v->hasError()) {
9779 ss += TString::Format("#pm %g", _v->getError());
9780 }
9781 }
9782 ss += "]";
9783 h->SetTitle(ss);
9784 }
9785 }
9786
9787 if (!hasSame) {
9788 if (obs().find(vv->GetName())) {
9789 gPad->SetGrid(0, 0);
9790 } else {
9791 gPad->SetGrid(1, 1);
9792 }
9793 }
9794 TString dOpt = (TString(rar->ClassName()).Contains("Hist") || rar->isBinnedDistribution(*vv) ||
9795 h->GetNbinsX() == 1 || rar->getAttribute("BinnedLikelihood") ||
9796 (dynamic_cast<RooAbsRealLValue *>(vv) &&
9797 std::unique_ptr<std::list<double>>(rar->binBoundaries(*dynamic_cast<RooAbsRealLValue *>(vv),
9798 -std::numeric_limits<double>::infinity(),
9799 std::numeric_limits<double>::infinity()))))
9800 ? ""
9801 : "LF2";
9802 if (auto d = dynamic_cast<RooHistFunc *>(rar); d && !d->isBinnedDistribution(*vv) && h->GetNbinsX() != 1) {
9803 dOpt = "LF2"; // hist func is interpolated, so draw it as such
9804 }
9805 if (dOpt == "LF2" && !components().empty()) {
9806 // check if all components of dOpt are "Hist" type (CMS model support)
9807 // if so then dOpt="";
9808 bool allHist = true;
9809 for (auto &s : components()) {
9810 if (!(s->get() && TString(s->get()->ClassName()).Contains("Hist"))) {
9811 allHist = false;
9812 break;
9813 }
9814 }
9815 if (allHist)
9816 dOpt = "";
9817 }
9818
9819 if (rar == vv && rar->IsA() == RooRealVar::Class()) {
9820 dOpt += "TEXT";
9821 // add a TExec to the histogram so that when edited it will propagate to var
9822 gROOT->SetEditHistograms(true);
9823 } else {
9824 gROOT->SetEditHistograms(false);
9825 }
9826
9827 if (hasSame) {
9828 dOpt += " same";
9829 } else {
9830 hAxis = h;
9831 }
9832
9833 if (dOpt.Contains("TEXT") || sOpt.Contains("text")) {
9834 // adjust marker size so text is good
9835 h->SetMarkerSize(gStyle->GetLabelSize("Z") / (0.02 * gPad->GetHNDC()));
9836 }
9837
9838 bool hasError(false);
9839 for (int i = 0; i < h->GetSumw2N(); i++) {
9840 if (h->GetSumw2()->At(i)) {
9841 hasError = true;
9842 break;
9843 }
9844 }
9845
9846 /** This doesn't seem necessary in at least 6.26 any more - pads seem adjusted on their own
9847 if (!hasSame && h->GetYaxis()->GetTitleFont()%10 == 2) {
9848 h->GetYaxis()->SetTitleOffset( gPad->GetLeftMargin() / gStyle->GetPadLeftMargin() );
9849 } */
9850 // don't this instead - dont want to leave as zero (auto) in case show aux plot
9851 if (!hasSame && h->GetYaxis()->GetTitleFont() % 10 == 2) {
9852 h->GetYaxis()->SetTitleOffset(1.);
9853 }
9854
9855 TH1 *errHist = nullptr;
9856 if (hasError) {
9857 h->SetFillStyle(hasError ? 3005 : 0);
9858 h->SetFillColor(h->GetLineColor());
9859 h->SetMarkerStyle(0);
9860 errHist = dynamic_cast<TH1 *>(h->Clone(Form("%s_err", h->GetName())));
9861 errHist->SetBit(kCanDelete);
9862 errHist->SetDirectory(nullptr);
9863 h->SetFillStyle(0);
9864 for (int i = 1; i <= h->GetNbinsX(); i++) {
9865 h->SetBinError(i, 0);
9866 }
9867 }
9868
9869 if (!hasSame)
9870 clearPad();
9871
9872 if (rar == vv && rar->IsA() == RooRealVar::Class()) {
9873 // add a TExec to the histogram so that when edited it will propagate to var
9874 // h->GetListOfFunctions()->Add(h->Clone("self"),"TEXTHIST");
9875 dOpt = "TEXT";
9876 auto node = new xRooNode(*this);
9877 auto _hist = (errHist) ? errHist : h;
9878 auto hCopy = (errHist) ? nullptr : dynamic_cast<TH1 *>(h->Clone());
9879 if (hCopy)
9880 hCopy->SetDirectory(nullptr);
9881 _hist->GetListOfFunctions()->Add(node);
9882 _hist->GetListOfFunctions()->Add(new TExec(
9883 ".update",
9885 "gROOT->SetEditHistograms(true);auto h = dynamic_cast<TH1*>(gPad->GetPrimitive(\"%s\")); if(h) { double "
9886 "range= h->GetMaximum()-h->GetMinimum(); if(auto n "
9887 "= dynamic_cast<xRooNode*>(h->GetListOfFunctions()->FindObject(\"%s\")); n && "
9888 "n->TestBit(TObject::kNotDeleted) && n->get<RooRealVar>()->getVal() != h->GetBinContent(1)) {"
9889 "h->SetBinContent(1, "
9890 "TString::Format(\"%%.2g\",int(h->GetBinContent(1)/(range*0.01))*range*0.01).Atof());n->SetContent( "
9891 "h->GetBinContent(1) ); for(auto pp : *h->GetListOfFunctions()) if(auto hh = "
9892 "dynamic_cast<TH1*>(pp))hh->SetBinContent(1,h->GetBinContent(1));} if(h->GetBinContent(1)==0.) "
9893 "h->SetBinContent(1,range*0.005); gPad->Modified();gPad->Update(); }",
9894 _hist->GetName(), node->GetName())));
9895 if (errHist) {
9896 errHist->GetListOfFunctions()->Add(h, "TEXT HIST same");
9897 errHist->SetFillColor(h->GetLineColor());
9898 } else {
9899 hCopy->SetBit(kCanDelete);
9900 _hist->GetListOfFunctions()->Add(hCopy, "TEXT HIST same");
9901 _hist->SetBinError(1, 0);
9902 }
9903 _hist->SetStats(false);
9904 // if (_hist->GetBinContent(1)==0.) _hist->SetBinContent(1,(_hist->GetMaximum()-_hist->GetMinimum())*0.005);
9905 _hist->Draw(((errHist) ? "e2" : ""));
9906 gPad->Modified();
9907 return;
9908 }
9909
9910 bool overlayExisted = false;
9911 if (hasOverlay) {
9912 h->SetName(TString::Format("%s%s", h->GetName(), overlayName.Data()));
9913 if (auto existing = dynamic_cast<TH1 *>(gPad->GetPrimitive(h->GetName())); existing) {
9914 existing->Reset();
9915 existing->Add(h);
9916 delete h;
9917 h = existing;
9918 overlayExisted = true;
9919 } else {
9920 TString oldStyle = (rar && rar->getStringAttribute("style")) ? rar->getStringAttribute("style") : "";
9921 h->SetTitle(overlayName);
9922 // for overlays will take style from current gStyle before overriding with personal style
9923 // this ensures initial style will be whatever gStyle is, rather than whatever ours is
9924 (TAttLine &)(*h) = *gStyle;
9925
9926 // std::shared_ptr<TStyle> style; // use to keep alive for access from GetStyle below, in case
9927 // getObject has decided to return the owning ptr (for some reason) if
9928 // (!gROOT->GetStyle(h->GetTitle())) {
9929 // if ( (style = getObject<TStyle>(h->GetTitle())) ) {
9930 // // loaded style (from workspace?) so put in list and use that
9931 // gROOT->GetListOfStyles()->Add(style.get());
9932 // } else {
9933 // // create new style - gets put in style list automatically so don't have to delete
9934 // // acquire them so saved to workspaces for auto reload ...
9935 // style = acquireNew<TStyle>(h->GetTitle(),
9936 // TString::Format("Style for %s component", h->GetTitle()));
9937 // (TAttLine &) (*style) = *dynamic_cast<TAttLine *>(h);
9938 // (TAttFill &) (*style) = *dynamic_cast<TAttFill *>(h);
9939 // (TAttMarker &) (*style) = *dynamic_cast<TAttMarker *>(h);
9940 // gROOT->GetListOfStyles()->Add(style.get());
9941 // }
9942 // }
9943 // (TAttLine&)(*h) = *(gROOT->GetStyle(h->GetTitle()) ? gROOT->GetStyle(h->GetTitle()) : gStyle);
9944 // (TAttFill&)(*h) = *(gROOT->GetStyle(h->GetTitle()) ? gROOT->GetStyle(h->GetTitle()) : gStyle);
9945 // (TAttMarker&)(*h) = *(gROOT->GetStyle(h->GetTitle()) ? gROOT->GetStyle(h->GetTitle()) : gStyle);
9946 auto _style = style(h);
9947 rar->setStringAttribute("style", oldStyle == "" ? nullptr : oldStyle.Data()); // restores old style
9948 if (_style) {
9949 (TAttLine &)(*h) = *_style;
9950 (TAttFill &)(*h) = *_style;
9951 (TAttMarker &)(*h) = *_style;
9952 }
9953 h->Draw(dOpt);
9954 if (errHist) {
9955 errHist->SetTitle(overlayName);
9956 (TAttLine &)(*errHist) = *h;
9957 errHist->SetFillColor(h->GetLineColor());
9958 }
9959 }
9960 } else {
9961 auto _style = style(h);
9962 if (_style) {
9963 (TAttLine &)(*h) = *_style;
9964 (TAttFill &)(*h) = *_style;
9965 (TAttMarker &)(*h) = *_style;
9966 if (errHist) {
9967 (TAttLine &)(*errHist) = *h;
9968 errHist->SetFillColor(h->GetLineColor());
9969 }
9970 }
9971 h->Draw(dOpt + sOpt);
9972 }
9973
9974 if (!hasOverlay && (rarNode->get()->InheritsFrom("RooRealSumPdf") || rarNode->get()->InheritsFrom("RooAddPdf"))) {
9975 // build a stack unless not requested
9976 if (!nostack) {
9977 THStack *stack = new THStack(TString::Format("%s_stack", rar->GetName()),
9978 TString::Format("%s;%s", rar->GetTitle(), h->GetXaxis()->GetTitle()));
9979 int count = 2;
9980 std::map<std::string, int> colorByTitle; // TODO: should fill from any existing legend
9981 std::set<std::string> allTitles;
9982 bool titleMatchName = true;
9983 std::map<std::string, TH1 *> histGroups;
9984 std::vector<TH1 *> hhs;
9985
9986 // support for CMS model case where has single component containing many coeffs
9987 // will build stack by setting each coeff equal to 0 in turn, rebuilding the histogram
9988 // the difference from the "full" histogram will be the component
9989 RooArgList cms_coefs;
9990 if (!rarNode->components().empty()) {
9991 auto comps = rarNode->components()[0];
9992 for (auto &c : *comps) {
9993 if (c->fFolder == "!.coeffs")
9994 cms_coefs.add(*c->get<RooAbsArg>());
9995 }
9996 }
9997 if (!cms_coefs.empty()) {
9998 RooRealVar zero("zero", "", 0);
9999 std::shared_ptr<TH1> prevHist(static_cast<TH1 *>(h->Clone()));
10000 for (auto c : cms_coefs) {
10001 // seems I have to remake the function each time, as haven't figured out what cache needs clearing?
10002 std::unique_ptr<RooAbsReal> f(
10003 dynamic_cast<RooAbsReal *>(rarNode->components()[0]->get()->Clone("tmpCopy")));
10004 zero.setAttribute(
10005 Form("ORIGNAME:%s", c->GetName())); // used in redirectServers to say what this replaces
10006 f->redirectServers(RooArgSet(zero), false, true); // each time will replace one additional coef
10007 // zero.setAttribute(Form("ORIGNAME:%s",c->GetName()),false); (commented out so that on next iteration
10008 // will still replace all prev)
10009 auto hh = xRooNode(*f, *this).BuildHistogram(v);
10010 if (sf)
10011 hh->Scale(sf->getVal());
10012 if (strlen(hh->GetTitle()) == 0)
10013 hh->SetTitle(c->GetName()); // ensure all hists has titles
10014 titleMatchName &= (TString(c->GetName()) == hh->GetTitle() ||
10015 TString(hh->GetTitle()).BeginsWith(TString(c->GetName()) + "_"));
10016 std::shared_ptr<TH1> nextHist(static_cast<TH1 *>(hh->Clone()));
10017 hh->Add(prevHist.get(), -1.);
10018 hh->Scale(-1.);
10019 hhs.push_back(hh);
10020 prevHist = nextHist;
10021 }
10022 } else {
10023 for (auto &samp : rarNode->components()) {
10024 auto hh = samp->BuildHistogram(v);
10025 if (sf)
10026 hh->Scale(sf->getVal());
10027 hhs.push_back(hh);
10028 if (strlen(hh->GetTitle()) == 0)
10029 hh->SetTitle(samp->GetName()); // ensure all hists has titles
10030 titleMatchName &= (TString(samp->GetName()) == hh->GetTitle() ||
10031 TString(hh->GetTitle()).BeginsWith(TString(samp->GetName()) + "_"));
10032 }
10033 }
10034 for (auto &hh : hhs) {
10035 // automatically group hists that all have the same title
10036 if (histGroups.find(hh->GetTitle()) == histGroups.end()) {
10037 histGroups[hh->GetTitle()] = hh;
10038 } else {
10039 // add it into this group
10040 histGroups[hh->GetTitle()]->Add(hh);
10041 delete hh;
10042 continue;
10043 }
10044 auto hhMin = (hh->GetMinimum() == 0) ? hh->GetMinimum(1e-9) : hh->GetMinimum();
10045 if (!stack->GetHists() && h->GetMinimum() > hhMin) {
10046 auto newMin = hhMin - (h->GetMaximum() - hhMin) * gStyle->GetHistTopMargin();
10047 if (hhMin >= 0 && newMin < 0)
10048 newMin = hhMin * 0.99;
10049 adjustYRange(newMin, h->GetMaximum());
10050 }
10051
10052 /*if(stack->GetHists() && stack->GetHists()->GetEntries()>0) {
10053 // to remove rounding effects on bin boundaries, see if binnings compatible
10054 auto _h1 = dynamic_cast<TH1*>(stack->GetHists()->At(0));
10055 if(_h1->GetNbinsX()==hh->GetNbinsX()) TODO ... finish dealing with silly rounding effects
10056 }*/
10057 TString thisOpt = dOpt;
10058 // uncomment next line to blend continuous with discrete components .. get some unpleasant "poke through"
10059 // effects though
10060 // if(auto s = samp->get<RooAbsReal>(); s) thisOpt = s->isBinnedDistribution(*dynamic_cast<RooAbsArg*>(v)) ?
10061 // "" : "LF2";
10062 stack->Add(hh, thisOpt);
10063 allTitles.insert(hh->GetTitle());
10064 }
10065 stack->SetBit(kCanDelete); // should delete its sub histograms
10066 stack->Draw("noclear same");
10067 h->Draw(
10068 dOpt + sOpt +
10069 "same"); // overlay again .. if stack would cover original hist (negative components) we still see integral
10070 h->Draw("axissame"); // redraws axis
10071
10072 TList *ll = stack->GetHists();
10073 if (ll && ll->GetEntries()) {
10074
10075 // get common prefix to strip off only if all titles match names and
10076 // any title is longer than 10 chars
10077 size_t e = std::min(allTitles.begin()->size(), allTitles.rbegin()->size());
10078 size_t ii = 0;
10079 bool goodPrefix = false;
10080 std::string commonSuffix;
10081 if (titleMatchName && ll->GetEntries() > 1) {
10082 while (ii < e - 1 && allTitles.begin()->at(ii) == allTitles.rbegin()->at(ii)) {
10083 ii++;
10084 if (allTitles.begin()->at(ii) == '_' || allTitles.begin()->at(ii) == ' ')
10085 goodPrefix = true;
10086 }
10087
10088 // find common suffix if there is one .. must start with a "_"
10089 bool stop = false;
10090 while (!stop && commonSuffix.size() < size_t(e - 1)) {
10091 commonSuffix = allTitles.begin()->substr(allTitles.begin()->length() - commonSuffix.length() - 1);
10092 for (auto &t : allTitles) {
10093 if (!TString(t).EndsWith(commonSuffix.c_str())) {
10094 commonSuffix = commonSuffix.substr(1);
10095 stop = true;
10096 break;
10097 }
10098 }
10099 }
10100 if (commonSuffix.find('_') == std::string::npos) {
10101 commonSuffix = "";
10102 } else {
10103 commonSuffix = commonSuffix.substr(commonSuffix.find('_'));
10104 }
10105 }
10106 if (!goodPrefix)
10107 ii = 0;
10108
10109 // also find how many characters are needed to distinguish all entries (that dont have the same name)
10110 // then carry on up to first space or underscore
10111 size_t jj = 0;
10112 std::map<std::string, std::string> reducedTitles;
10113 while (reducedTitles.size() != allTitles.size()) {
10114 jj++;
10115 std::map<std::string, int> titlesMap;
10116 for (auto &s : allTitles) {
10117 if (reducedTitles.count(s))
10118 continue;
10119 titlesMap[s.substr(0, jj)]++;
10120 }
10121 for (auto &s : allTitles) {
10122 if (titlesMap[s.substr(0, jj)] == 1 && (jj >= s.length() || s.at(jj) == ' ' || s.at(jj) == '_')) {
10123 reducedTitles[s] = s.substr(0, jj);
10124 }
10125 }
10126 }
10127
10128 // strip common prefix and suffix before adding
10129 for (int i = ll->GetEntries() - 1; i >= 0; i--) { // go in reverse order
10130 auto _title = (ll->GetEntries() > 5) ? reducedTitles[ll->At(i)->GetTitle()] : ll->At(i)->GetTitle();
10131 _title = _title.substr(ii < _title.size() ? ii : 0);
10132 if (!commonSuffix.empty() && TString(_title).EndsWith(commonSuffix.c_str()))
10133 _title = _title.substr(0, _title.length() - commonSuffix.length());
10134
10135 // style hists according to available styles ... creating if necessary
10136 dynamic_cast<TNamed *>(ll->At(i))->SetTitle(_title.c_str());
10137 addLegendEntry(ll->At(i), _title.c_str(), "f");
10138 }
10139 // finally, ensure all hists are styled
10140 for (auto ho : *ll) {
10141 TH1 *hh = dynamic_cast<TH1 *>(ho);
10142 if (!hh)
10143 continue;
10144 bool createdStyle = (xRooNode(*hh, *this).style(nullptr, false) == nullptr);
10145
10146 if (createdStyle) {
10147 // give hist a color, that isn't the same as any other hists color
10148 bool used = false;
10149 do {
10150 hh->SetFillColor((count++) % 100);
10151 // check not already used this color
10152 used = false;
10153 for (auto ho2 : *ll) {
10154 TH1 *hh2 = dynamic_cast<TH1 *>(ho2);
10155 if (!hh2)
10156 continue;
10157 auto _style = xRooNode(*hh2, *this).style(hh2, false);
10158 if (hh != hh2 && _style && _style->GetFillColor() == hh->GetFillColor()) {
10159 used = true;
10160 break;
10161 }
10162 }
10163 } while (used);
10164 }
10165
10166 auto _style = xRooNode(*hh, *this).style(hh);
10167 if (_style) {
10168 *dynamic_cast<TAttLine *>(hh) = *_style;
10169 *dynamic_cast<TAttFill *>(hh) = *_style;
10170 *dynamic_cast<TAttMarker *>(hh) = *_style;
10171 }
10172 }
10173 }
10174 }
10175 } else if (!overlayExisted) {
10176
10177 if (errHist) {
10178 addLegendEntry(errHist, strlen(errHist->GetTitle()) ? errHist->GetTitle() : GetName(), "fl");
10179 } else {
10180 addLegendEntry(h, strlen(h->GetTitle()) ? h->GetTitle() : GetName(), "l");
10181 }
10182 }
10183
10184 if (errHist) {
10185 dOpt.ReplaceAll("TEXT", "");
10186 errHist->Draw(dOpt + (dOpt.Contains("LF2") ? "e3same" : "e2same"));
10187 double ymax = -std::numeric_limits<double>::infinity();
10188 double ymin = std::numeric_limits<double>::infinity();
10189 for (int i = 1; i <= errHist->GetNbinsX(); i++) {
10190 ymax = std::max(ymax, errHist->GetBinContent(i) + errHist->GetBinError(i));
10191 ymin = std::min(ymin, errHist->GetBinContent(i) - errHist->GetBinError(i));
10192 }
10193 adjustYRange(ymin, ymax);
10194 } else {
10195 adjustYRange(h->GetMinimum() * 0.9, h->GetMaximum() * 1.1);
10196 }
10197
10198 if ((!auxPlotTitle.empty()) && !hasSame) {
10199 // create a pad for the ratio ... shift the bottom margin of this pad to make space for it
10200 double padFrac = 0.3;
10201 auto _tmpPad = gPad;
10202 gPad->SetBottomMargin(padFrac);
10203 auto ratioPad = new TPad("auxPad", "aux plot", 0, 0, 1, padFrac);
10204 ratioPad->SetFillColor(_tmpPad->GetFillColor());
10205 ratioPad->SetNumber(1);
10206 ratioPad->SetBottomMargin(ratioPad->GetBottomMargin() / padFrac);
10207 ratioPad->SetTopMargin(0.04);
10208 ratioPad->SetLeftMargin(gPad->GetLeftMargin());
10209 ratioPad->SetRightMargin(gPad->GetRightMargin());
10210 ratioPad->cd();
10211 TH1 *ratioHist = dynamic_cast<TH1 *>((errHist) ? errHist->Clone("auxHist") : h->Clone("auxHist"));
10212 ratioHist->SetDirectory(nullptr);
10213 ratioHist->SetTitle((errHist) ? errHist->GetName()
10214 : h->GetName()); // abuse the title string to hold the name of the main hist
10215
10216 ratioHist->GetYaxis()->SetNdivisions(5, 0, 0);
10217 ratioHist->GetYaxis()->SetTitle(auxPlotTitle.c_str());
10218 ratioHist->SetTitle(
10219 TString::Format("%s|%s", ratioHist->GetTitle(),
10220 auxPlotTitle.c_str())); // used when plotting data (above) to decide what to calculate
10221 ratioHist->SetMaximum();
10222 ratioHist->SetMinimum(); // resets min and max
10223 ratioPad->SetGridy();
10224
10225 for (int i = 1; i <= ratioHist->GetNbinsX(); i++) {
10226 double val = ratioHist->GetBinContent(i);
10227 double err = ratioHist->GetBinError(i);
10228 ratioHist->SetBinContent(i, std::get<0>(auxFunctions[auxPlotTitle])(val, val, err));
10229 ratioHist->SetBinError(i, std::get<0>(auxFunctions[auxPlotTitle])(val + err, val, err) -
10230 ratioHist->GetBinContent(i));
10231 }
10232
10233 double rHeight = 1. / padFrac; //(_tmpPad->GetWNDC())/(gPad->GetHNDC());
10234 if (ratioHist->GetYaxis()->GetTitleFont() % 10 == 2) {
10235 ratioHist->GetYaxis()->SetTitleSize(ratioHist->GetYaxis()->GetTitleSize() * rHeight);
10236 ratioHist->GetYaxis()->SetLabelSize(ratioHist->GetYaxis()->GetLabelSize() * rHeight);
10237 ratioHist->GetXaxis()->SetTitleSize(ratioHist->GetXaxis()->GetTitleSize() * rHeight);
10238 ratioHist->GetXaxis()->SetLabelSize(ratioHist->GetXaxis()->GetLabelSize() * rHeight);
10239 ratioHist->GetYaxis()->SetTitleOffset(ratioHist->GetYaxis()->GetTitleOffset() / rHeight);
10240 } else {
10241#if ROOT_VERSION_CODE < ROOT_VERSION(6, 26, 00)
10242 ratioHist->GetYaxis()->SetTitleOffset(ratioHist->GetYaxis()->GetTitleOffset() / rHeight);
10243#endif
10244 }
10245 ratioHist->GetXaxis()->SetTickLength(ratioHist->GetXaxis()->GetTickLength() * rHeight);
10246 ratioHist->SetStats(false);
10247 ratioHist->SetBit(TH1::kNoTitle);
10248 ratioHist->SetBit(kCanDelete);
10249 if (errHist) {
10250 auto _h = dynamic_cast<TH1 *>(ratioHist->Clone("auxHist_clone"));
10251 _h->SetDirectory(nullptr);
10252 _h->SetFillColor(0);
10253 ratioHist->GetListOfFunctions()->Add(_h, "histsame");
10254 //_h->Draw("histsame");
10255 }
10256 ratioHist->GetListOfFunctions()->Add(new TExec(
10257 ".updateAxis",
10258 TString::Format("auto h1 = (TH1*)%p; auto h2 = (TH1*)%p; if(h2->GetXaxis()->GetFirst() != "
10259 "h1->GetXaxis()->GetFirst() || h1->GetXaxis()->GetLast()!=h2->GetXaxis()->GetLast()) "
10260 "{h2->GetXaxis()->SetRange(h1->GetXaxis()->GetFirst(),h1->GetXaxis()->GetLast());if(gPad) "
10261 "{gPad->GetCanvas()->Paint();gPad->GetCanvas()->Update();}}",
10262 (void *)ratioHist, (void *)(h))));
10263 ratioHist->Draw((errHist ? "e2" : ""));
10264
10265 _tmpPad->cd();
10266 ratioPad->Draw();
10267 } else if (auto ratioPad = dynamic_cast<TPad *>(gPad->GetPrimitive("auxPad")); hasSame && ratioPad) {
10268 // need to draw histogram in the ratio pad ...
10269 // if doing overlay need to update histogram
10270
10271 if (auto hr = dynamic_cast<TH1 *>(ratioPad->GetPrimitive("auxHist"));
10272 hr && auxFunctions.find(hr->GetYaxis()->GetTitle()) != auxFunctions.end()) {
10273 TString histName = hr->GetTitle(); // split it by | char
10274 TString histType = histName(histName.Index('|') + 1, histName.Length());
10275 histName = histName(0, histName.Index('|'));
10276
10277 if (auto hnom = dynamic_cast<TH1 *>(gPad->GetPrimitive(histName)); hnom) {
10278 h = dynamic_cast<TH1 *>(h->Clone(h->GetName()));
10279 h->SetDirectory(nullptr);
10280 h->SetBit(kCanDelete);
10281 for (int i = 1; i <= hnom->GetNbinsX(); i++) {
10282 double val = h->GetBinContent(i);
10283 double err = h->GetBinError(i);
10284 h->SetBinContent(i, std::get<0>(auxFunctions[hr->GetYaxis()->GetTitle()])(
10285 h->GetBinContent(i), hnom->GetBinContent(i), hnom->GetBinError(i)));
10286 h->SetBinError(i, std::get<0>(auxFunctions[hr->GetYaxis()->GetTitle()])(
10287 val + err, hnom->GetBinContent(i), hnom->GetBinError(i)) -
10288 h->GetBinContent(i));
10289 }
10290 auto _tmpPad = gPad;
10291 ratioPad->cd();
10292 if (hasOverlay) {
10293 if (auto existing = dynamic_cast<TH1 *>(ratioPad->GetPrimitive(h->GetName())); existing) {
10294 existing->Reset();
10295 existing->Add(h);
10296 delete h;
10297 h = existing;
10298 overlayExisted = true;
10299 } else {
10300 h->Draw(dOpt);
10301 }
10302 } else {
10303 h->Draw(dOpt);
10304 }
10305 double ymax = -std::numeric_limits<double>::infinity();
10306 double ymin = std::numeric_limits<double>::infinity();
10307 for (int i = 1; i <= h->GetNbinsX(); i++) {
10308 ymax = std::max(ymax, h->GetBinContent(i) + h->GetBinError(i));
10309 ymin = std::min(ymin, h->GetBinContent(i) - h->GetBinError(i));
10310 }
10311 adjustYRange(ymin, ymax, hr, std::get<1>(auxFunctions[hr->GetYaxis()->GetTitle()]));
10312 // adjustYRange(h->GetMinimum() * (h->GetMinimum()<0 ? 1.1 : 0.9), h->GetMaximum() * (h->GetMinimum()<0 ?
10313 // 0.9 : 1.1), hr, std::get<1>(auxFunctions[hr->GetYaxis()->GetTitle()]));
10314 gPad->Modified();
10315 _tmpPad->cd();
10316 }
10317 }
10318 }
10319
10320 // see if it's in a simultaneous so need to select a cat
10321 /*auto _parent = fParent;
10322 auto _me = rar;
10323 while(_parent) {
10324 if (auto s = _parent->get<RooSimultaneous>(); s) {
10325 for (auto c : s->indexCat()) {
10326 if (auto p = s->getPdf(c.first.c_str());_me==p) {
10327 gPad->SetName(c.first.c_str());
10328 break;
10329 }
10330 }
10331 break;
10332 }
10333 _me = _parent->get<RooAbsReal>();
10334 _parent = _parent->fParent;
10335 }*/
10336
10337 // now draw selected datasets on top if this was a pdf
10338 if (auto _pdf = get<RooAbsPdf>();
10339 !hasSame && _pdf /*&& (_pdf->canBeExtended() || robs().empty())*/ && coefs().empty()) {
10340 auto _dsets = datasets();
10341 // bool _drawn=false;
10342 for (auto &d : _dsets) {
10343 if (d->get()->TestBit(1 << 20)) {
10344 d->Draw("same");
10345 //_drawn=true;
10346 }
10347 }
10348 // if (!_drawn && !_dsets.empty()) _dsets[0]->Draw("same"); // always draw if has a dataset
10349 }
10350
10351 gPad->Modified();
10352 // gPad->Update();
10353 getLegend();
10354 gPad->Modified();
10355 // gPad->Update();
10356}
10357
10358void xRooNode::SaveAs(const char *filename, Option_t *option) const
10359{
10360 TString sOpt(option);
10361 sOpt.ToLower();
10362 if (auto w = get<RooWorkspace>(); w) {
10363
10364 if (TString(filename).EndsWith(".json")) {
10365#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 26, 00)
10366 // stream with json tool
10367 RooJSONFactoryWSTool tool(*w);
10368 if (tool.exportJSON(filename)) {
10369 Info("SaveAs", "%s saved to %s", w->GetName(), filename);
10370 } else {
10371 Error("SaveAs", "Unable to save to %s", filename);
10372 }
10373#else
10374 Error("SaveAs", "json format workspaces only in ROOT 6.26 onwards");
10375#endif
10376 return;
10377 }
10378
10379#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
10380 // before saving, clear the eocache of all owned nodes
10381 // because causes memory leak when read back in (workspace streamer immediately overwrites the caches)
10382 // fixed in: https://github.com/root-project/root/pull/12024
10383 for (auto &c : w->components()) {
10384 c->_eocache = nullptr;
10385 }
10386#endif
10387 // const_cast<Node2*>(this)->sterilize(); - tried this to reduce mem leak on readback but no improve
10388 if (!w->writeToFile(filename, sOpt != "update")) {
10389 Info("SaveAs", "%s saved to %s", w->GetName(), filename);
10390 // save any fitDatabase that is loaded in memory too
10391 // TODO: We should do this as well for SaveAs on a scan object
10392 if (auto fitDb = dynamic_cast<TFile *>(gROOT->GetListOfFiles()->FindObject("fitDatabase"))) {
10393
10394 std::function<void(TDirectory *, TDirectory *)> CopyDir;
10395
10396 CopyDir = [&](TDirectory *source, TDirectory *dest) {
10397 auto dir = dest->GetDirectory(source->GetName());
10398 if (!dir) {
10399 dir = dest->mkdir(source->GetName());
10400 }
10401 for (auto k : *source->GetListOfKeys()) {
10402 auto key = dynamic_cast<TKey *>(k);
10403 const char *classname = key->GetClassName();
10404 TClass *cl = gROOT->GetClass(classname);
10405 // std::cout << "processing " << key->GetName() << " " << classname << std::endl;
10406 if (!cl) {
10407 continue;
10408 } else if (cl->InheritsFrom(TDirectory::Class())) {
10409 CopyDir(source->GetDirectory(key->GetName()), dir);
10410 } else {
10411 // don't write object if it already exists
10412 if (dir->FindKey(key->GetName()))
10413 continue;
10414 // support FitConfigs ....
10415 if (strcmp(classname, "ROOT::Fit::FitConfig") == 0) {
10416 auto fc = key->ReadObject<ROOT::Fit::FitConfig>();
10417 dir->WriteObject(fc, key->GetName());
10418 delete fc;
10419 } else {
10420 TObject *obj = key->ReadObj();
10421 if (obj) {
10422 dir->WriteTObject(obj, key->GetName());
10423 delete obj;
10424 }
10425 }
10426 }
10427 }
10428 };
10429 CopyDir(fitDb, std::make_unique<TFile>(filename, "UPDATE").get());
10430 Info("SaveAs", "Saved fitDatabase to %s", filename);
10431 }
10432
10433 } else {
10434 Error("SaveAs", "Unable to save to %s", filename);
10435 }
10436#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
10437 // restore the cache to every node
10438 for (auto &c : w->components()) {
10439 c->setExpensiveObjectCache(w->expensiveObjectCache());
10440 }
10441#endif
10442 }
10443}
10444
10445double xRooNode::GetBinError(int bin, const xRooNode &fr) const
10446{
10447 auto res = GetBinErrors(bin, bin, fr);
10448 if (res.empty())
10449 return std::numeric_limits<double>::quiet_NaN();
10450 return res.at(0);
10451}
10452
10453std::pair<double, double> xRooNode::IntegralAndError(const xRooNode &fr, const char *rangeName) const
10454{
10455 double out = 1.;
10456 double err = std::numeric_limits<double>::quiet_NaN();
10457
10458 std::unique_ptr<RooAbsCollection> _snap;
10459 RooArgList _pars;
10460 if (auto _fr = fr.get<RooFitResult>()) {
10461 _pars.add(pars().argList());
10462 _snap.reset(_pars.snapshot());
10463 _pars = _fr->floatParsFinal();
10464 _pars = _fr->constPars();
10465 }
10466
10467 auto _obs = obs();
10468 auto _coefs = coefs(); // need here to keep alive owned RooProduct
10469 if (auto c = _coefs.get<RooAbsReal>(); c) {
10470 out = c->getVal(*_obs.get<RooArgList>()); // assumes independent of observables!
10471 }
10472
10473 if (auto p = dynamic_cast<RooAbsPdf *>(get()); p) {
10474 // prefer to use expectedEvents for integrals of RooAbsPdf e.g. for RooProdPdf wont include constraint terms
10475 if (rangeName)
10476 p->setNormRange(rangeName);
10477#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 27, 00)
10479#endif
10480 out *= p->expectedEvents(*_obs.get<RooArgList>());
10481#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
10482 // improved normSet invalidity checking, so assuming no longer need this in 6.28 onwards
10483 p->_normSet = nullptr;
10484#endif
10485 err = GetBinError(-1, fr);
10486 if (rangeName)
10487 p->setNormRange(nullptr);
10488 } else if (auto p2 = dynamic_cast<RooAbsReal *>(get()); p2) {
10489 // only integrate over observables we actually depend on
10490 auto f = std::shared_ptr<RooAbsReal>(
10491 p2->createIntegral(*std::unique_ptr<RooArgSet>(p2->getObservables(*_obs.get<RooArgList>())),
10492 rangeName)); // did use x here before using obs
10493 RooProduct pr("int_x_coef", "int_x_coef",
10494 RooArgList(*f, !_coefs.get() ? RooFit::RooConst(1) : *_coefs.get<RooAbsReal>()));
10495 out *= f->getVal();
10496 err = xRooNode(pr, *this).GetBinError(-1, fr);
10497 sterilize(); // needed so that we can forget properly about the integral we just created (and are deleting)
10498 } else if (get<RooAbsData>()) {
10499 out = 0;
10500 auto vals = GetBinContents(1, 0); // returns all bins
10501 auto ax = (rangeName) ? GetXaxis() : nullptr;
10502 auto rv = (ax) ? dynamic_cast<RooRealVar *>(ax->GetParent()) : nullptr;
10503 auto cv = (ax && !rv) ? dynamic_cast<RooCategory *>(ax->GetParent()) : nullptr;
10504 int i = 0;
10505 for (auto &v : vals) {
10506 i++;
10507 if (rangeName) {
10508 if (rv && !rv->inRange(ax->GetBinCenter(i), rangeName))
10509 continue;
10510 if (cv && !cv->isStateInRange(rangeName, ax->GetBinLabel(i)))
10511 continue;
10512 }
10513 out += v;
10514 }
10515 err = 0; // should this be sqrt(sum(v^2)) or something similar
10516 } else {
10517 out = std::numeric_limits<double>::quiet_NaN();
10518 }
10519 if (_snap) {
10520 _pars.RooAbsCollection::operator=(*_snap);
10521 }
10522 return std::make_pair(out, err);
10523}
10524
10525std::vector<double> xRooNode::GetBinErrors(int binStart, int binEnd, const xRooNode &_fr) const
10526{
10527 // note: so far this method is inconsistent with the BuildHistogram in ways:
10528 // no projection over other variables
10529 // July2023: made RooRealSumPdf evaluate as a function if doesn't have a floor
10530 // but this method will still evaluate it as a pdf (uses PdfWrapper)
10531 // but can get away with it while added NaN recovery to getSimplePropagatedError to pickup raw values
10532
10533 if (fBinNumber != -1) {
10534 if (binStart != binEnd || !fParent) {
10535 throw std::runtime_error(TString::Format("%s is a bin - only has one value", GetName()));
10536 }
10537 return fParent->GetBinErrors(fBinNumber, fBinNumber);
10538 }
10539
10540 std::vector<double> out;
10541
10542 auto o = dynamic_cast<RooAbsReal *>(get());
10543 if (!o)
10544 return out;
10545
10546 std::shared_ptr<RooFitResult> fr = std::dynamic_pointer_cast<RooFitResult>(_fr.fComp);
10547 //= dynamic_cast<RooFitResult*>( _fr.get<RooFitResult>() ? _fr->Clone() : fitResult()->Clone());
10548
10549 auto _coefs = coefs();
10550
10551 if (!fr) {
10552 // need to ensure coefs, if any, are included in fit result retrieval so all pars are loaded
10553 auto frn = (!_coefs.get() ? *this : xRooNode(RooProduct("tmp", "tmp", RooArgList(*o, *_coefs.get<RooAbsReal>()))))
10554 .fitResult();
10555 if (strlen(_fr.GetName()))
10556 frn = frn.reduced(_fr.GetName());
10557
10558 // use name to reduce the fit result, if one given
10559 fr = std::dynamic_pointer_cast<RooFitResult>(frn.fComp);
10560 }
10561
10562 if (!GETDMP(fr.get(), _finalPars)) {
10563 fr->setFinalParList(RooArgList());
10564 }
10565
10566 /// Oct2022: No longer doing this because want to allow fitResult to be used to get partial error
10567 // // need to add any floating parameters not included somewhere already in the fit result ...
10568 // RooArgList l;
10569 // for(auto& p : pars()) {
10570 // auto v = p->get<RooRealVar>();
10571 // if (!v) continue;
10572 // if (v->isConstant()) continue;
10573 // if (fr->floatParsFinal().find(v->GetName())) continue;
10574 // if (fr->_constPars && fr->_constPars->find(v->GetName())) continue;
10575 // l.add(*v);
10576 // }
10577 //
10578 // if (!l.empty()) {
10579 // RooArgList l2; l2.addClone(fr->floatParsFinal());
10580 // l2.addClone(l);
10581 // fr->setFinalParList(l2);
10582 // }
10583
10584 TMatrixTSym<double> *prevCov = static_cast<TMatrixTSym<double> *>(GETDMP(fr.get(), _VM));
10585
10586 if (!prevCov || size_t(prevCov->GetNcols()) < fr->floatParsFinal().size()) {
10587 TMatrixDSym cov(fr->floatParsFinal().size());
10588 if (prevCov) {
10589 for (int i = 0; i < prevCov->GetNcols(); i++) {
10590 for (int j = 0; j < prevCov->GetNrows(); j++) {
10591 cov(i, j) = (*prevCov)(i, j);
10592 }
10593 }
10594 }
10595 int i = 0;
10596 for (auto &p : fr->floatParsFinal()) {
10597 if (!prevCov || i >= prevCov->GetNcols()) {
10598 cov(i, i) = pow(dynamic_cast<RooRealVar *>(p)->getError(), 2);
10599 }
10600 i++;
10601 }
10602 int covQualBackup = fr->covQual();
10603 fr->setCovarianceMatrix(cov);
10604 fr->setCovQual(covQualBackup);
10605 }
10606
10607 bool doBinWidth = false;
10608 auto ax = (binStart == -1 && binEnd == -1) ? nullptr : GetXaxis();
10609
10610 auto _obs = obs(); // may own an obs so keep alive here
10611 RooArgList normSet = _obs.argList();
10612 // to give consistency with BuildHistogram method, should be only the axis var if defined
10613 if (ax) {
10614 normSet.clear();
10615 normSet.add(*dynamic_cast<RooAbsArg *>(ax->GetParent()));
10616 }
10617
10618 if (auto p = dynamic_cast<RooAbsPdf *>(o); ax && (p || _coefs.get() || o->getAttribute("density"))) {
10619 // pdfs of samples embedded in a sumpdf (aka have a coef) will convert their density value to a content
10620 doBinWidth = true;
10621 }
10622 if (binEnd == 0) {
10623 if (ax) {
10624 binEnd = ax->GetNbins();
10625 } else {
10626 binEnd = binStart;
10627 }
10628 }
10629 for (int bin = binStart; bin <= binEnd; bin++) {
10630 if (ax)
10631 dynamic_cast<RooAbsLValue *>(ax->GetParent())->setBin(bin - 1, ax->GetName());
10632 // if (!SetBin(bin)) { return out; }
10633
10634 double res;
10635 if (auto p = dynamic_cast<RooAbsPdf *>(o); p) {
10636 // fr->covarianceMatrix().Print();
10637 res = PdfWrapper(*p, _coefs.get<RooAbsReal>(), !ax).getSimplePropagatedError(*fr, normSet);
10638#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
10639 // improved normSet invalidity checking, so assuming no longer need this in 6.28 onwards
10640 p->_normSet = nullptr;
10641#endif
10642 } else {
10643 // res = o->getPropagatedError(*fr, normSet);
10644 // // TODO: What if coef has error? - probably need a FuncWrapper class
10645 // if (auto c = _coefs.get<RooAbsReal>(); c) {
10646 // res *= c->getVal(normSet);
10647 // }
10648 res = RooProduct("errorEval", "errorEval",
10649 RooArgList(*o, !_coefs.get() ? RooFit::RooConst(1) : *_coefs.get<RooAbsReal>()))
10650 .getPropagatedError(*fr, normSet);
10651 }
10652 if (doBinWidth) {
10653 res *= ax->GetBinWidth(bin);
10654 }
10655 out.push_back(res);
10656 }
10657
10658 return out;
10659}
10660
10661
10662 std::string cling::printValue( const xRooNode *v ) {
10663 if(!v) return "nullptr\n";
10664 if(!v->empty()) {
10665 std::string out;
10666 size_t left = v->size();
10667 for(auto n : *v) {
10668 left--;
10669 if(!out.empty()) out += ","; else out += "{";
10670 out += n->GetName();
10671 if(out.length()>100 && left > 0) {
10672 out += TString::Format(",... and %lu more",left);
10673 break;
10674 }
10675 }
10676 out += "}\n";
10677 return out;
10678 }
10679 std::string out;
10680 if(!(*v)) {
10681 return "<empty node>";
10682 } else {
10683 return Form("Name: %s",v->GetName());
10684 }
10685
10686 return out;
10687}
10688
@ kButton1Down
Definition Buttons.h:17
int main()
Definition Prototype.cxx:12
#define d(i)
Definition RSha256.hxx:102
#define b(i)
Definition RSha256.hxx:100
#define f(i)
Definition RSha256.hxx:104
#define c(i)
Definition RSha256.hxx:101
#define g(i)
Definition RSha256.hxx:105
#define a(i)
Definition RSha256.hxx:99
#define h(i)
Definition RSha256.hxx:106
#define e(i)
Definition RSha256.hxx:103
#define ROOT_VERSION(a, b, c)
Definition RVersion.hxx:23
#define ROOT_VERSION_CODE
Definition RVersion.hxx:24
RooArgSet * _normSet
Pointer to set with observables used for normalization.
RooAbsData * _data
Pointer to original input dataset.
RooSetProxy _paramSet
Parameters of the test statistic (=parameters of the input function)
RooAbsReal * _func
Pointer to original input function.
virtual RooAbsTestStatistic * create(const char *name, const char *title, RooAbsReal &real, RooAbsData &data, const RooArgSet &projDeps, Configuration const &cfg)=0
size_t size(const MatrixT &matrix)
retrieve the size of a square matrix
const char Option_t
Definition RtypesCore.h:66
@ kGray
Definition Rtypes.h:65
@ kRed
Definition Rtypes.h:66
@ kBlack
Definition Rtypes.h:65
@ kGreen
Definition Rtypes.h:66
@ kWhite
Definition Rtypes.h:65
@ kCyan
Definition Rtypes.h:66
@ kBlue
Definition Rtypes.h:66
@ kAzure
Definition Rtypes.h:67
@ kYellow
Definition Rtypes.h:66
static void indent(ostringstream &buf, int indent_level)
R__EXTERN TEnv * gEnv
Definition TEnv.h:170
void Warning(const char *location, const char *msgfmt,...)
Use this function in warning situations.
Definition TError.cxx:229
#define gClient
Definition TGClient.h:156
@ kMBOk
Definition TGMsgBox.h:33
@ kMBIconExclamation
Definition TGMsgBox.h:24
winID h TVirtualViewer3D TVirtualGLPainter p
winID h TVirtualViewer3D vv
Option_t Option_t option
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t dest
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t winding char text const char depth char const char Int_t count const char ColorStruct_t color const char filename
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t winding char text const char depth char const char Int_t count const char ColorStruct_t color const char Pixmap_t Pixmap_t PictureAttributes_t attr const char char ret_data h unsigned char height h offset
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t np
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void data
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t r
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t 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:369
#define gROOT
Definition TROOT.h:406
char * Form(const char *fmt,...)
Formats a string in a circular formatting buffer.
Definition TString.cxx:2489
R__EXTERN TStyle * gStyle
Definition TStyle.h:433
R__EXTERN TSystem * gSystem
Definition TSystem.h:555
#define gPad
#define _(A, B)
Definition cfortran.h:108
double GetBinLowEdge(Int_t bin) const override
Return low edge of bin.
Definition xRooNode.cxx:845
double GetBinUpEdge(Int_t bin) const override
Return up edge of bin.
Definition xRooNode.cxx:853
Int_t FindFixBin(double x) const override
Find bin number corresponding to abscissa x.
Definition xRooNode.cxx:896
void Set(Int_t nbins, const double *xbins) override
Initialize axis with variable bins.
Definition xRooNode.cxx:873
RooAbsRealLValue * rvar() const
Definition xRooNode.cxx:900
void SetTitle(const char *title) override
Set the title of the TNamed.
Definition xRooNode.cxx:864
void Set(Int_t nbins, double xmin, double xmax) override
Initialize axis with fix bins.
Definition xRooNode.cxx:886
Int_t FindFixBin(const char *label) const override
Find bin number with label.
Definition xRooNode.cxx:895
RooAbsLValue * var() const
Definition xRooNode.cxx:899
const RooAbsBinning * binning() const
Definition xRooNode.cxx:893
void Set(Int_t nbins, const float *xbins) override
Initialize axis with variable bins.
Definition xRooNode.cxx:879
const char * GetTitle() const override
Returns title of object.
Definition xRooNode.cxx:860
double GetBinWidth(Int_t bin) const override
Return bin width.
Definition xRooNode.cxx:839
PadRefresher(TVirtualPad *p)
static int nExisting
A class which maps the current values of a RooRealVar (or a set of RooRealVars) to one of a number of...
bool isBinnedDistribution(const RooArgSet &obs) const override
Tests if the distribution is binned. Unless overridden by derived classes, this always returns false.
bool selfNormalized() const override
Shows if a PDF is self-normalized, which means that no attempt is made to add a normalization term.
~PdfWrapper() override
double getSimplePropagatedError(const RooFitResult &fr, const RooArgSet &nset_in) const
RooRealProxy fCoef
double evaluate() const override
Evaluate this PDF / function / constant. Needs to be overridden by all derived classes.
TObject * clone(const char *newname) const override
std::list< double > * binBoundaries(RooAbsRealLValue &obs, double xlo, double xhi) const override
Retrieve bin boundaries if this distribution is binned in obs.
PdfWrapper(const PdfWrapper &other, const char *name=nullptr)
RooRealProxy fExpPdf
PdfWrapper(RooAbsReal &f, RooAbsReal *coef, bool expEvMode=false, RooAbsPdf *expPdf=nullptr)
RooRealProxy fFunc
The PiecewiseInterpolation is a class that can morph distributions into each other,...
static std::shared_ptr< RooLinkedList > createNLLOptions()
Definition xRooFit.cxx:442
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:1863
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:474
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:824
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:903
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:550
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:77
bool dependsOn(const RooAbsCollection &serverList, const RooAbsArg *ignoreArg=nullptr, bool valueOnly=false) const
Test whether we depend on (ie, are served by) any object in the specified collection.
void setStringAttribute(const Text_t *key, const Text_t *value)
Associate string 'value' to this object under key 'key'.
RooFit::OwningPtr< RooArgSet > getParameters(const RooAbsData *data, bool stripDisconnected=true) const
Create a list of leaf nodes in the arg tree starting with ourself as top node that don't match any of...
RooFit::OwningPtr< RooArgSet > getObservables(const RooArgSet &set, bool valueOnly=true) const
Given a set of possible observables, return the observables that this PDF depends on.
void SetName(const char *name) override
Set the name of the TNamed.
const Text_t * getStringAttribute(const Text_t *key) const
Get string attribute mapped under key 'key'.
bool getAttribute(const Text_t *name) const
Check if a named attribute is set. By default, all attributes are unset.
void setAttribute(const Text_t *name, bool value=true)
Set (default) or clear a named boolean attribute of this object.
virtual bool isFundamental() const
Is this object a fundamental type that can be added to a dataset? Fundamental-type subclasses overrid...
Definition RooAbsArg.h:249
Abstract base class for RooRealVar binning definitions.
virtual double highBound() const =0
virtual double lowBound() const =0
Abstract base class for objects that represent a discrete value that can be set from the outside,...
A space to attach TBranches.
const char * getLabel() const
Retrieve current label. Use getCurrentLabel() for more clarity.
Abstract container object that can hold multiple RooAbsArg objects.
RooAbsCollection * selectByAttrib(const char *name, bool value) const
Create a subset of the current collection, consisting only of those elements with the specified attri...
virtual void removeAll()
Remove all arguments from our set, deleting them if we own them.
virtual bool remove(const RooAbsArg &var, bool silent=false, bool matchByNameOnly=false)
Remove the specified argument from our list.
RooAbsCollection * snapshot(bool deepCopy=true) const
Take a snap shot of current collection contents.
virtual bool add(const RooAbsArg &var, bool silent=false)
Add the specified argument to list.
Storage_t::size_type size() const
void clear()
Clear contents. If the collection is owning, it will also delete the contents.
virtual RooAbsArg * addClone(const RooAbsArg &var, bool silent=false)
Add a clone of the specified argument to list.
bool selectCommon(const RooAbsCollection &refColl, RooAbsCollection &outColl) const
Create a subset of the current collection, consisting only of those elements that are contained as we...
void setName(const char *name)
RooAbsArg * find(const char *name) const
Find object with given name in list.
Abstract base class for binned and unbinned datasets.
Definition RooAbsData.h:57
virtual const RooArgSet * get() const
Definition RooAbsData.h:101
virtual void reset()
RooFit::OwningPtr< RooAbsData > reduce(const RooCmdArg &arg1, const RooCmdArg &arg2={}, const RooCmdArg &arg3={}, const RooCmdArg &arg4={}, const RooCmdArg &arg5={}, const RooCmdArg &arg6={}, const RooCmdArg &arg7={}, const RooCmdArg &arg8={})
Create a reduced copy of this dataset.
virtual Int_t numEntries() const
Return number of entries in dataset, i.e., count unweighted entries.
virtual void add(const RooArgSet &row, double weight=1)=0
Abstract base class for objects that are lvalues, i.e.
virtual std::list< std::string > getBinningNames() const =0
Abstract interface for all probability density functions.
Definition RooAbsPdf.h:40
bool canBeExtended() const
If true, PDF can provide extended likelihood term.
Definition RooAbsPdf.h:219
Abstract base class for objects that represent a real value that may appear on the left hand side of ...
void setBin(Int_t ibin, const char *rangeName=nullptr) override
Set value to center of bin 'ibin' of binning 'rangeName' (or of default binning if no range is specif...
bool hasRange(const char *name) const override
Check if variable has a binning with given name.
Context to temporarily change the error logging mode as long as the context is alive.
Definition RooAbsReal.h:324
Abstract base class for objects that represent a real value and implements functionality common to al...
Definition RooAbsReal.h:59
bool isSelectedComp() const
If true, the current pdf is a selected component (for use in plotting)
double getVal(const RooArgSet *normalisationSet=nullptr) const
Evaluate object.
Definition RooAbsReal.h:103
virtual void forceNumInt(bool flag=true)
Definition RooAbsReal.h:169
Efficient implementation of a sum of PDFs of the form.
Definition RooAddPdf.h:33
Calculates the sum of a set of RooAbsReal terms, or when constructed with two sets,...
Definition RooAddition.h:27
RooArgList is a container object that can hold multiple RooAbsArg objects.
Definition RooArgList.h:22
RooAbsArg * at(Int_t idx) const
Return object at given index, or nullptr if index is out of range.
Definition RooArgList.h:110
Abstract interface for RooAbsArg proxy classes.
Definition RooArgProxy.h:24
RooArgSet is a container object that can hold multiple RooAbsArg objects.
Definition RooArgSet.h:55
RooArgSet * snapshot(bool deepCopy=true) const
Use RooAbsCollection::snapshot(), but return as RooArgSet.
Definition RooArgSet.h:178
Implements a RooAbsBinning in terms of an array of boundary values, posing no constraints on the choi...
Definition RooBinning.h:27
Object to represent discrete states.
Definition RooCategory.h:28
bool defineType(const std::string &label)
Define a state with given name.
static TClass * Class()
Named container for two doubles, two integers two object points and three string pointers that can be...
Definition RooCmdArg.h:26
static const RooCmdArg & none()
Return reference to null argument.
Definition RooCmdArg.cxx:48
Represents a constant real-valued object.
Definition RooConstVar.h:23
static TClass * Class()
Container class to hold N-dimensional binned data.
Definition RooDataHist.h:39
Container class to hold unbinned data.
Definition RooDataSet.h:57
RooFitResult is a container class to hold the input and output of a PDF fit to a dataset.
void setCovQual(Int_t val)
const TMatrixDSym & covarianceMatrix() const
Return covariance matrix.
TMatrixDSym reducedCovarianceMatrix(const RooArgList &params) const
Return a reduced covariance matrix (Note that Vred is a simple sub-matrix of V, row/columns are order...
void setCovarianceMatrix(TMatrixDSym &V)
Store externally provided correlation matrix in this RooFitResult ;.
const RooArgList & constPars() const
Return list of constant parameters.
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
Implementation of a probability density function that takes a RooArgList of servers and a C++ express...
A real-valued function sampled from a multidimensional histogram.
Definition RooHistFunc.h:31
bool isBinnedDistribution(const RooArgSet &) const override
Tests if the distribution is binned. Unless overridden by derived classes, this always returns false.
Definition RooHistFunc.h:95
When using RooFit, statistical models can be conveniently handled and stored as a RooWorkspace.
bool importJSON(std::string const &filename)
Imports a JSON file from the given filename to the workspace.
bool exportJSON(std::string const &fileName)
Export the workspace to JSON format and write to the specified file.
Collection class for internal use, storing a collection of RooAbsArg pointers in a doubly linked list...
Int_t GetSize() const
TObject * At(int index) const
Return object stored in sequential position given by index.
TIterator * MakeIterator(bool forward=true) const
Create a TIterator for this list.
static RooMsgService & instance()
Return reference to singleton instance.
StreamConfig & getStream(Int_t id)
void setGlobalKillBelow(RooFit::MsgLevel level)
RooFit::MsgLevel globalKillBelow() const
Implementation of a RooCacheManager<RooAbsCacheElement> that specializes in the storage of cache elem...
Poisson pdf.
Definition RooPoisson.h:19
Efficient implementation of a product of PDFs of the form.
Definition RooProdPdf.h:33
Represents the product of a given set of RooAbsReal objects.
Definition RooProduct.h:29
static TClass * Class()
A RooAbsPdf implementation that represent a projection of a given input p.d.f and the object returned...
RooProjectedPdf()
Default constructor.
Implements a PDF constructed from a sum of functions:
Variable that can be changed from the outside.
Definition RooRealVar.h:37
void setVal(double value) override
Set value of variable to 'value'.
void setError(double value)
Definition RooRealVar.h:60
static TClass * Class()
double getError() const
Definition RooRealVar.h:58
bool hasError(bool allowZero=true) const
Definition RooRealVar.h:59
Facilitates simultaneous fitting of multiple PDFs to subsets of a given dataset.
HypoTestResult is a base class for results from hypothesis tests.
A RooAbsArg implementing string values.
Implementation of RooAbsBinning that provides a uniform binning in 'n' bins between the range end poi...
Persistable container for RooFit projects.
const std::map< std::string, RooArgSet > & sets() const
bool import(const RooAbsArg &arg, const RooCmdArg &arg1={}, const RooCmdArg &arg2={}, const RooCmdArg &arg3={}, const RooCmdArg &arg4={}, const RooCmdArg &arg5={}, const RooCmdArg &arg6={}, const RooCmdArg &arg7={}, const RooCmdArg &arg8={}, const RooCmdArg &arg9={})
Import a RooAbsArg object, e.g.
bool removeSet(const char *name)
Remove a named set from the workspace.
static TClass * Class()
RooFactoryWSTool & factory()
Return instance to factory tool.
const Double_t * GetArray() const
Definition TArrayD.h:43
virtual void SetTitleOffset(Float_t offset=1)
Set distance between the axis and the axis title.
Definition TAttAxis.cxx:298
virtual Style_t GetTitleFont() const
Definition TAttAxis.h:47
virtual 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:1455
static Int_t GetNumberOfColors()
Static function returning number of colors in the color palette.
Definition TColor.cxx:1475
Describe directory structure in memory.
Definition TDirectory.h:45
static TClass * Class()
virtual TDirectory * GetDirectory(const char *namecycle, Bool_t printError=false, const char *funcname="GetDirectory")
Find a directory using apath.
virtual TList * GetListOfKeys() const
Definition TDirectory.h:223
virtual void SetValue(const char *name, const char *value, EEnvLevel level=kEnvChange, const char *type=nullptr)
Set the value of a resource or create a new resource.
Definition TEnv.cxx:736
TExec is a utility class that can be used to execute a C++ command when some event happens in a pad.
Definition TExec.h:26
A ROOT file is an on-disk file, usually with extension .root, that stores objects in a file-system-li...
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:98
virtual void SetPoint(Int_t i, Double_t x, Double_t y)
Set x and y values for point number i.
Definition TGraph.cxx:2319
Int_t GetN() const
Definition TGraph.h:131
void SetName(const char *name="") override
Set graph name.
Definition TGraph.cxx:2358
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:1533
void SetTitle(const char *title="") override
Change (i.e.
Definition TGraph.cxx:2374
virtual void SetPointX(Int_t i, Double_t x)
Set x value for point i.
Definition TGraph.cxx:2343
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:2278
1-D histogram with a double per channel (see TH1 documentation)
Definition TH1.h:669
1-D histogram with a float per channel (see TH1 documentation)
Definition TH1.h:621
TH1 is the base class of all histogram classes in ROOT.
Definition TH1.h:59
virtual void SetDirectory(TDirectory *dir)
By default, when a histogram is created, it is added to the list of histogram objects in the current ...
Definition TH1.cxx:8905
virtual Double_t GetBinCenter(Int_t bin) const
Return bin center for 1D histogram.
Definition TH1.cxx:9109
void SetTitle(const char *title) override
Change/set the title.
Definition TH1.cxx:6686
virtual Int_t GetNbinsY() const
Definition TH1.h:298
virtual Double_t GetBinError(Int_t bin) const
Return value of error associated to bin number bin.
Definition TH1.cxx:9031
static void AddDirectory(Bool_t add=kTRUE)
Sets the flag controlling the automatic add of histograms in memory.
Definition TH1.cxx:1294
@ kNoTitle
Don't draw the histogram title.
Definition TH1.h:170
virtual void Reset(Option_t *option="")
Reset this histogram: contents, errors, etc.
Definition TH1.cxx:7071
TAxis * GetXaxis()
Definition TH1.h:324
TObject * FindObject(const char *name) const override
Search object named name in the list of functions.
Definition TH1.cxx:3857
virtual Int_t GetNbinsX() const
Definition TH1.h:297
virtual void SetMaximum(Double_t maximum=-1111)
Definition TH1.h:403
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:9174
virtual Int_t Fill(Double_t x)
Increment bin with abscissa X by 1.
Definition TH1.cxx:3344
TAxis * GetYaxis()
Definition TH1.h:325
void Draw(Option_t *option="") override
Draw this histogram with options.
Definition TH1.cxx:3066
virtual void SetMinimum(Double_t minimum=-1111)
Definition TH1.h:404
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:9190
TList * GetListOfFunctions() const
Definition TH1.h:244
void SetName(const char *name) override
Change the name of this histogram.
Definition TH1.cxx:8928
virtual Double_t GetBinContent(Int_t bin) const
Return content of bin number bin.
Definition TH1.cxx:5029
virtual void Scale(Double_t c1=1, Option_t *option="")
Multiply this histogram by a constant c1.
Definition TH1.cxx:6572
TObject * Clone(const char *newname="") const override
Make a complete copy of the underlying object.
Definition TH1.cxx:2752
virtual Bool_t Divide(TF1 *f1, Double_t c1=1)
Performs the operation: this = this/(c1*f1) if errors are defined (see TH1::Sumw2),...
Definition TH1.cxx:2840
virtual void Sumw2(Bool_t flag=kTRUE)
Create structure to store sum of squares of weights.
Definition TH1.cxx:8988
static Bool_t AddDirectoryStatus()
Static function: cannot be inlined on Windows/NT.
Definition TH1.cxx:754
virtual void SetStats(Bool_t stats=kTRUE)
Set statistics option on/off.
Definition TH1.cxx:8958
2-D histogram with a double per channel (see TH1 documentation)
Definition TH2.h:357
The Histogram stack class.
Definition THStack.h:40
TList * GetHists() const
Definition THStack.h:72
virtual void Add(TH1 *h, Option_t *option="")
add a new histogram to the list Only 1-d and 2-d histograms currently supported.
Definition THStack.cxx:365
void Draw(Option_t *chopt="") override
Draw this multihist with its current attributes.
Definition THStack.cxx:450
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:355
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:201
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
A Pave (see TPave) with text, lines or/and boxes inside.
Definition TPaveText.h:21
virtual TText * AddText(Double_t x1, Double_t y1, const char *label)
Add a new Text line to this pavetext at given coordinates.
virtual void SetMargin(Float_t margin=0.05)
Definition TPaveText.h:62
virtual void SetY1NDC(Double_t y1)
Definition TPave.h:84
virtual void ConvertNDCtoPad()
Convert pave coordinates from NDC to Pad coordinates.
Definition TPave.cxx:139
virtual void SetName(const char *name="")
Definition TPave.h:79
virtual void SetBorderSize(Int_t bordersize=4)
Sets the border size of the TPave box and shadow.
Definition TPave.h:77
virtual void SetY2NDC(Double_t y2)
Definition TPave.h:85
Draw a Pie Chart,.
Definition TPie.h:23
void SetEntryVal(Int_t, Double_t)
Set the value of a slice.
Definition TPie.cxx:1250
Int_t GetEntries()
Definition TPie.h:76
void SetEntryFillColor(Int_t, Int_t)
Set the color for the slice "i".
Definition TPie.cxx:1226
void SetRadius(Double_t)
Set the pie chart's radius' value.
Definition TPie.cxx:1341
void SetEntryLabel(Int_t, const char *text="Slice")
Set slice number "i" label.
Definition TPie.cxx:1193
void Draw(Option_t *option="l") override
Draw the pie chart.
Definition TPie.cxx:277
This is the ROOT implementation of the Qt object communication mechanism (see also http://www....
Definition TQObject.h:48
Regular expression class.
Definition TRegexp.h:31
This class creates a ROOT object browser, constituted by three main tabs.
Stopwatch class.
Definition TStopwatch.h:28
Double_t RealTime()
Stop the stopwatch (if it is running) and return the realtime (in seconds) passed between the start a...
void Start(Bool_t reset=kTRUE)
Start the stopwatch.
void Stop()
Stop the stopwatch.
Provides iteration through tokens of a given string.
Definition TPRegexp.h:143
Bool_t NextToken()
Get the next token, it is stored in this TString.
Definition TPRegexp.cxx:975
Basic string class.
Definition TString.h:139
Ssiz_t Length() const
Definition TString.h:417
Bool_t IsDec() const
Returns true if all characters in string are decimal digits (0-9).
Definition TString.cxx:1940
void ToLower()
Change string to lower-case.
Definition TString.cxx:1182
Int_t Atoi() const
Return integer value of string.
Definition TString.cxx:1988
Bool_t EndsWith(const char *pat, ECaseCompare cmp=kExact) const
Return true if string ends with the specified string.
Definition TString.cxx:2244
Double_t Atof() const
Return floating-point value contained in string.
Definition TString.cxx:2054
TString & Replace(Ssiz_t pos, Ssiz_t n, const char *s)
Definition TString.h:694
const char * Data() const
Definition TString.h:376
TString & ReplaceAll(const TString &s1, const TString &s2)
Definition TString.h:704
Bool_t BeginsWith(const char *s, ECaseCompare cmp=kExact) const
Definition TString.h:623
Int_t CountChar(Int_t c) const
Return number of times character c occurs in the string.
Definition TString.cxx:515
static TString Format(const char *fmt,...)
Static method which formats a string using a printf style format descriptor and return a TString.
Definition TString.cxx:2378
Bool_t Contains(const char *pat, ECaseCompare cmp=kExact) const
Definition TString.h:632
Ssiz_t Index(const char *pat, Ssiz_t i=0, ECaseCompare cmp=kExact) const
Definition TString.h:651
TStyle objects may be created to define special styles.
Definition TStyle.h:29
void SetPaintTextFormat(const char *format="g")
Definition TStyle.h: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:1274
virtual Bool_t AccessPathName(const char *path, EAccessMode mode=kFileExists)
Returns FALSE if one can access a file using the specified access mode.
Definition TSystem.cxx:1296
virtual Bool_t ProcessEvents()
Process pending events (GUI, timers, sockets).
Definition TSystem.cxx:416
The TTimeStamp encapsulates seconds and ns since EPOCH.
Definition TTimeStamp.h:45
void Add(const TTimeStamp &offset)
Add "offset" as a delta time.
const char * AsString(const Option_t *option="") const
Return the date & time as a string.
This class defines a UUID (Universally Unique IDentifier), also known as GUIDs (Globally Unique IDent...
Definition TUUID.h:42
const char * AsString() const
Return UUID as string. Copy string immediately since it will be reused.
Definition TUUID.cxx:571
static TVirtualPadEditor * GetPadEditor(Bool_t load=kTRUE)
Returns the pad editor dialog. Static method.
TVirtualPad is an abstract base class for the Pad and Canvas classes.
Definition TVirtualPad.h:51
virtual void Modified(Bool_t flag=1)=0
virtual TList * GetListOfPrimitives() const =0
virtual void SetPad(const char *name, const char *title, Double_t xlow, Double_t ylow, Double_t xup, Double_t yup, Color_t color=35, Short_t bordersize=5, Short_t bordermode=-1)=0
virtual TVirtualPad * cd(Int_t subpadnumber=0)=0
const char * GetName() const override=0
Returns name of object.
virtual TVirtualPad * GetPad(Int_t subpadnumber) const =0
virtual void Divide(Int_t nx=1, Int_t ny=1, Float_t xmargin=0.01, Float_t ymargin=0.01, Int_t color=0)=0
virtual Int_t GetLogy() const =0
virtual void SetLogy(Int_t value=1)=0
virtual TObject * GetPrimitive(const char *name) const =0
virtual void SetBorderSize(Short_t bordersize)=0
virtual void SetName(const char *name)=0
TClass * IsA() const override
double evaluate() const override
Evaluate projected p.d.f.
TObject * clone(const char *newname) const override
double expectedEvents(const RooArgSet *nset) const override
Return expected number of events to be used in calculation of extended likelihood.
ExtendMode extendMode() const override
Returns ability of PDF to provide extended likelihood terms.
RooCmdArg RecycleConflictNodes(bool flag=true)
RooConstVar & RooConst(double val)
RooCmdArg Embedded(bool flag=true)
RooCmdArg WeightVar(const char *name="weight", bool reinterpretAsWeight=false)
RooCmdArg GlobalObservables(Args_t &&... argsOrArgSet)
RooCmdArg Range(const char *rangeName, bool adjustNorm=true)
const Double_t sigma
Double_t y[n]
Definition legend1.C:17
Double_t x[n]
Definition legend1.C:17
const Int_t n
Definition legend1.C:16
TGraphErrors * gr
Definition legend1.C:25
TH1F * h1
Definition legend1.C:5
#define F(x, y, z)
bool EndsWith(const std::string &theString, const std::string &theSubstring)
TClass * GetClass(T *)
Definition TClass.h:659
The namespace RooFit contains mostly switches that change the behaviour of functions of PDFs (or othe...
Definition JSONIO.h:26
MsgLevel
Verbosity level for RooMsgService::StreamConfig in RooMsgService.
@ NumIntegration
Double_t Prob(Double_t chi2, Int_t ndf)
Computation of the probability for a certain Chi-squared (chi2) and number of degrees of freedom (ndf...
Definition TMath.cxx:637
Double_t ChisquareQuantile(Double_t p, Double_t ndf)
Evaluate the quantiles of the chi-squared probability distribution function.
Definition TMath.cxx:2193
Definition graph.py:1
#define END_XROOFIT_NAMESPACE
Definition Config.h:25
static const char * what
Definition stlLoader.cc:5
RooAbsBinning * b
RooRealVar * x
void removeTopic(RooFit::MsgTopic oldTopic)
bool isNaNWithPayload() const
Test if this struct has a float packed into its mantissa.
static float unpackNaN(double val)
If val is NaN and a this NaN has been tagged as containing a payload, unpack the float from the manti...
th1 Draw()
TLine lv
Definition textalign.C:5
TLine l
Definition textangle.C:4
auto * tt
Definition textangle.C:16
static uint64_t sum(uint64_t i)
Definition Factory.cxx:2345
#define GETWS(a)
#define GETWSSETS(w)
void(* gOldHandlerr)(int)
void addLegendEntry(TObject *o, const char *title, const char *opt)
void buildHistogramInterrupt(int signum)
auto GETACTBROWSER(TRootBrowser *b)
Definition xRooNode.cxx: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)