This commit is contained in:
ovainola
2015-08-25 21:09:11 +03:00
18 changed files with 3821 additions and 1960 deletions
+10
View File
@@ -4,10 +4,20 @@ module JuliaFEM
VERSION < v"0.4-" && using Docile
using Lexicon
using Logging
@Logging.configure(level=DEBUG)
Logging.info("loading types")
include("types.jl") # type definitions
Logging.info("loading elements")
include("elements.jl") # elements
include("math.jl") # basic mathematical operations
include("elasticity_solver.jl")
include("xdmf.jl")
include("abaqus_reader.jl")
include("interfaces.jl")
export set_coordinates, get_coordinates, set_material
end # module
+9 -17
View File
@@ -1,17 +1,12 @@
# This file is a part of JuliaFEM.
# License is MIT: see https://github.com/JuliaFEM/JuliaFEM.jl/blob/master/LICENSE.md
module abaqus_reader
eldims = Dict(
"C3D10" => 10,
"C3D4" => 4)
using Logging
@Logging.configure(level=DEBUG)
VERSION < v"0.4-" && using Docile
eldims = Dict({"C3D10" => 10})
global handlers = Dict()
"""
Register new handler for parser
"""
@@ -29,13 +24,12 @@ end
function parse_header(header_line)
args = map(s -> strip(s), split(header_line, ","))
args[1] = strip(args[1], '*')
d = Dict({"section" => args[1]})
options = Dict()
d = Dict("section" => args[1], "options" => Dict())
options = d["options"]
for k in args[2:end]
args2 = split(k, "=")
options[args2[1]] = args2[2]
end
d["options"] = options
return d
end
@@ -56,7 +50,7 @@ function parse_element_section(model, header, data)
end
eldim = eldims[eltype]
m = matchall(r"[0-9]+", data)
m = map(integer, m)
m = map((s) -> parse(Int, s), m)
elements = create_or_get(model, "elements")
m = reshape(m, eldim+1, round(Int, length(m)/(eldim+1)))
nel = size(m)[2]
@@ -80,7 +74,7 @@ function parse_nodeset_section(model, header, data)
nset_name = header["options"]["NSET"]
Logging.debug("Creating node set $nset_name")
m = matchall(r"[0-9]+", data)
node_ids = map(integer, m)
node_ids = map((s) -> parse(Int, s), m)
nsets = create_or_get(model, "nsets")
nsets[nset_name] = Int64[]
for j in node_ids
@@ -110,10 +104,10 @@ function parse_abaqus(fid)
end
for line in eachline(fid)
if beginswith(line, "**")
if startswith(line, "**")
continue
end
if beginswith(line, "*")
if startswith(line, "*")
process_section(section)
header = parse_header(line)
Logging.debug("Found ", header["section"], " section")
@@ -131,5 +125,3 @@ add_handler("NODE", parse_node_section)
add_handler("ELEMENT", parse_element_section)
add_handler("NSET", parse_nodeset_section)
end
+85 -82
View File
@@ -3,6 +3,8 @@
module elasticity_solver
using ForwardDiff
using Logging
@Logging.configure(level=INFO)
@@ -12,103 +14,104 @@ VERSION < v"0.4-" && using Docile
# directly if needed or using general interface combining data model and
# solver.
"""
Interpolate field variable using basis functions f for point ip.
This function tries to be as general as possible and allows interpolating
lot of different fields.
This is dummy function. Testing doctests and documentation.
Parameters
----------
field :: Array{Number, dim}
Field variable
basis :: Function
Basis functions
ip :: Array{Number, 1}
Point to interpolate
x : Array{Float64, 1}
Returns
-------
Array{float64, 1}
x + 1
Notes
-----
This is dummy function
Raises
------
Exception
if things are not going right
Examples
--------
>>> a = [1.0, 2.0, 3.0]
>>> dummy(a)
[2.0, 3.0, 4.0]
"""
function interpolate{T<:Real}(field::Array{T,1}, basis::Function, ip)
result = dot(field, basis(ip))
return result
end
function interpolate{T<:Real}(field::Array{T,2}, basis::Function, ip)
m, n = size(field)
bip = basis(ip)
tmp = size(bip)
if length(tmp) == 1
ndim = 1
nnodes = tmp[1]
else
ndim, nnodes = size(bip)
end
if ndim == 1
if n == nnodes
result = field * bip
elseif m == nnodes
result = field' * bip
end
else
if n == nnodes
result = bip' * field
elseif m == nnodes
result = bip' * field'
end
end
if length(result) == 1
result = result[1]
end
return result
function dummy(a)
# not doing anything useful.
return a+1
end
"""
Calculate local tangent stiffness matrix and residual force vector R = T - F
Calculate local tangent stiffness matrix and residual force vector
R = T - F for elasticity problem.
Parameters
----------
X : Element coordinates
u : Displacement field
R : Residual force vector
K : Tangent stiffness matrix
basis : Basis functions
dbasis : Derivative of basis functions
lambda : Material parameter
mu : Material parameter
ipoints : integration points
iweights : integration weights
Returns
-------
None
Notes
-----
If material parameters are given in list, they are interpolated to gauss
points using shape functions.
"""
function calc_local_matrices!(X, u, R, Kt, N, dNdchi, lambda_, mu_, ipoints, iweights)
dim, nnodes = size(X)
I = eye(dim)
R[:,:] = 0.0
Kt[:,:] = 0.0
function calc_local_matrices!(X, u, R, K, basis, dbasis, lambda_, mu_, ipoints, iweights)
dim, nnodes = size(X)
I = eye(dim)
R[:,:] = 0.0
dF = zeros(dim, dim)
#dF = zeros(dim, dim)
for m = 1:length(iweights)
w = iweights[m]
chi = ipoints[m, :]
# interpolate material parameters from element node fields
#lambda = (lambda_*N(chi))[1]
#mu = (mu_*N(chi))[1]
# Jt = X*dNdchi(chi)
#@debug("Jt:\n",Jt)
lambda = interpolate(lambda_, N, chi)
mu = interpolate(mu_, N, chi)
Jt = interpolate(X, dNdchi, chi)
detJ = det(Jt)
deltaN = inv(Jt)*dNdchi(chi)'
delta_u = u*deltaN'
F = I + delta_u # Deformation gradient
E = 1/2*(delta_u' + delta_u + delta_u'*delta_u) # Green-Lagrange strain tensor
S = lambda*trace(E)*I + 2*mu*E # PK2 stress tensor
P = F*S # PK1 stress tensor
R[:,:] += w*P*deltaN*detJ
function calc_R!(u, R)
for m = 1:length(iweights)
w = iweights[m]
xi = ipoints[m, :]
# calculate material parameters
lambda = typeof(lambda_) == Float64 ? lambda_ : dot(lambda_, basis(xi))
mu = typeof(mu_) == Float64 ? mu_ : dot(mu_, basis(xi))
Jt = X*dbasis(xi)
detJ = det(Jt)
dbasisdX = dbasis(xi)*inv(Jt)
for p = 1:nnodes
for i = 1:dim
dF[:,:] = 0.0
dF[i,:] = deltaN[:,p]
dE = 1/2*(F'*dF + dF'*F)
dS = lambda*trace(dE)*I + 2*mu*dE
dP = dF*S + F*dS
for q = 1:nnodes
for j = 1:dim
Kt[dim*(p-1)+i,dim*(q-1)+j] += w*(dP[j,:]*deltaN[:,q])[1]*detJ
end
end
end
gradu = u*dbasisdX
F = I + gradu # Deformation gradient
E = 1/2*(gradu' + gradu + gradu'*gradu) # Green-Lagrange strain tensor
S = lambda*trace(E)*I + 2*mu*E # PK2 stress tensor
P = F*S # PK1 stress tensor
R[:,:] += w*P*dbasisdX'*detJ
end
end
# herlper for tangent stiffness matrix
function R!(u, R)
R[:] = 0
calc_R!(reshape(u, dim, nnodes), reshape(R, dim, nnodes))
#calc_Wext!(reshape(u, 2, 4), reshape(R, 2, 4))
end
Jacobian = ForwardDiff.forwarddiff_jacobian(R!, Float64, fadtype=:dual, n=dim*nnodes, m=dim*nnodes)
K[:, :] = Jacobian(reshape(u, dim*nnodes))
R!(reshape(u, dim*nnodes), reshape(R, dim*nnodes))
end
+235
View File
@@ -0,0 +1,235 @@
# This file is a part of JuliaFEM.
# License is MIT: see https://github.com/JuliaFEM/JuliaFEM.jl/blob/master/LICENSE.md
abstract Element
"""
Get jacobian of element evaluated at point xi
"""
function get_jacobian(el::Element, xi)
dbasisdxi(xi) = get_dbasisdxi(el, xi)
X = get_coordinates(el)
J = interpolate(X, dbasisdxi, xi)'
return J
end
"""
Evaluate partial derivatives of basis function w.r.t
material description X, i.e. dbasis/dX
"""
function get_dbasisdX(el::Element, xi)
dbasisdxi = get_dbasisdxi(el, xi)
J = get_jacobian(el, xi)
dbasisdxi*inv(J)
end
"""
Return coordinates of element in array of size dim x nnodes
"""
function get_coordinates(el::Element)
el.coordinates
end
"""
Set coordinates for element
"""
function set_coordinates(el::Element, coordinates)
el.coordinates = coordinates
end
"""
Get element id
"""
function get_element_id(el::Element)
el.id
end
### Lagrange family ###
abstract CG <: Element # Lagrange element family
"""
Create new Lagrange element
FIXME: this is not working
LoadError: error compiling anonymous: type definition not allowed inside a local scope
It's the for loop which is causing problems. See
https://github.com/JuliaLang/julia/issues/10555
"""
function create_lagrange_element(element_name, X, P, dP)
@eval begin
nnodes, dim = size(X)
A = zeros(nnodes, nnodes)
for i=1:nnodes
A[i,:] = P(X[i,:])
end
invA = inv(A)'
type $element_name
element_id :: Int
node_ids :: Array{Int, 1}
coordinates :: Array{Float64, 2}
fields :: Dict{ASCIIString, Any}
end
function $element_name(element_id, node_ids)
coordinates = zeros(dim, nnodes)
fields = Dict{ASCIIString, Any}()
$element_name(element_id, node_ids, coordinates, fields)
end
function $element_name(element_id, node_ids, coordinates)
fields = Dict{ASCIIString, Any}()
$element_name(element_id, node_ids, coordinates, fields)
end
function get_basis(el::$element_name, xi)
invA*P(xi)
end
function get_dbasisdxi(el::$element_name, xi)
invA*dP(xi)
end
$element_name
end
end
# 0d Lagrange elements
"""
1 node point element
"""
type Point1 <: CG
element_id :: Int
node_ids :: Array{Int, 1}
coordinates :: Array{Float64, 2}
fields :: Dict{ASCIIString, Any}
end
# 1d Lagrange elements
"""
2 node linear line element
"""
type Seg2 <: CG
element_id :: Int
node_ids :: Array{Int, 1}
coordinates :: Array{Float64, 2}
fields :: Dict{ASCIIString, Any}
end
# X = [-1.0 1.0]'
# P = (xi) -> [1.0 xi[1]]'
# dP = (xi) -> [0.0 1.0]'
# create_lagrange_element(:Seg2, X, P, dP)
"""
3 node quadratic line element
"""
type Seg2 <: CG
element_id :: Int
node_ids :: Array{Int, 1}
coordinates :: Array{Float64, 2}
fields :: Dict{ASCIIString, Any}
end
#X = [-1.0 1.0 0.0]'
#P = (xi) -> [1.0 xi[1] xi[1]^2]'
#dP = (xi) -> [0.0 1.0 2*xi[1]]'
#create_lagrange_element(:Seg3, X, P, dP)
# 2d Lagrange elements
"""
4 node bilinear quadrangle element
"""
type Quad4 <: CG
element_id :: Int
node_ids :: Array{Int, 1}
coordinates :: Array{Float64, 2}
fields :: Dict{ASCIIString, Any}
end
function get_basis(el::Quad4, xi)
[(1-xi[1])*(1-xi[2])/4
(1+xi[1])*(1-xi[2])/4
(1+xi[1])*(1+xi[2])/4
(1-xi[1])*(1+xi[2])/4]
end
function get_dbasisdxi(el::Quad4, xi)
[-(1-xi[2])/4.0 -(1-xi[1])/4.0
(1-xi[2])/4.0 -(1+xi[1])/4.0
(1+xi[2])/4.0 (1+xi[1])/4.0
-(1+xi[2])/4.0 (1-xi[1])/4.0]
end
#X = [
# -1.0 -1.0
# 1.0 -1.0
# 1.0 1.0
# -1.0 1.0]
#P = (xi) -> [
# 1.0
# xi[1]
# xi[2]
# xi[1]*xi[2]]
#dP = (xi) -> [
# 0.0 0.0
# 1.0 0.0
# 0.0 1.0
# xi[2] xi[1]]
#create_lagrange_element(:Quad4, X, P, dP)
# 3d Lagrange elements
"""
10 node quadratic tethahedron
"""
type Tet10 <: CG
element_id :: Int
node_ids :: Array{Int, 1}
coordinates :: Array{Float64, 2}
fields :: Dict{ASCIIString, Any}
end
# X = [
# 0.0 0.0 0.0
# 1.0 0.0 0.0
# 0.0 1.0 0.0
# 0.0 0.0 1.0
# 0.5 0.0 0.0
# 0.5 0.5 0.0
# 0.0 0.5 0.0
# 0.0 0.0 0.5
# 0.5 0.0 0.5
# 0.0 0.5 0.5]
# P(xi) = [
# 1
# xi[1]
# xi[2]
# xi[3]
# xi[1]^2
# xi[2]^2
# xi[3]^2
# xi[1]*xi[2]
# xi[2]*xi[3]
# xi[3]*xi[1]]
# dP(xi) = [
# 0 0 0
# 1 0 0
# 0 1 0
# 0 0 1
# 2*xi[1] 0 0
# 0 2*xi[2] 0
# 0 0 2*xi[3]
# xi[2] xi[1] 0
# 0 xi[3] xi[2]
# xi[3] 0 xi[1]
# ]
#create_lagrange_element(:Tet10, X, P, dP)
+186
View File
@@ -0,0 +1,186 @@
# This file is a part of JuliaFEM.
# License is MIT: see https://github.com/JuliaFEM/JuliaFEM.jl/blob/master/LICENSE.md
"""
This module contains math stuff, including interpolation, integration, linearization, ...
"""
using ForwardDiff
export interpolate, integrate, linearize
"""
Interpolate field variable using basis functions f for point ip.
This function tries to be as general as possible and allows interpolating
lot of different fields.
Parameters
----------
field :: Array{Number, dim}
Field variable
basis :: Function
Basis functions
ip :: Array{Number, 1}
Point to interpolate
"""
function interpolate(field::Float64, basis::Function, ip::Array{Float64,1})
# dummy function, unable to interpolate scalar value!
return field
end
function interpolate{T<:Real}(field::Array{T,1}, basis::Function, ip)
result = dot(field, basis(ip))
return result
end
function interpolate{T<:Real}(field::Array{T,2}, basis::Function, ip)
m, n = size(field)
bip = basis(ip)
tmp = size(bip)
if length(tmp) == 1
ndim = 1
nnodes = tmp[1]
else
ndim, nnodes = size(bip)
end
if ndim == 1
if n == nnodes
result = field * bip
elseif m == nnodes
result = field' * bip
end
else
if n == nnodes
result = bip' * field
elseif m == nnodes
result = bip' * field'
end
end
if length(result) == 1
result = result[1]
end
return result
end
function interpolate(e::Element, field::ASCIIString, x::Array{Float64,1}; derivative=false)
basis = derivative ? get_dbasisdxi(e) : get_basis(e)
return interpolate(e.attributes[field], basis, x)
end
"""
Linearize function f w.r.t some given field, i.e. calculate dR/du
Parameters
----------
f::Function
(possibly) nonlinear function to linearize
field::ASCIIString
field variable
Returns
-------
Array{Float64, 2}
jacobian / "tangent stiffness matrix"
"""
function linearize(f::Function, el::Element, field::ASCIIString)
dim, nnodes = size(el.attributes[field])
function helper!(x, y)
orig = copy(el.attributes[field])
el.attributes[field] = reshape(x, dim, nnodes)
y[:] = f(el)
el.attributes[field] = copy(orig)
end
jac = ForwardDiff.forwarddiff_jacobian(helper!, Float64, fadtype=:dual, n=dim*nnodes, m=dim*nnodes)
return jac(el.attributes[field][:])
end
"""
This version returns another function which can be then evaluated against field
"""
function linearize(f::Function, field::ASCIIString)
function jacobian(el::Element, args...)
dim, nnodes = size(el.attributes[field])
function helper!(x, y)
orig = copy(el.attributes[field])
el.attributes[field] = reshape(x, dim, nnodes)
y[:] = f(el, args...)
el.attributes[field] = copy(orig)
end
jac = ForwardDiff.forwarddiff_jacobian(helper!, Float64, fadtype=:dual, n=dim*nnodes, m=dim*nnodes)
return jac(el.attributes[field][:])
end
return jacobian
end
"""
In-place version, no additional garbage collection.
"""
function linearize!(f::Function, el::Element, field::ASCIIString, target::ASCIIString)
el.attributes[target][:] = 0.0
dim, nnodes = size(el.attributes[field])
function helper!(x, y)
orig = copy(el.attributes[field])
el.attributes[field] = reshape(x, dim, nnodes)
y[:] = f(el)
el.attributes[field] = copy(orig)
end
jac! = ForwardDiff.forwarddiff_jacobian!(helper!, Float64, fadtype=:dual, n=dim*nnodes, m=dim*nnodes)
jac!(el.attributes[field][:], el.attributes[target])
end
"""
Integrate f over element using Gaussian quadrature rules.
Parameters
----------
el::Element
well defined element
f::Function
Function to integrate
"""
function integrate(f::Function, el::Element)
target = []
for ip in el.integration_points
J = interpolate(el, "coordinates", ip.xi; derivative=true)
push!(target, ip.weight*f(el, ip)*det(J))
end
return sum(target)
end
#function integrate(f::Function, integration_points::Array{IntegrationPoint, 1}, Xargs...)
# target = []
# for ip in integration_points
# J = interpolate(el, "coordinates", ip.xi; derivative=true)
# push!(target, ip.weight*f(ip, args...)*det(J))
# end
# return sum(target)
#end
"""
This version returns a function which must be operated with element e
"""
function integrate(f::Function)
function integrate(el::Element)
target = []
for ip in el.integration_points
J = interpolate(el, "coordinates", ip.xi; derivative=true)
push!(target, ip.weight*f(el, ip)*det(J))
end
return sum(target)
end
return integrate
end
"""
This version saves results inplace to target, garbage collection free
"""
function integrate!(f::Function, el::Element, target)
# set target to zero
el.attributes[target][:] = 0.0
for ip in el.integration_points
J = interpolate(el, "coordinates", ip.xi; derivative=true)
el.attributes[target][:,:] += ip.weight*f(el, ip)*det(J)
end
end
+52
View File
@@ -0,0 +1,52 @@
# This file is a part of JuliaFEM.
# License is MIT: see https://github.com/JuliaFEM/JuliaFEM.jl/blob/master/LICENSE.md
export IntegrationPoint, Element, Assembly, FunctionSpace
"""
Integration point
xi :: Array{Float64, 1}
(dimensionless) coordinates of integration point
weight :: Float64
Integration weight
attributes :: Dict{ASCIIString, Any}
This is used to save internal variables of IP needed e.g. for incremental
material models.
"""
type IntegrationPoint
xi :: Array{Float64, 1}
weight :: Float64
attributes :: Dict{ASCIIString, Any}
end
type FunctionSpace
basis :: Function
dbasis :: Function
end
abstract Element
#type Element
# id :: Int
# node_ids :: Array{Int, 1}
# shape_functions :: FunctionSpace
# integration_points :: Array{IntegrationPoint, 1}
# attributes :: Dict{ASCIIString, Any}
#end
type Assembly
# LHS
I :: Array{Int64, 1}
J :: Array{Int64, 1}
A :: Array{Float64, 1}
# RHS
i :: Array{Int64, 1}
b :: Array{Float64, 1}
# global dofs for each element
gdofs :: Dict{Int64, Array{Int64, 1}}
end
Assembly() = Assembly(Int64[], Int64[], Float64[], Int64[], Float64[], Dict{Int64,Array{Int64,1}}())
Assembly(gdofs::Dict{Int64,Array{Int64,1}}) = Assembly(Int64[], Int64[], Float64[], Int64[], Float64[], gdofs)
+51 -22
View File
@@ -1,17 +1,8 @@
# This file is a part of JuliaFEM.
# License is MIT: see https://github.com/JuliaFEM/JuliaFEM.jl/blob/master/LICENSE.md
module xdmf
using Logging
@Logging.configure(level=INFO)
using LightXML
VERSION < v"0.4-" && using Docile
# i add docstrings later
# element codes: http://www.paraview.org/pipermail/paraview/2013-July/028859.html
# > from ./VTK/ThirdParty/xdmf2/vtkxdmf2/libsrc/XdmfTopology.h
# >
@@ -84,29 +75,68 @@ function xdmf_new_grid(temporal_collection; time=0)
return grid
end
#function xdmf_new_mesh(grid, X, elmap)
# geometry = new_child(grid, "Geometry")
# set_attribute(geometry, "Type", "XYZ")
# dataitem = new_child(geometry, "DataItem")
# set_attribute(dataitem, "DataType", "Float")
# set_attribute(dataitem, "Dimensions", length(X))
# set_attribute(dataitem, "Format", "XML")
# set_attribute(dataitem, "Precision", "4")
# add_text(dataitem, join(X, " "))
# topology = new_child(grid, "Topology")
# set_attribute(topology, "Dimensions", "1")
# set_attribute(topology, "Type", "Mixed")
# dataitem = new_child(topology, "DataItem")
# set_attribute(dataitem, "DataType", "Int")
# set_attribute(dataitem, "Dimensions", length(elmap))
# set_attribute(dataitem, "Format", "XML")
# set_attribute(dataitem, "Precision", 4)
# elmap2 = copy(elmap)
# elmap2[2:end,:] -= 1
# add_text(dataitem, join(elmap2, " "))
#end
function xdmf_new_mesh(grid, X, elmap)
dim, nnodes = size(X)
geometry = new_child(grid, "Geometry")
set_attribute(geometry, "Type", "XYZ")
dataitem = new_child(geometry, "DataItem")
set_attribute(dataitem, "DataType", "Float")
set_attribute(dataitem, "Dimensions", length(X))
set_attribute(dataitem, "Dimensions", "$nnodes $dim")
set_attribute(dataitem, "Format", "XML")
set_attribute(dataitem, "Precision", "4")
add_text(dataitem, join(X, " "))
set_attribute(dataitem, "Precision", 8)
#add_text(dataitem, join(X, " "))
s = "\n"
for i=1:nnodes
s *= "\t\t" * join(X[:,i], " ") * "\n"
end
s *= " "
add_text(dataitem, s)
topology = new_child(grid, "Topology")
set_attribute(topology, "Dimensions", "1")
set_attribute(topology, "Type", "Mixed")
dataitem = new_child(topology, "DataItem")
set_attribute(dataitem, "DataType", "Int")
set_attribute(dataitem, "Dimensions", length(elmap))
set_attribute(dataitem, "Format", "XML")
set_attribute(dataitem, "Precision", 4)
elmap2 = copy(elmap)
elmap2[2:end,:] -= 1
add_text(dataitem, join(elmap2, " "))
dim, nelements = size(elmap2)
topology = new_child(grid, "Topology")
#set_attribute(topology, "Dimensions", "1")
set_attribute(topology, "TopologyType", "Mixed")
set_attribute(topology, "NumberOfElements", nelements)
dataitem = new_child(topology, "DataItem")
set_attribute(dataitem, "DataType", "Int")
set_attribute(dataitem, "Dimensions", "$nelements $dim")
set_attribute(dataitem, "Format", "XML")
set_attribute(dataitem, "Precision", 8)
s = "\n"
for i=1:nelements
s *= "\t\t" * join(elmap2[:,i], " ") * "\n"
end
add_text(dataitem, s)
#add_text(dataitem, join(elmap2, " "))
end
function xdmf_new_field(grid, name, source, data)
loc = Dict("elements" => "Cell",
"nodes" => "Node")
@@ -149,4 +179,3 @@ function xdmf_save_model(xdoc, filename)
save_file(xdoc, filename)
end
end