Boundary Conditions & Convergence for 3D Navier-Stokes

Goal: I wish to find a steady state solution for the flow through the cell. I need to reach a flowrate of about 0.1 ml/min.

Problem: The solver diverges for higher flow rates (above ~0.05 ml/min). I have tried increasing mesh resolution and shortening time step with no luck.

Questions:

  1. Is there a better way to define the boundary conditions on the face of a cylinder?
  2. For this type of problem (should be laminar and steady) what type of linear algebra solver/preconditoners should I be using? And are there any parameters I can adjust to find convergence.

Important information:
This example is an adaptation of the Navier-Stokes IPCS tutorial provided by FEniCS. Not much has changed apart from applying boundary conditions suited to my mesh, and attempting speed up computation with the choice of linear algebra backend and preconditioner.

Mesh:
Mesh of topological dimension 3 (tetrahedra) with 13359 vertices and 55927 cells. Generated with Tetgen via ‘generate_mesh(domain, 250, ‘tetgen’)’. The mesh is very coarse, but has been improved with the help of local refinement. The total volume of the cell is roughly 0.4 ml.

The Code:
A pressure gradient is set up by enforcing a Dirichlet condition at the inlet and outlet, and a noslip condition on the walls. The inlet is a circle in the xz-plane centered at the origin with radius 1.59 , that is R^2 = 2.52. I define the walls to be everything except a circle slightly smaller than the radius of the pipe R^2=2.4. I do not like this method for defining my boundary condition and I think that it could easily cause problems. If, for instance, the no slip condition includes some points on the face of the inlet then the velocity will have to jump from zero to an increasingly larger number an infinitesimal distance away.


#Parameters
P= 0.1    # Pressure (g mm^-1 s^-2)
μ= 8.94e-4 # Dynamic Viscosity of Water (g mm^-1 s^-1)
ρ= 1       # density of water (g/ml)
Flow_Rate = 0.00166 # 0.1 ml/ 60s
Radius = 1.59 # radius of inlet pipe (mm)
T = 120       # simulation time (s)
dt = 0.05     # Timestep
...
#Boundary Conditions
inflow  = 'on_boundary && near(x[1],0,1e-4) && x[0]<10'
outflow = 'on_boundary && near(x[1],0,1e-4) && x[0]>10' 
walls =  'on_boundary && !((((pow(x[0],2)+pow(x[2],2))<2.4) | ((pow(x[0]-19.05,2)+pow(x[2],2))<2.4)) && near(x[1],0) && on_boundary)'

bcu_noslip  = DirichletBC(V, Constant((0,0,0)), walls)
bcp_inflow  = DirichletBC(Q, Constant(P), inflow)
bcp_outflow = DirichletBC(Q, Constant(0), outflow)
...

#Solve
# Step 1: Tentative velocity step
b1 = assemble(L1)
[bc.apply(b1) for bc in bcu]
solve(A1, u_.vector(), b1,"gmres", "default")

# Step 2: Pressure correction step
b2 = assemble(L2)
[bc.apply(b2) for bc in bcp]
solve(A2, p_.vector(), b2,"gmres", prec)

# Step 3: Velocity correction step
b3 = assemble(L3)
solve(A3, u_.vector(), b3,"gmres", "default")
...

Figure: (LEFT) Simple boundary color labeling. Red for the walls and blue the inlet.
(RIGHT) Pressure(surface colour) and Velocity (vector) solution.

Error:
*** Error: Unable to solve linear system using PETSc Krylov solver.
*** Reason: Solution failed to converge in 0 iterations (PETSc reason DIVERGED_NANORINF, residual norm ||r|| = 0.000000e+00).
*** Where: This error was encountered inside PETScKrylovSolver.cpp.
*** Process: 0
*** DOLFIN version: 2017.2.0

Fenics Version: 2017.2 (Installed via Ubuntu package)
OS: Ubuntu 16.04 LTS

I will suggest starting with a direct solver. If that works, then try gmres/cg (non-symmetric/symmetric) with jacobi preconditioner. If that works too, then experiment with the multigrid preconditioners (petsc_amg, amg, hypre_amg).

Hi

I got similar issue when solving Navier-Stokes

You figured it out what was wrong?

Thanks,
Victor

I am convinced that this is a boundary condition issue. The inlet + outlet pressure condition seems to conflict with the noslip condition at the edge of the cylinder, producing non-zero velocities on the wall. To remedy this I used a stronger set of boundary conditions, prescribing the velocity at the inlet and pressure at the outlet. I chose the velocity profile to be parabolic so that it is zero at on the walls, conforming with the noslip condition.

v_parabolic=Expression(('0','A*(R*R-(pow(x[0],2)+pow(x[2],2)))','0'), A=0.18, R=Constant(Radius),element = V.ufl_element())
bcu_inlet   = DirichletBC(V, v_parabolic, inflow)
bcu = [bcu_noslip,bcu_inlet]
bcp = [bcp_outflow] 

Note: The initial method of pressure-pressure conditions seemed to work fine up to a certain flow rate where the solver became unstable. It might be worth looking into a different solver/method alternative to IPCS.

If anyone has any suggestions on better defining the pressure+noslip conditions please share!

Hello, I’m having the exact same problem. I have no idea how to fix it and have attempted your recommended fix of assigning a parabolic velocity profile with no luck.

For myself -

  1. Import “.stp” file into NETGEN
  2. Prepare the mesh ready to be exported.
  3. Use Meshio to transform the mesh from .gmsh2 to .XDMF - Up to here everything is successful and looks fine in paraview
  4. Run typical Navier-Stokes script - Everything breaks - Flow and pressure increase to breaking point causing the “Reason: Solution failed to converge in 0 iterations (PETSc reason DIVERGED_NANORINF, residual norm ||r|| = 0.000000e+00).”

I have checked my old simulations where my mesh was generated using dolfin-convert. The mesh size with regards to tetrahedrals, surface faces and elements are very similar, if not identical. The script is the same. Only difference is using Meshio-convert instead of dolfin-convert.

Things I have tried and that have failed:

  • Attempted stronger set of boundary conditions
  • Force .XDMF to be -xml (Using meshio)
  • Exported as dolfin-xml (Using meshio)
  • Attempted abaqus/other formats instead of gmsh (Using meshio)
  • Increased mesh resolution and decreased time steps
  • Attempted different solvers
  • Attempted different base meshes
  • Attempted known .STEP files to create meshes which work using dolfin-convert
  • Assigned velocity inflow at different directions in X, Y and Z.

For myself, I am investigating other mesh converters as the only thing I’ve changed is using meshio. I doubt it is meshio but that is all I have remaining to check now. Unfortuantely I cannot use dolfin-convert as the majority of the time as it refuses to complete the conversion. The error is an “AssertionError” which is very unclear what the issue is.

Anyone wants to help or offer a suggestion, please do. I urgently need to simulate my models and this has halted my entire workflow.

The storage format of the mesh is most likely not the reasons for issues with your simulation. If you can create a minimal example (with for example the built in mesh (UnitCubeMesh), it will be easier to figure out what is the problem with your code.

Hello dokken,

I was going to start a new post with an example as this post is dated but glad it is being picked up in the forum. I believe you are correct that it is something in my code which is not working. However, I am unsure why it has been working prior to now and achieving convergence in dolfin meshes.

All files are available via this OneDrive link - Expires Saturday, 15th February 2020 - Files include “Navier-stokes.py”, “importScript.py”, “.stp” CAD, “.msh” (.gmsh2) mesh, “mesh.xdmf” converted meshio mesh, “mf.xdmf” (boundary conditions), velocity and pressure exported as “.xdmf” files. FWD.msh/mesh.xdmf is the current mesh presented which does no converge. MRBD.xml was a previous dolfin mesh which converged. It was imported as mesh = Mesh("mesh.xml").

Apologies it is not the cleanest code as I have been switching things around but I hope it provides enough information for you to recreate this example. Below are some code snippets for other users who may look back at this post at a later date without my files available.

Setup parameters

T = 3           # final time
num_steps = 300 #50    # number of time steps
dt = 0.01 #T / num_steps # time step size
mu = 1             # kinematic viscosity
rho = 1            # density    

from dolfin import * 
mesh = Mesh()
with XDMFFile("mesh.xdmf") as infile:
infile.read(mesh)

# Define function spaces
V = VectorFunctionSpace(mesh, "Lagrange", 3)#'P', 3)
Q = FunctionSpace(mesh, "Lagrange", 1)#'P', 1)

dim = mesh.topology().dim()
print('Dim:',dim)

mvc = MeshValueCollection("size_t", mesh, dim-1) 
with XDMFFile("mf.xdmf") as infile:
infile.read(mvc, "name_to_read")
facet_domains = cpp.mesh.MeshFunctionSizet(mesh, mvc)

Boundary conditions

Based upon the "name_to_read"
bcu_noslip = DirichletBC(V, Constant((0, 0, 0)), facet_domains, 0)
#bcp_inflow = DirichletBC(Q, Constant(2), facet_domains, 2)
bcu_inflow = DirichletBC(V, Constant((0.0016, 0, 0)), facet_domains, 26)
bcp_outflow = DirichletBC(Q, Constant(0), facet_domains, 8)

Solver

# Step 1: Tentative velocity step
b1 = assemble(L1)
[bc.apply(b1) for bc in bcu]
solve(A1, u_.vector(), b1,'bicgstab', 'hypre_amg')#,"bicgstab","default")#"gmres", "ilu") #,"bicgstab","default")#

# Step 2: Pressure correction step
b2 = assemble(L2)
[bc.apply(b2) for bc in bcp]
solve(A2, p_.vector(), b2,'bicgstab', 'hypre_amg')#,"bicgstab","prec")#,"cg", prec) 'cg','petsc_amg')

# Step 3: Velocity correction step
b3 = assemble(L3)
solve(A3, u_.vector(), b3,'cg', 'sor')#,"bicgstab","default")#'gmres','ilu')#"cg", "hypre_amg")

# Plot solution - Unable to plot aspect on 3D axes
#plot(u_)

# Compute error
u_e = Expression(('4*x[1]*(1.0 - x[1])', '0', '0'), degree=3)
u_e = interpolate(u_e, V)
error = np.abs(u_e.vector() - u_.vector()).max()
print('t = %.2f: error = %.3g' % (t, error))
print('max u:', u_.vector().max())
print("Worst possible Courant number=",(dt*(u_.vector().max()))/mesh.hmin())

My Courant number begins around 3.8e-4 with a 0.002340542151212975 m/s before reaching 4.5789732854215145e+147 Courant number with a maximum velocity of 2.8100367469290087e+148 m/s at 0.41 seconds. Time step was 0.1.

Hope this provides enough information and thank you for having a look.

Many thanks,
Matthew.

Simulation at time 0 using the meshio preprocessing setup. The following parameters explored above. Constant inflow of 0.0016 m/s was used.


Simulation at 0.31 seconds using the meshio preprocessing setup. The following parameters explored above. Constant inflow of 0.0016 m/s was used.

Simulation at time 0 using the dolfin-convert preprocessing setup. The following parameters explored above. Constant inflow of 0.0016 m/s was used.

Simulation at 0.31 seconds using the dolfin-convert preprocessing setup. The following parameters explored above. Constant inflow of 0.0016 m/s was used.

Command line displaying the runtime error that occurs. The solver was unable to complete using PETSc Krylov solver. No convergence. Also presented are the increasing velocity, Courant number and error.

Hi @Matt688 ,
As I said earlier, you need to make a minimal example, as your code requires alot of memory to be executed. There are several things that I note when browsing through your code, first of all, you are using a P3-P1 FEM pair, while I would rather use P2-P1 or P3-P2.
Secondly, there are many ways of approximating the non-linearity in the Tentative velocity equation, see for instance a summary in my arxiv-paper on IPCS for Navier-Stokes, section 2.

I also find the boundary terms in the tentative velocity equation quite strange (as you have them on all boundaries. To verify your code, you should start by doing 2D (as the only changes to the code is the dimension of velocity fields, and solving the Taylor-Green problem (Its stated in the paper i linked, section 6.1) or the Turek-Benchmark (section 6.2). When you get these codes to give the appropriate convergence rates, it is trivial to extend them to 3D.

Hey @Dokken,

I’ve been busy looking into the things you have suggested and there is a lot more I initially missed adapting the FEniCS demo. Apologies for my lack of understanding and thank you for linking your paper; your paper explains a lot of the implementations I had found in other Navier-Stokes FEniCS examples.

The solution to my original problem was the flow rate was set far too high causing the random flow developments. As always, it was the most obvious and simplest solution. Additionally, I have learnt a lot more about making efficiently dense meshes in desired areas which solved some additional problems I had experienced and highlighted others.

Regarding your point with FEM pairs, I have attempted several implementations of a P2-P1 and P3-P2 FEM pairs in 3D Navier Stokes but it appears to be beyond my understanding and capabilities currently. I followed your advice and ran the 2D Navier Stokes cylinder FEniCS demo successfully and attempted to adapted it for 3D. Unfortunately, I cannot get any 3D Navier Stokes results using a FEM P2-P1 as the solver will converge on zero velocity and pressure. I could only get FEM P3-P1 which does not calculate the turbulence around the cylinder. Is there a quick demo you or anyone else can produce of the 2D Navier Stokes cylinder FEniCS demo in 3D which I can study and understand?

You could have a look at Oasis, which is a library built on top of FEniCS for solving Navier-Stokes. The corresponding paper can be found here.

Hey @dokken,

Oasis helped a lot. I have resolved my problem regarding FEM P2-P1 incompressible Navier Stokes 3D simulation. Thank you for all the information and advice.

The problem was that I renamed all boundaries to an arbitrary number, 0 in many case, and assigned it to be a unified no-slip velocity boundary. I believed this was adequate as they formed a “bcu” boundary list passed into the velocity solver. This was not the case where each boundary has to be defined separately and passed into the “bcu” boundary list. I believe you mentioned this earlier but I misunderstood what you meant. I thought you were implying I needed different boundary conditions to a no-slip velocity to produce a realistic solution or my code implied everything was no-slip without inlet and/or outlet conditions.

Could you be more specific when you say that the pressure does not seem to be correct?
As a 2D and 3D implementation of the Navier-Stokes equation in dolfin only differs when it gets to boundary conditions, you should verify your solver with a 2D mesh.

There are many benchmarks that you can use to verify your implementation, see for instance:
the oasis paper, which used the 2D Taylor-Green problem to verify their solver, which can be found at the Oasis github page. You can also see the multimesh paper on Navier Stokes splitting schemes for fenics, the code is available at Zenodo. In this paper, both the Taylor-Green benchmark, as well as the DFG 2D-3 benchmark was used to verify the implementation. Note that there also exists 3D benchmarks, see this link.

1 Like

You should consider starting from this demo:
https://bitbucket.org/fenics-project/dolfin/src/master/python/demo/documented/navier-stokes/demo_navier-stokes.py
and consider where it differs in your problem.

I am not sure what you are expecting as a result. The code above is only for one time step, and is not runnable, as lines such as :

Makes it unrunnable.

If you are solving for the stationary Navier stokes, you Need to remove the temporal derivative from your scheme.

I would suggest starting without a splitting scheme solving the coupled problem.

1 Like