Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add brine p #417

Merged
merged 10 commits into from
Jun 29, 2021
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": {
olelod marked this conversation as resolved.
Show resolved Hide resolved
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
olelod marked this conversation as resolved.
Show resolved Hide resolved
- 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",
olelod marked this conversation as resolved.
Show resolved Hide resolved
"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"],
olelod marked this conversation as resolved.
Show resolved Hide resolved
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,
olelod marked this conversation as resolved.
Show resolved Hide resolved
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
olelod marked this conversation as resolved.
Show resolved Hide resolved
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
olelod marked this conversation as resolved.
Show resolved Hide resolved
/
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
15 changes: 15 additions & 0 deletions src/flownet/templates/observations.ertobs.jinja2
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,15 @@ 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: %}
olelod marked this conversation as resolved.
Show resolved Hide resolved
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.salt_rate) and error_config.WSIR.rel_error is not none and error_config.WSIR.min_error is not none: %}
olelod marked this conversation as resolved.
Show resolved Hide resolved
SUMMARY_OBSERVATION WSIR_{{ kw.well_name }}_{{ index_name }} { VALUE = {{ kw.salt_rate }}; ERROR = {{ [error_config.WSIR.rel_error * kw.salt_rate, error_config.WSIR.min_error] | max }}; DATE = {{ date_formatted }}; KEY = WSIR:{{ 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 +67,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) -%}
olelod marked this conversation as resolved.
Show resolved Hide resolved
{%- 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 -%}
Loading