Files
JuliaFEM.jl/src/problems_contact_3d.jl
T
2016-10-01 13:37:15 +03:00

306 lines
11 KiB
Julia

# This file is a part of JuliaFEM.
# License is MIT: see https://github.com/JuliaFEM/JuliaFEM.jl/blob/master/LICENSE.md
typealias ContactElements3D Union{Tri3, Tri6, Quad4, Quad8, Quad9}
function create_orthogonal_basis(n)
I = eye(3)
k = indmax([norm(cross(n,I[:,k])) for k in 1:3])
t1 = cross(n, I[:,k])/norm(cross(n, I[:,k]))
t2 = cross(n, t1)
return t1, t2
end
"""
Frictionless 3d small sliding contact.
problem
time
dimension
finite_sliding
friction
use_forwarddiff
"""
function assemble!(problem::Problem{Contact}, time::Float64,
::Type{Val{2}}, ::Type{Val{false}},
::Type{Val{false}}, ::Type{Val{false}}; debug=true)
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 = calculate_normals(slave_elements, time, Val{2};
rotate_normals=props.rotate_normals)
update!(slave_elements, "normal", time => normals)
# 2. loop all slave elements
for (slave_num, slave_element) in enumerate(slave_elements)
nsl = length(slave_element)
X1 = slave_element("geometry", time)
u1 = slave_element("displacement", time)
la = slave_element("reaction force", time)
n1 = slave_element("normal", time)
if nsl == 3
t11, t21 = create_orthogonal_basis(n1[1])
t12, t22 = create_orthogonal_basis(n1[2])
t13, t23 = create_orthogonal_basis(n1[3])
Q1_ = [n1[1] t11 t21]
Q2_ = [n1[2] t12 t22]
Q3_ = [n1[3] t13 t23]
Z = zeros(3, 3)
Q3 = [Q1_ Z Z; Z Q2_ Z; Z Z Q3_]
elseif nsl == 4
t11, t21 = create_orthogonal_basis(n1[1])
t12, t22 = create_orthogonal_basis(n1[2])
t13, t23 = create_orthogonal_basis(n1[3])
t14, t24 = create_orthogonal_basis(n1[4])
Q1_ = [n1[1] t11 t21]
Q2_ = [n1[2] t12 t22]
Q3_ = [n1[3] t13 t23]
Q4_ = [n1[4] t14 t24]
Z = zeros(3, 3)
Q3 = [Q1_ Z Z Z; Z Q2_ Z Z; Z Z Q3_ Z; Z Z Z Q4_]
else
error("nsl = $nsl")
end
contact_area = 0.0
contact_error = 0.0
element_area = 0.0
for ip in get_integration_points(slave_element)
detJ = slave_element(ip, time, Val{:detJ})
w = ip.weight*detJ
element_area += w
end
if "element area" in props.store_fields
update!(slave_element, "element area", time => element_area)
end
# if slave_num == 1
# info("First slave element area = $element_area")
# info("NT basis of first slave element")
# dump(Q3)
# end
# project slave nodes to auxiliary plane (x0, Q)
#xi = get_reference_element_midpoint(slave_element)
if nsl == 3
xi = [1/3, 1/3]
else
xi = [1/4, 1/4]
end
N = vec(get_basis(slave_element, xi, time))
x0 = N*X1
n0 = N*n1
S = Vector[project_vertex_to_auxiliary_plane(p, x0, n0) for p in X1]
# 3. loop all master elements
for master_element in slave_element("master elements", time)
nm = length(master_element)
X2 = master_element("geometry", time)
u2 = master_element("displacement", time)
x2 = X2 + u2
#=
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
norm(mean(X1) - X2[3]) / norm(X1[2] - X1[1]) < props.distval || continue
=#
# 3.1 project master nodes to auxiliary plane and create polygon clipping
M = Vector[project_vertex_to_auxiliary_plane(p, x0, n0) for p in X2]
P = get_polygon_clip(S, M, n0)
length(P) < 3 && continue # no clipping or shared edge (no volume)
check_orientation!(P, n0)
C0 = calculate_centroid(P)
De = zeros(nsl, nsl)
Me = zeros(nsl, nm)
ge = zeros(field_dim*nsl)
# 4. loop integration cells
for cell in get_cells(P, C0)
virtual_element = Element(Tri3)
update!(virtual_element, "geometry", cell)
# 5. loop integration point of integration cell
for ip in get_integration_points(virtual_element, 3)
# project gauss point from auxiliary plane to master and slave element
x_gauss = virtual_element("geometry", ip, time)
if isnan(x_gauss[1])
info("is nan")
info("x_gauss = $x_gauss")
info("cell = $cell")
info("C0 = $C0")
info("P = $P")
info("S = $S")
info("M = $M")
info("n0 = $n0")
error("nan, unable to continue")
end
xi_s, alpha = project_vertex_to_surface(x_gauss, x0, n0, slave_element, X1, time)
xi_m, alpha = project_vertex_to_surface(x_gauss, x0, n0, master_element, X2, time)
detJ = virtual_element(ip, time, Val{:detJ})
w = ip.weight*detJ
# add contributions
N1 = vec(get_basis(slave_element, xi_s, time))
N2 = vec(get_basis(master_element, xi_m, time))
De += w*N1*N1'
Me += w*N1*N2'
x_s = N1*(X1+u1)
x_m = N2*(X2+u2)
ge += w*vec((x_m-x_s)*N1')
contact_area += w
n_s = N1*n1
contact_error += 1/2*w*dot(n_s, x_s-x_m)^2
end # integration points done
end # integration cells done
# 6. add contribution to contact virtual work
sdofs = get_gdofs(problem, slave_element)
mdofs = get_gdofs(problem, master_element)
nsldofs = length(sdofs)
nmdofs = length(mdofs)
D3 = zeros(nsldofs, nsldofs)
M3 = zeros(nsldofs, nmdofs)
for i=1:field_dim
D3[i:field_dim:end, i:field_dim:end] += De
M3[i:field_dim:end, i:field_dim:end] += Me
end
add!(problem.assembly.C1, sdofs, sdofs, D3)
add!(problem.assembly.C1, sdofs, mdofs, -M3)
add!(problem.assembly.C2, sdofs, sdofs, Q3'*D3)
add!(problem.assembly.C2, sdofs, mdofs, -Q3'*M3)
add!(problem.assembly.g, sdofs, Q3'*ge)
end # master elements done
if "contact area" in props.store_fields
update!(slave_element, "contact area", time => contact_area)
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
ndofs = length(la)
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)
# active / inactive node detection
for j in S
dofs = [3*(j-1)+1, 3*(j-1)+2, 3*(j-1)+3]
weighted_gap[j] = g[dofs]
if length(la) != 0
normal = normals[j]
tangent1, tangent2 = create_orthogonal_basis(normal)
p = dot(normal, la[dofs])
t1 = dot(tangent1, la[dofs])
t2 = dot(tangent2, la[dofs])
contact_pressure[j] = [p, t1, t2]
else
contact_pressure[j] = [0.0, 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 "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
#=
info("# | 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], 3)) | $(round(contact_pressure[j], 3)) | $(round(complementarity_condition[j], 3))"
info(str1 * str2)
end
=#
# solve variational inequality
# constitutive modelling in tangent direction, frictionless contact
for j in S
dofs = [3*(j-1)+1, 3*(j-1)+2, 3*(j-1)+3]
tdofs = dofs[[2,3]]
if (is_active[j] == 1) && (is_slip[j] == 1)
# info("$j is in active/slip, removing tangential constraints $tdofs")
C2[tdofs,:] = 0.0
g[tdofs] = 0.0
normal = normals[j]
tangent1, tangent2 = create_orthogonal_basis(normal)
D[tdofs[1], dofs] = tangent1
D[tdofs[2], dofs] = tangent2
end
end
# remove inactive nodes from assembly
for j in S
dofs = [3*(j-1)+1, 3*(j-1)+2, 3*(j-1)+3]
if is_inactive[j] == 1
# info("$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