# SPDX-License-Identifier: Apache-2.0
# SPDX-FileCopyrightText: Copyright 2023-2024 TotalEnergies.
# SPDX-FileContributor: Alexandre Benedicto
# ruff: noqa: E402 # disable Module level import not at top of file
# Units conversion factors chosen in this file are from
# https://www.petrobasics.com/unit-convert
from enum import Enum
from typing import cast
from typing_extensions import Self
[docs]
class Unit:
def __init__(
self: Self, conversionMultiplier: float, conversionAdder: float, unitLabel: str
) -> None:
"""Unit enumeration.
Args:
conversionMultiplier (float): conversion multiplier
conversionAdder (float): conversion adder
unitLabel (str): symbol of the unit.
"""
self.conversionMultiplier: float = conversionMultiplier
self.conversionAdder: float = conversionAdder
self.unitLabel: str = unitLabel
[docs]
class Pressure(Enum):
PA = Unit(1.0, 0.0, "Pa")
KPA = Unit(1e-3, 0.0, "kPa")
MPA = Unit(1e-6, 0.0, "MPa")
GPA = Unit(1e-9, 0.0, "GPa")
BAR = Unit(1.0e-5, 0.0, "Bar")
PSI = Unit(0.00015, 0.0, "psi")
[docs]
class Length(Enum):
METER = Unit(1.0, 0.0, "m")
KILOMETER = Unit(1e-3, 0.0, "km")
FEET = Unit(3.28084, 0.0, "ft")
MILE = Unit(0.00062, 0.0, "mile")
[docs]
class Volume(Enum):
CUBIC_METER = Unit(1.0, 0.0, "m3")
CUBIC_FEET = Unit(35.31467, 0.0, "ft3")
BBL = Unit(6.28981, 0.0, "bbl")
[docs]
class Mass(Enum):
KG = Unit(1.0, 0.0, "kg")
TON = Unit(1e-3, 0.0, "ton")
MEGATON = Unit(1e-6, 0.0, "Mton")
POUND = Unit(2.20462, 0.0, "lb")
[docs]
class Density(Enum):
KG_PER_CUBIC_METER = Unit(1.0, 0.0, "kg/m3")
G_PER_CUBIC_CENTIMETER = Unit(0.001, 0.0, "g/cm3")
POUND_PER_BBL = Unit(0.35051, 0.0, "lb/bbl")
[docs]
class VolumetricRate(Enum):
CUBIC_METER_PER_SECOND = Unit(1.0, 0.0, "m3/s")
CUBIC_METER_PER_HOUR = Unit(3600.0, 0.0, "m3/h")
CUBIC_METER_PER_DAY = Unit(86400.0, 0.0, "m3/day")
BBL_PER_DAY = Unit(54343.962, 0.0, "bbl/day")
[docs]
class MassRate(Enum):
KG_PER_SECOND = Unit(1.0, 0.0, "kg/s")
KG_PER_HOUR = Unit(3600.0, 0.0, "kg/h")
KG_PER_DAY = Unit(86400.0, 0.0, "kg/day")
TON_PER_DAY = Unit(86.4, 0.0, "ton/day")
MTPA = Unit(0.0315576, 0.0, "MTPA")
[docs]
class Time(Enum):
SECOND = Unit(1.0, 0.0, "s")
HOUR = Unit(0.00028, 0.0, "h")
DAY = Unit(1.1574e-5, 0.0, "day")
MONTH = Unit(3.80263e-7, 0.0, "month")
YEAR = Unit(3.1688e-8, 0.0, "year")
[docs]
class Permeability(Enum):
SQUARE_METER = Unit(1.0, 0.0, "m2")
DARCY = Unit(1e12, 0.0, "D")
MILLI_DARCY = Unit(1e15, 0.0, "mD")
[docs]
class Temperature(Enum):
K = Unit(1.0, 0.0, "K")
CELSIUS = Unit(1.0, 273.15, "C")
FAHRENHEIT = Unit(1.8, -459.67, "F")
[docs]
class NoUnit(Enum):
SAME = Unit(1.0, 0.0, "")
associationPropertyUnitEnum: dict[str, Enum] = {
"pressure": cast(Enum, Pressure),
"bhp": cast(Enum, Pressure),
"stress": cast(Enum, Pressure),
"length": cast(Enum, Length),
"volumetricRate": cast(Enum, VolumetricRate),
"massRate": cast(Enum, MassRate),
"volume": cast(Enum, Volume),
"mass": cast(Enum, Mass),
"density": cast(Enum, Density),
"temperature": cast(Enum, Temperature),
"time": cast(Enum, Time),
"permeability": cast(Enum, Permeability),
"nounit": cast(Enum, NoUnit),
}
[docs]
def getPropertyUnitEnum(propertyName: str) -> Enum:
"""Get the Unit enum from property name.
Args:
propertyName (str): name of the property.
Returns:
Enum: Unit enum.
"""
return associationPropertyUnitEnum[propertyName]
[docs]
def getSIUnits() -> dict[str, Unit]:
"""Get the dictionary of property Names:Units.
Generates a dict where the keys are meta-properties
like pressure, mass etc ... and where the values are Unit
composed of a conversion factor and its unit associated.
Here, the conversion factor will always be 1.0 and the unit will
be the SI associate because we work with SI units.
Returns:
dict[str, Unit]: dictionary of unit names as keys and Unit enum as value.
"""
return {
"pressure": Pressure.PA.value,
"bhp": Pressure.PA.value,
"stress": Pressure.PA.value,
"length": Length.METER.value,
"volumetricRate": VolumetricRate.CUBIC_METER_PER_SECOND.value,
"massRate": MassRate.KG_PER_SECOND.value,
"volume": Volume.CUBIC_METER.value,
"mass": Mass.KG.value,
"density": Density.KG_PER_CUBIC_METER.value,
"temperature": Temperature.K.value,
"time": Time.SECOND.value,
"permeability": Permeability.SQUARE_METER.value,
"nounit": NoUnit.SAME.value,
}
[docs]
def convert(number: float, unitObj: Unit) -> float:
"""Converts a float number that has SI unit to a specific unit.
Args:
number (float): Number to convert.
unitObj (Unit): Object containing conversion multiplier and adder.
Returns:
float: number converted to the correct unit.
"""
return number * unitObj.conversionMultiplier + unitObj.conversionAdder
[docs]
def enumerationDomainUnit(enumObj: Enum) -> str:
"""Get the xml code corresponding to unit enum object for drop down list.
Args:
enumObj (Enum): Unit enum object.
Returns:
str: xml text.
"""
xml: str = """<EnumerationDomain name='enum'>"""
for i, unitObj in enumerate(list(enumObj)): # type: ignore[call-overload]
unit: Unit = unitObj.value
assert isinstance(unit, Unit), "enumObj does not contain Unit objects."
xml += f"""<Entry text='{unit.unitLabel}' value='{i}'/>"""
xml += """</EnumerationDomain>"""
return xml