Stability, Consistency, Stiffness

Stability

A finite difference solution of an ODE is stable if it produces a bounded solution for a stable ODE.

instability

Explicit Euler

To avoid instability, we need $|y_{n+1}/y_n|\le 1$, so

$$|1-\alpha\Delta t|\le 1,$$$$-1\le 1-\alpha\Delta t\le 1.$$ $$\Delta t \le\frac{2}{\alpha}.$$

Implicit Euler

$$y_{n+1} = y_n - \Delta t\alpha y_{n+1},$$$$\frac{y_{n+1}}{y_n} = \frac{1}{1+\alpha\Delta t} \le 1$$
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
from scipy.optimize import fsolve

def odeEE(f, y0, t):
    ns = len(t)-1
    y = np.zeros(ns+1)
    y[0] = y0
    
    for k in range(ns):
        h = t[k+1]-t[k]
        y[k+1] = y[k] + h*f(y[k],t[k])
    
    return y
#---------------------------

def odeIE(f, y0, t):
    ns = len(t)-1
    y = np.zeros(ns+1)
    y[0] = y0
    
    def F(ynext):
        return ynext - y[k] - h*f(ynext, t[k+1])
    
    for k in range(ns):
        h = t[k+1]-t[k]
        y[k+1] = fsolve(F, y[k-1])[0]
    
    return y
α=1
def f(y, t):
    return -α*y
#---------------------------
y0 = 1
tend = 10
Δt = 2.0/α * 1.2
t  = np.arange(0,tend+Δt, Δt)

yex = y0*np.exp(-t)
yee = odeEE(f, y0, t)
yie = odeIE(f, y0, t)
#---------------------------
plt.rc('font', size=14)
plt.plot(t,yex, 'k:'); plt.plot(t,yee); plt.plot(t,yie)
plt.xlabel('t'); plt.ylabel('y')
plt.legend(['exact', 'EE', 'IE'], frameon=False);
EE IE

Multi-dimensional system of ODEs

$$\frac{d\vec{y}}{dt} = \vec{F}(\vec{y}).$$

* Let $\vec{F}({\vec{y}})$ be arbitrary (nonlinear). * Linearize (and drop the arrow notation for simplicity):

$$\frac{dy}{dt} = F(y_n) + [J]_n(\Delta y)_n.$$

* Here, $J$ is the Jacobian and $\Delta y=y-y_n$.

$$\frac{d\hat{\Delta y}_n}{dt} = \Lambda \hat{\Delta y}_n + \hat{F}_n.$$ $$\Delta t \le -\frac{2}{\Lambda_{i,min}}.$$

Consistency

Explicit Euler

Stiff ODEs

Example

$$\frac{dy}{dt} = f(y,t) = -\alpha(y-F(t)) + F^{\prime}(t).$$ $$y=(y_0 - F(0))e^{-\alpha t} + F(t).$$ $$y(t) = -2e^{-1000t} + t + 2.$$
t = np.linspace(0,5,10000)
y = -2*np.exp(-1000*t) + t + 2

plt.subplot(2,1,1)
plt.plot(t,y)
plt.xlim([0,5])
plt.ylabel('y'); plt.xlabel('t')
plt.subplot(2,1,2)
plt.plot(t,y)
plt.xlim([0,0.005])
plt.ylim([0,2.5]); plt.ylabel('y'); plt.xlabel('t')
plt.tight_layout()
stiff

Solve with Explicit Euler

def f(y,t):
    alpha = 100
    return -alpha*(y-t-2.0) + 1.0

y0   = 0            # try with 0 and 2
tend = 4.5

tEX  = np.linspace(0,tend,10000)
yEX = -2*np.exp(-100*tEX) + tEX + 2
fac = 1.1              # try values: 0.1, 0.5, 0.9, 1.1
dt = 2.0/100*fac        # 2/alpha is the stability limit, * a factor to adjust up or down
t   = np.arange(0,tend+dt, dt)

yEE = odeEE(f, y0, t)

#----------------------
print(f'n steps = {len(t)-1}')

plt.subplot(2,1,1)
plt.plot(tEX,yEX); plt.plot(t,  yEE)
plt.subplot(2,1,2)
plt.plot(tEX,yEX); plt.plot(t,  yEE)
plt.xlim([0,0.05])
plt.tight_layout();
n steps = 205
stiff2

What can be done?

Solve with Implicit Euler

fac = 1.1               # try values: 0.1, 0.5, 0.9, 1.1, 10
dt = 2.0/100*fac        
t   = np.arange(0,tend+dt, dt)

yIE = odeIE(f, y0, t)

#----------------------
print(f'n steps = {len(t)-1}')

plt.subplot(2,1,1)
plt.plot(tEX,yEX); plt.plot(t,  yIE)
plt.subplot(2,1,2)
plt.plot(tEX,yEX); plt.plot(t,  yIE)
plt.xlim([0,0.05])
plt.tight_layout();
n steps = 205
stiff3