diff --git a/src/FieldArray.jl b/src/FieldArray.jl new file mode 100644 index 00000000..30e2ea25 --- /dev/null +++ b/src/FieldArray.jl @@ -0,0 +1,111 @@ +""" + abstract FieldArray{N, T, D} <: StaticArray{N, T, D} + +Inheriting from this type will make it easy to create your own rank-D tensor types. A `FieldArray` +will automatically define `getindex` and `setindex!` appropriately. An immutable +`FieldArray` will be as performant as an `SArray` of similar length and element type, +while a mutable `FieldArray` will behave similarly to an `MArray`. + +For example: + + struct Stiffness <: FieldArray{Tuple{2,2,2,2}, Float64, 4} + xxxx::Float64 + yxxx::Float64 + xyxx::Float64 + yyxx::Float64 + xxyx::Float64 + yxyx::Float64 + xyyx::Float64 + yyyx::Float64 + xxxy::Float64 + yxxy::Float64 + xyxy::Float64 + yyxy::Float64 + xxyy::Float64 + yxyy::Float64 + xyyy::Float64 + yyyy::Float64 + end + + Note that you must define the fields of any `FieldArray` subtype in column major order. If you + want to use an alternative ordering you will need to pay special attention in providing your + own definitions of `getindex`, `setindex!` and tuple conversion. +""" +abstract type FieldArray{N, T, D} <: StaticArray{N, T, D} end + +""" + abstract FieldMatrix{N1, N2, T} <: FieldArray{Tuple{N1, N2}, 2} + +Inheriting from this type will make it easy to create your own rank-two tensor types. A `FieldMatrix` +will automatically define `getindex` and `setindex!` appropriately. An immutable +`FieldMatrix` will be as performant as an `SMatrix` of similar length and element type, +while a mutable `FieldMatrix` will behave similarly to an `MMatrix`. + +For example: + + struct Stress <: FieldMatrix{3, 3, Float64} + xx::Float64 + yx::Float64 + zx::Float64 + xy::Float64 + yy::Float64 + zy::Float64 + xz::Float64 + yz::Float64 + zz::Float64 + end + + Note that the fields of any subtype of `FieldMatrix` must be defined in column major order. + This means that formatting of constructors for literal `FieldMatrix` can be confusing. For example + + sigma = Stress(1.0, 2.0, 3.0, + 4.0, 5.0, 6.0, + 7.0, 8.0, 9.0) + + 3×3 Stress: + 1.0 4.0 7.0 + 2.0 5.0 8.0 + 3.0 6.0 9.0 + + +will give you the transpose of what the multi-argument formatting suggests. For clarity, +you may consider using the alternative + + sigma = Stress(@SArray[1.0 2.0 3.0; + 4.0 5.0 6.0; + 7.0 8.0 9.0]) +""" +abstract type FieldMatrix{N1, N2, T} <: FieldArray{Tuple{N1, N2}, T, 2} end + +""" + abstract FieldVector{N, T} <: FieldArray{Tuple{N}, 1} + +Inheriting from this type will make it easy to create your own vector types. A `FieldVector` +will automatically define `getindex` and `setindex!` appropriately. An immutable +`FieldVector` will be as performant as an `SVector` of similar length and element type, +while a mutable `FieldVector` will behave similarly to an `MVector`. + +For example: + + struct Point3D <: FieldVector{3, Float64} + x::Float64 + y::Float64 + z::Float64 + end +""" +abstract type FieldVector{N, T} <: FieldArray{Tuple{N}, T, 1} end + +@inline function (::Type{FA})(x::Tuple{Vararg{Any, N}}) where {N, FA <: FieldArray} + if length(FA) == length(x) + FA(x...) + else + throw(DimensionMismatch("No precise constructor for $FA found. Length of input was $(length(x)).")) + end +end + +@propagate_inbounds getindex(a::FieldArray, i::Int) = getfield(a, i) +@propagate_inbounds setindex!(a::FieldArray, x, i::Int) = setfield!(a, i, x) + +Base.cconvert(::Type{<:Ptr}, a::FieldArray) = Base.RefValue(a) +Base.unsafe_convert(::Type{Ptr{T}}, m::Base.RefValue{FA}) where {N,T,D,FA<:FieldArray{N,T,D}} = + Ptr{T}(Base.unsafe_convert(Ptr{FA}, m)) diff --git a/src/FieldVector.jl b/src/FieldVector.jl deleted file mode 100644 index 25a339d7..00000000 --- a/src/FieldVector.jl +++ /dev/null @@ -1,28 +0,0 @@ -""" - abstract FieldVector{N, T} <: StaticVector{N, T} - -Inheriting from this type will make it easy to create your own vector types. A `FieldVector` -will automatically define `getindex` and `setindex!` appropriately. An immutable -`FieldVector` will be as performant as an `SVector` of similar length and element type, -while a mutable `FieldVector` will behave similarly to an `MVector`. - -For example: - - struct Point3D <: FieldVector{3, Float64} - x::Float64 - y::Float64 - z::Float64 - end -""" -abstract type FieldVector{N, T} <: StaticVector{N, T} end - -# Is this a good idea?? Should people just define constructors that accept tuples? -@inline (::Type{FV})(x::Tuple{Vararg{Any, N}}) where {N, FV <: FieldVector{N}} = FV(x...) - -@propagate_inbounds getindex(v::FieldVector, i::Int) = getfield(v, i) -@propagate_inbounds setindex!(v::FieldVector, x, i::Int) = setfield!(v, i, x) - -# See #53 -Base.cconvert(::Type{<:Ptr}, v::FieldVector) = Base.RefValue(v) -Base.unsafe_convert(::Type{Ptr{T}}, m::Base.RefValue{FV}) where {N,T,FV<:FieldVector{N,T}} = - Ptr{T}(Base.unsafe_convert(Ptr{FV}, m)) diff --git a/src/StaticArrays.jl b/src/StaticArrays.jl index 7ce955eb..75a1a1a1 100644 --- a/src/StaticArrays.jl +++ b/src/StaticArrays.jl @@ -29,7 +29,7 @@ export SOneTo export StaticScalar, StaticArray, StaticVector, StaticMatrix export Scalar, SArray, SVector, SMatrix export MArray, MVector, MMatrix -export FieldVector +export FieldVector, FieldMatrix, FieldArray export SizedArray, SizedVector, SizedMatrix export SDiagonal export SHermitianCompact @@ -71,7 +71,7 @@ For mutable containers you may also need to define the following: - In some cases, a zero-parameter constructor, `MyStaticArray{...}()` for unintialized data is assumed to exist. -(see also `SVector`, `SMatrix`, `SArray`, `MVector`, `MMatrix`, `MArray`, `SizedArray` and `FieldVector`) +(see also `SVector`, `SMatrix`, `SArray`, `MVector`, `MMatrix`, `MArray`, `SizedArray`, `FieldVector`, `FieldMatrix` and `FieldArray`) """ abstract type StaticArray{S <: Tuple, T, N} <: AbstractArray{T, N} end const StaticScalar{T} = StaticArray{Tuple{}, T, 0} @@ -101,7 +101,7 @@ include("util.jl") include("traits.jl") include("SUnitRange.jl") -include("FieldVector.jl") +include("FieldArray.jl") include("SArray.jl") include("SMatrix.jl") include("SVector.jl") diff --git a/test/FieldMatrix.jl b/test/FieldMatrix.jl new file mode 100644 index 00000000..7eb6ef35 --- /dev/null +++ b/test/FieldMatrix.jl @@ -0,0 +1,104 @@ +@testset "FieldMatrix" begin + @testset "Immutable Tensor3x3" begin + eval(quote + struct Tensor3x3 <: FieldMatrix{3, 3, Float64} + xx::Float64 + yx::Float64 + zx::Float64 + xy::Float64 + yy::Float64 + zy::Float64 + xz::Float64 + yz::Float64 + zz::Float64 + end + + StaticArrays.similar_type(::Type{Tensor3x3}, ::Type{Float64}, s::Size{(3,3)}) = Tensor3x3 + end) + + p = Tensor3x3(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0) + + @test_throws Exception p[2] = 4.0 + @test_throws Exception p[2, 3] = 6.0 + + @test size(p) === (3,3) + @test length(p) === 9 + @test eltype(p) === Float64 + + @testinf Tuple(p) === (1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0) + + @test @inferred(p + p) === Tensor3x3(2.0, 4.0, 6.0, 8.0, 10.0, 12.0, 14.0, 16.0, 18.0) + + @test (p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8], p[9]) === (p.xx, p.yx, p.zx, p.xy, p.yy, p.zy, p.xz, p.yz, p.zz) + + m = @SMatrix [2.0 0.0 0.0; + 0.0 2.0 0.0; + 0.0 0.0 2.0] + + @test @inferred(p*m) === Tensor3x3(2.0, 4.0, 6.0, 8.0, 10.0, 12.0, 14.0, 16.0, 18.0) + + @test @inferred(similar_type(Tensor3x3)) == Tensor3x3 + @test @inferred(similar_type(Tensor3x3, Float64)) == Tensor3x3 + @test @inferred(similar_type(Tensor3x3, Float32)) == SMatrix{3,3,Float32,9} + @test @inferred(similar_type(Tensor3x3, Size(4,4))) == SMatrix{4,4,Float64,16} + @test @inferred(similar_type(Tensor3x3, Float32, Size(4,4))) == SMatrix{4,4,Float32,16} + + # Issue 146 + @test [[Tensor3x3(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0)]; [Tensor3x3(2.0, 4.0, 6.0, 8.0, 10.0, 12.0, 14.0, 16.0, 18.0)]]::Vector{Tensor3x3} == [Tensor3x3(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0), Tensor3x3(2.0, 4.0, 6.0, 8.0, 10.0, 12.0, 14.0, 16.0, 18.0)] + + # Issue 342 + @test_throws DimensionMismatch("No precise constructor for Tensor3x3 found. Length of input was 10.") Tensor3x3(1,2,3,4,5,6,7,8,9,10) + end + + @testset "Mutable Tensor2x2" begin + eval(quote + mutable struct Tensor2x2{T} <: FieldMatrix{2, 2, T} + xx::T + yx::T + xy::T + yy::T + end + + StaticArrays.similar_type(::Type{<:Tensor2x2}, ::Type{T}, s::Size{(2,2)}) where {T} = Tensor2x2{T} + end) + + p = Tensor2x2(0.0, 0.0, 0.0, 0.0) + p[1,1] = 1.0 + p[2,1] = 2.0 + + @test size(p) === (2,2) + @test length(p) === 4 + @test eltype(p) === Float64 + + @testinf Tuple(p) === (1.0, 2.0, 0.0, 0.0) + + @test (p[1], p[2], p[3], p[4]) === (p.xx, p.yx, p.xy, p.yy) + @test (p[1], p[2], p[3], p[4]) === (1.0, 2.0, 0.0, 0.0) + + m = @SMatrix [2.0 0.0; + 0.0 2.0] + + @test @inferred(p*m)::Tensor2x2 == Tensor2x2(2.0, 4.0, 0.0, 0.0) + + @test @inferred(similar_type(Tensor2x2{Float64})) == Tensor2x2{Float64} + @test @inferred(similar_type(Tensor2x2{Float64}, Float32)) == Tensor2x2{Float32} + @test @inferred(similar_type(Tensor2x2{Float64}, Size(3,3))) == SMatrix{3,3,Float64,9} + @test @inferred(similar_type(Tensor2x2{Float64}, Float32, Size(4,4))) == SMatrix{4,4,Float32,16} + end + + @testset "FieldMatrix with Tuple fields" begin + # verify that having a field which is itself a Tuple + # doesn't break anything + + eval(quote + struct TupleField2 <: FieldMatrix{1, 1, NTuple{2, Int}} + x::NTuple{2, Int} + end + end) + + x = TupleField2((1,2)) + @test length(x) == 1 + @test length(x[1]) == 2 + @test x.x == (1, 2) + end +end diff --git a/test/FieldVector.jl b/test/FieldVector.jl index 55797322..72cf249e 100644 --- a/test/FieldVector.jl +++ b/test/FieldVector.jl @@ -50,7 +50,7 @@ y::T end - StaticArrays.similar_type(::Type{P2D}, ::Type{T}, s::Size{(2,)}) where {P2D<:Point2D,T} = Point2D{T} + StaticArrays.similar_type(::Type{<:Point2D}, ::Type{T}, s::Size{(2,)}) where {T} = Point2D{T} end) p = Point2D(0.0, 0.0) @@ -78,7 +78,7 @@ end @testset "FieldVector with Tuple fields" begin - # verify that having a field which is itself a Tuple + # verify that having a field which is itself a Tuple # doesn't break anything eval(quote diff --git a/test/runtests.jl b/test/runtests.jl index 085d58a8..380545f9 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -19,6 +19,7 @@ include("MMatrix.jl") include("SArray.jl") include("MArray.jl") include("FieldVector.jl") +include("FieldMatrix.jl") include("Scalar.jl") include("SUnitRange.jl") include("SizedArray.jl")