import math import os import sys from pathlib import Path import numpy as np PROJECT_ROOT = Path(__file__).resolve().parents[1] sys.path.insert(0, str(PROJECT_ROOT)) from onescience.utils.YParams import YParams def set_default_data_env(): os.environ.setdefault("ONESCIENCE_BENO_DATA_DIR", str(PROJECT_ROOT / "data")) def resolve_path(path_value): path = Path(path_value) return path if path.is_absolute() else PROJECT_ROOT / path def load_config(): set_default_data_env() cfg = YParams(str(PROJECT_ROOT / "conf" / "config.yaml"), "root") cfg.datapipe.source.data_dir = str(resolve_path(cfg.datapipe.source.data_dir)) cfg.datapipe.source.cache_dir = str(resolve_path(cfg.datapipe.source.cache_dir)) cfg.fake_data.root_dir = str(resolve_path(cfg.fake_data.root_dir)) return cfg def square_boundary(points, boundary_points): bottom = [(x, 0.0) for x in points] top = [(x, 1.0) for x in points] left = [(0.0, y) for y in points[1:-1]] right = [(1.0, y) for y in points[1:-1]] boundary = np.array(bottom + top + left + right, dtype=np.float64) if boundary.shape[0] == 0: raise ValueError("resolution is too small to build boundary points") repeats = int(math.ceil(boundary_points / boundary.shape[0])) return np.tile(boundary, (repeats, 1))[:boundary_points] def make_beno_arrays(cfg): data_cfg = cfg.datapipe.data fake_cfg = cfg.fake_data total_samples = int(data_cfg.ntrain) + int(data_cfg.ntest) resolution = int(data_cfg.resolution) boundary_points = int(fake_cfg.boundary_points) rng = np.random.default_rng(int(fake_cfg.seed)) axis = np.linspace(0.0, 1.0, resolution, dtype=np.float64) yy, xx = np.meshgrid(axis, axis, indexing="ij") coords = np.stack([xx.reshape(-1), yy.reshape(-1)], axis=-1) cell_state = np.zeros((resolution, resolution), dtype=np.float64) cell_state[0, :] = 1.0 cell_state[-1, :] = 1.0 cell_state[:, 0] = 1.0 cell_state[:, -1] = 1.0 cell_state = cell_state.reshape(-1) boundary_xy = square_boundary(axis, boundary_points) rhs = np.zeros((total_samples, resolution * resolution, 4), dtype=np.float64) sol = np.zeros((total_samples, resolution * resolution, 1), dtype=np.float64) bc = np.zeros((total_samples, boundary_points, 4), dtype=np.float64) for sample_idx in range(total_samples): phase = 0.25 * sample_idx amplitude = 1.0 + 0.1 * sample_idx forcing = ( amplitude * np.sin(np.pi * xx + phase) * np.sin(np.pi * yy) + 0.15 * np.cos(2.0 * np.pi * xx) * np.sin(np.pi * yy + phase) ) solution = 0.25 * forcing + 0.05 * (xx + yy) if fake_cfg.noise_std: solution += float(fake_cfg.noise_std) * rng.standard_normal(solution.shape) boundary_value = ( 0.05 * sample_idx + 0.1 * boundary_xy[:, 0] - 0.08 * boundary_xy[:, 1] ) rhs[sample_idx, :, 0:2] = coords rhs[sample_idx, :, 2] = forcing.reshape(-1) rhs[sample_idx, :, 3] = cell_state sol[sample_idx, :, 0] = solution.reshape(-1) bc[sample_idx, :, 0:2] = boundary_xy bc[sample_idx, :, 2] = boundary_value return rhs, sol, bc def clear_matching_cache(cfg): cache_dir = Path(cfg.datapipe.source.cache_dir) prefix = cfg.datapipe.source.file_prefix for split, count in (("train", cfg.datapipe.data.ntrain), ("test", cfg.datapipe.data.ntest)): cache_file = cache_dir / f"cached_{prefix}_{split}_{count}.pt" if cache_file.exists(): cache_file.unlink() def main(): cfg = load_config() data_dir = Path(cfg.datapipe.source.data_dir) data_dir.mkdir(parents=True, exist_ok=True) rhs, sol, bc = make_beno_arrays(cfg) prefix = cfg.datapipe.source.file_prefix np.save(data_dir / f"RHS_{prefix}_all.npy", rhs) np.save(data_dir / f"SOL_{prefix}_all.npy", sol) np.save(data_dir / f"BC_{prefix}_all.npy", bc) clear_matching_cache(cfg) print(f"Fake BENO data written to {data_dir}") print(f"RHS shape={rhs.shape}, SOL shape={sol.shape}, BC shape={bc.shape}") if __name__ == "__main__": main()