Skip to content

Fixing OutOfBounds errors on field.eval (attempt 2) #2681

Open
erikvansebille wants to merge 6 commits into
Parcels-code:mainfrom
erikvansebille:update_OutOfBounds_attempt2
Open

Fixing OutOfBounds errors on field.eval (attempt 2) #2681
erikvansebille wants to merge 6 commits into
Parcels-code:mainfrom
erikvansebille:update_OutOfBounds_attempt2

Conversation

@erikvansebille

Copy link
Copy Markdown
Member

Description

This PR supersedes #2665 by going for another approach to fixing OutOfBound errors. Instead of setting the GRID_SEARCH_ERROR, LEFT_OUT_OF_BOUNDS and RIGHT_OUT_OF_BOUNDS variables to very large numbers (which leads to IndexErrors in e.g. the RK4 kernel), this PR masks the returned values of particles that are out of bound to zero

In doing so, I also found that a few of the default interpolators didn't return numpy arrays (see also 2114), so that had to be fixed for all the tests to pass too

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):

By masking out all the interpolated values to zero for particles out of bound
Comment thread src/parcels/interpolators/_uxinterpolators.py
Comment thread src/parcels/_core/field.py Outdated
Comment thread src/parcels/_core/field.py Outdated
Comment thread tests/test_field.py

@fluidnumericsJoe fluidnumericsJoe 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.

This PR is a straightforward fix that applies a mask to the interpolated values for particles that are aligned with negative indices. The masking function, in its current form, assumes grid_position dimensions are X, Y and Z; this is only valid for structured grids - while this resolves the associated issue, it would introduce a breaking scenario for unstructured grids.

I've provided comments on the code changes that suggest a path forward .

@github-project-automation github-project-automation Bot moved this from Backlog to Ready in Parcels development Jun 17, 2026
And filtering this warning in kernel execution

@VeckoTheGecko VeckoTheGecko 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.

Code changes look good! Some comments about the tests though.

Do any changes to docs/migration guide need to be made?

Also, could we wait to merge this until #2646 is merged? (I think it would be easier to do that than have to deal with conflicts introduced in this PR on the other end)

Comment thread tests/test_field.py
Comment on lines +283 to +285
# eval inside bounds should work
np.testing.assert_allclose(fieldset.U.eval(0.0, 0.0, 0.0, 0.0), 1.0)
np.testing.assert_allclose(fieldset.UV.eval(0.0, 0.0, 0.0, 0.0), [[1.0], [2.0]])

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 see that in future we would (by default) have the test suite fail if any unexpected warnings are printed. This includes FieldEval warnings like this.

As such - I think this "eval inside bounds should work" test should be removed as the whole rest of the test suite already tests it.

Comment thread tests/test_field.py
Comment on lines +288 to +291
with pytest.warns(
FieldEvalWarning,
match="Some interpolated values are out-of-bounds. These values are set to 0. Treat carefully.",
):

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 block only tests that the warning is raised at some point during the evaluations below - not that it's being raised each time.

I think we can refactor the following test to something like the following

@pytest.fixture(scope="session")
def fieldset_1_2():
    ds = simple_UV_dataset(mesh="flat")
    fieldset = FieldSet.from_sgrid_conventions(ds, mesh="flat")
    fieldset.U.data[:] = 1.0
    fieldset.V.data[:] = 2.0
    return fieldset


@pytest.mark.parametrize(
    "field_name, location, expected",
    [
        ("U", (0.0, 0.0, 0.0, 5e6), 0.0),
        ("UV", (0.0, 0.0, 0.0, 5e6), [[0.0], [0.0]]),
        ("U", (0.0, 0.0, 5e6, 0.0), 0.0),
        ("UV", (0.0, 0.0, 5e6, 0.0), [[0.0], [0.0]]),
        ("U", (0.0, 5e6, 0.0, 0.0), 0.0),
        ("UV", (0.0, 5e6, 0.0, 0.0), [[0.0], [0.0]]),
    ],
)
def test_field_eval_out_of_bounds_structured(fieldset_1_2, field_name, location, expected):
    """Test that Field.eval returns IndexError when queried outside the grid boundaries."""
    # eval outside of bounds should return 0.0
    field = getattr(fieldset_1_2, field_name)
    with pytest.warns(
        FieldEvalWarning,
        match="Some interpolated values are out-of-bounds. These values are set to 0. Treat carefully.",
    ):
        np.testing.assert_allclose(field.eval(*location), expected)

Comment thread tests/test_field.py
np.testing.assert_allclose(fieldset.UV.eval(0.0, 5e6, 0.0, 0.0), [[0.0], [0.0]])


def test_field_eval_out_of_bounds_unstructured():

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.

Same as above

Comment thread src/parcels/__init__.py
Comment on lines +64 to 67
"FieldEvalWarning",
"FieldSetWarning",
"FileWarning",
"KernelWarning",

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.

nit: I wonder whether we should have a parcels.warnings namespace.

I'm not strongly for or against - just a convention some packages adopt.

Similarly for parcels.errors.

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.

Field.eval silently returns wrong values for out-of-domain queries

3 participants