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 // A particle with every degree of freedom fixed still has its force
241 // computed, because the reaction force on it is wanted.
242
243 // if this is a two-particle test, we set the force calculation off in
244 // first particle
245 if (d_testDeck_p->d_testName == "two_particle") {
246 d_particlesListTypeAll[0]->d_computeForce = false;
247 }
248
249 log(std::format("{}: Total particles = {}. \n",
251
252 for (const auto &p : d_particlesListTypeAll)
253 if (!p->d_computeForce)
254 log(std::format("{}: Force OFF in Particle i = {}. \n", d_name, p->getId()));
255
256 // Single-particle DOF-MPI: Metis node owners + PD ghost plan (T10).
258
259 log(d_name + ": Creating list of nodes on which force is to be computed.\n");
260 // Selection is per particle or wall: a node enters d_fCompNodes if force is
261 // computed on any node of its body. Selecting individual nodes is not
262 // implemented.
263 const int mpi_rank = util::parallel::mpiRank();
264 for (size_t i = 0; i < d_x.size(); i++) {
265 const auto &ptId = d_ptId[i];
266 const auto &pi = getParticleFromAllList(ptId);
267 bool node_owned = false;
268 if (d_pdDofMpi)
269 node_owned = (static_cast<int>(d_pdNodePartition[i]) == mpi_rank);
270 else if (pi->isWall())
271 // Walls are replicated; assemble wall contact/reaction on rank 0 only.
272 node_owned = (mpi_rank == 0);
273 else
274 node_owned = particle::isLocallyOwned(*pi);
275 if (pi->d_computeForce && node_owned) {
276 d_fContCompNodes.push_back(i);
277 // Walls keep contact (and reaction) but not peridynamic force. Treating a
278 // container as a PD body on a thin/boolean mesh makes Damage_Z explode
279 // and the neighbor search then allocates until the process is OOM-killed.
280 if (!pi->isWall())
281 d_fPdCompNodes.push_back(i);
282 }
283 }
284
285 // initialize remaining fields (if any)
286 d_Z = std::vector<float>(d_x.size(), 0.);
287 // Damage: volume-weighted phi (Silling/Trask) and broken-bond count
288 // fraction (Bhattacharya & Lipton).
289 d_phi = std::vector<float>(d_x.size(), 0.);
290 d_phiBond = std::vector<float>(d_x.size(), 0.);
291
292 t2 = steady_clock::now();
293 log(std::format("{}: Total setup time (ms) = {}. \n",
295
296 // compute complexity information
297 size_t free_dofs = 0;
298 for (const auto &f : d_fix) {
299 for (size_t dof = 0; dof < 3; dof++)
300 if (util::methods::isFree(f, dof))
301 free_dofs++;
302 }
303 log(std::format("{}: Computational complexity information \n"
304 " Total number of particles = {}, number of "
305 "particles = {}, number of walls = {}, \n"
306 " number of dofs = {}, number of free dofs = {}. \n",
310 3 * d_x.size(),
311 free_dofs));
312}
313
317
321
323
324 bool dbg_condition = d_n % d_infoN == 0;
325
326 log(" Compute forces \n", 2, dbg_condition, 3);
327
328 // Refresh ghost grain kinematics from owners before contact / damping.
329 {
330 const auto t_ex0 = steady_clock::now();
332 appendKeyData("mpi_exchange_wall_time",
333 util::methods::timeDiff(t_ex0, steady_clock::now()));
334 }
335
336 // Nodal DOF halo for PD neighbor reads (Single_Particle MPI).
338
339 // reset force
340 auto t1 = steady_clock::now();
341 float force_reset_time = 0;
342 {
343 tf::Executor executor(util::parallel::getNThreads());
344 tf::Taskflow taskflow;
345
346 taskflow.for_each_index(
347 (std::size_t) 0, d_x.size(), (std::size_t) 1,
348 [this](std::size_t i) { this->d_f[i] = util::Point(); }
349 ); // for_each
350
351 executor.run(taskflow).get();
352 force_reset_time = util::methods::timeDiff(t1, steady_clock::now());
353 }
354
355 // compute peridynamic forces
356 t1 = steady_clock::now();
357 pd::computeForces(*this);
358 auto pd_time = util::methods::timeDiff(t1, steady_clock::now());
359 appendKeyData("pd_compute_time", pd_time);
360 appendKeyData("avg_peridynamics_force_time", pd_time/d_infoN);
361
362 float current_contact_neigh_update_time = 0;
363 float contact_time = 0;
364 if (d_input_p->isMultiParticle() && d_contact_p) {
365 // update contact neighborlist
366 t1 = steady_clock::now();
367 d_contact_p->updateNeighborlist(*this);
368 current_contact_neigh_update_time = util::methods::timeDiff(t1,
369 steady_clock::now());
370 appendKeyData("contact_neigh_update_time",
371 current_contact_neigh_update_time);
372 appendKeyData("avg_contact_neigh_update_time",
373 current_contact_neigh_update_time / d_infoN);
374
375 // compute contact forces between particles
376 t1 = steady_clock::now();
377 d_contact_p->computeForces(*this);
378 contact_time = util::methods::timeDiff(t1, steady_clock::now());
379 appendKeyData("contact_compute_time", contact_time);
380 appendKeyData("avg_contact_force_time", contact_time / d_infoN);
381 }
382
383 // Compute external forces
384 t1 = steady_clock::now();
386 auto extf_time = util::methods::timeDiff(t1, steady_clock::now());
387 appendKeyData("extf_compute_time", extf_time);
388 appendKeyData("avg_extf_compute_time", extf_time/d_infoN);
389
390 // output avg time info
391 if (dbg_condition) {
392 if (d_input_p->isMultiParticle()) {
393 log(std::format(" Avg time (ms): \n"
394 " {:48s} = {:8d}\n"
395 " {:48s} = {:8d}\n"
396 " {:48s} = {:8d}\n"
397 " {:48s} = {:8d}\n"
398 " {:48s} = {:8d}\n"
399 " {:48s} = {:8d}\n",
400 "tree update", size_t(getKeyData("avg_tree_update_time")),
401 "contact neigh update",
402 size_t(getKeyData("avg_contact_neigh_update_time")),
403 "contact force",
404 size_t(getKeyData("avg_contact_force_time")),
405 "total contact", size_t(getKeyData("avg_tree_update_time")
406 + getKeyData(
407 "avg_contact_neigh_update_time")
408 + getKeyData(
409 "avg_contact_force_time")),
410 "peridynamics force",
411 size_t(getKeyData("avg_peridynamics_force_time")),
412 "external force",
413 size_t(getKeyData("avg_extf_compute_time") / d_infoN)),
414 2, dbg_condition, 3);
415
416 appendKeyData("avg_tree_update_time", 0.);
417 appendKeyData("avg_contact_neigh_update_time", 0.);
418 appendKeyData("avg_contact_force_time", 0.);
419 appendKeyData("avg_peridynamics_force_time", 0.);
420 appendKeyData("avg_extf_compute_time", 0.);
421 }
422 else {
423 log(std::format(" Avg time (ms): \n"
424 " {:48s} = {:8d}\n"
425 " {:48s} = {:8d}\n",
426 "peridynamics force", size_t(getKeyData("avg_peridynamics_force_time")),
427 "external force", size_t(getKeyData("avg_extf_compute_time")/d_infoN)),
428 2, dbg_condition, 3);
429
430 appendKeyData("avg_peridynamics_force_time", 0.);
431 appendKeyData("avg_extf_compute_time", 0.);
432 }
433 }
434
435 log(std::format(" {:50s} = {:8d} \n",
436 "Force reset time (ms)",
437 size_t(force_reset_time)
438 ),
439 2, dbg_condition, 3);
440
441 log(std::format(" {:50s} = {:8d} \n",
442 "External force time (ms)",
443 size_t(extf_time)
444 ),
445 2, dbg_condition, 3);
446
447 log(std::format(" {:50s} = {:8d} \n",
448 "Peridynamics force time (ms)",
449 size_t(pd_time)
450 ),
451 2, dbg_condition, 3);
452
453 if (d_input_p->isMultiParticle()) {
454
455 log(std::format(" {:50s} = {:8d} \n",
456 "Point cloud update time (ms)",
457 size_t(getKeyData("pt_cloud_update_time"))
458 ),
459 2, dbg_condition, 3);
460
461 log(std::format(" {:50s} = {:8d} \n",
462 "Contact neighborlist update time (ms)",
463 size_t(current_contact_neigh_update_time)
464 ),
465 2, dbg_condition, 3);
466
467 log(std::format(" {:50s} = {:8d} \n",
468 "Contact force time (ms)",
469 size_t(contact_time)
470 ),
471 2, dbg_condition, 3);
472 }
473
474}
475
477 log(" Computing external force \n", 3);
478
479 auto gravity = d_bcDeck_p->d_gravity;
480
481 if (gravity.length() > 1.0E-8) {
482 tf::Executor executor(util::parallel::getNThreads());
483 tf::Taskflow taskflow;
484
485 // Only owned force nodes (walls on rank 0 for particle-MPI; Metis owners
486 // for DOF-MPI). Applying gravity on every rank then Allreducing reaction
487 // would multiply wall weight by mpiSize.
488 const auto &nodes = d_fContCompNodes;
489 taskflow.for_each_index((std::size_t) 0, nodes.size(), (std::size_t)1,
490 [this, gravity, &nodes](std::size_t II) {
491 const size_t i = nodes[II];
492 this->d_f[i] += this->getDensity(i) * gravity;
493 });
494
495 executor.run(taskflow).get();
496 }
497
498 for (auto &p : d_particlesListTypeAll) {
499 if (p->isWall()) {
500 if (util::parallel::mpiRank() == 0)
501 d_fLoading_p->apply(d_time, p);
502 } else if (d_pdDofMpi) {
503 // Nodal ownership: apply on every rank; loading loops all nodes of p,
504 // but only owned force nodes are integrated.
505 d_fLoading_p->apply(d_time, p);
506 } else if (particle::isLocallyOwned(*p)) {
507 d_fLoading_p->apply(d_time, p);
508 }
509 }
510}
511
513 log(" Computing external displacement bc \n", 3);
514 for (auto &p : d_particlesListTypeAll)
515 d_uLoading_p->apply(d_time, p); // applied in parallel
516}
517
519 log("Applying initial condition \n", 3);
520 for (auto &p : d_particlesListTypeAll)
521 loading::applyIC(p, d_bcDeck_p->d_icDeck); // applied in parallel
522}
523
525 if (currentStep() == 0)
526 log(std::format("{}: Output step = {}, time = {:.6f} \n", d_name, d_n, d_time),
527 2);
528 rw::writeOutput(*this);
529}
530
532 if (!d_postprocess_p)
533 return "";
534 return d_postprocess_p->twoParticle(*this);
535}
536
538 if (d_testDeck_p->d_testName == "two_particle")
539 log(ppTwoParticleTest(), 2, d_n % d_infoN == 0, 3);
540 else if (d_testDeck_p->d_testName == "compressive_test")
541 log(ppCompressiveTest(), 2, d_n % d_infoN == 0, 3);
542 if (d_postprocess_p)
543 d_postprocess_p->checkStop(*this);
544}
545
547 if (!d_postprocess_p)
548 return "";
549 return d_postprocess_p->compressive(*this);
550}
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:76
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:309
bool isFree(const int &i, const unsigned int &dof)
Returns true if degree of freedom is free.
Definition vecMethods.h:254
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