Hipace
Parser.H
Go to the documentation of this file.
1 /* Copyright 2021
2  *
3  * This file is part of HiPACE++.
4  *
5  * Authors: AlexanderSinn
6  * License: BSD-3-Clause-LBNL
7  */
8 #ifndef HIPACE_Parser_H_
9 #define HIPACE_Parser_H_
10 
11 #include "Constants.H"
12 
13 #include <AMReX_ParmParse.H>
14 #include <AMReX_Parser.H>
15 #include <AMReX_RealVect.H>
16 #include <AMReX_IntVect.H>
17 
18 #include <algorithm>
19 #include <array>
20 #include <cmath>
21 #include <cstddef>
22 #include <limits>
23 #include <vector>
24 #include <set>
25 #include <string>
26 #include <sstream>
27 
28 
29 template<class T>
30 inline bool
31 queryWithParser (const amrex::ParmParse& pp, char const * const str, T& val);
32 
33 namespace Parser
34 {
35  // Cache for evaluated constants
36  extern std::map<std::string, double> my_constants_cache;
37 
38  // Physical / Numerical Constants available to parsed expressions
39  extern std::map<std::string, double> hipace_constants;
40  // {"pi", MathConst::pi},
41  // {"true", 1},
42  // {"false", 0}
43 
45  inline void
47  hipace_constants.insert({"clight", PhysConstSI::c });
48  hipace_constants.insert({"epsilon0", PhysConstSI::ep0 });
49  hipace_constants.insert({"mu0", PhysConstSI::mu0 });
50  hipace_constants.insert({"q_e", PhysConstSI::q_e });
51  hipace_constants.insert({"m_e", PhysConstSI::m_e });
52  hipace_constants.insert({"m_p", PhysConstSI::m_p });
53  hipace_constants.insert({"hbar", PhysConstSI::hbar});
54  hipace_constants.insert({"r_e", PhysConstSI::r_e});
55  }
56 
62  template<class T>
63  inline void
64  replaceWithParser (amrex::ParmParse& pp, char const * const str) {
65  T val{};
66  if(queryWithParser(pp, str, val)) {
67  pp.remove(str);
68  pp.add(str, val);
69  }
70  }
71 
77  template<class T>
78  inline void
79  replaceArrWithParser (amrex::ParmParse& pp, char const * const str) {
80  std::vector<T> val{};
81  if(queryWithParser(pp, str, val)) {
82  pp.remove(str);
83  pp.addarr(str, val);
84  }
85  }
86 
91  inline void
93  amrex::ParmParse pp_geom("geometry");
94  replaceArrWithParser<int>(pp_geom, "is_periodic");
95  replaceArrWithParser<double>(pp_geom, "prob_lo");
96  replaceArrWithParser<double>(pp_geom, "prob_hi");
97  }
98 
101  inline void
103  amrex::ParmParse pp_amrex("amrex");
104 
105  // https://amrex-codes.github.io/amrex/docs_html/GPU.html#inputs-parameters
106  bool the_arena_is_managed = false; // AMReX' default: true
107  pp_amrex.queryAdd("the_arena_is_managed", the_arena_is_managed);
108 
109  // https://amrex-codes.github.io/amrex/docs_html/InputsComputeBackends.html
110  std::string omp_threads = "nosmt"; // AMReX' default: system
111  pp_amrex.queryAdd("omp_threads", omp_threads);
112  }
113 
119  inline int
120  safeCastToInt (const double x, const std::string& real_name) {
121  int result = 0;
122  bool error_detected = false;
123  std::string assert_msg;
124  // (2.0*(numeric_limits<int>::max()/2+1)) converts numeric_limits<int>::max()+1 to a real
125  // ensuring accuracy to all digits. This accepts x = 2**31-1 but rejects 2**31.
126  if (x < (2.0*(std::numeric_limits<int>::max()/2+1))) {
127  if (std::ceil(x) >= std::numeric_limits<int>::min()) {
128  result = static_cast<int>(x);
129  } else {
130  error_detected = true;
131  assert_msg = "Error: Negative overflow detected when casting " +
132  real_name + " = " + std::to_string(x) + " to int";
133  }
134  } else if (x > 0) {
135  error_detected = true;
136  assert_msg = "Error: Overflow detected when casting " +
137  real_name + " = " + std::to_string(x) + " to int";
138  } else {
139  error_detected = true;
140  assert_msg = "Error: NaN detected when casting " + real_name + " to int";
141  }
142  AMREX_ALWAYS_ASSERT_WITH_MESSAGE(!error_detected, assert_msg);
143  return result;
144  }
145 
151  inline amrex::Long
152  safeCastToLong (const double x, const std::string& real_name) {
153  amrex::Long result = 0;
154  bool error_detected = false;
155  std::string assert_msg;
156  // (2.0*(numeric_limits<amrex::Long>::max()/2+1)) converts
157  // numeric_limits<amrex::Long>::max()+1 to a real
158  // ensuring accuracy to all digits. This accepts x = 2**31-1 but rejects 2**31.
159  if (x < (2.0*(std::numeric_limits<amrex::Long>::max()/2+1))) {
160  if (std::ceil(x) >= std::numeric_limits<amrex::Long>::min()) {
161  result = static_cast<amrex::Long>(x);
162  } else {
163  error_detected = true;
164  assert_msg = "Error: Negative overflow detected when casting " +
165  real_name + " = " + std::to_string(x) + " to amrex::Long";
166  }
167  } else if (x > 0) {
168  error_detected = true;
169  assert_msg = "Error: Overflow detected when casting " +
170  real_name + " = " + std::to_string(x) + " to amrex::Long";
171  } else {
172  error_detected = true;
173  assert_msg = "Error: NaN detected when casting " + real_name + " to amrex::Long";
174  }
175  AMREX_ALWAYS_ASSERT_WITH_MESSAGE(!error_detected, assert_msg);
176  return result;
177  }
178 
184  inline void
186  // Since queryWithParser recursively calls this routine, keep track of symbols
187  // in case an infinite recursion is found (a symbol's value depending on itself).
188  static std::set<std::string> recursive_symbols{};
189 
190  parser.registerVariables(varnames);
191 
192  std::set<std::string> symbols = parser.symbols();
193  for (auto const& v : varnames) symbols.erase(v);
194 
195  // User can provide inputs under this name, through which expressions
196  // can be provided for arbitrary variables. This potentially
197  // includes variable names that match physical or mathematical
198  // constants, in case the user wishes to enforce a different
199  // system of units or some form of quasi-physical behavior in the
200  // simulation. Thus, this needs to override any built-in
201  // constants.
202  amrex::ParmParse pp_my_constants("my_constants");
203 
204  for (auto const& s : symbols) {
205 
206  AMREX_ALWAYS_ASSERT_WITH_MESSAGE(recursive_symbols.count(s)==0,
207  "Expressions contains recursive symbol " + s);
208 
209  auto usr_constant = my_constants_cache.find(s);
210  if (usr_constant != my_constants_cache.end()) {
211  parser.setConstant(s, usr_constant->second);
212  continue;
213  }
214 
215  double v;
216  recursive_symbols.insert(s);
217  const bool is_input = queryWithParser(pp_my_constants, s.c_str(), v);
218  recursive_symbols.erase(s);
219 
220  if (is_input) {
221  my_constants_cache.insert({s, v});
222  parser.setConstant(s, v);
223  continue;
224  }
225 
226  auto phy_constant = hipace_constants.find(s);
227  if (phy_constant != hipace_constants.end()) {
228  parser.setConstant(s, phy_constant->second);
229  continue;
230  }
231 
232  amrex::Abort("makeParser::Unknown symbol " + s);
233  }
234  }
235 
242  inline void
243  fillWithParser (std::string const& str, double& val) {
245  initParser(parser, {});
246  val = parser.compileHost<0>()();
247  }
248 
249  inline void
250  fillWithParser (std::string const& str, float& val) {
252  initParser(parser, {});
253  val = static_cast<float>(parser.compileHost<0>()());
254  }
255 
256  inline void
257  fillWithParser (std::string const& str, int& val) {
259  initParser(parser, {});
260  val = safeCastToInt(std::round(parser.compileHost<0>()()),str);
261  }
262 
263  inline void
264  fillWithParser (std::string const& str, amrex::Long& val) {
266  initParser(parser, {});
267  val = safeCastToLong(std::round(parser.compileHost<0>()()),str);
268  }
269 
270  inline void
271  fillWithParser (std::string const& str, bool& val) {
273  initParser(parser, {});
274  val = parser.compileHost<0>()();
275  }
276 
277  inline void
278  fillWithParser (std::string const& str, std::string& val, bool do_escape_backtransform=true) {
279  std::string loc_str = str;
280 
281  // replace {{ and }} with 1 and 2
282  std::string::size_type pos = 0;
283  while ( (pos=loc_str.find("{{")) != std::string::npos) {
284  loc_str.replace(pos, 2, std::string{char(1)});
285  }
286  while ( (pos=loc_str.rfind("}}")) != std::string::npos) {
287  loc_str.replace(pos, 2, std::string{char(2)});
288  }
289 
290  // replace {expression} with to_string(parser(expression))
291  static std::set<std::string> recursive_symbols{};
292  while ( (pos=loc_str.rfind("{")) != std::string::npos) {
293  const auto pos_end = loc_str.find("}", pos);
294  if (pos_end == std::string::npos) {
295  amrex::ErrorStream() << "Bad format for input '"
296  << str
297  << "', unclosed brace!\n";
298  AMREX_ALWAYS_ASSERT(pos_end != std::string::npos);
299  }
300  auto pos_start_trim = pos + 1;
301  auto pos_count_trim = pos_end - pos - 1;
302  // remove leading and trailing spaces
303  while (loc_str.at(pos_start_trim) == ' ' && pos_count_trim > 0) {
304  ++pos_start_trim;
305  --pos_count_trim;
306  }
307  while (loc_str.at(pos_start_trim + pos_count_trim - 1) == ' ' && pos_count_trim > 0) {
308  --pos_count_trim;
309  }
310  const std::string parse_string = loc_str.substr(pos_start_trim, pos_count_trim);
311 
312  amrex::ParmParse pp_my_constants("my_constants");
313  if (pos_count_trim == 0) {
314  loc_str.replace(pos, pos_end-pos+1, "");
315  } else if (pp_my_constants.contains(parse_string.c_str())) {
316  // use my_constants directly (with recursive string parsing) if available
317  if (recursive_symbols.count(parse_string) != 0) {
318  amrex::ErrorStream() << "Expression '"
319  << str
320  << "' contains recursive symbol '"
321  << parse_string
322  << "'!\n";
323  AMREX_ALWAYS_ASSERT(recursive_symbols.count(parse_string) == 0);
324  }
325  std::string replacer;
326  pp_my_constants.get(parse_string.c_str(), replacer);
327  std::string parse_val;
328  recursive_symbols.insert(parse_string);
329  // for proper escape handling
330  fillWithParser(replacer, parse_val, false);
331  recursive_symbols.erase(parse_string);
332  loc_str.replace(pos, pos_end-pos+1, parse_val);
333  } else {
334  // else parse as number
335  double parse_val = 0.;
336  fillWithParser(parse_string, parse_val);
337  std::stringstream ss{};
338  ss << parse_val;
339  loc_str.replace(pos, pos_end-pos+1, ss.str());
340  }
341  }
342 
343  if (do_escape_backtransform) {
344  // replace 1 and 2 with { and }
345  while ( (pos=loc_str.find(char(1))) != std::string::npos) {
346  loc_str.replace(pos, 1, "{");
347  }
348  while ( (pos=loc_str.rfind(char(2))) != std::string::npos) {
349  loc_str.replace(pos, 1, "}");
350  }
351  }
352 
353  val = loc_str;
354  }
355 
363  template<class T>
364  inline void
365  fillWithParserArr (std::vector<std::string> const& str_arr, T& val) {
366  std::string str{};
367  if (!str_arr.empty()) {
368  str = str_arr[0];
369  for (auto i=1ul ; i != str_arr.size() ; ++i) {
370  str += ' ' + str_arr[i];
371  }
372  }
373  fillWithParser(str, val);
374  }
375 
376  template<class T>
377  inline void
378  fillWithParserArr (std::vector<std::string> const& str_arr, std::vector<T>& val_arr) {
379  auto const n = str_arr.size();
380  val_arr.resize(n);
381  for (auto i=0ul ; i != n ; ++i) {
382  fillWithParser(str_arr[i], val_arr[i]);
383  }
384  }
385 
386  template<class T>
387  inline void
388  fillWithParserArr (std::vector<std::string> const& str_arr, amrex::Vector<T>& val_arr) {
389  auto const n = str_arr.size();
390  val_arr.resize(n);
391  for (auto i=0ul ; i != n ; ++i) {
392  fillWithParser(str_arr[i], val_arr[i]);
393  }
394  }
395 
396  template<class T, std::size_t size>
397  inline void
398  fillWithParserArr (std::vector<std::string> const& str_arr, std::array<T,size>& val_arr) {
399  const auto n = str_arr.size();
400  if (n != size) {
401  for( auto const& str : str_arr) {
402  amrex::ErrorStream() << str << ' ';
403  }
404  amrex::ErrorStream() << "has wrong length " << n << " should be " << size << '\n';
405  }
406  AMREX_ALWAYS_ASSERT( n == size );
407  for (auto i=0ul ; i != n ; ++i) {
408  fillWithParser(str_arr[i], val_arr[i]);
409  }
410  }
411 
412  inline void
413  fillWithParserArr (std::vector<std::string> const& str_arr, amrex::RealVect& val_arr) {
414  const auto n = str_arr.size();
415  if (n != AMREX_SPACEDIM) {
416  for( auto const& str : str_arr) {
417  amrex::ErrorStream() << str << ' ';
418  }
419  amrex::ErrorStream() << "has wrong length " << n
420  << " should be " << AMREX_SPACEDIM << '\n';
421  }
422  AMREX_ALWAYS_ASSERT( n == AMREX_SPACEDIM );
423  for (auto i=0ul ; i != n ; ++i) {
424  fillWithParser(str_arr[i], val_arr[i]);
425  }
426  }
427 
428  inline void
429  fillWithParserArr (std::vector<std::string> const& str_arr, amrex::IntVect& val_arr) {
430  const auto n = str_arr.size();
431  if (n != AMREX_SPACEDIM) {
432  for( auto const& str : str_arr) {
433  amrex::ErrorStream() << str << ' ';
434  }
435  amrex::ErrorStream() << "has wrong length " << n
436  << " should be " << AMREX_SPACEDIM << '\n';
437  }
438  AMREX_ALWAYS_ASSERT( n == AMREX_SPACEDIM );
439  for (auto i=0ul ; i != n ; ++i) {
440  fillWithParser(str_arr[i], val_arr[i]);
441  }
442  }
443 }
444 
451 template<class T>
452 inline void
453 getWithParser (const amrex::ParmParse& pp, char const * const str, T& val) {
454  std::vector<std::string> f;
455  pp.getarr(str, f);
457 }
458 
465 template<class T>
466 inline bool
467 queryWithParser (const amrex::ParmParse& pp, char const * const str, T& val) {
468  std::vector<std::string> f;
469  const int is_specified = pp.queryarr(str, f);
470  if (is_specified) {
472  }
473  return is_specified;
474 }
475 
484 template<class T>
485 inline bool
486 queryWithParserAlt (const amrex::ParmParse& pp, char const * const str, T& val,
487  const amrex::ParmParse& pp_alt) {
488  // uses boolean short circuiting
489  return queryWithParser(pp, str, val) || queryWithParser(pp_alt, str, val);
490 }
491 
500 template<class T>
501 inline void
502 getWithParserAlt (const amrex::ParmParse& pp, char const * const str, T& val,
503  const amrex::ParmParse& pp_alt) {
504  if (!queryWithParserAlt(pp, str, val, pp_alt)) {
505  // Use error massage of the get function with the original pp
506  getWithParser(pp, str, val);
507  }
508 }
509 
516 template<int N>
517 inline auto
518 makeFunctionWithParser (std::string const& func_str,
520  amrex::Vector<std::string> const& varnames)
521 {
522  std::string clean_str = func_str;
523  for (char& c : clean_str) {
524  if (c=='\n' || c=='\r' || c=='\t') c = ' ';
525  }
526  parser.define(clean_str);
527  Parser::initParser(parser, varnames);
528  return parser.compile<N>();
529 }
530 
531 #endif
#define AMREX_ALWAYS_ASSERT_WITH_MESSAGE(EX, MSG)
#define AMREX_ALWAYS_ASSERT(EX)
amrex::ParmParse pp
bool queryWithParserAlt(const amrex::ParmParse &pp, char const *const str, T &val, const amrex::ParmParse &pp_alt)
return if input file contains the expression, if so it is parsed into val. The input is searched in p...
Definition: Parser.H:486
auto makeFunctionWithParser(std::string const &func_str, amrex::Parser &parser, amrex::Vector< std::string > const &varnames)
return function object for Host and Device from the input file
Definition: Parser.H:518
void getWithParserAlt(const amrex::ParmParse &pp, char const *const str, T &val, const amrex::ParmParse &pp_alt)
fill val with the evaluated expression from the input file The input is searched in pp first,...
Definition: Parser.H:502
bool queryWithParser(const amrex::ParmParse &pp, char const *const str, T &val)
return if input file contains the expression, if so it is parsed into val
Definition: Parser.H:467
void getWithParser(const amrex::ParmParse &pp, char const *const str, T &val)
fill val with the evaluated expression from the input file
Definition: Parser.H:453
bool contains(const char *name) const
void addarr(const char *name, const std::vector< int > &ref)
void get(const char *name, bool &ref, int ival=FIRST) const
int queryarr(const char *name, std::vector< int > &ref, int start_ix=FIRST, int num_val=ALL) const
int queryAdd(const char *name, T &ref)
void getarr(const char *name, std::vector< int > &ref, int start_ix=FIRST, int num_val=ALL) const
int remove(const char *name)
void add(const char *name, bool val)
int i
Definition: MakeOpenBoundary.py:152
Definition: Parser.cpp:8
void replaceAmrexParamsWithParser()
replace AMReX input parameters with Parsed version
Definition: Parser.H:92
void fillWithParserArr(std::vector< std::string > const &str_arr, T &val)
fill second argument val of array type with a value obtained through Parsing str_arr if val is just a...
Definition: Parser.H:365
void addConstantsToParser()
add Physical constants to Parser constants
Definition: Parser.H:46
amrex::Long safeCastToLong(const double x, const std::string &real_name)
return valid Long, asserts if inf or NaN
Definition: Parser.H:152
void replaceArrWithParser(amrex::ParmParse &pp, char const *const str)
array version of replaceWithParser
Definition: Parser.H:79
void initParser(amrex::Parser &parser, amrex::Vector< std::string > const &varnames)
init Parser ready to compile
Definition: Parser.H:185
void fillWithParser(std::string const &str, double &val)
fill second argument val with a value obtained through Parsing str for std::string: val is same as st...
Definition: Parser.H:243
std::map< std::string, double > my_constants_cache
Definition: Parser.cpp:10
void setDefaultParams()
set default ParmParse parameters before AMReX is initialized
Definition: Parser.H:102
int safeCastToInt(const double x, const std::string &real_name)
return valid int, asserts if inf or NaN
Definition: Parser.H:120
void replaceWithParser(amrex::ParmParse &pp, char const *const str)
replace ParmParse input with a Parsed version
Definition: Parser.H:64
std::map< std::string, double > hipace_constants
Definition: Parser.cpp:14
static constexpr auto m_p
Definition: Constants.H:22
static constexpr auto r_e
Definition: Constants.H:24
static constexpr auto mu0
Definition: Constants.H:19
static constexpr auto q_e
Definition: Constants.H:20
static constexpr auto hbar
Definition: Constants.H:23
static constexpr auto m_e
Definition: Constants.H:21
static constexpr auto c
Definition: Constants.H:17
static constexpr auto ep0
Definition: Constants.H:18
@ N
Definition: MultiLaser.H:92
AMREX_GPU_HOST_DEVICE Long size(T const &b) noexcept
std::ostream & ErrorStream()
void Abort(const std::string &msg)
str
Definition: checksumAPI.py:112
parser
Definition: checksumAPI.py:104