| from __future__ import annotations |
|
|
| import json |
| import sys |
| from pathlib import Path |
|
|
| import numpy as np |
| import torch |
| from torch_geometric.data import Data |
|
|
| ROOT = Path(__file__).resolve().parents[1] |
| sys.path.insert(0, str(ROOT)) |
|
|
| from onescience.utils.YParams import YParams |
|
|
|
|
| def resolve_path(path: str) -> Path: |
| value = Path(path).expanduser() |
| return value if value.is_absolute() else ROOT / value |
|
|
|
|
| def make_sample(num_nodes: int, input_dim: int, output_dim: int, surface_ratio: float, seed: int) -> Data: |
| generator = torch.Generator().manual_seed(seed) |
| pos = torch.rand((num_nodes, 2), generator=generator) |
| freestream = torch.ones((num_nodes, 2)) |
| freestream[:, 1] = torch.linspace(-0.2, 0.2, num_nodes) |
| sdf = torch.linalg.norm(pos - 0.5, dim=1, keepdim=True) - 0.25 |
| normals = torch.nn.functional.normalize(pos - 0.5, dim=1) |
| x = torch.cat([pos, freestream, sdf, normals], dim=1) |
| if x.size(1) != input_dim: |
| raise ValueError(f"fake input_dim must be 7 for this AirfRANS feature layout, got {input_dim}") |
|
|
| y0 = torch.sin(np.pi * pos[:, :1]) + 0.1 * freestream[:, :1] |
| y1 = torch.cos(np.pi * pos[:, 1:2]) + 0.1 * freestream[:, 1:2] |
| pressure = pos[:, :1] - pos[:, 1:2] |
| viscosity = torch.abs(sdf) + 0.01 |
| y = torch.cat([y0, y1, pressure, viscosity], dim=1) |
| if y.size(1) != output_dim: |
| raise ValueError(f"fake output_dim must be 4 for this AirfRANS target layout, got {output_dim}") |
|
|
| surf = torch.zeros(num_nodes, dtype=torch.bool) |
| surf_count = max(1, int(num_nodes * surface_ratio)) |
| surf[:surf_count] = True |
| perm = torch.randperm(num_nodes, generator=generator) |
| return Data(pos=pos[perm], x=x[perm].float(), y=y[perm].float(), surf=surf[perm]) |
|
|
|
|
| def main() -> int: |
| cfg = YParams(str(ROOT / "conf" / "config.yaml"), "fake_data") |
| cfg_data = YParams(str(ROOT / "conf" / "config.yaml"), "datapipe") |
| cfg_train = YParams(str(ROOT / "conf" / "config.yaml"), "training") |
| data_dir = resolve_path(cfg_data.source.data_dir) |
| stats_dir = resolve_path(cfg_data.source.stats_dir) |
| data_dir.mkdir(parents=True, exist_ok=True) |
| stats_dir.mkdir(parents=True, exist_ok=True) |
|
|
| split_counts = { |
| cfg_data.data.splits.train_name: int(cfg.num_train), |
| cfg_data.data.splits.val_name: int(cfg.num_val), |
| cfg_data.data.splits.test_name: int(cfg.num_test), |
| } |
| manifest: dict[str, list[str]] = {} |
| all_x, all_y = [], [] |
| seed = int(cfg_train.seed) |
|
|
| for split, count in split_counts.items(): |
| names = [] |
| for idx in range(count): |
| name = f"{split}_{idx:04d}" |
| sample = make_sample( |
| num_nodes=int(cfg.num_nodes), |
| input_dim=int(cfg.input_dim), |
| output_dim=int(cfg.output_dim), |
| surface_ratio=float(cfg.surface_ratio), |
| seed=seed + len(all_x), |
| ) |
| torch.save(sample, data_dir / f"{name}.pt") |
| names.append(name) |
| if split == cfg_data.data.splits.train_name: |
| all_x.append(sample.x.numpy()) |
| all_y.append(sample.y.numpy()) |
| manifest[split] = names |
|
|
| (data_dir / "manifest.json").write_text(json.dumps(manifest, indent=2), encoding="utf-8") |
| train_x = np.concatenate(all_x, axis=0) |
| train_y = np.concatenate(all_y, axis=0) |
| np.save(stats_dir / "mean_in.npy", train_x.mean(axis=0).astype(np.float32)) |
| np.save(stats_dir / "std_in.npy", (train_x.std(axis=0) + 1e-6).astype(np.float32)) |
| np.save(stats_dir / "mean_out.npy", train_y.mean(axis=0).astype(np.float32)) |
| np.save(stats_dir / "std_out.npy", (train_y.std(axis=0) + 1e-6).astype(np.float32)) |
| print(f"Fake AirfRANS graph data written to {data_dir}") |
| return 0 |
|
|
|
|
| if __name__ == "__main__": |
| raise SystemExit(main()) |
|
|