Skip to content

Commit 3023a1d

Browse files
Docs, Examples, and Validation for the XESMF extension (#4823)
* permutedims only before giving to python * fix docstring example * fix docstring example * fix docstring example * add docstrings in docs * try add XESMF.Regridder in Docs * fix syntax * fix type * add XESMF * it's a Regridder * simplify * add docs for OceananigansXESMFExt.regrid! * cleanup * cleaner * two gaussians * align * fix docstring * exponential -> gaussian * no need for amplitude kwarg * add validation scripts --------- Co-authored-by: Simone Silvestri <[email protected]>
1 parent 40b3275 commit 3023a1d

File tree

9 files changed

+1407
-45
lines changed

9 files changed

+1407
-45
lines changed

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ StaticArrays = "1"
101101
Statistics = "1.9"
102102
StructArrays = "0.4, 0.5, 0.6, 0.7"
103103
TimesDates = "0.3"
104-
XESMF = "0.1.4"
104+
XESMF = "0.1.5"
105105
julia = "1.9"
106106
oneAPI = "2.0.1"
107107

docs/Project.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ Rotations = "6038ab10-8711-5258-84ad-4b1120ba62dc"
1313
SeawaterPolynomials = "d496a93d-167e-4197-9f49-d3af4ff8fe40"
1414
StructArrays = "09ab397b-f2b6-538f-b94a-2f83cf4a842a"
1515
TimesDates = "bdfc003b-8df8-5c39-adcd-3a9087f5df4a"
16+
XESMF = "2e0b0046-e7a1-486f-88de-807ee8ffabe5"
1617

1718
[compat]
1819
CairoMakie = "0.11, 0.12, 0.13, 0.14"

docs/make.jl

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,16 @@ Distributed.addprocs(2)
1111
set_theme!(Theme(fontsize=20))
1212
CairoMakie.activate!(type = "svg")
1313

14-
using Oceananigans
1514
using NCDatasets
15+
using XESMF
16+
17+
using Oceananigans
18+
using Oceananigans.AbstractOperations
1619
using Oceananigans.Operators
1720
using Oceananigans.Diagnostics
1821
using Oceananigans.OutputWriters
19-
using Oceananigans.TurbulenceClosures
2022
using Oceananigans.TimeSteppers
21-
using Oceananigans.AbstractOperations
22-
23+
using Oceananigans.TurbulenceClosures
2324
using Oceananigans.BoundaryConditions: Flux, Value, Gradient, Open
2425

2526
bib_filepath = joinpath(dirname(@__FILE__), "oceananigans.bib")
@@ -173,18 +174,20 @@ format = Documenter.HTML(collapselevel = 1,
173174

174175
DocMeta.setdocmeta!(Oceananigans, :DocTestSetup, :(using Oceananigans); recursive=true)
175176

176-
OceananigansNCDatasetsExt = if isdefined(Base, :get_extension)
177-
Base.get_extension(Oceananigans, :OceananigansNCDatasetsExt)
178-
else
179-
Oceananigans.OceananigansNCDatasetsExt
177+
modules = Module[]
178+
OceananigansNCDatasetsExt = isdefined(Base, :get_extension) ? Base.get_extension(Oceananigans, :OceananigansNCDatasetsExt) : Oceananigans.OceananigansNCDatasetsExt
179+
OceananigansXESMFExt = isdefined(Base, :get_extension) ? Base.get_extension(Oceananigans, :OceananigansXESMFExt) : Oceananigans.OceananigansXESMFExt
180+
181+
for m in [Oceananigans, XESMF, OceananigansNCDatasetsExt, OceananigansXESMFExt]
182+
if !isnothing(m)
183+
push!(modules, m)
184+
end
180185
end
181186

182-
makedocs(sitename = "Oceananigans.jl",
187+
makedocs(; sitename = "Oceananigans.jl",
183188
authors = "Climate Modeling Alliance and contributors",
184-
format = format,
185-
pages = pages,
189+
format, pages, modules,
186190
plugins = [bib],
187-
modules = [Oceananigans, OceananigansNCDatasetsExt],
188191
warnonly = [:cross_references],
189192
draft = false, # set to true to speed things up
190193
doctest = true, # set to false to speed things up

docs/src/appendix/library.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,10 @@ Private = false
7171
Modules = [Oceananigans.Fields]
7272
Private = false
7373
```
74+
```@docs
75+
XESMF.Regridder
76+
OceananigansXESMFExt.regrid!
77+
```
7478

7579
## Forcings
7680

ext/OceananigansXESMFExt.jl

Lines changed: 41 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -9,24 +9,28 @@ using Oceananigans.Grids: AbstractGrid, λnodes, φnodes, Center, Face, total_le
99
import Oceananigans.Fields: regrid!
1010
import XESMF: Regridder
1111

12-
# permutedims below is used because Python's xESMF expects
13-
# 2D arrays with (x, y) coordinates with y varying in dim=1 and x varying in dim=2
12+
node_array::AbstractMatrix, Nx, Ny) = view(ξ, 1:Nx, 1:Ny)
1413

15-
node_array::AbstractMatrix, Nx, Ny) = permutedims(view(ξ, 1:Nx, 1:Ny), (2, 1))
16-
vertex_array::AbstractMatrix, Nx, Ny) = permutedims(view(ξ, 1:Nx+1, 1:Ny+1), (2, 1))
17-
18-
x_node_array(x::AbstractVector, Nx, Ny) = permutedims(repeat(view(x, 1:Nx), 1, Ny), (2, 1))
14+
x_node_array(x::AbstractVector, Nx, Ny) = repeat(view(x, 1:Nx), 1, Ny)
1915
x_node_array(x::AbstractMatrix, Nx, Ny) = node_array(x, Nx, Ny)
2016

21-
y_node_array(y::AbstractVector, Nx, Ny) = repeat(view(y, 1:Ny), 1, Nx)
17+
y_node_array(y::AbstractVector, Nx, Ny) = repeat(transpose(view(y, 1:Ny)), Nx, 1)
2218
y_node_array(y::AbstractMatrix, Nx, Ny) = node_array(y, Nx, Ny)
2319

24-
x_vertex_array(x::AbstractVector, Nx, Ny) = permutedims(repeat(view(x, 1:Nx+1), 1, Ny+1), (2, 1))
20+
vertex_array::AbstractMatrix, Nx, Ny) = view(ξ, 1:Nx+1, 1:Ny+1)
21+
22+
x_vertex_array(x::AbstractVector, Nx, Ny) = repeat(view(x, 1:Nx+1), 1, Ny+1)
2523
x_vertex_array(x::AbstractMatrix, Nx, Ny) = vertex_array(x, Nx, Ny)
2624

27-
y_vertex_array(y::AbstractVector, Nx, Ny) = repeat(view(y, 1:Ny+1), 1, Nx+1)
25+
y_vertex_array(y::AbstractVector, Nx, Ny) = repeat(transpose(view(y, 1:Ny+1)), Nx+1, 1)
2826
y_vertex_array(y::AbstractMatrix, Nx, Ny) = vertex_array(y, Nx, Ny)
2927

28+
"""
29+
xesmf_coordinates(grid::AbstractGrid, ℓx, ℓy, ℓz)
30+
31+
Extract the coordinates (latitude/longitude) and the coordinates' bounds from
32+
`grid` at locations `ℓx, ℓy, ℓz`.
33+
"""
3034
function xesmf_coordinates(grid::AbstractGrid, ℓx, ℓy, ℓz)
3135
Nx, Ny, Nz = size(grid)
3236

@@ -44,23 +48,26 @@ function xesmf_coordinates(grid::AbstractGrid, ℓx, ℓy, ℓz)
4448
λv = x_vertex_array(λv, Nx, Ny)
4549
φv = y_vertex_array(φv, Nx, Ny)
4650

47-
return Dict("lat" => φ, # φ is latitude
48-
"lon" => λ, # λ is longitude
49-
"lat_b" => φv,
50-
"lon_b" => λv)
51-
end
52-
53-
function xesmf_coordinates(dst_field::AbstractField, src_field::AbstractField)
54-
55-
ℓx, ℓy, ℓz = Oceananigans.Fields.instantiated_location(src_field)
51+
# Python's xESMF expects 2D arrays with (x, y) coordinates
52+
# in which y varies in dim=1 and x varies in dim=2
53+
# therefore we transpose the coordinate matrices
54+
coords_dictionary = Dict("lat" => permutedims(φ, (2, 1)), # φ is latitude
55+
"lon" => permutedims(λ, (2, 1)), # λ is longitude
56+
"lat_b" => permutedims(φv, (2, 1)),
57+
"lon_b" => permutedims(λv, (2, 1)))
5658

57-
dst_grid = dst_field.grid
58-
src_grid = src_field.grid
59+
return coords_dictionary
60+
end
5961

60-
dst_coordinates = xesmf_coordinates(dst_grid, ℓx, ℓy, ℓz)
61-
src_coordinates = xesmf_coordinates(src_grid, ℓx, ℓy, ℓz)
62+
"""
63+
xesmf_coordinates(field::AbstractField)
6264
63-
return dst_coordinates, src_coordinates
65+
Extract the coordinates (latitude/longitude) and the coordinates' bounds from
66+
the `field`'s grid.
67+
"""
68+
function xesmf_coordinates(field::AbstractField)
69+
ℓx, ℓy, ℓz = Oceananigans.Fields.instantiated_location(field)
70+
return xesmf_coordinates(field.grid, ℓx, ℓy, ℓz)
6471
end
6572

6673
"""
@@ -89,20 +96,24 @@ For more information, see the Python xESMF documentation at:
8996
Example
9097
=======
9198
92-
```@example
99+
To create a regridder for two fields that live on different grids.
100+
101+
```@example regridding
93102
using Oceananigans
94103
using XESMF
95104
96105
z = (-1, 0)
97-
tg = TripolarGrid(; size=(360, 170, 1), z, southernmost_latitude = -80)
98-
llg = LatitudeLongitudeGrid(; size=(360, 180, 1), z,
106+
tg = TripolarGrid(; size=(180, 85, 1), z, southernmost_latitude = -80)
107+
llg = LatitudeLongitudeGrid(; size=(170, 80, 1), z,
99108
longitude=(0, 360), latitude=(-82, 90))
100109
101110
src_field = CenterField(tg)
102111
dst_field = CenterField(llg)
103112
104-
regridder = Oceananigans.Fields.Regridder(dst_field, src_field, method="conservative")
113+
regridder = XESMF.Regridder(dst_field, src_field, method="conservative")
105114
```
115+
116+
We can use the above regridder to regrid via [`regrid!`](@ref).
106117
"""
107118
function Regridder(dst_field::AbstractField, src_field::AbstractField; method="conservative")
108119

@@ -117,7 +128,8 @@ function Regridder(dst_field::AbstractField, src_field::AbstractField; method="c
117128
dst_Nz = size(dst_field)[3]
118129
@assert src_field.grid.z.cᵃᵃᶠ[1:src_Nz+1] == dst_field.grid.z.cᵃᵃᶠ[1:dst_Nz+1]
119130

120-
dst_coordinates, src_coordinates = xesmf_coordinates(dst_field, src_field)
131+
dst_coordinates = xesmf_coordinates(dst_field)
132+
src_coordinates = xesmf_coordinates(src_field)
121133
periodic = Oceananigans.Grids.topology(src_field.grid, 1) === Periodic ? true : false
122134

123135
regridder = XESMF.Regridder(src_coordinates, dst_coordinates; method, periodic)
@@ -155,7 +167,7 @@ llg = LatitudeLongitudeGrid(; size=(360, 180, 1), z,
155167
src_field = CenterField(tg)
156168
dst_field = CenterField(llg)
157169
158-
λ₀, φ₀ = 150, 30. # degrees
170+
λ₀, φ₀ = 150, 30 # degrees
159171
width = 12 # degrees
160172
set!(src_field, (λ, φ, z) -> exp(-((λ - λ₀)^2 + (φ - φ₀)^2) / 2width^2))
161173

test/test_xesmf.jl

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ using XESMF
44
using SparseArrays
55
using LinearAlgebra
66

7+
gaussian_bump(λ, φ; λ₀=0, φ₀=0, width=10) = exp(-((λ - λ₀)^2 +- φ₀)^2) / 2width^2)
8+
79
for arch in archs
810
@testset "XESMF extension [$(typeof(arch))]" begin
911
@info "Testing XESMF regridding [$(typeof(arch))]..."
@@ -34,9 +36,9 @@ for arch in archs
3436
src_field = CenterField(src_grid)
3537
dst_field = CenterField(dst_grid)
3638

37-
λ₀, φ₀ = 150, 30. # degrees
3839
width = 12 # degrees
39-
set!(src_field, (λ, φ, z) -> exp(-((λ - λ₀)^2 +- φ₀)^2) / 2width^2))
40+
set!(src_field,
41+
(λ, φ, z) -> gaussian_bump(λ, φ; λ₀=150, φ₀=30, width) - 2gaussian_bump(λ, φ; λ₀=270, φ₀=-20, width))
4042

4143
regridder = XESMF.Regridder(dst_field, src_field)
4244

validation/regridding/latitude_longitude_regridding.jl

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,4 +56,3 @@ scatter!(ax5, Array(interior(c_xy, 180, 60, :)), z)
5656
scatter!(ax6, Array(interior(c_xyz, 180, 60, :)), z′)
5757

5858
display(fig)
59-

0 commit comments

Comments
 (0)