Skip to content

Commit 68007f1

Browse files
committed
Add PotentialEnergy methods for BT and linearEOS
1 parent c40e8d6 commit 68007f1

File tree

3 files changed

+103
-75
lines changed

3 files changed

+103
-75
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: 76 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -5,31 +5,22 @@ 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
1111
using Oceananigans.BuoyancyModels: Buoyancy, BuoyancyTracer, SeawaterBuoyancy, LinearEquationOfState
12-
using Oceananigans.Models: NonhydrostaticModel, HydrostaticFreeSurfaceModel
13-
using Oceanostics: validate_location, validate_buoyancy
12+
using Oceananigans.BuoyancyModels: buoyancy_perturbationᶜᶜᶜ
13+
using Oceananigans.Models: ShallowWaterModel
14+
using Oceanostics: validate_location
1415
using SeawaterPolynomials: BoussinesqEquationOfState
1516

16-
const ModelsWithBoussinesqEOS = Union{NonhydrostaticModel{TS, E, A, G, T,
17-
<:Buoyancy{<:SeawaterBuoyancy{FT, <:BoussinesqEquationOfState, Temp, Sal}},
18-
R, SD, U, C, Φ, F, V, S, K, BG, P, BGC, I, AF},
19-
HydrostaticFreeSurfaceModel{TS, E, A, S, G, T, V,
20-
<:Buoyancy{<:SeawaterBuoyancy{FT, <:BoussinesqEquationOfState, Temp, Sal}},
21-
R, F, P, BGC, U, C, Φ, K, AF}} where
22-
{TS, E, A, G, T, R, SD, U, C, Φ, F, V, S, K, BG, P,
23-
BGC, I, AF, FT, Temp, Sal}
24-
25-
const ModelsWithBuoyancyTracer = Union{NonhydrostaticModel{TS, E, A, G, T,
26-
<:Buoyancy{<:BuoyancyTracer, g},
27-
R, SD, U, C, Φ, F, V, S, K, BG, P, BGC, I, AF},
28-
HydrostaticFreeSurfaceModel{TS, E, A, S, G, T, V,
29-
<:Buoyancy{<:BuoyancyTracer, g},
30-
R, F, P, BGC, U, C, Φ, K, AF}} where
31-
{TS, E, A, G, T, R, SD, U, C, Φ, F, V, S, K, BG, P,
32-
BGC, I, AF, g}
17+
const NoBuoyancyModel = Union{Nothing, ShallowWaterModel}
18+
const BuoyancyTracerModel = Buoyancy{<:BuoyancyTracer, g} where g
19+
const BuoyancyLinearEOSModel = Buoyancy{<:SeawaterBuoyancy{FT, <:LinearEquationOfState, T, S}} where {FT, T, S}
20+
const BuoyancyBoussinesqEOSModel = Buoyancy{<:SeawaterBuoyancy{FT, <:BoussinesqEquationOfState, T, S}} where {FT, T, S}
21+
22+
linear_eos_buoyancy(model) = linear_eos_buoyancy(model.grid, model.buoyancy.model, model.tracers)
23+
linear_eos_buoyancy(grid, buoyancy, tracers) = KernelFunctionOperation{Center, Center, Center}(buoyancy_perturbationᶜᶜᶜ, grid, buoyancy, tracers)
3324

3425
"""
3526
$(SIGNATURES)
@@ -38,16 +29,48 @@ Return a `KernelFunctionOperation` to compute the `PotentialEnergy` per unit vol
3829
```math
3930
Eₚ = \\frac{gρz}{ρ₀}
4031
```
41-
at each grid `location` in `model`.
32+
at each grid `location` in `model`. `PotentialEnergy` is defined for both `BuoyancyTracer`
33+
and `SeawaterBuoyancy`. See the relevant Oceananigans.jl documentation on
34+
[buoyancy models](https://clima.github.io/OceananigansDocumentation/dev/model_setup/buoyancy_and_equation_of_state/)
35+
for more information about available options.
4236
43-
**NOTE:** A `BoussinesqEquationOfState` must be used in the `model` to calculate
44-
`seawater_density`. See the [relevant documentation](https://clima.github.io/OceananigansDocumentation/dev/model_setup/buoyancy_and_equation_of_state/#Idealized-nonlinear-equations-of-state)
45-
for how to set `SeawaterBuoyancy` using a `BoussinesqEquationOfState`.
37+
The optional keyword argument `geopotential_height` is only used
38+
if ones wishes to calculate `Eₚ` with a potential density referenced to `geopotential_height`,
39+
rather than in-situ density, when using a `BoussinesqEquationOfState`.
4640
4741
Example
4842
=======
4943
50-
The default behaviour of `PotentialEnergy` uses the *in-situ density* in the calculation:
44+
Usage with a `BuoyancyTracer` buoyacny model
45+
```jldoctest
46+
julia> using Oceananigans
47+
48+
julia> using Oceanostics.PotentialEnergyEquationTerms: PotentialEnergy
49+
50+
julia> grid = RectilinearGrid(size=100, z=(-1000, 0), topology=(Flat, Flat, Bounded))
51+
1×1×100 RectilinearGrid{Float64, Flat, Flat, Bounded} on CPU with 0×0×3 halo
52+
├── Flat x
53+
├── Flat y
54+
└── Bounded z ∈ [-1000.0, 0.0] regularly spaced with Δz=10.0
55+
56+
julia> model = NonhydrostaticModel(; grid, buoyancy=BuoyancyTracer(), tracers=(:b,))
57+
NonhydrostaticModel{CPU, RectilinearGrid}(time = 0 seconds, iteration = 0)
58+
├── grid: 1×1×100 RectilinearGrid{Float64, Flat, Flat, Bounded} on CPU with 0×0×3 halo
59+
├── timestepper: QuasiAdamsBashforth2TimeStepper
60+
├── tracers: b
61+
├── closure: Nothing
62+
├── buoyancy: BuoyancyTracer with ĝ = NegativeZDirection()
63+
└── coriolis: Nothing
64+
65+
julia> PotentialEnergy(model)
66+
KernelFunctionOperation at (Center, Center, Center)
67+
├── grid: 1×1×100 RectilinearGrid{Float64, Flat, Flat, Bounded} on CPU with 0×0×3 halo
68+
├── kernel_function: bz_ccc (generic function with 1 method)
69+
└── arguments: ("1×1×100 Field{Center, Center, Center} on RectilinearGrid on CPU", "KernelFunctionOperation at (Center, Center, Center)")
70+
```
71+
72+
The default behaviour of `PotentialEnergy` uses the *in-situ density* in the calculation
73+
when the equation of state is a `BoussinesqEquationOfState`:
5174
```jldoctest
5275
julia> using Oceananigans, SeawaterPolynomials.TEOS10
5376
@@ -114,35 +137,48 @@ KernelFunctionOperation at (Center, Center, Center)
114137
└── arguments: ("KernelFunctionOperation at (Center, Center, Center)", "KernelFunctionOperation at (Center, Center, Center)", "(g=9.80665, ρ₀=1020.0)")
115138
```
116139
"""
117-
@inline function PotentialEnergy(model::ModelsWithBoussinesqEOS;
118-
geopotential_height = model_geopotential_height(model),
119-
location = (Center, Center, Center))
140+
@inline function PotentialEnergy(model; location = (Center, Center, Center),
141+
geopotential_height = model_geopotential_height(model))
120142

121143
validate_location(location, "PotentialEnergy")
122144

145+
return PotentialEnergy(model, model.buoyancy, geopotential_height)
146+
end
147+
148+
@inline PotentialEnergy(model, buoyancy_model::NoBuoyancyModel, geopotential_height) =
149+
throw(ArgumentError("Cannot calculate gravitational potential energy without a Buoyancy model."))
150+
151+
@inline function PotentialEnergy(model, buoyancy_model::BuoyancyTracerModel, geopotential_height)
152+
123153
grid = model.grid
124-
ρ = seawater_density(model; geopotential_height)
154+
b = model.tracers.b
125155
Z = model_geopotential_height(model)
126-
parameters = (g = model.buoyancy.model.gravitational_acceleration,
127-
ρ₀ = model.buoyancy.model.equation_of_state.reference_density)
128156

129-
return KernelFunctionOperation{Center, Center, Center}(g′z_ccc, grid, ρ, Z, parameters)
157+
return KernelFunctionOperation{Center, Center, Center}(bz_ccc, grid, b, Z)
130158
end
131159

132-
@inline g′z_ccc(i, j, k, grid, ρ, Z, p) = (p.g / p.ρ₀) * ρ[i, j, k] * Z[i, j, k]
133-
134-
@inline function PotentialEnergy(model::ModelsWithBuoyancyTracer;
135-
location = (Center, Center, Center))
136-
137-
validate_location(location, "PotentialEnergy")
160+
@inline function PotentialEnergy(model, buoyancy_model::BuoyancyLinearEOSModel, geopotential_height)
138161

139162
grid = model.grid
140-
b = model.tracers.b
163+
b = linear_eos_buoyancy(model)
141164
Z = model_geopotential_height(model)
142165

143166
return KernelFunctionOperation{Center, Center, Center}(bz_ccc, grid, b, Z)
144167
end
145168

146169
@inline bz_ccc(i, j, k, grid, b, Z) = b[i, j, k] * Z[i, j, k]
147170

171+
@inline function PotentialEnergy(model, buoyancy_model::BuoyancyBoussinesqEOSModel, geopotential_height)
172+
173+
grid = model.grid
174+
ρ = seawater_density(model; geopotential_height)
175+
Z = model_geopotential_height(model)
176+
parameters = (g = model.buoyancy.model.gravitational_acceleration,
177+
ρ₀ = model.buoyancy.model.equation_of_state.reference_density)
178+
179+
return KernelFunctionOperation{Center, Center, Center}(g′z_ccc, grid, ρ, Z, parameters)
180+
end
181+
182+
@inline g′z_ccc(i, j, k, grid, ρ, Z, p) = (p.g / p.ρ₀) * ρ[i, j, k] * Z[i, j, k]
183+
148184
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)