Skip to content

Commit

Permalink
Add ParmParse features for WarpX
Browse files Browse the repository at this point in the history
* Add `queryIntAsDouble` and `queryarrIntAsDouble`.

* Make recursion detection more robust for WarpX useage.

* Add a test.
  • Loading branch information
WeiqunZhang committed Sep 15, 2024
1 parent 0286385 commit 8706f65
Show file tree
Hide file tree
Showing 8 changed files with 306 additions and 4 deletions.
64 changes: 64 additions & 0 deletions Src/Base/AMReX_ParmParse.H
Original file line number Diff line number Diff line change
Expand Up @@ -1428,6 +1428,70 @@ public:
}
}

//! \brief Query integer with Parser, but treat the number as double
//! precision during parsing. The final result is cast to integer. It
//! may result in a runtime error if the conversion is not safe.
template <typename T, std::enable_if_t<std::is_integral_v<T>,int> = 0>
int queryAsDouble (const char* name, T& ref) const
{
double dref;
int exist = queryWithParser(name, dref);
if (exist) {
dref = std::round(dref);
ref = static_cast<T>(dref);
if (static_cast<double>(ref) != dref) {
amrex::Abort("ParmParse:: queryAsDouble is not safe");
}
}
return exist;
}

//! \brief Query integer array with Parser, but treat the numbers as
//! double precision uring parsing. The final results are cast to
//! integers. It may result in a runtime error if the conversion is not
//! safe.
template <typename T, std::enable_if_t<std::is_integral_v<T>,int> = 0>
int queryarrAsDouble (const char* name, int nvals, T* ref) const
{
std::vector<double> dref(nvals);
int exist = queryarrWithParser(name, nvals, dref.data());
if (exist) {
for (int i = 0; i < nvals; ++i) {
dref[i] = std::round(dref[i]);
ref[i] = static_cast<T>(dref[i]);
if (static_cast<double>(ref[i]) != dref[i]) {
amrex::Abort("ParmParse:: queryarrAsDouble is not safe");
}
}
}
return exist;
}

//! \brief Get integer with Parser, but treat the number as double
//! precision during parsing. The final result is cast to integer. It
//! may result in a runtime error if the conversion is not safe.
template <typename T, std::enable_if_t<std::is_integral_v<T>,int> = 0>
void getAsDouble (const char* name, T& ref) const
{
int exist = this->queryAsDouble(name, ref);
if (!exist) {
amrex::Error(std::string("ParmParse::getAsDouble: failed to get ")+name);
}
}

//! \brief Get integer array with Parser, but treat the numbers as
//! double precision uring parsing. The final results are cast to
//! integers. It may result in a runtime error if the conversion is not
//! safe.
template <typename T, std::enable_if_t<std::is_integral_v<T>,int> = 0>
void getarrAsDouble (const char* name, int nvals, T* ref) const
{
int exist = this->queryarrAsDouble(name, nvals, ref);
if (!exist) {
amrex::Error(std::string("ParmParse::getarrAsDouble: failed to get ")+name);
}
}

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

Expand Down
20 changes: 17 additions & 3 deletions Src/Base/AMReX_ParmParse.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -991,20 +991,34 @@ pp_make_parser (std::string const& func, Vector<std::string> const& vars,
symbols.erase(var);
}

bool recursive = false;
auto& recursive_symbols = g_parser_recursive_symbols[OpenMP::get_thread_num()];

for (auto const& s : symbols) {
value_t v = 0;
bool r = false;
for (auto const& pf : prefixes) {
std::string pfs = pf + s;
if (auto found = recursive_symbols.find(pfs); found != recursive_symbols.end()) {
recursive = true;
continue;
}
if (use_querywithparser) {
r = squeryWithParser(table, parser_prefix, pf+s, v);
r = squeryWithParser(table, parser_prefix, pfs, v);
} else {
r = squeryval(table, parser_prefix, pf+s, v,
r = squeryval(table, parser_prefix, pfs, v,
ParmParse::FIRST, ParmParse::LAST);
}
if (r) { break; }
}
if (r == false) {
amrex::Error("ParmParse: failed to parse " + func);
std::string msg("ParmParse: failed to parse "+func);
if (recursive) {
msg.append(" due to recursive symbol ").append(s);
} else {
msg.append(" due to unknown symbol ").append(s);
}
amrex::Error(msg);
}
parser.setConstant(s, v);
}
Expand Down
2 changes: 1 addition & 1 deletion Tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ else()
# List of subdirectories to search for CMakeLists.
#
set( AMREX_TESTS_SUBDIRS Amr AsyncOut CLZ CTOParFor DeviceGlobal Enum
MultiBlock MultiPeriod Parser Parser2 Reinit
MultiBlock MultiPeriod ParmParse Parser Parser2 Reinit
RoundoffDomain)

if (AMReX_PARTICLES)
Expand Down
9 changes: 9 additions & 0 deletions Tests/ParmParse/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
foreach(D IN LISTS AMReX_SPACEDIM)
set(_sources main.cpp)
set(_input_files inputs)

setup_test(${D} _sources _input_files)

unset(_sources)
unset(_input_files)
endforeach()
24 changes: 24 additions & 0 deletions Tests/ParmParse/GNUmakefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
AMREX_HOME ?= ../../amrex

DEBUG = TRUE

DIM = 3

COMP = gcc

USE_MPI = FALSE
USE_OMP = FALSE
USE_CUDA = FALSE
USE_HIP = FALSE
USE_SYCL = FALSE

BL_NO_FORT = TRUE

TINY_PROFILE = FALSE

include $(AMREX_HOME)/Tools/GNUMake/Make.defs

include ./Make.package
include $(AMREX_HOME)/Src/Base/Make.package

include $(AMREX_HOME)/Tools/GNUMake/Make.rules
1 change: 1 addition & 0 deletions Tests/ParmParse/Make.package
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
CEXE_sources += main.cpp
62 changes: 62 additions & 0 deletions Tests/ParmParse/inputs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@

amrex.signal_handling = 0
amrex.throw_exception = 1
amrex.v = 0

name = "I am w" \
"line 2"

b = ((1, 2, 3) (7, 8,9) (1,0, 1))

# 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.5/c^2

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

# one string across multiple lines
amrex.bar2 = "sin(pi/2)+alpha+
-amrex.c**2.5/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))

long_int_1 = 123456789012345
long_int_2 = 123'456'789'012'345
long_int_3 = 1.23456789012345e14

# recursion like this is not allowed
code.a = code.b
code.b = code.c
code.c = code.d
code.d = code.a

# Recursion like this is allowed, if my_constants is added as parser prefix.
# It's same as max_steps = my_constants.max_steps
my_constants.max_steps = 40
max_steps = max_steps
warpx.max_steps = max_steps

# query int as double
my_constants.lx = 40.e-6
my_constants.dx = 6.25e-7
my_constants.nx = lx/dx
n_cell = nx nx nx
ny = nx
128 changes: 128 additions & 0 deletions Tests/ParmParse/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
#include <AMReX.H>
#include <AMReX_Box.H>
#include <AMReX_Utility.H>
#include <AMReX_Print.H>
#include <AMReX_ParmParse.H>

using namespace amrex;

int main(int argc, char* argv[])
{
amrex::Initialize(argc,argv);
{
ParmParse::SetParserPrefix("physical_constants");
ParmParse pp("physical_constants");
pp.add("c", 299792458.);
pp.add("pi", 3.14159265358979323846);
}
{
ParmParse pp;

std::string name;
pp.query("name", name);
AMREX_ALWAYS_ASSERT(name == "I am w");
pp.query("name", name, 1);
AMREX_ALWAYS_ASSERT(name == "line 2");

Box box;
pp.query("b", box);
AMREX_ALWAYS_ASSERT(box == Box(IntVect(AMREX_D_DECL(1,2,3)),
IntVect(AMREX_D_DECL(7,8,9)),
IntVect(AMREX_D_DECL(1,0,1))));

double f0 = -1;
pp.query("f", f0);
AMREX_ALWAYS_ASSERT(f0 == 7);

std::vector<int> f;
pp.queryarr("f", f);
AMREX_ALWAYS_ASSERT(f[0] == 7 && f[1] == 99 && f[2] == 11);

std::vector<double> g;
pp.queryarr("g", g);
AMREX_ALWAYS_ASSERT(amrex::almostEqual(g[0], 7.2) &&
amrex::almostEqual(g[1], 11.6));

double w;
pp.query("w", w);
AMREX_ALWAYS_ASSERT(w == 1);
pp.queryWithParser("w", w);
AMREX_ALWAYS_ASSERT(w == -1);
}
{
ParmParse pp("amrex", "my_constants");
double foo = -1, bar = -2, bar2 = -3;
pp.getWithParser("foo", foo);
AMREX_ALWAYS_ASSERT(amrex::almostEqual(foo, 6.0-std::sqrt(299792458.)));
pp.get("bar", bar);
AMREX_ALWAYS_ASSERT(foo == bar);
pp.get("bar2", bar2);
AMREX_ALWAYS_ASSERT(bar == bar2);
}
{
ParmParse pp;
std::array<double,3> prob_lo, prob_hi;
pp.get("geom.prob_lo", prob_lo);
pp.get("geom.prob_hi", prob_hi);
AMREX_ALWAYS_ASSERT(amrex::almostEqual(prob_lo[0], -1.0) &&
amrex::almostEqual(prob_lo[1], -1.0) &&
amrex::almostEqual(prob_lo[2], -1.0) &&
amrex::almostEqual(prob_hi[0], 1.0) &&
amrex::almostEqual(prob_hi[1], 1.0) &&
amrex::almostEqual(prob_hi[2], 1.0));
}
{
ParmParse pp;
auto parser = pp.makeParser("pi*x+c*y", {"x","y"});
auto exe = parser.compile<2>();
AMREX_ALWAYS_ASSERT(amrex::almostEqual(3.14159265358979323846+299792458.,
exe(1.0,1.0)) &&
amrex::almostEqual(3.14159265358979323846, exe(1.0,0.0)) &&
amrex::almostEqual(299792458., exe(0.0, 1.0)));
}
{
ParmParse pp;
long long int i = 123456789012345;
long long int j = 0;
pp.get("long_int_1", j);
AMREX_ALWAYS_ASSERT(i==j);
pp.get("long_int_2", j);
AMREX_ALWAYS_ASSERT(i==j);
pp.get("long_int_3", j);
AMREX_ALWAYS_ASSERT(i==j);
}
try
{
ParmParse pp("code");
int a = 0;
pp.query("a",a);
amrex::Abort("Should not get here, because query should raise an exception");
} catch (std::runtime_error const&) {
// Runtime error as expected
}
{
int max_steps = -1;
ParmParse pp("", "my_constants");
pp.query("max_steps", max_steps);
AMREX_ALWAYS_ASSERT(max_steps == 40);
int warpx_max_steps = -1;
pp.query("warpx.max_steps", warpx_max_steps);
AMREX_ALWAYS_ASSERT(max_steps == 40);
}
{
ParmParse::SetParserPrefix("my_constants");
ParmParse pp;

int ny = 0;
pp.queryAsDouble("ny", ny);
AMREX_ALWAYS_ASSERT(ny == 64);

Array<int,3> n_cell{0,0,0};
pp.queryarrAsDouble("n_cell", 3, n_cell.data());
AMREX_ALWAYS_ASSERT(n_cell[0] == 64 && n_cell[1] == 64 && n_cell[2] == 64);
}
{
amrex::Print() << "SUCCESS\n";
}
amrex::Finalize();
}

0 comments on commit 8706f65

Please sign in to comment.