def estimate_sorg(self): """Estimate sorg of the current krg or krog data. sorg is estimated by searching for a linear part in krg downwards from sg=1-swl. In practice it is impossible to infer sorg = 0, since we are limited by h, and the last segment from sg=1-swl-h to sg=1-swl can always be assumed linear. If krgend is anchored to sorg, krg data is used to infer sorg. If not, krg cannot be used for this, and krog is used. sorg might be overestimated when krog is used if it very close to zero before reaching sorw. If the curve is linear everywhere, sorg will be returned as sgcr + h Args: None Returns: float: The estimated sorg. """ if self.krgendanchor == "sorg": assert "krg" in self.table assert self.table["krg"].sum() > 0 return self.table["sg"].max() - utils.estimate_diffjumppoint( self.table, xcol="sg", ycol="krg", side="right") assert "krog" in self.table assert self.table["krog"].sum() > 0 return self.table["sg"].max() - utils.estimate_diffjumppoint( self.table, xcol="sg", ycol="krog", side="right")
def estimate_sorw(self, curve="krw"): """Estimate sorw of the current krw data. This is mostly relevant when add_fromtable() has been used. sorw is estimated by searching for a linear part in krw downwards from sw=1. In practice it is impossible to infer sorw = 0, since we are limited by h, and the last segment from sw=1-h to sw=1 can always be assumed linear. Expect sorw = h if the real sorw = 0, but do not depend that it might not return zero in the future (one could argue that sorw = h should be specially treated to mean sorw = 0) If the curve is linear everywhere, sorw will be returned as swl + h krow is not used, and should probably not be, as it can be very close to zero before approaching sorw. Args: curve (str): Colum name of column to use, default is krw. If this is all linear, but krow is not, you might be better off with krow Returns: float: The estimated sorw. """ assert curve in self.table assert self.table[curve].sum() > 0 return self.table["sw"].max() - utils.estimate_diffjumppoint( self.table, xcol="sw", ycol=curve, side="right" )
def test_diffjumppoint(): """Test estimator for the jump in first derivative for some manually set up cases. This code is also extensively tested throuth test_addfromtable""" dframe = pd.DataFrame(columns=["x", "y"], data=[[0, 0], [0.3, 0.2], [1, 1]]) assert utils.estimate_diffjumppoint(dframe, side="right") == 0.3 assert utils.estimate_diffjumppoint(dframe, side="left") == 0.3 dframe = pd.DataFrame(columns=["x", "y"], data=[[0, 0], [1, 1]]) # We don't really care what gets printed from this, just don't crash.. assert 0 <= utils.estimate_diffjumppoint(dframe, side="right") <= 1 assert 0 <= utils.estimate_diffjumppoint(dframe, side="left") <= 1 dframe = pd.DataFrame( columns=["x", "y"], data=[ [0, 0], [0.1, 0.1], [0.2, 0.2], # Linear until here [0.3, 0.4], # Nonlinear region [0.4, 0.45], # Nonlinear region [0.7, 0.7], # Linear from here [0.8, 0.8], [1, 1], ], ) assert utils.estimate_diffjumppoint(dframe, side="left") == 0.2 assert utils.estimate_diffjumppoint(dframe, side="right") == 0.7 dframe = pd.DataFrame( columns=["x", "y"], data=[ [0, 0], [0.1, 0.0], [0.2, 0.0], # Linear until here [0.3, 0.4], # Nonlinear region [0.9, 1], # Start linear region again [1, 1], ], ) assert utils.estimate_diffjumppoint(dframe, side="left") == 0.2 assert utils.estimate_diffjumppoint(dframe, side="right") == 0.9
def estimate_sgcr(self, curve="krog"): """Estimate sgcr of the current krog data. sgcr is estimated by searching for a linear part in krog upwards from sg=0. In practice it is impossible to infer sgcr = 0, since we are limited by h, and we always have to assume that the first segment is linear. If the curve is linear everywhere, sgcr will be returned as the right endpoint. Args: curve (str): Column name to use for search for linearity. Default is krog, if all of that is linear, you may try krg instead. Returns: float: The estimated sgcr. """ assert curve in self.table assert self.table[curve].sum() > 0 return utils.estimate_diffjumppoint( self.table, xcol="sg", ycol=curve, side="left" )
def estimate_swcr(self, curve="krow"): """Estimate swcr of the current krw data. swcr is estimated by searching for a linear part in krw upwards from sw=swl. In practice it is impossible to infer swcr = 0, since we are limited by h, and the first segment is assumed linear anyways. If the curve is linear everywhere, swcr can end up at the right end of your saturation interval. Args: curve (str): Colum name of column to use, default is krow. If this is all linear, but krw is not, you might be better off with krw Returns: float: The estimated sgcr. """ assert curve in self.table assert self.table[curve].sum() > 0 return utils.estimate_diffjumppoint( self.table, xcol="sw", ycol=curve, side="left" )
def add_fromtable( self, df, swcolname="Sw", krwcolname="krw", krowcolname="krow", pccolname="pcow", krwcomment="", krowcomment="", pccomment="", sorw=None, ): """Interpolate relpermdata from a dataframe. The saturation range with endpoints must be set up beforehand, and must be compatible with the tabular input. The tabular input will be interpolated to the initialized Sw-table If you have krw and krow in different dataframes, call this function twice Calling function is responsible for checking if any data was actually added to the table. The relpermdata will be interpolated using a monotone cubic interpolator below 1-sorw, and linearly above 1-sorw. Capillary pressure data will be interpolated monotone cubicly over the entire saturation interval The python package ecl2df has a tool for converting Eclipse input files to dataframes. Args: df (pd.DataFrame): containing data swcolname (string): column name with the saturation data in the dataframe df krwcolname (string): name of column in df with krw krowcolname (string): name of column in df with krow pccolname (string): name of column in df with capillary pressure data krwcomment (string): Inserted into comment krowcomment (string): Inserted into comment pccomment (str): Inserted into comment sorw (float): Explicit sorw. If None, it will be estimated from the numbers in krw (or krow) """ from scipy.interpolate import PchipInterpolator, interp1d # Avoid having to deal with multi-indices: if len(df.index.names) > 1: logging.warning( "add_fromtable() did a reset_index(), consider not supplying MultiIndex" ) df = df.reset_index() if swcolname not in df: logging.critical( swcolname + " not found in dataframe, can't read table data" ) raise ValueError if (df[swcolname].diff() < 0).any(): raise ValueError("sw data not sorted") if krwcolname in df: if not sorw: sorw = df[swcolname].max() - utils.estimate_diffjumppoint( df, xcol=swcolname, ycol=krwcolname, side="right" ) logging.info("Estimated sorw in tabular data to %f", sorw) assert -epsilon <= sorw <= 1 + epsilon linearpart = df[swcolname] >= 1 - sorw nonlinearpart = df[swcolname] <= 1 - sorw # (overlapping at sorw) if sum(linearpart) < 2: linearpart = pd.Series([False] * len(linearpart)) nonlinearpart = ~linearpart sorw = 0 if sum(nonlinearpart) < 2: nonlinearpart = pd.Series([False] * len(nonlinearpart)) linearpart = ~nonlinearpart if not np.isclose(df[swcolname].min(), self.table["sw"].min()): raise ValueError("Incompatible swl") # Verify that incoming data is increasing (or level): if not (df[krwcolname].diff().dropna() > -epsilon).all(): raise ValueError("Incoming krw not increasing") if sum(nonlinearpart) >= 2: pchip = PchipInterpolator( df[nonlinearpart][swcolname].astype(float), df[nonlinearpart][krwcolname].astype(float), ) self.table.loc[self.table["sw"] <= 1 - sorw, "krw"] = pchip( self.table.loc[self.table["sw"] <= 1 - sorw, "sw"] ) if sum(linearpart) >= 2: linearinterpolator = interp1d( df[linearpart][swcolname].astype(float), df[linearpart][krwcolname].astype(float), ) self.table.loc[self.table["sw"] > 1 - sorw, "krw"] = linearinterpolator( self.table.loc[self.table["sw"] > 1 - sorw, "sw"] ) self.sorw = sorw self.krwcomment = "-- krw from tabular input" + krwcomment + "\n" if krowcolname in df: if not sorw: sorw = df[swcolname].max() - utils.estimate_diffjumppoint( df, xcol=swcolname, ycol=krowcolname, side="right" ) logging.info("Estimated sorw in tabular data from krow to %s", sorw) assert -epsilon <= sorw <= 1 + epsilon linearpart = df[swcolname] >= 1 - sorw nonlinearpart = df[swcolname] <= 1 - sorw # (overlapping at sorw) if sum(linearpart) < 2: linearpart = pd.Series([False] * len(linearpart)) nonlinearpart = ~linearpart sorw = 0 if sum(nonlinearpart) < 2: nonlinearpart = pd.Series([False] * len(nonlinearpart)) linearpart = ~nonlinearpart if not np.isclose(df[swcolname].min(), self.table["sw"].min()): raise ValueError("Incompatible swl") if not (df[krowcolname].diff().dropna() < epsilon).all(): raise ValueError("Incoming krow not decreasing") if sum(nonlinearpart) >= 2: pchip = PchipInterpolator( df[nonlinearpart][swcolname].astype(float), df[nonlinearpart][krowcolname].astype(float), ) self.table.loc[self.table["sw"] <= 1 - sorw, "krow"] = pchip( self.table.loc[self.table["sw"] <= 1 - sorw, "sw"] ) if sum(linearpart) >= 2: linearinterpolator = interp1d( df[linearpart][swcolname].astype(float), df[linearpart][krowcolname].astype(float), ) self.table.loc[ self.table["sw"] > 1 - sorw, "krow" ] = linearinterpolator( self.table.loc[self.table["sw"] > 1 - sorw, "sw"] ) self.sorw = sorw self.krowcomment = "-- krow from tabular input" + krowcomment + "\n" if pccolname in df: # Incoming dataframe must cover the range: if df[swcolname].min() > self.table["sw"].min(): raise ValueError("Too large swl for pc interpolation") if df[swcolname].max() < self.table["sw"].max(): raise ValueError("max(sw) of incoming data not large enough") if np.isinf(df[pccolname]).any(): logging.warning( ( "Infinity pc values detected. Will be dropped. " "Risk of extrapolation" ) ) df = df.replace([np.inf, -np.inf], np.nan) df.dropna(subset=[pccolname], how="all", inplace=True) # If nonzero, then it must be decreasing: if df[pccolname].abs().sum() > 0: if not (df[pccolname].diff().dropna() < 0.0).all(): raise ValueError("Incoming pc not decreasing") pchip = PchipInterpolator( df[swcolname].astype(float), df[pccolname].astype(float) ) self.table["pc"] = pchip(self.table["sw"]) if np.isnan(self.table["pc"]).any() or np.isinf(self.table["pc"]).any(): raise ValueError("inf/nan in interpolated data, check input") self.pccomment = "-- pc from tabular input" + pccomment + "\n"