YENNEFER ODE SOLVER
Yennefer is a high-performance Python library for solving ordinary differential equations (ODEs), built on top of NumPy and accelerated via Numba JIT compilation.
Features
- Three integration methods covering fixed-step and adaptive workflows.
- Numba
@njit-compiled solver cores. - Unified, minimal API across all solvers.
- Pass arbitrary physical parameters to the RHS without global state.
- Works seamlessly with
@njit-decorated right-hand sides for maximum performance.
Installation
pip install yennefer
Solvers
| Class | Method | Order | Step control |
|---|---|---|---|
RK4Solver |
Classical Runge–Kutta | 4 | Fixed |
RK45Solver |
Runge–Kutta–Fehlberg 4(5) | 4/5 | Adaptive |
DOP853Solver |
Dormand–Prince 8(5,3) | 8 | Adaptive |
Example — Josephson Junction (RCSJ model)
The RCSJ model of a Josephson junction:
$$ \begin{cases} \frac{dV}{dt} = I_c + A\sin(u) - \beta V - \sin\varphi\ \frac{d\varphi}{dt} = V \ \frac{du}{dt} = \omega \end{cases} \tag{1} $$
import numpy as np
import matplotlib.pyplot as plt
from numba import njit
from yennefer import RK4Solver, RK45Solver, DOP853Solver
@njit
def jj_system(t, y, p):
V, ph, u = y
Ic, A, beta, omega = p[0], p[1], p[2], p[3]
return np.array([
Ic + A * np.sin(u) - beta * V - np.sin(ph),
V,
omega,
])
y0 = np.array([0.0, 0.0, 0.0])
params = np.array([0.1, 0.5, 0.2, 2.0]) # Ic, A, beta, omega
Fixed step with RK4
solver = RK4Solver(jj_system, y0, params)
T, Y = solver.solve(300.0, 5e-2)
Adaptive step with RK45
solver = RK45Solver(jj_system, y0, params)
T, Y = solver.solve(300.0, dt_initial=5e-2)
High-accuracy with DOP853
solver = DOP853Solver(jj_system, y0, params)
T, Y = solver.solve(300.0, dt_initial=5e-2)
Plot
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(10, 4))
ax1.plot(T, Y[:, 0], label="V(t)")
ax1.set_xlabel("t"); ax1.set_ylabel("V"); ax1.legend()
ax2.plot(T, Y[:, 1], label="φ(t)")
ax2.set_xlabel("t"); ax2.set_ylabel("φ"); ax2.legend()
plt.tight_layout()
plt.show()
More precise example is presented in example usage IPython notebook.
Another example is here.
RK4Solver — Fixed-Step 4th-Order Runge–Kutta
The classical RK4 method. Every step uses exactly 4 RHS evaluations. Best suited for smooth problems where you know the required step size in advance, or when uniform output is needed.
solver = RK4Solver(function, y0, params)
t, y = solver.solve(t_max, dt)
| Parameter | Description |
|---|---|
function |
RHS f(t, y, params) -> dy |
y0 |
Initial state vector (np.float64 array) |
params |
Parameter vector passed verbatim to f |
t_max |
Integration horizon |
dt |
Fixed step size |
RK45Solver — Adaptive Runge–Kutta–Fehlberg 4(5)
Embedded 4(5) pair with automatic step-size control. The 5th-order solution advances the state; the difference between 4th and 5th order estimates drives the error controller. 6 RHS evaluations per accepted step. Good general-purpose choice for moderate accuracy requirements.
solver = RK45Solver(function, y0, params, atol=1e-6, rtol=1e-3, max_step=np.inf)
t, y = solver.solve(t_max, dt_initial=1e-3)
| Parameter | Description |
|---|---|
atol |
Absolute error tolerance (default 1e-6) |
rtol |
Relative error tolerance (default 1e-3) |
max_step |
Maximum allowed step size (default ∞) |
dt_initial |
Initial step size guess |
DOP853Solver — Dormand–Prince 8(5,3)
Implementation of the classic Hairer–Nørsett–Wanner DOP853 method from Solving ODEs I (1993), based on the original Fortran source by Hairer & Williams. Dual embedded error estimators (5th and 3rd order), FSAL reuse (effectively 11 new RHS calls per accepted step). The go-to solver for high-accuracy and stiff-sensitive problems.
solver = DOP853Solver(function, y0, params, rtol=1e-9, atol=1e-9, n_max_steps=10000)
t, y = solver.solve(t_max, dt_init)
| Parameter | Description |
|---|---|
rtol |
Relative error tolerance (default 1e-9) |
atol |
Absolute error tolerance (default 1e-9) |
n_max_steps |
Maximum number of accepted steps (default 10000) |
dt_init |
Initial step size guess |
Common API
All solvers share the same RHS signature convention and output contract.
Right-hand side signature
@njit
def f(t: float, y: np.ndarray, params: np.ndarray) -> np.ndarray:
...
Decorating f with @njit is optional but strongly recommended — it eliminates the Python call overhead inside the inner loop and yields the best performance.
Output
solver.solve(...) returns a tuple (t, y):
| Name | Shape | Description |
|---|---|---|
t |
(N,) |
Time grid |
y |
(N, n_vars) |
Solution at each time point |
Both are also available as .t and .y properties after the call.
Performance tips
- Always decorate the RHS with
@njit. The first call triggers compilation (warm-up); subsequent calls run at native speed. - If you benchmark solvers against each other or against SciPy, run a short warm-up integration first to exclude JIT compilation time from the measurement.
- For adaptive solvers, tighter tolerances (
1e-12—1e-14) with DOP853 are often faster than the same accuracy with RK45, because DOP853 accepts far fewer steps.
Release files for yennefer 0.5.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| yennefer-0.5.0.tar.gz | 16.8 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| yennefer-0.5.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 35.2 kB
Release files / yennefer-0.5.0.tar.gz
| Download URL | yennefer-0.5.0.tar.gz |
|---|---|
| Size | 16.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
46a366e6f3170e1450f3c5aaaed151902458d8895ccf928add848e7c3a91cd4a
|
|
BLAKE2b-256 checksum How to use checksums |
6b58395e3b662d3786296a4e9fd9d4c2939bed104369e707fad80b0d2675d87c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.12
|
Release files / yennefer-0.5.0-py3-none-any.whl
| Download URL | yennefer-0.5.0-py3-none-any.whl |
|---|---|
| Size | 18.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
b6849f43f567e1eac299857f3811753434549d3c84ef3e9d36e58de9e2c372ef
|
|
BLAKE2b-256 checksum How to use checksums |
2a669706cd69a9288878bc492b0d620a65052e0ab4c241104676d209387de852
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.12
|