Description
MDPDatastore.boundary_mask (neural_lam/datastore/mdp.py) builds the interior region with:
ds_unstacked["boundary_mask"] = da_domain_allzero.isel(
x=slice(self._n_boundary_points, -self._n_boundary_points),
y=slice(self._n_boundary_points, -self._n_boundary_points),
)
For any n_boundary_points >= 1 this correctly selects the interior. But n_boundary_points=0 produces slice(0, -0), which in Python is slice(0, 0) - an empty selection, not "the whole array". The empty interior gets reindexed to the full domain as all-NaN when assigned back, and the subsequent .fillna(1) then marks every grid point as boundary instead of none.
Concretely: MDPDatastore(config_path=..., n_boundary_points=0).boundary_mask.sum() returns the full grid size (every point marked boundary) instead of 0. Verified against the danra example dataset (7680/7680 grid points incorrectly marked boundary).
This is silent - no error or warning - and would make ForecasterModule's interior mask empty, effectively zeroing out the loss over the whole domain for any config that sets n_boundary_points=0.
Note: this is unrelated to the global-domain boundary_mask semantics discussed in #445/#650 (whether a domain with no boundary concept should return None). This is purely a slice-arithmetic edge case in the existing LAM code path for an explicit n_boundary_points=0.
Fix
Guard the zero case, e.g. slice(n, -n) if n > 0 else slice(None).
Description
MDPDatastore.boundary_mask(neural_lam/datastore/mdp.py) builds the interior region with:For any
n_boundary_points >= 1this correctly selects the interior. Butn_boundary_points=0producesslice(0, -0), which in Python isslice(0, 0)- an empty selection, not "the whole array". The empty interior gets reindexed to the full domain as all-NaN when assigned back, and the subsequent.fillna(1)then marks every grid point as boundary instead of none.Concretely:
MDPDatastore(config_path=..., n_boundary_points=0).boundary_mask.sum()returns the full grid size (every point marked boundary) instead of 0. Verified against the danra example dataset (7680/7680 grid points incorrectly marked boundary).This is silent - no error or warning - and would make
ForecasterModule's interior mask empty, effectively zeroing out the loss over the whole domain for any config that setsn_boundary_points=0.Note: this is unrelated to the global-domain
boundary_masksemantics discussed in #445/#650 (whether a domain with no boundary concept should returnNone). This is purely a slice-arithmetic edge case in the existing LAM code path for an explicitn_boundary_points=0.Fix
Guard the zero case, e.g.
slice(n, -n) if n > 0 else slice(None).