Solve the following linear system of PDEs
using FE discretization with data
where \(B_R(\mathbf{z})\) is a circle of radius \(R\) and center \(\mathbf{z}\)
Task 1. Write the variational formulation of the problem and discretize the equation by mixed finite element method.
Task 2. Build mesh, prepare facet function marking \(\Gamma_\mathrm{N}\) and \(\Gamma_\mathrm{D}\) and plot it to check its correctness.
Hint. Use the mshr component of fenics - see mshr documentation
import mshr # Define domain center = Point(0.2, 0.2) radius = 0.05 L = 2.2 W = 0.41 geometry = mshr.Rectangle(Point(0.0,0.0), Point(L, W)) \ -mshr.Circle(center, radius, 10) # Build mesh mesh = mshr.generate_mesh(geometry, 50)Hint. Try yet another way to mark the boundaries by direct access to the mesh entities by facets(mehs), vertices(mesh), cells(mesh)
# Construct facet markers bndry = FacetFunction("size_t", mesh) for f in facets(mesh): mp = f.midpoint() if near(mp[0], 0.0): bndry[f] = 1 # inflow elif near(mp[0], L): bndry[f] = 2 # outflow elif near(mp[1], 0.0) or near(mp[1], W): bndry[f] = 3 # walls elif mp.distance(center) <= radius: bndry[f] = 5 # cylinder plot(boundary_parts, interactive=True)Task 3. Construct the mixed finite element space and the bilinear and linear forms together with the DirichletBC object.
Hint. Use for example the stable Taylor-Hood finite elements.
# Build function spaces (Taylor-Hood) V = VectorFunctionSpace(mesh, "Lagrange", 2) P = FunctionSpace(mesh, "Lagrange", 1) W = MixedFunctionSpace([V, P])Hint. To define Dirichlet BC on subspace use the W.sub method.
noslip = Constant((0, 0)) bc_walls = DirichletBC(W.sub(0), noslip, bndry, 3)Hint. To build the forms use the split method or function to access the components of the mixed space.
# Define unknown and test function(s) (v_, p_) = TestFunctions(W) (v , p) = TrialFunctions(W)Then you can define the forms for example as:
def a(u,v): return inner(grad(u), grad(v))*dx def b(p,v): return p*div(v)*dx def L(v): return inner(f, v)*dx F = a(v,v_) + b(p,v_) + b(p_,v) - L(v_)And solve by:
w = Function(W) solve(lhs(F)==rhs(F), w, bcs) (v,p)=w.split(w)Task 4. Now modify the problem to the Navier-Stokes equations and compute the DFG-flow around cylinder benchmark
Hint. You can use generic solve function or NonlinearVariationalProblem and NonlinearVariationalSolver classes.
(_v, _p) = TestFunctions(W) w = Function(W) (v, p) = split(w) I = Identity(v.geometric_dimension()) # Identity tensor D = 0.5*(grad(v)+grad(v).T) # or D=sym(grad(v)) T = -p*I + 2*nu*D # Define variational forms F = inner(T, grad(_v))*dx + _p*div(v)*dx + inner(grad(v)*v,_v)*dxHint. Use Assemble function to evaluate the lift and drag functionals.
force = T*n D = (force[0]/0.002)*ds(5) L = (force[1]/0.002)*ds(5) drag = assemble(D) lift = assemble(L) print "drag= %e lift= %e" % (drag , lift)Task 5. Now prepare the time dependent solution of the Navier-Stokes equations. Combine it with the heat equation example.
from dolfin import *
import mshr
# Define domain
center = Point(0.2, 0.2)
radius = 0.05
L = 2.2
W = 0.41
geometry = mshr.Rectangle(Point(0.0, 0.0), Point(L, W)) \
- mshr.Circle(center, radius, 10)
# Build mesh
mesh = mshr.generate_mesh(geometry, 50)
# Construct facet markers
bndry = FacetFunction("size_t", mesh)
for f in facets(mesh):
mp = f.midpoint()
if near(mp[0], 0.0): # inflow
bndry[f] = 1
elif near(mp[0], L): # outflow
bndry[f] = 2
elif near(mp[1], 0.0) or near(mp[1], W): # walls
bndry[f] = 3
elif mp.distance(center) <= radius: # cylinder
bndry[f] = 5
# Build function spaces (Taylor-Hood)
V = VectorFunctionSpace(mesh, "CG", 2)
P = FunctionSpace(mesh, "CG", 1)
W = MixedFunctionSpace([V, P])
# No-slip boundary condition for velocity on walls and cylinder - boundary id 3
noslip = Constant((0, 0))
bc_walls = DirichletBC(W.sub(0), noslip, bndry, 3)
bc_cylinder = DirichletBC(W.sub(0), noslip, bndry, 5)
# Inflow boundary condition for velocity - boundary id 1
v_in = Expression(("0.3 * 4.0 * x[1] * (0.41 - x[1]) / ( 0.41 * 0.41 )", "0.0"))
bc_in = DirichletBC(W.sub(0), v_in, bndry, 1)
# Collect boundary conditions
bcs = [bc_cylinder, bc_walls, bc_in]
# Facet normal, identity tensor and boundary measure
n = FacetNormal(mesh)
I = Identity(mesh.geometry().dim())
ds = Measure("ds", subdomain_data=bndry)
nu = Constant(0.001)
def stokes():
# Define variational forms for Stokes
def a(u,v):
return inner(nu*grad(u), grad(v))*dx
def b(p,v):
return p*div(v)*dx
def L(v):
return inner(Constant((0.0,0.0)), v)*dx
# Solve the problem
u, p = TrialFunctions(W)
v, q = TestFunctions(W)
w = Function(W)
solve(a(u, v) - b(p, v) - b(q, u) == L(v), w, bcs)
# Report in,out fluxes
u = w.sub(0, deepcopy=False)
info("Inflow flux: %e" % assemble(inner(u, n)*ds(1)))
info("Outflow flux: %e" % assemble(inner(u, n)*ds(2)))
return w
def navier_stokes():
v, q = TestFunctions(W)
w = Function(W)
u, p = split(w)
# Define variational forms
T = -p*I + 2.0*nu*sym(grad(u))
F = inner(T, grad(v))*dx - q*div(u)*dx + inner(grad(u)*u, v)*dx
solve(F == 0, w, bcs)
# Report drag and lift
force = dot(T, n)
D = (force[0]/0.002)*ds(5)
L = (force[1]/0.002)*ds(5)
drag = assemble(D)
lift = assemble(L)
info("drag= %e lift= %e" % (drag , lift))
# Report pressure difference
a_1 = Point(0.15, 0.2)
a_2 = Point(0.25, 0.2)
p = w.sub(1, deepcopy=False)
p_diff = p(a_1) - p(a_2)
info("p_diff = %e" % p_diff)
return w
if __name__ == "__main__":
for problem in [stokes, navier_stokes]:
begin("Running '%s'" % problem.__name__)
# Call solver
w = problem()
# Extract solutions
u, p = w.split()
# Save solution
ufile = File("results_%s/u.xdmf" % problem.__name__)
pfile = File("results_%s/p.xdmf" % problem.__name__)
u.rename("u", "velocity")
p.rename("p", "pressure")
ufile << u
pfile << p
plot(u, title='velocity %s' % problem.__name__)
plot(p, title='pressure %s' % problem.__name__)
end()
interactive()