diff --git a/ext/OceananigansMakieExt.jl b/ext/OceananigansMakieExt.jl index d214b8281c..e88cb8f0a7 100644 --- a/ext/OceananigansMakieExt.jl +++ b/ext/OceananigansMakieExt.jl @@ -38,7 +38,7 @@ end convert_arguments(pl::Type{<:AbstractPlot}, f::Field) = convert_arguments(pl, convert_field_argument(f)...) -function convert_arguments(pl::Type{<:AbstractPlot}, fop::AbstractOperation) +function convert_arguments(pl::Type{<:AbstractPlot}, op::AbstractOperation) f = Field(op) compute!(f) return convert_arguments(pl, f) diff --git a/src/Grids/Grids.jl b/src/Grids/Grids.jl index fd8aebbf21..27bd8f3a4a 100644 --- a/src/Grids/Grids.jl +++ b/src/Grids/Grids.jl @@ -18,6 +18,7 @@ export xnodes, ynodes, znodes, λnodes, φnodes export spacings export xspacings, yspacings, zspacings, xspacing, yspacing, zspacing export minimum_xspacing, minimum_yspacing, minimum_zspacing +export static_column_depthᶜᶜᵃ, static_column_depthᶠᶜᵃ, static_column_depthᶜᶠᵃ, static_column_depthᶠᶠᵃ export offset_data, new_data export on_architecture diff --git a/src/Grids/grid_utils.jl b/src/Grids/grid_utils.jl index c881ebeb86..178d7ab87c 100644 --- a/src/Grids/grid_utils.jl +++ b/src/Grids/grid_utils.jl @@ -314,6 +314,15 @@ coordinate_summary(topo, Δ::Union{AbstractVector, AbstractMatrix}, name) = name, prettysummary(minimum(parent(Δ))), name, prettysummary(maximum(parent(Δ)))) +##### +##### Static column depth +##### + +@inline static_column_depthᶜᶜᵃ(i, j, grid) = grid.Lz +@inline static_column_depthᶜᶠᵃ(i, j, grid) = grid.Lz +@inline static_column_depthᶠᶜᵃ(i, j, grid) = grid.Lz +@inline static_column_depthᶠᶠᵃ(i, j, grid) = grid.Lz + ##### ##### Spherical geometry ##### diff --git a/src/ImmersedBoundaries/ImmersedBoundaries.jl b/src/ImmersedBoundaries/ImmersedBoundaries.jl index b15f6ab591..7047948e31 100644 --- a/src/ImmersedBoundaries/ImmersedBoundaries.jl +++ b/src/ImmersedBoundaries/ImmersedBoundaries.jl @@ -17,15 +17,18 @@ import Base: show, summary import Oceananigans.Grids: cpu_face_constructor_x, cpu_face_constructor_y, cpu_face_constructor_z, x_domain, y_domain, z_domain -import Oceananigans.Grids: architecture, on_architecture, with_halo, inflate_halo_size_one_dimension, +import Oceananigans.Grids: architecture, with_halo, inflate_halo_size_one_dimension, xnode, ynode, znode, λnode, φnode, node, ξnode, ηnode, rnode, ξname, ηname, rname, node_names, xnodes, ynodes, znodes, λnodes, φnodes, nodes, ξnodes, ηnodes, rnodes, + static_column_depthᶜᶜᵃ, static_column_depthᶠᶜᵃ, static_column_depthᶜᶠᵃ, static_column_depthᶠᶠᵃ, inactive_cell +import Oceananigans.Architectures: on_architecture + import Oceananigans.Fields: fractional_x_index, fractional_y_index, fractional_z_index """ @@ -92,10 +95,6 @@ with_halo(halo, ibg::ImmersedBoundaryGrid) = inflate_halo_size_one_dimension(req_H, old_H, _, ::IBG) = max(req_H + 1, old_H) inflate_halo_size_one_dimension(req_H, old_H, ::Type{Flat}, ::IBG) = 0 -# Defining the bottom -@inline z_bottom(i, j, grid) = znode(i, j, 1, grid, c, c, f) -@inline z_bottom(i, j, ibg::IBG) = error("The function `bottom` has not been defined for $(summary(ibg))!") - function Base.summary(grid::ImmersedBoundaryGrid) FT = eltype(grid) TX, TY, TZ = topology(grid) diff --git a/src/ImmersedBoundaries/abstract_grid_fitted_boundary.jl b/src/ImmersedBoundaries/abstract_grid_fitted_boundary.jl index 64dad56766..2516b89c3c 100644 --- a/src/ImmersedBoundaries/abstract_grid_fitted_boundary.jl +++ b/src/ImmersedBoundaries/abstract_grid_fitted_boundary.jl @@ -18,21 +18,3 @@ const AGFB = AbstractGridFittedBoundary @inline immersed_cell(i, j, k, grid::AbstractGrid{<:Any, <:Any, Flat, Flat}, ib::AGFB) = _immersed_cell(i, 1, 1, grid, ib) @inline immersed_cell(i, j, k, grid::AbstractGrid{<:Any, Flat, Flat, Flat}, ib::AGFB) = _immersed_cell(1, 1, 1, grid, ib) end - -function clamp_bottom_height!(bottom_field, grid) - launch!(architecture(grid), grid, :xy, _clamp_bottom_height!, bottom_field, grid) - return nothing -end - -const c = Center() -const f = Face() - -@kernel function _clamp_bottom_height!(z, grid) - i, j = @index(Global, NTuple) - Nz = size(grid, 3) - zmin = znode(i, j, 1, grid, c, c, f) - zmax = znode(i, j, Nz+1, grid, c, c, f) - @inbounds z[i, j, 1] = clamp(z[i, j, 1], zmin, zmax) -end - - diff --git a/src/ImmersedBoundaries/grid_fitted_bottom.jl b/src/ImmersedBoundaries/grid_fitted_bottom.jl index 53e2aa2805..b2b1b2d2fc 100644 --- a/src/ImmersedBoundaries/grid_fitted_bottom.jl +++ b/src/ImmersedBoundaries/grid_fitted_bottom.jl @@ -18,14 +18,14 @@ abstract type AbstractGridFittedBottom{H} <: AbstractGridFittedBoundary end struct CenterImmersedCondition end struct InterfaceImmersedCondition end -Base.summary(::CenterImmersedCondition) = "CenterImmersedCondition" -Base.summary(::InterfaceImmersedCondition) = "InterfaceImmersedCondition" - struct GridFittedBottom{H, I} <: AbstractGridFittedBottom{H} bottom_height :: H immersed_condition :: I end +Base.summary(::CenterImmersedCondition) = "CenterImmersedCondition" +Base.summary(::InterfaceImmersedCondition) = "InterfaceImmersedCondition" + const GFBIBG = ImmersedBoundaryGrid{<:Any, <:Any, <:Any, <:Any, <:Any, <:GridFittedBottom} """ @@ -36,6 +36,7 @@ Return a bottom immersed boundary. Keyword Arguments ================= + * `bottom_height`: an array or function that gives the height of the bottom in absolute ``z`` coordinates. @@ -72,50 +73,77 @@ Base.summary(ib::GridFittedBottom{<:Function}) = @sprintf("GridFittedBottom(%s)" function Base.show(io::IO, ib::GridFittedBottom) print(io, summary(ib), '\n') print(io, "├── bottom_height: ", prettysummary(ib.bottom_height), '\n') - print(io, "└── immersed_condition: ", summary(ib.immersed_condition)) end -@inline z_bottom(i, j, ibg::GFBIBG) = @inbounds ibg.immersed_boundary.bottom_height[i, j, 1] +on_architecture(arch, ib::GridFittedBottom) = GridFittedBottom(on_architecture(arch, ib.bottom_height), ib.immersed_condition) + +function on_architecture(arch, ib::GridFittedBottom{<:Field}) + architecture(ib.bottom_height) == arch && return ib + arch_grid = on_architecture(arch, ib.bottom_height.grid) + new_bottom_height = Field{Center, Center, Nothing}(arch_grid) + set!(new_bottom_height, ib.bottom_height) + fill_halo_regions!(new_bottom_height) + return GridFittedBottom(new_bottom_height, ib.immersed_condition) +end + +Adapt.adapt_structure(to, ib::GridFittedBottom) = GridFittedBottom(adapt(to, ib.bottom_height), adapt(to, ib.immersed_condition)) """ ImmersedBoundaryGrid(grid, ib::GridFittedBottom) Return a grid with `GridFittedBottom` immersed boundary (`ib`). -Computes `ib.bottom_height` and wraps it in a Field. +Computes `ib.bottom_height` and wraps it in a Field. `ib.bottom_height` is the z-coordinate of top-most interface +of the last ``immersed`` cell in the column. """ function ImmersedBoundaryGrid(grid, ib::GridFittedBottom) bottom_field = Field{Center, Center, Nothing}(grid) set!(bottom_field, ib.bottom_height) - @apply_regionally clamp_bottom_height!(bottom_field, grid) + @apply_regionally compute_numerical_bottom_height!(bottom_field, grid, ib) fill_halo_regions!(bottom_field) - new_ib = GridFittedBottom(bottom_field, ib.immersed_condition) + new_ib = GridFittedBottom(bottom_field) TX, TY, TZ = topology(grid) return ImmersedBoundaryGrid{TX, TY, TZ}(grid, new_ib) end -@inline function _immersed_cell(i, j, k, underlying_grid, ib::GridFittedBottom{<:Any, <:InterfaceImmersedCondition}) - z = znode(i, j, k+1, underlying_grid, c, c, f) - h = @inbounds ib.bottom_height[i, j, 1] - return z ≤ h +compute_numerical_bottom_height!(bottom_field, grid, ib) = + launch!(architecture(grid), grid, :xy, _compute_numerical_bottom_height!, bottom_field, grid, ib) + +@kernel function _compute_numerical_bottom_height!(bottom_field, grid, ib::GridFittedBottom) + i, j = @index(Global, NTuple) + zb = @inbounds bottom_field[i, j, 1] + @inbounds bottom_field[i, j, 1] = znode(i, j, 1, grid, c, c, f) + condition = ib.immersed_condition + for k in 1:grid.Nz + z⁺ = znode(i, j, k+1, grid, c, c, f) + z = znode(i, j, k, grid, c, c, c) + bottom_cell = ifelse(condition isa CenterImmersedCondition, z ≤ zb, z⁺ ≤ zb) + @inbounds bottom_field[i, j, 1] = ifelse(bottom_cell, z⁺, zb) + end end -@inline function _immersed_cell(i, j, k, underlying_grid, ib::GridFittedBottom{<:Any, <:CenterImmersedCondition}) - z = znode(i, j, k, underlying_grid, c, c, c) - h = @inbounds ib.bottom_height[i, j, 1] - return z ≤ h +@inline function _immersed_cell(i, j, k, underlying_grid, ib::GridFittedBottom) + z = znode(i, j, k, underlying_grid, c, c, c) + zb = @inbounds ib.bottom_height[i, j, 1] + return z ≤ zb end -on_architecture(arch, ib::GridFittedBottom) = GridFittedBottom(ib.bottom_height, ib.immersed_condition) +##### +##### Static column depth +##### -function on_architecture(arch, ib::GridFittedBottom{<:Field}) - architecture(ib.bottom_height) == arch && return ib - arch_grid = on_architecture(arch, ib.bottom_height.grid) - new_bottom_height = Field{Center, Center, Nothing}(arch_grid) - set!(new_bottom_height, ib.bottom_height) - fill_halo_regions!(new_bottom_height) - return GridFittedBottom(new_bottom_height, ib.immersed_condition) -end +const AGFBIBG = ImmersedBoundaryGrid{<:Any, <:Any, <:Any, <:Any, <:Any, <:AbstractGridFittedBottom} + +@inline static_column_depthᶜᶜᵃ(i, j, ibg::AGFBIBG) = @inbounds znode(i, j, ibg.Nz+1, ibg, c, c, f) - ibg.immersed_boundary.bottom_height[i, j, 1] +@inline static_column_depthᶜᶠᵃ(i, j, ibg::AGFBIBG) = min(static_column_depthᶜᶜᵃ(i, j-1, ibg), static_column_depthᶜᶜᵃ(i, j, ibg)) +@inline static_column_depthᶠᶜᵃ(i, j, ibg::AGFBIBG) = min(static_column_depthᶜᶜᵃ(i-1, j, ibg), static_column_depthᶜᶜᵃ(i, j, ibg)) +@inline static_column_depthᶠᶠᵃ(i, j, ibg::AGFBIBG) = min(static_column_depthᶠᶜᵃ(i, j-1, ibg), static_column_depthᶠᶜᵃ(i, j, ibg)) + +# Make sure column_height works for horizontally-Flat topologies. +XFlatAGFIBG = ImmersedBoundaryGrid{<:Any, <:Flat, <:Any, <:Any, <:Any, <:AbstractGridFittedBottom} +YFlatAGFIBG = ImmersedBoundaryGrid{<:Any, <:Any, <:Flat, <:Any, <:Any, <:AbstractGridFittedBottom} -Adapt.adapt_structure(to, ib::GridFittedBottom) = GridFittedBottom(adapt(to, ib.bottom_height), - ib.immersed_condition) +@inline static_column_depthᶠᶜᵃ(i, j, ibg::XFlatAGFIBG) = static_column_depthᶜᶜᵃ(i, j, ibg) +@inline static_column_depthᶜᶠᵃ(i, j, ibg::YFlatAGFIBG) = static_column_depthᶜᶜᵃ(i, j, ibg) +@inline static_column_depthᶠᶠᵃ(i, j, ibg::XFlatAGFIBG) = static_column_depthᶜᶠᵃ(i, j, ibg) +@inline static_column_depthᶠᶠᵃ(i, j, ibg::YFlatAGFIBG) = static_column_depthᶠᶜᵃ(i, j, ibg) diff --git a/src/ImmersedBoundaries/partial_cell_bottom.jl b/src/ImmersedBoundaries/partial_cell_bottom.jl index d08f6db815..9e7b552df5 100644 --- a/src/ImmersedBoundaries/partial_cell_bottom.jl +++ b/src/ImmersedBoundaries/partial_cell_bottom.jl @@ -63,13 +63,42 @@ end function ImmersedBoundaryGrid(grid, ib::PartialCellBottom) bottom_field = Field{Center, Center, Nothing}(grid) set!(bottom_field, ib.bottom_height) - @apply_regionally clamp_bottom_height!(bottom_field, grid) + @apply_regionally compute_numerical_bottom_height!(bottom_field, grid, ib) fill_halo_regions!(bottom_field) new_ib = PartialCellBottom(bottom_field, ib.minimum_fractional_cell_height) TX, TY, TZ = topology(grid) return ImmersedBoundaryGrid{TX, TY, TZ}(grid, new_ib) end +@kernel function _compute_numerical_bottom_height!(bottom_field, grid, ib::PartialCellBottom) + i, j = @index(Global, NTuple) + + # Save analytical bottom height + zb = @inbounds bottom_field[i, j, 1] + + # Cap bottom height at Lz and at rnode(i, j, grid.Nz+1, grid, c, c, f) + domain_bottom = znode(i, j, 1, grid, c, c, f) + domain_top = znode(i, j, grid.Nz+1, grid, c, c, f) + @inbounds bottom_field[i, j, 1] = clamp(zb, domain_bottom, domain_top) + adjusted_zb = bottom_field[i, j, 1] + + ϵ = ib.minimum_fractional_cell_height + + for k in 1:grid.Nz + z⁻ = znode(i, j, k, grid, c, c, f) + z⁺ = znode(i, j, k+1, grid, c, c, f) + Δz = Δzᶜᶜᶜ(i, j, k, grid) + bottom_cell = (z⁻ ≤ zb) & (z⁺ ≥ zb) + capped_zb = min(z⁺ - ϵ * Δz, zb) + + # If the size of the bottom cell is less than ϵ Δz, + # we enforce a minimum size of ϵ Δz. + adjusted_zb = ifelse(bottom_cell, capped_zb, zb) + end + + @inbounds bottom_field[i, j, 1] = adjusted_zb +end + function on_architecture(arch, ib::PartialCellBottom{<:Field}) architecture(ib.bottom_height) == arch && return ib arch_grid = on_architecture(arch, ib.bottom_height.grid) @@ -106,13 +135,12 @@ Criterion is zb ≥ z - ϵ Δz """ @inline function _immersed_cell(i, j, k, underlying_grid, ib::PartialCellBottom) - # Face node below current cell - z = znode(i, j, k, underlying_grid, c, c, f) - zb = @inbounds ib.bottom_height[i, j, 1] + z⁺ = znode(i, j, k+1, underlying_grid, c, c, f) ϵ = ib.minimum_fractional_cell_height - # z + Δz is equal to the face above the current cell Δz = Δzᶜᶜᶜ(i, j, k, underlying_grid) - return (z + Δz * (1 - ϵ)) ≤ zb + z★ = z⁺ - Δz * ϵ + zb = @inbounds ib.bottom_height[i, j, 1] + return z★ < zb end @inline function bottom_cell(i, j, k, ibg::PCBIBG) @@ -129,15 +157,14 @@ end # Get node at face above and defining nodes on c,c,f z = znode(i, j, k+1, underlying_grid, c, c, f) - # Get bottom height and fractional Δz parameter - h = @inbounds ib.bottom_height[i, j, 1] - ϵ = ibg.immersed_boundary.minimum_fractional_cell_height + # Get bottom z-coordinate and fractional Δz parameter + zb = @inbounds ib.bottom_height[i, j, 1] # Are we in a bottom cell? at_the_bottom = bottom_cell(i, j, k, ibg) - full_Δz = Δzᶜᶜᶜ(i, j, k, ibg.underlying_grid) - partial_Δz = max(ϵ * full_Δz, z - h) + full_Δz = Δzᶜᶜᶜ(i, j, k, ibg.underlying_grid) + partial_Δz = z - zb return ifelse(at_the_bottom, partial_Δz, full_Δz) end @@ -175,6 +202,3 @@ YFlatPCBIBG = ImmersedBoundaryGrid{<:Any, <:Any, <:Flat, <:Any, <:Any, <:Partial @inline Δzᶜᶠᶠ(i, j, k, ibg::YFlatPCBIBG) = Δzᶜᶜᶠ(i, j, k, ibg) @inline Δzᶠᶠᶜ(i, j, k, ibg::XFlatPCBIBG) = Δzᶜᶠᶜ(i, j, k, ibg) @inline Δzᶠᶠᶜ(i, j, k, ibg::YFlatPCBIBG) = Δzᶠᶜᶜ(i, j, k, ibg) - -@inline z_bottom(i, j, ibg::PCBIBG) = @inbounds ibg.immersed_boundary.bottom_height[i, j, 1] - diff --git a/src/Models/HydrostaticFreeSurfaceModels/distributed_split_explicit_free_surface.jl b/src/Models/HydrostaticFreeSurfaceModels/distributed_split_explicit_free_surface.jl index 6f0afa5a26..d8b472fec8 100644 --- a/src/Models/HydrostaticFreeSurfaceModels/distributed_split_explicit_free_surface.jl +++ b/src/Models/HydrostaticFreeSurfaceModels/distributed_split_explicit_free_surface.jl @@ -10,27 +10,13 @@ function SplitExplicitAuxiliaryFields(grid::DistributedGrid) Gᵁ = Field((Face, Center, Nothing), grid) Gⱽ = Field((Center, Face, Nothing), grid) - Hᶠᶜ = Field((Face, Center, Nothing), grid) - Hᶜᶠ = Field((Center, Face, Nothing), grid) - - calculate_column_height!(Hᶠᶜ, (Face, Center, Center)) - calculate_column_height!(Hᶜᶠ, (Center, Face, Center)) - - fill_halo_regions!((Hᶠᶜ, Hᶜᶠ)) - # In a non-parallel grid we calculate only the interior kernel_size = augmented_kernel_size(grid) kernel_offsets = augmented_kernel_offsets(grid) kernel_parameters = KernelParameters(kernel_size, kernel_offsets) - return SplitExplicitAuxiliaryFields(Gᵁ, Gⱽ, Hᶠᶜ, Hᶜᶠ, kernel_parameters) -end - -"""Integrate z at locations `location` and set! `height`` with the result""" -@inline function calculate_column_height!(height, location) - dz = GridMetricOperation(location, Δz, height.grid) - return sum!(height, dz) + return SplitExplicitAuxiliaryFields(Gᵁ, Gⱽ, kernel_parameters) end @inline function augmented_kernel_size(grid::DistributedGrid) diff --git a/src/Models/HydrostaticFreeSurfaceModels/split_explicit_free_surface.jl b/src/Models/HydrostaticFreeSurfaceModels/split_explicit_free_surface.jl index 5d9a00f2ed..073fbfc4ea 100644 --- a/src/Models/HydrostaticFreeSurfaceModels/split_explicit_free_surface.jl +++ b/src/Models/HydrostaticFreeSurfaceModels/split_explicit_free_surface.jl @@ -209,10 +209,6 @@ Base.@kwdef struct SplitExplicitAuxiliaryFields{𝒞ℱ, ℱ𝒞, 𝒦} Gᵁ :: ℱ𝒞 "Vertically-integrated slow barotropic forcing function for `V` (`ReducedField` over ``z``)" Gⱽ :: 𝒞ℱ - "Depth at `(Face, Center)` (`ReducedField` over ``z``)" - Hᶠᶜ :: ℱ𝒞 - "Depth at `(Center, Face)` (`ReducedField` over ``z``)" - Hᶜᶠ :: 𝒞ℱ "kernel size for barotropic time stepping" kernel_parameters :: 𝒦 end @@ -227,20 +223,9 @@ function SplitExplicitAuxiliaryFields(grid::AbstractGrid) Gᵁ = Field((Face, Center, Nothing), grid) Gⱽ = Field((Center, Face, Nothing), grid) - Hᶠᶜ = Field((Face, Center, Nothing), grid) - Hᶜᶠ = Field((Center, Face, Nothing), grid) - - dz = GridMetricOperation((Face, Center, Center), Δz, grid) - sum!(Hᶠᶜ, dz) - - dz = GridMetricOperation((Center, Face, Center), Δz, grid) - sum!(Hᶜᶠ, dz) - - fill_halo_regions!((Hᶠᶜ, Hᶜᶠ)) - kernel_parameters = :xy - return SplitExplicitAuxiliaryFields(Gᵁ, Gⱽ, Hᶠᶜ, Hᶜᶠ, kernel_parameters) + return SplitExplicitAuxiliaryFields(Gᵁ, Gⱽ, kernel_parameters) end """ diff --git a/src/Models/HydrostaticFreeSurfaceModels/split_explicit_free_surface_kernels.jl b/src/Models/HydrostaticFreeSurfaceModels/split_explicit_free_surface_kernels.jl index 98413d5eb8..0042755123 100644 --- a/src/Models/HydrostaticFreeSurfaceModels/split_explicit_free_surface_kernels.jl +++ b/src/Models/HydrostaticFreeSurfaceModels/split_explicit_free_surface_kernels.jl @@ -1,3 +1,4 @@ +using Oceananigans.Grids using Oceananigans.Grids: topology using Oceananigans.Utils using Oceananigans.AbstractOperations: Δz @@ -82,7 +83,6 @@ end @inline function free_surface_evolution!(i, j, grid, Δτ, η, ηᵐ, ηᵐ⁻¹, ηᵐ⁻², U, V, Uᵐ⁻¹, Uᵐ⁻², Vᵐ⁻¹, Vᵐ⁻², timestepper) k_top = grid.Nz+1 - TX, TY, _ = topology(grid) @inbounds begin advance_previous_free_surface!(i, j, k_top, timestepper, η, ηᵐ, ηᵐ⁻¹, ηᵐ⁻²) @@ -96,20 +96,20 @@ end @kernel function _split_explicit_barotropic_velocity!(averaging_weight, grid, Δτ, η, ηᵐ, ηᵐ⁻¹, ηᵐ⁻², U, Uᵐ⁻¹, Uᵐ⁻², V, Vᵐ⁻¹, Vᵐ⁻², - η̅, U̅, V̅, Gᵁ, Gⱽ, Hᶠᶜ, Hᶜᶠ, g, + η̅, U̅, V̅, Gᵁ, Gⱽ, g, timestepper) i, j = @index(Global, NTuple) velocity_evolution!(i, j, grid, Δτ, η, ηᵐ, ηᵐ⁻¹, ηᵐ⁻², U, Uᵐ⁻¹, Uᵐ⁻², V, Vᵐ⁻¹, Vᵐ⁻², η̅, U̅, V̅, averaging_weight, - Gᵁ, Gⱽ, Hᶠᶜ, Hᶜᶠ, g, + Gᵁ, Gⱽ, g, timestepper) end @inline function velocity_evolution!(i, j, grid, Δτ, η, ηᵐ, ηᵐ⁻¹, ηᵐ⁻², U, Uᵐ⁻¹, Uᵐ⁻², V, Vᵐ⁻¹, Vᵐ⁻², η̅, U̅, V̅, averaging_weight, - Gᵁ, Gⱽ, Hᶠᶜ, Hᶜᶠ, g, + Gᵁ, Gⱽ, g, timestepper) k_top = grid.Nz+1 @@ -117,10 +117,13 @@ end advance_previous_velocity!(i, j, k_top-1, timestepper, U, Uᵐ⁻¹, Uᵐ⁻²) advance_previous_velocity!(i, j, k_top-1, timestepper, V, Vᵐ⁻¹, Vᵐ⁻²) + Hᶠᶜ = static_column_depthᶠᶜᵃ(i, j, grid) + Hᶜᶠ = static_column_depthᶜᶠᵃ(i, j, grid) + # ∂τ(U) = - ∇η + G - U[i, j, k_top-1] += Δτ * (- g * Hᶠᶜ[i, j] * ∂xTᶠᶜᶠ(i, j, k_top, grid, η★, timestepper, η, ηᵐ, ηᵐ⁻¹, ηᵐ⁻²) + Gᵁ[i, j, 1]) - V[i, j, k_top-1] += Δτ * (- g * Hᶜᶠ[i, j] * ∂yTᶜᶠᶠ(i, j, k_top, grid, η★, timestepper, η, ηᵐ, ηᵐ⁻¹, ηᵐ⁻²) + Gⱽ[i, j, 1]) - + U[i, j, k_top-1] += Δτ * (- g * Hᶠᶜ * ∂xTᶠᶜᶠ(i, j, k_top, grid, η★, timestepper, η, ηᵐ, ηᵐ⁻¹, ηᵐ⁻²) + Gᵁ[i, j, k_top-1]) + V[i, j, k_top-1] += Δτ * (- g * Hᶜᶠ * ∂yTᶜᶠᶠ(i, j, k_top, grid, η★, timestepper, η, ηᵐ, ηᵐ⁻¹, ηᵐ⁻²) + Gⱽ[i, j, k_top-1]) + # time-averaging η̅[i, j, k_top] += averaging_weight * η[i, j, k_top] U̅[i, j, k_top-1] += averaging_weight * U[i, j, k_top-1] @@ -197,29 +200,30 @@ function initialize_auxiliary_state!(state, η, timestepper) return nothing end -@kernel function _barotropic_split_explicit_corrector!(u, v, U̅, V̅, U, V, Hᶠᶜ, Hᶜᶠ, grid) +@kernel function _barotropic_split_explicit_corrector!(u, v, U̅, V̅, U, V, grid) i, j, k = @index(Global, NTuple) k_top = grid.Nz+1 @inbounds begin - u[i, j, k] = u[i, j, k] + (U̅[i, j, k_top-1] - U[i, j, k_top-1]) / Hᶠᶜ[i, j, 1] - v[i, j, k] = v[i, j, k] + (V̅[i, j, k_top-1] - V[i, j, k_top-1]) / Hᶜᶠ[i, j, 1] + Hᶠᶜ = static_column_depthᶠᶜᵃ(i, j, grid) + Hᶜᶠ = static_column_depthᶜᶠᵃ(i, j, grid) + + u[i, j, k] = u[i, j, k] + (U̅[i, j, k_top-1] - U[i, j, k_top-1]) / Hᶠᶜ + v[i, j, k] = v[i, j, k] + (V̅[i, j, k_top-1] - V[i, j, k_top-1]) / Hᶜᶠ end end function barotropic_split_explicit_corrector!(u, v, free_surface, grid) sefs = free_surface.state U, V, U̅, V̅ = sefs.U, sefs.V, sefs.U̅, sefs.V̅ - Hᶠᶜ, Hᶜᶠ = free_surface.auxiliary.Hᶠᶜ, free_surface.auxiliary.Hᶜᶠ arch = architecture(grid) - - # take out "bad" barotropic mode, + # take out "bad" barotropic mode, # !!!! reusing U and V for this storage since last timestep doesn't matter compute_barotropic_mode!(U, V, grid, u, v) # add in "good" barotropic mode launch!(arch, grid, :xyz, _barotropic_split_explicit_corrector!, - u, v, U̅, V̅, U, V, Hᶠᶜ, Hᶜᶠ, grid) + u, v, U̅, V̅, U, V, grid) return nothing end @@ -311,7 +315,7 @@ function iterate_split_explicit!(free_surface, grid, Δτᴮ, weights, ::Val{Nsu Vᵐ⁻¹, Vᵐ⁻² = state.Vᵐ⁻¹, state.Vᵐ⁻² ηᵐ, ηᵐ⁻¹, ηᵐ⁻² = state.ηᵐ, state.ηᵐ⁻¹, state.ηᵐ⁻² η̅, U̅, V̅ = state.η̅, state.U̅, state.V̅ - Gᵁ, Gⱽ, Hᶠᶜ, Hᶜᶠ = auxiliary.Gᵁ, auxiliary.Gⱽ, auxiliary.Hᶠᶜ, auxiliary.Hᶜᶠ + Gᵁ, Gⱽ = auxiliary.Gᵁ, auxiliary.Gⱽ timestepper = settings.timestepper @@ -326,7 +330,7 @@ function iterate_split_explicit!(free_surface, grid, Δτᴮ, weights, ::Val{Nsu U_args = (grid, Δτᴮ, η, ηᵐ, ηᵐ⁻¹, ηᵐ⁻², U, Uᵐ⁻¹, Uᵐ⁻², V, Vᵐ⁻¹, Vᵐ⁻², - η̅, U̅, V̅, Gᵁ, Gⱽ, Hᶠᶜ, Hᶜᶠ, g, + η̅, U̅, V̅, Gᵁ, Gⱽ, g, timestepper) GC.@preserve η_args U_args begin diff --git a/src/MultiRegion/multi_region_field.jl b/src/MultiRegion/multi_region_field.jl index cb6d06cda8..6abbee7ce2 100644 --- a/src/MultiRegion/multi_region_field.jl +++ b/src/MultiRegion/multi_region_field.jl @@ -70,6 +70,8 @@ Base.size(f::GriddedMultiRegionField) = size(getregion(f.grid, 1)) Reconstruct a global field from `mrf::MultiRegionField` on the `CPU`. """ function reconstruct_global_field(mrf::MultiRegionField) + + # TODO: Is this correct? Shall we reconstruct a global field on the architecture of the grid? global_grid = on_architecture(CPU(), reconstruct_global_grid(mrf.grid)) indices = reconstruct_global_indices(mrf.indices, mrf.grid.partition, size(global_grid)) global_field = Field(location(mrf), global_grid; indices) diff --git a/src/MultiRegion/multi_region_grid.jl b/src/MultiRegion/multi_region_grid.jl index 0c67d75a49..0702b20d43 100644 --- a/src/MultiRegion/multi_region_grid.jl +++ b/src/MultiRegion/multi_region_grid.jl @@ -183,14 +183,15 @@ end function reconstruct_global_grid(mrg::ImmersedMultiRegionGrid) global_grid = reconstruct_global_grid(mrg.underlying_grid) - global_boundary = reconstruct_global_boundary(mrg.immersed_boundary) + global_immersed_boundary = reconstruct_global_immersed_boundary(mrg.immersed_boundary) + global_immersed_boundary = on_architecture(architecture(mrg), global_immersed_boundary) - return ImmersedBoundaryGrid(global_grid, global_boundary) + return ImmersedBoundaryGrid(global_grid, global_immersed_boundary) end -reconstruct_global_boundary(g::GridFittedBottom{<:Field}) = GridFittedBottom(reconstruct_global_field(g.bottom_height), g.immersed_condition) -reconstruct_global_boundary(g::PartialCellBottom{<:Field}) = PartialCellBottom(reconstruct_global_field(g.bottom_height), g.minimum_fractional_cell_height) -reconstruct_global_boundary(g::GridFittedBoundary{<:Field}) = GridFittedBoundary(reconstruct_global_field(g.mask)) +reconstruct_global_immersed_boundary(g::GridFittedBottom{<:Field}) = GridFittedBottom(reconstruct_global_field(g.bottom_height), g.immersed_condition) +reconstruct_global_immersed_boundary(g::PartialCellBottom{<:Field}) = PartialCellBottom(reconstruct_global_field(g.bottom_height), g.minimum_fractional_cell_height) +reconstruct_global_immersed_boundary(g::GridFittedBoundary{<:Field}) = GridFittedBoundary(reconstruct_global_field(g.mask)) @inline getregion(mrg::ImmersedMultiRegionGrid{FT, TX, TY, TZ}, r) where {FT, TX, TY, TZ} = ImmersedBoundaryGrid{TX, TY, TZ}(_getregion(mrg.underlying_grid, r), _getregion(mrg.immersed_boundary, r)) @inline _getregion(mrg::ImmersedMultiRegionGrid{FT, TX, TY, TZ}, r) where {FT, TX, TY, TZ} = ImmersedBoundaryGrid{TX, TY, TZ}( getregion(mrg.underlying_grid, r), getregion(mrg.immersed_boundary, r)) diff --git a/src/MultiRegion/multi_region_split_explicit_free_surface.jl b/src/MultiRegion/multi_region_split_explicit_free_surface.jl index fb36463b39..10ba130b33 100644 --- a/src/MultiRegion/multi_region_split_explicit_free_surface.jl +++ b/src/MultiRegion/multi_region_split_explicit_free_surface.jl @@ -13,27 +13,13 @@ function SplitExplicitAuxiliaryFields(grid::MultiRegionGrids) Gᵁ = Field((Face, Center, Nothing), grid) Gⱽ = Field((Center, Face, Nothing), grid) - Hᶠᶜ = Field((Face, Center, Nothing), grid) - Hᶜᶠ = Field((Center, Face, Nothing), grid) - - @apply_regionally calculate_column_height!(Hᶠᶜ, (Face, Center, Center)) - @apply_regionally calculate_column_height!(Hᶜᶠ, (Center, Face, Center)) - - fill_halo_regions!((Hᶠᶜ, Hᶜᶠ)) - # In a non-parallel grid we calculate only the interior @apply_regionally kernel_size = augmented_kernel_size(grid, grid.partition) @apply_regionally kernel_offsets = augmented_kernel_offsets(grid, grid.partition) @apply_regionally kernel_parameters = KernelParameters(kernel_size, kernel_offsets) - return SplitExplicitAuxiliaryFields(Gᵁ, Gⱽ, Hᶠᶜ, Hᶜᶠ, kernel_parameters) -end - -@inline function calculate_column_height!(height, location) - dz = GridMetricOperation(location, Δz, height.grid) - sum!(height, dz) - return nothing + return SplitExplicitAuxiliaryFields(Gᵁ, Gⱽ, kernel_parameters) end @inline augmented_kernel_size(grid, ::XPartition) = (size(grid, 1) + 2halo_size(grid)[1]-2, size(grid, 2)) diff --git a/src/TurbulenceClosures/TurbulenceClosures.jl b/src/TurbulenceClosures/TurbulenceClosures.jl index 5b0aabcac3..6effcffc72 100644 --- a/src/TurbulenceClosures/TurbulenceClosures.jl +++ b/src/TurbulenceClosures/TurbulenceClosures.jl @@ -50,7 +50,8 @@ using Oceananigans.Utils using Oceananigans.Architectures: AbstractArchitecture, device using Oceananigans.Fields: FunctionField -using Oceananigans.ImmersedBoundaries: z_bottom +using Oceananigans.ImmersedBoundaries +using Oceananigans.ImmersedBoundaries: AbstractGridFittedBottom import Oceananigans.Grids: required_halo_size_x, required_halo_size_y, required_halo_size_z import Oceananigans.Architectures: on_architecture @@ -120,14 +121,21 @@ end @inline clip(x) = max(zero(x), x) +##### +##### Height, Depth and Bottom interfaces +##### + const c = Center() const f = Face() +const AGFBIBG = ImmersedBoundaryGrid{<:Any, <:Any, <:Any, <:Any, <:Any, <:AbstractGridFittedBottom} + @inline z_top(i, j, grid) = znode(i, j, grid.Nz+1, grid, c, c, f) +@inline z_bottom(i, j, grid) = znode(i, j, 1, grid, c, c, f) +@inline z_bottom(i, j, ibg::AGFBIBG) = @inbounds ibg.immersed_boundary.bottom_height[i, j, 1] -@inline depthᶜᶜᶠ(i, j, k, grid) = clip(z_top(i, j, grid) - znode(i, j, k, grid, c, c, f)) -@inline depthᶜᶜᶜ(i, j, k, grid) = clip(z_top(i, j, grid) - znode(i, j, k, grid, c, c, c)) -@inline total_depthᶜᶜᵃ(i, j, grid) = clip(z_top(i, j, grid) - z_bottom(i, j, grid)) +@inline depthᶜᶜᶠ(i, j, k, grid) = clip(z_top(i, j, grid) - znode(i, j, k, grid, c, c, f)) +@inline depthᶜᶜᶜ(i, j, k, grid) = clip(z_top(i, j, grid) - znode(i, j, k, grid, c, c, c)) @inline function height_above_bottomᶜᶜᶠ(i, j, k, grid) h = znode(i, j, k, grid, c, c, f) - z_bottom(i, j, grid) diff --git a/src/TurbulenceClosures/turbulence_closure_implementations/TKEBasedVerticalDiffusivities/catke_equation.jl b/src/TurbulenceClosures/turbulence_closure_implementations/TKEBasedVerticalDiffusivities/catke_equation.jl index a08fb4b969..bf2aaa7bfd 100644 --- a/src/TurbulenceClosures/turbulence_closure_implementations/TKEBasedVerticalDiffusivities/catke_equation.jl +++ b/src/TurbulenceClosures/turbulence_closure_implementations/TKEBasedVerticalDiffusivities/catke_equation.jl @@ -58,7 +58,7 @@ end ℓ★ = ifelse(isnan(ℓ★), zero(grid), ℓ★) ℓᴰ = max(ℓ★, ℓʰ) - H = total_depthᶜᶜᵃ(i, j, grid) + H = static_column_depthᶜᶜᵃ(i, j, grid) return min(H, ℓᴰ) end diff --git a/src/TurbulenceClosures/turbulence_closure_implementations/TKEBasedVerticalDiffusivities/catke_mixing_length.jl b/src/TurbulenceClosures/turbulence_closure_implementations/TKEBasedVerticalDiffusivities/catke_mixing_length.jl index 347490270a..dce68696b6 100644 --- a/src/TurbulenceClosures/turbulence_closure_implementations/TKEBasedVerticalDiffusivities/catke_mixing_length.jl +++ b/src/TurbulenceClosures/turbulence_closure_implementations/TKEBasedVerticalDiffusivities/catke_mixing_length.jl @@ -5,7 +5,7 @@ using ..TurbulenceClosures: height_above_bottomᶜᶜᶠ, depthᶜᶜᶜ, height_above_bottomᶜᶜᶜ, - total_depthᶜᶜᵃ + static_column_depthᶜᶜᵃ """ struct CATKEMixingLength{FT} @@ -232,7 +232,7 @@ end ℓ★ = ifelse(isnan(ℓ★), zero(grid), ℓ★) ℓu = max(ℓ★, ℓʰ) - H = total_depthᶜᶜᵃ(i, j, grid) + H = static_column_depthᶜᶜᵃ(i, j, grid) return min(H, ℓu) end @@ -252,7 +252,7 @@ end ℓ★ = ifelse(isnan(ℓ★), zero(grid), ℓ★) ℓc = max(ℓ★, ℓʰ) - H = total_depthᶜᶜᵃ(i, j, grid) + H = static_column_depthᶜᶜᵃ(i, j, grid) return min(H, ℓc) end @@ -272,7 +272,7 @@ end ℓ★ = ifelse(isnan(ℓ★), zero(grid), ℓ★) ℓe = max(ℓ★, ℓʰ) - H = total_depthᶜᶜᵃ(i, j, grid) + H = static_column_depthᶜᶜᵃ(i, j, grid) return min(H, ℓe) end diff --git a/validation/vertical_mixing_closures/heterogeneous_windy_convection.jl b/validation/vertical_mixing_closures/heterogeneous_windy_convection.jl index ac5efa7276..267be16537 100644 --- a/validation/vertical_mixing_closures/heterogeneous_windy_convection.jl +++ b/validation/vertical_mixing_closures/heterogeneous_windy_convection.jl @@ -34,8 +34,8 @@ grid = RectilinearGrid(size = (Nx, Ny, Nz), z = z, topology=(Periodic, Bounded, Bounded)) -z_bottom(x, y) = - Lz * (1 - (2y / Ly)^2) -grid = ImmersedBoundaryGrid(grid, PartialCellBottom(z_bottom, minimum_fractional_cell_height=0.1)) +bottom_height(x, y) = - Lz * (1 - (2y / Ly)^2) +grid = ImmersedBoundaryGrid(grid, PartialCellBottom(bottom_height, minimum_fractional_cell_height=0.1)) @show grid @inline Jᵇ(x, y, t) = 1e-7