aboutsummaryrefslogtreecommitdiff
path: root/Source/Utils/Parser/ParserUtils.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/Utils/Parser/ParserUtils.cpp')
-rw-r--r--Source/Utils/Parser/ParserUtils.cpp165
1 files changed, 165 insertions, 0 deletions
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;
+}