PeriDEM 0.3.0
PeriDEM -- Peridynamics-based high-fidelity model for granular media
Loading...
Searching...
No Matches
main.cpp File Reference
#include "geom/geomObjectsUtil.h"
#include "inp/deckIncludes.h"
#include "inp/input.h"
#include "material/materialUtil.h"
#include "data/modelData.h"
#include "particle/baseParticle.h"
#include "particle/particleMpi.h"
#include "periDEMModel.h"
#include "postprocess/postprocess.h"
#include "util/function.h"
#include "util/io.h"
#include "util/parallelUtil.h"
#include <mpi.h>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <filesystem>
#include <format>
#include <iostream>
#include <fstream>
#include <stdexcept>
#include <string>
#include <thread>
#include <vector>
Include dependency graph for main.cpp:

Go to the source code of this file.

Data Structures

struct  anonymous_namespace{main.cpp}::PackedParticle
 
class  anonymous_namespace{main.cpp}::GrainContactProbe
 

Namespaces

namespace  anonymous_namespace{main.cpp}
 

Functions

std::string anonymous_namespace{main.cpp}::directoryPathWithTrailingSep (const std::filesystem::path &dir)
 
std::vector< PackedParticleanonymous_namespace{main.cpp}::generateCircularGrid (int ncols, int nrows, double x_lo, double y_lo, double R, double gap)
 
json anonymous_namespace{main.cpp}::buildInputJson (const std::string &output_path_for_deck, const std::filesystem::path &mesh_cir, const std::filesystem::path &mesh_fixed, const std::filesystem::path &mesh_moving, double final_time, size_t num_steps, bool file_mesh, bool write_meshes, size_t search_interval, int ncols, int nrows, const std::string &mpi_strategy="auto")
 
void anonymous_namespace{main.cpp}::writeLocations (const std::filesystem::path &csv, const std::vector< PackedParticle > &packed)
 
double anonymous_namespace{main.cpp}::maxAbsForceFromCsv (const std::filesystem::path &csv)
 
int main (int argc, char *argv[])
 

Function Documentation

◆ main()

int main ( int  argc,
char *  argv[] 
)

Definition at line 460 of file main.cpp.

460 {
461
462 util::parallel::initMpi(argc, argv);
463 util::io::InputParser input(argc, argv);
464
465 unsigned int nThreads;
466 if (input.cmdOptionExists("-nThreads"))
467 nThreads = std::stoi(input.getCmdOption("-nThreads"));
468 else
469 nThreads = std::thread::hardware_concurrency();
471 util::io::print(std::format("Number of threads = {}\n", util::parallel::getNThreads()));
472
473 // Close ~1.08 Rc at vy=-0.06, then squeeze. 0.004 s × 0.06 m/s = 0.24 mm.
474 double final_time = 0.004;
475 size_t num_steps = 20000;
476 if (input.cmdOptionExists("-finalTime"))
477 final_time = std::stod(input.getCmdOption("-finalTime"));
478 if (input.cmdOptionExists("-numSteps"))
479 num_steps = std::stoul(input.getCmdOption("-numSteps"));
480 size_t search_interval = 40;
481 if (input.cmdOptionExists("-searchInterval"))
482 search_interval = std::stoul(input.getCmdOption("-searchInterval"));
483 int ncols = 4;
484 int nrows = 3;
485 if (input.cmdOptionExists("-nCols"))
486 ncols = std::stoi(input.getCmdOption("-nCols"));
487 if (input.cmdOptionExists("-nRows"))
488 nrows = std::stoi(input.getCmdOption("-nRows"));
489 std::string mpi_strategy = "auto";
490 if (input.cmdOptionExists("-mpiStrategy"))
491 mpi_strategy = input.getCmdOption("-mpiStrategy");
492 util::io::print(std::format("MPI_Strategy = {}, pack = {}x{} ({} grains)\n",
493 mpi_strategy, ncols, nrows, ncols * nrows));
494
495 namespace fs = std::filesystem;
496 const fs::path cwd = fs::current_path();
497 fs::path out_dir = cwd / "out";
498 fs::path inp_dir = cwd / "inp";
499 if (input.cmdOptionExists("-outputDir")) {
500 fs::path p = input.getCmdOption("-outputDir");
501 out_dir = p.is_absolute() ? std::move(p) : cwd / p;
502 }
503 if (input.cmdOptionExists("-inputDir")) {
504 fs::path p = input.getCmdOption("-inputDir");
505 inp_dir = p.is_absolute() ? std::move(p) : cwd / p;
506 } else if (input.cmdOptionExists("-outputDir"))
507 inp_dir = out_dir.parent_path() / "inp";
508
509 fs::create_directories(out_dir);
510 fs::create_directories(inp_dir);
511
512 const fs::path mesh_cir = inp_dir / "mesh_cir.msh";
513 const fs::path mesh_fixed = inp_dir / "mesh_fixed_container.msh";
514 const fs::path mesh_moving = inp_dir / "mesh_moving_container.msh";
515 const bool file_mesh = input.cmdOptionExists("-fileMesh");
516 const bool inbuilt = !file_mesh;
517 const bool write_meshes = input.cmdOptionExists("-writeMeshes") || inbuilt;
518 if (file_mesh) {
519#ifndef JHA2021_COMP_MESH_DIR
520 throw std::runtime_error("JHA2021_COMP_MESH_DIR is not set.");
521#else
522 const fs::path src(JHA2021_COMP_MESH_DIR);
523 for (const char *name :
524 {"mesh_cir.msh", "mesh_fixed_container.msh", "mesh_moving_container.msh"}) {
525 const fs::path from = src / name;
526 if (!fs::exists(from))
527 throw std::runtime_error("Missing frozen mesh " + from.string());
528 fs::copy_file(from, inp_dir / name, fs::copy_options::overwrite_existing);
529 }
530 util::io::print(std::format("Using frozen meshes from {}\n", src.string()));
531#endif
532 }
533
534 const std::string output_path_for_deck = directoryPathWithTrailingSep(out_dir);
535 auto inputJson = buildInputJson(output_path_for_deck, mesh_cir, mesh_fixed, mesh_moving,
536 final_time, num_steps, file_mesh, write_meshes,
537 search_interval, ncols, nrows, mpi_strategy);
538
539 {
540 std::ofstream os(inp_dir / "input.json");
541 os << inputJson.dump(2);
542 }
543
544 {
545 const double R = 0.001;
546 const double mesh_size = R / 5.0;
547 const double pad = 1.15 * 0.95 * 0.7 * mesh_size;
548 writeLocations(inp_dir / "particle_locations.csv",
549 generateCircularGrid(ncols, nrows, 0.0, 0.0, R, pad));
550 }
551
552 auto deck = std::make_shared<inp::Input>(inputJson);
553 PeriDEMModel dem(deck);
554 const size_t ts_every = std::max<size_t>(1, num_steps / 100);
555 auto probe = std::make_unique<GrainContactProbe>(out_dir, ts_every);
556 GrainContactProbe *probe_p = probe.get();
557 dem.setPostprocess(std::move(probe));
558 dem.run(deck);
559
560 // Under particle-MPI, ranks only refresh ghost centers; far grains stay stale.
561 // Reduce contact probe metrics so requireContact is rank-consistent.
562 int max_pairs = probe_p->maxPairs();
563 double min_gap = probe_p->minGap();
564 double gap0 = probe_p->gap0();
565 if (util::parallel::mpiSize() > 1) {
566 int max_pairs_g = 0;
567 double min_gap_g = 0., gap0_g = 0.;
568 MPI_Allreduce(&max_pairs, &max_pairs_g, 1, MPI_INT, MPI_MAX,
570 MPI_Allreduce(&min_gap, &min_gap_g, 1, MPI_DOUBLE, MPI_MIN,
572 MPI_Allreduce(&gap0, &gap0_g, 1, MPI_DOUBLE, MPI_MAX,
574 max_pairs = max_pairs_g;
575 min_gap = min_gap_g;
576 gap0 = gap0_g;
577 }
578
579 const float zmax =
580 dem.d_Z.empty() ? 0.f : *std::max_element(dem.d_Z.begin(), dem.d_Z.end());
581 util::io::print(std::format(
582 "grain contact: gap0={}, min_gap={} (t={}), Rc={}, pairs_in_Rc={}, max Damage_Z={}\n",
583 gap0, min_gap, probe_p->tMin(), probe_p->Rc(), max_pairs, zmax));
584
585 const bool require_contact = !input.cmdOptionExists("-noRequireContact");
586 if (require_contact) {
587 const bool grains_touched = max_pairs > 0 && min_gap < gap0 - 1.0e-8;
588 if (!grains_touched) {
589 util::io::print("requireContact: no grain–grain pair entered the contact radius.\n");
591 return EXIT_FAILURE;
592 }
593 if (!(gap0 > probe_p->Rc())) {
594 util::io::print("requireContact: grains already in Rc at t=0; packing is too tight.\n");
596 return EXIT_FAILURE;
597 }
598 }
599
600 if (input.cmdOptionExists("-assertForce")) {
601 // Rank 0 owns the reaction CSV; broadcast pass/fail.
602 int force_ok = 1;
603 if (util::parallel::mpiRank() == 0) {
604 const fs::path csv = out_dir / "pp_compressive_test_0.csv";
605 const double fmax = maxAbsForceFromCsv(csv);
606 util::io::print(std::format("assertForce: max |plate reaction| = {} from {}\n", fmax,
607 csv.string()));
608 if (fmax <= 0.) {
609 util::io::print("assertForce: plate reaction is zero.\n");
610 force_ok = 0;
611 }
612 }
613 if (util::parallel::mpiSize() > 1)
614 MPI_Bcast(&force_ok, 1, MPI_INT, 0, util::parallel::mpiComm());
615 if (!force_ok) {
617 return EXIT_FAILURE;
618 }
619 }
620
621 // Metric for serial vs MPI checks (rank 0). Allreduce max|u| for DOF-MPI.
622 {
623 double max_u = 0.;
624 for (const auto &u : dem.d_u)
625 max_u = std::max(max_u, u.length());
626 double com0x = 0., com0y = 0.;
627 if (!dem.d_particlesListTypeParticle.empty()) {
628 const auto c = dem.d_particlesListTypeParticle[0]->getXCenter();
629 com0x = c.d_x;
630 com0y = c.d_y;
631 }
632 if (util::parallel::mpiSize() > 1)
633 MPI_Allreduce(MPI_IN_PLACE, &max_u, 1, MPI_DOUBLE, MPI_MAX,
635 if (util::parallel::mpiRank() == 0) {
636 std::ofstream os(out_dir / "mpi_metric.txt");
637 os << std::format("{:.12e} {:.12e} {:.12e}\n", max_u, com0x, com0y);
638 util::io::print(std::format("mpi_metric: max|u|={:.12e} grain0_com=({:.12e},{:.12e})\n",
639 max_u, com0x, com0y));
640 }
641 }
642
644 return EXIT_SUCCESS;
645}
Input command line argument parser.
Definition inputParser.h:28
std::string directoryPathWithTrailingSep(const std::filesystem::path &dir)
Definition main.cpp:34
json buildInputJson(const std::string &output_path_for_deck, const std::filesystem::path &mesh_cir, const std::filesystem::path &mesh_fixed, const std::filesystem::path &mesh_moving, double final_time, size_t num_steps, bool file_mesh, bool write_meshes, size_t search_interval, int ncols, int nrows, const std::string &mpi_strategy="auto")
Definition main.cpp:83
std::vector< PackedParticle > generateCircularGrid(int ncols, int nrows, double x_lo, double y_lo, double R, double gap)
Definition main.cpp:61
double maxAbsForceFromCsv(const std::filesystem::path &csv)
Definition main.cpp:284
void writeLocations(const std::filesystem::path &csv, const std::vector< PackedParticle > &packed)
Definition main.cpp:276
void print(const T &msg, int nt=print_default_tab, int printMpiRank=print_default_mpi_rank)
Prints formatted information.
Definition io.h:128
T max(const std::vector< T > &data)
Returns the maximum from list of data.
Definition vecMethods.h:74
unsigned int getNThreads()
Get number of threads to be used by taskflow.
void initNThreads(unsigned int nThreads=std::thread::hardware_concurrency())
Initializes MpiStatus struct.
void initMpi(int argc=0, char *argv[]=nullptr)
Initializes MPI and also creates MpiStatus struct.
int mpiSize()
Get size (number) of processors.
int mpiRank()
get rank (id) of this processor
void finalizeMpi()
Call MPI_Finalize if this process initialized MPI.
MPI_Comm mpiComm()
Get MPI comm.

References anonymous_namespace{main.cpp}::buildInputJson(), util::io::InputParser::cmdOptionExists(), data::ModelData::d_particlesListTypeParticle, data::ModelData::d_u, data::ModelData::d_Z, util::parallel::finalizeMpi(), anonymous_namespace{main.cpp}::GrainContactProbe::gap0(), anonymous_namespace{main.cpp}::generateCircularGrid(), util::io::InputParser::getCmdOption(), util::parallel::getNThreads(), util::parallel::initMpi(), util::parallel::initNThreads(), anonymous_namespace{main.cpp}::maxAbsForceFromCsv(), util::parallel::mpiComm(), util::parallel::mpiRank(), util::parallel::mpiSize(), util::io::print(), PeriDEMModel::run(), PeriDEMModel::setPostprocess(), and anonymous_namespace{main.cpp}::writeLocations().

Here is the call graph for this function: