File size: 3,817 Bytes
56c4b9b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
import numpy as np
from scipy.fftpack import idct
from scipy.sparse import diags
from scipy.interpolate import RectBivariateSpline
from scipy.sparse.linalg import spsolve
from scipy.sparse import diags, block_diag, bmat


import matplotlib.pyplot as plt

def GRF(alpha, tau, s):
    # Random variables in KL expansion
    xi = np.random.normal(0, 1, (s, s))
    
    # Define the (square root of) eigenvalues of the covariance operator
    K1, K2 = np.meshgrid(np.arange(s), np.arange(s))
    coef = tau**(alpha - 1) * (np.pi**2 * (K1**2 + K2**2) + tau**2)**(-alpha / 2)
    
    # Construct the KL coefficients
    L = s * coef * xi
    L[0, 0] = 0  # Ensure mean is 0
    
    # Perform the inverse discrete cosine transform
    U = idct(idct(L, axis=0, norm='ortho'), axis=1, norm='ortho')
    
    return U


def solve_gwf(coef, F):
    K = len(coef)

    # Create meshgrids for interpolation
    X1, Y1 = np.meshgrid(np.linspace(1/(2*K), (2*K-1)/(2*K), K), 
                          np.linspace(1/(2*K), (2*K-1)/(2*K), K))
    X2, Y2 = np.meshgrid(np.linspace(0, 1, K), np.linspace(0, 1, K))

    # Interpolate coef and F
    interp_coef = RectBivariateSpline(X1[0, :], Y1[:, 0], coef, kx=3, ky=3)
    coef = interp_coef(X2[0, :], Y2[:, 0], grid=True)

    interp_F = RectBivariateSpline(X1[0, :], Y1[:, 0], F, kx=3, ky=3)
    F = interp_F(X2[0, :], Y2[:, 0], grid=True)

    # Trim F for the inner grid
    F = F[1:K-1, 1:K-1]

    N = (K-2)  # Size of inner grid

    # Initialize d with zero sparse matrices instead of None
    d = [[diags([np.zeros(N)], [0], shape=(N, N)) for _ in range(N)] for _ in range(N)]

    # Construct sparse matrix system
    for j in range(1, K-1):
        main_diag = (coef[:-2, j] + coef[1:-1, j])/2 + (coef[2:, j] + coef[1:-1, j])/2 \
                    + (coef[1:-1, j-1] + coef[1:-1, j])/2 + (coef[1:-1, j+1] + coef[1:-1, j])/2
        upper_diag = -(coef[1:-2, j] + coef[2:-1, j]) / 2
        lower_diag = upper_diag

        d[j-1][j-1] = diags([lower_diag, main_diag, upper_diag], offsets=[-1, 0, 1], shape=(N, N))

        if j != K-2:
            off_diag = - (coef[1:-1, j] + coef[1:-1, j+1]) / 2
            d[j-1][j] = diags([off_diag], offsets=[0], shape=(N, N))
            d[j][j-1] = d[j-1][j]

    # Convert d into a sparse block matrix
    A = bmat(d, format='csr') * (K-1)**2

    # Flatten F correctly for spsolve
    F_flat = F.flatten()

    # Solve the sparse system
    P_inner = spsolve(A, F_flat).reshape((N, N))

    # Construct full solution grid
    P = np.zeros((K, K))
    P[1:-1, 1:-1] = P_inner

    # Interpolate the solution back to original grid
    interp_P = RectBivariateSpline(X2[0, :], Y2[:, 0], P, kx=3, ky=3)
    P = interp_P(X1[0, :], Y1[:, 0], grid=True).T

    return P


def solve_darcy(alpha=2, tau=3, s=256):
    """
    Solve the Darcy equation using the GWF method
    tau and alpha are parameters of the covariance C = tau^(2*alpha-2)*(-Laplacian + tau^2 I)^(-alpha)
    Note that we need alpha > d/2 (here d=2)
    Laplacian has zero Neumann boundary
    alpha and tau control smoothness; the bigger they are, the smoother the function
    """
    # Number of grid points on [0,1]^2
    # i.e., uniform mesh with step h=1/(s-1)

    # Create mesh (only needed for plotting)
    x = np.linspace(0, 1, s)
    y = np.linspace(0, 1, s)
    X, Y = np.meshgrid(x, y)

    # Generate random coefficients from N(0,C)
    norm_a = GRF(alpha, tau, s)

    # Another way to achieve ellipticity is to threshold the coefficients
    thresh_a = np.where(norm_a >= 0, 12, 4)

    # Forcing function, f(x) = 1 
    f = np.ones((s, s))

    # Solve PDE: - div(a(x)*grad(p(x))) = f(x)
    # lognorm_p = solve_gwf(lognorm_a, f)
    thresh_p = solve_gwf(thresh_a, f)

    return thresh_p

if __name__ == "__main__":
    thresh_p = solve_darcy()