| """Run all PPNN replicas and save the complete verification payload.""" |
|
|
| import sys |
| from pathlib import Path |
|
|
| import numpy as np |
| import torch |
| import yaml |
|
|
| ROOT = Path(__file__).resolve().parents[1] |
| sys.path.insert(0, str(ROOT)) |
| from model.ppnn import FORMAT_VERSION, PPNN, ensemble_features |
|
|
|
|
| def main(): |
| config = yaml.safe_load((ROOT / "conf/config.yaml").read_text()) |
| data = np.load(ROOT / config["data"]["file"]) |
| checkpoint = torch.load(ROOT / config["paths"]["checkpoint"], map_location="cpu", weights_only=False) |
| if str(data["format_version"]) != FORMAT_VERSION or checkpoint.get("format_version") != FORMAT_VERSION: |
| raise ValueError("data/checkpoint format version mismatch") |
| if data["ensemble"].shape[1:] != (50, 18) or len(data["station_id"]) != 537 or int(data["lead_hours"]) != 48: |
| raise ValueError("inference requires 50 members, 18 variables, 537 stations, and 48h lead") |
| scaling = checkpoint["scaling"] |
| continuous = ensemble_features(torch.from_numpy(data["ensemble"]), torch.from_numpy(data["auxiliary"])) |
| continuous = (continuous - scaling["feature_mean"]) / scaling["feature_std"] |
| station = torch.from_numpy(data["station_index"]) |
| mus, sigmas = [], [] |
| for state in checkpoint["model"]: |
| model = PPNN(int(checkpoint["model_config"]["hidden_size"]), eps=float(checkpoint["model_config"]["sigma_epsilon"])) |
| model.load_state_dict(state) |
| model.eval() |
| with torch.no_grad(): |
| mu_scaled, sigma_scaled = model(continuous, station) |
| mus.append(mu_scaled * scaling["target_std"] + scaling["target_mean"]) |
| sigmas.append(sigma_scaled * scaling["target_std"]) |
| |
| mu = torch.stack(mus).mean(0).numpy() |
| sigma = torch.stack(sigmas).mean(0).numpy() |
| target = data["target"] |
| raw_t2m = data["ensemble"][:, :, 0] |
| if not all(np.isfinite(x).all() for x in (mu, sigma, target, raw_t2m)) or not np.all(sigma > 0): |
| raise RuntimeError("inference output is non-finite or has non-positive sigma") |
| output = ROOT / config["paths"]["inference"] |
| output.parent.mkdir(parents=True, exist_ok=True) |
| np.savez_compressed(output, mu=mu, sigma=sigma, targets=target, raw_ensemble_t2m=raw_t2m, |
| station_index=data["station_index"], station_id=data["station_id"], date_index=data["date_index"], |
| dates=data["dates"], lead_hours=data["lead_hours"], format_version=np.asarray(FORMAT_VERSION)) |
| print(f"saved={output.relative_to(ROOT)} samples={len(mu)} replicas={len(mus)} raw_ensemble={raw_t2m.shape}") |
|
|
|
|
| if __name__ == "__main__": |
| main() |
|
|