rewrite assembly, see #69. a lot of tests probably fail but the most important ones pass

This commit is contained in:
Jukka Aho
2015-11-12 21:54:46 +02:00
parent 9a7db4aef1
commit 95bee76438
12 changed files with 317 additions and 370 deletions
+1
View File
@@ -63,6 +63,7 @@ include("lagrange.jl") # Lagrange elements
### EQUATIONS ###
include("integrate.jl") # default integration points for elements
include("sparse.jl")
include("equations.jl")
include("problems.jl")
+4 -56
View File
@@ -3,61 +3,9 @@
# Functions to handle global assembly of problem
""" Global assembly. """
type GlobalAssembly <: Assembly
ndofs :: Int
mass_matrix :: SparseMatrixCSC
stiffness_matrix :: SparseMatrixCSC
force_vector :: SparseMatrixCSC
end
""" Initialize global assembly of size ndofs. """
function initialize_global_assembly(ndofs::Int=1)
mass_matrix = spzeros(ndofs, ndofs)
stiffness_matrix = spzeros(ndofs, ndofs)
force_vector = spzeros(ndofs, 1)
return GlobalAssembly(ndofs, mass_matrix, stiffness_matrix, force_vector)
end
""" Initialize global assembly, get dimension from problem. """
function initialize_global_assembly(problem::Problem)
dim, ndofs = size(problem)
return initialize_global_assembly(ndofs)
end
""" Initialize or empty workspace for global assembly. """
function initialize_global_assembly!(assembly::GlobalAssembly, problem::Problem)
ndofs = prod(size(problem))
if ndofs != assembly.ndofs
# if problem size changes, automatically initialize new work space
assembly.ndofs = ndofs
assembly.mass_matrix = spzeros(ndofs, ndofs)
assembly.stiffness_matrix = spzeros(ndofs, ndofs)
assembly.force_vector = spzeros(ndofs, 1)
return
end
# otherwise, empty workspace ready for next iteration
fill!(assembly.mass_matrix, 0.0)
fill!(assembly.stiffness_matrix, 0.0)
fill!(assembly.force_vector, 0.0)
return
end
""" Calculate global assembly for a problem. """
function calculate_global_assembly!(assembly::GlobalAssembly, problem::Problem, time::Number=Inf)
unknown_field_name = get_unknown_field_name(problem)
initialize_global_assembly!(assembly, problem) # zero all
dim, ndofs = size(problem)
info("assembling problem for $unknown_field_name")
info("dimension of unknown field: $dim, problem dofs: $ndofs")
local_assembly = initialize_local_assembly()
for (i, equation) in enumerate(get_equations(problem))
calculate_local_assembly!(local_assembly, equation, unknown_field_name, time)
conn = get_connectivity(get_element(equation))
gdofs = get_gdofs(problem, equation)
assembly.mass_matrix[gdofs, gdofs] += local_assembly.mass_matrix
assembly.stiffness_matrix[gdofs, gdofs] += local_assembly.stiffness_matrix
assembly.force_vector[gdofs] += local_assembly.force_vector
function assemble!(assembly::Assembly, problem::Problem, time::Number=0.0)
empty!(assembly)
for equation in get_equations(problem)
assemble!(assembly, equation, time, problem)
end
end
+8 -4
View File
@@ -5,6 +5,10 @@
abstract DirichletEquation <: Equation
function get_unknown_field_name(equation::DirichletEquation)
return "reaction force"
end
### Dirichlet problem + equations
type DirichletProblem <: BoundaryProblem
@@ -56,19 +60,19 @@ function DBC2D2(element::Seg2)
end
Base.size(equation::DBC2D2) = (1, 2)
function calculate_local_assembly!(assembly::LocalAssembly, equation::DirichletEquation, unknown_field_name::ASCIIString, time::Number=0.0, problem=nothing)
initialize_local_assembly!(assembly, equation)
function assemble!(assembly::Assembly, equation::DirichletEquation, time::Number=0.0, problem=nothing)
gdofs = get_gdofs(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
add!(assembly.stiffness_matrix, gdofs, gdofs, w*N'*N)
if !isa(problem, Void)
X = basis("geometry", ip, time)
u = problem.field_value(X)
assembly.force_vector += w * N'*u
add!(assembly.force_vector, gdofs, w*N'*u)
end
end
end
+4
View File
@@ -6,6 +6,10 @@
abstract ElasticityProblem <: Problem
abstract ElasticityEquation <: Equation
function get_unknown_field_name(equation::ElasticityEquation)
return "displacement"
end
### Formulation ###
""" Calculate internal energy for elasticity equation.
+115 -141
View File
@@ -5,203 +5,177 @@
abstract Equation
abstract Assembly
""" Local element assembly. """
type LocalAssembly <: Assembly
ndofs :: Int
mass_matrix :: Matrix
stiffness_matrix :: Matrix
force_vector :: Matrix
potential_energy
residual_vector :: Vector
type Assembly
mass_matrix :: SparseMatrixIJV
stiffness_matrix :: SparseMatrixIJV
force_vector :: SparseMatrixIJV
lhs :: SparseMatrixIJV
rhs :: SparseMatrixIJV
end
""" 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)
potential_energy = 0.0
residual_vector = zeros(ndofs)
return LocalAssembly(ndofs, mass_matrix, stiffness_matrix, force_vector,
potential_energy, residual_vector)
function Assembly()
return Assembly(
SparseMatrixIJV(),
SparseMatrixIJV(),
SparseMatrixIJV(),
SparseMatrixIJV(),
SparseMatrixIJV())
end
""" Initialize workspace for local matrices, get dimension from equation. """
function initialize_local_assembly(equation::Equation)
ndofs = prod(size(equation))
return initialize_local_assembly(ndofs)
function Base.empty!(assembly::Assembly)
empty!(assembly.mass_matrix)
empty!(assembly.stiffness_matrix)
empty!(assembly.force_vector)
empty!(assembly.lhs)
empty!(assembly.rhs)
end
""" 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
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)
fill!(assembly.stiffness_matrix, 0.0)
fill!(assembly.force_vector, 0.0)
assembly.potential_energy = 0.0
fill!(assembly.residual_vector, 0.0)
return
function get_mass_matrix
end
has_mass_matrix(equation::Equation) = false
function get_mass_matrix(equation::Equation, ip, time=0.0, problem=nothing)
get_mass_matrix(equation, ip, time)
end
function get_mass_matrix(equation::Equation, ip, time=0.0)
get_mass_matrix(equation, ip)
end
function get_mass_matrix(equation::Equation, ip)
nothing
function get_stiffness_matrix
end
has_stiffness_matrix(equation::Equation) = false
function get_stiffness_matrix(equation::Equation, ip, time=0.0, problem=nothing)
get_stiffness_matrix(equation, ip, time)
end
function get_stiffness_matrix(equation::Equation, ip, time=0.0)
get_stiffness_matrix(equation, ip)
end
function get_stiffness_matrix(equation::Equation, ip)
nothing
function get_force_vector
end
has_force_vector(equation::Equation) = false
function get_force_vector(equation::Equation, ip, time=0.0, problem=nothing)
get_force_vector(equation, ip, time)
end
function get_force_vector(equation::Equation, ip, time=0.0)
get_force_vector(equation, ip)
end
function get_force_vector(equation::Equation, ip)
nothing
function get_potential_energy
end
has_residual_vector(equation::Equation) = false
function get_residual_vector(equation::Equation, ip, time=0.0, problem=nothing)
get_residual_vector(equation, ip, time)
end
function get_residual_vector(equation::Equation, ip, time=0.0)
get_residual_vector(equation, ip)
end
function get_residual_vector(equation::Equation, ip)
nothing
function get_residual_vector
end
has_potential_energy(equation::Equation) = false
function get_potential_energy(equation::Equation, ip, time=0.0, problem=nothing)
get_potential_energy(equation, ip, time)
end
function get_potential_energy(equation::Equation, ip, time=0.0)
get_potential_energy(equation, ip)
end
function get_potential_energy(equation::Equation, ip)
nothing
function has_mass_matrix(equation::Equation)
default_args = Tuple{typeof(equation), IntegrationPoint, Float64}
return method_exists(get_mass_matrix, default_args)
end
get_element(equation::Equation) = equation.element
get_integration_points(equation::Equation) = equation.integration_points
function has_stiffness_matrix(equation::Equation)
default_args = Tuple{typeof(equation), IntegrationPoint, Float64}
return method_exists(get_stiffness_matrix, default_args)
end
function has_force_vector(equation::Equation)
default_args = Tuple{typeof(equation), IntegrationPoint, Float64}
return method_exists(get_force_vector, default_args)
end
""" Return a local assembly for element. """
function calculate_local_assembly!(assembly::LocalAssembly, equation::Equation,
unknown_field_name::ASCIIString, time::Number=0.0,
problem=nothing)
function has_potential_energy(equation::Equation)
default_args = Tuple{typeof(equation), IntegrationPoint, Float64}
return method_exists(get_potential_energy, default_args)
end
initialize_local_assembly!(assembly, equation) # zero all
function has_residual_vector(equation::Equation)
default_args = Tuple{typeof(equation), IntegrationPoint, Float64}
return method_exists(get_residual_vector, default_args)
end
function get_element(equation::Equation)
return equation.element
end
function get_integration_points(equation::Equation)
return equation.integration_points
end
function Base.size(equation::Equation, i::Int)
return size(equation)[i]
end
""" Return global degrees of freedom of element in matrix level.
Notes
-----
This is calculated from connectivity and equation dimension.
"""
function get_gdofs(equation::Equation)
element = get_element(equation)
conn = get_connectivity(element)
dim = size(equation, 1)
gdofs = vec(vcat([dim*conn'-i for i=dim-1:-1:0]...))
return gdofs
end
""" Assemble element. """
function assemble!(assembly::Assembly, equation::Equation, time::Number=0.0, problem=nothing)
element = get_element(equation)
gdofs = get_gdofs(equation)
basis = get_basis(element)
detJ = det(basis)
unknown_field_name = get_unknown_field_name(equation)
# 1. if equations are defined we just integrate them
# 1. if equations are defined we just integrate them, without caring how they are done
if has_mass_matrix(equation) || has_stiffness_matrix(equation) || has_force_vector(equation)
for ip in get_integration_points(equation)
s = ip.weight*detJ(ip)
if has_mass_matrix(equation)
assembly.mass_matrix += s*get_mass_matrix(equation, ip, time, problem)
add!(assembly.mass_matrix, gdofs, gdofs, s*get_mass_matrix(equation, ip, time))
end
if has_stiffness_matrix(equation)
assembly.stiffness_matrix += s*get_stiffness_matrix(equation, ip, time, problem)
add!(assembly.stiffness_matrix, gdofs, gdofs, s*get_stiffness_matrix(equation, ip, time))
end
if has_force_vector(equation)
assembly.force_vector += s*get_force_vector(equation, ip, time, problem)
add!(assembly.force_vector, gdofs, s*get_force_vector(equation, ip, time))
end
end
# external loads -- if any nodal loads is defined add to force vector
if haskey(element, "$unknown_field_name nodal load")
assembly.force_vector += vec(element["$unknown_field_name nodal load"](time))
add!(assembly.force_vector, gdofs, vec(element["$unknown_field_name nodal load"](time)))
end
end
# 2. variational / energy form - user has defined some potential energy / variational form
# 2. energy form -- user has defined potential energy W -> min!
if has_potential_energy(equation)
element = get_element(equation)
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
""" Wrapper for potential energy for ForwardDiff. """
function calc_W(data::Vector)
W = 0.0
df = similar(field, data)
# integrate potential energy
for ip in get_integration_points(equation)
s = ip.weight*detJ(ip)
dw = get_potential_energy(equation, ip, time; variation=df)
assembly.potential_energy += ip.weight * dw * detJ(ip)
W += s*dw
end
# external energy -- if any nodal loads is defined, decrease from potential energy
if haskey(element, "$unknown_field_name nodal load")
P = element["$unknown_field_name nodal load"](time)
assembly.potential_energy -= dot(vec(P), vec(df))
W -= dot(vec(P), vec(df))
end
if isa(assembly.potential_energy, Array)
return assembly.potential_energy[1]
end
return assembly.potential_energy
return isa(W, Array) ? W[1] : W
end
hessian, allresults = ForwardDiff.hessian(potential_energy, vec(field),
AllResults, cache=autodiffcache)
assembly.stiffness_matrix += hessian
assembly.force_vector -= ForwardDiff.gradient(allresults) # <--- minus explained in tutorial
assembly.potential_energy = ForwardDiff.value(allresults)
#info("potential energy of system: $(assembly.potential_energy)")
hessian, allresults = ForwardDiff.hessian(calc_W, vec(field), AllResults, cache=autodiffcache)
add!(assembly.stiffness_matrix, gdofs, gdofs, hessian)
add!(assembly.force_vector, gdofs, -ForwardDiff.gradient(allresults))
end
# 3. virtual work form - user has defined residual vector δW_int(u,δu) + δW_ext(u,δu) = 0 ∀ v
# 3. virtual work -- user has defined some residual r = p - f = 0
if has_residual_vector(equation)
element = get_element(equation)
field = element[unknown_field_name](time)
function residual_vector(data::Vector)
fill!(assembly.residual_vector, 0.0)
#@debug("field: $field, length = $(size(field))")
#@debug("data: $data, size = $(size(data))")
#df = similar(field, data)
df = Increment(reshape(data, size(equation)...))
# integrate W
for ip in get_integration_points(equation)
dr = get_residual_vector(equation, ip, time; variation=df)
assembly.residual_vector += ip.weight*dr*detJ(ip)
end
# external loads -- if any nodal loads is defined, remove from residual
if haskey(element, "$unknown_field_name nodal load")
assembly.residual_vector -= vec(element["$unknown_field_name nodal load"](time))
end
return assembly.residual_vector
end
jacobian, allresults = ForwardDiff.jacobian(residual_vector, vec(field),
AllResults, cache=autodiffcache)
assembly.stiffness_matrix += jacobian
assembly.force_vector -= ForwardDiff.value(allresults) # <-- minus explained in tutorial
end
""" Wrapper for virtual work for ForwardDiff. """
function calc_R(data::Vector)
R = zeros(length(data))
df = similar(field, data)
# integrate residual vector
for ip in get_integration_points(equation)
s = ip.weight*detJ(ip)
dr = get_residual_vector(equation, ip, time; variation=df)
R += s*dr
end
# external loads -- if any nodal loads is defined, decrease from residual
if haskey(element, "$unknown_field_name nodal load")
R -= vec(element["$unknown_field_name nodal load"](time))
end
return R
end
jacobian, allresults = ForwardDiff.jacobian(calc_R, vec(field), AllResults, cache=autodiffcache)
add!(assembly.stiffness_matrix, gdofs, gdofs, jacobian)
add!(assembly.force_vector, gdofs, -ForwardDiff.value(allresults))
end
end
+30 -27
View File
@@ -6,6 +6,10 @@
abstract HeatProblem <: Problem
abstract HeatEquation <: Equation
function get_unknown_field_name(equation::HeatEquation)
return "temperature"
end
### Formulation ###
""" Heat equations.
@@ -32,55 +36,36 @@ References
https://en.wikipedia.org/wiki/Heat_equation
"""
function calculate_local_assembly!(assembly::LocalAssembly, equation::HeatEquation,
unknown_field_name::ASCIIString, time::Number=Inf,
problem=nothing)
initialize_local_assembly!(assembly, equation)
function assemble!(assembly::Assembly, equation::HeatEquation, time::Number=0.0, problem=nothing)
element = get_element(equation)
gdofs = get_gdofs(equation)
basis = get_basis(element)
dbasis = grad(basis)
detJ = det(basis)
for ip in get_integration_points(equation)
w = ip.weight * detJ(ip)
w = ip.weight*detJ(ip)
N = basis(ip, time)
if haskey(element, "density")
rho = basis("density", ip, time)
assembly.mass_matrix += w * rho*N'*N
add!(assembly.mass_matrix, gdofs, gdofs, w*rho*N'*N)
end
if haskey(element, "temperature thermal conductivity")
dN = dbasis(ip, time)
k = basis("temperature thermal conductivity", ip, time)
assembly.stiffness_matrix += w * k*dN'*dN
add!(assembly.stiffness_matrix, gdofs, gdofs, w*k*dN'*dN)
end
if haskey(element, "temperature load")
f = basis("temperature load", ip, time)
assembly.force_vector += w * N'*f
add!(assembly.force_vector, gdofs, w*N'*f)
end
if haskey(element, "temperature flux")
g = basis("temperature flux", ip, time)
assembly.force_vector += w * N'*g
add!(assembly.force_vector, gdofs, w*N'*g)
end
end
end
### Problems ###
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()
element_mapping = Dict(
Quad4 => DC2D4,
Seg2 => DC2D2)
return PlaneHeatProblem("temperature", 1, [], element_mapping)
end
### Equations ###
@@ -101,7 +86,7 @@ Base.size(equation::DC2D4) = (1, 4)
""" Diffusive heat transfer for 2-node linear segment. """
type DC2D2 <: HeatEquation
element :: Seg2
integration_points :: Array{IntegrationPoint, 1}
integration_points :: Vector{IntegrationPoint}
end
function DC2D2(element::Seg2)
integration_points = get_default_integration_points(element)
@@ -112,3 +97,21 @@ function DC2D2(element::Seg2)
end
Base.size(equation::DC2D2) = (1, 2)
### Problems ###
type PlaneHeatProblem <: HeatProblem
unknown_field_name :: ASCIIString
unknown_field_dimension :: Int
equations :: Vector{Equation}
#element_mapping :: Dict{Element, Equation}
# FIXME: Why is not working ^
element_mapping :: Dict{Any, Any}
end
""" Default constructor for problem takes no arguments. """
function PlaneHeatProblem()
element_mapping = Dict(
Quad4 => DC2D4,
Seg2 => DC2D2)
return PlaneHeatProblem("temperature", 1, [], element_mapping)
end
+23 -18
View File
@@ -9,18 +9,19 @@ abstract Solver
Solve field equations for single element with some dofs fixed. This can be used
to test nonlinear element formulations.
"""
function solve!(equation::Equation, unknown_field_name::ASCIIString,
free_dofs::Array{Int, 1}, time::Number=0.0;
function solve!(equation::Equation, free_dofs::Vector{Int}, time::Number=0.0;
max_iterations::Int=10, tolerance::Float64=1.0e-12, dump_matrices::Bool=false)
unknown_field_name = get_unknown_field_name(equation)
element = get_element(equation)
x0 = element[unknown_field_name](0.0)
x = zeros(prod(size(equation)))
dx = fill!(similar(x), 0.0)
la = initialize_local_assembly()
ass = Assembly()
for i=1:max_iterations
calculate_local_assembly!(la, equation, unknown_field_name)
A = la.stiffness_matrix[free_dofs, free_dofs]
b = la.force_vector[free_dofs]
empty!(ass)
assemble!(ass, equation)
A = full(ass.stiffness_matrix)[free_dofs, free_dofs]
b = full(ass.force_vector)[free_dofs]
if dump_matrices
dump(full(A))
dump(full(b)')
@@ -39,30 +40,34 @@ to test nonlinear element formulations. Dirichlet boundary is assumed to be homo
and degrees of freedom are eliminated. So if boundary condition is known in nodal
points and everything is zero this should be quite good.
"""
function solve!(problem::Problem, free_dofs::Array{Int, 1}, time::Number=1.0;
max_iterations::Int=10, tolerance::Float64=1.0e-12, dump_matrices::Bool=false)
function solve!(problem::Problem, free_dofs::Vector{Int}, time::Number=1.0; max_iterations::Int=10, tolerance::Float64=1.0e-12, dump_matrices::Bool=false)
info("start solver")
ga = initialize_global_assembly(problem)
x = zeros(ga.ndofs)
dx = fill!(similar(x), 0.0)
assembly = Assembly()
# x = zeros(ga.ndofs)
# dx = fill!(similar(x), 0.0)
# FIXME: better.
x = nothing
dx = nothing
field_name = get_unknown_field_name(problem)
dim = get_unknown_field_dimension(problem)
for i=1:max_iterations
info("calculate global assembly")
calculate_global_assembly!(ga, problem)
info("done")
A = ga.stiffness_matrix[free_dofs, free_dofs]
b = ga.force_vector[free_dofs]
assemble!(assembly, problem, time)
A = sparse(assembly.stiffness_matrix)
b = sparse(assembly.force_vector)
if dump_matrices
dump(full(A))
dump(full(b)')
end
dx[free_dofs] = lufact(A) \ full(b)
if isa(dx, Void)
x = zeros(length(b))
dx = zeros(length(b))
end
dx[free_dofs] = lufact(A[free_dofs,free_dofs]) \ full(b)[free_dofs]
info("Difference in solution norm: $(norm(dx))")
x += dx
for equation in get_equations(problem)
element = get_element(equation)
gdofs = get_gdofs(problem, equation)
gdofs = get_gdofs(equation)
data = reshape(full(x[gdofs]), size(equation))
push!(element[field_name], data)
end
+11
View File
@@ -14,6 +14,10 @@ function SparseMatrixIJV()
SparseMatrixIJV([], [], [])
end
function Base.sparse(A::SparseMatrixIJV)
return sparse(A.I, A.J, A.V)
end
function Base.push!(A::SparseMatrixIJV, I::Int, J::Int, V::Float64)
push!(A.I, I)
push!(A.J, J)
@@ -63,3 +67,10 @@ function add!(A::SparseMatrixIJV, dofs1::Vector{Int}, dofs2::Vector{Int}, data::
append!(A.V, vec(data))
end
""" Sparse vector version. """
function add!(A::SparseMatrixIJV, dofs::Vector{Int}, data::Array{Float64})
append!(A.I, dofs)
append!(A.J, ones(Int, length(dofs)))
append!(A.V, vec(data))
end
+18 -13
View File
@@ -1,37 +1,42 @@
# This file is a part of JuliaFEM.
# License is MIT: see https://github.com/JuliaFEM/JuliaFEM.jl/blob/master/LICENSE.md
module GlobalAssemblyTests
module AssemblyTests
using JuliaFEM.Test
using JuliaFEM: Quad4, Seg2, FieldSet, Field, PlaneHeatProblem
using JuliaFEM: initialize_global_assembly, calculate_global_assembly!
using JuliaFEM: Assembly, assemble!
"""assemble a simple two element problem and solve"""
function test_asssembly()
function test_assembly()
info("create elements")
el1 = Quad4([1, 2, 3, 4])
el1["geometry"] = Vector[[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 1.0]]
el1["temperature thermal conductivity"] = 6.0
el1["temperature load"] = [12.0, 12.0, 12.0, 12.0]
el1["density"] = 10
el1["temperature load"] = 12.0
el1["density"] = 36.0
el2 = Seg2([1, 2])
el2["geometry"] = Vector[[0.0, 0.0], [1.0, 0.0]]
# Boundary load, linear ramp 0 -> 600 at time 0 -> 1
el2["temperature flux"] = FieldSet(Field[Field(0.0, 0.0), Field(1.0, 600.0)])
el2["temperature flux"] = ((0.0 => 0.0), (1.0 => 600.0))
info("element created")
problem = PlaneHeatProblem()
info("problem created. pushing elements")
push!(problem, el1)
push!(problem, el2)
global_assembly = initialize_global_assembly(problem)
calculate_global_assembly!(global_assembly, problem)
info("creating assembly from equations")
assembly = Assembly()
assemble!(assembly, problem, 1.0)
info("solving")
free_dofs = [1, 2]
A = lufact(global_assembly.stiffness_matrix[free_dofs, free_dofs])
b = full(global_assembly.force_vector)[free_dofs]
A = full(assembly.stiffness_matrix)[free_dofs, free_dofs]
b = full(assembly.force_vector)[free_dofs]
u = A \ b
@test isapprox(u, roughly([101.0, 101.0]))
info("solution u=$u")
@test isapprox(u, [101.0, 101.0])
end
end
+23 -14
View File
@@ -6,13 +6,8 @@
module HeatTests # always wrap tests to module ending with "Tests"
using JuliaFEM.Test # always use JuliaFEM.Test, not Base.Test
using JuliaFEM: Seg2, Quad4, DC2D4, DC2D2, Assembly, assemble!
using JuliaFEM: Seg2, Quad4, Field, FieldSet, DC2D4,
initialize_local_assembly, calculate_local_assembly!,
DC2D2
"tests on [0x1]x[0x1] domain"
function test_one_element() # always start test function with name test_
# volume element
@@ -31,20 +26,34 @@ function test_one_element() # always start test function with name test_
# Set constant source f=12 with k=6. Accurate solution is
# T=1 on free boundary, u(x,y) = -1/6*(1/2*f*x^2 - f*x)
equation = DC2D4(element)
la = initialize_local_assembly()
calculate_local_assembly!(la, equation, "temperature")
#la = initialize_local_assembly()
#calculate_local_assembly!(la, equation, "temperature")
assembly = Assembly()
assemble!(assembly, equation)
fdofs = [1, 2]
A = la.stiffness_matrix
b = la.force_vector
A = full(assembly.stiffness_matrix)
b = full(assembly.force_vector)
@test isapprox(A[fdofs, fdofs] \ b[fdofs], [1.0, 1.0])
# Set constant flux g=6 on boundary. Accurate solution is
# u(x,y) = x which equals T=1 on boundary.
boundary_equation = DC2D2(boundary_element);
boundary_equation = DC2D2(boundary_element)
empty!(assembly)
calculate_local_assembly!(la, boundary_equation, "temperature")
b = la.force_vector
@test isapprox(A[fdofs, fdofs] \ b[fdofs], [1.0, 1.0]) # always use @test to test things.
time = 1.0
assemble!(assembly, equation, time)
info("after first element: $(length(assembly.force_vector.V))")
info(full(assembly.force_vector)')
assemble!(assembly, boundary_equation, time)
info("after second element: $(length(assembly.force_vector.V))")
info(full(assembly.force_vector)')
#calculate_local_assembly!(la, boundary_equation, "temperature")
#b = la.force_vector
A = full(assembly.stiffness_matrix)
b = full(assembly.force_vector)
T = A[fdofs, fdofs] \ b[fdofs]
info("T = $T")
@test isapprox(T, [2.0, 2.0]) # always use @test to test things.
end
+67 -84
View File
@@ -6,25 +6,27 @@ module ElementTests
using JuliaFEM.Test
using JuliaFEM
using JuliaFEM: Equation, Quad4, IntegrationPoint, initialize_local_assembly,
get_element, get_basis, grad, calculate_local_assembly!,
PlaneHeatProblem, Seg2, HeatEquation, Problem, solve!
using JuliaFEM: Equation, Quad4, IntegrationPoint, Assembly, assemble!,
get_element, get_basis, grad, get_unknown_field_name,
PlaneHeatProblem, Seg2, Problem, solve!,
get_default_integration_points, Equation
abstract MyEquation <: Equation
""" Diffusive heat transfer for 4-node bilinear element, with a nonlinear source term. """
type DC2D4NL <: Equation
element :: Quad4
integration_points :: Array{IntegrationPoint, 1}
function JuliaFEM.get_unknown_field_name(equation::MyEquation)
return "temperature"
end
function DC2D4NL(element::Quad4, initial_temperature=zeros(4))
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)]
""" Diffusive heat transfer for 4-node bilinear element, with a nonlinear source term. """
type DC2D4NL <: MyEquation
element :: Quad4
integration_points :: Vector{IntegrationPoint}
end
function DC2D4NL(element::Quad4)
integration_points = get_default_integration_points(element)
if !haskey(element, "temperature")
element["temperature"] = initial_temperature
element["temperature"] = zeros(4)
end
DC2D4NL(element, integration_points)
end
@@ -34,17 +36,15 @@ function Base.size(equation::DC2D4NL)
end
""" Nonlinear flux term. """
type DC2D2NL <: Equation
type DC2D2NL <: MyEquation
element :: Seg2
integration_points :: Array{IntegrationPoint, 1}
integration_points :: Vector{IntegrationPoint}
end
function DC2D2NL(element::Seg2, initial_temperature=zeros(2))
#integration_points = [
# IntegrationPoint([0.0], 2.0)]
function DC2D2NL(element::Seg2)
integration_points = JuliaFEM.line5()
if !haskey(element, "temperature")
element["temperature"] = initial_temperature
element["temperature"] = zeros(2)
end
DC2D2NL(element, integration_points)
end
@@ -63,34 +63,23 @@ function JuliaFEM.get_potential_energy(equation::DC2D4NL, ip, time; variation=no
c = basis("temperature nonlinearity coefficient", ip, time)
gradT = grad(basis)("temperature", ip, time, variation)
Wint = (k + c*T) * 1/2*vecdot(gradT, gradT)
#Wint = k*1/2*vecdot(gradT, gradT)
Wext = f*T
#Wext = 0.0
return Wint - Wext
end
function JuliaFEM.has_potential_energy(eq::DC2D4NL)
return true
end
function JuliaFEM.get_potential_energy(equation::DC2D2NL, ip, time; variation=nothing)
element = get_element(equation)
basis = get_basis(element)
T = basis("temperature", ip, time, variation)[1]
Wint = 0.0
sig = 5.7e-8
eps = basis("emissivity", ip, time)[1]
T_ext = basis("temperature external", ip, time)[1]
q0 = eps*sig*((T_ext+273.15)^4 - (T+273.15)^4)
coeff = basis("temperature coefficient", ip, time)[1]
q0 = coeff*(T_ext^4 - T^4)
Wint = 0.0
Wext = q0*T
W = Wint - Wext
return W
end
function JuliaFEM.has_potential_energy(eq::DC2D2NL)
return true
end
function test_potential_energy_method()
# create model -- start
@@ -99,36 +88,37 @@ function test_potential_energy_method()
element["temperature thermal conductivity"] = 6.0
element["temperature load"] = [0.0, 0.0, 0.0, 0.0]
element["temperature nodal load"] = [3.0, 3.0, 0.0, 0.0]
element["temperature nonlinearity coefficient"] = [6.0, 6.0, 6.0, 6.0]
element["temperature nonlinearity coefficient"] = 6.0
equation = DC2D4NL(element)
# create model -- end
la = initialize_local_assembly() # create workspace for local matrices
ass = Assembly()
info("unknown field name: $(get_unknown_field_name(equation))")
T = zeros(4) # create workspace for solution vector
dT = zeros(4) #
fd = [1, 2] # free dofs
tic()
# start loops, in principle solve ∂r(u)/∂uΔu = -r(u) and update.
for i=1:10
calculate_local_assembly!(la, equation, "temperature") # calculate local matrices
dT[fd] = la.stiffness_matrix[fd,fd] \ la.force_vector[fd]
empty!(ass)
assemble!(ass, equation) # calculate local matrices
dT[fd] = full(ass.stiffness_matrix)[fd,fd] \ full(ass.force_vector)[fd]
T += dT
push!(element["temperature"], T) # add new increment to model
info("T = $T")
@printf("increment %2d, |du| = %8.5f\n", i, norm(dT))
err = last(element["temperature"])[1] - 2/3
isapprox(err, 0.0) && break
end
toc()
err = last(element["temperature"])[1] - 2/3
info("error: $err")
@test isapprox(err, 0.0)
end
type TestProblem <: Problem
unknown_field_name :: ASCIIString
unknown_field_dimension :: Int
equations :: Array{Equation, 1}
equations :: Vector{Equation}
element_mapping :: Dict{DataType, DataType}
end
@@ -143,55 +133,48 @@ function test_potential_energy_method_2()
# create model -- start
N = Vector[[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 1.0]]
element = Quad4([1, 2, 3, 4])
element["geometry"] = Vector[N[1], N[2], N[3], N[4]]
element["temperature thermal conductivity"] = 6.0
element["temperature load"] = [0.0, 0.0, 0.0, 0.0]
element["temperature nonlinearity coefficient"] = [0.0, 0.0, 0.0, 0.0]
#equation1 = DC2D4NL(element, initial_temperature=ones(4))
equation1 = DC2D4NL(element)
element1 = Quad4([1, 2, 3, 4])
element1["geometry"] = Vector[N[1], N[2], N[3], N[4]]
element1["temperature thermal conductivity"] = 6.0
element1["temperature load"] = [0.0, 0.0, 0.0, 0.0]
element1["temperature nonlinearity coefficient"] = [0.0, 0.0, 0.0, 0.0]
element1["temperature"] = ones(4)
boundary_element = Seg2([1, 2])
boundary_element["geometry"] = Vector[N[1], N[2]]
boundary_element["emissivity"] = 0.5
boundary_element["temperature external"] = 10.0
#equation2 = DC2D2NL(boundary_element, initial_temperature=ones(4))
equation2 = DC2D2NL(boundary_element)
element2 = Seg2([1, 2])
element2["geometry"] = Vector[N[1], N[2]]
element2["temperature coefficient"] = 3.0e-8 # ~ 5.7e-8 * 0.5
element2["temperature external"] = 100.0
element2["temperature"] = ones(2)
# create model -- end
equation1 = DC2D4NL(element1)
equation2 = DC2D2NL(element2)
ass = Assembly()
info("unknown field name: $(get_unknown_field_name(equation1))")
element["temperature"] = ones(4)
boundary_element["temperature"] = ones(2)
equations = [equation1, equation2]
la = initialize_local_assembly() # create workspace for local matrices
T = zeros(4) # create workspace for solution vector
dT = zeros(4) #
fd = [1, 2] # free dofs
info("equation 1")
calculate_local_assembly!(la, equation1, "temperature")
info("stiffness matrix: $(la.stiffness_matrix)")
# info("force vector: $(la.force_vector)")
info("equation 2")
calculate_local_assembly!(la, equation2, "temperature")
# info("stiffness matrix: $(la.stiffness_matrix)")
info("force vector: $(la.force_vector)")
# start loops, in principle solve ∂r(u)/∂uΔu = -r(u) and update.
for i=1:10
empty!(ass)
assemble!(ass, equation1)
assemble!(ass, equation2)
dT[fd] = full(ass.stiffness_matrix)[fd,fd] \ full(ass.force_vector)[fd]
T += dT
push!(element1["temperature"], T)
push!(element2["temperature"], T[fd])
@printf("increment %2d, |du| = %8.5f\n", i, norm(dT))
err = last(element1["temperature"])[1] - 0.5
isapprox(err, 0.0) && break
end
info("Creating problem")
#problem = PlaneHeatProblem("temperature", 1, equations, Dict())
problem = TestProblem(equations)
free_dofs = [1, 2]
tic()
solve!(problem, free_dofs; max_iterations=10)
toc()
temp = get_basis(boundary_element)("temperature", [0.0])[1]
info("temperature = $temp")
#err = last(element["temperature"])[1] - 2/3
#info("error: $err")
# 0.3888756709834147 tulee jostakin syysta...
# tai -0.39411350336960116
info(boundary_element["temperature"])
@test isapprox(temp, 2.93509690572300E+00) # tested using Code Aster
err = last(element1["temperature"])[1] - 0.5
info("error: $err")
@test isapprox(err, 0.0)
# @test isapprox(temp, 2.93509690572300E+00) # tested using Code Aster
end
end
+13 -13
View File
@@ -5,30 +5,32 @@ module TestAutoDiffWeakForm
using JuliaFEM.Test
using JuliaFEM
using JuliaFEM: Quad4, Equation, IntegrationPoint,
using JuliaFEM: Quad4, Equation, IntegrationPoint, assemble!,
Assembly,
solve!, get_field, get_element, get_basis,
grad
grad, get_default_integration_points
""" Plane stress formulation for 4-node bilinear element. """
type CPS4 <: Equation
element :: Quad4
integration_points :: Array{IntegrationPoint, 1}
integration_points :: Vector{IntegrationPoint}
end
function JuliaFEM.get_unknown_field_name(equation::CPS4)
return "displacement"
end
function CPS4(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)
if !haskey(element, "displacement")
# initial field must be defined if using autodiff
element["displacement"] = zeros(2, 4)
end
CPS4(element, integration_points)
end
JuliaFEM.size(eq::CPS4) = (2, 4)
function Base.size(eq::CPS4)
return (2, 4)
end
function JuliaFEM.get_residual_vector(equation::CPS4, ip, time; variation=nothing)
element = get_element(equation)
@@ -58,8 +60,6 @@ function JuliaFEM.get_residual_vector(equation::CPS4, ip, time; variation=nothin
return vec(r)
end
JuliaFEM.has_residual_vector(equation::CPS4) = true
function test_residual_form()
# create model -- start
element = Quad4([1, 2, 3, 4])
@@ -71,7 +71,7 @@ function test_residual_form()
# create model -- end
free_dofs = [3, 4, 5, 6]
solve!(equation, "displacement", free_dofs) # launch a newton solver for single element
solve!(equation, free_dofs) # launch a newton solver for single element
disp = get_basis(element)("displacement", [1.0, 1.0])[2]
println("displacement at tip: $disp")
# verified using Code Aster.