From 7f260dd8e5c56c4f6e7c57475416cd685a38bfca Mon Sep 17 00:00:00 2001 From: olelod Date: Fri, 7 May 2021 10:41:52 +0000 Subject: [PATCH 1/9] initial commit regional multipliers poro porv trans --- src/flownet/ahm/_run_ahm.py | 94 +++++++- src/flownet/config_parser/_config_parser.py | 232 ++++++++++++++++++-- src/flownet/parameters/_porv_poro_trans.py | 8 + 3 files changed, 316 insertions(+), 18 deletions(-) diff --git a/src/flownet/ahm/_run_ahm.py b/src/flownet/ahm/_run_ahm.py index 4c934eaaa..6e91563c0 100644 --- a/src/flownet/ahm/_run_ahm.py +++ b/src/flownet/ahm/_run_ahm.py @@ -1,5 +1,5 @@ import argparse -from typing import Union, List, Optional, Dict +from typing import Union, List, Optional, Dict, Tuple import json import pathlib from operator import add @@ -198,6 +198,43 @@ def _get_distribution( return df +def _get_regional_distribution( + parameters: Union[str, List[str]], + parameters_config: dict, + network: NetworkModel, + field_data: FlowData, + ti2ci: pd.DataFrame, +) -> Tuple[pd.DataFrame, pd.DataFrame]: + """ + Create the distribution min, mean, base, stddev, max for one or more parameters + + Args: + parameters: which parameter(s) should be outputted in the dataframe + parameters_config: the parameters definition from the configuration file + network: + field_data: + ti2ci: + + Returns: + A dataframe with distributions for the requested parameter(s) + + """ + if not isinstance(parameters, list): + parameters = [parameters] + + df = pd.DataFrame(index=index) + + for parameter in parameters: + parameter_config = getattr(parameters_config, parameter) + df[f"minimum_{parameter}"] = parameter_config.min + df[f"maximum_{parameter}"] = parameter_config.max + df[f"mean_{parameter}"] = parameter_config.mean + df[f"base_{parameter}"] = parameter_config.base + df[f"stddev_{parameter}"] = parameter_config.stddev + df[f"distribution_{parameter}"] = parameter_config.distribution + return df + + def _constrain_using_well_logs( porv_poro_trans_dist_values: pd.DataFrame, data: np.ndarray, @@ -771,6 +808,59 @@ def run_flownet_history_matching( equil_dist_values = equil_dist_values[equil_dist_values.isnull().sum(axis=1) < 5] + ######################################### + # Region volume multipliers # + ######################################### + + regional_porv_poro_trans_dist_values, df_regional = _get_regional_distribution( + ["bulkvolume_mult", "porosity", "permeability"], + config.model_parameters, + network, + field_data, + ti2ci, + ) + + # Create a Pandas dataframe with all region volume multipliers + # based on the chosen scheme + if config.model_parameters.bulkvolume_mult_scheme == "individual": + df_bulkvolume = None + regional_bulkvolume_dist_values = None + ri2ci = None + else: + if config.model_parameters.bulkvolume_mult_scheme == "regions_from_sim": + df_bulkvolume = pd.DataFrame( + _from_regions_to_flow_tubes( + network, + field_data, + ti2ci, + config.model_parameters.bulkvolume_parameter_from_sim_model, + ), + columns=["BV_MULT"], + ) + elif config.model_parameters.bulkvolume_mult_scheme == "global": + df_bulkvolume = pd.DataFrame( + [1] * len(network.grid.model.unique()), columns=["BV_MULT"] + ) + + ri2ci_index = ti2ci.index.values.copy() + for idx in np.unique(ti2ci.index.values): + ri2ci_index[ti2ci.index.values == idx] = df_bulkvolume.loc[idx] + ri2ci = pd.DataFrame(data=network.grid.index, index=ri2ci_index) + + # Create a pandas dataframe with all parameter definition for each individual tube + regional_bulkvolume_dist_values = pd.DataFrame( + columns=column_names_probdist + ["bv_mult"] + ) + + for i in np.sort(df_bulkvolume["BV_MULT"].unique()): + info = ["region_bulkvolume_mult"] + for keyword in ["min", "max", "mean", "base", "stddev", "distribution"]: + info.append( + getattr(config.model_parameters.region_bulkvolume_mult, keyword) + ) + info.append(i) + regional_bulkvolume_dist_values.loc[i] = info + ######################################### # Fault transmissibility # ######################################### @@ -832,7 +922,9 @@ def run_flownet_history_matching( parameters = [ PorvPoroTrans( porv_poro_trans_dist_values, + regional_bulkvolume_dist_values, ti2ci, + ri2ci, network, config.flownet.min_permeability, ), diff --git a/src/flownet/config_parser/_config_parser.py b/src/flownet/config_parser/_config_parser.py index 1aef01f2e..8731e6d6d 100644 --- a/src/flownet/config_parser/_config_parser.py +++ b/src/flownet/config_parser/_config_parser.py @@ -685,6 +685,61 @@ def _to_abs_path(path: Optional[str]) -> str: }, }, }, + "permeability_regional_scheme": { + MK.Type: types.String, + MK.Description: "The individual flow tubes will always have an individual permeability " + "prior distribution attached (specified as permeability in the config file). Setting this " + "option to 'individual' makes the individual uncertainty for each flow tube the only " + "permeability uncertainty. Using the 'global' option will add one additional multiplier " + "for the entire FlowNet model, and 'regions_from_sim' will add one multiplier for each " + "of the regions present in a region parameter from an input simulation model " + "(parameter name specified in 'permeability_parameter_from_sim_model').", + MK.Default: "individual", + MK.Transformation: _to_lower, + }, + "permeability_parameter_from_sim_model": { + MK.Type: types.String, + MK.Description: "The name of the region parameter in the simulation model to " + "base a regional permeability multiplier on.", + MK.Default: "EQLNUM", + }, + "permeability_regional_mult": { + MK.Type: types.NamedDict, + MK.Description: "Description of the regional permeability multiplier prior " + "distribution.", + MK.Content: { + "min": { + MK.Type: types.Number, + MK.AllowNone: True, + MK.Transformation: _str_none_to_none, + }, + "mean": { + MK.Type: types.Number, + MK.AllowNone: True, + MK.Transformation: _str_none_to_none, + }, + "max": { + MK.Type: types.Number, + MK.AllowNone: True, + MK.Transformation: _str_none_to_none, + }, + "base": { + MK.Type: types.Number, + MK.AllowNone: True, + MK.Transformation: _str_none_to_none, + }, + "stddev": { + MK.Type: types.Number, + MK.AllowNone: True, + MK.Transformation: _str_none_to_none, + }, + "distribution": { + MK.Type: types.String, + MK.Default: "uniform", + MK.Transformation: _to_lower, + }, + }, + }, "porosity": { MK.Type: types.NamedDict, MK.Description: "Description of the porosity prior " @@ -723,6 +778,73 @@ def _to_abs_path(path: Optional[str]) -> str: }, }, }, + "porosity_regional_scheme": { + MK.Type: types.String, + MK.Description: "The individual flow tubes will always have an individual porosity " + "prior distribution attached (specified as porosity in the config file). Setting this " + "option to 'individual' makes the individual uncertainty for each flow tube the only " + "porosity uncertainty. Using the 'global' option will add one additional multiplier " + "for the entire FlowNet model, and 'regions_from_sim' will add one multiplier for each " + "of the regions present in a region parameter from an input simulation model " + "(parameter name specified in 'porosity_parameter_from_sim_model').", + MK.Default: "individual", + MK.Transformation: _to_lower, + }, + "porosity_parameter_from_sim_model": { + MK.Type: types.String, + MK.Description: "The name of the region parameter in the simulation model to " + "base a regional permeability multiplier on.", + MK.Default: "EQLNUM", + }, + "porosity_regional_mult": { + MK.Type: types.NamedDict, + MK.Description: "Description of the regional porosity multiplier prior " + "distribution.", + MK.Content: { + "min": { + MK.Type: types.Number, + MK.AllowNone: True, + MK.Transformation: _str_none_to_none, + }, + "mean": { + MK.Type: types.Number, + MK.AllowNone: True, + MK.Transformation: _str_none_to_none, + }, + "max": { + MK.Type: types.Number, + MK.AllowNone: True, + MK.Transformation: _str_none_to_none, + }, + "base": { + MK.Type: types.Number, + MK.AllowNone: True, + MK.Transformation: _str_none_to_none, + }, + "stddev": { + MK.Type: types.Number, + MK.AllowNone: True, + MK.Transformation: _str_none_to_none, + }, + "distribution": { + MK.Type: types.String, + MK.Default: "uniform", + MK.Transformation: _to_lower, + }, + }, + }, + "bulkvolume_regional_scheme": { + MK.Type: types.String, + MK.Description: "The individual flow tubes will always have an individual bulk volume " + "multiplier attached (specified as bulkvolume_mult in the config file). Setting this " + "option to 'individual' makes the individual multipliers for each flow tube the only " + "bulk volume uncertainty. Using the 'global' option will add one additional multiplier " + "for the entire FlowNet model, and 'regions_from_sim' will add one multiplier for each " + "of the regions present in a region parameter from an input simulation model " + "(parameter name specified in 'bulkvolume_parameter_from_sim_model').", + MK.Default: "individual", + MK.Transformation: _to_lower, + }, "bulkvolume_mult": { MK.Type: types.NamedDict, MK.Description: "Description of bulk volume multiplier " @@ -764,6 +886,53 @@ def _to_abs_path(path: Optional[str]) -> str: }, }, }, + "bulkvolume_parameter_from_sim_model": { + MK.Type: types.String, + MK.Description: "The name of the region parameter in the simulation model to " + "base a regional bulk volume parameter on.", + MK.Default: "EQLNUM", + }, + "bulkvolume_regional_mult": { + MK.Type: types.NamedDict, + MK.Description: "Description of bulk volume multiplier " + "prior distribution. You define either min and max, or one " + "of the endpoints and mean. One multiplies is drawn per tube " + "which decreases/increases the default tube bulk volume " + "(which again is a proportional part of model convex hull bulk " + "with respect to tube length compared to other tubes).", + MK.Content: { + "min": { + MK.Type: types.Number, + MK.AllowNone: True, + MK.Transformation: _str_none_to_none, + }, + "mean": { + MK.Type: types.Number, + MK.AllowNone: True, + MK.Transformation: _str_none_to_none, + }, + "max": { + MK.Type: types.Number, + MK.AllowNone: True, + MK.Transformation: _str_none_to_none, + }, + "base": { + MK.Type: types.Number, + MK.AllowNone: True, + MK.Transformation: _str_none_to_none, + }, + "stddev": { + MK.Type: types.Number, + MK.AllowNone: True, + MK.Transformation: _str_none_to_none, + }, + "distribution": { + MK.Type: types.String, + MK.Default: "uniform", + MK.Transformation: _to_lower, + }, + }, + }, "fault_mult": { MK.Type: types.NamedDict, MK.Description: "Description of the fault multiplicator " @@ -1742,22 +1911,37 @@ def parse_config( "interpolation option for relative permeability." ) - # If 'regions_from_sim' is defined, or a csv file with rsvd tables - # is defined, we need to import the simulation case to check number - # regions + # If 'regions_from_sim' is defined for any parameter, or a csv file with + # rsvd tables is defined, we need to import the simulation case to check + # number of regions if ( - config.model_parameters.equil.scheme == "regions_from_sim" - or config.model_parameters.relative_permeability.scheme == "regions_from_sim" + "regions_from_sim" + in ( + config.model_parameters.equil.scheme, + config.model_parameters.relative_permeability.scheme, + config.model_parameters.porosity_regional_scheme, + config.model_parameters.permeability_regional_scheme, + config.model_parameters.bulkvolume_regional_scheme, + ) or config.flownet.pvt.rsvd ): if config.flownet.data_source.simulation.input_case is None: raise ValueError("Input simulation case is not defined.") field_data = FlowData(config.flownet.data_source.simulation.input_case) - region_parameters: dict = {"equil": "EQLNUM", "relative_permeability": "SATNUM"} + region_parameters: dict = { + "equil": "EQLNUM", + "relative_permeability": "SATNUM", + "permeability": None, + "porosity": None, + "bulkvolume": None, + } unique_regions: dict = {} for reg_param, flow_region_name in region_parameters.items(): - scheme = getattr(getattr(config.model_parameters, reg_param), "scheme") + if flow_region_name is not None: + scheme = getattr(getattr(config.model_parameters, reg_param), "scheme") + else: + scheme = getattr(config.model_parameters, reg_param + "_regional_scheme") if scheme not in available_region_schemes: raise ValueError( f"The {reg_param} scheme " @@ -1765,10 +1949,15 @@ def parse_config( f"Valid options are {available_region_schemes}" ) if scheme == "regions_from_sim": - reg_param_sim_model = getattr( - getattr(config.model_parameters, reg_param), - "region_parameter_from_sim_model", - ) + if flow_region_name is not None: + reg_param_sim_model = getattr( + getattr(config.model_parameters, reg_param), + "region_parameter_from_sim_model", + ) + else: + reg_param_sim_model = getattr( + config.model_parameters, reg_param + "_parameter_from_sim_model" + ) try: unique_regions[reg_param] = field_data.get_unique_regions( reg_param_sim_model @@ -1778,11 +1967,12 @@ def parse_config( f"REGION parameter {reg_param_sim_model} " "not found in input simulation model." ) from err - _check_if_all_region_priors_defined( - getattr(config.model_parameters, reg_param), - unique_regions[reg_param], - flow_region_name, - ) + if flow_region_name: + _check_if_all_region_priors_defined( + getattr(config.model_parameters, reg_param), + unique_regions[reg_param], + flow_region_name, + ) else: regions = getattr(getattr(config.model_parameters, reg_param), "regions") if regions[0].id is not None: @@ -1929,7 +2119,15 @@ def parse_config( "Queue name and server needs to be provided if system is not 'LOCAL'." ) - for parameter in ["bulkvolume_mult", "porosity", "permeability", "fault_mult"]: + for parameter in [ + "bulkvolume_mult", + "porosity", + "permeability", + "fault_mult", + "permeability_regional_mult", + "porosity_regional_mult", + "bulkvolume_regional_mult", + ]: if not len(_check_defined(config.model_parameters, parameter)) > 0: continue _check_distribution(config.model_parameters, parameter) diff --git a/src/flownet/parameters/_porv_poro_trans.py b/src/flownet/parameters/_porv_poro_trans.py index acbf12940..c58f5cc40 100644 --- a/src/flownet/parameters/_porv_poro_trans.py +++ b/src/flownet/parameters/_porv_poro_trans.py @@ -100,11 +100,14 @@ class PorvPoroTrans(Parameter): def __init__( self, distribution_values: pd.DataFrame, + regional_distribution_values: pd.DataFrame, ti2ci: pd.DataFrame, + ri2ci: pd.DataFrame, network: NetworkModel, min_permeability: Optional[float] = None, ): self._ti2ci: pd.DataFrame = ti2ci + self._ri2ci: pd.DataFrame = ri2ci self._random_variables: List[ProbabilityDistribution] = ( [ # Add random variables for bulk volume multipliers @@ -119,10 +122,15 @@ def __init__( parameter_probability_distribution_class(row, "permeability") for _, row in distribution_values.iterrows() ] + + [ + parameter_probability_distribution_class(row) + for _, row in regional_distribution_values.iterrows() + ] ) self._network: NetworkModel = network self._number_tubes: int = len(self._ti2ci.index.unique()) + self._number_regions: int = len(self._ri2ci.index.unique()) self.min_permeability: Optional[float] = min_permeability def render_output(self) -> Dict: From 50ee0a219f42be3811b1cba5a15a27d3665bff8b Mon Sep 17 00:00:00 2001 From: olelod Date: Tue, 11 May 2021 10:22:04 +0000 Subject: [PATCH 2/9] initial commit --- src/flownet/ahm/_run_ahm.py | 107 ++++++++++---------- src/flownet/config_parser/_config_parser.py | 35 ++++--- src/flownet/parameters/_equilibration.py | 19 ++++ src/flownet/parameters/_porv_poro_trans.py | 67 +++++++++--- 4 files changed, 142 insertions(+), 86 deletions(-) diff --git a/src/flownet/ahm/_run_ahm.py b/src/flownet/ahm/_run_ahm.py index 6e91563c0..7148f372f 100644 --- a/src/flownet/ahm/_run_ahm.py +++ b/src/flownet/ahm/_run_ahm.py @@ -222,17 +222,47 @@ def _get_regional_distribution( if not isinstance(parameters, list): parameters = [parameters] - df = pd.DataFrame(index=index) + df = pd.DataFrame(index=ti2ci.index.unique()) + df_dist_values = pd.DataFrame(index=[0]) + ci2ri = pd.DataFrame(index=network.grid.index) for parameter in parameters: - parameter_config = getattr(parameters_config, parameter) - df[f"minimum_{parameter}"] = parameter_config.min - df[f"maximum_{parameter}"] = parameter_config.max - df[f"mean_{parameter}"] = parameter_config.mean - df[f"base_{parameter}"] = parameter_config.base - df[f"stddev_{parameter}"] = parameter_config.stddev - df[f"distribution_{parameter}"] = parameter_config.distribution - return df + if ( + getattr(parameters_config, parameter + "_regional_scheme") + == "regions_from_sim" + ): + df[parameter + "_regional"] = pd.DataFrame( + _from_regions_to_flow_tubes( + network, + field_data, + ti2ci, + getattr(parameters_config, parameter + "_parameter_from_sim_model"), + ) + ) + elif getattr(parameters_config, parameter + "_regional_scheme") == "global": + df[parameter + "_regional"] = pd.DataFrame( + [1] * len(network.grid.model.unique()) + ) + + ci2ri_index = ti2ci.index.values.copy() + for idx in np.unique(ti2ci.index.values): + ci2ri_index[ti2ci.index.values == idx] = df.loc[ + idx, parameter + "_regional" + ] + ci2ri[parameter + "_regional"] = ci2ri_index + + parameter_config_regional = getattr(parameters_config, parameter + "_regional") + df_dist_values[f"minimum_{parameter}_regional"] = parameter_config_regional.min + df_dist_values[f"maximum_{parameter}_regional"] = parameter_config_regional.max + df_dist_values[f"mean_{parameter}_regional"] = parameter_config_regional.mean + df_dist_values[f"base_{parameter}_regional"] = parameter_config_regional.base + df_dist_values[ + f"stddev_{parameter}_regional" + ] = parameter_config_regional.stddev + df_dist_values[ + f"distribution_{parameter}_regional" + ] = parameter_config_regional.distribution + return ci2ri, df, df_dist_values def _constrain_using_well_logs( @@ -812,55 +842,23 @@ def run_flownet_history_matching( # Region volume multipliers # ######################################### - regional_porv_poro_trans_dist_values, df_regional = _get_regional_distribution( - ["bulkvolume_mult", "porosity", "permeability"], + regional_parameters = [ + param + for param in {"bulkvolume_mult", "porosity", "permeability"} + if getattr(config.model_parameters, param + "_regional_scheme") != "individual" + ] + ( + ci2ri, + df_regional, + regional_porv_poro_trans_dist_values, + ) = _get_regional_distribution( + regional_parameters, config.model_parameters, network, field_data, ti2ci, ) - # Create a Pandas dataframe with all region volume multipliers - # based on the chosen scheme - if config.model_parameters.bulkvolume_mult_scheme == "individual": - df_bulkvolume = None - regional_bulkvolume_dist_values = None - ri2ci = None - else: - if config.model_parameters.bulkvolume_mult_scheme == "regions_from_sim": - df_bulkvolume = pd.DataFrame( - _from_regions_to_flow_tubes( - network, - field_data, - ti2ci, - config.model_parameters.bulkvolume_parameter_from_sim_model, - ), - columns=["BV_MULT"], - ) - elif config.model_parameters.bulkvolume_mult_scheme == "global": - df_bulkvolume = pd.DataFrame( - [1] * len(network.grid.model.unique()), columns=["BV_MULT"] - ) - - ri2ci_index = ti2ci.index.values.copy() - for idx in np.unique(ti2ci.index.values): - ri2ci_index[ti2ci.index.values == idx] = df_bulkvolume.loc[idx] - ri2ci = pd.DataFrame(data=network.grid.index, index=ri2ci_index) - - # Create a pandas dataframe with all parameter definition for each individual tube - regional_bulkvolume_dist_values = pd.DataFrame( - columns=column_names_probdist + ["bv_mult"] - ) - - for i in np.sort(df_bulkvolume["BV_MULT"].unique()): - info = ["region_bulkvolume_mult"] - for keyword in ["min", "max", "mean", "base", "stddev", "distribution"]: - info.append( - getattr(config.model_parameters.region_bulkvolume_mult, keyword) - ) - info.append(i) - regional_bulkvolume_dist_values.loc[i] = info - ######################################### # Fault transmissibility # ######################################### @@ -922,9 +920,10 @@ def run_flownet_history_matching( parameters = [ PorvPoroTrans( porv_poro_trans_dist_values, - regional_bulkvolume_dist_values, + regional_porv_poro_trans_dist_values, + df_regional, ti2ci, - ri2ci, + ci2ri, network, config.flownet.min_permeability, ), diff --git a/src/flownet/config_parser/_config_parser.py b/src/flownet/config_parser/_config_parser.py index 8731e6d6d..657879272 100644 --- a/src/flownet/config_parser/_config_parser.py +++ b/src/flownet/config_parser/_config_parser.py @@ -703,7 +703,7 @@ def _to_abs_path(path: Optional[str]) -> str: "base a regional permeability multiplier on.", MK.Default: "EQLNUM", }, - "permeability_regional_mult": { + "permeability_regional": { MK.Type: types.NamedDict, MK.Description: "Description of the regional permeability multiplier prior " "distribution.", @@ -796,7 +796,7 @@ def _to_abs_path(path: Optional[str]) -> str: "base a regional permeability multiplier on.", MK.Default: "EQLNUM", }, - "porosity_regional_mult": { + "porosity_regional": { MK.Type: types.NamedDict, MK.Description: "Description of the regional porosity multiplier prior " "distribution.", @@ -833,7 +833,7 @@ def _to_abs_path(path: Optional[str]) -> str: }, }, }, - "bulkvolume_regional_scheme": { + "bulkvolume_mult_regional_scheme": { MK.Type: types.String, MK.Description: "The individual flow tubes will always have an individual bulk volume " "multiplier attached (specified as bulkvolume_mult in the config file). Setting this " @@ -841,7 +841,7 @@ def _to_abs_path(path: Optional[str]) -> str: "bulk volume uncertainty. Using the 'global' option will add one additional multiplier " "for the entire FlowNet model, and 'regions_from_sim' will add one multiplier for each " "of the regions present in a region parameter from an input simulation model " - "(parameter name specified in 'bulkvolume_parameter_from_sim_model').", + "(parameter name specified in 'bulkvolume_mult_parameter_from_sim_model').", MK.Default: "individual", MK.Transformation: _to_lower, }, @@ -886,13 +886,13 @@ def _to_abs_path(path: Optional[str]) -> str: }, }, }, - "bulkvolume_parameter_from_sim_model": { + "bulkvolume_mult_parameter_from_sim_model": { MK.Type: types.String, MK.Description: "The name of the region parameter in the simulation model to " "base a regional bulk volume parameter on.", MK.Default: "EQLNUM", }, - "bulkvolume_regional_mult": { + "bulkvolume_mult_regional": { MK.Type: types.NamedDict, MK.Description: "Description of bulk volume multiplier " "prior distribution. You define either min and max, or one " @@ -1921,7 +1921,7 @@ def parse_config( config.model_parameters.relative_permeability.scheme, config.model_parameters.porosity_regional_scheme, config.model_parameters.permeability_regional_scheme, - config.model_parameters.bulkvolume_regional_scheme, + config.model_parameters.bulkvolume_mult_regional_scheme, ) or config.flownet.pvt.rsvd ): @@ -1934,7 +1934,7 @@ def parse_config( "relative_permeability": "SATNUM", "permeability": None, "porosity": None, - "bulkvolume": None, + "bulkvolume_mult": None, } unique_regions: dict = {} for reg_param, flow_region_name in region_parameters.items(): @@ -1974,12 +1974,15 @@ def parse_config( flow_region_name, ) else: - regions = getattr(getattr(config.model_parameters, reg_param), "regions") - if regions[0].id is not None: - raise ValueError( - f"The region number for the first {reg_param} region parameter should not be set, \n" - "or set to 'None' when using the 'global' or 'individual' options" + if flow_region_name is not None: + regions = getattr( + getattr(config.model_parameters, reg_param), "regions" ) + if regions[0].id is not None: + raise ValueError( + f"The region number for the first {reg_param} region parameter should not be set, \n" + "or set to 'None' when using the 'global' or 'individual' options" + ) layers = config.flownet.data_source.simulation.layers if len(layers) > 0: @@ -2124,9 +2127,9 @@ def parse_config( "porosity", "permeability", "fault_mult", - "permeability_regional_mult", - "porosity_regional_mult", - "bulkvolume_regional_mult", + "permeability_regional", + "porosity_regional", + "bulkvolume_mult_regional", ]: if not len(_check_defined(config.model_parameters, parameter)) > 0: continue diff --git a/src/flownet/parameters/_equilibration.py b/src/flownet/parameters/_equilibration.py index 95e4684ac..75302944d 100644 --- a/src/flownet/parameters/_equilibration.py +++ b/src/flownet/parameters/_equilibration.py @@ -114,6 +114,25 @@ def render_output(self) -> Dict: eqlnum_combinations.extend(list(combinations(connections_at_node, 2))) eqlnum_combinations = list(set(eqlnum_combinations)) + + # Go from tube index to region index + eqlnum_combinations = set( + [ + ( + self._eqlnum.loc[tube_a, "EQLNUM"], + self._eqlnum.loc[tube_b, "EQLNUM"], + ) + for _, [tube_a, tube_b] in enumerate(eqlnum_combinations) + ] + ) + + # Remove duplicates and within region occurences + eqlnum_combinations = [ + (region_a, region_b) + for _, [region_a, region_b] in enumerate(eqlnum_combinations) + if region_b > region_a + ] + eqlnum1 = list(list(zip(*eqlnum_combinations))[0]) eqlnum2 = list(list(zip(*eqlnum_combinations))[1]) diff --git a/src/flownet/parameters/_porv_poro_trans.py b/src/flownet/parameters/_porv_poro_trans.py index c58f5cc40..40aebe35b 100644 --- a/src/flownet/parameters/_porv_poro_trans.py +++ b/src/flownet/parameters/_porv_poro_trans.py @@ -101,13 +101,21 @@ def __init__( self, distribution_values: pd.DataFrame, regional_distribution_values: pd.DataFrame, + df_regional: pd.DataFrame, ti2ci: pd.DataFrame, - ri2ci: pd.DataFrame, + ci2ri: pd.DataFrame, network: NetworkModel, min_permeability: Optional[float] = None, ): self._ti2ci: pd.DataFrame = ti2ci - self._ri2ci: pd.DataFrame = ri2ci + self._ci2ri: pd.DataFrame = ci2ri + + self._network: NetworkModel = network + self._number_tubes: int = len(self._ti2ci.index.unique()) + self._regions: dict[int] = { + key: df_regional[key].max() for key in df_regional.columns + } + self.min_permeability: Optional[float] = min_permeability self._random_variables: List[ProbabilityDistribution] = ( [ # Add random variables for bulk volume multipliers @@ -123,16 +131,16 @@ def __init__( for _, row in distribution_values.iterrows() ] + [ - parameter_probability_distribution_class(row) - for _, row in regional_distribution_values.iterrows() + parameter_probability_distribution_class(row, param) + for param in df_regional.columns + for _, row in regional_distribution_values.loc[ + regional_distribution_values.index.repeat(df_regional[param].max()) + ] + .reset_index(drop=True) + .iterrows() ] ) - self._network: NetworkModel = network - self._number_tubes: int = len(self._ti2ci.index.unique()) - self._number_regions: int = len(self._ri2ci.index.unique()) - self.min_permeability: Optional[float] = min_permeability - def render_output(self) -> Dict: """ Creates PORO, PORV, PERMX, PERMY, PERMZ and NNC include content - which are given to @@ -142,6 +150,11 @@ def render_output(self) -> Dict: PORO, PORV, PERMX, PERMY, PERMZ and NNC include content. """ + regional_param_to_property: dict = { + "bulkvolume_mult_regional": "BULKVOLUME", + "porosity_regional": "PORO", + "permeability_regional": "PERMX", + } # Calculate PORO, PORV AND MULTX @@ -173,6 +186,35 @@ def render_output(self) -> Dict: * properties_per_cell["BULKVOLUME_MULT"] ) + # Calculate updated base permeability (and NNC transmissibility) + + perm_per_tube = pd.DataFrame( + self.random_samples[2 * self._number_tubes : 3 * self._number_tubes], + index=self._ti2ci.index.unique(), + columns=["PERMX"], + ) + + # Regional multipliers (if applicable) + start_index = 3 * self._number_tubes + for param in self._regions.keys(): + end_index = start_index + self._regions[param] + mult_per_region = pd.DataFrame( + self.random_samples[start_index:end_index], + index=range(1, self._ci2ri[param].max() + 1), + columns=["REGIONAL_MULT"], + ) + df_merge = pd.DataFrame(self._ci2ri[param]).merge( + mult_per_region, left_on=param, right_index=True + ).sort_index() + + df_merge.index = properties_per_cell.index + properties_per_cell[regional_param_to_property[param]] = ( + properties_per_cell[regional_param_to_property[param]] + * df_merge["REGIONAL_MULT"] + ) + start_index = end_index + + # All parmeters from ERT have been read/set, calculate what is needed properties_per_cell["MULTX"] = properties_per_cell["BULKVOLUME"].values / ( self._network.area * self._network.grid["cell_length"].values ) @@ -181,13 +223,6 @@ def render_output(self) -> Dict: properties_per_cell["BULKVOLUME"] * properties_per_cell["PORO"] ) - # Calculate updated base permeability (and NNC transmissibility) - - perm_per_tube = pd.DataFrame( - self.random_samples[2 * self._number_tubes :], - index=self._ti2ci.index.unique(), - columns=["PERMX"], - ) perm_per_tube["PERMY"] = perm_per_tube["PERMX"] perm_per_tube["PERMZ"] = perm_per_tube["PERMX"] From e797d123a60e0e06b5f5dfda101b6a2a81329b3f Mon Sep 17 00:00:00 2001 From: olelod Date: Tue, 11 May 2021 12:08:43 +0000 Subject: [PATCH 3/9] black/mypy/pylint --- src/flownet/ahm/_run_ahm.py | 2 +- src/flownet/parameters/_equilibration.py | 2 +- src/flownet/parameters/_porv_poro_trans.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/flownet/ahm/_run_ahm.py b/src/flownet/ahm/_run_ahm.py index 7148f372f..347c8e965 100644 --- a/src/flownet/ahm/_run_ahm.py +++ b/src/flownet/ahm/_run_ahm.py @@ -204,7 +204,7 @@ def _get_regional_distribution( network: NetworkModel, field_data: FlowData, ti2ci: pd.DataFrame, -) -> Tuple[pd.DataFrame, pd.DataFrame]: +) -> Tuple[pd.DataFrame, pd.DataFrame, pd.DataFrame]: """ Create the distribution min, mean, base, stddev, max for one or more parameters diff --git a/src/flownet/parameters/_equilibration.py b/src/flownet/parameters/_equilibration.py index ad5fb6426..9c3fb5650 100644 --- a/src/flownet/parameters/_equilibration.py +++ b/src/flownet/parameters/_equilibration.py @@ -99,7 +99,7 @@ def render_output(self) -> Dict: zip( self._parameters, self.random_samples[ - i * samples_per_eqlnum: (i + 1) * samples_per_eqlnum + i * samples_per_eqlnum : (i + 1) * samples_per_eqlnum ], ) ) diff --git a/src/flownet/parameters/_porv_poro_trans.py b/src/flownet/parameters/_porv_poro_trans.py index 5a5c5e49b..2a3c4f076 100644 --- a/src/flownet/parameters/_porv_poro_trans.py +++ b/src/flownet/parameters/_porv_poro_trans.py @@ -112,7 +112,7 @@ def __init__( self._network: NetworkModel = network self._number_tubes: int = len(self._ti2ci.index.unique()) - self._regions: dict[int] = { + self._regions: Dict[str, int] = { key: df_regional[key].max() for key in df_regional.columns } self.min_permeability: Optional[float] = min_permeability From 733d8de51e62a7830f931763f91d9b8240382506 Mon Sep 17 00:00:00 2001 From: olelod Date: Wed, 19 May 2021 11:29:41 +0000 Subject: [PATCH 4/9] changelog --- CHANGELOG.md | 1 + src/flownet/parameters/_porv_poro_trans.py | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 54753a500..b2b1d9ecc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ This project adheres to [Semantic Versioning](https://semver.org/). ## Unreleased ### Added +- [#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. - [#356](https://github.com/equinor/flownet/pull/356) Added option to distribute the original volume over the FlowNet tubes in a Voronoi-diagram style. I.e., areas with a high density of FlowNet tubes get a lower volume per tube. diff --git a/src/flownet/parameters/_porv_poro_trans.py b/src/flownet/parameters/_porv_poro_trans.py index 2a3c4f076..5a5b22a78 100644 --- a/src/flownet/parameters/_porv_poro_trans.py +++ b/src/flownet/parameters/_porv_poro_trans.py @@ -211,8 +211,8 @@ def render_output(self) -> Dict: if param == "permeability_regional": df_merge = df_merge[~df_merge.index.duplicated(keep="first")] - perm_per_tube["PERMX"] = ( - perm_per_tube["PERMX"] * df_merge["REGIONAL_MULT"] + perm_per_tube[regional_param_to_property[param]] = ( + perm_per_tube[regional_param_to_property[param]] * df_merge["REGIONAL_MULT"] ) else: df_merge.index = properties_per_cell.index From c2f3872e911c72da05cb6f6c865bfa77fda26e6a Mon Sep 17 00:00:00 2001 From: olelod Date: Wed, 19 May 2021 12:11:46 +0000 Subject: [PATCH 5/9] docstrings --- src/flownet/ahm/_run_ahm.py | 28 +++++++++++----------- src/flownet/parameters/_porv_poro_trans.py | 20 +++++++++------- 2 files changed, 25 insertions(+), 23 deletions(-) diff --git a/src/flownet/ahm/_run_ahm.py b/src/flownet/ahm/_run_ahm.py index 347c8e965..f3a102c28 100644 --- a/src/flownet/ahm/_run_ahm.py +++ b/src/flownet/ahm/_run_ahm.py @@ -204,20 +204,25 @@ def _get_regional_distribution( network: NetworkModel, field_data: FlowData, ti2ci: pd.DataFrame, -) -> Tuple[pd.DataFrame, pd.DataFrame, pd.DataFrame]: +) -> Tuple[pd.DataFrame, pd.DataFrame]: """ - Create the distribution min, mean, base, stddev, max for one or more parameters + Create: + 1) The distribution with min, mean, base, stddev, max for one or more regional parameter + multipliers + 2) A dataframe linking region model index to cell model index for each regional multiplier Args: parameters: which parameter(s) should be outputted in the dataframe parameters_config: the parameters definition from the configuration file - network: - field_data: - ti2ci: + network: FlowNet network instance + field_data: FlowData class with information from simulation model data source + ti2ci: A dataframe with index equal to tube model index, and one column which equals cell indices. Returns: - A dataframe with distributions for the requested parameter(s) - + tuple: a tuple containing: + ci2ri (pd.DataFrame): A dataframe with index equal to cell model index, and one column containing + region index for each of the requested regional multiplier(s). + df_dist_values (pd.DataFrame): A dataframe with distributions for the requested regional multiplier(s) """ if not isinstance(parameters, list): parameters = [parameters] @@ -262,7 +267,7 @@ def _get_regional_distribution( df_dist_values[ f"distribution_{parameter}_regional" ] = parameter_config_regional.distribution - return ci2ri, df, df_dist_values + return ci2ri, df_dist_values def _constrain_using_well_logs( @@ -847,11 +852,7 @@ def run_flownet_history_matching( for param in {"bulkvolume_mult", "porosity", "permeability"} if getattr(config.model_parameters, param + "_regional_scheme") != "individual" ] - ( - ci2ri, - df_regional, - regional_porv_poro_trans_dist_values, - ) = _get_regional_distribution( + (ci2ri, regional_porv_poro_trans_dist_values,) = _get_regional_distribution( regional_parameters, config.model_parameters, network, @@ -921,7 +922,6 @@ def run_flownet_history_matching( PorvPoroTrans( porv_poro_trans_dist_values, regional_porv_poro_trans_dist_values, - df_regional, ti2ci, ci2ri, network, diff --git a/src/flownet/parameters/_porv_poro_trans.py b/src/flownet/parameters/_porv_poro_trans.py index 5a5b22a78..ed9180dfe 100644 --- a/src/flownet/parameters/_porv_poro_trans.py +++ b/src/flownet/parameters/_porv_poro_trans.py @@ -86,11 +86,15 @@ class PorvPoroTrans(Parameter): is written to the grid section. Args: - distribution_values: - A dataframe with index equal to tube model index, and with eighteen columns + distribution_values: A dataframe with index equal to tube model index, and with eighteen columns specifiying min, max, mean, mode, stddev and distribution for three different parameters (bulkvolume_mult, porosity and permeability). + regional_distribution_valuels: A dataframe with index equal to region model index, with 6 columns + specifying min, max, mean, mode, stddev and distribution, for the regional multipliers requested + for the FlowNet model (bulkvolume_mult, porosity and permeability can also have regional multipliers). ti2ci: A dataframe with index equal to tube model index, and one column which equals cell indices. + ci2ri: A dataframe with index equal to cell model index, and one column with region model index + for each of the regional multipliers requested for the FlowNet model network: The FlowNet NetworkModel instance. min_permeability: The minimum permeability threshold for which tube volumes are set to zero and thus made inactive. @@ -101,7 +105,6 @@ def __init__( self, distribution_values: pd.DataFrame, regional_distribution_values: pd.DataFrame, - df_regional: pd.DataFrame, ti2ci: pd.DataFrame, ci2ri: pd.DataFrame, network: NetworkModel, @@ -112,9 +115,7 @@ def __init__( self._network: NetworkModel = network self._number_tubes: int = len(self._ti2ci.index.unique()) - self._regions: Dict[str, int] = { - key: df_regional[key].max() for key in df_regional.columns - } + self._regions: Dict[str, int] = {key: ci2ri[key].max() for key in ci2ri.columns} self.min_permeability: Optional[float] = min_permeability self._random_variables: List[ProbabilityDistribution] = ( @@ -132,9 +133,9 @@ def __init__( ] + [ parameter_probability_distribution_class(row, param) - for param in df_regional.columns + for param in ci2ri.columns for _, row in regional_distribution_values.loc[ - regional_distribution_values.index.repeat(df_regional[param].max()) + regional_distribution_values.index.repeat(ci2ri.max()) ] .reset_index(drop=True) .iterrows() @@ -212,7 +213,8 @@ def render_output(self) -> Dict: if param == "permeability_regional": df_merge = df_merge[~df_merge.index.duplicated(keep="first")] perm_per_tube[regional_param_to_property[param]] = ( - perm_per_tube[regional_param_to_property[param]] * df_merge["REGIONAL_MULT"] + perm_per_tube[regional_param_to_property[param]] + * df_merge["REGIONAL_MULT"] ) else: df_merge.index = properties_per_cell.index From a2358d7cb06d4f61cef9b68c4374c4b21b6e3e31 Mon Sep 17 00:00:00 2001 From: olelod Date: Fri, 21 May 2021 09:01:41 +0000 Subject: [PATCH 6/9] change branch on testdata --- .github/workflows/flownet.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/flownet.yml b/.github/workflows/flownet.yml index 789191164..7f585b3a1 100644 --- a/.github/workflows/flownet.yml +++ b/.github/workflows/flownet.yml @@ -38,12 +38,12 @@ jobs: - name: 🍿 Download FlowNet test data env: INPUT_MODEL_FOLDER: ./flownet-testdata/${{ matrix.flownet-model }}/input_model - # If you want the CI to (temporarily) run against your fork of the testdada, + # If you want the CI to (temporarily) run against your fork of the testdata, # change the value her from "equinor" to your username. - TESTDATA_REPO_OWNER: equinor + TESTDATA_REPO_OWNER: olelod # If you want the CI to (temporarily) run against another branch than master, # change the value her from "master" to the relevant branch name. - TESTDATA_REPO_BRANCH: master + TESTDATA_REPO_BRANCH: regional-multipliers run: | git clone --depth 1 --branch $TESTDATA_REPO_BRANCH https://github.com/$TESTDATA_REPO_OWNER/flownet-testdata.git --recursive for f in $INPUT_MODEL_FOLDER/*.tar.gz; do tar -zxvf "$f" -C $INPUT_MODEL_FOLDER; done From f970b5bddb3726999b0e22907d4bb4a1b1db0178 Mon Sep 17 00:00:00 2001 From: olelod Date: Fri, 21 May 2021 09:06:11 +0000 Subject: [PATCH 7/9] change branch on testdata --- .github/workflows/flownet.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/flownet.yml b/.github/workflows/flownet.yml index 7f585b3a1..892b0c4c2 100644 --- a/.github/workflows/flownet.yml +++ b/.github/workflows/flownet.yml @@ -40,7 +40,7 @@ jobs: INPUT_MODEL_FOLDER: ./flownet-testdata/${{ matrix.flownet-model }}/input_model # If you want the CI to (temporarily) run against your fork of the testdata, # change the value her from "equinor" to your username. - TESTDATA_REPO_OWNER: olelod + TESTDATA_REPO_OWNER: equinor # If you want the CI to (temporarily) run against another branch than master, # change the value her from "master" to the relevant branch name. TESTDATA_REPO_BRANCH: regional-multipliers From e206f0a20948ff901bcd7501567816f989a54a36 Mon Sep 17 00:00:00 2001 From: olelod Date: Fri, 21 May 2021 10:54:58 +0000 Subject: [PATCH 8/9] fix failing ci --- src/flownet/parameters/_porv_poro_trans.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/flownet/parameters/_porv_poro_trans.py b/src/flownet/parameters/_porv_poro_trans.py index ed9180dfe..1d93c124d 100644 --- a/src/flownet/parameters/_porv_poro_trans.py +++ b/src/flownet/parameters/_porv_poro_trans.py @@ -135,7 +135,7 @@ def __init__( parameter_probability_distribution_class(row, param) for param in ci2ri.columns for _, row in regional_distribution_values.loc[ - regional_distribution_values.index.repeat(ci2ri.max()) + regional_distribution_values.index.repeat(ci2ri[param].max()) ] .reset_index(drop=True) .iterrows() From 0ebed53b8dd44dcc1cbb62171edf1131747664b8 Mon Sep 17 00:00:00 2001 From: olelod Date: Thu, 27 May 2021 11:22:58 +0000 Subject: [PATCH 9/9] changes after review --- .github/workflows/flownet.yml | 2 +- src/flownet/parameters/_porv_poro_trans.py | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/flownet.yml b/.github/workflows/flownet.yml index 892b0c4c2..56830b9b3 100644 --- a/.github/workflows/flownet.yml +++ b/.github/workflows/flownet.yml @@ -43,7 +43,7 @@ jobs: TESTDATA_REPO_OWNER: equinor # If you want the CI to (temporarily) run against another branch than master, # change the value her from "master" to the relevant branch name. - TESTDATA_REPO_BRANCH: regional-multipliers + TESTDATA_REPO_BRANCH: master run: | git clone --depth 1 --branch $TESTDATA_REPO_BRANCH https://github.com/$TESTDATA_REPO_OWNER/flownet-testdata.git --recursive for f in $INPUT_MODEL_FOLDER/*.tar.gz; do tar -zxvf "$f" -C $INPUT_MODEL_FOLDER; done diff --git a/src/flownet/parameters/_porv_poro_trans.py b/src/flownet/parameters/_porv_poro_trans.py index 1d93c124d..e2c45349a 100644 --- a/src/flownet/parameters/_porv_poro_trans.py +++ b/src/flownet/parameters/_porv_poro_trans.py @@ -188,7 +188,6 @@ def render_output(self) -> Dict: ) # Calculate updated base permeability (and NNC transmissibility) - perm_per_tube = pd.DataFrame( self.random_samples[2 * self._number_tubes : 3 * self._number_tubes], index=self._ti2ci.index.unique(),