Skip to content

Record what of UXarray we reach, and close three gaps - #192

Merged
rajeeja merged 1 commit into
mainfrom
uxarray-surface-inventory-and-subset-advanced
Oct 23, 2026
Merged

rajeeja merged 1 commit into
mainfrom
uxarray-surface-inventory-and-subset-advanced

Conversation

@rajeeja

@rajeeja rajeeja commented Oct 23, 2026

Copy link
Copy Markdown
Collaborator

Replaying the notebooks in UXARRAY/uxarray-community
through the server found capabilities a user reaches for that we cannot run.
Looking for the rest found twelve, and no record anywhere of which absences
were decided and which were simply unnoticed.

The inventory

domain/uxarray_surface.py lists every UXarray-specific method on
UxDataArray, UxDataset and the subset accessor, each with either the
operation that reaches it or the reason it is out of scope. The
xarray-inherited surface is excluded on purpose: ~150 methods that are not
UXarray's to change and would drown the signal.

tests/test_uxarray_surface.py holds it to the installed library:

  • a new upstream method fails until someone writes a verdict;
  • a renamed or removed one fails the day it lands, rather than the next
    time a user follows our advice into a method that is gone;
  • every route the inventory claims must exist — an inventory claiming a
    route that was never wired is worse than one with a visible gap.

Deliberately not a count. A test asserting "we expose N of M methods" goes
red on every upstream release for reasons nobody can act on, and the repair is
to edit the number — which teaches people to stop reading it. The weekly floor
job spent six Mondays red for exactly that reason.

Three gaps closed — as new tools, not in run_analysis

Tool Closes Notebook uses
subset_advanced subset.bounding_circle, subset.nearest_neighbor 4 each
regrid_elements topological_mean and 6 siblings 3

regrid_elements is the step that moves node-centred vorticity onto faces
before anything else can touch it — the cyclone notebook opens with it.

An empty selection is refused with the mesh extent, the rule
test_subset_coverage.py already enforces for bounding boxes. UXarray's own
message says the circle was empty without saying where a circle would not be:

Selection kept no faces: circle centred at (0.0, 0.0) with radius 30.0
degrees. The mesh covers {'lon_min': 40.0, 'lon_max': 46.0, ...}. Move the
centre inside that extent, or widen the radius.

Why separate tools: #89 measures run_analysis at 46 parameters over 25
operations and names that as the problem. Adding a radius, a neighbour count, a
centre and a destination element to it would have made the measured problem
worse in order to fix a different one. Each new tool takes only what its own
operation uses, and both are deferred.

Two honesty fixes

get_capabilities advertised things it cannot run. It lists native UXarray
calls — grid.get_ball_tree(), var.integrate(), grid.subset.bounding_circle()
— all valid UXarray, none executable here, and nothing said so. A model reading
that plans a call that does not exist; the same defect class as a wrong tool
annotation. The result now carries uxarray_capabilities_are_advisory, naming
mcp_server_tools as the executable surface.

The face-centred refusals were a dead end. Variable 't_node' is not face-centered is correct, and the standard repair is topological_mean("face")
— which the server could not run, so the message had nothing to offer. It now
names regrid_elements, and a test follows that advice to confirm it works
rather than landing on a second error.

Verification

  • 1538 passed, 1 skipped, 44 deselected, 1 xfailed
  • EXPECTED_DEFERRED 33 → 35; that counter guards the namespace plan against
    drift, which two new deferred tools genuinely changes.
  • Over stdio on deferred-full: both tools run through call_deferred, the
    circle and nearest selections return the expected face counts, regrid_elements
    moves ['n_node'] -> ['n_face'], and the empty-circle refusal carries the
    mesh extent.

Still open, now recorded rather than unnoticed

weighted_mean, neighborhood, to_raster, and the two
constant_*_interval selections are marked gap with reasons. get_dual,
scalardotgradient, topological_prod and the boolean reductions are
out-of-scope with reasons.

Replaying the notebooks in UXARRAY/uxarray-community through the server
found capabilities a user reaches for that we could not run. Looking for
the rest of them found twelve, and no record anywhere of which absences
were decided and which were merely unnoticed.

domain/uxarray_surface.py is that record. Every UXarray-specific method
on UxDataArray, UxDataset and the subset accessor appears once, with
either the operation that reaches it or the reason it is out of scope.
The xarray-inherited surface is excluded: ~150 methods that are not
UXarray's to change and would drown the signal.

tests/test_uxarray_surface.py holds the list to the installed library. A
new method upstream fails until someone writes a verdict; a renamed one
fails the day the rename lands rather than the next time a user follows
our advice into a method that is gone. It also checks that every route
the inventory claims actually exists, because an inventory claiming a
route that was never wired is worse than one with a visible gap.

Deliberately not a count. "We expose N of M methods" goes red on every
upstream release for reasons nobody can act on, and the repair is to
edit the number -- which teaches people to stop reading the test. The
weekly floor job spent six Mondays red for that reason.

Three gaps closed, in two new tools rather than in run_analysis:

- subset_advanced -- selection near a point, by radius (bounding_circle)
  or by neighbour count (nearest_neighbor). Four uses each in the
  notebooks. An empty selection is refused with the mesh extent, the
  same rule test_subset_coverage.py already enforces for bounding boxes;
  UXarray's own message says the circle was empty without saying where a
  circle would not be.
- regrid_elements -- carries a field between nodes, edges and faces via
  the topological_* reductions. Three uses, all in the step that moves
  node-centred vorticity onto faces before anything else can touch it.

They are separate tools, and deferred, because #89 measures run_analysis
at 46 parameters over 25 operations and names that as the problem to
fix. Adding a radius, a neighbour count, a centre and a destination
element to it would have made the measured problem worse in order to fix
a different one. Each new tool takes only what its own operation uses.

Two honesty fixes fall out of the same work:

- get_capabilities lists native UXarray calls, several of which the
  server cannot execute -- grid.get_ball_tree(), var.integrate(). All
  true, none runnable here, and nothing said so. A model reading that
  plans a call that does not exist. The result now carries
  uxarray_capabilities_are_advisory, naming mcp_server_tools as the
  executable surface.
- The face-centred refusals were a dead end. "Variable 't_node' is not
  face-centered" is correct, and the standard repair is
  topological_mean("face"), which the server could not run, so the
  message offered nothing. It now names regrid_elements, and a test
  follows that advice to confirm it works rather than landing on a
  second error.

EXPECTED_DEFERRED moves 33 -> 35. That counter guards the namespace plan
against drift, which two new deferred tools genuinely changes.

1538 passed, 1 skipped, 44 deselected, 1 xfailed. Verified over stdio on
the deferred-full profile: both tools run through call_deferred, and the
empty-circle refusal carries the mesh extent.
@rajeeja
rajeeja merged commit 61d59ff into main Oct 23, 2026
13 checks passed
@rajeeja
rajeeja deleted the uxarray-surface-inventory-and-subset-advanced branch October 23, 2026 19:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant