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 * Checks that a peridynamic material reproduces the elastic constants it was
11 * calibrated from. One force evaluation per field on a uniform grid:
12 * - homogeneous strain: interior strain energy density = mu e:e + lambda/2 (tr e)^2
13 * - u_x = a x^2 / 2: interior force density f_x = (lambda + 2 mu) a
14 * - u_y = a x^2 / 2: interior force density f_y = mu a
15 * lambda is the plane-stress value in 2D plane stress. Exits 1 if any
16 * relative error exceeds -tol.
17 */
18#include "inp/deckIncludes.h"
19#include "periDEMModel.h"
20#include "util/io.h"
21#include "util/parallelUtil.h"
22
23#include <cmath>
24#include <filesystem>
25#include <format>
26#include <functional>
27#include <memory>
28#include <string>
29
30namespace {
31
32json makeDeck(const std::string &model, size_t dim, bool plane_strain,
33 int influence, double L, double h, double horizon, double E,
34 double nu, double Gc, const std::string &out) {
35 const double rho = 1000.;
36 json j;
38 json{{"Dimension", dim}, {"Final_Time", 1.0e-6}, {"Time_Steps", 1},
39 {"Particle_Sim_Type", "Single_Particle"}});
41 json{{"Path", out}, {"Perform_Out", false}, {"Debug", 0}});
43 particle["Sets"] = 1;
44 particle["Set_1"] =
45 json{{"Type", dim == 2 ? "rectangle" : "cuboid"},
46 {"Parameters", {-L / 2, -L / 2, dim == 2 ? 0. : -L / 2, L / 2, L / 2,
47 dim == 2 ? 0. : L / 2}}};
48 j["Particle"] = particle;
49 j["Mesh"] = json{{"Sets", 1},
50 {"Set_1", json{{"File", out + "mesh.vtu"},
51 {"CreateMesh", json{{"Flag", true},
52 {"Info", "uniform"},
53 {"Mesh_Size", h}}}}}};
55 json m{{"Type", model}, {"Is_Plane_Strain", plane_strain},
56 {"Horizon", horizon}, {"Density", rho}, {"E", E}, {"Gc", Gc},
57 {"Compute_From_Classical", true},
58 {"Influence_Function", json{{"Type", influence}}}};
59 if (model == "PDState")
60 m["G"] = E / (2. * (1. + nu));
61 mat["Set_1"] = inp::MaterialDeck::getExampleJson(m);
62 j["Material"] = mat;
65 json{{"Contact_Radius", 0.9 * h}, {"Kn", 1.0}, {"Damping_On", false},
66 {"Friction_On", false}, {"Friction_Coeff", 0.}, {"K", E}});
67 j["Contact"] = contact;
68 j["Particle_Generation"] = json{
69 {"Method", "From_File"},
70 {"Data", json{{"N", 1},
71 {"0", json{{"x", 0.}, {"y", 0.}, {"z", 0.}, {"theta", 0.},
72 {"s", 1.}, {"geom_id", 0}, {"mat_id", 0},
73 {"contact_id", 0}}}}}};
74 return j;
75}
76
77} // namespace
78
79int main(int argc, char *argv[]) {
80 util::parallel::initMpi(argc, argv);
81 util::io::InputParser input(argc, argv);
83 input.cmdOptionExists("-nThreads")
84 ? std::stoi(input.getCmdOption("-nThreads"))
85 : 4);
86 auto opt = [&input](const char *f, const std::string &fb) {
87 return input.cmdOptionExists(f) ? input.getCmdOption(f) : fb;
88 };
89
90 const std::string model = opt("-model", "PDState");
91 const size_t dim = std::stoul(opt("-dim", "2"));
92 const bool plane_strain = std::stoi(opt("-planeStrain", "0")) != 0;
93 const int influence = std::stoi(opt("-influence", "0"));
94 const double m_ratio = std::stod(opt("-m", "4"));
95 const double h = std::stod(opt("-h", dim == 2 ? "0.02" : "0.05"));
96 const double tol = std::stod(opt("-tol", "0.02"));
97 const double L = std::stod(opt("-L", "1.0")), horizon = m_ratio * h;
98 const double E = 1.0e5, Gc = 10.;
99 // bond-based models fix nu by the plane mode; PDState takes the table nu
100 double nu = std::stod(opt("-nu", "0.3"));
101 if (model != "PDState")
102 nu = (dim == 2 && !plane_strain) ? 1. / 3. : 0.25;
103
104 const double mu = E / (2. * (1. + nu));
105 const double lambda = (dim == 2 && !plane_strain)
106 ? E * nu / (1. - nu * nu)
107 : E * nu / ((1. + nu) * (1. - 2. * nu));
108
109 namespace fs = std::filesystem;
110 struct Check { std::string name; double got, want; };
111
112 // build the model, apply each field, and compare with linear elasticity
113 auto runChecks = [&](const std::string &mname) {
114 const fs::path out = fs::current_path() /
115 std::format("calib_{}_{}d_{}_J{}", mname, dim,
116 plane_strain ? "pstrain" : "pstress", influence);
117 fs::create_directories(out);
118 auto deck = std::make_shared<inp::Input>(
119 makeDeck(mname, dim, plane_strain, influence, L, h, horizon, E, nu, Gc,
120 out.string() + "/"));
121 PeriDEMModel dem(deck);
122 dem.init();
123
124 // energy at x needs a full horizon; the state-based force at x also uses
125 // the dilatation of neighbors, so it needs two
126 auto inside = [&](const util::Point &x, double margin) {
127 const double c = 0.5 * L - margin - 1.5 * h;
128 return std::abs(x.d_x) < c && std::abs(x.d_y) < c &&
129 (dim == 2 || std::abs(x.d_z) < c);
130 };
131
132 // apply a displacement field, compute forces, and return interior
133 // averages of energy density, f_x and f_y
134 auto evaluate =
135 [&](const std::function<util::Point(const util::Point &)> &u) {
136 for (size_t i = 0; i < dem.d_xRef.size(); i++) {
137 dem.d_u[i] = u(dem.d_xRef[i]);
138 dem.d_x[i] = dem.d_xRef[i] + dem.d_u[i];
139 }
140 dem.computeForces();
141 double e = 0., fx = 0., fy = 0.;
142 size_t ne = 0, nf = 0;
143 for (size_t i = 0; i < dem.d_xRef.size(); i++) {
144 if (inside(dem.d_xRef[i], horizon)) {
145 e += dem.d_e[i];
146 ne++;
147 }
148 if (inside(dem.d_xRef[i], 2. * horizon)) {
149 fx += dem.d_f[i].d_x;
150 fy += dem.d_f[i].d_y;
151 nf++;
152 }
153 }
154 return std::array<double, 3>{e / ne, fx / nf, fy / nf};
155 };
156
157 const double eps = 1.0e-5, a = 1.0e-5 / L;
158 std::vector<Check> checks;
159
160 // homogeneous strains: uniaxial, equibiaxial, shear
161 auto r = evaluate([&](const util::Point &x) {
162 return util::Point(eps * x.d_x, 0., 0.); });
163 checks.push_back({"energy uniaxial strain", r[0],
164 0.5 * (lambda + 2. * mu) * eps * eps});
165 r = evaluate([&](const util::Point &x) {
166 return util::Point(eps * x.d_x, eps * x.d_y, dim == 3 ? eps * x.d_z : 0.); });
167 checks.push_back({"energy equibiaxial", r[0],
168 (mu * dim + 0.5 * lambda * dim * dim) * eps * eps});
169 r = evaluate([&](const util::Point &x) {
170 return util::Point(eps * x.d_y, eps * x.d_x, 0.); });
171 checks.push_back({"energy shear", r[0], 2. * mu * eps * eps});
172
173 // quadratic fields: divergence of stress
174 r = evaluate([&](const util::Point &x) {
175 return util::Point(0.5 * a * x.d_x * x.d_x, 0., 0.); });
176 checks.push_back({"force (lambda+2mu)", r[1], (lambda + 2. * mu) * a});
177 r = evaluate([&](const util::Point &x) {
178 return util::Point(0., 0.5 * a * x.d_x * x.d_x, 0.); });
179 checks.push_back({"force mu", r[2], mu * a});
180
181 util::io::print(std::format(
182 "CALIB model={} dim={} {} J={} delta/h={} nodes={} E={} nu={:.4f}\n",
183 mname, dim, plane_strain ? "plane_strain" : "plane_stress", influence,
184 m_ratio, dem.d_xRef.size(), E, nu));
185 return checks;
186 };
187
188 // -compareModel: a second bond-based model must give the same small-strain
189 // response (e.g. RNP against PMB), which removes the quadrature error
190 std::vector<Check> checks;
191 try {
192 checks = runChecks(model);
193 if (input.cmdOptionExists("-compareModel")) {
194 const auto ref = runChecks(input.getCmdOption("-compareModel"));
195 for (size_t k = 0; k < checks.size(); k++)
196 checks[k].want = ref[k].got;
197 }
198 } catch (const std::exception &e) {
199 // input checks are tested by matching this message
200 util::io::print(std::format("CALIB_ERROR {}\n", e.what()));
202 return 1;
203 }
204
205 bool ok = true;
206 for (const auto &c : checks) {
207 const double rel = (c.got - c.want) / c.want;
208 const bool pass = std::abs(rel) <= tol;
209 ok = ok && pass;
210 util::io::print(std::format(" {:<24} got={:+.15e} want={:+.6e} rel={:+.4f} {}\n",
211 c.name, c.got, c.want, rel, pass ? "OK" : "FAIL"));
212 }
213 util::io::print(ok ? "CALIB_PASS\n" : "CALIB_FAIL\n");
215 return ok ? 0 : 1;
216}
int main(int argc, char *argv[])
Definition main.cpp:29
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< float > d_e
Energy of the nodes.
Definition modelData.h:828
std::vector< util::Point > d_f
Total force on the nodes.
Definition modelData.h:757
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 makeDeck(const std::string &model, size_t dim, bool plane_strain, int influence, double L, double h, double horizon, double E, double nu, double Gc, const std::string &out)
Definition main.cpp:32
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:171
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.
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 getParticleContactExampleJson(size_t nSets=0)
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
double d_z
the z coordinate
Definition point.h:39
double d_x
the x coordinate
Definition point.h:33