PeriDEM 0.3.0
PeriDEM -- Peridynamics-based high-fidelity model for granular media
Loading...
Searching...
No Matches
pdForce.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
11#include "pdForce.h"
12#include "selfContact.h"
13
14#include <memory>
15#include <string>
16#include "pdMpi.h"
17
18#include "data/modelData.h"
19#include "util/io.h"
21#include "util/function.h"
22#include "util/point.h"
23#include "util/parallelUtil.h"
24
25#include <taskflow/taskflow/taskflow.hpp>
26#include <taskflow/taskflow/algorithm/for_each.hpp>
27
29
30
31 util::io::log(3, " Computing peridynamic force \n");
32
33 const auto dim = data.d_modelDeck_p->d_dim;
34 const bool is_state = data.d_particlesListTypeAll[0]->getMaterial()->isStateActive();
35 const auto selfContact =
36 pd::makeSelfContact(data.d_modelDeck_p->d_selfContact);
37 const bool abs_stretch_break =
38 (data.d_modelDeck_p->d_bondBreak == "absolute_stretch");
39
40 // compute state-based helper quantities
41 if (is_state) {
42
43 tf::Executor executor(util::parallel::getNThreads());
44 tf::Taskflow taskflow;
45
46 taskflow.for_each_index(
47 (std::size_t) 0, data.d_fPdCompNodes.size(), (std::size_t) 1,
48 [&data, abs_stretch_break, dim](std::size_t II) {
49 auto i = data.d_fPdCompNodes[II];
50
51 const auto rho = data.getDensity(i);
52 const auto &fix = data.d_fix[i];
53 const auto &ptId = data.getPtId(i);
54 auto &pi = data.getParticleFromAllList(ptId);
55
56 if (pi->d_material_p->isStateActive()) {
57
58 const double horizon = pi->getHorizon();
59 const double mesh_size = pi->getMeshSize();
60 const auto &xi = data.d_xRef[i];
61 const auto &ui = data.d_u[i];
62
63 // update bond state and compute thetax
64 const auto &m = data.d_mX[i];
65 double theta = 0.;
66
67 // upper and lower bound for volume correction
68 auto check_up = horizon + 0.5 * mesh_size;
69 auto check_low = horizon - 0.5 * mesh_size;
70
71 size_t k = 0;
72 for (size_t j : data.d_neighPd[i]) {
73
74 const auto &xj = data.d_xRef[j];
75 const auto &uj = data.d_u[j];
76 double rji = (xj - xi).length();
77 if (!(rji > 0.)) {
78 k += 1;
79 continue;
80 }
81 // double rji = std::sqrt(data.d_neighPdSqdDist[i][k]);
82 double change_length = (xj - xi + uj - ui).length() - rji;
83
84 // step 1: update the bond state
85 double s = change_length / rji;
86 double sc = pi->d_material_p->getSc(rji);
87
88 // get fracture state, modify, and set
89 auto fs = data.d_fracture_p->getBondState(i, k);
90 // Model.Bond_Break: tension (s > sc) or absolute_stretch (|s| > sc).
91 if (!fs) {
92 const bool broke = abs_stretch_break
93 ? util::isGreater(std::abs(s), sc + 1.0e-10)
94 : util::isGreater(s, sc + 1.0e-10);
95 if (broke)
96 fs = true;
97 }
98 data.d_fracture_p->setBondState(i, k, fs);
99
100 if (!fs) {
101
102 // get corrected volume of node j
103 auto volj = data.d_vol[j];
104
105 if (util::isGreater(rji, check_low))
106 volj *= (check_up - rji) / mesh_size;
107
108 theta += rji * change_length * pi->d_material_p->getInfFn(rji) *
109 volj;
110 } // if bond is not broken
111
112 k += 1;
113 } // loop over neighbors
114
115 // dilatation: 3/m in 3D, 2/m in 2D (Yang et al. 2024, eqs. 8-9)
116 data.d_thetaX[i] = double(dim) * theta / m;
117 } // if it is state-based
118 } // loop over nodes
119 ); // for_each
120
121 executor.run(taskflow).get();
122
123 // Ghost nodes need owner dilatations before the force pass.
125 }
126
127 // strain energy density per node
128 if (data.d_e.size() != data.d_x.size())
129 data.d_e.resize(data.d_x.size(), 0.f);
130
131 // compute the internal forces
132 tf::Executor executor(util::parallel::getNThreads());
133 tf::Taskflow taskflow;
134
135 taskflow.for_each_index(
136 (std::size_t) 0, data.d_fPdCompNodes.size(), (std::size_t) 1,
137 [&data, selfContact = selfContact.get(), abs_stretch_break](std::size_t II) {
138 auto i = data.d_fPdCompNodes[II];
139
140 // local variable to hold force
141 util::Point force_i = util::Point();
142 double scalar_f = 0.;
143 double energy_i = 0.;
144
145 // for damage
146 float Zi = 0.;
147 // Silling (2000) damage: phi = 1 - ∫ mu dV' / ∫ dV' over the horizon
148 // (reported by Silling 2003 and Trask). Bhattacharya & Lipton instead
149 // define damage as the broken-bond count fraction, kept separately.
150 double vol_all = 0.;
151 double vol_intact = 0.;
152 size_t n_bonds = 0;
153 size_t n_broken = 0;
154
155 const auto rhoi = data.getDensity(i);
156 const auto &ptIdi = data.getPtId(i);
157 auto &pi = data.getParticleFromAllList(ptIdi);
158
159 const double horizon = pi->getHorizon();
160 const double mesh_size = pi->getMeshSize();
161 const auto &xi = data.d_xRef[i];
162 const auto &ui = data.d_u[i];
163 const auto &mi = data.d_mX[i];
164 const auto &thetai = data.d_thetaX[i];
165
166 // upper and lower bound for volume correction
167 auto check_up = horizon + 0.5 * mesh_size;
168 auto check_low = horizon - 0.5 * mesh_size;
169
170 // loop over neighbors
171 {
172 size_t k = 0;
173 for (size_t j : data.d_neighPd[i]) {
174 auto fs = data.d_fracture_p->getBondState(i, k);
175 const auto &xj = data.d_xRef[j];
176 const auto &uj = data.d_u[j];
177 auto volj = data.d_vol[j];
178 double rji = (xj - xi).length();
179 if (!(rji > 0.)) {
180 k++;
181 continue;
182 }
183 double Sji = pi->d_material_p->getS(xj - xi, uj - ui);
184
185 if (!fs) {
186 const auto &mj = data.d_mX[j];
187 const auto &thetaj = data.d_thetaX[j];
188
189 // get corrected volume of node j
190 if (util::isGreater(rji, check_low))
191 volj *= (check_up - rji) / mesh_size;
192
193 // handle two cases differently
194 if (pi->d_material_p->isStateActive()) {
195
196 auto ef_i =
197 pi->d_material_p->getBondEF(rji, Sji, fs, mi, thetai);
198 auto ef_j =
199 pi->d_material_p->getBondEF(rji, Sji, fs, mj, thetaj);
200
201 // compute the contribution of bond force to force at i
202 scalar_f = (ef_i.second + ef_j.second) * volj;
203 energy_i += ef_i.first * volj;
204
205 force_i += scalar_f * pi->d_material_p->getBondForceDirection(
206 xj - xi, uj - ui);
207 } // if state-based
208 else {
209
210 // Bond-based materials (e.g. PMB) historically broke inside
211 // getBondEF using |s|>Sc. Honor Model.Bond_Break here instead:
212 // tension (default, literature PMB) vs absolute_stretch.
213 const double Sc = pi->d_material_p->getBreakSc(rji);
214 const bool broke = abs_stretch_break
215 ? util::isGreater(std::abs(Sji), Sc + 1.0e-10)
216 : util::isGreater(Sji, Sc + 1.0e-10);
217 if (broke)
218 fs = true;
219 data.d_fracture_p->setBondState(i, k, fs);
220
221 if (!fs) {
222 auto ef =
223 pi->d_material_p->getBondEF(rji, Sji, fs, /*break_bonds=*/false);
224 scalar_f = ef.second * volj;
225 energy_i += ef.first * volj;
226 force_i += scalar_f * pi->d_material_p->getBondForceDirection(
227 xj - xi, uj - ui);
228 } else {
229 const auto yji = xj + uj - (xi + ui);
230 force_i +=
231 selfContact->force(yji, volj, pi->d_Kn, pi->d_Rc, rji);
232 }
233 } // if bond-based
234 } // if bond not broken
235 else {
236 const auto yji = xj + uj - (xi + ui);
237 force_i +=
238 selfContact->force(yji, volj, pi->d_Kn, pi->d_Rc, rji);
239 } // if bond is broken
240
241 // calculate damage
242 auto Sc = pi->d_material_p->getSc(rji);
243 if (util::isGreater(std::abs(Sji / Sc), Zi))
244 Zi = std::abs(Sji / Sc);
245
246 // damage function phi (bond-state weighted by neighbor volume)
247 vol_all += data.d_vol[j];
248 n_bonds++;
249 if (!data.d_fracture_p->getBondState(i, k))
250 vol_intact += data.d_vol[j];
251 else
252 n_broken++;
253
254 k++;
255 } // loop over neighbors
256
257 } // peridynamic force
258
259 // update force (we remove any force from
260 // previous steps and add peridynamics force)
261 data.d_f[i] = force_i;
262 if (pi->d_material_p->isStateActive())
263 energy_i += pi->d_material_p->getDilatationEnergyDensity(thetai);
264 data.d_e[i] = static_cast<float>(energy_i);
265
266 data.d_Z[i] = Zi;
267 if (!data.d_phi.empty())
268 data.d_phi[i] =
269 (vol_all > 0.) ? static_cast<float>(1. - vol_intact / vol_all) : 0.f;
270 if (!data.d_phiBond.empty())
271 data.d_phiBond[i] =
272 (n_bonds > 0) ? static_cast<float>(double(n_broken) / double(n_bonds))
273 : 0.f;
274 }
275 ); // for_each
276
277 executor.run(taskflow).get();
278
279 // total strain energy over the nodes computed here
280 double te = 0.;
281 for (auto i : data.d_fPdCompNodes)
282 te += double(data.d_e[i]) * data.d_vol[i];
283 data.d_te = static_cast<float>(te);
284}
A class to store model data.
Definition modelData.h:50
Definition contact.h:20
std::unique_ptr< SelfContact > makeSelfContact(const std::string &name)
Build self-contact law named by Model.Self_Contact.
void computeForces(data::ModelData &data)
Assemble nodal peridynamic force (constitutive response stays in material).
Definition pdForce.cpp:28
void exchangeGhostTheta(data::ModelData &data)
Halo-exchange state-based dilatation d_thetaX for PD ghosts.
Definition pdMpi.cpp:317
void log(std::ostringstream &oss, bool screen_out=false, int printMpiRank=print_default_mpi_rank)
Global method to log the message.
Definition io.cpp:41
unsigned int getNThreads()
Get number of threads to be used by taskflow.