47 namespace fs = std::filesystem;
48 fs::path n = fs::absolute(dir).lexically_normal();
49 std::string s = n.string();
50 if (!s.empty() && s.back() !=
'/' && s.back() !=
'\\')
51 s += fs::path::preferred_separator;
57 if (
data.d_pdDofMpi) {
58 if (i >=
data.d_pdNodePartition.size())
60 return static_cast<int>(
data.d_pdNodePartition[i]) == rank;
62 for (
const auto *p :
data.d_particlesListTypeAll) {
63 const size_t i0 = p->d_globStart;
64 const size_t i1 = i0 + p->getNumNodes();
65 if (i < i0 || i >= i1)
75 const std::filesystem::path &out_dir) {
76 const size_t n =
data.d_u.size();
77 std::vector<double> buf(6 * n, 0.);
78 for (
size_t i = 0; i < n; ++i) {
81 buf[6 * i + 0] =
data.d_u[i].d_x;
82 buf[6 * i + 1] =
data.d_u[i].d_y;
83 buf[6 * i + 2] =
data.d_u[i].d_z;
84 buf[6 * i + 3] =
data.d_v[i].d_x;
85 buf[6 * i + 4] =
data.d_v[i].d_y;
86 buf[6 * i + 5] =
data.d_v[i].d_z;
89 MPI_Allreduce(MPI_IN_PLACE, buf.data(),
static_cast<int>(buf.size()),
94 std::filesystem::create_directories(out_dir /
"nodal");
96 std::ofstream xr(out_dir /
"nodal" /
"x_ref.bin", std::ios::binary);
97 const uint32_t nn =
static_cast<uint32_t
>(n);
98 xr.write(
reinterpret_cast<const char *
>(&nn),
sizeof(nn));
99 for (
size_t i = 0; i < n; ++i) {
100 const double p[3] = {
data.d_xRef[i].d_x,
data.d_xRef[i].d_y,
102 xr.write(
reinterpret_cast<const char *
>(p),
sizeof(p));
106 std::ofstream os(out_dir /
"nodal" /
"final_uv.bin", std::ios::binary);
107 const char magic[4] = {
'P',
'D',
'U',
'V'};
108 const uint32_t step32 =
static_cast<uint32_t
>(
data.currentStep());
109 const uint32_t nn =
static_cast<uint32_t
>(n);
111 os.write(
reinterpret_cast<const char *
>(&step32),
sizeof(step32));
112 os.write(
reinterpret_cast<const char *
>(&nn),
sizeof(nn));
113 os.write(
reinterpret_cast<const char *
>(buf.data()),
114 static_cast<std::streamsize
>(buf.size() *
sizeof(double)));
123 if (
data.d_particlesListTypeParticle.size() < 2)
125 const auto *g0 =
data.d_particlesListTypeParticle[0];
126 const auto *g1 =
data.d_particlesListTypeParticle[1];
129 Rc =
data.d_particleDeck_p->d_contactDeck.getContact(0, 0).d_contactR;
134 const double gap = g0->getXCenter().dist(g1->getXCenter()) -
135 g0->d_geom_p->boundingRadius() -
136 g1->d_geom_p->boundingRadius();
137 d_minPpGap = std::min(d_minPpGap, gap);
141 double min_y = 1.e300;
142 for (
size_t i = 0; i <
data.d_x.size(); ++i) {
143 if (
data.getParticleFromAllList(
data.d_ptId[i])->isWall())
145 min_y = std::min(min_y,
data.d_x[i].d_y);
148 MPI_Allreduce(MPI_IN_PLACE, &min_y, 1, MPI_DOUBLE, MPI_MIN,
150 d_minGrainY = std::min(d_minGrainY, min_y);
156 int saw_pp = d_sawPp ? 1 : 0;
157 int saw_wall = d_sawWall ? 1 : 0;
158 double min_pp = d_minPpGap;
159 double min_y = d_minGrainY;
162 int saw_pp_g = 0, saw_wall_g = 0;
163 double min_pp_g = 0., min_y_g = 0., rc_g = 0.;
164 MPI_Allreduce(&saw_pp, &saw_pp_g, 1, MPI_INT, MPI_MAX,
166 MPI_Allreduce(&saw_wall, &saw_wall_g, 1, MPI_INT, MPI_MAX,
168 MPI_Allreduce(&min_pp, &min_pp_g, 1, MPI_DOUBLE, MPI_MIN,
170 MPI_Allreduce(&min_y, &min_y_g, 1, MPI_DOUBLE, MPI_MIN,
172 MPI_Allreduce(&rc, &rc_g, 1, MPI_DOUBLE, MPI_MAX,
175 saw_wall = saw_wall_g;
181 throw std::runtime_error(std::format(
182 "particle-particle contact missing: min_gap={:.6e} Rc={:.6e}", min_pp,
185 throw std::runtime_error(std::format(
186 "particle-wall contact missing: min_grain_y={:.6e} Rc={:.6e}", min_y,
190 "contacts OK: min_pp_gap={:.6e} min_grain_y={:.6e} Rc={:.6e}\n",
195 bool d_sawPp =
false;
196 bool d_sawWall =
false;
197 double d_minPpGap = 1.e300;
198 double d_minGrainY = 1.e300;
203 const std::filesystem::path &mesh_cir,
204 const std::filesystem::path &mesh_wall,
205 const std::string &mpi_strategy,
double final_time,
207 const double R = 0.001;
208 const double mesh_size = R / 4.0;
209 const double horizon = 3.0 * mesh_size;
210 const double h_est = 0.7 * mesh_size;
211 const double Rc_est = 0.95 * h_est;
214 const double gap_pp = 0.5 * Rc_est;
215 const double cy = R + 0.35 * Rc_est;
216 const double cx0 = -(R + 0.5 * gap_pp);
217 const double cx1 = +(R + 0.5 * gap_pp);
219 const double wall_half = 3.5 * R;
220 const double wall_thick = std::max(2.0 * mesh_size, horizon);
227 floor.
d_geomParams = {-wall_half, -wall_thick, 0., wall_half, 0., 0.};
229 const double poisson = 0.25;
230 const double rho = 1200.0;
231 const double K = 2.16e7;
234 const double Gc = 50.0;
239 json{{
"Dimension", 2},
240 {
"Final_Time", final_time},
241 {
"Time_Steps", num_steps},
242 {
"Discretization_Type",
json{{
"Spatial",
"finite_difference"}, {
"Time",
"central_difference"}}},
243 {
"Populate_ElementNodeConnectivity",
true},
244 {
"Quad_Approximation_Order", 2},
245 {
"Particle_Sim_Type",
"Multi_Particle"},
247 model[
"MPI_Strategy"] = mpi_strategy;
248 model[
"Wall_Contact"] =
"meshed";
251 json{{
"File_Format",
"vtu"},
252 {
"Path", output_path},
253 {
"Tags", std::vector<std::string>({
"Displacement",
"Velocity",
"Force",
255 {
"Output_Interval", std::max<size_t>(1, num_steps)},
257 {
"Perform_FE_Out",
false},
258 {
"Compress_Type",
"zlib"},
259 {
"Perform_Out",
false},
260 {
"Test_Output_Interval", num_steps},
262 {
"PVD_Collection",
false}});
266 json{{
"Displacement_BC_Sets", 1},
269 bc[
"Displacement_BC"][
"Set_1"] =
json{
270 {
"Particle_List", std::vector<size_t>{2}},
271 {
"Direction", std::vector<size_t>{1, 2}},
273 {{
"Type",
"constant"}, {
"Parameters", std::vector<double>{0.}}}},
274 {
"Spatial_Function", {{
"Type",
"constant"}}},
275 {
"Zero_Displacement",
true}};
278 {
"Particle_List", {0, 1}},
279 {
"IC_Type",
"Constant_Velocity"},
280 {
"IC_Vector", {0., -0.02, 0.}}});
283 auto mesh_set = [&](
const std::filesystem::path &f) {
284 return json{{
"File", f.string()},
287 {
"Info",
"gmsh_builtin_mesh"},
288 {
"Mesh_Size", mesh_size},
289 {
"Write_Mesh_File",
true}}}};
292 {
"Set_1", mesh_set(mesh_cir)},
293 {
"Set_2", mesh_set(mesh_wall)}};
297 json{{
"Type",
"PDState"},
298 {
"Is_Plane_Strain",
false},
299 {
"Horizon", horizon},
304 {
"Compute_From_Classical",
true},
305 {
"Influence_Function",
json{{
"Type", 0}}}});
307 json{{
"Type",
"PDState"},
308 {
"Is_Plane_Strain",
false},
309 {
"Horizon", horizon},
314 {
"Compute_From_Classical",
true},
315 {
"Influence_Function",
json{{
"Type", 0}}}});
318 json{{
"Contact_Radius_Factor", 0.95},
320 {
"Damping_On",
false},
322 {
"Friction_On",
false},
323 {
"Friction_Coeff", 0.0},
325 {
"Beta_n_Factor", 100.0},
328 contact_base[
"Kn_Factor"] = 1.0e-4;
330 contact[
"Set_1_1"] = contact_base;
331 contact[
"Set_1_2"] = contact_base;
332 contact[
"Set_2_2"] = contact_base;
333 contact[
"Damping_Law"] =
"off";
334 contact[
"Friction_Law"] =
"coulomb_simple";
337 pgen[
"Random_Rotation"] =
false;
338 pgen[
"Data"][
"N"] = 3;
339 pgen[
"Data"][
"0"] =
json{{
"x", cx0},
347 pgen[
"Data"][
"1"] =
json{{
"x", cx1},
355 pgen[
"Data"][
"2"] =
json{{
"x", 0.},
356 {
"y", -0.5 * wall_thick},
366 "mpi_identity_twop_wall: R={:.4g} mesh={:.4g} Rc~{:.4g} gap_pp={:.4g} "
367 "cy={:.4g} T={:.3g} N={}\n",
368 R, mesh_size, Rc_est, gap_pp, cy, final_time, num_steps));
370 return json{{
"Model", model},
372 {
"Force_BC", bc[
"Force_BC"]},
373 {
"Displacement_BC", bc[
"Displacement_BC"]},
381 json{{
"Update_Criteria",
"simple_all"},
382 {
"Search_Factor", 5.0},
383 {
"Search_Interval", 1},
384 {
"Near_Bd_Nodes_Tol", 0.5}})},
385 {
"Particle_Generation", pgen}};
390int main(
int argc,
char *argv[]) {
394 unsigned n_threads = 1;
396 n_threads =
static_cast<unsigned>(std::stoi(input.
getCmdOption(
"-nThreads")));
399 std::string mpi_strategy =
"none";
403 namespace fs = std::filesystem;
404 fs::path out_dir = fs::current_path() /
"out";
405 fs::path inp_dir = fs::current_path() /
"inp";
410 fs::create_directories(out_dir);
411 fs::create_directories(inp_dir);
414 const double final_time = 2.0e-4;
415 const size_t num_steps = 1000;
419 inp_dir /
"mesh_cir.msh", inp_dir /
"mesh_wall.msh",
420 mpi_strategy, final_time, num_steps);
422 std::ofstream os(inp_dir /
"input.json");
423 os << input_json.dump(2);
426 auto deck = std::make_shared<inp::Input>(input_json);
428 auto witness = std::make_unique<ContactWitness>();
429 ContactWitness *witness_p = witness.get();
433 witness_p->assertOk();
438 util::io::print(std::format(
"mpi_identity_twop_wall done (strategy={})\n",
int main(int argc, char *argv[])
void setPostprocess(std::unique_ptr< postprocess::Postprocess > p)
void checkStop(data::ModelData &data) override
A class to store model data.
Extra postprocessing and stop criteria (VTU writing is in rw::).
virtual void checkStop(data::ModelData &data)
nlohmann::ordered_json json
std::string directoryPathWithTrailingSep(const std::filesystem::path &dir)
bool ownsNode(const data::ModelData &data, size_t i)
void dumpFinalNodal(const data::ModelData &data, const std::filesystem::path &out_dir)
json buildInputJson(const std::string &output_path, const std::filesystem::path &mesh_cir, const std::filesystem::path &mesh_wall, const std::string &mpi_strategy, double final_time, size_t num_steps)
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.
Collection of methods and data related to finite element and mesh.
Collection of methods and data related to particle object.
bool isLocallyOwned(const BaseParticle &p)
True if this rank updates / assembles forces for the particle. Walls are replicated on every rank....
void print(const T &msg, int nt=print_default_tab, int printMpiRank=print_default_mpi_rank)
Prints formatted information.
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.
double normalContactStiffness(const double &K1, const double &K2, const double &horizon, int horizonPower=5)
Silling normal contact stiffness from two bulk moduli and the horizon.
Input data for geometrical objects.
std::vector< double > d_geomParams
Zone parameters.
std::string d_geomName
Zone type.
static json getExampleJson(const json &given=json::object(), const geom::GeomData *region=nullptr)
Returns example JSON object for ModelDeck configuration.
static json getExampleJson(const json &given=json::object())
Returns example JSON object for ModelDeck configuration.
static json getExampleJson(const json &given=json::object())
Returns example JSON object for ModelDeck configuration.
static json getExampleJson(const json &given=json::object())
Returns the block with the given fields set.
static json getExampleJson(const std::string &)=delete
Returns example JSON object for ModelDeck configuration.
static json getExampleJson(const json &given=json::object())
Returns the block with the given fields set.
static json getExampleJson(const json &given=json::object())
Returns the block with the given fields set.
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.
std::vector< double > toVec() const
Returns coordinate in stl form.