Skip to content

Commit 7afb057

Browse files
Add support for ZStar in RK3 (#4156)
* add stuff * changes * test it out * let's go! * update * adapt tracer * try it out * fix this * let's go * add an RK3 test with ZStar * lock release
1 parent b3b5d00 commit 7afb057

File tree

8 files changed

+158
-189
lines changed

8 files changed

+158
-189
lines changed

src/Models/HydrostaticFreeSurfaceModels/explicit_free_surface.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ explicit_ab2_step_free_surface!(free_surface, model, Δt, χ) =
8181

8282
@kernel function _explicit_rk3_step_free_surface!(η, Δt, γⁿ, ζⁿ, Gⁿ, η⁻, Nz)
8383
i, j = @index(Global, NTuple)
84-
@inbounds η[i, j, Nz+1] += ζⁿ * η⁻[i, j, k] + γⁿ * (η[i, j, k] + convert(FT, Δt) * Gⁿ[i, j, k])
84+
@inbounds η[i, j, Nz+1] += ζⁿ * η⁻[i, j, Nz+1] + γⁿ * (η[i, j, Nz+1] + Δt * Gⁿ[i, j, Nz+1])
8585
end
8686

8787
@kernel function _explicit_ab2_step_free_surface!(η, Δt, χ, Gηⁿ, Gη⁻, Nz)

src/Models/HydrostaticFreeSurfaceModels/hydrostatic_free_surface_ab2_step.jl

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -89,8 +89,10 @@ function ab2_step_tracers!(tracers, model, Δt, χ)
8989
G⁻ = model.timestepper.G⁻[tracer_name]
9090
tracer_field = tracers[tracer_name]
9191
closure = model.closure
92+
grid = model.grid
9293

93-
ab2_step_tracer_field!(tracer_field, model.grid, Δt, χ, Gⁿ, G⁻)
94+
FT = eltype(grid)
95+
launch!(architecture(grid), grid, :xyz, _ab2_step_tracer_field!, tracer_field, grid, convert(FT, Δt), χ, Gⁿ, G⁻)
9496

9597
implicit_step!(tracer_field,
9698
model.timestepper.implicit_solver,
@@ -105,9 +107,6 @@ function ab2_step_tracers!(tracers, model, Δt, χ)
105107
return nothing
106108
end
107109

108-
ab2_step_tracer_field!(tracer_field, grid, Δt, χ, Gⁿ, G⁻) =
109-
launch!(architecture(grid), grid, :xyz, _ab2_step_tracer_field!, tracer_field, grid, Δt, χ, Gⁿ, G⁻)
110-
111110
#####
112111
##### Tracer update in mutable vertical coordinates
113112
#####
@@ -129,7 +128,7 @@ ab2_step_tracer_field!(tracer_field, grid, Δt, χ, Gⁿ, G⁻) =
129128

130129
# We store temporarily σθ in θ.
131130
# The unscaled θ will be retrieved with `unscale_tracers!`
132-
θ[i, j, k] = σᶜᶜⁿ * θ[i, j, k] + convert(FT, Δt) * ∂t_σθ
131+
θ[i, j, k] = σᶜᶜⁿ * θ[i, j, k] + Δt * ∂t_σθ
133132
end
134133
end
135134

src/Models/HydrostaticFreeSurfaceModels/hydrostatic_free_surface_field_tuples.jl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,5 +39,6 @@ previous_hydrostatic_tendency_fields(::Val{:SplitRungeKutta3}, args...) = nothin
3939
function previous_hydrostatic_tendency_fields(::Val{:SplitRungeKutta3}, velocities, free_surface::SplitExplicitFreeSurface, args...)
4040
U = similar(free_surface.barotropic_velocities.U)
4141
V = similar(free_surface.barotropic_velocities.V)
42-
return (; U=U, V=V)
42+
η = similar(free_surface.η)
43+
return (; U=U, V=V, η=η)
4344
end

src/Models/HydrostaticFreeSurfaceModels/hydrostatic_free_surface_rk3_step.jl

Lines changed: 67 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
1-
using Oceananigans.Fields: location
1+
using Oceananigans.Fields: location, instantiated_location
22
using Oceananigans.TurbulenceClosures: implicit_step!
33
using Oceananigans.ImmersedBoundaries: get_active_cells_map, get_active_column_map
44

5-
import Oceananigans.TimeSteppers: split_rk3_substep!, _split_rk3_substep_field!
5+
import Oceananigans.TimeSteppers: split_rk3_substep!, _split_rk3_substep_field!, store_fields!
66

77
function split_rk3_substep!(model::HydrostaticFreeSurfaceModel, Δt, γⁿ, ζⁿ)
88

99
grid = model.grid
1010
timestepper = model.timestepper
1111
free_surface = model.free_surface
12-
12+
1313
compute_free_surface_tendency!(grid, model, free_surface)
1414

1515
rk3_substep_velocities!(model.velocities, model, Δt, γⁿ, ζⁿ)
@@ -18,8 +18,7 @@ function split_rk3_substep!(model::HydrostaticFreeSurfaceModel, Δt, γⁿ, ζ
1818
# Full step for Implicit and Split-Explicit, substep for Explicit
1919
step_free_surface!(free_surface, model, timestepper, Δt)
2020

21-
# Average free surface variables
22-
# in the second stage
21+
# Average free surface variables in the second stage
2322
if model.clock.stage == 2
2423
rk3_average_free_surface!(free_surface, grid, timestepper, γⁿ, ζⁿ)
2524
end
@@ -46,11 +45,17 @@ function rk3_average_free_surface!(free_surface::SplitExplicitFreeSurface, grid,
4645

4746
Uⁿ⁻¹ = timestepper.Ψ⁻.U
4847
Vⁿ⁻¹ = timestepper.Ψ⁻.V
48+
ηⁿ⁻¹ = timestepper.Ψ⁻.η
4949
Uⁿ = free_surface.barotropic_velocities.U
5050
Vⁿ = free_surface.barotropic_velocities.V
51+
ηⁿ = free_surface.η
5152

5253
launch!(arch, grid, :xy, _rk3_average_free_surface!, Uⁿ, grid, Uⁿ⁻¹, γⁿ, ζⁿ)
5354
launch!(arch, grid, :xy, _rk3_average_free_surface!, Vⁿ, grid, Vⁿ⁻¹, γⁿ, ζⁿ)
55+
56+
# Averaging the free surface is only required for a grid with Mutable vertical coordinates,
57+
# which needs to update the grid based on the value of the free surface
58+
launch!(arch, grid, :xy, _rk3_average_free_surface!, ηⁿ, grid, ηⁿ⁻¹, γⁿ, ζⁿ)
5459

5560
return nothing
5661
end
@@ -97,19 +102,20 @@ function rk3_substep_tracers!(tracers, model, Δt, γⁿ, ζⁿ)
97102

98103
closure = model.closure
99104
grid = model.grid
105+
FT = eltype(grid)
100106

101107
# Tracer update kernels
102108
for (tracer_index, tracer_name) in enumerate(propertynames(tracers))
103109

104110
Gⁿ = model.timestepper.Gⁿ[tracer_name]
105111
Ψ⁻ = model.timestepper.Ψ⁻[tracer_name]
106-
tracer_field = tracers[tracer_name]
112+
θ = tracers[tracer_name]
107113
closure = model.closure
108114

109115
launch!(architecture(grid), grid, :xyz,
110-
_split_rk3_substep_field!, tracer_field, Δt, γⁿ, ζⁿ, Gⁿ, Ψ⁻)
116+
_split_rk3_substep_tracer_field!, θ, grid, convert(FT, Δt), γⁿ, ζⁿ, Gⁿ, Ψ⁻)
111117

112-
implicit_step!(tracer_field,
118+
implicit_step!(θ,
113119
model.timestepper.implicit_solver,
114120
closure,
115121
model.diffusivity_fields,
@@ -119,4 +125,56 @@ function rk3_substep_tracers!(tracers, model, Δt, γⁿ, ζⁿ)
119125
end
120126

121127
return nothing
122-
end
128+
end
129+
130+
#####
131+
##### Tracer update in mutable vertical coordinates
132+
#####
133+
134+
# σθ is the evolved quantity.
135+
# We store temporarily σθ in θ. Once σⁿ⁺¹ is known we can retrieve θⁿ⁺¹
136+
# with the `unscale_tracers!` function. Ψ⁻ is the previous tracer already scaled
137+
# by the vertical coordinate scaling factor: ψ⁻ = σ * θ
138+
@kernel function _split_rk3_substep_tracer_field!(θ, grid, Δt, γⁿ, ζⁿ, Gⁿ, Ψ⁻)
139+
i, j, k = @index(Global, NTuple)
140+
141+
σᶜᶜⁿ = σⁿ(i, j, k, grid, Center(), Center(), Center())
142+
@inbounds θ[i, j, k] = ζⁿ * Ψ⁻[i, j, k] + γⁿ * σᶜᶜⁿ * (θ[i, j, k] + Δt * Gⁿ[i, j, k])
143+
end
144+
145+
# We store temporarily σθ in θ.
146+
# The unscaled θ will be retrieved with `unscale_tracers!`
147+
@kernel function _split_rk3_substep_tracer_field!(θ, grid, Δt, ::Nothing, ::Nothing, Gⁿ, Ψ⁻)
148+
i, j, k = @index(Global, NTuple)
149+
@inbounds θ[i, j, k] = Ψ⁻[i, j, k] + Δt * Gⁿ[i, j, k] * σⁿ(i, j, k, grid, Center(), Center(), Center())
150+
end
151+
152+
#####
153+
##### Storing previous fields for the RK3 update
154+
#####
155+
156+
# Tracers are multiplied by the vertical coordinate scaling factor
157+
@kernel function _store_tracer_fields!(Ψ⁻, grid, Ψⁿ)
158+
i, j, k = @index(Global, NTuple)
159+
@inbounds Ψ⁻[i, j, k] = Ψⁿ[i, j, k] * σⁿ(i, j, k, grid, Center(), Center(), Center())
160+
end
161+
162+
function store_fields!(model::HydrostaticFreeSurfaceModel)
163+
164+
previous_fields = model.timestepper.Ψ⁻
165+
model_fields = prognostic_fields(model)
166+
grid = model.grid
167+
arch = architecture(grid)
168+
169+
for name in keys(model_fields)
170+
Ψ⁻ = previous_fields[name]
171+
Ψⁿ = model_fields[name]
172+
if name keys(model.tracers) # Tracers are stored with the grid scaling
173+
launch!(arch, grid, :xyz, _store_tracer_fields!, Ψ⁻, grid, Ψⁿ)
174+
else # Velocities and free surface are stored without the grid scaling
175+
parent(Ψ⁻) .= parent(Ψⁿ)
176+
end
177+
end
178+
179+
return nothing
180+
end

test/test_zstar_coordinate.jl

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,5 +197,31 @@ end
197197
end
198198
end
199199
end
200+
201+
@info " Testing a ZStar and Runge Kutta 3rd order time stepping"
202+
203+
topology = topologies[2]
204+
rtg = RectilinearGrid(arch; size = (10, 10, 20), x = (0, 100kilometers), y = (-10kilometers, 10kilometers), topology, z = z_uniform)
205+
llg = LatitudeLongitudeGrid(arch; size = (10, 10, 20), latitude = (0, 1), longitude = (0, 1), topology, z = z_uniform)
206+
irtg = ImmersedBoundaryGrid(rtg, GridFittedBottom((x, y) -> rand() - 10))
207+
illg = ImmersedBoundaryGrid(llg, GridFittedBottom((x, y) -> rand() - 10))
208+
209+
for grid in [rtg, llg, irtg, illg]
210+
211+
split_free_surface = SplitExplicitFreeSurface(grid; cfl = 0.75)
212+
model = HydrostaticFreeSurfaceModel(; grid,
213+
free_surface = split_free_surface,
214+
tracers = (:b, :c),
215+
timestepper = :SplitRungeKutta3,
216+
buoyancy = BuoyancyTracer(),
217+
vertical_coordinate = ZStar())
218+
219+
bᵢ(x, y, z) = x < grid.Lx / 2 ? 0.06 : 0.01
220+
221+
set!(model, c = (x, y, z) -> rand(), b = bᵢ)
222+
223+
Δt = 2minutes
224+
test_zstar_coordinate(model, 100, Δt)
225+
end
200226
end
201227
end

validation/distributed_simulations/distributed_geostrophic_adjustment.jl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ topo = (Bounded, Periodic, Bounded)
2121

2222
partition = Partition(x = Sizes(10, 13, 18, 39))
2323

24-
arch = Distributed(CPU(); partition)
24+
arch = Distributed(CPU())
2525

2626
# Distribute problem irregularly
2727
Nx = 80
@@ -46,6 +46,7 @@ coriolis = FPlane(f=1e-4)
4646

4747
model = HydrostaticFreeSurfaceModel(; grid,
4848
coriolis,
49+
timestepper = :SplitRungeKutta3,
4950
free_surface = SplitExplicitFreeSurface(grid; substeps=10))
5051

5152
gaussian(x, L) = exp(-x^2 / 2L^2)

0 commit comments

Comments
 (0)