ndmspc  v1.1.1-1
NGnTree.cxx
1 #include <cstddef>
2 #include <ctime>
3 #include <numbers>
4 #include <string>
5 #include <vector>
6 #include "TAxis.h"
7 #include <TDirectory.h>
8 #include <TObject.h>
9 #include <TList.h>
10 #include <TROOT.h>
11 #include <THnSparse.h>
12 #include <TH1.h>
13 #include <TCanvas.h>
14 #include <TSystem.h>
15 #include <TMap.h>
16 #include <TObjString.h>
17 #include <TTree.h>
18 #include <TBufferJSON.h>
19 #include <sys/poll.h>
20 #include "NParameters.h"
21 #include "NStorageTree.h"
22 #include "NBinning.h"
23 #include "NBinningDef.h"
24 #include "NDimensionalExecutor.h"
25 #include "NGnThreadData.h"
26 #include "NLogger.h"
27 #include "NTreeBranch.h"
28 #include "NUtils.h"
29 #include "NStorageTree.h"
30 #include "NWsClient.h"
31 #include "NGnNavigator.h"
32 #include "NGnTree.h"
33 
34 #include <nlohmann/json.hpp>
35 using json = nlohmann::json;
36 
38 ClassImp(Ndmspc::NGnTree);
40 
41 namespace Ndmspc {
42 
43 std::string NGnTree::BuildObjectPath(const json & cfg, const json & objCfg, const NBinningPoint * point)
44 {
45  std::string objPath = "";
46  if (objCfg.contains("prefix") && objCfg["prefix"].is_string()) {
47  objPath = objCfg["prefix"].get<std::string>();
48  }
49 
50  std::string axisObjectDefaultFormat =
51  cfg["axisObjectDefaultFormat"].is_string() ? cfg["axisObjectDefaultFormat"].get<std::string>() : "%.2f_%.2f";
52  std::string axisDefaultSeparator = cfg["axisDefaultSeparator"].is_string() ? cfg["axisDefaultSeparator"].get<std::string>() : "/";
53 
54  std::string lastSep;
55  for (auto & axisEntry : cfg["axes"]) {
56  std::string axisName;
57  std::string mode;
58  std::string format;
59 
60  if (axisEntry.is_string()) {
61  axisName = axisEntry.get<std::string>();
62  if (axisObjectDefaultFormat.empty()) {
63  mode = "bin";
64  }
65  else {
66  mode = "minmax";
67  format = axisObjectDefaultFormat;
68  }
69  }
70  else if (axisEntry.is_object()) {
71  if (axisEntry.contains("name") && axisEntry["name"].is_string()) {
72  axisName = axisEntry["name"].get<std::string>();
73  }
74  else {
75  continue;
76  }
77  if (axisEntry.contains("mode") && axisEntry["mode"].is_string()) {
78  mode = axisEntry["mode"].get<std::string>();
79  }
80  if (axisEntry.contains("format") && axisEntry["format"].is_string()) {
81  format = axisEntry["format"].get<std::string>();
82  }
83  }
84  else {
85  continue;
86  }
87 
88  if (mode.empty()) {
89  if (axisObjectDefaultFormat.empty())
90  mode = "bin";
91  else
92  mode = "minmax";
93  }
94  if (format.empty()) {
95  if (mode == "minmax")
96  format = axisObjectDefaultFormat.empty() ? "%.2f_%.2f" : axisObjectDefaultFormat;
97  else if (mode == "bin")
98  format = "%d";
99  else
100  format = "%.2f";
101  }
102 
103  if (mode == "minmax") {
104  double min = point->GetBinMin(axisName);
105  double max = point->GetBinMax(axisName);
106  objPath += TString::Format(format.c_str(), min, max).Data();
107  }
108  else if (mode == "min") {
109  double min = point->GetBinMin(axisName);
110  objPath += TString::Format(format.c_str(), min).Data();
111  }
112  else if (mode == "max") {
113  double max = point->GetBinMax(axisName);
114  objPath += TString::Format(format.c_str(), max).Data();
115  }
116  else if (mode == "center") {
117  double c = point->GetBinCenter(axisName);
118  objPath += TString::Format(format.c_str(), c).Data();
119  }
120  else if (mode == "label") {
121  std::string lbl = point->GetBinLabel(axisName);
122  objPath += lbl;
123  }
124  else if (mode == "bin") {
125  objPath += std::to_string(point->GetBin(axisName));
126  }
127  else {
128  objPath += std::to_string(point->GetBin(axisName));
129  }
130 
131  std::string sep = axisDefaultSeparator;
132  if (axisEntry.is_object() && axisEntry.contains("sufix") && axisEntry["sufix"].is_string()) {
133  sep = axisEntry["sufix"].get<std::string>();
134  }
135  objPath += sep;
136  lastSep = sep;
137  }
138 
139  if (!lastSep.empty() && objPath.size() >= lastSep.size()) {
140  objPath = objPath.substr(0, objPath.size() - lastSep.size());
141  }
142  if (objCfg.contains("sufix") && objCfg["sufix"].is_string()) {
143  objPath += objCfg["sufix"].get<std::string>();
144  }
145 
146  return objPath;
147 }
156 NGnTree::NGnTree() : TObject() {}
157 
158 NGnTree::NGnTree(std::vector<TAxis *> axes, std::string filename, std::string treename) : TObject(), fInput(nullptr)
159 {
163  if (axes.empty()) {
164  NLogError("NGnTree::NGnTree: No axes provided, binning is nullptr.");
165  // fBinning = new NBinning();
166  MakeZombie();
167  return;
168  }
169  fBinning = new NBinning(axes);
171  fTreeStorage->InitTree(filename, treename);
172  fNavigator = new NGnNavigator();
173  fNavigator->SetGnTree(this);
174 }
175 NGnTree::NGnTree(TObjArray * axes, std::string filename, std::string treename) : TObject(), fInput(nullptr)
176 {
180 
181  if (axes == nullptr) {
182  NLogError("NGnTree::NGnTree: Axes TObjArray is nullptr.");
183  MakeZombie();
184  return;
185  }
186 
187  if (axes == nullptr && axes->GetEntries() == 0) {
188  NLogError("NGnTree::NGnTree: No axes provided, binning is nullptr.");
189  MakeZombie();
190  return;
191  }
192  fBinning = new NBinning(axes);
194  fTreeStorage->InitTree(filename, treename);
195  fNavigator = new NGnNavigator();
196  fNavigator->SetGnTree(this);
197 }
198 
199 NGnTree::NGnTree(NGnTree * ngnt, std::string filename, std::string treename) : TObject(), fInput(nullptr)
200 {
204  if (ngnt == nullptr) {
205  NLogError("NGnTree::NGnTree: NGnTree is nullptr.");
206  MakeZombie();
207  return;
208  }
209 
210  if (ngnt->GetBinning() == nullptr) {
211  NLogError("NGnTree::NGnTree: Binning in NGnTree is nullptr.");
212  MakeZombie();
213  return;
214  }
215 
216  // TODO: Import binning from user
217  fBinning = (NBinning *)ngnt->GetBinning()->Clone();
219  fTreeStorage->InitTree(filename, treename);
220  fNavigator = new NGnNavigator();
221  fNavigator->SetGnTree(this);
222 }
223 
224 NGnTree::NGnTree(NBinning * b, NStorageTree * s) : TObject(), fBinning(b), fTreeStorage(s), fInput(nullptr)
225 {
229  if (fBinning == nullptr) {
230  NLogError("NGnTree::NGnTree: Binning is nullptr.");
231  MakeZombie();
232  }
233  if (s == nullptr) {
235  fTreeStorage->InitTree("", "ngnt");
236  }
237 
238  if (fTreeStorage == nullptr) {
239  NLogError("NGnTree::NGnTree: Storage tree is nullptr.");
240  MakeZombie();
241  }
242 
243  // fBinning->Initialize();
244  //
245  // TODO: Check if this is needed
248  fNavigator = new NGnNavigator();
249  fNavigator->SetGnTree(this);
250 }
251 NGnTree::NGnTree(THnSparse * hns, std::string parameterAxis, const std::string & outFileName, json cfg)
252  : TObject(), fInput(nullptr)
253 {
257 
258  std::map<std::string, std::vector<std::vector<int>>> b;
259  TObjArray * axes = new TObjArray();
260  int parameterAxisIdx = -1;
261  std::vector<std::string> labels;
262  for (int i = 0; i < hns->GetNdimensions(); i++) {
263  TAxis * axisIn = (TAxis *)hns->GetAxis(i);
264  TAxis * axis = (TAxis *)axisIn->Clone();
265 
266  // check if parameterAxis matches axis name
267  if (parameterAxis.compare(axis->GetName()) == 0) {
268  parameterAxisIdx = i;
269  TAxis * axis = hns->GetAxis(parameterAxisIdx);
270  for (int bin = 1; bin <= axis->GetNbins(); bin++) {
271  // NLogInfo("Axis bin %d label: %s", bin, axis->GetBinLabel(bin));
272  labels.push_back(axis->GetBinLabel(bin));
273  }
274  continue;
275  }
276 
277  // set label
278  if (axisIn->IsAlphanumeric()) {
279  NLogTrace("Setting axis '%s' labels from input THnSparse", axis->GetName());
280  for (int bin = 1; bin <= axisIn->GetNbins(); bin++) {
281  std::string label = axisIn->GetBinLabel(bin);
282  if (!labels.empty()) axis->SetBinLabel(bin, axisIn->GetBinLabel(bin));
283  }
284  }
285 
286  axes->Add(axis);
287  b[axis->GetName()] = {{1}};
288  }
289 
290  // json cfg;
291  cfg["_parameterAxis"] = parameterAxisIdx;
292  cfg["_labels"] = labels;
293 
294  // return nullptr;
295  NLogDebug("Importing THnSparse as NGnTree with parameter axis '%s' (index %d) ...", parameterAxis.c_str(),
296  parameterAxisIdx);
297  NGnTree * ngnt = new NGnTree(axes, outFileName);
298  NLogDebug("Created NGnTree for THnSparse import ...");
299  if (ngnt->IsZombie()) {
300  NLogError("NGnTree::Import: Failed to create NGnTree !!!");
301  MakeZombie();
302  return;
303  }
304  // ngnt->GetStorageTree()->GetTree()->GetUserInfo()->Add(hns->Clone());
305  // Get env variable for tmp dir $NDMSPC_TMP_DIR
306  const char * tmpDirStr = gSystem->Getenv("NDMSPC_TMP_DIR");
307 
308  std::string tmpDir;
309 
310  if (!tmpDirStr || tmpDir.empty()) {
311  tmpDir = "/tmp";
312  }
313  std::string tmpFilename = tmpDir + "/ngnt_imported_input" + std::to_string(gSystem->GetPid()) + ".root";
314  NGnTree * ngntIn = new NGnTree(axes, tmpFilename);
315  if (ngntIn->IsZombie()) {
316  NLogError("NGnTree::Import: Failed to create NGnTree for input !!!");
317  SafeDelete(ngnt);
318  MakeZombie();
319  return;
320  }
321  // ngntIn->GetStorageTree()->GetTree()->GetUserInfo()->Add(hns->Clone());
322  ngntIn->GetOutput("default")->Add(hns->Clone("test"));
323  ngntIn->Close(true);
324 
325  // return;
326  // delete ngntIn;
327 
328  ngnt->SetInput(NGnTree::Open(tmpFilename)); // Set input to self
329 
330  ngnt->GetInput()->Print();
331 
332  ngnt->GetBinning()->AddBinningDefinition("default", b);
333  ngnt->InitParameters(cfg["_labels"].get<std::vector<std::string>>());
334 
335  Ndmspc::NGnProcessFuncPtr processFunc = [](Ndmspc::NBinningPoint * point, TList * /*output*/, TList * outputPoint,
336  int /*threadId*/) {
337  // NLogInfo("Thread ID: %d", threadId);
338  TH1::AddDirectory(kFALSE); // Prevent histograms from being associated with the current directory
339  // point->Print();
340  json cfg = point->GetCfg();
341 
342 
343  NGnTree * ngntIn = point->GetInput();
344  if (!ngntIn) {
345  NLogError("NGnTree::Import: Input NGnTree is nullptr !!!");
346  return;
347  }
348  // ngntIn->Print();
349 
350  THnSparse * hns = (THnSparse *)ngntIn->GetOutput("default")->At(0);
351  if (hns == nullptr) {
352  NLogError("NGnTree::Import: THnSparse 'hns' not found in storage tree !!!");
353  return;
354  }
355 
356  int axisIdx = cfg["_parameterAxis"].get<int>();
357  std::vector<std::vector<int>> ranges;
358  // set ranges from point storage coords
359  int iAxis = 0;
360  for (int i = 0; i < hns->GetNdimensions(); i++) {
361  if (i == axisIdx) continue; // skip parameter axis
362  int coord = point->GetStorageCoords()[iAxis++];
363  ranges.push_back({i, coord, coord});
364  // NLogInfo("Setting axis %d range to [%d, %d]", i, coord, coord);
365  }
366 
367  NUtils::SetAxisRanges(hns, ranges);
368  TH1 * h = hns->Projection(axisIdx, "O");
369  if (!h) {
370  NLogError("NGnTree::Import: Projection of THnSparse failed !!!");
371  return;
372  }
373  if (h->GetEntries() > 0) {
374  NParameters * params = point->GetParameters();
375  if (params) {
376  for (int bin = 1; bin <= h->GetNbinsX(); bin++) {
377  params->SetParameter(bin, h->GetBinContent(bin), h->GetBinError(bin));
378  }
379  }
380  // outputPoint->Add(hParams);
381  outputPoint->Add(h);
382 
383  std::string filename = cfg["filename"].get<std::string>();
384  TFile * f = (TFile *)point->GetTempObject("file");
385  if (!f || filename.compare(f->GetName()) != 0) {
386  if (f) {
387  NLogDebug("NGnTree::Import: Closing previously opened file '%s' ...", f->GetName());
388  f->Close();
389  }
390  NLogDebug("NGnTree::Import: Opening file '%s' ...", filename.c_str());
391  f = TFile::Open(filename.c_str());
392  if (!f || f->IsZombie()) {
393  NLogError("NGnTree::Import: Cannot open file '%s' !!!", filename.c_str());
394  return;
395  }
396  point->SetTempObject("file", f);
397  }
398 
399  std::string axisObjectDefaultFormat =
400  cfg["axisObjectDefaultFormat"].is_string() ? cfg["axisObjectDefaultFormat"].get<std::string>() : "%.2f_%.2f";
401  std::string axisDefaultSeparator =
402  cfg["axisDefaultSeparator"].is_string() ? cfg["axisDefaultSeparator"].get<std::string>() : "/";
403  bool dryrun = false;
404  if (cfg.contains("dryrun") && cfg["dryrun"].is_boolean()) {
405  dryrun = cfg["dryrun"].get<bool>();
406  }
407 
408  if (dryrun) {
409  NLogInfo("NGnTree::Import (dryrun): '%s' ...", point->GetString().c_str());
410  }
411 
412  // loop over all object in cfg["objects"]
413  for (auto & [objName, objCfg] : cfg["objects"].items()) {
414  std::string objPath = NGnTree::BuildObjectPath(cfg, objCfg, point);
415 
416  if (dryrun) {
417  NLogInfo("NGnTree::Import (dryrun): would retrieve object '%s'", objPath.c_str());
418  continue;
419  }
420 
421  if (point->GetEntryNumber() == 0) {
422  NLogInfo("NGnTree::Import: Retrieving object '%s' from file '%s' ...", objPath.c_str(),
423  cfg["filename"].get<std::string>().c_str());
424  }
425  TObject * obj = f->Get(objPath.c_str());
426  if (!obj) {
427  if (point->GetEntryNumber() == 0) {
428  NLogWarning("NGnTree::Import: Cannot get object '%s' from file '%s' !!!", objPath.c_str(),
429  cfg["filename"].get<std::string>().c_str());
430  }
431  continue;
432  }
433 
434  if (obj->InheritsFrom(TCanvas::Class())) {
435  TCanvas * cObj = (TCanvas *)obj;
436  cObj->SetName(objName.c_str());
437  }
438  outputPoint->Add(obj->Clone(objName.c_str()));
439  }
440  // f->Close();
441  }
442  };
443 
444  // NUtils::SetAxisRanges(, std::vector<std::vector<int>> ranges)
445  ngnt->Process(processFunc, cfg);
446  ngnt->Close(true);
447  // Remove tmp file
448  gSystem->Exec(TString::Format("rm -f %s", tmpFilename.c_str()));
449 }
450 
452 {
456 
457  SafeDelete(fBinning);
458  // SafeDelete(fTreeStorage);
459  // SafeDelete(fNavigator);
460  SafeDelete(fWsClient);
461  SafeDelete(fParameters);
462 }
463 void NGnTree::Print(Option_t * option) const
464 {
468 
469  TString opt = option;
470 
471  // Print list of axes
472  NLogInfo("NGnTree::Print: Printing NGnTree object [ALL] ...");
473  if (fBinning) {
474  fBinning->Print(option);
475  }
476  else {
477  NLogError("Binning is not initialized in NGnTree !!!");
478  }
479  if (fTreeStorage) {
480  fTreeStorage->Print(option);
481  }
482  else {
483  NLogError("Storage tree is not initialized in NGnTree !!!");
484  }
485 }
486 
487 void NGnTree::Draw(Option_t * /*option*/)
488 {
492 
493  NLogInfo("NGnTree::Draw: Drawing NGnTree object [not implemented yet]...");
494 }
495 
496 bool NGnTree::Process(NGnProcessFuncPtr func, const json & cfg, std::string binningName, NGnBeginFuncPtr beginFunc,
497  NGnEndFuncPtr endFunc)
498 {
502 
503  if (!fBinning) {
504  NLogError("Binning is not initialized in NGnTree !!!");
505  return false;
506  }
507 
508  NBinning * binningIn = (NBinning *)fBinning->Clone();
509 
510  std::vector<std::string> defNames = fBinning->GetDefinitionNames();
511  if (!binningName.empty()) {
512  // Check if binning definitions exist
513  if (std::find(defNames.begin(), defNames.end(), binningName) == defNames.end()) {
514  NLogError("Binning definition '%s' not found in NGnTree !!!", binningName.c_str());
515  return false;
516  }
517  defNames.clear();
518  defNames.push_back(binningName);
519  }
520 
521  fBinning->Reset();
522  fBinning->SetCfg(cfg); // Set configuration to binning point
523  bool rc = Process(func, defNames, cfg, binningIn, beginFunc, endFunc);
524  if (!rc) {
525  NLogError("NGnTree::Process: Processing failed !!!");
526  return false;
527  }
528  // bool rc = false;
529  return true;
530 }
531 
532 bool NGnTree::Process(NGnProcessFuncPtr func, const std::vector<std::string> & defNames, const json & cfg,
533  NBinning * binningIn, NGnBeginFuncPtr beginFunc, NGnEndFuncPtr endFunc)
534 {
538 
539  NLogInfo("NGnTree::Process: Starting processing with %zu definitions ...", defNames.size());
540  bool batch = gROOT->IsBatch();
541  gROOT->SetBatch(kTRUE);
542  TH1::AddDirectory(kFALSE);
543 
545 
546  int nThreads = ROOT::GetThreadPoolSize(); // Get the number of threads to use
547  if (nThreads < 1) nThreads = 1;
548  const char * wsUrl = gSystem->Getenv("NDMSPC_WS_URL");
549  if (!fWsClient && wsUrl) {
550  fWsClient = new NWsClient();
551  fWsClient->Connect(wsUrl);
552  NLogInfo("NGnTree::Process: Connected to WebSocket server at '%s'", wsUrl);
553  }
554 
555  std::vector<Ndmspc::NGnThreadData> threadDataVector(nThreads);
556 
557  const char * tmpDirEnv = gSystem->Getenv("NDMSPC_TMP_DIR");
558  std::string tmpDir = tmpDirEnv ? tmpDirEnv : "/tmp";
559  TString tmpDirStr = fTreeStorage->GetPrefix();
560  // check if tmpDir starts with root:// or http:// or https://
561  if (!(tmpDirStr.BeginsWith("root://") || tmpDirStr.BeginsWith("http://") || tmpDirStr.BeginsWith("https://"))) {
562  tmpDir = tmpDirStr.Data();
563  }
564 
565  std::string jobDir = tmpDir + "/.ndmspc/tmp/" + std::to_string(gSystem->GetPid());
566  std::string filePrefix = jobDir;
567  for (size_t i = 0; i < threadDataVector.size(); ++i) {
568  std::string filename = filePrefix + "/" + std::to_string(i) + "/" + fTreeStorage->GetPostfix();
569  bool rc = threadDataVector[i].Init(i, func, beginFunc, endFunc, this, binningIn, fInput, filename,
570  fTreeStorage->GetTree()->GetName());
571  if (!rc) {
572  NLogError("Failed to initialize thread data %zu, exiting ...", i);
573  return false;
574  }
575  threadDataVector[i].SetCfg(cfg); // Set configuration to binning point
576  }
577  size_t processedEntries = 0;
578  size_t totalEntries = 0;
579  auto start_par = std::chrono::high_resolution_clock::now();
580  auto task = [&](const std::vector<int> & coords, Ndmspc::NGnThreadData & thread_obj) {
581  // NLogWarning("Processing coordinates %s in thread %zu", NUtils::GetCoordsString(coords).c_str(),
582  // thread_obj.GetAssignedIndex());
583  // thread_obj.Print();
584  json progress;
585  progress["jobName"] = thread_obj.GetHnSparseBase()->GetBinning()->GetCurrentDefinitionName();
586  progress["status"] = "R";
587  progress["task"] = coords[0];
588  NWsClient * wsClient = thread_obj.GetHnSparseBase()->GetWsClient();
589  if (wsClient) wsClient->Send(progress.dump());
590  thread_obj.Process(coords);
591  processedEntries++;
592  progress["status"] = "D";
593  if (wsClient) wsClient->Send(progress.dump());
594 
595  if (!NLogger::GetConsoleOutput()) {
596  size_t nRunning = (totalEntries - processedEntries >= threadDataVector.size()) ? threadDataVector.size()
597  : totalEntries - processedEntries;
598  NUtils::ProgressBar(processedEntries, totalEntries, start_par, TString::Format("R%4zu", nRunning).Data());
599  }
600  };
601  size_t iDef = 0;
602  int sumIds = 0;
603 
604  for (auto & name : defNames) {
605  auto binningDef = binningIn->GetDefinition(name);
606  if (!binningDef) {
607  NLogError("NGnTree::Process: Binning definition '%s' not found in NGnTree !!!", name.c_str());
608  return false;
609  }
610  std::vector<int> mins, maxs;
611  mins.push_back(0);
612  maxs.push_back(binningDef->GetIds().size() - 1);
613  NLogDebug("NGnTree::Process: Processing with binning definition '%s' with %zu entries", name.c_str(),
614  binningDef->GetIds().size());
615 
617  NLogInfo("NGnTree::Process: Processing binning definition '%s' with %d tasks ...", name.c_str(), maxs[0] + 1);
618  }
619  else {
620  Printf("Processing binning definition '%s' with %d tasks ...", name.c_str(), maxs[0] + 1);
621  }
622  totalEntries = maxs[0] + 1;
624  NUtils::ProgressBar(processedEntries, totalEntries, start_par,
625  TString::Format("R%4d", (int)threadDataVector.size()).Data());
626 
627  for (size_t i = 0; i < threadDataVector.size(); ++i) {
628  threadDataVector[i].GetHnSparseBase()->GetBinning()->SetCurrentDefinitionName(name);
629  }
630  json jobMon;
631  jobMon["jobName"] = name;
632  // jobMon["user"] = gSystem->Getenv("USER");
633  // jobMon["host"] = gSystem->HostName();
634  jobMon["pid"] = gSystem->GetPid();
635  jobMon["status"] = "I";
636  jobMon["ntasks"] = maxs[0] + 1;
637  if (fWsClient) {
638  NLogInfo("NGnTree::Process: Sending job monitor info to WebSocket server ...");
639  fWsClient->Send(jobMon.dump());
640  }
642 
643  // Disable ROOT's RecursiveRemove during the parallel phase.
644  // Without this, concurrent threads' object deletions trigger RecursiveRemove
645  // which iterates pad->fPrimitives without per-object locks → TObjLink corruption.
646  Bool_t prevMustClean = gROOT->MustClean();
647  gROOT->SetMustClean(kFALSE);
648 
649  // Enable batch mode during the parallel phase so that ROOT drawing calls
650  // (e.g. TH1::Fit drawing the fit function via f1->Draw) are no-ops.
651  // Without this, concurrent threads each trigger ROOT canvas creation ("c1"),
652  // the second canvas creation deletes the first → double-free / heap corruption,
653  // which then causes TPad::RecursiveRemove to crash when closing the per-thread TTree.
654  Bool_t prevBatch = gROOT->IsBatch();
655  gROOT->SetBatch(kTRUE);
656 
657  Ndmspc::NDimensionalExecutor executorMT(mins, maxs);
658  executorMT.ExecuteParallel<Ndmspc::NGnThreadData>(task, threadDataVector);
659 
661  Printf("Finished processing binning definition '%s'. Post-processing results ...", name.c_str());
662 
663  // Restore both flags before flushing deferred deletes, so each object's destructor
664  // properly calls gROOT->RecursiveRemove and removes itself from ROOT's global lists.
665  // This prevents dangling pointers that would crash the RecursiveRemove cascade
666  // triggered by TTree::~TTree when the per-thread storage files are closed below.
667  // It is safe here because all worker threads have already finished.
668  gROOT->SetMustClean(prevMustClean);
669  gROOT->SetBatch(prevBatch);
670 
671  // Flush deferred deletes single-threaded with MustClean and batch mode restored.
672  for (size_t i = 0; i < threadDataVector.size(); ++i) {
673  threadDataVector[i].FlushDeferredDeletes();
674  }
675 
676  for (size_t i = 0; i < threadDataVector.size(); ++i) {
677  threadDataVector[i].ExecuteEndFunction();
678  }
679  // Update hnsbBinningIn with the processed ids
680  NLogDebug("NGnTree::Process: [BEGIN] ------------------------------------------------");
681  sumIds += binningIn->GetDefinition(name)->GetIds().size();
682  binningIn->GetDefinition(name)->GetIds().clear();
683  for (size_t i = 0; i < threadDataVector.size(); ++i) {
684  NLogDebug("NGnTree::Process: -> Thread %zu processed %lld entries", i, threadDataVector[i].GetNProcessed());
685  // threadDataVector[i].GetHnSparseBase()->GetBinning()->GetDefinition(name)->Print();
686  binningIn->GetDefinition(name)->GetIds().insert(
687  binningIn->GetDefinition(name)->GetIds().end(),
688  threadDataVector[i].GetHnSparseBase()->GetBinning()->GetDefinition(name)->GetIds().begin(),
689  threadDataVector[i].GetHnSparseBase()->GetBinning()->GetDefinition(name)->GetIds().end());
690  sort(binningIn->GetDefinition(name)->GetIds().begin(), binningIn->GetDefinition(name)->GetIds().end());
691  }
692  // hnsbBinningIn->GetDefinition(name)->Print();
693  // remove entries present in hnsbBinningIn from other definitions
694  for (size_t i = 0; i < defNames.size(); i++) {
695 
696  std::string other_name = defNames[i];
697  auto otherDef = binningIn->GetDefinition(other_name);
698  if (i <= iDef) {
699  continue;
700  }
701  if (!otherDef) {
702  NLogError("NGnTree::Process: Binning definition '%s' not found in NGnTree !!!", other_name.c_str());
703  return false;
704  }
705  // remove entries that has value less then sumIds
706  for (auto it = otherDef->GetIds().begin(); it != otherDef->GetIds().end();) {
707  NLogDebug("NGnTree::Process: Checking entry %lld from definition '%s' against sumIds=%d", *it,
708  other_name.c_str(), sumIds);
709  if (*it < sumIds) {
710  NLogDebug("NGnTree::Process: Removing entry %lld from definition '%s'", *it, other_name.c_str());
711  it = otherDef->GetIds().erase(it);
712  }
713  else {
714  ++it;
715  }
716  }
717 
718  binningIn->GetDefinition(other_name)->Print();
719  }
720  // hnsbBinningIn->GetDefinition(name)->Print();
721  iDef++;
722 
723  NLogDebug("NGnTree::Process: [END] ------------------------------------------------");
724  auto end_par = std::chrono::high_resolution_clock::now();
725  std::chrono::duration<double, std::milli> par_duration = end_par - start_par;
726 
727  NLogInfo("NGnTree::Process: Execution completed and it took %s .",
728  NUtils::FormatTime(par_duration.count() / 1000).c_str());
729 
730  NLogInfo("NGnTree::Process: Post processing %zu results ...", threadDataVector.size());
731  for (auto & data : threadDataVector) {
732  NLogTrace("NGnTree::Process: Closing file from thread %zu: ", data.GetAssignedIndex());
733  data.GetHnSparseBase()->GetStorageTree()->Close(true);
734  }
735 
736  NLogDebug("NGnTree::Process: Merging %zu results ...", threadDataVector.size());
737  TList * mergeList = new TList();
738  Ndmspc::NGnThreadData * outputData = new Ndmspc::NGnThreadData();
739  outputData->Init(0, func, nullptr, nullptr, this, binningIn);
740  outputData->SetCfg(cfg);
741  // outputData->Init(0, func, this);
742 
743  for (auto & data : threadDataVector) {
744  NLogTrace("NGnTree::Process: Adding thread data %zu to merge list ...", data.GetAssignedIndex());
745  // data.GetHnSparseBase()->GetBinning()->GetPoint()->SetCfg(cfg);
746  mergeList->Add(&data);
747  }
748 
749  Long64_t nmerged = outputData->Merge(mergeList);
750  if (nmerged <= 0) {
751  NLogError("NGnTree::Process: Failed to merge thread data, exiting ...");
752  delete mergeList;
753  return false;
754  }
755  NLogInfo("NGnTree::Process: Merged %lld outputs successfully", nmerged);
756  // delete all temporary files
757  // for (auto & data : threadDataVector) {
758  // std::string filename = data.GetHnSparseBase()->GetStorageTree()->GetFileName();
759  // NLogTrace("NGnTree::Process: Deleting temporary file '%s' ...", filename.c_str());
760  // gSystem->Exec(TString::Format("rm -f %s", filename.c_str()));
761  // }
762  //
763 
764  fTreeStorage = outputData->GetHnSparseBase()->GetStorageTree();
765  fOutputs = outputData->GetHnSparseBase()->GetOutputs();
766  fBinning = outputData->GetHnSparseBase()->GetBinning(); // Update binning to the merged one
767  fParameters = outputData->GetHnSparseBase()->GetParameters();
768 
770  NLogInfo("NGnTree::Process: Processing completed successfully. Output was stored in '%s'.",
771  fTreeStorage->GetFileName().c_str());
772  }
773  else {
774  Printf("Processing completed successfully. Output was stored in '%s'.", fTreeStorage->GetFileName().c_str());
775  }
776 
777  jobMon["status"] = "D";
778  if (fWsClient) {
779  fWsClient->Send(jobMon.dump());
780  }
781  }
782 
783  if (fWsClient) {
784  // gSystem->Sleep(1000); // wait for messages to be sent
786  SafeDelete(fWsClient);
787  }
788 
789  // Close the final output file
790  Close(true);
791 
792  gSystem->Exec(TString::Format("rm -fr %s", jobDir.c_str()));
793  gROOT->SetBatch(batch); // Restore ROOT batch mode
794  return true;
795 }
796 TList * NGnTree::GetOutput(std::string name)
797 {
801 
802  if (name.empty()) {
804  }
805  if (fOutputs.find(name) == fOutputs.end()) {
806  fOutputs[name] = new TList();
807  fOutputs[name]->SetName(name.c_str());
808  }
809  return fOutputs[name];
810 }
811 
812 NGnTree * NGnTree::Open(const std::string & filename, const std::string & branches, const std::string & treename)
813 {
817 
818  NLogDebug("Opening '%s' with branches='%s' and treename='%s' ...", filename.c_str(), branches.c_str(),
819  treename.c_str());
820 
821  TFile * file = TFile::Open(filename.c_str());
822  if (!file) {
823  NLogError("NGnTree::Open: Cannot open file '%s'", filename.c_str());
824  return nullptr;
825  }
826 
827  TTree * tree = (TTree *)file->Get(treename.c_str());
828  if (!tree) {
829  NLogError("NGnTree::Open: Cannot get tree '%s' from file '%s'", treename.c_str(), filename.c_str());
830  return nullptr;
831  }
832 
833  return Open(tree, branches, file);
834 }
835 
836 NGnTree * NGnTree::Open(TTree * tree, const std::string & branches, TFile * file)
837 {
841 
842  NBinning * hnstBinning = (NBinning *)tree->GetUserInfo()->At(0);
843  if (!hnstBinning) {
844  NLogError("NGnTree::Open: Cannot get binning from tree '%s'", tree->GetName());
845  return nullptr;
846  }
847  NStorageTree * hnstStorageTree = (NStorageTree *)tree->GetUserInfo()->At(1);
848  if (!hnstStorageTree) {
849  NLogError("NGnTree::Open: Cannot get tree storage info from tree '%s'", tree->GetName());
850  return nullptr;
851  }
852 
853  std::map<std::string, TList *> outputs;
854  TDirectory * dir = nullptr;
855  if (file) {
856  dir = (TDirectory *)file->Get("outputs");
857  auto l = dir->GetListOfKeys();
858  for (auto kv : *l) {
859  TObject * obj = dir->Get(kv->GetName());
860  if (!obj) continue;
861  TList * l = dynamic_cast<TList *>(obj);
862  if (!l) continue;
863  outputs[l->GetName()] = l;
864  NLogDebug("Imported output list for binning '%s' with %d object(s) from file '%s'", l->GetName(), l->GetEntries(),
865  file->GetName());
866  }
867  }
868  // TDirectory * dir = (TDirectory *)tree->GetUserInfo()->FindObject("outputs");
869  // if (dir) {
870  // dir->Print();
871  // }
872 
873  NGnTree * ngnt = new NGnTree(hnstBinning, hnstStorageTree);
874 
875  if (!hnstStorageTree->SetFileTree(file, tree)) return nullptr;
876  // if (!ngnt->InitBinnings({})) return nullptr;
877  // ngnt->Print();
878  // Get list of branches
879  std::vector<std::string> enabledBranches;
880  if (!branches.empty()) {
881  enabledBranches = Ndmspc::NUtils::Tokenize(branches, ',');
882  NLogTrace("NGnTree::Open: Enabled branches: %s", NUtils::GetCoordsString(enabledBranches, -1).c_str());
883  hnstStorageTree->SetEnabledBranches(enabledBranches);
884  }
885  else {
886  // loop over all branches and set address
887  for (auto & kv : hnstStorageTree->GetBranchesMap()) {
888  NLogTrace("NGnTree::Open: Enabled branches: %s", kv.first.c_str());
889  }
890  }
891  // Set all branches to be read
892  hnstStorageTree->SetBranchAddresses();
893  ngnt->SetOutputs(outputs);
894 
895  NGnNavigator * nav = new NGnNavigator();
896  nav->SetGnTree(ngnt);
897  ngnt->SetNavigator(nav);
898 
899  return ngnt;
900 }
901 
903 {
907 
908  if (fNavigator) {
909  NLogTrace("NGnTree::SetNavigator: Replacing existing navigator ...");
910  SafeDelete(fNavigator);
911  }
912 
913  fNavigator = navigator;
914 }
915 
916 bool NGnTree::Close(bool write)
917 {
921 
922  if (!fTreeStorage) {
923  NLogError("NGnTree::Close: Storage tree is not initialized in NGnTree !!!");
924  return false;
925  }
926 
927  return fTreeStorage->Close(write, fOutputs);
928 }
929 
930 Int_t NGnTree::GetEntry(Long64_t entry, bool checkBinningDef)
931 {
935  if (!fTreeStorage) {
936  NLogError("NGnTree::GetEntry: Storage tree is not initialized in NGnTree !!!");
937  return -1;
938  }
939 
940  int bytes =
941  fTreeStorage->GetEntry(entry, fBinning->GetPoint(0, fBinning->GetCurrentDefinitionName()), checkBinningDef);
942  if (fTreeStorage->GetBranch("_params")) fParameters = (NParameters *)fTreeStorage->GetBranch("_params")->GetObject();
943  return bytes;
944 }
945 Int_t NGnTree::GetEntry(std::vector<std::vector<int>> /*range*/, bool checkBinningDef)
946 {
950 
951  return GetEntry(0, checkBinningDef);
952 }
953 
954 void NGnTree::Play(int timeout, std::string binning, std::vector<int> outputPointIds,
955  std::vector<std::vector<int>> ranges, Option_t * option, std::string ws)
956 {
960  TString opt = option;
961  opt.ToUpper();
962 
963  std::string annimationTempDir =
964  TString::Format("%s/.ndmspc/animation/%d", gSystem->Getenv("HOME"), gSystem->GetPid()).Data();
965  gSystem->Exec(TString::Format("mkdir -p %s", annimationTempDir.c_str()));
966 
967  Ndmspc::NWsClient * client = nullptr;
968 
969  if (!ws.empty()) {
970  client = new Ndmspc::NWsClient();
971  if (!client->Connect(ws)) {
972  NLogError("Failed to connect to '%s' !!!", ws.c_str());
973  return;
974  }
975  NLogInfo("Connected to %s", ws.c_str());
976  }
977 
978  if (binning.empty()) {
979  binning = fBinning->GetCurrentDefinitionName();
980  }
981 
982  NBinningDef * binningDef = fBinning->GetDefinition(binning);
983  if (!binningDef) {
984  NLogError("NGnTree::Play: Binning definition '%s' not found in NGnTree !!!", binning.c_str());
985  NLogError("Available binning definitions:");
986  for (auto & name : fBinning->GetDefinitionNames()) {
987  if (name == fBinning->GetCurrentDefinitionName())
988  NLogError(" [*] %s", name.c_str());
989  else
990  NLogError(" [ ] %s", name.c_str());
991  }
992  return;
993  }
994 
995  THnSparse * bdContent = (THnSparse *)binningDef->GetContent()->Clone();
996 
997  std::string bdContentName = TString::Format("bdContent_%s", binning.c_str()).Data();
998  // Set axis ranges if provided
999  if (!ranges.empty()) NUtils::SetAxisRanges(bdContent, ranges, false, true);
1000 
1001  Long64_t linBin = 0;
1002  std::unique_ptr<ROOT::Internal::THnBaseBinIter> iter{bdContent->CreateIter(true /*use axis range*/)};
1003  std::vector<Long64_t> ids;
1004  // std::vector<Long64_t> ids = binningDef->GetIds();
1005  while ((linBin = iter->Next()) >= 0) {
1006  // NLogDebug("Original content bin %lld: %f", linBin, bdContentOrig->GetBinContent(linBin));
1007  ids.push_back(linBin);
1008  }
1009  if (ids.empty()) {
1010  NLogWarning("NGnTree::Play: No entries found in binning definition '%s' !!!", binning.c_str());
1011  return;
1012  }
1013  // return;
1014 
1015  TCanvas * c1 = nullptr;
1016  if (!client) {
1017  c1 = (TCanvas *)gROOT->GetListOfCanvases()->FindObject("c1");
1018  if (c1 == nullptr) c1 = new TCanvas("c1", "NGnTree::Play", 800, 600);
1019  c1->Clear();
1020  c1->cd();
1021  c1->DivideSquare(outputPointIds.size() > 0 ? outputPointIds.size() + 1 : 1);
1022  gSystem->ProcessEvents();
1023  }
1024  binningDef->Print();
1025  bdContent->Reset();
1026 
1027  // loop over all ids and print them
1028  for (auto id : ids) {
1029  // for (int id = 0; id < GetEntries(); id++) {
1030  GetEntry(id);
1031  fBinning->GetPoint()->Print();
1032  TList * l = (TList *)fTreeStorage->GetBranch("_outputPoint")->GetObject();
1033  if (!l || l->IsEmpty()) {
1034  NLogWarning("NGnTree::Play: No 'outputPoint' for entry %lld !!!", id);
1035  continue;
1036  }
1037  else {
1038  // NLogInfo("Output for entry %lld:", id);
1039  // l->Print(opt.Data());
1040 
1041  if (outputPointIds.empty()) {
1042  outputPointIds.resize(l->GetEntries());
1043  for (int i = 0; i < l->GetEntries(); i++) {
1044  outputPointIds[i] = i;
1045  }
1046  }
1047  int n = outputPointIds.size();
1048 
1049  if (client) {
1050  std::string msg = TBufferJSON::ConvertToJSON(l).Data();
1051  if (!client->Send(msg)) {
1052  NLogError("Failed to send message `%s`", msg.c_str());
1053  }
1054  else {
1055  NLogTrace("Sent: %s", msg.c_str());
1056  }
1057  }
1058  else {
1059 
1060  Double_t v = 1.0;
1061  for (int i = 0; i < n; i++) {
1062  // NLogDebug("Drawing output object id %d (list index %d) on pad %d", outputPointIds[i], i, i + 1);
1063 
1064  c1->cd(i + 2);
1065  TObject * obj = l->At(outputPointIds[i]);
1066  if (obj) {
1067  if (obj->InheritsFrom(TH1::Class())) {
1068  TH1 * h = (TH1 *)obj;
1069  h->SetDirectory(nullptr);
1070  // Draw a clone to avoid transferring ownership or modifying
1071  // the original object stored in the TList (can cause
1072  // TPad/TList removal during drawing and lead to crashes).
1073  TH1 * hclone = (TH1 *)h->Clone();
1074  if (hclone) {
1075  hclone->SetDirectory(nullptr);
1076  hclone->Draw();
1077  }
1078  }
1079  // obj->Print();
1080  }
1081  if (obj->InheritsFrom(TH1::Class()) && i == 0) {
1082  TH1 * h = (TH1 *)obj;
1083  v = h->GetMean();
1084  NLogDebug("Mean value from histogram [%s]: %f", h->GetName(), v);
1085  }
1086  }
1087  bdContent->SetBinContent(fBinning->GetPoint()->GetStorageCoords(), 1);
1088  c1->cd(1);
1089  TH1 * bdProj = (TH1 *)gROOT->FindObjectAny("bdProj");
1090  if (bdProj) {
1091  delete bdProj;
1092  bdProj = nullptr;
1093  }
1094  if (bdContent->GetNdimensions() == 1) {
1095  bdProj = bdContent->Projection(0, "O");
1096  }
1097  else if (bdContent->GetNdimensions() == 2) {
1098  bdProj = bdContent->Projection(0, 1, "O");
1099  }
1100  else if (bdContent->GetNdimensions() == 3) {
1101  bdProj = bdContent->Projection(0, 1, 2, "O");
1102  }
1103  else {
1104  NLogError("NGnTree::Play: Cannot project THnSparse with %d dimensions", bdContent->GetNdimensions());
1105  }
1106  if (bdProj) {
1107  bdProj->SetName("bdProj");
1108  bdProj->SetTitle(TString::Format("Binning '%s' content projection", binning.c_str()).Data());
1109  bdProj->SetMinimum(0);
1110  // bdProj->SetDirectory(nullptr);
1111  bdProj->Draw("colz");
1112  // c1->ModifiedUpdate();
1113  }
1114  }
1115  }
1116  if (c1) {
1117  c1->ModifiedUpdate();
1118  c1->SaveAs(TString::Format("%s/ndmspc_play_%06lld.png", annimationTempDir.c_str(), bdContent->GetNbins()).Data());
1119  }
1120  gSystem->ProcessEvents();
1121  if (timeout > 0) gSystem->Sleep(timeout);
1122  NLogInfo("%d", id);
1123  }
1124 
1125  NLogInfo("Creating animation gif from %s/ndmspc_play_*.png ...", annimationTempDir.c_str());
1126  gSystem->Exec(
1127  TString::Format("magick -delay 20 -loop 0 %s/ndmspc_play_*.png ndmspc_play.gif", annimationTempDir.c_str()));
1128  gSystem->Exec(TString::Format("rm -fr %s", annimationTempDir.c_str()));
1129  NLogInfo("Animation saved to ndmspc_play.gif");
1130 
1131  if (client) client->Disconnect();
1132  delete bdContent;
1133 }
1134 
1135 TList * NGnTree::Projection(const json & cfg, std::string binningName)
1136 {
1140 
1141  // SetInput(); // Set input to selfp
1143  Ndmspc::NGnProcessFuncPtr processFunc = [](Ndmspc::NBinningPoint * point, TList * output, TList * /*outputPoint*/,
1144  int /*threadId*/) {
1145  // NLogInfo("Thread ID: %d", threadId);
1146  TH1::AddDirectory(kFALSE); // Prevent histograms from being associated with the current directory
1147  point->Print();
1148  json cfg = point->GetCfg();
1149 
1150  Printf("Processing THnSparse projection with configuration: %s", cfg.dump().c_str());
1151 
1152  Ndmspc::NGnTree * ngntIn = point->GetInput();
1153  // ngntIn->Print();
1154  // ngntIn->GetEntry(0);
1155  ngntIn->GetEntry(point->GetEntryNumber());
1156 
1157  // loop over all cfg["objects"]
1158  for (auto & [objName, objCfg] : cfg["objects"].items()) {
1159  NLogInfo("Processing object '%s' ...", objName.c_str());
1160 
1161  THnSparse * hns = (THnSparse *)(ngntIn->GetStorageTree()->GetBranchObject(objName));
1162  if (hns == nullptr) {
1163  NLogError("NGnTree::Projection: THnSparse 'hns' not found in storage tree !!!");
1164  return;
1165  }
1166  // hns->Print("all");
1167  // loop over cfg["objects"][objName] array of projection dimension names
1168  for (size_t i = 0; i < objCfg.size(); i++) {
1169 
1170  NLogInfo("Processing projection %zu for object '%s' ...", i, objName.c_str());
1171  std::vector<int> dims;
1172  std::vector<std::string> dimNames = cfg["objects"][objName][i].get<std::vector<std::string>>();
1173  for (const auto & dimName : dimNames) {
1174  NLogDebug("Looking for dimension name '%s' in THnSparse ...", dimName.c_str());
1175  int dim = -1;
1176  for (int i = 0; i < hns->GetNdimensions(); i++) {
1177  if (dimName == hns->GetAxis(i)->GetName()) {
1178  dim = i;
1179  break;
1180  }
1181  }
1182  if (dim >= 0)
1183  dims.push_back(dim);
1184  else {
1185  NLogError("NGnTree::Projection: Dimension name '%s' not found in THnSparse !!!", dimName.c_str());
1186  }
1187  }
1188  // Print dims
1189  NLogInfo("Projecting THnSparse on dimensions: %s", NUtils::GetCoordsString(dims, -1).c_str());
1190  TH1 * hPrev = (TH1 *)output->At(i);
1191  TH1 * hProj = NUtils::ProjectTHnSparse(hns, dims, "O");
1192  hProj->SetName(TString::Format("%s_proj_%s", objName.c_str(), NUtils::Join(dims, '_').c_str()).Data());
1193  if (hPrev) {
1194  hPrev->Add(hProj);
1195  }
1196  else {
1197  output->Add(hProj);
1198  }
1199  }
1200  }
1201  output->Print();
1202  };
1203 
1204  // NBinningDef * binningDef = fInput->GetBinning()->GetDefinition(binningName);
1205  NBinningDef * binningDef = GetBinning()->GetDefinition(binningName);
1206  THnSparse * hnsIn = binningDef->GetContent();
1207  // std::vector<std::vector<int>> ranges{{0, 2, 2}, {2, 1, 1}};
1208  std::vector<std::vector<int>> ranges = cfg["ranges"].get<std::vector<std::vector<int>>>();
1209  NUtils::SetAxisRanges(hnsIn, ranges); // Set the ranges for the axes
1210  Long64_t linBin = 0;
1211  std::unique_ptr<ROOT::Internal::THnBaseBinIter> iter{hnsIn->CreateIter(true /*use axis range*/)};
1212  std::vector<Long64_t> ids;
1213  // std::vector<Long64_t> ids = binningDef->GetIds();
1214  while ((linBin = iter->Next()) >= 0) {
1215  ids.push_back(linBin);
1216  }
1217  if (ids.empty()) {
1218  NLogWarning("NGnTree::Projection: No entries found in binning definition '%s' !!!", binningDef->GetName());
1219  binningDef->RefreshIdsFromContent();
1220  return nullptr;
1221  }
1222 
1223  binningDef->GetIds() = ids;
1224 
1225  // NUtils::SetAxisRanges(, std::vector<std::vector<int>> ranges)
1226  Process(processFunc, cfg);
1227 
1228  // Refresh binning definition ids from content after processing
1229  binningDef->RefreshIdsFromContent();
1230 
1231  // GetInput()->Close(false);
1233 
1234  // Close(false);
1235 }
1236 
1237 NGnNavigator * NGnTree::Reshape(std::string binningName, std::vector<std::vector<int>> levels, int level,
1238  std::map<int, std::vector<int>> ranges, std::map<int, std::vector<int>> rangesBase)
1239 {
1243 
1244  NGnNavigator navigator;
1245  navigator.SetGnTree(this);
1246 
1247  return navigator.Reshape(binningName, levels, level, ranges, rangesBase);
1248 }
1249 
1250 NGnNavigator * NGnTree::GetResourceStatisticsNavigator(std::string binningName, std::vector<std::vector<int>> levels,
1251  int level, std::map<int, std::vector<int>> ranges,
1252  std::map<int, std::vector<int>> rangesBase)
1253 {
1257 
1258  if (binningName.empty()) {
1259  binningName = fBinning->GetCurrentDefinitionName();
1260  }
1261 
1262  THnSparse * hns = (THnSparse *)fOutputs[binningName]->FindObject("resource_monitor");
1263  if (!hns) {
1264  NLogError("NGnTree::Draw: Resource monitor THnSparse not found in outputs !!!");
1265  return nullptr;
1266  }
1267  hns->Print("all");
1268  // return nullptr;
1269 
1270  auto ngnt = new NGnTree(hns, "stat", "/tmp/hnst_imported_for_drawing.root");
1271  if (ngnt->IsZombie()) {
1272  NLogError("NGnTree::GetResourceStatisticsNavigator: Failed to import resource monitor THnSparse !!!");
1273  return nullptr;
1274  }
1275  ngnt->Print();
1276  ngnt->Close();
1277 
1278  // return nullptr;
1279  auto ngnt2 = NGnTree::Open("/tmp/hnst_imported_for_drawing.root");
1280  auto nav = ngnt2->Reshape("default", levels, level, ranges, rangesBase);
1281  // // nav->Export("/tmp/hnst_imported_for_drawing.json", {}, "ws://localhost:8080/ws/root.websocket");
1282  // // nav->Draw();
1283  return nav;
1284 }
1285 
1286 bool NGnTree::InitParameters(const std::vector<std::string> & paramNames)
1287 {
1291 
1292  if (fParameters) {
1293  NLogTrace("NGnTree::InitParameters: Replacing existing parameters ...");
1294  delete fParameters;
1295  }
1296 
1297  if (paramNames.empty()) {
1298  NLogTrace("NGnTree::InitParameters: No parameter names provided, skipping ...");
1299  return false;
1300  }
1301 
1302  fParameters = new NParameters(paramNames, "results", "Results");
1303 
1304  return true;
1305 }
1306 
1307 NGnTree * NGnTree::Import(const std::string & findPath, const std::string & fileName,
1308  const std::vector<std::string> & headers, const std::string & outputFile, bool close)
1309 {
1313 
1314  // remove trailing slash from findPath if exists
1315  std::string findPathClean = findPath;
1316  if (!findPathClean.empty() && findPathClean.back() == '/') {
1317  findPathClean.pop_back();
1318  }
1319 
1320  std::vector<std::string> paths = NUtils::Find(findPathClean, fileName);
1321  NLogInfo("NGnTree::Import: Found %zu files to import ...", paths.size());
1322 
1323  TObjArray * ngntArray = NUtils::AxesFromDirectory(paths, findPathClean, fileName, headers);
1324  int nDirAxes = ngntArray->GetEntries();
1325 
1326  NGnTree * ngntFirst = NGnTree::Open(paths[0]);
1327  // Add all axes from ngntFirst to ngntArray
1328  for (const auto & axis : ngntFirst->GetBinning()->GetAxes()) {
1329  ngntArray->Add(axis->Clone());
1330  }
1331  ngntFirst->Close(false);
1332 
1333  std::map<std::string, std::vector<std::vector<int>>> b;
1334 
1335  for (int i = 0; i < ngntArray->GetEntries(); i++) {
1336  TAxis * axis = (TAxis *)ngntArray->At(i);
1337  b[axis->GetName()].push_back({1});
1338  }
1339 
1340  NGnTree * ngnt = new NGnTree(ngntArray, outputFile);
1341  ngnt->SetIsPureCopy(true);
1342 
1343  // return nullptr;
1344  ngnt->GetBinning()->AddBinningDefinition("default", b);
1345 
1346  json cfg;
1347  cfg["basedir"] = findPathClean;
1348  cfg["filename"] = fileName;
1349  cfg["nDirAxes"] = nDirAxes;
1350  cfg["headers"] = headers;
1351  // cfg["ndmspc"]["shared"]["currentFileName"] = "";
1352  Ndmspc::NGnProcessFuncPtr processFunc = [](Ndmspc::NBinningPoint * point, TList * /*output*/, TList * outputPoint,
1353  int /*threadId*/) {
1354  // point->Print();
1355 
1356  json cfg = point->GetCfg();
1357  std::string filename = cfg["basedir"].get<std::string>();
1358  filename += "/";
1359  for (auto & header : cfg["headers"]) {
1360  filename += point->GetBinLabel(header.get<std::string>());
1361  filename += "/";
1362  }
1363  // filename += "/";
1364  // filename += point->GetBinLabel("c");
1365  // filename += point->GetBinLabel("year");
1366  // filename += "/";
1367  filename += cfg["filename"].get<std::string>();
1368  NGnTree * ngnt = (NGnTree *)point->GetTempObject("file");
1369  if (!ngnt || filename.compare(ngnt->GetStorageTree()->GetFileName()) != 0) {
1370  NLogInfo("NGnTree::Import: Opening file '%s' ...", filename.c_str());
1371  if (ngnt) {
1372  NLogDebug("NGnTree::Import: Closing previously opened file '%s' ...",
1373  ngnt->GetStorageTree()->GetFileName().c_str());
1374  ngnt->Close(false);
1375  // delete ngnt;
1376  point->SetTempObject("file", nullptr);
1377  }
1378  ngnt = NGnTree::Open(filename.c_str());
1379  if (!ngnt || ngnt->IsZombie()) {
1380  NLogError("NGnTree::Import: Cannot open file '%s'", filename.c_str());
1381  return;
1382  }
1383  point->SetTempObject("file", ngnt);
1384  }
1385 
1386  int nDirAxes = cfg["nDirAxes"].get<int>();
1387  Int_t * coords = point->GetCoords();
1388  std::string coordsStr = NUtils::GetCoordsString(NUtils::ArrayToVector(coords, point->GetNDimensionsContent()));
1389  NLogInfo("NGnTree::Import: Processing point with coords %s ...", coordsStr.c_str());
1390 
1391  Long64_t entryNumber =
1392  ngnt->GetBinning()->GetContent()->GetBin(&coords[3 * nDirAxes], kFALSE); // skip first 3 dir axes
1393  NLogInfo("NGnTree::Import: Corresponding entry number in file '%s' is %lld", filename.c_str(), entryNumber);
1394 
1395  ngnt->GetEntry(entryNumber);
1396 
1397  // // add outputPoint content to outputPoint list
1398  // TList * inputOutputPoint = (TList *)ngnt->GetStorageTree()->GetBranch("_outputPoint")->GetObject();
1399  // for (int i = 0; i < inputOutputPoint->GetEntries(); i++) {
1400  // outputPoint->Add(inputOutputPoint->At(i));
1401  // }
1402 
1403  // set all branches from ngnt to branch addresses in current object
1404  for (const auto & kv : ngnt->GetStorageTree()->GetBranchesMap()) {
1405  // check if branch exists in current storage tree
1406  if (point->GetStorageTree()->GetBranch(kv.first) == nullptr) {
1407  NLogTrace("NGnTree::Import: Adding branch '%s' to storage tree ...", kv.first.c_str());
1408  point->GetStorageTree()->AddBranch(kv.first, nullptr, kv.second.GetObjectClassName());
1409  }
1410  NLogTrace("NGnTree::Import: Setting branch address for branch '%s' ...", kv.first.c_str());
1411  point->GetTreeStorage()->GetBranch(kv.first)->SetAddress(kv.second.GetObject());
1412  }
1413  outputPoint->Add(new TNamed("source_file", filename));
1414 
1415  // ngnt->Print();
1416 
1417  // NLogInfo("NGnTree::Import: nDirAxes=%d ...", cfg["nDirAxes"].get<int>());
1418 
1419  // json & tempCfg = point->GetTempCfg();
1420  // if (tempCfg["test"].is_null()) {
1421  // NLogInfo("Setting temp cfg test value to 42");
1422  // tempCfg["test"] = 42;
1423  // }
1424  // NLogInfo("Temp cfg test value: %d", tempCfg["test"].get<int>());
1425 
1426  // f->ls();
1427  };
1428  Ndmspc::NGnBeginFuncPtr beginFunc = [](Ndmspc::NBinningPoint * /*point*/, int /*threadId*/) {
1429  TH1::AddDirectory(kFALSE); // Prevent histograms from being associated with the current directory
1430  };
1431 
1432  Ndmspc::NGnEndFuncPtr endFunc = [](Ndmspc::NBinningPoint * point, int /*threadId*/) {
1433  NGnTree * ngnt = (NGnTree *)point->GetTempObject("file");
1434  if (ngnt) {
1435  NLogDebug("NGnTree::Import: Closing last file '%s' ...", ngnt->GetStorageTree()->GetFileName().c_str());
1436  // ngnt->Close(false);
1437  // delete ngnt;
1438  point->SetTempObject("file", nullptr);
1439  }
1440  };
1441 
1442  ngnt->Process(processFunc, cfg, "", beginFunc, endFunc);
1443  if (close) {
1444  ngnt->Close(true);
1445  delete ngnt;
1446  ngnt = NGnTree::Open(outputFile.c_str());
1447  }
1448  return ngnt;
1449 }
1450 
1451 } // 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
virtual void Print(Option_t *option="") const
Print binning definition information.
void RefreshIdsFromContent()
Refresh bin IDs from content histogram.
std::vector< Long64_t > GetIds() const
Get list of bin IDs.
Definition: NBinningDef.h:93
Represents a single point in multi-dimensional binning.
Definition: NBinningPoint.h:23
Double_t GetBinMax(std::string axis) const
Get the maximum value for a specific axis.
std::string GetString(const std::string &prefix="", bool all=false) const
Returns a string representation of the binning point.
void SetTreeStorage(NStorageTree *s)
Set storage tree object pointer.
std::string GetBinLabel(std::string axis) const
Get the label for a specific axis.
Long64_t GetEntryNumber() const
Get entry number for the point.
NParameters * GetParameters() const
Get the parameters associated with this binning point.
Int_t * GetStorageCoords() const
Get pointer to storage coordinates array.
Definition: NBinningPoint.h:69
Double_t GetBinCenter(std::string axis) const
Returns the center value of the bin along the specified axis.
NStorageTree * GetTreeStorage() const
Get pointer to storage tree object.
void SetTempObject(const std::string &name, TObject *obj)
Set a temporary object with the given name.
NStorageTree * GetStorageTree() const
Returns a pointer to the associated storage tree.
Definition: NBinningPoint.h:92
virtual void Print(Option_t *option="") const
Print binning point information.
TObject * GetTempObject(const std::string &name) const
Retrieve a temporary object by name.
NGnTree * GetInput() const
Get pointer to input NGnTree object.
Int_t GetBin(std::string axis) const
Returns the bin index for the specified axis.
Int_t * GetCoords() const
Get pointer to content coordinates array.
Definition: NBinningPoint.h:57
json & GetCfg()
Get reference to configuration JSON object.
Int_t GetNDimensionsContent() const
Get number of dimensions in content histogram.
Definition: NBinningPoint.h:51
Double_t GetBinMin(std::string axis) const
Get the minimum value for a specific axis.
NBinning object for managing multi-dimensional binning and axis definitions.
Definition: NBinning.h:45
NBinningDef * GetDefinition(const std::string &name="")
Get binning definition by name.
Definition: NBinning.cxx:1021
std::vector< std::string > GetDefinitionNames() const
Get all definition names.
Definition: NBinning.h:270
std::string GetCurrentDefinitionName() const
Get current definition name.
Definition: NBinning.h:276
NBinningPoint * GetPoint()
Get the current binning point.
Definition: NBinning.cxx:1104
virtual void Print(Option_t *option="") const
Print binning information.
Definition: NBinning.cxx:245
std::vector< TAxis * > GetAxes() const
Get vector of axis pointers.
Definition: NBinning.h:223
bool SetCfg(const json &cfg)
Set configuration from JSON.
Definition: NBinning.cxx:1150
void AddBinningDefinition(std::string name, std::map< std::string, std::vector< std::vector< int >>> binning, bool forceDefault=false)
Add a binning definition.
Definition: NBinning.cxx:1050
void Reset()
Reset the binning object to initial state.
Definition: NBinning.cxx:78
Executes a function over all points in an N-dimensional space, optionally in parallel.
void ExecuteParallel(const std::function< void(const std::vector< int > &coords, TObject &thread_object)> &func, std::vector< TObject > &thread_objects)
Execute a function in parallel over all coordinates, using thread-local objects.
Navigator object for managing hierarchical data structures and projections.
Definition: NGnNavigator.h:22
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.
void SetGnTree(NGnTree *tree)
Set NGnTree object pointer.
Definition: NGnNavigator.h:184
Thread-local data object for NDMSPC processing.
Definition: NGnThreadData.h:19
bool Init(size_t id, NGnProcessFuncPtr func, NGnBeginFuncPtr beginFunc, NGnEndFuncPtr endFunc, NGnTree *ngnt, NBinning *binningIn, NGnTree *input=nullptr, const std::string &filename="", const std::string &treename="ngnt")
Initialize thread data for processing.
NGnTree * GetHnSparseBase() const
Get pointer to base NGnTree object.
Definition: NGnThreadData.h:65
virtual Long64_t Merge(TCollection *list)
Merge thread data from a collection (virtual).
void FlushDeferredDeletes()
Delete deferred ROOT objects, skipping TCanvas/TPad (leaked safely).
void SetCfg(const json &cfg)
Set configuration JSON object.
Definition: NGnThreadData.h:77
NDMSPC tree object for managing multi-dimensional data storage and processing.
Definition: NGnTree.h:76
NBinning * GetBinning() const
Get pointer to binning object.
Definition: NGnTree.h:162
std::map< std::string, TList * > GetOutputs() const
Get outputs map.
Definition: NGnTree.h:180
virtual void Draw(Option_t *option="") override
Draws the tree object.
Definition: NGnTree.cxx:487
void SetIsPureCopy(bool val)
Sets the pure copy status of the tree.
Definition: NGnTree.h:231
bool Close(bool write=false)
Close the tree, optionally writing data.
Definition: NGnTree.cxx:916
virtual ~NGnTree()
Destructor.
Definition: NGnTree.cxx:451
Int_t GetEntry(Long64_t entry, bool checkBinningDef=true)
Get entry by index.
Definition: NGnTree.cxx:930
virtual void Print(Option_t *option="") const override
Print tree information.
Definition: NGnTree.cxx:463
void SetInput(NGnTree *input)
Set input NGnTree pointer.
Definition: NGnTree.h:205
NGnTree()
Default constructor.
Definition: NGnTree.cxx:156
static NGnTree * Import(const std::string &findPath, const std::string &fileName, const std::vector< std::string > &headers, const std::string &outFileName="/tmp/ngnt_imported.root", bool close=true)
Imports an NGnTree from a specified file.
Definition: NGnTree.cxx:1307
NGnNavigator * Reshape(std::string binningName, std::vector< std::vector< int >> levels, int level=0, std::map< int, std::vector< int >> ranges={}, std::map< int, std::vector< int >> rangesBase={})
Reshape navigator using binning name and levels.
Definition: NGnTree.cxx:1237
NBinning * fBinning
Binning object.
Definition: NGnTree.h:388
NWsClient * fWsClient
! WebSocket client for communication
Definition: NGnTree.h:394
TList * GetOutput(std::string name="")
Get output list by name.
Definition: NGnTree.cxx:796
NParameters * GetParameters() const
Returns the parameters associated with this tree.
Definition: NGnTree.h:319
NGnTree * fInput
Input NGnTree for processing.
Definition: NGnTree.h:391
NStorageTree * GetStorageTree() const
Get pointer to storage tree object.
Definition: NGnTree.h:174
NGnTree * GetInput() const
Get pointer to input NGnTree.
Definition: NGnTree.h:199
void SetOutputs(std::map< std::string, TList * > outputs)
Set outputs map.
Definition: NGnTree.h:193
bool InitParameters(const std::vector< std::string > &paramNames)
Initializes the parameters for the tree using the provided parameter names.
Definition: NGnTree.cxx:1286
std::map< std::string, TList * > fOutputs
Outputs.
Definition: NGnTree.h:390
NGnNavigator * fNavigator
! Navigator object
Definition: NGnTree.h:392
NParameters * fParameters
Parameters object.
Definition: NGnTree.h:393
TList * Projection(const json &cfg, std::string binningName="")
Project tree data using configuration and binning name.
Definition: NGnTree.cxx:1135
NGnNavigator * GetResourceStatisticsNavigator(std::string binningName, std::vector< std::vector< int >> levels, int level=0, std::map< int, std::vector< int >> ranges={}, std::map< int, std::vector< int >> rangesBase={})
Returns a navigator for resource statistics based on binning and levels.
Definition: NGnTree.cxx:1250
NStorageTree * fTreeStorage
Tree storage.
Definition: NGnTree.h:389
static std::string BuildObjectPath(const json &cfg, const json &objCfg, const NBinningPoint *point)
Helper: build object path string from configuration and a binning point.
Definition: NGnTree.cxx:43
void SetNavigator(NGnNavigator *navigator)
Sets the navigator for this tree.
Definition: NGnTree.cxx:902
static NGnTree * Open(const std::string &filename, const std::string &branches="", const std::string &treename="ngnt")
Open NGnTree from file.
Definition: NGnTree.cxx:812
bool Process(NGnProcessFuncPtr func, const json &cfg=json::object(), std::string binningName="", NGnBeginFuncPtr beginFunc=nullptr, NGnEndFuncPtr endFunc=nullptr)
Process tree data using a function pointer and configuration.
Definition: NGnTree.cxx:496
void Play(int timeout=0, std::string binning="", std::vector< int > outputPointIds={0}, std::vector< std::vector< int >> ranges={}, Option_t *option="", std::string ws="")
Play tree data with optional binning and output point IDs.
Definition: NGnTree.cxx:954
static bool GetConsoleOutput()
Get console output flag.
Definition: NLogger.h:533
NParameters object.
Definition: NParameters.h:13
bool SetParameter(int bin, Double_t value, Double_t error=0.)
Set the value and error of a parameter by bin index.
Definition: NParameters.cxx:55
NDMSPC storage tree object for managing ROOT TTree-based data storage.
Definition: NStorageTree.h:22
void SetBranchAddresses()
Set addresses for all branches.
void SetEnabledBranches(std::vector< std::string > branches, int status=1)
Set enabled/disabled status for branches.
bool SetFileTree(TFile *file, TTree *tree)
Tree handling.
std::string GetFileName() const
Get file name.
Definition: NStorageTree.h:194
bool AddBranch(const std::string &name, void *address, const std::string &className)
Add a branch to the tree.
Long64_t GetEntry(Long64_t entry, NBinningPoint *point=nullptr, bool checkBinningDef=false)
Get entry by index and fill NBinningPoint.
std::string GetPrefix() const
Get prefix path.
Definition: NStorageTree.h:200
TObject * GetBranchObject(const std::string &name)
Get pointer to branch object by name.
NTreeBranch * GetBranch(const std::string &name)
Get pointer to NTreeBranch by name.
bool InitTree(const std::string &filename="", const std::string &treename="ngnt")
Initialize tree from file and tree name.
std::string GetPostfix() const
Get postfix path.
Definition: NStorageTree.h:206
bool Close(bool write=false, std::map< std::string, TList * > outputs={})
Close the storage tree, optionally writing outputs.
TTree * GetTree() const
Get pointer to TTree object.
Definition: NStorageTree.h:188
virtual void Print(Option_t *option="") const
Print storage tree information.
void SetBinning(NBinning *binning)
Set binning object pointer.
Definition: NStorageTree.h:182
std::map< std::string, NTreeBranch > GetBranchesMap() const
Get map of branch names to NTreeBranch objects.
Definition: NStorageTree.h:129
void SetAddress(void *address, bool deleteExisting=false)
Set address for branch data.
Definition: NTreeBranch.cxx:59
TObject * GetObject() const
Get object pointer.
Definition: NTreeBranch.h:81
static bool SetAxisRanges(THnSparse *sparse, std::vector< std::vector< int >> ranges={}, bool withOverflow=false, bool modifyTitle=false, bool reset=true)
Set axis ranges for THnSparse using vector of ranges.
Definition: NUtils.cxx:1075
static TH1 * ProjectTHnSparse(THnSparse *hns, const std::vector< int > &axes, Option_t *option="")
Project a THnSparse histogram onto specified axes.
Definition: NUtils.cxx:1022
static std::vector< std::string > Tokenize(std::string_view input, const char delim)
Tokenize a string by delimiter.
Definition: NUtils.cxx:928
static std::string FormatTime(long long seconds)
Format time in seconds to human-readable string.
Definition: NUtils.cxx:1515
static bool EnableMT(Int_t numthreads=-1)
Enable multi-threading with specified number of threads.
Definition: NUtils.cxx:43
static std::string Join(const std::vector< std::string > &values, const char delim=',')
Join vector of strings into a single string with delimiter.
Definition: NUtils.cxx:966
static void ProgressBar(int current, int total, std::string prefix="", std::string suffix="", int barWidth=50)
Display progress bar.
Definition: NUtils.cxx:1528
static std::string GetCoordsString(const std::vector< int > &coords, int index=-1, int width=0)
Get string representation of coordinates.
Definition: NUtils.cxx:1443
static std::vector< int > ArrayToVector(Int_t *v1, int size)
Convert array to vector.
Definition: NUtils.cxx:1406
static TObjArray * AxesFromDirectory(const std::vector< std::string > paths, const std::string &findPath, const std::string &fileName, const std::vector< std::string > &axesNames)
Creates an array of axes objects from files in specified directories.
Definition: NUtils.cxx:1251
static std::vector< std::string > Find(std::string path, std::string filename="")
Find files in a path matching filename.
Definition: NUtils.cxx:818
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