Skip to content

Commit 7dba5fd

Browse files
committed
SSPRK2 and 3 implemented
1 parent 70d38d1 commit 7dba5fd

File tree

7 files changed

+264
-115
lines changed

7 files changed

+264
-115
lines changed

src/Constants.jl

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
struct Constants{T<:AbstractFloat,Tprog<:AbstractFloat}
22

3-
# RUNGE-KUTTA COEFFICIENTS 3rd/4th order including timestep Δt
3+
# RUNGE-KUTTA COEFFICIENTS 2nd/3rd/4th order including timestep Δt
44
RKaΔt::Array{Tprog,1}
55
RKbΔt::Array{Tprog,1}
6+
Δt_Δs::Tprog # Δt/(s-1) wher s the number of stages
7+
Δt_Δ::Tprog # Δt/Δ - timestep divided by grid spacing
8+
Δt_Δ_half::Tprog # 1/2 * Δt/Δ
69

710
# BOUNDARY CONDITIONS
811
one_minus_α::Tprog # tangential boundary condition for the ghost-point copy
@@ -39,6 +42,13 @@ function Constants{T,Tprog}(P::Parameter,G::Grid) where {T<:AbstractFloat,Tprog<
3942
RKbΔt = Tprog.([.5,.5,1.]*G.dtint/G.Δ)
4043
end
4144

45+
# Δt/(s-1) for SSPRK2
46+
Δt_Δs = Tprog(G.dtint/G.Δ/(P.RKs-1))
47+
48+
# time step and half the time step including the grid spacing as this is not included in the RHS
49+
Δt_Δ = Tprog(G.dtint/G.Δ)
50+
Δt_Δ_half = Tprog(G.dtint/G.Δ/2)
51+
4252
one_minus_α = Tprog(1-P.α) # for the ghost point copy/tangential boundary conditions
4353
g = T(P.g) # gravity - for Bernoulli potential
4454

@@ -65,7 +75,8 @@ function Constants{T,Tprog}(P::Parameter,G::Grid) where {T<:AbstractFloat,Tprog<
6575
ωFx = 2π*P.ωFx/24/365.25/3600
6676
ωFy = 2π*P.ωFy/24/365.25/3600
6777

68-
return Constants{T,Tprog}( RKaΔt,RKbΔt,one_minus_α,
78+
return Constants{T,Tprog}( RKaΔt,RKbΔt,Δt_Δs,Δt_Δ,Δt_Δ_half,
79+
one_minus_α,
6980
g,cD,rD,γ,cSmag,νB,rSST,
7081
jSST,SSTmin,ωFη,ωFx,ωFy)
7182
end

src/Continuity.jl

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -69,16 +69,31 @@ end
6969

7070

7171
"""Transit function to call the specified continuity function."""
72-
function continuity!( η::AbstractMatrix,
72+
function continuity!( u::AbstractMatrix,
73+
v::AbstractMatrix,
74+
η::AbstractMatrix,
7375
Diag::DiagnosticVars,
7476
S::ModelSetup,
7577
t::Int)
78+
79+
@unpack U,V,dUdx,dVdy = Diag.VolumeFluxes
80+
@unpack nstep_advcor = S.grid
81+
@unpack time_scheme,surface_relax,surface_forcing = S.parameters
7682

77-
if S.parameters.surface_relax
83+
if nstep_advcor > 0 || # then UV wasn't calculated inside rhs!
84+
time_scheme != "RK" # then u,v changed before evaluating semi-implciti continuity
85+
UVfluxes!(u,v,η,Diag,S)
86+
end
87+
88+
# divergence of mass flux
89+
∂x!(dUdx,U)
90+
∂y!(dVdy,V)
91+
92+
if surface_relax
7893
continuity_surf_relax!(η,Diag,S,t)
79-
elseif S.parameters.surface_forcing
94+
elseif surface_forcing
8095
continuity_forcing!(Diag,S,t)
8196
else
8297
continuity_itself!(Diag,S,t)
8398
end
84-
end
99+
end

src/DefaultParameters.jl

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,9 @@
4747
wk::Real=10e3 # width [m] in y of Gaussian used for surface forcing
4848

4949
# TIME STEPPING OPTIONS
50-
RKo::Int=4 # Order of the RK time stepping scheme (3 or 4)
50+
time_scheme::String="RK" # Runge-Kutta ("RK") or strong-stability preserving RK ("SSPRK2","SSPRK3")
51+
RKo::Int=4 # Order of the RK time stepping scheme (2, 3 or 4)
52+
RKs::Int=2 # Number of stages for SSPRK2
5153
cfl::Real=1.0 # CFL number (1.0 recommended for RK4, 0.6 for RK3)
5254
Ndays::Real=10.0 # number of days to integrate for
5355
nstep_diff::Int=1 # diffusive part every nstep_diff time steps.
@@ -118,19 +120,21 @@
118120
@assert topo_width > 0.0 "topo_width has to be >0, $topo_width given."
119121
@assert t_relax > 0.0 "t_relax has to be >0, $t_relax given."
120122
@assert η_refw > 0.0 "η_refw has to be >0, $η_refw given."
121-
@assert RKo in [2,3,4] "RKo has to be 2,3 or 4, $RKo given."
123+
@assert time_scheme in ["RK","SSPRK2","SSPRK3"] "Time scheme $time_scheme unsupported."
124+
@assert RKo in [2,3,4] "RKo has to be 2,3 or 4; $RKo given."
125+
@assert RKs > 1 "RKs has to be >= 2; $RKs given."
122126
@assert Ndays > 0.0 "Ndays has to be >0, $Ndays given."
123127
@assert nstep_diff > 0 "nstep_diff has to be >0, $nstep_diff given."
124128
@assert nstep_advcor >= 0 "nstep_advcor has to be >=0, $nstep_advcor given."
125129
@assert bc in ["periodic","nonperiodic"] "boundary condition '$bc' unsupported."
126130
@assert α >= 0.0 && α <= 2.0 "Tangential boundary condition α has to be in (0,2), given."
127131
@assert adv_scheme in ["Sadourny","ArakawaHsu"] "Advection scheme '$adv_scheme' unsupported"
128-
@assert dynamics in ["linear","nonlinear"] "Dynamics '$dynamics' unsupoorted."
132+
@assert dynamics in ["linear","nonlinear"] "Dynamics '$dynamics' unsupported."
129133
@assert bottom_drag in ["quadratic","linear","none"] "Bottom drag '$bottom_drag' unsupported."
130134
@assert cD >= 0.0 "Bottom drag coefficient cD has to be >=0, $cD given."
131135
@assert τD >= 0.0 "Bottom drag coefficient τD has to be >=0, $τD given."
132-
@assert diffusion in ["Smagorinsky", "constant"] "Diffusion '$diffusion' unsupoorted."
133-
@assert νB > 0.0 "Diffusion scaling constant νB has to be > 0, $νB given."
136+
@assert diffusion in ["Smagorinsky", "constant"] "Diffusion '$diffusion' unsupported."
137+
@assert νB > 0.0 "Diffusion scaling constant νB has to be >0, $νB given."
134138
@assert cSmag > 0.0 "Smagorinsky coefficient cSmag has to be >0, $cSmag given."
135139
@assert injection_region in ["west","south"] "Injection region '$injection_region' unsupported."
136140
@assert Uadv > 0.0 "Advection velocity scale Uadv has to be >0, $Uadv given."

src/GhostPoints.jl

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,24 @@ function ghost_points!( u::AbstractMatrix,
176176
end
177177
end
178178

179+
"""Decide on boundary condition P.bc which ghost point function to execute."""
180+
function ghost_points!( u::AbstractMatrix,
181+
v::AbstractMatrix,
182+
S::ModelSetup)
183+
184+
@unpack bc = S.parameters
185+
C = S.constants
186+
187+
if bc == "periodic"
188+
@unpack Tcomm = S.parameters
189+
ghost_points_u_periodic!(Tcomm,u,C)
190+
ghost_points_v_periodic!(Tcomm,v)
191+
else
192+
ghost_points_u_nonperiodic!(C,u)
193+
ghost_points_v_nonperiodic!(C,v)
194+
end
195+
end
196+
179197
"""Decide on boundary condition P.bc which ghost point function to execute."""
180198
function ghost_points_sst!(sst::AbstractMatrix,S::ModelSetup)
181199

src/InitialConditions.jl

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,12 +68,10 @@ function initial_conditions(::Type{T},S::ModelSetup) where {T<:AbstractFloat}
6868
v = reshape(v,size(v)[1:2])
6969
η = reshape(η,size(η)[1:2])
7070

71-
# Interpolation in case the grids don't match?
72-
7371
nx_old,ny_old = size(η)
7472

7573
if (nx_old,ny_old) != (nx,ny)
76-
if init_interpolation
74+
if init_interpolation # interpolation in case the grids don't match
7775

7876
# old grids
7977
x_T = collect(0.5:nx_old-0.5)

src/TimeIntegration.jl

Lines changed: 138 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,21 @@ function time_integration( Prog::PrognosticVars{Tprog},
99
@unpack du,dv,dη = Diag.Tendencies
1010
@unpack um,vm = Diag.SemiLagrange
1111

12-
@unpack dynamics,RKo,tracer_advection = S.parameters
12+
@unpack dynamics,RKo,RKs,tracer_advection = S.parameters
13+
@unpack time_scheme = S.parameters
1314
@unpack RKaΔt,RKbΔt = S.constants
15+
@unpack Δt_Δ,Δt_Δs = S.constants
1416

1517
@unpack nt,dtint = S.grid
1618
@unpack nstep_advcor,nstep_diff,nadvstep,nadvstep_half = S.grid
1719

18-
if dynamics == "linear"
19-
Ix!(Diag.VolumeFluxes.h_u,S.forcing.H)
20-
Iy!(Diag.VolumeFluxes.h_v,S.forcing.H)
21-
end
20+
# some precalculations
21+
thickness!(Diag.VolumeFluxes.h,η,S.forcing.H)
22+
Ix!(Diag.VolumeFluxes.h_u,Diag.VolumeFluxes.h)
23+
Iy!(Diag.VolumeFluxes.h_v,Diag.VolumeFluxes.h)
24+
Ixy!(Diag.Vorticity.h_q,Diag.VolumeFluxes.h)
25+
advection_coriolis!(u,v,η,Diag,S)
26+
PVadvection!(Diag,S)
2227

2328
# propagate initial conditions
2429
copyto!(u0,u)
@@ -40,29 +45,104 @@ function time_integration( Prog::PrognosticVars{Tprog},
4045
copyto!(v1,v)
4146
copyto!(η1,η)
4247

43-
# Runge-Kutta 4th order / 3rd order
44-
for rki = 1:RKo
45-
if rki > 1
46-
ghost_points!(u1,v1,η1,S)
48+
if time_scheme == "RK" # classic RK4,3 or 2
49+
50+
for rki = 1:RKo
51+
if rki > 1
52+
ghost_points!(u1,v1,η1,S)
53+
end
54+
55+
# type conversion for mixed precision
56+
u1rhs = convert(Diag.PrognosticVarsRHS.u,u1)
57+
v1rhs = convert(Diag.PrognosticVarsRHS.v,v1)
58+
η1rhs = convert(Diag.PrognosticVarsRHS.η,η1)
59+
60+
rhs!(u1rhs,v1rhs,η1rhs,Diag,S,t) # momentum only
61+
continuity!(u1rhs,v1rhs,η1rhs,Diag,S,t) # continuity equation
62+
63+
if rki < RKo
64+
caxb!(u1,u,RKbΔt[rki],du) #u1 .= u .+ RKb[rki]*Δt*du
65+
caxb!(v1,v,RKbΔt[rki],dv) #v1 .= v .+ RKb[rki]*Δt*dv
66+
caxb!(η1,η,RKbΔt[rki],dη) #η1 .= η .+ RKb[rki]*Δt*dη
67+
end
68+
69+
# sum RK-substeps on the go
70+
axb!(u0,RKaΔt[rki],du) #u0 .+= RKa[rki]*Δt*du
71+
axb!(v0,RKaΔt[rki],dv) #v0 .+= RKa[rki]*Δt*dv
72+
axb!(η0,RKaΔt[rki],dη) #η0 .+= RKa[rki]*Δt*dη
4773
end
4874

49-
# type conversion for mixed precision
50-
u1rhs = convert(Diag.PrognosticVarsRHS.u,u1)
51-
v1rhs = convert(Diag.PrognosticVarsRHS.v,v1)
52-
η1rhs = convert(Diag.PrognosticVarsRHS.η,η1)
75+
elseif time_scheme == "SSPRK2" # s-stage 2nd order SSPRK
76+
77+
for rki = 1:RKs
78+
if rki > 1
79+
# TODO technically the ghost point copy for u1,v1 is redundant as done further down
80+
ghost_points!(u1,v1,η1,S)
81+
end
82+
83+
# type conversion for mixed precision
84+
u1rhs = convert(Diag.PrognosticVarsRHS.u,u1)
85+
v1rhs = convert(Diag.PrognosticVarsRHS.v,v1)
86+
η1rhs = convert(Diag.PrognosticVarsRHS.η,η1)
5387

54-
rhs!(u1rhs,v1rhs,η1rhs,Diag,S,t)
88+
rhs!(u1rhs,v1rhs,η1rhs,Diag,S,t)
5589

56-
if rki < RKo
57-
caxb!(u1,u,RKbΔt[rki],du) #u1 .= u .+ RKb[rki]*Δt*du
58-
caxb!(v1,v,RKbΔt[rki],dv) #v1 .= v .+ RKb[rki]*Δt*dv
59-
caxb!(η1,η,RKbΔt[rki],dη) #η1 .= η .+ RKb[rki]*Δt*dη
90+
# the update step
91+
axb!(u1,Δt_Δs,du) # u1 = u1 + Δt/(s-1)*RHS(u1)
92+
axb!(v1,Δt_Δs,dv)
93+
94+
# semi-implicit for continuity equation, use u1,v1 to calcualte dη
95+
ghost_points!(u1,v1,S)
96+
u1rhs = convert(Diag.PrognosticVarsRHS.u,u1)
97+
v1rhs = convert(Diag.PrognosticVarsRHS.v,v1)
98+
continuity!(u1rhs,v1rhs,η1rhs,Diag,S,t)
99+
axb!(η1,Δt_Δs,dη) # η1 = η1 + Δt/(s-1)*RHS(u1)
60100
end
61101

62-
# sum RK-substeps on the go
63-
axb!(u0,RKaΔt[rki],du) #u0 .+= RKa[rki]*Δt*du
64-
axb!(v0,RKaΔt[rki],dv) #v0 .+= RKa[rki]*Δt*dv
65-
axb!(η0,RKaΔt[rki],dη) #η0 .+= RKa[rki]*Δt*dη
102+
a = 1/RKs
103+
b = (RKs-1)/RKs
104+
cxayb!(u0,a,u,b,u1)
105+
cxayb!(v0,a,v,b,v1)
106+
cxayb!(η0,a,η,b,η1)
107+
108+
elseif time_scheme == "SSPRK3" # 4-stage SSPRK3
109+
110+
for rki = 1:4
111+
if rki > 1
112+
ghost_points!(u1,v1,η1,S)
113+
end
114+
115+
# type conversion for mixed precision
116+
u1rhs = convert(Diag.PrognosticVarsRHS.u,u1)
117+
v1rhs = convert(Diag.PrognosticVarsRHS.v,v1)
118+
η1rhs = convert(Diag.PrognosticVarsRHS.η,η1)
119+
120+
rhs!(u1rhs,v1rhs,η1rhs,Diag,S,t)
121+
122+
caxb!(u0,u1,Δt_Δ,du) # store Euler update into u0,v0
123+
caxb!(v0,v1,Δt_Δ,dv)
124+
cxab!(u1,1/2,u1,u0) # average u0,u1 and store in u1
125+
cxab!(v1,1/2,v1,v0) # same
126+
127+
# semi-implicit for continuity equation, use u1,v1 to calcualte dη
128+
ghost_points!(u1,v1,S)
129+
u1rhs = convert(Diag.PrognosticVarsRHS.u,u1)
130+
v1rhs = convert(Diag.PrognosticVarsRHS.v,v1)
131+
continuity!(u1rhs,v1rhs,η1rhs,Diag,S,t)
132+
133+
caxb!(η0,η1,Δt_Δ,dη) # store Euler update into η0
134+
cxab!(η1,1/2,η1,η0) # average η0,η1 and store in η1
135+
136+
if rki == 3
137+
cxayb!(u1,2/3,u,1/3,u1)
138+
cxayb!(v1,2/3,v,1/3,v1)
139+
cxayb!(η1,2/3,η,1/3,η1)
140+
elseif rki == 4
141+
copyto!(u0,u1)
142+
copyto!(v0,v1)
143+
copyto!(η0,η1)
144+
end
145+
end
66146
end
67147

68148
ghost_points!(u0,v0,η0,S)
@@ -76,7 +156,9 @@ function time_integration( Prog::PrognosticVars{Tprog},
76156
# although included in the tendency of every RK substep,
77157
# only update every nstep_advcor steps if nstep_advcor > 0
78158
if dynamics == "nonlinear" && nstep_advcor > 0 && (i % nstep_advcor) == 0
159+
UVfluxes!(u0rhs,v0rhs,η0rhs,Diag,S)
79160
advection_coriolis!(u0rhs,v0rhs,η0rhs,Diag,S)
161+
PVadvection!(Diag,S)
80162
end
81163

82164
# DIFFUSIVE TERMS - SEMI-IMPLICIT EULER
@@ -86,23 +168,26 @@ function time_integration( Prog::PrognosticVars{Tprog},
86168
bottom_drag!(u0rhs,v0rhs,η0rhs,Diag,S)
87169
diffusion!(u0rhs,v0rhs,Diag,S)
88170
add_drag_diff_tendencies!(u0,v0,Diag,S)
171+
ghost_points!(u0,v0,S)
89172
end
90173

91174
t += dtint
92175

93176
# TRACER ADVECTION
177+
u0rhs = convert(Diag.PrognosticVarsRHS.u,u0) # copy back as add_drag_diff_tendencies changed u0,v0
178+
v0rhs = convert(Diag.PrognosticVarsRHS.v,v0)
94179
tracer!(i,u0rhs,v0rhs,Prog,Diag,S)
95180

96181
# feedback and output
97182
feedback.i = i
98183
feedback!(Prog,feedback,S)
99-
output_nc!(i,netCDFfiles,Prog,Diag,S)
184+
output_nc!(i,netCDFfiles,Prog,Diag,S) # uses u0,v0,η0
100185

101186
if feedback.nans_detected
102187
break
103188
end
104189

105-
# RK3/4 copy back from substeps
190+
# Copy back from substeps
106191
copyto!(u,u0)
107192
copyto!(v,v0)
108193
copyto!(η,η0)
@@ -134,14 +219,42 @@ function caxb!(c::Array{T,2},a::Array{T,2},x::T,b::Array{T,2}) where {T<:Abstrac
134219
@boundscheck (m,n) == size(b) || throw(BoundsError())
135220
@boundscheck (m,n) == size(c) || throw(BoundsError())
136221

137-
#TODO @simd?
138222
@inbounds for j 1:n
139223
for i 1:m
140224
c[i,j] = a[i,j] + x*b[i,j]
141225
end
142226
end
143227
end
144228

229+
"""c equals add x multiplied to a plus b. c = x*(a+b) """
230+
function cxab!(c::Array{T,2},x::Real,a::Array{T,2},b::Array{T,2}) where {T<:AbstractFloat}
231+
m,n = size(a)
232+
@boundscheck (m,n) == size(b) || throw(BoundsError())
233+
@boundscheck (m,n) == size(c) || throw(BoundsError())
234+
235+
xT = T(x)
236+
@inbounds for j 1:n
237+
for i 1:m
238+
c[i,j] = xT*(a[i,j] + b[i,j])
239+
end
240+
end
241+
end
242+
243+
"""c equals add x multiplied to a plus b. c = x*(a+b) """
244+
function cxayb!(c::Array{T,2},x::Real,a::Array{T,2},y::Real,b::Array{T,2}) where {T<:AbstractFloat}
245+
m,n = size(a)
246+
@boundscheck (m,n) == size(b) || throw(BoundsError())
247+
@boundscheck (m,n) == size(c) || throw(BoundsError())
248+
249+
xT = T(x)
250+
yT = T(y)
251+
@inbounds for j 1:n
252+
for i 1:m
253+
c[i,j] = xT*a[i,j] + yT*b[i,j]
254+
end
255+
end
256+
end
257+
145258
"""Convert function for two arrays, X1, X2, in case their eltypes differ.
146259
Convert every element from X1 and store it in X2."""
147260
function Base.convert(X2::Array{T2,N},X1::Array{T1,N}) where {T1,T2,N}

0 commit comments

Comments
 (0)