from __future__ import annotations import json import random import sys from pathlib import Path from typing import Any import numpy as np import torch import yaml PROJECT_ROOT = Path(__file__).resolve().parents[1] if str(PROJECT_ROOT) not in sys.path: sys.path.insert(0, str(PROJECT_ROOT)) def load_config(config_path: str | Path | None = None) -> dict[str, Any]: path = Path(config_path) if config_path else PROJECT_ROOT / "conf" / "config.yaml" if not path.is_absolute(): path = PROJECT_ROOT / path with path.open("r", encoding="utf-8") as f: return yaml.safe_load(f) def project_path(path: str | Path) -> Path: path = Path(path) return path if path.is_absolute() else PROJECT_ROOT / path def seed_everything(seed: int) -> None: random.seed(seed) np.random.seed(seed) torch.manual_seed(seed) if torch.cuda.is_available(): torch.cuda.manual_seed_all(seed) def select_device(value: str = "auto") -> torch.device: if value == "auto": return torch.device("cuda:0" if torch.cuda.is_available() else "cpu") return torch.device(value) def ensure_parent(path: Path) -> None: path.parent.mkdir(parents=True, exist_ok=True) def ensure_runtime_dirs(cfg: dict[str, Any]) -> None: for key in ("fake_data", "prediction", "metrics", "loss", "figure"): ensure_parent(project_path(cfg["paths"][key])) ensure_parent(project_path(cfg["training"]["checkpoint"])) def build_model(cfg: dict[str, Any]) -> torch.nn.Module: from model import PINNsformer1D model_cfg = cfg["model"] return PINNsformer1D( d_out=int(model_cfg["d_out"]), d_hidden=int(model_cfg["d_hidden"]), d_model=int(model_cfg["d_model"]), N=int(model_cfg["num_layers"]), heads=int(model_cfg["heads"]), ) def initial_condition(x: np.ndarray | torch.Tensor, cfg: dict[str, Any]): initial = cfg["equation"]["initial"] center = initial["center"] sigma = initial["sigma"] if isinstance(x, torch.Tensor): return torch.exp(-((x - center) ** 2) / (2 * sigma**2)) return np.exp(-((x - center) ** 2) / (2 * sigma**2)) def exact_reaction_solution( x: np.ndarray | torch.Tensor, t: np.ndarray | torch.Tensor, cfg: dict[str, Any], ): h = initial_condition(x, cfg) rate = cfg["equation"]["reaction_rate"] if isinstance(h, torch.Tensor): exp_term = torch.exp(rate * t) else: exp_term = np.exp(rate * t) return h * exp_term / (h * exp_term + 1 - h) def relative_errors(pred: np.ndarray, target: np.ndarray) -> dict[str, float]: return { "relative_l1": float(np.sum(np.abs(target - pred)) / np.sum(np.abs(target))), "relative_l2": float(np.sqrt(np.sum((target - pred) ** 2) / np.sum(target**2))), } def write_json(path: Path, payload: dict[str, Any]) -> None: ensure_parent(path) path.write_text(json.dumps(payload, indent=2), encoding="utf-8")