PeriDEM 0.3.0
PeriDEM -- Peridynamics-based high-fidelity model for granular media
Loading...
Searching...
No Matches
main.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 * Two-particle regression test: one deck stack for every geometry; only geom name,
8 * model dimension (2 vs 3), and particle placement differ.
9 *
10 * Reference meshes are always defined at the origin (same canonical shape for mesh
11 * groups 0 and 1). Particle positions are set only in Particle_Generation — do not
12 * bake translation into the second particle's geomParams (that broke the general
13 * path vs twop_circ_inbuilt).
14 *
15 * Layout (run_base defaults to the current working directory):
16 * <run_base>/<geom_name>/inp/ — input.json, meshes
17 * <run_base>/<geom_name>/out/ — VTU, log.txt (Output.Path must end with a separator)
18 *
19 * -outputDir <path> optional parent for all runs (each geometry still gets
20 * its own <geom>/inp and <geom>/out under this path).
21 * -inputDir <path> optional parent for inp trees only (default: same as -outputDir base).
22 * -allGeometries run every geometry in geom::acceptable_geometries (overrides -geometry).
23 * -geometry <name> run a single geometry (must be in acceptable_geometries). If omitted,
24 * default is circle (unless -allGeometries is set).
25 */
26
28#include "inp/deckIncludes.h"
29#include "util/io.h"
30#include "util/function.h"
32#include "periDEMModel.h"
33#include <cmath>
34#include <filesystem>
35#include <format>
36#include <fstream>
37#include <stdexcept>
38#include <thread>
39#include <vector>
40
41namespace {
42
43std::string directoryPathWithTrailingSep(const std::filesystem::path &dir) {
44 namespace fs = std::filesystem;
45 fs::path n = fs::absolute(dir).lexically_normal();
46 std::string s = n.string();
47 if (!s.empty() && s.back() != '/' && s.back() != '\\')
48 s += fs::path::preferred_separator;
49 return s;
50}
51
52bool is3DGeometry(const std::string &g) {
53 return g == "sphere" || g == "ellipsoid" || g == "cube" || g == "cuboid" || g == "cylinder" ||
54 g == "sphere_minus_sphere" || g == "cuboid_minus_cuboid" || g == "open_cuboid_channel_3d";
55}
56
57bool isAcceptableGeometryName(const std::string &g) {
58 for (const std::string &n : geom::getAcceptableGeometries()) {
59 if (n == g)
60 return true;
61 }
62 return false;
63}
64
65json buildInputJson(const std::string &geomName, const std::string &output_path_for_deck,
66 const std::filesystem::path &mesh_file_1,
67 const std::filesystem::path &mesh_file_2) {
68
69 const double s = 0.001;
70 const double mesh_size = s / 5.0;
71 const double horizon = 3.0 * mesh_size;
72 const double particle_dist = 0.001;
73
74 const double poisson1 = 0.25;
75 const double rho1 = 1200.0;
76 const double K1 = 2.16e+7;
77 const double E1 = material::toE(K1, poisson1);
78 const double G1 = material::toGE(E1, poisson1);
79 const double Gc1 = 50.0;
80
81 const double poisson2 = 0.25;
82 const double rho2 = 1200.0;
83 const double K2 = 2.16e+7;
84 const double E2 = material::toE(K2, poisson2);
85 const double G2 = material::toGE(E2, poisson2);
86 const double Gc2 = 50.0;
87
88 const double R_contact_factor = 0.95;
89 const double Kn_11 = 18.0 * util::harmonicMean(K1, K1) / (M_PI * std::pow(horizon, 5));
90 const double Kn_22 = 18.0 * util::harmonicMean(K2, K2) / (M_PI * std::pow(horizon, 5));
91 const double Kn_12 = 18.0 * util::harmonicMean(K1, K2) / (M_PI * std::pow(horizon, 5));
92 const double beta_n_eps = 0.9;
93 const double friction_coeff = 0.5;
94 const double beta_n_factor = 100.0;
95
96 const int model_dim = is3DGeometry(geomName) ? 3 : 2;
97
98 /* Reference shapes at origin for both mesh groups (same as twop_circ_inbuilt). */
99 const util::Point origin(0.0, 0.0, 0.0);
100 const std::vector<double> gp_ref = geom::exampleGeomParams(geomName, origin, s);
101
102 /* Same integration window and IC gap as twop_circ_inbuilt (stable dt). */
103 const double final_time = 0.0001; // 0.012;
104 const size_t num_steps = 1000; //36000;
105 const size_t dt_out_n = num_steps / 4; // 10;
106
107 auto modelDeckJson = inp::ModelDeck::getExampleJson(static_cast<size_t>(model_dim), final_time, num_steps,
108 "finite_difference", "central_difference",
109 true, 2, "Multi_Particle", 0);
110
111 auto outputDeckJson = inp::OutputDeck::getExampleJson("vtu", output_path_for_deck,
112 std::vector<std::string>({"Displacement", "Velocity", "Force", "Damage_Z", "Damage", "Particle_ID"}),
113 dt_out_n, 2, true, "zlib", true, 1, "", true);
114
115 auto bcDeckJson = inp::BCDeck::getExampleJson(0, 1, 1, true, util::Point(0, -10, 0));
116
117 bcDeckJson["Displacement_BC"]["Set_1"] = inp::BCBaseDeck::getExampleJson("Displacement_BC", false, geom::GeomData(),
118 {0}, {}, "", {}, "", {},
119 {1, 2}, true, "", {});
120
121 const double free_fall_dist = particle_dist - horizon;
122 const double free_fall_vel = -std::sqrt(2.0 * std::abs(-10.0) * std::max(1e-30, free_fall_dist));
123 bcDeckJson["IC"]["Set_1"] = inp::BCBaseDeck::getExampleJson("IC", false, geom::GeomData(),
124 {1}, {}, "", {}, "", {},
125 {}, false, "Constant_Velocity", {0.0, free_fall_vel, 0.0});
126
127 auto pDeckJson = json({});
128
129 std::vector<geom::GeomData> pGeomVec(2);
130 pGeomVec[0].d_geomName = geomName;
131 pGeomVec[0].d_geomParams = gp_ref;
132 pGeomVec[1].d_geomName = geomName;
133 pGeomVec[1].d_geomParams = gp_ref;
134
135 pDeckJson["Particle"] = inp::ParticleDeck::getParticleGeomExampleJson(pGeomVec);
136
137 const std::string f1 = mesh_file_1.string();
138 const std::string f2 = mesh_file_2.string();
139 auto meshSet1 = json({{"File", f1},
140 {"CreateMesh",
141 {{"Flag", true},
142 {"Info", "gmsh_builtin_mesh"},
143 {"Mesh_Size", mesh_size},
144 {"Write_Mesh_File", true}}}});
145 auto meshSet2 = json({{"File", f2},
146 {"CreateMesh",
147 {{"Flag", true},
148 {"Info", "gmsh_builtin_mesh"},
149 {"Mesh_Size", mesh_size},
150 {"Write_Mesh_File", true}}}});
151 pDeckJson["Mesh"] = json({{"Sets", 2}, {"Set_1", meshSet1}, {"Set_2", meshSet2}});
152
154 pMatJson["Set_1"] = inp::MaterialDeck::getExampleJson("PDState", false, horizon,
155 0, rho1, K1, G1, Gc1, true, 1);
156 pMatJson["Set_2"] = inp::MaterialDeck::getExampleJson("PDState", false, horizon,
157 0, rho2, K2, G2, Gc2, true, 1);
158 pDeckJson["Material"] = pMatJson;
159
161 json contact_base = inp::ContactPairDeck::getExampleJson(R_contact_factor,
162 true, false, false,
163 Kn_11, beta_n_eps, friction_coeff, 1.0, beta_n_factor, 1.0, 0.0, 0.0);
164
165 pContactJson["Set_1_1"] = contact_base;
166 pContactJson["Set_1_1"]["Kn"] = Kn_11;
167 pContactJson["Set_1_2"] = contact_base;
168 pContactJson["Set_1_2"]["Kn"] = Kn_12;
169 pContactJson["Set_2_2"] = contact_base;
170 pContactJson["Set_2_2"]["Kn"] = Kn_22;
171 pDeckJson["Contact"] = pContactJson;
172
173 pDeckJson["Neighbor"] = inp::PNeighborDeck::getExampleJson("simple_all", 10.0, 40, 0.5);
174
175 auto pGenJson = inp::PGenDeck::getExampleJson("From_File");
176 pGenJson["Data"]["N"] = 2;
177
178 /*
179 * Placement matches twop_circ: lower particle corner at (s,s[,s]); upper at
180 * (s, 2*s + particle_dist + s, [s]) so vertical gap is particle_dist between
181 * outer extents (same as R1=R2=s in twop_circ).
182 */
183 if (model_dim == 2) {
184 pGenJson["Data"]["0"] = {
185 {"x", s}, {"y", s}, {"z", 0.0},
186 {"theta", 0.0}, {"s", 1.0},
187 {"ax", 0.0}, {"ay", 0.0}, {"az", 1.0},
188 {"geom_id", 0}, {"mat_id", 0}, {"contact_id", 0}
189 };
190 pGenJson["Data"]["1"] = {
191 {"x", s}, {"y", 2.0 * s + particle_dist + s}, {"z", 0.0},
192 {"theta", M_PI}, {"s", 1.0},
193 {"ax", 0.0}, {"ay", 0.0}, {"az", 1.0},
194 {"geom_id", 1}, {"mat_id", 1}, {"contact_id", 1}
195 };
196 } else {
197 pGenJson["Data"]["0"] = {
198 {"x", s}, {"y", s}, {"z", s},
199 {"theta", 0.0}, {"s", 1.0},
200 {"ax", 0.0}, {"ay", 0.0}, {"az", 1.0},
201 {"geom_id", 0}, {"mat_id", 0}, {"contact_id", 0}
202 };
203 pGenJson["Data"]["1"] = {
204 {"x", s}, {"y", 2.0 * s + particle_dist + s}, {"z", s},
205 {"theta", M_PI}, {"s", 1.0},
206 {"ax", 0.0}, {"ay", 0.0}, {"az", 1.0},
207 {"geom_id", 1}, {"mat_id", 1}, {"contact_id", 1}
208 };
209 }
210 pDeckJson["Particle_Generation"] = pGenJson;
211
212 return json({{"Model", modelDeckJson},
213 {"Output", outputDeckJson},
214 {"Force_BC", bcDeckJson["Force_BC"]},
215 {"Displacement_BC", bcDeckJson["Displacement_BC"]},
216 {"IC", bcDeckJson["IC"]},
217 {"Particle", pDeckJson["Particle"]},
218 {"Mesh", pDeckJson["Mesh"]},
219 {"Material", pDeckJson["Material"]},
220 {"Contact", pDeckJson["Contact"]},
221 {"Neighbor", pDeckJson["Neighbor"]},
222 {"Particle_Generation", pDeckJson["Particle_Generation"]}});
223}
224
225} // namespace
226
227int main(int argc, char *argv[]) {
228
229 util::parallel::initMpi(argc, argv);
230 int mpiSize = util::parallel::mpiSize(), mpiRank = util::parallel::mpiRank();
231 util::io::print(std::format("Initialized MPI. MPI size = {}, MPI rank = {}\n", mpiSize, mpiRank));
233
234 util::io::InputParser input(argc, argv);
235
236 unsigned int nThreads;
237 if (input.cmdOptionExists("-nThreads"))
238 nThreads = std::stoi(input.getCmdOption("-nThreads"));
239 else {
240 nThreads = std::thread::hardware_concurrency();
241 util::io::print(std::format("Running test with default number of threads = {}\n", nThreads));
242 }
244 util::io::print(std::format("Number of threads = {}\n", util::parallel::getNThreads()));
245
246 namespace fs = std::filesystem;
247 const fs::path cwd = fs::current_path();
248
249 fs::path run_base = cwd;
250 if (input.cmdOptionExists("-outputDir")) {
251 fs::path p = input.getCmdOption("-outputDir");
252 run_base = p.is_absolute() ? std::move(p) : cwd / p;
253 }
254
255 fs::path inp_base = run_base;
256 if (input.cmdOptionExists("-inputDir")) {
257 fs::path p = input.getCmdOption("-inputDir");
258 inp_base = p.is_absolute() ? std::move(p) : cwd / p;
259 }
260
261 std::vector<std::string> geometriesToRun;
262 if (input.cmdOptionExists("-allGeometries")) {
263 geometriesToRun = geom::getAcceptableGeometries();
264 util::io::print("twop_general_inbuilt: -allGeometries — running full geometry list.\n");
265 } else if (input.cmdOptionExists("-geometry")) {
266 const std::string g = input.getCmdOption("-geometry");
268 throw std::runtime_error(
269 "twop_general_inbuilt: -geometry \"" + g +
270 "\" is not in geom::acceptable_geometries.");
271 geometriesToRun = {g};
272 util::io::print(std::format("twop_general_inbuilt: single geometry = {}.\n", g));
273 } else {
274 geometriesToRun = {"circle"};
275 util::io::print("twop_general_inbuilt: default geometry = circle (use -geometry <name> or -allGeometries).\n");
276 }
277
278 for (const std::string &geomName : geometriesToRun) {
279 util::io::print(std::format("--- twop_general_inbuilt: geometry = {} ---\n", geomName));
280
281 const fs::path out_dir = run_base / geomName / "out";
282 const fs::path inp_dir = inp_base / geomName / "inp";
283 fs::create_directories(out_dir);
284 fs::create_directories(inp_dir);
285
286 const std::string output_path_for_deck = directoryPathWithTrailingSep(out_dir);
287
288 const fs::path mesh1 = inp_dir / (std::string("mesh_gen_1_") + geomName + ".msh");
289 const fs::path mesh2 = inp_dir / (std::string("mesh_gen_2_") + geomName + ".msh");
290
291 auto inputJson = buildInputJson(geomName, output_path_for_deck, mesh1, mesh2);
292
293 const fs::path input_json_path = inp_dir / (std::string("input_") + geomName + ".json");
294 {
295 std::ofstream os(input_json_path);
296 if (!os)
297 throw std::runtime_error("Failed to open " + input_json_path.string() + " for writing.");
298 os << inputJson.dump(2);
299 }
300 util::io::print(std::format("Output directory (VTU, log.txt): {}\n", fs::absolute(out_dir).string()));
301 util::io::print(std::format("Input directory (input.json, meshes): {}\n", fs::absolute(inp_dir).string()));
302 util::io::print(std::format("Wrote deck to {}\n", fs::absolute(input_json_path).string()));
303
304 auto deck = std::make_shared<inp::Input>(inputJson);
305
306 PeriDEMModel dem(deck);
307 dem.run(deck);
308 }
309
310 return EXIT_SUCCESS;
311}
void run(std::shared_ptr< inp::Input > &deck)
Input command line argument parser.
Definition inputParser.h:28
bool cmdOptionExists(const std::string &option) const
Check if argument exists.
Definition inputParser.h:60
const std::string & getCmdOption(const std::string &option) const
Get value of argument specified by key.
Definition inputParser.h:45
nlohmann::ordered_json json
std::string directoryPathWithTrailingSep(const std::filesystem::path &dir)
Definition main.cpp:34
bool is3DGeometry(const std::string &g)
Definition main.cpp:52
json buildInputJson(const std::string &geomName, const std::string &output_path_for_deck, const std::filesystem::path &mesh_file_1, const std::filesystem::path &mesh_file_2)
Definition main.cpp:65
bool isAcceptableGeometryName(const std::string &g)
Definition main.cpp:57
const std::vector< std::string > & getAcceptableGeometries()
Returns list of acceptable geometries for PeriDEM simulation.
std::vector< double > exampleGeomParams(const std::string &geom_type, const util::Point &c, double s)
Canonical parameter vector for examples, mesh generation tests, and demos.
double toGE(double E, double nu)
Compute shear modulus from Young's modulus E and Poisson's ratio nu.
double toE(double K, double nu)
Compute Young's modulus E from Bulk modulus K and Poisson's ratio nu.
void print(const T &msg, int nt=print_default_tab, int printMpiRank=print_default_mpi_rank)
Prints formatted information.
Definition io.h:128
unsigned int getNThreads()
Get number of threads to be used by taskflow.
void initNThreads(unsigned int nThreads=std::thread::hardware_concurrency())
Initializes MpiStatus struct.
const MpiStatus * getMpiStatus()
Returns pointer to 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
double harmonicMean(const double &m1, const double &m2)
Definition function.cpp:131
Input data for geometrical objects.
static json getExampleJson(std::string type="Foce_BC", bool isRegionActive=false, geom::GeomData regionGeomData=geom::GeomData(), std::vector< size_t > pList=std::vector< size_t >(), std::vector< size_t > pNotList=std::vector< size_t >(), std::string timeFnType="", std::vector< double > timeFnParams=std::vector< double >(), std::string spatialFnType="", std::vector< double > spatialFnParams=std::vector< double >(), std::vector< size_t > direction=std::vector< size_t >(), bool isDisplacementZero=false, std::string icType="", std::vector< double > icVec=std::vector< double >())
Returns example JSON object for ModelDeck configuration.
Definition bcBaseDeck.h:157
static json getExampleJson(size_t nForceSets=0, size_t nDispSets=0, size_t nICSets=0, bool gravityActive=false, util::Point gravity=util::Point())
Returns example JSON object for ModelDeck configuration.
Definition bcDeck.h:69
static json getExampleJson(double contactR=0., bool computeContactR=true, bool dampingOn=true, bool frictionOn=true, double Kn=0., double eps=1., double mu=0., double KnFactor=1., double betanFactor=1., double deltaMax=1., double vMax=0., double K=0.)
Returns example JSON object for ModelDeck configuration.
static json getExampleJson(std::string materialType="PDState", bool isPlainStrain=false, double horizon=-1., double horizonMeshRatio=-1., double density=1., double K=0., double G=0., double Gc=0., bool computeParamsFromElastic=true, size_t influenceFnType=0, double E=-1.)
Returns example JSON object for ModelDeck configuration.
static json getExampleJson(size_t dim=2, double tFinal=1.0, size_t Nt=10, std::string spatialDiscretization="finite_difference", std::string timeDiscretization="central_difference", bool populateElementNodeConnectivity=true, size_t quadOrder=2, std::string particleSimType="Multi_Particle", int seed=0)
Returns example JSON object for ModelDeck configuration.
Definition modelDeck.h:164
static json getExampleJson(std::string outFormat="vtu", std::string path="./", std::vector< std::string > outTags={"Displacement"}, size_t outputInterval=1, size_t debug=2, bool performFEOut=true, std::string compressType="zlib", bool performOut=true, size_t dtTestOut=1, std::string tagPPFile="", bool pvdCollection=false)
Returns example JSON object for ModelDeck configuration.
Definition outputDeck.h:144
static json getExampleJson(std::string genMethod="From_File")
Returns example JSON object for ModelDeck configuration.
Definition pGenDeck.h:65
static json getExampleJson(std::string updateCriteria="simple_all", double sFactor=1., size_t neighUpdateInterval=1, double nearBdNodesTol=0.5)
Returns example JSON object for ModelDeck configuration.
static json getParticleContactExampleJson(size_t nSets=0)
Returns example JSON object for ModelDeck configuration.
static json getParticleGeomExampleJson(std::vector< geom::GeomData > pGeomVec=std::vector< geom::GeomData >())
Returns example JSON object for ModelDeck configuration.
static json getParticleMaterialExampleJson(size_t nSets=0)
Returns example JSON object for ModelDeck configuration.
A structure to represent 3d vectors.
Definition point.h:30
int main()
Definition main.cpp:31