40 namespace fs = std::filesystem;
41 fs::path n = fs::absolute(dir).lexically_normal();
42 std::string s = n.string();
43 if (!s.empty() && s.back() !=
'/' && s.back() !=
'\\')
44 s += fs::path::preferred_separator;
49 double y_lo,
double y_hi) {
50 if ((x0 - x_line) * (x1 - x_line) >= 0.)
52 const double t = (x_line - x0) / (x1 - x0);
55 const double y = y0 + t * (y1 - y0);
56 return y >= y_lo && y <= y_hi;
62 for (
size_t i = 0; i < dem.
d_neighPd.size(); ++i) {
65 const auto &xi = dem.
d_xRef[i];
66 for (
size_t k = 0; k < dem.
d_neighPd[i].size(); ++k) {
70 const auto &xj = dem.
d_xRef[j];
84 const double hw = 0.5 * notch_w;
85 auto crossesSlot = [&](
double x0,
double y0,
double x1,
double y1,
double xc) {
86 const double xa = xc - hw, xb = xc + hw;
87 auto inSlot = [&](
double x,
double y) {
88 return x >= xa && x <= xb && y >= y_lo && y <= y_hi;
90 if (inSlot(x0, y0) || inSlot(x1, y1))
92 for (
int s = 0; s <= 8; ++s) {
93 const double t = s / 8.0;
94 const double x = x0 + t * (x1 - x0);
95 const double y = y0 + t * (y1 - y0);
102 for (
size_t i = 0; i < dem.
d_neighPd.size(); ++i) {
105 const auto &xi = dem.
d_xRef[i];
106 for (
size_t k = 0; k < dem.
d_neighPd[i].size(); ++k) {
110 const auto &xj = dem.
d_xRef[j];
111 if (crossesSlot(xi.d_x, xi.d_y, xj.d_x, xj.d_y, -notch_half) ||
112 crossesSlot(xi.d_x, xi.d_y, xj.d_x, xj.d_y, notch_half)) {
124 auto crossesMid = [&](
double x0,
double y0,
double x1,
double y1,
double xc) {
125 if ((x0 - xc) * (x1 - xc) >= 0.)
127 const double den = x1 - x0;
128 if (std::abs(den) < 1.0e-30)
130 const double t = (xc - x0) / den;
131 if (t <= 0. || t >= 1.)
133 const double y = y0 + t * (y1 - y0);
134 return y >= y_tip && y <= y_top;
137 for (
size_t i = 0; i < dem.
d_neighPd.size(); ++i) {
140 const auto &xi = dem.
d_xRef[i];
141 for (
size_t k = 0; k < dem.
d_neighPd[i].size(); ++k) {
145 const auto &xj = dem.
d_xRef[j];
146 if (crossesMid(xi.d_x, xi.d_y, xj.d_x, xj.d_y, -notch_half) ||
147 crossesMid(xi.d_x, xi.d_y, xj.d_x, xj.d_y, notch_half)) {
161 for (
size_t k = 0; k < nb.size(); ++k) {
165 return static_cast<double>(n_br) /
static_cast<double>(nb.size());
169 if (use_bond_count) {
171 return static_cast<double>(dem.
d_phiBond[i]);
174 if (!dem.
d_phi.empty() && i < dem.
d_phi.size())
175 return static_cast<double>(dem.
d_phi[i]);
181 double angle_to_notch_deg = 0.;
188 double outward_sign,
double phi_cut,
double band_x,
double band_y,
189 double exclude_r,
double bin_h,
bool use_bond_count =
false) {
191 std::vector<double> xs, ys;
193 const int nbin = std::max(4,
static_cast<int>(std::ceil((band_x - exclude_r) / bin_h)));
194 for (
int b = 0; b < nbin; ++b) {
195 const double r0 = exclude_r + b * bin_h;
196 const double r1 = std::min(band_x, r0 + bin_h);
197 double best_phi = phi_cut;
198 double best_dx = 0., best_dy = 0.;
200 for (
size_t i = 0; i < dem.
d_xRef.size(); ++i) {
206 const double dx_ref = outward_sign * (dem.
d_xRef[i].d_x - tip_x);
207 const double dy_ref = tip_y - dem.
d_xRef[i].d_y;
208 if (dx_ref < r0 || dx_ref >= r1)
210 if (dy_ref < -2.0 * bin_h || dy_ref > band_y)
212 const double dx = dem.
d_xRef[i].d_x - tip_x;
213 const double dy = tip_y - dem.
d_xRef[i].d_y;
220 xs.push_back(best_dx);
221 ys.push_back(best_dy);
224 fit.
n_pts = xs.size();
227 double mx = 0., my = 0.;
228 for (
size_t i = 0; i < fit.
n_pts; ++i) {
232 mx /=
static_cast<double>(fit.
n_pts);
233 my /=
static_cast<double>(fit.
n_pts);
236 double cxx = 0., cxy = 0., cyy = 0.;
237 for (
size_t i = 0; i < fit.
n_pts; ++i) {
238 const double x = xs[i] - mx;
239 const double y = ys[i] - my;
244 const double trace = cxx + cyy;
245 const double det = cxx * cyy - cxy * cxy;
246 const double tmp = std::sqrt(std::max(0., 0.25 * trace * trace - det));
247 const double l1 = 0.5 * trace + tmp;
249 double vy = l1 - cxx;
250 if (vx * vx + vy * vy < 1.e-30) {
254 const double angle_from_horiz =
255 std::atan2(std::abs(vy), std::abs(vx)) * 180. / M_PI;
261 return json{{
"File", f.string()},
264 {
"Info",
"gmsh_builtin_mesh"},
265 {
"Mesh_Size", mesh_size},
266 {
"Write_Mesh_File",
true}}}};
273 const std::vector<std::vector<double>> &voids = {}) {
274 json cm = {{
"Flag",
true},
276 {
"Mesh_Size", mesh_size},
277 {
"Write_Mesh_File",
true}};
279 cm[
"Void_Regions"] = voids;
280 return json{{
"File", f.string()}, {
"CreateMesh", cm}};
285 double notch_w,
double notch_depth,
286 double z_lo,
double z_hi) {
287 const double hw = 0.5 * notch_w;
288 const double y_tip = 0.5 * H - notch_depth;
289 const double y_hi = 0.5 * H + 1.0e-9;
290 return {{-notch_half - hw, y_tip, z_lo, -notch_half + hw, y_hi, z_hi},
291 {notch_half - hw, y_tip, z_lo, notch_half + hw, y_hi, z_hi}};
297 double notch_w,
double notch_depth) {
298 const double x0 = -0.5 * W, y0 = -0.5 * H, x1 = 0.5 * W, y1 = 0.5 * H;
299 const double hw = 0.5 * notch_w;
300 const double y_tip = y1 - notch_depth;
304 std::vector<std::string>{
"rectangle",
"rectangle",
"rectangle"},
305 std::vector<std::string>{
"plus",
"minus",
"minus"}};
308 x0, y0, 0., x1, y1, 0.,
310 -notch_half - hw, y_tip, 0., -notch_half + hw, y1 + 1.0e-6, 0.,
312 notch_half - hw, y_tip, 0., notch_half + hw, y1 + 1.0e-6, 0.};
319 double notch_w,
double notch_depth) {
320 const double x0 = -0.5 * W, y0 = -0.5 * H, x1 = 0.5 * W, y1 = 0.5 * H;
321 const double hw = 0.5 * notch_w;
322 const double y_tip = y1 - notch_depth;
323 const double y_top = y1 + 1.0e-6;
327 std::vector<std::string>{
"rectangle",
"triangle",
"triangle"},
328 std::vector<std::string>{
"plus",
"minus",
"minus"}};
331 x0, y0, 0., x1, y1, 0.,
333 -notch_half - hw, y_top, 0., -notch_half + hw, y_top, 0., -notch_half, y_tip, 0.,
335 notch_half - hw, y_top, 0., notch_half + hw, y_top, 0., notch_half, y_tip, 0.};
340 const double fix_h = 0.08 * H;
343 {{
"Particle_List", std::vector<size_t>{0}},
346 {{
"Type",
"rectangle"},
348 std::vector<double>{-0.55 * W, 0.5 * H - fix_h, 0., -notch_half - 0.02 * W,
350 {
"Direction", std::vector<size_t>{1, 2}},
351 {
"Time_Function", {{
"Type",
"constant"}, {
"Parameters", std::vector<double>{0.}}}},
352 {
"Spatial_Function", {{
"Type",
"constant"}}},
353 {
"Zero_Displacement",
true}}},
355 {{
"Particle_List", std::vector<size_t>{0}},
358 {{
"Type",
"rectangle"},
360 std::vector<double>{notch_half + 0.02 * W, 0.5 * H - fix_h, 0., 0.55 * W, 0.55 * H,
362 {
"Direction", std::vector<size_t>{1, 2}},
363 {
"Time_Function", {{
"Type",
"constant"}, {
"Parameters", std::vector<double>{0.}}}},
364 {
"Spatial_Function", {{
"Type",
"constant"}}},
365 {
"Zero_Displacement",
true}}}};
370 const std::filesystem::path &mesh_plate,
double W,
double H,
371 double notch_half,
double notch_w,
double notch_depth,
double mesh_size,
372 double horizon,
double rho,
double E,
double K,
double G,
double Gc,
373 double v_impact,
double final_time,
size_t num_steps) {
375 "central_difference",
true, 2,
"Single_Particle",
378 model[
"Self_Contact"] =
"none";
379 model[
"Bond_Break"] =
"tension";
383 std::vector<std::string>({
"Displacement",
"Velocity",
"Force",
"Damage",
"Damage_Z",
384 "Particle_ID",
"Fixity"}),
385 std::max<size_t>(1, num_steps / 10), 1,
false,
"zlib",
true, num_steps,
"",
false);
389 const double drive_h = std::max(2.0 * mesh_size, 0.002);
390 const std::vector<double> drive_strip{-notch_half, 0.5 * H - drive_h, 0., notch_half,
395 {
"Set_1", outer[
"Set_1"]},
396 {
"Set_2", outer[
"Set_2"]},
398 {{
"Particle_List", std::vector<size_t>{0}},
399 {
"Region", {{
"Geometry", {{
"Type",
"rectangle"}, {
"Parameters", drive_strip}}}}},
400 {
"Direction", std::vector<size_t>{1}},
401 {
"Time_Function", {{
"Type",
"constant"}, {
"Parameters", std::vector<double>{0.}}}},
402 {
"Spatial_Function", {{
"Type",
"constant"}}},
403 {
"Zero_Displacement",
true}}},
405 {{
"Particle_List", std::vector<size_t>{0}},
406 {
"Region", {{
"Geometry", {{
"Type",
"rectangle"}, {
"Parameters", drive_strip}}}}},
407 {
"Direction", std::vector<size_t>{2}},
409 {{
"Type",
"linear"}, {
"Parameters", std::vector<double>{-v_impact}}}},
410 {
"Spatial_Function", {{
"Type",
"constant"}}}}}};
418 "PMBBond",
false, horizon, 0, rho, K, G, Gc,
true, 0, E);
420 material[
"Set_1"][
"Influence_Function"] = {
421 {
"Type", 0}, {
"Parameters", std::vector<double>{1.0}}};
423 return json{{
"Model", model},
425 {
"Displacement_BC", bc_disp},
432 const std::filesystem::path &mesh_plate,
433 const std::filesystem::path &mesh_impactor,
double W,
double H,
434 double notch_half,
double notch_w,
double notch_depth,
double Iw,
435 double Ih,
double gap,
double mesh_size,
double horizon,
436 double Rc_factor,
double Kn,
double rho,
double E,
double K,
double G,
437 double Gc,
double v_impact,
double final_time,
size_t num_steps,
438 double thickness = 0.) {
443 const bool dim3 = thickness > 0.;
444 const size_t dim = dim3 ? 3 : 2;
448 "central_difference", !dim3, 2,
"Multi_Particle",
450 model[
"Self_Contact"] =
"none";
451 model[
"Bond_Break"] =
"tension";
452 model[
"Wall_Contact"] =
"meshed";
455 model[
"Rigid_Particles"] = json::array(
456 {
json{{
"Id", 1}, {
"Mass", dim3 ? 1.57 : 1.57 / 0.009}}});
460 std::vector<std::string>({
"Displacement",
"Velocity",
"Force",
"Damage",
"Damage_Bond",
461 "Damage_Z",
"Particle_ID"}),
462 std::max<size_t>(1, num_steps / 10), 1,
false,
"zlib",
true, num_steps,
"",
false);
467 {{
"Particle_List", std::vector<size_t>{1}},
468 {
"Constant_Velocity",
469 {{
"Velocity_Vector", std::vector<double>{0., -v_impact, 0.}}}}}}};
473 json bc_disp = {{
"Sets", 1},
475 {{
"Particle_List", std::vector<size_t>{1}},
476 {
"Direction", std::vector<size_t>{1}},
478 {{
"Type",
"constant"}, {
"Parameters", std::vector<double>{0.}}}},
479 {
"Spatial_Function", {{
"Type",
"constant"}}},
480 {
"Zero_Displacement",
true}}}};
491 plate.
d_geomParams = {-0.5 * W, -0.5 * H, -0.5 * thickness,
492 0.5 * W, 0.5 * H, 0.5 * thickness};
497 impactor.
d_geomParams = {0.5 * Iw, 0., -0.5 * Ih, 0., 0., Ih, 0.};
500 plate.
d_geomParams = {-0.5 * W, -0.5 * H, 0., 0.5 * W, 0.5 * H, 0.};
503 impactor.
d_geomParams = {-0.5 * Iw, -0.5 * Ih, 0., 0.5 * Iw, 0.5 * Ih, 0.};
509 dim3 ? -0.5 * thickness - 1.0e-9 : -1.0e-9,
510 dim3 ? 0.5 * thickness + 1.0e-9 : 1.0e-9);
516 {
"Set_2", dim3 ?
meshSetJson(mesh_impactor, mesh_size)
524 "PMBBond",
false, horizon, 0, rho, K, G, Gc,
true, 0, E);
526 "PDElasticBond",
false, horizon, 0, rho, K, G, 0.,
true, 0, E);
527 material[
"Set_1"][
"Influence_Function"] = {
528 {
"Type", 0}, {
"Parameters", std::vector<double>{1.0}}};
529 material[
"Set_2"][
"Influence_Function"] = {
530 {
"Type", 0}, {
"Parameters", std::vector<double>{1.0}}};
533 const double Rc_abs = Rc_factor * mesh_size;
535 Rc_abs,
false,
false,
false, Kn,
536 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, K);
537 contact_base[
"Kn"] = Kn;
539 contact[
"Set_1_1"] = contact_base;
540 contact[
"Set_1_2"] = contact_base;
541 contact[
"Set_2_2"] = contact_base;
542 contact[
"Damping_Law"] =
"off";
543 contact[
"Friction_Law"] =
"coulomb_simple";
545 const double cy_imp = 0.5 * H + 0.5 * Ih + gap;
547 pgen[
"Random_Rotation"] =
false;
548 pgen[
"Data"][
"N"] = 2;
549 pgen[
"Data"][
"0"] =
json{{
"x", 0.},
557 pgen[
"Data"][
"1"] =
json{{
"x", 0.},
568 return json{{
"Model", model},
570 {
"Displacement_BC", bc_disp},
577 {
"Particle_Generation", pgen}};
582 const std::filesystem::path &mesh_plate,
583 const std::filesystem::path &mesh_impactor,
double W,
double H,
584 double notch_half,
double notch_w,
double notch_depth,
double Iw,
585 double Ih,
double gap,
double mesh_size,
double horizon,
586 double Rc_factor,
double rho,
double E,
double K,
double G,
double Gc,
587 double v_impact,
double final_time,
size_t num_steps) {
589 "velocity_verlet",
true, 2,
"Multi_Particle", 0);
590 model[
"Self_Contact"] =
"reference_gap";
591 model[
"Bond_Break"] =
"absolute_stretch";
592 model[
"Wall_Contact"] =
"meshed";
596 std::vector<std::string>({
"Displacement",
"Velocity",
"Force",
"Damage",
"Damage_Bond",
597 "Damage_Z",
"Particle_ID"}),
598 std::max<size_t>(1, num_steps / 10), 1,
false,
"zlib",
true, num_steps,
"",
false);
603 {{
"Particle_List", std::vector<size_t>{1}},
604 {
"Constant_Velocity",
605 {{
"Velocity_Vector", std::vector<double>{0., -v_impact, 0.}}}}}}};
609 json bc_disp = {{
"Sets", 2}, {
"Set_1", outer[
"Set_1"]}, {
"Set_2", outer[
"Set_2"]}};
616 impactor.
d_geomParams = {-0.5 * Iw, -0.5 * Ih, 0., 0.5 * Iw, 0.5 * Ih, 0.};
624 const double nu_bhat = 1.0 / 3.0;
625 const double c_bhat =
626 6.0 * E / (M_PI * std::pow(horizon, 3.0) * (1.0 - nu_bhat));
627 const double s0_bhat = std::sqrt(4.0 * M_PI * Gc / (9.0 * E * horizon));
632 mat_bhat[
"Bond_Potential_Params"] = std::vector<double>{c_bhat, s0_bhat};
633 mat_bhat[
"Influence_Function"] = {{
"Type", 0}, {
"Parameters", std::vector<double>{1.0}}};
639 mat_strike[
"Influence_Function"] = {{
"Type", 0}, {
"Parameters", std::vector<double>{1.0}}};
643 const double Kn_bhat = 18.0 * K / (M_PI * std::pow(horizon, 4.0));
644 const double Rc_abs = Rc_factor * mesh_size;
646 Rc_abs,
false,
false,
false, Kn_bhat,
647 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, K);
648 contact_base[
"Kn"] = Kn_bhat;
650 contact[
"Set_1_1"] = contact_base;
651 contact[
"Set_1_2"] = contact_base;
652 contact[
"Set_2_2"] = contact_base;
653 contact[
"Damping_Law"] =
"off";
654 contact[
"Friction_Law"] =
"coulomb_simple";
655 contact[
"Set_1_1"][
"Friction_Coefficient"] = 0.;
656 contact[
"Set_1_2"][
"Friction_Coefficient"] = 0.;
657 contact[
"Set_2_2"][
"Friction_Coefficient"] = 0.;
659 const double cy_imp = 0.5 * H + 0.5 * Ih + gap;
661 pgen[
"Random_Rotation"] =
false;
662 pgen[
"Data"][
"N"] = 2;
663 pgen[
"Data"][
"0"] =
json{{
"x", 0.}, {
"y", 0.}, {
"z", 0.},
664 {
"theta", 0.}, {
"s", 1.}, {
"geom_id", 0},
665 {
"mat_id", 0}, {
"contact_id", 0}};
666 pgen[
"Data"][
"1"] =
json{{
"x", 0.}, {
"y", cy_imp}, {
"z", 0.},
667 {
"theta", 0.}, {
"s", 1.}, {
"geom_id", 1},
668 {
"mat_id", 1}, {
"contact_id", 1}};
670 return json{{
"Model", model},
672 {
"Displacement_BC", bc_disp},
679 {
"Particle_Generation", pgen}};
684int main(
int argc,
char *argv[]) {
688 unsigned n_threads = 1;
690 n_threads =
static_cast<unsigned>(std::stoi(input.
getCmdOption(
"-nThreads")));
699 namespace fs = std::filesystem;
701 std::string run_tag =
702 quick ?
"quick" : (trask ?
"trask" : (bhat ?
"bhat" : (dim3 ?
"silling3d" :
"lit")));
704 fs::path base = fs::current_path() /
"runs_new" / run_tag;
707 const fs::path out_dir = base /
"out";
708 const fs::path inp_dir = base /
"inp";
709 fs::create_directories(out_dir);
710 fs::create_directories(inp_dir);
711 std::cout << std::format(
"notched_impact: outputDir={}\n", base.string());
716 double notch_depth = 0.050;
717 double notch_half = 0.025;
718 double notch_w = 0.0015;
719 double plate_thickness = 0.009;
727 double mesh_size = 0.001;
728 double horizon = 3.0 * mesh_size;
729 double Rc_factor = 0.95;
735 double gap = 1.5 * Rc_factor * mesh_size;
741 double Kbulk = 159.2e9;
742 double nu = 0.5 * (1.0 - E / (3.0 * Kbulk));
743 double G = E / (2.0 * (1.0 + nu));
745 double v_impact = 32.0;
749 double final_time = 1.7e-4;
753 final_time = std::stod(input.
getCmdOption(
"-finalTime"));
758 horizon = 3.0 * mesh_size;
763 horizon = std::stod(input.
getCmdOption(
"-horizonFactor")) * mesh_size;
768 notch_depth = 0.5 * H;
769 notch_half = 0.125 * W;
770 mesh_size = H / 16.0;
771 notch_w = std::max(mesh_size, 0.1 * notch_half);
774 gap = 1.5 * Rc_factor * mesh_size;
775 horizon = 3.0 * mesh_size;
779 nu = 0.5 * (1.0 - E / (3.0 * Kbulk));
780 G = E / (2.0 * (1.0 + nu));
791 (M_PI * std::pow(horizon, dim3 ? 5 : 4));
792 const size_t num_steps =
static_cast<size_t>(std::llround(final_time / dt));
797 inp_dir /
"mesh_plate.msh", W, H, notch_half, notch_w,
798 notch_depth, mesh_size, horizon, rho, E, Kbulk, G, Gc,
799 v_impact, final_time, num_steps);
802 directoryPathWithTrailingSep(out_dir), inp_dir /
"mesh_plate.msh",
803 inp_dir /
"mesh_impactor.msh", W, H, notch_half, notch_w, notch_depth, Iw, Ih, gap,
804 mesh_size, horizon, Rc_factor, rho, E, Kbulk, G, Gc, v_impact, final_time, num_steps);
807 directoryPathWithTrailingSep(out_dir), inp_dir /
"mesh_plate.msh",
808 inp_dir /
"mesh_impactor.msh", W, H, notch_half, notch_w, notch_depth, Iw, Ih, gap,
809 mesh_size, horizon, Rc_factor, Kn, rho, E, Kbulk, G, Gc, v_impact, final_time,
810 num_steps, dim3 ? plate_thickness : 0.);
816 input_json[
"Model"][
"Self_Contact"] = sc;
817 std::cout << std::format(
"notched_impact: Self_Contact override -> {}\n", sc);
821 input_json[
"Model"][
"Bond_Break"] = bb;
822 std::cout << std::format(
"notched_impact: Bond_Break override -> {}\n", bb);
825 const double s = std::stod(input.
getCmdOption(
"-knScale"));
826 for (
auto &kv : input_json[
"Contact"].items()) {
827 if (!kv.value().is_object() || !kv.value().contains(
"Kn"))
829 kv.value()[
"Kn"] = kv.value()[
"Kn"].get<
double>() * s;
831 std::cout << std::format(
"notched_impact: Contact Kn scaled by {}\n", s);
834 std::ofstream os(inp_dir /
"input.json");
835 os << input_json.dump(2);
838 auto deck = std::make_shared<inp::Input>(input_json);
842 const double y_top = 0.5 * H;
843 const double y_tip = y_top - notch_depth;
850 std::cerr <<
"notched_impact: expected prenotch bonds, got " << n_pre <<
"\n";
854 const char *mode_str =
856 : (trask ?
"trask_disp"
857 : (bhat ?
"bhat_kw" : (dim3 ?
"silling_3d" :
"silling_impact")));
858 std::cout << std::format(
859 "notched_impact: mode={} nodes={} prenotch={} h={:.4e} ε={:.4e} notch_w={:.4e} "
860 "Gc={:.0f} E={:.3e} v={:.1f} Nt={} T={:.3e}\n",
861 mode_str, dem.
d_x.size(), n_pre, mesh_size, horizon, notch_w, Gc, E, v_impact, num_steps,
867 std::vector<float> arrival(dem.
d_x.size(), -1.f);
868 const size_t sample_every = std::max<size_t>(1, num_steps / 400);
869 auto sampleArrivals = [&]() {
870 for (
size_t i = 0; i < dem.
d_xRef.size(); ++i) {
871 if (arrival[i] >= 0.f || dem.
d_ptId[i] != 0)
874 arrival[i] =
static_cast<float>(dem.
d_time);
895 double vmax = 0., phi_max = 0.;
897 for (
size_t i = 0; i < dem.
d_v.size(); ++i) {
898 vmax = std::max(vmax, dem.
d_v[i].length());
901 const double phi =
nodePhi(dem, i);
902 phi_max = std::max(phi_max, phi);
907 dem.
d_Z.empty() ? 0.f : *std::max_element(dem.
d_Z.begin(), dem.
d_Z.end());
908 std::cout << std::format(
909 "notched_impact diag: max|v|={:.3e} maxφ={:.3f} maxZ={:.3f} n(φ≥0.3)={}\n", vmax,
910 phi_max, zmax, n_phi30);
912 if (!(vmax < 8.0e3) || !std::isfinite(vmax)) {
913 std::cerr <<
"notched_impact: blow-up, max |v| = " << vmax <<
"\n";
918 for (
size_t i = 0; i < dem.
d_neighPd.size(); ++i) {
921 for (
size_t k = 0; k < dem.
d_neighPd[i].size(); ++k) {
927 const auto &xi = dem.
d_xRef[i];
928 const auto &xj = dem.
d_xRef[j];
929 const bool on_notch =
939 std::cerr <<
"notched_impact: expected new broken bonds, got " << n_extra
940 <<
" (prenotch=" << n_pre <<
")\n";
947 std::ofstream csv(base /
"damage.csv");
948 csv <<
"x_ref,y_ref,z_ref,x_cur,y_cur,z_cur,phi,phi_bond,arrival\n";
949 for (
size_t i = 0; i < dem.
d_xRef.size(); ++i) {
954 if (std::max(phi, phi_b) < 0.05)
956 csv << std::format(
"{:.6e},{:.6e},{:.6e},{:.6e},{:.6e},{:.6e},{:.4f},{:.4f},{:.6e}\n",
958 dem.
d_x[i].d_x, dem.
d_x[i].d_y, dem.
d_x[i].d_z, phi, phi_b,
963 const double band_x = 0.45 * W;
964 const double band_y = 0.55 * H;
965 const double phi_cut = quick ? 0.20 : 0.35;
966 const double exclude_r = 2.0 * mesh_size;
967 const bool use_bond_dmg = bhat;
968 auto left =
fitCrackFromPhi(dem, -notch_half, y_tip, -1.0, phi_cut, band_x, band_y, exclude_r,
969 mesh_size, use_bond_dmg);
970 auto right =
fitCrackFromPhi(dem, notch_half, y_tip, +1.0, phi_cut, band_x, band_y, exclude_r,
971 mesh_size, use_bond_dmg);
972 if (left.n_pts < 4 || right.n_pts < 4) {
973 std::cerr <<
"notched_impact: insufficient φ-ridge for path fit (L=" << left.n_pts
974 <<
" R=" << right.n_pts <<
")\n";
977 if (!(left.mean_dx < 0. && left.mean_dy > 0. && right.mean_dx > 0. && right.mean_dy > 0.)) {
978 std::cerr << std::format(
979 "notched_impact: tip damage not outward/down "
980 "(L dx={:.3e} dy={:.3e} R dx={:.3e} dy={:.3e})\n",
981 left.mean_dx, left.mean_dy, right.mean_dx, right.mean_dy);
985 const double ang = 0.5 * (left.angle_to_notch_deg + right.angle_to_notch_deg);
986 std::cout << std::format(
987 "notched_impact PASS: prenotch={} new_broken={} ang_to_notch={:.1f}° "
988 "(L={:.1f}/{} R={:.1f}/{}) max|v|={:.3e}\n",
989 n_pre, n_extra, ang, left.angle_to_notch_deg, left.n_pts, right.angle_to_notch_deg,
void applyInitialCondition()
void applyDisplacementBC()
std::vector< util::Point > d_x
Current positions of the nodes.
std::vector< util::Point > d_xRef
reference positions of the nodes
std::vector< float > d_Z
Damage at nodes.
size_t currentStep() const
std::unique_ptr< geometry::Fracture > d_fracture_p
Fracture state of bonds.
double d_time
Current time.
std::vector< size_t > d_ptId
Global node to particle id (walls are assigned id after last particle id)
std::vector< float > d_phi
Damage function at the nodes (volume-weighted, Silling 2000)
std::vector< std::vector< size_t > > d_neighPd
Neighbor data for peridynamic forces.
size_t numTimeSteps() const
void setCurrentDt(double dt)
bool shouldOutput() const
std::vector< util::Point > d_v
Velocity of the nodes.
std::vector< float > d_phiBond
Damage as broken-bond count fraction (Bhattacharya & Lipton 2023)
bool performOutput() const
nlohmann::ordered_json json
json buildBhatInputJson(const std::string &output_path, const std::filesystem::path &mesh_plate, const std::filesystem::path &mesh_impactor, double W, double H, double notch_half, double notch_w, double notch_depth, double Iw, double Ih, double gap, double mesh_size, double horizon, double Rc_factor, double rho, double E, double K, double G, double Gc, double v_impact, double final_time, size_t num_steps)
std::string directoryPathWithTrailingSep(const std::filesystem::path &dir)
size_t applyNotchMidplanes(PeriDEMModel &dem, double notch_half, double y_tip, double y_top)
json buildTraskInputJson(const std::string &output_path, const std::filesystem::path &mesh_plate, double W, double H, double notch_half, double notch_w, double notch_depth, double mesh_size, double horizon, double rho, double E, double K, double G, double Gc, double v_impact, double final_time, size_t num_steps)
bool segmentCrossesVertical(double x0, double y0, double x1, double y1, double x_line, double y_lo, double y_hi)
size_t applyNotchSlots(PeriDEMModel &dem, double notch_half, double notch_w, double y_lo, double y_hi)
json buildImpactInputJson(const std::string &output_path, const std::filesystem::path &mesh_plate, const std::filesystem::path &mesh_impactor, double W, double H, double notch_half, double notch_w, double notch_depth, double Iw, double Ih, double gap, double mesh_size, double horizon, double Rc_factor, double Kn, double rho, double E, double K, double G, double Gc, double v_impact, double final_time, size_t num_steps, double thickness=0.)
double nodePhi(const PeriDEMModel &dem, size_t i)
json meshSetJson(const std::filesystem::path &f, double mesh_size)
std::vector< std::vector< double > > notchVoidBoxes(double H, double notch_half, double notch_w, double notch_depth, double z_lo, double z_hi)
json uniformMeshSetJson(const std::filesystem::path &f, double mesh_size, const std::vector< std::vector< double > > &voids={})
CrackFit fitCrackFromPhi(const PeriDEMModel &dem, double tip_x, double tip_y, double outward_sign, double phi_cut, double band_x, double band_y, double exclude_r, double bin_h, bool use_bond_count=false)
size_t applyNotches(PeriDEMModel &dem, double x_left, double x_right, double y_lo, double y_hi)
double nodeDamageForFit(const PeriDEMModel &dem, size_t i, bool use_bond_count)
geom::GeomData bhatVNotchedPlateGeom(double W, double H, double notch_half, double notch_w, double notch_depth)
geom::GeomData sillingNotchedPlateGeom(double W, double H, double notch_half, double notch_w, double notch_depth)
json fixedTopOuterBC(double W, double H, double notch_half)
Collection of methods and data related to finite element and mesh.
Collection of methods and data related to particle object.
void applyRigidBodyConstraint(data::ModelData &data)
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.
double harmonicMean(const double &m1, const double &m2)
double angle_to_notch_deg
Input data for geometrical objects.
std::vector< double > d_geomParams
Zone parameters.
std::string d_geomName
Zone type.
std::pair< std::vector< std::string >, std::vector< std::string > > d_geomComplexInfo
Zone geometry info if it is a complex type.
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.
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.
static json getExampleJson(std::string genMethod="From_File")
Returns example JSON object for ModelDeck configuration.
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.