Ejemplo n.º 1
0
def validate_inputs(p_input,
                    t_input,
                    pressure_grid,
                    temperature_grid,
                    exceptions=None):
    '''
    Function to validate the input data before the model estimation.

    Parameters
    ----------
    p_input: float or np.array
            pressure to use for the estimation.
            This should be a :class:`astropy.units.Quantity` with specified units, if not `Pa` are assumed as units.
            Can either be a single value or an array.
    t_input: float or np.array
            temperature to use for the estimation.
            This should be a :class:`astropy.units.Quantity` with specified units, if not `K` are assumed as units.
            Can either be a single value or an array.
    pressure_grid: astropy.units.Quantity
        data pressure grid in `si` units
    temperature_grid: astropy.units.Quantity
        data temperature grid in `si` units
    exceptions: str
        data format exceptions. Default is None.

    Returns
    -------
    np.array
        pressure to use for the estimation.
    np.array
        temperature to use for the estimation.

    Raises
    -------
        ValueError
            if input temperature of pressure are out of the data grid
        u.UnitConversionError:
            if the input units cannot be converted to `si`
    '''
    if exceptions is None:
        p_input, t_input = _validate_inputs_units(p_input, t_input)
        p_input, t_input = _validate_inputs_size(p_input, t_input)
        _validate_inputs_boundaries(p_input, t_input, pressure_grid,
                                    temperature_grid)

    elif exceptions == 'rayleigh':
        if p_input:
            warnings.warn('Input pressure not needed for Rayleigh scattering.',
                          RuntimeWarning)
            p_input = None
        if not hasattr(t_input, 'unit'):
            t_input *= u.K
        else:
            try:
                t_input = t_input.to(u.K)
            except u.UnitConversionError:
                raise u.UnitConversionError('invalid temperature unit')
        if t_input.size == 1:
            t_input = [t_input.value] * t_input.unit
    return p_input, t_input
Ejemplo n.º 2
0
 def _check_type(self: Any, att: attr.Attribute, val: Any):
     if not isinstance(val, un.Quantity):
         raise UnitError(f"{att.name} must be an astropy Quantity!")
     if val.unit.physical_type != unit:
         raise un.UnitConversionError(
             f"{att.name} must have physical type of '{unit}'. "
             f"Got '{val.unit.physical_type}'")
Ejemplo n.º 3
0
def _validate_output_units(estimate, units):
    '''
    It checks the estimate units and convert them into the desired units, if possible.

    Parameters
    ----------
    estimate:  astropy.units.Quantity
        estimated opacity
    units: str or astropy.units, optional
        indicates the output units system.
        If `si` the opacity is returned as :math:`m^2/kg`, if `cgs` is returned as :math:`cm^2/g`.
        Instead of a string, you can also indicate the units using astropy.units. Units is `si` by default.

    Returns
    -------
    astropy.units.Quantity

    Raises
    ------
    astropy.units.UnitConversionError
        if invalid units are indicated
    '''
    try:
        if units == 'si':
            return estimate.si
        elif units == 'cgs':
            return estimate.cgs
        else:
            return estimate.to(units)
    except u.UnitConversionError:
        raise u.UnitConversionError('invalid output units')
Ejemplo n.º 4
0
 def wrapper(*args, **kwargs):
     if len(args) < 3:  # orbit input
         return method(*args, **kwargs)
     ro = kwargs.get('ro', None)
     if ro is None and hasattr(args[0], '_ro'):
         ro = args[0]._ro
     if _APY_LOADED and isinstance(ro, units.Quantity):
         ro = ro.to(units.kpc).value
     vo = kwargs.get('vo', None)
     if vo is None and hasattr(args[0], '_vo'):
         vo = args[0]._vo
     if _APY_LOADED and isinstance(vo, units.Quantity):
         vo = vo.to(units.km / units.s).value
     # Loop through args
     newargs = ()
     for ii in range(len(args)):
         if _APY_LOADED and isinstance(args[ii], units.Quantity):
             try:
                 targ = args[ii].to(units.kpc).value / ro
             except units.UnitConversionError:
                 try:
                     targ = args[ii].to(units.km / units.s).value / vo
                 except units.UnitConversionError:
                     try:
                         targ = args[ii].to(units.rad).value
                     except units.UnitConversionError:
                         raise units.UnitConversionError(
                             "Input units not understood")
             newargs = newargs + (targ, )
         else:
             newargs = newargs + (args[ii], )
     args = newargs
     return method(*args, **kwargs)
Ejemplo n.º 5
0
    def _check_unit(self, att, val):
        if not isinstance(val, un.Quantity):
            raise UnitError(f"{att.name} must be an astropy Quantity!")

        if not val.unit.is_equivalent(unit, equivalencies):
            raise un.UnitConversionError(
                f"{att.name} not convertible to {unit}. Got {val.unit}")
Ejemplo n.º 6
0
def _check_relativistic(V, funcname, betafrac=0.1):
    r"""Raise UserWarnings if a velocity is relativistic or superrelativistic

    Parameters
    ----------
    V : Quantity
        A velocity

    funcname : string
        The name of the original function to be printed in the error messages.

    betafrac : float
        The minimum fraction of the speed of light that will raise a
        UserWarning

    Raises
    ------
    TypeError
        If V is not a Quantity

    UnitConversionError
        If V is not in units of velocity

    ValueError
        If V contains any NaNs

    UserWarning
        If V is greater than betafrac times the speed of light

    Examples
    --------
    >>> from astropy import units as u
    >>> _check_relativistic(1*u.m/u.s, 'function_calling_this')

    """

    errmsg = ("V must be a Quantity with units of velocity in"
              "_check_relativistic")

    if not isinstance(V, u.Quantity):
        raise TypeError(errmsg)

    if V.si.unit != u.m / u.s:
        raise u.UnitConversionError(errmsg)

    if np.any(np.isnan(V.value)):
        raise ValueError("V includes NaNs in " + funcname)

    beta = np.max(np.abs((V / c).value))

    if beta == np.inf:
        raise UserWarning(funcname + " is yielding an infinite velocity.")
    elif beta >= 1:
        raise UserWarning(funcname + " is yielding a velocity that is " +
                          str(round(beta, 3)) + " times the speed of light.")
    elif beta >= betafrac:
        raise UserWarning(funcname + " is yielding a velocity that is " +
                          str(round(beta * 100, 3)) + "% of the speed of " +
                          "light. Relativistic effects may be important.")
Ejemplo n.º 7
0
    def quantity(self, val):
        val = u.Quantity(val)

        if not val.unit.is_equivalent(self.unit):
            raise u.UnitConversionError(
                f"Unit must be equivalent to {self.unit}")

        self.value = val.value
        self.unit = val.unit
Ejemplo n.º 8
0
def _validate_inputs_units(p_input, t_input):
    '''
    It checks the units in input pressure an temperature, and return them into `SI`.

    Parameters
    ----------
    p_input: float or np.array
            pressure to use for the estimation.
            This should be a :class:`astropy.units.Quantity` with specified units, if not `Pa` are assumed as units.
            Can either be a single value or an array.
    t_input: float or np.array
            temperature to use for the estimation.
            This should be a :class:`astropy.units.Quantity` with specified units, if not `K` are assumed as units.
            Can either be a single value or an array.

    Returns
    -------
    float or np.array
        p_input
    float or np.array
        t_input

    Raises
    ------
    astropy.units.UnitConversionError
        if invalid units are used
    '''

    if not hasattr(p_input, 'unit'):
        p_input *= u.Pa
    else:
        try:
            p_input = p_input.to(u.Pa).si
        except u.UnitConversionError:
            raise u.UnitConversionError('invalid pressure unit')
    if not hasattr(t_input, 'unit'):
        t_input *= u.K
    else:
        try:
            t_input = t_input.to(u.K)
        except u.UnitConversionError:
            raise u.UnitConversionError('invalid temperature unit')
    return p_input, t_input
Ejemplo n.º 9
0
    def _check_val_type(self, val1, val2):
        """Input value validation, typically overridden by derived classes"""
        # val1 cannot contain nan, but val2 can contain nan
        ok1 = (val1.dtype == np.double and np.all(np.isfinite(val1))
               or val1.size == 0)
        ok2 = val2 is None or (val2.dtype == np.double and
                               not np.any(np.isinf(val2))) or val2.size == 0
        if not (ok1 and ok2):
            raise TypeError(
                'Input values for {} class must be finite doubles'.format(
                    self.name))

        if getattr(val1, 'unit', None) is not None:
            # Convert any quantity-likes to days first, attempting to be
            # careful with the conversion, so that, e.g., large numbers of
            # seconds get converted without loosing precision because
            # 1/86400 is not exactly representable as a float.
            val1 = u.Quantity(val1, copy=False)
            if val2 is not None:
                val2 = u.Quantity(val2, copy=False)

            try:
                val1, val2 = quantity_day_frac(val1, val2)
            except u.UnitsError:
                raise u.UnitConversionError(
                    "only quantities with time units can be "
                    "used to instantiate Time instances.")
            # We now have days, but the format may expect another unit.
            # On purpose, multiply with 1./day_unit because typically it is
            # 1./erfa.DAYSEC, and inverting it recovers the integer.
            # (This conversion will get undone in format's set_jds, hence
            # there may be room for optimizing this.)
            factor = 1. / getattr(self, 'unit', 1.)
            if factor != 1.:
                val1, carry = two_product(val1, factor)
                carry += val2 * factor
                val1, val2 = two_sum(val1, carry)

        elif getattr(val2, 'unit', None) is not None:
            raise TypeError('Cannot mix float and Quantity inputs')

        if val2 is None:
            val2 = np.zeros_like(val1)

        def asarray_or_scalar(val):
            """
            Remove ndarray subclasses since for jd1/jd2 we want a pure ndarray
            or a Python or numpy scalar.
            """
            return np.asarray(val) if isinstance(val, np.ndarray) else val

        return asarray_or_scalar(val1), asarray_or_scalar(val2)
Ejemplo n.º 10
0
    def to_coord_frame(self, frame, galactocentric_frame=None, **kwargs):
        """
        Transform the orbit from Galactocentric, cartesian coordinates to
        Heliocentric coordinates in the specified Astropy coordinate frame.

        Parameters
        ----------
        frame : :class:`~astropy.coordinates.BaseCoordinateFrame`
            The class or frame instance specifying the desired output frame.
            For example, :class:`~astropy.coordinates.ICRS`.
        galactocentric_frame : :class:`~astropy.coordinates.Galactocentric`
            This is the assumed frame that the position and velocity of this
            object are in. The ``Galactocentric`` instand should have parameters
            specifying the position and motion of the sun in the Galactocentric
            frame, but no data.

        Returns
        -------
        c : :class:`~astropy.coordinates.BaseCoordinateFrame`
            An instantiated coordinate frame containing the positions and
            velocities from this object transformed to the specified coordinate
            frame.

        """

        if self.ndim != 3:
            raise ValueError("Can only change representation for "
                             "ndim=3 instances.")

        if galactocentric_frame is None:
            galactocentric_frame = coord.Galactocentric()

        if 'vcirc' in kwargs or 'vlsr' in kwargs:
            import warnings
            warnings.warn(
                "Instead of passing in 'vcirc' and 'vlsr', specify "
                "these parameters to the input Galactocentric frame "
                "using the `galcen_v_sun` argument.", DeprecationWarning)

        pos_keys = list(self.pos_components.keys())
        vel_keys = list(self.vel_components.keys())
        if (getattr(self, pos_keys[0]).unit == u.one
                or getattr(self, vel_keys[0]).unit == u.one):
            raise u.UnitConversionError("Position and velocity must have "
                                        "dimensioned units to convert to a "
                                        "coordinate frame.")

        # first we need to turn the position into a Galactocentric instance
        gc_c = galactocentric_frame.realize_frame(
            self.pos.with_differentials(self.vel))
        c = gc_c.transform_to(frame)
        return c
Ejemplo n.º 11
0
 def dt_max(self, value: u.s):
     if not isinstance(value, u.Quantity):
         raise TypeError("dt_max must be a Quantity.")
     try:
         value = value.to(u.s)
     except u.UnitConversionError as exc:
         raise u.UnitConversionError("Invalid units for dt_max.") from exc
     if (hasattr(self, "_dt_input") and self.dt_input is not None
             and self.dt_input > value):
         raise ValueError("dt_max cannot be less the inputted time step.")
     if hasattr(self, "_dt_min") and self.dt_min > value:
         raise ValueError("dt_min cannot exceed dt_max.")
     self._dt_max = value
Ejemplo n.º 12
0
def data_unit_check(value, unit):
    '''
    Check that a value has a unit equivalent to the given unit. If no unit is
    attached, add the given unit to the value.
    '''

    if hasattr(value, 'unit'):
        if not value.unit.is_equivalent(unit):
            raise u.UnitConversionError("The given value does not have "
                                        "equivalent units.")
        return value.to(unit)

    else:
        return value * unit
Ejemplo n.º 13
0
    def __init__(self, K=1.15, F=0.03, D=1.8, amp=1., ro=None, vo=None):
        """
        NAME:

           __init__

        PURPOSE:

           Initialize a KGPotential

        INPUT:

           K= K parameter (= :math:`2\\pi \\Sigma_{\\mathrm{disk}}`; specify either as surface density or directly as force [i.e., including :math:`2\\pi G`]; can be Quantity)

           F= F parameter (= :math:`4\\pi\\rho_{\\mathrm{halo}}`; specify either as density or directly as second potential derivative [i.e., including :math:`4\\pi G`]; can be Quantity)

           D= D parameter (natural units or Quantity length units)

           amp - an overall amplitude

        OUTPUT:

           instance

        HISTORY:

           2010-07-12 - Written - Bovy (NYU)

        """
        linearPotential.__init__(self, amp=amp, ro=ro, vo=vo)
        D = conversion.parse_length(D, ro=self._ro)
        K = conversion.parse_force(K, ro=self._ro, vo=self._vo)
        if _APY_LOADED and isinstance(F, units.Quantity):
            try:
                F= F.to(units.Msun/units.pc**3).value\
                    /conversion.dens_in_msolpc3(self._vo,self._ro)*4.*numpy.pi
            except units.UnitConversionError:
                pass
        if _APY_LOADED and isinstance(F, units.Quantity):
            try:
                F= F.to(units.km**2/units.s**2/units.kpc**2).value\
                    *ro**2/vo**2
            except units.UnitConversionError:
                raise units.UnitConversionError(
                    "Units for F not understood; should be density")
        self._K = K
        self._F = F
        self._D = D
        self._D2 = self._D**2.
        self.hasC = True
Ejemplo n.º 14
0
    def roll_lags(self, value):

        # Needs to be a quantity with a unit
        if not hasattr(value, "unit"):
            raise ValueError("roll_lags must be an astropy.units.Quantity.")

        try:
            self._to_pixel(value)
        except u.UnitConversionError:
            raise u.UnitConversionError("Cannot convert given roll lags to "
                                        "pixel units. `roll_lags` must have"
                                        " pixel, angular, or physical (if a"
                                        " distance is given) units.")
        self._roll_lags = value
Ejemplo n.º 15
0
 def GMs(self,gms):
     if _APY_LOADED and isinstance(gms,units.Quantity):
         try:
             gms= gms.to(units.Msun).value\
                 /bovy_conversion.mass_in_msol(self._vo,self._ro)
         except units.UnitConversionError:
             # Try G x mass
             try:
                 gms= gms.to(units.pc*units.km**2/units.s**2)\
                     .value\
                     /bovy_conversion.mass_in_msol(self._vo,self._ro)\
                     /bovy_conversion._G
             except units.UnitConversionError:
                 raise units.UnitConversionError('GMs for %s should have units of mass or G x mass' % (type(self).__name__))
     self._ms= gms
     return None
Ejemplo n.º 16
0
def _get_representation_attrs(frame, units, kwargs):
    """
    Find instances of the "representation attributes" for specifying data
    for this frame.  Pop them off of kwargs, run through the appropriate class
    constructor (to validate and apply unit), and put into the output
    valid_kwargs.  "Representation attributes" are the frame-specific aliases
    for the underlying data values in the representation, e.g. "ra" for "lon"
    for many equatorial spherical representations, or "w" for "x" in the
    cartesian representation of Galactic.

    This also gets any *differential* kwargs, because they go into the same
    frame initializer later on.
    """
    frame_attr_names = frame.representation_component_names.keys()
    repr_attr_classes = frame.representation_type.attr_classes.values()

    valid_kwargs = {}
    for frame_attr_name, repr_attr_class, unit in zip(frame_attr_names,
                                                      repr_attr_classes,
                                                      units):
        value = kwargs.pop(frame_attr_name, None)
        if value is not None:
            try:
                valid_kwargs[frame_attr_name] = repr_attr_class(value,
                                                                unit=unit)
            except u.UnitConversionError as err:
                error_message = (
                    f"Unit '{unit}' ({unit.physical_type}) could not be applied to '{frame_attr_name}'. "
                    "This can occur when passing units for some coordinate components "
                    "when other components are specified as Quantity objects. "
                    "Either pass a list of units for all components (and unit-less coordinate data), "
                    "or pass Quantities for all components.")
                raise u.UnitConversionError(error_message) from err

    # also check the differentials.  They aren't included in the units keyword,
    # so we only look for the names.

    differential_type = frame.differential_type
    if differential_type is not None:
        for frame_name, repr_name in frame.get_representation_component_names(
                's').items():
            diff_attr_class = differential_type.attr_classes[repr_name]
            value = kwargs.pop(frame_name, None)
            if value is not None:
                valid_kwargs[frame_name] = diff_attr_class(value)

    return valid_kwargs
Ejemplo n.º 17
0
def sky_offset_from_parallax(source_coord, epoch, parallax=None):
    """Given a source position, determines the position offset in the sky
    due to the parallax motion at the given epoch.

    This code is based on a Fortran-77 script (fit_parallax_multi_4d.f) from
    Katharina Immer.

    Parameters:
        source_coord : astropy.coordinates.SkyCoord
            The sky coordinates of the source to evaluate.
        epoch : astropy.time.Time
            Epoch at which the offset position must be determined.
        parallax : unit.Quantity or float [OPTIONAL]
            Parallax of the source. Required only if distance is not provided
            in source_coord or needs to be ignored. If units are not provided,
            milliarcsecond are assumed.

    Returns
        offset : (ra_offset: float, dec_offset: float)
            Tuple with the expected offsets in RA and DEC (astropy.units are provided).
    """
    obliquity = 23.439 * u.deg
    # Number of dates from 2000.0
    n_days = epoch.jd - 2451545.0
    # Mean longitude of the Sun (corrected for aberration of light)
    long_sun = (280.460 + 0.9856474 * n_days) * u.deg
    # Cartesian coordinates of the Sun at the given epoch
    x = np.cos(long_sun)
    y = np.sin(long_sun) * np.cos(obliquity)
    z = np.sin(long_sun) * np.sin(obliquity)
    # Correcting for eccentricity of the Earth orbit
    long_earth = 2 * np.pi * (epoch.byear - 0.257)
    factor = 1.0 + 0.0167 * np.sin(long_earth)
    proj_ra = y * np.cos(source_coord.ra) - x * np.sin(source_coord.ra)
    proj_dec = z*np.cos(source_coord.dec) - x*np.cos(source_coord.ra)*np.sin(source_coord.dec)\
               - y*np.sin(source_coord.ra)*np.cos(source_coord.dec)
    if parallax is not None:
        if not isinstance(parallax, u.Quantity):
            parallax = parallax * u.mas
    else:
        if source_coord.distance.unit is u.dimensionless_unscaled:
            raise u.UnitConversionError(
                'Distance is not set and thus parallax cannot be determined.')
        parallax = source_coord.distance.to(u.mas, equivalencies=u.parallax())

    return (factor * parallax * proj_ra, factor * parallax * proj_dec)
Ejemplo n.º 18
0
 def minimum_cells(self):
     """
     Minimum allowed number of grid cells,
     $n_{min}=\lceil L/\Delta s_{max}\\rceil$, where $L$ is the loop
     length and $\Delta s_{max}$ is the maximum allowed grid cell width.
     Optionally, if the minimum number of cells is specified
     in ``config['grid']['minimum_cells']``, this value will take
     precedence.
     """
     if 'minimum_cells' in self.config['grid']:
         return int(self.config['grid']['minimum_cells'])
     n_min = self.config['general']['loop_length'] / self.config['grid']['maximum_cell_width']
     if n_min.decompose().unit != u.dimensionless_unscaled:
         raise u.UnitConversionError(
             f'''Maximum cell width must be able to be converted to 
             {self.config['general']['loop_length'].unit}''')
     return int(np.round(n_min.decompose()))
Ejemplo n.º 19
0
    def _spatial_unit_conversion(self, pixel_value, unit):
        '''
        Convert a value in pixel units to the given unit.
        '''

        if isinstance(unit, u.Quantity):
            unit = unit.unit

        if unit.is_equivalent(u.pix):
            return pixel_value
        elif unit.is_equivalent(u.deg):
            return self._to_angular(pixel_value, unit)
        elif unit.is_equivalent(u.pc):
            return self._to_physical(pixel_value, unit)
        else:
            raise u.UnitConversionError("unit must be an angular or physical"
                                        " unit.")
Ejemplo n.º 20
0
    def distance(self, value):
        '''
        Value must be a quantity with a valid distance unit. Will keep the
        units given.
        '''

        if not isinstance(value, u.Quantity):
            raise TypeError("Value for distance must an astropy Quantity.")

        if not value.unit.is_equivalent(u.pc):
            raise u.UnitConversionError("Given unit ({}) is not a valid unit"
                                        " of distance.".format(value.unit))

        if not value.isscalar:
            raise TypeError("Distance must be a scalar quantity.")

        self._distance = value
Ejemplo n.º 21
0
    def _new_spectral_wcs(self, unit, velocity_convention=None,
                          rest_value=None):
        """
        Returns a new WCS with a different Spectral Axis unit.

        Parameters
        ----------
        unit : :class:`~astropy.units.Unit`
            Any valid spectral unit: velocity, (wave)length, or frequency.
            Only vacuum units are supported.
        velocity_convention : 'relativistic', 'radio', or 'optical'
            The velocity convention to use for the output velocity axis.
            Required if the output type is velocity. This can be either one
            of the above strings, or an `astropy.units` equivalency.
        rest_value : :class:`~astropy.units.Quantity`
            A rest wavelength or frequency with appropriate units.  Required if
            output type is velocity.  The cube's WCS should include this
            already if the *input* type is velocity, but the WCS's rest
            wavelength/frequency can be overridden with this parameter.

            .. note: This must be the rest frequency/wavelength *in vacuum*,
                     even if your cube has air wavelength units
        """

        unit = self._new_wcs_argument_validation(unit, velocity_convention,
                                                 rest_value)

        if velocity_convention is not None:
            equiv = getattr(u, 'doppler_{0}'.format(velocity_convention))
            rest_value.to(unit, equivalencies=equiv)

        # Store the original unit information for posterity
        meta = self._meta.copy()

        orig_unit = self.wcs.unit[0] if hasattr(self.wcs, 'unit') else self.spectral_axis.unit

        if 'original_unit' not in self._meta:
            meta['original_unit'] = orig_unit

        # Create the new wcs object
        if isinstance(unit, u.UnitBase) and unit.is_equivalent(
                orig_unit, equivalencies=u.spectral()):
            return gwcs_from_array(self.spectral_axis), meta

        raise u.UnitConversionError(f"WCS units incompatible: {unit} and {orig_unit}.")
Ejemplo n.º 22
0
 def __init__(self, ranges: astropy.table.Table,
              mask_auto_correlations: bool) -> None:
     super().__init__()
     cols = ('min_frequency', 'max_frequency', 'max_baseline')
     units = (u.Hz, u.Hz, u.m)
     self.ranges = astropy.table.QTable([ranges[col] for col in cols],
                                        names=cols,
                                        dtype=(np.float64, np.float64,
                                               np.float64))
     # Canonicalise the units to simplify to_hdf5 (and also remove the
     # cost of conversions when methods are called with canonical units).
     for col, unit in zip(cols, units):
         # Ensure we haven't been given unit-less data, as <<= will inject
         # the unit (see https://github.com/astropy/astropy/issues/10514).
         if self.ranges[col].unit is None:
             raise u.UnitConversionError(f'Column {col} has no units')
         self.ranges[col] <<= unit
     self._mask_auto_correlations = mask_auto_correlations
Ejemplo n.º 23
0
 def maximum_cells(self):
     """
     Maximum allowed number of grid cells,
     $n_{max}=\lfloor 2^{L_R}n_{min}\\rfloor$, where $L_R$ is the maximum
     refinement level and $n_{min}$ is the minimum allowed number of
     grid cells. Optionally, if the maximum number of cells is specified
     in ``config['grid']['maximum_cells']``, this value will take
     precedence.
     """
     if 'maximum_cells' in self.config['grid']:
         return int(self.config['grid']['maximum_cells'])
     n_min = self.config['general']['loop_length'] / self.config['grid']['maximum_cell_width']
     if n_min.decompose().unit != u.dimensionless_unscaled:
         raise u.UnitConversionError(
             f'''Maximum cell width must be able to be converted to
             {self.config['general']['loop_length'].unit}''')
     return int(np.floor(
         2**self.config['grid']['maximum_refinement_level'] * n_min))
Ejemplo n.º 24
0
 def wrapper(*args, **kwargs):
     given = sig.bind(*args, **kwargs)
     for par, unit in parameters.items():
         arg = given.arguments[par]
         if (isinstance(arg, str) or hasattr(arg, '__iter')
                 and all(isinstance(elem, str) for elem in arg)):
             # import here to prevent circular import
             from ..galaxy.spectrum import load_spectral_data
             arg = load_spectral_data(arg)
             given.arguments[par] = arg
         if not arg.unit.is_equivalent(
                 unit,
                 equivalencies=units.spectral_density(
                     arg.spectral_axis)):
             raise units.UnitConversionError(
                 '{} does not have {} units'.format(
                     par, unit.physical_type))
     return function(*given.args, **given.kwargs)
Ejemplo n.º 25
0
    def _to_pixel(self, value):
        '''
        Convert from angular or physical scales to pixels.
        '''

        if not isinstance(value, u.Quantity):
            raise TypeError("value must be an astropy Quantity object.")

        # Angular converions
        if value.unit.is_equivalent(u.pix):
            return value
        elif value.unit.is_equivalent(u.deg):
            return value.to(u.pix, equivalencies=self._angular_equiv)
        elif value.unit.is_equivalent(u.pc):
            return value.to(u.pix, equivalencies=self._physical_equiv)
        else:
            raise u.UnitConversionError("value has units of {}. It must have "
                                        "an angular or physical unit.".format(
                                            value.unit))
Ejemplo n.º 26
0
    def get_particle_mass(particle) -> u.Quantity:
        """Return the mass of a particle.

        Take a representation of a particle and returns the mass in
        kg.  If the input is a `~astropy.units.Quantity` or
        `~astropy.constants.Constant` with units of mass already, then
        this returns that mass converted to kg.
        """
        try:
            if isinstance(particle, (u.Quantity, const.Constant)):
                return particle.to(u.kg)
            if not isinstance(particle, Particle):
                particle = Particle(particle)
            return particle.mass.to(u.kg)
        except u.UnitConversionError as exc1:
            raise u.UnitConversionError(f"Incorrect units in reduced_mass.") from exc1
        except MissingAtomicDataError:
            raise MissingAtomicDataError(
                f"Unable to find the reduced mass because the mass of "
                f"{particle} is not available.") from None
Ejemplo n.º 27
0
def gyroradius(B, *args, Vperp=None, T_i=None, particle='e'):
    r"""Returns the particle gyroradius.

    Parameters
    ----------
    B: Quantity
        The magnetic field magnitude in units convertible to tesla.

    Vperp: Quantity, optional
        The component of particle velocity that is perpendicular to the
        magnetic field in units convertible to meters per second.

    T_i: Quantity, optional
        The particle temperature in units convertible to kelvin.

    particle : string, optional
        Representation of the particle species (e.g., 'p' for protons, 'D+'
        for deuterium, or 'He-4 +1' for singly ionized helium-4),
        which defaults to electrons.  If no charge state information is
        provided, then the particles are assumed to be singly charged.

    args : Quantity
        If the second positional argument is a Quantity with units
        appropriate to Vperp or T_i, then this argument will take the
        place of that keyword argument.

    Returns
    -------
    r_Li : Quantity
        The particle gyroradius in units of meters.  This Quantity will be
        based on either the perpendicular component of particle velocity as
        inputted, or the most probable speed for an particle within a
        Maxwellian distribution for the particle temperature.

    Raises
    ------
    TypeError
        The arguments are of an incorrect type

    UnitConversionError
        The arguments do not have appropriate units

    ValueError
        If any argument contains invalid values

    UserWarning
        If units are not provided and SI units are assumed

    Notes
    -----
    One but not both of Vperp and T_i must be inputted.

    If any of B, Vperp, or T_i is a number rather than a Quantity,
    then SI units will be assumed and a warning will be raised.

    Formula
    -------
    The particle gyroradius is also known as the particle Larmor radius and is
    given by

    .. math::
    r_{Li} = \frac{V_{\perp}}{omega_{ci}}

    where :math:`V_{\perp}` is the component of particle velocity that is
    perpendicular to the magnetic field and :math:`\omega_{ci}` is the
    particle gyrofrequency.  If a temperature is provided, then
    :math:`V_\perp` will be the most probable thermal velocity of an
    particle at that temperature.

    Examples
    --------
    >>> from astropy import units as u
    >>> gyroradius(0.2*u.T, 1e5*u.K, particle='p')
    <Quantity 0.002120874971411475 m>
    >>> gyroradius(0.2*u.T, 1e5*u.K, particle='p')
    <Quantity 0.002120874971411475 m>
    >>> gyroradius(5*u.uG, 1*u.eV, particle='alpha')
    <Quantity 288002.38837768475 m>
    >>> gyroradius(400*u.G, 1e7*u.m/u.s, particle='Fe+++')
    <Quantity 48.23129811339086 m>
    >>> gyroradius(B = 0.01*u.T, T_i = 1e6*u.K)
    <Quantity 0.0031303339253265536 m>
    >>> gyroradius(B = 0.01*u.T, Vperp = 1e6*u.m/u.s)
    <Quantity 0.0005685630062091092 m>
    >>> gyroradius(0.2*u.T, 1e5*u.K)
    <Quantity 4.9494925204636764e-05 m>
    >>> gyroradius(5*u.uG, 1*u.eV)
    <Quantity 6744.259818299466 m>
    >>> gyroradius(400*u.G, 1e7*u.m/u.s)
    <Quantity 0.0014214075155227729 m>

    """

    if Vperp is not None and T_i is not None:
        raise ValueError("Cannot have both Vperp and T_i as arguments to "
                         "gyroradius")

    if len(args) == 1 and isinstance(args[0], units.Quantity):
        arg = args[0].si
        if arg.unit == units.T and B.si.unit in [
                units.J, units.K, units.m / units.s
        ]:
            B, arg = arg, B

        if arg.unit == units.m / units.s:
            Vperp = arg
        elif arg.unit in (units.J, units.K):
            T_i = arg.to(units.K, equivalencies=units.temperature_energy())
        else:
            raise units.UnitConversionError("Incorrect units for positional "
                                            "argument in gyroradius")
    elif len(args) > 0:
        raise ValueError("Incorrect inputs to gyroradius")

    _check_quantity(B, 'B', 'gyroradius', units.T)

    if Vperp is not None:
        _check_quantity(Vperp, 'Vperp', 'gyroradius', units.m / units.s)
    elif T_i is not None:
        _check_quantity(T_i, 'T_i', 'gyroradius', units.K)
        Vperp = thermal_speed(T_i, particle=particle)

    omega_ci = gyrofrequency(B, particle)

    r_Li = np.abs(Vperp) / omega_ci

    return r_Li.to(units.m, equivalencies=units.dimensionless_angles())
Ejemplo n.º 28
0
def _check_relativistic(V, funcname, betafrac=0.05):
    r"""
    Warn or raise error for relativistic or superrelativistic
    velocities.

    Parameters
    ----------
    V : ~astropy.units.Quantity
        A velocity.

    funcname : str
        The name of the original function to be printed in the error
        messages.

    betafrac : float, optional
        The minimum fraction of the speed of light that will generate
        a warning. Defaults to 5%.

    Raises
    ------
    TypeError
        If `V` is not a `~astropy.units.Quantity`.

    ~astropy.units.UnitConversionError
        If `V` is not in units of velocity.

    ValueError
        If `V` contains any `~numpy.nan` values.

    RelativityError
        If `V` is greater than or equal to the speed of light.

    Warns
    -----
    ~plasmapy.utils.RelativityWarning
        If `V` is greater than or equal to the specified fraction of the
        speed of light.

    Examples
    --------
    >>> from astropy import units as u
    >>> _check_relativistic(1*u.m/u.s, 'function_calling_this')

    """

    # TODO: Replace `funcname` with func.__name__?

    errmsg = ("V must be a Quantity with units of velocity in"
              "_check_relativistic")

    if not isinstance(V, u.Quantity):
        raise TypeError(errmsg)

    try:
        V_over_c = (V / c).to_value(u.dimensionless_unscaled)
    except Exception:
        raise u.UnitConversionError(errmsg)

    beta = np.max(np.abs((V_over_c)))

    if beta == np.inf:
        raise RelativityError(f"{funcname} is yielding an infinite velocity.")
    elif beta >= 1:
        raise RelativityError(
            f"{funcname} is yielding a velocity that is {str(round(beta, 3))} "
            f"times the speed of light.")
    elif beta >= betafrac:
        warnings.warn(
            f"{funcname} is yielding a velocity that is "
            f"{str(round(beta * 100, 3))}% of the speed of "
            f"light. Relativistic effects may be important.",
            RelativityWarning)
Ejemplo n.º 29
0
def _check_quantity(arg,
                    argname,
                    funcname,
                    units,
                    can_be_negative=True,
                    can_be_complex=False,
                    can_be_inf=True,
                    can_be_nan=True,
                    none_shall_pass=False):
    """
    Raise an exception if an object is not a `~astropy.units.Quantity`
    with correct units and valid numerical values.

    Parameters
    ----------
    arg : ~astropy.units.Quantity
        The object to be tested.

    argname : str
        The name of the argument to be printed in error messages.

    funcname : str
        The name of the original function to be printed in error messages.

    units : `~astropy.units.Unit` or list of `~astropy.unit.Unit`
        Acceptable units for `arg`.

    can_be_negative : bool, optional
        `True` if the `~astropy.units.Quantity` can be negative,
        `False` otherwise.  Defaults to `True`.

    can_be_complex : bool, optional
        `True` if the `~astropy.units.Quantity` can be a complex number,
        `False` otherwise.  Defaults to `False`.

    can_be_inf : bool, optional
        `True` if the `~astropy.units.Quantity` can contain infinite
        values, `False` otherwise.  Defaults to `True`.

    can_be_nan : bool, optional
        `True` if the `~astropy.units.Quantity` can contain NaN
        values, `False` otherwise.  Defaults to `True`.

    none_shall_pass : bool, optional
        `True` if the `~astropy.units.Quantity` can contain None
        values, `False` otherwise.  Defaults to `True`.

    Raises
    ------
    TypeError
        If the argument is not a `~astropy.units.Quantity` or units is
        not entirely units.

    ~astropy.units.UnitConversionError
        If the argument is not in acceptable units.

    ~astropy.units.UnitsError
        If after the assumption checks, the argument is still not in acceptable
        units.

    ValueError
        If the argument contains any `~numpy.nan` or other invalid
        values as determined by the keywords.

    Warns
    -----
    ~astropy.units.UnitsWarning
        If a `~astropy.units.Quantity` is not provided and unique units
        are provided, a `UnitsWarning` will be raised and the inputted
        units will be assumed.

    Examples
    --------
    >>> from astropy import units as u
    >>> import pytest
    >>> _check_quantity(4*u.T, 'B', 'f', u.T)
    <Quantity 4. T>
    >>> with pytest.warns(u.UnitsWarning, match="No units are specified"):
    ...     assert _check_quantity(4, 'B', 'f', u.T) == 4 * u.T

    """

    # TODO: Replace `funcname` with func.__name__?

    if not isinstance(units, list):
        units = [units]

    for unit in units:
        if not isinstance(unit, (u.Unit, u.CompositeUnit, u.IrreducibleUnit)):
            raise TypeError("The keyword 'units' to check_quantity must be "
                            "a unit or a list/tuple containing only units.")

    # Create a generic error message

    typeerror_message = (
        f"The argument {argname} to {funcname} should be a Quantity with ")

    if len(units) == 1:
        typeerror_message += f"the following units: {str(units[0])}"
    else:
        typeerror_message += "one of the following units: "
        for unit in units:
            typeerror_message += str(unit)
            if unit != units[-1]:
                typeerror_message += ", "
    if none_shall_pass:
        typeerror_message += "or None "

    if isinstance(arg, (u.Unit, u.CompositeUnit, u.IrreducibleUnit)):
        raise TypeError(typeerror_message)

    # Make sure arg is a quantity with correct units

    unit_casting_warning = dedent(
        f"""No units are specified for {argname} = {arg} in {funcname}. Assuming units of {str(units[0])}.
                To silence this warning, explicitly pass in an Astropy Quantity (from astropy.units)
                (see http://docs.astropy.org/en/stable/units/)""")

    # TODO include explicit note on how to pass in Astropy Quantity

    valueerror_message = (
        f"The argument {argname} to function {funcname} cannot contain")

    if arg is None and none_shall_pass:
        return arg
    elif arg is None:
        raise ValueError(f"{valueerror_message} Nones.")
    if not isinstance(arg, (u.Quantity)):
        if len(units) != 1:
            raise TypeError(typeerror_message)
        else:
            try:
                arg = arg * units[0]
            except (u.UnitsError, ValueError):
                raise TypeError(typeerror_message)
            else:
                warnings.warn(UnitsWarning(unit_casting_warning))
    if not isinstance(arg, u.Quantity):
        raise u.UnitsError(
            "{} is still not a Quantity after checks!".format(arg))

    in_acceptable_units = []

    for unit in units:
        try:
            arg.unit.to(unit, equivalencies=u.temperature_energy())
        except Exception:
            in_acceptable_units.append(False)
        else:
            in_acceptable_units.append(True)

    if not np.any(in_acceptable_units):
        raise u.UnitConversionError(typeerror_message)

    # Make sure that the quantity has valid numerical values
    if np.any(np.isnan(arg.value)) and not can_be_nan:
        raise ValueError(f"{valueerror_message} NaNs.")
    elif np.any(np.iscomplex(arg.value)) and not can_be_complex:
        raise ValueError(f"{valueerror_message} complex numbers.")
    elif not can_be_negative:
        # Allow NaNs through without raising a warning
        with np.errstate(invalid='ignore'):
            isneg = np.any(arg.value < 0)
        if isneg:
            raise ValueError(f"{valueerror_message} negative numbers.")
    elif not can_be_inf and np.any(np.isinf(arg.value)):
        raise ValueError(f"{valueerror_message} infs.")

    return arg
Ejemplo n.º 30
0
def deBroglie_wavelength(V: u.m / u.s, particle) -> u.m:
    r"""
    Calculates the de Broglie wavelength.

    Parameters
    ----------
    V : ~astropy.units.Quantity
        Particle velocity in units convertible to meters per second.

    particle : str or ~astropy.units.Quantity
        Representation of the particle species (e.g., `'e'`, `'p'`, `'D+'`,
        or `'He-4 1+'`, or the particle mass in units convertible to
        kilograms.

    Returns
    -------
    lambda_dB : ~astropy.units.Quantity
        The de Broglie wavelength in units of meters.

    Raises
    ------
    TypeError
        The velocity is not a `~astropy.units.Quantity` and cannot be
        converted into a ~astropy.units.Quantity.

    ~astropy.units.UnitConversionError
        If the velocity is not in appropriate units.

    ~plasmapy.utils.RelativityError
        If the magnitude of `V` is faster than the speed of light.

    Warns
    -----
    ~astropy.units.UnitsWarning
        If units are not provided, SI units are assumed

    Notes
    -----
    The de Broglie wavelength is given by

    .. math::

        \lambda_{dB} = \frac{h}{p} = \frac{h}{\gamma m V}

    where :math:`h` is the Planck constant, :math:`p` is the
    relativistic momentum of the particle, :math:`gamma` is the
    Lorentz factor, :math:`m` is the particle's mass, and :math:`V` is the
    particle's velocity.

    Examples
    --------
    >>> from astropy import units as u
    >>> velocity = 1.4e7 * u.m / u.s
    >>> deBroglie_wavelength(velocity, 'e')
    <Quantity 5.18997095e-11 m>
    >>> deBroglie_wavelength(V = 0 * u.m / u.s, particle = 'D+')
    <Quantity inf m>
    """

    V = np.abs(V)

    if np.any(V >= c):
        raise RelativityError("Velocity input in deBroglie_wavelength cannot "
                              "be greater than or equal to the speed of "
                              "light.")

    if not isinstance(particle, u.Quantity):
        try:
            # TODO: Replace with more general routine!
            m = particles.particle_mass(particle)
        except Exception:
            raise ValueError("Unable to find particle mass.")
    else:
        try:
            m = particle.to(u.kg)
        except Exception:
            raise u.UnitConversionError("The second argument for deBroglie"
                                        " wavelength must be either a "
                                        "representation of a particle or a"
                                        " Quantity with units of mass.")

    if V.size > 1:

        lambda_dBr = np.ones(V.shape) * np.inf * u.m
        indices = V.value != 0
        lambda_dBr[indices] = h / (m * V[indices] * Lorentz_factor(V[indices]))

    else:

        if V == 0 * u.m / u.s:
            lambda_dBr = np.inf * u.m
        else:
            lambda_dBr = h / (Lorentz_factor(V) * m * V)

    return lambda_dBr