def inout_map(arg, unit_info, name=None):
        if unit_info is None:
            return arg

        use_units_msg = (" Make sure you're passing in a unitful value, either as a string or by "
                         "using `instrumental.u` or `instrumental.Q_()`")

        optional, units = unit_info
        if optional and arg is None:
            return None
        elif arg == 0:
            # Allow naked zeroes as long as we're using absolute units (e.g. not degF)
            # It's a bit dicey using this private method; works in 0.6 at least
            if units._ok_for_muldiv():
                return Q_(arg, units)
            else:
                if name is not None:
                    extra_msg = " for argument '{}'.".format(name) + use_units_msg
                    raise pint.DimensionalityError(u.dimensionless.units, units.units,
                                                   extra_msg=extra_msg)
                else:
                    extra_msg = " for return value." + use_units_msg
                    raise pint.DimensionalityError(u.dimensionless.units, units.units,
                                                   extra_msg=extra_msg)
        else:
            q = to_quantity(arg)
            if q.dimensionality != units.dimensionality:
                extra_info = '' if isinstance(arg, Q_) else use_units_msg
                if name is not None:
                    extra_msg = " for argument '{}'.".format(name) + extra_info
                    raise pint.DimensionalityError(q.units, units.units, extra_msg=extra_msg)
                else:
                    extra_msg = " for return value." + extra_info
                    raise pint.DimensionalityError(q.units, units.units, extra_msg=extra_msg)
            return q
Exemple #2
0
    def inout_map(arg, unit_info, name=None):
        if unit_info is None:
            return arg

        optional, units = unit_info
        if optional and arg is None:
            return None
        elif arg == 0:
            # Allow naked zeroes as long as we're using absolute units (e.g. not degF)
            # It's a bit dicey using this private method; works in 0.6 at least
            if units._ok_for_muldiv():
                return Q_(arg, units)
            else:
                if name is not None:
                    raise pint.DimensionalityError(u.dimensionless.units, units.units,
                                                   extra_msg=" for argument '{}'".format(name))
                else:
                    raise pint.DimensionalityError(u.dimensionless.units, units.units,
                                                   extra_msg=" for return value")
        else:
            q = Q_(arg)
            if q.dimensionality != units.dimensionality:
                if name is not None:
                    raise pint.DimensionalityError(q.units, units.units,
                                                   extra_msg=" for argument '{}'".format(name))
                else:
                    raise pint.DimensionalityError(q.units, units.units,
                                                   extra_msg=" for return value")
            return q
Exemple #3
0
    def in_map(arg, unit_info, name):
        if unit_info is None:
            return arg

        optional, units = unit_info
        if optional and arg is None:
            return None
        elif arg == 0:
            # Allow naked zeroes as long as we're using absolute units (e.g. not degF)
            # It's a bit dicey using this private method; works in 0.6 at least
            if units._ok_for_muldiv():
                return arg
            else:
                if name is not None:
                    raise pint.DimensionalityError(
                        u.dimensionless.units,
                        units.units,
                        extra_msg=" for argument '{}'".format(name))
                else:
                    raise pint.DimensionalityError(
                        u.dimensionless.units,
                        units.units,
                        extra_msg=" for return value")
        else:
            q = to_quantity(arg)
            try:
                if q.units == units:
                    return q.magnitude  # Speed up the common case
                else:
                    return q.to(units).magnitude
            except pint.DimensionalityError:
                raise pint.DimensionalityError(
                    q.units,
                    units.units,
                    extra_msg=" for argument '{}'".format(name))
Exemple #4
0
def validate_geq(value_name, value, low_lim):
    """Raise error if value lower than specified lower limit or wrong type.

    Parameters
    ---------
    value_name : str
        Name of value being tested
    value : int, float, numpy.ndarray, pint.Quantity
        Value to be tested
    low_lim : type(value)
        Lowest acceptable limit of ``value``

    Returns
    -------
    value : type(value)
        The original value

    """

    try:
        if validate_num(value_name, value) < low_lim:
            msg = (value_name + ' must be greater than or equal to ' +
                   str(low_lim) + '.\n'
                   'Value provided was: ' + str(value))
            # RuntimeError used to avoid being caught by Pint comparison error.
            # Pint should really raise TypeError (or something) rather than
            # ValueError.
            raise RuntimeError(msg)
        else:
            return value
    except ValueError:
        if isinstance(value, units.Quantity):
            msg = ('\n' + value_name + ' given with units, when variable '
                   'should be dimensionless.')
            raise pint.DimensionalityError(value.units, None, extra_msg=msg)
        else:
            msg = ('\n' + value_name + ' not given in units. '
                   'Correct units share dimensionality with: ' +
                   str(low_lim.units))
            raise pint.DimensionalityError(None, low_lim.units, extra_msg=msg)
    except pint.DimensionalityError:
        msg = ('\n' + value_name + ' given in incompatible units. '
               'Correct units share dimensionality with: ' +
               str(low_lim.units))
        raise pint.DimensionalityError(value.units,
                                       low_lim.units,
                                       extra_msg=msg)
    except:
        raise
Exemple #5
0
def convert_gwp(context, qty, to):
    """Helper for :meth:`convert_unit` to perform GWP conversions."""
    # Remove a leading 'gwp_' to produce the metric name
    metric = context.split('gwp_')[1] if context else context

    # Extract the species from *qty* and *to*, allowing supported aliases
    species_from, units_from = extract_species(qty[1])
    species_to, units_to = extract_species(to)

    try:
        # Convert using a (magnitude, unit) tuple with only units, and explicit
        # input and output units
        result = iam_units.convert_gwp(metric, (qty[0], units_from),
                                       species_from, species_to)
    except (AttributeError, ValueError):
        # Missing *metric*, or *species_to* contains invalid units. pyam
        # promises UndefinedUnitError in these cases. Use a subclass (above) to
        # add a usage hint.
        raise UndefinedUnitError(species_to) from None
    except pint.DimensionalityError:
        # Provide an exception with the user's inputs
        raise pint.DimensionalityError(qty[1], to) from None

    # Other exceptions are not caught and will pass up through convert_unit()

    if units_to:
        # Also convert the units
        result = result.to(units_to)
    else:
        # *to* was only a species name. Provide units based on input and the
        # output species name.
        to = iam_units.format_mass(result, species_to, spec=':~')

    return result, to
Exemple #6
0
    def inout_map(arg, unit_info, name=None):
        if unit_info is None:
            return arg

        optional, units = unit_info
        if optional and arg is None:
            return None
        else:
            q = Q_(arg)
            if q.dimensionality != units.dimensionality:
                if name is not None:
                    raise pint.DimensionalityError(q.units, units.units,
                                                   extra_msg=" for argument '{}'".format(name))
                else:
                    raise pint.DimensionalityError(q.units, units.units,
                                                   extra_msg=" for return value")
            return q
Exemple #7
0
 def span(self, span):
     span = Q_(span)
     if span.check('[frequency]'):
         self._rsrc.write('SPANF %07.3f' % span.to('THz').m)
     elif span.check('[length]'):
         self._rsrc.write('SPAN %06.1f' % span.to('nm').m)
     else:
         if self.x_unit == XUnit['frequency']:
             target_unit = u.THz
         else:
             target_unit = u.nm
         raise pint.DimensionalityError(span.units, target_unit)
Exemple #8
0
    def out_map(res, unit_info):
        if unit_info is None:
            return res

        optional, units = unit_info
        if optional and res is None:
            return None
        else:
            q = Q_(res)
            try:
                return q
            except pint.DimensionalityError:
                raise pint.DimensionalityError(q.units, units.units, extra_msg=" for return value")
Exemple #9
0
    def in_map(arg, unit_info, name):
        if unit_info is None:
            return arg

        optional, units = unit_info
        if optional and arg is None:
            return None
        else:
            q = Q_(arg)
            try:
                return q.to(units).magnitude
            except pint.DimensionalityError:
                raise pint.DimensionalityError(q.units, units.units,
                                               extra_msg=" for argument '{}'".format(name))
Exemple #10
0
def _enforce_dimensionality(arg, dimensionality):
    """
    Ensure quantity conforms to specified dimensionality.
    """
    # WARNING: Error has to be raised manually because is_compatible_with does not
    # accept dimensionality containers and no other public methods are available.
    if not isinstance(arg, (pint.Unit, pint.Quantity)):
        raise RuntimeError(f'Invalid input argument {arg!r}.')
    dimensionality = ureg.get_dimensionality(dimensionality)
    arg_dimensionality = ureg.get_dimensionality(arg)
    if arg_dimensionality != dimensionality:
        raise pint.DimensionalityError(
            arg, 'a quantity of', arg_dimensionality, dimensionality
        )
Exemple #11
0
def to_ureg(input_, unit=None, convert_quantities=True):
    """
	This method is an import function to import alien quantities (of different unit registries) or numeric formats into
		the ureg.

	:param input_: The input quantity or numeric format (e.g. float, int, numpy array) or string (e.g. "5 nm")

	:param unit: Given as a valid unit string. If a numeric format is used, this specifies the unit of it. If a quantity
		is used, the output quantity will be converted to it.

	:param convert_quantities: Boolean: Specifies what should be done if input_ is a quantity.
		True: convert the quantity to the unit if specified by unit parameter. (The default)
		False: only assure the dimension is correct and import to ureg, but leave unit of input_ as is is.

	:return: The imported quantity.
	"""
    # Python2/3 compatibility: If unit is a byte string, decode it for usage in pint.Quantity.
    if isinstance(unit, bytes):
        unit = unit.decode()

    # Check if input is quantity:
    if is_quantity(input_):
        # If output unit is specified, make sure it has a compatible dimension. Else a DimensionalityError will be
        # raised:
        if unit and not same_dimension(input_, to_ureg(1, unit)):
            raise pint.DimensionalityError(
                input_.units,
                to_ureg(1, unit).units,
                extra_msg="Incompatible units given to to_ureg!")
        # Check if input is already of our ureg. If so, nothing to do other then convert if unit is specified:
        if input_._REGISTRY is ureg:
            if unit and convert_quantities and input_.units != to_ureg(
                    1, unit).units:
                return input_.to(unit)
            else:
                return input_
        else:  # Use inputs magnitude, but our corresponding ureg unit.
            if unit and convert_quantities:
                return Quantity(input_.magnitude, str(input_.units)).to(unit)
            else:
                return Quantity(input_.magnitude, str(input_.units))
    elif (isinstance(input_, str) or isinstance(input_, str)):
        if unit:
            return ureg(input_).to(unit)
        else:
            return ureg(input_)
    else:  # we are dealing with numerial data
        return Quantity(input_, unit)
Exemple #12
0
 def _check_dimensionality(self):
     for quant in self._code_registry.unit_dict.keys():
         if (not self._base_registry[quant].dimensionality
                 == self._code_registry[quant].dimensionality):
             raise pint.DimensionalityError(
                 self._base_registry[quant],
                 self._code_registry[quant],
                 extra_msg=
                 "\n Dimensional inequality: Quantity {} has dimensionality {} "
                 "in the base registry but {} in the code "
                 "registry".format(
                     quant,
                     self._base_registry[quant].dimensionality,
                     self._code_registry[quant].dimensionality,
                 ),
             )
Exemple #13
0
    def _build_coordinates(
        cls, coordinates, time: Time = None
    ) -> Union[xr.DataArray, TimeSeries]:
        """Create xarray coordinates from different formats and time-inputs."""
        if isinstance(coordinates, TimeSeries):
            if coordinates.is_expression:
                return coordinates
            coordinates = cls._coords_from_discrete_time_series(coordinates)

        if coordinates is None:
            coordinates = np.zeros(3)

        if not isinstance(coordinates, xr.DataArray):
            if not isinstance(coordinates, (np.ndarray, pint.Quantity)):
                coordinates = np.array(coordinates)

            coordinates = ut.xr_3d_vector(coordinates, time)

        if isinstance(coordinates.data, pint.Quantity):
            # The first branch is a workaround until units are mandatory
            if coordinates.data.u == pint.Unit(""):
                coordinates.data = coordinates.data.m
            elif not coordinates.data.is_compatible_with(_DEFAULT_LEN_UNIT):
                raise pint.DimensionalityError(
                    coordinates.data.u,
                    _DEFAULT_LEN_UNIT,
                    extra_msg="\nThe coordinates require units representing a length.",
                )

        if not isinstance(coordinates.data, pint.Quantity) and not (
            coordinates.shape == (3,) and np.allclose(coordinates.data, np.zeros(3))
        ):

            warnings.warn(
                "Coordinates without units are deprecated and won't be supported in "
                "the future",
                DeprecationWarning,
                stacklevel=2,
            )

        # make sure we have correct "time" format
        coordinates = coordinates.weldx.time_ref_restore()

        return coordinates
Exemple #14
0
def convert_gwp(metric, quantity, *species):
    """Convert *quantity* between GHG *species* with a GWP *metric*.

    Parameters
    ----------
    metric : 'SARGWP100' or 'AR4GWP100' or 'AR5GWP100' or None
        Metric conversion factors to use. May be :obj:`None` if the input and
        output species are the same.
    quantity : str or pint.Quantity or tuple
        Quantity to convert. If a tuple of (magnitude, unit), these are passed
        as arguments to :class:`pint.Quantity`.
    species : sequence of str, length 1 or 2
        Output, or (input, output) species symbols, e.g. ('CH4', 'CO2') to
        convert mass of CH₄ to GWP-equivalent mass of CO₂. If only the output
        species is provided, *quantity* must contain the symbol of the input
        species in some location, e.g. '1.0 tonne CH4 / year'.

    Returns
    -------
    pint.Quantity
        `quantity` converted from the input to output species.
    """
    # Handle *species*: either (in, out) or only out
    try:
        species_in, species_out = species
    except ValueError:
        if len(species) != 1:
            raise ValueError('Must provide (from, to) or (to,) species')
        species_in, species_out = None, species[0]

    # Split *quantity* if it is a tuple. After this step:
    # - *mag* is the magnitude, or None.
    # - *expr* is a string expression for either just the units, or the entire
    #   quantity, including magnitude, as a str or pint.Quantity.
    mag, expr = quantity if isinstance(quantity, tuple) else (None, quantity)

    # If species_in wasn't provided, then *expr* must contain it
    if not species_in:
        # Extract it using the regex, then re-assemble the expression for the
        # units or whole quantity
        q0, species_in, q1 = emissions.pattern.split(expr, maxsplit=1)
        expr = q0 + q1

    # *metric* can only be None if the input and output species symbols are
    # identical or equivalent
    if metric is None:
        if (species_in == species_out or
                any({species_in, species_out} <= g for g in emissions.EQUIV)):
            metric = 'AR5GWP100'
        elif species_in in species_out:
            # Eg. 'CO2' in 'CO2 / a'. This is both a DimensionalityError and a
            # ValueError (no metric); raise the former for pyam compat
            raise pint.DimensionalityError(species_in, species_out)
        else:
            msg = f'Must provide GWP metric for ({species_in}, {species_out})'
            raise ValueError(msg)

    # Ensure a pint.Quantity object:
    # - If *quantity* was a tuple, use the 2-arg constructor.
    # - If a str, use the 1-arg form to parse it.
    # - If already a pint.Quantity, this is a no-op.
    args = (expr,) if mag is None else (mag, expr)
    quantity = registry.Quantity(*args)

    # Construct intermediate units with the same dimensionality as *quantity*,
    # except '[mass]' replaced with the dummy unit '_gwp'
    dummy = quantity.units / registry.Unit('tonne / _gwp')

    # Convert to dummy units using 'a' for the input species; then back to the
    # input units using 'a' for the output species.
    return quantity.to(dummy, metric, _a=f'a_{species_in}') \
                   .to(quantity.units, metric, _a=f'a_{species_out}')