Files
JuliaFEM.jl/src/solvers.jl
T

160 lines
5.0 KiB
Julia
Raw Normal View History

2015-10-09 23:45:28 +03:00
# 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
2015-10-28 04:29:14 +02:00
"""
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=0.0;
2015-10-28 04:29:14 +02:00
max_iterations::Int=10, tolerance::Float64=1.0e-12, dump_matrices::Bool=false)
unknown_field_name = get_unknown_field_name(equation)
2015-10-28 04:29:14 +02:00
element = get_element(equation)
2015-11-11 00:52:16 +02:00
x0 = element[unknown_field_name](0.0)
2015-10-28 04:29:14 +02:00
x = zeros(prod(size(equation)))
dx = fill!(similar(x), 0.0)
ass = Assembly()
2015-10-28 04:29:14 +02:00
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]
2015-10-28 04:29:14 +02:00
if dump_matrices
dump(full(A))
dump(full(b)')
end
dx[free_dofs] = A \ b
x += dx
2015-11-11 00:52:16 +02:00
push!(element[unknown_field_name], reshape(x, size(equation)))
norm(dx) < tolerance && return
2015-10-28 04:29:14 +02:00
end
2015-11-11 00:52:16 +02:00
error("Did not converge in $max_iterations iterations")
2015-10-28 04:29:14 +02:00
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::Number=1.0; max_iterations::Int=10, tolerance::Float64=1.0e-12, dump_matrices::Bool=false)
2015-11-11 00:52:16 +02:00
info("start solver")
assembly = Assembly()
# x = zeros(ga.ndofs)
# dx = fill!(similar(x), 0.0)
# FIXME: better.
x = nothing
dx = nothing
2015-10-28 04:29:14 +02:00
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)
2015-10-28 04:29:14 +02:00
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]
2015-11-11 00:52:16 +02:00
info("Difference in solution norm: $(norm(dx))")
2015-10-28 04:29:14 +02:00
x += dx
for equation in get_equations(problem)
element = get_element(equation)
gdofs = get_gdofs(equation)
2015-11-11 00:52:16 +02:00
data = reshape(full(x[gdofs]), size(equation))
push!(element[field_name], data)
2015-10-28 04:29:14 +02:00
end
2015-11-11 00:52:16 +02:00
norm(dx) < tolerance && return
2015-10-28 04:29:14 +02:00
end
2015-11-11 00:52:16 +02:00
error("Did not converge in $max_iterations iterations")
2015-10-28 04:29:14 +02:00
end
2015-10-20 15:54:47 +03:00
""" Add new problem to solver. """
2015-10-09 23:45:28 +03:00
function add_problem!(solver::Solver, problem::Problem)
push!(solver.problems, problem)
end
2015-10-28 04:29:14 +02:00
2015-10-20 15:54:47 +03:00
function Base.push!(solver::Solver, problem::Problem)
push!(solver.problems, problem)
end
2015-10-09 23:45:28 +03:00
2015-10-28 04:29:14 +02:00
""" Get all problems assigned to solver. """
2015-11-20 08:48:15 +02:00
function get_problems(solver::Solver)
return solver.problems
2015-10-09 23:45:28 +03:00
end
## SimpleSolver -- tiny direct demo solver
""" Simple solver for educational purposes. """
type SimpleSolver <: Solver
problems :: Vector{Problem}
2015-10-09 23:45:28 +03:00
end
2015-10-28 04:29:14 +02:00
2015-10-09 23:45:28 +03:00
""" Default initializer. """
function SimpleSolver()
SimpleSolver([])
2015-10-09 23:45:28 +03:00
end
"""
Call solver to solve a set of problems.
2015-10-28 04:29:14 +02:00
This is a simple direct solver for demonstration purposes. It handles the
2015-10-09 23:45:28 +03:00
common situation, i.e., some main field problem and it's Dirichlet boundary.
2015-10-28 04:29:14 +02:00
Au + C'λ = f
Cu = g
2015-10-09 23:45:28 +03:00
"""
2015-11-18 01:19:04 +02:00
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)
b1 = sparse(assembly1.force_vector, size(A1, 1), 1)
A2 = sparse(assembly2.stiffness_matrix)
b2 = sparse(assembly2.force_vector, size(A2, 1), 1)
2015-10-28 04:29:14 +02:00
# create a saddle point problem
2015-10-09 23:45:28 +03:00
A = [A1 A2; A2' zeros(A2)]
b = [b1; b2]
# solve problem
2015-11-18 01:19:04 +02:00
nz = unique(rowvals(A)) # take only non-zero rows
2015-10-09 23:45:28 +03:00
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
2015-11-18 01:19:04 +02:00
for equation in get_equations(problem1)
2015-10-09 23:45:28 +03:00
element = get_element(equation)
2015-11-18 01:19:04 +02:00
field_name = get_unknown_field_name(problem1)
gdofs = get_gdofs(problem1, equation)
local_sol = vec(full(x1[gdofs]))
push!(element[field_name], time => local_sol)
2015-10-09 23:45:28 +03:00
end
2015-10-28 04:29:14 +02:00
# update field for elements in problem 2 (Dirichlet boundary)
2015-11-18 01:19:04 +02:00
for equation in get_equations(problem2)
2015-10-09 23:45:28 +03:00
element = get_element(equation)
2015-11-18 01:19:04 +02:00
field_name = get_unknown_field_name(problem2)
gdofs = get_gdofs(problem2, equation)
local_sol = vec(full(x1[gdofs]))
push!(element[field_name], time => local_sol)
2015-10-09 23:45:28 +03:00
end
end