Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
132 commits
Select commit Hold shift + click to select a range
e0f73ed
Elixir for implicit solver using sparse jacobian
Jul 10, 2025
9dbe05c
Docstring for new semidiscretize
Jul 22, 2025
6adff66
PR comments
Jul 23, 2025
097e994
Making the hacks into an extension
Jul 23, 2025
2e7cebf
Apply suggestions from code review
DanielDoehring Jul 24, 2025
2be59f8
work
DanielDoehring Jul 24, 2025
254a79f
packages
DanielDoehring Jul 24, 2025
9089e6a
reduce time
DanielDoehring Jul 24, 2025
89a7b92
work
DanielDoehring Jul 24, 2025
524ae9a
use sparse ode
DanielDoehring Jul 24, 2025
c984c02
comment
DanielDoehring Jul 24, 2025
0dd324d
be more explicit
DanielDoehring Jul 24, 2025
1f5a40b
try nonlinear
DanielDoehring Jul 24, 2025
46b2d92
comment
DanielDoehring Jul 24, 2025
ca5f951
comment
DanielDoehring Jul 24, 2025
7bdfc8d
exchange solver
DanielDoehring Jul 25, 2025
2f9d246
work
DanielDoehring Jul 25, 2025
c1ebd50
comment
DanielDoehring Jul 25, 2025
7d141cf
comments
DanielDoehring Jul 25, 2025
bb6b7d5
Merge branch 'main' into SteveSparseDiff
DanielDoehring Jul 25, 2025
4e163fe
comment
DanielDoehring Jul 25, 2025
dad7fe7
rename
DanielDoehring Jul 28, 2025
b5db145
comments
DanielDoehring Jul 28, 2025
3ce1a4c
Moving the ext back into the elixirs
Jul 28, 2025
a65537c
Adding CI test
Jul 28, 2025
27bc102
Adding CI test for implicit_sparse_jacobian elixir
Jul 28, 2025
d956961
Update examples/tree_1d_dgsem/elixir_euler_convergence_implicit_spars…
DanielDoehring Jul 29, 2025
4b1dbdd
Update examples/tree_1d_dgsem/elixir_euler_convergence_implicit_spars…
DanielDoehring Jul 29, 2025
cc3fa6a
Apply suggestions from code review
DanielDoehring Jul 29, 2025
6e9a0d0
Update examples/tree_2d_dgsem/elixir_advection_implicit_sparse_jacobi…
DanielDoehring Jul 29, 2025
6344c66
comments on constant jacs
DanielDoehring Jul 29, 2025
63c9214
move dir
DanielDoehring Jul 29, 2025
691cedf
comment
DanielDoehring Jul 29, 2025
c5f5fcf
order
DanielDoehring Jul 29, 2025
5586491
restart file
DanielDoehring Jul 29, 2025
eb6aa90
Apply suggestions from code review
DanielDoehring Jul 29, 2025
e851808
compat
DanielDoehring Jul 29, 2025
82caac1
Merge branch 'elixir_implicit_sparse' of github.com:sbairos/Trixi.jl …
DanielDoehring Jul 29, 2025
9d97bcc
remove redundant comments
DanielDoehring Jul 29, 2025
2ed4e38
Elixir for implicit solver using sparse jacobian
Jul 10, 2025
7bd5f32
Docstring for new semidiscretize
Jul 22, 2025
238cadb
PR comments
Jul 23, 2025
8a4ce20
Making the hacks into an extension
Jul 23, 2025
d12dca7
Apply suggestions from code review
DanielDoehring Jul 24, 2025
6b3a900
work
DanielDoehring Jul 24, 2025
ef31890
packages
DanielDoehring Jul 24, 2025
d65fadb
reduce time
DanielDoehring Jul 24, 2025
8289379
work
DanielDoehring Jul 24, 2025
61e8f13
use sparse ode
DanielDoehring Jul 24, 2025
6e3d6ea
comment
DanielDoehring Jul 24, 2025
d02e4d1
be more explicit
DanielDoehring Jul 24, 2025
d024182
try nonlinear
DanielDoehring Jul 24, 2025
4041aab
comment
DanielDoehring Jul 24, 2025
b5f7e10
comment
DanielDoehring Jul 24, 2025
80b3d53
exchange solver
DanielDoehring Jul 25, 2025
979a66b
work
DanielDoehring Jul 25, 2025
914e1dd
comment
DanielDoehring Jul 25, 2025
9cfdbd0
comments
DanielDoehring Jul 25, 2025
0fa5381
comment
DanielDoehring Jul 25, 2025
987c1ea
rename
DanielDoehring Jul 28, 2025
8c7a425
comments
DanielDoehring Jul 28, 2025
b526312
Moving the ext back into the elixirs
Jul 28, 2025
51a5b77
Adding CI test
Jul 28, 2025
9f713b9
Adding CI test for implicit_sparse_jacobian elixir
Jul 28, 2025
4ca244b
CI tests for both
Jul 29, 2025
6909159
Merge branch 'workwork' into elixir_implicit_sparse
Jul 29, 2025
4467be4
Other 2 CI tests
Jul 29, 2025
5bb92d6
Uncomment commented tests and delete 1D convergence_implicit elixir
Jul 29, 2025
0cc77c0
Replace the test that I accidentally overwrote
Jul 29, 2025
a829d5c
Unit test
Jul 29, 2025
c303210
Update test/test_unit.jl
DanielDoehring Jul 29, 2025
84a5be8
Update Project.toml
DanielDoehring Jul 29, 2025
747768e
Update examples/tree_2d_dgsem/elixir_advection_implicit_sparse_jacobi…
DanielDoehring Jul 29, 2025
9a164a8
Update examples/tree_2d_dgsem/elixir_advection_implicit_sparse_jacobi…
DanielDoehring Jul 29, 2025
03ac768
revisit
DanielDoehring Jul 30, 2025
b0e9083
unit test
DanielDoehring Jul 30, 2025
5c33791
repeat comment
DanielDoehring Jul 30, 2025
5e7e7b1
linear structure
DanielDoehring Jul 30, 2025
9137af2
fmzt
DanielDoehring Jul 30, 2025
5ae5c72
fix unit test
DanielDoehring Jul 30, 2025
026bc8a
Apply suggestions from code review
DanielDoehring Jul 31, 2025
6e8115c
Spellcheck
Jul 31, 2025
2a894fe
Merge branch 'main' into elixir_implicit_sparse
DanielDoehring Jul 31, 2025
a147c1c
Update examples/tree_2d_dgsem/elixir_advection_implicit_sparse_jacobi…
DanielDoehring Jul 31, 2025
5719d0e
Merge branch 'main' into elixir_implicit_sparse
DanielDoehring Aug 1, 2025
61e4d47
comment
DanielDoehring Aug 1, 2025
122a46e
add sparse arrays
DanielDoehring Aug 1, 2025
47a7a5b
try module
DanielDoehring Aug 1, 2025
ea8cf44
rm
DanielDoehring Aug 1, 2025
b9a54d2
try more specific overload
DanielDoehring Aug 2, 2025
476b174
Merge branch 'main' into elixir_implicit_sparse
DanielDoehring Aug 2, 2025
309c730
simplify
DanielDoehring Aug 2, 2025
6d6a497
v
DanielDoehring Aug 4, 2025
36ed9b9
v
DanielDoehring Aug 4, 2025
07589c2
Update examples/structured_2d_dgsem/elixir_euler_convergence_implicit…
DanielDoehring Aug 11, 2025
70b3861
Update examples/structured_2d_dgsem/elixir_euler_convergence_implicit…
DanielDoehring Aug 11, 2025
f479c92
Update examples/structured_2d_dgsem/elixir_euler_convergence_implicit…
DanielDoehring Aug 11, 2025
8478589
Merge branch 'main' into elixir_implicit_sparse
DanielDoehring Aug 11, 2025
aba1e36
Merge branch 'main' into elixir_implicit_sparse
DanielDoehring Aug 12, 2025
25518ef
Update examples/tree_2d_dgsem/elixir_advection_implicit_sparse_jacobi…
DanielDoehring Aug 14, 2025
6ea38f2
Merge branch 'main' into elixir_implicit_sparse
DanielDoehring Aug 15, 2025
5153dab
Merge branch 'main' into elixir_implicit_sparse
DanielDoehring Aug 19, 2025
f2f3780
correct comment
DanielDoehring Aug 19, 2025
613f01a
Merge branch 'elixir_implicit_sparse' of github.com:sbairos/Trixi.jl …
DanielDoehring Aug 19, 2025
1b241ae
simplify
DanielDoehring Aug 19, 2025
25b54b5
kwargs
DanielDoehring Aug 19, 2025
c026cf9
adapt
DanielDoehring Aug 19, 2025
0f3f00c
shorten
DanielDoehring Aug 19, 2025
9bc0f6c
Update examples/tree_2d_dgsem/elixir_advection_implicit_sparse_jacobi…
DanielDoehring Aug 19, 2025
cde1bff
fmt
DanielDoehring Aug 19, 2025
eb29838
simplify
DanielDoehring Aug 19, 2025
a72dce2
try different warning
DanielDoehring Aug 19, 2025
2c9eb34
warning
DanielDoehring Aug 19, 2025
d9e4b51
Merge branch 'main' into elixir_implicit_sparse
DanielDoehring Aug 19, 2025
f99ac51
test
DanielDoehring Aug 19, 2025
5c5722f
Merge branch 'elixir_implicit_sparse' of github.com:sbairos/Trixi.jl …
DanielDoehring Aug 19, 2025
8542b3e
up
DanielDoehring Aug 19, 2025
43900a4
revert
DanielDoehring Aug 20, 2025
9d04c00
move comment
DanielDoehring Aug 20, 2025
03c6b7b
compat
DanielDoehring Aug 20, 2025
7824a97
v
DanielDoehring Aug 20, 2025
5357ec3
v
DanielDoehring Aug 20, 2025
a16419a
v
DanielDoehring Aug 20, 2025
a4f3af4
try plots
DanielDoehring Aug 20, 2025
6991c6e
v
DanielDoehring Aug 20, 2025
63a415e
Update test/Project.toml
DanielDoehring Aug 20, 2025
d21a0e2
module replace
DanielDoehring Aug 20, 2025
151bc57
Merge branch 'elixir_implicit_sparse' of github.com:sbairos/Trixi.jl …
DanielDoehring Aug 20, 2025
72ac79f
typo
DanielDoehring Aug 20, 2025
b04e814
Merge branch 'main' into elixir_implicit_sparse
DanielDoehring Aug 21, 2025
76754b5
error tol
DanielDoehring Aug 21, 2025
c815383
Merge branch 'elixir_implicit_sparse' of github.com:sbairos/Trixi.jl …
DanielDoehring Aug 21, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
using Trixi
using OrdinaryDiffEqSDIRK

# Functionality for automatic sparsity detection
using SparseDiffTools, Symbolics

###############################################################################################
### Overloads to construct the `LobattoLegendreBasis` with `Real` type (supertype of `Num`) ###

# Required for setting up the Lobatto-Legendre basis for abstract `Real` type.
# Constructing the Lobatto-Legendre basis with `Real` instead of `Num` is
# significantly easier as we do not have to care about e.g. if-clauses.
# As a consequence, we need to provide some overloads hinting towards the intended behavior.

const float_type = Float64 # Actual floating point type for the simulation

# Newton tolerance for finding LGL nodes & weights
Trixi.eps(::Type{Real}) = Base.eps(float_type)
# There are some places where `one(RealT)` or `zero(uEltype)` is called where `RealT` or `uEltype` is `Real`.
# This returns an `Int64`, i.e., `1` or `0`, respectively which gives errors when a floating-point alike type is expected.
Trixi.one(::Type{Real}) = Base.one(float_type)
Trixi.zero(::Type{Real}) = Base.zero(float_type)

module RealMatMulOverload

# Multiplying two Matrix{Real}s gives a Matrix{Any}.
# This causes problems when instantiating the Legendre basis, which calls
# `calc_{forward,reverse}_{upper, lower}` which in turn uses the matrix multiplication
# which is overloaded here in construction of the interpolation/projection operators
# required for mortars.
function Base.:*(A::Matrix{Real}, B::Matrix{Real})::Matrix{Real}
m, n = size(A, 1), size(B, 2)
kA = size(A, 2)
kB = size(B, 1)
@assert kA==kB "Matrix dimensions must match for multiplication"

C = Matrix{Real}(undef, m, n)
for i in 1:m, j in 1:n
#acc::Real = zero(promote_type(typeof(A[i,1]), typeof(B[1,j])))
acc = zero(Real)
for k in 1:kA
acc += A[i, k] * B[k, j]
end
C[i, j] = acc
end
return C
end
end

import .RealMatMulOverload

# We need to avoid if-clauses to be able to use `Num` type from Symbolics without additional hassle.
# In the Trixi implementation, we overload the sqrt function to first check if the argument
# is < 0 and then return NaN instead of an error.
# To turn off this behaviour, we switch back to the Base implementation here which does not contain an if-clause.
Trixi.sqrt(x::Num) = Base.sqrt(x)

###############################################################################################
### equations and solver ###

equations = CompressibleEulerEquations2D(1.4)

# For sparsity detection we can only use `flux_lax_friedrichs` at the moment since this is
# `if`-clause free
surface_flux = flux_lax_friedrichs

# `RealT = Real` requires fewer overloads than the more explicit `RealT = Num` from Symbolics.
# `solver_real` is used for computing the Jacobian sparsity pattern
solver_real = DGSEM(polydeg = 3, surface_flux = surface_flux, RealT = Real)
# `solver_float` is used for the subsequent simulation
solver_float = DGSEM(polydeg = 3, surface_flux = surface_flux, RealT = float_type)

###############################################################################################
### mesh ###

# Mapping as described in https://arxiv.org/abs/2012.12040,
# reduced to 2D on [0, 2]^2 instead of [0, 3]^3
function mapping(xi_, eta_)
# Transform input variables between -1 and 1 onto [0,2]
xi = xi_ + 1
eta = eta_ + 1

y = eta + 1 / 4 * (cos(pi * (xi - 1)) *
cos(0.5 * pi * (eta - 1)))

x = xi + 1 / 4 * (cos(0.5 * pi * (xi - 1)) *
cos(2 * pi * (y - 1)))

return SVector(x, y)
end
cells_per_dimension = (16, 16)
mesh = StructuredMesh(cells_per_dimension, mapping)

###############################################################################################
### semidiscretizations ###

initial_condition = initial_condition_convergence_test

# `semi_real` is used for computing the Jacobian sparsity pattern
semi_real = SemidiscretizationHyperbolic(mesh, equations,
initial_condition_convergence_test,
solver_real,
source_terms = source_terms_convergence_test)
# `semi_float` is used for the subsequent simulation
semi_float = SemidiscretizationHyperbolic(mesh, equations,
initial_condition_convergence_test,
solver_float,
source_terms = source_terms_convergence_test)

t0 = 0.0 # Re-used for the ODE function defined below
t_end = 5.0
t_span = (t0, t_end)

# Call `semidiscretize` on `semi_float` to create the ODE problem to have access to the initial condition.
ode_float = semidiscretize(semi_float, t_span)
u0_ode = ode_float.u0
du_ode = similar(u0_ode)

###############################################################################################
### Compute the Jacobian with SparseDiffTools ###

# Create a function with two parameters: `du_ode` and `u0_ode`
# to fulfill the requirements of an in_place function in SparseDiffTools
# (see example function `f` from https://docs.sciml.ai/SparseDiffTools/dev/#Example)
rhs = (du_ode, u0_ode) -> Trixi.rhs!(du_ode, u0_ode, semi_real, t0)

# Taken from example linked above to detect the pattern and choose how to do the differentiation
sd = SymbolicsSparsityDetection()
ad_type = AutoForwardDiff()
sparse_adtype = AutoSparse(ad_type)

# `sparse_cache` will reduce calculation time when Jacobian is calculated multiple times
sparse_cache = sparse_jacobian_cache(sparse_adtype, sd, rhs, du_ode, u0_ode)
# The jacobian can eventually be re-computed in-place using
#J_prealloc = sparse_jacobian(sparse_adtype, sparse_cache, rhs, du_ode, u0_ode)
#sparse_jacobian!(J_prealloc, sparse_adtype, sparse_cache, rhs, du_ode, u0_ode)

###############################################################################################
### Set up sparse-aware ODEProblem ###

# Revert overrides from above for the actual simulation -
# not strictly necessary, but good practice
Trixi.eps(x::Type{Real}) = Base.eps(x)
Trixi.one(x::Type{Real}) = Base.one(x)
Trixi.zero(x::Type{Real}) = Base.zero(x)

# Supply Jacobian prototype and coloring vector to the semidiscretization
ode_float_jac_sparse = semidiscretize(semi_float, t_span,
jac_prototype = sparse_cache.jac_prototype,
colorvec = sparse_cache.coloring.colorvec)

summary_callback = SummaryCallback()
analysis_callback = AnalysisCallback(semi_float, interval = 50)
alive_callback = AliveCallback(alive_interval = 3)

# Note: No `stepsize_callback` due to implicit solver
callbacks = CallbackSet(summary_callback, analysis_callback, alive_callback)

###############################################################################################
### solve the ODE problem ###

sol = solve(ode_float_jac_sparse, # using `ode_float` is essentially infeasible, even single step takes ages!
# Default `AutoForwardDiff()` is not yet working,
# probably related to https://docs.sciml.ai/DiffEqDocs/stable/basics/faq/#Autodifferentiation-and-Dual-Numbers
Kvaerno4(; autodiff = AutoFiniteDiff());
dt = 0.05, save_everystep = false, callback = callbacks);
147 changes: 147 additions & 0 deletions examples/tree_2d_dgsem/elixir_advection_implicit_sparse_jacobian.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
using Trixi
using OrdinaryDiffEqSDIRK

# Functionality for automatic sparsity detection
using SparseDiffTools, Symbolics

###############################################################################################
### Overloads to construct the `LobattoLegendreBasis` with `Real` type (supertype of `Num`) ###

# Required for setting up the Lobatto-Legendre basis for abstract `Real` type.
# Constructing the Lobatto-Legendre basis with `Real` instead of `Num` is
# significantly easier as we do not have to care about e.g. if-clauses.
# As a consequence, we need to provide some overloads hinting towards the intended behavior.

const float_type = Float64 # Actual floating point type for the simulation

# Newton tolerance for finding LGL nodes & weights
Trixi.eps(::Type{Real}) = Base.eps(float_type)
# There are some places where `one(RealT)` or `zero(uEltype)` is called where `RealT` or `uEltype` is `Real`.
# This returns an `Int64`, i.e., `1` or `0`, respectively which gives errors when a floating-point alike type is expected.
Trixi.one(::Type{Real}) = Base.one(float_type)
Trixi.zero(::Type{Real}) = Base.zero(float_type)

module RealMatMulOverload

# Multiplying two Matrix{Real}s gives a Matrix{Any}.
# This causes problems when instantiating the Legendre basis, which calls
# `calc_{forward,reverse}_{upper, lower}` which in turn uses the matrix multiplication
# which is overloaded here in construction of the interpolation/projection operators
# required for mortars.
function Base.:*(A::Matrix{Real}, B::Matrix{Real})::Matrix{Real}
m, n = size(A, 1), size(B, 2)
kA = size(A, 2)
kB = size(B, 1)
@assert kA==kB "Matrix dimensions must match for multiplication"

C = Matrix{Real}(undef, m, n)
for i in 1:m, j in 1:n
#acc::Real = zero(promote_type(typeof(A[i,1]), typeof(B[1,j])))
acc = zero(Real)
for k in 1:kA
acc += A[i, k] * B[k, j]
end
C[i, j] = acc
end
return C
end
end

import .RealMatMulOverload

###############################################################################################
### semidiscretizations of the linear advection equation ###

advection_velocity = (0.2, -0.7)
equation = LinearScalarAdvectionEquation2D(advection_velocity)

# `RealT = Real` requires fewer overloads than the more explicit `RealT = Num` from Symbolics
# `solver_real` is used for computing the Jacobian sparsity pattern
solver_real = DGSEM(polydeg = 3, surface_flux = flux_godunov, RealT = Real)
# `solver_float` is used for the subsequent simulation
solver_float = DGSEM(polydeg = 3, surface_flux = flux_godunov)

coordinates_min = (-1.0, -1.0)
coordinates_max = (1.0, 1.0)

mesh = TreeMesh(coordinates_min, coordinates_max,
initial_refinement_level = 4,
n_cells_max = 30_000)

# `semi_real` is used for computing the Jacobian sparsity pattern
semi_real = SemidiscretizationHyperbolic(mesh, equation,
initial_condition_convergence_test,
solver_real)
# `semi_float` is used for the subsequent simulation
semi_float = SemidiscretizationHyperbolic(mesh, equation,
initial_condition_convergence_test,
solver_float)

t0 = 0.0 # Re-used for the ODE function defined below
t_end = 1.0
t_span = (t0, t_end)

# Call `semidiscretize` on `semi_float` to create the ODE problem to have access to the initial condition.
# For the linear example considered here one could also use an arbitrary vector for the initial condition.
ode_float = semidiscretize(semi_float, t_span)
u0_ode = ode_float.u0
du_ode = similar(u0_ode)

###############################################################################################
### Compute the Jacobian with SparseDiffTools ###

# Create a function with two parameters: `du_ode` and `u0_ode`
# to fulfill the requirements of an in_place function in SparseDiffTools
# (see example function `f` from https://docs.sciml.ai/SparseDiffTools/dev/#Example)
rhs = (du_ode, u0_ode) -> Trixi.rhs!(du_ode, u0_ode, semi_real, t0)

# Taken from example linked above to detect the pattern and choose how to do the AutoDiff automatically
sd = SymbolicsSparsityDetection()
ad_type = AutoFiniteDiff() # `AutoForwardDiff()` does also work, but not strictly necessary for sparsity detection only
sparse_adtype = AutoSparse(ad_type)

# `sparse_cache` will reduce calculation time when Jacobian is calculated multiple times,
# which is in principle not required for the linear problem considered here.
sparse_cache = sparse_jacobian_cache(sparse_adtype, sd, rhs, du_ode, u0_ode)

###############################################################################################
### Set up sparse-aware ODEProblem ###

# Revert overrides from above for the actual simulation -
# not strictly necessary, but good practice
Trixi.eps(x::Type{Real}) = Base.eps(x)
Trixi.one(x::Type{Real}) = Base.one(x)
Trixi.zero(x::Type{Real}) = Base.zero(x)

# Supply Jacobian prototype and coloring vector to the semidiscretization
ode_float_jac_sparse = semidiscretize(semi_float, t_span,
jac_prototype = sparse_cache.jac_prototype,
colorvec = sparse_cache.coloring.colorvec)

# Note: We tried for this linear problem providing the constant, sparse Jacobian directly via
#
# const jac_sparse = sparse_jacobian(sparse_adtype, sparse_cache, rhs, du_ode, u0_ode)
# function jac_sparse_func!(J, u, p, t)
# J = jac_sparse
# end
# SciMLBase.ODEFunction(rhs!, jac_prototype=float.(jac_prototype), colorvec=colorvec, jac = jac_sparse_func!)
#
# which turned out (for the considered problems) to be significantly slower than
# just using the prototype and the coloring vector.

summary_callback = SummaryCallback()
analysis_callback = AnalysisCallback(semi_float, interval = 10)
save_restart = SaveRestartCallback(interval = 100,
save_final_restart = true)

# Note: No `stepsize_callback` due to (implicit) solver with adaptive timestep control
callbacks = CallbackSet(summary_callback, analysis_callback, save_restart)

###############################################################################################
### solve the ODE problem ###

sol = solve(ode_float_jac_sparse, # using `ode_float_jac_sparse` instead of `ode_float` results in speedup of factors 10-15!
# Default `AutoForwardDiff()` is not yet working,
# probably related to https://docs.sciml.ai/DiffEqDocs/stable/basics/faq/#Autodifferentiation-and-Dual-Numbers
TRBDF2(; autodiff = ad_type);
dt = 0.1, save_everystep = false, callback = callbacks);
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using Trixi

###############################################################################
# create a restart file

elixir_file = "elixir_advection_implicit_sparse_jacobian.jl"
restart_file = "restart_000000006.h5"

trixi_include(@__MODULE__, joinpath(@__DIR__, elixir_file))

###############################################################################

restart_filename = joinpath("out", restart_file)
t_span = (load_time(restart_filename), 2.0)
dt_restart = load_dt(restart_filename)

ode_float_jac_sparse = semidiscretize(semi_float, t_span,
jac_prototype = sparse_cache.jac_prototype,
colorvec = sparse_cache.coloring.colorvec)

###############################################################################
# run the simulation

sol = solve(ode_float_jac_sparse, # using `ode_float_jac_sparse` instead of `ode_float` results in speedup of factors 10-15!
# Default `AutoForwardDiff()` is not yet working,
# probably related to https://docs.sciml.ai/DiffEqDocs/stable/basics/faq/#Autodifferentiation-and-Dual-Numbers
TRBDF2(; autodiff = ad_type);
adaptive = true, dt = dt_restart, save_everystep = false, callback = callbacks);
Loading