Files
JuliaFEM.jl/test/test_mortar_2d_contact.jl
T
2016-02-01 09:16:30 +02:00

264 lines
8.5 KiB
Julia

# This file is a part of JuliaFEM.
# License is MIT: see https://github.com/JuliaFEM/JuliaFEM.jl/blob/master/LICENSE.md
using JuliaFEM.Test
using JuliaFEM.Core: Node, Seg2, Tri3, update!, calculate_normal_tangential_coordinates!,
PlaneStressLinearElasticityProblem, DirichletProblem, MortarProblem,
get_elements, DirectSolver, calculate_nodal_vector, FieldAssembly,
FieldProblem, set_linear_system_solver!, set_nonlinear_max_iterations!,
get_elements, Element, BoundaryAssembly, BoundaryProblem,
get_integration_points, get_jacobian, get_connectivity, StandardBasis,
add_postprocessor!, add_preprocessor!, SparseMatrixCOO, add!,
add_linear_system_solver_preprocessor!,
add_linear_system_solver_postprocessor!
import JuliaFEM.Core: assemble_preprocess!, assemble_postprocess!,
linear_system_solver_preprocess!, linear_system_solver_postprocess!
macro debug(msg)
haskey(ENV, "DEBUG") || return
return msg
end
function calculate_normal_tangential_coordinates(elements::Vector{Element}, time::Real)
P = SparseMatrixCOO()
field_dim = 2
for element in elements
haskey(element, "normal-tangential coordinates") || continue
for ip in get_integration_points(element, Val{2})
J = get_jacobian(element, ip, time)
w = ip.weight*norm(J)
nt = transpose(element("normal-tangential coordinates", ip, time))
normal = nt[1,:]
tangent = nt[2,:]
for nid in get_connectivity(element)
ndofs = [2*(nid-1)+1, 2*(nid-1)+2]
add!(P, [2*(nid-1)+1], ndofs, normal)
add!(P, [2*(nid-1)+2], ndofs, tangent)
end
end
end
P = sparse(P)
for i=1:size(P,1)
n = norm(P[i,:])
if n > 0.0
P[i,:] = P[i,:] / n
end
end
return SparseMatrixCOO(P)
end
function assemble_postprocess!(assembly, problem, time::Real, ::Type{Val{:remove_constraint_from_dofs}}, dofs)
# giving additional arguments and keywords is possible too
C1 = sparse(assembly.C1)
C2 = sparse(assembly.C2)
info("removing constraints from dofs: $dofs")
for d in dofs
C1[d,:] = 0
C2[d,:] = 0
end
assembly.C1 = C1
assembly.C2 = C2
end
function assemble_postprocess!(assembly, problem, time::Real, ::Type{Val{:remove_tangential_constraints}})
# example how to use postprocessor to manipulate constraint matrix before summing assemblies together
info("postprocess mortar assembly: remove contraints in tangent direction on boundary.")
dim = 12
C1 = sparse(assembly.C1, dim, dim)
C2 = sparse(assembly.C2, dim, dim)
P = calculate_normal_tangential_coordinates(get_elements(problem), time)
P = sparse(P, dim, dim)
info("projection matrix for normals: ")
dump(round(full(P), 3))
C1 = P*C1
C2 = P*C2
for i=2:2:dim
C1[i,:] = 0
C2[i,:] = 0
end
assembly.C1 = C1
assembly.C2 = C2
info("postprocess mortar assembly: done.")
end
function assemble_postprocess!(assembly, problem, time::Real, ::Type{Val{:primal_dual_active_set_strategy}})
info("PDASS: determining active contact set")
dim = 12
C1 = sparse(assembly.C1, dim, dim)
C2 = sparse(assembly.C2, dim, dim)
info("PDASS: constraint matrix C1")
# dump(round(full(C1[1:2:end,1:2:end]), 3))
dump(round(full(C1), 3))
info("PDASS: constraint matrix C2")
# dump(round(full(C2[1:2:end,1:2:end]), 3))
dump(round(full(C2), 3))
elements = get_elements(problem)
P = calculate_normal_tangential_coordinates(get_elements(problem), time)
P = sparse(P, dim, dim)
la = calculate_nodal_vector("reaction force", 2, elements, time)
X = calculate_nodal_vector("geometry", 2, elements, time)
u = calculate_nodal_vector("displacement", 2, elements, time)
resize!(la, 12)
resize!(X, 12)
resize!(u, 12)
x = X+u
info("x")
dump(reshape(round(x, 2), 2, 6))
P = sparse(eye(dim))
C1 = P*C1
C2 = P*C2
gn = P*C1*X #*4/6 ..?
un = P*C1*u
la = P*la
info("weighted gap in nt =")
dump(reshape(round(gn, 2), 2, 6))
info("weighted u in nt =")
dump(reshape(round(un, 2), 2, 6))
info("weighted joo in nt =")
dump(reshape(round(gn+un, 2), 2, 6))
info("lambda in nt =")
dump(reshape(round(la, 2), 2, 6))
# complementarity function
cn = 1.0
# C = la - clamp(la - cn*(gn+un), 0, Inf)
# C = la + clamp(la - cn*(gn+un), 0, Inf)
C = la + cn*(un - gn)
info("complementarity function =")
dump(reshape(round(C, 2), 2, 6))
g = zeros(length(gn))
for i=1:2:dim
if i == 1
#if C[i] > 0
if i == 7
info("skipping root dof 7")
continue
end
info("dof $i in active set")
# g[i] = -gn[i]
else
info("dof $i not in active set")
# C1[i,:] = 0
# C2[i,:] = 0
end
end
g[1] = 2.0
g[3] = 2.0
#=
for i=2:2:dim
C1[i,:] = 0
C2[i,:] = 0
end
=#
d = [7, 8]
C2[d, :] = 0
assembly.g = sparse(g)
assembly.C1 = C1
assembly.C2 = C2
info("PDASS ready.")
end
function linear_system_solver_preprocess!(solver, iter, time, K, f, C1, C2, D, g, sol, la, ::Type{Val{:before_solution}})
# example how to use preprocessor to dump matrices before solution
@debug begin
info("stiffness matrix")
dump(round(full(K), 3))
info("constraint matrix C1")
dump(round(full(C1), 3))
info("constraint matrix C2")
dump(round(full(C2), 3))
info("force vector")
dump(round(full(f)', 3))
info("constraint vector")
dump(round(full(g)', 3))
end
end
function linear_system_solver_postprocess!(solver, iter, time, K, f, C1, C2, D, g, x, la, ::Type{Val{:after_solution}})
@debug begin
info("solution vector")
dump(round(full(x)', 3))
info("reaction force vector")
dump(round(full(la)', 3))
end
end
@testset "2d frictionless contact" begin
gap = [1.0, 0.0]
nodes = Node[
[6.0, 6.0],
[6.0, 12.0]+gap,
[0.0, 0.0],
[6.0, 0.0],
[6.0, 0.0]+gap,
[18.0, 0.0]+gap]
fel1 = Tri3([1, 3, 4])
fel2 = Tri3([2, 5, 6])
force = Seg2([3, 1])
bnd1 = Seg2([3, 4])
bnd2 = Seg2([5, 6])
sel = Seg2([1, 4])
mel = Seg2([2, 5])
update!([fel1, fel2, force, sel, mel], "geometry", nodes)
update!([bnd1, bnd2], "geometry", nodes)
prob = FieldProblem(PlaneStressLinearElasticityProblem, "bodies", 2)
push!(prob, fel1, fel2)
push!(prob, force)
update!([fel1, fel2], "youngs modulus", 90.0)
update!([fel1, fel2], "poissons ratio", 0.25)
update!([force], "displacement traction force 1", 2*6/sqrt(2))
bc = BoundaryProblem(DirichletProblem, "support", "displacement", 2)
push!(bc, bnd1, bnd2)
update!(get_elements(bc), "displacement", 0.0 => Vector{Float64}[[0.0, 0.0], [0.0, 0.0]])
cont = BoundaryProblem(MortarProblem, "contact", "displacement", 2)
push!(cont, sel, mel)
calculate_normal_tangential_coordinates!(sel, 0.0)
nt = sel("normal-tangential coordinates", [0.0], 0.0)
info("normal direction = $(nt)")
sel["master elements"] = [mel]
# remove coefficients from node 4 (dofs 7-8) because this conflicts with dirichlet bc.
# add_postprocessor!(cont, :remove_constraint_from_dofs, [7, 8])
# remove tangential direction constraints
# add_postprocessor!(cont, :remove_tangential_constraints)
# apply PDASS
add_postprocessor!(cont, :primal_dual_active_set_strategy)
@debug begin
info("fel1.fields = $(fel1.fields)")
end
solver = DirectSolver()
push!(solver, prob)
push!(solver, bc)
push!(solver, cont)
solver.solve_residual = false
set_linear_system_solver!(solver, :UMFPACK)
set_nonlinear_max_iterations!(solver, 5)
add_linear_system_solver_preprocessor!(solver, :before_solution)
add_linear_system_solver_postprocessor!(solver, :after_solution)
add_linear_system_solver_preprocessor!(solver, :dump_matrices)
time = 0.0
call(solver, time)
@debug begin
u = calculate_nodal_vector("displacement", 2, get_elements(prob), time)
info("solution vector")
dump(reshape(round(u, 8), 2, 6))
# la = calculate_nodal_vector("reaction force", 2, get_elements(prob), time)
# info("reaction force")
# dump(reshape(round(la, 8), 2, 6))
end
end