Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Part 4: Stability Analysis

Module 3: ODE Methods & Conservation | COMP 521

Learning Objectives

By the end of this section, you will be able to:


Linear Stability Theory

To understand when methods fail catastrophically, we analyze their behavior on the test equation:

dydt=λy\frac{dy}{dt} = \lambda y

where λ∈C\lambda \in \mathbb{C}. The true solution is y(t)=y0eλty(t) = y_0 e^{\lambda t}.

Stability function: The amplification factor R(z)R(z) relating consecutive numerical solution values: yn+1=R(hλ)yny_{n+1} = R(h\lambda)y_n.

When we apply a numerical method, we get:

yn+1=R(hλ)yny_{n+1} = R(h\lambda) y_n

where R(z)R(z) is the stability function with z=hλz = h\lambda.

Stability region: The set of complex values z=hλz = h\lambda for which ∣R(z)∣≤1|R(z)| \leq 1, ensuring bounded solutions.

For bounded solutions, we need ∣R(hλ)∣≤1|R(h\lambda)| \leq 1. The stability region is the set of zz values where this holds.

Stability Functions for Common Methods

Let’s derive the stability functions for our methods:

Euler’s Method

Applying Euler to y′=λyy' = \lambda y:

yn+1=yn+hλyn=(1+hλ)yny_{n+1} = y_n + h\lambda y_n = (1 + h\lambda)y_n

Therefore: R(z)=1+z\boxed{R(z) = 1 + z}

This is stable when ∣1+z∣≤1|1 + z| \leq 1, which defines a circle of radius 1 centered at z=−1z = -1.

RK2 (Midpoint)

Following through the RK2 steps:

k1=λynk_1 = \lambda y_n

k2=λ(yn+h2k1)=λyn(1+z2)k_2 = \lambda(y_n + \frac{h}{2}k_1) = \lambda y_n(1 + \frac{z}{2})

yn+1=yn+hk2=yn(1+z+z22)y_{n+1} = y_n + hk_2 = y_n(1 + z + \frac{z^2}{2})

Therefore: R(z)=1+z+z22\boxed{R(z) = 1 + z + \frac{z^2}{2}}

RK4

Through similar analysis (but more algebra):

R(z)=1+z+z22+z36+z424\boxed{R(z) = 1 + z + \frac{z^2}{2} + \frac{z^3}{6} + \frac{z^4}{24}}

For this linear test equation, it is exactly the degree-four Taylor polynomial of eze^z. That does not make the general state-dependent RK4 update a scalar exponential polynomial.

Shaded stability regions show forward Euler's disk, RK4's bounded region, and backward Euler's exterior region including the left half-plane.

Shading marks ∣R(z)∣≤1|R(z)|\le1 for the scalar test equation. Forward Euler’s region touches the imaginary axis only at zero. Backward Euler covers the left half-plane but can still introduce substantial artificial damping.

Open full-size figure

Physical Interpretation

For different types of problems, we need different stability characteristics:

Oscillatory Systems (Circular Orbits)

For a harmonic oscillator with frequency ω\omega:

Dissipative Systems (Damped Motion)

For exponential decay with rate γ\gamma:

Stiff Equations - When Stability Dominates

Stiff equation: An ODE where stability requirements force much smaller timesteps than accuracy requirements. Named because they arise in stiff mechanical systems.

What Makes an Equation Stiff?

Widely separated timescales are a common source of stiffness, not a necessary definition. After fast transients decay, we may be tracking slow evolution while an explicit method still requires tiny timesteps for stability. The comparison is between the step needed for accuracy on the interval of interest and the step allowed by the chosen method’s stability region.

Understanding Stiffness Through Examples

Example 1: Chemical Kinetics

Consider a reaction where species A slowly converts to B, but B rapidly equilibrates:

dAdt=−0.04A\frac{dA}{dt} = -0.04A
dBdt=0.04A−104B\frac{dB}{dt} = 0.04A - 10^4 B

The eigenvalues are λ1=−0.04\lambda_1 = -0.04 (slow) and λ2=−104\lambda_2 = -10^4 (fast).

The fast transient decays as e−104te^{-10^4t}; after t=0.001t=0.001 its amplitude is about 4.5×10−54.5\times10^{-5} of its initial value. For initial data and error targets that make this remnant negligible, BB then follows the slowly decaying AA. Forward Euler still needs h<2/104=0.0002h<2/10^4=0.0002 to damp the fast numerical mode, even when the slow evolution could meet the accuracy target with a much larger step.

Example 2: Stellar Interior with Radiative Cooling

A thermal model can contain a cooling relaxation timescale much shorter than the duration of interest. For a stated toy model T′=A−BT4T'=A-BT^4, linearization about Teq=(A/B)1/4T_{\rm eq}=(A/B)^{1/4} gives

δT′=−4BTeq3δT,τrelax=14BTeq3.\delta T'=-4BT_{\rm eq}^3\delta T,\qquad \tau_{\rm relax}=\frac{1}{4BT_{\rm eq}^3}.

An explicit method may need to resolve this fast relaxation even while the equilibrium changes slowly. The T4T^4 law here is a pedagogical cooling model, not a universal prescription for stellar shocks; real cooling depends on opacity, density, ionization, and radiation transport.

Non-stiff equation: An ODE where accuracy requirements determine the timestep, with stability easily satisfied. Most orbital mechanics problems are non-stiff.

Non-Stiff vs Stiff: The Key Distinction

Non-stiff problems, for the chosen method and interval, allow accuracy to set the step without a much stricter stability restriction. Similar eigenvalue magnitudes are not a sufficient definition; even the scalar equation y′=−1000yy'=-1000y can be stiff over a long interval after its transient decays. Examples:

Widely separated decay rates are one common source of stiffness. The timestep can be forced by stability of the fastest mode even after it has decayed; a scalar fast-decay problem can also be stiff over a long interval. Examples:

Implicit Methods for Stiff Problems

Implicit method: A numerical scheme where the unknown appears on both sides of the equation, requiring solution of an algebraic system at each step.

Implicit methods evaluate the derivative at the future point, giving superior stability at the cost of solving equations.

Backward Euler

Instead of yn+1=yn+hf(yn,tn)y_{n+1} = y_n + hf(y_n, t_n), we use:

yn+1=yn+hf(yn+1,tn+1)\boxed{y_{n+1} = y_n + hf(y_{n+1}, t_{n+1})}

This is implicit because yn+1y_{n+1} appears on both sides!

Stability Analysis

For the test equation y′=λyy' = \lambda y:

yn+1=yn+hλyn+1y_{n+1} = y_n + h\lambda y_{n+1}

yn+1(1−hλ)=yny_{n+1}(1 - h\lambda) = y_n

yn+1=yn1−hλy_{n+1} = \frac{y_n}{1 - h\lambda}

Stability function: R(z)=11−z\boxed{R(z) = \frac{1}{1-z}}

This is stable for the entire left half-plane! We call this A-stable.

A limiting check: for y′=−1000yy'=-1000y with h=0.01h=0.01, forward Euler’s factor is -9, while backward Euler’s is 1/111/11. The exact factor is e−10e^{-10}. The implicit result is bounded and decays, but one step still does not resolve the actual fast transient accurately.

Solving the Implicit Equation

The challenge: we must solve yn+1=yn+hf(yn+1,tn+1)y_{n+1} = y_n + hf(y_{n+1}, t_{n+1}) for yn+1y_{n+1}.

For nonlinear ff, this requires iteration. Newton’s method is typical:

import numpy as np

def backward_euler_step(y, t, h, f, df_dy, tol=1e-10):
    """
    Single backward Euler step
    Requires a one-dimensional state array and its square Jacobian df/dy.
    tol bounds the algebraic residual norm, not the time-integration error.
    """
    y = np.asarray(y, dtype=float)
    y_new = y.copy()  # Initial guess
    
    for _ in range(10):  # Newton iterations
        # Residual: R(y_new) = y_new - y - h*f(y_new, t+h)
        residual = y_new - y - h*f(y_new, t + h)
        
        if np.linalg.norm(residual) < tol:
            return y_new
        
        # Jacobian of residual
        J = np.eye(len(y)) - h*df_dy(y_new, t + h)
        
        # Newton update
        delta = np.linalg.solve(J, -residual)
        y_new = y_new + delta
    
    raise RuntimeError("Newton iteration failed to converge")

Implicit Midpoint Rule

A second-order implicit method:

yn+1=yn+hf(yn+yn+12,tn+h2)y_{n+1} = y_n + hf\left(\frac{y_n + y_{n+1}}{2}, t_n + \frac{h}{2}\right)

Implicit midpoint is A-stable and symplectic for canonical Hamiltonian systems when the implicit solve is exact. It is not L-stable: its amplification tends to -1 for very stiff decay, so it can retain alternating unresolved transients. A-stability does not guarantee good damping or accuracy, and a loose nonlinear solve can spoil the structure.

The CFL Condition

CFL condition: Courant-Friedrichs-Lewy condition - the numerical domain of dependence must contain the physical one; its numerical coefficient depends on the scheme. Named after the mathematicians who discovered it in 1928.

The CFL condition is a necessary stability criterion for hyperbolic PDEs that also applies to ODEs from spatial discretization:

h≤CCFLΔxcmaxh \leq C_{\rm CFL}\frac{\Delta x}{c_{max}}

where Δx\Delta x is the spatial grid spacing, cmaxc_{max} is the maximum wave speed, and CCFLC_{\rm CFL} depends on the spatial and temporal discretizations and dimension. A CFL bound alone is not a sufficient stability proof.

For the wave equation ∂2u∂t2=c2∂2u∂x2\frac{\partial^2 u}{\partial t^2} = c^2 \frac{\partial^2 u}{\partial x^2} discretized in space:

This means finer spatial grids require smaller timesteps!

Diagnosing Integration Problems

Systematic Debugging Approach

When your integration fails, follow this diagnostic sequence:

  1. Check for NaN/Inf: Could indicate instability, an invalid function domain, division by zero, or overflow

  2. Plot the solution: Look for oscillations growing exponentially

  3. Compute eigenvalues: Estimate λmax\lambda_{max}

  4. Check stability region: Do all relevant complex values hλjh\lambda_j lie inside it? For non-normal systems, eigenvalues alone can miss transient amplification.

  5. Test with smaller h: Does halving h fix the problem?

  6. Monitor energy: For conservative systems, is energy bounded?

Stability Test Code

import numpy as np

def stability_test(method, lambda_val, h_values):
    """Flag non-finite or large finite-run outputs; not a stability proof."""
    results = []
    
    for h in h_values:
        y = 1.0  # Initial condition
        stable = True
        
        for n in range(1000):
            y = method(y, lambda_val, h)
            
            if not np.isfinite(y) or abs(y) > 1e10:  # Failure of this finite-run check
                stable = False
                break
        
        results.append((h, stable, abs(y)))
    
    return results

A True flag means only that this run stayed finite and below the stated threshold for 1000 steps. Slow growth can pass that check. Use the analytical ∣R(hλ)∣|R(h\lambda)| condition for this test equation and report the finite-run diagnostic separately. A nan result is always a failure of this check.


Bridge to the synthesis: choose and verify a method

Stability identifies restrictions that an error tolerance alone cannot enforce. The synthesis brings together step size, accuracy, conservation, and cost. Before optimizing a solver, establish a reference problem and a diagnostic that would reveal the failure mode you care about. A faster unstable solution is still an incorrect calculation; a stable result can still have unacceptable phase or trajectory error.

Next: Summary and Synthesis