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-09-28 13:22:12 +03:00
#=
R e l a t e d n o t e b o o k s
- - - - - - - - - - - - - - - - -
2 0 1 5 - 0 8 - 2 9 - d e v e l o p i n g - j u l i a f e m . i p y n b
=#
2015-08-31 20:15:28 +03:00
using FactCheck
2015-09-10 02:20:34 +03:00
using ForwardDiff
2015-08-31 20:15:28 +03:00
2015-08-25 00:21:05 +03:00
abstract Element
2015-08-24 01:14:03 +03:00
2015-10-20 15:54:47 +03:00
""" Get FieldSet from element. """
2015-10-26 05:40:41 +02:00
function Base . getindex ( element :: Element , field_name )
2015-10-27 06:37:58 +02:00
element . fields [ field_name ]
2015-10-20 15:54:47 +03:00
end
2015-09-10 02:20:34 +03:00
2015-10-20 15:54:47 +03:00
""" Add new FieldSet to element. """
2015-10-26 05:40:41 +02:00
function Base . setindex! ( element :: Element , fieldset :: FieldSet , fieldset_name )
2015-10-27 06:37:58 +02:00
fieldset . name = fieldset_name
2015-10-20 15:54:47 +03:00
element . fields [ fieldset . name ] = fieldset
end
function Base . push! ( element :: Element , fieldset :: FieldSet )
element [ fieldset . name ] = fieldset
end
2015-09-10 02:20:34 +03:00
2015-10-20 15:54:47 +03:00
#= E L E M E N T D E F I N I T I O N S
2015-09-10 02:20:34 +03:00
2015-10-20 15:54:47 +03:00
E x a m p l e
- - - - - - -
2015-09-10 20:35:28 +03:00
2015-09-14 23:20:33 +03:00
T h i s i s e x a m p l e h o w t o c r e a t e n e w e l e m e n t . T h i s i s c o m m e n t e d b e c a u s e I u s e c o d e
2015-09-10 20:35:28 +03:00
g e n e r a t i o n f o r s i m p l e e l e m e n t s l i k e L a g r a g e e l e m e n t s . F e e l f r e e t o u s e
c o d e g e n e r a t i o n b u t e l e m e n t s c a n b e o f c o u r s e c r e a t e d m a n u a l l y t o o !
2015-09-10 02:20:34 +03:00
2015-09-14 23:20:33 +03:00
a b s t r a c t C G < : E l e m e n t # c r e a t e n e w e l e m e n t f a m i l y " C o n t i n o u s G a l e r k i n "
2015-09-10 02:20:34 +03:00
t y p e Q u a d 4 < : C G
c o n n e c t i v i t y : : A r r a y { I n t , 1 }
f i e l d s : : D i c t { A n y , A n y }
e n d
" " " D e f a u l t c o n t r u c t o r . " " "
Q u a d 4 ( c o n n e c t i v i t y ) = Q u a d 4 ( c o n n e c t i v i t y , D i c t { A n y , A n y } ( ) )
" " " R e t u r n n u m b e r o f b a s i s f u n c t i o n s o f t h i s e l e m e n t . " " "
2015-09-10 20:35:28 +03:00
g e t _ n u m b e r _ o f _ b a s i s _ f u n c t i o n s ( e l : : T y p e { Q u a d 4 } ) = 4
2015-09-10 02:20:34 +03:00
" " " R e t u r n e l e m e n t d i m e n s i o n ( l e n g t h o f x i v e c t o r ) . " " "
g e t _ e l e m e n t _ d i m e n s i o n ( e l : : T y p e { Q u a d 4 } ) = 2
" " " R e t u r n b a s i s f u n c t i o n s f o r t h i s e l e m e n t ( x i d i m = 2 , f u n c t i o n s = 4 ) . " " "
f u n c t i o n g e t _ b a s i s ( e l : : Q u a d 4 , x i )
[ ( 1 - x i [ 1 ] ) * ( 1 - x i [ 2 ] ) / 4
( 1 + x i [ 1 ] ) * ( 1 - x i [ 2 ] ) / 4
( 1 + x i [ 1 ] ) * ( 1 + x i [ 2 ] ) / 4
( 1 - x i [ 1 ] ) * ( 1 + x i [ 2 ] ) / 4 ]
e n d
2015-08-31 20:15:28 +03:00
2015-09-10 02:20:34 +03:00
" " " R e t u r n p a r t i a l d e r i v a t i v e s o f b a s i s f u n c t i o n s . " " "
f u n c t i o n g e t _ d b a s i s d x i ( e l : : Q u a d 4 , x i )
[ - ( 1 - x i [ 2 ] ) / 4 . 0 - ( 1 - x i [ 1 ] ) / 4 . 0
( 1 - x i [ 2 ] ) / 4 . 0 - ( 1 + x i [ 1 ] ) / 4 . 0
( 1 + x i [ 2 ] ) / 4 . 0 ( 1 + x i [ 1 ] ) / 4 . 0
- ( 1 + x i [ 2 ] ) / 4 . 0 ( 1 - x i [ 1 ] ) / 4 . 0 ]
e n d
2015-09-10 20:35:28 +03:00
E n d o f e x a m p l e .
=#
2015-10-27 06:37:58 +02:00
# define size of your element as (dim, nbasis) tuple where first integer is spatial dimension and second is number of basis functions.
# Base.size(element::Type{Element}) = nothing
2015-09-10 02:20:34 +03:00
2015-09-14 23:20:33 +03:00
### COMMON ELEMENT ROUTINES ###
2015-09-10 20:35:28 +03:00
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 )
Logging . info ( " Testing element $element_type " )
local element
2015-10-27 06:37:58 +02:00
dim = nothing
n = nothing
try
dim , n = size ( element_type )
catch
Logging . 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. " )
end
Logging . info ( " element dimension: $dim x $n " )
2015-09-10 20:35:28 +03:00
Logging . info ( " Initializing element " )
2015-08-31 20:15:28 +03:00
try
2015-10-20 15:54:47 +03:00
element = element_type ( collect ( 1 : n ) )
2015-08-31 20:15:28 +03:00
catch
2015-09-10 20:35:28 +03:00
Logging . error ( """
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-10-27 06:37:58 +02:00
push! ( element , FieldSet ( " field1 " , [ Field ( 0.0 , collect ( 1 : n ) ) ] ) )
# TODO: how to parametrize this?
2015-10-26 05:40:41 +02:00
push! ( element , FieldSet ( " geometry " , [ Field ( 0.0 , Vector [ [ 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-10-27 06:37:58 +02:00
basis = get_basis ( element )
dbasis = grad ( basis )
2015-09-28 13:22:12 +03:00
mid = zeros ( dim )
2015-10-27 06:37:58 +02:00
val1 = basis ( mid , 0.0 )
Logging . info ( " basis at $mid : $val1 " )
val2 = basis ( " field1 " , mid , 0.0 )
Logging . info ( " field val at $mid : $val2 " )
val3 = dbasis ( mid , 0.0 )
Logging . info ( " derivative of basis at $mid : $val3 " )
val4 = dbasis ( " field1 " , mid , 0.0 )
Logging . info ( " field val at $mid : $val4 " )
2015-08-31 20:15:28 +03:00
2015-10-20 15:54:47 +03:00
Logging . info ( " Element $element_type passed tests. " )
2015-08-31 20:15:28 +03:00
end
2015-10-20 15:54:47 +03:00
2015-10-26 05:40:41 +02:00
function get_connectivity ( el :: Element )
el . connectivity
end
2015-09-14 23:20:33 +03:00
2015-10-26 05:40:41 +02:00
type MixedFunctionSpace
element1 :: Element
element2 :: Element
end
2015-09-28 13:22:12 +03:00
2015-10-26 05:40:41 +02:00
type FunctionSpace
element :: Element
end
2015-09-29 00:07:56 +03:00
2015-10-26 05:40:41 +02:00
type GradientFunctionSpace
element :: Element
2015-09-29 00:07:56 +03:00
end
2015-10-26 05:40:41 +02:00
function grad ( u :: FunctionSpace )
GradientFunctionSpace ( u . element )
2015-10-21 07:24:19 +03:00
end
2015-09-29 00:07:56 +03:00
2015-10-26 05:40:41 +02:00
""" Evaluate field on element function space. """
function call ( u :: FunctionSpace , field_name , xi :: Vector , t :: Number = Inf , variation = nothing )
f = ! isa ( variation , Void ) ? variation : u . element [ field_name ] ( t )
if length ( f ) == 1
return f . values
end
h = u . element . basis . basis ( xi )
2015-10-27 06:37:58 +02:00
return dot ( vec ( h ) , f )
2015-09-29 00:07:56 +03:00
end
2015-10-26 05:40:41 +02:00
""" If basis is called without a field, return basis functions evaluated at that point. """
function call ( u :: FunctionSpace , xi :: Vector , t :: Number = Inf )
2015-10-27 06:37:58 +02:00
return u . element . basis . basis ( xi )
2015-10-21 07:24:19 +03:00
end
2015-09-29 00:07:56 +03:00
2015-10-26 05:40:41 +02:00
""" Evaluate gradient of field on element function space. """
function call ( gradu :: GradientFunctionSpace , field_name , xi :: Vector , t :: Number = Inf , variation = nothing )
f = ! isa ( variation , Void ) ? variation : gradu . element [ field_name ] ( t )
X = gradu . element [ " geometry " ] ( t )
2015-10-27 06:37:58 +02:00
dN = gradu . element . basis . dbasisdxi ( xi )
J = sum ( [ dN [ : , i ] * X [ i ] ' for i = 1 : length ( X ) ] )
grad = inv ( J ) * dN
gradf = sum ( [ grad [ : , i ] * f [ i ] ' for i = 1 : length ( f ) ] ) '
return gradf
2015-10-23 01:11:45 +03:00
end
2015-10-26 05:40:41 +02:00
""" If gradient of basis is called without a field, return " empty " gradient evaluated at that point. """
function call ( gradu :: GradientFunctionSpace , xi :: Vector , t :: Number = Inf )
X = gradu . element [ " geometry " ] ( t )
2015-10-27 06:37:58 +02:00
dN = gradu . element . basis . dbasisdxi ( xi )
J = sum ( [ dN [ : , i ] * X [ i ] ' for i = 1 : length ( X ) ] )
grad = inv ( J ) * dN
return grad
2015-10-26 05:40:41 +02:00
end
2015-09-10 02:20:34 +03:00
2015-10-26 05:40:41 +02:00
# on-line functions to get api more easy to use, ip -> xi.ip
2015-10-27 06:37:58 +02:00
call ( u :: FunctionSpace , ip :: IntegrationPoint , t :: Number = Inf ) = call ( u , ip . xi , t )
call ( u :: GradientFunctionSpace , ip :: IntegrationPoint , t :: Number = Inf ) = call ( u , ip . xi , t )
# i think these will be the most called functions.
call ( u :: FunctionSpace , field_name , ip :: IntegrationPoint , t :: Number , variation = nothing ) = call ( u , field_name , ip . xi , t , variation )
call ( u :: GradientFunctionSpace , field_name , ip :: IntegrationPoint , t :: Number , variation = nothing ) = call ( u , field_name , ip . xi , t , variation )
call ( u :: FunctionSpace , field_name ) = ( args ... ) -> call ( u , field_name , args ... )
call ( u :: GradientFunctionSpace , field_name ) = ( args ... ) -> call ( u , field_name , args ... )
2015-09-14 23:20:33 +03:00
2015-10-27 06:37:58 +02:00
""" Return a field from function space. """
2015-10-26 05:40:41 +02:00
function get_field ( u :: FunctionSpace , field_name , time = Inf )
return u . element [ field_name ] ( time )
end
2015-10-27 06:37:58 +02:00
""" Return a field from function space. """
2015-10-26 05:40:41 +02:00
function get_field ( u :: FunctionSpace , field_name , time = Inf , variation = nothing )
return ! isa ( variation , Void ) ? variation : u . element [ field_name ] ( time )
2015-08-24 01:14:03 +03:00
end
2015-10-27 06:37:58 +02:00
""" Return a fieldset from function space. """
2015-10-26 05:40:41 +02:00
function get_fieldset ( u :: FunctionSpace , field_name )
return u . element [ field_name ]
2015-08-24 01:14:03 +03:00
end
2015-10-27 06:37:58 +02:00
function LinAlg . det ( u :: FunctionSpace , xi :: Vector , t :: Number = Inf )
X = u . element [ " geometry " ] ( t )
dN = u . element . basis . dbasisdxi ( xi )
J = sum ( [ dN [ : , i ] * X [ i ] ' for i = 1 : length ( X ) ] )
m , n = size ( J )
return m == n ? det ( J ) : norm ( J )
2015-10-26 05:40:41 +02:00
end
2015-10-27 06:37:58 +02:00
function LinAlg . det ( u :: FunctionSpace , ip :: IntegrationPoint , t :: Number = Inf )
LinAlg . det ( u , ip . xi , t )
2015-10-26 05:40:41 +02:00
end
function LinAlg . det ( u :: FunctionSpace )
2015-10-27 06:37:58 +02:00
return ( args ... ) -> det ( u , args ... )
2015-10-26 05:40:41 +02:00
end
2015-09-29 00:07:56 +03:00
2015-10-26 05:40:41 +02:00
function get_basis ( element :: Element )
return FunctionSpace ( element )
end
Base . ( : + ) ( u :: FunctionSpace , v :: FunctionSpace ) = ( args ... ) -> u ( args ... ) + v ( args ... )
Base . ( : - ) ( u :: FunctionSpace , v :: FunctionSpace ) = ( args ... ) -> u ( args ... ) - v ( args ... )
Base . ( : + ) ( u :: GradientFunctionSpace , v :: GradientFunctionSpace ) = ( args ... ) -> u ( args ... ) + v ( args ... )
Base . ( : - ) ( u :: GradientFunctionSpace , v :: GradientFunctionSpace ) = ( args ... ) -> u ( args ... ) - v ( args ... )
""" Check does fieldset exist. """
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-09-15 22:16:00 +03:00
2015-09-14 23:20:33 +03:00
# FIXME: These two needs integration -- maybe not in elements.jl ..?
"""
Fit field s.t. || ∫ (Nᵢ(ξ)αᵢ - f(el, ξ)) dS || -> min!
Parameters
----------
f::Function
Needs to take (el::Element, xi::Vector) as argument
fixed_coeffs::Int[]
These coefficients are not changed during fitting -> constrained optimizatio
"""
function fit_field! ( el :: Element , field , f , fixed_coeffs = Int [ ] )
w = [
128 / 225 ,
( 332 + 13 * sqrt ( 70 ) ) / 900 ,
( 332 + 13 * sqrt ( 70 ) ) / 900 ,
( 332 - 13 * sqrt ( 70 ) ) / 900 ,
( 332 - 13 * sqrt ( 70 ) ) / 900 ]
xi = Vector [
[ 0.0 ] ,
[ 1 / 3 * sqrt ( 5 - 2 * sqrt ( 10 / 7 ) ) ] ,
2015-10-20 15:54:47 +03:00
[ - 1 / 3 * sqrt ( 5 - 2 * sqrt ( 10 / 7 ) ) ] ,
2015-09-14 23:20:33 +03:00
[ 1 / 3 * sqrt ( 5 + 2 * sqrt ( 10 / 7 ) ) ] ,
[ - 1 / 3 * sqrt ( 5 + 2 * sqrt ( 10 / 7 ) ) ] ]
n = get_number_of_basis_functions ( el )
fld = get_field ( el , field )
nfld = length ( fld [ 1 ] )
#Logging.debug("dim of field $field: $nfld")
M = zeros ( n , n )
b = zeros ( n , nfld )
for i = 1 : length ( w )
detJ = get_detJ ( el , xi [ i ] )
N = get_basis ( el , xi [ i ] )
M += w [ i ] * N * N ' * detJ
fi = f ( el , xi [ i ] )
for j = 1 : nfld
b [ : , j ] += w [ i ] * N * fi [ j ] * detJ
end
end
coeffs = zeros ( n )
for j = 1 : nfld
for k = 1 : n
coeffs [ k ] = fld [ k ] [ j ]
end
if length ( fixed_coeffs ) != 0
# constrained problem, some coefficients are fixed
N = Int [ ] # rest of coeffs
S = Int [ ] # fixed coeffs
for i = 1 : n
if i in fixed_coeffs
push! ( S , i )
else
push! ( N , i )
end
end
lhs = M [ N , N ]
rhs = b [ N , j ] - M [ N , S ] * coeffs [ S ]
coeffs [ N ] = lhs \ rhs
else
coeffs [ : ] = M \ b [ : , j ]
end
for k = 1 : n
fld [ k ] [ j ] = coeffs [ k ]
end
end
set_field ( el , field , fld )
return
end
"""
Fit field s.t. || ∫ ∂/∂ξ(∑Nᵢ(ξ)αᵢ)f(el, ξ) dS || -> min!
"""
function fit_derivative_field! ( el :: Element , field , f , fixed_coeffs = Int [ ] )
w = [
128 / 225 ,
( 332 + 13 * sqrt ( 70 ) ) / 900 ,
( 332 + 13 * sqrt ( 70 ) ) / 900 ,
( 332 - 13 * sqrt ( 70 ) ) / 900 ,
( 332 - 13 * sqrt ( 70 ) ) / 900 ]
xi = Vector [
[ 0.0 ] ,
[ 1 / 3 * sqrt ( 5 - 2 * sqrt ( 10 / 7 ) ) ] ,
2015-10-20 15:54:47 +03:00
[ - 1 / 3 * sqrt ( 5 - 2 * sqrt ( 10 / 7 ) ) ] ,
2015-09-14 23:20:33 +03:00
[ 1 / 3 * sqrt ( 5 + 2 * sqrt ( 10 / 7 ) ) ] ,
[ - 1 / 3 * sqrt ( 5 + 2 * sqrt ( 10 / 7 ) ) ] ]
n = get_number_of_basis_functions ( el )
fld = get_field ( el , field )
nfld = length ( fld [ 1 ] )
#Logging.debug("dim of field $field: $nfld")
M = zeros ( n , n )
b = zeros ( n , nfld )
for i = 1 : length ( w )
detJ = get_detJ ( el , xi [ i ] )
dNdxi = get_dbasisdxi ( el , xi [ i ] )
dNdX = dNdxi / detJ
M += w [ i ] * dNdX * dNdX ' * detJ
fi = f ( el , xi [ i ] )
for j = 1 : nfld
b [ : , j ] += w [ i ] * dNdX * fi [ j ] * detJ
end
end
coeffs = zeros ( n )
for j = 1 : nfld
for k = 1 : n
coeffs [ k ] = fld [ k ] [ j ]
end
if length ( fixed_coeffs ) != 0
#Logging.info("constrained problem, some coefficients are fixed")
N = Int [ ] # rest of coeffs
S = Int [ ] # fixed coeffs
for i = 1 : n
if i in fixed_coeffs
push! ( S , i )
else
push! ( N , i )
end
end
lhs = M [ N , N ]
rhs = b [ N , j ] - M [ N , S ] * coeffs [ S ]
coeffs [ N ] = lhs \ rhs
else
coeffs [ : ] = M \ b [ : , j ]
end
for k = 1 : n
fld [ k ] [ j ] = coeffs [ k ]
end
end
set_field ( el , field , fld )
return
end
2015-09-15 22:16:00 +03:00