Skip to content

Commit 6dc4560

Browse files
committed
Better error messages and some documentation inside example
1 parent a191f83 commit 6dc4560

File tree

2 files changed

+125
-50
lines changed

2 files changed

+125
-50
lines changed

examples/13_write_dynamic_configuration.cpp

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ using namespace openPMD;
1010

1111
int main()
1212
{
13-
if (!getVariants()["adios2"])
13+
if (!getVariants()["hdf5"])
1414
{
1515
// Example configuration below selects the ADIOS2 backend
1616
return 0;
@@ -57,13 +57,40 @@ parameters.clevel = 5
5757
# type = "some other parameter"
5858
# # ...
5959
60+
# Sometimes, dataset configurations should not affect all datasets, but only
61+
# specific ones, e.g. only particle data.
62+
# Dataset configurations can be given as a list, here at the example of HDF5.
63+
# In such lists, each entry is an object with two keys:
64+
#
65+
# 1. 'cfg': Mandatory key, this is the actual dataset configuration.
66+
# 2. 'select': A Regex or a list of Regexes to match against the dataset name.
67+
#
68+
# This makes it possible to give dataset-specific configurations.
69+
# The dataset name is the same as returned
70+
# by `Attributable::myPath().openPMDPath()`.
71+
# The regex must match against either the full path (e.g. "/data/1/meshes/E/x")
72+
# or against the path within the iteration (e.g. "meshes/E/x").
73+
74+
# Example:
75+
# Let HDF5 datasets be automatically chunked by default
6076
[[hdf5.dataset]]
6177
cfg.chunks = "auto"
6278
79+
# For particles, we can specify the chunking explicitly
80+
[[hdf5.dataset]]
81+
# Multiple selection regexes can be given as a list.
82+
# They will be fused into a single regex '($^)|(regex1)|(regex2)|(regex3)|...'.
83+
select = ["/data/1/particles/e/.*", "/data/2/particles/e/.*"]
84+
cfg.chunks = [5]
85+
86+
# Selecting a match works top-down, the order of list entries is important.
6387
[[hdf5.dataset]]
88+
# Specifying only a single regex.
89+
# The regex can match against the full dataset path
90+
# or against the path within the Iteration.
91+
# Capitalization is irrelevant.
6492
select = "particles/e/.*"
65-
cfg.chunks = [10]
66-
cfg.chornks = []
93+
CFG.CHUNKS = [10]
6794
)END";
6895

6996
// open file for writing

src/auxiliary/JSONMatcher.cpp

Lines changed: 95 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@
22
#include "openPMD/Error.hpp"
33
#include "openPMD/auxiliary/JSON_internal.hpp"
44

5+
#include <iostream>
56
#include <nlohmann/json.hpp>
67
#include <sstream>
8+
#include <stdexcept>
79

810
namespace openPMD::json
911
{
@@ -16,6 +18,8 @@ namespace
1618
* The "select" key is optional, indicating the default configuration if it
1719
* is missing.
1820
*
21+
* @param backend_name For error messages.
22+
* @param index_in_list For error messages.
1923
* @param patterns Output parameter: Emplace a parsed pattern into this
2024
* list.
2125
* @param defaultConfig Output parameter: If the pattern was the default
@@ -24,6 +28,8 @@ namespace
2428
* @return Whether the pattern was the default configuration or not.
2529
*/
2630
auto readPattern(
31+
std::string const &backend_name,
32+
size_t index_in_list,
2733
std::vector<Pattern> &patterns,
2834
std::optional<nlohmann::json> &defaultConfig,
2935
nlohmann::json object) -> void;
@@ -53,19 +59,23 @@ void MatcherPerBackend::init(TracingJSON tracing_config)
5359
{
5460
std::optional<nlohmann::json> defaultConfig;
5561
// enhanced PIConGPU-defined layout
56-
for (auto &value : config)
62+
for (size_t i = 0; i < config.size(); ++i)
5763
{
58-
readPattern(m_patterns, defaultConfig, std::move(value));
64+
readPattern(
65+
backendName,
66+
i,
67+
m_patterns,
68+
defaultConfig,
69+
std::move(config.at(i)));
5970
}
6071
// now replace the pattern list with the default config
6172
tracing_config.json() =
6273
std::move(defaultConfig).value_or(nlohmann::json::object());
6374
}
6475
else
6576
{
66-
throw std::runtime_error(
67-
"[openPMD plugin] Expecting an object or an array as JSON "
68-
"configuration.");
77+
throw error::BackendConfigSchema(
78+
{backendName, "dataset"}, "Expecting an object or an array.");
6979
}
7080
}
7181

@@ -171,66 +181,104 @@ auto JsonMatcher::getDefault() -> TracingJSON
171181
namespace
172182
{
173183
auto readPattern(
184+
std::string const &backend_name,
185+
size_t index_in_list,
174186
std::vector<Pattern> &patterns,
175187
std::optional<nlohmann::json> &defaultConfig,
176188
nlohmann::json object) -> void
177189
{
178-
constexpr char const *errorMsg = &R"END(
179-
Each single pattern in an extended JSON configuration must be a JSON object
180-
with keys 'select' and 'cfg'.
181-
The key 'select' is optional, indicating a default configuration if it is
182-
not set.
183-
The key 'select' must point to either a single string or an array of strings.)END"
184-
[1];
190+
constexpr char const *errorMsg = R"END(
191+
Each single pattern in an dataset-specific JSON/TOML configuration must be
192+
an object with mandatory key 'cfg' and optional key 'select'.
193+
When the key 'select' is not specified, the given configuration is used
194+
for setting up the default dataset configuration upon backend initialization.
195+
The key 'select' must point to either a single string or an array of strings
196+
and is interpreted as a regular expression against which the dataset name
197+
(full path or path within an iteration) must match.)END";
198+
auto throw_up = [&](std::string const &additional_info,
199+
auto &&...additional_path) {
200+
throw error::BackendConfigSchema(
201+
{backend_name,
202+
"dataset",
203+
std::to_string(index_in_list),
204+
additional_path...},
205+
additional_info + errorMsg);
206+
};
185207

186208
if (!object.is_object())
187209
{
188-
throw std::runtime_error(errorMsg);
210+
throw_up("Not an object!");
211+
}
212+
if (!object.contains("cfg"))
213+
{
214+
throw_up("Mandatory key missing: 'cfg'!");
189215
}
190-
try
191216
{
192-
nlohmann::json &cfg = object.at("cfg");
193-
if (!object.contains("select"))
217+
std::vector<std::string> unrecognized_keys;
218+
for (auto it = object.begin(); it != object.end(); ++it)
194219
{
195-
if (defaultConfig.has_value())
220+
if (it.key() == "select" || it.key() == "cfg")
196221
{
197-
throw std::runtime_error(
198-
"Specified more than one default configuration.");
222+
continue;
199223
}
200-
defaultConfig.emplace(std::move(cfg));
201-
return;
224+
unrecognized_keys.emplace_back(it.key());
202225
}
203-
else
226+
if (!unrecognized_keys.empty())
204227
{
205-
nlohmann::json const &pattern = object.at("select");
206-
std::string pattern_str = [&]() -> std::string {
207-
if (pattern.is_string())
208-
{
209-
return pattern.get<std::string>();
210-
}
211-
else if (pattern.is_array())
228+
std::cerr << "[Warning] JSON/TOML config at '" << backend_name
229+
<< ".dataset." << index_in_list
230+
<< "' has unrecognized keys:";
231+
for (auto const &item : unrecognized_keys)
232+
{
233+
std::cerr << " '" << item << '\'';
234+
}
235+
std::cerr << '.' << std::endl;
236+
}
237+
}
238+
239+
nlohmann::json &cfg = object.at("cfg");
240+
if (!object.contains("select"))
241+
{
242+
if (defaultConfig.has_value())
243+
{
244+
throw_up("Specified more than one default configuration!");
245+
}
246+
defaultConfig.emplace(std::move(cfg));
247+
return;
248+
}
249+
else
250+
{
251+
nlohmann::json const &pattern = object.at("select");
252+
std::string pattern_str = [&]() -> std::string {
253+
if (pattern.is_string())
254+
{
255+
return pattern.get<std::string>();
256+
}
257+
else if (pattern.is_array())
258+
{
259+
std::stringstream res;
260+
res << "($^)";
261+
for (auto const &sub_pattern : pattern)
212262
{
213-
std::stringstream res;
214-
res << "($^)";
215-
for (auto const &sub_pattern : pattern)
263+
if (!sub_pattern.is_string())
216264
{
217-
res << "|(" << sub_pattern.get<std::string>()
218-
<< ")";
265+
throw_up(
266+
"Must be a string or an array of string!",
267+
"select");
219268
}
220-
return res.str();
221-
}
222-
else
223-
{
224-
throw std::runtime_error(errorMsg);
269+
res << "|(" << sub_pattern.get<std::string>() << ")";
225270
}
226-
}();
227-
patterns.emplace_back(pattern_str, std::move(cfg));
228-
return;
229-
}
230-
}
231-
catch (nlohmann::json::out_of_range const &)
232-
{
233-
throw std::runtime_error(errorMsg);
271+
return res.str();
272+
}
273+
else
274+
{
275+
throw_up(
276+
"Must be a string or an array of string!", "select");
277+
throw std::runtime_error("Unreachable!");
278+
}
279+
}();
280+
patterns.emplace_back(pattern_str, std::move(cfg));
281+
return;
234282
}
235283
}
236284
} // namespace

0 commit comments

Comments
 (0)