aboutsummaryrefslogtreecommitdiff
path: root/Source/Utils/Parser
diff options
context:
space:
mode:
Diffstat (limited to 'Source/Utils/Parser')
-rw-r--r--Source/Utils/Parser/CMakeLists.txt5
-rw-r--r--Source/Utils/Parser/IntervalsParser.H220
-rw-r--r--Source/Utils/Parser/IntervalsParser.cpp251
-rw-r--r--Source/Utils/Parser/Make.package4
-rw-r--r--Source/Utils/Parser/ParserUtils.H298
-rw-r--r--Source/Utils/Parser/ParserUtils.cpp165
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;
+}