SA-PINNs / scripts /problems.py
OneScience's picture
Upload folder using huggingface_hub
1f85b04 verified
Raw
History Blame Contribute Delete
6.51 kB
from __future__ import annotations
from collections.abc import Mapping
import numpy as np
import torch
from model.sa_pinn import Burgers2D, Equation, Helmholtz2D, Laplace1D
CASES = ("laplace", "helmholtz", "burgers")
def exact_solution(case: str, coordinates: np.ndarray) -> np.ndarray | None:
if case == "laplace":
return np.sin(np.pi * coordinates[:, 0:1])
if case == "helmholtz":
return np.sin(np.pi * coordinates[:, 0:1]) * np.sin(
4.0 * np.pi * coordinates[:, 1:2]
)
if case == "burgers":
return None
raise ValueError(f"unsupported case: {case}")
def build_equation(case: str, data_config: Mapping) -> Equation:
if case == "laplace":
return Laplace1D()
if case == "helmholtz":
return Helmholtz2D(float(data_config["wave_number"]))
if case == "burgers":
return Burgers2D(float(data_config["viscosity"]))
raise ValueError(f"unsupported case: {case}")
def generate_data(case: str, config: Mapping, seed: int) -> dict[str, np.ndarray | None]:
generator = np.random.default_rng(seed)
n_pde = int(config["n_pde"])
n_solution = int(config["n_sol"])
n_boundary = int(config["n_bnd"])
test_resolution = int(config["test_res"])
if min(n_pde, n_solution, n_boundary, test_resolution) <= 0:
raise ValueError("all data counts must be positive")
if case == "laplace":
lower, upper = map(float, config["domain"])
x_pde = generator.uniform(lower, upper, (n_pde, 1))
x_boundary = np.array([[lower], [upper]], dtype=np.float64)
u_boundary = exact_solution(case, x_boundary)
x_data = np.linspace(lower, upper, n_solution)[:, None]
u_data = exact_solution(case, x_data)
noise_std = float(config.get("noise_std", 0.0))
if noise_std:
u_data = u_data + noise_std * generator.standard_normal(u_data.shape)
x_test = np.linspace(lower, upper, test_resolution)[:, None]
test_shape = (test_resolution,)
elif case == "helmholtz":
x_lower, x_upper = map(float, config["domain_x"])
y_lower, y_upper = map(float, config["domain_y"])
x_pde = np.column_stack(
(
generator.uniform(x_lower, x_upper, n_pde),
generator.uniform(y_lower, y_upper, n_pde),
)
)
if n_boundary < 4 or n_boundary % 4:
raise ValueError("Helmholtz n_bnd must be divisible by 4 and at least 4")
per_edge = n_boundary // 4
x_axis = np.linspace(x_lower, x_upper, per_edge)
y_axis = np.linspace(y_lower, y_upper, per_edge)
x_boundary = np.vstack(
(
np.column_stack((x_axis, np.full(per_edge, y_lower))),
np.column_stack((x_axis, np.full(per_edge, y_upper))),
np.column_stack((np.full(per_edge, x_lower), y_axis)),
np.column_stack((np.full(per_edge, x_upper), y_axis)),
)
)
u_boundary = exact_solution(case, x_boundary)
x_data = np.column_stack(
(
generator.uniform(x_lower, x_upper, n_solution),
generator.uniform(y_lower, y_upper, n_solution),
)
)
u_data = exact_solution(case, x_data)
grid_x = np.linspace(x_lower, x_upper, test_resolution)
grid_y = np.linspace(y_lower, y_upper, test_resolution)
mesh_x, mesh_y = np.meshgrid(grid_x, grid_y, indexing="xy")
x_test = np.column_stack((mesh_x.ravel(), mesh_y.ravel()))
test_shape = mesh_x.shape
elif case == "burgers":
x_lower, x_upper = map(float, config["domain_x"])
t_lower, t_upper = map(float, config["domain_t"])
x_pde = np.column_stack(
(
generator.uniform(x_lower, x_upper, n_pde),
generator.uniform(t_lower, t_upper, n_pde),
)
)
if n_boundary < 2:
raise ValueError("Burgers n_bnd must be at least 2")
left_count = n_boundary // 2
right_count = n_boundary - left_count
left_time = generator.uniform(t_lower, t_upper, left_count)
right_time = generator.uniform(t_lower, t_upper, right_count)
x_boundary = np.vstack(
(
np.column_stack((np.full(left_count, x_lower), left_time)),
np.column_stack((np.full(right_count, x_upper), right_time)),
)
)
u_boundary = np.zeros((n_boundary, 1))
initial_x = generator.uniform(x_lower, x_upper, n_solution)
x_data = np.column_stack((initial_x, np.full(n_solution, t_lower)))
u_data = -np.sin(np.pi * initial_x)[:, None]
grid_x = np.linspace(x_lower, x_upper, test_resolution)
grid_t = np.linspace(t_lower, t_upper, test_resolution)
mesh_x, mesh_t = np.meshgrid(grid_x, grid_t, indexing="xy")
x_test = np.column_stack((mesh_x.ravel(), mesh_t.ravel()))
test_shape = mesh_x.shape
else:
raise ValueError(f"unsupported case: {case}")
return {
"x_pde": np.asarray(x_pde, dtype=np.float64),
"x_boundary": np.asarray(x_boundary, dtype=np.float64),
"u_boundary": np.asarray(u_boundary, dtype=np.float64),
"x_data": np.asarray(x_data, dtype=np.float64),
"u_data": np.asarray(u_data, dtype=np.float64),
"x_test": np.asarray(x_test, dtype=np.float64),
"u_exact": exact_solution(case, np.asarray(x_test, dtype=np.float64)),
"test_shape": test_shape,
}
def to_tensors(
data: Mapping[str, np.ndarray | None],
device: torch.device,
dtype: torch.dtype,
) -> dict[str, torch.Tensor | None]:
return {
key: (
torch.as_tensor(value, dtype=dtype, device=device)
if isinstance(value, np.ndarray)
else None
)
for key, value in data.items()
if key != "test_shape"
}
def point_counts(data: Mapping[str, np.ndarray | None]) -> dict[str, int]:
return {
"pde": int(data["x_pde"].shape[0]),
"boundary": int(data["x_boundary"].shape[0]),
"data": int(data["x_data"].shape[0]) if data.get("x_data") is not None else 0,
}
def relative_l2(prediction: np.ndarray, reference: np.ndarray | None) -> float | None:
if reference is None:
return None
return float(
np.linalg.norm(prediction.reshape(-1) - reference.reshape(-1))
/ (np.linalg.norm(reference.reshape(-1)) + 1.0e-12)
)