Hi Julia community,
I have been using DifferentialEquations.jl recently, which is an excellent package. In the spirit of writing performant Julia code, I have been trying to eliminate unnecessary heap allocations.
I have come across a type-instability that I have been unable to fix. The type instability arises when I nest one struct within another. Here I include an MWE illustrating the instability with a struct Fit nested in another struct EOS:
using DifferentialEquations
const conversion_factor = 755400.0
struct Fit
m₁::Float64
c₁::Float64
m₂::Float64
c₂::Float64
function Fit()
m₁ = 1.595
c₁ = 3.438
m₂ = 1.075
c₂ = 3.484
new(m₁, c₁, m₂, c₂)
end
end
nbfun(fit::Fit, pnum) = 10^((log10(pnum) - fit.c₁)/fit.m₁)
ε(fit::Fit, nb) = 10^(fit.m₂*log10(nb) + fit.c₂)
struct EOS
fit::Fit
function EOS()
fit = Fit()
new(fit)
end
end
ε(eos::EOS, p) = ε(eos.fit, nbfun(eos.fit, conversion_factor*p))/conversion_factor
function structure!(dy_dr, y, eos, r)
m, p = y
εᵣ = ε(eos, p)
dν_dr = 2*(m + 4*π*r^3*p)/(r*(r - 2*m))
dy_dr[1] = 4*π*r^2*εᵣ
dy_dr[2] = - (εᵣ + p)*dν_dr/2
dy_dr
end
function main()
eos = EOS()
pc = 1.19e-4
prob = ODEProblem{true}(structure!, [0, pc], (1e-5, 10), eos)
sol = solve(prob, Tsit5())
end
When I examine the types, I find
julia> @code_warntype main()
MethodInstance for main()
from main() @ Main REPL[9]:1
Arguments
#self#::Core.Const(main)
Locals
sol::Any
prob::ODEProblem{Vector{Float64}, Tuple{Float64, Float64}, true, EOS, ODEFunction{true, SciMLBase.AutoSpecialize, typeof(structure!), LinearAlgebra.UniformScaling{Bool}, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, typeof(SciMLBase.DEFAULT_OBSERVED), Nothing, Nothing}, Base.Pairs{Symbol, Union{}, Tuple{}, @NamedTuple{}}, SciMLBase.StandardODEProblem}
pc::Float64
eos::EOS
Body::Any
1 ─ (eos = Main.EOS())
│ (pc = 0.000119)
│ %3 = Core.apply_type(Main.ODEProblem, true)::Core.Const(ODEProblem{true})
│ %4 = Main.structure!::Core.Const(structure!)
│ %5 = Base.vect(0, pc::Core.Const(0.000119))::Vector{Float64}
│ %6 = Core.tuple(1.0e-5, 10)::Core.Const((1.0e-5, 10))
│ (prob = (%3)(%4, %5, %6, eos::Core.Const(EOS(Fit(1.595, 3.438, 1.075, 3.484)))))
│ %8 = prob::ODEProblem{Vector{Float64}, Tuple{Float64, Float64}, true, EOS, ODEFunction{true, SciMLBase.AutoSpecialize, typeof(structure!), LinearAlgebra.UniformScaling{Bool}, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, typeof(SciMLBase.DEFAULT_OBSERVED), Nothing, Nothing}, Base.Pairs{Symbol, Union{}, Tuple{}, @NamedTuple{}}, SciMLBase.StandardODEProblem}
│ %9 = Main.Tsit5()::Core.Const(Tsit5(; stage_limiter! = trivial_limiter!, step_limiter! = trivial_limiter!, thread = static(false),))
│ %10 = Main.solve(%8, %9)::Any
│ (sol = %10)
└── return %10
The type-unstable part is at the end with the variable sol as type Any.
Now, for this simple example, it is trivial to combine the two structs into one, which does eliminate the type-instability. However, for my purposes, I would like to keep the two structs separate.
How can I remove the type-instability? I would be grateful for any tips or suggestions on how I can address this problem.