from abc import ABCMeta, abstractmethod, abstractproperty
import numpy
__all__ = [
"BaseRelativePermeability",
]
# See <https://stackoverflow.com/questions/35673474/using-abc-abcmeta-in-a-way-it-is-compatible-both-with-python-2-7-and-python-3-5>
ABC = ABCMeta("ABC", (object,), {"__slots__": ()})
class BaseRelativePermeability(ABC):
_id = None
_name = ""
def __init__(self, *args):
"""
Base class for relative permeability models.
Do not use.
"""
pass
def __repr__(self):
"""Display relative permeability model informations."""
out = [
"{} relative permeability model (IRP = {}):".format(self._name, self._id)
]
out += [
" RP({}) = {}".format(i + 1, parameter)
for i, parameter in enumerate(self.parameters)
]
return "\n".join(out)
def __call__(self, sl):
"""Calculate relative permeability given liquid saturation."""
if numpy.ndim(sl) == 0:
if not (0.0 <= sl <= 1.0):
raise ValueError()
return self._eval(sl, *self.parameters)
else:
sl = numpy.asarray(sl)
if not numpy.logical_and((sl >= 0.0).all(), (sl <= 1.0).all()):
raise ValueError()
return numpy.transpose([self._eval(sat, *self.parameters) for sat in sl])
@abstractmethod
def _eval(self, sl, *args):
raise NotImplementedError()
def plot(self, n=100, ax=None, figsize=(10, 8), plt_kws=None):
"""
Plot relative permeability curve.
Parameters
----------
n : int, optional, default 100
Number of saturation points.
ax : matplotlib.pyplot.Axes or None, optional, default None
Matplotlib axes. If `None`, a new figure and axe is created.
figsize : array_like or None, optional, default None
New figure size if `ax` is `None`.
plt_kws : dict or None, optional, default None
Additional keywords passed to :func:`matplotlib.pyplot.plot`.
"""
try:
import matplotlib.pyplot as plt
except ImportError:
raise ImportError(
"Plotting relative permeability curve requires matplotlib to be installed."
)
if not (isinstance(n, int) and n > 1):
raise TypeError()
if not (ax is None or isinstance(ax, plt.Axes)):
raise TypeError()
if not (figsize is None or isinstance(figsize, (tuple, list, numpy.ndarray))):
raise TypeError()
if len(figsize) != 2:
raise ValueError()
if not (plt_kws is None or isinstance(plt_kws, dict)):
raise TypeError()
# Plot parameters
plt_kws = plt_kws if plt_kws is not None else {}
_kwargs = {"linestyle": "-", "linewidth": 2}
_kwargs.update(plt_kws)
# Initialize figure
if ax:
ax1 = ax
else:
figsize = figsize if figsize else (8, 5)
fig = plt.figure(figsize=figsize, facecolor="white")
ax1 = fig.add_subplot(1, 1, 1)
# Calculate liquid and gas relative permeability
sl = numpy.linspace(0.0, 1.0, n)
kl, kg = self(sl)
# Plot
ax1.plot(sl, kl, label="Liquid", **_kwargs)
ax1.plot(sl, kg, label="Gas", **_kwargs)
ax1.set_xlim(0.0, 1.0)
ax1.set_ylim(0.0, 1.0)
ax1.set_xlabel("Saturation (liquid)")
ax1.set_ylabel("Relative permeability")
ax1.grid(True, linestyle=":")
plt.draw()
plt.show()
return ax1
@property
def id(self):
"""Return relative permeability model ID in TOUGH."""
return self._id
@property
def name(self):
"""Return relative permeability model name."""
return self._name
@abstractproperty
def parameters(self):
raise NotImplementedError()
@parameters.setter
def parameters(self, value):
raise NotImplementedError()