Separate 2d contact code to own package (#195)

Moved plane contact related stuff to own separate package
`MortarContact2D.jl`, where the development continues.

The following changes to test files are done:

1) Problem name for plane mortar coupling is `Mortar2D` (was `Mortar`
before), and later on 3d coupling will be `Mortar`. So the dimension
of coupling operator is explicitly given in a problem name.

2) Before elements to coupling was defined using
```julia
update!(problem.elements, "master elements", master_elements)
add_elements!(problem, [slave_elements; master_elements])
```
Now, explicitly give master and slave elements as
```julia
add_slave_elements!(problem, slave_elements)
add_master_elements!(problem, master_elements)
```
Keep on mind that Lagrange multipliers are in slave side.
This commit is contained in:
Jukka Aho
2018-05-07 15:14:42 +03:00
committed by GitHub
parent 5ac771480e
commit 66a24d382b
16 changed files with 223 additions and 891 deletions
+4 -3
View File
@@ -43,9 +43,12 @@ include("problems_dirichlet.jl")
export Dirichlet
export assemble!, postprocess!
### Mortar methods ###
@reexport using MortarContact2D
include("problems_mortar.jl")
include("problems_mortar_2d.jl")
include("problems_mortar_3d.jl")
include("problems_mortar_2d_autodiff.jl")
export calculate_normals, calculate_normals!, project_from_slave_to_master,
@@ -63,10 +66,8 @@ export AbstractSolver, Solver, Nonlinear, NonlinearSolver, Linear, LinearSolver,
include("solvers_modal.jl")
export Modal
include("problems_contact.jl")
include("problems_contact_2d.jl")
include("problems_contact_3d.jl")
include("problems_contact_2d_autodiff.jl")
#include("problems_contact_3d_autodiff.jl")
export Contact
# Preprocess module
-354
View File
@@ -1,354 +0,0 @@
# This file is a part of JuliaFEM.
# License is MIT: see https://github.com/JuliaFEM/JuliaFEM.jl/blob/master/LICENSE.md
function create_rotation_matrix(element::Element{Seg2}, time::Float64)
n = element("normal", time)
R = [0.0 -1.0; 1.0 0.0]
t1 = R'*n[1]
t2 = R'*n[2]
Q1 = [n[1] t1]
Q2 = [n[2] t2]
Z = zeros(2, 2)
Q = [Q1 Z; Z Q2]
return Q
end
function create_contact_segmentation(problem::Problem{Contact}, slave_element::Element{Seg2}, master_elements::Vector, time::Float64; deformed=false)
result = []
x1 = slave_element("geometry", time)
if deformed
x1 += slave_element("displacement", time)
end
for master_element in master_elements
x2 = master_element("geometry", time)
if deformed
x2 += master_element("displacement", time)
end
if norm(mean(x1) - x2[1]) / norm(x1[2] - x1[1]) > problem.properties.distval
continue
end
if norm(mean(x1) - x2[2]) / norm(x1[2] - x1[1]) > problem.properties.distval
continue
end
# 3.1 calculate segmentation
xi1a = project_from_master_to_slave(slave_element, x2[1], time)
xi1b = project_from_master_to_slave(slave_element, x2[2], time)
xi1 = clamp.([xi1a; xi1b], -1.0, 1.0)
l = 1/2*abs(xi1[2]-xi1[1])
if isapprox(l, 0.0)
continue # no contribution in this master element
end
push!(result, (master_element, xi1, l))
end
return result
end
"""
Frictionless 2d small sliding contact without forwarddiff.
true/false flags: finite_sliding, friction, use_forwarddiff
"""
function assemble!(problem::Problem{Contact}, time::Float64, ::Type{Val{1}}, ::Type{Val{false}}, ::Type{Val{false}}, ::Type{Val{false}})
props = problem.properties
field_dim = get_unknown_field_dimension(problem)
field_name = get_parent_field_name(problem)
slave_elements = get_slave_elements(problem)
# 1. calculate nodal normals and tangents for slave element nodes j ∈ S
normals, tangents = calculate_normals(slave_elements, time, Val{1};
rotate_normals=props.rotate_normals)
update!(slave_elements, "normal", time => normals)
update!(slave_elements, "tangent", time => tangents)
Rn = 0.0
# 2. loop all slave elements
for slave_element in slave_elements
nsl = length(slave_element)
X1 = slave_element("geometry", time)
u1 = slave_element("displacement", time)
la1 = slave_element("lambda", time)
n1 = slave_element("normal", time)
t1 = slave_element("tangent", time)
x1 = map(+, X1, u1)
contact_area = 0.0
contact_error = 0.0
Q2 = create_rotation_matrix(slave_element, time)
master_elements = slave_element("master elements", time)
segmentation = create_contact_segmentation(problem, slave_element, master_elements, time)
if length(segmentation) == 0 # no overlapping in master and slave surfaces with this slave element
continue
end
Ae = eye(nsl)
if props.dual_basis
De = zeros(nsl, nsl)
Me = zeros(nsl, nsl)
for (master_element, xi1, l) in segmentation
for ip in get_integration_points(slave_element, 3)
detJ = slave_element(ip, time, Val{:detJ})
w = ip.weight*detJ*l
xi = ip.coords[1]
xi_s = dot([1/2*(1-xi); 1/2*(1+xi)], xi1)
N1 = vec(get_basis(slave_element, xi_s, time))
De += w*diagm(N1)
Me += w*N1*N1'
end
Ae = De*inv(Me)
end
end
# loop all segments
for (master_element, xi1, l) in segmentation
nm = length(master_element)
X2 = master_element("geometry", time)
u2 = master_element("displacement", time)
x2 = map(+, X2, u2)
# 3.3. loop integration points of one integration segment and calculate
# local mortar matrices
De = zeros(nsl, nsl)
Me = zeros(nsl, nsl)
Ne = zeros(nsl, 2*nsl)
Te = zeros(nsl, 2*nsl)
He = zeros(nsl, 2*nsl)
ce = zeros(nsl)
ge = zeros(nsl)
for ip in get_integration_points(slave_element, 3)
detJ = slave_element(ip, time, Val{:detJ})
w = ip.weight*detJ*l
xi = ip.coords[1]
xi_s = dot([1/2*(1-xi); 1/2*(1+xi)], xi1)
N1 = vec(get_basis(slave_element, xi_s, time))
Phi = Ae*N1
# project gauss point from slave element to master element in direction n_s
X_s = interpolate(N1, X1) # coordinate in gauss point
n_s = interpolate(N1, n1) # normal direction in gauss point
t_s = interpolate(N1, t1) # tangent condition in gauss point
n_s /= norm(n_s)
t_s /= norm(t_s)
xi_m = project_from_slave_to_master(master_element, X_s, n_s, time)
N2 = vec(get_basis(master_element, xi_m, time))
X_m = interpolate(N2, X2)
u_s = interpolate(N1, u1)
u_m = interpolate(N2, u2)
x_s = map(+, X_s, u_s)
x_m = map(+, X_m, u_m)
la_s = interpolate(Phi, la1)
# virtual work
De += w*Phi*N1'
Me += w*Phi*N2'
# contact constraints
Ne += w*reshape(kron(N1, n_s, Phi), 2, 4)
Te += w*reshape(kron(N2, n_s, Phi), 2, 4)
He += w*reshape(kron(N1, t_s, Phi), 2, 4)
ge += w*Phi*dot(n_s, x_m-x_s)
ce += w*N1*dot(n_s, la_s)
Rn += w*dot(n_s, la_s)
contact_area += w
contact_error += 1/2*w*dot(n_s, x_s-x_m)^2
end
sdofs = get_gdofs(problem, slave_element)
mdofs = get_gdofs(problem, master_element)
# add contribution to contact virtual work
for i=1:field_dim
lsdofs = sdofs[i:field_dim:end]
lmdofs = mdofs[i:field_dim:end]
add!(problem.assembly.C1, lsdofs, lsdofs, De)
add!(problem.assembly.C1, lsdofs, lmdofs, -Me)
end
# add contribution to contact constraints
add!(problem.assembly.C2, sdofs[1:field_dim:end], sdofs, Ne)
add!(problem.assembly.C2, sdofs[1:field_dim:end], mdofs, -Te)
add!(problem.assembly.D, sdofs[2:field_dim:end], sdofs, He)
add!(problem.assembly.g, sdofs[1:field_dim:end], ge)
add!(problem.assembly.c, sdofs[1:field_dim:end], ce)
end # master elements done
if "contact area" in props.store_fields
update!(slave_element, "contact area", time => contact_area)
end
if "contact error" in props.store_fields
update!(slave_element, "contact error", time => contact_error)
end
end # slave elements done, contact virtual work ready
S = sort(collect(keys(normals))) # slave element nodes
weighted_gap = Dict{Int64, Vector{Float64}}()
contact_pressure = Dict{Int64, Vector{Float64}}()
complementarity_condition = Dict{Int64, Vector{Float64}}()
is_active = Dict{Int64, Int}()
is_inactive = Dict{Int64, Int}()
is_slip = Dict{Int64, Int}()
is_stick = Dict{Int64, Int}()
la = problem.assembly.la
# FIXME: for matrix operations, we need to know the dimensions of the
# final matrices
ndofs = 0
ndofs = max(ndofs, size(problem.assembly.K, 2))
ndofs = max(ndofs, size(problem.assembly.C1, 2))
ndofs = max(ndofs, size(problem.assembly.C2, 2))
ndofs = max(ndofs, size(problem.assembly.D, 2))
ndofs = max(ndofs, size(problem.assembly.g, 2))
ndofs = max(ndofs, size(problem.assembly.c, 2))
C1 = sparse(problem.assembly.C1, ndofs, ndofs)
C2 = sparse(problem.assembly.C2, ndofs, ndofs)
D = sparse(problem.assembly.D, ndofs, ndofs)
g = full(problem.assembly.g, ndofs, 1)
c = full(problem.assembly.c, ndofs, 1)
for j in S
dofs = [2*(j-1)+1, 2*(j-1)+2]
weighted_gap[j] = g[dofs]
end
state = problem.properties.contact_state_in_first_iteration
if problem.properties.iteration == 1
info("First contact iteration, initial contact state = $state")
if state == :AUTO
avg_gap = mean([weighted_gap[j][1] for j in S])
std_gap = std([weighted_gap[j][1] for j in S])
if (avg_gap < 1.0e-12) && (std_gap < 1.0e-12)
state = :ACTIVE
else
state = :UNKNOWN
end
info("Average weighted gap = $avg_gap, std gap = $std_gap, automatically determined contact state = $state")
end
end
# active / inactive node detection
for j in S
dofs = [2*(j-1)+1, 2*(j-1)+2]
weighted_gap[j] = g[dofs]
if length(la) != 0
p = dot(normals[j], la[dofs])
t = dot(tangents[j], la[dofs])
contact_pressure[j] = [p, t]
else
contact_pressure[j] = [0.0, 0.0]
end
complementarity_condition[j] = contact_pressure[j] - weighted_gap[j]
if complementarity_condition[j][1] < 0
is_inactive[j] = 1
is_active[j] = 0
is_slip[j] = 0
is_stick[j] = 0
else
is_inactive[j] = 0
is_active[j] = 1
is_slip[j] = 1
is_stick[j] = 0
end
end
if (problem.properties.iteration == 1) && (state == :ACTIVE)
for j in S
is_inactive[j] = 0
is_active[j] = 1
is_slip[j] = 1
is_stick[j] = 0
end
end
if (problem.properties.iteration == 1) && (state == :INACTIVE)
for j in S
is_inactive[j] = 1
is_active[j] = 0
is_slip[j] = 0
is_stick[j] = 0
end
end
if "weighted gap" in props.store_fields
update!(slave_elements, "weighted gap", time => weighted_gap)
end
if "contact pressure" in props.store_fields
update!(slave_elements, "contact pressure", time => contact_pressure)
end
if "complementarity condition" in props.store_fields
update!(slave_elements, "complementarity condition", time => complementarity_condition)
end
if "active nodes" in props.store_fields
update!(slave_elements, "active nodes", time => is_active)
end
if "inactive nodes" in props.store_fields
update!(slave_elements, "inactive nodes", time => is_inactive)
end
if "stick nodes" in props.store_fields
update!(slave_elements, "stick nodes", time => is_stick)
end
if "slip nodes" in props.store_fields
update!(slave_elements, "slip nodes", time => is_slip)
end
debug("# | active | inactive | stick | slip | gap | pres | comp")
for j in S
str1 = "$j | $(is_active[j]) | $(is_inactive[j]) | $(is_stick[j]) | $(is_slip[j]) | "
str2 = "$(round(weighted_gap[j][1], 3)) | $(round(contact_pressure[j][1], 3)) | $(round(complementarity_condition[j][1], 3))"
debug(str1 * str2)
end
debug("normals: ", normals)
# solve variational inequality
# constitutive modelling in tangent direction, frictionless contact
for j in S
dofs = [2*(j-1)+1, 2*(j-1)+2]
if (is_active[j] == 1) && (is_slip[j] == 1)
debug("$j is in active/slip, removing tangential constraint $(dofs[2])")
C2[dofs[2],:] = 0.0
g[dofs[2]] = 0.0
D[dofs[2], dofs] = tangents[j]
end
end
# remove inactive nodes from assembly
for j in S
dofs = [2*(j-1)+1, 2*(j-1)+2]
if is_inactive[j] == 1
debug("$j is inactive, removing dofs $dofs")
C1[dofs,:] = 0.0
C2[dofs,:] = 0.0
D[dofs,:] = 0.0
g[dofs,:] = 0.0
end
end
problem.assembly.C1 = C1
problem.assembly.C2 = C2
problem.assembly.D = D
problem.assembly.g = g
end
+8 -3
View File
@@ -66,6 +66,11 @@ function assemble!(problem::Problem{Mortar}, time::Float64)
assemble!(problem, time, dimension, use_forwarddiff)
end
function get_slave_elements(problem::Problem)
cond(el) = haskey(el, "master elements") || haskey(el, "potential master elements")
return filter(cond, get_elements(problem))
end
""" Given a CCW ordered set of vertices, calculate area of polygon.
Examples
@@ -99,7 +104,7 @@ function diagnose_interface(problem::Problem{Mortar}, time::Float64)
field_dim = get_unknown_field_dimension(problem)
field_name = get_parent_field_name(problem)
slave_elements = get_slave_elements(problem)
I_area = 0.0
if props.split_quadratic_slave_elements
@@ -125,7 +130,7 @@ function diagnose_interface(problem::Problem{Mortar}, time::Float64)
info(repeat("-", 80))
info("Processing slave element $(slave_element.id), type = $(get_element_type(slave_element))")
info(repeat("-", 80))
S_area = 0.0
S_area_in_contact = 0.0
for ip in get_integration_points(slave_element)
@@ -245,7 +250,7 @@ function diagnose_interface(problem::Problem{Mortar}, time::Float64)
I_area += S_area_in_contact
end # slave elements done, contact virtual work ready
info("Area of interface: $I_area")
info("Smallest cell area: $(minimum(C_areas))")
info("Smallest polygon area: $(minimum(P_areas))")
-214
View File
@@ -1,214 +0,0 @@
# This file is a part of JuliaFEM.
# License is MIT: see https://github.com/JuliaFEM/JuliaFEM.jl/blob/master/LICENSE.md
const MortarElements2D = Union{Seg2,Seg3}
function newton(f, df, x; tol=1.0e-6, max_iterations=10)
for i=1:max_iterations
dx = -f(x)/df(x)
x += dx
if norm(dx) < tol
return x
end
end
error("Newton iteration did not converge in $max_iterations iterations")
end
function cross2(a, b)
cross([a; 0], [b; 0])[3]
end
function get_slave_elements(problem::Problem)
cond(el) = haskey(el, "master elements") || haskey(el, "potential master elements")
return filter(cond, get_elements(problem))
end
function project_from_master_to_slave{E<:MortarElements2D}(slave_element::Element{E}, x2, time)
x1_ = slave_element("geometry", time)
n1_ = slave_element("normal", time)
x1(xi1) = interpolate(vec(get_basis(slave_element, [xi1], time)), x1_)
dx1(xi1) = interpolate(vec(get_dbasis(slave_element, [xi1], time)), x1_)
n1(xi1) = interpolate(vec(get_basis(slave_element, [xi1], time)), n1_)
dn1(xi1) = interpolate(vec(get_dbasis(slave_element, [xi1], time)), n1_)
R(xi1) = cross2(x1(xi1)-x2, n1(xi1))
dR(xi1) = cross2(dx1(xi1), n1(xi1)) + cross2(x1(xi1)-x2, dn1(xi1))
xi1 = nothing
try
xi1 = newton(R, dR, 0.0)
catch
warn("projection from master to slave failed with following arguments:")
warn("slave element x1: $x1_")
warn("slave element n1: $n1_")
warn("master element x2: $x2")
warn("time: $time")
len = norm(x1_[2] - x1_[1])
midpnt = mean(x1_)
dist = norm(midpnt - x2)
distval = dist/len
warn("midpoint of slave element: $midpnt")
warn("length of slave element: $len")
warn("distance between midpoint of slave element and x2: $dist")
warn("charasteristic measure: $distval")
rethrow()
end
return xi1
end
function project_from_slave_to_master{E<:MortarElements2D}(master_element::Element{E}, x1, n1, time)
x2_ = master_element("geometry", time)
x2(xi2) = interpolate(vec(get_basis(master_element, [xi2], time)), x2_)
dx2(xi2) = interpolate(vec(get_dbasis(master_element, [xi2], time)), x2_)
cross2(a, b) = cross([a; 0], [b; 0])[3]
R(xi2) = cross2(x2(xi2)-x1, n1)
dR(xi2) = cross2(dx2(xi2), n1)
xi2 = newton(R, dR, 0.0)
return xi2
end
function calculate_normals(elements, time, ::Type{Val{1}}; rotate_normals=false)
tangents = Dict{Int64, Vector{Float64}}()
for element in elements
conn = get_connectivity(element)
#X1 = element("geometry", time)
#dN = get_dbasis(element, [0.0], time)
#tangent = vec(sum([kron(dN[:,i], X1[i]') for i=1:length(X1)]))
tangent = vec(element([0.0], time, Val{:Jacobian}))
for nid in conn
if haskey(tangents, nid)
tangents[nid] += tangent
else
tangents[nid] = tangent
end
end
end
Q = [0.0 -1.0; 1.0 0.0]
normals = Dict{Int64, Vector{Float64}}()
S = collect(keys(tangents))
for j in S
tangents[j] /= norm(tangents[j])
normals[j] = Q*tangents[j]
end
if rotate_normals
for j in S
normals[j] = -normals[j]
end
end
return normals, tangents
end
function calculate_normals!(elements, time, ::Type{Val{1}}; rotate_normals=false)
normals, tangents = calculate_normals(elements, time, Val{1}; rotate_normals=rotate_normals)
for element in elements
conn = get_connectivity(element)
update!(element, "normal", time => [normals[j] for j in conn])
update!(element, "tangent", time => [tangents[j] for j in conn])
end
end
function assemble!(problem::Problem{Mortar}, time::Float64, ::Type{Val{1}}, ::Type{Val{false}})
props = problem.properties
field_dim = get_unknown_field_dimension(problem)
field_name = get_parent_field_name(problem)
slave_elements = get_slave_elements(problem)
# 1. calculate nodal normals and tangents for slave element nodes j ∈ S
normals, tangents = calculate_normals(slave_elements, time, Val{1};
rotate_normals=props.rotate_normals)
update!(slave_elements, "normal", time => normals)
update!(slave_elements, "tangent", time => tangents)
# 2. loop all slave elements
for slave_element in slave_elements
nsl = length(slave_element)
X1 = slave_element("geometry", time)
n1 = slave_element("normal", time)
# 3. loop all master elements
for master_element in slave_element("master elements", time)
nm = length(master_element)
X2 = master_element("geometry", time)
# 3.1 calculate segmentation
xi1a = project_from_master_to_slave(slave_element, X2[1], time)
xi1b = project_from_master_to_slave(slave_element, X2[2], time)
xi1 = clamp.([xi1a; xi1b], -1.0, 1.0)
l = 1/2*abs(xi1[2]-xi1[1])
isapprox(l, 0.0) && continue # no contribution in this master element
# 3.2. bi-orthogonal basis
De = zeros(nsl, nsl)
Me = zeros(nsl, nsl)
Ae = zeros(nsl, nsl)
if props.dual_basis
for ip in get_integration_points(slave_element, 3)
detJ = slave_element(ip, time, Val{:detJ})
w = ip.weight*detJ*l
xi = ip.coords[1]
xi_s = dot([1/2*(1-xi); 1/2*(1+xi)], xi1)
N1 = vec(get_basis(slave_element, xi_s, time))
De += w*diagm(N1)
Me += w*N1*N1'
end
Ae = De*inv(Me)
else
Ae = eye(nsl)
end
# 3.3. loop integration points of one integration segment and calculate
# local mortar matrices
fill!(De, 0.0)
fill!(Me, 0.0)
ge = zeros(field_dim*nsl)
for ip in get_integration_points(slave_element, 2)
detJ = slave_element(ip, time, Val{:detJ})
w = ip.weight*detJ*l
xi = ip.coords[1]
xi_s = dot([1/2*(1-xi); 1/2*(1+xi)], xi1)
N1 = vec(get_basis(slave_element, xi_s, time))
Phi = Ae*N1
# project gauss point from slave element to master element in direction n_s
X_s = interpolate(N1, X1) # coordinate in gauss point
n_s = interpolate(N1, n1) # normal direction in gauss point
xi_m = project_from_slave_to_master(master_element, X_s, n_s, time)
N2 = vec(get_basis(master_element, xi_m, time))
X_m = interpolate(N2, X2)
De += w*Phi*N1'
Me += w*Phi*N2'
if props.adjust
haskey(slave_element, "displacement") || continue
haskey(master_element, "displacement") || continue
norm(mean(X1) - X2[1]) / norm(X1[2] - X1[1]) < props.distval || continue
norm(mean(X1) - X2[2]) / norm(X1[2] - X1[1]) < props.distval || continue
u1 = slave_element("displacement", time)
u2 = master_element("displacement", time)
x_s = X_s + interpolate(N1, u1)
x_m = X_m + interpolate(N2, u2)
ge += w*vec((x_m-x_s)*Phi')
end
end
# add contribution to contact virtual work
sdofs = get_gdofs(problem, slave_element)
mdofs = get_gdofs(problem, master_element)
for i=1:field_dim
lsdofs = sdofs[i:field_dim:end]
lmdofs = mdofs[i:field_dim:end]
add!(problem.assembly.C1, lsdofs, lsdofs, De)
add!(problem.assembly.C1, lsdofs, lmdofs, -Me)
add!(problem.assembly.C2, lsdofs, lsdofs, De)
add!(problem.assembly.C2, lsdofs, lmdofs, -Me)
end
add!(problem.assembly.g, sdofs, ge)
end # master elements done
end # slave elements done, contact virtual work ready
end
+4 -2
View File
@@ -3,6 +3,8 @@
using ForwardDiff
const MortarElements2D = Union{Seg2,Seg3}
# forwarddiff version of mesh tying in 2d
function project_from_master_to_slave_ad{E<:MortarElements2D}(
@@ -175,7 +177,7 @@ function assemble!(problem::Problem{Mortar}, time::Float64, ::Type{Val{1}}, ::Ty
#dN = get_dbasis(slave_element, ip, time)
#j = sum([kron(dN[:,i], x1[i]') for i=1:length(x1)])
#w = ip.weight*norm(j)*l
xi = ip.coords[1]
xi_s = dot([1/2*(1-xi); 1/2*(1+xi)], xi1)
N1 = vec(get_basis(slave_element, xi_s, time))
@@ -186,7 +188,7 @@ function assemble!(problem::Problem{Mortar}, time::Float64, ::Type{Val{1}}, ::Ty
#xi_m = project_from_slave_to_master(master_element, X_s, n_s, time)
xi_m = project_from_slave_to_master_ad(master_element, x_s, n_s, x2, time)
N2 = vec(get_basis(master_element, xi_m, time))
x_m = interpolate(N2, x2)
x_m = interpolate(N2, x2)
la_s = interpolate(Phi, la1)
gn = dot(n_s, x_s-x_m)
+6 -5
View File
@@ -78,7 +78,7 @@ function calc_projection(problem::Problem{Mortar}, ndim::Int)
@assert C1 == C2
#@assert problem.properties.dual_basis == true
@assert problem.properties.adjust == false
S = get_nonzero_rows(C2)
M = setdiff(get_nonzero_columns(C2), S)
@@ -102,7 +102,8 @@ end
""" Eliminate mesh tie constraints from matrices K, M. """
function eliminate_boundary_conditions!(K_red::SparseMatrixCSC,
M_red::SparseMatrixCSC,
problem::Problem{Mortar}, ndim::Int)
problem::Union{Problem{Mortar}, Problem{Mortar2D}},
ndim::Int)
C1 = sparse(problem.assembly.C1, ndim, ndim)
C2 = sparse(problem.assembly.C2, ndim, ndim)
@@ -145,7 +146,7 @@ function eliminate_boundary_conditions!(K_red::SparseMatrixCSC,
M_red[:,:] = Q*M_red*Q'
M_red[S,:] = 0.0
M_red[:,S] = 0.0
return true
end
@@ -213,7 +214,7 @@ function solve!(solver::Solver{Modal}, time::Float64)
info("Calculate $(props.nev) eigenvalues...")
tic()
if properties.symmetric
K_red = 1/2*(K_red + transpose(K_red))
M_red = 1/2*(M_red + transpose(M_red))
@@ -293,7 +294,7 @@ function solve!(solver::Solver{Modal}, time::Float64)
end
@timeit "save results to Xdmf" update_xdmf!(solver)
return true
end