Dear all,
I am solving the convection-difussion equation on a rectangular domain.
The boundary conditions are:
- Dirichlet BC at Inlet (parabolic velocity profile).
- Homogeneous Dirichlet BC at Walls.
- Zero stress BC at outlet.
The working code is shown below:
import numpy as np
from mpi4py import MPI
import dolfinx
from dolfinx.fem import (Constant, Function, FunctionSpace, apply_lifting, assemble_matrix,
assemble_scalar, assemble_vector, locate_dofs_topological, set_bc,
dirichletbc, form)
from dolfinx import fem
from ufl import (inner, div, dx, nabla_grad, sym, Identity, as_tensor, Dn, grad, ds, rhs, lhs, Measure, transpose, as_tensor,
dot, FacetNormal, indices)
from dolfinx.io import gmshio
from petsc4py import PETSc
import ufl
import gmsh
rank = MPI.COMM_WORLD.rank
mesh_comm = MPI.COMM_WORLD
model_rank = 0
# Problem parameters
L = 1
H = L/10
nu = 0.01
C = 1
Re = C*L/nu
# Numerical parameters
T = 2
dt = 1e-3
num_steps = int(T/dt)
# Mesh generation
gdim = 2
gmsh.initialize()
if rank == 0:
rectangle = gmsh.model.occ.addRectangle(0,0,0, L, H, tag=1)
gmsh.model.occ.synchronize()
fluid_marker = 1
if rank == 0:
volumes = gmsh.model.getEntities(dim=gdim)
assert(len(volumes) == 1)
gmsh.model.addPhysicalGroup(volumes[0][0], [volumes[0][1]], fluid_marker)
gmsh.model.setPhysicalName(volumes[0][0], fluid_marker, "Fluid")
inlet_marker, outlet_marker, wall_marker = 2, 3, 4
inflow, outflow, walls = [], [], []
if rank == 0:
boundaries = gmsh.model.getBoundary(volumes, oriented=False)
for boundary in boundaries:
center_of_mass = gmsh.model.occ.getCenterOfMass(boundary[0], boundary[1])
if np.allclose(center_of_mass, [0, H/2, 0]):
inflow.append(boundary[1])
elif np.allclose(center_of_mass, [L, H/2, 0]):
outflow.append(boundary[1])
else:
walls.append(boundary[1])
gmsh.model.addPhysicalGroup(1, walls, wall_marker)
gmsh.model.setPhysicalName(1, wall_marker, "Walls")
gmsh.model.addPhysicalGroup(1, inflow, inlet_marker)
gmsh.model.setPhysicalName(1, inlet_marker, "Inlet")
gmsh.model.addPhysicalGroup(1, outflow, outlet_marker)
gmsh.model.setPhysicalName(1, outlet_marker, "Outlet")
# Refinement
lc = 1
if mesh_comm.rank == model_rank:
gmsh.model.mesh.field.add("Box", 1)
gmsh.model.mesh.field.setNumber(1, "VIn", lc/50)
gmsh.model.mesh.field.setNumber(1, "VOut", lc)
gmsh.model.mesh.field.setNumber(1, "XMin", 0)
gmsh.model.mesh.field.setNumber(1, "XMax", L)
gmsh.model.mesh.field.setNumber(1, "YMin", 0)
gmsh.model.mesh.field.setNumber(1, "YMax", H)
gmsh.model.mesh.field.add("Min", 2)
gmsh.model.mesh.field.setNumbers(2, "FieldsList", [1])
gmsh.model.mesh.field.setAsBackgroundMesh(2)
gmsh.model.occ.synchronize()
if mesh_comm.rank == model_rank:
gmsh.option.setNumber("Mesh.Algorithm", 8)
gmsh.option.setNumber("Mesh.RecombinationAlgorithm", 2)
gmsh.option.setNumber("Mesh.RecombineAll", 1)
gmsh.option.setNumber("Mesh.SubdivisionAlgorithm", 1)
gmsh.model.mesh.generate(gdim)
gmsh.model.mesh.setOrder(2)
gmsh.model.mesh.optimize("Netgen")
# Generate mesh and facets
mesh, _, ft = gmshio.model_to_mesh(gmsh.model, mesh_comm, model_rank, gdim=gdim)
ft.name = "Facet markers"
# Define surface differential at boundaries
ds_Outlet = Measure("ds", domain=mesh, subdomain_data=ft, subdomain_id=outlet_marker)
ds_Inlet = Measure("ds", domain=mesh, subdomain_data=ft, subdomain_id=inlet_marker)
ds_Wall = Measure("ds", domain=mesh, subdomain_data=ft, subdomain_id=wall_marker)
# Define normal vector
n = FacetNormal(mesh)
# Define function spaces
from ufl import VectorElement, TrialFunction, TestFunction
# Velocity element space
v_cg2 = VectorElement('Lagrange', mesh.ufl_cell(), 2) # Order 2
V = FunctionSpace(mesh, v_cg2)
# Trial and test functions
u = TrialFunction(V)
v = TestFunction(V)
# Create initial condition
u_n = Function(V)
u_n.name = "u_n"
# Inlet BC
class Inlet():
def __init__(self, C, H):
self.C = C
self.H = H
def __call__(self, x):
values = np.zeros((gdim, x.shape[1]),dtype=PETSc.ScalarType)
values[0] = (1.0/self.C)*x[1]*(self.H - x[1])
values[1] = 0
return values
# Walls BC
class Walls():
def __init__(self, t):
self.t = T
def __call__(self, x):
values = np.zeros((gdim, x.shape[1]),dtype=PETSc.ScalarType)
values[0] = 0
values[1] = 0
return values
fdim = gdim - 1
outlet_facets = ft.indices[ft.values == outlet_marker]
inlet_facets = ft.indices[ft.values == inlet_marker]
wall_facets = ft.indices[ft.values == wall_marker]
t = 0
iterations = 0
# Dirichlet boundaries
inlet_velocity = Inlet(C, H)
noslip = Walls(t)
u_D_I = fem.Function(V)
u_D_W = fem.Function(V)
u_D_I.interpolate(inlet_velocity)
u_D_W.interpolate(noslip)
bc_I = fem.dirichletbc(u_D_I, fem.locate_dofs_topological(V, fdim, inlet_facets))
bc_W = fem.dirichletbc(u_D_W, fem.locate_dofs_topological(V, fdim, wall_facets))
bc = [bc_I, bc_W]
unit_vector_x = dolfinx.fem.Constant(mesh, np.array([1.0, 0.0]))
k = dolfinx.fem.Constant(mesh, dt)
''' Variational Form '''
# Unknown terms of the variational form
# Temporal component
T_comp = ufl.inner(u,v)*ufl.dx
# Spatial component
S_comp = ufl.inner(ufl.dot(u,unit_vector_x),ufl.div(v))*ufl.dx
S_comp -= ufl.inner((1/Re)*ufl.grad(u),ufl.grad(v))*ufl.dx
# Boundary component
B_comp = ufl.inner(ufl.dot(ufl.grad(u),n),v)*ds_Outlet
B_comp = ufl.inner(ufl.dot(ufl.grad(u),n),v)*ds_Wall
# Unknown terms of the variational form
# Temporal component
T_comp_old = ufl.inner(u_n,v)*ufl.dx
# Spatial component
S_comp_old = ufl.inner(ufl.dot(u_n,unit_vector_x),ufl.div(v))*ufl.dx
S_comp_old -= ufl.inner((1/Re)*ufl.grad(u_n),ufl.grad(v))*ufl.dx
# Boundary component
B_comp_old = ufl.inner(ufl.dot(ufl.grad(u_n),n),v)*ds_Outlet
B_comp_old = ufl.inner(ufl.dot(ufl.grad(u_n),n),v)*ds_Wall
# Bilinear and linear form
Form = (1/k)*(T_comp - T_comp_old) - (1/2)*(S_comp + S_comp_old) - (1/2)*(B_comp + B_comp_old)
a = fem.form(ufl.lhs(Form))
L = fem.form(ufl.rhs(Form))
# Assemble left-hand side matrix A and right-hand side vector b
A = fem.petsc.assemble_matrix(a, bcs=bc)
A.assemble()
b = fem.petsc.create_vector(L)
# Linear system solver
solver = PETSc.KSP().create(mesh.comm)
solver.setOperators(A)
solver.setType(PETSc.KSP.Type.GMRES)
import tqdm.notebook
if rank == 0:
progress = tqdm.tqdm(desc="Solving PDE", total=num_steps)
for iterations in range(num_steps):
if rank == 0:
progress.update(1)
t += dt
# Update the right hand side reusing the initial vector
with b.localForm() as loc_b:
loc_b.set(0)
fem.petsc.assemble_vector(b, L)
# Apply Dirichlet boundary condition to the vector
apply_lifting(b, [a], [bc])
b.ghostUpdate(addv=PETSc.InsertMode.ADD_VALUES, mode=PETSc.ScatterMode.REVERSE)
set_bc(b, bc)
# Solve linear problem
solver.solve(b, u_n.vector)
u_n.x.scatter_forward()
I need to get the velocity field at the right boundary, i.e., the outlet boundary.
I know the dofs of the boundary using fem.locate_dofs_topological(V, fdim, outlet_facets)
, but if I just do u_n.x.array[fem.locate_dofs_topological(V, fdim, outlet_facets)]
, the values are not in the correct order.
Does anybody know how to solve this issue?
Many thanks in advance.