5.4.9Scientific Computing (Python)

scipy.integrate — odeint, solve_ivp (RK45, DOP853), quad

1,902 words9 min readdifficulty · medium

1. quad — numerical definite integral

Deriving the simplest rule from scratch (trapezoid → Simpson)

We want abfdx\int_a^b f\,dx using only f(a),f(m),f(b)f(a), f(m), f(b) with m=a+b2m=\frac{a+b}{2}. Fit a parabola p(x)=α+βx+γx2p(x)=\alpha + \beta x + \gamma x^2 through these 3 points and integrate it exactly. Shift so a=h, m=0, b=ha=-h,\ m=0,\ b=h. Then:

hh(α+βx+γx2)dx=2αh+23γh3\int_{-h}^{h} (\alpha+\beta x+\gamma x^2)\,dx = 2\alpha h + \tfrac{2}{3}\gamma h^3

The odd term vanishes (Why? hhxdx=0\int_{-h}^h x\,dx=0 by symmetry). Now express α,γ\alpha,\gamma via the samples: f(0)=αf(0)=\alpha, and f(h)+f(h)=2α+2γh2γh2=f(h)+f(h)2f(0)f(h)+f(-h)=2\alpha+2\gamma h^2 \Rightarrow \gamma h^2 = \tfrac{f(h)+f(-h)}{2}-f(0). Substitute:

hhfh3[f(h)+4f(0)+f(h)]\boxed{\int_{-h}^{h} f \approx \frac{h}{3}\big[f(-h)+4f(0)+f(h)\big]}

That is Simpson's rule. quad is this idea, but adaptive and higher order.

from scipy.integrate import quad
import numpy as np
 
val, err = quad(lambda x: np.exp(-x**2), 0, np.inf)
# val ≈ 0.8862269  (= sqrt(pi)/2), err ≈ 7.1e-09
  • val = estimate, err = estimated absolute error.
  • Pass extra params with args=(...); use points=[...] to flag kinks.

2. solve_ivp — the modern ODE solver

Deriving a single step (forward Euler, then RK)

Start from the exact statement (fundamental theorem of calculus): y(tn+h)=y(tn)+tntn+hf(t,y)dty(t_{n}+h) = y(t_n) + \int_{t_n}^{t_n+h} f(t,y)\,dt

WHY this is the seed: if we knew that integral exactly we'd be done; every method is just a quadrature rule for that little integral.

  • Euler (left-rectangle): approximate the integral by hf(tn,yn)h\,f(t_n,y_n): yn+1=yn+hf(tn,yn)(error O(h2) per step)y_{n+1}=y_n + h f(t_n,y_n)\quad\text{(error }O(h^2)\text{ per step)}

  • RK4 / RK45 (Runge–Kutta): sample the slope at several trial points inside the step, like Simpson did inside an interval, and blend: yn+1=yn+h6(k1+2k2+2k3+k4)y_{n+1}=y_n+\tfrac{h}{6}(k_1+2k_2+2k_3+k_4) with k1=f(tn,yn)k_1=f(t_n,y_n), k2=f(tn+h2,yn+h2k1)k_2=f(t_n+\tfrac h2, y_n+\tfrac h2 k_1), etc.

Worked example — exponential decay y˙=2y, y(0)=3\dot y=-2y,\ y(0)=3

Exact solution: y(t)=3e2ty(t)=3e^{-2t}.

from scipy.integrate import solve_ivp
import numpy as np
 
sol = solve_ivp(lambda t, y: -2*y, [0, 5], [3.0],
                method='RK45', t_eval=np.linspace(0, 5, 100),
                rtol=1e-8, atol=1e-10)
sol.t      # times
sol.y[0]   # y values, shape (n_states, n_times)
  • Why y0=[3.0] (a list)? solve_ivp always treats the state as a vector; even a scalar must be 1-element.
  • Why sol.y[0]? rows are state components, columns are time points.

Worked example — a system (SHM / 2nd-order → 1st-order)

x¨=ω2x\ddot x = -\omega^2 x. Why convert? Solvers only take first-order systems. Let y0=x, y1=x˙y_0=x,\ y_1=\dot x: y˙0=y1,y˙1=ω2y0\dot y_0 = y_1,\qquad \dot y_1=-\omega^2 y_0

w = 2.0
def rhs(t, y):
    return [y[1], -w**2 * y[0]]
sol = solve_ivp(rhs, [0, 10], [1, 0], method='DOP853', rtol=1e-10)
# x(t) = sol.y[0] should match cos(2t); energy conserved

3. odeint — the legacy classic

from scipy.integrate import odeint
y = odeint(lambda y, t: -2*y, 3.0, np.linspace(0,5,100))  # note (y, t)!
  • Returns an array of shape (len(t), n_states) — also transposed vs solve_ivp.
  • Modern advice: prefer solve_ivp (events, dense output, method choice). Keep odeint knowledge for reading old code.

Recall Feynman: explain to a 12-year-old

Imagine you only know how fast a toy car is moving at every instant, but not where it is. To find where it ends up, you take a tiny moment, move it forward a little using its current speed, check the speed again, and repeat — that's solve_ivp solving an ODE. Smart solvers peek a few times inside each tiny step (RK45) to move more accurately, and they take bigger steps on a smooth straight road and smaller steps on a curvy one. quad is different: there you already know the shape of a hill and just want the area under it, so you sample the height at clever spots and add them up.


Flashcards

What does quad return?
A tuple (value, estimated_absolute_error).
Argument order of the RHS for solve_ivp?
f(t, y) — time first.
Argument order of the RHS for odeint?
f(y, t) — state first (reversed from solve_ivp).
What is RK45's default and how does it choose step size?
Dormand–Prince 5(4): compares an embedded 4th-order estimate to the 5th-order one; the difference is the local error used to adapt hh. :::
When should you switch from RK45/DOP853 to BDF/Radau?
When the ODE is stiff (very different time-scales) and explicit methods need impractically small steps. :::
What does t_eval control (and NOT control)?
Where the solution is reported; it does NOT set the solver's internal adaptive step size. :::
Simpson's rule formula on [h,h][-h,h]?
h3[f(h)+4f(0)+f(h)]\frac{h}{3}[f(-h)+4f(0)+f(h)], exact for cubics. :::
Why must a 2nd-order ODE be rewritten before solving?
Solvers accept only first-order systems; introduce y1=x˙y_1=\dot x so y˙0=y1, y˙1=x¨\dot y_0=y_1,\ \dot y_1=\ddot x. :::
Shape of solve_ivp solution array sol.y?
(n_states, n_time_points) — components are rows. :::
What makes DOP853 worth its cost?
8th order: huge accurate steps on smooth problems → fewer evaluations for tight tolerances. :::

Connections

Concept Map

solves

solves

unifies

method

special case

derived from

error term

legacy API

modern API

methods

built from

derived from

approximated by

scipy.integrate module

Definite integral quad

ODE solvers

Integration in calculus

Adaptive Gauss-Kronrod

Simpson rule

Fit parabola thru 3 pts

E ~ f 4th deriv

odeint

solve_ivp

RK45 and DOP853

Forward Euler step

FTC integral of slope

Hinglish (regional understanding)

Intuition Hinglish mein samjho

Dekho, scipy.integrate ke andar do bilkul alag kaam hote hain. Pehla hai quad — yahan tumhare paas pura function f(x)f(x) already hai aur tumhe sirf ek number chahiye: us curve ke neeche ka area, yaani abf(x)dx\int_a^b f(x)\,dx. quad smart tareeke se kuch points pe function ki value leta hai (Simpson/Gauss-Kronrod rule) aur weighted sum bana ke area de deta hai. Yeh tumhe (value, error) ka tuple deta hai — error woh khud estimate karta hai.

Dusra kaam hai ODE solve karna — solve_ivp aur purana odeint. Yahan tumhe function nahi pata, sirf uska slope pata hai: dydt=f(t,y)\frac{dy}{dt}=f(t,y), aur ek starting point y(t0)y(t_0). Solver chhote-chhote steps leke aage badhta hai. Forward Euler simplest hai (current speed se thoda aage badho), aur RK45 thoda smart hai — ek step ke andar 4-5 jagah slope check karta hai taaki accuracy zyada ho. RK45 default hai, DOP853 high-accuracy ke liye, aur agar problem stiff ho (bahut fast aur bahut slow scale saath me) to BDF/Radau use karo.

Sabse common galti: argument order ulta likh dena. solve_ivp me function f(t, y) hota hai — time pehle. Lekin odeint (purana hai) me f(y, t) hota hai — ulta. Yaad rakho: "old = reversed". Aur t_eval se step size control nahi hota, woh sirf decide karta hai ki answer kahan-kahan report karna hai; accuracy badhani ho to rtol/atol change karo ya method badlo.

Yeh isliye important hai kyunki science aur engineering ki har simulation — planet ki orbit, chemical reaction, circuit, population model — yahi tools use karti hai. Ek baar intuition clear ho jaaye to tum koi bhi physical system code me simulate kar sakte ho.

Go deeper — visual, from zero

Test yourself — Scientific Computing (Python)

Connections