Here is my full code simplified as much as I could:
import numpy as np # type: ignore
from ufl import sym, grad, Identity, tr, inner, Measure, TestFunction, TrialFunction # type: ignore
from mpi4py import MPI # type: ignore
from dolfinx import fem, io, mesh
from dolfinx.fem.petsc import assemble_vector, assemble_matrix, create_vector, apply_lifting, set_bc
from petsc4py import PETSc # type: ignore
import gmsh
# Initialize Gmsh
gmsh.initialize()
gmsh.option.setNumber("General.Terminal", 0)
gdim = 3
# Create a horizontal cylinder with circular faces on the yz-plane
gmsh.model.add("cylinder")
cylinder_tag = gmsh.model.occ.addCylinder(0, 0, 0, 10.0, 0, 0, 1.0)
gmsh.model.occ.synchronize()
# Retrieve boundary surfaces (left and right circular faces, and the lateral surface)
surfaces = gmsh.model.getBoundary([(3, cylinder_tag)], oriented=False, recursive=False)
# Right face
right_face_tag = surfaces[0][1]
gmsh.model.addPhysicalGroup(gdim - 1, [right_face_tag], 1)
gmsh.model.setPhysicalName(gdim - 1, 1, "Right")
# Left face
left_face_tag = surfaces[1][1]
gmsh.model.addPhysicalGroup(gdim - 1, [left_face_tag], 2)
gmsh.model.setPhysicalName(gdim - 1, 2, "Left")
# Lateral surface
lateral_surface_tags = [surf[1] for surf in surfaces[2:]]
gmsh.model.addPhysicalGroup(gdim - 1, lateral_surface_tags, 3)
gmsh.model.setPhysicalName(gdim - 1, 3, "Lateral")
# Generate the 3D mesh
gmsh.model.mesh.generate(gdim)
domain, _, facets = io.gmshio.model_to_mesh(gmsh.model, MPI.COMM_WORLD, 0, gdim=gdim)
gmsh.finalize()
shape = (gdim,)
# Define the function space
V = fem.functionspace(domain, ("P", 2, shape))
u = TrialFunction(V)
v = TestFunction(V)
u_h = fem.Function(V, name="Displacement")
# Lamé parameters (for a given material)
E = 10.0 # Young's modulus
nu = 0.3 # Poisson's ratio
lambda_ = (E * nu) / ((1 + nu) * (1 - 2 * nu))
mu = E / (2 * (1 + nu))
def epsilon(u):
return sym(grad(u))
def sigma(u):
return lambda_ * tr(epsilon(u)) * Identity(len(u)) + 2 * mu * epsilon(u)
friction_coefficient = 0.9
f = fem.Constant(domain, np.array([10.0, 0, 0]))
dx = Measure("dx", domain=domain) # Volume measure
ds = Measure("ds", domain=domain, subdomain_data=facets) # Surface measure for boundaries
bcs = []
a = (
inner(sigma(u), epsilon(v)) * dx +
friction_coefficient * inner(u, v) * ds(3)
)
L = inner(f, v) * ds(1)
bilinear_form = fem.form(a)
linear_form = fem.form(L)
A = assemble_matrix(bilinear_form, bcs=bcs)
A.assemble()
b = create_vector(linear_form)
solver = PETSc.KSP().create(domain.comm)
solver.setOperators(A)
solver.setType(PETSc.KSP.Type.PREONLY)
solver.getPC().setType(PETSc.PC.Type.LU)
# define the problem
problem = fem.petsc.LinearProblem(
a, L, u=u_h, bcs=bcs,
petsc_options={"ksp_type": "preonly", "pc_type": "lu"}
)
vtk = io.VTKFile(domain.comm, "results/dynamic/cylinder.pvd", "w")
with io.XDMFFile(domain.comm, "facets.xdmf", "w") as xdmf:
xdmf.write_mesh(domain)
xdmf.write_meshtags(facets, domain.geometry)
# Update the right hand side reusing the initial vector
with b.localForm() as loc_b:
loc_b.set(0)
assemble_vector(b, linear_form)
# Apply Dirichlet boundary condition to the vector
apply_lifting(b, [bilinear_form], [bcs])
b.ghostUpdate(addv=PETSc.InsertMode.ADD_VALUES, mode=PETSc.ScatterMode.REVERSE)
set_bc(b, bcs)
# Solve linear problem
solver.solve(b, u_h.x.petsc_vec)
u_h.x.scatter_forward()
# Write solution to file
vtk.write_function(u_h, 0)
vtk.close()
And this the paraview screenshot