diff options
Diffstat (limited to 'Source/Utils/Parser')
-rw-r--r-- | Source/Utils/Parser/CMakeLists.txt | 5 | ||||
-rw-r--r-- | Source/Utils/Parser/IntervalsParser.H | 220 | ||||
-rw-r--r-- | Source/Utils/Parser/IntervalsParser.cpp | 251 | ||||
-rw-r--r-- | Source/Utils/Parser/Make.package | 4 | ||||
-rw-r--r-- | Source/Utils/Parser/ParserUtils.H | 298 | ||||
-rw-r--r-- | Source/Utils/Parser/ParserUtils.cpp | 165 |
6 files changed, 943 insertions, 0 deletions
diff --git a/Source/Utils/Parser/CMakeLists.txt b/Source/Utils/Parser/CMakeLists.txt new file mode 100644 index 000000000..620ee8dcd --- /dev/null +++ b/Source/Utils/Parser/CMakeLists.txt @@ -0,0 +1,5 @@ +target_sources(WarpX + PRIVATE + IntervalsParser.cpp + ParserUtils.cpp +) diff --git a/Source/Utils/Parser/IntervalsParser.H b/Source/Utils/Parser/IntervalsParser.H new file mode 100644 index 000000000..e9fffce9e --- /dev/null +++ b/Source/Utils/Parser/IntervalsParser.H @@ -0,0 +1,220 @@ +/* Copyright 2022 Andrew Myers, Burlen Loring, Luca Fedeli + * Maxence Thevenet, Remi Lehe, Revathi Jambunathan + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ + +#ifndef WARPX_UTILS_PARSER_INTERVALSPARSER_H_ +#define WARPX_UTILS_PARSER_INTERVALSPARSER_H_ + +#include <limits> +#include <string> +#include <vector> + +namespace utils::parser +{ + + /** + * \brief This class is a parser for slices of the form i:j:k where i, j and k are integers + * representing respectively the starting point, the stopping point and the period. + */ + class SliceParser + { + public: + /** + * \brief Constructor of the SliceParser class. + * + * @param[in] instr an input string of the form "i:j:k", "i:j" or "k" where i, j and k are + * integers representing respectively the starting point, the stopping point and the period. + * Any of these integers may be omitted in which case it will be equal to their default value + * (0 for the starting point, std::numeric_limits<int>::max() for the stopping point and 1 for + * the period). For example SliceParser(":1000:") is equivalent to SliceParser("0:1000:1"). + */ + SliceParser (const std::string& instr, bool isBTD=false); + + /** + * \brief A method that returns true if the input integer is contained in the slice. (e.g. if + * the list is initialized with "300:500:100", this method returns true if and only if n is + * 300, 400 or 500). If the period is negative or 0, the function always returns false. + * + * @param[in] n the input integer + */ + bool contains (const int n) const; + + /** + * \brief A method that returns the smallest integer strictly greater than n such that + * contains(n) is true. Returns std::numeric_limits<int>::max() if there is no such integer. + * + * @param[in] n the input integer + */ + int nextContains (const int n) const; + + /** + * \brief A method that returns the greatest integer strictly smaller than n such that + * contains(n) is true. Returns 0 if there is no such integer. + * + * @param[in] n the input integer + */ + int previousContains (const int n) const; + + /** + * \brief A method that returns the slice period. + * + */ + int getPeriod () const; + + /** + * \brief A method that returns the slice start. + * + */ + int getStart () const; + + /** + * \brief A method that returns the slice stop. + * + */ + int getStop () const; + + /** + * @brief A method that returns the number of integers contained by the slice. + * + */ + int numContained() const; + + private: + bool m_isBTD = false; + int m_start = 0; + int m_stop = std::numeric_limits<int>::max(); + int m_period = 1; + std::string m_separator = ":"; + + }; + + + /** + * \brief This class is a parser for multiple slices of the form x,y,z,... where x, y and z are + * slices of the form i:j:k, as defined in the SliceParser class. This class contains a vector of + * SliceParsers. + */ + class IntervalsParser + { + public: + /** + * \brief Default constructor of the IntervalsParser class. + */ + IntervalsParser () = default; + + /** + * \brief Constructor of the IntervalsParser class. + * + * @param[in] instr_vec an input vector string, which when concatenated is of the form + * "x,y,z,...". This will call the constructor of SliceParser using x, y and z as input + * arguments. + */ + IntervalsParser (const std::vector<std::string>& instr_vec); + + /** + * \brief A method that returns true if the input integer is contained in any of the slices + * contained by the IntervalsParser. + * + * @param[in] n the input integer + */ + bool contains (const int n) const; + + /** + * \brief A method that returns the smallest integer strictly greater than n such that + * contains(n) is true. Returns std::numeric_limits<int>::max() if there is no such integer. + * + * @param[in] n the input integer + */ + int nextContains (const int n) const; + + /** + * \brief A method that returns the greatest integer strictly smaller than n such that + * contains(n) is true. Returns 0 if there is no such integer. + * + * @param[in] n the input integer + */ + int previousContains (const int n) const; + + /** + * \brief A method that returns the greatest integer smaller than or equal to n such that + * contains(n) is true. Returns 0 if there is no such integer. + * + * @param[in] n the input integer + */ + int previousContainsInclusive (const int n) const; + + /** + * \brief A method the local period (in timesteps) of the IntervalsParser at timestep n. + * The period is defined by nextContains(n) - previousContainsInclusive(n) + * + * @param[in] n the input integer + */ + int localPeriod (const int n) const; + + /** + * \brief A method that returns true if any of the slices contained by the IntervalsParser + * has a strictly positive period. + */ + bool isActivated () const; + + private: + std::vector<SliceParser> m_slices; + std::string m_separator = ","; + bool m_activated = false; + }; + + /** + * \brief This class is a parser for multiple slices of the form x,y,z,... where x, y and z are + * slices of the form i:j:k, as defined in the SliceParser class. This class contains a vector of + * SliceParsers. The supported function set differs from the IntervalsParser + */ + class BTDIntervalsParser + { + public: + /** + * \brief Default constructor of the BTDIntervalsParser class. + */ + BTDIntervalsParser () = default; + + /** + * \brief Constructor of the BTDIntervalsParser class. + * + * @param[in] instr_vec an input vector string, which when concatenated is of the form + * "x,y,z,...". This will call the constructor of SliceParser using x, y and z as input + * arguments. + */ + BTDIntervalsParser (const std::vector<std::string>& instr_vec); + + /** + * @brief Return the total number of unique labframe snapshots + */ + int NumSnapshots (); + + /** + * @brief Return the iteration number stored at index i_buffer + * + * @param i_buffer buffer or iteration index, between 0 and NumSnapshots + */ + int GetBTDIteration(int i_buffer); + + /** + * \brief A method that returns true if any of the slices contained by the IntervalsParser + * has a strictly positive period. + */ + bool isActivated () const; + + private: + std::vector<int> m_btd_iterations; + std::vector<SliceParser> m_slices; + std::vector<int> m_slice_starting_i_buffer; + int m_n_snapshots; + static constexpr char m_separator = ','; + bool m_activated = false; + }; +} + +#endif // WARPX_UTILS_PARSER_INTERVALSPARSER_H_ diff --git a/Source/Utils/Parser/IntervalsParser.cpp b/Source/Utils/Parser/IntervalsParser.cpp new file mode 100644 index 000000000..d535edeb8 --- /dev/null +++ b/Source/Utils/Parser/IntervalsParser.cpp @@ -0,0 +1,251 @@ +/* Copyright 2022 Andrew Myers, Burlen Loring, Luca Fedeli + * Maxence Thevenet, Remi Lehe, Revathi Jambunathan + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ + +#include "IntervalsParser.H" + +#include "ParserUtils.H" +#include "Utils/Strings/StringUtils.H" +#include "Utils/TextMsg.H" + +#include <AMReX_Utility.H> + +#include <algorithm> + +utils::parser::SliceParser::SliceParser (const std::string& instr, const bool isBTD) +{ + namespace utils_str = utils::strings; + + m_isBTD = isBTD; + // split string and trim whitespaces + auto insplit = utils_str::split<std::vector<std::string>>(instr, m_separator, true); + + if(insplit.size() == 1){ // no colon in input string. The input is the period. + WARPX_ALWAYS_ASSERT_WITH_MESSAGE(!m_isBTD, "must specify interval stop for BTD"); + m_period = parseStringtoInt(insplit[0], "interval period");} + else if(insplit.size() == 2) // 1 colon in input string. The input is start:stop + { + WARPX_ALWAYS_ASSERT_WITH_MESSAGE(!m_isBTD || !insplit[1].empty(), "must specify interval stop for BTD"); + if (!insplit[0].empty()){ + m_start = parseStringtoInt(insplit[0], "interval start");} + if (!insplit[1].empty()){ + m_stop = parseStringtoInt(insplit[1], "interval stop");} + } + else // 2 colons in input string. The input is start:stop:period + { + WARPX_ALWAYS_ASSERT_WITH_MESSAGE(!m_isBTD || !insplit[1].empty(), "must specify interval stop for BTD"); + WARPX_ALWAYS_ASSERT_WITH_MESSAGE( + insplit.size() == 3, + instr + "' is not a valid syntax for a slice."); + if (!insplit[0].empty()){ + m_start = parseStringtoInt(insplit[0], "interval start");} + if (!insplit[1].empty()){ + m_stop = parseStringtoInt(insplit[1], "interval stop");} + if (!insplit[2].empty()){ + m_period = parseStringtoInt(insplit[2], "interval period");} + } +} + + +bool utils::parser::SliceParser::contains (const int n) const +{ + if (m_period <= 0) {return false;} + return (n - m_start) % m_period == 0 && n >= m_start && n <= m_stop; +} + + +int utils::parser::SliceParser::nextContains (const int n) const +{ + if (m_period <= 0) {return std::numeric_limits<int>::max();} + int next = m_start; + if (n >= m_start) {next = ((n-m_start)/m_period + 1)*m_period+m_start;} + if (next > m_stop) {next = std::numeric_limits<int>::max();} + return next; +} + + +int utils::parser::SliceParser::previousContains (const int n) const +{ + if (m_period <= 0) {return false;} + int previous = ((std::min(n-1,m_stop)-m_start)/m_period)*m_period+m_start; + if ((n < m_start) || (previous < 0)) {previous = 0;} + return previous; +} + + +int utils::parser::SliceParser::getPeriod () const {return m_period;} + + +int utils::parser::SliceParser::getStart () const {return m_start;} + + +int utils::parser::SliceParser::getStop () const {return m_stop;} + + +int utils::parser::SliceParser::numContained () const { + return (m_stop - m_start) / m_period + 1;} + +utils::parser::IntervalsParser::IntervalsParser ( + const std::vector<std::string>& instr_vec) +{ + namespace utils_str = utils::strings; + + std::string inconcatenated; + for (const auto& instr_element : instr_vec) inconcatenated +=instr_element; + + auto insplit = utils_str::split<std::vector<std::string>>(inconcatenated, m_separator); + + for(const auto& inslc : insplit) + { + SliceParser temp_slice(inslc); + m_slices.push_back(temp_slice); + if ((temp_slice.getPeriod() > 0) && + (temp_slice.getStop() >= temp_slice.getStart())) m_activated = true; + } +} + + +bool utils::parser::IntervalsParser::contains (const int n) const +{ + return std::any_of(m_slices.begin(), m_slices.end(), + [&](const auto& slice){return slice.contains(n);}); +} + + +int utils::parser::IntervalsParser::nextContains (const int n) const +{ + int next = std::numeric_limits<int>::max(); + for(const auto& slice: m_slices){ + next = std::min(slice.nextContains(n),next); + } + return next; +} + + +int utils::parser::IntervalsParser::previousContains (const int n) const +{ + int previous = 0; + for(const auto& slice: m_slices){ + previous = std::max(slice.previousContains(n),previous); + } + return previous; +} + + +int utils::parser::IntervalsParser::previousContainsInclusive ( + const int n) const +{ + if (contains(n)){return n;} + else {return previousContains(n);} +} + + +int utils::parser::IntervalsParser::localPeriod (const int n) const +{ + return nextContains(n) - previousContainsInclusive(n); +} + + +bool utils::parser::IntervalsParser::isActivated () const {return m_activated;} + + +utils::parser::BTDIntervalsParser::BTDIntervalsParser ( + const std::vector<std::string>& instr_vec) +{ + std::string inconcatenated; + for (const auto& instr_element : instr_vec) inconcatenated +=instr_element; + + auto const insplit = utils::strings::split<std::vector<std::string>>(inconcatenated, std::string(1,m_separator)); + + // parse the Intervals string into Slices and store each slice in m_slices, + // in order of increasing Slice start value + for(const auto& inslc : insplit) + { + bool isBTD = true; + SliceParser temp_slice(inslc, isBTD); + if (m_slices.size() > 0) + { + // find the last index i_slice where + // the start value of m_slices[i_slice] is greater than temp_slices' start_value + int i_slice = 0; + while (temp_slice.getStart() > m_slices[i_slice].getStart() && i_slice < static_cast<int>(m_slices.size())) + { + i_slice++; + } + m_slices.insert(m_slices.begin() + i_slice, temp_slice); + } + else + { + m_slices.push_back(temp_slice); + } + } + // from the vector of slices, m_slices, + // create a vector of integers, m_btd_iterations, containing + // the iteration of every back-transformed snapshot that will be saved + // the iteration values in m_btd_iterations are + // 1. saved in increasing order + // 2. unique, i.e. no duplicate iterations are saved + for (const auto& temp_slice : m_slices) + { + const int start = temp_slice.getStart(); + const int period = temp_slice.getPeriod(); + int btd_iter_ind; + // for Slice temp_slice in m_slices, + // determine the index in m_btd_iterations where temp_slice's starting value goes + // + // Implementation note: + // assuming the user mostly lists slices in ascending order, + // start at the end of m_btd_iterations and search backward + if (m_btd_iterations.size() == 0) + { + btd_iter_ind = 0; + } + else + { + btd_iter_ind = m_btd_iterations.size() - 1; + while (start < m_btd_iterations[btd_iter_ind] and btd_iter_ind>0) + { + btd_iter_ind--; + } + } + // insert each iteration contained in temp_slice into m_btd_iterations + // adding them in increasing sorted order and not adding any iterations + // already contained in m_btd_iterations + for (int ii = start; ii <= temp_slice.getStop(); ii += period) + { + if (m_btd_iterations.size() > 0) + { + // find where iteration ii should go in m_btd_iterations + while (ii > m_btd_iterations[btd_iter_ind] && btd_iter_ind < static_cast<int>(m_btd_iterations.size())) + { + btd_iter_ind++; + } + if (ii != m_btd_iterations[btd_iter_ind]) + { + m_btd_iterations.insert(m_btd_iterations.begin() + btd_iter_ind, ii); + } + } else + { + m_btd_iterations.push_back(ii); + } + } + if ((temp_slice.getPeriod() > 0) && + (temp_slice.getStop() >= start)) m_activated = true; + } +} + + +int utils::parser::BTDIntervalsParser::NumSnapshots () +{ + return m_btd_iterations.size(); +} + + +int utils::parser::BTDIntervalsParser::GetBTDIteration (int i_buffer) +{ + return m_btd_iterations[i_buffer]; +} diff --git a/Source/Utils/Parser/Make.package b/Source/Utils/Parser/Make.package new file mode 100644 index 000000000..f5447bc50 --- /dev/null +++ b/Source/Utils/Parser/Make.package @@ -0,0 +1,4 @@ +CEXE_sources += IntervalsParser.cpp +CEXE_sources += ParserUtils.cpp + +VPATH_LOCATIONS += $(WARPX_HOME)/Source/Utils/Parser diff --git a/Source/Utils/Parser/ParserUtils.H b/Source/Utils/Parser/ParserUtils.H new file mode 100644 index 000000000..4195bed1a --- /dev/null +++ b/Source/Utils/Parser/ParserUtils.H @@ -0,0 +1,298 @@ +/* Copyright 2022 Andrew Myers, Burlen Loring, Luca Fedeli + * Maxence Thevenet, Remi Lehe, Revathi Jambunathan + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ + +#ifndef WARPX_UTILS_PARSER_PARSERUTILS_H_ +#define WARPX_UTILS_PARSER_PARSERUTILS_H_ + +#include <AMReX_ParmParse.H> +#include <AMReX_Parser.H> +#include <AMReX_REAL.H> +#include <AMReX_Vector.H> + +#include <cmath> +#include <string> +#include <type_traits> + +namespace utils::parser +{ + /** + * \brief Do a safe cast of a real to an int + * This ensures that the float value is within the range of ints and if not, + * raises an exception. + * + * \param x Real value to cast + * \param real_name String, the name of the variable being casted to use in the error message + */ + int + safeCastToInt(amrex::Real x, const std::string& real_name); + + + /** + * \brief Do a safe cast of a real to a long + * This ensures that the float value is within the range of longs and if not, + * raises an exception. + * + * \param x Real value to cast + * \param real_name String, the name of the variable being casted to use in the error message + */ + long + safeCastToLong(amrex::Real x, const std::string& real_name); + + + /** + * \brief Initialize an amrex::Parser object from a string containing a math expression + * + * \param parse_function String to read to initialize the parser. + * \param varnames A list of predefined independent variables + */ + amrex::Parser makeParser ( + std::string const& parse_function, + amrex::Vector<std::string> const& varnames); + + + /** + * \brief Parse a string (typically a mathematical expression) from the + * input file and store it into a variable. + * + * \param pp used to read the query_string `pp.<function>=string` + * \param query_string ParmParse.query will look for this string + * \param stored_string variable in which the string to parse is stored + */ + void Store_parserString( + const amrex::ParmParse &pp, + std::string query_string, + std::string& stored_string); + + + /** Parse a string and return as a double precision floating point number + * + * In case the string cannot be interpreted as a double, + * this function ... <throws an exception? aborts with error message?> + * + * \param str The string to be parsed + * \return representation as a double + */ + double parseStringtoDouble(const std::string& str); + + + /** Parse a string and return an int + * + * In case the string cannot be interpreted as Real, + * this function ... <throws an exception? aborts with error message?> + * + * \param str The string to be parsed + * \param name For integers, the name, to be used in error messages + * \return rounded closest integer + */ + int parseStringtoInt(const std::string& str, const std::string& name); + + + template <int N> + amrex::ParserExecutor<N> compileParser (amrex::Parser const* parser) + { + if (parser) { + return parser->compile<N>(); + } else { + return amrex::ParserExecutor<N>{}; + } + } + + + /** Similar to amrex::ParmParse::query, but also supports math expressions for the value. + * + * amrex::ParmParse::query reads a name and a value from the input file. This function does the + * same, and applies the amrex::Parser to the value, so the user has the choice to specify a value or + * a math expression (including user-defined constants). + * Works for amrex::Real numbers and integers. + * + * \param[in] a_pp amrex::ParmParse object + * \param[in] str name of the parameter to read + * \param[out] val where the value queried and parsed is stored, either a scalar or vector + */ + template <typename T> + int queryWithParser (const amrex::ParmParse& a_pp, char const * const str, T& val) + { + // call amrex::ParmParse::query, check if the user specified str. + std::string tmp_str; + int is_specified = a_pp.query(str, tmp_str); + if (is_specified) + { + // If so, create a parser object and apply it to the value provided by the user. + std::string str_val; + Store_parserString(a_pp, str, str_val); + + auto parser = makeParser(str_val, {}); + + if (std::is_same<T, int>::value) { + + val = safeCastToInt(std::round(parser.compileHost<0>()()), str); + } + else { + val = static_cast<T>(parser.compileHost<0>()()); + } + } + // return the same output as amrex::ParmParse::query + return is_specified; + } + + + template <typename T> + int queryArrWithParser (const amrex::ParmParse& a_pp, char const * const str, std::vector<T>& val) + { + // call amrex::ParmParse::query, check if the user specified str. + std::vector<std::string> tmp_str_arr; + int is_specified = a_pp.queryarr(str, tmp_str_arr); + if (is_specified) + { + // If so, create parser objects and apply them to the values provided by the user. + int const n = static_cast<int>(tmp_str_arr.size()); + val.resize(n); + for (int i=0 ; i < n ; i++) { + auto parser = makeParser(tmp_str_arr[i], {}); + if (std::is_same<T, int>::value) { + val[i] = safeCastToInt(std::round(parser.compileHost<0>()()), str); + } + else { + val[i] = static_cast<T>(parser.compileHost<0>()()); + } + } + } + // return the same output as amrex::ParmParse::query + return is_specified; + } + + + /** Similar to amrex::ParmParse::query, but also supports math expressions for the value. + * + * amrex::ParmParse::query reads a name and a value from the input file. This function does the + * same, and applies the amrex::Parser to the value, so the user has the choice to specify a value or + * a math expression (including user-defined constants). + * Works for amrex::Real numbers and integers. + * + * \param[in] a_pp amrex::ParmParse object + * \param[in] str name of the parameter to read + * \param[out] val where the value queried and parsed is stored, either a scalar or vector + * \param[in] start_ix start index in the list of inputs values (optional with arrays, default is + * amrex::ParmParse::FIRST for starting with the first input value) + * \param[in] num_val number of input values to use (optional with arrays, default is + * amrex::ParmParse::LAST for reading until the last input value) + */ + template <typename T> + int queryArrWithParser (const amrex::ParmParse& a_pp, char const * const str, std::vector<T>& val, + const int start_ix, const int num_val) + { + // call amrex::ParmParse::query, check if the user specified str. + std::vector<std::string> tmp_str_arr; + int is_specified = a_pp.queryarr(str, tmp_str_arr, start_ix, num_val); + if (is_specified) + { + // If so, create parser objects and apply them to the values provided by the user. + int const n = static_cast<int>(tmp_str_arr.size()); + val.resize(n); + for (int i=0 ; i < n ; i++) { + auto parser = makeParser(tmp_str_arr[i], {}); + if (std::is_same<T, int>::value) { + val[i] = safeCastToInt(std::round(parser.compileHost<0>()()), str); + } + else { + val[i] = static_cast<T>(parser.compileHost<0>()()); + } + } + } + // return the same output as amrex::ParmParse::query + return is_specified; + } + + + /** Similar to amrex::ParmParse::get, but also supports math expressions for the value. + * + * amrex::ParmParse::get reads a name and a value from the input file. This function does the + * same, and applies the Parser to the value, so the user has the choice to specify a value or + * a math expression (including user-defined constants). + * Works for amrex::Real numbers and integers. + * + * \param[in] a_pp amrex::ParmParse object + * \param[in] str name of the parameter to read + * \param[out] val where the value queried and parsed is stored + */ + template <typename T> + void getWithParser (const amrex::ParmParse& a_pp, char const * const str, T& val) + { + // If so, create a parser object and apply it to the value provided by the user. + std::string str_val; + Store_parserString(a_pp, str, str_val); + + auto parser = makeParser(str_val, {}); + if (std::is_same<T, int>::value) { + val = safeCastToInt(std::round(parser.compileHost<0>()()), str); + } + else { + val = static_cast<T>(parser.compileHost<0>()()); + } + } + + template <typename T> + void getArrWithParser (const amrex::ParmParse& a_pp, char const * const str, std::vector<T>& val) + { + // Create parser objects and apply them to the values provided by the user. + std::vector<std::string> tmp_str_arr; + a_pp.getarr(str, tmp_str_arr); + + int const n = static_cast<int>(tmp_str_arr.size()); + val.resize(n); + for (int i=0 ; i < n ; i++) { + auto parser = makeParser(tmp_str_arr[i], {}); + if (std::is_same<T, int>::value) { + val[i] = safeCastToInt(std::round(parser.compileHost<0>()()), str); + } + else { + val[i] = static_cast<T>(parser.compileHost<0>()()); + } + } + } + + + /** Similar to amrex::ParmParse::get, but also supports math expressions for the value. + * + * amrex::ParmParse::get reads a name and a value from the input file. This function does the + * same, and applies the Parser to the value, so the user has the choice to specify a value or + * a math expression (including user-defined constants). + * Works for amrex::Real numbers and integers. + * + * \param[in] a_pp amrex::ParmParse object + * \param[in] str name of the parameter to read + * \param[out] val where the value queried and parsed is stored + * \param[in] start_ix start index in the list of inputs values (optional with arrays, default is + * amrex::ParmParse::FIRST for starting with the first input value) + * \param[in] num_val number of input values to use (optional with arrays, default is + * amrex::ParmParse::LAST for reading until the last input value) + */ + template <typename T> + void getArrWithParser (const amrex::ParmParse& a_pp, char const * const str, std::vector<T>& val, + const int start_ix, const int num_val) + { + // Create parser objects and apply them to the values provided by the user. + std::vector<std::string> tmp_str_arr; + a_pp.getarr(str, tmp_str_arr, start_ix, num_val); + + int const n = static_cast<int>(tmp_str_arr.size()); + val.resize(n); + for (int i=0 ; i < n ; i++) { + auto parser = makeParser(tmp_str_arr[i], {}); + if (std::is_same<T, int>::value) { + val[i] = safeCastToInt(std::round(parser.compileHost<0>()()), str); + } + else { + val[i] = static_cast<T>(parser.compileHost<0>()()); + } + } + } + +} + +#endif // WARPX_UTILS_PARSER_PARSERUTILS_H_ diff --git a/Source/Utils/Parser/ParserUtils.cpp b/Source/Utils/Parser/ParserUtils.cpp new file mode 100644 index 000000000..48cc9b248 --- /dev/null +++ b/Source/Utils/Parser/ParserUtils.cpp @@ -0,0 +1,165 @@ +/* Copyright 2022 Andrew Myers, Burlen Loring, Luca Fedeli + * Maxence Thevenet, Remi Lehe, Revathi Jambunathan + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ + +#include "ParserUtils.H" + +#include "Utils/TextMsg.H" +#include "Utils/WarpXConst.H" + +#include <AMReX_Parser.H> +#include <AMReX_ParmParse.H> + +#include <limits> +#include <map> +#include <set> + +void utils::parser::Store_parserString( + const amrex::ParmParse& pp, + std::string query_string, + std::string& stored_string) +{ + std::vector<std::string> f; + pp.getarr(query_string.c_str(), f); + stored_string.clear(); + for (auto const& s : f) { + stored_string += s; + } + f.clear(); +} + + +namespace { + template< typename int_type > + AMREX_FORCE_INLINE + int_type safeCastTo(const amrex::Real x, const std::string& real_name) { + int_type result = int_type(0); + bool error_detected = false; + std::string assert_msg; + // (2.0*(numeric_limits<int>::max()/2+1)) converts numeric_limits<int>::max()+1 to a real ensuring accuracy to all digits + // This accepts x = 2**31-1 but rejects 2**31. + using namespace amrex::literals; + constexpr amrex::Real max_range = (2.0_rt*static_cast<amrex::Real>(std::numeric_limits<int_type>::max()/2+1)); + if (x < max_range) { + if (std::ceil(x) >= std::numeric_limits<int_type>::min()) { + result = static_cast<int_type>(x); + } else { + error_detected = true; + assert_msg = "Negative overflow detected when casting " + real_name + " = " + + std::to_string(x) + " to integer type"; + } + } else if (x > 0) { + error_detected = true; + assert_msg = "Overflow detected when casting " + real_name + " = " + std::to_string(x) + " to integer type"; + } else { + error_detected = true; + assert_msg = "NaN detected when casting " + real_name + " to integer type"; + } + WARPX_ALWAYS_ASSERT_WITH_MESSAGE(!error_detected, assert_msg); + return result; + } +} + + +int utils::parser::safeCastToInt(const amrex::Real x, const std::string& real_name) { + return ::safeCastTo<int> (x, real_name); +} + + +long utils::parser::safeCastToLong(const amrex::Real x, const std::string& real_name) { + return ::safeCastTo<long> (x, real_name); +} + + +amrex::Parser utils::parser::makeParser ( + std::string const& parse_function, amrex::Vector<std::string> const& varnames) +{ + // Since queryWithParser recursively calls this routine, keep track of symbols + // in case an infinite recursion is found (a symbol's value depending on itself). + static std::set<std::string> recursive_symbols; + + amrex::Parser parser(parse_function); + parser.registerVariables(varnames); + + std::set<std::string> symbols = parser.symbols(); + for (auto const& v : varnames) symbols.erase(v.c_str()); + + // User can provide inputs under this name, through which expressions + // can be provided for arbitrary variables. PICMI inputs are aware of + // this convention and use the same prefix as well. This potentially + // includes variable names that match physical or mathematical + // constants, in case the user wishes to enforce a different + // system of units or some form of quasi-physical behavior in the + // simulation. Thus, this needs to override any built-in + // constants. + amrex::ParmParse pp_my_constants("my_constants"); + + // Physical / Numerical Constants available to parsed expressions + static std::map<std::string, amrex::Real> warpx_constants = + { + {"clight", PhysConst::c}, + {"epsilon0", PhysConst::ep0}, + {"mu0", PhysConst::mu0}, + {"q_e", PhysConst::q_e}, + {"m_e", PhysConst::m_e}, + {"m_p", PhysConst::m_p}, + {"m_u", PhysConst::m_u}, + {"kb", PhysConst::kb}, + {"pi", MathConst::pi}, + }; + + for (auto it = symbols.begin(); it != symbols.end(); ) { + // Always parsing in double precision avoids potential overflows that may occur when parsing + // user's expressions because of the limited range of exponentials in single precision + double v; + + WARPX_ALWAYS_ASSERT_WITH_MESSAGE( + recursive_symbols.count(*it)==0, + "Expressions contains recursive symbol "+*it); + recursive_symbols.insert(*it); + const bool is_input = queryWithParser(pp_my_constants, it->c_str(), v); + recursive_symbols.erase(*it); + + if (is_input) { + parser.setConstant(*it, v); + it = symbols.erase(it); + continue; + } + + const auto constant = warpx_constants.find(*it); + if (constant != warpx_constants.end()) { + parser.setConstant(*it, constant->second); + it = symbols.erase(it); + continue; + } + + ++it; + } + for (auto const& s : symbols) { + amrex::Abort(Utils::TextMsg::Err("makeParser::Unknown symbol "+s)); + } + return parser; +} + + +double +utils::parser::parseStringtoDouble(const std::string& str) +{ + const auto parser = makeParser(str, {}); + const auto exe = parser.compileHost<0>(); + const auto result = exe(); + return result; +} + + +int +utils::parser::parseStringtoInt(const std::string& str, const std::string& name) +{ + const auto rval = static_cast<amrex::Real>(parseStringtoDouble(str)); + const auto ival = safeCastToInt(std::round(rval), name); + return ival; +} |