#!/usr/bin/env python
# coding: utf-8
# In[1]:
from IPython.display import HTML
s="""
2D Navier-Stokes for Channel Flow
""";
h=HTML(s); h
# ***
# # Understand the Problem
#
# ## Question
#
# * What is the 2D velocity **and** pressure field for the **2D Navier Stokes Equation** for flow within an **open channel**?
#
# ## Initial Conditions
#
# * The velocity and pressure fields are zero everywhere
#
# ## Boundary Conditions
#
# * `x = 0m` and `x = 2m` $\Rightarrow$ ` u, v ` and ` p ` are **periodic**
#
# * `y = 0m` and `y = 2m` $\Rightarrow$ `u = 0m/s` and `v = 0m/s` i.e. **no slip wall**
#
# * `y = 0m` and `y = 2m` $\Rightarrow$ `dp/dy = 0` i.e. **impermeable wall**
#
# **NOTE: where velocity is specified, pressure cannot also be specified, because in this case:**
#
# * velocity is a function of pressure
# * pressure is a function of velocity
#
# **Specifiying the pressure and the velocity would result in an overspecified problem**
#
#
#
# ## Governing Equations
#
# * The Navier Stokes Momentum Equation is described as follows:
#
# $$ {\partial u \over \partial t} + u {\partial u \over \partial x} + v {\partial u \over \partial y} = F_x -{1 \over \rho} {{\partial p} \over {\partial x}} + \nu \left ( {\partial^2 u \over \partial x^2}+ {\partial^2 u \over \partial y^2} \right ) $$
#
# where: $ F_x = 1$ (a source term)
#
# $$ {\partial v \over \partial t} + u {\partial v \over \partial x} + v {\partial v \over \partial y} = -{1 \over \rho} {{\partial p} \over {\partial y}} + \nu \left ( {\partial^2 v \over \partial x^2}+ {\partial^2 v \over \partial y^2} \right ) $$
#
# * The Poisson Equation to link pressure and velocity is:
#
# $$ \nabla^2 p^{n+1} = \rho {{\nabla \cdot \mathbf{u}^n} \over {\Delta t}}-
# \rho \nabla \cdot (\mathbf{u}^n \cdot \nabla \mathbf{u}^n)+
# \mu \nabla^2 (\nabla \cdot \mathbf{u}^n)
# $$
#
# Where:
#
# $$ \nabla \cdot (\mathbf{u} \cdot \nabla \mathbf{u}) =
# \begin{bmatrix} {{\partial} \over {\partial x} } \ {{\partial} \over {\partial y} } \end{bmatrix}
# \cdot \begin{bmatrix} (u {{\partial u} \over {\partial x} } + v {{\partial u} \over {\partial y}}) \ \mathbf{i} \\ (u {{\partial v} \over {\partial x} } + v {{\partial v} \over {\partial y} }) \ \mathbf{j} \end{bmatrix} = \left( {{\partial u} \over {\partial x}} \right)^2 +
# 2 {{\partial u} \over {\partial y}} {{\partial v} \over {\partial x}} +
# \left( {{\partial v} \over {\partial y}} \right)^2
# $$
#
# Hence:
#
# * **Assuming viscosity is small**, the Poisson Equation to link pressure and velocity is as follows:
#
# $$ {{{\partial^2 p} \over {\partial x^2}} + {{\partial^2 p} \over {\partial y^2}} =
# {\rho \over \Delta t} \left( {{\partial u} \over {\partial x}} + {{\partial v} \over {\partial y}} \right)
# - \rho \left[ \left( {{\partial u} \over {\partial x}} \right)^2 +
# 2 {{\partial u} \over {\partial y}} {{\partial v} \over {\partial x}} +
# \left( {{\partial v} \over {\partial y}} \right)^2 \right] }$$
#
# (this equation applies in the discrete domain)
#
# ***
# # Formulate the Problem
#
# ## Input Data:
#
# The Poisson Equation has **no temporal component**, so we use a number of iterations `niter`
#
# The Navier Stokes Momentum Equation **does have a temporal component**, so we use `nt`
#
# * `niter` = 51 (maximum number of iterations - for Poisson Equation)
# * `nt` = 51 (number of temporal points)
# * `nx` = 21 (number of x spatial points)
# * `ny` = 21 (number of y spatial points)
# * `tmax` = 0.5
# * `xmax` = 2
# * `ymax` = 2
# * `nu` = 0.1
# * `rho` = 1
#
# ## Initial Conditions:
#
# * $\forall (x, y, t) \quad t = 0 \rightarrow u(x,y,t) = 0 \land v(x,y,t) = 0 \land p(x,y,t) = 0$
#
# ## Velocity Boundary Conditions:
#
# * $\forall (x, y, t) \quad x = 0 \lor x = 2 \rightarrow u(0,y,t) = u(2,y,t) \land v(0,y,t) = v(2,y,t)$
#
# * $\forall (x, y, t) \quad y = 0 \lor y = 2 \rightarrow u(x,0,t) = 0 \land u(x,2,t) = 0 \land v(x,0,t) = 0 \land v(x,2,t) = 0$
#
# ## Pressure Boundary Conditions:
#
# * $\forall (x, y, t) \quad x = 0 \lor x = 2 \rightarrow p(0,y,t) = p(2,y,t)$
#
# * $\forall (x, y, t) \quad y = 0 \lor y = 2 \rightarrow {{\partial p(x,0,t)} / {\partial y}} = 0 \land {{\partial p(x,2,t)} / {\partial y}} = 0$
#
# ## Output Data:
#
# * $\forall (x, y, t) \quad \ p(x,y,t) = ? \land u(x,y,t) = ? \land v(x,y,t) = ?$
#
# ***
#
# # Design Algorithm to Solve Problem
#
# ## Space-time discretisation:
#
# * i $\rightarrow$ index of grid in x
# * j $\rightarrow$ index of grid in y
# * n $\rightarrow$ index of time
# * m $\rightarrow$ index of iterations
#
# ## Numerical schemes for each Momentum Equation
#
# * For the **one** first derivative of velocity in time: 1st order FD in time
# * For the **two** first derivatives of velocity in space: 1st order BD in space
# * For the **one** first derivative of pressure in space: 2nd order CD in space
# * For the **two** second derivatives of velocity in space: 2nd order CD in space
#
# ## Numerical schemes for the Poisson Equation
#
# * For the **two** second derivatives of pressure in space: 2nd order CD in space
# * The the **four** first derivatives of velocity in space: 2nd order CD in space
#
# ## Discrete equation for u-Momentum Equation
#
# $$ {{u_{i,j}^{n+1} - u_{i,j}^n} \over {\Delta t}} +
# u_{i,j}^n {{u_{i,j}^n - u_{i-1,j}^n} \over \Delta x} +
# v_{i,j}^n {{u_{i,j}^n - u_{i,j-1}^n} \over \Delta y} = \\
# 1 -{1 \over \rho} {{p_{i+1,j}^n - p_{i-1,j}^n} \over {2 \Delta x}} +
# \nu {{u_{i-1,j}^n - 2u_{i,j}^n + u_{i+1,j}^n} \over \Delta x^2} +
# \nu {{u_{i,j-1}^n - 2u_{i,j}^n + u_{i,j+1}^n} \over \Delta y^2}
# $$
#
# ## Transpose
#
# Assume $ \Delta x = \Delta y = h$
#
# $$ {u_{i,j}^{n+1} = u_{i,j}^n - {{\Delta t} \over h} \left[ u_{i,j}^n(u_{i,j}^n - u_{i-1,j}^n) + v_{i,j}^n(u_{i,j}^n - u_{i,j-1}^n)\right] \\
# + {\Delta t} - {{\Delta t} \over {2 \rho h}} (p_{i+1,j}^n - p_{i-1,j}^n) + {{\Delta t \nu} \over {h^2}}
# (u_{i-1,j}^n + u_{i+1,j}^n + u_{i,j-1}^n + u_{i,j+1}^n - 4 u_{i,j}^n )}
# $$
#
# ## Discrete equation for v-Momentum Equation
#
# $$ {{v_{i,j}^{n+1} - v_{i,j}^n} \over {\Delta t}} +
# u_{i,j}^n {{v_{i,j}^n - v_{i-1,j}^n} \over \Delta x} +
# v_{i,j}^n {{v_{i,j}^n - v_{i,j-1}^n} \over \Delta y} = \\
# -{1 \over \rho} {{p_{i,j+1}^n - p_{i,j-1}^n} \over {2 \Delta y}} +
# \nu {{v_{i-1,j}^n - 2v_{i,j}^n + v_{i+1,j}^n} \over \Delta x^2} +
# \nu {{v_{i,j-1}^n - 2v_{i,j}^n + v_{i,j+1}^n} \over \Delta y^2}
# $$
#
# ## Transpose
#
# Assume $ \Delta x = \Delta y = h$
#
# $$ v_{i,j}^{n+1} = v_{i,j}^n - {{\Delta t} \over h} \left[ u_{i,j}^n(v_{i,j}^n - v_{i-1,j}^n) + v_{i,j}^n(v_{i,j}^n - v_{i,j-1}^n)\right] - \\
# {{\Delta t} \over {2 \rho h}} (p_{i,j+1}^n - p_{i,j-1}^n) + {{\Delta t \nu} \over {h^2}}
# (v_{i-1,j}^n + v_{i+1,j}^n + v_{i,j-1}^n + v_{i,j+1}^n - 4 v_{i,j}^n )
# $$
#
# ## Discrete equation for Poisson Equation must be Divergence Free
#
# Since no terms have a differential temporal component - bring source term to other side and equate to forward differencing (as we did for Laplace Equation). Then take steady state after a certain number of iterations.
#
# $$ {{\partial p} \over {\partial t}} = {\partial^2 p \over \partial x^2} + {\partial^2 p \over \partial y^2} -
# \left[ {\rho \over \Delta t} \left( {{\partial u} \over {\partial x}} + {{\partial v} \over {\partial y}} \right)
# - \rho \left[ \left( {{\partial u} \over {\partial x}} \right)^2 +
# 2 {{\partial u} \over {\partial y}} {{\partial v} \over {\partial x}} +
# \left( {{\partial v} \over {\partial y}} \right)^2 \right] \right] $$
#
# $$ {{\partial p} \over {\partial t}} = {\partial^2 p \over \partial x^2} + {\partial^2 p \over \partial y^2} -
# b $$
#
# $$ {{p_{i,j}^{m+1}-p_{i,j}^m} \over {\Delta \tau}} = {{p_{i+1,j}^m -2p_{i,j}^m + p_{i-1,j}^m} \over \Delta x^2} + {{p_{i,j+1}^m -2p_{i,j}^m + p_{i,j-1}^m} \over \Delta y^2} - b_{i,j}^m $$
#
# Assume that $ \Delta x = \Delta y = h $
#
# $$ {{p_{i,j}^{m+1}-p_{i,j}^m} \over {\Delta \tau}} = {{p_{i+1,j}^m -2p_{i,j}^m + p_{i-1,j}^m} \over h^2} + {{p_{i,j+1}^m -2p_{i,j}^m + p_{i,j-1}^m} \over h^2} - b_{i,j}^m $$
#
#
# $$ p_{i,j}^{m+1} = p_{i,j}^m + {{\Delta \tau} \over {h^2}} \left(
# p_{i+1,j}^m + p_{i-1,j}^m + p_{i,j+1}^m + p_{i,j-1}^m - 4p_{i,j}^m \right) - b_{i,j}^m \Delta \tau $$
#
# For the fastest convergence $ r = {{\Delta \tau} \over {h^2}} = {1 \over 4} $ and $ \Delta \tau = {{h^2} \over 4} $
#
# Hence:
#
# $$ p_{i,j}^{m+1} = {1 \over 4} \left( p_{i+1,j}^m + p_{i-1,j}^m + p_{i,j+1}^m + p_{i,j-1}^m - b_{i,j}^m h^2 \right) $$
#
# So now we need to define $ b_{i,j}^m $
#
# $$
# b = {\rho \over \Delta t} \left( {{\partial u} \over {\partial x}} + {{\partial v} \over {\partial y}} \right)
# - \rho \left[ \left( {{\partial u} \over {\partial x}} \right)^2 +
# 2 {{\partial u} \over {\partial y}} {{\partial v} \over {\partial x}} +
# \left( {{\partial v} \over {\partial y}} \right)^2 \right]
# $$
#
#
#
# $$ b_{i,j}^{m} =
# {\rho \over \Delta t} \left( {{u_{i+1,j}^n - u_{i-1,j}^n} \over {2 \Delta x}} + {{v_{i,j+1}^n - v_{i,j-1}^n} \over {2 \Delta y}} \right) +
# \rho \left[ \left( {{u_{i+1,j}^n - u_{i-1,j}^n} \over {2 \Delta x}} \right)^2 + 2 \left( {{u_{i,j+1}^n - u_{i,j-1}^n} \over {2 \Delta y}} {{v_{i+1,j}^n - v_{i-1,j}^n} \over {2 \Delta x}}\right) +
# \left( {{v_{i,j+1}^n - v_{i,j-1}^n} \over {2 \Delta y}} \right)^2 \right] $$
#
# Also assume that $ \Delta x = \Delta y = h $
#
#
# $$ b_{i,j}^{m} =
# {\rho \over {2 h \Delta t}} ( {{u_{i+1,j}^n - u_{i-1,j}^n}} + {{v_{i,j+1}^n - v_{i,j-1}^n}} ) +
# {\rho \over {4h^2}} \left[ (u_{i+1,j}^n - u_{i-1,j}^n)^2 + 4h ( u_{i,j+1}^n - u_{i,j-1}^n) (v_{i+1,j}^n - v_{i-1,j}^n) + (v_{i,j+1}^n - v_{i,j-1}^n)^2 \right] $$
#
# ## Question: What governs the stability of these schemes?
#
# * The term that grows quickest with reducing spatial step size?
# * The CD term for diffusion contains the coefficient $ {\nu \Delta t} / h^2 $
# * Based on the previous von Neumann stability analysis $ {\nu \Delta t} / h^2 \le 0.5$
# ## Pseudo-code
#
# # Inputs:
# niter = 51
# nx = 21
# xmax = 2
# tmax = 0.5
# r = 0.25
# nu = 0.1
# rho = 1
#
# # Calculated values - nt is a function of r and dx (should be int) - WHAT VALUE OF r IS STABLE? 0.5?
# nt = int{ ( (nu*tmax) / (r*(dx)**2) )+1 }
# dt = tmax/(nt-1)
# ymax = xmax
#
# # Increments in x and y are the same - h, dy & dx should be rational numbers:
# h = dy = dx = xmax/(nx-1)
#
# # Compute ny from dy and ymax - ny should be an integer
# ny = int{ (ymax/dy) + 1 }
#
# # Initial Conditions:
# p[:,:] = 0
# pm[:,:] = 0
# u[:,:] = 0
# v[:,:] = 0
# un[:,:] = 0
# vn[:,:] = 0
# b[:,:] = 0
#
# # Velocity Boundary Conditions:
# u[:,0] = u[:,ny-1] = v[:,0] = v[:,ny-1] = 0
#
# for n between 0 and nt-1
#
# # Numerical Computation - u and v at i=0 and i=nx-1 are unknown
# - u and v at j=0 and j=ny-1 are known
#
# for i between 0 and nx-1
# for j between 1 and ny-2
#
# # Periodic Boundary Conditions
# if(i==0)
# u[i-1,:] = u[nx-1,:]
# v[i-1,:] = v[nx-1,:]
# else if(i==nx-1)
# u[i+1,:] = u[0, :]
# v[i+1,:] = v[0, :]
#
# # Value of Poisson RHS
# b[i,j] = { rho / (2*h*dt) } * { u[i+1,j] - u[i-1,j] + v[i,j+1] - v[i,j-1] } +
# { rho / (4*h^2) } * { (u[i+1,j] - u[i-1,j])^2 +
# 4*h*(u[i,j+1] - u[i,j-1])*(v[i+1,j] - v[i-1,j]) +
# (v[i,j+1] - v[i,j-1])^2 }
#
# for m between 0 and niter-1
#
# pm = copyof(p)
#
# for i between 0 and nx-1
# for j between 1 and ny-2
#
# # Periodic Boundary Conditions
# if(i==0)
# pm[i-1,:] = pm[nx-1,:]
# else if(i==nx-1)
# pm[i+1,:] = pm[0, :]
#
# p[i,j] = 0.25*( pm[i+1,j] + pm[i-1,j] + pm[i,j+1] + pm[i,j-1] - b[i,j]*h**2 )
#
# un = copyof(u)
# vn = copyof(v)
#
# for i between 0 and nx-1
# for j between 1 and ny-2
#
# # Periodic Boundary Conditions
# if(i==0)
# un[i-1,:] = un[nx-1,:]
# vn[i-1,:] = vn[nx-1,:]
# else if(i==nx-1)
# un[i+1,:] = un[0, :]
# vn[i+1,:] = vn[0, :]
#
# u[i,j] = un[i,j] - {dt / h} * { un[i,j] * ( un[i,j] - un[i-1,j] ) +
# vn[i,j] * ( un[i,j] - un[i,j-1] ) } +
# {dt} - {dt / (2 * rho * h)} * { p[i+1,j] - p[i-1,j] } +
# {(dt * nu) / (h^2)} * { un[i-1,j] + un[i+1,j] + un[i,j-1] + un[i,j+1] - 4 * un[i,j] }
#
# v[i,j] = vn[i,j] - {dt / h} * { un[i,j] * ( vn[i,j] - vn[i-1,j] ) +
# vn[i,j] * ( vn[i,j] - vn[i,j-1] ) } -
# {dt / (2 * rho * h)} * { p[i,j+1] - p[i,j-1] } +
# {(dt * nu) / (h^2)} * { vn[i-1,j] + vn[i+1,j] + vn[i,j-1] + vn[i,j+1] - 4 * vn[i,j] }
#
# ***
# # Implement Algorithm in Python
#
# ## Question: How does the value of r affect the pressure in the **Poisson** Equation?
# Try to implement a Parabolic velocity in u and v so that b is non-zero
# In[91]:
def poisson_initialisation(niter, r, nx_or_ny, tmax, xmax_or_ymax, nu):
"""
Initialise conditions for Poisson Equation
"""
# Increments:
nx = ny = nx_or_ny
xmax = ymax = xmax_or_ymax
dx = xmax/(nx-1)
dy = ymax/(ny-1)
nt = int(((nu * tmax) / (r*(dx)**2))+1)
dt = tmax/(nt-1)
# Initialise data structures:
import numpy as np
# Initial conditions
p = np.zeros((nx,ny))
u = np.zeros((nx,ny))
v = np.zeros((nx,ny))
# x and y range
x = np.zeros(nx)
y = np.zeros(ny)
# X Loop
x = np.linspace(0.0,2.0,nx)
# Y Loop
y = np.linspace(0.0,2.0,ny)
# Velocity Initial Conditions:
#u[:,0:ny] = [(1.0/nu) * (y[j] - ((y[j])**2.0 / 2.0)) for j in range(ny) ]
for i in range(nx):
for j in range(ny):
u[i,j] = x[i] * y[j] * (2.0-x[i])*(2.0-y[j])
v[i,j] = x[i] * y[j] * (2.0-x[i])*(2.0-y[j])
return p, x, y, u, v, nx, ny, nt, dx, dy, dt, niter, r
# In[92]:
(p30, x30, y30, u30, v30, nx, ny, nt,
dx, dy, dt, niter, r) = poisson_initialisation(50, 0.25, 51, 0.5, 2.0, 0.1)
# In[93]:
def plot_3D(u,x,y,title,label):
"""
Plots the 2D velocity field
"""
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
fig=plt.figure(figsize=(11,7),dpi=100)
ax=fig.gca(projection='3d')
ax.set_xlabel('x (m)')
ax.set_ylabel('y (m)')
ax.set_zlabel(label)
ax.set_xlim(0,2)
ax.set_ylim(0,1)
ax.view_init(30,225)
Y,X=np.meshgrid(y,x) #note meshgrid uses y,x not x,y!!!
surf=ax.plot_surface(X,Y,u[:,:], rstride=1, cstride=1)
plt.title(title)
plt.show()
# In[94]:
plot_3D(u30,x30,y30,'Figure 1: Initial Conditions','u (m/s)')
plot_3D(v30,x30,y30,'Figure 2: Initial Conditions','v (m/s)')
plot_3D(p30,x30,y30,'Figure 3: Initial Conditions','p (Pa)')
# In[95]:
def poisson_equation(rho, nu, r):
(p, x, y, u, v, nx, ny, nt,
dx, dy, dt, niter, r) = poisson_initialisation(50, r, 51, 0.5, 2.0, 0.1)
# Increments
h = dx
import numpy as np
# Intermediate copies:
un = np.zeros((nx, ny))
vn = np.zeros((nx, ny))
pm = np.zeros((nx, ny))
bm = np.zeros((nx, ny)) # bm needs to be exactly zero at the boundaries
# First points for b. We know the velocity at j=0 and j=ny-1. We don't know it at i=0 and i=nx-1
# However, we will re-write the equation for b at i=0 and i=nx-1
# Loop - use decimal points for all floating point numbers
for n in range(nt):
i=1
j=1
bm[i:nx-1, j:ny-1] = ( (rho / (2.0 * h * dt)) * ( u[i+1:nx, j:ny-1] - u[i-1:nx-2, j:ny-1]
+ v[i:nx-1, j+1:ny] - v[i:nx-1, j-1:ny-2] ) +
(rho / (4.0*h**2)) * ( (u[i+1:nx, j:ny-1] - u[i-1:nx-2, j:ny-1])**2.0 +
4.0*h*(u[i:nx-1, j+1:ny] - u[i:nx-1, j-1:ny-2])*
(v[i+1:nx, j:ny-1] - v[i-1:nx-2, j:ny-1]) +
(v[i:nx-1, j+1:ny] - v[i:nx-1, j-1:ny-2])**2.0 ) )
i=0
# Periodic at i = 0 Replace all the i=i-1 with i=nx-1
bm[i, j:ny-1] = ( (rho / (2.0 * h * dt)) * ( u[i+1, j:ny-1] - u[nx-1, j:ny-1]
+ v[i, j+1:ny] - v[i, j-1:ny-2] ) +
(rho / (4.0*h**2)) * ( (u[i+1, j:ny-1] - u[nx-1, j:ny-1])**2.0 +
4.0*h*(u[i, j+1:ny] - u[i, j-1:ny-2])*
(v[i+1, j:ny-1] - v[nx-1, j:ny-1]) +
(v[i, j+1:ny] - v[i, j-1:ny-2])**2.0 ) )
i=nx-1
# Periodic at i = nx-1 Replace all the i=i+1 with i=0
bm[i, j:ny-1] = ( (rho / (2.0 * h * dt)) * ( u[0, j:ny-1] - u[i-1, j:ny-1]
+ v[i, j+1:ny] - v[i, j-1:ny-2] ) +
(rho / (4.0*h**2)) * ( (u[0, j:ny-1] - u[i-1, j:ny-1])**2.0 +
4.0*h*(u[i, j+1:ny] - u[i, j-1:ny-2])*
(v[0, j:ny-1] - v[i-1, j:ny-1]) +
(v[i, j+1:ny] - v[i, j-1:ny-2])**2.0 ) )
# First points for p. We don't know the pressure at i=0 and i=nx-1. We DO know the pressure at j=ny-1
for m in range(niter):
pm = np.copy(p)
i=1
j=1
p[i:nx-1, j:ny-1] = 0.25*( pm[i+1:nx, j:ny-1] + pm[i-1:nx-2, j:ny-1]
+ pm[i:nx-1, j+1:ny] + pm[i:nx-1, j-1:ny-2]
- bm[i:nx-1, j:ny-1]*h**2.0 )
i=0
# Periodic at i = 0 Replace all the i=i-1 with i=nx-1
p[i, j:ny-1] = 0.25*( pm[i+1, j:ny-1] + pm[nx-1, j:ny-1]
+ pm[i, j+1:ny] + pm[i, j-1:ny-2]
- bm[i, j:ny-1]*h**2.0 )
i=nx-1
# Periodic at i = nx-1 Replace all the i=i+1 with i=0
p[i, j:ny-1] = 0.25*( pm[0, j:ny-1] + pm[i-1, j:ny-1]
+ pm[i, j+1:ny] + pm[i, j-1:ny-2]
- bm[i, j:ny-1]*h**2.0 )
# Set zero gradient boundary conditions
p[:, 0] = p[:, 1]
p[:, ny-1] = p[:, ny-2]
return u, v, p, x, y, bm
# In[96]:
u40, v40, p40, x40, y40, bm40 = poisson_equation(1, 0.1, 0.25)
# In[97]:
plot_3D(u40,x40,y40,'Figure 1: Final Conditions','u (m/s)')
plot_3D(v40,x40,y40,'Figure 2: Final Conditions','v (m/s)')
plot_3D(p40,x40,y40,'Figure 3: Final Conditions','p (Pa)')
plot_3D(bm40,x40,y40,'Figure 4: Final Conditions','b')
# ## Question: What is the effect of r on the magnitude of p?
# In[98]:
u1, v1, p1, x1, y1, bm1 = poisson_equation(1, 0.1, 0.25)
u2, v2, p2, x2, y2, bm2 = poisson_equation(1, 0.1, 0.50)
u3, v3, p3, x3, y3, bm3 = poisson_equation(1, 0.1, 1.00)
u4, v4, p4, x4, y4, bm4 = poisson_equation(1, 0.1, 1.50)
u5, v5, p5, x5, y5, bm5 = poisson_equation(1, 0.1, 2.00)
# In[99]:
plot_3D(p1,x1,y1,'Figure 1: Final Conditions','p (Pa)')
plot_3D(p2,x2,y2,'Figure 2: Final Conditions','p (Pa)')
plot_3D(p3,x3,y3,'Figure 3: Final Conditions','p (Pa)')
plot_3D(p4,x4,y4,'Figure 4: Final Conditions','p (Pa)')
plot_3D(p5,x5,y5,'Figure 5: Final Conditions','p (Pa)')
# ## Conclusion about the value of r in the equation for b
# r does affect the magnitude of p - therefore it is not a question of stability, but of dimensional consistency - the same r must be used so that the units of pressure are maintained.
#
# In other words, pressure and velocity must step forward in time using the same timestep
# ## So, now this is established - what is the effect of r on the stability of the Burgers Equation for this problem?
# See whether starting the values at zero and then allowing convection would make a difference?
# In[108]:
def burgers_initialisation(niter, r, nx_or_ny, tmax, xmax_or_ymax, nu):
"""
Initialise conditions for Poisson Equation
"""
# Increments:
nx = ny = nx_or_ny
xmax = ymax = xmax_or_ymax
dx = xmax/(nx-1)
dy = ymax/(ny-1)
nt = int(((nu * tmax) / (r*(dx)**2))+1)
dt = tmax/(nt-1)
# Initialise data structures:
import numpy as np
# Initial conditions
p = np.zeros((nx,ny))
u = np.zeros((nx,ny))
v = np.zeros((nx,ny))
# x and y range
x = np.zeros(nx)
y = np.zeros(ny)
# X Loop
x = np.linspace(0.0,2.0,nx)
# Y Loop
y = np.linspace(0.0,2.0,ny)
return p, x, y, u, v, nx, ny, nt, dx, dy, dt, niter, r
# In[111]:
def burgers_equation(rho, nu, r):
(p, x, y, u, v, nx, ny, nt,
dx, dy, dt, niter, r) = burgers_initialisation(50, r, 51, 0.5, 2.0, nu)
# Increments
h = dx
import numpy as np
# Intermediate copies:
un = np.zeros((nx, ny))
vn = np.zeros((nx, ny))
# Loop - use decimal points for all floating point numbers
for n in range(nt):
# First points for b. We know the velocity at i=0, j=0, i=nx-1 and j=ny-1. b is zero at the boundaries.
i=1
j=1
# First points for u and v. We know the velocity at i=0, j=0, i=nx-1 and j=ny-1.
# We are simply using the value of pressure here
un = np.copy(u)
vn = np.copy(v)
u[i:nx-1, j:ny-1] = ( un[i:nx-1, j:ny-1] -
(dt / h) * ( un[i:nx-1, j:ny-1] * ( un[i:nx-1, j:ny-1] - un[i-1:nx-2, j:ny-1] ) +
vn[i:nx-1, j:ny-1] * ( un[i:nx-1, j:ny-1] - un[i:nx-1, j-1:ny-2] ) ) +
dt - (dt / (2.0 * rho * h)) * ( p[i+1:nx, j:ny-1] - p[i-1:nx-2, j:ny-1] ) +
((dt * nu) / (h**2.0)) * ( un[i-1:nx-2, j:ny-1] + un[i+1:nx, j:ny-1] +
un[i:nx-1, j-1:ny-2] + un[i:nx-1, j+1:ny] -
4.0 * un[i:nx-1, j:ny-1] ) )
v[i:nx-1, j:ny-1] = ( vn[i:nx-1, j:ny-1] -
(dt / h) * ( un[i:nx-1, j:ny-1] * ( vn[i:nx-1, j:ny-1] - vn[i-1:nx-2, j:ny-1] ) +
vn[i:nx-1, j:ny-1] * ( vn[i:nx-1, j:ny-1] - vn[i:nx-1, j-1:ny-2] ) ) -
(dt / (2.0 * rho * h)) * ( p[i:nx-1, j+1:ny] - p[i:nx-1, j-1:ny-2] ) +
((dt * nu) / (h**2.0)) * ( vn[i-1:nx-2, j:ny-1] + vn[i+1:nx, j:ny-1] +
vn[i:nx-1, j-1:ny-2] + vn[i:nx-1, j+1:ny] -
4.0 * vn[i:nx-1, j:ny-1] ) )
i=0
# Periodic at i = 0 Replace all the i=i-1 with i=nx-1
u[i, j:ny-1] = ( un[i, j:ny-1] -
(dt / h) * ( un[i, j:ny-1] * ( un[i, j:ny-1] - un[nx-1, j:ny-1] ) +
vn[i, j:ny-1] * ( un[i, j:ny-1] - un[i, j-1:ny-2] ) ) +
dt - (dt / (2.0 * rho * h)) * ( p[i+1, j:ny-1] - p[nx-1, j:ny-1] ) +
((dt * nu) / (h**2.0)) * ( un[nx-1, j:ny-1] + un[i+1, j:ny-1] +
un[i, j-1:ny-2] + un[i, j+1:ny] -
4.0 * un[i, j:ny-1] ) )
v[i, j:ny-1] = ( vn[i, j:ny-1] -
(dt / h) * ( un[i, j:ny-1] * ( vn[i, j:ny-1] - vn[nx-1, j:ny-1] ) +
vn[i, j:ny-1] * ( vn[i, j:ny-1] - vn[i, j-1:ny-2] ) ) -
(dt / (2.0 * rho * h)) * ( p[i, j+1:ny] - p[i, j-1:ny-2] ) +
((dt * nu) / (h**2.0)) * ( vn[i-1, j:ny-1] + vn[i+1, j:ny-1] +
vn[i, j-1:ny-2] + vn[i, j+1:ny] -
4.0 * vn[i, j:ny-1] ) )
i=nx-1
# Periodic at i = nx-1 Replace all the i=i+1 with i=0
u[i, j:ny-1] = ( un[i, j:ny-1] -
(dt / h) * ( un[i, j:ny-1] * ( un[i, j:ny-1] - un[i-1, j:ny-1] ) +
vn[i, j:ny-1] * ( un[i, j:ny-1] - un[i, j-1:ny-2] ) ) +
dt - (dt / (2.0 * rho * h)) * ( p[0, j:ny-1] - p[i-1, j:ny-1] ) +
((dt * nu) / (h**2.0)) * ( un[i-1, j:ny-1] + un[0, j:ny-1] +
un[i, j-1:ny-2] + un[i, j+1:ny] -
4.0 * un[i, j:ny-1] ) )
v[i, j:ny-1] = ( vn[i, j:ny-1] -
(dt / h) * ( un[i, j:ny-1] * ( vn[i, j:ny-1] - vn[i-1, j:ny-1] ) +
vn[i, j:ny-1] * ( vn[i, j:ny-1] - vn[i, j-1:ny-2] ) ) -
(dt / (2.0 * rho * h)) * ( p[i, j+1:ny] - p[i, j-1:ny-2] ) +
((dt * nu) / (h**2.0)) * ( vn[i-1, j:ny-1] + vn[0, j:ny-1] +
vn[i, j-1:ny-2] + vn[i, j+1:ny] -
4.0 * vn[i, j:ny-1] ) )
return u, v, p, x, y
# In[115]:
u10, v10, p10, x10, y10 = burgers_equation(1, 0.1, 0.25)
u20, v20, p20, x20, y20 = burgers_equation(1, 0.1, 0.50)
u30, v30, p30, x30, y30 = burgers_equation(1, 0.1, 1.00)
u40, v40, p40, x40, y40 = burgers_equation(1, 0.1, 1.50)
u50, v50, p50, x50, y50 = burgers_equation(1, 0.1, 2.00)
# In[117]:
plot_3D(u10,x10,y10,'Figure 1: Final Conditions r = 0.25','u (m/s)')
plot_3D(u20,x20,y20,'Figure 2: Final Conditions r = 0.5','u (m/s)')
plot_3D(u30,x30,y30,'Figure 3: Final Conditions r = 1.0' ,'u (m/s)')
plot_3D(u40,x40,y40,'Figure 4: Final Conditions r = 1.5','u (m/s)')
plot_3D(u50,x50,y50,'Figure 5: Final Conditions r = 2.0','u (m/s)')
# As expected, if the value of r is greater than 0.5, then we have an unstable Burgers Equation
# ## Now try it with the full Navier Stokes Equations
# In[22]:
def navier_stokes_initialisation(niter, r, nx_or_ny, tmax, xmax_or_ymax, nu):
"""
Initialise conditions for Poisson Equation
"""
# Increments:
nx = ny = nx_or_ny
xmax = ymax = xmax_or_ymax
dx = xmax/(nx-1)
dy = ymax/(ny-1)
nt = int(((nu * tmax) / (r*(dx)**2))+1)
dt = tmax/(nt-1)
# Initialise data structures:
import numpy as np
# Initial conditions
p = np.zeros((nx,ny))
u = np.zeros((nx,ny))
v = np.zeros((nx,ny))
# x and y range
x = np.zeros(nx)
y = np.zeros(ny)
# X Loop
x = np.linspace(0.0,2.0,nx)
# Y Loop
y = np.linspace(0.0,2.0,ny)
return p, x, y, u, v, nx, ny, nt, dx, dy, dt, niter, r
# In[72]:
p, x, y, u, v, nx, ny, nt, dx, dy, dt, niter, r = navier_stokes_initialisation(50, 0.5, 51, 0.5, 2.0, 0.1)
# In[74]:
dt
# In[124]:
def navier_stokes_equation(rho, nu, r):
(p, x, y, u, v, nx, ny, nt,
dx, dy, dt, niter, r) = navier_stokes_initialisation(50, r, 51, 0.5, 2.0, nu)
# Increments
h = dx
import numpy as np
# Intermediate copies:
un = np.zeros((nx, ny))
vn = np.zeros((nx, ny))
pm = np.zeros((nx, ny))
bm = np.zeros((nx, ny)) # bm needs to be exactly zero at the boundaries
# Loop - use decimal points for all floating point numbers
for n in range(nt):
# First points for bm. We don't know the value at i=0 and i=nx-1. b is zero at j=0 and j=ny-1.
i=1
j=1
bm[i:nx-1, j:ny-1] = ( (rho / (2.0 * h * dt)) * ( u[i+1:nx, j:ny-1] - u[i-1:nx-2, j:ny-1]
+ v[i:nx-1, j+1:ny] - v[i:nx-1, j-1:ny-2] ) +
(rho / (4.0*h**2)) * ( (u[i+1:nx, j:ny-1] - u[i-1:nx-2, j:ny-1])**2.0 +
4.0*h*(u[i:nx-1, j+1:ny] - u[i:nx-1, j-1:ny-2])*
(v[i+1:nx, j:ny-1] - v[i-1:nx-2, j:ny-1]) +
(v[i:nx-1, j+1:ny] - v[i:nx-1, j-1:ny-2])**2.0 ) )
i=0
# Periodic at i = 0 Replace all the i=i-1 with i=nx-1
bm[i, j:ny-1] = ( (rho / (2.0 * h * dt)) * ( u[i+1, j:ny-1] - u[nx-1, j:ny-1]
+ v[i, j+1:ny] - v[i, j-1:ny-2] ) +
(rho / (4.0*h**2)) * ( (u[i+1, j:ny-1] - u[nx-1, j:ny-1])**2.0 +
4.0*h*(u[i, j+1:ny] - u[i, j-1:ny-2])*
(v[i+1, j:ny-1] - v[nx-1, j:ny-1]) +
(v[i, j+1:ny] - v[i, j-1:ny-2])**2.0 ) )
i=nx-1
# Periodic at i = nx-1 Replace all the i=i+1 with i=0
bm[i, j:ny-1] = ( (rho / (2.0 * h * dt)) * ( u[0, j:ny-1] - u[i-1, j:ny-1]
+ v[i, j+1:ny] - v[i, j-1:ny-2] ) +
(rho / (4.0*h**2)) * ( (u[0, j:ny-1] - u[i-1, j:ny-1])**2.0 +
4.0*h*(u[i, j+1:ny] - u[i, j-1:ny-2])*
(v[0, j:ny-1] - v[i-1, j:ny-1]) +
(v[i, j+1:ny] - v[i, j-1:ny-2])**2.0 ) )
for m in range(niter):
# First points for p. We don't know the pressure at i=0 and i=nx-1. dp/dy = 0 at y=0 and y=2
pm = np.copy(p)
i=1
j=1
p[i:nx-1, j:ny-1] = 0.25*( pm[i+1:nx, j:ny-1] + pm[i-1:nx-2, j:ny-1]
+ pm[i:nx-1, j+1:ny] + pm[i:nx-1, j-1:ny-2]
- bm[i:nx-1, j:ny-1]*h**2.0 )
i=0
# Periodic at i = 0 Replace all the i=i-1 with i=nx-1
p[i, j:ny-1] = 0.25*( pm[i+1, j:ny-1] + pm[nx-1, j:ny-1]
+ pm[i, j+1:ny] + pm[i, j-1:ny-2]
- bm[i, j:ny-1]*h**2.0 )
i=nx-1
# Periodic at i = nx-1 Replace all the i=i+1 with i=0
p[i, j:ny-1] = 0.25*( pm[0, j:ny-1] + pm[i-1, j:ny-1]
+ pm[i, j+1:ny] + pm[i, j-1:ny-2]
- bm[i, j:ny-1]*h**2.0 )
# Set zero gradient boundary conditions
p[:, 0] = p[:, 1]
p[:, ny-1] = p[:, ny-2]
# First points for u and v. We don't know u and v at i=0 and i=nx-1. u and v are zero at j=0 and j=ny-1.
i=1
j=1
un = np.copy(u)
vn = np.copy(v)
u[i:nx-1, j:ny-1] = ( un[i:nx-1, j:ny-1] -
(dt / h) * ( un[i:nx-1, j:ny-1] * ( un[i:nx-1, j:ny-1] - un[i-1:nx-2, j:ny-1] ) +
vn[i:nx-1, j:ny-1] * ( un[i:nx-1, j:ny-1] - un[i:nx-1, j-1:ny-2] ) ) +
dt - (dt / (2.0 * rho * h)) * ( p[i+1:nx, j:ny-1] - p[i-1:nx-2, j:ny-1] ) +
((dt * nu) / (h**2.0)) * ( un[i-1:nx-2, j:ny-1] + un[i+1:nx, j:ny-1] +
un[i:nx-1, j-1:ny-2] + un[i:nx-1, j+1:ny] -
4.0 * un[i:nx-1, j:ny-1] ) )
v[i:nx-1, j:ny-1] = ( vn[i:nx-1, j:ny-1] -
(dt / h) * ( un[i:nx-1, j:ny-1] * ( vn[i:nx-1, j:ny-1] - vn[i-1:nx-2, j:ny-1] ) +
vn[i:nx-1, j:ny-1] * ( vn[i:nx-1, j:ny-1] - vn[i:nx-1, j-1:ny-2] ) ) -
(dt / (2.0 * rho * h)) * ( p[i:nx-1, j+1:ny] - p[i:nx-1, j-1:ny-2] ) +
((dt * nu) / (h**2.0)) * ( vn[i-1:nx-2, j:ny-1] + vn[i+1:nx, j:ny-1] +
vn[i:nx-1, j-1:ny-2] + vn[i:nx-1, j+1:ny] -
4.0 * vn[i:nx-1, j:ny-1] ) )
# Periodic at i = 0 Replace all the i=i-1 with i=nx-1
i=0
u[i, j:ny-1] = ( un[i, j:ny-1] -
(dt / h) * ( un[i, j:ny-1] * ( un[i, j:ny-1] - un[nx-1, j:ny-1] ) +
vn[i, j:ny-1] * ( un[i, j:ny-1] - un[i, j-1:ny-2] ) ) +
dt - (dt / (2.0 * rho * h)) * ( p[i+1, j:ny-1] - p[nx-1, j:ny-1] ) +
((dt * nu) / (h**2.0)) * ( un[nx-1, j:ny-1] + un[i+1, j:ny-1] +
un[i, j-1:ny-2] + un[i, j+1:ny] -
4.0 * un[i, j:ny-1] ) )
v[i, j:ny-1] = ( vn[i, j:ny-1] -
(dt / h) * ( un[i, j:ny-1] * ( vn[i, j:ny-1] - vn[nx-1, j:ny-1] ) +
vn[i, j:ny-1] * ( vn[i, j:ny-1] - vn[i, j-1:ny-2] ) ) -
(dt / (2.0 * rho * h)) * ( p[i, j+1:ny] - p[i, j-1:ny-2] ) +
((dt * nu) / (h**2.0)) * ( vn[i-1, j:ny-1] + vn[i+1, j:ny-1] +
vn[i, j-1:ny-2] + vn[i, j+1:ny] -
4.0 * vn[i, j:ny-1] ) )
# Periodic at i = nx-1 Replace all the i=i+1 with i=0
i=nx-1
u[i, j:ny-1] = ( un[i, j:ny-1] -
(dt / h) * ( un[i, j:ny-1] * ( un[i, j:ny-1] - un[i-1, j:ny-1] ) +
vn[i, j:ny-1] * ( un[i, j:ny-1] - un[i, j-1:ny-2] ) ) +
dt - (dt / (2.0 * rho * h)) * ( p[0, j:ny-1] - p[i-1, j:ny-1] ) +
((dt * nu) / (h**2.0)) * ( un[i-1, j:ny-1] + un[0, j:ny-1] +
un[i, j-1:ny-2] + un[i, j+1:ny] -
4.0 * un[i, j:ny-1] ) )
v[i, j:ny-1] = ( vn[i, j:ny-1] -
(dt / h) * ( un[i, j:ny-1] * ( vn[i, j:ny-1] - vn[i-1, j:ny-1] ) +
vn[i, j:ny-1] * ( vn[i, j:ny-1] - vn[i, j-1:ny-2] ) ) -
(dt / (2.0 * rho * h)) * ( p[i, j+1:ny] - p[i, j-1:ny-2] ) +
((dt * nu) / (h**2.0)) * ( vn[i-1, j:ny-1] + vn[0, j:ny-1] +
vn[i, j-1:ny-2] + vn[i, j+1:ny] -
4.0 * vn[i, j:ny-1] ) )
return u, v, p, x, y
# In[125]:
u50, v50, p50, x50, y50 = navier_stokes_equation(1.0, 0.1, 0.5)
# In[126]:
plot_3D(u50,x50,y50,'Figure 1: Final Conditions','u (m/s)')
plot_3D(v50,x50,y50,'Figure 2: Final Conditions','v (m/s)')
plot_3D(p50,x50,y50,'Figure 3: Final Conditions','p (Pa)')
# ## How does this compare with the analytical solution?
# We know the answer to this problem, it's just flow between parallel plates
#
# $$ {\partial u \over \partial t} + u {\partial u \over \partial x} + v {\partial u \over \partial y} = 1 -{1 \over \rho} {{\partial p} \over {\partial x}} + \nu \left ( {\partial^2 u \over \partial x^2}+ {\partial^2 u \over \partial y^2} \right ) $$
#
# In steady state:
#
# * $ {\partial u / \partial t} = 0 $
# * $ {\partial u / \partial x} = 0 $
# * $ v = 0 $
# * $ {\partial p / \partial x} = 0 $
# * $ {\partial^2 u / \partial x^2} = 0 $
#
# Hence:
#
# $$ 1 + \nu {\partial^2 u \over \partial y^2} = 0 \qquad \Rightarrow \qquad {\partial^2 u \over \partial y^2} = -{1 \over \nu} \qquad \Rightarrow \qquad \int \int {d^2 u \over d y^2} dy dy = -{1 \over \nu} \int \int dy dy$$
#
# $$ \int {d u \over d y} dy = -{1 \over \nu} \int (y + C_1 ) dy \qquad \Rightarrow \qquad u = -{1 \over \nu} \left({y^2 \over 2} + C_1y +C_2 \right) $$
#
# * $u = 0 \text{ at } y = 0 \qquad \Rightarrow \qquad C_2 = 0$
# * $u = 0 \text{ at } y = 2 \qquad \Rightarrow \qquad C_2 = - 1$
#
# $$ u = -{1 \over \nu} \left({y^2 \over 2} - y \right) = {1 \over \nu} \left(y - {y^2 \over 2} \right)$$
# In[7]:
def parallel_plates_analytical(YMAX, NY, NU):
# Initialise data structures
u_analytical = np.zeros(NY)
y = np.zeros(NY)
# Constants
DY = YMAX/(NY-1)
# Y Loop
for j in range(0,NY):
y[j] = j*DY
# Analytical solution
for j in range(0, NY):
u_analytical[j]= (1.0 / NU) * (y[j] - (y[j]**2.0 / 2.0))
return u_analytical, y
# In[8]:
u_analytic, y_analytic = parallel_plates_analytical(2.0, 51, 0.1)
# In[59]:
def plot_diffusion(u1,u2,y1,y2,NX):
"""
Plots the 1D velocity field
"""
import matplotlib.pyplot as plt
import matplotlib.cm as cm
plt.figure()
ax=plt.subplot(111)
ax.plot(y1,u1[NX-1,:],'-', markerfacecolor='none', alpha=0.5, label='Numerical')
ax.plot(y2,u2[:],linestyle='-',c='r',label='Analytical')
box=ax.get_position()
ax.set_position([box.x0, box.y0, box.width*1.5,box.height*1.5])
ax.legend( bbox_to_anchor=(1.02,1), loc=2)
plt.xlabel('y (m)')
plt.ylabel('u (m/s)')
plt.show()
# In[150]:
plot_diffusion(u50,u_analytic,y_analytic,51)
# ## Why Doesn't the Numerical Solution Agree with the Analytical?
# * Where the gradient is shallow, the values are not changing very much. There I suspect convergence is loose
# * I'm not sure at this stage why the magnitude is about one tenth - could be a order of magnitude due to misplacement of the kinematic viscosity (which is 0.1)
#
# * **Instead of advancing in time, look for the steady state solution when the streamwise velocity does not change by more than** $10^{-3}$
# In[95]:
def navier_stokes_equation_accurate(niter, nx, rho, nu, r, tmax):
(p, x, y, u, v, nx, ny, nt,
dx, dy, dt, niter, r) = navier_stokes_initialisation(niter, r, nx, tmax, 2.0, nu)
# Increments
h = dx
import numpy as np
# Intermediate copies:
un = np.zeros((nx, ny))
vn = np.zeros((nx, ny))
pm = np.zeros((nx, ny))
bm = np.zeros((nx, ny)) # bm needs to be exactly zero at the boundaries
# Loop - use decimal points for all floating point numbers
#for n in range(nt):
udiff = 1.0
while udiff > 0.001:
# First points for bm. We don't know the value at i=0 and i=nx-1. b is zero at j=0 and j=ny-1.
i=1
j=1
bm[i:nx-1, j:ny-1] = ( (rho / (2.0 * h * dt)) * ( u[i+1:nx, j:ny-1] - u[i-1:nx-2, j:ny-1]
+ v[i:nx-1, j+1:ny] - v[i:nx-1, j-1:ny-2] ) +
(rho / (4.0*h**2)) * ( (u[i+1:nx, j:ny-1] - u[i-1:nx-2, j:ny-1])**2.0 +
4.0*h*(u[i:nx-1, j+1:ny] - u[i:nx-1, j-1:ny-2])*
(v[i+1:nx, j:ny-1] - v[i-1:nx-2, j:ny-1]) +
(v[i:nx-1, j+1:ny] - v[i:nx-1, j-1:ny-2])**2.0 ) )
i=0
# Periodic at i = 0 Replace all the i=i-1 with i=nx-1
bm[i, j:ny-1] = ( (rho / (2.0 * h * dt)) * ( u[i+1, j:ny-1] - u[nx-1, j:ny-1]
+ v[i, j+1:ny] - v[i, j-1:ny-2] ) +
(rho / (4.0*h**2)) * ( (u[i+1, j:ny-1] - u[nx-1, j:ny-1])**2.0 +
4.0*h*(u[i, j+1:ny] - u[i, j-1:ny-2])*
(v[i+1, j:ny-1] - v[nx-1, j:ny-1]) +
(v[i, j+1:ny] - v[i, j-1:ny-2])**2.0 ) )
i=nx-1
# Periodic at i = nx-1 Replace all the i=i+1 with i=0
bm[i, j:ny-1] = ( (rho / (2.0 * h * dt)) * ( u[0, j:ny-1] - u[i-1, j:ny-1]
+ v[i, j+1:ny] - v[i, j-1:ny-2] ) +
(rho / (4.0*h**2)) * ( (u[0, j:ny-1] - u[i-1, j:ny-1])**2.0 +
4.0*h*(u[i, j+1:ny] - u[i, j-1:ny-2])*
(v[0, j:ny-1] - v[i-1, j:ny-1]) +
(v[i, j+1:ny] - v[i, j-1:ny-2])**2.0 ) )
for m in range(niter):
# First points for p. We don't know the pressure at i=0 and i=nx-1. dp/dy = 0 at y=0 and y=2
pm = np.copy(p)
i=1
j=1
p[i:nx-1, j:ny-1] = 0.25*( pm[i+1:nx, j:ny-1] + pm[i-1:nx-2, j:ny-1]
+ pm[i:nx-1, j+1:ny] + pm[i:nx-1, j-1:ny-2]
- bm[i:nx-1, j:ny-1]*h**2.0 )
i=0
# Periodic at i = 0 Replace all the i=i-1 with i=nx-1
p[i, j:ny-1] = 0.25*( pm[i+1, j:ny-1] + pm[nx-1, j:ny-1]
+ pm[i, j+1:ny] + pm[i, j-1:ny-2]
- bm[i, j:ny-1]*h**2.0 )
i=nx-1
# Periodic at i = nx-1 Replace all the i=i+1 with i=0
p[i, j:ny-1] = 0.25*( pm[0, j:ny-1] + pm[i-1, j:ny-1]
+ pm[i, j+1:ny] + pm[i, j-1:ny-2]
- bm[i, j:ny-1]*h**2.0 )
# Set zero gradient boundary conditions
p[:, 0] = p[:, 1]
p[:, ny-1] = p[:, ny-2]
# First points for u and v. We don't know u and v at i=0 and i=nx-1. u and v are zero at j=0 and j=ny-1.
i=1
j=1
un = np.copy(u)
vn = np.copy(v)
u[i:nx-1, j:ny-1] = ( un[i:nx-1, j:ny-1] -
(dt / h) * ( un[i:nx-1, j:ny-1] * ( un[i:nx-1, j:ny-1] - un[i-1:nx-2, j:ny-1] ) +
vn[i:nx-1, j:ny-1] * ( un[i:nx-1, j:ny-1] - un[i:nx-1, j-1:ny-2] ) ) +
dt - (dt / (2.0 * rho * h)) * ( p[i+1:nx, j:ny-1] - p[i-1:nx-2, j:ny-1] ) +
((dt * nu) / (h**2.0)) * ( un[i-1:nx-2, j:ny-1] + un[i+1:nx, j:ny-1] +
un[i:nx-1, j-1:ny-2] + un[i:nx-1, j+1:ny] -
4.0 * un[i:nx-1, j:ny-1] ) )
v[i:nx-1, j:ny-1] = ( vn[i:nx-1, j:ny-1] -
(dt / h) * ( un[i:nx-1, j:ny-1] * ( vn[i:nx-1, j:ny-1] - vn[i-1:nx-2, j:ny-1] ) +
vn[i:nx-1, j:ny-1] * ( vn[i:nx-1, j:ny-1] - vn[i:nx-1, j-1:ny-2] ) ) -
(dt / (2.0 * rho * h)) * ( p[i:nx-1, j+1:ny] - p[i:nx-1, j-1:ny-2] ) +
((dt * nu) / (h**2.0)) * ( vn[i-1:nx-2, j:ny-1] + vn[i+1:nx, j:ny-1] +
vn[i:nx-1, j-1:ny-2] + vn[i:nx-1, j+1:ny] -
4.0 * vn[i:nx-1, j:ny-1] ) )
# Periodic at i = 0 Replace all the i=i-1 with i=nx-1
i=0
u[i, j:ny-1] = ( un[i, j:ny-1] -
(dt / h) * ( un[i, j:ny-1] * ( un[i, j:ny-1] - un[nx-1, j:ny-1] ) +
vn[i, j:ny-1] * ( un[i, j:ny-1] - un[i, j-1:ny-2] ) ) +
dt - (dt / (2.0 * rho * h)) * ( p[i+1, j:ny-1] - p[nx-1, j:ny-1] ) +
((dt * nu) / (h**2.0)) * ( un[nx-1, j:ny-1] + un[i+1, j:ny-1] +
un[i, j-1:ny-2] + un[i, j+1:ny] -
4.0 * un[i, j:ny-1] ) )
v[i, j:ny-1] = ( vn[i, j:ny-1] -
(dt / h) * ( un[i, j:ny-1] * ( vn[i, j:ny-1] - vn[nx-1, j:ny-1] ) +
vn[i, j:ny-1] * ( vn[i, j:ny-1] - vn[i, j-1:ny-2] ) ) -
(dt / (2.0 * rho * h)) * ( p[i, j+1:ny] - p[i, j-1:ny-2] ) +
((dt * nu) / (h**2.0)) * ( vn[i-1, j:ny-1] + vn[i+1, j:ny-1] +
vn[i, j-1:ny-2] + vn[i, j+1:ny] -
4.0 * vn[i, j:ny-1] ) )
# Periodic at i = nx-1 Replace all the i=i+1 with i=0
i=nx-1
u[i, j:ny-1] = ( un[i, j:ny-1] -
(dt / h) * ( un[i, j:ny-1] * ( un[i, j:ny-1] - un[i-1, j:ny-1] ) +
vn[i, j:ny-1] * ( un[i, j:ny-1] - un[i, j-1:ny-2] ) ) +
dt - (dt / (2.0 * rho * h)) * ( p[0, j:ny-1] - p[i-1, j:ny-1] ) +
((dt * nu) / (h**2.0)) * ( un[i-1, j:ny-1] + un[0, j:ny-1] +
un[i, j-1:ny-2] + un[i, j+1:ny] -
4.0 * un[i, j:ny-1] ) )
v[i, j:ny-1] = ( vn[i, j:ny-1] -
(dt / h) * ( un[i, j:ny-1] * ( vn[i, j:ny-1] - vn[i-1, j:ny-1] ) +
vn[i, j:ny-1] * ( vn[i, j:ny-1] - vn[i, j-1:ny-2] ) ) -
(dt / (2.0 * rho * h)) * ( p[i, j+1:ny] - p[i, j-1:ny-2] ) +
((dt * nu) / (h**2.0)) * ( vn[i-1, j:ny-1] + vn[0, j:ny-1] +
vn[i, j-1:ny-2] + vn[i, j+1:ny] -
4.0 * vn[i, j:ny-1] ) )
udiff = (np.sum(u)-np.sum(un))/np.sum(u)
return u, v, p, x, y
# In[78]:
u60, v60, p60, x60, y60 = navier_stokes_equation_accurate(50, 51, 1.0, 0.1, 0.5, 2.0)
# In[76]:
np.max(p60)
# In[70]:
plot_diffusion(u60,u_analytic,y60,y_analytic,51)
# ## Observations about accuracy
#
# * Iterating until the velocity doesn't change in steady state has improved the prediction
#
# ## Observations about stability
#
# * We are just on the limit of stability here with r=0.5.
#
# ## Observations about maximum velocity
#
# * However, there may now be a dependency of the maximum velocity on the value of dt because:
#
# - `dt x F` is the source term (like a short pulse)
#
# ## Observations about maximum velocity
#
# * We need to know what value of dt is going to produce the the correct pressure gradient and so the correct maximum velocity
#
# * We need to look at the discretised equation in steady state
# $$ u_{i,j}^{n+1} = u_{i,j}^n - {{\Delta t} \over h} \left[ u_{i,j}^n(u_{i,j}^n - u_{i-1,j}^n) + v_{i,j}^n(u_{i,j}^n - u_{i,j-1}^n)\right] \\
# + {\Delta t} - {{\Delta t} \over {2 \rho h}} (p_{i+1,j}^n - p_{i-1,j}^n) + {{\Delta t \nu} \over {h^2}}
# (u_{i-1,j}^n + u_{i+1,j}^n + u_{i,j-1}^n + u_{i,j+1}^n - 4 u_{i,j}^n )
# $$
# ## What is the stable value of r?
# In[115]:
u02, v02, p02, x02, y02 = navier_stokes_equation_accurate(50, 51, 1.0, 0.1, 0.2, 2.0)
u03, v03, p03, x03, y03 = navier_stokes_equation_accurate(50, 51, 1.0, 0.1, 0.3, 2.0)
u04, v04, p04, x04, y04 = navier_stokes_equation_accurate(50, 51, 1.0, 0.1, 0.4, 2.0)
u05, v05, p05, x05, y05 = navier_stokes_equation_accurate(50, 51, 1.0, 0.1, 0.5, 2.0)
u06, v06, p06, x06, y06 = navier_stokes_equation_accurate(50, 51, 1.0, 0.1, 0.6, 2.0)
# In[119]:
def plot_diffusion(u1,u2,u3,u4,u5,y1,y2,y3,y4,y5,NX):
"""
Plots the 1D velocity field
"""
import matplotlib.pyplot as plt
import matplotlib.cm as cm
plt.figure()
ax=plt.subplot(111)
ax.plot(y1,u1[-1,:],'-', markerfacecolor='none', alpha=0.5, label='Numerical r=0.2')
ax.plot(y2,u2[-1,:],'-', markerfacecolor='none', alpha=0.5, label='Numerical r=0.3')
ax.plot(y3,u3[-1,:],'-', markerfacecolor='none', alpha=0.5, label='Numerical r=0.4')
ax.plot(y4,u4[-1,:],'-', markerfacecolor='none', alpha=0.5, label='Numerical r=0.5')
ax.plot(y5,u5[-1,:],'-', markerfacecolor='none', alpha=0.5, label='Numerical r=0.6')
box=ax.get_position()
ax.set_position([box.x0, box.y0, box.width*1.5,box.height*1.5])
ax.legend( bbox_to_anchor=(1.02,1), loc=2)
plt.xlabel('y (m)')
plt.ylabel('u (m/s)')
plt.show()
# In[120]:
plot_diffusion(u02,u03,u04,u05,u06,y02,y03,y04,y05,y06,51)
# Clearly r=0.5 is still the limit of stability, and notice that r affects the maximum velocity (via it's influence on dt)
# For the same r, reducing nx will increase dx, reduce nt and increase dt
# In[118]:
u002, v002, p002, x002, y002 = navier_stokes_equation_accurate(50, 51, 1.0, 0.1, 0.5, 2.0)
u003, v003, p003, x003, y003 = navier_stokes_equation_accurate(50, 41, 1.0, 0.1, 0.5, 2.0)
u004, v004, p004, x004, y004 = navier_stokes_equation_accurate(50, 31, 1.0, 0.1, 0.5, 2.0)
u005, v005, p005, x005, y005 = navier_stokes_equation_accurate(50, 21, 1.0, 0.1, 0.5, 2.0)
u006, v006, p006, x006, y006 = navier_stokes_equation_accurate(50, 11, 1.0, 0.1, 0.5, 2.0)
# In[122]:
def plot_diffusion_2(u1,u2,u3,u4,u5,y1,y2,y3,y4,y5,NX):
"""
Plots the 1D velocity field
"""
import matplotlib.pyplot as plt
import matplotlib.cm as cm
plt.figure()
ax=plt.subplot(111)
ax.plot(y1,u1[-1,:],'-', markerfacecolor='none', alpha=0.5, label='Numerical nx=51')
ax.plot(y2,u2[-1,:],'-', markerfacecolor='none', alpha=0.5, label='Numerical nx=41')
ax.plot(y3,u3[-1,:],'-', markerfacecolor='none', alpha=0.5, label='Numerical nx=31')
ax.plot(y4,u4[-1,:],'-', markerfacecolor='none', alpha=0.5, label='Numerical nx=21')
ax.plot(y5,u5[-1,:],'-', markerfacecolor='none', alpha=0.5, label='Numerical nx=11')
box=ax.get_position()
ax.set_position([box.x0, box.y0, box.width*1.5,box.height*1.5])
ax.legend( bbox_to_anchor=(1.02,1), loc=2)
plt.xlabel('y (m)')
plt.ylabel('u (m/s)')
plt.show()
# In[123]:
plot_diffusion_2(u002,u003,u004,u005,u006,y002,y003,y004,y005,y006,51)
# Clearly there is also an effect or dx on stability and the maximum occurs when nx=31
# **Bascially we are using an explicit method here, which is not ideal, we keep reaching the limits of stability**
#
# * It is possible that the SOR method could be used to solve the Poisson Equation
# This makes it difficult to compare the values between the numerical and analytical solutions - the source term is being included like a pulse in time, so this might require much more stable methods
# ## How is the source term included?
#
# * The source term is split into steady and unsteady components:
#
# $$ p = P + p' $$
# * The applied pressure gradient is:
#
# $$ -{{\partial P} \over {\partial x}} = 1 $$
# * So the solution in steady state is where the fluctuating pressure is:
#
# $$ {{\partial p'} \over {\partial x}} = 0 $$
# ### Why was this done?
#
# * We used periodic boundary conditions.
# * **Without the source term** this means the pressure on the left and right would have been the same and no flow would have occured
# * **With the source term** the pressure in all the equations is the fluctuating pressure, which in steady state settles out to zero
# ### When is the fluctuating pressure non-zero?
#
# * In turbulent flow the fluctuating pressure is non-zero
# In[ ]: