Skip to content

Commit 28bb477

Browse files
committed
s-stage SSPRK3
1 parent 3d6f4c9 commit 28bb477

File tree

5 files changed

+140
-19
lines changed

5 files changed

+140
-19
lines changed

src/Constants.jl

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,31 @@
1+
"""Coefficients for strong stability-preserving Runge-Kutta 3rd order.
2+
From: KETCHESON, LOĆZI, AND PARSANI, 2014. INTERNAL ERROR PROPAGATION IN EXPLICIT RUNGE–KUTTA METHODS,
3+
SIAM J NUMER ANAL 52/5. DOI:10.1137/130936245"""
4+
struct SSPRK3coeff{T<:AbstractFloat}
5+
n::Int
6+
s::Int
7+
kn::Int
8+
mn::Int
9+
Δt_Δn::T
10+
kna::T
11+
knb::T
12+
Δt_Δnc::T
13+
end
14+
15+
"""Generator function for a SSPRK3coeff struct."""
16+
function SSPRK3coeff{T}(P::Parameter,Δt_Δ::T) where T
17+
n = P.RKn
18+
s = n^2
19+
kn = n*(n+1) ÷ 2 + 1
20+
mn = (n-1)*(n-2) ÷ 2 + 1
21+
Δt_Δn = T(Δt_Δ/(n^2-n))
22+
kna = T((n-1)/(2n-1))
23+
knb = T(n/(2n-1))
24+
Δt_Δnc = T(Δt_Δ/(n*(2n-1)))
25+
26+
return SSPRK3coeff{T}(n,s,kn,mn,Δt_Δn,kna,knb,Δt_Δnc)
27+
end
28+
129
struct Constants{T<:AbstractFloat,Tprog<:AbstractFloat}
230

331
# RUNGE-KUTTA COEFFICIENTS 2nd/3rd/4th order including timestep Δt
@@ -6,6 +34,7 @@ struct Constants{T<:AbstractFloat,Tprog<:AbstractFloat}
634
Δt_Δs::Tprog # Δt/(s-1) wher s the number of stages
735
Δt_Δ::Tprog # Δt/Δ - timestep divided by grid spacing
836
Δt_Δ_half::Tprog # 1/2 * Δt/Δ
37+
SSPRK3c::SSPRK3coeff # struct containing all coefficients for SSPRK3
938

1039
# BOUNDARY CONDITIONS
1140
one_minus_α::Tprog # tangential boundary condition for the ghost-point copy
@@ -49,8 +78,12 @@ function Constants{T,Tprog}(P::Parameter,G::Grid) where {T<:AbstractFloat,Tprog<
4978
Δt_Δ = Tprog(G.dtint/G.Δ)
5079
Δt_Δ_half = Tprog(G.dtint/G.Δ/2)
5180

52-
one_minus_α = Tprog(1-P.α) # for the ghost point copy/tangential boundary conditions
53-
g = T(P.g) # gravity - for Bernoulli potential
81+
# coefficients for SSPRK3
82+
SSPRK3c = SSPRK3coeff{Tprog}(P,Δt_Δ)
83+
84+
# BOUNDARY CONDITIONS AND PHYSICS
85+
one_minus_α = Tprog(1-P.α) # for the ghost point copy/tangential boundary conditions
86+
g = T(P.g) # gravity - for Bernoulli potential
5487

5588
# BOTTOM FRICTION COEFFICENTS
5689
# incl grid spacing Δ for non-dimensional gradients
@@ -76,7 +109,7 @@ function Constants{T,Tprog}(P::Parameter,G::Grid) where {T<:AbstractFloat,Tprog<
76109
ωFy = 2π*P.ωFy/24/365.25/3600
77110

78111
return Constants{T,Tprog}( RKaΔt,RKbΔt,Δt_Δs,Δt_Δ,Δt_Δ_half,
79-
one_minus_α,
112+
SSPRK3c,one_minus_α,
80113
g,cD,rD,γ,cSmag,νB,rSST,
81114
jSST,SSTmin,ωFη,ωFx,ωFy)
82115
end

src/DefaultParameters.jl

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

4949
# TIME STEPPING OPTIONS
50-
time_scheme::String="RK" # Runge-Kutta ("RK") or strong-stability preserving RK ("SSPRK2","SSPRK3")
50+
time_scheme::String="RK" # Runge-Kutta ("RK") or strong-stability preserving RK
51+
# "SSPRK2","SSPRK3","4SSPRK3"
5152
RKo::Int=4 # Order of the RK time stepping scheme (2, 3 or 4)
5253
RKs::Int=3 # Number of stages for SSPRK2
54+
RKn::Int=2 # n^2 = s = Number of stages for SSPRK3
5355
cfl::Real=1.0 # CFL number (1.0 recommended for RK4, 0.6 for RK3)
5456
Ndays::Real=10.0 # number of days to integrate for
5557
nstep_diff::Int=1 # diffusive part every nstep_diff time steps.
@@ -120,7 +122,7 @@
120122
@assert topo_width > 0.0 "topo_width has to be >0, $topo_width given."
121123
@assert t_relax > 0.0 "t_relax has to be >0, $t_relax given."
122124
@assert η_refw > 0.0 "η_refw has to be >0, $η_refw given."
123-
@assert time_scheme in ["RK","SSPRK2","SSPRK3"] "Time scheme $time_scheme unsupported."
125+
@assert time_scheme in ["RK","SSPRK2","SSPRK3","4SSPRK3"] "Time scheme $time_scheme unsupported."
124126
@assert RKo in [2,3,4] "RKo has to be 2,3 or 4; $RKo given."
125127
@assert RKs > 1 "RKs has to be >= 2; $RKs given."
126128
@assert Ndays > 0.0 "Ndays has to be >0, $Ndays given."

src/GhostPoints.jl

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ function ghost_points!( u::AbstractMatrix,
177177
end
178178

179179
"""Decide on boundary condition P.bc which ghost point function to execute."""
180-
function ghost_points!( u::AbstractMatrix,
180+
function ghost_points_uv!( u::AbstractMatrix,
181181
v::AbstractMatrix,
182182
S::ModelSetup)
183183

@@ -194,6 +194,20 @@ function ghost_points!( u::AbstractMatrix,
194194
end
195195
end
196196

197+
"""Decide on boundary condition P.bc which ghost point function to execute."""
198+
function ghost_points_η!( η::AbstractMatrix,
199+
S::ModelSetup)
200+
201+
@unpack bc = S.parameters
202+
203+
if bc == "periodic"
204+
@unpack Tcomm = S.parameters
205+
ghost_points_η_periodic!(Tcomm,η)
206+
else
207+
ghost_points_η_nonperiodic!(η)
208+
end
209+
end
210+
197211
"""Decide on boundary condition P.bc which ghost point function to execute."""
198212
function ghost_points_sst!(sst::AbstractMatrix,S::ModelSetup)
199213

src/TimeIntegration.jl

Lines changed: 75 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -81,23 +81,22 @@ function time_integration( Prog::PrognosticVars{Tprog},
8181

8282
for rki = 1:RKs
8383
if rki > 1
84-
# TODO technically the ghost point copy for u1,v1 is redundant as done further down
85-
ghost_points!(u1,v1,η1,S)
84+
ghost_points_η!(η1,S)
8685
end
8786

8887
# type conversion for mixed precision
8988
u1rhs = convert(Diag.PrognosticVarsRHS.u,u1)
9089
v1rhs = convert(Diag.PrognosticVarsRHS.v,v1)
9190
η1rhs = convert(Diag.PrognosticVarsRHS.η,η1)
9291

93-
rhs!(u1rhs,v1rhs,η1rhs,Diag,S,t)
92+
rhs!(u1rhs,v1rhs,η1rhs,Diag,S,t) # momentum only
9493

9594
# the update step
9695
axb!(u1,Δt_Δs,du) # u1 = u1 + Δt/(s-1)*RHS(u1)
9796
axb!(v1,Δt_Δs,dv)
9897

99-
# semi-implicit for continuity equation, use u1,v1 to calcualte dη
100-
ghost_points!(u1,v1,S)
98+
# semi-implicit for continuity equation, use new u1,v1 to calcualte dη
99+
ghost_points_uv!(u1,v1,S)
101100
u1rhs = convert(Diag.PrognosticVarsRHS.u,u1)
102101
v1rhs = convert(Diag.PrognosticVarsRHS.v,v1)
103102
continuity!(u1rhs,v1rhs,η1rhs,Diag,S,t)
@@ -109,8 +108,54 @@ function time_integration( Prog::PrognosticVars{Tprog},
109108
cxayb!(u0,a,u,b,u1)
110109
cxayb!(v0,a,v,b,v1)
111110
cxayb!(η0,a,η,b,η1)
111+
112+
elseif time_scheme == "SSPRK3" # s-stage 3rd order SSPRK
113+
114+
@unpack s,kn,mn,kna,knb,Δt_Δnc,Δt_Δn = S.constants.SSPRK3c
115+
116+
for rki = 1:s # number of stages
117+
if rki > 1
118+
ghost_points_η!(η1,S)
119+
end
120+
121+
# type conversion for mixed precision
122+
u1rhs = convert(Diag.PrognosticVarsRHS.u,u1)
123+
v1rhs = convert(Diag.PrognosticVarsRHS.v,v1)
124+
η1rhs = convert(Diag.PrognosticVarsRHS.η,η1)
125+
126+
rhs!(u1rhs,v1rhs,η1rhs,Diag,S,t)
127+
128+
if rki == kn # special case combining more previous stages
129+
dxaybzc!(u1,kna,u1,knb,u0,Δt_Δnc,du)
130+
dxaybzc!(v1,kna,v1,knb,v0,Δt_Δnc,dv)
131+
else # normal update case
132+
axb!(u1,Δt_Δn,du)
133+
axb!(v1,Δt_Δn,dv)
134+
end
135+
136+
# semi-implicit for continuity equation, use new u1,v1 to calcualte dη
137+
ghost_points_uv!(u1,v1,S)
138+
u1rhs = convert(Diag.PrognosticVarsRHS.u,u1)
139+
v1rhs = convert(Diag.PrognosticVarsRHS.v,v1)
140+
continuity!(u1rhs,v1rhs,η1rhs,Diag,S,t)
141+
142+
if rki == kn
143+
dxaybzc!(η1,kna,η1,knb,η0,Δt_Δnc,dη)
144+
else
145+
axb!(η1,Δt_Δn,dη)
146+
end
147+
148+
# special stage that is needed later for the kn-th stage, store in u0,v0,η0 therefore
149+
# or for the last step, as u0,v0,η0 is used as the last step's result of any RK scheme.
150+
if rki == mn || rki == s
151+
copyto!(u0,u1)
152+
copyto!(v0,v1)
153+
ghost_points_η!(η1,S)
154+
copyto!(η0,η1)
155+
end
156+
end
112157

113-
elseif time_scheme == "SSPRK3" # 4-stage SSPRK3
158+
elseif time_scheme == "4SSPRK3" # 4-stage SSPRK3
114159

115160
for rki = 1:4
116161
if rki > 1
@@ -130,7 +175,7 @@ function time_integration( Prog::PrognosticVars{Tprog},
130175
cxab!(v1,1/2,v1,v0) # same
131176

132177
# semi-implicit for continuity equation, use u1,v1 to calcualte dη
133-
ghost_points!(u1,v1,S)
178+
ghost_points_uv!(u1,v1,S)
134179
u1rhs = convert(Diag.PrognosticVarsRHS.u,u1)
135180
v1rhs = convert(Diag.PrognosticVarsRHS.v,v1)
136181
continuity!(u1rhs,v1rhs,η1rhs,Diag,S,t)
@@ -172,7 +217,7 @@ function time_integration( Prog::PrognosticVars{Tprog},
172217
bottom_drag!(u0rhs,v0rhs,η0rhs,Diag,S)
173218
diffusion!(u0rhs,v0rhs,Diag,S)
174219
add_drag_diff_tendencies!(u0,v0,Diag,S)
175-
ghost_points!(u0,v0,S)
220+
ghost_points_uv!(u0,v0,S)
176221
end
177222

178223
t += dtint
@@ -244,7 +289,7 @@ function cxab!(c::Array{T,2},x::Real,a::Array{T,2},b::Array{T,2}) where {T<:Abst
244289
end
245290
end
246291

247-
"""c equals add x multiplied to a plus b. c = x*(a+b) """
292+
"""c = x*a + y*b"""
248293
function cxayb!(c::Array{T,2},x::Real,a::Array{T,2},y::Real,b::Array{T,2}) where {T<:AbstractFloat}
249294
m,n = size(a)
250295
@boundscheck (m,n) == size(b) || throw(BoundsError())
@@ -259,6 +304,27 @@ function cxayb!(c::Array{T,2},x::Real,a::Array{T,2},y::Real,b::Array{T,2}) where
259304
end
260305
end
261306

307+
"""d = x*a + y*b + z*c"""
308+
function dxaybzc!( d::Array{T,2},
309+
x::Real,a::Array{T,2},
310+
y::Real,b::Array{T,2},
311+
z::Real,c::Array{T,2}) where {T<:AbstractFloat}
312+
m,n = size(a)
313+
@boundscheck (m,n) == size(b) || throw(BoundsError())
314+
@boundscheck (m,n) == size(c) || throw(BoundsError())
315+
@boundscheck (m,n) == size(d) || throw(BoundsError())
316+
317+
xT = T(x) # convert to type T
318+
yT = T(y)
319+
zT = T(z)
320+
321+
@inbounds for j 1:n
322+
for i 1:m
323+
d[i,j] = xT*a[i,j] + yT*b[i,j] + zT*c[i,j]
324+
end
325+
end
326+
end
327+
262328
"""Convert function for two arrays, X1, X2, in case their eltypes differ.
263329
Convert every element from X1 and store it in X2."""
264330
function Base.convert(X2::Array{T2,N},X1::Array{T1,N}) where {T1,T2,N}

test/runtests.jl

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using ShallowWaters
1+
# using ShallowWaters
22
using Test
33

44
@testset "No Forcing" begin
@@ -39,6 +39,12 @@ end
3939
@test all(Prog.u .!= 0.0f0)
4040
end
4141

42+
@testset "RK3 tests" begin
43+
Prog = RunModel(time_scheme="RK",RKo=3,cfl=0.6,nstep_advcor=0)
44+
@test all(abs.(Prog.η) .< 10) # sea surface height shouldn't exceed +-10m
45+
@test all(Prog.u .!= 0.0f0)
46+
end
47+
4248
@testset "SSPRK2 tests" begin
4349
Prog = RunModel(time_scheme="SSPRK2",RKs=2,cfl=0.7,nstep_advcor=1)
4450
@test all(abs.(Prog.η) .< 10) # sea surface height shouldn't exceed +-10m
@@ -60,17 +66,17 @@ end
6066
@test all(abs.(Prog.η) .< 10) # sea surface height shouldn't exceed +-10m
6167
@test all(Prog.u .!= 0.0f0)
6268

63-
Prog = RunModel(time_scheme="SSPRK2",RKs=4,cfl=1.2,nstep_advcor=0)
69+
Prog = RunModel(time_scheme="SSPRK2",RKs=4,cfl=1.4,nstep_advcor=0)
6470
@test all(abs.(Prog.η) .< 10) # sea surface height shouldn't exceed +-10m
6571
@test all(Prog.u .!= 0.0f0)
6672
end
6773

6874
@testset "SSPRK3 tests" begin
69-
Prog = RunModel(time_scheme="SSPRK3",cfl=1.2,nstep_advcor=1)
75+
Prog = RunModel(time_scheme="SSPRK3",RKn=2,cfl=1.2,nstep_advcor=1)
7076
@test all(abs.(Prog.η) .< 10) # sea surface height shouldn't exceed +-10m
7177
@test all(Prog.u .!= 0.0f0)
7278

73-
Prog = RunModel(time_scheme="SSPRK3",cfl=1.2,nstep_advcor=0)
79+
Prog = RunModel(time_scheme="SSPRK3",RKn=3,cfl=2.5,nstep_advcor=1)
7480
@test all(abs.(Prog.η) .< 10) # sea surface height shouldn't exceed +-10m
7581
@test all(Prog.u .!= 0.0f0)
7682
end

0 commit comments

Comments
 (0)