예제 #1
0
def unit_database():
    """
    Fixture to be used whenever a test needs a clean UnitDatabase. When using this fixture, it's
    safe to call UnitDatabase.GetSingleton().
    """
    from barril.units.unit_database import UnitDatabase

    yield UnitDatabase.PushSingleton()
    UnitDatabase.PopSingleton()
예제 #2
0
def testDiscoverCloseUnitMatches() -> None:
    unit_database = UnitDatabase.CreateDefaultSingleton()
    assert unit_database.FindSimilarUnitMatches("kg/day") == ["kg/d"]
    assert unit_database.FindSimilarUnitMatches("bbls/d") == [
        "bbl/d", "bbl/d2"
    ]
    assert unit_database.FindSimilarUnitMatches("mg/l") == ["mg/L"]
예제 #3
0
 def GetValue(self, unit=None):
     return UnitDatabase.GetSingleton().Convert(
         self._value_quantity.GetQuantityType(),
         self._value_quantity.GetUnit(),
         unit or self._unit,
         self._value,
     )
예제 #4
0
파일: _quantity.py 프로젝트: ESSS/barril
    def _CreateDerived(
        cls,
        category_to_unit_and_exps: Dict[str, Sequence[Union[str, int]]],
        validate_category_and_units: bool = True,
        unknown_unit_caption: Optional[str] = None,
    ) -> "Quantity":
        """
        Create a category that represents a derived quantity (usually resulting from operations
        among quantities).

        :type category_to_unit_and_exps: OrderedDict(str->(list(str, int)))
        :param category_to_unit_and_exps:
            This odict defines the category as well as the unit in a way that we can specify exponents.

        :param bool validate_category_and_units:
            If True, the category and units will be validated (otherwise, it'll just create it
            without trying to validate any value (note that it's meant to be used internally
            to make creating the quantity faster without having to actually validate it)

        :rtype: cls
        :returns:
            An instance that represents a quantity with the given categories, units and exponents.
        """
        unit_database = UnitDatabase.GetSingleton()

        if validate_category_and_units:
            assert hasattr(
                category_to_unit_and_exps, "items"
            ), "validate_category_and_units needs to be a dict"
            for category, (unit, _exp) in category_to_unit_and_exps.items():
                # will do the checkings needed for validation (category/unit)
                # so that we can later just store the internal information without
                # any other check.

                # changes to accomodate making operations with derived units (internally the
                # information must be a dict)
                if category.__class__ != str:
                    raise TypeError("Only str is accepted. %s is not." % category.__class__)

                # Getting the category should be enough to know that it's valid.
                category_info = unit_database.GetCategoryInfo(category)

                # if no unit was given, assume base unit for this quantity_type
                if unit is None:
                    unit = category_info.default_unit
                else:
                    if not isinstance(unit, str):
                        raise TypeError("Only str is accepted. %s is not." % unit.__class__)
                    unit_database.CheckQuantityTypeUnit(category_info.quantity_type, unit)

        return ObtainQuantity(
            OrderedDict(
                (category, unit_and_exp[:])
                for (category, unit_and_exp) in category_to_unit_and_exps.items()
            ),
            unknown_unit_caption=unknown_unit_caption,
        )
예제 #5
0
파일: test_posc2.py 프로젝트: ESSS/barril
def testAllValidTemperatureUnitsHaveSameDefaultCategory() -> None:
    """'temperature' units should have 'temperature' as defined category (#45)"""
    unit_database = UnitDatabase.CreateDefaultSingleton()

    expected_category = "temperature"
    valid_temperature_units = unit_database.GetValidUnits(expected_category)

    for unit in valid_temperature_units:
        T = units.Scalar(1.0, unit)
        assert T.category == expected_category
예제 #6
0
def _ObtainQuantity(unit, category=None):
    if not category:
        unit_database = UnitDatabase.GetSingleton()
        category = unit_database.GetDefaultCategory(unit)
        if not category:
            raise InvalidUnitError(unit)

    key = (category, unit)
    quantity = _quantities_cache.get(key)
    if quantity is None:
        quantity = _quantities_cache.setdefault(
            key, _SimpleQuantity(category, unit))
    return quantity
예제 #7
0
    def _InternalCreateWithQuantity(self,
                                    quantity,
                                    values=None,
                                    unit_database=None,
                                    value=None):
        if value is not None:
            if values is not None:
                raise ValueError("Duplicated values parameter given")
            values = value

        self._value = values
        self._unit_database = unit_database or UnitDatabase.GetSingleton()
        self._quantity = quantity
        self._is_valid = None
        self._validity_exception = None
예제 #8
0
파일: _array.py 프로젝트: ESSS/barril
    def _InternalCreateWithQuantity(  # type:ignore[override]
        self,
        quantity: Quantity,
        values: Optional[ValuesType] = None,
        unit_database: Optional[UnitDatabase] = None,
        value: Optional[ValuesType] = None,
    ) -> None:
        if value is not None:
            if values is not None:
                raise ValueError("Duplicated values parameter given")
            values = value

        assert values is not None
        self._value = values
        self._unit_database = unit_database or UnitDatabase.GetSingleton()
        self._quantity = quantity
        self._is_valid: Optional[bool] = None
        self._validity_exception: Optional[Exception] = None
예제 #9
0
파일: _array.py 프로젝트: ESSS/barril
    def FromScalars(cls,
                    scalars: Iterable[Scalar],
                    *,
                    unit: Optional[str] = None,
                    category: Optional[str] = None) -> "Array":
        """
        Create an Array from a sequence of Scalars.

        When not defined, the unit and category assumed will be from the first Scalar on the sequence.

        :param values:
            A sequence of Scalars. When the values parameter is an empty sequence and
            the unit is not provided an Array with an empty quantity will be returned.

        :param unit:
            A string representing the unit, if not defined
            the unit from the first Scalar on the sequence will be used.

        :param category:
            A string representing the category, if not defined
            the category from the first Scalar on the sequence will be used.
        """
        scalars = iter(scalars)
        try:
            first_scalar = next(scalars)
        except StopIteration:
            if unit is None and category is None:
                return cls.CreateEmptyArray()
            elif category is None:
                category = UnitDatabase.GetSingleton().GetDefaultCategory(unit)
                return cls(values=[], unit=unit,
                           category=category)  # type:ignore[arg-type]
            else:
                assert unit is None
                return cls(  # type:ignore[call-overload]
                    values=[],
                    unit=unit,
                    category=category)  # This actually will raise an exception

        unit = unit or first_scalar.unit
        category = category or first_scalar.category
        values = [first_scalar.GetValue(unit)
                  ] + [scalar.GetValue(unit) for scalar in scalars]
        return cls(values=values, unit=unit, category=category)
예제 #10
0
    def __init__(self,
                 category: Union[str, Quantity],
                 value: Any = None,
                 unit: Optional[str] = None):
        # NOTE: we don't even try to add overloads here because each subclass defines their own
        # overloads, often in confusing ways; adding them here would just add to the
        # confusion.

        unit_database = UnitDatabase.GetSingleton()
        if isinstance(category, Quantity):
            quantity = category

            assert unit is None, "If quantity is given, the unit must not!"

            if value is None:
                value = self._GetDefaultValue(quantity.GetCategoryInfo())

        else:
            if not isinstance(category, str):
                # Support for creating a scalar as:
                # Scalar(10, 'm')
                # Scalar(10, 'm', 'length')
                value, unit, category = category, value, unit  # type:ignore[assignment]

            if value is None or unit is None:
                if value is None:
                    category_info = unit_database.GetCategoryInfo(category)
                    value = self._GetDefaultValue(category_info, unit)
                    if unit is None:
                        unit = category_info.default_unit
                        assert unit is not None
                else:
                    # Note: if the category is given with a value, the unit MUST also be given.
                    # (because if later we change the default unit, the original category would be wrong).
                    assert (
                        unit is not None
                    ), "If category and value are given, the unit must be specified too."

            quantity = ObtainQuantity(
                unit, category)  # type:ignore[arg-type, assignment]

        self._quantity = quantity
        self._InternalCreateWithQuantity(quantity, value, unit_database)
예제 #11
0
def testDivisionError():
    unit_database = UnitDatabase.CreateDefaultSingleton()
    for category, category_info in unit_database.categories_to_quantity_types.items(
    ):
        base_unit = category_info.default_unit
        for unit in unit_database.GetValidUnits(category):
            for i in [-1, 0, 1]:
                try:
                    unit_database.Convert(category, base_unit, unit, i)
                except Exception as e:
                    raise TypeError(
                        f"Error converting: from: {base_unit} to: {unit}"
                    ) from e

                try:
                    unit_database.Convert(category, unit, base_unit, i)
                except Exception as e:
                    raise TypeError(
                        f"Error converting: from: {unit} to: {base_unit}"
                    ) from e
예제 #12
0
def testDivisionError():
    unit_database = UnitDatabase.CreateDefaultSingleton()
    for category, category_info in six.iteritems(
            unit_database.categories_to_quantity_types):
        base_unit = category_info.default_unit
        for unit in unit_database.GetValidUnits(category):
            for i in [-1, 0, 1]:
                try:
                    unit_database.Convert(category, base_unit, unit, i)
                except Exception as e:
                    Reraise(
                        e, "Error converting: from: %s to: %s" %
                        (base_unit, unit))

                try:
                    unit_database.Convert(category, unit, base_unit, i)
                except Exception as e:
                    Reraise(
                        e, "Error converting: from: %s to: %s" %
                        (unit, base_unit))
예제 #13
0
    def __init__(self, category, value=None, unit=None):

        unit_database = UnitDatabase.GetSingleton()
        if category.__class__ == _Quantity:
            quantity = category

            assert unit is None, "If quantity is given, the unit must not!"

            if value is None:
                value = self._GetDefaultValue(quantity.GetCategoryInfo())

        else:
            if category.__class__ != six.text_type:
                # Support for creating a scalar as:
                # Scalar(10, 'm')
                # Scalar(10, 'm', 'length')
                value, unit, category = category, value, unit

            if value is None or unit is None:
                if value is None:
                    category_info = unit_database.GetCategoryInfo(category)
                    value = self._GetDefaultValue(category_info, unit)
                    if unit is None:
                        unit = category_info.default_unit
                        assert unit is not None
                else:
                    # Note: if the category is given with a value, the unit MUST also be given.
                    # (because if later we change the default unit, the original category would be wrong).
                    assert (
                        unit is not None
                    ), "If category and value are given, the unit must be specified too."

            assert type(unit) is not bytes
            assert type(category) is not bytes
            quantity = ObtainQuantity(unit, category)

        self._InternalCreateWithQuantity(quantity, value, unit_database)
예제 #14
0
파일: test_posc2.py 프로젝트: ESSS/barril
def testScfPerBblToM3ToM3() -> None:
    unit_database = UnitDatabase.CreateDefaultSingleton()
    expected = 0.17776487178535644
    obtained = unit_database.Convert("standard volume per volume", "scf/bbl",
                                     "scm(15C)/m3", 1.0)
    assert approx(abs(obtained - expected), 7) == 0
예제 #15
0
 def GetUnitDatabase(self):
     return UnitDatabase.GetSingleton()
예제 #16
0
파일: test_posc2.py 프로젝트: ESSS/barril
def testMegagramPerCubicMeterToKilogramPerCubicMeter() -> None:
    unit_database = UnitDatabase.CreateDefaultSingleton()
    expected = 1000
    obtained = unit_database.Convert("density", "Mg/m3", "kg/m3", 1)
    assert obtained == expected
예제 #17
0
 def GetQuantityType(self):
     unit_database = UnitDatabase.GetSingleton()
     return unit_database.GetCategoryQuantityType(self._category)
예제 #18
0
def testDefaultCaption() -> None:
    unit_database = UnitDatabase.CreateDefaultSingleton()
    category_info = unit_database.GetCategoryInfo("angle per volume")
    assert category_info.caption == "Angle per Volume"
예제 #19
0
파일: _quantity.py 프로젝트: ESSS/barril
def ObtainQuantity(
    unit: Union[str, None, List[UnitExponentTuple], Dict[str, Sequence[Union[str, int]]]],
    category: Optional[Union[Tuple[str, ...], str]] = None,
    unknown_unit_caption: Optional[str] = None,
) -> "Quantity":
    """
    :type unit: str or OrderedDict(str -> list(str, int))
    :param unit:
        Either the string representing the unit or an ordered dict with the composing unit
        information (if composing all the info, including the category will be received in this
        parameter).

    :param str category:
        The category for the quantity. If not given it's gotten based on the unit passed.

    :param str unknown_unit_caption:
        The caption for the unit (used if unknown).

    :rtype Quantity:
    """
    unit_database = UnitDatabase.GetSingleton()
    quantities_cache = unit_database.quantities_cache

    if isinstance(unit, (list, tuple)):
        # It may be a derived unit with list(tuple(str, int)) -- in which case the category
        # must also be a list (of the same size)
        if len(unit) == 1 and unit[0][1] == 1:
            # Although passed as composing, it's a simple case
            unit = unit[0][0]
            if isinstance(category, (list, tuple)):
                category = category[0]
        else:
            assert isinstance(category, (list, tuple))
            unit = OrderedDict((cat, unit_and_exp) for (cat, unit_and_exp) in zip(category, unit))
            category = None

    if isinstance(unit, dict):
        assert category is None
        if len(unit) == 1 and next(iter(unit.values()))[1] == 1:  # type:ignore[comparison-overlap]
            # Although passed as composing, it's a simple case
            category, (unit, _exp) = next(iter(unit.items()))  # type:ignore[assignment]
        else:
            key: List[Any] = [
                (category, tuple(unit_and_exp)) for (category, unit_and_exp) in unit.items()
            ]
            if unknown_unit_caption:
                key.append(unknown_unit_caption)
            try:
                return quantities_cache[tuple(key)]
            except KeyError:
                quantity = quantities_cache[tuple(key)] = Quantity(unit, None, unknown_unit_caption)
                return quantity

    key = (category, unit, unknown_unit_caption)  # type:ignore[assignment]
    try:
        return quantities_cache[key]
    except KeyError:
        pass  # Just go on with the regular flow.

    if not isinstance(unit, str):
        if category is None:
            raise AssertionError("Currently only supporting unit as a string.")
        else:
            # Unit is given by the category
            unit = unit_database.GetDefaultUnit(category)
        quantity = quantities_cache[key] = Quantity(category, unit, unknown_unit_caption)
        return quantity

    elif category is None:
        category = unit_database.GetDefaultCategory(unit)
        if not category:
            is_legacy, unit = FixUnitIfIsLegacy(unit)
            if is_legacy:
                category = unit_database.GetDefaultCategory(unit)
            else:
                raise UnitsError(f"Unable to get default category for: {unit}")

        key_with_resolved_category = (category, unit, unknown_unit_caption)
        try:
            return quantities_cache[key_with_resolved_category]
        except KeyError:
            quantity = quantities_cache[key_with_resolved_category] = Quantity(
                category, unit, unknown_unit_caption
            )
            # Cache it with None category too.
            quantities_cache[key] = quantity
            return quantity

    else:
        quantities_cache[key] = quantity = Quantity(category, unit, unknown_unit_caption)
        return quantity
예제 #20
0
def ObtainQuantity(unit, category=None, unknown_unit_caption=None):
    """
    :type unit: str or OrderedDict(str -> list(str, int))
    :param unit:
        Either the string representing the unit or an ordered dict with the composing unit
        information (if composing all the info, including the category will be received in this
        parameter).

    :param str category:
        The category for the quantity. If not given it's gotten based on the unit passed.

    :param str unknown_unit_caption:
        The caption for the unit (used if unknown).

    :rtype Quantity:
    """
    unit_database = UnitDatabase.GetSingleton()
    quantities_cache = unit_database.quantities_cache

    if unit.__class__ in (list, tuple):
        # It may be a derived unit with list(tuple(str, int)) -- in which case the category
        # must also be a list (of the same size)
        if len(unit) == 1 and unit[0][1] == 1:
            # Although passed as composing, it's a simple case
            unit = unit[0][0]
            if category.__class__ in (list, tuple):
                category = category[0]
        else:
            assert category.__class__ in (list, tuple)
            unit = OrderedDict((cat, unit_and_exp)
                               for (cat, unit_and_exp) in zip(category, unit))
            category = None

    if hasattr(unit, "items"):
        assert category is None
        if len(unit) == 1 and next(iter(unit.values()))[1] == 1:
            # Although passed as composing, it's a simple case
            category, (unit, _exp) = next(iter(unit.items()))
        else:
            key = tuple((category, tuple(unit_and_exp))
                        for (category, unit_and_exp) in unit.items())
            if unknown_unit_caption:
                key += (unknown_unit_caption, )
            try:
                return quantities_cache[key]
            except KeyError:
                quantity = quantities_cache[key] = _Quantity(
                    unit, None, unknown_unit_caption)
                return quantity

    key = (category, unit, unknown_unit_caption)
    try:
        return quantities_cache[key]
    except KeyError:
        pass  # Just go on with the regular flow.

    assert unit.__class__ != bytes, "unit must be given as str string always"
    if unit.__class__ != str:
        if category is None:
            raise AssertionError("Currently only supporting unit as a string.")
        else:
            # Unit is given by the category
            unit = unit_database.GetDefaultUnit(category)
        quantity = quantities_cache[key] = _Quantity(category, unit,
                                                     unknown_unit_caption)
        return quantity

    elif category is None:
        category = unit_database.GetDefaultCategory(unit)
        if not category:
            is_legacy, unit = FixUnitIfIsLegacy(unit)
            if is_legacy:
                category = unit_database.GetDefaultCategory(unit)
            else:
                raise UnitsError(f"Unable to get default category for: {unit}")

        key_with_resolved_category = (category, unit, unknown_unit_caption)
        try:
            return quantities_cache[key_with_resolved_category]
        except KeyError:
            quantity = quantities_cache[
                key_with_resolved_category] = _Quantity(
                    category, unit, unknown_unit_caption)
            # Cache it with None category too.
            quantities_cache[key] = quantity
            return quantity

    else:
        quantities_cache[key] = quantity = _Quantity(category, unit,
                                                     unknown_unit_caption)
        return quantity
예제 #21
0
    def __init__(self, category, unit, unknown_unit_caption=None):
        """
        :type category: str or odict
        :param category:
            The category to which the new quantity should be bound or an odict with information on
            the derived category/unit (in which case the unit parameter is ignored).

        :param str unit:
            The unit which the new quantity should have.
        """
        try:
            # Bail out if unit is already configured: this may happen when the Quantity constructor
            # is called directly: if Quantity() is called, the protocol will call __new__ and even
            # if a configured instance is returned, it'll call __init__ again, so, this is a check
            # for this situation so that we return if the quantity is already configured.
            self._unknown_unit_caption
            return
        except AttributeError:
            pass

        # Keep self._unit_database for faster access.
        unit_database = self._unit_database = UnitDatabase.GetSingleton()

        if unknown_unit_caption is not None:
            assert (
                unknown_unit_caption.__class__ == str
            ), "Unit caption must be a string. Note: the unit database and unit system parameters were removed."
            self._unknown_unit_caption = unknown_unit_caption
        else:
            self._unknown_unit_caption = ""

        if category.__class__ is OrderedDict:
            assert unit is None
            self._category_to_unit_and_exps = category
            self._is_derived = True

            rep_and_exp = OrderedDict()
            for category, (_unit,
                           exp) in self._category_to_unit_and_exps.items():
                quantity_type = unit_database.GetCategoryQuantityType(category)
                existing = rep_and_exp.get(quantity_type, 0)
                rep_and_exp[quantity_type] = existing + exp

            self._category = self._MakeStr([
                (category, exp)
                for category, (_unit,
                               exp) in self._category_to_unit_and_exps.items()
            ])
            self._quantity_type = self._MakeStr(list(rep_and_exp.items()))
            self._unit = self._CreateUnitsWithJoinedExponentsString()
            self._composing_units = tuple((unit, exp) for _category, (
                unit, exp) in self._category_to_unit_and_exps.items())
            self._composing_categories = tuple(
                self._category_to_unit_and_exps.keys())
            self._category_info = None
            return

        self._is_derived = False

        # changes to accommodate making operations with derived units (internally the
        # information must be a dict)
        if category.__class__ != str:
            raise TypeError("Only str is accepted. %s is not." %
                            category.__class__)

        # Getting the category should be enough to check that it's valid.
        category_info = unit_database.GetCategoryInfo(category)
        self._category_info = category_info

        # if no unit was given, assume base unit for this quantity_type
        if unit is None:
            unit = category_info.default_unit
        else:
            if unit.__class__ != str:
                raise TypeError("Only str is accepted. %s is not." %
                                unit.__class__)

            try:
                unit_database.CheckCategoryUnit(category, unit)
            except InvalidUnitError as e:
                is_legacy, unit = FixUnitIfIsLegacy(unit)
                if is_legacy:
                    unit_database.CheckCategoryUnit(category, unit)
                else:
                    raise e

        # store it as odict so that we can have a decent order when creating a string from
        # an operation.
        category_to_unit_and_exps = OrderedDict()
        category_to_unit_and_exps[category] = [unit, 1]
        self._category_to_unit_and_exps = category_to_unit_and_exps

        self._category = category
        self._quantity_type = unit_database.GetCategoryQuantityType(category)
        self._unit = unit
        self._tobase = self._unit_database.GetInfo(self._quantity_type,
                                                   self._unit,
                                                   fix_unknown=True).tobase
        self._composing_units = unit
        self._composing_categories = category
예제 #22
0
def testPoscPermeabilityLength(unit_database_posc):
    unit_database = UnitDatabase.GetSingleton()
    assert "volume" == unit_database.GetQuantityType("mD.ft")
    assert "permeability length" == unit_database.GetDefaultCategory("mD.ft")
예제 #23
0
def testOperation():
    unit_database = UnitDatabase.GetSingleton()
    unit_database.CheckDefaultUnitDatabase()
    q = ObtainQuantity(unit="m", category="length")

    SCALAR_OPERATION = [
        operator.add,
        operator.sub,
        operator.truediv,
        operator.mul,
        operator.mod,
    ]
    SCALAR_TYPES = six.integer_types + (float, )

    for operation in SCALAR_OPERATION:
        for cast_type in SCALAR_TYPES:
            # Left
            q2 = operation(cast_type(2), q)
            assert q2.GetQuantityType() == "length"
            assert q2.GetUnit() == "m"

            # Right
            q2 = operation(q, cast_type(2))
            assert q2.GetQuantityType() == "length"
            assert q2.GetUnit() == "m"

    q2 = abs(q)
    assert q2.GetQuantityType() == "length"
    assert q2.GetUnit() == "m"

    q2 = q + q
    assert q2.GetQuantityType() == "length"
    assert q2.GetUnit() == "m"

    q2 = q - q
    assert q2.GetQuantityType() == "length"
    assert q2.GetUnit() == "m"

    q2 = q * int(2)
    assert q2.GetQuantityType() == "length"
    assert q2.GetUnit() == "m"

    q2 = q * 2.0
    assert q2.GetQuantityType() == "length"
    assert q2.GetUnit() == "m"

    q2 = q * q
    assert q2.GetQuantityType() == "(length) ** 2"
    assert q2.GetUnit() == "m2"

    q2 = q * q * q
    assert q2.GetQuantityType() == "(length) ** 3"
    assert q2.GetUnit() == "m3"

    # Check pow
    q2 = q**1
    assert q2.GetQuantityType() == "length"
    assert q2.GetUnit() == "m"

    q2 = q**2
    assert q2.GetQuantityType() == "(length) ** 2"
    assert q2.GetUnit() == "m2"

    q2 = q**3
    assert q2.GetQuantityType() == "(length) ** 3"
    assert q2.GetUnit() == "m3"

    q2 = pow(q, 3)
    assert q2.GetQuantityType() == "(length) ** 3"
    assert q2.GetUnit() == "m3"

    q2 = (q * q) / q
    assert q2.GetQuantityType() == "length"
    assert q2.GetUnit() == "m"

    q2 = (q * q) / ObtainQuantity(unit="d", category="time")
    assert q2.GetQuantityType() == "(length) ** 2 / time"
    assert q2.GetUnit() == "m2/d"

    with pytest.raises(TypeError):
        operator.mul(q, "s")