PeriDEM 0.3.0
PeriDEM -- Peridynamics-based high-fidelity model for granular media
Loading...
Searching...
No Matches
main.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 */
8
10#include "inp/deckIncludes.h"
12#include "periDEMModel.h"
13#include "util/function.h"
14#include "util/io.h"
15#include <algorithm>
16#include <cmath>
17#include <filesystem>
18#include <format>
19#include <fstream>
20#include <limits>
21#include <random>
22#include <stdexcept>
23#include <string>
24#include <thread>
25#include <vector>
26
27namespace {
28
29std::string directoryPathWithTrailingSep(const std::filesystem::path &dir) {
30 namespace fs = std::filesystem;
31 fs::path n = fs::absolute(dir).lexically_normal();
32 std::string s = n.string();
33 if (!s.empty() && s.back() != '/' && s.back() != '\\')
34 s += fs::path::preferred_separator;
35 return s;
36}
37
39 int zone{};
40 double x{}, y{}, z{}, r{}, theta{};
41};
42
43double maxElem(const std::vector<double> &v) {
44 double m = -std::numeric_limits<double>::infinity();
45 for (double x : v)
46 m = std::max(m, x);
47 return m;
48}
49
50bool doesParticleIntersect(const PackedParticle &p, const std::vector<PackedParticle> &existing,
51 const std::vector<double> &rect, double padding) {
52 const double p_rect_lox = p.x - p.r;
53 const double p_rect_loy = p.y - p.r;
54 const double p_rect_hix = p.x + p.r;
55 const double p_rect_hiy = p.y + p.r;
56 if (p_rect_lox < rect[0] + padding || p_rect_loy < rect[1] + padding || p_rect_hix > rect[3] - padding ||
57 p_rect_hiy > rect[4] - padding)
58 return true;
59
60 for (const auto &q : existing) {
61 const double dx = p.x - q.x;
62 const double dy = p.y - q.y;
63 const double dz = p.z - q.z;
64 const double dist = std::sqrt(dx * dx + dy * dy + dz * dz);
65 if (dist <= p.r + q.r + padding)
66 return true;
67 }
68 return false;
69}
70
75std::vector<PackedParticle> generateParticleLocations(const std::vector<double> &in_rect, double max_y,
76 double mesh_size, double R, int N_target,
77 double padding, std::mt19937 &gen) {
78 std::vector<PackedParticle> particles;
79 std::uniform_real_distribution<double> u_r(-0.1 * R, 0.1 * R);
80 /* Do not use uniform_int_distribution: libstdc++ vs libc++ map mt19937
81 * differently, so geom_id diverges while x,y,theta stay the same. */
82
83 const double check_r = R;
84 const int rows = static_cast<int>((max_y - in_rect[1]) / (2.0 * check_r));
85 const double rect_L = in_rect[3] - in_rect[0];
86 const int cols = static_cast<int>(rect_L / (2.0 * check_r));
87
88 int counter = 0;
89 double x_old = in_rect[0];
90 double x_old_right = in_rect[3];
91 double y_old = in_rect[1];
92 const double cz = 0.0;
93
94 std::vector<double> cy_accptd;
95 cy_accptd.push_back(y_old);
96
97 std::vector<double> row_rads_prev{R, R};
98 for (int i = 0; i < rows; ++i) {
99 if (i > 0)
100 y_old = maxElem(cy_accptd) + maxElem(row_rads_prev);
101
102 std::vector<double> row_rads{R};
103
104 if (y_old + padding + maxElem(row_rads) >= max_y)
105 break;
106
107 int num_p_cols = 0;
108 int j = 0;
109 while (true) {
110 if (num_p_cols > cols - 1 || j > 100 * N_target)
111 break;
112 if (counter >= N_target)
113 break;
114
115 if (j == 0) {
116 x_old = in_rect[0];
117 x_old_right = in_rect[3];
118 }
119
120 const int p_zone = static_cast<int>(gen() % 8);
121 const double r0 = R;
122 double r = r0 + u_r(gen);
123
124 double cx = 0., cy = 0.;
125 if (i % 2 == 0) {
126 std::uniform_real_distribution<double> rph(-0.1 * r0, 0.05 * r0);
127 std::uniform_real_distribution<double> rpv(-0.05 * r0, 0.05 * r0);
128 const double cx0 = x_old_right - padding - r;
129 cx = cx0 - rph(gen);
130 cy = y_old + padding + r + rpv(gen);
131 } else {
132 std::uniform_real_distribution<double> rph(-0.05 * r0, 0.1 * r0);
133 std::uniform_real_distribution<double> rpv(-0.05 * r0, 0.05 * r0);
134 const double cx0 = x_old + padding + r;
135 cx = cx0 + rph(gen);
136 cy = y_old + padding + r + rpv(gen);
137 }
138
139 PackedParticle trial{p_zone, cx, cy, cz, r, 0.0};
140 if (!doesParticleIntersect(trial, particles, in_rect, padding)) {
141 std::uniform_real_distribution<double> orient(0.0, 2.0 * M_PI);
142 trial.theta = orient(gen);
143 particles.push_back(trial);
144 row_rads.push_back(trial.r);
145 cy_accptd.push_back(cy);
146 if (i % 2 == 0)
147 x_old_right = cx - trial.r;
148 else
149 x_old = cx + trial.r;
150
151 ++counter;
152 num_p_cols++;
153 }
154 ++j;
155 }
156 row_rads_prev = std::move(row_rads);
157 }
158 return particles;
159}
160
163 double min_x{}, max_x{}, min_y{}, max_y{};
164};
165
166PackedBounds packedAxisBounds(const std::vector<PackedParticle> &packed) {
167 PackedBounds b;
168 b.min_x = std::numeric_limits<double>::infinity();
169 b.max_x = -std::numeric_limits<double>::infinity();
170 b.min_y = std::numeric_limits<double>::infinity();
171 b.max_y = -std::numeric_limits<double>::infinity();
172 for (const auto &p : packed) {
173 b.min_x = std::min(b.min_x, p.x - p.r);
174 b.max_x = std::max(b.max_x, p.x + p.r);
175 b.min_y = std::min(b.min_y, p.y - p.r);
176 b.max_y = std::max(b.max_y, p.y + p.r);
177 }
178 return b;
179}
180
181json contactPairJson(double R_contact_factor, bool damping_on, bool friction_on, double Kn, double beta_n_eps,
182 double friction_coeff, double Kn_factor, double beta_n_factor) {
183 json j;
184 j["Contact_Radius_Factor"] = R_contact_factor;
185 if (Kn < 1e-20) {
186 j["Kn"] = 0.0;
187 } else {
188 j["Kn"] = Kn;
189 }
190 j["Damping_On"] = damping_on;
191 j["Epsilon"] = beta_n_eps;
192 j["Friction_On"] = friction_on;
193 j["Friction_Coeff"] = friction_coeff;
194 j["Kn_Factor"] = Kn_factor;
195 j["Beta_n_Factor"] = damping_on ? beta_n_factor : 0.0;
196 return j;
197}
198
199double KnFromBulk(double Ka, double Kb, double horizon) {
200 return util::normalContactStiffness(Ka, Kb, horizon);
201}
202
203json buildInputJson(const std::string &output_path_for_deck, const std::filesystem::path &inp_dir,
204 int argc, char *argv[]) {
205
206 util::io::InputParser input(argc, argv);
207
208 const std::vector<double> center = {0.0, 0.0, 0.0};
209 const double R = 0.001;
210 const double mesh_size = R / 5.0;
211 const double horizon = 2.0 * mesh_size;
212
213 const double Lin = 0.05;
214 const double Win = 0.04;
215
216 constexpr double wall_top_inset = 1e-7;
217 constexpr double annulus_inset = 1e-7;
218 /* Clearance between void floor / plate and particle circum-bounds (~1–2 mesh). */
219 constexpr double clearance_mesh = 1.5;
220 /* Meshed triangles/hex/drum can extend slightly beyond circumcircle r in VTU. */
221 const double geom_pad = 0.25 * mesh_size;
222 const double plate_thickness = std::max(3.0 * mesh_size, 2.0 * mesh_size);
223
224 const double w_drum2d = R * 0.2;
225
226 const double final_time = 0.001;
227 size_t num_steps = 400;
228 if (input.cmdOptionExists("-numSteps"))
229 num_steps = static_cast<size_t>(std::stoul(input.getCmdOption("-numSteps")));
230
231 const size_t num_outputs = 10;
232 const size_t dt_out_n = std::max<size_t>(1, num_steps / num_outputs);
233 const size_t test_dt_out_n = std::max<size_t>(1, dt_out_n / 10);
234
235 const double rho_wall = 600.;
236 const double poisson_wall = 0.25;
237 const double K_wall = 1.e+4;
238 const double E_wall = material::toE(K_wall, poisson_wall);
239 const double G_wall = material::toGE(E_wall, poisson_wall);
240 const double KIc_wall = 5e+6;
241 const double Gc_wall = material::toGc(KIc_wall, poisson_wall, E_wall);
242
243 const double rho_p = 600.;
244 const double poisson_p = poisson_wall;
245 const double K_p = 5.e+3;
246 const double E_p = material::toE(K_p, poisson_p);
247 const double G_p = material::toGE(E_p, poisson_p);
248 const double KIc_p = 5e+6;
249 const double Gc_p = material::toGc(KIc_p, poisson_p, E_p);
250
251 const double R_contact_factor = 0.95;
252 const double padding = 1.1 * R_contact_factor * mesh_size;
253 const int N_target = 500;
254
255 const std::vector<double> in_rect = {center[0] - 0.5 * Lin, center[1] - 0.5 * Win, center[2],
256 center[0] + 0.5 * Lin, center[1] + 0.5 * Win, center[2]};
257
258 std::mt19937 gen(30);
259 const double max_y = in_rect[4] - clearance_mesh * mesh_size;
260 std::vector<PackedParticle> packed =
261 generateParticleLocations(in_rect, max_y, mesh_size, R, N_target, padding, gen);
262 if (packed.empty())
263 throw std::runtime_error("compression_large_set_inbuilt_2mat2contact: particle pack is empty");
264 for (auto &p : packed)
265 p.z = 0.0;
266
267 const double m = clearance_mesh * mesh_size;
268 /* Seat bed: lowest circum-bottom = void_floor + m (void floor = seed inner bottom). */
269 {
270 const PackedBounds bb0 = packedAxisBounds(packed);
271 const double delta_y = (in_rect[1] + m) - bb0.min_y;
272 for (auto &p : packed)
273 p.y += delta_y;
274 }
275
276 const PackedBounds bb = packedAxisBounds(packed);
277
278 /* Inner void (Gmsh “remove” rectangle): circum-bounds ± m ± geom_pad in x; floor at seed; top = domain. */
279 const double void_lox = bb.min_x - m - geom_pad;
280 const double void_hix = bb.max_x + m + geom_pad;
281 const double void_loy = in_rect[1];
282 /* Moving plate: bottom = top of bed + clearance + pad so FE nodes stay under the plate. */
283 const double plate_bottom_y = bb.max_y + m + geom_pad;
284 double plate_top_y = plate_bottom_y + plate_thickness;
285
286 /* Outer solid: wrap void with wall thickness; extend upward slightly past the plate. */
287 const double side = std::max(1.5 * horizon, 2.0 * mesh_size);
288 const double out_lox = void_lox - side;
289 const double out_hix = void_hix + side;
290 const double out_loy = void_loy - side;
291 const double out_hiy = plate_top_y + wall_top_inset + 2.5 * mesh_size;
292
293 std::vector<double> out_rect = {out_lox, out_loy, center[2], out_hix, out_hiy, center[2]};
294
295 /* Open-top void: inner cut runs to outer top (inset for Gmsh strict-inside). */
296 const double void_hi_y = out_rect[4] - annulus_inset;
297 std::vector<double> remove_rect = {void_lox, void_loy, center[2], void_hix, void_hi_y, center[2]};
298
299 util::io::print(std::format(
300 "[compression_large_set_inbuilt_2mat2contact] bbox(center±r) x∈[{:.6f},{:.6f}] y∈[{:.6f},{:.6f}]; "
301 "void x∈[{:.6f},{:.6f}] y_lo {:.6f}; plate y [{:.6f},{:.6f}]; void_hi {:.6f}; outer y∈[{:.6f},{:.6f}]\n",
302 bb.min_x, bb.max_x, bb.min_y, bb.max_y, void_lox, void_hix, void_loy, plate_bottom_y, plate_top_y,
303 void_hi_y, out_rect[1], out_rect[4]));
304
305 std::vector<double> moving_rect = {void_lox, plate_bottom_y, center[2], void_hix, plate_top_y, center[2]};
306
307 std::vector<double> fixed_container_params;
308 fixed_container_params.insert(fixed_container_params.end(), remove_rect.begin(), remove_rect.end());
309 fixed_container_params.insert(fixed_container_params.end(), out_rect.begin(), out_rect.end());
310
311 const size_t n_pack = packed.size();
312 const size_t n_wall_fixed = n_pack;
313 const size_t n_wall_moving = n_pack + 1;
314 const size_t n_total = n_pack + 2;
315
316 /* Reference geometries at origin; packing uses circum-radius ~ R for all zones. */
317 const double ell_a = 0.95 * R;
318 const double ell_b = 0.58 * R;
319 const double ell_theta = 0.35;
320 const double sq_half = R / std::sqrt(2.0);
321 /* Axis-aligned rectangle (~same circum extent as R). */
322 const double rx = 0.82 * R;
323 const double ry = 0.62 * R;
324
325 std::vector<geom::GeomData> pGeomVec(10);
326 pGeomVec[0].d_geomName = "circle";
327 pGeomVec[0].d_geomParams = {R, center[0], center[1], center[2]};
328 pGeomVec[1].d_geomName = "triangle";
329 pGeomVec[1].d_geomParams = {R, center[0], center[1], center[2]};
330 pGeomVec[2].d_geomName = "drum2d";
331 pGeomVec[2].d_geomParams = {R, w_drum2d, center[0], center[1], center[2]};
332 pGeomVec[3].d_geomName = "hexagon";
333 pGeomVec[3].d_geomParams = {R, center[0], center[1], center[2]};
334 pGeomVec[4].d_geomName = "ellipse";
335 pGeomVec[4].d_geomParams = {ell_a, ell_b, ell_theta, center[0], center[1], center[2]};
336 pGeomVec[5].d_geomName = "rectangle";
337 pGeomVec[5].d_geomParams = {-rx, -ry, center[2], rx, ry, center[2]};
338 pGeomVec[6].d_geomName = "square";
339 pGeomVec[6].d_geomParams = {-sq_half, -sq_half, center[2], sq_half, sq_half, center[2]};
340 pGeomVec[7].d_geomName = "circle_minus_circle";
341 pGeomVec[7].d_geomParams = {center[0], center[1], center[2], R, 0.35 * R};
342 pGeomVec[8].d_geomName = "rectangle_minus_rectangle";
343 pGeomVec[8].d_geomParams = fixed_container_params;
344 pGeomVec[9].d_geomName = "rectangle";
345 pGeomVec[9].d_geomParams = moving_rect;
346
347 for (auto &g : pGeomVec)
349
350 /* Annulus composite centroid can pick up tiny numerical z; 2D setup keeps all sites on z = 0. */
351 const util::Point cfix = pGeomVec[8].d_geom_p->center();
352 const util::Point cmov = pGeomVec[9].d_geom_p->center();
353 const util::Point site_wall_fixed(cfix.d_x, cfix.d_y, 0.0);
354 const util::Point site_wall_moving(cmov.d_x, cmov.d_y, 0.0);
355
356 auto modelDeckJson = inp::ModelDeck::getExampleJson(
357 json{{"Dimension", 2},
358 {"Final_Time", final_time},
359 {"Time_Steps", num_steps},
360 {"Discretization_Type", json{{"Spatial", "finite_difference"}, {"Time", "central_difference"}}},
361 {"Populate_ElementNodeConnectivity", true},
362 {"Quad_Approximation_Order", 2},
363 {"Particle_Sim_Type", "Multi_Particle"},
364 {"Seed", 0}});
365
366 std::vector<std::string> out_tags = {"Displacement", "Velocity", "Force", "Force_Density", "Damage_Z",
367 "Damage", "Nodal_Volume", "Zone_ID", "Particle_ID", "Fixity",
368 "Force_Fixity", "Contact_Nodes", "No_Fail_Node", "Boundary_Node_Flag"};
369 /* Perform_FE_Out must be true so VTU includes element connectivity (appendMesh). If false, only
370 * points are written (appendNodes) and ParaView’s default “Surface” view looks empty — use
371 * Representation → Points, or enable this flag. Large packs often leave FE out. */
372 auto outputDeckJson = inp::OutputDeck::getExampleJson(
373 json{{"File_Format", "vtu"},
374 {"Path", output_path_for_deck},
375 {"Tags", out_tags},
376 {"Output_Interval", dt_out_n},
377 {"Debug", 2},
378 {"Perform_FE_Out", true},
379 {"Compress_Type", "zlib"},
380 {"Perform_Out", true},
381 {"Test_Output_Interval", test_dt_out_n},
382 {"Tag_PP", "0"},
383 {"PVD_Collection", true}});
384
385 auto bcDeckJson = inp::BCDeck::getExampleJson(
386 json{{"Displacement_BC_Sets", 2},
387 {"Gravity", util::Point(0, -10, 0).toVec()}});
388
389 bcDeckJson["Displacement_BC"]["Set_1"] = inp::BCBaseDeck::getExampleJson(
390 json{{"Type", "Displacement_BC"},
391 {"Particle_List", {n_wall_fixed}},
392 {"Direction", {1, 2}},
393 {"Zero_Displacement", true}});
394
395 json set2;
396 set2["Particle_List"] = std::vector<size_t>{n_wall_moving};
397 set2["Direction"] = std::vector<size_t>{2};
398 set2["Time_Function"] = json{{"Type", "linear"}, {"Parameters", std::vector<double>{-0.06}}};
399 set2["Spatial_Function"] = json{{"Type", "constant"}};
400 bcDeckJson["Displacement_BC"]["Set_2"] = set2;
401
402 json pDeckJson = json::object();
403
404 pDeckJson["Particle"] = inp::ParticleDeck::getParticleGeomExampleJson(pGeomVec);
405
406 json meshRoot = json{{"Sets", 10}};
407 const char *mesh_names[] = {"mesh_cir", "mesh_tri", "mesh_drum2d", "mesh_hex",
408 "mesh_ellipse", "mesh_rect", "mesh_square", "mesh_circirc",
409 "mesh_fixed_container", "mesh_moving_container"};
410 for (int zi = 0; zi < 10; ++zi) {
411 const std::string fname = (inp_dir / (std::string(mesh_names[zi]) + ".msh")).string();
412 meshRoot["Set_" + std::to_string(zi + 1)] =
413 json{{"File", fname},
414 {"CreateMesh",
415 json{{"Flag", true}, {"Info", "gmsh_builtin_mesh"}, {"Mesh_Size", mesh_size}, {"Write_Mesh_File", true}}}};
416 }
417 pDeckJson["Mesh"] = meshRoot;
418
419 /* Two material laws: 0 = particle, 1 = wall (fixed + moving). geom_id still selects mesh shape (10 meshes). */
420 json matRoot = json{{"Sets", 2}};
421 matRoot["Set_1"] = inp::MaterialDeck::getExampleJson(
422 json{{"Type", "PDState"},
423 {"Is_Plane_Strain", false},
424 {"Horizon", horizon},
425 {"Density", rho_p},
426 {"K", K_p},
427 {"G", G_p},
428 {"Gc", Gc_p},
429 {"Compute_From_Classical", true},
430 {"Influence_Function", json{{"Type", 0}}}});
431 matRoot["Set_2"] =
433 json{{"Type", "PDState"},
434 {"Is_Plane_Strain", false},
435 {"Horizon", horizon},
436 {"Density", rho_wall},
437 {"K", K_wall},
438 {"G", G_wall},
439 {"Gc", Gc_wall},
440 {"Compute_From_Classical", true},
441 {"Influence_Function", json{{"Type", 0}}}});
442 pDeckJson["Material"] = matRoot;
443
444 const double Kn_pp = KnFromBulk(K_p, K_p, horizon);
445 const double Kn_pw = KnFromBulk(K_p, K_wall, horizon);
446 const double Kn_ww = 0.0;
447
448 const double beta_n_eps = 0.95;
449 const double friction_coeff = 0.5;
450 const bool damping_on = true;
451 const bool friction_on = false;
452 const double beta_n_factor = 100.;
453 const double Kn_factor = 1.;
454
455 /* Contact zones: 0 = granular, 1 = wall (fixed + moving). 2×2 matrix → three unique pairs (1-1, 1-2, 2-2). */
456 const json j_contact_pp =
457 contactPairJson(R_contact_factor, damping_on, friction_on, Kn_pp, beta_n_eps, friction_coeff, Kn_factor,
458 beta_n_factor);
459 const json j_contact_pw =
460 contactPairJson(R_contact_factor, damping_on, friction_on, Kn_pw, beta_n_eps, friction_coeff, Kn_factor,
461 beta_n_factor);
462 const json j_contact_ww =
463 contactPairJson(R_contact_factor, damping_on, friction_on, Kn_ww, beta_n_eps, friction_coeff, Kn_factor,
464 beta_n_factor);
465
466 json contactRoot = inp::ContactDeck::getExampleJson(json{{"Sets", 2}});
467 contactRoot["Set_1_1"] = j_contact_pp;
468 contactRoot["Set_1_2"] = j_contact_pw;
469 contactRoot["Set_2_2"] = j_contact_ww;
470 pDeckJson["Contact"] = contactRoot;
471
472 pDeckJson["Neighbor"] = inp::PNeighborDeck::getExampleJson(
473 json{{"Update_Criteria", "simple_all"},
474 {"Search_Factor", 10.0},
475 {"Search_Interval", 100},
476 {"Near_Bd_Nodes_Tol", 0.5}});
477
478 auto pGenJson = inp::PGenDeck::getExampleJson(json{{"Method", "From_File"}});
479 pGenJson["Random_Rotation"] = false;
480 pGenJson["Data"]["N"] = n_total;
481
482 constexpr size_t k_mat_particle = 0;
483 constexpr size_t k_mat_wall = 1;
484 constexpr size_t k_contact_grains = 0;
485 constexpr size_t k_contact_wall = 1;
486
487 for (size_t pi = 0; pi < n_pack; ++pi) {
488 const auto &p = packed[pi];
489 pGenJson["Data"][std::to_string(pi)] = json{
490 {"x", p.x}, {"y", p.y}, {"z", 0.0}, {"theta", p.theta},
491 {"s", 1.0}, {"geom_id", static_cast<size_t>(p.zone)},
492 {"mat_id", k_mat_particle},
493 {"contact_id", k_contact_grains},
494 };
495 }
496
497 pGenJson["Data"][std::to_string(n_pack)] = json{{"x", site_wall_fixed.d_x},
498 {"y", site_wall_fixed.d_y},
499 {"z", 0.0},
500 {"theta", 0.0},
501 {"s", 1.0},
502 {"geom_id", size_t(8)},
503 {"mat_id", k_mat_wall},
504 {"contact_id", k_contact_wall}};
505
506 pGenJson["Data"][std::to_string(n_pack + 1)] = json{{"x", site_wall_moving.d_x},
507 {"y", site_wall_moving.d_y},
508 {"z", 0.0},
509 {"theta", 0.0},
510 {"s", 1.0},
511 {"geom_id", size_t(9)},
512 {"mat_id", k_mat_wall},
513 {"contact_id", k_contact_wall}};
514
515 pDeckJson["Particle_Generation"] = pGenJson;
516
517 /* Top-level comment only: ignored by inp::Input (only known sections are parsed). */
518 return json{{"Comment",
519 "compression_large_set_inbuilt"},
520 {"Model", modelDeckJson},
521 {"Output", outputDeckJson},
522 {"Force_BC", bcDeckJson["Force_BC"]},
523 {"Displacement_BC", bcDeckJson["Displacement_BC"]},
524 {"Particle", pDeckJson["Particle"]},
525 {"Mesh", pDeckJson["Mesh"]},
526 {"Material", pDeckJson["Material"]},
527 {"Contact", pDeckJson["Contact"]},
528 {"Neighbor", pDeckJson["Neighbor"]},
529 {"Particle_Generation", pDeckJson["Particle_Generation"]}};
530}
531
532} // namespace
533
534int main(int argc, char *argv[]) {
535
536 util::parallel::initMpi(argc, argv);
537 int mpiSize = util::parallel::mpiSize(), mpiRank = util::parallel::mpiRank();
538 util::io::print(std::format("Initialized MPI. MPI size = {}, MPI rank = {}\n", mpiSize, mpiRank));
540
541 util::io::InputParser input(argc, argv);
542
543 unsigned int nThreads;
544 if (input.cmdOptionExists("-nThreads"))
545 nThreads = std::stoi(input.getCmdOption("-nThreads"));
546 else {
547 nThreads = std::thread::hardware_concurrency();
548 util::io::print(std::format("Running test with default number of threads = {}\n", nThreads));
549 }
551 util::io::print(std::format("Number of threads = {}\n", util::parallel::getNThreads()));
552
553 namespace fs = std::filesystem;
554 const fs::path cwd = fs::current_path();
555
556 fs::path out_dir = cwd / "out";
557 fs::path inp_dir = cwd / "inp";
558
559 if (input.cmdOptionExists("-outputDir")) {
560 fs::path p = input.getCmdOption("-outputDir");
561 out_dir = p.is_absolute() ? std::move(p) : cwd / p;
562 }
563 if (input.cmdOptionExists("-inputDir")) {
564 fs::path p = input.getCmdOption("-inputDir");
565 inp_dir = p.is_absolute() ? std::move(p) : cwd / p;
566 } else if (input.cmdOptionExists("-outputDir")) {
567 inp_dir = out_dir.parent_path() / "inp";
568 }
569
570 fs::create_directories(out_dir);
571 fs::create_directories(inp_dir);
572
573 const std::string output_path_for_deck = directoryPathWithTrailingSep(out_dir);
574
575 util::io::print(std::format("Output directory (VTU, log.txt): {}\n", fs::absolute(out_dir).string()));
576 util::io::print(std::format("Input directory (input.json, meshes): {}\n", fs::absolute(inp_dir).string()));
577
578 auto inputJson = buildInputJson(output_path_for_deck, inp_dir, argc, argv);
579
580 const fs::path input_json_path = inp_dir / "input.json";
581 {
582 std::ofstream os(input_json_path);
583 if (!os)
584 throw std::runtime_error("Failed to open " + input_json_path.string() + " for writing.");
585 os << inputJson.dump(2);
586 }
587 util::io::print(std::format("Wrote deck to {}\n", fs::absolute(input_json_path).string()));
588
589 auto deck = std::make_shared<inp::Input>(inputJson);
590
591 PeriDEMModel dem(deck);
592 dem.run(deck);
593
594 return EXIT_SUCCESS;
595}
int main(int argc, char *argv[])
Definition main.cpp:29
void run(std::shared_ptr< inp::Input > &deck)
Input command line argument parser.
Definition inputParser.h:28
bool cmdOptionExists(const std::string &option) const
Check if argument exists.
Definition inputParser.h:60
const std::string & getCmdOption(const std::string &option) const
Get value of argument specified by key.
Definition inputParser.h:45
nlohmann::ordered_json json
json buildInputJson(const std::string &output_path_for_deck, const std::filesystem::path &inp_dir, int argc, char *argv[])
Definition main.cpp:203
std::string directoryPathWithTrailingSep(const std::filesystem::path &dir)
Definition main.cpp:34
bool doesParticleIntersect(const PackedParticle &p, const std::vector< PackedParticle > &existing, const std::vector< double > &rect, double padding)
Definition main.cpp:50
std::vector< PackedParticle > generateParticleLocations(const std::vector< double > &in_rect, double max_y, double mesh_size, double R, int N_target, double padding, std::mt19937 &gen)
Definition main.cpp:75
PackedBounds packedAxisBounds(const std::vector< PackedParticle > &packed)
Definition main.cpp:166
double KnFromBulk(double Ka, double Kb, double horizon)
Definition main.cpp:199
double maxElem(const std::vector< double > &v)
Definition main.cpp:43
json contactPairJson(double R_contact_factor, bool damping_on, bool friction_on, double Kn, double beta_n_eps, double friction_coeff, double Kn_factor, double beta_n_factor)
Definition main.cpp:181
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)
double toGc(double KIc, double nu, double E)
Compute critical energy release rate Gc from critical stress-intensity factor KIc,...
double toGE(double E, double nu)
Compute shear modulus from Young's modulus E and Poisson's ratio nu.
double toE(double K, double nu)
Compute Young's modulus E from Bulk modulus K and Poisson's ratio nu.
void print(const T &msg, int nt=print_default_tab, int printMpiRank=print_default_mpi_rank)
Prints formatted information.
Definition io.h:171
unsigned int getNThreads()
Get number of threads to be used by taskflow.
void initNThreads(unsigned int nThreads=std::thread::hardware_concurrency())
Initializes MpiStatus struct.
const MpiStatus * getMpiStatus()
Returns pointer to MpiStatus struct.
void initMpi(int argc=0, char *argv[]=nullptr)
Initializes MPI and also creates MpiStatus struct.
int mpiSize()
Get size (number) of processors.
int mpiRank()
get rank (id) of this processor
double normalContactStiffness(const double &K1, const double &K2, const double &horizon, int horizonPower=5)
Silling normal contact stiffness from two bulk moduli and the horizon.
Definition function.cpp:139
static json getExampleJson(const json &given=json::object(), const geom::GeomData *region=nullptr)
Returns example JSON object for ModelDeck configuration.
Definition bcBaseDeck.h:233
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.
static json getExampleJson(const json &given=json::object())
Returns example JSON object for ModelDeck configuration.
static json getExampleJson(const json &given=json::object())
Returns the block with the given fields set.
Definition modelDeck.h:217
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
static json getExampleJson(const json &given=json::object())
Returns the block with the given fields set.
static json getParticleGeomExampleJson(std::vector< geom::GeomData > pGeomVec=std::vector< geom::GeomData >())
Returns example JSON object for ModelDeck configuration.
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
double d_x
the x coordinate
Definition point.h:33