"""Utility functions for handling redshift and cosmological calculations."""
from functools import partial
import astropy.cosmology.units as cu
import numpy as np
from astropy import units as u
from lightcurvelynx.base_models import FunctionNode
[docs]
def obs_to_rest_times_waves(observer_frame_times, observer_frame_wavelengths, redshift, t0):
"""Calculate the rest frame times and wavelengths needed to give user the observer frame times
and wavelengths (given the redshift).
Parameters
----------
observer_frame_times : numpy.ndarray
The times at which the observation is made.
observer_frame_wavelengths : numpy.ndarray
The wavelengths at which the observation is made (in Angstroms).
redshift : float
The redshift of the object.
t0 : float
The reference epoch for the given object.
Returns
-------
tuple of (numpy.ndarray, numpy.ndarray)
The rest frame times and wavelengths needed to generate the rest frame flux densities,
which will later be redshifted back to observer frame flux densities at the observer frame
times and wavelengths.
"""
if redshift < 0:
raise ValueError("Redshift must be non-negative.")
observed_times_rel_to_t0 = observer_frame_times - t0
rest_frame_times_rel_to_t0 = observed_times_rel_to_t0 / (1 + redshift)
rest_frame_times = rest_frame_times_rel_to_t0 + t0
rest_frame_wavelengths = observer_frame_wavelengths / (1 + redshift)
return (rest_frame_times, rest_frame_wavelengths)
[docs]
def rest_to_obs_flux(flux_density, redshift):
"""Convert rest-frame flux to obs-frame flux.
The (1+redshift) factor is applied to preserve bolometric flux.
The rest-frame flux is defined as ``F_nu = L_nu / 4*pi*D_L**2``,
where ``D_L`` is the luminosity distance.
Parameters
----------
flux_density : numpy.ndarray
A length T X N matrix of flux density values (in nJy).
redshift : float
The redshift of the object associated with given flux densities.
Returns
-------
flux_density : numpy.ndarray
The observer frame flux (in nJy).
"""
if redshift < 0:
raise ValueError("Redshift must be non-negative.")
# Note that the multiplication by (1+z) is due to the fact we are working in f_nu units,
# instead of f_lambda units and may be unintuitive for users who are used to working in f_lambda
# units. This factor can be derived by equaling the integrated flux in f_nu unit before and after
# redshift is applied.
return flux_density * (1 + redshift)
[docs]
def redshift_to_distance(redshift, cosmology):
"""Compute a source's luminosity distance given its redshift and a
specified cosmology using astropy's redshift_distance().
Parameters
----------
redshift : float or numpy.ndarray
The redshift value.
cosmology : astropy.cosmology
The cosmology specification.
Returns
-------
distance : float
The luminosity distance (in pc)
"""
if np.any(np.atleast_1d(redshift) < 0):
raise ValueError("Redshift must be non-negative.")
if cosmology is None:
raise ValueError("Cosmology must be specified.")
z = redshift * cu.redshift
distance = z.to(u.pc, cu.redshift_distance(cosmology, kind="luminosity"))
return distance.value
[docs]
class RedshiftDistFunc(FunctionNode):
"""A wrapper class for the redshift_to_distance() function.
Attributes
----------
cosmology : astropy.cosmology
The cosmology specification.
Parameters
----------
redshift : function or constant
The function or constant providing the redshift value.
cosmology : astropy.cosmology
The cosmology specification.
**kwargs : dict, optional
Any additional keyword arguments.
"""
def __init__(self, redshift, cosmology, **kwargs):
# Create a partial function with the cosmology fixed. We do this
# so that cosmology is not added as a node parameter.
func = partial(redshift_to_distance, cosmology=cosmology)
func.__name__ = "redshift_to_distance"
func.__doc__ = redshift_to_distance.__doc__
# Call the super class's constructor with the needed information.
super().__init__(
func=func,
redshift=redshift,
**kwargs,
)