This commit is contained in:
Jukka Aho
2016-02-03 20:39:03 +02:00
parent 32e7429fd3
commit aa7d775ec1
9 changed files with 239 additions and 293 deletions
+89 -58
View File
@@ -22,9 +22,14 @@ type Assembly
D :: SparseMatrixCOO
g :: SparseMatrixCOO
solution :: Vector{Float64} # full solution vector when solving problem Ax = b
previous_solution :: Vector{Float64} # previous solution vector
solution_norm_change :: Real # for convergence studies
u :: Vector{Float64} # solution vector u
u_prev :: Vector{Float64} # previous solution vector u
u_norm_change :: Real # change of norm in u
la :: Vector{Float64} # solution vector la
la_prev :: Vector{Float64} # previous solution vector u
la_norm_change :: Real # change of norm in la
prehooks :: Vector{Tuple{Symbol,Any,Any}} # assign possible prehooks before assembly
posthooks :: Vector{Tuple{Symbol,Any,Any}} # assign possible posthooks after assembly
changed :: Bool # flag to control is reassembly needed
@@ -40,6 +45,7 @@ function Assembly()
SparseMatrixCOO(),
SparseMatrixCOO(),
[], [], Inf,
[], [], Inf,
[], [], true)
end
@@ -101,22 +107,87 @@ function get_assembly(problem)
return problem.assembly
end
""" Update problem solution vector.
""" 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 update!(problem::Problem, solution::Vector{Float64})
function initialize!(problem::Problem, 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 field is found, copy last known solution to new time as initial guess
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
""" Update problem solution vector for assembly. """
function update_assembly!(problem, u, la)
assembly = get_assembly(problem)
# resize & fill with zeros solution vector if length mismatch with current solution
if length(solution) != length(assembly.solution)
resize!(assembly.solution, length(solution))
fill!(assembly.solution, 0.0)
# resize & fill with zeros vectors if length mismatch with current solution
if length(u) != length(assembly.u)
resize!(assembly.u, length(u))
fill!(assembly.u, 0.0)
end
assembly.previous_solution = copy(assembly.solution)
if length(la) != length(assembly.la)
resize!(assembly.la, length(la))
fill!(assembly.la, 0.0)
end
# copy current solutions to previous ones and add/replace new solution
assembly.u_prev = copy(assembly.u)
assembly.la_prev = copy(assembly.la)
if get_formulation_type(problem) == :incremental
assembly.solution += solution
assembly.u += u
assembly.la += la
else
assembly.solution = solution
assembly.u = u
assembly.la = la
end
# calculate change of norm
assembly.u_norm_change = norm(assembly.u - assembly.u_prev)
assembly.la_norm_change = norm(assembly.la - assembly.la_prev)
return assembly.u_norm_change, assembly.la_norm_change
end
""" Update solutions to elements.
Notes
-----
This assumes that element is properly initialized so that last known field data
is from current time. For boundary problems solution is updated from lambda vector
and for field problems from actual solution vector.
"""
function update_elements!(problem, u, la)
field_name = get_unknown_field_name(problem)
field_dim = get_unknown_field_dimension(problem)
nnodes = round(Int, length(u)/field_dim)
solution = nothing
if is_field_problem(problem)
solution = reshape(u, field_dim, nnodes)
elseif is_boundary_problem(problem)
solution = reshape(la, field_dim, nnodes)
else
error("update_elements!(): unknown problem type $(typeof(problem))")
end
for element in get_elements(problem)
connectivity = get_connectivity(element) # node ids
local_sol = Vector{Float64}[solution[:, node_id] for node_id in connectivity]
last(element[field_name]).data = local_sol
end
assembly.solution_norm_change = norm(assembly.solution - assembly.previous_solution)
end
#=
@@ -144,52 +215,12 @@ function get_unknown_field_name{P}(problem::Problem{P})
return get_unknown_field_name(P)
end
""" Return the name of the parent field of this (boundary) problem. """
function get_parent_field_name{P<:BoundaryProblem}(problem::Problem{P})
return problem.parent_field_name
end
function push!(problem::Problem, element)
push!(problem.elements, element)
end
# TODO: better place for utility functions?
""" Calculate "nodal" vector from set of elements.
For example element 1 with dofs [1, 2, 3, 4] has [1, 1, 1, 1] and
element 2 with dofs [3, 4, 5, 6] has [2, 2, 2, 2] the result will
be sparse matrix with values [1, 1, 3, 3, 2, 2].
Parameters
----------
field_name
name of field, e.g. "geometry"
field_dim
degrees of freedom / node
elements
elements used to calculate vector
time
"""
function calculate_nodal_vector(field_name::ASCIIString, field_dim::Int, elements::Vector{Element}, time::Real)
A = SparseMatrixCOO()
b = SparseMatrixCOO()
for element in elements
haskey(element, field_name) || continue
gdofs = get_gdofs(element, 1)
for ip in get_integration_points(element, Val{2})
J = get_jacobian(element, ip, time)
w = ip.weight*norm(J)
f = element(field_name, ip, time)
N = element(ip, time)
add!(A, gdofs, gdofs, w*kron(N', N))
for dim=1:field_dim
add!(b, gdofs, w*f[dim]*N, dim)
end
end
end
A = sparse(A)
b = sparse(b)
nz = sort(unique(rowvals(A)))
x = zeros(size(b)...)
x[nz, :] = A[nz,nz] \ b[nz, :]
return vec(transpose(x))
end