PeriDEM 0.3.0
PeriDEM -- Peridynamics-based high-fidelity model for granular media
Loading...
Searching...
No Matches
problem.py
Go to the documentation of this file.
1#!/usr/bin/env python3
2# -------------------------------------------
3# Copyright (c) 2021 - 2026 Prashant K. Jha
4# -------------------------------------------
5# PeriDEM https://github.com/prashjha/PeriDEM
6#
7# Distributed under the Boost Software License, Version 1.0. (See accompanying
8# file LICENSE)
9
10"""Single circular particle: fixed SW patch, linear pull on the NE patch.
11
12The deck is built in Python. ``input.json`` next to this file states the same
13problem as a deck file. ``python/tests/test_example_parity.py`` runs the Python
14deck through ``bin/PeriDEM`` and through the in-process interface and compares
15the node fields.
16"""
17
18from __future__ import annotations
19
20import os
21import sys
22from pathlib import Path
23
24HERE = Path(__file__).resolve().parent
25for _p in HERE.parents:
26 if (_p / "examples" / "peridem_env.py").is_file():
27 sys.path.insert(0, str(_p / "examples"))
28 break
29import peridem_env # noqa: F401,E402 (puts the built peridem on sys.path)
30
31import peridem # noqa: E402
32from peridem import Deck, Geometry # noqa: E402
33from peridem.deck import MeshSpec, to_G # noqa: E402
34
35RADIUS = 0.003
36HORIZON = 0.0006
37DENSITY = 1200.0
38K = 216000.0
39NU = 0.25
40GC = 500.0
41
42FINAL_TIME = 0.01
43NUM_STEPS = 20000
44PULL_RATE = 0.005 # m/s, linear time function on the NE patch
45
46TAGS = ["Displacement", "Velocity", "Force", "Force_Density", "Damage_Z",
47 "Damage", "Nodal_Volume", "Zone_ID", "Particle_ID", "Fixity",
48 "Force_Fixity", "Theta"]
49
50MESH_FILE = "mesh_cir_1_0.msh"
51
52
53def build_deck(output_path: str | os.PathLike[str] = "runs/", *,
54 final_time: float = FINAL_TIME, num_steps: int = NUM_STEPS,
55 output_interval: int = 1000,
56 mesh: str | os.PathLike[str] | None = None,
57 mesh_size: float | None = None,
58 tags: list[str] | None = None) -> Deck:
59 """Assemble the deck.
60
61 Pass ``mesh_size`` to have Gmsh build the disc in memory (no ``.msh`` on
62 disk); otherwise the committed ``mesh_cir_1_0.msh`` is read, which is what
63 the parity run against ``bin/PeriDEM`` uses.
64 """
65 d = Deck(dim=2, t_final=final_time, n_steps=num_steps,
66 particle_sim_type="Single_Particle")
67 d.set_comment("Single-particle circle: fixed SW patch, linear pull on NE "
68 "(PDState), set up in Python")
69 d.set_output(output_path, tags=tags or TAGS, interval=output_interval,
70 debug=1, tag_pp="0", pvd_collection=True)
71
72 if mesh_size is not None:
73 mesh_spec = MeshSpec(size=mesh_size)
74 else:
75 mesh_spec = MeshSpec(file=Path(mesh) if mesh is not None
76 else HERE / MESH_FILE)
77 d.add_particle_type(Geometry("circle", [RADIUS, 0.0, 0.0, 0.0]), mesh_spec)
78 d.add_material(horizon=HORIZON, density=DENSITY, K=K,
79 G=to_G(E=peridem.to_E(K, NU), nu=NU), Gc=GC,
80 influence_fn_type=1)
81
82 # A square in the third quadrant is held and an equal square in the
83 # first quadrant is pulled.
84 hold = Geometry("rectangle", [-0.001, -0.001, 0.0, -0.0005, -0.0005, 0.0])
85 pull = Geometry("rectangle", [0.0005, 0.0005, 0.0, 0.001, 0.001, 0.0])
86 d.add_displacement_bc(region=hold, direction=[1, 2],
87 time_fn_type="constant", time_fn_params=[0.0],
88 spatial_fn_type="constant", zero_displacement=True)
89 d.add_displacement_bc(region=pull, direction=[1, 2],
90 time_fn_type="linear", time_fn_params=[PULL_RATE],
91 spatial_fn_type="constant")
92
93 d.set_test("test_peridynamics")
94 return d
95
96
97def main(argv: list[str] | None = None) -> int:
98 import argparse
99
100 p = argparse.ArgumentParser(description=__doc__)
101 p.add_argument("-o", "--output", default=str(HERE / "runs_py"))
102 p.add_argument("--final-time", type=float, default=FINAL_TIME)
103 p.add_argument("--num-steps", type=int, default=NUM_STEPS)
104 p.add_argument("--output-interval", type=int, default=1000)
105 p.add_argument("--mesh-size", type=float, default=None,
106 help="build the mesh with Gmsh in memory instead of "
107 "reading mesh_cir_1_0.msh")
108 p.add_argument("--nthreads", type=int,
109 default=int(os.environ.get("NTHREADS", "4")))
110 p.add_argument("--snapshot", metavar="PNG", default=None)
111 p.add_argument("--write-deck", metavar="PATH", default=None)
112 args = p.parse_args(argv)
113
114 out = Path(args.output).resolve()
115 d = build_deck(str(out) + "/", final_time=args.final_time,
116 num_steps=args.num_steps,
117 output_interval=args.output_interval,
118 mesh_size=args.mesh_size)
119 if args.write_deck:
120 print(d.write(args.write_deck))
121 return 0
122
123 out.mkdir(parents=True, exist_ok=True)
124 peridem.init(n_threads=args.nthreads)
125 sim = d.run(workdir=HERE)
126 if peridem.mpi_rank() != 0:
127 return 0
128
129 u = sim.displacement
130 print(f"done: nodes={sim.n_nodes} step={sim.step_index} t={sim.time:g}")
131 print(f"max |u| = {float(abs(u).max()):.6e}")
132 if args.snapshot:
133 print(peridem.snapshot(peridem.last_vtu(out), args.snapshot,
134 color="|Displacement|",
135 title="circle: |u| at final time"))
136 print(f"VTU under {out}")
137 return 0
138
139
140if __name__ == "__main__":
141 raise SystemExit(main())
Deck build_deck(str|os.PathLike[str] output_path="runs/out/", *str preset="short", float|None final_time=None, int|None num_steps=None, int|None output_interval=None, float omega=OMEGA, str|os.PathLike[str] mesh_dir=MESH_DIR, str|os.PathLike[str] csv=CSV)
Definition problem.py:132