diff --git a/examples/dataset.rml b/examples/dataset.rml new file mode 100644 index 000000000..742aed4f9 --- /dev/null +++ b/examples/dataset.rml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/source/framework/core/inc/TRestAnalysisTree.h b/source/framework/core/inc/TRestAnalysisTree.h index 30f620512..a1542afdd 100644 --- a/source/framework/core/inc/TRestAnalysisTree.h +++ b/source/framework/core/inc/TRestAnalysisTree.h @@ -266,7 +266,7 @@ class TRestAnalysisTree : public TTree { Bool_t EvaluateCuts(const std::string& expression); Bool_t EvaluateCut(const std::string& expression); - TString GetStringWithObservableNames(); + std::vector GetObservableNames(); std::vector GetCutObservables(const std::string& cut_str); diff --git a/source/framework/core/inc/TRestDataSet.h b/source/framework/core/inc/TRestDataSet.h new file mode 100644 index 000000000..b72a4c63e --- /dev/null +++ b/source/framework/core/inc/TRestDataSet.h @@ -0,0 +1,131 @@ +/************************************************************************* + * This file is part of the REST software framework. * + * * + * Copyright (C) 2016 GIFNA/TREX (University of Zaragoza) * + * For more information see https://gifna.unizar.es/trex * + * * + * REST is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 3 of the License, or * + * (at your option) any later version. * + * * + * REST is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have a copy of the GNU General Public License along with * + * REST in $REST_PATH/LICENSE. * + * If not, see https://www.gnu.org/licenses/. * + * For the list of contributors see $REST_PATH/CREDITS. * + *************************************************************************/ + +#ifndef REST_TRestDataSet +#define REST_TRestDataSet + +#include + +#include + +#include "TRestMetadata.h" + +struct RelevantQuantity { + /// The associated metadata member used to register the relevant quantity + std::string metadata; + + /// It determines how to produce the relevant quantity (accumulate/unique/last/max/min) + std::string strategy; + + /// A user given description that can be used to define the relevant quantity + std::string description; + + /// The quantity value + Double_t value; +}; + +/// It allows to group a number of runs that satisfy given metadata conditions +class TRestDataSet : public TRestMetadata { + private: + /// All the selected runs will have a starting date after fStartTime + std::string fStartTime = "2000/01/01"; //< + + /// All the selected runs will have an ending date before fEndTime + std::string fEndTime = "3000/12/31"; //< + + /// A glob file pattern that must be satisfied by all files + std::string fFilePattern = ""; //< + + /// It contains a list of the observables that will be added to the final tree or exported file + std::vector fObservablesList; //< + + /// It contains a list of the process where all observables should be added + std::vector fProcessObservablesList; //< + + /// A list of metadata members where filters will be applied + std::vector fFilterMetadata; //< + + /// If not empty it will check if the metadata member contains the value + std::vector fFilterContains; //< + + /// If the corresponding element is not empty it will check if the metadata member is greater + std::vector fFilterGreaterThan; //< + + /// If the corresponding element is not empty it will check if the metadata member is lower + std::vector fFilterLowerThan; //< + + /// The properties of a relevant quantity that we want to store together with the dataset + std::map fQuantity; //< + + /// The total integrated run time of selected files + Double_t fTotalDuration = 0; //< + + /// The resulting RDataFrame object after initialization + ROOT::RDataFrame fDataSet = 0; //! + + /// A pointer to the generated tree + TTree* fTree = nullptr; //! + + /// A list populated by the FileSelection method using the conditions of the dataset + std::vector fFileSelection; //! + + void InitFromConfigFile() override; + + protected: + virtual std::vector FileSelection(); + + public: + /// Gives access to the RDataFrame + ROOT::RDataFrame GetDataFrame() const { + if (fTree == nullptr) RESTWarning << "DataFrame has not been yet initialized" << RESTendl; + return fDataSet; + } + + /// Gives access to the tree + TTree* GetTree() const { + if (fTree == nullptr) RESTWarning << "Tree has not been yet initialized" << RESTendl; + return fTree; + } + + /// Number of variables (or observables) + size_t GetNumberOfColumns() { return fDataSet.GetColumnNames().size(); } + + /// Number of variables (or observables) + size_t GetNumberOfBranches() { return GetNumberOfColumns(); } + + /// It returns a list of the files that have been finally selected + std::vector GetFileSelection() { return fFileSelection; } + + /// It returns the accumulated run time in seconds + Double_t GetTotalTimeInSeconds() const { return fTotalDuration; } + + void Export(const std::string& filename); + + void PrintMetadata() override; + void Initialize() override; + TRestDataSet(); + TRestDataSet(const char* cfgFileName, const std::string& name = ""); + ~TRestDataSet(); + + ClassDefOverride(TRestDataSet, 1); +}; +#endif diff --git a/source/framework/core/src/TRestAnalysisTree.cxx b/source/framework/core/src/TRestAnalysisTree.cxx index f271f51b7..b52cd0ac7 100644 --- a/source/framework/core/src/TRestAnalysisTree.cxx +++ b/source/framework/core/src/TRestAnalysisTree.cxx @@ -1022,18 +1022,19 @@ Double_t TRestAnalysisTree::GetObservableMinimum(const TString& obsName, Double_ } /////////////////////////////////////////////// -/// \brief It returns a string containing all the observables that exist in the analysis tree. +/// \brief It returns a vector with strings containing all the observables that exist in +/// the analysis tree. /// -TString TRestAnalysisTree::GetStringWithObservableNames() { - Int_t nEntries = GetEntries(); +std::vector TRestAnalysisTree::GetObservableNames() { + std::vector names; + + // Int_t nEntries = GetEntries(); auto branches = GetListOfBranches(); - std::string branchNames = ""; for (int i = 0; i < branches->GetEntries(); i++) { - if (i > 0) branchNames += " "; - branchNames += (string)branches->At(i)->GetName(); + names.push_back((string)branches->At(i)->GetName()); } - return (TString)branchNames; + return names; } /////////////////////////////////////////////// diff --git a/source/framework/core/src/TRestDataSet.cxx b/source/framework/core/src/TRestDataSet.cxx new file mode 100644 index 000000000..c51234c4c --- /dev/null +++ b/source/framework/core/src/TRestDataSet.cxx @@ -0,0 +1,666 @@ +/************************************************************************* + * This file is part of the REST software framework. * + * * + * Copyright (C) 2016 GIFNA/TREX (University of Zaragoza) * + * For more information see https://gifna.unizar.es/trex * + * * + * REST is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 3 of the License, or * + * (at your option) any later version. * + * * + * REST is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have a copy of the GNU General Public License along with * + * REST in $REST_PATH/LICENSE. * + * If not, see https://www.gnu.org/licenses/. * + * For the list of contributors see $REST_PATH/CREDITS. * + *************************************************************************/ + +///////////////////////////////////////////////////////////////////////// +/// This class allows to make a selection of ROOT data files that fulfill +/// certain metadata conditions allowing to create a group of files that +/// define a particular dataset. The files will be searched in a relative +/// or absolute path that is given together the `filePattern` parameter. +/// +/// ### Basic file selection +/// +/// We will be able to define the dates range where files will be +/// accepted, using `startTime` and `endTime` parameters. The run start +/// time and end time stored inside TRestRun will be evaluated to decide +/// if the file should be considered. +/// +/// A summary of the basic parameters follows: +/// +/// * **filePattern**: A full path glob pattern to the files that will +/// be considered. It is a first filter considering the path and the +/// filename. Usual wild cards such as * or ? will be allowed to target +/// a given range of files. +/// * **startTime**: Only files with start run after `startTime` will be +/// considered. +/// * **endTime**: Only files with end run before `endTime` will be +/// considered. +/// +/// ### Metadata rules +/// +/// We may add rules for any metadata class existing inside our ROOT +/// datafiles. For such, we use the `filter` key where we define the +/// metadata member name where we want to evaluate the rules. We need +/// to define the `metadata` field where we specify the class name or +/// metadata user given name, together with the metadata member we want +/// to access, the metadata member must be named using the coventions +/// defined inside the methods TRestRun::ReplaceMetadataMember and +/// TRestRun::ReplaceMetadataMembers. +/// +/// Three optional fields can be used to apply the rule: +/// +/// - **contains**: It will accept the file if the metadata member +/// contains the string given inside this field, it can also be a +/// number. +/// - **greaterThan**: It will accept only runs with the corresponding +/// metadata member above the given value. Obviously, the value must +/// be a numeric value. +/// - **lowerThan**: It will accept only runs with the corresponding +/// metadata member below the given value. Obviously, the value must +/// be a numeric value. +/// +/// Example of metadata rule: +/// \code +/// +/// \endcode +/// +/// ### Branches (or observables) selection +/// +/// Once the files that fulfill the given dates, filename pattern and +/// metadata rules have been identified, the initialization will produce +/// an instance to a ROOT::RDataFrame and an instance to a ROOT::TTree +/// that will give access to the unified analysis tree. The available +/// columns or branches at those instances will be defined by the user +/// inside this metadata class, through the special keywords +/// `observables` and `processObservables`. +/// +/// - **observables**: This key will allow to add a list of observables +/// present at the analysis tree to be considered for the new compilation. +/// The values must be separated by colons, ":". +/// +/// - **processObservables**: This key will allow to add a list of process +/// names for which all the observables found inside will be added to +/// the compilation. The values must be separated by colons, ":". +/// +/// Their use can be seen in the following example: +/// \code +/// +/// +/// +/// +/// +/// +/// +/// +/// // Will add to the final tree only the specific observables +/// +/// +/// // Will add all the observables from the process `rawAna` +/// +/// +/// +/// \endcode +/// +/// ### Basic usage +/// +/// The basic usage of this class is by loading the metadata class as any +/// other metadata class. After initialization, the user will get access +/// to the internal RDataFrame and TTree data members, as shown in the +/// following example: +/// +/// \code +/// restRoot +/// [0] TRestDataSet d("dataset"); +/// [1] d.Initialize(); +/// [2] d.GetTree()->GetEntries() +/// [3] d.GetDataFrame().GetColumnNames() +/// \endcode +/// +/// We can then use our favorite ROOT::RDataFrame or TTree methods. +/// +/// ### Exporting datasets +/// +/// On top of performing a compilation of runs to construct a dataset +/// and access the data in a unified way, we may also save the generated +/// dataset to disk. This feature might be used to generate easier to +/// handle data compilations that have been extracted from an **official +/// data repository**. +/// +/// Different output formats are supported: +/// +/// - `root`: It will store the simplified `TTree` with the observables +/// selected by the user and compiled with the corresponding file selection. +/// The root file will also contain a TRestDataSet object to allow future +/// users of the output file generated to identify the origin of the data. +/// +/// - `txt` or `csv`: It will create an ASCII table where each column +/// will contain the data of a given branch. A header will be written +/// inside the file with all the information found inside the TRestDataSet +/// instance. +/// +/// +/// Example: +/// \code +/// restRoot +/// [0] TRestDataSet d("dataset"); +/// [1] d.Initialize(); +/// [2] d.Export("mydataset.csv"); +/// [3] d.Export("mydataset.root"); +/// \endcode +/// +/// ### Relevant quantities +/// +/// Sometimes we will be willing that our dataset contains few variables +/// that are extremelly meaningful for the data compilation, and that will +/// be required for further calculations or for the proper interpretation +/// of the data. The key ` +/// \endcode +/// +/// The `name` field will be the user given name of the quantity. The +/// `metadata` field inside the ` +/// +#include "TRestDataSet.h" + +#include "TRestRun.h" +#include "TRestTools.h" + +ClassImp(TRestDataSet); + +/////////////////////////////////////////////// +/// \brief Default constructor +/// +TRestDataSet::TRestDataSet() { Initialize(); } + +///////////////////////////////////////////// +/// \brief Constructor loading data from a config file +/// +/// If no configuration path is defined using TRestMetadata::SetConfigFilePath +/// the path to the config file must be specified using full path, absolute or +/// relative. +/// +/// The default behaviour is that the config file must be specified with +/// full path, absolute or relative. +/// +/// \param cfgFileName A const char* giving the path to an RML file. +/// \param name The name of the specific metadata. It will be used to find the +/// corresponding TRestAxionMagneticField section inside the RML. +/// +TRestDataSet::TRestDataSet(const char* cfgFileName, const std::string& name) : TRestMetadata(cfgFileName) { + LoadConfigFromFile(fConfigFileName, name); + + if (GetVerboseLevel() >= TRestStringOutput::REST_Verbose_Level::REST_Info) PrintMetadata(); +} + +/////////////////////////////////////////////// +/// \brief Default destructor +/// +TRestDataSet::~TRestDataSet() {} + +/////////////////////////////////////////////// +/// \brief It will initialize the data frame with the filelist and column names +/// (or observables) that have been defined by the user. +/// +void TRestDataSet::Initialize() { + SetSectionName(this->ClassName()); + + if (fTree != nullptr) { + RESTWarning << "Tree has already been loaded. Skipping TRestDataSet::Initialize ... " << RESTendl; + return; + } + + if (fFileSelection.empty()) FileSelection(); + + // We are not ready yet + if (fFileSelection.empty()) return; + + ///// Disentangling process observables --> producing finalList + TRestRun run(fFileSelection[0]); + std::vector finalList; + finalList.push_back("runOrigin"); + finalList.push_back("eventID"); + finalList.push_back("timeStamp"); + + for (const auto& obs : fObservablesList) finalList.push_back(obs); + + std::vector obsNames = run.GetAnalysisTree()->GetObservableNames(); + for (const auto& name : obsNames) { + for (const auto& pcs : fProcessObservablesList) { + if (name.find(pcs) == 0) finalList.push_back(name); + } + } + /////// + + ROOT::EnableImplicitMT(); + + fDataSet = ROOT::RDataFrame("AnalysisTree", fFileSelection); + + std::string user = getenv("USER"); + std::string fOutName = "/tmp/rest_output_" + user + ".root"; + fDataSet.Snapshot("AnalysisTree", fOutName, finalList); + + fDataSet = ROOT::RDataFrame("AnalysisTree", fOutName); + + TFile* f = TFile::Open(fOutName.c_str()); + fTree = (TTree*)f->Get("AnalysisTree"); + + int cont = 0; + std::string obsListStr; + for (const auto& l : finalList) { + if (cont > 0) obsListStr += ":"; + obsListStr += l; + cont++; + } + + // We do this so that later we can recover the values using TTree::GetVal + fTree->Draw((TString)obsListStr, "", "goff"); + + RESTInfo << " - Dataset initialized!" << RESTendl; +} + +/////////////////////////////////////////////// +/// \brief Function to determine the filenames that satisfy the dataset conditions +/// +std::vector TRestDataSet::FileSelection() { + fFileSelection.clear(); + + std::time_t time_stamp_start = REST_StringHelper::StringToTimeStamp(fStartTime); + std::time_t time_stamp_end = REST_StringHelper::StringToTimeStamp(fEndTime); + + if (!time_stamp_end || !time_stamp_start) { + RESTError << "TRestDataSet::FileSelect. Start or end dates not properly formed. Please, check " + "REST_StringHelper::StringToTimeStamp documentation for valid formats" + << RESTendl; + return fFileSelection; + } + + std::vector fileNames = TRestTools::GetFilesMatchingPattern(fFilePattern); + + if (!fileNames.empty()) { + RESTInfo << "TRestDataSet::FileSelection. Starting file selection." << RESTendl; + RESTInfo << "Total files : " << fileNames.size() << RESTendl; + RESTInfo << "This process may take long computation time in case there are many files." << RESTendl; + } + + fTotalDuration = 0; + for (const auto& file : fileNames) { + TRestRun run(file); + double runStart = run.GetStartTimestamp(); + double runEnd = run.GetEndTimestamp(); + + if (runStart < time_stamp_start && runEnd > time_stamp_end) { + continue; + } + + int n = 0; + bool accept = true; + for (const auto& md : fFilterMetadata) { + std::string mdValue = run.GetMetadataMember(md); + + if (!fFilterContains[n].empty()) + if (mdValue.find(fFilterContains[n]) == std::string::npos) accept = false; + + if (fFilterGreaterThan[n] != -1) { + if (StringToDouble(mdValue) <= fFilterGreaterThan[n]) accept = false; + } + + if (fFilterLowerThan[n] != -1) + if (StringToDouble(mdValue) >= fFilterLowerThan[n]) accept = false; + + n++; + } + + if (!accept) continue; + + for (auto& [name, properties] : fQuantity) { + Double_t value = + REST_StringHelper::StringToDouble(run.ReplaceMetadataMembers(properties.metadata)); + + if (properties.strategy == "accumulate") properties.value += value; + + if (properties.strategy == "max") + if (properties.value == 0 || properties.value < value) properties.value = value; + + if (properties.strategy == "min") + if (properties.value == 0 || properties.value > value) properties.value = value; + + if (properties.strategy == "unique") + if (properties.value == 0) + properties.value = value; + else if (properties.value != value) { + RESTWarning << "TRestDataSet::FileSelection. Relevant quantity retrieval." << RESTendl; + RESTWarning << "A unique metadata member used for the `" << name + << "` quantity is not unique!!" << RESTendl; + RESTWarning << "Pre-registered value : " << properties.value << " New value : " << value + << RESTendl; + } + + if (properties.strategy == "last") properties.value = value; + } + + fTotalDuration += run.GetEndTimestamp() - run.GetStartTimestamp(); + fFileSelection.push_back(file); + } + RESTInfo << RESTendl; + + return fFileSelection; +} + +///////////////////////////////////////////// +/// \brief Prints on screen the information about the metadata members of TRestAxionSolarFlux +/// +void TRestDataSet::PrintMetadata() { + TRestMetadata::PrintMetadata(); + + RESTMetadata << " - StartTime : " << fStartTime << RESTendl; + RESTMetadata << " - EndTime : " << fEndTime << RESTendl; + RESTMetadata << " - Path : " << TRestTools::SeparatePathAndName(fFilePattern).first << RESTendl; + RESTMetadata << " - File pattern : " << TRestTools::SeparatePathAndName(fFilePattern).second << RESTendl; + RESTMetadata << " " << RESTendl; + RESTMetadata << " - Accumulated run time (seconds) : " << fTotalDuration << RESTendl; + RESTMetadata << " - Accumulated run time (hours) : " << fTotalDuration / 3600. << RESTendl; + RESTMetadata << " - Accumulated run time (days) : " << fTotalDuration / 3600. / 24. << RESTendl; + + RESTMetadata << " " << RESTendl; + + if (!fObservablesList.empty()) { + RESTMetadata << " Single observables added:" << RESTendl; + RESTMetadata << " -------------------------" << RESTendl; + for (const auto& l : fObservablesList) RESTMetadata << " - " << l << RESTendl; + + RESTMetadata << " " << RESTendl; + } + + if (!fProcessObservablesList.empty()) { + RESTMetadata << " Process observables added: " << RESTendl; + RESTMetadata << " -------------------------- " << RESTendl; + for (const auto& l : fProcessObservablesList) RESTMetadata << " - " << l << RESTendl; + + RESTMetadata << " " << RESTendl; + } + + if (!fFilterMetadata.empty()) { + RESTMetadata << " Metadata filters: " << RESTendl; + RESTMetadata << " ----------------- " << RESTendl; + + int n = 0; + for (const auto& mdFilter : fFilterMetadata) { + RESTMetadata << " - " << mdFilter << "."; + + if (!fFilterContains[n].empty()) RESTMetadata << " Contains: " << fFilterContains[n]; + if (fFilterGreaterThan[n] != -1) RESTMetadata << " Greater than: " << fFilterGreaterThan[n]; + if (fFilterLowerThan[n] != -1) RESTMetadata << " Lower than: " << fFilterLowerThan[n]; + + RESTMetadata << RESTendl; + n++; + } + + RESTMetadata << " " << RESTendl; + } + + if (!fQuantity.empty()) { + RESTMetadata << " Relevant quantities: " << RESTendl; + RESTMetadata << " -------------------- " << RESTendl; + + int n = 0; + for (auto const& [name, properties] : fQuantity) { + RESTMetadata << " - Name : " << name << ". Value : " << properties.value + << ". Strategy: " << properties.strategy << RESTendl; + RESTMetadata << " - Metadata: " << properties.metadata << RESTendl; + RESTMetadata << " - Description: " << properties.description << RESTendl; + + RESTMetadata << " " << RESTendl; + n++; + } + } + + RESTMetadata << "----" << RESTendl; +} + +/////////////////////////////////////////////// +/// \brief Initialization of specific TRestDataSet members through an RML file +/// +void TRestDataSet::InitFromConfigFile() { + TRestMetadata::InitFromConfigFile(); + + /// Reading filters + TiXmlElement* filterDefinition = GetElement("filter"); + while (filterDefinition != nullptr) { + std::string metadata = GetFieldValue("metadata", filterDefinition); + if (metadata.empty() || metadata == "Not defined") { + RESTError << "Filter key defined without metadata member!" << RESTendl; + exit(1); + } + + fFilterMetadata.push_back(metadata); + + std::string contains = GetFieldValue("contains", filterDefinition); + if (contains == "Not defined") contains = ""; + Double_t greaterThan = StringToDouble(GetFieldValue("greaterThan", filterDefinition)); + Double_t lowerThan = StringToDouble(GetFieldValue("lowerThan", filterDefinition)); + + fFilterContains.push_back(contains); + fFilterGreaterThan.push_back(greaterThan); + fFilterLowerThan.push_back(lowerThan); + + filterDefinition = GetNextElement(filterDefinition); + } + + /// Reading observables + TiXmlElement* observablesDefinition = GetElement("observables"); + while (observablesDefinition != nullptr) { + std::string observables = GetFieldValue("list", observablesDefinition); + if (observables.empty() || observables == "Not defined") { + RESTError << " obsList = REST_StringHelper::Split(observables, ","); + + for (const auto& l : obsList) fObservablesList.push_back(l); + + observablesDefinition = GetNextElement(observablesDefinition); + } + + /// Reading process observables + TiXmlElement* obsProcessDefinition = GetElement("processObservables"); + while (obsProcessDefinition != nullptr) { + std::string observables = GetFieldValue("list", obsProcessDefinition); + if (observables.empty() || observables == "Not defined") { + RESTError << " obsList = REST_StringHelper::Split(observables, ","); + + for (const auto& l : obsList) fProcessObservablesList.push_back(l); + + obsProcessDefinition = GetNextElement(obsProcessDefinition); + } + + /// Reading relevant quantities + TiXmlElement* quantityDefinition = GetElement("quantity"); + while (quantityDefinition != nullptr) { + std::string name = GetFieldValue("name", quantityDefinition); + if (name.empty() || name == "Not defined") { + RESTError << " dataTypes; + for (int n = 0; n < fTree->GetListOfBranches()->GetEntries(); n++) { + std::string bName = fTree->GetListOfBranches()->At(n)->GetName(); + std::string type = fTree->GetLeaf((TString)bName)->GetTypeName(); + dataTypes.push_back(type); + if (type != "Double_t" && type != "Int_t") { + RESTError << "Branch name : " << bName << " is type : " << type << RESTendl; + RESTError << "Only Int_t and Double_t types are allowed for " + "exporting to ASCII table" + << RESTendl; + RESTError << "File will not be generated" << RESTendl; + return; + } + } + + FILE* f = fopen(filename.c_str(), "wt"); + ///// Writing header + fprintf(f, "### TRestDataSet generated file\n"); + fprintf(f, "### \n"); + fprintf(f, "### StartTime : %s\n", fStartTime.c_str()); + fprintf(f, "### EndTime : %s\n", fEndTime.c_str()); + fprintf(f, "###\n"); + fprintf(f, "### Accumulated run time (seconds) : %lf\n", fTotalDuration); + fprintf(f, "### Accumulated run time (hours) : %lf\n", fTotalDuration / 3600.); + fprintf(f, "### Accumulated run time (days) : %lf\n", fTotalDuration / 3600. / 24.); + fprintf(f, "###\n"); + fprintf(f, "### Data path : %s\n", TRestTools::SeparatePathAndName(fFilePattern).first.c_str()); + fprintf(f, "### File pattern : %s\n", TRestTools::SeparatePathAndName(fFilePattern).second.c_str()); + fprintf(f, "###\n"); + if (!fFilterMetadata.empty()) { + fprintf(f, "### Metadata filters : \n"); + int n = 0; + for (const auto& md : fFilterMetadata) { + fprintf(f, "### - %s.", md.c_str()); + if (!fFilterContains[n].empty()) fprintf(f, " Contains: %s.", fFilterContains[n].c_str()); + if (fFilterGreaterThan[n] != -1) fprintf(f, " Greater than: %6.3lf.", fFilterGreaterThan[n]); + if (fFilterLowerThan[n] != -1) fprintf(f, " Lower than: %6.3lf.", fFilterLowerThan[n]); + fprintf(f, "\n"); + n++; + } + } + fprintf(f, "###\n"); + fprintf(f, "### Relevant quantities: \n"); + for (auto& [name, properties] : fQuantity) { + fprintf(f, "### - %s : %lf - %s\n", name.c_str(), properties.value, + properties.description.c_str()); + } + fprintf(f, "###\n"); + fprintf(f, "### Observables list: "); + for (int n = 0; n < fTree->GetListOfBranches()->GetEntries(); n++) { + std::string bName = fTree->GetListOfBranches()->At(n)->GetName(); + fprintf(f, " %s", bName.c_str()); + } + fprintf(f, "\n"); + fprintf(f, "###\n"); + fprintf(f, "### Data starts here\n"); + + for (int n = 0; n < fTree->GetEntries(); n++) { + for (int m = 0; m < GetNumberOfBranches(); m++) { + std::string bName = fTree->GetListOfBranches()->At(m)->GetName(); + if (m > 0) fprintf(f, "\t"); + if (dataTypes[m] == "Double_t") + if (bName == "timeStamp") + fprintf(f, "%010.0lf", fTree->GetVal(m)[n]); + else + fprintf(f, "%05.3e", fTree->GetVal(m)[n]); + else + fprintf(f, "%06d", (Int_t)fTree->GetVal(m)[n]); + } + fprintf(f, "\n"); + } + fclose(f); + + return; + } else if (TRestTools::GetFileNameExtension(filename) == "root") { + fDataSet.Snapshot("AnalysisTree", filename); + + TFile* f = TFile::Open(filename.c_str(), "UPDATE"); + this->Write(); + f->Close(); + } else { + RESTWarning << "TRestDataSet::Export. Extension " << TRestTools::GetFileNameExtension(filename) + << " not recognized" << RESTendl; + } +}