Note
Go to the end to download the full example code.
TET10 — prescribed uniform strain on a 10-node tet#
Single TET10 (10-node quadratic tet) driven by a fully-prescribed
displacement field that matches a uniform strain state. Under
all-Dirichlet BCs the solve has a unique solution equal to the
prescribed field, so the recovered strain tensor matches the analytical
one to machine precision. Useful as a self-contained smoke test that
TET10 + femorph_solver.Model.eel() are in lockstep.
from __future__ import annotations
import numpy as np
import pyvista as pv
from vtkmodules.util.vtkConstants import VTK_QUADRATIC_TETRA
import femorph_solver
from femorph_solver import ELEMENTS
Reference 10-node unit tet#
Corners at (0,0,0), (1,0,0), (0,1,0), (0,0,1); mid-edge nodes in VTK_QUADRATIC_TETRA order halfway along each edge.
E = 2.1e11 # Pa
NU = 0.30
EPS_XX = 5.0e-4
EPS_YY = -NU * EPS_XX # Poisson contraction
I = np.array([0.0, 0.0, 0.0])
J = np.array([1.0, 0.0, 0.0])
K = np.array([0.0, 1.0, 0.0])
L = np.array([0.0, 0.0, 1.0])
coords = np.array(
[
I,
J,
K,
L,
0.5 * (I + J), # 5
0.5 * (J + K), # 6
0.5 * (K + I), # 7
0.5 * (I + L), # 8
0.5 * (J + L), # 9
0.5 * (K + L), # 10
],
dtype=np.float64,
)
Build the model + prescribed displacement#
Apply u = (εxx·x, εyy·y, εyy·z) at every node.
cells = np.concatenate([[10], np.arange(10, dtype=np.int64)])
cell_types = np.array([VTK_QUADRATIC_TETRA], dtype=np.uint8)
grid = pv.UnstructuredGrid(cells, cell_types, coords)
m = femorph_solver.Model.from_grid(grid)
m.assign(ELEMENTS.TET10, material={"EX": E, "PRXY": NU})
for i, (x, y, z) in enumerate(coords, start=1):
m.fix(nodes=[i], dof="UX", value=EPS_XX * x)
m.fix(nodes=[i], dof="UY", value=EPS_YY * y)
m.fix(nodes=[i], dof="UZ", value=EPS_YY * z)
Solve + verify strain recovery#
res = m.solve()
eps = m.eel(res.displacement)
print(f"εxx prescribed = {EPS_XX:.3e} | recovered (mean) = {eps[:, 0].mean():.3e}")
print(f"εyy prescribed = {EPS_YY:.3e} | recovered (mean) = {eps[:, 1].mean():.3e}")
np.testing.assert_allclose(eps[:, 0], EPS_XX, rtol=1e-10)
np.testing.assert_allclose(eps[:, 1], EPS_YY, rtol=1e-10)
np.testing.assert_allclose(eps[:, 2], EPS_YY, rtol=1e-10)
εxx prescribed = 5.000e-04 | recovered (mean) = 5.000e-04
εyy prescribed = -1.500e-04 | recovered (mean) = -1.500e-04
Render the deformed tet coloured by εxx#
grid = femorph_solver.io.static_result_to_grid(m, res)
node_nums = m.node_numbers
node_to_idx = {int(nn): i for i, nn in enumerate(node_nums)}
grid.point_data["eps_xx"] = np.array(
[eps[node_to_idx[int(nn)], 0] for nn in grid.point_data["ansys_node_num"]]
)
plotter = pv.Plotter(off_screen=True)
plotter.add_mesh(grid, style="wireframe", color="gray", line_width=1, opacity=0.4)
plotter.add_mesh(
grid.warp_by_vector("displacement", factor=200.0),
scalars="eps_xx",
show_edges=True,
cmap="viridis",
scalar_bar_args={"title": "εxx"},
)
plotter.add_axes()
plotter.show()

Total running time of the script: (0 minutes 0.232 seconds)