45 {x_left, x_right}, y_lo, y_hi);
49 double y_lo,
double y_hi) {
52 {-notch_half, notch_half}, notch_w, y_lo,
61 {-notch_half, notch_half}, y_tip, y_top);
65std::string directoryPathWithTrailingSep(
const std::filesystem::path &dir) {
66 namespace fs = std::filesystem;
67 fs::path n = fs::absolute(dir).lexically_normal();
68 std::string s = n.string();
69 if (!s.empty() && s.back() !=
'/' && s.back() !=
'\\')
70 s += fs::path::preferred_separator;
84 for (
size_t k = 0; k < nb.size(); ++k) {
88 return static_cast<double>(n_br) /
static_cast<double>(nb.size());
94 return static_cast<double>(dem.
d_phiBond[i]);
97 if (!dem.
d_phi.empty() && i < dem.
d_phi.size())
98 return static_cast<double>(dem.
d_phi[i]);
104 double angle_to_notch_deg = 0.;
111 double outward_sign,
double phi_cut,
double band_x,
double band_y,
112 double exclude_r,
double bin_h,
bool use_bond_count =
false) {
114 std::vector<double> xs, ys;
116 const int nbin = std::max(4,
static_cast<int>(std::ceil((band_x - exclude_r) / bin_h)));
117 for (
int b = 0; b < nbin; ++b) {
118 const double r0 = exclude_r + b * bin_h;
119 const double r1 = std::min(band_x, r0 + bin_h);
120 double best_phi = phi_cut;
121 double best_dx = 0., best_dy = 0.;
123 for (
size_t i = 0; i < dem.
d_xRef.size(); ++i) {
129 const double dx_ref = outward_sign * (dem.
d_xRef[i].d_x - tip_x);
130 const double dy_ref = tip_y - dem.
d_xRef[i].d_y;
131 if (dx_ref < r0 || dx_ref >= r1)
133 if (dy_ref < -2.0 * bin_h || dy_ref > band_y)
135 const double dx = dem.
d_xRef[i].d_x - tip_x;
136 const double dy = tip_y - dem.
d_xRef[i].d_y;
143 xs.push_back(best_dx);
144 ys.push_back(best_dy);
147 fit.
n_pts = xs.size();
150 double mx = 0., my = 0.;
151 for (
size_t i = 0; i < fit.
n_pts; ++i) {
155 mx /=
static_cast<double>(fit.
n_pts);
156 my /=
static_cast<double>(fit.
n_pts);
159 double cxx = 0., cxy = 0., cyy = 0.;
160 for (
size_t i = 0; i < fit.
n_pts; ++i) {
161 const double x = xs[i] - mx;
162 const double y = ys[i] - my;
167 const double trace = cxx + cyy;
168 const double det = cxx * cyy - cxy * cxy;
169 const double tmp = std::sqrt(std::max(0., 0.25 * trace * trace - det));
170 const double l1 = 0.5 * trace + tmp;
172 double vy = l1 - cxx;
173 if (vx * vx + vy * vy < 1.e-30) {
177 const double angle_from_horiz =
178 std::atan2(std::abs(vy), std::abs(vx)) * 180. / M_PI;
184 return json{{
"File", f.string()},
187 {
"Info",
"gmsh_builtin_mesh"},
188 {
"Mesh_Size", mesh_size},
189 {
"Write_Mesh_File",
true}}}};
196 const std::vector<std::vector<double>> &voids = {}) {
197 json cm = {{
"Flag",
true},
199 {
"Mesh_Size", mesh_size},
200 {
"Write_Mesh_File",
true}};
202 cm[
"Void_Regions"] = voids;
203 return json{{
"File", f.string()}, {
"CreateMesh", cm}};
208 double notch_w,
double notch_depth,
209 double z_lo,
double z_hi) {
210 const double hw = 0.5 * notch_w;
211 const double y_tip = 0.5 * H - notch_depth;
212 const double y_hi = 0.5 * H + 1.0e-9;
213 return {{-notch_half - hw, y_tip, z_lo, -notch_half + hw, y_hi, z_hi},
214 {notch_half - hw, y_tip, z_lo, notch_half + hw, y_hi, z_hi}};
220 double notch_w,
double notch_depth) {
221 const double x0 = -0.5 * W, y0 = -0.5 * H, x1 = 0.5 * W, y1 = 0.5 * H;
222 const double hw = 0.5 * notch_w;
223 const double y_tip = y1 - notch_depth;
227 std::vector<std::string>{
"rectangle",
"rectangle",
"rectangle"},
228 std::vector<std::string>{
"plus",
"minus",
"minus"}};
231 x0, y0, 0., x1, y1, 0.,
233 -notch_half - hw, y_tip, 0., -notch_half + hw, y1 + 1.0e-6, 0.,
235 notch_half - hw, y_tip, 0., notch_half + hw, y1 + 1.0e-6, 0.};
242 double notch_w,
double notch_depth) {
243 const double x0 = -0.5 * W, y0 = -0.5 * H, x1 = 0.5 * W, y1 = 0.5 * H;
244 const double hw = 0.5 * notch_w;
245 const double y_tip = y1 - notch_depth;
246 const double y_top = y1 + 1.0e-6;
250 std::vector<std::string>{
"rectangle",
"triangle",
"triangle"},
251 std::vector<std::string>{
"plus",
"minus",
"minus"}};
254 x0, y0, 0., x1, y1, 0.,
256 -notch_half - hw, y_top, 0., -notch_half + hw, y_top, 0., -notch_half, y_tip, 0.,
258 notch_half - hw, y_top, 0., notch_half + hw, y_top, 0., notch_half, y_tip, 0.};
263 const double fix_h = 0.08 * H;
266 {{
"Particle_List", std::vector<size_t>{0}},
269 {{
"Type",
"rectangle"},
271 std::vector<double>{-0.55 * W, 0.5 * H - fix_h, 0., -notch_half - 0.02 * W,
273 {
"Direction", std::vector<size_t>{1, 2}},
274 {
"Time_Function", {{
"Type",
"constant"}, {
"Parameters", std::vector<double>{0.}}}},
275 {
"Spatial_Function", {{
"Type",
"constant"}}},
276 {
"Zero_Displacement",
true}}},
278 {{
"Particle_List", std::vector<size_t>{0}},
281 {{
"Type",
"rectangle"},
283 std::vector<double>{notch_half + 0.02 * W, 0.5 * H - fix_h, 0., 0.55 * W, 0.55 * H,
285 {
"Direction", std::vector<size_t>{1, 2}},
286 {
"Time_Function", {{
"Type",
"constant"}, {
"Parameters", std::vector<double>{0.}}}},
287 {
"Spatial_Function", {{
"Type",
"constant"}}},
288 {
"Zero_Displacement",
true}}}};
293 const std::filesystem::path &mesh_plate,
double W,
double H,
294 double notch_half,
double notch_w,
double notch_depth,
double mesh_size,
295 double horizon,
double rho,
double E,
double K,
double G,
double Gc,
296 double v_impact,
double final_time,
size_t num_steps) {
298 json{{
"Dimension", 2},
299 {
"Final_Time", final_time},
300 {
"Time_Steps", num_steps},
301 {
"Discretization_Type",
json{{
"Spatial",
"finite_difference"}, {
"Time",
"central_difference"}}},
302 {
"Populate_ElementNodeConnectivity",
true},
303 {
"Quad_Approximation_Order", 2},
304 {
"Particle_Sim_Type",
"Single_Particle"},
307 model[
"Self_Contact"] =
"none";
308 model[
"Bond_Break"] =
"tension";
311 json{{
"File_Format",
"vtu"},
312 {
"Path", output_path},
313 {
"Tags", std::vector<std::string>({
"Displacement",
"Velocity",
"Force",
"Damage",
"Damage_Z",
314 "Particle_ID",
"Fixity"})},
315 {
"Output_Interval", std::max<size_t>(1, num_steps / 10)},
317 {
"Perform_FE_Out",
false},
318 {
"Compress_Type",
"zlib"},
319 {
"Perform_Out",
true},
320 {
"Test_Output_Interval", num_steps},
322 {
"PVD_Collection",
false}});
326 const double drive_h = std::max(2.0 * mesh_size, 0.002);
327 const std::vector<double> drive_strip{-notch_half, 0.5 * H - drive_h, 0., notch_half,
332 {
"Set_1", outer[
"Set_1"]},
333 {
"Set_2", outer[
"Set_2"]},
335 {{
"Particle_List", std::vector<size_t>{0}},
336 {
"Region", {{
"Geometry", {{
"Type",
"rectangle"}, {
"Parameters", drive_strip}}}}},
337 {
"Direction", std::vector<size_t>{1}},
338 {
"Time_Function", {{
"Type",
"constant"}, {
"Parameters", std::vector<double>{0.}}}},
339 {
"Spatial_Function", {{
"Type",
"constant"}}},
340 {
"Zero_Displacement",
true}}},
342 {{
"Particle_List", std::vector<size_t>{0}},
343 {
"Region", {{
"Geometry", {{
"Type",
"rectangle"}, {
"Parameters", drive_strip}}}}},
344 {
"Direction", std::vector<size_t>{2}},
346 {{
"Type",
"linear"}, {
"Parameters", std::vector<double>{-v_impact}}}},
347 {
"Spatial_Function", {{
"Type",
"constant"}}}}}};
355 json{{
"Type",
"PMBBond"},
356 {
"Is_Plane_Strain",
false},
357 {
"Horizon", horizon},
363 {
"Compute_From_Classical",
true},
364 {
"Influence_Function",
json{{
"Type", 0}}}});
366 material[
"Set_1"][
"Influence_Function"] = {
367 {
"Type", 0}, {
"Parameters", std::vector<double>{1.0}}};
369 return json{{
"Model", model},
371 {
"Displacement_BC", bc_disp},
378 const std::filesystem::path &mesh_plate,
379 const std::filesystem::path &mesh_impactor,
double W,
double H,
380 double notch_half,
double notch_w,
double notch_depth,
double Iw,
381 double Ih,
double gap,
double mesh_size,
double horizon,
382 double Rc_factor,
double Kn,
double rho,
double E,
double K,
double G,
383 double Gc,
double v_impact,
double final_time,
size_t num_steps,
384 double thickness = 0.) {
389 const bool dim3 = thickness > 0.;
390 const size_t dim = dim3 ? 3 : 2;
394 json{{
"Dimension", dim},
395 {
"Final_Time", final_time},
396 {
"Time_Steps", num_steps},
397 {
"Discretization_Type",
json{{
"Spatial",
"finite_difference"}, {
"Time",
"central_difference"}}},
398 {
"Populate_ElementNodeConnectivity", !dim3},
399 {
"Quad_Approximation_Order", 2},
400 {
"Particle_Sim_Type",
"Multi_Particle"},
402 model[
"Self_Contact"] =
"none";
403 model[
"Bond_Break"] =
"tension";
404 model[
"Wall_Contact"] =
"meshed";
407 model[
"Rigid_Particles"] = json::array(
408 {
json{{
"Id", 1}, {
"Mass", dim3 ? 1.57 : 1.57 / 0.009}}});
411 json{{
"File_Format",
"vtu"},
412 {
"Path", output_path},
413 {
"Tags", std::vector<std::string>({
"Displacement",
"Velocity",
"Force",
"Damage",
"Damage_Bond",
414 "Damage_Z",
"Particle_ID"})},
415 {
"Output_Interval", std::max<size_t>(1, num_steps / 10)},
417 {
"Perform_FE_Out",
false},
418 {
"Compress_Type",
"zlib"},
419 {
"Perform_Out",
true},
420 {
"Test_Output_Interval", num_steps},
422 {
"PVD_Collection",
false}});
427 {{
"Particle_List", std::vector<size_t>{1}},
428 {
"Constant_Velocity",
429 {{
"Velocity_Vector", std::vector<double>{0., -v_impact, 0.}}}}}}};
433 json bc_disp = {{
"Sets", 1},
435 {{
"Particle_List", std::vector<size_t>{1}},
436 {
"Direction", std::vector<size_t>{1}},
438 {{
"Type",
"constant"}, {
"Parameters", std::vector<double>{0.}}}},
439 {
"Spatial_Function", {{
"Type",
"constant"}}},
440 {
"Zero_Displacement",
true}}}};
451 plate.
d_geomParams = {-0.5 * W, -0.5 * H, -0.5 * thickness,
452 0.5 * W, 0.5 * H, 0.5 * thickness};
457 impactor.
d_geomParams = {0.5 * Iw, 0., -0.5 * Ih, 0., 0., Ih, 0.};
460 plate.
d_geomParams = {-0.5 * W, -0.5 * H, 0., 0.5 * W, 0.5 * H, 0.};
463 impactor.
d_geomParams = {-0.5 * Iw, -0.5 * Ih, 0., 0.5 * Iw, 0.5 * Ih, 0.};
469 dim3 ? -0.5 * thickness - 1.0e-9 : -1.0e-9,
470 dim3 ? 0.5 * thickness + 1.0e-9 : 1.0e-9);
476 {
"Set_2", dim3 ?
meshSetJson(mesh_impactor, mesh_size)
484 json{{
"Type",
"PMBBond"},
485 {
"Is_Plane_Strain",
false},
486 {
"Horizon", horizon},
492 {
"Compute_From_Classical",
true},
493 {
"Influence_Function",
json{{
"Type", 0}}}});
495 json{{
"Type",
"PDElasticBond"},
496 {
"Is_Plane_Strain",
false},
497 {
"Horizon", horizon},
503 {
"Compute_From_Classical",
true},
504 {
"Influence_Function",
json{{
"Type", 0}}}});
505 material[
"Set_1"][
"Influence_Function"] = {
506 {
"Type", 0}, {
"Parameters", std::vector<double>{1.0}}};
507 material[
"Set_2"][
"Influence_Function"] = {
508 {
"Type", 0}, {
"Parameters", std::vector<double>{1.0}}};
511 const double Rc_abs = Rc_factor * mesh_size;
513 json{{
"Contact_Radius", Rc_abs},
515 {
"Damping_On",
false},
517 {
"Friction_On",
false},
518 {
"Friction_Coeff", 0.0},
520 {
"Beta_n_Factor", 0.0},
522 contact_base[
"Kn"] = Kn;
524 contact[
"Set_1_1"] = contact_base;
525 contact[
"Set_1_2"] = contact_base;
526 contact[
"Set_2_2"] = contact_base;
527 contact[
"Damping_Law"] =
"off";
528 contact[
"Friction_Law"] =
"coulomb_simple";
530 const double cy_imp = 0.5 * H + 0.5 * Ih + gap;
532 pgen[
"Random_Rotation"] =
false;
533 pgen[
"Data"][
"N"] = 2;
534 pgen[
"Data"][
"0"] =
json{{
"x", 0.},
542 pgen[
"Data"][
"1"] =
json{{
"x", 0.},
553 return json{{
"Model", model},
555 {
"Displacement_BC", bc_disp},
562 json{{
"Update_Criteria",
"simple_all"},
563 {
"Search_Factor", 5.0},
564 {
"Search_Interval", 1},
565 {
"Near_Bd_Nodes_Tol", 0.5}})},
566 {
"Particle_Generation", pgen}};
571 const std::filesystem::path &mesh_plate,
572 const std::filesystem::path &mesh_impactor,
double W,
double H,
573 double notch_half,
double notch_w,
double notch_depth,
double Iw,
574 double Ih,
double gap,
double mesh_size,
double horizon,
575 double Rc_factor,
double rho,
double E,
double K,
double G,
double Gc,
576 double v_impact,
double final_time,
size_t num_steps) {
578 json{{
"Dimension", 2},
579 {
"Final_Time", final_time},
580 {
"Time_Steps", num_steps},
581 {
"Discretization_Type",
json{{
"Spatial",
"finite_difference"}, {
"Time",
"velocity_verlet"}}},
582 {
"Populate_ElementNodeConnectivity",
true},
583 {
"Quad_Approximation_Order", 2},
584 {
"Particle_Sim_Type",
"Multi_Particle"},
586 model[
"Self_Contact"] =
"reference_gap";
587 model[
"Bond_Break"] =
"absolute_stretch";
588 model[
"Wall_Contact"] =
"meshed";
591 json{{
"File_Format",
"vtu"},
592 {
"Path", output_path},
593 {
"Tags", std::vector<std::string>({
"Displacement",
"Velocity",
"Force",
"Damage",
"Damage_Bond",
594 "Damage_Z",
"Particle_ID"})},
595 {
"Output_Interval", std::max<size_t>(1, num_steps / 10)},
597 {
"Perform_FE_Out",
false},
598 {
"Compress_Type",
"zlib"},
599 {
"Perform_Out",
true},
600 {
"Test_Output_Interval", num_steps},
602 {
"PVD_Collection",
false}});
607 {{
"Particle_List", std::vector<size_t>{1}},
608 {
"Constant_Velocity",
609 {{
"Velocity_Vector", std::vector<double>{0., -v_impact, 0.}}}}}}};
613 json bc_disp = {{
"Sets", 2}, {
"Set_1", outer[
"Set_1"]}, {
"Set_2", outer[
"Set_2"]}};
620 impactor.
d_geomParams = {-0.5 * Iw, -0.5 * Ih, 0., 0.5 * Iw, 0.5 * Ih, 0.};
628 const double nu_bhat = 1.0 / 3.0;
629 const double c_bhat =
630 6.0 * E / (M_PI * std::pow(horizon, 3.0) * (1.0 - nu_bhat));
631 const double s0_bhat = std::sqrt(4.0 * M_PI * Gc / (9.0 * E * horizon));
635 json{{
"Type",
"PMBBond"},
636 {
"Is_Plane_Strain",
false},
637 {
"Horizon", horizon},
643 {
"Compute_From_Classical",
false},
644 {
"Influence_Function",
json{{
"Type", 0}}}});
645 mat_bhat[
"Bond_Potential_Params"] = std::vector<double>{c_bhat, s0_bhat};
646 mat_bhat[
"Influence_Function"] = {{
"Type", 0}, {
"Parameters", std::vector<double>{1.0}}};
651 json{{
"Type",
"PDElasticBond"},
652 {
"Is_Plane_Strain",
false},
653 {
"Horizon", horizon},
659 {
"Compute_From_Classical",
true},
660 {
"Influence_Function",
json{{
"Type", 0}}}});
661 mat_strike[
"Influence_Function"] = {{
"Type", 0}, {
"Parameters", std::vector<double>{1.0}}};
665 const double Kn_bhat = 18.0 * K / (M_PI * std::pow(horizon, 4.0));
666 const double Rc_abs = Rc_factor * mesh_size;
668 json{{
"Contact_Radius", Rc_abs},
670 {
"Damping_On",
false},
672 {
"Friction_On",
false},
673 {
"Friction_Coeff", 0.0},
675 {
"Beta_n_Factor", 0.0},
677 contact_base[
"Kn"] = Kn_bhat;
679 contact[
"Set_1_1"] = contact_base;
680 contact[
"Set_1_2"] = contact_base;
681 contact[
"Set_2_2"] = contact_base;
682 contact[
"Damping_Law"] =
"off";
683 contact[
"Friction_Law"] =
"coulomb_simple";
684 contact[
"Set_1_1"][
"Friction_Coefficient"] = 0.;
685 contact[
"Set_1_2"][
"Friction_Coefficient"] = 0.;
686 contact[
"Set_2_2"][
"Friction_Coefficient"] = 0.;
688 const double cy_imp = 0.5 * H + 0.5 * Ih + gap;
690 pgen[
"Random_Rotation"] =
false;
691 pgen[
"Data"][
"N"] = 2;
692 pgen[
"Data"][
"0"] =
json{{
"x", 0.}, {
"y", 0.}, {
"z", 0.},
693 {
"theta", 0.}, {
"s", 1.}, {
"geom_id", 0},
694 {
"mat_id", 0}, {
"contact_id", 0}};
695 pgen[
"Data"][
"1"] =
json{{
"x", 0.}, {
"y", cy_imp}, {
"z", 0.},
696 {
"theta", 0.}, {
"s", 1.}, {
"geom_id", 1},
697 {
"mat_id", 1}, {
"contact_id", 1}};
699 return json{{
"Model", model},
701 {
"Displacement_BC", bc_disp},
708 json{{
"Update_Criteria",
"simple_all"},
709 {
"Search_Factor", 5.0},
710 {
"Search_Interval", 1},
711 {
"Near_Bd_Nodes_Tol", 0.5}})},
712 {
"Particle_Generation", pgen}};
717int main(
int argc,
char *argv[]) {
721 unsigned n_threads = 1;
723 n_threads =
static_cast<unsigned>(std::stoi(input.
getCmdOption(
"-nThreads")));
732 namespace fs = std::filesystem;
734 std::string run_tag =
735 quick ?
"quick" : (trask ?
"trask" : (bhat ?
"bhat" : (dim3 ?
"silling3d" :
"lit")));
737 fs::path base = fs::current_path() /
"runs_new" / run_tag;
740 const fs::path out_dir = base /
"out";
741 const fs::path inp_dir = base /
"inp";
742 fs::create_directories(out_dir);
743 fs::create_directories(inp_dir);
744 std::cout << std::format(
"notched_impact: outputDir={}\n", base.string());
749 double notch_depth = 0.050;
750 double notch_half = 0.025;
751 double notch_w = 0.0015;
752 double plate_thickness = 0.009;
760 double mesh_size = 0.001;
761 double horizon = 3.0 * mesh_size;
762 double Rc_factor = 0.95;
768 double gap = 1.5 * Rc_factor * mesh_size;
774 double Kbulk = 159.2e9;
775 double nu = 0.5 * (1.0 - E / (3.0 * Kbulk));
776 double G = E / (2.0 * (1.0 + nu));
778 double v_impact = 32.0;
782 double final_time = 1.7e-4;
786 final_time = std::stod(input.
getCmdOption(
"-finalTime"));
791 horizon = 3.0 * mesh_size;
796 horizon = std::stod(input.
getCmdOption(
"-horizonFactor")) * mesh_size;
801 notch_depth = 0.5 * H;
802 notch_half = 0.125 * W;
803 mesh_size = H / 16.0;
804 notch_w = std::max(mesh_size, 0.1 * notch_half);
807 gap = 1.5 * Rc_factor * mesh_size;
808 horizon = 3.0 * mesh_size;
812 nu = 0.5 * (1.0 - E / (3.0 * Kbulk));
813 G = E / (2.0 * (1.0 + nu));
825 const size_t num_steps =
static_cast<size_t>(std::llround(final_time / dt));
830 inp_dir /
"mesh_plate.msh", W, H, notch_half, notch_w,
831 notch_depth, mesh_size, horizon, rho, E, Kbulk, G, Gc,
832 v_impact, final_time, num_steps);
835 directoryPathWithTrailingSep(out_dir), inp_dir /
"mesh_plate.msh",
836 inp_dir /
"mesh_impactor.msh", W, H, notch_half, notch_w, notch_depth, Iw, Ih, gap,
837 mesh_size, horizon, Rc_factor, rho, E, Kbulk, G, Gc, v_impact, final_time, num_steps);
840 directoryPathWithTrailingSep(out_dir), inp_dir /
"mesh_plate.msh",
841 inp_dir /
"mesh_impactor.msh", W, H, notch_half, notch_w, notch_depth, Iw, Ih, gap,
842 mesh_size, horizon, Rc_factor, Kn, rho, E, Kbulk, G, Gc, v_impact, final_time,
843 num_steps, dim3 ? plate_thickness : 0.);
849 input_json[
"Model"][
"Self_Contact"] = sc;
850 std::cout << std::format(
"notched_impact: Self_Contact override -> {}\n", sc);
854 input_json[
"Model"][
"Bond_Break"] = bb;
855 std::cout << std::format(
"notched_impact: Bond_Break override -> {}\n", bb);
862 static_cast<size_t>(std::stoull(input.
getCmdOption(
"-outInterval")));
863 input_json[
"Output"][
"Output_Interval"] = std::max<size_t>(1, oi);
864 std::cout << std::format(
"notched_impact: Output_Interval override -> {}\n", oi);
867 const double s = std::stod(input.
getCmdOption(
"-knScale"));
868 for (
auto &kv : input_json[
"Contact"].items()) {
869 if (!kv.value().is_object() || !kv.value().contains(
"Kn"))
871 kv.value()[
"Kn"] = kv.value()[
"Kn"].get<
double>() * s;
873 std::cout << std::format(
"notched_impact: Contact Kn scaled by {}\n", s);
876 std::ofstream os(inp_dir /
"input.json");
877 os << input_json.dump(2);
888 auto deck = std::make_shared<inp::Input>(input_json);
892 const double y_top = 0.5 * H;
893 const double y_tip = y_top - notch_depth;
900 std::cerr <<
"notched_impact: expected prenotch bonds, got " << n_pre <<
"\n";
904 const char *mode_str =
906 : (trask ?
"trask_disp"
907 : (bhat ?
"bhat_kw" : (dim3 ?
"silling_3d" :
"silling_impact")));
908 std::cout << std::format(
909 "notched_impact: mode={} nodes={} prenotch={} h={:.4e} ε={:.4e} notch_w={:.4e} "
910 "Gc={:.0f} E={:.3e} v={:.1f} Nt={} T={:.3e}\n",
911 mode_str, dem.
d_x.size(), n_pre, mesh_size, horizon, notch_w, Gc, E, v_impact, num_steps,
917 std::vector<float> arrival(dem.
d_x.size(), -1.f);
918 const size_t sample_every = std::max<size_t>(1, num_steps / 400);
919 auto sampleArrivals = [&]() {
920 for (
size_t i = 0; i < dem.
d_xRef.size(); ++i) {
921 if (arrival[i] >= 0.f || dem.
d_ptId[i] != 0)
924 arrival[i] =
static_cast<float>(dem.
d_time);
945 double vmax = 0., phi_max = 0.;
947 for (
size_t i = 0; i < dem.
d_v.size(); ++i) {
948 vmax = std::max(vmax, dem.
d_v[i].length());
951 const double phi =
nodePhi(dem, i);
952 phi_max = std::max(phi_max, phi);
957 dem.
d_Z.empty() ? 0.f : *std::max_element(dem.
d_Z.begin(), dem.
d_Z.end());
958 std::cout << std::format(
959 "notched_impact diag: max|v|={:.3e} maxφ={:.3f} maxZ={:.3f} n(φ≥0.3)={}\n", vmax,
960 phi_max, zmax, n_phi30);
962 if (!(vmax < 8.0e3) || !std::isfinite(vmax)) {
963 std::cerr <<
"notched_impact: blow-up, max |v| = " << vmax <<
"\n";
968 for (
size_t i = 0; i < dem.
d_neighPd.size(); ++i) {
971 for (
size_t k = 0; k < dem.
d_neighPd[i].size(); ++k) {
977 const auto &xi = dem.
d_xRef[i];
978 const auto &xj = dem.
d_xRef[j];
979 const bool on_notch =
991 std::cerr <<
"notched_impact: expected new broken bonds, got " << n_extra
992 <<
" (prenotch=" << n_pre <<
")\n";
999 std::ofstream csv(base /
"damage.csv");
1000 csv <<
"x_ref,y_ref,z_ref,x_cur,y_cur,z_cur,phi,phi_bond,arrival\n";
1001 for (
size_t i = 0; i < dem.
d_xRef.size(); ++i) {
1006 if (std::max(phi, phi_b) < 0.05)
1008 csv << std::format(
"{:.6e},{:.6e},{:.6e},{:.6e},{:.6e},{:.6e},{:.4f},{:.4f},{:.6e}\n",
1010 dem.
d_x[i].d_x, dem.
d_x[i].d_y, dem.
d_x[i].d_z, phi, phi_b,
1011 double(arrival[i]));
1015 const double band_x = 0.45 * W;
1016 const double band_y = 0.55 * H;
1017 const double phi_cut = quick ? 0.20 : 0.35;
1018 const double exclude_r = 2.0 * mesh_size;
1019 const bool use_bond_dmg = bhat;
1020 auto left =
fitCrackFromPhi(dem, -notch_half, y_tip, -1.0, phi_cut, band_x, band_y, exclude_r,
1021 mesh_size, use_bond_dmg);
1022 auto right =
fitCrackFromPhi(dem, notch_half, y_tip, +1.0, phi_cut, band_x, band_y, exclude_r,
1023 mesh_size, use_bond_dmg);
1024 if (left.n_pts < 4 || right.n_pts < 4) {
1025 std::cerr <<
"notched_impact: insufficient φ-ridge for path fit (L=" << left.n_pts
1026 <<
" R=" << right.n_pts <<
")\n";
1029 if (!(left.mean_dx < 0. && left.mean_dy > 0. && right.mean_dx > 0. && right.mean_dy > 0.)) {
1030 std::cerr << std::format(
1031 "notched_impact: tip damage not outward/down "
1032 "(L dx={:.3e} dy={:.3e} R dx={:.3e} dy={:.3e})\n",
1033 left.mean_dx, left.mean_dy, right.mean_dx, right.mean_dy);
1037 const double ang = 0.5 * (left.angle_to_notch_deg + right.angle_to_notch_deg);
1038 std::cout << std::format(
1039 "notched_impact PASS: prenotch={} new_broken={} ang_to_notch={:.1f}° "
1040 "(L={:.1f}/{} R={:.1f}/{}) max|v|={:.3e}\n",
1041 n_pre, n_extra, ang, left.angle_to_notch_deg, left.n_pts, right.angle_to_notch_deg,
int main(int argc, char *argv[])
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)
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)
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)
bool segmentCrossesVerticalLine(double x0, double y0, double x1, double y1, double xLine, double yLo, double yHi)
Pre-notch by breaking peridynamic bonds.
std::size_t breakBondsCrossingVerticalLines(Fracture &fracture, const std::vector< util::Point > &xRef, const std::vector< std::vector< std::size_t > > &neighbors, const std::vector< std::size_t > &nodeParticleId, std::size_t particleId, const std::vector< double > &xLines, double yLo, double yHi)
Break the bonds of one particle that cross vertical cut lines.
std::size_t breakBondsInSlots(Fracture &fracture, const std::vector< util::Point > &xRef, const std::vector< std::vector< std::size_t > > &neighbors, const std::vector< std::size_t > &nodeParticleId, std::size_t particleId, const std::vector< double > &xCenters, double width, double yLo, double yHi)
Break the bonds of one particle that enter open notch slots.
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 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.
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.
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(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.