Exemplo n.º 1
0
 def test_error(self):
     self.err_mat = np.array([[3, 2, 1, 0], [-3, -2, 7, 1], [3, 2, -1, 5],
                              [0, 1, 2, 3]])
     self.err_rhs = np.array([6, 3, 9, 6])
     sol_2 = pp.solve(self.err_mat, self.err_rhs, is_flat=False, solver=2)
     diff_2 = np.max(np.abs(np.dot(self.err_mat, sol_2) - self.err_rhs))
     self.assertAlmostEqual(diff_2, 0.0)
Exemplo n.º 2
0
 def solve_linear(self, option = 0):
     """
     solves the assembled system using either pentapy or numpy linalg solve
     """
     if option == 0:
         self.c = pp.solve(self.A,self.b, is_flat = False)
     else:
         self.c = np.linalg.solve(self.A,self.b)
Exemplo n.º 3
0
def pentapy_solve(A,B):
    #extracting the diagonals
    second_super = [np.append(A.diagonal(2),[0.0, 0.0])]   #second subdiagonal
    first_super  = [np.append(A.diagonal(1),[0.0])]        #first subdiagonal
    main         = [A.diagonal()]                          #main diagonal
    first_sub    = [np.append([0.0],A.diagonal(-1))]       #first superdiagonal
    second_sub   = [np.append([0.0, 0.0],A.diagonal(-2))]  #second superdiagonal
    A_diagflat   = np.concatenate((second_super, first_super,main,first_sub,second_sub), axis=0)
    
    #solving the system of equation
    X = pp.solve(A_diagflat, B, is_flat=True)
    return X;
Exemplo n.º 4
0
    def test_solve2(self):
        self.mat_col = pp.shift_banded(self.mat, col_to_row=False)
        self.mat_ful = pp.create_full(self.mat, col_wise=False)

        sol_row = pp.solve(self.mat, self.rhs, is_flat=True, solver=2)
        sol_col = pp.solve(
            self.mat_col,
            self.rhs,
            is_flat=True,
            index_row_wise=False,
            solver=2,
        )
        sol_ful = pp.solve(self.mat_ful, self.rhs, solver=2)

        diff_row = np.max(np.abs(np.dot(self.mat_ful, sol_row) - self.rhs))
        diff_col = np.max(np.abs(np.dot(self.mat_ful, sol_col) - self.rhs))
        diff_ful = np.max(np.abs(np.dot(self.mat_ful, sol_ful) - self.rhs))

        diff_row_col = np.max(
            np.abs(self.mat_ful - pp.create_full(self.mat_col)))
        self.assertAlmostEqual(diff_row * 1e-5, 0.0)
        self.assertAlmostEqual(diff_col * 1e-5, 0.0)
        self.assertAlmostEqual(diff_ful * 1e-5, 0.0)
        self.assertAlmostEqual(diff_row_col * 1e5, 0.0)
Exemplo n.º 5
0
# -*- coding: utf-8 -*-
"""
5. Example: Failing Corner Cases
--------------------------------

Here we demonstrate that the solver PTRANS-I can fail to solve a given system.
A warning is given in that case and the output will be a nan-array.
"""
import numpy as np
import pentapy as pp

# create a full pentadiagonal matrix
mat = np.array([[3, 2, 1, 0], [-3, -2, 7, 1], [3, 2, -1, 5], [0, 1, 2, 3]])
V = np.array([6, 3, 9, 6])
# solve the LES with mat as a qudratic input matrix
X = pp.solve(mat, V, is_flat=False, solver=1)
print(X)
Exemplo n.º 6
0
def solve_4(in_val):
    mat, V = in_val
    return solve(mat, V, is_flat=False, solver=4)
Exemplo n.º 7
0
def solve_2(in_val):
    """PTRANS-II"""
    mat, V = in_val
    return solve(mat, V, is_flat=False, solver=2)
Exemplo n.º 8
0
# -*- coding: utf-8 -*-
"""
1. Example: Solving
-------------------

Here we create a random row wise flattened matrix M_flat and a random
right hand side for the pentadiagonal equation system.

After solving we calculate the absolute difference between the right hand side
and the product of the matrix (which is transformed to a full quadratic one)
and the solution of the system.
"""
import numpy as np
import pentapy as pp

size = 1000
# create a flattened pentadiagonal matrix
M_flat = (np.random.random((5, size)) - 0.5) * 1e-5
V = np.random.random(size) * 1e5
# solve the LES with M_flat as row-wise flattened matrix
X = pp.solve(M_flat, V, is_flat=True)

# create the corresponding matrix for checking
M = pp.create_full(M_flat, col_wise=False)
# calculate the error
print(np.max(np.abs(np.dot(M, X) - V)))
Exemplo n.º 9
0
def grf_laplace(
    s,
    rad=None,
    S_part=None,
    K_part=None,
    R_part=None,
    dim=2,
    lat_ext=1.0,
    rate=None,
    K_well=None,
    cut_off_prec=1e-20,
    cond=0,
    cond_kw=None,
):
    """
    The extended GRF-model for transient flow in Laplace-space.

    The General Radial Flow (GRF) Model allowes fractured dimensions for
    transient flow under a pumping condition in a confined aquifer.
    The solutions assumes concentric annuli around the pumpingwell,
    where each annulus has its own conductivity and storativity value.

    Parameters
    ----------
    s : :class:`numpy.ndarray`
        Array with all Laplace-space-points
        where the function should be evaluated
    rad : :class:`numpy.ndarray`
        Array with all radii where the function should be evaluated
    S_part : :class:`numpy.ndarray` of length N
        Given storativity values for each disk
    K_part : :class:`numpy.ndarray` of length N
        Given conductivity values for each disk
    R_part : :class:`numpy.ndarray` of length N+1
        Given radii separating the disks as well as starting- and endpoints
    dim : :class:`float`
        Flow dimension. Default: 3
    lat_ext : :class:`float`
        The lateral extend of the flow-domain, used in `L^(3-dim)`. Default: 1
    rate : :class:`float`
        Pumpingrate at the well
    K_well : :class:`float`, optional
        Conductivity at the well. Default: ``K_part[0]``
    cut_off_prec : :class:`float`, optional
        Define a cut-off precision for the calculation to select the disks
        included in the calculation. Default ``1e-20``
    cond : :class:`int`, optional
        Type of the pumping condition:

            * 0 : constant
            * 1 : periodic (needs "w" as cond_kw)
            * 2 : slug (rate will be interpreted as slug-volume)
            * 3 : interval (needs "t" as cond_kw)
            * callable: laplace-transformation of the transient pumping-rate

        Default: 0
    cond_kw : :class:`dict` optional
        Keyword args for the pumping condition. Default: None

    Returns
    -------
    grf_laplace : :class:`numpy.ndarray`
        Array with all values in laplace-space

    Examples
    --------
    >>> grf_laplace([5,10],[1,2,3],[1e-3,1e-3],[1e-3,2e-3],[0,2,10], 2, 1, -1)
    array([[-2.71359196e+00, -1.66671965e-01, -2.82986917e-02],
           [-4.58447458e-01, -1.12056319e-02, -9.85673855e-04]])
    """
    cond_kw = {} if cond_kw is None else cond_kw
    cond = cond if callable(cond) else PUMP_COND[cond]
    # ensure that input is treated as arrays
    s = np.squeeze(s).reshape(-1)
    rad = np.squeeze(rad).reshape(-1)
    S_part = np.squeeze(S_part).reshape(-1)
    K_part = np.squeeze(K_part).reshape(-1)
    R_part = np.squeeze(R_part).reshape(-1)
    # the dimension is used by nu in the literature (See Barker 88)
    dim = float(dim)
    nu = 1.0 - dim / 2.0
    nu1 = nu - 1
    # the lateral extend is a bit subtle in fractured dimension
    lat_ext = float(lat_ext)
    rate = float(rate)
    # get the number of partitions
    parts = len(K_part)
    # set the conductivity at the well
    K_well = K_part[0] if K_well is None else float(K_well)
    # check the input
    if not len(R_part) - 1 == len(S_part) == len(K_part) > 0:
        raise ValueError("R_part, S_part and K_part need matching lengths.")
    if R_part[0] < 0.0:
        raise ValueError("The wellradius needs to be >= 0.")
    if not all([r1 < r2 for r1, r2 in zip(R_part[:-1], R_part[1:])]):
        raise ValueError("The radii values need to be sorted.")
    if not np.min(rad) > R_part[0] or np.max(rad) > R_part[-1]:
        raise ValueError("The given radii need to be in the given range.")
    if not all([con > 0 for con in K_part]):
        raise ValueError("The Conductivity needs to be positiv.")
    if not all([stor > 0 for stor in S_part]):
        raise ValueError("The Storage needs to be positiv.")
    if not dim > 0.0 or dim > 3.0:
        raise ValueError("The dimension needs to be positiv and <= 3.")
    if not lat_ext > 0.0:
        raise ValueError("The lateral extend needs to be positiv.")
    if not K_well > 0:
        raise ValueError("The well conductivity needs to be positiv.")

    # initialize the result
    res = np.zeros(s.shape + rad.shape)
    # the first sqrt of the diffusivity values
    diff_sr0 = np.sqrt(S_part[0] / K_part[0])
    # set the general pumping-condtion depending on the well-radius
    if R_part[0] > 0.0:
        Qs = -s ** (-0.5) / diff_sr0 * R_part[0] ** nu1 * cond(s, **cond_kw)
    else:
        Qs = -(2 / diff_sr0) ** nu * s ** (-nu / 2) * cond(s, **cond_kw)

    # if there is a homgeneouse aquifer, compute the result by hand
    if parts == 1:
        # initialize the equation system
        V = np.zeros(2, dtype=float)
        M = np.array([[-gamma(1 - nu), 2.0 / gamma(nu)], [0, 1]])

        for si, se in enumerate(s):
            Cs = np.sqrt(se) * diff_sr0
            # set the pumping-condition at the well
            V[0] = Qs[si]
            # incorporate the boundary-conditions
            if R_part[0] > 0.0:
                M[0, :] = [-kv(nu1, Cs * R_part[0]), iv(nu1, Cs * R_part[0])]
            if R_part[-1] < np.inf:
                M[1, :] = [kv(nu, Cs * R_part[-1]), iv(nu, Cs * R_part[-1])]
            else:
                M[0, 1] = 0  # Bs is 0 in this case either way
            # solve the equation system
            As, Bs = np.linalg.solve(M, V)

            # calculate the head
            for ri, re in enumerate(rad):
                if re < R_part[-1]:
                    res[si, ri] = re ** nu * (
                        As * kv(nu, Cs * re) + Bs * iv(nu, Cs * re)
                    )

    # if there is more than one partition, create an equation system
    else:
        # initialize LHS and RHS for the linear equation system
        # Mb is the banded matrix for the Eq-System
        V = np.zeros(2 * parts)
        Mb = np.zeros((5, 2 * parts))
        X = np.zeros(2 * parts)
        # set the standard boundary conditions for rwell=0.0 and rinf=np.inf
        Mb[2, 0] = -gamma(1 - nu)
        Mb[1, 1] = 2.0 / gamma(nu)
        Mb[2, -1] = 1.0

        # calculate the consecutive fractions of the conductivities
        Kfrac = K_part[:-1] / K_part[1:]
        # calculate the square-root of the diffusivities
        difsr = np.sqrt(S_part / K_part)
        # calculate a temporal substitution (factor from mass-conservation)
        tmp = Kfrac * difsr[:-1] / difsr[1:]
        # match the radii to the different disks
        pos = np.searchsorted(R_part, rad) - 1

        # iterate over the laplace-variable
        for si, se in enumerate(s):
            Cs = np.sqrt(se) * difsr

            # set the pumping-condition at the well
            # --> implement other pumping conditions
            V[0] = Qs[si]

            # generate the equation system as banded matrix
            for i in range(parts - 1):
                Mb[0, 2 * i + 3] = -iv(nu, Cs[i + 1] * R_part[i + 1])
                Mb[1, 2 * i + 2 : 2 * i + 4] = [
                    -kv(nu, Cs[i + 1] * R_part[i + 1]),
                    -iv(nu1, Cs[i + 1] * R_part[i + 1]),
                ]
                Mb[2, 2 * i + 1 : 2 * i + 3] = [
                    iv(nu, Cs[i] * R_part[i + 1]),
                    kv(nu1, Cs[i + 1] * R_part[i + 1]),
                ]
                Mb[3, 2 * i : 2 * i + 2] = [
                    kv(nu, Cs[i] * R_part[i + 1]),
                    tmp[i] * iv(nu1, Cs[i] * R_part[i + 1]),
                ]
                Mb[4, 2 * i] = -tmp[i] * kv(nu1, Cs[i] * R_part[i + 1])

            # set the boundary-conditions if needed
            if R_part[0] > 0.0:
                Mb[2, 0] = -kv(nu1, Cs[0] * R_part[0])
                Mb[1, 1] = iv(nu1, Cs[0] * R_part[0])
            if R_part[-1] < np.inf:
                Mb[-2, -2] = kv(nu, Cs[-1] * R_part[-1])
                Mb[2, -1] = iv(nu, Cs[-1] * R_part[-1])
            else:  # erase the last row, since X[-1] will be 0
                Mb[0, -1] = 0
                Mb[1, -1] = 0

            # find first disk which has no impact
            Mb_cond = np.max(np.abs(Mb), axis=0)
            Mb_cond_lo = Mb_cond < cut_off_prec
            Mb_cond_hi = Mb_cond > 1 / cut_off_prec
            Mb_cond = np.logical_or(Mb_cond_lo, Mb_cond_hi)
            cond = np.where(Mb_cond)[0]
            found = cond.shape[0] > 0
            first = cond[0] // 2 if found else parts

            # initialize coefficients
            X[2 * first :] = 0.0
            # only the first disk has an impact
            if first <= 1:
                M_sgl = np.eye(2, dtype=float)
                M_sgl[:, 0] = Mb[2:4, 0]
                M_sgl[:, 1] = Mb[1:3, 1]
                # solve the equation system
                try:
                    X[:2] = np.linalg.solve(M_sgl, V[:2])
                except np.linalg.LinAlgError:
                    # set 0 if matrix singular
                    X[:2] = 0
            elif first > 1:
                # shrink the matrix
                M_sgl = Mb[:, : 2 * first]
                if first < parts:
                    M_sgl[-1, -1] = 0
                    M_sgl[-2, -1] = 0
                    M_sgl[-1, -2] = 0
                X[: 2 * first] = solve(
                    M_sgl, V[: 2 * first], is_flat=True, index_row_wise=False
                )
            np.nan_to_num(X, copy=False)

            # calculate the head (ignore small values)
            with warnings.catch_warnings():
                warnings.simplefilter("ignore", RuntimeWarning)
                k0_sub = X[2 * pos] * kv(nu, Cs[pos] * rad)
                k0_sub[np.abs(X[2 * pos]) < cut_off_prec] = 0
                i0_sub = X[2 * pos + 1] * iv(nu, Cs[pos] * rad)
                i0_sub[np.abs(X[2 * pos + 1]) < cut_off_prec] = 0
                res[si, :] = rad ** nu * (k0_sub + i0_sub)

    # set problematic values to 0
    # --> the algorithm tends to violate small values,
    #     therefore this approach is suitable
    np.nan_to_num(res, copy=False)
    # scale to pumpingrate
    res *= rate / (K_well * sph_surf(dim) * lat_ext ** (3.0 - dim))

    return res
Exemplo n.º 10
0
# -*- coding: utf-8 -*-
"""
2. Example: Comparison
----------------------

Here we compare the outcome of the PTRANS-I and PTRANS-II algorithm for
a random input.
"""
import numpy as np
import pentapy as pp

size = 1000
# create a flattened pentadiagonal matrix
M_flat = (np.random.random((5, size)) - 0.5) * 1e-5
V = np.random.random(size) * 1e5
# compare the two solvers
X1 = pp.solve(M_flat, V, is_flat=True, solver=1)
X2 = pp.solve(M_flat, V, is_flat=True, solver=2)

# calculate the error
print("rel. diff X1 X2: ", np.max(np.abs(X1 - X2)) / np.max(np.abs(X1 + X2)))
print("max X1: ", np.max(np.abs(X1)))
print("max X2: ", np.max(np.abs(X2)))

M = pp.create_full(M_flat, col_wise=False)
# calculate the error
print("max |M*X1 - V|: ", np.max(np.abs(np.dot(M, X1) - V)))
print("max |M*X2 - V|: ", np.max(np.abs(np.dot(M, X2) - V)))
Exemplo n.º 11
0
def solve_4(in_val):
    mat, V = in_val
    return solve(mat, V, is_flat=True, index_row_wise=False, solver=4)
Exemplo n.º 12
0
def solve_2(in_val):
    """PTRANS-II"""
    mat, V = in_val
    return solve(mat, V, is_flat=True, index_row_wise=False, solver=2)