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
2016-05-20 04:39:51 +03:00
type Element { E <: AbstractElement }
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-07-10 04:26:21 +03:00
fields :: Dict { AbstractString , Field }
2016-05-20 04:39:51 +03:00
properties :: E
end
2016-05-27 17:45:12 +03:00
function Element { E <: AbstractElement } ( :: Type { E } , connectivity = [ ] , integration_points = [ ] , id = - 1 , fields = Dict ( ) , properties ... )
2016-05-25 22:33:47 +03:00
variant = E ( properties ... )
2016-05-27 17:45:12 +03:00
element = Element { E } ( id , connectivity , integration_points , fields , variant )
return element
2016-05-20 04:39:51 +03:00
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
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-07-10 04:26:21 +03:00
function call ( element :: Element , field_name )
2016-07-01 02:55:56 +03:00
return element [ field_name ]
end
2016-07-10 04:26:21 +03:00
function call ( element :: Element , field_name , time )
2016-05-24 19:05:25 +03:00
return element [ field_name ] ( time )
end
2016-07-10 04:26:21 +03:00
function last ( element :: Element , field_name :: AbstractString )
2016-07-01 02:55:56 +03:00
return last ( element [ field_name ] )
end
2016-07-10 04:26:21 +03:00
function call ( 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-07-10 04:26:21 +03:00
function call ( element :: Element , ip , time :: Float64 , :: Type { Val { :Jacobian } } )
2016-05-20 04:39:51 +03:00
X = element [ " geometry " ] ( time )
2016-05-25 22:33:47 +03:00
dN = get_dbasis ( element , ip , time )
2016-05-20 04:39:51 +03:00
J = sum ( [ kron ( dN [ : , i ] , X [ i ] ' ) for i = 1 : length ( X ) ] )
return J
end
2016-07-10 04:26:21 +03:00
function call ( 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-07-10 04:26:21 +03:00
function call ( 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-07-10 04:26:21 +03:00
function call ( element :: Element , field_name :: AbstractString , 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-07-03 05:01:18 +03:00
function call ( element :: Element , field :: Field , time )
return field ( time )
end
function call ( element :: Element , field :: DCTI , time )
return field . data
end
2016-07-10 04:26:21 +03:00
function call ( element :: Element , field_name :: AbstractString , time )
2016-07-03 05:01:18 +03:00
field = element [ field_name ]
2016-07-04 03:29:24 +03:00
return element ( field , time )
2016-05-27 17:45:12 +03:00
end
2016-07-10 04:26:21 +03:00
function call ( element :: Element , field_name :: AbstractString , 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
function call ( element :: Element , field :: DCTI , ip , time :: Float64 )
return field . data
end
2016-07-01 02:55:56 +03:00
function call ( element :: Element , field :: DCTV , ip , time :: Float64 )
return field ( time ) . data
end
2016-06-27 16:11:33 +03:00
function call ( element :: Element , field :: CVTV , ip , time :: Float64 )
return field ( ip , time )
end
function call ( element :: Element , field :: Field , ip , time :: Float64 )
field_ = field ( time )
2016-05-25 22:33:47 +03:00
basis = element ( ip , time )
n = length ( element )
2016-06-27 16:11:33 +03:00
m = length ( field_ )
2016-07-01 02:55:56 +03:00
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
2016-06-27 16:11:33 +03:00
return sum ( [ field_ [ i ] * basis [ i ] for i = 1 : n ] )
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-07-10 04:26:21 +03:00
function update! ( element :: Element , field_name , data :: Dict )
2016-05-20 04:39:51 +03:00
element [ field_name ] = [ data [ i ] for i in get_connectivity ( element ) ]
end
2016-07-10 04:26:21 +03:00
function update! { K , V } ( element :: Element , field_name , data :: Pair { Float64 , Dict { K , V } } )
2016-06-27 16:11: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 )
end
2016-07-10 04:26:21 +03:00
function update! ( element :: Element , field_name :: AbstractString , datas :: Union { Real , Vector , Pair { Float64 , Union { Float64 , Real , Vector { Any } } } } ... )
2016-06-08 23:42:36 +03:00
for data in datas
if haskey ( element , field_name )
update! ( element [ field_name ] , data )
2016-06-27 16:11:33 +03:00
else
if length ( data ) != length ( element )
update! ( element , field_name , DCTI ( data ) )
else
element [ field_name ] = data
end
end
end
end
2016-07-10 04:26:21 +03:00
function update! ( element :: Element , field_name , datas :: Pair ... )
2016-07-01 02:55:56 +03:00
for data in datas
update! ( element , field_name , data )
end
end
2016-07-10 04:26:21 +03:00
function update! ( element :: Element , field_name , 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-07-10 04:26:21 +03:00
function update! ( element :: Element , field_name , 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-07-10 04:26:21 +03:00
function update! ( element :: Element , field_name , 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-07-10 04:26:21 +03:00
function update! ( element :: Element , field_name , 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-07-10 04:26:21 +03:00
function update! ( element :: Element , field_name , data :: Function )
2016-06-27 16:11:33 +03:00
element [ field_name ] = data
end
2016-07-10 04:26:21 +03:00
function update! ( element :: Element , field_name , field :: Field )
2016-06-27 16:11:33 +03:00
element [ field_name ] = field
end
2016-07-10 04:26:21 +03:00
function update! ( elements :: Vector , field_name , 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. """
function haskey ( element :: Element , field_name )
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
2016-05-25 22:33:47 +03:00
function get_integration_points ( element :: Element )
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 )
element . integration_points = [ IP ( i , w , xi ) for ( i , ( w , xi ) ) in enumerate ( ips ) ]
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.
"""
function get_integration_points ( element :: Element , change_order :: Int )
order = get_integration_order ( element . properties )
order += change_order
2016-06-01 20:43:25 +03:00
ips = get_integration_points ( element . properties , order )
2016-05-25 22:33:47 +03:00
return [ IP ( i , w , xi ) for ( i , ( w , xi ) ) in enumerate ( ips ) ]
end
2016-05-22 02:34:38 +03:00
function get_gdofs ( element :: Element )
return get_gdofs ( element , 1 )
end
""" 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
2016-05-20 04:39:51 +03:00