Stop wrestling with manual ODE reduction. Write differential equations as they appear in your textbook.
Traditional approach: Converting high-order ODEs to first-order systems manually
# The mathematical equation: y'' + 0.3*y' + y = 0
# Must be manually converted to a system:
def system(t, z):
y, dy_dt = z
d2y_dt2 = -0.3*dy_dt - y
return [dy_dt, d2y_dt2]
# Then solve with scipy.integrate.solve_ivp
sol = solve_ivp(system, [0, 10], [1.0, 0.0])
# Which variable is which? What order? Error-prone! 😵💫Write math as math:
from odecast import var, Eq, solve
y = var("y")
eq = Eq(y.d(2) + 0.3*y.d() + y, 0) # Exactly as written in textbooks!
sol = solve(eq, ivp={y: 1.0, y.d(): 0.0}, tspan=(0, 10))
# Crystal clear, impossible to mess up! 🎯"Finally, I can copy equations directly from papers into code!" - Frustrated PhD student everywhere
| Traditional Approach | Odecast Approach |
|---|---|
| 🔴 Manual reduction to first-order | ✅ Automatic order inference |
| 🔴 Error-prone variable ordering | ✅ Named variables with clear semantics |
| 🔴 Complex system setup | ✅ Write equations as they appear in papers |
| 🔴 Lost connection to original math | ✅ Maintains mathematical clarity |
| 🔴 Vector systems are nightmares | ✅ u.d(2) + u = 0 for vector ODEs |
pip install odecast30-Second Example: Damped harmonic oscillator
from odecast import var, Eq, solve
# Write the equation exactly as it appears in your textbook
y = var("y")
equation = Eq(y.d(2) + 0.3*y.d() + y, 0)
# Solve it (automatically converts to first-order system)
solution = solve(
equation,
ivp={y: 1.0, y.d(): 0.0}, # y(0)=1, y'(0)=0
tspan=(0, 10)
)
# Get results
import matplotlib.pyplot as plt
plt.plot(solution.t, solution[y])
plt.show() # Beautiful decay curve! 📈Comprehensive documentation is available at https://maroba.github.io/odecast.
# Traditional nightmare:
def system(t, z): return [z[1], -0.3*z[1] - z[0]]
# Odecast elegance:
Eq(y.d(2) + 0.3*y.d() + y, 0)# 2D harmonic oscillator in one line:
u = var("u", shape=2)
Eq(u.d(2) + u, 0) # Automatically expands to u₀'' + u₀ = 0, u₁'' + u₁ = 0- SciPy: Lightning-fast numerics for engineering
- SymPy: Exact symbolic solutions for analysis
- Auto: Tries numeric first, falls back to symbolic
# Clear error messages when you mess up:
y = var("y")
solve(Eq(y.d(2) + y, 0), ivp={y: 1.0}) # Missing y'(0)!
# ❌ ODEValidationError: Missing initial condition for y.d()| Domain | Equation | Odecast Code |
|---|---|---|
| Physics | Mass-spring: mẍ + cẋ + kx = F(t) |
Eq(m*x.d(2) + c*x.d() + k*x, F) |
| Biology | Population: ṅ = rn(1-n/K) |
Eq(n.d() - r*n*(1-n/K), 0) |
| Engineering | RLC Circuit: Lq̈ + Rq̇ + q/C = V(t) |
Eq(L*q.d(2) + R*q.d() + q/C, V) |
| Economics | Growth: K̇ = sY - δK |
Eq(K.d() - s*Y + delta*K, 0) |
- 🎓 Researchers: Copy equations directly from papers
- 👨🎓 Students: Focus on physics, not programming
- 🏭 Engineers: Rapid prototyping of dynamic systems
- 📊 Data Scientists: Time-series modeling made easy
Example 1: Pendulum with damping
θ = var("θ") # Angle
eq = Eq(θ.d(2) + 0.5*θ.d() + np.sin(θ), 0) # Non-linear!
sol = solve(eq, ivp={θ: np.pi/4, θ.d(): 0}, tspan=(0, 20))Example 2: Lotka-Volterra (predator-prey)
x, y = var("x"), var("y") # Rabbits, foxes
eqs = [
Eq(x.d() - x*(1 - y), 0), # Rabbit growth
Eq(y.d() - y*(-1 + x), 0) # Fox dynamics
]
sol = solve(eqs, ivp={x: 1, y: 1}, tspan=(0, 15))Example 3: Vector oscillator (physics)
u = var("u", shape=2) # 2D position
eq = Eq(u.d(2) + 0.1*u.d() + u, 0) # Damped 2D harmonic oscillator
sol = solve(eq, ivp={u: [1, 0], u.d(): [0, 1]}, tspan=(0, 10))
plt.plot(sol[u[0]], sol[u[1]]) # Phase space plot! 🌀from odecast import t, var, Eq, solve
# Define a variable
y = var("y")
# Create an equation: y'' + 0.3*y' + y = 0 (damped harmonic oscillator)
eq = Eq(y.d(2) + 0.3*y.d() + y, 0)
# Solve with initial conditions y(0) = 1, y'(0) = 0
sol = solve(eq, ivp={y: 1.0, y.d(): 0.0}, tspan=(0.0, 10.0), backend="scipy")
# Access solution data
y_values = sol[y] # Position over time
velocity = sol[y.d()] # Velocity over time
times = sol.t # Time points
# Evaluate at specific times
position_at_5s = sol.eval(y, 5.0)
---
*Built for scientists, engineers, and students who want to focus on the math, not the code.*
## Examples
The `examples/` directory contains comprehensive examples:
- `01_ivp_damped_oscillator.py` - Numeric IVP solving with visualization
- `02_symbolic_simple.py` - Symbolic solutions using SymPy backend
- `03_mixed_orders.py` - Coupled systems with mixed derivative orders
- `04_vector_harmonic_oscillator.py` - 2D harmonic oscillator using vector variables
- `05_vector_mixed_system.py` - Mixed vector/scalar systems
- `06_vector_simple.py` - Simple vector variable introduction
Run any example:
```bash
python examples/01_ivp_damped_oscillator.py# Solve numerically over a time span
sol = solve(eq, ivp=conditions, tspan=(0, 10), backend="scipy")# Get exact symbolic solution
sol = solve(eq, backend="sympy")
expr = sol.as_expr(y) # Returns SymPy expression# Try numeric first, fall back to symbolic
sol = solve(eq, ivp=conditions, tspan=(0, 10), backend="auto")var(name, order=None)- Create a scalar dependent variablevar(name, shape=n)- Create a vector variable with n componentsvar(name, shape=(m,n))- Create a matrix variable (coming soon)y.d(n)- n-th derivative of variable yu[i]- Access i-th component of vector variable uu.d(n)- n-th derivative of vector variable (returns vector derivative)Eq(lhs, rhs)- Create an equationt- Independent variable (time)
solve(equations, ivp=None, bvp=None, tspan=None, backend="auto")
sol[y]- Access solution values for variable ysol[y.d()]- Access derivative valuessol[u]- Access vector solution (returns 2D array for vector variables)sol[u[i]]- Access i-th component solution (returns 1D array)sol.eval(target, time_points)- Evaluate at specific timessol.as_first_order()- Inspect first-order system representation
This project follows a test-driven development approach. All functionality is defined by comprehensive tests.
git clone https://github.com/maroba/odecast.git
cd odecast
pip install -e ".[dev]"pytest # Run all tests
pytest -v # Verbose output
pytest tests/test_api* # Run specific test filesruff check . # Linting
black . # Formatting- All new features must have comprehensive tests
- Follow the existing API patterns and naming conventions
- Add examples for significant new functionality
- Update documentation for user-facing changes
- Ensure all tests pass before submitting PRs
Current implementation status:
- ✅ Core DSL (variables, equations, derivatives)
- ✅ Vector/Matrix variables with automatic equation expansion
- ✅ SciPy numeric backend (IVP)
- ✅ SymPy symbolic backend (decoupled systems)
- ✅ Automatic order inference and validation
- ✅ Comprehensive error handling
- ✅ Mixed-order coupled systems
- 🚧 BVP support (boundary value problems) - coming soon
MIT License - see LICENSE file for details.
Check out the examples/ directory for complete working examples:
- 🔢 Numeric solutions:
01_ivp_damped_oscillator.py - 🔣 Symbolic solutions:
02_symbolic_simple.py - 🏹 Vector systems:
04_vector_harmonic_oscillator.py - 🔗 Coupled systems:
05_vector_mixed_system.py
# Run any example
python examples/01_ivp_damped_oscillator.pyGive us a star! ⭐ It helps others discover Odecast.
Share it with colleagues who are tired of manual ODE reduction.
Follow us for updates on new features and improvements.
Stop fighting with first-order systems. Start writing math like math.
⭐ Star on GitHub • 📦 Install from PyPI • 📖 Read the Docs
Made with ❤️ for scientists, engineers, and students worldwide.
Want to make Odecast even better? We'd love your help!
git clone https://github.com/maroba/odecast.git
cd odecast
pip install -e ".[dev]"
pytest # Run testsWhat we need:
- 📖 More examples from your field
- 🐛 Bug reports and fixes
- 🚀 Performance improvements
- 📝 Documentation improvements