def __new__(cls, value, unit=None, observer=None, target=None, radial_velocity=None, redshift=None, **kwargs): obj = super().__new__(cls, value, unit=unit, **kwargs) # There are two main modes of operation in this class. Either the # observer and target are both defined, in which case the radial # velocity and redshift are automatically computed from these, or # only one of the observer and target are specified, along with a # manually specified radial velocity or redshift. So if a target and # observer are both specified, we can't also accept a radial velocity # or redshift. if target is not None and observer is not None: if radial_velocity is not None or redshift is not None: raise ValueError( "Cannot specify radial velocity or redshift if both " "target and observer are specified") # We only deal with redshifts here and in the redshift property. # Otherwise internally we always deal with velocities. if redshift is not None: if radial_velocity is not None: raise ValueError( "Cannot set both a radial velocity and redshift") redshift = u.Quantity(redshift) # For now, we can't specify redshift=u.one in quantity_input above # and have it work with plain floats, but if that is fixed, for # example as in https://github.com/astropy/astropy/pull/10232, we # can remove the check here and add redshift=u.one to the decorator if not redshift.unit.is_equivalent(u.one): raise u.UnitsError('redshift should be dimensionless') radial_velocity = redshift.to(u.km / u.s, u.doppler_redshift()) # If we're initializing from an existing SpectralCoord, keep any # parameters that aren't being overridden if observer is None: observer = getattr(value, 'observer', None) if target is None: target = getattr(value, 'target', None) # As mentioned above, we should only specify the radial velocity # manually if either or both the observer and target are not # specified. if observer is None or target is None: if radial_velocity is None: radial_velocity = getattr(value, 'radial_velocity', None) obj._radial_velocity = radial_velocity obj._observer = cls._validate_coordinate(observer, label='observer') obj._target = cls._validate_coordinate(target, label='target') return obj
def redshift(self): """ Redshift of target relative to observer. Calculated from the radial velocity. Returns ------- `astropy.units.Quantity` Redshift of target. """ return self.radial_velocity.to(u.dimensionless_unscaled, u.doppler_redshift())
def redshift(self): """ Redshift of target relative to observer. Calculated from the radial velocity. Returns ------- float Redshift of target. """ return self.radial_velocity.to(u.dimensionless_unscaled, u.doppler_redshift()).value
def test_doppler_redshift_no_cosmology(): from astropy.cosmology.units import redshift with pytest.raises(u.UnitConversionError, match='not convertible'): (0 * (u.km / u.s)).to(redshift, u.doppler_redshift())
def test_doppler_redshift(z, rv_ans): z_in = z * u.dimensionless_unscaled rv_out = z_in.to(u.km / u.s, u.doppler_redshift()) z_out = rv_out.to(u.dimensionless_unscaled, u.doppler_redshift()) assert_quantity_allclose(rv_out, rv_ans) assert_quantity_allclose(z_out, z_in) # Check roundtrip
def with_radial_velocity_shift(self, target_shift=None, observer_shift=None): """ Apply a velocity shift to this spectral coordinate. The shift can be provided as a redshift (float value) or radial velocity (`~astropy.units.Quantity` with physical type of 'speed'). Parameters ---------- target_shift : float or `~astropy.units.Quantity` ['speed'] Shift value to apply to current target. observer_shift : float or `~astropy.units.Quantity` ['speed'] Shift value to apply to current observer. Returns ------- `SpectralCoord` New spectral coordinate with the target/observer velocity changed to incorporate the shift. This is always a new object even if ``target_shift`` and ``observer_shift`` are both `None`. """ if observer_shift is not None and (self.target is None or self.observer is None): raise ValueError("Both an observer and target must be defined " "before applying a velocity shift.") for arg in [ x for x in [target_shift, observer_shift] if x is not None ]: if isinstance(arg, u.Quantity) and not arg.unit.is_equivalent( (u.one, KMS)): raise u.UnitsError("Argument must have unit physical type " "'speed' for radial velocty or " "'dimensionless' for redshift.") # The target or observer value is defined but is not a quantity object, # assume it's a redshift float value and convert to velocity if target_shift is None: if self._observer is None or self._target is None: return self.replicate() target_shift = 0 * KMS else: target_shift = u.Quantity(target_shift) if target_shift.unit.physical_type == 'dimensionless': target_shift = target_shift.to(u.km / u.s, u.doppler_redshift()) if self._observer is None or self._target is None: return self.replicate(value=_apply_relativistic_doppler_shift( self, target_shift), radial_velocity=self.radial_velocity + target_shift) if observer_shift is None: observer_shift = 0 * KMS else: observer_shift = u.Quantity(observer_shift) if observer_shift.unit.physical_type == 'dimensionless': observer_shift = observer_shift.to(u.km / u.s, u.doppler_redshift()) target_icrs = self._target.transform_to(ICRS()) observer_icrs = self._observer.transform_to(ICRS()) pos_hat = SpectralCoord._normalized_position_vector( observer_icrs, target_icrs) target_velocity = _get_velocities(target_icrs) + target_shift * pos_hat observer_velocity = _get_velocities( observer_icrs) + observer_shift * pos_hat target_velocity = CartesianDifferential(target_velocity.xyz) observer_velocity = CartesianDifferential(observer_velocity.xyz) new_target = (target_icrs.realize_frame( target_icrs.cartesian.with_differentials( target_velocity)).transform_to(self._target)) new_observer = (observer_icrs.realize_frame( observer_icrs.cartesian.with_differentials( observer_velocity)).transform_to(self._observer)) init_obs_vel = self._calculate_radial_velocity(observer_icrs, target_icrs, as_scalar=True) fin_obs_vel = self._calculate_radial_velocity(new_observer, new_target, as_scalar=True) new_data = _apply_relativistic_doppler_shift( self, fin_obs_vel - init_obs_vel) return self.replicate(value=new_data, observer=new_observer, target=new_target)