2015-10-28 04:29:14 +02:00
|
|
|
# This file is a part of JuliaFEM.
|
|
|
|
|
# License is MIT: see https://github.com/JuliaFEM/JuliaFEM.jl/blob/master/LICENSE.md
|
|
|
|
|
|
|
|
|
|
# Functions to handle global assembly of problem
|
|
|
|
|
|
2015-11-30 16:04:13 +02:00
|
|
|
type CAssembly
|
|
|
|
|
interior_dofs :: Vector{Int}
|
|
|
|
|
boundary_dofs :: Vector{Int}
|
2015-12-01 20:19:11 +02:00
|
|
|
F :: Union{Factorization, Matrix}
|
2015-11-30 16:04:13 +02:00
|
|
|
Kc :: SparseMatrixCSC
|
|
|
|
|
fc :: SparseMatrixCSC
|
2015-12-01 20:19:11 +02:00
|
|
|
Kib :: SparseMatrixCSC
|
2015-11-30 16:04:13 +02:00
|
|
|
fi :: SparseMatrixCSC
|
|
|
|
|
end
|
|
|
|
|
|
2015-12-02 17:42:48 +02:00
|
|
|
function optimize!(assembly::Assembly)
|
2016-06-27 16:11:33 +03:00
|
|
|
optimize!(assembly.K)
|
|
|
|
|
optimize!(assembly.Kg)
|
|
|
|
|
optimize!(assembly.f)
|
|
|
|
|
optimize!(assembly.fg)
|
|
|
|
|
optimize!(assembly.C1)
|
|
|
|
|
optimize!(assembly.C2)
|
|
|
|
|
optimize!(assembly.D)
|
|
|
|
|
optimize!(assembly.g)
|
|
|
|
|
optimize!(assembly.c)
|
2015-12-02 17:42:48 +02:00
|
|
|
end
|
|
|
|
|
|
2015-12-03 11:29:01 +02:00
|
|
|
function append!(assembly::Assembly, sub_assembly::Assembly)
|
2016-02-03 06:49:42 +02:00
|
|
|
append!(assembly.M, sub_assembly.M)
|
|
|
|
|
append!(assembly.K, sub_assembly.K)
|
2016-06-27 16:11:33 +03:00
|
|
|
append!(assembly.Kg, sub_assembly.Kg)
|
2016-02-03 06:49:42 +02:00
|
|
|
append!(assembly.f, sub_assembly.f)
|
2016-06-27 16:11:33 +03:00
|
|
|
append!(assembly.fg, sub_assembly.fg)
|
2015-12-23 01:52:28 +02:00
|
|
|
append!(assembly.C1, sub_assembly.C1)
|
|
|
|
|
append!(assembly.C2, sub_assembly.C2)
|
|
|
|
|
append!(assembly.D, sub_assembly.D)
|
|
|
|
|
append!(assembly.g, sub_assembly.g)
|
2016-02-09 13:43:27 +02:00
|
|
|
append!(assembly.c, sub_assembly.c)
|
2015-12-23 01:52:28 +02:00
|
|
|
end
|
|
|
|
|
|
2016-02-13 01:08:07 +02:00
|
|
|
function assemble_prehook!
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function assemble_posthook!
|
|
|
|
|
end
|
|
|
|
|
|
2016-06-27 16:11:33 +03:00
|
|
|
function assemble!(problem::Problem, time::Real)
|
|
|
|
|
if !isempty(problem.assembly)
|
|
|
|
|
warn("problem.assembly is not empty and assembling, are you sure you know what are you doing?")
|
|
|
|
|
end
|
2016-02-13 01:08:07 +02:00
|
|
|
if method_exists(assemble_prehook!, Tuple{typeof(problem), Real})
|
|
|
|
|
assemble_prehook!(problem, time)
|
|
|
|
|
end
|
2015-11-27 10:10:00 +02:00
|
|
|
for element in get_elements(problem)
|
2016-02-01 09:10:12 +02:00
|
|
|
assemble!(problem.assembly, problem, element, time)
|
2015-10-28 04:29:14 +02:00
|
|
|
end
|
2016-02-13 01:08:07 +02:00
|
|
|
if method_exists(assemble_posthook!, Tuple{typeof(problem), Real})
|
|
|
|
|
assemble_posthook!(problem, time)
|
|
|
|
|
end
|
2016-06-17 02:10:17 +03:00
|
|
|
end
|
|
|
|
|
|
2016-06-27 16:11:33 +03:00
|
|
|
function assemble!(problem::Problem, time::Real, ::Type{Val{:mass_matrix}}; density=0.0, dual_basis=false, dim=0)
|
|
|
|
|
if !isempty(problem.assembly.M)
|
|
|
|
|
warn("problem.assembly.M is not empty and assembling, are you sure you know what are you doing?")
|
|
|
|
|
end
|
|
|
|
|
if dim == 0
|
|
|
|
|
dim = get_unknown_field_dimension(problem)
|
|
|
|
|
end
|
2016-06-17 02:10:17 +03:00
|
|
|
for element in get_elements(problem)
|
2016-06-27 16:11:33 +03:00
|
|
|
if !haskey(element, "density") && density == 0.0
|
|
|
|
|
error("Failed to assemble mass matrix, density not defined!")
|
|
|
|
|
end
|
2016-06-17 02:10:17 +03:00
|
|
|
nnodes = length(element)
|
|
|
|
|
M = zeros(nnodes, nnodes)
|
|
|
|
|
for ip in get_integration_points(element, 1)
|
|
|
|
|
detJ = element(ip, time, Val{:detJ})
|
|
|
|
|
N = element(ip, time)
|
2016-06-27 16:11:33 +03:00
|
|
|
rho = haskey(element, "density") ? element("density", ip, time) : density
|
2016-06-17 02:10:17 +03:00
|
|
|
M += ip.weight*rho*N'*N*detJ
|
|
|
|
|
end
|
|
|
|
|
gdofs = get_gdofs(problem, element)
|
|
|
|
|
for j=1:dim
|
|
|
|
|
ldofs = gdofs[j:dim:end]
|
|
|
|
|
add!(problem.assembly.M, ldofs, ldofs, M)
|
|
|
|
|
end
|
|
|
|
|
end
|
2015-12-02 17:42:48 +02:00
|
|
|
end
|
|
|
|
|
|
|
|
|
|
""" Calculate reduced stiffness matrix.
|
|
|
|
|
mindofs: if dofs < mindofs, do not reduce
|
|
|
|
|
"""
|
|
|
|
|
function reduce(assembly::Assembly, boundary_dofs_::Vector{Int}, mindofs=100000)
|
2015-11-30 16:04:13 +02:00
|
|
|
all_dofs = unique(assembly.stiffness_matrix.I)
|
|
|
|
|
boundary_dofs = intersect(all_dofs, boundary_dofs_)
|
|
|
|
|
interior_dofs = setdiff(all_dofs, boundary_dofs_)
|
2015-12-01 20:19:11 +02:00
|
|
|
|
|
|
|
|
K = sparse(assembly.stiffness_matrix)
|
|
|
|
|
f = sparse(assembly.force_vector)
|
|
|
|
|
|
2015-11-30 16:04:13 +02:00
|
|
|
dim = size(K, 1)
|
|
|
|
|
|
2015-12-01 20:19:11 +02:00
|
|
|
# empty assembly to release memory for factorization
|
|
|
|
|
empty!(assembly.stiffness_matrix)
|
|
|
|
|
empty!(assembly.force_vector)
|
2015-12-02 08:36:17 +02:00
|
|
|
gc()
|
2015-12-01 20:19:11 +02:00
|
|
|
|
2015-12-02 17:42:48 +02:00
|
|
|
if dim < mindofs
|
2015-12-02 08:36:17 +02:00
|
|
|
# no need to do any reduction of matrix size at all, just \ it.
|
2015-12-01 20:19:11 +02:00
|
|
|
return CAssembly([], all_dofs, Matrix{Float64}(), K, f, spzeros(0, 0), spzeros(0,1))
|
2015-11-30 16:04:13 +02:00
|
|
|
end
|
2015-12-01 20:19:11 +02:00
|
|
|
|
|
|
|
|
# check that matrix is symmetric
|
|
|
|
|
s = maximum(abs(1/2*(K + K') - K))
|
|
|
|
|
@assert s < 1.0e-6
|
2015-11-30 16:04:13 +02:00
|
|
|
K = 1/2*(K + K')
|
2015-12-01 20:19:11 +02:00
|
|
|
|
|
|
|
|
Kib = K[interior_dofs, boundary_dofs]
|
|
|
|
|
Kbb = K[boundary_dofs, boundary_dofs]
|
2015-11-30 16:04:13 +02:00
|
|
|
fi = f[interior_dofs]
|
2015-12-01 20:19:11 +02:00
|
|
|
fb = f[boundary_dofs]
|
2015-11-30 16:04:13 +02:00
|
|
|
|
2015-12-01 20:19:11 +02:00
|
|
|
F = cholfact(K[interior_dofs, interior_dofs])
|
2015-12-02 08:36:17 +02:00
|
|
|
K = 0
|
|
|
|
|
gc()
|
|
|
|
|
|
2015-11-30 16:04:13 +02:00
|
|
|
|
2015-12-01 20:19:11 +02:00
|
|
|
if dim < 100000
|
|
|
|
|
# for small problems we don't need to care about memory usage
|
|
|
|
|
Kd = Kib' * (F \ Kib)
|
|
|
|
|
else
|
|
|
|
|
# for larger problems calculate schur complement in pieces
|
|
|
|
|
nb = length(boundary_dofs)
|
|
|
|
|
p = nb > 10 ? round(Int, nb/10) : nb
|
|
|
|
|
Kd = zeros(nb, nb)
|
|
|
|
|
for bi in 1:nb
|
|
|
|
|
mod(bi, p) == 0 && info("Reduction: ", round(Int, bi/nb*100), " % done")
|
|
|
|
|
C = full(F \ Kib[:, bi])
|
2015-12-02 08:36:17 +02:00
|
|
|
for bj in bi:nb
|
2015-12-01 20:19:11 +02:00
|
|
|
d = Kib[:, bj]
|
2015-12-02 08:36:17 +02:00
|
|
|
@inbounds Kd[bj,bi] = dot(C[rowvals(d)], nonzeros(d))
|
2015-12-01 20:19:11 +02:00
|
|
|
end
|
|
|
|
|
end
|
2015-12-02 08:36:17 +02:00
|
|
|
Kd += tril(Kd, -1)'
|
2015-12-01 20:19:11 +02:00
|
|
|
end
|
2015-11-30 16:04:13 +02:00
|
|
|
Kc = spzeros(dim, dim)
|
2015-12-01 20:19:11 +02:00
|
|
|
Kc[boundary_dofs, boundary_dofs] = Kbb - Kd
|
|
|
|
|
|
2015-12-02 08:36:17 +02:00
|
|
|
#= # this is slightly faster but uses more memory
|
2015-12-01 20:19:11 +02:00
|
|
|
chunks = round(Int, dim/3000)
|
|
|
|
|
info("Reduction is done in $chunks chunks.")
|
|
|
|
|
nb = length(boundary_dofs)
|
|
|
|
|
kk = round(Int, collect(linspace(0, nb, chunks+1)))
|
|
|
|
|
sl = [kk[j]+1:kk[j+1] for j=1:length(kk)-1]
|
2015-12-02 08:36:17 +02:00
|
|
|
Kd = zeros(Float64, nb, nb)
|
|
|
|
|
#Kd = SharedArray(Float64, nb, nb)
|
2015-12-01 20:19:11 +02:00
|
|
|
for (k,sli) in enumerate(sl)
|
|
|
|
|
b1 = boundary_dofs[sli]
|
|
|
|
|
Sc = F \ Kib[:,sli]
|
|
|
|
|
for slj in sl
|
|
|
|
|
b2 = boundary_dofs[slj]
|
2015-12-02 08:36:17 +02:00
|
|
|
#Kc[b2,b1] = Kbb[slj,sli] - Kib[:,slj]'*Sc
|
|
|
|
|
Kd[slj, sli] = Kib[:,slj]'*Sc
|
2015-12-01 20:19:11 +02:00
|
|
|
end
|
|
|
|
|
info("Reduction: ", round(k/chunks*100, 0), " % done")
|
|
|
|
|
end
|
2015-12-02 17:42:48 +02:00
|
|
|
|
2015-12-02 08:36:17 +02:00
|
|
|
Kc = spzeros(dim, dim)
|
|
|
|
|
Kc[boundary_dofs, boundary_dofs] = Kbb - Kd
|
|
|
|
|
=#
|
2015-11-30 16:04:13 +02:00
|
|
|
|
2015-12-01 20:19:11 +02:00
|
|
|
fc = spzeros(dim, 1)
|
|
|
|
|
fc[boundary_dofs] = fb - Kib' * (F \ fi)
|
2015-11-30 16:04:13 +02:00
|
|
|
|
2015-12-01 20:19:11 +02:00
|
|
|
return CAssembly(interior_dofs, boundary_dofs, F, Kc, fc, Kib, fi)
|
2015-11-30 16:04:13 +02:00
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function reconstruct!(ca::CAssembly, x::SparseMatrixCSC)
|
2015-12-01 20:19:11 +02:00
|
|
|
if isa(ca.F, Factorization)
|
|
|
|
|
x[ca.interior_dofs] = ca.F \ (ca.fi - ca.Kib*x[ca.boundary_dofs])
|
|
|
|
|
else # normal inverse of matrix
|
|
|
|
|
x[ca.interior_dofs] = ca.F * (ca.fi - ca.Kib*x[ca.boundary_dofs])
|
|
|
|
|
end
|
2015-11-30 16:04:13 +02:00
|
|
|
end
|
|
|
|
|
|
2015-11-24 03:06:56 +02:00
|
|
|
function Base.(:+)(ass1::Assembly, ass2::Assembly)
|
|
|
|
|
mass_matrix = ass1.mass_matrix + ass2.mass_matrix
|
|
|
|
|
stiffness_matrix = ass1.stiffness_matrix + ass2.stiffness_matrix
|
|
|
|
|
force_vector = ass1.force_vector + ass2.force_vector
|
|
|
|
|
return Assembly(mass_matrix, stiffness_matrix, force_vector)
|
2015-10-28 04:29:14 +02:00
|
|
|
end
|