11using CubedSphere. SphericalGeometry
2- using Oceananigans. Grids: LatitudeLongitudeGrid, Bounded
2+ using Oceananigans. Grids: LatitudeLongitudeGrid, Bounded, validate_lat_lon_grid_args
33using Oceananigans. Utils: KernelParameters
44using StaticArrays
55using LinearAlgebra
@@ -59,24 +59,26 @@ z = (0, 1)
5959grid = RotatedLatitudeLongitudeGrid(; size, longitude, latitude, z, north_pole=(70, 55))
6060
6161# output
62- 90×40×1 OrthogonalSphericalShellGrid{Float64, Bounded , Bounded, Bounded} on CPU with 3×3×3 halo and with precomputed metrics
63- ├── centered at (λ, φ) = (146.656, 11.3134 )
64- ├── longitude: Bounded extent 360.0 degrees variably spaced with min(Δλ)=0.694593, max(Δλ)=4.0
65- ├── latitude: Bounded extent 160.0 degrees variably spaced with min(Δφ)=4.0, max(Δφ)=4.0
66- └── z: Bounded z ∈ [0.0, 1.0] regularly spaced with Δz=1.0
62+ 90×40×1 OrthogonalSphericalShellGrid{Float64, Periodic , Bounded, Bounded} on CPU with 3×3×3 halo and with precomputed metrics
63+ ├── centered at (λ, φ) = (-110.0, 35.0 )
64+ ├── longitude: Periodic extent 360.0 degrees variably spaced with min(Δλ)=0.694593, max(Δλ)=4.0
65+ ├── latitude: Bounded extent 160.0 degrees variably spaced with min(Δφ)=4.0, max(Δφ)=4.0
66+ └── z: Bounded z ∈ [0.0, 1.0] regularly spaced with Δz=1.0
6767```
6868
69- We can also make an ordinary LatitudeLongitudeGrid using `north_polar = (0, 90)`:
69+ Note that the center latitude ``λ = -110`` follows from ``180 + 70 - 360 = -110``:
70+ a clockwise rotation of 70 degrees modulo 360 degrees.
71+ We can also make an ordinary LatitudeLongitudeGrid using `north_pole = (0, 90)`:
7072
7173```jldoctest rllg
7274grid = RotatedLatitudeLongitudeGrid(; size, longitude, latitude, z, north_pole=(0, 90))
7375
7476# output
75- 90×40×1 OrthogonalSphericalShellGrid{Float64, Bounded , Bounded, Bounded} on CPU with 3×3×3 halo and with precomputed metrics
77+ 90×40×1 OrthogonalSphericalShellGrid{Float64, Periodic , Bounded, Bounded} on CPU with 3×3×3 halo and with precomputed metrics
7678├── centered at (λ, φ) = (180.0, 0.0)
77- ├── longitude: Bounded extent 360.0 degrees variably spaced with min(Δλ)=0.694593, max(Δλ)=4.0
78- ├── latitude: Bounded extent 160.0 degrees variably spaced with min(Δφ)=4.0, max(Δφ)=4.0
79- └── z: Bounded z ∈ [0.0, 1.0] regularly spaced with Δz=1.0
79+ ├── longitude: Periodic extent 360.0 degrees variably spaced with min(Δλ)=0.694593, max(Δλ)=4.0
80+ ├── latitude: Bounded extent 160.0 degrees variably spaced with min(Δφ)=4.0, max(Δφ)=4.0
81+ └── z: Bounded z ∈ [0.0, 1.0] regularly spaced with Δz=1.0
8082```
8183"""
8284function RotatedLatitudeLongitudeGrid (arch:: AbstractArchitecture = CPU (),
@@ -88,15 +90,23 @@ function RotatedLatitudeLongitudeGrid(arch::AbstractArchitecture = CPU(),
8890 z,
8991 halo = (3 , 3 , 3 ),
9092 radius = Oceananigans. defaults. planet_radius,
91- topology = (Bounded, Bounded, Bounded) )
93+ topology = nothing )
9294
93- shifted_halo = halo .+ 1
94- source_grid = LatitudeLongitudeGrid (arch, FT; size, z, topology, radius,
95- latitude, longitude, halo = shifted_halo)
95+ precompute_metrics = true
96+ topology, size, halo, latitude, longitude, z, precompute_metrics =
97+ validate_lat_lon_grid_args (topology, size, halo, FT, latitude, longitude, z, precompute_metrics)
98+
99+ _, φ₀ = north_pole
100+
101+ if φ₀ < 0
102+ throw (ArgumentError (" North pole latitude must be >= 0." ))
103+ elseif φ₀ > 90
104+ throw (ArgumentError (" North pole latitude must be <= 90 degrees." ))
105+ end
96106
97- Nx, Ny, Nz = size
98- Hx, Hy, Hz = halo_size (source_grid)
99- Lz = source_grid . Lz
107+ shifted_halo = halo .+ 1
108+ source_grid = LatitudeLongitudeGrid (arch, FT; halo = shifted_halo, precompute_metrics,
109+ size, z, topology, radius, latitude, longitude)
100110
101111 conformal_mapping = LatitudeLongitudeRotation (north_pole)
102112 grid = OrthogonalSphericalShellGrid (arch, FT; size, z, radius, halo, topology, conformal_mapping)
@@ -136,44 +146,50 @@ function cartesian_to_spherical(X)
136146end
137147
138148# Rotation about x-axis by dλ (Change in Longitude)
139- x_rotation (dλ) = @SMatrix [1 0 0
140- 0 cos (dλ) - sin (dλ)
141- 0 sin (dλ) cos (dλ)]
149+ x_rotation_matrix (dλ) = @SMatrix [1 0 0
150+ 0 cos (dλ) - sin (dλ)
151+ 0 sin (dλ) cos (dλ)]
142152
143153# Rotation about y-axis by dφ (Change in Latitude)
144- y_rotation (dφ) = @SMatrix [ cos (dφ) 0 sin (dφ)
145- 0 1 0
146- - sin (dφ) 0 cos (dφ)]
154+ y_rotation_matrix (dφ) = @SMatrix [ cos (dφ) 0 sin (dφ)
155+ 0 1 0
156+ - sin (dφ) 0 cos (dφ)]
147157
148158# Rotation about z-axis by dλ (Change in Longitude)
149- z_rotation (dλ) = @SMatrix [cos (dλ) - sin (dλ) 0
150- sin (dλ) cos (dλ) 0
151- 0 0 1 ]
159+ z_rotation_matrix (dλ) = @SMatrix [cos (dλ) - sin (dλ) 0
160+ sin (dλ) cos (dλ) 0
161+ 0 0 1 ]
162+
163+ """
164+ rotate_coordinates(λ′, φ′, λ₀, φ₀)
152165
153- # Perform the rotation
166+ Return the geographic longitude and latitude `(λ, φ)` corresponding to the rotated
167+ coordinates `(λ′, φ′)` on a grid whose north pole is located at `(λ₀, φ₀)`. All
168+ arguments are interpreted in degrees. The rotation aligns the grid pole with the
169+ geographic pole, then maps the rotated point back to geographic coordinates.
170+ """
154171function rotate_coordinates (λ′, φ′, λ₀, φ₀)
155- λ′ *= π/ 180
156- φ′ *= π/ 180
157- λ₀ *= π/ 180
158- φ₀ *= π/ 180
172+ λ′ *= π / 180
173+ φ′ *= π / 180
174+ λ₀ *= π / 180
175+ φ₀ *= π / 180
159176
160- dλ = - λ₀
177+ dλ = λ₀
161178 dφ = π/ 2 - φ₀
162179
163180 # Convert to Cartesian
164181 X′ = SVector (spherical_to_cartesian (φ′, λ′; check_latitude_bounds = false )... )
165182
166183 # Rotate Cartesian coordinates
167- Rx = x_rotation (dλ)
168- Ry = y_rotation (dφ)
169- Rz = z_rotation (dλ)
170- X = Rx * Ry * X′
184+ Ry = y_rotation_matrix (dφ)
185+ Rz = z_rotation_matrix (dλ)
186+ X = Rz * Ry * X′
171187
172188 # Convert back to Spherical
173189 φ, λ = cartesian_to_spherical (X)
174190
175- λ *= 180 / π
176- φ *= 180 / π
191+ λ *= 180 / π
192+ φ *= 180 / π
177193
178194 return λ, φ
179195end
0 commit comments