Skip to content

Commit 72f1f5a

Browse files
authored
Pressure solver for vertically stretched grids (#306)
Pressure solver for vertically stretched grids
2 parents 008e7c2 + 28e304b commit 72f1f5a

File tree

3 files changed

+175
-16
lines changed

3 files changed

+175
-16
lines changed

src/Solvers/batched_tridiagonal_solver.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,9 @@ where `dl` stores the lower diagonal coefficients `a(i, j, k)`, `d` stores the d
3434
3535
`params` is an optional named tuple of parameters that is accessible to functions.
3636
"""
37-
function BatchedTridiagonalSolver(arch=CPU(); dl, d, du, f, grid, params=nothing)
37+
function BatchedTridiagonalSolver(arch=CPU(), FT=Float64; dl, d, du, f, grid, params=nothing)
3838
ArrayType = array_type(arch)
39-
t = zeros(grid.Nx, grid.Ny, grid.Nz) |> ArrayType
39+
t = zeros(FT, grid.Nx, grid.Ny, grid.Nz) |> ArrayType
4040

4141
return BatchedTridiagonalSolver(dl, d, du, f, t, grid, params)
4242
end

test/test_pressure_solvers.jl

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,10 @@ function ∇²!(grid, f, ∇²f)
1010
end
1111
end
1212

13-
function fftw_planner_works(FT, Nx, Ny, Nz, planner_flag)
13+
function pressure_solver_instantiates(FT, Nx, Ny, Nz, planner_flag)
1414
grid = RegularCartesianGrid(FT; size=(Nx, Ny, Nz), length=(100, 100, 100))
15-
solver = PressureSolver(CPU(), grid, HorizontallyPeriodicBCs())
16-
true # Just making sure the PressureSolver does not error/crash.
15+
solver = PressureSolver(CPU(), grid, HorizontallyPeriodicBCs(), planner_flag)
16+
return true # Just making sure the PressureSolver does not error/crash.
1717
end
1818

1919
function poisson_ppn_planned_div_free_cpu(FT, Nx, Ny, Nz, planner_flag)
@@ -187,16 +187,16 @@ end
187187
@testset "Pressure solvers" begin
188188
@info "Testing pressure solvers..."
189189

190-
# @testset "FFTW plans" begin
191-
# @info " Testing FFTW planning..."
192-
#
193-
# for FT in float_types
194-
# @test fftw_planner_works(FT, 32, 32, 32, FFTW.ESTIMATE)
195-
# @test fftw_planner_works(FT, 1, 32, 32, FFTW.ESTIMATE)
196-
# @test fftw_planner_works(FT, 32, 1, 32, FFTW.ESTIMATE)
197-
# @test fftw_planner_works(FT, 1, 1, 32, FFTW.ESTIMATE)
198-
# end
199-
# end
190+
@testset "Pressure solver instantiation" begin
191+
@info " Testing pressure solver instantiation..."
192+
193+
for FT in float_types
194+
@test pressure_solver_instantiates(FT, 32, 32, 32, FFTW.ESTIMATE)
195+
@test pressure_solver_instantiates(FT, 1, 32, 32, FFTW.ESTIMATE)
196+
@test pressure_solver_instantiates(FT, 32, 1, 32, FFTW.ESTIMATE)
197+
@test pressure_solver_instantiates(FT, 1, 1, 32, FFTW.ESTIMATE)
198+
end
199+
end
200200

201201
@testset "Divergence-free solution [CPU]" begin
202202
@info " Testing divergence-free solution [CPU]..."

test/test_solvers.jl

Lines changed: 160 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
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!
35

46
function can_solve_single_tridiagonal_system(arch, N)
57
ArrayType = array_type(arch)
@@ -118,6 +120,153 @@ function can_solve_batched_tridiagonal_system_with_3D_functions(arch, Nx, Ny, Nz
118120
return Array(ϕ) ϕ_correct
119121
end
120122

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+
= 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+
121270
@testset "Solvers" begin
122271
@info "Testing Solvers..."
123272

@@ -134,4 +283,14 @@ end
134283
end
135284
end
136285
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
137296
end

0 commit comments

Comments
 (0)