Skip to content

Commit 943a896

Browse files
authored
Merge pull request #626 from cfbaptista/FieldMatrix
Add FieldMatrix: a rank-two tensor generalisation of FieldVector
2 parents b150a8c + cae2d1f commit 943a896

File tree

6 files changed

+221
-33
lines changed

6 files changed

+221
-33
lines changed

src/FieldArray.jl

+111
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
"""
2+
abstract FieldArray{N, T, D} <: StaticArray{N, T, D}
3+
4+
Inheriting from this type will make it easy to create your own rank-D tensor types. A `FieldArray`
5+
will automatically define `getindex` and `setindex!` appropriately. An immutable
6+
`FieldArray` will be as performant as an `SArray` of similar length and element type,
7+
while a mutable `FieldArray` will behave similarly to an `MArray`.
8+
9+
For example:
10+
11+
struct Stiffness <: FieldArray{Tuple{2,2,2,2}, Float64, 4}
12+
xxxx::Float64
13+
yxxx::Float64
14+
xyxx::Float64
15+
yyxx::Float64
16+
xxyx::Float64
17+
yxyx::Float64
18+
xyyx::Float64
19+
yyyx::Float64
20+
xxxy::Float64
21+
yxxy::Float64
22+
xyxy::Float64
23+
yyxy::Float64
24+
xxyy::Float64
25+
yxyy::Float64
26+
xyyy::Float64
27+
yyyy::Float64
28+
end
29+
30+
Note that you must define the fields of any `FieldArray` subtype in column major order. If you
31+
want to use an alternative ordering you will need to pay special attention in providing your
32+
own definitions of `getindex`, `setindex!` and tuple conversion.
33+
"""
34+
abstract type FieldArray{N, T, D} <: StaticArray{N, T, D} end
35+
36+
"""
37+
abstract FieldMatrix{N1, N2, T} <: FieldArray{Tuple{N1, N2}, 2}
38+
39+
Inheriting from this type will make it easy to create your own rank-two tensor types. A `FieldMatrix`
40+
will automatically define `getindex` and `setindex!` appropriately. An immutable
41+
`FieldMatrix` will be as performant as an `SMatrix` of similar length and element type,
42+
while a mutable `FieldMatrix` will behave similarly to an `MMatrix`.
43+
44+
For example:
45+
46+
struct Stress <: FieldMatrix{3, 3, Float64}
47+
xx::Float64
48+
yx::Float64
49+
zx::Float64
50+
xy::Float64
51+
yy::Float64
52+
zy::Float64
53+
xz::Float64
54+
yz::Float64
55+
zz::Float64
56+
end
57+
58+
Note that the fields of any subtype of `FieldMatrix` must be defined in column major order.
59+
This means that formatting of constructors for literal `FieldMatrix` can be confusing. For example
60+
61+
sigma = Stress(1.0, 2.0, 3.0,
62+
4.0, 5.0, 6.0,
63+
7.0, 8.0, 9.0)
64+
65+
3×3 Stress:
66+
1.0 4.0 7.0
67+
2.0 5.0 8.0
68+
3.0 6.0 9.0
69+
70+
71+
will give you the transpose of what the multi-argument formatting suggests. For clarity,
72+
you may consider using the alternative
73+
74+
sigma = Stress(@SArray[1.0 2.0 3.0;
75+
4.0 5.0 6.0;
76+
7.0 8.0 9.0])
77+
"""
78+
abstract type FieldMatrix{N1, N2, T} <: FieldArray{Tuple{N1, N2}, T, 2} end
79+
80+
"""
81+
abstract FieldVector{N, T} <: FieldArray{Tuple{N}, 1}
82+
83+
Inheriting from this type will make it easy to create your own vector types. A `FieldVector`
84+
will automatically define `getindex` and `setindex!` appropriately. An immutable
85+
`FieldVector` will be as performant as an `SVector` of similar length and element type,
86+
while a mutable `FieldVector` will behave similarly to an `MVector`.
87+
88+
For example:
89+
90+
struct Point3D <: FieldVector{3, Float64}
91+
x::Float64
92+
y::Float64
93+
z::Float64
94+
end
95+
"""
96+
abstract type FieldVector{N, T} <: FieldArray{Tuple{N}, T, 1} end
97+
98+
@inline function (::Type{FA})(x::Tuple{Vararg{Any, N}}) where {N, FA <: FieldArray}
99+
if length(FA) == length(x)
100+
FA(x...)
101+
else
102+
throw(DimensionMismatch("No precise constructor for $FA found. Length of input was $(length(x))."))
103+
end
104+
end
105+
106+
@propagate_inbounds getindex(a::FieldArray, i::Int) = getfield(a, i)
107+
@propagate_inbounds setindex!(a::FieldArray, x, i::Int) = setfield!(a, i, x)
108+
109+
Base.cconvert(::Type{<:Ptr}, a::FieldArray) = Base.RefValue(a)
110+
Base.unsafe_convert(::Type{Ptr{T}}, m::Base.RefValue{FA}) where {N,T,D,FA<:FieldArray{N,T,D}} =
111+
Ptr{T}(Base.unsafe_convert(Ptr{FA}, m))

src/FieldVector.jl

-28
This file was deleted.

src/StaticArrays.jl

+3-3
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ export SOneTo
2929
export StaticScalar, StaticArray, StaticVector, StaticMatrix
3030
export Scalar, SArray, SVector, SMatrix
3131
export MArray, MVector, MMatrix
32-
export FieldVector
32+
export FieldVector, FieldMatrix, FieldArray
3333
export SizedArray, SizedVector, SizedMatrix
3434
export SDiagonal
3535
export SHermitianCompact
@@ -71,7 +71,7 @@ For mutable containers you may also need to define the following:
7171
- In some cases, a zero-parameter constructor, `MyStaticArray{...}()` for unintialized data
7272
is assumed to exist.
7373
74-
(see also `SVector`, `SMatrix`, `SArray`, `MVector`, `MMatrix`, `MArray`, `SizedArray` and `FieldVector`)
74+
(see also `SVector`, `SMatrix`, `SArray`, `MVector`, `MMatrix`, `MArray`, `SizedArray`, `FieldVector`, `FieldMatrix` and `FieldArray`)
7575
"""
7676
abstract type StaticArray{S <: Tuple, T, N} <: AbstractArray{T, N} end
7777
const StaticScalar{T} = StaticArray{Tuple{}, T, 0}
@@ -101,7 +101,7 @@ include("util.jl")
101101
include("traits.jl")
102102

103103
include("SUnitRange.jl")
104-
include("FieldVector.jl")
104+
include("FieldArray.jl")
105105
include("SArray.jl")
106106
include("SMatrix.jl")
107107
include("SVector.jl")

test/FieldMatrix.jl

+104
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
@testset "FieldMatrix" begin
2+
@testset "Immutable Tensor3x3" begin
3+
eval(quote
4+
struct Tensor3x3 <: FieldMatrix{3, 3, Float64}
5+
xx::Float64
6+
yx::Float64
7+
zx::Float64
8+
xy::Float64
9+
yy::Float64
10+
zy::Float64
11+
xz::Float64
12+
yz::Float64
13+
zz::Float64
14+
end
15+
16+
StaticArrays.similar_type(::Type{Tensor3x3}, ::Type{Float64}, s::Size{(3,3)}) = Tensor3x3
17+
end)
18+
19+
p = Tensor3x3(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0)
20+
21+
@test_throws Exception p[2] = 4.0
22+
@test_throws Exception p[2, 3] = 6.0
23+
24+
@test size(p) === (3,3)
25+
@test length(p) === 9
26+
@test eltype(p) === Float64
27+
28+
@testinf Tuple(p) === (1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0)
29+
30+
@test @inferred(p + p) === Tensor3x3(2.0, 4.0, 6.0, 8.0, 10.0, 12.0, 14.0, 16.0, 18.0)
31+
32+
@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)
33+
34+
m = @SMatrix [2.0 0.0 0.0;
35+
0.0 2.0 0.0;
36+
0.0 0.0 2.0]
37+
38+
@test @inferred(p*m) === Tensor3x3(2.0, 4.0, 6.0, 8.0, 10.0, 12.0, 14.0, 16.0, 18.0)
39+
40+
@test @inferred(similar_type(Tensor3x3)) == Tensor3x3
41+
@test @inferred(similar_type(Tensor3x3, Float64)) == Tensor3x3
42+
@test @inferred(similar_type(Tensor3x3, Float32)) == SMatrix{3,3,Float32,9}
43+
@test @inferred(similar_type(Tensor3x3, Size(4,4))) == SMatrix{4,4,Float64,16}
44+
@test @inferred(similar_type(Tensor3x3, Float32, Size(4,4))) == SMatrix{4,4,Float32,16}
45+
46+
# Issue 146
47+
@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)]
48+
49+
# Issue 342
50+
@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)
51+
end
52+
53+
@testset "Mutable Tensor2x2" begin
54+
eval(quote
55+
mutable struct Tensor2x2{T} <: FieldMatrix{2, 2, T}
56+
xx::T
57+
yx::T
58+
xy::T
59+
yy::T
60+
end
61+
62+
StaticArrays.similar_type(::Type{<:Tensor2x2}, ::Type{T}, s::Size{(2,2)}) where {T} = Tensor2x2{T}
63+
end)
64+
65+
p = Tensor2x2(0.0, 0.0, 0.0, 0.0)
66+
p[1,1] = 1.0
67+
p[2,1] = 2.0
68+
69+
@test size(p) === (2,2)
70+
@test length(p) === 4
71+
@test eltype(p) === Float64
72+
73+
@testinf Tuple(p) === (1.0, 2.0, 0.0, 0.0)
74+
75+
@test (p[1], p[2], p[3], p[4]) === (p.xx, p.yx, p.xy, p.yy)
76+
@test (p[1], p[2], p[3], p[4]) === (1.0, 2.0, 0.0, 0.0)
77+
78+
m = @SMatrix [2.0 0.0;
79+
0.0 2.0]
80+
81+
@test @inferred(p*m)::Tensor2x2 == Tensor2x2(2.0, 4.0, 0.0, 0.0)
82+
83+
@test @inferred(similar_type(Tensor2x2{Float64})) == Tensor2x2{Float64}
84+
@test @inferred(similar_type(Tensor2x2{Float64}, Float32)) == Tensor2x2{Float32}
85+
@test @inferred(similar_type(Tensor2x2{Float64}, Size(3,3))) == SMatrix{3,3,Float64,9}
86+
@test @inferred(similar_type(Tensor2x2{Float64}, Float32, Size(4,4))) == SMatrix{4,4,Float32,16}
87+
end
88+
89+
@testset "FieldMatrix with Tuple fields" begin
90+
# verify that having a field which is itself a Tuple
91+
# doesn't break anything
92+
93+
eval(quote
94+
struct TupleField2 <: FieldMatrix{1, 1, NTuple{2, Int}}
95+
x::NTuple{2, Int}
96+
end
97+
end)
98+
99+
x = TupleField2((1,2))
100+
@test length(x) == 1
101+
@test length(x[1]) == 2
102+
@test x.x == (1, 2)
103+
end
104+
end

test/FieldVector.jl

+2-2
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@
5050
y::T
5151
end
5252

53-
StaticArrays.similar_type(::Type{P2D}, ::Type{T}, s::Size{(2,)}) where {P2D<:Point2D,T} = Point2D{T}
53+
StaticArrays.similar_type(::Type{<:Point2D}, ::Type{T}, s::Size{(2,)}) where {T} = Point2D{T}
5454
end)
5555

5656
p = Point2D(0.0, 0.0)
@@ -78,7 +78,7 @@
7878
end
7979

8080
@testset "FieldVector with Tuple fields" begin
81-
# verify that having a field which is itself a Tuple
81+
# verify that having a field which is itself a Tuple
8282
# doesn't break anything
8383

8484
eval(quote

test/runtests.jl

+1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ include("MMatrix.jl")
1919
include("SArray.jl")
2020
include("MArray.jl")
2121
include("FieldVector.jl")
22+
include("FieldMatrix.jl")
2223
include("Scalar.jl")
2324
include("SUnitRange.jl")
2425
include("SizedArray.jl")

0 commit comments

Comments
 (0)