36 namespace fs = std::filesystem;
37 fs::path n = fs::absolute(dir).lexically_normal();
38 std::string s = n.string();
39 if (!s.empty() && s.back() !=
'/' && s.back() !=
'\\')
40 s += fs::path::preferred_separator;
45 const std::filesystem::path &mesh_cir,
46 const std::filesystem::path &mesh_wall,
double R,
47 double mesh_size,
double horizon,
double Rc_factor,
48 double Kn,
double final_time,
size_t num_steps,
49 bool policy_combo =
false) {
51 json{{
"Dimension", 2},
52 {
"Final_Time", final_time},
53 {
"Time_Steps", num_steps},
54 {
"Discretization_Type",
json{{
"Spatial",
"finite_difference"}, {
"Time",
"central_difference"}}},
55 {
"Populate_ElementNodeConnectivity",
true},
56 {
"Quad_Approximation_Order", 2},
57 {
"Particle_Sim_Type",
"Multi_Particle"},
59 model[
"Wall_Contact"] =
"analytical_plane";
61 model[
"Self_Contact"] =
"reference_gap";
62 model[
"Bond_Break"] =
"absolute_stretch";
66 json{{
"File_Format",
"vtu"},
67 {
"Path", output_path},
68 {
"Tags", std::vector<std::string>({
"Displacement",
"Force",
"Particle_ID"})},
69 {
"Output_Interval", std::max<size_t>(1, num_steps / 5)},
71 {
"Perform_FE_Out",
false},
72 {
"Compress_Type",
"zlib"},
73 {
"Perform_Out",
false},
74 {
"Test_Output_Interval", num_steps},
76 {
"PVD_Collection",
false}});
79 json{{
"Displacement_BC_Sets", 1}});
80 bc[
"Displacement_BC"][
"Set_1"] =
json{
81 {
"Particle_List", std::vector<size_t>{1}},
82 {
"Direction", std::vector<size_t>{1, 2}},
83 {
"Time_Function", {{
"Type",
"constant"}, {
"Parameters", std::vector<double>{0.}}}},
84 {
"Spatial_Function", {{
"Type",
"constant"}}},
85 {
"Zero_Displacement",
true}};
87 const double wall_half = 3.0 * R;
88 const double wall_thick = mesh_size;
94 floor.
d_geomParams = {-wall_half, -wall_thick, 0., wall_half, 0., 0.};
98 auto mesh_set = [&](
const std::filesystem::path &f) {
99 return json{{
"File", f.string()},
102 {
"Info",
"gmsh_builtin_mesh"},
103 {
"Mesh_Size", mesh_size},
104 {
"Write_Mesh_File",
true}}}};
107 {
"Set_1", mesh_set(mesh_cir)},
108 {
"Set_2", mesh_set(mesh_wall)}};
110 const double rho = 1200.0;
111 const double K = 2.16e7;
112 const double G = 1.296e7;
113 const double Gc = 50.0;
117 json{{
"Type",
"PDState"},
118 {
"Is_Plane_Strain",
false},
119 {
"Horizon", horizon},
124 {
"Compute_From_Classical",
true},
125 {
"Influence_Function",
json{{
"Type", 0}}}});
128 json{{
"Type",
"PDState"},
129 {
"Is_Plane_Strain",
false},
130 {
"Horizon", horizon},
135 {
"Compute_From_Classical",
true},
136 {
"Influence_Function",
json{{
"Type", 0}}}});
139 json{{
"Contact_Radius_Factor", Rc_factor},
141 {
"Damping_On",
false},
143 {
"Friction_On",
false},
144 {
"Friction_Coeff", 0.0},
146 {
"Beta_n_Factor", 100.0},
149 contact_base[
"Kn"] = Kn;
150 contact_base[
"Kn_Factor"] = 1.0;
152 contact[
"Set_1_1"] = contact_base;
153 contact[
"Set_1_2"] = contact_base;
154 contact[
"Set_2_2"] = contact_base;
155 contact[
"Damping_Law"] = policy_combo ?
"node" :
"off";
156 contact[
"Friction_Law"] = policy_combo ?
"stick_slip" :
"coulomb_simple";
159 const double cy = R + 0.25 * mesh_size;
161 pgen[
"Random_Rotation"] =
false;
162 pgen[
"Data"][
"N"] = 2;
163 pgen[
"Data"][
"0"] =
json{{
"x", 0.},
171 pgen[
"Data"][
"1"] =
json{{
"x", 0.},
172 {
"y", -0.5 * wall_thick},
181 return json{{
"Model", model},
183 {
"Displacement_BC", bc[
"Displacement_BC"]},
190 json{{
"Update_Criteria",
"simple_all"},
191 {
"Search_Factor", 5.0},
192 {
"Search_Interval", 1},
193 {
"Near_Bd_Nodes_Tol", 0.5}})},
194 {
"Particle_Generation", pgen}};
198 size_t n_contact_nodes = 0;
199 double min_gap = 1.e300;
200 double max_force_err = 0.;
201 double max_force_ref = 0.;
208 throw std::runtime_error(
"analytical wall: contact path not set up");
211 const double Kn =
contact.d_Kn;
212 const double Rc =
contact.d_contactR;
214 for (
auto &f : dem.
d_f)
219 for (
size_t i = 0; i < dem.
d_x.size(); ++i) {
235 const double err = (dem.
d_f[i] - f_ref).length();
245 double ming = 1.e300;
247 for (
size_t i = 0; i < dem.
d_x.size(); ++i) {
256 return n ? ming : 0.;
261int main(
int argc,
char *argv[]) {
265 unsigned n_threads = 2;
267 n_threads =
static_cast<unsigned>(std::stoi(input.
getCmdOption(
"-nThreads")));
270 namespace fs = std::filesystem;
271 fs::path base = fs::current_path() /
"wall_analytical_run";
274 const fs::path out_dir = base /
"out";
275 const fs::path inp_dir = base /
"inp";
276 fs::create_directories(out_dir);
277 fs::create_directories(inp_dir);
279 const double R = 0.001;
280 const double mesh_size = R / 5.0;
281 const double horizon = 3.0 * mesh_size;
282 const double Rc_factor = 0.95;
283 const double Kn = 1.0e11;
284 const double final_time = 5.0e-4;
285 const size_t num_steps = 5000;
289 buildInputJson(directoryPathWithTrailingSep(out_dir), inp_dir /
"mesh_cir.msh",
290 inp_dir /
"mesh_wall.msh", R, mesh_size, horizon, Rc_factor, Kn,
291 final_time, num_steps, policy_combo);
293 std::ofstream os(inp_dir /
"input.json");
294 os << input_json.dump(2);
305 auto deck = std::make_shared<inp::Input>(input_json);
310 throw std::runtime_error(
"analytical wall: Wall_Contact not analytical_plane");
313 throw std::runtime_error(
"alternate deck: Self_Contact is not reference_gap");
315 throw std::runtime_error(
"alternate deck: Bond_Break is not absolute_stretch");
317 throw std::runtime_error(
"alternate deck: Friction_Law is not stick_slip");
319 throw std::runtime_error(
"alternate deck: Damping_Law is not node");
322 throw std::runtime_error(
"alternate deck: node damping not active on contact");
326 !dem.
d_contact_p->d_wallContact->skipsMeshedGrainWall())
327 throw std::runtime_error(
"analytical wall: must skip meshed grain-wall");
329 const double Rc = dem.
d_particleDeck_p->d_contactDeck.getContact(0, 1).d_contactR;
335 const double push = 0.5 * Rc;
336 for (
size_t i = 0; i < dem.
d_x.size(); ++i) {
339 dem.
d_u[i].d_y -= push;
345 "analytical wall formula: n_contact={}, min_gap={}, Rc={}, max|f_ref|={}, max|f-f_ref|={}\n",
346 frozen.n_contact_nodes, frozen.min_gap, Rc, frozen.max_force_ref,
347 frozen.max_force_err));
348 if (frozen.n_contact_nodes == 0)
349 throw std::runtime_error(
"analytical wall: no grain nodes in contact");
350 if (!(frozen.max_force_ref > 0.))
351 throw std::runtime_error(
"analytical wall: expected nonzero force");
352 if (!(frozen.max_force_err <= 1.e-6 * (1. + frozen.max_force_ref)))
353 throw std::runtime_error(std::format(
354 "analytical wall: force density mismatch vs Kn*(gap-Rc)*voli*(-n): err={}",
355 frozen.max_force_err));
356 if (!(frozen.min_gap > -Rc))
357 throw std::runtime_error(std::format(
358 "analytical wall: deep penetration past Rc in contact config: min_gap={}, Rc={}",
359 frozen.min_gap, Rc));
365 util::io::print(std::format(
"analytical wall after computeForces: min_gap={}, Rc={}\n", ming, Rc));
367 throw std::runtime_error(
368 "analytical wall: expected grain-wall contact (min_gap < Rc)");
370 throw std::runtime_error(std::format(
371 "analytical wall: deep penetration past Rc: min_gap={}, Rc={}", ming, Rc));
int main(int argc, char *argv[])
std::unique_ptr< contact::Contact > d_contact_p
std::vector< util::Point > d_x
Current positions of the nodes.
std::vector< util::Point > d_xRef
reference positions of the nodes
std::vector< util::Point > d_u
Displacement of the nodes.
std::vector< util::Point > d_f
Total force on the nodes.
std::shared_ptr< inp::ModelDeck > d_modelDeck_p
Pointer to deck holding problem related data.
std::vector< size_t > d_ptId
Global node to particle id (walls are assigned id after last particle id)
std::shared_ptr< inp::ParticleDeck > d_particleDeck_p
Pointer to deck holding particle related data.
std::vector< double > d_vol
Nodal volumes.
std::vector< util::Point > d_v
Velocity of the nodes.
Defines abstract geometrical domain.
virtual bool wallContactQuery(const util::Point &x, WallContactHit &hit) const
Closest-point / signed-gap query for analytical wall contact. Default: unsupported (returns false,...
Infinite plane: free space is the half-space in the normal direction.
nlohmann::ordered_json json
std::string directoryPathWithTrailingSep(const std::filesystem::path &dir)
Probe checkWallSpringOnly(PeriDEMModel &dem, geom::GeomObject &wall_geom)
double minGrainWallGap(PeriDEMModel &dem, geom::GeomObject &wall_geom)
json buildInputJson(const std::string &output_path, const std::filesystem::path &mesh_cir, const std::filesystem::path &mesh_wall, double R, double mesh_size, double horizon, double Rc_factor, double Kn, double final_time, size_t num_steps, bool policy_combo=false)
Collection of methods and data related to finite element and mesh.
Collection of methods and data related to particle object.
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.
void finalizeMpi()
Call MPI_Finalize if this process initialized MPI.
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())
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.
double length() const
Computes the Euclidean length of the vector.