def test_gasoil_gascond_fails(): """Interpolation between an object for gas condendensate (sgro > 0) and an object with sgro = 0 (regular gas-oil) should fail""" gascond = GasOil(sgro=0.1, sgcr=0.1) gasoil = GasOil(sgro=0.0, sgcr=0.1) gasoil.add_corey_gas() gasoil.add_corey_oil() gascond.add_corey_gas() gascond.add_corey_oil() # interpolation parameter at zero is no interpolation, just lookup, # so it works fine: check_table(interpolate_go(gasoil, gascond, parameter=0.0).table) check_table(interpolate_go(gasoil, gascond, parameter=epsilon).table) check_table(interpolate_go(gasoil, gascond, parameter=10 * epsilon).table) with pytest.raises(ValueError, match="Interpolated sgro"): interpolate_go(gasoil, gascond, parameter=100 * epsilon) with pytest.raises(ValueError, match="Interpolated sgro"): interpolate_go(gasoil, gascond, parameter=0.5) check_table(interpolate_go(gasoil, gascond, parameter=1.0 - epsilon).table) check_table(interpolate_go(gasoil, gascond, parameter=1.0).table)
def test_tag_preservation(): """Test that we can preserve tags/comments through interpolation""" wo_low = WaterOil(swl=0.1) wo_high = WaterOil(swl=0.2) wo_low.add_corey_water(nw=2) wo_high.add_corey_water(nw=3) wo_low.add_corey_oil(now=2) wo_high.add_corey_oil(now=3) interpolant1 = interpolate_wo(wo_low, wo_high, parameter=0.1, h=0.2) assert "Interpolated to 0.1" in interpolant1.tag sat_table_str_ok(interpolant1.SWOF()) wo_high.tag = "FOOBAR" interpolant2 = interpolate_wo(wo_low, wo_high, parameter=0.1, h=0.2) assert "Interpolated to 0.1" in interpolant2.tag assert "between" in interpolant2.tag assert wo_high.tag in interpolant2.tag sat_table_str_ok(interpolant2.SWOF()) # wo_low.tag was empty deliberately here. # When wo_log and wo_high has the same tag: wo_low.tag = "FOOBAR" interpolant3 = interpolate_wo(wo_low, wo_high, parameter=0.1, h=0.2) assert "Interpolated to 0.1" in interpolant3.tag assert "between" not in interpolant3.tag assert wo_high.tag in interpolant3.tag sat_table_str_ok(interpolant3.SWOF()) # Explicit tag: interpolant4 = interpolate_wo( wo_low, wo_high, parameter=0.1, h=0.2, tag="Explicit tag" ) assert interpolant4.tag == "Explicit tag" # Tag with newline interpolant6 = interpolate_wo( wo_low, wo_high, parameter=0.1, h=0.2, tag="Explicit tag\non two lines" ) assert "Explicit tag" in interpolant6.tag print(interpolant6.SWOF()) sat_table_str_ok(interpolant6.SWOF()) # Empty tag: interpolant5 = interpolate_wo(wo_low, wo_high, parameter=0.1, h=0.2, tag="") assert interpolant5.tag == "" # Also sample check for GasOil (calls the same code) go_low = GasOil() go_high = GasOil() go_low.add_corey_gas(ng=2) go_high.add_corey_gas(ng=3) go_low.add_corey_oil(nog=2) go_high.add_corey_oil(nog=3) interpolant1 = interpolate_go(go_low, go_high, parameter=0.1, h=0.2) assert "Interpolated to 0.1" in interpolant1.tag sat_table_str_ok(interpolant1.SGOF())
def test_gascond_interpolation(sgro_low, sgro_high): """sgro is required to be either 0 or sgcr, and interpolations will crash when this is not the case. This test validates that we can let sgro and sgcr go to zero, and that we always are able to interpolate without crashes.""" go_low = GasOil(sgro=sgro_low, sgcr=sgro_low) go_high = GasOil(sgro=sgro_high, sgcr=sgro_high) go_low.add_corey_gas() go_low.add_corey_oil() go_high.add_corey_gas() go_high.add_corey_oil() go_ip = interpolate_go(go_low, go_high, parameter=0.5) check_table(go_ip.table)
def test_ip_go_kroendmax(): """Test behaviour of kroend/kromax under interpolation, gas condensate modelling""" go_low = GasOil(swl=0, sgro=0.1, sgcr=0.1) go_high = GasOil(swl=0, sgro=0) go_low.add_corey_gas() go_low.add_corey_oil(nog=2, kroend=0.5, kromax=1) go_high.add_corey_gas() go_high.add_corey_oil(nog=2, kroend=1) # Interpolate to midpoint between the curves above go_ip = interpolate_go(go_low, go_high, 0.5) # kro(sg=0) is 1 for all interpolants: assert float_df_checker(go_ip.table, "SG", 0.0, "KROG", 1.0) # kro(sg=mean(sgro)) = mean kroeend assert float_df_checker(go_ip.table, "SG", (0 + 0.1) / 2.0, "KROG", 0.75) assert np.isclose(go_ip.estimate_sgro(), (0 + 0.1) / 2.0)
def test_interpolations_go_fromtable(): """Test based on bug exposed in pyscal 0.6.1, where sgcr was underestimated in interpolations following add_fromtable(). """ base = pd.DataFrame( columns=["Sg", "krg", "krog"], data=[ [0.0, 0.0, 1.0], [0.1, 0.0, 1.0], [0.2, 0.0, 1.0], # sgcr [0.3, 0.1, 0.9], [0.8, 0.8, 0.0], # sorg [0.9, 0.9, 0.0], [1.0, 1.0, 0.0], ], ) opt = pd.DataFrame( columns=["Sg", "krg", "krog"], data=[ [0.0, 0.0, 1.0], [0.1, 0.0, 1.0], [0.3, 0.0, 1.0], [0.4, 0.1, 0.2], # sgcr [0.9, 0.9, 0.0], # sorg [0.95, 0.95, 0.0], [1.0, 1.0, 0.0], ], ) go_base = GasOil(h=0.01) go_base.add_fromtable(base) assert np.isclose(go_base.estimate_sgcr(), 0.2) assert np.isclose(go_base.estimate_sorg(), 0.2) go_opt = GasOil(h=0.01) go_opt.add_fromtable(opt) assert np.isclose(go_opt.estimate_sgcr(), 0.3) assert np.isclose(go_opt.estimate_sorg(), 0.1) go_ip = interpolate_go(go_base, go_opt, 0.5, h=0.01) assert np.isclose(go_ip.estimate_sgcr(), 0.25) assert np.isclose(go_ip.estimate_sorg(), 0.15)
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_ip_go_kroend(): """Test behaviour of kroend under interpolation""" go_low = GasOil(swl=0, sgcr=0.1, sorg=0.2) go_low.add_corey_gas(ng=2, krgend=0.5, krgmax=0.7) go_low.add_corey_oil(nog=2, kroend=0.6) go_high = GasOil(swl=0.02, sgcr=0.05, sorg=0.1) go_high.add_corey_gas(ng=2, krgend=0.5, krgmax=0.72) go_high.add_corey_oil(nog=2, kroend=0.7) # Interpolate to midpoint between the curves above go_ip = interpolate_go(go_low, go_high, 0.5) # kroend at sg zero: assert float_df_checker(go_ip.table, "sg", 0.0, "krog", (0.6 + 0.7) / 2.0) assert np.isclose(go_ip.swl, 0.01) assert np.isclose(go_ip.sorg, 0.15) # krgmax at 1 - swl: assert float_df_checker(go_ip.table, "sg", 1 - go_ip.swl, "krg", 0.71) # krgend at 1 - swl - sorg assert float_df_checker(go_ip.table, "sg", 1 - go_ip.swl - go_ip.sorg, "krg", 0.5) # If krgendanchor is None, then krgmax should be irrelevant go_low = GasOil(swl=0, sgcr=0.1, sorg=0.2, krgendanchor="") go_low.add_corey_gas(ng=5, krgend=0.5, krgmax=0.7) # krgmax will trigger warning message, intended, as the 0.7 # value here will mean nothing. go_low.add_corey_oil(nog=2, kroend=0.6) go_high = GasOil(swl=0.02, sgcr=0.05, sorg=0.1, krgendanchor="") go_high.add_corey_gas(ng=4, krgend=0.5, krgmax=0.72) # krgmax will trigger warning message, intended. go_high.add_corey_oil(nog=2, kroend=0.7) # Interpolate to midpoint between the curves above go_ip = interpolate_go(go_low, go_high, 0.5, h=0.1) assert float_df_checker(go_ip.table, "sg", 0.0, "krog", (0.6 + 0.7) / 2.0) # Activate these line to see a bug, interpolation_go # does not honor krgendanchorA: # _, mpl_ax = plt.subplots() # go_low.plotkrgkrog(mpl_ax=mpl_ax, color="red") # go_high.plotkrgkrog(mpl_ax=mpl_ax, color="blue") # go_ip.plotkrgkrog(mpl_ax=mpl_ax, color="green") # plt.show() # krgmax is irrelevant, krgend is used here: assert float_df_checker(go_ip.table, "sg", 1 - 0.01, "krg", 0.5) # Also check that estimated new sgcr is between the inputs: assert 0.05 <= go_ip.estimate_sgcr() <= 0.1 # Do we get into trouble if krgendanchor is different in low and high? go_low = GasOil(swl=0, sgcr=0.1, sorg=0.2, krgendanchor="sorg") go_low.add_corey_gas(ng=2, krgend=0.5, krgmax=0.7) go_low.add_corey_oil(nog=2, kroend=0.6) go_high = GasOil(swl=0.02, sgcr=0.05, sorg=0.1, krgendanchor="") go_high.add_corey_gas(ng=2, krgend=0.5) go_high.add_corey_oil(nog=2, kroend=0.7) # Interpolate to midpoint between the curves above go_ip = interpolate_go(go_low, go_high, 0.5) assert float_df_checker(go_ip.table, "sg", 0.0, "krog", (0.6 + 0.7) / 2.0) # max(krg) is here avg of krgmax and krgend from the differnt tables: assert float_df_checker(go_ip.table, "sg", 1 - 0.01, "krg", 0.6) # krgend at 1 - swl - sorg, non-trivial expression, so a numerical # value is used here in the test: assert float_df_checker(go_ip.table, "sg", 1 - 0.01 - 0.15, "krg", 0.4491271) # krog-zero at 1 - swl - sorg: assert float_df_checker(go_ip.table, "sg", 1 - 0.01 - 0.15, "krog", 0)
def interpolate(self, parameter, parameter2=None, h=0.02): """Interpolate between low, base and high Endpoints are located for input curves, and interpolated individually. Interpolation for the nonlinear part is done on a normalized interval between the endpoints Interpolation is linear in relperm-direction, and will thus not be linear in log-relperm-direction This method returns an WaterOilGas object which can be realized into printed tables. No attempt is made to parametrize the interpolant in L,E,T parameter space, or Corey-space. Args: parameter (float): Between -1 and 1, inclusive. -1 reproduces low/ pessimistic curve, 0 gives base, 1 gives high/optimistic. parameter2 (float): If not None, used for the gas-oil interpolation, enables having interpolation uncorrelated for WaterOil and GasOil. Ignored for GasWater (no warning). h (float): Saturation step length in generated tables. Does not need to be the same as the tables interpolation is done from. """ if parameter2 is not None: gasparameter = parameter2 else: gasparameter = parameter # Either wateroil or gasoil can be None in the low, base, high # If they are None, it is a two-phase problem and we # should support this. do_gaswater = False do_wateroil = False do_gasoil = False if self.type == GasWater: do_gaswater = True elif self.type == WaterOilGas: do_wateroil = (self.base.wateroil is not None and self.low.wateroil is not None and self.high.wateroil is not None) do_gasoil = (self.base.gasoil is not None and self.low.gasoil is not None and self.high.gasoil is not None) if not do_gaswater: if not do_wateroil and not do_gasoil: raise ValueError( "Neither WaterOil or GasOil is complete in SCAL recommendation" ) if parameter2 is not None: if not do_gasoil: logger.warning("parameter2 is meaningless for water-oil only") if do_gaswater: logger.warning("parameter2 is meaningless for gas-water") # Initialize wateroil and gasoil curves to be filled with # interpolated curves: tags = set() if do_wateroil or do_gaswater: tags = tags.union( set([ self.base.wateroil.tag, self.low.wateroil.tag, self.high.wateroil.tag, ])) if do_gasoil: tags = tags.union( set([ self.base.gasoil.tag, self.low.gasoil.tag, self.high.gasoil.tag ])) tagstring = "\n".join(tags) if do_gaswater: interpolant = GasWater(h=h, tag=tagstring) if gasparameter != parameter: logger.warning( "Different interpolation parameters for Water and for " "gas in GasWater, this is maybe not what you want") else: interpolant = WaterOilGas(h=h, tag=tagstring) if do_wateroil or do_gaswater: tag = ( "SCAL recommendation interpolation to {}\n".format(parameter) + tagstring) if abs(parameter) > 1.0: logger.error("Interpolation parameter must be in [-1,1]") assert abs(parameter) <= 1.0 elif np.isclose(parameter, 0.0): interpolant.wateroil = copy.deepcopy(self.base.wateroil) interpolant.wateroil.tag = tag elif np.isclose(parameter, -1.0): interpolant.wateroil = copy.deepcopy(self.low.wateroil) interpolant.wateroil.tag = tag elif np.isclose(parameter, 1.0): interpolant.wateroil = copy.deepcopy(self.high.wateroil) interpolant.wateroil.tag = tag elif parameter < 0.0: interpolant.wateroil = interpolate_wo(self.base.wateroil, self.low.wateroil, -parameter, h=h, tag=tag) elif parameter > 0.0: interpolant.wateroil = interpolate_wo(self.base.wateroil, self.high.wateroil, parameter, h=h, tag=tag) else: interpolant.wateroil = None if do_gasoil or do_gaswater: tag = ("SCAL recommendation interpolation to {}\n".format( gasparameter) + tagstring) if abs(gasparameter) > 1.0: logger.error("Interpolation parameter must be in [-1,1]") assert abs(gasparameter) <= 1.0 elif np.isclose(gasparameter, 0.0): interpolant.gasoil = copy.deepcopy(self.base.gasoil) interpolant.gasoil.tag = tag elif np.isclose(gasparameter, -1.0): interpolant.gasoil = copy.deepcopy(self.low.gasoil) interpolant.gasoil.tag = tag elif np.isclose(gasparameter, 1.0): interpolant.gasoil = copy.deepcopy(self.high.gasoil) interpolant.gasoil.tag = tag elif gasparameter < 0.0: interpolant.gasoil = interpolate_go(self.base.gasoil, self.low.gasoil, -1 * gasparameter, h=h, tag=tag) elif gasparameter > 0.0: interpolant.gasoil = interpolate_go(self.base.gasoil, self.high.gasoil, gasparameter, h=h, tag=tag) else: interpolant.gasoil = None return interpolant