ndmspc  v1.1.1-1
NGnNavigator.cxx
1 #include <cstddef>
2 #include <string>
3 #include <vector>
4 #include <iostream>
5 #include "TAxis.h"
6 #include "THnSparse.h"
7 #include <TSystem.h>
8 #include <TMath.h>
9 #include <TROOT.h>
10 #include <TVirtualPad.h>
11 #include <TPad.h>
12 #include <TCanvas.h>
13 #include <TLatex.h>
14 #include <TH1.h>
15 #include <THStack.h>
16 #include <TBufferJSON.h>
17 #include <TGClient.h>
18 #include <TPaveText.h>
19 
20 #include "Buttons.h"
21 #include "NBinningDef.h"
22 #include "NDimensionalExecutor.h"
23 #include "NGnTree.h"
24 #include "NLogger.h"
25 #include "NParameters.h"
26 #include "NUtils.h"
27 #include "NWsClient.h"
28 
29 #include "NGnNavigator.h"
30 
32 ClassImp(Ndmspc::NGnNavigator);
34 
35 namespace Ndmspc {
36 NGnNavigator::NGnNavigator(const char * name, const char * title, std::vector<std::string> objectTypes)
37  : TNamed(name, title), fObjectTypes(objectTypes)
38 {
39  // Force ROOT to fully initialize THStack's TClass runtime properties now,
40  // while still in the main thread. Without this, the first THStack constructed
41  // inside an HTTP handler triggers TClass::SetRuntimeProperties() lazily, which
42  // creates a temporary TObject whose ~TObject() calls TROOT::RecursiveRemove and
43  // cascades into a crash on the live TTree.
44  static bool sThStackWarmedUp = false;
45  if (!sThStackWarmedUp) {
46  THStack _warmup("_ndmspc_warmup_stack", "");
47  (void)_warmup;
48  sThStackWarmedUp = true;
49  }
50 }
52 {
53  if (fProjection) {
54  fProjection->SetDirectory(nullptr);
55  delete fProjection;
56  fProjection = nullptr;
57  }
58  // Recursively delete the entire child-navigator subtree.
59  for (auto * child : fChildren) {
60  delete child;
61  }
62  fChildren.clear();
63  // Delete histograms owned by this node (cloned in Reshape).
64  for (auto & [key, vec] : fObjectContentMap) {
65  for (TObject * obj : vec) {
66  delete obj;
67  }
68  }
69 }
70 
71 NGnNavigator * NGnNavigator::Reshape(std::string binningName, std::vector<std::vector<int>> levels, size_t level,
72  std::map<int, std::vector<int>> ranges, std::map<int, std::vector<int>> rangesBase)
73 {
77  NBinningDef * binningDef = fGnTree->GetBinning()->GetDefinition(binningName);
78  if (!binningDef) {
79  NLogError("NGnNavigator::Reshape: Binning definition is null !!!");
80  return nullptr;
81  }
82  std::vector<int> axes;
83  size_t nAxes = 0;
84  if (levels.empty()) {
85 
86  size_t nVarAxes = binningDef->GetVariableAxes().size();
87  if (nVarAxes == 0) {
88  NLogError("NGnNavigator::Reshape: Binning definition has no variable axes !!!");
89  return nullptr;
90  }
91  if (nVarAxes > 3) {
92  NLogError("NGnNavigator::Reshape: Binning definition has more than 3 variable axes (%zu) !!!", nVarAxes);
93  return nullptr;
94  }
95 
96  NLogTrace("========== NGnNavigator::Reshape: Levels are empty, using all variable axes...");
97  levels.resize(1);
98  for (size_t i = 0; i < nVarAxes; i++) {
99  levels[0].push_back(i);
100  }
101  // set levels[0] in reverse order
102  for (size_t i = 0; i < nVarAxes / 2; i++) {
103  std::swap(levels[0][i], levels[0][nVarAxes - i - 1]);
104  }
105  }
106 
107  for (auto & l : levels) {
108  nAxes += l.size();
109  for (auto & a : l) {
110  if (std::find(axes.begin(), axes.end(), a) == axes.end()) {
111  axes.push_back(a);
112  }
113  }
114  }
115  NLogTrace("============= NGnNavigator::Reshape: Number of axes in levels = %s",
116  NUtils::GetCoordsString(axes, -1).c_str());
117  std::vector<int> axesSorted = axes;
118  std::sort(axesSorted.begin(), axesSorted.end());
119  std::vector<int> axesVariavble = binningDef->GetVariableAxes();
120  std::sort(axesVariavble.begin(), axesVariavble.end());
121 
122  NLogTrace("NGnNavigator::Reshape: Axes in levels before removing duplicates: %s",
123  NUtils::GetCoordsString(axesSorted, -1).c_str());
124 
125  // remove all duplicates from axesSorted
126  for (size_t i = 1; i < axesSorted.size(); i++) {
127  if (axesSorted[i] == axesSorted[i - 1]) {
128  axesSorted.erase(axesSorted.begin() + i);
129  i--;
130  }
131  }
132 
133  if (axesSorted != axesVariavble) {
134  NLogError("NGnNavigator::Reshape: Axes in levels '%s' do not match variable axes in binning definition '%s' !!!",
135  NUtils::GetCoordsString(axesSorted, -1).c_str(), NUtils::GetCoordsString(axesVariavble, -1).c_str());
136  return nullptr;
137  }
138 
139  // NLogDebug("NGnNavigator::Reshape: Number of axes in levels = %d GetVariableAxes=%zu", nAxes,
140  // binningDef->GetVariableAxes().size());
141  // return nullptr;
142 
143  if (nAxes != binningDef->GetVariableAxes().size()) {
144  NLogError("NGnNavigator::Reshape: Number of axes in levels (%d) does not match number of axes in binning "
145  "definition (%d) !!! Available axes indices: %s",
146  nAxes, binningDef->GetVariableAxes().size(),
147  NUtils::GetCoordsString(binningDef->GetVariableAxes(), -1).c_str());
148 
149  return nullptr;
150  }
151 
152  NLogInfo("NGnNavigator::Reshape: Reshaping navigator for level %d/%zu with binning '%s' ...", level, levels.size(),
153  binningName.c_str());
154  return Reshape(binningDef, levels, level, ranges, rangesBase);
155 }
156 NGnNavigator * NGnNavigator::Reshape(NBinningDef * binningDef, std::vector<std::vector<int>> levels, size_t level,
157  std::map<int, std::vector<int>> ranges, std::map<int, std::vector<int>> rangesBase,
158  NGnNavigator * parent)
159 {
163 
164  NLogTrace("NGnNavigator::Reshape: Reshaping navigator for level=%d levels=%zu", level, levels.size());
165  TH1::AddDirectory(kFALSE);
166  NTreeBranch * branch = fGnTree->GetStorageTree()->GetBranch("_outputPoint");
167  if (!branch) {
168  // fallback to old branch name for backward compatibility
169  branch = fGnTree->GetStorageTree()->GetBranch("outputPoint");
170  }
171  int outputPointStatus = 0;
172  if (branch) {
173  outputPointStatus = branch->GetBranchStatus();
174  branch->SetBranchStatus(0); // Disable the _outputPoint branch to avoid memory issues with large trees
175  }
176  fNLevels = levels.size();
177  fLevel = level;
178  // NBinningDef * binningDef = fGnTree->GetBinning()->GetDefinition(binningName);
179 
180  NGnNavigator * current = parent;
181  if (current == nullptr) {
182  NLogDebug("NGnNavigator::Reshape: Creating root navigator %d/%zu...", level, levels.size());
183  current = new NGnNavigator(TString::Format("%s_L%zu", GetName(), level).Data(),
184  TString::Format("%s_L%zu", GetTitle(), level).Data());
185  // current->SetParent(this);
186  current->SetLevel(fLevel);
187  current->SetNLevels(fNLevels);
188  current->SetLevels(levels);
189  current->SetGnTree(fGnTree);
190  }
191  // current->Print();
192  // return current;
193 
194  if (level < levels.size()) {
195  NLogTrace("NGnNavigator::Reshape: levels[%d]=%s...", level, NUtils::GetCoordsString(levels[level]).c_str());
196 
197  // Generate projection histogram
198 
199  // loop for every bin in the current level
200 
201  std::vector<int> minsBin;
202  std::vector<int> maxsBin;
203  for (auto & idx : levels[level]) {
204  NLogTrace("NGnNavigator::Reshape: [B%d] Axis %d: %s", level, idx,
205  binningDef->GetContent()->GetAxis(idx)->GetName());
206  // int minBase = 0, maxBase = 0;
207  // NUtils::GetAxisRangeInBase(GetAxis(idx), 1, GetAxis(idx)->GetNbins(), fBinning->GetAxes()[idx], minBase,
208  // maxBase); ranges[idx] = {minBase, maxBase}; // Set the ranges for the axis
209  minsBin.push_back(1); // Get the minimum bin edge);
210  maxsBin.push_back(binningDef->GetContent()->GetAxis(idx)->GetNbins()); // Get the maximum bin edge);
211  NLogTrace("NGnNavigator::Reshape: [B%d] Axis %d: %s bins=[%d,%d]", level, idx,
212  binningDef->GetContent()->GetAxis(idx)->GetName(), minsBin.back(), maxsBin.back());
213  }
214 
215  NDimensionalExecutor executorBin(minsBin, maxsBin);
216  auto loop_task_bin = [this, current, binningDef, levels, level, ranges,
217  rangesBase](const std::vector<int> & coords) {
218  NLogTrace("NGnNavigator::Reshape: [B%d] Processing coordinates: coords=%s levels=%s", level,
219  NUtils::GetCoordsString(coords, -1).c_str(), NUtils::GetCoordsString(levels[level]).c_str());
220  NLogTrace("NGnNavigator::Reshape: [L%d] Generating %zuD histogram %s with ranges: %s", level,
221  levels[level].size(), NUtils::GetCoordsString(levels[level]).c_str(), ranges.size() == 0 ? "[]" : "");
222 
223  std::vector<int> axesIds = levels[level];
225  // THnSparse * hns = nullptr;
226  Int_t nDims = axesIds.size();
227  auto dims = std::make_unique<Int_t[]>(nDims);
228  // Int_t dims[nDims];
229  for (int i = 0; i < nDims; i++) {
230  dims[i] = axesIds[i];
231  }
232  THnSparse * hnsIn = binningDef->GetContent();
233 
234  NUtils::SetAxisRanges(hnsIn, ranges); // Set the ranges for the axes
235  // hns = static_cast<THnSparse *>(hnsIn->ProjectionND(axesIds.size(), dims.get(), "O"));
236  // if (!hns) {
237  // NLogError("NGnNavigator::Reshape: Projection failed for level %d !!!", level);
238  // return;
239  // }
240 
241  // // int nCells = h->GetNcells();
242  // // h->SetMinimum(0);
243  // // h->SetStats(kFALSE);
244  std::string name = "";
245  std::string title = "";
246  for (auto & axisId : axesIds) {
247  TAxis * a = hnsIn->GetAxis(axisId);
248  title += std::string(a->GetName()) + " vs ";
249  name += TString::Format("%s-", a->GetName()).Data();
250  }
251  name = name.substr(0, name.size() - 1); // Remove last "_"
252  if (name.empty()) name = "hns_proj"; // default name
253  title = title.substr(0, title.size() - 4); // Remove last " vs "
254  if (ranges.size() > 0) title += " for ranges: ";
255  for (const auto & [axisId, range] : rangesBase) {
256  // NLogDebug("XX Axis '%s' range: [%d, %d]", GetAxis(axisId)->GetName(), range[0], range[1]);
257  TAxis * a = hnsIn->GetAxis(axisId);
258  if (a->IsAlphanumeric()) {
259  title += TString::Format("%s[%s]", a->GetName(), a->GetBinLabel(range[0]));
260  }
261  else {
262  title +=
263  TString::Format("%s[%.2f,%.2f]", a->GetName(), a->GetBinLowEdge(range[0]), a->GetBinUpEdge(range[1]));
264  }
265  }
266  // hns->SetTitle(title.c_str());
267  // // hns is only needed to derive name/title strings; delete it now to
268  // // avoid leaking one THnSparse per bin iteration.
269  // delete hns;
270  // hns = nullptr;
271  // hns->Print();
272 
273  // TODO: Handle it via NGnSparseObject
274  // if (obj->GetHnSparse() == nullptr) {
275  // NLogDebug("NGnNavigator::Reshape: Setting histogram '%s' ...", hns->GetTitle());
276  // obj->SetHnSparse(hns);
277  // }
278 
279  int indexInProj = -1;
280  TH1 * hProj = nullptr;
281  // if (level < 3) {
282  if (nDims == 1) {
283  hProj = hnsIn->Projection(axesIds[0]);
284  hProj->SetDirectory(nullptr); // detach from gDirectory; navigator owns this histogram
285  // set name from hnsIn
286  TAxis * axisIn0 = hnsIn->GetAxis(axesIds[0]);
287  TAxis * axisProjX = hProj->GetXaxis();
288  axisProjX->SetName(axisIn0->GetName());
289  // apply lables from hnsIn to hProj
290  if (axisIn0->IsAlphanumeric()) {
291  for (int b = 1; b <= hProj->GetNbinsX(); b++) {
292  axisProjX->SetBinLabel(b, axisIn0->GetBinLabel(b));
293  }
294  }
295  indexInProj = hProj->FindFixBin(axisProjX->GetBinCenter(coords[0]));
296  }
297  else if (nDims == 2) {
298  // TODO: Check the order of axes is really correct
299  hProj = hnsIn->Projection(axesIds[1], axesIds[0]);
300  hProj->SetDirectory(nullptr); // detach from gDirectory; navigator owns this histogram
301  TAxis * axisIn1 = hnsIn->GetAxis(axesIds[0]);
302  TAxis * axisIn0 = hnsIn->GetAxis(axesIds[1]);
303  TAxis * axisProjX = hProj->GetXaxis();
304  TAxis * axisProjY = hProj->GetYaxis();
305  axisProjX->SetName(axisIn1->GetName());
306  axisProjY->SetName(axisIn0->GetName());
307  // apply lables from hnsIn to hProj
308  if (axisIn1->IsAlphanumeric()) {
309  for (int b = 1; b <= hProj->GetNbinsX(); b++) {
310  axisProjX->SetBinLabel(b, axisIn1->GetBinLabel(b));
311  }
312  }
313 
314  if (axisIn0->IsAlphanumeric()) {
315  for (int b = 1; b <= hProj->GetNbinsY(); b++) {
316  axisProjY->SetBinLabel(b, axisIn0->GetBinLabel(b));
317  }
318  }
319  indexInProj = hProj->FindFixBin(axisProjX->GetBinCenter(coords[0]), axisProjY->GetBinCenter(coords[1]));
320  }
321  else if (nDims == 3) {
322  hProj = hnsIn->Projection(axesIds[0], axesIds[1], axesIds[2]);
323  hProj->SetDirectory(nullptr); // detach from gDirectory; navigator owns this histogram
324  TAxis * axisIn0 = hnsIn->GetAxis(axesIds[0]);
325  TAxis * axisIn1 = hnsIn->GetAxis(axesIds[1]);
326  TAxis * axisIn2 = hnsIn->GetAxis(axesIds[2]);
327  TAxis * axisProjX = hProj->GetXaxis();
328  TAxis * axisProjY = hProj->GetYaxis();
329  TAxis * axisProjZ = hProj->GetZaxis();
330  axisProjX->SetName(axisIn0->GetName());
331  axisProjY->SetName(axisIn1->GetName());
332  axisProjZ->SetName(axisIn2->GetName());
333  // apply lables from hnsIn to hProj
334  if (axisIn0->IsAlphanumeric()) {
335  for (int b = 1; b <= hProj->GetNbinsX(); b++) {
336  axisProjX->SetBinLabel(b, axisIn0->GetBinLabel(b));
337  }
338  }
339 
340  if (axisIn1->IsAlphanumeric()) {
341  for (int b = 1; b <= hProj->GetNbinsY(); b++) {
342  axisProjY->SetBinLabel(b, axisIn1->GetBinLabel(b));
343  }
344  }
345  if (axisIn2->IsAlphanumeric()) {
346  for (int b = 1; b <= hProj->GetNbinsZ(); b++) {
347  axisProjZ->SetBinLabel(b, axisIn2->GetBinLabel(b));
348  }
349  }
350  indexInProj = hProj->FindFixBin(axisProjX->GetBinCenter(coords[0]), axisProjY->GetBinCenter(coords[1]),
351  axisProjZ->GetBinCenter(coords[2]));
352  }
353  else {
354  NLogError("NGnNavigator::Reshape: Cannot project THnSparse with %d dimensions", nDims);
355  return;
356  }
357  if (!hProj) {
358  NLogError("NGnNavigator::Reshape: Projection failed for level %d !!!", level);
359  return;
360  }
361 
362  hProj->SetName(name.c_str());
363  hProj->SetTitle(title.c_str());
364  // Increase all bin contents by 1 to avoid empty bins
365  double content;
366  int dim = hProj->GetDimension();
367  if (dim == 1) {
368  for (int x = 1; x <= hProj->GetNbinsX(); ++x) {
369  content = hProj->GetBinContent(x);
370  if (content > 0) {
371  hProj->SetBinContent(x, content + 1.0);
372  }
373  }
374  }
375  else if (dim == 2) {
376  for (int x = 1; x <= hProj->GetNbinsX(); ++x) {
377  for (int y = 1; y <= hProj->GetNbinsY(); ++y) {
378  content = hProj->GetBinContent(x, y);
379  if (content > 0) {
380  hProj->SetBinContent(x, y, content + 1.0);
381  }
382  }
383  }
384  }
385  else if (dim == 3) {
386  for (int x = 1; x <= hProj->GetNbinsX(); ++x) {
387  for (int y = 1; y <= hProj->GetNbinsY(); ++y) {
388  for (int z = 1; z <= hProj->GetNbinsZ(); ++z) {
389  content = hProj->GetBinContent(x, y, z);
390  if (content > 0) {
391  hProj->SetBinContent(x, y, z, content + 1.0);
392  }
393  }
394  }
395  }
396 
397  }
398 
399  // Handle special THnSparse reserved first cell without looping: use
400  // GetBinContent(0, coords) which fills coords for the linear bin 0.
401  {
402  Int_t nd = hnsIn->GetNdimensions();
403  Int_t *firstCoord = new Int_t[nd];
404  // Double_t firstVal =
405  hnsIn->GetBinContent(0, firstCoord); // fills firstCoord
406  // If bin 0 exists (firstVal may be 0.0), map its coordinates to projection
407  // bin indices and increment that projection bin by +1.
408  if (firstCoord) {
409  if (nDims == 1) {
410  int bx = firstCoord[axesIds[0]];
411  if (bx >= 1 && bx <= hProj->GetNbinsX())
412  hProj->SetBinContent(bx, hProj->GetBinContent(bx) + 1.0);
413  }
414  else if (nDims == 2) {
415  int bx = firstCoord[axesIds[0]];
416  int by = firstCoord[axesIds[1]];
417  if (bx >= 1 && bx <= hProj->GetNbinsX() && by >= 1 && by <= hProj->GetNbinsY())
418  hProj->SetBinContent(bx, by, hProj->GetBinContent(bx, by) + 1.0);
419  }
420  else if (nDims == 3) {
421  int bx = firstCoord[axesIds[0]];
422  int by = firstCoord[axesIds[1]];
423  int bz = firstCoord[axesIds[2]];
424  if (bx >= 1 && bx <= hProj->GetNbinsX() && by >= 1 && by <= hProj->GetNbinsY() &&
425  bz >= 1 && bz <= hProj->GetNbinsZ())
426  hProj->SetBinContent(bx, by, bz, hProj->GetBinContent(bx, by, bz) + 1.0);
427  }
428  }
429  delete[] firstCoord;
430  }
431 
432  NLogTrace("NGnNavigator::Reshape: [L%d] Projection histogram '%s' for coords=%s index=%d", level,
433  hProj->GetTitle(), NUtils::GetCoordsString(coords, -1).c_str(), indexInProj);
434  //
435  // hProj->SetMinimum(0);
436  // hProj->SetStats(kFALSE);
437  // hProj->Draw();
438  // gPad->ModifiedUpdate();
439  // gSystem->Sleep(1000);
440  // }
441  // hProj->Print();
442  // hProj->Draw("colz text");
443  // `current` is shared across all bin iterations at this level. The
444  // projection is identical for every iteration (ranges are fixed), so
445  // only store it once. Subsequent iterations delete the duplicate and
446  // reuse the already-stored pointer so that `nCells` below stays valid.
447  if (current->GetProjection() == nullptr) {
448  current->SetProjection(hProj);
449  }
450  else {
451  delete hProj;
452  hProj = current->GetProjection();
453  }
455 
456  std::map<int, std::vector<int>> rangesTmp = ranges;
457  std::map<int, std::vector<int>> rangesBaseTmp = rangesBase;
458  for (auto & kv : rangesBaseTmp) {
459  std::vector<int> range = rangesTmp[kv.first];
460  NLogTrace("NGnNavigator::Reshape: [L%d] Axis %d[%s]: rangeBase=%s range=%s", level, kv.first,
461  hnsIn->GetAxis(kv.first)->GetName(), NUtils::GetCoordsString(kv.second).c_str(),
462  NUtils::GetCoordsString(range).c_str());
463  }
464  int minBase = 0, maxBase = 0;
465  int i = 0;
466  for (auto & c : coords) {
467  // NLogDebug("Coordinate: %d v=%d axis=%d", i, coords[i], axes[i]);
468  NUtils::GetAxisRangeInBase(hnsIn->GetAxis(axesIds[i]), c, c, binningDef->GetBinning()->GetAxes()[axesIds[i]],
469  minBase, maxBase);
470  NLogTrace("NGnNavigator::Reshape: Axis %d: minBase=%d maxBase=%d", axesIds[i], minBase, maxBase);
471  rangesTmp[axesIds[i]] = {c, c}; // Set the range for the first axis
472  rangesBaseTmp[axesIds[i]] = {minBase, maxBase}; // Set the range for the first axis
473  i++;
474  }
475 
476  if (hProj == nullptr) {
477  NLogError("NGnNavigator::Reshape: Projection histogram is null for level %d !!!", level);
478  return;
479  }
480 
481  size_t nCells = hProj->GetNcells();
482 
483  // NGnNavigator * o = fParent->GetChild(indexInProj);
484  NGnNavigator * currentChild = current->GetChild(indexInProj);
485  if (currentChild == nullptr) {
486  NLogTrace("NGnNavigator::Reshape: [L%d] Creating new child for index %d nCells=%d ...", level, indexInProj,
487  nCells);
488  std::string childName = TString::Format("%s_L%zu_C%d", GetName(), level + 1, indexInProj).Data();
489  currentChild = new NGnNavigator(childName.c_str(), childName.c_str());
490  currentChild->SetLevel(level + 1);
491  currentChild->SetNLevels(levels.size());
492  currentChild->SetParent(current);
493  currentChild->SetGnTree(fGnTree);
494  // o = new NGnNavigator(hns->GetListOfAxes());
495  // if (fParent->GetChildren().size() != nCells) fParent->SetChildrenSize(nCells);
496  // fParent->SetChild(o, indexInProj); // Set the child at the index
497  // fParent = o;
498  // INFO: it was moved down
499  // if (current->GetChildren().size() != nCells) current->SetChildrenSize(nCells);
500  // current->SetChild(currentChild, indexInProj); // Set the child at the index
501  // currentChild->Print();
502  }
503  else {
504  NLogError("NGnNavigator::Reshape: [L%d] Using existing child for index %d [NOT OK] ...", level, indexInProj);
505  }
506 
507  // currentChild->Print();
508  // return;
509 
510  if (level == levels.size() - 1) {
511  NLogTrace("NGnNavigator::Reshape: [L%d] Filling projections from all branches %s for ranges:", level,
512  NUtils::GetCoordsString(levels[level]).c_str());
513  for (auto & kv : rangesBaseTmp) {
514  std::vector<int> range = rangesTmp[kv.first];
515  NLogTrace("NGnNavigator::Reshape: [L%d] Axis %d ['%s']: rangeBase=%s range=%s", level, kv.first,
516  binningDef->GetContent()->GetAxis(kv.first)->GetName(), NUtils::GetCoordsString(kv.second).c_str(),
517  NUtils::GetCoordsString(range).c_str());
518  // rangesTmp[kv.first] = range;
519  }
520  // Get projectiosn histograms from all branches
521 
522  // Int_t * cCoords = new Int_t[hns->GetNdimensions()];
523  Long64_t linBin = 0;
524 
525  // hns->Print("all");
526  NUtils::SetAxisRanges(hnsIn, rangesTmp); // Set the ranges for the axes
527  std::unique_ptr<ROOT::Internal::THnBaseBinIter> iter{hnsIn->CreateIter(true /*use axis range*/)};
528  std::vector<int> linBins;
529 
530  // loop over all bins in the sparse
531 
532  while ((linBin = iter->Next()) >= 0) {
533  NLogTrace("NGnNavigator::Reshape: [L%d] Found bin %lld", level, linBin);
534  linBins.push_back(linBin);
535  }
536  if (linBins.empty()) {
537  NLogTrace("NGnNavigator::Reshape: [L%d] No bins found for the given ranges, skipping ...", level);
538  // continue;
539  return; // No bins found, nothing to process
540  }
541  // // bool skipBin = false; // Skip bin if no bins are found
542  NLogTrace("NGnNavigator::Reshape: Branch object Point coordinates: %s",
543  NUtils::GetCoordsString(linBins, -1).c_str());
544  current->SetNCells(nCells);
545 
546  for (int lb : linBins) {
547  fGnTree->GetEntry(lb);
548  for (auto & [key, val] : fGnTree->GetStorageTree()->GetBranchesMap()) {
549  if (val.GetBranchStatus() == 0) {
550  NLogTrace("NGnNavigator::Reshape: [L%d] Branch '%s' is disabled, skipping ...", level, key.c_str());
551  continue; // Skip disabled branches
552  }
553  NLogTrace("NGnNavigator::Reshape: [L%d] Processing branch '%s' with %zu objects to loop ...", level,
554  key.c_str(), linBins.size());
555 
556  // if (obj->GetParent()->GetObjectContentMap()[key].size() != nCells)
557  // obj->GetParent()->ResizeObjectContentMap(key, nCells);
558 
559  // if (skipBin) continue; // Skip processing if the previous bin was skipped
560 
564  // obj->GetParent()->SetNCells(nCells);
565  // fParent->SetNCells(nCells);
566  TString className = val.GetObjectClassName();
567  if (className.BeginsWith("THnSparse")) {
568  // // if (obj->GetParent()->GetObjectContentMap()[key].size() != nCells)
569  // // obj->GetParent()->ResizeObjectContentMap(key, nCells);
570  // // TODO: Make it configurable
571  // int projectionAxis = 0;
572  //
573  // TH1 * hProjTmp = nullptr;
574  // // loop over all linBins
575  // for (int lb : linBins) {
576  // fGnTree->GetEntry(lb);
577  // if (hProjTmp == nullptr) {
578  // hProjTmp = ProjectionFromObject(key, projectionAxis, rangesBaseTmp);
579  // // NLogDebug("AAAA %.0f", hProjTmp ? hProjTmp->GetEntries() : 0);
580  // }
581  // else {
582  // TH1 * temp = ProjectionFromObject(key, projectionAxis, rangesBaseTmp);
583  // // NLogDebug("BBBB %.0f", temp ? temp->GetEntries() : 0);
584  // if (temp) {
585  // hProjTmp->Add(temp);
586  // delete temp; // Delete the temporary histogram to avoid memory leaks
587  // }
588  // }
589  // }
590  // if (!hProjTmp) {
591  // // skipBin = true; // Skip bin if no histogram is created
592  // continue;
593  // }
594  // // generate histogram title from axis base ranges
595  // std::string title = "Projection of " + key + " ";
596  // for (const auto & [axis, range] : rangesBaseTmp) {
597  // TAxis * a = fBinning->GetAxes()[axis];
598  //
599  // title +=
600  // TString::Format("%s[%.2f,%.2f]", a->GetName(), a->GetBinLowEdge(range[0]),
601  // a->GetBinUpEdge(range[1]));
602  // }
603  // hProjTmp->SetTitle(title.c_str());
604  // NLogTrace("[L%d] Projection histogram '%s' for branch '%s' storing with indexInProj=%d,
605  // entries = % .0f ",
606  // level,
607  // hProjTmp->GetTitle(), key.c_str(), indexInProj, hProjTmp->GetEntries());
608  // obj->GetParent()->SetObject(key, hProjTmp, indexInProj);
609  // // hProjTmp->Draw();
610  // // gPad->Update();
611  // // gPad->Modified();
612  // // gSystem->ProcessEvents();
613  // // TODO: We may to set entries from projection histogram to the bin content of mapping file
614  }
615  else if (className.BeginsWith("TList")) {
616  NLogTrace("[L%d] Branch '%s' is a TList, getting object at index %d ...", level, key.c_str(),
617  indexInProj);
618  TList * list = dynamic_cast<TList *>(val.GetObject());
619  // list->Print();
620  // get list of object names
621  std::vector<std::string> objNames;
622  for (int i = 0; i < list->GetEntries(); i++) {
623  TObject * o = list->At(i);
624  objNames.push_back(o->GetName());
625  }
626  // remove "results" histogram
627  // objNames.erase(std::remove(objNames.begin(), objNames.end(), "_params"), objNames.end());
628 
629  NLogTrace("[L%d] Branch '%s' TList contains %d objects: %s", level, key.c_str(), list->GetEntries(),
630  NUtils::GetCoordsString(objNames).c_str());
631 
632  // std::vector<std::string> possibleNames = {"hPeak", "hBgNorm", "unlikepm_proj_0"};
633 
634  // current->SetObjectNames(objNames);
635  bool isValid = true;
636  for (auto & name : objNames) {
637  TH1 * hProjTmp = dynamic_cast<TH1 *>(list->FindObject(name.c_str()));
638  if (hProjTmp == nullptr) {
639  NLogTrace("NGnNavigator::Reshape::Warning Branch '%s' TList does not contain '%s' as TH1 !!!",
640  key.c_str(), name.c_str());
641  isValid = false;
642 
643  continue;
644  }
645  if (TMath::IsNaN(hProjTmp->GetEntries()) || TMath::IsNaN(hProjTmp->GetSumOfWeights())) {
646  NLogWarning("NGnNavigator::Reshape: Branch '%s' '%s' histogram is nan !!!", key.c_str(),
647 
648  name.c_str());
649  isValid = false;
650  continue;
651  }
652  NLogTrace(
653  "[L%d] Histogram name='%s' title='%s' for branch '%s' storing with indexInProj=%d, entries=%.0f",
654  level, name.c_str(), hProjTmp->GetTitle(), key.c_str(), indexInProj, hProjTmp->GetEntries());
655  // if (obj->GetParent()->GetObjectContentMap()[name].size() != nCells)
656  // obj->GetParent()->ResizeObjectContentMap(name, nCells);
657  // // obj->GetParent()->SetObject(key, hProjTmp, indexInProj);
658  // obj->GetParent()->SetObject(name, hProjTmp, indexInProj);
659  // if (fParent->GetObjectContentMap()[name].size() != nCells) fParent->ResizeObjectContentMap(name,
660  // nCells); fParent->SetObject(name, hProjTmp, indexInProj);
661  if (current->GetObjectContentMap()[name].size() != nCells)
662  current->ResizeObjectContentMap(name, nCells);
663  // Clone the histogram so the navigator owns it independently of the TTree branch
664  // buffer. ROOT clears TList branch objects on every GetEntry() call, which would
665  // leave the stored pointer dangling if we don't clone here.
666  TH1 * hClone = (TH1 *)hProjTmp->Clone();
667  hClone->SetDirectory(nullptr);
668  current->SetObject(name, hClone, indexInProj);
669  }
670  if (isValid == false) {
671  NLogTrace("NGnNavigator::Reshape::Warning: Branch '%s' TList does not contain any valid histograms !!!",
672  key.c_str());
673  continue;
674  }
675  }
676  else if (className.BeginsWith("Ndmspc::NParameters")) {
677 
678  NParameters * parameters = dynamic_cast<NParameters *>(val.GetObject());
679  if (parameters) {
680  TH1 * hParams = parameters->GetHisto();
681  if (hParams) {
682  // hParams->Print("all");
683  NLogTrace("[L%d] Branch '%s' Point contains '_params' histogram with %.0f entries ...", level,
684  key.c_str(), hParams->GetEntries());
685  // loop over bin labels
686  for (int b = 1; b <= hParams->GetNbinsX(); b++) {
687 
688  std::string binLabel = hParams->GetXaxis()->GetBinLabel(b);
689  double binValue = hParams->GetBinContent(b);
690  double binError = hParams->GetBinError(b);
691  NLogTrace("[L%d] Bin %d[%s] = %e indexInProj=%d", level, b, binLabel.c_str(), binValue,
692  indexInProj);
693  // // check if binlabel is "mass"
694  // if (binLabel.compare("mass") == 0) {
695  // NLogInfo("[L%d] Checking bin 'mass' = %f ...", level, binValue);
696  // if (binValue < 1.015 || binValue > 1.025) {
697  // NLogInfo("[L%d] Skipping bin 'mass' with value %f ...", level, binValue);
698  // continue;
699  // }
700  // }
701  // obj->GetParent()->SetParameter(binLabel, binValue, indexInProj);
702  // fParent->SetParameter(binLabel, binValue, indexInProj);
703  current->SetParameter(binLabel, binValue, indexInProj);
704  current->SetParameterError(binLabel, binError, indexInProj);
705  NLogTrace("[L%d] Stored parameter '%s' = %e +/- %e at indexInProj=%d", level, binLabel.c_str(),
706  binValue, binError, indexInProj);
707  }
708  }
709  }
710  else {
711  NLogWarning("NGnNavigator::Reshape: Branch '%s' Point parameters object is null !!!", key.c_str());
712  }
713  }
714  else {
715  NLogWarning("NGnNavigator::Reshape: Branch '%s' has unsupported class '%s' !!! Skipping ...", key.c_str(),
716  className.Data());
717  }
718  }
719  }
720 
721  // Reshape(binningDef, levels, level + 1, rangesTmp, rangesBaseTmp, currentChild);
722  // return;
723  }
724  if (current->GetChildren().size() != nCells) current->SetChildrenSize(nCells);
725  current->SetChild(currentChild, indexInProj); // Set the child at the index
726  // execute next child
727  Reshape(binningDef, levels, level + 1, rangesTmp, rangesBaseTmp, currentChild);
728  };
729  executorBin.Execute(loop_task_bin);
730  }
731  else {
732  NLogTrace("NGnNavigator::Reshape: Reached the end of levels, level=%d", level);
733  std::vector<std::vector<int>> rangesEmpty;
734  NUtils::SetAxisRanges(binningDef->GetContent(), rangesEmpty); // Set the ranges for the axes
735 
736  return current;
737  }
738 
739  NLogTrace("NGnNavigator::Reshape: =========== Reshaping navigator for level %d DONE ================", level);
740 
741  if (level == 0) {
742  NLogInfo("NGnNavigator::Reshape: Reshaping navigator DONE.");
743  // print exported axes from indexes from levels
744  for (size_t l = 0; l < levels.size(); l++) {
745  std::string axesStr = "";
746  for (auto & a : levels[l]) {
747  TAxis * axis = binningDef->GetContent()->GetAxis(a);
748  axesStr += TString::Format("%d('%s') ", a, axis->GetName()).Data();
749  }
750  NLogInfo(" Level %zu axes: %s", l, axesStr.c_str());
751  }
752  }
753 
754  if (branch) branch->SetBranchStatus(outputPointStatus);
755 
756  // current->Print("");
757 
758  return current;
759 }
760 
761 void NGnNavigator::Export(const std::string & filename, std::vector<std::string> objectNames, const std::string & wsUrl,
762  int timeoutMs)
763 {
767  NLogInfo("Exporting NGnNavigator to file: %s", filename.c_str());
768 
769  json objJson;
770 
771  // if filename ends with .root, remove it
772  if (filename.size() > 5 && filename.substr(filename.size() - 5) == ".root") {
773  NLogInfo("Exporting NGnNavigator to ROOT file: %s", filename.c_str());
774  TFile * file = TFile::Open(filename.c_str(), "RECREATE");
775  if (!file || file->IsZombie()) {
776  NLogError("Failed to open file: %s", filename.c_str());
777  return;
778  }
779  file->cd();
780  this->Write();
781  file->Close();
782  delete file;
783  }
784  else if (filename.size() > 5 && filename.substr(filename.size() - 5) == ".json") {
785  NLogInfo("Exporting NGnNavigator to JSON file: %s", filename.c_str());
786  NGnNavigator * obj = const_cast<NGnNavigator *>(this);
787  ExportToJson(objJson, obj, objectNames);
788  // std::cout << objJson.dump(2) << std::endl;
789  bool rc = NUtils::SaveRawFile(filename, objJson.dump());
790  if (rc == false) {
791  NLogError("Failed to save JSON file: %s", filename.c_str());
792  return;
793  }
794  NLogInfo("Exported NGnNavigator to file: %s", filename.c_str());
795  }
796  else if (filename.empty()) {
797  NLogInfo("No filename provided, export to JSON only ...");
798  NGnNavigator * obj = const_cast<NGnNavigator *>(this);
799  ExportToJson(objJson, obj, objectNames);
800  std::cout << objJson.dump() << std::endl;
801  }
802  else {
803  NLogError("Unsupported file format for export: %s", filename.c_str());
804  return;
805  }
806  if (!wsUrl.empty()) {
807  NLogInfo("Uploading exported file to web socket: %s", wsUrl.c_str());
808  // NUtils::UploadFileToWebService(filename, wsUrl);
809 
810  std::string message = objJson.dump();
811  Ndmspc::NWsClient client;
812  if (!message.empty()) {
813  NLogInfo("Connecting to web socket: %s", wsUrl.c_str());
814  if (!client.Connect(wsUrl)) {
815  NLogError("Failed to connect to '%s' !!!", wsUrl.c_str());
816  }
817  else {
818 
819  if (!client.Send(objJson.dump())) {
820  NLogError("Failed to send message `%s`", message.c_str());
821  }
822  else {
823  NLogInfo("Successfully sent message to '%s'", wsUrl.c_str());
824  }
825  }
826  if (timeoutMs > 0) {
827  NLogInfo("Waiting %d ms before disconnecting ...", timeoutMs);
828  gSystem->Sleep(timeoutMs); // wait for a while to ensure message is sent
829  }
830  NLogInfo("Disconnecting from '%s' ...", wsUrl.c_str());
831  client.Disconnect();
832  }
833 
834  NLogInfo("Sent: %s", message.c_str());
835  }
836 }
837 
838 void NGnNavigator::ExportToJson(json & j, NGnNavigator * obj, std::vector<std::string> objectNames)
839 {
843 
844  if (obj == nullptr) {
845  NLogError("NGnNavigator::ExportJson: Object is nullptr !!!");
846  return;
847  }
848 
849  if (obj->GetChildren().empty()) {
850  return;
851  }
852 
853  TH1 * h = obj->GetProjection();
854  if (h == nullptr) {
855  NLogError("NGnNavigator::ExportJson: Projection is nullptr !!!");
856  return;
857  }
858 
859  // h->SetNameTitle(name.c_str(), title.c_str());
860  h->SetMinimum(0);
861  h->SetStats(kFALSE); // Turn off stats box for clarity
862  // h->SetDirectory(nullptr); // Avoid ROOT trying to save the histogram in a file
863 
864  // Store TString in a named variable: in Cling's interpreted mode, TString temporaries
865  // can be destroyed before json::parse() reads the char* pointer, causing a segfault.
866  TString hJsonStr = TBufferJSON::ConvertToJSON(h);
867  j = json::parse(hJsonStr.Data());
868  // loop over content map and add objects
869  double entries = 0.0;
870  // int idx = 0;
871  if (objectNames.empty()) {
872  // NLogDebug("NGnNavigator::ExportJson: Exporting all objects ...");
873  // loop over all keys and add them to objectNames
874  bool isValid = false;
875  for (const auto & [key, val] : obj->GetObjectContentMap()) {
876  isValid = false;
877  for (size_t i = 0; i < val.size(); i++) {
878  TObject * objContent = val[i];
879  // NLogDebug("NGnNavigator::ExportJson: Processing object '%s' at index %zu ...", key.c_str(), i);
880  if (objContent) {
881  // check if object type is inherited from list of names in objectTypes
882  std::string className = objContent ? objContent->ClassName() : "";
883  if (className.empty()) {
884  NLogWarning("NGnNavigator::ExportJson: Object %s has empty class name", key.c_str());
885  continue;
886  }
887  // shrink className string to 3 characters if it is longer than 3
888  className = className.substr(0, 3);
889  // NLogDebug("NGnNavigator::ExportJson: Object %s has class '%s'", key.c_str(), className.c_str());
890  if (std::find(NGnNavigator::fObjectTypes.begin(), NGnNavigator::fObjectTypes.end(), className) !=
892  // NLogWarning(
893  // "NGnNavigator::ExportJson: Skipping unsupported object type '%s' for object '%s' at index %zu",
894  // className.c_str(), key.c_str(), i);
895  isValid = true;
896  break;
897  }
898  }
899  }
900  if (isValid) objectNames.push_back(key);
901  }
902  }
903  else {
904  NLogDebug("NGnNavigator::ExportJson: Exporting selected objects: %s", NUtils::GetCoordsString(objectNames).c_str());
905  }
906 
907  // Print all included object names
908  for (const auto & name : objectNames) {
909  NLogTrace("NGnNavigator::ExportJson: Included object name: '%s'", name.c_str());
910  }
911 
912  for (const auto & [key, val] : obj->GetObjectContentMap()) {
913 
914  NLogTrace("NGnNavigator::ExportJson: Processing object '%s' with %zu entries ...", key.c_str(), val.size());
915  // Filter by objectNames
916  if (std::find(objectNames.begin(), objectNames.end(), key) == objectNames.end()) {
917  NLogDebug("NGnNavigator::ExportJson: Skipping object '%s' ...", key.c_str());
918  continue;
919  }
920 
921  double min = std::numeric_limits<double>::max(); // Initialize with largest possible double
922  double max = -std::numeric_limits<double>::max(); // Initialize with smallest possible double
923  entries = 0.0; // Reset entries for each key
924 
925  for (size_t i = 0; i < val.size(); i++) {
926  TObject * objContent = val[i];
927 
928  if (objContent) {
929  // Runtime check: Only cast if objContent inherits from TH1
930  TH1 * hist = dynamic_cast<TH1 *>(objContent);
931  if (hist) {
932  // Store TString in a named variable to avoid Cling temporary lifetime issue.
933  TString histJsonStr = TBufferJSON::ConvertToJSON(hist);
934  json objJson = json::parse(histJsonStr.Data());
935  double objMin, objMax;
936  NUtils::GetTrueHistogramMinMax(hist, objMin, objMax, false);
937  min = TMath::Min(min, objMin);
938  max = TMath::Max(max, objMax);
939  entries = hist->GetEntries();
940  j["fArray"][i] = entries;
941  if (entries > 0) {
942  j["children"][key].push_back(objJson);
943  }
944  else {
945  j["children"][key].push_back(nullptr);
946  }
947  }
948  else {
949  NLogWarning("NGnNavigator::ExportJson: Object %s at index %zu is not a TH1, skipping.", key.c_str(), i);
950  entries = 0.0;
951  j["children"][key].push_back(nullptr);
952  }
953  }
954  else {
955  entries = 0.0;
956  j["children"][key].push_back(nullptr);
957  }
958  }
959  }
960 
961  for (auto & [key, val] : obj->GetParameterContentMap()) {
962  double min = std::numeric_limits<double>::max(); // Initialize with largest possible double
963  double max = -std::numeric_limits<double>::max(); // Initialize with smallest possible double
964  entries = 0.0; // Reset entries for each key
965 
966  for (size_t i = 0; i < val.size(); i++) {
967  double param = val[i];
968  double paramError = 0.0;
969  // Runtime check: Only call GetParameterError if index is valid
970  if (i < val.size()) {
971  try {
972  paramError = obj->GetParameterError(key, i);
973  }
974  catch (...) {
975  NLogWarning("NGnNavigator::ExportJson: Exception in GetParameterError for key %s index %zu", key.c_str(), i);
976  paramError = 0.0;
977  }
978  }
979  if (!std::isnan(param) && std::fabs(param) > 1e-12) {
980  min = TMath::Min(min, param);
981  max = TMath::Max(max, param);
982  j["fArrays"][key]["values"][i] = param;
983  j["fArrays"][key]["errors"][i] = TMath::Power(paramError, 2);
984  }
985  else {
986  j["fArrays"][key]["values"][i] = 0.0;
987  j["fArrays"][key]["errors"][i] = 0.0;
988  }
989  }
990 
991  if (key.compare("mass") == 0) {
992  // for chi2, ndf and pvalue set min and max to 0 and 1
993  min = 1.018;
994  max = 1.023;
995  }
996  else {
997 
998  // set min max with 5 percent margin
999  double margin = 0.05 * (max - min);
1000  min = min - margin;
1001  max = max + margin;
1002  j["fArrays"][key]["min"] = min;
1003  j["fArrays"][key]["max"] = max;
1004  }
1005  // j["ndmspc"][key]["fEntries"] = entries;
1006  // NLogDebug("NGnNavigator::ExportJson: key=%s Min=%f, Max=%f", key.c_str(), min, max);
1007  }
1008 
1009  double min = std::numeric_limits<double>::max(); // Initialize with largest possible double
1010  double max = -std::numeric_limits<double>::max(); // Initialize with smallest possible double
1011  std::vector<double> tmpContent;
1012  // --- Propagate parameter min/max from children ---
1013  std::map<std::string, double> paramMinGlobal;
1014  std::map<std::string, double> paramMaxGlobal;
1015  bool firstChild = true;
1016  for (const auto & child : obj->GetChildren()) {
1017  json childJson;
1018  if (child != nullptr) {
1019  try {
1020  ExportToJson(childJson, child, objectNames);
1021  }
1022  catch (...) {
1023  NLogWarning("NGnNavigator::ExportJson: Exception in recursive ExportToJson for child.");
1024  childJson = json();
1025  }
1026  // Aggregate parameter min/max from child
1027  if (childJson.contains("fArrays")) {
1028  for (auto & [param, arr] : childJson["fArrays"].items()) {
1029  if (arr.contains("min") && arr.contains("max")) {
1030  double cmin = arr["min"].get<double>();
1031  double cmax = arr["max"].get<double>();
1032  if (firstChild || paramMinGlobal.find(param) == paramMinGlobal.end()) {
1033  paramMinGlobal[param] = cmin;
1034  paramMaxGlobal[param] = cmax;
1035  }
1036  else {
1037  paramMinGlobal[param] = std::min(paramMinGlobal[param], cmin);
1038  paramMaxGlobal[param] = std::max(paramMaxGlobal[param], cmax);
1039  }
1040  }
1041  }
1042  }
1043  }
1044  firstChild = false;
1045  j["children"]["content"].push_back(childJson);
1046  }
1047 
1048  // Store aggregated min/max at this level if any
1049  if (!paramMinGlobal.empty()) {
1050  for (const auto & [param, minVal] : paramMinGlobal) {
1051  j["fArrays"][param]["min"] = minVal;
1052  j["fArrays"][param]["max"] = paramMaxGlobal[param];
1053  }
1054  }
1055  // loop over j["children"]["content"] and remove empty objects"
1056  bool hasContent = false;
1057  for (auto & c : j["children"]["content"]) {
1058  if (c != nullptr) {
1059  hasContent = true;
1060  break;
1061  }
1062  }
1063 
1064  if (!hasContent) {
1065  j["children"].erase("content");
1066  // NOTE: GetChildren() returns by value, so .clear() here was a no-op and did NOT
1067  // modify fChildren. The node's actual children remain intact.
1068  // If pruning is desired, add a ClearChildren() method that clears fChildren directly.
1069  // j["ndmspc"]["content"]["fMinimum"] = min;
1070  // j["ndmspc"]["content"]["fMaximum"] = max;
1071  }
1072  else {
1073  j["ndmspc"]["content"]["fMinimum"] = min;
1074  j["ndmspc"]["content"]["fMaximum"] = max;
1075  }
1076 
1077  if (obj->GetParent() == nullptr) {
1078  // NLogDebug("NGnNavigator::ExportJson: LLLLLLLLLLLLLLLLLLLLLLast");
1079  int i = -1;
1080  for (const auto & child : j["children"]["content"]) {
1081  i++;
1082  if (child == nullptr || child.is_null()) {
1083  // NLogError("NGnNavigator::ExportJson: Child is nullptr !!!");
1084  j["fArray"][i] = 0; // Store the maximum value for the content
1085  continue;
1086  }
1087 
1088  if (child.contains("ndmspc")) {
1089  // std::cout << child["fTitle"].dump() << std::endl;
1090  // double min = std::numeric_limits<double>::max(); // Initialize with largest possible double
1091  double max = -std::numeric_limits<double>::max(); // Initialize with smallest possible double
1092  // loop over all keys in "ndmspc"
1093  for (auto & [key, value] : child["ndmspc"].items()) {
1094  if (value.is_object()) {
1095  // min = TMath::Min(min, value["fMinimum"].get<double>());
1096  // min = 0;
1097  max = TMath::Max(max, value["fMaximum"].get<double>());
1098  }
1099  }
1100  j["fArray"][i] = max; // Store the maximum value for the content
1101  }
1102  else {
1103  j["fArray"][i] = 1; // Store the maximum value for the content
1104  }
1105  }
1106  if (j["children"]["content"].is_null()) j["children"].erase("content");
1107  }
1108 }
1109 
1110 void NGnNavigator::Print(Option_t * option) const
1111 {
1115  TString opt = option;
1116  opt.ToUpper();
1117 
1118  if (opt.Contains("A") && fGnTree) fGnTree->Print(option);
1119  if (fProjection) {
1120  NLogInfo("NGnNavigator: name='%s' title='%s' level=%d levels=%d projection='%s' title='%s'", GetName(), GetTitle(),
1121  fLevel, fNLevels, fProjection->GetName(), fProjection->GetTitle());
1122  // fProjection->Print(option);
1123  }
1124  else {
1125  NLogInfo("NGnNavigator: name='%s' title='%s' level=%d levels=%d projection=nullptr", GetName(), GetTitle(), fLevel,
1126  fNLevels);
1127  }
1128  if (!fParent) {
1129  NLogInfo("NGnNavigator: This is the root navigator.");
1130  // Print levels
1131  NBinningDef * binningDef = fGnTree->GetBinning()->GetDefinition();
1132  for (size_t l = 0; l < fLevels.size(); l++) {
1133  std::string axesStr = "";
1134  for (auto & a : fLevels[l]) {
1135  TAxis * axis = binningDef->GetContent()->GetAxis(a);
1136  axesStr += TString::Format("%d('%s') ", a, axis->GetName()).Data();
1137  }
1138  NLogInfo(" Level %zu axes: %s", l, axesStr.c_str());
1139  }
1140  }
1141 
1142  // Print only list of parameters
1143  NLogInfo("NGnNavigator: Parameters : %s", NUtils::GetCoordsString(GetParameterNames()).c_str());
1144  NLogInfo("NGnNavigator: Objects : %s", NUtils::GetCoordsString(GetObjectNames()).c_str());
1145 
1146  // print children
1147  std::vector<int> childIndices;
1148  for (size_t i = 0; i < fChildren.size(); i++) {
1149  NGnNavigator * child = fChildren[i];
1150  if (child) {
1151  childIndices.push_back(i);
1152  // NLogInfo("NGnNavigator: Child %d/%d:", i + 1, fChildren.size());
1153  // child->Print(option);
1154  }
1155  }
1156  NLogTrace("NGnNavigator: %zu children with indices: %s", childIndices.size(),
1157  NUtils::GetCoordsString(childIndices, -1).c_str());
1158  for (size_t i = 0; i < fChildren.size(); i++) {
1159  NGnNavigator * child = fChildren[i];
1160  if (child && child->GetChildren().empty() == false) {
1161  child->Print(option);
1162  }
1163  }
1164 }
1165 
1167 {
1171  json j;
1172  j["name"] = GetName();
1173  j["title"] = GetTitle();
1174  j["level"] = fLevel;
1175  j["nLevels"] = fNLevels;
1176  j["nChildren"] = fChildren.size();
1177  j["objects"] = GetObjectNames();
1178  j["params"] = GetParameterNames();
1179  auto parent = const_cast<NGnNavigator *>(this)->GetParent();
1180  j["levels"] = parent ? parent->GetLevels() : fLevels;
1181  return j;
1182 }
1183 
1184 void NGnNavigator::Draw(Option_t * option)
1185 {
1190 
1191  // TODO: Handle if size od levels is greater than 2 (since ROOT cannot hover more than 2D histograms)
1192  // TH1 * proj = GetProjection();
1193  for (size_t level = 0; level < fNLevels; level++) {
1194  NLogDebug("NGnNavigator::Draw: Level %d/%d", level + 1, fNLevels);
1195  if (fProjection->GetDimension() > 2) {
1196  NLogWarning("NGnNavigator::Draw: Level %d has projection with dimension %d > 2, which is not supported for "
1197  "hover/click !!!",
1198  level + 1, fProjection->GetDimension());
1199  return;
1200  }
1201  // proj = fChildren[0]->GetProjection();
1202  }
1203 
1204  // if (fGnTree == nullptr) {
1205  // NLogError("NGnNavigator::Draw: NGnTree is nullptr !!!");
1206  // return;
1207  // }
1208  //
1209  TString opt = option;
1210  opt.ToUpper();
1211  if (opt.Contains("HOVER")) {
1212  fTrigger = kMouseMotion;
1213  }
1214  if (opt.Contains("CLICK")) {
1215  fTrigger = kButton1Down;
1216  }
1217 
1218  // std::string name;
1219  if (gPad) SafeDelete(gPad);
1220  if (!gPad) {
1221  // NLogInfo("NGnNavigator::Draw: Making default canvas ...");
1222  gROOT->MakeDefCanvas();
1223  // if (!gPad->IsEditable()) return;
1224  if (fNLevels > fLevel + 1) {
1225  Int_t cy = TMath::Sqrt(fNLevels);
1226  Int_t cx = TMath::Ceil(fNLevels / (Double_t)cy);
1227  gPad->Divide(cy, cx); // Divide the pad into a grid based on the number of levels
1228  }
1229  }
1230  TVirtualPad * originalPad = gPad; // Save the original pad
1231  NGnNavigator * obj = nullptr;
1232  for (size_t level = fLevel; level < fNLevels; level++) {
1233  NLogDebug("NGnNavigator::Draw: Drawing level %d", level);
1234  TVirtualPad * pad = originalPad->cd(level + 1);
1235  if (pad) {
1236  NLogDebug("NGnNavigator::Draw: Clearing pad %d", level + 1);
1237  pad->Clear();
1238  gPad = pad; // Set the current pad to the level + 1 pad
1239  if (level == fLevel) {
1240  obj = this; // For the first level, use the current object
1241  NLogDebug("NGnNavigator::Draw: Using current object at level %d: %s", level, obj->GetName());
1242  }
1243  else {
1244 
1245  // obj = nullptr; // Reset the object for the next level
1246  for (size_t i = 0; i < obj->GetChildren().size(); i++) {
1247  NGnNavigator * child = obj->GetChild(i);
1248  if (child) {
1249  NLogDebug("NGnNavigator::Draw: Found child at level %d: %s", level, child->GetProjection()->GetTitle());
1250  obj = child; // Get the child object at the current level
1251  break;
1252  }
1253  }
1254  NLogDebug("NGnNavigator::Draw: Using child object at level %d: %s", level, obj ? obj->GetName() : "nullptr");
1255  }
1256  if (obj == nullptr) {
1257  NLogError("NGnNavigator::Draw: Child object at level %d is nullptr !!!", level);
1258  continue; // Skip to the next level if the child is nullptr
1259  }
1260  TH1 * projection = obj->GetProjection();
1261  if (projection) {
1262  NLogDebug("NGnNavigator::Draw: Drawing projection at level %d: %s", level, projection->GetTitle());
1263  projection->SetMinimum(0);
1264  projection->SetStats(kFALSE); // Turn off stats box for clarity
1265  // gPad->cd(); // Change to the current pad
1266  // projection->Draw(option); // Draw the projection histogram
1267  // obj->Draw(option); // Draw the object at the current level
1268  obj->AppendPad(option); // Append the pad to the current pad stack
1269  }
1270  else {
1271  NLogError("NGnNavigator::Draw: Projection at level %d is nullptr !!!", level);
1272  }
1273  }
1274  else {
1275  NLogError("NGnNavigator::Draw: Pad %d is nullptr !!!", level + 1);
1276  }
1277  }
1278  gPad = originalPad; // Restore the original pad
1279 }
1280 
1281 void NGnNavigator::Paint(Option_t * /*option*/)
1282 {
1286  NLogInfo("NGnNavigator::Paint: Painting object ...");
1287  if (fProjection) {
1288  NLogDebug("NGnNavigator::Paint: Painting to pad=%d projection name=%s title=%s ...", fLevel + 1,
1289  fProjection->GetName(), fProjection->GetTitle());
1290  // fProjection->Paint(option);
1291  fProjection->Paint("colz text");
1292  }
1293 }
1294 
1295 Int_t NGnNavigator::DistancetoPrimitive(Int_t px, Int_t py)
1296 {
1300 
1301  if (!fProjection) return 9999; // Not drawn, so we are infinitely far.
1302 
1303  return fProjection->DistancetoPrimitive(px, py);
1304 }
1305 
1306 void NGnNavigator::ExecuteEvent(Int_t event, Int_t px, Int_t py)
1307 {
1312 
1313  // NLogInfo("NGnNavigator::ExecuteEvent: event=%d, px=%d, py=%d", event, px, py);
1314 
1315  if (!fProjection || !gPad) return;
1316 
1317  // gPad = gPad->GetMother();
1318  // NLogDebug("NGnNavigator::ExecuteEvent: event=%d, px=%d, py=%d, gPad=%s title=%s", event, px, py,
1319  // gPad->GetName(), gPad->GetTitle());
1320  // NLogDebug("NGnNavigator::ExecuteEvent: event=%d, px=%d, py=%d", event, px, py);
1321 
1322  // Step 1: Convert absolute pixel coordinates to the pad's normalized coordinates (0-1 range)
1323  Double_t x_pad = gPad->AbsPixeltoX(px);
1324  Double_t y_pad = gPad->AbsPixeltoY(py);
1325 
1326  // Step 2: Convert the pad's normalized coordinates to the user's coordinate system (the histogram axes)
1327  Double_t x_user = gPad->PadtoX(x_pad);
1328  Double_t y_user = gPad->PadtoY(y_pad);
1329 
1330  size_t bin = fProjection->FindBin(x_user, y_user);
1331 
1332  TVirtualPad * originalPad = gPad; // Save the original pad
1333  bool isBinChanged = (bin != fLastHoverBin);
1334  // --- MOUSE CLICK LOGIC ---
1335  bool isActionTriggered = (event == fTrigger);
1336 
1337  // trigger action only if hover bin changed or event is click
1338  isActionTriggered = isActionTriggered && (isBinChanged || event == kButton1Down);
1339 
1340  if (isActionTriggered) {
1341  Int_t binx, biny, binz;
1342  fProjection->GetBinXYZ(bin, binx, biny, binz);
1343  Double_t content = fProjection->GetBinContent(bin);
1344  NLogDebug("NGnNavigator::ExecuteEvent: [%s] Mouse trigger on bin=[%d, %d] at px=[%f, %f] with content: %f "
1345  "level=%d nLevels=%d",
1346  gPad->GetName(), binx, biny, x_user, y_user, content, fLevel, fNLevels);
1347 
1348  int nDimensions = fGnTree->GetBinning()->GetDefinition()->GetContent()->GetNdimensions();
1349 
1350  Int_t index = fProjection->FindFixBin(fProjection->GetXaxis()->GetBinCenter(binx),
1351  fProjection->GetYaxis()->GetBinCenter(biny));
1352  if (nDimensions == 1) {
1353  // For 1D histograms, we need to find the index correctly
1354  index = fProjection->GetXaxis()->FindFixBin(fProjection->GetXaxis()->GetBinCenter(binx));
1355  }
1356  NLogTrace("NGnNavigator::ExecuteEvent: Index in histogram: %d level=%d", index, fLevel);
1357  NGnNavigator * child = GetChild(index);
1358  TCanvas * cObject = (TCanvas *)gROOT->GetListOfCanvases()->FindObject("cObject");
1359  if (child && child->GetProjection()) {
1360  NLogTrace("NGnNavigator::ExecuteEvent: [%s]Child object '%p' found at index %d", gPad->GetName(),
1361  child->GetProjection(), index);
1362  // originalPad->Clear(); // Clear the original pad
1363  gPad = originalPad->GetMother(); // Get the mother pad to avoid clearing the current pad
1364  TVirtualPad * pad = gPad->cd(fLevel + 1 + 1); // Ensure we are in the correct pad
1365  pad->Clear(); // Clear the pad for the child object
1366 
1367  SetLastIndexSelected(index);
1368  TH1 * hProj = child->GetProjection();
1369  hProj->SetStats(kFALSE); // Turn off stats box for clarity
1370  hProj->SetMinimum(0); // Set minimum to 0 for better visibility
1371  hProj->Draw("text colz"); // Draw the projection histogram of the child
1372  child->AppendPad();
1373  NLogTrace("NGnNavigator::ExecuteEvent: %d", child->GetLastIndexSelected());
1374  if (cObject) {
1375  cObject->Clear(); // Clear the existing canvas if it exists
1376  cObject->cd(); // Set the current canvas to cObject
1377  // Add text with note about the projection
1378 
1379  TLatex latex;
1380  latex.SetNDC();
1381  latex.SetTextAlign(22); // center
1382  latex.SetTextSize(0.05);
1383  latex.DrawLatex(0.5, 0.5, "Select bottom pad to see projection");
1384  // delete cObject; // Delete the existing canvas if it exists
1385  }
1386  }
1387  else {
1388 
1389  // TH1 * projection = child->GetProjection();
1390  // index = projection->GetXaxis()->FindFixBin(projection->GetXaxis()->GetBinCenter(binx));
1391  NLogTrace("NGnNavigator::ExecuteEvent: No child object found at index %d", index);
1392  std::string objName = fObjectNames.empty() ? "resource_monitor" : fObjectNames[0];
1393  TH1 * hProj = (TH1 *)GetObject(objName, index);
1394  if (hProj == nullptr) {
1395  NLogError("NGnNavigator::ExecuteEvent: No histogram found for index %d", index);
1396  return;
1397  }
1398  // hProj->Print("all");
1399  if (cObject == nullptr) {
1400  cObject = new TCanvas("cObject", "cObject", 800, 600);
1401  }
1402  cObject->cd();
1403  // gPad = cObject->GetPad(0); // Get the current pad
1404  // hProj->SetTitle(Form("Projection of %s at index %d", fProjection->GetName(), index));
1405  if (hProj->GetXaxis()->IsAlphanumeric()) {
1406  TPaveText * pt = new TPaveText(0.15, 0.15, 0.85, 0.85);
1407  for (Int_t binx = 1; binx <= hProj->GetNbinsX(); ++binx) {
1408  std::string name = hProj->GetXaxis()->GetBinLabel(binx);
1409  std::string value = TString::Format("%.3f", hProj->GetBinContent(binx)).Data();
1410  std::string t = TString::Format("%s: %s", name.c_str(), value.c_str()).Data();
1411 
1412  pt->AddText(t.c_str());
1413  }
1414  pt->Draw();
1415  }
1416  else {
1417  hProj->Draw(); // Draw the projection histogram
1418  }
1419  }
1420  // else {
1421  // }
1422  gPad->ModifiedUpdate(); // Force pad to redraw
1423  }
1424  // --- MOUSE HOVER LOGIC ---
1425  if (event == kMouseMotion) {
1426  if (isBinChanged) {
1427  // Check if the cursor is inside a bin with content
1428  if (fProjection->GetBinContent(bin) > 0) {
1429  Int_t binx, biny, binz;
1430  fProjection->GetBinXYZ(bin, binx, biny, binz);
1431  NLogTrace("[%s] Mouse hover on bin[%d, %d] at px[%f, %f] level=%d nLevels=%d", gPad->GetName(), binx, biny,
1432  x_user, y_user, fLevel, fNLevels);
1433  }
1434  fLastHoverBin = bin;
1435  NLogTrace("[%s] Setting point for level %d %s", gPad->GetName(), fLevel, fProjection->GetTitle());
1436  }
1437  }
1438  gPad = originalPad; // Restore the original pad
1439 }
1440 
1442 {
1446  NLogTrace("NGnNavigator::GetChild: index=%d, size=%zu", index, fChildren.size());
1447  return (index < fChildren.size()) ? fChildren[index] : nullptr;
1448 }
1449 
1450 void NGnNavigator::SetChild(NGnNavigator * child, int index)
1451 {
1455  if (child) {
1456  fChildren[index < 0 ? fChildren.size() : index] = child;
1457  child->SetParent(this);
1458  }
1459 }
1460 
1461 NGnNavigator * NGnNavigator::GetChild(std::vector<std::vector<size_t>> coords) const
1462 {
1466 
1467  NGnNavigator * next;
1468 
1469  NBinningDef * binningDef = fGnTree->GetBinning()->GetDefinition();
1470  THnSparse * hnsObjContent = binningDef->GetContent();
1471 
1472  std::vector<std::vector<int>> ranges;
1473  NGnNavigator * root = GetRoot();
1474 
1475  // if (root != this) {
1476 
1477  NLogDebug("NGnNavigator::GetChild: Setting axis ranges for level=%d", fLevel);
1478  std::vector<int> levels = root->GetLevels()[fLevel];
1479  for (size_t i = 0; i < levels.size(); i++) {
1480  int coords_i = (int)coords[i][fLevel];
1481  ranges.push_back({levels[i], coords_i, coords_i}); // Initialize ranges vector
1482  NLogDebug("NGnNavigator::GetChild: level=%d axis=%d coord=%d", fLevel, levels[i], coords_i);
1483  }
1484  NUtils::SetAxisRanges(hnsObjContent, ranges, false, false, false);
1485 
1486  coords[0].resize(3, 0);
1487 
1488  size_t bin = fProjection->GetBin(coords[0][0], coords[0][1], coords[0][2]);
1489  NLogDebug("NGnNavigator::GetChild: coords=%s bin=%d", NUtils::GetCoordsString(coords[0]).c_str(), bin);
1490 
1491  next = GetChild(bin);
1492  if (!next) return nullptr;
1493  next->Print();
1494 
1495  return next;
1496 }
1497 
1498 // NGnNavigator * NGnNavigator::Open(TTree * tree, const std::string & branches, TFile * file)
1499 // {
1500 // ///
1501 // /// Open NGnNavigator from TTree
1502 // ///
1503 //
1504 // NGnTree * ngt = NGnTree::Open(tree, branches, file);
1505 //
1506 // NGnNavigator * navigator = new NGnNavigator();
1507 // if (!navigator) {
1508 // return nullptr;
1509 // }
1510 // navigator->SetGnTree(ngt);
1511 //
1512 // return navigator;
1513 // }
1514 //
1515 // NGnNavigator * NGnNavigator::Open(const std::string & filename, const std::string & branches,
1516 // const std::string & treename)
1517 // {
1518 // ///
1519 // /// Open NGnNavigator from file
1520 // ///
1521 // NGnTree * ngt = NGnTree::Open(filename, branches, treename);
1522 //
1523 // NGnNavigator * navigator = new NGnNavigator();
1524 // if (!navigator) {
1525 // return nullptr;
1526 // }
1527 // navigator->SetGnTree(ngt);
1528 //
1529 // return navigator;
1530 // }
1531 std::vector<TObject *> NGnNavigator::GetObjects(const std::string & name) const
1532 {
1534  auto it = fObjectContentMap.find(name);
1535  if (it != fObjectContentMap.end()) {
1536  return it->second;
1537  }
1538  return {};
1539 }
1540 
1541 TObject * NGnNavigator::GetObject(const std::string & name, int index) const
1542 {
1544  auto it = fObjectContentMap.find(name);
1545  if (it != fObjectContentMap.end() && index < (int)it->second.size()) {
1546  return it->second[index];
1547  }
1548  return nullptr;
1549 }
1550 
1551 void NGnNavigator::SetObject(const std::string & name, TObject * obj, int index)
1552 {
1556  if (obj) {
1557  if (fObjectContentMap.find(name) == fObjectContentMap.end()) {
1559  }
1560  NLogTrace("NGnNavigator::SetObject: name=%s, obj=%p, index=%d", name.c_str(), obj, index,
1561  fObjectContentMap[name].size());
1562 
1563  // Add object name if missing
1564  if (std::find(fObjectNames.begin(), fObjectNames.end(), name) == fObjectNames.end()) {
1565  fObjectNames.push_back(name);
1566  }
1567 
1568  if (index < 0) {
1569  fObjectContentMap[name].push_back(obj);
1570  }
1571  else {
1572  // if (index >= (int)fObjectContentMap[name].size()) {
1573  // fObjectContentMap[name].resize(index + 1, nullptr);
1574  // }
1575  // Delete any previous object at this slot before overwriting, so that
1576  // repeated SetObject calls at the same index don't leak the old pointer.
1577  delete fObjectContentMap[name][index];
1578  fObjectContentMap[name][index] = obj;
1579  }
1580  }
1581 }
1582 
1583 std::vector<double> NGnNavigator::GetParameters(const std::string & name) const
1584 {
1586  auto it = fParameterContentMap.find(name);
1587  if (it != fParameterContentMap.end()) {
1588  return it->second;
1589  }
1590  return {};
1591 }
1592 
1593 double NGnNavigator::GetParameter(const std::string & name, int index) const
1594 {
1596  auto it = fParameterContentMap.find(name);
1597  if (it != fParameterContentMap.end() && index < (int)it->second.size()) {
1598  return it->second[index];
1599  }
1600  return TMath::QuietNaN();
1601 }
1602 
1603 void NGnNavigator::SetParameter(const std::string & name, double value, int index)
1604 {
1608  if (!std::isnan(value)) {
1609  if (fParameterContentMap.find(name) == fParameterContentMap.end() || fParameterContentMap[name].size() < fNCells) {
1610  NLogTrace("NGnNavigator::SetParameter: Resizing parameter content map for '%s' to %d", name.c_str(), fNCells);
1612  }
1613  NLogTrace("NGnNavigator::SetParameter: name=%s, value=%f, index=%d", name.c_str(), value, index,
1614  fParameterContentMap[name].size());
1615 
1616  // Append parameter name if missing
1617  if (std::find(fParameterNames.begin(), fParameterNames.end(), name) == fParameterNames.end()) {
1618  fParameterNames.push_back(name);
1619  }
1620 
1621  if (index < 0) {
1622  fParameterContentMap[name].push_back(value);
1623  }
1624  else {
1625  fParameterContentMap[name][index] = value;
1626  }
1627  }
1628 }
1629 
1630 std::vector<double> NGnNavigator::GetParameterErrors(const std::string & name) const
1631 {
1633  auto it = fParameterErrorContentMap.find(name);
1634  if (it != fParameterErrorContentMap.end()) {
1635  return it->second;
1636  }
1637  return {};
1638 }
1639 
1640 double NGnNavigator::GetParameterError(const std::string & name, int index) const
1641 {
1643  auto it = fParameterErrorContentMap.find(name);
1644  if (it != fParameterErrorContentMap.end() && index < (int)it->second.size()) {
1645  return it->second[index];
1646  }
1647  return TMath::QuietNaN();
1648 }
1649 
1650 void NGnNavigator::SetParameterError(const std::string & name, double value, int index)
1651 {
1655  if (!std::isnan(value)) {
1656  if (fParameterErrorContentMap.find(name) == fParameterErrorContentMap.end() ||
1657  fParameterErrorContentMap[name].size() < fNCells) {
1658  NLogTrace("NGnNavigator::SetParameter: Resizing parameter content map for '%s' to %d", name.c_str(), fNCells);
1660  }
1661  NLogTrace("NGnNavigator::SetParameter: name=%s, value=%f, index=%d", name.c_str(), value, index,
1662  fParameterErrorContentMap[name].size());
1663 
1664  // Append parameter name if missing
1665  if (std::find(fParameterNames.begin(), fParameterNames.end(), name) == fParameterNames.end()) {
1666  fParameterNames.push_back(name);
1667  }
1668 
1669  if (index < 0) {
1670  fParameterErrorContentMap[name].push_back(value);
1671  }
1672  else {
1673  fParameterErrorContentMap[name][index] = value;
1674  }
1675  }
1676 }
1677 TList * NGnNavigator::DrawSpectraAll(std::string parameterName, std::vector<double> minmax,
1678  const std::string & minmaxMode, Option_t * option) const
1679 {
1683  std::vector<int> projIds;
1684  return DrawSpectra(parameterName, projIds, minmax, minmaxMode, option);
1685 }
1686 
1687 TList * NGnNavigator::DrawSpectraByName(std::string parameterName, std::vector<std::string> projAxes,
1688  std::vector<double> minmax, const std::string & minmaxMode,
1689  Option_t * option) const
1690 {
1694 
1695  std::vector<int> projIds;
1696  // lopp over fProjection axes and find the indices of projAxes then fill projIds and remove from projAxes
1697  for (const auto & axisName : projAxes) {
1698  TAxis * axis = nullptr;
1699  for (int i = 0; i < fProjection->GetDimension(); i++) {
1700  if (i == 0)
1701  axis = fProjection->GetXaxis();
1702  else if (i == 1)
1703  axis = fProjection->GetYaxis();
1704  else if (i == 2)
1705  axis = fProjection->GetZaxis();
1706 
1707  if (axis && axis->GetName() == axisName) {
1708  projIds.push_back(i);
1709  break;
1710  }
1711  }
1712  }
1713  if (projIds.empty()) {
1714  NLogError("NGnNavigator::DrawSpectra: No projection axes found for names: %s",
1715  NUtils::GetCoordsString(projAxes, -1).c_str());
1716  return nullptr;
1717  }
1718 
1719  if (projIds.size() > 3) {
1720  NLogError("NGnNavigator::DrawSpectra: Too many projection dimensions: %zu (max 3)", projIds.size());
1721  return nullptr;
1722  }
1723 
1724  if (projIds.size() != projAxes.size()) {
1725  NLogError("NGnNavigator::DrawSpectra: Not all projection axes found: %s",
1726  NUtils::GetCoordsString(projAxes, -1).c_str());
1727  return nullptr;
1728  }
1729 
1730  return DrawSpectra(parameterName, projIds, minmax, minmaxMode, option);
1731 }
1732 
1733 TList * NGnNavigator::DrawSpectra(std::string parameterName, std::vector<int> projIds, std::vector<double> minmax,
1734  const std::string & minmaxMode, Option_t * option) const
1735 {
1739 
1740  if (parameterName.empty()) {
1741  NLogError("NGnNavigator::DrawSpectra: Parameter name is empty");
1742  return nullptr;
1743  }
1744 
1745  // check if parameterName exists in fParameterContentMap
1746  if (fParameterContentMap.find(parameterName) == fParameterContentMap.end()) {
1747  NLogError("NGnNavigator::DrawSpectra: Parameter name '%s' not found in fParameterContentMap",
1748  parameterName.c_str());
1749  return nullptr;
1750  }
1751  Int_t screenWidth = gClient->GetDisplayWidth();
1752  Int_t screenHeight = gClient->GetDisplayHeight();
1753 
1754  // int padCounter = 0;
1755  TCanvas * c = nullptr;
1756  // Create a canvas that is, for example, 40% of the screen width and height
1757  constexpr double canvasScale = 0.2;
1758  Int_t canvasWidth = static_cast<Int_t>(screenWidth * canvasScale);
1759  Int_t canvasHeight = static_cast<Int_t>(screenHeight * canvasScale);
1760 
1761  if (canvasWidth < 800) canvasWidth = 800;
1762  if (canvasHeight < 600) canvasHeight = 600;
1763 
1764  NLogTrace("Screen size: %dx%d", screenWidth, screenHeight);
1765 
1766  NBinningDef * binningDef = fGnTree->GetBinning()->GetDefinition();
1767  THnSparse * hnsObjContent = binningDef->GetContent();
1768  // hnsObjContent->Print("all");
1769  std::vector<std::vector<int>> projections;
1770  if (projIds.empty()) {
1771  projIds.resize(fProjection->GetDimension());
1772  std::iota(projIds.begin(), projIds.end(), 0); // Fill projIds with 0, 1, 2, ..., n-1
1773  projections = Ndmspc::NUtils::Permutations(projIds);
1774  }
1775  else {
1776  projections.push_back(projIds);
1777  }
1778 
1779  if (projections.empty()) {
1780  NLogError("NGnNavigator::DrawSpectra: No projections found");
1781  return nullptr;
1782  }
1783  if (projections[0].size() > 3) {
1784  NLogError("NGnNavigator::DrawSpectra: Too many projection dimensions: %zu (max 3)", projections[0].size());
1785  return nullptr;
1786  }
1787 
1788  TList * outputList = new TList();
1789  for (const auto & proj : projections) {
1790 
1791  NLogTrace("Projection IDs: %s", NUtils::GetCoordsString(projIds, -1).c_str());
1792 
1793  // fProjection->Draw("colz");
1794  TH1 * hParameterProjection = (TH1 *)fProjection->Clone("hParameterProjection");
1795  // Detach from gDirectory so the TFile does not claim ownership and cause a double-free
1796  hParameterProjection->SetDirectory(nullptr);
1797  // hParameterProjection->Sumw2(kFALSE);
1798 
1799  // fill hParameterProjection from fParameterContentMap
1800  hParameterProjection->SetContent(GetParameters(parameterName).data());
1801 
1802  // NLogTrace("NGnNavigator::DrawSpectra: Setting parameter errors for '%s'", parameterName.c_str());
1803  // Print all errors
1804 
1805  hParameterProjection->SetError(GetParameterErrors(parameterName).data());
1806  // hParameterProjection->Draw("colz text");
1807  // return;
1808 
1809  // Create parameter THnSparse
1810  THnSparse * hsParam = THnSparse::CreateSparse(parameterName.c_str(), parameterName.c_str(), hParameterProjection);
1811 
1812  // append all labels from from fProjection to hs
1813  int nDimensions = hParameterProjection->GetDimension();
1814  for (int i = 0; i < nDimensions; i++) {
1815  TAxis * axis = nullptr;
1816  if (i == 0)
1817  axis = hParameterProjection->GetXaxis();
1818  else if (i == 1)
1819  axis = hParameterProjection->GetYaxis();
1820  else if (i == 2)
1821  axis = hParameterProjection->GetZaxis();
1822 
1823  hsParam->GetAxis(i)->SetName(axis->GetName());
1824  hsParam->GetAxis(i)->SetTitle(axis->GetTitle());
1825  if (axis->IsVariableBinSize()) {
1826  hsParam->GetAxis(i)->Set(axis->GetNbins(), axis->GetXbins()->GetArray());
1827  }
1828  if (axis->IsAlphanumeric()) {
1829  for (int j = 1; j <= axis->GetNbins(); j++) {
1830  const char * label = axis->GetBinLabel(j);
1831  hsParam->GetAxis(i)->SetBinLabel(j, label);
1832  }
1833  }
1834  }
1835 
1836  delete hParameterProjection;
1837 
1838  std::vector<int> dims;
1839 
1840  std::vector<int> currentLevels = GetRoot()->GetLevels()[fLevel];
1841  NLogTrace("Current levels: %s", NUtils::GetCoordsString(currentLevels, -1).c_str());
1842  for (auto & id : proj) {
1843  dims.push_back(currentLevels[id]);
1844  }
1845 
1846  if (dims.size() > 3) {
1847  NLogError("NGnNavigator::DrawSpectra: Too many projection dimensions: %zu (max 3)", dims.size());
1848  delete outputList;
1849  return nullptr;
1850  }
1851 
1852  NLogTrace("Projection dims: %s", NUtils::GetCoordsString(dims, -1).c_str());
1853 
1854  // hsParam->Print("all");
1855  // return;
1856 
1857  std::vector<std::set<int>> dimsResults(3);
1858 
1859  std::vector<std::vector<int>> ranges;
1860  for (int dim : dims) {
1861  int nBins = hnsObjContent->GetAxis(dim)->GetNbins();
1862  ranges.push_back({dim, 1, nBins}); // 1-based indexing for THnSparse
1863  }
1864 
1865  NUtils::SetAxisRanges(hnsObjContent, ranges, false, false, false);
1866  Int_t * p = new Int_t[hnsObjContent->GetNdimensions()];
1867  Long64_t linBin = 0;
1868  std::unique_ptr<ROOT::Internal::THnBaseBinIter> iter{hnsObjContent->CreateIter(true /*use axis range*/)};
1869  while ((linBin = iter->Next()) >= 0) {
1870  // NLogDebug("Linear bin: %lld", linBin);
1871  Double_t v = hnsObjContent->GetBinContent(linBin, p);
1872  Long64_t idx = hnsObjContent->GetBin(p);
1873  std::string binCoords = NUtils::GetCoordsString(NUtils::ArrayToVector(p, hnsObjContent->GetNdimensions()), -1);
1874  NLogTrace("Bin %lld(%lld): %f %s", linBin, idx, v, binCoords.c_str());
1875  dimsResults[0].insert(p[dims[0]]);
1876  if (dims.size() > 1) dimsResults[1].insert(p[dims[1]]);
1877  if (dims.size() > 2) dimsResults[2].insert(p[dims[2]]);
1878  }
1879 
1880  if (!gROOT->IsBatch()) {
1881  // Ensure gClient is initialized if not already
1882  if (!gClient) {
1883  // This will initialize gClient if needed
1884  (void)gClient;
1885  }
1886  }
1887 
1888  // if (dims.size() <= 2) {
1889  // dims.push_back(0); // Add a dummy dimension for 2D plotting
1890  // }
1891  int nPads = dims.size() > 2 ? dimsResults[2].size() : 1;
1892  NLogDebug("Number of pads: %d", nPads);
1893  std::vector<std::string> projNames;
1894  // hsParam->Print("all");
1895  NLogTrace("Projection dims: %d %d %d", dims[0], dims.size() > 1 ? dims[1] : -1, dims.size() > 2 ? dims[2] : -1);
1896  projNames.push_back(hnsObjContent->GetAxis(dims[0])->GetName());
1897  if (dims.size() > 1) projNames.push_back(hnsObjContent->GetAxis(dims[1])->GetName());
1898  if (dims.size() > 2) projNames.push_back(hnsObjContent->GetAxis(dims[2])->GetName());
1899 
1900  NLogDebug("Drawing %s ...", NUtils::GetCoordsString(projNames, -1).c_str());
1901 
1902  std::string posfix = NUtils::Join(projNames, '-');
1903 
1904  std::string canvasName = Form("c_%s", posfix.c_str());
1905  NLogTrace("Creating canvas '%s' with size %dx%d", canvasName.c_str(), canvasWidth, canvasHeight);
1906 
1907  // Delete existing canvas with same name to prevent crash during auto-deletion
1908  TCanvas * existingCanvas = (TCanvas *)gROOT->GetListOfCanvases()->FindObject(canvasName.c_str());
1909  if (existingCanvas) {
1910  NLogTrace("Deleting existing canvas '%s'", canvasName.c_str());
1911  delete existingCanvas;
1912  }
1913 
1914  c = new TCanvas(canvasName.c_str(), canvasName.c_str(), canvasWidth, canvasHeight);
1915  c->SetBit(kMustCleanup, kFALSE);
1916  outputList->Add(c);
1917  // c->DivideSquare(nPads);
1918 
1919  TList * stackList = new TList();
1920 
1921  for (int iPad = 0; iPad < nPads; iPad++) {
1922  // c->cd(iPad + 1);
1923 
1924  std::string stackName = Form("hStack_%s_%d", posfix.c_str(), iPad);
1925  std::string stackTitle = parameterName + " : ";
1926  stackTitle += projNames[0];
1927  stackTitle += projNames.size() > 1 ? " vs " + projNames[1] : "";
1928  if (proj.size() > 2) {
1929  p[proj[2]] = iPad + 1; // 1-based index for the third dimension
1930 
1931  // print projIds[2] range
1932  // NLogDebug("Pad %d: Setting projection indices: %d %d %d", iPad, p[0], p[1], p[2]);
1933  // NLogDebug("Pad %d: Setting projection indices: %d %d %d", iPad, projIds[0], projIds[1],
1934  // projIds[2]);
1935 
1936  TAxis * aPad = hsParam->GetAxis(proj[2]);
1937  if (aPad->IsAlphanumeric()) {
1938  stackTitle += projNames.size() > 2 ? " for " + projNames[2] + " [" + aPad->GetBinLabel(p[proj[2]]) + "]" : "";
1939  }
1940  else {
1941  double binLowEdge = aPad->GetBinLowEdge(p[proj[2]]);
1942  double binUpEdge = aPad->GetBinUpEdge(p[proj[2]]);
1943  stackTitle +=
1944  projNames.size() > 2 ? " for " + projNames[2] + " " + Form(" [%.3f,%.3f]", binLowEdge, binUpEdge) : "";
1945  }
1946  }
1947  NLogTrace("Creating stack '%s' with title '%s'", stackName.c_str(), stackTitle.c_str());
1948  // Delete any existing THStack with the same name to prevent a cascade crash
1949  // through ROOT's RecursiveRemove when the duplicate is added to gROOT's lists.
1950  // TObject * existingStack = gROOT->FindObject(stackName.c_str());
1951  // if (existingStack && existingStack->IsA() == THStack::Class()) {
1952  // NLogTrace("Deleting existing THStack '%s'", stackName.c_str());
1953  // delete existingStack;
1954  // }
1955  THStack * hStack = new THStack(stackName.c_str(), stackTitle.c_str());
1956  hStack->SetBit(kMustCleanup, kFALSE);
1957 
1958  int nStacks = proj.size() > 1 ? dimsResults[1].size() : 1;
1959  double stackMin = std::numeric_limits<double>::max();
1960  double stackMax = std::numeric_limits<double>::lowest();
1961  // Only support "VE" and "V" modes; default to "V" if unsupported
1962  std::string mode = (minmaxMode == "VE" || minmaxMode == "V" || minmaxMode == "D") ? minmaxMode : "V";
1963  for (int iStack = 0; iStack < nStacks; iStack++) {
1964  p[proj[0]] = 0;
1965  if (proj.size() > 1) p[proj[1]] = iStack + 1;
1966  std::vector<std::vector<int>> ranges;
1967  if (proj.size() > 2) ranges.push_back({proj[2], p[proj[2]], p[proj[2]]});
1968  if (proj.size() > 1) ranges.push_back({proj[1], p[proj[1]], p[proj[1]]});
1969  NUtils::SetAxisRanges(hsParam, ranges, true);
1970  TH1 * hProj = NUtils::ProjectTHnSparse(hsParam, {proj[0]}, option);
1971  if (!hProj || hProj->GetEntries() == 0) {
1972  NLogError("Failed to project THnSparse for stack %d with projection IDs: %s", iStack,
1973  NUtils::GetCoordsString(proj, -1).c_str());
1974  delete hProj;
1975  continue;
1976  }
1977  if (proj.size() > 1) {
1978  TAxis * aStack = hsParam->GetAxis(proj[1]);
1979  if (aStack->IsAlphanumeric()) {
1980  std::string label = aStack->GetBinLabel(p[proj[1]]);
1981  hProj->SetName(Form("%s_%s", aStack->GetName(), label.c_str()));
1982  hProj->SetTitle(Form("%s [%s]", aStack->GetName(), label.c_str()));
1983  }
1984  else {
1985  double binLowEdge = aStack->GetBinLowEdge(p[proj[1]]);
1986  double binUpEdge = aStack->GetBinUpEdge(p[proj[1]]);
1987  hProj->SetName(Form("%s_%.3f_%.3f", aStack->GetName(), binLowEdge, binUpEdge));
1988  hProj->SetTitle(Form("%s [%.3f,%.3f]", aStack->GetName(), binLowEdge, binUpEdge));
1989  }
1990  }
1991 
1992  // Track min/max according to mode
1993  for (int bin = 1; bin <= hProj->GetNbinsX(); ++bin) {
1994  double val = hProj->GetBinContent(bin);
1995  double err = hProj->GetBinError(bin);
1996  if (mode == "VE") {
1997  if (stackMin > val - err) stackMin = val - err;
1998  if (stackMax < val + err) stackMax = val + err;
1999  }
2000  else if (mode == "V") {
2001  if (stackMin > val) stackMin = val;
2002  if (stackMax < val) stackMax = val;
2003  }
2004  }
2005 
2006  hProj->SetMarkerStyle(20);
2007  hProj->SetMarkerColor(iStack + 1);
2008  // NLogDebug("Histogram '%s' entries: %lld", hProj->GetName(), hProj->GetEntries());
2009  TH1 * hProjClone = (TH1 *)hProj->Clone();
2010  // Detach clone from gDirectory to prevent TFile from owning it and causing a
2011  // double-free when THStack deletes it during canvas cleanup later.
2012  hProjClone->SetDirectory(nullptr);
2013  hStack->Add(hProjClone);
2014  // NLogTrace("Added histogram to stack: %s", hProj->GetTitle());
2015  delete hProj;
2016  }
2017 
2018  if (mode == "D") {
2019  hStack->SetMinimum(-1111);
2020  hStack->SetMaximum(-1111);
2021  }
2022  else {
2023  // Always set stackMin and stackMax to THStack, using value ± error for all bins
2024  if (minmax.size() > 0) {
2025  if (minmax.size() == 2) {
2026  stackMin = minmax[0];
2027  stackMax = minmax[1];
2028  }
2029  else if (minmax.size() == 1) {
2030  // minmax[0] is N percent (e.g., 0.05 for 5%)
2031  double percent = minmax[0];
2032  double center = 0.5 * (stackMax + stackMin);
2033  double halfRange = 0.5 * (stackMax - stackMin);
2034  double margin = percent * halfRange;
2035  stackMin = center - (halfRange + margin);
2036  stackMax = center + (halfRange + margin);
2037  }
2038  }
2039 
2040  // Safeguard: avoid wmin == wmax errors
2041  if (stackMin == stackMax) {
2042  if (stackMin == 0.0) {
2043  stackMin = 0.0;
2044  stackMax = 1.0;
2045  }
2046  else {
2047  stackMin -= 1.0;
2048  stackMax += 1.0;
2049  }
2050  }
2051  hStack->SetMinimum(stackMin);
2052  hStack->SetMaximum(stackMax);
2053  }
2054 
2055  if (hStack->GetNhists() == 0) {
2056  NLogError("No histograms were added to the stack for pad %d with projection IDs: %s", iPad,
2057  NUtils::GetCoordsString(proj, -1).c_str());
2058  continue;
2059  }
2060 
2061  // std::string drawOption = "nostack E";
2062  // drawOption += option;
2063  // NLogDebug("Drawing stack with option: %s in pad %d %d", drawOption.c_str(), iPad + 1, hStack->GetNhists());
2064  // hStack->Draw(drawOption.c_str());
2065  // hStack->GetHistogram()->GetXaxis()->SetTitle(projNames[0].c_str());
2066  // hStack->GetHistogram()->GetYaxis()->SetTitle(parameterName.c_str());
2067  // // gPad->ModifiedUpdate();
2068  // if (dims.size() > 1) gPad->BuildLegend(0.75, 0.75, 0.95, 0.95, "");
2069  stackList->Add(hStack);
2070  // c->ModifiedUpdate();
2071  // gSystem->ProcessEvents();
2072  }
2073  delete[] p;
2074  delete hsParam;
2075 
2076  if (stackList->GetEntries() > 0) {
2077  nPads = stackList->GetEntries();
2078  c->DivideSquare(nPads);
2079  for (int iPad = 0; iPad < nPads; iPad++) {
2080  c->cd(iPad + 1);
2081  THStack * hStack = (THStack *)stackList->At(iPad);
2082  if (hStack) {
2083  hStack->Draw("nostack E");
2084  NLogTrace("Drawing stack with option: %s in pad %d %d", "nostack E", iPad + 1, hStack->GetNhists());
2085  hStack->GetHistogram()->GetXaxis()->SetTitle(projNames[0].c_str());
2086  hStack->GetHistogram()->GetYaxis()->SetTitle(parameterName.c_str());
2087  if (dims.size() > 1) gPad->BuildLegend(0.75, 0.75, 0.95, 0.95, "");
2088  c->ModifiedUpdate();
2089  // gSystem->ProcessEvents();
2090  }
2091  }
2092  }
2093 
2094  stackList->Clear();
2095  delete stackList;
2096  }
2097 
2098  return outputList;
2099 }
2100 
2101 NGnNavigator * NGnNavigator::GetRoot() const
2102 {
2106 
2107  if (fParent == nullptr) {
2108  return const_cast<NGnNavigator *>(this);
2109  }
2110 
2111  NGnNavigator * current = fParent;
2112  while (current->GetParent() != nullptr) {
2113  current = current->GetParent();
2114  }
2115  return current;
2116 }
2117 
2118 } // namespace Ndmspc
Defines binning mapping and content for NDMSPC histograms.
Definition: NBinningDef.h:26
THnSparse * GetContent() const
Get the template content histogram.
Definition: NBinningDef.h:118
std::vector< int > GetVariableAxes() const
Get list of variable axes indices.
Definition: NBinningDef.h:71
NBinningDef * GetDefinition(const std::string &name="")
Get binning definition by name.
Definition: NBinning.cxx:1021
Executes a function over all points in an N-dimensional space, optionally in parallel.
void Execute(const std::function< void(const std::vector< int > &coords)> &func)
Execute a function over all coordinates in the N-dimensional space.
Navigator object for managing hierarchical data structures and projections.
Definition: NGnNavigator.h:22
void SetLevel(size_t l)
Set current level in hierarchy.
Definition: NGnNavigator.h:417
std::vector< TObject * > GetObjects(const std::string &name) const
Get objects by name.
void SetChild(NGnNavigator *child, int index=-1)
Set child navigator at index.
void ResizeParameterContentMap(const std::string &name, int n)
Resize parameter content map for a given name.
Definition: NGnNavigator.h:310
std::vector< std::string > GetParameterNames() const
Get parameter names managed by navigator.
Definition: NGnNavigator.h:375
void SetObject(const std::string &name, TObject *obj, int index=-1)
Set object by name and index.
void SetLevels(const std::vector< std::vector< int >> &levels)
Set the levels using a vector of vectors of integers.
Definition: NGnNavigator.h:429
void SetChildrenSize(size_t n)
Set number of children.
Definition: NGnNavigator.h:196
virtual TList * DrawSpectraByName(std::string parameterName, std::vector< std::string > projAxes, std::vector< double > minmax={0.05}, const std::string &minmaxMode="V", Option_t *option="") const
Draws spectra for the given parameter and projection axes.
virtual void ExecuteEvent(Int_t event, Int_t px, Int_t py) override
Handle execution of events (e.g., mouse, keyboard).
virtual void Print(Option_t *option="") const override
Print navigator information.
NGnNavigator * GetParent() const
Get parent navigator.
Definition: NGnNavigator.h:227
double GetParameterError(const std::string &name, int index=0) const
Retrieves a specific error value for a parameter.
void SetProjection(TH1 *h)
Set projection histogram.
Definition: NGnNavigator.h:393
NGnNavigator * fParent
Parent object.
Definition: NGnNavigator.h:495
virtual ~NGnNavigator()
Destructor.
std::vector< std::string > fParameterNames
Parameter names.
Definition: NGnNavigator.h:490
TH1 * GetProjection() const
Get projection histogram.
Definition: NGnNavigator.h:387
size_t fNLevels
Number of levels in the hierarchy.
Definition: NGnNavigator.h:499
virtual void Paint(Option_t *option="") override
Paint navigator objects.
void ResizeParameterErrorContentMap(const std::string &name, int n)
Resizes the error vector for a given parameter name.
Definition: NGnNavigator.h:346
virtual TList * DrawSpectra(std::string parameterName, std::vector< int > projIds, std::vector< double > minmax={0.05}, const std::string &minmaxMode="V", Option_t *option="") const
Draw spectra for a parameter.
virtual void Draw(Option_t *option="") override
Draw navigator objects.
std::map< std::string, std::vector< TObject * > > GetObjectContentMap() const
Get object content map.
Definition: NGnNavigator.h:249
void SetParameterError(const std::string &name, double value, int index=-1)
Sets a specific error value for a parameter.
std::vector< std::vector< int > > fLevels
Levels definition.
Definition: NGnNavigator.h:501
void SetLastIndexSelected(size_t idx)
Set last selected index.
Definition: NGnNavigator.h:453
TObject * GetObject(const std::string &name, int index=0) const
Get object by name and index.
std::vector< std::string > GetObjectNames() const
Get object names managed by navigator.
Definition: NGnNavigator.h:291
NGnNavigator * GetChild(size_t index) const
Get child navigator at index.
size_t fNCells
Number of cells in the projection histogram.
Definition: NGnNavigator.h:502
void Export(const std::string &filename, std::vector< std::string > objectNames, const std::string &wsUrl="", int timeoutMs=1000)
Export navigator data to file.
NGnNavigator(const char *name="GnNavigator", const char *title="Gn Navigator", std::vector< std::string > objectTypes={"TH1"})
Constructor.
NGnNavigator * Reshape(std::string binningName, std::vector< std::vector< int >> levels, size_t level=0, std::map< int, std::vector< int >> ranges={}, std::map< int, std::vector< int >> rangesBase={})
Reshape navigator using binning name and levels.
std::vector< std::string > fObjectTypes
Object types.
Definition: NGnNavigator.h:493
Int_t DistancetoPrimitive(Int_t px, Int_t py) override
Calculate distance to primitive for graphical selection.
void SetGnTree(NGnTree *tree)
Set NGnTree object pointer.
Definition: NGnNavigator.h:184
size_t fLastHoverBin
To avoid spamming the console on hover.
Definition: NGnNavigator.h:503
json GetInfoJson() const
Retrieves information about the navigator in JSON format.
NGnTree * fGnTree
! Pointer to the NGnTree
Definition: NGnNavigator.h:487
size_t GetLastIndexSelected() const
Get last selected index.
Definition: NGnNavigator.h:447
std::map< std::string, std::vector< double > > fParameterContentMap
Parameter content map.
Definition: NGnNavigator.h:491
virtual TList * DrawSpectraAll(std::string parameterName, std::vector< double > minmax={0.05}, const std::string &minmaxMode="V", Option_t *option="") const
Draws all spectra for the given parameter.
size_t fLevel
Level of the object in the hierarchy.
Definition: NGnNavigator.h:500
void ExportToJson(json &j, NGnNavigator *obj, std::vector< std::string > objectNames)
Export navigator data to JSON.
std::vector< NGnNavigator * > fChildren
Children objects.
Definition: NGnNavigator.h:496
void SetNLevels(size_t n)
Set number of levels in hierarchy.
Definition: NGnNavigator.h:405
void SetNCells(size_t n)
Set number of cells in projection histogram.
Definition: NGnNavigator.h:441
std::map< std::string, std::vector< double > > fParameterErrorContentMap
Parameter error content map.
Definition: NGnNavigator.h:492
Int_t fTrigger
last triggered event
Definition: NGnNavigator.h:505
void SetParent(NGnNavigator *parent)
Set parent navigator.
Definition: NGnNavigator.h:243
std::vector< std::string > fObjectNames
Object names.
Definition: NGnNavigator.h:488
void ResizeObjectContentMap(const std::string &name, size_t n)
Resize object content map for a given name.
Definition: NGnNavigator.h:256
void SetParameter(const std::string &name, double value, int index=-1)
Set parameter value by name and index.
double GetParameter(const std::string &name, int index=0) const
Get parameter value by name and index.
std::vector< double > GetParameters(const std::string &name) const
Get parameters by name.
std::map< std::string, std::vector< double > > GetParameterContentMap() const
Get parameter content map.
Definition: NGnNavigator.h:303
std::vector< std::vector< int > > GetLevels() const
Get the current levels as a vector of vectors of integers.
Definition: NGnNavigator.h:423
std::vector< NGnNavigator * > GetChildren() const
Get vector of child navigators.
Definition: NGnNavigator.h:190
std::map< std::string, std::vector< TObject * > > fObjectContentMap
Object content map.
Definition: NGnNavigator.h:489
NGnNavigator * GetRoot() const
Returns the root parent of this NGnNavigator.
TH1 * fProjection
Projection histogram.
Definition: NGnNavigator.h:498
std::vector< double > GetParameterErrors(const std::string &name) const
Retrieves the error vector for a given parameter name.
NBinning * GetBinning() const
Get pointer to binning object.
Definition: NGnTree.h:162
Int_t GetEntry(Long64_t entry, bool checkBinningDef=true)
Get entry by index.
Definition: NGnTree.cxx:930
virtual void Print(Option_t *option="") const override
Print tree information.
Definition: NGnTree.cxx:463
NStorageTree * GetStorageTree() const
Get pointer to storage tree object.
Definition: NGnTree.h:174
NParameters object.
Definition: NParameters.h:13
TH1D * GetHisto() const
Returns the associated histogram.
Definition: NParameters.h:84
NTreeBranch * GetBranch(const std::string &name)
Get pointer to NTreeBranch by name.
std::map< std::string, NTreeBranch > GetBranchesMap() const
Get map of branch names to NTreeBranch objects.
Definition: NStorageTree.h:129
NDMSPC tree branch object for managing ROOT TBranch and associated data.
Definition: NTreeBranch.h:18
int GetBranchStatus() const
Get branch status.
Definition: NTreeBranch.h:75
void SetBranchStatus(int status)
Set branch status.
Definition: NTreeBranch.h:114
static void GetTrueHistogramMinMax(const TH1 *h, double &min_val, double &max_val, bool include_overflow_underflow=false)
Get minimum and maximum value of histogram bins.
Definition: NUtils.cxx:618
static bool SetAxisRanges(THnSparse *sparse, std::vector< std::vector< int >> ranges={}, bool withOverflow=false, bool modifyTitle=false, bool reset=true)
Set axis ranges for THnSparse using vector of ranges.
Definition: NUtils.cxx:1075
static TH1 * ProjectTHnSparse(THnSparse *hns, const std::vector< int > &axes, Option_t *option="")
Project a THnSparse histogram onto specified axes.
Definition: NUtils.cxx:1022
static bool SaveRawFile(std::string filename, std::string content)
Save content to a raw file.
Definition: NUtils.cxx:754
static bool GetAxisRangeInBase(TAxis *a, int rebin, int rebin_start, int bin, int &min, int &max)
Get axis range in base for rebinned axis.
Definition: NUtils.cxx:1193
static std::string Join(const std::vector< std::string > &values, const char delim=',')
Join vector of strings into a single string with delimiter.
Definition: NUtils.cxx:966
static std::string GetCoordsString(const std::vector< int > &coords, int index=-1, int width=0)
Get string representation of coordinates.
Definition: NUtils.cxx:1443
static std::vector< int > ArrayToVector(Int_t *v1, int size)
Convert array to vector.
Definition: NUtils.cxx:1406
static std::vector< std::vector< int > > Permutations(const std::vector< int > &v)
Generate all permutations of a vector.
Definition: NUtils.cxx:1494
WebSocket client for asynchronous communication using libwebsockets.
Definition: NWsClient.h:49
bool Connect(const std::string &uriString)
Connect to a WebSocket server.
Definition: NWsClient.cxx:148
void Disconnect()
Disconnect from the WebSocket server.
Definition: NWsClient.cxx:256
bool Send(const std::string &message)
Send a message to the server.
Definition: NWsClient.cxx:300