Hello,
I have coded this algorithm to solve the Navier Stokes equations, and I observe a very strange behavior in the solution.
I created a minimal working example for you:
from __future__ import print_function
from fenics import *
import argparse
import ufl as ufl
from mshr import *
import numpy as np
import meshio
parser = argparse.ArgumentParser()
parser.add_argument("T")
parser.add_argument("N")
args = parser.parse_args()
#latin indexes run on 2d curvilinear coordinates
i, j = ufl.indices(2)
L = 2.2
h = 0.41
r = 0.05
c_r = [0.2, 0.2, 0]
# Create mesh
channel = Rectangle(Point(0, 0), Point(2.2, 0.41))
cylinder = Ellipse(Point(0.2, 0.2), 0.05, 0.05)
domain = channel - cylinder
mesh = generate_mesh(domain, 64)
# Define boundaries and obstacle
#CHANGE PARAMETERS HERE
inflow = 'near(x[0], 0)'
outflow = 'near(x[0], 2.2)'
walls = 'near(x[1], 0) || near(x[1], 0.41)'
cylinder = 'on_boundary && x[0]>0.1 && x[0]<0.3 && x[1]>0.1 && x[1]<0.3'
#CHANGE PARAMETERS HERE
T = (float)(args.T)
num_steps = (int)(args.N)
dt = T / num_steps # time step size
mu = 0.001
# Create XDMF files for visualization output
xdmffile_v = XDMFFile("v.xdmf")
xdmffile_v_ = XDMFFile( "v_bar.xdmf" )
xdmf_file_p = XDMFFile( "p.xdmf" )
xdmf_file_phi = XDMFFile("phi.xdmf")
Q_v = VectorFunctionSpace( mesh, 'P', 2, dim=2 )
Q_p = FunctionSpace( mesh, 'P', 1 )
v_n_1 = Function( Q_v )
v_n_2 = Function( Q_v )
v_n = Function( Q_v )
v_ = Function( Q_v )
p_n_1 = Function( Q_p )
p_n_2 = Function( Q_p )
p_n = Function( Q_p )
phi = Function( Q_p )
nu = TestFunction( Q_v )
q = TestFunction( Q_p )
#trial analytical expression for a vector
class TangentVelocityExpression(UserExpression):
def eval(self, values, x):
values[0] = 0.0
values[1] = 0.0
def value_shape(self):
return (2,)
# trial analytical expression for the surface tension p(x,y)
class SurfaceTensionExpression(UserExpression):
def eval(self, values, x):
# values[0] = 4*x[0]*x[1]*sin(8*(norm(np.subtract(x, c_r)) - r))*sin(8*(norm(np.subtract(x, c_R)) - R))
# values[0] = cos(norm(np.subtract(x, c_r)) - r) * sin(norm(np.subtract(x, c_R)) - R)
values[0] = 0.0
def value_shape(self):
return (1,)
# set the initial conditions for all fields
v_n_1.interpolate( TangentVelocityExpression( element=Q_v.ufl_element() ) )
v_n_2.assign(v_n_1)
p_n_1.interpolate( SurfaceTensionExpression( element=Q_p.ufl_element() ) )
p_n_2.assign(p_n_1)
inflow_profile = ('4.0*1.5*x[1]*(0.41 - x[1]) / pow(0.41, 2)', '0')
bcv_inflow = DirichletBC( Q_v, Expression( inflow_profile, degree=2 ), inflow )
bcv_walls = DirichletBC( Q_v, Constant( (0, 0) ), walls )
bcv_cylinder = DirichletBC( Q_v, Constant( (0, 0) ), cylinder )
bcphi_outflow = DirichletBC( Q_p, Constant( 0 ), outflow )
# boundary conditions for the surface_tension p
bc_v = [bcv_walls, bcv_cylinder, bcv_inflow]
bc_phi = [bcphi_outflow]
# Define expressions used in variational forms
V = (v_n_1 + v_)/2.0
Deltat = Constant(dt)
mu = Constant(mu)
# Define variational problem for step 1
F1 = ( (\
( (v_[i]- v_n_1[i]) / Deltat \
+ (3.0/2.0 * v_n_1[j] - 1.0/2.0 * v_n_2[j]) * (V[i]).dx(j) ) ) * nu[i] \
- (p_n_1 + p_n_2)/2.0 * ((nu[i]).dx(i)) + mu * ((V[i]).dx(j)) * ((nu[i]).dx(j)) \
) * dx
# step 2
F2 = ( (phi.dx(i)) * (q.dx(i)) + (1.0 / Deltat) * (v_[i]).dx(i) * q ) * dx
# Define variational problem for step 3
F3 = ( (v_n[i] - v_[i]) + Deltat * (phi.dx(i)) ) * nu[i] * dx
print("Starting time iteration ...", flush=True)
# Time-stepping
t = 0
for n in range(num_steps):
# Write the solution to file
xdmffile_v.write(v_n_1, t)
xdmffile_v_.write(v_, t )
xdmf_file_p.write(p_n_1, t )
xdmf_file_phi.write(phi, t)
# Update current time
t += dt
# Step 1: Tentative velocity step
solve(F1 == 0, v_, bc_v)
# Step 2: surface_tension correction step
solve(F2 == 0, phi, bc_phi)
# obtain p_n from phi by using the definition of phi
p_n.assign(2*phi + p_n_2)
# Step 3: Velocity correction step
solve(F3 == 0, v_n)
#update the fields for the next step
v_n_2.assign(v_n_1)
v_n_1.assign(v_n)
p_n_2.assign(p_n_1)
p_n_1.assign(p_n)
print("\t%.2f %%" % (100.0 * (t / T)), flush=True)
print("... done.", flush=True)
which you can run with python3 example.py 0.1 128
, where 0.1 is the simulation time and 128 the number of time steps in which it is divided.
The solution for the pressure p is odd: it alternates between positive and negative signs at subsequent time steps, see the screenshots attached for two time steps as an example. The same alternating behavior occurs for other time steps.
May you please explain to me what I am doing wrong here ??