ndmspc v1.2.0-0.1.rc7
Loading...
Searching...
No Matches
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
32ClassImp(Ndmspc::NGnNavigator);
34
35namespace Ndmspc {
36NGnNavigator::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
71NGnNavigator * 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}
156NGnNavigator * 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
760void 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
837void 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
1109void 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
1183void 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
1280void 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
1294Int_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
1305void 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
1449void 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
1460NGnNavigator * 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// }
1530std::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
1540TObject * 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
1550void 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
1582std::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
1592double 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
1602void 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
1629std::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
1639double 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
1649void 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}
1676TList * 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
1686TList * 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
1732TList * 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
std::vector< int > GetVariableAxes() const
Get list of variable axes indices.
Definition NBinningDef.h:71
THnSparse * GetContent() const
Get the template content histogram.
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.
void SetLevel(size_t l)
Set current level in hierarchy.
std::vector< std::vector< int > > GetLevels() const
Get the current levels as a vector of vectors of integers.
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.
void SetObject(const std::string &name, TObject *obj, int index=-1)
Set object by name and index.
void SetChildrenSize(size_t n)
Set number of children.
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.
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.
NGnNavigator * fParent
Parent object.
virtual ~NGnNavigator()
Destructor.
std::vector< std::string > GetParameterNames() const
Get parameter names managed by navigator.
std::vector< std::string > fParameterNames
Parameter names.
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.
size_t fNLevels
Number of levels in the hierarchy.
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.
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::vector< std::string > GetObjectNames() const
Get object names managed by navigator.
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.
void SetLevels(const std::vector< std::vector< int > > &levels)
Set the levels using a vector of vectors of integers.
void SetLastIndexSelected(size_t idx)
Set last selected index.
TH1 * GetProjection() const
Get projection histogram.
TObject * GetObject(const std::string &name, int index=0) const
Get object by name and index.
NGnNavigator * GetChild(size_t index) const
Get child navigator at index.
size_t fNCells
Number of cells in the projection histogram.
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.
std::vector< std::string > fObjectTypes
Object types.
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.
size_t fLastHoverBin
To avoid spamming the console on hover.
json GetInfoJson() const
Retrieves information about the navigator in JSON format.
NGnTree * fGnTree
Open navigator from file.
size_t GetLastIndexSelected() const
Get last selected index.
std::map< std::string, std::vector< double > > fParameterContentMap
Parameter content map.
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.
void ExportToJson(json &j, NGnNavigator *obj, std::vector< std::string > objectNames)
Export navigator data to JSON.
std::vector< NGnNavigator * > fChildren
Children objects.
void SetNLevels(size_t n)
Set number of levels in hierarchy.
NGnNavigator * GetParent() const
Get parent navigator.
void SetNCells(size_t n)
Set number of cells in projection histogram.
std::map< std::string, std::vector< double > > fParameterErrorContentMap
Parameter error content map.
Int_t fTrigger
last triggered event
void SetParent(NGnNavigator *parent)
Set parent navigator.
std::vector< std::string > fObjectNames
Object names.
void ResizeObjectContentMap(const std::string &name, size_t n)
Resize object content map for a given name.
std::map< std::string, std::vector< double > > GetParameterContentMap() const
Get parameter content map.
std::vector< NGnNavigator * > GetChildren() const
Get vector of child navigators.
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< TObject * > > GetObjectContentMap() const
Get object content map.
std::map< std::string, std::vector< TObject * > > fObjectContentMap
Object content map.
NGnNavigator * GetRoot() const
Returns the root parent of this NGnNavigator.
TH1 * fProjection
Projection histogram.
std::vector< double > GetParameterErrors(const std::string &name) const
Retrieves the error vector for a given parameter name.
NParameters object.
Definition NParameters.h:13
TH1D * GetHisto() const
Returns the associated histogram.
Definition NParameters.h:84
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.
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:1224
static TH1 * ProjectTHnSparse(THnSparse *hns, const std::vector< int > &axes, Option_t *option="")
Project a THnSparse histogram onto specified axes.
Definition NUtils.cxx:1171
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:1342
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:1115
static std::string GetCoordsString(const std::vector< int > &coords, int index=-1, int width=0)
Get string representation of coordinates.
Definition NUtils.cxx:1592
static std::vector< std::vector< int > > Permutations(const std::vector< int > &v)
Generate all permutations of a vector.
Definition NUtils.cxx:1643
WebSocket client for asynchronous communication using libwebsockets.
Definition NWsClient.h:49
bool Connect(const std::string &uriString)
Connect to a WebSocket server.
void Disconnect()
Disconnect from the WebSocket server.
bool Send(const std::string &message)
Send a message to the server.
Global callback function for libwebsockets client events.