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 * Hollow ellipse dropped onto a short tip-up triangle (pointy tip only).
8 * Tip contact opens a crack; target is bipartition into two pieces.
9 *
10 * -outputDir / -inputDir / -finalTime / -numSteps / -nThreads
11 */
12
13#include "inp/deckIncludes.h"
15#include "periDEMModel.h"
16#include "util/function.h"
17#include "util/io.h"
18#include "util/parallelUtil.h"
19
20#include <cmath>
21#include <cstdlib>
22#include <filesystem>
23#include <format>
24#include <fstream>
25#include <iomanip>
26#include <memory>
27#include <stdexcept>
28#include <string>
29#include <thread>
30#include <vector>
31
32namespace {
33
34std::string directoryPathWithTrailingSep(const std::filesystem::path &dir) {
35 namespace fs = std::filesystem;
36 fs::path n = fs::absolute(dir).lexically_normal();
37 std::string s = n.string();
38 if (!s.empty() && s.back() != '/' && s.back() != '\\')
39 s += fs::path::preferred_separator;
40 return s;
41}
42
43json buildInputJson(const std::string &output_path, const std::filesystem::path &mesh_tri,
44 const std::filesystem::path &mesh_ell, double final_time, size_t num_steps,
45 double mesh_size, double horizon) {
46 // Short tip — point only, not a tall triangle. Narrower tip concentrates load.
47 const double W = 0.0008;
48 const double H = 0.0012;
49
50 // Hollow ellipse (visibly elliptical ring).
51 const double a_out = 0.0020;
52 const double b_out = 0.0014;
53 // Thin ring so a tip-seeded crack can bipartition.
54 const double a_in = 0.00180;
55 const double b_in = 0.00122;
56 const double ell_theta = 0.0;
57 const double tip_gap = 0.00008;
58 const double tip_y = H;
59 const double ell_cy = tip_y + tip_gap + b_out;
60
61 const double rho_t = 1200.0;
62 const double K_t = 2.16e7;
63 const double nu_t = 0.25;
64 const double E_t = material::toE(K_t, nu_t);
65 const double G_t = material::toGE(E_t, nu_t);
66 const double Gc_t = 200.0;
67
68 const double rho_e = 1200.0;
69 const double K_e = 2.16e7;
70 const double nu_e = 0.25;
71 const double E_e = material::toE(K_e, nu_e);
72 const double G_e = material::toGE(E_e, nu_e);
73 // Low Gc so tip damage runs into a diametral through-crack.
74 const double Gc_e = 1.0;
75
76 const double R_contact_factor = 0.95;
77 const double Kn_tt = util::normalContactStiffness(K_t, K_t, horizon);
78 const double Kn_ee = util::normalContactStiffness(K_e, K_e, horizon);
79 const double Kn_te = util::normalContactStiffness(K_t, K_e, horizon);
80
81 const double g = 10.0;
82 const double ic_vy = -2.5;
83
84 const size_t dt_out_n = std::max<size_t>(1, num_steps / 40);
85
87 json{{"Dimension", 2},
88 {"Final_Time", final_time},
89 {"Time_Steps", num_steps},
90 {"Discretization_Type", json{{"Spatial", "finite_difference"}, {"Time", "central_difference"}}},
91 {"Populate_ElementNodeConnectivity", true},
92 {"Quad_Approximation_Order", 2},
93 {"Particle_Sim_Type", "Multi_Particle"},
94 {"Seed", 0}});
95 model["Bond_Break"] = "tension";
96 // No self-contact across broken bonds — otherwise the crack stays glued.
97 model["Self_Contact"] = "none";
98
100 json{{"File_Format", "vtu"},
101 {"Path", output_path},
102 {"Tags", std::vector<std::string>({"Displacement", "Velocity", "Force", "Damage_Z", "Damage",
103 "Particle_ID", "Fixity"})},
104 {"Output_Interval", dt_out_n},
105 {"Debug", 1},
106 {"Perform_FE_Out", true},
107 {"Compress_Type", "zlib"},
108 {"Perform_Out", true},
109 {"Test_Output_Interval", 1},
110 {"Tag_PP", ""},
111 {"PVD_Collection", true}});
112
114 json{{"Displacement_BC_Sets", 1},
115 {"IC_Sets", 1},
116 {"Gravity", util::Point(0., -g, 0.).toVec()}});
117 bc["Displacement_BC"]["Set_1"] = inp::BCBaseDeck::getExampleJson(
118 json{{"Type", "Displacement_BC"},
119 {"Particle_List", {0}},
120 {"Direction", {1, 2}},
121 {"Zero_Displacement", true}});
122
123 bc["IC"]["Set_1"] = {
124 {"Particle_List", std::vector<size_t>{1}},
125 {"Constant_Velocity", {{"Velocity_Vector", std::vector<double>{0., ic_vy, 0.}}}}};
126
127 std::vector<geom::GeomData> pGeom(2);
128 pGeom[0].d_geomName = "triangle";
129 pGeom[0].d_geomParams = {-0.5 * W, 0., 0., 0.5 * W, 0., 0., 0., H, 0.};
130
131 pGeom[1].d_geomName = "ellipse_minus_ellipse";
132 pGeom[1].d_geomParams = {a_out, b_out, a_in, b_in, ell_theta, 0., 0., 0.};
133
134 for (auto &g : pGeom)
136
137 const util::Point c_tri = pGeom[0].d_geom_p->center();
138
139 json pDeck = json::object();
140 pDeck["Particle"] = inp::ParticleDeck::getParticleGeomExampleJson(pGeom);
141
142 pDeck["Mesh"] = {
143 {"Sets", 2},
144 {"Set_1",
145 {{"File", mesh_tri.string()},
146 {"CreateMesh",
147 {{"Flag", true},
148 {"Info", "gmsh_builtin_mesh"},
149 {"Mesh_Size", mesh_size},
150 {"Write_Mesh_File", true}}}}},
151 {"Set_2",
152 {{"File", mesh_ell.string()},
153 {"CreateMesh",
154 {{"Flag", true},
155 {"Info", "gmsh_builtin_mesh"},
156 {"Mesh_Size", mesh_size},
157 {"Write_Mesh_File", true}}}}}};
158
160 mat["Set_1"] =
162 json{{"Type", "PDState"},
163 {"Is_Plane_Strain", false},
164 {"Horizon", horizon},
165 {"Density", rho_t},
166 {"K", K_t},
167 {"G", G_t},
168 {"Gc", Gc_t},
169 {"Compute_From_Classical", true},
170 {"Influence_Function", json{{"Type", 0}}}});
171 mat["Set_2"] =
173 json{{"Type", "PDState"},
174 {"Is_Plane_Strain", false},
175 {"Horizon", horizon},
176 {"Density", rho_e},
177 {"K", K_e},
178 {"G", G_e},
179 {"Gc", Gc_e},
180 {"Compute_From_Classical", true},
181 {"Influence_Function", json{{"Type", 0}}}});
182 pDeck["Material"] = mat;
183
186 json{{"Contact_Radius_Factor", R_contact_factor},
187 {"Kn", Kn_tt},
188 {"Damping_On", true},
189 {"Epsilon", 0.95},
190 {"Friction_On", false},
191 {"Friction_Coeff", 0.0},
192 {"Kn_Factor", 1.0},
193 {"Beta_n_Factor", 100.0},
194 {"K", K_t}});
195 // Tip contact: enough Beta_n to drive a crack, damping on to limit spray.
197 json{{"Contact_Radius_Factor", 0.90},
198 {"Kn", Kn_te},
199 {"Damping_On", true},
200 {"Epsilon", 0.4},
201 {"Friction_On", false},
202 {"Friction_Coeff", 0.0},
203 {"Kn_Factor", 1.0},
204 {"Beta_n_Factor", 8.0},
205 {"K", util::harmonicMean(K_t, K_e)}});
207 json{{"Contact_Radius_Factor", R_contact_factor},
208 {"Kn", Kn_ee},
209 {"Damping_On", true},
210 {"Epsilon", 0.95},
211 {"Friction_On", false},
212 {"Friction_Coeff", 0.0},
213 {"Kn_Factor", 1.0},
214 {"Beta_n_Factor", 100.0},
215 {"K", K_e}});
216 contact["Set_1_1"] = cpair_wall;
217 contact["Set_1_1"]["Kn"] = Kn_tt;
218 contact["Set_1_1"]["K"] = K_t;
219 contact["Set_1_2"] = cpair_tip;
220 contact["Set_1_2"]["Kn"] = Kn_te;
221 contact["Set_1_2"]["K"] = util::harmonicMean(K_t, K_e);
222 contact["Set_2_2"] = cpair_ell;
223 contact["Set_2_2"]["Kn"] = Kn_ee;
224 contact["Set_2_2"]["K"] = K_e;
225 contact["Damping_Law"] = "com_and_node";
226 contact["Friction_Law"] = "coulomb_simple";
227 pDeck["Contact"] = contact;
228
229 pDeck["Neighbor"] = inp::PNeighborDeck::getExampleJson(
230 json{{"Update_Criteria", "simple_all"},
231 {"Search_Factor", 8.0},
232 {"Search_Interval", 5},
233 {"Near_Bd_Nodes_Tol", 0.5}});
234
235 auto gen = inp::PGenDeck::getExampleJson(json{{"Method", "From_File"}});
236 gen["Random_Rotation"] = false;
237 gen["Data"]["N"] = 2;
238 gen["Data"]["0"] = {{"x", c_tri.d_x}, {"y", c_tri.d_y}, {"z", 0.0},
239 {"theta", 0.0}, {"s", 1.0},
240 {"geom_id", 0}, {"mat_id", 0}, {"contact_id", 0},
241 {"is_wall", true}};
242 gen["Data"]["1"] = {{"x", 0.0}, {"y", ell_cy}, {"z", 0.0},
243 {"theta", 0.0}, {"s", 1.0},
244 {"geom_id", 1}, {"mat_id", 1}, {"contact_id", 1}};
245 pDeck["Particle_Generation"] = gen;
246
247 json j = json::object();
248 j["Model"] = model;
249 j["Output"] = output;
250 j["Force_BC"] = bc["Force_BC"];
251 j["Displacement_BC"] = bc["Displacement_BC"];
252 j["IC"] = bc["IC"];
253 j["Particle"] = pDeck["Particle"];
254 j["Mesh"] = pDeck["Mesh"];
255 j["Material"] = pDeck["Material"];
256 j["Contact"] = pDeck["Contact"];
257 j["Neighbor"] = pDeck["Neighbor"];
258 j["Particle_Generation"] = pDeck["Particle_Generation"];
259 return j;
260}
261
262} // namespace
263
264int main(int argc, char *argv[]) {
265 util::parallel::initMpi(argc, argv);
266 util::io::InputParser input(argc, argv);
267
268 unsigned int n_threads = std::thread::hardware_concurrency();
269 if (input.cmdOptionExists("-nThreads"))
270 n_threads = std::stoul(input.getCmdOption("-nThreads"));
272 util::io::print(std::format("Number of threads = {}\n", util::parallel::getNThreads()));
273
274 double final_time = 0.0030;
275 size_t num_steps = 30000;
276 if (input.cmdOptionExists("-finalTime"))
277 final_time = std::stod(input.getCmdOption("-finalTime"));
278 if (input.cmdOptionExists("-numSteps"))
279 num_steps = std::stoul(input.getCmdOption("-numSteps"));
280
281 const double mesh_size = 0.00010;
282 const double horizon = 3.0 * mesh_size;
283
284 namespace fs = std::filesystem;
285 const fs::path cwd = fs::current_path();
286 fs::path out_dir = cwd / "out";
287 fs::path inp_dir = cwd / "inp";
288 if (input.cmdOptionExists("-outputDir")) {
289 fs::path p = input.getCmdOption("-outputDir");
290 out_dir = p.is_absolute() ? std::move(p) : cwd / p;
291 }
292 if (input.cmdOptionExists("-inputDir")) {
293 fs::path p = input.getCmdOption("-inputDir");
294 inp_dir = p.is_absolute() ? std::move(p) : cwd / p;
295 } else if (input.cmdOptionExists("-outputDir")) {
296 inp_dir = out_dir.parent_path() / "inp";
297 }
298 fs::create_directories(out_dir);
299 fs::create_directories(inp_dir);
300
301 const std::string output_path = directoryPathWithTrailingSep(out_dir);
302 const fs::path mesh_tri = inp_dir / "mesh_triangle.msh";
303 const fs::path mesh_ell = inp_dir / "mesh_hollow_ellipse.msh";
304
305 auto j = buildInputJson(output_path, mesh_tri, mesh_ell, final_time, num_steps, mesh_size,
306 horizon);
307 {
308 std::ofstream ofs(inp_dir / "input.json");
309 ofs << std::setw(2) << j << std::endl;
310 }
311
312 // -deckOnly stops after the deck is written. The Python comparison reads
313 // the deck and does not need this driver to run the simulation.
314 if (input.cmdOptionExists("-deckOnly")) {
315 util::io::print("deck written; -deckOnly, not running\n");
317 return 0;
318 }
319
320 // Hard check: deck must request a hollow ellipse, not a rectangle plate.
321 if (j["Particle"]["Set_2"]["Type"] != "ellipse_minus_ellipse")
322 throw std::runtime_error("ellipse_triangle: Particle Set_2 must be ellipse_minus_ellipse");
323
324 util::io::print(std::format(
325 "[ellipse_triangle] Type={}, T={}, Nt={}, mesh={:.4e}, out={}\n",
326 j["Particle"]["Set_2"]["Type"].get<std::string>(), final_time, num_steps, mesh_size,
327 out_dir.string()));
328
329 auto deck = std::make_shared<inp::Input>(j);
330 PeriDEMModel dem(deck);
331 dem.run(deck);
332
333 return 0;
334}
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
std::string directoryPathWithTrailingSep(const std::filesystem::path &dir)
Definition main.cpp:34
json buildInputJson(const std::string &output_path, const std::filesystem::path &mesh_tri, const std::filesystem::path &mesh_ell, double final_time, size_t num_steps, double mesh_size, double horizon)
Definition main.cpp:43
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 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.
void initMpi(int argc=0, char *argv[]=nullptr)
Initializes MPI and also creates MpiStatus struct.
void finalizeMpi()
Call MPI_Finalize if this process initialized MPI.
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
double harmonicMean(const double &m1, const double &m2)
Definition function.cpp:135
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 example JSON object for ModelDeck configuration.
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 getParticleContactExampleJson(size_t nSets=0)
Returns example JSON object for ModelDeck configuration.
static json getParticleGeomExampleJson(std::vector< geom::GeomData > pGeomVec=std::vector< geom::GeomData >())
Returns example JSON object for ModelDeck configuration.
static json getParticleMaterialExampleJson(size_t nSets=0)
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