ndmspc  v1.2.0-0.1.rc3
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  // Handle special THnSparse reserved first cell without looping: use
399  // GetBinContent(0, coords) which fills coords for the linear bin 0.
400  {
401  Int_t nd = hnsIn->GetNdimensions();
402  Int_t * firstCoord = new Int_t[nd];
403  // Double_t firstVal =
404  content = hnsIn->GetBinContent(0, firstCoord); // fills firstCoord
405  // If bin 0 exists (firstVal may be 0.0), map its coordinates to projection
406  // bin indices and increment that projection bin by +1.
407  if (firstCoord) {
408  if (nDims == 1) {
409  int bx = firstCoord[axesIds[0]];
410  if (content < 0.5 && bx >= 1 && bx <= hProj->GetNbinsX())
411  hProj->SetBinContent(bx, hProj->GetBinContent(bx) + 1.0);
412  }
413  else if (nDims == 2) {
414  int bx = firstCoord[axesIds[0]];
415  int by = firstCoord[axesIds[1]];
416  if (content < 0.5 && bx >= 1 && bx <= hProj->GetNbinsX() && by >= 1 && by <= hProj->GetNbinsY())
417  hProj->SetBinContent(bx, by, hProj->GetBinContent(bx, by) + 1.0);
418  }
419  else if (nDims == 3) {
420  int bx = firstCoord[axesIds[0]];
421  int by = firstCoord[axesIds[1]];
422  int bz = firstCoord[axesIds[2]];
423  if (content < 0.5 && bx >= 1 && bx <= hProj->GetNbinsX() && by >= 1 && by <= hProj->GetNbinsY() &&
424  bz >= 1 && bz <= hProj->GetNbinsZ())
425  hProj->SetBinContent(bx, by, bz, hProj->GetBinContent(bx, by, bz) + 1.0);
426  }
427  }
428  delete[] firstCoord;
429  }
430 
431  NLogTrace("NGnNavigator::Reshape: [L%d] Projection histogram '%s' for coords=%s index=%d", level,
432  hProj->GetTitle(), NUtils::GetCoordsString(coords, -1).c_str(), indexInProj);
433  //
434  // hProj->SetMinimum(0);
435  // hProj->SetStats(kFALSE);
436  // hProj->Draw();
437  // gPad->ModifiedUpdate();
438  // gSystem->Sleep(1000);
439  // }
440  // hProj->Print();
441  // hProj->Draw("colz text");
442  // `current` is shared across all bin iterations at this level. The
443  // projection is identical for every iteration (ranges are fixed), so
444  // only store it once. Subsequent iterations delete the duplicate and
445  // reuse the already-stored pointer so that `nCells` below stays valid.
446  if (current->GetProjection() == nullptr) {
447  current->SetProjection(hProj);
448  }
449  else {
450  delete hProj;
451  hProj = current->GetProjection();
452  }
454 
455  std::map<int, std::vector<int>> rangesTmp = ranges;
456  std::map<int, std::vector<int>> rangesBaseTmp = rangesBase;
457  for (auto & kv : rangesBaseTmp) {
458  std::vector<int> range = rangesTmp[kv.first];
459  NLogTrace("NGnNavigator::Reshape: [L%d] Axis %d[%s]: rangeBase=%s range=%s", level, kv.first,
460  hnsIn->GetAxis(kv.first)->GetName(), NUtils::GetCoordsString(kv.second).c_str(),
461  NUtils::GetCoordsString(range).c_str());
462  }
463  int minBase = 0, maxBase = 0;
464  int i = 0;
465  for (auto & c : coords) {
466  // NLogDebug("Coordinate: %d v=%d axis=%d", i, coords[i], axes[i]);
467  NUtils::GetAxisRangeInBase(hnsIn->GetAxis(axesIds[i]), c, c, binningDef->GetBinning()->GetAxes()[axesIds[i]],
468  minBase, maxBase);
469  NLogTrace("NGnNavigator::Reshape: Axis %d: minBase=%d maxBase=%d", axesIds[i], minBase, maxBase);
470  rangesTmp[axesIds[i]] = {c, c}; // Set the range for the first axis
471  rangesBaseTmp[axesIds[i]] = {minBase, maxBase}; // Set the range for the first axis
472  i++;
473  }
474 
475  if (hProj == nullptr) {
476  NLogError("NGnNavigator::Reshape: Projection histogram is null for level %d !!!", level);
477  return;
478  }
479 
480  size_t nCells = hProj->GetNcells();
481 
482  // NGnNavigator * o = fParent->GetChild(indexInProj);
483  NGnNavigator * currentChild = current->GetChild(indexInProj);
484  if (currentChild == nullptr) {
485  NLogTrace("NGnNavigator::Reshape: [L%d] Creating new child for index %d nCells=%d ...", level, indexInProj,
486  nCells);
487  std::string childName = TString::Format("%s_L%zu_C%d", GetName(), level + 1, indexInProj).Data();
488  currentChild = new NGnNavigator(childName.c_str(), childName.c_str());
489  currentChild->SetLevel(level + 1);
490  currentChild->SetNLevels(levels.size());
491  currentChild->SetParent(current);
492  currentChild->SetGnTree(fGnTree);
493  // o = new NGnNavigator(hns->GetListOfAxes());
494  // if (fParent->GetChildren().size() != nCells) fParent->SetChildrenSize(nCells);
495  // fParent->SetChild(o, indexInProj); // Set the child at the index
496  // fParent = o;
497  // INFO: it was moved down
498  // if (current->GetChildren().size() != nCells) current->SetChildrenSize(nCells);
499  // current->SetChild(currentChild, indexInProj); // Set the child at the index
500  // currentChild->Print();
501  }
502  else {
503  NLogError("NGnNavigator::Reshape: [L%d] Using existing child for index %d [NOT OK] ...", level, indexInProj);
504  }
505 
506  // currentChild->Print();
507  // return;
508 
509  if (level == levels.size() - 1) {
510  NLogTrace("NGnNavigator::Reshape: [L%d] Filling projections from all branches %s for ranges:", level,
511  NUtils::GetCoordsString(levels[level]).c_str());
512  for (auto & kv : rangesBaseTmp) {
513  std::vector<int> range = rangesTmp[kv.first];
514  NLogTrace("NGnNavigator::Reshape: [L%d] Axis %d ['%s']: rangeBase=%s range=%s", level, kv.first,
515  binningDef->GetContent()->GetAxis(kv.first)->GetName(), NUtils::GetCoordsString(kv.second).c_str(),
516  NUtils::GetCoordsString(range).c_str());
517  // rangesTmp[kv.first] = range;
518  }
519  // Get projectiosn histograms from all branches
520 
521  // Int_t * cCoords = new Int_t[hns->GetNdimensions()];
522  Long64_t linBin = 0;
523 
524  // hns->Print("all");
525  NUtils::SetAxisRanges(hnsIn, rangesTmp); // Set the ranges for the axes
526  std::unique_ptr<ROOT::Internal::THnBaseBinIter> iter{hnsIn->CreateIter(true /*use axis range*/)};
527  std::vector<int> linBins;
528 
529  // loop over all bins in the sparse
530 
531  while ((linBin = iter->Next()) >= 0) {
532  NLogTrace("NGnNavigator::Reshape: [L%d] Found bin %lld [%lld]", level, linBin, binningDef->GetId(linBin));
533  linBins.push_back(binningDef->GetId(linBin));
534  }
535  if (linBins.empty()) {
536  NLogTrace("NGnNavigator::Reshape: [L%d] No bins found for the given ranges, skipping ...", level);
537  // continue;
538  return; // No bins found, nothing to process
539  }
540  // // bool skipBin = false; // Skip bin if no bins are found
541  NLogTrace("NGnNavigator::Reshape: Branch object Point coordinates: %s",
542  NUtils::GetCoordsString(linBins, -1).c_str());
543  current->SetNCells(nCells);
544 
545  for (int lb : linBins) {
546  fGnTree->GetEntry(lb);
547  for (auto & [key, val] : fGnTree->GetStorageTree()->GetBranchesMap()) {
548  if (val.GetBranchStatus() == 0) {
549  NLogTrace("NGnNavigator::Reshape: [L%d] Branch '%s' is disabled, skipping ...", level, key.c_str());
550  continue; // Skip disabled branches
551  }
552  NLogTrace("NGnNavigator::Reshape: [L%d] Processing branch '%s' with %zu objects to loop ...", level,
553  key.c_str(), linBins.size());
554 
555  // if (obj->GetParent()->GetObjectContentMap()[key].size() != nCells)
556  // obj->GetParent()->ResizeObjectContentMap(key, nCells);
557 
558  // if (skipBin) continue; // Skip processing if the previous bin was skipped
559 
563  // obj->GetParent()->SetNCells(nCells);
564  // fParent->SetNCells(nCells);
565  TString className = val.GetObjectClassName();
566  if (className.BeginsWith("THnSparse")) {
567  // // if (obj->GetParent()->GetObjectContentMap()[key].size() != nCells)
568  // // obj->GetParent()->ResizeObjectContentMap(key, nCells);
569  // // TODO: Make it configurable
570  // int projectionAxis = 0;
571  //
572  // TH1 * hProjTmp = nullptr;
573  // // loop over all linBins
574  // for (int lb : linBins) {
575  // fGnTree->GetEntry(lb);
576  // if (hProjTmp == nullptr) {
577  // hProjTmp = ProjectionFromObject(key, projectionAxis, rangesBaseTmp);
578  // // NLogDebug("AAAA %.0f", hProjTmp ? hProjTmp->GetEntries() : 0);
579  // }
580  // else {
581  // TH1 * temp = ProjectionFromObject(key, projectionAxis, rangesBaseTmp);
582  // // NLogDebug("BBBB %.0f", temp ? temp->GetEntries() : 0);
583  // if (temp) {
584  // hProjTmp->Add(temp);
585  // delete temp; // Delete the temporary histogram to avoid memory leaks
586  // }
587  // }
588  // }
589  // if (!hProjTmp) {
590  // // skipBin = true; // Skip bin if no histogram is created
591  // continue;
592  // }
593  // // generate histogram title from axis base ranges
594  // std::string title = "Projection of " + key + " ";
595  // for (const auto & [axis, range] : rangesBaseTmp) {
596  // TAxis * a = fBinning->GetAxes()[axis];
597  //
598  // title +=
599  // TString::Format("%s[%.2f,%.2f]", a->GetName(), a->GetBinLowEdge(range[0]),
600  // a->GetBinUpEdge(range[1]));
601  // }
602  // hProjTmp->SetTitle(title.c_str());
603  // NLogTrace("[L%d] Projection histogram '%s' for branch '%s' storing with indexInProj=%d,
604  // entries = % .0f ",
605  // level,
606  // hProjTmp->GetTitle(), key.c_str(), indexInProj, hProjTmp->GetEntries());
607  // obj->GetParent()->SetObject(key, hProjTmp, indexInProj);
608  // // hProjTmp->Draw();
609  // // gPad->Update();
610  // // gPad->Modified();
611  // // gSystem->ProcessEvents();
612  // // TODO: We may to set entries from projection histogram to the bin content of mapping file
613  }
614  else if (className.BeginsWith("TList")) {
615  NLogTrace("[L%d] Branch '%s' is a TList, getting object at index %d ...", level, key.c_str(),
616  indexInProj);
617  TList * list = dynamic_cast<TList *>(val.GetObject());
618  // list->Print();
619  // get list of object names
620  std::vector<std::string> objNames;
621  for (int i = 0; i < list->GetEntries(); i++) {
622  TObject * o = list->At(i);
623  objNames.push_back(o->GetName());
624  }
625  // remove "results" histogram
626  // objNames.erase(std::remove(objNames.begin(), objNames.end(), "_params"), objNames.end());
627 
628  NLogTrace("[L%d] Branch '%s' TList contains %d objects: %s", level, key.c_str(), list->GetEntries(),
629  NUtils::GetCoordsString(objNames).c_str());
630 
631  // std::vector<std::string> possibleNames = {"hPeak", "hBgNorm", "unlikepm_proj_0"};
632 
633  // current->SetObjectNames(objNames);
634  bool isValid = true;
635  for (auto & name : objNames) {
636  TH1 * hProjTmp = dynamic_cast<TH1 *>(list->FindObject(name.c_str()));
637  if (hProjTmp == nullptr) {
638  NLogTrace("NGnNavigator::Reshape::Warning Branch '%s' TList does not contain '%s' as TH1 !!!",
639  key.c_str(), name.c_str());
640  isValid = false;
641 
642  continue;
643  }
644  if (TMath::IsNaN(hProjTmp->GetEntries()) || TMath::IsNaN(hProjTmp->GetSumOfWeights())) {
645  NLogWarning("NGnNavigator::Reshape: Branch '%s' '%s' histogram is nan !!!", key.c_str(),
646 
647  name.c_str());
648  isValid = false;
649  continue;
650  }
651  NLogTrace(
652  "[L%d] Histogram name='%s' title='%s' for branch '%s' storing with indexInProj=%d, entries=%.0f",
653  level, name.c_str(), hProjTmp->GetTitle(), key.c_str(), indexInProj, hProjTmp->GetEntries());
654  // if (obj->GetParent()->GetObjectContentMap()[name].size() != nCells)
655  // obj->GetParent()->ResizeObjectContentMap(name, nCells);
656  // // obj->GetParent()->SetObject(key, hProjTmp, indexInProj);
657  // obj->GetParent()->SetObject(name, hProjTmp, indexInProj);
658  // if (fParent->GetObjectContentMap()[name].size() != nCells) fParent->ResizeObjectContentMap(name,
659  // nCells); fParent->SetObject(name, hProjTmp, indexInProj);
660  if (current->GetObjectContentMap()[name].size() != nCells)
661  current->ResizeObjectContentMap(name, nCells);
662  // Clone the histogram so the navigator owns it independently of the TTree branch
663  // buffer. ROOT clears TList branch objects on every GetEntry() call, which would
664  // leave the stored pointer dangling if we don't clone here.
665  TH1 * hClone = (TH1 *)hProjTmp->Clone();
666  hClone->SetDirectory(nullptr);
667  current->SetObject(name, hClone, indexInProj);
668  }
669  if (isValid == false) {
670  NLogTrace("NGnNavigator::Reshape::Warning: Branch '%s' TList does not contain any valid histograms !!!",
671  key.c_str());
672  continue;
673  }
674  }
675  else if (className.BeginsWith("Ndmspc::NParameters")) {
676 
677  NParameters * parameters = dynamic_cast<NParameters *>(val.GetObject());
678  if (parameters) {
679  TH1 * hParams = parameters->GetHisto();
680  if (hParams) {
681  // hParams->Print("all");
682  NLogTrace("[L%d] Branch '%s' Point contains '_params' histogram with %.0f entries ...", level,
683  key.c_str(), hParams->GetEntries());
684  // loop over bin labels
685  for (int b = 1; b <= hParams->GetNbinsX(); b++) {
686 
687  std::string binLabel = hParams->GetXaxis()->GetBinLabel(b);
688  double binValue = hParams->GetBinContent(b);
689  double binError = hParams->GetBinError(b);
690  NLogTrace("[L%d] Bin %d[%s] = %e indexInProj=%d", level, b, binLabel.c_str(), binValue,
691  indexInProj);
692  // // check if binlabel is "mass"
693  // if (binLabel.compare("mass") == 0) {
694  // NLogInfo("[L%d] Checking bin 'mass' = %f ...", level, binValue);
695  // if (binValue < 1.015 || binValue > 1.025) {
696  // NLogInfo("[L%d] Skipping bin 'mass' with value %f ...", level, binValue);
697  // continue;
698  // }
699  // }
700  // obj->GetParent()->SetParameter(binLabel, binValue, indexInProj);
701  // fParent->SetParameter(binLabel, binValue, indexInProj);
702  current->SetParameter(binLabel, binValue, indexInProj);
703  current->SetParameterError(binLabel, binError, indexInProj);
704  NLogTrace("[L%d] Stored parameter '%s' = %e +/- %e at indexInProj=%d", level, binLabel.c_str(),
705  binValue, binError, indexInProj);
706  }
707  }
708  }
709  else {
710  NLogWarning("NGnNavigator::Reshape: Branch '%s' Point parameters object is null !!!", key.c_str());
711  }
712  }
713  else {
714  NLogWarning("NGnNavigator::Reshape: Branch '%s' has unsupported class '%s' !!! Skipping ...", key.c_str(),
715  className.Data());
716  }
717  }
718  }
719 
720  // Reshape(binningDef, levels, level + 1, rangesTmp, rangesBaseTmp, currentChild);
721  // return;
722  }
723  if (current->GetChildren().size() != nCells) current->SetChildrenSize(nCells);
724  current->SetChild(currentChild, indexInProj); // Set the child at the index
725  // execute next child
726  Reshape(binningDef, levels, level + 1, rangesTmp, rangesBaseTmp, currentChild);
727  };
728  executorBin.Execute(loop_task_bin);
729  }
730  else {
731  NLogTrace("NGnNavigator::Reshape: Reached the end of levels, level=%d", level);
732  std::vector<std::vector<int>> rangesEmpty;
733  NUtils::SetAxisRanges(binningDef->GetContent(), rangesEmpty); // Set the ranges for the axes
734 
735  return current;
736  }
737 
738  NLogTrace("NGnNavigator::Reshape: =========== Reshaping navigator for level %d DONE ================", level);
739 
740  if (level == 0) {
741  NLogInfo("NGnNavigator::Reshape: Reshaping navigator DONE.");
742  // print exported axes from indexes from levels
743  for (size_t l = 0; l < levels.size(); l++) {
744  std::string axesStr = "";
745  for (auto & a : levels[l]) {
746  TAxis * axis = binningDef->GetContent()->GetAxis(a);
747  axesStr += TString::Format("%d('%s') ", a, axis->GetName()).Data();
748  }
749  NLogInfo(" Level %zu axes: %s", l, axesStr.c_str());
750  }
751  }
752 
753  if (branch) branch->SetBranchStatus(outputPointStatus);
754 
755  // current->Print("");
756 
757  return current;
758 }
759 
760 void NGnNavigator::Export(const std::string & filename, std::vector<std::string> objectNames, const std::string & wsUrl,
761  int timeoutMs)
762 {
766  NLogInfo("Exporting NGnNavigator to file: %s", filename.c_str());
767 
768  json objJson;
769 
770  // if filename ends with .root, remove it
771  if (filename.size() > 5 && filename.substr(filename.size() - 5) == ".root") {
772  NLogInfo("Exporting NGnNavigator to ROOT file: %s", filename.c_str());
773  TFile * file = TFile::Open(filename.c_str(), "RECREATE");
774  if (!file || file->IsZombie()) {
775  NLogError("Failed to open file: %s", filename.c_str());
776  return;
777  }
778  file->cd();
779  this->Write();
780  file->Close();
781  delete file;
782  }
783  else if (filename.size() > 5 && filename.substr(filename.size() - 5) == ".json") {
784  NLogInfo("Exporting NGnNavigator to JSON file: %s", filename.c_str());
785  NGnNavigator * obj = const_cast<NGnNavigator *>(this);
786  ExportToJson(objJson, obj, objectNames);
787  // std::cout << objJson.dump(2) << std::endl;
788  bool rc = NUtils::SaveRawFile(filename, objJson.dump());
789  if (rc == false) {
790  NLogError("Failed to save JSON file: %s", filename.c_str());
791  return;
792  }
793  NLogInfo("Exported NGnNavigator to file: %s", filename.c_str());
794  }
795  else if (filename.empty()) {
796  NLogInfo("No filename provided, export to JSON only ...");
797  NGnNavigator * obj = const_cast<NGnNavigator *>(this);
798  ExportToJson(objJson, obj, objectNames);
799  std::cout << objJson.dump() << std::endl;
800  }
801  else {
802  NLogError("Unsupported file format for export: %s", filename.c_str());
803  return;
804  }
805  if (!wsUrl.empty()) {
806  NLogInfo("Uploading exported file to web socket: %s", wsUrl.c_str());
807  // NUtils::UploadFileToWebService(filename, wsUrl);
808 
809  std::string message = objJson.dump();
810  Ndmspc::NWsClient client;
811  if (!message.empty()) {
812  NLogInfo("Connecting to web socket: %s", wsUrl.c_str());
813  if (!client.Connect(wsUrl)) {
814  NLogError("Failed to connect to '%s' !!!", wsUrl.c_str());
815  }
816  else {
817 
818  if (!client.Send(objJson.dump())) {
819  NLogError("Failed to send message `%s`", message.c_str());
820  }
821  else {
822  NLogInfo("Successfully sent message to '%s'", wsUrl.c_str());
823  }
824  }
825  if (timeoutMs > 0) {
826  NLogInfo("Waiting %d ms before disconnecting ...", timeoutMs);
827  gSystem->Sleep(timeoutMs); // wait for a while to ensure message is sent
828  }
829  NLogInfo("Disconnecting from '%s' ...", wsUrl.c_str());
830  client.Disconnect();
831  }
832 
833  NLogInfo("Sent: %s", message.c_str());
834  }
835 }
836 
837 void NGnNavigator::ExportToJson(json & j, NGnNavigator * obj, std::vector<std::string> objectNames)
838 {
842 
843  if (obj == nullptr) {
844  NLogError("NGnNavigator::ExportJson: Object is nullptr !!!");
845  return;
846  }
847 
848  if (obj->GetChildren().empty()) {
849  return;
850  }
851 
852  TH1 * h = obj->GetProjection();
853  if (h == nullptr) {
854  NLogError("NGnNavigator::ExportJson: Projection is nullptr !!!");
855  return;
856  }
857 
858  // h->SetNameTitle(name.c_str(), title.c_str());
859  h->SetMinimum(0);
860  h->SetStats(kFALSE); // Turn off stats box for clarity
861  // h->SetDirectory(nullptr); // Avoid ROOT trying to save the histogram in a file
862 
863  // Store TString in a named variable: in Cling's interpreted mode, TString temporaries
864  // can be destroyed before json::parse() reads the char* pointer, causing a segfault.
865  TString hJsonStr = TBufferJSON::ConvertToJSON(h);
866  j = json::parse(hJsonStr.Data());
867  // loop over content map and add objects
868  double entries = 0.0;
869  // int idx = 0;
870  if (objectNames.empty()) {
871  // NLogDebug("NGnNavigator::ExportJson: Exporting all objects ...");
872  // loop over all keys and add them to objectNames
873  bool isValid = false;
874  for (const auto & [key, val] : obj->GetObjectContentMap()) {
875  isValid = false;
876  for (size_t i = 0; i < val.size(); i++) {
877  TObject * objContent = val[i];
878  // NLogDebug("NGnNavigator::ExportJson: Processing object '%s' at index %zu ...", key.c_str(), i);
879  if (objContent) {
880  // check if object type is inherited from list of names in objectTypes
881  std::string className = objContent ? objContent->ClassName() : "";
882  if (className.empty()) {
883  NLogWarning("NGnNavigator::ExportJson: Object %s has empty class name", key.c_str());
884  continue;
885  }
886  // shrink className string to 3 characters if it is longer than 3
887  className = className.substr(0, 3);
888  // NLogDebug("NGnNavigator::ExportJson: Object %s has class '%s'", key.c_str(), className.c_str());
889  if (std::find(NGnNavigator::fObjectTypes.begin(), NGnNavigator::fObjectTypes.end(), className) !=
891  // NLogWarning(
892  // "NGnNavigator::ExportJson: Skipping unsupported object type '%s' for object '%s' at index %zu",
893  // className.c_str(), key.c_str(), i);
894  isValid = true;
895  break;
896  }
897  }
898  }
899  if (isValid) objectNames.push_back(key);
900  }
901  }
902  else {
903  NLogDebug("NGnNavigator::ExportJson: Exporting selected objects: %s", NUtils::GetCoordsString(objectNames).c_str());
904  }
905 
906  // Print all included object names
907  for (const auto & name : objectNames) {
908  NLogTrace("NGnNavigator::ExportJson: Included object name: '%s'", name.c_str());
909  }
910 
911  for (const auto & [key, val] : obj->GetObjectContentMap()) {
912 
913  NLogTrace("NGnNavigator::ExportJson: Processing object '%s' with %zu entries ...", key.c_str(), val.size());
914  // Filter by objectNames
915  if (std::find(objectNames.begin(), objectNames.end(), key) == objectNames.end()) {
916  NLogDebug("NGnNavigator::ExportJson: Skipping object '%s' ...", key.c_str());
917  continue;
918  }
919 
920  double min = std::numeric_limits<double>::max(); // Initialize with largest possible double
921  double max = -std::numeric_limits<double>::max(); // Initialize with smallest possible double
922  entries = 0.0; // Reset entries for each key
923 
924  for (size_t i = 0; i < val.size(); i++) {
925  TObject * objContent = val[i];
926 
927  if (objContent) {
928  // Runtime check: Only cast if objContent inherits from TH1
929  TH1 * hist = dynamic_cast<TH1 *>(objContent);
930  if (hist) {
931  // Store TString in a named variable to avoid Cling temporary lifetime issue.
932  TString histJsonStr = TBufferJSON::ConvertToJSON(hist);
933  json objJson = json::parse(histJsonStr.Data());
934  double objMin, objMax;
935  NUtils::GetTrueHistogramMinMax(hist, objMin, objMax, false);
936  min = TMath::Min(min, objMin);
937  max = TMath::Max(max, objMax);
938  entries = hist->GetEntries();
939  j["fArray"][i] = entries;
940  if (entries > 0) {
941  j["children"][key].push_back(objJson);
942  }
943  else {
944  j["children"][key].push_back(nullptr);
945  }
946  }
947  else {
948  NLogWarning("NGnNavigator::ExportJson: Object %s at index %zu is not a TH1, skipping.", key.c_str(), i);
949  entries = 0.0;
950  j["children"][key].push_back(nullptr);
951  }
952  }
953  else {
954  entries = 0.0;
955  j["children"][key].push_back(nullptr);
956  }
957  }
958  }
959 
960  for (auto & [key, val] : obj->GetParameterContentMap()) {
961  double min = std::numeric_limits<double>::max(); // Initialize with largest possible double
962  double max = -std::numeric_limits<double>::max(); // Initialize with smallest possible double
963  entries = 0.0; // Reset entries for each key
964 
965  for (size_t i = 0; i < val.size(); i++) {
966  double param = val[i];
967  double paramError = 0.0;
968  // Runtime check: Only call GetParameterError if index is valid
969  if (i < val.size()) {
970  try {
971  paramError = obj->GetParameterError(key, i);
972  }
973  catch (...) {
974  NLogWarning("NGnNavigator::ExportJson: Exception in GetParameterError for key %s index %zu", key.c_str(), i);
975  paramError = 0.0;
976  }
977  }
978  if (!std::isnan(param) && std::fabs(param) > 1e-12) {
979  min = TMath::Min(min, param);
980  max = TMath::Max(max, param);
981  j["fArrays"][key]["values"][i] = param;
982  j["fArrays"][key]["errors"][i] = TMath::Power(paramError, 2);
983  }
984  else {
985  j["fArrays"][key]["values"][i] = 0.0;
986  j["fArrays"][key]["errors"][i] = 0.0;
987  }
988  }
989 
990  if (key.compare("mass") == 0) {
991  // for chi2, ndf and pvalue set min and max to 0 and 1
992  min = 1.018;
993  max = 1.023;
994  }
995  else {
996 
997  // set min max with 5 percent margin
998  double margin = 0.05 * (max - min);
999  min = min - margin;
1000  max = max + margin;
1001  j["fArrays"][key]["min"] = min;
1002  j["fArrays"][key]["max"] = max;
1003  }
1004  // j["ndmspc"][key]["fEntries"] = entries;
1005  // NLogDebug("NGnNavigator::ExportJson: key=%s Min=%f, Max=%f", key.c_str(), min, max);
1006  }
1007 
1008  double min = std::numeric_limits<double>::max(); // Initialize with largest possible double
1009  double max = -std::numeric_limits<double>::max(); // Initialize with smallest possible double
1010  std::vector<double> tmpContent;
1011  // --- Propagate parameter min/max from children ---
1012  std::map<std::string, double> paramMinGlobal;
1013  std::map<std::string, double> paramMaxGlobal;
1014  bool firstChild = true;
1015  for (const auto & child : obj->GetChildren()) {
1016  json childJson;
1017  if (child != nullptr) {
1018  try {
1019  ExportToJson(childJson, child, objectNames);
1020  }
1021  catch (...) {
1022  NLogWarning("NGnNavigator::ExportJson: Exception in recursive ExportToJson for child.");
1023  childJson = json();
1024  }
1025  // Aggregate parameter min/max from child
1026  if (childJson.contains("fArrays")) {
1027  for (auto & [param, arr] : childJson["fArrays"].items()) {
1028  if (arr.contains("min") && arr.contains("max")) {
1029  double cmin = arr["min"].get<double>();
1030  double cmax = arr["max"].get<double>();
1031  if (firstChild || paramMinGlobal.find(param) == paramMinGlobal.end()) {
1032  paramMinGlobal[param] = cmin;
1033  paramMaxGlobal[param] = cmax;
1034  }
1035  else {
1036  paramMinGlobal[param] = std::min(paramMinGlobal[param], cmin);
1037  paramMaxGlobal[param] = std::max(paramMaxGlobal[param], cmax);
1038  }
1039  }
1040  }
1041  }
1042  }
1043  firstChild = false;
1044  j["children"]["content"].push_back(childJson);
1045  }
1046 
1047  // Store aggregated min/max at this level if any
1048  if (!paramMinGlobal.empty()) {
1049  for (const auto & [param, minVal] : paramMinGlobal) {
1050  j["fArrays"][param]["min"] = minVal;
1051  j["fArrays"][param]["max"] = paramMaxGlobal[param];
1052  }
1053  }
1054  // loop over j["children"]["content"] and remove empty objects"
1055  bool hasContent = false;
1056  for (auto & c : j["children"]["content"]) {
1057  if (c != nullptr) {
1058  hasContent = true;
1059  break;
1060  }
1061  }
1062 
1063  if (!hasContent) {
1064  j["children"].erase("content");
1065  // NOTE: GetChildren() returns by value, so .clear() here was a no-op and did NOT
1066  // modify fChildren. The node's actual children remain intact.
1067  // If pruning is desired, add a ClearChildren() method that clears fChildren directly.
1068  // j["ndmspc"]["content"]["fMinimum"] = min;
1069  // j["ndmspc"]["content"]["fMaximum"] = max;
1070  }
1071  else {
1072  j["ndmspc"]["content"]["fMinimum"] = min;
1073  j["ndmspc"]["content"]["fMaximum"] = max;
1074  }
1075 
1076  if (obj->GetParent() == nullptr) {
1077  // NLogDebug("NGnNavigator::ExportJson: LLLLLLLLLLLLLLLLLLLLLLast");
1078  int i = -1;
1079  for (const auto & child : j["children"]["content"]) {
1080  i++;
1081  if (child == nullptr || child.is_null()) {
1082  // NLogError("NGnNavigator::ExportJson: Child is nullptr !!!");
1083  j["fArray"][i] = 0; // Store the maximum value for the content
1084  continue;
1085  }
1086 
1087  if (child.contains("ndmspc")) {
1088  // std::cout << child["fTitle"].dump() << std::endl;
1089  // double min = std::numeric_limits<double>::max(); // Initialize with largest possible double
1090  double max = -std::numeric_limits<double>::max(); // Initialize with smallest possible double
1091  // loop over all keys in "ndmspc"
1092  for (auto & [key, value] : child["ndmspc"].items()) {
1093  if (value.is_object()) {
1094  // min = TMath::Min(min, value["fMinimum"].get<double>());
1095  // min = 0;
1096  max = TMath::Max(max, value["fMaximum"].get<double>());
1097  }
1098  }
1099  j["fArray"][i] = max; // Store the maximum value for the content
1100  }
1101  else {
1102  j["fArray"][i] = 1; // Store the maximum value for the content
1103  }
1104  }
1105  if (j["children"]["content"].is_null()) j["children"].erase("content");
1106  }
1107 }
1108 
1109 void NGnNavigator::Print(Option_t * option) const
1110 {
1114  TString opt = option;
1115  opt.ToUpper();
1116 
1117  if (opt.Contains("A") && fGnTree) fGnTree->Print(option);
1118  if (fProjection) {
1119  NLogInfo("NGnNavigator: name='%s' title='%s' level=%d levels=%d projection='%s' title='%s'", GetName(), GetTitle(),
1120  fLevel, fNLevels, fProjection->GetName(), fProjection->GetTitle());
1121  // fProjection->Print(option);
1122  }
1123  else {
1124  NLogInfo("NGnNavigator: name='%s' title='%s' level=%d levels=%d projection=nullptr", GetName(), GetTitle(), fLevel,
1125  fNLevels);
1126  }
1127  if (!fParent) {
1128  NLogInfo("NGnNavigator: This is the root navigator.");
1129  // Print levels
1130  NBinningDef * binningDef = fGnTree->GetBinning()->GetDefinition();
1131  for (size_t l = 0; l < fLevels.size(); l++) {
1132  std::string axesStr = "";
1133  for (auto & a : fLevels[l]) {
1134  TAxis * axis = binningDef->GetContent()->GetAxis(a);
1135  axesStr += TString::Format("%d('%s') ", a, axis->GetName()).Data();
1136  }
1137  NLogInfo(" Level %zu axes: %s", l, axesStr.c_str());
1138  }
1139  }
1140 
1141  // Print only list of parameters
1142  NLogInfo("NGnNavigator: Parameters : %s", NUtils::GetCoordsString(GetParameterNames()).c_str());
1143  NLogInfo("NGnNavigator: Objects : %s", NUtils::GetCoordsString(GetObjectNames()).c_str());
1144 
1145  // print children
1146  std::vector<int> childIndices;
1147  for (size_t i = 0; i < fChildren.size(); i++) {
1148  NGnNavigator * child = fChildren[i];
1149  if (child) {
1150  childIndices.push_back(i);
1151  // NLogInfo("NGnNavigator: Child %d/%d:", i + 1, fChildren.size());
1152  // child->Print(option);
1153  }
1154  }
1155  NLogTrace("NGnNavigator: %zu children with indices: %s", childIndices.size(),
1156  NUtils::GetCoordsString(childIndices, -1).c_str());
1157  for (size_t i = 0; i < fChildren.size(); i++) {
1158  NGnNavigator * child = fChildren[i];
1159  if (child && child->GetChildren().empty() == false) {
1160  child->Print(option);
1161  }
1162  }
1163 }
1164 
1166 {
1170  json j;
1171  j["name"] = GetName();
1172  j["title"] = GetTitle();
1173  j["level"] = fLevel;
1174  j["nLevels"] = fNLevels;
1175  j["nChildren"] = fChildren.size();
1176  j["objects"] = GetObjectNames();
1177  j["params"] = GetParameterNames();
1178  auto parent = const_cast<NGnNavigator *>(this)->GetParent();
1179  j["levels"] = parent ? parent->GetLevels() : fLevels;
1180  return j;
1181 }
1182 
1183 void NGnNavigator::Draw(Option_t * option)
1184 {
1189 
1190  // TODO: Handle if size od levels is greater than 2 (since ROOT cannot hover more than 2D histograms)
1191  // TH1 * proj = GetProjection();
1192  for (size_t level = 0; level < fNLevels; level++) {
1193  NLogDebug("NGnNavigator::Draw: Level %d/%d", level + 1, fNLevels);
1194  if (fProjection->GetDimension() > 2) {
1195  NLogWarning("NGnNavigator::Draw: Level %d has projection with dimension %d > 2, which is not supported for "
1196  "hover/click !!!",
1197  level + 1, fProjection->GetDimension());
1198  return;
1199  }
1200  // proj = fChildren[0]->GetProjection();
1201  }
1202 
1203  // if (fGnTree == nullptr) {
1204  // NLogError("NGnNavigator::Draw: NGnTree is nullptr !!!");
1205  // return;
1206  // }
1207  //
1208  TString opt = option;
1209  opt.ToUpper();
1210  if (opt.Contains("HOVER")) {
1211  fTrigger = kMouseMotion;
1212  }
1213  if (opt.Contains("CLICK")) {
1214  fTrigger = kButton1Down;
1215  }
1216 
1217  // std::string name;
1218  if (gPad) SafeDelete(gPad);
1219  if (!gPad) {
1220  // NLogInfo("NGnNavigator::Draw: Making default canvas ...");
1221  gROOT->MakeDefCanvas();
1222  // if (!gPad->IsEditable()) return;
1223  if (fNLevels > fLevel + 1) {
1224  Int_t cy = TMath::Sqrt(fNLevels);
1225  Int_t cx = TMath::Ceil(fNLevels / (Double_t)cy);
1226  gPad->Divide(cy, cx); // Divide the pad into a grid based on the number of levels
1227  }
1228  }
1229  TVirtualPad * originalPad = gPad; // Save the original pad
1230  NGnNavigator * obj = nullptr;
1231  for (size_t level = fLevel; level < fNLevels; level++) {
1232  NLogDebug("NGnNavigator::Draw: Drawing level %d", level);
1233  TVirtualPad * pad = originalPad->cd(level + 1);
1234  if (pad) {
1235  NLogDebug("NGnNavigator::Draw: Clearing pad %d", level + 1);
1236  pad->Clear();
1237  gPad = pad; // Set the current pad to the level + 1 pad
1238  if (level == fLevel) {
1239  obj = this; // For the first level, use the current object
1240  NLogDebug("NGnNavigator::Draw: Using current object at level %d: %s", level, obj->GetName());
1241  }
1242  else {
1243 
1244  // obj = nullptr; // Reset the object for the next level
1245  for (size_t i = 0; i < obj->GetChildren().size(); i++) {
1246  NGnNavigator * child = obj->GetChild(i);
1247  if (child) {
1248  NLogDebug("NGnNavigator::Draw: Found child at level %d: %s", level, child->GetProjection()->GetTitle());
1249  obj = child; // Get the child object at the current level
1250  break;
1251  }
1252  }
1253  NLogDebug("NGnNavigator::Draw: Using child object at level %d: %s", level, obj ? obj->GetName() : "nullptr");
1254  }
1255  if (obj == nullptr) {
1256  NLogError("NGnNavigator::Draw: Child object at level %d is nullptr !!!", level);
1257  continue; // Skip to the next level if the child is nullptr
1258  }
1259  TH1 * projection = obj->GetProjection();
1260  if (projection) {
1261  NLogDebug("NGnNavigator::Draw: Drawing projection at level %d: %s", level, projection->GetTitle());
1262  projection->SetMinimum(0);
1263  projection->SetStats(kFALSE); // Turn off stats box for clarity
1264  // gPad->cd(); // Change to the current pad
1265  // projection->Draw(option); // Draw the projection histogram
1266  // obj->Draw(option); // Draw the object at the current level
1267  obj->AppendPad(option); // Append the pad to the current pad stack
1268  }
1269  else {
1270  NLogError("NGnNavigator::Draw: Projection at level %d is nullptr !!!", level);
1271  }
1272  }
1273  else {
1274  NLogError("NGnNavigator::Draw: Pad %d is nullptr !!!", level + 1);
1275  }
1276  }
1277  gPad = originalPad; // Restore the original pad
1278 }
1279 
1280 void NGnNavigator::Paint(Option_t * /*option*/)
1281 {
1285  NLogInfo("NGnNavigator::Paint: Painting object ...");
1286  if (fProjection) {
1287  NLogDebug("NGnNavigator::Paint: Painting to pad=%d projection name=%s title=%s ...", fLevel + 1,
1288  fProjection->GetName(), fProjection->GetTitle());
1289  // fProjection->Paint(option);
1290  fProjection->Paint("colz text");
1291  }
1292 }
1293 
1294 Int_t NGnNavigator::DistancetoPrimitive(Int_t px, Int_t py)
1295 {
1299 
1300  if (!fProjection) return 9999; // Not drawn, so we are infinitely far.
1301 
1302  return fProjection->DistancetoPrimitive(px, py);
1303 }
1304 
1305 void NGnNavigator::ExecuteEvent(Int_t event, Int_t px, Int_t py)
1306 {
1311 
1312  // NLogInfo("NGnNavigator::ExecuteEvent: event=%d, px=%d, py=%d", event, px, py);
1313 
1314  if (!fProjection || !gPad) return;
1315 
1316  // gPad = gPad->GetMother();
1317  // NLogDebug("NGnNavigator::ExecuteEvent: event=%d, px=%d, py=%d, gPad=%s title=%s", event, px, py,
1318  // gPad->GetName(), gPad->GetTitle());
1319  // NLogDebug("NGnNavigator::ExecuteEvent: event=%d, px=%d, py=%d", event, px, py);
1320 
1321  // Step 1: Convert absolute pixel coordinates to the pad's normalized coordinates (0-1 range)
1322  Double_t x_pad = gPad->AbsPixeltoX(px);
1323  Double_t y_pad = gPad->AbsPixeltoY(py);
1324 
1325  // Step 2: Convert the pad's normalized coordinates to the user's coordinate system (the histogram axes)
1326  Double_t x_user = gPad->PadtoX(x_pad);
1327  Double_t y_user = gPad->PadtoY(y_pad);
1328 
1329  size_t bin = fProjection->FindBin(x_user, y_user);
1330 
1331  TVirtualPad * originalPad = gPad; // Save the original pad
1332  bool isBinChanged = (bin != fLastHoverBin);
1333  // --- MOUSE CLICK LOGIC ---
1334  bool isActionTriggered = (event == fTrigger);
1335 
1336  // trigger action only if hover bin changed or event is click
1337  isActionTriggered = isActionTriggered && (isBinChanged || event == kButton1Down);
1338 
1339  if (isActionTriggered) {
1340  Int_t binx, biny, binz;
1341  fProjection->GetBinXYZ(bin, binx, biny, binz);
1342  Double_t content = fProjection->GetBinContent(bin);
1343  NLogDebug("NGnNavigator::ExecuteEvent: [%s] Mouse trigger on bin=[%d, %d] at px=[%f, %f] with content: %f "
1344  "level=%d nLevels=%d",
1345  gPad->GetName(), binx, biny, x_user, y_user, content, fLevel, fNLevels);
1346 
1347  int nDimensions = fGnTree->GetBinning()->GetDefinition()->GetContent()->GetNdimensions();
1348 
1349  Int_t index = fProjection->FindFixBin(fProjection->GetXaxis()->GetBinCenter(binx),
1350  fProjection->GetYaxis()->GetBinCenter(biny));
1351  if (nDimensions == 1) {
1352  // For 1D histograms, we need to find the index correctly
1353  index = fProjection->GetXaxis()->FindFixBin(fProjection->GetXaxis()->GetBinCenter(binx));
1354  }
1355  NLogTrace("NGnNavigator::ExecuteEvent: Index in histogram: %d level=%d", index, fLevel);
1356  NGnNavigator * child = GetChild(index);
1357  TCanvas * cObject = (TCanvas *)gROOT->GetListOfCanvases()->FindObject("cObject");
1358  if (child && child->GetProjection()) {
1359  NLogTrace("NGnNavigator::ExecuteEvent: [%s]Child object '%p' found at index %d", gPad->GetName(),
1360  child->GetProjection(), index);
1361  // originalPad->Clear(); // Clear the original pad
1362  gPad = originalPad->GetMother(); // Get the mother pad to avoid clearing the current pad
1363  TVirtualPad * pad = gPad->cd(fLevel + 1 + 1); // Ensure we are in the correct pad
1364  pad->Clear(); // Clear the pad for the child object
1365 
1366  SetLastIndexSelected(index);
1367  TH1 * hProj = child->GetProjection();
1368  hProj->SetStats(kFALSE); // Turn off stats box for clarity
1369  hProj->SetMinimum(0); // Set minimum to 0 for better visibility
1370  hProj->Draw("text colz"); // Draw the projection histogram of the child
1371  child->AppendPad();
1372  NLogTrace("NGnNavigator::ExecuteEvent: %d", child->GetLastIndexSelected());
1373  if (cObject) {
1374  cObject->Clear(); // Clear the existing canvas if it exists
1375  cObject->cd(); // Set the current canvas to cObject
1376  // Add text with note about the projection
1377 
1378  TLatex latex;
1379  latex.SetNDC();
1380  latex.SetTextAlign(22); // center
1381  latex.SetTextSize(0.05);
1382  latex.DrawLatex(0.5, 0.5, "Select bottom pad to see projection");
1383  // delete cObject; // Delete the existing canvas if it exists
1384  }
1385  }
1386  else {
1387 
1388  // TH1 * projection = child->GetProjection();
1389  // index = projection->GetXaxis()->FindFixBin(projection->GetXaxis()->GetBinCenter(binx));
1390  NLogTrace("NGnNavigator::ExecuteEvent: No child object found at index %d", index);
1391  std::string objName = fObjectNames.empty() ? "resource_monitor" : fObjectNames[0];
1392  TH1 * hProj = (TH1 *)GetObject(objName, index);
1393  if (hProj == nullptr) {
1394  NLogError("NGnNavigator::ExecuteEvent: No histogram found for index %d", index);
1395  return;
1396  }
1397  // hProj->Print("all");
1398  if (cObject == nullptr) {
1399  cObject = new TCanvas("cObject", "cObject", 800, 600);
1400  }
1401  cObject->cd();
1402  // gPad = cObject->GetPad(0); // Get the current pad
1403  // hProj->SetTitle(Form("Projection of %s at index %d", fProjection->GetName(), index));
1404  if (hProj->GetXaxis()->IsAlphanumeric()) {
1405  TPaveText * pt = new TPaveText(0.15, 0.15, 0.85, 0.85);
1406  for (Int_t binx = 1; binx <= hProj->GetNbinsX(); ++binx) {
1407  std::string name = hProj->GetXaxis()->GetBinLabel(binx);
1408  std::string value = TString::Format("%.3f", hProj->GetBinContent(binx)).Data();
1409  std::string t = TString::Format("%s: %s", name.c_str(), value.c_str()).Data();
1410 
1411  pt->AddText(t.c_str());
1412  }
1413  pt->Draw();
1414  }
1415  else {
1416  hProj->Draw(); // Draw the projection histogram
1417  }
1418  }
1419  // else {
1420  // }
1421  gPad->ModifiedUpdate(); // Force pad to redraw
1422  }
1423  // --- MOUSE HOVER LOGIC ---
1424  if (event == kMouseMotion) {
1425  if (isBinChanged) {
1426  // Check if the cursor is inside a bin with content
1427  if (fProjection->GetBinContent(bin) > 0) {
1428  Int_t binx, biny, binz;
1429  fProjection->GetBinXYZ(bin, binx, biny, binz);
1430  NLogTrace("[%s] Mouse hover on bin[%d, %d] at px[%f, %f] level=%d nLevels=%d", gPad->GetName(), binx, biny,
1431  x_user, y_user, fLevel, fNLevels);
1432  }
1433  fLastHoverBin = bin;
1434  NLogTrace("[%s] Setting point for level %d %s", gPad->GetName(), fLevel, fProjection->GetTitle());
1435  }
1436  }
1437  gPad = originalPad; // Restore the original pad
1438 }
1439 
1441 {
1445  NLogTrace("NGnNavigator::GetChild: index=%d, size=%zu", index, fChildren.size());
1446  return (index < fChildren.size()) ? fChildren[index] : nullptr;
1447 }
1448 
1449 void NGnNavigator::SetChild(NGnNavigator * child, int index)
1450 {
1454  if (child) {
1455  fChildren[index < 0 ? fChildren.size() : index] = child;
1456  child->SetParent(this);
1457  }
1458 }
1459 
1460 NGnNavigator * NGnNavigator::GetChild(std::vector<std::vector<size_t>> coords) const
1461 {
1465 
1466  NGnNavigator * next;
1467 
1468  NBinningDef * binningDef = fGnTree->GetBinning()->GetDefinition();
1469  THnSparse * hnsObjContent = binningDef->GetContent();
1470 
1471  std::vector<std::vector<int>> ranges;
1472  NGnNavigator * root = GetRoot();
1473 
1474  // if (root != this) {
1475 
1476  NLogDebug("NGnNavigator::GetChild: Setting axis ranges for level=%d", fLevel);
1477  std::vector<int> levels = root->GetLevels()[fLevel];
1478  for (size_t i = 0; i < levels.size(); i++) {
1479  int coords_i = (int)coords[i][fLevel];
1480  ranges.push_back({levels[i], coords_i, coords_i}); // Initialize ranges vector
1481  NLogDebug("NGnNavigator::GetChild: level=%d axis=%d coord=%d", fLevel, levels[i], coords_i);
1482  }
1483  NUtils::SetAxisRanges(hnsObjContent, ranges, false, false, false);
1484 
1485  coords[0].resize(3, 0);
1486 
1487  size_t bin = fProjection->GetBin(coords[0][0], coords[0][1], coords[0][2]);
1488  NLogDebug("NGnNavigator::GetChild: coords=%s bin=%d", NUtils::GetCoordsString(coords[0]).c_str(), bin);
1489 
1490  next = GetChild(bin);
1491  if (!next) return nullptr;
1492  next->Print();
1493 
1494  return next;
1495 }
1496 
1497 // NGnNavigator * NGnNavigator::Open(TTree * tree, const std::string & branches, TFile * file)
1498 // {
1499 // ///
1500 // /// Open NGnNavigator from TTree
1501 // ///
1502 //
1503 // NGnTree * ngt = NGnTree::Open(tree, branches, file);
1504 //
1505 // NGnNavigator * navigator = new NGnNavigator();
1506 // if (!navigator) {
1507 // return nullptr;
1508 // }
1509 // navigator->SetGnTree(ngt);
1510 //
1511 // return navigator;
1512 // }
1513 //
1514 // NGnNavigator * NGnNavigator::Open(const std::string & filename, const std::string & branches,
1515 // const std::string & treename)
1516 // {
1517 // ///
1518 // /// Open NGnNavigator from file
1519 // ///
1520 // NGnTree * ngt = NGnTree::Open(filename, branches, treename);
1521 //
1522 // NGnNavigator * navigator = new NGnNavigator();
1523 // if (!navigator) {
1524 // return nullptr;
1525 // }
1526 // navigator->SetGnTree(ngt);
1527 //
1528 // return navigator;
1529 // }
1530 std::vector<TObject *> NGnNavigator::GetObjects(const std::string & name) const
1531 {
1533  auto it = fObjectContentMap.find(name);
1534  if (it != fObjectContentMap.end()) {
1535  return it->second;
1536  }
1537  return {};
1538 }
1539 
1540 TObject * NGnNavigator::GetObject(const std::string & name, int index) const
1541 {
1543  auto it = fObjectContentMap.find(name);
1544  if (it != fObjectContentMap.end() && index < (int)it->second.size()) {
1545  return it->second[index];
1546  }
1547  return nullptr;
1548 }
1549 
1550 void NGnNavigator::SetObject(const std::string & name, TObject * obj, int index)
1551 {
1555  if (obj) {
1556  if (fObjectContentMap.find(name) == fObjectContentMap.end()) {
1558  }
1559  NLogTrace("NGnNavigator::SetObject: name=%s, obj=%p, index=%d", name.c_str(), obj, index,
1560  fObjectContentMap[name].size());
1561 
1562  // Add object name if missing
1563  if (std::find(fObjectNames.begin(), fObjectNames.end(), name) == fObjectNames.end()) {
1564  fObjectNames.push_back(name);
1565  }
1566 
1567  if (index < 0) {
1568  fObjectContentMap[name].push_back(obj);
1569  }
1570  else {
1571  // if (index >= (int)fObjectContentMap[name].size()) {
1572  // fObjectContentMap[name].resize(index + 1, nullptr);
1573  // }
1574  // Delete any previous object at this slot before overwriting, so that
1575  // repeated SetObject calls at the same index don't leak the old pointer.
1576  delete fObjectContentMap[name][index];
1577  fObjectContentMap[name][index] = obj;
1578  }
1579  }
1580 }
1581 
1582 std::vector<double> NGnNavigator::GetParameters(const std::string & name) const
1583 {
1585  auto it = fParameterContentMap.find(name);
1586  if (it != fParameterContentMap.end()) {
1587  return it->second;
1588  }
1589  return {};
1590 }
1591 
1592 double NGnNavigator::GetParameter(const std::string & name, int index) const
1593 {
1595  auto it = fParameterContentMap.find(name);
1596  if (it != fParameterContentMap.end() && index < (int)it->second.size()) {
1597  return it->second[index];
1598  }
1599  return TMath::QuietNaN();
1600 }
1601 
1602 void NGnNavigator::SetParameter(const std::string & name, double value, int index)
1603 {
1607  if (!std::isnan(value)) {
1608  if (fParameterContentMap.find(name) == fParameterContentMap.end() || fParameterContentMap[name].size() < fNCells) {
1609  NLogTrace("NGnNavigator::SetParameter: Resizing parameter content map for '%s' to %d", name.c_str(), fNCells);
1611  }
1612  NLogTrace("NGnNavigator::SetParameter: name=%s, value=%f, index=%d", name.c_str(), value, index,
1613  fParameterContentMap[name].size());
1614 
1615  // Append parameter name if missing
1616  if (std::find(fParameterNames.begin(), fParameterNames.end(), name) == fParameterNames.end()) {
1617  fParameterNames.push_back(name);
1618  }
1619 
1620  if (index < 0) {
1621  fParameterContentMap[name].push_back(value);
1622  }
1623  else {
1624  fParameterContentMap[name][index] = value;
1625  }
1626  }
1627 }
1628 
1629 std::vector<double> NGnNavigator::GetParameterErrors(const std::string & name) const
1630 {
1632  auto it = fParameterErrorContentMap.find(name);
1633  if (it != fParameterErrorContentMap.end()) {
1634  return it->second;
1635  }
1636  return {};
1637 }
1638 
1639 double NGnNavigator::GetParameterError(const std::string & name, int index) const
1640 {
1642  auto it = fParameterErrorContentMap.find(name);
1643  if (it != fParameterErrorContentMap.end() && index < (int)it->second.size()) {
1644  return it->second[index];
1645  }
1646  return TMath::QuietNaN();
1647 }
1648 
1649 void NGnNavigator::SetParameterError(const std::string & name, double value, int index)
1650 {
1654  if (!std::isnan(value)) {
1655  if (fParameterErrorContentMap.find(name) == fParameterErrorContentMap.end() ||
1656  fParameterErrorContentMap[name].size() < fNCells) {
1657  NLogTrace("NGnNavigator::SetParameter: Resizing parameter content map for '%s' to %d", name.c_str(), fNCells);
1659  }
1660  NLogTrace("NGnNavigator::SetParameter: name=%s, value=%f, index=%d", name.c_str(), value, index,
1661  fParameterErrorContentMap[name].size());
1662 
1663  // Append parameter name if missing
1664  if (std::find(fParameterNames.begin(), fParameterNames.end(), name) == fParameterNames.end()) {
1665  fParameterNames.push_back(name);
1666  }
1667 
1668  if (index < 0) {
1669  fParameterErrorContentMap[name].push_back(value);
1670  }
1671  else {
1672  fParameterErrorContentMap[name][index] = value;
1673  }
1674  }
1675 }
1676 TList * NGnNavigator::DrawSpectraAll(std::string parameterName, std::vector<double> minmax,
1677  const std::string & minmaxMode, Option_t * option) const
1678 {
1682  std::vector<int> projIds;
1683  return DrawSpectra(parameterName, projIds, minmax, minmaxMode, option);
1684 }
1685 
1686 TList * NGnNavigator::DrawSpectraByName(std::string parameterName, std::vector<std::string> projAxes,
1687  std::vector<double> minmax, const std::string & minmaxMode,
1688  Option_t * option) const
1689 {
1693 
1694  std::vector<int> projIds;
1695  // lopp over fProjection axes and find the indices of projAxes then fill projIds and remove from projAxes
1696  for (const auto & axisName : projAxes) {
1697  TAxis * axis = nullptr;
1698  for (int i = 0; i < fProjection->GetDimension(); i++) {
1699  if (i == 0)
1700  axis = fProjection->GetXaxis();
1701  else if (i == 1)
1702  axis = fProjection->GetYaxis();
1703  else if (i == 2)
1704  axis = fProjection->GetZaxis();
1705 
1706  if (axis && axis->GetName() == axisName) {
1707  projIds.push_back(i);
1708  break;
1709  }
1710  }
1711  }
1712  if (projIds.empty()) {
1713  NLogError("NGnNavigator::DrawSpectra: No projection axes found for names: %s",
1714  NUtils::GetCoordsString(projAxes, -1).c_str());
1715  return nullptr;
1716  }
1717 
1718  if (projIds.size() > 3) {
1719  NLogError("NGnNavigator::DrawSpectra: Too many projection dimensions: %zu (max 3)", projIds.size());
1720  return nullptr;
1721  }
1722 
1723  if (projIds.size() != projAxes.size()) {
1724  NLogError("NGnNavigator::DrawSpectra: Not all projection axes found: %s",
1725  NUtils::GetCoordsString(projAxes, -1).c_str());
1726  return nullptr;
1727  }
1728 
1729  return DrawSpectra(parameterName, projIds, minmax, minmaxMode, option);
1730 }
1731 
1732 TList * NGnNavigator::DrawSpectra(std::string parameterName, std::vector<int> projIds, std::vector<double> minmax,
1733  const std::string & minmaxMode, Option_t * option) const
1734 {
1738 
1739  if (parameterName.empty()) {
1740  NLogError("NGnNavigator::DrawSpectra: Parameter name is empty");
1741  return nullptr;
1742  }
1743 
1744  // check if parameterName exists in fParameterContentMap
1745  if (fParameterContentMap.find(parameterName) == fParameterContentMap.end()) {
1746  NLogError("NGnNavigator::DrawSpectra: Parameter name '%s' not found in fParameterContentMap",
1747  parameterName.c_str());
1748  return nullptr;
1749  }
1750  Int_t screenWidth = gClient->GetDisplayWidth();
1751  Int_t screenHeight = gClient->GetDisplayHeight();
1752 
1753  // int padCounter = 0;
1754  TCanvas * c = nullptr;
1755  // Create a canvas that is, for example, 40% of the screen width and height
1756  constexpr double canvasScale = 0.2;
1757  Int_t canvasWidth = static_cast<Int_t>(screenWidth * canvasScale);
1758  Int_t canvasHeight = static_cast<Int_t>(screenHeight * canvasScale);
1759 
1760  if (canvasWidth < 800) canvasWidth = 800;
1761  if (canvasHeight < 600) canvasHeight = 600;
1762 
1763  NLogTrace("Screen size: %dx%d", screenWidth, screenHeight);
1764 
1765  NBinningDef * binningDef = fGnTree->GetBinning()->GetDefinition();
1766  THnSparse * hnsObjContent = binningDef->GetContent();
1767  hnsObjContent->Print("all");
1768  std::vector<std::vector<int>> projections;
1769  if (projIds.empty()) {
1770  projIds.resize(fProjection->GetDimension());
1771  std::iota(projIds.begin(), projIds.end(), 0); // Fill projIds with 0, 1, 2, ..., n-1
1772  projections = Ndmspc::NUtils::Permutations(projIds);
1773  }
1774  else {
1775  projections.push_back(projIds);
1776  }
1777 
1778  if (projections.empty()) {
1779  NLogError("NGnNavigator::DrawSpectra: No projections found");
1780  return nullptr;
1781  }
1782  if (projections[0].size() > 3) {
1783  NLogError("NGnNavigator::DrawSpectra: Too many projection dimensions: %zu (max 3)", projections[0].size());
1784  return nullptr;
1785  }
1786 
1787  TList * outputList = new TList();
1788  for (const auto & proj : projections) {
1789 
1790  NLogTrace("Projection IDs: %s", NUtils::GetCoordsString(projIds, -1).c_str());
1791 
1792  // fProjection->Draw("colz");
1793  TH1 * hParameterProjection = (TH1 *)fProjection->Clone("hParameterProjection");
1794  // Detach from gDirectory so the TFile does not claim ownership and cause a double-free
1795  hParameterProjection->SetDirectory(nullptr);
1796  // hParameterProjection->Sumw2(kFALSE);
1797 
1798  // fill hParameterProjection from fParameterContentMap
1799  hParameterProjection->SetContent(GetParameters(parameterName).data());
1800 
1801  // NLogTrace("NGnNavigator::DrawSpectra: Setting parameter errors for '%s'", parameterName.c_str());
1802  // Print all errors
1803 
1804  hParameterProjection->SetError(GetParameterErrors(parameterName).data());
1805  // hParameterProjection->Draw("colz text");
1806  // return;
1807 
1808  // Create parameter THnSparse
1809  THnSparse * hsParam = THnSparse::CreateSparse(parameterName.c_str(), parameterName.c_str(), hParameterProjection);
1810 
1811  // append all labels from from fProjection to hs
1812  int nDimensions = hParameterProjection->GetDimension();
1813  for (int i = 0; i < nDimensions; i++) {
1814  TAxis * axis = nullptr;
1815  if (i == 0)
1816  axis = hParameterProjection->GetXaxis();
1817  else if (i == 1)
1818  axis = hParameterProjection->GetYaxis();
1819  else if (i == 2)
1820  axis = hParameterProjection->GetZaxis();
1821 
1822  hsParam->GetAxis(i)->SetName(axis->GetName());
1823  hsParam->GetAxis(i)->SetTitle(axis->GetTitle());
1824  if (axis->IsVariableBinSize()) {
1825  hsParam->GetAxis(i)->Set(axis->GetNbins(), axis->GetXbins()->GetArray());
1826  }
1827  if (axis->IsAlphanumeric()) {
1828  for (int j = 1; j <= axis->GetNbins(); j++) {
1829  const char * label = axis->GetBinLabel(j);
1830  hsParam->GetAxis(i)->SetBinLabel(j, label);
1831  }
1832  }
1833  }
1834 
1835  delete hParameterProjection;
1836 
1837  std::vector<int> dims;
1838 
1839  std::vector<int> currentLevels = GetRoot()->GetLevels()[fLevel];
1840  NLogTrace("Current levels: %s", NUtils::GetCoordsString(currentLevels, -1).c_str());
1841  for (auto & id : proj) {
1842  dims.push_back(currentLevels[id]);
1843  }
1844 
1845  if (dims.size() > 3) {
1846  NLogError("NGnNavigator::DrawSpectra: Too many projection dimensions: %zu (max 3)", dims.size());
1847  delete outputList;
1848  return nullptr;
1849  }
1850 
1851  NLogTrace("Projection dims: %s", NUtils::GetCoordsString(dims, -1).c_str());
1852 
1853  // hsParam->Print("all");
1854  // hsParam->Projection(0, 1, 2)->Draw("colz text");
1855  // return;
1856 
1857  std::vector<std::set<int>> dimsResults(3);
1858 
1859  std::vector<std::vector<int>> ranges;
1860  NUtils::SetAxisRanges(hsParam, ranges, false, false, true);
1861  Int_t * p = new Int_t[hsParam->GetNdimensions()];
1862  Long64_t linBin = 0;
1863  std::unique_ptr<ROOT::Internal::THnBaseBinIter> iter{hsParam->CreateIter(true /*use axis range*/)};
1864  while ((linBin = iter->Next()) >= 0) {
1865 
1866  // NLogDebug("Linear bin: %lld", linBin);
1867  hsParam->GetBinContent(linBin, p);
1868  // Double_t v = hsParam->GetBinContent(linBin, p);
1869  // Long64_t idx = hsParam->GetBin(p);
1870  // if (idx < 0) {
1871  // NLogError("Negative bin index: %lld for bin coordinates: %s", idx,
1872  // NUtils::GetCoordsString(NUtils::ArrayToVector(p, hsParam->GetNdimensions()), -1).c_str());
1873  // continue;
1874  // }
1875  // std::string binCoords = NUtils::GetCoordsString(NUtils::ArrayToVector(p, hsParam->GetNdimensions()), -1);
1876  // NLogDebug("Bin %lld(%lld): %f %s", linBin, idx, v, binCoords.c_str());
1877  dimsResults[0].insert(p[proj[0]]);
1878  if (dims.size() > 1) dimsResults[1].insert(p[proj[1]]);
1879  if (dims.size() > 2) dimsResults[2].insert(p[proj[2]]);
1880  }
1881 
1882  // // Print dinsResults for each dimension
1883  // for (size_t i = 0; i < dims.size(); i++) {
1884  // NLogDebug("Dimension %d (axis %d) has %zu unique bins: ", i, dims[i], dimsResults[i].size());
1885  // for (const auto & bin : dimsResults[i]) {
1886  // NLogDebug(" Bin %d", bin);
1887  // }
1888  // }
1889 
1890  if (!gROOT->IsBatch()) {
1891  // Ensure gClient is initialized if not already
1892  if (!gClient) {
1893  // This will initialize gClient if needed
1894  (void)gClient;
1895  }
1896  }
1897 
1898  // if (dims.size() <= 2) {
1899  // dims.push_back(0); // Add a dummy dimension for 2D plotting
1900  // }
1901  int nPads = dims.size() > 2 ? dimsResults[2].size() : 1;
1902  // NLogDebug("Number of pads: %d", nPads);
1903  std::vector<std::string> projNames;
1904  // hsParam->Print("all");
1905  NLogTrace("Projection dims: %d %d %d", dims[0], dims.size() > 1 ? dims[1] : -1, dims.size() > 2 ? dims[2] : -1);
1906  projNames.push_back(hnsObjContent->GetAxis(dims[0])->GetName());
1907  if (dims.size() > 1) projNames.push_back(hnsObjContent->GetAxis(dims[1])->GetName());
1908  if (dims.size() > 2) projNames.push_back(hnsObjContent->GetAxis(dims[2])->GetName());
1909 
1910  NLogDebug("Drawing %s [pads=%d] ...", NUtils::GetCoordsString(projNames, -1).c_str(), nPads);
1911 
1912  std::string posfix = NUtils::Join(projNames, '-');
1913 
1914  std::string canvasName = Form("c_%s", posfix.c_str());
1915  NLogTrace("Creating canvas '%s' with size %dx%d", canvasName.c_str(), canvasWidth, canvasHeight);
1916 
1917  // Delete existing canvas with same name to prevent crash during auto-deletion
1918  TCanvas * existingCanvas = (TCanvas *)gROOT->GetListOfCanvases()->FindObject(canvasName.c_str());
1919  if (existingCanvas) {
1920  NLogTrace("Deleting existing canvas '%s'", canvasName.c_str());
1921  delete existingCanvas;
1922  }
1923 
1924  c = new TCanvas(canvasName.c_str(), canvasName.c_str(), canvasWidth, canvasHeight);
1925  c->SetBit(kMustCleanup, kFALSE);
1926  outputList->Add(c);
1927  // c->DivideSquare(nPads);
1928 
1929  TList * stackList = new TList();
1930 
1931  for (int iPad = 0; iPad < nPads; iPad++) {
1932  // c->cd(iPad + 1);
1933 
1934  std::string stackName = Form("hStack_%s_%d", posfix.c_str(), iPad);
1935  std::string stackTitle = parameterName + " : ";
1936  stackTitle += projNames[0];
1937  stackTitle += projNames.size() > 1 ? " vs " + projNames[1] : "";
1938  if (proj.size() > 2) {
1939  // FIXME: check if p[proj[2]] is within the axis range before using it to avoid out-of-range errors
1940  // p[proj[2]] = iPad + 1; // 1-based index for the third dimension
1941 
1942  auto it = std::next(dimsResults[2].begin(), iPad);
1943  if (it != dimsResults[2].end()) {
1944  p[proj[2]] = *it; // 30
1945  }
1946 
1947  // print projIds[2] range
1948  // NLogDebug("Pad %d: Setting projection indices: %d %d %d", iPad, p[0], p[1], p[2]);
1949  // NLogDebug("Pad %d: Setting projection indices: %d %d %d", iPad, projIds[0], projIds[1],
1950  // projIds[2]);
1951 
1952  TAxis * aPad = hsParam->GetAxis(proj[2]);
1953  if (aPad->IsAlphanumeric()) {
1954  stackTitle += projNames.size() > 2 ? " for " + projNames[2] + " [" + aPad->GetBinLabel(p[proj[2]]) + "]" : "";
1955  }
1956  else {
1957  double binLowEdge = aPad->GetBinLowEdge(p[proj[2]]);
1958  double binUpEdge = aPad->GetBinUpEdge(p[proj[2]]);
1959  stackTitle +=
1960  projNames.size() > 2 ? " for " + projNames[2] + " " + Form(" [%.3f,%.3f]", binLowEdge, binUpEdge) : "";
1961  }
1962  }
1963  NLogTrace("Creating stack '%s' with title '%s'", stackName.c_str(), stackTitle.c_str());
1964  // Delete any existing THStack with the same name to prevent a cascade crash
1965  // through ROOT's RecursiveRemove when the duplicate is added to gROOT's lists.
1966  // TObject * existingStack = gROOT->FindObject(stackName.c_str());
1967  // if (existingStack && existingStack->IsA() == THStack::Class()) {
1968  // NLogTrace("Deleting existing THStack '%s'", stackName.c_str());
1969  // delete existingStack;
1970  // }
1971  THStack * hStack = new THStack(stackName.c_str(), stackTitle.c_str());
1972  hStack->SetBit(kMustCleanup, kFALSE);
1973 
1974  int nStacks = proj.size() > 1 ? dimsResults[1].size() : 1;
1975  double stackMin = std::numeric_limits<double>::max();
1976  double stackMax = std::numeric_limits<double>::lowest();
1977  // Only support "VE" and "V" modes; default to "V" if unsupported
1978  std::string mode = (minmaxMode == "VE" || minmaxMode == "V" || minmaxMode == "D") ? minmaxMode : "V";
1979  for (int iStack = 0; iStack < nStacks; iStack++) {
1980  p[proj[0]] = 0;
1981  if (proj.size() > 1) {
1982  auto it = std::next(dimsResults[1].begin(), iStack);
1983  if (it != dimsResults[1].end()) {
1984  p[proj[1]] = *it; // 30
1985  }
1986 
1987  // p[proj[1]] = dimsResults[1][(size_t)iStack] + 1;
1988  }
1989  std::vector<std::vector<int>> rangesTmp;
1990  if (proj.size() > 2) rangesTmp.push_back({proj[2], p[proj[2]], p[proj[2]]});
1991  if (proj.size() > 1) rangesTmp.push_back({proj[1], p[proj[1]], p[proj[1]]});
1992  NUtils::SetAxisRanges(hsParam, rangesTmp, true);
1993  TH1 * hProj = NUtils::ProjectTHnSparse(hsParam, {proj[0]}, option);
1994  if (!hProj || hProj->GetEntries() == 0) {
1995  NLogError("Failed to project THnSparse for stack %d with projection IDs: %s", iStack,
1996  NUtils::GetCoordsString(proj, -1).c_str());
1997  delete hProj;
1998  continue;
1999  }
2000  if (proj.size() > 1) {
2001  TAxis * aStack = hsParam->GetAxis(proj[1]);
2002  if (aStack->IsAlphanumeric()) {
2003  std::string label = aStack->GetBinLabel(p[proj[1]]);
2004  hProj->SetName(Form("%s_%s", aStack->GetName(), label.c_str()));
2005  hProj->SetTitle(Form("%s [%s]", aStack->GetName(), label.c_str()));
2006  }
2007  else {
2008  double binLowEdge = aStack->GetBinLowEdge(p[proj[1]]);
2009  double binUpEdge = aStack->GetBinUpEdge(p[proj[1]]);
2010  hProj->SetName(Form("%s_%.3f_%.3f", aStack->GetName(), binLowEdge, binUpEdge));
2011  hProj->SetTitle(Form("%s [%.3f,%.3f]", aStack->GetName(), binLowEdge, binUpEdge));
2012  }
2013  }
2014 
2015  // Track min/max according to mode
2016  for (int bin = 1; bin <= hProj->GetNbinsX(); ++bin) {
2017  double val = hProj->GetBinContent(bin);
2018  double err = hProj->GetBinError(bin);
2019  if (mode == "VE") {
2020  if (stackMin > val - err) stackMin = val - err;
2021  if (stackMax < val + err) stackMax = val + err;
2022  }
2023  else if (mode == "V") {
2024  if (stackMin > val) stackMin = val;
2025  if (stackMax < val) stackMax = val;
2026  }
2027  }
2028 
2029  hProj->SetMarkerStyle(20);
2030  hProj->SetMarkerColor(iStack + 1);
2031  // NLogDebug("Histogram '%s' entries: %lld", hProj->GetName(), hProj->GetEntries());
2032  TH1 * hProjClone = (TH1 *)hProj->Clone();
2033  // Detach clone from gDirectory to prevent TFile from owning it and causing a
2034  // double-free when THStack deletes it during canvas cleanup later.
2035  hProjClone->SetDirectory(nullptr);
2036  hStack->Add(hProjClone);
2037  // NLogTrace("Added histogram to stack: %s", hProj->GetTitle());
2038  delete hProj;
2039  }
2040 
2041  if (mode == "D") {
2042  hStack->SetMinimum(-1111);
2043  hStack->SetMaximum(-1111);
2044  }
2045  else {
2046  // Always set stackMin and stackMax to THStack, using value ± error for all bins
2047  if (minmax.size() > 0) {
2048  if (minmax.size() == 2) {
2049  stackMin = minmax[0];
2050  stackMax = minmax[1];
2051  }
2052  else if (minmax.size() == 1) {
2053  // minmax[0] is N percent (e.g., 0.05 for 5%)
2054  double percent = minmax[0];
2055  double center = 0.5 * (stackMax + stackMin);
2056  double halfRange = 0.5 * (stackMax - stackMin);
2057  double margin = percent * halfRange;
2058  stackMin = center - (halfRange + margin);
2059  stackMax = center + (halfRange + margin);
2060  }
2061  }
2062 
2063  // Safeguard: avoid wmin == wmax errors
2064  if (stackMin == stackMax) {
2065  if (stackMin == 0.0) {
2066  stackMin = 0.0;
2067  stackMax = 1.0;
2068  }
2069  else {
2070  stackMin -= 1.0;
2071  stackMax += 1.0;
2072  }
2073  }
2074  hStack->SetMinimum(stackMin);
2075  hStack->SetMaximum(stackMax);
2076  }
2077 
2078  if (hStack->GetNhists() == 0) {
2079  NLogError("No histograms were added to the stack for pad %d with projection IDs: %s", iPad,
2080  NUtils::GetCoordsString(proj, -1).c_str());
2081  continue;
2082  }
2083 
2084  // std::string drawOption = "nostack E";
2085  // drawOption += option;
2086  // NLogDebug("Drawing stack with option: %s in pad %d %d", drawOption.c_str(), iPad + 1, hStack->GetNhists());
2087  // hStack->Draw(drawOption.c_str());
2088  // hStack->GetHistogram()->GetXaxis()->SetTitle(projNames[0].c_str());
2089  // hStack->GetHistogram()->GetYaxis()->SetTitle(parameterName.c_str());
2090  // // gPad->ModifiedUpdate();
2091  // if (dims.size() > 1) gPad->BuildLegend(0.75, 0.75, 0.95, 0.95, "");
2092  stackList->Add(hStack);
2093  // c->ModifiedUpdate();
2094  // gSystem->ProcessEvents();
2095  }
2096  delete[] p;
2097  delete hsParam;
2098 
2099  if (stackList->GetEntries() > 0) {
2100  nPads = stackList->GetEntries();
2101  c->DivideSquare(nPads);
2102  for (int iPad = 0; iPad < nPads; iPad++) {
2103  c->cd(iPad + 1);
2104  THStack * hStack = (THStack *)stackList->At(iPad);
2105  if (hStack) {
2106  hStack->Draw("nostack E");
2107  NLogTrace("Drawing stack with option: %s in pad %d %d", "nostack E", iPad + 1, hStack->GetNhists());
2108  hStack->GetHistogram()->GetXaxis()->SetTitle(projNames[0].c_str());
2109  hStack->GetHistogram()->GetYaxis()->SetTitle(parameterName.c_str());
2110  if (dims.size() > 1) gPad->BuildLegend(0.75, 0.75, 0.95, 0.95, "");
2111  c->ModifiedUpdate();
2112  // gSystem->ProcessEvents();
2113  }
2114  }
2115  }
2116 
2117  stackList->Clear();
2118  delete stackList;
2119  }
2120 
2121  return outputList;
2122 }
2123 
2125 {
2129 
2130  if (fParent == nullptr) {
2131  return const_cast<NGnNavigator *>(this);
2132  }
2133 
2134  NGnNavigator * current = fParent;
2135  while (current->GetParent() != nullptr) {
2136  current = current->GetParent();
2137  }
2138  return current;
2139 }
2140 
2141 } // 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:1024
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:161
Int_t GetEntry(Long64_t entry, bool checkBinningDef=true)
Get entry by index.
Definition: NGnTree.cxx:1340
virtual void Print(Option_t *option="") const override
Print tree information.
Definition: NGnTree.cxx:468
NStorageTree * GetStorageTree() const
Get pointer to storage tree object.
Definition: NGnTree.h:173
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:621
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:1220
static TH1 * ProjectTHnSparse(THnSparse *hns, const std::vector< int > &axes, Option_t *option="")
Project a THnSparse histogram onto specified axes.
Definition: NUtils.cxx:1167
static bool SaveRawFile(std::string filename, std::string content)
Save content to a raw file.
Definition: NUtils.cxx:765
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:1338
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:1111
static std::string GetCoordsString(const std::vector< int > &coords, int index=-1, int width=0)
Get string representation of coordinates.
Definition: NUtils.cxx:1588
static std::vector< std::vector< int > > Permutations(const std::vector< int > &v)
Generate all permutations of a vector.
Definition: NUtils.cxx:1639
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