Skip to content

INT_FILL_VALUE overflows in downcast, corrupting polygon meshes #1658

Description

@cmdupuis3

Version

v2026.07.0

How did you install UXarray?

Source

What happened?

Writing a ragged grid (mixed face sizes) to ESMF silently corrupts padded slots instead of preserving them as fill values: _encode_esmf in uxarray/io/_esmf.py adds 1 to every entry of face_node_connectivity, including INT_FILL_VALUE padding, before encoding as int32. INT_FILL_VALUE + 1 (-2**63 + 1) truncates under that narrowing cast to 1, so padding is written as a valid node index (node 0) instead of the declared _FillValue of -1, and the numElementConn fallback (which counts entries != -1) inherits the same error and reports every face at maximum width; a triangle padded to width 4 round-trips back as a quad with an extra vertex at node 0. The reader (_read_esmf) has the mirror-image bug: CF decoding turns the on-disk -1 into NaN, and the code casts to INT_DTYPE before checking for the fill, which only happens to produce INT_FILL_VALUE on platforms where NaN casts to INT64_MIN (x86) — on arm64 it casts to 0, decoding padding to -1, a valid negative index that silently wraps to the last node rather than raising the IndexError INT_FILL_VALUE is designed to trigger. Both directions fail without any exception or warning; only ragged grids are affected, which is why test_esmf_round_trip_consistency (using the all-quad outCSne30 fixture) never caught it.

What did you expect to happen?

The fill value should not be able to be cast as a valid value or overflow into a valid value (positive or negative, since Python is like that).

Can you provide a MCVE to repoduce the bug?

import numpy as np, xarray as xr, uxarray as ux
from uxarray.constants import INT_FILL_VALUE as fv

# one quad, one triangle padded to width 4
fnc = np.array([[0, 1, 2, 3], [1, 4, 2, fv]])
grid = ux.Grid.from_topology(
    np.array([0., 10, 10, 0, 20]), np.array([0., 0, 10, 10, 0]), fnc, fill_value=fv
)
grid.to_xarray(grid_format="ESMF").to_netcdf("out.nc")

raw = xr.open_dataset("out.nc", mask_and_scale=False)
print(raw["elementConn"].values)     # [[1 2 3 4] [2 5 3 1]]  <- expected -1 in last slot
print(raw["numElementConn"].values)  # [4 4]                   <- expected [4 3]

print(ux.open_grid("out.nc").face_node_connectivity.values)

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    Status
    📚 Backlog

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions