Download scripts/result.py from OneScience-Group/GLONET: direct link, hf CLI and curl.
- Browser
- Download file 6.04 kB
-
https://huggingface.co/OneScience-Group/GLONET/resolve/main/scripts/result.py
- Command line
-
hf download hf://OneScience-Group/GLONET/scripts/result.py
-
curl -L -o result.py https://huggingface.co/OneScience-Group/GLONET/resolve/main/scripts/result.py
6.04 kB
| import argparse | |
| import sys | |
| from pathlib import Path | |
| ROOT = Path(__file__).resolve().parents[1] | |
| sys.path.insert(0, str(ROOT)) | |
| import math | |
| import matplotlib.pyplot as plt | |
| import numpy as np | |
| import torch | |
| import yaml | |
| def load_field(path, time_index=0): | |
| """Return a prediction as [C, H, W] from common model output layouts.""" | |
| value = torch.load(path, map_location="cpu", weights_only=False) | |
| if isinstance(value, dict): | |
| for key in ("prediction", "predictions", "output", "outputs"): | |
| if key in value: | |
| value = value[key] | |
| break | |
| field = torch.as_tensor(value).detach().cpu().float().numpy() | |
| if field.ndim == 5: # [B, T, C, H, W] | |
| field = field[0, time_index] | |
| elif field.ndim == 4: # [B, C, H, W] or [T, C, H, W] | |
| field = field[0 if field.shape[0] == 1 else time_index] | |
| elif field.ndim != 3: | |
| raise ValueError(f"Expected [C,H,W], [B,C,H,W], or [B,T,C,H,W], got {field.shape}") | |
| if field.ndim != 3: | |
| raise ValueError(f"Selected output is not [C,H,W]: {field.shape}") | |
| return field | |
| def load_channel_names(config_path, channel_count): | |
| if config_path is None: | |
| return [f"channel_{index}" for index in range(channel_count)] | |
| with open(config_path, encoding="utf-8") as handle: | |
| config = yaml.safe_load(handle) | |
| names = config.get("data", {}).get("channels", []) | |
| if len(names) != channel_count: | |
| return [f"channel_{index}" for index in range(channel_count)] | |
| return names | |
| def choose_channels(names, requested, max_panels): | |
| if requested: | |
| selected = [] | |
| for item in requested: | |
| if item.isdigit(): | |
| index = int(item) | |
| if not 0 <= index < len(names): | |
| raise ValueError(f"Channel index out of range: {index}") | |
| else: | |
| if item not in names: | |
| raise ValueError(f"Unknown channel: {item}") | |
| index = names.index(item) | |
| if index not in selected: | |
| selected.append(index) | |
| return selected | |
| return list(range(min(max_panels, len(names)))) | |
| def is_signed_channel(name): | |
| return name.startswith(("u_", "v_")) or name.startswith("ssh_") | |
| def plot_fields(field, names, indices, output, title, reference=None): | |
| columns = min(3, len(indices)) | |
| rows = math.ceil(len(indices) / columns) | |
| has_reference = reference is not None | |
| fig, axes = plt.subplots(rows, columns, figsize=(5.6 * columns, 4.4 * rows), squeeze=False) | |
| axes = axes.ravel() | |
| height, width = field.shape[-2:] | |
| longitude = np.linspace(0, 360, width, endpoint=False) | |
| latitude = np.linspace(90, -90, height) | |
| extent = [longitude[0], longitude[-1], latitude[-1], latitude[0]] | |
| for axis, index in zip(axes, indices): | |
| data = field[index] | |
| reference_data = reference[index] if has_reference else None | |
| if reference_data is not None: | |
| data_min = min(np.nanpercentile(data, 2), np.nanpercentile(reference_data, 2)) | |
| data_max = max(np.nanpercentile(data, 98), np.nanpercentile(reference_data, 98)) | |
| else: | |
| data_min, data_max = np.nanpercentile(data, [2, 98]) | |
| if np.isclose(data_min, data_max): | |
| data_min, data_max = float(np.nanmin(data)), float(np.nanmax(data) + 1e-6) | |
| cmap = "RdBu_r" if is_signed_channel(names[index]) else "viridis" | |
| image = axis.imshow(data, extent=extent, origin="upper", cmap=cmap, | |
| vmin=data_min, vmax=data_max, aspect="auto") | |
| axis.set_title(names[index], fontsize=11, fontweight="bold") | |
| axis.set_xlabel("Longitude (degrees)") | |
| axis.set_ylabel("Latitude (degrees)") | |
| axis.set_xticks([0, 90, 180, 270, 360]) | |
| axis.set_yticks([-90, -45, 0, 45, 90]) | |
| axis.grid(color="white", linewidth=0.35, alpha=0.35) | |
| colorbar = fig.colorbar(image, ax=axis, fraction=0.046, pad=0.04) | |
| colorbar.ax.tick_params(labelsize=8) | |
| stats = f"min {np.nanmin(data):.3g} | max {np.nanmax(data):.3g} | mean {np.nanmean(data):.3g}" | |
| if reference_data is not None: | |
| rmse = np.sqrt(np.nanmean((data - reference_data) ** 2)) | |
| stats += f" | RMSE {rmse:.3g}" | |
| axis.text(0.02, 0.02, stats, transform=axis.transAxes, fontsize=8, | |
| color="white", bbox={"facecolor": "black", "alpha": 0.55, "pad": 3}) | |
| for axis in axes[len(indices):]: | |
| axis.remove() | |
| fig.suptitle(title, fontsize=15, fontweight="bold") | |
| fig.tight_layout() | |
| fig.savefig(output, dpi=180, bbox_inches="tight") | |
| plt.close(fig) | |
| def main(): | |
| parser = argparse.ArgumentParser() | |
| parser.add_argument("--input", default=str(ROOT / "result/glonet/data/prediction.pt")) | |
| parser.add_argument("--output", default=str(ROOT / "result/glonet/prediction.png")) | |
| parser.add_argument("--config", default=str(ROOT / "conf/config.yaml")) | |
| parser.add_argument("--reference", default=None, help="Optional .pt truth field for RMSE comparison") | |
| parser.add_argument("--channel", action="append", help="Channel name or zero-based index; repeatable") | |
| parser.add_argument("--max-panels", type=int, default=6) | |
| parser.add_argument("--time-index", type=int, default=0) | |
| args = parser.parse_args() | |
| prediction = load_field(args.input, args.time_index) | |
| names = load_channel_names(args.config, prediction.shape[0]) | |
| indices = choose_channels(names, args.channel, args.max_panels) | |
| reference = load_field(args.reference, args.time_index) if args.reference else None | |
| if reference is not None and reference.shape != prediction.shape: | |
| raise ValueError(f"Prediction/reference shape mismatch: {prediction.shape} vs {reference.shape}") | |
| Path(args.output).parent.mkdir(parents=True, exist_ok=True) | |
| plot_fields(prediction, names, indices, args.output, | |
| f"GLONET ocean forecast | {len(indices)} channel(s)", reference) | |
| print(f"saved={args.output}") | |
| if __name__ == "__main__": | |
| main() | |