PeriDEM 0.3.0
PeriDEM -- Peridynamics-based high-fidelity model for granular media
Loading...
Searching...
No Matches
testDeckField.cpp
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 * inp::Field generates the reader, the writer, the printed form and the schema
11 * of a deck from one declaration of its fields. These checks cover that
12 * generation on a deck defined here, so that a failure names the table
13 * machinery rather than one of the decks that use it.
14 */
15
16#include "inp/deckField.h"
17
18#include <iostream>
19#include <string>
20#include <vector>
21
22namespace {
23
24int g_failures = 0;
25int g_checks = 0;
26
27void check(bool ok, const std::string &what) {
28 ++g_checks;
29 if (ok)
30 return;
31 ++g_failures;
32 std::cerr << " FAIL: " << what << "\n";
33}
34
36template <typename Call>
37void checkThrows(Call call, const std::string &contains,
38 const std::string &what) {
39 ++g_checks;
40 try {
41 call();
42 } catch (const std::exception &e) {
43 if (std::string(e.what()).find(contains) != std::string::npos)
44 return;
45 ++g_failures;
46 std::cerr << " FAIL: " << what << ": message was " << e.what();
47 return;
48 }
49 ++g_failures;
50 std::cerr << " FAIL: " << what << ": nothing was thrown\n";
51}
52
54struct SampleDeck {
55 std::size_t d_count = 2;
56 double d_length = 1.5;
57 std::string d_method = "central_difference";
58 bool d_write = true;
59 double d_optional = -1.;
60
61 static const std::vector<inp::Field<SampleDeck>> &fields() {
62 static const std::vector<inp::Field<SampleDeck>> f = {
63 inp::field(&SampleDeck::d_count, "Count", std::size_t(2),
64 "How many of them", {{}, std::size_t(1), std::size_t(3)}),
65 inp::field(&SampleDeck::d_length, "Length", 1.5, "Length of the side"),
67 std::string("central_difference"), "Time discretization",
68 {{"central_difference", "velocity_verlet"}, {}, {}}),
69 inp::field(&SampleDeck::d_write, "Write", true, "Write the output"),
70 // Absent from the block when it is not positive, which is how the
71 // decks treat a value that another field supplies instead.
72 inp::field<SampleDeck, double>(
73 &SampleDeck::d_optional, "Optional", -1., "Set only when positive",
74 {}, [](const double &v) { return v > 0.; }),
75 };
76 return f;
77 }
78};
79
81 std::cout << "defaults and schema\n";
83 check(d.at("Count") == 2, "Count default is written");
84 check(d.at("Method") == "central_difference", "Method default is written");
85 check(d.find("Optional") == d.end(),
86 "a field whose emit condition is false is absent");
87
89 check(s.size() == 5, "schema reports one entry per field");
90 check(s.at(0).at("key") == "Count", "schema reports the key");
91 check(s.at(0).at("type") == "size_t", "schema reports the type");
92 check(s.at(0).at("default") == 2, "schema reports the default");
93 check(s.at(0).at("doc") == "How many of them", "schema reports the doc");
94 check(s.at(4).find("default") == s.at(4).end(),
95 "schema omits the default of a field that is not written");
96}
97
99 std::cout << "read and write\n";
100 json j = inp::applyGiven(json{{"Count", 3}, {"Length", 2.25}},
102 SampleDeck d;
104 check(d.d_count == 3, "Count is read");
105 check(d.d_length == 2.25, "Length is read");
106 check(d.d_method == "central_difference", "Method keeps its default");
107 check(d.d_write, "Write keeps its default");
108
109 json out = json::object();
111 check(out == j, "writing what was read gives the block back");
112
113 // A block with no keys leaves every member at its default.
114 SampleDeck empty;
115 inp::readFields(empty, json::object(), SampleDeck::fields());
116 check(empty.d_count == 2, "an absent key leaves the default");
117}
118
120 std::cout << "accepted values and bounds\n";
121 SampleDeck d;
123 [&] {
124 inp::readFields(d, json{{"Method", "backward_euler"}},
126 },
127 "must be one of", "a value outside the accepted set is rejected");
129 [&] {
130 inp::readFields(d, json{{"Count", 7}}, SampleDeck::fields());
131 },
132 "must be at most", "a value above the upper bound is rejected");
134 [&] {
135 inp::readFields(d, json{{"Count", 0}}, SampleDeck::fields());
136 },
137 "must be at least", "a value below the lower bound is rejected");
138}
139
141 std::cout << "keys are named and checked\n";
142 checkThrows([] { inp::applyGiven(json{{"Coun", 3}}, SampleDeck::fields()); },
143 "Closest declared field is Count",
144 "a misspelled key names the closest field");
145 checkThrows([] { inp::applyGiven(json{{"Horizon", 1.}}, SampleDeck::fields()); },
146 "no field named Horizon", "an unrelated key is rejected");
148 "name and value pairs",
149 "a list of values without names is rejected");
150
151 // A key the deck reads outside the table is accepted when declared.
152 json j = inp::applyGiven(json{{"Nested", json::object()}},
153 SampleDeck::fields(), {"Nested"});
154 check(j.find("Nested") != j.end(), "a key named in extra is kept");
155}
156
158 std::cout << "values are checked when a deck is built\n";
159 // A deck is rejected where it is built, not only where it is read.
160 checkThrows([] { inp::applyGiven(json{{"Count", 9}}, SampleDeck::fields()); },
161 "must be at most",
162 "a value above the bound is rejected when the deck is built");
164 [] {
165 inp::applyGiven(json{{"Method", "backward_euler"}},
167 },
168 "must be one of",
169 "a value outside the accepted set is rejected when the deck is built");
170
171 json ok = inp::applyGiven(json{{"Count", 3}}, SampleDeck::fields());
172 check(ok.at("Count") == 3, "an accepted value is kept");
173
174 // Naming a field must not change whether it appears in the block.
175 json given = inp::applyGiven(json{{"Optional", -1.}}, SampleDeck::fields());
176 check(given.find("Optional") == given.end(),
177 "giving a value the field does not write leaves it out");
178 json set = inp::applyGiven(json{{"Optional", 2.5}}, SampleDeck::fields());
179 check(set.at("Optional") == 2.5, "giving a value the field writes keeps it");
180}
181
182void testPrint() {
183 std::cout << "printed form\n";
184 SampleDeck d;
185 std::ostringstream oss;
186 inp::printFields(d, oss, SampleDeck::fields(), " ");
187 const std::string s = oss.str();
188 check(s.find(" Count = 2") != std::string::npos,
189 "the printed form has Count after the tab prefix");
190 check(s.find(" Method = central_difference") != std::string::npos,
191 "the printed form has Method after the tab prefix");
192}
193
195 std::cout << "quantities given in more than one way\n";
196 const std::vector<inp::OneOf> groups = {
197 {"the horizon", {"Horizon", "Horizon_Mesh_Ratio"}},
198 {"the contact stiffness", {"Kn", "V_Max"}, false},
199 };
200
201 inp::checkGroups(json{{"Horizon", 6.0e-4}, {"Kn", 1.0e12}}, groups);
202 // The second group is not required, so leaving it out is accepted.
203 inp::checkGroups(json{{"Horizon_Mesh_Ratio", 3.0}}, groups);
204
205 checkThrows([&] { inp::checkGroups(json{{"Kn", 1.0e12}}, groups); },
206 "None was given",
207 "a required group with neither key is rejected");
209 [&] {
211 json{{"Horizon", 6.0e-4}, {"Horizon_Mesh_Ratio", 3.0}}, groups);
212 },
213 "These were given: Horizon Horizon_Mesh_Ratio",
214 "a group with both keys is rejected and names them");
215}
216
218 std::cout << "edit distance\n";
219 check(inp::editDistance("Count", "Count") == 0, "equal strings");
220 check(inp::editDistance("Coun", "Count") == 1, "one deletion");
221 check(inp::editDistance("", "abc") == 3, "an empty string");
222}
223
224void run(void (*fn)(), const std::string &name) {
225 try {
226 fn();
227 } catch (const std::exception &e) {
228 ++g_checks;
229 ++g_failures;
230 std::cerr << " FAIL: " << name << " threw: " << e.what() << "\n";
231 }
232}
233
234} // namespace
235
236int main() {
237 std::cout << "Deck field table\n"
238 << "----------------\n";
239 run(testDefaultsAndSchema, "defaults and schema");
240 run(testReadAndWrite, "read and write");
241 run(testAccepted, "accepted values");
242 run(testKeysAreNamed, "keys are named");
243 run(testValuesCheckedWhenBuilt, "values checked when built");
244 run(testGroups, "exclusive groups");
245 run(testPrint, "printed form");
246 run(testEditDistance, "edit distance");
247
248 std::cout << "----------------\n"
249 << g_checks - g_failures << " / " << g_checks << " checks passed\n";
250 return g_failures == 0 ? 0 : 1;
251}
nlohmann::ordered_json json
void check(bool ok, const std::string &what)
void checkThrows(Call call, const std::string &contains, const std::string &what)
Fails unless the call throws with a message containing contains.
std::size_t editDistance(const std::string &a, const std::string &b)
Number of single character edits between two strings.
Definition deckField.h:354
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
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 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
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 checkGroups(const json &block, const std::vector< OneOf > &groups)
Checks that each group has exactly one of its keys.
Definition deckField.h:317
json schemaJson(const std::vector< Field< Deck > > &fs)
Key, type, default and description of every field.
Definition deckField.h:276
Definition run.py:1
A deck with one field of each kind the tables have to handle.
static const std::vector< inp::Field< SampleDeck > > & fields()
int main()