Source code for astropy.coordinates.distances

# Licensed under a 3-clause BSD style license - see LICENSE.rst

"""
This module contains the classes and utility functions for distance and
cartesian coordinates.
"""

import warnings

import numpy as np

from astropy import units as u
from astropy.utils.exceptions import AstropyWarning
from .angles import Angle

__all__ = ['Distance']


__doctest_requires__ = {'*': ['scipy.integrate']}


[docs]class Distance(u.SpecificTypeQuantity): """ A one-dimensional distance. This can be initialized in one of four ways: * A distance ``value`` (array or float) and a ``unit`` * A `~astropy.units.Quantity` object * A redshift and (optionally) a cosmology. * Providing a distance modulus Parameters ---------- value : scalar or `~astropy.units.Quantity`. The value of this distance. unit : `~astropy.units.UnitBase` The units for this distance, *if* ``value`` is not a `~astropy.units.Quantity`. Must have dimensions of distance. z : float A redshift for this distance. It will be converted to a distance by computing the luminosity distance for this redshift given the cosmology specified by ``cosmology``. Must be given as a keyword argument. cosmology : ``Cosmology`` or `None` A cosmology that will be used to compute the distance from ``z``. If `None`, the current cosmology will be used (see `astropy.cosmology` for details). distmod : float or `~astropy.units.Quantity` The distance modulus for this distance. Note that if ``unit`` is not provided, a guess will be made at the unit between AU, pc, kpc, and Mpc. parallax : `~astropy.units.Quantity` or `~astropy.coordinates.Angle` The parallax in angular units. dtype : `~numpy.dtype`, optional See `~astropy.units.Quantity`. copy : bool, optional See `~astropy.units.Quantity`. order : {'C', 'F', 'A'}, optional See `~astropy.units.Quantity`. subok : bool, optional See `~astropy.units.Quantity`. ndmin : int, optional See `~astropy.units.Quantity`. allow_negative : bool, optional Whether to allow negative distances (which are possible is some cosmologies). Default: ``False``. Raises ------ `~astropy.units.UnitsError` If the ``unit`` is not a distance. ValueError If value specified is less than 0 and ``allow_negative=False``. If ``z`` is provided with a ``unit`` or ``cosmology`` is provided when ``z`` is *not* given, or ``value`` is given as well as ``z``. Examples -------- >>> from astropy import units as u >>> from astropy import cosmology >>> from astropy.cosmology import WMAP5, WMAP7 >>> cosmology.set_current(WMAP7) >>> d1 = Distance(10, u.Mpc) >>> d2 = Distance(40, unit=u.au) >>> d3 = Distance(value=5, unit=u.kpc) >>> d4 = Distance(z=0.23) >>> d5 = Distance(z=0.23, cosmology=WMAP5) >>> d6 = Distance(distmod=24.47) >>> d7 = Distance(Distance(10 * u.Mpc)) >>> d8 = Distance(parallax=21.34*u.mas) """ _equivalent_unit = u.m _include_easy_conversion_members = True def __new__(cls, value=None, unit=None, z=None, cosmology=None, distmod=None, parallax=None, dtype=None, copy=True, order=None, subok=False, ndmin=0, allow_negative=False): if z is not None: if value is not None or distmod is not None: raise ValueError('Should given only one of `value`, `z` ' 'or `distmod` in Distance constructor.') if cosmology is None: from astropy.cosmology import default_cosmology cosmology = default_cosmology.get() value = cosmology.luminosity_distance(z) # Continue on to take account of unit and other arguments # but a copy is already made, so no longer necessary copy = False else: if cosmology is not None: raise ValueError('A `cosmology` was given but `z` was not ' 'provided in Distance constructor') value_msg = ('Should given only one of `value`, `z`, `distmod`, or ' '`parallax` in Distance constructor.') n_not_none = np.sum([x is not None for x in [value, z, distmod, parallax]]) if n_not_none > 1: raise ValueError(value_msg) if distmod is not None: value = cls._distmod_to_pc(distmod) if unit is None: # if the unit is not specified, guess based on the mean of # the log of the distance meanlogval = np.log10(value.value).mean() if meanlogval > 6: unit = u.Mpc elif meanlogval > 3: unit = u.kpc elif meanlogval < -3: # ~200 AU unit = u.AU else: unit = u.pc # Continue on to take account of unit and other arguments # but a copy is already made, so no longer necessary copy = False elif parallax is not None: value = parallax.to_value(u.pc, equivalencies=u.parallax()) unit = u.pc # Continue on to take account of unit and other arguments # but a copy is already made, so no longer necessary copy = False if np.any(parallax < 0): if allow_negative: warnings.warn( "Negative parallaxes are converted to NaN " "distances even when `allow_negative=True`, " "because negative parallaxes cannot be transformed " "into distances. See discussion in this paper: " "https://arxiv.org/abs/1507.02105", AstropyWarning) else: raise ValueError("Some parallaxes are negative, which " "are notinterpretable as distances. " "See the discussion in this paper: " "https://arxiv.org/abs/1507.02105 . " "If you want parallaxes to pass " "through, with negative parallaxes " "instead becoming NaN, use the " "`allow_negative=True` argument.") elif value is None: raise ValueError('None of `value`, `z`, `distmod`, or ' '`parallax` were given to Distance ' 'constructor') # now we have arguments like for a Quantity, so let it do the work distance = super().__new__( cls, value, unit, dtype=dtype, copy=copy, order=order, subok=subok, ndmin=ndmin) if not allow_negative and np.any(distance.value < 0): raise ValueError("Distance must be >= 0. Use the argument " "'allow_negative=True' to allow negative values.") return distance @property def z(self): """Short for ``self.compute_z()``""" return self.compute_z()
[docs] def compute_z(self, cosmology=None): """ The redshift for this distance assuming its physical distance is a luminosity distance. Parameters ---------- cosmology : ``Cosmology`` or `None` The cosmology to assume for this calculation, or `None` to use the current cosmology (see `astropy.cosmology` for details). Returns ------- z : float The redshift of this distance given the provided ``cosmology``. """ if cosmology is None: from astropy.cosmology import default_cosmology cosmology = default_cosmology.get() from astropy.cosmology import z_at_value return z_at_value(cosmology.luminosity_distance, self, ztol=1.e-10)
@property def distmod(self): """The distance modulus as a `~astropy.units.Quantity`""" val = 5. * np.log10(self.to_value(u.pc)) - 5. return u.Quantity(val, u.mag, copy=False) @classmethod def _distmod_to_pc(cls, dm): dm = u.Quantity(dm, u.mag) return cls(10 ** ((dm.value + 5) / 5.), u.pc, copy=False) @property def parallax(self): """The parallax angle as an `~astropy.coordinates.Angle` object""" return Angle(self.to(u.milliarcsecond, u.parallax()))