Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 88 additions & 0 deletions src/Base/ParmParse.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@
#include <AMReX_IntVect.H>
#include <AMReX_ParmParse.H>

#include <functional>
#include <iostream>
#include <string>
#include <string_view>
#include <vector>


Expand Down Expand Up @@ -103,5 +105,91 @@ void init_ParmParse(py::module &m)
)

// TODO: dumpTable, hasUnusedInputs, getUnusedInputs, getEntries

.def(
"to_dict",
[](ParmParse &pp) {
py::dict d;

auto g_table = pp.table();

// sort all keys
std::vector<std::string> sorted_names;
sorted_names.reserve(g_table.size());
for (auto const& [name, entry] : g_table) {
sorted_names.push_back(name);
}
std::sort(sorted_names.begin(), sorted_names.end());

// helper function to unroll any nested parameter.sub.options into dict of dict of value
auto add_nested = [&d](auto && value, std::string_view s) {
py::dict d_inner = d; // just hold a handle to the current dict

while (true) {
auto pos = s.find('.');
bool last = pos == std::string_view::npos;
py::str key(s.substr(0, pos));

if (last) {
d_inner[key] = value;
break;
} else {
// Create nested dict if missing or wrong type
if (!d_inner.contains(key) ||
!py::isinstance<py::dict>(d_inner[key])) {
d_inner[key] = py::dict();
}

// Move one level deeper (safe, keeps reference alive)
d_inner = d_inner[key].cast<py::dict>();
}
s.remove_prefix(pos + 1);
}
};

for (auto const& name : sorted_names) {
auto const& entry = g_table[name];
for (auto const & vals : entry.m_vals) {
if (vals.size() == 1) {
std::visit(
[&](auto&& arg) {
using T = std::remove_pointer_t<std::decay_t<decltype(arg)>>;
T v;
pp.get(name.c_str(), v);
add_nested(v, name);
},
entry.m_typehint
);
} else {
std::visit(
[&](auto&& arg) {
using T = std::remove_pointer_t<std::decay_t<decltype(arg)>>;
if constexpr (!std::is_same_v<T, bool>) {
std::vector<T> valarr;
pp.getarr(name.c_str(), valarr);
add_nested(valarr, name);
}
},
entry.m_typehint
);
}
}
}

return d;
},
R"(Convert to a nested Python dictionary.

.. code-block:: python

# Example: dump all ParmParse entries to YAML or TOML
import toml
import yaml

pp = amr.ParmParse("").to_dict()
yaml_string = yaml.dump(d)
toml_string = toml.dumps(d)
)"
)
;
}
34 changes: 34 additions & 0 deletions tests/test_parmparse.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,42 @@ def test_parmparse():
dt = pp_param.get_real("dt")
dopml = pp_param.get_bool("do_pml")

pp_param.add("ncell", 42) # overwrite file
pp_param.add(
"question", "What is the answer to life, the universe, and everything?"
)
pp_param.add("answer", 41)
pp_param.add("answer", 42) # last wins
pp_param.add("pi_approx", 3.1415)
pp_param.addarr("floats", [1.0, 2.0, 3.0])
pp_param.addarr("ints", [4, 5, 6])
pp_param.addarr("strs", ["Who", "Where", "What", "When", "How"])

assert dopml
assert np.isclose(dt, 1.0e-5)
assert ncell == 100

# printing
pp.pretty_print_table()

# type hints
d = pp.to_dict()
assert isinstance(d["param"]["ncell"], int) # overwritten
assert isinstance(d["param"]["dt"], str) # file
assert isinstance(d["param"]["do_pml"], str) # file
assert isinstance(d["param"]["question"], str)
assert isinstance(d["param"]["answer"], int)
assert d["param"]["answer"] == 42 # last wins
assert isinstance(d["param"]["pi_approx"], float)
assert isinstance(d["param"]["floats"], list)
assert isinstance(d["param"]["ints"], list)
assert isinstance(d["param"]["strs"], list)
assert d["param"]["floats"] == [1.0, 2.0, 3.0]
assert d["param"]["ints"] == [4, 5, 6]
assert d["param"]["strs"] == ["Who", "Where", "What", "When", "How"]

# You can now dump to YAML or TOML or any other format
# import toml
# import yaml
# yaml_string = yaml.dump(d)
# toml_string = toml.dumps(d)
Loading