Copyright (C) 2010-2020 Luke Olson
Copyright (C) 2020 Andreas Kloeckner
import numpy as np
import numpy.linalg as la
import firedrake.mesh as fd_mesh
import matplotlib.pyplot as plt
from firedrake import *
firedrake:WARNING OMP_NUM_THREADS is not set or is set to a value greater than 1, we suggest setting OMP_NUM_THREADS=1 to improve performance
import meshpy.triangle as triangle
def round_trip_connect(start, end):
return [(i, i+1) for i in range(start, end)] + [(end, start)]
reentrant_corner = 1
def make_mesh():
if not reentrant_corner:
# tube
points = [
(-1, -1),
(1,-1),
(1, 1),
(-1, 1)]
else:
# reentrant corner
points = [
(-1, 0),
(0,0),
(0, -1),
(1,-1),
(1, 1),
(-1, 1)]
facets = round_trip_connect(0, len(points)-1)
# 1 for "prescribed 0 velocity"
# 2 for "prescribed velocity"
facet_markers = [1] * len(facets)
facet_markers[-1] = 2
facet_markers[-3] = 3
def needs_refinement(vertices, area):
bary = np.sum(np.array(vertices), axis=0)/3
if reentrant_corner:
max_area = 0.0001 + la.norm(bary, np.inf)*0.01
else:
max_area = 0.01
return bool(area > max_area)
info = triangle.MeshInfo()
info.set_points(points)
info.set_facets(facets, facet_markers=facet_markers)
built_mesh = triangle.build(info, refinement_func=needs_refinement)
plex = fd_mesh._from_cell_list(
2, np.array(built_mesh.elements), np.array(built_mesh.points), COMM_WORLD)
import firedrake.cython.dmcommon as dmcommon
v_start, v_end = plex.getDepthStratum(0) # vertices
for facet, fmarker in zip(built_mesh.facets, built_mesh.facet_markers):
vertices = [fvert + v_start for fvert in facet]
join = plex.getJoin(vertices)
plex.setLabelValue(dmcommon.FACE_SETS_LABEL, join[0], fmarker)
return Mesh(plex)
mesh = make_mesh()
triplot(mesh)
plt.gca().set_aspect("equal")
plt.legend()
<matplotlib.legend.Legend at 0x7f3799426e00>
Choose some function spaces:
if 0:
# "P1-P1"
V = VectorFunctionSpace(mesh, "CG", 1)
W = FunctionSpace(mesh, "CG", 1)
elif 1:
# MINI
P1 = FiniteElement("CG", cell=mesh.ufl_cell(), degree=1)
B = FiniteElement("B", cell=mesh.ufl_cell(), degree=3)
mini = P1 + B
V = VectorFunctionSpace(mesh, mini)
W = FunctionSpace(mesh, 'CG', 1)
else:
# Taylor-Hood
V = VectorFunctionSpace(mesh, "CG", 2)
W = FunctionSpace(mesh, "CG", 1)
Z = V * W
Set up the weak form: $$ \begin{align*} a (\b{u}, \b{v}) = \int_{\Omega} J_{\b{u}} : J_{\b{v}}, \\ b (\b{v}, q) = \int_{\Omega} \nabla \cdot \b{v}q, \end{align*} $$ where $A : B = \operatorname{tr} (AB^T)$. Find $(\b{u}, p) \in X \times M$ so that $$ \begin{eqnarray*} a (\b{u}, \b{v}) + b (\b{v}, p) & = & \ip{\b{f}}{\b{v}}_{L^2} \quad (\b{v} \in X),\\ b (\b{u}, q) & = & 0 \quad (q \in M), \end{eqnarray*} $$
u, p = TrialFunctions(Z)
v, q = TestFunctions(Z)
a = (inner(grad(u), grad(v)) - p * div(v) + div(u) * q)*dx
L = inner(Constant((0, 0)), v) * dx
Pick boundary conditions:
bcs = [
DirichletBC(Z.sub(0), Constant((1, 0)), (2,)),
DirichletBC(Z.sub(0), Constant((0.5 if reentrant_corner else 1, 0)), (3,)),
DirichletBC(Z.sub(0), Constant((0, 0)), (1,))
]
Let the linear solver know about the nullspace:
nullspace = MixedVectorSpaceBasis(
Z, [Z.sub(0), VectorSpaceBasis(constant=True)])
Solve:
upsol = Function(Z)
usol, psol = upsol.split()
solve(a == L, upsol, bcs=bcs,
nullspace=nullspace,
solver_parameters={'pc_type': 'fieldsplit',
'ksp_rtol': 1e-15,
'pc_fieldsplit_type': 'schur',
'fieldsplit_schur_fact_type': 'diag',
'fieldsplit_0_pc_type': 'redundant',
'fieldsplit_0_redundant_pc_type': 'lu',
'fieldsplit_1_pc_type': 'none',
'ksp_monitor_true_residual': None,
'mat_type': 'aij'})
Residual norms for firedrake_3_ solve. 0 KSP preconditioned resid norm 1.346059418025e+02 true resid norm 5.120663688914e+00 ||r(i)||/||b|| 1.000000000000e+00 1 KSP preconditioned resid norm 6.478207717897e-02 true resid norm 5.713573615823e-03 ||r(i)||/||b|| 1.115787710916e-03 2 KSP preconditioned resid norm 7.423464474138e-07 true resid norm 5.711395787996e-03 ||r(i)||/||b|| 1.115362409049e-03 3 KSP preconditioned resid norm 8.900113115251e-12 true resid norm 5.711395787993e-03 ||r(i)||/||b|| 1.115362409048e-03 4 KSP preconditioned resid norm 2.712346736920e-14 true resid norm 5.711395787993e-03 ||r(i)||/||b|| 1.115362409048e-03
Plot the velocity:
plt.figure(figsize=(8, 8))
ax = plt.gca()
ax.set_aspect("equal")
triplot(mesh, axes=ax, interior_kw=dict(alpha=0.05))
quiver(usol, axes=ax)
<matplotlib.quiver.Quiver at 0x7f378aff2650>
Plot the pressure and the divergence of $u$:
div_usol = project(div(usol), W)
plt.figure(figsize=(12,6))
plt.subplot(121)
ax = plt.gca()
l = tricontourf(psol, axes=ax)
triplot(mesh, axes=ax, interior_kw=dict(alpha=0.05))
plt.colorbar(l)
plt.title("Pressure")
plt.subplot(122)
ax = plt.gca()
l = tricontourf(div_usol, axes=ax)
triplot(mesh, axes=ax, interior_kw=dict(alpha=0.05))
plt.colorbar(l)
plt.title(r"$\nabla\cdot u$")
Text(0.5, 1.0, '$\\nabla\\cdot u$')