Source code for cosmofit.cosmology.models.rde

"""
Ricci dark energy (Gao, Chen & Shen 2009).
"""

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


[docs] class RDE(Cosmology): r""" Ricci dark energy: the holographic cutoff is the Ricci scalar. :class:`~cosmology.models.hde.HDE` takes the future event horizon as its infrared cutoff, which makes today's dark-energy density depend on the entire future expansion history -- unusual as physics, and the thing most often objected to. Ricci dark energy takes a *local* curvature scale instead, rho_DE = 3 gamma M_p^2 (Hdot + 2 H^2), which is the Ricci scalar of a spatially flat universe up to a constant. Same holographic idea, no reference to the future. Unlike HDE this has a closed form. Solving the Friedmann equation with that density gives E(z)^2 = A (1+z)^3 + (1 - A) (1+z)^(4 - 2/gamma), A = 2 Omega_m / (2 - gamma), so the dark sector behaves as a power law in ``(1+z)`` whose exponent the single parameter ``gamma_rde`` sets. Flatness fixes the normalization: ``E(0) = 1`` identically, for every ``gamma``. Reading the exponent is the quickest way to see what the model does. ``gamma = 1/2`` gives ``(1+z)^0``, a constant dark-energy density -- so the model is LCDM there, though with an *effective* matter density ``A = (4/3) Omega_m`` rather than ``Omega_m``, because part of the Ricci density scales like matter. Below that the exponent is negative and the dark energy *grows* into the future; above it the density falls with redshift like a decaying component. Fits have consistently preferred ``gamma`` slightly above 1/2 -- and, more awkwardly, a low matter density: this model wants ``Omega_m ~ 0.22`` where everything else in this library wants 0.31, which is its main observational problem rather than a detail. Caveats ------- Its perturbations are outside what a standard Boltzmann code solves, so it is refused by :class:`~cosmology.boltzmann.CAMBBackend`; use the compressed distance priors for its CMB constraint. Flat universes only, for the same reason as :class:`~cosmology.models.hde.HDE`: the closed form above is derived from the flat Friedmann equation, and curvature changes the Ricci scalar the cutoff is built from rather than adding a term. References ---------- Gao, Chen, Shen & Saridakis (2009), Phys. Rev. D 79, 043511, `arXiv:0712.1394 <https://arxiv.org/abs/0712.1394>`_. """ MODEL_NAME = "RDE" FLAT_ONLY = True MODEL_LABEL = "RDE" EXTRA_PARAMS = { "gamma_rde": { "default": 0.45, "bounds": (0.15, 1.2), "label": r"$\gamma$", }, } # --------------------------------------------------------- def _check_flat(self): if abs(self.Omega_k) > 1.0e-12: raise ValueError( f"RDE is implemented for a flat universe only, but " f"Omega_k = {self.Omega_k:.4g}. The closed form it " f"uses is derived from the flat Friedmann equation, " f"and curvature changes the Ricci scalar the cutoff " f"is built from rather than adding a term.", ) # --------------------------------------------------------- @property def _amplitude(self) -> float: """ ``A = 2 Omega_m / (2 - gamma)``, the weight of the matter-like term. """ gamma = float(self.params.gamma_rde) if gamma <= 0.0 or abs(2.0 - gamma) < 1.0e-12: raise ValueError( f"RDE needs 0 < gamma != 2, got " f"gamma_rde = {gamma:.4g}.", ) return 2.0 * self.Omega_m / (2.0 - gamma) @property def _exponent(self) -> float: """ ``4 - 2/gamma``: the power of ``(1+z)`` the dark sector follows. Zero at ``gamma = 1/2``, which is a cosmological constant. """ return 4.0 - 2.0 / float(self.params.gamma_rde) # --------------------------------------------------------- def Omega_de(self, z: Redshift) -> Array: r""" Dark-energy density in units of today's critical density -- ``E(z)^2 - Omega_m (1+z)^3``, as every other model here returns, so that ``Omega_de(0) = 1 - Omega_m``. Not the same as the second term of :meth:`E`. Ricci dark energy carries a piece that *scales like matter*: the coefficient of ``(1+z)^3`` in ``E^2`` is ``A = 2 Omega_m / (2 - gamma)``, which exceeds ``Omega_m`` for ``0 < gamma < 2``. Reading the two terms of ``E^2`` as "matter" and "dark energy" would therefore misattribute ``(A - Omega_m)(1+z)^3`` -- about a third of the matter density at the fitted ``gamma`` -- and would break ``Omega_de(0) = 1 - Omega_m``, which the recombination module relies on. """ self._check_flat() z = np.asarray(z, dtype=float) return self.E(z) ** 2 - self.Omega_m * cube(1.0 + z) # --------------------------------------------------------- def E(self, z: Redshift) -> Array: self._check_flat() z = np.asarray(z, dtype=float) amplitude = self._amplitude return np.sqrt( amplitude * cube(1.0 + z) + (1.0 - amplitude) * (1.0 + z) ** self._exponent ) # --------------------------------------------------------- def dEdz(self, z: Redshift) -> Array: z = np.asarray(z, dtype=float) amplitude = self._amplitude exponent = self._exponent return ( 3.0 * amplitude * (1.0 + z) ** 2 + (1.0 - amplitude) * exponent * (1.0 + z) ** (exponent - 1.0) ) / (2.0 * self.E(z))