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< PackedParticle > anonymous_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 509 of file main.cpp.

509 {
510
511 util::parallel::initMpi(argc, argv);
512 util::io::InputParser input(argc, argv);
513
514 unsigned int nThreads;
515 if (input.cmdOptionExists("-nThreads"))
516 nThreads = std::stoi(input.getCmdOption("-nThreads"));
517 else
518 nThreads = std::thread::hardware_concurrency();
520 util::io::print(std::format("Number of threads = {}\n", util::parallel::getNThreads()));
521
522 // Close ~1.08 Rc at vy=-0.06, then squeeze. 0.004 s × 0.06 m/s = 0.24 mm.
523 double final_time = 0.004;
524 size_t num_steps = 20000;
525 if (input.cmdOptionExists("-finalTime"))
526 final_time = std::stod(input.getCmdOption("-finalTime"));
527 if (input.cmdOptionExists("-numSteps"))
528 num_steps = std::stoul(input.getCmdOption("-numSteps"));
529 size_t search_interval = 40;
530 if (input.cmdOptionExists("-searchInterval"))
531 search_interval = std::stoul(input.getCmdOption("-searchInterval"));
532 int ncols = 4;
533 int nrows = 3;
534 if (input.cmdOptionExists("-nCols"))
535 ncols = std::stoi(input.getCmdOption("-nCols"));
536 if (input.cmdOptionExists("-nRows"))
537 nrows = std::stoi(input.getCmdOption("-nRows"));
538 std::string mpi_strategy = "auto";
539 if (input.cmdOptionExists("-mpiStrategy"))
540 mpi_strategy = input.getCmdOption("-mpiStrategy");
541 util::io::print(std::format("MPI_Strategy = {}, pack = {}x{} ({} grains)\n",
542 mpi_strategy, ncols, nrows, ncols * nrows));
543
544 namespace fs = std::filesystem;
545 const fs::path cwd = fs::current_path();
546 fs::path out_dir = cwd / "out";
547 fs::path inp_dir = cwd / "inp";
548 if (input.cmdOptionExists("-outputDir")) {
549 fs::path p = input.getCmdOption("-outputDir");
550 out_dir = p.is_absolute() ? std::move(p) : cwd / p;
551 }
552 if (input.cmdOptionExists("-inputDir")) {
553 fs::path p = input.getCmdOption("-inputDir");
554 inp_dir = p.is_absolute() ? std::move(p) : cwd / p;
555 } else if (input.cmdOptionExists("-outputDir"))
556 inp_dir = out_dir.parent_path() / "inp";
557
558 fs::create_directories(out_dir);
559 fs::create_directories(inp_dir);
560
561 const fs::path mesh_cir = inp_dir / "mesh_cir.msh";
562 const fs::path mesh_fixed = inp_dir / "mesh_fixed_container.msh";
563 const fs::path mesh_moving = inp_dir / "mesh_moving_container.msh";
564 const bool file_mesh = input.cmdOptionExists("-fileMesh");
565 const bool inbuilt = !file_mesh;
566 const bool write_meshes = input.cmdOptionExists("-writeMeshes") || inbuilt;
567 if (file_mesh) {
568#ifndef JHA2021_COMP_MESH_DIR
569 throw std::runtime_error("JHA2021_COMP_MESH_DIR is not set.");
570#else
571 const fs::path src(JHA2021_COMP_MESH_DIR);
572 for (const char *name :
573 {"mesh_cir.msh", "mesh_fixed_container.msh", "mesh_moving_container.msh"}) {
574 const fs::path from = src / name;
575 if (!fs::exists(from))
576 throw std::runtime_error("Missing frozen mesh " + from.string());
577 fs::copy_file(from, inp_dir / name, fs::copy_options::overwrite_existing);
578 }
579 util::io::print(std::format("Using frozen meshes from {}\n", src.string()));
580#endif
581 }
582
583 const std::string output_path_for_deck = directoryPathWithTrailingSep(out_dir);
584 auto inputJson = buildInputJson(output_path_for_deck, mesh_cir, mesh_fixed, mesh_moving,
585 final_time, num_steps, file_mesh, write_meshes,
586 search_interval, ncols, nrows, mpi_strategy);
587
588 {
589 std::ofstream os(inp_dir / "input.json");
590 os << inputJson.dump(2);
591 }
592
593 // -deckOnly stops after the deck is written. The Python comparison reads
594 // the deck and does not need this driver to run the simulation.
595 if (input.cmdOptionExists("-deckOnly")) {
596 util::io::print("deck written; -deckOnly, not running\n");
598 return 0;
599 }
600
601 {
602 const double R = 0.001;
603 const double mesh_size = R / 5.0;
604 const double pad = 1.15 * 0.95 * 0.7 * mesh_size;
605 writeLocations(inp_dir / "particle_locations.csv",
606 generateCircularGrid(ncols, nrows, 0.0, 0.0, R, pad));
607 }
608
609 auto deck = std::make_shared<inp::Input>(inputJson);
610 PeriDEMModel dem(deck);
611 const size_t ts_every = std::max<size_t>(1, num_steps / 100);
612 auto probe = std::make_unique<GrainContactProbe>(out_dir, ts_every);
613 GrainContactProbe *probe_p = probe.get();
614 dem.setPostprocess(std::move(probe));
615 dem.run(deck);
616
617 // Under particle-MPI, ranks only refresh ghost centers; far grains stay stale.
618 // Reduce contact probe metrics so requireContact is rank-consistent.
619 int max_pairs = probe_p->maxPairs();
620 double min_gap = probe_p->minGap();
621 double gap0 = probe_p->gap0();
622 if (util::parallel::mpiSize() > 1) {
623 int max_pairs_g = 0;
624 double min_gap_g = 0., gap0_g = 0.;
625 MPI_Allreduce(&max_pairs, &max_pairs_g, 1, MPI_INT, MPI_MAX,
627 MPI_Allreduce(&min_gap, &min_gap_g, 1, MPI_DOUBLE, MPI_MIN,
629 MPI_Allreduce(&gap0, &gap0_g, 1, MPI_DOUBLE, MPI_MAX,
631 max_pairs = max_pairs_g;
632 min_gap = min_gap_g;
633 gap0 = gap0_g;
634 }
635
636 const float zmax =
637 dem.d_Z.empty() ? 0.f : *std::max_element(dem.d_Z.begin(), dem.d_Z.end());
638 util::io::print(std::format(
639 "grain contact: gap0={}, min_gap={} (t={}), Rc={}, pairs_in_Rc={}, max Damage_Z={}\n",
640 gap0, min_gap, probe_p->tMin(), probe_p->Rc(), max_pairs, zmax));
641
642 const bool require_contact = !input.cmdOptionExists("-noRequireContact");
643 if (require_contact) {
644 const bool grains_touched = max_pairs > 0 && min_gap < gap0 - 1.0e-8;
645 if (!grains_touched) {
646 util::io::print("requireContact: no grain–grain pair entered the contact radius.\n");
648 return EXIT_FAILURE;
649 }
650 if (!(gap0 > probe_p->Rc())) {
651 util::io::print("requireContact: grains already in Rc at t=0; packing is too tight.\n");
653 return EXIT_FAILURE;
654 }
655 }
656
657 if (input.cmdOptionExists("-assertForce")) {
658 // Rank 0 owns the reaction CSV; broadcast pass/fail.
659 int force_ok = 1;
660 if (util::parallel::mpiRank() == 0) {
661 const fs::path csv = out_dir / "pp_compressive_test_0.csv";
662 const double fmax = maxAbsForceFromCsv(csv);
663 util::io::print(std::format("assertForce: max |plate reaction| = {} from {}\n", fmax,
664 csv.string()));
665 if (fmax <= 0.) {
666 util::io::print("assertForce: plate reaction is zero.\n");
667 force_ok = 0;
668 }
669 }
670 if (util::parallel::mpiSize() > 1)
671 MPI_Bcast(&force_ok, 1, MPI_INT, 0, util::parallel::mpiComm());
672 if (!force_ok) {
674 return EXIT_FAILURE;
675 }
676 }
677
678 // Metric for serial vs MPI checks (rank 0). Allreduce max|u| for DOF-MPI.
679 {
680 double max_u = 0.;
681 for (const auto &u : dem.d_u)
682 max_u = std::max(max_u, u.length());
683 double com0x = 0., com0y = 0.;
684 if (!dem.d_particlesListTypeParticle.empty()) {
685 const auto c = dem.d_particlesListTypeParticle[0]->getXCenter();
686 com0x = c.d_x;
687 com0y = c.d_y;
688 }
689 if (util::parallel::mpiSize() > 1)
690 MPI_Allreduce(MPI_IN_PLACE, &max_u, 1, MPI_DOUBLE, MPI_MAX,
692 if (util::parallel::mpiRank() == 0) {
693 std::ofstream os(out_dir / "mpi_metric.txt");
694 os << std::format("{:.12e} {:.12e} {:.12e}\n", max_u, com0x, com0y);
695 util::io::print(std::format("mpi_metric: max|u|={:.12e} grain0_com=({:.12e},{:.12e})\n",
696 max_u, com0x, com0y));
697 }
698 }
699
701 return EXIT_SUCCESS;
702}
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:333
void writeLocations(const std::filesystem::path &csv, const std::vector< PackedParticle > &packed)
Definition main.cpp:325
float R
Definition problem.py:40
void print(const T &msg, int nt=print_default_tab, int printMpiRank=print_default_mpi_rank)
Prints formatted information.
Definition io.h:171
T max(const std::vector< T > &data)
Returns the maximum from list of data.
Definition vecMethods.h:76
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(), main(), 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: