-
Notifications
You must be signed in to change notification settings - Fork 155
Expand file tree
/
Copy pathFieldArray.jl
More file actions
152 lines (123 loc) · 5.7 KB
/
FieldArray.jl
File metadata and controls
152 lines (123 loc) · 5.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
"""
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`.
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.
If you define a `FieldArray` which is parametric on the element type you should
consider defining `similar_type` as in the `FieldVector` example.
# 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
"""
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`.
Note that the fields of any subtype of `FieldMatrix` must be defined in column
major order unless you are willing to implement your own `getindex`.
If you define a `FieldMatrix` which is parametric on the element type you
should consider defining `similar_type` as in the `FieldVector` example.
# 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(SA[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`.
If you define a `FieldVector` which is parametric on the element type you
should consider defining `similar_type` to preserve your array type through
array operations as in the example below.
# Example
struct Vec3D{T} <: FieldVector{3, T}
x::T
y::T
z::T
end
StaticArrays.similar_type(::Type{<:Vec3D}, ::Type{T}, s::Size{(3,)}) where {T} = Vec3D{T}
"""
abstract type FieldVector{N, T} <: FieldArray{Tuple{N}, T, 1} end
@inline (::Type{FA})(x::Tuple) where {FA <: FieldArray} = construct_type(FA, x)(x...)
function construct_type(::Type{FA}, x) where {FA <: FieldArray}
has_size(FA) || error("$FA has no static size!")
length_match_size(FA, x)
return adapt_eltype(FA, x)
end
@propagate_inbounds getindex(a::FieldArray, i::Int) = getfield(a, i)
@propagate_inbounds setindex!(a::FieldArray, x, i::Int) = (setfield!(a, i, x); a)
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))
function similar_type(::Type{A}, ::Type{T}, S::Size) where {T,A<:FieldArray}
# We can preserve FieldArrays in array operations which do not change their `Size` and `eltype`.
has_eltype(A) && eltype(A) === T && has_size(A) && Size(A) === S && return A
# FieldArrays with parametric `eltype` would be adapted to the new `eltype` automatically.
A′ = Base.typeintersect(base_type(A), StaticArray{Tuple{Tuple(S)...},T,length(S)})
# But extra parameters are disallowed here. Also we check `fieldtypes` to make sure the result is valid.
isconcretetype(A′) && fieldtypes(A′) === ntuple(_ -> T, Val(prod(S))) && return A′
# Otherwise, we fallback to `S/MArray` based on it's mutability.
if ismutabletype(A)
return mutable_similar_type(T, S, length_val(S))
else
return default_similar_type(T, S, length_val(S))
end
end
# return `Union{}` for Union Type. Otherwise return the constructor with no parameters.
@pure base_type(@nospecialize(T::Type)) = (T′ = Base.unwrap_unionall(T);
T′ isa DataType ? T′.name.wrapper : Union{})
if VERSION < v"1.8"
fieldtypes(::Type{T}) where {T} = ntuple(i -> fieldtype(T, i), Val(fieldcount(T)))
@eval @pure function ismutabletype(@nospecialize(T::Type))
T′ = Base.unwrap_unionall(T)
T′ isa DataType && $(VERSION < v"1.7" ? :(T′.mutable) : :(T′.name.flags & 0x2 == 0x2))
end
end