Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions ResSimpy/Units/Units.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,14 @@ def unit_system_enum_to_variable(self, unit_system: UnitSystem) -> str:
case _:
raise ValueError(f'Unit system {unit_system} not recognised.')

def english_to_metbar(self, value: float) -> float:
"""Converts the value from English to metbar."""
return value

def metbar_to_english(self, value: float) -> float:
"""Converts the value from metbar to English."""
return value


class Area(UnitDimension):
"""Units for area."""
Expand Down Expand Up @@ -340,6 +348,14 @@ class SurfaceRatesLiquid(UnitDimension):
lab = 'stcc/hour'
metric_atm = 'stm3/day'

def english_to_metbar(self, value: float) -> float:
"""Converts the value from English to metbar."""
return value / 6.28981077

def metbar_to_english(self, value: float) -> float:
"""Converts the value from metbar to English."""
return value * 6.28981077


class SurfaceRatesGas(UnitDimension):
"""Units for surface rates gas."""
Expand Down Expand Up @@ -480,6 +496,14 @@ class Diameter(UnitDimension):
lab = 'cm'
metric_atm = 'cm'

def english_to_metbar(self, value: float) -> float:
"""Converts the value from English to metbar."""
return value * 2.54

def metbar_to_english(self, value: float) -> float:
"""Converts the value from metbar to English."""
return value / 2.54


class HeatTransfer(UnitDimension):
"""Heat transfer units."""
Expand Down
65 changes: 65 additions & 0 deletions ResSimpy/Units/convert_units.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
from typing import Callable, Optional, Protocol, TypeVar

from ResSimpy.Enums.UnitsEnum import UnitSystem
from ResSimpy.Units.AttributeMappings.BaseUnitMapping import BaseUnitMapping
from ResSimpy.Units.Units import UnitDimension


# Set up typing and generics for the convert_units function
class UnitConvertableObject(Protocol):
"""Protocol for objects that can be converted between unit systems.
Implements _unit_system and units properties.
"""
_unit_system: UnitSystem

@property
def units(self) -> BaseUnitMapping: ...

@property
def unit_system(self) -> UnitSystem: ...


T = TypeVar('T', bound=UnitConvertableObject)


def get_map(from_unit: UnitSystem, to_unit: UnitSystem, unit_dim: UnitDimension) -> Optional[Callable]:
mapping = {'ENGLISH_METBAR': 'english_to_metbar',
'METBAR_ENGLISH': 'metbar_to_english',
}

function_name = mapping[from_unit.value + '_' + to_unit.value]

function_call = getattr(unit_dim, function_name, None)
if function_call is None:
return None
return function_call


def convert_units(from_unit: UnitSystem, to_unit: UnitSystem, unit_dim: UnitDimension, value: float) -> float:
unit_mapping_function = get_map(from_unit, to_unit, unit_dim)
if unit_mapping_function is None:
return value
return unit_mapping_function(value)


def convert_object_units(obj: T, to_unit: UnitSystem) -> T:
"""Inplace conversion of all attributes on the provided object to the new unit system.

Args:
obj (UnitConvertableObject): The object to convert attributes of.
to_unit (UnitSystem): The unit system to convert to.
"""
for attr, value in obj.__dict__.items():
# TODO: use the typing in the dict rather than checking if it is a float
if isinstance(value, float):

attribute_unit_dims = obj.units.attribute_map.get(attr, None)
if attribute_unit_dims is None:
continue
converted_value = convert_units(obj.unit_system,
to_unit,
attribute_unit_dims,
value)
setattr(obj, attr, converted_value)
obj._unit_system = to_unit
return obj
22 changes: 22 additions & 0 deletions tests/Units/test_unit_conversion.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
from ResSimpy.Nexus.DataModels.Network.NexusWellConnection import NexusWellConnection
from ResSimpy.Nexus.NexusEnums.DateFormatEnum import DateFormat
from ResSimpy.Units.convert_units import convert_object_units
from ResSimpy.Enums.UnitsEnum import UnitSystem


def test_unit_object_conversion(mocker):
# Arrange

test_obj = NexusWellConnection(
properties_dict=dict(diameter=100.1),
unit_system=UnitSystem.ENGLISH, date='01/01/2020', date_format=DateFormat.DD_MM_YYYY
)

to_unit = UnitSystem.METBAR

# Act
result = convert_object_units(test_obj, to_unit)

# Assert
assert result.diameter == 100.1 * 2.54
assert result.units.unit_system == to_unit