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
|
|
|
|
|
|
|
2016-06-17 02:10:17 +03:00
|
|
|
|
abstract AbstractSolver
|
|
|
|
|
|
|
|
|
|
|
|
type Solver{S<:AbstractSolver}
|
2016-08-01 01:15:41 +03:00
|
|
|
|
name :: AbstractString # some descriptive name for problem
|
|
|
|
|
|
time :: Float64 # current time
|
2016-06-17 02:10:17 +03:00
|
|
|
|
problems :: Vector{Problem}
|
2016-08-01 01:15:41 +03:00
|
|
|
|
norms :: Vector{Tuple} # solution norms for convergence studies
|
|
|
|
|
|
ndofs :: Int # number of degrees of freedom in problem
|
2016-08-02 02:51:32 +03:00
|
|
|
|
xdmf :: Nullable{Xdmf} # input/output handle
|
2016-08-04 13:15:19 +03:00
|
|
|
|
initialized :: Bool
|
|
|
|
|
|
u :: Vector{Float64}
|
|
|
|
|
|
la :: Vector{Float64}
|
2017-03-02 08:43:58 +02:00
|
|
|
|
alpha :: Float64 # generalized alpha time integration coefficient
|
|
|
|
|
|
fields :: Dict{AbstractString, Field}
|
2016-06-17 02:10:17 +03:00
|
|
|
|
properties :: S
|
|
|
|
|
|
end
|
|
|
|
|
|
|
2016-02-01 09:13:07 +02:00
|
|
|
|
|
2016-06-27 16:11:33 +03:00
|
|
|
|
function Solver{S<:AbstractSolver}(::Type{S}, name="solver", properties...)
|
2016-06-17 02:10:17 +03:00
|
|
|
|
variant = S(properties...)
|
2017-03-02 08:43:58 +02:00
|
|
|
|
solver = Solver{S}(name, 0.0, [], [], 0, nothing, false, [], [], 0.0, Dict(), variant)
|
2016-06-17 02:10:17 +03:00
|
|
|
|
return solver
|
2016-02-01 09:13:07 +02:00
|
|
|
|
end
|
|
|
|
|
|
|
2016-07-10 23:35:17 +03:00
|
|
|
|
function Solver{S<:AbstractSolver}(::Type{S}, problems::Problem...)
|
2016-08-04 13:15:19 +03:00
|
|
|
|
solver = Solver(S, "$(S)Solver")
|
2016-07-10 23:35:17 +03:00
|
|
|
|
push!(solver.problems, problems...)
|
|
|
|
|
|
return solver
|
|
|
|
|
|
end
|
|
|
|
|
|
|
2016-06-25 04:12:53 +03:00
|
|
|
|
function get_problems(solver::Solver)
|
|
|
|
|
|
return solver.problems
|
|
|
|
|
|
end
|
|
|
|
|
|
|
2017-03-02 08:43:58 +02:00
|
|
|
|
function push!(solver::Solver, problem::Problem)
|
2016-02-01 09:13:07 +02:00
|
|
|
|
push!(solver.problems, problem)
|
|
|
|
|
|
end
|
|
|
|
|
|
|
2017-03-02 08:43:58 +02:00
|
|
|
|
function getindex(solver::Solver, problem_name::String)
|
2016-06-25 04:12:53 +03:00
|
|
|
|
for problem in get_problems(solver)
|
|
|
|
|
|
if problem.name == problem_name
|
|
|
|
|
|
return problem
|
|
|
|
|
|
end
|
|
|
|
|
|
end
|
|
|
|
|
|
throw(KeyError(problem_name))
|
|
|
|
|
|
end
|
|
|
|
|
|
|
2017-03-02 08:43:58 +02:00
|
|
|
|
function haskey(solver::Solver, field_name::String)
|
|
|
|
|
|
return haskey(solver.fields, field_name)
|
|
|
|
|
|
end
|
|
|
|
|
|
|
2016-02-01 09:13:07 +02:00
|
|
|
|
# one-liner helpers to identify problem types
|
|
|
|
|
|
|
2016-06-27 16:11:33 +03:00
|
|
|
|
is_field_problem(problem) = false
|
|
|
|
|
|
is_field_problem{P<:FieldProblem}(problem::Problem{P}) = true
|
|
|
|
|
|
is_boundary_problem(problem) = false
|
|
|
|
|
|
is_boundary_problem{P<:BoundaryProblem}(problem::Problem{P}) = true
|
|
|
|
|
|
get_field_problems(solver::Solver) = filter(is_field_problem, get_problems(solver))
|
|
|
|
|
|
get_boundary_problems(solver::Solver) = filter(is_boundary_problem, get_problems(solver))
|
2016-02-01 09:13:07 +02:00
|
|
|
|
|
|
|
|
|
|
"""Return one combined field assembly for a set of field problems.
|
|
|
|
|
|
|
|
|
|
|
|
Parameters
|
|
|
|
|
|
----------
|
|
|
|
|
|
solver :: Solver
|
|
|
|
|
|
|
|
|
|
|
|
Returns
|
|
|
|
|
|
-------
|
2016-06-27 16:11:33 +03:00
|
|
|
|
M, K, Kg, f, fg :: SparseMatrixCSC
|
2016-02-01 09:13:07 +02:00
|
|
|
|
|
|
|
|
|
|
Notes
|
|
|
|
|
|
-----
|
|
|
|
|
|
If several field problems exists, they are simply summed together, so
|
|
|
|
|
|
problems must have unique node ids.
|
|
|
|
|
|
|
|
|
|
|
|
"""
|
2017-03-21 08:36:18 +02:00
|
|
|
|
function get_field_assembly(solver::Solver)
|
2016-02-05 12:27:36 +02:00
|
|
|
|
problems = get_field_problems(solver)
|
2016-06-27 16:11:33 +03:00
|
|
|
|
|
2016-06-17 02:10:17 +03:00
|
|
|
|
M = SparseMatrixCOO()
|
2016-02-01 09:13:07 +02:00
|
|
|
|
K = SparseMatrixCOO()
|
2016-06-17 02:10:17 +03:00
|
|
|
|
Kg = SparseMatrixCOO()
|
2016-02-01 09:13:07 +02:00
|
|
|
|
f = SparseMatrixCOO()
|
2016-06-27 16:11:33 +03:00
|
|
|
|
fg = SparseMatrixCOO()
|
|
|
|
|
|
|
2016-02-01 09:13:07 +02:00
|
|
|
|
for problem in problems
|
2016-06-27 16:11:33 +03:00
|
|
|
|
append!(M, problem.assembly.M)
|
2016-06-09 01:27:56 +03:00
|
|
|
|
append!(K, problem.assembly.K)
|
2016-06-17 02:10:17 +03:00
|
|
|
|
append!(Kg, problem.assembly.Kg)
|
2016-06-09 01:27:56 +03:00
|
|
|
|
append!(f, problem.assembly.f)
|
2016-06-27 16:11:33 +03:00
|
|
|
|
append!(fg, problem.assembly.fg)
|
2016-06-17 02:10:17 +03:00
|
|
|
|
end
|
2016-06-27 16:11:33 +03:00
|
|
|
|
|
2016-06-17 02:10:17 +03:00
|
|
|
|
if solver.ndofs == 0
|
|
|
|
|
|
solver.ndofs = size(K, 1)
|
2017-03-21 08:36:18 +02:00
|
|
|
|
info("automatically determined problem dimension, ndofs = $(solver.ndofs)")
|
2016-06-17 02:10:17 +03:00
|
|
|
|
end
|
2016-06-27 16:11:33 +03:00
|
|
|
|
|
|
|
|
|
|
M = sparse(M, solver.ndofs, solver.ndofs)
|
2016-06-17 02:10:17 +03:00
|
|
|
|
K = sparse(K, solver.ndofs, solver.ndofs)
|
2016-07-03 21:16:03 +03:00
|
|
|
|
if nnz(K) == 0
|
|
|
|
|
|
warn("Field assembly seems to be empty. Check that elements are pushed to problem and formulation is correct.")
|
|
|
|
|
|
end
|
2016-06-17 02:10:17 +03:00
|
|
|
|
Kg = sparse(Kg, solver.ndofs, solver.ndofs)
|
2016-02-05 12:27:36 +02:00
|
|
|
|
f = sparse(f, solver.ndofs, 1)
|
2016-06-27 16:11:33 +03:00
|
|
|
|
fg = sparse(fg, solver.ndofs, 1)
|
2016-02-05 14:03:27 +02:00
|
|
|
|
|
2016-06-27 16:11:33 +03:00
|
|
|
|
return M, K, Kg, f, fg
|
2016-02-01 09:13:07 +02:00
|
|
|
|
end
|
|
|
|
|
|
|
2017-01-06 10:07:58 +02:00
|
|
|
|
""" Loop through boundary assemblies and check for possible overconstrain situations. """
|
|
|
|
|
|
function check_for_overconstrained_dofs(solver::Solver)
|
|
|
|
|
|
overdetermined = false
|
|
|
|
|
|
constrained_dofs = Set{Int}()
|
2017-02-25 18:40:14 +02:00
|
|
|
|
all_overconstrained_dofs = Set{Int}()
|
2017-01-06 10:07:58 +02:00
|
|
|
|
boundary_problems = get_boundary_problems(solver)
|
|
|
|
|
|
for problem in boundary_problems
|
|
|
|
|
|
new_constraints = Set(problem.assembly.C2.I)
|
2017-01-09 08:56:31 +02:00
|
|
|
|
new_constraints = setdiff(new_constraints, problem.assembly.removed_dofs)
|
2017-01-06 10:07:58 +02:00
|
|
|
|
overconstrained_dofs = intersect(constrained_dofs, new_constraints)
|
2017-02-25 18:40:14 +02:00
|
|
|
|
all_overconstrained_dofs = union(all_overconstrained_dofs, overconstrained_dofs)
|
2017-01-06 10:07:58 +02:00
|
|
|
|
if length(overconstrained_dofs) != 0
|
2017-01-09 08:56:31 +02:00
|
|
|
|
warn("problem is overconstrained, finding overconstrained dofs... ")
|
2017-01-06 10:07:58 +02:00
|
|
|
|
overdetermined = true
|
|
|
|
|
|
for dof in overconstrained_dofs
|
|
|
|
|
|
for problem_ in boundary_problems
|
|
|
|
|
|
new_constraints_ = Set(problem_.assembly.C2.I)
|
2017-01-09 08:56:31 +02:00
|
|
|
|
new_constraints_ = setdiff(new_constraints_, problem_.assembly.removed_dofs)
|
2017-01-06 10:07:58 +02:00
|
|
|
|
if dof in new_constraints_
|
|
|
|
|
|
warn("overconstrained dof $dof defined in problem $(problem_.name)")
|
|
|
|
|
|
end
|
|
|
|
|
|
end
|
2017-01-09 08:56:31 +02:00
|
|
|
|
warn("To solve overconstrained situation, remove dofs from problems so that it exists only in one.")
|
|
|
|
|
|
warn("To do this, use push! to add dofs to remove to problem.assembly.removed_dofs, e.g.")
|
|
|
|
|
|
warn("`push!(bc.assembly.removed_dofs, $dof`)")
|
2017-01-06 10:07:58 +02:00
|
|
|
|
end
|
|
|
|
|
|
end
|
|
|
|
|
|
constrained_dofs = union(constrained_dofs, new_constraints)
|
|
|
|
|
|
end
|
|
|
|
|
|
if overdetermined
|
2017-02-25 18:40:14 +02:00
|
|
|
|
warn("List of all overconstrained dofs:")
|
|
|
|
|
|
warn(sort(collect(all_overconstrained_dofs)))
|
2017-01-06 10:07:58 +02:00
|
|
|
|
error("problem is overconstrained, not continuing to solution.")
|
|
|
|
|
|
end
|
|
|
|
|
|
return true
|
2016-02-05 14:03:27 +02:00
|
|
|
|
end
|
2016-02-05 12:27:36 +02:00
|
|
|
|
|
2016-02-01 09:13:07 +02:00
|
|
|
|
""" Return one combined boundary assembly for a set of boundary problems.
|
|
|
|
|
|
|
|
|
|
|
|
Returns
|
|
|
|
|
|
-------
|
2017-01-06 10:07:58 +02:00
|
|
|
|
K, C1, C2, D, f, g :: SparseMatrixCSC
|
2016-02-01 09:13:07 +02:00
|
|
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
|
function get_boundary_assembly(solver::Solver)
|
2017-01-06 10:07:58 +02:00
|
|
|
|
|
|
|
|
|
|
check_for_overconstrained_dofs(solver)
|
|
|
|
|
|
|
2016-02-05 12:27:36 +02:00
|
|
|
|
ndofs = solver.ndofs
|
|
|
|
|
|
@assert ndofs != 0
|
2016-02-24 01:20:39 +02:00
|
|
|
|
K = spzeros(ndofs, ndofs)
|
2016-02-05 12:27:36 +02:00
|
|
|
|
C1 = spzeros(ndofs, ndofs)
|
|
|
|
|
|
C2 = spzeros(ndofs, ndofs)
|
|
|
|
|
|
D = spzeros(ndofs, ndofs)
|
2016-02-24 01:20:39 +02:00
|
|
|
|
f = spzeros(ndofs, 1)
|
2016-02-05 12:27:36 +02:00
|
|
|
|
g = spzeros(ndofs, 1)
|
|
|
|
|
|
for problem in get_boundary_problems(solver)
|
|
|
|
|
|
assembly = problem.assembly
|
2016-02-24 01:20:39 +02:00
|
|
|
|
K_ = sparse(assembly.K, ndofs, ndofs)
|
2016-02-05 12:27:36 +02:00
|
|
|
|
C1_ = sparse(assembly.C1, ndofs, ndofs)
|
|
|
|
|
|
C2_ = sparse(assembly.C2, ndofs, ndofs)
|
|
|
|
|
|
D_ = sparse(assembly.D, ndofs, ndofs)
|
2016-02-24 01:20:39 +02:00
|
|
|
|
f_ = sparse(assembly.f, ndofs, 1)
|
2016-02-05 12:27:36 +02:00
|
|
|
|
g_ = sparse(assembly.g, ndofs, 1)
|
2017-01-09 08:56:31 +02:00
|
|
|
|
for dof in assembly.removed_dofs
|
|
|
|
|
|
info("$(problem.name): removing dof $dof from assembly")
|
2017-01-30 12:28:33 +02:00
|
|
|
|
C1_[dof,:] = 0.0
|
2017-01-09 08:56:31 +02:00
|
|
|
|
C2_[dof,:] = 0.0
|
|
|
|
|
|
end
|
2017-01-30 12:28:33 +02:00
|
|
|
|
SparseArrays.dropzeros!(C1_)
|
|
|
|
|
|
SparseArrays.dropzeros!(C2_)
|
2017-01-06 10:07:58 +02:00
|
|
|
|
|
2016-02-05 12:27:36 +02:00
|
|
|
|
already_constrained = get_nonzero_rows(C2)
|
|
|
|
|
|
new_constraints = get_nonzero_rows(C2_)
|
|
|
|
|
|
overconstrained_dofs = intersect(already_constrained, new_constraints)
|
|
|
|
|
|
if length(overconstrained_dofs) != 0
|
2017-01-09 09:32:02 +02:00
|
|
|
|
warn("overconstrained dofs $overconstrained_dofs")
|
|
|
|
|
|
warn("already constrained = $already_constrained")
|
|
|
|
|
|
warn("new constraints = $new_constraints")
|
2016-02-05 12:27:36 +02:00
|
|
|
|
overconstrained_dofs = sort(overconstrained_dofs)
|
2017-01-06 10:07:58 +02:00
|
|
|
|
error("overconstrained dofs, not solving problem.")
|
2016-02-05 12:27:36 +02:00
|
|
|
|
end
|
2017-01-06 10:07:58 +02:00
|
|
|
|
|
2016-02-24 01:20:39 +02:00
|
|
|
|
K += K_
|
2016-02-05 12:27:36 +02:00
|
|
|
|
C1 += C1_
|
|
|
|
|
|
C2 += C2_
|
|
|
|
|
|
D += D_
|
2016-02-24 01:20:39 +02:00
|
|
|
|
f += f_
|
2016-02-05 12:27:36 +02:00
|
|
|
|
g += g_
|
2016-02-01 09:13:07 +02:00
|
|
|
|
end
|
2016-02-24 01:20:39 +02:00
|
|
|
|
return K, C1, C2, D, f, g
|
2016-02-01 09:13:07 +02:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
|
2016-06-22 01:39:28 +03:00
|
|
|
|
"""
|
|
|
|
|
|
Solve linear system using LDLt factorization (SuiteSparse). This version
|
|
|
|
|
|
requires that final system is symmetric and positive definite, so boundary
|
|
|
|
|
|
conditions are first eliminated before solution.
|
|
|
|
|
|
"""
|
2017-01-30 12:28:33 +02:00
|
|
|
|
function solve!(solver::Solver, K, C1, C2, D, f, g, u, la, ::Type{Val{1}})
|
2016-06-22 01:39:28 +03:00
|
|
|
|
|
2016-08-04 13:15:19 +03:00
|
|
|
|
nnz(D) == 0 || return false
|
2016-06-22 01:39:28 +03:00
|
|
|
|
|
|
|
|
|
|
A = get_nonzero_rows(K)
|
2016-11-28 12:20:54 +02:00
|
|
|
|
B = get_nonzero_rows(C2)
|
|
|
|
|
|
B2 = get_nonzero_columns(C2)
|
|
|
|
|
|
B == B2 || return false
|
2016-06-22 01:39:28 +03:00
|
|
|
|
I = setdiff(A, B)
|
2016-11-28 12:20:54 +02:00
|
|
|
|
|
2017-01-30 12:28:33 +02:00
|
|
|
|
debug("# A = $(length(A))")
|
|
|
|
|
|
debug("# B = $(length(B))")
|
|
|
|
|
|
debug("# I = $(length(I))")
|
2016-02-05 12:27:36 +02:00
|
|
|
|
|
2016-11-28 12:20:54 +02:00
|
|
|
|
if length(B) == 0
|
|
|
|
|
|
warn("No rows in C2, forget to set Dirichlet boundary conditions to model?")
|
|
|
|
|
|
else
|
2017-01-30 12:28:33 +02:00
|
|
|
|
u[B] = lufact(C2[B,B2]) \ full(g[B])
|
2016-06-22 01:39:28 +03:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
# solve interior domain using LDLt factorization
|
2016-08-04 13:15:19 +03:00
|
|
|
|
F = ldltfact(K[I,I])
|
2016-06-27 16:11:33 +03:00
|
|
|
|
u[I] = F \ (f[I] - K[I,B]*u[B])
|
2016-11-28 12:20:54 +02:00
|
|
|
|
|
|
|
|
|
|
# solve lagrange multipliers
|
|
|
|
|
|
la[B] = lufact(C1[B2,B]) \ full(f[B] - K[B,I]*u[I] - K[B,B]*u[B])
|
2016-06-22 01:39:28 +03:00
|
|
|
|
|
2016-08-04 13:15:19 +03:00
|
|
|
|
return true
|
2016-06-22 01:39:28 +03:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
|
Solve linear system using LU factorization (UMFPACK). This version solves
|
|
|
|
|
|
directly the saddle point problem without elimination of boundary conditions.
|
2017-02-01 12:36:26 +02:00
|
|
|
|
It is assumed that C1 == C2 and D = 0, so problem is symmetric and zero rows
|
|
|
|
|
|
cand be removed from total system before solution. This kind of system arises
|
|
|
|
|
|
in e.g. mesh tie problem
|
2016-06-22 01:39:28 +03:00
|
|
|
|
"""
|
2016-08-04 13:15:19 +03:00
|
|
|
|
function solve!(solver::Solver, K, C1, C2, D, f, g, u, la, ::Type{Val{2}})
|
2017-02-01 12:36:26 +02:00
|
|
|
|
|
|
|
|
|
|
C1 == C2 || return false
|
|
|
|
|
|
length(D) == 0 || return false
|
|
|
|
|
|
|
2016-06-22 01:39:28 +03:00
|
|
|
|
A = [K C1'; C2 D]
|
|
|
|
|
|
b = [f; g]
|
2017-02-01 12:36:26 +02:00
|
|
|
|
|
|
|
|
|
|
nz1 = get_nonzero_rows(A)
|
|
|
|
|
|
nz2 = get_nonzero_columns(A)
|
|
|
|
|
|
nz1 == nz2 || return false
|
|
|
|
|
|
|
|
|
|
|
|
x = zeros(2*solver.ndofs)
|
|
|
|
|
|
x[nz1] = lufact(A[nz1,nz2]) \ full(b[nz1])
|
|
|
|
|
|
|
|
|
|
|
|
u[:] = x[1:solver.ndofs]
|
|
|
|
|
|
la[:] = x[solver.ndofs+1:end]
|
|
|
|
|
|
|
|
|
|
|
|
return true
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
|
Solve linear system using LU factorization (UMFPACK). This version solves
|
|
|
|
|
|
directly the saddle point problem without elimination of boundary conditions.
|
|
|
|
|
|
If matrix has zero rows, diagonal term is added to that matrix is invertible.
|
|
|
|
|
|
"""
|
|
|
|
|
|
function solve!(solver::Solver, K, C1, C2, D, f, g, u, la, ::Type{Val{3}})
|
|
|
|
|
|
|
|
|
|
|
|
A = [K C1'; C2 D]
|
|
|
|
|
|
b = [f; g]
|
|
|
|
|
|
|
|
|
|
|
|
nz = ones(2*solver.ndofs)
|
|
|
|
|
|
nz[get_nonzero_rows(A)] = 0.0
|
|
|
|
|
|
A += spdiagm(nz)
|
|
|
|
|
|
|
2017-01-09 12:44:40 +02:00
|
|
|
|
x = lufact(A) \ full(b)
|
2017-02-01 12:36:26 +02:00
|
|
|
|
|
2016-08-04 13:15:19 +03:00
|
|
|
|
u[:] = x[1:solver.ndofs]
|
|
|
|
|
|
la[:] = x[solver.ndofs+1:end]
|
2017-02-25 18:40:14 +02:00
|
|
|
|
|
2016-08-04 13:15:19 +03:00
|
|
|
|
return true
|
2016-02-01 09:13:07 +02:00
|
|
|
|
end
|
|
|
|
|
|
|
2016-06-27 16:11:33 +03:00
|
|
|
|
""" Default linear system solver for solver. """
|
2017-01-30 12:28:33 +02:00
|
|
|
|
function solve!(solver::Solver; empty_assemblies_before_solution=true, symmetric=true)
|
2016-11-28 12:20:54 +02:00
|
|
|
|
|
2017-01-30 12:28:33 +02:00
|
|
|
|
info("Solving problems ...")
|
2016-06-27 16:11:33 +03:00
|
|
|
|
t0 = Base.time()
|
|
|
|
|
|
|
|
|
|
|
|
# assemble field & boundary problems
|
|
|
|
|
|
# TODO: return same kind of set for both assembly types
|
|
|
|
|
|
# M1, K1, Kg1, f1, fg1, C11, C21, D1, g1 = get_field_assembly(solver)
|
|
|
|
|
|
# M2, K2, Kg2, f2, fg2, C12, C22, D2, g2 = get_boundary_assembly(solver)
|
2016-06-09 01:27:56 +03:00
|
|
|
|
|
2016-06-27 16:11:33 +03:00
|
|
|
|
M, K, Kg, f, fg = get_field_assembly(solver)
|
2016-06-09 01:27:56 +03:00
|
|
|
|
Kb, C1, C2, D, fb, g = get_boundary_assembly(solver)
|
2016-06-22 01:39:28 +03:00
|
|
|
|
K = K + Kg + Kb
|
2016-06-27 16:11:33 +03:00
|
|
|
|
f = f + fg + fb
|
|
|
|
|
|
|
2016-11-28 12:20:54 +02:00
|
|
|
|
if symmetric
|
|
|
|
|
|
K = 1/2*(K + K')
|
|
|
|
|
|
M = 1/2*(M + M')
|
|
|
|
|
|
end
|
2016-08-04 13:15:19 +03:00
|
|
|
|
|
2017-01-30 12:28:33 +02:00
|
|
|
|
if empty_assemblies_before_solution
|
|
|
|
|
|
# free up some memory before solution by emptying field assemblies from problems
|
|
|
|
|
|
for problem in get_field_problems(solver)
|
2016-06-27 16:11:33 +03:00
|
|
|
|
empty!(problem.assembly)
|
|
|
|
|
|
end
|
|
|
|
|
|
gc()
|
|
|
|
|
|
end
|
2017-03-02 08:43:58 +02:00
|
|
|
|
|
|
|
|
|
|
if !haskey(solver, "fint")
|
|
|
|
|
|
solver.fields["fint"] = Field(time => f)
|
|
|
|
|
|
else
|
|
|
|
|
|
update!(solver.fields["fint"], time => f)
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
fint = solver.fields["fint"]
|
|
|
|
|
|
|
|
|
|
|
|
if length(fint) > 1
|
|
|
|
|
|
# kick in generalized alpha rule for time integration
|
|
|
|
|
|
alpha = solver.alpha
|
|
|
|
|
|
debug("Using generalized-α time integration, α=$alpha")
|
|
|
|
|
|
K = (1-alpha)*K
|
|
|
|
|
|
C1 = (1-alpha)*C1
|
|
|
|
|
|
f = (1-alpha)*f + alpha*fint[end-1].data
|
|
|
|
|
|
end
|
2016-06-22 01:39:28 +03:00
|
|
|
|
|
2016-08-04 13:15:19 +03:00
|
|
|
|
ndofs = solver.ndofs
|
|
|
|
|
|
u = zeros(ndofs)
|
|
|
|
|
|
la = zeros(ndofs)
|
2017-01-30 12:28:33 +02:00
|
|
|
|
is_solved = false
|
2016-06-27 16:11:33 +03:00
|
|
|
|
i = 0
|
2017-02-01 12:36:26 +02:00
|
|
|
|
for i in [1, 2, 3]
|
2017-01-30 12:28:33 +02:00
|
|
|
|
is_solved = solve!(solver, K, C1, C2, D, f, g, u, la, Val{i})
|
|
|
|
|
|
if is_solved
|
|
|
|
|
|
break
|
|
|
|
|
|
end
|
|
|
|
|
|
end
|
|
|
|
|
|
if !is_solved
|
|
|
|
|
|
error("Failed to solve linear system!")
|
2016-06-09 01:27:56 +03:00
|
|
|
|
end
|
2016-06-27 16:11:33 +03:00
|
|
|
|
t1 = round(Base.time()-t0, 2)
|
|
|
|
|
|
norms = (norm(u), norm(la))
|
|
|
|
|
|
push!(solver.norms, norms)
|
2016-08-04 13:15:19 +03:00
|
|
|
|
|
|
|
|
|
|
solver.u = u
|
|
|
|
|
|
solver.la = la
|
|
|
|
|
|
|
2017-01-30 12:28:33 +02:00
|
|
|
|
info("Solved problems in $t1 seconds using solver $i.")
|
|
|
|
|
|
info("Solution norms = $norms.")
|
2016-11-28 12:20:54 +02:00
|
|
|
|
|
2016-08-04 13:15:19 +03:00
|
|
|
|
return
|
2016-06-27 16:11:33 +03:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
""" Default assembler for solver. """
|
2017-03-21 08:36:18 +02:00
|
|
|
|
function assemble!(solver::Solver; timing=true, with_mass_matrix=false)
|
|
|
|
|
|
info("Assembling problems ...")
|
2016-10-13 00:59:38 +03:00
|
|
|
|
|
|
|
|
|
|
function do_assemble(problem)
|
2016-07-27 18:12:24 +03:00
|
|
|
|
t00 = Base.time()
|
2016-06-27 16:11:33 +03:00
|
|
|
|
empty!(problem.assembly)
|
|
|
|
|
|
assemble!(problem, solver.time)
|
2016-10-13 00:59:38 +03:00
|
|
|
|
if with_mass_matrix && is_field_problem(problem)
|
|
|
|
|
|
assemble!(problem, solver.time, Val{:mass_matrix})
|
|
|
|
|
|
end
|
|
|
|
|
|
t11 = Base.time()
|
|
|
|
|
|
return t11-t00
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
t0 = Base.time()
|
|
|
|
|
|
assembly_times = map(do_assemble, solver.problems)
|
|
|
|
|
|
nproblems = length(assembly_times)
|
|
|
|
|
|
|
|
|
|
|
|
ndofs = 0
|
|
|
|
|
|
for problem in solver.problems
|
2016-07-27 18:12:24 +03:00
|
|
|
|
Ks = size(problem.assembly.K, 2)
|
|
|
|
|
|
Cs = size(problem.assembly.C1, 2)
|
|
|
|
|
|
ndofs = max(ndofs, Ks, Cs)
|
2016-06-27 16:11:33 +03:00
|
|
|
|
end
|
2016-10-13 00:59:38 +03:00
|
|
|
|
|
2016-06-27 16:11:33 +03:00
|
|
|
|
solver.ndofs = ndofs
|
|
|
|
|
|
t1 = round(Base.time()-t0, 2)
|
2017-03-21 08:36:18 +02:00
|
|
|
|
info("Assembled $nproblems problems in $t1 seconds. ndofs = $ndofs.")
|
2016-07-27 18:12:24 +03:00
|
|
|
|
if timing
|
|
|
|
|
|
info("Assembly times:")
|
|
|
|
|
|
for (i, problem) in enumerate(solver.problems)
|
|
|
|
|
|
pn = problem.name
|
2016-10-13 00:59:38 +03:00
|
|
|
|
pt = round(assembly_times[i], 2)
|
2016-07-27 18:12:24 +03:00
|
|
|
|
info("$i $pn $pt")
|
|
|
|
|
|
end
|
|
|
|
|
|
end
|
2016-06-27 16:11:33 +03:00
|
|
|
|
end
|
|
|
|
|
|
|
2016-07-10 23:35:17 +03:00
|
|
|
|
function get_unknown_fields(solver::Solver)
|
|
|
|
|
|
fields = Dict()
|
|
|
|
|
|
for problem in get_field_problems(solver)
|
|
|
|
|
|
field_name = get_unknown_field_name(problem)
|
|
|
|
|
|
field_dim = get_unknown_field_dimension(problem)
|
|
|
|
|
|
fields[field_name] = field_dim
|
|
|
|
|
|
end
|
|
|
|
|
|
return fields
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
function get_unknown_field_name(solver::Solver)
|
|
|
|
|
|
fields = get_unknown_fields(solver)
|
|
|
|
|
|
return join(sort(collect(keys(fields))), ", ")
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
function get_unknown_field_dimension(solver::Solver)
|
|
|
|
|
|
fields = get_unknown_fields(solver)
|
|
|
|
|
|
return sum(values(fields))
|
|
|
|
|
|
end
|
|
|
|
|
|
|
2016-06-27 16:11:33 +03:00
|
|
|
|
""" Default initializer for solver. """
|
2017-03-21 08:36:18 +02:00
|
|
|
|
function initialize!(solver::Solver)
|
2016-08-04 13:15:19 +03:00
|
|
|
|
if solver.initialized
|
2017-03-21 08:36:18 +02:00
|
|
|
|
warn("initialize!(): solver already initialized")
|
2016-08-04 13:15:19 +03:00
|
|
|
|
return
|
|
|
|
|
|
end
|
2017-03-21 08:36:18 +02:00
|
|
|
|
info("Initializing solver ...")
|
2016-07-04 21:47:49 +03:00
|
|
|
|
problems = get_problems(solver)
|
|
|
|
|
|
length(problems) != 0 || error("Empty solver, add problems to solver using push!")
|
2016-06-27 16:11:33 +03:00
|
|
|
|
t0 = Base.time()
|
2016-07-04 21:47:49 +03:00
|
|
|
|
field_problems = get_field_problems(solver)
|
|
|
|
|
|
length(field_problems) != 0 || warn("No field problem found from solver, add some..?")
|
2016-07-10 23:35:17 +03:00
|
|
|
|
field_name = get_unknown_field_name(solver)
|
|
|
|
|
|
field_dim = get_unknown_field_dimension(solver)
|
2016-07-04 21:47:49 +03:00
|
|
|
|
info("initialize!(): looks we are solving $field_name, $field_dim dofs/node")
|
|
|
|
|
|
nodes = Set{Int64}()
|
|
|
|
|
|
for problem in problems
|
2016-06-27 16:11:33 +03:00
|
|
|
|
initialize!(problem, solver.time)
|
2016-07-04 21:47:49 +03:00
|
|
|
|
for element in get_elements(problem)
|
|
|
|
|
|
conn = get_connectivity(element)
|
|
|
|
|
|
push!(nodes, conn...)
|
|
|
|
|
|
end
|
|
|
|
|
|
end
|
|
|
|
|
|
nnodes = length(nodes)
|
|
|
|
|
|
info("Total number of nodes in problems: $nnodes")
|
2017-02-25 07:18:51 +02:00
|
|
|
|
maxdof = maximum(nodes)*field_dim
|
2016-07-04 21:47:49 +03:00
|
|
|
|
info("# of max dof (=size of solution vector) is $maxdof")
|
2016-08-04 13:15:19 +03:00
|
|
|
|
solver.u = zeros(maxdof)
|
|
|
|
|
|
solver.la = zeros(maxdof)
|
2016-07-04 21:47:49 +03:00
|
|
|
|
# TODO: this could be used to initialize elements too...
|
2016-08-04 13:15:19 +03:00
|
|
|
|
# TODO: cannot initialize to zero always, construct vector from elements.
|
2016-07-04 21:47:49 +03:00
|
|
|
|
for problem in problems
|
2016-08-04 13:15:19 +03:00
|
|
|
|
problem.assembly.u = zeros(maxdof)
|
|
|
|
|
|
problem.assembly.la = zeros(maxdof)
|
2016-07-04 21:47:49 +03:00
|
|
|
|
# initialize(problem, ....)
|
2016-06-27 16:11:33 +03:00
|
|
|
|
end
|
|
|
|
|
|
t1 = round(Base.time()-t0, 2)
|
2017-03-21 08:36:18 +02:00
|
|
|
|
info("Initialized solver in $t1 seconds.")
|
2016-08-04 13:15:19 +03:00
|
|
|
|
solver.initialized = true
|
2016-06-27 16:11:33 +03:00
|
|
|
|
end
|
|
|
|
|
|
|
2016-08-01 01:15:41 +03:00
|
|
|
|
function get_all_elements(solver::Solver)
|
|
|
|
|
|
elements = [get_elements(problem) for problem in get_problems(solver)]
|
|
|
|
|
|
return [elements...;]
|
|
|
|
|
|
end
|
|
|
|
|
|
|
2017-03-21 08:36:18 +02:00
|
|
|
|
function (solver::Solver)(field_name::String, time::Float64)
|
2016-11-13 13:24:08 +02:00
|
|
|
|
fields = []
|
|
|
|
|
|
for problem in get_problems(solver)
|
|
|
|
|
|
field = problem(field_name, time)
|
|
|
|
|
|
if length(field) == 0
|
|
|
|
|
|
warn("no field $field_name found for problem $(problem.name)")
|
|
|
|
|
|
else
|
|
|
|
|
|
push!(fields, field)
|
|
|
|
|
|
end
|
|
|
|
|
|
end
|
2016-08-01 01:15:41 +03:00
|
|
|
|
return merge(fields...)
|
|
|
|
|
|
end
|
|
|
|
|
|
|
2016-06-27 16:11:33 +03:00
|
|
|
|
""" Default update for solver. """
|
2017-03-21 08:36:18 +02:00
|
|
|
|
function update!{S}(solver::Solver{S})
|
2016-08-04 13:15:19 +03:00
|
|
|
|
u = solver.u
|
|
|
|
|
|
la = solver.la
|
|
|
|
|
|
|
2017-03-21 08:36:18 +02:00
|
|
|
|
info("Updating problems ...")
|
2016-06-27 16:11:33 +03:00
|
|
|
|
t0 = Base.time()
|
2016-08-02 02:51:32 +03:00
|
|
|
|
|
2017-03-21 08:36:18 +02:00
|
|
|
|
for problem in get_problems(solver)
|
2016-07-14 12:43:41 +03:00
|
|
|
|
assembly = get_assembly(problem)
|
|
|
|
|
|
elements = get_elements(problem)
|
|
|
|
|
|
# update solution, first for assembly (u,la) ...
|
|
|
|
|
|
update!(problem, assembly, u, la)
|
|
|
|
|
|
# .. and then from assembly (u,la) to elements
|
|
|
|
|
|
update!(problem, assembly, elements, solver.time)
|
2016-06-27 16:11:33 +03:00
|
|
|
|
end
|
2016-08-01 01:15:41 +03:00
|
|
|
|
|
2016-06-27 16:11:33 +03:00
|
|
|
|
t1 = round(Base.time()-t0, 2)
|
2017-03-21 08:36:18 +02:00
|
|
|
|
info("Updated problems in $t1 seconds.")
|
2016-06-27 16:11:33 +03:00
|
|
|
|
end
|
|
|
|
|
|
|
2017-03-21 08:36:18 +02:00
|
|
|
|
""" Default postprocess for solver. Loop all problems and run postprocess
|
|
|
|
|
|
functions to calculate secondary fields, i.e. contact pressure, stress,
|
|
|
|
|
|
heat flux, reaction force etc. quantities.
|
|
|
|
|
|
"""
|
|
|
|
|
|
function postprocess!(solver::Solver)
|
|
|
|
|
|
info("Running postprocess scripts for solver...")
|
|
|
|
|
|
for problem in get_problems(solver)
|
|
|
|
|
|
for field_name in problem.postprocess_fields
|
|
|
|
|
|
field = Val{Symbol(field_name)}
|
|
|
|
|
|
info("Running postprocess for problem $(problem.name), field $field_name")
|
|
|
|
|
|
postprocess!(problem, solver.time, field)
|
2016-08-04 13:15:19 +03:00
|
|
|
|
end
|
2016-11-28 12:20:54 +02:00
|
|
|
|
end
|
2017-03-21 08:36:18 +02:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
""" Default xdmf update for solver. Loop all problems and write them individually
|
|
|
|
|
|
to Xdmf file. By default write the main unknown field (displacement, temperature,
|
|
|
|
|
|
...) and any fields requested separately in `problem.postprocess_fields` vector
|
|
|
|
|
|
(stress, strain, ...)
|
|
|
|
|
|
"""
|
|
|
|
|
|
function update_xdmf!(solver::Solver)
|
|
|
|
|
|
if isnull(solver.xdmf)
|
|
|
|
|
|
info("update_xdmf: xdmf not attached to solver, not writing output to file.")
|
|
|
|
|
|
info("turn Xdmf writing on to solver by typing: solver.xdmf = Xdmf(\"results\")")
|
|
|
|
|
|
return
|
2016-08-04 13:15:19 +03:00
|
|
|
|
end
|
2017-03-21 08:36:18 +02:00
|
|
|
|
xdmf = get(solver.xdmf)
|
|
|
|
|
|
for problem in get_problems(solver)
|
|
|
|
|
|
fields = [get_unknown_field_name(problem); problem.postprocess_fields]
|
|
|
|
|
|
if is_boundary_problem(problem)
|
|
|
|
|
|
fields = [fields; get_parent_field_name(problem)]
|
|
|
|
|
|
end
|
|
|
|
|
|
update_xdmf!(xdmf, problem, solver.time, fields)
|
2016-08-04 13:15:19 +03:00
|
|
|
|
end
|
|
|
|
|
|
end
|
|
|
|
|
|
|
2016-06-27 16:11:33 +03:00
|
|
|
|
### Nonlinear quasistatic solver
|
|
|
|
|
|
|
|
|
|
|
|
type Nonlinear <: AbstractSolver
|
|
|
|
|
|
iteration :: Int # iteration counter
|
|
|
|
|
|
min_iterations :: Int64 # minimum number of iterations
|
|
|
|
|
|
max_iterations :: Int64 # maximum number of iterations
|
|
|
|
|
|
convergence_tolerance :: Float64
|
|
|
|
|
|
error_if_no_convergence :: Bool # throw error if no convergence
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
function Nonlinear()
|
2016-08-04 13:15:19 +03:00
|
|
|
|
solver = Nonlinear(0, 1, 10, 5.0e-5, true)
|
2016-06-27 16:11:33 +03:00
|
|
|
|
return solver
|
2016-06-09 01:27:56 +03:00
|
|
|
|
end
|
2016-02-05 12:27:36 +02:00
|
|
|
|
|
2016-02-01 09:13:07 +02:00
|
|
|
|
""" Check convergence of problems.
|
|
|
|
|
|
|
|
|
|
|
|
Notes
|
|
|
|
|
|
-----
|
|
|
|
|
|
Default convergence criteria is obtained by checking each sub-problem convergence.
|
|
|
|
|
|
"""
|
2017-01-30 12:28:33 +02:00
|
|
|
|
function has_converged(solver::Solver{Nonlinear})
|
2016-06-17 02:10:17 +03:00
|
|
|
|
properties = solver.properties
|
2016-02-01 09:13:07 +02:00
|
|
|
|
converged = true
|
2016-06-17 02:10:17 +03:00
|
|
|
|
eps = properties.convergence_tolerance
|
2017-01-30 12:28:33 +02:00
|
|
|
|
for problem in get_field_problems(solver)
|
|
|
|
|
|
has_converged = problem.assembly.u_norm_change < eps
|
|
|
|
|
|
if isapprox(norm(problem.assembly.u), 0.0)
|
|
|
|
|
|
# trivial solution
|
|
|
|
|
|
has_converged = true
|
2016-02-01 09:13:07 +02:00
|
|
|
|
end
|
2017-01-30 12:28:33 +02:00
|
|
|
|
debug("Details for problem $(problem.name)")
|
|
|
|
|
|
debug("Norm: $(norm(problem.assembly.u))")
|
|
|
|
|
|
debug("Norm change: $(problem.assembly.u_norm_change)")
|
|
|
|
|
|
debug("Has converged? $(has_converged)")
|
2016-02-01 09:13:07 +02:00
|
|
|
|
converged &= has_converged
|
|
|
|
|
|
end
|
2016-06-27 16:11:33 +03:00
|
|
|
|
return converged
|
2016-02-01 09:13:07 +02:00
|
|
|
|
end
|
|
|
|
|
|
|
2016-06-17 02:10:17 +03:00
|
|
|
|
""" Default solver for quasistatic nonlinear problems. """
|
2017-01-30 12:28:33 +02:00
|
|
|
|
function (solver::Solver{Nonlinear})()
|
2016-06-17 02:10:17 +03:00
|
|
|
|
|
|
|
|
|
|
properties = solver.properties
|
2016-02-23 11:32:53 +02:00
|
|
|
|
|
2016-02-01 09:13:07 +02:00
|
|
|
|
# 1. initialize each problem so that we can start nonlinear iterations
|
2016-06-22 01:39:28 +03:00
|
|
|
|
initialize!(solver)
|
2016-02-01 09:13:07 +02:00
|
|
|
|
|
|
|
|
|
|
# 2. start non-linear iterations
|
2016-06-17 02:10:17 +03:00
|
|
|
|
for properties.iteration=1:properties.max_iterations
|
2017-01-30 12:28:33 +02:00
|
|
|
|
info(repeat("-", 80))
|
|
|
|
|
|
info("Starting nonlinear iteration #$(properties.iteration)")
|
|
|
|
|
|
info("Increment time t=$(round(solver.time, 3))")
|
|
|
|
|
|
info(repeat("-", 80))
|
2016-02-24 01:20:39 +02:00
|
|
|
|
|
2017-03-21 08:36:18 +02:00
|
|
|
|
# 2.1 update assemblies
|
2016-06-19 20:01:37 +03:00
|
|
|
|
assemble!(solver)
|
2017-03-21 08:36:18 +02:00
|
|
|
|
|
2016-06-27 16:11:33 +03:00
|
|
|
|
# 2.2 call solver for linearized system
|
2016-08-04 13:15:19 +03:00
|
|
|
|
solve!(solver)
|
2017-03-21 08:36:18 +02:00
|
|
|
|
|
2016-02-01 09:13:07 +02:00
|
|
|
|
# 2.3 update solution back to elements
|
2016-08-04 13:15:19 +03:00
|
|
|
|
update!(solver)
|
2016-02-01 09:13:07 +02:00
|
|
|
|
|
|
|
|
|
|
# 2.4 check convergence
|
2017-03-21 08:36:18 +02:00
|
|
|
|
if properties.iteration >= properties.min_iterations && has_converged(solver)
|
2016-06-17 02:10:17 +03:00
|
|
|
|
info("Converged in $(properties.iteration) iterations.")
|
2017-03-21 08:36:18 +02:00
|
|
|
|
# 2.4.1 run any postprocessing of problems
|
|
|
|
|
|
postprocess!(solver)
|
|
|
|
|
|
# 2.4.2 update Xdmf output
|
|
|
|
|
|
update_xdmf!(solver)
|
|
|
|
|
|
return true
|
2016-02-01 09:13:07 +02:00
|
|
|
|
end
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
# 3. did not converge
|
2017-01-30 12:28:33 +02:00
|
|
|
|
if properties.error_if_no_convergence
|
|
|
|
|
|
error("nonlinear iteration did not converge in $(properties.iteration) iterations!")
|
|
|
|
|
|
end
|
2016-06-27 16:11:33 +03:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
""" Convenience function to call nonlinear solver. """
|
|
|
|
|
|
function NonlinearSolver(problems...)
|
|
|
|
|
|
solver = Solver(Nonlinear, "default nonlinear solver")
|
|
|
|
|
|
if length(problems) != 0
|
|
|
|
|
|
push!(solver, problems...)
|
|
|
|
|
|
end
|
|
|
|
|
|
return solver
|
|
|
|
|
|
end
|
2016-07-10 04:26:21 +03:00
|
|
|
|
function NonlinearSolver(name::AbstractString, problems::Problem...)
|
2016-07-03 21:16:03 +03:00
|
|
|
|
solver = NonlinearSolver(problems...)
|
|
|
|
|
|
solver.name = name
|
|
|
|
|
|
return solver
|
|
|
|
|
|
end
|
2016-06-27 16:11:33 +03:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### Linear quasistatic solver
|
|
|
|
|
|
|
|
|
|
|
|
""" Quasistatic solver for linear problems.
|
|
|
|
|
|
|
|
|
|
|
|
Notes
|
|
|
|
|
|
-----
|
|
|
|
|
|
Main differences in this solver, compared to nonlinear solver are:
|
|
|
|
|
|
1. system of problems is assumed to converge in one step
|
|
|
|
|
|
2. reassembly of problem is done only if it's manually requested using empty!(problem.assembly)
|
|
|
|
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
|
type Linear <: AbstractSolver
|
|
|
|
|
|
end
|
|
|
|
|
|
|
2017-03-21 08:36:18 +02:00
|
|
|
|
function assemble!(solver::Solver{Linear})
|
|
|
|
|
|
info("Assembling problems ...")
|
2016-06-27 16:11:33 +03:00
|
|
|
|
tic()
|
|
|
|
|
|
nproblems = 0
|
|
|
|
|
|
ndofs = 0
|
|
|
|
|
|
for problem in get_problems(solver)
|
|
|
|
|
|
if isempty(problem.assembly)
|
|
|
|
|
|
assemble!(problem, solver.time)
|
|
|
|
|
|
nproblems += 1
|
|
|
|
|
|
else
|
2017-03-21 08:36:18 +02:00
|
|
|
|
info("$(problem.name) already assembled, skipping.")
|
2016-06-27 16:11:33 +03:00
|
|
|
|
end
|
|
|
|
|
|
ndofs = max(ndofs, size(problem.assembly.K, 2))
|
|
|
|
|
|
end
|
|
|
|
|
|
solver.ndofs = ndofs
|
|
|
|
|
|
t1 = round(toq(), 2)
|
2017-03-21 08:36:18 +02:00
|
|
|
|
info("Assembled $nproblems problems in $t1 seconds. ndofs = $ndofs.")
|
2016-06-27 16:11:33 +03:00
|
|
|
|
end
|
|
|
|
|
|
|
2017-03-21 08:36:18 +02:00
|
|
|
|
function (solver::Solver{Linear})()
|
2016-06-27 16:11:33 +03:00
|
|
|
|
t0 = Base.time()
|
2017-03-21 08:36:18 +02:00
|
|
|
|
info(repeat("-", 80))
|
|
|
|
|
|
info("Starting linear solver")
|
|
|
|
|
|
info("Increment time t=$(round(solver.time, 3))")
|
|
|
|
|
|
info(repeat("-", 80))
|
2017-04-11 10:39:18 +03:00
|
|
|
|
@timeit to "initialize solver" initialize!(solver)
|
|
|
|
|
|
@timeit to "assemble problems" assemble!(solver)
|
|
|
|
|
|
@timeit to "solve linear system" solve!(solver)
|
|
|
|
|
|
@timeit to "update problems" update!(solver)
|
2016-06-27 16:11:33 +03:00
|
|
|
|
t1 = round(Base.time()-t0, 2)
|
2017-03-21 08:36:18 +02:00
|
|
|
|
info("Linear solver ready in $t1 seconds.")
|
2016-02-01 09:13:07 +02:00
|
|
|
|
end
|
2016-02-24 01:20:39 +02:00
|
|
|
|
|
2016-06-27 16:11:33 +03:00
|
|
|
|
""" Convenience function to call linear solver. """
|
2016-07-01 02:55:56 +03:00
|
|
|
|
function LinearSolver(problems::Problem...)
|
2016-06-27 16:11:33 +03:00
|
|
|
|
solver = Solver(Linear, "default linear solver")
|
|
|
|
|
|
if length(problems) != 0
|
|
|
|
|
|
push!(solver, problems...)
|
|
|
|
|
|
end
|
|
|
|
|
|
return solver
|
|
|
|
|
|
end
|
2016-07-10 04:26:21 +03:00
|
|
|
|
function LinearSolver(name::AbstractString, problems::Problem...)
|
2016-07-01 02:55:56 +03:00
|
|
|
|
solver = LinearSolver(problems...)
|
|
|
|
|
|
solver.name = name
|
|
|
|
|
|
return solver
|
|
|
|
|
|
end
|
2016-06-27 16:11:33 +03:00
|
|
|
|
|
|
|
|
|
|
### End of linear quasistatic solver
|