#!/usr/bin/env python
# coding: utf-8
# In[1]:
from IPython.display import HTML
s="""
Leapfrog, Lax-Friedrichs and Lax-Wendroff Schemes (Heaviside Input)
""";
h=HTML(s); h
# ***
# # Understand the Problem
#
# ## Question
#
# * What is the 1D velocity for the **1D Wave Equation** for flow with a step function for the initial conditions?
#
# ## Initial Conditions
#
# * `t = 0s` and `x = 0m to 2m` $\Rightarrow$ ` u = 1m/s`
#
# * `t = 0s` and `x = 2m to 4m` $\Rightarrow$ `u = 0m/s`
#
# ## Boundary Conditions
#
# * `x = 0m` $\Rightarrow$ ` u = 1m/s`
#
# * `y = 2m` $\Rightarrow$ ` u = 0m/s`
#
#
# ## Governing Equation
#
# * The Wave Equation described as follows:
#
# $$ {\partial u \over \partial t} + c {\partial u \over \partial x} = 0 $$
#
#
#
# ***
# # Formulate the Problem
#
# ## Input Data:
#
# The Wave Equation **does have a temporal component**, so we use `nt`
#
# * `nt` = ? (number of temporal points)
# * `nx` = 51 (number of x spatial points)
# * `tmax` = 1.0
# * `xmax` = 4
# * `c` = 1.0
#
# Formulate `nt` based on the CFL Condition `sigma = c (dt / dx) = 1`
#
# ## Initial Conditions:
#
# * $\forall (x, t) \quad t = 0 \land x \le 2 \rightarrow u(x,0) = 2 $
# * $\forall (x, t) \quad t = 0 \land x \gt 2 \rightarrow u(x,0) = 0 $
#
# ## Velocity Boundary Conditions:
#
# * $\forall (x, t) \quad x = 0 \rightarrow u(0,t) = 2 $
#
# * $\forall (x, t) \quad x = 4 \rightarrow u(4,t) = 0 $
#
# ## Output Data:
#
# * $\forall (x, t) \quad \ u(x,t) = ? $
#
# ***
#
# # Design Algorithm to Solve Problem using Upwind Scheme
#
# ## Space-time discretisation:
#
# * i $\rightarrow$ index of grid in x
# * n $\rightarrow$ index of time
#
# ## Upwind Numerical scheme
#
# * For the **one** first derivative of velocity in time: 1st order FD in time
# * For the **one** first derivative of velocity in space: 1st order BD in space
#
#
# ## Discrete equation
#
# $$ {{u_i^{n+1} - u_i^n} \over {\Delta t}} + c {{u_i^n - u_{i-1}^n} \over \Delta x}=0 $$
#
# ## Transpose
#
# $$ u_i^{n+1} = u_i^n - c{\Delta t \over \Delta x}(u_i^n - u_{i-1}^n) $$
#
# ## Analytical solution
#
# This is simply the wave travelling in the x-direction
#
# $$ u_i^{n+1} = u_{i-1}^n $$
#
# We must limit the number of timesteps used for the analytical solution, proportional to the value of $\sigma$ in order to compare with the numerical solution. Simply limit $t_{max}$ to $\sigma \times t_{max}$
#
# ## Pseudo-code
# #Constants
# xmax = 4.0
# nx = 51
# dx = xmax/(nx-1)
# tmax = 1.0
# sigma = 1.0
# dt = (dx / sigma)
# nt = int(tmax/dt + 1)
#
# #Boundary Conditions
# for n between 0 and nt
# u(0,n)=1
# u(nx-1,n)=0
#
# #Initial Conditions
# for i between 1 and nx-2
# if(i < (nx-1)/2)
# u(i,0) = 1
# else
# u(i,0) = 0
#
# #Iteration
# for n between 0 and nt-1
# for i between 1 and nx-2
# u(i,n+1) = u(i,n)-c*(dt/dx)*(u(i,n)-u(i-1,n)
# In[6]:
def initial_and_boundary_conditions(nx, nt):
# Initialise data structures
import numpy as np
u = np.zeros((nx,nt))
# Boundary conditions
u[0,:] = 1.0
u[nx-1,:] = 0.0
half = int((nx-1)/2)
# Initial conditions
u[0:half,0] = 1.0
return u
# In[88]:
def analytical(sigma, nx, tmax, xmax, c):
# Increments
# dt = tmax/(nt-1)
# dx = (c * dt) / sigma
dx = xmax/(nx-1)
dt = ((dx*sigma) / c)
nt = int(sigma*tmax/dt + 1)
# Initial and Boundary Conditions
u = initial_and_boundary_conditions(nx, nt)
# X Loop
x = np.zeros(nx)
x = np.linspace(0.0,xmax,nx)
i = 1
# Loop - must loop as NumPy cannot be used for dependency reasons
for n in range(0,nt-1):
u[i:nx-1, n+1] = u[i-1:nx-2, n]
return u, x, nt
# In[79]:
def upwind_convection(sigma, nx, tmax, xmax, c):
"""
Returns the velocity field and distance for 1D linear convection
"""
# Increments
dx = xmax/(nx-1)
dt = ((dx * sigma) / c)
nt = int(tmax/dt + 1)
# Initial and Boundary Conditions
u = initial_and_boundary_conditions(nx, nt)
# X Loop
x = np.zeros(nx)
x = np.linspace(0.0,xmax,nx)
i = 1
# Loop - must loop as NumPy cannot be used for dependency reasons
for n in range(0,nt-1):
u[i:nx-1, n+1] = u[i:nx-1, n]-sigma*(u[i:nx-1, n]-u[i-1:nx-2, n])
#u[i:nx-1, n+1] = 0.5*(u[i-1:nx-2, n]+u[i+1:nx, n])-0.5*sigma*(u[i+1:nx, n]-u[i-1:nx-2, n])
return u, x, nt
# In[103]:
def plot(u,x,NT,u_analytical, x_analytical, NT2):
"""
Plots the 1D velocity field
"""
import matplotlib.pyplot as plt
import matplotlib.cm as cm
plt.figure()
ax=plt.subplot(111)
colour=iter(cm.rainbow(np.linspace(0,4,NT)))
for n in range(0,NT,4):
c=next(colour)
ax.plot(x,u[:,n],linestyle='-',c=c,label='n='+str(n))
ax.plot(x_analytical,u_analytical[:,NT2-1],linestyle='--',c='k',label='n='+str(NT2-1)+' analytical')
box=ax.get_position()
ax.set_position([box.x0, box.y0, box.width*2,box.height*2])
ax.legend( bbox_to_anchor=(1.02,1), loc=2)
plt.ylim([-0.5,1.5])
plt.xlim([0.0,5.0])
plt.xlabel('x (m)')
plt.ylabel('u (m/s)')
plt.show()
# In[145]:
u00, x00, nt00 = analytical(1.0, 101, 1.0, 5.0, 1.0)
# In[146]:
u0, x0, nt0 = upwind_convection(1.0, 101, 1.0, 5.0, 1.0)
# In[147]:
u11.shape
# In[148]:
plot(u0,x0,nt0, u00, x00,nt00)
# * If the value of $\sigma = 1$ then the new velocity simply equals the old velocity.
# * The phase angle is zero, so there is no diffusion or dispersion error
# In[149]:
u11, x11, nt11 = analytical(0.5, 101, 1.0, 5.0, 1.0)
# In[150]:
u1, x1, nt1 = upwind_convection(0.5, 101, 1.0, 5.0, 1.0)
# In[151]:
plot(u1,x1,nt1,u11,x11,nt11)
# * The jump is diffused by the numerical diffusion arising from the first order truncation error
# * The amount of diffusion is increasing as we move through time (as n increases)
# * The numerical speed with which the solution moves through the domain is also reduced
# In[152]:
u22, x22, nt22 = analytical(0.25, 101, 1.0, 5.0, 1.0)
# In[153]:
u2, x2, nt2 = upwind_convection(0.25, 101, 1.0, 5.0, 1.0)
# In[154]:
plot(u2,x2,nt2,u22, x22, nt22)
# * Clearly, the smaller Courant Number reduces the speed at which the solution travels through the domain
# * At these very low frequencies, there is not much more added diffusion between $\sigma = 0.50$ and $\sigma = 0.25$
# # Design Algorithm to Solve Problem using Leapfrog Scheme
#
# ## Space-time discretisation:
#
# * i $\rightarrow$ index of grid in x
# * n $\rightarrow$ index of time
#
# ## Lax Friedrichs scheme
#
# * For the **one** first derivative of velocity in time: 2nd order CD in time
# * For the **one** first derivative of velocity in space: 2nd order CD in space
#
# * Use upwind to initialise the leapfrog scheme
#
# ## Discrete equation
#
# $$ {{u_i^{n+1} - u_i^{n-1}} \over {2 \Delta t}} + c {{u_{i-1}^n - u_{i-1}^n} \over {2 \Delta x}}=0 $$
#
# ## Transpose
#
# $$ u_i^{n+1} = u_i^{n-1} - {{c \Delta t} \over \Delta x} (u_{i+1}^n - u_{i-1}^n) $$
#
# ## Pseudo-code
# #Constants
# xmax = 4.0
# nx = 51
# dx = xmax/(nx-1)
# tmax = 1.0
# sigma = 1.0
# dt = (dx / sigma)
# nt = int(tmax/dt + 1)
#
# #Boundary Conditions
# for n between 0 and nt
# u(0,n)=1
# u(nx-1,n)=0
#
# #Initial Conditions
# for i between 1 and nx-2
# if(i < (nx-1)/2)
# u(i,0) = 1
# else
# u(i,0) = 0
#
# #Initialise using Upwind
# n = 0
# for i between 1 and nx-2
# u(i,n+1) = u(i,n)-c*(dt/dx)*(u(i,n)-u(i-1,n)
#
# #Proceed Using Leapfrog
# for n between 1 and nt-1
# for i between 1 and nx-2
# u(i,n+1) = u(i,n-1)-c*(dt/dx)*(u(i+1,n)-u(i-1,n)
# In[126]:
def leapfrog_convection(sigma, nx, tmax, xmax, c):
"""
Returns the velocity field and distance for 1D linear convection
"""
# Increments
dx = xmax/(nx-1)
dt = ((dx * sigma) / c)
nt = int(tmax/dt + 1)
# Initial and Boundary Conditions
u = initial_and_boundary_conditions(nx, nt)
# X Loop
x = np.zeros(nx)
x = np.linspace(0.0,xmax,nx)
# Initialise using Upwind
n = 0
i = 1
u[i:nx-1, n+1] = u[i:nx-1, n]-sigma*(u[i:nx-1, n]-u[i-1:nx-2, n])
# Proceed using Leapfrog
for n in range(1, nt-1):
u[i:nx-1, n+1] = u[i:nx-1, n-1]-sigma*(u[i+1:nx, n]-u[i-1:nx-2, n])
return u, x, nt
# In[155]:
u44, x44, nt44 = analytical(0.5, 101, 1.0, 5.0, 1.0)
# In[156]:
u4, x4, nt4 = leapfrog_convection(0.5, 101, 1.0, 5.0, 1.0)
# In[157]:
plot(u4,x4,nt4,u44, x44, nt44)
# Oscillatory, behind the wave, more accurate than Lax-Friedrichs
# # Design Algorithm to Solve Problem using Lax Friedrichs Scheme
#
# ## Space-time discretisation:
#
# * i $\rightarrow$ index of grid in x
# * n $\rightarrow$ index of time
#
# ## Lax Friedrichs scheme
#
# $$ u_i^{n+1} = {1 \over 2} (u_{i-1}^n + u_{i+1}^n) - {\sigma \over 2}(u_{i+1}^n - u_{i-1}^n) $$
#
# ## Pseudo-code
# #Constants
# xmax = 4.0
# nx = 51
# dx = xmax/(nx-1)
# tmax = 1.0
# sigma = 1.0
# dt = (dx / sigma)
# nt = int(tmax/dt + 1)
#
# #Boundary Conditions
# for n between 0 and nt
# u(0,n)=1
# u(nx-1,n)=0
#
# #Initial Conditions
# for i between 1 and nx-2
# if(i < (nx-1)/2)
# u(i,0) = 1
# else
# u(i,0) = 0
#
# #Iteration
# for n between 0 and nt-1
# for i between 1 and nx-2
# u(i,n+1) = 0.5*(u(i-1,n)+u(i+1,n))-0.5*sigma*(u(i+1,n)-u(i-1,n)
# In[132]:
def lax_friedrichs_convection(sigma, nx, tmax, xmax, c):
"""
Returns the velocity field and distance for 1D linear convection
"""
# Increments
dx = xmax/(nx-1)
dt = ((dx * sigma) / c)
nt = int(tmax/dt + 1)
# Initial and Boundary Conditions
u = initial_and_boundary_conditions(nx, nt)
# X Loop
x = np.zeros(nx)
x = np.linspace(0.0,xmax,nx)
i = 1
# Loop - must loop as NumPy cannot be used for dependency reasons
for n in range(0,nt-1):
u[i:nx-1, n+1] = 0.5*(u[i-1:nx-2, n]+u[i+1:nx, n])-0.5*sigma*(u[i+1:nx, n]-u[i-1:nx-2, n])
return u, x, nt
# In[158]:
u55, x55, nt55 = analytical(0.5, 101, 1.0, 5.0, 1.0)
# In[159]:
u5, x5, nt5 = lax_friedrichs_convection(0.5, 101, 1.0, 5.0, 1.0)
# In[160]:
plot(u5,x5,nt5,u55, x55, nt55)
# * Numerical dissipation and odd-even decoupling
# * Amount of diffusion is still increasing with increasing n
# # Design Algorithm to Solve Problem using Lax Wendroff Scheme
#
# ## Space-time discretisation:
#
# * i $\rightarrow$ index of grid in x
# * n $\rightarrow$ index of time
#
# ## Lax Wendroff scheme
#
# $$ u_i^{n+1} = u_i^n - {\sigma \over 2}(u_{i+1}^n - u_{i-1}^n) + {\sigma^2 \over 2} (u_{i+1}^n - 2u_i^n + u_{i-1}^n) $$
# In[138]:
def lax_wendroff_convection(sigma, nx, tmax, xmax, c):
"""
Returns the velocity field and distance for 1D linear convection
"""
# Increments
dx = xmax/(nx-1)
dt = ((dx * sigma) / c)
nt = int(tmax/dt + 1)
# Initial and Boundary Conditions
u = initial_and_boundary_conditions(nx, nt)
# X Loop
x = np.zeros(nx)
x = np.linspace(0.0,xmax,nx)
i = 1
# Loop - must loop as NumPy cannot be used for dependency reasons
for n in range(0,nt-1):
u[i:nx-1, n+1] = (u[i:nx-1, n] - 0.5*sigma*(u[i+1:nx, n] - u[i-1:nx-2, n]) +
0.5*(sigma**2)*(u[i+1:nx, n] - 2.0*u[i:nx-1, n] + u[i-1:nx-2, n]))
return u, x, nt
# In[161]:
u66, x66, nt66 = analytical(0.5, 101, 1.0, 5.0, 1.0)
# In[162]:
u6, x6, nt6 = lax_wendroff_convection(0.5, 101, 1.0, 5.0, 1.0)
# In[163]:
plot(u6,x6,nt6,u66, x66, nt66)
# * This more accurately represents the step change
# * However, there is an oscillatory response
# In[ ]: