"""Thin validation wrapper around the official torch-harmonics SFNO class.""" from __future__ import annotations import torch from torch import nn from torch_harmonics.examples.models.sfno import SphericalFourierNeuralOperator from .config import SFNOConfig class OfficialSFNOAdapter(nn.Module): def __init__(self, config: SFNOConfig) -> None: super().__init__() config.validate() self.expected_shape = (config.channels, config.nlat, config.nlon) self.model = SphericalFourierNeuralOperator( img_size=(config.nlat, config.nlon), grid=config.grid, grid_internal=config.grid_internal, scale_factor=config.scale_factor, in_chans=config.channels, out_chans=config.channels, embed_dim=config.embed_dim, num_layers=config.num_layers, use_mlp=True, normalization_layer="none", residual_prediction=False, pos_embed="none", ) def forward(self, inputs: torch.Tensor) -> torch.Tensor: if inputs.ndim != 4: raise ValueError(f"Expected [B, C, Nlat, Nlon], got {tuple(inputs.shape)}") if tuple(inputs.shape[1:]) != self.expected_shape: raise ValueError( f"Expected trailing shape {self.expected_shape}, got {tuple(inputs.shape[1:])}" ) if not inputs.is_floating_point(): raise TypeError("SFNO inputs must be floating point") return self.model(inputs.float())