# 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