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 * Distributed under the Boost Software License, Version 1.0. (See accompanying
8 * file LICENSE)
9 *
10 * Single-particle Mode-I open then close across a through precrack.
11 * Compares Model.Self_Contact broken_bond_kn vs reference_gap under the same
12 * prescribed kinematics (r0 ≠ Rc for bonds that crossed the crack).
13 */
14
15#include "inp/deckIncludes.h"
17#include "periDEMModel.h"
18#include "util/io.h"
19#include "util/parallelUtil.h"
20#include "util/point.h"
21
22#include <cmath>
23#include <cstdlib>
24#include <filesystem>
25#include <format>
26#include <fstream>
27#include <memory>
28#include <stdexcept>
29#include <string>
30#include <thread>
31#include <vector>
32
33namespace {
34
35std::string directoryPathWithTrailingSep(const std::filesystem::path &dir) {
36 namespace fs = std::filesystem;
37 fs::path n = fs::absolute(dir).lexically_normal();
38 std::string s = n.string();
39 if (!s.empty() && s.back() != '/' && s.back() != '\\')
40 s += fs::path::preferred_separator;
41 return s;
42}
43
44json buildInputJson(const std::string &output_path, const std::filesystem::path &mesh_file,
45 double L, double mesh_size, double horizon, double final_time,
46 size_t num_steps, const std::string &self_contact) {
47 auto model = inp::ModelDeck::getExampleJson(2, final_time, num_steps, "finite_difference",
48 "central_difference", true, 2, "Single_Particle",
49 0);
50 model["Self_Contact"] = self_contact;
51 model["Bond_Break"] = "tension";
52
53 // Soft PD; large Gc so only the precrack is fractured.
54 const double rho = 1200.0;
55 const double K = 2.16e5;
56 const double G = 1.296e5;
57 const double Gc = 5.0e6;
58
60 "vtu", output_path,
61 std::vector<std::string>({"Displacement", "Force", "Damage_Z", "Particle_ID"}),
62 num_steps, 1, false, "zlib", false, num_steps, "", false);
63
64 const double strip = 0.2 * L;
65 json bc = json::object();
66 bc["Displacement_BC"] = {
67 {"Sets", 2},
68 {"Set_1",
69 {{"Region",
70 {{"Geometry",
71 {{"Type", "rectangle"},
72 {"Parameters", std::vector<double>{-0.05 * L, -0.05 * L, 0., strip, L + 0.05 * L, 0.}}}}}},
73 {"Direction", std::vector<size_t>{1, 2}},
74 {"Time_Function", {{"Type", "constant"}, {"Parameters", std::vector<double>{0.}}}},
75 {"Spatial_Function", {{"Type", "constant"}}},
76 {"Zero_Displacement", true}}},
77 {"Set_2",
78 {{"Region",
79 {{"Geometry",
80 {{"Type", "rectangle"},
81 {"Parameters",
82 std::vector<double>{L - strip, -0.05 * L, 0., L + 0.05 * L, L + 0.05 * L, 0.}}}}}},
83 {"Direction", std::vector<size_t>{1}},
84 {"Time_Function", {{"Type", "constant"}, {"Parameters", std::vector<double>{0.}}}},
85 {"Spatial_Function", {{"Type", "constant"}}},
86 {"Zero_Displacement", false}}}};
87
88 geom::GeomData rect;
89 rect.d_geomName = "rectangle";
90 rect.d_geomParams = {0., 0., 0., L, L, 0.};
92
93 json mesh_set = {{"File", mesh_file.string()},
94 {"CreateMesh",
95 {{"Flag", true},
96 {"Info", "gmsh_builtin_mesh"},
97 {"Mesh_Size", mesh_size},
98 {"Write_Mesh_File", true}}}};
99 json mesh = {{"Set_1", mesh_set}};
100
102 material["Set_1"] =
103 inp::MaterialDeck::getExampleJson("PDState", false, horizon, 0, rho, K, G, Gc, true, 1);
104
105 json root = {{"Model", model},
106 {"Output", output},
107 {"Particle", particle},
108 {"Mesh", mesh},
109 {"Material", material}};
110 root.merge_patch(bc);
111 return root;
112}
113
114size_t applyPrecrack(PeriDEMModel &dem, double x_crack) {
115 size_t n_broken = 0;
116 for (size_t i = 0; i < dem.d_neighPd.size(); ++i) {
117 const auto &xi = dem.d_xRef[i];
118 for (size_t k = 0; k < dem.d_neighPd[i].size(); ++k) {
119 const size_t j = dem.d_neighPd[i][k];
120 const auto &xj = dem.d_xRef[j];
121 if ((xi.d_x - x_crack) * (xj.d_x - x_crack) < 0.) {
122 dem.d_fracture_p->setBondState(i, k, true);
123 ++n_broken;
124 }
125 }
126 }
127 return n_broken;
128}
129
130void applyRigidOpenClose(PeriDEMModel &dem, double x_crack, double u_right) {
131 for (size_t i = 0; i < dem.d_xRef.size(); ++i) {
132 const double ux = (dem.d_xRef[i].d_x >= x_crack) ? u_right : 0.;
133 dem.d_u[i] = util::Point(ux, 0., 0.);
134 dem.d_v[i] = util::Point();
135 dem.d_x[i] = dem.d_xRef[i] + dem.d_u[i];
136 }
137}
138
140 double min_gap = 1.0e300;
141 double max_gap = 0.;
142 size_t n_pairs = 0;
143 double force_x_left = 0.;
144 double force_x_right = 0.;
145};
146
147CrackMetrics measure(PeriDEMModel &dem, double x_crack, double band) {
148 CrackMetrics m;
149 for (size_t i = 0; i < dem.d_neighPd.size(); ++i) {
150 const auto &xi = dem.d_xRef[i];
151 for (size_t k = 0; k < dem.d_neighPd[i].size(); ++k) {
152 if (!dem.d_fracture_p->getBondState(i, k))
153 continue;
154 const size_t j = dem.d_neighPd[i][k];
155 const auto &xj = dem.d_xRef[j];
156 if ((xi.d_x - x_crack) * (xj.d_x - x_crack) >= 0.)
157 continue;
158 const double gap = (dem.d_x[j] - dem.d_x[i]).length();
159 m.min_gap = std::min(m.min_gap, gap);
160 m.max_gap = std::max(m.max_gap, gap);
161 ++m.n_pairs;
162 }
163 }
164 if (m.n_pairs == 0)
165 m.min_gap = 0.;
166
167 for (size_t i = 0; i < dem.d_xRef.size(); ++i) {
168 const double x = dem.d_xRef[i].d_x;
169 const double fx = dem.d_f[i].d_x * dem.d_vol[i];
170 if (x < x_crack && x > x_crack - band)
171 m.force_x_left += fx;
172 if (x >= x_crack && x < x_crack + band)
173 m.force_x_right += fx;
174 }
175 return m;
176}
177
178struct LawResult {
179 double open_min_gap = 0.;
180 double close_min_gap = 0.;
181 double close_force_amp = 0.;
182};
183
184LawResult runLaw(const std::string &self_contact, const std::filesystem::path &run_dir, double L,
185 double mesh_size, double horizon, unsigned n_threads) {
186 namespace fs = std::filesystem;
187 const fs::path out_dir = run_dir / "out";
188 const fs::path inp_dir = run_dir / "inp";
189 fs::create_directories(out_dir);
190 fs::create_directories(inp_dir);
191
192 const fs::path mesh = inp_dir / "mesh_rect.msh";
193 // Time fields unused for prescribed kinematics; keep Output_Interval valid.
194 auto input_json =
195 buildInputJson(directoryPathWithTrailingSep(out_dir), mesh, L, mesh_size, horizon, 1.0e-6,
196 2, self_contact);
197 {
198 std::ofstream os(inp_dir / "input.json");
199 os << input_json.dump(2);
200 }
201
202 auto deck = std::make_shared<inp::Input>(input_json);
203 PeriDEMModel dem(deck);
204 dem.init();
205
206 const double x_crack = 0.5 * L;
207 const size_t n_broken = applyPrecrack(dem, x_crack);
208 if (n_broken == 0)
209 throw std::runtime_error(self_contact + ": precrack broke no bonds");
210
211 const double Rc = dem.d_maxContactR > 0. ? dem.d_maxContactR : 0.95 * mesh_size;
212 const double h = dem.d_hMax > 0. ? dem.d_hMax : mesh_size;
213 util::io::print(std::format("{}: nodes={}, broken_bonds={}, h={}, Rc={}, horizon={}\n",
214 self_contact, dem.d_x.size(), n_broken, h, Rc, horizon));
215
216 // Open: separate faces so current gap >> Rc (no self-contact yet).
217 const double u_open = 2.0 * Rc;
218 applyRigidOpenClose(dem, x_crack, u_open);
219 dem.computeForces();
220 const auto open_m = measure(dem, x_crack, 2.0 * horizon);
221 if (!(open_m.min_gap > 1.5 * Rc))
222 throw std::runtime_error(std::format("{}: open did not separate (min_gap={})", self_contact,
223 open_m.min_gap));
224
225 // Close into the window where reference_gap (natural R = r0) and
226 // broken_bond_kn (natural R = Rc) disagree: Rc < R < typical r0.
227 // Mid-horizon neighbor distances are ~O(horizon) >> Rc = 0.95 h.
228 const double u_close = -0.5 * (Rc + 0.5 * horizon);
229 applyRigidOpenClose(dem, x_crack, u_close);
230 dem.computeForces();
231 const auto close_m = measure(dem, x_crack, 2.0 * horizon);
232 const double force_amp =
233 0.5 * (std::abs(close_m.force_x_left) + std::abs(close_m.force_x_right));
234
235 util::io::print(std::format("{}: open min_gap={}, close min_gap={}, force_amp={}\n", self_contact,
236 open_m.min_gap, close_m.min_gap, force_amp));
237
238 dem.close();
239
240 LawResult r;
241 r.open_min_gap = open_m.min_gap;
242 r.close_min_gap = close_m.min_gap;
243 r.close_force_amp = force_amp;
244 return r;
245}
246
247} // namespace
248
249int main(int argc, char *argv[]) {
250 util::parallel::initMpi(argc, argv);
251 util::io::InputParser input(argc, argv);
252
253 unsigned int n_threads = 2;
254 if (input.cmdOptionExists("-nThreads"))
255 n_threads = static_cast<unsigned>(std::stoi(input.getCmdOption("-nThreads")));
257
258 namespace fs = std::filesystem;
259 fs::path base = fs::current_path() / "self_contact_mode1_run";
260 if (input.cmdOptionExists("-outputDir"))
261 base = input.getCmdOption("-outputDir");
262
263 const double L = 0.002;
264 const double mesh_size = L / 6.0;
265 const double horizon = 3.0 * mesh_size;
266
267 LawResult bb;
268 LawResult rg;
269 try {
270 bb = runLaw("broken_bond_kn", base / "broken_bond_kn", L, mesh_size, horizon, n_threads);
271 rg = runLaw("reference_gap", base / "reference_gap", L, mesh_size, horizon, n_threads);
272 } catch (const std::exception &e) {
273 util::io::print(std::format("self-contact Mode-I failed: {}\n", e.what()));
274 return 1;
275 }
276
277 if (!(bb.close_force_amp > 0.) || !(rg.close_force_amp > 0.)) {
278 util::io::print(std::format(
279 "close did not activate self-contact: F_bb={}, F_rg={}\n", bb.close_force_amp,
280 rg.close_force_amp));
281 return 1;
282 }
283
284 const double fmax = std::max(bb.close_force_amp, rg.close_force_amp);
285 const double rel = std::abs(bb.close_force_amp - rg.close_force_amp) / fmax;
286 if (!(rel > 0.05)) {
287 util::io::print(std::format(
288 "self-contact laws did not differ enough under close: F_bb={}, F_rg={}, rel={}\n",
289 bb.close_force_amp, rg.close_force_amp, rel));
290 return 1;
291 }
292
293 util::io::print(std::format(
294 "self-contact Mode-I OK: open gaps bb={}/rg={}, close forces bb={}/rg={}, rel_diff={}\n", bb.open_min_gap,
295 rg.open_min_gap, bb.close_force_amp, rg.close_force_amp, rel));
296 return 0;
297}
std::vector< util::Point > d_x
Current positions of the nodes.
Definition modelData.h:745
std::vector< util::Point > d_xRef
reference positions of the nodes
Definition modelData.h:742
std::vector< util::Point > d_u
Displacement of the nodes.
Definition modelData.h:748
std::vector< util::Point > d_f
Total force on the nodes.
Definition modelData.h:757
std::unique_ptr< geometry::Fracture > d_fracture_p
Fracture state of bonds.
Definition modelData.h:736
std::vector< std::vector< size_t > > d_neighPd
Neighbor data for peridynamic forces.
Definition modelData.h:770
double d_maxContactR
Maximum contact radius between over pairs of particles and walls.
Definition modelData.h:649
double d_hMax
Minimum mesh over all particles and walls.
Definition modelData.h:643
std::vector< double > d_vol
Nodal volumes.
Definition modelData.h:760
std::vector< util::Point > d_v
Velocity of the nodes.
Definition modelData.h:751
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
LawResult runLaw(const std::string &self_contact, const std::filesystem::path &run_dir, double L, double mesh_size, double horizon, unsigned n_threads)
Definition main.cpp:184
std::string directoryPathWithTrailingSep(const std::filesystem::path &dir)
Definition main.cpp:34
json buildInputJson(const std::string &output_path, const std::filesystem::path &mesh_file, double L, double mesh_size, double horizon, double final_time, size_t num_steps, const std::string &self_contact)
Definition main.cpp:44
CrackMetrics measure(PeriDEMModel &dem, double x_crack, double band)
Definition main.cpp:147
void applyRigidOpenClose(PeriDEMModel &dem, double x_crack, double u_right)
Definition main.cpp:130
size_t applyPrecrack(PeriDEMModel &dem, double x_crack)
Definition main.cpp:114
Collection of methods and data related to finite element and mesh.
Definition mesh.cpp:28
Collection of methods and data related to particle object.
Definition modelData.h:36
void print(const T &msg, int nt=print_default_tab, int printMpiRank=print_default_mpi_rank)
Prints formatted information.
Definition io.h:128
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.
Input data for geometrical objects.
std::vector< double > d_geomParams
Zone parameters.
std::string d_geomName
Zone type.
static json getExampleJson(std::string materialType="PDState", bool isPlainStrain=false, double horizon=-1., double horizonMeshRatio=-1., double density=1., double K=0., double G=0., double Gc=0., bool computeParamsFromElastic=true, size_t influenceFnType=0, double E=-1.)
Returns example JSON object for ModelDeck configuration.
static json getExampleJson(size_t dim=2, double tFinal=1.0, size_t Nt=10, std::string spatialDiscretization="finite_difference", std::string timeDiscretization="central_difference", bool populateElementNodeConnectivity=true, size_t quadOrder=2, std::string particleSimType="Multi_Particle", int seed=0)
Returns example JSON object for ModelDeck configuration.
Definition modelDeck.h:164
static json getExampleJson(std::string outFormat="vtu", std::string path="./", std::vector< std::string > outTags={"Displacement"}, size_t outputInterval=1, size_t debug=2, bool performFEOut=true, std::string compressType="zlib", bool performOut=true, size_t dtTestOut=1, std::string tagPPFile="", bool pvdCollection=false)
Returns example JSON object for ModelDeck configuration.
Definition outputDeck.h:144
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
int main()
Definition main.cpp:31