def test_wateroil_swir(swirr): """Check that the saturation values are valid for all swirr""" wateroil = WaterOil(swirr=swirr) check_table(wateroil.table)
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_interpolate_go( swl, sgcr, dsgcr, dswlhigh, sorg, dsorg, ng_l, ng_h, nog_l, nog_h, krgend_l, krgend_h, kroend_l, kroend_h, ): # pylint: disable=too-many-arguments,too-many-locals """Test many possible combinations of interpolation between two Corey gasoil curves, looking for numerical corner cases""" h = 0.01 go_low = GasOil(swl=swl, sgcr=sgcr, sorg=sorg, h=h) go_high = GasOil( swl=swl + dswlhigh, sgcr=sgcr + dsgcr, sorg=max(sorg - dsorg, 0), h=h ) go_low.add_corey_gas(ng=ng_l, krgend=krgend_l) go_high.add_corey_gas(ng=ng_h, krgend=krgend_h) go_low.add_corey_oil(nog=nog_l, kroend=kroend_l) go_high.add_corey_oil(nog=nog_h, kroend=kroend_h) ips = [] ip_dist = 0.05 for t in np.arange(0, 1 + ip_dist, ip_dist): go_ip = interpolate_go(go_low, go_high, t) check_table(go_ip.table) ips.append(go_ip) assert 0 < go_ip.crosspoint() < 1 # sgcr is non-trivial, if exponents are high, an effective sgcr might # be larger than the value used to initialize the curve. Try to be # permissive enough here. This can even cause the interpolant to be # outside the low-high envelope, but it is the way it is supposed to # be when sgcr is interpolated separately. sgcr_low = min( go_low.sgcr, go_low.estimate_sgcr(), go_high.sgcr, go_high.estimate_sgcr() ) sgcr_high = max( go_low.sgcr, go_low.estimate_sgcr(), go_high.sgcr, go_high.estimate_sgcr() ) sgcr_ip = go_ip.estimate_sgcr() sgcr_lower_bound_ok = sgcr_low - h - epsilon < sgcr_ip sgcr_upper_bound_ok = sgcr_ip < sgcr_high + h + epsilon assert sgcr_lower_bound_ok assert sgcr_upper_bound_ok # Distances between low and interpolants: dists = [ (go_low.table - interp.table)[["krg", "krog"]].sum().sum() for interp in ips ] print( "Interpolation, mean: {}, min: {}, max: {}, std: {} ip-par-dist: {}".format( np.mean(dists), min(dists), max(dists), np.std(np.diff(dists[1:])), ip_dist ) ) assert np.isclose(dists[0], 0) # Reproducing go_low # All curves that are close in parameter t, should be close in sum().sum(). # That means that diff of the distances should be similar, # that is the std.dev of the distances is low: ip_dist_std = np.std( np.diff(dists[1:]) ) # This number depends on 'h' and 't' range, and # by how different the low and high is. # (avoiding the first which reproduces go_low if ip_dist_std > 1.0: # number found from trial and error. print("ip_dist_std: {}".format(ip_dist_std)) print(dists) _, mpl_ax = plt.subplots() go_low.plotkrgkrog(mpl_ax=mpl_ax, color="red") go_high.plotkrgkrog(mpl_ax=mpl_ax, color="blue") for interp in ips: interp.plotkrgkrog(mpl_ax=mpl_ax, color="green") plt.show() assert False
def test_interpolate_wo( swl, dswcr, dswlhigh, sorw, nw_l, nw_h, now_l, now_h, krwend_l, krwend_h, kroend_l, kroend_h, ): # pylint: disable=too-many-arguments,too-many-locals """ Generate two random WaterOil curves, interpolate between them and check that the difference between each interpolant is small, this essentially checks that we can go continously between the two functions. """ wo_low = WaterOil(swl=swl, swcr=swl + dswcr, sorw=sorw) wo_high = WaterOil( swl=swl + dswlhigh, swcr=swl + dswlhigh + dswcr, sorw=max(sorw - 0.01, 0) ) wo_low.add_corey_water(nw=nw_l, krwend=krwend_l) wo_high.add_corey_water(nw=nw_h, krwend=krwend_h) wo_low.add_corey_oil(now=now_l, kroend=kroend_l) wo_high.add_corey_oil(now=now_h, kroend=kroend_h) ips = [] ip_dist = 0.05 for tparam in np.arange(0, 1 + ip_dist, ip_dist): wo_ip = interpolate_wo(wo_low, wo_high, tparam) check_table(wo_ip.table) assert wo_ip.tag ips.append(wo_ip) assert 0 < wo_ip.crosspoint() < 1 # Distances between low and interpolants: dists = [ (wo_low.table - interp.table)[["krw", "krow"]].sum().sum() for interp in ips ] assert np.isclose(dists[0], 0) # Distance between high and the last interpolant assert (wo_high.table - ips[-1].table)[["krw", "krow"]].sum().sum() < 0.01 # Distances between low and interpolants: dists = [ (wo_low.table - interp.table)[["krw", "krow"]].sum().sum() for interp in ips ] print( "Interpolation, mean: {}, min: {}, max: {}, std: {} ip-par-dist: {}".format( np.mean(dists), min(dists), max(dists), np.std(np.diff(dists[1:])), ip_dist ) ) assert np.isclose(dists[0], 0) # Reproducing wo_low # All curves that are close in parameter t, should be close in sum().sum(). # That means that diff of the distances should be similar, # that is the std.dev of the distances is low: ip_dist_std = np.std(np.diff(dists[1:])) # This number depends on 'h' and 't' range # (avoiding the first which reproduces go_low if ip_dist_std > 1.0: # Found by trial and error print("ip_dist_std: {}".format(ip_dist_std)) print(dists) _, mpl_ax = plt.subplots() wo_low.plotkrwkrow(mpl_ax=mpl_ax, color="red") wo_high.plotkrwkrow(mpl_ax=mpl_ax, color="blue") for interp in ips: interp.plotkrwkrow(mpl_ax=mpl_ax, color="green") plt.show() assert False
def test_wateroil_sorw(sorw): """Check that the saturation values are valid for all sorw""" wateroil = WaterOil(sorw=sorw) check_table(wateroil.table)
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) with pytest.raises(ValueError, match="Input must be a dict"): pyscal_factory.create_scal_recommendation("low") # (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) # Check that we error if any of the parameters above is missing: for case in ["low", "BASE", "high"]: copy1 = scal_input.copy() del copy1[case] with pytest.raises(ValueError): pyscal_factory.create_scal_recommendation(copy1) 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() basehigh = scal_input.copy() del basehigh["low"] with pytest.raises(ValueError, match='"low" case not supplied'): pyscal_factory.create_scal_recommendation(basehigh) baselow = scal_input.copy() del baselow["high"] with pytest.raises(ValueError, match='"high" case not supplied'): pyscal_factory.create_scal_recommendation(baselow) with pytest.raises( ValueError, match="All values in parameter dict must be dictionaries"): pyscal_factory.create_scal_recommendation({ "low": [1, 2], "base": { "swl": 0.1 }, "high": { "swl": 0.1 } })