Skip to content

Commit fd4fb3c

Browse files
Merge remote-tracking branch 'origin/master' into jacobian-reuse-rosenbrock-w-methods
2 parents 0b0ded4 + b42e41a commit fd4fb3c

File tree

15 files changed

+805
-16
lines changed

15 files changed

+805
-16
lines changed

.github/workflows/SublibraryCI.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ jobs:
3333
CHANGED=$(git diff --name-only ${{ github.event.before }} ${{ github.sha }} 2>/dev/null || git diff --name-only HEAD~1 HEAD)
3434
else
3535
# For pull requests, compare against the base branch
36-
git fetch origin ${{ github.event.pull_request.base.ref }} --depth=1
36+
git fetch origin ${{ github.event.pull_request.base.ref }}
3737
CHANGED=$(git diff --name-only origin/${{ github.event.pull_request.base.ref }}...HEAD)
3838
fi
3939
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
[deps]
2+
ADTypes = "47edcb42-4c32-4615-8424-f2b9edc5f35b"
3+
ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210"
4+
ModelingToolkit = "961ee093-0014-501f-94e3-6117800e7a78"
5+
OrdinaryDiffEq = "1dea7af3-3e70-54e6-95c3-0bf5283fa5ed"
6+
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
7+
8+
[sources]
9+
OrdinaryDiffEq = {path = "../../../.."}
10+
11+
[compat]
12+
ADTypes = "1.16"
13+
ForwardDiff = "0.10, 1"
14+
ModelingToolkit = "10.10, 11"
15+
OrdinaryDiffEq = "6"
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
using OrdinaryDiffEq, ForwardDiff, Test, ADTypes
2+
3+
function d_alembert(du, u, p, t)
4+
return du[1] = p[1] - p[2] * u[1] + p[3] * t
5+
end
6+
7+
function d_alembert_jac(J, u, p, t)
8+
return J[1] = -p[2]
9+
end
10+
11+
function d_alembert_analytic(u0, p, t::Number)
12+
a, b, c = p
13+
ebt = exp(b * t)
14+
return @. exp(-b * t) * (-a * b + c + ebt * (a * b + c * (b * t - 1)) + b^2 * u0) / (b^2)
15+
end
16+
17+
p = (1.0, 2.0, 3.0)
18+
u0 = [1.0]
19+
tspan = (0.0, 10.0)
20+
prob = ODEProblem(
21+
ODEFunction(
22+
d_alembert,
23+
jac = d_alembert_jac,
24+
analytic = d_alembert_analytic
25+
),
26+
u0, tspan, p
27+
)
28+
29+
sol = solve(prob, Tsit5(), abstol = 1.0e-10, reltol = 1.0e-10)
30+
@test sol.errors[:l2] < 1.0e-7
31+
sol = solve(prob, Rosenbrock23(), abstol = 1.0e-8, reltol = 1.0e-8)
32+
@test sol.errors[:l2] < 1.0e-7
33+
sol = solve(prob, Rodas4(), abstol = 1.0e-10, reltol = 1.0e-10)
34+
@test sol.errors[:l2] < 1.0e-7
35+
sol = solve(prob, Veldd4(), abstol = 1.0e-10, reltol = 1.0e-10)
36+
@test sol.errors[:l2] < 1.0e-7
37+
sol = solve(prob, Rodas5(), abstol = 1.0e-10, reltol = 1.0e-10)
38+
@test sol.errors[:l2] < 1.0e-7
39+
sol = solve(prob, TRBDF2(), abstol = 1.0e-10, reltol = 1.0e-10)
40+
@test sol.errors[:l2] < 2.0e-6
41+
sol = solve(prob, Trapezoid(), abstol = 1.0e-10, reltol = 1.0e-10)
42+
@test sol.errors[:l2] < 2.0e-6
43+
sol = solve(prob, KenCarp3(), abstol = 1.0e-10, reltol = 1.0e-10)
44+
@test sol.errors[:l2] < 8.0e-4
45+
sol = solve(prob, KenCarp4(), abstol = 1.0e-10, reltol = 1.0e-10)
46+
@test sol.errors[:l2] < 1.0e-7
47+
sol = solve(prob, KenCarp47(), abstol = 1.0e-10, reltol = 1.0e-10)
48+
@test sol.errors[:l2] < 1.0e-7
49+
sol = solve(prob, KenCarp58(), abstol = 1.0e-10, reltol = 1.0e-10)
50+
@test sol.errors[:l2] < 1.0e-7
51+
52+
using ModelingToolkit
53+
function lotka(du, u, p, t)
54+
x = u[1]
55+
y = u[2]
56+
du[1] = p[1] * x - p[2] * x * y
57+
return du[2] = -p[3] * y + p[4] * x * y
58+
end
59+
60+
prob = ODEProblem(lotka, [1.0, 1.0], (0.0, 1.0), [1.5, 1.0, 3.0, 1.0])
61+
de = ModelingToolkit.modelingtoolkitize(prob) |> complete
62+
prob2 = ODEProblem(de, [], prob.tspan; jac = true)
63+
64+
sol = solve(prob, TRBDF2())
65+
66+
for Alg in [Rodas5, Rosenbrock23, TRBDF2, KenCarp4]
67+
@test Array(
68+
solve(
69+
prob2,
70+
Alg(),
71+
tstops = sol.t,
72+
adaptive = false
73+
)
74+
) Array(
75+
solve(
76+
prob,
77+
Alg(),
78+
tstops = sol.t,
79+
adaptive = false
80+
)
81+
) atol = 1.0e-4
82+
end
83+
84+
## check chunk_size handling in ForwardDiff Jacobians
85+
const chunksize = 1
86+
function rober(du, u, p, t)
87+
y₁, y₂, y₃ = u
88+
k₁, k₂, k₃, check = p
89+
if check && eltype(u) <: ForwardDiff.Dual && ForwardDiff.npartials(u[1]) != chunksize
90+
@show ForwardDiff.npartials(u[1]), chunksize
91+
error("chunk_size is not as specified")
92+
end
93+
94+
du[1] = -k₁ * y₁ + k₃ * y₂ * y₃
95+
du[2] = k₁ * y₁ - k₂ * y₂^2 - k₃ * y₂ * y₃
96+
du[3] = k₂ * y₂^2
97+
return nothing
98+
end
99+
prob1 = ODEProblem(rober, [1.0, 0.0, 0.0], (0.0, 1.0e5), (0.04, 3.0e7, 1.0e4, true))
100+
sol1 = solve(prob1, TRBDF2(autodiff = AutoForwardDiff(chunksize = chunksize)))
101+
prob = ODEProblem(rober, [1.0, 0.0, 0.0], (0.0, 1.0e5), (0.04, 3.0e7, 1.0e4, false))
102+
sol = solve(prob, TRBDF2())
103+
@test sol.u[end] == sol1.u[end]
104+
@test length(sol.t) == length(sol1.t)
Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,42 @@
11
using SafeTestsets
2+
using Pkg
23

34
const TEST_GROUP = get(ENV, "ODEDIFFEQ_TEST_GROUP", "ALL")
45

6+
function activate_sparse_env()
7+
Pkg.activate(joinpath(@__DIR__, "sparse"))
8+
Pkg.develop(PackageSpec(path = dirname(dirname(dirname(@__DIR__)))))
9+
return Pkg.instantiate()
10+
end
11+
12+
function activate_modelingtoolkit_env()
13+
Pkg.activate(joinpath(@__DIR__, "modelingtoolkit"))
14+
Pkg.develop(PackageSpec(path = dirname(dirname(dirname(@__DIR__)))))
15+
return Pkg.instantiate()
16+
end
17+
518
# Run QA tests (JET, Aqua)
6-
if TEST_GROUP != "Core" && isempty(VERSION.prerelease)
19+
if TEST_GROUP ("Core", "Sparse", "ModelingToolkit") && isempty(VERSION.prerelease)
720
@time @safetestset "JET Tests" include("jet.jl")
821
@time @safetestset "Aqua" include("qa.jl")
922
end
1023

1124
# Run functional tests
12-
if TEST_GROUP != "QA"
25+
if TEST_GROUP ("QA", "Sparse", "ModelingToolkit")
1326
@time @safetestset "OOP J_t Tracking" include("oop_jt_tracking_test.jl")
1427
@time @safetestset "Differentiation Trait Tests" include("differentiation_traits_tests.jl")
1528
@time @safetestset "Autodiff Error Tests" include("autodiff_error_tests.jl")
1629
@time @safetestset "No Jac Tests" include("nojac_tests.jl")
1730
end
31+
32+
# Run sparse tests (separate environment due to ComponentArrays dep conflicts)
33+
if TEST_GROUP == "Sparse" || TEST_GROUP == "ALL"
34+
activate_sparse_env()
35+
@time @safetestset "Non-Full Diagonal Sparsity Tests" include("sparse/nonfulldiagonal_sparse_tests.jl")
36+
end
37+
38+
# Run ModelingToolkit tests (separate environment due to heavy MTK dependency)
39+
if TEST_GROUP == "ModelingToolkit" && isempty(VERSION.prerelease)
40+
activate_modelingtoolkit_env()
41+
@time @safetestset "Jacobian Tests" include("modelingtoolkit/jacobian_tests.jl")
42+
end
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
[deps]
2+
ComponentArrays = "b0b7db55-cfe3-40fc-9ded-d10e2dbeff66"
3+
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
4+
LinearSolve = "7ed4a6bd-45f5-4d41-b270-4a48e9bafcae"
5+
OrdinaryDiffEq = "1dea7af3-3e70-54e6-95c3-0bf5283fa5ed"
6+
SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"
7+
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
8+
9+
[sources]
10+
OrdinaryDiffEq = {path = "../../../.."}
11+
12+
[compat]
13+
ComponentArrays = "0.15, 1"
14+
LinearSolve = "3"
15+
OrdinaryDiffEq = "6"
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
using OrdinaryDiffEq, SparseArrays, LinearSolve, LinearAlgebra
2+
using ComponentArrays
3+
4+
function enclosethetimedifferential(parameters::NamedTuple)::Function
5+
@info "Enclosing the time differential"
6+
7+
(; Δr, r_space, countorderapprox) = parameters.compute
8+
N = length(r_space)
9+
10+
function first_deriv(N)
11+
dx = 1 / (N + 1)
12+
du = -1 * ones(N - 1) # off diagonal
13+
du2 = ones(N - 1) # off diagonal
14+
diag = zeros(N)
15+
lower = spzeros(Float64, N)
16+
upper = spzeros(Float64, N)
17+
lower[1] = -1.0
18+
upper[end] = 1.0
19+
M = hcat(lower, sparse(diagm(-1 => du, 0 => diag, 1 => du2)), upper)
20+
return MatrixOperator(1 / dx * M)
21+
end
22+
23+
function second_deriv(N)
24+
dx = 1 / (N + 1)
25+
du = ones(N - 1) # off diagonal
26+
du2 = ones(N - 1) # off diagonal
27+
diag = -2 * ones(N)
28+
lower = spzeros(Float64, N)
29+
upper = spzeros(Float64, N)
30+
lower[1] = 1.0
31+
upper[end] = 1.0
32+
M = hcat(lower, sparse(diagm(-1 => du, 0 => diag, 1 => du2)), upper)
33+
return MatrixOperator(1 / dx^2 * M)
34+
end
35+
36+
function extender(N)
37+
dx = 1 / (N + 1)
38+
diag = ones(N)
39+
lower = spzeros(Float64, N)
40+
upper = spzeros(Float64, N)
41+
lower[1] = 1.0
42+
upper[end] = 1.0
43+
M = vcat(
44+
transpose(lower),
45+
sparse(diagm(diag)),
46+
transpose(upper)
47+
)
48+
return MatrixOperator(1 / dx^2 * M)
49+
end
50+
51+
bc_handler = extender(N)
52+
53+
= first_deriv(N) * bc_handler
54+
Δ = second_deriv(N) * bc_handler
55+
56+
bc_x = zeros(Real, N)
57+
bc_xx = zeros(Real, N)
58+
59+
function timedifferentialclosure!(du, u, p, t)
60+
(;
61+
α, D, v, k_p, V_c, Q_l, Q_r, V_b,
62+
S, Lm, Dm, V_v,
63+
) = p
64+
65+
c = u[1:(end - 3)]
66+
c_v = u[end - 2]
67+
c_c = u[end - 1]
68+
c_b = u[end]
69+
70+
J_B0 = (Dm / Lm) ** c_v - c[1])
71+
J_BL = (Dm / Lm) * (c[end] - α * c_c)
72+
grad_0 = (v ./ D) .* c[1] .- J_B0 ./ D
73+
grad_L = (v ./ D) .* c[end] .- J_BL ./ D
74+
75+
bc_x[1] = grad_0 / 2
76+
bc_x[end] = grad_L / 2
77+
grad_c =* c + bc_x
78+
79+
bc_xx[1] = -grad_0 / Δr
80+
bc_xx[end] = grad_L / Δr
81+
Lap_c = Δ * c + bc_xx
82+
83+
C = sum(Δr .* S * (k_p * (c .- c_b)))
84+
85+
dc_dt = D * Lap_c - v * grad_c .- k_p * (c .- c_b)
86+
du[1:(end - 3)] = dc_dt[1:end]
87+
88+
dcv_dt = -S * J_B0 / V_v - (Q_l / V_v) * c_v
89+
du[end - 2] = dcv_dt
90+
91+
dcc_dt = S * α * J_BL / V_c + (Q_l / V_c) * c_v - (Q_l / V_c) * c_c
92+
du[end - 1] = dcc_dt
93+
94+
dcb_dt = (Q_l / V_b) * c_c + C / V_b
95+
return du[end] = dcb_dt
96+
end
97+
98+
return timedifferentialclosure!
99+
end
100+
101+
prior = ComponentArray(;
102+
α = 0.2,
103+
D = 0.46,
104+
v = 0.0,
105+
k_p = 0.0,
106+
V_c = 18,
107+
Q_l = 20,
108+
Q_r = 3.6,
109+
V_b = 1490,
110+
S = 52,
111+
Lm = 0.05,
112+
Dm = 0.046,
113+
V_v = 18.0
114+
)
115+
116+
r_space = collect(range(0.0, 2.0, length = 15))
117+
computeparams = (
118+
Δr = r_space[2],
119+
r_space = r_space,
120+
countorderapprox = 2,
121+
)
122+
parameters = (
123+
prior = prior,
124+
compute = computeparams,
125+
)
126+
127+
dudt = enclosethetimedifferential(parameters)
128+
IC = ones(length(r_space) + 3)
129+
odeprob = ODEProblem(
130+
dudt,
131+
IC,
132+
(0, 2.1),
133+
parameters.prior
134+
);
135+
du0 = copy(odeprob.u0);
136+
# Hardcoded sparsity pattern for 15 spatial points + 3 state variables (18x18 matrix)
137+
I = [1, 2, 16, 18, 1, 2, 3, 18, 2, 3, 4, 18, 3, 4, 5, 18, 4, 5, 6, 18, 5, 6, 7, 18, 6, 7, 8, 18, 7, 8, 9, 18, 8, 9, 10, 18, 9, 10, 11, 18, 10, 11, 12, 18, 11, 12, 13, 18, 12, 13, 14, 18, 13, 14, 15, 18, 14, 15, 17, 18, 1, 16, 17, 15, 17, 18, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 18]
138+
J = [1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 10, 10, 10, 10, 11, 11, 11, 11, 12, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15, 16, 16, 16, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18]
139+
jac_sparsity = sparse(I, J, ones(Bool, length(I)), 18, 18);
140+
f = ODEFunction(
141+
dudt;
142+
jac_prototype = float.(jac_sparsity)
143+
);
144+
sparseodeprob = ODEProblem(
145+
f,
146+
odeprob.u0,
147+
(0, 2.1),
148+
parameters.prior
149+
);
150+
151+
solve(odeprob, TRBDF2());
152+
solve(sparseodeprob, TRBDF2());
153+
solve(sparseodeprob, Rosenbrock23(linsolve = KLUFactorization()));
154+
solve(sparseodeprob, KenCarp47(linsolve = KrylovJL_GMRES()));
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
[Core]
2+
versions = ["lts", "1.11", "1", "pre"]
3+
4+
[QA]
5+
versions = ["1"]
6+
7+
[Sparse]
8+
versions = ["1"]
9+
10+
[ModelingToolkit]
11+
versions = ["1"]

lib/OrdinaryDiffEqNonlinearSolve/test/linear_solver_split_ode_tests.jl

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
using Test
22
using OrdinaryDiffEqBDF, OrdinaryDiffEqSDIRK
33
using LinearAlgebra, LinearSolve
4-
using OrdinaryDiffEqCore: dolinsolve
54

65
n = 8
76
dt = 1 / 1000
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
[deps]
2+
DiffEqDevTools = "f3b72e0c-5b89-59e1-b016-84e28bfd966d"
3+
IncompleteLU = "40713840-3770-5561-ab4c-a76e7d0d7895"
4+
LinearSolve = "7ed4a6bd-45f5-4d41-b270-4a48e9bafcae"
5+
SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"
6+
ModelingToolkit = "961ee093-0014-501f-94e3-6117800e7a78"
7+
NonlinearSolve = "8913a72c-1f9b-4ce2-8d82-65094dcecaec"
8+
OrdinaryDiffEq = "1dea7af3-3e70-54e6-95c3-0bf5283fa5ed"
9+
OrdinaryDiffEqBDF = "6ad6398a-0878-4a85-9266-38940aa047c8"
10+
OrdinaryDiffEqNonlinearSolve = "127b3ac7-2247-4354-8eb6-78cf4e7c58e8"
11+
OrdinaryDiffEqSDIRK = "2d112036-d095-4a1e-ab9a-08536f3ecdbf"
12+
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
13+
14+
[sources]
15+
OrdinaryDiffEq = {path = "../../../.."}
16+
OrdinaryDiffEqBDF = {path = "../../../OrdinaryDiffEqBDF"}
17+
OrdinaryDiffEqNonlinearSolve = {path = "../.."}
18+
OrdinaryDiffEqSDIRK = {path = "../../../OrdinaryDiffEqSDIRK"}
19+
20+
[compat]
21+
DiffEqDevTools = "2"
22+
IncompleteLU = "0.2"
23+
LinearSolve = "3"
24+
ModelingToolkit = "10.10, 11"
25+
NonlinearSolve = "4.10"
26+
OrdinaryDiffEq = "6"
27+
OrdinaryDiffEqBDF = "1"
28+
OrdinaryDiffEqNonlinearSolve = "1"
29+
OrdinaryDiffEqSDIRK = "1"

0 commit comments

Comments
 (0)