FAST-DeepONet-DarcyFlow / scripts /generate_data.py
yushuang88's picture
Upload folder using huggingface_hub
b887635 verified
Raw History Blame Contribute Delete
5.62 kB
#!/usr/bin/env python3
"""Generate the FAST-DeepONet Darcy inclusion benchmark from a fixed seed."""
from __future__ import annotations
import argparse
import json
from pathlib import Path
import numpy as np
from scipy import sparse
from scipy.sparse.linalg import spsolve
GRID = 64
SEED = 33
SPLITS = (("train", 60), ("tune", 10), ("calib", 20), ("test", 10))
OFFICIAL_COMMIT = "9541c112176c42470cd39138dacbab8af6892232"
def make_basis(x: np.ndarray, y: np.ndarray) -> np.ndarray:
modes = (
(1, 0, 0, 0), (0, 1, 0, 0), (1, 1, 0, 0), (1, 0, 1, 0),
(0, 1, 0, 1), (1, 1, 1, 1), (2, 0, 0, 0), (0, 2, 0, 0),
)
fields = []
for kx, ky, px, py in modes:
x_wave = np.cos(2 * np.pi * kx * x + px * np.pi / 2) if kx else 1.0
y_wave = np.cos(2 * np.pi * ky * y + py * np.pi / 2) if ky else 1.0
field = x_wave * y_wave
fields.append(field / np.max(np.abs(field)))
return np.stack(fields)
def site_locations(rng: np.random.Generator) -> np.ndarray:
axis = np.linspace(0.2, 0.8, 4)
candidates = np.array([(a, b) for a in axis for b in axis])
sites = candidates[rng.permutation(len(candidates))[:10]]
return sites + rng.uniform(-0.04, 0.04, size=sites.shape)
def solve_pressure(log_coefficient: np.ndarray) -> np.ndarray:
coefficient = np.exp(log_coefficient)
inner = coefficient[1:-1, 1:-1]
harmonic = lambda neighbor: 2 * inner * neighbor / (inner + neighbor)
east = harmonic(coefficient[2:, 1:-1])
west = harmonic(coefficient[:-2, 1:-1])
north = harmonic(coefficient[1:-1, 2:])
south = harmonic(coefficient[1:-1, :-2])
side = GRID - 2
nodes = np.arange(side * side).reshape(side, side)
spacing_squared = (1.0 / GRID) ** 2
connections = (
(nodes[:-1, :], nodes[1:, :], east[:-1, :]),
(nodes[1:, :], nodes[:-1, :], west[1:, :]),
(nodes[:, :-1], nodes[:, 1:], north[:, :-1]),
(nodes[:, 1:], nodes[:, :-1], south[:, 1:]),
)
row = np.concatenate([nodes.ravel()] + [src.ravel() for src, _, _ in connections])
col = np.concatenate([nodes.ravel()] + [dst.ravel() for _, dst, _ in connections])
diagonal = (east + west + north + south).ravel() / spacing_squared
values = np.concatenate(
[diagonal] + [-edge.ravel() / spacing_squared for _, _, edge in connections]
)
operator = sparse.csr_matrix(
(values, (row, col)), shape=(side * side, side * side)
)
pressure = np.zeros((GRID, GRID))
pressure[1:-1, 1:-1] = spsolve(
operator, np.ones(side * side)
).reshape(side, side)
return pressure
def generate_sample(
rng: np.random.Generator,
basis: np.ndarray,
sites: np.ndarray,
x: np.ndarray,
y: np.ndarray,
) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
factors = rng.normal(size=len(basis))
weights = np.sqrt(np.exp(-0.5 * np.arange(len(basis))))
log_coefficient = np.tensordot(factors * weights, basis, axes=1) * 0.8
count = rng.integers(3, 7)
chosen = sites[rng.permutation(len(sites))[:count]]
mask = np.zeros((GRID, GRID))
for center in chosen:
sign = rng.choice([-1, 1])
magnitude = rng.uniform(1.2, 1.8)
inclusion = (
(x - center[0]) ** 2 + (y - center[1]) ** 2 <= 0.045 ** 2
).astype(np.float64)
log_coefficient += sign * magnitude * inclusion
mask = np.maximum(mask, inclusion)
log_coefficient = np.clip(log_coefficient, -3, 3)
pressure = solve_pressure(log_coefficient)
return tuple(
array.astype(np.float32).ravel()
for array in (log_coefficient, pressure, mask)
)
def main() -> None:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument(
"--output-dir",
type=Path,
default=Path(__file__).resolve().parents[1] / "data",
)
parser.add_argument(
"--overwrite", action="store_true", help="Replace existing NPZ samples"
)
args = parser.parse_args()
root = args.output_dir.resolve()
existing = [path for split, _ in SPLITS for path in (root / split).glob("*.npz")]
if existing and not args.overwrite:
parser.error(f"{root} already contains NPZ samples; use --overwrite to replace")
axis = (np.arange(GRID) + 0.5) / GRID
x, y = np.meshgrid(axis, axis, indexing="ij")
rng = np.random.default_rng(SEED)
basis = make_basis(x, y)
sites = site_locations(rng)
for split, count in SPLITS:
directory = root / split
directory.mkdir(parents=True, exist_ok=True)
for index in range(count):
permeability, pressure, mask = generate_sample(rng, basis, sites, x, y)
np.savez(
directory / f"darcy_inclusion_{split}_{index:03d}.npz",
nx=np.int64(GRID),
ny=np.int64(GRID),
pde_name="darcy_inclusion",
init_value=permeability,
last_value=pressure,
sparse_mask=mask,
)
print(f"{split}: generated {count} samples in {directory}", flush=True)
manifest = {
"source": "https://github.com/kwonji39/FAST-DeepONet",
"source_commit": OFFICIAL_COMMIT,
"experiment": "Table 2 Darcy inclusion",
"seed": SEED,
"grid_size": [GRID, GRID],
"splits": dict(SPLITS),
"arrays": ["init_value", "last_value", "sparse_mask"],
}
root.mkdir(parents=True, exist_ok=True)
(root / "DATA_MANIFEST.json").write_text(
json.dumps(manifest, indent=2) + "\n", encoding="utf-8"
)
if __name__ == "__main__":
main()