From efd7d24b70f49cf987f7331fed7a95ae2a5a330c Mon Sep 17 00:00:00 2001 From: ssadig <116078191+ssadig@users.noreply.github.com> Date: Thu, 16 Feb 2023 13:17:38 +0100 Subject: [PATCH] Support for LET and Skjaeveland Pc in Pyscalcli (#392) Co-authored-by: Subhi Sadigov (SUB CC RE) --- .gitignore | 1 + pyscal/factory.py | 73 ++++++++++++++++--- pyscal/pyscalcli.py | 7 ++ tests/data/pyscal_config_simple_let_hyst.csv | 3 + .../relperm-input-example_skjaeveland.csv | 3 + tests/test_pyscalcli.py | 36 ++++++++- 6 files changed, 111 insertions(+), 12 deletions(-) create mode 100644 tests/data/pyscal_config_simple_let_hyst.csv create mode 100644 tests/data/relperm-input-example_skjaeveland.csv diff --git a/.gitignore b/.gitignore index 21cfcbce..d23c3db6 100644 --- a/.gitignore +++ b/.gitignore @@ -94,6 +94,7 @@ celerybeat-schedule env/ venv/ ENV/ +myenv/ env.bak/ venv.bak/ diff --git a/pyscal/factory.py b/pyscal/factory.py index dc1bc5fc..009af6ea 100644 --- a/pyscal/factory.py +++ b/pyscal/factory.py @@ -56,6 +56,19 @@ def slicedict(dct: dict, keys: Iterable): "perm_ref", "drho", ] # "g" is optional +WO_LET_PC_PD = ["lpow", "epow", "tpow", "ltow", "etow", "ttow", "pcowmax", "pcowt"] +WO_LET_PC_IMB = [ + "lsow", + "esow", + "tsow", + "lfow", + "efow", + "tfow", + "pcowmax", + "pcowmin", + "pcowt", +] +WO_SKJAEVELAND_PC = ["cw", "co", "aw", "ao"] GO_INIT = ["swirr", "sgcr", "sorg", "sgro", "swl", "krgendanchor", "h", "tag"] GO_COREY_GAS = ["ng"] @@ -68,6 +81,18 @@ def slicedict(dct: dict, keys: Iterable): GO_LET_GAS = ["lg", "eg", "tg"] GO_LET_OIL = ["log", "eog", "tog"] +GO_LET_PC_PD = ["lpog", "epog", "tpog", "ltog", "etog", "ttog", "pcogmax", "pcogt"] +GO_LET_PC_IMB = [ + "lsog", + "esog", + "tsog", + "lfog", + "efog", + "tfog", + "pcogmax", + "pcogmin", + "pcogt", +] GW_INIT = ["swirr", "swl", "sgl", "swcr", "sgrw", "sgcr", "h", "tag"] GW_COREY_WATER = ["nw"] @@ -86,6 +111,18 @@ def slicedict(dct: dict, keys: Iterable): "perm_ref", "drho", ] # "g" is optional +GW_LET_PC_PD = ["lpgw", "epgw", "tpgw", "ltgw", "etgw", "ttgw", "pcgwmax", "pcgwt"] +GW_LET_PC_IMB = [ + "lsgw", + "esgw", + "tsgw", + "lfgw", + "efgw", + "tfgw", + "pcgwmax", + "pcgwmin", + "pcgwt", +] WOG_INIT = ["swirr", "swl", "swcr", "sorw", "socr", "sorg", "sgcr", "h", "tag"] @@ -263,6 +300,10 @@ def create_water_oil( params_simple_j = slicedict(params, WO_SIMPLE_J + ["g"]) params_norm_j = slicedict(params, WO_NORM_J) params_simple_j_petro = slicedict(params, WO_SIMPLE_J_PETRO + ["g"]) + params_let_pc_pd = slicedict(params, WO_LET_PC_PD) + params_let_pc_imb = slicedict(params, WO_LET_PC_IMB) + params_skjaeveland_pc = slicedict(params, WO_SKJAEVELAND_PC) + if set(WO_SIMPLE_J).issubset(set(params_simple_j)): wateroil.add_simple_J(**params_simple_j) elif set(WO_SIMPLE_J_PETRO).issubset(set(params_simple_j_petro)): @@ -271,6 +312,29 @@ def create_water_oil( wateroil.add_simple_J_petro(**params_simple_j_petro) elif set(WO_NORM_J).issubset(set(params_norm_j)): wateroil.add_normalized_J(**params_norm_j) + elif set(WO_LET_PC_PD).issubset(set(params_let_pc_pd)): + params_let_pc_pd["Lp"] = params_let_pc_pd.pop("lpow") + params_let_pc_pd["Ep"] = params_let_pc_pd.pop("epow") + params_let_pc_pd["Tp"] = params_let_pc_pd.pop("tpow") + params_let_pc_pd["Lt"] = params_let_pc_pd.pop("ltow") + params_let_pc_pd["Et"] = params_let_pc_pd.pop("etow") + params_let_pc_pd["Tt"] = params_let_pc_pd.pop("ttow") + params_let_pc_pd["Pcmax"] = params_let_pc_pd.pop("pcowmax") + params_let_pc_pd["Pct"] = params_let_pc_pd.pop("pcowt") + wateroil.add_LET_pc_pd(**params_let_pc_pd) + elif set(WO_LET_PC_IMB).issubset(set(params_let_pc_imb)): + params_let_pc_imb["Ls"] = params_let_pc_imb.pop("lsow") + params_let_pc_imb["Es"] = params_let_pc_imb.pop("esow") + params_let_pc_imb["Ts"] = params_let_pc_imb.pop("tsow") + params_let_pc_imb["Lf"] = params_let_pc_imb.pop("lfow") + params_let_pc_imb["Ef"] = params_let_pc_imb.pop("efow") + params_let_pc_imb["Tf"] = params_let_pc_imb.pop("tfow") + params_let_pc_imb["Pcmax"] = params_let_pc_imb.pop("pcowmax") + params_let_pc_imb["Pct"] = params_let_pc_imb.pop("pcowt") + params_let_pc_imb["Pcmin"] = params_let_pc_imb.pop("pcowmin") + wateroil.add_LET_pc_imb(**params_let_pc_imb) + elif set(WO_SKJAEVELAND_PC).issubset(set(params_skjaeveland_pc)): + wateroil.add_skjaeveland_pc(**params_skjaeveland_pc) else: logger.info( ( @@ -712,15 +776,6 @@ def load_relperm_df( "merged cells in XLSX, which is not supported." ) - # Warn about any other columns with empty cells: - nan_columns = set(input_df.columns[input_df.isnull().any()]) - allowed_nan_columns = set(["COMMENT"]) - if nan_columns - allowed_nan_columns: - logger.warning( - "Found empty cells in these columns, this might create trouble: %s", - str(nan_columns - allowed_nan_columns), - ) - # Check that SATNUM's are consecutive and integers: try: input_df["SATNUM"] = input_df["SATNUM"].astype(int) diff --git a/pyscal/pyscalcli.py b/pyscal/pyscalcli.py index f8ee6cab..ad1761f5 100644 --- a/pyscal/pyscalcli.py +++ b/pyscal/pyscalcli.py @@ -39,6 +39,13 @@ is used. Check API for exact formulas. Normalized J-function is used if 'a', 'b', 'poro', 'perm' and 'sigma_costau' is provided. +Primary drainage and imbibition capillary pressure with LET parametrization are +based on presence of the columnns 'Lp', 'Ep', 'Tp','Lt', 'Et', 'Tt', 'Pcmax', 'Pct' and +'Ls', 'Es', 'Ts', 'Lf', 'Ef', 'Tf', 'Pcmax', 'Pct', 'Pcmin' respectively. + +Capillary pressure from the Skjæveland correlation is based on the on the presence +of the columns 'cw', 'co', 'aw', 'ao'. + For SCAL recommendations, there should be exactly three rows for each SATNUM, tagged with the strings 'low', 'base' and 'high' in the column 'CASE' diff --git a/tests/data/pyscal_config_simple_let_hyst.csv b/tests/data/pyscal_config_simple_let_hyst.csv new file mode 100644 index 00000000..2939cbe3 --- /dev/null +++ b/tests/data/pyscal_config_simple_let_hyst.csv @@ -0,0 +1,3 @@ +SATNUM,swirr,sorw,sorg,sgcr,krwend,krgend,kroend,Lw,Ew,Tw,Lo,Eo,To,Lg,Eg,Tg,Log,Eog,Tog,swl,swcr,Lpow,Epow,Tpow,Ltow,Etow,Ttow,Lsow,Esow,Tsow,Lfow,Efow,Tfow,Pcowmax,Pcowt,Pcowmin +1,0,0,0.1,0.05,0.9,0.95,1,2.6,3.2,1.2,3.7,2.4,1.2,2.4,1.6,1.1,3.9,1.8,1,0.020560098943989,0.030560098943989,2.7,3.3,1.3,3.8,2.5,1.3,,,,,,,15,10, +2,0,0,0.1,0.05,0.9,0.95,1,2.6,3.2,1.2,3.7,2.4,1.2,2.4,1.6,1.1,3.9,1.8,1,0.020560098943989,0.030560098943989,,,,,,,2.7,3.3,1.3,3.8,2.5,1.3,5,-5,-10 diff --git a/tests/data/relperm-input-example_skjaeveland.csv b/tests/data/relperm-input-example_skjaeveland.csv new file mode 100644 index 00000000..912ccac3 --- /dev/null +++ b/tests/data/relperm-input-example_skjaeveland.csv @@ -0,0 +1,3 @@ +SATNUM,swirr,sorw,sorg,sgcr,krwend,krgend,kroend,Lw,Ew,Tw,Lo,Eo,To,Lg,Eg,Tg,Log,Eog,Tog,swl,swcr,cw,co,aw,ao +1,0,0,0.1,0.05,0.9,0.95,1,2.6,3.2,1.2,3.7,2.4,1.2,2.4,1.6,1.1,3.9,1.8,1,0.020560098943989,0.030560098943989,0.5,-0.5,0.5,0.5 +2,0,0,0.1,0.05,0.9,0.95,1,2.6,3.2,1.2,3.7,2.4,1.2,2.4,1.6,1.1,3.9,1.8,1,0.020560098943989,0.030560098943989,0.5,-0.5,0.5,0.5 diff --git a/tests/test_pyscalcli.py b/tests/test_pyscalcli.py index bdb40532..8d99e48f 100644 --- a/tests/test_pyscalcli.py +++ b/tests/test_pyscalcli.py @@ -74,9 +74,6 @@ def test_pyscal_client_static(tmp_path, caplog, default_loglevel, mocker): assert not any(record.levelno == logging.ERROR for record in caplog.records) assert not any(record.levelno == logging.INFO for record in caplog.records) - # We get one warning due to empty cells in xlsx: - assert sum(record.levelno == logging.WARNING for record in caplog.records) == 1 - relpermlines = os.linesep.join( Path("relperm.inc").read_text(encoding="utf8").splitlines() ) @@ -273,6 +270,39 @@ def test_pyscalcli_stdout_output(capsys, mocker): assert "SOF3" in captured.out +def test_pyscalcli_let_pd_and_imb(capsys, mocker): + """Test that we can write to stdout""" + scalrec_file = ( + Path(__file__).absolute().parent / "data/pyscal_config_simple_let_hyst.csv" + ) + mocker.patch( + "sys.argv", + ["pyscal", str(scalrec_file), "--output", "-"], + ) + pyscalcli.main() + captured = capsys.readouterr() + assert "SWOF" in captured.out + assert "SGOF" in captured.out + assert "LET correlation for primary drainage Pc" in captured.out + assert "LET correlation for imbibition Pc" in captured.out + + +def test_pyscalcli_pc_skjaeveland(capsys, mocker): + """Test that we can write to stdout""" + scalrec_file = ( + Path(__file__).absolute().parent / "data/relperm-input-example_skjaeveland.csv" + ) + mocker.patch( + "sys.argv", + ["pyscal", str(scalrec_file), "--output", "-"], + ) + pyscalcli.main() + captured = capsys.readouterr() + assert "SWOF" in captured.out + assert "SGOF" in captured.out + assert "Skjæveland correlation for Pc" in captured.out + + def test_pyscalcli_exception_catching(capsys, mocker): """The command line client catches selected exceptions.