| name | differential-equations |
| description | Ordinary and partial differential equations including analytical solutions, numerical methods, stability analysis, and applications in physics and engineering. |
| category | mathematics |
| tags | ["mathematics","differential-equations","ode","pde","numerical-methods","stability","physics"] |
| difficulty | advanced |
| author | neuralblitz |
Differential Equations
What I do
I provide comprehensive expertise in differential equations, mathematical equations relating functions to their derivatives. I enable you to solve ordinary differential equations (ODEs) analytically and numerically, analyze partial differential equations (PDEs), study stability and bifurcations, and apply these techniques to model physical systems. My knowledge spans from first-order ODE solution techniques to advanced PDE methods essential for physics, engineering, biology, and economics modeling.
When to use me
Use differential equations when you need to: model population dynamics and growth, simulate heat transfer and diffusion processes, analyze mechanical and electrical systems, predict fluid flow behavior, model chemical reaction kinetics, study epidemic spread (SIR models), analyze financial derivatives and growth, or solve wave and heat equations in physics.
Core Concepts
- Ordinary Differential Equations (ODEs): Equations involving functions of a single variable and their derivatives.
- Partial Differential Equations (PDEs): Equations involving functions of multiple variables and partial derivatives.
- Initial Value Problems (IVPs): ODEs with specified values at an initial time for determining unique solutions.
- Boundary Value Problems (BVPs): ODEs/PDEs with conditions specified at multiple boundary points.
- Linear vs Nonlinear ODEs: Linear ODEs have solutions that superimpose; nonlinear ODEs exhibit complex behaviors.
- Analytical Solutions: Closed-form expressions obtained through algebraic manipulation and integration.
- Numerical Methods: Approximation techniques including Euler, Runge-Kutta, and finite difference methods.
- Stability Analysis: Studying how solutions behave near equilibrium points (stable, unstable, asymptotically stable).
- Phase Plane Analysis: Visualizing trajectories of systems of ODEs in state space.
- Eigenvalue Methods: Using eigenvalues of matrices to analyze linear systems and stability.
Code Examples
Solving ODEs with Scipy
import numpy as np
from scipy.integrate import solve_ivp, odeint
from scipy.optimize import fsolve
import matplotlib.pyplot as plt
def exponential_decay(t, y):
return -y
t_span = (0, 10)
y0 = [1.0]
sol = solve_ivp(exponential_decay, t_span, y0, method='RK45', t_eval=np.linspace(0, 10, 100))
print(f"Exact solution y(10) = {np.exp(-10):.6f}")
print(f"Numerical solution y(10) = {sol.y[0, -1]:.6f}")
print(f"Relative error: {abs(sol.y[0, -1] - np.exp(-10)) / np.exp(-10):.2e}")
def lotka_volterra(t, z, alpha=1.1, beta=0.4, delta=0.4, gamma=0.1):
x, y = z
dxdt = alpha * x - beta * x * y
dydt = delta * x * y - gamma * y
return [dxdt, dydt]
z0 = [10, ]
t_span = (, )
t_eval = np.linspace(, , )
sol_lv = solve_ivp(lotka_volterra, t_span, z0, t_eval=t_eval, method=)
()
()
()
()
():
x, v = y
dxdt = v
dvdt = -(b/m) * v - (k/m) * x
[dxdt, dvdt]
y0 = [, ]
t_span = (, )
sol_osc = solve_ivp(damped_oscillator, t_span, y0, t_eval=np.linspace(, , ))
()
()
()
()
Numerical Methods Implementation
import numpy as np
def euler_method(f, y0, t0, tf, h):
"""Forward Euler method (first-order)."""
n = int((tf - t0) / h)
t = np.linspace(t0, tf, n + 1)
y = np.zeros((n + 1, len(y0)))
y[0] = y0
for i in range(n):
y[i + 1] = y[i] + h * f(t[i], y[i])
return t, y
def heun_method(f, y0, t0, tf, h):
"""Heun's method (improved Euler, second-order)."""
n = int((tf - t0) / h)
t = np.linspace(t0, tf, n + 1)
y = np.zeros((n + 1, len(y0)))
y[0] = y0
for i in range(n):
k1 = f(t[i], y[i])
k2 = f(t[i] + h, y[i] + h * k1)
y[i + 1] = y[i] + (h / 2) * (k1 + k2)
return t, y
def runge_kutta4(f, y0, t0, tf, h):
"""Fourth-order Runge-Kutta method."""
n = int((tf - t0) / h)
t = np.linspace(t0, tf, n + 1)
y = np.zeros((n + 1, len(y0)))
y[0] = y0
for i in range(n):
k1 = f(t[i], y[i])
k2 = f(t[i] + h/2, y[i] + (h/2) * k1)
k3 = f(t[i] + h/, y[i] + (h/) * k2)
k4 = f(t[i] + h, y[i] + h * k3)
y[i + ] = y[i] + (h / ) * (k1 + *k2 + *k3 + k4)
t, y
f = t, y: -y
y0 = []
t0, tf = ,
h =
t_euler, y_euler = euler_method(f, y0, t0, tf, h)
t_heun, y_heun = heun_method(f, y0, t0, tf, h)
t_rk4, y_rk4 = runge_kutta4(f, y0, t0, tf, h)
exact = np.exp(-t_rk4)
()
()
()
()
()
()
():
t = [t0]
y = [y0[]]
h =
t[-] < tf:
k1 = f(t[-], y[-])
k2 = f(t[-] + h/, y[-] + h*k1/)
k3 = f(t[-] + h/, y[-] + h*k2/)
k4 = f(t[-] + h, y[-] + h*k3)
y_h = y[-] + (h/)*(k1 + *k2 + *k3 + k4)
h_half = h/
k1 = f(t[-], y[-])
k2 = f(t[-] + h_half, y[-] + h_half*k1/)
k3 = f(t[-] + h_half, y[-] + h_half*k2/)
k4 = f(t[-] + h_half, y[-] + h_half*k3)
y_h2_step1 = y[-] + (h_half/)*(k1 + *k2 + *k3 + k4)
k1 = f(t[-] + h_half, y_h2_step1)
k2 = f(t[-] + h, y_h2_step1 + h_half*k1/)
k3 = f(t[-] + h, y_h2_step1 + h_half*k2/)
k4 = f(t[-] + h, y_h2_step1 + h_half*k3)
y_h2 = y_h2_step1 + (h_half/)*(k1 + *k2 + *k3 + k4)
error = (y_h2 - y_h) /
error < tol:
t.append(t[-] + h)
y.append(y_h2)
h = h * (, (, (tol/error)**))
:
h = h * (, (tol/error)**)
np.array(t), np.array(y)
Stability Analysis
import numpy as np
from scipy.linalg import eig
def linear_stability_analysis(A):
"""Analyze stability of linear system x' = Ax."""
eigenvalues, eigenvectors = eig(A)
print(f"Eigenvalues: {eigenvalues}")
stability = "Stable (all real parts < 0)"
for ev in eigenvalues:
if np.real(ev) > 0:
stability = "Unstable (positive real part)"
break
elif abs(np.imag(ev)) > 1e-10:
stability = "Spiral (complex with negative real part)"
return eigenvalues, stability
print("1. Decaying oscillator:")
A1 = np.array([[0, 1], [-2, -0.5]])
ev1, stab1 = linear_stability_analysis(A1)
print(f" Stability: {stab1}")
print("\n2. Unstable saddle:")
A2 = np.array([[1, 0], [0, -1]])
ev2, stab2 = linear_stability_analysis(A2)
print(f" Stability: {stab2}")
print("\n3. Center (pure oscillation):")
A3 = np.array([[0, 1], [-1, ]])
ev3, stab3 = linear_stability_analysis(A3)
()
():
x = np.linspace(xlim[], xlim[], n_points)
y = np.linspace(ylim[], ylim[], n_points)
X, Y = np.meshgrid(x, y)
U = A[, ] * X + A[, ] * Y
V = A[, ] * X + A[, ] * Y
X, Y, U, V
():
scipy.optimize fsolve
eq = fsolve(f, [x0, y0])
()
h =
J = np.zeros((, ))
j ():
delta = np.zeros()
delta[j] = h
f_plus = f(eq + delta, )
f_minus = f(eq - delta, )
J[:, j] = (f_plus - f_minus) / ( * h)
eigenvalues, _ = eig(J)
()
(np.real(ev) < ev eigenvalues):
(np.real(ev) > ev eigenvalues):
:
():
S, I, R = y
dSdt = -beta * S * I
dIdt = beta * S * I - gamma * I
dRdt = gamma * I
[dSdt, dIdt, dRdt]
()
Boundary Value Problems
import numpy as np
from scipy.optimize import minimize, root_scalar
from scipy.integrate import solve_bvp
def shooting_method(f, t_span, y0_guess, bc, tol=1e-6, max_iter=50):
"""Solve BVP using shooting method."""
a, b = t_span
def objective(s):
"""Minimize boundary condition difference."""
y0 = [y0_guess[0], s]
sol = solve_ivp(f, t_span, y0, method='RK45', rtol=1e-8, atol=1e-10)
return bc(sol.y[:, -1])
s = root_scalar(objective, bracket=[-10, 10], method='brentq').root
y0 = [y0_guess[0], s]
sol = solve_ivp(f, t_span, y0, method='RK45')
return sol
def ode_bvp(t, y):
"""y'' + y = 0 written as system."""
return [y[1], -y[0]]
def bc_final(y_final):
"""Boundary condition at t=π/2."""
return y_final[0] - 1
t_span = (0, np.pi/2)
sol = shooting_method(ode_bvp, t_span, [, ], bc_final)
()
()
()
():
[y[], -y[]]
():
[ya[], yb[] - ]
t_bvp = np.linspace(, np.pi/, )
y_bvp = np.zeros((, (t_bvp)))
y_bvp[] = np.sin(t_bvp)
sol_bvp = solve_bvp(ode_collocation, bc, t_bvp, y_bvp)
()
():
():
[y[], -lambda_val * y[]]
():
[ya[], yb[]]
t = np.linspace(, np.pi, )
y_guess = np.zeros((, (t)))
y_guess[] = np.sin(np.sqrt(lambda_val) * t)
:
sol = solve_bvp(ode, bc, t, y_guess, tol=)
sol
:
()
n (, ):
lambda_n = n**
sol = eigenvalue_bvp(lambda_n)
sol :
()
Partial Differential Equations
import numpy as np
def heat_equation_fd(u0, L, T, nx, nt, alpha=1):
"""
Solve heat equation u_t = αu_xx using finite differences.
Boundary conditions: u(0,t) = u(L,t) = 0
"""
dx = L / (nx - 1)
dt = T / (nt - 1)
r = alpha * dt / dx**2
if r > 0.5:
print(f"Warning: r = {r:.2f} > 0.5, method may be unstable")
u = u0.copy()
u_new = np.zeros_like(u)
for n in range(nt - 1):
for i in range(1, nx - 1):
u_new[i] = r * u[i-1] + (1 - 2*r) * u[i] + r * u[i+1]
u = u_new.copy()
return u
L = np.pi
T = 0.5
nx, nt = 50, 100
x = np.linspace(0, L, nx)
t = np.linspace(0, T, nt)
u0 = np.sin(x)
u_final = heat_equation_fd(u0, L, T, nx, nt)
analytical = np.exp(-t[-1]) * np.sin(x)
print("Heat equation u_t = u_xx:")
print(f" Numerical u(π/2, 0.5) = {u_final[nx//]:f}")
()
scipy.integrate solve_ivp
():
u = y[:nx]
v = y[nx:]
uxx = np.zeros(nx)
uxx[:-] = (u[:-] - *u[:-] + u[:]) / (L/(nx-))**
uxx[] =
uxx[-] =
dvdt = c** * uxx
dudt = v
np.concatenate([dudt, dvdt])
nx =
L =
x = np.linspace(, L, nx)
y0 = np.concatenate([np.sin(np.pi * x), np.zeros(nx)])
t_span = (, )
sol_wave = solve_ivp(wave_equation_mol, t_span, y0, method=, t_eval=np.linspace(, , ))
()
()
():
scipy.sparse lil_matrix, csr_matrix
scipy.sparse.linalg spsolve
N = nx * ny
A = lil_matrix((N, N))
b = np.zeros(N)
dx = Lx / (nx - )
dy = Ly / (ny - )
h2 = dx**
i (nx):
j (ny):
k = i + j * nx
i == i == nx - j == j == ny - :
A[k, k] =
b[k] =
:
A[k, k] = -
A[k, k-] =
A[k, k+] =
A[k, k-nx] =
A[k, k+nx] =
b[k] = h2 * f(i*dx, j*dy)
A = csr_matrix(A)
u = spsolve(A, b)
u.reshape(nx, ny)
f_poisson = x, y: - * np.pi** * np.sin(np.pi * x) * np.sin(np.pi * y)
u_poisson = poisson_equation_fd(f_poisson, , , , )
()
()
()
Best Practices
- For stiff ODEs, use implicit methods (BDF, Rosenbrock) rather than explicit Runge-Kutta to avoid stability issues.
- Always check the CFL condition for PDEs to ensure numerical stability in explicit schemes.
- Use adaptive step size methods when solution behavior varies significantly across the domain.
- For boundary value problems, shooting methods work well when solutions are sensitive to initial conditions.
- For elliptic PDEs, use iterative solvers (Gauss-Seidel, SOR) or direct sparse solvers for large systems.
- Validate numerical solutions by checking conservation laws, symmetry, and limiting cases.
- When solving eigenvalue problems, verify that computed eigenvalues satisfy the original equation.
- For nonlinear PDEs, consider using Newton's method with continuation techniques for difficult problems.
- Use method of lines to convert PDEs to ODE systems, then apply ODE solvers.
- Monitor computational cost: implicit methods have higher per-step cost but can use larger time steps.