Skip to content

Commit f4f7430

Browse files
committed
Add ParmParse features for WarpX
* Add `queryIntAsDouble` and `queryarrIntAsDouble`. * Make recursion detection more robust for WarpX useage. * Add a test.
1 parent 0286385 commit f4f7430

File tree

8 files changed

+308
-4
lines changed

8 files changed

+308
-4
lines changed

Src/Base/AMReX_ParmParse.H

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1428,6 +1428,70 @@ public:
14281428
}
14291429
}
14301430

1431+
//! \brief Query integer with Parser, but treat the number as double
1432+
//! precision during parsing. The final result is cast to integer. It
1433+
//! may result in a runtime error if the conversion is not safe.
1434+
template <typename T, std::enable_if_t<std::is_integral_v<T>,int> = 0>
1435+
int queryAsDouble (const char* name, T& ref) const
1436+
{
1437+
double dref;
1438+
int exist = queryWithParser(name, dref);
1439+
if (exist) {
1440+
dref = std::round(dref);
1441+
ref = static_cast<T>(dref);
1442+
if (static_cast<double>(ref) != dref) {
1443+
amrex::Abort("ParmParse:: queryAsDouble is not safe");
1444+
}
1445+
}
1446+
return exist;
1447+
}
1448+
1449+
//! \brief Query integer array with Parser, but treat the numbers as
1450+
//! double precision uring parsing. The final results are cast to
1451+
//! integers. It may result in a runtime error if the conversion is not
1452+
//! safe.
1453+
template <typename T, std::enable_if_t<std::is_integral_v<T>,int> = 0>
1454+
int queryarrAsDouble (const char* name, int nvals, T* ref) const
1455+
{
1456+
std::vector<double> dref(nvals);
1457+
int exist = queryarrWithParser(name, nvals, dref.data());
1458+
if (exist) {
1459+
for (int i = 0; i < nvals; ++i) {
1460+
dref[i] = std::round(dref[i]);
1461+
ref[i] = static_cast<T>(dref[i]);
1462+
if (static_cast<double>(ref[i]) != dref[i]) {
1463+
amrex::Abort("ParmParse:: queryarrAsDouble is not safe");
1464+
}
1465+
}
1466+
}
1467+
return exist;
1468+
}
1469+
1470+
//! \brief Get integer with Parser, but treat the number as double
1471+
//! precision during parsing. The final result is cast to integer. It
1472+
//! may result in a runtime error if the conversion is not safe.
1473+
template <typename T, std::enable_if_t<std::is_integral_v<T>,int> = 0>
1474+
void getAsDouble (const char* name, T& ref) const
1475+
{
1476+
int exist = this->queryAsDouble(name, ref);
1477+
if (!exist) {
1478+
amrex::Error(std::string("ParmParse::getAsDouble: failed to get ")+name);
1479+
}
1480+
}
1481+
1482+
//! \brief Get integer array with Parser, but treat the numbers as
1483+
//! double precision uring parsing. The final results are cast to
1484+
//! integers. It may result in a runtime error if the conversion is not
1485+
//! safe.
1486+
template <typename T, std::enable_if_t<std::is_integral_v<T>,int> = 0>
1487+
void getarrAsDouble (const char* name, int nvals, T* ref) const
1488+
{
1489+
int exist = this->queryarrAsDouble(name, nvals, ref);
1490+
if (!exist) {
1491+
amrex::Error(std::string("ParmParse::getarrAsDouble: failed to get ")+name);
1492+
}
1493+
}
1494+
14311495
//! Remove given name from the table.
14321496
int remove (const char* name);
14331497

Src/Base/AMReX_ParmParse.cpp

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -991,20 +991,34 @@ pp_make_parser (std::string const& func, Vector<std::string> const& vars,
991991
symbols.erase(var);
992992
}
993993

994+
bool recursive = false;
995+
auto& recursive_symbols = g_parser_recursive_symbols[OpenMP::get_thread_num()];
996+
994997
for (auto const& s : symbols) {
995998
value_t v = 0;
996999
bool r = false;
9971000
for (auto const& pf : prefixes) {
1001+
std::string pfs = pf + s;
1002+
if (auto found = recursive_symbols.find(pfs); found != recursive_symbols.end()) {
1003+
recursive = true;
1004+
continue;
1005+
}
9981006
if (use_querywithparser) {
999-
r = squeryWithParser(table, parser_prefix, pf+s, v);
1007+
r = squeryWithParser(table, parser_prefix, pfs, v);
10001008
} else {
1001-
r = squeryval(table, parser_prefix, pf+s, v,
1009+
r = squeryval(table, parser_prefix, pfs, v,
10021010
ParmParse::FIRST, ParmParse::LAST);
10031011
}
10041012
if (r) { break; }
10051013
}
10061014
if (r == false) {
1007-
amrex::Error("ParmParse: failed to parse " + func);
1015+
std::string msg("ParmParse: failed to parse "+func);
1016+
if (recursive) {
1017+
msg.append(" due to recursive symbol ").append(s);
1018+
} else {
1019+
msg.append(" due to unknown symbol ").append(s);
1020+
}
1021+
amrex::Error(msg);
10081022
}
10091023
parser.setConstant(s, v);
10101024
}

Tests/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ else()
122122
# List of subdirectories to search for CMakeLists.
123123
#
124124
set( AMREX_TESTS_SUBDIRS Amr AsyncOut CLZ CTOParFor DeviceGlobal Enum
125-
MultiBlock MultiPeriod Parser Parser2 Reinit
125+
MultiBlock MultiPeriod ParmParse Parser Parser2 Reinit
126126
RoundoffDomain)
127127

128128
if (AMReX_PARTICLES)

Tests/ParmParse/CMakeLists.txt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
foreach(D IN LISTS AMReX_SPACEDIM)
2+
set(_sources main.cpp)
3+
set(_input_files inputs)
4+
5+
setup_test(${D} _sources _input_files)
6+
7+
unset(_sources)
8+
unset(_input_files)
9+
endforeach()

Tests/ParmParse/GNUmakefile

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
AMREX_HOME ?= ../../amrex
2+
3+
DEBUG = TRUE
4+
5+
DIM = 3
6+
7+
COMP = gcc
8+
9+
USE_MPI = FALSE
10+
USE_OMP = FALSE
11+
USE_CUDA = FALSE
12+
USE_HIP = FALSE
13+
USE_SYCL = FALSE
14+
15+
BL_NO_FORT = TRUE
16+
17+
TINY_PROFILE = FALSE
18+
19+
include $(AMREX_HOME)/Tools/GNUMake/Make.defs
20+
21+
include ./Make.package
22+
include $(AMREX_HOME)/Src/Base/Make.package
23+
24+
include $(AMREX_HOME)/Tools/GNUMake/Make.rules

Tests/ParmParse/Make.package

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
CEXE_sources += main.cpp

Tests/ParmParse/inputs

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
2+
amrex.signal_handling = 0
3+
amrex.throw_exception = 1
4+
amrex.v = 0
5+
6+
name = "I am w" \
7+
"line 2"
8+
9+
b = ((1, 2, 3) (7, 8,9) (1,0, 1))
10+
11+
# three numbers. whitespaces inside `""` are okay.
12+
f = 3+4 99 "5 + 6"
13+
14+
# two numbers. `\` is for continuation
15+
g = 3.1+4.1 \
16+
5.0+6.6
17+
18+
# two numbers unless using [query|get]WithParser
19+
w = 1 -2
20+
21+
my_constants.alpha = 5.
22+
amrex.c = c
23+
24+
physical_constants.c = 3.e10
25+
26+
# must use [query|get]WithParser
27+
amrex.foo = sin( pi/2 ) + alpha + -amrex.c**2.5/c^2
28+
29+
# either [query|get] or [query|get]WithParser is okay
30+
amrex.bar = sin(pi/2)+alpha+-amrex.c**2.5/c^2
31+
32+
# one string across multiple lines
33+
amrex.bar2 = "sin(pi/2)+alpha+
34+
-amrex.c**2.5/c^2"
35+
36+
geom.prob_lo = -2*sin(pi/4)/sqrt(2) -sin(pi/2)-cos(pi/2) (sin(pi*3/2)+cos(pi*3/2))
37+
38+
# three numbers. `\` is for continuation
39+
geom.prob_hi = "2*sin(pi/4)/sqrt(2)" \
40+
"sin(pi/2) + cos(pi/2)" \
41+
-(sin(pi*3/2)+cos(pi*3/2))
42+
43+
long_int_1 = 123456789012345
44+
long_int_2 = 123'456'789'012'345
45+
long_int_3 = 1.23456789012345e14
46+
47+
# recursion like this is not allowed
48+
code.a = code.b
49+
code.b = code.c
50+
code.c = code.d
51+
code.d = code.a
52+
53+
# Recursion like this is allowed, if my_constants is added as parser prefix.
54+
# It's same as max_steps = my_constants.max_steps
55+
my_constants.max_steps = 40
56+
max_steps = max_steps
57+
warpx.max_steps = max_steps
58+
59+
# query int as double
60+
my_constants.lx = 40.e-6
61+
my_constants.dx = 6.25e-7
62+
my_constants.nx = lx/dx
63+
n_cell = nx nx nx
64+
ny = nx

Tests/ParmParse/main.cpp

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
#include <AMReX.H>
2+
#include <AMReX_Box.H>
3+
#include <AMReX_Utility.H>
4+
#include <AMReX_Print.H>
5+
#include <AMReX_ParmParse.H>
6+
7+
using namespace amrex;
8+
9+
int main(int argc, char* argv[])
10+
{
11+
amrex::Initialize(argc,argv);
12+
{
13+
ParmParse::SetParserPrefix("physical_constants");
14+
ParmParse pp("physical_constants");
15+
pp.add("c", 299792458.);
16+
pp.add("pi", 3.14159265358979323846);
17+
}
18+
{
19+
ParmParse pp;
20+
21+
std::string name;
22+
pp.query("name", name);
23+
AMREX_ALWAYS_ASSERT(name == "I am w");
24+
pp.query("name", name, 1);
25+
AMREX_ALWAYS_ASSERT(name == "line 2");
26+
27+
Box box;
28+
pp.query("b", box);
29+
AMREX_ALWAYS_ASSERT(box == Box(IntVect(AMREX_D_DECL(1,2,3)),
30+
IntVect(AMREX_D_DECL(7,8,9)),
31+
IntVect(AMREX_D_DECL(1,0,1))));
32+
33+
double f0 = -1;
34+
pp.query("f", f0);
35+
AMREX_ALWAYS_ASSERT(f0 == 7);
36+
37+
std::vector<int> f;
38+
pp.queryarr("f", f);
39+
AMREX_ALWAYS_ASSERT(f[0] == 7 && f[1] == 99 && f[2] == 11);
40+
41+
std::vector<double> g;
42+
pp.queryarr("g", g);
43+
AMREX_ALWAYS_ASSERT(amrex::almostEqual(g[0], 7.2) &&
44+
amrex::almostEqual(g[1], 11.6));
45+
46+
double w;
47+
pp.query("w", w);
48+
AMREX_ALWAYS_ASSERT(w == 1);
49+
pp.queryWithParser("w", w);
50+
AMREX_ALWAYS_ASSERT(w == -1);
51+
}
52+
{
53+
ParmParse pp("amrex", "my_constants");
54+
double foo = -1, bar = -2, bar2 = -3;
55+
pp.getWithParser("foo", foo);
56+
AMREX_ALWAYS_ASSERT(amrex::almostEqual(foo, 6.0-std::sqrt(299792458.)));
57+
pp.get("bar", bar);
58+
AMREX_ALWAYS_ASSERT(foo == bar);
59+
pp.get("bar2", bar2);
60+
AMREX_ALWAYS_ASSERT(bar == bar2);
61+
}
62+
{
63+
ParmParse pp;
64+
std::array<double,3> prob_lo, prob_hi;
65+
pp.get("geom.prob_lo", prob_lo);
66+
pp.get("geom.prob_hi", prob_hi);
67+
AMREX_ALWAYS_ASSERT(amrex::almostEqual(prob_lo[0], -1.0) &&
68+
amrex::almostEqual(prob_lo[1], -1.0) &&
69+
amrex::almostEqual(prob_lo[2], -1.0) &&
70+
amrex::almostEqual(prob_hi[0], 1.0) &&
71+
amrex::almostEqual(prob_hi[1], 1.0) &&
72+
amrex::almostEqual(prob_hi[2], 1.0));
73+
}
74+
{
75+
ParmParse pp;
76+
auto parser = pp.makeParser("pi*x+c*y", {"x","y"});
77+
auto exe = parser.compile<2>();
78+
AMREX_ALWAYS_ASSERT(amrex::almostEqual(3.14159265358979323846+299792458.,
79+
exe(1.0,1.0)) &&
80+
amrex::almostEqual(3.14159265358979323846, exe(1.0,0.0)) &&
81+
amrex::almostEqual(299792458., exe(0.0, 1.0)));
82+
}
83+
{
84+
ParmParse pp;
85+
long long int i = 123456789012345;
86+
long long int j = 0;
87+
pp.get("long_int_1", j);
88+
AMREX_ALWAYS_ASSERT(i==j);
89+
pp.get("long_int_2", j);
90+
AMREX_ALWAYS_ASSERT(i==j);
91+
pp.get("long_int_3", j);
92+
AMREX_ALWAYS_ASSERT(i==j);
93+
}
94+
try
95+
{
96+
ParmParse pp("code");
97+
int a = 0;
98+
pp.query("a",a);
99+
amrex::Abort("Should not get here, because query should raise an exception");
100+
} catch (std::runtime_error const&) {
101+
// Runtime error as expected
102+
}
103+
{
104+
int max_steps = -1;
105+
ParmParse pp("", "my_constants");
106+
pp.query("max_steps", max_steps);
107+
AMREX_ALWAYS_ASSERT(max_steps == 40);
108+
int warpx_max_steps = -1;
109+
pp.query("warpx.max_steps", warpx_max_steps);
110+
AMREX_ALWAYS_ASSERT(max_steps == 40);
111+
}
112+
{
113+
ParmParse::SetParserPrefix("my_constants");
114+
ParmParse pp;
115+
116+
int ny = 0;
117+
pp.queryAsDouble("ny", ny);
118+
AMREX_ALWAYS_ASSERT(ny == 64);
119+
120+
Array<int,3> n_cell{0,0,0};
121+
pp.queryarrAsDouble("n_cell", 3, n_cell.data());
122+
AMREX_ALWAYS_ASSERT(n_cell[0] == 64 && n_cell[1] == 64 && n_cell[2] == 64);
123+
}
124+
{
125+
amrex::Print() << "SUCCESS\n";
126+
}
127+
amrex::Finalize();
128+
}

0 commit comments

Comments
 (0)