Source code for geotecha.constitutive_models.void_ratio_permeability

# geotecha - A software suite for geotechncial engineering
# Copyright (C) 2018  Rohan T. Walker (rtrwalker@gmail.com)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see http://www.gnu.org/licenses/gpl.html.

"""
Relationships between permeability and void-ratio
"""
from __future__ import print_function, division

import numpy as np
import matplotlib.pyplot as plt
import matplotlib
import functools

[docs]class PermeabilityVoidRatioRelationship(object): """Base class for defining permeability void ratio relationships"""
[docs] def e_from_k(self, estress, **kwargs): """Void ratio from permeability""" raise NotImplementedError("e_from_k must be implemented")
[docs] def k_from_e(self, e, **kwargs): """Permeability from void ratio""" raise NotImplementedError("k_from_e must be implemented")
[docs] def e_and_k_for_plotting(self, **kwargs): """Void ratio and permeability that plot the method""" # should return a tuple of x and y values # x-values are peremability, y-values are void ratio. raise NotImplementedError("e_and_k_for_plotting must be implemented")
[docs] def plot_model(self, **kwargs): """Plot the void ratio-permeability""" ax = kwargs.pop('ax', plt.gca()) x, y = self.e_and_k_for_plotting(**kwargs) ax.plot(x, y) return
[docs]class CkPermeabilityModel(PermeabilityVoidRatioRelationship): """Semi-log void ratio permeability relationship Parameters ---------- Ck : float Slope of void-ratio vs permeability. ka, ea : float Permeability and void ratio specifying a point on e-k line. """ def __init__(self, Ck, ka, ea): self.Ck = Ck self.ka = ka self.ea = ea
[docs] def e_from_k(self, k, **kwargs): """Void ratio from permeability Parameters ---------- k : float Current permeability. Returns ------- e : float Void ratio corresponding to current permeability. Examples -------- >>> a = CkPermeabilityModel(Ck=1.5, ka=10, ea=4) >>> a.e_from_k(8.0) 3.854... Array Inputs: >>> a.e_from_k(np.array([8.0, 4.0])) array([3.854..., 3.403...]) """ return self.ea + self.Ck * np.log10(k/self.ka)
[docs] def k_from_e(self, e, **kwargs): """Permeability from void ratio Parameters ---------- e : float Void ratio. Returns ------- k : float Permeability corresponding to current void ratio. Examples -------- >>> a = CkPermeabilityModel(Ck=1.5, ka=10, ea=4) >>> a.k_from_e(3.855) 8.0... Array Inputs >>> a.k_from_e(np.array([3.855, 3.404])) array([8.0..., 4.0...]) """ return self.ka * np.power(10.0, (e - self.ea) / self.Ck)
[docs] def e_and_k_for_plotting(self, **kwargs): """Void ratio and permeability that plot the model Parameters ---------- npts : int, optional Number of points to return. Default npts=100. xmin, xmax : float, optional Range of x (i.e. effective stress) values from which to return points. Default xmin=1, xmax=100. Returns ------- x, y : 1d ndarray `npts` permeability, and void ratio values between `xmin` and `xmax`. """ npts = kwargs.get('npts', 100) xmin, xmax = kwargs.get('xmin', 1.0), kwargs.get('xmax', 100) x = np.linspace(xmin, xmax, npts) y = self.e_from_k(x) return x, y
[docs]class ConstantPermeabilityModel(PermeabilityVoidRatioRelationship): """Permeability constant with void ratio Note that the method e_from_k is meaningless because there are multiple e values for single k value. Parameters ---------- ka : float Permeability. """ def __init__(self, ka): self.ka = ka
[docs] def e_from_k(self, k, **kwarks): """Void ratio from permeability, parameters are irrelevant.""" raise ValueError("e_from_k is meaningless for a constant permeability model")
[docs] def k_from_e(self, e, **kwargs): """Permeability from void ratio Parameters ---------- e : float Void ratio. Returns ------- ka : float The constant permeability. Examples -------- >>> a = ConstantPermeabilityModel(ka=2.5) >>> a.k_from_e(3.855) 2.5 Array Inputs: >>> a.k_from_e(np.array([3.855, 3.404])) array([2.5, 2.5]) """ return np.ones_like(e, dtype=float) * self.ka
[docs] def e_and_k_for_plotting(self, **kwargs): """Void ratio and permeability that plot the model Parameters ---------- npts : int, optional Number of points to return. Default npts=100 xmin, xmax : float, optional Range of x (i.e. effective stress) values from which to return points. Default xmin=1, xmax=100. Returns ------- x, y : 1d ndarray `npts` permeability, and void ratio values between `xmin` and `xmax`. """ npts = kwargs.get('npts', 100) xmin, xmax = kwargs.get('xmin', 1.0), kwargs.get('xmax', 100) x = np.linspace(xmin, xmax, npts) y = self.e_from_k(x) return x, y
[docs]class PwiseLinearPermeabilityModel(PermeabilityVoidRatioRelationship): """Piecewise linear ratio permeability relationship x and y data can be interpolated natural-natural, natural-log10, log10-natural, or log10, log10. Parameters ---------- ka, ea : 1d array Permeability values and void ratio values defining a one-to-one relationship. xlog, ylog : True/False, Optional If True then interpolation on each axis is assumed to be logarithmic with base 10. x refers to the peremability axis, y to the void ratio axis. Default xlog=ylog=False. """ def __init__(self, ka, ea, xlog=False, ylog=False, xbase=10, ybase=10): self.ka = np.asarray(ka, dtype=float) self.ea = np.asarray(ea, dtype=float) self.xlog = xlog self.ylog = ylog #TODO: adjust for different logarithm bases. self.ka_slice = slice(None) self.ea_slice = slice(None) if np.any(np.diff(self.ka) <= 0): # ka is in decreasing order # reverse the slice for e_from_k interpolation self.ka_slice = slice(None, None, -1) # raise ValueError("'ka' must be in monotonically increasing order.") if np.any(np.diff(self.ea) <= 0): # ea is in decreasing order # reverse the slice for k_from_e interpolation self.ea_slice = slice(None, None, -1) # raise ValueError("'ea' must be in monotomically increasing order.") if len(ka)!=len(ea): raise IndexError("'ka' and 'ea' must be the same length.") self.log_ka = np.log10(self.ka) self.log_ea = np.log10(self.ea)
[docs] def e_from_k(self, k, **kwargs): """Void ratio from permeability Parameters ---------- k : float Current permeability. Returns ------- e : float Void ratio corresponding to current permeability. Examples -------- >>> a = PwiseLinearPermeabilityModel(ka=np.array([1.0, 2.0]), ea=np.array([5, 7.0])) >>> a.e_from_k(1.25) 5.5 Array Inputs: >>> a.e_from_k(np.array([1.25, 1.75])) array([5.5, 6.5]) Logarithmic permeability scale: >>> a = PwiseLinearPermeabilityModel(ka=np.array([1.0, 2.0]), ... ea=np.array([5, 7.0]), xlog=True) >>> a.e_from_k(1.25) 5.643... >>> a.e_from_k(np.array([1.25, 1.75])) array([5.643..., 6.614...]) Logarithmic void ratio scale: >>> a = PwiseLinearPermeabilityModel(ka=np.array([1.0, 2.0]), ... ea=np.array([5, 7.0]), ylog=True) >>> a.e_from_k(1.25) 5.4387... >>> a.e_from_k(np.array([1.25, 1.75])) array([5.4387..., 6.435...]) Logarithmic permeability and void ratio scale: >>> a = PwiseLinearPermeabilityModel(ka=np.array([1.0, 2.0]), ... ea=np.array([5, 7.0]), xlog=True, ylog=True) >>> a.e_from_k(1.25) 5.572... >>> a.e_from_k(np.array([1.25, 1.75])) array([5.572..., 6.560...]) Increasing vs decreasing inputs: >>> ea = np.arange(1,10) >>> ka = 3 * ea >>> np.isclose(PwiseLinearPermeabilityModel(ka, ea).e_from_k(7.2), ... PwiseLinearPermeabilityModel(ka[::-1], ea[::-1]).e_from_k(7.2)) True """ if self.xlog: ka = self.log_ka k = np.log10(k) else: ka = self.ka if self.ylog: ea = self.log_ea else: ea = self.ea e = np.interp(k, ka[self.ka_slice], ea[self.ka_slice]) if self.ylog: e = np.power(10, e) return e
[docs] def k_from_e(self, e, **kwargs): """Permeability from void ratio Parameters ---------- e : float Void ratio. Returns ------- k : float Permeability corresponding to current void ratio. Examples -------- >>> a = PwiseLinearPermeabilityModel(ka=np.array([1.0, 2.0]), ea=np.array([5, 7.0])) >>> a.k_from_e(5.5) 1.25 Array Inputs: >>> a.k_from_e(np.array([5.5, 6.5])) array([1.25, 1.75]) Logarithmic permeability scale: >>> a = PwiseLinearPermeabilityModel(ka=np.array([1.0, 2.0]), ... ea=np.array([5, 7.0]), xlog=True) >>> a.k_from_e(5.644) 1.25... >>> a.k_from_e(np.array([5.644, 6.615])) array([1.25..., 1.75...]) Logarithmic void ratio scale: >>> a = PwiseLinearPermeabilityModel(ka=np.array([1.0, 2.0]), ... ea=np.array([5, 7.0]), ylog=True) >>> a.k_from_e(5.4388) 1.25... >>> a.k_from_e(np.array([5.4388, 6.436])) array([1.25..., 1.75...]) Logarithmic permeability and void ratio scale: >>> a = PwiseLinearPermeabilityModel(ka=np.array([1.0, 2.0]), ... ea=np.array([5, 7.0]), xlog=True, ylog=True) >>> a.k_from_e(5.573) 1.25... >>> a.k_from_e(np.array([5.573, 6.561])) array([1.25..., 1.75...]) Increasing vs decreasing inputs: >>> ea = np.arange(1,10) >>> ka = 3 * ea >>> np.isclose(PwiseLinearPermeabilityModel(ka, ea).k_from_e(3.0), ... PwiseLinearPermeabilityModel(ka[::-1], ea[::-1]).k_from_e(3.0)) True """ if self.xlog: ka = self.log_ka else: ka = self.ka if self.ylog: ea = self.log_ea e = np.log10(e) else: ea = self.ea k = np.interp(e, ea[self.ea_slice], ka[self.ea_slice]) if self.xlog: k = np.power(10, k) return k
[docs] def e_and_k_for_plotting(self, **kwargs): """Void ratio and permeability that plot the model Parameters ---------- npts : int, optional Number of points to return. Default npts=100. xmin, xmax : float, optional Range of x (i.e. effective stress) values from which to return points. Default min and max of model `ka`. Returns ------- x, y : 1d ndarray `npts` permeability, and void ratio values between `xmin` and `xmax`. """ npts = kwargs.get('npts', 100) xmin, xmax = np.min(self.ka), np.max(self.ka) x = np.linspace(xmin, xmax, npts) y = self.e_from_k(x) return x, y
[docs]class FunctionPermeabilityModel(PermeabilityVoidRatioRelationship): """Functional definition of permeability void-ratio realationship User provides python functions. Parameters ---------- fn_e_from_k: callable object Function to obtain void ratio from permeability. fn_e_from_k should be the inverse function of fn_k_from_e. fn_k_from_e : callable object Function to obtain peremability from void ratio. fn_k_from_e should be the inverse function of fn_e_from_k. *args, **kwargs : anything Positional and keyword arguments to be passed to the fn_e_from_k, fn_k_from_efunctions. Note that any additional args and kwargs passed to the functions will be appended to the args, and kwargs. You may get into a mess for example with e_from_k because normally the first postional argument passed to such fucntions is k. If you add your own positional arguments, then k will be after you own arguments. Just be aware that the usual way to call methods of the base object `PermeabilityVoidRatioRelationship` a single positonal arguement, e.g. k, e, followed by any required keyword arguments. Notes ----- Any function should be able to accept additonal keywords. Examples -------- >>> def efk(k, b=2): ... return k * b >>> def kfe(e, b=2): ... return e / b >>> a = FunctionPermeabilityModel(efk, kfe, b=5) >>> a.e_from_k(3) 15 >>> a.k_from_e(15) 3.0 """ def __init__(self, fn_e_from_k, fn_k_from_e, *args, **kwargs): self.fn_e_from_k = fn_e_from_k self.fn_k_from_e = fn_k_from_e self.args = args self.kwargs = kwargs self.e_from_k = functools.partial(fn_e_from_k, *args, **kwargs) self.k_from_e = functools.partial(fn_k_from_e, *args, **kwargs) return
if __name__ == '__main__': import nose nose.runmodule(argv=['nose', '--verbosity=3', '--with-doctest', '--doctest-options=+ELLIPSIS']) pass # a = CkPermeabilityModel(Ck=1.5, ka=10, ea=4) ## b = a.e_from_k(8.0) ## print(b) # a.plot_model() # plt.show()