Source code for ForMoSA.grid.grid_loader
import os
import xarray as xr
from pathlib import Path
import numpy as np
from ForMoSA.core.errors import ForMoSAError
[docs]
class GridLoader:
'''
Class responsible for Grid loading.
Notes
-----
Authors: Allan Denis
'''
@staticmethod
def _validate_model_grid_dataset(ds):
'''
Validate that an xarray.Dataset conforms to the ForMoSA grid specifications
Notes
-----
Authors: Allan Denis
'''
# Grid key
if "grid" not in ds:
raise ForMoSAError("Dataset must contain 'grid'")
# Attributes
required_attrs = ["key", "par", "title", "unit", "res"]
for attr in required_attrs:
if attr not in ds.attrs:
raise ForMoSAError(f"Missing grid attribute: {attr}")
# Wavelength
dims = ds["grid"].dims
if dims[0] != "wavelength":
raise ForMoSAError("First grid dimension must be 'wavelength'")
# Grid dimensions
param_keys = ds.attrs["key"]
if list(dims[1:]) != list(param_keys):
raise ForMoSAError(f"Grid dimensions {dims[1:]} do not match attrs['key']={param_keys}")
# Resolution
if len(ds["wavelength"]) != len(ds.attrs["res"]):
raise ForMoSAError("attrs['res'] length must match wavelength size")
# Wavelength unit
if "wave_unit" in ds.attrs:
if not isinstance(ds.attrs['wave_unit'], str):
raise ForMoSAError(f"Invalid type for 'wave_unit' attribute: {type(ds.attrs['wave_unit'])}. Must be a string")
@staticmethod
def _from_file(path: str | os.PathLike) -> xr.Dataset:
'''
Load and validate a ForMoSA model grid from a NetCDF file.
Parameters
----------
path : str | os.PathLike
Path to the NetCDF grid file.
Returns
-------
xr.Dataset
Validated ForMoSA grid dataset.
Notes
-----
Authors: Allan Denis
'''
path = Path(path)
# Check path
if not path.exists():
raise ForMoSAError(f"Grid file does not exist: {path}")
if path.suffix != ".nc":
raise ForMoSAError(f"Grid file must be a .nc file: {path}")
# Open dataset
try:
ds = xr.open_dataset(path, decode_cf=False)
ds.attrs['res'] = np.atleast_1d(ds.attrs['res']).astype(float)
except Exception as e:
raise ForMoSAError(f"Failed to open grid file: {e}")
# Validation
GridLoader._validate_model_grid_dataset(ds)
return ds
@staticmethod
def _from_data(data: np.ndarray, coords: dict, attrs: dict) -> xr.Dataset:
'''
Build and validate a ForMoSA model grid from raw arrays.
Parameters
----------
data : np.ndarray
Grid data array. Expected shape: (n_wavelength, n_par1, ..., n_parN)
coords : dict
Coordinate dictionary. Must include 'wavelength' and parameter coordinates.
attrs : dict
Grid attributes required by ForMoSA.
Returns
-------
xr.Dataset
Validated ForMoSA grid dataset.
Notes
-----
Authors: Allan Denis
'''
# Data type
if not isinstance(data, np.ndarray):
raise ForMoSAError("data must be a numpy.ndarray")
# Wavelength
if "wavelength" not in coords:
raise ForMoSAError("coords must include 'wavelength'")
# Keys
param_keys = attrs.get("key")
if param_keys is None:
raise ForMoSAError("attrs must include 'key'")
# Data shape
dims = ["wavelength"] + param_keys
expected_shape = tuple(len(coords[d]) for d in dims)
if data.shape != expected_shape:
raise ForMoSAError(f"Data shape {data.shape} does not match expected {expected_shape}")
# Grid generation
ds = xr.Dataset(data_vars=dict(grid=(dims, data)), coords=coords, attrs=attrs)
# Validation
GridLoader._validate_model_grid_dataset(ds)
return ds