def test_factory_wateroilgas_deprecated_krowgend(caplog): """Some users will use deprecated krowend krogend, these values should be translated to kroend""" wog = PyscalFactory.create_water_oil_gas( dict(nw=2, now=3, ng=1, nog=2.5, krowend=0.6, krogend=0.7)) assert "deprecated" in caplog.text swof = wog.SWOF() assert "kroend=0.6" in swof sgof = wog.SGOF() assert "kroend=0.7" in sgof assert not wog.threephaseconsistency() sat_table_str_ok(swof) # sgof code works for swof also currently sat_table_str_ok(sgof) assert "Corey krg" in sgof assert "Corey krog" in sgof assert "Corey krw" in swof assert "Corey krow" in swof
def test_fast_mode(): """Test that the fast-flag is passed on to constructed objects Each object's own test code tests the actual effects of the fast flag""" wateroil = PyscalFactory.create_water_oil({"nw": 2, "now": 2}) assert not wateroil.fast wateroil = PyscalFactory.create_water_oil({"nw": 2, "now": 2}, fast=True) assert wateroil.fast gasoil = PyscalFactory.create_gas_oil({"ng": 2, "nog": 2}) assert not gasoil.fast gasoil = PyscalFactory.create_gas_oil({"ng": 2, "nog": 2}, fast=True) assert gasoil.fast gaswater = PyscalFactory.create_gas_water({"nw": 2, "ng": 2}) assert not gaswater.gasoil.fast assert not gaswater.wateroil.fast gaswater = PyscalFactory.create_gas_water({"nw": 2, "ng": 2}, fast=True) assert gaswater.gasoil.fast assert gaswater.wateroil.fast assert gaswater.fast wateroilgas = PyscalFactory.create_water_oil_gas( {"nw": 2, "now": 2, "ng": 2, "nog": 2}, fast=True ) assert wateroilgas.fast assert wateroilgas.wateroil.fast assert wateroilgas.gasoil.fast scalrec = PyscalFactory.create_scal_recommendation( { "low": {"nw": 2, "now": 2, "ng": 2, "nog": 2}, "base": {"nw": 2, "now": 2, "ng": 2, "nog": 2}, "high": {"nw": 2, "now": 2, "ng": 2, "nog": 2}, }, fast=True, ) assert scalrec.low.fast assert scalrec.base.fast assert scalrec.high.fast interpolant = scalrec.interpolate(-0.5) assert interpolant.fast
def test_xls_scalrecommendation(): """Test making SCAL recommendations from xls data""" if "__file__" in globals(): # Easen up copying test code into interactive sessions testdir = os.path.dirname(os.path.abspath(__file__)) else: testdir = os.path.abspath(".") xlsxfile = testdir + "/data/scal-pc-input-example.xlsx" scalinput = pd.read_excel(xlsxfile).set_index(["SATNUM", "CASE"]) print(scalinput) for satnum in scalinput.index.levels[0].values: dictofdict = scalinput.loc[satnum, :].to_dict(orient="index") print(dictofdict) scalrec = PyscalFactory.create_scal_recommendation(dictofdict) print(scalrec.interpolate(-0.5).SWOF()) scalrec.interpolate(+0.5)
def interpolatetest(): """Test interpolation (sample test) in a scal recommentation""" rec = PyscalFactory.create_scal_recommendation( { "low": LOW_SAMPLE_LET, "base": BASE_SAMPLE_LET, "high": HIGH_SAMPLE_LET }, "foo", h=0.001, ) rec.add_simple_J() # Add default pc curve # print rec.low.wateroil.table interpolant = rec.interpolate(0.3, parameter2=-0.9, h=0.05) print(interpolant.wateroil.SWOF()) print(interpolant.gasoil.SGOF()) print("Consistency check: ", end=" ") print(interpolant.threephaseconsistency())
def test_comments_df(): """Test that we support a tag column in the dataframe, and that we are able to handle UTF-8 stuff nicely in py2-3 """ dframe = pd.DataFrame( columns=["SATNUM", "tag", "Nw", "Now", "ng", "nOG"], data=[[1, "thisisacomment", 2, 2, 2, 2]], ) relperm_data = PyscalFactory.load_relperm_df(dframe) p_list = PyscalFactory.create_pyscal_list(relperm_data, h=0.2) relperm_str = p_list.dump_family_1() assert "thisisacomment" in relperm_str # tag vs comment in dataframe should not matter. dframe = pd.DataFrame( columns=["SATNUM", "comment", "Nw", "Now", "ng", "nOG"], data=[[1, "thisisacomment", 2, 2, 2, 2]], ) relperm_data = PyscalFactory.load_relperm_df(dframe) p_list = PyscalFactory.create_pyscal_list(relperm_data, h=0.2) relperm_str = p_list.dump_family_1() assert relperm_str.count("thisisacomment") == 2 # Check case insensitiveness: dframe = pd.DataFrame( columns=["SAtnUM", "coMMent", "Nw", "Now", "ng", "nOG"], data=[[1, "thisisacomment", 2, 2, 2, 2]], ) relperm_data = PyscalFactory.load_relperm_df(dframe) p_list = PyscalFactory.create_pyscal_list(relperm_data, h=0.2) assert p_list.dump_family_1().count("thisisacomment") == 2 # UTF-8 stuff: dframe = pd.DataFrame( columns=["SATNUM", "TAG", "Nw", "Now", "Ng", "Nog"], data=[[1, "æøå", 2, 2, 2, 2]], ) relperm_data = PyscalFactory.load_relperm_df(dframe) p_list = PyscalFactory.create_pyscal_list(relperm_data, h=0.2) assert p_list.dump_family_1().count("æøå") == 2
def test_scalrecommendation(): """Testing making SCAL rec from dict of dict.""" pyscal_factory = PyscalFactory() scal_input = { "low": { "nw": 2, "now": 4, "ng": 1, "nog": 2 }, "BASE": { "nw": 3, "NOW": 3, "ng": 1, "nog": 2 }, "high": { "nw": 4, "now": 2, "ng": 1, "nog": 3 }, } scal = pyscal_factory.create_scal_recommendation(scal_input) # (not supported yet to make WaterOil only..) interp = scal.interpolate(-0.5) sat_table_str_ok(interp.SWOF()) sat_table_str_ok(interp.SGOF()) sat_table_str_ok(interp.SLGOF()) sat_table_str_ok(interp.SOF3()) check_table(interp.wateroil.table) check_table(interp.gasoil.table) incomplete1 = scal_input.copy() del incomplete1["BASE"] with pytest.raises(ValueError): pyscal_factory.create_scal_recommendation(incomplete1) go_only = scal_input.copy() del go_only["low"]["now"] del go_only["low"]["nw"] gasoil = pyscal_factory.create_scal_recommendation(go_only) assert gasoil.low.wateroil is None assert gasoil.base.wateroil is not None assert gasoil.high.wateroil is not None # SCALrecommendation of gasoil only works as long as you # don't try to ask for water data: assert "SGFN" in gasoil.interpolate(-0.4).SGFN() assert "SWOF" not in gasoil.interpolate(-0.2).SWOF()
def test_factory_gaswater(): """Test that we can create gas-water curves from dictionaries of parameters""" pyscal_factory = PyscalFactory() with pytest.raises(TypeError): # pylint: disable=unexpected-keyword-arg pyscal_factory.create_gas_water(swirr=0.01) # noqa gaswater = pyscal_factory.create_gas_water( dict(swirr=0.01, swl=0.03, sgrw=0.1, sgcr=0.15, tag="gassy sand", ng=2, nw=2)) assert isinstance(gaswater, GasWater) assert gaswater.swirr == 0.01 assert gaswater.swl == 0.03 assert gaswater.sgrw == 0.1 assert gaswater.sgcr == 0.15 assert gaswater.tag == "gassy sand" sgfn = gaswater.SGFN() swfn = gaswater.SWFN() sat_table_str_ok(sgfn) sat_table_str_ok(swfn) check_table(gaswater.wateroil.table) check_table(gaswater.gasoil.table) assert "sgrw=0.1" in swfn assert "swirr=0.01" in sgfn assert "swirr=0.01" in swfn assert "sgrw=0.1" in swfn assert "sgcr=0.15" in sgfn assert "nw=2" in swfn assert "ng=2" in sgfn assert "gassy sand" in sgfn gaswater = pyscal_factory.create_gas_water(dict(lg=1, eg=1, tg=1, nw=3)) sgfn = gaswater.SGFN() swfn = gaswater.SWFN() sat_table_str_ok(sgfn) sat_table_str_ok(swfn) check_table(gaswater.wateroil.table) check_table(gaswater.gasoil.table)
def test_load_scalrec(tmpdir): """Load a SATNUM range from xlsx""" testdir = Path(__file__).absolute().parent scalrec_data = PyscalFactory.load_relperm_df( testdir / "data/scal-pc-input-example.xlsx") # Also check that we can read the old excel format scalrec_data_legacy_xls = PyscalFactory.load_relperm_df( testdir / "data/scal-pc-input-example.xls") pd.testing.assert_frame_equal(scalrec_data, scalrec_data_legacy_xls) scalrec_list = PyscalFactory.create_scal_recommendation_list(scalrec_data) wog_list = scalrec_list.interpolate(-0.3) with pytest.raises((AssertionError, ValueError)): scalrec_list.interpolate(-1.1) assert wog_list.pyscaltype == WaterOilGas with pytest.raises(TypeError): scalrec_list.dump_family_1() with pytest.raises(TypeError): scalrec_list.dump_family_2() with pytest.raises(TypeError): scalrec_list.SWOF() scalrec_list.interpolate([-1, 1, 0]) with pytest.raises((AssertionError, ValueError)): scalrec_list.interpolate([-1, 0, 1.1]) # Not enough interpolation parameters with pytest.raises(ValueError): scalrec_list.interpolate([-1, 1]) with pytest.raises(ValueError): scalrec_list.interpolate([-1, 1, 0, 0]) scalrec_list.interpolate(-1, 1) scalrec_list.interpolate(-1, [0, -1, 1]) with pytest.raises(ValueError): scalrec_list.interpolate(1, [-1, 1, 0, 0]) # Test slicing the scalrec to base, this is relevant for # API usage. base_data = scalrec_data[scalrec_data["CASE"] == "base"].drop("CASE", axis=1) PyscalFactory.load_relperm_df(base_data) # Ensure no errors. pyscal_list = PyscalFactory.create_pyscal_list(base_data) assert "LET" in pyscal_list.dump_family_1()
def test_factory_gasoil(): """Test that we can create curves from dictionaries of parameters""" logging.getLogger().setLevel("INFO") factory = PyscalFactory() with pytest.raises(TypeError): # (this must be a dictionary) factory.create_gas_oil(swirr=0.01) # noqa go = factory.create_gas_oil( dict(swirr=0.01, swl=0.1, sgcr=0.05, tag="Good sand", ng=1, nog=2)) assert isinstance(go, GasOil) assert go.sgcr == 0.05 assert go.swl == 0.1 assert go.swirr == 0.01 assert go.tag == "Good sand" sgof = go.SGOF() assert "Corey krg" in sgof assert "Corey krog" in sgof assert "Zero capillary pressure" in sgof go = factory.create_gas_oil( dict(ng=1.2, nog=2, krgend=0.8, krgmax=0.9, kroend=0.6)) sgof = go.SGOF() assert "kroend=0.6" in sgof assert "krgend=0.8" in sgof go = factory.create_gas_oil(dict(ng=1.3, Log=2, Eog=2, Tog=2)) sgof = go.SGOF() assert "Corey krg" in sgof assert "LET krog" in sgof go = factory.create_gas_oil(dict(Lg=1, Eg=1, Tg=1, Log=2, Eog=2, Tog=2)) sgof = go.SGOF() assert "LET krg" in sgof assert "LET krog" in sgof
def test_boundary_cases(): """Test that interpolation is able to return the boundaries at +/- 1""" rec = PyscalFactory.create_scal_recommendation( { "low": LOW_SAMPLE_LET, "base": BASE_SAMPLE_LET, "high": HIGH_SAMPLE_LET }, "foo", h=0.1, ) # Object reference equivalence is a little bit strict, # because it would be perfectly fine if interpolate() # retured copied objects. But we don't have an equivalence operator # implemented. assert rec.interpolate(0).wateroil == rec.base.wateroil assert rec.interpolate(-1).wateroil == rec.low.wateroil assert rec.interpolate(1).wateroil == rec.high.wateroil assert rec.interpolate(0).gasoil == rec.base.gasoil assert rec.interpolate(-1).gasoil == rec.low.gasoil assert rec.interpolate(1).gasoil == rec.high.gasoil assert rec.interpolate(0, 1).wateroil == rec.base.wateroil assert rec.interpolate(-1, 1).wateroil == rec.low.wateroil assert rec.interpolate(1, 1).wateroil == rec.high.wateroil assert rec.interpolate(0, 1).gasoil == rec.high.gasoil assert rec.interpolate(-1, 1).gasoil == rec.high.gasoil assert rec.interpolate(1, 1).gasoil == rec.high.gasoil assert rec.interpolate(0, 0).gasoil == rec.base.gasoil assert rec.interpolate(-1, 0).gasoil == rec.base.gasoil assert rec.interpolate(1, 0).gasoil == rec.base.gasoil assert rec.interpolate(0, -1).gasoil == rec.low.gasoil assert rec.interpolate(-1, -1).gasoil == rec.low.gasoil assert rec.interpolate(1, -1).gasoil == rec.low.gasoil
def test_xls_factory(): """Test/demonstrate how to go from data in an excel row to pyscal objects""" if "__file__" in globals(): # Easen up copying test code into interactive sessions testdir = os.path.dirname(os.path.abspath(__file__)) else: testdir = os.path.abspath(".") xlsxfile = testdir + "/data/scal-pc-input-example.xlsx" scalinput = pd.read_excel(xlsxfile).set_index(["SATNUM", "CASE"]) for ((satnum, _), params) in scalinput.iterrows(): assert satnum wog = PyscalFactory.create_water_oil_gas(params.to_dict()) swof = wog.SWOF() assert "LET krw" in swof assert "LET krow" in swof assert "Simplified J" in swof sgof = wog.SGOF() assert "LET krg" in sgof assert "LET krog" in sgof
def test_xls_factory(): """Test/demonstrate how to go from data in an excel row to pyscal objects This test function predates the load_relperm_df() function, but can still be in here. """ testdir = Path(__file__).absolute().parent xlsxfile = testdir / "data/scal-pc-input-example.xlsx" scalinput = pd.read_excel(xlsxfile, engine="openpyxl").set_index(["SATNUM", "CASE"]) for ((satnum, _), params) in scalinput.iterrows(): assert satnum wog = PyscalFactory.create_water_oil_gas(params.to_dict()) swof = wog.SWOF() assert "LET krw" in swof assert "LET krow" in swof assert "Simplified J" in swof sgof = wog.SGOF() sat_table_str_ok(sgof) assert "LET krg" in sgof assert "LET krog" in sgof
def test_interpolation(param_wo, param_go): """Test interpolation with random interpolation parameters, looking for numerical corner cases""" rec = PyscalFactory.create_scal_recommendation( { "low": LOW_SAMPLE_LET, "base": BASE_SAMPLE_LET, "high": HIGH_SAMPLE_LET }, "foo", h=0.1, ) rec.add_simple_J() # Add default pc curve # Check that added pc curve is non-zero assert sum(rec.low.wateroil.table["PC"]) assert sum(rec.base.wateroil.table["PC"]) assert sum(rec.high.wateroil.table["PC"]) try: interpolant = rec.interpolate(param_wo, param_go, h=0.1) except ValueError: return check_table(interpolant.wateroil.table) check_table(interpolant.gasoil.table) assert len(interpolant.gasoil.SGOF()) > 100 assert len(interpolant.gasoil.SGFN()) > 100 assert len(interpolant.wateroil.SWFN()) > 100 assert len(interpolant.SOF3()) > 100 assert len(interpolant.wateroil.SWOF()) > 100 assert interpolant.threephaseconsistency() assert sum(interpolant.wateroil.table["PC"])
def test_scalrecommendation(): """Testing making SCAL rec from dict of dict""" factory = PyscalFactory() scal_input = { "low": { "nw": 2, "now": 4, "ng": 1, "nog": 2 }, "BASE": { "nw": 3, "NOW": 3, "ng": 1, "nog": 2 }, "high": { "nw": 4, "now": 2, "ng": 1, "nog": 3 }, } scal = factory.create_scal_recommendation(scal_input) # (not supported yet to make WaterOil only..) scal.interpolate(-0.5).SWOF() incomplete1 = scal_input.copy() del incomplete1["BASE"] with pytest.raises(ValueError): factory.create_scal_recommendation(incomplete1) incomplete2 = scal_input.copy() del incomplete2["low"]["now"] with pytest.raises(ValueError): factory.create_scal_recommendation(incomplete2)
def test_load_relperm_df(tmpdir): """Test loading of dataframes with validation from excel or from csv""" testdir = Path(__file__).absolute().parent scalfile_xls = testdir / "data/scal-pc-input-example.xlsx" scaldata = PyscalFactory.load_relperm_df(scalfile_xls) with pytest.raises(IOError): PyscalFactory.load_relperm_df("not-existing-file") assert "SATNUM" in scaldata assert "CASE" in scaldata assert not scaldata.empty tmpdir.chdir() scaldata.to_csv("scal-input.csv") scaldata_fromcsv = PyscalFactory.load_relperm_df("scal-input.csv") assert "CASE" in scaldata_fromcsv assert not scaldata_fromcsv.empty scaldata_fromdf = PyscalFactory.load_relperm_df(scaldata_fromcsv) assert "CASE" in scaldata_fromdf assert "SATNUM" in scaldata_fromdf assert len(scaldata_fromdf) == len(scaldata_fromcsv) == len(scaldata) # Perturb the dataframe, this should trigger errors with pytest.raises(ValueError): PyscalFactory.load_relperm_df(scaldata.drop("SATNUM", axis="columns")) wrongsatnums = scaldata.copy() wrongsatnums["SATNUM"] = wrongsatnums["SATNUM"] * 2 with pytest.raises(ValueError): PyscalFactory.load_relperm_df(wrongsatnums) wrongsatnums = scaldata.copy() wrongsatnums["SATNUM"] = wrongsatnums["SATNUM"].astype(int) wrongsatnums = wrongsatnums[wrongsatnums["SATNUM"] > 2] with pytest.raises(ValueError): PyscalFactory.load_relperm_df(wrongsatnums) wrongcases = scaldata.copy() wrongcases["CASE"] = wrongcases["CASE"] + "ffooo" with pytest.raises(ValueError): PyscalFactory.load_relperm_df(wrongcases) with pytest.raises(ValueError): PyscalFactory.load_relperm_df(scaldata.drop(["Lw", "Lg"], axis="columns")) # Insert a NaN, this replicates what happens if cells are merged mergedcase = scaldata.copy() mergedcase.loc[3, "SATNUM"] = np.nan with pytest.raises(ValueError): PyscalFactory.load_relperm_df(mergedcase) relpermfile_xls = testdir / "data/relperm-input-example.xlsx" relpermdata = PyscalFactory.load_relperm_df(relpermfile_xls) assert "TAG" in relpermdata assert "SATNUM" in relpermdata assert "satnum" not in relpermdata # always converted to upper-case assert len(relpermdata) == 3 swof_str = PyscalFactory.create_pyscal_list(relpermdata, h=0.2).SWOF() assert "Åre 1.8" in swof_str assert "SATNUM 2" in swof_str # Autogenerated in SWOF, generated by factory assert "SATNUM 3" in swof_str assert "foobar" in swof_str # Random string injected in xlsx. # Make a dummy text file with open("dummy.txt", "w") as fhandle: fhandle.write("foo\nbar, com") with pytest.raises(ValueError): PyscalFactory.load_relperm_df("dummy.txt")
def test_factory_wateroil(): """Test that we can create curves from dictionaries of parameters""" pyscal_factory = PyscalFactory() # Factory refuses to create incomplete defaulted objects. with pytest.raises(ValueError): pyscal_factory.create_water_oil() with pytest.raises(TypeError): # (it must be a dictionary) # pylint: disable=unexpected-keyword-arg pyscal_factory.create_water_oil(swirr=0.01) # noqa wateroil = pyscal_factory.create_water_oil( dict( swirr=0.01, swl=0.1, bogus="foobar", tag="Good sand", nw=3, now=2, krwend=0.2, krwmax=0.5, ) ) assert isinstance(wateroil, WaterOil) assert wateroil.swirr == 0.01 assert wateroil.swl == 0.1 assert wateroil.tag == "Good sand" assert "krw" in wateroil.table assert "Corey" in wateroil.krwcomment assert wateroil.table["krw"].max() == 0.2 # Because sorw==0 by default check_table(wateroil.table) sat_table_str_ok(wateroil.SWOF()) sat_table_str_ok(wateroil.SWFN()) wateroil = pyscal_factory.create_water_oil( dict(nw=3, now=2, sorw=0.1, krwend=0.2, krwmax=0.5) ) assert isinstance(wateroil, WaterOil) assert "krw" in wateroil.table assert "Corey" in wateroil.krwcomment assert wateroil.table["krw"].max() == 0.5 check_table(wateroil.table) sat_table_str_ok(wateroil.SWOF()) sat_table_str_ok(wateroil.SWFN()) # Ambiguous works, but we don't guarantee that this results # in LET or Corey. wateroil = pyscal_factory.create_water_oil(dict(nw=3, Lw=2, Ew=2, Tw=2, now=3)) assert "krw" in wateroil.table assert "Corey" in wateroil.krwcomment or "LET" in wateroil.krwcomment check_table(wateroil.table) sat_table_str_ok(wateroil.SWOF()) sat_table_str_ok(wateroil.SWFN()) # Mixing Corey and LET wateroil = pyscal_factory.create_water_oil(dict(Lw=2, Ew=2, Tw=2, krwend=1, now=4)) assert isinstance(wateroil, WaterOil) assert "krw" in wateroil.table assert wateroil.table["krw"].max() == 1.0 assert "LET" in wateroil.krwcomment check_table(wateroil.table) sat_table_str_ok(wateroil.SWOF()) sat_table_str_ok(wateroil.SWFN()) wateroil = pyscal_factory.create_water_oil( dict(Lw=2, Ew=2, Tw=2, Low=3, Eow=3, Tow=3, krwend=0.5) ) assert isinstance(wateroil, WaterOil) assert "krw" in wateroil.table assert "krow" in wateroil.table assert wateroil.table["krw"].max() == 0.5 assert wateroil.table["krow"].max() == 1 assert "LET" in wateroil.krwcomment assert "LET" in wateroil.krowcomment check_table(wateroil.table) sat_table_str_ok(wateroil.SWOF()) sat_table_str_ok(wateroil.SWFN()) # Add capillary pressure wateroil = pyscal_factory.create_water_oil( dict(swl=0.1, nw=1, now=1, a=2, b=-1, poro_ref=0.2, perm_ref=100, drho=200) ) assert "pc" in wateroil.table assert wateroil.table["pc"].max() > 0.0 assert "Simplified J" in wateroil.pccomment check_table(wateroil.table) sat_table_str_ok(wateroil.SWOF()) sat_table_str_ok(wateroil.SWFN()) # Test that the optional gravity g is picked up: wateroil = pyscal_factory.create_water_oil( dict(swl=0.1, nw=1, now=1, a=2, b=-1, poro_ref=0.2, perm_ref=100, drho=200, g=0) ) assert "pc" in wateroil.table assert wateroil.table["pc"].max() == 0.0 check_table(wateroil.table) sat_table_str_ok(wateroil.SWOF()) sat_table_str_ok(wateroil.SWFN()) # Test petrophysical simple J: wateroil = pyscal_factory.create_water_oil( dict( swl=0.1, nw=1, now=1, a_petro=2, b_petro=-1, poro_ref=0.2, perm_ref=100, drho=200, ) ) assert "pc" in wateroil.table assert wateroil.table["pc"].max() > 0.0 assert "etrophysic" in wateroil.pccomment check_table(wateroil.table) sat_table_str_ok(wateroil.SWOF()) sat_table_str_ok(wateroil.SWFN()) # One pc param missing: wateroil = pyscal_factory.create_water_oil( dict(swl=0.1, nw=1, now=1, a=2, b=-1, perm_ref=100, drho=200, g=0) ) assert "pc" not in wateroil.table
def test_init_with_swlheight(): """With sufficient parameters, swl will be calculated on the fly when initializing the WaterOil object""" pyscal_factory = PyscalFactory() wateroil = pyscal_factory.create_water_oil( dict( swlheight=200, nw=1, now=1, swirr=0.01, a=1, b=-2, poro_ref=0.2, perm_ref=100, drho=200, ) ) assert np.isclose(wateroil.swl, 0.02480395) assert "swl=0.024" in wateroil.SWOF() with pytest.raises( ValueError, match="Can't initialize from SWLHEIGHT without sufficient simple-J parameters", ): # This should fail because capillary pressure parameters are not provided. pyscal_factory.create_water_oil(dict(swlheight=200, nw=1, now=1)) # swcr must be larger than swl: with pytest.raises(ValueError, match="lower than computed swl"): pyscal_factory.create_water_oil( dict( swlheight=200, nw=1, now=1, swirr=0.01, swcr=0.0101, a=1, b=-2, poro_ref=0.2, perm_ref=100, drho=200, ) ) # If swcr is large enough, it will pass: wateroil = pyscal_factory.create_water_oil( dict( swlheight=200, nw=1, now=1, swirr=0.01, swcr=0.3, a=1, b=-2, poro_ref=0.2, perm_ref=100, drho=200, ) ) assert wateroil.swcr > wateroil.swl assert wateroil.swcr == 0.3 assert "swcr=0.3" in wateroil.SWOF() # Test that GasWater also can be initialized with swlheight: gaswater = pyscal_factory.create_gas_water( dict( swlheight=200, nw=1, ng=1, swirr=0.01, swcr=0.3, a=1, b=-2, poro_ref=0.2, perm_ref=100, drho=200, ) ) assert "swl=0.024" in gaswater.SWFN() assert gaswater.swcr > gaswater.swl assert gaswater.swcr == 0.3 assert "swcr=0.3" in gaswater.SWFN()
def pyscal_main( parametertable, verbose=False, output="relperm.inc", delta_s=None, int_param_wo=None, int_param_go=None, sheet_name=None, slgof=False, family2=False, ): """A "main()" method not relying on argparse. This can be used for testing, and also by an ERT forward model, e.g. in semeio (github.com/equinor/semeio) Args: parametertable (string): Filename (CSV or XLSX) to load verbose (bool): verbose or not output (string): Output filename delta_s (float): Saturation step-length int_param_wo (list): Interpolation params for wateroil int_param_go (list): Interpolation params for gasoil sheet_name (string): Which sheet in XLSX file slgof (bool): Use SLGOF family2 (bool): Dump family 2 keywords """ if verbose: # Fixme: Logging level is not inherited in called modules. logger.setLevel(logging.INFO) if sheet_name: logger.info("Loading data from %s and sheetname %s", parametertable, sheet_name) else: logger.info("Loading data from %s", parametertable) scalinput_df = PyscalFactory.load_relperm_df(parametertable, sheet_name=sheet_name) logger.info("Input data:\n%s", scalinput_df.to_string(index=False)) if int_param_go is not None and int_param_wo is None: logger.error("Don't use int_param_go alone, only int_param_wo") raise ValueError if "SATNUM" not in scalinput_df: logger.error("There is no column called SATNUM in the input data") raise ValueError if "CASE" in scalinput_df: # Then we should do interpolation if int_param_wo is None: logger.error("No interpolation parameters provided") raise ValueError scalrec_list = PyscalFactory.create_scal_recommendation_list( scalinput_df, h=delta_s) logger.info( "Interpolating, wateroil=%s, gasoil=%s", str(int_param_wo), str(int_param_go), ) wog_list = scalrec_list.interpolate(int_param_wo, int_param_go, h=delta_s) else: wog_list = PyscalFactory.create_pyscal_list( scalinput_df, h=delta_s) # can be both water-oil, water-oil-gas, or gas-water if (int_param_wo is not None or int_param_go is not None) and "CASE" not in scalinput_df: logger.error( "Interpolation parameter provided but no CASE column in input data" ) raise ValueError if not family2: logger.info("Generating family 1 keywords.") if output == "-": print(wog_list.dump_family_1(slgof=slgof)) else: wog_list.dump_family_1(filename=output, slgof=slgof) print("Written to " + output) else: logger.info("Generating family 2 keywords") if output == "-": print(wog_list.dump_family_2()) else: wog_list.dump_family_2(filename=output) print("Written to " + output)
def test_error_messages_pr_satnum(caplog): """When errors are somewhere in a big dataframe, we should provide hints to the user for where to look""" # A string instead of a number in SATNUM 2 dframe = pd.DataFrame(columns=["SATNUM", "nw", "now"], data=[[1, 1, 1], [2, "foo", 1]]) with pytest.raises(ValueError, match="SATNUM 2"): PyscalFactory.create_pyscal_list(dframe, h=0.2) dframe = pd.DataFrame(columns=["SATNUM", "nw", "now"], data=[[1, 1, 1], [2, np.nan, 1]]) with pytest.raises(ValueError, match="SATNUM 2"): PyscalFactory.create_pyscal_list(dframe, h=0.2) # Mixed up order: dframe = pd.DataFrame(columns=["SATNUM", "nw", "now"], data=[[2, np.nan, 1], [1, 1, 1]]) with pytest.raises(ValueError, match="SATNUM 2"): PyscalFactory.create_pyscal_list(dframe, h=0.2) # Gasoil list dframe = pd.DataFrame(columns=["SATNUM", "ng", "nog"], data=[[1, 1, 1], [2, np.nan, 1]]) with pytest.raises(ValueError, match="SATNUM 2"): PyscalFactory.create_pyscal_list(dframe, h=0.2) # Gaswater list: dframe = pd.DataFrame(columns=["SATNUM", "ng", "nw"], data=[[1, 1, 1], [2, np.nan, 1]]) with pytest.raises(ValueError, match="SATNUM 2"): PyscalFactory.create_pyscal_list(dframe, h=0.2) # Wateroilgas list: dframe = pd.DataFrame( columns=["SATNUM", "nw", "now", "ng", "nog"], data=[[1, 1, 1, 1, 1], [2, np.nan, 1, 2, 3]], ) with pytest.raises(ValueError, match="SATNUM 2"): PyscalFactory.create_pyscal_list(dframe, h=0.2) # SCAL rec list: dframe = pd.DataFrame( columns=["SATNUM", "CASE", "nw", "now", "ng", "nog"], data=[ [1, "low", 1, 1, 1, 1], [1, "base", 1, 1, 1, 1], [1, "high", 1, 1, 1, 1], [2, "low", np.nan, 1, 2, 3], [2, "base", 2, 1, 2, 3], [2, "high", 3, 1, 2, 3], ], ) # The error should hint both to SATNUM and to low/base/high with pytest.raises(ValueError, match="SATNUM 2"): PyscalFactory.create_scal_recommendation_list(dframe, h=0.2) with pytest.raises(ValueError, match="Problem with low"): PyscalFactory.create_scal_recommendation_list(dframe, h=0.2)
def test_explicit_df(): """Test some dataframes, check the error messages given""" dframe = pd.DataFrame(columns=["satnum"], data=[[1], [2]]) with pytest.raises(ValueError): # SATNUM column must be upper case, or should we allow lowercase?? relperm_data = PyscalFactory.load_relperm_df(dframe) dframe = pd.DataFrame(columns=["SATNUM"], data=[[0], [1]]) with pytest.raises(ValueError): # SATNUM must start at 1. relperm_data = PyscalFactory.load_relperm_df(dframe) dframe = pd.DataFrame(columns=["SATNUM"], data=[[1], ["foo"]]) with pytest.raises(ValueError): # SATNUM must contain only integers relperm_data = PyscalFactory.load_relperm_df(dframe) dframe = pd.DataFrame(columns=["SATNUM", "nw", "now"], data=[[1.01, 1, 1], [2.01, 1, 1]]) # This one will pass, as these can be converted to ints relperm_data = PyscalFactory.load_relperm_df(dframe) dframe = pd.DataFrame(columns=["SATNUM", "nw", "now"], data=[[1.01, 1, 1], [1.3, 1, 1]]) with pytest.raises(ValueError): # complains about non-uniqueness in SATNUM relperm_data = PyscalFactory.load_relperm_df(dframe) dframe = pd.DataFrame(columns=["SATNUM"], data=[[1], [2]]) with pytest.raises(ValueError): # Not enough data relperm_data = PyscalFactory.load_relperm_df(dframe) # Minimal amount of data: dframe = pd.DataFrame(columns=["SATNUM", "Nw", "Now"], data=[[1, 2, 2]]) relperm_data = PyscalFactory.load_relperm_df(dframe) p_list = PyscalFactory.create_pyscal_list(relperm_data, h=0.2) p_list.dump_family_1() with pytest.raises(ValueError): p_list.dump_family_2() # Case insensitive for parameters dframe = pd.DataFrame(columns=["SATNUM", "nw", "now"], data=[[1, 2, 2]]) relperm_data = PyscalFactory.load_relperm_df(dframe) p_list = PyscalFactory.create_pyscal_list(relperm_data, h=0.2) p_list.dump_family_1() # Minimal wateroilgas dframe = pd.DataFrame(columns=["SATNUM", "Nw", "Now", "ng", "nOG"], data=[[1, 2, 2, 2, 2]]) relperm_data = PyscalFactory.load_relperm_df(dframe) p_list = PyscalFactory.create_pyscal_list(relperm_data, h=0.2) relperm_str = p_list.dump_family_1() assert "SWOF" in relperm_str assert "SGOF" in relperm_str assert "SLGOF" not in relperm_str assert "SOF3" not in relperm_str # Minimal wateroilgas with pc dframe = pd.DataFrame( columns=[ "SATNUM", "swl", "Nw", "Now", "ng", "nOG", "a", "b", "poro_ref", "perm_ref", "drho", ], data=[[1, 0.1, 2, 2, 2, 2, 1.5, -0.5, 0.1, 100, 300]], ) relperm_data = PyscalFactory.load_relperm_df(dframe) p_list = PyscalFactory.create_pyscal_list(relperm_data, h=0.2) relperm_str = p_list.dump_family_1() assert "SWOF" in relperm_str assert "Simplified" in relperm_str # Bad practice, testing for stuff in comments assert "a=1.5" in relperm_str
def test_capillary_pressure(): """Test that we recognize capillary pressure parametrizations""" dframe = pd.DataFrame( columns=[ "SATNUM", "nw", "now", "swl", "a", "b", "PORO_reF", "PERM_ref", "drho", ], data=[[1, 1, 1, 0.05, 3.6, -3.5, 0.25, 15, 150]], ) pyscal_list = PyscalFactory.create_pyscal_list( PyscalFactory.load_relperm_df(dframe)) swof = pyscal_list.dump_family_1() assert "Simplified J-function" in swof assert "petrophysical" not in swof dframe = pd.DataFrame( columns=[ "SATNUM", "nw", "now", "swl", "a_petro", "b_petro", "PORO_reF", "PERM_ref", "drho", ], data=[[1, 1, 1, 0.05, 3.6, -3.5, 0.25, 15, 150]], ) pyscal_list = PyscalFactory.create_pyscal_list( PyscalFactory.load_relperm_df(dframe)) swof = pyscal_list.dump_family_1() assert "Simplified J-function" in swof assert "petrophysical" in swof dframe = pd.DataFrame( columns=[ "SATNUM", "nw", "now", "swl", "a", "b", "PORO", "PERM", "sigma_COStau", ], data=[[1, 1, 1, 0.05, 3.6, -3.5, 0.25, 15, 30]], ) pyscal_list = PyscalFactory.create_pyscal_list( PyscalFactory.load_relperm_df(dframe)) swof = pyscal_list.dump_family_1() assert "normalized J-function" in swof assert "sigma_costau" in swof assert "petrophysical" not in swof
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() columns = [ "SATNUM", "Nw", "Now", "Ng", "Nog", "swl", "a", "b", "poro_ref", "perm_ref", "drho", ] dframe_pess = pd.DataFrame( columns=columns, data=[[1, 1, 1, 1, 1, 0.1, 2, -2, 0.25, 100, 150]], ) dframe_base = pd.DataFrame( columns=columns, data=[[1, 2, 2, 2, 2, 0.1, 2, -2, 0.25, 200, 150]], ) dframe_opt = pd.DataFrame( columns=columns, data=[[1, 3, 3, 3, 3, 0.1, 2, -2, 0.25, 300, 150]], ) PyscalFactory.create_pyscal_list(dframe_pess).dump_family_1("pess.inc") PyscalFactory.create_pyscal_list(dframe_base).dump_family_1("base.inc") PyscalFactory.create_pyscal_list(dframe_opt).dump_family_1("opt.inc") config = { "base": ["base.inc"], "low": ["pess.inc"], "high": ["opt.inc"], "result_file": "outfile.inc", "interpolations": [{ "param_w": -0.5, "param_g": 0.5 }], "delta_s": 0.1, } interp_relperm.process_config(config) interp_relperm.process_config(config) outfile_df = satfunc.df(open("outfile.inc").read(), ntsfun=1) assert set(outfile_df["KEYWORD"].unique()) == {"SWOF", "SGOF"} assert outfile_df["SW"].sum() > 0 assert outfile_df["SG"].sum() > 0 assert outfile_df["KRW"].sum() > 0 assert outfile_df["KROW"].sum() > 0 assert outfile_df["KRG"].sum() > 0 assert outfile_df["KROG"].sum() > 0 assert outfile_df["PCOW"].sum() > 0
def test_fast(): """Test fast mode for SCALrecommendation""" testdir = Path(__file__).absolute().parent scalrec_data = PyscalFactory.load_relperm_df( testdir / "data/scal-pc-input-example.xlsx") scalrec_list_fast = PyscalFactory.create_scal_recommendation_list( scalrec_data, fast=True) for item in scalrec_list_fast: assert item.fast assert item.low.fast assert item.base.fast assert item.high.fast wog_list_fast = scalrec_list_fast.interpolate(-0.5) for item in wog_list_fast: assert item.fast # WaterOilGas list dframe = pd.DataFrame( columns=["SATNUM", "nw", "now", "ng", "nog"], data=[ [1, 2, 2, 2, 2], [2, 2, 2, 2, 2], [3, 2, 2, 2, 2], ], ) relperm_data = PyscalFactory.load_relperm_df(dframe) p_list_fast = PyscalFactory.create_pyscal_list(relperm_data, h=0.2, fast=True) for item in p_list_fast: assert item.fast # GasOil list input_dframe = dframe[["SATNUM", "ng", "nog"]].copy() relperm_data = PyscalFactory.load_relperm_df(input_dframe) p_list_fast = PyscalFactory.create_pyscal_list(relperm_data, h=0.2, fast=True) for item in p_list_fast: assert item.fast # WaterOil list input_dframe = dframe[["SATNUM", "nw", "now"]].copy() relperm_data = PyscalFactory.load_relperm_df(input_dframe) p_list_fast = PyscalFactory.create_pyscal_list(relperm_data, h=0.2, fast=True) for item in p_list_fast: assert item.fast # GasWater list input_dframe = dframe[["SATNUM", "nw", "ng"]].copy() relperm_data = PyscalFactory.load_relperm_df(input_dframe) p_list_fast = PyscalFactory.create_pyscal_list(relperm_data, h=0.2, fast=True) for item in p_list_fast: assert item.fast # Testing with "fast" column in dataframe # Currently the choice is to only implement fast mode # as a global option. This column should do nothing now. # One could imagine it implemented # for individual SATNUM regions at a later stage dframe = pd.DataFrame( columns=["SATNUM", "nw", "now", "ng", "nog", "fast"], data=[ [1, 2, 2, 2, 2, True], [2, 2, 2, 2, 2, False], [3, 2, 2, 2, 2, True], ], ) relperm_data = PyscalFactory.load_relperm_df(dframe) assert "fast" in relperm_data p_list = PyscalFactory.create_pyscal_list(relperm_data, h=0.2) for item in p_list: assert not item.fast
def test_mock_two_satnums(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. """ # pylint: disable=no-value-for-parameter tmpdir.chdir() columns = [ "SATNUM", "Nw", "Now", "Ng", "Nog", "swl", "a", "b", "poro_ref", "perm_ref", "drho", ] dframe_pess = pd.DataFrame( columns=columns, data=[ [1, 1, 1, 1, 1, 0.1, 2, -2, 0.25, 100, 150], [1, 1, 1, 1, 1, 0.1, 2, -2, 0.25, 100, 150], ], ) dframe_base = pd.DataFrame( columns=columns, data=[ [1, 2, 2, 2, 2, 0.1, 2, -2, 0.25, 200, 150], [1, 2, 2, 2, 2, 0.1, 2, -2, 0.25, 200, 150], ], ) dframe_opt = pd.DataFrame( columns=columns, data=[ [1, 3, 3, 3, 3, 0.1, 2, -2, 0.25, 300, 150], [1, 3, 3, 3, 3, 0.1, 2, -2, 0.25, 300, 150], ], ) PyscalFactory.create_pyscal_list(dframe_pess).dump_family_1("pess.inc") PyscalFactory.create_pyscal_list(dframe_base).dump_family_1("base.inc") PyscalFactory.create_pyscal_list(dframe_opt).dump_family_1("opt.inc") config = { "base": ["base.inc"], "low": ["pess.inc"], "high": ["opt.inc"], "result_file": "outfile.inc", "interpolations": [{ "param_w": -0.5, "param_g": 0.5 }], "delta_s": 0.1, } interp_relperm.process_config(config) outfile_str = open("outfile.inc").read() # Assert things about the comments emitted by pyscal when interpolating: # This is used as a proxy for asserting that interpolation parameters # are used for the correct satnums assert outfile_str.find("SCAL recommendation interpolation to 0.5") assert outfile_str.find("SCAL recommendation interpolation to -0.5") # SWOF comes before SGOF: assert outfile_str.find("to -0.5") < outfile_str.find("to 0.5") outfile_df = satfunc.df(outfile_str, ntsfun=2) assert set(outfile_df["KEYWORD"].unique()) == {"SWOF", "SGOF"} assert set(outfile_df["SATNUM"].unique()) == {1, 2} config = { "base": ["base.inc"], "low": ["pess.inc"], "high": ["opt.inc"], "result_file": "outfile.inc", "interpolations": [ { "tables": [1], "param_w": -0.9, "param_g": -0.5 }, { "tables": [2], "param_w": 0.5, "param_g": 0.8 }, ], "delta_s": 0.1, } interp_relperm.process_config(config) outfile_str = open("outfile.inc").read() assert outfile_str.find("to -0.9") < outfile_str.find("to 0.5") assert outfile_str.find("to 0.5") < outfile_str.find("to -0.5") assert outfile_str.find("to 0.5") < outfile_str.find("to 0.8") config = { "base": ["base.inc"], "low": ["pess.inc"], "high": ["opt.inc"], "result_file": "outfile.inc", "interpolations": [ # This is a user error, the latter will override the first { "param_w": -0.9, "param_g": -0.5 }, { "param_w": 0.5, "param_g": 0.8 }, ], "delta_s": 0.1, } interp_relperm.process_config(config) outfile_str = open("outfile.inc").read() assert "interpolation to -0.9" not in outfile_str assert "interpolation to 0.8" in outfile_str config = { "base": ["base.inc"], "low": ["pess.inc"], "high": ["opt.inc"], "result_file": "outfile.inc", "interpolations": [ # Here the user intentionally overwrites the first: { "param_w": -0.9, "param_g": -0.5 }, { "tables": [], "param_w": 0.5, "param_g": 0.8 }, ], "delta_s": 0.1, } interp_relperm.process_config(config) outfile_str = open("outfile.inc").read() assert "interpolation to -0.9" not in outfile_str assert "interpolation to 0.8" in outfile_str
def test_case_aliasing(): """Test that we can use aliases for the CASE column in SCAL recommendations""" dframe = pd.DataFrame( columns=["SATNUM", "CASE", "Nw", "Now", "Ng", "Nog"], data=[ [1, "pess", 2, 2, 1, 1], [1, "base", 3, 1, 1, 1], [1, "opt", 3, 1, 1, 1], ], ) relperm_data = PyscalFactory.load_relperm_df(dframe) PyscalFactory.create_scal_recommendation_list(relperm_data, h=0.2).interpolate(-0.4) dframe = pd.DataFrame( columns=["SATNUM", "CASE", "Nw", "Now", "Ng", "Nog"], data=[ [1, "pessimistic", 2, 2, 1, 1], [1, "base", 3, 1, 1, 1], [1, "optiMISTIc", 3, 1, 1, 1], ], ) relperm_data = PyscalFactory.load_relperm_df(dframe) PyscalFactory.create_scal_recommendation_list(relperm_data, h=0.2).interpolate(-0.4) with pytest.raises(ValueError): PyscalFactory.load_relperm_df( pd.DataFrame( columns=["SATNUM", "CASE", "Nw", "Now", "Ng", "Nog"], data=[ [1, "FOOBAR", 2, 2, 1, 1], [1, "base", 3, 1, 1, 1], [1, "optIMIstiC", 3, 1, 1, 1], ], ) ) # Ambigous data: with pytest.raises(ValueError): amb = PyscalFactory.load_relperm_df( pd.DataFrame( columns=["SATNUM", "CASE", "Nw", "Now", "Ng", "Nog"], data=[ [1, "low", 2, 2, 1, 1], [1, "pess", 5, 5, 5, 5], [1, "base", 3, 1, 1, 1], [1, "optIMIstiC", 3, 1, 1, 1], ], ) ) PyscalFactory.create_scal_recommendation_list(amb) # Missing a case with pytest.raises(ValueError): PyscalFactory.load_relperm_df( pd.DataFrame( columns=["SATNUM", "CASE", "Nw", "Now", "Ng", "Nog"], data=[[1, "base", 3, 1, 1, 1], [1, "optIMIstiC", 3, 1, 1, 1]], ) ) # Missing a case with pytest.raises(ValueError): PyscalFactory.load_relperm_df( pd.DataFrame( columns=["SATNUM", "CASE", "Nw", "Now", "Ng", "Nog"], data=[[1, "base", 3, 1, 1, 1]], ) )
def test_load_scalrec_tags(): """Test tag handling for a SCAL recommendation with SATNUM range""" if "__file__" in globals(): # Easen up copying test code into interactive sessions testdir = os.path.dirname(os.path.abspath(__file__)) else: testdir = os.path.abspath(".") scalrec_data = PyscalFactory.load_relperm_df( testdir + "/data/scal-pc-input-example.xlsx" ) scalrec_list = PyscalFactory.create_scal_recommendation_list(scalrec_data) wog_list = scalrec_list.interpolate(-1) swof = wog_list.SWOF() assert swof.count("SCAL recommendation interpolation to -1") == 3 assert swof.count("SATNUM 1") == 1 assert swof.count("SATNUM") == 3 sof3 = wog_list.SOF3() assert sof3.count("SCAL recommendation interpolation to -1") == 3 assert sof3.count("SATNUM 1") == 1 assert sof3.count("SATNUM") == 3 assert ( scalrec_list.interpolate(1) .SWOF() .count("SCAL recommendation interpolation to 1\n") == 3 ) assert ( scalrec_list.interpolate(0) .SWOF() .count("SCAL recommendation interpolation to 0\n") == 3 ) assert ( scalrec_list.interpolate(-0.444) .SWOF() .count("SCAL recommendation interpolation to -0.444\n") == 3 ) # Give each row in the SCAL recommendation its own tag (low, base, high # explicitly in each tag, someone will do that) scalrec_data["TAG"] = [ "SAT1 low", "SAT1 base", "SAT1 high", "SAT2 pess", "SAT2 base", "SAT2 opt", "SAT3 pessimistic", "SAT3 base case", "SAT3 optimistic", ] scalrec_list2 = PyscalFactory.create_scal_recommendation_list(scalrec_data) swof = scalrec_list2.interpolate(-0.5, h=0.2).SWOF() assert swof.count("SCAL recommendation") == 3 for tag in scalrec_data["TAG"]: assert swof.count(tag) == 1
def test_swl_from_height(): """Test that can initialize swl from capillary pressure height""" df_columns = [ "SATNUM", "nw", "now", "swl", "swlheight", "swirr", "a", "b", "PORO_reF", "PERM_ref", "drho", ] dframe = pd.DataFrame( columns=df_columns, data=[[1, 1, 1, np.nan, 300, 0.00, 3.6, -3.5, 0.25, 15, 150]], ) pyscal_list = PyscalFactory.create_pyscal_list( PyscalFactory.load_relperm_df(dframe)) # Mix swlheight init and direct swl-init: assert np.isclose(pyscal_list[1].swl, 0.157461) dframe = pd.DataFrame( columns=df_columns, data=[ [1, 1, 1, np.nan, 300, 0.00, 3.6, -3.5, 0.25, 15, 150], [2, 1, 1, 0.3, np.nan, 0.00, 3.6, -3.5, 0.25, 15, 150], ], ) pyscal_list = PyscalFactory.create_pyscal_list( PyscalFactory.load_relperm_df(dframe)) assert np.isclose(pyscal_list[1].swl, 0.157461) assert np.isclose(pyscal_list[2].swl, 0.3) # Ambiguous, swl and swlheight both supplied: dframe = pd.DataFrame( columns=df_columns, data=[[1, 1, 1, 0.3, 300, 0.00, 3.6, -3.5, 0.25, 15, 150]], ) with pytest.raises(ValueError): PyscalFactory.create_pyscal_list(PyscalFactory.load_relperm_df(dframe)) # WaterOilGas (gasoil is also dependant on the computed swl) df_wog_columns = [ "SATNUM", "nw", "now", "ng", "nog", "swlheight", "swirr", "a", "b", "PORO_reF", "PERM_ref", "drho", ] dframe = pd.DataFrame( columns=df_wog_columns, data=[[1, 1, 1, 2, 2, 300, 0.00, 3.6, -3.5, 0.25, 15, 150]], ) pyscal_list = PyscalFactory.create_pyscal_list( PyscalFactory.load_relperm_df(dframe)) assert np.isclose(pyscal_list[1].wateroil.swl, 0.157461) assert np.isclose(pyscal_list[1].gasoil.swl, 0.157461) # Test for GasWater: df_gw_columns = [ "SATNUM", "nw", "ng", "swlheight", "swirr", "a", "b", "PORO_REF", "PERM_REF", "drho", ] dframe = pd.DataFrame( columns=df_gw_columns, data=[[1, 2, 3, 300, 0.00, 3.6, -3.5, 0.25, 15, 150]], ) pyscal_list = PyscalFactory.create_pyscal_list( PyscalFactory.load_relperm_df(dframe)) assert np.isclose(pyscal_list[1].wateroil.swl, 0.157461) assert np.isclose(pyscal_list[1].swl, 0.157461)
def test_SCAL_interpolation(): """Demonstration of interpolation between LET curves, 2x2 subplot""" matplotlib.style.use("ggplot") rec = PyscalFactory.create_scal_recommendation( { "low": LOW_SAMPLE_LET, "base": BASE_SAMPLE_LET, "high": HIGH_SAMPLE_LET }, "FOO", h=0.001, ) _, ((ax1, ax2), (ax3, ax4)) = pyplot.subplots(2, 2) # Choosing logarithmic spaced interpolation parameters # is not the same as interpolating in log(kr)-space # check the effect by setting # for t in -2 + np.logspace(1e-5,1e-1,15): # and # for t in -1 + np.logspace(1e-5,1e-1,15) # in the loops below. Curves get clustered to the bottom # in both linear and log(kr) spaces, but there # still might be some other distribution for the interpolants # that yields something that spans nicely both the linear and the # logarithmic kr space (?) for tparam in np.arange(-1, 0, 0.2): interp = rec.interpolate(tparam, h=0.001) interp.wateroil.plotkrwkrow(ax1, "r") interp.wateroil.plotkrwkrow(ax2, "r") for tparam in np.arange(0, 1, 0.2): interp = rec.interpolate(tparam, h=0.001) interp.wateroil.plotkrwkrow(ax1, "g") interp.wateroil.plotkrwkrow(ax2, "g") rec.low.wateroil.plotkrwkrow(ax1, linewidth=2, linestyle=":") rec.base.wateroil.plotkrwkrow(ax1, linewidth=2) rec.high.wateroil.plotkrwkrow(ax1, linewidth=2, linestyle="--") rec.low.wateroil.plotkrwkrow(ax2, linewidth=2, linestyle=":") rec.base.wateroil.plotkrwkrow(ax2, linewidth=2) rec.high.wateroil.plotkrwkrow(ax2, linewidth=2, linestyle="--") ax2.set_yscale("log") ax2.set_ylim([1e-10, 1]) ax1.set_xlabel("") ax2.set_xlabel("") ax1.set_title("Water-oil, low, base, high and interpolants", fontsize=10) ax2.set_title("Water-oil, low, base, high and interpolants", fontsize=10) for tparam in np.arange(-1, 0, 0.2): interp = rec.interpolate(tparam, h=0.001) interp.gasoil.plotkrgkrog(ax3, "r") interp.gasoil.plotkrgkrog(ax4, "r") for tparam in np.arange(0, 1, 0.2): interp = rec.interpolate(tparam, h=0.001) interp.gasoil.plotkrgkrog(ax3, "g") interp.gasoil.plotkrgkrog(ax4, "g") rec.low.gasoil.plotkrgkrog(ax3, linewidth=2, linestyle=":") rec.base.gasoil.plotkrgkrog(ax3, linewidth=2) rec.high.gasoil.plotkrgkrog(ax3, linewidth=2, linestyle="--") rec.low.gasoil.plotkrgkrog(ax4, linewidth=2, linestyle=":") rec.base.gasoil.plotkrgkrog(ax4, linewidth=2) rec.high.gasoil.plotkrgkrog(ax4, linewidth=2, linestyle="--") ax3.set_title("Gas-oil, low, base, high and interpolants", fontsize=10) ax4.set_title("Gas-oil, low, base, high and interpolants", fontsize=10) ax4.set_yscale("log") ax4.set_ylim([1e-05, 1]) ax3.set_xlabel("") ax4.set_xlabel("") pyplot.subplots_adjust(hspace=0.3) print("-- Check:") print("-- * Red curves are between dotted and solid blue line") print("-- * Green curves are between solid blue and dashed") print("[Close windows to continue tests]") pyplot.show()
def test_df(): """Test dataframe dumps""" testdir = Path(__file__).absolute().parent scalrec_data = PyscalFactory.load_relperm_df( testdir / "data/scal-pc-input-example.xlsx") scalrec_list = PyscalFactory.create_scal_recommendation_list(scalrec_data) wog_list = scalrec_list.interpolate(-0.3) # Test dataframe dumps: dframe = scalrec_list.df() assert "SG" in dframe assert "KRG" in dframe assert "KROG" in dframe assert "PCOW" in dframe if "PCOG" in dframe: # Allow PCOG to be included later. assert dframe["PCOG"].sum() == 0 assert "KRW" in dframe assert "KROW" in dframe assert "SATNUM" in dframe assert dframe["SATNUM"].min() == 1 assert len(dframe["SATNUM"].unique()) == len(scalrec_list) assert set(dframe["CASE"]) == set(["pess", "base", "opt"]) assert dframe["SATNUM"].max() == len(scalrec_list) if HAVE_ECL2DF: # Test using ecl2df to do the include file printing. First we need to # massage the dataframe into what ecl2df can handle: base_df_swof = (dframe.set_index("CASE").loc["base"][[ "SW", "KRW", "KROW", "PCOW", "SATNUM" ]].assign(KEYWORD="SWOF").dropna().reset_index(drop=True)) ecl_inc = ecl2df.satfunc.df2ecl(base_df_swof) dframe_from_inc = ecl2df.satfunc.df(ecl_inc) pd.testing.assert_frame_equal(base_df_swof, dframe_from_inc) # Test also SGOF base_df_sgof = (dframe.set_index("CASE").loc["base"][[ "SG", "KRG", "KROG", "SATNUM" ]].assign(KEYWORD="SGOF", PCOG=0.0).dropna().reset_index(drop=True)) ecl_inc = ecl2df.satfunc.df2ecl(base_df_sgof) dframe_from_inc = ecl2df.satfunc.df(ecl_inc) pd.testing.assert_frame_equal(base_df_sgof, dframe_from_inc, check_like=True) # WaterOilGasList: dframe = wog_list.df() assert "SG" in dframe assert "KRG" in dframe assert "KROG" in dframe assert "PCOW" in dframe assert "PCOG" in dframe # This gets included through interpolation assert "KRW" in dframe assert "KROW" in dframe assert "SATNUM" in dframe assert dframe["SATNUM"].min() == 1 assert len(dframe["SATNUM"].unique()) == len(wog_list) assert "CASE" not in dframe assert dframe["SATNUM"].max() == len(wog_list) # WaterOil list input_dframe = pd.DataFrame(columns=["SATNUM", "Nw", "Now"], data=[[1, 2, 2]]) relperm_data = PyscalFactory.load_relperm_df(input_dframe) p_list = PyscalFactory.create_pyscal_list(relperm_data, h=0.2) dframe = p_list.df() assert "SW" in dframe assert "KRW" in dframe assert "KROW" in dframe assert "PCOW" not in dframe # to be interpreted as zero assert "SATNUM" in dframe assert len(dframe.columns) == 4 assert not dframe.empty # GasOil list input_dframe = pd.DataFrame(columns=["SATNUM", "Ng", "Nog"], data=[[1, 2, 2]]) relperm_data = PyscalFactory.load_relperm_df(input_dframe) p_list = PyscalFactory.create_pyscal_list(relperm_data, h=0.2) dframe = p_list.df() assert "SG" in dframe assert "KRG" in dframe assert "KROG" in dframe assert "PCOG" not in dframe # to be interpreted as zero assert "SATNUM" in dframe assert len(dframe.columns) == 4 assert not dframe.empty
def test_mock_two_satnums_via_files(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. """ # pylint: disable=no-value-for-parameter tmpdir.chdir() PyscalFactory.create_pyscal_list( TWO_SATNUM_PYSCAL_MOCK.loc["low"]).dump_family_1("pess.inc") PyscalFactory.create_pyscal_list( TWO_SATNUM_PYSCAL_MOCK.loc["base"]).dump_family_1("base.inc") PyscalFactory.create_pyscal_list( TWO_SATNUM_PYSCAL_MOCK.loc["high"]).dump_family_1("opt.inc") config = { "base": ["base.inc"], "low": ["pess.inc"], "high": ["opt.inc"], "result_file": "outfile.inc", "interpolations": [{ "param_w": -0.5, "param_g": 0.5 }], "delta_s": 0.1, } interp_relperm.process_config(config) outfile_str = Path("outfile.inc").read_text() # Assert things about the comments emitted by pyscal when interpolating: # This is used as a proxy for asserting that interpolation parameters # are used for the correct satnums assert outfile_str.find("SCAL recommendation interpolation to 0.5") assert outfile_str.find("SCAL recommendation interpolation to -0.5") # SWOF comes before SGOF: assert outfile_str.find("to -0.5") < outfile_str.find("to 0.5") outfile_df = satfunc.df(outfile_str, ntsfun=2) assert set(outfile_df["KEYWORD"].unique()) == {"SWOF", "SGOF"} assert set(outfile_df["SATNUM"].unique()) == {1, 2} config = { "base": ["base.inc"], "low": ["pess.inc"], "high": ["opt.inc"], "result_file": "outfile.inc", "interpolations": [ { "tables": [1], "param_w": -0.9, "param_g": -0.5 }, { "tables": [2], "param_w": 0.5, "param_g": 0.8 }, ], "delta_s": 0.1, } interp_relperm.process_config(config) outfile_str = open("outfile.inc").read() assert outfile_str.find("to -0.9") < outfile_str.find("to 0.5") assert outfile_str.find("to 0.5") < outfile_str.find("to -0.5") assert outfile_str.find("to 0.5") < outfile_str.find("to 0.8") config = { "base": ["base.inc"], "low": ["pess.inc"], "high": ["opt.inc"], "result_file": "outfile.inc", "interpolations": [ # This is a user error, the latter will override the first { "param_w": -0.9, "param_g": -0.5 }, { "param_w": 0.5, "param_g": 0.8 }, ], "delta_s": 0.1, } interp_relperm.process_config(config) outfile_str = open("outfile.inc").read() assert "interpolation to -0.9" not in outfile_str assert "interpolation to 0.8" in outfile_str config = { "base": ["base.inc"], "low": ["pess.inc"], "high": ["opt.inc"], "result_file": "outfile.inc", "interpolations": [ # Here the user intentionally overwrites the first: { "param_w": -0.9, "param_g": -0.5 }, { "tables": [], "param_w": 0.5, "param_g": 0.8 }, ], "delta_s": 0.1, } interp_relperm.process_config(config) outfile_str = open("outfile.inc").read() assert "interpolation to -0.9" not in outfile_str assert "interpolation to 0.8" in outfile_str