Skip to content

Improve API of DEDataArray for fields that are AbstractArray but immutable #507

@ronisbr

Description

@ronisbr

Hi!

I am trying to create a workspace using DEDataArray as follow:

mutable struct Workspace{T} <: DEDataVector{T}
    x::Vector{T} = zeros(12)
    q::Quaternion = Quaternion(1,0,0,0)

where Quaternion is defined in ReferenceFrameRotations.jl. Here, I get the following error:

ERROR: LoadError: setindex! not defined for Quaternion{Float64}
Stacktrace:
 [1] error(::String, ::Type{T} where T) at ./error.jl:42
 [2] error_if_canonical_setindex(::IndexLinear, ::Quaternion{Float64}, ::Int64) at ./abstractarray.jl:1081
 [3] setindex! at ./abstractarray.jl:1072 [inlined]
 [4] copyto!(::Quaternion{Float64}, ::Quaternion{Float64}) at ./multidimensional.jl:962
 [5] recursivecopy!(::Quaternion{Float64}, ::Quaternion{Float64}) at /Users/ronan.arraes/.julia/packages/RecursiveArrayTools/YIS5B/src/utils.jl:25
 [6] macro expansion at /Users/ronan.arraes/.julia/packages/DiffEqBase/8A5uo/src/data_array.jl:60 [inlined]
 [7] recursivecopy!(::Workspace{Float64}, ::Workspace{Float64}) at /Users/ronan.arraes/.julia/packages/DiffEqBase/8A5uo/src/data_array.jl:60
 [8] apply_step!(::OrdinaryDiffEq.ODEIntegrator{Tsit5,true,Workspace{Float64},Nothing,Float64,Tuple{Configuration{Float64},Auxiliary{OrbitPropagatorJ4{Float64},Float64},StepRangeLen{Float64,Base.TwicePrecision{Float64},Base.TwicePrecision{Float64}}},Float64,Float64,Float64,Array{Workspace{Float64},1},ODESolution{Float64,2,Array{Workspace{Float64},1},Nothing,Nothing,Array{Float64,1},Array{Array{Workspace{Float64},1},1},ODEProblem{Workspace{Float64},Tuple{Float64,Float64},true,Tuple{Configuration{Float64},Auxiliary{OrbitPropagatorJ4{Float64},Float64},StepRangeLen{Float64,Base.TwicePrecision{Float64},Base.TwicePrecision{Float64}}},ODEFunction{true,typeof(dyn),UniformScaling{Bool},Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing},Base.Iterators.Pairs{Union{},Union{},Tuple{},NamedTuple{(),Tuple{}}},DiffEqBase.StandardODEProblem},Tsit5,OrdinaryDiffEq.InterpolationData{ODEFunction{true,typeof(dyn),UniformScaling{Bool},Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing},Array{Workspace{Float64},1},Array{Float64,1},Array{Array{Workspace{Float64},1},1},OrdinaryDiffEq.Tsit5Cache{Workspace{Float64},Workspace{Float64},Workspace{Float64},OrdinaryDiffEq.Tsit5ConstantCache{Float64,Float64}}},DiffEqBase.DEStats},ODEFunction{true,typeof(dyn),UniformScaling{Bool},Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing},OrdinaryDiffEq.Tsit5Cache{Workspace{Float64},Workspace{Float64},Workspace{Float64},OrdinaryDiffEq.Tsit5ConstantCache{Float64,Float64}},OrdinaryDiffEq.DEOptions{Float64,Float64,Float64,Float64,typeof(DiffEqBase.ODE_DEFAULT_NORM),typeof(opnorm),CallbackSet{Tuple{},Tuple{DiscreteCallback{typeof(condition_control_loop),typeof(affect_control_loop!),typeof(DiffEqBase.INITIALIZE_DEFAULT)}}},typeof(DiffEqBase.ODE_DEFAULT_ISOUTOFDOMAIN),typeof(DiffEqBase.ODE_DEFAULT_PROG_MESSAGE),typeof(DiffEqBase.ODE_DEFAULT_UNSTABLE_CHECK),DataStructures.BinaryHeap{Float64,DataStructures.LessThan},DataStructures.BinaryHeap{Float64,DataStructures.LessThan},Nothing,Nothing,Int64,StepRangeLen{Float64,Base.TwicePrecision{Float64},Base.TwicePrecision{Float64}},Array{Float64,1},Array{Float64,1}},Workspace{Float64},Float64,Nothing,OrdinaryDiffEq.DefaultInit}) at /Users/ronan.arraes/.julia/packages/OrdinaryDiffEq/V4HnF/src/integrators/integrator_utils.jl:288

The problem is that Quaternion (like SVector) is an immutable AbstractArray. In the current code, this is handle as follows:

        if Tf <: Union{Number,SArray}
            expressions[i] = :( dest.$f = getfield( src, $qf ) )
        elseif Tf <: AbstractArray
            expressions[i] = :( recursivecopy!(dest.$f, getfield( src, $qf ) ) )
        else
            expressions[i] = :( dest.$f = deepcopy( getfield( src, $qf ) ) )
        end

Thus, an element of type SVector is copied as b = a. However, Quaternion will be copied using recursivecopy!. This, in turn, will call copyto! that is not defined.

Thus, I have two suggestions:

  1. Create a simple API like DiffEqBase.ismutablearray(::Some_type) = true. If this is true, then proceed with recursivecopy!. Otherwise, copy the element using b = a.
  2. (I think this is better) Check if the method setindex! is implemented for the type. If it is, then proceed with recursivecopy!. Otherwise, copy the element using b = a.

P.S.: If this is accepted, I can submit a PR.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions