New State & Parameter Vector Types
I’m hoping for others’ thoughts on this idea, including suggestions, alternatives, rotten fruit, etc. All input is welcome.
The “Problem”
Users have tried to add unit handling to their numerical integration functions. It’s usually a bit tricky. I haven’t seen a perfect solution yet.
Motivation
Many users describe physical systems with ModelingToolkit or self-written ODEFunctions. Some try to add unit handling, and initialize the dynamics with Unitful quantities; at this point, they usually see some error. Several Discourse posts and GitHub issues have been created for this over the years. including the two linked below.
- DifferentialEquations.jl and systems with heterogenous units - #4 by ChrisRackauckas
- https://github.com/SciML/DifferentialEquations.jl/issues/352
There are a few issues with Unitful quantities in ODEFunctions today. Some math operators aren’t defined for Unitful.Unitlike or similar types. As an anecdotal example, I have encountered an error like “no method defined for one(::Unitlike)”. Generally, math with Unitful quantities is very good. That’s a testament to the Unitful developers, and Julia’s composability. Still, I know there’s lots of numerics magic going on in numerical integrators these days, so defining every possible required method for Unitful types seems like a game of whack-a-mole.
Current Solutions
I’ve seen the following solutions so far.
Stripping Units
If we strip units before integrating, then all’s fine! This can be a bit clunky, though.
Defining all Methods for Unitful Types
We could play that game of whack-a-mole, and define missing methods for Unitful types as we find them. That’s all fine, and Unitful vectors may even work with DifferentialEquations out of the box today (they did a few months ago). Still, then you run into an issue where your state vector has non-concrete eltypes.
If you pass a ComponentArray or a LabelledArray with Unitful element types, then you could make sure the formal eltype is Union{<:Unitful.LengthUnit, <:Unitful.VelocityUnit} (as an example for a system with position and velocity states), but this is still less efficient than simply passing a Vector{Float64} or similar.
Proposed Solution
What if we parameterized our state vectors and parameter vectors by their units. I’ve been using this for some time with GeneralAstrodynamics. The relevant source code is here. Right now, that functionality is pulling a lot of code from LabelledArrays, hence the ParameterizedLabelledArrays filename linked there.
As an example, we could define the following types.
"""
A vector with physical elements (aka `Unitful`)
"""
abstract type PhysicalVector{F, MU, LU, TU, AU,L} <: AbstractVector{F} end
"""
A representation for all state vectors for physical systems.
The parameters are...
- F => Floating-point type
- MU => Mass unit
- LU => Length unit
- TU => Time unit
- AU => Angular unit
- L => Labels
"""
abstract type StateVector{F, MU, LU, TU, AU,L} <: PhysicalVector{F, MU, LU, TU, AU,L} end
"""
A representation for all parameter vectors for physical systems.
The parameters are...
- F => Floating-point type
- MU => Mass unit
- LU => Length unit
- TU => Time unit
- AU => Angular unit
- L => Labels
"""
abstract type ParameterVector{F, MU, LU, TU, AU,L} <: PhysicalVector{F, MU, LU, TU, AU,L} end
Then, we could define a CartesianState like so…
struct CartesianState{F, MU, LU, TU, AU} <: StateVector{F, MU, LU, TU, AU,(x = 1, y = 2, z = 3, ẋ = 4, ẏ = 5, ż = 6, r = 1:3, v = 4:6)}
data::MVector{6,F}
end
And we could dispatch on functions like…
Base.@pure lengthunit(::CartesianState{F,MU,LU}) = LU
position(state::CartesianState) = state[1:3] * LU
Conclusion
Anyone have any thoughts on this? I think the struct definition is rough, but I do think there’s promise in parameterizing our state and parameter vectors by Unitful units. Maybe instead of carrying around units for all dimensions (like in the MU, LU, TU, AU above), we have a NamedTuple struct parameter which tracks units in the same way that LabelledArrays (and the copycat examples above) track labels.