primat.evolution — time-evolution schema¶
evolution.py¶
Unified time-evolution schema: the shared columns both the Python and C backends populate, so a notebook can plot nuclide evolution from either backend’s output without caring which one ran.
This module docstring is the authoritative contract for that schema
(column names, order, and semantics): both backends’ writers must conform to
it, and any new column must be added to both before it is considered part of
the schema. It supersedes the now-removed PRIMAT.md design note (whose
§7 is still recoverable for historical context via
git show a140bab^:PRIMAT.md).
EvolutionResult is the in-memory primary artifact – populated by
NuclearNetwork.solve() as self.evolution (and surfaced as
PRIMAT.solve()’s returned dict’s "evolution" key) whenever
cfg.output_time_evolution=True, with no disk I/O required to get it.
Disk output (the cfg.output_file TSV) is a derived convenience: it is
produced by calling dump_evolution() on that same object, not
something the solver opens a file for directly.
Columns (tab-separated, #-free header line – see _CORE_COLUMNS):
t_s a T_gamma_MeV T_nue_MeV T_numu_MeV T_nutau_MeV Y_<nuclide> ...
The Y_<nuclide> block is network-dependent (small/large have different
nuclide lists), so the header line is the source of truth – load_evolution()
reads it dynamically rather than assuming a fixed column count.
Per-reaction forward-rate columns (<reaction>_frwrd) are an optional
trailing block, appended after the Y_<nuclide> block when
cfg.output_rates_time_evolution=True – one column per reaction in the
active LT network (~12 for small/small_parthenope, 68 for
large``+``amax=8, ~429 for full large). They carry the forward
reaction-rate interpolant [same table units as the shipped nuclear rates]
evaluated at each row’s photon temperature, and live in
EvolutionResult.rates (None when the flag is off). Both backends
emit the identical column names in the identical (lexicographically sorted)
order – see the CLAUDE.md schema-parity mandate. The a/T_nu columns
are np.nan when the active background has no scale-factor/neutrino-sector
tracking (e.g. a minimal custom background).
- class primat.evolution.EvolutionResult(t, a, T_gamma, T_nu, Y=<factory>, rates=None)[source]¶
Bases:
objectIn-memory unified time-evolution result.
- Variables:
t (np.ndarray) – Cosmic time [s].
a (np.ndarray) – Scale factor (
np.naneverywhere if the background has no scale-factor relation, e.g. a minimal custom background).T_gamma (np.ndarray) – Photon temperature [MeV].
T_nu (dict of str -> np.ndarray) – Per-flavour neutrino temperature [MeV], keyed
"e"/"mu"/"tau"(np.nanarrays if the background tracks no neutrino sector).Y (dict of str -> np.ndarray) – Per-nuclide mass-fraction abundance, keyed by nuclide name, in network order (
n/pfirst).rates (dict of str -> np.ndarray, optional) – Optional per-reaction forward-rate columns (populated when
cfg.output_rates_time_evolution=True– one per reaction in the active LT network;Noneotherwise). Keyed by column name<reaction>_frwrd(canonical rate syntax, e.g.n_p__d_g_frwrd), each value an array aligned witht, holding the forward reaction-rate interpolant at that step’s photon temperature. Serialised as the trailing column block afterY_(see the module docstring); both backends emit the identical names in the identical sorted order.
- Parameters:
- t: NDArray[float64]¶
- a: NDArray[float64]¶
- T_gamma: NDArray[float64]¶
- primat.evolution.dump_evolution(result, path=None)[source]¶
Serialise
resultto the shared TSV schema (module docstring).Always returns the TSV text; additionally writes it to
pathif given (relative paths resolve against the current working directory, likecfg.output_fileelsewhere in this package). Called by:NuclearNetwork._write_time_evolution(thecfg.output_fileconvenience path); andprimat-gui’s download buttons, which call this lazily onrun.evolutionto produce the file text forst.download_button(data=...)– never via a tempfile.- Parameters:
result (EvolutionResult)
path (str, optional)
- Returns:
str – The TSV text (header line + one row per time step).
- Return type:
- primat.evolution.Y_interpolator(result, name)[source]¶
Build a
Y(t)callable for nuclidenamefromresult.t/result.Y[name]alone – the backend-agnostic counterpart ofprimat.main.PRIMAT.__getitem__’s live SciPy interpolator, usable on a plainEvolutionResultfrom either backend’sprimat.backend.run_bbn()(no livePRIMAT/NuclearNetworkobject required).Same convention as the live interpolator built by
NuclearNetwork.solve()(nuclear_network.py’sself.Y_of_t): piecewise-linear, withfill_value=(0, Y[-1])so a query beforeresult.t[0]reads as zero abundance (not yet produced) and a query afterresult.t[-1]holds at the final value (no decay beyond the integrated era – seerun_bbn’sdecay_eragap, module docstring ofprimat.backend, for the one case this does not cover).- Parameters:
result (EvolutionResult) – EvolutionResult.
name (str) – str. A key of
result.Y.
- Returns:
callable –
t -> Y(accepts a scalar or arrayt[s]).- Return type:
- primat.evolution.T_gamma_interpolator(result)[source]¶
Build a
T_gamma(t)[MeV] callable fromresult.t/result.T_gammaalone – the backend-agnostic counterpart ofprimat.main.PRIMAT.T_of_t, usable on a plainEvolutionResultfrom either backend.Piecewise-linear, clamped to the first/last sampled temperature outside
[result.t[0], result.t[-1]](T_gammadecreases monotonically witht, so this is the high-T/low-T extrapolation respectively).- Parameters:
result (EvolutionResult) – EvolutionResult.
- Returns:
callable –
t -> T_gamma[MeV] (accepts a scalar or arrayt[s]).- Return type:
- primat.evolution.t_of_T_interpolator(result)[source]¶
Build a
T_gamma -> t[s] inverse-lookup callable, backend-agnostic.The counterpart of
T_gamma_interpolator(), useful e.g. to add a secondary x-axis labelled inT_gammaon a plot whose primary axis is cosmic timet(seenotebooks/AbundanceEvolution.ipynb). Backend- agnostic replacement forprimat.main.PRIMAT.t_of_T, the live-instance, Python-only method this used to require.T_gammadecreases monotonically withtover a BBN run, so the(T_gamma, t)pairs are reversed into ascending order and interpolated withnumpy.interp()(clamped at the ends, matching the monotone extrapolation behaviour ofT_gamma_interpolator()).- Parameters:
result (EvolutionResult) – EvolutionResult.
- Returns:
callable –
T_gamma [MeV] -> t [s](accepts a scalar or array).- Return type:
- primat.evolution.load_evolution(path)[source]¶
Parse the shared TSV schema written by either backend’s
dump_evolution/equivalent writer, returning the sameEvolutionResultstructure assolve()’s in-memoryrun.evolution– for the case of reloading a previously-saved run without re-solving.Trailing per-reaction rate columns (
*_frwrd/*_bkwrd, written whenoutput_rates_time_evolution=True) are collected intoEvolutionResult.rates(Noneif the file has none). Any other column beyond the core +Y_<nuclide>+ rate blocks (e.g. a backend-specific bonus column) is simply ignored, so a file containing extra columns is still loadable.- Parameters:
path (str)
- Returns:
EvolutionResult
- Return type: