This is a follow up to a previous question I had Seperated Domains within problem . But this question has diverged a bit from the discussion there so I think a seperate post makes sense.
I am trying to solve an advection diffusion problem with a no flux boundary condtion in the middle of the mesh. I was struggling to implement this with mixed dimensional versions of fenics so I am trying to implement it using DG methods. After reading some resources on this method I have managed to get an implementation which is semi-working using a symmetric interior penalty method on the diffusive term, and then balancing the diffusive fluxes against the advective ones.
The solutions appears sensible and the code doesn’t diverge. However, in the system the mass doesn’t appear to be conserved perfectly. In fact if I start off with total mass=4, by time=2 units of simulation time, the mass has gone up to 4.5. If I comment out the advective terms (IE lines 54 and 58-60) then I end up with just simple diffusion with an internal boundary and this seems to work perfectly.
I was wondering if anyone can advise how to improve my simulation with the adevction so that mass is better conserved here? I suspect maybe there is a problem with my formulation of the boundary terms in the symmetric interior penalty method, but I am not sure.
Code:
# Code to solve advection-diffusion equation using DG method
from fenics import *
set_log_level(30)
T=10
dt=1E-3
theta=0.5
# Create mesh
Lx=1
Ly=1
nx=100
ny=30
mesh = RectangleMesh(Point(-Lx,-Ly),Point(Lx,Ly),nx,ny)
D=0.25
eta=1E-1
# Define function space and functions
V = FunctionSpace(mesh, 'DG', 1)
phi=Function(V)
phi0=Function(V)
v=TestFunction(V)
# initial condition
phi_init=Expression("exp(-pow(x[0]/lx,2)-pow(x[1]/ly,2))",lx=0.2,ly=0.2,degree=2)
# phi_init=Constant(1)
phi0.assign(project(phi_init,V))
# Mark boundary subdomains
class halfway_line( SubDomain ):
def inside ( self, x, on_boundary ):
return abs ( x[0] - 0.5 ) < DOLFIN_EPS
boundary_parts = MeshFunction("size_t", mesh, mesh.topology().dim() - 1)
boundary_parts.set_all(0)
HL=halfway_line()
HL.mark(boundary_parts, 1)
# Set up surface elemtns for fluxes
dS=Measure('dS',domain=mesh,subdomain_data=boundary_parts) # all intenral boundaires
dS_DG=dS(0) # non-physical boundaries due to DG basis
dS_real=dS(1) # internal boundary which is actually real
n=FacetNormal(mesh) # normal vector, used in fluxes
# Weak form bulk part
phi_star=theta*phi+(1-theta)*phi0 # Crank-Nicolson timestepping
vel=Constant((0.1,0)) # velocity field to advect by
h=mesh.hmin()
F=(
(phi-phi0)/dt*v*dx # time derivative
+ D*dot(grad(phi_star),grad(v))*dx # diffusion
+ div(phi_star*vel)*v*dx # advection
- D*dot(jump(phi_star,n),avg(grad(v)))*dS_DG # interior flux, this term and next one are from symmetric interior penalty method (SIPG)
+ D*dot(avg(grad(phi_star)),jump(v,n))*dS_DG
+ (eta/avg(h))*dot(jump(phi_star,n),jump(v,n))*dS_DG # interior flux, enforces continuity also from SIPG
- phi_star('+')*dot(vel('+'),n('+'))*v('+')*dS_real # balances diffuses flux with advectie one accross internal boundary
- phi_star('-')*dot(vel('-'),n('-'))*v('-')*dS_real # balances diffuses flux with advectie one accross internal boundary
- phi_star*dot(vel,n)*v*ds # balances diffuses flux with advectie one accross external boundary
)
# save data
from os.path import expanduser
out_file=expanduser("~/outputs2/DG_test/phi.pvd")
f=File(out_file,"compressed")
for i in range(int(T/dt)):
# Solve nonlinearly as future code will need this
solve(F==0,phi,
solver_parameters={"newton_solver":{"relative_tolerance":1E-12,"absolute_tolerance":1E-12,
"maximum_iterations":1000,"relaxation_parameter":1.0}}) # solve using Newton's method
f<<(phi0,i*dt) # save data
phi0.assign(phi) # update for next timestep
total_mass=assemble(phi*dx) # check mass conservation
print("time: ",i*dt,"total mass: ",total_mass) # print mass conservation