5 min read•august 1, 2024
Adaptive Runge-Kutta methods are game-changers for solving differential equations. They adjust step sizes on the fly, balancing accuracy and speed. This means you can tackle tricky problems without breaking a sweat.
These methods shine when dealing with equations that change rapidly. They take small steps where needed and bigger ones where it's smooth sailing. It's like having a smart autopilot for your math problems.
def rkf45_step(f, t, y, h, tol): k1 = h * f(t, y) k2 = h * f(t + 1/4*h, y + 1/4*k1) k3 = h * f(t + 3/8*h, y + 3/32*k1 + 9/32*k2) k4 = h * f(t + 12/13*h, y + 1932/2197*k1 - 7200/2197*k2 + 7296/2197*k3) k5 = h * f(t + h, y + 439/216*k1 - 8*k2 + 3680/513*k3 - 845/4104*k4) k6 = h * f(t + 1/2*h, y - 8/27*k1 + 2*k2 - 3544/2565*k3 + 1859/4104*k4 - 11/40*k5) y_new = y + 25/216*k1 + 1408/2565*k3 + 2197/4104*k4 - 1/5*k5 y_err = y + 16/135*k1 + 6656/12825*k3 + 28561/56430*k4 - 9/50*k5 + 2/55*k6 error = np.linalg.norm(y_new - y_err) h_new = h * (tol / error)**0.2 return y_new, h_new, error
def compare_methods(f, y0, t_span, tol): # Fixed-step RK4 h_fixed = 0.01 t_fixed = np.arange(t_span[0], t_span[1], h_fixed) y_fixed = odeint(f, y0, t_fixed) # Adaptive RKF45 t_adaptive = [t_span[0]] y_adaptive = [y0] h = 0.01 while t_adaptive[-1] < t_span[1]: y_new, h_new, _ = rkf45_step(f, t_adaptive[-1], y_adaptive[-1], h, tol) t_adaptive.append(t_adaptive[-1] + h) y_adaptive.append(y_new) h = h_new return t_fixed, y_fixed, t_adaptive, y_adaptive
def solve_stiff_problem(f, y0, t_span, tol): t = [t_span[0]] y = [y0] h = 0.01 while t[-1] < t_span[1]: y_new, h_new, error = rkf45_step(f, t[-1], y[-1], h, tol) if error > 100 * tol: # Stiffness detected h_new = h / 10 # Drastically reduce step size t.append(t[-1] + h) y.append(y_new) h = min(h_new, t_span[1] - t[-1]) return t, y