from __future__ import annotations
from dataclasses import dataclass
from typing import Optional
from ResSimpy.Constraint import Constraint
from ResSimpy.Enums.UnitsEnum import UnitSystem
from ResSimpy.Utils import to_dict_generic
[docs]@dataclass
class NexusConstraint(Constraint):
"""Attributes:
name (str): name of the well (NAME)
max_surface_oil_rate (float): max surface oil rate (QOSMAX)
max_surface_gas_rate (float): max surface gas rate (QGSMAX)
max_surface_water_rate (float): max surface (QWSMAX)
max_surface_liquid_rate (float): max surface liquid rate (QLIQSMAX)
max_hc_molar_rate (float): Max hc molar rate (QMHCMAX)
max_reverse_surface_oil_rate (float): max reverse surface oil rate (QOSMAX-)
max_reverse_surface_gas_rate (float): max reverse surface gas rate (QGSMAX-)
max_reverse_surface_water_rate (float): max reverse surface water rate (QWSMAX-)
max_reverse_surface_liquid_rate (float): max reverse surface liquid rate (QLIQSMAX-)
max_reservoir_oil_rate (float): max reservoir oil rate (QOMAX)
max_reservoir_gas_rate (float): max reservoir gas rate (QGMAX)
max_reservoir_water_rate (float): max reservoir water rate (QWMAX)
max_reservoir_liquid_rate (float): max reservoir liquid rate (QLIQMAX)
max_reservoir_total_fluids_rate (float): max reservoir total fluids rate (QALLMAX)
max_reservoir_hc_rate (float): max reservoir hc rate (QHCMAX)
max_reverse_reservoir_oil_rate (float): max reverse reservoir oil rate (QOMAX-)
max_reverse_reservoir_gas_rate (float): max reverse reservoir gas rate (QGMAX-)
max_reverse_reservoir_water_rate (float): max reverse reservoir water rate (QWMAX-)
max_reverse_reservoir_liquid_rate (float): max reverse reservoir liquid rate (QLIQMAX-)
max_reverse_reservoir_total_fluids_rate (float): max reverse reservoir total fluids rate (QALLMAX-)
max_reverse_reservoir_hc_rate (float): max reverse reservoir hc rate (QHCMAX-)
min_pressure (float): min pressure (PMIN)
max_pressure (float): max pressure (PMAX)
max_wag_water_pressure (float): max wag water pressure (PWMAX)
max_wag_gas_pressure (float): max wag gas pressure (PGMAX)
bottom_hole_pressure (float): bottom hole pressure (BHP)
tubing_head_pressure (float): tubing head pressure (THP)
min_surface_oil_rate (float): min surface oil rate (QOSMIN)
min_surface_gas_rate (float): min surface gas rate (QGSMIN)
min_surface_water_rate (float): min surface water rate (QWSMIN)
min_surface_liquid_rate (float): min surface liquid rate (QLIQSMIN)
min_reservoir_oil_rate (float): min reservoir oil rate (QOMIN)
min_reservoir_gas_rate (float): min reservoir gas rate (QGMIN)
min_reservoir_water_rate (float): min reservoir water rate (QWMIN)
min_reservoir_liquid_rate (float): min reservoir liquid rate (QLIQMIN)
min_reservoir_total_fluids_rate (float): min reservoir total fluids rate (QALLMIN)
min_reservoir_hc_rate (float): min reservoir hc rate (QHCMIN)
min_reverse_surface_oil_rate (float): min reservoir oil rate (QOSMIN-)
min_reverse_surface_gas_rate (float): min reservoir gas rate (QGSMIN-)
min_reverse_surface_water_rate (float): min reservoir water rate (QWSMIN-)
min_reverse_surface_liquid_rate (float): min reservoir liquid rate (QLIQSMIN-)
min_reverse_reservoir_oil_rate (float): min reverse reservoir oil rate (QOMIN-)
min_reverse_reservoir_gas_rate (float): min reverse reservoir gas rate (QGMIN-)
min_reverse_reservoir_water_rate (float): min reverse reservoir water rate (QWMIN-)
min_reverse_reservoir_liquid_rate (float): min reverse reservoir liquid rate (QLIQMIN-)
min_reverse_reservoir_total_fluids_rate (float): min reverse reservoir total fluids rate (QALLMIN-)
min_reverse_reservoir_hc_rate (float): min reverse reservoir hc rate (QHCMIN-)
max_watercut (float): max watercut (WCUTMAX)
max_watercut_plug (float): max watercut plug (WCUTPLUG)
max_watercut_plugplus (float): max watercut plugplus (WCUTPLUGPLUS)
max_watercut_perf (float): max watercut perf (WCUTPERF)
max_watercut_perfplus (float): max watercut perfplus (WCUTPERFPLUS)
max_wor (float): max wor (WORMAX)
max_wor_plug (float): max wor plug (WORPLUG)
max_wor_plug_plus (float): max wor plug plus (WORPLUGPLUS)
max_wor_perf (float): max wor perf (WORPERF)
max_wor_perfplus (float): max wor perfplus (WORPERFPLUS)
max_gor (float): max gor (GORMAX)
max_gor_plug (float): max gor plug (GORPLUG)
max_gor_plug_plus (float): max gor plug plus (GORPLUGPLUS)
max_gor_perf (float): max gor perf (GORPERF)
max_gor_perfplus (float): max gor perfplus (GORPERFPLUS)
max_lgr (float): max lgr (LGRMAX)
max_lgr_plug (float): max lgr plug (LGRPLUG)
max_lgr_plug_plus (float): max lgr plug plus (LGRPLUGPLUS)
max_lgr_perf (float): max lgr perf (LGRPERF)
max_lgr_perfplus (float): max lgr perfplus (LGRPERFPLUS)
max_cum_gas_prod (float): max cum gas prod (CGLIM)
max_cum_water_prod (float): max cum water prod (CWLIM)
max_cum_oil_prod (float): max cum oil prod (COLIM).
artificial_lift_number (int): artificial lift number within the hydraulic table (ALQ)
max_choke_setting (float): maximum choke/ICD/valve setting (SETTING)
min_gas_lift_efficiency (float): minimum gas lift efficiency below which the connection will be shut in(GLEFMIN)
gl_additive_correction (float): additive correction term to value of gas/liquid ratio from optimal gl \
tables (GLRADD)
active_node (bool): active/inactive node/well (ACTIVATE)
pump_power (float): power for the pump (POWER)
pump_speed (float): maximum speed of the pump/esp/compressor (SPEED)
choke_limit (str): ON/OFF for whether the esp should exceed the choke limit (CHOKELIMIT)
manifold_position (int): position in the manifold for the well (POSITION)
"""
name: Optional[str] = None
well_name: Optional[str] = None
max_surface_oil_rate: Optional[float] = None
max_surface_gas_rate: Optional[float] = None
max_surface_water_rate: Optional[float] = None
max_surface_liquid_rate: Optional[float] = None
max_hc_molar_rate: Optional[float] = None
max_reverse_surface_oil_rate: Optional[float] = None
max_reverse_surface_gas_rate: Optional[float] = None
max_reverse_surface_water_rate: Optional[float] = None
max_reverse_surface_liquid_rate: Optional[float] = None
max_reservoir_oil_rate: Optional[float] = None
max_reservoir_gas_rate: Optional[float] = None
max_reservoir_water_rate: Optional[float] = None
max_reservoir_liquid_rate: Optional[float] = None
max_reservoir_total_fluids_rate: Optional[float] = None
max_reservoir_hc_rate: Optional[float] = None
max_reverse_reservoir_oil_rate: Optional[float] = None
max_reverse_reservoir_gas_rate: Optional[float] = None
max_reverse_reservoir_water_rate: Optional[float] = None
max_reverse_reservoir_liquid_rate: Optional[float] = None
max_reverse_reservoir_total_fluids_rate: Optional[float] = None
max_reverse_reservoir_hc_rate: Optional[float] = None
max_avg_comp_dp: Optional[float] = None
max_comp_dp: Optional[float] = None
min_pressure: Optional[float] = None
max_pressure: Optional[float] = None
max_wag_water_pressure: Optional[float] = None
max_wag_gas_pressure: Optional[float] = None
bottom_hole_pressure: Optional[float] = None
tubing_head_pressure: Optional[float] = None
min_reverse_surface_oil_rate: Optional[float] = None
min_reverse_surface_gas_rate: Optional[float] = None
min_reverse_surface_water_rate: Optional[float] = None
min_reverse_surface_liquid_rate: Optional[float] = None
min_surface_oil_rate: Optional[float] = None
min_surface_gas_rate: Optional[float] = None
min_surface_water_rate: Optional[float] = None
min_surface_liquid_rate: Optional[float] = None
min_reservoir_oil_rate: Optional[float] = None
min_reservoir_gas_rate: Optional[float] = None
min_reservoir_water_rate: Optional[float] = None
min_reservoir_liquid_rate: Optional[float] = None
min_reservoir_total_fluids_rate: Optional[float] = None
min_reservoir_hc_rate: Optional[float] = None
min_reverse_reservoir_oil_rate: Optional[float] = None
min_reverse_reservoir_gas_rate: Optional[float] = None
min_reverse_reservoir_water_rate: Optional[float] = None
min_reverse_reservoir_liquid_rate: Optional[float] = None
min_reverse_reservoir_total_fluids_rate: Optional[float] = None
min_reverse_reservoir_hc_rate: Optional[float] = None
max_watercut: Optional[float] = None
max_watercut_plug: Optional[float] = None
max_watercut_plugplus: Optional[float] = None
max_watercut_perf: Optional[float] = None
max_watercut_perfplus: Optional[float] = None
max_wor: Optional[float] = None
max_wor_plug: Optional[float] = None
max_wor_plug_plus: Optional[float] = None
max_wor_perf: Optional[float] = None
max_wor_perfplus: Optional[float] = None
max_gor: Optional[float] = None
max_gor_plug: Optional[float] = None
max_gor_plug_plus: Optional[float] = None
max_gor_perf: Optional[float] = None
max_gor_perfplus: Optional[float] = None
gor_limit: Optional[float] = None
gor_limit_exponent: Optional[float] = None
gor_limit_frequency: Optional[float] = None
max_lgr: Optional[float] = None
max_lgr_plug: Optional[float] = None
max_lgr_plug_plus: Optional[float] = None
max_lgr_perf: Optional[float] = None
max_lgr_perfplus: Optional[float] = None
max_cum_gas_prod: Optional[float] = None
max_cum_water_prod: Optional[float] = None
max_cum_oil_prod: Optional[float] = None
max_qmult_total_reservoir_rate: Optional[float] = None
convert_qmult_to_reservoir_barrels: Optional[bool] = None
qmult_oil_rate: Optional[float] = None
qmult_water_rate: Optional[float] = None
qmult_gas_rate: Optional[float] = None
use_qmult_qoil_surface_rate: Optional[bool] = None
use_qmult_qwater_surface_rate: Optional[bool] = None
use_qmult_qgas_surface_rate: Optional[bool] = None
use_qmult_qoilqwat_surface_rate: Optional[bool] = None
artificial_lift_number: Optional[int] = None
max_choke_setting: Optional[float] = None
min_gas_lift_efficiency: Optional[float] = None
gl_additive_correction: Optional[float] = None
active_node: Optional[bool] = None
pump_power: Optional[float] = None
pump_speed: Optional[float] = None
choke_limit: Optional[str] = None
manifold_position: Optional[int] = None
clear_all: Optional[bool] = None
clear_q: Optional[bool] = None
clear_limit: Optional[bool] = None
clear_alq: Optional[bool] = None
clear_p: Optional[bool] = None
[docs] def __init__(self, properties_dict: dict[str, None | int | str | float | UnitSystem]) -> None:
super().__init__()
for key, prop in properties_dict.items():
self.__setattr__(key, prop)
[docs] @staticmethod
def get_keyword_mapping() -> dict[str, tuple[str, type]]:
"""Gets the mapping of nexus keywords to attribute definitions."""
nexus_mapping: dict[str, tuple[str, type]] = {
'WELL': ('well_name', str),
'NAME': ('name', str),
# Specialkeywords - QMULT
'QALLRMAX_MULT': ('convert_qmult_to_reservoir_barrels', bool),
'QOSMAX_MULT': ('use_qmult_qoil_surface_rate', bool),
'QWSMAX_MULT': ('use_qmult_qwater_surface_rate', bool),
'QGSMAX_MULT': ('use_qmult_qgas_surface_rate', bool),
'QLIQSMAX_MULT': ('use_qmult_qoilqwat_surface_rate', bool),
# SpecialClearkeywords
'CLEAR': ('clear_all', bool),
'CLEARQ': ('clear_q', bool),
'CLEARP': ('clear_p', bool),
'CLEARLIMIT': ('clear_limit', bool),
'CLEARALQ': ('clear_alq', bool),
}
nexus_mapping.update(NexusConstraint.get_limit_constraints_map())
nexus_mapping.update(NexusConstraint.get_pressure_constraints_map())
nexus_mapping.update(NexusConstraint.get_rate_constraints_map())
nexus_mapping.update(NexusConstraint.get_alq_constraints_map())
return nexus_mapping
@staticmethod
def get_rate_constraints_map() -> dict[str, tuple[str, type]]:
nexus_mapping: dict[str, tuple[str, type]] = {
'QALLRMAX': ('max_qmult_total_reservoir_rate', float),
'QOSMAX': ('max_surface_oil_rate', float),
'QGSMAX': ('max_surface_gas_rate', float),
'QWSMAX': ('max_surface_water_rate', float),
'QLIQSMAX': ('max_surface_liquid_rate', float),
'QMHCMAX': ('max_hc_molar_rate', float),
'QOSMAX-': ('max_reverse_surface_oil_rate', float),
'QGSMAX-': ('max_reverse_surface_gas_rate', float),
'QWSMAX-': ('max_reverse_surface_water_rate', float),
'QLIQSMAX-': ('max_reverse_surface_liquid_rate', float),
'QOMAX': ('max_reservoir_oil_rate', float),
'QGMAX': ('max_reservoir_gas_rate', float),
'QWMAX': ('max_reservoir_water_rate', float),
'QLIQMAX': ('max_reservoir_liquid_rate', float),
'QALLMAX': ('max_reservoir_total_fluids_rate', float),
'QHCMAX': ('max_reservoir_hc_rate', float),
'QOMAX-': ('max_reverse_reservoir_oil_rate', float),
'QGMAX-': ('max_reverse_reservoir_gas_rate', float),
'QWMAX-': ('max_reverse_reservoir_water_rate', float),
'QLIQMAX-': ('max_reverse_reservoir_liquid_rate', float),
'QALLMAX-': ('max_reverse_reservoir_total_fluids_rate', float),
'QHCMAX-': ('max_reverse_reservoir_hc_rate', float),
'QOSMIN': ('min_surface_oil_rate', float),
'QGSMIN': ('min_surface_gas_rate', float),
'QWSMIN': ('min_surface_water_rate', float),
'QLIQSMIN': ('min_surface_liquid_rate', float),
'QOMIN': ('min_reservoir_oil_rate', float),
'QGMIN': ('min_reservoir_gas_rate', float),
'QWMIN': ('min_reservoir_water_rate', float),
'QLIQMIN': ('min_reservoir_liquid_rate', float),
'QALLMIN': ('min_reservoir_total_fluids_rate', float),
'QHCMIN': ('min_reservoir_hc_rate', float),
'QOSMIN-': ('min_reverse_surface_oil_rate', float),
'QGSMIN-': ('min_reverse_surface_gas_rate', float),
'QWSMIN-': ('min_reverse_surface_water_rate', float),
'QLIQSMIN-': ('min_reverse_surface_liquid_rate', float),
'QOMIN-': ('min_reverse_reservoir_oil_rate', float),
'QGMIN-': ('min_reverse_reservoir_gas_rate', float),
'QWMIN-': ('min_reverse_reservoir_water_rate', float),
'QLIQMIN-': ('min_reverse_reservoir_liquid_rate', float),
'QALLMIN-': ('min_reverse_reservoir_total_fluids_rate', float),
'QHCMIN-': ('min_reverse_reservoir_hc_rate', float),
'QOIL': ('qmult_oil_rate', float),
'QWATER': ('qmult_water_rate', float),
'QGAS': ('qmult_gas_rate', float),
'DPBHAVG': ('max_avg_comp_dp', float),
'DPBHMX': ('max_comp_dp', float),
}
return nexus_mapping
@staticmethod
def get_pressure_constraints_map() -> dict[str, tuple[str, type]]:
nexus_mapping: dict[str, tuple[str, type]] = {
'PMIN': ('min_pressure', float),
'PMAX': ('max_pressure', float),
'PWMAX': ('max_wag_water_pressure', float),
'PGMAX': ('max_wag_gas_pressure', float),
'BHP': ('bottom_hole_pressure', float),
'THP': ('tubing_head_pressure', float),
}
return nexus_mapping
@staticmethod
def get_limit_constraints_map() -> dict[str, tuple[str, type]]:
nexus_mapping: dict[str, tuple[str, type]] = {
'WCUTMAX': ('max_watercut', float),
'WCUTPLUG': ('max_watercut_plug', float),
'WCUTPLUGPLUS': ('max_watercut_plugplus', float),
'WCUTPERF': ('max_watercut_perf', float),
'WCUTPERFPLUS': ('max_watercut_perfplus', float),
'WORMAX': ('max_wor', float),
'WORPLUG': ('max_wor_plug', float),
'WORPLUGPLUS': ('max_wor_plug_plus', float),
'WORPERF': ('max_wor_perf', float),
'WORPERFPLUS': ('max_wor_perfplus', float),
'GORLIM': ('gor_limit', float),
'EXPONENT': ('gor_limit_exponent', float),
'FREQUENCY': ('gor_limit_frequency', float),
'GORMAX': ('max_gor', float),
'GORPLUG': ('max_gor_plug', float),
'GORPLUGPLUS': ('max_gor_plug_plus', float),
'GORPERF': ('max_gor_perf', float),
'GORPERFPLUS': ('max_gor_perfplus', float),
'LGRMAX': ('max_lgr', float),
'LGRPLUG': ('max_lgr_plug', float),
'LGRPLUGPLUS': ('max_lgr_plug_plus', float),
'LGRPERF': ('max_lgr_perf', float),
'LGRPERFPLUS': ('max_lgr_perfplus', float),
'CGLIM': ('max_cum_gas_prod', float),
'CWLIM': ('max_cum_water_prod', float),
'COLIM': ('max_cum_oil_prod', float),
}
return nexus_mapping
[docs] @staticmethod
def get_alq_constraints_map() -> dict[str, tuple[str, type]]:
"""Gets the nexus mapping for artificial lift constraints."""
nexus_mapping: dict[str, tuple[str, type]] = {
'ALQ': ('artificial_lift_number', int),
'SETTING': ('max_choke_setting', float),
'GLEFMIN': ('min_gas_lift_efficiency', float),
'GLRADD': ('gl_additive_correction', float),
'ACTIVATE': ('active_node', bool),
'POWER': ('pump_power', float),
'SPEED': ('pump_speed', float),
'CHOKELIMIT': ('choke_limit', str),
'POSITION': ('manifold_position', int),
}
return nexus_mapping
[docs] def to_dict(self, keys_in_nexus_style: bool = False) -> dict[str, None | str | int | float]:
"""Returns a dictionary of the attributes of the Constraint.
Args:
keys_in_nexus_style (bool): if True returns the key values in Nexus keywords, otherwise returns the \
attribute name as stored by ressimpy.
Returns:
a dictionary keyed by attributes and values as the value of the attribute
"""
result_dict = to_dict_generic.to_dict(self, keys_in_nexus_style, add_date=True, add_units=True)
return result_dict
[docs] def update(self, new_data: dict[str, None | int | str | float | UnitSystem], nones_overwrite: bool = False):
"""Updates attributes in the object based on the dictionary provided."""
for k, v in new_data.items():
if v is not None or nones_overwrite:
setattr(self, k, v)
[docs] def to_table_line(self) -> str:
"""String representation of the constraint for entry to an inline constraint table."""
qmult_control_key_words = ['QALLRMAX_MULT', 'QOSMAX_MULT', 'QWSMAX_MULT', 'QGSMAX_MULT', 'QLIQSMAX_MULT']
skip_attributes = ['date', 'unit_system', 'NAME', 'ACTIVATE', 'QOIL', 'QWATER', 'QGAS', 'WELL']
clear_attributes = ['CLEAR', 'CLEARQ', 'CLEARP', 'CLEARLIMIT', 'CLEARALQ']
if self.name is not None:
constraint_string = self.name
elif self.well_name is not None:
constraint_string = self.well_name
else:
raise ValueError('Must have a well or node name for returning a constraint to a string')
for attribute, value in self.to_dict(keys_in_nexus_style=True).items():
if value and attribute in qmult_control_key_words:
constraint_string += (' ' + attribute.replace('_MULT', '') + ' MULT')
elif value is None or attribute in skip_attributes:
continue
elif value and attribute in clear_attributes:
constraint_string += ' ' + attribute
else:
constraint_string += (' ' + attribute + ' ' + str(value))
if self.active_node:
constraint_string += ' ACTIVATE'
elif self.active_node is not None:
# equivalent to active node being False
constraint_string += ' DEACTIVATE'
constraint_string += '\n'
return constraint_string
[docs] def write_qmult_table(self) -> list[str]:
"""Writes out a QMULT table from a constraint that uses the following attributes.
'QOIL': ('qmult_oil_rate', float).
'QWATER': ('qmult_water_rate', float).
'QGAS': ('qmult_gas_rate', float).
Returns:
list[str] with a representation of the QMULT table with a new line as a new entry in the list.
"""
table_to_return = ['QMULT\n', 'WELL QOIL QGAS QWATER\n']
qmult_values = self.write_qmult_values()
table_to_return.append(qmult_values)
table_to_return.append('ENDQMULT\n')
return table_to_return
[docs] def write_qmult_values(self) -> str:
"""Writes out the values for a QMULT table, callable on its own or using the write_qmult_table method."""
qmult_values_keywords = ['qmult_oil_rate', 'qmult_gas_rate', 'qmult_water_rate']
if self.name is not None:
string_to_return = self.name
elif self.well_name is not None:
string_to_return = self.well_name
else:
raise ValueError('Must have a well or node name for returning a qmult table')
for keyword in qmult_values_keywords:
value = getattr(self, keyword, None)
if value is None:
string_to_return += ' NA'
else:
string_to_return += f' {str(value)}'
string_to_return += '\n'
return string_to_return