CodePDE / solvers /darcy /beta_1.0 /seeds /implementation_1.py
LDA1020's picture
feat: code release
56c4b9b verified
import numpy as np
from scipy import sparse
from scipy.sparse.linalg import spsolve
from concurrent.futures import ProcessPoolExecutor
import time
def solver(a):
"""Solve the Darcy equation.
Args:
a (np.ndarray): Shape [batch_size, N, N], the coefficient of the Darcy equation,
where N denotes the number of spatial grid points in each direction.
Returns:
solutions (np.ndarray): Shape [batch_size, N, N].
"""
start_time = time.time()
batch_size, N, _ = a.shape
# Create the solutions array
solutions = np.zeros((batch_size, N, N), dtype=np.float64)
# Process each batch element
if batch_size > 1:
print(f"Solving {batch_size} PDE problems with grid size {N}x{N}")
# For larger batches, use parallel processing
with ProcessPoolExecutor() as executor:
# Create a list of tasks
futures = [executor.submit(solve_single_problem, a[i], N) for i in range(batch_size)]
# Collect results
for i, future in enumerate(futures):
solutions[i] = future.result()
else:
# For a single problem, solve directly
solutions[0] = solve_single_problem(a[0], N)
elapsed_time = time.time() - start_time
print(f"Total solving time: {elapsed_time:.4f} seconds")
return solutions
def solve_single_problem(a_coeff, N):
"""Solve a single Darcy flow problem.
Args:
a_coeff (np.ndarray): Shape [N, N], coefficient function
N (int): Number of grid points in each direction
Returns:
u (np.ndarray): Shape [N, N], solution
"""
# Set up the grid
h = 1.0 / (N - 1) # Grid spacing
# Construct the sparse matrix for the linear system
# We'll use the 5-point stencil for the Laplacian
main_diag = np.zeros(N*N)
upper_diag = np.zeros(N*N-1)
lower_diag = np.zeros(N*N-1)
upper_N_diag = np.zeros(N*N-N)
lower_N_diag = np.zeros(N*N-N)
# Right-hand side vector
rhs = np.ones(N*N)
# Apply boundary conditions and set up the linear system
for i in range(N):
for j in range(N):
idx = i*N + j
# Handle boundary conditions
if i == 0 or i == N-1 or j == 0 or j == N-1:
main_diag[idx] = 1.0
rhs[idx] = 0.0 # u = 0 on the boundary
else:
# Get coefficients at neighboring points
a_center = a_coeff[i, j]
a_west = a_coeff[i, j-1]
a_east = a_coeff[i, j+1]
a_north = a_coeff[i-1, j]
a_south = a_coeff[i+1, j]
# Average coefficients at interfaces
a_w = 0.5 * (a_center + a_west)
a_e = 0.5 * (a_center + a_east)
a_n = 0.5 * (a_center + a_north)
a_s = 0.5 * (a_center + a_south)
# Set diagonal entries
main_diag[idx] = (a_w + a_e + a_n + a_s) / (h*h)
# Set off-diagonal entries
if j > 0: # West
lower_diag[idx-1] = -a_w / (h*h)
if j < N-1: # East
upper_diag[idx] = -a_e / (h*h)
if i > 0: # North
lower_N_diag[idx-N] = -a_n / (h*h)
if i < N-1: # South
upper_N_diag[idx] = -a_s / (h*h)
# Construct the sparse matrix
diagonals = [main_diag, upper_diag, lower_diag, upper_N_diag, lower_N_diag]
offsets = [0, 1, -1, N, -N]
A = sparse.diags(diagonals, offsets, shape=(N*N, N*N), format='csr')
# Solve the linear system
u_flat = spsolve(A, rhs)
# Reshape the solution to 2D
u = u_flat.reshape((N, N))
return u