|
1 | 1 | using LinearAlgebra
|
2 |
| -using Oceananigans: array_type |
| 2 | +using Oceananigans.Architectures: array_type |
| 3 | +using Oceananigans.BoundaryConditions: NoPenetrationBC |
| 4 | +using Oceananigans.TimeSteppers: _compute_w_from_continuity! |
3 | 5 |
|
4 | 6 | function can_solve_single_tridiagonal_system(arch, N)
|
5 | 7 | ArrayType = array_type(arch)
|
@@ -118,6 +120,153 @@ function can_solve_batched_tridiagonal_system_with_3D_functions(arch, Nx, Ny, Nz
|
118 | 120 | return Array(ϕ) ≈ ϕ_correct
|
119 | 121 | end
|
120 | 122 |
|
| 123 | +function vertically_stretched_poisson_solver_correct_answer(arch, Nx, Ny, zF) |
| 124 | + Lx, Ly, Lz = 1, 1, zF[end] |
| 125 | + Δx, Δy = Lx/Nx, Ly/Ny |
| 126 | + |
| 127 | + ##### |
| 128 | + ##### Vertically stretched operators |
| 129 | + ##### |
| 130 | + |
| 131 | + @inline δx_caa(i, j, k, f) = @inbounds f[i+1, j, k] - f[i, j, k] |
| 132 | + @inline δy_aca(i, j, k, f) = @inbounds f[i, j+1, k] - f[i, j, k] |
| 133 | + @inline δz_aac(i, j, k, f) = @inbounds f[i, j, k+1] - f[i, j, k] |
| 134 | + |
| 135 | + @inline ∂x_caa(i, j, k, Δx, f) = δx_caa(i, j, k, f) / Δx |
| 136 | + @inline ∂y_aca(i, j, k, Δy, f) = δy_aca(i, j, k, f) / Δy |
| 137 | + @inline ∂z_aac(i, j, k, ΔzF, f) = δz_aac(i, j, k, f) / ΔzF[k] |
| 138 | + |
| 139 | + @inline ∂x²(i, j, k, Δx, f) = (∂x_caa(i, j, k, Δx, f) - ∂x_caa(i-1, j, k, Δx, f)) / Δx |
| 140 | + @inline ∂y²(i, j, k, Δy, f) = (∂y_aca(i, j, k, Δy, f) - ∂y_aca(i, j-1, k, Δy, f)) / Δy |
| 141 | + @inline ∂z²(i, j, k, ΔzF, ΔzC, f) = (∂z_aac(i, j, k, ΔzF, f) - ∂z_aac(i, j, k-1, ΔzF, f)) / ΔzC[k] |
| 142 | + |
| 143 | + @inline div_ccc(i, j, k, Δx, Δy, ΔzF, u, v, w) = ∂x_caa(i, j, k, Δx, u) + ∂y_aca(i, j, k, Δy, v) + ∂z_aac(i, j, k, ΔzF, w) |
| 144 | + |
| 145 | + @inline ∇²(i, j, k, Δx, Δy, ΔzF, ΔzC, f) = ∂x²(i, j, k, Δx, f) + ∂y²(i, j, k, Δy, f) + ∂z²(i, j, k, ΔzF, ΔzC, f) |
| 146 | + |
| 147 | + ##### |
| 148 | + ##### Generate "fake" vertically stretched grid |
| 149 | + ##### |
| 150 | + |
| 151 | + function grid(zF) |
| 152 | + Nz = length(zF) - 1 |
| 153 | + ΔzF = [zF[k+1] - zF[k] for k in 1:Nz] |
| 154 | + zC = [(zF[k] + zF[k+1]) / 2 for k in 1:Nz] |
| 155 | + ΔzC = [zC[k+1] - zC[k] for k in 1:Nz-1] |
| 156 | + return zF, zC, ΔzF, ΔzC |
| 157 | + end |
| 158 | + |
| 159 | + Nz = length(zF) - 1 |
| 160 | + zF, zC, ΔzF, ΔzC = grid(zF) |
| 161 | + |
| 162 | + # Need some halo regions. |
| 163 | + ΔzF = OffsetArray([ΔzF[1], ΔzF...], 0:Nz) |
| 164 | + ΔzC = [ΔzC..., ΔzC[end]] |
| 165 | + |
| 166 | + # Useful for broadcasting z operations |
| 167 | + ΔzC = reshape(ΔzC, (1, 1, Nz)) |
| 168 | + |
| 169 | + # Temporary hack: Useful for reusing fill_halo_regions! and BatchedTridiagonalSolver |
| 170 | + # which only need Nx, Ny, Nz. |
| 171 | + fake_grid = RegularCartesianGrid(size=(Nx, Ny, Nz), length=(Lx, Ly, Lz)) |
| 172 | + |
| 173 | + ##### |
| 174 | + ##### Generate batched tridiagonal system coefficients and solver |
| 175 | + ##### |
| 176 | + |
| 177 | + function λi(Nx, Δx) |
| 178 | + is = reshape(1:Nx, Nx, 1, 1) |
| 179 | + @. (2sin((is-1)*π/Nx) / Δx)^2 |
| 180 | + end |
| 181 | + |
| 182 | + function λj(Ny, Δy) |
| 183 | + js = reshape(1:Ny, 1, Ny, 1) |
| 184 | + @. (2sin((js-1)*π/Ny) / Δy)^2 |
| 185 | + end |
| 186 | + |
| 187 | + kx² = λi(Nx, Δx) |
| 188 | + ky² = λj(Ny, Δy) |
| 189 | + |
| 190 | + # Lower and upper diagonals are the same |
| 191 | + ld = [1/ΔzF[k] for k in 1:Nz-1] |
| 192 | + ud = copy(ld) |
| 193 | + |
| 194 | + # Diagonal (different for each i,j) |
| 195 | + @inline δ(k, ΔzF, ΔzC, kx², ky²) = - (1/ΔzF[k-1] + 1/ΔzF[k]) - ΔzC[k] * (kx² + ky²) |
| 196 | + |
| 197 | + d = zeros(Nx, Ny, Nz) |
| 198 | + for i in 1:Nx, j in 1:Ny |
| 199 | + d[i, j, 1] = -1/ΔzF[1] - ΔzC[1] * (kx²[i] + ky²[j]) |
| 200 | + d[i, j, 2:Nz-1] .= [δ(k, ΔzF, ΔzC, kx²[i], ky²[j]) for k in 2:Nz-1] |
| 201 | + d[i, j, Nz] = -1/ΔzF[Nz-1] - ΔzC[Nz] * (kx²[i] + ky²[j]) |
| 202 | + end |
| 203 | + |
| 204 | + ##### |
| 205 | + ##### Random right hand side |
| 206 | + ##### |
| 207 | + |
| 208 | + # Random right hand side |
| 209 | + Ru = CellField(Float64, arch, fake_grid) |
| 210 | + Rv = CellField(Float64, arch, fake_grid) |
| 211 | + Rw = CellField(Float64, arch, fake_grid) |
| 212 | + |
| 213 | + interior(Ru) .= rand(Nx, Ny, Nz) |
| 214 | + interior(Rv) .= rand(Nx, Ny, Nz) |
| 215 | + interior(Rw) .= zeros(Nx, Ny, Nz) |
| 216 | + U = (u=Ru, v=Rv, w=Rw) |
| 217 | + |
| 218 | + uv_bcs = HorizontallyPeriodicBCs() |
| 219 | + w_bcs = HorizontallyPeriodicBCs(top=NoPenetrationBC(), bottom=NoPenetrationBC()) |
| 220 | + |
| 221 | + fill_halo_regions!(Ru.data, uv_bcs, arch, fake_grid) |
| 222 | + fill_halo_regions!(Rv.data, uv_bcs, arch, fake_grid) |
| 223 | + |
| 224 | + _compute_w_from_continuity!(U, fake_grid) |
| 225 | + |
| 226 | + fill_halo_regions!(Rw.data, w_bcs, arch, fake_grid) |
| 227 | + |
| 228 | + R = zeros(Nx, Ny, Nz) |
| 229 | + for i in 1:Nx, j in 1:Ny, k in 1:Nz |
| 230 | + R[i, j, k] = div_ccc(i, j, k, Δx, Δy, ΔzF, Ru.data, Rv.data, Rw.data) |
| 231 | + end |
| 232 | + |
| 233 | + # @show sum(R) # should be zero by construction. |
| 234 | + |
| 235 | + F = zeros(Nx, Ny, Nz) |
| 236 | + F = ΔzC .* R # RHS needs to be multiplied by ΔzC |
| 237 | + |
| 238 | + ##### |
| 239 | + ##### Solve system |
| 240 | + ##### |
| 241 | + |
| 242 | + F̃ = fft(F, [1, 2]) |
| 243 | + |
| 244 | + btsolver = BatchedTridiagonalSolver(arch, dl=ld, d=d, du=ud, f=F̃, grid=fake_grid) |
| 245 | + |
| 246 | + ϕ̃ = zeros(Complex{Float64}, Nx, Ny, Nz) |
| 247 | + solve_batched_tridiagonal_system!(ϕ̃, arch, btsolver) |
| 248 | + |
| 249 | + ϕ = CellField(Float64, arch, fake_grid) |
| 250 | + interior(ϕ) .= real.(ifft(ϕ̃, [1, 2])) |
| 251 | + ϕ.data .= ϕ.data .- mean(interior(ϕ)) |
| 252 | + |
| 253 | + ##### |
| 254 | + ##### Compute Laplacian of solution ϕ to test that it's correct |
| 255 | + ##### |
| 256 | + |
| 257 | + # Gotta fill halo regions |
| 258 | + fbcs = HorizontallyPeriodicBCs() |
| 259 | + fill_halo_regions!(ϕ.data, fbcs, arch, fake_grid) |
| 260 | + |
| 261 | + ∇²ϕ = CellField(Float64, arch, fake_grid) |
| 262 | + |
| 263 | + for i in 1:Nx, j in 1:Ny, k in 1:Nz |
| 264 | + ∇²ϕ.data[i, j, k] = ∇²(i, j, k, Δx, Δy, ΔzF, ΔzC, ϕ.data) |
| 265 | + end |
| 266 | + |
| 267 | + return interior(∇²ϕ) ≈ R |
| 268 | +end |
| 269 | + |
121 | 270 | @testset "Solvers" begin
|
122 | 271 | @info "Testing Solvers..."
|
123 | 272 |
|
|
134 | 283 | end
|
135 | 284 | end
|
136 | 285 | end
|
| 286 | + |
| 287 | + for arch in [CPU()] |
| 288 | + @testset "Vertically stretched Poisson solver [FACR, $arch]" begin |
| 289 | + @info " Testing vertically stretched Poisson solver [FACR, $arch]..." |
| 290 | + |
| 291 | + Nx = Ny = 8 |
| 292 | + zF = [1, 2, 4, 7, 11, 16, 22, 29, 37] |
| 293 | + @test vertically_stretched_poisson_solver_correct_answer(arch, Nx, Ny, zF) |
| 294 | + end |
| 295 | + end |
137 | 296 | end
|
0 commit comments