Skip to content

Commit e3bf6db

Browse files
committed
Add simple version of file input and parser
1 parent 54ab811 commit e3bf6db

6 files changed

Lines changed: 223 additions & 5 deletions

File tree

diffmpm/io.py

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import json
2+
import tomllib as tl
3+
4+
import jax.numpy as jnp
5+
import numpy as np
6+
7+
from diffmpm import element as mpel
8+
from diffmpm import material as mpmat
9+
from diffmpm import mesh as mpmesh
10+
from diffmpm.node import Nodes
11+
from diffmpm.particle import Particles
12+
13+
14+
class Config:
15+
def __init__(self, filepath):
16+
self._filepath = filepath
17+
self.config = {}
18+
self.parse()
19+
20+
def parse(self):
21+
with open(self._filepath, "rb") as f:
22+
self._fileconfig = tl.load(f)
23+
24+
self._parse_meta(self._fileconfig)
25+
self._parse_output(self._fileconfig)
26+
self._parse_materials(self._fileconfig)
27+
self._parse_particles(self._fileconfig)
28+
mesh = self._parse_mesh(self._fileconfig)
29+
return mesh
30+
31+
def _parse_meta(self, config):
32+
self.config["meta"] = config["meta"]
33+
34+
def _parse_output(self, config):
35+
self.config["output"] = config["output"]
36+
37+
def _parse_materials(self, config):
38+
materials = []
39+
for mat_config in config["materials"]:
40+
mat_type = mat_config.pop("type")
41+
mat_cls = getattr(mpmat, mat_type)
42+
mat = mat_cls(mat_config)
43+
materials.append(mat)
44+
self.config["materials"] = materials
45+
46+
def _parse_particles(self, config):
47+
particle_sets = []
48+
for pset_config in config["particles"]:
49+
pmat = self.config["materials"][pset_config["material_id"]]
50+
with open(pset_config["file"], "r") as f:
51+
ploc = jnp.asarray(json.load(f))
52+
peids = jnp.zeros(ploc.shape[0], dtype=jnp.int32)
53+
pset = Particles(ploc, pmat, peids)
54+
pset.velocity = pset.velocity.at[:].set(
55+
pset_config["init_velocity"]
56+
)
57+
particle_sets.append(pset)
58+
self.config["particles"] = particle_sets
59+
60+
def _parse_mesh(self, config):
61+
element_cls = getattr(mpel, config["mesh"]["element"])
62+
mesh_cls = getattr(mpmesh, f"Mesh{config['meta']['dimension']}D")
63+
if config["mesh"]["type"] == "file":
64+
nodes_loc = jnp.asarray(np.loadtxt(config["mesh"]["file"]))
65+
nodes = Nodes(len(nodes_loc), nodes_loc)
66+
elements = element_cls(nelements, el_len, boundary_nodes)
67+
elif config["mesh"]["type"] == "generator":
68+
elements = element_cls(
69+
config["mesh"]["nelements"],
70+
config["mesh"]["element_length"],
71+
jnp.asarray(config["mesh"]["boundary_nodes"]),
72+
)
73+
self.config["elements"] = elements
74+
mesh = mesh_cls(self.config)
75+
return mesh

diffmpm/particle.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,11 @@ def __init__(
5151
self.loc = loc
5252

5353
if initialized is None:
54-
self.mass = jnp.zeros((self.loc.shape[0], 1, 1))
55-
self.density = jnp.zeros_like(self.mass)
56-
self.volume = jnp.zeros_like(self.mass)
54+
self.mass = jnp.ones((self.loc.shape[0], 1, 1))
55+
self.density = (
56+
jnp.ones_like(self.mass) * self.material.properties["density"]
57+
)
58+
self.volume = jnp.divide(self.mass, self.density)
5759
self.velocity = jnp.zeros_like(self.loc)
5860
self.acceleration = jnp.zeros_like(self.loc)
5961
self.momentum = jnp.zeros_like(self.loc)

diffmpm/solver.py

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,28 @@
11
import jax.numpy as jnp
22
from jax import lax
3-
from diffmpm.scheme import _schemes, USF, USL
4-
from tqdm import tqdm
53
from jax.tree_util import register_pytree_node_class
4+
from tqdm import tqdm
5+
6+
from diffmpm.io import Config
7+
from diffmpm.scheme import USF, USL, _schemes
8+
9+
10+
# TODO: Move to __init__.py mostly.
11+
class MPM:
12+
def __init__(self, filepath):
13+
self._config = Config(filepath)
14+
mesh = self._config.parse()
15+
if self._config.config["meta"]["type"] == "MPMExplicit":
16+
self.solver = MPMExplicit(mesh, self._config.config["meta"]["dt"])
17+
else:
18+
raise ValueError("Wrong type of solver specified.")
19+
20+
def solve(self):
21+
res = self.solver.solve_jit(
22+
self._config.config["meta"]["nsteps"],
23+
self._config.config["meta"]["gravity"],
24+
)
25+
return res
626

727

828
@register_pytree_node_class

examples/input_1d.toml

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# The `meta` group contains top level attributes that govern the
2+
# behaviour of the MPM Solver.
3+
#
4+
# Attributes:
5+
# title: The title of the experiment. This is just for the user's
6+
# reference.
7+
# type: The type of simulation to be used. Allowed values are
8+
# {"MPMExplicit"}
9+
# scheme: The MPM Scheme used for simulation. Allowed values are
10+
# {"usl", "usf"}
11+
# dt: Timestep used in the simulation.
12+
# nsteps: Number of steps to run the simulation for.
13+
[meta]
14+
title = "Example TOML Input for 1D MPM"
15+
type = "MPMExplicit"
16+
dimension = 1
17+
scheme = "usl"
18+
dt = 0.01
19+
nsteps = 1000
20+
gravity = 0
21+
22+
[output]
23+
type = "hdf5"
24+
file = "results/example_1d_out.hdf5"
25+
step_frequency = 5
26+
27+
[mesh]
28+
# type = "file"
29+
# file = "mesh-1d.txt"
30+
# boundary_nodes = "boundary-1d.txt"
31+
# particle_element_ids = "particles-elements.txt"
32+
type = "generator"
33+
nelements = 1
34+
element_length = 1.0
35+
boundary_nodes = [0]
36+
particle_element_ids = [0]
37+
element = "Linear1D"
38+
39+
[[materials]]
40+
id = 0
41+
density = 1
42+
poisson_ratio = 1
43+
E = 39.5
44+
type = "SimpleMaterial"
45+
46+
[[particles]]
47+
file = "examples/particles-1d.json"
48+
material_id = 0
49+
init_velocity = 0.1

examples/particles-1d.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
[[[0.5]]]

examples/simple_1d_file.py

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import sys
2+
import jax.numpy as jnp
3+
import matplotlib.pyplot as plt
4+
from diffmpm.solver import MPM
5+
6+
mpm = MPM(sys.argv[1])
7+
result = mpm.solve()
8+
9+
10+
def analytical_vibration(E, rho, v0, x_loc, L, dt, nsteps):
11+
tt, vt, xt = [], [], []
12+
t = 0
13+
for _ in range(nsteps):
14+
omega = 1.0 / L * jnp.sqrt(E / rho)
15+
v = v0 * jnp.cos(omega * t)
16+
x = x_loc * jnp.exp(v0 / (L * omega) * jnp.sin(omega * t))
17+
vt.append(v)
18+
xt.append(x)
19+
tt.append(t)
20+
t += dt
21+
return tt, vt, xt
22+
23+
24+
E = mpm._config._fileconfig["materials"][0]["E"]
25+
nsteps = mpm._config._fileconfig["meta"]["nsteps"]
26+
dt = mpm._config._fileconfig["meta"]["dt"]
27+
28+
# analytical solution at the end of the bar
29+
ta, va, xa = analytical_vibration(
30+
E=E, rho=1, v0=0.1, x_loc=0.5, nsteps=nsteps, dt=dt, L=1.0
31+
)
32+
fig, ax = plt.subplots(1, 2, figsize=(16, 6))
33+
ax[0].plot(ta, va, "r", label="analytical")
34+
ax[0].plot(ta, result["velocity"].squeeze(), "ob", markersize=2, label="mpm")
35+
ax[0].legend()
36+
ax[0].set_title("Velocity")
37+
38+
ax[1].plot(ta, xa, "r", label="analytical")
39+
ax[1].plot(ta, result["position"].squeeze(), "ob", markersize=2, label="mpm")
40+
ax[1].legend()
41+
ax[1].set_title("Position")
42+
43+
fig, ax = plt.subplots()
44+
ax.plot(
45+
ta,
46+
result["strain_energy"].squeeze(),
47+
"r",
48+
linewidth=1,
49+
alpha=0.7,
50+
label="Strain energy",
51+
)
52+
ax.plot(
53+
ta,
54+
result["kinetic_energy"].squeeze(),
55+
"b",
56+
linewidth=1,
57+
alpha=0.7,
58+
label="Kinetic energy",
59+
)
60+
ax.plot(
61+
ta,
62+
result["total_energy"].squeeze(),
63+
"k",
64+
linewidth=1,
65+
alpha=0.7,
66+
label="Total energy",
67+
)
68+
ax.legend()
69+
ax.set_title("Energies")
70+
71+
plt.show()

0 commit comments

Comments
 (0)