Skip to content

Commit fe8ba37

Browse files
committed
Nested Dictionary
1 parent cb77918 commit fe8ba37

File tree

2 files changed

+64
-18
lines changed

2 files changed

+64
-18
lines changed

src/Base/ParmParse.cpp

Lines changed: 45 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,10 @@
99
#include <AMReX_IntVect.H>
1010
#include <AMReX_ParmParse.H>
1111

12+
#include <functional>
1213
#include <iostream>
1314
#include <string>
15+
#include <string_view>
1416
#include <vector>
1517

1618

@@ -111,34 +113,61 @@ void init_ParmParse(py::module &m)
111113

112114
auto g_table = pp.table();
113115

116+
// sort all keys
114117
std::vector<std::string> sorted_names;
115118
sorted_names.reserve(g_table.size());
116119
for (auto const& [name, entry] : g_table) {
117120
sorted_names.push_back(name);
118121
}
119122
std::sort(sorted_names.begin(), sorted_names.end());
120123

124+
// helper function to unroll any nested parameter.sub.options into dict of dict of value
125+
auto add_nested = [&d](auto && value, std::string_view s) {
126+
py::dict d_inner = d; // just hold a handle to the current dict
127+
128+
while (true) {
129+
auto pos = s.find('.');
130+
bool last = pos == std::string_view::npos;
131+
py::str key(s.substr(0, pos));
132+
133+
if (last) {
134+
d_inner[key] = value;
135+
break;
136+
} else {
137+
// Create nested dict if missing or wrong type
138+
if (!d_inner.contains(key) ||
139+
!py::isinstance<py::dict>(d_inner[key])) {
140+
d_inner[key] = py::dict();
141+
}
142+
143+
// Move one level deeper (safe, keeps reference alive)
144+
d_inner = d_inner[key].cast<py::dict>();
145+
}
146+
s.remove_prefix(pos + 1);
147+
}
148+
};
149+
121150
for (auto const& name : sorted_names) {
122151
auto const& entry = g_table[name];
123152
for (auto const & vals : entry.m_vals) {
124153
if (vals.size() == 1) {
125154
std::visit(
126-
[&d,&name,&pp](auto&& arg) {
155+
[&](auto&& arg) {
127156
using T = std::remove_pointer_t<std::decay_t<decltype(arg)>>;
128157
T v;
129158
pp.get(name.c_str(), v);
130-
d[name.c_str()] = v;
159+
add_nested(v, name);
131160
},
132161
entry.m_typehint
133162
);
134163
} else {
135164
std::visit(
136-
[&d,&name,&pp](auto&& arg) {
165+
[&](auto&& arg) {
137166
using T = std::remove_pointer_t<std::decay_t<decltype(arg)>>;
138167
if constexpr (!std::is_same_v<T, bool>) {
139168
std::vector<T> valarr;
140169
pp.getarr(name.c_str(), valarr);
141-
d[name.c_str()] = valarr;
170+
add_nested(valarr, name);
142171
}
143172
},
144173
entry.m_typehint
@@ -149,7 +178,18 @@ void init_ParmParse(py::module &m)
149178

150179
return d;
151180
},
152-
"DOC GOES HERE TODO TODO"
181+
R"(Convert to a nested Python dictionary.
182+
183+
.. code-block:: python
184+
185+
# Example: dump all ParmParse entries to YAML or TOML
186+
import toml
187+
import yaml
188+
189+
pp = amr.ParmParse("").to_dict()
190+
yaml_string = yaml.dump(d)
191+
toml_string = toml.dumps(d)
192+
)"
153193
)
154194
;
155195
}

tests/test_parmparse.py

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -35,16 +35,22 @@ def test_parmparse():
3535

3636
# type hints
3737
d = pp.to_dict()
38-
assert isinstance(d["param.ncell"], int) # overwritten
39-
assert isinstance(d["param.dt"], str) # file
40-
assert isinstance(d["param.do_pml"], str) # file
41-
assert isinstance(d["param.question"], str)
42-
assert isinstance(d["param.answer"], int)
43-
assert d["param.answer"] == 42 # last wins
44-
assert isinstance(d["param.pi_approx"], float)
45-
assert isinstance(d["param.floats"], list)
46-
assert isinstance(d["param.ints"], list)
47-
assert isinstance(d["param.strs"], list)
48-
assert d["param.floats"] == [1.0, 2.0, 3.0]
49-
assert d["param.ints"] == [4, 5, 6]
50-
assert d["param.strs"] == ["Who", "Where", "What", "When", "How"]
38+
assert isinstance(d["param"]["ncell"], int) # overwritten
39+
assert isinstance(d["param"]["dt"], str) # file
40+
assert isinstance(d["param"]["do_pml"], str) # file
41+
assert isinstance(d["param"]["question"], str)
42+
assert isinstance(d["param"]["answer"], int)
43+
assert d["param"]["answer"] == 42 # last wins
44+
assert isinstance(d["param"]["pi_approx"], float)
45+
assert isinstance(d["param"]["floats"], list)
46+
assert isinstance(d["param"]["ints"], list)
47+
assert isinstance(d["param"]["strs"], list)
48+
assert d["param"]["floats"] == [1.0, 2.0, 3.0]
49+
assert d["param"]["ints"] == [4, 5, 6]
50+
assert d["param"]["strs"] == ["Who", "Where", "What", "When", "How"]
51+
52+
# You can now dump to YAML or TOML or any other format
53+
# import toml
54+
# import yaml
55+
# yaml_string = yaml.dump(d)
56+
# toml_string = toml.dumps(d)

0 commit comments

Comments
 (0)