tutorial notebook working again

This commit is contained in:
Jukka Aho
2015-10-27 06:37:58 +02:00
parent 1844530303
commit aba63bb44c
15 changed files with 862 additions and 1305 deletions
+1 -1
View File
@@ -36,12 +36,12 @@ include("interpolate.jl") # interpolation routines
include("elements.jl")
include("lagrange.jl") # Lagrange elements
#include("hierarchical.jl") # P-elements
include("integrate.jl") # integration points
include("equations.jl")
include("problems.jl")
include("solvers.jl")
#include("math.jl") # basic mathematical operations -- obsolete ..?
# pre- and postprocess
include("xdmf.jl")
include("abaqus_reader.jl")
+36 -28
View File
@@ -5,48 +5,56 @@
abstract DirichletEquation <: Equation
get_unknown_field_name(eq::DirichletEquation) = symbol("reaction force")
### Dirichlet problem + equations
type DirichletProblem <: BoundaryProblem
unknown_field_name :: ASCIIString
unknown_field_dimension :: Int
equations :: Array{DirichletEquation, 1}
element_mapping :: Dict{DataType, DataType}
field_value :: Function
end
function DirichletProblem()
DirichletProblem([])
end
get_dimension(pr::Type{DirichletProblem}) = 1 # ..?
get_equation(pr::Type{DirichletProblem}, el::Type{Seg2}) = DBC2D2
"""
Dirichlet boundary condition element for 2 node line segment
"""
function DirichletProblem(dimension::Int, field_value::Function=(X)->[0.0,0.0,0.0])
element_mapping = nothing
if dimension == 1
element_mapping = Dict(
Seg2 => DBC2D2
)
end
DirichletProblem("reaction force", dimension, [], element_mapping, field_value)
end
""" Dirichlet boundary condition element for 2 node line segment """
type DBC2D2 <: DirichletEquation
element :: Seg2
integration_points :: Array{IntegrationPoint, 1}
global_dofs :: Array{Int64, 1}
fieldval :: Function
end
function DBC2D2(element::Seg2)
integration_points = [
IntegrationPoint([-sqrt(1/3)], 1.0),
IntegrationPoint([+sqrt(1/3)], 1.0)]
push!(element, FieldSet("reaction force"))
fieldval(X, t) = 0.0
DBC2D2(element, integration_points, [], fieldval)
DBC2D2(element, integration_points)
end
Base.size(equation::DBC2D2) = (1, 2)
function calculate_local_assembly!(assembly::LocalAssembly, equation::DirichletEquation,
unknown_field_name::ASCIIString, time::Number=Inf,
problem=nothing)
initialize_local_assembly!(assembly, equation)
element = get_element(equation)
basis = get_basis(element)
detJ = det(basis)
for ip in get_integration_points(equation)
w = ip.weight * detJ(ip)
N = basis(ip, time)
assembly.stiffness_matrix += w * N'*N
if !isa(problem, Void)
X = basis("geometry", ip, time)
u = problem.field_value(X)
assembly.force_vector += w * N'*u
end
end
end
function get_lhs(eq::DBC2D2, ip, t)
el = get_element(eq)
h = get_basis(el)(ip.xi)
return h*h'
end
function get_rhs(eq::DBC2D2, ip, t)
el = get_element(eq)
h = get_basis(el, ip.xi)
f = eq.fieldval
X = interpolate(el, "geometry", ip.xi, t)
return h*f(X, t)
end
has_lhs(eq::DBC2D2) = true
has_rhs(eq::DBC2D2) = true
+55 -79
View File
@@ -15,12 +15,12 @@ abstract Element
""" Get FieldSet from element. """
function Base.getindex(element::Element, field_name)
element.fields[symbol(field_name)]
element.fields[field_name]
end
""" Add new FieldSet to element. """
function Base.setindex!(element::Element, fieldset::FieldSet, fieldset_name)
fieldset.name = symbol(fieldset_name)
fieldset.name = fieldset_name
element.fields[fieldset.name] = fieldset
end
function Base.push!(element::Element, fieldset::FieldSet)
@@ -73,9 +73,8 @@ End of example.
=#
# These must be implemented for your own element
get_number_of_basis_functions(el::Type{Element}) = nothing
get_element_dimension(el::Type{Element}) = nothing
# 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
### COMMON ELEMENT ROUTINES ###
@@ -95,12 +94,14 @@ This uses FactCheck and throws exceptions if element is not passing all tests.
function test_element(element_type)
Logging.info("Testing element $element_type")
local element
n = get_number_of_basis_functions(element_type)
Logging.info("number of basis functions in this element: $n")
@fact n --> not(nothing) """
Unable to determine number of nodes for $eltype define a function
'get_number_of_basis_functions' which returns the number of nodes
for this element."""
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")
Logging.info("Initializing element")
try
@@ -112,45 +113,23 @@ function test_element(element_type)
return false
end
dim = get_element_dimension(element_type)
Logging.info("Element dimension: $dim")
@fact dim --> not(nothing) """
Unable to get element dimension define function 'get_element_dimension'
which return the dimension of this element (1, 2, 3)"""
# try to interpolate some scalar field
field = Field(0.0, collect(1:n))
Logging.info("Creating new scalar field $field")
fieldset = FieldSet("field1")
push!(fieldset, field)
push!(element, fieldset)
push!(element, FieldSet("field1", [Field(0.0, collect(1:n))]))
# TODO: how to parametrize this?
push!(element, FieldSet("geometry", [Field(0.0, Vector[[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 1.0]])]))
# evaluate basis functions at middle point of element
basis = get_basis(element)
dbasis = grad(basis)
mid = zeros(dim)
try
basis = get_basis(element)
val1 = basis(mid, 0.0)
Logging.info("basis at $mid: $val1")
val2 = basis("field1", mid, 0.0)
Logging.info("field val at $mid: $val2")
catch
Logging.error("""
Unable to evaluate basis, define function 'get_basis' for
this element.""")
end
try
basis = get_basis(element)
dbasis = grad(basis)
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")
catch
Logging.error("""
Unable to evaluate partial derivatives of basis,
define function 'get_dbasisdxi' for this element.""")
end
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")
Logging.info("Element $element_type passed tests.")
end
@@ -184,73 +163,70 @@ function call(u::FunctionSpace, field_name, xi::Vector, t::Number=Inf, variation
return f.values
end
h = u.element.basis.basis(xi)
return h*f
return dot(vec(h), f)
end
""" If basis is called without a field, return basis functions evaluated at that point. """
function call(u::FunctionSpace, xi::Vector, t::Number=Inf)
return u.element.basis.basis(xi)'
return u.element.basis.basis(xi)
end
""" 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)
b = gradu.element.basis.dbasisdxi(xi)
return b*f*inv(b*X)
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
end
""" 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)
b = gradu.element.basis.dbasisdxi(xi)
return (b*inv(b*X))'
dN = gradu.element.basis.dbasisdxi(xi)
J = sum([dN[:,i]*X[i]' for i=1:length(X)])
grad = inv(J)*dN
return grad
end
# on-line functions to get api more easy to use, ip -> xi.ip
call(u::FunctionSpace, ip::IntegrationPoint, t::Number) = call(u, ip.xi, t)
call(u::FunctionSpace, ip::IntegrationPoint) = call(u, ip.xi)
call(u::GradientFunctionSpace, ip::IntegrationPoint, t::Number) = call(u, ip.xi, t)
call(u::GradientFunctionSpace, ip::IntegrationPoint) = call(u, ip.xi)
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...)
""" Return field from function space. """
""" Return a field from function space. """
function get_field(u::FunctionSpace, field_name, time=Inf)
return u.element[field_name](time)
end
""" Return field from function space. """
""" Return a field from function space. """
function get_field(u::FunctionSpace, field_name, time=Inf, variation=nothing)
return !isa(variation, Void) ? variation : u.element[field_name](time)
end
""" Return fieldset from function space. """
""" Return a fieldset from function space. """
function get_fieldset(u::FunctionSpace, field_name)
return u.element[field_name]
end
# 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)
function jacobian(u::FunctionSpace, xi, t)
u.element.basis.dbasisdxi(xi)*u.element["geometry"](t)
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)
end
function jacobian(u::FunctionSpace, ip::IntegrationPoint, t::Number)
jacobian(u, ip.xi, t)
function LinAlg.det(u::FunctionSpace, ip::IntegrationPoint, t::Number=Inf)
LinAlg.det(u, ip.xi, t)
end
function jacobian(u::FunctionSpace, xi)
jacobian(u, xi, Inf)
end
function LinAlg.det(u::FunctionSpace)
function detJ(args...)
J = jacobian(u, args...)
m, n = size(J)
return m == n ? det(J) : norm(J)
end
return detJ
return (args...) -> det(u, args...)
end
function get_basis(element::Element)
@@ -265,7 +241,7 @@ Base.(:-)(u::GradientFunctionSpace, v::GradientFunctionSpace) = (args...) -> u(a
""" Check does fieldset exist. """
function Base.haskey(element::Element, what)
haskey(element.fields, symbol(what))
haskey(element.fields, what)
end
+30 -49
View File
@@ -11,17 +11,12 @@ type LocalAssembly <: Assembly
mass_matrix :: Matrix
stiffness_matrix :: Matrix
force_vector :: Matrix
potential_energy# :: Union{Array, Float64}
potential_energy
residual_vector :: Vector
end
function LocalAssembly(ndofs, mass_matrix, stiffness_matrix, force_vector::Matrix)
LocalAssembly(ndofs, mass_matrix, stiffness_matrix, force_vector[:])
end
""" Initialize workspace for local assembly. """
function LocalAssembly(equation::Equation)
ndofs = size(equation)
""" Initialize workspace for local matrices for dimension ndofs. """
function initialize_local_assembly(ndofs::Int=1)
mass_matrix = zeros(ndofs, ndofs)
stiffness_matrix = zeros(ndofs, ndofs)
force_vector = zeros(ndofs, 1)
@@ -31,14 +26,24 @@ function LocalAssembly(equation::Equation)
potential_energy, residual_vector)
end
""" Initialize workspace for local matrices, get dimension from equation. """
function initialize_local_assembly(equation::Equation)
LocalAssembly(equation)
ndofs = prod(size(equation))
return initialize_local_assembly(ndofs)
end
function initialize_local_assembly(equation::Equation, assembly::LocalAssembly)
if size(equation) != assembly.ndofs
""" Initialize or zero workspace. """
function initialize_local_assembly!(assembly::LocalAssembly, equation::Equation)
ndofs = prod(size(equation))
if ndofs != assembly.ndofs
# if problem size changes, automatically initialize new work space
return initialize_local_assembly(equation)
assembly.ndofs = ndofs
assembly.mass_matrix = zeros(ndofs, ndofs)
assembly.stiffness_matrix = zeros(ndofs, ndofs)
assembly.force_vector = zeros(ndofs, 1)
assembly.potential_energy = 0.0
assembly.residual_vector = zeros(ndofs)
return
end
# otherwise, empty workspace ready for next iteration
fill!(assembly.mass_matrix, 0.0)
@@ -46,15 +51,7 @@ function initialize_local_assembly(equation::Equation, assembly::LocalAssembly)
fill!(assembly.force_vector, 0.0)
assembly.potential_energy = 0.0
fill!(assembly.residual_vector, 0.0)
return assembly
end
function initialize_local_assembly(assembly::LocalAssembly, equation::Equation)
initialize_local_assembly(equation, assembly)
end
function get_unknown_field_name(equation::Equation)
eqtype = typeof(equation)
error("define get_unknown_field_name for this equation type $eqtype")
return
end
has_mass_matrix(equation::Equation) = false
@@ -73,14 +70,15 @@ get_integration_points(equation::Equation) = equation.integration_points
""" Return a local assembly for element. """
function calculate_local_assembly!(assembly::LocalAssembly, equation::Equation, time::Number=Inf)
function calculate_local_assembly!(assembly::LocalAssembly, equation::Equation,
unknown_field_name::ASCIIString, time::Number=Inf,
problem=nothing)
initialize_local_assembly(assembly, equation) # zero all
initialize_local_assembly!(assembly, equation) # zero all
element = get_element(equation)
basis = get_basis(element)
detJ = det(basis)
field_name = get_unknown_field_name(equation)
# 1. if equations are defined we just integrate them
if has_mass_matrix(equation) || has_stiffness_matrix(equation) || has_force_vector(equation)
@@ -96,17 +94,16 @@ function calculate_local_assembly!(assembly::LocalAssembly, equation::Equation,
assembly.force_vector += s*get_force_vector(equation, ip, time)[:]
end
# external loads -- if any nodal loads is defined add to force vector
if haskey(element, "$field_name nodal load")
assembly.force_vector += element["$field_name nodal load"](time)[:]
if haskey(element, "$unknown_field_name nodal load")
assembly.force_vector += element["$unknown_field_name nodal load"](time)[:]
end
end
end
# 2. variational / energy form - user has defined some potential energy / variational form
if has_potential_energy(equation)
field_name = get_unknown_field_name(equation)
element = get_element(equation)
field = element[field_name](time)
field = element[unknown_field_name](time)
function potential_energy(data::Vector)
# calculate potential energy for some setting. this is needed by forwarddiff
assembly.potential_energy = 0.0
@@ -117,8 +114,8 @@ function calculate_local_assembly!(assembly::LocalAssembly, equation::Equation,
assembly.potential_energy += ip.weight * dw * detJ(ip)
end
# external energy -- if any nodal loads is defined, decrease from potential energy
if haskey(element, "$field_name nodal load")
P = element["$field_name nodal load"](time)
if haskey(element, "$unknown_field_name nodal load")
P = element["$unknown_field_name nodal load"](time)
assembly.potential_energy -= dot(P[:], df[:])
end
if isa(assembly.potential_energy, Array)
@@ -135,9 +132,8 @@ function calculate_local_assembly!(assembly::LocalAssembly, equation::Equation,
# 3. virtual work form - user has defined residual vector δW_int(u,δu) + δW_ext(u,δu) = 0 ∀ v
if has_residual_vector(equation)
field_name = get_unknown_field_name(equation)
element = get_element(equation)
field = element[field_name](time)
field = element[unknown_field_name](time)
function residual_vector(data::Vector)
fill!(assembly.residual_vector, 0.0)
df = similar(field, data)
@@ -147,8 +143,8 @@ function calculate_local_assembly!(assembly::LocalAssembly, equation::Equation,
assembly.residual_vector += ip.weight*dr*detJ(ip)
end
# external loads -- if any nodal loads is defined, remove from residual
if haskey(element, "$field_name nodal load")
assembly.residual_vector -= element["$field_name nodal load"](time)[:]
if haskey(element, "$unknown_field_name nodal load")
assembly.residual_vector -= element["$unknown_field_name nodal load"](time)[:]
end
return assembly.residual_vector
end
@@ -160,18 +156,3 @@ function calculate_local_assembly!(assembly::LocalAssembly, equation::Equation,
end
function calculate_local_assembly!(equation::Equation, assembly::LocalAssembly, time::Number=Inf)
calculate_local_assembly!(assembly, equation)
end
""" Get global degrees of freedom for this element. """
function get_global_dofs(eq::Equation)
eq.global_dofs
end
""" Set global degrees of freedom for this element. """
function set_global_dofs!(eq::Equation, dofs)
eq.global_dofs = dofs
end
+38 -34
View File
@@ -6,68 +6,72 @@
abstract HeatProblem <: Problem
abstract HeatEquation <: Equation
get_unknown_field_name(eq::HeatEquation) = symbol("temperature")
### Plane heat problem + equations ###
type PlaneHeatProblem <: HeatProblem
unknown_field_name :: ASCIIString
unknown_field_dimension :: Int
equations :: Array{HeatEquation, 1}
element_mapping :: Dict{DataType, DataType}
end
""" Default constructor for problem takes no arguments. """
function PlaneHeatProblem()
return PlaneHeatProblem([])
element_mapping = Dict(
Quad4 => DC2D4,
Seg2 => DC2D2)
return PlaneHeatProblem("temperature", 1, [], element_mapping)
end
""" Return dimension of unknown field variable, temperature is scalar field. """
get_dimension(pr::Type{PlaneHeatProblem}) = 1
""" Map Lagrange element Quad4 to equation DC2D4 """
get_equation(pr::Type{PlaneHeatProblem}, el::Type{Quad4}) = DC2D4
""" Map Lagrange element Seg2 to equation DC2D2 """
get_equation(pr::Type{PlaneHeatProblem}, el::Type{Seg2}) = DC2D2
""" Diffusive heat transfer for 4-node bilinear element. """
type DC2D4 <: HeatEquation
element :: Quad4
integration_points :: Array{IntegrationPoint, 1}
global_dofs :: Array{Int64, 1}
end
function DC2D4(element::Quad4)
integration_points = [
IntegrationPoint(1.0/sqrt(3.0)*[-1, -1], 1.0),
IntegrationPoint(1.0/sqrt(3.0)*[ 1, -1], 1.0),
IntegrationPoint(1.0/sqrt(3.0)*[ 1, 1], 1.0),
IntegrationPoint(1.0/sqrt(3.0)*[-1, 1], 1.0)]
integration_points = get_default_integration_points(element)
push!(element, FieldSet("temperature"))
DC2D4(element, integration_points, [])
DC2D4(element, integration_points)
end
function get_lhs(equation::DC2D4, ip, time)
element = get_element(equation)
dNdX = get_dbasisdX(element, ip.xi, time)
k = interpolate(element, "temperature thermal conductivity", ip.xi, time)
return dNdX*k*dNdX'
end
JuliaFEM.has_lhs(eq::DC2D4) = true
Base.size(equation::DC2D4) = (1, 4)
""" Diffusive heat transfer for 2-node linear segment. """
type DC2D2 <: HeatEquation
element :: Seg2
integration_points :: Array{IntegrationPoint, 1}
global_dofs :: Array{Int64, 1}
end
function DC2D2(element::Seg2)
integration_points = [IntegrationPoint([0.0], 2.0)]
integration_points = get_default_integration_points(element)
push!(element, FieldSet("temperature"))
DC2D2(element, integration_points, [])
DC2D2(element, integration_points)
end
function get_rhs(equation::DC2D2, ip, time)
Base.size(equation::DC2D2) = (1, 2)
function calculate_local_assembly!(assembly::LocalAssembly, equation::HeatEquation,
unknown_field_name::ASCIIString, time::Number=Inf,
problem=nothing)
initialize_local_assembly!(assembly, equation)
element = get_element(equation)
h = get_basis(element, ip.xi)
f = interpolate(element, "temperature flux", ip.xi, time)
return h*f
basis = get_basis(element)
dbasis = grad(basis)
detJ = det(basis)
for ip in get_integration_points(equation)
w = ip.weight * detJ(ip)
# evaluate fields in integration point
ρ = basis("density", ip, time)
k = basis("temperature thermal conductivity", ip, time)
f = basis("temperature load", ip, time)
# evaluate basis functions and gradient in integration point
N = basis(ip, time)
dN = dbasis(ip, time)
# do assembly
assembly.mass_matrix += w * ρ*N'*N
assembly.stiffness_matrix += w * k*dN'*dN
assembly.force_vector += w * N'*f
end
end
JuliaFEM.has_rhs(eq::DC2D2) = true
+17
View File
@@ -0,0 +1,17 @@
# This file is a part of JuliaFEM.
# License is MIT: see https://github.com/JuliaFEM/JuliaFEM.jl/blob/master/LICENSE.md
function get_default_integration_points(element::Quad4)
[
IntegrationPoint(1.0/sqrt(3.0)*[-1, -1], 1.0),
IntegrationPoint(1.0/sqrt(3.0)*[ 1, -1], 1.0),
IntegrationPoint(1.0/sqrt(3.0)*[ 1, 1], 1.0),
IntegrationPoint(1.0/sqrt(3.0)*[-1, 1], 1.0)
]
end
function get_default_integration_points(element::Seg2)
[
IntegrationPoint([0.0], 2.0)
]
end
+3 -3
View File
@@ -59,7 +59,7 @@ function interpolate(basis::Basis, field::Field, ip::IntegrationPoint)
interpolate(basis, field, ip.xi)
end
function dinterpolate(basis::Basis, u::Field, xi::Array{Float64, 1})
basis.dbasisdxi(xi)*u
end
#function dinterpolate(basis::Basis, u::Field, xi::Array{Float64, 1})
# basis.dbasisdxi(xi)*u
#end
+12 -10
View File
@@ -17,8 +17,9 @@ function calculate_lagrange_basis(P, X)
end
# Logging.debug("Calculating inverse of A")
invA = inv(A)'
basis(xi) = invA*P(xi)
basis
basis(xi) = (invA*P(xi))'
dbasisdxi(xi) = (ForwardDiff.jacobian((xi) -> invA*P(xi), xi, cache=autodiffcache))'
basis, dbasisdxi
end
"""
@@ -33,21 +34,22 @@ macro create_lagrange_element(element_name, element_description, X, P)
eltype = esc(element_name)
quote
global get_element_description
global get_number_of_basis_functions, get_element_dimension
dim = size($X, 1)
nbasis = size($X, 2)
basis = calculate_lagrange_basis($P, $X)
#global get_number_of_basis_functions, get_element_dimension
#dim = size($X, 1)
#nbasis = size($X, 2)
basis, dbasisdxi = calculate_lagrange_basis($P, $X)
type $eltype <: CG
connectivity :: Array{Int, 1}
basis :: Basis
fields :: Dict{Symbol, FieldSet}
fields :: Dict{ASCIIString, FieldSet}
end
function $eltype(connectivity, args...)
$eltype(connectivity, Basis(basis), Dict())
$eltype(connectivity, Basis(basis, dbasisdxi), Dict())
end
get_element_description(el::Type{$eltype}) = $element_description
get_number_of_basis_functions(el::Type{$eltype}) = nbasis
get_element_dimension(el::Type{$eltype}) = dim
#get_number_of_basis_functions(el::Type{$eltype}) = nbasis
#get_element_dimension(el::Type{$eltype}) = dim
Base.size(el::Type{$eltype}) = Base.size($X)
end
end
-115
View File
@@ -1,115 +0,0 @@
# This file is a part of JuliaFEM.
# License is MIT: see https://github.com/JuliaFEM/JuliaFEM.jl/blob/master/LICENSE.md
using ForwardDiff
"""
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...)
fld = get_field(el, field)
dim, nnodes = size(fld)
function helper!(x, y)
orig = copy(fld)
set_field(el, field, reshape(x, dim, nnodes))
y[:] = f(el, args...)
set_field(el, field, copy(orig))
end
jac = ForwardDiff.forwarddiff_jacobian(helper!, Float64, fadtype=:dual, n=dim*nnodes, m=dim*nnodes)
return jac(fld[:])
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
"""
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, :geometry, 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, :geometry, ip.xi; derivative=true)
el.attributes[target][:,:] += ip.weight*f(el, ip)*det(J)
end
end
function linearize(eq::Equation, f::Function, field::ASCIIString)
function jacobian(eq::Equation, args...)
el = get_element(eq)
fld = get_field(el, field)
dim, nnodes = size(fld)
function helper(x::Vector)
orig = copy(fld)
set_field(el, field, reshape(x, dim, nnodes))
y = f(eq, args...)
set_field(el, field, orig)
return y[:]
end
jac = ForwardDiff.jacobian(helper)
return jac(fld[:])
end
return jacobian
end
+11 -130
View File
@@ -5,141 +5,22 @@ abstract Problem
abstract BoundaryProblem <: Problem
abstract FieldProblem <: Problem
get_equations(pr::Problem) = pr.equations
function get_dimension(pr::Type{Problem})
throw("Unable to determine problem dimension for problem $pr")
function get_equations(problem::Problem)
problem.equations
end
function get_equation(pr::Type{Problem}, el::Type{Element})
throw("Could not find corresponding equation for element $el in problem $pr")
function get_unknown_field_dimension(problem::Problem)
problem.unknown_field_dimension
end
"""
Add new element to problem
"""
function add_element!(problem::Problem, element::Element)
equation = get_equation(typeof(problem), typeof(element))
push!(problem.equations, equation(element))
function get_unknown_field_name(problem::Problem)
problem.unknown_field_name
end
""" Add new element to problem. """
function Base.push!(problem::Problem, element::Element)
equation = get_equation(typeof(problem), typeof(element))
push!(problem.equations, equation(element))
end
"""
Return total number of basis functions in problem
"""
function get_number_of_basis_functions(pr::Problem)
conn = Int[]
for eq in get_equations(pr)
append!(conn, get_connectivity(eq))
end
length(unique(conn))
end
"""
Problem matrix size dimension
"""
function get_matrix_dimension(pr::Problem)
get_dimension(typeof(pr))*get_number_of_basis_functions(pr)
end
"""
Assign global dofs for element. This doesn't do any reordering.
"""
function set_global_dofs!(pr::Problem)
#ndim = get_dimension(pr)*get_number_of_basis_functions(pr)
#ndim = get_matrix_dimension(pr)
dim = get_dimension(typeof(pr))
nconn = get_number_of_basis_functions(pr)
ndim = dim*nconn
Logging.debug("Problem (matrix) dimension: $ndim")
gdofs = reshape(collect(1:ndim), dim, nconn)
for eq in get_equations(pr)
lconn = get_connectivity(eq)
gconn = gdofs[:, lconn][:]
set_global_dofs!(eq, gconn)
end
end
""" Return unique list of connectivity (i.e. node ids). """
function get_connectivity(problem::Problem)
connectivity = Int[]
for equation in get_equations(problem)
element = get_element(equation)
append!(connectivity, get_connectivity(element))
end
connectivity = unique(connectivity)
return connectivity
end
"""
Calculate global dofs for equations, maybe using some bandwidth
minimizing or fill reducing algorithm
"""
function calculate_global_dofs(pr::Problem)
conn = get_connectivity(pr)
dim = get_dimension(typeof(pr))
ndofs = dim*length(conn)
Logging.debug("total dofs: $ndofs")
mconn = maximum(conn)
gdofs = reshape(collect(1:mconn), dim, mconn)
dofmap = Dict{Int64, Array{Int64, 1}}()
for (i, c) in enumerate(conn)
dofmap[c] = gdofs[:, i]
end
return dofmap
end
"""
Assign global dofs for equations.
"""
function assign_global_dofs!(pr::Problem, dofmap)
for eq in get_equations(pr)
el = get_element(eq)
c = get_connectivity(el)
#gdofs = [dofmap[ci] for ci in c]
gdofs = Int64[]
for ci in c
append!(gdofs, dofmap[ci])
end
set_global_dofs!(eq, gdofs)
end
end
function get_lhs(pr::Problem, t::Float64)
I = Int64[]
J = Int64[]
V = Float64[]
dim = get_dimension(typeof(pr))
for eq in filter(has_lhs, get_equations(pr))
dofs = get_global_dofs(eq)
lhs = integrate_lhs(eq, t)
for (li, i) in enumerate(dofs)
for (lj, j) in enumerate(dofs)
push!(I, i)
push!(J, j)
push!(V, lhs[li, lj])
end
end
end
return I, J, V
end
function get_rhs(pr::Problem, t::Float64)
I = Int64[]
V = Float64[]
dim = get_dimension(typeof(pr))
for eq in filter(has_rhs, get_equations(pr))
dofs = get_global_dofs(eq)
rhs = integrate_rhs(eq, t)
for (li, i) in enumerate(dofs)
push!(I, i)
push!(V, rhs[li])
end
end
return I, V
element_type = typeof(element)
equation_type = problem.element_mapping[element_type]
push!(problem.equations, equation_type(element))
end
+27 -22
View File
@@ -15,6 +15,9 @@ end
function Field(time, values)
Field(time, 0, values)
end
function Field(values)
Field(0.0, 0, values)
end
""" Get length of a field (number of basis functions in practice). """
function Base.length(f::Field)
length(f.values)
@@ -31,16 +34,23 @@ end
function Base.(:*)(k::Number, f::Field)
Field(f.time, k*f.values)
end
""" Multiply field with some vector x. """
function Base.(:*)(x::Vector, f::Field)
""" Inner product of field and vector x. """
function Base.dot(x::Vector, f::Field)
@assert length(x) == length(f)
sum([f[i]*x[i] for i in 1:length(f)])
end
""" Multiply field with some matrix x. """
# function Base.(:*){T}(x::Matrix, f::Field{Vector{T}})
function Base.(:*)(x::Matrix, f::Field)
sum([f[i]*x[i,:] for i in 1:length(f)])
function Base.size(field::Field)
(length(field.values[1]), length(field.values))
end
#""" Multiply field with some matrix x. """
# function Base.(:*){T}(x::Matrix, f::Field{Vector{T}})
#function Base.(:*)(x::Matrix, f::Field)
# sum([f[i]*x[:,i]' for i in 1:length(f)])
#end
""" Sum two fields. """
function Base.(:+)(f1::Field, f2::Field)
@assert(f1.time == f2.time, "Cannot add fields: time mismatch, $(f1.time) != $(f2.time)")
@@ -59,6 +69,9 @@ Examples
function Base.getindex(field::Field, c::Colon)
[field.values...;]
end
function Base.vec(field::Field)
[field.values...;]
end
""" Return field similar to input but with new data in it.
@@ -87,20 +100,17 @@ end
""" FieldSet is set of fields, each field can have different time and/or increment. """
type FieldSet
name :: Symbol
name :: ASCIIString
fields :: Array{Field, 1}
end
""" Initializer for FieldSet. """
function FieldSet(field_name)
FieldSet(Symbol(field_name), [])
FieldSet(field_name, [])
end
function FieldSet()
FieldSet(Symbol("unknown field"), [])
FieldSet("unknown field", [])
end
""" Add new field to fieldset. """
function Base.push!(fs::FieldSet, field::Field)
@@ -127,15 +137,10 @@ type Basis
basis :: Function
dbasisdxi :: Function
end
""" Constructor of basis function. """
function Basis(basis)
Basis(basis, ForwardDiff.jacobian(basis))
end
""" Get partial derivative of basis function. """
function grad(basis::Basis)
(ip) -> basis.dbasisdxi(ip.xi)
end
#""" Constructor of basis function. """
#function Basis(basis)
# Basis(basis, ForwardDiff.jacobian(basis))
#end
"""
Integration point
@@ -151,7 +156,7 @@ attributes :: Dict{Any, Any}
type IntegrationPoint
xi :: Array{Float64, 1}
weight :: Float64
fields :: Dict{Symbol, FieldSet}
fields :: Dict{ASCIIString, FieldSet}
end
function IntegrationPoint(xi, weight)
IntegrationPoint(xi, weight, Dict())