@dokken and @bleyerj, here’s my code, simplified as much as possible (you’ll need to import the mesh). Thank you for the help!
The boundary conditions are:
You can find the mesh at this link
(The link also includes a Python script to facilitate the process and the results)
code:
import dolfinx
import json
from mpi4py import MPI
import numpy as np
from petsc4py.PETSc import ScalarType
import shutil
import time
import ufl
# Material
def eps(v):
return ufl.sym(ufl.grad(v))
E = 200000.0
nu = 0.3
model = "plane_stress"
shear_modulus = E / 2 / (1 + nu)
lame_modulus = E * nu / (1 + nu) / (1 - 2 * nu)
def get_strain_tensor(displacement):
strain = 0.5 * (ufl.nabla_grad(displacement) +
ufl.nabla_grad(displacement).T)
strain_zz = -lame_modulus / (lame_modulus + 2 * shear_modulus) * (
strain[0, 0] + strain[1, 1])
strain = ufl.as_tensor([[strain[0, 0], strain[0, 1], 0],
[strain[1, 0], strain[1, 1], 0], [0, 0, strain_zz]])
return strain
def get_stress_tensor(displacement):
lame_modulus_2 = 2 * lame_modulus * shear_modulus / (lame_modulus +
2 * shear_modulus)
dim = len(displacement)
strain = 0.5 * (ufl.nabla_grad(displacement) +
ufl.nabla_grad(displacement).T)
stress = lame_modulus_2 * ufl.nabla_div(displacement) * ufl.Identity(
dim) + 2 * shear_modulus * strain
stress = ufl.as_tensor([[stress[0, 0], stress[0, 1], 0],
[stress[1, 0], stress[1, 1], 0], [0, 0, 0]])
return stress
def get_von_mises_stress(displacement):
d = len(displacement) + 1
deviatoric_stress = get_stress_tensor(displacement) - (1. / 3) * ufl.tr(
get_stress_tensor(displacement)) * ufl.Identity(d)
von_mises_stress = ufl.sqrt(3. / 2 *
ufl.inner(deviatoric_stress, deviatoric_stress))
return von_mises_stress
# Mesh
mesh_path = "mesh.msh"
dolfinx_mesh, _, _ = dolfinx.io.gmshio.read_from_msh(mesh_path,
MPI.COMM_WORLD,
gdim=2)
# Boundary
def bottom(x):
return np.isclose(x[1], min(x[1]))
def top(x):
return np.isclose(x[1], max(x[1]))
def left(x):
return np.isclose(x[0], min(x[0]))
def right(x):
return np.isclose(x[0], max(x[0]))
# Solve
v_func_space = dolfinx.fem.VectorFunctionSpace(dolfinx_mesh, ("S", 1))
u_trial_func = ufl.TrialFunction(v_func_space)
v_test_func = ufl.TestFunction(v_func_space)
# Bilinear form
stress_u = get_stress_tensor(u_trial_func)
strain_v = get_strain_tensor(v_test_func)
bilinear_form_a = ufl.inner(stress_u, strain_v) * ufl.dx
# Lienar form
facet_indices = []
facet_markers = []
facet_dim = dolfinx_mesh.topology.dim - 1
facet_indices_boundary_left = dolfinx.mesh.locate_entities_boundary(
dolfinx_mesh, facet_dim, left)
facet_indices.append(facet_indices_boundary_left)
facet_markers.append(np.full_like(facet_indices_boundary_left, 0))
facet_indices_boundary_top = dolfinx.mesh.locate_entities_boundary(
dolfinx_mesh, facet_dim, top)
facet_indices.append(facet_indices_boundary_top)
facet_markers.append(np.full_like(facet_indices_boundary_top, 1))
facet_indices_boundary_right = dolfinx.mesh.locate_entities_boundary(
dolfinx_mesh, facet_dim, right)
facet_indices.append(facet_indices_boundary_right)
facet_markers.append(np.full_like(facet_indices_boundary_right, 2))
facet_indices = np.hstack(facet_indices).astype(np.int32)
facet_markers = np.hstack(facet_markers).astype(np.int32)
sorted_facets = np.argsort(facet_indices)
facet_tag = dolfinx.mesh.meshtags(dolfinx_mesh, facet_dim,
facet_indices[sorted_facets],
facet_markers[sorted_facets])
ds = ufl.Measure("ds", domain=dolfinx_mesh, subdomain_data=facet_tag)
tension_left = dolfinx.fem.Constant(dolfinx_mesh, ScalarType((1, 0)))
tension_top = dolfinx.fem.Constant(dolfinx_mesh, ScalarType((0, 1)))
tension_right = dolfinx.fem.Constant(dolfinx_mesh, ScalarType((-1, 0)))
tensions = [tension_left, tension_top, tension_right]
linear_form_l = 0
for i, tension in enumerate(tensions):
linear_form_l += ufl.dot(tension, v_test_func) * ds(i)
# BCs
sub_0, _ = v_func_space.sub(0).collapse()
edges = dolfinx.mesh.locate_entities_boundary(dolfinx_mesh,
dolfinx_mesh.topology.dim - 1,
bottom)
boundary_dofs = dolfinx.fem.locate_dofs_topological(
(v_func_space.sub(0), sub_0), dolfinx_mesh.topology.dim - 1, edges)
disp_func = dolfinx.fem.Function(sub_0)
x_disp_const = dolfinx.fem.Constant(dolfinx_mesh, ScalarType(0.0))
x_disp_expr = dolfinx.fem.Expression(x_disp_const,
sub_0.element.interpolation_points())
disp_func.interpolate(x_disp_expr)
bc_x = dolfinx.fem.dirichletbc(disp_func, boundary_dofs, v_func_space.sub(0))
sub_1, _ = v_func_space.sub(1).collapse()
edges = dolfinx.mesh.locate_entities_boundary(dolfinx_mesh,
dolfinx_mesh.topology.dim - 1,
bottom)
boundary_dofs = dolfinx.fem.locate_dofs_topological(
(v_func_space.sub(1), sub_1), dolfinx_mesh.topology.dim - 1, edges)
disp_func = dolfinx.fem.Function(sub_1)
y_disp_const = dolfinx.fem.Constant(dolfinx_mesh, ScalarType(0.0))
y_disp_expr = dolfinx.fem.Expression(y_disp_const,
sub_1.element.interpolation_points())
disp_func.interpolate(y_disp_expr)
bc_y = dolfinx.fem.dirichletbc(disp_func, boundary_dofs, v_func_space.sub(1))
bc = [bc_x, bc_y]
linear_variational_problem = dolfinx.fem.petsc.LinearProblem(
a=bilinear_form_a,
L=linear_form_l,
bcs=bc,
petsc_options={
"ksp_type": "preonly",
"pc_type": "lu"
})
displacement_func = linear_variational_problem.solve()
# von mises
v_func_space = dolfinx.fem.FunctionSpace(dolfinx_mesh, ("S", 1))
tensor = get_von_mises_stress(displacement_func)
field_expr = dolfinx.fem.Expression(tensor,
v_func_space.element.interpolation_points())
von_mises_func = dolfinx.fem.Function(v_func_space)
von_mises_func.interpolate(field_expr)
# Save to xdmf
fields = {displacement_func, von_mises_func}
with dolfinx.io.XDMFFile(dolfinx_mesh.comm, "test.xdmf", "w") as xdmf:
xdmf.write_mesh(dolfinx_mesh)
displacement_func.name = "displacement"
xdmf.write_function(displacement_func)
von_mises_func.name="von_mises"
xdmf.write_function(von_mises_func)