Source code for cosmofit.theories.legacy_expansion

"""
Any old-style model as a dark sector: models built from an action,
with ``define_model``, or by subclassing ``Cosmology``.

The wrapped class supplies ``E(z)`` itself, without radiation -- it
never had any -- so this works with ``radiation: false`` only. Its
parameters beyond the background's own (``H0``, ``Omega_m``,
``Omega_k``, ...) become parameters of the background, defaulting to
the class's defaults::

    theory:
      background:
        radiation: false
        dark_energy: {name: legacy, model: "mypackage.models:MyModel"}
"""

from __future__ import annotations

import numpy as np

from .dark_sector import DarkSector


__all__ = ["LegacyExpansion"]


#: Parameters the background itself provides, never forwarded as the
#: sector's own.
_BACKGROUND_NAMES = {"H0", "Omega_m", "Omega_b", "Omega_k", "N_eff", "m_nu"}


[docs] class LegacyExpansion(DarkSector): """ Options ------- model : str or type A ``Cosmology`` subclass, a built-in model name, or an import path. """ name = "legacy" radiation_ok = False def __init__(self, model="LCDM"): from cosmofit.core.legacy import _resolve_model self.model_cls = _resolve_model(model) params_cls = self.model_cls.PARAMS_CLASS self.defaults = { name: value for name, value in params_cls.defaults().items() if name not in _BACKGROUND_NAMES } self.flat_only = bool(getattr(self.model_cls, "FLAT_ONLY", False)) # ADE fixes Omega_m itself and ignores the one it is given; the # background must then take the model's, or its densities and # its E(z) describe different universes. self.derives_matter = "Omega_m" in getattr(self.model_cls, "DERIVED_PARAMS", ()) self._cache = {} def _instance(self, **values): key = tuple(sorted(values.items())) if key not in self._cache: params_cls = self.model_cls.PARAMS_CLASS full = dict(params_cls.defaults()) full.update(values) self._cache.clear() self._cache[key] = self.model_cls( params_cls(**{n: full[n] for n in params_cls.names()}) ) return self._cache[key]
[docs] def solve(self, ctx, **p): model = self._model(ctx, **p) def E2(z): return model.E(z) ** 2 return E2
def _model(self, ctx, **p): values = dict(H0=ctx.H0, Omega_k=ctx.Omega_k, **p) # A model that derives Omega_m ignores the one it is built with, # but its parameter class still wants one. values["Omega_m"] = 0.3 if self.derives_matter else ctx.Omega_cb return self._instance(**values)
[docs] def matter_density(self, make_context, **p): return float(self._model(make_context(1.0), **p).Omega_m)
# The growth hooks are the wrapped model's own.
[docs] def clustering_matter(self, z, growth, **p): return self._model(growth.ctx, **p).Omega_matter(z)
[docs] def matter_exchange(self, z, growth, **p): return self._model(growth.ctx, **p).matter_exchange(z)
[docs] def mu(self, z, k, growth, **p): a = 1.0 / (1.0 + np.asarray(z, dtype=float)) return self._model(growth.ctx, **p).mu(a, k=k)
[docs] def jumps(self, **p): """The wrapped model's own -- LsCDM's sign switch, for one.""" # The instance the last solve built, if it has these parameters: # building another would evict it from the one-entry cache. for key, model in self._cache.items(): if all(dict(key).get(name) == value for name, value in p.items()): return tuple(model.background_jumps()) return tuple(self._instance(**p).background_jumps())
def __repr__(self) -> str: return f"LegacyExpansion({self.model_cls.__name__})"