Skip to content

Commit 8f98486

Browse files
committed
Add centered fintie volume (no upwind) advection scheme
1 parent 360c701 commit 8f98486

File tree

1 file changed

+55
-40
lines changed

1 file changed

+55
-40
lines changed

src/matrixbuilding.jl

Lines changed: 55 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -25,15 +25,15 @@ function makeindices(v3D)
2525
end
2626

2727
"""
28-
buildTadv(; ϕ, gridmetrics, indices, ρ)
28+
buildTadv(; ϕ, gridmetrics, indices, ρ, upwind = true)
2929
3030
Build the advection operator Tadv.
3131
"""
32-
function buildTadv(; ϕ, gridmetrics, indices, ρ)
32+
function buildTadv(; ϕ, gridmetrics, indices, ρ, upwind = true)
3333
# default ρ = 1035 kg/m^3 is the value originally used by Chamberlain et al. (2019)
3434

3535
@debug "Building Tadv"
36-
𝑖s, 𝑗s, Tvals = upwind_advection_operator_sparse_entries(ϕ, gridmetrics, indices, ρ)
36+
𝑖s, 𝑗s, Tvals = advection_operator_sparse_entries(ϕ, gridmetrics, indices, ρ; upwind)
3737

3838
N = indices.N
3939

@@ -105,8 +105,9 @@ function buildTκVdeep(; mlotst, gridmetrics, indices, κVdeep)
105105

106106
N = indices.N
107107

108-
# Deep mask for vertical diffusivity
109-
Ω = trues(N) # TODO (maybe): make Ωdeep not overlap with ΩML at MLD?
108+
# κVdeep is a bit of a misnomer: It should be κVBG for "background".
109+
# And its mask is entire ocean, naturally.
110+
Ω = trues(N)
110111

111112
@debug "Building TκVdeep"
112113
𝑖s, 𝑗s, Tvals = vertical_diffusion_operator_sparse_entries(; gridmetrics, indices, κV = κVdeep, Ω)
@@ -120,7 +121,7 @@ function buildTκVdeep(; mlotst, gridmetrics, indices, κVdeep)
120121
end
121122

122123
"""
123-
transportmatrix(; ϕ, mlotst, gridmetrics, indices, ρ, κH, κVML, κVdeep, Tadv, TκH, TκVML, TκVdeep)
124+
transportmatrix(; ϕ, mlotst, gridmetrics, indices, ρ, κH, κVML, κVdeep, Tadv, TκH, TκVML, TκVdeep, upwind)
124125
125126
Build the transport matrix, i.e., the flux-divergence operator T = Tadv + TκH + TκVML + TκVdeep,
126127
and check divergence and mass conservation.
@@ -133,9 +134,10 @@ function transportmatrix(; ϕ, mlotst, gridmetrics, indices, ρ,
133134
TκH = nothing,
134135
TκVML = nothing,
135136
TκVdeep = nothing,
137+
upwind = true,
136138
)
137139

138-
isnothing(Tadv) && (Tadv = buildTadv(; ϕ, gridmetrics, indices, ρ))
140+
isnothing(Tadv) && (Tadv = buildTadv(; ϕ, gridmetrics, indices, ρ, upwind))
139141
isnothing(TκH) && (TκH = buildTκH(; gridmetrics, indices, ρ, κH))
140142
isnothing(TκVML) && (TκVML = buildTκVML(; mlotst, gridmetrics, indices, κVML))
141143
isnothing(TκVdeep) && (TκVdeep = buildTκVdeep(; mlotst, gridmetrics, indices, κVdeep))
@@ -173,6 +175,36 @@ end
173175
# T[i,j] = -ϕ[j→i] / m[i] units = kg s⁻¹ / kg = s⁻¹
174176
# and ϕ[j→i] / m[j] should be added to the diagonal T[j,j].
175177

178+
# For a centered scheme, where the concentration on the cell face is used,
179+
# I still have the matrix operating as
180+
# ∂χ[i] = -T[i,j] * χ[j] units: 1/s * mol/kg (or mol/m^3)
181+
# But the centered mass transfer is (regardless of the sign of ϕ)
182+
# ∂χ[i] = ϕ[j→i] * (χ[j] + χ[i]) / 2m[i] units kg[j]/s * mol/kg[j] / kg[i] = mol/kg[i]
183+
# and should also incur the opposite mass tendency at j:
184+
# ∂χ[j] = -ϕ[j→i] * (χ[j] + χ[i]) / 2m[j] units kg[j]/s * mol/kg[j] / kg[j] = mol/kg[j]
185+
# Thus the matrix term should be constructed as
186+
# T[i,j] = -ϕ[j→i] / 2m[i] units = kg s⁻¹ / kg = s⁻¹
187+
# and ϕ[j→i] / 2m[j] should be added to the diagonal T[j,j].
188+
# So essentially just divide ϕ by 2 compared to upwind, but don't branch on the sign of ϕ.
189+
190+
"""
191+
pushTadvectionvalues!(𝑖s, 𝑗s, Tvals, 𝑖, 𝑗, ϕ, ρ𝑖, ρ𝑗, v𝑖, v𝑗)
192+
193+
Pushes the sparse indices and values into (𝑖s, 𝑗s, Tvals) corresponding to the j→i advection.
194+
"""
195+
function pushTadvectionvalues!(𝑖s, 𝑗s, Tvals, 𝑖, 𝑗, ϕ, ρ𝑖, ρ𝑗, v𝑖, v𝑗)
196+
ρ = (ρ𝑖 + ρ𝑗) / 2
197+
m𝑖 = ρ * v𝑖
198+
m𝑗 = ρ * v𝑗
199+
push!(𝑖s, 𝑖)
200+
push!(𝑗s, 𝑗)
201+
push!(Tvals, -ϕ / m𝑖)
202+
push!(𝑖s, 𝑗)
203+
push!(𝑗s, 𝑗)
204+
push!(Tvals, ϕ / m𝑗)
205+
end
206+
207+
176208
# Is this mass conserving?
177209
# Only if
178210
# v[i] * T[i,i] + v[j] * T[j,i] ≈ 0
@@ -183,16 +215,16 @@ end
183215
# So I must use the mean density between facing cells.
184216

185217
"""
186-
upwind_advection_operator_sparse_entries(ϕ, gridmetrics, indices, ρ)
218+
advection_operator_sparse_entries(ϕ, gridmetrics, indices, ρ; upwind = true)
187219
188220
Return the sparse (i, j, v) for the upwind advection operator Tadv.
189221
"""
190-
function upwind_advection_operator_sparse_entries(ϕ, gridmetrics, indices, ρ::Number)
222+
function advection_operator_sparse_entries(ϕ, gridmetrics, indices, ρ::Number; upwind = true)
191223
# If ρ is a scalar, broadcast it to the gridmetrics size
192224
ρ = fill(ρ, size(gridmetrics.v3D))
193-
return upwind_advection_operator_sparse_entries(ϕ, gridmetrics, indices, ρ)
225+
return advection_operator_sparse_entries(ϕ, gridmetrics, indices, ρ; upwind)
194226
end
195-
function upwind_advection_operator_sparse_entries(ϕ, gridmetrics, indices, ρ)
227+
function advection_operator_sparse_entries(ϕ, gridmetrics, indices, ρ; upwind = true)
196228

197229
# Unpack model grid
198230
(; v3D, gridtopology) = gridmetrics
@@ -210,53 +242,53 @@ function upwind_advection_operator_sparse_entries(ϕ, gridmetrics, indices, ρ)
210242
v𝑖 = v3D[C𝑖]
211243
ρ𝑖 = ρ[C𝑖]
212244
# From West
213-
ϕwest = ϕ.west[C𝑖]
214-
if ϕwest > 0
245+
ϕwest = upwind ? max(ϕ.west[C𝑖], 0) : ϕ.west[C𝑖] / 2
246+
if (ϕwest > 0) || (ϕwest < 0)
215247
C𝑗 = i₋₁(C𝑖, gridtopology)
216248
𝑗 = Lwet3D[C𝑗]
217249
v𝑗 = v3D[C𝑗]
218250
ρ𝑗 = ρ[C𝑗]
219251
pushTadvectionvalues!(𝑖s, 𝑗s, Tvals, 𝑖, 𝑗, ϕwest, ρ𝑖, ρ𝑗, v𝑖, v𝑗)
220252
end
221253
# From East
222-
ϕeast = ϕ.east[C𝑖]
223-
if ϕeast < 0
254+
ϕeast = upwind ? min(ϕ.east[C𝑖], 0) : ϕ.east[C𝑖] / 2
255+
if (ϕeast > 0) || (ϕeast < 0)
224256
C𝑗 = i₊₁(C𝑖, gridtopology)
225257
𝑗 = Lwet3D[C𝑗]
226258
v𝑗 = v3D[C𝑗]
227259
ρ𝑗 = ρ[C𝑗]
228260
pushTadvectionvalues!(𝑖s, 𝑗s, Tvals, 𝑖, 𝑗, -ϕeast, ρ𝑖, ρ𝑗, v𝑖, v𝑗)
229261
end
230262
# From South
231-
ϕsouth = ϕ.south[C𝑖]
232-
if ϕsouth > 0
263+
ϕsouth = upwind ? max(ϕ.south[C𝑖], 0) : ϕ.south[C𝑖] / 2
264+
if (ϕsouth > 0) || (ϕsouth < 0)
233265
C𝑗 = j₋₁(C𝑖, gridtopology)
234266
𝑗 = Lwet3D[C𝑗]
235267
v𝑗 = v3D[C𝑗]
236268
ρ𝑗 = ρ[C𝑗]
237269
pushTadvectionvalues!(𝑖s, 𝑗s, Tvals, 𝑖, 𝑗, ϕsouth, ρ𝑖, ρ𝑗, v𝑖, v𝑗)
238270
end
239271
# From North (Special case with north bipole)
240-
ϕnorth = ϕ.north[C𝑖]
241-
if ϕnorth < 0
272+
ϕnorth = upwind ? min(ϕ.north[C𝑖], 0) : ϕ.north[C𝑖] / 2
273+
if (ϕnorth > 0) || (ϕnorth < 0)
242274
C𝑗 = j₊₁(C𝑖, gridtopology)
243275
𝑗 = Lwet3D[C𝑗]
244276
v𝑗 = v3D[C𝑗]
245277
ρ𝑗 = ρ[C𝑗]
246278
pushTadvectionvalues!(𝑖s, 𝑗s, Tvals, 𝑖, 𝑗, -ϕnorth, ρ𝑖, ρ𝑗, v𝑖, v𝑗)
247279
end
248280
# From Bottom
249-
ϕbottom = ϕ.bottom[C𝑖]
250-
if ϕbottom > 0
281+
ϕbottom = upwind ? max(ϕ.bottom[C𝑖], 0) : ϕ.bottom[C𝑖] / 2
282+
if (ϕbottom > 0) || (ϕbottom < 0)
251283
C𝑗 = k₊₁(C𝑖, gridtopology)
252284
𝑗 = Lwet3D[C𝑗]
253285
v𝑗 = v3D[C𝑗]
254286
ρ𝑗 = ρ[C𝑗]
255287
pushTadvectionvalues!(𝑖s, 𝑗s, Tvals, 𝑖, 𝑗, ϕbottom, ρ𝑖, ρ𝑗, v𝑖, v𝑗)
256288
end
257289
# From Top
258-
ϕtop = ϕ.top[C𝑖]
259-
if ϕtop < 0 && k > 1 # Evaporation/precipitation -> no change to χ
290+
ϕtop = upwind ? min(ϕ.top[C𝑖], 0) : ϕ.top[C𝑖] / 2
291+
if (k > 1) && ((ϕtop > 0) || (ϕtop < 0)) # Evaporation/precipitation -> no change to χ
260292
C𝑗 = k₋₁(C𝑖, gridtopology)
261293
𝑗 = Lwet3D[C𝑗]
262294
v𝑗 = v3D[C𝑗]
@@ -268,23 +300,6 @@ function upwind_advection_operator_sparse_entries(ϕ, gridmetrics, indices, ρ)
268300
end
269301

270302

271-
"""
272-
pushTadvectionvalues!(𝑖s, 𝑗s, Tvals, 𝑖, 𝑗, ϕ, ρ𝑖, ρ𝑗, v𝑖, v𝑗)
273-
274-
Pushes the sparse indices and values into (𝑖s, 𝑗s, Tvals) corresponding to the j→i advection.
275-
"""
276-
function pushTadvectionvalues!(𝑖s, 𝑗s, Tvals, 𝑖, 𝑗, ϕ, ρ𝑖, ρ𝑗, v𝑖, v𝑗)
277-
ρ = (ρ𝑖 + ρ𝑗) / 2
278-
m𝑖 = ρ * v𝑖
279-
m𝑗 = ρ * v𝑗
280-
push!(𝑖s, 𝑖)
281-
push!(𝑗s, 𝑗)
282-
push!(Tvals, -ϕ / m𝑖)
283-
push!(𝑖s, 𝑗)
284-
push!(𝑗s, 𝑗)
285-
push!(Tvals, ϕ / m𝑗)
286-
end
287-
288303

289304

290305
# Following Chamberlain et al. (2019)

0 commit comments

Comments
 (0)