FourCastNet_v2 / scripts /result.py
yzt15806542928's picture
Upload folder using huggingface_hub
eca4864 verified
Raw
History Blame Contribute Delete
5.07 kB
from __future__ import annotations
import argparse
import json
import sys
from pathlib import Path
SCRIPT_DIR = Path(__file__).resolve().parent
if str(SCRIPT_DIR) not in sys.path:
sys.path.insert(0, str(SCRIPT_DIR))
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import numpy as np
from common import DEFAULT_CONFIG, load_config, resolve_path
def latitude_weights(height: int) -> np.ndarray:
latitude = np.linspace(np.pi / 2, -np.pi / 2, height)
weights = np.cos(latitude).clip(min=0)
return weights / weights.mean()
def compute_metrics(prediction: np.ndarray, target: np.ndarray) -> dict[str, list[float]]:
weights = latitude_weights(target.shape[-2])[None, None, None, :, None]
error = prediction - target
rmse = np.sqrt(np.mean(error**2 * weights, axis=(0, 1, 3, 4)))
spatial_weight = weights / (
weights.sum(axis=(-2, -1), keepdims=True) * target.shape[-1]
)
pred_mean = np.sum(prediction * spatial_weight, axis=(-2, -1), keepdims=True)
target_mean = np.sum(target * spatial_weight, axis=(-2, -1), keepdims=True)
pred_anomaly = prediction - pred_mean
target_anomaly = target - target_mean
numerator = np.sum(pred_anomaly * target_anomaly * weights, axis=(0, 1, 3, 4))
denominator = np.sqrt(
np.sum(pred_anomaly**2 * weights, axis=(0, 1, 3, 4))
* np.sum(target_anomaly**2 * weights, axis=(0, 1, 3, 4))
)
acc = numerator / np.maximum(denominator, 1e-12)
return {"rmse": rmse.tolist(), "acc": acc.tolist()}
def plot_sample(
prediction: np.ndarray,
target: np.ndarray,
variable: str,
channel_index: int,
cmap: str,
output_path: Path,
) -> None:
predicted = prediction[0, 0, channel_index]
expected = target[0, 0, channel_index]
error = predicted - expected
value_min = min(predicted.min(), expected.min())
value_max = max(predicted.max(), expected.max())
error_limit = max(abs(error.min()), abs(error.max()), 1e-12)
extent = (0, 360, -90, 90)
figure, axes = plt.subplots(3, 1, figsize=(12, 10), constrained_layout=True)
image = axes[0].imshow(
expected, origin="upper", extent=extent, aspect="auto", cmap=cmap,
vmin=value_min, vmax=value_max,
)
axes[0].set_title(f"Target {variable}")
figure.colorbar(image, ax=axes[0], orientation="vertical")
image = axes[1].imshow(
predicted, origin="upper", extent=extent, aspect="auto", cmap=cmap,
vmin=value_min, vmax=value_max,
)
axes[1].set_title(f"Prediction {variable}")
figure.colorbar(image, ax=axes[1], orientation="vertical")
image = axes[2].imshow(
error, origin="upper", extent=extent, aspect="auto", cmap="RdBu_r",
vmin=-error_limit, vmax=error_limit,
)
axes[2].set_title(f"Error {variable}")
figure.colorbar(image, ax=axes[2], orientation="vertical")
for axis in axes:
axis.set_xlabel("Longitude")
axis.set_ylabel("Latitude")
output_path.parent.mkdir(parents=True, exist_ok=True)
figure.savefig(output_path, dpi=160)
plt.close(figure)
def main() -> None:
parser = argparse.ArgumentParser(description="Evaluate and plot FCNv2 output")
parser.add_argument("--config", default=str(DEFAULT_CONFIG))
parser.add_argument("--input")
args = parser.parse_args()
config = load_config(args.config)
inference_dir = resolve_path(config, config["inference"]["output_dir"])
input_path = Path(args.input).expanduser().resolve() if args.input else None
files = [input_path] if input_path else sorted(inference_dir.glob("sample_*.npz"))
if not files:
raise FileNotFoundError(f"No inference outputs found in {inference_dir}")
predictions = []
targets = []
for path in files:
with np.load(path) as data:
predictions.append(data["prediction"])
targets.append(data["target"])
prediction = np.concatenate(predictions)
target = np.concatenate(targets)
metrics = compute_metrics(prediction, target)
output_dir = resolve_path(config, config["visualization"]["output_dir"])
output_dir.mkdir(parents=True, exist_ok=True)
(output_dir / "metrics.json").write_text(
json.dumps(metrics, indent=2), encoding="utf-8"
)
variable = config["visualization"]["variable"]
channel_index = config["data"]["variables"].index(variable)
sample_index = config["visualization"]["sample_index"]
if not 0 <= sample_index < prediction.shape[0]:
raise IndexError(
f"visualization.sample_index={sample_index} is outside "
f"the available range [0, {prediction.shape[0] - 1}]"
)
plot_sample(
prediction[sample_index : sample_index + 1],
target[sample_index : sample_index + 1],
variable,
channel_index,
config["visualization"]["cmap"],
output_dir / f"{variable}_forecast.png",
)
print(output_dir / "metrics.json")
print(output_dir / f"{variable}_forecast.png")
if __name__ == "__main__":
main()