Skip to content

Commit 458c00e

Browse files
authored
Improve stability of soil model (#1158)
1 parent c916cf5 commit 458c00e

File tree

1 file changed

+49
-18
lines changed

1 file changed

+49
-18
lines changed

src/standalone/Soil/soil_hydrology_parameterizations.jl

Lines changed: 49 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -111,25 +111,41 @@ function update_albedo!(
111111
112112
A pointwise function returning the volumetric liquid fraction
113113
given the augmented liquid fraction and the effective porosity.
114+
The output is guaranteed to be in (θ_r, ν_eff].
115+
116+
For Richards model, ν_eff = ν; and the clipping below is not required,
117+
(and will do nothing; ν_eff_safe = ν_eff), but we leave it in for a
118+
simpler interface.
114119
"""
115120
function volumetric_liquid_fraction(ϑ_l::FT, ν_eff::FT, θ_r::FT) where {FT}
116-
ϑ_l_safe = max(ϑ_l, θ_r + eps(FT))
117-
if ϑ_l_safe < ν_eff
121+
ϑ_l_safe = max(ϑ_l, θ_r + sqrt(eps(FT)))
122+
ν_eff_safe = max(ν_eff, θ_r + sqrt(eps(FT)))
123+
if ϑ_l_safe < ν_eff_safe
118124
θ_l = ϑ_l_safe
119125
else
120-
θ_l = ν_eff
126+
θ_l = ν_eff_safe
121127
end
122128
return θ_l
123129
end
124130

125131
"""
126-
effective_saturation(porosity::FT, ϑ_l::FT, θr::FT) where {FT}
132+
effective_saturation(ν_eff::FT, ϑ_l::FT, θr::FT) where {FT}
133+
134+
A point-wise function computing the effective saturation given the
135+
effective porosity, augmented liquid fraction, and residual water
136+
fraction as input.
127137
128-
A point-wise function computing the effective saturation.
138+
The output is guaranteed to lie in (0, 1].
139+
140+
For Richards model, or any other parameterization where ice is not
141+
relevant, ν_eff = ν; and the clipping below is not required,
142+
(and will do nothing; ν_eff_safe = ν_eff), but we leave it in for a
143+
simpler interface.
129144
"""
130-
function effective_saturation(porosity::FT, ϑ_l::FT, θr::FT) where {FT}
131-
ϑ_l_safe = max(ϑ_l, θr + eps(FT))
132-
S_l = (ϑ_l_safe - θr) / (porosity - θr)
145+
function effective_saturation(ν_eff::FT, ϑ_l::FT, θr::FT) where {FT}
146+
ϑ_l_safe = max(ϑ_l, θr + sqrt(eps(FT)))
147+
ν_eff_safe = max(ν_eff, θr + sqrt(eps(FT)))
148+
S_l = (ϑ_l_safe - θr) / (ν_eff_safe - θr)
133149
return S_l
134150
end
135151

@@ -197,8 +213,9 @@ function pressure_head(
197213
ν_eff::FT,
198214
S_s::FT,
199215
) where {FT}
200-
ϑ_l_safe = max(ϑ_l, θ_r + eps(FT))
201-
S_l_eff = effective_saturation(ν_eff, ϑ_l_safe, θ_r)
216+
# effective saturation clips ν_eff and ϑ_l
217+
# as needed so that S_l ∈ (0,1].
218+
S_l_eff = effective_saturation(ν_eff, ϑ_l, θ_r)
202219
if S_l_eff <= FT(1.0)
203220
ψ = matric_potential(cm, S_l_eff)
204221
else
@@ -208,17 +225,23 @@ function pressure_head(
208225
end
209226

210227
"""
211-
dψdϑ(cm::vanGenuchten{FT}, ϑ, ν, θ_r, S_s)
228+
dψdϑ(cm::vanGenuchten{FT}, ϑ, ν_eff, θ_r, S_s)
212229
213230
Computes and returns the derivative of the pressure head
214231
with respect to ϑ for the van Genuchten formulation.
215232
"""
216-
function dψdϑ(cm::vanGenuchten{FT}, ϑ, ν, θ_r, S_s) where {FT}
217-
S = effective_saturation(ν, ϑ, θ_r)
233+
function dψdϑ(cm::vanGenuchten{FT}, ϑ, ν_eff, θ_r, S_s) where {FT}
234+
# effective saturation clips ν_eff and ϑ
235+
# as needed so that S_l ∈ (0,1],
236+
# but we use ν_eff alone, so we clip that here.
237+
# The second clipping in effective saturation
238+
# will not change it further.
239+
ν_eff_safe = max(ν_eff, θ_r + sqrt(eps(FT)))
240+
S = effective_saturation(ν_eff_safe, ϑ, θ_r)
218241
(; α, m, n) = cm
219242
if S < 1.0
220243
return FT(
221-
1.0 /* m * n) / (ν - θ_r) *
244+
1.0 /* m * n) / (ν_eff_safe - θ_r) *
222245
(S^(-1 / m) - 1)^(1 / n - 1) *
223246
S^(-1 / m - 1),
224247
)
@@ -288,16 +311,22 @@ end
288311

289312

290313
"""
291-
dψdϑ(cm::BrooksCorey{FT}, ϑ, ν, θ_r, S_s)
314+
dψdϑ(cm::BrooksCorey{FT}, ϑ, ν_eff, θ_r, S_s)
292315
293316
Computes and returns the derivative of the pressure head
294317
with respect to ϑ for the Brooks and Corey formulation.
295318
"""
296-
function dψdϑ(cm::BrooksCorey{FT}, ϑ, ν, θ_r, S_s) where {FT}
297-
S = effective_saturation(ν, ϑ, θ_r)
319+
function dψdϑ(cm::BrooksCorey{FT}, ϑ, ν_eff, θ_r, S_s) where {FT}
320+
# effective saturation clips ν_eff and ϑ
321+
# as needed so that S_l ∈ (0,1],
322+
# but we use ν_eff alone, so we clip that here.
323+
# The second clipping in effective saturation
324+
# will not change it further.
325+
ν_eff_safe = max(ν_eff, θ_r + sqrt(eps(FT)))
326+
S = effective_saturation(ν_eff_safe, ϑ, θ_r)
298327
(; ψb, c) = cm
299328
if S < 1.0
300-
return -ψb / (c * (ν - θ_r)) * S^(-(1 + 1 / c))
329+
return -ψb / (c * (ν_eff_safe - θ_r)) * S^(-(1 + 1 / c))
301330
else
302331
return 1 / S_s
303332
end
@@ -347,6 +376,8 @@ function pressure_head(
347376
ν_eff::FT,
348377
S_s::FT,
349378
) where {FT}
379+
# effective saturation clips ν_eff and ϑ_l
380+
# as needed so that S_l ∈ (0,1].
350381
S_l_eff = effective_saturation(ν_eff, ϑ_l, θ_r)
351382
if S_l_eff <= FT(1.0)
352383
ψ = matric_potential(cm, S_l_eff)

0 commit comments

Comments
 (0)