diff --git a/.github/workflows/gcc.yml b/.github/workflows/gcc.yml index 26ad6eaf52..94f625e93f 100644 --- a/.github/workflows/gcc.yml +++ b/.github/workflows/gcc.yml @@ -295,7 +295,7 @@ jobs: run: | export CCACHE_COMPRESS=1 export CCACHE_COMPRESSLEVEL=10 - export CCACHE_MAXSIZE=40M + export CCACHE_MAXSIZE=100M export CCACHE_EXTRAFILES=${{ github.workspace }}/.clang-tidy export CCACHE_LOGFILE=${{ github.workspace }}/ccache.log.txt ccache -z @@ -360,7 +360,7 @@ jobs: run: | export CCACHE_COMPRESS=1 export CCACHE_COMPRESSLEVEL=10 - export CCACHE_MAXSIZE=315M + export CCACHE_MAXSIZE=400M export CCACHE_EXTRAFILES=${{ github.workspace }}/.clang-tidy export CCACHE_LOGFILE=${{ github.workspace }}/ccache.log.txt ccache -z @@ -616,7 +616,7 @@ jobs: run: | export CCACHE_COMPRESS=1 export CCACHE_COMPRESSLEVEL=10 - export CCACHE_MAXSIZE=30M + export CCACHE_MAXSIZE=100M export CCACHE_EXTRAFILES=${{ github.workspace }}/.clang-tidy export CCACHE_LOGFILE=${{ github.workspace }}/ccache.log.txt ccache -z diff --git a/.github/workflows/hip.yml b/.github/workflows/hip.yml index a38c1f6547..345d7c468b 100644 --- a/.github/workflows/hip.yml +++ b/.github/workflows/hip.yml @@ -32,7 +32,7 @@ jobs: run: | export CCACHE_COMPRESS=1 export CCACHE_COMPRESSLEVEL=10 - export CCACHE_MAXSIZE=100M + export CCACHE_MAXSIZE=150M ccache -z source /etc/profile.d/rocm.sh @@ -90,7 +90,7 @@ jobs: run: | export CCACHE_COMPRESS=1 export CCACHE_COMPRESSLEVEL=10 - export CCACHE_MAXSIZE=40M + export CCACHE_MAXSIZE=100M ccache -z source /etc/profile.d/rocm.sh @@ -142,7 +142,7 @@ jobs: run: | export CCACHE_COMPRESS=1 export CCACHE_COMPRESSLEVEL=10 - export CCACHE_MAXSIZE=60M + export CCACHE_MAXSIZE=100M ccache -z ./configure --dim 2 --with-hip yes --enable-eb yes --enable-xsdk-defaults yes --with-mpi no --with-omp no --single-precision yes --single-precision-particles yes diff --git a/.github/workflows/intel.yml b/.github/workflows/intel.yml index 36fc674ecd..227b0f9738 100644 --- a/.github/workflows/intel.yml +++ b/.github/workflows/intel.yml @@ -32,7 +32,7 @@ jobs: run: | export CCACHE_COMPRESS=1 export CCACHE_COMPRESSLEVEL=10 - export CCACHE_MAXSIZE=45M + export CCACHE_MAXSIZE=100M export CCACHE_DEPEND=1 ccache -z @@ -80,7 +80,7 @@ jobs: run: | export CCACHE_COMPRESS=1 export CCACHE_COMPRESSLEVEL=10 - export CCACHE_MAXSIZE=55M + export CCACHE_MAXSIZE=150M export CCACHE_DEPEND=1 ccache -z @@ -126,7 +126,7 @@ jobs: run: | export CCACHE_COMPRESS=1 export CCACHE_COMPRESSLEVEL=10 - export CCACHE_MAXSIZE=55M + export CCACHE_MAXSIZE=100M export CCACHE_DEPEND=1 ccache -z @@ -175,7 +175,7 @@ jobs: run: | export CCACHE_COMPRESS=1 export CCACHE_COMPRESSLEVEL=10 - export CCACHE_MAXSIZE=55M + export CCACHE_MAXSIZE=100M export CCACHE_DEPEND=1 ccache -z @@ -221,7 +221,7 @@ jobs: run: | export CCACHE_COMPRESS=1 export CCACHE_COMPRESSLEVEL=10 - export CCACHE_MAXSIZE=40M + export CCACHE_MAXSIZE=100M ccache -z set +e diff --git a/.github/workflows/smoke.yml b/.github/workflows/smoke.yml index 650ef144e8..d56e52c63e 100644 --- a/.github/workflows/smoke.yml +++ b/.github/workflows/smoke.yml @@ -29,7 +29,7 @@ jobs: run: | export CCACHE_COMPRESS=1 export CCACHE_COMPRESSLEVEL=10 - export CCACHE_MAXSIZE=40M + export CCACHE_MAXSIZE=100M export CCACHE_EXTRAFILES=${{ github.workspace }}/.clang-tidy export CCACHE_LOGFILE=${{ github.workspace }}/ccache.log.txt ccache -z diff --git a/Src/Base/AMReX_ParmParse.H b/Src/Base/AMReX_ParmParse.H index c7ed675580..caa4697cd5 100644 --- a/Src/Base/AMReX_ParmParse.H +++ b/Src/Base/AMReX_ParmParse.H @@ -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 ,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(dref); + if (static_cast(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 ,int> = 0> + int queryarrAsDouble (const char* name, int nvals, T* ref) const + { + std::vector 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(dref[i]); + if (static_cast(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 ,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 ,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); diff --git a/Src/Base/AMReX_ParmParse.cpp b/Src/Base/AMReX_ParmParse.cpp index 767bd2a4d0..9d61fad89f 100644 --- a/Src/Base/AMReX_ParmParse.cpp +++ b/Src/Base/AMReX_ParmParse.cpp @@ -991,20 +991,34 @@ pp_make_parser (std::string const& func, Vector 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); } diff --git a/Tests/CMakeLists.txt b/Tests/CMakeLists.txt index 29489e8d4f..a5ec9e1cf4 100644 --- a/Tests/CMakeLists.txt +++ b/Tests/CMakeLists.txt @@ -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) diff --git a/Tests/ParmParse/CMakeLists.txt b/Tests/ParmParse/CMakeLists.txt new file mode 100644 index 0000000000..9c0e7f321d --- /dev/null +++ b/Tests/ParmParse/CMakeLists.txt @@ -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() diff --git a/Tests/ParmParse/GNUmakefile b/Tests/ParmParse/GNUmakefile new file mode 100644 index 0000000000..cacc297050 --- /dev/null +++ b/Tests/ParmParse/GNUmakefile @@ -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 diff --git a/Tests/ParmParse/Make.package b/Tests/ParmParse/Make.package new file mode 100644 index 0000000000..6b4b865e8f --- /dev/null +++ b/Tests/ParmParse/Make.package @@ -0,0 +1 @@ +CEXE_sources += main.cpp diff --git a/Tests/ParmParse/inputs b/Tests/ParmParse/inputs new file mode 100644 index 0000000000..b053026d87 --- /dev/null +++ b/Tests/ParmParse/inputs @@ -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 diff --git a/Tests/ParmParse/main.cpp b/Tests/ParmParse/main.cpp new file mode 100644 index 0000000000..cc8aa2261e --- /dev/null +++ b/Tests/ParmParse/main.cpp @@ -0,0 +1,129 @@ +#include +#include +#include +#include +#include + +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 f; + pp.queryarr("f", f); + AMREX_ALWAYS_ASSERT(f[0] == 7 && f[1] == 99 && f[2] == 11); + + std::vector 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 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& e) { + // Runtime error as expected + amrex::ignore_unused(e); + } + { + 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 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(); +}