2015-08-24 01:14:03 +03:00
# This file is a part of JuliaFEM.
# License is MIT: see https://github.com/JuliaFEM/JuliaFEM.jl/blob/master/LICENSE.md
2015-11-27 10:10:00 +02:00
abstract AbstractElement
2015-08-31 20:15:28 +03:00
2015-12-31 12:35:05 +02:00
type Element { E }
2015-11-27 10:10:00 +02:00
connectivity :: Vector { Int }
fields :: Dict { ASCIIString , Field }
2016-02-05 11:32:09 +02:00
# matrices to construct dual basis
D :: Matrix { Float64 }
M :: Matrix { Float64 }
A :: Matrix { Float64 }
2015-11-27 10:10:00 +02:00
end
2015-12-14 02:09:33 +02:00
function Base . size { E } ( :: Element { E } )
return size ( E )
end
2015-12-23 17:39:53 +02:00
function Base . size { E } ( :: Element { E } , i :: Int64 )
return size ( E ) [ i ]
end
2015-11-27 10:10:00 +02:00
function convert { E } ( :: Type { Element { E } } , connectivity :: Vector { Int } )
2016-02-05 11:32:09 +02:00
return Element { E } ( connectivity , Dict ( ) , Matrix ( ) , Matrix ( ) , Matrix ( ) )
2015-11-27 10:10:00 +02:00
end
2015-12-05 10:58:01 +02:00
function get_integration_points { E } ( element :: Element { E } , args ... )
return get_integration_points ( E , args ... )
2015-11-27 10:10:00 +02:00
end
2015-08-24 01:14:03 +03:00
2015-11-30 16:04:13 +02:00
function update_gauss_fields! ( element :: Element , data :: Vector { IntegrationPoint } , time :: Real )
if haskey ( element , " integration points " )
# push or update
if ! isapprox ( last ( element [ " integration points " ] ) . time , time )
push! ( element [ " integration points " ] , time => data )
else
last ( element [ " integration points " ] ) . data = data
end
else
# create
element [ " integration points " ] = Field ( time => data )
end
end
2015-09-10 02:20:34 +03:00
"""
2015-09-14 23:20:33 +03:00
Test routine for element. If this passes, element interface is properly
defined.
2015-09-10 02:20:34 +03:00
Parameters
----------
eltype::Type{Element}
Element to test
2015-08-31 20:15:28 +03:00
2015-09-10 02:20:34 +03:00
Raises
------
2015-09-14 23:20:33 +03:00
This uses FactCheck and throws exceptions if element is not passing all tests.
2015-09-10 02:20:34 +03:00
"""
2015-10-20 15:54:47 +03:00
function test_element ( element_type )
2015-11-01 18:44:50 +02:00
info ( " Testing element $element_type " )
2015-10-20 15:54:47 +03:00
local element
2015-10-27 06:37:58 +02:00
dim = nothing
n = nothing
try
dim , n = size ( element_type )
catch
2015-11-01 18:44:50 +02:00
error ( " Unable to determine element dimensions. Define Base.size(element::Type{ $elementtype }) = (dim, nbasis) where dim is spatial dimension of element and nbasis is number of basis functions of element. " )
2015-10-27 06:37:58 +02:00
end
2015-11-01 18:44:50 +02:00
info ( " element dimension: $dim x $n " )
2015-09-10 20:35:28 +03:00
2015-11-01 18:44:50 +02:00
info ( " Initializing element " )
2015-08-31 20:15:28 +03:00
try
2015-11-30 16:04:13 +02:00
element = Element { element_type } ( collect ( 1 : n ) )
2015-08-31 20:15:28 +03:00
catch
2015-11-01 18:44:50 +02:00
error ( """
2015-09-10 20:35:28 +03:00
Unable to create element with default constructor define function
$eltype (connectivity) which initializes this element. """ )
2015-09-10 02:20:34 +03:00
return false
2015-08-31 20:15:28 +03:00
end
2015-09-10 20:35:28 +03:00
2015-08-31 20:15:28 +03:00
# try to interpolate some scalar field
2015-11-30 16:04:13 +02:00
element [ " field1 " ] = range ( 1 , n )
2015-10-27 06:37:58 +02:00
# TODO: how to parametrize this?
2015-11-30 16:04:13 +02:00
element [ " geometry " ] = Vector { Float64 } [ [ 0.0 , 0.0 ] , [ 1.0 , 0.0 ] , [ 1.0 , 1.0 ] , [ 0.0 , 1.0 ] ]
2015-10-20 15:54:47 +03:00
# evaluate basis functions at middle point of element
2015-09-28 13:22:12 +03:00
mid = zeros ( dim )
2015-11-30 16:04:13 +02:00
val1 = element ( mid , 0.0 )
2015-11-01 18:44:50 +02:00
info ( " basis at $mid : $val1 " )
2015-11-30 16:04:13 +02:00
val2 = element ( " field1 " , mid , 0.0 )
2015-11-01 18:44:50 +02:00
info ( " field val at $mid : $val2 " )
2015-11-30 16:04:13 +02:00
val3 = element ( mid , 0.0 , Val { :grad } )
2015-11-11 00:52:16 +02:00
info ( " derivative of basis at $mid : \n $val3 " )
2015-11-30 16:04:13 +02:00
#val4 = element("field1", mid, Val{:grad})
#info("field val at $mid: $val4")
2015-08-31 20:15:28 +03:00
2015-11-01 18:44:50 +02:00
info ( " Element $element_type passed tests. " )
2015-08-31 20:15:28 +03:00
end
2015-10-28 04:29:14 +02:00
""" Get FieldSet from element. """
function Base . getindex ( element :: Element , field_name )
2015-11-11 00:52:16 +02:00
return element . fields [ field_name ]
2015-10-28 04:29:14 +02:00
end
2015-11-27 10:10:00 +02:00
function Base . length { E } ( element :: Element { E } )
size ( E ) [ 2 ]
end
2015-11-23 03:17:15 +02:00
2015-11-11 00:52:16 +02:00
""" Add new Field to element.
2015-10-28 04:29:14 +02:00
Examples
--------
2015-11-11 00:52:16 +02:00
>>> element[ " temperature " ] = [1, 2, 3, 4]
>>> element[ " temperature " ] = (0.0, [0, 0, 0, 0]), (1.0, [1, 2, 3, 4])
>>> element[ " temperature " ] = (0.0 => [0, 0, 0, 0], 1.0 => [1, 2, 3, 4])
2015-10-28 04:29:14 +02:00
"""
2015-11-21 18:23:41 +02:00
function Base . setindex! ( element :: Element , data , name :: ASCIIString )
element . fields [ name ] = Field ( data )
2015-10-28 04:29:14 +02:00
end
2015-11-23 03:17:15 +02:00
function Base . setindex! ( element :: Element , field :: Field , name :: ASCIIString )
element . fields [ name ] = field
end
2015-11-21 18:23:41 +02:00
function Base . setindex! ( element :: Element , data :: Tuple , name :: ASCIIString )
element . fields [ name ] = Field ( data ... )
2015-11-11 00:52:16 +02:00
end
2015-10-26 05:40:41 +02:00
function get_connectivity ( el :: Element )
2015-11-11 00:52:16 +02:00
return el . connectivity
2015-10-26 05:40:41 +02:00
end
2015-09-14 23:20:33 +03:00
2015-11-23 03:17:15 +02:00
typealias VecOrIP Union { Vector , IntegrationPoint }
2015-12-04 07:27:40 +02:00
function call ( element :: Element , field_name :: ASCIIString , time :: Real , variation = nothing )
return isa ( variation , Void ) ? element [ field_name ] ( time ) : variation
end
2015-11-27 10:10:00 +02:00
function call ( element :: Element , field_name :: ASCIIString , xi :: VecOrIP , time :: Number , variation = nothing )
2015-12-04 07:27:40 +02:00
field = element ( field_name , time , variation )
# field = isa(variation, Void) ? element[field_name](time) : variation
2015-11-27 10:10:00 +02:00
basis = get_basis ( element )
return basis ( field , xi )
2015-11-23 03:17:15 +02:00
end
2015-11-27 10:10:00 +02:00
function call ( element :: Element , field_name :: ASCIIString , xi :: VecOrIP , time :: Number , :: Type { Val { :grad } } , variation = nothing )
2015-12-04 07:27:40 +02:00
# field = isa(variation, Void) ? element[field_name](time) : variation
field = element ( field_name , time , variation )
2015-11-27 10:10:00 +02:00
basis = get_basis ( element )
geom = element [ " geometry " ] ( time )
return basis ( geom , field , xi , Val { :grad } )
2015-11-23 03:17:15 +02:00
end
2015-11-27 10:10:00 +02:00
function call ( element :: Element , field_name :: ASCIIString , xi :: VecOrIP )
2015-11-30 16:04:13 +02:00
field = element [ field_name ]
basis = get_basis ( element )
return basis ( element [ field_name ] , xi )
2015-11-23 03:17:15 +02:00
end
2015-11-27 10:10:00 +02:00
function call ( element :: Element , field_name :: ASCIIString , xi :: VecOrIP , :: Type { Val { :grad } } )
2015-11-30 16:04:13 +02:00
field = element [ field_name ]
geom = element [ " geometry " ]
basis = get_basis ( element )
return basis ( geom , field , xi , Val { :grad } )
2015-11-23 03:17:15 +02:00
end
2015-11-27 10:10:00 +02:00
function call ( element :: Element , field_name :: ASCIIString , time :: Number )
2015-11-23 03:17:15 +02:00
return element [ field_name ] ( time )
end
2015-11-30 18:49:45 +02:00
function get_dbasis { E <: AbstractElement } ( :: Type { E } , xi :: Vector )
basis ( xi ) = vec ( get_basis ( E , xi ) )
return ForwardDiff . jacobian ( basis , xi , cache = autodiffcache ) '
end
2015-11-27 10:10:00 +02:00
function get_basis { E } ( element :: Element { E } , ip :: IntegrationPoint )
return get_basis ( E , ip . xi )
2015-11-23 03:17:15 +02:00
end
2015-11-30 16:04:13 +02:00
function get_basis { E } ( :: Type { Element { E } } , xi :: Vector { Float64 } )
return get_basis ( E , xi )
end
2015-11-27 15:06:55 +02:00
function get_basis { E } ( element :: Element { E } , xi :: Vector { Float64 } )
return get_basis ( E , xi )
end
function call { E } ( element :: Element { E } , xi :: VecOrIP , time :: Float64 = 0.0 )
2015-11-27 10:10:00 +02:00
return get_basis ( element , xi )
2015-11-23 03:17:15 +02:00
end
2015-11-11 00:52:16 +02:00
2016-02-05 11:32:09 +02:00
""" Return dual basis transformation matrix Ae. """
function get_dualbasis ( element :: Element , time :: Real )
2016-02-05 12:27:36 +02:00
if length ( element . A ) == 0
2016-02-03 20:39:03 +02:00
nnodes = size ( element , 2 )
2016-02-05 12:27:36 +02:00
De = zeros ( nnodes , nnodes )
Me = zeros ( nnodes , nnodes )
2016-02-03 20:39:03 +02:00
for ip in get_integration_points ( element , Val { 3 } )
2016-02-05 11:32:09 +02:00
w = ip . weight
2016-02-03 20:39:03 +02:00
J = get_jacobian ( element , ip , time )
2016-02-05 11:32:09 +02:00
JT = transpose ( J )
if size ( JT , 2 ) == 1 # plane problem
# || ∂X/∂ξ ||
w *= norm ( JT )
else
# || ∂X/∂ξ₁ × ∂X/∂ξ₂ ||
w *= norm ( cross ( JT [ : , 1 ] , JT [ : , 2 ] ) )
end
2016-02-03 20:39:03 +02:00
N = element ( ip , time )
De += w * diagm ( vec ( N ) )
Me += w * N ' * N
end
2016-02-05 12:27:36 +02:00
element . D = De
element . M = Me
2016-02-05 11:32:09 +02:00
element . A = De * inv ( Me )
2016-02-03 20:39:03 +02:00
end
2016-02-05 11:32:09 +02:00
return element . D , element . M , element . A
end
function call ( element :: Element , xi :: VecOrIP , time :: Real , :: Type { Val { :dualbasis } } )
De , Me , Ae = get_dualbasis ( element , time )
2016-02-03 20:39:03 +02:00
N = get_basis ( element , xi )
2016-02-05 11:32:09 +02:00
Phi = Ae * N '
2016-02-03 20:39:03 +02:00
return Phi '
end
2015-11-27 10:10:00 +02:00
function get_basis { E } ( element :: Element { E } )
basis = CVTI (
( xi :: Vector ) -> get_basis ( E , xi ) ,
( xi :: Vector ) -> get_dbasis ( E , xi ) )
return basis
2015-10-26 05:40:41 +02:00
end
2015-11-30 16:04:13 +02:00
function call { E } ( element :: Element { E } , xi :: VecOrIP , :: Type { Val { :grad } } )
basis = get_basis ( element )
geom = element [ " geometry " ]
return basis ( geom , xi , Val { :grad } )
end
2015-11-27 10:10:00 +02:00
function call { E } ( element :: Element { E } , xi :: VecOrIP , time :: Float64 , :: Type { Val { :grad } } )
basis = get_basis ( element )
2015-11-30 16:04:13 +02:00
return basis ( element [ " geometry " ] ( time ) , xi , Val { :grad } )
2015-08-24 01:14:03 +03:00
end
2015-11-27 10:10:00 +02:00
function call ( element :: Element , field_name :: ASCIIString )
return element [ field_name ]
2015-08-24 01:14:03 +03:00
end
2015-12-13 13:47:45 +02:00
""" Return the jacobian of element. """
function get_jacobian { E } ( element :: Element { E } , xi :: Vector { Float64 } , time :: Real )
2015-11-27 10:10:00 +02:00
X = element ( " geometry " , time )
2015-12-13 13:47:45 +02:00
dN = get_dbasis ( E , xi )
2015-12-04 07:27:40 +02:00
J = sum ( [ kron ( dN [ : , i ] , X [ i ] ' ) for i = 1 : length ( X ) ] )
2015-12-13 13:47:45 +02:00
return J
end
function get_jacobian { E } ( element :: Element { E } , ip :: IntegrationPoint , time :: Real )
return get_jacobian ( element , ip . xi , time )
end
""" Return the determinant of jacobian. """
function LinAlg . det { E <: AbstractElement } ( element :: Element { E } , xi :: Vector { Float64 } , time :: Real )
J = get_jacobian ( element , xi , time )
n , m = size ( J )
if n == m
2015-12-14 02:09:33 +02:00
warn ( " det(element, ip, time) is ambiguous: use J = get_jacobian(element, ip, time); det(J) instead. " )
2015-12-13 13:47:45 +02:00
return det ( J )
end
JT = transpose ( J )
2015-12-14 02:09:33 +02:00
if size ( JT , 2 ) == 1
warn ( " det(element, ip, time) is ambiguous: use J = get_jacobian(element, ip, time); norm(J) instead. " )
return norm ( JT )
else
warn ( " det(element, ip, time) is ambiguous: use J = get_jacobian(element, ip, time); norm(cross(...)) instead. " )
return norm ( cross ( JT [ : , 1 ] , JT [ : , 2 ] ) )
end
2015-12-13 13:47:45 +02:00
end
function LinAlg . det { E <: AbstractElement } ( element :: Element { E } , ip :: IntegrationPoint , time :: Real )
return det ( element , ip . xi , time )
2015-10-26 05:40:41 +02:00
end
2015-11-23 03:17:15 +02:00
2015-12-13 13:47:45 +02:00
2015-11-11 00:52:16 +02:00
""" Check does field exist. """
2015-10-26 05:40:41 +02:00
function Base . haskey ( element :: Element , what )
2015-10-27 06:37:58 +02:00
haskey ( element . fields , what )
2015-10-26 05:40:41 +02:00
end
2015-09-14 23:20:33 +03:00
2015-12-12 10:08:20 +02:00
""" Calculate local normal-tangential coordinates for element. """
function calculate_normal_tangential_coordinates! { E } ( element :: Element { E } , time :: Real )
ntcoords = Matrix [ ]
2016-02-03 20:39:03 +02:00
normals = Vector { Float64 } [ ]
2015-12-12 10:08:20 +02:00
refcoords = get_reference_element_coordinates ( E )
x = element ( " geometry " , time )
for xi in refcoords
dN = get_dbasis ( E , xi ) * x
2015-12-20 21:17:47 +02:00
n , m = size ( dN )
@assert n != m # if n == m -> this is not manifold
if m == 1 # plane case
tangent = dN / norm ( dN )
normal = [ - tangent [ 2 ] tangent [ 1 ] ] '
2016-02-03 20:39:03 +02:00
push! ( normals , vec ( normal ) )
2015-12-20 21:17:47 +02:00
push! ( ntcoords , [ normal tangent ] )
elseif m == 2
normal = cross ( dN [ : , 1 ] , dN [ : , 2 ] )
normal /= norm ( normal )
u1 = normal
j = indmax ( abs ( u1 ) )
v2 = zeros ( 3 )
v2 [ mod ( j , 3 ) + 1 ] = 1.0
u2 = v2 - dot ( u1 , v2 ) / dot ( v2 , v2 ) * v2
u3 = cross ( u1 , u2 )
tangent1 = u2 / norm ( u2 )
tangent2 = u3 / norm ( u3 )
push! ( ntcoords , [ normal tangent1 tangent2 ] )
2016-02-03 20:39:03 +02:00
push! ( normals , vec ( normal ) )
2015-12-20 21:17:47 +02:00
else
error ( " calculate_normal_tangential_coordinates!(): n= $n , m= $m " )
end
2015-12-12 10:08:20 +02:00
end
element [ " normal-tangential coordinates " ] = ntcoords
2016-02-03 20:39:03 +02:00
element [ " normals " ] = normals
2015-12-12 10:08:20 +02:00
end
2016-02-03 20:39:03 +02:00
2016-02-05 22:26:07 +02:00
""" Return list of nodes / connectivity points from a set of elements.
"""
function get_nodes ( elements :: Vector )
nodes = Set { Int64 } ( )
for element in elements
push! ( nodes , get_connectivity ( element ) ... )
end
nodes = sort ( collect ( nodes ) )
return nodes
end
2016-02-03 20:39:03 +02:00
""" Calculate normal-tangential coordinates for a set of elements.
Notes
-----
Average normals so that normals are unique in nodes.
"""
2016-02-05 22:26:07 +02:00
2016-02-03 20:39:03 +02:00
function calculate_normal_tangential_coordinates! ( elements :: Vector , time :: Real )
2016-02-05 22:26:07 +02:00
if size ( elements [ 1 ] , 1 ) == 1
return calculate_normal_tangential_coordinates! ( elements , time , Val { 2 } )
else
return calculate_normal_tangential_coordinates! ( elements , time , Val { 3 } )
end
end
""" Calculate normal-tangential coordinates for 2d case.
Notes
-----
n = (e₃×∂X/∂ξ) / || e₃×∂X/∂ξ || and e₃ = [0 0 1]
"""
function calculate_normal_tangential_coordinates! ( elements :: Vector , time :: Real , :: Type { Val { 2 } } )
nodes = get_nodes ( elements )
n = zeros ( 2 , maximum ( nodes ) )
Q = [ 0 - 1 ; 1 0 ]
2015-12-20 21:17:47 +02:00
for element in elements
2016-02-05 22:26:07 +02:00
gdofs = get_gdofs ( element , 1 )
for ip in get_integration_points ( element , Val { 3 } )
J = get_jacobian ( element , ip , time )
N = element ( ip , time )
n [ : , gdofs ] += ip . weight * Q * J ' * N
end
2015-12-20 21:17:47 +02:00
end
2016-02-03 20:39:03 +02:00
t = zeros ( n )
2016-02-05 22:26:07 +02:00
for i = 1 : size ( n , 2 )
n [ : , i ] = n [ : , i ] / norm ( n [ : , i ] )
t [ : , i ] = [ - n [ 2 , i ] , n [ 1 , i ] ]
end
for element in elements
node_ids = get_connectivity ( element )
R = Matrix { Float64 } [ [ n [ : , i ] t [ : , i ] ] for i in node_ids ]
element [ " normal-tangential coordinates " ] = R
element [ " normals " ] = Vector { Float64 } [ n [ : , i ] for i in node_ids ]
end
end
""" Calculate normal-tangential coordinates for 3d case.
"""
function calculate_normal_tangential_coordinates! ( elements :: Vector , time :: Real , :: Type { Val { 3 } } )
nodes = get_nodes ( elements )
n = zeros ( 3 , maximum ( nodes ) )
for element in elements
gdofs = get_gdofs ( element , 1 )
for ip in get_integration_points ( element , Val { 3 } )
J = transpose ( get_jacobian ( element , ip , time ) )
N = element ( ip , time )
c = reshape ( cross ( J [ : , 1 ] , J [ : , 2 ] ) , 3 , 1 )
n [ : , gdofs ] += ip . weight * c * N
end
end
t1 = zeros ( n )
t2 = zeros ( n )
for i = 1 : size ( n , 2 )
i in nodes || continue
n [ : , i ] = n [ : , i ] / norm ( n [ : , i ] )
u1 = n [ : , i ]
j = indmax ( abs ( n [ : , i ] ) )
v2 = zeros ( 3 )
v2 [ mod ( j , 3 ) + 1 ] = 1.0
u2 = v2 - dot ( u1 , v2 ) / dot ( v2 , v2 ) * v2
u3 = cross ( u1 , u2 )
t1 [ : , i ] = u2 / norm ( u2 )
t2 [ : , i ] = u3 / norm ( u3 )
2016-02-03 20:39:03 +02:00
end
for element in elements
node_ids = get_connectivity ( element )
2016-02-05 22:26:07 +02:00
Q = Matrix { Float64 } [ [ n [ : , i ] t1 [ : , i ] t2 [ : , i ] ] for i in node_ids ]
2016-02-03 20:39:03 +02:00
element [ " normal-tangential coordinates " ] = Q
2016-02-05 22:26:07 +02:00
element [ " normals " ] = Vector { Float64 } [ n [ : , i ] for i in node_ids ]
2016-02-03 20:39:03 +02:00
end
2015-12-20 21:17:47 +02:00
end
2015-12-12 10:08:20 +02:00
2016-02-01 09:11:33 +02:00
""" Update element field based on a dictionary of nodal data and connectivity information.
2016-01-01 15:00:18 +02:00
Examples
--------
julia> data = Dict(1 => [0.0, 0.0], 2 => [1.0, 2.0])
julia> element = Seg2([1, 2])
julia> update!(element, " geometry " , data)
As a result element now have time invariant (variable) vector field " geometry " with data ([0.0, 0.0], [1.0, 2.0]).
"""
2016-02-01 09:11:33 +02:00
function update! ( element :: Element , field_name :: ASCIIString , data :: Dict )
2015-12-17 15:33:51 +02:00
element [ field_name ] = [ data [ i ] for i in get_connectivity ( element ) ]
end
2016-01-01 15:00:18 +02:00
2016-02-01 09:11:33 +02:00
function update! ( element :: Element , field_name :: ASCIIString , data :: Union { Real , Vector , Pair } )
2016-01-01 15:00:18 +02:00
element [ field_name ] = data
end
""" Update values for several elements at once. """
# FIXME: with or without {T} ?
function update! { T } ( elements :: Vector { Element { T } } , field_name :: ASCIIString , data )
for element in elements
update! ( element , field_name , data )
end
end
function update! ( elements :: Vector { Element } , field_name :: ASCIIString , data )
2015-12-17 15:33:51 +02:00
for element in elements
2015-12-20 21:17:47 +02:00
update! ( element , field_name , data )
2015-12-17 15:33:51 +02:00
end
end