Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactoring field interpolation and allow custom interpolation methods in Scipy mode #1816

Open
wants to merge 41 commits into
base: main
Choose a base branch
from

Conversation

VeckoTheGecko
Copy link
Contributor

@VeckoTheGecko VeckoTheGecko commented Jan 8, 2025

This PR refactors many of the indexing methods and interpolation methods out of field.py, and moves indexing code to a separate file to make things more manageable and reduce the coupling with the Field class.

This PR also allows users to easily overwrite the behaviour of existing interpolation methods or (untested) define new interpolation methods in Scipy mode. This can be done via the register_2d_interpolator(...) and register_3d_interpolator(...) decorators. This behaviour is in beta and is subject to change.

This also makes the interpolation functions more easily testable by providing only the required data.

Fixes #1823

Copy link
Contributor Author

@VeckoTheGecko VeckoTheGecko left a comment

Choose a reason for hiding this comment

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

Thoughts on the below @erikvansebille ? Let me know if you want me to break this into separate PRs

parcels/_interpolation.py Outdated Show resolved Hide resolved
parcels/tools/statuscodes.py Outdated Show resolved Hide resolved
@VeckoTheGecko
Copy link
Contributor Author

VeckoTheGecko commented Jan 16, 2025

Note for review: de89311 includes testing that shows the refactor of the 3d interpolation is 100% equivalent to the previous version for all combinations of interp method, grid types, and cell locations.

@VeckoTheGecko
Copy link
Contributor Author

I'm quite aware that this is getting to be a big PR. To be expected for such a big refactor, but I think it would be good to continue in other PRs (for vector interp and indexing) to keep this reviewable.

@VeckoTheGecko VeckoTheGecko changed the title Refactoring indexing and interpolation Refactoring field interpolation (and move indexing code) Jan 16, 2025
Comment on lines +68 to +93
@pytest.mark.parametrize(
"func, eta, xsi, expected",
[
pytest.param(interpolation._nearest_2d, 0.49, 0.49, 3.0, id="nearest_2d-1"),
pytest.param(interpolation._nearest_2d, 0.49, 0.51, 4.0, id="nearest_2d-2"),
pytest.param(interpolation._nearest_2d, 0.51, 0.49, 5.0, id="nearest_2d-3"),
pytest.param(interpolation._nearest_2d, 0.51, 0.51, 6.0, id="nearest_2d-4"),
pytest.param(interpolation._tracer_2d, None, None, 6.0, id="tracer_2d"),
# pytest.param(interpolation._linear_2d, ...),
# pytest.param(interpolation._linear_invdist_land_tracer_2d, ...),
],
)
def test_2d(self, data_2d, func, eta, xsi, expected):
ctx = interpolation.InterpolationContext2D(data_2d, eta, xsi, self.ti, self.yi, self.xi)
assert func(ctx) == expected

@pytest.mark.parametrize(
"func, eta, xsi, expected",
[
# pytest.param(interpolation._nearest_3d, ...),
# pytest.param(interpolation._cgrid_velocity_3d, ...),
# pytest.param(interpolation._linear_invdist_land_tracer_3d, ...),
# pytest.param(interpolation._linear_3d, ...),
# pytest.param(interpolation._tracer_3d, ...),
],
)
Copy link
Contributor Author

@VeckoTheGecko VeckoTheGecko Jan 16, 2025

Choose a reason for hiding this comment

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

Thoughts on test cases @erikvansebille ? I think it might be good to add some on the raw arrays

@VeckoTheGecko VeckoTheGecko marked this pull request as ready for review January 16, 2025 18:18
@VeckoTheGecko
Copy link
Contributor Author

I added an extra commit in the history that shows 100% equivalence of the refactor. See updated #1816 (comment)

@VeckoTheGecko VeckoTheGecko changed the title Refactoring field interpolation (and move indexing code) Refactoring field interpolation and allow custom interpolation methods in Scipy mode Jan 17, 2025
Copy link
Member

@erikvansebille erikvansebille left a comment

Choose a reason for hiding this comment

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

First bit of reviews (of the three main new files). Rest to come after the weekend

+ xsi * eta * grid.depth[:, yi + 1, xi + 1]
+ (1 - xsi) * eta * grid.depth[:, yi + 1, xi]
)
z = np.float32(z) # type: ignore # TODO: remove type ignore once we migrate to float64
Copy link
Member

Choose a reason for hiding this comment

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

I'm surprised by this statement here. I know it doesn't come from this PR, but why only cast to float32 in the s-case (and not the z-case)? In general, z can be either float32 or float64, depending on the lonlatdepth_dtype

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No idea :/

Copy link
Member

Choose a reason for hiding this comment

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

OK, then I propose we remove it

Copy link
Member

Choose a reason for hiding this comment

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

Then I propose we remove it

Suggested change
z = np.float32(z) # type: ignore # TODO: remove type ignore once we migrate to float64

parcels/_interpolation.py Outdated Show resolved Hide resolved
parcels/_interpolation.py Outdated Show resolved Hide resolved
parcels/_interpolation.py Show resolved Hide resolved
parcels/_interpolation.py Outdated Show resolved Hide resolved
parcels/_interpolation.py Show resolved Hide resolved
parcels/_interpolation.py Outdated Show resolved Hide resolved

@dataclass
class InterpolationContext2D:
"""Information provided by Parcels during 2D spatial interpolation. See Delandmeter, P. and van Sebille, E (2019) for more info.
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
"""Information provided by Parcels during 2D spatial interpolation. See Delandmeter, P. and van Sebille, E (2019) for more info.
"""Information provided by Parcels during 2D spatial interpolation. See Delandmeter and Van Sebille (2019), 10.5194/gmd-12-3571-2019 for more info.
Suggested change
"""Information provided by Parcels during 2D spatial interpolation. See Delandmeter, P. and van Sebille, E (2019) for more info.
"""Information provided by Parcels during 2D spatial interpolation. See Delandmeter, P. and van Sebille, E (2019) for more info.


@dataclass
class InterpolationContext3D:
"""Information provided by Parcels during 3D spatial interpolation. See Delandmeter, P. and van Sebille, E (2019) for more info.
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
"""Information provided by Parcels during 3D spatial interpolation. See Delandmeter, P. and van Sebille, E (2019) for more info.
"""Information provided by Parcels during 3D spatial interpolation. See Delandmeter and Van Sebille (2019), 10.5194/gmd-12-3571-2019 for more info.

+ xsi * eta * grid.depth[:, yi + 1, xi + 1]
+ (1 - xsi) * eta * grid.depth[:, yi + 1, xi]
)
z = np.float32(z) # type: ignore # TODO: remove type ignore once we migrate to float64
Copy link
Member

Choose a reason for hiding this comment

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

OK, then I propose we remove it

Copy link
Member

@erikvansebille erikvansebille left a comment

Choose a reason for hiding this comment

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

More comments, now on all files except for the tests/* files

parcels/_interpolation.py Show resolved Hide resolved
+ xsi * eta * grid.depth[:, yi + 1, xi + 1]
+ (1 - xsi) * eta * grid.depth[:, yi + 1, xi]
)
z = np.float32(z) # type: ignore # TODO: remove type ignore once we migrate to float64
Copy link
Member

Choose a reason for hiding this comment

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

Then I propose we remove it

Suggested change
z = np.float32(z) # type: ignore # TODO: remove type ignore once we migrate to float64

Copy link
Member

Choose a reason for hiding this comment

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

Should we not also move the _search_indices(), _search_indices_curvilinear() and _search_indices_rectilinear() methods to the _index_search.py file? Why are they still in field.py?

In general, I see that even in this PR, field.py still contains a lot of methods/functions that don't specifically need to be here. That would really clean up the field.py file?

E.g. VectorField.dist, VectorField._is_land2D() and VectorField.jacobian can go to an interpolation_utils.py file (mimicking what is in include folder for C)?

And the spatial interpolation for VectorFields can also go to _interpolation.py?

Comment on lines +75 to +76
if msg == "show_time":
message += " Try explicitly providing a 'show_time'."
Copy link
Member

Choose a reason for hiding this comment

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

We don't have show_time anymore (was part of the old plotting we got rid off years ago, so this is not needed

Suggested change
if msg == "show_time":
message += " Try explicitly providing a 'show_time'."

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Status: Backlog
Development

Successfully merging this pull request may close these issues.

Refactor interpolation and indexing methods out of field.py
2 participants