Source code for cosmofit.cosmology.models.gede
"""
Generalized Emergent Dark Energy (GEDE).
"""
from __future__ import annotations
import numpy as np
from cosmofit.typing import Array, Redshift
from cosmofit.cosmology.numerics.powers import cube
from cosmofit.cosmology.core import Cosmology
_LN10 = np.log(10.0)
[docs]
class GEDE(Cosmology):
r"""
Generalized Emergent Dark Energy (Li & Shafieloo 2020).
::
Omega_de(z) = Omega_de0
* [1 - tanh(Delta log10((1+z)/(1+z_t)))]
/ [1 + tanh(Delta log10(1 + z_t))]
The one-parameter family that contains both of the models it
generalizes, which is what makes it useful:
- ``Delta -> 0`` gives a constant ``Omega_de`` -- **LCDM**.
- ``Delta = 1``, ``z_t = 0`` gives
:class:`~cosmology.models.pede.PEDE`.
So a fit for ``Delta`` is a direct, continuous test of how far
the data pushes away from a cosmological constant, with LCDM
sitting at a specific value of a real parameter rather than at
the boundary of a different model. ``Delta`` controls how
sharply dark energy emerges; ``z_t`` sets when.
The denominator is not decoration -- it is the normalization
that enforces ``Omega_de(z=0) = Omega_de0`` for any
``(Delta, z_t)``, and hence ``E(z=0) = 1``. Dropping it (an
easy transcription slip) leaves a model that silently violates
the Friedmann constraint by tens of percent.
Notes
-----
In Li & Shafieloo's original presentation ``z_t`` is a
*derived* quantity -- the redshift at which the dark-energy and
matter densities are equal -- fixed by ``Delta`` and
``Omega_m`` rather than sampled. Here it is a free parameter,
which is the form used in most follow-up work: it makes the
model a genuine two-parameter extension that can be compared
against CPL on equal footing, at the cost of the two being
somewhat degenerate.
References
----------
Li & Shafieloo (2020), "Evidence for Emergent Dark Energy",
ApJ 902, 58, arXiv:2001.05103.
"""
MODEL_NAME = "GEDE"
EXTRA_PARAMS = {
"Delta": {
"default": 1.0,
"bounds": (-10.0, 10.0),
"label": r"$\Delta$",
},
"z_t": {
"default": 0.0,
"bounds": (0.0, 5.0),
"label": r"$z_t$",
},
}
# ---------------------------------------------------------
@property
def _norm(self) -> float:
"""
The ``1 + tanh(Delta log10(1 + z_t))`` denominator, which
pins ``Omega_de(0)`` to ``Omega_de0``.
"""
return 1.0 + np.tanh(
self.Delta * np.log10(1.0 + self.z_t),
)
# ---------------------------------------------------------
def _v(self, z):
"""
``Delta * log10((1 + z) / (1 + z_t))``.
"""
z = np.asarray(z, dtype=float)
return self.Delta * (
np.log10(1.0 + z)
- np.log10(1.0 + self.z_t)
)
# ---------------------------------------------------------
def Omega_de(self, z: Redshift) -> Array:
return (
self.Omega_de0
* (1.0 - np.tanh(self._v(z)))
/ self._norm
)
# ---------------------------------------------------------
def E(self, z: Redshift) -> Array:
z = np.asarray(z, dtype=float)
return np.sqrt(
self.Omega_m * cube(1.0 + z)
+
self.Omega_k * (1.0 + z) ** 2
+
self.Omega_de(z)
)
# ---------------------------------------------------------
def dEdz(self, z: Redshift) -> Array:
z = np.asarray(z, dtype=float)
v = self._v(z)
# dv/dz = Delta / ((1+z) ln 10).
d_omega_de = -(
self.Omega_de0
/ self._norm
/ (np.cosh(v) ** 2)
* self.Delta
/ ((1.0 + z) * _LN10)
)
return (
(
3.0 * self.Omega_m * (1.0 + z) ** 2
+
2.0 * self.Omega_k * (1.0 + z)
+
d_omega_de
)
/
(2.0 * self.E(z))
)
# ---------------------------------------------------------
def w(self, z: Redshift) -> Array:
r"""
Effective dark-energy equation of state,
w(z) = -1 - (Delta / (3 ln 10)) [1 + tanh(v)]
with ``v = Delta log10((1+z)/(1+z_t))``. The same
derivation as :meth:`~cosmology.models.pede.PEDE.w`, with
the extra factor of ``Delta`` from the chain rule -- and
with the normalization cancelling, since it is a constant
multiplying ``rho_de``.
``Delta = 0`` gives ``w = -1`` exactly, the LCDM limit.
"""
return -1.0 - (
self.Delta
* (1.0 + np.tanh(self._v(z)))
/ (3.0 * _LN10)
)