21#elif defined(__APPLE__)
25#include <sys/socket.h>
28#include "NHttpRequest.h"
32#include <arrow/io/api.h>
33#include <parquet/arrow/reader.h>
34#include <parquet/exception.h>
52 bool previouslyEnabled = ROOT::IsImplicitMTEnabled();
54 if (ROOT::IsImplicitMTEnabled()) {
55 ROOT::DisableImplicitMT();
64 if (numthreads == -1) {
66 const char * nThreadsEnv = gSystem->Getenv(
"ROOT_MAX_THREADS");
69 numthreads = std::stoul(nThreadsEnv);
71 catch (
const std::exception & e) {
72 NLogError(
"Error parsing ROOT_MAX_THREADS: %s !!! Setting it to '1' ...", e.what());
82 ROOT::EnableThreadSafety();
86 ROOT::EnableImplicitMT(numthreads);
90 if (ROOT::IsImplicitMTEnabled()) {
91 NLogInfo(
"ROOT::ImplicitMT is enabled with number of threads: %d", ROOT::GetThreadPoolSize());
94 return previouslyEnabled;
103 if (filename.find(
"http://") == 0 || filename.find(
"https://") == 0 || filename.find(
"root://") == 0 ||
104 filename.find(
"file://") == 0 || filename.find(
"alien://") == 0) {
107 TString fn(filename.c_str());
108 if (fn.BeginsWith(
"/") || !fn.Contains(
"://")) {
111 NLogError(
"NUtils::IsFileSupported: File '%s' not found", filename.c_str());
120 TString pathStr(gSystem->ExpandPathName(path.c_str()));
122 if (pathStr.BeginsWith(
"http://") || pathStr.BeginsWith(
"https://")) {
127 int http_code = request.
head(pathStr.Data());
128 if (http_code == 200) {
134 else if (pathStr.BeginsWith(
"file://") || pathStr.BeginsWith(
"/") || !pathStr.Contains(
"://")) {
136 return gSystem->AccessPathName(pathStr.Data()) ==
false;
138 else if (pathStr.BeginsWith(
"root://") || pathStr.BeginsWith(
"alien://")) {
140 if (!pathStr.EndsWith(
".root")) {
142 pathStr +=
"?filetype=raw";
144 NLogDebug(
"NUtils::AccessPathName: Trying to open file '%s' ...", pathStr.Data());
145 TFile * f = TFile::Open(pathStr.Data());
146 if (f && !f->IsZombie()) {
155int NUtils::Cp(std::string source, std::string destination, Bool_t progressbar )
162 if (source.empty()) {
163 NLogError(
"NUtils::Cp: Source file is empty");
166 if (destination.empty()) {
167 NLogError(
"NUtils::Cp: Destination file is empty");
172 NLogError(
"NUtils::Cp: Source file '%s' is not supported", source.c_str());
176 NLogError(
"NUtils::Cp: Destination file '%s' is not supported", destination.c_str());
180 NLogInfo(
"Copying file from '%s' to '%s' ...", source.c_str(), destination.c_str());
181 rc = TFile::Cp(source.c_str(), destination.c_str(), progressbar);
186 const std::vector<std::string> & labels)
191 int nBins = labels.size();
192 TAxis * a =
new TAxis(nBins, 0, nBins);
193 a->SetName(name.c_str());
194 a->SetTitle(title.c_str());
195 for (
int i = 0; i < nBins; i++) {
196 NLogTrace(
"NUtils::CreateAxisFromLabels: Adding label: %s", labels[i].c_str());
197 a->SetBinLabel(i + 1, labels[i].c_str());
203 const std::set<std::string> & labels)
208 int nBins = labels.size();
209 TAxis * a =
new TAxis(nBins, 0, nBins);
210 a->SetName(name.c_str());
211 a->SetTitle(title.c_str());
213 for (
const auto & label : labels) {
214 NLogTrace(
"NUtils::CreateAxisFromLabels: Adding label: %s", label.c_str());
215 a->SetBinLabel(i, label.c_str());
221THnSparse *
NUtils::Convert(TH1 * h1, std::vector<std::string> names, std::vector<std::string> titles)
228 NLogError(
"TH1 h1 is null");
232 NLogInfo(
"Converting TH1 '%s' to THnSparse ...", h1->GetName());
238 auto bins = std::make_unique<Int_t[]>(nDims);
239 auto xmin = std::make_unique<Double_t[]>(nDims);
240 auto xmax = std::make_unique<Double_t[]>(nDims);
242 TAxis * aIn = h1->GetXaxis();
243 bins[0] = aIn->GetNbins();
244 xmin[0] = aIn->GetXmin();
245 xmax[0] = aIn->GetXmax();
247 THnSparse * hns =
new THnSparseD(h1->GetName(), h1->GetTitle(), nDims, bins.get(), xmin.get(), xmax.get());
250 for (
int i = 0; i < nDims; i++) {
251 TAxis * a = hns->GetAxis(i);
252 TAxis * aIn = h1->GetXaxis();
253 a->SetName(aIn->GetName());
254 a->SetTitle(aIn->GetTitle());
255 if (aIn->GetXbins()->GetSize() > 0) {
257 auto arr = std::make_unique<Double_t[]>(aIn->GetNbins() + 1);
258 arr[0] = aIn->GetBinLowEdge(1);
259 for (
int iBin = 1; iBin <= aIn->GetNbins(); iBin++) {
260 arr[iBin] = aIn->GetBinUpEdge(iBin);
262 a->Set(a->GetNbins(), arr.get());
266 for (
int i = 0; i < nDims; i++) {
267 if (!names[i].empty()) hns->GetAxis(i)->SetName(names[i].c_str());
268 if (!titles[i].empty()) hns->GetAxis(i)->SetTitle(titles[i].c_str());
272 for (Int_t i = 0; i <= h1->GetNbinsX() + 1; i++) {
273 double content = h1->GetBinContent(i);
275 hns->SetBinContent(p, content);
278 hns->SetEntries(h1->GetEntries());
279 if (h1->GetSumw2N() > 0) {
286THnSparse *
NUtils::Convert(TH2 * h2, std::vector<std::string> names, std::vector<std::string> titles)
292 NLogError(
"TH2 h2 is null");
295 NLogInfo(
"Converting TH2 '%s' to THnSparse ...", h2->GetName());
297 auto bins = std::make_unique<Int_t[]>(nDims);
298 auto xmin = std::make_unique<Double_t[]>(nDims);
299 auto xmax = std::make_unique<Double_t[]>(nDims);
301 for (
int i = 0; i < nDims; i++) {
302 TAxis * aIn =
nullptr;
304 aIn = h2->GetXaxis();
306 aIn = h2->GetYaxis();
308 NLogError(
"Invalid axis index %d", i);
311 bins[i] = aIn->GetNbins();
312 xmin[i] = aIn->GetXmin();
313 xmax[i] = aIn->GetXmax();
316 THnSparse * hns =
new THnSparseD(h2->GetName(), h2->GetTitle(), nDims, bins.get(), xmin.get(), xmax.get());
318 for (Int_t i = 0; i < nDims; i++) {
319 TAxis * a = hns->GetAxis(i);
320 TAxis * aIn =
nullptr;
322 aIn = h2->GetXaxis();
324 aIn = h2->GetYaxis();
326 NLogError(
"Invalid axis index %d", i);
330 a->SetName(aIn->GetName());
331 a->SetTitle(aIn->GetTitle());
332 if (aIn->GetXbins()->GetSize() > 0) {
333 auto arr = std::make_unique<Double_t[]>(aIn->GetNbins() + 1);
334 arr[0] = aIn->GetBinLowEdge(1);
335 for (
int iBin = 1; iBin <= aIn->GetNbins(); iBin++) {
336 arr[iBin] = aIn->GetBinUpEdge(iBin);
338 a->Set(a->GetNbins(), arr.get());
342 for (Int_t i = 0; i < nDims; i++) {
343 if (!names[i].empty()) hns->GetAxis(i)->SetName(names[i].c_str());
344 if (!titles[i].empty()) hns->GetAxis(i)->SetTitle(titles[i].c_str());
348 for (Int_t i = 0; i <= h2->GetNbinsX() + 1; i++) {
349 for (Int_t j = 0; j <= h2->GetNbinsY() + 1; j++) {
350 double content = h2->GetBinContent(i, j);
352 hns->SetBinContent(p, content);
356 hns->SetEntries(h2->GetEntries());
357 if (h2->GetSumw2N() > 0) {
364THnSparse *
NUtils::Convert(TH3 * h3, std::vector<std::string> names, std::vector<std::string> titles)
371 NLogError(
"TH3 h3 is null");
375 NLogInfo(
"Converting TH3 '%s' to THnSparse ...", h3->GetName());
378 auto bins = std::make_unique<Int_t[]>(nDims);
379 auto xmin = std::make_unique<Double_t[]>(nDims);
380 auto xmax = std::make_unique<Double_t[]>(nDims);
382 for (
int i = 0; i < nDims; i++) {
383 TAxis * aIn =
nullptr;
385 aIn = h3->GetXaxis();
387 aIn = h3->GetYaxis();
389 aIn = h3->GetZaxis();
391 NLogError(
"Invalid axis index %d", i);
394 bins[i] = aIn->GetNbins();
395 xmin[i] = aIn->GetXmin();
396 xmax[i] = aIn->GetXmax();
399 THnSparse * hns =
new THnSparseD(h3->GetName(), h3->GetTitle(), nDims, bins.get(), xmin.get(), xmax.get());
402 for (
int i = 0; i < nDims; i++) {
403 TAxis * a = hns->GetAxis(i);
404 TAxis * aIn =
nullptr;
406 aIn = h3->GetXaxis();
408 aIn = h3->GetYaxis();
410 aIn = h3->GetZaxis();
412 NLogError(
"Invalid axis index %d", i);
416 a->SetName(aIn->GetName());
417 a->SetTitle(aIn->GetTitle());
418 if (aIn->GetXbins()->GetSize() > 0) {
419 auto arr = std::make_unique<Double_t[]>(aIn->GetNbins() + 1);
420 arr[0] = aIn->GetBinLowEdge(1);
421 for (
int iBin = 1; iBin <= aIn->GetNbins(); iBin++) {
422 arr[iBin] = aIn->GetBinUpEdge(iBin);
424 a->Set(a->GetNbins(), arr.get());
428 for (Int_t i = 0; i < nDims; i++) {
429 if (!names[i].empty()) hns->GetAxis(i)->SetName(names[i].c_str());
430 if (!titles[i].empty()) hns->GetAxis(i)->SetTitle(titles[i].c_str());
434 for (Int_t i = 0; i <= h3->GetNbinsX() + 1; i++) {
435 for (Int_t j = 0; j <= h3->GetNbinsY() + 1; j++) {
436 for (Int_t k = 0; k <= h3->GetNbinsZ() + 1; k++) {
437 double content = h3->GetBinContent(i, j, k);
438 Int_t p[3] = {i, j, k};
439 hns->SetBinContent(p, content);
444 hns->SetEntries(h3->GetEntries());
445 if (h3->GetSumw2N() > 0) {
453 std::vector<int> newPoint, Option_t * option)
461 if (hns ==
nullptr) {
462 NLogError(
"NUtils::ReshapeSparseAxes: THnSparse hns is null");
467 NLogTrace(
"NUtils::ReshapeSparseAxes: Order vector is empty");
468 for (
long unsigned int i = 0; i < hns->GetNdimensions() + newAxes.size(); i++) {
469 NLogTrace(
"NUtils::ReshapeSparseAxes: Adding axis %d to order", i);
474 if (order.size() != hns->GetNdimensions() + newAxes.size()) {
475 NLogError(
"NUtils::ReshapeSparseAxes: Invalid size %d [order] != %d [hns->GetNdimensions()+newAxes]", order.size(),
476 hns->GetNdimensions() + newAxes.size());
480 if (newPoint.empty()) {
483 if (newAxes.size() != newPoint.size()) {
484 NLogError(
"NUtils::ReshapeSparseAxes: Invalid size %d [newAxes] != %d [newPoint]", newAxes.size(),
490 for (
size_t i = 0; i < order.size(); i++) {
491 if (order[i] < 0 || order[i] >= hns->GetNdimensions() + (
int)newAxes.size()) {
492 NLogError(
"NUtils::ReshapeSparseAxes: Invalid order[%d]=%d. Value is negative or higher then "
493 "'hns->GetNdimensions() + newAxes.size()' !!!",
500 for (
size_t i = 0; i < order.size(); i++) {
501 for (
size_t j = i + 1; j < order.size(); j++) {
502 if (order[i] == order[j]) {
503 NLogError(
"NUtils::ReshapeSparseAxes: Invalid order[%d]=%d and order[%d]=%d. Value is not unique !!!", i,
504 order[i], j, order[j]);
514 NLogTrace(
"NUtils::ReshapeSparseAxes: Reshaping sparse axes ...");
516 int nDims = hns->GetNdimensions() + newAxes.size();
517 auto bins = std::make_unique<Int_t[]>(nDims);
518 auto xmin = std::make_unique<Double_t[]>(nDims);
519 auto xmax = std::make_unique<Double_t[]>(nDims);
521 int newAxesIndex = 0;
522 for (
int i = 0; i < nDims; i++) {
525 if (id < hns->GetNdimensions()) {
526 a = hns->GetAxis(
id);
527 NLogTrace(
"NUtils::ReshapeSparseAxes: [ORIG] Axis [%d]->[%d]: %s %s %d %.2f %.2f",
id, i, a->GetName(),
528 a->GetTitle(), a->GetNbins(), a->GetXmin(), a->GetXmax());
531 newAxesIndex =
id - hns->GetNdimensions();
532 a = newAxes[newAxesIndex];
533 NLogTrace(
"NUtils::ReshapeSparseAxes: [NEW ] Axis [%d]->[%d]: %s %s %d %.2f %.2f",
id, i, a->GetName(),
534 a->GetTitle(), a->GetNbins(), a->GetXmin(), a->GetXmax());
536 bins[i] = a->GetNbins();
537 xmin[i] = a->GetXmin();
538 xmax[i] = a->GetXmax();
541 THnSparse * hnsNew =
new THnSparseD(hns->GetName(), hns->GetTitle(), nDims, bins.get(), xmin.get(), xmax.get());
544 for (
int i = 0; i < hnsNew->GetNdimensions(); i++) {
545 TAxis * aIn =
nullptr;
546 if (order[i] < hns->GetNdimensions()) {
547 aIn = hns->GetAxis(order[i]);
550 newAxesIndex = order[i] - hns->GetNdimensions();
551 aIn = newAxes[newAxesIndex];
554 TAxis * a = hnsNew->GetAxis(i);
555 a->SetName(aIn->GetName());
556 a->SetTitle(aIn->GetTitle());
557 if (aIn->GetXbins()->GetSize() > 0) {
558 auto arr = std::make_unique<Double_t[]>(aIn->GetNbins() + 1);
559 arr[0] = aIn->GetBinLowEdge(1);
560 for (
int iBin = 1; iBin <= aIn->GetNbins(); iBin++) {
561 arr[iBin] = aIn->GetBinUpEdge(iBin);
563 a->Set(a->GetNbins(), arr.get());
567 if (aIn->IsAlphanumeric()) {
568 for (
int j = 1; j <= aIn->GetNbins(); j++) {
569 const char * label = aIn->GetBinLabel(j);
570 a->SetBinLabel(j, label);
575 if (newPoint.empty()) {
576 NLogTrace(
"NUtils::ReshapeSparseAxes: New point is empty, filling is skipped and doing reset ...");
582 if (hns->GetNbins() > 0) {
584 NLogTrace(
"NUtils::ReshapeSparseAxes: Filling all bins ...");
585 for (Long64_t i = 0; i < hns->GetNbins(); i++) {
586 auto p = std::make_unique<Int_t[]>(nDims);
587 auto pNew = std::make_unique<Int_t[]>(nDims);
588 hns->GetBinContent(i, p.get());
589 Double_t v = hns->GetBinContent(i);
591 for (
int j = 0; j < nDims; j++) {
593 if (id < hns->GetNdimensions()) {
597 newAxesIndex =
id - hns->GetNdimensions();
598 pNew[j] = newPoint[newAxesIndex];
601 hnsNew->SetBinContent(pNew.get(), v);
603 hnsNew->SetEntries(hns->GetEntries());
606 if (opt.Contains(
"E")) {
607 NLogTrace(
"ReshapeSparseAxes: Calculating sumw2 ...");
610 NLogTrace(
"ReshapeSparseAxes: Reshaped sparse axes:");
612 for (
int i = 0; i < nDims; i++) {
613 TAxis * a = hnsNew->GetAxis(i);
614 NLogTrace(
"ReshapeSparseAxes: Axis %d: %s %s %d %.2f %.2f", i, a->GetName(), a->GetTitle(), a->GetNbins(),
615 a->GetXmin(), a->GetXmax());
632 max_val = -std::numeric_limits<double>::max();
633 min_val = std::numeric_limits<double>::max();
635 int first_bin_x = include_overflow_underflow ? 0 : 1;
636 int last_bin_x = include_overflow_underflow ? h->GetNbinsX() + 1 : h->GetNbinsX();
638 int first_bin_y = include_overflow_underflow ? 0 : 1;
639 int last_bin_y = include_overflow_underflow ? h->GetNbinsY() + 1 : h->GetNbinsY();
641 int first_bin_z = include_overflow_underflow ? 0 : 1;
642 int last_bin_z = include_overflow_underflow ? h->GetNbinsZ() + 1 : h->GetNbinsZ();
645 if (h->GetDimension() == 1) {
646 for (
int i = first_bin_x; i <= last_bin_x; ++i) {
647 double content = h->GetBinContent(i);
648 if (content > max_val) max_val = content;
649 if (content < min_val) min_val = content;
652 else if (h->GetDimension() == 2) {
653 for (
int i = first_bin_x; i <= last_bin_x; ++i) {
654 for (
int j = first_bin_y; j <= last_bin_y; ++j) {
655 double content = h->GetBinContent(i, j);
656 if (content > max_val) max_val = content;
657 if (content < min_val) min_val = content;
661 else if (h->GetDimension() == 3) {
662 for (
int i = first_bin_x; i <= last_bin_x; ++i) {
663 for (
int j = first_bin_y; j <= last_bin_y; ++j) {
664 for (
int k = first_bin_z; k <= last_bin_z; ++k) {
665 double content = h->GetBinContent(i, j, k);
666 if (content > max_val) max_val = content;
667 if (content < min_val) min_val = content;
673 NLogWarning(
"GetTrueHistogramMinMax: Histogram '%s' has unsupported dimension %d. "
674 "Using GetMaximum/GetMinimum as fallback.",
675 h->GetName(), h->GetDimension());
677 max_val = h->GetMaximum();
678 min_val = h->GetMinimum();
682 if (max_val == -std::numeric_limits<double>::max() && min_val == std::numeric_limits<double>::max()) {
696 if (path.empty())
return false;
698 TString dir(path.c_str());
699 bool isLocalFile = dir.BeginsWith(
"file://");
701 dir.ReplaceAll(
"file://",
"");
703 isLocalFile = !dir.Contains(
"://");
706 if (!isLocalFile)
return true;
708 std::string pwd = gSystem->pwd();
709 if (dir[0] !=
'/') dir = (pwd +
"/" + std::string(dir.Data())).c_str();
710 dir.ReplaceAll(
"?remote=1&",
"?");
711 dir.ReplaceAll(
"?remote=1",
"");
712 dir.ReplaceAll(
"&remote=1",
"");
713 TUrl url(dir.Data());
715 const std::string localDir = url.GetFile();
716 return gSystem->mkdir(localDir.c_str(), kTRUE) == 0;
725 filename = gSystem->ExpandPathName(filename.c_str());
726 if (createLocalDir) {
727 if (!mode.compare(
"RECREATE") || !mode.compare(
"UPDATE") || !mode.compare(
"WRITE")) {
728 const std::string dir = gSystem->GetDirName(filename.c_str()).Data();
732 return TFile::Open(filename.c_str(), mode.c_str());
742 TFile * f =
OpenFile(TString::Format(
"%s?filetype=raw", filename.c_str()).Data());
749 auto buff = std::make_unique<char[]>(buffsize + 1);
752 Long64_t buffread = 0;
753 while (buffread < f->GetSize()) {
754 if (buffread + buffsize > f->GetSize()) buffsize = f->GetSize() - buffread;
757 f->ReadBuffer(buff.get(), buffread, buffsize);
758 buff[buffsize] =
'\0';
759 content += buff.get();
760 buffread += buffsize;
771 TFile * f =
OpenFile(TString::Format(
"%s?filetype=raw", filename.c_str()).Data(),
"RECREATE");
773 NLogError(
"Error: Problem opening file '%s' in 'rw' mode ...", filename.c_str());
776 f->WriteBuffer(content.c_str(), content.size());
788 if (filename.find(
"http://") == 0 || filename.find(
"https://") == 0) {
790 content = request.
get(filename);
791 if (content.empty()) {
792 Printf(
"Error: Problem fetching macro from '%s' ...", filename.c_str());
798 TString expanded(filename.c_str());
799 gSystem->ExpandPathName(expanded);
800 filename = expanded.Data();
802 if (content.empty()) {
803 Printf(
"Error: Problem opening macro '%s' ...", filename.c_str());
807 Printf(
"Using macro '%s' ...", filename.c_str());
808 TUrl url(filename.c_str());
809 std::string basefilename = gSystem->BaseName(url.GetFile());
810 basefilename.pop_back();
811 basefilename.pop_back();
812 TMacro * m =
new TMacro();
813 m->SetName(basefilename.c_str());
814 m->AddLine(content.c_str());
825 if (content.empty()) {
826 NLogError(
"NUtils::LoadJsonFile: Problem opening JSON file '%s' ...", filename.c_str());
831 json myCfg = json::parse(content.c_str());
832 cfg.merge_patch(myCfg);
833 NLogInfo(
"NUtils::LoadJsonFile: Successfully parsed JSON file '%s' ...", filename.c_str());
835 catch (json::parse_error & e) {
836 NLogError(
"NUtils::LoadJsonFile: JSON parse error in file '%s' at byte %d: %s", filename.c_str(), e.byte, e.what());
845 const std::string kPlaceholderBase =
"##RAW_JSON_INJECT_";
848 for (
size_t i = 0; i < injections.size(); ++i) {
849 const auto & keys = injections[i].first;
851 throw std::invalid_argument(
"Keys array must not be empty at injection index " + std::to_string(i));
855 for (
size_t k = 0; k < keys.size() - 1; ++k) {
856 if (!current->contains(keys[k])) {
857 (*current)[keys[k]] = json::object();
859 current = &(*current)[keys[k]];
861 (*current)[keys.back()] = kPlaceholderBase + std::to_string(i) +
"##";
864 std::string result = j.dump();
868 for (
size_t i = 0; i < injections.size(); ++i) {
869 const std::string quotedPlaceholder =
"\"" + kPlaceholderBase + std::to_string(i) +
"##\"";
871 size_t pos = result.find(quotedPlaceholder);
872 if (pos == std::string::npos) {
873 throw std::runtime_error(
"Placeholder not found for key path ending in \"" + injections[i].first.back() +
"\"");
876 while (pos != std::string::npos) {
877 result.replace(pos, quotedPlaceholder.length(), injections[i].second);
878 pos = result.find(quotedPlaceholder, pos + injections[i].second.size());
886 const std::string & injectionsKey)
889 throw std::invalid_argument(
"AddRawJsonInjection: path must not be empty");
895 bool pathReachable =
true;
896 for (
size_t k = 0; k + 1 < path.size(); ++k) {
897 if (!current->is_object() || !current->contains(path[k])) {
898 pathReachable =
false;
901 current = &(*current)[path[k]];
904 std::string valueToStore = rawJson;
905 if (pathReachable && current->is_object() && current->contains(path.back())) {
906 const json & existing = (*current)[path.back()];
907 if (existing.is_object() && !existing.empty()) {
910 size_t lastBrace = valueToStore.rfind(
'}');
911 if (lastBrace != std::string::npos) {
912 std::string extras = existing.dump();
913 std::string extraFields = extras.substr(1, extras.size() - 2);
914 if (!extraFields.empty()) {
915 valueToStore = valueToStore.substr(0, lastBrace) +
"," + extraFields +
"}";
921 if (!j.contains(injectionsKey) || !j[injectionsKey].is_array()) {
922 j[injectionsKey] = json::array();
925 j[injectionsKey].push_back({{
"path", path}, {
"value", valueToStore}});
931 if (!j.contains(injectionsKey) || !j[injectionsKey].is_array()) {
935 for (
const auto & entry : j[injectionsKey]) {
936 if (!entry.contains(
"path") || !entry[
"path"].is_array() || !entry.contains(
"value") || !entry[
"value"].is_string()) {
939 injections.emplace_back(entry[
"path"].get<std::vector<std::string>>(), entry[
"value"].get<std::string>());
942 return !injections.empty();
952 json obj = json::parse(rawJson);
955 for (
const auto & [key, val] : metadata.items()) {
961 catch (
const std::exception & e) {
962 NLogError(
"NUtils::MergeRawJsonWithMetadata: Failed to parse raw JSON: %s", e.what());
967std::vector<std::string>
NUtils::Find(std::string path, std::string filename)
973 std::vector<std::string> files;
974 TString pathStr = gSystem->ExpandPathName(path.c_str());
975 if (pathStr.IsNull() || filename.empty()) {
976 NLogError(
"NUtils::Find: Path or filename is empty");
980 if (pathStr.BeginsWith(
"root://")) {
981 return FindEos(path, filename);
996 std::vector<std::string> files;
997 if (gSystem->AccessPathName(path.c_str())) {
998 NLogError(
"NUtils::FindLocal: Path '%s' does not exist", path.c_str());
1001 NLogInfo(
"Doing find %s -name %s", path.c_str(), filename.c_str());
1002 std::string linesMerge =
1003 gSystem->GetFromPipe(TString::Format(
"find %s -name %s", path.c_str(), filename.c_str())).Data();
1005 std::stringstream check2(linesMerge);
1007 while (std::getline(check2, line)) {
1008 files.push_back(line);
1018 std::vector<std::string> files;
1019 NLogInfo(
"Doing eos find -f --name %s %s ", filename.c_str(), path.c_str());
1021 TUrl url(path.c_str());
1022 std::string host = url.GetHost();
1023 std::string directory = url.GetFile();
1024 std::string findUrl =
"root://";
1025 findUrl += host +
"//proc/user/";
1026 findUrl +=
"?mgm.cmd=find&mgm.find.match=" + filename;
1027 findUrl +=
"&mgm.path=" + directory;
1028 findUrl +=
"&mgm.format=json&mgm.option=f&filetype=raw";
1029 NLogInfo(
"Doing TFile::Open on '%s' ...", findUrl.c_str());
1032 if (!f)
return files;
1036 int buffsize = 4096;
1038 auto buff = std::make_unique<char[]>(buffsize + 1);
1041 Long64_t buffread = 0;
1042 std::string content;
1043 while (buffread < f->GetSize()) {
1045 if (buffread + buffsize > f->GetSize()) buffsize = f->GetSize() - buffread;
1048 f->ReadBuffer(buff.get(), buffread, buffsize);
1049 buff[buffsize] =
'\0';
1050 content += buff.get();
1051 buffread += buffsize;
1056 std::string ss =
"mgm.proc.stdout=";
1057 size_t pos = ss.size() + 1;
1058 content = content.substr(pos);
1061 std::stringstream check1(content);
1063 std::string intermediate;
1066 std::vector<std::string> tokens;
1067 while (getline(check1, intermediate,
'&')) {
1068 tokens.push_back(intermediate);
1070 std::string linesString = tokens[0];
1072 files.push_back(
"root://" + host +
"/" + line);
1082 std::vector<std::string> out;
1084 size_t end = input.find(delim);
1086 while (end != std::string_view::npos) {
1088 out.emplace_back(input.substr(start, end - start));
1091 end = input.find(delim, start);
1094 if (start < input.length()) {
1095 out.emplace_back(input.substr(start));
1105 std::vector<int> out;
1106 std::vector<std::string> tokens =
Tokenize(input, delim);
1107 for (
auto & t : tokens) {
1108 if (t.empty())
continue;
1109 out.push_back(std::stoi(t));
1115std::string
NUtils::Join(
const std::vector<std::string> & values,
const char delim)
1122 for (
const auto & v : values) {
1123 if (!out.empty()) out += delim;
1135 for (
const auto & v : values) {
1136 if (!out.empty()) out += delim;
1137 out += std::to_string(v);
1148 std::vector<std::string> out;
1149 for (
auto & v : values) {
1150 v = std::string(v.begin() + value.size(), v.end());
1156std::set<std::string>
NUtils::Unique(std::vector<std::string> & paths,
int axis, std::string path,
char token)
1162 std::set<std::string> out;
1164 for (
auto & p : truncatedPaths) {
1165 std::vector<std::string> tokens =
Tokenize(p, token);
1166 out.insert(tokens[axis]);
1176 if (sparse ==
nullptr) {
1177 NLogError(
"Error: Sparse is nullptr ...");
1182 if (axes.size() == 1) {
1183 h = sparse->Projection(axes[0], option);
1185 else if (axes.size() == 2) {
1186 h = sparse->Projection(axes[1], axes[0], option);
1188 else if (axes.size() == 3) {
1189 h = sparse->Projection(axes[0], axes[1], axes[2], option);
1192 NLogError(
"Error: Only projection onto single axis is supported for TH1 ...");
1195 h->SetName(TString::Format(
"%s_proj", sparse->GetName()).Data());
1196 h->SetTitle(TString::Format(
"%s Projection", sparse->GetTitle()).Data());
1200 h->SetDirectory(
nullptr);
1203 for (
size_t i = 0; i < axes.size(); i++) {
1204 TAxis * axisSparse = sparse->GetAxis(axes[i]);
1205 TAxis * axisHist = h->GetXaxis();
1206 if (i == 1) axisHist = h->GetYaxis();
1207 if (i == 2) axisHist = h->GetZaxis();
1209 axisHist->SetName(axisSparse->GetName());
1210 axisHist->SetTitle(axisSparse->GetTitle());
1213 if (axisSparse->IsAlphanumeric()) {
1214 for (
int j = 1; j <= axisSparse->GetNbins(); j++) {
1215 const char * label = axisSparse->GetBinLabel(j);
1216 axisHist->SetBinLabel(j, label);
1225 bool modifyTitle,
bool reset)
1232 if (sparse ==
nullptr) {
1233 NLogError(
"Error: Sparse is nullptr ...");
1236 if (sparse->GetNdimensions() == 0)
return true;
1239 NLogTrace(
"Setting axis ranges on '%s' THnSparse ...", sparse->GetName());
1241 for (
int i = 0; i < sparse->GetNdimensions(); i++) {
1243 NLogTrace(
"Resetting '%s' axis ...", sparse->GetAxis(i)->GetName());
1244 sparse->GetAxis(i)->SetRange(0, 0);
1247 NLogTrace(
"Resetting '%s' axis [%d,%d] ...", sparse->GetAxis(i)->GetName(), 1, sparse->GetAxis(i)->GetNbins());
1248 sparse->GetAxis(i)->SetRange(1, sparse->GetAxis(i)->GetNbins());
1253 if (ranges.empty()) {
1254 NLogTrace(
"No axis ranges to set ...");
1258 TAxis * axis =
nullptr;
1259 TString title = sparse->GetTitle();
1260 if (modifyTitle) title +=
" Ranges:";
1261 for (
size_t i = 0; i < ranges.size(); i++) {
1262 axis = sparse->GetAxis(ranges[i][0]);
1263 NLogTrace(
"Setting axis range %s=[%d,%d] ...", axis->GetName(), ranges[i][1], ranges[i][2]);
1264 if (ranges[i].size() != 3) {
1265 NLogError(
"Error: Axis range must have 3 values, but has %zu ...", ranges[i].size());
1268 axis->SetRange(ranges[i][1], ranges[i][2]);
1269 if (axis->IsAlphanumeric()) {
1271 title += TString::Format(
" %s[%s]", axis->GetName(), axis->GetBinLabel(ranges[i][1]));
1274 title += TString::Format(
" %s[%0.2f - %0.2f]", axis->GetName(), axis->GetBinLowEdge(ranges[i][1]),
1275 axis->GetBinUpEdge(ranges[i][2]));
1278 if (modifyTitle) sparse->SetTitle(title.Data());
1283 bool modifyTitle,
bool reset)
1290 if (sparse ==
nullptr) {
1291 NLogError(
"NUtils::SetAxisRanges: Sparse is nullptr ...");
1294 if (sparse->GetNdimensions() == 0)
return true;
1296 NLogTrace(
"NUtils::SetAxisRanges: Setting axis ranges on '%s' THnSparse ...", sparse->GetName());
1299 for (
int i = 0; i < sparse->GetNdimensions(); i++) {
1301 NLogTrace(
"NUtils::SetAxisRanges: Resetting '%s' axis ...", sparse->GetAxis(i)->GetName());
1302 sparse->GetAxis(i)->SetRange(0, 0);
1305 NLogTrace(
"NUtils::SetAxisRanges: Resetting '%s' axis [%d,%d] ...", sparse->GetAxis(i)->GetName(), 1,
1306 sparse->GetAxis(i)->GetNbins());
1307 sparse->GetAxis(i)->SetRange(1, sparse->GetAxis(i)->GetNbins());
1312 if (ranges.empty()) {
1313 NLogTrace(
"NUtils::SetAxisRanges: No axis ranges to set ...");
1316 TAxis * axis =
nullptr;
1317 TString title = sparse->GetTitle();
1318 for (
const auto & [key, val] : ranges) {
1319 NLogTrace(
"NUtils::SetAxisRanges: Setting axis range for axis %d to [%d,%d] ...", key, val[0], val[1]);
1320 axis = sparse->GetAxis(key);
1321 if (axis ==
nullptr) {
1322 NLogError(
"NUtils::SetAxisRanges: Axis %d is nullptr ...", key);
1325 NLogTrace(
"NUtils::SetAxisRanges: Setting axis range %s=[%d,%d] ...", axis->GetName(), val[0], val[1]);
1326 axis->SetRange(val[0], val[1]);
1327 if (axis->IsAlphanumeric()) {
1329 title += TString::Format(
" %s[%s]", axis->GetName(), axis->GetBinLabel(val[0]));
1332 title += TString::Format(
" %s[%0.2f - %0.2f]", axis->GetName(), axis->GetBinLowEdge(val[0]),
1333 axis->GetBinUpEdge(val[1]));
1337 if (modifyTitle) sparse->SetTitle(title.Data());
1338 NLogTrace(
"NUtils::SetAxisRanges: New title: %s", sparse->GetTitle());
1348 NLogError(
"Error: Axis is nullptr ...");
1354 NLogTrace(
"Getting axis range in base for '%s' rebin=%d rebin_start=%d bin=%d...", a->GetName(), rebin, rebin_start,
1357 min = rebin * (bin - 1) + rebin_start;
1358 max = min + rebin - 1;
1359 NLogTrace(
"Axis '%s' min=%d max=%d", a->GetName(), min, max);
1362 NLogError(
"Error: Axis '%s' min=%d is lower then 1 ...", a->GetName(), min);
1368 if (max > a->GetNbins()) {
1369 NLogError(
"Error: Axis '%s' max=%d is higher then %d ...", a->GetName(), max, a->GetNbins());
1383 int rebin = base->GetNbins() / a->GetNbins();
1386 int rebin_start = (base->GetNbins() % a->GetNbins()) + 1;
1387 rebin_start = rebin != 1 ? rebin_start : 1;
1389 NLogTrace(
"Getting axis range in base for '%s' min=%d max=%d rebin=%d rebin_start=%d...", a->GetName(), min, max,
1390 rebin, rebin_start);
1395 NLogTrace(
"Axis '%s' minBase=%d maxBase=%d", a->GetName(), minBase, maxBase);
1401 const std::string & fileName,
const std::vector<std::string> & axesNames)
1403 if (paths.empty()) {
1404 NLogError(
"Error: No paths provided ...");
1408 std::map<std::string, std::set<std::string>> axes;
1409 for (
const auto & path : paths) {
1410 NLogInfo(
"Found file: %s", path.c_str());
1412 TString relativePath = path;
1413 relativePath.ReplaceAll(findPath.c_str(),
"");
1414 relativePath.ReplaceAll(fileName.c_str(),
"");
1417 relativePath.ReplaceAll(
"//",
"/");
1419 relativePath.Remove(0, relativePath.BeginsWith(
"/") ? 1 : 0);
1421 relativePath.Remove(relativePath.EndsWith(
"/") ? relativePath.Length() - 1 : relativePath.Length(), 1);
1429 if (tokens.size() != axesNames.size()) {
1433 for (
size_t i = 0; i < tokens.size(); ++i) {
1434 axes[axesNames[i]].insert(tokens[i]);
1438 TObjArray * axesArr =
new TObjArray();
1439 for (
const auto & axisName : axesNames) {
1452 if (j.is_string()) {
1453 return j.get<std::string>();
1455 else if (j.is_number_integer()) {
1456 return std::to_string(j.get<
int>());
1458 else if (j.is_number_float()) {
1459 return std::to_string(j.get<
double>());
1461 else if (j.is_boolean()) {
1462 return j.get<
bool>() ?
"true" :
"false";
1464 else if (j.is_null()) {
1477 if (j.is_number_integer()) {
1478 return j.get<
int>();
1480 else if (j.is_number_float()) {
1481 return static_cast<int>(j.get<
double>());
1483 else if (j.is_boolean()) {
1484 return j.get<
bool>() ? 1 : 0;
1486 else if (j.is_null()) {
1500 if (j.is_number_float()) {
1501 return j.get<
double>();
1503 else if (j.is_number_integer()) {
1504 return static_cast<double>(j.get<
int>());
1506 else if (j.is_boolean()) {
1507 return j.get<
bool>() ? 1.0 : 0.0;
1509 else if (j.is_null()) {
1523 if (j.is_boolean()) {
1524 return j.get<
bool>();
1526 else if (j.is_number_integer()) {
1527 return j.get<
int>() != 0;
1529 else if (j.is_number_float()) {
1530 return j.get<
double>() != 0.0;
1532 else if (j.is_null()) {
1546 std::vector<std::string> out;
1548 for (
auto & v : j) {
1561 std::vector<int> v2;
1562 for (
int i = 0; i < size; i++) {
1563 v2.push_back(v1[i]);
1574 for (
size_t i = 0; i < v1.size(); i++) {
1583 std::stringstream msg;
1584 if (index >= 0) msg <<
"[" << std::setw(3) << std::setfill(
'0') << index <<
"] ";
1586 for (
size_t i = 0; i < coords.size(); ++i) {
1587 msg << std::setw(width) << std::setfill(
' ') << coords[i] << (i == coords.size() - 1 ?
"" :
",");
1597 std::stringstream msg;
1598 if (index >= 0) msg <<
"[" << std::setw(3) << std::setfill(
'0') << index <<
"] ";
1600 for (
size_t i = 0; i < coords.size(); ++i) {
1601 msg << std::setw(width) << std::setfill(
' ') << coords[i] << (i == coords.size() - 1 ?
"" :
",");
1611 std::stringstream msg;
1612 if (index >= 0) msg <<
"[" << std::setw(3) << std::setfill(
'0') << index <<
"] ";
1614 for (
size_t i = 0; i < coords.size(); ++i) {
1615 msg << std::setw(width) << std::setfill(
' ') << coords[i] << (i == coords.size() - 1 ?
"" :
",");
1625 std::stringstream msg;
1626 if (index >= 0) msg <<
"[" << std::setw(3) << std::setfill(
'0') << index <<
"] ";
1628 for (
size_t i = 0; i < coords.size(); ++i) {
1629 msg << std::setw(width) << std::setfill(
' ') << coords[i] << (i == coords.size() - 1 ?
"" :
",");
1648 std::vector<std::vector<int>> result;
1649 std::vector<int> current = v;
1650 std::sort(current.begin(), current.end());
1652 result.push_back(current);
1653 }
while (std::next_permutation(current.begin(), current.end()));
1657 for (
const auto & perm : result) {
1666 long long hours = seconds / 3600;
1668 long long minutes = seconds / 60;
1671 std::stringstream ss;
1672 ss << std::setw(2) << std::setfill(
'0') << hours <<
":" << std::setw(2) << std::setfill(
'0') << minutes <<
":"
1673 << std::setw(2) << std::setfill(
'0') << seconds;
1683 if (total == 0)
return;
1688 float percentage =
static_cast<float>(current) / total;
1689 int numChars =
static_cast<int>(percentage * barWidth);
1692 if (!prefix.empty()) std::cout <<
"[" << prefix <<
"]";
1695 for (
int i = 0; i < numChars; ++i) {
1698 for (
int i = 0; i < barWidth - numChars; ++i) {
1701 std::cout <<
"] " <<
static_cast<int>(percentage * 100.0) <<
"%"
1702 <<
" (" << current <<
"/" << total <<
")";
1703 if (!suffix.empty()) std::cout <<
" [" << suffix <<
"]";
1704 if (current == total) std::cout << std::endl;
1705 std::cout << std::flush;
1709 std::string prefix, std::string suffix,
int barWidth)
1714 if (total == 0)
return;
1716 if (current > total) current = total;
1718 float percentage =
static_cast<float>(current) / total;
1719 int numChars =
static_cast<int>(percentage * barWidth);
1723 if (!prefix.empty()) std::cout << prefix <<
"][";
1724 for (
int i = 0; i < numChars; ++i) {
1727 for (
int i = 0; i < barWidth - numChars; ++i) {
1730 std::cout <<
"] " << std::setw(3) <<
static_cast<int>(percentage * 100.0) <<
"%";
1733 auto currentTime = std::chrono::high_resolution_clock::now();
1734 auto elapsedSeconds = std::chrono::duration_cast<std::chrono::seconds>(currentTime - startTime).count();
1737 long long estimatedRemainingSeconds = 0;
1738 if (current > 0 && percentage > 0) {
1740 long long totalEstimatedSeconds =
static_cast<long long>(elapsedSeconds / percentage);
1741 estimatedRemainingSeconds = totalEstimatedSeconds - elapsedSeconds;
1744 std::cout <<
" (" << current <<
"/" << total <<
") "
1745 <<
"Elapsed: " <<
FormatTime(elapsedSeconds) <<
" "
1746 <<
"ETA: " <<
FormatTime(estimatedRemainingSeconds);
1747 if (!suffix.empty()) std::cout <<
" [" << suffix <<
"]";
1748 if (current == total) std::cout << std::endl;
1749 std::cout << std::flush;
1759 TCanvas * c =
new TCanvas(
"", title.c_str(), width, height);
1760 gROOT->GetListOfCanvases()->Remove(c);
1761 c->ResetBit(kMustCleanup);
1762 c->SetBit(kCanDelete, kFALSE);
1763 c->SetName(name.c_str());
1776 if (hns ==
nullptr) {
1777 NLogError(
"NUtils::CreateSparseFromParquetTaxi: THnSparse 'hns' is nullptr ...");
1781 std::shared_ptr<arrow::io::ReadableFile> infile;
1782 arrow::Result<std::shared_ptr<arrow::io::ReadableFile>> infile_result = arrow::io::ReadableFile::Open(filename);
1783 if (!infile_result.ok()) {
1784 NLogError(
"NUtils::CreateSparseFromParquetTaxi: Error opening file %s: %s", filename.c_str(),
1785 infile_result.status().ToString().c_str());
1788 infile = infile_result.ValueUnsafe();
1791 std::unique_ptr<parquet::arrow::FileReader> reader;
1794 arrow::Result<std::unique_ptr<parquet::arrow::FileReader>> reader_result =
1795 parquet::arrow::OpenFile(infile, arrow::default_memory_pool());
1796 if (!reader_result.ok()) {
1797 NLogError(
"NUtils::CreateSparseFromParquetTaxi: Error opening Parquet file reader for file %s: %s",
1798 filename.c_str(), reader_result.status().ToString().c_str());
1799 arrow::Status status = infile->Close();
1802 reader = std::move(reader_result).ValueUnsafe();
1806 std::shared_ptr<parquet::FileMetaData> file_metadata = reader->parquet_reader()->metadata();
1807 NLogTrace(
"Parquet file '%s' opened successfully.", filename.c_str());
1808 NLogTrace(
"Parquet file version: %d", file_metadata->version());
1809 NLogTrace(
"Parquet created by: %s", file_metadata->created_by().c_str());
1810 NLogTrace(
"Parquet number of columns: %d", file_metadata->num_columns());
1811 NLogTrace(
"Parquet number of rows: %lld", file_metadata->num_rows());
1812 NLogTrace(
"Parquet number of row groups: %d", file_metadata->num_row_groups());
1817 std::shared_ptr<arrow::RecordBatchReader> batch_reader;
1818 arrow::Status status = reader->GetRecordBatchReader(&batch_reader);
1820 NLogError(
"NUtils::CreateSparseFromParquetTaxi: Error reading table from Parquet file %s: %s", filename.c_str(),
1821 status.ToString().c_str());
1822 status = infile->Close();
1827 status = infile->Close();
1829 NLogWarning(
"NUtils::CreateSparseFromParquetTaxi: Error closing input file %s: %s", filename.c_str(),
1830 status.ToString().c_str());
1835 NLogTrace(
"Parquet Table Schema:\n%s", batch_reader->schema()->ToString().c_str());
1837 const Int_t nDims = hns->GetNdimensions();
1838 std::vector<std::string> column_names;
1839 for (
int i = 0; i < nDims; ++i) {
1840 column_names.push_back(hns->GetAxis(i)->GetName());
1846 max_rows = nMaxRows > 0 ? std::min(max_rows, nMaxRows) : max_rows;
1847 int print_rows = std::min(max_rows, 5);
1849 auto table_batch_reader = batch_reader;
1850 std::shared_ptr<arrow::RecordBatch> batch;
1851 auto point = std::make_unique<Double_t[]>(nDims);
1854 if (print_rows > 0) {
1855 NLogTrace(
"Printing first %d rows of Parquet file '%s' ...", print_rows, filename.c_str());
1859 int batch_count = 0;
1860 while (table_batch_reader->ReadNext(&batch).ok() && batch) {
1862 NLogTrace(
"Processing batch with %d rows and %d columns ...", batch->num_rows(), batch->num_columns());
1863 for (
int i = 0; i < batch->num_rows(); ++i) {
1864 if (i >= max_rows)
break;
1866 bool isValid =
true;
1868 for (
int j = 0; j < batch->num_columns(); ++j) {
1869 if (std::find(column_names.begin(), column_names.end(), batch->column_name(j)) == column_names.end())
1874 const auto & array = batch->column(j);
1875 arrow::Result<std::shared_ptr<arrow::Scalar>> scalar_result = array->GetScalar(i);
1876 if (scalar_result.ok()) {
1878 if (scalar_result.ValueUnsafe()->is_valid) {
1879 TAxis * axis = hns->GetAxis(idx);
1880 if (scalar_result.ValueUnsafe()->type->id() == arrow::Type::STRING ||
1881 scalar_result.ValueUnsafe()->type->id() == arrow::Type::LARGE_STRING) {
1884 std::string value = scalar_result.ValueUnsafe()->ToString();
1888 point[idx] = axis->GetBinCenter(axis->FindBin(value.c_str()));
1890 else if (scalar_result.ValueUnsafe()->type->id() == arrow::Type::INT32) {
1891 auto int_scalar = std::static_pointer_cast<arrow::Int32Scalar>(scalar_result.ValueUnsafe());
1893 point[idx] =
static_cast<Double_t
>(int_scalar->value);
1895 else if (scalar_result.ValueUnsafe()->type->id() == arrow::Type::INT64) {
1896 auto int64_scalar = std::static_pointer_cast<arrow::Int64Scalar>(scalar_result.ValueUnsafe());
1897 point[idx] =
static_cast<Double_t
>(int64_scalar->value);
1899 else if (scalar_result.ValueUnsafe()->type->id() == arrow::Type::UINT32) {
1900 auto uint32_scalar = std::static_pointer_cast<arrow::UInt32Scalar>(scalar_result.ValueUnsafe());
1901 point[idx] =
static_cast<Double_t
>(uint32_scalar->value);
1903 else if (scalar_result.ValueUnsafe()->type->id() == arrow::Type::FLOAT) {
1904 auto float_scalar = std::static_pointer_cast<arrow::FloatScalar>(scalar_result.ValueUnsafe());
1905 point[idx] =
static_cast<Double_t
>(float_scalar->value);
1907 else if (scalar_result.ValueUnsafe()->type->id() == arrow::Type::DOUBLE) {
1908 auto double_scalar = std::static_pointer_cast<arrow::DoubleScalar>(scalar_result.ValueUnsafe());
1909 point[idx] = double_scalar->value;
1912 NLogError(
"NUtils::CreateSparseFromParquetTaxi: Unsupported data type for column '%s' ...",
1913 batch->column_name(j).c_str());
1927 NLogError(
"NUtils::CreateSparseFromParquetTaxi: Error getting scalar at (%d,%d): %s", i, j,
1928 scalar_result.status().ToString().c_str());
1939 hns->Fill(point.get());
1942 NLogWarning(
"Skipping row %d due to invalid data.", i);
1952 NLogError(
"Parquet support is not enabled. Please compile with Parquet support.");
1959 if (objects.empty())
return;
1971 Bool_t prevMustClean = gROOT->MustClean();
1972 gROOT->SetMustClean(kFALSE);
1975 std::vector<TPad *> pads;
1976 for (
auto * obj : objects) {
1977 if (obj && obj->InheritsFrom(TPad::Class())) pads.push_back(
static_cast<TPad *
>(obj));
1979 for (
size_t i = 0; i < pads.size(); ++i) {
1980 TList * prims = pads[i]->GetListOfPrimitives();
1981 if (!prims || prims->IsEmpty())
continue;
1982 for (TObjLink * lnk = prims->FirstLink(); lnk; lnk = lnk->Next()) {
1983 TObject * child = lnk->GetObject();
1984 if (child && child->InheritsFrom(TPad::Class())) pads.push_back(
static_cast<TPad *
>(child));
1989 std::set<TObject *> inputSet(objects.begin(), objects.end());
1990 inputSet.erase(
nullptr);
1993 std::set<TObject *> orphans;
1994 for (
auto it = pads.rbegin(); it != pads.rend(); ++it) {
1995 TList * prims = (*it)->GetListOfPrimitives();
1996 if (!prims)
continue;
1998 for (TObjLink * lnk = prims->FirstLink(); lnk; lnk = lnk->Next()) {
1999 TObject * child = lnk->GetObject();
2000 if (child && inputSet.find(child) == inputSet.end()) orphans.insert(child);
2003 prims->UseRWLock(kFALSE);
2004 prims->SetOwner(kFALSE);
2005 prims->Clear(
"nodelete");
2009 for (
auto * obj : objects) {
2010 if (obj)
delete obj;
2015 for (
auto * obj : orphans) {
2019 gROOT->SetMustClean(prevMustClean);
2027 std::vector<TObject *> objects;
2028 for (TObjLink * lnk = lst->FirstLink(); lnk; lnk = lnk->Next()) {
2029 TObject * obj = lnk->GetObject();
2030 if (obj) objects.push_back(obj);
2034 lst->UseRWLock(kFALSE);
2035 lst->SetOwner(kFALSE);
2036 lst->Clear(
"nodelete");
2048 if (obj->InheritsFrom(TList::Class())) {
2049 TList * lst =
static_cast<TList *
>(obj);
2063 gSystem->GetProcInfo(&info);
2065 out[
"cpu_user"] = info.fCpuUser;
2066 out[
"cpu_sys"] = info.fCpuSys;
2067 out[
"cpu_total"] = info.fCpuUser + info.fCpuSys;
2068 out[
"mem_rss_kb"] = info.fMemResident;
2069 out[
"mem_vsize_kb"] = info.fMemVirtual;
2072 unsigned int hc = std::thread::hardware_concurrency();
2073 out[
"cpu_count"] = (hc == 0) ? 1 :
static_cast<int>(hc);
2081 out[
"totalRead"] = 0LL;
2082 out[
"totalWritten"] = 0LL;
2084 TList * files = (TList *)gROOT->GetListOfFiles();
2085 if (!files)
return out;
2087 Long64_t totalRead = 0;
2088 Long64_t totalWritten = 0;
2091 TObject * obj =
nullptr;
2092 while ((obj = next())) {
2093 TFile * f =
dynamic_cast<TFile *
>(obj);
2096 fi[
"name"] = f->GetName() ? f->GetName() :
"";
2097 fi[
"isZombie"] = (bool)f->IsZombie();
2098 fi[
"isOpen"] = (bool)f->IsOpen();
2101 Long64_t bytesRead = 0;
2102 Long64_t bytesWritten = 0;
2110 bytesRead = f->GetBytesRead();
2111 bytesWritten = f->GetBytesWritten();
2119 fi[
"bytesRead"] = bytesRead;
2120 fi[
"bytesWritten"] = bytesWritten;
2122 totalRead += bytesRead;
2123 totalWritten += bytesWritten;
2125 out[
"files"].push_back(fi);
2128 out[
"totalRead"] = totalRead;
2129 out[
"totalWritten"] = totalWritten;
2137 out[
"total_rx"] = 0ULL;
2138 out[
"total_tx"] = 0ULL;
2140#if defined(__linux__)
2141 std::ifstream f(
"/proc/net/dev");
2142 if (!f.good())
return out;
2145 std::getline(f, line);
2146 std::getline(f, line);
2147 while (std::getline(f, line)) {
2148 if (line.empty())
continue;
2149 size_t colon = line.find(
':');
2150 if (colon == std::string::npos)
continue;
2151 std::string ifname = line.substr(0, colon);
2153 auto ltrim = [](std::string & s) {
2154 size_t start = s.find_first_not_of(
" \t");
2155 if (start != std::string::npos)
2156 s = s.substr(start);
2160 auto rtrim = [](std::string & s) {
2161 size_t end = s.find_last_not_of(
" \t");
2162 if (end != std::string::npos)
2163 s = s.substr(0, end + 1);
2169 std::string rest = line.substr(colon + 1);
2170 std::stringstream ss(rest);
2171 std::vector<unsigned long long> vals;
2175 vals.push_back(std::stoull(tok));
2178 vals.push_back(0ULL);
2181 if (vals.size() >= 9) {
2182 unsigned long long rx = vals[0];
2183 unsigned long long tx = vals[8];
2185 iface[
"name"] = ifname;
2188 out[
"interfaces"].push_back(iface);
2189 out[
"total_rx"] =
static_cast<unsigned long long>(
2190 out[
"total_rx"].is_null() ? 0ULL : out[
"total_rx"].get<unsigned long long>()) +
2192 out[
"total_tx"] =
static_cast<unsigned long long>(
2193 out[
"total_tx"].is_null() ? 0ULL : out[
"total_tx"].get<unsigned long long>()) +
2198#elif defined(__APPLE__)
2199 struct ifaddrs * ifap =
nullptr;
2200 if (getifaddrs(&ifap) != 0)
return out;
2201 for (
struct ifaddrs * ifa = ifap; ifa; ifa = ifa->ifa_next) {
2202 if (!ifa->ifa_data)
continue;
2203 struct if_data * ifd = (
struct if_data *)ifa->ifa_data;
2205 unsigned long long rx = (
unsigned long long)ifd->ifi_ibytes;
2206 unsigned long long tx = (
unsigned long long)ifd->ifi_obytes;
2208 iface[
"name"] = ifa->ifa_name ? ifa->ifa_name : std::string();
2211 out[
"interfaces"].push_back(iface);
2213 static_cast<unsigned long long>(out[
"total_rx"].is_null() ? 0ULL : out[
"total_rx"].get<unsigned long long>()) +
2216 static_cast<unsigned long long>(out[
"total_tx"].is_null() ? 0ULL : out[
"total_tx"].get<unsigned long long>()) +
Provides HTTP request functionality using libcurl.
std::string get(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 GET request.
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.
Utility class providing static helper functions for file operations, histogram manipulations,...
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.
static TFile * OpenFile(std::string filename, std::string mode="READ", bool createLocalDir=true)
Open a ROOT file.
static void AddRawJsonInjection(json &j, const std::vector< std::string > &path, const std::string &rawJson, const std::string &injectionsKey="__raw_json_injections")
Add one raw JSON injection entry into metadata field.
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.
static std::vector< std::string > Truncate(std::vector< std::string > values, std::string value)
Truncate vector of strings by a value.
static TH1 * ProjectTHnSparse(THnSparse *hns, const std::vector< int > &axes, Option_t *option="")
Project a THnSparse histogram onto specified axes.
static bool IsFileSupported(std::string filename)
Check if a file is supported.
static std::vector< std::string > FindEos(std::string path, std::string filename="")
Find EOS files in a path matching filename.
static bool LoadJsonFile(json &cfg, std::string filename)
Loads a JSON configuration file into the provided json object.
static json GetTFileIOStats()
Get TFile read/write statistics by inspecting ROOT's list of open files.
static bool SaveRawFile(std::string filename, std::string content)
Save content to a raw file.
static std::string OpenRawFile(std::string filename)
Open a raw file and return its content as string.
static std::vector< std::string > FindLocal(std::string path, std::string filename="")
Find local files in a path matching filename.
static void SafeDeleteTList(TList *&lst)
Safely delete a TList and all its contents, bypassing ROOT's GarbageCollect.
static void PrintPointSafe(const std::vector< int > &coords, int index=-1)
Print coordinates safely.
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.
static std::string MergeRawJsonWithMetadata(const std::string &rawJson, const json &metadata)
Merge raw JSON string with metadata fields.
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.
static json GetNetDevStats()
Get system-wide network interface totals (RX/TX bytes) in a cross-platform way. On Linux reads /proc/...
static bool AccessPathName(std::string path)
Check if a path is accessible.
static std::vector< int > TokenizeInt(std::string_view input, const char delim)
Tokenize a string into integers by delimiter.
static THnSparse * Convert(TH1 *h1, std::vector< std::string > names={}, std::vector< std::string > titles={})
Convert TH1 to THnSparse.
static TMacro * OpenMacro(std::string filename)
Open a macro file.
static std::vector< std::string > Tokenize(std::string_view input, const char delim)
Tokenize a string by delimiter.
static bool CreateDirectory(const std::string &path)
static std::string FormatTime(long long seconds)
Format time in seconds to human-readable string.
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.
static bool GetAxisRangeInBase(TAxis *a, int rebin, int rebin_start, int bin, int &min, int &max)
Get axis range in base for rebinned axis.
static json GetSystemStats()
Get process CPU and RSS memory statistics using ROOT's gSystem::GetProcInfo.
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.
static std::vector< std::string > GetJsonStringArray(json j)
Get JSON value as array of strings.
static std::string GetJsonString(json j)
Get JSON value as string.
static int Cp(std::string source, std::string destination, Bool_t progressbar=kTRUE)
Copy a file from source to destination.
static bool CollectRawJsonInjections(const json &j, RawJsonInjections &injections, const std::string &injectionsKey="__raw_json_injections")
Collect raw JSON injection entries from metadata field.
static bool EnableMT(Int_t numthreads=-1)
Enable multi-threading with specified number of threads.
static int GetJsonInt(json j)
Get JSON value as integer.
static std::string Join(const std::vector< std::string > &values, const char delim=',')
Join vector of strings into a single string with delimiter.
static void ProgressBar(int current, int total, std::string prefix="", std::string suffix="", int barWidth=50)
Display progress bar.
static std::string GetCoordsString(const std::vector< int > &coords, int index=-1, int width=0)
Get string representation of coordinates.
static void SafeDeleteObjects(std::vector< TObject * > &objects)
Safely delete a vector of ROOT objects, bypassing GarbageCollect.
static THnSparse * CreateSparseFromParquetTaxi(const std::string &filename, THnSparse *hns=nullptr, Int_t nMaxRows=-1)
Create THnSparse from Parquet Taxi file.
static void SafeDeleteObject(TObject *&obj)
Safely delete a TObject, handling TList contents and TCanvas/TPad cleanup.
static void VectorToArray(std::vector< int > v1, Int_t *v2)
Convert vector to array.
static std::vector< int > ArrayToVector(Int_t *v1, int size)
Convert array to vector.
static double GetJsonDouble(json j)
Get JSON value as double.
static bool GetJsonBool(json j)
Get JSON value as boolean.
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.
static std::vector< std::string > Find(std::string path, std::string filename="")
Find files in a path matching filename.
static std::vector< std::vector< int > > Permutations(const std::vector< int > &v)
Generate all permutations of a vector.
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.
static std::string InjectRawJson(json &j, const RawJsonInjections &injections)
Global callback function for libwebsockets client events.