Guide: the model
Parameters holds the physical constants of Table 1 and
builds the dimensionless theta vector the integrator consumes.
ExoDaisyWorld wraps the compiled SRK1
step[1] in a small stateful interface that keeps the
MATLAB usage.
Parameters
from pyEDW import Parameters
p = Parameters() # paper defaults (Table 1)
p = Parameters(dT=15.0) # narrower growth-rate bandwidth
theta = p.to_theta() # 11-vector in the documented layout
with_bandwidth and with_lambda return modified copies for sweeping
\(\Delta T\) or \(\lambda\).
Stepping and running
import numpy as np
from pyEDW import ExoDaisyWorld
env = ExoDaisyWorld(p, x0=[0.1, 0.1, 1.0, 1.0], rng=np.random.default_rng(0))
env.evolve() # one SRK1 step, updates env.x
end = env.run(2000) # advance in place, return endpoint
path = env.run(2000, record=True) # full (2001, 4) trajectory
The state is [f_B, f_W, T, L], and daisy fractions are clamped non-negative
after every step, as in the MATLAB.
Ensembles
ensemble() is a parallel (Numba prange)
sweep over a luminosity axis. Each column sets \(\lambda = L - 1\) so the
Ornstein–Uhlenbeck mean tracks the axis, samples N initial conditions as
eDW_BHsim.m does, integrates nsteps steps, and records the endpoints:
Ls = 1.0 + np.linspace(-0.7, 1.4, 400)
bio = ExoDaisyWorld.ensemble(p, Ls=Ls, nsteps=2000, N=500, seed=1)
An integer seed makes the result reproducible however the parallel loop is
scheduled: each column seeds its own RNG stream with seed + column_index.
Reproducing the original runs
The MATLAB drivers and figure scripts ship under pyEDW/matlab/;
validate_against_matlab.py checks a Python ensemble against a saved
data_*.mat.