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
2017-08-05 11:33:43 +03:00
type Element { E <: AbstractBasis }
2016-05-25 02:44:29 +03:00
id :: Int
2016-05-20 04:39:51 +03:00
connectivity :: Vector { Int }
2016-05-25 22:33:47 +03:00
integration_points :: Vector { IP }
2016-11-15 22:01:00 +02:00
fields :: Dict { String , Field }
2016-05-20 04:39:51 +03:00
properties :: E
end
2016-12-15 09:15:26 +02:00
""" Construct a new element of type E.
2016-08-04 13:15:19 +03:00
2016-12-15 09:15:26 +02:00
Examples
--------
julia> element = Element(Tri3, [1, 2, 3])
"""
2017-08-05 11:33:43 +03:00
function Element { E <: AbstractBasis } ( :: Type { E } , connectivity :: Vector { Int } )
2016-10-09 13:52:21 +03:00
return Element { E } ( - 1 , connectivity , [ ] , Dict ( ) , E ( ) )
2016-05-20 04:39:51 +03:00
end
2017-08-05 11:33:43 +03:00
"""
length(element::Element)
Return the number of nodes in element.
"""
function length { B } ( element :: Element { B } )
return length ( B )
end
function size { B } ( element :: Element { B } )
return size ( B )
end
2016-07-10 04:26:21 +03:00
function getindex ( element :: Element , field_name :: AbstractString )
2016-06-27 16:11:33 +03:00
return element . fields [ field_name ]
end
2016-07-10 04:26:21 +03:00
function setindex! ( element :: Element , data :: Field , field_name )
2016-06-27 16:11:33 +03:00
element . fields [ field_name ] = data
2016-05-20 04:39:51 +03:00
end
2017-01-30 12:28:33 +02:00
function get_element_type { E } ( element :: Element { E } )
return E
end
function get_element_id { E } ( element :: Element { E } )
return element . id
end
function is_element_type { E } ( element :: Element { E } , element_type )
2017-08-18 17:13:25 +03:00
return E === element_type
2017-01-30 12:28:33 +02:00
end
function filter_by_element_type ( element_type , elements )
return filter ( element -> is_element_type ( element , element_type ) , elements )
end
2017-08-18 18:35:55 +03:00
"""
group_by_element_type(elements::Vector{Element})
Given a vector of elements, group elements by element type to several vectors.
Returns a dictionary, where key is the element type and value is a vector
containing all elements of type `element_type`.
"""
function group_by_element_type ( elements :: Vector { Element } )
results = Dict { DataType , Any } ( )
basis_types = map ( element -> typeof ( element . properties ) , elements )
for basis in unique ( basis_types )
element_type = Element { basis }
subset = filter ( element -> isa ( element , element_type ) , elements )
results [ element_type ] = convert ( Vector { element_type } , subset )
end
return results
end
2016-07-10 04:26:21 +03:00
function setindex! ( element :: Element , data :: Function , field_name )
2016-07-07 18:00:36 +03:00
if method_exists ( data , Tuple { Element , Vector , Float64 } )
# create enclosure to pass element as argument
function wrapper_ ( ip , time )
return data ( element , ip , time )
end
field = Field ( wrapper_ )
else
field = Field ( data )
end
element . fields [ field_name ] = field
end
2016-07-10 04:26:21 +03:00
function setindex! ( element :: Element , data , field_name )
2016-05-20 04:39:51 +03:00
element . fields [ field_name ] = Field ( data )
end
2016-11-13 13:24:08 +02:00
""" Return a Field object from element.
Examples
--------
>>> element = Element(Seg2, [1, 2])
>>> data = Dict(1 => 1.0, 2 => 2.0)
>>> update!(element, " my field " , data)
>>> element( " my field " )
"""
function ( element :: Element ) ( field_name :: String )
2016-07-01 02:55:56 +03:00
return element [ field_name ]
end
2016-11-13 13:24:08 +02:00
""" Return a Field object from element and interpolate in time direction.
Examples
--------
>>> element = Element(Seg2, [1, 2])
>>> data1 = Dict(1 => 1.0, 2 => 2.0)
>>> data2 = Dict(1 => 2.0, 2 => 3.0)
>>> update!(element, " my field " , 0.0 => data1, 1.0 => data2)
>>> element( " my field " , 0.5)
"""
function ( element :: Element ) ( field_name :: String , time :: Float64 )
2016-05-24 19:05:25 +03:00
return element [ field_name ] ( time )
end
2016-11-13 13:24:08 +02:00
function last ( element :: Element , field_name :: String )
2016-07-01 02:55:56 +03:00
return last ( element [ field_name ] )
end
2016-11-13 13:24:08 +02:00
function ( element :: Element ) ( ip , time :: Float64 = 0.0 )
2016-06-27 16:11:33 +03:00
return get_basis ( element , ip , time )
2016-05-20 04:39:51 +03:00
end
2016-10-01 13:37:15 +03:00
"""
Examples
julia> el = Element(Quad4, [1, 2, 3, 4]);
julia> el([0.0, 0.0], 0.0, 1)
1x4 Array{Float64,2}:
0.25 0.25 0.25 0.25
julia> el([0.0, 0.0], 0.0, 2)
2x8 Array{Float64,2}:
2016-10-02 17:34:05 +03:00
0.25 0.0 0.25 0.0 0.25 0.0 0.25 0.0
2016-10-01 13:37:15 +03:00
0.0 0.25 0.0 0.25 0.0 0.25 0.0 0.25
"""
2016-11-13 13:24:08 +02:00
function ( element :: Element ) ( ip , time :: Float64 , dim :: Int )
2016-10-01 13:37:15 +03:00
dim == 1 && return get_basis ( element , ip , time )
2017-01-30 12:28:33 +02:00
Ni = vec ( get_basis ( element , ip , time ) )
2016-10-01 13:37:15 +03:00
N = zeros ( dim , length ( element ) * dim )
for i = 1 : dim
N [ i , i : dim :end ] += Ni
end
return N
end
2016-11-13 13:24:08 +02:00
function ( element :: Element ) ( ip , time :: Float64 , :: Type { Val { :Jacobian } } )
2016-08-04 13:15:19 +03:00
X = element ( " geometry " , time )
2016-05-25 22:33:47 +03:00
dN = get_dbasis ( element , ip , time )
2016-08-04 13:15:19 +03:00
nbasis = length ( element )
if isa ( X . data , Vector )
J = sum ( [ kron ( dN [ : , i ] , X [ i ] ' ) for i = 1 : nbasis ] )
else
c = get_connectivity ( element )
J = sum ( [ kron ( dN [ : , i ] , X [ c [ i ] ] ' ) for i = 1 : nbasis ] )
end
2016-05-20 04:39:51 +03:00
return J
end
2016-11-13 13:24:08 +02:00
function ( element :: Element ) ( ip , time :: Float64 , :: Type { Val { :detJ } } )
2016-05-25 22:33:47 +03:00
J = element ( ip , time , Val { :Jacobian } )
2016-05-22 17:00:01 +03:00
n , m = size ( J )
if n == m # volume element
return det ( J )
end
JT = transpose ( J )
if size ( JT , 2 ) == 1 # boundary of 2d problem, || ∂X/∂ξ ||
return norm ( JT )
else # manifold on 3d problem, || ∂X/∂ξ₁ × ∂X/∂ξ₂ ||
return norm ( cross ( JT [ : , 1 ] , JT [ : , 2 ] ) )
end
end
2016-11-13 13:24:08 +02:00
function ( element :: Element ) ( ip , time :: Float64 , :: Type { Val { :Grad } } )
2016-05-25 22:33:47 +03:00
J = element ( ip , time , Val { :Jacobian } )
return inv ( J ) * get_dbasis ( element , ip , time )
2016-05-20 04:39:51 +03:00
end
2016-11-13 13:24:08 +02:00
function ( element :: Element ) ( field_name :: String , ip , time :: Float64 , :: Type { Val { :Grad } } )
2016-06-27 16:11:33 +03:00
return element ( ip , time , Val { :Grad } ) * element [ field_name ] ( time )
2016-05-25 22:33:47 +03:00
end
2016-11-13 13:24:08 +02:00
function ( element :: Element ) ( field_name :: String , ip , time :: Float64 )
2016-06-27 16:11:33 +03:00
field = element [ field_name ]
2016-07-04 03:29:24 +03:00
return element ( field , ip , time )
2016-06-27 16:11:33 +03:00
end
2016-11-13 13:24:08 +02:00
function ( element :: Element ) ( field :: DCTI , ip , time :: Float64 )
2016-06-27 16:11:33 +03:00
return field . data
end
2016-11-13 13:24:08 +02:00
function ( element :: Element ) ( field :: DCTV , ip , time :: Float64 )
2016-07-01 02:55:56 +03:00
return field ( time ) . data
end
2016-11-13 13:24:08 +02:00
function ( element :: Element ) ( field :: CVTV , ip , time :: Float64 )
2016-06-27 16:11:33 +03:00
return field ( ip , time )
end
2016-11-13 13:24:08 +02:00
function ( element :: Element ) ( field :: Field , ip , time :: Float64 )
2016-06-27 16:11:33 +03:00
field_ = field ( time )
2016-05-25 22:33:47 +03:00
basis = element ( ip , time )
n = length ( element )
2016-08-04 13:15:19 +03:00
if isa ( field_ . data , Vector )
m = length ( field_ )
if n != m
error ( " Error when trying to interpolate field $field at coords $ip and time $time : element length is $n and field length is $m , f = Nᵢfᵢ makes no sense! " )
end
return sum ( [ field_ [ i ] * basis [ i ] for i = 1 : n ] )
else
c = get_connectivity ( element )
return sum ( [ field_ [ c [ i ] ] * basis [ i ] for i = 1 : n ] )
2016-07-01 02:55:56 +03:00
end
2016-06-27 16:11:33 +03:00
end
2016-05-20 04:39:51 +03:00
2016-07-10 04:26:21 +03:00
function size ( element :: Element , dim )
2016-06-27 16:11:33 +03:00
return size ( element ) [ dim ]
2016-05-22 02:34:38 +03:00
end
2016-05-20 04:39:51 +03:00
""" Update element field based on a dictionary of nodal data and connectivity information.
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-11-28 10:09:48 +02:00
function update! { E } ( element :: Element { E } , field_name :: AbstractString , data :: Dict )
2016-09-12 03:39:33 +03:00
#element[field_name] = Field(data)
2016-11-28 10:09:48 +02:00
element_id = element . id
local_connectivity = get_connectivity ( element )
for i in local_connectivity
if ! haskey ( data , i )
ndata = length ( data )
critical ( " Unable to set field data $field_name for element $E with
id $element_id and connectivity $local_connectivity : no data for
node id $i found. Length of data dict = $ndata " )
end
end
local_data = [ data [ i ] for i in local_connectivity ]
element [ field_name ] = local_data
2016-05-20 04:39:51 +03:00
end
2016-07-10 04:26:21 +03:00
function update! { K , V } ( element :: Element , field_name , data :: Pair { Float64 , Dict { K , V } } )
2016-09-12 03:39:33 +03:00
time , field_data = data
element_data = V [ field_data [ i ] for i in get_connectivity ( element ) ]
update! ( element , field_name , time => element_data )
2016-06-27 16:11:33 +03:00
end
2016-10-01 13:37:15 +03:00
function update! ( element :: Element , field_name :: AbstractString , data :: Pair { Float64 , Vector { Any } } )
2016-06-27 16:11:33 +03:00
if haskey ( element , field_name )
update! ( element [ field_name ] , data )
else
element [ field_name ] = data
end
end
2016-10-01 13:37:15 +03:00
function update! ( element :: Element , field_name :: AbstractString , data :: Pair { Float64 , Vector { Int64 } } )
2016-06-27 16:11:33 +03:00
if haskey ( element , field_name )
update! ( element [ field_name ] , data )
else
element [ field_name ] = data
end
end
2016-10-01 13:37:15 +03:00
function update! ( element :: Element , field_name :: AbstractString , data :: Pair { Float64 , Vector { Float64 } } )
2016-07-14 12:43:41 +03:00
if haskey ( element , field_name )
update! ( element [ field_name ] , data )
else
element [ field_name ] = data
end
end
2016-10-01 13:37:15 +03:00
function update! ( element :: Element , field_name :: AbstractString , data :: Pair { Float64 , Vector { Vector { Float64 } } } )
2016-06-27 16:11:33 +03:00
if haskey ( element , field_name )
update! ( element [ field_name ] , data )
else
element [ field_name ] = data
end
end
2016-10-01 13:37:15 +03:00
function update! ( element :: Element , field_name :: AbstractString , data :: Pair { Float64 , Float64 } )
2016-06-27 16:11:33 +03:00
if haskey ( element , field_name )
update! ( element [ field_name ] , data )
else
element [ field_name ] = data
end
end
2016-07-10 04:26:21 +03:00
function update! ( element :: Element , field_name :: AbstractString , data :: Union { Float64 , Vector } )
2016-06-27 16:11:33 +03:00
if haskey ( element , field_name )
update! ( element [ field_name ] , data )
else
if length ( data ) != length ( element )
update! ( element , field_name , DCTI ( data ) )
2016-06-08 23:42:36 +03:00
else
element [ field_name ] = data
end
2016-06-02 22:58:54 +03:00
end
2016-05-20 04:39:51 +03:00
end
2016-06-17 02:10:17 +03:00
function update! ( element :: Element , datas :: Pair ... )
for ( field_name , data ) in datas
if haskey ( element , field_name )
update! ( element [ field_name ] , data )
else
element [ field_name ] = data
end
end
end
2016-11-15 22:01:00 +02:00
function update! ( element :: Element , field_name :: String , data :: Function )
2016-06-27 16:11:33 +03:00
element [ field_name ] = data
end
2016-11-15 22:01:00 +02:00
function update! ( element :: Element , field_name :: String , field :: Field )
2016-06-27 16:11:33 +03:00
element [ field_name ] = field
end
2016-11-15 22:01:00 +02:00
function update! ( elements :: Vector , field_name :: String , data )
2016-05-22 02:34:38 +03:00
for element in elements
update! ( element , field_name , data )
end
end
2016-05-22 00:22:17 +03:00
""" Check existence of field. """
2016-11-15 22:01:00 +02:00
function haskey ( element :: Element , field_name :: String )
2016-05-22 00:22:17 +03:00
haskey ( element . fields , field_name )
end
2016-05-20 04:39:51 +03:00
2016-05-22 02:34:38 +03:00
function get_connectivity ( element :: Element )
return element . connectivity
end
2017-07-23 18:42:04 +03:00
function get_integration_points { E } ( element :: Element { E } )
2016-05-27 17:45:12 +03:00
# first time initialize default integration points
if length ( element . integration_points ) == 0
ips = get_integration_points ( element . properties )
2017-08-13 21:56:52 +03:00
if E in ( Poi1 , Seg2 , Seg3 , NSeg )
element . integration_points = [ IP ( i , w , ( xi , ) ) for ( i , ( w , xi ) ) in enumerate ( ips ) ]
2017-07-23 18:42:04 +03:00
else
element . integration_points = [ IP ( i , w , xi ) for ( i , ( w , xi ) ) in enumerate ( ips ) ]
end
2016-05-27 17:45:12 +03:00
end
2016-05-25 22:33:47 +03:00
return element . integration_points
end
""" This is a special case, temporarily change order
of integration scheme mainly for mass matrix.
"""
2017-07-23 18:42:04 +03:00
function get_integration_points { E } ( element :: Element { E } , change_order :: Int )
ips = get_integration_points ( element . properties , Val { change_order } )
2017-08-13 21:56:52 +03:00
if E in ( Poi1 , Seg2 , Seg3 , NSeg )
return [ IP ( i , w , ( xi , ) ) for ( i , ( w , xi ) ) in enumerate ( ips ) ]
2017-07-23 18:42:04 +03:00
else
return [ IP ( i , w , xi ) for ( i , ( w , xi ) ) in enumerate ( ips ) ]
end
2016-05-25 22:33:47 +03:00
end
2016-05-22 02:34:38 +03:00
""" Return dual basis transformation matrix Ae. """
2016-07-07 18:00:36 +03:00
function get_dualbasis ( element :: Element , time :: Float64 , order = 1 )
2016-05-22 02:34:38 +03:00
nnodes = length ( element )
De = zeros ( nnodes , nnodes )
Me = zeros ( nnodes , nnodes )
2016-07-07 18:00:36 +03:00
for ip in get_integration_points ( element , order )
2016-05-25 22:33:47 +03:00
detJ = element ( ip , time , Val { :detJ } )
w = ip . weight * detJ
N = element ( ip , time )
De += w * diagm ( vec ( N ) )
Me += w * N ' * N
2016-05-22 02:34:38 +03:00
end
return De , Me , De * inv ( Me )
end
2016-07-07 18:00:36 +03:00
""" Find inverse isoparametric mapping of element. """
function get_local_coordinates ( element :: Element , X :: Vector , time :: Float64 ; max_iterations = 10 , tolerance = 1.0e-6 )
haskey ( element , " geometry " ) || error ( " element geometry not defined, cannot calculate inverse isoparametric mapping " )
dim = size ( element , 1 )
dim == length ( X ) || error ( " manifolds not supported. " )
xi = zeros ( dim )
dX = element ( " geometry " , xi , time ) - X
for i = 1 : max_iterations
J = element ( xi , time , Val { :Jacobian } ) '
xi -= J \ dX
dX = element ( " geometry " , xi , time ) - X
norm ( dX ) < tolerance && return xi
2015-11-30 16:04:13 +02:00
end
2016-07-07 18:00:36 +03:00
info ( " X = $X , dX = $dX , xi = $xi " )
error ( " Unable to find inverse isoparametric mapping for element $element for X = $X " )
2015-11-30 16:04:13 +02:00
end
2016-07-07 18:00:36 +03:00
""" Test is X inside element. """
function inside { E } ( element :: Element { E } , X , time )
xi = get_local_coordinates ( element , X , time )
return inside ( E , xi )
2016-01-01 15:00:18 +02:00
end