Skip to content

Default C-grid tracer fields to CGrid_Tracer interpolator - #3053

Open
v3ravani wants to merge 4 commits into
Parcels-code:mainfrom
v3ravani:fix-cgrid-tracer-interpolator
Open

v3ravani wants to merge 4 commits into
Parcels-code:mainfrom
v3ravani:fix-cgrid-tracer-interpolator

Conversation

@v3ravani

@v3ravani v3ravani commented Nov 10, 2026 •

Copy link
Copy Markdown

Description

Fixes #3049

Problem

In StructuredModelData.from_sgrid_conventions, all scalar fields defaulted to XLinear, even for tracer fields located at cell centers on staggered C-grids. Following Delandmeter and Van Sebille (2019), C-grid tracers should default to CGrid_Tracer to be interpolated constant over the grid cell.

Solution

  • Added _select_xinterpolator in src/parcels/_core/model.py to identify tracer fields defined at cell centers (face dimensions) on staggered C-grids and default them to CGrid_Tracer.
  • A-grid datasets (where velocity components are collocated on identical dimensions) default all scalar fields to XLinear.
  • Node-located fields, edge-located fields, vertical interface fields, and unstaggered grids continue to default to XLinear.
  • In peninsula_dataset, aligned streamfunction P dimensions to ["YG", "XG"] to match its underlying node coordinates lat (YG) and lon (XG) (consistent with stommel_gyre_dataset).

Testing

  • Added regression tests in tests/test_fieldset.py covering default interpolators on C-grids (test_cgrid_tracer_default_interpolator), dimensional edge cases (test_select_xinterpolator_edge_cases), and A-grid behavior (test_agrid_all_fields_default_to_xlinear).
  • Verified test suite passes locally (tests/test_fieldset.py, tests/test_advection.py, tests/test_interpolation.py, tests/test_field.py, tests/test_xgrid.py).
  • Verified with pixi run lint and pixi run typing.

Checklist

AI Disclosure

  • This PR contains AI-generated content.
    • I have tested any AI-generated content in my PR.
    • I take responsibility for any AI-generated content in my PR.
    • Describe how you used it (e.g., by pasting your prompt): I used an AI coding assistant to help draft _select_xinterpolator, align dataset coordinates, and author regression tests. The code was developed at my direction, and I have read, tested, and validated all changes locally.

When constructing fields from SGRID conventions in StructuredModelData, default tracer fields defined at cell centers (face dimensions) on staggered C-grids to CGrid_Tracer rather than hardcoding all scalar fields to XLinear.
@erikvansebille

Copy link
Copy Markdown
Member

Thanks for this PR< but you have not filled out the AI statement in our PR template. As per our AI-usage policy, that is very important for us

@v3ravani

Copy link
Copy Markdown
Author

Thanks for this PR< but you have not filled out the AI statement in our PR template. As per our AI-usage policy, that is very important for us

Thanks, I have updated the PR description with the AI disclosure.

@v3ravani

v3ravani commented Nov 10, 2026 •

Copy link
Copy Markdown
Author

Updated with root-cause fix and synced with latest main:

  1. Handled A-grid datasets in _select_xinterpolator so collocated scalar fields default to XLinear.
  2. In peninsula_dataset, aligned streamfunction P dimensions to ['YG', 'XG'] to match its underlying node coordinates lat and lon (consistent with stommel_gyre_dataset), ensuring continuous linear interpolation along particle streamlines.
  3. Verified all unit tests (test_peninsula_fieldset, test_fieldset, test_interpolation, test_field, test_xgrid), typing (mypy), and linting (pre-commit) pass cleanly.

@wyatt-fluidnumerics wyatt-fluidnumerics left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hello, thanks for this PR! See comments.

Broadly I think some of the testing introduced in this PR is out of scope since it focuses more on the correctness of the CGrid_Tracer interpolator rather than the interpolator selection.

It also broke CI but that seemed to be resolved as I finalized this review.

Comment thread src/parcels/_core/model.py Outdated
model.grid.sgrid_metadata,
model.vector_field_components,
)
if interp_cls is not None:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_select_xinterpolator never returns None

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed, directly instantiated without the redundant None check.

Comment thread src/parcels/_core/model.py Outdated
return XLinear

# Velocity components (U, V, W or components of vector fields) are not tracers
velocity_names = {"U", "V", "W"}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this is needed. CGrid horizontal velocities will be caught in the second check above since they are not on the face, and the vertical velocity gets caught below.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, removed the velocity names check.

Comment thread tests/test_fieldset.py Outdated
@pytest.mark.parametrize(
"dataset_name",
[
"ds_2d_left",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These datasets all store their velocities/tracers on CGrids, so they all check the exact same logic, only one should be used.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated to test only \ds_2d_left.

Comment thread tests/test_fieldset.py Outdated
assert isinstance(fieldset.UV.interp_method, CGrid_Velocity)


def test_cgrid_tracer_piecewise_constant_evaluation():

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is out of scope for this PR.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed as out of scope.

Comment thread tests/test_fieldset.py Outdated
)

# Unstaggered grid metadata (face == node) -> XLinear
from parcels._sgrid.core import FaceNodePadding, Padding, SGrid2DMetadata

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This import should be moved to the top of the file.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved to the top of the file.

Comment thread tests/test_fieldset.py Outdated
assert _select_xinterpolator(da_unstaggered, unstaggered_metadata) is XLinear


def test_cgrid_tracer_particle_sampling():

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Out of scope.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed as out of scope.

Comment thread src/parcels/_core/model.py Outdated
over the grid cell using CGrid_Tracer. Non-tracer fields, velocity components,
node-located variables, or fields on unstaggered grids default to XLinear.
"""
fnp_x, fnp_y = sgrid_metadata.face_dimensions

@wyatt-fluidnumerics wyatt-fluidnumerics Nov 10, 2026 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am confused on what this is checking for, my understanding is that if a single dimension is assigned to both the faces and nodes it would break assertions during Fieldset creation, so I believe this is unneeded.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch, removed.

@github-project-automation github-project-automation Bot moved this from Backlog to Ready in Parcels development Nov 10, 2026
@v3ravani

Copy link
Copy Markdown
Author

Thanks for the review and guidance @wyatt-fluidnumerics!

I have addressed all the feedback in commit 996ee7b2:

  1. Cleaned up _select_xinterpolator:

    • Removed the redundant if interp_cls is not None: check in model.py.
    • Removed the redundant face == node check and hardcoded velocity name sets; selection now relies cleanly on dimension placement and SGRID face/node coordinates.
  2. Scoped tests strictly to interpolator selection:

    • De-parametrized test_cgrid_tracer_default_interpolator to use only ds_2d_left.
    • Removed the out-of-scope piecewise constant evaluation and particle sampling tests.
    • Moved all SGRID imports to the module top in tests/test_fieldset.py.
    • Retained targeted tests for interpolator selection edge cases and A-grid defaults.
  3. Verification:

    • All tests in tests/test_fieldset.py, tests/test_advection.py, tests/test_interpolation.py, tests/test_field.py, and tests/test_xgrid.py pass.
    • Code formatting, linting (pre-commit), and typing (mypy) pass with zero issues.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Ready

Development

Successfully merging this pull request may close these issues.

CGrid Tracers do not default to CGrid_Tracer Interpolator

3 participants