Skip to content

Commit b956dc6

Browse files
committed
Add analytical derivatives for SWRCs
1 parent 203e4c1 commit b956dc6

File tree

4 files changed

+56
-4
lines changed

4 files changed

+56
-4
lines changed

Project.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ ForwardDiff = "0.10, 1"
2323
IfElse = "0.1"
2424
Interpolations = "0.14, 0.15, 0.16"
2525
IntervalSets = "0.7"
26+
QuadGK = "2"
2627
NLsolve = "4"
2728
NonlinearSolve = "1.1, 2, 3, 4"
2829
RecipesBase = "1"
@@ -36,9 +37,10 @@ julia = "1.6"
3637
[extras]
3738
BenchmarkTools = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf"
3839
NonlinearSolve = "8913a72c-1f9b-4ce2-8d82-65094dcecaec"
40+
QuadGK = "1fd47b50-473d-5c70-9696-f719f8f3bcdc"
3941
Optim = "429524aa-4258-5aef-a3af-852621145aeb"
4042
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
4143
Turing = "fce5fe82-541a-59a6-adf8-730c64b5f9a0"
4244

4345
[targets]
44-
test = ["Test", "BenchmarkTools", "NonlinearSolve", "Optim", "Turing"]
46+
test = ["Test", "BenchmarkTools", "NonlinearSolve", "QuadGK", "Optim", "Turing"]

src/swrc.jl

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,17 @@ abstract type SWRC end
1818
"""
1919
Base.inv(f::SWRC)
2020
21-
Returns a function `(args...) -> f(inv, args...)` which, when implemented, evaluates the inverse of the
22-
soil water retention curve.
21+
Returns a function that evaluates the inverse of the soil water retention curve, `f`.
2322
"""
2423
Base.inv(f::SWRC) = (args...; kwargs...) -> f(inv, args...; kwargs...)
2524

25+
"""
26+
derivative(f::SWRC)
27+
28+
Returns a function that evaluates the derivative `∂f∂ψ` of the soil water retention curve, `f`.
29+
"""
30+
derivative(f::SWRC) = (args...; kwargs...) -> f(derivative, args..., kwargs...)
31+
2632
"""
2733
SoilWaterVolume{Tρw,Tθres,Tθsat}
2834
@@ -78,9 +84,10 @@ function (f::VanGenuchten)(ψ; θsat=f.vol.θsat, θres=f.vol.θres, α=f.α, n=
7884
let m = 1-1/n,
7985
x = 1 + (-α*ψ)^n;
8086
# @assert isnan(x) || x > zero(x) "van Genuchten violated constraint $x > 0 with input $ψ and parameters θsat=$θsat, θres=$θres, α=$α, n=$n"
81-
IfElse.ifelse<= zero(ψ), θres + (θsat - θres)*x^(-m), θsat*one(ψ))
87+
IfElse.ifelse< zero(ψ), θres + (θsat - θres)*x^(-m), θsat*one(ψ))
8288
end
8389
end
90+
8491
function (f::VanGenuchten)(
8592
::typeof(inv),
8693
θ;
@@ -94,6 +101,21 @@ function (f::VanGenuchten)(
94101
end
95102
end
96103

104+
function (f::VanGenuchten)(
105+
::typeof(derivative),
106+
ψ;
107+
θsat=f.vol.θsat,
108+
θres=f.vol.θres,
109+
α=f.α,
110+
n=f.n
111+
)
112+
let m = 1-1/n,
113+
x = 1 + (-α*ψ)^n;
114+
# @assert isnan(x) || x > zero(x) "van Genuchten violated constraint $x > 0 with input $ψ and parameters θsat=$θsat, θres=$θres, α=$α, n=$n"
115+
IfElse.ifelse< zero(ψ), α*n*m*(θsat - θres)*x^(-m-1)*(-α*ψ)^(n-1), 0)
116+
end
117+
end
118+
97119
inflectionpoint(f::VanGenuchten; α=f.α, n=f.n) = -1/α*((n - 1) / n)^(1/n)
98120

99121
# Preset Van Genucthen curves from soil types;
@@ -121,6 +143,7 @@ Base.@kwdef struct BrooksCorey{Tvol,Tψₛ,Tλ} <: SWRC
121143
ψₛ::Tψₛ = 0.01u"m"
122144
λ::Tλ = 0.2 # domain: (0,Inf)
123145
end
146+
124147
function (f::BrooksCorey)(
125148
ψ;
126149
θsat=f.vol.θsat,
@@ -130,6 +153,7 @@ function (f::BrooksCorey)(
130153
)
131154
IfElse.ifelse< -ψₛ, θres + (θsat - θres)*(-ψₛ / ψ)^λ, θsat*one(ψ))
132155
end
156+
133157
function (f::BrooksCorey)(
134158
::typeof(inv),
135159
θ;
@@ -140,4 +164,16 @@ function (f::BrooksCorey)(
140164
)
141165
IfElse.ifelse< θsat, -ψₛ*((θ - θres)/(θsat - θres))^(-1/λ), -ψₛ*one(θ))
142166
end
167+
168+
function (f::BrooksCorey)(
169+
::typeof(derivative),
170+
ψ;
171+
θsat=f.vol.θsat,
172+
θres=f.vol.θres,
173+
ψₛ=f.ψₛ,
174+
λ=f.λ
175+
)
176+
IfElse.ifelse< -ψₛ, (θsat - θres)*λ*(-ψₛ / ψ)^-1)*(ψₛ*ψ^-2), θsat*one(ψ))
177+
end
178+
143179
inflectionpoint(f::BrooksCorey; ψₛ=f.ψₛ) = ψₛ

test/env/Project.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210"
44
FreezeCurves = "71e4ad71-e4f2-45a3-aa0a-91ffaa9676be"
55
NonlinearSolve = "8913a72c-1f9b-4ce2-8d82-65094dcecaec"
66
Optim = "429524aa-4258-5aef-a3af-852621145aeb"
7+
QuadGK = "1fd47b50-473d-5c70-9696-f719f8f3bcdc"
78
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
89
Turing = "fce5fe82-541a-59a6-adf8-730c64b5f9a0"
910
Unitful = "1986cc42-f94f-5a68-af5c-568840ba703d"

test/swrc_tests.jl

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using FreezeCurves
2+
using QuadGK
23
using Test
34
using Unitful
45

@@ -16,6 +17,12 @@ using Unitful
1617
# check inverse
1718
@test isapprox(Base.inv(f)(θw; θsat, θres, α, n), -0.1u"m", atol=1e-6u"m")
1819
@test isapprox(Base.inv(f)(θsat; θsat, θres, α, n), 0.0u"m", atol=1e-6u"m")
20+
# check derivative using fundamental theorem of calculus
21+
a = -10.0u"m"
22+
b = -0.01u"m"
23+
∂f∂ψ = FreezeCurves.derivative(f)
24+
f̃, _ = quadgk(∂f∂ψ, a, b)
25+
@test f(b) - f(a)
1926
end
2027
end
2128
@testset "VanGenuchten (units)" begin
@@ -37,6 +44,12 @@ using Unitful
3744
# check inverse
3845
@test isapprox(Base.inv(f)(θw; θsat, θres, ψₛ, λ), -0.5u"m", atol=1e-6u"m")
3946
@test isapprox(Base.inv(f)(θsat; θsat, θres, ψₛ, λ), -ψₛ, atol=1e-6u"m")
47+
# check derivative using fundamental theorem of calculus
48+
a = -10.0u"m"
49+
b = -0.01u"m"
50+
∂f∂ψ = FreezeCurves.derivative(f)
51+
f̃, _ = quadgk(∂f∂ψ, a, b)
52+
@test f(b) - f(a)
4053
end
4154
end
4255
end

0 commit comments

Comments
 (0)