Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/development' into fix_facediv_…
Browse files Browse the repository at this point in the history
…interp
  • Loading branch information
cgilet committed Jul 29, 2024
2 parents d81beba + 20e6f2e commit 0ebfd06
Show file tree
Hide file tree
Showing 21 changed files with 845 additions and 1,793 deletions.
165 changes: 155 additions & 10 deletions Docs/sphinx_documentation/source/Basics.rst
Original file line number Diff line number Diff line change
Expand Up @@ -224,17 +224,17 @@ file is a series of
definitions in the form of ``prefix.name = value value ....`` For each line,
text after # are comments. Here is an example inputs file.

.. highlight:: python
.. highlight:: python

::
::

nsteps = 100 # integer
nsteps = 1000 # nsteps appears a second time
dt = 0.03 # floating point number
ncells = 128 64 32 # a list of 3 ints
xrange = -0.5 0.5 # a list of 2 reals
title = "Three Kingdoms" # a string
hydro.cfl = 0.8 # with prefix, hydro
nsteps = 100 # integer
nsteps = 1000 # nsteps appears a second time
dt = 0.03 # floating point number
ncells = 128 64 32 # a list of 3 ints
xrange = -0.5 0.5 # a list of 2 reals
title = "Three Kingdoms" # a string
hydro.cfl = 0.8 # with prefix, hydro

The following code shows how to use :cpp:`ParmParse` to get/query the values.

Expand Down Expand Up @@ -275,6 +275,147 @@ by default returns the last one. The difference between :cpp:`query` and
get the value, whereas :cpp:`query` returns an error code without generating a
runtime error that will abort the run.

Math Expressions
----------------

:cpp:`ParmParse` supports math expressions for integers and floating point
numbers. For example,

.. highlight:: python

::

# three numbers. whitespaces inside `""` are okay.
f = 3+4 99 "5 + 6"

# two numbers. `\` is for continuation
g = 3.1+4.1 \
5.0+6.6

# two numbers unless using [query|get]WithParser
w = 1 -2

my_constants.alpha = 5.
amrex.c = c

# must use [query|get]WithParser
amrex.foo = sin( pi/2 ) + alpha + -amrex.c**2/c^2

# either [query|get] or [query|get]WithParser is okay
amrex.bar = sin(pi/2)+alpha+-amrex.c**2/c^2

geom.prob_lo = 2*sin(pi/4)/sqrt(2) sin(pi/2)+cos(pi/2) -(sin(pi*3/2)+cos(pi*3/2))

# three numbers. `\` is for continuation
geom.prob_hi = "2*sin(pi/4)/sqrt(2)" \
"sin(pi/2) + cos(pi/2)" \
-(sin(pi*3/2)+cos(pi*3/2))

can be processed by

.. highlight:: c++

::

{
ParmParse::SetParserPrefix("physical_constants");
ParmParse pp("physical_constants");
pp.add("c", 299792458.);
pp.add("pi", 3.14159265358979323846);
}
{
ParmParse pp;

double f0 = -1;
pp.query("f", f0);
std::cout << " double f = " << f0 << '\n';

std::vector<int> f;
pp.queryarr("f", f);
std::cout << " int f[3] = {" << f[0] << ", " << f[1] << ", "
<< f[2] << "}\n";

std::vector<double> g;
pp.queryarr("g", g);
std::cout << " double g[] = " << g[0] << " " << g[1] << '\n';

double w;
pp.query("w", w);
std::cout << " w = " << w << " with query\n";
pp.queryWithParser("w", w);
std::cout << " w = " << w << " with queryParser\n";
}
{
ParmParse pp("amrex", "my_constants");
double foo = -1, bar;
pp.getWithParser("foo", foo);
pp.get("bar", bar);
std::cout << " foo = " << foo << ", bar = " << bar << '\n';
}
{
ParmParse pp;
std::array<double,3> prob_lo, prob_hi;
pp.get("geom.prob_lo", prob_lo);
pp.get("geom.prob_hi", prob_hi);
std::cout << " double prob_lo[] = {" << prob_lo[0] << ", "
<< prob_lo[1] << ", " << prob_lo[2] << "}\n"
<< " double prob_hi[] = {" << prob_hi[0] << ", "
<< prob_hi[1] << ", " << prob_hi[2] << "}\n";
}

The results will be

.. highlight:: console

::

double f = 7
int f[3] = {7, 99, 11}
double g[] = 7.2 11.6
w = 1 with query
w = -1 with queryParser
foo = 5, bar = 5
double prob_lo[] = {1, 1, 1}
double prob_hi[] = {1, 1, 1}

Note that the empty spaces are significant for math expressions unless they
are inside a pair of ``"`` or explicitly parsed by
:cpp:`ParmParse::queryWithParser` or :cpp:`ParmParse::getWithParser`. If the
expression contains another variable, it will be looked up by
:cpp:`ParmParse`. :cpp:`ParmParse`'s constructor accepts an optional second
argument, ``parser_prefix``. When a variable in a math expression is being
looked up, it will first try to find it by using the exact name of the
variable. If this attempt fails and the :cpp:`ParmParse` object has a
non-empty non-static member ``parser_prefix``, it will try again, this time
looking up the variable by prefixing its name with the value of
``parser_prefix`` followed by a ``.``. If this attempt also fails and the
:cpp:`ParmParse` class has a non-empty static member ``ParserPrefix`` (which
can be set by :cpp:`ParmParse::SetParserPrefix`), it will try again, this
time looking up the variable by prefixing its name with the value of
``ParserPrefix`` followed by a ``.``.

The variables in :cpp:`ParmParse` math expressions are not evaluated until
they are referenced. If a variable is defined multiple times, the last
occurrence will override previous ones even if it appears after the variable
has been referenced. This behavior is demonstrated in the following example.

.. highlight:: python

::

foo.a = 1
foo.b = foo.a
foo.a = 2

will become

.. highlight:: python

::

foo.a = 2
foo.b = 2

Overriding Parameters with Command-Line Arguments
-------------------------------------------------

Expand Down Expand Up @@ -453,7 +594,11 @@ Besides :cpp:`amrex::Parser` for floating point numbers, AMReX also provides
similarity, but floating point number specific functions (e.g., ``sqrt``,
``sin``, etc.) are not supported in ``IParser``. In addition to ``/`` whose
result truncates towards zero, the integer parser also supports ``//`` whose
result truncates towards negative infinity.
result truncates towards negative infinity. Single quotes ``'`` are allowed
as a separator for :cpp:`IParser` numbers just like C++ integer
literals. Additionally, a floating point like number with a positive
exponent may be accepted as an integer if it is reasonable to do so. For
example, it's okay to have ``1.234e3``, but ``1.234e2`` is an error.

.. _sec:basics:initialize:

Expand Down
113 changes: 109 additions & 4 deletions Src/Base/AMReX_ParmParse.H
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

#include <AMReX_BLassert.H>
#include <AMReX_INT.H>
#include <AMReX_IParser.H>
#include <AMReX_Parser.H>
#include <AMReX_TypeTraits.H>

#include <array>
Expand All @@ -20,6 +22,7 @@ class Box;
template<int dim>
class IntVectND;
using IntVect = IntVectND<AMREX_SPACEDIM>;
class RealVect;

//
// ParmParse class implements a simple database for the storage and
Expand Down Expand Up @@ -268,18 +271,31 @@ using IntVect = IntVectND<AMREX_SPACEDIM>;
* t = 1.5
* #endif
*
* Math expression is supported for integers and reals. For example
*
* n_cell = 128
* amrex.n_cell = n_cell*2 8 16**2
*
* becomes
*
* n_cell = 128
* amrex.n_cell = 256 8 256
*
* More details can be found at https://amrex-codes.github.io/amrex/docs_html/Basics.html#parmparse
*/
class ParmParse
{
public:
enum { LAST = -1, FIRST = 0, ALL = -1 };
/**
* \brief Construct an additional ParmParse object sharing the same
* internal table as any other such objects in existence. If
* prefix is specified, load this string as the code prefix
* for this particular ParmParse object.
* internal table as any other such objects in existence. If prefix is
* specified, load this string as the code prefix for this particular
* ParmParse object. If parser_prefix is specified, it will be used as
* prefixed in math expression evaluations.
*/
explicit ParmParse (std::string prefix = std::string());
explicit ParmParse (std::string prefix = std::string(),
std::string parser_prefix = std::string());

//! Returns true if name is in table.
[[nodiscard]] bool contains (const char* name) const;
Expand Down Expand Up @@ -889,6 +905,28 @@ public:
//! Add a key 'name' with vector of values 'ref' to the end of the PP table.
void addarr (const char* name, const std::vector<Box>& refd);

/*
* \brief Query IntVect from array
*
* This reads IntVect from an array (e.g., `8 16 8`), not the format
* using parentheses (e.g., `(8,16,8)`).
*/
int queryarr (const char* name, IntVect& ref) const;

/*
* \brief Get IntVect from array
*
* This reads IntVect from an array (e.g., `8 16 8`), not the format
* using parentheses (e.g., `(8,16,8)`).
*/
void getarr (const char* name, IntVect& ref) const;

//! Query RealVect from array
int queryarr (const char* name, RealVect& ref) const;

//! Get RealVect from array
void getarr (const char* name, RealVect& ref) const;

template <typename T, std::size_t N>
void get (const char* name, std::array<T,N>& ref) const {
std::vector<T> v;
Expand Down Expand Up @@ -997,9 +1035,70 @@ public:
return exist;
}

/**
* \brief Query with Parser. If `name` is found, this uses amrex::Parser
* to parse the entire list of empty space separated values as a single
* scalar. The return value indicates whether it's found.
*/
int queryWithParser (const char* name, int& ref) const;
int queryWithParser (const char* name, long& ref) const;
int queryWithParser (const char* name, long long& ref) const;
int queryWithParser (const char* name, float& ref) const;
int queryWithParser (const char* name, double& ref) const;

/**
* \brief Query with Parser. If `name` is found, this uses amrex::Parser
* to parse the entire list of empty space separated values as a single
* scalar. If not, the value in `ref` will be added to the ParmParse
* database. The return value indicates whether it's found.
*/
template <typename T, std::enable_if_t<std::is_same_v<T,int> ||
std::is_same_v<T,long> ||
std::is_same_v<T,long long> ||
std::is_same_v<T,float> ||
std::is_same_v<T,double>,int> = 0>
int queryAddWithParser (const char* name, T& ref) const
{
int exist = this->queryWithParser(name, ref);
if (!exist) {
this->add(name, ref);
}
return exist;
}

/**
* \brief Get with Parser. If `name` is found, this uses amrex::Parser
* to parse the entire list of empty space separated values as a single
* scalar. If not, it's a runtime error.
*/
template <typename T, std::enable_if_t<std::is_same_v<T,int> ||
std::is_same_v<T,long> ||
std::is_same_v<T,long long> ||
std::is_same_v<T,float> ||
std::is_same_v<T,double>,int> = 0>
void getWithParser (const char* name, T& ref) const
{
int exist = this->queryWithParser(name, ref);
if (!exist) {
amrex::Error(std::string("ParmParse::getWithParser: failed to get ")+name);
}
}

//! Remove given name from the table.
int remove (const char* name);

//! Make Parser using given string `func` as function body and `vars` as
//! variable names. Constants known to ParmParse will be set. It's a
//! runtime error, if there are unknown symbols in `func`.
[[nodiscard]] Parser makeParser (std::string const& func,
Vector<std::string> const& vars) const;

//! Make IParser using given string `func` as function body and `vars`
//! as variable names. Constants known to ParmParse will be set. It's a
//! runtime error, if there are unknown symbols in `func`.
[[nodiscard]] IParser makeIParser (std::string const& func,
Vector<std::string> const& vars) const;

/**
* \brief Construct an initial ParmParse object from the argc and argv
* passed in to main(). An error will be signalled if another
Expand All @@ -1014,6 +1113,9 @@ public:
*/
static void Finalize ();

//! Set prefix used by math expression Parser
static void SetParserPrefix (std::string a_prefix);

static int Verbose ();
static void SetVerbose (int v);

Expand Down Expand Up @@ -1049,11 +1151,14 @@ public:
//! keyword for files to load
static std::string const FileKeyword;

static std::string ParserPrefix;

protected:

[[nodiscard]] std::string prefixedName (const std::string_view& str) const;

std::string m_prefix; // Prefix used in keyword search
std::string m_parser_prefix; // Prefix used by Parser
Table* m_table;
};

Expand Down
Loading

0 comments on commit 0ebfd06

Please sign in to comment.