From 64910412b46e78d5c11bf1b5fb815c9d855e3e41 Mon Sep 17 00:00:00 2001 From: Ni Kaixiang Date: Tue, 26 Apr 2022 22:45:26 +0800 Subject: [PATCH 01/28] update logic of expression identification. --- .../framework/tools/src/TRestStringHelper.cxx | 52 ++++++++++++++----- 1 file changed, 40 insertions(+), 12 deletions(-) diff --git a/source/framework/tools/src/TRestStringHelper.cxx b/source/framework/tools/src/TRestStringHelper.cxx index b8ea5b19a..c92999b93 100644 --- a/source/framework/tools/src/TRestStringHelper.cxx +++ b/source/framework/tools/src/TRestStringHelper.cxx @@ -21,28 +21,56 @@ using namespace std; /// \brief Returns 1 only if valid mathematical expression keywords (or numbers) /// are found in the string **in**. If not it returns 0. /// +/// By logic, mathematical expressions must have: +-*/e^% in the middle, or % in the end, or math functions in the beginning. +/// despite those symbols, the string should be purely numeric. +/// example: +/// 1+1 : expression +/// sin(1.5) : expression +/// 123456789 : not expression, It is a pure number that can be directly parsed. Int_t REST_StringHelper::isAExpression(string in) { - string temp = in; - vector replace{"sqrt", "log", "exp", "gaus", "cos", "sin", "tan", "atan", "acos", "asin"}; - for (int i = 0; i < replace.size(); i++) { - temp = Replace(temp, replace[i], "0", 0); + bool symbol = false; + bool numeric = false; + + if (in.length() < 2) // minimum expression: 3% + return 0; + + vector funcs{"sqrt", "log", "exp", "gaus", "cos", "sin", "tan", "atan", "acos", "asin"}; + for (int i = 0; i < funcs.size(); i++) { + if (in.find(funcs[i]) != std::string::npos) { + symbol = true; + break; + } } - if (temp.length() == 0) - return 0; - else if (temp.length() == 1) { - if (temp.find_first_not_of("0123456789") == std::string::npos) { - return 1; - } else { - return 0; + if (!symbol) { + int pos = in.find_first_of("+-*/e^%"); + if (pos > 0 && pos < in.size() - 1) { + symbol = true; + } + } + + if (!symbol) { + int pos = in.find_first_of("%"); + if (pos == in.size() - 1) { + symbol = true; + } + } + + if (symbol) { + string temp = in; + for (int i = 0; i < funcs.size(); i++) { + temp = Replace(temp, funcs[i], "0", 0); } - } else { if (temp.find_first_not_of("-0123456789e+*/.,)( ^%") == std::string::npos) { if (temp.find("/") == 0 || temp.find("./") == 0 || temp.find("../") == 0) return 0; // identify path return 1; } } + else + { + return 0; + } return 0; } From a31104f435d6b04a7dd7bed2d29e69f9e2cff223 Mon Sep 17 00:00:00 2001 From: Ni Kaixiang Date: Tue, 26 Apr 2022 22:48:01 +0800 Subject: [PATCH 02/28] StringToLong(): handles correctly scientific notation --- source/framework/tools/src/TRestStringHelper.cxx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/source/framework/tools/src/TRestStringHelper.cxx b/source/framework/tools/src/TRestStringHelper.cxx index c92999b93..31cffe353 100644 --- a/source/framework/tools/src/TRestStringHelper.cxx +++ b/source/framework/tools/src/TRestStringHelper.cxx @@ -535,6 +535,10 @@ Bool_t REST_StringHelper::StringToBool(std::string in) { } Long64_t REST_StringHelper::StringToLong(std::string in) { + if (in.find_first_of("eE") != string::npos) { + // in case for scientific numbers + return (Long64_t)StringToDouble(in); + } stringstream strIn; strIn << in; long long llNum; From 6603cdac4459a14fae015c316588f8e5a6048f82 Mon Sep 17 00:00:00 2001 From: Date: Thu, 28 Apr 2022 03:24:51 +0800 Subject: [PATCH 03/28] update TRestStringHelper --- .../framework/tools/src/TRestStringHelper.cxx | 1608 ++++++++--------- 1 file changed, 804 insertions(+), 804 deletions(-) diff --git a/source/framework/tools/src/TRestStringHelper.cxx b/source/framework/tools/src/TRestStringHelper.cxx index 31cffe353..382e14dd9 100644 --- a/source/framework/tools/src/TRestStringHelper.cxx +++ b/source/framework/tools/src/TRestStringHelper.cxx @@ -1,804 +1,804 @@ -#include "TRestStringHelper.h" - -#include - -#include "Rtypes.h" -#include "TApplication.h" -#include "TSystem.h" - -#if ROOT_VERSION_CODE < ROOT_VERSION(6, 0, 0) -#include "TFormula.h" -#else -#include "v5/TFormula.h" -#endif - -#include -#include - -using namespace std; - -/////////////////////////////////////////////// -/// \brief Returns 1 only if valid mathematical expression keywords (or numbers) -/// are found in the string **in**. If not it returns 0. -/// -/// By logic, mathematical expressions must have: +-*/e^% in the middle, or % in the end, or math functions in the beginning. -/// despite those symbols, the string should be purely numeric. -/// example: -/// 1+1 : expression -/// sin(1.5) : expression -/// 123456789 : not expression, It is a pure number that can be directly parsed. -Int_t REST_StringHelper::isAExpression(string in) { - bool symbol = false; - bool numeric = false; - - if (in.length() < 2) // minimum expression: 3% - return 0; - - vector funcs{"sqrt", "log", "exp", "gaus", "cos", "sin", "tan", "atan", "acos", "asin"}; - for (int i = 0; i < funcs.size(); i++) { - if (in.find(funcs[i]) != std::string::npos) { - symbol = true; - break; - } - } - - if (!symbol) { - int pos = in.find_first_of("+-*/e^%"); - if (pos > 0 && pos < in.size() - 1) { - symbol = true; - } - } - - if (!symbol) { - int pos = in.find_first_of("%"); - if (pos == in.size() - 1) { - symbol = true; - } - } - - if (symbol) { - string temp = in; - for (int i = 0; i < funcs.size(); i++) { - temp = Replace(temp, funcs[i], "0", 0); - } - if (temp.find_first_not_of("-0123456789e+*/.,)( ^%") == std::string::npos) { - if (temp.find("/") == 0 || temp.find("./") == 0 || temp.find("../") == 0) - return 0; // identify path - return 1; - } - } - else - { - return 0; - } - - return 0; -} - -/////////////////////////////////////////////// -/// \brief Evaluates and replaces valid mathematical expressions found in the -/// input string **buffer**. -/// -std::string REST_StringHelper::ReplaceMathematicalExpressions(std::string buffer, std::string errorMessage) { - buffer = Replace(buffer, " AND ", " && "); - buffer = Replace(buffer, " OR ", " || "); - // we spilt the unit part and the expresstion part - int pos = buffer.find_last_of("1234567890()."); - - string unit = buffer.substr(pos + 1, -1); - string temp = buffer.substr(0, pos + 1); - string result = ""; - - bool erased = false; - - std::vector Expressions = Split(temp, ","); - - if (Expressions.size() > 1 && Expressions[0][0] == '(' && - Expressions[Expressions.size() - 1][Expressions[Expressions.size() - 1].size() - 1] == ')') { - Expressions[0].erase(0, 1); - Expressions[Expressions.size() - 1].erase(Expressions[Expressions.size() - 1].size() - 1, 1); - erased = true; - } - - for (int i = 0; i < Expressions.size(); i++) { - if (!isAExpression(Expressions[i])) { - result += Expressions[i] + ","; - continue; - } - string evaluated = EvaluateExpression(Expressions[i]); - if (evaluated == "RESTerror") { - result += Expressions[i] + ","; - ferr << "ReplaceMathematicalExpressions. Error on RML syntax!" << endl; - if (errorMessage != "") ferr << errorMessage << endl; - } else - result += evaluated + ","; - } - if (Expressions.size() > 0) result.erase(result.size() - 1, 1); - - if (erased) { - result = "(" + result + ")"; - } - - return result + unit; -} - -/////////////////////////////////////////////// -/// \brief Evaluates a complex numerical expression and returns the resulting -/// value using TFormula. -/// -std::string REST_StringHelper::EvaluateExpression(std::string exp) { - if (!isAExpression(exp)) { - return exp; - } - -// NOTE!!! In root6 the expression like "1/2" will be computed using the input -// as int number, which will return 0, and cause problem. we roll back to -// TFormula of version 5 -#if ROOT_VERSION_CODE < ROOT_VERSION(6, 0, 0) - TFormula formula("tmp", exp.c_str()); -#else - ROOT::v5::TFormula formula("tmp", exp.c_str()); -#endif - - ostringstream sss; - Double_t number = formula.EvalPar(0); - if (number > 0 && number < 1.e-300) { - warning << "REST_StringHelper::EvaluateExpresssion. Expression not recognized --> " << exp << endl; - return (string) "RESTerror"; - } - - sss << number; - string out = sss.str(); - - return out; -} - -/////////////////////////////////////////////////// -/// \brief Helps to pause the program, printing a message before pausing. -/// -/// ROOT GUI won't be jammed during this pause. -Int_t REST_StringHelper::GetChar(string hint) { - if (gApplication != nullptr && !gApplication->IsRunning()) { - thread t = thread(&TApplication::Run, gApplication, true); - t.detach(); - - cout << hint << endl; - int result = Console::CompatibilityMode ? 1 : getchar(); - gSystem->ExitLoop(); - return result; - } else { - cout << hint << endl; - return Console::CompatibilityMode ? 1 : getchar(); - } - return -1; -} - -/////////////////////////////////////////////// -/// \brief Returns 1 only if a valid number is found in the string **in**. If -/// not it returns 0. -/// -Int_t REST_StringHelper::isANumber(string in) { - return (in.find_first_not_of("-+0123456789.eE") == std::string::npos && in.length() != 0); -} - -/////////////////////////////////////////////// -/// \brief Split the input string according to the given separator. Returning a -/// vector of fragments -/// -/// e.g. -/// Input: "" and "", Output: {} -/// Input: ":" and ":", Output: {} -/// Input: "abc" and "", Output: { "a", "b", "c" } -/// Input: "abc:def" and ":", Output: { "abc", "def" } -/// Input: "abc:def" and ":def", Output: { "abc" } -std::vector REST_StringHelper::Split(std::string in, string separator, bool allowBlankString, - bool removeWhiteSpaces, int startPos) { - std::vector result; - - int pos = startPos; - int front = 0; - while (1) { - pos = in.find(separator.c_str(), pos + 1); - string sub = in.substr(front, pos - front); - if (removeWhiteSpaces) sub = RemoveWhiteSpaces(sub); - if (allowBlankString || sub != "") { - result.push_back(sub); - } - front = pos + separator.size(); - if (pos == -1) break; - } - - return result; -} - -/////////////////////////////////////////////// -/// \brief Convert the input string into a vector of double elements -/// -/// e.g. Input: "1,2,3,4", Output: {1.,2.,3.,4.} -/// -std::vector REST_StringHelper::StringToElements(std::string in, string separator) { - vector result; - vector vec_str = REST_StringHelper::Split(in, separator); - for (unsigned int i = 0; i < vec_str.size(); i++) { - double temp = REST_StringHelper::StringToDouble(vec_str[i]); - result.push_back(temp); - } - - return result; -} - -/////////////////////////////////////////////// -/// \brief Convert the input string `in` into a vector of double elements -/// -/// Called as `StringToElements( in, "[", ",", "]" );` will get the -/// elements from a string with the following format "[a,b,c]" where a,b,c -/// are double numbers. -/// -std::vector REST_StringHelper::StringToElements(std::string in, string headChar, string separator, - string tailChar) { - std::vector result; - size_t startPos = in.find(headChar); - size_t endPos = in.find(tailChar); - if (startPos == string::npos || endPos == string::npos) { - return result; - } - std::vector values = Split(in.substr(startPos + 1, endPos - startPos - 1), ","); - - for (unsigned int i = 0; i < values.size(); i++) { - double temp = REST_StringHelper::StringToDouble(values[i]); - result.push_back(temp); - } - - return result; -} - -/////////////////////////////////////////////// -/// \brief Returns the input string removing all white spaces. -/// -string REST_StringHelper::RemoveWhiteSpaces(string in) { - string out = in; - size_t pos = 0; - while ((pos = out.find(" ", pos)) != string::npos) { - out.erase(pos, 1); - } - - return out; -} - -ULong64_t REST_StringHelper::ToHash(string str) { - ULong64_t prime = 0x100000001B3ull; - ULong64_t basis = 0xCBF29CE484222325ull; - ULong64_t ret{basis}; - - for (int i = 0; i < str.size(); i++) { - ret ^= str[i]; - ret *= prime; - } - - return ret; -} - -/////////////////////////////////////////////// -/// \brief Counts the number of occurences of **substring** inside the input -/// string **in**. -/// -Int_t REST_StringHelper::Count(string in, string substring) { - int count = 0; - size_t nPos = in.find(substring, 0); // First occurrence - while (nPos != string::npos) { - count++; - nPos = in.find(substring, nPos + 1); - } - - return count; -} - -/// \brief Returns the position of the **nth** occurence of the string -/// **strToFind** inside the string **in**. -/// -Int_t REST_StringHelper::FindNthStringPosition(const string& in, size_t pos, const string& strToFind, - size_t nth) { - size_t found_pos = in.find(strToFind, pos); - if (nth == 0 || string::npos == found_pos) return found_pos; - return FindNthStringPosition(in, found_pos + 1, strToFind, nth - 1); -} - -/// \brief Returns the number of different characters between two strings -/// -/// This algorithm is case insensitive. It matches the two strings in pieces -/// after inserting proper blanks, then counts the unmatched part(just a guess). -/// e.g. -/// "woll " -/// "world" -/// 00101 --> 2 -/// -/// " torgae" -/// "Storage" -/// 1000110 --> 3 -/// -/// "fi le" -/// "title" -/// 10100 --> 2 -/// -/// Source code from -/// https://blog.csdn.net/baidu_23086307/article/details/53020566 -/// -Int_t REST_StringHelper::DiffString(const string& source, const string& target) { - int n = source.length(); - int m = target.length(); - if (m == 0) return n; - if (n == 0) return m; - // Construct a matrix - typedef vector > Tmatrix; - Tmatrix matrix(n + 1); - for (int i = 0; i <= n; i++) matrix[i].resize(m + 1); - - // step 2 Initialize - - for (int i = 1; i <= n; i++) matrix[i][0] = i; - for (int i = 1; i <= m; i++) matrix[0][i] = i; - - // step 3 - for (int i = 1; i <= n; i++) { - const char si = source[i - 1]; - // step 4 - for (int j = 1; j <= m; j++) { - const char dj = target[j - 1]; - // step 5 - int cost; - if (si == dj) { - cost = 0; - } else { - cost = 1; - } - // step 6 - const int above = matrix[i - 1][j] + 1; - const int left = matrix[i][j - 1] + 1; - const int diag = matrix[i - 1][j - 1] + cost; - matrix[i][j] = min(above, min(left, diag)); - } - } // step7 - return matrix[n][m]; -} - -/////////////////////////////////////////////// -/// \brief Replace every occurences of **thisSring** by **byThisString** inside -/// string **in**. -/// -string REST_StringHelper::Replace(string in, string thisString, string byThisString, size_t fromPosition, - Int_t N) { - string out = in; - size_t pos = fromPosition; - Int_t cont = 0; - while ((pos = out.find(thisString, pos)) != string::npos) { - out.replace(pos, thisString.length(), byThisString); - pos = pos + byThisString.length(); - cont++; - - if (N > 0 && cont == N) return out; - } - - return out; -} - -std::string REST_StringHelper::EscapeSpecialLetters(string in) { - string result = Replace(in, "(", "\\(", 0); - result = Replace(result, ")", "\\)", 0); - result = Replace(result, "$", "\\$", 0); - result = Replace(result, "#", "\\#", 0); - result = Replace(result, "{", "\\{", 0); - result = Replace(result, "}", "\\}", 0); - result = Replace(result, "<", "\\<", 0); - result = Replace(result, ">", "\\>", 0); - return result; -} - -/////////////////////////////////////////////// -/// \brief Format time_t into string -/// -/// The output datatime format is "Y-M-D H:M:S". e.g. -/// \code -/// REST_StringHelper::ToDateTimeString(0) -/// (return) 1970-1-1 8:00:00 -/// \endcode -/// here the type "time_t" is actually the type "long long", which indicates the -/// elapsed time in second from 1970-1-1 8:00:00 -string REST_StringHelper::ToDateTimeString(time_t time) { - tm* tm_ = localtime(&time); - int year, month, day, hour, minute, second; - year = tm_->tm_year + 1900; - month = tm_->tm_mon + 1; - day = tm_->tm_mday; - hour = tm_->tm_hour; - minute = tm_->tm_min; - second = tm_->tm_sec; - char yearStr[5], monthStr[3], dayStr[3], hourStr[3], minuteStr[3], secondStr[3]; - sprintf(yearStr, "%d", year); - sprintf(monthStr, "%d", month); - sprintf(dayStr, "%d", day); - sprintf(hourStr, "%d", hour); - sprintf(minuteStr, "%d", minute); - if (minuteStr[1] == '\0') { - minuteStr[2] = '\0'; - minuteStr[1] = minuteStr[0]; - minuteStr[0] = '0'; - } - sprintf(secondStr, "%d", second); - if (secondStr[1] == '\0') { - secondStr[2] = '\0'; - secondStr[1] = secondStr[0]; - secondStr[0] = '0'; - } - char s[20]; - sprintf(s, "%s-%s-%s %s:%s:%s", yearStr, monthStr, dayStr, hourStr, minuteStr, secondStr); - string str(s); - return str; -} - -/////////////////////////////////////////////// -/// \brief A method to convert a date/time formatted string to a timestamp. -/// -/// The input datatime format should match any of the following patterns: -/// "YYYY-mm-DD HH:MM:SS", "YYYY/mm/DD HH:MM:SS", "YYYY-mm-DD", or "YYYY/mm/DD". -/// If no time is given it will be assumed to be 00:00:00. -/// -/// \code -/// REST_StringHelper::ToTime("2018-1-1 8:00:00") -/// (return) 1514764800 -/// \endcode -/// -/// here the type "time_t" is actually the type "long long", which indicates the -/// elapsed time in second from 1970-1-1 8:00:00 -/// -time_t REST_StringHelper::StringToTimeStamp(string time) { - struct tm tm1; - tm1.tm_hour = 0; - tm1.tm_min = 0; - tm1.tm_sec = 0; - time_t time1; - if (time.find(":") != string::npos) { - if (time.find("/") != string::npos) - sscanf(time.c_str(), "%d/%d/%d %d:%d:%d", &(tm1.tm_year), &(tm1.tm_mon), &(tm1.tm_mday), - &(tm1.tm_hour), &(tm1.tm_min), &(tm1.tm_sec)); - else - sscanf(time.c_str(), "%d-%d-%d %d:%d:%d", &(tm1.tm_year), &(tm1.tm_mon), &(tm1.tm_mday), - &(tm1.tm_hour), &(tm1.tm_min), &(tm1.tm_sec)); - } else { - if (time.find("/") != string::npos) - sscanf(time.c_str(), "%d/%d/%d", &(tm1.tm_year), &(tm1.tm_mon), &(tm1.tm_mday)); - else - sscanf(time.c_str(), "%d-%d-%d", &(tm1.tm_year), &(tm1.tm_mon), &(tm1.tm_mday)); - } - - tm1.tm_year -= 1900; - tm1.tm_mon--; - tm1.tm_isdst = -1; - time1 = mktime(&tm1); - - return time1; -} - -REST_Verbose_Level REST_StringHelper::StringToVerboseLevel(string in) { - if (ToUpper(in) == "SILENT" || in == "0") return REST_Silent; - if (ToUpper(in) == "ESSENTIAL" || ToUpper(in) == "WARNING" || in == "1") return REST_Essential; - if (ToUpper(in) == "INFO" || in == "2") return REST_Info; - if (ToUpper(in) == "DEBUG" || in == "3") return REST_Debug; - if (ToUpper(in) == "EXTREME" || in == "4") return REST_Extreme; - - return REST_Essential; -} - -/////////////////////////////////////////////// -/// \brief Gets a double from a string. -/// -Double_t REST_StringHelper::StringToDouble(string in) { - if (isANumber(in)) { - return stod(in); - } else { - return -1; - } -} - -/////////////////////////////////////////////// -/// \brief Gets a float from a string. -/// -Float_t REST_StringHelper::StringToFloat(string in) { - if (isANumber(in)) { - return stof(in); - } else { - return -1; - } -} - -/////////////////////////////////////////////// -/// \brief Gets an integer from a string. -/// -Int_t REST_StringHelper::StringToInteger(string in) { - // If we find an hexadecimal number - if (in.find("0x") != std::string::npos) return (Int_t)std::stoul(in, nullptr, 16); - - return (Int_t)StringToDouble(in); -} - -/////////////////////////////////////////////// -/// \brief Gets a string from an integer. -/// -string REST_StringHelper::IntegerToString(Int_t n) { return Form("%d", n); } - -/////////////////////////////////////////////// -/// \brief Gets a string from a double -/// -string REST_StringHelper::DoubleToString(Double_t d) { return Form("%4.2lf", d); } - -Bool_t REST_StringHelper::StringToBool(std::string in) { - return (ToUpper(in) == "TRUE" || ToUpper(in) == "ON"); -} - -Long64_t REST_StringHelper::StringToLong(std::string in) { - if (in.find_first_of("eE") != string::npos) { - // in case for scientific numbers - return (Long64_t)StringToDouble(in); - } - stringstream strIn; - strIn << in; - long long llNum; - strIn >> llNum; - return llNum; -} - -/////////////////////////////////////////////// -/// \brief Gets a 3D-vector from a string. Format should be : (X,Y,Z). -/// -TVector3 REST_StringHelper::StringTo3DVector(string in) { - TVector3 a; - - size_t startVector = in.find_first_of("("); - if (startVector == string::npos) return a; - - size_t endVector = in.find_first_of(")"); - if (endVector == string::npos) return a; - - size_t n = count(in.begin(), in.end(), ','); - if (n != 2) return a; - - size_t firstComma = in.find_first_of(","); - size_t secondComma = in.find(",", firstComma + 1); - - if (firstComma >= endVector || firstComma <= startVector) return a; - if (secondComma >= endVector || secondComma <= startVector) return a; - - string X = in.substr(startVector + 1, firstComma - startVector - 1); - string Y = in.substr(firstComma + 1, secondComma - firstComma - 1); - string Z = in.substr(secondComma + 1, endVector - secondComma - 1); - - a.SetXYZ(StringToDouble(X), StringToDouble(Y), StringToDouble(Z)); - - return a; -} - -/////////////////////////////////////////////// -/// \brief Gets a 2D-vector from a string. -/// -TVector2 REST_StringHelper::StringTo2DVector(string in) { - TVector2 a(-1, -1); - - size_t startVector = in.find_first_of("("); - if (startVector == string::npos) return a; - - size_t endVector = in.find_first_of(")"); - if (endVector == string::npos) return a; - - size_t n = count(in.begin(), in.end(), ','); - if (n != 1) return a; - - size_t firstComma = in.find_first_of(","); - - if (firstComma >= endVector || firstComma <= startVector) return a; - - string X = in.substr(startVector + 1, firstComma - startVector - 1); - string Y = in.substr(firstComma + 1, endVector - firstComma - 1); - - a.Set(StringToDouble(X), StringToDouble(Y)); - - return a; -} - -/////////////////////////////////////////////// -/// \brief Convert string to its upper case. Alternative of TString::ToUpper -/// -std::string REST_StringHelper::ToUpper(std::string s) { - transform(s.begin(), s.end(), s.begin(), (int (*)(int))toupper); - return s; -} - -/////////////////////////////////////////////// -/// \brief Convert string to its lower case. Alternative of TString::ToLower -/// -std::string REST_StringHelper::ToLower(std::string s) { - transform(s.begin(), s.end(), s.begin(), (int (*)(int))tolower); - return s; -} - -/////////////////////////////////////////////// -/// \brief Removes all white spaces found at the end of the string -/// (https://stackoverflow.com/questions/216823/whats-the-best-way-to-trim-stdstring) -/// -std::string REST_StringHelper::RightTrim(std::string s, const char* t) { - s.erase(s.find_last_not_of(t) + 1); - return s; -} - -/////////////////////////////////////////////// -/// \brief Removes all white spaces found at the beginning of the string -/// (https://stackoverflow.com/questions/216823/whats-the-best-way-to-trim-stdstring) -/// -std::string REST_StringHelper::LeftTrim(std::string s, const char* t) { - s.erase(0, s.find_first_not_of(t)); - return s; -} - -/////////////////////////////////////////////// -/// \brief Removes all white spaces found at the beginning and the end of the string -/// (https://stackoverflow.com/questions/216823/whats-the-best-way-to-trim-stdstring) -/// -std::string REST_StringHelper::Trim(std::string s, const char* t) { return LeftTrim(RightTrim(s, t), t); } - -/////////////////////////////////////////////// -/// \brief It trims and uppers the string -/// -std::string REST_StringHelper::TrimAndUpper(std::string s) { - s = Trim(s); - s = ToUpper(s); - return s; -} - -/////////////////////////////////////////////// -/// \brief It trims and lowers the string -/// -std::string REST_StringHelper::TrimAndLower(std::string s) { - s = Trim(s); - s = ToLower(s); - return s; -} - -/////////////////////////////////////////////// -/// \brief Convert data member name to parameter name, following REST parameter naming convention. -/// -/// > The name of class data member, if starts from “f” and have the second character in -/// capital form, will be linked to a parameter. The linked parameter will strip the first -/// “f” and have the first letter in lowercase. For example, data member “fTargetName” is -/// linked to parameter “targetName”. -string REST_StringHelper::DataMemberNameToParameterName(string name) { - if (name == "") { - return ""; - } - if (name[0] == 'f' && name.size() > 1 && (name[1] >= 65 && name[1] <= 90)) { - return string(1, tolower(name[1])) + name.substr(2, -1); - } else { - return ""; - } -} - -/////////////////////////////////////////////// -/// \brief Convert parameter name to datamember name, following REST parameter naming convention. -/// -/// > The name of class data member, if starts from “f” and have the second character in -/// capital form, will be linked to a parameter. The linked parameter will strip the first -/// “f” and have the first letter in lowercase. For example, data member “fTargetName” is -/// linked to parameter “targetName”. -string REST_StringHelper::ParameterNameToDataMemberName(string name) { - if (name == "") { - return ""; - } - if (islower(name[0])) { - return "f" + string(1, toupper(name[0])) + name.substr(1, -1); - } else { - return ""; - } -} - -///////////////////////////////////////////// -/// \brief Reads a function with parameter options from string and returns it as TF1*. -/// -/// > The function is defined as a string following ROOT::TFormula conventions for parameters. -/// Inside the square brackets we allow parameter initialization, constant parameter and ranges. -/// This has been created for fitting functions, where each parameter has its own restrictions. -/// -/// Examples: -/// -- Initial value: [0=3.5] -/// -- Fixed value: [0==3.5] -/// -- Range: [0=3.5(1,5)] The parameter 0 begin at 3.5 and it can move between 1 and 5. -/// -/// All parameters should be initialized. -/// -/// Input arguments: -/// * String with function and parameter options. -/// * Two doubles with the range of the function. -/// -/// Output: TF1* with the interpreted fuction. It contains all restrictions, ranges, etc. -/// -/// \warning This object will create a new TF1 object in memory, so the code creating a -/// TF1 object using this method as a helper will be responsible to delete this object from -/// memory. -/// -/// \code -/// TF1* ff = CreateTF1FromString(xxx); -/// ... -/// ff->DoSomething(); -/// ... -/// delete ff; -/// \endcode -/// -TF1* REST_StringHelper::CreateTF1FromString(std::string func, double init, double end) { - string tf1 = func; - // Number of parameters - size_t n = std::count(func.begin(), func.end(), '['); - // Reading options - int a = 0; - int optPos[n]; - std::vector options(n); // Vector of strings of any size. - for (int i = 0; i < n; i++) { - optPos[i] = func.find("[", a); - options[i] = - func.substr(func.find("[", a) + 1, func.find("]", func.find("[", a)) - func.find("[", a) - 1); - a = func.find("[", a) + 1; - } - // Removing options from function string - for (int i = 0; i < n; i++) { - tf1.replace(optPos[n - 1 - i] + 1, (func.find("]", optPos[n - 1 - i]) - optPos[n - 1 - i] - 1), - std::string(1, func[optPos[n - 1 - i] + 1])); - } - - // Function - const char* tf1c = tf1.c_str(); - TF1* f = new TF1("f", tf1c, init, end); - - // Initial conditions - for (int i = 0; i < n; i++) { - if (options[i].find("=") != std::string::npos) { - string op = options[i].substr(options[i].find_last_of("=") + 1, - options[i].find("(") - options[i].find_last_of("=") - 1); - if (isANumber(op)) { - f->SetParameter(i, stod(op)); - } else { - cout << "Initial condition for parameter " << i << " is not a number: " << op << endl; - } - } else { - cout << "No initial condition given for parameter " << i << "!" << endl; - } - } - - // Fixed values - for (int i = 0; i < n; i++) { - if (std::count(options[i].begin(), options[i].end(), '=') == 2) { - string op = options[i].substr(options[i].find_last_of("=") + 1, - options[i].find("(") - options[i].find_last_of("=") - 1); - if (isANumber(op)) { - f->FixParameter(i, stod(op)); - } - // cout << "Parameter " << i << " fixed with value " << op << endl; - } - } - - // Ranges - for (int i = 0; i < n; i++) { - if (options[i].find("(") != std::string::npos) { - string op = - options[i].substr(options[i].find("(") + 1, options[i].find(")") - options[i].find("(") - 1); - f->SetParLimits(i, stod(op.substr(0, op.find(","))), - stod(op.substr(op.find(",") + 1, op.size() - 1 - op.find(",")))); - // cout << "Parameter " << i << " range " << "(" << stod(op.substr(0, op.find(","))) << "," << - // stod(op.substr(op.find(",")+1, op.size()-1-op.find(",") )) << ")" << endl; - } - } - - return f; -} - -#ifdef WIN32 -string get_current_dir_name() { - char pBuf[MAX_PATH]; - GetCurrentDirectory(MAX_PATH, pBuf); - return string(pBuf); -} -#endif +#include "TRestStringHelper.h" + +#include + +#include "Rtypes.h" +#include "TApplication.h" +#include "TSystem.h" + +#if ROOT_VERSION_CODE < ROOT_VERSION(6, 0, 0) +#include "TFormula.h" +#else +#include "v5/TFormula.h" +#endif + +#include +#include + +using namespace std; + +/////////////////////////////////////////////// +/// \brief Returns 1 only if valid mathematical expression keywords (or numbers) +/// are found in the string **in**. If not it returns 0. +/// +/// By logic, mathematical expressions must have: +-*/e^% in the middle, or % in the end, or math functions in the beginning. +/// despite those symbols, the string should be purely numeric. +/// example: +/// 1+1 --> expression +/// sin(1.5) --> expression +/// 123456789 --> not expression, It is a pure number that can be directly parsed. +Int_t REST_StringHelper::isAExpression(string in) { + bool symbol = false; + bool numeric = false; + + if (in.length() < 2) // minimum expression: 3% + return 0; + + vector funcs{"sqrt", "log", "exp", "gaus", "cos", "sin", "tan", "atan", "acos", "asin"}; + for (int i = 0; i < funcs.size(); i++) { + if (in.find(funcs[i]) != std::string::npos) { + symbol = true; + break; + } + } + + if (!symbol) { + int pos = in.find_first_of("+-*/e^%"); + if (pos > 0 && pos < in.size() - 1) { + symbol = true; + } + } + + if (!symbol) { + int pos = in.find_first_of("%"); + if (pos == in.size() - 1) { + symbol = true; + } + } + + if (symbol) { + string temp = in; + for (int i = 0; i < funcs.size(); i++) { + temp = Replace(temp, funcs[i], "0", 0); + } + if (temp.find_first_not_of("-0123456789e+*/.,)( ^%") == std::string::npos) { + if (temp.find("/") == 0 || temp.find("./") == 0 || temp.find("../") == 0) + return 0; // identify path + return 1; + } + } + else + { + return 0; + } + + return 0; +} + +/////////////////////////////////////////////// +/// \brief Evaluates and replaces valid mathematical expressions found in the +/// input string **buffer**. +/// +std::string REST_StringHelper::ReplaceMathematicalExpressions(std::string buffer, std::string errorMessage) { + buffer = Replace(buffer, " AND ", " && "); + buffer = Replace(buffer, " OR ", " || "); + // we spilt the unit part and the expresstion part + int pos = buffer.find_last_of("1234567890()."); + + string unit = buffer.substr(pos + 1, -1); + string temp = buffer.substr(0, pos + 1); + string result = ""; + + bool erased = false; + + std::vector Expressions = Split(temp, ","); + + if (Expressions.size() > 1 && Expressions[0][0] == '(' && + Expressions[Expressions.size() - 1][Expressions[Expressions.size() - 1].size() - 1] == ')') { + Expressions[0].erase(0, 1); + Expressions[Expressions.size() - 1].erase(Expressions[Expressions.size() - 1].size() - 1, 1); + erased = true; + } + + for (int i = 0; i < Expressions.size(); i++) { + if (!isAExpression(Expressions[i])) { + result += Expressions[i] + ","; + continue; + } + string evaluated = EvaluateExpression(Expressions[i]); + if (evaluated == "RESTerror") { + result += Expressions[i] + ","; + ferr << "ReplaceMathematicalExpressions. Error on RML syntax!" << endl; + if (errorMessage != "") ferr << errorMessage << endl; + } else + result += evaluated + ","; + } + if (Expressions.size() > 0) result.erase(result.size() - 1, 1); + + if (erased) { + result = "(" + result + ")"; + } + + return result + unit; +} + +/////////////////////////////////////////////// +/// \brief Evaluates a complex numerical expression and returns the resulting +/// value using TFormula. +/// +std::string REST_StringHelper::EvaluateExpression(std::string exp) { + if (!isAExpression(exp)) { + return exp; + } + +// NOTE!!! In root6 the expression like "1/2" will be computed using the input +// as int number, which will return 0, and cause problem. we roll back to +// TFormula of version 5 +#if ROOT_VERSION_CODE < ROOT_VERSION(6, 0, 0) + TFormula formula("tmp", exp.c_str()); +#else + ROOT::v5::TFormula formula("tmp", exp.c_str()); +#endif + + ostringstream sss; + Double_t number = formula.EvalPar(0); + if (number > 0 && number < 1.e-300) { + warning << "REST_StringHelper::EvaluateExpresssion. Expression not recognized --> " << exp << endl; + return (string) "RESTerror"; + } + + sss << number; + string out = sss.str(); + + return out; +} + +/////////////////////////////////////////////////// +/// \brief Helps to pause the program, printing a message before pausing. +/// +/// ROOT GUI won't be jammed during this pause. +Int_t REST_StringHelper::GetChar(string hint) { + if (gApplication != nullptr && !gApplication->IsRunning()) { + thread t = thread(&TApplication::Run, gApplication, true); + t.detach(); + + cout << hint << endl; + int result = Console::CompatibilityMode ? 1 : getchar(); + gSystem->ExitLoop(); + return result; + } else { + cout << hint << endl; + return Console::CompatibilityMode ? 1 : getchar(); + } + return -1; +} + +/////////////////////////////////////////////// +/// \brief Returns 1 only if a valid number is found in the string **in**. If +/// not it returns 0. +/// +Int_t REST_StringHelper::isANumber(string in) { + return (in.find_first_not_of("-+0123456789.eE") == std::string::npos && in.length() != 0); +} + +/////////////////////////////////////////////// +/// \brief Split the input string according to the given separator. Returning a +/// vector of fragments +/// +/// e.g. +/// Input: "" and "", Output: {} +/// Input: ":" and ":", Output: {} +/// Input: "abc" and "", Output: { "a", "b", "c" } +/// Input: "abc:def" and ":", Output: { "abc", "def" } +/// Input: "abc:def" and ":def", Output: { "abc" } +std::vector REST_StringHelper::Split(std::string in, string separator, bool allowBlankString, + bool removeWhiteSpaces, int startPos) { + std::vector result; + + int pos = startPos; + int front = 0; + while (1) { + pos = in.find(separator.c_str(), pos + 1); + string sub = in.substr(front, pos - front); + if (removeWhiteSpaces) sub = RemoveWhiteSpaces(sub); + if (allowBlankString || sub != "") { + result.push_back(sub); + } + front = pos + separator.size(); + if (pos == -1) break; + } + + return result; +} + +/////////////////////////////////////////////// +/// \brief Convert the input string into a vector of double elements +/// +/// e.g. Input: "1,2,3,4", Output: {1.,2.,3.,4.} +/// +std::vector REST_StringHelper::StringToElements(std::string in, string separator) { + vector result; + vector vec_str = REST_StringHelper::Split(in, separator); + for (unsigned int i = 0; i < vec_str.size(); i++) { + double temp = REST_StringHelper::StringToDouble(vec_str[i]); + result.push_back(temp); + } + + return result; +} + +/////////////////////////////////////////////// +/// \brief Convert the input string `in` into a vector of double elements +/// +/// Called as `StringToElements( in, "[", ",", "]" );` will get the +/// elements from a string with the following format "[a,b,c]" where a,b,c +/// are double numbers. +/// +std::vector REST_StringHelper::StringToElements(std::string in, string headChar, string separator, + string tailChar) { + std::vector result; + size_t startPos = in.find(headChar); + size_t endPos = in.find(tailChar); + if (startPos == string::npos || endPos == string::npos) { + return result; + } + std::vector values = Split(in.substr(startPos + 1, endPos - startPos - 1), ","); + + for (unsigned int i = 0; i < values.size(); i++) { + double temp = REST_StringHelper::StringToDouble(values[i]); + result.push_back(temp); + } + + return result; +} + +/////////////////////////////////////////////// +/// \brief Returns the input string removing all white spaces. +/// +string REST_StringHelper::RemoveWhiteSpaces(string in) { + string out = in; + size_t pos = 0; + while ((pos = out.find(" ", pos)) != string::npos) { + out.erase(pos, 1); + } + + return out; +} + +ULong64_t REST_StringHelper::ToHash(string str) { + ULong64_t prime = 0x100000001B3ull; + ULong64_t basis = 0xCBF29CE484222325ull; + ULong64_t ret{basis}; + + for (int i = 0; i < str.size(); i++) { + ret ^= str[i]; + ret *= prime; + } + + return ret; +} + +/////////////////////////////////////////////// +/// \brief Counts the number of occurences of **substring** inside the input +/// string **in**. +/// +Int_t REST_StringHelper::Count(string in, string substring) { + int count = 0; + size_t nPos = in.find(substring, 0); // First occurrence + while (nPos != string::npos) { + count++; + nPos = in.find(substring, nPos + 1); + } + + return count; +} + +/// \brief Returns the position of the **nth** occurence of the string +/// **strToFind** inside the string **in**. +/// +Int_t REST_StringHelper::FindNthStringPosition(const string& in, size_t pos, const string& strToFind, + size_t nth) { + size_t found_pos = in.find(strToFind, pos); + if (nth == 0 || string::npos == found_pos) return found_pos; + return FindNthStringPosition(in, found_pos + 1, strToFind, nth - 1); +} + +/// \brief Returns the number of different characters between two strings +/// +/// This algorithm is case insensitive. It matches the two strings in pieces +/// after inserting proper blanks, then counts the unmatched part(just a guess). +/// e.g. +/// "woll " +/// "world" +/// 00101 --> 2 +/// +/// " torgae" +/// "Storage" +/// 1000110 --> 3 +/// +/// "fi le" +/// "title" +/// 10100 --> 2 +/// +/// Source code from +/// https://blog.csdn.net/baidu_23086307/article/details/53020566 +/// +Int_t REST_StringHelper::DiffString(const string& source, const string& target) { + int n = source.length(); + int m = target.length(); + if (m == 0) return n; + if (n == 0) return m; + // Construct a matrix + typedef vector > Tmatrix; + Tmatrix matrix(n + 1); + for (int i = 0; i <= n; i++) matrix[i].resize(m + 1); + + // step 2 Initialize + + for (int i = 1; i <= n; i++) matrix[i][0] = i; + for (int i = 1; i <= m; i++) matrix[0][i] = i; + + // step 3 + for (int i = 1; i <= n; i++) { + const char si = source[i - 1]; + // step 4 + for (int j = 1; j <= m; j++) { + const char dj = target[j - 1]; + // step 5 + int cost; + if (si == dj) { + cost = 0; + } else { + cost = 1; + } + // step 6 + const int above = matrix[i - 1][j] + 1; + const int left = matrix[i][j - 1] + 1; + const int diag = matrix[i - 1][j - 1] + cost; + matrix[i][j] = min(above, min(left, diag)); + } + } // step7 + return matrix[n][m]; +} + +/////////////////////////////////////////////// +/// \brief Replace every occurences of **thisSring** by **byThisString** inside +/// string **in**. +/// +string REST_StringHelper::Replace(string in, string thisString, string byThisString, size_t fromPosition, + Int_t N) { + string out = in; + size_t pos = fromPosition; + Int_t cont = 0; + while ((pos = out.find(thisString, pos)) != string::npos) { + out.replace(pos, thisString.length(), byThisString); + pos = pos + byThisString.length(); + cont++; + + if (N > 0 && cont == N) return out; + } + + return out; +} + +std::string REST_StringHelper::EscapeSpecialLetters(string in) { + string result = Replace(in, "(", "\\(", 0); + result = Replace(result, ")", "\\)", 0); + result = Replace(result, "$", "\\$", 0); + result = Replace(result, "#", "\\#", 0); + result = Replace(result, "{", "\\{", 0); + result = Replace(result, "}", "\\}", 0); + result = Replace(result, "<", "\\<", 0); + result = Replace(result, ">", "\\>", 0); + return result; +} + +/////////////////////////////////////////////// +/// \brief Format time_t into string +/// +/// The output datatime format is "Y-M-D H:M:S". e.g. +/// \code +/// REST_StringHelper::ToDateTimeString(0) +/// (return) 1970-1-1 8:00:00 +/// \endcode +/// here the type "time_t" is actually the type "long long", which indicates the +/// elapsed time in second from 1970-1-1 8:00:00 +string REST_StringHelper::ToDateTimeString(time_t time) { + tm* tm_ = localtime(&time); + int year, month, day, hour, minute, second; + year = tm_->tm_year + 1900; + month = tm_->tm_mon + 1; + day = tm_->tm_mday; + hour = tm_->tm_hour; + minute = tm_->tm_min; + second = tm_->tm_sec; + char yearStr[5], monthStr[3], dayStr[3], hourStr[3], minuteStr[3], secondStr[3]; + sprintf(yearStr, "%d", year); + sprintf(monthStr, "%d", month); + sprintf(dayStr, "%d", day); + sprintf(hourStr, "%d", hour); + sprintf(minuteStr, "%d", minute); + if (minuteStr[1] == '\0') { + minuteStr[2] = '\0'; + minuteStr[1] = minuteStr[0]; + minuteStr[0] = '0'; + } + sprintf(secondStr, "%d", second); + if (secondStr[1] == '\0') { + secondStr[2] = '\0'; + secondStr[1] = secondStr[0]; + secondStr[0] = '0'; + } + char s[20]; + sprintf(s, "%s-%s-%s %s:%s:%s", yearStr, monthStr, dayStr, hourStr, minuteStr, secondStr); + string str(s); + return str; +} + +/////////////////////////////////////////////// +/// \brief A method to convert a date/time formatted string to a timestamp. +/// +/// The input datatime format should match any of the following patterns: +/// "YYYY-mm-DD HH:MM:SS", "YYYY/mm/DD HH:MM:SS", "YYYY-mm-DD", or "YYYY/mm/DD". +/// If no time is given it will be assumed to be 00:00:00. +/// +/// \code +/// REST_StringHelper::ToTime("2018-1-1 8:00:00") +/// (return) 1514764800 +/// \endcode +/// +/// here the type "time_t" is actually the type "long long", which indicates the +/// elapsed time in second from 1970-1-1 8:00:00 +/// +time_t REST_StringHelper::StringToTimeStamp(string time) { + struct tm tm1; + tm1.tm_hour = 0; + tm1.tm_min = 0; + tm1.tm_sec = 0; + time_t time1; + if (time.find(":") != string::npos) { + if (time.find("/") != string::npos) + sscanf(time.c_str(), "%d/%d/%d %d:%d:%d", &(tm1.tm_year), &(tm1.tm_mon), &(tm1.tm_mday), + &(tm1.tm_hour), &(tm1.tm_min), &(tm1.tm_sec)); + else + sscanf(time.c_str(), "%d-%d-%d %d:%d:%d", &(tm1.tm_year), &(tm1.tm_mon), &(tm1.tm_mday), + &(tm1.tm_hour), &(tm1.tm_min), &(tm1.tm_sec)); + } else { + if (time.find("/") != string::npos) + sscanf(time.c_str(), "%d/%d/%d", &(tm1.tm_year), &(tm1.tm_mon), &(tm1.tm_mday)); + else + sscanf(time.c_str(), "%d-%d-%d", &(tm1.tm_year), &(tm1.tm_mon), &(tm1.tm_mday)); + } + + tm1.tm_year -= 1900; + tm1.tm_mon--; + tm1.tm_isdst = -1; + time1 = mktime(&tm1); + + return time1; +} + +REST_Verbose_Level REST_StringHelper::StringToVerboseLevel(string in) { + if (ToUpper(in) == "SILENT" || in == "0") return REST_Silent; + if (ToUpper(in) == "ESSENTIAL" || ToUpper(in) == "WARNING" || in == "1") return REST_Essential; + if (ToUpper(in) == "INFO" || in == "2") return REST_Info; + if (ToUpper(in) == "DEBUG" || in == "3") return REST_Debug; + if (ToUpper(in) == "EXTREME" || in == "4") return REST_Extreme; + + return REST_Essential; +} + +/////////////////////////////////////////////// +/// \brief Gets a double from a string. +/// +Double_t REST_StringHelper::StringToDouble(string in) { + if (isANumber(in)) { + return stod(in); + } else { + return -1; + } +} + +/////////////////////////////////////////////// +/// \brief Gets a float from a string. +/// +Float_t REST_StringHelper::StringToFloat(string in) { + if (isANumber(in)) { + return stof(in); + } else { + return -1; + } +} + +/////////////////////////////////////////////// +/// \brief Gets an integer from a string. +/// +Int_t REST_StringHelper::StringToInteger(string in) { + // If we find an hexadecimal number + if (in.find("0x") != std::string::npos) return (Int_t)std::stoul(in, nullptr, 16); + + return (Int_t)StringToDouble(in); +} + +/////////////////////////////////////////////// +/// \brief Gets a string from an integer. +/// +string REST_StringHelper::IntegerToString(Int_t n) { return Form("%d", n); } + +/////////////////////////////////////////////// +/// \brief Gets a string from a double +/// +string REST_StringHelper::DoubleToString(Double_t d) { return Form("%4.2lf", d); } + +Bool_t REST_StringHelper::StringToBool(std::string in) { + return (ToUpper(in) == "TRUE" || ToUpper(in) == "ON"); +} + +Long64_t REST_StringHelper::StringToLong(std::string in) { + if (in.find_first_of("eE") != string::npos) { + // in case for scientific numbers + return (Long64_t)StringToDouble(in); + } + stringstream strIn; + strIn << in; + long long llNum; + strIn >> llNum; + return llNum; +} + +/////////////////////////////////////////////// +/// \brief Gets a 3D-vector from a string. Format should be : (X,Y,Z). +/// +TVector3 REST_StringHelper::StringTo3DVector(string in) { + TVector3 a; + + size_t startVector = in.find_first_of("("); + if (startVector == string::npos) return a; + + size_t endVector = in.find_first_of(")"); + if (endVector == string::npos) return a; + + size_t n = count(in.begin(), in.end(), ','); + if (n != 2) return a; + + size_t firstComma = in.find_first_of(","); + size_t secondComma = in.find(",", firstComma + 1); + + if (firstComma >= endVector || firstComma <= startVector) return a; + if (secondComma >= endVector || secondComma <= startVector) return a; + + string X = in.substr(startVector + 1, firstComma - startVector - 1); + string Y = in.substr(firstComma + 1, secondComma - firstComma - 1); + string Z = in.substr(secondComma + 1, endVector - secondComma - 1); + + a.SetXYZ(StringToDouble(X), StringToDouble(Y), StringToDouble(Z)); + + return a; +} + +/////////////////////////////////////////////// +/// \brief Gets a 2D-vector from a string. +/// +TVector2 REST_StringHelper::StringTo2DVector(string in) { + TVector2 a(-1, -1); + + size_t startVector = in.find_first_of("("); + if (startVector == string::npos) return a; + + size_t endVector = in.find_first_of(")"); + if (endVector == string::npos) return a; + + size_t n = count(in.begin(), in.end(), ','); + if (n != 1) return a; + + size_t firstComma = in.find_first_of(","); + + if (firstComma >= endVector || firstComma <= startVector) return a; + + string X = in.substr(startVector + 1, firstComma - startVector - 1); + string Y = in.substr(firstComma + 1, endVector - firstComma - 1); + + a.Set(StringToDouble(X), StringToDouble(Y)); + + return a; +} + +/////////////////////////////////////////////// +/// \brief Convert string to its upper case. Alternative of TString::ToUpper +/// +std::string REST_StringHelper::ToUpper(std::string s) { + transform(s.begin(), s.end(), s.begin(), (int (*)(int))toupper); + return s; +} + +/////////////////////////////////////////////// +/// \brief Convert string to its lower case. Alternative of TString::ToLower +/// +std::string REST_StringHelper::ToLower(std::string s) { + transform(s.begin(), s.end(), s.begin(), (int (*)(int))tolower); + return s; +} + +/////////////////////////////////////////////// +/// \brief Removes all white spaces found at the end of the string +/// (https://stackoverflow.com/questions/216823/whats-the-best-way-to-trim-stdstring) +/// +std::string REST_StringHelper::RightTrim(std::string s, const char* t) { + s.erase(s.find_last_not_of(t) + 1); + return s; +} + +/////////////////////////////////////////////// +/// \brief Removes all white spaces found at the beginning of the string +/// (https://stackoverflow.com/questions/216823/whats-the-best-way-to-trim-stdstring) +/// +std::string REST_StringHelper::LeftTrim(std::string s, const char* t) { + s.erase(0, s.find_first_not_of(t)); + return s; +} + +/////////////////////////////////////////////// +/// \brief Removes all white spaces found at the beginning and the end of the string +/// (https://stackoverflow.com/questions/216823/whats-the-best-way-to-trim-stdstring) +/// +std::string REST_StringHelper::Trim(std::string s, const char* t) { return LeftTrim(RightTrim(s, t), t); } + +/////////////////////////////////////////////// +/// \brief It trims and uppers the string +/// +std::string REST_StringHelper::TrimAndUpper(std::string s) { + s = Trim(s); + s = ToUpper(s); + return s; +} + +/////////////////////////////////////////////// +/// \brief It trims and lowers the string +/// +std::string REST_StringHelper::TrimAndLower(std::string s) { + s = Trim(s); + s = ToLower(s); + return s; +} + +/////////////////////////////////////////////// +/// \brief Convert data member name to parameter name, following REST parameter naming convention. +/// +/// > The name of class data member, if starts from “f” and have the second character in +/// capital form, will be linked to a parameter. The linked parameter will strip the first +/// “f” and have the first letter in lowercase. For example, data member “fTargetName” is +/// linked to parameter “targetName”. +string REST_StringHelper::DataMemberNameToParameterName(string name) { + if (name == "") { + return ""; + } + if (name[0] == 'f' && name.size() > 1 && (name[1] >= 65 && name[1] <= 90)) { + return string(1, tolower(name[1])) + name.substr(2, -1); + } else { + return ""; + } +} + +/////////////////////////////////////////////// +/// \brief Convert parameter name to datamember name, following REST parameter naming convention. +/// +/// > The name of class data member, if starts from “f” and have the second character in +/// capital form, will be linked to a parameter. The linked parameter will strip the first +/// “f” and have the first letter in lowercase. For example, data member “fTargetName” is +/// linked to parameter “targetName”. +string REST_StringHelper::ParameterNameToDataMemberName(string name) { + if (name == "") { + return ""; + } + if (islower(name[0])) { + return "f" + string(1, toupper(name[0])) + name.substr(1, -1); + } else { + return ""; + } +} + +///////////////////////////////////////////// +/// \brief Reads a function with parameter options from string and returns it as TF1*. +/// +/// > The function is defined as a string following ROOT::TFormula conventions for parameters. +/// Inside the square brackets we allow parameter initialization, constant parameter and ranges. +/// This has been created for fitting functions, where each parameter has its own restrictions. +/// +/// Examples: +/// -- Initial value: [0=3.5] +/// -- Fixed value: [0==3.5] +/// -- Range: [0=3.5(1,5)] The parameter 0 begin at 3.5 and it can move between 1 and 5. +/// +/// All parameters should be initialized. +/// +/// Input arguments: +/// * String with function and parameter options. +/// * Two doubles with the range of the function. +/// +/// Output: TF1* with the interpreted fuction. It contains all restrictions, ranges, etc. +/// +/// \warning This object will create a new TF1 object in memory, so the code creating a +/// TF1 object using this method as a helper will be responsible to delete this object from +/// memory. +/// +/// \code +/// TF1* ff = CreateTF1FromString(xxx); +/// ... +/// ff->DoSomething(); +/// ... +/// delete ff; +/// \endcode +/// +TF1* REST_StringHelper::CreateTF1FromString(std::string func, double init, double end) { + string tf1 = func; + // Number of parameters + size_t n = std::count(func.begin(), func.end(), '['); + // Reading options + int a = 0; + int optPos[n]; + std::vector options(n); // Vector of strings of any size. + for (int i = 0; i < n; i++) { + optPos[i] = func.find("[", a); + options[i] = + func.substr(func.find("[", a) + 1, func.find("]", func.find("[", a)) - func.find("[", a) - 1); + a = func.find("[", a) + 1; + } + // Removing options from function string + for (int i = 0; i < n; i++) { + tf1.replace(optPos[n - 1 - i] + 1, (func.find("]", optPos[n - 1 - i]) - optPos[n - 1 - i] - 1), + std::string(1, func[optPos[n - 1 - i] + 1])); + } + + // Function + const char* tf1c = tf1.c_str(); + TF1* f = new TF1("f", tf1c, init, end); + + // Initial conditions + for (int i = 0; i < n; i++) { + if (options[i].find("=") != std::string::npos) { + string op = options[i].substr(options[i].find_last_of("=") + 1, + options[i].find("(") - options[i].find_last_of("=") - 1); + if (isANumber(op)) { + f->SetParameter(i, stod(op)); + } else { + cout << "Initial condition for parameter " << i << " is not a number: " << op << endl; + } + } else { + cout << "No initial condition given for parameter " << i << "!" << endl; + } + } + + // Fixed values + for (int i = 0; i < n; i++) { + if (std::count(options[i].begin(), options[i].end(), '=') == 2) { + string op = options[i].substr(options[i].find_last_of("=") + 1, + options[i].find("(") - options[i].find_last_of("=") - 1); + if (isANumber(op)) { + f->FixParameter(i, stod(op)); + } + // cout << "Parameter " << i << " fixed with value " << op << endl; + } + } + + // Ranges + for (int i = 0; i < n; i++) { + if (options[i].find("(") != std::string::npos) { + string op = + options[i].substr(options[i].find("(") + 1, options[i].find(")") - options[i].find("(") - 1); + f->SetParLimits(i, stod(op.substr(0, op.find(","))), + stod(op.substr(op.find(",") + 1, op.size() - 1 - op.find(",")))); + // cout << "Parameter " << i << " range " << "(" << stod(op.substr(0, op.find(","))) << "," << + // stod(op.substr(op.find(",")+1, op.size()-1-op.find(",") )) << ")" << endl; + } + } + + return f; +} + +#ifdef WIN32 +string get_current_dir_name() { + char pBuf[MAX_PATH]; + GetCurrentDirectory(MAX_PATH, pBuf); + return string(pBuf); +} +#endif From 73dc883be4bdfd786a917753db53abc61f4b7a91 Mon Sep 17 00:00:00 2001 From: Date: Thu, 28 Apr 2022 03:32:35 +0800 Subject: [PATCH 04/28] update TRestStringHelper --- source/framework/tools/src/TRestStringHelper.cxx | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/source/framework/tools/src/TRestStringHelper.cxx b/source/framework/tools/src/TRestStringHelper.cxx index 382e14dd9..806e84caf 100644 --- a/source/framework/tools/src/TRestStringHelper.cxx +++ b/source/framework/tools/src/TRestStringHelper.cxx @@ -27,16 +27,17 @@ using namespace std; /// 1+1 --> expression /// sin(1.5) --> expression /// 123456789 --> not expression, It is a pure number that can be directly parsed. +/// ./123 --> not expression, it is a path +/// 333/555 --> is expression. But it may also be a path. We should avoid using paths like that Int_t REST_StringHelper::isAExpression(string in) { bool symbol = false; - bool numeric = false; if (in.length() < 2) // minimum expression: 3% return 0; vector funcs{"sqrt", "log", "exp", "gaus", "cos", "sin", "tan", "atan", "acos", "asin"}; - for (int i = 0; i < funcs.size(); i++) { - if (in.find(funcs[i]) != std::string::npos) { + for (const auto& item : funcs) { + if (in.find(item) != std::string::npos) { symbol = true; break; } @@ -58,8 +59,8 @@ Int_t REST_StringHelper::isAExpression(string in) { if (symbol) { string temp = in; - for (int i = 0; i < funcs.size(); i++) { - temp = Replace(temp, funcs[i], "0", 0); + for (const auto& item : funcs) { + temp = Replace(temp, item, "0", 0); } if (temp.find_first_not_of("-0123456789e+*/.,)( ^%") == std::string::npos) { if (temp.find("/") == 0 || temp.find("./") == 0 || temp.find("../") == 0) From 5e9c15c382f0ed88645e2f2485173880bb3a33c0 Mon Sep 17 00:00:00 2001 From: Date: Thu, 28 Apr 2022 03:35:08 +0800 Subject: [PATCH 05/28] fix line endings --- .../framework/tools/src/TRestStringHelper.cxx | 1610 ++++++++--------- 1 file changed, 805 insertions(+), 805 deletions(-) diff --git a/source/framework/tools/src/TRestStringHelper.cxx b/source/framework/tools/src/TRestStringHelper.cxx index 806e84caf..880de6f35 100644 --- a/source/framework/tools/src/TRestStringHelper.cxx +++ b/source/framework/tools/src/TRestStringHelper.cxx @@ -1,805 +1,805 @@ -#include "TRestStringHelper.h" - -#include - -#include "Rtypes.h" -#include "TApplication.h" -#include "TSystem.h" - -#if ROOT_VERSION_CODE < ROOT_VERSION(6, 0, 0) -#include "TFormula.h" -#else -#include "v5/TFormula.h" -#endif - -#include -#include - -using namespace std; - -/////////////////////////////////////////////// -/// \brief Returns 1 only if valid mathematical expression keywords (or numbers) -/// are found in the string **in**. If not it returns 0. -/// -/// By logic, mathematical expressions must have: +-*/e^% in the middle, or % in the end, or math functions in the beginning. -/// despite those symbols, the string should be purely numeric. -/// example: -/// 1+1 --> expression -/// sin(1.5) --> expression -/// 123456789 --> not expression, It is a pure number that can be directly parsed. -/// ./123 --> not expression, it is a path -/// 333/555 --> is expression. But it may also be a path. We should avoid using paths like that -Int_t REST_StringHelper::isAExpression(string in) { - bool symbol = false; - - if (in.length() < 2) // minimum expression: 3% - return 0; - - vector funcs{"sqrt", "log", "exp", "gaus", "cos", "sin", "tan", "atan", "acos", "asin"}; - for (const auto& item : funcs) { - if (in.find(item) != std::string::npos) { - symbol = true; - break; - } - } - - if (!symbol) { - int pos = in.find_first_of("+-*/e^%"); - if (pos > 0 && pos < in.size() - 1) { - symbol = true; - } - } - - if (!symbol) { - int pos = in.find_first_of("%"); - if (pos == in.size() - 1) { - symbol = true; - } - } - - if (symbol) { - string temp = in; - for (const auto& item : funcs) { - temp = Replace(temp, item, "0", 0); - } - if (temp.find_first_not_of("-0123456789e+*/.,)( ^%") == std::string::npos) { - if (temp.find("/") == 0 || temp.find("./") == 0 || temp.find("../") == 0) - return 0; // identify path - return 1; - } - } - else - { - return 0; - } - - return 0; -} - -/////////////////////////////////////////////// -/// \brief Evaluates and replaces valid mathematical expressions found in the -/// input string **buffer**. -/// -std::string REST_StringHelper::ReplaceMathematicalExpressions(std::string buffer, std::string errorMessage) { - buffer = Replace(buffer, " AND ", " && "); - buffer = Replace(buffer, " OR ", " || "); - // we spilt the unit part and the expresstion part - int pos = buffer.find_last_of("1234567890()."); - - string unit = buffer.substr(pos + 1, -1); - string temp = buffer.substr(0, pos + 1); - string result = ""; - - bool erased = false; - - std::vector Expressions = Split(temp, ","); - - if (Expressions.size() > 1 && Expressions[0][0] == '(' && - Expressions[Expressions.size() - 1][Expressions[Expressions.size() - 1].size() - 1] == ')') { - Expressions[0].erase(0, 1); - Expressions[Expressions.size() - 1].erase(Expressions[Expressions.size() - 1].size() - 1, 1); - erased = true; - } - - for (int i = 0; i < Expressions.size(); i++) { - if (!isAExpression(Expressions[i])) { - result += Expressions[i] + ","; - continue; - } - string evaluated = EvaluateExpression(Expressions[i]); - if (evaluated == "RESTerror") { - result += Expressions[i] + ","; - ferr << "ReplaceMathematicalExpressions. Error on RML syntax!" << endl; - if (errorMessage != "") ferr << errorMessage << endl; - } else - result += evaluated + ","; - } - if (Expressions.size() > 0) result.erase(result.size() - 1, 1); - - if (erased) { - result = "(" + result + ")"; - } - - return result + unit; -} - -/////////////////////////////////////////////// -/// \brief Evaluates a complex numerical expression and returns the resulting -/// value using TFormula. -/// -std::string REST_StringHelper::EvaluateExpression(std::string exp) { - if (!isAExpression(exp)) { - return exp; - } - -// NOTE!!! In root6 the expression like "1/2" will be computed using the input -// as int number, which will return 0, and cause problem. we roll back to -// TFormula of version 5 -#if ROOT_VERSION_CODE < ROOT_VERSION(6, 0, 0) - TFormula formula("tmp", exp.c_str()); -#else - ROOT::v5::TFormula formula("tmp", exp.c_str()); -#endif - - ostringstream sss; - Double_t number = formula.EvalPar(0); - if (number > 0 && number < 1.e-300) { - warning << "REST_StringHelper::EvaluateExpresssion. Expression not recognized --> " << exp << endl; - return (string) "RESTerror"; - } - - sss << number; - string out = sss.str(); - - return out; -} - -/////////////////////////////////////////////////// -/// \brief Helps to pause the program, printing a message before pausing. -/// -/// ROOT GUI won't be jammed during this pause. -Int_t REST_StringHelper::GetChar(string hint) { - if (gApplication != nullptr && !gApplication->IsRunning()) { - thread t = thread(&TApplication::Run, gApplication, true); - t.detach(); - - cout << hint << endl; - int result = Console::CompatibilityMode ? 1 : getchar(); - gSystem->ExitLoop(); - return result; - } else { - cout << hint << endl; - return Console::CompatibilityMode ? 1 : getchar(); - } - return -1; -} - -/////////////////////////////////////////////// -/// \brief Returns 1 only if a valid number is found in the string **in**. If -/// not it returns 0. -/// -Int_t REST_StringHelper::isANumber(string in) { - return (in.find_first_not_of("-+0123456789.eE") == std::string::npos && in.length() != 0); -} - -/////////////////////////////////////////////// -/// \brief Split the input string according to the given separator. Returning a -/// vector of fragments -/// -/// e.g. -/// Input: "" and "", Output: {} -/// Input: ":" and ":", Output: {} -/// Input: "abc" and "", Output: { "a", "b", "c" } -/// Input: "abc:def" and ":", Output: { "abc", "def" } -/// Input: "abc:def" and ":def", Output: { "abc" } -std::vector REST_StringHelper::Split(std::string in, string separator, bool allowBlankString, - bool removeWhiteSpaces, int startPos) { - std::vector result; - - int pos = startPos; - int front = 0; - while (1) { - pos = in.find(separator.c_str(), pos + 1); - string sub = in.substr(front, pos - front); - if (removeWhiteSpaces) sub = RemoveWhiteSpaces(sub); - if (allowBlankString || sub != "") { - result.push_back(sub); - } - front = pos + separator.size(); - if (pos == -1) break; - } - - return result; -} - -/////////////////////////////////////////////// -/// \brief Convert the input string into a vector of double elements -/// -/// e.g. Input: "1,2,3,4", Output: {1.,2.,3.,4.} -/// -std::vector REST_StringHelper::StringToElements(std::string in, string separator) { - vector result; - vector vec_str = REST_StringHelper::Split(in, separator); - for (unsigned int i = 0; i < vec_str.size(); i++) { - double temp = REST_StringHelper::StringToDouble(vec_str[i]); - result.push_back(temp); - } - - return result; -} - -/////////////////////////////////////////////// -/// \brief Convert the input string `in` into a vector of double elements -/// -/// Called as `StringToElements( in, "[", ",", "]" );` will get the -/// elements from a string with the following format "[a,b,c]" where a,b,c -/// are double numbers. -/// -std::vector REST_StringHelper::StringToElements(std::string in, string headChar, string separator, - string tailChar) { - std::vector result; - size_t startPos = in.find(headChar); - size_t endPos = in.find(tailChar); - if (startPos == string::npos || endPos == string::npos) { - return result; - } - std::vector values = Split(in.substr(startPos + 1, endPos - startPos - 1), ","); - - for (unsigned int i = 0; i < values.size(); i++) { - double temp = REST_StringHelper::StringToDouble(values[i]); - result.push_back(temp); - } - - return result; -} - -/////////////////////////////////////////////// -/// \brief Returns the input string removing all white spaces. -/// -string REST_StringHelper::RemoveWhiteSpaces(string in) { - string out = in; - size_t pos = 0; - while ((pos = out.find(" ", pos)) != string::npos) { - out.erase(pos, 1); - } - - return out; -} - -ULong64_t REST_StringHelper::ToHash(string str) { - ULong64_t prime = 0x100000001B3ull; - ULong64_t basis = 0xCBF29CE484222325ull; - ULong64_t ret{basis}; - - for (int i = 0; i < str.size(); i++) { - ret ^= str[i]; - ret *= prime; - } - - return ret; -} - -/////////////////////////////////////////////// -/// \brief Counts the number of occurences of **substring** inside the input -/// string **in**. -/// -Int_t REST_StringHelper::Count(string in, string substring) { - int count = 0; - size_t nPos = in.find(substring, 0); // First occurrence - while (nPos != string::npos) { - count++; - nPos = in.find(substring, nPos + 1); - } - - return count; -} - -/// \brief Returns the position of the **nth** occurence of the string -/// **strToFind** inside the string **in**. -/// -Int_t REST_StringHelper::FindNthStringPosition(const string& in, size_t pos, const string& strToFind, - size_t nth) { - size_t found_pos = in.find(strToFind, pos); - if (nth == 0 || string::npos == found_pos) return found_pos; - return FindNthStringPosition(in, found_pos + 1, strToFind, nth - 1); -} - -/// \brief Returns the number of different characters between two strings -/// -/// This algorithm is case insensitive. It matches the two strings in pieces -/// after inserting proper blanks, then counts the unmatched part(just a guess). -/// e.g. -/// "woll " -/// "world" -/// 00101 --> 2 -/// -/// " torgae" -/// "Storage" -/// 1000110 --> 3 -/// -/// "fi le" -/// "title" -/// 10100 --> 2 -/// -/// Source code from -/// https://blog.csdn.net/baidu_23086307/article/details/53020566 -/// -Int_t REST_StringHelper::DiffString(const string& source, const string& target) { - int n = source.length(); - int m = target.length(); - if (m == 0) return n; - if (n == 0) return m; - // Construct a matrix - typedef vector > Tmatrix; - Tmatrix matrix(n + 1); - for (int i = 0; i <= n; i++) matrix[i].resize(m + 1); - - // step 2 Initialize - - for (int i = 1; i <= n; i++) matrix[i][0] = i; - for (int i = 1; i <= m; i++) matrix[0][i] = i; - - // step 3 - for (int i = 1; i <= n; i++) { - const char si = source[i - 1]; - // step 4 - for (int j = 1; j <= m; j++) { - const char dj = target[j - 1]; - // step 5 - int cost; - if (si == dj) { - cost = 0; - } else { - cost = 1; - } - // step 6 - const int above = matrix[i - 1][j] + 1; - const int left = matrix[i][j - 1] + 1; - const int diag = matrix[i - 1][j - 1] + cost; - matrix[i][j] = min(above, min(left, diag)); - } - } // step7 - return matrix[n][m]; -} - -/////////////////////////////////////////////// -/// \brief Replace every occurences of **thisSring** by **byThisString** inside -/// string **in**. -/// -string REST_StringHelper::Replace(string in, string thisString, string byThisString, size_t fromPosition, - Int_t N) { - string out = in; - size_t pos = fromPosition; - Int_t cont = 0; - while ((pos = out.find(thisString, pos)) != string::npos) { - out.replace(pos, thisString.length(), byThisString); - pos = pos + byThisString.length(); - cont++; - - if (N > 0 && cont == N) return out; - } - - return out; -} - -std::string REST_StringHelper::EscapeSpecialLetters(string in) { - string result = Replace(in, "(", "\\(", 0); - result = Replace(result, ")", "\\)", 0); - result = Replace(result, "$", "\\$", 0); - result = Replace(result, "#", "\\#", 0); - result = Replace(result, "{", "\\{", 0); - result = Replace(result, "}", "\\}", 0); - result = Replace(result, "<", "\\<", 0); - result = Replace(result, ">", "\\>", 0); - return result; -} - -/////////////////////////////////////////////// -/// \brief Format time_t into string -/// -/// The output datatime format is "Y-M-D H:M:S". e.g. -/// \code -/// REST_StringHelper::ToDateTimeString(0) -/// (return) 1970-1-1 8:00:00 -/// \endcode -/// here the type "time_t" is actually the type "long long", which indicates the -/// elapsed time in second from 1970-1-1 8:00:00 -string REST_StringHelper::ToDateTimeString(time_t time) { - tm* tm_ = localtime(&time); - int year, month, day, hour, minute, second; - year = tm_->tm_year + 1900; - month = tm_->tm_mon + 1; - day = tm_->tm_mday; - hour = tm_->tm_hour; - minute = tm_->tm_min; - second = tm_->tm_sec; - char yearStr[5], monthStr[3], dayStr[3], hourStr[3], minuteStr[3], secondStr[3]; - sprintf(yearStr, "%d", year); - sprintf(monthStr, "%d", month); - sprintf(dayStr, "%d", day); - sprintf(hourStr, "%d", hour); - sprintf(minuteStr, "%d", minute); - if (minuteStr[1] == '\0') { - minuteStr[2] = '\0'; - minuteStr[1] = minuteStr[0]; - minuteStr[0] = '0'; - } - sprintf(secondStr, "%d", second); - if (secondStr[1] == '\0') { - secondStr[2] = '\0'; - secondStr[1] = secondStr[0]; - secondStr[0] = '0'; - } - char s[20]; - sprintf(s, "%s-%s-%s %s:%s:%s", yearStr, monthStr, dayStr, hourStr, minuteStr, secondStr); - string str(s); - return str; -} - -/////////////////////////////////////////////// -/// \brief A method to convert a date/time formatted string to a timestamp. -/// -/// The input datatime format should match any of the following patterns: -/// "YYYY-mm-DD HH:MM:SS", "YYYY/mm/DD HH:MM:SS", "YYYY-mm-DD", or "YYYY/mm/DD". -/// If no time is given it will be assumed to be 00:00:00. -/// -/// \code -/// REST_StringHelper::ToTime("2018-1-1 8:00:00") -/// (return) 1514764800 -/// \endcode -/// -/// here the type "time_t" is actually the type "long long", which indicates the -/// elapsed time in second from 1970-1-1 8:00:00 -/// -time_t REST_StringHelper::StringToTimeStamp(string time) { - struct tm tm1; - tm1.tm_hour = 0; - tm1.tm_min = 0; - tm1.tm_sec = 0; - time_t time1; - if (time.find(":") != string::npos) { - if (time.find("/") != string::npos) - sscanf(time.c_str(), "%d/%d/%d %d:%d:%d", &(tm1.tm_year), &(tm1.tm_mon), &(tm1.tm_mday), - &(tm1.tm_hour), &(tm1.tm_min), &(tm1.tm_sec)); - else - sscanf(time.c_str(), "%d-%d-%d %d:%d:%d", &(tm1.tm_year), &(tm1.tm_mon), &(tm1.tm_mday), - &(tm1.tm_hour), &(tm1.tm_min), &(tm1.tm_sec)); - } else { - if (time.find("/") != string::npos) - sscanf(time.c_str(), "%d/%d/%d", &(tm1.tm_year), &(tm1.tm_mon), &(tm1.tm_mday)); - else - sscanf(time.c_str(), "%d-%d-%d", &(tm1.tm_year), &(tm1.tm_mon), &(tm1.tm_mday)); - } - - tm1.tm_year -= 1900; - tm1.tm_mon--; - tm1.tm_isdst = -1; - time1 = mktime(&tm1); - - return time1; -} - -REST_Verbose_Level REST_StringHelper::StringToVerboseLevel(string in) { - if (ToUpper(in) == "SILENT" || in == "0") return REST_Silent; - if (ToUpper(in) == "ESSENTIAL" || ToUpper(in) == "WARNING" || in == "1") return REST_Essential; - if (ToUpper(in) == "INFO" || in == "2") return REST_Info; - if (ToUpper(in) == "DEBUG" || in == "3") return REST_Debug; - if (ToUpper(in) == "EXTREME" || in == "4") return REST_Extreme; - - return REST_Essential; -} - -/////////////////////////////////////////////// -/// \brief Gets a double from a string. -/// -Double_t REST_StringHelper::StringToDouble(string in) { - if (isANumber(in)) { - return stod(in); - } else { - return -1; - } -} - -/////////////////////////////////////////////// -/// \brief Gets a float from a string. -/// -Float_t REST_StringHelper::StringToFloat(string in) { - if (isANumber(in)) { - return stof(in); - } else { - return -1; - } -} - -/////////////////////////////////////////////// -/// \brief Gets an integer from a string. -/// -Int_t REST_StringHelper::StringToInteger(string in) { - // If we find an hexadecimal number - if (in.find("0x") != std::string::npos) return (Int_t)std::stoul(in, nullptr, 16); - - return (Int_t)StringToDouble(in); -} - -/////////////////////////////////////////////// -/// \brief Gets a string from an integer. -/// -string REST_StringHelper::IntegerToString(Int_t n) { return Form("%d", n); } - -/////////////////////////////////////////////// -/// \brief Gets a string from a double -/// -string REST_StringHelper::DoubleToString(Double_t d) { return Form("%4.2lf", d); } - -Bool_t REST_StringHelper::StringToBool(std::string in) { - return (ToUpper(in) == "TRUE" || ToUpper(in) == "ON"); -} - -Long64_t REST_StringHelper::StringToLong(std::string in) { - if (in.find_first_of("eE") != string::npos) { - // in case for scientific numbers - return (Long64_t)StringToDouble(in); - } - stringstream strIn; - strIn << in; - long long llNum; - strIn >> llNum; - return llNum; -} - -/////////////////////////////////////////////// -/// \brief Gets a 3D-vector from a string. Format should be : (X,Y,Z). -/// -TVector3 REST_StringHelper::StringTo3DVector(string in) { - TVector3 a; - - size_t startVector = in.find_first_of("("); - if (startVector == string::npos) return a; - - size_t endVector = in.find_first_of(")"); - if (endVector == string::npos) return a; - - size_t n = count(in.begin(), in.end(), ','); - if (n != 2) return a; - - size_t firstComma = in.find_first_of(","); - size_t secondComma = in.find(",", firstComma + 1); - - if (firstComma >= endVector || firstComma <= startVector) return a; - if (secondComma >= endVector || secondComma <= startVector) return a; - - string X = in.substr(startVector + 1, firstComma - startVector - 1); - string Y = in.substr(firstComma + 1, secondComma - firstComma - 1); - string Z = in.substr(secondComma + 1, endVector - secondComma - 1); - - a.SetXYZ(StringToDouble(X), StringToDouble(Y), StringToDouble(Z)); - - return a; -} - -/////////////////////////////////////////////// -/// \brief Gets a 2D-vector from a string. -/// -TVector2 REST_StringHelper::StringTo2DVector(string in) { - TVector2 a(-1, -1); - - size_t startVector = in.find_first_of("("); - if (startVector == string::npos) return a; - - size_t endVector = in.find_first_of(")"); - if (endVector == string::npos) return a; - - size_t n = count(in.begin(), in.end(), ','); - if (n != 1) return a; - - size_t firstComma = in.find_first_of(","); - - if (firstComma >= endVector || firstComma <= startVector) return a; - - string X = in.substr(startVector + 1, firstComma - startVector - 1); - string Y = in.substr(firstComma + 1, endVector - firstComma - 1); - - a.Set(StringToDouble(X), StringToDouble(Y)); - - return a; -} - -/////////////////////////////////////////////// -/// \brief Convert string to its upper case. Alternative of TString::ToUpper -/// -std::string REST_StringHelper::ToUpper(std::string s) { - transform(s.begin(), s.end(), s.begin(), (int (*)(int))toupper); - return s; -} - -/////////////////////////////////////////////// -/// \brief Convert string to its lower case. Alternative of TString::ToLower -/// -std::string REST_StringHelper::ToLower(std::string s) { - transform(s.begin(), s.end(), s.begin(), (int (*)(int))tolower); - return s; -} - -/////////////////////////////////////////////// -/// \brief Removes all white spaces found at the end of the string -/// (https://stackoverflow.com/questions/216823/whats-the-best-way-to-trim-stdstring) -/// -std::string REST_StringHelper::RightTrim(std::string s, const char* t) { - s.erase(s.find_last_not_of(t) + 1); - return s; -} - -/////////////////////////////////////////////// -/// \brief Removes all white spaces found at the beginning of the string -/// (https://stackoverflow.com/questions/216823/whats-the-best-way-to-trim-stdstring) -/// -std::string REST_StringHelper::LeftTrim(std::string s, const char* t) { - s.erase(0, s.find_first_not_of(t)); - return s; -} - -/////////////////////////////////////////////// -/// \brief Removes all white spaces found at the beginning and the end of the string -/// (https://stackoverflow.com/questions/216823/whats-the-best-way-to-trim-stdstring) -/// -std::string REST_StringHelper::Trim(std::string s, const char* t) { return LeftTrim(RightTrim(s, t), t); } - -/////////////////////////////////////////////// -/// \brief It trims and uppers the string -/// -std::string REST_StringHelper::TrimAndUpper(std::string s) { - s = Trim(s); - s = ToUpper(s); - return s; -} - -/////////////////////////////////////////////// -/// \brief It trims and lowers the string -/// -std::string REST_StringHelper::TrimAndLower(std::string s) { - s = Trim(s); - s = ToLower(s); - return s; -} - -/////////////////////////////////////////////// -/// \brief Convert data member name to parameter name, following REST parameter naming convention. -/// -/// > The name of class data member, if starts from “f” and have the second character in -/// capital form, will be linked to a parameter. The linked parameter will strip the first -/// “f” and have the first letter in lowercase. For example, data member “fTargetName” is -/// linked to parameter “targetName”. -string REST_StringHelper::DataMemberNameToParameterName(string name) { - if (name == "") { - return ""; - } - if (name[0] == 'f' && name.size() > 1 && (name[1] >= 65 && name[1] <= 90)) { - return string(1, tolower(name[1])) + name.substr(2, -1); - } else { - return ""; - } -} - -/////////////////////////////////////////////// -/// \brief Convert parameter name to datamember name, following REST parameter naming convention. -/// -/// > The name of class data member, if starts from “f” and have the second character in -/// capital form, will be linked to a parameter. The linked parameter will strip the first -/// “f” and have the first letter in lowercase. For example, data member “fTargetName” is -/// linked to parameter “targetName”. -string REST_StringHelper::ParameterNameToDataMemberName(string name) { - if (name == "") { - return ""; - } - if (islower(name[0])) { - return "f" + string(1, toupper(name[0])) + name.substr(1, -1); - } else { - return ""; - } -} - -///////////////////////////////////////////// -/// \brief Reads a function with parameter options from string and returns it as TF1*. -/// -/// > The function is defined as a string following ROOT::TFormula conventions for parameters. -/// Inside the square brackets we allow parameter initialization, constant parameter and ranges. -/// This has been created for fitting functions, where each parameter has its own restrictions. -/// -/// Examples: -/// -- Initial value: [0=3.5] -/// -- Fixed value: [0==3.5] -/// -- Range: [0=3.5(1,5)] The parameter 0 begin at 3.5 and it can move between 1 and 5. -/// -/// All parameters should be initialized. -/// -/// Input arguments: -/// * String with function and parameter options. -/// * Two doubles with the range of the function. -/// -/// Output: TF1* with the interpreted fuction. It contains all restrictions, ranges, etc. -/// -/// \warning This object will create a new TF1 object in memory, so the code creating a -/// TF1 object using this method as a helper will be responsible to delete this object from -/// memory. -/// -/// \code -/// TF1* ff = CreateTF1FromString(xxx); -/// ... -/// ff->DoSomething(); -/// ... -/// delete ff; -/// \endcode -/// -TF1* REST_StringHelper::CreateTF1FromString(std::string func, double init, double end) { - string tf1 = func; - // Number of parameters - size_t n = std::count(func.begin(), func.end(), '['); - // Reading options - int a = 0; - int optPos[n]; - std::vector options(n); // Vector of strings of any size. - for (int i = 0; i < n; i++) { - optPos[i] = func.find("[", a); - options[i] = - func.substr(func.find("[", a) + 1, func.find("]", func.find("[", a)) - func.find("[", a) - 1); - a = func.find("[", a) + 1; - } - // Removing options from function string - for (int i = 0; i < n; i++) { - tf1.replace(optPos[n - 1 - i] + 1, (func.find("]", optPos[n - 1 - i]) - optPos[n - 1 - i] - 1), - std::string(1, func[optPos[n - 1 - i] + 1])); - } - - // Function - const char* tf1c = tf1.c_str(); - TF1* f = new TF1("f", tf1c, init, end); - - // Initial conditions - for (int i = 0; i < n; i++) { - if (options[i].find("=") != std::string::npos) { - string op = options[i].substr(options[i].find_last_of("=") + 1, - options[i].find("(") - options[i].find_last_of("=") - 1); - if (isANumber(op)) { - f->SetParameter(i, stod(op)); - } else { - cout << "Initial condition for parameter " << i << " is not a number: " << op << endl; - } - } else { - cout << "No initial condition given for parameter " << i << "!" << endl; - } - } - - // Fixed values - for (int i = 0; i < n; i++) { - if (std::count(options[i].begin(), options[i].end(), '=') == 2) { - string op = options[i].substr(options[i].find_last_of("=") + 1, - options[i].find("(") - options[i].find_last_of("=") - 1); - if (isANumber(op)) { - f->FixParameter(i, stod(op)); - } - // cout << "Parameter " << i << " fixed with value " << op << endl; - } - } - - // Ranges - for (int i = 0; i < n; i++) { - if (options[i].find("(") != std::string::npos) { - string op = - options[i].substr(options[i].find("(") + 1, options[i].find(")") - options[i].find("(") - 1); - f->SetParLimits(i, stod(op.substr(0, op.find(","))), - stod(op.substr(op.find(",") + 1, op.size() - 1 - op.find(",")))); - // cout << "Parameter " << i << " range " << "(" << stod(op.substr(0, op.find(","))) << "," << - // stod(op.substr(op.find(",")+1, op.size()-1-op.find(",") )) << ")" << endl; - } - } - - return f; -} - -#ifdef WIN32 -string get_current_dir_name() { - char pBuf[MAX_PATH]; - GetCurrentDirectory(MAX_PATH, pBuf); - return string(pBuf); -} -#endif +#include "TRestStringHelper.h" + +#include + +#include "Rtypes.h" +#include "TApplication.h" +#include "TSystem.h" + +#if ROOT_VERSION_CODE < ROOT_VERSION(6, 0, 0) +#include "TFormula.h" +#else +#include "v5/TFormula.h" +#endif + +#include +#include + +using namespace std; + +/////////////////////////////////////////////// +/// \brief Returns 1 only if valid mathematical expression keywords (or numbers) +/// are found in the string **in**. If not it returns 0. +/// +/// By logic, mathematical expressions must have: +-*/e^% in the middle, or % in the end, or math functions in the beginning. +/// despite those symbols, the string should be purely numeric. +/// example: +/// 1+1 --> expression +/// sin(1.5) --> expression +/// 123456789 --> not expression, It is a pure number that can be directly parsed. +/// ./123 --> not expression, it is a path +/// 333/555 --> is expression. But it may also be a path. We should avoid using paths like that +Int_t REST_StringHelper::isAExpression(string in) { + bool symbol = false; + + if (in.length() < 2) // minimum expression: 3% + return 0; + + vector funcs{"sqrt", "log", "exp", "gaus", "cos", "sin", "tan", "atan", "acos", "asin"}; + for (const auto& item : funcs) { + if (in.find(item) != std::string::npos) { + symbol = true; + break; + } + } + + if (!symbol) { + int pos = in.find_first_of("+-*/e^%"); + if (pos > 0 && pos < in.size() - 1) { + symbol = true; + } + } + + if (!symbol) { + int pos = in.find_first_of("%"); + if (pos == in.size() - 1) { + symbol = true; + } + } + + if (symbol) { + string temp = in; + for (const auto& item : funcs) { + temp = Replace(temp, item, "0", 0); + } + if (temp.find_first_not_of("-0123456789e+*/.,)( ^%") == std::string::npos) { + if (temp.find("/") == 0 || temp.find("./") == 0 || temp.find("../") == 0) + return 0; // identify path + return 1; + } + } + else + { + return 0; + } + + return 0; +} + +/////////////////////////////////////////////// +/// \brief Evaluates and replaces valid mathematical expressions found in the +/// input string **buffer**. +/// +std::string REST_StringHelper::ReplaceMathematicalExpressions(std::string buffer, std::string errorMessage) { + buffer = Replace(buffer, " AND ", " && "); + buffer = Replace(buffer, " OR ", " || "); + // we spilt the unit part and the expresstion part + int pos = buffer.find_last_of("1234567890()."); + + string unit = buffer.substr(pos + 1, -1); + string temp = buffer.substr(0, pos + 1); + string result = ""; + + bool erased = false; + + std::vector Expressions = Split(temp, ","); + + if (Expressions.size() > 1 && Expressions[0][0] == '(' && + Expressions[Expressions.size() - 1][Expressions[Expressions.size() - 1].size() - 1] == ')') { + Expressions[0].erase(0, 1); + Expressions[Expressions.size() - 1].erase(Expressions[Expressions.size() - 1].size() - 1, 1); + erased = true; + } + + for (int i = 0; i < Expressions.size(); i++) { + if (!isAExpression(Expressions[i])) { + result += Expressions[i] + ","; + continue; + } + string evaluated = EvaluateExpression(Expressions[i]); + if (evaluated == "RESTerror") { + result += Expressions[i] + ","; + ferr << "ReplaceMathematicalExpressions. Error on RML syntax!" << endl; + if (errorMessage != "") ferr << errorMessage << endl; + } else + result += evaluated + ","; + } + if (Expressions.size() > 0) result.erase(result.size() - 1, 1); + + if (erased) { + result = "(" + result + ")"; + } + + return result + unit; +} + +/////////////////////////////////////////////// +/// \brief Evaluates a complex numerical expression and returns the resulting +/// value using TFormula. +/// +std::string REST_StringHelper::EvaluateExpression(std::string exp) { + if (!isAExpression(exp)) { + return exp; + } + +// NOTE!!! In root6 the expression like "1/2" will be computed using the input +// as int number, which will return 0, and cause problem. we roll back to +// TFormula of version 5 +#if ROOT_VERSION_CODE < ROOT_VERSION(6, 0, 0) + TFormula formula("tmp", exp.c_str()); +#else + ROOT::v5::TFormula formula("tmp", exp.c_str()); +#endif + + ostringstream sss; + Double_t number = formula.EvalPar(0); + if (number > 0 && number < 1.e-300) { + warning << "REST_StringHelper::EvaluateExpresssion. Expression not recognized --> " << exp << endl; + return (string) "RESTerror"; + } + + sss << number; + string out = sss.str(); + + return out; +} + +/////////////////////////////////////////////////// +/// \brief Helps to pause the program, printing a message before pausing. +/// +/// ROOT GUI won't be jammed during this pause. +Int_t REST_StringHelper::GetChar(string hint) { + if (gApplication != nullptr && !gApplication->IsRunning()) { + thread t = thread(&TApplication::Run, gApplication, true); + t.detach(); + + cout << hint << endl; + int result = Console::CompatibilityMode ? 1 : getchar(); + gSystem->ExitLoop(); + return result; + } else { + cout << hint << endl; + return Console::CompatibilityMode ? 1 : getchar(); + } + return -1; +} + +/////////////////////////////////////////////// +/// \brief Returns 1 only if a valid number is found in the string **in**. If +/// not it returns 0. +/// +Int_t REST_StringHelper::isANumber(string in) { + return (in.find_first_not_of("-+0123456789.eE") == std::string::npos && in.length() != 0); +} + +/////////////////////////////////////////////// +/// \brief Split the input string according to the given separator. Returning a +/// vector of fragments +/// +/// e.g. +/// Input: "" and "", Output: {} +/// Input: ":" and ":", Output: {} +/// Input: "abc" and "", Output: { "a", "b", "c" } +/// Input: "abc:def" and ":", Output: { "abc", "def" } +/// Input: "abc:def" and ":def", Output: { "abc" } +std::vector REST_StringHelper::Split(std::string in, string separator, bool allowBlankString, + bool removeWhiteSpaces, int startPos) { + std::vector result; + + int pos = startPos; + int front = 0; + while (1) { + pos = in.find(separator.c_str(), pos + 1); + string sub = in.substr(front, pos - front); + if (removeWhiteSpaces) sub = RemoveWhiteSpaces(sub); + if (allowBlankString || sub != "") { + result.push_back(sub); + } + front = pos + separator.size(); + if (pos == -1) break; + } + + return result; +} + +/////////////////////////////////////////////// +/// \brief Convert the input string into a vector of double elements +/// +/// e.g. Input: "1,2,3,4", Output: {1.,2.,3.,4.} +/// +std::vector REST_StringHelper::StringToElements(std::string in, string separator) { + vector result; + vector vec_str = REST_StringHelper::Split(in, separator); + for (unsigned int i = 0; i < vec_str.size(); i++) { + double temp = REST_StringHelper::StringToDouble(vec_str[i]); + result.push_back(temp); + } + + return result; +} + +/////////////////////////////////////////////// +/// \brief Convert the input string `in` into a vector of double elements +/// +/// Called as `StringToElements( in, "[", ",", "]" );` will get the +/// elements from a string with the following format "[a,b,c]" where a,b,c +/// are double numbers. +/// +std::vector REST_StringHelper::StringToElements(std::string in, string headChar, string separator, + string tailChar) { + std::vector result; + size_t startPos = in.find(headChar); + size_t endPos = in.find(tailChar); + if (startPos == string::npos || endPos == string::npos) { + return result; + } + std::vector values = Split(in.substr(startPos + 1, endPos - startPos - 1), ","); + + for (unsigned int i = 0; i < values.size(); i++) { + double temp = REST_StringHelper::StringToDouble(values[i]); + result.push_back(temp); + } + + return result; +} + +/////////////////////////////////////////////// +/// \brief Returns the input string removing all white spaces. +/// +string REST_StringHelper::RemoveWhiteSpaces(string in) { + string out = in; + size_t pos = 0; + while ((pos = out.find(" ", pos)) != string::npos) { + out.erase(pos, 1); + } + + return out; +} + +ULong64_t REST_StringHelper::ToHash(string str) { + ULong64_t prime = 0x100000001B3ull; + ULong64_t basis = 0xCBF29CE484222325ull; + ULong64_t ret{basis}; + + for (int i = 0; i < str.size(); i++) { + ret ^= str[i]; + ret *= prime; + } + + return ret; +} + +/////////////////////////////////////////////// +/// \brief Counts the number of occurences of **substring** inside the input +/// string **in**. +/// +Int_t REST_StringHelper::Count(string in, string substring) { + int count = 0; + size_t nPos = in.find(substring, 0); // First occurrence + while (nPos != string::npos) { + count++; + nPos = in.find(substring, nPos + 1); + } + + return count; +} + +/// \brief Returns the position of the **nth** occurence of the string +/// **strToFind** inside the string **in**. +/// +Int_t REST_StringHelper::FindNthStringPosition(const string& in, size_t pos, const string& strToFind, + size_t nth) { + size_t found_pos = in.find(strToFind, pos); + if (nth == 0 || string::npos == found_pos) return found_pos; + return FindNthStringPosition(in, found_pos + 1, strToFind, nth - 1); +} + +/// \brief Returns the number of different characters between two strings +/// +/// This algorithm is case insensitive. It matches the two strings in pieces +/// after inserting proper blanks, then counts the unmatched part(just a guess). +/// e.g. +/// "woll " +/// "world" +/// 00101 --> 2 +/// +/// " torgae" +/// "Storage" +/// 1000110 --> 3 +/// +/// "fi le" +/// "title" +/// 10100 --> 2 +/// +/// Source code from +/// https://blog.csdn.net/baidu_23086307/article/details/53020566 +/// +Int_t REST_StringHelper::DiffString(const string& source, const string& target) { + int n = source.length(); + int m = target.length(); + if (m == 0) return n; + if (n == 0) return m; + // Construct a matrix + typedef vector > Tmatrix; + Tmatrix matrix(n + 1); + for (int i = 0; i <= n; i++) matrix[i].resize(m + 1); + + // step 2 Initialize + + for (int i = 1; i <= n; i++) matrix[i][0] = i; + for (int i = 1; i <= m; i++) matrix[0][i] = i; + + // step 3 + for (int i = 1; i <= n; i++) { + const char si = source[i - 1]; + // step 4 + for (int j = 1; j <= m; j++) { + const char dj = target[j - 1]; + // step 5 + int cost; + if (si == dj) { + cost = 0; + } else { + cost = 1; + } + // step 6 + const int above = matrix[i - 1][j] + 1; + const int left = matrix[i][j - 1] + 1; + const int diag = matrix[i - 1][j - 1] + cost; + matrix[i][j] = min(above, min(left, diag)); + } + } // step7 + return matrix[n][m]; +} + +/////////////////////////////////////////////// +/// \brief Replace every occurences of **thisSring** by **byThisString** inside +/// string **in**. +/// +string REST_StringHelper::Replace(string in, string thisString, string byThisString, size_t fromPosition, + Int_t N) { + string out = in; + size_t pos = fromPosition; + Int_t cont = 0; + while ((pos = out.find(thisString, pos)) != string::npos) { + out.replace(pos, thisString.length(), byThisString); + pos = pos + byThisString.length(); + cont++; + + if (N > 0 && cont == N) return out; + } + + return out; +} + +std::string REST_StringHelper::EscapeSpecialLetters(string in) { + string result = Replace(in, "(", "\\(", 0); + result = Replace(result, ")", "\\)", 0); + result = Replace(result, "$", "\\$", 0); + result = Replace(result, "#", "\\#", 0); + result = Replace(result, "{", "\\{", 0); + result = Replace(result, "}", "\\}", 0); + result = Replace(result, "<", "\\<", 0); + result = Replace(result, ">", "\\>", 0); + return result; +} + +/////////////////////////////////////////////// +/// \brief Format time_t into string +/// +/// The output datatime format is "Y-M-D H:M:S". e.g. +/// \code +/// REST_StringHelper::ToDateTimeString(0) +/// (return) 1970-1-1 8:00:00 +/// \endcode +/// here the type "time_t" is actually the type "long long", which indicates the +/// elapsed time in second from 1970-1-1 8:00:00 +string REST_StringHelper::ToDateTimeString(time_t time) { + tm* tm_ = localtime(&time); + int year, month, day, hour, minute, second; + year = tm_->tm_year + 1900; + month = tm_->tm_mon + 1; + day = tm_->tm_mday; + hour = tm_->tm_hour; + minute = tm_->tm_min; + second = tm_->tm_sec; + char yearStr[5], monthStr[3], dayStr[3], hourStr[3], minuteStr[3], secondStr[3]; + sprintf(yearStr, "%d", year); + sprintf(monthStr, "%d", month); + sprintf(dayStr, "%d", day); + sprintf(hourStr, "%d", hour); + sprintf(minuteStr, "%d", minute); + if (minuteStr[1] == '\0') { + minuteStr[2] = '\0'; + minuteStr[1] = minuteStr[0]; + minuteStr[0] = '0'; + } + sprintf(secondStr, "%d", second); + if (secondStr[1] == '\0') { + secondStr[2] = '\0'; + secondStr[1] = secondStr[0]; + secondStr[0] = '0'; + } + char s[20]; + sprintf(s, "%s-%s-%s %s:%s:%s", yearStr, monthStr, dayStr, hourStr, minuteStr, secondStr); + string str(s); + return str; +} + +/////////////////////////////////////////////// +/// \brief A method to convert a date/time formatted string to a timestamp. +/// +/// The input datatime format should match any of the following patterns: +/// "YYYY-mm-DD HH:MM:SS", "YYYY/mm/DD HH:MM:SS", "YYYY-mm-DD", or "YYYY/mm/DD". +/// If no time is given it will be assumed to be 00:00:00. +/// +/// \code +/// REST_StringHelper::ToTime("2018-1-1 8:00:00") +/// (return) 1514764800 +/// \endcode +/// +/// here the type "time_t" is actually the type "long long", which indicates the +/// elapsed time in second from 1970-1-1 8:00:00 +/// +time_t REST_StringHelper::StringToTimeStamp(string time) { + struct tm tm1; + tm1.tm_hour = 0; + tm1.tm_min = 0; + tm1.tm_sec = 0; + time_t time1; + if (time.find(":") != string::npos) { + if (time.find("/") != string::npos) + sscanf(time.c_str(), "%d/%d/%d %d:%d:%d", &(tm1.tm_year), &(tm1.tm_mon), &(tm1.tm_mday), + &(tm1.tm_hour), &(tm1.tm_min), &(tm1.tm_sec)); + else + sscanf(time.c_str(), "%d-%d-%d %d:%d:%d", &(tm1.tm_year), &(tm1.tm_mon), &(tm1.tm_mday), + &(tm1.tm_hour), &(tm1.tm_min), &(tm1.tm_sec)); + } else { + if (time.find("/") != string::npos) + sscanf(time.c_str(), "%d/%d/%d", &(tm1.tm_year), &(tm1.tm_mon), &(tm1.tm_mday)); + else + sscanf(time.c_str(), "%d-%d-%d", &(tm1.tm_year), &(tm1.tm_mon), &(tm1.tm_mday)); + } + + tm1.tm_year -= 1900; + tm1.tm_mon--; + tm1.tm_isdst = -1; + time1 = mktime(&tm1); + + return time1; +} + +REST_Verbose_Level REST_StringHelper::StringToVerboseLevel(string in) { + if (ToUpper(in) == "SILENT" || in == "0") return REST_Silent; + if (ToUpper(in) == "ESSENTIAL" || ToUpper(in) == "WARNING" || in == "1") return REST_Essential; + if (ToUpper(in) == "INFO" || in == "2") return REST_Info; + if (ToUpper(in) == "DEBUG" || in == "3") return REST_Debug; + if (ToUpper(in) == "EXTREME" || in == "4") return REST_Extreme; + + return REST_Essential; +} + +/////////////////////////////////////////////// +/// \brief Gets a double from a string. +/// +Double_t REST_StringHelper::StringToDouble(string in) { + if (isANumber(in)) { + return stod(in); + } else { + return -1; + } +} + +/////////////////////////////////////////////// +/// \brief Gets a float from a string. +/// +Float_t REST_StringHelper::StringToFloat(string in) { + if (isANumber(in)) { + return stof(in); + } else { + return -1; + } +} + +/////////////////////////////////////////////// +/// \brief Gets an integer from a string. +/// +Int_t REST_StringHelper::StringToInteger(string in) { + // If we find an hexadecimal number + if (in.find("0x") != std::string::npos) return (Int_t)std::stoul(in, nullptr, 16); + + return (Int_t)StringToDouble(in); +} + +/////////////////////////////////////////////// +/// \brief Gets a string from an integer. +/// +string REST_StringHelper::IntegerToString(Int_t n) { return Form("%d", n); } + +/////////////////////////////////////////////// +/// \brief Gets a string from a double +/// +string REST_StringHelper::DoubleToString(Double_t d) { return Form("%4.2lf", d); } + +Bool_t REST_StringHelper::StringToBool(std::string in) { + return (ToUpper(in) == "TRUE" || ToUpper(in) == "ON"); +} + +Long64_t REST_StringHelper::StringToLong(std::string in) { + if (in.find_first_of("eE") != string::npos) { + // in case for scientific numbers + return (Long64_t)StringToDouble(in); + } + stringstream strIn; + strIn << in; + long long llNum; + strIn >> llNum; + return llNum; +} + +/////////////////////////////////////////////// +/// \brief Gets a 3D-vector from a string. Format should be : (X,Y,Z). +/// +TVector3 REST_StringHelper::StringTo3DVector(string in) { + TVector3 a; + + size_t startVector = in.find_first_of("("); + if (startVector == string::npos) return a; + + size_t endVector = in.find_first_of(")"); + if (endVector == string::npos) return a; + + size_t n = count(in.begin(), in.end(), ','); + if (n != 2) return a; + + size_t firstComma = in.find_first_of(","); + size_t secondComma = in.find(",", firstComma + 1); + + if (firstComma >= endVector || firstComma <= startVector) return a; + if (secondComma >= endVector || secondComma <= startVector) return a; + + string X = in.substr(startVector + 1, firstComma - startVector - 1); + string Y = in.substr(firstComma + 1, secondComma - firstComma - 1); + string Z = in.substr(secondComma + 1, endVector - secondComma - 1); + + a.SetXYZ(StringToDouble(X), StringToDouble(Y), StringToDouble(Z)); + + return a; +} + +/////////////////////////////////////////////// +/// \brief Gets a 2D-vector from a string. +/// +TVector2 REST_StringHelper::StringTo2DVector(string in) { + TVector2 a(-1, -1); + + size_t startVector = in.find_first_of("("); + if (startVector == string::npos) return a; + + size_t endVector = in.find_first_of(")"); + if (endVector == string::npos) return a; + + size_t n = count(in.begin(), in.end(), ','); + if (n != 1) return a; + + size_t firstComma = in.find_first_of(","); + + if (firstComma >= endVector || firstComma <= startVector) return a; + + string X = in.substr(startVector + 1, firstComma - startVector - 1); + string Y = in.substr(firstComma + 1, endVector - firstComma - 1); + + a.Set(StringToDouble(X), StringToDouble(Y)); + + return a; +} + +/////////////////////////////////////////////// +/// \brief Convert string to its upper case. Alternative of TString::ToUpper +/// +std::string REST_StringHelper::ToUpper(std::string s) { + transform(s.begin(), s.end(), s.begin(), (int (*)(int))toupper); + return s; +} + +/////////////////////////////////////////////// +/// \brief Convert string to its lower case. Alternative of TString::ToLower +/// +std::string REST_StringHelper::ToLower(std::string s) { + transform(s.begin(), s.end(), s.begin(), (int (*)(int))tolower); + return s; +} + +/////////////////////////////////////////////// +/// \brief Removes all white spaces found at the end of the string +/// (https://stackoverflow.com/questions/216823/whats-the-best-way-to-trim-stdstring) +/// +std::string REST_StringHelper::RightTrim(std::string s, const char* t) { + s.erase(s.find_last_not_of(t) + 1); + return s; +} + +/////////////////////////////////////////////// +/// \brief Removes all white spaces found at the beginning of the string +/// (https://stackoverflow.com/questions/216823/whats-the-best-way-to-trim-stdstring) +/// +std::string REST_StringHelper::LeftTrim(std::string s, const char* t) { + s.erase(0, s.find_first_not_of(t)); + return s; +} + +/////////////////////////////////////////////// +/// \brief Removes all white spaces found at the beginning and the end of the string +/// (https://stackoverflow.com/questions/216823/whats-the-best-way-to-trim-stdstring) +/// +std::string REST_StringHelper::Trim(std::string s, const char* t) { return LeftTrim(RightTrim(s, t), t); } + +/////////////////////////////////////////////// +/// \brief It trims and uppers the string +/// +std::string REST_StringHelper::TrimAndUpper(std::string s) { + s = Trim(s); + s = ToUpper(s); + return s; +} + +/////////////////////////////////////////////// +/// \brief It trims and lowers the string +/// +std::string REST_StringHelper::TrimAndLower(std::string s) { + s = Trim(s); + s = ToLower(s); + return s; +} + +/////////////////////////////////////////////// +/// \brief Convert data member name to parameter name, following REST parameter naming convention. +/// +/// > The name of class data member, if starts from “f” and have the second character in +/// capital form, will be linked to a parameter. The linked parameter will strip the first +/// “f” and have the first letter in lowercase. For example, data member “fTargetName” is +/// linked to parameter “targetName”. +string REST_StringHelper::DataMemberNameToParameterName(string name) { + if (name == "") { + return ""; + } + if (name[0] == 'f' && name.size() > 1 && (name[1] >= 65 && name[1] <= 90)) { + return string(1, tolower(name[1])) + name.substr(2, -1); + } else { + return ""; + } +} + +/////////////////////////////////////////////// +/// \brief Convert parameter name to datamember name, following REST parameter naming convention. +/// +/// > The name of class data member, if starts from “f” and have the second character in +/// capital form, will be linked to a parameter. The linked parameter will strip the first +/// “f” and have the first letter in lowercase. For example, data member “fTargetName” is +/// linked to parameter “targetName”. +string REST_StringHelper::ParameterNameToDataMemberName(string name) { + if (name == "") { + return ""; + } + if (islower(name[0])) { + return "f" + string(1, toupper(name[0])) + name.substr(1, -1); + } else { + return ""; + } +} + +///////////////////////////////////////////// +/// \brief Reads a function with parameter options from string and returns it as TF1*. +/// +/// > The function is defined as a string following ROOT::TFormula conventions for parameters. +/// Inside the square brackets we allow parameter initialization, constant parameter and ranges. +/// This has been created for fitting functions, where each parameter has its own restrictions. +/// +/// Examples: +/// -- Initial value: [0=3.5] +/// -- Fixed value: [0==3.5] +/// -- Range: [0=3.5(1,5)] The parameter 0 begin at 3.5 and it can move between 1 and 5. +/// +/// All parameters should be initialized. +/// +/// Input arguments: +/// * String with function and parameter options. +/// * Two doubles with the range of the function. +/// +/// Output: TF1* with the interpreted fuction. It contains all restrictions, ranges, etc. +/// +/// \warning This object will create a new TF1 object in memory, so the code creating a +/// TF1 object using this method as a helper will be responsible to delete this object from +/// memory. +/// +/// \code +/// TF1* ff = CreateTF1FromString(xxx); +/// ... +/// ff->DoSomething(); +/// ... +/// delete ff; +/// \endcode +/// +TF1* REST_StringHelper::CreateTF1FromString(std::string func, double init, double end) { + string tf1 = func; + // Number of parameters + size_t n = std::count(func.begin(), func.end(), '['); + // Reading options + int a = 0; + int optPos[n]; + std::vector options(n); // Vector of strings of any size. + for (int i = 0; i < n; i++) { + optPos[i] = func.find("[", a); + options[i] = + func.substr(func.find("[", a) + 1, func.find("]", func.find("[", a)) - func.find("[", a) - 1); + a = func.find("[", a) + 1; + } + // Removing options from function string + for (int i = 0; i < n; i++) { + tf1.replace(optPos[n - 1 - i] + 1, (func.find("]", optPos[n - 1 - i]) - optPos[n - 1 - i] - 1), + std::string(1, func[optPos[n - 1 - i] + 1])); + } + + // Function + const char* tf1c = tf1.c_str(); + TF1* f = new TF1("f", tf1c, init, end); + + // Initial conditions + for (int i = 0; i < n; i++) { + if (options[i].find("=") != std::string::npos) { + string op = options[i].substr(options[i].find_last_of("=") + 1, + options[i].find("(") - options[i].find_last_of("=") - 1); + if (isANumber(op)) { + f->SetParameter(i, stod(op)); + } else { + cout << "Initial condition for parameter " << i << " is not a number: " << op << endl; + } + } else { + cout << "No initial condition given for parameter " << i << "!" << endl; + } + } + + // Fixed values + for (int i = 0; i < n; i++) { + if (std::count(options[i].begin(), options[i].end(), '=') == 2) { + string op = options[i].substr(options[i].find_last_of("=") + 1, + options[i].find("(") - options[i].find_last_of("=") - 1); + if (isANumber(op)) { + f->FixParameter(i, stod(op)); + } + // cout << "Parameter " << i << " fixed with value " << op << endl; + } + } + + // Ranges + for (int i = 0; i < n; i++) { + if (options[i].find("(") != std::string::npos) { + string op = + options[i].substr(options[i].find("(") + 1, options[i].find(")") - options[i].find("(") - 1); + f->SetParLimits(i, stod(op.substr(0, op.find(","))), + stod(op.substr(op.find(",") + 1, op.size() - 1 - op.find(",")))); + // cout << "Parameter " << i << " range " << "(" << stod(op.substr(0, op.find(","))) << "," << + // stod(op.substr(op.find(",")+1, op.size()-1-op.find(",") )) << ")" << endl; + } + } + + return f; +} + +#ifdef WIN32 +string get_current_dir_name() { + char pBuf[MAX_PATH]; + GetCurrentDirectory(MAX_PATH, pBuf); + return string(pBuf); +} +#endif From a4efadd6924189496a589bc849f740456169faab Mon Sep 17 00:00:00 2001 From: Date: Thu, 28 Apr 2022 03:42:23 +0800 Subject: [PATCH 06/28] using const string --- source/framework/tools/inc/TRestStringHelper.h | 2 +- source/framework/tools/src/TRestStringHelper.cxx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/source/framework/tools/inc/TRestStringHelper.h b/source/framework/tools/inc/TRestStringHelper.h index c143eb229..0a8662d67 100644 --- a/source/framework/tools/inc/TRestStringHelper.h +++ b/source/framework/tools/inc/TRestStringHelper.h @@ -27,7 +27,7 @@ namespace REST_StringHelper { Int_t GetChar(std::string hint = "Press a KEY to continue ..."); Int_t isANumber(std::string in); -Int_t isAExpression(std::string in); +Int_t isAExpression(const std::string& in); std::string ReplaceMathematicalExpressions(std::string buffer, std::string errorMessage = ""); std::string EvaluateExpression(std::string exp); Float_t StringToFloat(std::string in); diff --git a/source/framework/tools/src/TRestStringHelper.cxx b/source/framework/tools/src/TRestStringHelper.cxx index 880de6f35..83bc818e6 100644 --- a/source/framework/tools/src/TRestStringHelper.cxx +++ b/source/framework/tools/src/TRestStringHelper.cxx @@ -29,7 +29,7 @@ using namespace std; /// 123456789 --> not expression, It is a pure number that can be directly parsed. /// ./123 --> not expression, it is a path /// 333/555 --> is expression. But it may also be a path. We should avoid using paths like that -Int_t REST_StringHelper::isAExpression(string in) { +Int_t REST_StringHelper::isAExpression(const string& in) { bool symbol = false; if (in.length() < 2) // minimum expression: 3% From 8a4fc351f03073a5119876c034af2cd859fae5e4 Mon Sep 17 00:00:00 2001 From: nkx Nicholas Date: Sun, 8 May 2022 16:33:48 +0800 Subject: [PATCH 07/28] TRestThread no longer inherites from TRestMetadata; added compression setup --- source/framework/core/inc/TRestThread.h | 7 ++++++- source/framework/core/src/TRestThread.cxx | 7 +++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/source/framework/core/inc/TRestThread.h b/source/framework/core/inc/TRestThread.h index 861bc595a..9f726963c 100644 --- a/source/framework/core/inc/TRestThread.h +++ b/source/framework/core/inc/TRestThread.h @@ -21,7 +21,7 @@ #include "TRestProcessRunner.h" /// Threaded worker of a process chain -class TRestThread : public TRestMetadata { +class TRestThread { private: Int_t fThreadId; @@ -36,6 +36,8 @@ class TRestThread : public TRestMetadata { std::thread t; //! Bool_t isFinished; //! Bool_t fProcessNullReturned; //! + Int_t fCompressionLevel; //! + Int_t fVerboseLevel; //! public: void Initialize(); @@ -58,6 +60,8 @@ class TRestThread : public TRestMetadata { void SetThreadId(Int_t id); void SetOutputTree(TRestAnalysisTree* t) { fAnalysisTree = t; } void SetProcessRunner(TRestProcessRunner* r) { fHostRunner = r; } + void SetCompressionLevel(Int_t comp) { fCompressionLevel = comp; } + void SetVerboseLevel(Int_t verb) { fVerboseLevel = verb; } Int_t GetThreadId() { return fThreadId; } TRestEvent* GetInputEvent() { return fInputEvent; } @@ -68,6 +72,7 @@ class TRestThread : public TRestMetadata { TRestAnalysisTree* GetAnalysisTree() { return fAnalysisTree; } TTree* GetEventTree() { return fEventTree; } Bool_t Finished() { return isFinished; } + Int_t GetVerboseLevel() { return fVerboseLevel; } // Constructor & Destructor TRestThread() { Initialize(); } diff --git a/source/framework/core/src/TRestThread.cxx b/source/framework/core/src/TRestThread.cxx index e6ddbc085..f7d2b05aa 100644 --- a/source/framework/core/src/TRestThread.cxx +++ b/source/framework/core/src/TRestThread.cxx @@ -49,6 +49,9 @@ void TRestThread::Initialize() { fProcessChain.clear(); isFinished = false; + + fCompressionLevel = 1; + fVerboseLevel = REST_Essential; } /////////////////////////////////////////////// @@ -240,7 +243,7 @@ void TRestThread::PrepareToProcess(bool* outputConfig) { if (fProcessChain.size() > 0) { debug << "TRestThread: Creating file : " << threadFileName << endl; fOutputFile = new TFile(threadFileName.c_str(), "recreate"); - fOutputFile->SetCompressionLevel(0); + fOutputFile->SetCompressionLevel(fCompressionLevel); fAnalysisTree = new TRestAnalysisTree("AnalysisTree_" + ToString(fThreadId), "dummyTree"); fAnalysisTree->DisableQuickObservableValueSetting(); @@ -387,7 +390,7 @@ void TRestThread::PrepareToProcess(bool* outputConfig) { fInputEvent = REST_Reflection::Assembly(tmp); fOutputEvent = fInputEvent; fOutputFile = new TFile(threadFileName.c_str(), "recreate"); - fOutputFile->SetCompressionLevel(0); + fOutputFile->SetCompressionLevel(fCompressionLevel); fOutputFile->cd(); debug << "Creating Analysis Tree..." << endl; From 932372605afa8ec9d9d8f933d811e17738d17617 Mon Sep 17 00:00:00 2001 From: nkx Nicholas Date: Sun, 8 May 2022 17:20:21 +0800 Subject: [PATCH 08/28] TrestAnalysisTree now supports operation under chain mode --- source/framework/core/inc/TRestAnalysisTree.h | 116 +++++++++- .../framework/core/src/TRestAnalysisTree.cxx | 212 +++++++++++++----- 2 files changed, 267 insertions(+), 61 deletions(-) diff --git a/source/framework/core/inc/TRestAnalysisTree.h b/source/framework/core/inc/TRestAnalysisTree.h index 819c67ebb..3b0bf6a9c 100644 --- a/source/framework/core/inc/TRestAnalysisTree.h +++ b/source/framework/core/inc/TRestAnalysisTree.h @@ -23,6 +23,7 @@ #ifndef RestCore_TRestAnalysisTree #define RestCore_TRestAnalysisTree +#include "TChain.h" #include "TRestEvent.h" #include "TRestReflector.h" #include "TTree.h" @@ -44,7 +45,7 @@ class TRestAnalysisTree : public TTree { Bool_t fQuickSetObservableValue = true; //! std::vector fObservables; //! std::map fObservableIdMap; //! - TTree* fROOTTree; //! + TChain* fChain = NULL; //! in case multiple files for reading // for storage Int_t fNObservables; @@ -87,8 +88,6 @@ class TRestAnalysisTree : public TTree { //!< There are branches in the tree with data. There are observables in the list. //! Once filled, it is forbidden to add new observable to the tree. Filled = 5, - //!< first status when constructed from ROOT tree - ROOTTree = 6 }; TRestAnalysisTree(TTree* tree); @@ -101,10 +100,19 @@ class TRestAnalysisTree : public TTree { int GetStatus() { return fStatus; } Int_t GetObservableID(const std::string& obsName); Bool_t ObservableExists(const std::string& obsName); - Int_t GetEventID() { return fEventID; } - Int_t GetSubEventID() { return fSubEventID; } - Double_t GetTimeStamp() { return fTimeStamp; } - TString GetSubEventTag() { return *fSubEventTag; } + // six basic event prameters + Int_t GetEventID() { return fChain ? ((TRestAnalysisTree*)fChain->GetTree())->GetEventID() : fEventID; } + Int_t GetSubEventID() { + return fChain ? ((TRestAnalysisTree*)fChain->GetTree())->GetEventID() : fSubEventID; + } + Double_t GetTimeStamp() { + return fChain ? ((TRestAnalysisTree*)fChain->GetTree())->GetEventID() : fTimeStamp; + } + TString GetSubEventTag() { + return fChain ? ((TRestAnalysisTree*)fChain->GetTree())->GetEventID() : *fSubEventTag; + } + // we suppose all the chained trees have same run and sub run id. + // so there is no need to call fChain->GetTree() Int_t GetRunOrigin() { return fRunOrigin; } Int_t GetSubRunOrigin() { return fSubRunOrigin; } Int_t GetNumberOfObservables() { return fNObservables; } @@ -128,6 +136,9 @@ class TRestAnalysisTree : public TTree { std::cout << "Error! TRestAnalysisTree::GetObservableValue(): index outside limits!" << endl; return T(); } + if (fChain != NULL) { + return ((TRestAnalysisTree*)fChain->GetTree())->GetObservableValue(n); + } return fObservables[n].GetValue(); } /////////////////////////////////////////////// @@ -156,6 +167,10 @@ class TRestAnalysisTree : public TTree { std::cout << "Error! TRestAnalysisTree::SetObservableValue(): index outside limits!" << endl; return; } + if (fChain != NULL) { + std::cout << "Error! cannot set observable! AnalysisTree is in chain state" << endl; + return; + } fObservables[id].SetValue(value); } /////////////////////////////////////////////// @@ -266,6 +281,93 @@ class TRestAnalysisTree : public TTree { Int_t WriteAsTTree(const char* name = 0, Int_t option = 0, Int_t bufsize = 0); + Bool_t AddChainFile(std::string file); + + TTree* GetTree() const; + + TChain* GetChain() { return fChain; } + + Long64_t LoadTree(Long64_t entry); + + Long64_t GetEntries() const; + + Long64_t GetEntries(const char* sel); + + void Browse(TBrowser* b) { fChain ? fChain->Browse(b) : TTree::Browse(b); } + Long64_t Draw(const char* varexp, const TCut& selection, Option_t* option = "", + Long64_t nentries = kMaxEntries, Long64_t firstentry = 0) { + return fChain ? fChain->Draw(varexp, selection, option, nentries, firstentry) + : TTree::Draw(varexp, selection, option, nentries, firstentry); + } + Long64_t Draw(const char* varexp, const char* selection, Option_t* option = "", + Long64_t nentries = kMaxEntries, Long64_t firstentry = 0) { + return fChain ? fChain->Draw(varexp, selection, option, nentries, firstentry) + : TTree::Draw(varexp, selection, option, nentries, firstentry); + } + void Draw(Option_t* opt) { fChain ? fChain->Draw(opt) : TTree::Draw(opt); } + TBranch* FindBranch(const char* name) { + return fChain ? fChain->FindBranch(name) : TTree::FindBranch(name); + } + TLeaf* FindLeaf(const char* name) { return fChain ? fChain->FindLeaf(name) : TTree::FindLeaf(name); } + TBranch* GetBranch(const char* name) { return fChain ? fChain->GetBranch(name) : TTree::GetBranch(name); } + Bool_t GetBranchStatus(const char* branchname) const { + return fChain ? fChain->GetBranchStatus(branchname) : TTree::GetBranchStatus(branchname); + } + Long64_t GetCacheSize() const { return fChain ? fChain->GetCacheSize() : TTree::GetCacheSize(); } + Long64_t GetChainEntryNumber(Long64_t entry) const { + return fChain ? fChain->GetChainEntryNumber(entry) : TTree::GetChainEntryNumber(entry); + } + Long64_t GetEntryNumber(Long64_t entry) const { + return fChain ? fChain->GetEntryNumber(entry) : TTree::GetEntryNumber(entry); + } + Long64_t GetReadEntry() const { return fChain ? fChain->GetReadEntry() : TTree::GetReadEntry(); } + + TLeaf* GetLeaf(const char* branchname, const char* leafname) { + return fChain ? fChain->GetLeaf(branchname, leafname) : TTree::GetLeaf(branchname, leafname); + } + TLeaf* GetLeaf(const char* name) { return fChain ? fChain->GetLeaf(name) : TTree::GetLeaf(name); } + TObjArray* GetListOfBranches() { + return fChain ? fChain->GetListOfBranches() : TTree::GetListOfBranches(); + } + TObjArray* GetListOfLeaves() { return fChain ? fChain->GetListOfLeaves() : TTree::GetListOfLeaves(); } + Long64_t Process(const char* filename, Option_t* option = "", Long64_t nentries = kMaxEntries, + Long64_t firstentry = 0) { + return fChain ? fChain->Process(filename, option, nentries, firstentry) + : TTree::Process(filename, option, nentries, firstentry); + } + Long64_t Process(TSelector* selector, Option_t* option = "", Long64_t nentries = kMaxEntries, + Long64_t firstentry = 0) { + return fChain ? fChain->Process(selector, option, nentries, firstentry) + : TTree::Process(selector, option, nentries, firstentry); + } + Long64_t Scan(const char* varexp = "", const char* selection = "", Option_t* option = "", + Long64_t nentries = kMaxEntries, Long64_t firstentry = 0) { + return fChain ? fChain->Scan(varexp, selection, option, nentries, firstentry) + : TTree::Scan(varexp, selection, option, nentries, firstentry); + } + Int_t SetBranchAddress(const char* bname, void* add, TBranch** ptr = 0) { + return fChain ? fChain->SetBranchAddress(bname, add, ptr) : TTree::SetBranchAddress(bname, add, ptr); + } + Int_t SetBranchAddress(const char* bname, void* add, TBranch** ptr, TClass* realClass, EDataType datatype, + Bool_t isptr) { + return fChain ? fChain->SetBranchAddress(bname, add, ptr, realClass, datatype, isptr) + : TTree::SetBranchAddress(bname, add, ptr, realClass, datatype, isptr); + } + Int_t SetBranchAddress(const char* bname, void* add, TClass* realClass, EDataType datatype, + Bool_t isptr) { + return fChain ? fChain->SetBranchAddress(bname, add, realClass, datatype, isptr) + : TTree::SetBranchAddress(bname, add, realClass, datatype, isptr); + } + void SetBranchStatus(const char* bname, Bool_t status = 1, UInt_t* found = 0) { + fChain ? fChain->SetBranchStatus(bname, status, found) : TTree::SetBranchStatus(bname, status, found); + } + void SetDirectory(TDirectory* dir) { fChain ? fChain->SetDirectory(dir) : TTree::SetDirectory(dir); } + + void ResetBranchAddress(TBranch* br) { + fChain ? fChain->ResetBranchAddress(br) : TTree::ResetBranchAddress(br); + } + void ResetBranchAddresses() { fChain ? fChain->ResetBranchAddresses() : TTree::ResetBranchAddresses(); } + // Construtor TRestAnalysisTree(); TRestAnalysisTree(TString name, TString title); diff --git a/source/framework/core/src/TRestAnalysisTree.cxx b/source/framework/core/src/TRestAnalysisTree.cxx index c210bf21a..5a8c3515b 100644 --- a/source/framework/core/src/TRestAnalysisTree.cxx +++ b/source/framework/core/src/TRestAnalysisTree.cxx @@ -72,6 +72,7 @@ #include "TRestAnalysisTree.h" +#include #include #include #include @@ -96,7 +97,6 @@ TRestAnalysisTree::TRestAnalysisTree(TTree* tree) : TTree() { Initialize(); - fROOTTree = tree; tree->GetEntry(0); TObjArray* lfs = tree->GetListOfLeaves(); @@ -134,7 +134,7 @@ void TRestAnalysisTree::Initialize() { fStatus = 0; fNObservables = 0; - fROOTTree = nullptr; + fChain = nullptr; fObservableIdMap.clear(); fObservableDescriptions.clear(); @@ -172,14 +172,16 @@ Bool_t TRestAnalysisTree::ObservableExists(const string& obsName) { /// \brief Evaluate the Status of this tree. /// /////////////////////////////////////////////////////////////////////////////// -// Status | Nbranches | NObs in list | Entries | -// 1. Created | 0 | 0 | 0 | -// 2. Retrieved/Cloned | >=6 | 0 | >0 | -// 3. EmptyCloned | >=6 | 0 | 0 | -// 4. Connected | >=6 | >=Nbranches-6 | 0 | -// 5. Filled | >=6 | =NObs | >0 | -// 6. ROOTTree | - | 0 | 0 | -// if Nbranches == 6, We regard the tree as Connected/Filled +// Status | Nbranches | NObservables | Entries | Description +// 1. Created | 0 | 0 | 0 | The AnalysisTree is just created in code +// 2. Retrieved | >=6 | 0 | >0 | The AnalysisTree is just retrieved from file +// 3. EmptyCloned | >=6 | 0 | 0 | The AnalysisTree is cloned from another +// tree, it is empty +// 4. Connected | >=6 | >=Nbranches-6 | 0 | Local observables are created and connected +// to the branches. We can still modify observables in this case. +// 5. Filled | >=6 | =NObs | >0 | Local observables are created and connected +// to the branches, and entry > 0. We cannot modify observables in this case. if Nbranches == 6, We regard the +// tree as Connected/Filled // // Status | AddObs Logic | Fill Logic | GetEntry Logic // 1. Created | Allowed | ->4, ->5 | ->4 @@ -187,16 +189,15 @@ Bool_t TRestAnalysisTree::ObservableExists(const string& obsName) { // 3. EmptyCloned | ->4 | ->4, ->5 | ->4 // 4. Connected | Allowed | ->5 | Allowed // 5. Filled | Denied | Allowed | Allowed -// 6. ROOTTree | Denied | Denied | Allowed // int TRestAnalysisTree::EvaluateStatus() { int NBranches = GetListOfBranches()->GetEntriesFast(); int NObsInList = fObservables.size(); int Entries = GetEntriesFast(); - if (fROOTTree != nullptr) { - return TRestAnalysisTree_Status::ROOTTree; - } + // if (fROOTTree != nullptr) { + // return TRestAnalysisTree_Status::ROOTTree; + //} if (NBranches == 0) { return TRestAnalysisTree_Status::Created; @@ -274,7 +275,11 @@ void TRestAnalysisTree::UpdateObservables() { } MakeObservableIdMap(); - TTree::GetEntry(0); + if (fChain) { + fChain->GetEntry(0); + } else { + TTree::GetEntry(0); + } for (int i = 0; i < GetNumberOfObservables(); i++) { TBranch* branch = GetBranch(fObservableNames[i]); @@ -470,6 +475,11 @@ RESTValue TRestAnalysisTree::GetObservable(Int_t n) { cout << "Error! TRestAnalysisTree::GetObservable(): index outside limits!" << endl; return RESTValue(); } + + if (fChain != nullptr) { + return ((TRestAnalysisTree*)fChain->GetTree())->GetObservable(n); + } + return fObservables[n]; } /////////////////////////////////////////////// @@ -545,8 +555,8 @@ RESTValue TRestAnalysisTree::AddObservable(TString observableName, TString obser } else if (fStatus == Filled) { cout << "REST_Warning: cannot add observable! AnalysisTree is already filled" << endl; return -1; - } else if (fStatus == ROOTTree) { - cout << "REST_Warning: cannot add observable for root tree!" << endl; + } else if (fChain != nullptr) { + cout << "Error! cannot add observable! AnalysisTree is in chain state" << endl; return -1; } @@ -587,15 +597,14 @@ void TRestAnalysisTree::PrintObservables() { } else if (fStatus == Connected) { cout << "REST_Warning: AnalysisTree is empty" << endl; } else if (fStatus == Filled) { - } else if (fStatus == ROOTTree) { } cout.precision(15); - std::cout << "Entry : " << fReadEntry << endl; - std::cout << "> Event ID : " << GetEventID() << endl; - std::cout << "> Event Time : " << GetTimeStamp() << endl; - std::cout << "> Event Tag : " << GetSubEventTag() << endl; - std::cout << "-----------------------------------------" << endl; + std::cout << "Entry : " << GetReadEntry() << std::endl; + std::cout << "> Event ID : " << GetEventID() << std::endl; + std::cout << "> Event Time : " << GetTimeStamp() << std::endl; + std::cout << "> Event Tag : " << GetSubEventTag() << std::endl; + std::cout << "-----------------------------------------" << std::endl; cout.precision(6); for (int n = 0; n < GetNumberOfObservables(); n++) { @@ -608,11 +617,11 @@ void TRestAnalysisTree::PrintObservable(int n) { if (n < 0 || n >= fNObservables) { return; } - string obsVal = fObservables[n].ToString(); + string obsVal = GetObservable(n).ToString(); int lengthRemaining = Console::GetWidth() - 14 - 45 - 13; std::cout << "Observable : " << ToString(fObservableNames[n], 45) - << " Value : " << ToString(obsVal, lengthRemaining) << endl; + << " Value : " << ToString(obsVal, lengthRemaining) << std::endl; } Int_t TRestAnalysisTree::GetEntry(Long64_t entry, Int_t getall) { @@ -626,37 +635,44 @@ Int_t TRestAnalysisTree::GetEntry(Long64_t entry, Int_t getall) { UpdateObservables(); } else if (fStatus == Connected) { } else if (fStatus == Filled) { - } else if (fStatus == ROOTTree) { - } - - if (fROOTTree != nullptr) { - // if it is connected to an external tree, we get entry on that - int result = fROOTTree->GetEntry(entry, getall); - - TObjArray* lfs = fROOTTree->GetListOfLeaves(); - if (fObservables.size() != lfs->GetLast() + 1) { - cout << "Warning: external TTree may have changed!" << endl; - for (int i = 0; i < lfs->GetLast() + 1; i++) { - TLeaf* lf = (TLeaf*)lfs->UncheckedAt(i); - RESTValue obs; - ReadLeafValueToObservable(lf, obs); - obs.name = lf->GetName(); - SetObservable(-1, obs); - } - } else { - for (int i = 0; i < lfs->GetLast() + 1; i++) { - TLeaf* lf = (TLeaf*)lfs->UncheckedAt(i); - ReadLeafValueToObservable(lf, fObservables[i]); - } - } - - return result; } - return TTree::GetEntry(entry, getall); + // if (fROOTTree != nullptr) { + // // if it is connected to an external tree, we get entry on that + // int result = fROOTTree->GetEntry(entry, getall); + + // TObjArray* lfs = fROOTTree->GetListOfLeaves(); + // if (fObservables.size() != lfs->GetLast() + 1) { + // cout << "Warning: external TTree may have changed!" << endl; + // for (int i = 0; i < lfs->GetLast() + 1; i++) { + // TLeaf* lf = (TLeaf*)lfs->UncheckedAt(i); + // RESTValue obs; + // ReadLeafValueToObservable(lf, obs); + // obs.name = lf->GetName(); + // SetObservable(-1, obs); + // } + // } else { + // for (int i = 0; i < lfs->GetLast() + 1; i++) { + // TLeaf* lf = (TLeaf*)lfs->UncheckedAt(i); + // ReadLeafValueToObservable(lf, fObservables[i]); + // } + // } + + // return result; + //} + if (fChain != nullptr) { + return fChain->GetEntry(entry, getall); + } else { + return TTree::GetEntry(entry, getall); + } } void TRestAnalysisTree::SetEventInfo(TRestEvent* evt) { + if (fChain != nullptr) { + cout << "Error! cannot fill tree. AnalysisTree is in chain state" << endl; + return; + } + if (evt != nullptr) { fEventID = evt->GetID(); fSubEventID = evt->GetSubID(); @@ -670,6 +686,11 @@ void TRestAnalysisTree::SetEventInfo(TRestEvent* evt) { Int_t TRestAnalysisTree::Fill() { if (fStatus == None) fStatus = EvaluateStatus(); + if (fChain != nullptr) { + cout << "Error! cannot fill tree. AnalysisTree is in chain state" << endl; + return -1; + } + fSetObservableIndex = 0; if (fStatus == Filled) { @@ -694,9 +715,6 @@ Int_t TRestAnalysisTree::Fill() { int a = TTree::Fill(); fStatus = EvaluateStatus(); return a; - } else if (fStatus == ROOTTree) { - cout << "REST_Warning: cannot Fill the root tree, read only!" << endl; - return -1; } return -1; } @@ -705,6 +723,10 @@ Int_t TRestAnalysisTree::Fill() { /// \brief Set the value of observable whose id is as specified /// void TRestAnalysisTree::SetObservable(Int_t id, RESTValue obs) { + if (fChain != nullptr) { + cout << "Error! cannot set observable! AnalysisTree is in chain state" << endl; + return; + } if (id == -1) { // this means we want to find observable id by its name id = GetObservableID(obs.name); @@ -982,6 +1004,88 @@ Int_t TRestAnalysisTree::WriteAsTTree(const char* name, Int_t option, Int_t bufs return result; } +/// +/// Add a series output file like TChain. +/// +/// The input file that contains another AnalysisTree with same run id +/// +Bool_t TRestAnalysisTree::AddChainFile(string _file) { + TFile* file = new TFile(_file.c_str(), "read"); + if (!file->IsOpen()) { + warning << "TRestAnalysisTree::AddChainFile(): failed to open file " << _file << endl; + return false; + } + + TRestAnalysisTree* tree = (TRestAnalysisTree*)file->Get("AnalysisTree"); + if (tree != nullptr && tree->GetEntry(0)) { + if (tree->GetEntry(0) > 0) { + if (tree->GetRunOrigin() == this->GetRunOrigin()) { + // this is a valid tree + delete tree; + delete file; + + if (fChain == nullptr) { + fChain = new TChain("AnalysisTree", "Chained AnalysisTree"); + fChain->Add(this->GetCurrentFile()->GetName()); + } + return fChain->Add(_file.c_str()) == 1; + } + warning << "TRestAnalysisTree::AddChainFile(): invalid file, AnalysisTree in file has different " + "run id!" + << endl; + } + warning << "TRestAnalysisTree::AddChainFile(): invalid file, AnalysisTree in file is empty!" << endl; + delete tree; + } + warning << "TRestAnalysisTree::AddChainFile(): invalid file, file does not contain an AnalysisTree!" + << endl; + + delete file; + + return false; +} + +/// +/// Overrides TTree::GetTree(), to get the actual tree used in case of chain operation(fCurrentTree != nullptr) +/// +/// +TTree* TRestAnalysisTree::GetTree() const { + if (fChain == nullptr) { + return TTree::GetTree(); + } + return (TTree*)fChain; +} + +/// +/// Overrides TTree::LoadTree(), to set current tree according to the given entry number, in case of chain +/// operation +/// +/// +/// +Long64_t TRestAnalysisTree::LoadTree(Long64_t entry) { + if (fChain == nullptr) { + return TTree::LoadTree(entry); + } else { + return fChain->LoadTree(entry); + } + + return -2; +} + +Long64_t TRestAnalysisTree::GetEntries() const { + if (fChain == nullptr) { + return TTree::GetEntries(); + } + return fChain->GetEntries(); +} + +Long64_t TRestAnalysisTree::GetEntries(const char* sel) { + if (fChain == nullptr) { + return TTree::GetEntries(sel); + } + return fChain->GetEntries(sel); +} + TRestAnalysisTree::~TRestAnalysisTree() { if (fSubEventTag != nullptr) delete fSubEventTag; } From 645a1b32ffbaf9ee7e238bb05ea9e80d8b6323a6 Mon Sep 17 00:00:00 2001 From: nkx Nicholas Date: Sun, 8 May 2022 17:29:30 +0800 Subject: [PATCH 09/28] TRestProcessRunner updated to auto split files --- source/framework/core/inc/TRestEventProcess.h | 2 + .../framework/core/inc/TRestProcessRunner.h | 26 ++- .../framework/core/src/TRestProcessRunner.cxx | 220 ++++++++++++------ 3 files changed, 173 insertions(+), 75 deletions(-) diff --git a/source/framework/core/inc/TRestEventProcess.h b/source/framework/core/inc/TRestEventProcess.h index 5d0f3eb36..6ac66ef12 100644 --- a/source/framework/core/inc/TRestEventProcess.h +++ b/source/framework/core/inc/TRestEventProcess.h @@ -208,6 +208,8 @@ class TRestEventProcess : public TRestMetadata { void SetFriendProcess(TRestEventProcess* p); /// Add parallel process to this process void SetParallelProcess(TRestEventProcess* p); + /// In case the analysis tree is reset(switched to new file), some process needs to have action + virtual void NotifyAnalysisTreeReset() {} // getters /// Get pointer to input event. Must be implemented in the derived class diff --git a/source/framework/core/inc/TRestProcessRunner.h b/source/framework/core/inc/TRestProcessRunner.h index b5a2003cf..c62ff84fa 100644 --- a/source/framework/core/inc/TRestProcessRunner.h +++ b/source/framework/core/inc/TRestProcessRunner.h @@ -36,11 +36,14 @@ class TRestProcessRunner : public TRestMetadata { // self variables for processing std::vector fThreads; //! - TFile* fTempOutputDataFile; //! - TTree* fEventTree; //! - TRestAnalysisTree* fAnalysisTree; //! - ProcStatus fProcStatus; //! - Int_t fNBranches; //! + TFile* fOutputDataFile; //! the TFile pointer being used + TString fOutputDataFileName; //! indicates the name of the first file created as output data file. The actual + //! output file maybe changed if tree is too large + TTree* fEventTree; //! + TRestAnalysisTree* fAnalysisTree; //! + ProcStatus fProcStatus; //! + Int_t fNBranches; //! + Int_t fNFilesSplit; //! Number of files being split. // metadata Bool_t fUseTestRun; @@ -56,8 +59,14 @@ class TRestProcessRunner : public TRestMetadata { Int_t fFirstEntry; Int_t fEventsToProcess; Int_t fProcessedEvents; + + Long64_t fFileSplitSize; // in bytes + Int_t fFileCompression; // 1~9 std::map fProcessInfo; + // bool fOutputItem[4] = { + // false}; // the on/off status for item: inputAnalysis, inputEvent, outputEvent, outputAnalysis + public: /// REST run class void Initialize(); @@ -89,6 +98,8 @@ class TRestProcessRunner : public TRestMetadata { Int_t GetNextevtFunc(TRestEvent* targetevt, TRestAnalysisTree* targettree); void FillThreadEventFunc(TRestThread* t); void ConfigOutputFile(); + void MergeOutputFile(); + void WriteMetadata(); // tools void ResetRunTimes(); @@ -100,7 +111,7 @@ class TRestProcessRunner : public TRestMetadata { TRestEvent* GetInputEvent(); TRestAnalysisTree* GetInputAnalysisTree(); TRestAnalysisTree* GetOutputAnalysisTree() { return fAnalysisTree; } - TFile* GetTempOutputDataFile() { return fTempOutputDataFile; } + TFile* GetOutputDataFile() { return fOutputDataFile; } std::string GetProcInfo(std::string infoname) { return fProcessInfo[infoname] == "" ? infoname : fProcessInfo[infoname]; } @@ -110,12 +121,13 @@ class TRestProcessRunner : public TRestMetadata { double GetReadingSpeed(); bool UseTestRun() const { return fUseTestRun; } ProcStatus GetStatus() { return fProcStatus; } + Long64_t GetFileSplitSize() { return fFileSplitSize; } // Constructor & Destructor TRestProcessRunner(); ~TRestProcessRunner(); - ClassDef(TRestProcessRunner, 6); + ClassDef(TRestProcessRunner, 7); }; #endif \ No newline at end of file diff --git a/source/framework/core/src/TRestProcessRunner.cxx b/source/framework/core/src/TRestProcessRunner.cxx index 3e71c0c65..e4258f774 100644 --- a/source/framework/core/src/TRestProcessRunner.cxx +++ b/source/framework/core/src/TRestProcessRunner.cxx @@ -23,6 +23,7 @@ ////////////////////////////////////////////////////////////////////////// #include "Math/MinimizerOptions.h" +#include "TBranchRef.h" #include "TInterpreter.h" #include "TMinuitMinimizer.h" #include "TMutex.h" @@ -30,13 +31,12 @@ #include "TRestManager.h" #include "TRestThread.h" -std::mutex mutexx; +std::mutex mutex_write; using namespace std; - #ifdef TIME_MEASUREMENT #include -using namespace chrono; +using namespace std::chrono; int deltaTime; int writeTime; int readTime; @@ -68,16 +68,21 @@ void TRestProcessRunner::Initialize() { fOutputEvent = nullptr; fEventTree = nullptr; fAnalysisTree = nullptr; - fTempOutputDataFile = nullptr; + fOutputDataFile = nullptr; + fOutputDataFileName = ""; + fThreads.clear(); fProcessInfo.clear(); fThreadNumber = 0; fFirstEntry = 0; + fNFilesSplit = 0; fEventsToProcess = 0; fProcessedEvents = 0; fProcessNumber = 0; fProcStatus = kNormal; + fFileSplitSize = 10000000000LL; // 10GB maximum + fFileCompression = 2; // default compression level fUseTestRun = true; fUsePauseMenu = true; @@ -148,6 +153,12 @@ void TRestProcessRunner::BeginOfInit() { } fRunInfo->SetCurrentEntry(fFirstEntry); + if (fFileSplitSize < 50000000LL || fFileSplitSize > 100000000000LL) { + warning << "automatic file splitting size cannot < 10MB or > 100GB, setting to default (10GB)." + << endl; + fFileSplitSize = 10000000000LL; + } + // fUseTestRun = StringToBool(GetParameter("useTestRun", "ON")); // fUsePauseMenu = StringToBool(GetParameter("usePauseMenu", "OFF")); if (!fUsePauseMenu || fVerboseLevel >= REST_Debug) fProcStatus = kIgnore; @@ -173,6 +184,7 @@ void TRestProcessRunner::BeginOfInit() { t->SetProcessRunner(this); t->SetVerboseLevel(fVerboseLevel); t->SetThreadId(i); + t->SetCompressionLevel(fFileCompression); fThreads.push_back(t); } } @@ -211,14 +223,18 @@ Int_t TRestProcessRunner::ReadConfig(string keydeclare, TiXmlElement* e) { fRunInfo->SetExtProcess(p); return 0; } - if (fThreadNumber > 1 && (p->GetVerboseLevel() >= REST_Debug || p->singleThreadOnly())) { + if ((p->GetVerboseLevel() >= REST_Debug || p->singleThreadOnly())) { + fUsePauseMenu = false; fProcStatus = kIgnore; - info << "multi-threading is disabled due to process \"" << p->GetName() << "\"" << endl; - info << "This process is in debug mode or is single thread only" << endl; - for (i = fThreadNumber; i > 1; i--) { - delete (*(fThreads.end() - 1)); - fThreads.erase(fThreads.end() - 1); - fThreadNumber--; + if (fThreadNumber > 1) { + info << "multi-threading is disabled due to process \"" << p->GetName() << "\"" + << endl; + info << "This process is in debug mode or is single thread only" << endl; + for (i = fThreadNumber; i > 1; i--) { + // delete (*fThreads.end()); + fThreads.erase(fThreads.end() - 1); + fThreadNumber--; + } } } processes.push_back(p); @@ -313,9 +329,13 @@ void TRestProcessRunner::RunProcess() { debug << "Creating output File " << fRunInfo->GetOutputFileName() << endl; TString filename = fRunInfo->FormFormat(fRunInfo->GetOutputFileName()); - fTempOutputDataFile = new TFile(filename, "recreate"); - if (!fTempOutputDataFile->IsOpen()) { - ferr << "Failed to create output file: " << fTempOutputDataFile << endl; + fOutputDataFileName = filename; + fOutputDataFile = new TFile(filename, "recreate"); + // set compression level here will cause problem in pipeline + // we must set in each threadCompression + // fOutputDataFile->SetCompressionLevel(fFile); + if (!fOutputDataFile->IsOpen()) { + ferr << "Failed to create output file: " << fOutputDataFile->GetName() << endl; exit(1); } info << endl; @@ -353,25 +373,24 @@ void TRestProcessRunner::RunProcess() { fout << "=" << endl; // copy thread's event tree to local - fTempOutputDataFile->cd(); + fOutputDataFile->cd(); TTree* tree = fThreads[0]->GetEventTree(); if (tree != nullptr) { - fEventTree = (TRestAnalysisTree*)tree->Clone(); + fEventTree = (TTree*)tree->Clone(); fEventTree->SetName("EventTree"); - string outputeventname; - if (fThreads[0]->GetOutputEvent() != nullptr) { - outputeventname = fThreads[0]->GetOutputEvent()->ClassName(); - } - - fEventTree->SetTitle((outputeventname + "Tree").c_str()); - fEventTree->SetDirectory(fTempOutputDataFile); + fEventTree->SetTitle("REST Event Tree"); + fEventTree->SetDirectory(fOutputDataFile); + fEventTree->SetMaxTreeSize(100000000000LL > fFileSplitSize * 2 + ? 100000000000LL + : fFileSplitSize * 2); // the default size is 100GB } else { fEventTree = nullptr; } // initialize analysis tree fAnalysisTree = new TRestAnalysisTree("AnalysisTree", "REST Process Analysis Tree"); - fAnalysisTree->SetDirectory(fTempOutputDataFile); + fAnalysisTree->SetDirectory(fOutputDataFile); + fAnalysisTree->SetMaxTreeSize(100000000000LL > fFileSplitSize * 2 ? 100000000000LL : fFileSplitSize * 2); tree = fThreads[0]->GetAnalysisTree(); if (tree != nullptr) { @@ -381,6 +400,8 @@ void TRestProcessRunner::RunProcess() { exit(1); } + ConfigOutputFile(); + // reset runner this->ResetRunTimes(); fProcessedEvents = 0; @@ -451,10 +472,7 @@ void TRestProcessRunner::RunProcess() { while (getchar() != '\n') ; // clear buffer - // CursorDown(4); - essential << "Waiting for processes to finish ..." << endl; - while (1) { #ifdef WIN32 _sleep(50); @@ -468,8 +486,9 @@ void TRestProcessRunner::RunProcess() { if (finish) break; } - // make analysis tree filled with observables, which may used in EndProcess() + // make dummy analysis tree filled with observables fAnalysisTree->GetEntry(fAnalysisTree->GetEntries() - 1); + // call EndProcess() for all processes for (int i = 0; i < fThreadNumber; i++) { fThreads[i]->EndProcess(); } @@ -504,6 +523,7 @@ void TRestProcessRunner::RunProcess() { if (fRunInfo->GetOutputFileName() != "/dev/null") { ConfigOutputFile(); + MergeOutputFile(); } } @@ -644,7 +664,7 @@ void TRestProcessRunner::PauseMenu() { if (pid == 0) { fout << "Child process created! pid: " << getpid() << endl; info << "Restarting threads" << endl; - mutexx.unlock(); + mutex_write.unlock(); for (int i = 0; i < fThreadNumber; i++) { fThreads[i]->StartThread(); } @@ -714,7 +734,7 @@ void TRestProcessRunner::PauseMenu() { /// over. This method returns -1. /// Int_t TRestProcessRunner::GetNextevtFunc(TRestEvent* targetevt, TRestAnalysisTree* targettree) { - mutexx.lock(); // lock on + mutex_write.lock(); // lock on while (fProcStatus == kPause) { #ifdef WIN32 _sleep(50); @@ -740,7 +760,7 @@ Int_t TRestProcessRunner::GetNextevtFunc(TRestEvent* targetevt, TRestAnalysisTre high_resolution_clock::time_point t2 = high_resolution_clock::now(); readTime += (int)duration_cast(t2 - t1).count(); #endif - mutexx.unlock(); + mutex_write.unlock(); return n; } @@ -779,7 +799,7 @@ void TRestProcessRunner::FillThreadEventFunc(TRestThread* t) { } // Start event saving, entering mutex lock region. - mutexx.lock(); + mutex_write.lock(); #ifdef TIME_MEASUREMENT high_resolution_clock::time_point t5 = high_resolution_clock::now(); #endif @@ -824,6 +844,74 @@ void TRestProcessRunner::FillThreadEventFunc(TRestThread* t) { } fProcessedEvents++; + // cout << fTempOutputDataFile << " " << fTempOutputDataFile->GetEND() << " " << + // fAnalysisTree->GetDirectory() << endl; cout << fAutoSplitFileSize << endl; switch file if file size + // reaches target + if (fOutputDataFile->GetEND() > fFileSplitSize) { + if (fAnalysisTree->GetDirectory() == (TDirectory*)fOutputDataFile) { + fNFilesSplit++; + cout << "TRestProcessRunner: file size reaches limit (" << fFileSplitSize + << " bytes), switching to new file with index " << fNFilesSplit << endl; + + // wait 0.1s for the process to finish + usleep(100000); + for (int i = 0; i < fThreadNumber; i++) { + for (int j = 0; j < fProcessNumber; j++) { + auto proc = fThreads[i]->GetProcess(j); + proc->NotifyAnalysisTreeReset(); + } + } + + fAnalysisTree->AutoSave(); + fAnalysisTree->Reset(); + + if (fEventTree != nullptr) { + fEventTree->AutoSave(); + fEventTree->Reset(); + } + + // write some information to the first(main) data file + fRunInfo->SetNFilesSplit(fNFilesSplit); + if (fOutputDataFile->GetName() != fOutputDataFileName) { + TFile* Mainfile = new TFile(fOutputDataFileName, "update"); + WriteMetadata(); + Mainfile->Close(); + delete Mainfile; + } else { + WriteMetadata(); + } + + TFile* newfile = new TFile(fOutputDataFileName + "." + ToString(fNFilesSplit), "recreate"); + + TBranch* branch = 0; + fAnalysisTree->SetDirectory(newfile); + TIter nextb1(fAnalysisTree->GetListOfBranches()); + while ((branch = (TBranch*)nextb1())) { + branch->SetFile(newfile); + } + if (fAnalysisTree->GetBranchRef()) { + fAnalysisTree->GetBranchRef()->SetFile(newfile); + } + + if (fEventTree != nullptr) { + fEventTree->SetDirectory(newfile); + TIter nextb2(fEventTree->GetListOfBranches()); + while ((branch = (TBranch*)nextb2())) { + branch->SetFile(newfile); + } + if (fEventTree->GetBranchRef()) { + fEventTree->GetBranchRef()->SetFile(newfile); + } + } + + fOutputDataFile->Close(); + delete fOutputDataFile; + fOutputDataFile = newfile; + } else { + ferr << "internal error!" << endl; + } + } + #ifdef TIME_MEASUREMENT high_resolution_clock::time_point t6 = high_resolution_clock::now(); writeTime += (int)duration_cast(t6 - t5).count(); @@ -834,7 +922,7 @@ void TRestProcessRunner::FillThreadEventFunc(TRestThread* t) { cout << "Processed events:" << fProcessedEvents << endl; } } - mutexx.unlock(); + mutex_write.unlock(); } /////////////////////////////////////////////// @@ -843,63 +931,58 @@ void TRestProcessRunner::FillThreadEventFunc(TRestThread* t) { /// It first saves process metadata in to the main output file, then calls /// TRestRun::FormOutputFile() to merge the main file with process's tmp file. void TRestProcessRunner::ConfigOutputFile() { - essential << "Configuring output file, merging thread files together" << endl; + essential << "Configuring output file, writing metadata and tree objects" << endl; #ifdef TIME_MEASUREMENT fProcessInfo["ProcessTime"] = ToString(deltaTime) + "ms"; #endif + // write tree + fOutputDataFile->cd(); + if (fEventTree != nullptr) fEventTree->Write(0, kWriteDelete); + if (fAnalysisTree != nullptr) fAnalysisTree->Write(0, kWriteDelete); - vector files_to_merge; + // go back to the first file + if (fOutputDataFile->GetName() != fOutputDataFileName) { + delete fOutputDataFile; + fOutputDataFile = new TFile(fOutputDataFileName, "update"); + } + // write metadata + WriteMetadata(); +} - // add data file - // string savemetadata = GetParameter("saveMetadata", "true"); - // if (savemetadata == "true" || savemetadata == "True" || savemetadata == - // "yes" || savemetadata == "ON") - //{ - fTempOutputDataFile->cd(); +void TRestProcessRunner::WriteMetadata() { fRunInfo->Write(); this->Write(); char tmpString[256]; if (fRunInfo->GetFileProcess() != nullptr) { - sprintf(tmpString, "Process-%d. %s", 0, fRunInfo->GetFileProcess()->GetName()); - fRunInfo->GetFileProcess()->Write(); + // sprintf(tmpString, "Process-%d. %s", 0, fRunInfo->GetFileProcess()->GetName()); + fRunInfo->GetFileProcess()->Write(0, kWriteDelete); } for (int i = 0; i < fProcessNumber; i++) { - sprintf(tmpString, "Process-%d. %s", i + 1, fThreads[0]->GetProcess(i)->GetName()); - fThreads[0]->GetProcess(i)->Write(); + // sprintf(tmpString, "Process-%d. %s", i + 1, fThreads[0]->GetProcess(i)->GetName()); + fThreads[0]->GetProcess(i)->Write(0, kWriteDelete); } +} - //} - if (fEventTree != nullptr) fEventTree->Write(0, kWriteDelete); - if (fAnalysisTree != nullptr) fAnalysisTree->Write(0, kWriteDelete); - fTempOutputDataFile->Close(); - // files_to_merge.push_back(fTempOutputDataFile->GetName()); - +/////////////////////////////////////////////// +/// \brief Forming an output file +/// +/// It first saves process metadata in to the main output file, then calls +/// TRestRun::FormOutputFile() to merge the main file with process's tmp file. +void TRestProcessRunner::MergeOutputFile() { + essential << "Merging thread files together" << endl; // add threads file // processes may have their own TObject output. They are stored in the threads // file these files are mush smaller that data file, so they are merged to the // data file. + vector files_to_merge; for (int i = 0; i < fThreadNumber; i++) { TFile* f = fThreads[i]->GetOutputFile(); if (f != nullptr) f->Close(); files_to_merge.push_back(f->GetName()); } - fRunInfo->MergeToOutputFile(files_to_merge, fTempOutputDataFile->GetName()); - - std::vector mdNames = fRunInfo->GetMetadataStructureNames(); - Int_t nErrors = 0; - Int_t nWarnings = 0; - for (int n = 0; n < mdNames.size(); n++) { - if (fRunInfo->GetMetadata(mdNames[n])->GetError()) nErrors++; - if (fRunInfo->GetMetadata(mdNames[n])->GetWarning()) nWarnings++; - } - - if (nWarnings) - warning << nWarnings - << " process warnings were found! Use run0->PrintWarnings(); to get additional info." << endl; - if (nErrors) - ferr << nErrors << " process errors were found! Use run0->PrintErrors(); to get additional info." - << endl; + fOutputDataFile->Close(); + fRunInfo->MergeToOutputFile(files_to_merge, fOutputDataFile->GetName()); } // tools @@ -1072,7 +1155,8 @@ void TRestProcessRunner::PrintMetadata() { metadata << "Analysis tree branches : " << fNBranches << endl; metadata << "Thread number : " << fThreadNumber << endl; metadata << "Processes in each thread : " << fProcessNumber << endl; - + metadata << "File auto split size: " << fFileSplitSize << endl; + metadata << "File compression level: " << fFileCompression << endl; // cout << "Input filename : " << fInputFilename << endl; // cout << "Output filename : " << fOutputFilename << endl; // cout << "Number of initial events : " << GetNumberOfEvents() << endl; From 1bf18088b81d9e4143c8ee72956af8d5c8a047b2 Mon Sep 17 00:00:00 2001 From: nkx Nicholas Date: Sun, 8 May 2022 17:30:01 +0800 Subject: [PATCH 10/28] TRestRun supported to read splitted data files --- source/framework/core/inc/TRestRun.h | 8 +- source/framework/core/src/TRestRun.cxx | 133 +++++++++++++++------- source/framework/core/src/TRestThread.cxx | 2 +- 3 files changed, 102 insertions(+), 41 deletions(-) diff --git a/source/framework/core/inc/TRestRun.h b/source/framework/core/inc/TRestRun.h index 535ea354b..8ce5df782 100644 --- a/source/framework/core/inc/TRestRun.h +++ b/source/framework/core/inc/TRestRun.h @@ -35,6 +35,7 @@ class TRestRun : public TRestMetadata { Double_t fStartTime; ///< Event absolute starting time/date (unix timestamp) Double_t fEndTime; ///< Event absolute ending time/date (unix timestamp) Int_t fEntriesSaved; + Int_t fNFilesSplit; // Number of files being split. Used when retrieveing // data-like metadata objects std::vector fMetadata; //! @@ -126,6 +127,10 @@ class TRestRun : public TRestMetadata { void AddEventBranch(TRestEvent* eve); void SkipEventTree() {} + void cd() { + if (fInputFile != NULL) fInputFile->cd(); + } + // Getters inline Int_t GetParentRunNumber() const { return fParentRunNumber; } inline Int_t GetRunNumber() const { return fRunNumber; } @@ -214,6 +219,7 @@ class TRestRun : public TRestMetadata { inline void SetEndTimeStamp(Double_t timestamp) { fEndTime = timestamp; } inline void SetTotalBytes(Long64_t totalBytes) { fTotalBytes = totalBytes; } inline void SetHistoricMetadataSaving(bool save) { fSaveHistoricData = save; } + inline void SetNFilesSplit(int n) { fNFilesSplit = n; } inline void HangUpEndFile() { fHangUpEndFile = true; } inline void ReleaseEndFile() { fHangUpEndFile = false; } // Printers @@ -252,7 +258,7 @@ class TRestRun : public TRestMetadata { TRestRun(const std::string& filename); ~TRestRun(); - ClassDef(TRestRun, 5); + ClassDef(TRestRun, 6); }; #endif diff --git a/source/framework/core/src/TRestRun.cxx b/source/framework/core/src/TRestRun.cxx index e87a93c81..19b617d02 100644 --- a/source/framework/core/src/TRestRun.cxx +++ b/source/framework/core/src/TRestRun.cxx @@ -34,7 +34,7 @@ using namespace std; -mutex mutex2; +std::mutex mutex_read; ClassImp(TRestRun); @@ -93,6 +93,7 @@ void TRestRun::Initialize() { fTotalBytes = -1; fOverwrite = true; fEntriesSaved = -1; + fNFilesSplit = 0; fInputMetadata.clear(); fMetadata.clear(); @@ -162,8 +163,6 @@ void TRestRun::InitFromConfigFile() { if (ToUpper(runNstr) != "AUTO") { fRunNumber = atoi(runNstr.c_str()); - fStartTime = gDataBase->query_run(fRunNumber).tstart; - fEndTime = gDataBase->query_run(fRunNumber).tend; } if (ToUpper(inputname) == "AUTO") { @@ -182,6 +181,7 @@ void TRestRun::InitFromConfigFile() { fRunNumber = -1; } + // add a new run if (fRunNumber == 0) { fRunNumber = db->get_lastrun() + 1; DBEntry entry; @@ -194,6 +194,18 @@ void TRestRun::InitFromConfigFile() { } } + // get some run information + if (fRunNumber != -1) { + DBEntry entry = gDataBase->query_run(fRunNumber); + if (!entry.IsZombie()) { + fStartTime = entry.tstart; + fEndTime = entry.tend; + fRunDescription = entry.description; + fRunTag = entry.tag; + fRunType = entry.type; + } + } + if (fInputFileNames.size() == 0) { if (fInputFileName != "") { ferr << "cannot find the input file!" << endl; @@ -435,17 +447,20 @@ void TRestRun::OpenInputFile(const TString& filename, const string& mode) { ReadInputFileTrees(); fCurrentEvent = 0; } else { - fAnalysisTree = nullptr; + warning << "TRestRun object not found in file! The input file is problematic!" << endl; - // set its analysistree as the first TTree object in the file, if exists - TIter nextkey(fInputFile->GetListOfKeys()); - TKey* key; - while ((key = (TKey*)nextkey())) { - if ((string)key->GetClassName() == "TTree") { - fAnalysisTree = - TRestAnalysisTree::ConvertFromTTree((TTree*)fInputFile->Get(key->GetName())); - } - } + ReadInputFileTrees(); + // fAnalysisTree = nullptr; + + //// set its analysistree as the first TTree object in the file, if exists + // TIter nextkey(fInputFile->GetListOfKeys()); + // TKey* key; + // while ((key = (TKey*)nextkey())) { + // if ((string)key->GetClassName() == "TTree") { + // fAnalysisTree = + // TRestAnalysisTree::ConvertFromTTree((TTree*)fInputFile->Get(key->GetName())); + // } + //} } } else { fInputFile = nullptr; @@ -461,7 +476,7 @@ void TRestRun::OpenInputFile(const TString& filename, const string& mode) { } void TRestRun::AddInputFileExternal(const string& file) { - mutex2.lock(); + mutex_read.lock(); if (fFileProcess != nullptr) { bool add = fFileProcess->AddInputFile(file); if (!add) { @@ -469,7 +484,7 @@ void TRestRun::AddInputFileExternal(const string& file) { } fInputFileNames.push_back(file); } - mutex2.unlock(); + mutex_read.unlock(); } void TRestRun::ReadInputFileMetadata() { @@ -526,7 +541,30 @@ void TRestRun::ReadInputFileTrees() { if (fInputFile->Get("AnalysisTree") != nullptr) { fAnalysisTree = (TRestAnalysisTree*)fInputFile->Get("AnalysisTree"); - fAnalysisTree->GetEntry(0); // we call GetEntry() to connect branches + + if (fNFilesSplit > 0) { // fNFilesSplit=1: split to 1 additional file + essential << "Linking analysis tree from split data files" << endl; + fAnalysisTree = + (TRestAnalysisTree*) + fAnalysisTree->Clone(); // we must make a copy to have TBrowser correctly browsed. + for (int i = 1; i <= fNFilesSplit; i++) { + string filename = fInputFile->GetName() + (string) "." + ToString(i); + info << filename << " --> "; + info << (fAnalysisTree->AddChainFile(filename) ? "success" : "failed") << endl; + } + if (fAnalysisTree->GetChain() == nullptr || + fAnalysisTree->GetChain()->GetNtrees() != fNFilesSplit + 1) { + ferr << "Error adding split files, files missing?" << endl; + ferr << "Your data could be incomplete!" << endl; + } + } + + // Note: we call GetEntries() to initialize total entry number + // Otherwise the child analysis tree's observables will be reset + // on the next call of GetEntries() + fAnalysisTree->GetEntries(); + // Call GetEntry() to initialize observables and connect branches + fAnalysisTree->GetEntry(0); _eventTree = (TTree*)fInputFile->Get("EventTree"); } else if (fInputFile->FindKey("TRestAnalysisTree") != nullptr) { @@ -561,7 +599,23 @@ void TRestRun::ReadInputFileTrees() { } if (_eventTree != nullptr) { - fEventTree = _eventTree; + if (fNFilesSplit > 0) { + // eventTree shall be initailized as TChain + delete _eventTree; + essential << "Linking event tree from split data files" << endl; + TChain* _fEventTree = new TChain("EventTree"); + info << fInputFile->GetName() << " --> "; + info << (_fEventTree->Add(fInputFile->GetName()) ? "success" : "failed") << endl; + + for (int i = 1; i <= fNFilesSplit; i++) { + string filename = fInputFile->GetName() + (string) "." + ToString(i); + info << filename << " --> "; + info << (_fEventTree->Add(filename.c_str()) ? "success" : "failed") << endl; + } + fEventTree = _fEventTree; + } else { + fEventTree = _eventTree; + } debug << "Finding event branch.." << endl; if (fInputEvent == nullptr) { @@ -758,44 +812,41 @@ Int_t TRestRun::GetNextEvent(TRestEvent* targetevt, TRestAnalysisTree* targettre if (fFileProcess != nullptr) { debug << "TRestRun: getting next event from external process" << endl; GetEventExt: - mutex2.lock(); + mutex_read.lock(); fFileProcess->BeginOfEventProcess(); eve = fFileProcess->ProcessEvent(nullptr); fFileProcess->EndOfEventProcess(); - mutex2.unlock(); + mutex_read.unlock(); fBytesRead = fFileProcess->GetTotalBytesReaded(); - if (targettree != nullptr) { - for (int n = 0; n < fAnalysisTree->GetNumberOfObservables(); n++) - targettree->SetObservable(n, fAnalysisTree->GetObservable(n)); - } + // if (targettree != nullptr) { + // for (int n = 0; n < fAnalysisTree->GetNumberOfObservables(); n++) + // targettree->SetObservable(n, fAnalysisTree->GetObservable(n)); + //} fCurrentEvent++; } else { debug << "TRestRun: getting next event from root file" << endl; - if (fAnalysisTree != nullptr) { - if (fCurrentEvent >= fAnalysisTree->GetEntriesFast()) { + if (fAnalysisTree == nullptr) { + warning << "error to get event from input file, missing analysis tree from input file" << endl; + eve = nullptr; + } else { + if (fCurrentEvent >= fAnalysisTree->GetTree()->GetEntriesFast()) { eve = nullptr; } else { - eve->Initialize(); - fBytesRead += fAnalysisTree->GetEntry(fCurrentEvent); if (targettree != nullptr) { + // normal reading procedure + eve->Initialize(); + fBytesRead += fAnalysisTree->GetEntry(fCurrentEvent); for (int n = 0; n < fAnalysisTree->GetNumberOfObservables(); n++) targettree->SetObservable(n, fAnalysisTree->GetObservable(n)); } if (fEventTree != nullptr) { fBytesRead += ((TBranch*)fEventTree->GetListOfBranches()->UncheckedAt(fEventBranchLoc)) - ->GetEntry(fCurrentEvent); + ->GetEntry(fCurrentEvent); // fBytesReaded += fEventTree->GetEntry(fCurrentEvent); } fCurrentEvent++; } } - - else { - warning << "error to get event from input file, missing file process or " - "analysis tree" - << endl; - eve = nullptr; - } } // cout << fHangUpEndFile << endl; @@ -1595,7 +1646,6 @@ string TRestRun::ReplaceMetadataMember(const string& instr) { if (instr.find("::") == string::npos && instr.find("->") == string::npos) { return "<<" + instr + ">>"; } - vector results = Split(instr, "::", false, true); if (results.size() == 1) results = Split(instr, "->", false, true); @@ -1727,6 +1777,14 @@ void TRestRun::PrintMetadata() { << endl; metadata << "Input file : " << TRestTools::GetPureFileName((string)GetInputFileNamePattern()) << endl; metadata << "Output file : " << TRestTools::GetPureFileName((string)GetOutputFileName()) << endl; + if (fInputFile != nullptr) { + metadata << "Data file : " << fInputFile->GetName(); + if (fNFilesSplit > 0) { + metadata << " (Splitted into " << fNFilesSplit + 1 << " files)" << endl; + } else { + metadata << endl; + } + } metadata << "Number of events : " << fEntriesSaved << endl; // metadata << "Input filename : " << fInputFilename << endl; // metadata << "Output filename : " << fOutputFilename << endl; @@ -1759,9 +1817,6 @@ void TRestRun::PrintStartDate() { cout << "++++++++++++++++++++++++" << endl; } -/////////////////////////////////////////////// -/// \brief Prints the run end date and time in human format -/// void TRestRun::PrintEndDate() { cout << "----------------------" << endl; cout << "---- Run end date ----" << endl; diff --git a/source/framework/core/src/TRestThread.cxx b/source/framework/core/src/TRestThread.cxx index f7d2b05aa..8c6137e16 100644 --- a/source/framework/core/src/TRestThread.cxx +++ b/source/framework/core/src/TRestThread.cxx @@ -221,7 +221,7 @@ void TRestThread::PrepareToProcess(bool* outputConfig) { debug << "Entering TRestThread::PrepareToProcess" << endl; string threadFileName; - if (fHostRunner->GetTempOutputDataFile()->GetName() == (string) "/dev/null") { + if (fHostRunner->GetOutputDataFile()->GetName() == (string) "/dev/null") { threadFileName = "/dev/null"; } else { threadFileName = "/tmp/rest_thread_tmp" + ToString(this) + ".root"; From 84fd1a00843e1a3e908a78cdfbaf07308033dc11 Mon Sep 17 00:00:00 2001 From: nkx Nicholas Date: Sun, 8 May 2022 17:30:17 +0800 Subject: [PATCH 11/28] restRoot supported to read splitted data files --- source/bin/restRoot.cxx | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/source/bin/restRoot.cxx b/source/bin/restRoot.cxx index f34fb1f2d..2e963a1fa 100644 --- a/source/bin/restRoot.cxx +++ b/source/bin/restRoot.cxx @@ -103,6 +103,36 @@ int main(int argc, char* argv[]) { runTmp->GetEntry(0); } + // command line AnalysisTree object + if (runTmp->GetAnalysisTree() != NULL) { + // if (runTmp->GetAnalysisTree()->GetChain() != NULL) { + // printf("Attaching ana_tree%i...\n", Nfile); + // string evcmd = Form("TChain* ana_tree%i = (TChain*)%s;", Nfile, + // ToString(runTmp->GetAnalysisTree()->GetChain()).c_str()); + // if (debug) printf("%s\n", evcmd.c_str()); + // gROOT->ProcessLine(evcmd.c_str()); + //} + // else + //{ + printf("Attaching ana_tree%i...\n", Nfile); + string evcmd = Form("TRestAnalysisTree* ana_tree%i = (TRestAnalysisTree*)%s;", Nfile, + ToString(runTmp->GetAnalysisTree()).c_str()); + if (debug) printf("%s\n", evcmd.c_str()); + gROOT->ProcessLine(evcmd.c_str()); + // runTmp->GetEntry(0); + //} + } + + // command line EventTree object + if (runTmp->GetEventTree() != NULL) { + printf("Attaching ev_tree%i...\n", Nfile); + string evcmd = + Form("TTree* ev_tree%i = (TTree*)%s;", Nfile, ToString(runTmp->GetEventTree()).c_str()); + if (debug) printf("%s\n", evcmd.c_str()); + gROOT->ProcessLine(evcmd.c_str()); + } + + printf("\n%s\n", "Attaching metadata structures..."); Int_t Nmetadata = runTmp->GetNumberOfMetadataStructures(); map metanames; From 783e6df93099502499e22041a6df8b6276a5899c Mon Sep 17 00:00:00 2001 From: nkx Nicholas Date: Sun, 8 May 2022 17:31:41 +0800 Subject: [PATCH 12/28] TRestReflector::Converter adds both ROOT def type(Long64_t) and standard c++ type name(long long) --- source/framework/tools/inc/TRestReflector.h | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/source/framework/tools/inc/TRestReflector.h b/source/framework/tools/inc/TRestReflector.h index f4c56868b..7dff4644b 100644 --- a/source/framework/tools/inc/TRestReflector.h +++ b/source/framework/tools/inc/TRestReflector.h @@ -445,20 +445,24 @@ class Converter : RESTVirtualConverter { } void CloneObj(void* from, void* to) override { *((T*)(to)) = *((T*)(from)); } - Converter(std::string (*_ToStringFunc)(T), T (*_ParseStringFunc)(std::string)) { + Converter(std::string type_name, std::string (*_ToStringFunc)(T), T (*_ParseStringFunc)(std::string)) { ToStringFunc = _ToStringFunc; ParseStringFunc = _ParseStringFunc; - std::string typestr = REST_Reflection::GetTypeName(); - if (RESTConverterMethodBase.count(typestr) > 0) { - std::cout << "Warning! converter for type: " << typestr << " already added!" << endl; + if (RESTConverterMethodBase.count(type_name) > 0) { + std::cout << "Warning! converter for type: " << type_name << " already added!" << endl; } else { - RESTConverterMethodBase[typestr] = this; + RESTConverterMethodBase[type_name] = this; + } + + std::string type_name_actual = REST_Reflection::GetTypeName(); // in case ROOT redefines type name + if (RESTConverterMethodBase.count(type_name_actual) == 0) { + RESTConverterMethodBase[type_name_actual] = this; } } }; #define AddConverter(ToStringFunc, ParseStringFunc, type) \ template <> \ - Converter* Converter::thisptr = new Converter(&ToStringFunc, &ParseStringFunc); + Converter* Converter::thisptr = new Converter(#type, &ToStringFunc, &ParseStringFunc); #endif From 89b3aef928dc2210fddb76e73872f09fa9ea9f72 Mon Sep 17 00:00:00 2001 From: nkx Nicholas Date: Sun, 8 May 2022 18:21:21 +0800 Subject: [PATCH 13/28] fixing g4 selection pipeline --- pipeline/selection/Validate.C | 4 +++- pipeline/selection/g4OnSelection.rml | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/pipeline/selection/Validate.C b/pipeline/selection/Validate.C index 81abf127c..56993b5d8 100644 --- a/pipeline/selection/Validate.C +++ b/pipeline/selection/Validate.C @@ -7,6 +7,8 @@ Int_t Validate(TString fname) { TFile* f = new TFile(fname); TTree* tr = (TTree*)f->Get("AnalysisTree"); + tr->Scan("eventID:g4Ana_totalEdep:g4Ana_energyPrimary"); + if (tr->GetEntries() != 3) { cout << "Number of entries is not the same!" << endl; cout << "Expected: 3. Obtained: " << tr->GetEntries() << endl; @@ -14,7 +16,7 @@ Int_t Validate(TString fname) { } // Check IDs. - std::vector ids = {0, 3, 6}; + std::vector ids = {0, 1, 4}; for (Int_t i = 0; i < tr->GetEntries(); i++) { tr->GetEntry(i); if (tr->GetLeaf("eventID")->GetValue(0) != ids[i]) { diff --git a/pipeline/selection/g4OnSelection.rml b/pipeline/selection/g4OnSelection.rml index a1e1f1d51..10e46dd69 100644 --- a/pipeline/selection/g4OnSelection.rml +++ b/pipeline/selection/g4OnSelection.rml @@ -26,7 +26,7 @@ - + From 05191666084d0a0873956f4aca54f25f6b3ae8c1 Mon Sep 17 00:00:00 2001 From: nkx Nicholas Date: Sun, 8 May 2022 23:09:37 +0800 Subject: [PATCH 14/28] ci: ctest job is after build, to avoid PCM warning --- .gitlab-ci.yml | 41 +++++++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index c995d191e..f498a281f 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -7,12 +7,12 @@ stages: # Basic checks to verify correctness of code - pre-build - # Build and run tests - - test - # Project compilation validation - build + # Build and run tests + - test + # REST libraries installed as submodules - libraries @@ -78,23 +78,6 @@ Validate Code: - python3 pull-submodules.py --force --dontask --latest:${CI_COMMIT_BRANCH} - python3 pipeline/validateProcesses.py source/libraries/ -Build and Test: - stage: test - script: - - cd ${CI_PROJECT_DIR} - - python3 pull-submodules.py --force --dontask --latest:${CI_COMMIT_BRANCH} - - mkdir ${CI_PROJECT_DIR}/build && cd ${CI_PROJECT_DIR}/build - - cmake ${CI_PROJECT_DIR} - -DTEST=ON -DREST_GARFIELD=OFF -DREST_G4=ON -DRESTLIB_DETECTOR=ON -DRESTLIB_RAW=ON -DRESTLIB_TRACK=ON - - make -j2 - - ctest --verbose -O ${CI_PROJECT_DIR}/build/Testing/summary.txt - - artifacts: - name: "Testing" - paths: - - ${CI_PROJECT_DIR}/build/Testing - expire_in: 1 day - Build and Install: stage: build script: @@ -112,6 +95,24 @@ Build and Install: - ${CI_PROJECT_DIR}/install expire_in: 1 day +Build and Test: + stage: test + script: + - source ${CI_PROJECT_DIR}/install/thisREST.sh + - cd ${CI_PROJECT_DIR} + - python3 pull-submodules.py --force --dontask --latest:${CI_COMMIT_BRANCH} + - mkdir ${CI_PROJECT_DIR}/build && cd ${CI_PROJECT_DIR}/build + - cmake ${CI_PROJECT_DIR} + -DTEST=ON -DREST_GARFIELD=OFF -DREST_G4=ON -DRESTLIB_DETECTOR=ON -DRESTLIB_RAW=ON -DRESTLIB_TRACK=ON + - make -j2 + - ctest --verbose -O ${CI_PROJECT_DIR}/build/Testing/summary.txt + + artifacts: + name: "Testing" + paths: + - ${CI_PROJECT_DIR}/build/Testing + expire_in: 1 day + Load REST Libraries: stage: install script: From 7062418a12988985a613482d3d24b947653baf8a Mon Sep 17 00:00:00 2001 From: nkx Nicholas Date: Mon, 9 May 2022 02:06:47 +0800 Subject: [PATCH 15/28] TRestDataBase: updated IsZombie() logic --- source/framework/tools/inc/TRestDataBase.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/framework/tools/inc/TRestDataBase.h b/source/framework/tools/inc/TRestDataBase.h index 1935f824e..624925fc6 100644 --- a/source/framework/tools/inc/TRestDataBase.h +++ b/source/framework/tools/inc/TRestDataBase.h @@ -42,7 +42,7 @@ struct DBEntry { std::string value = ""; - bool IsZombie() { return runNr == 0 && type == "" && value == ""; } + bool IsZombie() { return runNr == 0 || (type == "" && value == ""); } }; struct DBFile { From 6d394af8817060a6331b84dbc37b4e5d6373867a Mon Sep 17 00:00:00 2001 From: nkx Nicholas Date: Mon, 9 May 2022 04:20:19 +0800 Subject: [PATCH 16/28] update selector pipeline --- pipeline/selection/Validate.C | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pipeline/selection/Validate.C b/pipeline/selection/Validate.C index 56993b5d8..2ed366658 100644 --- a/pipeline/selection/Validate.C +++ b/pipeline/selection/Validate.C @@ -9,14 +9,14 @@ Int_t Validate(TString fname) { tr->Scan("eventID:g4Ana_totalEdep:g4Ana_energyPrimary"); - if (tr->GetEntries() != 3) { + if (tr->GetEntries() != 1) { cout << "Number of entries is not the same!" << endl; - cout << "Expected: 3. Obtained: " << tr->GetEntries() << endl; + cout << "Expected: 1. Obtained: " << tr->GetEntries() << endl; return 1; } // Check IDs. - std::vector ids = {0, 1, 4}; + std::vector ids = {1}; for (Int_t i = 0; i < tr->GetEntries(); i++) { tr->GetEntry(i); if (tr->GetLeaf("eventID")->GetValue(0) != ids[i]) { From 0ef05d313e088add7ddda4ff464232997efeea35 Mon Sep 17 00:00:00 2001 From: DavidDiezIb Date: Tue, 10 May 2022 11:52:00 +0200 Subject: [PATCH 17/28] Pipeline EvSelection IDs from file --- .gitlab-ci.yml | 4 +++ pipeline/selection/ValidateIDsFromFile.C | 30 ++++++++++++++++ .../selection/g4EvSelectionIDsFromFile.rml | 34 +++++++++++++++++++ 3 files changed, 68 insertions(+) create mode 100644 pipeline/selection/ValidateIDsFromFile.C create mode 100644 pipeline/selection/g4EvSelectionIDsFromFile.rml diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index f498a281f..378f61ef5 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -302,8 +302,12 @@ Event Selection: - source ${CI_PROJECT_DIR}/install/thisREST.sh - cd ${CI_PROJECT_DIR}/pipeline/selection - restManager --c g4Analysis.rml --f "${CI_PROJECT_DIR}/install/examples/restG4/01.NLDBD/Run00001_NLDBD_Test.root" + - restManager --c g4OnSelection.rml --f Run00001_NLDBD_Test_g4Analysis.root - restRoot -b -q Validate.C'("Run00001_NLDBD_Test_EvSelection.root")' + + - restManager --c g4EvSelectionIDsFromFile.rml --f Run00001_NLDBD_Test_g4Analysis.root + - restRoot -b -q ValidateIDsFromFile.C'("Run00001_NLDBD_Test_EvSelectionIDsFromFile.root")' artifacts: paths: diff --git a/pipeline/selection/ValidateIDsFromFile.C b/pipeline/selection/ValidateIDsFromFile.C new file mode 100644 index 000000000..c9e832a5c --- /dev/null +++ b/pipeline/selection/ValidateIDsFromFile.C @@ -0,0 +1,30 @@ +#include +#include + +#include +#include +Int_t Validate(TString fname) { + TFile* f = new TFile(fname); + TTree* tr = (TTree*)f->Get("AnalysisTree"); + + if (tr->GetEntries() != 3) { + cout << "Number of entries is not the same!" << endl; + cout << "Expected: 3. Obtained: " << tr->GetEntries() << endl; + return 1; + } + + // Check IDs. + std::vector ids = {0, 3, 6}; + for (Int_t i = 0; i < tr->GetEntries(); i++) { + tr->GetEntry(i); + if (tr->GetLeaf("eventID")->GetValue(0) != ids[i]) { + cout << "Same number of entries but different IDs" << endl; + cout << "Found entry " << i << " with ID: " << tr->GetLeaf("eventID")->GetValue(0) << endl; + cout << "Expected entry " << i << " with ID: " << ids[i] << endl; + return 2; + } + } + + cout << "Tree validation sucess!" << endl; + return 0; +} \ No newline at end of file diff --git a/pipeline/selection/g4EvSelectionIDsFromFile.rml b/pipeline/selection/g4EvSelectionIDsFromFile.rml new file mode 100644 index 000000000..f037a146b --- /dev/null +++ b/pipeline/selection/g4EvSelectionIDsFromFile.rml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From 0c2dddb84d698a2f7bf6785e8d6f04376efdd7ee Mon Sep 17 00:00:00 2001 From: Luis Obis <35803280+lobis@users.noreply.github.com> Date: Thu, 12 May 2022 17:27:05 +0200 Subject: [PATCH 18/28] Update source/framework/core/inc/TRestAnalysisTree.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Juan Antonio García <80903717+juanangp@users.noreply.github.com> --- source/framework/core/inc/TRestAnalysisTree.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/framework/core/inc/TRestAnalysisTree.h b/source/framework/core/inc/TRestAnalysisTree.h index 3b0bf6a9c..21fc52e40 100644 --- a/source/framework/core/inc/TRestAnalysisTree.h +++ b/source/framework/core/inc/TRestAnalysisTree.h @@ -45,7 +45,7 @@ class TRestAnalysisTree : public TTree { Bool_t fQuickSetObservableValue = true; //! std::vector fObservables; //! std::map fObservableIdMap; //! - TChain* fChain = NULL; //! in case multiple files for reading + TChain* fChain = nullptr; //! in case multiple files for reading // for storage Int_t fNObservables; From 772195fd018a520146f91c03fbad9893d5216608 Mon Sep 17 00:00:00 2001 From: Luis Obis <35803280+lobis@users.noreply.github.com> Date: Thu, 12 May 2022 17:27:16 +0200 Subject: [PATCH 19/28] Update source/bin/restRoot.cxx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Juan Antonio García <80903717+juanangp@users.noreply.github.com> --- source/bin/restRoot.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/bin/restRoot.cxx b/source/bin/restRoot.cxx index 058b3b756..c52be30ff 100644 --- a/source/bin/restRoot.cxx +++ b/source/bin/restRoot.cxx @@ -124,7 +124,7 @@ int main(int argc, char* argv[]) { } // command line EventTree object - if (runTmp->GetEventTree() != NULL) { + if (runTmp->GetEventTree() != nullptr) { printf("Attaching ev_tree%i...\n", Nfile); string evcmd = Form("TTree* ev_tree%i = (TTree*)%s;", Nfile, ToString(runTmp->GetEventTree()).c_str()); From aec750c1d515343eca188ff4d2a6125ee774ee4c Mon Sep 17 00:00:00 2001 From: Ni Kaixiang Date: Fri, 13 May 2022 10:55:35 +0800 Subject: [PATCH 20/28] Update .gitlab-ci.yml --- .gitlab-ci.yml | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index a68f1fe65..2759d58d8 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -7,11 +7,11 @@ stages: # Basic checks to verify correctness of code - pre-build - # Project compilation validation - - build - # Build and run tests - test + + # Project compilation validation + - build # REST libraries installed as submodules - libraries @@ -78,23 +78,6 @@ Validate Code: - python3 pull-submodules.py --force --dontask --latest:${CI_COMMIT_BRANCH} - python3 pipeline/validateProcesses.py source/libraries/ -Build and Install: - stage: build - script: - - cd ${CI_PROJECT_DIR} - - python3 pull-submodules.py --force --dontask --latest:${CI_COMMIT_BRANCH} - - rm -rf ${CI_PROJECT_DIR}/build && mkdir ${CI_PROJECT_DIR}/build && cd ${CI_PROJECT_DIR}/build - - rm -rf ${CI_PROJECT_DIR}/install - - cmake ${CI_PROJECT_DIR} -DCMAKE_INSTALL_PREFIX=${CI_PROJECT_DIR}/install - -DREST_GARFIELD=ON -DREST_G4=ON -DRESTLIB_DETECTOR=ON -DRESTLIB_RAW=ON -DRESTLIB_TRACK=ON -DREST_WELCOME=OFF - - make install -j2 - - artifacts: - name: "Install" - paths: - - ${CI_PROJECT_DIR}/install - expire_in: 1 day - Build and Test: stage: test script: @@ -113,6 +96,23 @@ Build and Test: - ${CI_PROJECT_DIR}/build/Testing expire_in: 1 day +Build and Install: + stage: build + script: + - cd ${CI_PROJECT_DIR} + - python3 pull-submodules.py --force --dontask --latest:${CI_COMMIT_BRANCH} + - rm -rf ${CI_PROJECT_DIR}/build && mkdir ${CI_PROJECT_DIR}/build && cd ${CI_PROJECT_DIR}/build + - rm -rf ${CI_PROJECT_DIR}/install + - cmake ${CI_PROJECT_DIR} -DCMAKE_INSTALL_PREFIX=${CI_PROJECT_DIR}/install + -DREST_GARFIELD=ON -DREST_G4=ON -DRESTLIB_DETECTOR=ON -DRESTLIB_RAW=ON -DRESTLIB_TRACK=ON -DREST_WELCOME=OFF + - make install -j2 + + artifacts: + name: "Install" + paths: + - ${CI_PROJECT_DIR}/install + expire_in: 1 day + Load REST Libraries: stage: install script: From 56ea1832111e7615abd2a05a19f165c6d8bb0820 Mon Sep 17 00:00:00 2001 From: Ni Kaixiang Date: Fri, 13 May 2022 10:57:19 +0800 Subject: [PATCH 21/28] ci.yml reverted to previous sequence. test-->build --- .gitlab-ci.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 2759d58d8..012fb185b 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -9,7 +9,7 @@ stages: # Build and run tests - test - + # Project compilation validation - build @@ -81,7 +81,6 @@ Validate Code: Build and Test: stage: test script: - - source ${CI_PROJECT_DIR}/install/thisREST.sh - cd ${CI_PROJECT_DIR} - python3 pull-submodules.py --force --dontask --latest:${CI_COMMIT_BRANCH} - mkdir ${CI_PROJECT_DIR}/build && cd ${CI_PROJECT_DIR}/build From a5cedd1e07d37745e7c218429c21518971e1040a Mon Sep 17 00:00:00 2001 From: nkx Nicholas Date: Fri, 13 May 2022 11:41:32 +0800 Subject: [PATCH 22/28] replace NULL to nullptr; replace new TFile to TFile::Open --- source/bin/restRoot.cxx | 6 +++--- source/framework/core/inc/TRestAnalysisTree.h | 4 ++-- source/framework/core/src/TRestAnalysisTree.cxx | 5 +---- source/framework/core/src/TRestProcessRunner.cxx | 9 ++++----- 4 files changed, 10 insertions(+), 14 deletions(-) diff --git a/source/bin/restRoot.cxx b/source/bin/restRoot.cxx index c52be30ff..0a9ae7843 100644 --- a/source/bin/restRoot.cxx +++ b/source/bin/restRoot.cxx @@ -92,7 +92,7 @@ int main(int argc, char* argv[]) { string runcmd = Form("TRestRun* run%i = (TRestRun*)%s;", Nfile, ToString(runTmp).c_str()); if (debug) printf("%s\n", runcmd.c_str()); gROOT->ProcessLine(runcmd.c_str()); - if (runTmp->GetInputEvent() != NULL) { + if (runTmp->GetInputEvent() != nullptr) { string eventType = runTmp->GetInputEvent()->ClassName(); printf("Attaching event %s as ev%i...\n", eventType.c_str(), Nfile); @@ -104,8 +104,8 @@ int main(int argc, char* argv[]) { } // command line AnalysisTree object - if (runTmp->GetAnalysisTree() != NULL) { - // if (runTmp->GetAnalysisTree()->GetChain() != NULL) { + if (runTmp->GetAnalysisTree() != nullptr) { + // if (runTmp->GetAnalysisTree()->GetChain() != nullptr) { // printf("Attaching ana_tree%i...\n", Nfile); // string evcmd = Form("TChain* ana_tree%i = (TChain*)%s;", Nfile, // ToString(runTmp->GetAnalysisTree()->GetChain()).c_str()); diff --git a/source/framework/core/inc/TRestAnalysisTree.h b/source/framework/core/inc/TRestAnalysisTree.h index 21fc52e40..955ab0e11 100644 --- a/source/framework/core/inc/TRestAnalysisTree.h +++ b/source/framework/core/inc/TRestAnalysisTree.h @@ -136,7 +136,7 @@ class TRestAnalysisTree : public TTree { std::cout << "Error! TRestAnalysisTree::GetObservableValue(): index outside limits!" << endl; return T(); } - if (fChain != NULL) { + if (fChain != nullptr) { return ((TRestAnalysisTree*)fChain->GetTree())->GetObservableValue(n); } return fObservables[n].GetValue(); @@ -167,7 +167,7 @@ class TRestAnalysisTree : public TTree { std::cout << "Error! TRestAnalysisTree::SetObservableValue(): index outside limits!" << endl; return; } - if (fChain != NULL) { + if (fChain != nullptr) { std::cout << "Error! cannot set observable! AnalysisTree is in chain state" << endl; return; } diff --git a/source/framework/core/src/TRestAnalysisTree.cxx b/source/framework/core/src/TRestAnalysisTree.cxx index 5a8c3515b..9f990e99f 100644 --- a/source/framework/core/src/TRestAnalysisTree.cxx +++ b/source/framework/core/src/TRestAnalysisTree.cxx @@ -1010,7 +1010,7 @@ Int_t TRestAnalysisTree::WriteAsTTree(const char* name, Int_t option, Int_t bufs /// The input file that contains another AnalysisTree with same run id /// Bool_t TRestAnalysisTree::AddChainFile(string _file) { - TFile* file = new TFile(_file.c_str(), "read"); + auto file = std::unique_ptr{TFile::Open(_file.c_str(), "update")}; if (!file->IsOpen()) { warning << "TRestAnalysisTree::AddChainFile(): failed to open file " << _file << endl; return false; @@ -1022,7 +1022,6 @@ Bool_t TRestAnalysisTree::AddChainFile(string _file) { if (tree->GetRunOrigin() == this->GetRunOrigin()) { // this is a valid tree delete tree; - delete file; if (fChain == nullptr) { fChain = new TChain("AnalysisTree", "Chained AnalysisTree"); @@ -1040,8 +1039,6 @@ Bool_t TRestAnalysisTree::AddChainFile(string _file) { warning << "TRestAnalysisTree::AddChainFile(): invalid file, file does not contain an AnalysisTree!" << endl; - delete file; - return false; } diff --git a/source/framework/core/src/TRestProcessRunner.cxx b/source/framework/core/src/TRestProcessRunner.cxx index e4258f774..85799f5b3 100644 --- a/source/framework/core/src/TRestProcessRunner.cxx +++ b/source/framework/core/src/TRestProcessRunner.cxx @@ -855,9 +855,9 @@ void TRestProcessRunner::FillThreadEventFunc(TRestThread* t) { // wait 0.1s for the process to finish usleep(100000); - for (int i = 0; i < fThreadNumber; i++) { + for (auto th : fThreads) { for (int j = 0; j < fProcessNumber; j++) { - auto proc = fThreads[i]->GetProcess(j); + auto proc = th->GetProcess(j); proc->NotifyAnalysisTreeReset(); } } @@ -873,17 +873,16 @@ void TRestProcessRunner::FillThreadEventFunc(TRestThread* t) { // write some information to the first(main) data file fRunInfo->SetNFilesSplit(fNFilesSplit); if (fOutputDataFile->GetName() != fOutputDataFileName) { - TFile* Mainfile = new TFile(fOutputDataFileName, "update"); + auto Mainfile = std::unique_ptr{TFile::Open(fOutputDataFileName, "update")}; WriteMetadata(); Mainfile->Close(); - delete Mainfile; } else { WriteMetadata(); } TFile* newfile = new TFile(fOutputDataFileName + "." + ToString(fNFilesSplit), "recreate"); - TBranch* branch = 0; + TBranch* branch = nullptr; fAnalysisTree->SetDirectory(newfile); TIter nextb1(fAnalysisTree->GetListOfBranches()); while ((branch = (TBranch*)nextb1())) { From 2d88d50ba01fda055aadc4af592f87256c6634b3 Mon Sep 17 00:00:00 2001 From: nkx Nicholas Date: Wed, 18 May 2022 12:47:41 +0800 Subject: [PATCH 23/28] TRestAnalysisTree: fix a bug --- source/framework/core/inc/TRestAnalysisTree.h | 7 ++++--- source/framework/core/src/TRestAnalysisTree.cxx | 17 +++++++++++++++++ 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/source/framework/core/inc/TRestAnalysisTree.h b/source/framework/core/inc/TRestAnalysisTree.h index 955ab0e11..a4da87d4f 100644 --- a/source/framework/core/inc/TRestAnalysisTree.h +++ b/source/framework/core/inc/TRestAnalysisTree.h @@ -103,13 +103,13 @@ class TRestAnalysisTree : public TTree { // six basic event prameters Int_t GetEventID() { return fChain ? ((TRestAnalysisTree*)fChain->GetTree())->GetEventID() : fEventID; } Int_t GetSubEventID() { - return fChain ? ((TRestAnalysisTree*)fChain->GetTree())->GetEventID() : fSubEventID; + return fChain ? ((TRestAnalysisTree*)fChain->GetTree())->GetSubEventID() : fSubEventID; } Double_t GetTimeStamp() { - return fChain ? ((TRestAnalysisTree*)fChain->GetTree())->GetEventID() : fTimeStamp; + return fChain ? ((TRestAnalysisTree*)fChain->GetTree())->GetTimeStamp() : fTimeStamp; } TString GetSubEventTag() { - return fChain ? ((TRestAnalysisTree*)fChain->GetTree())->GetEventID() : *fSubEventTag; + return fChain ? ((TRestAnalysisTree*)fChain->GetTree())->GetSubEventTag() : *fSubEventTag; } // we suppose all the chained trees have same run and sub run id. // so there is no need to call fChain->GetTree() @@ -241,6 +241,7 @@ class TRestAnalysisTree : public TTree { void SetRunOrigin(Int_t run_origin) { fRunOrigin = run_origin; } void SetSubRunOrigin(Int_t sub_run_origin) { fSubRunOrigin = sub_run_origin; } + void SetEventInfo(TRestAnalysisTree* tree); void SetEventInfo(TRestEvent* evt); Int_t Fill(); diff --git a/source/framework/core/src/TRestAnalysisTree.cxx b/source/framework/core/src/TRestAnalysisTree.cxx index 9f990e99f..9d1ab5f22 100644 --- a/source/framework/core/src/TRestAnalysisTree.cxx +++ b/source/framework/core/src/TRestAnalysisTree.cxx @@ -667,6 +667,23 @@ Int_t TRestAnalysisTree::GetEntry(Long64_t entry, Int_t getall) { } } + +void TRestAnalysisTree::SetEventInfo(TRestAnalysisTree* tree) { + if (fChain != NULL) { + cout << "Error! cannot fill tree. AnalysisTree is in chain state" << endl; + return; + } + + if (tree != NULL) { + fEventID = tree->GetEventID(); + fSubEventID = tree->GetSubEventID(); + fTimeStamp = tree->GetTimeStamp(); + *fSubEventTag = tree->GetSubEventTag(); + fRunOrigin = tree->GetRunOrigin(); + fSubRunOrigin = tree->GetSubRunOrigin(); + } +} + void TRestAnalysisTree::SetEventInfo(TRestEvent* evt) { if (fChain != nullptr) { cout << "Error! cannot fill tree. AnalysisTree is in chain state" << endl; From 67992073220ad1807a4dc88c9f61bb8cc4f4943a Mon Sep 17 00:00:00 2001 From: nkx Nicholas Date: Wed, 18 May 2022 12:51:51 +0800 Subject: [PATCH 24/28] TRestAnalysisTree event info wil be set correctly when reading from input root file --- source/framework/core/src/TRestProcessRunner.cxx | 1 + source/framework/core/src/TRestRun.cxx | 1 + 2 files changed, 2 insertions(+) diff --git a/source/framework/core/src/TRestProcessRunner.cxx b/source/framework/core/src/TRestProcessRunner.cxx index 85799f5b3..1a0eb33f7 100644 --- a/source/framework/core/src/TRestProcessRunner.cxx +++ b/source/framework/core/src/TRestProcessRunner.cxx @@ -949,6 +949,7 @@ void TRestProcessRunner::ConfigOutputFile() { } void TRestProcessRunner::WriteMetadata() { + fRunInfo->SetNFilesSplit(fNFilesSplit); fRunInfo->Write(); this->Write(); char tmpString[256]; diff --git a/source/framework/core/src/TRestRun.cxx b/source/framework/core/src/TRestRun.cxx index 8cb81a12f..3bcb21b40 100644 --- a/source/framework/core/src/TRestRun.cxx +++ b/source/framework/core/src/TRestRun.cxx @@ -837,6 +837,7 @@ Int_t TRestRun::GetNextEvent(TRestEvent* targetevt, TRestAnalysisTree* targettre // normal reading procedure eve->Initialize(); fBytesRead += fAnalysisTree->GetEntry(fCurrentEvent); + targettree->SetEventInfo(fAnalysisTree); for (int n = 0; n < fAnalysisTree->GetNumberOfObservables(); n++) targettree->SetObservable(n, fAnalysisTree->GetObservable(n)); } From d877fb399ae075d56ec0880b2fccf4976e889e00 Mon Sep 17 00:00:00 2001 From: nkx Nicholas Date: Wed, 18 May 2022 14:22:33 +0800 Subject: [PATCH 25/28] fix compilation error --- source/framework/core/inc/TRestThread.h | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/source/framework/core/inc/TRestThread.h b/source/framework/core/inc/TRestThread.h index a22238dad..df5092354 100644 --- a/source/framework/core/inc/TRestThread.h +++ b/source/framework/core/inc/TRestThread.h @@ -40,8 +40,7 @@ class TRestThread { Int_t fVerboseLevel; //! public: - void Initialize() override; - void InitFromConfigFile() override {} + void Initialize(); void AddProcess(TRestEventProcess* process); void PrepareToProcess(bool* outputConfig = nullptr); @@ -76,7 +75,7 @@ class TRestThread { TRestThread() { Initialize(); } ~TRestThread(){}; - ClassDefOverride(TRestThread, 1); + ClassDef(TRestThread, 1); }; #endif From aba1def30ec7af3798162d93c5865da0e4ccb756 Mon Sep 17 00:00:00 2001 From: nkx Nicholas Date: Fri, 20 May 2022 02:20:06 +0800 Subject: [PATCH 26/28] fixed event tree reading from splitted file --- source/framework/core/src/TRestRun.cxx | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/source/framework/core/src/TRestRun.cxx b/source/framework/core/src/TRestRun.cxx index 394eb8edf..90a4b14f2 100644 --- a/source/framework/core/src/TRestRun.cxx +++ b/source/framework/core/src/TRestRun.cxx @@ -842,9 +842,16 @@ Int_t TRestRun::GetNextEvent(TRestEvent* targetevt, TRestAnalysisTree* targettre targettree->SetObservable(n, fAnalysisTree->GetObservable(n)); } if (fEventTree != nullptr) { - fBytesRead += ((TBranch*)fEventTree->GetListOfBranches()->UncheckedAt(fEventBranchLoc)) - ->GetEntry(fCurrentEvent); - // fBytesReaded += fEventTree->GetEntry(fCurrentEvent); + if (fEventTree->IsA() == TChain::Class()) { + Long64_t entry = fEventTree->LoadTree(fCurrentEvent); + fBytesReaded += ((TBranch*)fEventTree->GetTree()->GetListOfBranches()->UncheckedAt( + fEventBranchLoc)) + ->GetEntry(entry); + } else { + fBytesReaded += + ((TBranch*)fEventTree->GetListOfBranches()->UncheckedAt(fEventBranchLoc)) + ->GetEntry(fCurrentEvent); + } } fCurrentEvent++; } From ddab667d402f8e538f1ac3a526667a548100dcc4 Mon Sep 17 00:00:00 2001 From: Ni Kaixiang Date: Fri, 20 May 2022 10:46:34 +0800 Subject: [PATCH 27/28] Update TRestRun.cxx --- source/framework/core/src/TRestRun.cxx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/framework/core/src/TRestRun.cxx b/source/framework/core/src/TRestRun.cxx index 90a4b14f2..25b907b19 100644 --- a/source/framework/core/src/TRestRun.cxx +++ b/source/framework/core/src/TRestRun.cxx @@ -844,11 +844,11 @@ Int_t TRestRun::GetNextEvent(TRestEvent* targetevt, TRestAnalysisTree* targettre if (fEventTree != nullptr) { if (fEventTree->IsA() == TChain::Class()) { Long64_t entry = fEventTree->LoadTree(fCurrentEvent); - fBytesReaded += ((TBranch*)fEventTree->GetTree()->GetListOfBranches()->UncheckedAt( + fBytesRead += ((TBranch*)fEventTree->GetTree()->GetListOfBranches()->UncheckedAt( fEventBranchLoc)) ->GetEntry(entry); } else { - fBytesReaded += + fBytesRead += ((TBranch*)fEventTree->GetListOfBranches()->UncheckedAt(fEventBranchLoc)) ->GetEntry(fCurrentEvent); } From 6795538645f86509da6572f43ee7942e1c05ff72 Mon Sep 17 00:00:00 2001 From: Ni Kaixiang Date: Fri, 20 May 2022 15:06:39 +0800 Subject: [PATCH 28/28] Update TRestRun.h --- source/framework/core/inc/TRestRun.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/framework/core/inc/TRestRun.h b/source/framework/core/inc/TRestRun.h index 37a66cb43..3336fb95b 100644 --- a/source/framework/core/inc/TRestRun.h +++ b/source/framework/core/inc/TRestRun.h @@ -128,7 +128,7 @@ class TRestRun : public TRestMetadata { void SkipEventTree() {} void cd() { - if (fInputFile != NULL) fInputFile->cd(); + if (fInputFile != nullptr) fInputFile->cd(); } // Getters