PeriDEM 0.3.0
PeriDEM -- Peridynamics-based high-fidelity model for granular media
Loading...
Searching...
No Matches
anonymous_namespace{main.cpp} Namespace Reference

Data Structures

class  ContactWitness
 
struct  CrackFit
 
struct  CrackMetrics
 
class  GrainContactProbe
 
struct  LawResult
 
struct  PackedBounds
 
struct  PackedParticle
 
struct  Probe
 

Functions

std::string directoryPathWithTrailingSep (const std::filesystem::path &dir)
 
json buildInputJson (const std::string &output_path, const std::filesystem::path &mesh_tri, const std::filesystem::path &mesh_ell, double final_time, size_t num_steps, double mesh_size, double horizon)
 
double maxElem (const std::vector< double > &v)
 
bool doesParticleIntersect (const PackedParticle &p, const std::vector< PackedParticle > &existing, const std::vector< double > &rect, double padding)
 
std::vector< PackedParticle > generateParticleLocations (const std::vector< double > &in_rect, double max_y, double mesh_size, double R, int N_target, double padding, std::mt19937 &gen)
 
PackedBounds packedAxisBounds (const std::vector< PackedParticle > &packed)
 
json contactPairJson (double R_contact_factor, bool damping_on, bool friction_on, double Kn, double beta_n_eps, double friction_coeff, double Kn_factor, double beta_n_factor)
 
double KnFromBulk (double Ka, double Kb, double horizon)
 
json buildInputJson (const std::string &output_path_for_deck, const std::filesystem::path &inp_dir, int argc, char *argv[])
 
std::vector< PackedParticle > generateCircularGrid (int ncols, int nrows, double x_lo, double y_lo, double R, double gap)
 
json buildInputJson (const std::string &output_path_for_deck, const std::filesystem::path &mesh_cir, const std::filesystem::path &mesh_fixed, const std::filesystem::path &mesh_moving, double final_time, size_t num_steps, bool file_mesh, bool write_meshes, size_t search_interval, int ncols, int nrows, const std::string &mpi_strategy="auto")
 
void writeLocations (const std::filesystem::path &csv, const std::vector< PackedParticle > &packed)
 
double maxAbsForceFromCsv (const std::filesystem::path &csv)
 
bool ownsNode (const data::ModelData &data, size_t i)
 
void dumpFinalNodal (const data::ModelData &data, const std::filesystem::path &out_dir)
 
json buildInputJson (const std::string &output_path, const std::filesystem::path &mesh_cir, const std::filesystem::path &mesh_wall, const std::string &mpi_strategy, double final_time, size_t num_steps)
 
size_t applyNotches (PeriDEMModel &dem, double x_left, double x_right, double y_lo, double y_hi)
 
size_t applyNotchSlots (PeriDEMModel &dem, double notch_half, double notch_w, double y_lo, double y_hi)
 
size_t applyNotchMidplanes (PeriDEMModel &dem, double notch_half, double y_tip, double y_top)
 
double nodePhi (const PeriDEMModel &dem, size_t i)
 
double nodeDamageForFit (const PeriDEMModel &dem, size_t i, bool use_bond_count)
 
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)
 
json meshSetJson (const std::filesystem::path &f, double mesh_size)
 
json uniformMeshSetJson (const std::filesystem::path &f, double mesh_size, const std::vector< std::vector< double > > &voids={})
 
std::vector< std::vector< double > > notchVoidBoxes (double H, double notch_half, double notch_w, double notch_depth, double z_lo, double z_hi)
 
geom::GeomData sillingNotchedPlateGeom (double W, double H, double notch_half, double notch_w, double notch_depth)
 
geom::GeomData bhatVNotchedPlateGeom (double W, double H, double notch_half, double notch_w, double notch_depth)
 
json fixedTopOuterBC (double W, double H, double notch_half)
 
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)
 
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.)
 
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)
 
json makeDeck (const std::string &model, size_t dim, bool plane_strain, int influence, double L, double h, double horizon, double E, double nu, double Gc, const std::string &out)
 
json buildInputJson (const std::string &output_path, const std::filesystem::path &mesh_file, double L, double mesh_size, double horizon, double final_time, size_t num_steps, const std::string &self_contact)
 
size_t applyPrecrack (PeriDEMModel &dem, double x_crack)
 
void applyRigidOpenClose (PeriDEMModel &dem, double x_crack, double u_right)
 
CrackMetrics measure (PeriDEMModel &dem, double x_crack, double band)
 
LawResult runLaw (const std::string &self_contact, const std::filesystem::path &run_dir, double L, double mesh_size, double horizon, unsigned n_threads, bool deck_only=false)
 
json buildInputJson (const std::string &output_path_for_deck, const std::filesystem::path &mesh_file_1, const std::filesystem::path &mesh_file_2, double final_time, size_t num_steps, bool zero_ic, double mesh_size_in, double horizon_in, bool damping_on, double eps_n, bool two_particle_test, bool file_mesh, double beta_n_factor, const std::string &damping_law="com_and_node", const std::string &friction_law="coulomb_simple", bool friction_on=false, double friction_mu=-1., double ic_vx=0., bool bottom_patch_bc=false, const std::string &mpi_strategy="auto")
 
bool is3DGeometry (const std::string &g)
 
bool isAcceptableGeometryName (const std::string &g)
 
json buildInputJson (const std::string &geomName, const std::string &output_path_for_deck, const std::filesystem::path &mesh_file_1, const std::filesystem::path &mesh_file_2)
 
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)
 
Probe checkWallSpringOnly (PeriDEMModel &dem, geom::GeomObject &wall_geom)
 
double minGrainWallGap (PeriDEMModel &dem, geom::GeomObject &wall_geom)
 
bool near (double a, double b, double tol=1.e-12)
 

Function Documentation

◆ applyNotches()

size_t anonymous_namespace{main.cpp}::applyNotches ( PeriDEMModel &  dem,
double  x_left,
double  x_right,
double  y_lo,
double  y_hi 
)

Definition at line 41 of file main.cpp.

42 {
44 *dem.d_fracture_p, dem.d_xRef, dem.d_neighPd, dem.d_ptId, 0,
45 {x_left, x_right}, y_lo, y_hi);
46}
std::vector< util::Point > d_xRef
reference positions of the nodes
Definition modelData.h:742
std::unique_ptr< geometry::Fracture > d_fracture_p
Fracture state of bonds.
Definition modelData.h:736
std::vector< size_t > d_ptId
Global node to particle id (walls are assigned id after last particle id)
Definition modelData.h:764
std::vector< std::vector< size_t > > d_neighPd
Neighbor data for peridynamic forces.
Definition modelData.h:770
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.
Definition prenotch.cpp:84

References geometry::breakBondsCrossingVerticalLines(), data::ModelData::d_fracture_p, data::ModelData::d_neighPd, data::ModelData::d_ptId, and data::ModelData::d_xRef.

Here is the call graph for this function:

◆ applyNotchMidplanes()

size_t anonymous_namespace{main.cpp}::applyNotchMidplanes ( PeriDEMModel &  dem,
double  notch_half,
double  y_tip,
double  y_top 
)

Definition at line 57 of file main.cpp.

58 {
60 *dem.d_fracture_p, dem.d_xRef, dem.d_neighPd, dem.d_ptId, 0,
61 {-notch_half, notch_half}, y_tip, y_top);
62}

References geometry::breakBondsCrossingVerticalLines(), data::ModelData::d_fracture_p, data::ModelData::d_neighPd, data::ModelData::d_ptId, and data::ModelData::d_xRef.

Referenced by main().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ applyNotchSlots()

size_t anonymous_namespace{main.cpp}::applyNotchSlots ( PeriDEMModel &  dem,
double  notch_half,
double  notch_w,
double  y_lo,
double  y_hi 
)

Definition at line 48 of file main.cpp.

49 {
51 dem.d_neighPd, dem.d_ptId, 0,
52 {-notch_half, notch_half}, notch_w, y_lo,
53 y_hi);
54}
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.
Definition prenotch.cpp:99

References geometry::breakBondsInSlots(), data::ModelData::d_fracture_p, data::ModelData::d_neighPd, data::ModelData::d_ptId, and data::ModelData::d_xRef.

Referenced by main().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ applyPrecrack()

size_t anonymous_namespace{main.cpp}::applyPrecrack ( PeriDEMModel &  dem,
double  x_crack 
)

Definition at line 137 of file main.cpp.

137 {
138 size_t n_broken = 0;
139 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) {
142 const size_t j = dem.d_neighPd[i][k];
143 const auto &xj = dem.d_xRef[j];
144 if ((xi.d_x - x_crack) * (xj.d_x - x_crack) < 0.) {
145 dem.d_fracture_p->setBondState(i, k, true);
146 ++n_broken;
147 }
148 }
149 }
150 return n_broken;
151}

References data::ModelData::d_fracture_p, data::ModelData::d_neighPd, and data::ModelData::d_xRef.

Referenced by runLaw().

Here is the caller graph for this function:

◆ applyRigidOpenClose()

void anonymous_namespace{main.cpp}::applyRigidOpenClose ( PeriDEMModel &  dem,
double  x_crack,
double  u_right 
)

Definition at line 153 of file main.cpp.

153 {
154 for (size_t i = 0; i < dem.d_xRef.size(); ++i) {
155 const double ux = (dem.d_xRef[i].d_x >= x_crack) ? u_right : 0.;
156 dem.d_u[i] = util::Point(ux, 0., 0.);
157 dem.d_v[i] = util::Point();
158 dem.d_x[i] = dem.d_xRef[i] + dem.d_u[i];
159 }
160}
std::vector< util::Point > d_x
Current positions of the nodes.
Definition modelData.h:745
std::vector< util::Point > d_u
Displacement of the nodes.
Definition modelData.h:748
std::vector< util::Point > d_v
Velocity of the nodes.
Definition modelData.h:751
A structure to represent 3d vectors.
Definition point.h:30

References data::ModelData::d_u, data::ModelData::d_v, data::ModelData::d_x, and data::ModelData::d_xRef.

Referenced by runLaw().

Here is the caller graph for this function:

◆ bhatVNotchedPlateGeom()

geom::GeomData anonymous_namespace{main.cpp}::bhatVNotchedPlateGeom ( double  W,
double  H,
double  notch_half,
double  notch_w,
double  notch_depth 
)

Definition at line 241 of file main.cpp.

242 {
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;
247 geom::GeomData plate;
248 plate.d_geomName = "complex";
249 plate.d_geomComplexInfo = {
250 std::vector<std::string>{"rectangle", "triangle", "triangle"},
251 std::vector<std::string>{"plus", "minus", "minus"}};
252 plate.d_geomParams = {
253 // outer plate
254 x0, y0, 0., x1, y1, 0.,
255 // left V: top-left, top-right, tip
256 -notch_half - hw, y_top, 0., -notch_half + hw, y_top, 0., -notch_half, y_tip, 0.,
257 // right V
258 notch_half - hw, y_top, 0., notch_half + hw, y_top, 0., notch_half, y_tip, 0.};
259 return plate;
260}
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.

References geom::GeomData::d_geomComplexInfo, geom::GeomData::d_geomName, and geom::GeomData::d_geomParams.

Referenced by buildBhatInputJson().

Here is the caller graph for this function:

◆ buildBhatInputJson()

json anonymous_namespace{main.cpp}::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 
)

Definition at line 570 of file main.cpp.

576 {
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"},
585 {"Seed", 0}});
586 model["Self_Contact"] = "reference_gap"; // Bhat 2023 §4.4
587 model["Bond_Break"] = "absolute_stretch"; // Bhat §3.1: |s| > |s0|
588 model["Wall_Contact"] = "meshed";
589
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)},
596 {"Debug", 1},
597 {"Perform_FE_Out", false},
598 {"Compress_Type", "zlib"},
599 {"Perform_Out", true},
600 {"Test_Output_Interval", num_steps},
601 {"Tag_PP", ""},
602 {"PVD_Collection", false}});
603
604 json ic = {
605 {"Sets", 1},
606 {"Set_1",
607 {{"Particle_List", std::vector<size_t>{1}},
608 {"Constant_Velocity",
609 {{"Velocity_Vector", std::vector<double>{0., -v_impact, 0.}}}}}}};
610
611 // Paper: only the outer top ligaments are fixed; impactor is free.
612 json outer = fixedTopOuterBC(W, H, notch_half);
613 json bc_disp = {{"Sets", 2}, {"Set_1", outer["Set_1"]}, {"Set_2", outer["Set_2"]}};
614
615 // Bhat specimen: OCC/Gmsh nonconvex plate with V-notch cutouts (§5, Fig. 4).
616 geom::GeomData plate =
617 bhatVNotchedPlateGeom(W, H, notch_half, notch_w, notch_depth);
618 geom::GeomData impactor;
619 impactor.d_geomName = "rectangle";
620 impactor.d_geomParams = {-0.5 * Iw, -0.5 * Ih, 0., 0.5 * Iw, 0.5 * Ih, 0.};
622
623 json mesh = {{"Sets", 2},
624 {"Set_1", meshSetJson(mesh_plate, mesh_size)},
625 {"Set_2", meshSetJson(mesh_impactor, mesh_size)}};
626
627 // Bhat Table 1, constant micromodulus, ν = 1/3 (bond-based, 2D).
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));
632
634 auto mat_bhat = inp::MaterialDeck::getExampleJson(
635 json{{"Type", "PMBBond"},
636 {"Is_Plane_Strain", false},
637 {"Horizon", horizon},
638 {"Density", rho},
639 {"K", K},
640 {"G", G},
641 {"Gc", Gc},
642 {"E", E},
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}}};
647 material["Set_1"] = mat_bhat;
648 // Free deformable striker (paper). Unbreakable elastic so the contact face
649 // delivers bulk impulse; plate remains PMB with paper (c, s0).
650 auto mat_strike = inp::MaterialDeck::getExampleJson(
651 json{{"Type", "PDElasticBond"},
652 {"Is_Plane_Strain", false},
653 {"Horizon", horizon},
654 {"Density", rho},
655 {"K", K},
656 {"G", G},
657 {"Gc", 0.},
658 {"E", E},
659 {"Compute_From_Classical", true},
660 {"Influence_Function", json{{"Type", 0}}}});
661 mat_strike["Influence_Function"] = {{"Type", 0}, {"Parameters", std::vector<double>{1.0}}};
662 material["Set_2"] = mat_strike;
663
664 // Absolute Rc from mesh_size (not Factor×hMin).
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},
669 {"Kn", Kn_bhat},
670 {"Damping_On", false},
671 {"Epsilon", 1.0},
672 {"Friction_On", false},
673 {"Friction_Coeff", 0.0},
674 {"Kn_Factor", 1.0},
675 {"Beta_n_Factor", 0.0},
676 {"K", K}});
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"; // Bhat §6: β_d = 0
683 contact["Friction_Law"] = "coulomb_simple"; // μ = 0 below
684 contact["Set_1_1"]["Friction_Coefficient"] = 0.;
685 contact["Set_1_2"]["Friction_Coefficient"] = 0.;
686 contact["Set_2_2"]["Friction_Coefficient"] = 0.;
687
688 const double cy_imp = 0.5 * H + 0.5 * Ih + gap;
689 auto pgen = inp::PGenDeck::getExampleJson(json{{"Method", "From_File"}});
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}};
698
699 return json{{"Model", model},
700 {"Output", output},
701 {"Displacement_BC", bc_disp},
702 {"IC", ic},
703 {"Particle", particle},
704 {"Mesh", mesh},
705 {"Material", material},
706 {"Contact", contact},
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}};
713}
nlohmann::ordered_json json
json meshSetJson(const std::filesystem::path &f, double mesh_size)
Definition main.cpp:183
geom::GeomData bhatVNotchedPlateGeom(double W, double H, double notch_half, double notch_w, double notch_depth)
Definition main.cpp:241
json fixedTopOuterBC(double W, double H, double notch_half)
Definition main.cpp:262
Collection of methods and data related to finite element and mesh.
Definition mesh.cpp:29
Collection of methods and data related to particle object.
Definition modelData.h:36
float H
Definition problem.py:40
float K
Definition problem.py:43
float E
Definition problem.py:64
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.
Definition modelDeck.h:217
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.
Definition pGenDeck.h:82
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.

References bhatVNotchedPlateGeom(), geom::GeomData::d_geomName, geom::GeomData::d_geomParams, fixedTopOuterBC(), inp::ContactPairDeck::getExampleJson(), inp::MaterialDeck::getExampleJson(), inp::ModelDeck::getExampleJson(), inp::PGenDeck::getExampleJson(), inp::PNeighborDeck::getExampleJson(), inp::OutputDeck::getExampleJson(), inp::ParticleDeck::getParticleContactExampleJson(), inp::ParticleDeck::getParticleGeomExampleJson(), inp::ParticleDeck::getParticleMaterialExampleJson(), and meshSetJson().

Referenced by main().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ buildImpactInputJson()

json anonymous_namespace{main.cpp}::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. 
)

Definition at line 377 of file main.cpp.

384 {
385 // Silling 2003 EMU KW: explicit central difference; brittle-microelastic PMB;
386 // broken bond force = 0; plate boundaries load-free (no Displacement_BC);
387 // rigid impactor of finite mass.
388 // thickness > 0 runs his actual 3D case (200 x 100 x 9 grid, 9 mm plate).
389 const bool dim3 = thickness > 0.;
390 const size_t dim = dim3 ? 3 : 2;
391 // Element-node connectivity is only used for strain/stress output and does not
392 // support hexahedra, which is what the 3D structured grid produces.
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"},
401 {"Seed", 0}});
402 model["Self_Contact"] = "none";
403 model["Bond_Break"] = "tension";
404 model["Wall_Contact"] = "meshed";
405 // Silling Fig. 2: rigid cylinder of 1.57 kg. In 2D the analogue is the mass
406 // per unit thickness of his 9 mm plate, M/t = 1.57/0.009 kg/m.
407 model["Rigid_Particles"] = json::array(
408 {json{{"Id", 1}, {"Mass", dim3 ? 1.57 : 1.57 / 0.009}}});
409
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)},
416 {"Debug", 1},
417 {"Perform_FE_Out", false},
418 {"Compress_Type", "zlib"},
419 {"Perform_Out", true},
420 {"Test_Output_Interval", num_steps},
421 {"Tag_PP", ""},
422 {"PVD_Collection", false}});
423
424 json ic = {
425 {"Sets", 1},
426 {"Set_1",
427 {{"Particle_List", std::vector<size_t>{1}},
428 {"Constant_Velocity",
429 {{"Velocity_Vector", std::vector<double>{0., -v_impact, 0.}}}}}}};
430
431 // The impactor is rigid but of finite mass (Model.Rigid_Particles above), so
432 // it decelerates on contact instead of being driven through the plate.
433 json bc_disp = {{"Sets", 1},
434 {"Set_1",
435 {{"Particle_List", std::vector<size_t>{1}},
436 {"Direction", std::vector<size_t>{1}},
437 {"Time_Function",
438 {{"Type", "constant"}, {"Parameters", std::vector<double>{0.}}}},
439 {"Spatial_Function", {{"Type", "constant"}}},
440 {"Zero_Displacement", true}}}};
441
442 // Plate is the full box on a structured grid with the two 1.5 mm notch slots
443 // removed from the mesh, so they are real gaps (Fig. 2) rather than material
444 // whose bonds have been cut.
445 geom::GeomData plate;
446 geom::GeomData impactor;
447 if (dim3) {
448 // Keep both bodies centred on z = 0 so particle generation places them
449 // consistently (it positions objects by their centre).
450 plate.d_geomName = "cuboid";
451 plate.d_geomParams = {-0.5 * W, -0.5 * H, -0.5 * thickness,
452 0.5 * W, 0.5 * H, 0.5 * thickness};
453 // Silling Fig. 4: the cylinder axis is along the impact direction and its
454 // flat end face strikes the plate edge. Params: radius, centre of the
455 // beginning cross-section, and the vector to the end cross-section.
456 impactor.d_geomName = "cylinder";
457 impactor.d_geomParams = {0.5 * Iw, 0., -0.5 * Ih, 0., 0., Ih, 0.};
458 } else {
459 plate.d_geomName = "rectangle";
460 plate.d_geomParams = {-0.5 * W, -0.5 * H, 0., 0.5 * W, 0.5 * H, 0.};
461 // In-plane section of that same cylinder: Iw wide, Ih long, flat face down.
462 impactor.d_geomName = "rectangle";
463 impactor.d_geomParams = {-0.5 * Iw, -0.5 * Ih, 0., 0.5 * Iw, 0.5 * Ih, 0.};
464 }
466
467 const auto voids =
468 notchVoidBoxes(H, notch_half, notch_w, notch_depth,
469 dim3 ? -0.5 * thickness - 1.0e-9 : -1.0e-9,
470 dim3 ? 0.5 * thickness + 1.0e-9 : 1.0e-9);
471 // Plate on Silling's equally spaced structured grid. In 2D the impactor is a
472 // rectangle so it goes on the same grid; in 3D it is a cylinder, which the
473 // uniform grid cannot represent, so it is meshed by Gmsh at the same spacing.
474 json mesh = {{"Sets", 2},
475 {"Set_1", uniformMeshSetJson(mesh_plate, mesh_size, voids)},
476 {"Set_2", dim3 ? meshSetJson(mesh_impactor, mesh_size)
477 : uniformMeshSetJson(mesh_impactor, mesh_size)}};
478
479 // Impactor is steel like the plate but unbreakable, and its nodal forces are
480 // replaced by the rigid-body acceleration each step, so its bond stiffness
481 // never enters the solution.
484 json{{"Type", "PMBBond"},
485 {"Is_Plane_Strain", false},
486 {"Horizon", horizon},
487 {"Density", rho},
488 {"K", K},
489 {"G", G},
490 {"Gc", Gc},
491 {"E", E},
492 {"Compute_From_Classical", true},
493 {"Influence_Function", json{{"Type", 0}}}});
495 json{{"Type", "PDElasticBond"},
496 {"Is_Plane_Strain", false},
497 {"Horizon", horizon},
498 {"Density", rho},
499 {"K", K},
500 {"G", G},
501 {"Gc", 0.},
502 {"E", E},
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}}};
509
510 // Absolute Contact_Radius from the requested mesh size (not Factor×hMin).
511 const double Rc_abs = Rc_factor * mesh_size;
513 json{{"Contact_Radius", Rc_abs},
514 {"Kn", Kn},
515 {"Damping_On", false},
516 {"Epsilon", 1.0},
517 {"Friction_On", false},
518 {"Friction_Coeff", 0.0},
519 {"Kn_Factor", 1.0},
520 {"Beta_n_Factor", 0.0},
521 {"K", K}});
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";
529
530 const double cy_imp = 0.5 * H + 0.5 * Ih + gap; // flat face `gap` above the edge
531 auto pgen = inp::PGenDeck::getExampleJson(json{{"Method", "From_File"}});
532 pgen["Random_Rotation"] = false;
533 pgen["Data"]["N"] = 2;
534 pgen["Data"]["0"] = json{{"x", 0.},
535 {"y", 0.},
536 {"z", 0.},
537 {"theta", 0.},
538 {"s", 1.},
539 {"geom_id", 0},
540 {"mat_id", 0},
541 {"contact_id", 0}};
542 pgen["Data"]["1"] = json{{"x", 0.},
543 {"y", cy_imp},
544 {"z", 0.},
545 {"theta", 0.},
546 {"s", 1.},
547 {"geom_id", 1},
548 {"mat_id", 1},
549 {"contact_id", 1}};
550
551 // Plate has no Displacement_BC (Silling: load-free all around); the only
552 // Displacement_BC drives the rigid impactor.
553 return json{{"Model", model},
554 {"Output", output},
555 {"Displacement_BC", bc_disp},
556 {"IC", ic},
557 {"Particle", particle},
558 {"Mesh", mesh},
559 {"Material", material},
560 {"Contact", contact},
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}};
567}
std::vector< std::vector< double > > notchVoidBoxes(double H, double notch_half, double notch_w, double notch_depth, double z_lo, double z_hi)
Definition main.cpp:207
json uniformMeshSetJson(const std::filesystem::path &f, double mesh_size, const std::vector< std::vector< double > > &voids={})
Definition main.cpp:195
float W
Definition problem.py:39

References geom::GeomData::d_geomName, geom::GeomData::d_geomParams, inp::ContactPairDeck::getExampleJson(), inp::MaterialDeck::getExampleJson(), inp::ModelDeck::getExampleJson(), inp::PGenDeck::getExampleJson(), inp::PNeighborDeck::getExampleJson(), inp::OutputDeck::getExampleJson(), inp::ParticleDeck::getParticleContactExampleJson(), inp::ParticleDeck::getParticleGeomExampleJson(), inp::ParticleDeck::getParticleMaterialExampleJson(), meshSetJson(), notchVoidBoxes(), and uniformMeshSetJson().

Referenced by main().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ buildInputJson() [1/8]

json anonymous_namespace{main.cpp}::buildInputJson ( const std::string &  geomName,
const std::string &  output_path_for_deck,
const std::filesystem::path &  mesh_file_1,
const std::filesystem::path &  mesh_file_2 
)

Definition at line 65 of file main.cpp.

67 {
68
69 const double s = 0.001;
70 const double mesh_size = s / 5.0;
71 const double horizon = 3.0 * mesh_size;
72 const double particle_dist = 0.001;
73
74 const double poisson1 = 0.25;
75 const double rho1 = 1200.0;
76 const double K1 = 2.16e+7;
77 const double E1 = material::toE(K1, poisson1);
78 const double G1 = material::toGE(E1, poisson1);
79 const double Gc1 = 50.0;
80
81 const double poisson2 = 0.25;
82 const double rho2 = 1200.0;
83 const double K2 = 2.16e+7;
84 const double E2 = material::toE(K2, poisson2);
85 const double G2 = material::toGE(E2, poisson2);
86 const double Gc2 = 50.0;
87
88 const double R_contact_factor = 0.95;
89 const double Kn_11 = util::normalContactStiffness(K1, K1, horizon);
90 const double Kn_22 = util::normalContactStiffness(K2, K2, horizon);
91 const double Kn_12 = util::normalContactStiffness(K1, K2, horizon);
92 const double beta_n_eps = 0.9;
93 const double friction_coeff = 0.5;
94 const double beta_n_factor = 100.0;
95
96 const int model_dim = is3DGeometry(geomName) ? 3 : 2;
97
98 /* Reference shapes at origin for both mesh groups (same as twop_circ_inbuilt). */
99 const util::Point origin(0.0, 0.0, 0.0);
100 const std::vector<double> gp_ref = geom::exampleGeomParams(geomName, origin, s);
101
102 /* Same integration window and IC gap as twop_circ_inbuilt (stable dt). */
103 const double final_time = 0.0001; // 0.012;
104 const size_t num_steps = 1000; //36000;
105 const size_t dt_out_n = num_steps / 4; // 10;
106
107 auto modelDeckJson = inp::ModelDeck::getExampleJson(
108 json{{"Dimension", static_cast<size_t>(model_dim)},
109 {"Final_Time", final_time},
110 {"Time_Steps", num_steps},
111 {"Discretization_Type", json{{"Spatial", "finite_difference"}, {"Time", "central_difference"}}},
112 {"Populate_ElementNodeConnectivity", true},
113 {"Quad_Approximation_Order", 2},
114 {"Particle_Sim_Type", "Multi_Particle"},
115 {"Seed", 0}});
116
117 auto outputDeckJson = inp::OutputDeck::getExampleJson(
118 json{{"File_Format", "vtu"},
119 {"Path", output_path_for_deck},
120 {"Tags", std::vector<std::string>({"Displacement", "Velocity", "Force", "Damage_Z", "Damage", "Particle_ID"})},
121 {"Output_Interval", dt_out_n},
122 {"Debug", 2},
123 {"Perform_FE_Out", true},
124 {"Compress_Type", "zlib"},
125 {"Perform_Out", true},
126 {"Test_Output_Interval", 1},
127 {"Tag_PP", ""},
128 {"PVD_Collection", true}});
129
130 auto bcDeckJson = inp::BCDeck::getExampleJson(
131 json{{"Displacement_BC_Sets", 1},
132 {"IC_Sets", 1},
133 {"Gravity", util::Point(0, -10, 0).toVec()}});
134
135 bcDeckJson["Displacement_BC"]["Set_1"] = inp::BCBaseDeck::getExampleJson(
136 json{{"Type", "Displacement_BC"},
137 {"Particle_List", {0}},
138 {"Direction", {1, 2}},
139 {"Zero_Displacement", true}});
140
141 const double free_fall_dist = particle_dist - horizon;
142 const double free_fall_vel = -std::sqrt(2.0 * std::abs(-10.0) * std::max(1e-30, free_fall_dist));
143 bcDeckJson["IC"]["Set_1"] = inp::BCBaseDeck::getExampleJson(
144 json{{"Type", "IC"},
145 {"Particle_List", {1}},
146 {"IC_Type", "Constant_Velocity"},
147 {"IC_Vector", {0.0, free_fall_vel, 0.0}}});
148
149 auto pDeckJson = json({});
150
151 std::vector<geom::GeomData> pGeomVec(2);
152 pGeomVec[0].d_geomName = geomName;
153 pGeomVec[0].d_geomParams = gp_ref;
154 pGeomVec[1].d_geomName = geomName;
155 pGeomVec[1].d_geomParams = gp_ref;
156
157 pDeckJson["Particle"] = inp::ParticleDeck::getParticleGeomExampleJson(pGeomVec);
158
159 const std::string f1 = mesh_file_1.string();
160 const std::string f2 = mesh_file_2.string();
161 auto meshSet1 = json({{"File", f1},
162 {"CreateMesh",
163 {{"Flag", true},
164 {"Info", "gmsh_builtin_mesh"},
165 {"Mesh_Size", mesh_size},
166 {"Write_Mesh_File", true}}}});
167 auto meshSet2 = json({{"File", f2},
168 {"CreateMesh",
169 {{"Flag", true},
170 {"Info", "gmsh_builtin_mesh"},
171 {"Mesh_Size", mesh_size},
172 {"Write_Mesh_File", true}}}});
173 pDeckJson["Mesh"] = json({{"Sets", 2}, {"Set_1", meshSet1}, {"Set_2", meshSet2}});
174
176 pMatJson["Set_1"] = inp::MaterialDeck::getExampleJson(
177 json{{"Type", "PDState"},
178 {"Is_Plane_Strain", false},
179 {"Horizon", horizon},
180 {"Density", rho1},
181 {"K", K1},
182 {"G", G1},
183 {"Gc", Gc1},
184 {"Compute_From_Classical", true},
185 {"Influence_Function", json{{"Type", 0}}}});
186 pMatJson["Set_2"] = inp::MaterialDeck::getExampleJson(
187 json{{"Type", "PDState"},
188 {"Is_Plane_Strain", false},
189 {"Horizon", horizon},
190 {"Density", rho2},
191 {"K", K2},
192 {"G", G2},
193 {"Gc", Gc2},
194 {"Compute_From_Classical", true},
195 {"Influence_Function", json{{"Type", 0}}}});
196 pDeckJson["Material"] = pMatJson;
197
200 json{{"Contact_Radius_Factor", R_contact_factor},
201 {"Kn", Kn_11},
202 {"Damping_On", false},
203 {"Epsilon", beta_n_eps},
204 {"Friction_On", false},
205 {"Friction_Coeff", friction_coeff},
206 {"Kn_Factor", 1.0},
207 {"Beta_n_Factor", beta_n_factor},
208 {"K", 0.0}});
209
210 pContactJson["Set_1_1"] = contact_base;
211 pContactJson["Set_1_1"]["Kn"] = Kn_11;
212 pContactJson["Set_1_2"] = contact_base;
213 pContactJson["Set_1_2"]["Kn"] = Kn_12;
214 pContactJson["Set_2_2"] = contact_base;
215 pContactJson["Set_2_2"]["Kn"] = Kn_22;
216 pDeckJson["Contact"] = pContactJson;
217
218 pDeckJson["Neighbor"] = inp::PNeighborDeck::getExampleJson(
219 json{{"Update_Criteria", "simple_all"},
220 {"Search_Factor", 10.0},
221 {"Search_Interval", 40},
222 {"Near_Bd_Nodes_Tol", 0.5}});
223
224 auto pGenJson = inp::PGenDeck::getExampleJson(json{{"Method", "From_File"}});
225 pGenJson["Data"]["N"] = 2;
226
227 /*
228 * Placement matches twop_circ: lower particle corner at (s,s[,s]); upper at
229 * (s, 2*s + particle_dist + s, [s]) so vertical gap is particle_dist between
230 * outer extents (same as R1=R2=s in twop_circ).
231 */
232 if (model_dim == 2) {
233 pGenJson["Data"]["0"] = {
234 {"x", s}, {"y", s}, {"z", 0.0},
235 {"theta", 0.0}, {"s", 1.0},
236 {"ax", 0.0}, {"ay", 0.0}, {"az", 1.0},
237 {"geom_id", 0}, {"mat_id", 0}, {"contact_id", 0}
238 };
239 pGenJson["Data"]["1"] = {
240 {"x", s}, {"y", 2.0 * s + particle_dist + s}, {"z", 0.0},
241 {"theta", M_PI}, {"s", 1.0},
242 {"ax", 0.0}, {"ay", 0.0}, {"az", 1.0},
243 {"geom_id", 1}, {"mat_id", 1}, {"contact_id", 1}
244 };
245 } else {
246 pGenJson["Data"]["0"] = {
247 {"x", s}, {"y", s}, {"z", s},
248 {"theta", 0.0}, {"s", 1.0},
249 {"ax", 0.0}, {"ay", 0.0}, {"az", 1.0},
250 {"geom_id", 0}, {"mat_id", 0}, {"contact_id", 0}
251 };
252 pGenJson["Data"]["1"] = {
253 {"x", s}, {"y", 2.0 * s + particle_dist + s}, {"z", s},
254 {"theta", M_PI}, {"s", 1.0},
255 {"ax", 0.0}, {"ay", 0.0}, {"az", 1.0},
256 {"geom_id", 1}, {"mat_id", 1}, {"contact_id", 1}
257 };
258 }
259 pDeckJson["Particle_Generation"] = pGenJson;
260
261 return json({{"Model", modelDeckJson},
262 {"Output", outputDeckJson},
263 {"Force_BC", bcDeckJson["Force_BC"]},
264 {"Displacement_BC", bcDeckJson["Displacement_BC"]},
265 {"IC", bcDeckJson["IC"]},
266 {"Particle", pDeckJson["Particle"]},
267 {"Mesh", pDeckJson["Mesh"]},
268 {"Material", pDeckJson["Material"]},
269 {"Contact", pDeckJson["Contact"]},
270 {"Neighbor", pDeckJson["Neighbor"]},
271 {"Particle_Generation", pDeckJson["Particle_Generation"]}});
272}
bool is3DGeometry(const std::string &g)
Definition main.cpp:52
std::vector< double > exampleGeomParams(const std::string &geom_type, const util::Point &c, double s)
Canonical parameter vector for examples, mesh generation tests, and demos.
double toGE(double E, double nu)
Compute shear modulus from Young's modulus E and Poisson's ratio nu.
double toE(double K, double nu)
Compute Young's modulus E from Bulk modulus K and Poisson's ratio nu.
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.
Definition function.cpp:139
static json getExampleJson(const json &given=json::object(), const geom::GeomData *region=nullptr)
Returns example JSON object for ModelDeck configuration.
Definition bcBaseDeck.h:233
static json getExampleJson(const json &given=json::object())
Returns example JSON object for ModelDeck configuration.
Definition bcDeck.h:79
std::vector< double > toVec() const
Returns coordinate in stl form.
Definition point.h:118

References geom::exampleGeomParams(), inp::BCDeck::getExampleJson(), inp::ContactPairDeck::getExampleJson(), inp::MaterialDeck::getExampleJson(), inp::ModelDeck::getExampleJson(), inp::PGenDeck::getExampleJson(), inp::PNeighborDeck::getExampleJson(), inp::BCBaseDeck::getExampleJson(), inp::OutputDeck::getExampleJson(), inp::ParticleDeck::getParticleContactExampleJson(), inp::ParticleDeck::getParticleGeomExampleJson(), inp::ParticleDeck::getParticleMaterialExampleJson(), is3DGeometry(), util::normalContactStiffness(), material::toE(), material::toGE(), and util::Point::toVec().

Referenced by main().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ buildInputJson() [2/8]

json anonymous_namespace{main.cpp}::buildInputJson ( const std::string &  output_path,
const std::filesystem::path &  mesh_cir,
const std::filesystem::path &  mesh_wall,
const std::string &  mpi_strategy,
double  final_time,
size_t  num_steps 
)

Definition at line 202 of file main.cpp.

206 {
207 const double R = 0.001;
208 const double mesh_size = R / 4.0;
209 const double horizon = 3.0 * mesh_size;
210 const double h_est = 0.7 * mesh_size;
211 const double Rc_est = 0.95 * h_est;
212
213 // Start inside PP Rc and near the floor; soft Kn (below) keeps contact stable.
214 const double gap_pp = 0.5 * Rc_est;
215 const double cy = R + 0.35 * Rc_est;
216 const double cx0 = -(R + 0.5 * gap_pp);
217 const double cx1 = +(R + 0.5 * gap_pp);
218
219 const double wall_half = 3.5 * R;
220 const double wall_thick = std::max(2.0 * mesh_size, horizon);
221
222 geom::GeomData grain;
223 grain.d_geomName = "circle";
224 grain.d_geomParams = {R, 0., 0., 0.};
225 geom::GeomData floor;
226 floor.d_geomName = "rectangle";
227 floor.d_geomParams = {-wall_half, -wall_thick, 0., wall_half, 0., 0.};
228
229 const double poisson = 0.25;
230 const double rho = 1200.0;
231 const double K = 2.16e7;
232 const double E = material::toE(K, poisson);
233 const double G = material::toGE(E, poisson);
234 const double Gc = 50.0;
235 const double Kn =
236 util::normalContactStiffness(K, K, horizon);
237
239 json{{"Dimension", 2},
240 {"Final_Time", final_time},
241 {"Time_Steps", num_steps},
242 {"Discretization_Type", json{{"Spatial", "finite_difference"}, {"Time", "central_difference"}}},
243 {"Populate_ElementNodeConnectivity", true},
244 {"Quad_Approximation_Order", 2},
245 {"Particle_Sim_Type", "Multi_Particle"},
246 {"Seed", 0}});
247 model["MPI_Strategy"] = mpi_strategy;
248 model["Wall_Contact"] = "meshed";
249
251 json{{"File_Format", "vtu"},
252 {"Path", output_path},
253 {"Tags", std::vector<std::string>({"Displacement", "Velocity", "Force",
254 "Particle_ID"})},
255 {"Output_Interval", std::max<size_t>(1, num_steps)},
256 {"Debug", 1},
257 {"Perform_FE_Out", false},
258 {"Compress_Type", "zlib"},
259 {"Perform_Out", false},
260 {"Test_Output_Interval", num_steps},
261 {"Tag_PP", ""},
262 {"PVD_Collection", false}});
263
264 // Wall (particle index 2) fully fixed; downward IC on both grains.
266 json{{"Displacement_BC_Sets", 1},
267 {"IC_Sets", 1},
268 {"Gravity", util::Point(0, -10, 0).toVec()}});
269 bc["Displacement_BC"]["Set_1"] = json{
270 {"Particle_List", std::vector<size_t>{2}},
271 {"Direction", std::vector<size_t>{1, 2}},
272 {"Time_Function",
273 {{"Type", "constant"}, {"Parameters", std::vector<double>{0.}}}},
274 {"Spatial_Function", {{"Type", "constant"}}},
275 {"Zero_Displacement", true}};
276 bc["IC"]["Set_1"] = inp::BCBaseDeck::getExampleJson(
277 json{{"Type", "IC"},
278 {"Particle_List", {0, 1}},
279 {"IC_Type", "Constant_Velocity"},
280 {"IC_Vector", {0., -0.02, 0.}}});
281
283 auto mesh_set = [&](const std::filesystem::path &f) {
284 return json{{"File", f.string()},
285 {"CreateMesh",
286 {{"Flag", true},
287 {"Info", "gmsh_builtin_mesh"},
288 {"Mesh_Size", mesh_size},
289 {"Write_Mesh_File", true}}}};
290 };
291 json mesh = {{"Sets", 2},
292 {"Set_1", mesh_set(mesh_cir)},
293 {"Set_2", mesh_set(mesh_wall)}};
294
297 json{{"Type", "PDState"},
298 {"Is_Plane_Strain", false},
299 {"Horizon", horizon},
300 {"Density", rho},
301 {"K", K},
302 {"G", G},
303 {"Gc", Gc},
304 {"Compute_From_Classical", true},
305 {"Influence_Function", json{{"Type", 0}}}});
307 json{{"Type", "PDState"},
308 {"Is_Plane_Strain", false},
309 {"Horizon", horizon},
310 {"Density", rho},
311 {"K", K},
312 {"G", G},
313 {"Gc", Gc},
314 {"Compute_From_Classical", true},
315 {"Influence_Function", json{{"Type", 0}}}});
316
318 json{{"Contact_Radius_Factor", 0.95},
319 {"Kn", Kn},
320 {"Damping_On", false},
321 {"Epsilon", 0.95},
322 {"Friction_On", false},
323 {"Friction_Coeff", 0.0},
324 {"Kn_Factor", 1.0},
325 {"Beta_n_Factor", 100.0},
326 {"K", K}});
327 // Soften contact spring so short MPI identity runs stay stable and bit-reproducible.
328 contact_base["Kn_Factor"] = 1.0e-4;
330 contact["Set_1_1"] = contact_base;
331 contact["Set_1_2"] = contact_base;
332 contact["Set_2_2"] = contact_base;
333 contact["Damping_Law"] = "off";
334 contact["Friction_Law"] = "coulomb_simple";
335
336 auto pgen = inp::PGenDeck::getExampleJson(json{{"Method", "From_File"}});
337 pgen["Random_Rotation"] = false;
338 pgen["Data"]["N"] = 3;
339 pgen["Data"]["0"] = json{{"x", cx0},
340 {"y", cy},
341 {"z", 0.},
342 {"theta", 0.},
343 {"s", 1.},
344 {"geom_id", 0},
345 {"mat_id", 0},
346 {"contact_id", 0}};
347 pgen["Data"]["1"] = json{{"x", cx1},
348 {"y", cy},
349 {"z", 0.},
350 {"theta", 0.},
351 {"s", 1.},
352 {"geom_id", 0},
353 {"mat_id", 0},
354 {"contact_id", 0}};
355 pgen["Data"]["2"] = json{{"x", 0.},
356 {"y", -0.5 * wall_thick},
357 {"z", 0.},
358 {"theta", 0.},
359 {"s", 1.},
360 {"geom_id", 1},
361 {"mat_id", 1},
362 {"contact_id", 1},
363 {"is_wall", true}};
364
365 util::io::print(std::format(
366 "mpi_identity_twop_wall: R={:.4g} mesh={:.4g} Rc~{:.4g} gap_pp={:.4g} "
367 "cy={:.4g} T={:.3g} N={}\n",
368 R, mesh_size, Rc_est, gap_pp, cy, final_time, num_steps));
369
370 return json{{"Model", model},
371 {"Output", output},
372 {"Force_BC", bc["Force_BC"]},
373 {"Displacement_BC", bc["Displacement_BC"]},
374 {"IC", bc["IC"]},
375 {"Particle", particle},
376 {"Mesh", mesh},
377 {"Material", material},
378 {"Contact", contact},
379 {"Neighbor",
381 json{{"Update_Criteria", "simple_all"},
382 {"Search_Factor", 5.0},
383 {"Search_Interval", 1},
384 {"Near_Bd_Nodes_Tol", 0.5}})},
385 {"Particle_Generation", pgen}};
386}
void print(const T &msg, int nt=print_default_tab, int printMpiRank=print_default_mpi_rank)
Prints formatted information.
Definition io.h:171

References geom::GeomData::d_geomName, geom::GeomData::d_geomParams, inp::BCDeck::getExampleJson(), inp::ContactPairDeck::getExampleJson(), inp::MaterialDeck::getExampleJson(), inp::ModelDeck::getExampleJson(), inp::PGenDeck::getExampleJson(), inp::PNeighborDeck::getExampleJson(), inp::BCBaseDeck::getExampleJson(), inp::OutputDeck::getExampleJson(), inp::ParticleDeck::getParticleContactExampleJson(), inp::ParticleDeck::getParticleGeomExampleJson(), inp::ParticleDeck::getParticleMaterialExampleJson(), util::normalContactStiffness(), util::io::print(), material::toE(), material::toGE(), and util::Point::toVec().

Referenced by main().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ buildInputJson() [3/8]

json anonymous_namespace{main.cpp}::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 
)

Definition at line 44 of file main.cpp.

49 {
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"},
58 {"Seed", 0}});
59 model["Wall_Contact"] = "analytical_plane";
60 if (policy_combo) {
61 model["Self_Contact"] = "reference_gap";
62 model["Bond_Break"] = "absolute_stretch";
63 }
64
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)},
70 {"Debug", 1},
71 {"Perform_FE_Out", false},
72 {"Compress_Type", "zlib"},
73 {"Perform_Out", false},
74 {"Test_Output_Interval", num_steps},
75 {"Tag_PP", ""},
76 {"PVD_Collection", false}});
77
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}};
86
87 const double wall_half = 3.0 * R;
88 const double wall_thick = mesh_size;
89 geom::GeomData grain;
90 grain.d_geomName = "circle";
91 grain.d_geomParams = {R, 0., 0., 0.};
92 geom::GeomData floor;
93 floor.d_geomName = "rectangle";
94 floor.d_geomParams = {-wall_half, -wall_thick, 0., wall_half, 0., 0.};
95
97
98 auto mesh_set = [&](const std::filesystem::path &f) {
99 return json{{"File", f.string()},
100 {"CreateMesh",
101 {{"Flag", true},
102 {"Info", "gmsh_builtin_mesh"},
103 {"Mesh_Size", mesh_size},
104 {"Write_Mesh_File", true}}}};
105 };
106 json mesh = {{"Sets", 2},
107 {"Set_1", mesh_set(mesh_cir)},
108 {"Set_2", mesh_set(mesh_wall)}};
109
110 const double rho = 1200.0;
111 const double K = 2.16e7;
112 const double G = 1.296e7;
113 const double Gc = 50.0;
115 material["Set_1"] =
117 json{{"Type", "PDState"},
118 {"Is_Plane_Strain", false},
119 {"Horizon", horizon},
120 {"Density", rho},
121 {"K", K},
122 {"G", G},
123 {"Gc", Gc},
124 {"Compute_From_Classical", true},
125 {"Influence_Function", json{{"Type", 0}}}});
126 material["Set_2"] =
128 json{{"Type", "PDState"},
129 {"Is_Plane_Strain", false},
130 {"Horizon", horizon},
131 {"Density", rho},
132 {"K", K},
133 {"G", G},
134 {"Gc", Gc},
135 {"Compute_From_Classical", true},
136 {"Influence_Function", json{{"Type", 0}}}});
137
139 json{{"Contact_Radius_Factor", Rc_factor},
140 {"Kn", Kn},
141 {"Damping_On", false},
142 {"Epsilon", 1.0},
143 {"Friction_On", false},
144 {"Friction_Coeff", 0.0},
145 {"Kn_Factor", 1.0},
146 {"Beta_n_Factor", 100.0},
147 {"K", K}});
148 // Soften spring for a short stable settle (formula check uses this Kn too).
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";
157
158 // Bottom of grain near the floor (y=0): slight overlap into Rc after setup.
159 const double cy = R + 0.25 * mesh_size;
160 auto pgen = inp::PGenDeck::getExampleJson(json{{"Method", "From_File"}});
161 pgen["Random_Rotation"] = false;
162 pgen["Data"]["N"] = 2;
163 pgen["Data"]["0"] = json{{"x", 0.},
164 {"y", cy},
165 {"z", 0.},
166 {"theta", 0.},
167 {"s", 1.},
168 {"geom_id", 0},
169 {"mat_id", 0},
170 {"contact_id", 0}};
171 pgen["Data"]["1"] = json{{"x", 0.},
172 {"y", -0.5 * wall_thick},
173 {"z", 0.},
174 {"theta", 0.},
175 {"s", 1.},
176 {"geom_id", 1},
177 {"mat_id", 1},
178 {"contact_id", 1},
179 {"is_wall", true}};
180
181 return json{{"Model", model},
182 {"Output", output},
183 {"Displacement_BC", bc["Displacement_BC"]},
184 {"Particle", particle},
185 {"Mesh", mesh},
186 {"Material", material},
187 {"Contact", contact},
188 {"Neighbor",
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}};
195}
float R
Definition problem.py:40

References geom::GeomData::d_geomName, geom::GeomData::d_geomParams, inp::BCDeck::getExampleJson(), inp::ContactPairDeck::getExampleJson(), inp::MaterialDeck::getExampleJson(), inp::ModelDeck::getExampleJson(), inp::PGenDeck::getExampleJson(), inp::PNeighborDeck::getExampleJson(), inp::OutputDeck::getExampleJson(), inp::ParticleDeck::getParticleContactExampleJson(), inp::ParticleDeck::getParticleGeomExampleJson(), and inp::ParticleDeck::getParticleMaterialExampleJson().

Referenced by main().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ buildInputJson() [4/8]

json anonymous_namespace{main.cpp}::buildInputJson ( const std::string &  output_path,
const std::filesystem::path &  mesh_file,
double  L,
double  mesh_size,
double  horizon,
double  final_time,
size_t  num_steps,
const std::string &  self_contact 
)

Definition at line 44 of file main.cpp.

46 {
48 json{{"Dimension", 2},
49 {"Final_Time", final_time},
50 {"Time_Steps", num_steps},
51 {"Discretization_Type", json{{"Spatial", "finite_difference"}, {"Time", "central_difference"}}},
52 {"Populate_ElementNodeConnectivity", true},
53 {"Quad_Approximation_Order", 2},
54 {"Particle_Sim_Type", "Single_Particle"},
55 {"Seed", 0}});
56 model["Self_Contact"] = self_contact;
57 model["Bond_Break"] = "tension";
58
59 // Soft PD; large Gc so only the precrack is fractured.
60 const double rho = 1200.0;
61 const double K = 2.16e5;
62 const double G = 1.296e5;
63 const double Gc = 5.0e6;
64
66 json{{"File_Format", "vtu"},
67 {"Path", output_path},
68 {"Tags", std::vector<std::string>({"Displacement", "Force", "Damage_Z", "Particle_ID"})},
69 {"Output_Interval", num_steps},
70 {"Debug", 1},
71 {"Perform_FE_Out", false},
72 {"Compress_Type", "zlib"},
73 {"Perform_Out", false},
74 {"Test_Output_Interval", num_steps},
75 {"Tag_PP", ""},
76 {"PVD_Collection", false}});
77
78 const double strip = 0.2 * L;
79 json bc = json::object();
80 bc["Displacement_BC"] = {
81 {"Sets", 2},
82 {"Set_1",
83 {{"Region",
84 {{"Geometry",
85 {{"Type", "rectangle"},
86 {"Parameters", std::vector<double>{-0.05 * L, -0.05 * L, 0., strip, L + 0.05 * L, 0.}}}}}},
87 {"Direction", std::vector<size_t>{1, 2}},
88 {"Time_Function", {{"Type", "constant"}, {"Parameters", std::vector<double>{0.}}}},
89 {"Spatial_Function", {{"Type", "constant"}}},
90 {"Zero_Displacement", true}}},
91 {"Set_2",
92 {{"Region",
93 {{"Geometry",
94 {{"Type", "rectangle"},
95 {"Parameters",
96 std::vector<double>{L - strip, -0.05 * L, 0., L + 0.05 * L, L + 0.05 * L, 0.}}}}}},
97 {"Direction", std::vector<size_t>{1}},
98 {"Time_Function", {{"Type", "constant"}, {"Parameters", std::vector<double>{0.}}}},
99 {"Spatial_Function", {{"Type", "constant"}}},
100 {"Zero_Displacement", false}}}};
101
102 geom::GeomData rect;
103 rect.d_geomName = "rectangle";
104 rect.d_geomParams = {0., 0., 0., L, L, 0.};
106
107 json mesh_set = {{"File", mesh_file.string()},
108 {"CreateMesh",
109 {{"Flag", true},
110 {"Info", "gmsh_builtin_mesh"},
111 {"Mesh_Size", mesh_size},
112 {"Write_Mesh_File", true}}}};
113 json mesh = {{"Set_1", mesh_set}};
114
116 material["Set_1"] =
118 json{{"Type", "PDState"},
119 {"Is_Plane_Strain", false},
120 {"Horizon", horizon},
121 {"Density", rho},
122 {"K", K},
123 {"G", G},
124 {"Gc", Gc},
125 {"Compute_From_Classical", true},
126 {"Influence_Function", json{{"Type", 0}}}});
127
128 json root = {{"Model", model},
129 {"Output", output},
130 {"Particle", particle},
131 {"Mesh", mesh},
132 {"Material", material}};
133 root.merge_patch(bc);
134 return root;
135}

References geom::GeomData::d_geomName, geom::GeomData::d_geomParams, inp::MaterialDeck::getExampleJson(), inp::ModelDeck::getExampleJson(), inp::OutputDeck::getExampleJson(), inp::ParticleDeck::getParticleGeomExampleJson(), and inp::ParticleDeck::getParticleMaterialExampleJson().

Referenced by runLaw().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ buildInputJson() [5/8]

json anonymous_namespace{main.cpp}::buildInputJson ( const std::string &  output_path,
const std::filesystem::path &  mesh_tri,
const std::filesystem::path &  mesh_ell,
double  final_time,
size_t  num_steps,
double  mesh_size,
double  horizon 
)

Definition at line 43 of file main.cpp.

45 {
46 // Short tip — point only, not a tall triangle. Narrower tip concentrates load.
47 const double W = 0.0008;
48 const double H = 0.0012;
49
50 // Hollow ellipse (visibly elliptical ring).
51 const double a_out = 0.0020;
52 const double b_out = 0.0014;
53 // Thin ring so a tip-seeded crack can bipartition.
54 const double a_in = 0.00180;
55 const double b_in = 0.00122;
56 const double ell_theta = 0.0;
57 const double tip_gap = 0.00008;
58 const double tip_y = H;
59 const double ell_cy = tip_y + tip_gap + b_out;
60
61 const double rho_t = 1200.0;
62 const double K_t = 2.16e7;
63 const double nu_t = 0.25;
64 const double E_t = material::toE(K_t, nu_t);
65 const double G_t = material::toGE(E_t, nu_t);
66 const double Gc_t = 200.0;
67
68 const double rho_e = 1200.0;
69 const double K_e = 2.16e7;
70 const double nu_e = 0.25;
71 const double E_e = material::toE(K_e, nu_e);
72 const double G_e = material::toGE(E_e, nu_e);
73 // Low Gc so tip damage runs into a diametral through-crack.
74 const double Gc_e = 1.0;
75
76 const double R_contact_factor = 0.95;
77 const double Kn_tt = util::normalContactStiffness(K_t, K_t, horizon);
78 const double Kn_ee = util::normalContactStiffness(K_e, K_e, horizon);
79 const double Kn_te = util::normalContactStiffness(K_t, K_e, horizon);
80
81 const double g = 10.0;
82 const double ic_vy = -2.5;
83
84 const size_t dt_out_n = std::max<size_t>(1, num_steps / 40);
85
87 json{{"Dimension", 2},
88 {"Final_Time", final_time},
89 {"Time_Steps", num_steps},
90 {"Discretization_Type", json{{"Spatial", "finite_difference"}, {"Time", "central_difference"}}},
91 {"Populate_ElementNodeConnectivity", true},
92 {"Quad_Approximation_Order", 2},
93 {"Particle_Sim_Type", "Multi_Particle"},
94 {"Seed", 0}});
95 model["Bond_Break"] = "tension";
96 // No self-contact across broken bonds — otherwise the crack stays glued.
97 model["Self_Contact"] = "none";
98
100 json{{"File_Format", "vtu"},
101 {"Path", output_path},
102 {"Tags", std::vector<std::string>({"Displacement", "Velocity", "Force", "Damage_Z", "Damage",
103 "Particle_ID", "Fixity"})},
104 {"Output_Interval", dt_out_n},
105 {"Debug", 1},
106 {"Perform_FE_Out", true},
107 {"Compress_Type", "zlib"},
108 {"Perform_Out", true},
109 {"Test_Output_Interval", 1},
110 {"Tag_PP", ""},
111 {"PVD_Collection", true}});
112
114 json{{"Displacement_BC_Sets", 1},
115 {"IC_Sets", 1},
116 {"Gravity", util::Point(0., -g, 0.).toVec()}});
117 bc["Displacement_BC"]["Set_1"] = inp::BCBaseDeck::getExampleJson(
118 json{{"Type", "Displacement_BC"},
119 {"Particle_List", {0}},
120 {"Direction", {1, 2}},
121 {"Zero_Displacement", true}});
122
123 bc["IC"]["Set_1"] = {
124 {"Particle_List", std::vector<size_t>{1}},
125 {"Constant_Velocity", {{"Velocity_Vector", std::vector<double>{0., ic_vy, 0.}}}}};
126
127 std::vector<geom::GeomData> pGeom(2);
128 pGeom[0].d_geomName = "triangle";
129 pGeom[0].d_geomParams = {-0.5 * W, 0., 0., 0.5 * W, 0., 0., 0., H, 0.};
130
131 pGeom[1].d_geomName = "ellipse_minus_ellipse";
132 pGeom[1].d_geomParams = {a_out, b_out, a_in, b_in, ell_theta, 0., 0., 0.};
133
134 for (auto &g : pGeom)
136
137 const util::Point c_tri = pGeom[0].d_geom_p->center();
138
139 json pDeck = json::object();
140 pDeck["Particle"] = inp::ParticleDeck::getParticleGeomExampleJson(pGeom);
141
142 pDeck["Mesh"] = {
143 {"Sets", 2},
144 {"Set_1",
145 {{"File", mesh_tri.string()},
146 {"CreateMesh",
147 {{"Flag", true},
148 {"Info", "gmsh_builtin_mesh"},
149 {"Mesh_Size", mesh_size},
150 {"Write_Mesh_File", true}}}}},
151 {"Set_2",
152 {{"File", mesh_ell.string()},
153 {"CreateMesh",
154 {{"Flag", true},
155 {"Info", "gmsh_builtin_mesh"},
156 {"Mesh_Size", mesh_size},
157 {"Write_Mesh_File", true}}}}}};
158
160 mat["Set_1"] =
162 json{{"Type", "PDState"},
163 {"Is_Plane_Strain", false},
164 {"Horizon", horizon},
165 {"Density", rho_t},
166 {"K", K_t},
167 {"G", G_t},
168 {"Gc", Gc_t},
169 {"Compute_From_Classical", true},
170 {"Influence_Function", json{{"Type", 0}}}});
171 mat["Set_2"] =
173 json{{"Type", "PDState"},
174 {"Is_Plane_Strain", false},
175 {"Horizon", horizon},
176 {"Density", rho_e},
177 {"K", K_e},
178 {"G", G_e},
179 {"Gc", Gc_e},
180 {"Compute_From_Classical", true},
181 {"Influence_Function", json{{"Type", 0}}}});
182 pDeck["Material"] = mat;
183
186 json{{"Contact_Radius_Factor", R_contact_factor},
187 {"Kn", Kn_tt},
188 {"Damping_On", true},
189 {"Epsilon", 0.95},
190 {"Friction_On", false},
191 {"Friction_Coeff", 0.0},
192 {"Kn_Factor", 1.0},
193 {"Beta_n_Factor", 100.0},
194 {"K", K_t}});
195 // Tip contact: enough Beta_n to drive a crack, damping on to limit spray.
197 json{{"Contact_Radius_Factor", 0.90},
198 {"Kn", Kn_te},
199 {"Damping_On", true},
200 {"Epsilon", 0.4},
201 {"Friction_On", false},
202 {"Friction_Coeff", 0.0},
203 {"Kn_Factor", 1.0},
204 {"Beta_n_Factor", 8.0},
205 {"K", util::harmonicMean(K_t, K_e)}});
207 json{{"Contact_Radius_Factor", R_contact_factor},
208 {"Kn", Kn_ee},
209 {"Damping_On", true},
210 {"Epsilon", 0.95},
211 {"Friction_On", false},
212 {"Friction_Coeff", 0.0},
213 {"Kn_Factor", 1.0},
214 {"Beta_n_Factor", 100.0},
215 {"K", K_e}});
216 contact["Set_1_1"] = cpair_wall;
217 contact["Set_1_1"]["Kn"] = Kn_tt;
218 contact["Set_1_1"]["K"] = K_t;
219 contact["Set_1_2"] = cpair_tip;
220 contact["Set_1_2"]["Kn"] = Kn_te;
221 contact["Set_1_2"]["K"] = util::harmonicMean(K_t, K_e);
222 contact["Set_2_2"] = cpair_ell;
223 contact["Set_2_2"]["Kn"] = Kn_ee;
224 contact["Set_2_2"]["K"] = K_e;
225 contact["Damping_Law"] = "com_and_node";
226 contact["Friction_Law"] = "coulomb_simple";
227 pDeck["Contact"] = contact;
228
229 pDeck["Neighbor"] = inp::PNeighborDeck::getExampleJson(
230 json{{"Update_Criteria", "simple_all"},
231 {"Search_Factor", 8.0},
232 {"Search_Interval", 5},
233 {"Near_Bd_Nodes_Tol", 0.5}});
234
235 auto gen = inp::PGenDeck::getExampleJson(json{{"Method", "From_File"}});
236 gen["Random_Rotation"] = false;
237 gen["Data"]["N"] = 2;
238 gen["Data"]["0"] = {{"x", c_tri.d_x}, {"y", c_tri.d_y}, {"z", 0.0},
239 {"theta", 0.0}, {"s", 1.0},
240 {"geom_id", 0}, {"mat_id", 0}, {"contact_id", 0},
241 {"is_wall", true}};
242 gen["Data"]["1"] = {{"x", 0.0}, {"y", ell_cy}, {"z", 0.0},
243 {"theta", 0.0}, {"s", 1.0},
244 {"geom_id", 1}, {"mat_id", 1}, {"contact_id", 1}};
245 pDeck["Particle_Generation"] = gen;
246
247 json j = json::object();
248 j["Model"] = model;
249 j["Output"] = output;
250 j["Force_BC"] = bc["Force_BC"];
251 j["Displacement_BC"] = bc["Displacement_BC"];
252 j["IC"] = bc["IC"];
253 j["Particle"] = pDeck["Particle"];
254 j["Mesh"] = pDeck["Mesh"];
255 j["Material"] = pDeck["Material"];
256 j["Contact"] = pDeck["Contact"];
257 j["Neighbor"] = pDeck["Neighbor"];
258 j["Particle_Generation"] = pDeck["Particle_Generation"];
259 return j;
260}
void createGeomObject(const std::string &geom_type, const std::vector< double > &params, const std::vector< std::string > &vec_type, const std::vector< std::string > &vec_flag, std::shared_ptr< geom::GeomObject > &obj, bool perform_check)
double harmonicMean(const double &m1, const double &m2)
Definition function.cpp:135
double d_y
the y coordinate
Definition point.h:36
double d_x
the x coordinate
Definition point.h:33

References geom::createGeomObject(), util::Point::d_x, util::Point::d_y, inp::BCDeck::getExampleJson(), inp::ContactPairDeck::getExampleJson(), inp::MaterialDeck::getExampleJson(), inp::ModelDeck::getExampleJson(), inp::PGenDeck::getExampleJson(), inp::PNeighborDeck::getExampleJson(), inp::BCBaseDeck::getExampleJson(), inp::OutputDeck::getExampleJson(), inp::ParticleDeck::getParticleContactExampleJson(), inp::ParticleDeck::getParticleGeomExampleJson(), inp::ParticleDeck::getParticleMaterialExampleJson(), util::harmonicMean(), util::normalContactStiffness(), material::toE(), material::toGE(), and util::Point::toVec().

Referenced by main().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ buildInputJson() [6/8]

json anonymous_namespace{main.cpp}::buildInputJson ( const std::string &  output_path_for_deck,
const std::filesystem::path &  inp_dir,
int  argc,
char *  argv[] 
)

Definition at line 203 of file main.cpp.

204 {
205
206 util::io::InputParser input(argc, argv);
207
208 const std::vector<double> center = {0.0, 0.0, 0.0};
209 const double R = 0.001;
210 const double mesh_size = R / 5.0;
211 const double horizon = 2.0 * mesh_size;
212
213 const double Lin = 0.05;
214 const double Win = 0.04;
215
216 constexpr double wall_top_inset = 1e-7;
217 constexpr double annulus_inset = 1e-7;
218 /* Clearance between void floor / plate and particle circum-bounds (~1–2 mesh). */
219 constexpr double clearance_mesh = 1.5;
220 /* Meshed triangles/hex/drum can extend slightly beyond circumcircle r in VTU. */
221 const double geom_pad = 0.25 * mesh_size;
222 const double plate_thickness = std::max(3.0 * mesh_size, 2.0 * mesh_size);
223
224 const double w_drum2d = R * 0.2;
225
226 const double final_time = 0.001;
227 size_t num_steps = 400;
228 if (input.cmdOptionExists("-numSteps"))
229 num_steps = static_cast<size_t>(std::stoul(input.getCmdOption("-numSteps")));
230
231 const size_t num_outputs = 10;
232 const size_t dt_out_n = std::max<size_t>(1, num_steps / num_outputs);
233 const size_t test_dt_out_n = std::max<size_t>(1, dt_out_n / 10);
234
235 const double rho_wall = 600.;
236 const double poisson_wall = 0.25;
237 const double K_wall = 1.e+4;
238 const double E_wall = material::toE(K_wall, poisson_wall);
239 const double G_wall = material::toGE(E_wall, poisson_wall);
240 const double KIc_wall = 5e+6;
241 const double Gc_wall = material::toGc(KIc_wall, poisson_wall, E_wall);
242
243 const double rho_p = 600.;
244 const double poisson_p = poisson_wall;
245 const double K_p = 5.e+3;
246 const double E_p = material::toE(K_p, poisson_p);
247 const double G_p = material::toGE(E_p, poisson_p);
248 const double KIc_p = 5e+6;
249 const double Gc_p = material::toGc(KIc_p, poisson_p, E_p);
250
251 const double R_contact_factor = 0.95;
252 const double padding = 1.1 * R_contact_factor * mesh_size;
253 const int N_target = 500;
254
255 const std::vector<double> in_rect = {center[0] - 0.5 * Lin, center[1] - 0.5 * Win, center[2],
256 center[0] + 0.5 * Lin, center[1] + 0.5 * Win, center[2]};
257
258 std::mt19937 gen(30);
259 const double max_y = in_rect[4] - clearance_mesh * mesh_size;
260 std::vector<PackedParticle> packed =
261 generateParticleLocations(in_rect, max_y, mesh_size, R, N_target, padding, gen);
262 if (packed.empty())
263 throw std::runtime_error("compression_large_set_inbuilt_2mat2contact: particle pack is empty");
264 for (auto &p : packed)
265 p.z = 0.0;
266
267 const double m = clearance_mesh * mesh_size;
268 /* Seat bed: lowest circum-bottom = void_floor + m (void floor = seed inner bottom). */
269 {
270 const PackedBounds bb0 = packedAxisBounds(packed);
271 const double delta_y = (in_rect[1] + m) - bb0.min_y;
272 for (auto &p : packed)
273 p.y += delta_y;
274 }
275
276 const PackedBounds bb = packedAxisBounds(packed);
277
278 /* Inner void (Gmsh “remove” rectangle): circum-bounds ± m ± geom_pad in x; floor at seed; top = domain. */
279 const double void_lox = bb.min_x - m - geom_pad;
280 const double void_hix = bb.max_x + m + geom_pad;
281 const double void_loy = in_rect[1];
282 /* Moving plate: bottom = top of bed + clearance + pad so FE nodes stay under the plate. */
283 const double plate_bottom_y = bb.max_y + m + geom_pad;
284 double plate_top_y = plate_bottom_y + plate_thickness;
285
286 /* Outer solid: wrap void with wall thickness; extend upward slightly past the plate. */
287 const double side = std::max(1.5 * horizon, 2.0 * mesh_size);
288 const double out_lox = void_lox - side;
289 const double out_hix = void_hix + side;
290 const double out_loy = void_loy - side;
291 const double out_hiy = plate_top_y + wall_top_inset + 2.5 * mesh_size;
292
293 std::vector<double> out_rect = {out_lox, out_loy, center[2], out_hix, out_hiy, center[2]};
294
295 /* Open-top void: inner cut runs to outer top (inset for Gmsh strict-inside). */
296 const double void_hi_y = out_rect[4] - annulus_inset;
297 std::vector<double> remove_rect = {void_lox, void_loy, center[2], void_hix, void_hi_y, center[2]};
298
299 util::io::print(std::format(
300 "[compression_large_set_inbuilt_2mat2contact] bbox(center±r) x∈[{:.6f},{:.6f}] y∈[{:.6f},{:.6f}]; "
301 "void x∈[{:.6f},{:.6f}] y_lo {:.6f}; plate y [{:.6f},{:.6f}]; void_hi {:.6f}; outer y∈[{:.6f},{:.6f}]\n",
302 bb.min_x, bb.max_x, bb.min_y, bb.max_y, void_lox, void_hix, void_loy, plate_bottom_y, plate_top_y,
303 void_hi_y, out_rect[1], out_rect[4]));
304
305 std::vector<double> moving_rect = {void_lox, plate_bottom_y, center[2], void_hix, plate_top_y, center[2]};
306
307 std::vector<double> fixed_container_params;
308 fixed_container_params.insert(fixed_container_params.end(), remove_rect.begin(), remove_rect.end());
309 fixed_container_params.insert(fixed_container_params.end(), out_rect.begin(), out_rect.end());
310
311 const size_t n_pack = packed.size();
312 const size_t n_wall_fixed = n_pack;
313 const size_t n_wall_moving = n_pack + 1;
314 const size_t n_total = n_pack + 2;
315
316 /* Reference geometries at origin; packing uses circum-radius ~ R for all zones. */
317 const double ell_a = 0.95 * R;
318 const double ell_b = 0.58 * R;
319 const double ell_theta = 0.35;
320 const double sq_half = R / std::sqrt(2.0);
321 /* Axis-aligned rectangle (~same circum extent as R). */
322 const double rx = 0.82 * R;
323 const double ry = 0.62 * R;
324
325 std::vector<geom::GeomData> pGeomVec(10);
326 pGeomVec[0].d_geomName = "circle";
327 pGeomVec[0].d_geomParams = {R, center[0], center[1], center[2]};
328 pGeomVec[1].d_geomName = "triangle";
329 pGeomVec[1].d_geomParams = {R, center[0], center[1], center[2]};
330 pGeomVec[2].d_geomName = "drum2d";
331 pGeomVec[2].d_geomParams = {R, w_drum2d, center[0], center[1], center[2]};
332 pGeomVec[3].d_geomName = "hexagon";
333 pGeomVec[3].d_geomParams = {R, center[0], center[1], center[2]};
334 pGeomVec[4].d_geomName = "ellipse";
335 pGeomVec[4].d_geomParams = {ell_a, ell_b, ell_theta, center[0], center[1], center[2]};
336 pGeomVec[5].d_geomName = "rectangle";
337 pGeomVec[5].d_geomParams = {-rx, -ry, center[2], rx, ry, center[2]};
338 pGeomVec[6].d_geomName = "square";
339 pGeomVec[6].d_geomParams = {-sq_half, -sq_half, center[2], sq_half, sq_half, center[2]};
340 pGeomVec[7].d_geomName = "circle_minus_circle";
341 pGeomVec[7].d_geomParams = {center[0], center[1], center[2], R, 0.35 * R};
342 pGeomVec[8].d_geomName = "rectangle_minus_rectangle";
343 pGeomVec[8].d_geomParams = fixed_container_params;
344 pGeomVec[9].d_geomName = "rectangle";
345 pGeomVec[9].d_geomParams = moving_rect;
346
347 for (auto &g : pGeomVec)
349
350 /* Annulus composite centroid can pick up tiny numerical z; 2D setup keeps all sites on z = 0. */
351 const util::Point cfix = pGeomVec[8].d_geom_p->center();
352 const util::Point cmov = pGeomVec[9].d_geom_p->center();
353 const util::Point site_wall_fixed(cfix.d_x, cfix.d_y, 0.0);
354 const util::Point site_wall_moving(cmov.d_x, cmov.d_y, 0.0);
355
356 auto modelDeckJson = inp::ModelDeck::getExampleJson(
357 json{{"Dimension", 2},
358 {"Final_Time", final_time},
359 {"Time_Steps", num_steps},
360 {"Discretization_Type", json{{"Spatial", "finite_difference"}, {"Time", "central_difference"}}},
361 {"Populate_ElementNodeConnectivity", true},
362 {"Quad_Approximation_Order", 2},
363 {"Particle_Sim_Type", "Multi_Particle"},
364 {"Seed", 0}});
365
366 std::vector<std::string> out_tags = {"Displacement", "Velocity", "Force", "Force_Density", "Damage_Z",
367 "Damage", "Nodal_Volume", "Zone_ID", "Particle_ID", "Fixity",
368 "Force_Fixity", "Contact_Nodes", "No_Fail_Node", "Boundary_Node_Flag"};
369 /* Perform_FE_Out must be true so VTU includes element connectivity (appendMesh). If false, only
370 * points are written (appendNodes) and ParaView’s default “Surface” view looks empty — use
371 * Representation → Points, or enable this flag. Large packs often leave FE out. */
372 auto outputDeckJson = inp::OutputDeck::getExampleJson(
373 json{{"File_Format", "vtu"},
374 {"Path", output_path_for_deck},
375 {"Tags", out_tags},
376 {"Output_Interval", dt_out_n},
377 {"Debug", 2},
378 {"Perform_FE_Out", true},
379 {"Compress_Type", "zlib"},
380 {"Perform_Out", true},
381 {"Test_Output_Interval", test_dt_out_n},
382 {"Tag_PP", "0"},
383 {"PVD_Collection", true}});
384
385 auto bcDeckJson = inp::BCDeck::getExampleJson(
386 json{{"Displacement_BC_Sets", 2},
387 {"Gravity", util::Point(0, -10, 0).toVec()}});
388
389 bcDeckJson["Displacement_BC"]["Set_1"] = inp::BCBaseDeck::getExampleJson(
390 json{{"Type", "Displacement_BC"},
391 {"Particle_List", {n_wall_fixed}},
392 {"Direction", {1, 2}},
393 {"Zero_Displacement", true}});
394
395 json set2;
396 set2["Particle_List"] = std::vector<size_t>{n_wall_moving};
397 set2["Direction"] = std::vector<size_t>{2};
398 set2["Time_Function"] = json{{"Type", "linear"}, {"Parameters", std::vector<double>{-0.06}}};
399 set2["Spatial_Function"] = json{{"Type", "constant"}};
400 bcDeckJson["Displacement_BC"]["Set_2"] = set2;
401
402 json pDeckJson = json::object();
403
404 pDeckJson["Particle"] = inp::ParticleDeck::getParticleGeomExampleJson(pGeomVec);
405
406 json meshRoot = json{{"Sets", 10}};
407 const char *mesh_names[] = {"mesh_cir", "mesh_tri", "mesh_drum2d", "mesh_hex",
408 "mesh_ellipse", "mesh_rect", "mesh_square", "mesh_circirc",
409 "mesh_fixed_container", "mesh_moving_container"};
410 for (int zi = 0; zi < 10; ++zi) {
411 const std::string fname = (inp_dir / (std::string(mesh_names[zi]) + ".msh")).string();
412 meshRoot["Set_" + std::to_string(zi + 1)] =
413 json{{"File", fname},
414 {"CreateMesh",
415 json{{"Flag", true}, {"Info", "gmsh_builtin_mesh"}, {"Mesh_Size", mesh_size}, {"Write_Mesh_File", true}}}};
416 }
417 pDeckJson["Mesh"] = meshRoot;
418
419 /* Two material laws: 0 = particle, 1 = wall (fixed + moving). geom_id still selects mesh shape (10 meshes). */
420 json matRoot = json{{"Sets", 2}};
421 matRoot["Set_1"] = inp::MaterialDeck::getExampleJson(
422 json{{"Type", "PDState"},
423 {"Is_Plane_Strain", false},
424 {"Horizon", horizon},
425 {"Density", rho_p},
426 {"K", K_p},
427 {"G", G_p},
428 {"Gc", Gc_p},
429 {"Compute_From_Classical", true},
430 {"Influence_Function", json{{"Type", 0}}}});
431 matRoot["Set_2"] =
433 json{{"Type", "PDState"},
434 {"Is_Plane_Strain", false},
435 {"Horizon", horizon},
436 {"Density", rho_wall},
437 {"K", K_wall},
438 {"G", G_wall},
439 {"Gc", Gc_wall},
440 {"Compute_From_Classical", true},
441 {"Influence_Function", json{{"Type", 0}}}});
442 pDeckJson["Material"] = matRoot;
443
444 const double Kn_pp = KnFromBulk(K_p, K_p, horizon);
445 const double Kn_pw = KnFromBulk(K_p, K_wall, horizon);
446 const double Kn_ww = 0.0;
447
448 const double beta_n_eps = 0.95;
449 const double friction_coeff = 0.5;
450 const bool damping_on = true;
451 const bool friction_on = false;
452 const double beta_n_factor = 100.;
453 const double Kn_factor = 1.;
454
455 /* Contact zones: 0 = granular, 1 = wall (fixed + moving). 2×2 matrix → three unique pairs (1-1, 1-2, 2-2). */
456 const json j_contact_pp =
457 contactPairJson(R_contact_factor, damping_on, friction_on, Kn_pp, beta_n_eps, friction_coeff, Kn_factor,
458 beta_n_factor);
459 const json j_contact_pw =
460 contactPairJson(R_contact_factor, damping_on, friction_on, Kn_pw, beta_n_eps, friction_coeff, Kn_factor,
461 beta_n_factor);
462 const json j_contact_ww =
463 contactPairJson(R_contact_factor, damping_on, friction_on, Kn_ww, beta_n_eps, friction_coeff, Kn_factor,
464 beta_n_factor);
465
466 json contactRoot = inp::ContactDeck::getExampleJson(json{{"Sets", 2}});
467 contactRoot["Set_1_1"] = j_contact_pp;
468 contactRoot["Set_1_2"] = j_contact_pw;
469 contactRoot["Set_2_2"] = j_contact_ww;
470 pDeckJson["Contact"] = contactRoot;
471
472 pDeckJson["Neighbor"] = inp::PNeighborDeck::getExampleJson(
473 json{{"Update_Criteria", "simple_all"},
474 {"Search_Factor", 10.0},
475 {"Search_Interval", 100},
476 {"Near_Bd_Nodes_Tol", 0.5}});
477
478 auto pGenJson = inp::PGenDeck::getExampleJson(json{{"Method", "From_File"}});
479 pGenJson["Random_Rotation"] = false;
480 pGenJson["Data"]["N"] = n_total;
481
482 constexpr size_t k_mat_particle = 0;
483 constexpr size_t k_mat_wall = 1;
484 constexpr size_t k_contact_grains = 0;
485 constexpr size_t k_contact_wall = 1;
486
487 for (size_t pi = 0; pi < n_pack; ++pi) {
488 const auto &p = packed[pi];
489 pGenJson["Data"][std::to_string(pi)] = json{
490 {"x", p.x}, {"y", p.y}, {"z", 0.0}, {"theta", p.theta},
491 {"s", 1.0}, {"geom_id", static_cast<size_t>(p.zone)},
492 {"mat_id", k_mat_particle},
493 {"contact_id", k_contact_grains},
494 };
495 }
496
497 pGenJson["Data"][std::to_string(n_pack)] = json{{"x", site_wall_fixed.d_x},
498 {"y", site_wall_fixed.d_y},
499 {"z", 0.0},
500 {"theta", 0.0},
501 {"s", 1.0},
502 {"geom_id", size_t(8)},
503 {"mat_id", k_mat_wall},
504 {"contact_id", k_contact_wall}};
505
506 pGenJson["Data"][std::to_string(n_pack + 1)] = json{{"x", site_wall_moving.d_x},
507 {"y", site_wall_moving.d_y},
508 {"z", 0.0},
509 {"theta", 0.0},
510 {"s", 1.0},
511 {"geom_id", size_t(9)},
512 {"mat_id", k_mat_wall},
513 {"contact_id", k_contact_wall}};
514
515 pDeckJson["Particle_Generation"] = pGenJson;
516
517 /* Top-level comment only: ignored by inp::Input (only known sections are parsed). */
518 return json{{"Comment",
519 "compression_large_set_inbuilt"},
520 {"Model", modelDeckJson},
521 {"Output", outputDeckJson},
522 {"Force_BC", bcDeckJson["Force_BC"]},
523 {"Displacement_BC", bcDeckJson["Displacement_BC"]},
524 {"Particle", pDeckJson["Particle"]},
525 {"Mesh", pDeckJson["Mesh"]},
526 {"Material", pDeckJson["Material"]},
527 {"Contact", pDeckJson["Contact"]},
528 {"Neighbor", pDeckJson["Neighbor"]},
529 {"Particle_Generation", pDeckJson["Particle_Generation"]}};
530}
Input command line argument parser.
Definition inputParser.h:28
std::vector< PackedParticle > generateParticleLocations(const std::vector< double > &in_rect, double max_y, double mesh_size, double R, int N_target, double padding, std::mt19937 &gen)
Definition main.cpp:75
PackedBounds packedAxisBounds(const std::vector< PackedParticle > &packed)
Definition main.cpp:166
double KnFromBulk(double Ka, double Kb, double horizon)
Definition main.cpp:199
json contactPairJson(double R_contact_factor, bool damping_on, bool friction_on, double Kn, double beta_n_eps, double friction_coeff, double Kn_factor, double beta_n_factor)
Definition main.cpp:181
double toGc(double KIc, double nu, double E)
Compute critical energy release rate Gc from critical stress-intensity factor KIc,...
static json getExampleJson(const json &given=json::object())
Returns the block with the given fields set.

References util::io::InputParser::cmdOptionExists(), contactPairJson(), geom::createGeomObject(), util::Point::d_x, util::Point::d_y, generateParticleLocations(), util::io::InputParser::getCmdOption(), inp::BCDeck::getExampleJson(), inp::ContactDeck::getExampleJson(), inp::MaterialDeck::getExampleJson(), inp::ModelDeck::getExampleJson(), inp::PGenDeck::getExampleJson(), inp::PNeighborDeck::getExampleJson(), inp::BCBaseDeck::getExampleJson(), inp::OutputDeck::getExampleJson(), inp::ParticleDeck::getParticleGeomExampleJson(), KnFromBulk(), anonymous_namespace{main.cpp}::PackedBounds::max_x, anonymous_namespace{main.cpp}::PackedBounds::max_y, anonymous_namespace{main.cpp}::PackedBounds::min_x, anonymous_namespace{main.cpp}::PackedBounds::min_y, packedAxisBounds(), util::io::print(), material::toE(), material::toGc(), material::toGE(), and util::Point::toVec().

Referenced by main().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ buildInputJson() [7/8]

json anonymous_namespace{main.cpp}::buildInputJson ( const std::string &  output_path_for_deck,
const std::filesystem::path &  mesh_cir,
const std::filesystem::path &  mesh_fixed,
const std::filesystem::path &  mesh_moving,
double  final_time,
size_t  num_steps,
bool  file_mesh,
bool  write_meshes,
size_t  search_interval,
int  ncols,
int  nrows,
const std::string &  mpi_strategy = "auto" 
)

Definition at line 83 of file main.cpp.

89 {
90
91 if (ncols < 1 || nrows < 1)
92 throw std::runtime_error("jha2021_comp: ncols and nrows must be >= 1");
93 const int ngrains = ncols * nrows;
94
95 const std::vector<double> center = {0.0, 0.0, 0.0};
96 const double R = 0.001;
97 const double mesh_size = R / 5.0;
98 const double horizon = 3.0 * mesh_size;
99 // Realized hmin on this disk is 0.7 lc. Start slightly outside Rc so contact
100 // is not present at t=0; plate and gravity pull pairs into Rc.
101 const double h_est = 0.7 * mesh_size;
102 const double Rc_est = 0.95 * h_est;
103 const double particle_padding = 1.15 * Rc_est;
104 const double wpd = particle_padding;
105 const double rwp = horizon + wpd;
106 constexpr double wall_vy = -0.06;
107
108 const double Lin = 2.0 * particle_padding + 2.0 * R +
109 static_cast<double>(ncols - 1) * (2.0 * R + particle_padding);
110 const double Win = 2.0 * particle_padding + 2.0 * R +
111 static_cast<double>(nrows - 1) * (2.0 * R + particle_padding);
112
113 const double wall_t = rwp - wpd;
114 const std::vector<double> mw_rect = {center[0] - wpd, Win, center[2],
115 Lin + wpd, Win + wall_t, center[2]};
116 const std::vector<double> cup_channel = {center[0] - rwp, center[1] - rwp,
117 Lin + rwp, Win + wall_t, wall_t,
118 center[2]};
119
120 std::vector<geom::GeomData> pGeomVec(3);
121 pGeomVec[0].d_geomName = "circle";
122 pGeomVec[0].d_geomParams = {R, center[0], center[1], center[2]};
123 pGeomVec[1].d_geomName = "open_rect_channel_2d";
124 pGeomVec[1].d_geomParams = cup_channel;
125 pGeomVec[2].d_geomName = "rectangle";
126 pGeomVec[2].d_geomParams = mw_rect;
127
128 std::vector<PackedParticle> packed =
129 generateCircularGrid(ncols, nrows, 0.0, 0.0, R, particle_padding);
130 if (static_cast<int>(packed.size()) != ngrains)
131 throw std::runtime_error("jha2021_comp: grid count mismatch");
132
133 const double poisson = 0.25;
134 const double rho = 1200.0;
135 const double K = 2.16e+7;
136 const double E = material::toE(K, poisson);
137 const double G = material::toGE(E, poisson);
138 const double Gc = 50.0;
139 const double Kn = util::normalContactStiffness(K, K, horizon);
140
141 const double dt = final_time / static_cast<double>(num_steps);
142 const double c_wave = std::sqrt(E / rho);
143 const double dt_cfl = h_est / c_wave;
144 util::io::print(std::format(
145 "jha2021_comp: N={} ({}x{}), gap={:.6e} (1.15 Rc_est), wpd={:.6e}, "
146 "dt={:.6e} s, h/c={:.6e} s, dt/(h/c)={:.3f}\n",
147 ngrains, ncols, nrows, particle_padding, wpd, dt, dt_cfl, dt / dt_cfl));
148
149 const size_t num_outputs = 4;
150 const size_t dt_out_n = std::max<size_t>(1, num_steps / num_outputs);
151 const size_t test_dt_out_n = std::max<size_t>(1, dt_out_n / 10);
152
153 for (auto &g : pGeomVec)
155
156 const util::Point cfix = pGeomVec[1].d_geom_p->center();
157 const util::Point cmov = pGeomVec[2].d_geom_p->center();
158 const size_t n_pack = packed.size();
159 const size_t n_wall_fixed = n_pack;
160 const size_t n_wall_moving = n_pack + 1;
161 const size_t n_total = n_pack + 2;
162
163 auto modelDeckJson = inp::ModelDeck::getExampleJson(
164 json{{"Dimension", 2},
165 {"Final_Time", final_time},
166 {"Time_Steps", num_steps},
167 {"Discretization_Type", json{{"Spatial", "finite_difference"}, {"Time", "central_difference"}}},
168 {"Populate_ElementNodeConnectivity", true},
169 {"Quad_Approximation_Order", 2},
170 {"Particle_Sim_Type", "Multi_Particle"},
171 {"Seed", 0}});
172 modelDeckJson["MPI_Strategy"] = mpi_strategy;
173
174 std::vector<std::string> out_tags = {"Displacement", "Velocity", "Force", "Damage_Z",
175 "Damage", "Particle_ID", "Contact_Nodes"};
176 auto outputDeckJson = inp::OutputDeck::getExampleJson(
177 json{{"File_Format", "vtu"},
178 {"Path", output_path_for_deck},
179 {"Tags", out_tags},
180 {"Output_Interval", dt_out_n},
181 {"Debug", 1},
182 {"Perform_FE_Out", true},
183 {"Compress_Type", "zlib"},
184 {"Perform_Out", true},
185 {"Test_Output_Interval", test_dt_out_n},
186 {"Tag_PP", "0"},
187 {"PVD_Collection", true}});
188
189 auto bcDeckJson =
191 json{{"Displacement_BC_Sets", 2},
192 {"Gravity", util::Point(0, -10, 0).toVec()}});
193 bcDeckJson["Displacement_BC"]["Set_1"] = inp::BCBaseDeck::getExampleJson(
194 json{{"Type", "Displacement_BC"},
195 {"Particle_List", {n_wall_fixed}},
196 {"Direction", {1, 2}},
197 {"Zero_Displacement", true}});
198 json set2;
199 set2["Particle_List"] = std::vector<size_t>{n_wall_moving};
200 set2["Direction"] = std::vector<size_t>{2};
201 set2["Time_Function"] = json{{"Type", "linear"}, {"Parameters", std::vector<double>{wall_vy}}};
202 set2["Spatial_Function"] = json{{"Type", "constant"}};
203 bcDeckJson["Displacement_BC"]["Set_2"] = set2;
204
205 json pDeckJson = json::object();
206 pDeckJson["Particle"] = inp::ParticleDeck::getParticleGeomExampleJson(pGeomVec);
207
208 auto meshEntry = [&](const std::filesystem::path &f) -> json {
209 if (file_mesh)
210 return json{{"File", f.string()}};
211 return json{{"File", f.string()},
212 {"CreateMesh",
213 json{{"Flag", true},
214 {"Info", "gmsh_builtin_mesh"},
215 {"Mesh_Size", mesh_size},
216 {"Write_Mesh_File", write_meshes}}}};
217 };
218 pDeckJson["Mesh"] = json{{"Sets", 3},
219 {"Set_1", meshEntry(mesh_cir)},
220 {"Set_2", meshEntry(mesh_fixed)},
221 {"Set_3", meshEntry(mesh_moving)}};
222
223 json matRoot = json{{"Sets", 2}};
224 matRoot["Set_1"] =
226 json{{"Type", "PDState"},
227 {"Is_Plane_Strain", false},
228 {"Horizon", horizon},
229 {"Density", rho},
230 {"K", K},
231 {"G", G},
232 {"Gc", Gc},
233 {"Compute_From_Classical", true},
234 {"Influence_Function", json{{"Type", 0}}}});
235 matRoot["Set_2"] =
237 json{{"Type", "PDState"},
238 {"Is_Plane_Strain", false},
239 {"Horizon", horizon},
240 {"Density", rho},
241 {"K", K},
242 {"G", G},
243 {"Gc", Gc},
244 {"Compute_From_Classical", true},
245 {"Influence_Function", json{{"Type", 0}}}});
246 pDeckJson["Material"] = matRoot;
247
249 json{{"Contact_Radius_Factor", 0.95},
250 {"Kn", Kn},
251 {"Damping_On", true},
252 {"Epsilon", 0.95},
253 {"Friction_On", false},
254 {"Friction_Coeff", 0.0},
255 {"Kn_Factor", 1.0},
256 {"Beta_n_Factor", 100.0},
257 {"K", K}});
259 contactRoot["Set_1_1"] = contact_base;
260 contactRoot["Set_1_2"] = contact_base;
261 contactRoot["Set_2_2"] = contact_base;
262 pDeckJson["Contact"] = contactRoot;
263 pDeckJson["Neighbor"] =
265 json{{"Update_Criteria", "simple_all"},
266 {"Search_Factor", 5.0},
267 {"Search_Interval", search_interval},
268 {"Near_Bd_Nodes_Tol", 0.5}});
269
270 auto pGenJson = inp::PGenDeck::getExampleJson(json{{"Method", "From_File"}});
271 pGenJson["Random_Rotation"] = false;
272 pGenJson["Data"]["N"] = n_total;
273 for (size_t pi = 0; pi < n_pack; ++pi) {
274 const auto &p = packed[pi];
275 pGenJson["Data"][std::to_string(pi)] = json{{"x", p.x},
276 {"y", p.y},
277 {"z", 0.0},
278 {"theta", 0.0},
279 {"s", 1.0},
280 {"geom_id", size_t(0)},
281 {"mat_id", size_t(0)},
282 {"contact_id", size_t(0)}};
283 }
284 pGenJson["Data"][std::to_string(n_pack)] = json{{"x", cfix.d_x},
285 {"y", cfix.d_y},
286 {"z", 0.0},
287 {"theta", 0.0},
288 {"s", 1.0},
289 {"geom_id", size_t(1)},
290 {"mat_id", size_t(1)},
291 {"contact_id", size_t(1)},
292 {"is_wall", true}};
293 pGenJson["Data"][std::to_string(n_pack + 1)] = json{{"x", cmov.d_x},
294 {"y", cmov.d_y},
295 {"z", 0.0},
296 {"theta", 0.0},
297 {"s", 1.0},
298 {"geom_id", size_t(2)},
299 {"mat_id", size_t(1)},
300 {"contact_id", size_t(1)},
301 {"is_wall", true}};
302 pDeckJson["Particle_Generation"] = pGenJson;
303
304 util::io::print(std::format(
305 "jha2021_comp: Lin={:.6f}, Win={:.6f}, horizon={:.6f}, plate v_y={}, Wall_Id={}\n",
306 Lin, Win, horizon, wall_vy, n_wall_moving));
307
308 return json{{"Comment", "jha2021_comp_contact"},
309 {"Model", modelDeckJson},
310 {"Output", outputDeckJson},
311 {"Force_BC", bcDeckJson["Force_BC"]},
312 {"Displacement_BC", bcDeckJson["Displacement_BC"]},
313 {"Particle", pDeckJson["Particle"]},
314 {"Mesh", pDeckJson["Mesh"]},
315 {"Material", pDeckJson["Material"]},
316 {"Contact", pDeckJson["Contact"]},
317 {"Neighbor", pDeckJson["Neighbor"]},
318 {"Particle_Generation", pDeckJson["Particle_Generation"]},
319 {"Test", json{{"Test_Name", "compressive_test"},
320 {"Compressive_Test",
321 json{{"Wall_Id", n_wall_moving},
322 {"Wall_Force_Direction", 2}}}}}};
323}
std::vector< PackedParticle > generateCircularGrid(int ncols, int nrows, double x_lo, double y_lo, double R, double gap)
Definition main.cpp:61

References geom::createGeomObject(), util::Point::d_x, util::Point::d_y, generateCircularGrid(), inp::BCDeck::getExampleJson(), inp::ContactPairDeck::getExampleJson(), inp::MaterialDeck::getExampleJson(), inp::ModelDeck::getExampleJson(), inp::PGenDeck::getExampleJson(), inp::PNeighborDeck::getExampleJson(), inp::BCBaseDeck::getExampleJson(), inp::OutputDeck::getExampleJson(), inp::ParticleDeck::getParticleContactExampleJson(), inp::ParticleDeck::getParticleGeomExampleJson(), util::normalContactStiffness(), util::io::print(), material::toE(), material::toGE(), and util::Point::toVec().

Referenced by main().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ buildInputJson() [8/8]

json anonymous_namespace{main.cpp}::buildInputJson ( const std::string &  output_path_for_deck,
const std::filesystem::path &  mesh_file_1,
const std::filesystem::path &  mesh_file_2,
double  final_time,
size_t  num_steps,
bool  zero_ic,
double  mesh_size_in,
double  horizon_in,
bool  damping_on,
double  eps_n,
bool  two_particle_test,
bool  file_mesh,
double  beta_n_factor,
const std::string &  damping_law = "com_and_node",
const std::string &  friction_law = "coulomb_simple",
bool  friction_on = false,
double  friction_mu = -1.,
double  ic_vx = 0.,
bool  bottom_patch_bc = false,
const std::string &  mpi_strategy = "auto" 
)

Definition at line 95 of file main.cpp.

108 {
109
110 const std::vector<double> center = {0.0, 0.0, 0.0};
111 const double R1 = 0.001;
112 const double R2 = 0.001;
113 const double mesh_size =
114 mesh_size_in > 0. ? mesh_size_in : std::min(R1, R2) / 5.0;
115 const double horizon = horizon_in > 0. ? horizon_in : 3.0 * mesh_size;
116 const double particle_dist = 0.001;
117
118 const double poisson1 = 0.25;
119 const double rho1 = 1200.0;
120 const double K1 = 2.16e+7;
121 const double E1 = material::toE(K1, poisson1);
122 const double G1 = material::toGE(E1, poisson1);
123 const double Gc1 = 50.0;
124
125 const double poisson2 = 0.25;
126 const double rho2 = 1200.0;
127 const double K2 = 2.16e+7;
128 const double E2 = material::toE(K2, poisson2);
129 const double G2 = material::toGE(E2, poisson2);
130 const double Gc2 = 50.0;
131
132 const double R_contact_factor = 0.95;
133 const double Kn_11 = util::normalContactStiffness(K1, K1, horizon);
134 const double Kn_22 = util::normalContactStiffness(K2, K2, horizon);
135 const double Kn_12 = util::normalContactStiffness(K1, K2, horizon);
136 const double friction_coeff = 0.5;
137
138 std::vector<double> p1_center = center;
139 std::vector<double> p2_center = center;
140
141 const double H0_drop = particle_dist; // paper drop height, 1 mm
142 // Table 2 energy: remaining gap = horizon, rest of H0 already in IC velocity.
143 const double top_gap = two_particle_test ? horizon : particle_dist;
144
145 const size_t dt_out_n = num_steps / 10;
146 auto modelDeckJson = inp::ModelDeck::getExampleJson(
147 json{{"Dimension", 2},
148 {"Final_Time", final_time},
149 {"Time_Steps", num_steps},
150 {"Discretization_Type", json{{"Spatial", "finite_difference"}, {"Time", "central_difference"}}},
151 {"Populate_ElementNodeConnectivity", true},
152 {"Quad_Approximation_Order", 2},
153 {"Particle_Sim_Type", "Multi_Particle"},
154 {"Seed", 0}});
155 modelDeckJson["MPI_Strategy"] = mpi_strategy;
156
157 auto outputDeckJson = inp::OutputDeck::getExampleJson(
158 json{{"File_Format", "vtu"},
159 {"Path", output_path_for_deck},
160 {"Tags", std::vector<std::string>({"Displacement", "Velocity", "Force", "Damage_Z", "Damage", "Particle_ID"})},
161 {"Output_Interval", dt_out_n},
162 {"Debug", 2},
163 {"Perform_FE_Out", true},
164 {"Compress_Type", "zlib"},
165 {"Perform_Out", true},
166 {"Test_Output_Interval", 1},
167 {"Tag_PP", ""},
168 {"PVD_Collection", true}});
169
170 auto bcDeckJson = inp::BCDeck::getExampleJson(
171 json{{"Displacement_BC_Sets", 1},
172 {"IC_Sets", 1},
173 {"Gravity", util::Point(0, -10, 0).toVec()}});
174
175 if (bottom_patch_bc) {
176 // Particle 0 centered at (R1,R1); fix only a bottom strip (not the whole grain).
177 geom::GeomData patch;
178 patch.d_geomName = "rectangle";
179 patch.d_geomParams = {R1 - 1.1 * R1, -0.1 * R1, 0., R1 + 1.1 * R1, 0.35 * R1,
180 0.};
181 json j_geom;
182 geom::writeGeometry(j_geom, patch);
183 bcDeckJson["Displacement_BC"]["Set_1"] = json{
184 {"Particle_List", json::array({0})},
185 {"Region", {{"Geometry", j_geom}}},
186 {"Direction", json::array({1, 2})},
187 {"Zero_Displacement", true}};
188 } else {
189 bcDeckJson["Displacement_BC"]["Set_1"] =
191 json{{"Type", "Displacement_BC"},
192 {"Particle_List", {0}},
193 {"Direction", {1, 2}},
194 {"Zero_Displacement", true}});
195 }
196
197 std::vector<double> ic_vel = {0.0, 0.0, 0.0};
198 if (!zero_ic) {
199 const double fallen =
200 two_particle_test ? (H0_drop - top_gap) : (particle_dist - horizon);
201 if (fallen > 0.)
202 ic_vel[1] = -std::sqrt(2.0 * std::abs(-10.0) * fallen);
203 }
204 ic_vel[0] = ic_vx;
205 bcDeckJson["IC"]["Set_1"] = inp::BCBaseDeck::getExampleJson(
206 json{{"Type", "IC"},
207 {"Particle_List", {1}},
208 {"IC_Type", "Constant_Velocity"},
209 {"IC_Vector", ic_vel}});
210
211 auto pDeckJson = json({});
212
213 std::vector<geom::GeomData> pGeomVec(2);
214 pGeomVec[0].d_geomName = "circle";
215 pGeomVec[0].d_geomParams = {R1, p1_center[0], p1_center[1], p1_center[2]};
216 pGeomVec[1].d_geomName = "circle";
217 pGeomVec[1].d_geomParams = {R2, p2_center[0], p2_center[1], p2_center[2]};
218
219 pDeckJson["Particle"] = inp::ParticleDeck::getParticleGeomExampleJson(pGeomVec);
220
221 const std::string f1 = mesh_file_1.string();
222 const std::string f2 = mesh_file_2.string();
223 json meshSet1, meshSet2;
224 if (file_mesh) {
225 meshSet1 = json({{"File", f1}});
226 meshSet2 = json({{"File", f2}});
227 } else {
228 meshSet1 = json({{"File", f1},
229 {"CreateMesh",
230 {{"Flag", true},
231 {"Info", "gmsh_builtin_mesh"},
232 {"Mesh_Size", mesh_size},
233 {"Write_Mesh_File", true}}}});
234 meshSet2 = json({{"File", f2},
235 {"CreateMesh",
236 {{"Flag", true},
237 {"Info", "gmsh_builtin_mesh"},
238 {"Mesh_Size", mesh_size},
239 {"Write_Mesh_File", true}}}});
240 }
241 pDeckJson["Mesh"] = json({{"Sets", 2}, {"Set_1", meshSet1}, {"Set_2", meshSet2}});
242
244 pMatJson["Set_1"] = inp::MaterialDeck::getExampleJson(
245 json{{"Type", "PDState"},
246 {"Is_Plane_Strain", false},
247 {"Horizon", horizon},
248 {"Density", rho1},
249 {"K", K1},
250 {"G", G1},
251 {"Gc", Gc1},
252 {"Compute_From_Classical", true},
253 {"Influence_Function", json{{"Type", 0}}}});
254 pMatJson["Set_2"] = inp::MaterialDeck::getExampleJson(
255 json{{"Type", "PDState"},
256 {"Is_Plane_Strain", false},
257 {"Horizon", horizon},
258 {"Density", rho2},
259 {"K", K2},
260 {"G", G2},
261 {"Gc", Gc2},
262 {"Compute_From_Classical", true},
263 {"Influence_Function", json{{"Type", 0}}}});
264 pDeckJson["Material"] = pMatJson;
265
267 // v0.1.0 circ_damp: Friction_On: false leaves μ = 0 (coeff is not read).
268 // Nonzero μ with the flag off still applies a tangential force in PairForce,
269 // which gives the falling particle a lateral velocity (Table 2 test 4).
270 const double mu =
271 (friction_mu >= 0.) ? friction_mu
272 : (two_particle_test ? 0. : friction_coeff);
274 json{{"Contact_Radius_Factor", R_contact_factor},
275 {"Kn", Kn_11},
276 {"Damping_On", damping_on},
277 {"Epsilon", eps_n},
278 {"Friction_On", friction_on},
279 {"Friction_Coeff", mu},
280 {"Kn_Factor", 1.0},
281 {"Beta_n_Factor", beta_n_factor},
282 {"K", K1}});
283
284 pContactJson["Set_1_1"] = contact_base;
285 pContactJson["Set_1_1"]["Kn"] = Kn_11;
286 pContactJson["Set_1_1"]["K"] = K1;
287 pContactJson["Set_1_2"] = contact_base;
288 pContactJson["Set_1_2"]["Kn"] = Kn_12;
289 pContactJson["Set_1_2"]["K"] = util::harmonicMean(K1, K2);
290 pContactJson["Set_2_2"] = contact_base;
291 pContactJson["Set_2_2"]["Kn"] = Kn_22;
292 pContactJson["Set_2_2"]["K"] = K2;
293 pContactJson["Damping_Law"] = damping_law;
294 pContactJson["Friction_Law"] = friction_law;
295 pDeckJson["Contact"] = pContactJson;
296
297 pDeckJson["Neighbor"] = two_particle_test
299 json{{"Update_Criteria", "simple_all"},
300 {"Search_Factor", 5.0},
301 {"Search_Interval", 1},
302 {"Near_Bd_Nodes_Tol", 0.5}})
303 : inp::PNeighborDeck::getExampleJson(
304 json{{"Update_Criteria", "simple_all"},
305 {"Search_Factor", 10.0},
306 {"Search_Interval", 40},
307 {"Near_Bd_Nodes_Tol", 0.5}});
308
309 auto pGenJson = inp::PGenDeck::getExampleJson(json{{"Method", "From_File"}});
310 pGenJson["Random_Rotation"] = false;
311 pGenJson["Data"]["N"] = 2;
312 pGenJson["Data"]["0"] = {
313 {"x", R1}, {"y", R1}, {"z", 0.0},
314 {"theta", 0.0}, {"s", 1.0},
315 {"geom_id", 0}, {"mat_id", 0}, {"contact_id", 0}
316 };
317 pGenJson["Data"]["1"] = {
318 {"x", R1}, {"y", 2.0 * R1 + R2 + top_gap}, {"z", 0.0},
319 {"theta", two_particle_test ? M_PI / 2.0 : M_PI}, {"s", 1.0},
320 {"geom_id", 1}, {"mat_id", 1}, {"contact_id", 1}
321 };
322 pDeckJson["Particle_Generation"] = pGenJson;
323
324 auto j = json({{"Model", modelDeckJson},
325 {"Output", outputDeckJson},
326 {"Force_BC", bcDeckJson["Force_BC"]},
327 {"Displacement_BC", bcDeckJson["Displacement_BC"]},
328 {"IC", bcDeckJson["IC"]},
329 {"Particle", pDeckJson["Particle"]},
330 {"Mesh", pDeckJson["Mesh"]},
331 {"Material", pDeckJson["Material"]},
332 {"Contact", pDeckJson["Contact"]},
333 {"Neighbor", pDeckJson["Neighbor"]},
334 {"Particle_Generation", pDeckJson["Particle_Generation"]}});
335 if (two_particle_test)
336 j["Test"] = json{{"Test_Name", "two_particle"}};
337 // bottom_patch_bc keeps PD force on particle 0 (deformable base).
338 return j;
339}
void writeGeometry(json &j, const geom::GeomData &geomData)
Collection of methods and database related to input.
Definition pairForce.h:20
float R1
Definition problem.py:38

References geom::GeomData::d_geomName, geom::GeomData::d_geomParams, inp::BCDeck::getExampleJson(), inp::ContactPairDeck::getExampleJson(), inp::MaterialDeck::getExampleJson(), inp::ModelDeck::getExampleJson(), inp::PGenDeck::getExampleJson(), inp::PNeighborDeck::getExampleJson(), inp::BCBaseDeck::getExampleJson(), inp::OutputDeck::getExampleJson(), inp::ParticleDeck::getParticleContactExampleJson(), inp::ParticleDeck::getParticleGeomExampleJson(), inp::ParticleDeck::getParticleMaterialExampleJson(), util::harmonicMean(), util::normalContactStiffness(), material::toE(), material::toGE(), util::Point::toVec(), and geom::writeGeometry().

Referenced by main().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ buildTraskInputJson()

json anonymous_namespace{main.cpp}::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 
)

Definition at line 292 of file main.cpp.

296 {
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"},
305 {"Seed", 0}});
306 // Trask §5: broken bond → weight 0 (no force). No self-contact.
307 model["Self_Contact"] = "none";
308 model["Bond_Break"] = "tension"; // literature PMB: break in tension only
309
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)},
316 {"Debug", 1},
317 {"Perform_FE_Out", false},
318 {"Compress_Type", "zlib"},
319 {"Perform_Out", true},
320 {"Test_Output_Interval", num_steps},
321 {"Tag_PP", ""},
322 {"PVD_Collection", false}});
323
324 // Trask §6.2: top left/right of notches u=<0,0>; drive between notches
325 // u=<0,-v t>; sides and bottom free (collar bond-break is not implemented).
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,
328 0.55 * H, 0.};
329 json outer = fixedTopOuterBC(W, H, notch_half);
330 json bc_disp = {
331 {"Sets", 4},
332 {"Set_1", outer["Set_1"]},
333 {"Set_2", outer["Set_2"]},
334 {"Set_3",
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}}},
341 {"Set_4",
342 {{"Particle_List", std::vector<size_t>{0}},
343 {"Region", {{"Geometry", {{"Type", "rectangle"}, {"Parameters", drive_strip}}}}},
344 {"Direction", std::vector<size_t>{2}},
345 {"Time_Function",
346 {{"Type", "linear"}, {"Parameters", std::vector<double>{-v_impact}}}},
347 {"Spatial_Function", {{"Type", "constant"}}}}}};
348
349 geom::GeomData plate =
350 sillingNotchedPlateGeom(W, H, notch_half, notch_w, notch_depth);
352 json mesh = {{"Sets", 1}, {"Set_1", meshSetJson(mesh_plate, mesh_size)}};
355 json{{"Type", "PMBBond"},
356 {"Is_Plane_Strain", false},
357 {"Horizon", horizon},
358 {"Density", rho},
359 {"K", K},
360 {"G", G},
361 {"Gc", Gc},
362 {"E", E},
363 {"Compute_From_Classical", true},
364 {"Influence_Function", json{{"Type", 0}}}});
365 // Trask/Silling ω≡1 (ConstInfluence default a0=dim+1 would mis-scale c)
366 material["Set_1"]["Influence_Function"] = {
367 {"Type", 0}, {"Parameters", std::vector<double>{1.0}}};
368
369 return json{{"Model", model},
370 {"Output", output},
371 {"Displacement_BC", bc_disp},
372 {"Particle", particle},
373 {"Mesh", mesh},
374 {"Material", material}};
375}
geom::GeomData sillingNotchedPlateGeom(double W, double H, double notch_half, double notch_w, double notch_depth)
Definition main.cpp:219

References fixedTopOuterBC(), inp::MaterialDeck::getExampleJson(), inp::ModelDeck::getExampleJson(), inp::OutputDeck::getExampleJson(), inp::ParticleDeck::getParticleGeomExampleJson(), inp::ParticleDeck::getParticleMaterialExampleJson(), meshSetJson(), and sillingNotchedPlateGeom().

Referenced by main().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ checkWallSpringOnly()

Probe anonymous_namespace{main.cpp}::checkWallSpringOnly ( PeriDEMModel &  dem,
geom::GeomObject &  wall_geom 
)

Definition at line 204 of file main.cpp.

204 {
205 Probe p;
206 if (!dem.d_contact_p || !dem.d_contact_p->d_wallContact ||
207 !dem.d_contact_p->d_pairForce)
208 throw std::runtime_error("analytical wall: contact path not set up");
209
210 const auto &contact = dem.d_particleDeck_p->d_contactDeck.getContact(0, 1);
211 const double Kn = contact.d_Kn;
212 const double Rc = contact.d_contactR;
213
214 for (auto &f : dem.d_f)
215 f = util::Point();
216 dem.d_contact_p->d_wallContact->apply(dem, dem.d_contact_p->d_pairForce.get(),
217 /*use_node_damping*/ false);
218
219 for (size_t i = 0; i < dem.d_x.size(); ++i) {
220 if (dem.d_ptId[i] != 0)
221 continue;
223 if (!wall_geom.wallContactQuery(dem.d_x[i], hit) || !hit.active)
224 continue;
225 if (!(hit.signed_gap < Rc))
226 continue;
227
228 ++p.n_contact_nodes;
229 p.min_gap = std::min(p.min_gap, hit.signed_gap);
230
231 auto scalar = Kn * (hit.signed_gap - Rc) * dem.d_vol[i];
232 if (scalar > 0.)
233 scalar = 0.;
234 const util::Point f_ref = scalar * (-1. * hit.outward_n);
235 const double err = (dem.d_f[i] - f_ref).length();
236 p.max_force_err = std::max(p.max_force_err, err);
237 p.max_force_ref = std::max(p.max_force_ref, f_ref.length());
238 }
239 if (p.n_contact_nodes == 0)
240 p.min_gap = 0.;
241 return p;
242}
std::unique_ptr< contact::Contact > d_contact_p
std::vector< util::Point > d_f
Total force on the nodes.
Definition modelData.h:757
std::shared_ptr< inp::ParticleDeck > d_particleDeck_p
Pointer to deck holding particle related data.
Definition modelData.h:637
std::vector< double > d_vol
Nodal volumes.
Definition modelData.h:760
virtual bool wallContactQuery(const util::Point &x, WallContactHit &hit) const
Closest-point / signed-gap query for analytical wall contact. Default: unsupported (returns false,...
Collection of methods useful in simulation.
Definition constants.h:14
Result of a wall-contact query against a geom (analytical walls).
util::Point outward_n
double length() const
Computes the Euclidean length of the vector.
Definition point.h:124

References geom::WallContactHit::active, PeriDEMModel::d_contact_p, data::ModelData::d_f, data::ModelData::d_particleDeck_p, data::ModelData::d_ptId, data::ModelData::d_vol, data::ModelData::d_x, util::Point::length(), anonymous_namespace{main.cpp}::Probe::max_force_err, anonymous_namespace{main.cpp}::Probe::max_force_ref, anonymous_namespace{main.cpp}::Probe::min_gap, anonymous_namespace{main.cpp}::Probe::n_contact_nodes, geom::WallContactHit::outward_n, geom::WallContactHit::signed_gap, and geom::GeomObject::wallContactQuery().

Referenced by main().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ contactPairJson()

json anonymous_namespace{main.cpp}::contactPairJson ( double  R_contact_factor,
bool  damping_on,
bool  friction_on,
double  Kn,
double  beta_n_eps,
double  friction_coeff,
double  Kn_factor,
double  beta_n_factor 
)

Definition at line 181 of file main.cpp.

182 {
183 json j;
184 j["Contact_Radius_Factor"] = R_contact_factor;
185 if (Kn < 1e-20) {
186 j["Kn"] = 0.0;
187 } else {
188 j["Kn"] = Kn;
189 }
190 j["Damping_On"] = damping_on;
191 j["Epsilon"] = beta_n_eps;
192 j["Friction_On"] = friction_on;
193 j["Friction_Coeff"] = friction_coeff;
194 j["Kn_Factor"] = Kn_factor;
195 j["Beta_n_Factor"] = damping_on ? beta_n_factor : 0.0;
196 return j;
197}

Referenced by buildInputJson().

Here is the caller graph for this function:

◆ directoryPathWithTrailingSep()

std::string anonymous_namespace{main.cpp}::directoryPathWithTrailingSep ( const std::filesystem::path &  dir)

OutputDeck concatenates Path + "log.txt" without inserting a separator; Path must end with one.

Definition at line 34 of file main.cpp.

34 {
35 namespace fs = std::filesystem;
36 fs::path n = fs::absolute(dir).lexically_normal();
37 std::string s = n.string();
38 if (!s.empty() && s.back() != '/' && s.back() != '\\')
39 s += fs::path::preferred_separator;
40 return s;
41}

Referenced by main(), and runLaw().

Here is the caller graph for this function:

◆ doesParticleIntersect()

bool anonymous_namespace{main.cpp}::doesParticleIntersect ( const PackedParticle &  p,
const std::vector< PackedParticle > &  existing,
const std::vector< double > &  rect,
double  padding 
)

Definition at line 50 of file main.cpp.

51 {
52 const double p_rect_lox = p.x - p.r;
53 const double p_rect_loy = p.y - p.r;
54 const double p_rect_hix = p.x + p.r;
55 const double p_rect_hiy = p.y + p.r;
56 if (p_rect_lox < rect[0] + padding || p_rect_loy < rect[1] + padding || p_rect_hix > rect[3] - padding ||
57 p_rect_hiy > rect[4] - padding)
58 return true;
59
60 for (const auto &q : existing) {
61 const double dx = p.x - q.x;
62 const double dy = p.y - q.y;
63 const double dz = p.z - q.z;
64 const double dist = std::sqrt(dx * dx + dy * dy + dz * dz);
65 if (dist <= p.r + q.r + padding)
66 return true;
67 }
68 return false;
69}

References anonymous_namespace{main.cpp}::PackedParticle::r, anonymous_namespace{main.cpp}::PackedParticle::x, anonymous_namespace{main.cpp}::PackedParticle::y, and anonymous_namespace{main.cpp}::PackedParticle::z.

Referenced by generateParticleLocations().

Here is the caller graph for this function:

◆ dumpFinalNodal()

void anonymous_namespace{main.cpp}::dumpFinalNodal ( const data::ModelData &  data,
const std::filesystem::path &  out_dir 
)

Definition at line 74 of file main.cpp.

75 {
76 const size_t n = data.d_u.size();
77 std::vector<double> buf(6 * n, 0.);
78 for (size_t i = 0; i < n; ++i) {
79 if (!ownsNode(data, i))
80 continue;
81 buf[6 * i + 0] = data.d_u[i].d_x;
82 buf[6 * i + 1] = data.d_u[i].d_y;
83 buf[6 * i + 2] = data.d_u[i].d_z;
84 buf[6 * i + 3] = data.d_v[i].d_x;
85 buf[6 * i + 4] = data.d_v[i].d_y;
86 buf[6 * i + 5] = data.d_v[i].d_z;
87 }
89 MPI_Allreduce(MPI_IN_PLACE, buf.data(), static_cast<int>(buf.size()),
90 MPI_DOUBLE, MPI_SUM, util::parallel::mpiComm());
91 if (util::parallel::mpiRank() != 0)
92 return;
93
94 std::filesystem::create_directories(out_dir / "nodal");
95 {
96 std::ofstream xr(out_dir / "nodal" / "x_ref.bin", std::ios::binary);
97 const uint32_t nn = static_cast<uint32_t>(n);
98 xr.write(reinterpret_cast<const char *>(&nn), sizeof(nn));
99 for (size_t i = 0; i < n; ++i) {
100 const double p[3] = {data.d_xRef[i].d_x, data.d_xRef[i].d_y,
101 data.d_xRef[i].d_z};
102 xr.write(reinterpret_cast<const char *>(p), sizeof(p));
103 }
104 }
105 {
106 std::ofstream os(out_dir / "nodal" / "final_uv.bin", std::ios::binary);
107 const char magic[4] = {'P', 'D', 'U', 'V'};
108 const uint32_t step32 = static_cast<uint32_t>(data.currentStep());
109 const uint32_t nn = static_cast<uint32_t>(n);
110 os.write(magic, 4);
111 os.write(reinterpret_cast<const char *>(&step32), sizeof(step32));
112 os.write(reinterpret_cast<const char *>(&nn), sizeof(nn));
113 os.write(reinterpret_cast<const char *>(buf.data()),
114 static_cast<std::streamsize>(buf.size() * sizeof(double)));
115 }
116}
bool ownsNode(const data::ModelData &data, size_t i)
Definition main.cpp:55
Definition contact.h:20
int mpiSize()
Get size (number) of processors.
int mpiRank()
get rank (id) of this processor
MPI_Comm mpiComm()
Get MPI comm.

References util::parallel::mpiComm(), util::parallel::mpiRank(), util::parallel::mpiSize(), and ownsNode().

Referenced by main().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ fitCrackFromPhi()

CrackFit anonymous_namespace{main.cpp}::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 
)

Definition at line 110 of file main.cpp.

112 {
113 CrackFit fit;
114 std::vector<double> xs, ys;
115
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.;
122 bool found = false;
123 for (size_t i = 0; i < dem.d_xRef.size(); ++i) {
124 if (dem.d_ptId[i] != 0)
125 continue;
126 const double phi = nodeDamageForFit(dem, i, use_bond_count);
127 if (phi < best_phi)
128 continue;
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)
132 continue;
133 if (dy_ref < -2.0 * bin_h || dy_ref > band_y)
134 continue;
135 const double dx = dem.d_xRef[i].d_x - tip_x;
136 const double dy = tip_y - dem.d_xRef[i].d_y;
137 best_phi = phi;
138 best_dx = dx;
139 best_dy = dy;
140 found = true;
141 }
142 if (found) {
143 xs.push_back(best_dx);
144 ys.push_back(best_dy);
145 }
146 }
147 fit.n_pts = xs.size();
148 if (fit.n_pts < 4)
149 return fit;
150 double mx = 0., my = 0.;
151 for (size_t i = 0; i < fit.n_pts; ++i) {
152 mx += xs[i];
153 my += ys[i];
154 }
155 mx /= static_cast<double>(fit.n_pts);
156 my /= static_cast<double>(fit.n_pts);
157 fit.mean_dx = mx;
158 fit.mean_dy = my;
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;
163 cxx += x * x;
164 cxy += x * y;
165 cyy += y * y;
166 }
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;
171 double vx = cxy;
172 double vy = l1 - cxx;
173 if (vx * vx + vy * vy < 1.e-30) {
174 vx = l1 - cyy;
175 vy = cxy;
176 }
177 const double angle_from_horiz =
178 std::atan2(std::abs(vy), std::abs(vx)) * 180. / M_PI;
179 fit.angle_to_notch_deg = 90. - angle_from_horiz;
180 return fit;
181}
double nodeDamageForFit(const PeriDEMModel &dem, size_t i, bool use_bond_count)
Definition main.cpp:91
double det(const std::vector< std::vector< double > > &m)
Computes the determinant of matrix.
Definition matrix.cpp:75

References anonymous_namespace{main.cpp}::CrackFit::angle_to_notch_deg, data::ModelData::d_ptId, data::ModelData::d_xRef, anonymous_namespace{main.cpp}::CrackFit::mean_dx, anonymous_namespace{main.cpp}::CrackFit::mean_dy, anonymous_namespace{main.cpp}::CrackFit::n_pts, and nodeDamageForFit().

Referenced by main().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ fixedTopOuterBC()

json anonymous_namespace{main.cpp}::fixedTopOuterBC ( double  W,
double  H,
double  notch_half 
)

Definition at line 262 of file main.cpp.

262 {
263 const double fix_h = 0.08 * H;
264 return json{
265 {"Set_1",
266 {{"Particle_List", std::vector<size_t>{0}},
267 {"Region",
268 {{"Geometry",
269 {{"Type", "rectangle"},
270 {"Parameters",
271 std::vector<double>{-0.55 * W, 0.5 * H - fix_h, 0., -notch_half - 0.02 * W,
272 0.55 * H, 0.}}}}}},
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}}},
277 {"Set_2",
278 {{"Particle_List", std::vector<size_t>{0}},
279 {"Region",
280 {{"Geometry",
281 {{"Type", "rectangle"},
282 {"Parameters",
283 std::vector<double>{notch_half + 0.02 * W, 0.5 * H - fix_h, 0., 0.55 * W, 0.55 * H,
284 0.}}}}}},
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}}}};
289}

Referenced by buildBhatInputJson(), and buildTraskInputJson().

Here is the caller graph for this function:

◆ generateCircularGrid()

std::vector< PackedParticle > anonymous_namespace{main.cpp}::generateCircularGrid ( int  ncols,
int  nrows,
double  x_lo,
double  y_lo,
double  R,
double  gap 
)

Definition at line 61 of file main.cpp.

62 {
63 std::vector<PackedParticle> particles;
64 particles.reserve(static_cast<size_t>(ncols * nrows));
65 const double step = 2.0 * R + gap;
66 const double x0 = x_lo + gap + R;
67 const double y0 = y_lo + gap + R;
68 for (int j = 0; j < nrows; ++j) {
69 for (int i = 0; i < ncols; ++i) {
71 p.x = x0 + static_cast<double>(i) * step;
72 p.y = y0 + static_cast<double>(j) * step;
73 p.z = 0.0;
74 p.r = R;
75 p.theta = 0.0;
76 particles.push_back(p);
77 }
78 }
79 return particles;
80}

Referenced by buildInputJson(), and main().

Here is the caller graph for this function:

◆ generateParticleLocations()

std::vector< PackedParticle > anonymous_namespace{main.cpp}::generateParticleLocations ( const std::vector< double > &  in_rect,
double  max_y,
double  mesh_size,
double  R,
int  N_target,
double  padding,
std::mt19937 &  gen 
)

Random row-wise packing (same spirit as problem_setup.py method 1) with std::mt19937(30). One characteristic radius R; zones 0–7 pick among eight geometry types.

Definition at line 75 of file main.cpp.

77 {
78 std::vector<PackedParticle> particles;
79 std::uniform_real_distribution<double> u_r(-0.1 * R, 0.1 * R);
80 /* Do not use uniform_int_distribution: libstdc++ vs libc++ map mt19937
81 * differently, so geom_id diverges while x,y,theta stay the same. */
82
83 const double check_r = R;
84 const int rows = static_cast<int>((max_y - in_rect[1]) / (2.0 * check_r));
85 const double rect_L = in_rect[3] - in_rect[0];
86 const int cols = static_cast<int>(rect_L / (2.0 * check_r));
87
88 int counter = 0;
89 double x_old = in_rect[0];
90 double x_old_right = in_rect[3];
91 double y_old = in_rect[1];
92 const double cz = 0.0;
93
94 std::vector<double> cy_accptd;
95 cy_accptd.push_back(y_old);
96
97 std::vector<double> row_rads_prev{R, R};
98 for (int i = 0; i < rows; ++i) {
99 if (i > 0)
100 y_old = maxElem(cy_accptd) + maxElem(row_rads_prev);
101
102 std::vector<double> row_rads{R};
103
104 if (y_old + padding + maxElem(row_rads) >= max_y)
105 break;
106
107 int num_p_cols = 0;
108 int j = 0;
109 while (true) {
110 if (num_p_cols > cols - 1 || j > 100 * N_target)
111 break;
112 if (counter >= N_target)
113 break;
114
115 if (j == 0) {
116 x_old = in_rect[0];
117 x_old_right = in_rect[3];
118 }
119
120 const int p_zone = static_cast<int>(gen() % 8);
121 const double r0 = R;
122 double r = r0 + u_r(gen);
123
124 double cx = 0., cy = 0.;
125 if (i % 2 == 0) {
126 std::uniform_real_distribution<double> rph(-0.1 * r0, 0.05 * r0);
127 std::uniform_real_distribution<double> rpv(-0.05 * r0, 0.05 * r0);
128 const double cx0 = x_old_right - padding - r;
129 cx = cx0 - rph(gen);
130 cy = y_old + padding + r + rpv(gen);
131 } else {
132 std::uniform_real_distribution<double> rph(-0.05 * r0, 0.1 * r0);
133 std::uniform_real_distribution<double> rpv(-0.05 * r0, 0.05 * r0);
134 const double cx0 = x_old + padding + r;
135 cx = cx0 + rph(gen);
136 cy = y_old + padding + r + rpv(gen);
137 }
138
139 PackedParticle trial{p_zone, cx, cy, cz, r, 0.0};
140 if (!doesParticleIntersect(trial, particles, in_rect, padding)) {
141 std::uniform_real_distribution<double> orient(0.0, 2.0 * M_PI);
142 trial.theta = orient(gen);
143 particles.push_back(trial);
144 row_rads.push_back(trial.r);
145 cy_accptd.push_back(cy);
146 if (i % 2 == 0)
147 x_old_right = cx - trial.r;
148 else
149 x_old = cx + trial.r;
150
151 ++counter;
152 num_p_cols++;
153 }
154 ++j;
155 }
156 row_rads_prev = std::move(row_rads);
157 }
158 return particles;
159}
bool doesParticleIntersect(const PackedParticle &p, const std::vector< PackedParticle > &existing, const std::vector< double > &rect, double padding)
Definition main.cpp:50
double maxElem(const std::vector< double > &v)
Definition main.cpp:43

References doesParticleIntersect(), and maxElem().

Referenced by buildInputJson().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ is3DGeometry()

bool anonymous_namespace{main.cpp}::is3DGeometry ( const std::string &  g)

Definition at line 52 of file main.cpp.

52 {
53 return g == "sphere" || g == "ellipsoid" || g == "cube" || g == "cuboid" || g == "cylinder" ||
54 g == "sphere_minus_sphere" || g == "cuboid_minus_cuboid" || g == "open_cuboid_channel_3d";
55}

Referenced by buildInputJson().

Here is the caller graph for this function:

◆ isAcceptableGeometryName()

bool anonymous_namespace{main.cpp}::isAcceptableGeometryName ( const std::string &  g)

Definition at line 57 of file main.cpp.

57 {
58 for (const std::string &n : geom::getAcceptableGeometries()) {
59 if (n == g)
60 return true;
61 }
62 return false;
63}

References geom::getAcceptableGeometries().

Referenced by main().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ KnFromBulk()

double anonymous_namespace{main.cpp}::KnFromBulk ( double  Ka,
double  Kb,
double  horizon 
)

Definition at line 199 of file main.cpp.

199 {
200 return util::normalContactStiffness(Ka, Kb, horizon);
201}

References util::normalContactStiffness().

Referenced by buildInputJson().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ makeDeck()

json anonymous_namespace{main.cpp}::makeDeck ( const std::string &  model,
size_t  dim,
bool  plane_strain,
int  influence,
double  L,
double  h,
double  horizon,
double  E,
double  nu,
double  Gc,
const std::string &  out 
)

Definition at line 32 of file main.cpp.

34 {
35 const double rho = 1000.;
36 json j;
38 json{{"Dimension", dim}, {"Final_Time", 1.0e-6}, {"Time_Steps", 1},
39 {"Particle_Sim_Type", "Single_Particle"}});
41 json{{"Path", out}, {"Perform_Out", false}, {"Debug", 0}});
43 particle["Sets"] = 1;
44 particle["Set_1"] =
45 json{{"Type", dim == 2 ? "rectangle" : "cuboid"},
46 {"Parameters", {-L / 2, -L / 2, dim == 2 ? 0. : -L / 2, L / 2, L / 2,
47 dim == 2 ? 0. : L / 2}}};
48 j["Particle"] = particle;
49 j["Mesh"] = json{{"Sets", 1},
50 {"Set_1", json{{"File", out + "mesh.vtu"},
51 {"CreateMesh", json{{"Flag", true},
52 {"Info", "uniform"},
53 {"Mesh_Size", h}}}}}};
55 json m{{"Type", model}, {"Is_Plane_Strain", plane_strain},
56 {"Horizon", horizon}, {"Density", rho}, {"E", E}, {"Gc", Gc},
57 {"Compute_From_Classical", true},
58 {"Influence_Function", json{{"Type", influence}}}};
59 if (model == "PDState")
60 m["G"] = E / (2. * (1. + nu));
61 mat["Set_1"] = inp::MaterialDeck::getExampleJson(m);
62 j["Material"] = mat;
65 json{{"Contact_Radius", 0.9 * h}, {"Kn", 1.0}, {"Damping_On", false},
66 {"Friction_On", false}, {"Friction_Coeff", 0.}, {"K", E}});
67 j["Contact"] = contact;
68 j["Particle_Generation"] = json{
69 {"Method", "From_File"},
70 {"Data", json{{"N", 1},
71 {"0", json{{"x", 0.}, {"y", 0.}, {"z", 0.}, {"theta", 0.},
72 {"s", 1.}, {"geom_id", 0}, {"mat_id", 0},
73 {"contact_id", 0}}}}}};
74 return j;
75}

References inp::ContactPairDeck::getExampleJson(), inp::MaterialDeck::getExampleJson(), inp::ModelDeck::getExampleJson(), inp::OutputDeck::getExampleJson(), inp::ParticleDeck::getParticleContactExampleJson(), and inp::ParticleDeck::getParticleMaterialExampleJson().

Referenced by main().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ maxAbsForceFromCsv()

double anonymous_namespace{main.cpp}::maxAbsForceFromCsv ( const std::filesystem::path &  csv)

Definition at line 333 of file main.cpp.

333 {
334 std::ifstream is(csv);
335 if (!is)
336 return 0.;
337 std::string line;
338 std::getline(is, line);
339 double max_abs = 0.;
340 while (std::getline(is, line)) {
341 if (line.empty())
342 continue;
343 const auto c2 = line.rfind(',');
344 if (c2 == std::string::npos)
345 continue;
346 max_abs = std::max(max_abs, std::abs(std::stod(line.substr(c2 + 1))));
347 }
348 return max_abs;
349}

Referenced by main().

Here is the caller graph for this function:

◆ maxElem()

double anonymous_namespace{main.cpp}::maxElem ( const std::vector< double > &  v)

Definition at line 43 of file main.cpp.

43 {
44 double m = -std::numeric_limits<double>::infinity();
45 for (double x : v)
46 m = std::max(m, x);
47 return m;
48}

Referenced by generateParticleLocations().

Here is the caller graph for this function:

◆ measure()

CrackMetrics anonymous_namespace{main.cpp}::measure ( PeriDEMModel &  dem,
double  x_crack,
double  band 
)

Definition at line 170 of file main.cpp.

170 {
171 CrackMetrics m;
172 for (size_t i = 0; i < dem.d_neighPd.size(); ++i) {
173 const auto &xi = dem.d_xRef[i];
174 for (size_t k = 0; k < dem.d_neighPd[i].size(); ++k) {
175 if (!dem.d_fracture_p->getBondState(i, k))
176 continue;
177 const size_t j = dem.d_neighPd[i][k];
178 const auto &xj = dem.d_xRef[j];
179 if ((xi.d_x - x_crack) * (xj.d_x - x_crack) >= 0.)
180 continue;
181 const double gap = (dem.d_x[j] - dem.d_x[i]).length();
182 m.min_gap = std::min(m.min_gap, gap);
183 m.max_gap = std::max(m.max_gap, gap);
184 ++m.n_pairs;
185 }
186 }
187 if (m.n_pairs == 0)
188 m.min_gap = 0.;
189
190 for (size_t i = 0; i < dem.d_xRef.size(); ++i) {
191 const double x = dem.d_xRef[i].d_x;
192 const double fx = dem.d_f[i].d_x * dem.d_vol[i];
193 if (x < x_crack && x > x_crack - band)
194 m.force_x_left += fx;
195 if (x >= x_crack && x < x_crack + band)
196 m.force_x_right += fx;
197 }
198 return m;
199}

References data::ModelData::d_f, data::ModelData::d_fracture_p, data::ModelData::d_neighPd, data::ModelData::d_vol, data::ModelData::d_x, data::ModelData::d_xRef, anonymous_namespace{main.cpp}::CrackMetrics::force_x_left, anonymous_namespace{main.cpp}::CrackMetrics::force_x_right, anonymous_namespace{main.cpp}::CrackMetrics::max_gap, anonymous_namespace{main.cpp}::CrackMetrics::min_gap, and anonymous_namespace{main.cpp}::CrackMetrics::n_pairs.

Referenced by runLaw().

Here is the caller graph for this function:

◆ meshSetJson()

json anonymous_namespace{main.cpp}::meshSetJson ( const std::filesystem::path &  f,
double  mesh_size 
)

Definition at line 183 of file main.cpp.

183 {
184 return json{{"File", f.string()},
185 {"CreateMesh",
186 {{"Flag", true},
187 {"Info", "gmsh_builtin_mesh"},
188 {"Mesh_Size", mesh_size},
189 {"Write_Mesh_File", true}}}};
190}

Referenced by buildBhatInputJson(), buildImpactInputJson(), and buildTraskInputJson().

Here is the caller graph for this function:

◆ minGrainWallGap()

double anonymous_namespace{main.cpp}::minGrainWallGap ( PeriDEMModel &  dem,
geom::GeomObject &  wall_geom 
)

Definition at line 244 of file main.cpp.

244 {
245 double ming = 1.e300;
246 size_t n = 0;
247 for (size_t i = 0; i < dem.d_x.size(); ++i) {
248 if (dem.d_ptId[i] != 0)
249 continue;
251 if (!wall_geom.wallContactQuery(dem.d_x[i], hit) || !hit.active)
252 continue;
253 ming = std::min(ming, hit.signed_gap);
254 ++n;
255 }
256 return n ? ming : 0.;
257}

References geom::WallContactHit::active, data::ModelData::d_ptId, data::ModelData::d_x, geom::WallContactHit::signed_gap, and geom::GeomObject::wallContactQuery().

Referenced by main().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ near()

bool anonymous_namespace{main.cpp}::near ( double  a,
double  b,
double  tol = 1.e-12 
)

Definition at line 25 of file main.cpp.

25 {
26 return std::abs(a - b) <= tol * (1. + std::abs(a) + std::abs(b));
27}

Referenced by main().

Here is the caller graph for this function:

◆ nodeDamageForFit()

double anonymous_namespace{main.cpp}::nodeDamageForFit ( const PeriDEMModel &  dem,
size_t  i,
bool  use_bond_count 
)

Definition at line 91 of file main.cpp.

91 {
92 if (use_bond_count) {
93 if (!dem.d_phiBond.empty() && i < dem.d_phiBond.size())
94 return static_cast<double>(dem.d_phiBond[i]);
95 return nodePhi(dem, i);
96 }
97 if (!dem.d_phi.empty() && i < dem.d_phi.size())
98 return static_cast<double>(dem.d_phi[i]);
99 return nodePhi(dem, i);
100}
std::vector< float > d_phi
Damage function at the nodes (volume-weighted, Silling 2000)
Definition modelData.h:834
std::vector< float > d_phiBond
Damage as broken-bond count fraction (Bhattacharya & Lipton 2023)
Definition modelData.h:837
double nodePhi(const PeriDEMModel &dem, size_t i)
Definition main.cpp:79

References data::ModelData::d_phi, data::ModelData::d_phiBond, and nodePhi().

Referenced by fitCrackFromPhi(), and main().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ nodePhi()

double anonymous_namespace{main.cpp}::nodePhi ( const PeriDEMModel &  dem,
size_t  i 
)

Definition at line 79 of file main.cpp.

79 {
80 const auto &nb = dem.d_neighPd[i];
81 if (nb.empty())
82 return 0.;
83 size_t n_br = 0;
84 for (size_t k = 0; k < nb.size(); ++k) {
85 if (dem.d_fracture_p->getBondState(i, k))
86 ++n_br;
87 }
88 return static_cast<double>(n_br) / static_cast<double>(nb.size());
89}

References data::ModelData::d_fracture_p, and data::ModelData::d_neighPd.

Referenced by main(), and nodeDamageForFit().

Here is the caller graph for this function:

◆ notchVoidBoxes()

std::vector< std::vector< double > > anonymous_namespace{main.cpp}::notchVoidBoxes ( double  H,
double  notch_half,
double  notch_w,
double  notch_depth,
double  z_lo,
double  z_hi 
)

Definition at line 207 of file main.cpp.

209 {
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}};
215}

Referenced by buildImpactInputJson().

Here is the caller graph for this function:

◆ ownsNode()

bool anonymous_namespace{main.cpp}::ownsNode ( const data::ModelData &  data,
size_t  i 
)

Definition at line 55 of file main.cpp.

55 {
56 const int rank = util::parallel::mpiRank();
57 if (data.d_pdDofMpi) {
58 if (i >= data.d_pdNodePartition.size())
59 return false;
60 return static_cast<int>(data.d_pdNodePartition[i]) == rank;
61 }
62 for (const auto *p : data.d_particlesListTypeAll) {
63 const size_t i0 = p->d_globStart;
64 const size_t i1 = i0 + p->getNumNodes();
65 if (i < i0 || i >= i1)
66 continue;
67 if (p->isWall())
68 return rank == 0;
69 return particle::isLocallyOwned(*p);
70 }
71 return rank == 0;
72}
bool isLocallyOwned(const BaseParticle &p)
True if this rank updates / assembles forces for the particle. Walls are replicated on every rank....

References particle::isLocallyOwned(), and util::parallel::mpiRank().

Referenced by dumpFinalNodal(), and anonymous_namespace{main.cpp}::GrainContactProbe::sampleMetric().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ packedAxisBounds()

PackedBounds anonymous_namespace{main.cpp}::packedAxisBounds ( const std::vector< PackedParticle > &  packed)

Definition at line 166 of file main.cpp.

166 {
167 PackedBounds b;
168 b.min_x = std::numeric_limits<double>::infinity();
169 b.max_x = -std::numeric_limits<double>::infinity();
170 b.min_y = std::numeric_limits<double>::infinity();
171 b.max_y = -std::numeric_limits<double>::infinity();
172 for (const auto &p : packed) {
173 b.min_x = std::min(b.min_x, p.x - p.r);
174 b.max_x = std::max(b.max_x, p.x + p.r);
175 b.min_y = std::min(b.min_y, p.y - p.r);
176 b.max_y = std::max(b.max_y, p.y + p.r);
177 }
178 return b;
179}

References anonymous_namespace{main.cpp}::PackedBounds::max_x, anonymous_namespace{main.cpp}::PackedBounds::max_y, anonymous_namespace{main.cpp}::PackedBounds::min_x, and anonymous_namespace{main.cpp}::PackedBounds::min_y.

Referenced by buildInputJson().

Here is the caller graph for this function:

◆ runLaw()

LawResult anonymous_namespace{main.cpp}::runLaw ( const std::string &  self_contact,
const std::filesystem::path &  run_dir,
double  L,
double  mesh_size,
double  horizon,
unsigned  n_threads,
bool  deck_only = false 
)

Definition at line 207 of file main.cpp.

209 {
210 namespace fs = std::filesystem;
211 const fs::path out_dir = run_dir / "out";
212 const fs::path inp_dir = run_dir / "inp";
213 fs::create_directories(out_dir);
214 fs::create_directories(inp_dir);
215
216 const fs::path mesh = inp_dir / "mesh_rect.msh";
217 // Time fields unused for prescribed kinematics; keep Output_Interval valid.
218 auto input_json =
219 buildInputJson(directoryPathWithTrailingSep(out_dir), mesh, L, mesh_size, horizon, 1.0e-6,
220 2, self_contact);
221 {
222 std::ofstream os(inp_dir / "input.json");
223 os << input_json.dump(2);
224 }
225
226 // -deckOnly stops after the deck is written. The deck comparison reads the
227 // deck and does not need this driver to run the simulation.
228 if (deck_only) {
229 util::io::print("deck written; -deckOnly, not running\n");
230 return LawResult{};
231 }
232
233 auto deck = std::make_shared<inp::Input>(input_json);
234 PeriDEMModel dem(deck);
235 dem.init();
236
237 const double x_crack = 0.5 * L;
238 const size_t n_broken = applyPrecrack(dem, x_crack);
239 if (n_broken == 0)
240 throw std::runtime_error(self_contact + ": precrack broke no bonds");
241
242 const double Rc = dem.d_maxContactR > 0. ? dem.d_maxContactR : 0.95 * mesh_size;
243 const double h = dem.d_hMax > 0. ? dem.d_hMax : mesh_size;
244 util::io::print(std::format("{}: nodes={}, broken_bonds={}, h={}, Rc={}, horizon={}\n",
245 self_contact, dem.d_x.size(), n_broken, h, Rc, horizon));
246
247 // Open: separate faces so current gap >> Rc (no self-contact yet).
248 const double u_open = 2.0 * Rc;
249 applyRigidOpenClose(dem, x_crack, u_open);
250 dem.computeForces();
251 const auto open_m = measure(dem, x_crack, 2.0 * horizon);
252 if (!(open_m.min_gap > 1.5 * Rc))
253 throw std::runtime_error(std::format("{}: open did not separate (min_gap={})", self_contact,
254 open_m.min_gap));
255
256 // Close into the window where reference_gap (natural R = r0) and
257 // broken_bond_kn (natural R = Rc) disagree: Rc < R < typical r0.
258 // Mid-horizon neighbor distances are ~O(horizon) >> Rc = 0.95 h.
259 const double u_close = -0.5 * (Rc + 0.5 * horizon);
260 applyRigidOpenClose(dem, x_crack, u_close);
261 dem.computeForces();
262 const auto close_m = measure(dem, x_crack, 2.0 * horizon);
263 const double force_amp =
264 0.5 * (std::abs(close_m.force_x_left) + std::abs(close_m.force_x_right));
265
266 util::io::print(std::format("{}: open min_gap={}, close min_gap={}, force_amp={}\n", self_contact,
267 open_m.min_gap, close_m.min_gap, force_amp));
268
269 dem.close();
270
271 LawResult r;
272 r.open_min_gap = open_m.min_gap;
273 r.close_min_gap = close_m.min_gap;
274 r.close_force_amp = force_amp;
275 return r;
276}
std::string directoryPathWithTrailingSep(const std::filesystem::path &dir)
Definition main.cpp:34
json buildInputJson(const std::string &output_path, const std::filesystem::path &mesh_file, double L, double mesh_size, double horizon, double final_time, size_t num_steps, const std::string &self_contact)
Definition main.cpp:44
CrackMetrics measure(PeriDEMModel &dem, double x_crack, double band)
Definition main.cpp:170
void applyRigidOpenClose(PeriDEMModel &dem, double x_crack, double u_right)
Definition main.cpp:153
size_t applyPrecrack(PeriDEMModel &dem, double x_crack)
Definition main.cpp:137

References applyPrecrack(), applyRigidOpenClose(), buildInputJson(), PeriDEMModel::close(), anonymous_namespace{main.cpp}::LawResult::close_force_amp, anonymous_namespace{main.cpp}::LawResult::close_min_gap, PeriDEMModel::computeForces(), data::ModelData::d_hMax, data::ModelData::d_maxContactR, data::ModelData::d_x, directoryPathWithTrailingSep(), PeriDEMModel::init(), measure(), anonymous_namespace{main.cpp}::LawResult::open_min_gap, and util::io::print().

Referenced by main().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ sillingNotchedPlateGeom()

geom::GeomData anonymous_namespace{main.cpp}::sillingNotchedPlateGeom ( double  W,
double  H,
double  notch_half,
double  notch_w,
double  notch_depth 
)

Definition at line 219 of file main.cpp.

220 {
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;
224 geom::GeomData plate;
225 plate.d_geomName = "complex";
226 plate.d_geomComplexInfo = {
227 std::vector<std::string>{"rectangle", "rectangle", "rectangle"},
228 std::vector<std::string>{"plus", "minus", "minus"}};
229 plate.d_geomParams = {
230 // outer plate
231 x0, y0, 0., x1, y1, 0.,
232 // left notch cutout (opens at top)
233 -notch_half - hw, y_tip, 0., -notch_half + hw, y1 + 1.0e-6, 0.,
234 // right notch cutout
235 notch_half - hw, y_tip, 0., notch_half + hw, y1 + 1.0e-6, 0.};
236 return plate;
237}

References geom::GeomData::d_geomComplexInfo, geom::GeomData::d_geomName, and geom::GeomData::d_geomParams.

Referenced by buildTraskInputJson().

Here is the caller graph for this function:

◆ uniformMeshSetJson()

json anonymous_namespace{main.cpp}::uniformMeshSetJson ( const std::filesystem::path &  f,
double  mesh_size,
const std::vector< std::vector< double > > &  voids = {} 
)

Definition at line 195 of file main.cpp.

196 {}) {
197 json cm = {{"Flag", true},
198 {"Info", "uniform"},
199 {"Mesh_Size", mesh_size},
200 {"Write_Mesh_File", true}};
201 if (!voids.empty())
202 cm["Void_Regions"] = voids;
203 return json{{"File", f.string()}, {"CreateMesh", cm}};
204}

Referenced by buildImpactInputJson().

Here is the caller graph for this function:

◆ writeLocations()

void anonymous_namespace{main.cpp}::writeLocations ( const std::filesystem::path &  csv,
const std::vector< PackedParticle > &  packed 
)

Definition at line 325 of file main.cpp.

326 {
327 std::ofstream os(csv);
328 os << "i, x, y, z, r, o\n";
329 for (const auto &p : packed)
330 os << std::format("0, {:.6f}, {:.6f}, {:.6f}, {:.6f}, 0.000000\n", p.x, p.y, p.z, p.r);
331}

Referenced by main().

Here is the caller graph for this function: