2015-11-23 03:17:15 +02:00
|
|
|
# This file is a part of JuliaFEM.
|
|
|
|
|
# License is MIT: see https://github.com/JuliaFEM/JuliaFEM.jl/blob/master/LICENSE.md
|
|
|
|
|
|
|
|
|
|
## Direct solver
|
|
|
|
|
|
2015-12-02 17:42:48 +02:00
|
|
|
using JuliaFEM
|
|
|
|
|
|
2016-02-01 09:14:43 +02:00
|
|
|
type DirectSolver
|
2015-12-04 08:09:11 +02:00
|
|
|
name :: ASCIIString
|
2015-11-27 10:10:00 +02:00
|
|
|
field_problems :: Vector{Problem}
|
2015-11-23 03:17:15 +02:00
|
|
|
boundary_problems :: Vector{BoundaryProblem}
|
2015-11-24 03:06:56 +02:00
|
|
|
parallel :: Bool
|
2016-01-02 00:16:36 +02:00
|
|
|
solve_residual :: Bool
|
2016-01-01 16:56:17 +02:00
|
|
|
nonlinear_max_iterations :: Int64
|
|
|
|
|
nonlinear_convergence_tolerance :: Float64
|
2016-01-02 00:16:36 +02:00
|
|
|
linear_system_solver_preprocessors :: Vector{Tuple{Symbol,Any,Any}}
|
|
|
|
|
linear_system_solvers :: Vector{Tuple{Symbol,Any,Any}}
|
|
|
|
|
linear_system_solver_postprocessors :: Vector{Tuple{Symbol,Any,Any}}
|
2015-12-02 17:42:48 +02:00
|
|
|
end
|
|
|
|
|
|
|
|
|
|
""" Default initializer. """
|
2015-12-04 08:09:11 +02:00
|
|
|
function DirectSolver(name="DirectSolver")
|
2015-12-02 17:42:48 +02:00
|
|
|
DirectSolver(
|
2015-12-04 08:09:11 +02:00
|
|
|
name,
|
2016-01-01 16:56:17 +02:00
|
|
|
[], # field problems
|
|
|
|
|
[], # boundary problems
|
|
|
|
|
false, # parallel run?
|
2016-01-02 00:16:36 +02:00
|
|
|
true, # solve residual or total quantity
|
2016-01-01 16:56:17 +02:00
|
|
|
10, # nonlinear problem max iterations
|
|
|
|
|
5.0e-6, # nonlinear convergence tolerance
|
2016-01-02 00:16:36 +02:00
|
|
|
[], # default solution preprocessors
|
|
|
|
|
[(:UMFPACK, (), [])], # linear system solver: CHOLMOD, UMFPACK
|
|
|
|
|
[], # default solution postprocessors
|
2015-12-02 17:42:48 +02:00
|
|
|
)
|
2015-11-23 03:17:15 +02:00
|
|
|
end
|
|
|
|
|
|
2016-01-01 20:34:10 +02:00
|
|
|
function set_name!(solver::DirectSolver, name::ASCIIString)
|
|
|
|
|
solver.name = name
|
|
|
|
|
end
|
|
|
|
|
|
2016-01-01 16:56:17 +02:00
|
|
|
function set_linear_system_solver!(solver::DirectSolver, method::Symbol)
|
2016-01-02 00:16:36 +02:00
|
|
|
solver.linear_system_solvers = [(method, (), [])]
|
2016-01-01 16:56:17 +02:00
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function set_nonlinear_max_iterations!(solver::DirectSolver, max_iterations::Int)
|
|
|
|
|
solver.nonlinear_max_iterations = max_iterations
|
|
|
|
|
end
|
|
|
|
|
|
2015-12-23 01:52:28 +02:00
|
|
|
function push!(solver::DirectSolver, problem::FieldProblem)
|
2015-11-23 03:17:15 +02:00
|
|
|
push!(solver.field_problems, problem)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function push!(solver::DirectSolver, problem::BoundaryProblem)
|
|
|
|
|
push!(solver.boundary_problems, problem)
|
|
|
|
|
end
|
|
|
|
|
|
2016-01-02 00:16:36 +02:00
|
|
|
function add_linear_system_solver_preprocessor!(solver::DirectSolver, preprocessor_name::Symbol, args...; kwargs...)
|
|
|
|
|
push!(solver.linear_system_solver_preprocessors, (preprocessor_name, args, kwargs))
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function add_linear_system_solver_postprocessor!(solver::DirectSolver, postprocessor_name::Symbol, args...; kwargs...)
|
|
|
|
|
push!(solver.linear_system_solver_postprocessors, (postprocessor_name, args, kwargs))
|
|
|
|
|
end
|
|
|
|
|
|
2015-11-28 14:06:12 +02:00
|
|
|
function tic(timing, what::ASCIIString)
|
|
|
|
|
timing[what * " start"] = time()
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function toc(timing, what::ASCIIString)
|
|
|
|
|
timing[what * " finish"] = time()
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function time_elapsed(timing, what::ASCIIString)
|
|
|
|
|
return timing[what * " finish"] - timing[what * " start"]
|
|
|
|
|
end
|
|
|
|
|
|
2015-12-03 11:29:01 +02:00
|
|
|
"""
|
2016-01-01 16:56:17 +02:00
|
|
|
Linear system solver for problem
|
2015-12-03 11:29:01 +02:00
|
|
|
|
2016-01-01 16:56:17 +02:00
|
|
|
Ku + C₁'λ = f
|
|
|
|
|
C₂u + Dλ = g
|
2015-12-03 11:29:01 +02:00
|
|
|
|
|
|
|
|
"""
|
2016-01-01 16:56:17 +02:00
|
|
|
function linear_system_solver_solve!(solver, iter, time, K, f, C1, C2, D, g, sol, la, ::Type{Val{:UMFPACK}})
|
|
|
|
|
t0 = Base.time()
|
2015-12-23 01:52:28 +02:00
|
|
|
dim = size(K, 1)
|
|
|
|
|
A = [K C1'; C2 D]
|
|
|
|
|
b = [f; g]
|
|
|
|
|
nz1 = sort(unique(rowvals(A)))
|
|
|
|
|
nz2 = sort(unique(rowvals(A')))
|
|
|
|
|
u = zeros(length(b))
|
|
|
|
|
u[nz1] = lufact(A[nz1,nz2]) \ full(b[nz1])
|
2016-01-01 16:56:17 +02:00
|
|
|
sol[:] = u[1:dim]
|
|
|
|
|
la[:] = u[dim+1:end]
|
|
|
|
|
info("UMFPACK: solved in ", Base.time()-t0, " seconds. norm = ", norm(sol))
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
""" Solution preprocessor: dump matrices to disk before solution.
|
|
|
|
|
|
|
|
|
|
Examples
|
|
|
|
|
--------
|
|
|
|
|
julia> solver = DirectSolver()
|
|
|
|
|
julia> push!(solver.linear_system_solver_preprocessors, :dump_matrices)
|
|
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
function linear_system_solver_preprocess!(solver, iter, time, K, f, C1, C2, D, g, sol, la, ::Type{Val{:dump_matrices}})
|
|
|
|
|
filename = "matrices_$(solver.name)_host_$(myid())_iteration_$(iter).jld"
|
|
|
|
|
info("dumping matrices to disk, file = $filename")
|
|
|
|
|
save(filename,
|
|
|
|
|
"stiffness matrix K", K,
|
|
|
|
|
"force vector f", f,
|
|
|
|
|
"constraint matrix C1", C1,
|
|
|
|
|
"constraint matrix C2", C2,
|
|
|
|
|
"constraint matrix D", D,
|
|
|
|
|
"constraint vector g", g)
|
2015-12-23 01:52:28 +02:00
|
|
|
end
|
2015-12-03 11:29:01 +02:00
|
|
|
|
2016-01-01 17:56:52 +02:00
|
|
|
function linear_system_solver_postprocess!
|
|
|
|
|
end
|
2016-01-01 16:56:17 +02:00
|
|
|
|
2016-02-01 09:14:43 +02:00
|
|
|
""" Initialize unknown field ready for nonlinear iterations, i.e.,
|
|
|
|
|
take last known value and set it as a initial quess for next
|
|
|
|
|
time increment.
|
|
|
|
|
"""
|
|
|
|
|
function initialize!(problem::FieldProblem, time::Real)
|
|
|
|
|
field_name = get_unknown_field_name(problem)
|
|
|
|
|
field_dim = get_unknown_field_dimension(problem)
|
|
|
|
|
for element in get_elements(problem)
|
|
|
|
|
gdofs = get_gdofs(element, problem)
|
|
|
|
|
if haskey(element, field_name)
|
|
|
|
|
if !isapprox(last(element[field_name]).time, time)
|
|
|
|
|
last_data = copy(last(element[field_name]).data)
|
|
|
|
|
push!(element[field_name], time => last_data)
|
|
|
|
|
end
|
|
|
|
|
else # if field not found at all, initialize new zero field.
|
|
|
|
|
data = Vector{Float64}[zeros(field_dim) for i in 1:length(element)]
|
|
|
|
|
element[field_name] = (time => data)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function initialize!(problem::BoundaryProblem, time::Real; initialize_primary_field=false)
|
|
|
|
|
field_name = problem.parent_field_name
|
|
|
|
|
field_dim = problem.parent_field_dim
|
|
|
|
|
|
|
|
|
|
for element in get_elements(problem)
|
|
|
|
|
gdofs = get_gdofs(element, problem)
|
|
|
|
|
data = Vector{Float64}[zeros(field_dim) for i in 1:length(element)]
|
|
|
|
|
|
|
|
|
|
# add new field "reaction force" for boundary element if not found
|
|
|
|
|
if haskey(element, "reaction force")
|
|
|
|
|
if !isapprox(last(element["reaction force"]).time, time)
|
|
|
|
|
push!(element["reaction force"], time => data)
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
element["reaction force"] = (time => data)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
if initialize_primary_field
|
|
|
|
|
# add new primary field for boundary element if not found
|
|
|
|
|
if haskey(element, field_name)
|
|
|
|
|
if !isapprox(last(element[field_name]).time, time)
|
|
|
|
|
last_data = copy(last(element[field_name]).data)
|
|
|
|
|
push!(element[field_name], time => last_data)
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
data = Vector{Float64}[zeros(field_dim) for i in 1:length(element)]
|
|
|
|
|
element[field_name] = (time => data)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function update!(problem::FieldProblem, solution::Vector, ::Type{Val{:elements}})
|
|
|
|
|
field_name = get_unknown_field_name(problem)
|
|
|
|
|
field_dim = get_unknown_field_dimension(problem)
|
|
|
|
|
for element in get_elements(problem)
|
|
|
|
|
gdofs = get_gdofs(element, problem)
|
|
|
|
|
local_sol = solution[gdofs]
|
|
|
|
|
local_sol = reshape(local_sol, field_dim, length(element))
|
|
|
|
|
local_sol = Vector{Float64}[local_sol[:,i] for i=1:length(element)]
|
|
|
|
|
last(element[field_name]).data = local_sol
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function update!(problem::BoundaryProblem, solution::Vector, ::Type{Val{:elements}})
|
|
|
|
|
field_name = problem.parent_field_name
|
|
|
|
|
field_dim = problem.parent_field_dim
|
|
|
|
|
for element in get_elements(problem)
|
|
|
|
|
gdofs = get_gdofs(element, field_dim)
|
|
|
|
|
local_sol = solution[gdofs]
|
|
|
|
|
local_sol = reshape(local_sol, field_dim, length(element))
|
|
|
|
|
local_sol = Vector{Float64}[local_sol[:,i] for i=1:length(element)]
|
|
|
|
|
last(element["reaction force"]).data = local_sol
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
2015-12-01 20:19:11 +02:00
|
|
|
""" Call solver to solve a set of problems. """
|
2015-12-23 01:52:28 +02:00
|
|
|
function call(solver::DirectSolver, time::Real=0.0)
|
2015-12-20 21:17:47 +02:00
|
|
|
info("Starting solver $(solver.name)")
|
2015-12-01 20:19:11 +02:00
|
|
|
info("# of field problems: $(length(solver.field_problems))")
|
|
|
|
|
info("# of boundary problems: $(length(solver.boundary_problems))")
|
2015-12-20 21:17:47 +02:00
|
|
|
(length(solver.field_problems) != 0) || error("no field problems defined for solver, use push!(solver, problem, ...) to define field problems.")
|
2015-12-02 17:42:48 +02:00
|
|
|
|
2015-12-01 20:19:11 +02:00
|
|
|
timing = Dict{ASCIIString, Float64}()
|
|
|
|
|
tic(timing, "solver")
|
|
|
|
|
|
|
|
|
|
# check that all problems are "same kind"
|
|
|
|
|
field_name = get_unknown_field_name(solver.field_problems[1])
|
|
|
|
|
field_dim = get_unknown_field_dimension(solver.field_problems[1])
|
|
|
|
|
for field_problem in solver.field_problems
|
|
|
|
|
get_unknown_field_name(field_problem) == field_name || error("several different fields not supported yet")
|
|
|
|
|
get_unknown_field_dimension(field_problem) == field_dim || error("several different field dimensions not supported yet")
|
|
|
|
|
end
|
|
|
|
|
|
2016-02-01 09:14:43 +02:00
|
|
|
tic(timing, "initialization")
|
2015-12-01 20:19:11 +02:00
|
|
|
for field_problem in solver.field_problems
|
2016-02-01 09:14:43 +02:00
|
|
|
initialize!(field_problem, time)
|
2015-12-01 20:19:11 +02:00
|
|
|
end
|
|
|
|
|
for boundary_problem in solver.boundary_problems
|
2016-02-01 09:14:43 +02:00
|
|
|
initialize!(boundary_problem, time)
|
2015-12-01 20:19:11 +02:00
|
|
|
end
|
|
|
|
|
toc(timing, "initialization")
|
|
|
|
|
|
2016-01-01 16:56:17 +02:00
|
|
|
dim = nothing
|
|
|
|
|
sol = nothing
|
2016-01-02 00:16:36 +02:00
|
|
|
last_sol = nothing
|
2016-01-01 16:56:17 +02:00
|
|
|
la = nothing
|
2016-01-02 00:16:36 +02:00
|
|
|
last_la = nothing
|
2015-12-01 20:19:11 +02:00
|
|
|
|
2016-01-01 16:56:17 +02:00
|
|
|
for iter=1:solver.nonlinear_max_iterations
|
|
|
|
|
info("Starting nonlinear iteration $iter")
|
2015-12-01 20:19:11 +02:00
|
|
|
tic(timing, "non-linear iteration")
|
|
|
|
|
|
2015-12-03 11:29:01 +02:00
|
|
|
tic(timing, "field assembly")
|
2015-12-01 20:19:11 +02:00
|
|
|
info("Assembling field problems...")
|
2015-12-23 01:52:28 +02:00
|
|
|
field_assembly = FieldAssembly()
|
2015-12-01 20:19:11 +02:00
|
|
|
for (i, problem) in enumerate(solver.field_problems)
|
2015-12-20 21:17:47 +02:00
|
|
|
info("Assembling body $i: $(problem.name)")
|
2015-12-03 11:29:01 +02:00
|
|
|
append!(field_assembly, assemble(problem, time))
|
2015-12-01 20:19:11 +02:00
|
|
|
end
|
2015-12-03 11:29:01 +02:00
|
|
|
K = sparse(field_assembly.stiffness_matrix)
|
|
|
|
|
dim = size(K, 1)
|
|
|
|
|
f = sparse(field_assembly.force_vector, dim, 1)
|
|
|
|
|
field_assembly = nothing
|
|
|
|
|
gc()
|
|
|
|
|
toc(timing, "field assembly")
|
2015-12-01 20:19:11 +02:00
|
|
|
|
2015-12-03 11:29:01 +02:00
|
|
|
tic(timing, "boundary assembly")
|
|
|
|
|
info("Assembling boundary problems...")
|
2015-12-23 01:52:28 +02:00
|
|
|
boundary_assembly = BoundaryAssembly()
|
2015-12-03 11:29:01 +02:00
|
|
|
for (i, problem) in enumerate(solver.boundary_problems)
|
2015-12-20 21:17:47 +02:00
|
|
|
info("Assembling boundary $i: $(problem.name)")
|
2015-12-03 11:29:01 +02:00
|
|
|
append!(boundary_assembly, assemble(problem, time))
|
2015-12-01 20:19:11 +02:00
|
|
|
end
|
2015-12-23 01:52:28 +02:00
|
|
|
|
|
|
|
|
C1 = sparse(boundary_assembly.C1, dim, dim)
|
|
|
|
|
C2 = sparse(boundary_assembly.C2, dim, dim)
|
|
|
|
|
D = sparse(boundary_assembly.D, dim, dim)
|
|
|
|
|
g = sparse(boundary_assembly.g, dim, 1)
|
2015-12-03 11:29:01 +02:00
|
|
|
boundary_assembly = nothing
|
|
|
|
|
gc()
|
|
|
|
|
toc(timing, "boundary assembly")
|
|
|
|
|
|
2016-01-01 16:56:17 +02:00
|
|
|
if iter == 1
|
|
|
|
|
# initialize vectors in first iteration
|
|
|
|
|
sol = zeros(dim)
|
|
|
|
|
la = zeros(dim)
|
2016-01-02 00:16:36 +02:00
|
|
|
last_sol = zeros(dim)
|
|
|
|
|
last_la = zeros(dim)
|
2015-12-02 17:42:48 +02:00
|
|
|
end
|
2015-12-01 20:19:11 +02:00
|
|
|
|
2016-01-01 16:56:17 +02:00
|
|
|
tic(timing, "preprocess solution")
|
|
|
|
|
# NOTE: sol and la are vectors from previous solution
|
2016-01-02 00:16:36 +02:00
|
|
|
for (preprocessor, args, kwargs) in solver.linear_system_solver_preprocessors
|
|
|
|
|
linear_system_solver_preprocess!(solver, iter, time, K, f, C1, C2, D, g, sol, la, Val{preprocessor}, args...; kwargs...)
|
2016-01-01 16:56:17 +02:00
|
|
|
end
|
|
|
|
|
toc(timing, "preprocess solution")
|
2015-12-23 01:52:28 +02:00
|
|
|
|
2015-12-03 11:29:01 +02:00
|
|
|
gc()
|
2016-01-01 16:56:17 +02:00
|
|
|
tic(timing, "solution of system")
|
|
|
|
|
info("Solving linear system Ax=b")
|
2016-01-02 00:16:36 +02:00
|
|
|
for (linear_solver, args, kwargs) in solver.linear_system_solvers
|
|
|
|
|
last_sol = copy(sol)
|
|
|
|
|
last_la = copy(la)
|
|
|
|
|
sol = fill!(sol, 0.0)
|
|
|
|
|
la = fill!(la, 0.0)
|
|
|
|
|
linear_system_solver_solve!(solver, iter, time, K, f, C1, C2, D, g, sol, la, Val{linear_solver}, args...; kwargs...)
|
2016-02-01 09:14:43 +02:00
|
|
|
# if solved only difference, add to last known solution, i.e. x(i+1) = x(i) + Δx
|
|
|
|
|
solver.solve_residual && (sol += last_sol)
|
2016-01-01 16:56:17 +02:00
|
|
|
end
|
2015-12-02 17:42:48 +02:00
|
|
|
toc(timing, "solution of system")
|
2016-01-01 16:56:17 +02:00
|
|
|
gc()
|
|
|
|
|
|
|
|
|
|
tic(timing, "postprocess solution")
|
2016-01-02 00:16:36 +02:00
|
|
|
for (postprocessor, args, kwargs) in solver.linear_system_solver_postprocessors
|
|
|
|
|
linear_system_solver_postprocess!(solver, iter, time, K, f, C1, C2, D, g, sol, la, Val{postprocessor}, args...; kwargs...)
|
2016-01-01 16:56:17 +02:00
|
|
|
end
|
|
|
|
|
toc(timing, "postprocess solution")
|
2015-12-01 20:19:11 +02:00
|
|
|
|
|
|
|
|
tic(timing, "update element data")
|
2016-02-01 09:14:43 +02:00
|
|
|
for problem in solver.field_problems
|
|
|
|
|
update!(problem, sol, Val{:elements})
|
2015-12-01 20:19:11 +02:00
|
|
|
end
|
2016-02-01 09:14:43 +02:00
|
|
|
for problem in solver.boundary_problems
|
|
|
|
|
update!(problem, la, Val{:elements})
|
2015-12-01 20:19:11 +02:00
|
|
|
end
|
|
|
|
|
toc(timing, "update element data")
|
2015-12-03 11:29:01 +02:00
|
|
|
|
2015-12-01 20:19:11 +02:00
|
|
|
toc(timing, "non-linear iteration")
|
|
|
|
|
|
2016-02-01 09:14:43 +02:00
|
|
|
if false
|
2015-12-14 17:48:35 +02:00
|
|
|
info("timing info for iteration:")
|
2016-01-01 16:56:17 +02:00
|
|
|
info("boundary assembly : ", time_elapsed(timing, "boundary assembly"))
|
|
|
|
|
info("field assembly : ", time_elapsed(timing, "field assembly"))
|
|
|
|
|
info("preprocess of solution : ", time_elapsed(timing, "preprocess solution"))
|
|
|
|
|
info("solve linearized problem : ", time_elapsed(timing, "solution of system"))
|
|
|
|
|
info("update element data : ", time_elapsed(timing, "update element data"))
|
|
|
|
|
info("non-linear iteration : ", time_elapsed(timing, "non-linear iteration"))
|
2015-12-01 20:19:11 +02:00
|
|
|
end
|
|
|
|
|
|
2016-01-02 00:16:36 +02:00
|
|
|
# check convergence
|
|
|
|
|
function is_converged(solver, sol, last_sol, la, last_la)
|
|
|
|
|
if solver.solve_residual
|
|
|
|
|
if norm(sol) < solver.nonlinear_convergence_tolerance
|
|
|
|
|
return true
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
if abs(norm(sol) - norm(last_sol)) < solver.nonlinear_convergence_tolerance
|
|
|
|
|
return true
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
return false
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
if is_converged(solver, sol, last_sol, la, last_la)
|
2015-12-01 20:19:11 +02:00
|
|
|
toc(timing, "solver")
|
2016-01-01 20:34:10 +02:00
|
|
|
info("converged in $iter iterations! solver finished in ", time_elapsed(timing, "solver"), " seconds.")
|
2015-12-01 20:19:11 +02:00
|
|
|
return (iter, true)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
2016-01-01 16:56:17 +02:00
|
|
|
info("Warning: did not coverge in $(solver.nonlinear_max_iterations) iterations!")
|
|
|
|
|
return (solver.nonlinear_max_iterations, false)
|
2015-12-01 20:19:11 +02:00
|
|
|
|
|
|
|
|
end
|