PeriDEM 0.3.0
PeriDEM -- Peridynamics-based high-fidelity model for granular media
Loading...
Searching...
No Matches
testDeckRoundTrip.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 * Each inp::*Deck lists its fields twice, once in getExampleJson, which writes
11 * the block, and once in readFromJson, which reads it. No test compared the
12 * two, and they disagreed in three places. BCBaseDeck emitted Region,
13 * Time_Function and Spatial_Function as JSON arrays, where readFromJson calls
14 * at() and value() on them as objects, and Parameters overwrote Type.
15 * TestDeck emitted an array for Test_Name. MaterialDeck wrote Is_Plane_Strain
16 * and readFromJson read Is_Plain_Strain.
17 *
18 * For each deck this writes a block with values other than the defaults, reads
19 * it back, and compares the values. A disagreement between the two lists fails
20 * here rather than producing a deck that cannot be read.
21 */
22
23#include "inp/deckIncludes.h"
25#include "util/io.h"
26#include "util/json.h"
27#include "util/point.h"
28
29#include <cmath>
30#include <iostream>
31#include <string>
32#include <vector>
33
34namespace {
35
36int g_failures = 0;
37int g_checks = 0;
38
39void check(bool ok, const std::string &what) {
40 ++g_checks;
41 if (!ok) {
42 ++g_failures;
43 std::cerr << " FAIL: " << what << "\n";
44 }
45}
46
47void checkClose(double got, double want, const std::string &what,
48 double tol = 1.0e-12) {
49 check(std::abs(got - want) <= tol * std::max(1.0, std::abs(want)),
50 what + " (got " + std::to_string(got) + ", want " +
51 std::to_string(want) + ")");
52}
53
55void checkIsObject(const json &j, const std::string &key) {
56 check(j.contains(key), key + " present");
57 if (j.contains(key))
58 check(j.at(key).is_object(), key + " is an object, not an array");
59}
60
62 std::cout << "ModelDeck\n";
64 json{{"Dimension", 3},
65 {"Final_Time", 0.25},
66 {"Time_Steps", 500},
67 {"Discretization_Type", json{{"Spatial", "finite_difference"}, {"Time", "central_difference"}}},
68 {"Populate_ElementNodeConnectivity", false},
69 {"Quad_Approximation_Order", 3},
70 {"Particle_Sim_Type", "Single_Particle"},
71 {"Seed", 7}});
73 d.readFromJson(j);
74 check(d.d_dim == 3, "Dimension round-trips");
75 checkClose(d.d_tFinal, 0.25, "Final_Time round-trips");
76 check(d.d_Nt == 500, "Time_Steps round-trips");
77 check(!d.d_populateElementNodeConnectivity,
78 "Populate_ElementNodeConnectivity round-trips");
79 check(d.d_quadOrder == 3, "Quad_Approximation_Order round-trips");
80 check(d.d_particleSimType == "Single_Particle",
81 "Particle_Sim_Type round-trips");
82 check(d.d_seed == 7, "Seed round-trips");
83 checkClose(d.d_dt, 0.25 / 500., "dt derived from Final_Time / Time_Steps");
84}
85
87 std::cout << "OutputDeck\n";
89 json{{"File_Format", "vtu"},
90 {"Path", "out/"},
91 {"Tags", std::vector<std::string>({"Displacement", "Damage_Z"})},
92 {"Output_Interval", 25},
93 {"Debug", 1},
94 {"Perform_FE_Out", false},
95 {"Compress_Type", "zlib"},
96 {"Perform_Out", true},
97 {"Test_Output_Interval", 5},
98 {"Tag_PP", "3"},
99 {"PVD_Collection", true}});
101 d.readFromJson(j);
102 check(d.d_path == "out/", "Path round-trips");
103 check(d.d_dtOut == 25, "Output_Interval round-trips");
104 check(d.d_debug == 1, "Debug round-trips");
105 check(!d.d_performFEOut, "Perform_FE_Out round-trips");
106 check(d.d_dtTestOut == 5, "Test_Output_Interval round-trips");
107 check(d.d_tagPPFile == "3", "Tag_PP round-trips");
108 check(d.d_pvdCollection, "PVD_Collection round-trips");
109 check(d.d_outTags.size() == 2, "Tags round-trip");
110}
111
113 std::cout << "MaterialDeck\n";
114 // Plane strain on: the writer key and the reader key must agree.
116 json{{"Type", "PMBBond"},
117 {"Is_Plane_Strain", true},
118 {"Horizon", 6.0e-4},
119 {"Density", 1200.},
120 {"K", 2.16e7},
121 {"G", 1.296e7},
122 {"Gc", 50.},
123 {"E", 3.24e7},
124 {"Compute_From_Classical", true},
125 {"Influence_Function", json{{"Type", 1}}}});
127 d.readFromJson(j);
128 check(d.d_materialType == "PMBBond", "Type round-trips");
129 check(d.d_isPlaneStrain, "Is_Plane_Strain round-trips (writer/reader key)");
130 checkClose(d.d_horizon, 6.0e-4, "Horizon round-trips");
131 checkClose(d.d_density, 1200., "Density round-trips");
132 check(d.d_influenceFnType == 1, "Influence_Function.Type round-trips");
133
134 // Plane strain off must also survive.
136 json{{"Type", "PDState"},
137 {"Is_Plane_Strain", false},
138 {"Horizon", 6.0e-4},
139 {"Density", 1200.},
140 {"K", 2.16e7},
141 {"G", 1.296e7},
142 {"Gc", 50.},
143 {"Compute_From_Classical", true},
144 {"Influence_Function", json{{"Type", 0}}}});
146 d2.readFromJson(j2);
147 check(!d2.d_isPlaneStrain, "Is_Plane_Strain false round-trips");
148}
149
151 std::cout << "MeshDeck\n";
152 // A mesh size with no filename requests generation.
153 auto j = inp::MeshDeck::getExampleJson(json{{"Mesh_Size", 1.0e-4}});
155 d.readFromJson(j);
156 check(d.d_createMesh, "bare Mesh_Size still means CreateMesh");
157 checkClose(d.d_hMeshing, 1.0e-4, "Mesh_Size round-trips");
158
159 // File only.
160 auto j2 = inp::MeshDeck::getExampleJson(json{{"File", "mesh.msh"}});
161 inp::MeshDeck d2;
162 d2.readFromJson(j2);
163 check(d2.d_filename == "mesh.msh", "File round-trips");
164 check(!d2.d_createMesh, "a plain File does not request mesh creation");
165
166 // File plus generation, which is what the examples need.
168 json{{"File", "mesh.msh"},
169 {"Mesh_Size", 2.0e-4},
170 {"Create_Mesh", true},
171 {"Info", "uniform"},
172 {"Write_Mesh_File", false},
173 {"Void_Regions", {{0., 0., 0., 1., 1., 1.}}}});
174 inp::MeshDeck d3;
175 d3.readFromJson(j3);
176 check(d3.d_filename == "mesh.msh", "File round-trips with CreateMesh");
177 check(d3.d_createMesh, "CreateMesh.Flag round-trips");
178 check(d3.d_createMeshInfo == "uniform", "CreateMesh.Info round-trips");
179 check(!d3.d_writeMeshFile, "Write_Mesh_File round-trips");
180 checkClose(d3.d_hMeshing, 2.0e-4, "CreateMesh.Mesh_Size round-trips");
181 check(d3.d_voidRegions.size() == 1, "Void_Regions round-trip");
182}
183
185 std::cout << "ContactPairDeck\n";
187 json{{"Contact_Radius_Factor", 0.95},
188 {"Kn", 1.0e12},
189 {"Damping_On", true},
190 {"Epsilon", 0.9},
191 {"Friction_On", true},
192 {"Friction_Coeff", 0.5},
193 {"Kn_Factor", 2.0},
194 {"Beta_n_Factor", 100.},
195 {"K", 2.16e7}});
197 d.readFromJson(j);
198 check(d.d_computeContactR, "Contact_Radius_Factor round-trips");
199 checkClose(d.d_contactR, 0.95, "contact radius factor value");
200 checkClose(d.d_Kn, 1.0e12, "Kn round-trips");
201 checkClose(d.d_eps, 0.9, "Epsilon round-trips");
202 check(d.d_frictionOn, "Friction_On round-trips");
203 checkClose(d.d_mu, 0.5, "Friction_Coeff round-trips");
204 checkClose(d.d_KnFactor, 2.0, "Kn_Factor round-trips");
205 checkClose(d.d_betanFactor, 100., "Beta_n_Factor round-trips");
206
207 // Damping off must zero the damping factor, not carry it.
209 json{{"Contact_Radius_Factor", 0.95},
210 {"Kn", 1.0e12},
211 {"Damping_On", false},
212 {"Epsilon", 0.9},
213 {"Friction_On", false},
214 {"Friction_Coeff", 0.},
215 {"Kn_Factor", 1.},
216 {"Beta_n_Factor", 100.},
217 {"K", 2.16e7}});
219 d2.readFromJson(j2);
220 check(!d2.d_dampingOn, "Damping_On false round-trips");
221 checkClose(d2.d_betanFactor, 0., "Beta_n_Factor zeroed when damping is off");
222}
223
225 std::cout << "ContactDeck\n";
226 auto j = inp::ContactDeck::getExampleJson(json{{"Sets", 2}});
227 check(j.at("Sets") == 2, "Sets count");
228 for (const auto &key : {"Set_1_1", "Set_1_2", "Set_2_2"})
229 check(j.contains(key), std::string("pair ") + key + " present");
230 check(j.contains("Damping_Law"), "Damping_Law present");
231 check(j.contains("Correct_Volume"), "Correct_Volume present");
232}
233
235 std::cout << "PNeighborDeck / PGenDeck\n";
237 json{{"Update_Criteria", "simple_all"},
238 {"Search_Factor", 8.},
239 {"Search_Interval", 5},
240 {"Near_Bd_Nodes_Tol", 0.25}});
242 d.readFromJson(j);
243 check(d.d_updateCriteria == "simple_all", "Update_Criteria round-trips");
244 checkClose(d.d_sFactor, 8., "Search_Factor round-trips");
245 check(d.d_neighUpdateInterval == 5, "Search_Interval round-trips");
246 checkClose(d.d_nearBdNodesTol, 0.25, "Near_Bd_Nodes_Tol round-trips");
247
248 auto jg = inp::PGenDeck::getExampleJson(json{{"Method", "From_File"}});
249 check(jg.at("Method") == "From_File", "Method present");
250}
251
253 std::cout << "TestDeck\n";
254 // An empty name yields an empty block, which the reader ignores.
255 auto empty = inp::TestDeck::getExampleJson(json{{"Test_Name", ""}});
256 check(empty.empty(), "an unnamed test yields an empty block");
257
258 // A plain test name. This used to be emitted as a JSON array.
259 auto j = inp::TestDeck::getExampleJson(json{{"Test_Name", "two_particle"}});
260 check(j.is_object(), "Test block is an object, not an array");
262 d.readFromJson(j);
263 check(d.d_testName == "two_particle", "Test_Name round-trips");
264
265 // readFromJson requires the sub-block for both spellings of the name.
266 for (const std::string name : {"Compressive_Test", "compressive_test"}) {
268 json{{"Test_Name", name},
269 {"Compressive_Test",
270 json{{"Wall_Id", 13}, {"Wall_Force_Direction", 2}}}});
271 checkIsObject(jc, "Compressive_Test");
272 inp::TestDeck dc;
273 dc.readFromJson(jc);
274 check(dc.d_testName == name, name + ": Test_Name round-trips");
275 check(dc.d_particleIdCompressiveTest == 13, name + ": Wall_Id round-trips");
277 name + ": Wall_Force_Direction round-trips");
278 }
279}
280
282 std::cout << "BCBaseDeck\n";
283 geom::GeomData region;
284 region.d_geomName = "rectangle";
285 region.d_geomParams = {0., 0., 0., 1.0e-3, 1.0e-3, 0.};
287
288 // Region plus both function blocks: all three used to come out as arrays.
290 json{{"Type", "Displacement_BC"},
291 {"Particle_List", {0, 1}},
292 {"Particle_Exclude_List", {2}},
293 {"Direction", {1, 2}},
294 {"Zero_Displacement", true},
295 {"Time_Function",
296 json{{"Type", "linear"},
297 {"Parameters", std::vector<double>{0.5}}}},
298 {"Spatial_Function",
299 json{{"Type", "constant"},
300 {"Parameters", std::vector<double>{}}}}},
301 &region);
302 checkIsObject(j, "Region");
303 if (j.contains("Region"))
304 checkIsObject(j.at("Region"), "Geometry");
305 checkIsObject(j, "Time_Function");
306 checkIsObject(j, "Spatial_Function");
307 check(j.at("Time_Function").contains("Type"),
308 "Time_Function.Type survives alongside Parameters");
309 check(j.at("Time_Function").contains("Parameters"),
310 "Time_Function.Parameters present");
311
313 d.readFromJson(j, "Displacement_BC");
314 check(d.d_isRegionActive, "Region is picked up by the reader");
315 check(d.d_regionGeomData.d_geomName == "rectangle",
316 "Region geometry round-trips");
317 check(d.d_timeFnType == "linear", "Time_Function.Type round-trips");
318 check(d.d_timeFnParams.size() == 1, "Time_Function.Parameters round-trip");
319 check(d.d_spatialFnType == "constant", "Spatial_Function.Type round-trips");
320 check(d.d_pList.size() == 2, "Particle_List round-trips");
321 check(d.d_pNotList.size() == 1, "Particle_Exclude_List round-trips");
322 check(d.d_direction.size() == 2, "Direction round-trips");
323 check(d.d_isDisplacementZero, "Zero_Displacement round-trips");
324
325 // Initial condition shape.
327 json{{"Type", "IC"},
328 {"Particle_List", {1}},
329 {"IC_Type", "Constant_Velocity"},
330 {"IC_Vector", {0., -2.5, 0.}}});
331 checkIsObject(jic, "Constant_Velocity");
332 inp::BCBaseDeck dic;
333 dic.readFromJson(jic, "IC");
334 check(dic.d_icType == "Constant_Velocity", "IC type round-trips");
335 checkClose(dic.d_icVec.d_y, -2.5, "IC velocity round-trips");
336}
337
339 std::cout << "BCDeck\n";
341 json{{"Displacement_BC_Sets", 1},
342 {"IC_Sets", 1},
343 {"Gravity", util::Point(0., -10., 0.).toVec()}});
344 checkIsObject(j, "Force_BC");
345 check(j.at("Force_BC").contains("Gravity"), "Gravity present");
346 checkIsObject(j, "Displacement_BC");
347 checkIsObject(j, "IC");
348}
349
350} // namespace
351
352namespace {
353
357void run(void (*fn)(), const std::string &name) {
358 try {
359 fn();
360 } catch (const std::exception &e) {
361 ++g_checks;
362 ++g_failures;
363 std::cerr << " FAIL: " << name << " threw: " << e.what() << "\n";
364 }
365}
366
367} // namespace
368
369int main() {
370 std::cout << "Deck factory round-trip\n"
371 << "-----------------------\n";
372 run(testModelDeck, "ModelDeck");
373 run(testOutputDeck, "OutputDeck");
374 run(testMaterialDeck, "MaterialDeck");
375 run(testMeshDeck, "MeshDeck");
376 run(testContactPairDeck, "ContactPairDeck");
377 run(testContactDeck, "ContactDeck");
378 run(testNeighborAndGenDecks, "PNeighborDeck/PGenDeck");
379 run(testTestDeck, "TestDeck");
380 run(testBCBaseDeck, "BCBaseDeck");
381 run(testBCDeck, "BCDeck");
382
383 std::cout << "-----------------------\n"
384 << g_checks - g_failures << " / " << g_checks << " checks passed\n";
385 if (g_failures > 0) {
386 std::cerr << g_failures << " check(s) failed\n";
387 return 1;
388 }
389 return 0;
390}
nlohmann::ordered_json json
void checkIsObject(const json &j, const std::string &key)
void check(bool ok, const std::string &what)
void checkClose(double got, double want, const std::string &what, double tol=1.0e-12)
void createGeomObject(const std::string &geom_type, const std::vector< double > &params, const std::vector< std::string > &vec_type, const std::vector< std::string > &vec_flag, std::shared_ptr< geom::GeomObject > &obj, bool perform_check)
Definition run.py:1
Input data for geometrical objects.
std::vector< double > d_geomParams
Zone parameters.
std::string d_geomName
Zone type.
User-input data for particle neighbor search.
Definition bcBaseDeck.h:26
std::vector< size_t > d_pList
List of particles (if any)
Definition bcBaseDeck.h:52
std::vector< size_t > d_pNotList
List of particles to not include (if any)
Definition bcBaseDeck.h:55
geom::GeomData d_regionGeomData
Region geometry (if any)
Definition bcBaseDeck.h:49
void readFromJson(const json &j, std::string type)
Reads from json object.
Definition bcBaseDeck.h:294
std::vector< double > d_timeFnParams
List of parameters for function wrt time.
Definition bcBaseDeck.h:93
static json getExampleJson(const json &given=json::object(), const geom::GeomData *region=nullptr)
Returns example JSON object for ModelDeck configuration.
Definition bcBaseDeck.h:233
std::vector< size_t > d_direction
List of dofs on which this bc will be applied.
Definition bcBaseDeck.h:90
std::string d_icType
Type.
Definition bcBaseDeck.h:102
bool d_isDisplacementZero
Specify if this bc corresponds to zero displacement condition.
Definition bcBaseDeck.h:99
bool d_isRegionActive
Flag that indicates if region-based application of boundary condition is active. So cases of 'region'...
Definition bcBaseDeck.h:46
util::Point d_icVec
Initial velocity vector.
Definition bcBaseDeck.h:105
std::string d_timeFnType
Name of the formula with respect to time.
Definition bcBaseDeck.h:68
std::string d_spatialFnType
Name of the formula of with respect to spatial coordinate.
Definition bcBaseDeck.h:81
static json getExampleJson(const json &given=json::object())
Returns example JSON object for ModelDeck configuration.
Definition bcDeck.h:79
static json getExampleJson(const json &given=json::object())
Returns the block with the given fields set.
Structure to read and store particle-particle contact related input data.
void readFromJson(const json &j)
Reads from json object.
static json getExampleJson(const json &given=json::object())
Returns example JSON object for ModelDeck configuration.
Structure to read and store material related data.
static json getExampleJson(const json &given=json::object())
Returns example JSON object for ModelDeck configuration.
void readFromJson(const json &j)
Reads from json object.
Structure to read and store mesh related input data.
Definition meshDeck.h:28
void readFromJson(const json &j)
Definition meshDeck.h:178
static json getExampleJson(const json &given=json::object())
Returns example JSON object for ModelDeck configuration.
Definition meshDeck.h:81
Structure to read and store model related input data.
Definition modelDeck.h:27
void readFromJson(const json &j)
Reads from json object.
Definition modelDeck.h:229
static json getExampleJson(const json &given=json::object())
Returns the block with the given fields set.
Definition modelDeck.h:217
Structure to read input data for performing simulation output.
Definition outputDeck.h:29
void readFromJson(const json &j)
Reads from json object.
Definition outputDeck.h:208
static json getExampleJson(const std::string &)=delete
Returns example JSON object for ModelDeck configuration.
static json getExampleJson(const json &given=json::object())
Returns the block with the given fields set.
Definition pGenDeck.h:82
User-input data for particle neighbor search.
void readFromJson(const json &j)
Reads from json object.
static json getExampleJson(const json &given=json::object())
Returns the block with the given fields set.
Structure to read and store test-related input data.
Definition testDeck.h:26
size_t d_particleForceDirectionCompressiveTest
if it is a compressive test, specify force direction on wall
Definition testDeck.h:34
void readFromJson(const json &j)
Reads from json object.
Definition testDeck.h:121
static json getExampleJson(const json &given=json::object())
Returns the block with the given fields set.
Definition testDeck.h:85
std::string d_testName
Specify test name (if any)
Definition testDeck.h:28
size_t d_particleIdCompressiveTest
if it is a compressive test, specify wall id and direction
Definition testDeck.h:31
A structure to represent 3d vectors.
Definition point.h:30
double d_y
the y coordinate
Definition point.h:36
std::vector< double > toVec() const
Returns coordinate in stl form.
Definition point.h:118
int main()