import json import os import sys from pathlib import Path import torch import yaml PROJECT_ROOT = Path(__file__).resolve().parents[1] CONFIG_PATH = PROJECT_ROOT / "conf" / "config.yaml" PROBLEMS = ("doublepipe", "diffuser", "rugby", "pipebend") def ensure_onescience_path(explicit_src=None): candidates = [] if explicit_src: candidates.append(Path(explicit_src).expanduser()) if os.environ.get("ONESCIENCE_SRC"): candidates.append(Path(os.environ["ONESCIENCE_SRC"]).expanduser()) for parent in (PROJECT_ROOT, *PROJECT_ROOT.parents): candidates.append(parent / "refactor" / "onescience" / "src") candidates.append(parent / "onescience" / "src") for candidate in candidates: if (candidate / "onescience").is_dir(): path = str(candidate.resolve()) if path not in sys.path: sys.path.insert(0, path) return candidate.resolve() return None def load_config(): with CONFIG_PATH.open("r", encoding="utf-8") as f: return yaml.safe_load(f)["root"] def resolve_path(path_value): path = Path(path_value) return path if path.is_absolute() else PROJECT_ROOT / path def select_device(section): requested = section.get("device", "auto") gpu = int(section.get("gpu", 0)) if requested == "auto": return torch.device(f"cuda:{gpu}" if torch.cuda.is_available() else "cpu") if requested.startswith("cuda") and not torch.cuda.is_available(): print(f"Requested {requested}, but CUDA is not available. Falling back to CPU.") return torch.device("cpu") return torch.device(requested) def dtype_from_config(name): mapping = { "float32": torch.float32, "float": torch.float32, "float64": torch.float64, "double": torch.float64, } if name not in mapping: raise ValueError(f"Unsupported dtype: {name}") return mapping[name] def set_nested(cfg, section, key, value): if value is not None: cfg[section][key] = value def build_models(cfg, device, *, n_col_domain=None, n_train_per_bc=None, problem=None): ensure_onescience_path(cfg.get("runtime", {}).get("onescience_src")) from model import GPPLUS from onescience.utils.GP_TO import get_data_fluid, set_seed problem = problem or cfg["problem"] n_col_domain = int(n_col_domain or cfg["data"]["n_col_domain"]) n_train_per_bc = int(n_train_per_bc or cfg["data"]["n_train_per_bc"]) dtype = dtype_from_config(cfg["model"].get("dtype", "float32")) set_seed(int(cfg["seed"])) x_col, x_train, sol_train = get_data_fluid( problem=problem, N_col_domain=n_col_domain, N_train=n_train_per_bc, ) collocation_x = x_col.to(device=device, dtype=dtype).clone().requires_grad_(True) models = [] for i, name in enumerate(cfg["output_names"]): model = GPPLUS( train_x=x_train[i].type(dtype), train_y=sol_train[i].type(dtype), collocation_x=collocation_x, basis=cfg["model"]["mean_function"], NN_layers_base=cfg["model"]["nn_layers_base"], name_output=name, device=device, dtype=dtype, ).to(device=device, dtype=dtype) models.append(model) return models, { "problem": problem, "n_col_domain": n_col_domain, "n_train_per_bc": n_train_per_bc, "x_col_shape": tuple(x_col.shape), "x_train_shapes": [tuple(x.shape) for x in x_train], "sol_train_shapes": [tuple(y.shape) for y in sol_train], } def save_checkpoint(path, model_list, cfg, metadata, loss_history): path = resolve_path(path) path.parent.mkdir(parents=True, exist_ok=True) torch.save( { "model_state_dicts": [model.state_dict() for model in model_list], "config": cfg, "metadata": metadata, "loss_history": loss_history, }, path, ) return path def load_checkpoint(path, model_list, device): path = resolve_path(path) if not path.is_file(): raise FileNotFoundError(f"Missing checkpoint: {path}. Run scripts/train.py first.") try: checkpoint = torch.load(path, map_location=device, weights_only=False) except TypeError: checkpoint = torch.load(path, map_location=device) state_dicts = checkpoint.get("model_state_dicts") if state_dicts is None: raise KeyError(f"Checkpoint does not contain model_state_dicts: {path}") for model, state_dict in zip(model_list, state_dicts): model.load_state_dict(state_dict) return checkpoint def dump_json(data, path): path = resolve_path(path) path.parent.mkdir(parents=True, exist_ok=True) with path.open("w", encoding="utf-8") as f: json.dump(data, f, indent=2) return path def tensor_to_numpy_dict(fields): return {key: value.detach().cpu().numpy() for key, value in fields.items()}