Files
JuliaFEM.jl/src/elements.jl
T

315 lines
9.2 KiB
Julia
Raw Normal View History

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}
fields :: Dict{AbstractString, Field}
2016-05-20 04:39:51 +03:00
properties :: E
end
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...)
element = Element{E}(id, connectivity, integration_points, fields, variant)
return element
2016-05-20 04:39:51 +03:00
end
function getindex(element::Element, field_name::AbstractString)
2016-06-27 16:11:33 +03:00
return element.fields[field_name]
end
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
function setindex!(element::Element, data::Function, field_name)
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
function setindex!(element::Element, data, field_name)
2016-05-20 04:39:51 +03:00
element.fields[field_name] = Field(data)
end
function call(element::Element, field_name)
2016-07-01 02:55:56 +03:00
return element[field_name]
end
function call(element::Element, field_name, time)
2016-05-24 19:05:25 +03:00
return element[field_name](time)
end
function last(element::Element, field_name::AbstractString)
2016-07-01 02:55:56 +03:00
return last(element[field_name])
end
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
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
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
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
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
function call(element::Element, field_name::AbstractString, time)
2016-07-03 05:01:18 +03:00
field = element[field_name]
return element(field, time)
end
function call(element::Element, field_name::AbstractString, ip, time::Float64)
2016-06-27 16:11:33 +03:00
field = element[field_name]
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
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]).
"""
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
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
function update!(element::Element, field_name::AbstractString, datas::Union{Real, Vector, Pair{Float64, Union{Float64, Real, Vector{Any}}}}...)
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
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
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
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
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
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
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))
else
element[field_name] = data
end
2016-06-02 22:58:54 +03:00
end
2016-05-20 04:39:51 +03:00
end
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
function update!(element::Element, field_name, data::Function)
2016-06-27 16:11:33 +03:00
element[field_name] = data
end
function update!(element::Element, field_name, field::Field)
2016-06-27 16:11:33 +03:00
element[field_name] = field
end
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)
# 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. """
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)
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
""" 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
end
info("X = $X, dX = $dX, xi = $xi")
error("Unable to find inverse isoparametric mapping for element $element for X = $X")
end
""" Test is X inside element. """
function inside{E}(element::Element{E}, X, time)
xi = get_local_coordinates(element, X, time)
return inside(E, xi)
end
2016-05-20 04:39:51 +03:00