Skip to content

Conversation

@juliasloan25
Copy link
Member

@juliasloan25 juliasloan25 commented Dec 4, 2025

Prototypes regridding using ConservativeRegridding with FV approximation

Closes #2398

Content

  • Add extension to ClimaCore; update Project deps
  • Copy functions over from ConservativeRegridding.jl test: get_element_vertices, integrate_each_element, get_value_per_element!, set_value_per_element!
  • Copy test from ConservativeRegridding.jl test, update to use new functions
  • Add a Regridder constructor for ClimaCore spaces (and fields?)
  • Extend regrid! for ClimaCore fields - think about intermediate fields

New method ambiguities

This PR increases the number of method ambiguities because ConservativeRegridding.jl pulls in GeometryOps, leading to more ambiguities of broadcasted. For example, in GeometryOps, they define broadcasted(f, a::AbstractArray{T}, b::GeometryOps.UnitSpherical.UnitSphericalPoint) and in ClimaCore, we define broadcasted(op::ClimaCore.Operators.FiniteDifferenceOperator, args...). Thank you to @ph-kev for finding this.

A note on zero-area elements when using an odd number of elements

When using a space with odd h_elem (but not even) we encounter zero-area polygons. This is because when we have odd h_elem, there are elements centered around the north and south poles. Their vertices each lie along a line of constant latitude. When we then compute planar area of this element, we get 0 area, but if we were to compute the correct spherical area it would be nonzero.

ConservativeRegridding.jl currently computes simple planar areas, so it currently fails when using a space with even h_elem. This should be resolved once we switch to computing spherical areas (coming soon).

Thank you to @imreddyTeja and @ph-kev for tracking this down!

Prototype regridding latitude

Source field

field1

Source field with one value per element

field1_one_value_per_element

Destination field (remapped)

field2
  • Code follows the style guidelines OR N/A.
  • Unit tests are included OR N/A.
  • Code is exercised in an integration test OR N/A.
  • Documentation has been added/updated OR N/A.

@ph-kev
Copy link
Member

ph-kev commented Dec 13, 2025

Thanks to @imreddyTeja for helping with debuging the zero area polygons. The zero area polygons are line segments on a lonlat grid. If you convert to Cartesian coordinates, and plot them on a XYZ grid, then the vertices of the elements have positive area. In particular, they correspond to polygons at the north and south poles.

See the plot below where I plotted the vertices of one of the zero area polygons as yellow nodes.
image

@juliasloan25 juliasloan25 changed the title prototype ConservativeRegridding with FV approximation add ConservativeRegridding extension Dec 15, 2025
@juliasloan25 juliasloan25 force-pushed the js/kp/conservative branch 2 times, most recently from eeb08ee to 52748ca Compare January 5, 2026 23:09
@juliasloan25 juliasloan25 force-pushed the js/kp/conservative branch 3 times, most recently from ce2b29b to e998d45 Compare January 7, 2026 18:12
Comment on lines +46 to +51
# Check for zero area polygons (all latitude or longitude values are the same)
for polygon in vertices
if allequal(first.(polygon)) || allequal(last.(polygon))
@error "Zero area polygon found in vertices" polygon
end
end
Copy link
Member

Choose a reason for hiding this comment

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

I think the error message can be better here. They are only zero area polygons, because the polygons are on a longitude-latitude grid. If you view the polygons on a sphere, then they are not zero area. It might also be helpful to point out that the current implementation of ConservativeRegridding does not work with these zero area polygons in the docstring or error message.

Comment on lines +31 to +34
CartesianIndex(i, j, 1, 1, e) # f and v are 1 for SpectralElementSpace2D
for e in 1:Nh
for (i, j) in [(1, 1), (1, Nq), (Nq, Nq), (Nq, 1), (1, 1)]
] # repeat the first coordinate pair at the end
Copy link
Member

Choose a reason for hiding this comment

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

Does this only work with a SpectralElementSpace2D? If it is, can you add a type annotation for that?

Comment on lines +96 to +100
function Remapping.get_value_per_element!(
value_per_element,
field,
ones_field,
)
Copy link
Member

Choose a reason for hiding this comment

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

Is this function meant to be used by ClimaCore users or is it internal? If it is the latter, can you rename this to _get_value_per_element!? If it the former, can you add a check for ones_field that the field contains all ones or refactor this to remove that argument?

Comment on lines +138 to +139
Create a regridder between two ClimaCore Spaces.
This works by finding the vertices of the elements of the source and
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
Create a regridder between two ClimaCore Spaces.
This works by finding the vertices of the elements of the source and
Create a regridder between two ClimaCore spaces.
This works by finding the vertices of the elements of the source and

Comment on lines +64 to +65
function Remapping.integrate_each_element(field)
space = axes(field)
Copy link
Member

Choose a reason for hiding this comment

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

Can you add a type annotation for field?

Comment on lines +171 to +173
- `value_per_element_src`: Pre-allocated buffer for source values
- `value_per_element_dst`: Pre-allocated buffer for destination values
- `ones_src`: Pre-allocated field of ones on the source space
Copy link
Member

Choose a reason for hiding this comment

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

Is it possible to encapsulate this in the regridder object? It seems error prone to expect an user to make this NamedTuple. Alternatively, is it possible to make a helper function for constructing this?

Comment on lines +148 to +151
function ConservativeRegridding.Regridder(
dst_space::Spaces.SpectralElementSpace2D,
src_space::Spaces.SpectralElementSpace2D;
kwargs...,
Copy link
Member

Choose a reason for hiding this comment

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

What are the keyword arguments? I couldn't find them.

Comment on lines +209 to +213
function ConservativeRegridding.regrid!(
dst_field::Fields.Field,
regridder::ConservativeRegridding.Regridder,
src_field::Fields.Field,
)
Copy link
Member

Choose a reason for hiding this comment

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

I am not sure if it is worth doing, but if you think the backend (what interpolator is being used) will change, you can put this regridder in another struct and have the user be exposed to that as the public interface. Then, developers can change what is happening behind the scene without breaking anything.

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.

Write a function to convert 2D ClimaCore space to vector of coordinates

2 participants