From b3df9a34aa3ddb07e9eb7ee9bf62b5fc9c27bbad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5vard=20Berland?= Date: Fri, 28 May 2021 09:43:59 +0200 Subject: [PATCH 1/2] Tighten up typing and formatting in interp_relperm --- .../interp_relperm/interp_relperm.py | 55 ++++++++++--------- tests/test_interp_relperm.py | 8 +-- tests/testdata_interp_relperm/cfg.yml | 40 ++++++-------- 3 files changed, 50 insertions(+), 53 deletions(-) diff --git a/src/subscript/interp_relperm/interp_relperm.py b/src/subscript/interp_relperm/interp_relperm.py index cac40eac8..6cc8e9d54 100755 --- a/src/subscript/interp_relperm/interp_relperm.py +++ b/src/subscript/interp_relperm/interp_relperm.py @@ -3,6 +3,7 @@ import logging import argparse from pathlib import Path +from typing import List, Dict, Any import yaml import pandas as pd @@ -61,36 +62,36 @@ - swof_pes.inc - /project/snakeoil/user/best/r001/ert/input/relperm/sgof_low.inc - result_file : outfilen.inc # Required: Name of output file with interpolated tables + result_file: outfilen.inc # Required: Name of output file with interpolated tables - delta_s : 0.02 # Optional: resolution of Sw/Sg, defaulted to 0.01 + delta_s: 0.02 # Optional: resolution of Sw/Sg, defaulted to 0.01 # Required: applied in order of appearance so that # a default value for all tables can set and overrided # for individual satnums later. interpolations: - - tables : [] + - tables: [] # Required: list of satnums to be interpolated # empty list interpreted as all entries # for individual satnums later. - param_w : -0.23 - param_g : 0.44 + param_w: -0.23 + param_g: 0.44 # Required: list of satnums to be interpolated # empty list interpreted as all entries - - tables : [1] + - tables: [1] # will only apply to satnum nr. 1, for SWOF and SGOF - param_w : -0.23 - param_g : 0.24 + param_w: -0.23 + param_g: 0.24 - - tables : [2,5,75] + - tables: [2,5,75] # applies to satnum 2, 5, and 75, for SWOF # (not SGOF since param_g not declared) SGOF # will be interpolated using 0.44, from above. # If a parameter not set, no interpolation will # be applied ie base table is returned - param_w : 0.5 + param_w: 0.5 """ @@ -231,20 +232,20 @@ def get_cfg_schema() -> dict: return schema -def tables_to_dataframe(filenames: list) -> pd.DataFrame: +def tables_to_dataframe(filenames: List[str]) -> pd.DataFrame: """ Routine to gather scal tables (SWOF and SGOF) from ecl include files. Parameters: - filenames (list): List with filenames (str) to be parsed. - Assumed to contain ecl SCAL tables + filenames : List with filenames to be parsed. Assumed to contain Eclipse + saturation function keywords. Returns: dataframe with the tables """ return pd.concat( - [satfunc.df(open(filename).read()) for filename in filenames], sort=False + [satfunc.df(Path(filename).read_text()) for filename in filenames], sort=False ) @@ -252,7 +253,7 @@ def make_interpolant( base_df: pd.DataFrame, low_df: pd.DataFrame, high_df: pd.DataFrame, - interp_param: dict, + interp_param: Dict[str, float], satnum: int, delta_s: float, ) -> pyscal.WaterOilGas: @@ -260,16 +261,16 @@ def make_interpolant( Routine to define a pyscal.interpolant instance and perform interpolation. Parameters: - base_df (pd.DataFrame): containing the base tables - low_df (pd.DataFrame): containing the low tables - high_df (pd.DataFrame): containing the high tables - interp_param (dict): With keys ('param_w', 'param_g'), + base_df: containing the base tables + low_df: containing the low tables + high_df: containing the high tables + interp_param: With keys ('param_w', 'param_g'), the interp parameter values - satnum (int): the satuation number index - delta_s (float): the saturation spacing to be used in out tables + satnum: the satuation number index + delta_s: the saturation spacing to be used in out tables Returns: - pyscal.WaterOilGas: Object holding tables for one satnum + Object holding tables for one satnum """ # Define base tables @@ -451,7 +452,7 @@ def get_parser() -> argparse.ArgumentParser: return parser -def main(): +def main() -> None: """ Main function; this is what is executed """ @@ -472,13 +473,13 @@ def main(): process_config(cfg, args.root_path) -def process_config(cfg: dict, root_path: str = "") -> None: +def process_config(cfg: Dict[str, Any], root_path: str = "") -> None: """ Process a configuration and dumps produced Eclipse include file to disk. Args: - cfg (dict): Configuration for files to parse and interpolate in - root_path (str): Preprended to the file paths. Defaults to empty string + cfg: Configuration for files to parse and interpolate in + root_path: Prepended to the file paths. Defaults to empty string """ # add root-path to all include files if "base" in cfg.keys(): @@ -546,7 +547,7 @@ def process_config(cfg: dict, root_path: str = "") -> None: satnums = range(1, base_df.reset_index("SATNUM")["SATNUM"].unique().max() + 1) for satnum in satnums: - interp_values = {"param_w": 0, "param_g": 0} + interp_values = {"param_w": 0.0, "param_g": 0.0} for interp in cfg_suite.snapshot.interpolations: if not interp.tables or satnum in interp.tables: if interp.param_w: diff --git a/tests/test_interp_relperm.py b/tests/test_interp_relperm.py index 4ec8f2316..f942684c7 100644 --- a/tests/test_interp_relperm.py +++ b/tests/test_interp_relperm.py @@ -249,7 +249,7 @@ def test_args(tmpdir, mocker): interp_relperm.main() - assert Path("outfilen.inc").exists() + assert Path("outfile.inc").exists() def test_mock(tmpdir): @@ -451,7 +451,7 @@ def test_main(tmpdir, mocker): mocker.patch("sys.argv", [__file__, "-c", str(test_cfg), "-r", str(TESTDATA)]) interp_relperm.main() - assert Path("outfilen.inc").exists() + assert Path("outfile.inc").exists() if __name__ == "__main__": @@ -461,5 +461,5 @@ def test_main(tmpdir, mocker): test_schema_errors() test_tables_to_dataframe() test_make_interpolant() - test_args() - test_main() + test_args() # type: ignore + test_main() # type: ignore diff --git a/tests/testdata_interp_relperm/cfg.yml b/tests/testdata_interp_relperm/cfg.yml index 565f6f160..f6430f89c 100644 --- a/tests/testdata_interp_relperm/cfg.yml +++ b/tests/testdata_interp_relperm/cfg.yml @@ -1,33 +1,29 @@ -base: # a unified file with SWOF and SGOF or two separate files. Both tables are required +base: + # Unified file with SWOF and SGOF or two separate files. Both tables are required - swof_base.inc - sgof_base.inc -high: # a unified file with SWOF and SGOF or two separate files. Nothing required +high: + # Not required. - swof_opt.inc - sgof_opt.inc -low: # a unified file with SWOF and SGOF or two separate files. Nothing required +low: + # Not required - swof_pes.inc - sgof_pes.inc -result_file : outfilen.inc +result_file: outfile.inc -delta_s : 0.001 # optional: defaulted to 0.01 this does not work as expected! +delta_s: 0.001 # optional, defaulted to 0.01 interpolations: - - param_w : -0.9975 - param_g : -0.44 - - - tables : [] # all - param_w : -0.9975 - param_g : -0.44 - - tables : [1] # 1,3,6 #count from 1 NOT from 0 - param_w : 0.3975 - - tables : [2] # 1,3,6 #count from 1 NOT from 0 - param_w : 0.1975 - - - - - - - + - param_w: -0.9975 + param_g: -0.44 + + - tables: [] # all + param_w: -0.9975 + param_g: -0.44 + - tables: [1] # 1,3,6 # count from 1 NOT from 0 + param_w: 0.3975 + - tables: [2] # 1,3,6 # count from 1 NOT from 0 + param_w: 0.1975 From 5b8a3d2496660b9844832899514664514bc8580b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5vard=20Berland?= Date: Fri, 28 May 2021 10:13:39 +0200 Subject: [PATCH 2/2] Support family 2 in output from interp_relperm --- .../interp_relperm/interp_relperm.py | 22 ++++++---- tests/test_interp_relperm.py | 42 +++++++++++++++---- 2 files changed, 46 insertions(+), 18 deletions(-) diff --git a/src/subscript/interp_relperm/interp_relperm.py b/src/subscript/interp_relperm/interp_relperm.py index 6cc8e9d54..9ca5b1388 100755 --- a/src/subscript/interp_relperm/interp_relperm.py +++ b/src/subscript/interp_relperm/interp_relperm.py @@ -64,6 +64,8 @@ result_file: outfilen.inc # Required: Name of output file with interpolated tables + family: 1 # Eclipse keyword family. 1 is optional, 2 is the alternative + delta_s: 0.02 # Optional: resolution of Sw/Sg, defaulted to 0.01 # Required: applied in order of appearance so that @@ -172,6 +174,11 @@ def _is_valid_table_entries(schema: dict): return valid +@configsuite.validator_msg("Valid Eclipse keyword family") +def _is_valid_eclipse_keyword_family(schema: dict): + return schema["family"] in [1, 2] + + def get_cfg_schema() -> dict: """ Defines the yml config schema @@ -207,6 +214,7 @@ def get_cfg_schema() -> dict: }, }, "result_file": {MK.Type: types.String}, + "family": {MK.Type: types.Number, MK.Default: 1}, "delta_s": {MK.Type: types.Number, MK.Default: 0.01}, "interpolations": { MK.Type: types.List, @@ -543,7 +551,7 @@ def process_config(cfg: Dict[str, Any], root_path: str = "") -> None: high_df.sort_index(inplace=True) # Loop over satnum and interpolate according to default and cfg values - interpolants = [] + interpolants = pyscal.PyscalList() satnums = range(1, base_df.reset_index("SATNUM")["SATNUM"].unique().max() + 1) for satnum in satnums: @@ -561,14 +569,10 @@ def process_config(cfg: Dict[str, Any], root_path: str = "") -> None: ) ) - # Dump to Eclipse include file: - with open(cfg_suite.snapshot.result_file, "w") as fileh: - fileh.write("SWOF\n") - for interpolant in interpolants: - fileh.write(interpolant.wateroil.SWOF(header=False)) - fileh.write("\nSGOF\n") - for interpolant in interpolants: - fileh.write(interpolant.gasoil.SGOF(header=False)) + if cfg_suite.snapshot.family == 1: + interpolants.dump_family_1(cfg_suite.snapshot.result_file) + else: + interpolants.dump_family_2(cfg_suite.snapshot.result_file) logger.info( "Done; interpolated relperm curves written to file: %s", diff --git a/tests/test_interp_relperm.py b/tests/test_interp_relperm.py index f942684c7..df8b3c5d0 100644 --- a/tests/test_interp_relperm.py +++ b/tests/test_interp_relperm.py @@ -252,14 +252,7 @@ def test_args(tmpdir, mocker): assert Path("outfile.inc").exists() -def test_mock(tmpdir): - """Mocked pyscal-generated input files. - - Note that this is using pyscal both for dumping to disk and - parsing from disk, and is thus not representative for how flexible - the code is for reading from include files not originating in pyscal. - """ - tmpdir.chdir() +def mock_family_1(): columns = [ "SATNUM", "Nw", @@ -289,6 +282,17 @@ def test_mock(tmpdir): PyscalFactory.create_pyscal_list(dframe_base).dump_family_1("base.inc") PyscalFactory.create_pyscal_list(dframe_opt).dump_family_1("opt.inc") + +def test_mock(tmpdir): + """Mocked pyscal-generated input files. + + Note that this is using pyscal both for dumping to disk and + parsing from disk, and is thus not representative for how flexible + the code is for reading from include files not originating in pyscal. + """ + tmpdir.chdir() + mock_family_1() + config = { "base": ["base.inc"], "low": ["pess.inc"], @@ -298,7 +302,6 @@ def test_mock(tmpdir): "delta_s": 0.1, } - interp_relperm.process_config(config) interp_relperm.process_config(config) outfile_df = satfunc.df(open("outfile.inc").read(), ntsfun=1) @@ -312,6 +315,27 @@ def test_mock(tmpdir): assert outfile_df["PCOW"].sum() > 0 +def test_family_2_output(tmpdir): + tmpdir.chdir() + mock_family_1() + + config = { + "base": ["base.inc"], + "low": ["pess.inc"], + "high": ["opt.inc"], + "result_file": "outfile.inc", + "interpolations": [{"param_w": -0.5, "param_g": 0.5}], + "family": 2, + "delta_s": 0.1, + } + interp_relperm.process_config(config) + output = Path("outfile.inc").read_text() + + assert "SWFN" in output + assert "SGFN" in output + assert "SOF3" in output + + def test_mock_two_satnums(tmpdir): """Mocked pyscal-generated input files.