Source code for wcosmo.astropy

"""
:code:`astropy`-like cosmology objects to work with :code:`wcosmo`.

By default, this provides a drop-in replacement for :code:`astropy.FlatLambdaCDM`
and :code:`astropy.FlatwCDM` cosmology objects along with all of the pre-defined
astropy cosmologies.

By changing the backend and disabling units, these classes can then be used with
:code:`numpy`, :code:`jax`, or :code:`cupy`.
"""

import sys
from dataclasses import dataclass, field

import astropy.cosmology as _acosmo
import numpy as xp

from .utils import autodoc, method_autodoc, strip_units
from .wcosmo import *

USE_UNITS = True

__all__ = [
    "FlatwCDM",
    "FlatLambdaCDM",
    "available",
] + list(_acosmo.available)


[docs] class WCosmoMixin: """ Mixin to provide access to the :code:`wcosmo` functionality to :code:`astropy` cosmology objects. We clobber all units to ensure consistent behavior across backends. Notes ----- The following methods are not compatible with non-:code:`numpy` backends: - :code:`kpc_comoving_per_arcmin` - :code:`kpc_proper_per_arcmin` - :code:`nu_relative_density` These methods internally coerce the input to :code:`numpy` arrays if the backend supports implicit conversion. Additionally, we don't overwrite the various integrands, protected methods, and other utility methods, e.g., :code:`clone`. We include the following methods that are not present in :code:`astropy`: - :code:`dLdH` - derivative of the luminosity distance w.r.t. the Hubble distance - :code:`dDLdz` - Jacobian for the conversion of luminosity distance to redshift, see :func:`dDLdz` - :code:`detector_to_source_frame` - convert masses and luminosity distance from the detector frame to the source frame, also returns the jacobian, see :func:`detector_to_source_frame` - :code:`source_to_detector_frame` - convert masses and redshift from the source frame to the detector frame, see :func:`source_to_detector_frame` """ def __getattribute__(self, name): value = super().__getattribute__(name) if not USE_UNITS: value = strip_units(value) return value @property def _kwargs(self): return {"H0": self.H0, "Om0": self.Om0, "w0": self.w0} @property @method_autodoc(alt=hubble_time) def hubble_time(self): return hubble_time(self.H0) @property @method_autodoc(alt=hubble_distance) def hubble_distance(self): return hubble_distance(self.H0)
[docs] @method_autodoc(alt=luminosity_distance) def luminosity_distance(self, z): return luminosity_distance(z, **self._kwargs)
[docs] @autodoc def dLdH(self, z): r""" Derivative of the luminosity distance w.r.t. the Hubble distance. .. math:: \frac{{dd_L}}{{dd_H}} = \frac{{d_L}}{{d_H}} Parameters ---------- {z} Returns ------- array_like: The derivative of the luminosity distance w.r.t., the Hubble distance """ return self.luminosity_distance(z) / self.hubble_distance
[docs] @method_autodoc(alt=dDLdz) def dDLdz(self, z): return dDLdz(z, **self._kwargs)
[docs] @method_autodoc(alt=differential_comoving_volume) def differential_comoving_volume(self, z): return differential_comoving_volume(z, **self._kwargs)
[docs] @method_autodoc(alt=detector_to_source_frame) def detector_to_source_frame(self, m1z, m2z, dL, zmin=1e-4, zmax=100): return detector_to_source_frame( m1z, m2z, dL, **self._kwargs, zmin=zmin, zmax=zmax )
[docs] @method_autodoc(alt=source_to_detector_frame) def source_to_detector_frame(self, m1, m2, z): return source_to_detector_frame(m1, m2, z, **self._kwargs)
[docs] @method_autodoc(alt=efunc) def efunc(self, z): return efunc(z, self.Om0, self.w0)
[docs] @method_autodoc(alt=inv_efunc) def inv_efunc(self, z): return inv_efunc(z, self.Om0, self.w0)
[docs] @method_autodoc(alt=hubble_parameter) def H(self, z): return hubble_parameter(z, **self._kwargs)
[docs] @method_autodoc(alt=comoving_distance) def comoving_distance(self, z): return comoving_distance(z, **self._kwargs)
[docs] @method_autodoc(alt=comoving_volume) def comoving_volume(self, z): return comoving_volume(z, **self._kwargs)
[docs] @method_autodoc(alt=lookback_time) def lookback_time(self, z): return lookback_time(z, **self._kwargs)
[docs] @method_autodoc(alt=absorption_distance) def absorption_distance(self, z): return absorption_distance(z, self.Om0, self.w0)
[docs] @autodoc def age(self, z, zmax=1e5): """ Compute the age of the universe at redshift z. Parameters ---------- {z} zmax: float, optional The maximum redshift to consider, default is 1e5 Returns ------- age: array_like The age of the universe in Gyr """ return self.lookback_time(zmax) - self.lookback_time(z)
comoving_transverse_distance = comoving_distance
[docs] @autodoc def distmod(self, z): """ Compute the distance modulus at redshift z. Parameters ---------- {z} Returns ------- distmod: array_like The distance modulus (units: mag) """ distance = strip_units(self.luminosity_distance(z)) return 5 * xp.log10(xp.abs(distance)) + 25
[docs] @autodoc def de_density_scale(self, z): """ Dark energy density at redshift z. Parameters ---------- {z} Returns ------- rho_de: array_like The dark energy density at redshift z """ return (z + 1) ** (3 * (1 + self.w0))
[docs] @dataclass(frozen=True) class FlatwCDM(WCosmoMixin, _acosmo.FlatwCDM): pass
[docs] @dataclass(frozen=True) class FlatLambdaCDM(WCosmoMixin, _acosmo.FlatLambdaCDM): w0: float = field(init=False, default=-1)
def __getattr__(name): alt = _acosmo.__getattr__(name) cosmo = FlatLambdaCDM(**alt.parameters) setattr(sys.modules[__name__], name, cosmo) return cosmo class _Available: def keys(self): return ("FlatLambdaCDM", "FlatwCDM") + _acosmo.available def __getitem__(self, key): return getattr(sys.modules[__name__], key) def __repr__(self): return repr(self.keys()) available = _Available()