LDA1020's picture
feat: code release
56c4b9b verified
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()