mirror of
https://github.com/JuliaFEM/JuliaFEM.jl.git
synced 2026-08-19 03:33:45 +00:00
c307c1482c
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
374 lines
11 KiB
Julia
374 lines
11 KiB
Julia
# 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
|