Abstract-type field in struct for solving an ODE problem

Hi, I’m learning Julia and particularly interested in solving differential equations using the DifferentialEquations package. I’m having a trouble with understanding how to make a good type-stable struct that is integrated. For example, I have this mutable struct to be integrated.

mutable struct State{T} <: DEDataVector{T}
    x::Vector{T}
    r::Region
end

where Region is custom-defined abstract type: abstract type Region end
The role of Region is to parameterize my ODE function with Callback functions. For instance,

function myODE(du, u, p, t)
    c = getCoeff(u.r)
    du[1] = c*u[1]
end

which is a simplified version of my actual code but I believe it exhibits what I’m trying to say well enough. Especially, which region r::Region should be in at each time t should be determined by t and it must be updated properly by Callback features.

I have a bunch of concrete-type structs whose supertype is Region, for example

struct myRegion1 <: Region
    property1::Int64
    property2::Float64
    ...
end

and so on similarly for the other myRegions. And I have well-defined overloaded functions getCoeff(r::myRegion1), getCoeff(r::myRegion2), … for each region. This woked fine, but I found the integration is not optimized unless I have type-stable fields in my State struct. It’s tested with @code_warntype on myODE function. Since Region is an abstract type, the compiler has no solid inference on type, which leads to a performance hit for the integration.

After searching for the solution for this case, I found many people suggest to rather use parametric type struct or use Union of concrete-type structs. The former one can’t be the solution for this case, because I have a mixed type for r in my State struct. For instance, say r is initialized to be myRegion1. But as the integration proceeds, it should be updated to myRegion2 or another when Callback is called. Therefore, I must not use a parametric State struct in terms of Region.

The second solution seemed like it’d work, but I end up facing another problem.
First I defined a Union type as following:

const myRegions = Union{myRegion1, myRegion2, ...}

Then I modified the definition of my State struct as

mutable struct State{T} <: DEDataVector{T}
    x::Vector{T}
    r::myRegions
end

and r is no longer abstract type. But when I run the integration by

prob = ODEProblem(myODE, u0, timeSpan, p)
solve(prob, algorithm, callback=callbacks)

I got an unexpected error:

type Union has no field mutable

Apparently, the integrator checks whether type of every field of u is mutable or not, which is not even defined in Union type. So I really got stuck here… any ideas or suggestions would be really appreciated. Thanks for reading this post.

It assumes concrete types, because you’ll want that if you want your code to be fast anyways. Mutability is checked for how it does recursive copying. You can open up an issue if you want and I can take a deeper look if there’s a good solution here, but I won’t get to it soon. My suggestion would just be to use concrete types.