509 {
510
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();
521
522
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
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
594
595 if (input.cmdOptionExists("-deckOnly")) {
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;
607 }
608
609 auto deck = std::make_shared<inp::Input>(inputJson);
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
618
619 int max_pairs = probe_p->maxPairs();
620 double min_gap = probe_p->minGap();
621 double gap0 = probe_p->gap0();
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;
634 }
635
636 const float zmax =
637 dem.d_Z.empty() ? 0.f : *std::max_element(dem.d_Z.begin(), dem.d_Z.end());
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
659 int force_ok = 1;
661 const fs::path csv = out_dir / "pp_compressive_test_0.csv";
663 util::io::print(std::format(
"assertForce: max |plate reaction| = {} from {}\n", fmax,
664 csv.string()));
665 if (fmax <= 0.) {
667 force_ok = 0;
668 }
669 }
672 if (!force_ok) {
674 return EXIT_FAILURE;
675 }
676 }
677
678
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 }
690 MPI_Allreduce(MPI_IN_PLACE, &max_u, 1, MPI_DOUBLE, MPI_MAX,
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}
std::string directoryPathWithTrailingSep(const std::filesystem::path &dir)
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")
std::vector< PackedParticle > generateCircularGrid(int ncols, int nrows, double x_lo, double y_lo, double R, double gap)
double maxAbsForceFromCsv(const std::filesystem::path &csv)
void writeLocations(const std::filesystem::path &csv, const std::vector< PackedParticle > &packed)
void print(const T &msg, int nt=print_default_tab, int printMpiRank=print_default_mpi_rank)
Prints formatted information.
T max(const std::vector< T > &data)
Returns the maximum from list of data.
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.