261 {
264
265 unsigned n_threads = 2;
266 if (input.cmdOptionExists("-nThreads"))
267 n_threads = static_cast<unsigned>(std::stoi(input.getCmdOption("-nThreads")));
269
270 namespace fs = std::filesystem;
271 fs::path base = fs::current_path() / "wall_analytical_run";
272 if (input.cmdOptionExists("-outputDir"))
273 base = input.getCmdOption("-outputDir");
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);
278
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;
286
287 const bool policy_combo = input.cmdOptionExists("-policyCombo");
288 auto input_json =
290 inp_dir / "mesh_wall.msh", R, mesh_size, horizon, Rc_factor, Kn,
291 final_time, num_steps, policy_combo);
292 {
293 std::ofstream os(inp_dir / "input.json");
294 os << input_json.dump(2);
295 }
296
297
298
299 if (input.cmdOptionExists("-deckOnly")) {
302 return 0;
303 }
304
305 auto deck = std::make_shared<inp::Input>(input_json);
307 dem.init();
308
309 if (dem.d_modelDeck_p->d_wallContact != "analytical_plane")
310 throw std::runtime_error("analytical wall: Wall_Contact not analytical_plane");
311 if (policy_combo) {
312 if (dem.d_modelDeck_p->d_selfContact != "reference_gap")
313 throw std::runtime_error("alternate deck: Self_Contact is not reference_gap");
314 if (dem.d_modelDeck_p->d_bondBreak != "absolute_stretch")
315 throw std::runtime_error("alternate deck: Bond_Break is not absolute_stretch");
316 if (dem.d_particleDeck_p->d_contactDeck.d_frictionLaw != "stick_slip")
317 throw std::runtime_error("alternate deck: Friction_Law is not stick_slip");
318 if (dem.d_particleDeck_p->d_contactDeck.d_dampingLaw != "node")
319 throw std::runtime_error("alternate deck: Damping_Law is not node");
320 if (!dem.d_contact_p || !dem.d_contact_p->d_pairForce ||
321 dem.d_contact_p->d_useNodeDamping != true)
322 throw std::runtime_error("alternate deck: node damping not active on contact");
324 }
325 if (!dem.d_contact_p || !dem.d_contact_p->d_wallContact ||
326 !dem.d_contact_p->d_wallContact->skipsMeshedGrainWall())
327 throw std::runtime_error("analytical wall: must skip meshed grain-wall");
328
329 const double Rc = dem.d_particleDeck_p->d_contactDeck.getContact(0, 1).d_contactR;
330
332
333
334 {
335 const double push = 0.5 * Rc;
336 for (size_t i = 0; i < dem.d_x.size(); ++i) {
337 if (dem.d_ptId[i] != 0)
338 continue;
339 dem.d_u[i].d_y -= push;
340 dem.d_x[i] = dem.d_xRef[i] + dem.d_u[i];
342 }
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));
360 }
361
362
363 dem.computeForces();
365 util::io::print(std::format(
"analytical wall after computeForces: min_gap={}, Rc={}\n", ming, Rc));
366 if (!(ming < Rc))
367 throw std::runtime_error(
368 "analytical wall: expected grain-wall contact (min_gap < Rc)");
369 if (!(ming > -Rc))
370 throw std::runtime_error(std::format(
371 "analytical wall: deep penetration past Rc: min_gap={}, Rc={}", ming, Rc));
372
373 dem.close();
375 return 0;
376}
Infinite plane: free space is the half-space in the normal direction.
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)
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.
A structure to represent 3d vectors.