Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enforce gauge condition in preconditioners for ConjugateGradientPoissonSolver #3802

Merged
merged 13 commits into from
Oct 1, 2024
9 changes: 5 additions & 4 deletions src/BoundaryConditions/field_boundary_conditions.jl
Original file line number Diff line number Diff line change
Expand Up @@ -156,11 +156,12 @@ end
# Friendly warning?
function regularize_immersed_boundary_condition(ibc, grid, loc, field_name, args...)
if !(ibc isa DefaultBoundaryCondition)
msg = """
$field_name was assigned an immersed $ibc, but this is not supported on
$(summary(grid))
msg = """$field_name was assigned an immersed boundary condition
$ibc ,
but this is not supported on
$(summary(grid)) .
The immersed boundary condition on $field_name will have no effect.
"""
"""

@warn msg
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ where
𝐮_⋆ = 𝐮^n + \\int_{t_n}^{t_{n+1}} 𝐆ᵤ \\, 𝖽t .
```

This equation can be solved, in general, using the [`PreconditionedConjugateGradientSolver`](@ref) but
This equation can be solved, in general, using the [`ConjugateGradientSolver`](@ref) but
other solvers can be invoked in special cases.

If ``H`` is constant, we divide through out to obtain
Expand All @@ -69,7 +69,7 @@ surface can be obtained using the [`FFTBasedPoissonSolver`](@ref).
`solver_method` can be either of:
* `:FastFourierTransform` for [`FFTBasedPoissonSolver`](@ref)
* `:HeptadiagonalIterativeSolver` for [`HeptadiagonalIterativeSolver`](@ref)
* `:PreconditionedConjugateGradient` for [`PreconditionedConjugateGradientSolver`](@ref)
* `:PreconditionedConjugateGradient` for [`ConjugateGradientSolver`](@ref)

By default, if the grid has regular spacing in the horizontal directions then the `:FastFourierTransform` is chosen,
otherwise the `:HeptadiagonalIterativeSolver`.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ function PCGImplicitFreeSurfaceSolver(grid::AbstractGrid, settings, gravitationa
# TODO: reuse solver.storage for rhs when preconditioner isa FFTImplicitFreeSurfaceSolver?
right_hand_side = ZFaceField(grid, indices = (:, :, size(grid, 3) + 1))

solver = PreconditionedConjugateGradientSolver(implicit_free_surface_linear_operation!;
solver = ConjugateGradientSolver(implicit_free_surface_linear_operation!;
template_field = right_hand_side,
settings...)

Expand Down
1 change: 1 addition & 0 deletions src/Models/Models.jl
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ using Oceananigans.Utils: Time
import Oceananigans: initialize!
import Oceananigans.Architectures: architecture
import Oceananigans.TimeSteppers: reset!
import Oceananigans.Solvers: iteration

# A prototype interface for AbstractModel.
#
Expand Down
28 changes: 9 additions & 19 deletions src/Models/NonhydrostaticModels/NonhydrostaticModels.jl
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ using Oceananigans.DistributedComputations: reconstruct_global_grid, Distributed
using Oceananigans.DistributedComputations: DistributedFFTBasedPoissonSolver, DistributedFourierTridiagonalPoissonSolver
using Oceananigans.Grids: XYRegularRG, XZRegularRG, YZRegularRG, XYZRegularRG
using Oceananigans.ImmersedBoundaries: ImmersedBoundaryGrid
using Oceananigans.Solvers: GridWithFFTSolver, GridWithFourierTridiagonalSolver
using Oceananigans.Utils: SumOfArrays

import Oceananigans: fields, prognostic_fields
Expand All @@ -26,35 +27,24 @@ function nonhydrostatic_pressure_solver(::Distributed, local_grid::XYZRegularRG)
return DistributedFFTBasedPoissonSolver(global_grid, local_grid)
end

function nonhydrostatic_pressure_solver(::Distributed, local_grid::XYRegularRG)
global_grid = reconstruct_global_grid(local_grid)
return DistributedFourierTridiagonalPoissonSolver(global_grid, local_grid)
end

function nonhydrostatic_pressure_solver(::Distributed, local_grid::YZRegularRG)
global_grid = reconstruct_global_grid(local_grid)
return DistributedFourierTridiagonalPoissonSolver(global_grid, local_grid)
end

function nonhydrostatic_pressure_solver(::Distributed, local_grid::XZRegularRG)
function nonhydrostatic_pressure_solver(::Distributed, local_grid::GridWithFourierTridiagonalSolver)
global_grid = reconstruct_global_grid(local_grid)
return DistributedFourierTridiagonalPoissonSolver(global_grid, local_grid)
end

nonhydrostatic_pressure_solver(arch, grid::XYZRegularRG) = FFTBasedPoissonSolver(grid)
nonhydrostatic_pressure_solver(arch, grid::XYRegularRG) = FourierTridiagonalPoissonSolver(grid)
nonhydrostatic_pressure_solver(arch, grid::XZRegularRG) = FourierTridiagonalPoissonSolver(grid)
nonhydrostatic_pressure_solver(arch, grid::YZRegularRG) = FourierTridiagonalPoissonSolver(grid)
nonhydrostatic_pressure_solver(arch, grid::GridWithFourierTridiagonalSolver) =
FourierTridiagonalPoissonSolver(grid)

const GridWithFFT = Union{XYZRegularRG, XYRegularRG, XZRegularRG, YZRegularRG}
const IBGWithFFTSolver = ImmersedBoundaryGrid{<:Any, <:Any, <:Any, <:Any, <:GridWithFFTSolver}

function nonhydrostatic_pressure_solver(arch, ibg::ImmersedBoundaryGrid{<:Any, <:Any, <:Any, <:Any, <:GridWithFFT})
msg = """The FFT-based pressure_solver for `NonhydrostaticModel`s on `ImmersedBoundaryGrid`
function nonhydrostatic_pressure_solver(arch, ibg::IBGWithFFTSolver)
msg = """The FFT-based pressure_solver for NonhydrostaticModels on ImmersedBoundaryGrid
is approximate and will probably produce velocity fields that are divergent
adjacent to the immersed boundary. An experimental but improved pressure_solver
is available which may be used by writing

using Oceananigans.Models.NonhydrostaticModels: ConjugateGradientPoissonSolver
using Oceananigans.Solvers: ConjugateGradientPoissonSolver
pressure_solver = ConjugateGradientPoissonSolver(grid)

Please report issues to https://github.com/CliMA/Oceananigans.jl/issues.
Expand Down Expand Up @@ -115,9 +105,9 @@ include("solve_for_pressure.jl")
include("update_hydrostatic_pressure.jl")
include("update_nonhydrostatic_model_state.jl")
include("pressure_correction.jl")
include("conjugate_gradient_poisson_solver.jl")
include("nonhydrostatic_tendency_kernel_functions.jl")
include("compute_nonhydrostatic_tendencies.jl")
include("compute_nonhydrostatic_boundary_tendencies.jl")

end # module

107 changes: 52 additions & 55 deletions src/Models/NonhydrostaticModels/solve_for_pressure.jl
Original file line number Diff line number Diff line change
@@ -1,94 +1,91 @@
using Oceananigans.Operators
using Oceananigans.Solvers: FFTBasedPoissonSolver, FourierTridiagonalPoissonSolver, solve!
using Oceananigans.DistributedComputations: DistributedFFTBasedPoissonSolver
using Oceananigans.Grids: XDirection, YDirection, ZDirection
using Oceananigans.Grids: XDirection, YDirection, ZDirection, inactive_cell
using Oceananigans.Solvers: FFTBasedPoissonSolver, FourierTridiagonalPoissonSolver
using Oceananigans.Solvers: ConjugateGradientPoissonSolver
using Oceananigans.Solvers: solve!

#####
##### Calculate the right-hand-side of the non-hydrostatic pressure Poisson equation.
#####

@kernel function calculate_pressure_source_term_fft_based_solver!(rhs, grid, Δt, U★)
@kernel function _compute_source_term!(rhs, grid, Δt, )
i, j, k = @index(Global, NTuple)
@inbounds rhs[i, j, k] = divᶜᶜᶜ(i, j, k, grid, U★.u, U★.v, U★.w) / Δt
active = !inactive_cell(i, j, k, grid)
δ = divᶜᶜᶜ(i, j, k, grid, Ũ.u, Ũ.v, Ũ.w)
@inbounds rhs[i, j, k] = active * δ / Δt
end

@kernel function calculate_pressure_source_term_fourier_tridiagonal_solver!(rhs, grid, Δt, U★, ::XDirection)
@kernel function _fourier_tridiagonal_source_term!(rhs, ::XDirection, grid, Δt, )
i, j, k = @index(Global, NTuple)
@inbounds rhs[i, j, k] = Δxᶜᶜᶜ(i, j, k, grid) * divᶜᶜᶜ(i, j, k, grid, U★.u, U★.v, U★.w) / Δt
active = !inactive_cell(i, j, k, grid)
δ = divᶜᶜᶜ(i, j, k, grid, Ũ.u, Ũ.v, Ũ.w)
@inbounds rhs[i, j, k] = active * Δxᶜᶜᶜ(i, j, k, grid) * δ / Δt
end

@kernel function calculate_pressure_source_term_fourier_tridiagonal_solver!(rhs, grid, Δt, U★, ::YDirection)
@kernel function _fourier_tridiagonal_source_term!(rhs, ::YDirection, grid, Δt, )
i, j, k = @index(Global, NTuple)
@inbounds rhs[i, j, k] = Δyᶜᶜᶜ(i, j, k, grid) * divᶜᶜᶜ(i, j, k, grid, U★.u, U★.v, U★.w) / Δt
active = !inactive_cell(i, j, k, grid)
δ = divᶜᶜᶜ(i, j, k, grid, Ũ.u, Ũ.v, Ũ.w)
@inbounds rhs[i, j, k] = active * Δyᶜᶜᶜ(i, j, k, grid) * δ / Δt
end

@kernel function calculate_pressure_source_term_fourier_tridiagonal_solver!(rhs, grid, Δt, U★, ::ZDirection)
@kernel function _fourier_tridiagonal_source_term!(rhs, ::ZDirection, grid, Δt, )
i, j, k = @index(Global, NTuple)
@inbounds rhs[i, j, k] = Δzᶜᶜᶜ(i, j, k, grid) * divᶜᶜᶜ(i, j, k, grid, U★.u, U★.v, U★.w) / Δt
active = !inactive_cell(i, j, k, grid)
δ = divᶜᶜᶜ(i, j, k, grid, Ũ.u, Ũ.v, Ũ.w)
@inbounds rhs[i, j, k] = active * Δzᶜᶜᶜ(i, j, k, grid) * δ / Δt
end

#####
##### Solve for pressure
#####

function solve_for_pressure!(pressure, solver::DistributedFFTBasedPoissonSolver, Δt, U★)
function compute_source_term!(pressure, solver::DistributedFFTBasedPoissonSolver, Δt, Ũ)
rhs = solver.storage.zfield
arch = architecture(solver)
grid = solver.local_grid

launch!(arch, grid, :xyz, calculate_pressure_source_term_fft_based_solver!,
rhs, grid, Δt, U★)

# Solve pressure Poisson equation for pressure, given rhs
solve!(pressure, solver)

return pressure
launch!(arch, grid, :xyz, _compute_source_term!, rhs, grid, Δt, Ũ)
return nothing
end

function solve_for_pressure!(pressure, solver::FFTBasedPoissonSolver, Δt, U★)

# Calculate right hand side:
rhs = solver.storage
function compute_source_term!(pressure, solver::DistributedFourierTridiagonalPoissonSolver, Δt, Ũ)
rhs = solver.storage.zfield
arch = architecture(solver)
grid = solver.grid

launch!(arch, grid, :xyz, calculate_pressure_source_term_fft_based_solver!,
rhs, grid, Δt, U★)

# Solve pressure Poisson given for pressure, given rhs
solve!(pressure, solver, rhs)

grid = solver.local_grid
tdir = solver.batched_tridiagonal_solver.tridiagonal_direction
launch!(arch, grid, :xyz, _fourier_tridiagonal_source_term!, rhs, tdir, grid, Δt, Ũ)
return nothing
end

function solve_for_pressure!(pressure, solver::DistributedFourierTridiagonalPoissonSolver, Δt, U★)

# Calculate right hand side:
rhs = solver.storage.zfield
function compute_source_term!(pressure, solver::FourierTridiagonalPoissonSolver, Δt, Ũ)
rhs = solver.source_term
arch = architecture(solver)
grid = solver.local_grid

launch!(arch, grid, :xyz, calculate_pressure_source_term_fourier_tridiagonal_solver!,
rhs, grid, Δt, U★, solver.batched_tridiagonal_solver.tridiagonal_direction)

# Pressure Poisson rhs, scaled by the spacing in the stretched direction at ᶜᶜᶜ, is stored in solver.source_term:
solve!(pressure, solver)

grid = solver.grid
tdir = solver.batched_tridiagonal_solver.tridiagonal_direction
launch!(arch, grid, :xyz, _fourier_tridiagonal_source_term!, rhs, tdir, grid, Δt, Ũ)
return nothing
end

function solve_for_pressure!(pressure, solver::FourierTridiagonalPoissonSolver, Δt, U★)

# Calculate right hand side:
rhs = solver.source_term
function compute_source_term!(pressure, solver::FFTBasedPoissonSolver, Δt, Ũ)
rhs = solver.storage
arch = architecture(solver)
grid = solver.grid
launch!(arch, grid, :xyz, _compute_source_term!, rhs, grid, Δt, Ũ)
return nothing
end

launch!(arch, grid, :xyz, calculate_pressure_source_term_fourier_tridiagonal_solver!,
rhs, grid, Δt, U★, solver.batched_tridiagonal_solver.tridiagonal_direction)
#####
##### Solve for pressure
#####

# Pressure Poisson rhs, scaled by the spacing in the stretched direction at ᶜᶜᶜ, is stored in solver.source_term:
function solve_for_pressure!(pressure, solver, Δt, Ũ)
compute_source_term!(pressure, solver, Δt, Ũ)
solve!(pressure, solver)
return pressure
end

return nothing
function solve_for_pressure!(pressure, solver::ConjugateGradientPoissonSolver, Δt, Ũ)
rhs = solver.right_hand_side
grid = solver.grid
arch = architecture(grid)
launch!(arch, grid, :xyz, _compute_source_term!, rhs, grid, Δt, Ũ)
return solve!(pressure, solver.conjugate_gradient_solver, rhs)
end

4 changes: 2 additions & 2 deletions src/MultiRegion/multi_region_models.jl
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ using Oceananigans.TurbulenceClosures: VerticallyImplicitTimeDiscretization
using Oceananigans.Advection: AbstractAdvectionScheme
using Oceananigans.Advection: OnlySelfUpwinding, CrossAndSelfUpwinding
using Oceananigans.ImmersedBoundaries: GridFittedBottom, PartialCellBottom, GridFittedBoundary
using Oceananigans.Solvers: PreconditionedConjugateGradientSolver
using Oceananigans.Solvers: ConjugateGradientSolver

import Oceananigans.Advection: WENO, cell_advection_timescale
import Oceananigans.Models.HydrostaticFreeSurfaceModels: build_implicit_step_solver, validate_tracer_advection
Expand All @@ -34,7 +34,7 @@ Types = (:HydrostaticFreeSurfaceModel,
:SplitExplicitState,
:SplitExplicitFreeSurface,
:PrescribedVelocityFields,
:PreconditionedConjugateGradientSolver,
:ConjugateGradientSolver,
:CrossAndSelfUpwinding,
:OnlySelfUpwinding,
:GridFittedBoundary,
Expand Down
16 changes: 13 additions & 3 deletions src/Solvers/Solvers.jl
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ export
BatchedTridiagonalSolver, solve!,
FFTBasedPoissonSolver,
FourierTridiagonalPoissonSolver,
PreconditionedConjugateGradientSolver,
ConjugateGradientSolver,
HeptadiagonalIterativeSolver

using Statistics
Expand All @@ -14,12 +14,14 @@ using SparseArrays
using KernelAbstractions

using Oceananigans.Architectures: device, CPU, GPU, array_type, on_architecture
using Oceananigans.BoundaryConditions: fill_halo_regions!
using Oceananigans.Utils
using Oceananigans.Grids
using Oceananigans.BoundaryConditions
using Oceananigans.Fields

using Oceananigans.Grids: unpack_grid
using Oceananigans.Grids: unpack_grid, inactive_cell
using Oceananigans.Grids: XYRegularRG, XZRegularRG, YZRegularRG, XYZRegularRG

"""
ω(M, k)
Expand All @@ -33,16 +35,24 @@ reshaped_size(N, dim) = dim == 1 ? (N, 1, 1) :
dim == 3 ? (1, 1, N) : nothing

include("batched_tridiagonal_solver.jl")
include("conjugate_gradient_solver.jl")
include("poisson_eigenvalues.jl")
include("index_permutations.jl")
include("discrete_transforms.jl")
include("plan_transforms.jl")
include("fft_based_poisson_solver.jl")
include("fourier_tridiagonal_poisson_solver.jl")
include("preconditioned_conjugate_gradient_solver.jl")
include("conjugate_gradient_poisson_solver.jl")
include("sparse_approximate_inverse.jl")
include("matrix_solver_utils.jl")
include("sparse_preconditioners.jl")
include("heptadiagonal_iterative_solver.jl")

const GridWithFFTSolver = Union{XYZRegularRG, XYRegularRG, XZRegularRG, YZRegularRG}
const GridWithFourierTridiagonalSolver = Union{XYRegularRG, XZRegularRG, YZRegularRG}

fft_poisson_solver(grid::XYZRegularRG) = FFTBasedPoissonSolver(grid)
fft_poisson_solver(grid::GridWithFourierTridiagonalSolver) =
FourierTridiagonalPoissonSolver(grid.underlying_grid)

end # module
Loading