diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 9e1cbcfad76fe..bc8ee82056ec1 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -21,7 +21,7 @@ jobs: - name: Checkout code uses: actions/checkout@v2 with: - fetch-depth: 0 + submodules: 'true' - name: Set system to non-interactive mode run: export DEBIAN_FRONTEND=noninteractive - name: install dependencies @@ -29,6 +29,32 @@ jobs: pip install sphinx pip install sphinx-rtd-theme pip install sphinx-multiversion + sudo apt install -y --force-yes -qq build-essential cmake + - name: generate csv files + run: | + echo "Repo = ${GITHUB_REPOSITORY}" + mkdir -p build && cd build + cmake -DPARTHENON_DISABLE_HDF5=ON \ + -DPARTHENON_DISABLE_MPI=ON \ + -DREGRESSION_GOLD_STANDARD_SYNC=OFF \ + -DBUILD_TESTING=OFF \ + .. + make -j4 + make param-docs + cd .. + - name: Check generated files for this branch are up to date + run: | + if git diff --exit-code -- doc/sphinx/src/generated/; then + echo "Generated doc files up to date." + else + echo "Generated doc files not up to date. Run make param-docs and commit changes." + exit 1 + fi + - name: Get all git references, needed for sphinx multiversion + run: | + git fetch --prune --unshallow --tags + git branch -a + git tag --list - name: build docs run: | echo "Repo = ${GITHUB_REPOSITORY}" diff --git a/.gitmodules b/.gitmodules index bceb2843cd199..997be39169881 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,3 +4,6 @@ [submodule "external/kokkos"] path = external/Kokkos url = https://github.com/kokkos/kokkos.git +[submodule "external/tomlplusplus"] + path = external/tomlplusplus + url = https://github.com/marzer/tomlplusplus.git diff --git a/CHANGELOG.md b/CHANGELOG.md index ba2d2361e2f60..6e012fff0a482 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## Current develop ### Added (new features/APIs/variables/...) +- [[PR 1283]](https://github.com/parthenon-hpc-lab/parthenon/pull/1283) Ability to automatically document ParameterInputs - [[PR 1258]](https://github.com/parthenon-hpc-lab/parthenon/pull/1258) Add "corehdf" version of hdf5 output that dumps everything - [[PR 1162]](https://github.com/parthenon-hpc-lab/parthenon/pull/1162) Add dev container (e.g., GitHub Codepsacer or VSCode) diff --git a/CMakeLists.txt b/CMakeLists.txt index b30e375863741..c3e85b5499856 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -268,6 +268,7 @@ if (NOT TARGET Kokkos::kokkos) endif() endif() endif() +include_directories("external/tomlplusplus") # After we have imported Kokkos we can now report/check our config as Kokkos_ENABLE_XXX # is also availalbe when imported. @@ -389,6 +390,7 @@ if (PARTHENON_ENABLE_UNIT_TESTS OR PARTHENON_ENABLE_INTEGRATION_TESTS OR PARTHEN add_subdirectory(tst) endif() + if (PARTHENON_ENABLE_ASCENT) if (PARTHENON_USE_SYSTEM_PACKAGES) find_package(Ascent REQUIRED) @@ -401,6 +403,9 @@ endif() include(GNUInstallDirs) set(CMAKE_INSTALL_INCLUDEDIR "${CMAKE_INSTALL_INCLUDEDIR}/parthenon") +set(DOC_GEN_PATH "${CMAKE_SOURCE_DIR}/doc/sphinx/src/generated" CACHE STRING + "Path to save generated data for docs.") + add_subdirectory(src) add_subdirectory(example) add_subdirectory(benchmarks) @@ -410,6 +415,13 @@ include(cmake/CheckCopyright.cmake) configure_file("${PROJECT_SOURCE_DIR}/cmake/parthenonConfig.cmake.in" "${PROJECT_BINARY_DIR}/cmake/parthenonConfig.cmake" @ONLY) +if (NOT PARTHENON_DISABLE_EXAMPLES) + add_custom_target(param-docs + DEPENDS + burgers-csv + diffusion-csv) +endif() + # Currently Ctest/Cmake doesn't ensure that tests are not stale # before running them. # diff --git a/benchmarks/burgers/CMakeLists.txt b/benchmarks/burgers/CMakeLists.txt index 5d14f736dfae1..3d023b5ec9038 100644 --- a/benchmarks/burgers/CMakeLists.txt +++ b/benchmarks/burgers/CMakeLists.txt @@ -25,4 +25,21 @@ if( "burgers-benchmark" IN_LIST DRIVER_LIST OR NOT PARTHENON_DISABLE_EXAMPLES) ) target_link_libraries(burgers-benchmark PRIVATE Parthenon::parthenon) lint_target(burgers-benchmark) + + # JMM: Only run burgers in 2D so startup time is reasonable because + # -p flag goes through pgen + add_custom_command( + OUTPUT ${DOC_GEN_PATH}/burgers-table.csv + COMMAND ${CMAKE_COMMAND} -E env + bash -c + "${CMAKE_CURRENT_BINARY_DIR}/burgers-benchmark -i ${CMAKE_CURRENT_LIST_DIR}/burgers.pin parthenon/mesh/nx3=1 parthenon/meshblock/nx3=1 -p '^(?!.*parthenon/).*' > ${DOC_GEN_PATH}/burgers-table.csv" + DEPENDS burgers-benchmark burgers.pin + VERBATIM + ) + + add_custom_target(burgers-csv + DEPENDS + ${DOC_GEN_PATH}/burgers-table.csv + ) + endif() diff --git a/doc/sphinx/_static/custom.css b/doc/sphinx/_static/custom.css new file mode 100644 index 0000000000000..320ea4b7cc8e3 --- /dev/null +++ b/doc/sphinx/_static/custom.css @@ -0,0 +1,18 @@ +/* Enable word wrap in table cells */ +/* JMM: This is preferable IMO but it messed up the word wrapping for + * our hand-written tables, so I removed it and enabled wrapping only + * for csv-imported tables +/* +.rst-content table.docutils td { + white-space: normal !important; + word-wrap: break-word; + word-break: break-word; +} +*/ +/* Only apply word wrapping to CSV-imported tables with .csv-wrap class */ +.rst-content table.csv-wrap td { + white-space: normal !important; + word-wrap: break-word; + word-break: break-word; +} + diff --git a/doc/sphinx/conf.py b/doc/sphinx/conf.py index dc548e4f3378e..d6672276ca28c 100644 --- a/doc/sphinx/conf.py +++ b/doc/sphinx/conf.py @@ -50,5 +50,10 @@ # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ["_static"] + +def setup(app): + app.add_css_file("custom.css") # Sphinx >= 1.8 + + # configuration for sphinx_multiversion smv_remote_whitelist = r"^(origin)$" diff --git a/doc/sphinx/src/0_input_parameters_ref.rst b/doc/sphinx/src/0_input_parameters_ref.rst new file mode 100644 index 0000000000000..92d1f694af15b --- /dev/null +++ b/doc/sphinx/src/0_input_parameters_ref.rst @@ -0,0 +1,48 @@ +.. _inputref: + +Input Parameters Reference +=========================== + +This reference is automatically generated with Parthenon's +``ParameterInput`` class. To generate it, pass the ``-p`` flag into a +parthenon-based executable (in addition to the other flags you would +normally pass), and optionally a ``regex`` to specify which blocks +you'd like to output. Parthenon will print a valid CSV file to the +terminal. + +Every call to ``ParameterInput::Get*`` optionally takes a "docustring" +as a final argument, which is the details column listed here. Default +values are recorded if they are available. + +In Parthenon +-------------- + +An incomplete list of all input parameters provided by Parthenon is tabulated below + +.. csv-table:: Parthenon input parameters + :file: generated/diffusion-parth-table.csv + :header-rows: 1 + :widths: 20 20 10 20 30 + :class: csv-wrap + +In the diffusion example +---------------------------- + +The non-parthenon input parameters used in the diffusion example are tabulated below + +.. csv-table:: Parthenon input parameters + :file: generated/diffusion-table.csv + :header-rows: 1 + :widths: 20 20 10 20 30 + :class: csv-wrap + +In the Burgers benchmark, Parthenon-VIBE +------------------------------------------ + +The non-parthenon input parameters used in Parthenon-VIBE are tabulated below + +.. csv-table:: Parthenon input parameters + :file: generated/burgers-table.csv + :header-rows: 1 + :widths: 20 20 10 20 30 + :class: csv-wrap diff --git a/doc/sphinx/src/generated/burgers-table.csv b/doc/sphinx/src/generated/burgers-table.csv new file mode 100644 index 0000000000000..b2beb47bb673c --- /dev/null +++ b/doc/sphinx/src/generated/burgers-table.csv @@ -0,0 +1,100 @@ +block,parameters,type,default,description +"burgers","cfl","Real","0.8","" +"burgers","kx_fact","Real","1","" +"burgers","ky_fact","Real","1","" +"burgers","kz_fact","Real","1","" +"burgers","num_scalars","int","1","" +"burgers","recon","string","weno5","" +"","","","","" +"parthenon.job","output_params_and_exit","bool","0","output a description of all input parameters accessed and quit" +"parthenon.job","output_params_block_regex","string","(.*)","when outputting input parameters, this selects which input blocks to output; all are output by default" +"parthenon.job","problem_id","string","parthenon","prefix for output files" +"parthenon.job","run_only_analysis","bool","","" +"","","","","" +"parthenon.loadbalancing","balancer","string","default","load balancing strategy; Allowed values: automatic, default, manual" +"parthenon.loadbalancing","interval","int","10","how frequently load balancing is performed if the mesh does not change" +"parthenon.loadbalancing","tolerance","Real","0.5","load balancer tolerance" +"","","","","" +"parthenon.mesh","derefine_count","int","10","number of iterations a block must request derefinement before it is derefined" +"parthenon.mesh","ix1_bc","string","outflow","global mesh boundary condition on inner X1 face" +"parthenon.mesh","ix2_bc","string","outflow","global mesh boundary condition on inner X2 face" +"parthenon.mesh","ix3_bc","string","outflow","global mesh boundary condition on inner X3 face" +"parthenon.mesh","multigrid","bool","0","enable a multigrid mesh" +"parthenon.mesh","nghost","int","2","number of ghost zones on a block" +"parthenon.mesh","num_threads","int","1","number of host threads for infrastructure; unused" +"parthenon.mesh","numlevel","int","1","maximum level of refinement globally" +"parthenon.mesh","nx1","int","","number of cells on base mesh in x1 direction" +"parthenon.mesh","nx2","int","","number of cells on base mesh in x2 direction" +"parthenon.mesh","nx3","int","","number of cells on base mesh in x3 direction" +"parthenon.mesh","ox1_bc","string","outflow","global mesh boundary condition on outer X1 face" +"parthenon.mesh","ox2_bc","string","outflow","global mesh boundary condition on outer X2 face" +"parthenon.mesh","ox3_bc","string","outflow","global mesh boundary condition on outer X3 face" +"parthenon.mesh","packs_per_rank","int","1","number of meshblockpacks per rank, overrides pack_size" +"parthenon.mesh","refinement","string","none","mesh refinement mode; Allowed values: adaptive, none, static" +"parthenon.mesh","refinement_in_one_min_nbufs","int","64","" +"parthenon.mesh","x1max","Real","","maximum x1 value of domain" +"parthenon.mesh","x1min","Real","","minimum x1 value of domain" +"parthenon.mesh","x1rat","Real","1","unused" +"parthenon.mesh","x2max","Real","","maximum x2 value of domain" +"parthenon.mesh","x2min","Real","","minimum x2 value of domain" +"parthenon.mesh","x2rat","Real","1","unused" +"parthenon.mesh","x3max","Real","","maximum x3 value of domain" +"parthenon.mesh","x3min","Real","","minimum x3 value of domain" +"parthenon.mesh","x3rat","Real","1","unused" +"","","","","" +"parthenon.meshblock","nx1","int","parthenon.mesh.nx1","logical size of a meshblock; defaults to the base size of the mesh" +"parthenon.meshblock","nx2","int","parthenon.mesh.nx2","logical size of a meshblock; defaults to the base size of the mesh" +"parthenon.meshblock","nx3","int","parthenon.mesh.nx3","logical size of a meshblock; defaults to the base size of the mesh" +"","","","","" +"parthenon.output0","dn","int","-1","output cadence in cycles" +"parthenon.output0","dt","Real","0.4","output cadence in physical time" +"","","","","" +"parthenon.output1","analysis_output","bool","0","" +"parthenon.output1","data_format","string","%12.5e","" +"parthenon.output1","dn","int","-1","output cadence in cycles" +"parthenon.output1","dt","Real","0.4","output cadence in physical time" +"parthenon.output1","file_number_width","int","5","" +"parthenon.output1","file_type","string","","output type" +"parthenon.output1","ghost_zones","bool","0","whether or not ghost zones are output" +"parthenon.output1","id","string","out1","" +"parthenon.output1","use_final_label","bool","1","final output will use the word final instead of a number for its index" +"","","","","" +"parthenon.refinement0","derefine_tol","Real","0.05","magnitude that triggers de-refinement" +"parthenon.refinement0","field","string","NO FIELD WAS SET","Field to refine on" +"parthenon.refinement0","max_level","int","2","maximum level this refinement criterion will achieve" +"parthenon.refinement0","method","string","PLEASE SPECIFY method","" +"parthenon.refinement0","refine_tol","Real","0.5","magnitude that triggers refinement" +"parthenon.refinement0","vector_i","int","","" +"","","","","" +"parthenon.sparse","alloc_threshold","Real","1e-12","" +"parthenon.sparse","dealloc_count","int","5","" +"parthenon.sparse","dealloc_threshold","Real","1e-14","" +"parthenon.sparse","enable_sparse","bool","1","" +"","","","","" +"parthenon.swarm","ix1_bc","string","periodic","global particle boundary condition on inner X1 face" +"parthenon.swarm","ix2_bc","string","periodic","global particle boundary condition on inner X2 face" +"parthenon.swarm","ix3_bc","string","periodic","global particle boundary condition on inner X3 face" +"parthenon.swarm","ox1_bc","string","periodic","global particle boundary condition on outer X1 face" +"parthenon.swarm","ox2_bc","string","periodic","global particle boundary condition on outer X2 face" +"parthenon.swarm","ox3_bc","string","periodic","global particle boundary condition on outer X3 face" +"","","","","" +"parthenon.time","dt","Real","1.7976931e+308","initial value of time step before constraining via cfl" +"parthenon.time","dt_ceil","Real","1.7976931e+308","maximum allowed timestep" +"parthenon.time","dt_factor","Real","2","maximum relative change in dt per timestep" +"parthenon.time","dt_floor","Real","2.2250739e-308","minimum allowed timestep" +"parthenon.time","dt_force","Real","-1.7976931e+308","if set manually enforces this time step exactly" +"parthenon.time","dt_init","Real","1.7976931e+308","the first time step will be at least as small as dt_init" +"parthenon.time","dt_init_force","bool","0","if set to true, the first time step will be exactly dt_init" +"parthenon.time","dt_max","Real","1.7976931e+308","if timestep is above this level for dt_max_count_max iterations, parthenon throws an error" +"parthenon.time","dt_max_cycle_limit","int","1","number of cycles where dt > dt_max before error" +"parthenon.time","dt_min","Real","2.2250739e-308","if timestep falls below this level for dt_min_count_max iterations, parthenon throws an error" +"parthenon.time","dt_min_cycle_limit","int","10","number of cycles where dt < dt_min before error" +"parthenon.time","integrator","string","rk2","" +"parthenon.time","ncycle","int","0","initial iteration count" +"parthenon.time","ncycle_out","int","1","cadence of outputs to stdout" +"parthenon.time","ncycle_out_mesh","int","0","cadence of outputs describing mesh" +"parthenon.time","nlim","int","-1","maximum number of iterations, only limiting if non-negative" +"parthenon.time","perf_cycle_offset","int","0","don't measure performance for some number of initial cycles" +"parthenon.time","recv_bdry_buf_timeout_sec","Real","-1","" +"parthenon.time","start_time","Real","0","physical time at which to start the simulation" +"parthenon.time","tlim","Real","inf","physical time at which to end the simulation" diff --git a/doc/sphinx/src/generated/diffusion-parth-table.csv b/doc/sphinx/src/generated/diffusion-parth-table.csv new file mode 100644 index 0000000000000..aae49e581d758 --- /dev/null +++ b/doc/sphinx/src/generated/diffusion-parth-table.csv @@ -0,0 +1 @@ +block,parameters,type,default,description diff --git a/doc/sphinx/src/generated/diffusion-table.csv b/doc/sphinx/src/generated/diffusion-table.csv new file mode 100644 index 0000000000000..510e528b4e1da --- /dev/null +++ b/doc/sphinx/src/generated/diffusion-table.csv @@ -0,0 +1,104 @@ +block,parameters,type,default,description +"diffusion","boundary_prolongation","string","Linear","" +"diffusion","cfl","Real","1","" +"diffusion","constant_coefficient","bool","1","" +"diffusion","diagonal_alpha","Real","1","" +"diffusion","dt","Real","1","" +"diffusion","flux_correct","bool","0","" +"diffusion","set_flux_boundary","bool","0","" +"diffusion","solver","string","MG","" +"","","","","" +"diffusion.solver_params","block_interior_prolongation","string","MetadataDefault","" +"diffusion.solver_params","do_FAS","bool","1","" +"diffusion.solver_params","max_coarsenings","int","2147483647","" +"diffusion.solver_params","max_iterations","int","1000","" +"diffusion.solver_params","prolongation","string","OldLinear","" +"diffusion.solver_params","residual_tolerance","Real","1e-12","" +"diffusion.solver_params","smoother","string","SRJ2","" +"diffusion.solver_params","two_by_two_diagonal","bool","0","" +"","","","","" +"diffusion","t0","Real","0.001","" +"diffusion","x0","Real","0","" +"diffusion","y0","Real","0","" +"diffusion","z0","Real","0","" +"","","","","" +"parthenon.job","output_params_and_exit","bool","0","output a description of all input parameters accessed and quit" +"parthenon.job","output_params_block_regex","string","(.*)","when outputting input parameters, this selects which input blocks to output; all are output by default" +"parthenon.job","problem_id","string","parthenon","prefix for output files" +"parthenon.job","run_only_analysis","bool","","" +"","","","","" +"parthenon.loadbalancing","balancer","string","default","load balancing strategy; Allowed values: automatic, default, manual" +"parthenon.loadbalancing","interval","int","10","how frequently load balancing is performed if the mesh does not change" +"parthenon.loadbalancing","tolerance","Real","0.5","load balancer tolerance" +"","","","","" +"parthenon.mesh","derefine_count","int","10","number of iterations a block must request derefinement before it is derefined" +"parthenon.mesh","ix1_bc","string","outflow","global mesh boundary condition on inner X1 face" +"parthenon.mesh","ix2_bc","string","outflow","global mesh boundary condition on inner X2 face" +"parthenon.mesh","ix3_bc","string","outflow","global mesh boundary condition on inner X3 face" +"parthenon.mesh","multigrid","bool","0","enable a multigrid mesh" +"parthenon.mesh","nghost","int","2","number of ghost zones on a block" +"parthenon.mesh","num_threads","int","1","number of host threads for infrastructure; unused" +"parthenon.mesh","nx1","int","","number of cells on base mesh in x1 direction" +"parthenon.mesh","nx2","int","","number of cells on base mesh in x2 direction" +"parthenon.mesh","nx3","int","","number of cells on base mesh in x3 direction" +"parthenon.mesh","ox1_bc","string","outflow","global mesh boundary condition on outer X1 face" +"parthenon.mesh","ox2_bc","string","outflow","global mesh boundary condition on outer X2 face" +"parthenon.mesh","ox3_bc","string","outflow","global mesh boundary condition on outer X3 face" +"parthenon.mesh","packs_per_rank","int","1","number of meshblockpacks per rank, overrides pack_size" +"parthenon.mesh","refinement","string","none","mesh refinement mode; Allowed values: adaptive, none, static" +"parthenon.mesh","refinement_in_one_min_nbufs","int","64","" +"parthenon.mesh","x1max","Real","","maximum x1 value of domain" +"parthenon.mesh","x1min","Real","","minimum x1 value of domain" +"parthenon.mesh","x1rat","Real","1","unused" +"parthenon.mesh","x2max","Real","","maximum x2 value of domain" +"parthenon.mesh","x2min","Real","","minimum x2 value of domain" +"parthenon.mesh","x2rat","Real","1","unused" +"parthenon.mesh","x3max","Real","","maximum x3 value of domain" +"parthenon.mesh","x3min","Real","","minimum x3 value of domain" +"parthenon.mesh","x3rat","Real","1","unused" +"","","","","" +"parthenon.meshblock","nx1","int","parthenon.mesh.nx1","logical size of a meshblock; defaults to the base size of the mesh" +"parthenon.meshblock","nx2","int","parthenon.mesh.nx2","logical size of a meshblock; defaults to the base size of the mesh" +"parthenon.meshblock","nx3","int","parthenon.mesh.nx3","logical size of a meshblock; defaults to the base size of the mesh" +"","","","","" +"parthenon.output0","dn","int","-1","output cadence in cycles" +"parthenon.output0","dt","int","0.02","output cadence in physical time" +"","","","","" +"parthenon.sparse","alloc_threshold","Real","1e-12","" +"parthenon.sparse","dealloc_count","int","5","" +"parthenon.sparse","dealloc_threshold","Real","1e-14","" +"parthenon.sparse","enable_sparse","bool","1","" +"","","","","" +"parthenon.static_refinement0","level","int","","" +"parthenon.static_refinement0","x1max","Real","","" +"parthenon.static_refinement0","x1min","Real","","" +"parthenon.static_refinement0","x2max","Real","","" +"parthenon.static_refinement0","x2min","Real","","" +"","","","","" +"parthenon.swarm","ix1_bc","string","outflow","global particle boundary condition on inner X1 face" +"parthenon.swarm","ix2_bc","string","outflow","global particle boundary condition on inner X2 face" +"parthenon.swarm","ix3_bc","string","periodic","global particle boundary condition on inner X3 face" +"parthenon.swarm","ox1_bc","string","outflow","global particle boundary condition on outer X1 face" +"parthenon.swarm","ox2_bc","string","outflow","global particle boundary condition on outer X2 face" +"parthenon.swarm","ox3_bc","string","periodic","global particle boundary condition on outer X3 face" +"","","","","" +"parthenon.time","dt","Real","1.7976931e+308","initial value of time step before constraining via cfl" +"parthenon.time","dt_ceil","Real","1.7976931e+308","maximum allowed timestep" +"parthenon.time","dt_factor","Real","2","maximum relative change in dt per timestep" +"parthenon.time","dt_floor","Real","2.2250739e-308","minimum allowed timestep" +"parthenon.time","dt_force","Real","-1.7976931e+308","if set manually enforces this time step exactly" +"parthenon.time","dt_init","Real","1.7976931e+308","the first time step will be at least as small as dt_init" +"parthenon.time","dt_init_force","bool","0","if set to true, the first time step will be exactly dt_init" +"parthenon.time","dt_max","Real","1.7976931e+308","if timestep is above this level for dt_max_count_max iterations, parthenon throws an error" +"parthenon.time","dt_max_cycle_limit","int","1","number of cycles where dt > dt_max before error" +"parthenon.time","dt_min","Real","2.2250739e-308","if timestep falls below this level for dt_min_count_max iterations, parthenon throws an error" +"parthenon.time","dt_min_cycle_limit","int","10","number of cycles where dt < dt_min before error" +"parthenon.time","integrator","string","rk2","" +"parthenon.time","ncycle","int","0","initial iteration count" +"parthenon.time","ncycle_out","int","1","cadence of outputs to stdout" +"parthenon.time","ncycle_out_mesh","int","0","cadence of outputs describing mesh" +"parthenon.time","nlim","int","-1","maximum number of iterations, only limiting if non-negative" +"parthenon.time","perf_cycle_offset","int","0","don't measure performance for some number of initial cycles" +"parthenon.time","recv_bdry_buf_timeout_sec","Real","-1","" +"parthenon.time","start_time","Real","0","physical time at which to start the simulation" +"parthenon.time","tlim","Real","inf","physical time at which to end the simulation" diff --git a/example/advection/advection_package.cpp b/example/advection/advection_package.cpp index 64e4ea30712d5..8d02cab9d8877 100644 --- a/example/advection/advection_package.cpp +++ b/example/advection/advection_package.cpp @@ -118,10 +118,9 @@ std::shared_ptr Initialize(ParameterInput *pin) { // For lambda choose the smaller of the 3 Real lambda = x1; - if ((pin->GetOrAddInteger("parthenon/mesh", "nx2", 1) > 1) && ang_3 != 0.0) - lambda = std::min(lambda, x2); - if ((pin->GetOrAddInteger("parthenon/mesh", "nx3", 1) > 1) && ang_2 != 0.0) - lambda = std::min(lambda, x3); + auto [mesh_size, meshblock_size] = Mesh::GetRegionSizes(pin); + if ((mesh_size.nx(X2DIR) > 1) && ang_3 != 0.0) lambda = std::min(lambda, x2); + if ((mesh_size.nx(X3DIR) > 1) && ang_2 != 0.0) lambda = std::min(lambda, x3); // If cos_a2 or cos_a3 = 0, need to override lambda if (ang_3_vert) lambda = x2; @@ -202,9 +201,9 @@ std::shared_ptr Initialize(ParameterInput *pin) { pin->GetOrAddBoolean("Advection", "test_metadata_none", false); pkg->AddParam("test_metadata_none", test_metadata_none); if (test_metadata_none) { - const int nx1 = pin->GetOrAddInteger("parthenon/meshblock", "nx1", 1); - const int nx2 = pin->GetOrAddInteger("parthenon/meshblock", "nx2", 1); - const int nx3 = pin->GetOrAddInteger("parthenon/meshblock", "nx3", 1); + const int nx1 = meshblock_size.nx(X1DIR); + const int nx2 = meshblock_size.nx(X2DIR); + const int nx3 = meshblock_size.nx(X3DIR); std::vector test_shape = {nx1 + 1, nx2 + 1, nx3 + 1, 3}; m = Metadata({Metadata::OneCopy, Metadata::None}, test_shape); pkg->AddField("metadata_none_var", m); diff --git a/example/advection/parthenon_app_inputs.cpp b/example/advection/parthenon_app_inputs.cpp index 3a8e918c11a73..17e429dcba60f 100644 --- a/example/advection/parthenon_app_inputs.cpp +++ b/example/advection/parthenon_app_inputs.cpp @@ -1,5 +1,5 @@ //======================================================================================== -// (C) (or copyright) 2020-2021. Triad National Security, LLC. All rights reserved. +// (C) (or copyright) 2020-2025. Triad National Security, LLC. All rights reserved. // // This program was produced under U.S. Government contract 89233218CNA000001 for Los // Alamos National Laboratory (LANL), which is operated by Triad National Security, LLC @@ -25,7 +25,6 @@ #include "defs.hpp" #include "interface/variable_pack.hpp" #include "kokkos_abstraction.hpp" -#include "parameter_input.hpp" #include "utils/error_checking.hpp" using namespace parthenon::package::prelude; diff --git a/example/diffusion/CMakeLists.txt b/example/diffusion/CMakeLists.txt index bfd1258a635aa..60859422f3ffb 100644 --- a/example/diffusion/CMakeLists.txt +++ b/example/diffusion/CMakeLists.txt @@ -25,4 +25,28 @@ if( "diffusion-example" IN_LIST DRIVER_LIST OR NOT PARTHENON_DISABLE_EXAMPLES) ) target_link_libraries(diffusion-example PRIVATE Parthenon::parthenon) lint_target(diffusion-example) + + add_custom_command( + OUTPUT ${DOC_GEN_PATH}/diffusion-parth-table.csv + COMMAND ${CMAKE_COMMAND} -E env + bash -c + "${CMAKE_CURRENT_BINARY_DIR}/diffusion-example -i ${CMAKE_CURRENT_LIST_DIR}/parthinput.diffusion parthenon/output0/dt=-1 -p 'parthenon/(.*)' > ${DOC_GEN_PATH}/diffusion-parth-table.csv" + DEPENDS diffusion-example parthinput.diffusion + VERBATIM + ) + add_custom_command( + OUTPUT ${DOC_GEN_PATH}/diffusion-table.csv + COMMAND ${CMAKE_COMMAND} -E env + bash -c + "${CMAKE_CURRENT_BINARY_DIR}/diffusion-example -i ${CMAKE_CURRENT_LIST_DIR}/parthinput.diffusion parthenon/output0/dt=-1 -p '^(?!.*parthenon/).*' > ${DOC_GEN_PATH}/diffusion-table.csv" + DEPENDS diffusion-example parthinput.diffusion + VERBATIM + ) + + add_custom_target(diffusion-csv + DEPENDS + ${DOC_GEN_PATH}/diffusion-parth-table.csv + ${DOC_GEN_PATH}/diffusion-table.csv + ) + endif() diff --git a/example/fine_advection/parthenon_app_inputs.cpp b/example/fine_advection/parthenon_app_inputs.cpp index 8025970f35ca9..10e8b1a65673c 100644 --- a/example/fine_advection/parthenon_app_inputs.cpp +++ b/example/fine_advection/parthenon_app_inputs.cpp @@ -23,7 +23,6 @@ #include "defs.hpp" #include "interface/variable_pack.hpp" #include "kokkos_abstraction.hpp" -#include "parameter_input.hpp" #include "utils/error_checking.hpp" using namespace parthenon::package::prelude; diff --git a/external/tomlplusplus b/external/tomlplusplus new file mode 160000 index 0000000000000..708fff700f36a --- /dev/null +++ b/external/tomlplusplus @@ -0,0 +1 @@ +Subproject commit 708fff700f36ab3c2ab107b984ec9f3b8be5f055 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b5d27532458a6..2e46938e72a3b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -134,6 +134,12 @@ add_library(parthenon driver/multistage.cpp driver/multistage.hpp + inputs/argument_parser.hpp + inputs/inputs_package.cpp + inputs/inputs_package.hpp + inputs/parameter_input.cpp + inputs/parameter_input.hpp + interface/data_collection.cpp interface/data_collection.hpp interface/mesh_data.cpp @@ -303,14 +309,11 @@ add_library(parthenon utils/unique_id.hpp utils/utils.hpp - argument_parser.hpp basic_types.hpp defs.hpp globals.cpp globals.hpp kokkos_abstraction.hpp - parameter_input.cpp - parameter_input.hpp parthenon_array_generic.hpp parthenon_arrays.cpp parthenon_arrays.hpp diff --git a/src/amr_criteria/amr_criteria.cpp b/src/amr_criteria/amr_criteria.cpp index a0625b81e6524..81a5ef95ad1d0 100644 --- a/src/amr_criteria/amr_criteria.cpp +++ b/src/amr_criteria/amr_criteria.cpp @@ -1,5 +1,5 @@ //======================================================================================== -// (C) (or copyright) 2020-2021. Triad National Security, LLC. All rights reserved. +// (C) (or copyright) 2020-2025. Triad National Security, LLC. All rights reserved. // // This program was produced under U.S. Government contract 89233218CNA000001 for Los // Alamos National Laboratory (LANL), which is operated by Triad National Security, LLC @@ -17,16 +17,17 @@ #include #include "amr_criteria/refinement_package.hpp" +#include "inputs/parameter_input.hpp" #include "interface/meshblock_data.hpp" #include "interface/variable.hpp" #include "mesh/mesh.hpp" -#include "parameter_input.hpp" namespace parthenon { AMRCriteria::AMRCriteria(ParameterInput *pin, std::string &block_name) : comp6(0), comp5(0), comp4(0) { - field = pin->GetOrAddString(block_name, "field", "NO FIELD WAS SET"); + field = + pin->GetOrAddString(block_name, "field", "NO FIELD WAS SET", "Field to refine on"); if (field == "NO FIELD WAS SET") { std::cerr << "Error in " << block_name << ": no field set" << std::endl; exit(1); @@ -50,10 +51,15 @@ AMRCriteria::AMRCriteria(ParameterInput *pin, std::string &block_name) "vector_i requires one value, e.g. vector_i = 2"); comp4 = index[0]; } - refine_criteria = pin->GetOrAddReal(block_name, "refine_tol", 0.5); - derefine_criteria = pin->GetOrAddReal(block_name, "derefine_tol", 0.05); - int global_max_level = pin->GetOrAddInteger("parthenon/mesh", "numlevel", 1); - max_level = pin->GetOrAddInteger(block_name, "max_level", global_max_level); + refine_criteria = pin->GetOrAddReal(block_name, "refine_tol", 0.5, + "magnitude that triggers refinement"); + derefine_criteria = pin->GetOrAddReal(block_name, "derefine_tol", 0.05, + "magnitude that triggers de-refinement"); + int global_max_level = pin->GetOrAddInteger("parthenon/mesh", "numlevel", 1, + "maximum level of refinement globally"); + max_level = + pin->GetOrAddInteger(block_name, "max_level", global_max_level, + "maximum level this refinement criterion will achieve"); if (max_level > global_max_level) { std::cerr << "WARNING: max_level in " << block_name << " exceeds numlevel (the global maximum number of levels) set in " diff --git a/src/amr_criteria/refinement_package.cpp b/src/amr_criteria/refinement_package.cpp index 549099f491571..e70d6218b417a 100644 --- a/src/amr_criteria/refinement_package.cpp +++ b/src/amr_criteria/refinement_package.cpp @@ -1,5 +1,5 @@ //======================================================================================== -// (C) (or copyright) 2020-2023. Triad National Security, LLC. All rights reserved. +// (C) (or copyright) 2020-2025. Triad National Security, LLC. All rights reserved. // // This program was produced under U.S. Government contract 89233218CNA000001 for Los // Alamos National Laboratory (LANL), which is operated by Triad National Security, LLC @@ -20,6 +20,7 @@ #include #include "amr_criteria/amr_criteria.hpp" +#include "inputs/parameter_input.hpp" #include "interface/mesh_data.hpp" #include "interface/meshblock_data.hpp" #include "interface/state_descriptor.hpp" @@ -28,7 +29,6 @@ #include "mesh/mesh_refinement.hpp" #include "mesh/meshblock.hpp" #include "pack/make_pack_descriptor.hpp" -#include "parameter_input.hpp" #include "utils/instrument.hpp" namespace parthenon { @@ -37,16 +37,14 @@ namespace Refinement { std::shared_ptr Initialize(ParameterInput *pin) { auto ref = std::make_shared("Refinement"); - int numcrit = 0; - while (true) { - std::string block_name = "parthenon/refinement" + std::to_string(numcrit); - if (!pin->DoesBlockExist(block_name)) { - break; + for (auto pib : pin->Blocks("parthenon")) { + std::string block_name = std::string(pib.first); + if (block_name.compare(0, 10, "refinement") == 0) { + std::string block_path = "parthenon." + block_name; + std::string method = + pin->GetOrAddString(block_path, "method", "PLEASE SPECIFY method"); + ref->amr_criteria.push_back(AMRCriteria::MakeAMRCriteria(method, pin, block_path)); } - std::string method = - pin->GetOrAddString(block_name, "method", "PLEASE SPECIFY method"); - ref->amr_criteria.push_back(AMRCriteria::MakeAMRCriteria(method, pin, block_name)); - numcrit++; } return ref; } diff --git a/src/bvals/bvals.cpp b/src/bvals/bvals.cpp index 37a4d46899169..0b1a774006f94 100644 --- a/src/bvals/bvals.cpp +++ b/src/bvals/bvals.cpp @@ -35,10 +35,10 @@ #include "parthenon_mpi.hpp" #include "globals.hpp" +#include "inputs/parameter_input.hpp" #include "mesh/mesh.hpp" #include "mesh/mesh_refinement.hpp" #include "mesh/meshblock.hpp" -#include "parameter_input.hpp" #include "utils/buffer_utils.hpp" #include "utils/error_checking.hpp" diff --git a/src/driver/driver.cpp b/src/driver/driver.cpp index 9ba7250e05e14..595066d0ee58d 100644 --- a/src/driver/driver.cpp +++ b/src/driver/driver.cpp @@ -1,5 +1,5 @@ //======================================================================================== -// (C) (or copyright) 2020-2024. Triad National Security, LLC. All rights reserved. +// (C) (or copyright) 2020-2025. Triad National Security, LLC. All rights reserved. // // This program was produced under U.S. Government contract 89233218CNA000001 for Los // Alamos National Laboratory (LANL), which is operated by Triad National Security, LLC @@ -24,11 +24,11 @@ #include "bvals/comms/bvals_in_one.hpp" #include "globals.hpp" +#include "inputs/parameter_input.hpp" #include "interface/update.hpp" #include "mesh/mesh.hpp" #include "mesh/meshblock.hpp" #include "outputs/outputs.hpp" -#include "parameter_input.hpp" #include "parthenon_mpi.hpp" #include "utils/utils.hpp" @@ -41,9 +41,9 @@ Kokkos::Timer Driver::timer_cycle; Kokkos::Timer Driver::timer_LBandAMR; void Driver::DumpInputParameters() { - auto archive_parameters = + const auto archive_parameters = pinput->GetOrAddBoolean("parthenon/job", "archive_parameters", false); - auto archive_timestamp = + const auto archive_timestamp = pinput->GetOrAddBoolean("parthenon/job", "archive_timestamp", false); if (archive_parameters && Globals::my_rank == 0) { std::ostringstream ss; @@ -59,7 +59,7 @@ void Driver::DumpInputParameters() { pinput->ParameterDump(pars); pars.close(); } - auto print_parameters = + const auto print_parameters = pinput->GetOrAddBoolean("parthenon/job", "print_parameters", false); if (print_parameters && Globals::my_rank == 0) { pinput->ParameterDump(std::cout); @@ -71,7 +71,11 @@ void Driver::PreExecute() { // Optionally also dump to console DumpInputParameters(); + bool check_orphans = pinput->GetOrAddBoolean( + "parthenon/job", "check_orphans", true, + "print a warning if any parameters are in the input deck but not used in the code"); if (Globals::my_rank == 0) { + if (check_orphans) pinput->CheckOrphans(); std::cout << "# Variables in use:\n" << *(pmesh->resolved_packages) << std::endl; std::cout << std::endl; std::cout << "Setup complete, executing driver...\n" << std::endl; @@ -95,6 +99,25 @@ void Driver::PostExecute(DriverStatus status) { } DriverStatus EvolutionDriver::Execute() { + // JMM: I want these before output_params_and_exit so we capture as much as possible + OutputSignal signal = pinput->GetBoolean("parthenon/job", "run_only_analysis") + ? OutputSignal::analysis + : OutputSignal::none; + int perf_cycle_offset = pinput->GetOrAddInteger( + "parthenon/time", "perf_cycle_offset", 0, + "don't measure performance for some number of initial cycles"); + const bool output_params_and_exit = pinput->GetOrAddBoolean( + "parthenon/job", "output_params_and_exit", false, + "output a description of all input parameters accessed and quit"); + const std::string params_block_regex = + pinput->GetOrAddString("parthenon/job", "output_params_block_regex", "(.*)", + "when outputting input parameters, this selects which input " + "blocks to output; all are output by default"); + if (output_params_and_exit && Globals::my_rank == 0) { + pinput->OutputParameterTable(std::cout, std::regex(params_block_regex)); + return DriverStatus::complete; + } + PreExecute(); InitializeBlockTimeSteps(); SetGlobalTimeStep(); @@ -112,13 +135,8 @@ DriverStatus EvolutionDriver::Execute() { } } // UserWorkBeforeLoop - OutputSignal signal = pinput->GetBoolean("parthenon/job", "run_only_analysis") - ? OutputSignal::analysis - : OutputSignal::none; pouts->MakeOutputs(pmesh, pinput, &tm, signal); pmesh->mbcnt = 0; - int perf_cycle_offset = - pinput->GetOrAddInteger("parthenon/time", "perf_cycle_offset", 0); { // Main t < tmax loop region PARTHENON_INSTRUMENT @@ -255,8 +273,6 @@ void EvolutionDriver::SetGlobalTimeStep() { tm.dt = std::min(tm.dt, pmb->NewDt()); pmb->SetAllowedDt(std::numeric_limits::max()); } - // Allow the user to enforce maximum timestep - tm.dt = std::min(tm.dt, dt_user); // Force timestep to be in the allowable range tm.dt = std::max(dt_floor, std::min(tm.dt, dt_ceil)); #ifdef MPI_PARALLEL diff --git a/src/driver/driver.hpp b/src/driver/driver.hpp index 7edd1e5233e0c..86acc58481c9c 100644 --- a/src/driver/driver.hpp +++ b/src/driver/driver.hpp @@ -1,5 +1,5 @@ //======================================================================================== -// (C) (or copyright) 2020-2022. Triad National Security, LLC. All rights reserved. +// (C) (or copyright) 2020-2025. Triad National Security, LLC. All rights reserved. // // This program was produced under U.S. Government contract 89233218CNA000001 for Los // Alamos National Laboratory (LANL), which is operated by Triad National Security, LLC @@ -24,9 +24,9 @@ #include "application_input.hpp" #include "basic_types.hpp" #include "globals.hpp" +#include "inputs/parameter_input.hpp" #include "mesh/mesh.hpp" #include "outputs/outputs.hpp" -#include "parameter_input.hpp" #include "tasks/tasks.hpp" namespace parthenon { @@ -65,41 +65,61 @@ class EvolutionDriver : public Driver { public: EvolutionDriver(ParameterInput *pin, ApplicationInput *app_in, Mesh *pm) : Driver(pin, app_in, pm) { - Real start_time = pinput->GetOrAddReal("parthenon/time", "start_time", 0.0); + Real start_time = + pinput->GetOrAddReal("parthenon/time", "start_time", 0.0, + "physical time at which to start the simulation"); Real tstop = pinput->GetOrAddReal("parthenon/time", "tlim", - std::numeric_limits::infinity()); + std::numeric_limits::infinity(), + "physical time at which to end the simulation"); Real dt = - pinput->GetOrAddReal("parthenon/time", "dt", std::numeric_limits::max()); - dt_min = pinput->GetOrAddReal("parthenon/time", "dt_min", - std::numeric_limits::min()); - dt_max = pinput->GetOrAddReal("parthenon/time", "dt_max", - std::numeric_limits::max()); - dt_init = pinput->GetOrAddReal("parthenon/time", "dt_init", - std::numeric_limits::max()); - dt_init_force = pinput->GetOrAddBoolean("parthenon/time", "dt_init_force", false); - - dt_user = pinput->GetOrAddReal("parthenon/time", "dt_user", - std::numeric_limits::max()); + pinput->GetOrAddReal("parthenon/time", "dt", std::numeric_limits::max(), + "initial value of time step before constraining via cfl"); + dt_min = + pinput->GetOrAddReal("parthenon/time", "dt_min", std::numeric_limits::min(), + "if timestep falls below this level for dt_min_count_max " + "iterations, parthenon throws an error"); + dt_max = + pinput->GetOrAddReal("parthenon/time", "dt_max", std::numeric_limits::max(), + "if timestep is above this level for dt_max_count_max " + "iterations, parthenon throws an error"); + dt_init = pinput->GetOrAddReal( + "parthenon/time", "dt_init", std::numeric_limits::max(), + "the first time step will be at least as small as dt_init"); + dt_init_force = pinput->GetOrAddBoolean( + "parthenon/time", "dt_init_force", false, + "if set to true, the first time step will be exactly dt_init"); + dt_force = pinput->GetOrAddReal("parthenon/time", "dt_force", - std::numeric_limits::lowest()); + std::numeric_limits::lowest(), + "if set manually enforces this time step exactly"); dt_floor = pinput->GetOrAddReal("parthenon/time", "dt_floor", - std::numeric_limits::min()); + std::numeric_limits::min(), + "minimum allowed timestep"); dt_ceil = pinput->GetOrAddReal("parthenon/time", "dt_ceil", - std::numeric_limits::max()); + std::numeric_limits::max(), + "maximum allowed timestep"); dt_min_count_max = - pinput->GetOrAddInteger("parthenon/time", "dt_min_cycle_limit", 10); - dt_max_count_max = pinput->GetOrAddInteger("parthenon/time", "dt_max_cycle_limit", 1); + pinput->GetOrAddInteger("parthenon/time", "dt_min_cycle_limit", 10, + "number of cycles where dt < dt_min before error"); + dt_max_count_max = + pinput->GetOrAddInteger("parthenon/time", "dt_max_cycle_limit", 1, + "number of cycles where dt > dt_max before error"); dt_min_count = 0; dt_max_count = 0; - dt_factor = pinput->GetOrAddReal("parthenon/time", "dt_factor", 2.0); + dt_factor = pinput->GetOrAddReal("parthenon/time", "dt_factor", 2.0, + "maximum relative change in dt per timestep"); - const auto ncycle = pinput->GetOrAddInteger("parthenon/time", "ncycle", 0); - const auto nmax = pinput->GetOrAddInteger("parthenon/time", "nlim", -1); - const auto nout = pinput->GetOrAddInteger("parthenon/time", "ncycle_out", 1); + const auto ncycle = + pinput->GetOrAddInteger("parthenon/time", "ncycle", 0, "initial iteration count"); + const auto nmax = pinput->GetOrAddInteger( + "parthenon/time", "nlim", -1, + "maximum number of iterations, only limiting if non-negative"); + const auto nout = pinput->GetOrAddInteger("parthenon/time", "ncycle_out", 1, + "cadence of outputs to stdout"); // disable mesh output by default - const auto nout_mesh = - pinput->GetOrAddInteger("parthenon/time", "ncycle_out_mesh", 0); + const auto nout_mesh = pinput->GetOrAddInteger("parthenon/time", "ncycle_out_mesh", 0, + "cadence of outputs describing mesh"); tm = SimTime(start_time, tstop, nmax, ncycle, nout, nout_mesh, dt); pouts = std::make_unique(pmesh, pinput, &tm); } @@ -112,7 +132,7 @@ class EvolutionDriver : public Driver { protected: void PostExecute(DriverStatus status) override; - Real dt_user, dt_force, dt_init, dt_min, dt_max, dt_floor, dt_ceil; + Real dt_force, dt_init, dt_min, dt_max, dt_floor, dt_ceil; Real dt_factor; bool dt_init_force; int dt_min_count, dt_max_count; diff --git a/src/driver/multistage.hpp b/src/driver/multistage.hpp index d23c894be592d..d48e287f08fff 100644 --- a/src/driver/multistage.hpp +++ b/src/driver/multistage.hpp @@ -20,8 +20,8 @@ #include "application_input.hpp" #include "driver/driver.hpp" +#include "inputs/parameter_input.hpp" #include "mesh/mesh.hpp" -#include "parameter_input.hpp" #include "tasks/tasks.hpp" #include "time_integration/staged_integrator.hpp" diff --git a/src/globals.cpp b/src/globals.cpp index b00db585a3fe7..110f66f75d647 100644 --- a/src/globals.cpp +++ b/src/globals.cpp @@ -3,7 +3,7 @@ // Copyright(C) 2014 James M. Stone and other code contributors // Licensed under the 3-clause BSD License, see LICENSE file for details //======================================================================================== -// (C) (or copyright) 2020-2024. Triad National Security, LLC. All rights reserved. +// (C) (or copyright) 2020-2025. Triad National Security, LLC. All rights reserved. // // This program was produced under U.S. Government contract 89233218CNA000001 for Los // Alamos National Laboratory (LANL), which is operated by Triad National Security, LLC @@ -21,8 +21,10 @@ // e.g., global constants that are set once and never changed. To prevent name collisions // global variables are wrapped in their own namespace. -#include "globals.hpp" +#include + #include "defs.hpp" +#include "globals.hpp" namespace parthenon { namespace Globals { diff --git a/src/globals.hpp b/src/globals.hpp index 539c158d6570c..e6451dadab7ca 100644 --- a/src/globals.hpp +++ b/src/globals.hpp @@ -3,7 +3,7 @@ // Copyright(C) 2014 James M. Stone and other code contributors // Licensed under the 3-clause BSD License, see LICENSE file for details //======================================================================================== -// (C) (or copyright) 2020-2024. Triad National Security, LLC. All rights reserved. +// (C) (or copyright) 2020-2025. Triad National Security, LLC. All rights reserved. // // This program was produced under U.S. Government contract 89233218CNA000001 for Los // Alamos National Laboratory (LANL), which is operated by Triad National Security, LLC @@ -19,6 +19,8 @@ //! \file globals.hpp // \brief namespace containing external global variables +#include + #include "basic_types.hpp" namespace parthenon { diff --git a/src/argument_parser.hpp b/src/inputs/argument_parser.hpp similarity index 86% rename from src/argument_parser.hpp rename to src/inputs/argument_parser.hpp index 8b79eaebc9753..0d8ce4bcdcd21 100644 --- a/src/argument_parser.hpp +++ b/src/inputs/argument_parser.hpp @@ -1,5 +1,5 @@ //======================================================================================== -// (C) (or copyright) 2021. Triad National Security, LLC. All rights reserved. +// (C) (or copyright) 2021-2025. Triad National Security, LLC. All rights reserved. // // This program was produced under U.S. Government contract 89233218CNA000001 for Los // Alamos National Laboratory (LANL), which is operated by Triad National Security, LLC @@ -11,8 +11,8 @@ // the public, perform publicly and display publicly, and to permit others to do so. //======================================================================================== -#ifndef ARGUMENT_PARSER_HPP_ -#define ARGUMENT_PARSER_HPP_ +#ifndef INPUTS_ARGUMENT_PARSER_HPP_ +#define INPUTS_ARGUMENT_PARSER_HPP_ #include #include @@ -64,8 +64,13 @@ class ArgParse { invalid = invalid_arg(); prundir = argv[++i]; break; - case 'n': - narg_flag = 1; + case 'p': + param_flag = 1; + if ((i + 1 == argc) || (argv[i + 1][0] == '-')) { + params_regex = default_params_regex_; + } else { + params_regex = argv[++i]; + } break; case 'm': // -m invalid = invalid_arg(); @@ -89,7 +94,9 @@ class ArgParse { std::cout << " -r restart with this file\n"; std::cout << " -a analyze/postprocess this file\n"; std::cout << " -d specify run dir [current dir]\n"; - std::cout << " -n parse input file and quit\n"; + std::cout << " -p [regex] parse input file, report parameters\n" + << " for blocks matching regex in table and quit\n" + << " default regex is wildcard\n"; std::cout << " -c show configuration and quit\n"; std::cout << " -m output mesh structure and quit\n"; std::cout << " -t hh:mm:ss wall time limit for final output\n"; @@ -126,14 +133,18 @@ class ArgParse { char *input_filename = nullptr; char *restart_filename = nullptr; char *prundir = nullptr; + char *params_regex = nullptr; bool analysis_flag = false; int res_flag = 0; - int narg_flag = 0; + int param_flag = 0; int mesh_flag = 0; int wtlim = 0; int exit_flag = 0; + + private: + char default_params_regex_[64] = "(.*)"; // wildcard }; } // namespace parthenon -#endif // ARGUMENT_PARSER_HPP_ +#endif // INPUTS_ARGUMENT_PARSER_HPP_ diff --git a/src/inputs/inputs_package.cpp b/src/inputs/inputs_package.cpp new file mode 100644 index 0000000000000..11c67f3408c00 --- /dev/null +++ b/src/inputs/inputs_package.cpp @@ -0,0 +1,51 @@ +//======================================================================================== +// Parthenon performance portable AMR framework +// Copyright(C) 2020-2025 The Parthenon collaboration +// Licensed under the 3-clause BSD License, see LICENSE file for details +//======================================================================================== +// Athena++ astrophysical MHD code +// Copyright(C) 2014 James M. Stone and other code contributors +// Licensed under the 3-clause BSD License, see LICENSE file for details +//======================================================================================== +// (C) (or copyright) 2020-2025. Triad National Security, LLC. All rights reserved. +// +// This program was produced under U.S. Government contract 89233218CNA000001 for Los +// Alamos National Laboratory (LANL), which is operated by Triad National Security, LLC +// for the U.S. Department of Energy/National Nuclear Security Administration. All rights +// in the program are reserved by Triad National Security, LLC, and the U.S. Department +// of Energy/National Nuclear Security Administration. The Government is granted for +// itself and others acting on its behalf a nonexclusive, paid-up, irrevocable worldwide +// license in this material to reproduce, prepare derivative works, distribute copies to +// the public, perform publicly and display publicly, and to permit others to do so. +//======================================================================================== + +#include +#include +#include +#include +#include + +#include "inputs/inputs_package.hpp" +#include "inputs/parameter_input.hpp" +#include "interface/state_descriptor.hpp" + +namespace parthenon { +namespace InputsPackage { + +std::shared_ptr Initialize(ParameterInput *pin) { + auto pkg = std::make_shared("Inputs"); + // Add the pre-parsed inputs as params also, for archival + // purposes. The original input deck is stashed in the restart. The + // command line arguments are not. + const auto &raw_inputs = pin->GetPreParsedInputs(); + for (const auto &[origin, input] : raw_inputs) { + pkg->AddParam(origin.ToString(), input, + origin.type == RecordOrigin::Type::InputFile + ? Params::Mutability::Restart + : Params::Mutability::Immutable); + } + return pkg; +} + +} // namespace InputsPackage +} // namespace parthenon diff --git a/src/inputs/inputs_package.hpp b/src/inputs/inputs_package.hpp new file mode 100644 index 0000000000000..d26b220e53997 --- /dev/null +++ b/src/inputs/inputs_package.hpp @@ -0,0 +1,39 @@ +//======================================================================================== +// Parthenon performance portable AMR framework +// Copyright(C) 2020-2025 The Parthenon collaboration +// Licensed under the 3-clause BSD License, see LICENSE file for details +//======================================================================================== +// Athena++ astrophysical MHD code +// Copyright(C) 2014 James M. Stone and other code contributors +// Licensed under the 3-clause BSD License, see LICENSE file for details +//======================================================================================== +// (C) (or copyright) 2020-2025. Triad National Security, LLC. All rights reserved. +// +// This program was produced under U.S. Government contract 89233218CNA000001 for Los +// Alamos National Laboratory (LANL), which is operated by Triad National Security, LLC +// for the U.S. Department of Energy/National Nuclear Security Administration. All rights +// in the program are reserved by Triad National Security, LLC, and the U.S. Department +// of Energy/National Nuclear Security Administration. The Government is granted for +// itself and others acting on its behalf a nonexclusive, paid-up, irrevocable worldwide +// license in this material to reproduce, prepare derivative works, distribute copies to +// the public, perform publicly and display publicly, and to permit others to do so. +//======================================================================================== +#ifndef INPUTS_INPUTS_PACKAGE_HPP_ +#define INPUTS_INPUTS_PACKAGE_HPP_ + +#include +#include + +namespace parthenon { + +class ParameterInput; +class StateDescriptor; + +namespace InputsPackage { + +std::shared_ptr Initialize(ParameterInput *pin); + +} // namespace InputsPackage +} // namespace parthenon + +#endif // INPUTS_INPUTS_PACKAGE_HPP_ diff --git a/src/inputs/parameter_input.cpp b/src/inputs/parameter_input.cpp new file mode 100644 index 0000000000000..bfd377377a624 --- /dev/null +++ b/src/inputs/parameter_input.cpp @@ -0,0 +1,501 @@ +//======================================================================================== +// Athena++ astrophysical MHD code +// Copyright(C) 2014 James M. Stone and other code contributors +// Licensed under the 3-clause BSD License, see LICENSE file for details +//======================================================================================== +// (C) (or copyright) 2020-2025. Triad National Security, LLC. All rights reserved. +// +// This program was produced under U.S. Government contract 89233218CNA000001 for Los +// Alamos National Laboratory (LANL), which is operated by Triad National Security, LLC +// for the U.S. Department of Energy/National Nuclear Security Administration. All rights +// in the program are reserved by Triad National Security, LLC, and the U.S. Department +// of Energy/National Nuclear Security Administration. The Government is granted for +// itself and others acting on its behalf a nonexclusive, paid-up, irrevocable worldwide +// license in this material to reproduce, prepare derivative works, distribute copies to +// the public, perform publicly and display publicly, and to permit others to do so. +//======================================================================================== + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "globals.hpp" +#include "inputs/parameter_input.hpp" +#include "utils/error_checking.hpp" + +namespace parthenon { + +std::string ParameterPath(const std::string block, const std::string name) { + if (!std::count(block.begin(), block.end(), '/')) { + if (name == "") { + return block; + } else { + return block + "." + name; + } + } else { + std::string b(block); + std::replace(b.begin(), b.end(), '/', '.'); + if (name == "") { + return b; + } else { + return b + "." + name; + } + } +} + +void ParameterInput::Merge(toml::table &a, const toml::table &b, bool check_dups) { + b.for_each([&](const toml::key &key, auto &&el) { + recursive_merge(a, b, key, el, check_dups); + }); +} + +void ParameterInput::recursive_get_paths(const toml::table &a, toml::path prefix, + const toml::key &key, + std::vector &paths) const { + if (a[key].is()) { + const toml::table &achild = a[key].ref(); + toml::path block = (prefix != toml::path("")) ? toml::path(prefix.append(key.str())) + : toml::path(key.str()); + achild.for_each([&](const toml::key &key, auto &&el) { + recursive_get_paths(achild, block, key, paths); + }); + } else { + paths.push_back(toml::path(prefix.append(key.str()))); + } +} +std::vector ParameterInput::GetAllPaths(const toml::table &a) const { + std::vector paths; + a.for_each([&](const toml::key &key, auto &&el) { + recursive_get_paths(a, toml::path(""), key, paths); + }); + // Could have a version that returns these path objs, + // but probably everyone wants strings? + std::vector path_strings; + for (auto path : paths) { + path_strings.push_back(path.str()); + } + return path_strings; +} + +toml::table ParameterInput::Blocks() { return parameters_; } +toml::table ParameterInput::Blocks(const char *path) { + return GetPath(path); +} +toml::table ParameterInput::Blocks(std::string &path) { + return GetPath(path); +} +// Get const copies of entire contents, primarily for hashing +const toml::table ParameterInput::GetAll() const { return parameters_; } + +RecordOrigin ParameterInput::GetOrigin(const std::string &path) { + return queries_.at(path).origin; +} + +void ParameterInput::LoadFile(const std::string fname, bool check_for_overrides) { + std::stringstream contents; + char *buf = reinterpret_cast(calloc(sizeof(char), max_input_filesize_)); + + IOWrapper infile; + infile.Open(fname.c_str(), IOWrapper::FileMode::read); + infile.Read_all(buf, sizeof(char), max_input_filesize_); + infile.Close(); + contents.write(buf, max_input_filesize_); + free(buf); + + LoadFromStream(contents, RecordOrigin(fname), check_for_overrides); + + return; +} + +void ParameterInput::LoadFromStream(std::istream &is, const RecordOrigin &origin, + bool check_for_overrides) { + std::stringstream ss; + ss << is.rdbuf(); + std::string input = ss.str(); + + // Stash this for archival use + PARTHENON_REQUIRE_THROWS(pre_parsed_inputs_.count(origin) == 0, + "Each input must only be processed once"); + pre_parsed_inputs_[origin] = input; + + // Remove all null bytes: it's faster & toml doesn't like them + input.erase(std::remove(input.begin(), input.end(), '\00'), input.end()); + // If n(<) > n([), we're parsing an old-style file. Otherwise, TOML + int nangle = std::count(input.begin(), input.end(), '<'); + int nsquare = std::count(input.begin(), input.end(), '['); + auto new_parameters = toml::table(); + if (nangle > nsquare) { + is.seekg(is.beg); + new_parameters = LegacyParse(is, origin); + } else { + new_parameters = toml::parse(input); + } + // Merge from different inputs, only check for overrides if asked + Merge(parameters_, new_parameters, check_for_overrides); +} + +void ParameterInput::ModifyFromCmdline(int argc, char *argv[]) { + std::string input_text, path, value; + std::stringstream msg, cli_record_string; + + for (int i = 1; i < argc; i++) { + input_text = argv[i]; + + std::size_t equal_posn = input_text.find_first_of("="); // first "=" character + + // Only parse arguments with '=' + if (equal_posn == std::string::npos) continue; + + // stash the argument for archival purposes + cli_record_string << input_text << std::endl; + + // Sanitize block + name together, but only if there are no dots + path = input_text.substr(0, equal_posn); + // This replaces all '/', so they can't be used in varnames which will be passed + // on the command line, even when using pure TOML inputs. + // ('/' obviously can't be used in block names anyway if we want back-compat) + // Alternatively we could check for '.', which would forbid '.' in old-style + // blocks and varnames retroactively. + path = ParameterPath(path, ""); + value = input_text.substr(equal_posn + 1, std::string::npos); + + if (!parameters_.at_path(path)) { + if (Globals::my_rank == 0) { + msg << "In function [ParameterInput::ModifyFromCmdline]:" << std::endl + << " Parameter '" << path + << "' on command line not found in input/restart file. Parameter will be " + "added."; + PARTHENON_WARN(msg); + } + } + // Commandline parameters can override anything or each other, don't check anything + AddParameter_(parameters_, path, value, RecordOrigin(OriginType::CommandLine)); + } + pre_parsed_inputs_[RecordOrigin(OriginType::CommandLine)] = cli_record_string.str(); +} + +int ParameterInput::DoesParameterExist(const std::string &block, + const std::string &name) { + return DoesParameterExist(ParameterPath(block, name)); +} +int ParameterInput::DoesParameterExist(const std::string &path) { + return !!parameters_.at_path(path); +} +int ParameterInput::DoesBlockExist(const std::string &block) { + return parameters_.contains(ParameterPath(block, "")); +} + +void ParameterInput::CheckRequired(const std::string &block, const std::string &name) { + return CheckRequired(ParameterPath(block, name)); +} +void ParameterInput::CheckRequired(const std::string &path) { + bool exists = DoesParameterExist(path) && (GetOrigin(path).type != OriginType::Default); + if (!exists) { + std::stringstream ss; + ss << std::endl + << "### ERROR in CheckRequired:" << std::endl + << "Parameter file missing required field " << path << std::endl + << std::endl; + throw std::runtime_error(ss.str()); + } +} + +void ParameterInput::CheckDesired(const std::string &block, const std::string &name) { + return CheckDesired(ParameterPath(block, name)); +} +void ParameterInput::CheckDesired(const std::string &path) { + bool missing = true; + bool defaulted = false; + if (DoesParameterExist(path)) { + missing = false; + defaulted = (GetOrigin(path).type == OriginType::Default); + } + if (missing) { + std::cout << std::endl + << "### WARNING in CheckDesired:" << std::endl + << "Parameter file missing desired field " << path << std::endl; + } + if (defaulted) { // Could look up the default here, but it's set to that anyway + std::cout << std::endl + << "Defaulting to " << path << " = " << parameters_.at_path(path) + << std::endl; + } +} + +void ParameterInput::CheckOrphans() const { + std::size_t count = 0; + std::stringstream msg; + msg << "The following input parameters are set but unused:\n"; + for (auto path : GetAllPaths(parameters_)) { + auto &query = queries_.at(path); + if (!query.requested) { + msg << path << ", with " << query.origin << "\n"; + count++; + } + } + msg << std::endl; + if ((Globals::my_rank == 0) && (count > 0)) { + PARTHENON_WARN(msg); + } +} + +void ParameterInput::ParameterDump(std::ostream &os) { os << parameters_ << "\n"; } + +toml::table ParameterInput::LegacyParse(std::istream &is, const RecordOrigin &origin) { + std::string line, block_name, param_name, param_value, param_comment; + std::size_t first_char, last_char; + std::stringstream msg; + int blocks_found{0}; + + // Buffer multiple lines if a continuation character is present + std::string multiline_name, multiline_value, multiline_comment; + // Status in/out of continuation + bool continuing = false; + + // Table for accumulating results + toml::table tmp_tbl = toml::table(); + + while (is.good()) { + std::getline(is, line); + + // remove all \t\f\n\r\v but leave pure spaces + line.erase(std::remove_if(line.begin(), line.end(), + [](char c) { return std::isspace(c) && c != ' '; }), + line.end()); + + if (line.empty()) continue; // skip blank line + first_char = line.find_first_not_of(" "); // skip white space + if (first_char == std::string::npos) continue; // line is all white space + if (line.compare(first_char, 1, "#") == 0) continue; // skip comments + if (line.compare(first_char, 9, "") == 0) break; // stop on + + if (line.compare(first_char, 1, "<") == 0) { // a new block + if (continuing) { + msg << "### FATAL ERROR in function [ParameterInput::LegacyParse]" << std::endl + << "Multiline field ended unexpectedly with new block " + << "character <. Look above this line for the error:" << std::endl + << line << std::endl + << std::endl; + PARTHENON_THROW(msg); + } + first_char++; + last_char = (line.find_first_of(">", first_char)); + block_name.assign(line, first_char, last_char - 1); // extract block name + block_name = ParameterPath(block_name, ""); + + if (last_char == std::string::npos) { + msg << "### FATAL ERROR in function [ParameterInput::LegacyParse]" << std::endl + << "Block name '" << block_name << "' in the input stream'" + << "' not properly ended"; + PARTHENON_THROW(msg); + } + + blocks_found++; + continue; // skip to next line if block name was found + } // end "a new block was found" + + // if line does not contain a block name or skippable information (comments, + // whitespace), it must contain a parameter value + if (blocks_found == 0) { + msg << "### FATAL ERROR in function [ParameterInput::LegacyParse]" << std::endl + << "Input file must specify a block name before the first" + << " parameter = value line"; + PARTHENON_THROW(msg); + } + // parse line and add name/value/comment strings (if found) to current block name + bool has_cont_char = LegacyParseLine(line, param_name, param_value); + if (continuing || has_cont_char) { + // Append line data + multiline_name += param_name; + multiline_value += param_value; + // Set new state + continuing = true; + } + + if (continuing && !has_cont_char) { + // Flush line data + param_name = multiline_name; + param_value = multiline_value; + param_comment = multiline_comment; + multiline_name = ""; + multiline_value = ""; + multiline_comment = ""; + // Set new state + continuing = false; + } + + if (!continuing) { + if (param_name != "") { + AddParameter_(tmp_tbl, ParameterPath(block_name, param_name), param_value, origin, + true); + } + } + } + + return tmp_tbl; +} + +bool ParameterInput::LegacyParseLine(std::string line, std::string &name, + std::string &value) { + std::size_t first_char, last_char, equal_char, hash_char, cont_char, len; + bool continuation = false; + + hash_char = line.find_first_of("#"); // find "#" (optional) + if (hash_char != std::string::npos) { + line.erase(hash_char, std::string::npos); + } + + first_char = line.find_first_not_of(" "); // find first non-white space + equal_char = line.find_first_of("="); // find "=" char + + // copy substring into name, remove white space at end of name + if (equal_char == std::string::npos) { + name = ""; + line.erase(0, first_char); + } else { + len = equal_char - first_char; + name.assign(line, first_char, len); + last_char = name.find_last_not_of(" "); + name.erase(last_char + 1, std::string::npos); + line.erase(0, equal_char + 1); + } + + cont_char = line.find_first_of("&"); // find "&" continuation character + // copy substring into value, remove white space at start and end + len = cont_char; + if (cont_char != std::string::npos) { + std::string right_of_cont; + right_of_cont.assign(line, cont_char + 1, std::string::npos); + first_char = right_of_cont.find_first_not_of(" "); + if (first_char != std::string::npos) { + throw std::runtime_error("ERROR: Non-comment characters are not permitted to the " + "right of line continuations"); + } + continuation = true; + } + value.assign(line, 0, len); + + first_char = value.find_first_not_of(" "); + value.erase(0, first_char); + + last_char = value.find_last_not_of(" "); + value.erase(last_char + 1, std::string::npos); + + return continuation; +} + +void ParameterInput::OutputParameterTable(std::ostream &os, + const std::regex &block_regex) const { + // TOML's node-types are a bit verbose. This is simpler. + auto SimpleName = [](const toml::node_type tp) -> std::string { + if (tp == toml::node_type::integer) return "int"; + if (tp == toml::node_type::boolean) return "bool"; + if (tp == toml::node_type::floating_point) return "Real"; + std::stringstream ss; + ss << tp; + return ss.str(); + }; + // Loop through parameters. Already alphabetical, just gotta split block/name + os << "block,parameters,type,default,description" << std::endl; + std::string last_block_name = ""; + for (auto path : GetAllPaths(parameters_)) { + // Yeah, GetAllPaths returns strings. Make it back into a path + auto toml_path = toml::path(path); + std::string block_name, param_name; + if (toml_path.size() < 2) { + param_name = path; + block_name = "root"; + } else { + std::size_t idx = toml_path.size() - 1; + param_name = toml_path[idx].key(); + block_name = toml_path.subpath(0, idx).str(); + } + // Filter on block name fitting user regex + if (std::regex_match(block_name, block_regex)) { + // Output blank lines on block change + if (block_name != last_block_name && last_block_name != "") + os << "\"\",\"\",\"\",\"\",\"\"" << std::endl; + last_block_name = block_name; + /* clang-format off */ + if (queries_.count(path) > 0) { + auto record = queries_.at(path); + std::stringstream ss; + ss << "\"" << block_name << "\"" + << "," << "\"" << param_name << "\"" + << "," << "\"" << SimpleName(GetTypePath(path)) << "\"" + << "," << "\"" << record.default_value_str << "\"" + << "," << "\""; + std::size_t num_allowed_vals = record.allowed_vals_str.size(); + if (record.docstring.has_value()) { + ss << record.docstring.value(); + if (num_allowed_vals > 0) { + ss << "; "; + } + } + if (num_allowed_vals > 0) { + ss << "Allowed values: "; + std::size_t ival = 0; + for (const auto &v : record.allowed_vals_str) { + ss << v; + if (ival < num_allowed_vals - 1) { + ss << ", "; + } + ival++; + } + } + ss << "\""; + /* clang-format on */ + os << ss.str() << std::endl; + } + } + } +} + +bool operator<(const RecordOrigin &lhs, const RecordOrigin &rhs) { + return (lhs.type < rhs.type) || (lhs.type == rhs.type && lhs.file < rhs.file); +} +std::ostream &operator<<(std::ostream &os, RecordOrigin::Type type) { + switch (type) { + case RecordOrigin::Type::None: + return os << "None"; + case RecordOrigin::Type::InputFile: + return os << "InputFile"; + case RecordOrigin::Type::Restart: + return os << "Restart"; + case RecordOrigin::Type::Default: + return os << "Default"; + case RecordOrigin::Type::SetInCode: + return os << "SetInCode"; + case RecordOrigin::Type::CommandLine: + return os << "CommandLine"; + default: + return os << "Unknown"; + } +} +std::ostream &operator<<(std::ostream &os, const RecordOrigin &origin) { + os << "Origin Type: " << origin.type; + if (origin.HasFile()) { + os << ", File: \"" << origin.file << "\""; + } + return os; +} +std::string RecordOrigin::ToString() const { + std::stringstream ss; + ss << *this; + return ss.str(); +} + +} // namespace parthenon diff --git a/src/inputs/parameter_input.hpp b/src/inputs/parameter_input.hpp new file mode 100644 index 0000000000000..f996cd454fa52 --- /dev/null +++ b/src/inputs/parameter_input.hpp @@ -0,0 +1,700 @@ +//======================================================================================== +// (C) (or copyright) 2020-2025. Triad National Security, LLC. All rights reserved. +// +// This program was produced under U.S. Government contract 89233218CNA000001 for Los +// Alamos National Laboratory (LANL), which is operated by Triad National Security, LLC +// for the U.S. Department of Energy/National Nuclear Security Administration. All rights +// in the program are reserved by Triad National Security, LLC, and the U.S. Department +// of Energy/National Nuclear Security Administration. The Government is granted for +// itself and others acting on its behalf a nonexclusive, paid-up, irrevocable worldwide +// license in this material to reproduce, prepare derivative works, distribute copies to +// the public, perform publicly and display publicly, and to permit others to do so. +//======================================================================================== +// Athena++ astrophysical MHD code +// Copyright(C) 2014 James M. Stone and other code contributors +// Licensed under the 3-clause BSD License, see LICENSE file for details +//======================================================================================== + +#ifndef INPUTS_PARAMETER_INPUT_HPP_ +#define INPUTS_PARAMETER_INPUT_HPP_ +//! \file parameter_input.hpp +// \brief definition of class ParameterInput +// Contains data structures used to store, and functions used to access, parameters +// read from the input file. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include // for std::forward, std::pair +#include + +#include + +#include "config.hpp" +#include "defs.hpp" +#include "globals.hpp" +#include "outputs/io_wrapper.hpp" +#include "utils/hash.hpp" +#include "utils/sort.hpp" +#include "utils/string_utils.hpp" +#include "utils/utils.hpp" + +namespace parthenon { + +std::string ParameterPath(const std::string block, const std::string name); + +struct RecordOrigin { + friend class std::hash; + enum class Type : std::size_t { + None, + InputFile, + Restart, + Default, + SetInCode, + CommandLine + }; + RecordOrigin() = default; + explicit RecordOrigin(const std::string &filename) + : type(Type::InputFile), file(filename) {} + explicit RecordOrigin(const Type origin_type) : type(origin_type) {} + RecordOrigin(const Type origin_type, const std::string &filename) + : type(origin_type), file(filename) {} + std::string ToString() const; + bool HasFile() const { return (type == Type::InputFile) || (type == Type::Restart); } + + Type type = Type::None; + std::string file = ""; +}; +bool operator<(const RecordOrigin &lhs, const RecordOrigin &rhs); +std::ostream &operator<<(std::ostream &os, RecordOrigin::Type type); +std::ostream &operator<<(std::ostream &os, const RecordOrigin &origin); + +struct QueryRecord { + // JMM: This tracks whether or not this parameter has been requested + // in the code. We use this, along with record origin, to track + // provenance and look for orphan parameters. + bool requested = false; // default false here is very important + + RecordOrigin origin; + std::any default_value; // std::any::has_value to check if default + // val exists + std::string + default_value_str; // used for output, so we don't have to mess with types later + std::vector allowed_values; // size to check if allowed values exist + std::vector allowed_vals_str; // used for output + std::optional docstring; // std::optional::has_value to check if exists + template + static std::string ToString(const T &val) { + std::stringstream ss; + if constexpr (std::is_same_v) { + ss.precision(8); // max digits is totally unreadable + // ss.precision(std::numeric_limits::max_digits10); + } + ss << val; + return ss.str(); + } +}; + +// This can be used to tell the params infrastructure that the default +// value of one parameter depends on another one +class ParameterRef { + public: + ParameterRef(const std::string &block, const std::string &name) + : path_(ParameterPath(block, name)) {} + explicit ParameterRef(const std::string &path) : path_(path) {} + std::string CanonicalPath() const { return path_; } + + private: + const std::string path_; +}; + +class ParameterInput { + friend class std::hash; + using OriginType = RecordOrigin::Type; + + public: + // constructor/destructor + ParameterInput() { parameters_ = toml::table(); } + explicit ParameterInput(std::string input_filename) { + parameters_ = toml::table(); + LoadFile(input_filename); + } + ~ParameterInput() {} + + // functions + void LoadFromStream(std::istream &is, const RecordOrigin &origin = RecordOrigin(), + bool check_for_overrides = false); + void LoadFile(std::string fname, bool check_for_overrides = false); + void ModifyFromCmdline(int argc, char *argv[]); + + void ParameterDump(std::ostream &os); + const std::map &GetPreParsedInputs() const { + return pre_parsed_inputs_; + } + + int DoesParameterExist(const std::string &block, const std::string &name); + int DoesParameterExist(const std::string &path); + int DoesBlockExist(const std::string &block); + + // TODO(JMM): Make this more general? + void OutputParameterTable(std::ostream &os, + const std::regex &block_regex = std::regex("(.*)")) const; + + void CheckRequired(const std::string &block, const std::string &name); + void CheckRequired(const std::string &path); + void CheckDesired(const std::string &block, const std::string &name); + void CheckDesired(const std::string &path); + void CheckOrphans() const; + + toml::table Blocks(); + toml::table Blocks(const char *path); + toml::table Blocks(std::string &path); + const toml::table GetAll() const; + std::vector GetAllPaths(const toml::table &a) const; + + RecordOrigin GetOrigin(const std::string &path); + + // toml++ only supports int64_t + template + int GetInteger(Args &&...args) { + return static_cast(Get(std::forward(args)...)); + } + template + int GetOrAddInteger(Args &&...args) { + return static_cast(GetOrAdd(std::forward(args)...)); + } + template + int SetInteger(Args &&...args) { + return static_cast(Set(std::forward(args)...)); + } + // toml++ only supports double + template + double GetReal(Args &&...args) { + return Get(std::forward(args)...); + } + template + double GetOrAddReal(Args &&...args) { + return GetOrAdd(std::forward(args)...); + } + template + double SetReal(Args &&...args) { + return Set(std::forward(args)...); + } + template + bool GetBoolean(Args &&...args) { + return Get(std::forward(args)...); + } + template + bool GetOrAddBoolean(Args &&...args) { + return GetOrAdd(std::forward(args)...); + } + template + bool SetBoolean(Args &&...args) { + return Set(std::forward(args)...); + } + template + std::string GetString(Args &&...args) { + return Get(std::forward(args)...); + } + template + std::string GetOrAddString(Args &&...args) { + return GetOrAdd(std::forward(args)...); + } + template + std::string SetString(Args &&...args) { + return Set(std::forward(args)...); + } + + template + T Set(const std::string &block, const std::string &name, const T &value, + const std::optional &docstring = std::optional{}) { + return SetPath(ParameterPath(block, name), value, docstring); + } + template + T SetPath(const std::string &path, const T &value, + const std::optional &docstring = std::optional{}) { + if (queries_.count(path) == 0) { + CheckAndUpdateQueries_(path, docstring); + } + + // We still call AddParameter_, to overwrite the origin + AddParameter_(parameters_, path, value, RecordOrigin(OriginType::SetInCode)); + + // Convert string to integer and return value + return value; + } + + template + T Get(const std::string &block, const std::string &name, + const std::optional &docstring = std::optional{}) { + return GetPath(ParameterPath(block, name), docstring); + } + template + T Get(const ParameterRef &r, Args &&...args) { + return GetPath(r.CanonicalPath(), std::forward(args)...); + } + + template + T GetPath(const std::string &path, + const std::optional &docstring = std::optional{}) { + // Check and error + // TODO(BSP) better compile-time error if type isn't supported by toml++? + // maybe || !parameters_.is() later? Maybe tweak as()? + if (!parameters_.at_path(path)) { + std::stringstream msg; + msg << "### FATAL ERROR in function [ParameterInput::GetPath]" << std::endl + << "Parameter name '" << path << "' not found"; + PARTHENON_THROW(msg); + } + + CheckAndUpdateQueries_(path, docstring); + + if constexpr (std::is_same::value) { + if (parameters_.at_path(path).is()) { + return parameters_.at_path(path).ref(); + } else { + std::stringstream msg; + msg << "### FATAL ERROR in function [ParameterInput::GetPath]" << std::endl + << "Parameter name '" << path << "' is of the wrong type" << std::endl + << "Value: " << parameters_.at_path(path) << " (" + << parameters_.at_path(path).type() << ")" << std::endl; + PARTHENON_THROW(msg); + } + } else { + if (auto val = parameters_.at_path(path).value(); val) { + return *val; + } else { + std::stringstream msg; + msg << "### FATAL ERROR in function [ParameterInput::GetPath]" << std::endl + << "Parameter name '" << path << "' is of the wrong type" << std::endl + << "Value: " << parameters_.at_path(path) << " (" + << parameters_.at_path(path).type() << ")" << std::endl; + PARTHENON_THROW(msg); + } + } + } + + auto GetTypePath(const std::string &path) const { + if (!parameters_.at_path(path)) { + std::stringstream msg; + msg << "### FATAL ERROR in function [ParameterInput::GetTypePath]" << std::endl + << "Parameter name '" << path << "' not found"; + PARTHENON_THROW(msg); + } + return parameters_.at_path(path).type(); + } + auto GetType(const std::string &block, const std::string &name) const { + return GetTypePath(ParameterPath(block, name)); + } + template + bool IsTypePath(const std::string &path) const { + if (!parameters_.at_path(path)) { + std::stringstream msg; + msg << "### FATAL ERROR in function [ParameterInput::IsTypePath]" << std::endl + << "Parameter name '" << path << "' not found"; + PARTHENON_THROW(msg); + } + return parameters_.is(path); + } + template + bool IsType(const std::string &block, const std::string &name) const { + return IsTypePath(ParameterPath(block, name)); + } + + template + T GetOrAdd(const std::string &block, const std::string &name, const T &value, + const std::optional &docstring = std::optional{}) { + return GetOrAddPath(ParameterPath(block, name), value, docstring); + } + template + T GetOrAdd(const std::string &block, const std::string &name, const T &value, + const std::vector allowed_values, + const std::optional &docstring = std::optional{}) { + return GetOrAddPath(ParameterPath(block, name), value, allowed_values, docstring); + } + template + T GetOrAdd(const std::string &block, const std::string &name, const ParameterRef &ref, + Args &&...args) { + return GetOrAddPath(ParameterPath(block, name), ref, std::forward(args)...); + } + + template + T GetOrAddPath( + const std::string &path, const T &value, + const std::optional &docstring = std::optional{}) { + CheckAndUpdateQueries_(path, value, std::vector{}, docstring); + if (!parameters_.at_path(path)) { + AddParameter_(parameters_, path, value, RecordOrigin(OriginType::Default)); + } + return GetPath(path); + } + template + T GetOrAddPath( + const std::string &path, const T &value, const std::vector allowed_values, + const std::optional &docstring = std::optional{}) { + // Check allowed values if non-empty + if (!allowed_values.empty()) CheckAllowedValues_(path, value, allowed_values); + + // Update docs with allowed values + CheckAndUpdateQueries_(path, value, allowed_values, docstring); + + return GetOrAddPath(path, value); + } + template + T GetOrAddPath(const std::string &path, const ParameterRef &ref, Args &&...args) { + auto value = GetPath(ref.CanonicalPath()); + auto ret = GetOrAddPath(path, value, std::forward(args)...); + SetQueryDependency_(path, ref); + return ret; + } + + template + std::vector + GetVector(const std::string &block, const std::string &name, + const std::optional &docstring = std::optional{}) { + return GetVectorPath(ParameterPath(block, name), docstring); + } + + template + std::vector GetVectorPath( + const std::string &path, + const std::optional &docstring = std::optional{}) { + // Check and error + // TODO(BSP) type checking of contents or singleton + if (!parameters_.at_path(path)) { + std::stringstream msg; + msg << "### FATAL ERROR in function [ParameterInput::GetVectorPath]" << std::endl + << "Parameter name '" << path << "' not found"; + PARTHENON_THROW(msg); + } + + CheckAndUpdateQueries_(path, docstring); + + std::vector ret; + // Handle single elements like 1-element arrays wherever they appear + // We have *no way* of knowing beforehand what's an array when parsing + if (!parameters_.at_path(path).is_array()) { + if constexpr (std::is_same::value) { + ret.push_back( + static_cast(parameters_.at_path(path).template ref())); + } else { + ret.push_back(parameters_.at_path(path).template ref()); + } + } else { + for (const auto &el : *parameters_.at_path(path).as_array()) { + if constexpr (std::is_same::value) { + ret.push_back(static_cast(el.template ref())); + } else { + ret.push_back(el.template ref()); + } + } + } + + return ret; + } + + template + std::vector GetOrAddVector(const std::string &block, const std::string &name, + Args &&...args) { + return GetOrAddVectorPath(ParameterPath(block, name), std::forward(args)...); + } + template + std::vector GetOrAddVectorPath( + const std::string &path, std::vector def, + const std::optional &docstring = std::optional{}) { + // Always load defaults into an array, for below + auto def_array = toml::array(); + for (auto el : def) { + def_array.push_back(el); + } + CheckAndUpdateQueries_(path, def_array, std::vector{}, + docstring); + + if (!parameters_.at_path(path)) { + InsertOrAssignPath_(parameters_, path, def_array); + UpdateQueryProvenance_(path, RecordOrigin(OriginType::Default)); + } + return GetVectorPath(path); + } + template + std::vector GetOrAddVectorPath( + const std::string &path, const ParameterRef &def, + const std::optional &docstring = std::optional{}) { + auto defval = GetVectorPath(def.CanonicalPath()); + auto ret = GetOrAddVectorPath(path, defval, docstring); + SetQueryDependency_(path, def); + return ret; + } + // TODO(BSP) SetVector/Path? + + private: + // Alloc 1MB temporarily, to avoid ever thinking about this again + static constexpr int max_input_filesize_ = 1024 * 1024; + toml::table parameters_; + // We will want to iterate through the record in lexicographic + // order, so this needs to be an ordered map + std::map queries_; + std::map pre_parsed_inputs_; + + toml::table LegacyParse(std::istream &is, const RecordOrigin &origin = RecordOrigin()); + bool LegacyParseLine(std::string line, std::string &name, std::string &value); + void Merge(toml::table &a, const toml::table &b, bool check_dups); + + void recursive_get_paths(const toml::table &a, toml::path prefix, const toml::key &key, + std::vector &paths) const; + + template class Container_t, class... extra> + void CheckAllowedValues_(const std::string &path, const T &val, + Container_t allowed) { + bool found = std::any_of(allowed.begin(), allowed.end(), + [&](const T &t) { return (t == val); }); + if (!found) { + std::stringstream msg; + msg << "### FATAL ERROR in function [ParameterInput::Get]\n" + << "Parameter '" << path << "' must be one of the following values:\n"; + for (const auto &v : allowed) { + msg << v << " "; + } + msg << std::endl; + PARTHENON_THROW(msg); + } + } + + template + inline void recursive_merge(toml::table &a, const toml::table &b, const toml::key &key, + T &&el, bool check_dups) { + if (b[key].is()) { + a.insert(key, toml::table()); + const toml::table &bchild = b[key].ref(); + toml::table &achild = a[key].ref(); + bchild.for_each([&](const toml::key &key, auto &&el) { + recursive_merge(achild, bchild, key, el, check_dups); + }); + } else { + auto [itr, success] = a.insert(key, el); + if (!success) { + if (check_dups) { + // TODO(BSP) can we print full path here instead of key? + std::stringstream msg; + msg << "### ERROR in parameter parsing\n" + << "Parameter '" << key << "' is duplicate!\n" + << "Previous definition: " << a[key] << " new definition: " << b[key] + << std::endl; + PARTHENON_THROW(msg); + } + // Insert over the existing key + a.insert_or_assign(key, el); + } + } + } + + template + void InsertOrAssignPath_(toml::table &tab, const std::string &path, const T &value) { + if (path == "") return; + // Recursively create the tables in the path + toml::path fullpath = toml::path(path); + toml::path parent = fullpath.parent(); + if (!tab.at_path(parent)) { + InsertOrAssignPath_(tab, parent.str(), toml::table()); + } + // Now we know parent exists (or is the root), so insert just the leaf key + if (parent.str() == "") { + tab.insert_or_assign(fullpath.leaf().str(), value); + } else { + tab.at_path(parent).ref().insert_or_assign(fullpath.leaf().str(), + value); + } + } + + template + void AddParameter_(toml::table &tbl, const std::string &path, const T &value, + RecordOrigin og, bool check_dups = false) { + // If it's already got a type, just add it + if constexpr (!std::is_same::value) { + InsertOrAssignPath_(tbl, path, value); + } else { + // Anything we know is a string: the code says, so, it contains quotes... + if (og.type == OriginType::Default || og.type == OriginType::SetInCode || + std::count(value.begin(), value.end(), '\"')) { + InsertOrAssignPath_(tbl, path, value); + } else { + // Otherwise, a "string" might need to be something else internally + // Parse it with toml++ and see what pops out + toml::table new_tbl; + std::string v = value; + v.erase(std::remove(v.begin(), v.end(), ' '), v.end()); + v.erase(std::remove(v.begin(), v.end(), '\''), v.end()); + if (std::count(v.begin(), v.end(), ',')) { + // Record an array by adding the necessary TOML + try { + v = std::regex_replace(v, std::regex(","), ", "); + new_tbl = toml::parse(path + " = [" + v + "]"); + } catch (const toml::parse_error &err) { + v = std::regex_replace(v, std::regex(", "), "\", \""); + new_tbl = toml::parse(path + " = [\"" + v + "\"]"); + } + } else { + // Record parameter + try { + new_tbl = toml::parse(path + " = " + v); + } catch (const toml::parse_error &err) { + try { + // If stod would have taken this, + double v_parsed = std::stod(v.c_str()); + // ...it's because of the 1.eX vs 1.0eX thing, so replace + v = std::regex_replace(v, std::regex("([0-9])[.]e([+-0-9])"), "$1.0e$2"); + // then parse + new_tbl = toml::parse(path + " = " + v); + } catch (const std::invalid_argument &err) { + new_tbl = toml::parse(path + " = \"" + v + "\""); + } catch (const toml::parse_error &err) { + new_tbl = toml::parse(path + " = \"" + v + "\""); + } + } + } + + if (tbl.empty()) { + tbl = new_tbl; + } else { + // Then merge our newly parsed (typed) values into the table + // Optionally check for duplicates depending on where called + Merge(tbl, new_tbl, check_dups); + } + } + } + + UpdateQueryProvenance_(path, og); + } + + // JMM: Using std::optional here aggressively to simplify overload + // and default parameter logic logic + template class Container_t, class... extra> + void CheckAndUpdateQueries_(const std::string &path, const std::optional &defval, + Container_t allowed_vals, + const std::optional &docstring) { + if constexpr (is_sortable_v && + !std::is_same_v && !std::is_same_v) { + if (allowed_vals.size() > 0) { + std::sort(std::begin(allowed_vals), std::end(allowed_vals)); + } + } + // Always create, but it may or may not have been requested before + QueryRecord &record = queries_[path]; + + if (record.requested) { + if (defval.has_value()) { + if (!record.default_value.has_value()) { + if (record.origin.type == OriginType::SetInCode) { + // This was set with Set* and we should respect it. Add + // the new default and move on. + record.default_value = defval.value(); + record.default_value_str = record.ToString(defval.value()); + } else { + // JMM: Forbid setting a default value after requesting but + // allow requesting without a default if a default has + // already been set. I know this is unpleasantly stateful, + // but we do this in a few places in the code. + std::stringstream msg; + msg << "Input parameter " << path + << " called previously without a default value and now called with one." + << " If a default value is used, the first call must always set one." + << std::endl; + PARTHENON_THROW(msg); + } + } else if (defval.value() != std::any_cast(record.default_value)) { + std::stringstream msg; + msg << "Input parameter " << path + << " has at least two inconsistent default values. " + << "The ones I detected are " << defval.value() << " and " + << std::any_cast(record.default_value) << std::endl; + PARTHENON_THROW(msg); + } + } + // Allowed values are checked after a query, so this function + // will be called twice: once with no allowed values and once + // with them. This check ensures that validation for allowed + // values only happens if they're both active. + if ((allowed_vals.size() > 0) && (record.allowed_values.size() > 0)) { + PARTHENON_REQUIRE_THROWS(allowed_vals.size() == record.allowed_values.size(), + "Allowed values must be consistently shaped"); + std::size_t i = 0; + for (const auto &allowed : allowed_vals) { + PARTHENON_REQUIRE_THROWS(allowed == + std::any_cast(record.allowed_values[i++]), + "Allowed values must be consistent"); + } + } else if (allowed_vals.size() > 0) { + for (const auto &allowed : allowed_vals) { + record.allowed_values.push_back(std::any(allowed)); + } + } + // if two inconsistent docstrings exist, complain + if (record.docstring.has_value() && docstring.has_value() && + (record.docstring.value() != docstring.value())) { + std::stringstream msg; + msg << "Input parameter " << path + << " has inconsistent docstrings. The strings are:\n" + << record.docstring.value() << "\nand\n" + << docstring.value() << std::endl; + PARTHENON_THROW(msg); + } else if (docstring.has_value()) { + // if the new query contains a docstring but the record does + // not, add the docstring + record.docstring = docstring; // record is a reference + } + // if the record contains a docstring but the new query does + // not, do nothing + // if neither contains a docstring, do nothing + } else { + record.requested = true; + if (defval.has_value()) { + record.default_value = defval.value(); + record.default_value_str = record.ToString(defval.value()); + } else { + record.default_value_str = ""; + } + for (const auto &allowed : allowed_vals) { + record.allowed_values.push_back(std::any(allowed)); + record.allowed_vals_str.push_back(record.ToString(allowed)); + } + record.docstring = docstring; // might be empty + } + } + template + void CheckAndUpdateQueries_(const std::string &path, + const std::optional &docstring) { + CheckAndUpdateQueries_(path, std::optional{}, std::vector{}, docstring); + } + void UpdateQueryProvenance_(const std::string path, RecordOrigin og) { + queries_[path].origin = og; + } + void SetQueryDependency_(const std::string &path, const ParameterRef &ref) { + queries_[path].default_value_str = ref.CanonicalPath(); + } +}; + +} // namespace parthenon + +// JMM: Believe it or not, this is the recommended way to overload hash functions +// See: https://en.cppreference.com/w/cpp/utility/hash +namespace std { +// We hash the string representation of parameters_, which is what gets written, +// and thus what needs to be consistent between ranks. +template <> +struct hash { + std::size_t operator()(const parthenon::ParameterInput &in) { + std::stringstream ss; + ss << in.GetAll(); + return std::hash()(ss.str()); + } +}; +} // namespace std + +#endif // INPUTS_PARAMETER_INPUT_HPP_ diff --git a/src/interface/state_descriptor.hpp b/src/interface/state_descriptor.hpp index a1519b42653ea..4d9c46854c09c 100644 --- a/src/interface/state_descriptor.hpp +++ b/src/interface/state_descriptor.hpp @@ -26,12 +26,12 @@ #include "basic_types.hpp" #include "bvals/boundary_conditions.hpp" +#include "inputs/parameter_input.hpp" #include "interface/metadata.hpp" #include "interface/params.hpp" #include "interface/sparse_pool.hpp" #include "interface/var_id.hpp" #include "outputs/output_parameters.hpp" -#include "parameter_input.hpp" #include "prolong_restrict/prolong_restrict.hpp" #include "utils/error_checking.hpp" diff --git a/src/mesh/forest/tree.cpp b/src/mesh/forest/tree.cpp index f35248275efb7..facbea7382cc8 100644 --- a/src/mesh/forest/tree.cpp +++ b/src/mesh/forest/tree.cpp @@ -27,11 +27,11 @@ #include "application_input.hpp" #include "basic_types.hpp" #include "defs.hpp" +#include "inputs/parameter_input.hpp" #include "mesh/forest/forest_topology.hpp" #include "mesh/forest/logical_coordinate_transformation.hpp" #include "mesh/forest/logical_location.hpp" #include "mesh/forest/tree.hpp" -#include "parameter_input.hpp" #include "utils/bit_hacks.hpp" #include "utils/indexer.hpp" diff --git a/src/mesh/mesh.cpp b/src/mesh/mesh.cpp index e1f50ad0df266..37464cdd6dcf3 100644 --- a/src/mesh/mesh.cpp +++ b/src/mesh/mesh.cpp @@ -3,7 +3,7 @@ // Copyright(C) 2014 James M. Stone and other code contributors // Licensed under the 3-clause BSD License, see LICENSE file for details //======================================================================================== -// (C) (or copyright) 2020-2024. Triad National Security, LLC. All rights reserved. +// (C) (or copyright) 2020-2025. Triad National Security, LLC. All rights reserved. // // This program was produced under U.S. Government contract 89233218CNA000001 for Los // Alamos National Laboratory (LANL), which is operated by Triad National Security, LLC @@ -45,6 +45,7 @@ #include "bvals/bvals.hpp" #include "defs.hpp" #include "globals.hpp" +#include "inputs/parameter_input.hpp" #include "interface/packages.hpp" #include "interface/state_descriptor.hpp" #include "interface/update.hpp" @@ -53,7 +54,6 @@ #include "mesh/meshblock.hpp" #include "outputs/restart.hpp" #include "outputs/restart_hdf5.hpp" -#include "parameter_input.hpp" #include "parthenon_arrays.hpp" #include "prolong_restrict/prolong_restrict.hpp" #include "utils/buffer_utils.hpp" @@ -65,18 +65,23 @@ Mesh::Mesh(ParameterInput *pin, ApplicationInput *app_in, Packages_t &packages, base_constructor_selector_t) : // public members: modified(true), is_restart(false), - adaptive(pin->GetOrAddString("parthenon/mesh", "refinement", "none") == "adaptive" + adaptive(pin->GetOrAddString("parthenon/mesh", "refinement", "none", + std::vector{"none", "static", "adaptive"}, + "mesh refinement mode") == "adaptive" ? true : false), - multilevel( - (adaptive || - pin->GetOrAddString("parthenon/mesh", "refinement", "none") == "static" || - pin->GetOrAddBoolean("parthenon/mesh", "multigrid", false))), - multigrid(pin->GetOrAddBoolean("parthenon/mesh", "multigrid", false)), nbnew(), - nbdel(), step_since_lb(), gflag(), packages(packages), + multilevel((adaptive || + pin->GetString("parthenon/mesh", "refinement") == "static" || + pin->GetOrAddBoolean("parthenon/mesh", "multigrid", false, + "enable a multigrid mesh"))), + multigrid(pin->GetOrAddBoolean("parthenon/mesh", "multigrid", false, + "enable a multigrid mesh")), + nbnew(), nbdel(), step_since_lb(), gflag(), packages(packages), resolved_packages(ResolvePackages(packages)), // private members: - num_mesh_threads_(pin->GetOrAddInteger("parthenon/mesh", "num_threads", 1)), + num_mesh_threads_( + pin->GetOrAddInteger("parthenon/mesh", "num_threads", 1, + "number of host threads for infrastructure; unused")), use_uniform_meshgen_fn_{true, true, true, true}, lb_flag_(true), lb_automatic_(), lb_manual_(), nslist(Globals::nranks), nblist(Globals::nranks), nref(Globals::nranks), nderef(Globals::nranks), rdisp(Globals::nranks), @@ -90,8 +95,12 @@ Mesh::Mesh(ParameterInput *pin, ApplicationInput *app_in, Packages_t &packages, // pack_size existed first) so the new value should take precedent. if (pack_size_exists && num_partitions_exists) { use_pack_size_ = false; - default_num_packs_ = pin->GetInteger("parthenon/mesh", "packs_per_rank"); - auto pack_size = pin->GetInteger("parthenon/mesh", "pack_size"); + default_num_packs_ = + pin->GetInteger("parthenon/mesh", "packs_per_rank", + "number of meshblockpacks per rank, overrides pack_size"); + auto pack_size = pin->GetInteger( + "parthenon/mesh", "pack_size", + "default size of meshblock packs on a given rank, overwritten by packs_per_rank"); bool are_both_default = (default_num_packs_ == 1) && (pack_size == -1); if (!are_both_default && (Globals::my_rank == 0)) { PARTHENON_WARN("Both pack_size and packs_per_rank set to non default values! " @@ -101,12 +110,16 @@ Mesh::Mesh(ParameterInput *pin, ApplicationInput *app_in, Packages_t &packages, } else { if (pack_size_exists) { use_pack_size_ = true; - default_pack_size_ = pin->GetInteger("parthenon/mesh", "pack_size"); + default_pack_size_ = pin->GetInteger("parthenon/mesh", "pack_size", + "default size of meshblock packs on a given " + "rank, overwritten by packs_per_rank"); // use packs_per_rank (and set default value if not set) } else { use_pack_size_ = false; - default_num_packs_ = - std::max(1, pin->GetOrAddInteger("parthenon/mesh", "packs_per_rank", 1)); + default_num_packs_ = std::max( + 1, + pin->GetOrAddInteger("parthenon/mesh", "packs_per_rank", 1, + "number of meshblockpacks per rank, overrides pack_size")); } } @@ -146,7 +159,9 @@ Mesh::Mesh(ParameterInput *pin, ApplicationInput *app_in, Packages_t &packages, root_level = 0; // SMR / AMR: if (adaptive) { - max_level = pin->GetOrAddInteger("parthenon/mesh", "numlevel", 1) + root_level - 1; + max_level = pin->GetOrAddInteger("parthenon/mesh", "numlevel", 1, + "maximum level of refinement globally") + + root_level - 1; } else { max_level = 63; } @@ -163,34 +178,13 @@ Mesh::Mesh(ParameterInput *pin, ApplicationInput *app_in, Packages_t &packages, Mesh::Mesh(ParameterInput *pin, ApplicationInput *app_in, Packages_t &packages, hyper_rectangular_constructor_selector_t) : Mesh(pin, app_in, packages, base_constructor_selector_t()) { - mesh_size = RegionSize( - {pin->GetReal("parthenon/mesh", "x1min"), pin->GetReal("parthenon/mesh", "x2min"), - pin->GetReal("parthenon/mesh", "x3min")}, - {pin->GetReal("parthenon/mesh", "x1max"), pin->GetReal("parthenon/mesh", "x2max"), - pin->GetReal("parthenon/mesh", "x3max")}, - {pin->GetOrAddReal("parthenon/mesh", "x1rat", 1.0), - pin->GetOrAddReal("parthenon/mesh", "x2rat", 1.0), - pin->GetOrAddReal("parthenon/mesh", "x3rat", 1.0)}, - {pin->GetInteger("parthenon/mesh", "nx1"), pin->GetInteger("parthenon/mesh", "nx2"), - pin->GetInteger("parthenon/mesh", "nx3")}, - {false, pin->GetInteger("parthenon/mesh", "nx2") == 1, - pin->GetInteger("parthenon/mesh", "nx3") == 1}); + + std::tie(mesh_size, base_block_size) = GetRegionSizes(pin); + SetBCNames_(pin); mesh_bcs = GetBCsFromNames_(mesh_bc_names); ndim = (mesh_size.nx(X3DIR) > 1) ? 3 : ((mesh_size.nx(X2DIR) > 1) ? 2 : 1); - for (auto &[dir, label] : std::vector>{ - {X1DIR, "nx1"}, {X2DIR, "nx2"}, {X3DIR, "nx3"}}) { - base_block_size.xrat(dir) = mesh_size.xrat(dir); - base_block_size.symmetry(dir) = mesh_size.symmetry(dir); - if (!base_block_size.symmetry(dir)) { - base_block_size.nx(dir) = - pin->GetOrAddInteger("parthenon/meshblock", label, mesh_size.nx(dir)); - } else { - base_block_size.nx(dir) = mesh_size.nx(dir); - } - } - // Load balancing flag and parameters forest = forest::Forest::HyperRectangular(mesh_size, base_block_size, mesh_bcs); root_level = forest.root_level; @@ -200,7 +194,9 @@ Mesh::Mesh(ParameterInput *pin, ApplicationInput *app_in, Packages_t &packages, // SMR / AMR: if (adaptive) { - max_level = pin->GetOrAddInteger("parthenon/mesh", "numlevel", 1) + root_level - 1; + max_level = pin->GetOrAddInteger("parthenon/mesh", "numlevel", 1, + "maximum level of refinement globally") + + root_level - 1; } else { max_level = 63; } @@ -218,17 +214,7 @@ Mesh::Mesh(ParameterInput *pin, ApplicationInput *app_in, Packages_t &packages, SetBCNames_(pin); mesh_bcs = GetBCsFromNames_(mesh_bc_names); - for (auto &[dir, label] : std::vector>{ - {X1DIR, "nx1"}, {X2DIR, "nx2"}, {X3DIR, "nx3"}}) { - base_block_size.xrat(dir) = mesh_size.xrat(dir); - base_block_size.symmetry(dir) = mesh_size.symmetry(dir); - if (!base_block_size.symmetry(dir)) { - base_block_size.nx(dir) = - pin->GetOrAddInteger("parthenon/meshblock", label, mesh_size.nx(dir)); - } else { - base_block_size.nx(dir) = mesh_size.nx(dir); - } - } + base_block_size = GetBaseMeshBlockSize(pin, mesh_size); forest_def.SetBlockSize(base_block_size); ndim = 2; @@ -346,11 +332,15 @@ void Mesh::BuildBlockList(ParameterInput *pin, ApplicationInput *app_in, #ifdef MPI_PARALLEL // check if there are sufficient blocks + const bool output_params_and_exit = + pin->GetOrAddBoolean("parthenon/job", "output_params_and_exit", false); if (nbtotal < Globals::nranks) { if (mesh_test != 0) { - std::cout << "### Warning in Mesh constructor" << std::endl - << "Too few mesh blocks: nbtotal (" << nbtotal << ") < nranks (" - << Globals::nranks << ")" << std::endl; + if ((Globals::my_rank == 0) && !output_params_and_exit) { + std::cout << "### Warning in Mesh constructor" << std::endl + << "Too few mesh blocks: nbtotal (" << nbtotal << ") < nranks (" + << Globals::nranks << ")" << std::endl; + } } } #endif @@ -762,14 +752,13 @@ void Mesh::FillDerived() { } } -//---------------------------------------------------------------------------------------- -// \!fn void Mesh::Initialize(bool init_problem, ParameterInput *pin) -// \brief initialization before the main loop - void Mesh::Initialize(bool init_problem, ParameterInput *pin, ApplicationInput *app_in) { PARTHENON_INSTRUMENT bool init_done = true; const int nb_initial = nbtotal; + const bool output_params_and_exit = + pin->GetOrAddBoolean("parthenon/job", "output_params_and_exit", false); + do { int nmb = GetNumMeshBlocksThisRank(Globals::my_rank); @@ -848,13 +837,14 @@ void Mesh::Initialize(bool init_problem, ParameterInput *pin, ApplicationInput * LoadBalancingAndAdaptiveMeshRefinement(pin, app_in); if (nbtotal == nb_before_loadbalance) { init_done = true; - } else if (nbtotal < nb_before_loadbalance && Globals::my_rank == 0) { + } else if (nbtotal < nb_before_loadbalance && Globals::my_rank == 0 && + !output_params_and_exit) { std::cout << "### Warning in Mesh::Initialize" << std::endl << "The number of MeshBlocks decreased during AMR grid initialization." << std::endl << "Possibly the refinement criteria have a problem." << std::endl; } - if (nbtotal > 2 * nb_initial && Globals::my_rank == 0) { + if (nbtotal > 2 * nb_initial && Globals::my_rank == 0 && !output_params_and_exit) { std::cout << "### Warning in Mesh::Initialize" << std::endl << "The number of MeshBlocks increased more than twice during " "initialization." @@ -867,7 +857,7 @@ void Mesh::Initialize(bool init_problem, ParameterInput *pin, ApplicationInput * #ifdef MPI_PARALLEL // check if there are sufficient blocks - if (nbtotal < Globals::nranks) { + if (nbtotal < Globals::nranks && Globals::my_rank == 0 && !output_params_and_exit) { std::stringstream msg; msg << "### FATAL ERROR in Mesh Initialize" << std::endl << "Too few mesh blocks after initialization: nbtotal (" << nbtotal @@ -944,10 +934,13 @@ ParArray1D &Mesh::GetAmrTags() { // Functionality re-used in mesh constructor void Mesh::RegisterLoadBalancing_(ParameterInput *pin) { -#ifdef MPI_PARALLEL // JMM: Not sure this ifdef is needed + // JMM: This machinery is only used with MPI, but I don't want these + // options hidden if the code is built without MPI, so there's no + // ifdef. const std::string balancer = pin->GetOrAddString("parthenon/loadbalancing", "balancer", "default", - std::vector{"default", "automatic", "manual"}); + std::vector{"default", "automatic", "manual"}, + "load balancing strategy"); if (balancer == "automatic") { // JMM: I am disabling timing based load balancing, as it's not // threaded through the infrastructure. I think some thought needs @@ -958,9 +951,11 @@ void Mesh::RegisterLoadBalancing_(ParameterInput *pin) { } else if (balancer == "manual") { lb_manual_ = true; } - lb_tolerance_ = pin->GetOrAddReal("parthenon/loadbalancing", "tolerance", 0.5); - lb_interval_ = pin->GetOrAddInteger("parthenon/loadbalancing", "interval", 10); -#endif // MPI_PARALLEL + lb_tolerance_ = pin->GetOrAddReal("parthenon/loadbalancing", "tolerance", 0.5, + "load balancer tolerance"); + lb_interval_ = pin->GetOrAddInteger( + "parthenon/loadbalancing", "interval", 10, + "how frequently load balancing is performed if the mesh does not change"); } // Create separate communicators for all variables. Needs to be done at the mesh @@ -1118,27 +1113,28 @@ void Mesh::DoStaticRefinement(ParameterInput *pin) { return std::pair{lxmin, lxmax}; }; - InputBlock *pib = pin->pfirst_block; - while (pib != nullptr) { - if (pib->block_name.compare(0, 27, "parthenon/static_refinement") == 0) { + for (auto pib : pin->Blocks("parthenon")) { + std::string block_name = std::string(pib.first); + if (block_name.compare(0, 17, "static_refinement") == 0) { RegionSize ref_size; - ref_size.xmin(X1DIR) = pin->GetReal(pib->block_name, "x1min"); - ref_size.xmax(X1DIR) = pin->GetReal(pib->block_name, "x1max"); + std::string block_path = "parthenon." + block_name; + ref_size.xmin(X1DIR) = pin->GetReal(block_path, "x1min"); + ref_size.xmax(X1DIR) = pin->GetReal(block_path, "x1max"); if (ndim >= 2) { - ref_size.xmin(X2DIR) = pin->GetReal(pib->block_name, "x2min"); - ref_size.xmax(X2DIR) = pin->GetReal(pib->block_name, "x2max"); + ref_size.xmin(X2DIR) = pin->GetReal(block_path, "x2min"); + ref_size.xmax(X2DIR) = pin->GetReal(block_path, "x2max"); } else { ref_size.xmin(X2DIR) = mesh_size.xmin(X2DIR); ref_size.xmax(X2DIR) = mesh_size.xmax(X2DIR); } if (ndim == 3) { - ref_size.xmin(X3DIR) = pin->GetReal(pib->block_name, "x3min"); - ref_size.xmax(X3DIR) = pin->GetReal(pib->block_name, "x3max"); + ref_size.xmin(X3DIR) = pin->GetReal(block_path, "x3min"); + ref_size.xmax(X3DIR) = pin->GetReal(block_path, "x3max"); } else { ref_size.xmin(X3DIR) = mesh_size.xmin(X3DIR); ref_size.xmax(X3DIR) = mesh_size.xmax(X3DIR); } - int ref_lev = pin->GetInteger(pib->block_name, "level"); + int ref_lev = pin->GetInteger(block_path, "level"); int lrlev = ref_lev + GetLegacyTreeRootLevel(); // range check if (ref_lev < 1) { @@ -1190,7 +1186,6 @@ void Mesh::DoStaticRefinement(ParameterInput *pin) { } } } - pib = pib->pnext; } } // Return list of locations and levels for the legacy tree @@ -1214,24 +1209,37 @@ Mesh::GetLevelsAndLogicalLocationsFlat() const noexcept { } void Mesh::SetBCNames_(ParameterInput *pin) { - mesh_bc_names = {pin->GetOrAddString("parthenon/mesh", "ix1_bc", "outflow"), - pin->GetOrAddString("parthenon/mesh", "ox1_bc", "outflow"), - pin->GetOrAddString("parthenon/mesh", "ix2_bc", "outflow"), - pin->GetOrAddString("parthenon/mesh", "ox2_bc", "outflow"), - pin->GetOrAddString("parthenon/mesh", "ix3_bc", "outflow"), - pin->GetOrAddString("parthenon/mesh", "ox3_bc", "outflow")}; + mesh_bc_names = { + pin->GetOrAddString("parthenon/mesh", "ix1_bc", "outflow", + "global mesh boundary condition on inner X1 face"), + pin->GetOrAddString("parthenon/mesh", "ox1_bc", "outflow", + "global mesh boundary condition on outer X1 face"), + pin->GetOrAddString("parthenon/mesh", "ix2_bc", "outflow", + "global mesh boundary condition on inner X2 face"), + pin->GetOrAddString("parthenon/mesh", "ox2_bc", "outflow", + "global mesh boundary condition on outer X2 face"), + pin->GetOrAddString("parthenon/mesh", "ix3_bc", "outflow", + "global mesh boundary condition on inner X3 face"), + pin->GetOrAddString("parthenon/mesh", "ox3_bc", "outflow", + "global mesh boundary condition on outer X3 face")}; // JMM: This is needed because not all BCs are necessarily // implemented for swarms auto maybe = [](const std::string &s) { return ((s == "outflow") || (s == "periodic")) ? s : "outflow"; }; mesh_swarm_bc_names = { - pin->GetOrAddString("parthenon/swarm", "ix1_bc", maybe(mesh_bc_names[0])), - pin->GetOrAddString("parthenon/swarm", "ox1_bc", maybe(mesh_bc_names[1])), - pin->GetOrAddString("parthenon/swarm", "ix2_bc", maybe(mesh_bc_names[2])), - pin->GetOrAddString("parthenon/swarm", "ox2_bc", maybe(mesh_bc_names[3])), - pin->GetOrAddString("parthenon/swarm", "ix3_bc", maybe(mesh_bc_names[4])), - pin->GetOrAddString("parthenon/swarm", "ox3_bc", maybe(mesh_bc_names[5]))}; + pin->GetOrAddString("parthenon/swarm", "ix1_bc", maybe(mesh_bc_names[0]), + "global particle boundary condition on inner X1 face"), + pin->GetOrAddString("parthenon/swarm", "ox1_bc", maybe(mesh_bc_names[1]), + "global particle boundary condition on outer X1 face"), + pin->GetOrAddString("parthenon/swarm", "ix2_bc", maybe(mesh_bc_names[2]), + "global particle boundary condition on inner X2 face"), + pin->GetOrAddString("parthenon/swarm", "ox2_bc", maybe(mesh_bc_names[3]), + "global particle boundary condition on outer X2 face"), + pin->GetOrAddString("parthenon/swarm", "ix3_bc", maybe(mesh_bc_names[4]), + "global particle boundary condition on inner X3 face"), + pin->GetOrAddString("parthenon/swarm", "ox3_bc", maybe(mesh_bc_names[5]), + "global particle boundary condition on outer X3 face")}; // JMM: A consequence of having only one boundary flag array but // multiple boundary function arrays is that swarms *must* be // periodic if the mesh is periodic but otherwise mesh and swarm @@ -1253,4 +1261,48 @@ Mesh::GetBCsFromNames_(const std::array &names) co return out; } +RegionSize Mesh::GetBaseMeshBlockSize(ParameterInput *pin, const RegionSize &mesh_size) { + RegionSize base_block_size; + for (auto &[dir, label] : std::vector>{ + {X1DIR, "nx1"}, {X2DIR, "nx2"}, {X3DIR, "nx3"}}) { + base_block_size.xrat(dir) = mesh_size.xrat(dir); + base_block_size.symmetry(dir) = mesh_size.symmetry(dir); + // JMM: Just to ensure it gets written to params + ParameterRef meshref("parthenon/mesh", label); + base_block_size.nx(dir) = pin->GetOrAddInteger( + "parthenon/meshblock", label, meshref, + "logical size of a meshblock; defaults to the base size of the mesh"); + if (base_block_size.symmetry(dir)) { + base_block_size.nx(dir) = mesh_size.nx(dir); + pin->SetInteger("parthenon/meshblock", label, mesh_size.nx(dir)); + } + } + return base_block_size; +} + +std::pair Mesh::GetRegionSizes(ParameterInput *pin) { + RegionSize mesh_size( + {pin->GetReal("parthenon/mesh", "x1min", "minimum x1 value of domain"), + pin->GetReal("parthenon/mesh", "x2min", "minimum x2 value of domain"), + pin->GetReal("parthenon/mesh", "x3min", "minimum x3 value of domain")}, + {pin->GetReal("parthenon/mesh", "x1max", "maximum x1 value of domain"), + pin->GetReal("parthenon/mesh", "x2max", "maximum x2 value of domain"), + pin->GetReal("parthenon/mesh", "x3max", "maximum x3 value of domain")}, + {pin->GetOrAddReal("parthenon/mesh", "x1rat", 1.0, "unused"), + pin->GetOrAddReal("parthenon/mesh", "x2rat", 1.0, "unused"), + pin->GetOrAddReal("parthenon/mesh", "x3rat", 1.0, "unused")}, + {pin->GetInteger("parthenon/mesh", "nx1", + "number of cells on base mesh in x1 direction"), + pin->GetInteger("parthenon/mesh", "nx2", + "number of cells on base mesh in x2 direction"), + pin->GetInteger("parthenon/mesh", "nx3", + "number of cells on base mesh in x3 direction")}, + {false, pin->GetInteger("parthenon/mesh", "nx2") == 1, + pin->GetInteger("parthenon/mesh", "nx3") == 1}); + + RegionSize base_block_size = GetBaseMeshBlockSize(pin, mesh_size); + + return std::make_pair(mesh_size, base_block_size); +} + } // namespace parthenon diff --git a/src/mesh/mesh.hpp b/src/mesh/mesh.hpp index f850d419c8444..bcc61870cad6f 100644 --- a/src/mesh/mesh.hpp +++ b/src/mesh/mesh.hpp @@ -40,6 +40,7 @@ #include "coordinates/coordinates.hpp" #include "defs.hpp" #include "domain.hpp" +#include "inputs/parameter_input.hpp" #include "interface/data_collection.hpp" #include "interface/mesh_data.hpp" #include "interface/state_descriptor.hpp" @@ -49,7 +50,6 @@ #include "mesh/meshblock_pack.hpp" #include "outputs/io_wrapper.hpp" #include "pack/pack_descriptor.hpp" -#include "parameter_input.hpp" #include "parthenon_arrays.hpp" #include "utils/communication_buffer.hpp" #include "utils/hash.hpp" @@ -96,6 +96,9 @@ class Mesh { Packages_t &packages, int test_flag = 0); Mesh(ParameterInput *pin, ApplicationInput *app_in, Packages_t &packages, forest::ForestDefinition &forest_def); + static RegionSize GetBaseMeshBlockSize(ParameterInput *pin, + const RegionSize &mesh_size); + static std::pair GetRegionSizes(ParameterInput *pin); ~Mesh(); // accessors diff --git a/src/mesh/mesh_refinement.cpp b/src/mesh/mesh_refinement.cpp index 23cd952794d72..8d09cca897f82 100644 --- a/src/mesh/mesh_refinement.cpp +++ b/src/mesh/mesh_refinement.cpp @@ -3,7 +3,7 @@ // Copyright(C) 2014 James M. Stone and other code contributors // Licensed under the 3-clause BSD License, see LICENSE file for details //======================================================================================== -// (C) (or copyright) 2020-2022. Triad National Security, LLC. All rights reserved. +// (C) (or copyright) 2020-2025. Triad National Security, LLC. All rights reserved. // // This program was produced under U.S. Government contract 89233218CNA000001 for Los // Alamos National Laboratory (LANL), which is operated by Triad National Security, LLC @@ -36,11 +36,11 @@ #include "coordinates/coordinates.hpp" #include "defs.hpp" #include "globals.hpp" +#include "inputs/parameter_input.hpp" #include "interface/variable.hpp" #include "mesh/mesh.hpp" #include "mesh/mesh_refinement.hpp" #include "mesh/meshblock.hpp" -#include "parameter_input.hpp" #include "parthenon_arrays.hpp" #include "prolong_restrict/pr_loops.hpp" #include "prolong_restrict/prolong_restrict.hpp" @@ -54,7 +54,10 @@ namespace parthenon { MeshRefinement::MeshRefinement(std::weak_ptr pmb, ParameterInput *pin) : pmy_block_(pmb), deref_count_(0), - deref_threshold_(pin->GetOrAddInteger("parthenon/mesh", "derefine_count", 10)) { + deref_threshold_( + pin->GetOrAddInteger("parthenon/mesh", "derefine_count", 10, + "number of iterations a block must " + "request derefinement before it is derefined")) { // Create coarse mesh object for parent grid coarse_coords = Coordinates_t(pmb.lock()->coords, 2); diff --git a/src/mesh/meshblock.cpp b/src/mesh/meshblock.cpp index 0bccf068576dc..3938b29d29a5f 100644 --- a/src/mesh/meshblock.cpp +++ b/src/mesh/meshblock.cpp @@ -33,6 +33,7 @@ #include "coordinates/coordinates.hpp" #include "defs.hpp" #include "globals.hpp" +#include "inputs/parameter_input.hpp" #include "interface/metadata.hpp" #include "interface/state_descriptor.hpp" #include "interface/variable.hpp" @@ -40,7 +41,6 @@ #include "mesh/mesh.hpp" #include "mesh/mesh_refinement.hpp" #include "mesh/meshblock.hpp" -#include "parameter_input.hpp" #include "parthenon_arrays.hpp" #include "utils/buffer_utils.hpp" diff --git a/src/mesh/meshblock.hpp b/src/mesh/meshblock.hpp index 4e8d7f18ff36d..0868b40489d25 100644 --- a/src/mesh/meshblock.hpp +++ b/src/mesh/meshblock.hpp @@ -32,6 +32,7 @@ #include "defs.hpp" #include "domain.hpp" #include "globals.hpp" +#include "inputs/parameter_input.hpp" #include "interface/data_collection.hpp" #include "interface/meshblock_data.hpp" #include "interface/packages.hpp" @@ -39,7 +40,6 @@ #include "kokkos_abstraction.hpp" #include "mesh/forest/forest.hpp" #include "outputs/io_wrapper.hpp" -#include "parameter_input.hpp" #include "parthenon_arrays.hpp" namespace parthenon { @@ -435,8 +435,7 @@ class MeshBlock : public std::enable_shared_from_this { private: // data - Real new_block_dt_, new_block_dt_hyperbolic_, new_block_dt_parabolic_, - new_block_dt_user_; + Real new_block_dt_, new_block_dt_hyperbolic_, new_block_dt_parabolic_; std::vector>> vars_cc_; // Initializer to set up a meshblock called with the default constructor diff --git a/src/outputs/histogram.cpp b/src/outputs/histogram.cpp index 4b189b48a4039..44cc803e64920 100644 --- a/src/outputs/histogram.cpp +++ b/src/outputs/histogram.cpp @@ -21,8 +21,8 @@ #include "basic_types.hpp" #include "config.hpp" #include "globals.hpp" +#include "inputs/parameter_input.hpp" #include "kokkos_abstraction.hpp" -#include "parameter_input.hpp" #include "parthenon_array_generic.hpp" // Only proceed if HDF5 output enabled diff --git a/src/outputs/output_utils.cpp b/src/outputs/output_utils.cpp index 5d6064ad35d44..4f69e665be6be 100644 --- a/src/outputs/output_utils.cpp +++ b/src/outputs/output_utils.cpp @@ -25,6 +25,7 @@ #include "defs.hpp" #include "globals.hpp" +#include "inputs/parameter_input.hpp" #include "interface/metadata.hpp" #include "interface/swarm.hpp" #include "interface/swarm_container.hpp" @@ -33,7 +34,6 @@ #include "mesh/mesh_refinement.hpp" #include "mesh/meshblock.hpp" #include "outputs/output_utils.hpp" -#include "parameter_input.hpp" namespace parthenon { namespace OutputUtils { diff --git a/src/outputs/outputs.cpp b/src/outputs/outputs.cpp index d74ca54de294c..fa1b502fc41f7 100644 --- a/src/outputs/outputs.cpp +++ b/src/outputs/outputs.cpp @@ -72,11 +72,11 @@ #include "coordinates/coordinates.hpp" #include "defs.hpp" #include "globals.hpp" +#include "inputs/parameter_input.hpp" #include "mesh/mesh.hpp" #include "mesh/meshblock.hpp" #include "outputs/output_parameters.hpp" #include "pack/swarm_default_names.hpp" -#include "parameter_input.hpp" #include "parthenon_arrays.hpp" #include "utils/error_checking.hpp" #include "utils/utils.hpp" @@ -121,13 +121,14 @@ Outputs::Outputs(Mesh *pm, ParameterInput *pin, SimTime *tm) { Real dt = 0.0; // default value == 0 means that initial data is written by default int dn = -1; if (tm != nullptr) { - dn = pin->GetOrAddInteger(op.block_name, "dn", -1); + dn = pin->GetOrAddInteger(op.block_name, "dn", -1, "output cadence in cycles"); // If this is a dn controlled output (dn >= 0), soft disable dt based triggering // (-> dt = -1), otherwise setting dt to tlim ensures a final output is also // written for temporal drivers. const auto tlim = dn >= 0 ? -1.0 : tm->tlim; - dt = pin->GetOrAddReal(op.block_name, "dt", tlim); + dt = + pin->GetOrAddReal(op.block_name, "dt", tlim, "output cadence in physical time"); } // if this output is "soft-disabled" (negative value) skip processing if (dt < 0.0 && dn < 0) { @@ -181,17 +182,21 @@ Outputs::Outputs(Mesh *pm, ParameterInput *pin, SimTime *tm) { // set file number, basename, id, and format op.file_number = std::max((*pfile_numbers)[iinput], 0); - op.file_basename = pin->GetOrAddString("parthenon/job", "problem_id", "parthenon"); + op.file_basename = pin->GetOrAddString("parthenon/job", "problem_id", "parthenon", + "prefix for output files"); op.file_number_width = pin->GetOrAddInteger(op.block_name, "file_number_width", 5); - op.file_label_final = pin->GetOrAddBoolean(op.block_name, "use_final_label", true); + op.file_label_final = pin->GetOrAddBoolean( + op.block_name, "use_final_label", true, + "final output will use the word final instead of a number for its index"); char define_id[10]; std::snprintf(define_id, sizeof(define_id), "out%d", op.block_number); // default id="outN" op.file_id = pin->GetOrAddString(op.block_name, "id", define_id); - op.file_type = pin->GetString(op.block_name, "file_type"); + op.file_type = pin->GetString(op.block_name, "file_type", "output type"); // read ghost cell option - op.include_ghost_zones = pin->GetOrAddBoolean(op.block_name, "ghost_zones", false); + op.include_ghost_zones = pin->GetOrAddBoolean( + op.block_name, "ghost_zones", false, "whether or not ghost zones are output"); // read cartesian mapping option op.cartesian_vector = false; @@ -206,8 +211,10 @@ Outputs::Outputs(Mesh *pm, ParameterInput *pin, SimTime *tm) { op.single_precision_output = pin->GetOrAddBoolean(op.block_name, "single_precision_output", false); op.sparse_seed_nans = - pin->GetOrAddBoolean(op.block_name, "sparse_seed_nans", false); - op.meshdata_name = pin->GetOrAddString(op.block_name, "meshdata_name", "base"); + pin->GetOrAddBoolean(op.block_name, "sparse_seed_nans", false, + "write non-allocated sparse data as NaN"); + op.meshdata_name = pin->GetOrAddString(op.block_name, "meshdata_name", "base", + "which meshdata object to write from"); } else { op.single_precision_output = false; op.sparse_seed_nans = false; @@ -265,13 +272,14 @@ Outputs::Outputs(Mesh *pm, ParameterInput *pin, SimTime *tm) { (op.file_type != "corehdf5") && (op.file_type != "ascent") && (op.file_type != "histogram")) { op.variables = pin->GetOrAddVector(block_names[iinput], "variables", - std::vector()); + std::vector(), + "variables to output"); // JMM: If the requested var isn't present for a given swarm, // it is simply not output. op.swarms.clear(); // Not sure this is needed if (pin->DoesParameterExist(block_names[iinput], "swarms")) { - std::vector swarmnames = - pin->GetVector(block_names[iinput], "swarms"); + std::vector swarmnames = pin->GetVector( + block_names[iinput], "swarms", "swarms to output"); std::size_t nswarms = swarmnames.size(); if ((pin->DoesParameterExist(block_names[iinput], "swarm_variables")) && (nswarms > 1)) { @@ -285,12 +293,14 @@ Outputs::Outputs(Mesh *pm, ParameterInput *pin, SimTime *tm) { for (const auto &swname : swarmnames) { if (pin->DoesParameterExist(block_names[iinput], "swarm_variables")) { auto varnames = - pin->GetVector(block_names[iinput], "swarm_variables"); + pin->GetVector(block_names[iinput], "swarm_variables", + "swarm variables to output for all swarms"); op.swarms[swname].insert(varnames.begin(), varnames.end()); } if (pin->DoesParameterExist(block_names[iinput], swname + "_variables")) { - auto varnames = - pin->GetVector(block_names[iinput], swname + "_variables"); + auto varnames = pin->GetVector( + block_names[iinput], swname + "_variables", + "swarm variables to output for a specific swarm"); op.swarms[swname].insert(varnames.begin(), varnames.end()); } // Always output id, x, y, and z for swarms so that they work with vis tools. diff --git a/src/outputs/outputs_package.cpp b/src/outputs/outputs_package.cpp index a97b2ccaf7056..d76aea6beca6a 100644 --- a/src/outputs/outputs_package.cpp +++ b/src/outputs/outputs_package.cpp @@ -25,9 +25,9 @@ #include #include +#include "inputs/parameter_input.hpp" #include "interface/state_descriptor.hpp" #include "outputs/outputs_package.hpp" -#include "parameter_input.hpp" namespace parthenon { @@ -36,7 +36,8 @@ namespace OutputsPackage { std::shared_ptr Initialize(ParameterInput *pin) { auto pkg = std::make_shared("Outputs"); - std::string basename = pin->GetOrAddString("parthenon/job", "problem_id", "parthenon"); + std::string basename = pin->GetOrAddString("parthenon/job", "problem_id", "parthenon", + "prefix for output files"); std::vector block_names; std::vector block_numbers; @@ -45,12 +46,12 @@ std::shared_ptr Initialize(ParameterInput *pin) { std::vector last_times; std::vector last_ns; - // loop over input block names. Find those that start with "parthenon/output", read + // loop over input block names. Find those in the parthenon.output blocks, read // parameters, and construct singly linked list of OutputTypes. - for (InputBlock *pib = pin->pfirst_block; pib != nullptr; pib = pib->pnext) { - if (pib->block_name.compare(0, 16, "parthenon/output") == 0) { - std::string outn = pib->block_name.substr(16); // 6 because counting starts at 0! - std::string block_name = pib->block_name; + for (auto pib : pin->Blocks("parthenon")) { + std::string block_name = std::string(pib.first); + if (block_name.compare(0, 6, "output") == 0) { + std::string outn = block_name.substr(6); // 6 because counting starts at 0! if (pin->DoesParameterExist(block_name, "next_time")) { std::stringstream msg; @@ -68,7 +69,7 @@ std::shared_ptr Initialize(ParameterInput *pin) { } // these are used for book-keeping - block_names.push_back(block_name); + block_names.push_back("parthenon." + block_name); block_numbers.push_back(atoi(outn.c_str())); // These will be updated later or restarted from diff --git a/src/parameter_input.cpp b/src/parameter_input.cpp deleted file mode 100644 index 9b898b5edac02..0000000000000 --- a/src/parameter_input.cpp +++ /dev/null @@ -1,928 +0,0 @@ -//======================================================================================== -// Athena++ astrophysical MHD code -// Copyright(C) 2014 James M. Stone and other code contributors -// Licensed under the 3-clause BSD License, see LICENSE file for details -//======================================================================================== -// (C) (or copyright) 2020-2024. Triad National Security, LLC. All rights reserved. -// -// This program was produced under U.S. Government contract 89233218CNA000001 for Los -// Alamos National Laboratory (LANL), which is operated by Triad National Security, LLC -// for the U.S. Department of Energy/National Nuclear Security Administration. All rights -// in the program are reserved by Triad National Security, LLC, and the U.S. Department -// of Energy/National Nuclear Security Administration. The Government is granted for -// itself and others acting on its behalf a nonexclusive, paid-up, irrevocable worldwide -// license in this material to reproduce, prepare derivative works, distribute copies to -// the public, perform publicly and display publicly, and to permit others to do so. -//======================================================================================== -//! \file parameter_input.cpp -// \brief implementation of functions in class ParameterInput -// -// PURPOSE: Member functions of this class are used to read and parse the input file. -// Functionality is loosely modeled after FORTRAN namelist. -// -// EXAMPLE of input file in 'Athena++' format: -// # block name; must be on a line by itself -// # everything after a hash symbol is a comment and is ignored -// name1=value # each parameter name must be on a line by itself -// name2 = value1 # whitespace around the = is optional -// # blank lines are OK -// # my comment here comment lines are OK -// # name3 = value3 values (and blocks) that are commented out are ignored -// -// # start new block -// name1 = value1 # note that same parameter names can appear in different blocks -// name2 = value2 # empty lines (like following) are OK -// -// # same blockname can re-appear, although NOT recommended -// name3 = value3 # this would be the 3rd parameter name in blockname1 -// name1 = value4 # if parameter name is repeated, previous value is overwritten! -// -// LIMITATIONS: -// - parameter specification (name=val #comment) must all be on a single line -// -// HISTORY: -// - Nov 2002: Created for Athena1.0/Cambridge release by Peter Teuben -// - 2003-2008: Many improvements and extensions by T. Gardiner and J.M. Stone -// - Jan 2014: Rewritten in C++ for the Athena++ code by J.M. Stone -//======================================================================================== - -#include "parameter_input.hpp" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "globals.hpp" -#include "utils/error_checking.hpp" - -namespace parthenon { - -//---------------------------------------------------------------------------------------- -// ParameterInput constructor - -ParameterInput::ParameterInput() : pfirst_block{}, last_filename_{} {} - -ParameterInput::ParameterInput(std::string input_filename) - : pfirst_block{}, last_filename_{} { - IOWrapper infile; - infile.Open(input_filename.c_str(), IOWrapper::FileMode::read); - LoadFromFile(infile); - infile.Close(); -} - -// ParameterInput destructor- iterates through nested singly linked lists of blocks/lines -// and deletes each InputBlock node (whose destructor below deletes linked list "line" -// nodes) - -ParameterInput::~ParameterInput() { - InputBlock *pib = pfirst_block; - while (pib != nullptr) { - InputBlock *pold_block = pib; - pib = pib->pnext; - delete pold_block; - } -} - -// InputBlock destructor- iterates through singly linked list of "line" nodes and deletes -// them - -InputBlock::~InputBlock() { - InputLine *pil = pline; - while (pil != nullptr) { - InputLine *pold_line = pil; - pil = pil->pnext; - delete pold_line; - } -} - -//---------------------------------------------------------------------------------------- -//! \fn void ParameterInput::LoadFromStream(std::istream &is) -// \brief Load input parameters from a stream - -// Input block names are allocated and stored in a singly linked list of InputBlocks. -// Within each InputBlock the names, values, and comments of each parameter are allocated -// and stored in a singly linked list of InputLines. - -void ParameterInput::LoadFromStream(std::istream &is) { - std::string line, block_name, param_name, param_value, param_comment; - std::size_t first_char, last_char; - std::stringstream msg; - InputBlock *pib{}; - int line_num{-1}, blocks_found{0}; - - // Buffer multiple lines if a continuation character is present - std::string multiline_name, multiline_value, multiline_comment; - // Status in/out of continuation - bool continuing = false; - - while (is.good()) { - std::getline(is, line); - line_num++; - - // remove all \t\f\n\r\v but leave pure spaces - line.erase(std::remove_if(line.begin(), line.end(), - [](char c) { return std::isspace(c) && c != ' '; }), - line.end()); - - if (line.empty()) continue; // skip blank line - first_char = line.find_first_not_of(" "); // skip white space - if (first_char == std::string::npos) continue; // line is all white space - if (line.compare(first_char, 1, "#") == 0) continue; // skip comments - if (line.compare(first_char, 9, "") == 0) break; // stop on - - if (line.compare(first_char, 1, "<") == 0) { // a new block - if (continuing) { - msg << "### FATAL ERROR in function [ParameterInput::LoadFromStream]" << std::endl - << "Multiline field ended unexpectedly with new block " - << "character <. Look above this line for the error:" << std::endl - << line << std::endl - << std::endl; - PARTHENON_FAIL(msg); - } - first_char++; - last_char = (line.find_first_of(">", first_char)); - block_name.assign(line, first_char, last_char - 1); // extract block name - - if (last_char == std::string::npos) { - msg << "### FATAL ERROR in function [ParameterInput::LoadFromStream]" << std::endl - << "Block name '" << block_name << "' in the input stream'" - << "' not properly ended"; - PARTHENON_FAIL(msg); - } - - pib = FindOrAddBlock(block_name); // find or add block to singly linked list - - if (pib == nullptr) { - msg << "### FATAL ERROR in function [ParameterInput::LoadFromStream]" << std::endl - << "Block name '" << block_name << "' could not be found/added"; - PARTHENON_FAIL(msg); - } - blocks_found++; - continue; // skip to next line if block name was found - } // end "a new block was found" - - // if line does not contain a block name or skippable information (comments, - // whitespace), it must contain a parameter value - if (blocks_found == 0) { - msg << "### FATAL ERROR in function [ParameterInput::LoadFromStream]" << std::endl - << "Input file must specify a block name before the first" - << " parameter = value line"; - PARTHENON_FAIL(msg); - } - // parse line and add name/value/comment strings (if found) to current block name - bool has_cont_char = ParseLine(pib, line, param_name, param_value, param_comment); - if (continuing || has_cont_char) { - // Append line data - multiline_name += param_name; - multiline_value += param_value; - multiline_comment += param_comment; - // Set new state - continuing = true; - } - - if (continuing && !has_cont_char) { - // Flush line data - param_name = multiline_name; - param_value = multiline_value; - param_comment = multiline_comment; - multiline_name = ""; - multiline_value = ""; - multiline_comment = ""; - // Set new state - continuing = false; - } - - if (!continuing) { - if (param_name != "") { - AddParameter(pib, param_name, param_value, param_comment); - } - } - } - return; -} - -//---------------------------------------------------------------------------------------- -//! \fn void ParameterInput::LoadFromFile(IOWrapper &input) -// \brief Read the parameters from an input file or restarting file. -// Return the position at the end of the header, which is used in restarting - -void ParameterInput::LoadFromFile(IOWrapper &input) { - std::stringstream par, msg; - constexpr int kBufSize = 4096; - char buf[kBufSize]; - IOWrapperSizeT header = 0, ret, loc; - - // search or EOF. - do { - if (Globals::my_rank == 0) // only the master process reads the header from the file - ret = input.Read(buf, sizeof(char), kBufSize); -#ifdef MPI_PARALLEL - // then broadcasts it - PARTHENON_MPI_CHECK( - MPI_Bcast(&ret, sizeof(IOWrapperSizeT), MPI_BYTE, 0, MPI_COMM_WORLD)); - PARTHENON_MPI_CHECK(MPI_Bcast(buf, ret, MPI_BYTE, 0, MPI_COMM_WORLD)); -#endif - par.write(buf, ret); // add the buffer into the stream - header += ret; - std::string sbuf = par.str(); // create string for search - loc = sbuf.find("", 0); // search from the top of the stream - if (loc != std::string::npos) { // found - header = loc + 10; // store the header length - break; - } - if (header > kBufSize * 10) { - msg << "### FATAL ERROR in function [ParameterInput::LoadFromFile]" - << " is not found in the first 40KBytes." << std::endl - << "Probably the file is broken or a wrong file is specified" << std::endl; - PARTHENON_FAIL(msg); - } - } while (ret == kBufSize); // till EOF (or par_end is found) - - // Now par contains the parameter inputs + some additional including - // Read the stream and load the parameters - LoadFromStream(par); - // Seek the file to the end of the header - input.Seek(header); - - return; -} - -//---------------------------------------------------------------------------------------- -//! \fn InputBlock* ParameterInput::FindOrAddBlock(const std::string & name) -// \brief find or add specified InputBlock. Returns pointer to block. - -InputBlock *ParameterInput::FindOrAddBlock(const std::string &name) { - InputBlock *pib, *plast; - plast = pfirst_block; - pib = pfirst_block; - - // Search singly linked list of InputBlocks to see if name exists, return if found. - while (pib != nullptr) { - if (name.compare(pib->block_name) == 0) return pib; - plast = pib; - pib = pib->pnext; - } - - // Create new block in list if not found above - pib = new InputBlock; - pib->block_name.assign(name); // store the new block name - pib->pline = nullptr; // Terminate the InputLine list - pib->pnext = nullptr; // Terminate the InputBlock list - - // Default max lengths to zero (in case of no parameters in this block) - pib->max_len_parname = 0; - pib->max_len_parvalue = 0; - - // if this is the first block in list, save pointer to it in class - if (pfirst_block == nullptr) { - pfirst_block = pib; - } else { - plast->pnext = pib; // link new node into list - } - - return pib; -} - -//---------------------------------------------------------------------------------------- -//! \fn void ParameterInput::ParseLine(InputBlock *pib, std::string line, -// std::string& name, std::string& value, std::string& comment) -// \brief parse "name = value # comment" format, return name/value/comment strings. - -bool ParameterInput::ParseLine(InputBlock *pib, std::string line, std::string &name, - std::string &value, std::string &comment) { - std::size_t first_char, last_char, equal_char, hash_char, cont_char, len; - bool continuation = false; - - hash_char = line.find_first_of("#"); // find "#" (optional) - comment = ""; - if (hash_char != std::string::npos) { - comment = line.substr(hash_char); - line.erase(hash_char, std::string::npos); - } - - first_char = line.find_first_not_of(" "); // find first non-white space - equal_char = line.find_first_of("="); // find "=" char - - // copy substring into name, remove white space at end of name - if (equal_char == std::string::npos) { - name = ""; - line.erase(0, first_char); - } else { - len = equal_char - first_char; - name.assign(line, first_char, len); - last_char = name.find_last_not_of(" "); - name.erase(last_char + 1, std::string::npos); - line.erase(0, equal_char + 1); - } - - cont_char = line.find_first_of("&"); // find "&" continuation character - // copy substring into value, remove white space at start and end - len = cont_char; - if (cont_char != std::string::npos) { - std::string right_of_cont; - right_of_cont.assign(line, cont_char + 1, std::string::npos); - first_char = right_of_cont.find_first_not_of(" "); - if (first_char != std::string::npos) { - throw std::runtime_error("ERROR: Non-comment characters are not permitted to the " - "right of line continuations"); - } - continuation = true; - } - value.assign(line, 0, len); - - first_char = value.find_first_not_of(" "); - value.erase(0, first_char); - - last_char = value.find_last_not_of(" "); - value.erase(last_char + 1, std::string::npos); - - return continuation; -} - -//---------------------------------------------------------------------------------------- -//! \fn void ParameterInput::AddParameter(InputBlock *pb, const std::string & name, -// std::string value, const std::string & comment) -// \brief add name/value/comment tuple to the InputLine singly linked list in block *pb. -// If a parameter with the same name already exists, the value and comment strings -// are replaced (overwritten). - -void ParameterInput::AddParameter(InputBlock *pb, const std::string &name, - const std::string &value, const std::string &comment) { - InputLine *pl, *plast; - // Search singly linked list of InputLines to see if name exists. This also sets *plast - // to point to the tail node (but not storing a pointer to the tail node in InputBlock) - pl = pb->pline; - plast = pb->pline; - while (pl != nullptr) { - if (name.compare(pl->param_name) == 0) { // param name already exists - pl->param_value.assign(value); // replace existing param value - pl->param_comment.assign(comment); // replace exisiting param comment - if (value.length() > pb->max_len_parvalue) pb->max_len_parvalue = value.length(); - return; - } - plast = pl; - pl = pl->pnext; - } - - // Create new node in singly linked list if name does not already exist - pl = new InputLine; - pl->param_name.assign(name); - pl->param_value.assign(value); - pl->param_comment.assign(comment); - pl->pnext = nullptr; - - // if this is the first parameter in list, save pointer to it in block. - if (pb->pline == nullptr) { - pb->pline = pl; - pb->max_len_parname = name.length(); - pb->max_len_parvalue = value.length(); - } else { - plast->pnext = pl; // link new node into list - if (name.length() > pb->max_len_parname) pb->max_len_parname = name.length(); - if (value.length() > pb->max_len_parvalue) pb->max_len_parvalue = value.length(); - } - - return; -} - -//---------------------------------------------------------------------------------------- -//! void ParameterInput::ModifyFromCmdline(int argc, char *argv[]) -// \brief parse commandline for changes to input parameters -// Note this function is very forgiving (no warnings!) if there is an error in format - -void ParameterInput::ModifyFromCmdline(int argc, char *argv[]) { - std::string input_text, block, name, value; - std::stringstream msg; - InputBlock *pb; - InputLine *pl; - - for (int i = 1; i < argc; i++) { - input_text = argv[i]; - std::size_t equal_posn = input_text.find_first_of("="); // first "=" character - std::size_t slash_posn = input_text.rfind("/", equal_posn); // last "/" before "=" - - if (slash_posn > equal_posn) { - msg << "'/' used as value (rhs of =) when modifying " << input_text << "." - << " Please update value of change " - << "logic in ModifyFromCmdline function."; - PARTHENON_FAIL(msg.str().c_str()); - } - - // skip if either "/" or "=" do not exist in input - if ((slash_posn == std::string::npos) || (equal_posn == std::string::npos)) continue; - - // extract block/name/value strings - block = input_text.substr(0, slash_posn); - name = input_text.substr(slash_posn + 1, (equal_posn - slash_posn - 1)); - value = input_text.substr(equal_posn + 1, std::string::npos); - - // get pointer to node with same block name in singly linked list of InputBlocks - pb = GetPtrToBlock(block); - if (pb == nullptr) { - if (Globals::my_rank == 0) { - msg << "In function [ParameterInput::ModifyFromCmdline]:" << std::endl - << " Block name '" << block - << "' on command line not found in input/restart file. Block will be added."; - PARTHENON_WARN(msg); - } - pb = FindOrAddBlock(block); - } - - // get pointer to node with same parameter name in singly linked list of InputLines - pl = pb->GetPtrToLine(name); - if (pl == nullptr) { - if (Globals::my_rank == 0) { - msg << "In function [ParameterInput::ModifyFromCmdline]:" << std::endl - << " Parameter '" << name << "' in block '" << block - << "' on command line not found in input/restart file. Parameter will be " - "added."; - PARTHENON_WARN(msg); - } - AddParameter(pb, name, value, " # Added from command line"); - - } else { - pl->param_value.assign(value); // replace existing value - } - - if (value.length() > pb->max_len_parvalue) pb->max_len_parvalue = value.length(); - } -} - -//---------------------------------------------------------------------------------------- -//! \fn InputBlock* ParameterInput::GetPtrToBlock(const std::string & name) -// \brief return pointer to specified InputBlock if it exists - -InputBlock *ParameterInput::GetPtrToBlock(const std::string &name) { - InputBlock *pb; - for (pb = pfirst_block; pb != nullptr; pb = pb->pnext) { - if (name.compare(pb->block_name) == 0) return pb; - } - return nullptr; -} - -//---------------------------------------------------------------------------------------- -//! \fn int ParameterInput::DoesParameterExist(const std::string & block, const -//! std::string & name) -// \brief check whether parameter of given name in given block exists - -int ParameterInput::DoesParameterExist(const std::string &block, - const std::string &name) { - InputLine *pl; - InputBlock *pb; - pb = GetPtrToBlock(block); - if (pb == nullptr) return 0; - pl = pb->GetPtrToLine(name); - return (pl == nullptr ? 0 : 1); -} - -//---------------------------------------------------------------------------------------- -//! \fn int ParameterInput::DoesBlockExist(const std::string & block) -// \brief check whether block exists - -int ParameterInput::DoesBlockExist(const std::string &block) { - InputBlock *pb = GetPtrToBlock(block); - if (pb == nullptr) return 0; - return 1; -} - -std::string ParameterInput::GetComment(const std::string &block, - const std::string &name) { - InputBlock *pb; - InputLine *pl; - std::stringstream msg; - - // get pointer to node with same block name in singly linked list of InputBlocks - pb = GetPtrToBlock(block); - if (pb == nullptr) { - msg << "### FATAL ERROR in function [ParameterInput::GetComment]" << std::endl - << "Block name '" << block << "' not found when trying to set value " - << "for parameter '" << name << "'"; - PARTHENON_FAIL(msg); - } - - // get pointer to node with same parameter name in singly linked list of InputLines - pl = pb->GetPtrToLine(name); - if (pl == nullptr) { - msg << "### FATAL ERROR in function [ParameterInput::GetComment]" << std::endl - << "Parameter name '" << name << "' not found in block '" << block << "'"; - PARTHENON_FAIL(msg); - } - - std::string val = pl->param_comment; - return val; -} - -//---------------------------------------------------------------------------------------- -//! \fn int ParameterInput::GetInteger(const std::string & block, const std::string & -//! name) -// \brief returns integer value of string stored in block/name - -int ParameterInput::GetInteger(const std::string &block, const std::string &name) { - InputBlock *pb; - InputLine *pl; - std::stringstream msg; - - // get pointer to node with same block name in singly linked list of InputBlocks - pb = GetPtrToBlock(block); - if (pb == nullptr) { - msg << "### FATAL ERROR in function [ParameterInput::GetInteger]" << std::endl - << "Block name '" << block << "' not found when trying to set value " - << "for parameter '" << name << "'"; - PARTHENON_FAIL(msg); - } - - // get pointer to node with same parameter name in singly linked list of InputLines - pl = pb->GetPtrToLine(name); - if (pl == nullptr) { - msg << "### FATAL ERROR in function [ParameterInput::GetInteger]" << std::endl - << "Parameter name '" << name << "' not found in block '" << block << "'"; - PARTHENON_FAIL(msg); - } - - std::string val = pl->param_value; - - // Convert string to integer and return value - return stoi(val); -} - -//---------------------------------------------------------------------------------------- -//! \fn Real ParameterInput::GetReal(const std::string & block, const std::string & name) -// \brief returns real value of string stored in block/name - -Real ParameterInput::GetReal(const std::string &block, const std::string &name) { - InputBlock *pb; - InputLine *pl; - std::stringstream msg; - - // get pointer to node with same block name in singly linked list of InputBlocks - pb = GetPtrToBlock(block); - if (pb == nullptr) { - msg << "### FATAL ERROR in function [ParameterInput::GetReal]" << std::endl - << "Block name '" << block << "' not found when trying to set value " - << "for parameter '" << name << "'"; - PARTHENON_FAIL(msg); - } - - // get pointer to node with same parameter name in singly linked list of InputLines - pl = pb->GetPtrToLine(name); - if (pl == nullptr) { - msg << "### FATAL ERROR in function [ParameterInput::GetReal]" << std::endl - << "Parameter name '" << name << "' not found in block '" << block << "'"; - PARTHENON_FAIL(msg); - } - - std::string val = pl->param_value; - - // Convert string to real and return value - return static_cast(atof(val.c_str())); -} - -//---------------------------------------------------------------------------------------- -//! \fn bool ParameterInput::GetBoolean(const std::string & block, const std::string & -//! name) -// \brief returns boolean value of string stored in block/name - -bool ParameterInput::GetBoolean(const std::string &block, const std::string &name) { - InputBlock *pb; - InputLine *pl; - std::stringstream msg; - - // get pointer to node with same block name in singly linked list of InputBlocks - pb = GetPtrToBlock(block); - if (pb == nullptr) { - msg << "### FATAL ERROR in function [ParameterInput::GetBoolean]" << std::endl - << "Block name '" << block << "' not found when trying to set value " - << "for parameter '" << name << "'"; - PARTHENON_FAIL(msg); - } - - // get pointer to node with same parameter name in singly linked list of InputLines - pl = pb->GetPtrToLine(name); - if (pl == nullptr) { - msg << "### FATAL ERROR in function [ParameterInput::GetBoolean]" << std::endl - << "Parameter name '" << name << "' not found in block '" << block << "'"; - PARTHENON_FAIL(msg); - } - - std::string val = pl->param_value; - return stob(val); -} - -//---------------------------------------------------------------------------------------- -//! \fn std::string ParameterInput::GetString(const std::string & block, const std::string -//! & name) -// \brief returns string stored in block/name - -std::string ParameterInput::GetString(const std::string &block, const std::string &name) { - InputBlock *pb; - InputLine *pl; - std::stringstream msg; - - // get pointer to node with same block name in singly linked list of InputBlocks - pb = GetPtrToBlock(block); - if (pb == nullptr) { - msg << "### FATAL ERROR in function [ParameterInput::GetString]" << std::endl - << "Block name '" << block << "' not found when trying to set value " - << "for parameter '" << name << "'"; - PARTHENON_FAIL(msg); - } - - // get pointer to node with same parameter name in singly linked list of InputLines - pl = pb->GetPtrToLine(name); - if (pl == nullptr) { - msg << "### FATAL ERROR in function [ParameterInput::GetString]" << std::endl - << "Parameter name '" << name << "' not found in block '" << block << "'"; - PARTHENON_FAIL(msg); - } - - std::string val = pl->param_value; - - // return value - return val; -} - -std::string ParameterInput::GetString(const std::string &block, const std::string &name, - const std::vector &allowed_values) { - auto val = GetString(block, name); - CheckAllowedValues_(block, name, val, allowed_values); - return val; -} - -//---------------------------------------------------------------------------------------- -//! \fn int ParameterInput::GetOrAddInteger(const std::string & block, const std::string & -//! name, -// int default_value) -// \brief returns integer value stored in block/name if it exists, or creates and sets -// value to def_value if it does not exist - -int ParameterInput::GetOrAddInteger(const std::string &block, const std::string &name, - int def_value) { - InputBlock *pb; - InputLine *pl; - std::stringstream ss_value; - int ret; - - if (DoesParameterExist(block, name)) { - pb = GetPtrToBlock(block); - pl = pb->GetPtrToLine(name); - std::string val = pl->param_value; - ret = stoi(val); - } else { - pb = FindOrAddBlock(block); - ss_value << def_value; - AddParameter(pb, name, ss_value.str(), "# Default value added at run time"); - ret = def_value; - } - return ret; -} - -//---------------------------------------------------------------------------------------- -//! \fn Real ParameterInput::GetOrAddReal(const std::string & block, const std::string & -//! name, -// Real def_value) -// \brief returns real value stored in block/name if it exists, or creates and sets -// value to def_value if it does not exist - -Real ParameterInput::GetOrAddReal(const std::string &block, const std::string &name, - Real def_value) { - InputBlock *pb; - InputLine *pl; - std::stringstream ss_value; - Real ret; - - if (DoesParameterExist(block, name)) { - pb = GetPtrToBlock(block); - pl = pb->GetPtrToLine(name); - std::string val = pl->param_value; - ret = static_cast(atof(val.c_str())); - } else { - pb = FindOrAddBlock(block); - static_assert(sizeof(Real) <= sizeof(double), "Real is greater than double!"); - ss_value.precision(std::numeric_limits::max_digits10); - ss_value << def_value; - AddParameter(pb, name, ss_value.str(), "# Default value added at run time"); - ret = def_value; - } - return ret; -} - -//---------------------------------------------------------------------------------------- -//! \fn bool ParameterInput::GetOrAddBoolean(const std::string & block, const std::string -//! & name, -// bool def_value) -// \brief returns boolean value stored in block/name if it exists, or creates and sets -// value to def_value if it does not exist - -bool ParameterInput::GetOrAddBoolean(const std::string &block, const std::string &name, - bool def_value) { - InputBlock *pb; - InputLine *pl; - std::stringstream ss_value; - bool ret; - - if (DoesParameterExist(block, name)) { - pb = GetPtrToBlock(block); - pl = pb->GetPtrToLine(name); - std::string val = pl->param_value; - if (val.compare(0, 1, "0") == 0 || val.compare(0, 1, "1") == 0) { - ret = static_cast(stoi(val)); - } else { - std::transform(val.begin(), val.end(), val.begin(), ::tolower); - std::istringstream is(val); - is >> std::boolalpha >> ret; - } - } else { - pb = FindOrAddBlock(block); - ss_value << def_value; - AddParameter(pb, name, ss_value.str(), "# Default value added at run time"); - ret = def_value; - } - return ret; -} - -//---------------------------------------------------------------------------------------- -//! \fn std::string ParameterInput::GetOrAddString(const std::string & block, const -//! std::string & name, -// const std::string & def_value) -// \brief returns string value stored in block/name if it exists, or creates and sets -// value to def_value if it does not exist - -std::string ParameterInput::GetOrAddString(const std::string &block, - const std::string &name, - const std::string &def_value) { - InputBlock *pb; - InputLine *pl; - std::stringstream ss_value; - std::string ret; - - if (DoesParameterExist(block, name)) { - pb = GetPtrToBlock(block); - pl = pb->GetPtrToLine(name); - ret = pl->param_value; - } else { - pb = FindOrAddBlock(block); - AddParameter(pb, name, def_value, "# Default value added at run time"); - ret = def_value; - } - return ret; -} -std::string -ParameterInput::GetOrAddString(const std::string &block, const std::string &name, - const std::string &def_value, - const std::vector &allowed_values) { - auto val = GetOrAddString(block, name, def_value); - CheckAllowedValues_(block, name, val, allowed_values); - return val; -} - -//---------------------------------------------------------------------------------------- -//! \fn int ParameterInput::SetInteger(const std::string & block, const std::string & -//! name, int value) -// \brief updates an integer parameter; creates it if it does not exist - -int ParameterInput::SetInteger(const std::string &block, const std::string &name, - int value) { - InputBlock *pb; - std::stringstream ss_value; - - pb = FindOrAddBlock(block); - ss_value << value; - AddParameter(pb, name, ss_value.str(), "# Updated during run time"); - return value; -} - -//---------------------------------------------------------------------------------------- -//! \fn Real ParameterInput::SetReal(const std::string & block, const std::string & -//! name, Real value) -// \brief updates a real parameter with full precision; creates it if it does not exist - -Real ParameterInput::SetReal(const std::string &block, const std::string &name, - Real value) { - InputBlock *pb; - std::stringstream ss_value; - - pb = FindOrAddBlock(block); - static_assert(sizeof(Real) <= sizeof(double), "Real is greater than double!"); - ss_value.precision(std::numeric_limits::max_digits10); - ss_value << value; - AddParameter(pb, name, ss_value.str(), "# Updated during run time"); - return value; -} - -//---------------------------------------------------------------------------------------- -//! \fn bool ParameterInput::SetBoolean(const std::string & block, const std::string & -//! name, bool value) -// \brief updates a boolean parameter; creates it if it does not exist - -bool ParameterInput::SetBoolean(const std::string &block, const std::string &name, - bool value) { - InputBlock *pb; - std::stringstream ss_value; - - pb = FindOrAddBlock(block); - ss_value << value; - AddParameter(pb, name, ss_value.str(), "# Updated during run time"); - return value; -} - -//---------------------------------------------------------------------------------------- -//! \fn std::string ParameterInput::SetString(const std::string & block, const std::string -//! & name, -// std::string value) -// \brief updates a string parameter; creates it if it does not exist - -std::string ParameterInput::SetString(const std::string &block, const std::string &name, - const std::string &value) { - InputBlock *pb; - - pb = FindOrAddBlock(block); - AddParameter(pb, name, value, "# Updated during run time"); - return value; -} - -void ParameterInput::CheckRequired(const std::string &block, const std::string &name) { - bool missing = true; - if (DoesParameterExist(block, name)) { - missing = (GetComment(block, name) == "# Default value added at run time"); - } - if (missing) { - std::stringstream ss; - ss << std::endl - << "### ERROR in CheckRequired:" << std::endl - << "Parameter file missing required field <" << block << ">/" << name << std::endl - << std::endl; - throw std::runtime_error(ss.str()); - } -} - -void ParameterInput::CheckDesired(const std::string &block, const std::string &name) { - bool missing = true; - bool defaulted = false; - if (DoesParameterExist(block, name)) { - missing = false; - defaulted = (GetComment(block, name) == "# Default value added at run time"); - } - if (missing) { - std::cout << std::endl - << "### WARNING in CheckDesired:" << std::endl - << "Parameter file missing desired field <" << block << ">/" << name - << std::endl; - } - if (defaulted) { - std::cout << std::endl - << "Defaulting to <" << block << ">/" << name << " = " - << GetString(block, name) << std::endl; - } -} - -//---------------------------------------------------------------------------------------- -//! \fn void ParameterInput::ParameterDump(std::ostream& os) -// \brief output entire InputBlock/InputLine hierarchy to specified stream - -void ParameterInput::ParameterDump(std::ostream &os) { - InputBlock *pb; - InputLine *pl; - std::string param_name, param_value; - std::size_t len; - - os << "#------------------------- PAR_DUMP -------------------------" << std::endl; - - for (pb = pfirst_block; pb != nullptr; pb = pb->pnext) { // loop over InputBlocks - os << "<" << pb->block_name << ">" << std::endl; // write block name - for (pl = pb->pline; pl != nullptr; pl = pl->pnext) { // loop over InputLines - param_name.assign(pl->param_name); - param_value.assign(pl->param_value); - - len = pb->max_len_parname - param_name.length() + 1; - param_name.append(len, ' '); // pad name to align vertically - len = pb->max_len_parvalue - param_value.length() + 1; - param_value.append(len, ' '); // pad value to align vertically - - os << param_name << "= " << param_value << pl->param_comment << std::endl; - } - } - - os << "#------------------------- PAR_DUMP -------------------------" << std::endl; - os << "" << std::endl; // finish with par-end (useful in restart files) -} - -//---------------------------------------------------------------------------------------- -//! \fn InputLine* InputBlock::GetPtrToLine(std::string name) -// \brief return pointer to InputLine containing specified parameter if it exists - -InputLine *InputBlock::GetPtrToLine(std::string name) { - for (InputLine *pl = pline; pl != nullptr; pl = pl->pnext) { - if (name.compare(pl->param_name) == 0) return pl; - } - return nullptr; -} - -} // namespace parthenon diff --git a/src/parameter_input.hpp b/src/parameter_input.hpp deleted file mode 100644 index c3d5b9a32ebbd..0000000000000 --- a/src/parameter_input.hpp +++ /dev/null @@ -1,291 +0,0 @@ -//======================================================================================== -// (C) (or copyright) 2020-2025. Triad National Security, LLC. All rights reserved. -// -// This program was produced under U.S. Government contract 89233218CNA000001 for Los -// Alamos National Laboratory (LANL), which is operated by Triad National Security, LLC -// for the U.S. Department of Energy/National Nuclear Security Administration. All rights -// in the program are reserved by Triad National Security, LLC, and the U.S. Department -// of Energy/National Nuclear Security Administration. The Government is granted for -// itself and others acting on its behalf a nonexclusive, paid-up, irrevocable worldwide -// license in this material to reproduce, prepare derivative works, distribute copies to -// the public, perform publicly and display publicly, and to permit others to do so. -//======================================================================================== -// Athena++ astrophysical MHD code -// Copyright(C) 2014 James M. Stone and other code contributors -// Licensed under the 3-clause BSD License, see LICENSE file for details -//======================================================================================== - -#ifndef PARAMETER_INPUT_HPP_ -#define PARAMETER_INPUT_HPP_ -//! \file parameter_input.hpp -// \brief definition of class ParameterInput -// Contains data structures used to store, and functions used to access, parameters -// read from the input file. See comments at start of parameter_input.cpp for more -// information on the Athena++ input file format. - -#include -#include -#include -#include -#include // for std::forward -#include - -#include "config.hpp" -#include "defs.hpp" -#include "outputs/io_wrapper.hpp" -#include "utils/hash.hpp" -#include "utils/string_utils.hpp" - -namespace parthenon { - -//---------------------------------------------------------------------------------------- -//! \struct InputLine -// \brief node in a singly linked list of parameters contained within 1x input block - -struct InputLine { - std::string param_name; - std::string param_value; // value of the parameter is stored as a string! - std::string param_comment; - InputLine *pnext; // pointer to the next node in this nested singly linked list -}; - -//---------------------------------------------------------------------------------------- -//! \class InputBlock -// \brief node in a singly linked list of all input blocks contained within input file - -class InputBlock { - public: - InputBlock() = default; - ~InputBlock(); - - // data - std::string block_name; - std::size_t max_len_parname; // length of longest param_name, for nice-looking output - std::size_t max_len_parvalue; // length of longest param_value, to format outputs - InputBlock *pnext; // pointer to the next node in InputBlock singly linked list - - InputLine *pline; // pointer to head node in nested singly linked list (in this block) - // (not storing a reference to the tail node) - - // functions - InputLine *GetPtrToLine(std::string name); -}; - -//---------------------------------------------------------------------------------------- -//! \class ParameterInput -// \brief data and definitions of functions used to store and access input parameters -// Functions are implemented in parameter_input.cpp - -class ParameterInput { - friend class std::hash; - - public: - // constructor/destructor - ParameterInput(); - explicit ParameterInput(std::string input_filename); - ~ParameterInput(); - - // data - InputBlock *pfirst_block; // pointer to head node in singly linked list of InputBlock - // (not storing a reference to the tail node) - - // functions - void LoadFromStream(std::istream &is); - void LoadFromFile(IOWrapper &input); - void ModifyFromCmdline(int argc, char *argv[]); - void ParameterDump(std::ostream &os); - int DoesParameterExist(const std::string &block, const std::string &name); - int DoesBlockExist(const std::string &block); - std::string GetComment(const std::string &block, const std::string &name); - int GetInteger(const std::string &block, const std::string &name); - int GetOrAddInteger(const std::string &block, const std::string &name, int value); - int SetInteger(const std::string &block, const std::string &name, int value); - Real GetReal(const std::string &block, const std::string &name); - Real GetOrAddReal(const std::string &block, const std::string &name, Real value); - Real SetReal(const std::string &block, const std::string &name, Real value); - bool GetBoolean(const std::string &block, const std::string &name); - bool GetOrAddBoolean(const std::string &block, const std::string &name, bool value); - bool SetBoolean(const std::string &block, const std::string &name, bool value); - - std::string GetString(const std::string &block, const std::string &name); - std::string GetOrAddString(const std::string &block, const std::string &name, - const std::string &value); - std::string SetString(const std::string &block, const std::string &name, - const std::string &value); - std::string GetString(const std::string &block, const std::string &name, - const std::vector &allowed_values); - std::string GetOrAddString(const std::string &block, const std::string &name, - const std::string &value, - const std::vector &allowed_values); - void CheckRequired(const std::string &block, const std::string &name); - void CheckDesired(const std::string &block, const std::string &name); - - template - T GetOrAdd(const std::string &block, const std::string &name, const T &value, - Args &&...args) { - if constexpr (std::is_same_v) { - return GetOrAddBoolean(block, name, value, std::forward(args)...); - } else if constexpr (std::is_same_v) { - return GetOrAddReal(block, name, value, std::forward(args)...); - } else if constexpr (std::is_same_v) { - return GetOrAddString(block, name, value, std::forward(args)...); - } else if (std::is_integral_v) { - return GetOrAddInteger(block, name, value, std::forward(args)...); - } else { - PARTHENON_THROW("Unknown type\n"); - } - } - template - T Get(const std::string &block, const std::string &name, Args &&...args) { - if constexpr (std::is_same_v) { - return GetOrAddBoolean(block, name, std::forward(args)...); - } else if constexpr (std::is_same_v) { - return GetReal(block, name, std::forward(args)...); - } else if constexpr (std::is_same_v) { - return GetString(block, name, std::forward(args)...); - } else if (std::is_integral_v) { - return GetInteger(block, name, std::forward(args)...); - } else { - PARTHENON_THROW("Unknown type\n"); - } - } - - template - std::vector GetVector(const std::string &block, const std::string &name, - Args &&...args) { - std::vector fields = GetVector_(block, name); - if constexpr (std::is_same::value) return fields; - - std::vector ret; - for (auto &f : fields) { - if constexpr (std::is_same::value) { - ret.push_back(stoi(f)); - } else if constexpr (std::is_same::value) { - ret.push_back(atof(f.c_str())); - } else if constexpr (std::is_same::value) { - ret.push_back(stob(f)); - } - } - return ret; - } - template - std::vector GetOrAddVector(const std::string &block, const std::string &name, - std::vector def) { - if (DoesParameterExist(block, name)) return GetVector(block, name); - - std::string cname = ConcatVector_(def); - auto *pb = FindOrAddBlock(block); - AddParameter(pb, name, cname, "# Default value added at run time"); - return def; - } - - private: - std::string last_filename_; // last input file opened, to prevent duplicate reads - - InputBlock *FindOrAddBlock(const std::string &name); - InputBlock *GetPtrToBlock(const std::string &name); - bool ParseLine(InputBlock *pib, std::string line, std::string &name, std::string &value, - std::string &comment); - void AddParameter(InputBlock *pib, const std::string &name, const std::string &value, - const std::string &comment); - bool stob(std::string val) { - // check is string contains integers 0 or 1 (instead of true or false) and return - if (val.compare(0, 1, "0") == 0 || val.compare(0, 1, "1") == 0) { - return static_cast(stoi(val)); - } - - // convert string to all lower case - std::transform(val.begin(), val.end(), val.begin(), ::tolower); - // Convert string to bool and return value - bool b; - std::istringstream is(val); - is >> std::boolalpha >> b; - return b; - } - template class Container_t, class... extra> - void CheckAllowedValues_(const std::string &block, const std::string &name, - const T &val, Container_t allowed) { - bool found = std::any_of(allowed.begin(), allowed.end(), - [&](const T &t) { return (t == val); }); - if (!found) { - std::stringstream msg; - msg << "### FATAL ERROR in function [ParameterInput::Get*]\n" - << "Parameter '" << name << "/" << block - << "' must be one of the following values:\n"; - for (const auto &v : allowed) { - msg << v << " "; - } - msg << std::endl; - PARTHENON_THROW(msg); - } - } - std::vector GetVector_(const std::string &block, const std::string &name) { - std::string s = GetString(block, name); - std::string delimiter = ","; - size_t pos = 0; - std::string token; - std::vector variables; - while ((pos = s.find(delimiter)) != std::string::npos) { - token = s.substr(0, pos); - variables.push_back(string_utils::trim(token)); - s.erase(0, pos + delimiter.length()); - } - variables.push_back(string_utils::trim(s)); - return variables; - } - template - std::string ConcatVector_(std::vector &vec) { - std::stringstream ss; - const int n = vec.size(); - if (n == 0) return ""; - - ss << vec[0]; - for (int i = 1; i < n; i++) { - ss << "," << vec[i]; - } - return ss.str(); - } -}; -} // namespace parthenon - -// JMM: Believe it or not, this is the recommended way to overload hash functions -// See: https://en.cppreference.com/w/cpp/utility/hash -namespace std { -template <> -struct hash { - std::size_t operator()(const parthenon::InputLine &il) { - return parthenon::impl::hash_combine(0, il.param_name, il.param_value, - il.param_comment); - } -}; - -template <> -struct hash { - std::size_t operator()(const parthenon::InputBlock &ib) { - using parthenon::impl::hash_combine; - std::size_t out = - hash_combine(0, ib.block_name, ib.max_len_parname, ib.max_len_parvalue); - for (parthenon::InputLine *pline = ib.pline; pline != nullptr; pline = pline->pnext) { - out = hash_combine(out, *pline); - } - return out; - } -}; - -template <> -struct hash { - std::size_t operator()(const parthenon::ParameterInput &in) { - using parthenon::InputBlock; - using parthenon::impl::hash_combine; - std::size_t out = 0; - out = hash_combine(out, in.last_filename_); - for (InputBlock *pblock = in.pfirst_block; pblock != nullptr; - pblock = pblock->pnext) { - out = hash_combine(out, *pblock); - } - return out; - } -}; -} // namespace std - -#endif // PARAMETER_INPUT_HPP_ diff --git a/src/parthenon/driver.hpp b/src/parthenon/driver.hpp index eced2c6684d3a..5964041bc4ed2 100644 --- a/src/parthenon/driver.hpp +++ b/src/parthenon/driver.hpp @@ -20,12 +20,12 @@ #include #include #include +#include #include #include #include #include #include -#include #include #include #include diff --git a/src/parthenon/package.hpp b/src/parthenon/package.hpp index 7f9fe274ee52d..80c8964c2e294 100644 --- a/src/parthenon/package.hpp +++ b/src/parthenon/package.hpp @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -32,7 +33,6 @@ #include #include #include -#include #include #include #include diff --git a/src/parthenon_manager.cpp b/src/parthenon_manager.cpp index 8f862132926b1..12e341c27e559 100644 --- a/src/parthenon_manager.cpp +++ b/src/parthenon_manager.cpp @@ -18,6 +18,7 @@ #include "parthenon_manager.hpp" #include +#include #include #include #include @@ -33,6 +34,8 @@ #include "config.hpp" #include FS_HEADER #include "globals.hpp" +#include "inputs/inputs_package.hpp" +#include "inputs/parameter_input.hpp" #include "mesh/domain.hpp" #include "mesh/meshblock.hpp" #include "outputs/output_utils.hpp" @@ -97,6 +100,7 @@ ParthenonStatus ParthenonManager::ParthenonInitEnv(int argc, char *argv[]) { // Populate the ParameterInput object. // If restart, then ParameterInput in the restart file takes precedence. + pinput = std::make_unique(); if (arg.res_flag != 0) { // Read input from restart file if (fs::path(arg.restart_filename).extension() == ".rhdf") { @@ -106,24 +110,16 @@ ParthenonStatus ParthenonManager::ParthenonInitEnv(int argc, char *argv[]) { } // Load input stream - pinput = std::make_unique(); auto inputString = restartReader->GetInputString(); std::istringstream is(inputString); - pinput->LoadFromStream(is); + pinput->LoadFromStream( + is, RecordOrigin(RecordOrigin::Type::Restart, arg.restart_filename)); } // If an input file was provided + // TODO(BSP) loop for multiple inputs if (arg.input_filename != nullptr) { - // Modify info read from restart file - if (arg.res_flag != 0) { - IOWrapper infile; - infile.Open(arg.input_filename, IOWrapper::FileMode::read); - pinput->LoadFromFile(infile); - infile.Close(); - - // Populate new object for fresh simulation - } else { - pinput = std::make_unique(arg.input_filename); - } + // Modify info with new parameters from input + pinput->LoadFile(arg.input_filename); } // Modify based on command line inputs @@ -139,7 +135,8 @@ ParthenonStatus ParthenonManager::ParthenonInitEnv(int argc, char *argv[]) { pinput->SetBoolean("parthenon/job", "run_only_analysis", arg.analysis_flag); // Set the global number of ghost zones - Globals::nghost = pinput->GetOrAddInteger("parthenon/mesh", "nghost", 2); + Globals::nghost = pinput->GetOrAddInteger("parthenon/mesh", "nghost", 2, + "number of ghost zones on a block"); // set sparse config Globals::sparse_config.enabled = pinput->GetOrAddBoolean( @@ -185,6 +182,7 @@ void ParthenonManager::ParthenonInitPackagesAndMesh( // always add the Refinement package packages.Add(Refinement::Initialize(pinput.get())); packages.Add(OutputsPackage::Initialize(pinput.get())); + packages.Add(InputsPackage::Initialize(pinput.get())); if (forest_def) { pmesh = std::make_unique(pinput.get(), app_input.get(), packages, *forest_def); } else if (arg.res_flag == 0) { @@ -227,6 +225,11 @@ void ParthenonManager::ParthenonInitPackagesAndMesh( exit(0); } + if (arg.param_flag) { + pinput->SetBoolean("parthenon/job", "output_params_and_exit", true); + pinput->SetString("parthenon/job", "output_params_block_regex", arg.params_regex); + } + pmesh->Initialize(!IsRestart(), pinput.get(), app_input.get()); ChangeRunDir(arg.prundir); diff --git a/src/parthenon_manager.hpp b/src/parthenon_manager.hpp index 2f05f671b1b0b..3231915436879 100644 --- a/src/parthenon_manager.hpp +++ b/src/parthenon_manager.hpp @@ -20,16 +20,16 @@ #include #include "application_input.hpp" -#include "argument_parser.hpp" #include "basic_types.hpp" #include "driver/driver.hpp" +#include "inputs/argument_parser.hpp" +#include "inputs/parameter_input.hpp" #include "interface/state_descriptor.hpp" #include "interface/swarm.hpp" #include "mesh/domain.hpp" #include "mesh/forest/forest_topology.hpp" #include "mesh/mesh.hpp" #include "outputs/restart.hpp" -#include "parameter_input.hpp" #include "utils/utils.hpp" namespace parthenon { diff --git a/src/pgen/default_pgen.cpp b/src/pgen/default_pgen.cpp index 52aa4582cf1b8..292d9d478f399 100644 --- a/src/pgen/default_pgen.cpp +++ b/src/pgen/default_pgen.cpp @@ -3,7 +3,7 @@ // Copyright(C) 2014 James M. Stone and other code contributors // Licensed under the 3-clause BSD License, see LICENSE file for details //======================================================================================== -// (C) (or copyright) 2020-2024. Triad National Security, LLC. All rights reserved. +// (C) (or copyright) 2020-2025. Triad National Security, LLC. All rights reserved. // // This program was produced under U.S. Government contract 89233218CNA000001 for Los // Alamos National Laboratory (LANL), which is operated by Triad National Security, LLC @@ -18,10 +18,12 @@ // \brief Provides default versions of user callbacks that loop over per-package // functions. +// TODO(JMM): Does this file serve any purpose anymore? + #include "defs.hpp" +#include "inputs/parameter_input.hpp" #include "mesh/mesh.hpp" #include "mesh/meshblock.hpp" -#include "parameter_input.hpp" #include "parthenon_arrays.hpp" namespace parthenon { diff --git a/src/time_integration/butcher_integrator.cpp b/src/time_integration/butcher_integrator.cpp index 40096dd1eeba2..8199e625e3f05 100644 --- a/src/time_integration/butcher_integrator.cpp +++ b/src/time_integration/butcher_integrator.cpp @@ -15,7 +15,7 @@ #include #include "basic_types.hpp" -#include "parameter_input.hpp" +#include "inputs/parameter_input.hpp" #include "staged_integrator.hpp" namespace parthenon { diff --git a/src/time_integration/low_storage_integrator.cpp b/src/time_integration/low_storage_integrator.cpp index 4d30e70fa97c6..cf6cbdd82a706 100644 --- a/src/time_integration/low_storage_integrator.cpp +++ b/src/time_integration/low_storage_integrator.cpp @@ -15,7 +15,7 @@ #include #include "basic_types.hpp" -#include "parameter_input.hpp" +#include "inputs/parameter_input.hpp" #include "staged_integrator.hpp" namespace parthenon { diff --git a/src/time_integration/staged_integrator.cpp b/src/time_integration/staged_integrator.cpp index 22b96342703ad..686e4080f6013 100644 --- a/src/time_integration/staged_integrator.cpp +++ b/src/time_integration/staged_integrator.cpp @@ -15,7 +15,6 @@ #include #include "basic_types.hpp" -#include "parameter_input.hpp" #include "staged_integrator.hpp" namespace parthenon { diff --git a/src/time_integration/staged_integrator.hpp b/src/time_integration/staged_integrator.hpp index e2dbe570150a3..1ee929844918e 100644 --- a/src/time_integration/staged_integrator.hpp +++ b/src/time_integration/staged_integrator.hpp @@ -18,7 +18,7 @@ #include #include "basic_types.hpp" -#include "parameter_input.hpp" +#include "inputs/parameter_input.hpp" namespace parthenon { class StagedIntegrator { diff --git a/src/utils/sort.hpp b/src/utils/sort.hpp index a4ab8139dab35..c2f09d30b6a58 100644 --- a/src/utils/sort.hpp +++ b/src/utils/sort.hpp @@ -28,9 +28,21 @@ #endif #include +#include namespace parthenon { +template +struct is_sortable : std::false_type {}; + +template +struct is_sortable()), + std::end(std::declval())))>> + : std::true_type {}; + +template +constexpr bool is_sortable_v = is_sortable::value; + // Returns the upper bound (or the array size if value has not been found) // Could/Should be replaced with a Kokkos std version once available (currently schedule // for 4.2 release). diff --git a/tst/unit/test_coordinates.cpp b/tst/unit/test_coordinates.cpp index 0e0d94039df73..3f4aabe2ee67c 100644 --- a/tst/unit/test_coordinates.cpp +++ b/tst/unit/test_coordinates.cpp @@ -21,7 +21,7 @@ #include "basic_types.hpp" #include "defs.hpp" #include "globals.hpp" -#include "parameter_input.hpp" +#include "inputs/parameter_input.hpp" using Real = double; using parthenon::ParameterInput; using parthenon::RegionSize; diff --git a/tst/unit/test_parameter_input.cpp b/tst/unit/test_parameter_input.cpp index d401269d9b038..cc583b5c0eed2 100644 --- a/tst/unit/test_parameter_input.cpp +++ b/tst/unit/test_parameter_input.cpp @@ -24,7 +24,7 @@ #include -#include "parameter_input.hpp" +#include "inputs/parameter_input.hpp" using parthenon::ParameterInput; @@ -39,11 +39,11 @@ TEST_CASE("Test required/desired checking from inputs", "[ParameterInput]") { << "" << std::endl << "var3 = 3" << std::endl << "# comment" << std::endl - << "var4 = 4" << std::endl - << "var_default = 5 # Default value added at run time" << std::endl; + << "var4 = 4" << std::endl; std::istringstream s(ss.str()); in.LoadFromStream(s); + in.GetOrAddInteger("block2", "var_default", 5); // capture all std::cout std::stringstream cout_cap; @@ -74,10 +74,10 @@ TEST_CASE("Test required/desired checking from inputs", "[ParameterInput]") { std::stringstream ss; ss << std::endl << "### WARNING in CheckDesired:" << std::endl - << "Parameter file missing desired field /var2" << std::endl + << "Parameter file missing desired field block2.var2" << std::endl << std::endl << "### WARNING in CheckDesired:" << std::endl - << "Parameter file missing desired field /var4" << std::endl; + << "Parameter file missing desired field block3.var4" << std::endl; REQUIRE(cout_cap.str() == ss.str()); } } diff --git a/tst/unit/test_state_descriptor.cpp b/tst/unit/test_state_descriptor.cpp index f3336b03992d7..23259be809016 100644 --- a/tst/unit/test_state_descriptor.cpp +++ b/tst/unit/test_state_descriptor.cpp @@ -24,12 +24,12 @@ #include "basic_types.hpp" #include "defs.hpp" +#include "inputs/parameter_input.hpp" #include "interface/metadata.hpp" #include "interface/packages.hpp" #include "interface/sparse_pool.hpp" #include "interface/state_descriptor.hpp" #include "interface/variable.hpp" -#include "parameter_input.hpp" #include "prolong_restrict/pr_ops.hpp" #include "prolong_restrict/prolong_restrict.hpp" diff --git a/tst/unit/test_unit_integrators.cpp b/tst/unit/test_unit_integrators.cpp index 834526d455036..32c339705f972 100644 --- a/tst/unit/test_unit_integrators.cpp +++ b/tst/unit/test_unit_integrators.cpp @@ -26,7 +26,7 @@ #include #include "basic_types.hpp" -#include "parameter_input.hpp" +#include "inputs/parameter_input.hpp" #include "time_integration/staged_integrator.hpp" using parthenon::ButcherIntegrator; diff --git a/tst/unit/test_unit_params.cpp b/tst/unit/test_unit_params.cpp index fad51c0695382..9a95a72176aa6 100644 --- a/tst/unit/test_unit_params.cpp +++ b/tst/unit/test_unit_params.cpp @@ -22,10 +22,10 @@ #include "basic_types.hpp" #include "config.hpp" +#include "inputs/parameter_input.hpp" #include "interface/params.hpp" #include "kokkos_abstraction.hpp" #include "outputs/parthenon_hdf5.hpp" -#include "parameter_input.hpp" using parthenon::Params; using parthenon::Real;