Exemplo n.º 1
0
    def add(self, symbol, base_value, dimensions, tex_repr=None, offset=None):
        """
        Add a symbol to this registry.

        """
        from unyt.unit_object import _validate_dimensions

        self._unit_system_id = None

        # Validate
        if not isinstance(base_value, float):
            raise UnitParseError("base_value (%s) must be a float, got a %s."
                                 % (base_value, type(base_value)))

        if offset is not None:
            if not isinstance(offset, float):
                raise UnitParseError(
                    "offset value (%s) must be a float, got a %s."
                    % (offset, type(offset)))
        else:
            offset = 0.0

        _validate_dimensions(dimensions)

        if tex_repr is None:
            # make educated guess that will look nice in most cases
            tex_repr = r"\rm{" + symbol.replace('_', '\ ') + "}"

        # Add to lut
        self.lut.update({symbol: (base_value, dimensions, offset, tex_repr)})
Exemplo n.º 2
0
def _validate_dimensions(dimensions):
    if isinstance(dimensions, Mul):
        for dim in dimensions.args:
            _validate_dimensions(dim)
    elif isinstance(dimensions, Symbol):
        if dimensions not in base_dimensions:
            raise UnitParseError(
                "Dimensionality expression contains an "
                "unknown symbol '%s'." % dimensions
            )
    elif isinstance(dimensions, Pow):
        if not isinstance(dimensions.args[1], Number):
            raise UnitParseError(
                "Dimensionality expression '%s' contains a "
                "unit symbol as a power." % dimensions
            )
    elif isinstance(dimensions, (Add, Number)):
        if not isinstance(dimensions, One):
            raise UnitParseError(
                "Only dimensions that are instances of Pow, "
                "Mul, or symbols in the base dimensions are "
                "allowed.  Got dimensions '%s'" % dimensions
            )
    elif not isinstance(dimensions, Basic):
        raise UnitParseError("Bad dimensionality expression '%s'." % dimensions)
Exemplo n.º 3
0
    def add(self,
            symbol,
            base_value,
            dimensions,
            tex_repr=None,
            offset=None,
            prefixable=False):
        """
        Add a symbol to this registry.

        Parameters
        ----------

        symbol : str
           The name of the unit
        base_value : float
           The scaling from the units value to the equivalent SI unit
           with the same dimensions
        dimensions : expr
           The dimensions of the unit
        tex_repr : str, optional
           The LaTeX representation of the unit. If not provided a LaTeX
           representation is automatically generated from the name of
           the unit.
        offset : float, optional
           If set, the zero-point offset to apply to the unit to convert
           to SI. This is mostly used for units like Farhenheit and
           Celcius that are not defined on an absolute scale.
        prefixable : bool
           If True, then SI-prefix versions of the unit will be created
           along with the unit itself.

        """
        from unyt.unit_object import _validate_dimensions

        self._unit_system_id = None

        # Validate
        if not isinstance(base_value, float):
            raise UnitParseError("base_value (%s) must be a float, got a %s." %
                                 (base_value, type(base_value)))

        if offset is not None:
            if not isinstance(offset, float):
                raise UnitParseError(
                    "offset value (%s) must be a float, got a %s." %
                    (offset, type(offset)))
        else:
            offset = 0.0

        _validate_dimensions(dimensions)

        if tex_repr is None:
            # make educated guess that will look nice in most cases
            tex_repr = r"\rm{" + symbol.replace('_', '\ ') + "}"

        # Add to lut
        self.lut[symbol] = (base_value, dimensions, offset, tex_repr,
                            prefixable)
Exemplo n.º 4
0
def _get_unit_data_from_expr(unit_expr, unit_symbol_lut):
    """
    Grabs the total base_value and dimensions from a valid unit expression.

    Parameters
    ----------
    unit_expr: Unit object, or sympy Expr object
        The expression containing unit symbols.
    unit_symbol_lut: dict
        Provides the unit data for each valid unit symbol.

    """
    # Now for the sympy possibilities
    if isinstance(unit_expr, Number):
        if unit_expr is sympy_one:
            return (1.0, sympy_one)
        return (float(unit_expr), sympy_one)

    if isinstance(unit_expr, Symbol):
        return _lookup_unit_symbol(unit_expr.name, unit_symbol_lut)

    if isinstance(unit_expr, Pow):
        unit_data = _get_unit_data_from_expr(unit_expr.args[0], unit_symbol_lut)
        power = unit_expr.args[1]
        if isinstance(power, Symbol):
            raise UnitParseError("Invalid unit expression '%s'." % unit_expr)
        conv = float(unit_data[0] ** power)
        unit = unit_data[1] ** power
        return (conv, unit)

    if isinstance(unit_expr, Mul):
        base_value = 1.0
        dimensions = 1
        for expr in unit_expr.args:
            unit_data = _get_unit_data_from_expr(expr, unit_symbol_lut)
            base_value *= unit_data[0]
            dimensions *= unit_data[1]

        return (float(base_value), dimensions)

    raise UnitParseError(
        "Cannot parse for unit data from '%s'. Please supply"
        " an expression of only Unit, Symbol, Pow, and Mul"
        "objects." % str(unit_expr)
    )
Exemplo n.º 5
0
def _lookup_unit_symbol(symbol_str, unit_symbol_lut):
    """
    Searches for the unit data tuple corresponding to the given symbol.

    Parameters
    ----------
    symbol_str : str
        The unit symbol to look up.
    unit_symbol_lut : dict
        Dictionary with symbols as keys and unit data tuples as values.

    """
    if symbol_str in unit_symbol_lut:
        # lookup successful, return the tuple directly
        return unit_symbol_lut[symbol_str]

    # could still be a known symbol with a prefix
    prefix, symbol_wo_prefix = _split_prefix(symbol_str, unit_symbol_lut)

    if prefix:
        # lookup successful, it's a symbol with a prefix
        unit_data = unit_symbol_lut[symbol_wo_prefix]
        prefix_value = unit_prefixes[prefix][0]

        # Need to add some special handling for comoving units
        # this is fine for now, but it wouldn't work for a general
        # unit that has an arbitrary LaTeX representation
        if symbol_wo_prefix != "cm" and symbol_wo_prefix.endswith("cm"):
            sub_symbol_wo_prefix = symbol_wo_prefix[:-2]
            sub_symbol_str = symbol_str[:-2]
        else:
            sub_symbol_wo_prefix = symbol_wo_prefix
            sub_symbol_str = symbol_str
        latex_repr = unit_data[3].replace(
            "{" + sub_symbol_wo_prefix + "}", "{" + sub_symbol_str + "}"
        )

        # Leave offset and dimensions the same, but adjust scale factor and
        # LaTeX representation
        ret = (
            unit_data[0] * prefix_value,
            unit_data[1],
            unit_data[2],
            latex_repr,
            False,
        )

        unit_symbol_lut[symbol_str] = ret

        return ret

    # no dice
    raise UnitParseError(
        "Could not find unit symbol '%s' in the provided " "symbols." % symbol_str
    )
Exemplo n.º 6
0
def parse_unyt_expr(unit_expr):
    if not unit_expr:
        # Bug catch...
        # if unit_expr is an empty string, parse_expr fails hard...
        unit_expr = "1"
    try:
        unit_expr = parse_expr(unit_expr,
                               global_dict=global_dict,
                               transformations=unit_text_transform)
    except Exception as e:
        msg = "Unit expression '%s' raised an error during parsing:\n%s" % (
            unit_expr,
            repr(e),
        )
        raise UnitParseError(msg)
    return unit_expr
Exemplo n.º 7
0
def parse_unyt_expr(unit_expr):
    if not unit_expr:
        # Bug catch...
        # if unit_expr is an empty string, parse_expr fails hard...
        unit_expr = "1"
    # Avoid a parse error if someone uses the percent unit and the
    # parser tries to interpret it as the modulo operator
    unit_expr = unit_expr.replace("%", "percent")
    try:
        unit_expr = parse_expr(unit_expr,
                               global_dict=global_dict,
                               transformations=unit_text_transform)
    except Exception as e:
        msg = "Unit expression '%s' raised an error during parsing:\n%s" % (
            unit_expr,
            repr(e),
        )
        raise UnitParseError(msg)
    return unit_expr
Exemplo n.º 8
0
    def __new__(
        cls,
        unit_expr=sympy_one,
        base_value=None,
        base_offset=0.0,
        dimensions=None,
        registry=None,
        latex_repr=None,
    ):
        """
        Create a new unit. May be an atomic unit (like a gram) or combinations
        of atomic units (like g / cm**3).

        Parameters
        ----------
        unit_expr : Unit object, sympy.core.expr.Expr object, or str
            The symbolic unit expression.
        base_value : float
            The unit's value in yt's base units.
        base_offset : float
            The offset necessary to normalize temperature units to a common
            zero point.
        dimensions : sympy.core.expr.Expr
            A sympy expression representing the dimensionality of this unit.
            It must contain only mass, length, time, temperature and angle
            symbols.
        registry : UnitRegistry object
            The unit registry we use to interpret unit symbols.
        latex_repr : string
            A string to render the unit as LaTeX

        """
        unit_cache_key = None
        # Simplest case. If user passes a Unit object, just use the expr.
        if hasattr(unit_expr, "is_Unit"):
            # grab the unit object's sympy expression.
            unit_expr = unit_expr.expr
        elif hasattr(unit_expr, "units") and hasattr(unit_expr, "value"):
            # something that looks like a unyt_array, grab the unit and value
            if unit_expr.shape != ():
                raise UnitParseError(
                    "Cannot create a unit from a non-scalar unyt_array, "
                    "received: %s" % (unit_expr,)
                )
            value = unit_expr.value
            if value == 1:
                unit_expr = unit_expr.units.expr
            else:
                unit_expr = unit_expr.value * unit_expr.units.expr
        # Parse a text unit representation using sympy's parser
        elif isinstance(unit_expr, (str, bytes)):
            if isinstance(unit_expr, bytes):
                unit_expr = unit_expr.decode("utf-8")

            # this cache substantially speeds up unit conversions
            if registry and unit_expr in registry._unit_object_cache:
                return registry._unit_object_cache[unit_expr]
            unit_cache_key = unit_expr
            unit_expr = parse_unyt_expr(unit_expr)
        # Make sure we have an Expr at this point.
        if not isinstance(unit_expr, Expr):
            raise UnitParseError(
                "Unit representation must be a string or "
                "sympy Expr. '%s' has type '%s'." % (unit_expr, type(unit_expr))
            )

        if dimensions is None and unit_expr is sympy_one:
            dimensions = dimensionless

        if registry is None:
            # Caller did not set the registry, so use the default.
            registry = default_unit_registry

        # done with argument checking...

        # see if the unit is atomic.
        is_atomic = False
        if isinstance(unit_expr, Symbol):
            is_atomic = True

        #
        # check base_value and dimensions
        #

        if base_value is not None:
            # check that base_value is a float or can be converted to one
            try:
                base_value = float(base_value)
            except ValueError:
                raise UnitParseError(
                    "Could not use base_value as a float. "
                    "base_value is '%s' (type '%s')." % (base_value, type(base_value))
                )

            # check that dimensions is valid
            if dimensions is not None:
                _validate_dimensions(dimensions)
        else:
            # lookup the unit symbols
            unit_data = _get_unit_data_from_expr(unit_expr, registry.lut)
            base_value = unit_data[0]
            dimensions = unit_data[1]
            if len(unit_data) > 2:
                base_offset = unit_data[2]
                latex_repr = unit_data[3]
            else:
                base_offset = 0.0

        # Create obj with superclass construct.
        obj = super(Unit, cls).__new__(cls)

        # Attach attributes to obj.
        obj.expr = unit_expr
        obj.is_atomic = is_atomic
        obj.base_value = base_value
        obj.base_offset = base_offset
        obj.dimensions = dimensions
        obj._latex_repr = latex_repr
        obj.registry = registry
        # lets us avoid isinstance calls
        obj.is_Unit = True

        # if we parsed a string unit expression, cache the result
        # for faster lookup later
        if unit_cache_key is not None:
            registry._unit_object_cache[unit_cache_key] = obj

        # Return `obj` so __init__ can handle it.

        return obj
Exemplo n.º 9
0
def _lookup_unit_symbol(symbol_str, unit_symbol_lut):
    """
    Searches for the unit data tuple corresponding to the given symbol.

    Parameters
    ----------
    symbol_str : str
        The unit symbol to look up.
    unit_symbol_lut : dict
        Dictionary with symbols as keys and unit data tuples as values.

    """
    if symbol_str in unit_symbol_lut:
        # lookup successful, return the tuple directly
        return unit_symbol_lut[symbol_str]

    # could still be a known symbol with a prefix
    possible_prefix = symbol_str[0]

    if symbol_str[:2] == 'da':
        possible_prefix = 'da'

    if possible_prefix in unit_prefixes:
        # the first character could be a prefix, check the rest of the symbol
        symbol_wo_pref = symbol_str[1:]

        # deca is the only prefix with length 2
        if symbol_str[:2] == 'da':
            symbol_wo_pref = symbol_str[2:]
            possible_prefix = 'da'

        prefixable_units = [
            u for u in unit_symbol_lut if unit_symbol_lut[u][4]
        ]

        unit_is_si_prefixable = (symbol_wo_pref in unit_symbol_lut
                                 and symbol_wo_pref in prefixable_units)

        if unit_is_si_prefixable is True:
            # lookup successful, it's a symbol with a prefix
            unit_data = unit_symbol_lut[symbol_wo_pref]
            prefix_value = unit_prefixes[possible_prefix]

            if possible_prefix in latex_prefixes:
                latex_repr = symbol_str.replace(
                    possible_prefix,
                    '{' + latex_prefixes[possible_prefix] + '}')
            else:
                # Need to add some special handling for comoving units
                # this is fine for now, but it wouldn't work for a general
                # unit that has an arbitrary LaTeX representation
                if symbol_wo_pref != 'cm' and symbol_wo_pref.endswith('cm'):
                    sub_symbol_wo_prefix = symbol_wo_pref[:-2]
                    sub_symbol_str = symbol_str[:-2]
                else:
                    sub_symbol_wo_prefix = symbol_wo_pref
                    sub_symbol_str = symbol_str
                latex_repr = unit_data[3].replace(
                    '{' + sub_symbol_wo_prefix + '}',
                    '{' + sub_symbol_str + '}')

            # Leave offset and dimensions the same, but adjust scale factor and
            # LaTeX representation
            ret = (unit_data[0] * prefix_value, unit_data[1], unit_data[2],
                   latex_repr, False)

            unit_symbol_lut[symbol_str] = ret

            return ret

    # no dice
    raise UnitParseError("Could not find unit symbol '%s' in the provided "
                         "symbols." % symbol_str)