Chapter 4: equation
This chapter covers equation. You will learn essential concepts and techniques.
Numerical simulation of dynamical systems with time evolution
4.1 Fundamentals of Ordinary Differential equations
equation(ODE: Ordinary Differential equation) 、time evolution do mathematical model and 、materials science、chemical reaction kinetics、heat conduction、population dynamics etc.widefield 。initial value problem \( dy/dt = f(t, y) \), \( y(t_0) = y_0 \) solvemethod。
📚 Theory: equation classification
initial value problem (IVP: Initial Value Problem):
\[ \frac{dy}{dt} = f(t, y), \quad y(t_0) = y_0 \]\( t_0 \) \( y_0 \) 、 \( t \) \( y(t) \) 。
classification:
- single-step method: 1 use(Euler、Runge-Kutta)
- multistep method: multiple past use(Adams)
- method: \( y_{n+1} \) can be obtained(calculation simple、Stiffproblem unsuitable)
- method: \( y_{n+1} \) Solution of the equation and can be obtained(stable、Stiffproblem suitable)
1: forward Euler method implementation and erroranalysis
import numpy as np import matplotlib.pyplot as plt def forward_euler(f, t_span, y0, h): """ forward Euler method ODE solution Parameters: ----------- f : callable function f(t, y) t_span : tuple interval (t0, tf) y0 : float or array initial value h : float time step size Returns: -------- t : ndarray y : ndarray """ t0, tf = t_span t = np.arange(t0, tf + h, h) n = len(t) # y y0 = np.atleast_1d(y0) y = np.zeros((n, len(y0))) y[0] = y0 for i in range(n - 1): y[i + 1] = y[i] + h * f(t[i], y[i]) # 1 and if len(y0) == 1: y = y.flatten() return t, y # problem: dy/dt = -y, y(0) = 1 # Solution: y(t) = exp(-t) def f_exponential(t, y): return -y def y_exact(t): return np.exp(-t) print("=" * 60) print("forward Euler method: dy/dt = -y, y(0) = 1") print("=" * 60) t_span = (0, 5) y0 = 1.0 # calculation step_sizes = [0.5, 0.25, 0.1, 0.05] fig, axes = plt.subplots(2, 2, figsize=(14, 10)) axes = axes.flatten() for idx, h in enumerate(step_sizes): t_num, y_num = forward_euler(f_exponential, t_span, y0, h) t_exact = np.linspace(0, 5, 200) y_exact_vals = y_exact(t_exact) ax = axes[idx] ax.plot(t_exact, y_exact_vals, 'b-', linewidth=2, label='') ax.plot(t_num, y_num, 'ro-', markersize=6, linewidth=2, label=f'Euler (h={h})') ax.set_xlabel(' t', fontsize=11) ax.set_ylabel('y(t)', fontsize=11) ax.set_title(f' h = {h}', fontsize=12) ax.legend(fontsize=10) ax.grid(True, alpha=0.3) # error calculation y_exact_at_t = y_exact(t_num) error = np.abs(y_num - y_exact_at_t) print(f"\nh = {h}:") print(f" : {len(t_num)}") print(f" error: {error[-1]:.6f}") print(f" error: {np.max(error):.6f}") plt.tight_layout() plt.savefig('forward_euler_convergence.png', dpi=150, bbox_inches='tight') plt.show() # convergence analysis print("\n" + "=" * 60) print("convergence analysis") print("=" * 60) h_values = np.array([0.5, 0.25, 0.1, 0.05, 0.02, 0.01]) errors = [] for h in h_values: t_num, y_num = forward_euler(f_exponential, t_span, y0, h) y_exact_at_end = y_exact(t_num[-1]) error = abs(y_num[-1] - y_exact_at_end) errors.append(error) errors = np.array(errors) # convergence print("\n error convergence") print("-" * 45) for i, (h, err) in enumerate(zip(h_values, errors)): if i > 0: rate = np.log(errors[i-1] / err) / np.log(h_values[i-1] / h) print(f"{h:6.3f} {err:.6e} {rate:.2f}") else: print(f"{h:6.3f} {err:.6e} -") # convergence 1(O(h)) plt.figure(figsize=(10, 6)) plt.loglog(h_values, errors, 'o-', linewidth=2, markersize=8, label=' error') plt.loglog(h_values, h_values, '--', linewidth=2, label='O(h) ', alpha=0.5) plt.xlabel(' h', fontsize=12) plt.ylabel('error', fontsize=12) plt.title('forward Euler method convergence(: O(h))', fontsize=14) plt.legend(fontsize=11) plt.grid(True, alpha=0.3) plt.tight_layout() plt.savefig('forward_euler_convergence_rate.png', dpi=150, bbox_inches='tight') plt.show() print(f"\nconvergence: {np.mean([np.log(errors[i-1]/errors[i])/np.log(h_values[i-1]/h_values[i]) for i in range(1, len(errors))]):.2f}") print("convergence: 1.0 (O(h))") 4.2 Backward Euler and Improved Euler Methods
backward Euler method method 、stable 。improved Euler method(Heun ) 2 calculation 。
📚 Theory: Euler
backward Euler method():
\[ y_{n+1} = y_n + h f(t_{n+1}, y_{n+1}) \]\( y_{n+1} \) 、iterationNonlinear equation Solution Methodsis required 。Stiffequation stable 。
improved Euler method(Heun ):
\[ \begin{aligned} k_1 &= f(t_n, y_n) \\ k_2 &= f(t_n + h, y_n + h k_1) \\ y_{n+1} &= y_n + \frac{h}{2}(k_1 + k_2) \end{aligned} \]and 、error \( O(h^3) \)、error \( O(h^2) \) 。
2: backward Euler method implementation(method)
from scipy.optimize import fsolve def backward_euler(f, t_span, y0, h, max_iter=10, tol=1e-10): """ backward Euler method ODE solution(method) Parameters: ----------- f : callable function f(t, y) t_span : tuple interval (t0, tf) y0 : float or array initial value h : float time step size max_iter : int Maximum iterations(Nonlinear equation Solution Methods) tol : float convergence Returns: -------- t : ndarray y : ndarray """ t0, tf = t_span t = np.arange(t0, tf + h, h) n = len(t) y0 = np.atleast_1d(y0) y = np.zeros((n, len(y0))) y[0] = y0 for i in range(n - 1): # backward Euler method equation: y_{n+1} - y_n - h * f(t_{n+1}, y_{n+1}) = 0 def implicit_eq(y_next): return y_next - y[i] - h * f(t[i + 1], y_next) # Nonlinear equationSolve( forward Euler method) y_guess = y[i] + h * f(t[i], y[i]) y[i + 1] = fsolve(implicit_eq, y_guess) if len(y0) == 1: y = y.flatten() return t, y # Test: dy/dt = -10y, y(0) = 1 (Stiffequation ) def f_stiff(t, y): return -10 * y def y_exact_stiff(t): return np.exp(-10 * t) print("=" * 60) print("backward Euler method vs forward Euler method (Stiffproblem)") print("dy/dt = -10y, y(0) = 1") print("=" * 60) t_span = (0, 2) y0 = 1.0 h = 0.25 # forward Euler method t_forward, y_forward = forward_euler(f_stiff, t_span, y0, h) # backward Euler method t_backward, y_backward = backward_euler(f_stiff, t_span, y0, h) # t_exact = np.linspace(0, 2, 200) y_exact_vals = y_exact_stiff(t_exact) # plt.figure(figsize=(12, 5)) # : plt.subplot(1, 2, 1) plt.plot(t_exact, y_exact_vals, 'b-', linewidth=2, label='') plt.plot(t_forward, y_forward, 'ro-', markersize=6, linewidth=2, label='forward Euler method') plt.plot(t_backward, y_backward, 'gs-', markersize=6, linewidth=2, label='backward Euler method') plt.xlabel(' t', fontsize=12) plt.ylabel('y(t)', fontsize=12) plt.title(f'Stiffequation (h={h})', fontsize=14) plt.legend(fontsize=11) plt.grid(True, alpha=0.3) # : error plt.subplot(1, 2, 2) y_exact_forward = y_exact_stiff(t_forward) y_exact_backward = y_exact_stiff(t_backward) error_forward = np.abs(y_forward - y_exact_forward) error_backward = np.abs(y_backward - y_exact_backward) plt.semilogy(t_forward, error_forward, 'ro-', markersize=6, linewidth=2, label='forward Euler method') plt.semilogy(t_backward, error_backward, 'gs-', markersize=6, linewidth=2, label='backward Euler method') plt.xlabel(' t', fontsize=12) plt.ylabel('error', fontsize=12) plt.title('error ', fontsize=14) plt.legend(fontsize=11) plt.grid(True, alpha=0.3) plt.tight_layout() plt.savefig('backward_euler_stiff.png', dpi=150, bbox_inches='tight') plt.show() print(f"\n error:") print(f" forward Euler method: {error_forward[-1]:.6e}") print(f" backward Euler method: {error_backward[-1]:.6e}") print(f"\nbackward Euler method method Stiffproblem stable") 3: improved Euler method(Heun )
def improved_euler(f, t_span, y0, h): """ improved Euler method(Heun ) ODE solution Parameters: ----------- f : callable function f(t, y) t_span : tuple interval (t0, tf) y0 : float or array initial value h : float time step size Returns: -------- t : ndarray y : ndarray """ t0, tf = t_span t = np.arange(t0, tf + h, h) n = len(t) y0 = np.atleast_1d(y0) y = np.zeros((n, len(y0))) y[0] = y0 for i in range(n - 1): # 1: Euler k1 = f(t[i], y[i]) # 2: calculation k2 = f(t[i + 1], y[i] + h * k1) # use y[i + 1] = y[i] + h * (k1 + k2) / 2 if len(y0) == 1: y = y.flatten() return t, y # 3 method print("=" * 60) print("Euler : dy/dt = -y, y(0) = 1") print("=" * 60) t_span = (0, 5) y0 = 1.0 h = 0.2 # method solve t_forward, y_forward = forward_euler(f_exponential, t_span, y0, h) t_improved, y_improved = improved_euler(f_exponential, t_span, y0, h) # t_exact = np.linspace(0, 5, 200) y_exact_vals = y_exact(t_exact) # fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(14, 5)) # : ax1.plot(t_exact, y_exact_vals, 'b-', linewidth=2, label='') ax1.plot(t_forward, y_forward, 'ro-', markersize=6, linewidth=2, label='forward Euler method') ax1.plot(t_improved, y_improved, 'gs-', markersize=6, linewidth=2, label='improved Euler method') ax1.set_xlabel(' t', fontsize=12) ax1.set_ylabel('y(t)', fontsize=12) ax1.set_title(f' (h={h})', fontsize=14) ax1.legend(fontsize=11) ax1.grid(True, alpha=0.3) # : error y_exact_at_t = y_exact(t_forward) error_forward = np.abs(y_forward - y_exact_at_t) error_improved = np.abs(y_improved - y_exact_at_t) ax2.semilogy(t_forward, error_forward, 'ro-', markersize=6, linewidth=2, label='forward Euler method (O(h))') ax2.semilogy(t_improved, error_improved, 'gs-', markersize=6, linewidth=2, label='improved Euler method (O(h²))') ax2.set_xlabel(' t', fontsize=12) ax2.set_ylabel('error', fontsize=12) ax2.set_title('error ', fontsize=14) ax2.legend(fontsize=11) ax2.grid(True, alpha=0.3) plt.tight_layout() plt.savefig('improved_euler_comparison.png', dpi=150, bbox_inches='tight') plt.show() print(f"\n t={t_forward[-1]} error:") print(f" forward Euler method: {error_forward[-1]:.6e}") print(f" improved Euler method: {error_improved[-1]:.6e}") print(f" error: {error_forward[-1]/error_improved[-1]:.2f}times") 4.3 Runge-Kutta Methods
Runge-Kutta(RK) 、single-step method widely usedmethod 。multiple calculation、。
📚 Theory: Runge-Kutta
2Runge-Kutta(RK2):
\[ \begin{aligned} k_1 &= f(t_n, y_n) \\ k_2 &= f(t_n + \frac{h}{2}, y_n + \frac{h}{2} k_1) \\ y_{n+1} &= y_n + h k_2 \end{aligned} \]use、error \( O(h^3) \)、error \( O(h^2) \) 。
4Runge-Kutta(RK4):
\[ \begin{aligned} k_1 &= f(t_n, y_n) \\ k_2 &= f(t_n + \frac{h}{2}, y_n + \frac{h}{2} k_1) \\ k_3 &= f(t_n + \frac{h}{2}, y_n + \frac{h}{2} k_2) \\ k_4 &= f(t_n + h, y_n + h k_3) \\ y_{n+1} &= y_n + \frac{h}{6}(k_1 + 2k_2 + 2k_3 + k_4) \end{aligned} \]RK。error \( O(h^5) \)、error \( O(h^4) \) 。
4: Runge-Kutta 2(RK2)
def rk2(f, t_span, y0, h): """ 2Runge-Kutta() ODE solution Parameters: ----------- f : callable function f(t, y) t_span : tuple interval (t0, tf) y0 : float or array initial value h : float time step size Returns: -------- t : ndarray y : ndarray """ t0, tf = t_span t = np.arange(t0, tf + h, h) n = len(t) y0 = np.atleast_1d(y0) y = np.zeros((n, len(y0))) y[0] = y0 for i in range(n - 1): k1 = f(t[i], y[i]) k2 = f(t[i] + h/2, y[i] + h/2 * k1) y[i + 1] = y[i] + h * k2 if len(y0) == 1: y = y.flatten() return t, y # print("=" * 60) print("Runge-Kutta 2") print("=" * 60) t_span = (0, 5) y0 = 1.0 h = 0.5 t_rk2, y_rk2 = rk2(f_exponential, t_span, y0, h) t_exact = np.linspace(0, 5, 200) y_exact_vals = y_exact(t_exact) plt.figure(figsize=(10, 6)) plt.plot(t_exact, y_exact_vals, 'b-', linewidth=2, label='') plt.plot(t_rk2, y_rk2, 'ro-', markersize=8, linewidth=2, label=f'RK2 (h={h})') plt.xlabel(' t', fontsize=12) plt.ylabel('y(t)', fontsize=12) plt.title('2Runge-Kutta', fontsize=14) plt.legend(fontsize=11) plt.grid(True, alpha=0.3) plt.tight_layout() plt.savefig('rk2_solution.png', dpi=150, bbox_inches='tight') plt.show() y_exact_at_t = y_exact(t_rk2) error_rk2 = np.abs(y_rk2 - y_exact_at_t) print(f"\n error: {error_rk2[-1]:.6e}") 5: Runge-Kutta 4(RK4)
def rk4(f, t_span, y0, h): """ 4Runge-Kutta(RK4) ODE solution Parameters: ----------- f : callable function f(t, y) t_span : tuple interval (t0, tf) y0 : float or array initial value h : float time step size Returns: -------- t : ndarray y : ndarray """ t0, tf = t_span t = np.arange(t0, tf + h, h) n = len(t) y0 = np.atleast_1d(y0) y = np.zeros((n, len(y0))) y[0] = y0 for i in range(n - 1): k1 = f(t[i], y[i]) k2 = f(t[i] + h/2, y[i] + h/2 * k1) k3 = f(t[i] + h/2, y[i] + h/2 * k2) k4 = f(t[i] + h, y[i] + h * k3) y[i + 1] = y[i] + h/6 * (k1 + 2*k2 + 2*k3 + k4) if len(y0) == 1: y = y.flatten() return t, y # method print("=" * 60) print(" ") print("=" * 60) h_values = np.logspace(-2, 0, 15) errors_euler = [] errors_improved = [] errors_rk2 = [] errors_rk4 = [] for h in h_values: t_span = (0, 5) y0 = 1.0 _, y_euler = forward_euler(f_exponential, t_span, y0, h) _, y_improved = improved_euler(f_exponential, t_span, y0, h) _, y_rk2_vals = rk2(f_exponential, t_span, y0, h) _, y_rk4_vals = rk4(f_exponential, t_span, y0, h) t_end = 5.0 y_exact_end = y_exact(t_end) errors_euler.append(abs(y_euler[-1] - y_exact_end)) errors_improved.append(abs(y_improved[-1] - y_exact_end)) errors_rk2.append(abs(y_rk2_vals[-1] - y_exact_end)) errors_rk4.append(abs(y_rk4_vals[-1] - y_exact_end)) # plt.figure(figsize=(12, 6)) plt.loglog(h_values, errors_euler, 'o-', linewidth=2, markersize=6, label='Euler (O(h))') plt.loglog(h_values, errors_improved, 's-', linewidth=2, markersize=6, label='Euler (O(h²))') plt.loglog(h_values, errors_rk2, '^-', linewidth=2, markersize=6, label='RK2 (O(h²))') plt.loglog(h_values, errors_rk4, 'v-', linewidth=2, markersize=6, label='RK4 (O(h⁴))') # plt.loglog(h_values, h_values, '--', color='gray', alpha=0.5, label='O(h)') plt.loglog(h_values, h_values**2, '--', color='gray', alpha=0.5, label='O(h²)') plt.loglog(h_values, h_values**4, '--', color='gray', alpha=0.5, label='O(h⁴)') plt.xlabel(' h', fontsize=12) plt.ylabel('error (t=5 )', fontsize=12) plt.title('method convergence', fontsize=14) plt.legend(fontsize=10) plt.grid(True, alpha=0.3) plt.tight_layout() plt.savefig('ode_methods_comparison.png', dpi=150, bbox_inches='tight') plt.show() print("\n h=0.1 error:") h_test = 0.1 idx = np.argmin(np.abs(h_values - h_test)) print(f" Euler: {errors_euler[idx]:.2e}") print(f" Euler: {errors_improved[idx]:.2e}") print(f" RK2: {errors_rk2[idx]:.2e}") print(f" RK4: {errors_rk4[idx]:.2e}") 4.4 Multistep Methods (Adams Methods)
multistep method 、past multiple method 。Adams-Bashforth() and Adams-Moulton() 。
📚 Theory: Adams
Adams-Bashforth 2():
\[ y_{n+1} = y_n + \frac{h}{2}[3f(t_n, y_n) - f(t_{n-1}, y_{n-1})] \]past2 use、 \( O(h^3) \) 。
Adams-Moulton 2():
\[ y_{n+1} = y_n + \frac{h}{12}[5f(t_{n+1}, y_{n+1}) + 8f(t_n, y_n) - f(t_{n-1}, y_{n-1})] \]method stable 、 \( O(h^4) \) 。
Predictor-Corrector:
Adams-Bashforth() and Adams-Moulton()method 。
6: Adams-Bashforthmultistep method
def adams_bashforth_2(f, t_span, y0, h): """ Adams-Bashforth 2 ODE solution Parameters: ----------- f : callable function f(t, y) t_span : tuple interval (t0, tf) y0 : float or array initial value h : float time step size Returns: -------- t : ndarray y : ndarray """ t0, tf = t_span t = np.arange(t0, tf + h, h) n = len(t) y0 = np.atleast_1d(y0) y = np.zeros((n, len(y0))) y[0] = y0 # 1 RK4 k1 = f(t[0], y[0]) k2 = f(t[0] + h/2, y[0] + h/2 * k1) k3 = f(t[0] + h/2, y[0] + h/2 * k2) k4 = f(t[0] + h, y[0] + h * k3) y[1] = y[0] + h/6 * (k1 + 2*k2 + 2*k3 + k4) # Adams-Bashforth 2 for i in range(1, n - 1): f_n = f(t[i], y[i]) f_n_minus_1 = f(t[i - 1], y[i - 1]) y[i + 1] = y[i] + h/2 * (3*f_n - f_n_minus_1) if len(y0) == 1: y = y.flatten() return t, y # Test: ODE(equation) # dy/dt = r*y*(1 - y/K), y(0) = 0.1 # Solution: y(t) = K / (1 + ((K-y0)/y0)*exp(-r*t)) def logistic_ode(t, y, r=1.0, K=1.0): """equation""" return r * y * (1 - y / K) def logistic_exact(t, y0=0.1, r=1.0, K=1.0): """equation """ return K / (1 + ((K - y0) / y0) * np.exp(-r * t)) print("=" * 60) print("Adams-Bashforth: equation") print("dy/dt = y(1 - y), y(0) = 0.1") print("=" * 60) t_span = (0, 10) y0 = 0.1 h = 0.2 # Adams-Bashforth t_ab, y_ab = adams_bashforth_2(lambda t, y: logistic_ode(t, y), t_span, y0, h) # RK4 t_rk4, y_rk4 = rk4(lambda t, y: logistic_ode(t, y), t_span, y0, h) # t_exact = np.linspace(0, 10, 200) y_exact_vals = logistic_exact(t_exact) # fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(14, 5)) # : ax1.plot(t_exact, y_exact_vals, 'b-', linewidth=2, label='') ax1.plot(t_ab, y_ab, 'ro-', markersize=6, linewidth=2, label='Adams-Bashforth 2') ax1.plot(t_rk4, y_rk4, 'gs-', markersize=5, linewidth=1.5, label='RK4', alpha=0.7) ax1.set_xlabel(' t', fontsize=12) ax1.set_ylabel('y(t)', fontsize=12) ax1.set_title(f'equation (h={h})', fontsize=14) ax1.legend(fontsize=11) ax1.grid(True, alpha=0.3) # : error y_exact_at_t = logistic_exact(t_ab) error_ab = np.abs(y_ab - y_exact_at_t) error_rk4 = np.abs(y_rk4 - logistic_exact(t_rk4)) ax2.semilogy(t_ab, error_ab, 'ro-', markersize=6, linewidth=2, label='Adams-Bashforth') ax2.semilogy(t_rk4, error_rk4, 'gs-', markersize=5, linewidth=1.5, label='RK4') ax2.set_xlabel(' t', fontsize=12) ax2.set_ylabel('error', fontsize=12) ax2.set_title('error ', fontsize=14) ax2.legend(fontsize=11) ax2.grid(True, alpha=0.3) plt.tight_layout() plt.savefig('adams_bashforth_logistic.png', dpi=150, bbox_inches='tight') plt.show() print(f"\n error:") print(f" Adams-Bashforth: {error_ab[-1]:.6e}") print(f" RK4: {error_rk4[-1]:.6e}") 4.5 Stiff equations and scipy.integrate
Stiffequation 、 ODE 、method is required 。scipy.integrate 。
📚 Theory: Stiffequation
Stiffequation 、 ()ODE 。
: Robertsonproblem()
\[ \begin{aligned} \frac{dy_1}{dt} &= -0.04 y_1 + 10^4 y_2 y_3 \\ \frac{dy_2}{dt} &= 0.04 y_1 - 10^4 y_2 y_3 - 3 \times 10^7 y_2^2 \\ \frac{dy_3}{dt} &= 3 \times 10^7 y_2^2 \end{aligned} \](\( 10^7 \) vs \( 0.04 \)) than 、Stiff 。
:
- method(backward Euler method、BDF)use
- scipy.integrate.solve_ivp method='BDF''Radau'use
7: scipy.integrate.solve_ivp
from scipy.integrate import solve_ivp, odeint # Robertsonproblem(Stiff ODE) def robertson(t, y): """ Robertsonproblem( Stiff ODE) 3 : y1 + y2 + y3 = 1 () """ y1, y2, y3 = y dy1 = -0.04 * y1 + 1e4 * y2 * y3 dy2 = 0.04 * y1 - 1e4 * y2 * y3 - 3e7 * y2**2 dy3 = 3e7 * y2**2 return [dy1, dy2, dy3] print("=" * 60) print("scipy.integrate.solve_ivp: Stiff ODE(Robertsonproblem)") print("=" * 60) # initial value y0 = [1.0, 0.0, 0.0] t_span = (0, 1e5) # 0 from 100,000 t_eval = np.logspace(-6, 5, 200) # # method1: RK45(、Stiff) print("\n1. RK45(Runge-Kutta)") import time start = time.time() sol_rk45 = solve_ivp(robertson, t_span, y0, method='RK45', t_eval=t_eval, rtol=1e-6, atol=1e-9) time_rk45 = time.time() - start print(f" calculation: {time_rk45:.3f} ") print(f" functioniterations: {sol_rk45.nfev}") print(f" : {sol_rk45.success}") # method2: BDF(、Stiff) print("\n2. BDF(、Stiff)") start = time.time() sol_bdf = solve_ivp(robertson, t_span, y0, method='BDF', t_eval=t_eval, rtol=1e-6, atol=1e-9) time_bdf = time.time() - start print(f" calculation: {time_bdf:.3f} ") print(f" functioniterations: {sol_bdf.nfev}") print(f" : {sol_bdf.success}") # method3: Radau(Runge-Kutta、Stiff) print("\n3. Radau(Runge-Kutta、Stiff)") start = time.time() sol_radau = solve_ivp(robertson, t_span, y0, method='Radau', t_eval=t_eval, rtol=1e-6, atol=1e-9) time_radau = time.time() - start print(f" calculation: {time_radau:.3f} ") print(f" functioniterations: {sol_radau.nfev}") print(f" : {sol_radau.success}") print(f"\ncalculation :") print(f" BDF vs RK45: {time_rk45 / time_bdf:.2f}timesfast") # fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(14, 5)) # : time evolution(BDF) ax1.semilogx(sol_bdf.t, sol_bdf.y[0], 'b-', linewidth=2, label='y₁') ax1.semilogx(sol_bdf.t, sol_bdf.y[1], 'r-', linewidth=2, label='y₂') ax1.semilogx(sol_bdf.t, sol_bdf.y[2], 'g-', linewidth=2, label='y₃') ax1.set_xlabel(' t [s]', fontsize=12) ax1.set_ylabel('', fontsize=12) ax1.set_title('Robertsonproblem (BDF)', fontsize=14) ax1.legend(fontsize=11) ax1.grid(True, alpha=0.3) # : (y1 + y2 + y3 = 1) conservation_bdf = sol_bdf.y[0] + sol_bdf.y[1] + sol_bdf.y[2] conservation_rk45 = sol_rk45.y[0] + sol_rk45.y[1] + sol_rk45.y[2] ax2.semilogx(sol_bdf.t, np.abs(conservation_bdf - 1), 'b-', linewidth=2, label='BDF') ax2.semilogx(sol_rk45.t, np.abs(conservation_rk45 - 1), 'r--', linewidth=2, label='RK45') ax2.set_xlabel(' t [s]', fontsize=12) ax2.set_ylabel('|y₁ + y₂ + y₃ - 1|', fontsize=12) ax2.set_title(' ', fontsize=14) ax2.legend(fontsize=11) ax2.grid(True, alpha=0.3) plt.tight_layout() plt.savefig('stiff_ode_robertson.png', dpi=150, bbox_inches='tight') plt.show() # print(f"\n t = {sol_bdf.t[-1]:.2e} :") print(f" y₁ = {sol_bdf.y[0][-1]:.6e}") print(f" y₂ = {sol_bdf.y[1][-1]:.6e}") print(f" y₃ = {sol_bdf.y[2][-1]:.6e}") print(f" = {np.sum(sol_bdf.y[:, -1]):.10f} (: 1.0)") # : (Lotka-Volterraequation) print("\n" + "=" * 60) print("application: Lotka-Volterraequation(-)") print("=" * 60) def lotka_volterra(t, y, alpha=1.0, beta=0.1, gamma=1.5, delta=0.075): """ Lotka-Volterraequation y[0]: y[1]: """ x, y_prey = y dx = alpha * x - beta * x * y_prey dy = -gamma * y_prey + delta * x * y_prey return [dx, dy] y0 = [40, 9] # t_span = (0, 50) t_eval = np.linspace(0, 50, 500) sol_lv = solve_ivp(lotka_volterra, t_span, y0, method='RK45', t_eval=t_eval, dense_output=True) # fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(14, 5)) # ax1.plot(sol_lv.t, sol_lv.y[0], 'b-', linewidth=2, label='') ax1.plot(sol_lv.t, sol_lv.y[1], 'r-', linewidth=2, label='') ax1.set_xlabel(' t', fontsize=12) ax1.set_ylabel('', fontsize=12) ax1.set_title('Lotka-Volterraequation: ', fontsize=14) ax1.legend(fontsize=11) ax1.grid(True, alpha=0.3) # ax2.plot(sol_lv.y[0], sol_lv.y[1], 'g-', linewidth=2) ax2.plot(sol_lv.y[0][0], sol_lv.y[1][0], 'ko', markersize=10, label='initial value') ax2.set_xlabel('', fontsize=12) ax2.set_ylabel('', fontsize=12) ax2.set_title('()', fontsize=14) ax2.legend(fontsize=11) ax2.grid(True, alpha=0.3) plt.tight_layout() plt.savefig('lotka_volterra.png', dpi=150, bbox_inches='tight') plt.show() print("\n ( )") 🏋️ Exercises
1: Euler stable
Stiffequation \( dy/dt = -100y \), \( y(0) = 1 \) forward Euler method 。 \( h = 0.01, 0.02, 0.03 \) calculation、 from stable 。
2: RK4 implementation
ODERK4 、 and :
\[ \frac{dy}{dt} = t^2 + y, \quad y(0) = 1 \]interval \([0, 1]\)、 \( h = 0.1 \) calculation、error。
3: Solution of the equation
equation \( dy/dt = r y (1 - y/K) \) 、 \( r \)() and \( K \)() and 。\( r = 0.5, 1.0, 2.0 \)、\( K = 1.0 \) 。
4: ODE
do ODE:
\[ \begin{cases} \frac{dx}{dt} = v \\ \frac{dv}{dt} = -2\zeta\omega_0 v - \omega_0^2 x \end{cases} \]RK4 。: \( \omega_0 = 2\pi \)、\( \zeta = 0.1 \)()、initial value: \( x(0) = 1, v(0) = 0 \)。 \((x, v)\) 。
Exercise 5: Application to Materials Science
equation(1、):
\[ \frac{dT_i}{dt} = \alpha \frac{T_{i+1} - 2T_i + T_{i-1}}{\Delta x^2} \]ODE and 。: \( T_0 = 100 \)℃、\( T_N = 0 \)℃、: 0℃。scipy.integrate.solve_ivpuse、time evolution。
Summary
、equation :
- Euler: ・・improved Euler method and stable
- Runge-Kutta: RK2、RK4 single-step method
- multistep method: Adams-Bashforth calculation
- Stiffequation: BDF、Radau method
- scipy.integrate: solve_ivp ODEsolution
- application: 、population dynamics、heat conduction etc.
equation 、time evolution analysis 。materials science 、chemical reaction kinetics、heat conduction etc. 。 、SciPycalculation and 、suitable・・。
Disclaimer
- This content is provided solely for educational, research, and informational purposes and does not constitute professional advice (legal, accounting, technical warranty, etc.).
- This content and accompanying code examples are provided "AS IS" without any warranty, express or implied, including but not limited to merchantability, fitness for a particular purpose, non-infringement, accuracy, completeness, operation, or safety.
- The author and Tohoku University assume no responsibility for the content, availability, or safety of external links, third-party data, tools, libraries, etc.
- To the maximum extent permitted by applicable law, the author and Tohoku University shall not be liable for any direct, indirect, incidental, special, consequential, or punitive damages arising from the use, execution, or interpretation of this content.
- The content may be changed, updated, or discontinued without notice.
- The copyright and license of this content are subject to the stated conditions (e.g., CC BY 4.0). Such licenses typically include no-warranty clauses.