Skip to content

Commit ffa54aa

Browse files
authored
Merge pull request #167 from tomchor/jib-dispatch-on-buoyancymodel
Add `PotentialEnergy` methods for `BuoyancyTracer` and `SeawaterBuoyancy{<:LinearEquationOfState}`
2 parents f5fbff7 + 23d5e8e commit ffa54aa

File tree

3 files changed

+118
-52
lines changed

3 files changed

+118
-52
lines changed

src/Oceanostics.jl

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,6 @@ export ProgressMessengers
3333
#+++ Utils for validation
3434
# Right now, all kernels must be located at ccc
3535
using Oceananigans.TurbulenceClosures: AbstractScalarDiffusivity, ThreeDimensionalFormulation
36-
using Oceananigans.BuoyancyModels: Buoyancy, BuoyancyTracer, SeawaterBuoyancy, LinearEquationOfState
37-
using SeawaterPolynomials: BoussinesqEquationOfState
3836
using Oceananigans.Grids: Center, Face
3937

4038
validate_location(location, type, valid_location=(Center, Center, Center)) =
@@ -45,13 +43,6 @@ validate_dissipative_closure(closure) = error("Cannot calculate dissipation rate
4543
validate_dissipative_closure(::AbstractScalarDiffusivity{<:Any, ThreeDimensionalFormulation}) = nothing
4644
validate_dissipative_closure(closure_tuple::Tuple) = Tuple(validate_dissipative_closure(c) for c in closure_tuple)
4745

48-
validate_buoyancy(buoyancy::Nothing) = throw(ArgumentError("Cannot calculate gravitational potential energy without a buoyancy model."))
49-
validate_buoyancy(buoyancy::Buoyancy{<:BuoyancyTracer, g}) where g =
50-
throw(ArgumentError("Cannot calculate gravitational potential energy for the buoyancy model $(buoyancy.model)."))
51-
validate_buoyancy(buoyancy::Buoyancy{<:SeawaterBuoyancy{FT, <:LinearEquationOfState, T, S}, g}) where {FT, T, S, g} =
52-
throw(ArgumentError("To calculate gravitational potential energy with a linear equation of state use a linear `BoussinesqEquationOfState`."))
53-
validate_buoyancy(buoyancy::Buoyancy{<:SeawaterBuoyancy{FT, <:BoussinesqEquationOfState, T, S}, g}) where {FT, T, S, g} = nothing
54-
5546
#---
5647

5748
#+++ Utils for background fields

src/PotentialEnergyEquationTerms.jl

Lines changed: 91 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,24 @@ using DocStringExtensions
55
export PotentialEnergy
66

77
using Oceananigans.AbstractOperations: KernelFunctionOperation
8-
using Oceananigans: Models.seawater_density
9-
using Oceananigans: Models.model_geopotential_height
8+
using Oceananigans.Models: seawater_density
9+
using Oceananigans.Models: model_geopotential_height
1010
using Oceananigans.Grids: Center, Face
11-
using Oceanostics: validate_location, validate_buoyancy
12-
11+
using Oceananigans.Grids: NegativeZDirection
12+
using Oceananigans.BuoyancyModels: Buoyancy, BuoyancyTracer, SeawaterBuoyancy, LinearEquationOfState
13+
using Oceananigans.BuoyancyModels: buoyancy_perturbationᶜᶜᶜ, Zᶜᶜᶜ
14+
using Oceananigans.Models: ShallowWaterModel
15+
using Oceanostics: validate_location
16+
using SeawaterPolynomials: BoussinesqEquationOfState
17+
18+
const NoBuoyancyModel = Union{Nothing, ShallowWaterModel}
19+
const BuoyancyTracerModel = Buoyancy{<:BuoyancyTracer, g} where g
20+
const BuoyancyLinearEOSModel = Buoyancy{<:SeawaterBuoyancy{FT, <:LinearEquationOfState, T, S} where {FT, T, S}, g} where {g}
21+
const BuoyancyBoussinesqEOSModel = Buoyancy{<:SeawaterBuoyancy{FT, <:BoussinesqEquationOfState, T, S} where {FT, T, S}, g} where {g}
22+
23+
validate_gravity_unit_vector(gravity_unit_vector::NegativeZDirection) = nothing
24+
validate_gravity_unit_vector(gravity_unit_vector) =
25+
throw(ArgumentError("`PotentialEnergy` is curently only defined for models that have a `NegativeZDirection` gravity unit vector."))
1326

1427
"""
1528
$(SIGNATURES)
@@ -18,16 +31,48 @@ Return a `KernelFunctionOperation` to compute the `PotentialEnergy` per unit vol
1831
```math
1932
Eₚ = \\frac{gρz}{ρ₀}
2033
```
21-
at each grid `location` in `model`.
34+
at each grid `location` in `model`. `PotentialEnergy` is defined for both `BuoyancyTracer`
35+
and `SeawaterBuoyancy`. See the relevant Oceananigans.jl documentation on
36+
[buoyancy models](https://clima.github.io/OceananigansDocumentation/dev/model_setup/buoyancy_and_equation_of_state/)
37+
for more information about available options.
2238
23-
**NOTE:** A `BoussinesqEquationOfState` must be used in the `model` to calculate
24-
`seawater_density`. See the [relevant documentation](https://clima.github.io/OceananigansDocumentation/dev/model_setup/buoyancy_and_equation_of_state/#Idealized-nonlinear-equations-of-state)
25-
for how to set `SeawaterBuoyancy` using a `BoussinesqEquationOfState`.
39+
The optional keyword argument `geopotential_height` is only used
40+
if ones wishes to calculate `Eₚ` with a potential density referenced to `geopotential_height`,
41+
rather than in-situ density, when using a `BoussinesqEquationOfState`.
2642
2743
Example
2844
=======
2945
30-
The default behaviour of `PotentialEnergy` uses the *in-situ density* in the calculation:
46+
Usage with a `BuoyancyTracer` buoyacny model
47+
```jldoctest
48+
julia> using Oceananigans
49+
50+
julia> using Oceanostics.PotentialEnergyEquationTerms: PotentialEnergy
51+
52+
julia> grid = RectilinearGrid(size=100, z=(-1000, 0), topology=(Flat, Flat, Bounded))
53+
1×1×100 RectilinearGrid{Float64, Flat, Flat, Bounded} on CPU with 0×0×3 halo
54+
├── Flat x
55+
├── Flat y
56+
└── Bounded z ∈ [-1000.0, 0.0] regularly spaced with Δz=10.0
57+
58+
julia> model = NonhydrostaticModel(; grid, buoyancy=BuoyancyTracer(), tracers=(:b,))
59+
NonhydrostaticModel{CPU, RectilinearGrid}(time = 0 seconds, iteration = 0)
60+
├── grid: 1×1×100 RectilinearGrid{Float64, Flat, Flat, Bounded} on CPU with 0×0×3 halo
61+
├── timestepper: QuasiAdamsBashforth2TimeStepper
62+
├── tracers: b
63+
├── closure: Nothing
64+
├── buoyancy: BuoyancyTracer with ĝ = NegativeZDirection()
65+
└── coriolis: Nothing
66+
67+
julia> PotentialEnergy(model)
68+
KernelFunctionOperation at (Center, Center, Center)
69+
├── grid: 1×1×100 RectilinearGrid{Float64, Flat, Flat, Bounded} on CPU with 0×0×3 halo
70+
├── kernel_function: bz_ccc (generic function with 2 methods)
71+
└── arguments: ("1×1×100 Field{Center, Center, Center} on RectilinearGrid on CPU",)
72+
```
73+
74+
The default behaviour of `PotentialEnergy` uses the *in-situ density* in the calculation
75+
when the equation of state is a `BoussinesqEquationOfState`:
3176
```jldoctest
3277
julia> using Oceananigans, SeawaterPolynomials.TEOS10
3378
@@ -65,7 +110,7 @@ julia> PotentialEnergy(model)
65110
KernelFunctionOperation at (Center, Center, Center)
66111
├── grid: 1×1×100 RectilinearGrid{Float64, Flat, Flat, Bounded} on CPU with 0×0×3 halo
67112
├── kernel_function: g′z_ccc (generic function with 1 method)
68-
└── arguments: ("KernelFunctionOperation at (Center, Center, Center)", "KernelFunctionOperation at (Center, Center, Center)", "(g=9.80665, ρ₀=1020.0)")
113+
└── arguments: ("KernelFunctionOperation at (Center, Center, Center)", "(g=9.80665, ρ₀=1020.0)")
69114
```
70115
71116
To use a reference density set a constant value for the keyword argument `geopotential_height`
@@ -87,27 +132,56 @@ julia> model = NonhydrostaticModel(; grid, buoyancy, tracers);
87132
88133
julia> geopotential_height = 0; # density variable will be σ₀
89134
90-
julia> PotentialEnergy(model; geopotential_height)
135+
julia> PotentialEnergy(model)
91136
KernelFunctionOperation at (Center, Center, Center)
92137
├── grid: 1×1×100 RectilinearGrid{Float64, Flat, Flat, Bounded} on CPU with 0×0×3 halo
93138
├── kernel_function: g′z_ccc (generic function with 1 method)
94-
└── arguments: ("KernelFunctionOperation at (Center, Center, Center)", "KernelFunctionOperation at (Center, Center, Center)", "(g=9.80665, ρ₀=1020.0)")
139+
└── arguments: ("KernelFunctionOperation at (Center, Center, Center)", "(g=9.80665, ρ₀=1020.0)")
95140
```
96141
"""
97-
@inline function PotentialEnergy(model; geopotential_height = model_geopotential_height(model), location = (Center, Center, Center))
142+
@inline function PotentialEnergy(model; location = (Center, Center, Center),
143+
geopotential_height = model_geopotential_height(model))
98144

99145
validate_location(location, "PotentialEnergy")
100-
validate_buoyancy(model.buoyancy)
146+
isnothing(model.buoyancy) ? nothing : validate_gravity_unit_vector(model.buoyancy.gravity_unit_vector)
147+
148+
return PotentialEnergy(model, model.buoyancy, geopotential_height)
149+
end
150+
151+
@inline PotentialEnergy(model, buoyancy_model::NoBuoyancyModel, geopotential_height) =
152+
throw(ArgumentError("Cannot calculate gravitational potential energy without a Buoyancy model."))
153+
154+
@inline function PotentialEnergy(model, buoyancy_model::BuoyancyTracerModel, geopotential_height)
155+
156+
grid = model.grid
157+
b = model.tracers.b
158+
159+
return KernelFunctionOperation{Center, Center, Center}(bz_ccc, grid, b)
160+
end
161+
162+
@inline bz_ccc(i, j, k, grid, b) = b[i, j, k] * Zᶜᶜᶜ(i, j, k, grid)
163+
164+
@inline function PotentialEnergy(model, buoyancy_model::BuoyancyLinearEOSModel, geopotential_height)
165+
166+
grid = model.grid
167+
C = model.tracers
168+
b = buoyancy_model.model
169+
170+
return KernelFunctionOperation{Center, Center, Center}(bz_ccc, grid, b, C)
171+
end
172+
173+
@inline bz_ccc(i, j, k, grid, b, C) = buoyancy_perturbationᶜᶜᶜ(i, j, k, grid, b, C) * Zᶜᶜᶜ(i, j, k, grid)
174+
175+
@inline function PotentialEnergy(model, buoyancy_model::BuoyancyBoussinesqEOSModel, geopotential_height)
101176

102177
grid = model.grid
103178
ρ = seawater_density(model; geopotential_height)
104-
Z = model_geopotential_height(model)
105179
parameters = (g = model.buoyancy.model.gravitational_acceleration,
106180
ρ₀ = model.buoyancy.model.equation_of_state.reference_density)
107181

108-
return KernelFunctionOperation{Center, Center, Center}(g′z_ccc, grid, ρ, Z, parameters)
182+
return KernelFunctionOperation{Center, Center, Center}(g′z_ccc, grid, ρ, parameters)
109183
end
110184

111-
@inline g′z_ccc(i, j, k, grid, ρ, Z, p) = (p.g / p.ρ₀) * ρ[i, j, k] * Z[i, j, k]
185+
@inline g′z_ccc(i, j, k, grid, ρ, p) = (p.g / p.ρ₀) * ρ[i, j, k] * Zᶜᶜᶜ(i, j, k, grid)
112186

113187
end # module

test/runtests.jl

Lines changed: 27 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ using SeawaterPolynomials: RoquetEquationOfState, TEOS10EquationOfState
1111
using Oceanostics
1212
using Oceanostics: TKEBudgetTerms, TracerVarianceBudgetTerms, FlowDiagnostics, PressureRedistributionTerm, BuoyancyProductionTerm, AdvectionTerm
1313
using Oceanostics.TKEBudgetTerms: AdvectionTerm
14-
using Oceanostics: PotentialEnergy
14+
using Oceanostics: PotentialEnergy, PotentialEnergyEquationTerms.BuoyancyBoussinesqEOSModel
1515
using Oceanostics.ProgressMessengers
1616

1717
include("test_budgets.jl")
@@ -324,15 +324,15 @@ function test_tracer_diagnostics(model)
324324
return nothing
325325
end
326326

327-
function test_density_equation_terms_errors(model)
327+
function test_potential_energy_equation_terms_errors(model)
328328

329329
@test_throws ArgumentError PotentialEnergy(model)
330330
@test_throws ArgumentError PotentialEnergy(model, geopotential_height = 0)
331331

332332
return nothing
333333
end
334334

335-
function test_density_equation_terms(model; geopotential_height = nothing)
335+
function test_potential_energy_equation_terms(model; geopotential_height = nothing)
336336

337337
Eₚ = isnothing(geopotential_height) ? PotentialEnergy(model) :
338338
PotentialEnergy(model; geopotential_height)
@@ -342,18 +342,20 @@ function test_density_equation_terms(model; geopotential_height = nothing)
342342
@test Eₚ_field isa Field
343343
compute!(Eₚ_field)
344344

345-
ρ = isnothing(geopotential_height) ? Field(seawater_density(model)) :
346-
Field(seawater_density(model; geopotential_height))
345+
if model.buoyancy isa BuoyancyBoussinesqEOSModel
346+
ρ = isnothing(geopotential_height) ? Field(seawater_density(model)) :
347+
Field(seawater_density(model; geopotential_height))
347348

348-
compute!(ρ)
349-
Z = Field(model_geopotential_height(model))
350-
compute!(Z)
351-
ρ₀ = model.buoyancy.model.equation_of_state.reference_density
352-
g = model.buoyancy.model.gravitational_acceleration
349+
compute!(ρ)
350+
Z = Field(model_geopotential_height(model))
351+
compute!(Z)
352+
ρ₀ = model.buoyancy.model.equation_of_state.reference_density
353+
g = model.buoyancy.model.gravitational_acceleration
353354

354-
true_value = (g / ρ₀) .* ρ.data .* Z.data
355+
true_value = (g / ρ₀) .* ρ.data .* Z.data
355356

356-
@test isequal(Eₚ_field.data, true_value)
357+
@test isequal(Eₚ_field.data, true_value)
358+
end
357359

358360
return nothing
359361
end
@@ -464,9 +466,9 @@ closures = (ScalarDiffusivity(ν=1e-6, κ=1e-7),
464466
SmagorinskyLilly(),
465467
(ScalarDiffusivity=1e-6, κ=1e-7), AnisotropicMinimumDissipation()),)
466468

467-
invalid_buoyancy_models = (nothing, BuoyancyTracer(), SeawaterBuoyancy())
468-
valid_buoyancy_models = (SeawaterBuoyancy(equation_of_state=TEOS10EquationOfState()),
469-
SeawaterBuoyancy(equation_of_state=RoquetEquationOfState(:Linear)))
469+
buoyancy_models = (nothing, BuoyancyTracer(), SeawaterBuoyancy(),
470+
SeawaterBuoyancy(equation_of_state=TEOS10EquationOfState()),
471+
SeawaterBuoyancy(equation_of_state=RoquetEquationOfState(:Linear)))
470472

471473
grids = (regular_grid, stretched_grid)
472474

@@ -519,23 +521,22 @@ model_types = (NonhydrostaticModel, HydrostaticFreeSurfaceModel)
519521
test_tracer_diagnostics(model)
520522

521523
end
522-
@info "Testing error throws for invalid buoyancy models"
523-
for buoyancy in invalid_buoyancy_models
524+
525+
@info "Testing `PotentialEnergy`"
526+
for buoyancy in buoyancy_models
524527

525528
tracers = buoyancy isa BuoyancyTracer ? :b : (:S, :T)
526529
model = model_type(; grid, buoyancy, tracers)
527-
test_density_equation_terms_errors(model)
530+
buoyancy isa BuoyancyTracer ? set!(model, b = 9.87) : set!(model, S = 34.7, T = 0.5)
531+
if isnothing(buoyancy)
532+
test_potential_energy_equation_terms_errors(model)
533+
else
534+
test_potential_energy_equation_terms(model)
535+
test_potential_energy_equation_terms(model, geopotential_height = 0)
536+
end
528537

529538
end
530-
@info "Testing valid buoyancy models"
531-
for buoyancy in valid_buoyancy_models
532539

533-
model = model_type(; grid, buoyancy, tracers = (:S, :T))
534-
set!(model, S = 34.7, T = 0.5)
535-
test_density_equation_terms(model)
536-
test_density_equation_terms(model, geopotential_height = 0)
537-
538-
end
539540
end
540541

541542
@info "Testing input validation for dissipation rates"

0 commit comments

Comments
 (0)