Skip to content

Test of VectorInterp() on the FV3 grid - #125

Merged
pletzer merged 1 commit into
pletzer:debug_fv3from
ta440:interp_cs_test
Aug 21, 2026
Merged

Test of VectorInterp() on the FV3 grid#125
pletzer merged 1 commit into
pletzer:debug_fv3from
ta440:interp_cs_test

Conversation

@ta440

@ta440 ta440 commented Aug 20, 2026

Copy link
Copy Markdown
Collaborator

Adds a test of applying the VectorInterp() function on the FV3 cubed-sphere grid. Here, we aim to interpolate winds to regularly spaced nodes on a 2x2 lon-lat grid.

@pletzer

pletzer commented Aug 21, 2026

Copy link
Copy Markdown
Owner

Diagnostic report — mint / VectorInterp
593 bad cells
test_lonlat_vector_interp_fv3_grid fails because 593 of the 16,200 regularly-spaced lon/lat probe points land outside every cell of the C24 FV3 cubed-sphere grid, as far as the point locator is concerned. Plotting where they fall splits them cleanly into two unrelated bugs.

grid C24_SCRIP_desc.181018.nc · 3,456 cells
probe points 180 lon × 90 lat, 1°–359°, ±89°
tol² 1e-12
Result
Where the 593 land
Re-running findPoints one target at a time (the C API has no bulk accessor for which points failed) and cross-checking each failure against every cell in the grid — with and without periodic wrap in longitude — separates the failures into two populations with completely different geometry.

16,200
target points probed
593
outside every cell (3.7%)
225
fixed by periodic wrap — seam bug
368
fail even with wrap — pole geometry
lat 90°N
lat 90°S
equator
lon 360°
seam bug — recoverable with periodic wrap (225 pts)
pole geometry gap — not recoverable (368 pts)
Fig. 1 — every probe point, gray; failures in color. The teal band hugs the lon 0°/360° seam at almost every latitude — classic periodic-wrap symptom. The rust points crowd the ±89° rows almost end to end, plus a wedge near lon 355° at high northern latitude where a second seam-adjacent panel meets a pole panel.
Root cause A
seam · 225 points
findPoints() never wraps longitude, even though you told it to
VectorInterp.buildLocator(periodX=360., enableFolding=True) configures vmtCellLocator with a periodicity length and pole-folding — but those settings are only ever read by FindCellsAlongLine (the path used by PolylineIntegral). The point lookup used by findPoints goes through a completely different, unwrapped method:

src/mntVectorInterp.cpp:87
(*self)->cellIds[i] = (self)->locator->FindCell(&targetPoints[3i], tol2,
cell, pcoords, weights);
src/vmtCellLocator.cpp:203-224
vtkIdType
vmtCellLocator::FindCell(...) {
int bucketId = this->getBucketId(point);
...
for (candidate cells in that bucket) {
if (this->containsPoint(cId, point, tol)) { // no periodicity, no folding
... return cId;
}
}
return -1; // "outside the domain"
}
containsPoint() tests the raw point against the cell's raw stored corners. containsPointMultiValued() — the method that actually shifts the point by ±periodX and applies foldAtPole() — sits right next to it in the same file and is never called from FindCell. Because fixLonAcrossDateline=1 stretches some cells' stored longitudes outside [0°, 360°] (this grid's corners actually span -10°…440°), a probe point sitting at lon 1° or 359° can have its true containing cell registered a full period away, and FindCell has no way to find it.

Root cause B
pole · 368 points
the 4 pole cells are diamonds in (lon, lat) space, not wedges
The remaining 368 failures don't respond to periodic wrap or pole-folding at all — they were checked against every one of the grid's 3,456 cells, shifted by every combination of ±360° and foldAtPole(), and still fall outside all of them. This isn't a search bug; the cell that should contain them genuinely doesn't, as a flat polygon.

Four cells touch the pole vertex exactly — one per neighboring panel — each storing it as a plain quad corner with averageLonAtPole-assigned longitude. Cell 1427, for instance, is stored as:

84°
87°
90° (pole)
(35°,84.1°)
(80°,85.8°)
(35°,90°) pole
(-10°,85.8°)
target (1°, 89°)
— clearly inside the wedge on the sphere,
outside the straight-edge quad
Because a pole-adjacent cell spans nearly a full quarter of all longitudes while its latitude range shrinks to almost nothing, its true edge — a straight line on the cube face — projects into (lon, lat) as a curve that bows noticeably away from the chord isPointInQuad() draws between its corners. The chord cuts the near-pole corner off far more aggressively than the actual cell boundary does, so any probe point that isn't close to the cell's own averageLonAtPole longitude (35°, 125°, 215°, or 305° for these four cells) reads as outside. Sampling the ±89° rows confirms it exactly: the only longitudes that succeed there are the ones within about ±10° of one of those four values — everywhere else in the row fails, which is exactly the gap-toothed pattern in Fig. 1.

Recommendation
Two independent fixes
Make FindCell periodicity/folding-aware. Either route it through containsPointMultiValued (already implements the right ±periodX / foldAtPole logic, just needs a bucket lookup that also checks the periodic-shifted buckets) or, cheaper, have getBucketId probe the buckets for point[0], point[0]-periodX and point[0]+periodX when periodicity is enabled, before giving up. This alone clears 225 of the 593 failures and — since PolylineIntegral already relies on the multivalued path — brings point lookup and line lookup back in sync.
Give pole-touching cells a projection-aware containment test. The straight-chord test is fundamentally the wrong tool near a coordinate singularity. For any cell with a corner at ±90° latitude, reproject that cell's corners and the target point into a local azimuthal (e.g. gnomonic) projection centered on the pole before running isPointInQuad — in that projection the cube-face edges really are straight, so the test becomes exact instead of a lon/lat chord approximation. This is a few dozen lines gated on "does this cell have a ±90° corner," not a rewrite of the locator.
mint / VectorInterp · locator: src/vmtCellLocator.cpp, src/mntVectorInterp.cpp · test: mint/tests/test_vector_interp_cubedsphere.py::test_lonlat_vector_interp_fv3_grid

@pletzer
pletzer changed the base branch from master to debug_fv3 August 21, 2026 17:37
@pletzer

pletzer commented Aug 21, 2026

Copy link
Copy Markdown
Owner

Thanks @ta440 for submitting this bug

@pletzer
pletzer marked this pull request as ready for review August 21, 2026 17:42
@pletzer
pletzer merged commit e781eb9 into pletzer:debug_fv3 Aug 21, 2026
2 of 3 checks passed
pletzer added a commit that referenced this pull request Aug 21, 2026
* test for vector interp on the FV3 grid (#125)

* make FindCell aware of the periodicity by haing getBucketId also probe +/- periodicity

* setCubedSphere(bool) — a new, explicit flag on the locator, threaded down only from grids where fixLonAcrossDateline && averageLonAtPole (the flags that already meant "gnomonic cubed sphere" on Grid_t, just never passed to the locator before). Wired into mnt_vectorinterp_buildLocator only — deliberately not into PolylineIntegral/RegridEdges, since their line-intersection code (collectIntersectionPoints) does its own independent flat-straight-line math that a spherical containsPoint would put out of sync with — that's a separate, deeper fix (great-circle arc intersection) I've documented at both call sites rather than half-applying.
Spherical bilinear patch (sphericalBilinearMap, invertSphericalBilinearPatch, containsPointCubedSphere) — for a cubed-sphere face, containment and parametric coordinates now come from one consistent model (double-slerp patch + Gauss-Newton inverse), instead of pairing an accurate spherical containment test with vtkQuad::EvaluatePosition's unrelated flat model — which is what caused the interpolation blow-ups.
Performance: each face's XYZ corners/centroid/radius are precomputed once in BuildLocator, and a cheap centroid-distance pre-filter (proven safe — 2× margin above the measured worst case) rejects the large majority of candidate cells before ever running Newton. Net: the originally-reported test went from 2s → 928s (suite) → back to 2s / 44s suite-wide.

* fixed compilation warnings

---------

Co-authored-by: Timothy Andrews <75810156+ta440@users.noreply.github.com>
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.

2 participants