From d34f64f05b8d3543daeb20be0aef8810493b66de Mon Sep 17 00:00:00 2001 From: Alexander Sinn <64009254+AlexanderSinn@users.noreply.github.com> Date: Thu, 12 Sep 2024 18:37:23 +0200 Subject: [PATCH 1/5] Add std::setfill to PrintMemStats (#4147) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary Set the fill in TinyProfiler::PrintMemStats to `' '` instead of `'0'`. ## Additional background This PR fixes the output looking like this: ``` Device Memory Usage: --------------------------------------------------------------------------------- Name00000000000000000000000000000000000000000000Nalloc00Nfree0000AvgMem0000MaxMem --------------------------------------------------------------------------------- The_Arena::Initialize()00000000000000000000000000000010000001001259 KiB000029 GiB MultiLaser::InitData()000000000000000000000000000000030000003002948 MiB002948 MiB Fields::AllocData()000000000000000000000000000000000010000001002820 MiB002821 MiB PlasmaParticleContainer::InitParticles()000000000000170000017001597 MiB001727 MiB MultiLaser::AdvanceSliceMG()000000000000000000000000400000040001090 MiB001110 MiB Hipace::ExplicitMGSolveBxBy()00000000000000000000000400000040000921 MiB000939 MiB FFTPoissonSolverDirichletFast::define()0000000000000070000007000639 MiB000640 MiB ResizeRandomSeed000000000000000000000000000000000000010000001000040 MiB000040 MiB BeamParticleContainer::InSituComputeDiags()000000010000001000002115 B004968 KiB MultiLaser::InSituComputeDiags()00000000000000000010000001000007572 B001728 KiB hpmg::MultiGrid::solve2()000000000000000000000000029590002959000045 KiB000432 KiB hpmg::MultiGrid::solve1()000000000000000000000000021990002199000030 KiB000432 KiB shiftSlippedParticles()00000000000000000000000000001620000162000011 B000108 KiB sortBeamParticlesByBox()0000000000000000000000000000040000004000078 KiB000094 KiB BeamParticleContainer::InitBeamFixedWeight3D()000000010000001000078 KiB000078 KiB BeamParticleContainer::resize()000000000000000000003380000338000014 KiB000054 KiB main()0000000000000000000000000000000000000000000000090000009001087 B001088 B Hipace::InitData()0000000000000000000000000000000000030000003000143 B000144 B Fields::Copy()00000000000000000000000000000000000000010000001000031 B000032 B DepositCurrent_PlasmaParticleContainer()000000000010010001001000000 B000016 B --------------------------------------------------------------------------------- Managed Memory Usage: ---------------------------------------------------------------- Name00000000000000000000000000000Nalloc00Nfree00AvgMem0000MaxMem ---------------------------------------------------------------- The_Managed_Arena::Initialize()0000000100000010010 B008192 KiB ---------------------------------------------------------------- Pinned Memory Usage: ------------------------------------------------------------------------------ Name00000000000000000000000000000000000000000Nalloc00Nfree0000AvgMem0000MaxMem ------------------------------------------------------------------------------ Diagnostic::ResizeFDiagFAB()000000000000000000000020000002001183 MiB001187 MiB The_Pinned_Arena::Initialize()0000000000000000000010000001000781 B008192 KiB OpenPMDWriter::InitBeamData()00000000000000000000080000008000576 KiB000625 KiB Hipace::InitData()0000000000000000000000000000000190000019000015 KiB000015 KiB main()0000000000000000000000000000000000000000000670000067001087 B001120 B Hipace::ExplicitMGSolveBxBy()00000000000000000000010000001000753 B000768 B MultiLaser::AdvanceSliceMG()000000000000000000000010000001000754 B000768 B BeamParticleContainer::InSituComputeDiags()000010000001000000000 B000192 B MultiLaser::InSituComputeDiags()00000000000000010000001000000000 B000064 B Fields::Copy()00000000000000000000000000000000000010000001000031 B000032 B PlasmaParticleContainer::InitParticles()000000000010000001000000 B000016 B hpmg::MultiGrid::solve1()000000000000000000000021990002199000001 B000016 B hpmg::MultiGrid::solve2()000000000000000000000029590002959000002 B000016 B shiftSlippedParticles()00000000000000000000000001620000162000000 B000016 B ------------------------------------------------------------------------------ ``` --- Src/Base/AMReX_TinyProfiler.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Src/Base/AMReX_TinyProfiler.cpp b/Src/Base/AMReX_TinyProfiler.cpp index 22bcaa6173..32b35bf770 100644 --- a/Src/Base/AMReX_TinyProfiler.cpp +++ b/Src/Base/AMReX_TinyProfiler.cpp @@ -881,6 +881,7 @@ TinyProfiler::PrintMemStats (std::map& memstats, if (allstatsstr.size() == 1 || !os) { return; } IOFormatSaver iofmtsaver(*os); + *os << std::setfill(' '); int lenhline = 0; for (auto i : maxlen) { From 028638564f7be0694b9898f8d4088cdbf9a6f9f5 Mon Sep 17 00:00:00 2001 From: Zhi Chen <62574124+zhichen3@users.noreply.github.com> Date: Thu, 12 Sep 2024 17:41:36 -0400 Subject: [PATCH 2/5] add geometric terms for spherical 2D support. (#4141) ## Summary This adds the appropriate geometric terms (area and volume) for spherical 2D geometry. We assume dx[0]=dr, and dx[1]=d$`\theta`$. ## Additional background This intended to deal with issue #3670 --- Src/Base/AMReX_CoordSys.cpp | 78 ++++++++++++++++++++++++++++++++++++- Src/Base/AMReX_Geometry.H | 15 ++++++- 2 files changed, 90 insertions(+), 3 deletions(-) diff --git a/Src/Base/AMReX_CoordSys.cpp b/Src/Base/AMReX_CoordSys.cpp index 757af532cc..a958d33a5b 100644 --- a/Src/Base/AMReX_CoordSys.cpp +++ b/Src/Base/AMReX_CoordSys.cpp @@ -164,7 +164,7 @@ CoordSys::UpperIndex(const Real* point) const noexcept IntVect ix; for (int k = 0; k < AMREX_SPACEDIM; k++) { - ix[k] = (int) ((point[k]-offset[k])/dx[k]); + ix[k] = (int) ((point[k]-offset[k])/dx[k]) + 1; } return ix; } @@ -330,6 +330,8 @@ CoordSys::GetEdgeVolCoord (Vector& vc, GetEdgeLoc(vc,region,dir); // // In R direction of RZ, vol coord = (r^2)/2 + // In R direction of SPHERICAL, vol coord = (r^3)/3 + // In theta direction of SPHERICAL, vol coord = -cos(theta) // #if (AMREX_SPACEDIM == 2) if (dir == 0 && c_sys == RZ) @@ -342,6 +344,29 @@ CoordSys::GetEdgeVolCoord (Vector& vc, vc[i] = 0.5_rt*r*r; } } + else if (c_sys == SPHERICAL) + { + if (dir == 0) + { + int len = static_cast(vc.size()); + AMREX_PRAGMA_SIMD + for (int i = 0; i < len; i++) + { + Real r = vc[i]; + vc[i] = r*r*r/3.0_rt; + } + } + else + { + int len = static_cast(vc.size()); + AMREX_PRAGMA_SIMD + for (int i = 0; i < len; i++) + { + Real theta = vc[i]; + vc[i] = -std::cos(theta); + } + } + } #elif (AMREX_SPACEDIM == 1) if (c_sys == SPHERICAL) { @@ -365,8 +390,11 @@ CoordSys::GetCellVolCoord (Vector& vc, // are identical to physical distance from axis. // GetCellLoc(vc,region,dir); + // - // In R direction of RZ, vol coord = (r^2)/2. + // In R direction of RZ, vol coord = (r^2)/2 + // In R direction of SPHERICAL, vol coord = (r^3)/3 + // In theta direction of SPHERICAL, vol coord = -cos(theta) // #if (AMREX_SPACEDIM == 2) if (dir == 0 && c_sys == RZ) @@ -379,6 +407,29 @@ CoordSys::GetCellVolCoord (Vector& vc, vc[i] = 0.5_rt*r*r; } } + else if (c_sys == SPHERICAL) + { + if (dir == 0) + { + int len = static_cast(vc.size()); + AMREX_PRAGMA_SIMD + for (int i = 0; i < len; i++) + { + Real r = vc[i]; + vc[i] = r*r*r/3.0_rt; + } + } + else + { + int len = static_cast(vc.size()); + AMREX_PRAGMA_SIMD + for (int i = 0; i < len; i++) + { + Real theta = vc[i]; + vc[i] = -std::cos(theta); + } + } + } #elif (AMREX_SPACEDIM == 1) if (c_sys == SPHERICAL) { int len = static_cast(vc.size()); @@ -462,6 +513,9 @@ CoordSys::Volume (const Real xlo[AMREX_SPACEDIM], #if (AMREX_SPACEDIM==2) case RZ: return static_cast(0.5*TWOPI)*(xhi[1]-xlo[1])*(xhi[0]*xhi[0]-xlo[0]*xlo[0]); + case SPHERICAL: + return static_cast(TWOPI/3.)*(std::cos(xlo[1])-std::cos(xhi[1])) * + (xhi[0]-xlo[0])*(xhi[0]*xhi[0]+xhi[0]*xlo[0]+xlo[0]*xlo[0]); #endif default: AMREX_ASSERT(0); @@ -496,6 +550,16 @@ CoordSys::AreaLo (const IntVect& point, int dir) const noexcept // NOLINT(readab AMREX_ASSERT(0); } return 0._rt; // to silent compiler warning + case SPHERICAL: + LoNode(point,xlo); + switch (dir) + { + case 0: return Real(TWOPI)*xlo[0]*xlo[0]*(std::cos(xlo[1]) - std::cos(xlo[1]+dx[1])); + case 1: return (xlo[0]+xlo[0]+dx[0])*dx[0]*std::sin(xlo[1])*static_cast(0.5*TWOPI); + default: + AMREX_ASSERT(0); + } + return 0._rt; // to silent compiler warning default: AMREX_ASSERT(0); } @@ -540,6 +604,16 @@ CoordSys::AreaHi (const IntVect& point, int dir) const noexcept // NOLINT(readab AMREX_ASSERT(0); } return 0._rt; // to silent compiler warning + case SPHERICAL: + HiNode(point,xhi); + switch (dir) + { + case 0: return Real(TWOPI)*xhi[0]*xhi[0]*(std::cos(xhi[1]-dx[1]) - std::cos(xhi[1])); + case 1: return (xhi[0]+xhi[0]-dx[0])*dx[0]*std::sin(xhi[1])*static_cast(0.5*TWOPI); + default: + AMREX_ASSERT(0); + } + return 0._rt; // to silent compiler warning default: AMREX_ASSERT(0); } diff --git a/Src/Base/AMReX_Geometry.H b/Src/Base/AMReX_Geometry.H index 6231fbd1f9..1b7bae1a7d 100644 --- a/Src/Base/AMReX_Geometry.H +++ b/Src/Base/AMReX_Geometry.H @@ -264,7 +264,7 @@ public: vol = dx[0] * dx[1]; } - else { + else if (coord == CoordSys::RZ) { // Cylindrical Real r_l = geomdata.ProbLo()[0] + static_cast(point[0]) * dx[0]; @@ -273,6 +273,19 @@ public: constexpr Real pi = Real(3.1415926535897932); vol = pi * (r_l + r_r) * dx[0] * dx[1]; } + else { + // Spherical + + Real r_l = geomdata.ProbLo()[0] + static_cast(point[0]) * dx[0]; + Real r_r = geomdata.ProbLo()[0] + static_cast(point[0]+1) * dx[0]; + + Real theta_l = geomdata.ProbLo()[1] + static_cast(point[1]) * dx[1]; + Real theta_r = geomdata.ProbLo()[1] + static_cast(point[1]+1) * dx[1]; + + constexpr Real twoThirdsPi = static_cast(2.0 * 3.1415926535897932 / 3.0); + vol = twoThirdsPi * (std::cos(theta_l) - std::cos(theta_r)) * dx[0] * + (r_r*r_r + r_r*r_l + r_l*r_l); + } #else From dfe17478d5037483914b09a6ebc3375e33603bd0 Mon Sep 17 00:00:00 2001 From: Weiqun Zhang Date: Sun, 15 Sep 2024 12:13:21 -0500 Subject: [PATCH 3/5] Add ParmParse features for WarpX (#4149) * Add `queryIntAsDouble` and `queryarrIntAsDouble`. * Make recursion detection more robust for WarpX useage. * Add a test. --- .github/workflows/gcc.yml | 6 +- .github/workflows/hip.yml | 6 +- .github/workflows/intel.yml | 10 +-- .github/workflows/smoke.yml | 2 +- Src/Base/AMReX_ParmParse.H | 64 ++++++++++++++++ Src/Base/AMReX_ParmParse.cpp | 20 ++++- Tests/CMakeLists.txt | 2 +- Tests/ParmParse/CMakeLists.txt | 9 +++ Tests/ParmParse/GNUmakefile | 24 ++++++ Tests/ParmParse/Make.package | 1 + Tests/ParmParse/inputs | 62 ++++++++++++++++ Tests/ParmParse/main.cpp | 129 +++++++++++++++++++++++++++++++++ 12 files changed, 319 insertions(+), 16 deletions(-) create mode 100644 Tests/ParmParse/CMakeLists.txt create mode 100644 Tests/ParmParse/GNUmakefile create mode 100644 Tests/ParmParse/Make.package create mode 100644 Tests/ParmParse/inputs create mode 100644 Tests/ParmParse/main.cpp 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(); +} From 97fcea36cbb4c0c69ff002a34ffa6520e858b6f7 Mon Sep 17 00:00:00 2001 From: Weiqun Zhang Date: Mon, 16 Sep 2024 21:20:16 -0500 Subject: [PATCH 4/5] ParmParse::queryAsDouble: Support bool and std::optional (#4152) This is needed in WarpX. --- Src/Base/AMReX_ParmParse.H | 103 ++++++++++++++++++++++++++++--------- Tests/ParmParse/inputs | 2 + Tests/ParmParse/main.cpp | 14 +++++ 3 files changed, 94 insertions(+), 25 deletions(-) diff --git a/Src/Base/AMReX_ParmParse.H b/Src/Base/AMReX_ParmParse.H index caa4697cd5..a29ac163d6 100644 --- a/Src/Base/AMReX_ParmParse.H +++ b/Src/Base/AMReX_ParmParse.H @@ -11,10 +11,11 @@ #include #include -#include +#include #include #include #include +#include #include namespace amrex { @@ -27,6 +28,32 @@ class IntVectND; using IntVect = IntVectND; class RealVect; +namespace ppdetail { + template + struct ArithmeticOptional_TT : std::false_type {}; + + template + struct ArithmeticOptional_TT>> + : std::true_type + { + using value_type = T; + }; + + template + struct ArithmeticOptional_TT, + std::enable_if_t>> + : std::true_type + { + using value_type = T; + }; + + template + inline constexpr bool IsArithmeticOptional_v = ArithmeticOptional_TT::value; + + template + using underlying_type_t = typename ArithmeticOptional_TT::value_type; +} + // // ParmParse class implements a simple database for the storage and // retrieval of command-line and input-file arguments. The entries are @@ -1428,49 +1455,71 @@ 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> + /** + * \brief Query T with Parser, but treat the number as double precision + * during parsing. + * + * The final result is cast to T. It may result in a runtime error if + * the conversion is not safe. T is either arithmetic type or + * std::optional of arithmetic type. + */ + template , int> = 0> int queryAsDouble (const char* name, T& ref) const { + using value_type = ppdetail::underlying_type_t; 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"); + auto vref = static_cast(dref); + if constexpr (!std::is_same_v) { + if (static_cast(vref) != dref) { + amrex::Abort("ParmParse:: queryAsDouble is not safe"); + } } + ref = vref; } 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> + /** + * \brief Query T array with Parser, but treat the number as double + * precision during parsing. + * + * The final result is cast to T's. It may result in a runtime error if + * the conversion is not safe. T is either arithmetic type or + * std::optional of arithmetic type. + */ + template , int> = 0> int queryarrAsDouble (const char* name, int nvals, T* ref) const { + using value_type = ppdetail::underlying_type_t; 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"); + auto vref = static_cast(dref[i]); + if constexpr (!std::is_same_v) { + if (static_cast(vref) != dref[i]) { + amrex::Abort("ParmParse:: queryarrAsDouble is not safe"); + } } + ref[i] = vref; } } 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> + /** + * \brief Get T with Parser, but treat the number as double precision + * during parsing. + * + * The final result is cast to T. It may result in a runtime error if + * the conversion is not safe. T is either arithmetic type or + * std::optional of arithmetic type. + */ + template , int> = 0> void getAsDouble (const char* name, T& ref) const { int exist = this->queryAsDouble(name, ref); @@ -1479,11 +1528,15 @@ public: } } - //! \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> + /** + * \brief Get T array with Parser, but treat the number as double + * precision during parsing. + * + * The final result is cast to T's. It may result in a runtime error if + * the conversion is not safe. T is either arithmetic type or + * std::optional of arithmetic type. + */ + template , int> = 0> void getarrAsDouble (const char* name, int nvals, T* ref) const { int exist = this->queryarrAsDouble(name, nvals, ref); diff --git a/Tests/ParmParse/inputs b/Tests/ParmParse/inputs index b053026d87..ab876ad9ef 100644 --- a/Tests/ParmParse/inputs +++ b/Tests/ParmParse/inputs @@ -60,3 +60,5 @@ my_constants.dx = 6.25e-7 my_constants.nx = lx/dx n_cell = nx nx nx ny = nx + +do_this = 1 diff --git a/Tests/ParmParse/main.cpp b/Tests/ParmParse/main.cpp index cc8aa2261e..68612e82ea 100644 --- a/Tests/ParmParse/main.cpp +++ b/Tests/ParmParse/main.cpp @@ -122,6 +122,20 @@ int main(int argc, char* argv[]) pp.queryarrAsDouble("n_cell", 3, n_cell.data()); AMREX_ALWAYS_ASSERT(n_cell[0] == 64 && n_cell[1] == 64 && n_cell[2] == 64); } + { + ParmParse pp; + bool b_do_this = false; + pp.queryAsDouble("do_this", b_do_this); + AMREX_ALWAYS_ASSERT(b_do_this); + + std::optional o_do_this; + pp.queryAsDouble("do_this", o_do_this); + AMREX_ALWAYS_ASSERT(o_do_this.has_value() && o_do_this.value()); + + std::optional o_do_that; + pp.queryAsDouble("do_that", o_do_that); + AMREX_ALWAYS_ASSERT(!o_do_that.has_value()); + } { amrex::Print() << "SUCCESS\n"; } From a0ed390edbecd117b4c5576a1a05313326d657d0 Mon Sep 17 00:00:00 2001 From: Weiqun Zhang Date: Tue, 17 Sep 2024 12:30:12 -0500 Subject: [PATCH 5/5] ParmParse::queryAsDouble: Fix bug in #4152 & #4149 (#4154) std::round should only be used when the value type is an integer, not a floating point number. --- Src/Base/AMReX_ParmParse.H | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/Src/Base/AMReX_ParmParse.H b/Src/Base/AMReX_ParmParse.H index a29ac163d6..dc4fa07fb4 100644 --- a/Src/Base/AMReX_ParmParse.H +++ b/Src/Base/AMReX_ParmParse.H @@ -1470,9 +1470,11 @@ public: double dref; int exist = queryWithParser(name, dref); if (exist) { - dref = std::round(dref); + if (std::is_integral_v) { + dref = std::round(dref); + } auto vref = static_cast(dref); - if constexpr (!std::is_same_v) { + if constexpr (std::is_integral_v && !std::is_same_v) { if (static_cast(vref) != dref) { amrex::Abort("ParmParse:: queryAsDouble is not safe"); } @@ -1498,9 +1500,11 @@ public: int exist = queryarrWithParser(name, nvals, dref.data()); if (exist) { for (int i = 0; i < nvals; ++i) { - dref[i] = std::round(dref[i]); + if (std::is_integral_v) { + dref[i] = std::round(dref[i]); + } auto vref = static_cast(dref[i]); - if constexpr (!std::is_same_v) { + if constexpr (std::is_integral_v && !std::is_same_v) { if (static_cast(vref) != dref[i]) { amrex::Abort("ParmParse:: queryarrAsDouble is not safe"); }