Skip to content

Commit 4265add

Browse files
Materialize buoyancy gradients for use in parameterizations (#4837)
* cmopute * fix tests * overwrite * Remove @const from kernel function parameters * Update src/Models/NonhydrostaticModels/nonhydrostatic_model.jl Co-authored-by: Gregory L. Wagner <[email protected]> * Update src/BuoyancyFormulations/buoyancy_force.jl Co-authored-by: Gregory L. Wagner <[email protected]> * Update src/Models/HydrostaticFreeSurfaceModels/hydrostatic_free_surface_model.jl Co-authored-by: Gregory L. Wagner <[email protected]> * regularize -> materialize * regularize -> materialize again * more precompute -> materialize * bugfixes * one fix * bugfix * another bugfix * fix multiregion * do not do this * make sure the correct parameters are passed * fix * Update multi_region_models.jl * fix all bugs --------- Co-authored-by: Gregory L. Wagner <[email protected]>
1 parent 83dd948 commit 4265add

File tree

8 files changed

+116
-33
lines changed

8 files changed

+116
-33
lines changed

src/BuoyancyFormulations/buoyancy_force.jl

Lines changed: 69 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,25 @@
1-
using Oceananigans.Grids: NegativeZDirection, validate_unit_vector
1+
using Oceananigans.Utils
2+
using Oceananigans.Fields
3+
using Oceananigans.Grids: NegativeZDirection, validate_unit_vector, architecture
4+
using Oceananigans.BoundaryConditions
5+
6+
using KernelAbstractions: @kernel, @index
27
using Adapt
38

4-
struct BuoyancyForce{M, G}
9+
struct BuoyancyForce{M, G, B}
510
formulation :: M
611
gravity_unit_vector :: G
12+
gradients :: B
713
end
814

9-
Adapt.adapt_structure(to, bf::BuoyancyForce) =
10-
BuoyancyForce(adapt(to, bf.formulation),
11-
adapt(to, bf.gravity_unit_vector))
12-
1315
"""
14-
BuoyancyForce(formulation; gravity_unit_vector=NegativeZDirection())
16+
BuoyancyForce(grid, formulation::AbstractBuoyancyFormulation; gravity_unit_vector=NegativeZDirection(), materialize_gradients=true)
1517
16-
Construct a `buoyancy` given a buoyancy `model`. Optional keyword argument `gravity_unit_vector`
18+
Construct a `buoyancy` given a buoyancy `formulation`. Optional keyword argument `gravity_unit_vector`
1719
can be used to specify the direction of gravity (default `NegativeZDirection()`).
1820
The buoyancy acceleration acts in the direction opposite to gravity.
21+
If `materialize_gradients` is true (default), the buoyancy gradients will be precomputed and stored in fields for
22+
performance reasons. For `materialize_gradients=true`, the `grid` argument must be provided.
1923
2024
Example
2125
=======
@@ -44,11 +48,31 @@ NonhydrostaticModel{CPU, RectilinearGrid}(time = 0 seconds, iteration = 0)
4448
└── coriolis: Nothing
4549
```
4650
"""
47-
function BuoyancyForce(formulation; gravity_unit_vector=NegativeZDirection())
51+
function BuoyancyForce(grid, formulation::AbstractBuoyancyFormulation; gravity_unit_vector=NegativeZDirection(), materialize_gradients=true)
4852
gravity_unit_vector = validate_unit_vector(gravity_unit_vector)
49-
return BuoyancyForce(formulation, gravity_unit_vector)
53+
54+
if materialize_gradients
55+
∂x_b = XFaceField(grid)
56+
∂y_b = YFaceField(grid)
57+
∂z_b = ZFaceField(grid)
58+
59+
gradients = (; ∂x_b, ∂y_b, ∂z_b)
60+
else
61+
gradients = nothing
62+
end
63+
64+
return BuoyancyForce(formulation, gravity_unit_vector, gradients)
5065
end
5166

67+
# Fallback for when no grid is available, we overwrite `materialize_gradients` to false
68+
BuoyancyForce(formulation::AbstractBuoyancyFormulation; materialize_gradients=false, kwargs...) =
69+
BuoyancyForce(nothing, formulation; materialize_gradients=false, kwargs...)
70+
71+
Adapt.adapt_structure(to, bf::BuoyancyForce) =
72+
BuoyancyForce(Adapt.adapt(to, bf.formulation),
73+
Adapt.adapt(to, bf.gravity_unit_vector),
74+
Adapt.adapt(to, bf.gradients))
75+
5276
@inline ĝ_x(bf) = @inbounds - bf.gravity_unit_vector[1]
5377
@inline ĝ_y(bf) = @inbounds - bf.gravity_unit_vector[2]
5478
@inline ĝ_z(bf) = @inbounds - bf.gravity_unit_vector[3]
@@ -65,14 +89,18 @@ end
6589

6690
@inline get_temperature_and_salinity(bf::BuoyancyForce, C) = get_temperature_and_salinity(bf.formulation, C)
6791

68-
@inline ∂x_b(i, j, k, grid, b::BuoyancyForce, C) = ∂x_b(i, j, k, grid, b.formulation, C)
69-
@inline ∂y_b(i, j, k, grid, b::BuoyancyForce, C) = ∂y_b(i, j, k, grid, b.formulation, C)
70-
@inline ∂z_b(i, j, k, grid, b::BuoyancyForce, C) = ∂z_b(i, j, k, grid, b.formulation, C)
92+
@inline ∂x_b(i, j, k, grid, b::BuoyancyForce{<:Any, <:Any, Nothing}, C) = ∂x_b(i, j, k, grid, b.formulation, C)
93+
@inline ∂y_b(i, j, k, grid, b::BuoyancyForce{<:Any, <:Any, Nothing}, C) = ∂y_b(i, j, k, grid, b.formulation, C)
94+
@inline ∂z_b(i, j, k, grid, b::BuoyancyForce{<:Any, <:Any, Nothing}, C) = ∂z_b(i, j, k, grid, b.formulation, C)
7195

7296
@inline top_buoyancy_flux(i, j, grid, b::BuoyancyForce, args...) = top_buoyancy_flux(i, j, grid, b.formulation, args...)
7397

74-
regularize_buoyancy(bf) = bf
75-
regularize_buoyancy(formulation::AbstractBuoyancyFormulation) = BuoyancyForce(formulation)
98+
materialize_buoyancy(bf, grid; kw...) = bf
99+
materialize_buoyancy(formulation::AbstractBuoyancyFormulation, grid; kw...) = BuoyancyForce(grid, formulation; kw...)
100+
101+
# Fallback
102+
compute_buoyancy_gradients!(::BuoyancyForce{<:Any, <:Any, <:Nothing}, grid, tracers; kw...) = nothing
103+
compute_buoyancy_gradients!(::Nothing, grid, tracers; kw...) = nothing
76104

77105
Base.summary(bf::BuoyancyForce) = string(summary(bf.formulation),
78106
" with ĝ = ",
@@ -89,3 +117,29 @@ function Base.show(io::IO, bf::BuoyancyForce)
89117
"├── formulation: ", prettysummary(bf.formulation), '\n',
90118
"└── gravity_unit_vector: ", summarize_vector(bf.gravity_unit_vector))
91119
end
120+
121+
#####
122+
##### Some performance optimizations for models that compute gradients over and over...
123+
#####
124+
125+
@inline ∂x_b(i, j, k, grid, b::BuoyancyForce, C) = @inbounds b.gradients.∂x_b[i, j, k]
126+
@inline ∂y_b(i, j, k, grid, b::BuoyancyForce, C) = @inbounds b.gradients.∂y_b[i, j, k]
127+
@inline ∂z_b(i, j, k, grid, b::BuoyancyForce, C) = @inbounds b.gradients.∂z_b[i, j, k]
128+
129+
function compute_buoyancy_gradients!(buoyancy, grid, tracers; parameters=:xyz)
130+
gradients = buoyancy.gradients
131+
formulation = buoyancy.formulation
132+
launch!(architecture(grid), grid, parameters, _compute_buoyancy_gradients!, gradients, grid, formulation, tracers)
133+
fill_halo_regions!(gradients, only_local_halos=true)
134+
135+
return nothing
136+
end
137+
138+
@kernel function _compute_buoyancy_gradients!(g, grid, b, C)
139+
i, j, k = @index(Global, NTuple)
140+
@inbounds begin
141+
g.∂x_b[i, j, k] = ∂x_b(i, j, k, grid, b, C)
142+
g.∂y_b[i, j, k] = ∂y_b(i, j, k, grid, b, C)
143+
g.∂z_b[i, j, k] = ∂z_b(i, j, k, grid, b, C)
144+
end
145+
end

src/BuoyancyFormulations/nonlinear_equation_of_state.jl

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
using Oceananigans.Fields: AbstractField
2-
using Oceananigans.Grids: znode
2+
using Oceananigans.Grids: znode, ZFlatGrid
33
using Oceananigans.Operators: Δzᶜᶜᶠ, Δzᶜᶜᶜ
44

55
const c = Center()
@@ -16,6 +16,11 @@ const f = Face()
1616
ifelse(k < 1, znode(i, j, 1, grid, c, c, f) + (1 - k) * Δzᶜᶜᶜ(i, j, 1, grid),
1717
ifelse(k > grid.Nz + 1, znode(i, j, grid.Nz + 1, grid, c, c, f) + (k - grid.Nz - 1) * Δzᶜᶜᶜ(i, j, grid.Nz, grid),
1818
znode(i, j, k, grid, c, c, f)))
19+
20+
# Fallbacks for ZFlatGrids (assumed to be at z = 0 for buoyancy purposes)
21+
@inline Zᶜᶜᶜ(i, j, k, grid::ZFlatGrid) = zero(grid)
22+
@inline Zᶜᶜᶠ(i, j, k, grid::ZFlatGrid) = zero(grid)
23+
1924
# Dispatch shenanigans
2025
@inline θ_and_sᴬ(i, j, k, θ::AbstractArray, sᴬ::AbstractArray) = @inbounds θ[i, j, k], sᴬ[i, j, k]
2126
@inline θ_and_sᴬ(i, j, k, θ::Number, sᴬ::AbstractArray) = @inbounds θ, sᴬ[i, j, k]

src/Models/HydrostaticFreeSurfaceModels/hydrostatic_free_surface_model.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ using OrderedCollections: OrderedDict
33
using Oceananigans.DistributedComputations
44
using Oceananigans.Architectures: AbstractArchitecture
55
using Oceananigans.Advection: AbstractAdvectionScheme, Centered, VectorInvariant, adapt_advection_order
6-
using Oceananigans.BuoyancyFormulations: validate_buoyancy, regularize_buoyancy, SeawaterBuoyancy
6+
using Oceananigans.BuoyancyFormulations: validate_buoyancy, materialize_buoyancy, SeawaterBuoyancy
77
using Oceananigans.BoundaryConditions: regularize_field_boundary_conditions
88
using Oceananigans.Biogeochemistry: validate_biogeochemistry, AbstractBiogeochemistry, biogeochemical_auxiliary_fields
99
using Oceananigans.Fields: Field, CenterField, tracernames, VelocityFields, TracerFields
@@ -163,7 +163,7 @@ function HydrostaticFreeSurfaceModel(; grid,
163163
advection = NamedTuple(name => adapt_advection_order(scheme, grid) for (name, scheme) in pairs(advection))
164164

165165
validate_buoyancy(buoyancy, tracernames(tracers))
166-
buoyancy = regularize_buoyancy(buoyancy)
166+
buoyancy = materialize_buoyancy(buoyancy, grid)
167167

168168
# Collect boundary conditions for all model prognostic fields and, if specified, some model
169169
# auxiliary fields. Boundary conditions are "regularized" based on the _name_ of the field:

src/Models/HydrostaticFreeSurfaceModels/update_hydrostatic_free_surface_model_state.jl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ using Oceananigans.BoundaryConditions
44
using Oceananigans: UpdateStateCallsite
55
using Oceananigans.Biogeochemistry: update_biogeochemical_state!
66
using Oceananigans.BoundaryConditions: update_boundary_conditions!
7+
using Oceananigans.BuoyancyFormulations: compute_buoyancy_gradients!
78
using Oceananigans.TurbulenceClosures: compute_diffusivities!
89
using Oceananigans.ImmersedBoundaries: mask_immersed_field!, mask_immersed_field_xy!, inactive_node
910
using Oceananigans.Models: update_model_field_time_series!
@@ -83,6 +84,9 @@ function compute_auxiliaries!(model::HydrostaticFreeSurfaceModel; w_parameters =
8384
P = model.pressure.pHY′
8485
arch = architecture(grid)
8586

87+
# Maybe compute buoyancy gradients
88+
compute_buoyancy_gradients!(buoyancy, grid, tracers; parameters = κ_parameters)
89+
8690
# Update the vertical velocity to comply with the barotropic correction step
8791
update_grid_vertical_velocity!(model, grid, model.vertical_coordinate)
8892

src/Models/NonhydrostaticModels/nonhydrostatic_model.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ using OrderedCollections: OrderedDict
33
using Oceananigans.Architectures: AbstractArchitecture
44
using Oceananigans.DistributedComputations: Distributed
55
using Oceananigans.Advection: Centered, adapt_advection_order
6-
using Oceananigans.BuoyancyFormulations: validate_buoyancy, regularize_buoyancy, SeawaterBuoyancy
6+
using Oceananigans.BuoyancyFormulations: validate_buoyancy, materialize_buoyancy, SeawaterBuoyancy
77
using Oceananigans.BoundaryConditions: MixedBoundaryCondition
88
using Oceananigans.Biogeochemistry: validate_biogeochemistry, AbstractBiogeochemistry, biogeochemical_auxiliary_fields
99
using Oceananigans.BoundaryConditions: regularize_field_boundary_conditions
@@ -188,7 +188,7 @@ function NonhydrostaticModel(; grid,
188188
all_auxiliary_fields = merge(auxiliary_fields, biogeochemical_auxiliary_fields(biogeochemistry))
189189
tracers, auxiliary_fields = validate_biogeochemistry(tracers, all_auxiliary_fields, biogeochemistry, grid, clock)
190190
validate_buoyancy(buoyancy, tracernames(tracers))
191-
buoyancy = regularize_buoyancy(buoyancy)
191+
buoyancy = materialize_buoyancy(buoyancy, grid)
192192

193193
# Adjust advection scheme to be valid on a particular grid size. i.e. if the grid size
194194
# is smaller than the advection order, reduce the order of the advection in that particular

src/Models/NonhydrostaticModels/update_nonhydrostatic_model_state.jl

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ using Oceananigans.Architectures
33
using Oceananigans.BoundaryConditions
44
using Oceananigans.Biogeochemistry: update_biogeochemical_state!
55
using Oceananigans.BoundaryConditions: update_boundary_conditions!
6+
using Oceananigans.BuoyancyFormulations: compute_buoyancy_gradients!
67
using Oceananigans.TurbulenceClosures: compute_diffusivities!
78
using Oceananigans.Fields: compute!
89
using Oceananigans.ImmersedBoundaries: mask_immersed_field!
@@ -55,15 +56,23 @@ function update_state!(model::NonhydrostaticModel, callbacks=[]; compute_tendenc
5556
return nothing
5657
end
5758

58-
function compute_auxiliaries!(model::NonhydrostaticModel; p_parameters = tuple(p_kernel_parameters(model.grid)),
59-
κ_parameters = tuple(:xyz))
59+
function compute_auxiliaries!(model::NonhydrostaticModel; p_parameters = p_kernel_parameters(model.grid),
60+
κ_parameters = :xyz)
6061

62+
grid = model.grid
6163
closure = model.closure
6264
diffusivity = model.diffusivity_fields
65+
tracers = model.tracers
66+
buoyancy = model.buoyancy
67+
68+
# Maybe compute buoyancy gradients
69+
compute_buoyancy_gradients!(buoyancy, grid, tracers; parameters = κ_parameters)
70+
71+
# Compute diffusivities
72+
compute_diffusivities!(diffusivity, closure, model; parameters = κ_parameters)
73+
74+
# Update hydrostatic pressure
75+
update_hydrostatic_pressure!(model; parameters = p_parameters)
6376

64-
for (ppar, κpar) in zip(p_parameters, κ_parameters)
65-
compute_diffusivities!(diffusivity, closure, model; parameters = κpar)
66-
update_hydrostatic_pressure!(model; parameters = ppar)
67-
end
6877
return nothing
6978
end

src/MultiRegion/multi_region_models.jl

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using Oceananigans.Models: AbstractModel
22
using Oceananigans.Advection: WENO, VectorInvariant
3+
using Oceananigans.BuoyancyFormulations: BuoyancyForce, NegativeZDirection, AbstractBuoyancyFormulation, validate_unit_vector
34
using Oceananigans.Models.HydrostaticFreeSurfaceModels: AbstractFreeSurface
45
using Oceananigans.TimeSteppers: AbstractTimeStepper, QuasiAdamsBashforth2TimeStepper
56
using Oceananigans.Models: PrescribedVelocityFields
@@ -53,6 +54,15 @@ for T in Types
5354
end
5455
end
5556

57+
# TODO: For the moment, buoyancy gradients cannot be precomputed in MultiRegionModels
58+
function BuoyancyForce(grid::MultiRegionGrids, formulation::AbstractBuoyancyFormulation;
59+
gravity_unit_vector=NegativeZDirection(),
60+
materialize_gradients=false)
61+
62+
gravity_unit_vector = validate_unit_vector(gravity_unit_vector)
63+
return BuoyancyForce(formulation, gravity_unit_vector, nothing)
64+
end
65+
5666
@inline isregional(pv::PrescribedVelocityFields) = isregional(pv.u) | isregional(pv.v) | isregional(pv.w)
5767
@inline regions(pv::PrescribedVelocityFields) = regions(pv[findfirst(isregional, (pv.u, pv.v, pv.w))])
5868

test/regression_tests/rayleigh_benard_regression_test.jl

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using Oceananigans.Grids: xnode, znode
22
using Oceananigans.TimeSteppers: update_state!
3+
using Oceananigans.BuoyancyFormulations: BuoyancyForce
34
using Oceananigans.DistributedComputations: cpu_architecture, partition, reconstruct_global_grid
45

56
function run_rayleigh_benard_regression_test(arch, grid_type)
@@ -48,13 +49,13 @@ function run_rayleigh_benard_regression_test(arch, grid_type)
4849
bottom = BoundaryCondition(Value(), Δb))
4950

5051
model = NonhydrostaticModel(; grid,
51-
timestepper = :QuasiAdamsBashforth2,
52-
closure = ScalarDiffusivity=ν, κ=κ),
53-
tracers = (:b, :c),
54-
buoyancy = BuoyancyTracer(),
55-
boundary_conditions = (; b=bbcs),
56-
hydrostatic_pressure_anomaly = CenterField(grid),
57-
forcing = (; c=cforcing))
52+
timestepper = :QuasiAdamsBashforth2,
53+
closure = ScalarDiffusivity=ν, κ=κ),
54+
tracers = (:b, :c),
55+
buoyancy = BuoyancyForce(BuoyancyTracer()),
56+
boundary_conditions = (; b=bbcs),
57+
hydrostatic_pressure_anomaly = CenterField(grid),
58+
forcing = (; c=cforcing))
5859

5960
# Lz/Nz will work for both the :regular and :vertically_unstretched grids.
6061
Δt = 0.01 * min(model.grid.Δxᶜᵃᵃ, model.grid.Δyᵃᶜᵃ, Lz/Nz)^2 / ν

0 commit comments

Comments
 (0)