PeriDEM 0.3.0
PeriDEM -- Peridynamics-based high-fidelity model for granular media
Loading...
Searching...
No Matches
periDEMModel.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 "periDEMModel.h"
12
13#include "contact/contact.h"
14#include "pd/pdForce.h"
16#include "time_int/integrator.h"
17#include "rw/particleOutput.h"
19
20#include <algorithm>
21#include <stdexcept>
22
23// utils
26#include "util/function.h"
27#include "geom/geomIncludes.h"
28#include "util/matrix.h"
29#include "util/vecMethods.h"
30#include "util/point.h"
31#include "inp/input.h"
32#include "rw/reader.h"
33#include "util/function.h"
34#include "util/randomDist.h"
35#include "util/parallelUtil.h"
40#include "loading/particleIC.h"
41#include "util/io.h"
44#include "pd/pdMpi.h"
45
46#include <cmath>
47#include <cstdio>
48#include <cstdlib>
49#include <filesystem>
50#include <format>
51#include <iostream>
52#include <random>
53
54#include <taskflow/taskflow/taskflow.hpp>
55#include <taskflow/taskflow/algorithm/for_each.hpp>
56
57
58PeriDEMModel::PeriDEMModel(std::shared_ptr<inp::Input> & deck, std::string modelName)
59 : data::ModelData(deck) {
60
61 d_name = std::move(modelName);
62
63 // Ensure output directory exists (Path from input; relative to cwd).
64 {
65 namespace fs = std::filesystem;
66 fs::path out(d_outputDeck_p->d_path);
67 if (!out.empty())
68 fs::create_directories(out);
69 }
70
71 // initialize logger
73 d_outputDeck_p->d_path + "log.txt");
74 if (!d_postprocess_p)
75 d_postprocess_p = std::make_unique<postprocess::Postprocess>();
76}
77
78void PeriDEMModel::log(std::ostringstream &oss, int priority, bool check_condition, int override_priority,
79 bool screen_out) {
80 util::io::log(priority, oss, check_condition, override_priority, screen_out);
81}
82
83void PeriDEMModel::log(const std::string &str, int priority, bool check_condition, int override_priority,
84 bool screen_out) {
85 util::io::log(priority, str, check_condition, override_priority, screen_out);
86}
87
88void PeriDEMModel::run(std::shared_ptr<inp::Input> & deck) {
89
90 // initialize data
91 init();
92
93 // check for restart
94 if (d_modelDeck_p->d_isRestartActive)
95 restart(deck);
96
97 // integrate in time
98 integrate();
99
100 // close
101 close();
102}
103
104void PeriDEMModel::restart(std::shared_ptr<inp::Input> & deck) {
105
106 log(d_name + ": Restarting the simulation\n");
107
108 // set time step to step specified in restart deck
109 d_n = d_restartDeck_p->d_step;
110 d_time = double(d_n) * d_modelDeck_p->d_dt;
111 log(std::format(" Restart step = {}, time = {:.6f} \n", d_n, d_time));
112
113 // get backup of reference configuration
114 std::vector<util::Point> x_ref(d_x.size(), util::Point());
115 for (auto &x : d_x)
116 x_ref.push_back(x);
117
118 // read displacement and velocity from restart file
119 log(" Reading data from restart file = " + d_restartDeck_p->d_file + " \n");
120 auto reader = rw::reader::VtkParticleReader(d_restartDeck_p->d_file);
121 reader.readNodes(this);
122}
123
125 if (d_postprocess_p)
126 d_postprocess_p->close(*this);
127}
128
130
131 // init time step
132 d_n = 0;
133 d_time = 0.;
134 if (d_outputDeck_p->d_dtOut < 1)
135 throw std::runtime_error(
136 "Output_Interval must be >= 1 (use 1 to write every step).");
137 if (d_outputDeck_p->d_dtOutCriteria < 1)
138 d_outputDeck_p->d_dtOutCriteria = d_outputDeck_p->d_dtOut;
139 if (d_outputDeck_p->d_dtOutOld < 1)
140 d_outputDeck_p->d_dtOutOld = d_outputDeck_p->d_dtOut;
141 if (d_outputDeck_p->d_dtTestOut == 0)
142 d_outputDeck_p->d_dtTestOut = std::max<size_t>(1, d_outputDeck_p->d_dtOut / 10);
143 d_infoN = d_outputDeck_p->d_dtOut;
144
145 // debug/information variables
146 {
147 appendKeyData("debug_once", -1);
148 appendKeyData("update_contact_neigh_search_params_init_call_count", 0);
149 appendKeyData("tree_compute_time", 0);
150 appendKeyData("contact_compute_time", 0);
151 appendKeyData("contact_neigh_update_time", 0);
152 appendKeyData("peridynamics_neigh_update_time", 0);
153 appendKeyData("pd_compute_time", 0);
154 appendKeyData("extf_compute_time", 0);
155 appendKeyData("integrate_compute_time", 0);
156 appendKeyData("pt_cloud_update_time", 0);
157 appendKeyData("avg_tree_update_time", 0);
158 appendKeyData("avg_contact_neigh_update_time", 0);
159 appendKeyData("avg_contact_force_time", 0);
160 appendKeyData("avg_peridynamics_force_time", 0);
161 appendKeyData("avg_extf_compute_time", 0);
162 appendKeyData("pen_dist", 0);
163 appendKeyData("max_y", 0);
164 appendKeyData("contact_area_radius", 0);
165 }
166
167
168 auto t1 = steady_clock::now();
169 auto t2 = steady_clock::now();
170 log(d_name + ": Initializing objects.\n");
171
172 // create particles
173 log(d_name + ": Creating particles.\n");
176
177 log(d_name + ": Creating maximum velocity data for particles.\n");
179 = std::vector<double>(d_particlesListTypeAll.size(), 0.);
181
182 // setup contact
183 if (d_input_p->isMultiParticle()) {
184 if (!d_contact_p)
185 d_contact_p = std::make_unique<contact::Contact>();
186 log(d_name + ": Setting up contact.\n");
187 d_contact_p->setup(*this);
188 }
189
190 // setup element-node connectivity data if needed
191 log(d_name + ": Setting up element-node connectivity data for strain/stress.\n");
193
194 // create search object
195 log(d_name + ": Creating neighbor search tree.\n");
196
197 // create tree object
198 d_nsearch_p = std::make_unique<NSearch>(d_x, d_outputDeck_p->d_debug);
199
200 // setup tree
201 double set_tree_time = d_nsearch_p->setInputCloud();
202 log(std::format("{}: Tree setup time (ms) = {}. \n", d_name, set_tree_time));
203
204 // create neighborlists
205 log(d_name + ": Creating neighborlist for peridynamics.\n");
206 t1 = steady_clock::now();
208 t2 = steady_clock::now();
209 appendKeyData("peridynamics_neigh_update_time", util::methods::timeDiff(t1, t2));
210
211 if (d_input_p->isMultiParticle()) {
212 log(d_name + ": Creating neighborlist for contact.\n");
213 d_contNeighUpdateInterval = d_particleDeck_p->d_pNeighDeck.d_neighUpdateInterval;
214 d_contNeighSearchRadius = d_particleDeck_p->d_pNeighDeck.d_sFactor * d_maxContactR;
215 t1 = steady_clock::now();
216 d_contact_p->updateNeighborlist(*this);
217 t2 = steady_clock::now();
218 appendKeyData("contact_neigh_update_time", util::methods::timeDiff(t1, t2));
219 }
220
221 // create peridynamic bonds
222 log(d_name + ": Creating peridynamics bonds.\n");
223 d_fracture_p = std::make_unique<geometry::Fracture>(&d_x, &d_neighPd);
224
225 // compute quantities in state-based simulations
226 log(d_name + ": Compute state-based peridynamic quantities.\n");
227 material::computeStateMx(this, true);
228
229 // initialize loading class
230 log(d_name + ": Initializing displacement loading object.\n");
232 std::make_unique<loading::ParticleULoading>(d_bcDeck_p->d_dispDeck);
233 for (auto &p : d_particlesListTypeAll)
234 d_uLoading_p->setFixity(p);
235
236 log(d_name + ": Initializing force loading object.\n");
238 std::make_unique<loading::ParticleFLoading>(d_bcDeck_p->d_forceDeck);
239
240 // if all dofs of particle is fixed, then mark it so that we do not
241 // compute force
242 // MAYBE NOT as we may be interested in reaction forces
243 // for (auto &p : d_particlesListTypeAll)
244 // p->checkFixityForForce(); // TODO implement
245
246 // if this is a two-particle test, we set the force calculation off in
247 // first particle
248 if (d_testDeck_p->d_testName == "two_particle") {
249 d_particlesListTypeAll[0]->d_computeForce = false;
250 }
251
252 log(std::format("{}: Total particles = {}. \n",
254
255 for (const auto &p : d_particlesListTypeAll)
256 if (!p->d_computeForce)
257 log(std::format("{}: Force OFF in Particle i = {}. \n", d_name, p->getId()));
258
259 // Single-particle DOF-MPI: Metis node owners + PD ghost plan (T10).
261
262 log(d_name + ": Creating list of nodes on which force is to be computed.\n");
263 // TODO for now we simply look at particle/wall and check if we compute
264 // force on any of its node. Later, one can have control on individual
265 // nodes of particle/wall and remove from d_fCompNodes if no force is to
266 // be computed on them
267 const int mpi_rank = util::parallel::mpiRank();
268 for (size_t i = 0; i < d_x.size(); i++) {
269 const auto &ptId = d_ptId[i];
270 const auto &pi = getParticleFromAllList(ptId);
271 bool node_owned = false;
272 if (d_pdDofMpi)
273 node_owned = (static_cast<int>(d_pdNodePartition[i]) == mpi_rank);
274 else if (pi->isWall())
275 // Walls are replicated; assemble wall contact/reaction on rank 0 only.
276 node_owned = (mpi_rank == 0);
277 else
278 node_owned = particle::isLocallyOwned(*pi);
279 if (pi->d_computeForce && node_owned) {
280 d_fContCompNodes.push_back(i);
281 // Walls keep contact (and reaction) but not peridynamic force. Treating a
282 // container as a PD body on a thin/boolean mesh makes Damage_Z explode
283 // and the neighbor search then allocates until the process is OOM-killed.
284 if (!pi->isWall())
285 d_fPdCompNodes.push_back(i);
286 }
287 }
288
289 // initialize remaining fields (if any)
290 d_Z = std::vector<float>(d_x.size(), 0.);
291 // Damage: volume-weighted phi (Silling/Trask) and broken-bond count
292 // fraction (Bhattacharya & Lipton).
293 d_phi = std::vector<float>(d_x.size(), 0.);
294 d_phiBond = std::vector<float>(d_x.size(), 0.);
295
296 t2 = steady_clock::now();
297 log(std::format("{}: Total setup time (ms) = {}. \n",
299
300 // compute complexity information
301 size_t free_dofs = 0;
302 for (const auto &f : d_fix) {
303 for (size_t dof = 0; dof < 3; dof++)
304 if (util::methods::isFree(f, dof))
305 free_dofs++;
306 }
307 log(std::format("{}: Computational complexity information \n"
308 " Total number of particles = {}, number of "
309 "particles = {}, number of walls = {}, \n"
310 " number of dofs = {}, number of free dofs = {}. \n",
314 3 * d_x.size(),
315 free_dofs));
316}
317
321
325
327
328 bool dbg_condition = d_n % d_infoN == 0;
329
330 log(" Compute forces \n", 2, dbg_condition, 3);
331
332 // Refresh ghost grain kinematics from owners before contact / damping.
333 {
334 const auto t_ex0 = steady_clock::now();
336 appendKeyData("mpi_exchange_wall_time",
337 util::methods::timeDiff(t_ex0, steady_clock::now()));
338 }
339
340 // Nodal DOF halo for PD neighbor reads (Single_Particle MPI).
342
343 // reset force
344 auto t1 = steady_clock::now();
345 float force_reset_time = 0;
346 {
347 tf::Executor executor(util::parallel::getNThreads());
348 tf::Taskflow taskflow;
349
350 taskflow.for_each_index(
351 (std::size_t) 0, d_x.size(), (std::size_t) 1,
352 [this](std::size_t i) { this->d_f[i] = util::Point(); }
353 ); // for_each
354
355 executor.run(taskflow).get();
356 force_reset_time = util::methods::timeDiff(t1, steady_clock::now());
357 }
358
359 // compute peridynamic forces
360 t1 = steady_clock::now();
361 pd::computeForces(*this);
362 auto pd_time = util::methods::timeDiff(t1, steady_clock::now());
363 appendKeyData("pd_compute_time", pd_time);
364 appendKeyData("avg_peridynamics_force_time", pd_time/d_infoN);
365
366 float current_contact_neigh_update_time = 0;
367 float contact_time = 0;
368 if (d_input_p->isMultiParticle() && d_contact_p) {
369 // update contact neighborlist
370 t1 = steady_clock::now();
371 d_contact_p->updateNeighborlist(*this);
372 current_contact_neigh_update_time = util::methods::timeDiff(t1,
373 steady_clock::now());
374 appendKeyData("contact_neigh_update_time",
375 current_contact_neigh_update_time);
376 appendKeyData("avg_contact_neigh_update_time",
377 current_contact_neigh_update_time / d_infoN);
378
379 // compute contact forces between particles
380 t1 = steady_clock::now();
381 d_contact_p->computeForces(*this);
382 contact_time = util::methods::timeDiff(t1, steady_clock::now());
383 appendKeyData("contact_compute_time", contact_time);
384 appendKeyData("avg_contact_force_time", contact_time / d_infoN);
385 }
386
387 // Compute external forces
388 t1 = steady_clock::now();
390 auto extf_time = util::methods::timeDiff(t1, steady_clock::now());
391 appendKeyData("extf_compute_time", extf_time);
392 appendKeyData("avg_extf_compute_time", extf_time/d_infoN);
393
394 // output avg time info
395 if (dbg_condition) {
396 if (d_input_p->isMultiParticle()) {
397 log(std::format(" Avg time (ms): \n"
398 " {:48s} = {:8d}\n"
399 " {:48s} = {:8d}\n"
400 " {:48s} = {:8d}\n"
401 " {:48s} = {:8d}\n"
402 " {:48s} = {:8d}\n"
403 " {:48s} = {:8d}\n",
404 "tree update", size_t(getKeyData("avg_tree_update_time")),
405 "contact neigh update",
406 size_t(getKeyData("avg_contact_neigh_update_time")),
407 "contact force",
408 size_t(getKeyData("avg_contact_force_time")),
409 "total contact", size_t(getKeyData("avg_tree_update_time")
410 + getKeyData(
411 "avg_contact_neigh_update_time")
412 + getKeyData(
413 "avg_contact_force_time")),
414 "peridynamics force",
415 size_t(getKeyData("avg_peridynamics_force_time")),
416 "external force",
417 size_t(getKeyData("avg_extf_compute_time") / d_infoN)),
418 2, dbg_condition, 3);
419
420 appendKeyData("avg_tree_update_time", 0.);
421 appendKeyData("avg_contact_neigh_update_time", 0.);
422 appendKeyData("avg_contact_force_time", 0.);
423 appendKeyData("avg_peridynamics_force_time", 0.);
424 appendKeyData("avg_extf_compute_time", 0.);
425 }
426 else {
427 log(std::format(" Avg time (ms): \n"
428 " {:48s} = {:8d}\n"
429 " {:48s} = {:8d}\n",
430 "peridynamics force", size_t(getKeyData("avg_peridynamics_force_time")),
431 "external force", size_t(getKeyData("avg_extf_compute_time")/d_infoN)),
432 2, dbg_condition, 3);
433
434 appendKeyData("avg_peridynamics_force_time", 0.);
435 appendKeyData("avg_extf_compute_time", 0.);
436 }
437 }
438
439 log(std::format(" {:50s} = {:8d} \n",
440 "Force reset time (ms)",
441 size_t(force_reset_time)
442 ),
443 2, dbg_condition, 3);
444
445 log(std::format(" {:50s} = {:8d} \n",
446 "External force time (ms)",
447 size_t(extf_time)
448 ),
449 2, dbg_condition, 3);
450
451 log(std::format(" {:50s} = {:8d} \n",
452 "Peridynamics force time (ms)",
453 size_t(pd_time)
454 ),
455 2, dbg_condition, 3);
456
457 if (d_input_p->isMultiParticle()) {
458
459 log(std::format(" {:50s} = {:8d} \n",
460 "Point cloud update time (ms)",
461 size_t(getKeyData("pt_cloud_update_time"))
462 ),
463 2, dbg_condition, 3);
464
465 log(std::format(" {:50s} = {:8d} \n",
466 "Contact neighborlist update time (ms)",
467 size_t(current_contact_neigh_update_time)
468 ),
469 2, dbg_condition, 3);
470
471 log(std::format(" {:50s} = {:8d} \n",
472 "Contact force time (ms)",
473 size_t(contact_time)
474 ),
475 2, dbg_condition, 3);
476 }
477
478}
479
481 log(" Computing external force \n", 3);
482
483 auto gravity = d_bcDeck_p->d_gravity;
484
485 if (gravity.length() > 1.0E-8) {
486 tf::Executor executor(util::parallel::getNThreads());
487 tf::Taskflow taskflow;
488
489 // Only owned force nodes (walls on rank 0 for particle-MPI; Metis owners
490 // for DOF-MPI). Applying gravity on every rank then Allreducing reaction
491 // would multiply wall weight by mpiSize.
492 const auto &nodes = d_fContCompNodes;
493 taskflow.for_each_index((std::size_t) 0, nodes.size(), (std::size_t)1,
494 [this, gravity, &nodes](std::size_t II) {
495 const size_t i = nodes[II];
496 this->d_f[i] += this->getDensity(i) * gravity;
497 });
498
499 executor.run(taskflow).get();
500 }
501
502 for (auto &p : d_particlesListTypeAll) {
503 if (p->isWall()) {
504 if (util::parallel::mpiRank() == 0)
505 d_fLoading_p->apply(d_time, p);
506 } else if (d_pdDofMpi) {
507 // Nodal ownership: apply on every rank; loading loops all nodes of p,
508 // but only owned force nodes are integrated.
509 d_fLoading_p->apply(d_time, p);
510 } else if (particle::isLocallyOwned(*p)) {
511 d_fLoading_p->apply(d_time, p);
512 }
513 }
514}
515
517 log(" Computing external displacement bc \n", 3);
518 for (auto &p : d_particlesListTypeAll)
519 d_uLoading_p->apply(d_time, p); // applied in parallel
520}
521
523 log("Applying initial condition \n", 3);
524 for (auto &p : d_particlesListTypeAll)
525 loading::applyIC(p, d_bcDeck_p->d_icDeck); // applied in parallel
526}
527
529 if (currentStep() == 0)
530 log(std::format("{}: Output step = {}, time = {:.6f} \n", d_name, d_n, d_time),
531 2);
532 rw::writeOutput(*this);
533}
534
536 if (!d_postprocess_p)
537 return "";
538 return d_postprocess_p->twoParticle(*this);
539}
540
542 if (d_testDeck_p->d_testName == "two_particle")
543 log(ppTwoParticleTest(), 2, d_n % d_infoN == 0, 3);
544 else if (d_testDeck_p->d_testName == "compressive_test")
545 log(ppCompressiveTest(), 2, d_n % d_infoN == 0, 3);
546 if (d_postprocess_p)
547 d_postprocess_p->checkStop(*this);
548}
549
551 if (!d_postprocess_p)
552 return "";
553 return d_postprocess_p->compressive(*this);
554}
void applyInitialCondition()
void restart(std::shared_ptr< inp::Input > &deck)
std::unique_ptr< contact::Contact > d_contact_p
void computeExternalForces()
void applyDisplacementBC()
void log(std::ostringstream &oss, int priority=0, bool check_condition=true, int override_priority=-1, bool screen_out=false)
std::string ppTwoParticleTest()
std::unique_ptr< postprocess::Postprocess > d_postprocess_p
void run(std::shared_ptr< inp::Input > &deck)
PeriDEMModel(std::shared_ptr< inp::Input > &deck, std::string modelName="PeriDEMModel")
std::string ppCompressiveTest()
std::vector< util::Point > d_x
Current positions of the nodes.
Definition modelData.h:745
std::vector< float > d_Z
Damage at nodes.
Definition modelData.h:818
std::vector< size_t > d_fContCompNodes
List of global nodes on which force (contact) is to be computed.
Definition modelData.h:815
std::vector< double > d_maxVelocityParticlesListTypeAll
Maximum velocity among all nodes in the particle for each particle.
Definition modelData.h:719
std::vector< uint8_t > d_fix
Vector of fixity mask of each node.
Definition modelData.h:792
size_t currentStep() const
Definition modelData.h:164
std::unique_ptr< loading::ParticleULoading > d_uLoading_p
Pointer to displacement Loading object.
Definition modelData.h:730
std::shared_ptr< inp::TestDeck > d_testDeck_p
Test deck.
Definition modelData.h:631
bool d_pdDofMpi
Nodal DOF-MPI active.
Definition modelData.h:680
std::vector< particle::BaseParticle * > d_particlesListTypeParticle
List of particles.
Definition modelData.h:707
std::vector< size_t > d_pdNodePartition
Owner rank for each node when d_pdDofMpi is true.
Definition modelData.h:692
std::unique_ptr< geometry::Fracture > d_fracture_p
Fracture state of bonds.
Definition modelData.h:736
double d_time
Current time.
Definition modelData.h:595
std::string d_name
Model name for log prefixes.
Definition modelData.h:604
std::shared_ptr< inp::ModelDeck > d_modelDeck_p
Pointer to deck holding problem related data.
Definition modelData.h:622
std::vector< size_t > d_ptId
Global node to particle id (walls are assigned id after last particle id)
Definition modelData.h:764
std::vector< float > d_phi
Damage function at the nodes (volume-weighted, Silling 2000)
Definition modelData.h:834
std::shared_ptr< inp::RestartDeck > d_restartDeck_p
Pointer to deck holding restart related data.
Definition modelData.h:628
std::shared_ptr< inp::ParticleDeck > d_particleDeck_p
Pointer to deck holding particle related data.
Definition modelData.h:637
std::vector< particle::BaseParticle * > d_particlesListTypeWall
List of walls.
Definition modelData.h:710
std::unique_ptr< NSearch > d_nsearch_p
Pointer to nsearch.
Definition modelData.h:739
std::vector< particle::BaseParticle * > d_particlesListTypeAll
List of particles + walls.
Definition modelData.h:704
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
void appendKeyData(std::string key, double data, bool issue_err=false)
Append value to data associated with key.
Definition modelData.h:215
double d_contNeighSearchRadius
Neighborlist contact search radius (multiple of d_maxContactR). This variable will be updated during ...
Definition modelData.h:658
std::vector< size_t > d_fPdCompNodes
List of global nodes on which force (peridynamic/internal) is to be computed.
Definition modelData.h:812
double getKeyData(std::string key, bool issue_err=false)
Get data for a key.
Definition modelData.h:206
std::unique_ptr< loading::ParticleFLoading > d_fLoading_p
Pointer to force Loading object.
Definition modelData.h:733
double d_maxVelocity
Maximum velocity among all nodes.
Definition modelData.h:722
std::shared_ptr< inp::Input > d_input_p
Pointer to Input object.
Definition modelData.h:619
size_t d_contNeighUpdateInterval
Neighborlist update interval.
Definition modelData.h:652
size_t d_infoN
Print log step interval.
Definition modelData.h:601
std::shared_ptr< inp::BCDeck > d_bcDeck_p
Boundary condition deck.
Definition modelData.h:634
std::shared_ptr< inp::OutputDeck > d_outputDeck_p
Pointer to deck holding output related data.
Definition modelData.h:625
std::vector< float > d_phiBond
Damage as broken-bond count fraction (Bhattacharya & Lipton 2023)
Definition modelData.h:837
size_t d_n
Current time step.
Definition modelData.h:592
const particle::BaseParticle * getParticleFromAllList(size_t i) const
Get pointer to base particle.
Definition modelData.h:86
A vtk writer for simple point data and complex fem mesh data.
void step(Model &model)
Definition integrator.h:90
void integrate(Model &model)
Definition integrator.h:37
void setupQuadratureData(ModelData &data)
Definition contact.h:20
void applyIC(particle::BaseParticle *particle, const std::vector< inp::BCBaseDeck > &icVec)
Applies displacement initial condition.
void computeStateMx(data::ModelData *model, bool compute_in_parallel=false)
Computes the moment term in state-based peridynamic formulation.
void updatePeridynamicNeighborlist(data::ModelData &data)
Horizon search restricted to the same particle.
void createParticles(data::ModelData &data)
Build refs then place particles according to the generation method.
void assignMpiOwners(data::ModelData &data)
Assign grain owners by spatial 2D brick decomposition of centers. Walls get owner -1 (all ranks)....
void exchangeGhostKinematics(data::ModelData &data)
Distance-limited ghosts + kinematics exchange. Rebuilds the ghost plan on a Verlet-skin cadence (tied...
bool isLocallyOwned(const BaseParticle &p)
True if this rank updates / assembles forces for the particle. Walls are replicated on every rank....
void setupDofPartition(data::ModelData &data)
Metis-partition nodes on the PD neighbor graph and build ghost plans. Call after d_neighPd is ready a...
Definition pdMpi.cpp:233
void computeForces(data::ModelData &data)
Assemble nodal peridynamic force (constitutive response stays in material).
Definition pdForce.cpp:28
void exchangeGhostDisplacement(data::ModelData &data)
Halo-exchange nodal displacements (and current x) for PD ghosts.
Definition pdMpi.cpp:286
void writeOutput(data::ModelData &data)
Write VTU/PVD (and optional strain VTU / particle-location CSV).
void initLogger(int debug_level=logger_default_debug_lvl, std::string filename="")
Initializes the logger.
Definition io.cpp:18
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
T max(const std::vector< T > &data)
Returns the maximum from list of data.
Definition vecMethods.h:74
float timeDiff(std::chrono::steady_clock::time_point begin, std::chrono::steady_clock::time_point end, std::string unit="microseconds")
Returns difference between two times.
Definition vecMethods.h:304
bool isFree(const int &i, const unsigned int &dof)
Returns true if degree of freedom is free.
Definition vecMethods.h:249
unsigned int getNThreads()
Get number of threads to be used by taskflow.
int mpiRank()
get rank (id) of this processor
A structure to represent 3d vectors.
Definition point.h:30