Files
JuliaFEM.jl/src/solvers.jl
T
2015-11-24 03:06:56 +02:00

179 lines
5.6 KiB
Julia

# This file is a part of JuliaFEM.
# License is MIT: see https://github.com/JuliaFEM/JuliaFEM.jl/blob/master/LICENSE.md
# Solver stuff
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, free_dofs::Vector{Int}, time::Number; max_iterations::Int=10, tolerance::Float64=1.0e-12, dump_matrices::Bool=false, callback=nothing)
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)
ass = Assembly()
for i=1:max_iterations
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)')
end
dx[free_dofs] = A \ b
x += dx
eqsize = size(equation)
data = eqsize[1] != 1 ? reshape(x, eqsize) : x
push!(element[unknown_field_name], time => data)
norm(dx) < tolerance && return
if !isa(callback, Void)
callback(x)
end
end
error("Did not converge in $max_iterations iterations")
end
"""
Solve field equations for a single problem with some dofs fixed. This can be used
to test nonlinear element formulations. Dirichlet boundary is assumed to be homogeneous
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::Vector{Int}, time::Float64; max_iterations::Int=10, tolerance::Float64=1.0e-12, dump_matrices::Bool=false, callback=nothing)
info("start solver")
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
assemble!(assembly, problem, time)
A = sparse(assembly.stiffness_matrix)
b = sparse(assembly.force_vector)
if dump_matrices
dump(full(A))
dump(full(b)')
end
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
if !(isa(callback, Void))
callback(x)
end
for equation in get_equations(problem)
element = get_element(equation)
gdofs = get_gdofs(equation)
data = full(x[gdofs])
eqsize = size(equation)
if eqsize[1] != 1
data = reshape(data, eqsize)
end
push!(element[field_name], time => data)
end
norm(dx) < tolerance && return
end
error("Did not converge in $max_iterations iterations")
end
function Base.push!(solver::Solver, problem::Problem)
push!(solver.problems, problem)
end
""" Get all problems assigned to solver. """
function get_problems(solver::Solver)
return solver.problems
end
## SimpleSolver -- tiny direct demo solver
""" Simple solver for educational purposes. """
type SimpleSolver <: Solver
problems :: Vector{Problem}
end
""" Default initializer. """
function SimpleSolver()
SimpleSolver([])
end
"""
Call solver to solve a set of problems.
This is a simple direct solver for demonstration purposes. It handles the
common situation, i.e., some main field problem and it's Dirichlet boundary.
Au + C'λ = f
Cu = g
"""
function call(solver::SimpleSolver, time::Number=0.0)
problem1, problem2 = get_problems(solver)
assembly1 = Assembly()
assemble!(assembly1, problem1, time)
assembly2 = Assembly()
assemble!(assembly2, problem2, time)
# info("Creating sparse matrices")
A1 = sparse(assembly1.stiffness_matrix)
dims = size(A1)
b1 = sparse(assembly1.force_vector, dims[1], 1)
A2 = sparse(assembly2.stiffness_matrix, dims[1], dims[2])
b2 = sparse(assembly2.force_vector, dims[1], 1)
# create a saddle point problem
A = [A1 A2; A2' zeros(A2)]
b = [b1; b2]
# solve problem
nz = unique(rowvals(A)) # take only non-zero rows
x = zeros(b)
x[nz] = lufact(A[nz,nz]) \ full(b[nz])
# get "problem-wise" solution vectors
x1 = x[1:length(b1)]
x2 = x[length(b1)+1:end]
# update field for elements in problem 1
for equation in get_equations(problem1)
element = get_element(equation)
field_name = get_unknown_field_name(problem1)
gdofs = get_gdofs(problem1, equation)
local_sol = vec(full(x1[gdofs]))
eqsize = size(equation)
if eqsize[1] != 1
local_sol = reshape(local_sol, eqsize)
end
#info("problem1: pushing to $field_name")
push!(element[field_name], time => local_sol)
end
# update field for elements in problem 2 (Dirichlet boundary)
for equation in get_equations(problem2)
element = get_element(equation)
field_name = "reaction force" #get_unknown_field_name(problem2)
gdofs = get_gdofs(problem2, equation)
local_sol = vec(full(x1[gdofs]))
eqsize = size(equation)
if eqsize[1] != 1
local_sol = reshape(local_sol, eqsize)
end
#info("problem2: pushing to $field_name")
#push!(element[field_name], time => local_sol)
end
return norm(x1)
end