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](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 data.d_thetaX[i] = 3. * theta / m;
116 } // if it is state-based
117 } // loop over nodes
118 ); // for_each
119
120 executor.run(taskflow).get();
121
122 // Ghost nodes need owner dilatations before the force pass.
124 }
125
126 // compute the internal forces
127 tf::Executor executor(util::parallel::getNThreads());
128 tf::Taskflow taskflow;
129
130 taskflow.for_each_index(
131 (std::size_t) 0, data.d_fPdCompNodes.size(), (std::size_t) 1,
132 [&data, selfContact = selfContact.get(), abs_stretch_break](std::size_t II) {
133 auto i = data.d_fPdCompNodes[II];
134
135 // local variable to hold force
136 util::Point force_i = util::Point();
137 double scalar_f = 0.;
138
139 // for damage
140 float Zi = 0.;
141 // Silling (2000) damage: phi = 1 - ∫ mu dV' / ∫ dV' over the horizon
142 // (reported by Silling 2003 and Trask). Bhattacharya & Lipton instead
143 // define damage as the broken-bond count fraction, kept separately.
144 double vol_all = 0.;
145 double vol_intact = 0.;
146 size_t n_bonds = 0;
147 size_t n_broken = 0;
148
149 const auto rhoi = data.getDensity(i);
150 const auto &ptIdi = data.getPtId(i);
151 auto &pi = data.getParticleFromAllList(ptIdi);
152
153 const double horizon = pi->getHorizon();
154 const double mesh_size = pi->getMeshSize();
155 const auto &xi = data.d_xRef[i];
156 const auto &ui = data.d_u[i];
157 const auto &mi = data.d_mX[i];
158 const auto &thetai = data.d_thetaX[i];
159
160 // upper and lower bound for volume correction
161 auto check_up = horizon + 0.5 * mesh_size;
162 auto check_low = horizon - 0.5 * mesh_size;
163
164 // loop over neighbors
165 {
166 size_t k = 0;
167 for (size_t j : data.d_neighPd[i]) {
168 auto fs = data.d_fracture_p->getBondState(i, k);
169 const auto &xj = data.d_xRef[j];
170 const auto &uj = data.d_u[j];
171 auto volj = data.d_vol[j];
172 double rji = (xj - xi).length();
173 if (!(rji > 0.)) {
174 k++;
175 continue;
176 }
177 double Sji = pi->d_material_p->getS(xj - xi, uj - ui);
178
179 if (!fs) {
180 const auto &mj = data.d_mX[j];
181 const auto &thetaj = data.d_thetaX[j];
182
183 // get corrected volume of node j
184 if (util::isGreater(rji, check_low))
185 volj *= (check_up - rji) / mesh_size;
186
187 // handle two cases differently
188 if (pi->d_material_p->isStateActive()) {
189
190 auto ef_i =
191 pi->d_material_p->getBondEF(rji, Sji, fs, mi, thetai);
192 auto ef_j =
193 pi->d_material_p->getBondEF(rji, Sji, fs, mj, thetaj);
194
195 // compute the contribution of bond force to force at i
196 scalar_f = (ef_i.second + ef_j.second) * volj;
197
198 force_i += scalar_f * pi->d_material_p->getBondForceDirection(
199 xj - xi, uj - ui);
200 } // if state-based
201 else {
202
203 // Bond-based materials (e.g. PMB) historically broke inside
204 // getBondEF using |s|>Sc. Honor Model.Bond_Break here instead:
205 // tension (default, literature PMB) vs absolute_stretch.
206 const double Sc = pi->d_material_p->getSc(rji);
207 const bool broke = abs_stretch_break
208 ? util::isGreater(std::abs(Sji), Sc + 1.0e-10)
209 : util::isGreater(Sji, Sc + 1.0e-10);
210 if (broke)
211 fs = true;
212 data.d_fracture_p->setBondState(i, k, fs);
213
214 if (!fs) {
215 auto ef =
216 pi->d_material_p->getBondEF(rji, Sji, fs, /*break_bonds=*/false);
217 scalar_f = ef.second * volj;
218 force_i += scalar_f * pi->d_material_p->getBondForceDirection(
219 xj - xi, uj - ui);
220 } else {
221 const auto yji = xj + uj - (xi + ui);
222 force_i +=
223 selfContact->force(yji, volj, pi->d_Kn, pi->d_Rc, rji);
224 }
225 } // if bond-based
226 } // if bond not broken
227 else {
228 const auto yji = xj + uj - (xi + ui);
229 force_i +=
230 selfContact->force(yji, volj, pi->d_Kn, pi->d_Rc, rji);
231 } // if bond is broken
232
233 // calculate damage
234 auto Sc = pi->d_material_p->getSc(rji);
235 if (util::isGreater(std::abs(Sji / Sc), Zi))
236 Zi = std::abs(Sji / Sc);
237
238 // damage function phi (bond-state weighted by neighbor volume)
239 vol_all += data.d_vol[j];
240 n_bonds++;
241 if (!data.d_fracture_p->getBondState(i, k))
242 vol_intact += data.d_vol[j];
243 else
244 n_broken++;
245
246 k++;
247 } // loop over neighbors
248
249 } // peridynamic force
250
251 // update force (we remove any force from
252 // previous steps and add peridynamics force)
253 data.d_f[i] = force_i;
254
255 data.d_Z[i] = Zi;
256 if (!data.d_phi.empty())
257 data.d_phi[i] =
258 (vol_all > 0.) ? static_cast<float>(1. - vol_intact / vol_all) : 0.f;
259 if (!data.d_phiBond.empty())
260 data.d_phiBond[i] =
261 (n_bonds > 0) ? static_cast<float>(double(n_broken) / double(n_bonds))
262 : 0.f;
263 }
264 ); // for_each
265
266 executor.run(taskflow).get();
267
268}
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.