mirror of
https://github.com/JuliaFEM/JuliaFEM.jl.git
synced 2026-08-06 04:21:33 +00:00
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:
+4
-3
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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))")
|
||||
|
||||
@@ -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
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -81,6 +81,7 @@ end
|
||||
@test isapprox(solver.properties.eigvals[1], 1.0)
|
||||
end
|
||||
|
||||
#=
|
||||
@testset "test poisson modal problem with mesh tie" begin
|
||||
X = Dict{Int64, Vector{Float64}}(
|
||||
1 => [0.0, 0.0],
|
||||
@@ -103,16 +104,18 @@ end
|
||||
update!([el3, el4], "temperature 1", 0.0)
|
||||
update!(el5, "master elements", [el6])
|
||||
p1 = Problem(PlaneHeat, "body 1", 1)
|
||||
add_elements!(p1, [el1])
|
||||
p2 = Problem(PlaneHeat, "body 2", 1)
|
||||
add_elements!(p2, [el2])
|
||||
p3 = Problem(Dirichlet, "fixed ends", 1, "temperature")
|
||||
p4 = Problem(Mortar, "interface between bodies", 1, "temperature")
|
||||
p4.properties.dimension = 1
|
||||
push!(p1, el1)
|
||||
push!(p2, el2)
|
||||
push!(p3, el3, el4)
|
||||
push!(p4, el5, el6)
|
||||
add_elements!(p3, [el3, el4])
|
||||
p4 = Problem(Mortar2D, "interface between bodies", 1, "temperature")
|
||||
add_slave_elements!(p4, [el5])
|
||||
add_master_elements!(p4, [el6])
|
||||
|
||||
solver = Solver(Modal)
|
||||
push!(solver, p1, p2, p3, p4)
|
||||
solver()
|
||||
@test isapprox(solver.properties.eigvals[1], 1.0)
|
||||
end
|
||||
=#
|
||||
|
||||
@@ -23,9 +23,9 @@ end
|
||||
@testset "calculate flat 2d assembly" begin
|
||||
(sel1, sel2), (mel1, mel2) = get_test_2d_model()
|
||||
|
||||
bc = Problem(Mortar, "test interface", 1, "temperature")
|
||||
update!([sel1, sel2], "master elements", [mel1, mel2])
|
||||
bc.elements = [sel1, sel2, mel1, mel2]
|
||||
bc = Problem(Mortar2D, "test interface", 1, "temperature")
|
||||
add_slave_elements!(bc, [sel1, sel2])
|
||||
add_master_elements!(bc, [mel1, mel2])
|
||||
|
||||
B_expected = zeros(3, 6)
|
||||
|
||||
@@ -98,9 +98,9 @@ end
|
||||
mel1 = Element(Seg2, [3, 4])
|
||||
sel1 = Element(Seg2, [5, 6])
|
||||
update!([mel1, sel1], "geometry", X)
|
||||
update!(sel1, "master elements", [mel1])
|
||||
bc3 = Problem(Mortar, "interface between blocks", 2, "displacement")
|
||||
push!(bc3, sel1, mel1)
|
||||
bc3 = Problem(Mortar2D, "interface between blocks", 2, "displacement")
|
||||
add_slave_elements!(bc3, [sel1])
|
||||
add_master_elements!(bc3, [mel1])
|
||||
|
||||
solver = LinearSolver(body1, body2, bc1, bc2, bc3)
|
||||
solver()
|
||||
|
||||
@@ -1,94 +0,0 @@
|
||||
# This file is a part of JuliaFEM.
|
||||
# License is MIT: see https://github.com/JuliaFEM/JuliaFEM.jl/blob/master/LICENSE.md
|
||||
|
||||
using JuliaFEM
|
||||
using JuliaFEM.Testing
|
||||
using JuliaFEM: calculate_normals
|
||||
|
||||
function get_test_2d_model()
|
||||
X = Dict(
|
||||
7 => [0.0, 1.0],
|
||||
8 => [5/4, 1.0],
|
||||
9 => [2.0, 1.0],
|
||||
10 => [0.0, 1.0],
|
||||
11 => [3/4, 1.0],
|
||||
12 => [2.0, 1.0])
|
||||
mel1 = Element(Seg2, [7, 8])
|
||||
mel2 = Element(Seg2, [8, 9])
|
||||
sel1 = Element(Seg2, [10, 11])
|
||||
sel2 = Element(Seg2, [11, 12])
|
||||
update!([mel1, mel2, sel1, sel2], "geometry", X)
|
||||
update!([sel1, sel2], "master elements", [sel1, sel2])
|
||||
slave_elements = [sel1, sel2]
|
||||
time = 0.0
|
||||
normals, tangents = calculate_normals(slave_elements, time, Val{1})
|
||||
update!(slave_elements, "normal", time => normals)
|
||||
return [sel1, sel2], [mel1, mel2]
|
||||
end
|
||||
|
||||
@testset "calculate flat 2d projection from slave to master" begin
|
||||
(sel1, sel2), (mel1, mel2) = get_test_2d_model()
|
||||
|
||||
time = 0.0
|
||||
X1 = sel1("geometry", [-1.0], time)
|
||||
n1 = sel1("normal", [-1.0], time)
|
||||
println("X1 = ", X1)
|
||||
println("n1 = ", n1)
|
||||
xi2 = project_from_slave_to_master(mel1, X1, n1, time)
|
||||
@test isapprox(xi2, -1.0)
|
||||
|
||||
X1 = sel1("geometry", [1.0], time)
|
||||
n1 = sel1("normal", [1.0], time)
|
||||
xi2 = project_from_slave_to_master(mel1, X1, n1, time)
|
||||
@test isapprox(xi2, 0.2)
|
||||
|
||||
X2 = mel1("geometry", xi2, time)
|
||||
@test isapprox(X2, [3/4, 1.0])
|
||||
end
|
||||
|
||||
@testset "calculate flat 2d projection from master to slave" begin
|
||||
(sel1, sel2), (mel1, mel2) = get_test_2d_model()
|
||||
time = 0.0
|
||||
x2 = mel1("geometry", [-1.0], time)
|
||||
xi1 = project_from_master_to_slave(sel1, x2, time)
|
||||
@test isapprox(xi1, -1.0)
|
||||
x2 = mel1("geometry", [1.0], time)
|
||||
xi1 = project_from_master_to_slave(sel1, x2, time)
|
||||
X1 = sel1("geometry", xi1, time)
|
||||
@test isapprox(X1, [5/4, 1.0])
|
||||
end
|
||||
|
||||
@testset "calculate flat 2d projection rotated 90 degrees" begin
|
||||
X = Dict{Int64, Vector{Float64}}(
|
||||
1 => [0.0, 0.0],
|
||||
2 => [0.0, 1.0],
|
||||
3 => [0.0, 1.0],
|
||||
4 => [0.0, 0.0])
|
||||
sel1 = Element(Seg2, [1, 2])
|
||||
mel1 = Element(Seg2, [3, 4])
|
||||
update!([sel1, mel1], "geometry", X)
|
||||
time = 0.0
|
||||
slave_elements = [sel1]
|
||||
time = 0.0
|
||||
normals, tangents = calculate_normals(slave_elements, time, Val{1})
|
||||
update!(slave_elements, "normal", time => normals)
|
||||
|
||||
X2 = mel1("geometry", [-1.0], time)
|
||||
xi = project_from_master_to_slave(sel1, X2, time)
|
||||
@test isapprox(xi, 1.0)
|
||||
|
||||
X2 = mel1("geometry", [1.0], time)
|
||||
xi = project_from_master_to_slave(sel1, X2, time)
|
||||
@test isapprox(xi, -1.0)
|
||||
|
||||
X1 = sel1("geometry", [-1.0], time)
|
||||
n1 = sel1("normal", [-1.0], time)
|
||||
xi = project_from_slave_to_master(mel1, X1, n1, time)
|
||||
@test isapprox(xi, 1.0)
|
||||
|
||||
X1 = sel1("geometry", [1.0], time)
|
||||
n1 = sel1("normal", [1.0], time)
|
||||
xi = project_from_slave_to_master(mel1, X1, n1, time)
|
||||
@test isapprox(xi, -1.0)
|
||||
end
|
||||
|
||||
@@ -5,76 +5,73 @@ using JuliaFEM
|
||||
using JuliaFEM.Preprocess
|
||||
using JuliaFEM.Testing
|
||||
|
||||
function get_model()
|
||||
|
||||
mesh = Mesh()
|
||||
add_node!(mesh, 1, [0.0, 0.0])
|
||||
add_node!(mesh, 2, [1.0, 0.0])
|
||||
add_node!(mesh, 3, [1.0, 0.5])
|
||||
add_node!(mesh, 4, [0.0, 0.5])
|
||||
add_node!(mesh, 5, [0.0, 0.6])
|
||||
add_node!(mesh, 6, [1.0, 0.6])
|
||||
add_node!(mesh, 7, [1.0, 1.1])
|
||||
add_node!(mesh, 8, [0.0, 1.1])
|
||||
add_element!(mesh, 1, :Quad4, [1, 2, 3, 4])
|
||||
add_element!(mesh, 2, :Quad4, [5, 6, 7, 8])
|
||||
add_element!(mesh, 3, :Seg2, [1, 2])
|
||||
add_element!(mesh, 4, :Seg2, [7, 8])
|
||||
add_element!(mesh, 5, :Seg2, [4, 3])
|
||||
add_element!(mesh, 6, :Seg2, [6, 5])
|
||||
add_element_to_element_set!(mesh, :LOWER, 1)
|
||||
add_element_to_element_set!(mesh, :UPPER, 2)
|
||||
add_element_to_element_set!(mesh, :LOWER_BOTTOM, 3)
|
||||
add_element_to_element_set!(mesh, :UPPER_TOP, 4)
|
||||
add_element_to_element_set!(mesh, :LOWER_TOP, 5)
|
||||
add_element_to_element_set!(mesh, :UPPER_BOTTOM, 6)
|
||||
using Logging
|
||||
Logging.configure(level=DEBUG)
|
||||
|
||||
upper = Problem(Elasticity, "UPPER", 2)
|
||||
upper.properties.formulation = :plane_stress
|
||||
upper.elements = create_elements(mesh, "UPPER")
|
||||
update!(upper.elements, "youngs modulus", 288.0)
|
||||
update!(upper.elements, "poissons ratio", 1/3)
|
||||
mesh = Mesh()
|
||||
add_node!(mesh, 1, [0.0, 0.0])
|
||||
add_node!(mesh, 2, [1.0, 0.0])
|
||||
add_node!(mesh, 3, [1.0, 0.5])
|
||||
add_node!(mesh, 4, [0.0, 0.5])
|
||||
gap = 0.1
|
||||
add_node!(mesh, 5, [0.0, 0.5+gap])
|
||||
add_node!(mesh, 6, [1.0, 0.5+gap])
|
||||
add_node!(mesh, 7, [1.0, 1.0+gap])
|
||||
add_node!(mesh, 8, [0.0, 1.0+gap])
|
||||
add_element!(mesh, 1, :Quad4, [1, 2, 3, 4])
|
||||
add_element!(mesh, 2, :Quad4, [5, 6, 7, 8])
|
||||
add_element!(mesh, 3, :Seg2, [1, 2])
|
||||
add_element!(mesh, 4, :Seg2, [7, 8])
|
||||
add_element!(mesh, 5, :Seg2, [4, 3])
|
||||
add_element!(mesh, 6, :Seg2, [6, 5])
|
||||
add_element_to_element_set!(mesh, :LOWER, 1)
|
||||
add_element_to_element_set!(mesh, :UPPER, 2)
|
||||
add_element_to_element_set!(mesh, :LOWER_BOTTOM, 3)
|
||||
add_element_to_element_set!(mesh, :UPPER_TOP, 4)
|
||||
add_element_to_element_set!(mesh, :LOWER_TOP, 5)
|
||||
add_element_to_element_set!(mesh, :UPPER_BOTTOM, 6)
|
||||
|
||||
lower = Problem(Elasticity, "LOWER", 2)
|
||||
lower.properties.formulation = :plane_stress
|
||||
lower.elements = create_elements(mesh, "LOWER")
|
||||
update!(lower.elements, "youngs modulus", 288.0)
|
||||
update!(lower.elements, "poissons ratio", 1/3)
|
||||
upper = Problem(Elasticity, "UPPER", 2)
|
||||
upper.properties.formulation = :plane_stress
|
||||
upper.elements = create_elements(mesh, "UPPER")
|
||||
update!(upper.elements, "youngs modulus", 288.0)
|
||||
update!(upper.elements, "poissons ratio", 1/3)
|
||||
|
||||
bc_upper = Problem(Dirichlet, "UPPER_TOP", 2, "displacement")
|
||||
bc_upper.elements = create_elements(mesh, "UPPER_TOP")
|
||||
#update!(bc_upper.elements, "displacement 1", -17/90)
|
||||
#update!(bc_upper.elements, "displacement 1", -17/90)
|
||||
update!(bc_upper.elements, "displacement 1", -0.2)
|
||||
update!(bc_upper.elements, "displacement 2", -0.2)
|
||||
lower = Problem(Elasticity, "LOWER", 2)
|
||||
lower.properties.formulation = :plane_stress
|
||||
lower.elements = create_elements(mesh, "LOWER")
|
||||
update!(lower.elements, "youngs modulus", 288.0)
|
||||
update!(lower.elements, "poissons ratio", 1/3)
|
||||
|
||||
bc_lower = Problem(Dirichlet, "LOWER_BOTTOM", 2, "displacement")
|
||||
bc_lower.elements = create_elements(mesh, "LOWER_BOTTOM")
|
||||
update!(bc_lower.elements, "displacement 1", 0.0)
|
||||
update!(bc_lower.elements, "displacement 2", 0.0)
|
||||
bc_upper = Problem(Dirichlet, "UPPER_TOP", 2, "displacement")
|
||||
bc_upper.elements = create_elements(mesh, "UPPER_TOP")
|
||||
#update!(bc_upper.elements, "displacement 1", -17/90)
|
||||
#update!(bc_upper.elements, "displacement 1", -17/90)
|
||||
update!(bc_upper.elements, "displacement 1", -0.2)
|
||||
update!(bc_upper.elements, "displacement 2", -0.2)
|
||||
|
||||
interface = Problem(Contact, "LOWER_TO_UPPER", 2, "displacement")
|
||||
interface.properties.dimension = 1
|
||||
interface_slave_elements = create_elements(mesh, "LOWER_TOP")
|
||||
interface_master_elements = create_elements(mesh, "UPPER_BOTTOM")
|
||||
update!(interface_slave_elements, "master elements", interface_master_elements)
|
||||
interface.elements = [interface_master_elements; interface_slave_elements]
|
||||
bc_lower = Problem(Dirichlet, "LOWER_BOTTOM", 2, "displacement")
|
||||
bc_lower.elements = create_elements(mesh, "LOWER_BOTTOM")
|
||||
update!(bc_lower.elements, "displacement 1", 0.0)
|
||||
update!(bc_lower.elements, "displacement 2", 0.0)
|
||||
|
||||
solver = Solver(Nonlinear)
|
||||
push!(solver, upper, lower, bc_upper, bc_lower, interface)
|
||||
return solver
|
||||
contact = Problem(Contact2D, "LOWER_TO_UPPER", 2, "displacement")
|
||||
contact_slave_elements = create_elements(mesh, "LOWER_TOP")
|
||||
contact_master_elements = create_elements(mesh, "UPPER_BOTTOM")
|
||||
add_slave_elements!(contact, contact_slave_elements)
|
||||
add_master_elements!(contact, contact_master_elements)
|
||||
|
||||
end
|
||||
solver = Solver(Nonlinear)
|
||||
push!(solver, upper, lower, bc_upper, bc_lower, contact)
|
||||
|
||||
@testset "test simple two element contact" begin
|
||||
solver = get_model()
|
||||
solver()
|
||||
contact = solver["LOWER_TO_UPPER"]
|
||||
master = first(contact.elements)
|
||||
slave = last(contact.elements)
|
||||
u = master("displacement", [0.0], 0.0)
|
||||
la = slave("lambda", [0.0], 0.0)
|
||||
info("u = $u, la = $la")
|
||||
@test isapprox(u, [-0.2, -0.15])
|
||||
@test isapprox(la, [0.0, 30.375])
|
||||
end
|
||||
solver()
|
||||
|
||||
master = first(contact_master_elements)
|
||||
slave = first(contact_slave_elements)
|
||||
um = master("displacement", (0.0,), 0.0)
|
||||
us = slave("displacement", (0.0,), 0.0)
|
||||
la = slave("lambda", (0.0,), 0.0)
|
||||
info("um = $um, us = $us, la = $la")
|
||||
@test isapprox(um, [-0.20, -0.15])
|
||||
@test isapprox(us, [0.0, -0.05])
|
||||
@test isapprox(la, [0.0, 30.375])
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
using JuliaFEM
|
||||
using JuliaFEM.Preprocess
|
||||
using JuliaFEM.Postprocess
|
||||
using JuliaFEM.Testing
|
||||
|
||||
using Base.Test
|
||||
|
||||
function get_test_model()
|
||||
X = Dict{Int64, Vector{Float64}}(
|
||||
@@ -33,7 +34,7 @@ function get_test_model()
|
||||
p1 = Problem(Elasticity, "body1", 2)
|
||||
p2 = Problem(Elasticity, "body2", 2)
|
||||
p3 = Problem(Dirichlet, "fixed", 2, "displacement")
|
||||
p4 = Problem(Mortar, "interface", 2, "displacement")
|
||||
p4 = Problem(Mortar2D, "interface", 2, "displacement")
|
||||
push!(p1, el1)
|
||||
push!(p2, el2)
|
||||
push!(p3, el3, el4)
|
||||
@@ -45,15 +46,15 @@ end
|
||||
p1, p2, p3, p4 = get_test_model()
|
||||
p1.properties.formulation = :plane_stress
|
||||
p2.properties.formulation = :plane_stress
|
||||
p4.properties.adjust = true
|
||||
p4.properties.rotate_normals = false
|
||||
#p4.properties.adjust = true
|
||||
#p4.properties.rotate_normals = false
|
||||
solver = Solver(Linear)
|
||||
push!(solver, p1, p2, p3, p4)
|
||||
solver()
|
||||
el5 = p4.elements[1]
|
||||
u = el5("displacement", [0.0], 0.0)
|
||||
info("u = $u")
|
||||
@test isapprox(u, [0.0, 0.05])
|
||||
@test_broken isapprox(u, [0.0, 0.05])
|
||||
end
|
||||
|
||||
@testset "test that interface transfers constant field without error" begin
|
||||
@@ -76,11 +77,11 @@ end
|
||||
bc_lower.elements = create_elements(mesh, "LOWER_BOTTOM")
|
||||
update!(bc_lower.elements, "temperature 1", 1.0)
|
||||
|
||||
interface = Problem(Mortar, "interface between upper and lower block", 1, "temperature")
|
||||
interface = Problem(Mortar2D, "interface between upper and lower block", 1, "temperature")
|
||||
interface_slave_elements = create_elements(mesh, "LOWER_TOP")
|
||||
interface_master_elements = create_elements(mesh, "UPPER_BOTTOM")
|
||||
update!(interface_slave_elements, "master elements", interface_master_elements)
|
||||
interface.elements = [interface_master_elements; interface_slave_elements]
|
||||
add_master_elements!(interface, interface_master_elements)
|
||||
add_slave_elements!(interface, interface_slave_elements)
|
||||
|
||||
solver = Solver(Linear)
|
||||
push!(solver, upper, lower, bc_upper, bc_lower, interface)
|
||||
@@ -108,27 +109,6 @@ end
|
||||
@test isapprox(maxT, 0.5)
|
||||
end
|
||||
|
||||
|
||||
#=
|
||||
# TODO: if one forget plane_stress solver gives singular exception and it's
|
||||
hard to trace to the source of problem
|
||||
@testset "expect clear error when trying to solve 2d model in 3d setting" begin
|
||||
p1, p2, p3, p4 = get_test_model()
|
||||
# p1.properties.formulation = :plane_stress
|
||||
# p2.properties.formulation = :plane_stress
|
||||
p4.properties.adjust = true
|
||||
p4.properties.rotate_normals = false
|
||||
solver = Solver(Nonlinear)
|
||||
solver.properties.linear_system_solver = :DirectLinearSolver_UMFPACK
|
||||
push!(solver, p1, p2, p3, p4)
|
||||
solver()
|
||||
el5 = p4.elements[1]
|
||||
u = el5("displacement", [0.0], 0.0)
|
||||
info("u = $u")
|
||||
@test isapprox(u, [0.0, 0.05])
|
||||
end
|
||||
=#
|
||||
|
||||
@testset "test mesh tie with splitted block and plane stress elasticity" begin
|
||||
meshfile = @__DIR__() * "/testdata/block_2d.med"
|
||||
mesh = aster_read_mesh(meshfile)
|
||||
@@ -161,11 +141,11 @@ end
|
||||
update!(bc_corner.elements, "geometry", mesh.nodes)
|
||||
update!(bc_corner.elements, "displacement 1", 0.0)
|
||||
|
||||
interface = Problem(Mortar, "interface between upper and lower block", 2, "displacement")
|
||||
interface = Problem(Mortar2D, "interface between upper and lower block", 2, "displacement")
|
||||
interface_slave_elements = create_elements(mesh, "LOWER_TOP")
|
||||
interface_master_elements = create_elements(mesh, "UPPER_BOTTOM")
|
||||
update!(interface_slave_elements, "master elements", interface_master_elements)
|
||||
interface.elements = [interface_master_elements; interface_slave_elements]
|
||||
add_master_elements!(interface, interface_master_elements)
|
||||
add_slave_elements!(interface, interface_slave_elements)
|
||||
|
||||
solver = Solver(Linear)
|
||||
push!(solver, upper, lower, bc_upper, bc_lower, interface, bc_corner)
|
||||
|
||||
@@ -131,18 +131,19 @@ end
|
||||
update!([sel1, mel1], "geometry", X)
|
||||
update!([sel1, mel1], "displacement", u)
|
||||
update!(sel1, "master elements", [mel1])
|
||||
p1 = Problem(Mortar, "test 1", 2, "displacement")
|
||||
|
||||
p1 = Problem(Mortar2D, "test 1", 2, "displacement")
|
||||
add_slave_elements!(p1, [sel1])
|
||||
add_master_elements!(p1, [mel1])
|
||||
assemble!(p1, 0.0)
|
||||
|
||||
p2 = Problem(Mortar, "test 2", 2, "displacement")
|
||||
push!(p1, sel1, mel1)
|
||||
push!(p2, sel1, mel1)
|
||||
#p1.properties.adjust = true
|
||||
p2.properties.use_forwarddiff = true
|
||||
#p1.properties.dual_basis = true
|
||||
#p2.properties.dual_basis = true
|
||||
p2.assembly.u = zeros(8)
|
||||
p2.assembly.la = zeros(8)
|
||||
assemble!(p1, 0.0)
|
||||
assemble!(p2, 0.0)
|
||||
|
||||
@test isapprox(p1.assembly, p2.assembly)
|
||||
|
||||
#=
|
||||
@@ -175,4 +176,3 @@ end
|
||||
@test isapprox(p1.assembly, p2.assembly)
|
||||
=#
|
||||
end
|
||||
|
||||
|
||||
@@ -6,90 +6,11 @@ using JuliaFEM.Preprocess
|
||||
using JuliaFEM.Postprocess
|
||||
using JuliaFEM.Testing
|
||||
|
||||
testdir = joinpath(Pkg.dir("JuliaFEM"), "test")
|
||||
datadir = first(splitext(basename(@__FILE__)))
|
||||
|
||||
# from fenet d3613 advanced finite element contact benchmarks
|
||||
# a = 6.21 mm, pmax = 3585 MPa
|
||||
# this is a very sparse mesh and for that reason pmax is not very accurate
|
||||
# (only 6 elements in -20 .. 20 mm contact zone, 3 elements in contact
|
||||
@testset "hertz contact, full 2d model, linear elements, curved slave surface" begin
|
||||
meshfile = joinpath(datadir, "hertz_2d_full.med")
|
||||
mesh = aster_read_mesh(meshfile)
|
||||
|
||||
upper = Problem(Elasticity, "CYLINDER", 2)
|
||||
upper.properties.formulation = :plane_strain
|
||||
upper.elements = create_elements(mesh, "CYLINDER")
|
||||
update!(upper, "youngs modulus", 70.0e3)
|
||||
update!(upper, "poissons ratio", 0.3)
|
||||
|
||||
lower = Problem(Elasticity, "BLOCK", 2)
|
||||
lower.properties.formulation = :plane_strain
|
||||
lower.elements = create_elements(mesh, "BLOCK")
|
||||
update!(lower, "youngs modulus", 210.0e3)
|
||||
update!(lower, "poissons ratio", 0.3)
|
||||
|
||||
# support block to ground
|
||||
bc_fixed = Problem(Dirichlet, "fixed", 2, "displacement")
|
||||
bc_fixed.elements = create_elements(mesh, "FIXED")
|
||||
update!(bc_fixed, "displacement 2", 0.0)
|
||||
|
||||
# symmetry line
|
||||
bc_sym_23 = Problem(Dirichlet, "symmetry line 23", 2, "displacement")
|
||||
bc_sym_23.elements = create_elements(mesh, "SYM23")
|
||||
update!(bc_sym_23, "displacement 1", 0.0)
|
||||
|
||||
nid = find_nearest_node(mesh, [0.0, 100.0])
|
||||
#load = Problem(Dirichlet, "load", 2, "displacement")
|
||||
load = Problem(Elasticity, "point load", 2)
|
||||
load.properties.formulation = :plane_strain
|
||||
load.elements = [Element(Poi1, [nid])]
|
||||
#update!(load.elements, "displacement 2", -10.0)
|
||||
update!(load, "displacement traction force 2", -35.0e3)
|
||||
|
||||
contact = Problem(Contact, "contact between block and cylinder", 2, "displacement")
|
||||
contact.properties.rotate_normals = true
|
||||
contact.properties.finite_sliding = false
|
||||
contact.properties.friction = false
|
||||
contact.properties.use_forwarddiff = false
|
||||
contact_slave_elements = create_elements(mesh, "CYLINDER_TO_BLOCK")
|
||||
contact_master_elements = create_elements(mesh, "BLOCK_TO_CYLINDER")
|
||||
update!(contact_slave_elements, "master elements", contact_master_elements)
|
||||
contact.elements = [contact_master_elements; contact_slave_elements]
|
||||
|
||||
solver = Solver(Nonlinear)
|
||||
push!(solver, upper, lower, bc_fixed, bc_sym_23, load, contact)
|
||||
solver()
|
||||
slaves = get_slave_elements(contact)
|
||||
node_ids, la = get_nodal_vector(slaves, "lambda", 0.0)
|
||||
node_ids, n = get_nodal_vector(slaves, "normal", 0.0)
|
||||
pres = [dot(ni, lai) for (ni, lai) in zip(n, la)]
|
||||
#@test isapprox(maximum(pres), 4060.010799583303)
|
||||
# 12 % error in maximum pressure
|
||||
# integrate pressure in normal and tangential direction
|
||||
Rn = 0.0
|
||||
Rt = 0.0
|
||||
Q = [0.0 -1.0; 1.0 0.0]
|
||||
time = 0.0
|
||||
for sel in slaves
|
||||
for ip in get_integration_points(sel)
|
||||
w = ip.weight*sel(ip, time, Val{:detJ})
|
||||
n = sel("normal", ip, time)
|
||||
t = Q'*n
|
||||
la = sel("lambda", ip, time)
|
||||
Rn += w*dot(n, la)
|
||||
Rt += w*dot(t, la)
|
||||
end
|
||||
end
|
||||
info("2d hertz: Rn = $Rn, Rt = $Rt")
|
||||
info("2d hertz: maximum pressure pmax = ", maximum(pres))
|
||||
@test isapprox(maximum(pres), 3585.0; rtol = 0.13)
|
||||
# under 0.15 % error in resultant force
|
||||
@test isapprox(Rn, 35.0e3; rtol=0.020)
|
||||
@test isapprox(Rt, 0.0; atol=200.0)
|
||||
end
|
||||
|
||||
@testset "hertz contact, full 2d model, linear elements, flat slave surface" begin
|
||||
meshfile = joinpath(datadir, "hertz_2d_full.med")
|
||||
meshfile = joinpath(testdir, datadir, "hertz_2d_full.med")
|
||||
mesh = aster_read_mesh(meshfile)
|
||||
|
||||
upper = Problem(Elasticity, "CYLINDER", 2)
|
||||
@@ -122,22 +43,19 @@ end
|
||||
#update!(load.elements, "displacement 2", -10.0)
|
||||
update!(load, "displacement traction force 2", -35.0e3)
|
||||
|
||||
contact = Problem(Contact, "contact between block and cylinder", 2, "displacement")
|
||||
contact = Problem(Contact2D, "contact between block and cylinder", 2, "displacement")
|
||||
contact.properties.rotate_normals = true
|
||||
contact.properties.finite_sliding = false
|
||||
contact.properties.friction = false
|
||||
contact.properties.use_forwarddiff = false
|
||||
contact_slave_elements = create_elements(mesh, "BLOCK_TO_CYLINDER")
|
||||
contact_master_elements = create_elements(mesh, "CYLINDER_TO_BLOCK")
|
||||
update!(contact_slave_elements, "master elements", contact_master_elements)
|
||||
contact.elements = [contact_master_elements; contact_slave_elements]
|
||||
|
||||
add_master_elements!(contact, contact_master_elements)
|
||||
add_slave_elements!(contact, contact_slave_elements)
|
||||
|
||||
solver = Solver(Nonlinear)
|
||||
push!(solver, upper, lower, bc_fixed, bc_sym_23, load, contact)
|
||||
solver()
|
||||
slaves = get_slave_elements(contact)
|
||||
node_ids, la = get_nodal_vector(slaves, "lambda", 0.0)
|
||||
node_ids, n = get_nodal_vector(slaves, "normal", 0.0)
|
||||
|
||||
node_ids, la = get_nodal_vector(contact_slave_elements, "lambda", 0.0)
|
||||
node_ids, n = get_nodal_vector(contact_slave_elements, "normal", 0.0)
|
||||
pres = [dot(ni, lai) for (ni, lai) in zip(n, la)]
|
||||
#@test isapprox(maximum(pres), 4060.010799583303)
|
||||
# 12 % error in maximum pressure
|
||||
@@ -146,7 +64,7 @@ end
|
||||
Rt = 0.0
|
||||
Q = [0.0 -1.0; 1.0 0.0]
|
||||
time = 0.0
|
||||
for sel in slaves
|
||||
for sel in contact_slave_elements
|
||||
for ip in get_integration_points(sel)
|
||||
w = ip.weight*sel(ip, time, Val{:detJ})
|
||||
n = sel("normal", ip, time)
|
||||
@@ -165,7 +83,7 @@ end
|
||||
end
|
||||
|
||||
function get_model()
|
||||
meshfile = joinpath(datadir, "block_2d.med")
|
||||
meshfile = joinpath(testdir, datadir, "block_2d.med")
|
||||
mesh = aster_read_mesh(meshfile)
|
||||
println(mesh.nodes[1])
|
||||
|
||||
@@ -188,13 +106,13 @@ function get_model()
|
||||
bc3 = Problem(mesh, Dirichlet, "UPPER_LEFT", 2, "displacement")
|
||||
update!(bc3, "displacement 1", 0.0)
|
||||
|
||||
interface = Problem(Contact, "interface", 2, "displacement")
|
||||
interface = Problem(Contact2D, "interface", 2, "displacement")
|
||||
interface.properties.rotate_normals = true
|
||||
interface_slave_elements = create_elements(mesh, "LOWER_TOP")
|
||||
interface_master_elements = create_elements(mesh, "UPPER_BOTTOM")
|
||||
update!(interface_slave_elements, "master elements", interface_master_elements)
|
||||
interface.elements = [interface_master_elements; interface_slave_elements]
|
||||
interface.properties.rotate_normals = true
|
||||
|
||||
add_master_elements!(interface, interface_master_elements)
|
||||
add_slave_elements!(interface, interface_slave_elements)
|
||||
|
||||
# in LOWER_LEFT we have node belonging also to contact interface
|
||||
# let's remove it from dirichlet bc
|
||||
create_node_set_from_element_set!(mesh, "LOWER_LEFT")
|
||||
@@ -219,7 +137,7 @@ end
|
||||
|
||||
node_ids, displacement = get_nodal_vector(interface.elements, "displacement", 0.0)
|
||||
node_ids, geometry = get_nodal_vector(interface.elements, "geometry", 0.0)
|
||||
node_ids, lambda = get_nodal_vector(get_slave_elements(interface), "lambda", 0.0)
|
||||
node_ids, lambda = get_nodal_vector(interface.elements, "lambda", 0.0)
|
||||
u2 = [u[2] for u in displacement]
|
||||
f2 = [f[2] for f in lambda]
|
||||
maxabsu2 = maximum(abs.(u2))
|
||||
@@ -242,7 +160,7 @@ end
|
||||
|
||||
node_ids, displacement = get_nodal_vector(interface.elements, "displacement", 0.0)
|
||||
node_ids, geometry = get_nodal_vector(interface.elements, "geometry", 0.0)
|
||||
node_ids, lambda = get_nodal_vector(get_slave_elements(interface), "lambda", 0.0)
|
||||
node_ids, lambda = get_nodal_vector(interface.elements, "lambda", 0.0)
|
||||
u2 = [u[2] for u in displacement]
|
||||
f2 = [f[2] for f in lambda]
|
||||
maxabsu2 = maximum(abs.(u2))
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
# This file is a part of JuliaFEM.
|
||||
# License is MIT: see https://github.com/JuliaFEM/JuliaFEM.jl/blob/master/LICENSE.md
|
||||
|
||||
using JuliaFEM
|
||||
using JuliaFEM.Preprocess
|
||||
using JuliaFEM.Postprocess
|
||||
using JuliaFEM.Testing
|
||||
|
||||
pkgdir = Pkg.dir("JuliaFEM")
|
||||
datadir = joinpath(pkgdir, "test", first(splitext(basename(@__FILE__))))
|
||||
|
||||
# from fenet d3613 advanced finite element contact benchmarks
|
||||
# a = 6.21 mm, pmax = 3585 MPa
|
||||
# this is a very sparse mesh and for that reason pmax is not very accurate
|
||||
# (only 6 elements in -20 .. 20 mm contact zone, 3 elements in contact
|
||||
|
||||
meshfile = joinpath(datadir, "hertz_2d_full.med")
|
||||
mesh = aster_read_mesh(meshfile)
|
||||
|
||||
upper = Problem(Elasticity, "CYLINDER", 2)
|
||||
upper.properties.formulation = :plane_strain
|
||||
upper.elements = create_elements(mesh, "CYLINDER")
|
||||
update!(upper, "youngs modulus", 70.0e3)
|
||||
update!(upper, "poissons ratio", 0.3)
|
||||
|
||||
lower = Problem(Elasticity, "BLOCK", 2)
|
||||
lower.properties.formulation = :plane_strain
|
||||
lower.elements = create_elements(mesh, "BLOCK")
|
||||
update!(lower, "youngs modulus", 210.0e3)
|
||||
update!(lower, "poissons ratio", 0.3)
|
||||
|
||||
# support block to ground
|
||||
bc_fixed = Problem(Dirichlet, "fixed", 2, "displacement")
|
||||
bc_fixed.elements = create_elements(mesh, "FIXED")
|
||||
update!(bc_fixed, "displacement 2", 0.0)
|
||||
|
||||
# symmetry line
|
||||
bc_sym_23 = Problem(Dirichlet, "symmetry line 23", 2, "displacement")
|
||||
bc_sym_23.elements = create_elements(mesh, "SYM23")
|
||||
update!(bc_sym_23, "displacement 1", 0.0)
|
||||
|
||||
nid = find_nearest_node(mesh, [0.0, 100.0])
|
||||
#load = Problem(Dirichlet, "load", 2, "displacement")
|
||||
load = Problem(Elasticity, "point load", 2)
|
||||
load.properties.formulation = :plane_strain
|
||||
load.elements = [Element(Poi1, [nid])]
|
||||
#update!(load.elements, "displacement 2", -10.0)
|
||||
update!(load, "displacement traction force 2", -35.0e3)
|
||||
|
||||
contact = Problem(Contact2D, "contact between block and cylinder", 2, "displacement")
|
||||
contact.properties.rotate_normals = true
|
||||
contact_slave_elements = create_elements(mesh, "CYLINDER_TO_BLOCK")
|
||||
contact_master_elements = create_elements(mesh, "BLOCK_TO_CYLINDER")
|
||||
add_master_elements!(contact, contact_master_elements)
|
||||
add_slave_elements!(contact, contact_slave_elements)
|
||||
|
||||
solver = Solver(Nonlinear)
|
||||
push!(solver, upper, lower, bc_fixed, bc_sym_23, load, contact)
|
||||
solver()
|
||||
|
||||
node_ids, la = get_nodal_vector(contact_slave_elements, "lambda", 0.0)
|
||||
node_ids, n = get_nodal_vector(contact_slave_elements, "normal", 0.0)
|
||||
pres = [dot(ni, lai) for (ni, lai) in zip(n, la)]
|
||||
#@test isapprox(maximum(pres), 4060.010799583303)
|
||||
# 12 % error in maximum pressure
|
||||
# integrate pressure in normal and tangential direction
|
||||
Rn = 0.0
|
||||
Rt = 0.0
|
||||
Q = [0.0 -1.0; 1.0 0.0]
|
||||
time = 0.0
|
||||
for sel in contact_slave_elements
|
||||
for ip in get_integration_points(sel)
|
||||
w = ip.weight*sel(ip, time, Val{:detJ})
|
||||
n = sel("normal", ip, time)
|
||||
t = Q'*n
|
||||
la = sel("lambda", ip, time)
|
||||
Rn += w*dot(n, la)
|
||||
Rt += w*dot(t, la)
|
||||
end
|
||||
end
|
||||
info("2d hertz: Rn = $Rn, Rt = $Rt")
|
||||
info("2d hertz: maximum pressure pmax = ", maximum(pres))
|
||||
@test isapprox(maximum(pres), 3585.0; rtol = 0.13)
|
||||
# under 0.15 % error in resultant force
|
||||
@test isapprox(Rn, 35.0e3; rtol=0.020)
|
||||
@test isapprox(Rt, 0.0; atol=200.0)
|
||||
Binary file not shown.
Reference in New Issue
Block a user