ndmspc  v1.1.1-1
NUtils.cxx
1 #include <cstddef>
2 #include <iostream>
3 #include <TSystem.h>
4 #include <TROOT.h>
5 #include <TPad.h>
6 #include <vector>
7 #include <string>
8 #include <sstream>
9 #include <thread>
10 #include <TF1.h>
11 #include <TThread.h>
12 #include <TAxis.h>
13 #include <THnSparse.h>
14 #include <TString.h>
15 #if defined(__linux__)
16 #include <fstream>
17 #elif defined(__APPLE__)
18 #include <ifaddrs.h>
19 #include <net/if.h>
20 #include <sys/types.h>
21 #include <sys/socket.h>
22 #endif
23 #include "NLogger.h"
24 #include "NHttpRequest.h"
25 #include "ndmspc.h"
26 #ifdef WITH_PARQUET
27 #include <arrow/api.h>
28 #include <arrow/io/api.h>
29 #include <parquet/arrow/reader.h>
30 #include <parquet/exception.h>
31 #endif
32 #include "NUtils.h"
33 
34 
35 using std::ifstream;
36 
38 ClassImp(Ndmspc::NUtils);
40 
41 namespace Ndmspc {
42 
43 bool NUtils::EnableMT(Int_t numthreads)
44 {
49  bool previouslyEnabled = ROOT::IsImplicitMTEnabled();
50 
51  if (ROOT::IsImplicitMTEnabled()) {
52  ROOT::DisableImplicitMT();
53  }
54 
55  // TH1D * h = new TH1D("h", "Test Histogram", 20, -10, 10);
56  // h->FillRandom("gaus", 1000);
57  // TF1 * f1 = new TF1("f1", "gaus", 0, 10);
58  // h->Fit(f1, "N");
59  // delete h;
60 
61  if (numthreads == -1) {
62  // take numeber of cores from env variable
63  const char * nThreadsEnv = gSystem->Getenv("ROOT_MAX_THREADS");
64  if (nThreadsEnv) {
65  try {
66  numthreads = std::stoul(nThreadsEnv);
67  }
68  catch (const std::exception & e) {
69  NLogError("Error parsing ROOT_MAX_THREADS: %s !!! Setting it to '1' ...", e.what());
70  numthreads = 1;
71  }
72  }
73  else {
74  numthreads = 1; // use default
75  }
76  }
77 
78  // Initialise ROOT's thread-safety infrastructure (gROOTMutex, etc.)
79  ROOT::EnableThreadSafety();
80 
81  // Enable IMT with default number of threads (usually number of CPU cores)
82  if (numthreads > 0) {
83  ROOT::EnableImplicitMT(numthreads);
84  }
85 
86  // Check if IMT is enabled
87  if (ROOT::IsImplicitMTEnabled()) {
88  NLogInfo("ROOT::ImplicitMT is enabled with number of threads: %d", ROOT::GetThreadPoolSize());
89  }
90 
91  return previouslyEnabled;
92 }
93 
94 bool NUtils::IsFileSupported(std::string filename)
95 {
99 
100  if (filename.find("http://") == 0 || filename.find("https://") == 0 || filename.find("root://") == 0 ||
101  filename.find("file://") == 0 || filename.find("alien://") == 0) {
102  return true;
103  }
104  TString fn(filename.c_str());
105  if (fn.BeginsWith("/") || !fn.Contains("://")) {
106  return true;
107  }
108  NLogError("NUtils::IsFileSupported: File '%s' not found", filename.c_str());
109  return false;
110 }
111 
112 bool NUtils::AccessPathName(std::string path)
113 {
117  TString pathStr(gSystem->ExpandPathName(path.c_str()));
118 
119  if (pathStr.BeginsWith("http://") || pathStr.BeginsWith("https://")) {
120  // TODO: check if URL exists via HTTP request
121  NHttpRequest request;
122  // request.SetUrl(pathStr.Data());
123  // return request.Exists();
124  int http_code = request.head(pathStr.Data());
125  if (http_code == 200) {
126  return true;
127  }
128 
129  return false;
130  }
131  else if (pathStr.BeginsWith("file://") || pathStr.BeginsWith("/") || !pathStr.Contains("://")) {
132 
133  return gSystem->AccessPathName(pathStr.Data()) == false;
134  }
135  else if (pathStr.BeginsWith("root://") || pathStr.BeginsWith("alien://")) {
136  // For root and alien protocols, we can try to open the file
137  if (!pathStr.EndsWith(".root")) {
138  // For raw files, we cannot use TFile
139  pathStr += "?filetype=raw";
140  }
141  NLogDebug("NUtils::AccessPathName: Trying to open file '%s' ...", pathStr.Data());
142  TFile * f = TFile::Open(pathStr.Data());
143  if (f && !f->IsZombie()) {
144  f->Close();
145  return true;
146  }
147  return false;
148  }
149  return false;
150 }
151 
152 int NUtils::Cp(std::string source, std::string destination)
153 {
157  int rc = 0;
158 
159  if (source.empty()) {
160  NLogError("NUtils::Cp: Source file is empty");
161  return -1;
162  }
163  if (destination.empty()) {
164  NLogError("NUtils::Cp: Destination file is empty");
165  return -1;
166  }
167 
168  if (IsFileSupported(source) == false) {
169  NLogError("NUtils::Cp: Source file '%s' is not supported", source.c_str());
170  return -1;
171  }
172  if (IsFileSupported(destination) == false) {
173  NLogError("NUtils::Cp: Destination file '%s' is not supported", destination.c_str());
174  return -1;
175  }
176 
177  NLogInfo("Copying file from '%s' to '%s' ...", source.c_str(), destination.c_str());
178  // rc = TFile::Cp(source, destination);
179  return rc;
180 }
181 
182 TAxis * NUtils::CreateAxisFromLabels(const std::string & name, const std::string & title,
183  const std::vector<std::string> & labels)
184 {
188  int nBins = labels.size();
189  TAxis * a = new TAxis(nBins, 0, nBins);
190  a->SetName(name.c_str());
191  a->SetTitle(title.c_str());
192  for (int i = 0; i < nBins; i++) {
193  NLogTrace("NUtils::CreateAxisFromLabels: Adding label: %s", labels[i].c_str());
194  a->SetBinLabel(i + 1, labels[i].c_str());
195  }
196  return a;
197 }
198 
199 TAxis * NUtils::CreateAxisFromLabelsSet(const std::string & name, const std::string & title,
200  const std::set<std::string> & labels)
201 {
205  int nBins = labels.size();
206  TAxis * a = new TAxis(nBins, 0, nBins);
207  a->SetName(name.c_str());
208  a->SetTitle(title.c_str());
209  int i = 1;
210  for (const auto & label : labels) {
211  NLogTrace("NUtils::CreateAxisFromLabels: Adding label: %s", label.c_str());
212  a->SetBinLabel(i, label.c_str());
213  i++;
214  }
215  return a;
216 }
217 
218 THnSparse * NUtils::Convert(TH1 * h1, std::vector<std::string> names, std::vector<std::string> titles)
219 {
223 
224  if (h1 == nullptr) {
225  NLogError("TH1 h1 is null");
226  return nullptr;
227  }
228 
229  NLogInfo("Converting TH1 '%s' to THnSparse ...", h1->GetName());
230 
231  int nDims = 1;
232  // Int_t bins[nDims];
233  // Double_t xmin[nDims];
234  // Double_t xmax[nDims];
235  auto bins = std::make_unique<Int_t[]>(nDims);
236  auto xmin = std::make_unique<Double_t[]>(nDims);
237  auto xmax = std::make_unique<Double_t[]>(nDims);
238 
239  TAxis * aIn = h1->GetXaxis();
240  bins[0] = aIn->GetNbins();
241  xmin[0] = aIn->GetXmin();
242  xmax[0] = aIn->GetXmax();
243 
244  THnSparse * hns = new THnSparseD(h1->GetName(), h1->GetTitle(), nDims, bins.get(), xmin.get(), xmax.get());
245 
246  // loop over all axes
247  for (int i = 0; i < nDims; i++) {
248  TAxis * a = hns->GetAxis(i);
249  TAxis * aIn = h1->GetXaxis();
250  a->SetName(aIn->GetName());
251  a->SetTitle(aIn->GetTitle());
252  if (aIn->GetXbins()->GetSize() > 0) {
253  // Double_t arr[aIn->GetNbins() + 1];
254  auto arr = std::make_unique<Double_t[]>(aIn->GetNbins() + 1);
255  arr[0] = aIn->GetBinLowEdge(1);
256  for (int iBin = 1; iBin <= aIn->GetNbins(); iBin++) {
257  arr[iBin] = aIn->GetBinUpEdge(iBin);
258  }
259  a->Set(a->GetNbins(), arr.get());
260  }
261  }
262 
263  for (int i = 0; i < nDims; i++) {
264  if (!names[i].empty()) hns->GetAxis(i)->SetName(names[i].c_str());
265  if (!titles[i].empty()) hns->GetAxis(i)->SetTitle(titles[i].c_str());
266  }
267 
268  // fill the sparse with the content of the TH3
269  for (Int_t i = 0; i <= h1->GetNbinsX() + 1; i++) {
270  double content = h1->GetBinContent(i);
271  Int_t p[1] = {i}; // bin indices in TH3
272  hns->SetBinContent(p, content);
273  }
274 
275  hns->SetEntries(h1->GetEntries());
276  if (h1->GetSumw2N() > 0) {
277  hns->Sumw2();
278  }
279 
280  return hns;
281 }
282 
283 THnSparse * NUtils::Convert(TH2 * h2, std::vector<std::string> names, std::vector<std::string> titles)
284 {
288  if (h2 == nullptr) {
289  NLogError("TH2 h2 is null");
290  return nullptr;
291  }
292  NLogInfo("Converting TH2 '%s' to THnSparse ...", h2->GetName());
293  int nDims = 2;
294  auto bins = std::make_unique<Int_t[]>(nDims);
295  auto xmin = std::make_unique<Double_t[]>(nDims);
296  auto xmax = std::make_unique<Double_t[]>(nDims);
297 
298  for (int i = 0; i < nDims; i++) {
299  TAxis * aIn = nullptr;
300  if (i == 0)
301  aIn = h2->GetXaxis();
302  else if (i == 1)
303  aIn = h2->GetYaxis();
304  else {
305  NLogError("Invalid axis index %d", i);
306  return nullptr;
307  }
308  bins[i] = aIn->GetNbins();
309  xmin[i] = aIn->GetXmin();
310  xmax[i] = aIn->GetXmax();
311  }
312 
313  THnSparse * hns = new THnSparseD(h2->GetName(), h2->GetTitle(), nDims, bins.get(), xmin.get(), xmax.get());
314 
315  for (Int_t i = 0; i < nDims; i++) {
316  TAxis * a = hns->GetAxis(i);
317  TAxis * aIn = nullptr;
318  if (i == 0)
319  aIn = h2->GetXaxis();
320  else if (i == 1)
321  aIn = h2->GetYaxis();
322  else {
323  NLogError("Invalid axis index %d", i);
324  delete hns;
325  return nullptr;
326  }
327  a->SetName(aIn->GetName());
328  a->SetTitle(aIn->GetTitle());
329  if (aIn->GetXbins()->GetSize() > 0) {
330  auto arr = std::make_unique<Double_t[]>(aIn->GetNbins() + 1);
331  arr[0] = aIn->GetBinLowEdge(1);
332  for (int iBin = 1; iBin <= aIn->GetNbins(); iBin++) {
333  arr[iBin] = aIn->GetBinUpEdge(iBin);
334  }
335  a->Set(a->GetNbins(), arr.get());
336  }
337  }
338 
339  for (Int_t i = 0; i < nDims; i++) {
340  if (!names[i].empty()) hns->GetAxis(i)->SetName(names[i].c_str());
341  if (!titles[i].empty()) hns->GetAxis(i)->SetTitle(titles[i].c_str());
342  }
343 
344  // fill the sparse with the content of the TH2
345  for (Int_t i = 0; i <= h2->GetNbinsX() + 1; i++) {
346  for (Int_t j = 0; j <= h2->GetNbinsY() + 1; j++) {
347  double content = h2->GetBinContent(i, j);
348  Int_t p[2] = {i, j}; // bin indices in TH3
349  hns->SetBinContent(p, content);
350  }
351  }
352 
353  hns->SetEntries(h2->GetEntries());
354  if (h2->GetSumw2N() > 0) {
355  hns->Sumw2();
356  }
357 
358  return hns;
359 }
360 
361 THnSparse * NUtils::Convert(TH3 * h3, std::vector<std::string> names, std::vector<std::string> titles)
362 {
366 
367  if (h3 == nullptr) {
368  NLogError("TH3 h3 is null");
369  return nullptr;
370  }
371 
372  NLogInfo("Converting TH3 '%s' to THnSparse ...", h3->GetName());
373 
374  int nDims = 3;
375  auto bins = std::make_unique<Int_t[]>(nDims);
376  auto xmin = std::make_unique<Double_t[]>(nDims);
377  auto xmax = std::make_unique<Double_t[]>(nDims);
378 
379  for (int i = 0; i < nDims; i++) {
380  TAxis * aIn = nullptr;
381  if (i == 0)
382  aIn = h3->GetXaxis();
383  else if (i == 1)
384  aIn = h3->GetYaxis();
385  else if (i == 2)
386  aIn = h3->GetZaxis();
387  else {
388  NLogError("Invalid axis index %d", i);
389  return nullptr;
390  }
391  bins[i] = aIn->GetNbins();
392  xmin[i] = aIn->GetXmin();
393  xmax[i] = aIn->GetXmax();
394  }
395 
396  THnSparse * hns = new THnSparseD(h3->GetName(), h3->GetTitle(), nDims, bins.get(), xmin.get(), xmax.get());
397 
398  // loop over all axes
399  for (int i = 0; i < nDims; i++) {
400  TAxis * a = hns->GetAxis(i);
401  TAxis * aIn = nullptr;
402  if (i == 0)
403  aIn = h3->GetXaxis();
404  else if (i == 1)
405  aIn = h3->GetYaxis();
406  else if (i == 2)
407  aIn = h3->GetZaxis();
408  else {
409  NLogError("Invalid axis index %d", i);
410  delete hns;
411  return nullptr;
412  }
413  a->SetName(aIn->GetName());
414  a->SetTitle(aIn->GetTitle());
415  if (aIn->GetXbins()->GetSize() > 0) {
416  auto arr = std::make_unique<Double_t[]>(aIn->GetNbins() + 1);
417  arr[0] = aIn->GetBinLowEdge(1);
418  for (int iBin = 1; iBin <= aIn->GetNbins(); iBin++) {
419  arr[iBin] = aIn->GetBinUpEdge(iBin);
420  }
421  a->Set(a->GetNbins(), arr.get());
422  }
423  }
424 
425  for (Int_t i = 0; i < nDims; i++) {
426  if (!names[i].empty()) hns->GetAxis(i)->SetName(names[i].c_str());
427  if (!titles[i].empty()) hns->GetAxis(i)->SetTitle(titles[i].c_str());
428  }
429 
430  // fill the sparse with the content of the TH3
431  for (Int_t i = 0; i <= h3->GetNbinsX() + 1; i++) {
432  for (Int_t j = 0; j <= h3->GetNbinsY() + 1; j++) {
433  for (Int_t k = 0; k <= h3->GetNbinsZ() + 1; k++) {
434  double content = h3->GetBinContent(i, j, k);
435  Int_t p[3] = {i, j, k}; // bin indices in TH3
436  hns->SetBinContent(p, content);
437  }
438  }
439  }
440 
441  hns->SetEntries(h3->GetEntries());
442  if (h3->GetSumw2N() > 0) {
443  hns->Sumw2();
444  }
445 
446  return hns;
447 }
448 
449 THnSparse * NUtils::ReshapeSparseAxes(THnSparse * hns, std::vector<int> order, std::vector<TAxis *> newAxes,
450  std::vector<int> newPoint, Option_t * option)
451 {
455 
456  TString opt(option);
457 
458  if (hns == nullptr) {
459  NLogError("NUtils::ReshapeSparseAxes: THnSparse hns is null");
460  return nullptr;
461  }
462 
463  if (order.empty()) {
464  NLogTrace("NUtils::ReshapeSparseAxes: Order vector is empty");
465  for (long unsigned int i = 0; i < hns->GetNdimensions() + newAxes.size(); i++) {
466  NLogTrace("NUtils::ReshapeSparseAxes: Adding axis %d to order", i);
467  order.push_back(i);
468  }
469  }
470 
471  if (order.size() != hns->GetNdimensions() + newAxes.size()) {
472  NLogError("NUtils::ReshapeSparseAxes: Invalid size %d [order] != %d [hns->GetNdimensions()+newAxes]", order.size(),
473  hns->GetNdimensions() + newAxes.size());
474  return nullptr;
475  }
476 
477  if (newPoint.empty()) {
478  }
479  else {
480  if (newAxes.size() != newPoint.size()) {
481  NLogError("NUtils::ReshapeSparseAxes: Invalid size %d [newAxes] != %d [newPoint]", newAxes.size(),
482  newPoint.size());
483  return nullptr;
484  }
485  }
486  // loop over order and check if order contains values from 0 to hns->GetNdimensions() + newAxes.size()
487  for (size_t i = 0; i < order.size(); i++) {
488  if (order[i] < 0 || order[i] >= hns->GetNdimensions() + (int)newAxes.size()) {
489  NLogError("NUtils::ReshapeSparseAxes: Invalid order[%d]=%d. Value is negative or higher then "
490  "'hns->GetNdimensions() + newAxes.size()' !!!",
491  i, order[i]);
492  return nullptr;
493  }
494  }
495 
496  // check if order contains unique values
497  for (size_t i = 0; i < order.size(); i++) {
498  for (size_t j = i + 1; j < order.size(); j++) {
499  if (order[i] == order[j]) {
500  NLogError("NUtils::ReshapeSparseAxes: Invalid order[%d]=%d and order[%d]=%d. Value is not unique !!!", i,
501  order[i], j, order[j]);
502  return nullptr;
503  }
504  }
505  }
506 
507  // print info about original THnSparse
508  // NLogDebug("NUtils::ReshapeSparseAxes: Original THnSparse object:");
509  // hns->Print();
510 
511  NLogTrace("NUtils::ReshapeSparseAxes: Reshaping sparse axes ...");
512 
513  int nDims = hns->GetNdimensions() + newAxes.size();
514  auto bins = std::make_unique<Int_t[]>(nDims);
515  auto xmin = std::make_unique<Double_t[]>(nDims);
516  auto xmax = std::make_unique<Double_t[]>(nDims);
518  int newAxesIndex = 0;
519  for (int i = 0; i < nDims; i++) {
520  TAxis * a = nullptr;
521  int id = order[i];
522  if (id < hns->GetNdimensions()) {
523  a = hns->GetAxis(id);
524  NLogTrace("NUtils::ReshapeSparseAxes: [ORIG] Axis [%d]->[%d]: %s %s %d %.2f %.2f", id, i, a->GetName(),
525  a->GetTitle(), a->GetNbins(), a->GetXmin(), a->GetXmax());
526  }
527  else {
528  newAxesIndex = id - hns->GetNdimensions();
529  a = newAxes[newAxesIndex];
530  NLogTrace("NUtils::ReshapeSparseAxes: [NEW ] Axis [%d]->[%d]: %s %s %d %.2f %.2f", id, i, a->GetName(),
531  a->GetTitle(), a->GetNbins(), a->GetXmin(), a->GetXmax());
532  }
533  bins[i] = a->GetNbins();
534  xmin[i] = a->GetXmin();
535  xmax[i] = a->GetXmax();
536  }
537 
538  THnSparse * hnsNew = new THnSparseD(hns->GetName(), hns->GetTitle(), nDims, bins.get(), xmin.get(), xmax.get());
539 
540  // loop over all axes
541  for (int i = 0; i < hnsNew->GetNdimensions(); i++) {
542  TAxis * aIn = nullptr;
543  if (order[i] < hns->GetNdimensions()) {
544  aIn = hns->GetAxis(order[i]);
545  }
546  else {
547  newAxesIndex = order[i] - hns->GetNdimensions();
548  aIn = newAxes[newAxesIndex];
549  }
550 
551  TAxis * a = hnsNew->GetAxis(i);
552  a->SetName(aIn->GetName());
553  a->SetTitle(aIn->GetTitle());
554  if (aIn->GetXbins()->GetSize() > 0) {
555  auto arr = std::make_unique<Double_t[]>(aIn->GetNbins() + 1);
556  arr[0] = aIn->GetBinLowEdge(1);
557  for (int iBin = 1; iBin <= aIn->GetNbins(); iBin++) {
558  arr[iBin] = aIn->GetBinUpEdge(iBin);
559  }
560  a->Set(a->GetNbins(), arr.get());
561  }
562 
563  // copy bin labels
564  if (aIn->IsAlphanumeric()) {
565  for (int j = 1; j <= aIn->GetNbins(); j++) {
566  const char * label = aIn->GetBinLabel(j);
567  a->SetBinLabel(j, label);
568  }
569  }
570  }
571 
572  if (newPoint.empty()) {
573  NLogTrace("NUtils::ReshapeSparseAxes: New point is empty, filling is skipped and doing reset ...");
574  // hnsNew->Reset();
575  // hnsNew->SetEntries(0);
576  return hnsNew;
577  }
578 
579  if (hns->GetNbins() > 0) {
580  // loop over all bins
581  NLogTrace("NUtils::ReshapeSparseAxes: Filling all bins ...");
582  for (Long64_t i = 0; i < hns->GetNbins(); i++) {
583  auto p = std::make_unique<Int_t[]>(nDims);
584  auto pNew = std::make_unique<Int_t[]>(nDims);
585  hns->GetBinContent(i, p.get());
586  Double_t v = hns->GetBinContent(i);
587  // remap p to pNew
588  for (int j = 0; j < nDims; j++) {
589  int id = order[j];
590  if (id < hns->GetNdimensions()) {
591  pNew[j] = p[id];
592  }
593  else {
594  newAxesIndex = id - hns->GetNdimensions();
595  pNew[j] = newPoint[newAxesIndex];
596  }
597  }
598  hnsNew->SetBinContent(pNew.get(), v);
599  }
600  hnsNew->SetEntries(hns->GetEntries());
601  }
602  // Calsculate sumw2
603  if (opt.Contains("E")) {
604  NLogTrace("ReshapeSparseAxes: Calculating sumw2 ...");
605  hnsNew->Sumw2();
606  }
607  NLogTrace("ReshapeSparseAxes: Reshaped sparse axes:");
608  // print all axes
609  for (int i = 0; i < nDims; i++) {
610  TAxis * a = hnsNew->GetAxis(i);
611  NLogTrace("ReshapeSparseAxes: Axis %d: %s %s %d %.2f %.2f", i, a->GetName(), a->GetTitle(), a->GetNbins(),
612  a->GetXmin(), a->GetXmax());
613  }
614  // hnsNew->Print("all");
615  return hnsNew;
616 }
617 
618 void NUtils::GetTrueHistogramMinMax(const TH1 * h, double & min_val, double & max_val, bool include_overflow_underflow)
619 {
623  if (!h) {
624  max_val = 0.0;
625  min_val = 0.0;
626  return;
627  }
628 
629  max_val = -std::numeric_limits<double>::max(); // Initialize with smallest possible double
630  min_val = std::numeric_limits<double>::max(); // Initialize with largest possible double
631 
632  int first_bin_x = include_overflow_underflow ? 0 : 1;
633  int last_bin_x = include_overflow_underflow ? h->GetNbinsX() + 1 : h->GetNbinsX();
634 
635  int first_bin_y = include_overflow_underflow ? 0 : 1;
636  int last_bin_y = include_overflow_underflow ? h->GetNbinsY() + 1 : h->GetNbinsY();
637 
638  int first_bin_z = include_overflow_underflow ? 0 : 1;
639  int last_bin_z = include_overflow_underflow ? h->GetNbinsZ() + 1 : h->GetNbinsZ();
640 
641  // Determine the dimensionality of the histogram
642  if (h->GetDimension() == 1) { // TH1
643  for (int i = first_bin_x; i <= last_bin_x; ++i) {
644  double content = h->GetBinContent(i);
645  if (content > max_val) max_val = content;
646  if (content < min_val) min_val = content;
647  }
648  }
649  else if (h->GetDimension() == 2) { // TH2
650  for (int i = first_bin_x; i <= last_bin_x; ++i) {
651  for (int j = first_bin_y; j <= last_bin_y; ++j) {
652  double content = h->GetBinContent(i, j);
653  if (content > max_val) max_val = content;
654  if (content < min_val) min_val = content;
655  }
656  }
657  }
658  else if (h->GetDimension() == 3) { // TH3
659  for (int i = first_bin_x; i <= last_bin_x; ++i) {
660  for (int j = first_bin_y; j <= last_bin_y; ++j) {
661  for (int k = first_bin_z; k <= last_bin_z; ++k) {
662  double content = h->GetBinContent(i, j, k);
663  if (content > max_val) max_val = content;
664  if (content < min_val) min_val = content;
665  }
666  }
667  }
668  }
669  else {
670  NLogWarning("GetTrueHistogramMinMax: Histogram '%s' has unsupported dimension %d. "
671  "Using GetMaximum/GetMinimum as fallback.",
672  h->GetName(), h->GetDimension());
673  // As a fallback, try to get from GetMaximum/GetMinimum if dimension not 1,2,3
674  max_val = h->GetMaximum();
675  min_val = h->GetMinimum();
676  }
677 
678  // Handle the case where all bins might be empty or zero
679  if (max_val == -std::numeric_limits<double>::max() && min_val == std::numeric_limits<double>::max()) {
680  max_val = 0.0; // If no content was found, assume 0
681  min_val = 0.0;
682  }
683 }
684 
685 TFile * NUtils::OpenFile(std::string filename, std::string mode, bool createLocalDir)
686 {
690 
691  filename = gSystem->ExpandPathName(filename.c_str());
692  if (createLocalDir) {
693  // Printf("%s", filename.c_str());
694  if (!mode.compare("RECREATE") || !mode.compare("UPDATE") || !mode.compare("WRITE")) {
695 
696  TString filenameT(filename.c_str());
697  bool isLocalFile = filenameT.BeginsWith("file://");
698  if (isLocalFile) {
699  // Remove file:// prefix
700  filenameT.ReplaceAll("file://", "");
701  }
702  else {
703  isLocalFile = !filenameT.Contains("://");
704  }
705 
706  if (isLocalFile) {
707 
708  std::string pwd = gSystem->pwd();
709  if (filenameT[0] != '/') filenameT = pwd + "/" + filenameT;
710  filenameT.ReplaceAll("?remote=1&", "?");
711  filenameT.ReplaceAll("?remote=1", "");
712  filenameT.ReplaceAll("&remote=1", "");
713  TUrl url(filenameT.Data());
714 
715  std::string filenameLocal = gSystem->GetDirName(url.GetFile()).Data();
716  // Printf("Ndmspc::NUtils::OpenRootFile: Creating directory '%s' ...", filenameLocal.c_str());
717  gSystem->mkdir(filenameLocal.c_str(), kTRUE);
718  }
719  }
720  }
721  return TFile::Open(filename.c_str(), mode.c_str());
722 }
723 
724 std::string NUtils::OpenRawFile(std::string filename)
725 {
729 
730  std::string content;
731  TFile * f = OpenFile(TString::Format("%s?filetype=raw", filename.c_str()).Data());
732  if (!f) return "";
733 
734  // Printf("%lld", f->GetSize());
735 
736  int buffsize = 4096;
737  // FIXME: use smart pointer to avoid large stack allocation (check if working)
738  auto buff = std::make_unique<char[]>(buffsize + 1);
739  // char buff[buffsize + 1];
740 
741  Long64_t buffread = 0;
742  while (buffread < f->GetSize()) {
743  if (buffread + buffsize > f->GetSize()) buffsize = f->GetSize() - buffread;
744 
745  // Printf("Buff %lld %d", buffread, buffsize);
746  f->ReadBuffer(buff.get(), buffread, buffsize);
747  buff[buffsize] = '\0';
748  content += buff.get();
749  buffread += buffsize;
750  }
751  f->Close();
752  return content;
753 }
754 bool NUtils::SaveRawFile(std::string filename, std::string content)
755 {
759 
760  TFile * f = OpenFile(TString::Format("%s?filetype=raw", filename.c_str()).Data(), "RECREATE");
761  if (!f) {
762  NLogError("Error: Problem opening file '%s' in 'rw' mode ...", filename.c_str());
763  return false;
764  }
765  f->WriteBuffer(content.c_str(), content.size());
766  f->Close();
767  return true;
768 }
769 
770 TMacro * NUtils::OpenMacro(std::string filename)
771 {
775 
776  std::string content = OpenRawFile(filename);
777  if (content.empty()) {
778 
779  Printf("Error: Problem opening macro '%s' ...", filename.c_str());
780  return nullptr;
781  }
782  Printf("Using macro '%s' ...", filename.c_str());
783  TUrl url(filename.c_str());
784  std::string basefilename = gSystem->BaseName(url.GetFile());
785  basefilename.pop_back();
786  basefilename.pop_back();
787  TMacro * m = new TMacro();
788  m->SetName(basefilename.c_str());
789  m->AddLine(content.c_str());
790  return m;
791 }
792 
793 bool NUtils::LoadJsonFile(json & cfg, std::string filename)
794 {
798 
799  std::string content = OpenRawFile(filename);
800  if (content.empty()) {
801  NLogError("NUtils::LoadJsonFile: Problem opening JSON file '%s' ...", filename.c_str());
802  return false;
803  }
804 
805  try {
806  json myCfg = json::parse(content.c_str());
807  cfg.merge_patch(myCfg);
808  NLogInfo("NUtils::LoadJsonFile: Successfully parsed JSON file '%s' ...", filename.c_str());
809  }
810  catch (json::parse_error & e) {
811  NLogError("NUtils::LoadJsonFile: JSON parse error in file '%s' at byte %d: %s", filename.c_str(), e.byte, e.what());
812  return false;
813  }
814 
815  return true;
816 }
817 
818 std::vector<std::string> NUtils::Find(std::string path, std::string filename)
819 {
823 
824  std::vector<std::string> files;
825  TString pathStr = gSystem->ExpandPathName(path.c_str());
826  if (pathStr.IsNull() || filename.empty()) {
827  NLogError("NUtils::Find: Path or filename is empty");
828  return files;
829  }
830 
831  if (pathStr.BeginsWith("root://")) {
832  return FindEos(path, filename);
833  }
834  else {
835  return FindLocal(path, filename);
836  }
837 
838  return files;
839 }
840 
841 std::vector<std::string> NUtils::FindLocal(std::string path, std::string filename)
842 {
846 
847  std::vector<std::string> files;
848  if (gSystem->AccessPathName(path.c_str())) {
849  NLogError("NUtils::FindLocal: Path '%s' does not exist", path.c_str());
850  return files;
851  }
852  NLogInfo("Doing find %s -name %s", path.c_str(), filename.c_str());
853  std::string linesMerge =
854  gSystem->GetFromPipe(TString::Format("find %s -name %s", path.c_str(), filename.c_str())).Data();
855 
856  std::stringstream check2(linesMerge);
857  std::string line;
858  while (std::getline(check2, line)) {
859  files.push_back(line);
860  }
861  return files;
862 }
863 std::vector<std::string> NUtils::FindEos(std::string path, std::string filename)
864 {
868 
869  std::vector<std::string> files;
870  NLogInfo("Doing eos find -f --name %s %s ", filename.c_str(), path.c_str());
871 
872  TUrl url(path.c_str());
873  std::string host = url.GetHost();
874  std::string directory = url.GetFile();
875  std::string findUrl = "root://";
876  findUrl += host + "//proc/user/";
877  findUrl += "?mgm.cmd=find&mgm.find.match=" + filename;
878  findUrl += "&mgm.path=" + directory;
879  findUrl += "&mgm.format=json&mgm.option=f&filetype=raw";
880  NLogInfo("Doing TFile::Open on '%s' ...", findUrl.c_str());
881 
882  TFile * f = Ndmspc::NUtils::OpenFile(findUrl.c_str());
883  if (!f) return files;
884 
885  // Printf("%lld", f->GetSize());
886 
887  int buffsize = 4096;
888  // FIXME: use smart pointer to avoid large stack allocation (check if working)
889  auto buff = std::make_unique<char[]>(buffsize + 1);
890  // char buff[buffsize + 1];
891 
892  Long64_t buffread = 0;
893  std::string content;
894  while (buffread < f->GetSize()) {
895 
896  if (buffread + buffsize > f->GetSize()) buffsize = f->GetSize() - buffread;
897 
898  // Printf("Buff %lld %d", buffread, buffsize);
899  f->ReadBuffer(buff.get(), buffread, buffsize);
900  buff[buffsize] = '\0';
901  content += buff.get();
902  buffread += buffsize;
903  }
904 
905  f->Close();
906 
907  std::string ss = "mgm.proc.stdout=";
908  size_t pos = ss.size() + 1;
909  content = content.substr(pos);
910 
911  // stringstream class check1
912  std::stringstream check1(content);
913 
914  std::string intermediate;
915 
916  // Tokenizing w.r.t. space '&'
917  std::vector<std::string> tokens;
918  while (getline(check1, intermediate, '&')) {
919  tokens.push_back(intermediate);
920  }
921  std::string linesString = tokens[0];
922  for (auto & line : NUtils::Tokenize(linesString, '\n')) {
923  files.push_back("root://" + host + "/" + line);
924  }
925  return files;
926 }
927 
928 std::vector<std::string> NUtils::Tokenize(std::string_view input, const char delim)
929 {
933  std::vector<std::string> out;
934  size_t start = 0;
935  size_t end = input.find(delim);
936 
937  while (end != std::string_view::npos) {
938  if (end > start) {
939  out.emplace_back(input.substr(start, end - start));
940  }
941  start = end + 1;
942  end = input.find(delim, start);
943  }
944 
945  if (start < input.length()) {
946  out.emplace_back(input.substr(start));
947  }
948  return out;
949 }
950 std::vector<int> NUtils::TokenizeInt(std::string_view input, const char delim)
951 {
955 
956  std::vector<int> out;
957  std::vector<std::string> tokens = Tokenize(input, delim);
958  for (auto & t : tokens) {
959  if (t.empty()) continue;
960  out.push_back(std::stoi(t));
961  }
962 
963  return out;
964 }
965 
966 std::string NUtils::Join(const std::vector<std::string> & values, const char delim)
967 {
971 
972  std::string out;
973  for (const auto & v : values) {
974  if (!out.empty()) out += delim;
975  out += v;
976  }
977  return out;
978 }
979 std::string NUtils::Join(const std::vector<int> & values, const char delim)
980 {
984 
985  std::string out;
986  for (const auto & v : values) {
987  if (!out.empty()) out += delim;
988  out += std::to_string(v);
989  }
990  return out;
991 }
992 
993 std::vector<std::string> NUtils::Truncate(std::vector<std::string> values, std::string value)
994 {
998 
999  std::vector<std::string> out;
1000  for (auto & v : values) {
1001  v = std::string(v.begin() + value.size(), v.end());
1002  out.push_back(v);
1003  }
1004  return out;
1005 }
1006 
1007 std::set<std::string> NUtils::Unique(std::vector<std::string> & paths, int axis, std::string path, char token)
1008 {
1012 
1013  std::set<std::string> out;
1014  std::vector<std::string> truncatedPaths = NUtils::Truncate(paths, path);
1015  for (auto & p : truncatedPaths) {
1016  std::vector<std::string> tokens = Tokenize(p, token);
1017  out.insert(tokens[axis]);
1018  }
1019  return out;
1020 }
1021 
1022 TH1 * NUtils::ProjectTHnSparse(THnSparse * sparse, const std::vector<int> & axes, Option_t * option)
1023 {
1027  if (sparse == nullptr) {
1028  NLogError("Error: Sparse is nullptr ...");
1029  return nullptr;
1030  }
1031 
1032  TH1 * h = nullptr;
1033  if (axes.size() == 1) {
1034  h = sparse->Projection(axes[0], option);
1035  }
1036  else if (axes.size() == 2) {
1037  h = sparse->Projection(axes[1], axes[0], option);
1038  }
1039  else if (axes.size() == 3) {
1040  h = sparse->Projection(axes[0], axes[1], axes[2], option);
1041  }
1042  else {
1043  NLogError("Error: Only projection onto single axis is supported for TH1 ...");
1044  }
1045 
1046  h->SetName(TString::Format("%s_proj", sparse->GetName()).Data());
1047  h->SetTitle(TString::Format("%s Projection", sparse->GetTitle()).Data());
1048  // Detach from gDirectory to prevent TFile from claiming ownership of the histogram.
1049  // Without this, if gDirectory is an open TFile, the TFile will delete this histogram
1050  // when closed, causing a double-free when THStack/canvas tries to clean it up later.
1051  h->SetDirectory(nullptr);
1052 
1053  // Set labels for axis
1054  for (size_t i = 0; i < axes.size(); i++) {
1055  TAxis * axisSparse = sparse->GetAxis(axes[i]);
1056  TAxis * axisHist = h->GetXaxis();
1057  if (i == 1) axisHist = h->GetYaxis();
1058  if (i == 2) axisHist = h->GetZaxis();
1059 
1060  axisHist->SetName(axisSparse->GetName());
1061  axisHist->SetTitle(axisSparse->GetTitle());
1062 
1063  // Copy bin labels if alphanumeric
1064  if (axisSparse->IsAlphanumeric()) {
1065  for (int j = 1; j <= axisSparse->GetNbins(); j++) {
1066  const char * label = axisSparse->GetBinLabel(j);
1067  axisHist->SetBinLabel(j, label);
1068  }
1069  }
1070  }
1071 
1072  return h;
1073 }
1074 
1075 bool NUtils::SetAxisRanges(THnSparse * sparse, std::vector<std::vector<int>> ranges, bool withOverflow,
1076  bool modifyTitle, bool reset)
1077 {
1082 
1083  if (sparse == nullptr) {
1084  NLogError("Error: Sparse is nullptr ...");
1085  return false;
1086  }
1087  if (sparse->GetNdimensions() == 0) return true;
1088 
1089  if (reset) {
1090  NLogTrace("Setting axis ranges on '%s' THnSparse ...", sparse->GetName());
1092  for (int i = 0; i < sparse->GetNdimensions(); i++) {
1093  if (withOverflow) {
1094  NLogTrace("Resetting '%s' axis ...", sparse->GetAxis(i)->GetName());
1095  sparse->GetAxis(i)->SetRange(0, 0);
1096  }
1097  else {
1098  NLogTrace("Resetting '%s' axis [%d,%d] ...", sparse->GetAxis(i)->GetName(), 1, sparse->GetAxis(i)->GetNbins());
1099  sparse->GetAxis(i)->SetRange(1, sparse->GetAxis(i)->GetNbins());
1100  }
1101  }
1102  }
1103 
1104  if (ranges.empty()) {
1105  NLogTrace("No axis ranges to set ...");
1106  return true;
1107  }
1108 
1109  TAxis * axis = nullptr;
1110  TString title = sparse->GetTitle();
1111  if (modifyTitle) title += " Ranges:";
1112  for (size_t i = 0; i < ranges.size(); i++) {
1113  axis = sparse->GetAxis(ranges[i][0]);
1114  NLogTrace("Setting axis range %s=[%d,%d] ...", axis->GetName(), ranges[i][1], ranges[i][2]);
1115  if (ranges[i].size() != 3) {
1116  NLogError("Error: Axis range must have 3 values, but has %zu ...", ranges[i].size());
1117  return false;
1118  }
1119  axis->SetRange(ranges[i][1], ranges[i][2]);
1120  if (axis->IsAlphanumeric()) {
1121 
1122  title += TString::Format(" %s[%s]", axis->GetName(), axis->GetBinLabel(ranges[i][1]));
1123  }
1124  else {
1125  title += TString::Format(" %s[%0.2f - %0.2f]", axis->GetName(), axis->GetBinLowEdge(ranges[i][1]),
1126  axis->GetBinUpEdge(ranges[i][2]));
1127  }
1128  }
1129  if (modifyTitle) sparse->SetTitle(title.Data());
1130  return true;
1131 }
1132 
1133 bool NUtils::SetAxisRanges(THnSparse * sparse, std::map<int, std::vector<int>> ranges, bool withOverflow,
1134  bool modifyTitle, bool reset)
1135 {
1140 
1141  if (sparse == nullptr) {
1142  NLogError("NUtils::SetAxisRanges: Sparse is nullptr ...");
1143  return false;
1144  }
1145  if (sparse->GetNdimensions() == 0) return true;
1146 
1147  NLogTrace("NUtils::SetAxisRanges: Setting axis ranges on '%s' THnSparse ...", sparse->GetName());
1148  if (reset) {
1150  for (int i = 0; i < sparse->GetNdimensions(); i++) {
1151  if (withOverflow) {
1152  NLogTrace("NUtils::SetAxisRanges: Resetting '%s' axis ...", sparse->GetAxis(i)->GetName());
1153  sparse->GetAxis(i)->SetRange(0, 0);
1154  }
1155  else {
1156  NLogTrace("NUtils::SetAxisRanges: Resetting '%s' axis [%d,%d] ...", sparse->GetAxis(i)->GetName(), 1,
1157  sparse->GetAxis(i)->GetNbins());
1158  sparse->GetAxis(i)->SetRange(1, sparse->GetAxis(i)->GetNbins());
1159  }
1160  }
1161  }
1162 
1163  if (ranges.empty()) {
1164  NLogTrace("NUtils::SetAxisRanges: No axis ranges to set ...");
1165  return true;
1166  }
1167  TAxis * axis = nullptr;
1168  TString title = sparse->GetTitle();
1169  for (const auto & [key, val] : ranges) {
1170  NLogTrace("NUtils::SetAxisRanges: Setting axis range for axis %d to [%d,%d] ...", key, val[0], val[1]);
1171  axis = sparse->GetAxis(key);
1172  if (axis == nullptr) {
1173  NLogError("NUtils::SetAxisRanges: Axis %d is nullptr ...", key);
1174  return false;
1175  }
1176  NLogTrace("NUtils::SetAxisRanges: Setting axis range %s=[%d,%d] ...", axis->GetName(), val[0], val[1]);
1177  axis->SetRange(val[0], val[1]);
1178  if (axis->IsAlphanumeric()) {
1179 
1180  title += TString::Format(" %s[%s]", axis->GetName(), axis->GetBinLabel(val[0]));
1181  }
1182  else {
1183  title += TString::Format(" %s[%0.2f - %0.2f]", axis->GetName(), axis->GetBinLowEdge(val[0]),
1184  axis->GetBinUpEdge(val[1]));
1185  }
1186  }
1187 
1188  if (modifyTitle) sparse->SetTitle(title.Data());
1189  NLogTrace("NUtils::SetAxisRanges: New title: %s", sparse->GetTitle());
1190 
1191  return true;
1192 }
1193 bool NUtils::GetAxisRangeInBase(TAxis * a, int rebin, int rebin_start, int bin, int & min, int & max)
1194 {
1198  if (a == nullptr) {
1199  NLogError("Error: Axis is nullptr ...");
1200  return false;
1201  }
1202  min = -1;
1203  max = -1;
1204 
1205  NLogTrace("Getting axis range in base for '%s' rebin=%d rebin_start=%d bin=%d...", a->GetName(), rebin, rebin_start,
1206  bin);
1207 
1208  min = rebin * (bin - 1) + rebin_start;
1209  max = min + rebin - 1;
1210  NLogTrace("Axis '%s' min=%d max=%d", a->GetName(), min, max);
1211 
1212  if (min < 1) {
1213  NLogError("Error: Axis '%s' min=%d is lower then 1 ...", a->GetName(), min);
1214  min = -1;
1215  max = -1;
1216  return false;
1217  }
1218 
1219  if (max > a->GetNbins()) {
1220  NLogError("Error: Axis '%s' max=%d is higher then %d ...", a->GetName(), max, a->GetNbins());
1221  min = -1;
1222  max = -1;
1223  return false;
1224  }
1225 
1226  return true;
1227 }
1228 bool NUtils::GetAxisRangeInBase(TAxis * a, int min, int max, TAxis * base, int & minBase, int & maxBase)
1229 {
1234  int rebin = base->GetNbins() / a->GetNbins();
1235 
1236  // TODO: Improve handling of rebin_start correctly (depending on axis min and max of first bin)
1237  int rebin_start = (base->GetNbins() % a->GetNbins()) + 1;
1238  rebin_start = rebin != 1 ? rebin_start : 1; // start from 1
1239 
1240  NLogTrace("Getting axis range in base for '%s' min=%d max=%d rebin=%d rebin_start=%d...", a->GetName(), min, max,
1241  rebin, rebin_start);
1242 
1243  int tmp;
1244  GetAxisRangeInBase(base, rebin, rebin_start, min, minBase, tmp);
1245  GetAxisRangeInBase(base, rebin, rebin_start, max, tmp, maxBase);
1246  NLogTrace("Axis '%s' minBase=%d maxBase=%d", a->GetName(), minBase, maxBase);
1247 
1248  return true;
1249 }
1250 
1251 TObjArray * NUtils::AxesFromDirectory(const std::vector<std::string> paths, const std::string & findPath,
1252  const std::string & fileName, const std::vector<std::string> & axesNames)
1253 {
1254  if (paths.empty()) {
1255  NLogError("Error: No paths provided ...");
1256  return nullptr;
1257  }
1258 
1259  std::map<std::string, std::set<std::string>> axes;
1260  for (const auto & path : paths) {
1261  NLogInfo("Found file: %s", path.c_str());
1262  // remove prefix basePath from path
1263  TString relativePath = path;
1264  relativePath.ReplaceAll(findPath.c_str(), "");
1265  relativePath.ReplaceAll(fileName.c_str(), "");
1266  // relativePath.ReplaceAll("years", "");
1267  // relativePath.ReplaceAll("data", "");
1268  relativePath.ReplaceAll("//", "/");
1269  // remove leading slash
1270  relativePath.Remove(0, relativePath.BeginsWith("/") ? 1 : 0);
1271  // remove trailing slash
1272  relativePath.Remove(relativePath.EndsWith("/") ? relativePath.Length() - 1 : relativePath.Length(), 1);
1273 
1274  std::vector<std::string> tokens = Ndmspc::NUtils::Tokenize(relativePath.Data(), '/');
1275 
1276  // if (tokens.size() < axesNames.size()) {
1277  // tokens.push_back("mb");
1278  // }
1279  //
1280  if (tokens.size() != axesNames.size()) {
1281  continue;
1282  }
1283 
1284  for (size_t i = 0; i < tokens.size(); ++i) {
1285  axes[axesNames[i]].insert(tokens[i]);
1286  }
1287  }
1288 
1289  TObjArray * axesArr = new TObjArray();
1290  for (const auto & axisName : axesNames) {
1291  TAxis * axis = Ndmspc::NUtils::CreateAxisFromLabelsSet(axisName, axisName, axes[axisName]); // Convert set to vector
1292  axesArr->Add(axis);
1293  }
1294 
1295  return axesArr;
1296 }
1297 std::string NUtils::GetJsonString(json j)
1298 {
1302 
1303  if (j.is_string()) {
1304  return j.get<std::string>();
1305  }
1306  else if (j.is_number_integer()) {
1307  return std::to_string(j.get<int>());
1308  }
1309  else if (j.is_number_float()) {
1310  return std::to_string(j.get<double>());
1311  }
1312  else if (j.is_boolean()) {
1313  return j.get<bool>() ? "true" : "false";
1314  }
1315  else if (j.is_null()) {
1316  return "";
1317  }
1318  else {
1319  return "";
1320  }
1321 }
1323 {
1327 
1328  if (j.is_number_integer()) {
1329  return j.get<int>();
1330  }
1331  else if (j.is_number_float()) {
1332  return static_cast<int>(j.get<double>());
1333  }
1334  else if (j.is_boolean()) {
1335  return j.get<bool>() ? 1 : 0;
1336  }
1337  else if (j.is_null()) {
1338  return -1;
1339  }
1340  else {
1341  return -1;
1342  }
1343 }
1344 
1346 {
1350 
1351  if (j.is_number_float()) {
1352  return j.get<double>();
1353  }
1354  else if (j.is_number_integer()) {
1355  return static_cast<double>(j.get<int>());
1356  }
1357  else if (j.is_boolean()) {
1358  return j.get<bool>() ? 1.0 : 0.0;
1359  }
1360  else if (j.is_null()) {
1361  return -1.0;
1362  }
1363  else {
1364  return -1.0;
1365  }
1366 }
1367 
1369 {
1373 
1374  if (j.is_boolean()) {
1375  return j.get<bool>();
1376  }
1377  else if (j.is_number_integer()) {
1378  return j.get<int>() != 0;
1379  }
1380  else if (j.is_number_float()) {
1381  return j.get<double>() != 0.0;
1382  }
1383  else if (j.is_null()) {
1384  return false;
1385  }
1386  else {
1387  return false;
1388  }
1389 }
1390 
1391 std::vector<std::string> NUtils::GetJsonStringArray(json j)
1392 {
1396 
1397  std::vector<std::string> out;
1398  if (j.is_array()) {
1399  for (auto & v : j) {
1400  out.push_back(GetJsonString(v));
1401  }
1402  }
1403  return out;
1404 }
1405 
1406 std::vector<int> NUtils::ArrayToVector(Int_t * v1, int size)
1407 {
1411 
1412  std::vector<int> v2;
1413  for (int i = 0; i < size; i++) {
1414  v2.push_back(v1[i]);
1415  }
1416  return v2;
1417 }
1418 
1419 void NUtils::VectorToArray(std::vector<int> v1, Int_t * v2)
1420 {
1424 
1425  for (size_t i = 0; i < v1.size(); i++) {
1426  v2[i] = v1[i];
1427  }
1428 }
1429 std::string NUtils::GetCoordsString(const std::vector<Long64_t> & coords, int index, int width)
1430 {
1434  std::stringstream msg;
1435  if (index >= 0) msg << "[" << std::setw(3) << std::setfill('0') << index << "] ";
1436  msg << "[";
1437  for (size_t i = 0; i < coords.size(); ++i) {
1438  msg << std::setw(width) << std::setfill(' ') << coords[i] << (i == coords.size() - 1 ? "" : ",");
1439  }
1440  msg << "]";
1441  return msg.str();
1442 }
1443 std::string NUtils::GetCoordsString(const std::vector<int> & coords, int index, int width)
1444 {
1448  std::stringstream msg;
1449  if (index >= 0) msg << "[" << std::setw(3) << std::setfill('0') << index << "] ";
1450  msg << "[";
1451  for (size_t i = 0; i < coords.size(); ++i) {
1452  msg << std::setw(width) << std::setfill(' ') << coords[i] << (i == coords.size() - 1 ? "" : ",");
1453  }
1454  msg << "]";
1455  return msg.str();
1456 }
1457 std::string NUtils::GetCoordsString(const std::vector<size_t> & coords, int index, int width)
1458 {
1462  std::stringstream msg;
1463  if (index >= 0) msg << "[" << std::setw(3) << std::setfill('0') << index << "] ";
1464  msg << "[";
1465  for (size_t i = 0; i < coords.size(); ++i) {
1466  msg << std::setw(width) << std::setfill(' ') << coords[i] << (i == coords.size() - 1 ? "" : ",");
1467  }
1468  msg << "]";
1469  return msg.str();
1470 }
1471 std::string NUtils::GetCoordsString(const std::vector<std::string> & coords, int index, int width)
1472 {
1476  std::stringstream msg;
1477  if (index >= 0) msg << "[" << std::setw(3) << std::setfill('0') << index << "] ";
1478  msg << "[";
1479  for (size_t i = 0; i < coords.size(); ++i) {
1480  msg << std::setw(width) << std::setfill(' ') << coords[i] << (i == coords.size() - 1 ? "" : ",");
1481  }
1482  msg << "]";
1483  return msg.str();
1484 }
1485 void NUtils::PrintPointSafe(const std::vector<int> & coords, int index)
1486 {
1490 
1491  NLogInfo("%s", GetCoordsString(coords, index).c_str());
1492 }
1493 
1494 std::vector<std::vector<int>> NUtils::Permutations(const std::vector<int> & v)
1495 {
1499  std::vector<std::vector<int>> result;
1500  std::vector<int> current = v;
1501  std::sort(current.begin(), current.end());
1502  do {
1503  result.push_back(current);
1504  } while (std::next_permutation(current.begin(), current.end()));
1505 
1506  // print the permutations
1507  NLogTrace("Permutations of vector: %s", GetCoordsString(v).c_str());
1508  for (const auto & perm : result) {
1509  NLogTrace("Permutation: %s", GetCoordsString(perm).c_str());
1510  }
1511 
1512  return result;
1513 }
1514 
1515 std::string NUtils::FormatTime(long long seconds)
1516 {
1517  long long hours = seconds / 3600;
1518  seconds %= 3600;
1519  long long minutes = seconds / 60;
1520  seconds %= 60;
1521 
1522  std::stringstream ss;
1523  ss << std::setw(2) << std::setfill('0') << hours << ":" << std::setw(2) << std::setfill('0') << minutes << ":"
1524  << std::setw(2) << std::setfill('0') << seconds;
1525  return ss.str();
1526 }
1527 
1528 void NUtils::ProgressBar(int current, int total, std::string prefix, std::string suffix, int barWidth)
1529 {
1530 
1534  if (total == 0) return; // Avoid division by zero
1535 
1536  // Let's do protection against any log to be written during progress bar
1537  std::lock_guard<std::mutex> lock(NLogger::GetLoggerMutex());
1538 
1539  float percentage = static_cast<float>(current) / total;
1540  int numChars = static_cast<int>(percentage * barWidth);
1541 
1542  std::cout << "\r"; // Carriage return
1543  if (!prefix.empty()) std::cout << "[" << prefix << "]"; // Carriage return
1544  std::cout << "["; // Carriage return
1545 
1546  for (int i = 0; i < numChars; ++i) {
1547  std::cout << "=";
1548  }
1549  for (int i = 0; i < barWidth - numChars; ++i) {
1550  std::cout << " ";
1551  }
1552  std::cout << "] " << static_cast<int>(percentage * 100.0) << "%"
1553  << " (" << current << "/" << total << ")";
1554  if (!suffix.empty()) std::cout << " [" << suffix << "]";
1555  if (current == total) std::cout << std::endl;
1556  std::cout << std::flush; // Ensure immediate output
1557 }
1558 
1559 void NUtils::ProgressBar(int current, int total, std::chrono::high_resolution_clock::time_point startTime,
1560  std::string prefix, std::string suffix, int barWidth)
1561 {
1565  if (total == 0) return; // Avoid division by zero
1566  std::lock_guard<std::mutex> lock(NLogger::GetLoggerMutex());
1567  if (current > total) current = total; // Cap current to total for safety
1568 
1569  float percentage = static_cast<float>(current) / total;
1570  int numChars = static_cast<int>(percentage * barWidth);
1571 
1572  // std::cout << "\r[" << prefix << "] ["; // Carriage return
1573  std::cout << "\r[";
1574  if (!prefix.empty()) std::cout << prefix << "]["; // Carriage return
1575  for (int i = 0; i < numChars; ++i) {
1576  std::cout << "=";
1577  }
1578  for (int i = 0; i < barWidth - numChars; ++i) {
1579  std::cout << " ";
1580  }
1581  std::cout << "] " << std::setw(3) << static_cast<int>(percentage * 100.0) << "%";
1582 
1583  // Calculate elapsed time
1584  auto currentTime = std::chrono::high_resolution_clock::now();
1585  auto elapsedSeconds = std::chrono::duration_cast<std::chrono::seconds>(currentTime - startTime).count();
1586 
1587  // Calculate estimated remaining time (only if we've made some progress)
1588  long long estimatedRemainingSeconds = 0;
1589  if (current > 0 && percentage > 0) {
1590  // Total estimated time = (elapsed time / current progress) * 100%
1591  long long totalEstimatedSeconds = static_cast<long long>(elapsedSeconds / percentage);
1592  estimatedRemainingSeconds = totalEstimatedSeconds - elapsedSeconds;
1593  }
1594 
1595  std::cout << " (" << current << "/" << total << ") "
1596  << "Elapsed: " << FormatTime(elapsedSeconds) << " "
1597  << "ETA: " << FormatTime(estimatedRemainingSeconds);
1598  if (!suffix.empty()) std::cout << " [" << suffix << "]";
1599  if (current == total) std::cout << std::endl;
1600  std::cout << std::flush; // Ensure immediate output
1601 }
1602 
1603 TCanvas * NUtils::CreateCanvas(const std::string & name, const std::string & title, int width, int height)
1604 {
1608 
1609  TThread::Lock();
1610  TCanvas * c = new TCanvas("", title.c_str(), width, height);
1611  gROOT->GetListOfCanvases()->Remove(c);
1612  c->ResetBit(kMustCleanup);
1613  c->SetBit(kCanDelete, kFALSE);
1614  c->SetName(name.c_str());
1615  TThread::UnLock();
1616  return c;
1617 }
1618 
1619 #ifdef WITH_PARQUET
1620 THnSparse * NUtils::CreateSparseFromParquetTaxi(const std::string & filename, THnSparse * hns, Int_t nMaxRows)
1621 {
1625  // Open the Parquet file
1626 
1627  if (hns == nullptr) {
1628  NLogError("NUtils::CreateSparseFromParquetTaxi: THnSparse 'hns' is nullptr ...");
1629  return nullptr;
1630  }
1631 
1632  std::shared_ptr<arrow::io::ReadableFile> infile;
1633  arrow::Result<std::shared_ptr<arrow::io::ReadableFile>> infile_result = arrow::io::ReadableFile::Open(filename);
1634  if (!infile_result.ok()) {
1635  NLogError("NUtils::CreateSparseFromParquetTaxi: Error opening file %s: %s", filename.c_str(),
1636  infile_result.status().ToString().c_str());
1637  return nullptr;
1638  }
1639  infile = infile_result.ValueUnsafe();
1640 
1641  // Create a Parquet reader using the modern arrow::Result API
1642  std::unique_ptr<parquet::arrow::FileReader> reader;
1643 
1644  // The new approach using arrow::Result:
1645  arrow::Result<std::unique_ptr<parquet::arrow::FileReader>> reader_result =
1646  parquet::arrow::OpenFile(infile, arrow::default_memory_pool()); // No third parameter!
1647  if (!reader_result.ok()) {
1648  NLogError("NUtils::CreateSparseFromParquetTaxi: Error opening Parquet file reader for file %s: %s",
1649  filename.c_str(), reader_result.status().ToString().c_str());
1650  arrow::Status status = infile->Close(); // Attempt to close
1651  return nullptr;
1652  }
1653  reader = std::move(reader_result).ValueUnsafe(); // Transfer ownership from Result to unique_ptr
1654 
1655  // Get file metadata (optional)
1656  // Note: parquet_reader() returns a const ptr, and metadata() returns a shared_ptr
1657  std::shared_ptr<parquet::FileMetaData> file_metadata = reader->parquet_reader()->metadata();
1658  NLogTrace("Parquet file '%s' opened successfully.", filename.c_str());
1659  NLogTrace("Parquet file version: %d", file_metadata->version());
1660  NLogTrace("Parquet created by: %s", file_metadata->created_by().c_str());
1661  NLogTrace("Parquet number of columns: %d", file_metadata->num_columns());
1662  NLogTrace("Parquet number of rows: %lld", file_metadata->num_rows());
1663  NLogTrace("Parquet number of row groups: %d", file_metadata->num_row_groups());
1664 
1665  // Read the entire file as a Table
1666  // std::shared_ptr<arrow::Table> table;
1667  // arrow::Status status = reader->ReadTable(&table); // ReadTable still returns Status
1668  std::shared_ptr<arrow::RecordBatchReader> batch_reader;
1669  arrow::Status status = reader->GetRecordBatchReader(&batch_reader);
1670  if (!status.ok()) {
1671  NLogError("NUtils::CreateSparseFromParquetTaxi: Error reading table from Parquet file %s: %s", filename.c_str(),
1672  status.ToString().c_str());
1673  status = infile->Close();
1674  return nullptr;
1675  }
1676 
1677  // It's good practice to close the input file stream when done
1678  status = infile->Close();
1679  if (!status.ok()) {
1680  NLogWarning("NUtils::CreateSparseFromParquetTaxi: Error closing input file %s: %s", filename.c_str(),
1681  status.ToString().c_str());
1682  // This is a warning, we still want to return the table.
1683  }
1684 
1685  // Print schema of the table
1686  NLogTrace("Parquet Table Schema:\n%s", batch_reader->schema()->ToString().c_str());
1687 
1688  const Int_t nDims = hns->GetNdimensions();
1689  std::vector<std::string> column_names;
1690  for (int i = 0; i < nDims; ++i) {
1691  column_names.push_back(hns->GetAxis(i)->GetName());
1692  }
1693  // std::cout << "\nData (first 5 rows):\n";
1694 
1695  // int max_rows = table->num_rows();
1696  int max_rows = 1e8;
1697  max_rows = nMaxRows > 0 ? std::min(max_rows, nMaxRows) : max_rows;
1698  int print_rows = std::min(max_rows, 5);
1699  // auto table_batch_reader = std::make_shared<arrow::TableBatchReader>(*table);
1700  auto table_batch_reader = batch_reader;
1701  std::shared_ptr<arrow::RecordBatch> batch;
1702  auto point = std::make_unique<Double_t[]>(nDims);
1703  // Double_t point[nDims];
1704 
1705  if (print_rows > 0) {
1706  NLogTrace("Printing first %d rows of Parquet file '%s' ...", print_rows, filename.c_str());
1707  // NLogInfo("Columns: %s", NUtils::Join(column_names, '\t').c_str());
1708  }
1709 
1710  int batch_count = 0;
1711  while (table_batch_reader->ReadNext(&batch).ok() && batch) {
1712  batch_count++;
1713  NLogTrace("Processing batch with %d rows and %d columns ...", batch->num_rows(), batch->num_columns());
1714  for (int i = 0; i < batch->num_rows(); ++i) {
1715  if (i >= max_rows) break; // Limit to first 5 rows for display
1716 
1717  bool isValid = true;
1718  int idx = 0;
1719  for (int j = 0; j < batch->num_columns(); ++j) {
1720  if (std::find(column_names.begin(), column_names.end(), batch->column_name(j)) == column_names.end())
1721  continue; // Skip columns not in our list
1722  // NLogDebug("[%d %s]Processing row %d, column '%s' ...", idx, hns->GetAxis(idx)->GetName(), i,
1723  // batch->column_name(j).c_str());
1724  // std::cout << batch->column_name(j) << "\t";
1725  const auto & array = batch->column(j);
1726  arrow::Result<std::shared_ptr<arrow::Scalar>> scalar_result = array->GetScalar(i);
1727  if (scalar_result.ok()) {
1728  // if (i * batch_count < print_rows) std::cout << scalar_result.ValueUnsafe()->ToString() << "\t";
1729  if (scalar_result.ValueUnsafe()->is_valid) {
1730  TAxis * axis = hns->GetAxis(idx);
1731  if (scalar_result.ValueUnsafe()->type->id() == arrow::Type::STRING ||
1732  scalar_result.ValueUnsafe()->type->id() == arrow::Type::LARGE_STRING) {
1733  // Arrow StringScalar's value is an arrow::util::string_view or arrow::util::string_view
1734  // It's best to convert it to std::string for general use.
1735  std::string value = scalar_result.ValueUnsafe()->ToString();
1736  // TODO: check if not shifted by one
1737  // NLogInfo("NUtils::CreateSparseFromParquetTaxi: Mapping string value '%s' to axis '%s' ...",
1738  // value.c_str(), axis->GetName());
1739  point[idx] = axis->GetBinCenter(axis->FindBin(value.c_str()));
1740  }
1741  else if (scalar_result.ValueUnsafe()->type->id() == arrow::Type::INT32) {
1742  auto int_scalar = std::static_pointer_cast<arrow::Int32Scalar>(scalar_result.ValueUnsafe());
1743 
1744  point[idx] = static_cast<Double_t>(int_scalar->value);
1745  }
1746  else if (scalar_result.ValueUnsafe()->type->id() == arrow::Type::INT64) {
1747  auto int64_scalar = std::static_pointer_cast<arrow::Int64Scalar>(scalar_result.ValueUnsafe());
1748  point[idx] = static_cast<Double_t>(int64_scalar->value);
1749  }
1750  else if (scalar_result.ValueUnsafe()->type->id() == arrow::Type::UINT32) {
1751  auto uint32_scalar = std::static_pointer_cast<arrow::UInt32Scalar>(scalar_result.ValueUnsafe());
1752  point[idx] = static_cast<Double_t>(uint32_scalar->value);
1753  }
1754  else if (scalar_result.ValueUnsafe()->type->id() == arrow::Type::FLOAT) {
1755  auto float_scalar = std::static_pointer_cast<arrow::FloatScalar>(scalar_result.ValueUnsafe());
1756  point[idx] = static_cast<Double_t>(float_scalar->value);
1757  }
1758  else if (scalar_result.ValueUnsafe()->type->id() == arrow::Type::DOUBLE) {
1759  auto double_scalar = std::static_pointer_cast<arrow::DoubleScalar>(scalar_result.ValueUnsafe());
1760  point[idx] = double_scalar->value;
1761  }
1762  else {
1763  NLogError("NUtils::CreateSparseFromParquetTaxi: Unsupported data type for column '%s' ...",
1764  batch->column_name(j).c_str());
1765  isValid = false;
1766  }
1767  }
1768  else {
1769  // Handle null values (set to 0 or some default)
1770  //
1771  //
1772  point[idx] = -1000;
1773  isValid = false;
1774  isValid = true;
1775  }
1776  }
1777  else {
1778  NLogError("NUtils::CreateSparseFromParquetTaxi: Error getting scalar at (%d,%d): %s", i, j,
1779  scalar_result.status().ToString().c_str());
1780  isValid = false;
1781  }
1782  idx++;
1783  }
1784  // if (i * batch_count < print_rows) std::cout << std::endl;
1785  if (isValid) {
1786  // print point
1787  // for (int d = 0; d < nDims; ++d) {
1788  // NLogDebug("Point[%d=%s]=%f", d, hns->GetAxis(d)->GetName(), point[d]);
1789  // }
1790  hns->Fill(point.get());
1791  }
1792  else {
1793  NLogWarning("Skipping row %d due to invalid data.", i);
1794  }
1795  }
1796  }
1797  return hns;
1798 }
1799 #else
1800 THnSparse * NUtils::CreateSparseFromParquetTaxi(const std::string & /*filename*/, THnSparse * /*hns*/,
1801  Int_t /*nMaxRows*/)
1802 {
1803  NLogError("Parquet support is not enabled. Please compile with Parquet support.");
1804  return nullptr;
1805 }
1806 #endif
1807 
1808 void NUtils::SafeDeleteObjects(std::vector<TObject *> & objects)
1809 {
1810  if (objects.empty()) return;
1811 
1812  // With EnableThreadSafety(), every TList has fUsingRWLock=true, so
1813  // TList::Clear() acquires gCoreMutex and calls GarbageCollect() on each
1814  // element. When deleting a TCanvas, the cascade TPad::Close() →
1815  // fPrimitives->Clear() → GarbageCollect() crashes.
1816  //
1817  // Fix: fully disarm every pad's primitive list (disable RW lock, mark
1818  // non-owning, remove all links with "nodelete"), then delete the objects.
1819  // Orphaned primitives (histograms, frames, sub-pads) are collected and
1820  // deleted manually afterwards.
1821 
1822  Bool_t prevMustClean = gROOT->MustClean();
1823  gROOT->SetMustClean(kFALSE);
1824 
1825  // Collect all pads breadth-first (top-level + nested sub-pads)
1826  std::vector<TPad *> pads;
1827  for (auto * obj : objects) {
1828  if (obj && obj->InheritsFrom(TPad::Class()))
1829  pads.push_back(static_cast<TPad *>(obj));
1830  }
1831  for (size_t i = 0; i < pads.size(); ++i) {
1832  TList * prims = pads[i]->GetListOfPrimitives();
1833  if (!prims || prims->IsEmpty()) continue;
1834  for (TObjLink * lnk = prims->FirstLink(); lnk; lnk = lnk->Next()) {
1835  TObject * child = lnk->GetObject();
1836  if (child && child->InheritsFrom(TPad::Class()))
1837  pads.push_back(static_cast<TPad *>(child));
1838  }
1839  }
1840 
1841  // Track input objects to avoid double-deleting shared primitives
1842  std::set<TObject *> inputSet(objects.begin(), objects.end());
1843  inputSet.erase(nullptr);
1844 
1845  // Disarm all pad primitive lists (deepest first) and collect orphans
1846  std::set<TObject *> orphans;
1847  for (auto it = pads.rbegin(); it != pads.rend(); ++it) {
1848  TList * prims = (*it)->GetListOfPrimitives();
1849  if (!prims) continue;
1850  // Collect primitives not in the input vector — they'd leak otherwise
1851  for (TObjLink * lnk = prims->FirstLink(); lnk; lnk = lnk->Next()) {
1852  TObject * child = lnk->GetObject();
1853  if (child && inputSet.find(child) == inputSet.end())
1854  orphans.insert(child);
1855  }
1856  // Disarm: no lock, non-owning, remove links only (no GarbageCollect)
1857  prims->UseRWLock(kFALSE);
1858  prims->SetOwner(kFALSE);
1859  prims->Clear("nodelete");
1860  }
1861 
1862  // Delete all input objects (canvas/pad destructors find empty primitive lists)
1863  for (auto * obj : objects) {
1864  if (obj) delete obj;
1865  }
1866  objects.clear();
1867 
1868  // Delete orphaned primitives (histograms, frames, sub-pads extracted above)
1869  for (auto * obj : orphans) {
1870  delete obj;
1871  }
1872 
1873  gROOT->SetMustClean(prevMustClean);
1874 }
1875 
1876 void NUtils::SafeDeleteTList(TList *& lst)
1877 {
1878  if (!lst) return;
1879 
1880  // Extract objects from the TList into a vector
1881  std::vector<TObject *> objects;
1882  for (TObjLink * lnk = lst->FirstLink(); lnk; lnk = lnk->Next()) {
1883  TObject * obj = lnk->GetObject();
1884  if (obj) objects.push_back(obj);
1885  }
1886 
1887  // Destroy the TList shell without touching objects
1888  lst->UseRWLock(kFALSE);
1889  lst->SetOwner(kFALSE);
1890  lst->Clear("nodelete");
1891  delete lst;
1892  lst = nullptr;
1893 
1894  // Delete all collected objects safely
1895  SafeDeleteObjects(objects);
1896 }
1897 
1898 void NUtils::SafeDeleteObject(TObject *& obj)
1899 {
1900  if (!obj) return;
1901 
1902  if (obj->InheritsFrom(TList::Class())) {
1903  TList * lst = static_cast<TList *>(obj);
1904  obj = nullptr;
1905  SafeDeleteTList(lst);
1906  } else {
1907  delete obj;
1908  obj = nullptr;
1909  }
1910 }
1911 
1913 {
1914  json out;
1915  ProcInfo_t info;
1916  gSystem->GetProcInfo(&info);
1917 
1918  out["cpu_user"] = info.fCpuUser;
1919  out["cpu_sys"] = info.fCpuSys;
1920  out["cpu_total"] = info.fCpuUser + info.fCpuSys;
1921  out["mem_rss_kb"] = info.fMemResident;
1922  out["mem_vsize_kb"] = info.fMemVirtual;
1923 
1924  // Report number of logical CPUs available on the host
1925  unsigned int hc = std::thread::hardware_concurrency();
1926  out["cpu_count"] = (hc == 0) ? 1 : static_cast<int>(hc);
1927 
1928  return out;
1929 }
1930 
1932 {
1933  json out;
1934  out["totalRead"] = 0LL;
1935  out["totalWritten"] = 0LL;
1936 
1937  TList * files = (TList*)gROOT->GetListOfFiles();
1938  if (!files) return out;
1939 
1940  Long64_t totalRead = 0;
1941  Long64_t totalWritten = 0;
1942 
1943  TIter next(files);
1944  TObject * obj = nullptr;
1945  while ((obj = next())) {
1946  TFile * f = dynamic_cast<TFile *>(obj);
1947  if (!f) continue;
1948  json fi;
1949  fi["name"] = f->GetName() ? f->GetName() : "";
1950  fi["isZombie"] = (bool)f->IsZombie();
1951  fi["isOpen"] = (bool)f->IsOpen();
1952 
1953  // Try to read per-file counters if available in this ROOT build
1954  Long64_t bytesRead = 0;
1955  Long64_t bytesWritten = 0;
1956  // Many ROOT versions expose GetBytesRead/GetBytesWritten on TFile; attempt to call them.
1957  // If they are not available, these calls will fail to link — in that case, users
1958  // can replace this implementation with a platform-specific /proc reader.
1959 #if 1
1960  // Use C-style cast to call methods if they exist; rely on linker to resolve.
1961  // If unavailable, these lines may need adjustment for older ROOT versions.
1962  try {
1963  bytesRead = f->GetBytesRead();
1964  bytesWritten = f->GetBytesWritten();
1965  }
1966  catch (...) {
1967  bytesRead = 0;
1968  bytesWritten = 0;
1969  }
1970 #endif
1971 
1972  fi["bytesRead"] = bytesRead;
1973  fi["bytesWritten"] = bytesWritten;
1974 
1975  totalRead += bytesRead;
1976  totalWritten += bytesWritten;
1977 
1978  out["files"].push_back(fi);
1979  }
1980 
1981  out["totalRead"] = totalRead;
1982  out["totalWritten"] = totalWritten;
1983 
1984  return out;
1985 }
1986 
1988 {
1989  json out;
1990  out["total_rx"] = 0ULL;
1991  out["total_tx"] = 0ULL;
1992 
1993 #if defined(__linux__)
1994  std::ifstream f("/proc/net/dev");
1995  if (!f.good()) return out;
1996  std::string line;
1997  // skip headers
1998  std::getline(f, line);
1999  std::getline(f, line);
2000  while (std::getline(f, line)) {
2001  if (line.empty()) continue;
2002  size_t colon = line.find(':');
2003  if (colon == std::string::npos) continue;
2004  std::string ifname = line.substr(0, colon);
2005  // trim
2006  auto ltrim = [](std::string & s) {
2007  size_t start = s.find_first_not_of(" \t");
2008  if (start != std::string::npos) s = s.substr(start);
2009  else s.clear();
2010  };
2011  auto rtrim = [](std::string & s) {
2012  size_t end = s.find_last_not_of(" \t");
2013  if (end != std::string::npos) s = s.substr(0, end + 1);
2014  else s.clear();
2015  };
2016  ltrim(ifname);
2017  rtrim(ifname);
2018  std::string rest = line.substr(colon + 1);
2019  std::stringstream ss(rest);
2020  std::vector<unsigned long long> vals;
2021  std::string tok;
2022  while (ss >> tok) {
2023  try {
2024  vals.push_back(std::stoull(tok));
2025  }
2026  catch (...) {
2027  vals.push_back(0ULL);
2028  }
2029  }
2030  if (vals.size() >= 9) {
2031  unsigned long long rx = vals[0];
2032  unsigned long long tx = vals[8];
2033  json iface;
2034  iface["name"] = ifname;
2035  iface["rx"] = rx;
2036  iface["tx"] = tx;
2037  out["interfaces"].push_back(iface);
2038  out["total_rx"] = static_cast<unsigned long long>(out["total_rx"].is_null() ? 0ULL : out["total_rx"].get<unsigned long long>()) + rx;
2039  out["total_tx"] = static_cast<unsigned long long>(out["total_tx"].is_null() ? 0ULL : out["total_tx"].get<unsigned long long>()) + tx;
2040  }
2041  }
2042 
2043 #elif defined(__APPLE__)
2044  struct ifaddrs *ifap = nullptr;
2045  if (getifaddrs(&ifap) != 0) return out;
2046  for (struct ifaddrs *ifa = ifap; ifa; ifa = ifa->ifa_next) {
2047  if (!ifa->ifa_data) continue;
2048  struct if_data *ifd = (struct if_data *)ifa->ifa_data;
2049  if (!ifd) continue;
2050  unsigned long long rx = (unsigned long long)ifd->ifi_ibytes;
2051  unsigned long long tx = (unsigned long long)ifd->ifi_obytes;
2052  json iface;
2053  iface["name"] = ifa->ifa_name ? ifa->ifa_name : std::string();
2054  iface["rx"] = rx;
2055  iface["tx"] = tx;
2056  out["interfaces"].push_back(iface);
2057  out["total_rx"] = static_cast<unsigned long long>(out["total_rx"].is_null() ? 0ULL : out["total_rx"].get<unsigned long long>()) + rx;
2058  out["total_tx"] = static_cast<unsigned long long>(out["total_tx"].is_null() ? 0ULL : out["total_tx"].get<unsigned long long>()) + tx;
2059  }
2060  freeifaddrs(ifap);
2061 #else
2062  // Unsupported platform: return empty totals
2063 #endif
2064 
2065  return out;
2066 }
2067 
2068 } // namespace Ndmspc
Provides HTTP request functionality using libcurl.
Definition: NHttpRequest.h:21
int head(const std::string &url, const std::string &cert_path="", const std::string &key_path="", const std::string &key_password_file="", bool insecure=false)
Performs an HTTP HEAD request.
static std::mutex & GetLoggerMutex()
Get logger mutex reference.
Definition: NLogger.h:536
Utility class providing static helper functions for file operations, histogram manipulations,...
Definition: NUtils.h:24
static void GetTrueHistogramMinMax(const TH1 *h, double &min_val, double &max_val, bool include_overflow_underflow=false)
Get minimum and maximum value of histogram bins.
Definition: NUtils.cxx:618
static bool SetAxisRanges(THnSparse *sparse, std::vector< std::vector< int >> ranges={}, bool withOverflow=false, bool modifyTitle=false, bool reset=true)
Set axis ranges for THnSparse using vector of ranges.
Definition: NUtils.cxx:1075
static TFile * OpenFile(std::string filename, std::string mode="READ", bool createLocalDir=true)
Open a ROOT file.
Definition: NUtils.cxx:685
static int Cp(std::string source, std::string destination)
Copy a file from source to destination.
Definition: NUtils.cxx:152
static std::vector< std::string > Truncate(std::vector< std::string > values, std::string value)
Truncate vector of strings by a value.
Definition: NUtils.cxx:993
static TH1 * ProjectTHnSparse(THnSparse *hns, const std::vector< int > &axes, Option_t *option="")
Project a THnSparse histogram onto specified axes.
Definition: NUtils.cxx:1022
static bool IsFileSupported(std::string filename)
Check if a file is supported.
Definition: NUtils.cxx:94
static std::vector< std::string > FindEos(std::string path, std::string filename="")
Find EOS files in a path matching filename.
Definition: NUtils.cxx:863
static bool LoadJsonFile(json &cfg, std::string filename)
Loads a JSON configuration file into the provided json object.
Definition: NUtils.cxx:793
static json GetTFileIOStats()
Get TFile read/write statistics by inspecting ROOT's list of open files.
Definition: NUtils.cxx:1931
static bool SaveRawFile(std::string filename, std::string content)
Save content to a raw file.
Definition: NUtils.cxx:754
static std::string OpenRawFile(std::string filename)
Open a raw file and return its content as string.
Definition: NUtils.cxx:724
static std::vector< std::string > FindLocal(std::string path, std::string filename="")
Find local files in a path matching filename.
Definition: NUtils.cxx:841
static void SafeDeleteTList(TList *&lst)
Safely delete a TList and all its contents, bypassing ROOT's GarbageCollect.
Definition: NUtils.cxx:1876
static void PrintPointSafe(const std::vector< int > &coords, int index=-1)
Print coordinates safely.
Definition: NUtils.cxx:1485
static TCanvas * CreateCanvas(const std::string &name, const std::string &title, int width=800, int height=600)
Create a ROOT TCanvas with specified name, title, and dimensions.
Definition: NUtils.cxx:1603
static THnSparse * ReshapeSparseAxes(THnSparse *hns, std::vector< int > order, std::vector< TAxis * > newAxes={}, std::vector< int > newPoint={}, Option_t *option="E")
Reshape axes of THnSparse.
Definition: NUtils.cxx:449
static json GetNetDevStats()
Get system-wide network interface totals (RX/TX bytes) in a cross-platform way. On Linux reads /proc/...
Definition: NUtils.cxx:1987
static bool AccessPathName(std::string path)
Check if a path is accessible.
Definition: NUtils.cxx:112
static std::vector< int > TokenizeInt(std::string_view input, const char delim)
Tokenize a string into integers by delimiter.
Definition: NUtils.cxx:950
static THnSparse * Convert(TH1 *h1, std::vector< std::string > names={}, std::vector< std::string > titles={})
Convert TH1 to THnSparse.
Definition: NUtils.cxx:218
static TMacro * OpenMacro(std::string filename)
Open a macro file.
Definition: NUtils.cxx:770
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 TAxis * CreateAxisFromLabels(const std::string &name, const std::string &title, const std::vector< std::string > &labels)
Create a TAxis from a list of labels.
Definition: NUtils.cxx:182
static bool GetAxisRangeInBase(TAxis *a, int rebin, int rebin_start, int bin, int &min, int &max)
Get axis range in base for rebinned axis.
Definition: NUtils.cxx:1193
static json GetSystemStats()
Get process CPU and RSS memory statistics using ROOT's gSystem::GetProcInfo.
Definition: NUtils.cxx:1912
static std::set< std::string > Unique(std::vector< std::string > &paths, int axis, std::string path, char token='/')
Get unique values from vector of strings at specified axis.
Definition: NUtils.cxx:1007
static std::vector< std::string > GetJsonStringArray(json j)
Get JSON value as array of strings.
Definition: NUtils.cxx:1391
static std::string GetJsonString(json j)
Get JSON value as string.
Definition: NUtils.cxx:1297
static bool EnableMT(Int_t numthreads=-1)
Enable multi-threading with specified number of threads.
Definition: NUtils.cxx:43
static int GetJsonInt(json j)
Get JSON value as integer.
Definition: NUtils.cxx:1322
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 void SafeDeleteObjects(std::vector< TObject * > &objects)
Safely delete a vector of ROOT objects, bypassing GarbageCollect.
Definition: NUtils.cxx:1808
static THnSparse * CreateSparseFromParquetTaxi(const std::string &filename, THnSparse *hns=nullptr, Int_t nMaxRows=-1)
Create THnSparse from Parquet Taxi file.
Definition: NUtils.cxx:1800
static void SafeDeleteObject(TObject *&obj)
Safely delete a TObject, handling TList contents and TCanvas/TPad cleanup.
Definition: NUtils.cxx:1898
static void VectorToArray(std::vector< int > v1, Int_t *v2)
Convert vector to array.
Definition: NUtils.cxx:1419
static std::vector< int > ArrayToVector(Int_t *v1, int size)
Convert array to vector.
Definition: NUtils.cxx:1406
static double GetJsonDouble(json j)
Get JSON value as double.
Definition: NUtils.cxx:1345
static bool GetJsonBool(json j)
Get JSON value as boolean.
Definition: NUtils.cxx:1368
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
static std::vector< std::vector< int > > Permutations(const std::vector< int > &v)
Generate all permutations of a vector.
Definition: NUtils.cxx:1494
static TAxis * CreateAxisFromLabelsSet(const std::string &name, const std::string &title, const std::set< std::string > &labels)
Create a TAxis from a set of labels.
Definition: NUtils.cxx:199