Contact algorithms testing & develpoment (#95)

* contact 3d patch test, standard lagrange, small sliding, linear tet4 elements

* tet4 dual basis contact patch test pass

* contact 3d patch test, standard lagrange, small sliding, linear tet4 elements

* tet4 dual basis contact patch test pass

* Patch test for linear elements standard lagrange / dual lagrange pass now

* Patch test for quadratic contact surfaces for standard + dual basis pass

* refactoring

* renamed files

* Improvements to preprocess scripts

* convert several elements to node sets in one command

* possibility to find particular node from mesh filtered by node set

* 2d small sliding contact patch test, linear elements

* Added backward compatibility

* 2d contact algorithms pass patch tests

* test data for 2d contacts

* no common models in different tests. testing generalized alpha stabilization

* Preprocess tests

* moved tests from test_preprocess_aster_reader.jl to test_preprocess.jl

* generalized-alpha time integration, alpha=0.0 by default

* Improvements to logging

* JuliaFEM.jl: can set environment variable to one of logging levels: OFF, CRITICAL, ERROR, WARNING, INFO, DEBUG

* problems_contact_2d_autodiff.jl: do not loop over nodes if logging level != DEBUG
This commit is contained in:
Jukka Aho
2017-03-02 08:43:58 +02:00
committed by Tero Frondelius
parent 28e09f0305
commit a0d18568ce
24 changed files with 3368 additions and 803 deletions
+1 -12
View File
@@ -17,8 +17,7 @@ using Logging
Logging.configure(level=INFO)
if haskey(ENV, "JULIAFEM_LOGLEVEL")
ENV["JULIAFEM_LOGLEVEL"] == "INFO" && Logging.configure(level=INFO)
ENV["JULIAFEM_LOGLEVEL"] == "DEBUG" && Logging.configure(level=DEBUG)
Logging.configure(level=LogLevel(ENV["JULIAFEM_LOGLEVEL"]))
end
export info, debug
@@ -141,16 +140,6 @@ export aster_create_elements, parse_aster_med_file, is_aster_mail_keyword,
aster_read_mesh_names, aster_read_node_sets, aster_read_nodes, RMEDFile
end
function get_mesh(mesh_name::AbstractString, args...; kwargs...)
return get_mesh(Val{Symbol(mesh_name)}, args...; kwargs...)
end
function get_model(model_name::AbstractString, args...; kwargs...)
return get_model(Val{Symbol(model_name)}, args...; kwargs...)
end
export get_mesh, get_model
module Postprocess
include("postprocess_utils.jl")
+15 -2
View File
@@ -25,6 +25,10 @@ type Contact <: BoundaryProblem
remove_nodes :: Vector{Int}
always_in_contact :: Bool
update_contact_pairing :: Bool
iteration :: Int
contact_state_in_first_iteration :: Symbol
alpha :: Float64
drop_tolerance :: Float64
store_fields :: Vector{AbstractString}
end
@@ -47,6 +51,10 @@ function Contact()
[], # remove these nodes always from set
false, # mainly for debugging, do not remove inactive nodes
true, # update contact pairing on each loop
1, # iteration counter
:AUTO, # contact state in first iteration, AUTO, INACTIVE, ACTIVE
0.0, # alpha basis transform parameter
1.0e-9, # drop tolerance
default_fields)
end
@@ -55,24 +63,29 @@ function get_unknown_field_name(problem::Problem{Contact})
end
function get_formulation_type(problem::Problem{Contact})
#=
if problem.properties.use_forwarddiff
return :forwarddiff
else
return :incremental
end
=#
return :incremental
#return :forwarddiff
end
function assemble!(problem::Problem{Contact}, time::Real)
if problem.properties.dimension == -1
problem.properties.dimension = dim = size(first(problem.elements), 1)
info("assuming dimension of mesh tie surface is $dim")
info("if this is wrong set is manually using problem.properties.dimension")
debug("assuming dimension of mesh tie surface is $dim")
debug("if this is wrong set is manually using problem.properties.dimension")
end
dimension = Val{problem.properties.dimension}
finite_sliding = Val{problem.properties.finite_sliding}
friction = Val{problem.properties.friction}
use_forwarddiff = Val{problem.properties.use_forwarddiff}
assemble!(problem, time, dimension, finite_sliding, friction, use_forwarddiff)
problem.properties.iteration += 1
end
typealias ContactElements2D Union{Seg2}
+123 -66
View File
@@ -1,14 +1,62 @@
# 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}}; debug=false)
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)
@@ -33,51 +81,22 @@ function assemble!(problem::Problem{Contact}, time::Float64,
n1 = slave_element("normal", time)
t1 = slave_element("tangent", time)
x1 = X1 + u1
Q1_ = [n1[1] t1[1]]
Q2_ = [n1[2] t1[2]]
Z = zeros(2, 2)
Q2 = [Q1_ Z; Z Q2_]
contact_area = 0.0
contact_error = 0.0
Q2 = create_rotation_matrix(slave_element, time)
if "element area" in props.store_fields
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
update!(slave_element, "element area", time => element_area)
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
# 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
if norm(mean(X1) - X2[1]) / norm(X1[2] - X1[1]) > props.distval
continue
end
if norm(mean(X1) - X2[2]) / norm(X1[2] - X1[1]) > props.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])
isapprox(l, 0.0) && continue # no contribution in this master element
# 3.2. bi-orthogonal basis
Ae = eye(nsl)
if props.dual_basis
De = zeros(nsl, nsl)
Me = zeros(nsl, nsl)
Ae = zeros(nsl, nsl)
if props.dual_basis
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
@@ -88,14 +107,21 @@ function assemble!(problem::Problem{Contact}, time::Float64,
Me += w*N1*N1'
end
Ae = De*inv(Me)
else
Ae = eye(nsl)
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 = X2 + u2
# 3.3. loop integration points of one integration segment and calculate
# local mortar matrices
fill!(De, 0.0)
fill!(Me, 0.0)
De = zeros(nsl, nsl)
Me = zeros(nsl, nsl)
Ne = zeros(nsl, 2*nsl)
Te = zeros(nsl, 2*nsl)
He = zeros(nsl, 2*nsl)
@@ -117,7 +143,7 @@ function assemble!(problem::Problem{Contact}, time::Float64,
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 = N2*X2
X_m = N2*X2
u_s = N1*u1
u_m = N2*u2
@@ -182,14 +208,34 @@ function assemble!(problem::Problem{Contact}, time::Float64,
la = problem.assembly.la
ndofs = length(la)
# info("contact ndofs: $ndofs")
# info("Rn = $Rn")
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
@@ -204,7 +250,6 @@ function assemble!(problem::Problem{Contact}, time::Float64,
contact_pressure[j] = [0.0, 0.0]
end
# contact_pressure[j] = c[dofs]
complementarity_condition[j] = contact_pressure[j] - weighted_gap[j]
if complementarity_condition[j][1] < 0
is_inactive[j] = 1
@@ -216,11 +261,24 @@ function assemble!(problem::Problem{Contact}, time::Float64,
is_active[j] = 1
is_slip[j] = 1
is_stick[j] = 0
# _c1 = complementarity_condition[j][1]
# _c2 = c[dofs]
# _c3 = contact_pressure[j][1]
# _c4 = g[dofs]
# info("active $j: c1 = $_c1, c2 = $_c2, c3 = $_c3, c4 = $_c4")
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
@@ -246,34 +304,33 @@ function assemble!(problem::Problem{Contact}, time::Float64,
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
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
# solve variational inequality
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)
info("$j is in active/slip, removing tangential constraint $(dofs[2])")
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
# info("$j is inactive, removing dofs $dofs")
debug("$j is inactive, removing dofs $dofs")
C1[dofs,:] = 0.0
C2[dofs,:] = 0.0
D[dofs,:] = 0.0
+116 -37
View File
@@ -163,6 +163,38 @@ function assemble!(problem::Problem{Contact}, time::Float64,
n1 = Field(Vector[normals[:,i] for i in slave_element_nodes])
nnodes = size(slave_element, 2)
# construct dual basis
De = zeros(nnodes, nnodes)
Me = zeros(nnodes, nnodes)
for master_element in slave_element("master elements", time)
master_element_nodes = get_connectivity(master_element)
X2 = master_element("geometry", time)
u2 = Field(Vector[u[:,i] for i in master_element_nodes])
x2 = X2 + u2
# calculate segmentation: we care only about endpoints
xi1a = project_from_master_to_slave(slave_element, x1, n1, x2[1])
xi1b = project_from_master_to_slave(slave_element, x1, n1, x2[2])
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
for ip in get_integration_points(slave_element, 3)
# jacobian of slave element in deformed state
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 = get_basis(slave_element, xi_s, time)
De += w*diagm(vec(N1))
Me += w*N1'*N1
end
end
Ae = De*inv(Me)
# 3. loop all master elements
for master_element in slave_element("master elements", time)
@@ -183,21 +215,6 @@ function assemble!(problem::Problem{Contact}, time::Float64,
l = 1/2*abs(xi1[2]-xi1[1])
isapprox(l, 0.0) && continue # no contribution in this master element
De = zeros(nnodes, nnodes)
Me = zeros(nnodes, nnodes)
for ip in get_integration_points(slave_element, 3)
# jacobian of slave element in deformed state
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 = get_basis(slave_element, xi_s, time)
De += w*diagm(vec(N1))
Me += w*N1'*N1
end
Ae = De*inv(Me)
slave_dofs = get_gdofs(slave_element, field_dim)
master_dofs = get_gdofs(master_element, field_dim)
@@ -237,27 +254,67 @@ function assemble!(problem::Problem{Contact}, time::Float64,
# at this point we have calculated contact force fc and gap for all slave elements.
# next task is to find out are they in contact or not and remove inactive nodes
#nzgap = sort(nonzeros(sparse(ForwardDiff.get_value(gap))))
#info("gap: $nzgap")
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 = ForwardDiff.value(mean([gap[1, j] for j in S]))
std_gap = ForwardDiff.value(std([gap[1, j] 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
for (i, j) in enumerate(sort(collect(S)))
# if j in props.always_inactive
# info("special node $j always inactive")
# C[:,j] = la[:,j]
# continue
# end
n = normals[:,j]
t = Q'*n
lan = dot(n, la[:,j])
lat = dot(t, la[:,j])
is_active = Dict{Int, Bool}()
condition = Dict()
if lan - gap[1, j] > 0
# info("set node $j active, normal direction = $(ForwardDiff.get_value(n)), tangent plane = $(ForwardDiff.get_value(t))")
for j in S
if j in props.always_in_contact
is_active[j] = true
continue
end
lan = dot(normals[:,j], la[:,j])
condition[j] = ForwardDiff.value(lan - gap[1, j])
is_active[j] = condition[j] > 0
end
if problem.properties.iteration == 1 && state == :ACTIVE
for j in S
is_active[j] = true
end
end
if problem.properties.iteration == 1 && state == :INACTIVE
for j in S
is_active[j] = false
end
end
if Logging._root.level == DEBUG
debug("Summary of nodes")
for j in sort(collect(keys(is_active)))
n = map(ForwardDiff.value, normals[:,j])
debug("$j, c=$(condition[j]), s=$(is_active[j]), n=$n")
end
end
for j in S
if is_active[j]
n = normals[:,j]
t = Q'*n
lan = dot(n, la[:,j])
lat = dot(t, la[:,j])
C[1,j] += gap[1, j]
C[2,j] += lat
else
C[:,j] = la[:,j]
end
end
return vec([fc C])
@@ -269,6 +326,7 @@ function assemble!(problem::Problem{Contact}, time::Float64,
if length(x) == 0
error("2d autodiff contact problem: initialize problem.assembly.u & la before solution")
end
A = ForwardDiff.jacobian(calculate_interface, x)
b = calculate_interface(x)
A = sparse(A)
@@ -278,19 +336,40 @@ function assemble!(problem::Problem{Contact}, time::Float64,
ndofs = round(Int, length(x)/2)
K = A[1:ndofs,1:ndofs]
C1 = transpose(A[1:ndofs,ndofs+1:end])
C1 = A[1:ndofs,ndofs+1:end]
C2 = A[ndofs+1:end,1:ndofs]
D = A[ndofs+1:end,ndofs+1:end]
f = -b[1:ndofs]
g = -b[ndofs+1:end]
f += C1*problem.assembly.la
g += D*problem.assembly.la
empty!(problem.assembly)
add!(problem.assembly.K, K)
add!(problem.assembly.C1, C1)
add!(problem.assembly.C2, C2)
add!(problem.assembly.D, D)
add!(problem.assembly.f, f)
add!(problem.assembly.g, g)
#=
if !haskey(problem, "contact force")
problem.fields["contact force"] = Field(time => f)
else
update!(problem.fields["contact force"], time => f)
end
fc = problem.fields["contact force"]
if length(fc) > 1
# kick in generalized alpha rule for time integration
alpha = 0.5
info("Applying Generalized alpha time integration")
K = (1-alpha)*K
C1 = (1-alpha)*C1
f = alpha*fc[end-1].data
end
=#
problem.assembly.K = K
problem.assembly.C1 = transpose(C1)
problem.assembly.C2 = C2
problem.assembly.D = D
problem.assembly.f = sparse(f)
problem.assembly.g = sparse(g)
end
+565 -193
View File
@@ -11,6 +11,459 @@ function create_orthogonal_basis(n)
return t1, t2
end
""" Create rotation matrix Q for element nodes rotating quantities to nt coordinaet system. """
function create_rotation_matrix(element::Element{Tri3}, time::Float64)
n = element("normal", time)
t11, t21 = create_orthogonal_basis(n[1])
t12, t22 = create_orthogonal_basis(n[2])
t13, t23 = create_orthogonal_basis(n[3])
Q1_ = [n[1] t11 t21]
Q2_ = [n[2] t12 t22]
Q3_ = [n[3] t13 t23]
Z = zeros(3, 3)
Q = [
Q1_ Z Z
Z Q2_ Z
Z Z Q3_]
return Q
end
function create_rotation_matrix(element::Element{Quad4}, time::Float64)
n = element("normal", time)
t11, t21 = create_orthogonal_basis(n[1])
t12, t22 = create_orthogonal_basis(n[2])
t13, t23 = create_orthogonal_basis(n[3])
t14, t24 = create_orthogonal_basis(n[4])
Q1_ = [n[1] t11 t21]
Q2_ = [n[2] t12 t22]
Q3_ = [n[3] t13 t23]
Q4_ = [n[4] t14 t24]
Z = zeros(3, 3)
Q = [
Q1_ Z Z Z
Z Q2_ Z Z
Z Z Q3_ Z
Z Z Z Q4_]
return Q
end
function create_rotation_matrix(element::Element{Tri6}, time::Float64)
n = element("normal", time)
t11, t21 = create_orthogonal_basis(n[1])
t12, t22 = create_orthogonal_basis(n[2])
t13, t23 = create_orthogonal_basis(n[3])
t14, t24 = create_orthogonal_basis(n[4])
t15, t25 = create_orthogonal_basis(n[5])
t16, t26 = create_orthogonal_basis(n[6])
Q1_ = [n[1] t11 t21]
Q2_ = [n[2] t12 t22]
Q3_ = [n[3] t13 t23]
Q4_ = [n[4] t14 t24]
Q5_ = [n[5] t15 t25]
Q6_ = [n[6] t16 t26]
Z = zeros(3, 3)
Q = [
Q1_ Z Z Z Z Z
Z Q2_ Z Z Z Z
Z Z Q3_ Z Z Z
Z Z Z Q4_ Z Z
Z Z Z Z Q5_ Z
Z Z Z Z Z Q6_]
return Q
end
""" Create a contact segmentation between one slave element and list of master elements.
Returns
-------
Vector with tuples: (master_element, polygon_clip_vertices, polygon_clip_centroid, polygon_clip_area)
"""
function create_contact_segmentation(slave_element, master_elements, x0, n0, time::Float64; deformed=false)
result = []
x1 = slave_element("geometry", time)
if deformed
x1 += slave_element("displacement", time)
end
S = Vector[project_vertex_to_auxiliary_plane(p, x0, n0) for p in x1]
for master_element in master_elements
x2 = master_element("geometry", time)
if deformed
x2 += master_element("displacement", time)
end
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)
N_P = length(P)
P_area = sum([norm(1/2*cross(P[i]-P[1], P[mod(i,N_P)+1]-P[1])) for i=2:N_P])
if isapprox(P_area, 0.0)
error("Polygon P has zero area")
end
C0 = calculate_centroid(P)
push!(result, (master_element, P, C0, P_area))
end
return result
end
"Assemble linear surface element to contact problem. """
function assemble!(problem::Problem{Contact}, slave_element::Element{Tri3}, time::Float64)
props = problem.properties
field_dim = get_unknown_field_dimension(problem)
nsl = length(slave_element)
X1 = slave_element("geometry", time)
u1 = slave_element("displacement", time)
x1 = X1 + u1
n1 = slave_element("normal", time)
la = slave_element("reaction force", time)
Q3 = create_rotation_matrix(slave_element, time)
# project slave nodes to auxiliary plane (x0, Q)
xi = mean(get_reference_coordinates(slave_element))
N = vec(get_basis(slave_element, xi, time))
x0 = N*X1
n0 = N*n1
# create contact segmentation
segmentation = create_contact_segmentation(slave_element, slave_element("master elements", time), x0, n0, time)
if length(segmentation) == 0 # no overlapping surface in slave and maters
return
end
Ae = eye(nsl)
if problem.properties.dual_basis # construct dual basis
De = zeros(nsl, nsl)
Me = zeros(nsl, nsl)
# loop all polygons
for (master_element, P, C0, P_area) in segmentation
# loop integration cells
for cell in get_cells(P, C0)
virtual_element = Element(Tri3, Int[])
update!(virtual_element, "geometry", cell)
for ip in get_integration_points(virtual_element, 3)
detJ = virtual_element(ip, time, Val{:detJ})
w = ip.weight*detJ
x_gauss = virtual_element("geometry", ip, time)
xi_s, alpha = project_vertex_to_surface(x_gauss, x0, n0, slave_element, X1, time)
N1 = slave_element(xi_s, time)
De += w*diagm(vec(N1))
Me += w*N1'*N1
end # integration points done
end # integration cells done
end # master elements done
Ae = De*inv(Me)
debug("Dual basis coeffients = $Ae")
end
# loop all polygons
for (master_element, P, C0, P_area) in segmentation
nm = length(master_element)
X2 = master_element("geometry", time)
u2 = master_element("displacement", time)
x2 = X2 + u2
De = zeros(nsl, nsl)
Me = zeros(nsl, nm)
ce = zeros(field_dim*nsl)
ge = zeros(field_dim*nsl)
# loop integration cells
for cell in get_cells(P, C0)
virtual_element = Element(Tri3, Int[])
update!(virtual_element, "geometry", cell)
# 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)
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))
Phi = Ae*N1
De += w*Phi*N1'
Me += w*Phi*N2'
x_s = N1*(X1+u1)
x_m = N2*(X2+u2)
ge += w*vec((x_m-x_s)*Phi')
end # integration points done
end # integration cells done
# 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
end
""" Assemble quadratic surface element to contact problem. """
function assemble!(problem::Problem{Contact}, slave_element::Element{Tri6}, time::Float64)
props = problem.properties
field_dim = get_unknown_field_dimension(problem)
alp = props.alpha
if alp != 0.0
T = [
1.0 0.0 0.0 0.0 0.0 0.0
0.0 1.0 0.0 0.0 0.0 0.0
0.0 0.0 1.0 0.0 0.0 0.0
alp alp 0.0 1.0-2*alp 0.0 0.0
0.0 alp alp 0.0 1.0-2*alp 0.0
alp 0.0 alp 0.0 0.0 1.0-2*alp
]
else
T = eye(6)
end
nsl = length(slave_element)
Xs = slave_element("geometry", time)
n1 = slave_element("normal", time)
Q3 = create_rotation_matrix(slave_element, time)
Ae = eye(nsl)
if problem.properties.dual_basis # construct dual basis
nsl = length(slave_element)
De = zeros(nsl, nsl)
Me = zeros(nsl, nsl)
for sub_slave_element in split_quadratic_element(slave_element, time)
slave_element_nodes = get_connectivity(sub_slave_element)
nsl = length(sub_slave_element)
X1 = sub_slave_element("geometry", time)
#u1 = sub_slave_element("displacement", time)
#x1 = X1 + u1
n1 = sub_slave_element("normal", time)
#la = sub_slave_element("reaction force", time)
# create auxiliary plane
xi = mean(get_reference_coordinates(sub_slave_element))
N = vec(get_basis(sub_slave_element, xi, time))
x0 = N*X1
n0 = N*n1
# project slave nodes to auxiliary plane
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)
Xm = master_element("geometry", time)
if norm(mean(Xs) - mean(Xm)) > problem.properties.distval
continue
end
# split master element to linear sub-elements and loop
for sub_master_element in split_quadratic_element(master_element, time)
master_element_nodes = get_connectivity(sub_master_element)
nm = length(sub_master_element)
X2 = sub_master_element("geometry", time)
#u2 = sub_master_element("displacement", time)
#x2 = X2 + u2
# 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)
N_P = length(P)
P_area = sum([norm(1/2*cross(P[i]-P[1], P[mod(i,N_P)+1]-P[1])) for i=2:N_P])
if isapprox(P_area, 0.0)
error("Polygon P has zero area")
end
C0 = calculate_centroid(P)
# 4. loop integration cells
for cell in get_cells(P, C0)
virtual_element = Element(Tri3, Int[])
update!(virtual_element, "geometry", cell)
for ip in get_integration_points(virtual_element, 3)
detJ = virtual_element(ip, time, Val{:detJ})
w = ip.weight*detJ
x_gauss = virtual_element("geometry", ip, time)
xi_s, alpha = project_vertex_to_surface(x_gauss, x0, n0, slave_element, Xs, time)
N1 = vec(slave_element(xi_s, time)*T)
De += w*diagm(N1)
Me += w*N1*N1'
end # integration points done
end # integration cells done
end # sub master elements done
end # master elements done
end # sub slave elements done
Ae = De*inv(Me)
debug("Dual basis coeffients = $Ae")
end
# split slave element to linear sub-elements and loop
for sub_slave_element in split_quadratic_element(slave_element, time)
slave_element_nodes = get_connectivity(sub_slave_element)
nsl = length(sub_slave_element)
X1 = sub_slave_element("geometry", time)
n1 = sub_slave_element("normal", time)
# create auxiliary plane
xi = mean(get_reference_coordinates(sub_slave_element))
N = vec(get_basis(sub_slave_element, xi, time))
x0 = N*X1
n0 = N*n1
# project slave nodes to auxiliary plane
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)
Xm = master_element("geometry", time)
if norm(mean(Xs) - mean(Xm)) > problem.properties.distval
continue
end
# split master element to linear sub-elements and loop
for sub_master_element in split_quadratic_element(master_element, time)
master_element_nodes = get_connectivity(sub_master_element)
nm = length(master_element)
X2 = sub_master_element("geometry", time)
#u2 = master_element("displacement", time)
#x2 = X2 + u2
# 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)
N_P = length(P)
P_area = sum([norm(1/2*cross(P[i]-P[1], P[mod(i,N_P)+1]-P[1])) for i=2:N_P])
if isapprox(P_area, 0.0)
error("Polygon P has zero area")
end
C0 = calculate_centroid(P)
# integration is done in quadratic elements
nsl = length(slave_element)
nm = length(master_element)
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, Int[])
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)
xi_s, alpha = project_vertex_to_surface(x_gauss, x0, n0, slave_element, Xs, time)
xi_m, alpha = project_vertex_to_surface(x_gauss, x0, n0, master_element, Xm, time)
detJ = virtual_element(ip, time, Val{:detJ})
w = ip.weight*detJ
# add contributions
N1 = vec(get_basis(slave_element, xi_s, time)*T)
N2 = vec(get_basis(master_element, xi_m, time))
Phi = Ae*N1
De += w*Phi*N1'
Me += w*Phi*N2'
us = slave_element("displacement", time)
um = master_element("displacement", time)
xs = N1*(Xs+us)
xm = N2*(Xs+um)
ge += w*vec((xm-xs)*Phi')
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 # sub master elements done
end # master elements done
end # sub slave elements done
end
"""
Frictionless 3d small sliding contact.
@@ -21,9 +474,7 @@ 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)
function assemble!(problem::Problem{Contact}, time::Float64, ::Type{Val{2}}, ::Type{Val{false}}, ::Type{Val{false}}, ::Type{Val{false}})
props = problem.properties
field_dim = get_unknown_field_dimension(problem)
@@ -36,153 +487,8 @@ function assemble!(problem::Problem{Contact}, time::Float64,
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 = mean(get_reference_coordinates(slave_element))
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, Int[])
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
for slave_element in slave_elements
assemble!(problem, slave_element, time)
end # slave elements done, contact virtual work ready
S = sort(collect(keys(normals))) # slave element nodes
@@ -196,13 +502,87 @@ function assemble!(problem::Problem{Contact}, time::Float64,
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)
maxdim = maximum(size(C1))
if problem.properties.alpha != 0.0
debug("mortar_3d: size C1 = ", size(C1), " max dim = $maxdim")
debug("alpha != 0.0, applying transformation D = Dh*T^-1")
alp = problem.properties.alpha
Te = [
1.0 0.0 0.0 0.0 0.0 0.0
0.0 1.0 0.0 0.0 0.0 0.0
0.0 0.0 1.0 0.0 0.0 0.0
alp alp 0.0 1.0-2*alp 0.0 0.0
0.0 alp alp 0.0 1.0-2*alp 0.0
alp 0.0 alp 0.0 0.0 1.0-2*alp
]
invTe = [
1.0 0.0 0.0 0.0 0.0 0.0
0.0 1.0 0.0 0.0 0.0 0.0
0.0 0.0 1.0 0.0 0.0 0.0
-alp/(1-2*alp) -alp/(1-2*alp) 0.0 1/(1-2*alp) 0.0 0.0
0.0 -alp/(1-2*alp) -alp/(1-2*alp) 0.0 1/(1-2*alp) 0.0
-alp/(1-2*alp) 0.0 -alp/(1-2*alp) 0.0 0.0 1/(1-2*alp)
]
# construct global transformation matrices T and invT
T = SparseMatrixCOO()
invT = SparseMatrixCOO()
for element in slave_elements
dofs = get_gdofs(problem, element)
for i=1:field_dim
ldofs = dofs[i:field_dim:end]
add!(T, ldofs, ldofs, Te)
add!(invT, ldofs, ldofs, invTe)
end
end
T = sparse(T, maxdim, maxdim, (a, b) -> b)
invT = sparse(invT, maxdim, maxdim, (a, b) -> b)
# fill diagonal
d = ones(size(T, 1))
d[get_nonzero_rows(T)] = 0.0
T += spdiagm(d)
invT += spdiagm(d)
#invT2 = sparse(inv(full(T)))
#info("invT == invT2? ", invT == invT2)
#maxabsdiff = maximum(abs(invT - invT2))
#info("max diff = $maxabsdiff")
C1 = C1*invT
C2 = C2*invT
end
tol = problem.properties.drop_tolerance
debug("Dropping small values from C1 & C2, tolerace = $tol")
SparseArrays.droptol!(C1, tol)
SparseArrays.droptol!(C2, tol)
for j in S
dofs = [3*(j-1)+1, 3*(j-1)+2, 3*(j-1)+3]
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 = [3*(j-1)+1, 3*(j-1)+2, 3*(j-1)+3]
@@ -218,7 +598,8 @@ function assemble!(problem::Problem{Contact}, time::Float64,
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
if complementarity_condition[j][1] < 0.0
is_inactive[j] = 1
is_active[j] = 0
is_slip[j] = 0
@@ -231,45 +612,49 @@ function assemble!(problem::Problem{Contact}, time::Float64,
end
end
if "weighted gap" in props.store_fields
update!(slave_elements, "weighted gap", time => weighted_gap)
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 "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)
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
#=
info("# | active | inactive | stick | slip | gap | pres | comp")
info("# | active | 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))"
str1 = "$j | $(is_active[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))"
info(str1 * str2)
end
=#
# solve variational inequality
# 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
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
# 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")
debug("$j is in active/slip, removing tangential constraints $tdofs")
C2[tdofs,:] = 0.0
g[tdofs] = 0.0
normal = normals[j]
@@ -279,22 +664,9 @@ function assemble!(problem::Problem{Contact}, time::Float64,
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
+26 -3
View File
@@ -13,13 +13,15 @@ type Solver{S<:AbstractSolver}
initialized :: Bool
u :: Vector{Float64}
la :: Vector{Float64}
alpha :: Float64 # generalized alpha time integration coefficient
fields :: Dict{AbstractString, Field}
properties :: S
end
function Solver{S<:AbstractSolver}(::Type{S}, name="solver", properties...)
variant = S(properties...)
solver = Solver{S}(name, 0.0, [], [], 0, nothing, false, [], [], variant)
solver = Solver{S}(name, 0.0, [], [], 0, nothing, false, [], [], 0.0, Dict(), variant)
return solver
end
@@ -33,11 +35,11 @@ function get_problems(solver::Solver)
return solver.problems
end
function push!(solver::Solver, problem)
function push!(solver::Solver, problem::Problem)
push!(solver.problems, problem)
end
function getindex(solver::Solver, problem_name)
function getindex(solver::Solver, problem_name::String)
for problem in get_problems(solver)
if problem.name == problem_name
return problem
@@ -46,6 +48,10 @@ function getindex(solver::Solver, problem_name)
throw(KeyError(problem_name))
end
function haskey(solver::Solver, field_name::String)
return haskey(solver.fields, field_name)
end
# one-liner helpers to identify problem types
is_field_problem(problem) = false
@@ -312,6 +318,23 @@ function solve!(solver::Solver; empty_assemblies_before_solution=true, symmetric
end
gc()
end
if !haskey(solver, "fint")
solver.fields["fint"] = Field(time => f)
else
update!(solver.fields["fint"], time => f)
end
fint = solver.fields["fint"]
if length(fint) > 1
# kick in generalized alpha rule for time integration
alpha = solver.alpha
debug("Using generalized-α time integration, α=$alpha")
K = (1-alpha)*K
C1 = (1-alpha)*C1
f = (1-alpha)*f + alpha*fint[end-1].data
end
ndofs = solver.ndofs
u = zeros(ndofs)
-148
View File
@@ -1,148 +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.Preprocess
using JuliaFEM.Postprocess
using JuliaFEM.Testing
import JuliaFEM: get_mesh, get_model
function get_mesh(::Type{Val{Symbol("curved 2d mesh model")}})
meshfile = Pkg.dir("JuliaFEM") * "/test/testdata/block_2d_curved.med"
mesh = aster_read_mesh(meshfile)
end
function get_model(::Type{Val{Symbol("curved 2d contact small sliding")}})
mesh = get_mesh("curved 2d mesh model")
upper = Problem(Elasticity, "upper", 2)
upper.properties.formulation = :plane_stress
upper.elements = create_elements(mesh, "UPPER")
update!(upper, "youngs modulus", 96.0)
update!(upper, "poissons ratio", 1/3)
lower = Problem(Elasticity, "lower", 2)
lower.properties.formulation = :plane_stress
lower.elements = create_elements(mesh, "LOWER")
update!(lower, "youngs modulus", 96.0)
update!(lower, "poissons ratio", 1/3)
bc_upper = Problem(Dirichlet, "upper boundary", 2, "displacement")
bc_upper.elements = create_elements(mesh, "UPPER_TOP")
update!(bc_upper, "displacement 1", 0.0)
update!(bc_upper, "displacement 2", -0.15)
bc_lower = Problem(Dirichlet, "lower boundary", 2, "displacement")
bc_lower.elements = create_elements(mesh, "LOWER_BOTTOM")
update!(bc_lower, "displacement 1", 0.0)
update!(bc_lower, "displacement 2", 0.0)
contact = Problem(Contact, "contact between upper and lower block", 2, "displacement")
contact.properties.dimension = 1
contact.properties.rotate_normals = true
contact_slave_elements = create_elements(mesh, "LOWER_TOP")
contact_master_elements = create_elements(mesh, "UPPER_BOTTOM")
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_upper, bc_lower, contact)
return solver
end
@testset "test all nodes in contact" begin
# FIXME: needs verification of some other fem software
solver = get_model("curved 2d contact small sliding")
solver()
upper, lower, bc_upper, bc_lower, contact = solver.problems
@test isapprox(norm(contact.assembly.u), 0.49563347601324315)
end
function get_model(::Type{Val{Symbol("hertz contact, full 2d model")}})
meshfile = Pkg.dir("JuliaFEM") * "/test/testdata/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_nodes(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)
return solver
end
@testset "test frictionless hertz contact, 2d plane strain" begin
# 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
# (only 6 elements in -20 .. 20 mm contact zone, 3 elements in contact
# instead integrate pressure in normal and tangential direction
solver = get_model("hertz contact, full 2d model")
upper, lower, bc_fixed, bc_sym_23, load, contact = solver.problems
solver()
slaves = get_slave_elements(contact)
node_ids, la = get_nodal_vector(slaves, "reaction force", 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
@test isapprox(maximum(pres), 3585.0; rtol = 12.0e-2)
# 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("reaction force", ip, time)
Rn += w*dot(n, -la)
Rt += w*dot(t, la)
end
end
# under 0.15 % error in reaction force
@test isapprox(Rn, 35.0e3; rtol=0.15e-2)
@test isapprox(Rt, 0.0; atol=10.0)
end
-60
View File
@@ -1,60 +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.Preprocess
using JuliaFEM.Postprocess
using JuliaFEM.Testing
@testset "3d upper side curved contact" begin
# TODO: accurate solution is not known, verify using another fem software
# however results look very meaningful and probably this is right.
meshfile = Pkg.dir("JuliaFEM") * "/test/testdata/block_3d_curved.med"
mesh = aster_read_mesh(meshfile)
upper = Problem(Elasticity, "upper", 3)
upper.elements = create_elements(mesh, "UPPER")
update!(upper, "youngs modulus", 96.0)
update!(upper, "poissons ratio", 1/3)
lower = Problem(Elasticity, "lower", 3)
lower.elements = create_elements(mesh, "LOWER")
update!(lower, "youngs modulus", 96.0)
update!(lower, "poissons ratio", 1/3)
bc_upper = Problem(Dirichlet, "upper boundary", 3, "displacement")
bc_upper.elements = create_elements(mesh, "UPPER_TOP")
update!(bc_upper, "displacement 1", 0.0)
update!(bc_upper, "displacement 2", 0.0)
update!(bc_upper, "displacement 3", -0.1)
bc_lower = Problem(Dirichlet, "lower boundary", 3, "displacement")
bc_lower.elements = create_elements(mesh, "LOWER_BOTTOM")
update!(bc_lower, "displacement 1", 0.0)
update!(bc_lower, "displacement 2", 0.0)
update!(bc_lower, "displacement 3", 0.0)
contact = Problem(Contact, "contact between upper and lower block", 3, "displacement")
contact_slave_elements = create_elements(mesh, "LOWER_TOP")
contact_master_elements = create_elements(mesh, "UPPER_BOTTOM")
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_upper, bc_lower, contact)
solver()
for element in get_slave_elements(contact)
normal = element("normal", [1/3, 1/3], solver.time)
@test isapprox(normal, [0.0, 0.0, 1.0])
pres = dot(normal, element("reaction force", [1/3, 1/3], solver.time))
info("pressure = $pres")
#info(element("displacement", [1/3, 1/3], solver.time))
end
normu = norm(contact.assembly.u)
info("displacement field norm = $normu")
@test isapprox(normu, 0.7417568870648232)
end
+3 -9
View File
@@ -5,7 +5,8 @@ using JuliaFEM
using JuliaFEM.Preprocess
using JuliaFEM.Testing
function JuliaFEM.get_mesh(::Type{Val{Symbol("two elements 1.0x0.5 with 0.1 gap in y direction")}})
function get_model()
mesh = Mesh()
add_node!(mesh, 1, [0.0, 0.0])
add_node!(mesh, 2, [1.0, 0.0])
@@ -27,12 +28,6 @@ function JuliaFEM.get_mesh(::Type{Val{Symbol("two elements 1.0x0.5 with 0.1 gap
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)
return mesh
end
function JuliaFEM.get_model(::Type{Val{Symbol("two element contact")}})
mesh = get_mesh("two elements 1.0x0.5 with 0.1 gap in y direction")
upper = Problem(Elasticity, "UPPER", 2)
upper.properties.formulation = :plane_stress
@@ -72,7 +67,7 @@ function JuliaFEM.get_model(::Type{Val{Symbol("two element contact")}})
end
@testset "test simple two element contact" begin
solver = get_model("two element contact")
solver = get_model()
solver()
contact = solver["LOWER_TO_UPPER"]
master = first(contact.elements)
@@ -82,5 +77,4 @@ end
info("u = $u, la = $la")
@test isapprox(u, [-0.2, -0.15])
@test isapprox(la, [0.0, -30.375])
# FIXME
end
+2 -108
View File
@@ -131,13 +131,9 @@ end
end
=#
function JuliaFEM.get_mesh(::Type{Val{Symbol("1x1 block splitted to upper and lower")}})
@testset "test mesh tie with splitted block and plane stress elasticity" begin
meshfile = Pkg.dir("JuliaFEM") * "/test/testdata/block_2d.med"
mesh = aster_read_mesh(meshfile)
end
function JuliaFEM.get_model(::Type{Val{Symbol("splitted block, plane stress elasticity and mesh tie")}})
mesh = get_mesh("1x1 block splitted to upper and lower")
upper = Problem(Elasticity, "upper", 2)
upper.properties.formulation = :plane_stress
@@ -175,113 +171,11 @@ function JuliaFEM.get_model(::Type{Val{Symbol("splitted block, plane stress elas
solver = Solver(Linear)
push!(solver, upper, lower, bc_upper, bc_lower, interface, bc_corner)
return solver
end
@testset "test mesh tie with splitted block and plane stress elasticity" begin
solver = get_model("splitted block, plane stress elasticity and mesh tie")
upper, lower, bc_upper, bc_lower, interface = solver.problems
solver()
slave_elements = get_slave_elements(interface)
node_ids, la = get_nodal_vector(slave_elements, "reaction force", 0.0)
for lai in la
@test isapprox(lai, [0.0, -10.0])
end
# FIXME
end
function JuliaFEM.get_mesh(::Type{Val{Symbol("curved 2d block splitted to upper and lower")}})
meshfile = Pkg.dir("JuliaFEM") * "/test/testdata/block_2d_curved.med"
mesh = aster_read_mesh(meshfile)
end
function JuliaFEM.get_model(::Type{Val{Symbol("mesh tie with curved 2d block")}};
dy=0.0, adjust=false, tolerance=0.0, rotate_normals=false, swap=false,
dual_basis=false, use_forwarddiff=false)
mesh = get_mesh("curved 2d block splitted to upper and lower")
upper = Problem(Elasticity, "upper", 2)
upper.properties.formulation = :plane_stress
upper.elements = create_elements(mesh, "UPPER")
update!(upper.elements, "youngs modulus", 96.0)
update!(upper.elements, "poissons ratio", 1/3)
lower = Problem(Elasticity, "lower", 2)
lower.properties.formulation = :plane_stress
lower.elements = create_elements(mesh, "LOWER")
update!(lower.elements, "youngs modulus", 96.0)
update!(lower.elements, "poissons ratio", 1/3)
bc_upper = Problem(Dirichlet, "upper boundary", 2, "displacement")
bc_upper.elements = create_elements(mesh, "UPPER_TOP")
update!(bc_upper.elements, "displacement 1", 0.0)
update!(bc_upper.elements, "displacement 2", dy)
bc_lower = Problem(Dirichlet, "lower boundary", 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)
interface = Problem(Mortar, "interface between upper and lower block", 2, "displacement")
interface_slave_elements = create_elements(mesh, "LOWER_TOP")
interface_master_elements = create_elements(mesh, "UPPER_BOTTOM")
if swap
interface_slave_elements, interface_master_elements = interface_master_elements, interface_slave_elements
end
update!(interface_slave_elements, "master elements", interface_master_elements)
interface.elements = [interface_master_elements; interface_slave_elements]
interface.properties.adjust = adjust
interface.properties.distval = tolerance
interface.properties.rotate_normals = rotate_normals
interface.properties.dual_basis = dual_basis
interface.properties.use_forwarddiff = use_forwarddiff
solver = Solver(Linear)
push!(solver, upper, lower, bc_upper, bc_lower, interface)
return solver
end
@testset "curved surface with adjust=true, standard lagrange, slave=lower surface, dy=0.0" begin
# TODO: analytical solution now known, verify using other fem software
solver = get_model("mesh tie with curved 2d block";
adjust=true, tolerance=10, dy=0.0, rotate_normals=true,
dual_basis=false)
solver()
interface = solver["interface between upper and lower block"]
@test isapprox(norm(interface.assembly.u), 0.11339715157447851)
end
@testset "curved surface with adjust=true, dual lagrange, slave=lower surface, dy=0.0" begin
# TODO: analytical solution now known, verify using other fem software
solver = get_model("mesh tie with curved 2d block";
adjust=true, tolerance=10, dy=0.0, rotate_normals=true,
dual_basis=true)
solver()
interface = solver["interface between upper and lower block"]
@test isapprox(norm(interface.assembly.u), 0.11660422877751599)
end
@testset "curved surface with adjust=true, standard lagrange, slave=lower surface, dy=-0.1" begin
# TODO: analytical solution now known, verify using other fem software
solver = get_model("mesh tie with curved 2d block";
adjust=true, tolerance=10, dy=-0.1, rotate_normals=true,
dual_basis=false)
solver()
interface = solver["interface between upper and lower block"]
@test isapprox(norm(interface.assembly.u), 0.34230262165505887)
end
@testset "curved surface, adjust=true, dual basis, slave=lower surface, dy=-0.1" begin
# TODO: analytical solution now known, verify using other fem software
solver = get_model("mesh tie with curved 2d block";
adjust=true, tolerance=10, dy=-0.1, rotate_normals=true,
dual_basis=true)
solver()
interface = solver["interface between upper and lower block"]
@test isapprox(norm(interface.assembly.u), 0.34318800698017704)
end
+3 -7
View File
@@ -6,17 +6,13 @@ using JuliaFEM.Preprocess
using JuliaFEM.Postprocess
using JuliaFEM.Testing
function JuliaFEM.get_mesh(::Type{Val{Symbol("curved 2d block splitted to upper and lower")}})
meshfile = Pkg.dir("JuliaFEM") * "/test/testdata/block_2d_curved.med"
mesh = aster_read_mesh(meshfile)
end
function JuliaFEM.get_model(::Type{Val{Symbol("mesh tie with curved 2d block")}};
function get_model(::Type{Val{Symbol("mesh tie with curved 2d block")}};
dy=0.0, adjust=false, tolerance=0.0, rotate_normals=false, swap=false,
dual_basis=false, use_forwarddiff=true, finite_strain=false,
geometric_stiffness=false)
mesh = get_mesh("curved 2d block splitted to upper and lower")
meshfile = Pkg.dir("JuliaFEM") * "/test/testdata/block_2d_curved.med"
mesh = aster_read_mesh(meshfile)
upper = Problem(Elasticity, "upper", 2)
upper.properties.formulation = :plane_stress
+91
View File
@@ -44,3 +44,94 @@ end
nid = find_nearest_node(mesh, [0.0, 0.5]; node_set="UPPER_BOTTOM")
@test first(nid) == 13
end
@testset "code aster / parse nodes" begin
section = """
N9 2.0 3.0 4.0
COOR_3D
N1 0.0 0.0 0.0
N2 1.0 0.0 0.0
N3 1.0 1.0 0.0
N4 0.0 1.0 0.0
N5 0.0 0.0 1.0
N6 1.0 0.0 1.0
N7 1.0 1.0 1.0
N8 0.0 1.0 1.0
FINSF
absdflasdf
N12 3.0 4.0 5.0 6.0
N13 3.0 4.0 5.0
"""
nodes = aster_parse_nodes(section)
@test nodes[1] == Float64[0.0, 0.0, 0.0]
@test nodes[8] == Float64[0.0, 1.0, 1.0]
@test length(nodes) == 8
end
@testset "test reading aster .med file" begin
meshfile = joinpath(datadir, "block_2d_1elem_quad4.med")
mesh = aster_read_mesh(meshfile)
@test length(mesh.element_sets) == 5
@test length(mesh.node_sets) == 4
@test length(mesh.elements) == 5
@test length(mesh.nodes) == 4
for elset in [:BLOCK, :TOP, :BOTTOM, :LEFT, :RIGHT]
@test haskey(mesh.element_sets, elset)
@test length(mesh.element_sets[elset]) == 1
end
for nset in [:TOP_LEFT, :TOP_RIGHT, :BOTTOM_LEFT, :BOTTOM_RIGHT]
@test haskey(mesh.node_sets, nset)
@test length(mesh.node_sets[nset]) == 1
end
end
@testset "test filter by element set" begin
mesh = aster_read_mesh(joinpath(datadir, "block_2d_1elem_quad4.med"))
mesh2 = filter_by_element_set(mesh, :BLOCK)
@test haskey(mesh2.element_sets, :BLOCK)
@test length(mesh2.elements) == 1
end
function calculate_volume(mesh_name, eltype)
mesh_file = joinpath(datadir, "primitives.med")
mesh = aster_read_mesh(mesh_file, mesh_name)
elements = create_elements(mesh; element_type=eltype)
V = 0.0
time = 0.0
for element in elements
for ip in get_integration_points(element)
detJ = element(ip, time, Val{:detJ})
detJ > 0 || warn("negative determinant for element $eltype !")
V += ip.weight*detJ
end
end
info("volume of $eltype is $V")
return V
end
@testset "calculate volume for 1 element models" begin
@test isapprox(calculate_volume("TRIANGLE_TRI3_1", :Tri3), 1/2)
@test isapprox(calculate_volume("TRIANGLE_TRI6_1", :Tri6), 1/2)
@test isapprox(calculate_volume("TRIANGLE_TRI7_1", :Tri7), 1/2)
@test isapprox(calculate_volume("SQUARE_QUAD4_1", :Quad4), 2^2)
@test isapprox(calculate_volume("SQUARE_QUAD8_1", :Quad8), 2^2)
@test isapprox(calculate_volume("SQUARE_QUAD9_1", :Quad9), 2^2)
@test isapprox(calculate_volume("TETRA_TET4_1", :Tet4), 1/6)
@test isapprox(calculate_volume("TETRA_TET10_1", :Tet10), 1/6)
# @test isapprox(calculate_volume("TETRA_TET14_1", :Tet14), 1/6)
@test isapprox(calculate_volume("CUBE_HEX8_1", :Hex8), 2^3)
@test isapprox(calculate_volume("CUBE_HEX20_1", :Hex20), 2^3)
@test isapprox(calculate_volume("CUBE_HEX27_1", :Hex27), 2^3)
@test isapprox(calculate_volume("WEDGE_WEDGE6_1", :Wedge6), 1)
# @test isapprox(calculate_volume("WEDGE_WEDGE15_1", :Wedge15, 1/2))
# @test isapprox(calculate_volume("PYRAMID_PYRAMID5_1", :Pyramid5, ?))
# @test isapprox(calculate_volume("PYRAMID_PYRAMID13_1", :Pyramid13, ?))
end
@testset "read nodal field from code aster result file" begin
rmedfile = joinpath(datadir, "rings.rmed")
rmed = JuliaFEM.Preprocess.RMEDFile(rmedfile)
temp = JuliaFEM.Preprocess.aster_read_data(rmed, "TEMP")
@test isapprox(temp[15], 1.0)
@test isapprox(temp[95], 2.0)
end
Binary file not shown.
Binary file not shown.
Binary file not shown.
-158
View File
@@ -1,158 +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.Preprocess
using JuliaFEM.Testing
@testset "read ascii mesh" begin
mesh = """
COOR_2D
N1 0.0 0.0
N2 1.0 0.0
N3 1.0 1.0
N4 0.0 1.0
FINSF
QUAD4
E1 N1 N2 N3 N4
FINSF
SEG2
E2 N3 N4
FINSF
GROUP_NO NOM=NALL
N1 N2
FINSF
GROUP_MA NOM=BODY1
E1 E2
FINSF
FIN
"""
# m = parse(mesh, Val{:CODE_ASTER_MAIL})
# @test m["nodes"]["N1"] == [0.0, 0.0]
# @test m["elements"]["E1"] == ["QUAD4", ["N1", "N2", "N3", "N4"]]
# @test m["elements"]["E2"] == ["SEG2", ["N3", "N4"]]
# @test m["elsets"]["BODY1"] == ["E1", "E2"]
# @test m["nsets"]["NALL"] == ["N1", "N2"]
end
@testset "parse nodes" begin
section = """
N9 2.0 3.0 4.0
COOR_3D
N1 0.0 0.0 0.0
N2 1.0 0.0 0.0
N3 1.0 1.0 0.0
N4 0.0 1.0 0.0
N5 0.0 0.0 1.0
N6 1.0 0.0 1.0
N7 1.0 1.0 1.0
N8 0.0 1.0 1.0
FINSF
absdflasdf
N12 3.0 4.0 5.0 6.0
N13 3.0 4.0 5.0
"""
nodes = aster_parse_nodes(section)
@test nodes[1] == Float64[0.0, 0.0, 0.0]
@test nodes[8] == Float64[0.0, 1.0, 1.0]
@test length(nodes) == 8
end
function JuliaFEM.get_mesh(::Type{Val{Symbol("block_2d_1elem_quad4")}})
fn = Pkg.dir("JuliaFEM") * "/test/testdata/block_2d_1elem_quad4.med"
mesh = aster_read_mesh(fn)
return mesh
end
@testset "test reading aster .med file" begin
mesh = get_mesh("block_2d_1elem_quad4")
#=
info("nodes")
for (k, v) in mesh.nodes
info("$k => $v")
end
info("node sets")
for (k, v) in mesh.node_sets
info("$k => $v")
end
info("elements")
for (k, v) in mesh.elements
info("$k => $v, type = $(mesh.element_types[k])")
end
info("element sets")
for (k, v) in mesh.element_sets
info("$k => $v")
end
=#
@test length(mesh.element_sets) == 5
@test length(mesh.node_sets) == 4
@test length(mesh.elements) == 5
@test length(mesh.nodes) == 4
for elset in [:BLOCK, :TOP, :BOTTOM, :LEFT, :RIGHT]
@test haskey(mesh.element_sets, elset)
@test length(mesh.element_sets[elset]) == 1
end
for nset in [:TOP_LEFT, :TOP_RIGHT, :BOTTOM_LEFT, :BOTTOM_RIGHT]
@test haskey(mesh.node_sets, nset)
@test length(mesh.node_sets[nset]) == 1
end
end
@testset "test filter by element set" begin
mesh = get_mesh("block_2d_1elem_quad4")
mesh2 = filter_by_element_set(mesh, :BLOCK)
@test haskey(mesh2.element_sets, :BLOCK)
@test length(mesh2.elements) == 1
end
function calculate_volume(mesh_name, eltype)
fn = Pkg.dir("JuliaFEM") * "/test/testdata/primitives.med"
mesh = aster_read_mesh(fn, mesh_name)
elements = create_elements(mesh; element_type=eltype)
V = 0.0
time = 0.0
for element in elements
for ip in get_integration_points(element)
detJ = element(ip, time, Val{:detJ})
detJ > 0 || warn("negative determinant for element $eltype !")
V += ip.weight*detJ
end
end
info("volume of $eltype is $V")
return V
end
@testset "calculate volume for 1 element models" begin
@test isapprox(calculate_volume("TRIANGLE_TRI3_1", :Tri3), 1/2)
@test isapprox(calculate_volume("TRIANGLE_TRI6_1", :Tri6), 1/2)
@test isapprox(calculate_volume("TRIANGLE_TRI7_1", :Tri7), 1/2)
@test isapprox(calculate_volume("SQUARE_QUAD4_1", :Quad4), 2^2)
@test isapprox(calculate_volume("SQUARE_QUAD8_1", :Quad8), 2^2)
@test isapprox(calculate_volume("SQUARE_QUAD9_1", :Quad9), 2^2)
@test isapprox(calculate_volume("TETRA_TET4_1", :Tet4), 1/6)
@test isapprox(calculate_volume("TETRA_TET10_1", :Tet10), 1/6)
# @test isapprox(calculate_volume("TETRA_TET14_1", :Tet14), 1/6)
@test isapprox(calculate_volume("CUBE_HEX8_1", :Hex8), 2^3)
@test isapprox(calculate_volume("CUBE_HEX20_1", :Hex20), 2^3)
@test isapprox(calculate_volume("CUBE_HEX27_1", :Hex27), 2^3)
@test isapprox(calculate_volume("WEDGE_WEDGE6_1", :Wedge6), 1)
# @test isapprox(calculate_volume("WEDGE_WEDGE15_1", :Wedge15, 1/2))
# @test isapprox(calculate_volume("PYRAMID_PYRAMID5_1", :Pyramid5, ?))
# @test isapprox(calculate_volume("PYRAMID_PYRAMID13_1", :Pyramid13, ?))
end
@testset "get nodal field from aster file" begin
fn = Pkg.dir("JuliaFEM") * "/test/testdata/rings.rmed"
medfile = JuliaFEM.Preprocess.RMEDFile(fn)
temp = JuliaFEM.Preprocess.aster_read_data(medfile, "TEMP")
info("temp = $temp")
# more like functional testing, results are what they are,
# we're happy to just have some results
@test true
end
+257
View File
@@ -0,0 +1,257 @@
# 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
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, "reaction force", 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("reaction force", 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 reaction 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")
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, "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]
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, "reaction force", 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("reaction force", 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 reaction force
@test isapprox(Rn, 35.0e3; rtol=0.020)
@test isapprox(Rt, 0.0; atol=200.0)
end
function get_model()
meshfile = joinpath(datadir, "block_2d.med")
mesh = aster_read_mesh(meshfile)
println(mesh.nodes[1])
upper = Problem(mesh, Elasticity, "UPPER", 2)
lower = Problem(mesh, Elasticity, "LOWER", 2)
for body in [upper, lower]
body.properties.formulation = :plane_stress
update!(body, "youngs modulus", 288.0)
update!(body, "poissons ratio", 1/3)
end
load = Problem(mesh, Elasticity, "UPPER_TOP", 2)
load.properties.formulation = :plane_stress
update!(load, "displacement traction force 2", -28.8)
bc1 = Problem(mesh, Dirichlet, "LOWER_BOTTOM", 2, "displacement")
update!(bc1, "displacement 2", 0.0)
bc2 = Problem(mesh, Dirichlet, "LOWER_LEFT", 2, "displacement")
update!(bc2, "displacement 1", 0.0)
bc3 = Problem(mesh, Dirichlet, "UPPER_LEFT", 2, "displacement")
update!(bc3, "displacement 1", 0.0)
interface = Problem(Contact, "interface", 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]
interface.properties.rotate_normals = true
# 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")
nid = find_nearest_node(mesh, [0.0, 0.5]; node_set="LOWER_LEFT")
coords = mesh.nodes[nid]
info("nearest node to (0.0, 0.5) = $nid, coordinates = $coords")
dofs = [2*(nid-1)+1, 2*(nid-1)+2]
info("removing nid $nid, dofs $dofs from LOWER_LEFT")
push!(bc2.assembly.removed_dofs, dofs...)
solver = Solver(Nonlinear)
#push!(solver, upper, lower, load, bc1, interface)
push!(solver, upper, lower, load, bc1, bc2, bc3, interface)
return solver
end
@testset "small sliding 2d patch test, linear Seg2 elements, standard basis" begin
solver = get_model()
interface = solver["interface"]
solver()
node_ids, displacement = get_nodal_vector(interface.elements, "displacement", 0.0)
node_ids, geometry = get_nodal_vector(interface.elements, "geometry", 0.0)
node_ids, reaction_force = get_nodal_vector(get_slave_elements(interface), "reaction force", 0.0)
u2 = [u[2] for u in displacement]
f2 = [f[2] for f in reaction_force]
maxabsu2 = maximum(abs(u2))
stdabsu2 = std(abs(u2))
info("max(abs(u2)) = $maxabsu2, std(abs(u2)) = $stdabsu2")
@test isapprox(stdabsu2, 0.0; atol=1.0e-12)
maxabsf2 = maximum(abs(f2))
stdabsf2 = std(abs(f2))
info("max(abs(f2)) = $maxabsf2, std(abs(f2)) = $stdabsf2")
@test isapprox(stdabsf2, 0.0; atol=1.0e-12)
@test isapprox(mean(abs(f2)), 28.8; atol=1.0e-12)
end
@testset "small sliding 2d patch test, linear Seg2 elements, dual basis" begin
solver = get_model()
interface = solver["interface"]
interface.properties.dual_basis = true
solver()
node_ids, displacement = get_nodal_vector(interface.elements, "displacement", 0.0)
node_ids, geometry = get_nodal_vector(interface.elements, "geometry", 0.0)
node_ids, reaction_force = get_nodal_vector(get_slave_elements(interface), "reaction force", 0.0)
u2 = [u[2] for u in displacement]
f2 = [f[2] for f in reaction_force]
maxabsu2 = maximum(abs(u2))
stdabsu2 = std(abs(u2))
info("max(abs(u2)) = $maxabsu2, std(abs(u2)) = $stdabsu2")
@test isapprox(stdabsu2, 0.0; atol=1.0e-12)
maxabsf2 = maximum(abs(f2))
stdabsf2 = std(abs(f2))
info("max(abs(f2)) = $maxabsf2, std(abs(f2)) = $stdabsf2")
@test isapprox(stdabsf2, 0.0; atol=1.0e-12)
@test isapprox(mean(abs(f2)), 28.8; atol=1.0e-12)
end
Binary file not shown.
+92
View File
@@ -0,0 +1,92 @@
# 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
datadir = first(splitext(basename(@__FILE__)))
function get_model()
meshfile = joinpath(datadir, "block_2d.med")
mesh = aster_read_mesh(meshfile)
println(mesh.nodes[1])
upper = Problem(mesh, Elasticity, "UPPER", 2)
lower = Problem(mesh, Elasticity, "LOWER", 2)
for body in [upper, lower]
body.properties.formulation = :plane_stress
update!(body, "youngs modulus", 288.0)
update!(body, "poissons ratio", 1/3)
end
load = Problem(mesh, Elasticity, "UPPER_TOP", 2)
load.properties.formulation = :plane_stress
update!(load, "displacement traction force 2", 0.0 => 0.0)
update!(load, "displacement traction force 2", 1.0 => -28.8)
bc1 = Problem(mesh, Dirichlet, "LOWER_BOTTOM", 2, "displacement")
update!(bc1, "displacement 2", 0.0)
bc2 = Problem(mesh, Dirichlet, "LOWER_LEFT", 2, "displacement")
update!(bc2, "displacement 1", 0.0)
bc3 = Problem(mesh, Dirichlet, "UPPER_LEFT", 2, "displacement")
update!(bc3, "displacement 1", 0.0)
interface = Problem(Contact, "interface", 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]
interface.properties.rotate_normals = true
# 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")
nid = find_nearest_node(mesh, [0.0, 0.5]; node_set="LOWER_LEFT")
coords = mesh.nodes[nid]
info("nearest node to (0.0, 0.5) = $nid, coordinates = $coords")
dofs = [2*(nid-1)+1, 2*(nid-1)+2]
info("removing nid $nid, dofs $dofs from LOWER_LEFT")
push!(bc2.assembly.removed_dofs, dofs...)
solver = Solver(Nonlinear)
push!(solver, upper, lower, load, bc1, bc2, bc3, interface)
return solver
end
@testset "finite sliding 2d patch test, linear Seg2 elements, standard basis" begin
solver = get_model()
interface = solver["interface"]
upper = solver["UPPER"]
lower = solver["LOWER"]
for body in [upper, lower]
body.properties.geometric_stiffness = true
body.properties.finite_strain = true
end
interface.properties.finite_sliding = true
interface.properties.use_forwarddiff = true
for time in [0.0, 1/3, 2/3, 1.0]
interface.properties.iteration = 1
solver.time = time
solver()
end
node_ids, displacement = get_nodal_vector(interface.elements, "displacement", 1.0)
node_ids, geometry = get_nodal_vector(interface.elements, "geometry", 1.0)
node_ids, reaction_force = get_nodal_vector(get_slave_elements(interface), "reaction force", 1.0)
u2 = [u[2] for u in displacement]
f2 = [f[2] for f in reaction_force]
maxabsu2 = maximum(abs(u2))
stdabsu2 = std(abs(u2))
info("max(abs(u2)) = $maxabsu2, std(abs(u2)) = $stdabsu2")
@test isapprox(stdabsu2, 0.0; atol=1.0e-12)
maxabsf2 = maximum(abs(f2))
stdabsf2 = std(abs(f2))
info("max(abs(f2)) = $maxabsf2, std(abs(f2)) = $stdabsf2")
@test isapprox(stdabsf2, 0.0; atol=1.0e-12)
# for linear case pressure 28.8
@test isapprox(mean(abs(f2)), 27.76616800689944; rtol=1.0e-3)
end
Binary file not shown.
+131
View File
@@ -0,0 +1,131 @@
# 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
using JuliaFEM.Abaqus: create_surface_elements
tet4_meshfile = "test_problems_contact_3d/tet4.inp"
tet10_meshfile = "test_problems_contact_3d/tet10.inp"
function get_model(meshfile)
mesh = abaqus_read_mesh(meshfile)
upper = Problem(Elasticity, "UPPER", 3)
upper.elements = create_elements(mesh, "UPPER")
update!(upper, "youngs modulus", 3*288.0)
update!(upper, "poissons ratio", 1/3)
lower = Problem(Elasticity, "LOWER", 3)
lower.elements = create_elements(mesh, "LOWER")
update!(lower, "youngs modulus", 288.0)
update!(lower, "poissons ratio", 1/3)
bc_upper = Problem(Dirichlet, "UPPER_TOP", 3, "displacement")
bc_upper.elements = create_surface_elements(mesh, "UPPER_TOP")
update!(bc_upper, "displacement 3", -0.4)
bc_lower = Problem(Dirichlet, "LOWER_BOTTOM", 3, "displacement")
bc_lower.elements = create_surface_elements(mesh, "LOWER_BOTTOM")
update!(bc_lower, "displacement 3", 0.0)
# point-wise boundary conditions to prevent free body move
nid1 = find_nearest_nodes(mesh, [0.0, 0.0, 0.0])[1]
nid2 = find_nearest_nodes(mesh, [1.0, 0.0, 0.0])[1]
nid3 = find_nearest_nodes(mesh, [0.0, 1.0, 0.0])[1]
nid4 = find_nearest_nodes(mesh, [0.0, 0.0, 1.0])[1]
nid5 = find_nearest_nodes(mesh, [1.0, 0.0, 1.0])[1]
nid6 = find_nearest_nodes(mesh, [0.0, 1.0, 1.0])[1]
bc_sym13 = Problem(Dirichlet, "SYM13", 3, "displacement")
# nodes in X2=0 plane
bc_sym13.elements = [Element(Poi1, [j]) for j in [nid1, nid2, nid4, nid5]]
update!(bc_sym13, "geometry", mesh.nodes)
update!(bc_sym13, "displacement 2", 0.0)
bc_sym23 = Problem(Dirichlet, "SYM23", 3, "displacement")
# nodes in X1=0 plane
bc_sym23.elements = [Element(Poi1, [j]) for j in [nid1, nid3, nid4, nid6]]
update!(bc_sym23, "geometry", mesh.nodes)
update!(bc_sym23, "displacement 1", 0.0)
interface = Problem(Contact, "LOWER_TO_UPPER", 3, "displacement")
interface_slave_elements = create_surface_elements(mesh, "LOWER_TO_UPPER")
interface_master_elements = create_surface_elements(mesh, "UPPER_TO_LOWER")
update!(interface_slave_elements, "master elements", interface_master_elements)
interface.elements = [interface_slave_elements; interface_master_elements]
interface.properties.contact_state_in_first_iteration = :AUTO
#append!(bc_sym13.assembly.removed_dofs, [1316, 1319, 1388, 1358])
#append!(bc_sym23.assembly.removed_dofs, [1492, 1627, 1387, 1597])
#append!(interface.assembly.removed_dofs, [1316, 1319, 1358, 1387, 1388, 1492, 1597, 1627])
solver = NonlinearSolver(upper, lower, bc_upper, bc_lower, bc_sym13, bc_sym23, interface)
# solver.properties.max_iterations = 5
return solver
end
@testset "small sliding contact patch test, tet4 + standard basis" begin
solver = get_model(tet4_meshfile)
solver.xdmf = Xdmf("contact_sl_lin_disp_results"; overwrite=true)
interface = solver["LOWER_TO_UPPER"]
interface.properties.dual_basis = false
solver()
node_ids, displacement = get_nodal_vector(interface.elements, "displacement", 0.0)
node_ids, geometry = get_nodal_vector(interface.elements, "geometry", 0.0)
# node_ids, pressure = get_nodal_vector(interface.elements, "contact pressure", 0.0)
u3 = [u[3] for u in displacement]
maxabsu3 = maximum(abs(u3))
stdabsu3 = std(abs(u3))
info("max(abs(u3)) = $maxabsu3, std(abs(u3)) = $stdabsu3")
@test isapprox(stdabsu3, 0.0; atol=1.0e-12)
end
@testset "small sliding contact patch test, tet4 + dual basis" begin
solver = get_model(tet4_meshfile)
solver.xdmf = Xdmf("contact_dl_lin_disp_results"; overwrite=true)
interface = solver["LOWER_TO_UPPER"]
interface.properties.dual_basis = true
solver()
node_ids, displacement = get_nodal_vector(interface.elements, "displacement", 0.0)
node_ids, geometry = get_nodal_vector(interface.elements, "geometry", 0.0)
u3 = [u[3] for u in displacement]
maxabsu3 = maximum(abs(u3))
stdabsu3 = std(abs(u3))
info("max(abs(u3)) = $maxabsu3, std(abs(u3)) = $stdabsu3")
@test isapprox(stdabsu3, 0.0; atol=1.0e-12)
end
@testset "small sliding contact patch test, tet10 + standard basis" begin
solver = get_model(tet10_meshfile)
solver.xdmf = Xdmf("contact_sl_quad_disp_results"; overwrite=true)
interface = solver["LOWER_TO_UPPER"]
interface.properties.dual_basis = false
solver()
node_ids, displacement = get_nodal_vector(interface.elements, "displacement", 0.0)
node_ids, geometry = get_nodal_vector(interface.elements, "geometry", 0.0)
u3 = [u[3] for u in displacement]
maxabsu3 = maximum(abs(u3))
stdabsu3 = std(abs(u3))
info("max(abs(u3)) = $maxabsu3, std(abs(u3)) = $stdabsu3")
@test isapprox(stdabsu3, 0.0; atol=1.0e-10)
end
@testset "small sliding contact patch test, tet10 + dual basis, alpha=0.2" begin
solver = get_model(tet10_meshfile)
solver.xdmf = Xdmf("contact_sl_quad_disp_results"; overwrite=true)
interface = solver["LOWER_TO_UPPER"]
interface.properties.dual_basis = true
interface.properties.alpha = 0.2
solver()
node_ids, displacement = get_nodal_vector(interface.elements, "displacement", 0.0)
node_ids, geometry = get_nodal_vector(interface.elements, "geometry", 0.0)
u3 = [u[3] for u in displacement]
maxabsu3 = maximum(abs(u3))
stdabsu3 = std(abs(u3))
info("max(abs(u3)) = $maxabsu3, std(abs(u3)) = $stdabsu3")
@test isapprox(stdabsu3, 0.0; atol=1.0e-10)
end
File diff suppressed because it is too large Load Diff
+589
View File
@@ -0,0 +1,589 @@
**NSET COUNT = 116
*NODE
127, 1.00000, 0.00000, 0.75000
128, 1.00000, 0.00000, 1.00000
129, 0.75000, 0.00000, 1.00000
133, 0.75000, 0.00000, 0.75000
136, 1.00000, 0.00000, 0.50000
139, 0.75000, 0.00000, 0.50000
142, 0.50000, 0.00000, 1.00000
145, 0.50000, 0.00000, 0.75000
149, 0.50000, 0.00000, 0.50000
152, 0.25000, 0.00000, 1.00000
155, 0.25000, 0.00000, 0.75000
159, 0.25000, 0.00000, 0.50000
162, 0.00000, 0.00000, 1.00000
165, 0.00000, 0.00000, 0.75000
169, 0.00000, 0.00000, 0.50000
172, 0.75000, 1.00000, 1.00000
173, 1.00000, 1.00000, 1.00000
174, 1.00000, 1.00000, 0.75000
178, 0.75000, 1.00000, 0.75000
181, 1.00000, 1.00000, 0.50000
184, 0.75000, 1.00000, 0.50000
187, 0.50000, 1.00000, 1.00000
190, 0.50000, 1.00000, 0.75000
194, 0.50000, 1.00000, 0.50000
197, 0.25000, 1.00000, 1.00000
200, 0.25000, 1.00000, 0.75000
204, 0.25000, 1.00000, 0.50000
207, 0.00000, 1.00000, 1.00000
210, 0.00000, 1.00000, 0.75000
214, 0.00000, 1.00000, 0.50000
217, 1.00000, 0.75000, 1.00000
220, 1.00000, 0.75000, 0.75000
224, 1.00000, 0.75000, 0.50000
227, 1.00000, 0.50000, 1.00000
230, 1.00000, 0.50000, 0.75000
234, 1.00000, 0.50000, 0.50000
237, 1.00000, 0.25000, 1.00000
240, 1.00000, 0.25000, 0.75000
244, 1.00000, 0.25000, 0.50000
252, 0.00000, 0.75000, 1.00000
255, 0.00000, 0.75000, 0.75000
259, 0.00000, 0.75000, 0.50000
262, 0.00000, 0.50000, 1.00000
265, 0.00000, 0.50000, 0.75000
269, 0.00000, 0.50000, 0.50000
272, 0.00000, 0.25000, 1.00000
275, 0.00000, 0.25000, 0.75000
279, 0.00000, 0.25000, 0.50000
288, 0.75000, 0.75000, 1.00000
292, 0.50000, 0.75000, 1.00000
296, 0.25000, 0.75000, 1.00000
302, 0.75000, 0.50000, 1.00000
306, 0.50000, 0.50000, 1.00000
310, 0.25000, 0.50000, 1.00000
316, 0.75000, 0.25000, 1.00000
320, 0.50000, 0.25000, 1.00000
324, 0.25000, 0.25000, 1.00000
337, 0.75000, 0.75000, 0.50000
341, 0.50000, 0.75000, 0.50000
345, 0.25000, 0.75000, 0.50000
351, 0.75000, 0.50000, 0.50000
355, 0.50000, 0.50000, 0.50000
359, 0.25000, 0.50000, 0.50000
365, 0.75000, 0.25000, 0.50000
369, 0.50000, 0.25000, 0.50000
373, 0.25000, 0.25000, 0.50000
438, 1.00000, 0.00000, 0.25000
439, 1.00000, 0.00000, 0.50000
440, 0.66667, 0.00000, 0.50000
444, 0.66667, 0.00000, 0.25000
447, 1.00000, 0.00000, 0.00000
450, 0.66667, 0.00000, 0.00000
453, 0.33333, 0.00000, 0.50000
456, 0.33333, 0.00000, 0.25000
460, 0.33333, 0.00000, 0.00000
463, 0.00000, 0.00000, 0.50000
466, 0.00000, 0.00000, 0.25000
470, 0.00000, 0.00000, 0.00000
473, 0.66667, 1.00000, 0.50000
474, 1.00000, 1.00000, 0.50000
475, 1.00000, 1.00000, 0.25000
479, 0.66667, 1.00000, 0.25000
482, 1.00000, 1.00000, 0.00000
485, 0.66667, 1.00000, 0.00000
488, 0.33333, 1.00000, 0.50000
491, 0.33333, 1.00000, 0.25000
495, 0.33333, 1.00000, 0.00000
498, 0.00000, 1.00000, 0.50000
501, 0.00000, 1.00000, 0.25000
505, 0.00000, 1.00000, 0.00000
508, 1.00000, 0.66667, 0.50000
511, 1.00000, 0.66667, 0.25000
515, 1.00000, 0.66667, 0.00000
518, 1.00000, 0.33333, 0.50000
521, 1.00000, 0.33333, 0.25000
525, 1.00000, 0.33333, 0.00000
533, 0.00000, 0.66667, 0.50000
536, 0.00000, 0.66667, 0.25000
540, 0.00000, 0.66667, 0.00000
543, 0.00000, 0.33333, 0.50000
546, 0.00000, 0.33333, 0.25000
550, 0.00000, 0.33333, 0.00000
559, 0.66667, 0.66667, 0.50000
563, 0.33333, 0.66667, 0.50000
569, 0.66667, 0.33333, 0.50000
573, 0.33333, 0.33333, 0.50000
584, 0.66667, 0.66667, 0.00000
588, 0.33333, 0.66667, 0.00000
594, 0.66667, 0.33333, 0.00000
598, 0.33333, 0.33333, 0.00000
608, 0.66006, 0.47128, 0.70078
609, 0.19762, 0.64074, 0.81889
610, 0.65771, 0.82813, 0.74829
611, 0.59993, 0.15953, 0.76106
612, 0.40915, 0.16311, 0.74937
613, 0.50000, 0.50000, 0.25000
**
**ELSET COUNT = 172
**HWCOLOR COMP 54 0
*ELEMENT, TYPE=C3D4, ELSET=UPPER
570, 252, 262, 255, 609
571, 252, 310, 262, 609
572, 252, 296, 310, 609
573, 200, 252, 255, 609
574, 296, 306, 310, 609
575, 262, 310, 265, 609
576, 259, 265, 269, 359
577, 184, 337, 220, 610
578, 259, 345, 265, 359
579, 288, 608, 292, 610
580, 187, 288, 292, 610
581, 172, 288, 187, 610
582, 178, 172, 187, 610
583, 178, 220, 172, 610
584, 172, 220, 288, 610
585, 230, 240, 237, 316
586, 178, 184, 220, 610
587, 178, 190, 184, 610
588, 178, 187, 190, 610
589, 187, 292, 190, 610
590, 190, 292, 609, 610
591, 190, 609, 341, 610
592, 190, 341, 194, 610
593, 184, 190, 194, 610
594, 184, 194, 337, 610
595, 194, 341, 337, 610
596, 337, 341, 608, 610
597, 230, 337, 608, 610
598, 230, 608, 288, 610
599, 220, 230, 288, 610
600, 220, 337, 230, 610
601, 341, 609, 608, 610
602, 292, 608, 609, 610
603, 306, 316, 320, 611
604, 306, 608, 316, 611
605, 365, 608, 369, 611
606, 240, 608, 365, 611
607, 240, 316, 608, 611
608, 240, 133, 316, 611
609, 240, 365, 133, 611
610, 365, 139, 133, 611
611, 365, 369, 139, 611
612, 369, 149, 139, 611
613, 149, 145, 611, 612
614, 240, 127, 128, 316
615, 320, 129, 142, 611
616, 316, 129, 320, 611
617, 316, 133, 129, 611
618, 129, 133, 142, 611
619, 142, 133, 145, 611
620, 139, 145, 133, 611
621, 139, 149, 145, 611
622, 310, 320, 324, 612
623, 306, 320, 310, 612
624, 306, 611, 320, 612
625, 306, 608, 611, 612
626, 306, 609, 608, 612
627, 306, 310, 609, 612
628, 265, 609, 310, 612
629, 265, 310, 324, 612
630, 265, 324, 275, 612
631, 265, 275, 373, 612
632, 265, 373, 359, 612
633, 265, 359, 609, 612
634, 355, 609, 359, 612
635, 355, 608, 609, 612
636, 355, 369, 608, 612
637, 369, 611, 608, 612
638, 214, 255, 259, 345
639, 142, 611, 145, 612
640, 320, 611, 142, 612
641, 320, 142, 324, 612
642, 324, 142, 152, 612
643, 324, 152, 155, 612
644, 275, 324, 155, 612
645, 275, 155, 373, 612
646, 373, 155, 159, 612
647, 373, 159, 149, 612
648, 369, 373, 149, 612
649, 359, 373, 369, 612
650, 355, 359, 369, 612
651, 369, 149, 611, 612
652, 149, 155, 145, 612
653, 149, 159, 155, 612
654, 152, 145, 155, 612
655, 142, 145, 152, 612
656, 237, 240, 128, 316
657, 244, 136, 127, 365
658, 240, 365, 127, 133
659, 240, 351, 244, 365
660, 220, 224, 230, 337
661, 227, 288, 230, 302
662, 200, 207, 252, 296
663, 240, 127, 316, 133
664, 230, 337, 234, 351
665, 275, 162, 165, 155
666, 172, 220, 217, 288
667, 200, 252, 210, 255
668, 190, 292, 197, 296
669, 230, 234, 240, 351
670, 204, 210, 214, 345
671, 210, 255, 214, 345
672, 275, 165, 373, 155
673, 172, 174, 217, 220
674, 200, 204, 341, 345
675, 240, 244, 127, 365
676, 220, 230, 227, 288
677, 272, 275, 324, 155
678, 272, 152, 162, 155
679, 200, 255, 210, 345
680, 190, 200, 194, 341
681, 197, 207, 200, 296
682, 190, 197, 200, 296
683, 279, 165, 169, 159
684, 200, 207, 210, 252
685, 224, 234, 230, 337
686, 187, 197, 190, 292
687, 275, 165, 279, 373
688, 265, 275, 269, 373
689, 272, 324, 152, 155
690, 262, 265, 310, 324
691, 265, 272, 275, 324
692, 184, 224, 220, 337
693, 272, 162, 275, 155
694, 262, 272, 265, 324
695, 178, 174, 172, 220
696, 136, 365, 139, 133
697, 227, 230, 237, 302
698, 178, 184, 181, 220
699, 172, 174, 173, 217
700, 194, 200, 204, 341
701, 234, 244, 240, 351
702, 200, 210, 204, 345
703, 230, 237, 302, 316
704, 128, 316, 127, 129
705, 217, 220, 227, 288
706, 127, 365, 136, 133
707, 265, 269, 359, 373
708, 279, 373, 165, 159
709, 165, 159, 373, 155
710, 269, 275, 279, 373
711, 127, 129, 316, 133
712, 181, 220, 184, 224
713, 178, 181, 174, 220
714, 292, 302, 306, 608
715, 288, 302, 292, 608
716, 230, 302, 288, 608
717, 230, 316, 302, 608
718, 230, 240, 316, 608
719, 230, 351, 240, 608
720, 230, 337, 351, 608
721, 302, 316, 306, 608
722, 240, 351, 365, 608
723, 351, 355, 365, 608
724, 341, 355, 351, 608
725, 337, 341, 351, 608
726, 355, 369, 365, 608
727, 341, 355, 608, 609
728, 292, 608, 306, 609
729, 292, 306, 296, 609
730, 190, 292, 296, 609
731, 190, 200, 341, 609
732, 190, 296, 200, 609
733, 200, 296, 252, 609
734, 200, 255, 345, 609
735, 200, 345, 341, 609
736, 341, 345, 355, 609
737, 345, 359, 355, 609
738, 265, 359, 345, 609
739, 255, 265, 345, 609
740, 255, 265, 259, 345
741, 255, 262, 265, 609
**
**ELSET COUNT = 92
**HWCOLOR COMP 1 0
*ELEMENT, TYPE=C3D4, ELSET=LOWER
742, 456, 460, 466, 598
743, 444, 450, 456, 598
744, 444, 525, 447, 594
745, 536, 540, 588, 598
746, 453, 456, 463, 543
747, 533, 543, 536, 573
748, 444, 456, 453, 573
749, 466, 550, 546, 598
750, 536, 543, 546, 573
751, 491, 536, 495, 588
752, 438, 447, 444, 525
753, 444, 594, 450, 598
754, 444, 521, 525, 594
755, 438, 444, 440, 521
756, 540, 546, 550, 598
757, 453, 456, 543, 573
758, 488, 491, 559, 563
759, 505, 536, 540, 588
760, 440, 444, 453, 573
761, 491, 495, 584, 588
762, 444, 447, 450, 594
763, 485, 491, 495, 584
764, 440, 569, 444, 573
765, 495, 501, 505, 536
766, 473, 479, 511, 559
767, 438, 440, 439, 521
768, 491, 533, 536, 563
769, 444, 569, 521, 594
770, 473, 479, 475, 511
771, 491, 498, 501, 533
772, 495, 536, 505, 588
773, 438, 444, 521, 525
774, 473, 511, 508, 559
775, 440, 521, 444, 569
776, 511, 515, 521, 584
777, 491, 498, 533, 563
778, 515, 525, 521, 584
779, 460, 550, 466, 598
780, 439, 521, 440, 569
781, 456, 463, 543, 546
782, 479, 482, 511, 515
783, 533, 536, 563, 573
784, 479, 515, 511, 584
785, 450, 460, 456, 598
786, 521, 569, 559, 594
787, 559, 594, 569, 613
788, 559, 584, 594, 613
789, 521, 559, 584, 594
790, 536, 546, 540, 598
791, 511, 521, 559, 584
792, 479, 559, 491, 584
793, 491, 559, 563, 584
794, 511, 521, 518, 559
795, 491, 584, 563, 588
796, 563, 588, 584, 613
797, 559, 563, 584, 613
798, 559, 569, 563, 613
799, 563, 569, 573, 613
800, 536, 563, 573, 588
801, 563, 573, 588, 613
802, 573, 598, 588, 613
803, 491, 501, 495, 536
804, 536, 573, 546, 598
805, 536, 588, 573, 598
806, 588, 598, 594, 613
807, 584, 588, 594, 613
808, 444, 569, 594, 598
809, 521, 584, 525, 594
810, 444, 573, 569, 598
811, 569, 598, 573, 613
812, 569, 594, 598, 613
813, 444, 456, 573, 598
814, 439, 518, 521, 569
815, 456, 546, 543, 573
816, 479, 485, 482, 515
817, 508, 511, 518, 559
818, 456, 546, 573, 598
819, 488, 498, 491, 563
820, 479, 485, 515, 584
821, 491, 563, 536, 588
822, 473, 488, 479, 559
823, 479, 491, 485, 584
824, 491, 533, 501, 536
825, 479, 511, 559, 584
826, 479, 488, 491, 559
827, 475, 479, 482, 511
828, 518, 559, 521, 569
829, 473, 475, 508, 511
830, 473, 475, 474, 508
831, 460, 470, 466, 550
832, 456, 466, 463, 546
833, 456, 466, 546, 598
**
**ELSET COUNT = 18
*SURFACE,TYPE=ELEMENT,NAME=LOWER_TO_UPPER
799,S1
798,S1
764,S2
814,S2
747,S2
828,S2
819,S2
822,S2
777,S3
746,S4
757,S4
760,S4
780,S4
783,S4
817,S4
758,S4
774,S4
830,S4
**
**ELSET COUNT = 18
*SURFACE,TYPE=ELEMENT,NAME=LOWER_BOTTOM
806,S1
807,S1
831,S2
779,S2
785,S2
778,S2
753,S3
762,S3
744,S3
745,S3
809,S3
761,S3
820,S3
816,S3
756,S4
759,S4
772,S4
763,S4
**
**ELSET COUNT = 12
*SURFACE,TYPE=ELEMENT,NAME=LOWER_SYM13
831,S1
742,S1
832,S1
746,S1
785,S1
743,S1
748,S1
760,S1
762,S1
752,S1
755,S1
767,S1
**
**ELSET COUNT = 12
*SURFACE,TYPE=ELEMENT,NAME=LOWER_SYM23
749,S1
756,S1
790,S1
750,S1
747,S1
759,S1
831,S3
832,S3
781,S3
765,S3
824,S3
771,S3
**
**ELSET COUNT = 32
*SURFACE,TYPE=ELEMENT,NAME=UPPER_TOP
678,S1
689,S1
642,S1
641,S1
615,S1
616,S1
622,S1
623,S1
603,S1
721,S1
571,S1
572,S1
574,S1
729,S1
714,S1
715,S1
580,S1
581,S1
704,S2
694,S2
661,S2
681,S2
686,S2
703,S3
662,S3
668,S3
656,S4
690,S4
697,S4
705,S4
666,S4
699,S4
**
**ELSET COUNT = 32
*SURFACE,TYPE=ELEMENT,NAME=UPPER_TO_LOWER
647,S1
648,S1
612,S1
611,S1
696,S1
649,S1
650,S1
726,S1
723,S1
737,S1
736,S1
724,S1
725,S1
595,S1
594,S1
708,S2
657,S2
701,S2
578,S2
685,S2
692,S2
707,S3
659,S3
664,S3
674,S3
683,S4
710,S4
576,S4
638,S4
670,S4
700,S4
712,S4
**
**ELSET COUNT = 16
*SURFACE,TYPE=ELEMENT,NAME=UPPER_SYM13
653,S1
652,S1
654,S1
655,S1
621,S1
620,S1
619,S1
618,S1
709,S2
711,S2
683,S3
665,S3
678,S3
696,S4
706,S4
704,S4
**
**ELSET COUNT = 16
*SURFACE,TYPE=ELEMENT,NAME=UPPER_SYM23
683,S1
687,S1
665,S1
693,S1
710,S1
688,S1
691,S1
694,S1
576,S1
740,S1
741,S1
570,S1
638,S1
671,S1
667,S3
684,S3
**
**Property Definitions
**
*SOLID SECTION, ELSET=UPPER, MATERIAL=Def_Material
*SOLID SECTION, ELSET=LOWER, MATERIAL=Def_Material
**
**Material Definitions
**
**Material:Def_Material
*MATERIAL,NAME=Def_Material
*ELASTIC,TYPE=ISO
2.08000e+005,3.00000e-001
*DENSITY
7.80000e-009,
*SPECIFIC HEAT
5.00000e-001
*CONDUCTIVITY
4.98100e-002
**