Files
JuliaFEM.jl/src/elements.jl
T
Jukka Aho c307c1482c Testing/code coverage (#83)
Change the code coverage to green. 

* removed duplicate code

* Removed unused code

* removed unmaintained code

* DCTI + DVTI refactored

* discrete fields refactored and tested

* fields are now tested quite well.

* Removed obsolete code not used anywhere

* Element descriptions to common dictionary

* size in global const dictionary also

* Added coverage to sparse tools and removed couple unused functions

* get nonzero rows from SparseMatrixCSC

* bugfix: extending element basis now working and tested

* Removed two unused functions from elements.jl

* removed useless function

* Useless conversion

* remove elasticity assembly using ForwardDiff because it's not used anywhere'

* Added basic testing for NURBS. Fixed bug in NSolid interpolation.

* removed unused functions

* Removed some debug stuff

* renamed file

* removed field assembly posthook, i think not good idea at all

* test for nnz(K) == 0 and automatic determination of dofs

* Testing that solver is throwing error if having problems with boundary assembly

* Removed some unused options. Refactoring.

* Moved solver non-related code to elements.jl

* Removed custom exception (no need)

* unneeded postprocess code

* More tests for NURBS elements.

* Removed unfinished .mail parser

* proper use of Logging package

* also read results

* renamed test file

* create_surface_elements accepts surface name in String now

* bugfix: remove zero rows from constraint matrix after manually removing dofs from some boundary assemblies.

* New test, displacement 3d patch test

* skip displacement field in surface element splitting if not defined

* test element splitting and linear surface elements, fails.

* Bugfix: Xdmf, not XDMF

* removed nonworking tests, requires bugfix

* abaqus_read_results is not working -> bug
2017-01-30 12:28:33 +02:00

374 lines
11 KiB
Julia
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# This file is a part of JuliaFEM.
# License is MIT: see https://github.com/JuliaFEM/JuliaFEM.jl/blob/master/LICENSE.md
abstract AbstractElement
type Element{E<:AbstractElement}
id :: Int
connectivity :: Vector{Int}
integration_points :: Vector{IP}
fields :: Dict{String, Field}
properties :: E
end
""" Construct a new element of type E.
Examples
--------
julia> element = Element(Tri3, [1, 2, 3])
"""
function Element{E<:AbstractElement}(::Type{E}, connectivity::Vector{Int})
return Element{E}(-1, connectivity, [], Dict(), E())
end
function getindex(element::Element, field_name::AbstractString)
return element.fields[field_name]
end
function setindex!(element::Element, data::Field, field_name)
element.fields[field_name] = data
end
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)
return is(E, element_type)
end
function filter_by_element_type(element_type, elements)
return filter(element -> is_element_type(element, element_type), elements)
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)
element.fields[field_name] = Field(data)
end
""" 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)
return element[field_name]
end
""" 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)
return element[field_name](time)
end
function last(element::Element, field_name::String)
return last(element[field_name])
end
function (element::Element)(ip, time::Float64=0.0)
return get_basis(element, ip, time)
end
"""
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}:
0.25 0.0 0.25 0.0 0.25 0.0 0.25 0.0
0.0 0.25 0.0 0.25 0.0 0.25 0.0 0.25
"""
function (element::Element)(ip, time::Float64, dim::Int)
dim == 1 && return get_basis(element, ip, time)
Ni = vec(get_basis(element, ip, time))
N = zeros(dim, length(element)*dim)
for i=1:dim
N[i,i:dim:end] += Ni
end
return N
end
function (element::Element)(ip, time::Float64, ::Type{Val{:Jacobian}})
X = element("geometry", time)
dN = get_dbasis(element, ip, time)
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
return J
end
function (element::Element)(ip, time::Float64, ::Type{Val{:detJ}})
J = element(ip, time, Val{:Jacobian})
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 (element::Element)(ip, time::Float64, ::Type{Val{:Grad}})
J = element(ip, time, Val{:Jacobian})
return inv(J)*get_dbasis(element, ip, time)
end
function (element::Element)(field_name::String, ip, time::Float64, ::Type{Val{:Grad}})
return element(ip, time, Val{:Grad})*element[field_name](time)
end
function (element::Element)(field_name::String, ip, time::Float64)
field = element[field_name]
return element(field, ip, time)
end
function (element::Element)(field::DCTI, ip, time::Float64)
return field.data
end
function (element::Element)(field::DCTV, ip, time::Float64)
return field(time).data
end
function (element::Element)(field::CVTV, ip, time::Float64)
return field(ip, time)
end
function (element::Element)(field::Field, ip, time::Float64)
field_ = field(time)
basis = element(ip, time)
n = length(element)
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])
end
end
function size(element::Element, dim)
return size(element)[dim]
end
""" 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!{E}(element::Element{E}, field_name::AbstractString, data::Dict)
#element[field_name] = Field(data)
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
end
function update!{K,V}(element::Element, field_name, data::Pair{Float64, Dict{K, V}})
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, data::Pair{Float64, Vector{Any}})
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::Pair{Float64, Vector{Int64}})
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::Pair{Float64, Vector{Float64}})
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::Pair{Float64, Vector{Vector{Float64}}})
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::Pair{Float64, Float64})
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})
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
end
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::String, data::Function)
element[field_name] = data
end
function update!(element::Element, field_name::String, field::Field)
element[field_name] = field
end
function update!(elements::Vector, field_name::String, data)
for element in elements
update!(element, field_name, data)
end
end
""" Check existence of field. """
function haskey(element::Element, field_name::String)
haskey(element.fields, field_name)
end
function get_connectivity(element::Element)
return element.connectivity
end
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
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
ips = get_integration_points(element.properties, order)
return [IP(i, w, xi) for (i, (w, xi)) in enumerate(ips)]
end
""" Return dual basis transformation matrix Ae. """
function get_dualbasis(element::Element, time::Float64, order=1)
nnodes = length(element)
De = zeros(nnodes, nnodes)
Me = zeros(nnodes, nnodes)
for ip in get_integration_points(element, order)
detJ = element(ip, time, Val{:detJ})
w = ip.weight*detJ
N = element(ip, time)
De += w*diagm(vec(N))
Me += w*N'*N
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