PeriDEM 0.3.0
PeriDEM -- Peridynamics-based high-fidelity model for granular media
Loading...
Searching...
No Matches
deckField.h
Go to the documentation of this file.
1/*
2 * -------------------------------------------
3 * Copyright (c) 2021 - 2026 Prashant K. Jha
4 * -------------------------------------------
5 * PeriDEM https://github.com/prashjha/PeriDEM
6 *
7 * Distributed under the Boost Software License, Version 1.0. (See accompanying
8 * file LICENSE)
9 */
10
11#ifndef INP_DECKFIELD_H
12#define INP_DECKFIELD_H
13
14#include "util/io.h"
15#include "util/json.h"
16
17#include <algorithm>
18#include <functional>
19#include <optional>
20#include <optional>
21#include <sstream>
22#include <stdexcept>
23#include <string>
24#include <type_traits>
25#include <vector>
26
27namespace inp {
28
43template <class Deck> struct Field {
44
46 std::string key;
47
49 std::string doc;
50
52 std::string type;
53
55 std::function<void(Deck &, const json &)> read;
56
58 std::function<void(const Deck &, json &)> write;
59
61 std::function<void(json &)> writeDefault;
62
64 std::function<void(const Deck &, std::ostringstream &, const std::string &)>
66
68 std::function<void(const json &)> check;
69
71 std::function<bool(const json &)> emit;
72};
73
75template <class T> inline std::string typeName() { return "value"; }
77template <> inline std::string typeName<bool>() { return "bool"; }
79template <> inline std::string typeName<int>() { return "int"; }
81template <> inline std::string typeName<std::size_t>() { return "size_t"; }
83template <> inline std::string typeName<double>() { return "double"; }
85template <> inline std::string typeName<std::string>() { return "string"; }
87template <> inline std::string typeName<std::vector<double>>() {
88 return "double[]";
89}
91template <> inline std::string typeName<std::vector<std::string>>() {
92 return "string[]";
93}
95template <> inline std::string typeName<std::vector<std::size_t>>() {
96 return "size_t[]";
97}
98
100template <class T>
101void printValue(std::ostringstream &oss, const T &v) {
102 oss << v;
103}
104
106template <class T>
107void printValue(std::ostringstream &oss, const std::vector<T> &v) {
108 oss << util::io::printStr<T>(v, 0);
109}
110
118template <class T> struct Accepts {
119
121 std::vector<T> values = {};
122
124 std::optional<T> min = std::nullopt;
125
127 std::optional<T> max = std::nullopt;
128};
129
143template <class Deck, class T>
145 T Deck::*m, std::string key, T def, std::string doc,
146 Accepts<T> accepts = {},
147 std::function<bool(const T &)> emitIf = [](const T &) { return true; }) {
148
149 // Accepted values and bounds apply to the scalar types. A field holding a
150 // list is checked by the deck if it needs to be.
151 constexpr bool checkable =
152 std::is_arithmetic_v<T> || std::is_same_v<T, std::string>;
153
154 auto check = [key, accepts](const T &v) {
155 if constexpr (checkable) {
156 if (!accepts.values.empty() &&
157 std::find(accepts.values.begin(), accepts.values.end(), v) ==
158 accepts.values.end()) {
159 std::ostringstream oss;
160 oss << "Error: " << key << " must be one of:";
161 for (const auto &a : accepts.values)
162 oss << " " << a;
163 oss << ". Given value is " << v << ".\n";
164 throw std::runtime_error(oss.str());
165 }
166 if (accepts.min.has_value() && v < accepts.min.value()) {
167 std::ostringstream oss;
168 oss << "Error: " << key << " must be at least " << accepts.min.value()
169 << ". Given value is " << v << ".\n";
170 throw std::runtime_error(oss.str());
171 }
172 if (accepts.max.has_value() && accepts.max.value() < v) {
173 std::ostringstream oss;
174 oss << "Error: " << key << " must be at most " << accepts.max.value()
175 << ". Given value is " << v << ".\n";
176 throw std::runtime_error(oss.str());
177 }
178 }
179 };
180
181 return Field<Deck>{
182 key, std::move(doc), typeName<T>(),
183 [m, key, def, check](Deck &d, const json &j) {
184 d.*m = j.value(key, def);
185 check(d.*m);
186 },
187 [m, key, emitIf](const Deck &d, json &j) {
188 if (emitIf(d.*m))
189 j[key] = d.*m;
190 },
191 [key, def, emitIf](json &j) {
192 if (emitIf(def))
193 j[key] = def;
194 },
195 [m, key](const Deck &d, std::ostringstream &oss,
196 const std::string &tab) {
197 oss << tab << key << " = ";
198 printValue(oss, d.*m);
199 oss << std::endl;
200 },
201 [check](const json &v) { check(v.get<T>()); },
202 [emitIf](const json &v) { return emitIf(v.get<T>()); }};
203}
204
214template <class Deck, class T>
215Field<Deck> requiredField(T Deck::*m, std::string key, std::string doc,
216 Accepts<T> accepts = {}) {
217 auto f = field(m, key, T{}, std::move(doc), accepts);
218 f.read = [m, key, accepts, f](Deck &d, const json &j) {
219 if (j.find(key) == j.end())
220 throw std::runtime_error("Error: " + key + " is required.\n");
221 json one = json::object();
222 one[key] = j.at(key);
223 f.read(d, one);
224 };
225 return f;
226}
227
229template <class Deck>
230void readFields(Deck &d, const json &j, const std::vector<Field<Deck>> &fs) {
231 for (const auto &f : fs)
232 f.read(d, j);
233}
234
236template <class Deck>
237void writeFields(const Deck &d, json &j, const std::vector<Field<Deck>> &fs) {
238 for (const auto &f : fs)
239 f.write(d, j);
240}
241
250template <class Deck>
251void printFields(const Deck &d, std::ostringstream &oss,
252 const std::vector<Field<Deck>> &fs,
253 const std::string &tab = "") {
254 for (const auto &f : fs)
255 f.print(d, oss, tab);
256}
257
259template <class Deck>
260json defaultsJson(const std::vector<Field<Deck>> &fs) {
261 json j = json::object();
262 for (const auto &f : fs)
263 f.writeDefault(j);
264 return j;
265}
266
276template <class Deck> json schemaJson(const std::vector<Field<Deck>> &fs) {
277 json out = json::array();
278 const json defaults = defaultsJson(fs);
279 for (const auto &f : fs) {
280 json entry = {{"key", f.key}, {"type", f.type}, {"doc", f.doc}};
281 if (defaults.find(f.key) != defaults.end())
282 entry["default"] = defaults.at(f.key);
283 out.push_back(entry);
284 }
285 return out;
286}
287
299struct OneOf {
300
302 std::string what;
303
305 std::vector<std::string> keys;
306
308 bool required = true;
309};
310
317inline void checkGroups(const json &block, const std::vector<OneOf> &groups) {
318 for (const auto &g : groups) {
319 std::vector<std::string> present;
320 for (const auto &k : g.keys)
321 if (block.find(k) != block.end())
322 present.push_back(k);
323
324 if (present.size() == 1 || (present.empty() && !g.required))
325 continue;
326
327 std::ostringstream oss;
328 oss << "Error: give exactly one of";
329 for (const auto &k : g.keys)
330 oss << " " << k;
331 oss << " for " << g.what << ". ";
332 if (present.empty())
333 oss << "None was given.\n";
334 else {
335 oss << "These were given:";
336 for (const auto &k : present)
337 oss << " " << k;
338 oss << ".\n";
339 }
340 throw std::runtime_error(oss.str());
341 }
342}
343
354inline std::size_t editDistance(const std::string &a, const std::string &b) {
355 std::vector<std::size_t> prev(b.size() + 1), cur(b.size() + 1);
356 for (std::size_t j = 0; j <= b.size(); ++j)
357 prev[j] = j;
358 for (std::size_t i = 1; i <= a.size(); ++i) {
359 cur[0] = i;
360 for (std::size_t j = 1; j <= b.size(); ++j)
361 cur[j] = std::min({prev[j] + 1, cur[j - 1] + 1,
362 prev[j - 1] + (a[i - 1] == b[j - 1] ? 0 : 1)});
363 prev = cur;
364 }
365 return prev[b.size()];
366}
367
379inline void checkNames(const json &given,
380 const std::vector<std::string> &names) {
381 if (given.is_null() || given.empty())
382 return;
383 if (!given.is_object())
384 throw std::runtime_error(
385 "Error: deck values must be given as name and value pairs.\n");
386
387 for (const auto &item : given.items()) {
388 const std::string key = item.key();
389 if (std::find(names.begin(), names.end(), key) != names.end())
390 continue;
391
392 std::string closest;
393 std::size_t best = std::string::npos;
394 for (const auto &n : names) {
395 const std::size_t d = editDistance(key, n);
396 if (d < best) {
397 best = d;
398 closest = n;
399 }
400 }
401 std::ostringstream oss;
402 oss << "Error: no field named " << key << ".";
403 if (best <= 3)
404 oss << " Closest declared field is " << closest << ".";
405 oss << "\nDeclared fields are:";
406 for (const auto &n : names)
407 oss << " " << n;
408 oss << "\n";
409 throw std::runtime_error(oss.str());
410 }
411}
412
420template <class Deck>
421void checkKeys(const json &given, const std::vector<Field<Deck>> &fs,
422 const std::vector<std::string> &extra = {}) {
423 std::vector<std::string> names;
424 names.reserve(fs.size() + extra.size());
425 for (const auto &f : fs)
426 names.push_back(f.key);
427 names.insert(names.end(), extra.begin(), extra.end());
428 checkNames(given, names);
429}
430
442template <class Deck>
443json applyGiven(const json &given, const std::vector<Field<Deck>> &fs,
444 const std::vector<std::string> &extra = {}) {
445 checkKeys(given, fs, extra);
446 json j = defaultsJson(fs);
447 if (!given.is_object())
448 return j;
449
450 for (const auto &item : given.items()) {
451 bool emit = true;
452 for (const auto &f : fs)
453 if (f.key == item.key()) {
454 f.check(item.value());
455 emit = f.emit(item.value());
456 }
457 // A field that is not written at its default is not written when the
458 // same value is given, so that the block does not depend on whether the
459 // caller named the field.
460 if (emit)
461 j[item.key()] = item.value();
462 else
463 j.erase(item.key());
464 }
465 return j;
466}
467
468} // namespace inp
469
470#endif // INP_DECKFIELD_H
nlohmann::ordered_json json
void check(bool ok, const std::string &what)
Collection of methods and database related to input.
Definition pairForce.h:20
std::size_t editDistance(const std::string &a, const std::string &b)
Number of single character edits between two strings.
Definition deckField.h:354
std::string typeName< bool >()
Name of the type of a field, reported by the schema.
Definition deckField.h:77
Field< Deck > field(T Deck::*m, std::string key, T def, std::string doc, Accepts< T > accepts={}, std::function< bool(const T &)> emitIf=[](const T &) { return true;})
Declares a field that maps one key to one member.
Definition deckField.h:144
void readFields(Deck &d, const json &j, const std::vector< Field< Deck > > &fs)
Reads every field of the table from the block.
Definition deckField.h:230
json defaultsJson(const std::vector< Field< Deck > > &fs)
The block with every field at its default.
Definition deckField.h:260
std::string typeName< std::size_t >()
Name of the type of a field, reported by the schema.
Definition deckField.h:81
void printValue(std::ostringstream &oss, const T &v)
Appends a value to a stream.
Definition deckField.h:101
void writeFields(const Deck &d, json &j, const std::vector< Field< Deck > > &fs)
Writes every field of the table into the block.
Definition deckField.h:237
void checkNames(const json &given, const std::vector< std::string > &names)
Rejects a key that is not among those named.
Definition deckField.h:379
void printFields(const Deck &d, std::ostringstream &oss, const std::vector< Field< Deck > > &fs, const std::string &tab="")
Appends every field of the table to a stream.
Definition deckField.h:251
std::string typeName< double >()
Name of the type of a field, reported by the schema.
Definition deckField.h:83
json applyGiven(const json &given, const std::vector< Field< Deck > > &fs, const std::vector< std::string > &extra={})
The default block with the given keys replaced.
Definition deckField.h:443
void checkKeys(const json &given, const std::vector< Field< Deck > > &fs, const std::vector< std::string > &extra={})
Rejects a key that no field declares.
Definition deckField.h:421
Field< Deck > requiredField(T Deck::*m, std::string key, std::string doc, Accepts< T > accepts={})
Declares a field that has no default and must be present.
Definition deckField.h:215
void checkGroups(const json &block, const std::vector< OneOf > &groups)
Checks that each group has exactly one of its keys.
Definition deckField.h:317
std::string typeName< std::string >()
Name of the type of a field, reported by the schema.
Definition deckField.h:85
std::string typeName()
Name of the type of a field, reported by the schema.
Definition deckField.h:75
json schemaJson(const std::vector< Field< Deck > > &fs)
Key, type, default and description of every field.
Definition deckField.h:276
std::string typeName< int >()
Name of the type of a field, reported by the schema.
Definition deckField.h:79
What a field accepts.
Definition deckField.h:118
std::optional< T > max
Upper bound, applied when it is set.
Definition deckField.h:127
std::vector< T > values
Accepted values, or empty when any value is accepted.
Definition deckField.h:121
std::optional< T > min
Lower bound, applied when it is set.
Definition deckField.h:124
One field of a deck, declared once.
Definition deckField.h:43
std::function< bool(const json &)> emit
Whether a value is written rather than left out of the block.
Definition deckField.h:71
std::string doc
One line describing the field, reported by the schema.
Definition deckField.h:49
std::function< void(const json &)> check
Throws if the value is one the field does not accept.
Definition deckField.h:68
std::string key
Key in the JSON block.
Definition deckField.h:46
std::string type
Name of the type, reported by the schema.
Definition deckField.h:52
std::function< void(Deck &, const json &)> read
Reads the key into the member, or leaves the default in place.
Definition deckField.h:55
std::function< void(const Deck &, std::ostringstream &, const std::string &)> print
Appends the key and its value to a stream, after a tab prefix.
Definition deckField.h:65
std::function< void(const Deck &, json &)> write
Writes the member under the key, if it is to be written.
Definition deckField.h:58
std::function< void(json &)> writeDefault
Writes the default under the key, if it is to be written.
Definition deckField.h:61
A quantity that can be given in more than one way.
Definition deckField.h:299
bool required
Whether one of the keys has to be given.
Definition deckField.h:308
std::string what
What the group gives, named in the error message.
Definition deckField.h:302
std::vector< std::string > keys
Keys of the group.
Definition deckField.h:305