@@ -25,15 +25,15 @@ function makeindices(v3D)
2525end
2626
2727"""
28- buildTadv(; ϕ, gridmetrics, indices, ρ)
28+ buildTadv(; ϕ, gridmetrics, indices, ρ, upwind = true )
2929
3030Build 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)
120121end
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
125126Build the transport matrix, i.e., the flux-divergence operator T = Tadv + TκH + TκVML + TκVdeep,
126127and 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))
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
188220Return 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 )
194226end
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, ρ)
268300end
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