54#include <taskflow/taskflow/taskflow.hpp>
55#include <taskflow/taskflow/algorithm/for_each.hpp>
59 :
data::ModelData(deck) {
61 d_name = std::move(modelName);
65 namespace fs = std::filesystem;
68 fs::create_directories(out);
78void PeriDEMModel::log(std::ostringstream &oss,
int priority,
bool check_condition,
int override_priority,
80 util::io::log(priority, oss, check_condition, override_priority, screen_out);
83void PeriDEMModel::log(
const std::string &str,
int priority,
bool check_condition,
int override_priority,
85 util::io::log(priority, str, check_condition, override_priority, screen_out);
106 log(
d_name +
": Restarting the simulation\n");
111 log(std::format(
" Restart step = {}, time = {:.6f} \n",
d_n,
d_time));
121 reader.readNodes(
this);
135 throw std::runtime_error(
136 "Output_Interval must be >= 1 (use 1 to write every step).");
148 appendKeyData(
"update_contact_neigh_search_params_init_call_count", 0);
168 auto t1 = steady_clock::now();
169 auto t2 = steady_clock::now();
170 log(
d_name +
": Initializing objects.\n");
177 log(
d_name +
": Creating maximum velocity data for particles.\n");
185 d_contact_p = std::make_unique<contact::Contact>();
191 log(
d_name +
": Setting up element-node connectivity data for strain/stress.\n");
195 log(
d_name +
": Creating neighbor search tree.\n");
201 double set_tree_time =
d_nsearch_p->setInputCloud();
202 log(std::format(
"{}: Tree setup time (ms) = {}. \n",
d_name, set_tree_time));
205 log(
d_name +
": Creating neighborlist for peridynamics.\n");
206 t1 = steady_clock::now();
208 t2 = steady_clock::now();
212 log(
d_name +
": Creating neighborlist for contact.\n");
215 t1 = steady_clock::now();
217 t2 = steady_clock::now();
222 log(
d_name +
": Creating peridynamics bonds.\n");
226 log(
d_name +
": Compute state-based peridynamic quantities.\n");
230 log(
d_name +
": Initializing displacement loading object.\n");
232 std::make_unique<loading::ParticleULoading>(
d_bcDeck_p->d_dispDeck);
236 log(
d_name +
": Initializing force loading object.\n");
238 std::make_unique<loading::ParticleFLoading>(
d_bcDeck_p->d_forceDeck);
252 log(std::format(
"{}: Total particles = {}. \n",
256 if (!p->d_computeForce)
257 log(std::format(
"{}: Force OFF in Particle i = {}. \n",
d_name, p->getId()));
262 log(
d_name +
": Creating list of nodes on which force is to be computed.\n");
268 for (
size_t i = 0; i <
d_x.size(); i++) {
269 const auto &ptId =
d_ptId[i];
271 bool node_owned =
false;
274 else if (pi->isWall())
276 node_owned = (mpi_rank == 0);
279 if (pi->d_computeForce && node_owned) {
290 d_Z = std::vector<float>(
d_x.size(), 0.);
293 d_phi = std::vector<float>(
d_x.size(), 0.);
296 t2 = steady_clock::now();
297 log(std::format(
"{}: Total setup time (ms) = {}. \n",
301 size_t free_dofs = 0;
302 for (
const auto &f :
d_fix) {
303 for (
size_t dof = 0; dof < 3; dof++)
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",
330 log(
" Compute forces \n", 2, dbg_condition, 3);
334 const auto t_ex0 = steady_clock::now();
344 auto t1 = steady_clock::now();
345 float force_reset_time = 0;
348 tf::Taskflow taskflow;
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(); }
355 executor.run(taskflow).get();
360 t1 = steady_clock::now();
366 float current_contact_neigh_update_time = 0;
367 float contact_time = 0;
370 t1 = steady_clock::now();
373 steady_clock::now());
375 current_contact_neigh_update_time);
377 current_contact_neigh_update_time /
d_infoN);
380 t1 = steady_clock::now();
388 t1 = steady_clock::now();
397 log(std::format(
" Avg time (ms): \n"
404 "tree update",
size_t(
getKeyData(
"avg_tree_update_time")),
405 "contact neigh update",
406 size_t(
getKeyData(
"avg_contact_neigh_update_time")),
409 "total contact",
size_t(
getKeyData(
"avg_tree_update_time")
411 "avg_contact_neigh_update_time")
413 "avg_contact_force_time")),
414 "peridynamics force",
415 size_t(
getKeyData(
"avg_peridynamics_force_time")),
418 2, dbg_condition, 3);
427 log(std::format(
" Avg time (ms): \n"
430 "peridynamics force",
size_t(
getKeyData(
"avg_peridynamics_force_time")),
432 2, dbg_condition, 3);
439 log(std::format(
" {:50s} = {:8d} \n",
440 "Force reset time (ms)",
441 size_t(force_reset_time)
443 2, dbg_condition, 3);
445 log(std::format(
" {:50s} = {:8d} \n",
446 "External force time (ms)",
449 2, dbg_condition, 3);
451 log(std::format(
" {:50s} = {:8d} \n",
452 "Peridynamics force time (ms)",
455 2, dbg_condition, 3);
459 log(std::format(
" {:50s} = {:8d} \n",
460 "Point cloud update time (ms)",
463 2, dbg_condition, 3);
465 log(std::format(
" {:50s} = {:8d} \n",
466 "Contact neighborlist update time (ms)",
467 size_t(current_contact_neigh_update_time)
469 2, dbg_condition, 3);
471 log(std::format(
" {:50s} = {:8d} \n",
472 "Contact force time (ms)",
475 2, dbg_condition, 3);
481 log(
" Computing external force \n", 3);
485 if (gravity.length() > 1.0E-8) {
487 tf::Taskflow taskflow;
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;
499 executor.run(taskflow).get();
517 log(
" Computing external displacement bc \n", 3);
523 log(
"Applying initial condition \n", 3);
544 else if (
d_testDeck_p->d_testName ==
"compressive_test")
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.
std::vector< float > d_Z
Damage at nodes.
std::vector< size_t > d_fContCompNodes
List of global nodes on which force (contact) is to be computed.
std::vector< double > d_maxVelocityParticlesListTypeAll
Maximum velocity among all nodes in the particle for each particle.
std::vector< uint8_t > d_fix
Vector of fixity mask of each node.
size_t currentStep() const
std::unique_ptr< loading::ParticleULoading > d_uLoading_p
Pointer to displacement Loading object.
std::shared_ptr< inp::TestDeck > d_testDeck_p
Test deck.
bool d_pdDofMpi
Nodal DOF-MPI active.
std::vector< particle::BaseParticle * > d_particlesListTypeParticle
List of particles.
std::vector< size_t > d_pdNodePartition
Owner rank for each node when d_pdDofMpi is true.
std::unique_ptr< geometry::Fracture > d_fracture_p
Fracture state of bonds.
double d_time
Current time.
std::string d_name
Model name for log prefixes.
std::shared_ptr< inp::ModelDeck > d_modelDeck_p
Pointer to deck holding problem related data.
std::vector< size_t > d_ptId
Global node to particle id (walls are assigned id after last particle id)
std::vector< float > d_phi
Damage function at the nodes (volume-weighted, Silling 2000)
std::shared_ptr< inp::RestartDeck > d_restartDeck_p
Pointer to deck holding restart related data.
std::shared_ptr< inp::ParticleDeck > d_particleDeck_p
Pointer to deck holding particle related data.
std::vector< particle::BaseParticle * > d_particlesListTypeWall
List of walls.
std::unique_ptr< NSearch > d_nsearch_p
Pointer to nsearch.
std::vector< particle::BaseParticle * > d_particlesListTypeAll
List of particles + walls.
std::vector< std::vector< size_t > > d_neighPd
Neighbor data for peridynamic forces.
double d_maxContactR
Maximum contact radius between over pairs of particles and walls.
void appendKeyData(std::string key, double data, bool issue_err=false)
Append value to data associated with key.
double d_contNeighSearchRadius
Neighborlist contact search radius (multiple of d_maxContactR). This variable will be updated during ...
std::vector< size_t > d_fPdCompNodes
List of global nodes on which force (peridynamic/internal) is to be computed.
double getKeyData(std::string key, bool issue_err=false)
Get data for a key.
std::unique_ptr< loading::ParticleFLoading > d_fLoading_p
Pointer to force Loading object.
double d_maxVelocity
Maximum velocity among all nodes.
std::shared_ptr< inp::Input > d_input_p
Pointer to Input object.
size_t d_contNeighUpdateInterval
Neighborlist update interval.
size_t d_infoN
Print log step interval.
std::shared_ptr< inp::BCDeck > d_bcDeck_p
Boundary condition deck.
std::shared_ptr< inp::OutputDeck > d_outputDeck_p
Pointer to deck holding output related data.
std::vector< float > d_phiBond
Damage as broken-bond count fraction (Bhattacharya & Lipton 2023)
size_t d_n
Current time step.
const particle::BaseParticle * getParticleFromAllList(size_t i) const
Get pointer to base particle.
A vtk writer for simple point data and complex fem mesh data.
void integrate(Model &model)
void setupQuadratureData(ModelData &data)
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...
void computeForces(data::ModelData &data)
Assemble nodal peridynamic force (constitutive response stays in material).
void exchangeGhostDisplacement(data::ModelData &data)
Halo-exchange nodal displacements (and current x) for PD ghosts.
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.
void log(std::ostringstream &oss, bool screen_out=false, int printMpiRank=print_default_mpi_rank)
Global method to log the message.
T max(const std::vector< T > &data)
Returns the maximum from list of data.
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.
bool isFree(const int &i, const unsigned int &dof)
Returns true if degree of freedom is free.
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.