coarse-grained-forecasting-benchmark's picture
Add anonymous reproduction code
41dd7e0 verified
Raw History Blame Contribute Delete
2.99 kB
"""NLKG image adapter: score displacement while tracking both displacement and velocity diagnostics.
The 392-dimensional state contains 196 displacement coordinates followed by 196 velocity
coordinates scaled by 1/4. Images reconstruct the displacement on a 128 x 128 grid.
"""
from __future__ import annotations
import numpy as np
from ..models.spectral import K_C, mode_int_wavenumbers
from .adapters import register_adapter
from .euler import EulerVorticityAdapter
_D_C = 196
@register_adapter("nlkg_coarse_u")
class NLKGCoarseUAdapter(EulerVorticityAdapter):
field_kind = "signed"
def __init__(self, quantile: float = 0.9999, n_disp: int = 128, k_c: int = K_C, **kwargs):
super().__init__(quantile, n_disp=n_disp, k_c=k_c, **kwargs)
kx, ky = mode_int_wavenumbers(self.k_c)
self._k2 = kx.astype(np.float64) ** 2 + ky.astype(np.float64) ** 2
if 2 * self.n_modes != _D_C:
raise ValueError("nlkg_coarse_u assumes the 196-D shared coarse operator per component")
@staticmethod
def _split(x: np.ndarray) -> tuple[np.ndarray, np.ndarray]:
x = np.asarray(x, dtype=np.float64)
if x.ndim == 1:
x = x[None]
if x.shape[1] == 2 * _D_C:
return x[:, :_D_C], x[:, _D_C:]
if x.shape[1] == _D_C:
return x, None
raise ValueError(f"expected {2 * _D_C}-D two-component (u, v/4) states or {_D_C}-D displacement-only states, got D={x.shape[1]}")
def to_fields(self, states_raw: np.ndarray) -> np.ndarray:
re, _ = self._split(states_raw)
return super().to_fields(re)
def card(self) -> dict:
c = super().card()
c.update({"state": "392-D coarse state (196-D u + 196-D v/4, shared coarse operator |k| <= 8 on each); "
"the image reads only the first component u -- identical reconstruction to euler_coarse_vorticity",
"clip": "symmetric [-c, c], c = Q_q(|u_coarse|) over the training pool's final-time fields",
"physical_diagnostic": "coarse_u_variance_ratio, gradient_energy_ratio (on u), coarse_v_variance_ratio (on v/4)"})
return c
def _power(self, v: np.ndarray) -> tuple[float, float]:
n = self.n_modes
power = v[:, :n] ** 2 + v[:, n:] ** 2
return float(np.mean(np.sum(power, axis=1))), float(np.mean(np.sum(self._k2 * power, axis=1)))
def physical_diagnostics(self, gen_raw: np.ndarray, true_raw: np.ndarray) -> dict:
gr, gi = self._split(gen_raw); tr, ti = self._split(true_raw)
vg, eg = self._power(gr); vt, et = self._power(tr)
out = {"coarse_u_variance_ratio": vg / vt if vt > 0 else float("nan"),
"gradient_energy_ratio": eg / et if et > 0 else float("nan")}
if gi is not None and ti is not None:
vgi, _ = self._power(gi); vti, _ = self._power(ti)
out["coarse_v_variance_ratio"] = vgi / vti if vti > 0 else float("nan")
return out