mirror of
https://github.com/JuliaFEM/JuliaFEM.jl.git
synced 2026-09-21 18:33:36 +00:00
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:
committed by
Tero Frondelius
parent
28e09f0305
commit
a0d18568ce
+1
-12
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
@@ -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
|
||||
@@ -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.
@@ -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.
@@ -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
@@ -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
|
||||
**
|
||||
Reference in New Issue
Block a user