221 {
224
225 unsigned n_threads = 2;
226 if (input.cmdOptionExists("-nThreads"))
227 n_threads = static_cast<unsigned>(std::stoi(input.getCmdOption("-nThreads")));
229
230 namespace fs = std::filesystem;
231 fs::path base = fs::current_path() / "wall_analytical_run";
232 if (input.cmdOptionExists("-outputDir"))
233 base = input.getCmdOption("-outputDir");
234 const fs::path out_dir = base / "out";
235 const fs::path inp_dir = base / "inp";
236 fs::create_directories(out_dir);
237 fs::create_directories(inp_dir);
238
239 const double R = 0.001;
240 const double mesh_size = R / 5.0;
241 const double horizon = 3.0 * mesh_size;
242 const double Rc_factor = 0.95;
243 const double Kn = 1.0e11;
244 const double final_time = 5.0e-4;
245 const size_t num_steps = 5000;
246
247 const bool policy_combo = input.cmdOptionExists("-policyCombo");
248 auto input_json =
250 inp_dir / "mesh_wall.msh", R, mesh_size, horizon, Rc_factor, Kn,
251 final_time, num_steps, policy_combo);
252 {
253 std::ofstream os(inp_dir / "input.json");
254 os << input_json.dump(2);
255 }
256
257 auto deck = std::make_shared<inp::Input>(input_json);
259 dem.init();
260
261 if (dem.d_modelDeck_p->d_wallContact != "analytical_plane")
262 throw std::runtime_error("analytical wall: Wall_Contact not analytical_plane");
263 if (policy_combo) {
264 if (dem.d_modelDeck_p->d_selfContact != "reference_gap")
265 throw std::runtime_error("alternate deck: Self_Contact is not reference_gap");
266 if (dem.d_modelDeck_p->d_bondBreak != "absolute_stretch")
267 throw std::runtime_error("alternate deck: Bond_Break is not absolute_stretch");
268 if (dem.d_particleDeck_p->d_contactDeck.d_frictionLaw != "stick_slip")
269 throw std::runtime_error("alternate deck: Friction_Law is not stick_slip");
270 if (dem.d_particleDeck_p->d_contactDeck.d_dampingLaw != "node")
271 throw std::runtime_error("alternate deck: Damping_Law is not node");
272 if (!dem.d_contact_p || !dem.d_contact_p->d_pairForce ||
273 dem.d_contact_p->d_useNodeDamping != true)
274 throw std::runtime_error("alternate deck: node damping not active on contact");
276 }
277 if (!dem.d_contact_p || !dem.d_contact_p->d_wallContact ||
278 !dem.d_contact_p->d_wallContact->skipsMeshedGrainWall())
279 throw std::runtime_error("analytical wall: must skip meshed grain-wall");
280
281 const double Rc = dem.d_particleDeck_p->d_contactDeck.getContact(0, 1).d_contactR;
282
284
285
286 {
287 const double push = 0.5 * Rc;
288 for (size_t i = 0; i < dem.d_x.size(); ++i) {
289 if (dem.d_ptId[i] != 0)
290 continue;
291 dem.d_u[i].d_y -= push;
292 dem.d_x[i] = dem.d_xRef[i] + dem.d_u[i];
294 }
297 "analytical wall formula: n_contact={}, min_gap={}, Rc={}, max|f_ref|={}, max|f-f_ref|={}\n",
298 frozen.n_contact_nodes, frozen.min_gap, Rc, frozen.max_force_ref,
299 frozen.max_force_err));
300 if (frozen.n_contact_nodes == 0)
301 throw std::runtime_error("analytical wall: no grain nodes in contact");
302 if (!(frozen.max_force_ref > 0.))
303 throw std::runtime_error("analytical wall: expected nonzero force");
304 if (!(frozen.max_force_err <= 1.e-6 * (1. + frozen.max_force_ref)))
305 throw std::runtime_error(std::format(
306 "analytical wall: force density mismatch vs Kn*(gap-Rc)*voli*(-n): err={}",
307 frozen.max_force_err));
308 if (!(frozen.min_gap > -Rc))
309 throw std::runtime_error(std::format(
310 "analytical wall: deep penetration past Rc in contact config: min_gap={}, Rc={}",
311 frozen.min_gap, Rc));
312 }
313
314
315 dem.computeForces();
317 util::io::print(std::format(
"analytical wall after computeForces: min_gap={}, Rc={}\n", ming, Rc));
318 if (!(ming < Rc))
319 throw std::runtime_error(
320 "analytical wall: expected grain-wall contact (min_gap < Rc)");
321 if (!(ming > -Rc))
322 throw std::runtime_error(std::format(
323 "analytical wall: deep penetration past Rc: min_gap={}, Rc={}", ming, Rc));
324
325 dem.close();
327 return 0;
328}
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.
A structure to represent 3d vectors.