PeriDEM 0.3.0
PeriDEM -- Peridynamics-based high-fidelity model for granular media
Loading...
Searching...
No Matches
gen_input.py
Go to the documentation of this file.
1#!/usr/bin/env python3
2"""Build modular JSON for attrition sim1 — match main GIF deck.
3
4Reference (main README links attrition_test_sim1.gif here):
5 examples/PeriDEM/attrition_tests/sim1_multi_particle_circ_tri_drum_with_rotating_cylinder_with_protrusion
6
7Physics from that problem_setup.py: Kn_Factor=1, Gc=50/100, Bond_Break unset
8(tension default). Wall site = ComplexGeom centroid so mesh at origin is not shifted.
9"""
10
11from __future__ import annotations
12
13import json
14import math
15from pathlib import Path
16
17HERE = Path(__file__).resolve().parent
18MESH = HERE / "meshes"
19CSV = HERE / "particle_locations_0.csv"
20
21R_REF = {0: 0.001, 1: 0.001, 2: 0.001, 3: 0.003, 4: 0.003, 5: 0.003}
22MESH_FILES = [
23 "mesh_cir_small_0.msh",
24 "mesh_tri_small_0.msh",
25 "mesh_drum2d_small_0.msh",
26 "mesh_cir_large_0.msh",
27 "mesh_tri_large_0.msh",
28 "mesh_drum2d_large_0.msh",
29 "mesh_wall_0.msh",
30]
31
32# outer circle, inner circle (minus), protrusion rectangle (plus)
33WALL_PARAMS = [
34 0.021,
35 0.0,
36 0.0,
37 0.0,
38 0.02,
39 0.0,
40 0.0,
41 0.0,
42 0.014,
43 -0.0015,
44 0.0,
45 0.02,
46 0.0015,
47 0.0,
48]
49
50# Kn from sim1 (h=6e-4, K_small=1e4, K_large=K_wall=1e5)
51KN = {
52 (0, 0): 7.368284e20,
53 (0, 1): 1.339688e21,
54 (0, 2): 1.339688e21,
55 (1, 1): 7.368284e21,
56 (1, 2): 7.368284e21,
57 (2, 2): 7.368284e21,
58}
59
60K_MAT = {0: 1.0e4, 1: 1.0e5, 2: 1.0e5}
61KN_FACTOR = 1.0 # GIF / main sim1 (not increased_Kn_factor)
62R_IN = 0.02
63
64
65def wall_geom_center(params: list[float]) -> tuple[float, float, float]:
66 """Signed-volume centroid of circle(+)-circle(-)-rectangle(+) wall."""
67 r_out, cx0, cy0, cz0 = params[0], params[1], params[2], params[3]
68 r_in, cx1, cy1, cz1 = params[4], params[5], params[6], params[7]
69 x0, y0, z0, x1, y1, z1 = params[8:14]
70 a_out = math.pi * r_out * r_out
71 a_in = math.pi * r_in * r_in
72 a_rect = abs(x1 - x0) * abs(y1 - y0)
73 vol = a_out - a_in + a_rect
74 cx = (a_out * cx0 - a_in * cx1 + a_rect * 0.5 * (x0 + x1)) / vol
75 cy = (a_out * cy0 - a_in * cy1 + a_rect * 0.5 * (y0 + y1)) / vol
76 cz = (a_out * cz0 - a_in * cz1 + a_rect * 0.5 * (z0 + z1)) / vol
77 return cx, cy, cz
78
79
80def contact_pair(i: int, j: int) -> dict:
81 a, b = min(i, j), max(i, j)
82 return {
83 "Contact_Radius_Factor": 0.95,
84 "Damping_On": False,
85 "Friction_On": False,
86 "Kn": KN[(a, b)],
87 "K": 2.0 * K_MAT[a] * K_MAT[b] / (K_MAT[a] + K_MAT[b]),
88 "Epsilon": 0.95,
89 "Friction_Coeff": 0.5,
90 "Kn_Factor": KN_FACTOR,
91 "Beta_n_Factor": 100.0,
92 }
93
94
95def material(horizon: float, K: float, G: float, Gc: float) -> dict:
96 return {
97 "Type": "PDState",
98 "Horizon": horizon,
99 "Density": 1200.0,
100 "Compute_From_Classical": True,
101 "K": K,
102 "G": G,
103 "Gc": Gc,
104 "Influence_Function": {"Type": 1},
105 }
106
107
109 final_time: float,
110 time_steps: int,
111 out_path: str,
112 output_interval: int,
113 omega: float = -20.0 * math.pi,
114) -> dict:
115 horizon = 6.0e-4
116 particles = []
117 with CSV.open() as f:
118 next(f)
119 for line in f:
120 parts = [p.strip() for p in line.split(",")]
121 if len(parts) < 6:
122 continue
123 zi = int(float(parts[0]))
124 x, y, z = float(parts[1]), float(parts[2]), float(parts[3])
125 r, o = float(parts[4]), float(parts[5])
126 large = zi >= 3
127 if math.hypot(x, y) + r > R_IN - 1.0e-6:
128 raise SystemExit(
129 f"IC overlap: particle zone={zi} at ({x},{y}) r={r} "
130 f"extends past R_in={R_IN}"
131 )
132 particles.append(
133 {
134 "x": x,
135 "y": y,
136 "z": z,
137 "theta": o,
138 "s": r / R_REF[zi],
139 "geom_id": zi,
140 "mat_id": 1 if large else 0,
141 "contact_id": 1 if large else 0,
142 }
143 )
144
145 wall_id = len(particles)
146 # MUST equal ComplexGeom centroid so mesh (axis at origin) is not shifted.
147 wcx, wcy, wcz = wall_geom_center(WALL_PARAMS)
148 particles.append(
149 {
150 "x": wcx,
151 "y": wcy,
152 "z": wcz,
153 "theta": 0.0,
154 "s": 1.0,
155 "geom_id": 6,
156 "mat_id": 1,
157 "contact_id": 2,
158 "is_wall": True,
159 }
160 )
161
162 gen = {"Method": "From_File", "Random_Rotation": False, "Data": {"N": len(particles)}}
163 for i, p in enumerate(particles):
164 gen["Data"][str(i)] = p
165
166 particle_geom = {
167 "Sets": 7,
168 "Set_1": {"Type": "circle", "Parameters": [0.001, 0.0, 0.0, 0.0]},
169 "Set_2": {"Type": "triangle", "Parameters": [0.001, 0.0, 0.0, 0.0]},
170 "Set_3": {"Type": "drum2d", "Parameters": [0.001, 0.0004, 0.0, 0.0, 0.0]},
171 "Set_4": {"Type": "circle", "Parameters": [0.003, 0.0, 0.0, 0.0]},
172 "Set_5": {"Type": "triangle", "Parameters": [0.003, 0.0, 0.0, 0.0]},
173 "Set_6": {"Type": "drum2d", "Parameters": [0.003, 0.0012, 0.0, 0.0, 0.0]},
174 "Set_7": {
175 "Type": "complex",
176 "Vec_type": ["circle", "circle", "rectangle"],
177 "Vec_flag": ["plus", "minus", "plus"],
178 "Parameters": WALL_PARAMS,
179 },
180 }
181
182 mesh = {"Sets": 7}
183 for i, name in enumerate(MESH_FILES):
184 mesh[f"Set_{i + 1}"] = {"File": str((MESH / name).resolve())}
185
186 contact = {
187 "Sets": 3,
188 "Damping_Law": "off",
189 "Friction_Law": "coulomb_simple",
190 "Correct_Volume": False, # main DEM contact used full Vj
191 }
192 for i in range(3):
193 for j in range(i, 3):
194 contact[f"Set_{i + 1}_{j + 1}"] = contact_pair(i, j)
195
196 return {
197 "Model": {
198 "Dimension": 2,
199 "Final_Time": final_time,
200 "Time_Steps": time_steps,
201 "Discretization_Type": {
202 "Spatial": "finite_difference",
203 "Time": "central_difference",
204 },
205 "Populate_ElementNodeConnectivity": True,
206 "Quad_Approximation_Order": 2,
207 "Particle_Sim_Type": "Multi_Particle",
208 "Seed": 0,
209 # Bond_Break omitted → tension (PMB default)
210 "Bond_Break": "tension",
211 "Self_Contact": "none",
212 "Wall_Contact": "meshed",
213 },
214 "Output": {
215 "Path": out_path if out_path.endswith("/") else out_path + "/",
216 "Perform_Out": True,
217 "Tags": [
218 "Displacement",
219 "Velocity",
220 "Force",
221 "Damage_Z",
222 "Damage",
223 "Particle_ID",
224 "Fixity",
225 "Contact_Nodes",
226 ],
227 "Output_Interval": output_interval,
228 "Debug": 1,
229 "Perform_FE_Out": False,
230 "Compress_Type": "zlib",
231 "File_Format": "vtu",
232 "Test_Output_Interval": max(1, output_interval // 10),
233 "Tag_PP": "0",
234 "PVD_Collection": True,
235 },
236 "Force_BC": {"Gravity": [0.0, -10.0, 0.0]},
237 "Displacement_BC": {
238 "Sets": 1,
239 "Set_1": {
240 "Particle_List": [wall_id],
241 "Direction": [1, 2],
242 "Time_Function": {
243 "Type": "rotation",
244 "Parameters": [omega, 0.0, 0.0, 0.0],
245 },
246 "Spatial_Function": {"Type": "rotation"},
247 "Zero_Displacement": False,
248 },
249 },
250 "Particle": particle_geom,
251 "Mesh": mesh,
252 "Material": {
253 "Sets": 2,
254 # paper Gc: small=50, large/wall=100
255 "Set_1": material(horizon, 1.0e4, 6.0e3, 50.0),
256 "Set_2": material(horizon, 1.0e5, 6.0e4, 100.0),
257 },
258 "Contact": contact,
259 "Neighbor": {
260 "Update_Criteria": "simple_all",
261 "Search_Factor": 10.0,
262 "Search_Interval": 40,
263 "Near_Bd_Nodes_Tol": 0.5,
264 },
265 "Particle_Generation": gen,
266 }
267
268
269def main() -> None:
270 for mesh in MESH_FILES:
271 if not (MESH / mesh).is_file():
272 raise SystemExit(f"missing mesh {MESH / mesh}")
273 if not CSV.is_file():
274 raise SystemExit(f"missing {CSV}")
275
276 short = build(0.01, 100000, "runs/out", 2000)
277 medium = build(0.03, 300000, "runs/out", 3000)
278 paper = build(0.1, 1000000, "runs/out", 2500)
279 (HERE / "input_short.json").write_text(json.dumps(short, indent=2) + "\n")
280 (HERE / "input_medium.json").write_text(json.dumps(medium, indent=2) + "\n")
281 (HERE / "input.json").write_text(json.dumps(paper, indent=2) + "\n")
282 n = short["Particle_Generation"]["Data"]["N"]
283 wid = short["Displacement_BC"]["Set_1"]["Particle_List"][0]
284 wp = short["Particle_Generation"]["Data"][str(wid)]
285 print(
286 f"wrote input_short/medium/json (N={n}, wall_id={wid}, KnF={KN_FACTOR}, "
287 f"Gc=50/100, Bond_Break=tension, Damp=off, "
288 f"wall_site=({wp['x']:.6f},{wp['y']:.6f}))"
289 )
290
291
292if __name__ == "__main__":
293 main()
dict build(float final_time, int time_steps, str out_path, int output_interval, float omega=-20.0 *math.pi)
Definition gen_input.py:114
None main()
Definition gen_input.py:269
tuple[float, float, float] wall_geom_center(list[float] params)
Definition gen_input.py:65
dict contact_pair(int i, int j)
Definition gen_input.py:80