Skip to content

Commit

Permalink
Add brine p (#417)
Browse files Browse the repository at this point in the history
* Add BRINE support

* Fix WSALT keyword generation (implicit fix for BRINE keyword not showing up)

* Support for generating WSALT keyword in HISTORY_SCHEDULE.inc

* added vector WSPR

* added also support for  WSPT, WPIT, WPIR

* removed WSIR from WCONHIST

Co-authored-by: Wouter J. de Bruin <wouterjdb@hotmail.com>
Co-authored-by: Ubuntu <egbertspjp@FlowNetVM.5wpzv3yfndyepfomm2aamy43xf.ax.internal.cloudapp.net>
Co-authored-by: Ole Petter Lødøen <opl@equinor.com>
  • Loading branch information
4 people authored Jun 29, 2021
1 parent f1ce2b2 commit 0ea8158
Show file tree
Hide file tree
Showing 11 changed files with 258 additions and 3 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ This project adheres to [Semantic Versioning](https://semver.org/).
## Unreleased

### Added
- [#417] (https://github.com/equinor/flownet/pull/417) Added functionality to history match dissolved salts (TDS) in produced water.
- [#404](https://github.com/equinor/flownet/pull/404) Added possibility for regional multipliers for permeability, porosity and bulkvolume multiplier. Current implementation allows for defining either one global multiplier, or a regional multipliers based on a region parameter extracted from an existing simulation model (typically FIPNUM, EQLNUM, SATNUM etc). The regional multiplier will be in addition to the per tube multipliers. New keys in config yaml are: porosity_regional_scheme (global, individual or regions_from_sim), porosity_regional (define prior same way as for other model parameters) and porosity_parameter_from_sim_model (name of region parameter in simulation model). The same three keys exists for permeability and bulkvolume_mult.
- [#383](https://github.com/equinor/flownet/pull/383) Added option to either define a prior distribution for KRWMAX directly by using krwmax in the config yaml, or to let KRWMAX be calculated as KRWEND + delta. To do the latter, set krwmax_add_to_krwend to true, and then the prior distribution definition in the config yaml for krwmax will be interpreted as a prior distribution for the delta value to be added to KRWEND to get the KRWMAX.
- [#386](https://github.com/equinor/flownet/pull/386) Expose FlowNet timeout to user.
Expand Down
52 changes: 52 additions & 0 deletions src/flownet/config_parser/_config_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,58 @@ def _to_abs_path(path: Optional[str]) -> str:
},
},
},
"WSPR": {
MK.Type: types.NamedDict,
MK.Content: {
"rel_error": {
MK.Type: types.Number,
MK.AllowNone: True,
},
"min_error": {
MK.Type: types.Number,
MK.AllowNone: True,
},
},
},
"WSPT": {
MK.Type: types.NamedDict,
MK.Content: {
"rel_error": {
MK.Type: types.Number,
MK.AllowNone: True,
},
"min_error": {
MK.Type: types.Number,
MK.AllowNone: True,
},
},
},
"WSIR": {
MK.Type: types.NamedDict,
MK.Content: {
"rel_error": {
MK.Type: types.Number,
MK.AllowNone: True,
},
"min_error": {
MK.Type: types.Number,
MK.AllowNone: True,
},
},
},
"WSIT": {
MK.Type: types.NamedDict,
MK.Content: {
"rel_error": {
MK.Type: types.Number,
MK.AllowNone: True,
},
"min_error": {
MK.Type: types.Number,
MK.AllowNone: True,
},
},
},
},
},
"layers": {
Expand Down
12 changes: 10 additions & 2 deletions src/flownet/data/from_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -163,12 +163,16 @@ def _production_data(self) -> pd.DataFrame:
- WGPR Well Gas Production Rate
- WWPR Well Water Production Rate
- WOPT Well Cumulative Oil Production
- WGPT Well Cumulative Gas Production Rate
- WWPT Well Cumulative Water Production Rate
- WGPT Well Cumulative Gas Production
- WWPT Well Cumulative Water Production
- WBHP Well Bottom Hole Pressure
- WTHP Well Tubing Head Pressure
- WGIR Well Gas Injection Rate
- WWIR Well Water Injection Rate
- WSPR Well Salt Production Rate
- WSIR Well Salt Injection Rate
- WSPT Well Cumulative Salt Production
- WSIT Well Cumulative Salt Injection
- WSTAT Well status (OPEN, SHUT, STOP)
- TYPE Well Type: "OP", "GP", "WI", "GI"
- PHASE Main producing/injecting phase fluid: "OIL", "GAS", "WATER"
Expand All @@ -191,6 +195,10 @@ def _production_data(self) -> pd.DataFrame:
"WWIR",
"WGIT",
"WWIT",
"WSPR",
"WSIR",
"WSPT",
"WSIT",
"WSTAT",
]

Expand Down
40 changes: 39 additions & 1 deletion src/flownet/realization/_schedule.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import pandas as pd

from configsuite import ConfigSuite
from ._simulation_keywords import Keyword, COMPDAT, WCONHIST, WCONINJH, WELSPECS
from ._simulation_keywords import Keyword, COMPDAT, WCONHIST, WCONINJH, WELSPECS, WSALT
from ..network_model import NetworkModel


Expand Down Expand Up @@ -61,8 +61,27 @@ def _create_schedule(self):
self._calculate_welspecs()
self._calculate_wconhist()
self._calculate_wconinjh()
self._calculate_wsalt()
print("done.", flush=True)

def _calculate_wsalt(self):
"""
Helper Function that generates the WSALT keywords based on salt measurements.
Returns:
Nothing
"""
for _, value in self._df_production_data.iterrows():
if value["WWIR"] > 0 and value["WSIR"] > 0:
self.append(
WSALT(
date=value["date"],
well_name=value["WELL_NAME"],
salt_concentration=value["WSIR"] / value["WWIR"],
)
)

def _calculate_compdat(self):
"""
Helper Function that generates the COMPDAT keywords based on geometrical information from the NetworkModel.
Expand Down Expand Up @@ -214,6 +233,8 @@ def _calculate_wconhist(self):
oil_total=value["WOPT"],
water_total=value["WWPT"],
gas_total=value["WGPT"],
salt_rate=value["WSPR"],
salt_total=value["WSPT"],
bhp=value["WBHP"],
thp=value["WTHP"],
)
Expand Down Expand Up @@ -533,6 +554,23 @@ def get_vfp(self) -> Dict:

return vfp_tables

def has_brine(self) -> bool:
"""Helper function to determine whether the schedule has brine data.
Returns:
True if non zero brine data found, otherwise False
"""
return (
sum(
[
kw.salt_concentration
for kw in self._schedule_items
if kw.name == "WSALT"
]
)
> 0
)

def get_nr_observations(self, training_set_fraction: float) -> int:
"""
Helper function to retrieve the number of unique observations in the training process.
Expand Down
26 changes: 26 additions & 0 deletions src/flownet/realization/_simulation_keywords.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,8 @@ def __init__(
oil_rate: float = np.nan,
water_rate: float = np.nan,
gas_rate: float = np.nan,
salt_rate: float = np.nan,
salt_total: float = np.nan,
oil_total: float = np.nan,
water_total: float = np.nan,
gas_total: float = np.nan,
Expand All @@ -123,6 +125,8 @@ def __init__(
self.oil_rate: float = oil_rate
self.water_rate: float = water_rate
self.gas_rate: float = gas_rate
self.salt_rate: float = salt_rate
self.salt_total: float = salt_total
self.oil_total: float = oil_total
self.water_total: float = water_total
self.gas_total: float = gas_total
Expand Down Expand Up @@ -215,3 +219,25 @@ def __init__(
self.pvt_table: str = pvt_table
self.density_calc: str = density_calc
self.fip: str = fip


class WSALT(Keyword):
"""
The WSALT keyword defines the salt concentration of the injected water.
See the OPM Flow manual for further details.
"""

# pylint: disable=too-many-instance-attributes,too-many-arguments

def __init__(
self,
date: datetime.date,
well_name: str,
salt_concentration: float = np.nan,
):
super().__init__(date)
self.name = "WSALT"
self.well_name: str = well_name
self.salt_concentration: float = salt_concentration
12 changes: 12 additions & 0 deletions src/flownet/static/SUMMARY.inc
Original file line number Diff line number Diff line change
Expand Up @@ -151,3 +151,15 @@ WBHPH
/
WTHPH
/

-- Salt rates
WSPR
/
WSIR
/

-- Salt cumulatives
WSPT
/
WSIT
/
10 changes: 10 additions & 0 deletions src/flownet/templates/HISTORY_SCHEDULE.inc.jinja2
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,16 @@ WCONINJH
/
{%- endif %}

{%- if schedule.get_keywords(dates=date, kw_class="WSALT"): %}
WSALT
-- WELL SALT
-- NAME SALTCON
{%- for kw in schedule.get_keywords(dates=date, kw_class="WSALT"): %}
'{{ kw.well_name }}' {{ kw.salt_concentration }} /
{%- endfor %}
/
{%- endif %}

{%- if date > startdate: %}
DATES
{{ date.strftime('%d %b %Y').upper() }} /
Expand Down
4 changes: 4 additions & 0 deletions src/flownet/templates/TEMPLATE_MODEL.DATA.jinja2
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ INCLUDE
INCLUDE
'./include/RUNSPEC.inc' /

{% if schedule.has_brine() -%}
BRINE
{%- endif %}

----
GRID
----
Expand Down
12 changes: 12 additions & 0 deletions src/flownet/templates/observations.ertobs.jinja2
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@ SUMMARY_OBSERVATION WGPR_{{ kw.well_name }}_{{ index_name }} { VALUE = {{ kw.gas
{%- if not isnan(kw.water_rate) and error_config.WWPR.rel_error is not none and error_config.WWPR.min_error is not none: %}
SUMMARY_OBSERVATION WWPR_{{ kw.well_name }}_{{ index_name }} { VALUE = {{ kw.water_rate }}; ERROR = {{ [error_config.WWPR.rel_error * kw.water_rate, error_config.WWPR.min_error] | max }}; DATE = {{ date_formatted }}; KEY = WWPR:{{ kw.well_name }}; };
{%- endif %}
{%- if not isnan(kw.salt_total) and error_config.WSPT.rel_error is not none and error_config.WSPT.min_error is not none: %}
SUMMARY_OBSERVATION WSPT_{{ kw.well_name }}_{{ index_name }} { VALUE = {{ kw.salt_total }}; ERROR = {{ [error_config.WSPT.rel_error * kw.salt_total, error_config.WSPR.min_error] | max }}; DATE = {{ date_formatted }}; KEY = WSPR:{{ kw.well_name }}; };
{%- endif %}
{%- if not isnan(kw.salt_rate) and error_config.WSPR.rel_error is not none and error_config.WSPR.min_error is not none: %}
SUMMARY_OBSERVATION WSPR_{{ kw.well_name }}_{{ index_name }} { VALUE = {{ kw.salt_rate }}; ERROR = {{ [error_config.WSPR.rel_error * kw.salt_rate, error_config.WSPR.min_error] | max }}; DATE = {{ date_formatted }}; KEY = WSPR:{{ kw.well_name }}; };
{%- endif %}
{%- if not isnan(kw.bhp) and error_config.WBHP.rel_error is not none and error_config.WBHP.min_error is not none : %}
SUMMARY_OBSERVATION WBHP_{{ kw.well_name }}_{{ index_name }} { VALUE = {{ kw.bhp }}; ERROR = {{ [error_config.WBHP.rel_error * kw.bhp, error_config.WBHP.min_error] | max }}; DATE = {{ date_formatted }}; KEY = WBHP:{{ kw.well_name }}; };
{%- endif %}
Expand Down Expand Up @@ -58,6 +64,12 @@ SUMMARY_OBSERVATION WGIT_{{ kw.well_name }}_{{ index_name }} { VALUE = {{ kw.tot
{%- if not isnan(kw.total) and kw.inj_type == "WATER" and error_config.WWIT.rel_error is not none and error_config.WWIT.min_error is not none: %}
SUMMARY_OBSERVATION WWIT_{{ kw.well_name }}_{{ index_name }} { VALUE = {{ kw.total }}; ERROR = {{ [error_config.WWIT.rel_error * kw.total, error_config.WWIT.min_error] | max }}; DATE = {{ date_formatted }}; KEY = WWIT:{{ kw.well_name }}; };
{%- endif %}
{%- if not isnan(kw.total) and kw.inj_type == "WATER" and error_config.WSIR.rel_error is not none and error_config.WSIR.min_error is not none: %}
SUMMARY_OBSERVATION WSIR_{{ kw.well_name }}_{{ index_name }} { VALUE = {{ kw.rate }}; ERROR = {{ [error_config.WSIR.rel_error * kw.rate, error_config.WSIR.min_error] | max }}; DATE = {{ date_formatted }}; KEY = WSIR:{{ kw.well_name }}; };
{%- endif %}
{%- if not isnan(kw.total) and kw.inj_type == "WATER" and error_config.WSIT.rel_error is not none and error_config.WSIT.min_error is not none: %}
SUMMARY_OBSERVATION WSIT_{{ kw.well_name }}_{{ index_name }} { VALUE = {{ kw.total }}; ERROR = {{ [error_config.WSIT.rel_error * kw.total, error_config.WSIT.min_error] | max }}; DATE = {{ date_formatted }}; KEY = WSIT:{{ kw.well_name }}; };
{%- endif %}

{% endfor %}
{%- endif %}
Expand Down
68 changes: 68 additions & 0 deletions src/flownet/templates/observations.yamlobs.jinja2
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,23 @@ smry:
{%- endif -%}
{%- endfor %}
{%- endif -%}
{%- if (error_config.WSPR.rel_error is not none) and (error_config.WSPR.min_error is not none) -%}
{%- for kw in schedule.get_keywords(kw_class="WCONHIST", well_name=well_name, ignore_nan="salt_rate", dates=dates[num_beginning_date:num_end_date]) -%}
{% if loop.first and not loop.last: %}
- key: WSPR:{{ well_name }}
observations:
{%- endif %}
- date: {{ kw.date.strftime('%Y-%m-%d') }}
value: {{ kw.salt_rate }}
error: {{ [error_config.WSPR.rel_error * kw.salt_rate, error_config.WSPR.min_error] | max }}
comment:
{%- if (kw.date > last_training_date) -%}
{{ " Test" }}
{%- else -%}
{{ " Training" }}
{%- endif -%}
{%- endfor %}
{%- endif -%}
{%- if (error_config.WOPT.rel_error is not none) and (error_config.WOPT.min_error is not none) -%}
{%- for kw in schedule.get_keywords(kw_class="WCONHIST", well_name=well_name, ignore_nan="oil_total", dates=dates[num_beginning_date:num_end_date]) -%}
{% if loop.first and not loop.last: %}
Expand Down Expand Up @@ -102,6 +119,23 @@ smry:
{%- endif -%}
{%- endfor %}
{%- endif -%}
{%- if (error_config.WSPT.rel_error is not none) and (error_config.WSPT.min_error is not none) -%}
{%- for kw in schedule.get_keywords(kw_class="WCONHIST", well_name=well_name, ignore_nan="salt_total", dates=dates[num_beginning_date:num_end_date]) -%}
{% if loop.first and not loop.last: %}
- key: WSPT:{{ well_name }}
observations:
{%- endif %}
- date: {{ kw.date.strftime('%Y-%m-%d') }}
value: {{ kw.salt_total }}
error: {{ [error_config.WSPT.rel_error * kw.salt_total, error_config.WSPT.min_error] | max }}
comment:
{%- if (kw.date > last_training_date) -%}
{{ " Test" }}
{%- else -%}
{{ " Training" }}
{%- endif -%}
{%- endfor %}
{%- endif -%}
{%- if (error_config.WBHP.rel_error is not none) and (error_config.WBHP.min_error is not none) -%}
{%- for kw in schedule.get_keywords(kw_class="WCONHIST", well_name=well_name, ignore_nan="bhp", dates=dates[num_beginning_date:num_end_date]) -%}
{% if loop.first and not loop.last: %}
Expand Down Expand Up @@ -241,4 +275,38 @@ smry:
{%- endif -%}
{%- endfor %}
{%- endif -%}
{%- if (error_config.WSIR.rel_error is not none) and (error_config.WSIR.min_error is not none) -%}
{%- for kw in schedule.get_keywords(kw_class="WCONINJH", well_name=well_name, ignore_nan="rate", dates=dates[num_beginning_date:num_end_date]) if kw.inj_type == "WATER" -%}
{% if loop.first and not loop.last: %}
- key: WSIR:{{ well_name }}
observations:
{%- endif %}
- date: {{ kw.date.strftime('%Y-%m-%d') }}
value: {{ kw.rate }}
error: {{ [error_config.WSIR.rel_error * kw.rate, error_config.WSIR.min_error] | max }}
comment:
{%- if (kw.date > last_training_date) -%}
{{ " Test" }}
{%- else -%}
{{ " Training" }}
{%- endif -%}
{%- endfor %}
{%- endif -%}
{%- if (error_config.WSIT.rel_error is not none) and (error_config.WSIT.min_error is not none) -%}
{%- for kw in schedule.get_keywords(kw_class="WCONHIST", well_name=well_name, ignore_nan="salt_total", dates=dates[num_beginning_date:num_end_date]) -%}
{% if loop.first and not loop.last: %}
- key: WSIT:{{ well_name }}
observations:
{%- endif %}
- date: {{ kw.date.strftime('%Y-%m-%d') }}
value: {{ kw.salt_total }}
error: {{ [error_config.WSIT.rel_error * kw.salt_total, error_config.WSIT.min_error] | max }}
comment:
{%- if (kw.date > last_training_date) -%}
{{ " Test" }}
{%- else -%}
{{ " Training" }}
{%- endif -%}
{%- endfor %}
{%- endif -%}
{%- endfor -%}
24 changes: 24 additions & 0 deletions tests/test_check_obsfiles_ert_yaml.py
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,30 @@ def test_check_obsfiles_ert_yaml() -> None:
config.flownet.data_source.simulation.vectors.WWIT.min_error = _MIN_ERROR
config.flownet.data_source.simulation.vectors.WWIT.rel_error = _REL_ERROR

config.flownet.data_source.simulation.vectors.WSPR = collections.namedtuple(
"WSPR", "min_error"
)
config.flownet.data_source.simulation.vectors.WSPR.min_error = _MIN_ERROR
config.flownet.data_source.simulation.vectors.WSPR.rel_error = _REL_ERROR

config.flownet.data_source.simulation.vectors.WSPT = collections.namedtuple(
"WSPT", "min_error"
)
config.flownet.data_source.simulation.vectors.WSPT.min_error = _MIN_ERROR
config.flownet.data_source.simulation.vectors.WSPT.rel_error = _REL_ERROR

config.flownet.data_source.simulation.vectors.WSIR = collections.namedtuple(
"WSIR", "min_error"
)
config.flownet.data_source.simulation.vectors.WSIR.min_error = _MIN_ERROR
config.flownet.data_source.simulation.vectors.WSIR.rel_error = _REL_ERROR

config.flownet.data_source.simulation.vectors.WSIT = collections.namedtuple(
"WSIT", "min_error"
)
config.flownet.data_source.simulation.vectors.WSIT.min_error = _MIN_ERROR
config.flownet.data_source.simulation.vectors.WSIT.rel_error = _REL_ERROR

config.flownet.data_source.resampling = _RESAMPLING

# Load production
Expand Down

0 comments on commit 0ea8158

Please sign in to comment.