Exemplo n.º 1
0
def dimensions_and_type_from_string(unit_string):
    '''
    Returns the physical dimensions that results from evaluating a string like
    "siemens / metre ** 2", allowing for the special string "1" to signify
    dimensionless units, the string "boolean" for a boolean and "integer" for
    an integer variable.

    Parameters
    ----------
    unit_string : str
        The string that should evaluate to a unit

    Returns
    -------
    d, type : (`Dimension`, {FLOAT, INTEGER or BOOL})
        The resulting physical dimensions and the type of the variable.

    Raises
    ------
    ValueError
        If the string cannot be evaluated to a unit.
    '''
    # Lazy import to avoid circular dependency
    from brian2.core.namespace import DEFAULT_UNITS
    global _base_units  # we only want to do this once
    global _single_base_units

    if _base_units is None:
        base_units_for_dims = {}
        _base_units = collections.OrderedDict()
        for unit_name, unit in DEFAULT_UNITS.iteritems():
            if float(unit) == 1.0:
                _base_units[unit_name] = unit
        # Go through it a second time -- we only want to display one unit per
        # dimensionality to the user and don't bother displaying powered units
        # (meter2, meter3, ...)
        for unit in _base_units.itervalues():
            if (unit.dim not in base_units_for_dims
                    and repr(unit)[-1] not in ['2', '3']):
                base_units_for_dims[unit.dim] = unit
        _single_base_units = sorted(
            [repr(unit) for unit in base_units_for_dims.itervalues()])

    unit_string = unit_string.strip()

    # Special case: dimensionless unit
    if unit_string == '1':
        return DIMENSIONLESS, FLOAT

    # Another special case: boolean variable
    if unit_string == 'boolean':
        return DIMENSIONLESS, BOOLEAN
    if unit_string == 'bool':
        raise TypeError("Use 'boolean' not 'bool' as the unit for a boolean "
                        "variable.")

    # Yet another special case: integer variable
    if unit_string == 'integer':
        return DIMENSIONLESS, INTEGER

    # Check first whether the expression only refers to base units
    identifiers = get_identifiers(unit_string)
    for identifier in identifiers:
        if identifier not in _base_units:
            if identifier in DEFAULT_UNITS:
                # A known unit, but not a base unit
                base_unit = get_unit(DEFAULT_UNITS[identifier].dim)
                if not repr(base_unit) in _base_units:
                    # Make sure that we don't suggest a unit that is not allowed
                    # (should not happen, normally)
                    base_unit = Unit(1, dim=base_unit.dim)
                raise ValueError(
                    ('Unit specification refers to '
                     '"{identifier}", but this is not a base '
                     'unit. Use "{base_unit}" '
                     'instead.').format(identifier=identifier,
                                        base_unit=repr(base_unit)))
            else:
                # Not a known unit
                allowed = ', '.join(_single_base_units)
                raise ValueError(('Unit specification refers to '
                                  '"{identifier}", but this is not a base '
                                  'unit. The following base units are '
                                  'allowed: {allowed_units} (plus some '
                                  'variants of these, e.g. "Hz" instead of '
                                  '"hertz", or "meter" instead of '
                                  '"metre").').format(identifier=identifier,
                                                      allowed_units=allowed))
    try:
        evaluated_unit = eval(unit_string, _base_units)
    except Exception as ex:
        raise ValueError(('Could not interpret "%s" as a unit specification: '
                          '%s') % (unit_string, ex))

    # Check whether the result is a unit
    if not isinstance(evaluated_unit, Unit):
        if isinstance(evaluated_unit, Quantity):
            raise ValueError(
                ('"%s" does not evaluate to a unit but to a '
                 'quantity -- make sure to only use units, e.g. '
                 '"siemens/metre**2" and not "1 * siemens/metre**2"') %
                unit_string)
        else:
            raise ValueError(
                ('"%s" does not evaluate to a unit, the result '
                 'has type %s instead.' % (unit_string, type(evaluated_unit))))

    # No error has been raised, all good
    return evaluated_unit.dim, FLOAT
Exemplo n.º 2
0
def dimensions_and_type_from_string(unit_string):
    '''
    Returns the physical dimensions that results from evaluating a string like
    "siemens / metre ** 2", allowing for the special string "1" to signify
    dimensionless units, the string "boolean" for a boolean and "integer" for
    an integer variable.

    Parameters
    ----------
    unit_string : str
        The string that should evaluate to a unit

    Returns
    -------
    d, type : (`Dimension`, {FLOAT, INTEGER or BOOL})
        The resulting physical dimensions and the type of the variable.

    Raises
    ------
    ValueError
        If the string cannot be evaluated to a unit.
    '''
    # Lazy import to avoid circular dependency
    from brian2.core.namespace import DEFAULT_UNITS
    global _base_units_with_alternatives
    global _base_units
    if _base_units_with_alternatives is None:
        base_units_for_dims = {}
        for unit_name, unit in reversed(DEFAULT_UNITS.items()):
            if float(unit) == 1.0 and repr(unit)[-1] not in ['2', '3']:
                if unit.dim in base_units_for_dims:
                    if unit_name not in base_units_for_dims[unit.dim]:
                        base_units_for_dims[unit.dim].append(unit_name)
                else:
                    base_units_for_dims[unit.dim] = [repr(unit)]
                    if unit_name != repr(unit):
                        base_units_for_dims[unit.dim].append(unit_name)
        alternatives = sorted(
            [tuple(values) for values in base_units_for_dims.itervalues()])
        _base_units = dict([(v, DEFAULT_UNITS[v]) for values in alternatives
                            for v in values])
        # Create a string that lists all allowed base units
        alternative_strings = []
        for units in alternatives:
            string = units[0]
            if len(units) > 1:
                string += ' ({other_units})'.format(
                    other_units=', '.join(units[1:]))
            alternative_strings.append(string)
        _base_units_with_alternatives = ', '.join(alternative_strings)

    unit_string = unit_string.strip()

    # Special case: dimensionless unit
    if unit_string == '1':
        return DIMENSIONLESS, FLOAT

    # Another special case: boolean variable
    if unit_string == 'boolean':
        return DIMENSIONLESS, BOOLEAN
    if unit_string == 'bool':
        raise TypeError("Use 'boolean' not 'bool' as the unit for a boolean "
                        "variable.")

    # Yet another special case: integer variable
    if unit_string == 'integer':
        return DIMENSIONLESS, INTEGER

    # Check first whether the expression only refers to base units
    identifiers = get_identifiers(unit_string)
    for identifier in identifiers:
        if identifier not in _base_units:
            if identifier in DEFAULT_UNITS:
                # A known unit, but not a base unit
                base_unit = get_unit(DEFAULT_UNITS[identifier].dim)
                if not repr(base_unit) in _base_units:
                    # Make sure that we don't suggest a unit that is not allowed
                    # (should not happen, normally)
                    base_unit = Unit(1, dim=base_unit.dim)
                raise ValueError(
                    ('Unit specification refers to '
                     '"{identifier}", but this is not a base '
                     'unit. Use "{base_unit}" '
                     'instead.').format(identifier=identifier,
                                        base_unit=repr(base_unit)))
            else:
                # Not a known unit
                raise ValueError(
                    ('Unit specification refers to '
                     '"{identifier}", but this is not a base '
                     'unit. The following base units are '
                     'allowed: '
                     '{allowed_units}.').format(
                         identifier=identifier,
                         allowed_units=_base_units_with_alternatives))
    try:
        evaluated_unit = eval(unit_string, _base_units)
    except Exception as ex:
        raise ValueError(('Could not interpret "%s" as a unit specification: '
                          '%s') % (unit_string, ex))

    # Check whether the result is a unit
    if not isinstance(evaluated_unit, Unit):
        if isinstance(evaluated_unit, Quantity):
            raise ValueError(
                ('"%s" does not evaluate to a unit but to a '
                 'quantity -- make sure to only use units, e.g. '
                 '"siemens/metre**2" and not "1 * siemens/metre**2"') %
                unit_string)
        else:
            raise ValueError(
                ('"%s" does not evaluate to a unit, the result '
                 'has type %s instead.' % (unit_string, type(evaluated_unit))))

    # No error has been raised, all good
    return evaluated_unit.dim, FLOAT
Exemplo n.º 3
0
def dimensions_and_type_from_string(unit_string):
    '''
    Returns the physical dimensions that results from evaluating a string like
    "siemens / metre ** 2", allowing for the special string "1" to signify
    dimensionless units, the string "boolean" for a boolean and "integer" for
    an integer variable.

    Parameters
    ----------
    unit_string : str
        The string that should evaluate to a unit

    Returns
    -------
    d, type : (`Dimension`, {FLOAT, INTEGER or BOOL})
        The resulting physical dimensions and the type of the variable.

    Raises
    ------
    ValueError
        If the string cannot be evaluated to a unit.
    '''
    # Lazy import to avoid circular dependency
    from brian2.core.namespace import DEFAULT_UNITS
    global _base_units_with_alternatives
    global _base_units
    if _base_units_with_alternatives is None:
        base_units_for_dims = {}
        for unit_name, unit in reversed(DEFAULT_UNITS.items()):
            if float(unit) == 1.0 and repr(unit)[-1] not in ['2', '3']:
                if unit.dim in base_units_for_dims:
                    if unit_name not in base_units_for_dims[unit.dim]:
                        base_units_for_dims[unit.dim].append(unit_name)
                else:
                    base_units_for_dims[unit.dim] = [repr(unit)]
                    if unit_name != repr(unit):
                        base_units_for_dims[unit.dim].append(unit_name)
        alternatives = sorted([tuple(values) for values in base_units_for_dims.itervalues()])
        _base_units = dict([(v, DEFAULT_UNITS[v])
                            for values in alternatives for v in values])
        # Create a string that lists all allowed base units
        alternative_strings = []
        for units in alternatives:
            string = units[0]
            if len(units) > 1:
                string += ' ({other_units})'.format(other_units=', '.join(units[1:]))
            alternative_strings.append(string)
        _base_units_with_alternatives = ', '.join(alternative_strings)

    unit_string = unit_string.strip()

    # Special case: dimensionless unit
    if unit_string == '1':
        return DIMENSIONLESS, FLOAT

    # Another special case: boolean variable
    if unit_string == 'boolean':
        return DIMENSIONLESS, BOOLEAN
    if unit_string == 'bool':
        raise TypeError("Use 'boolean' not 'bool' as the unit for a boolean "
                        "variable.")

    # Yet another special case: integer variable
    if unit_string == 'integer':
        return DIMENSIONLESS, INTEGER

    # Check first whether the expression only refers to base units
    identifiers = get_identifiers(unit_string)
    for identifier in identifiers:
        if identifier not in _base_units:
            if identifier in DEFAULT_UNITS:
                # A known unit, but not a base unit
                base_unit = get_unit(DEFAULT_UNITS[identifier].dim)
                if not repr(base_unit) in _base_units:
                    # Make sure that we don't suggest a unit that is not allowed
                    # (should not happen, normally)
                    base_unit = Unit(1, dim=base_unit.dim)
                raise ValueError(('Unit specification refers to '
                                  '"{identifier}", but this is not a base '
                                  'unit. Use "{base_unit}" '
                                  'instead.').format(identifier=identifier,
                                                     base_unit=repr(base_unit)))
            else:
                # Not a known unit
                raise ValueError(('Unit specification refers to '
                                  '"{identifier}", but this is not a base '
                                  'unit. The following base units are '
                                  'allowed: '
                                  '{allowed_units}.').format(identifier=identifier,
                                                             allowed_units=_base_units_with_alternatives))
    try:
        evaluated_unit = eval(unit_string, _base_units)
    except Exception as ex:
        raise ValueError(('Could not interpret "%s" as a unit specification: '
                          '%s') % (unit_string, ex))

    # Check whether the result is a unit
    if not isinstance(evaluated_unit, Unit):
        if isinstance(evaluated_unit, Quantity):
            raise ValueError(('"%s" does not evaluate to a unit but to a '
                              'quantity -- make sure to only use units, e.g. '
                              '"siemens/metre**2" and not "1 * siemens/metre**2"') %
                             unit_string)
        else:
            raise ValueError(('"%s" does not evaluate to a unit, the result '
                             'has type %s instead.' % (unit_string,
                                                       type(evaluated_unit))))

    # No error has been raised, all good
    return evaluated_unit.dim, FLOAT