Skip to content

Commit a6e2f7d

Browse files
(0.96.30) Fix vector rotation operation (#4560)
* fix vector rotation * add a more comprehensive test * Update vector_rotation_operators.jl * add more tests * fix vector rotation in case of zero spacing * test also the return * this is the only test we need * clean up testing * is this screwing up with the tests? * maybe it's a random problem * bump the project * add the rotation * improve comment * correction * new test * rotation_matrix -> rotation_angle * add rotation_angle docs * cleanup --------- Co-authored-by: Navid C. Constantinou <navidcy@users.noreply.github.com>
1 parent 6383a85 commit a6e2f7d

File tree

3 files changed

+157
-192
lines changed

3 files changed

+157
-192
lines changed

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name = "Oceananigans"
22
uuid = "9e8cae18-63c1-5223-a75c-80ca9d6e9a09"
33
authors = ["Climate Modeling Alliance and contributors"]
4-
version = "0.96.29"
4+
version = "0.96.30"
55

66
[deps]
77
Adapt = "79e6a3ab-5dfb-504d-930d-738a2a938a0e"

src/Operators/vector_rotation_operators.jl

Lines changed: 47 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ coordinate system associated with the domain, to the coordinate system _intrinsi
1313
1414
_extrinsic_ coordinate systems are:
1515
16-
- Cartesian for any grid that discretizes a Cartesian domain (e.g. a `RectilinearGrid`)
17-
- Geographic coordinates for any grid that discretizes a Spherical domain (e.g. an `AbstractCurvilinearGrid`)
16+
- Cartesian coordinates for any grid that discretizes a cartesian domain (e.g. a `RectilinearGrid`)
17+
- Geographic coordinates for any grid that discretizes a spherical domain (e.g. an `AbstractCurvilinearGrid`)
1818
1919
Therefore, for the [`RectilinearGrid`](@ref) and the [`LatitudeLongitudeGrid`](@ref), the _extrinsic_ and the
2020
_intrinsic_ coordinate system are equivalent. However, for other grids (e.g., for the
@@ -31,8 +31,8 @@ system of the grid, to the _extrinsic_ coordinate system associated with the dom
3131
3232
_extrinsic_ coordinate systems are:
3333
34-
- Cartesian for any grid that discretizes a Cartesian domain (e.g. a `RectilinearGrid`)
35-
- Geographic coordinates for any grid that discretizes a Spherical domain (e.g. an `AbstractCurvilinearGrid`)
34+
- Cartesian coordinates for any grid that discretizes a cartesian domain (e.g. a `RectilinearGrid`)
35+
- Geographic coordinates for any grid that discretizes a spherical domain (e.g. an `AbstractCurvilinearGrid`)
3636
3737
Therefore, for the [`RectilinearGrid`](@ref) and the [`LatitudeLongitudeGrid`](@ref), the _extrinsic_ and the
3838
_intrinsic_ coordinate systems are equivalent. However, for other grids (e.g., for the
@@ -48,14 +48,15 @@ _intrinsic_ coordinate systems are equivalent. However, for other grids (e.g., f
4848
@inline extrinsic_vector(i, j, k, grid::AbstractGrid, uᵢ, vᵢ) =
4949
getvalue(uᵢ, i, j, k, grid), getvalue(vᵢ, i, j, k, grid)
5050

51-
# Intrinsic and extrinsic conversion for `OrthogonalSphericalShellGrid`s,
52-
# i.e. curvilinear grids defined on a sphere which are locally orthogonal.
53-
# If the coordinates match with the coordinates of a latitude-longitude grid
54-
# (i.e. globally orthogonal), these functions collapse to
55-
# uₑ, vₑ, wₑ = uᵢ, vᵢ, wᵢ
5651

57-
# 2D vectors
58-
@inline function intrinsic_vector(i, j, k, grid::OrthogonalSphericalShellGrid, uₑ, vₑ)
52+
"""
53+
rotation_angle(i, j, grid::OrthogonalSphericalShellGrid)
54+
55+
Return the rotation angle (in degrees) of the `i, j`-th point of the `grid`.
56+
The rotation angle is the angle (positive counter-clockwise) that we need to rotate
57+
the grid's intrinsic coordinates in order to match the grid's extrinsic coordinates.
58+
"""
59+
@inline function rotation_angle(i, j, grid::OrthogonalSphericalShellGrid)
5960

6061
φᶠᶠᵃ⁺⁺ = φnode(i+1, j+1, 1, grid, Face(), Face(), Center())
6162
φᶠᶠᵃ⁺⁻ = φnode(i+1, j, 1, grid, Face(), Face(), Center())
@@ -67,21 +68,40 @@ _intrinsic_ coordinate systems are equivalent. However, for other grids (e.g., f
6768
Δxᶜᶠᵃ⁺ = Δxᶜᶠᶜ(i, j+1, 1, grid)
6869
Δxᶜᶠᵃ⁻ = Δxᶜᶠᶜ(i, j, 1, grid)
6970

70-
# θᵢ is the rotation angle between intrinsic and extrinsic reference frame
71-
Rcosθ = (deg2rad(φᶠᶠᵃ⁺⁺ - φᶠᶠᵃ⁺⁻) / Δyᶠᶜᵃ⁺ + deg2rad(φᶠᶠᵃ⁻⁺ - φᶠᶠᵃ⁻⁻) / Δyᶠᶜᵃ⁻) / 2
71+
Rcosθ₁ = ifelse(Δyᶠᶜᵃ⁺ == 0, zero(grid), deg2rad(φᶠᶠᵃ⁺⁺ - φᶠᶠᵃ⁺⁻) / Δyᶠᶜᵃ⁺)
72+
Rcosθ₂ = ifelse(Δyᶠᶜᵃ⁻ == 0, zero(grid), deg2rad(φᶠᶠᵃ⁻⁺ - φᶠᶠᵃ⁻⁻) / Δyᶠᶜᵃ⁻)
73+
74+
# θ is the rotation angle between intrinsic and extrinsic reference frame
75+
Rcosθ = (Rcosθ₁ + Rcosθ₂) / 2
7276
Rsinθ = - (deg2rad(φᶠᶠᵃ⁺⁺ - φᶠᶠᵃ⁻⁺) / Δxᶜᶠᵃ⁺ + deg2rad(φᶠᶠᵃ⁺⁻ - φᶠᶠᵃ⁻⁻) / Δxᶜᶠᵃ⁻) / 2
7377

7478
# Normalization for the rotation angles
7579
R = sqrt(Rcosθ^2 + Rsinθ^2)
7680

77-
u = getvalue(uₑ, i, j, k, grid)
78-
v = getvalue(vₑ, i, j, k, grid)
81+
cosθ, sinθ = Rcosθ / R, Rsinθ / R
82+
83+
θ_degrees = atand(sinθ / cosθ)
84+
return θ_degrees
85+
end
86+
87+
# Intrinsic and extrinsic conversion for `OrthogonalSphericalShellGrid`s,
88+
# i.e. curvilinear grids defined on a sphere which are locally orthogonal.
89+
# If the coordinates match with the coordinates of a latitude-longitude grid
90+
# (i.e. globally orthogonal), these functions collapse to
91+
# uₑ, vₑ, wₑ = uᵢ, vᵢ, wᵢ
92+
93+
# 2D vectors
94+
@inline function intrinsic_vector(i, j, k, grid::OrthogonalSphericalShellGrid, uₑ, vₑ)
7995

80-
cosθ = Rcosθ / R
81-
sinθ = Rsinθ / R
96+
u = getvalue(uₑ, i, j, k, grid)
97+
v = getvalue(vₑ, i, j, k, grid)
8298

83-
uᵢ = u * cosθ + v * sinθ
84-
vᵢ = - u * sinθ + v * cosθ
99+
θ_degrees = rotation_angle(i, j, grid::OrthogonalSphericalShellGrid)
100+
sinθ = sind(θ_degrees)
101+
cosθ = cosd(θ_degrees)
102+
103+
uᵢ = u * cosθ - v * sinθ
104+
vᵢ = u * sinθ + v * cosθ
85105

86106
return uᵢ, vᵢ
87107
end
@@ -98,31 +118,15 @@ end
98118
# 2D vectors
99119
@inline function extrinsic_vector(i, j, k, grid::OrthogonalSphericalShellGrid, uᵢ, vᵢ)
100120

101-
φᶠᶠᵃ⁺⁺ = φnode(i+1, j+1, 1, grid, Face(), Face(), Center())
102-
φᶠᶠᵃ⁺⁻ = φnode(i+1, j, 1, grid, Face(), Face(), Center())
103-
φᶠᶠᵃ⁻⁺ = φnode(i, j+1, 1, grid, Face(), Face(), Center())
104-
φᶠᶠᵃ⁻⁻ = φnode(i, j, 1, grid, Face(), Face(), Center())
121+
u = getvalue(uᵢ, i, j, k, grid)
122+
v = getvalue(vᵢ, i, j, k, grid)
105123

106-
Δyᶠᶜᵃ⁺ = Δyᶠᶜᶜ(i+1, j, 1, grid)
107-
Δyᶠᶜᵃ⁻ = Δyᶠᶜᶜ(i, j, 1, grid)
108-
Δxᶜᶠᵃ⁺ = Δxᶜᶠᶜ(i, j+1, 1, grid)
109-
Δxᶜᶠᵃ⁻ = Δxᶜᶠᶜ(i, j, 1, grid)
124+
θ_degrees = rotation_angle(i, j, grid::OrthogonalSphericalShellGrid)
125+
sinθ = sind(θ_degrees)
126+
cosθ = cosd(θ_degrees)
110127

111-
# θᵢ is the rotation angle between intrinsic and extrinsic reference frame
112-
Rcosθ = (deg2rad(φᶠᶠᵃ⁺⁺ - φᶠᶠᵃ⁺⁻) / Δyᶠᶜᵃ⁺ + deg2rad(φᶠᶠᵃ⁻⁺ - φᶠᶠᵃ⁻⁻) / Δyᶠᶜᵃ⁻) / 2
113-
Rsinθ = - (deg2rad(φᶠᶠᵃ⁺⁺ - φᶠᶠᵃ⁻⁺) / Δxᶜᶠᵃ⁺ + deg2rad(φᶠᶠᵃ⁺⁻ - φᶠᶠᵃ⁻⁻) / Δxᶜᶠᵃ⁻) / 2
114-
115-
# Normalization for the rotation angles
116-
R = sqrt(Rcosθ^2 + Rsinθ^2)
117-
118-
u = getvalue(uᵢ, i, j, k, grid)
119-
v = getvalue(vᵢ, i, j, k, grid)
120-
121-
cosθ = Rcosθ / R
122-
sinθ = Rsinθ / R
123-
124-
uₑ = u * cosθ - v * sinθ
125-
vₑ = u * sinθ + v * cosθ
128+
uₑ = + u * cosθ + v * sinθ
129+
vₑ = - u * sinθ + v * cosθ
126130

127131
return uₑ, vₑ
128132
end
@@ -134,4 +138,4 @@ end
134138
wₑ = getvalue(wᵢ, i, j, k, grid)
135139

136140
return uₑ, vₑ, wₑ
137-
end
141+
end

0 commit comments

Comments
 (0)