refactor contact and tests

This commit is contained in:
Jukka Aho
2016-06-25 04:12:53 +03:00
parent 6d3e33c3ff
commit 90f7c581c5
28 changed files with 874 additions and 420 deletions
+11 -7
View File
@@ -1,19 +1,23 @@
# 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.Test
function run_tests(; quiet=false)
test_files = readdir(Pkg.dir("JuliaFEM")*"/test")
test_files = filter(f -> (startswith(f, "test_") & endswith(f, ".jl")), test_files)
for test_file in test_files
if !quiet
info("Running tests from file $test_file")
maybe_test_files = readdir(Pkg.dir("JuliaFEM")*"/test")
is_test_file(fn) = startswith(fn, "test_") & endswith(fn, ".jl")
test_files = filter(is_test_file, maybe_test_files)
#test_files = ["test_nodal_constraints.jl"]
body = quote
@testset "JuliaFEM" begin
for fn in $test_files
@testset "$fn" begin include(fn) end
end
end
include(test_file)
end
eval(body)
end
+147
View File
@@ -0,0 +1,147 @@
# 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.Test
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.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", -0.15)
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(Contact, "contact between upper and lower block", 2, "displacement")
interface.properties.dimension = 1
interface.properties.rotate_normals = true
interface_slave_elements = create_elements(mesh, "LOWER_TOP")
interface_master_elements = create_elements(mesh, "UPPER_BOTTOM")
update!(interface_slave_elements, "master elements", interface_master_elements)
interface.elements = [interface_master_elements; interface_slave_elements]
solver = Solver(Nonlinear)
push!(solver, upper, lower, bc_upper, bc_lower, interface)
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")
call(solver)
upper, lower, bc_upper, bc_lower, interface = solver.problems
@test isapprox(norm(interface.assembly.u), 0.49563347601324315)
end
function get_mesh(::Type{Val{Symbol("hertz contact, full 2d model")}})
meshfile = Pkg.dir("JuliaFEM") * "/test/testdata/hertz_2d_full.med"
mesh = aster_read_mesh(meshfile)
end
function get_model(::Type{Val{Symbol("hertz contact, full 2d model")}})
# from fenet d3613 advanced finite element contact benchmarks
# a = 6.21 mm, pmax = 3585 MPa
# this is a very dense 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
mesh = get_mesh("hertz contact, full 2d model")
upper = Problem(Elasticity, "CYLINDER", 2)
upper.properties.formulation = :plane_strain
upper.elements = create_elements(mesh, "CYLINDER")
update!(upper.elements, "youngs modulus", 70.0e3)
update!(upper.elements, "poissons ratio", 0.3)
lower = Problem(Elasticity, "BLOCK", 2)
lower.properties.formulation = :plane_strain
lower.elements = create_elements(mesh, "BLOCK")
update!(lower.elements, "youngs modulus", 210.0e3)
update!(lower.elements, "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.elements, "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.elements, "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.elements, "displacement traction force 2", -35.0e3)
contact = Problem(Contact, "contact between block and cylinder", 2, "displacement")
contact.properties.rotate_normals = true
contact_slave_elements = create_elements(mesh, "CYLINDER_TO_BLOCK")
contact_master_elements = create_elements(mesh, "BLOCK_TO_CYLINDER")
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
solver = get_model("hertz contact, full 2d model")
call(solver)
upper, lower, bc_fixed, bc_sym_23, load, contact = solver.problems
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)
# 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
@test isapprox(Rn, 35.0e3; rtol=0.0015)
@test isapprox(Rt, 0.0; atol=10.0)
end
+75 -253
View File
@@ -1,263 +1,85 @@
# 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.Test
using JuliaFEM.Core: Node, Seg2, Tri3, update!, calculate_normal_tangential_coordinates!,
PlaneStressLinearElasticityProblem, DirichletProblem, MortarProblem,
get_elements, DirectSolver, calculate_nodal_vector, FieldAssembly,
FieldProblem, set_linear_system_solver!, set_nonlinear_max_iterations!,
get_elements, Element, BoundaryAssembly, BoundaryProblem,
get_integration_points, get_jacobian, get_connectivity, StandardBasis,
add_postprocessor!, add_preprocessor!, SparseMatrixCOO, add!,
add_linear_system_solver_preprocessor!,
add_linear_system_solver_postprocessor!
import JuliaFEM.Core: assemble_preprocess!, assemble_postprocess!,
linear_system_solver_preprocess!, linear_system_solver_postprocess!
macro debug(msg)
haskey(ENV, "DEBUG") || return
return msg
function JuliaFEM.get_mesh(::Type{Val{Symbol("two elements 1.0x0.5 with 0.1 gap in y direction")}})
mesh = Mesh()
add_node!(mesh, 1, [0.0, 0.0])
add_node!(mesh, 2, [1.0, 0.0])
add_node!(mesh, 3, [1.0, 0.5])
add_node!(mesh, 4, [0.0, 0.5])
add_node!(mesh, 5, [0.0, 0.6])
add_node!(mesh, 6, [1.0, 0.6])
add_node!(mesh, 7, [1.0, 1.1])
add_node!(mesh, 8, [0.0, 1.1])
add_element!(mesh, 1, :Quad4, [1, 2, 3, 4])
add_element!(mesh, 2, :Quad4, [5, 6, 7, 8])
add_element!(mesh, 3, :Seg2, [1, 2])
add_element!(mesh, 4, :Seg2, [7, 8])
add_element!(mesh, 5, :Seg2, [4, 3])
add_element!(mesh, 6, :Seg2, [6, 5])
add_element_to_element_set!(mesh, "LOWER", 1)
add_element_to_element_set!(mesh, "UPPER", 2)
add_element_to_element_set!(mesh, "LOWER_BOTTOM", 3)
add_element_to_element_set!(mesh, "UPPER_TOP", 4)
add_element_to_element_set!(mesh, "LOWER_TOP", 5)
add_element_to_element_set!(mesh, "UPPER_BOTTOM", 6)
return mesh
end
function calculate_normal_tangential_coordinates(elements::Vector{Element}, time::Real)
P = SparseMatrixCOO()
field_dim = 2
for element in elements
haskey(element, "normal-tangential coordinates") || continue
for ip in get_integration_points(element, Val{2})
J = get_jacobian(element, ip, time)
w = ip.weight*norm(J)
nt = transpose(element("normal-tangential coordinates", ip, time))
normal = nt[1,:]
tangent = nt[2,:]
for nid in get_connectivity(element)
ndofs = [2*(nid-1)+1, 2*(nid-1)+2]
add!(P, [2*(nid-1)+1], ndofs, normal)
add!(P, [2*(nid-1)+2], ndofs, tangent)
end
end
end
P = sparse(P)
for i=1:size(P,1)
n = norm(P[i,:])
if n > 0.0
P[i,:] = P[i,:] / n
end
end
return SparseMatrixCOO(P)
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
upper.elements = create_elements(mesh, "UPPER")
update!(upper.elements, "youngs modulus", 288.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", 288.0)
update!(lower.elements, "poissons ratio", 1/3)
bc_upper = Problem(Dirichlet, "UPPER_TOP", 2, "displacement")
bc_upper.elements = create_elements(mesh, "UPPER_TOP")
#update!(bc_upper.elements, "displacement 1", -17/90)
#update!(bc_upper.elements, "displacement 1", -17/90)
update!(bc_upper.elements, "displacement 1", -0.2)
update!(bc_upper.elements, "displacement 2", -0.2)
bc_lower = Problem(Dirichlet, "LOWER_BOTTOM", 2, "displacement")
bc_lower.elements = create_elements(mesh, "LOWER_BOTTOM")
update!(bc_lower.elements, "displacement 1", 0.0)
update!(bc_lower.elements, "displacement 2", 0.0)
interface = Problem(Contact, "LOWER_TO_UPPER", 2, "displacement")
interface.properties.dimension = 1
interface_slave_elements = create_elements(mesh, "LOWER_TOP")
interface_master_elements = create_elements(mesh, "UPPER_BOTTOM")
update!(interface_slave_elements, "master elements", interface_master_elements)
interface.elements = [interface_master_elements; interface_slave_elements]
solver = Solver(Nonlinear)
push!(solver, upper, lower, bc_upper, bc_lower, interface)
return solver
end
function assemble_postprocess!(assembly, problem, time::Real, ::Type{Val{:remove_constraint_from_dofs}}, dofs)
# giving additional arguments and keywords is possible too
C1 = sparse(assembly.C1)
C2 = sparse(assembly.C2)
info("removing constraints from dofs: $dofs")
for d in dofs
C1[d,:] = 0
C2[d,:] = 0
end
assembly.C1 = C1
assembly.C2 = C2
@testset "test simple two element contact" begin
solver = get_model("two element contact")
call(solver)
contact = solver["LOWER_TO_UPPER"]
master = first(contact.elements)
slave = last(contact.elements)
u = master("displacement", [0.0], 0.0)
la = slave("reaction force", [0.0], 0.0)
info("u = $u, la = $la")
@test isapprox(u, [-0.2, -0.15])
@test isapprox(la, [0.0, 30.375])
end
function assemble_postprocess!(assembly, problem, time::Real, ::Type{Val{:remove_tangential_constraints}})
# example how to use postprocessor to manipulate constraint matrix before summing assemblies together
info("postprocess mortar assembly: remove contraints in tangent direction on boundary.")
dim = 12
C1 = sparse(assembly.C1, dim, dim)
C2 = sparse(assembly.C2, dim, dim)
P = calculate_normal_tangential_coordinates(get_elements(problem), time)
P = sparse(P, dim, dim)
info("projection matrix for normals: ")
dump(round(full(P), 3))
C1 = P*C1
C2 = P*C2
for i=2:2:dim
C1[i,:] = 0
C2[i,:] = 0
end
assembly.C1 = C1
assembly.C2 = C2
info("postprocess mortar assembly: done.")
end
function assemble_postprocess!(assembly, problem, time::Real, ::Type{Val{:primal_dual_active_set_strategy}})
info("PDASS: determining active contact set")
dim = 12
C1 = sparse(assembly.C1, dim, dim)
C2 = sparse(assembly.C2, dim, dim)
info("PDASS: constraint matrix C1")
# dump(round(full(C1[1:2:end,1:2:end]), 3))
dump(round(full(C1), 3))
info("PDASS: constraint matrix C2")
# dump(round(full(C2[1:2:end,1:2:end]), 3))
dump(round(full(C2), 3))
elements = get_elements(problem)
P = calculate_normal_tangential_coordinates(get_elements(problem), time)
P = sparse(P, dim, dim)
la = calculate_nodal_vector("reaction force", 2, elements, time)
X = calculate_nodal_vector("geometry", 2, elements, time)
u = calculate_nodal_vector("displacement", 2, elements, time)
resize!(la, 12)
resize!(X, 12)
resize!(u, 12)
x = X+u
info("x")
dump(reshape(round(x, 2), 2, 6))
P = sparse(eye(dim))
C1 = P*C1
C2 = P*C2
gn = P*C1*X #*4/6 ..?
un = P*C1*u
la = P*la
info("weighted gap in nt =")
dump(reshape(round(gn, 2), 2, 6))
info("weighted u in nt =")
dump(reshape(round(un, 2), 2, 6))
info("weighted joo in nt =")
dump(reshape(round(gn+un, 2), 2, 6))
info("lambda in nt =")
dump(reshape(round(la, 2), 2, 6))
# complementarity function
cn = 1.0
# C = la - clamp(la - cn*(gn+un), 0, Inf)
# C = la + clamp(la - cn*(gn+un), 0, Inf)
C = la + cn*(un - gn)
info("complementarity function =")
dump(reshape(round(C, 2), 2, 6))
g = zeros(length(gn))
for i=1:2:dim
if i == 1
#if C[i] > 0
if i == 7
info("skipping root dof 7")
continue
end
info("dof $i in active set")
# g[i] = -gn[i]
else
info("dof $i not in active set")
# C1[i,:] = 0
# C2[i,:] = 0
end
end
g[1] = 2.0
g[3] = 2.0
#=
for i=2:2:dim
C1[i,:] = 0
C2[i,:] = 0
end
=#
d = [7, 8]
C2[d, :] = 0
assembly.g = sparse(g)
assembly.C1 = C1
assembly.C2 = C2
info("PDASS ready.")
end
function linear_system_solver_preprocess!(solver, iter, time, K, f, C1, C2, D, g, sol, la, ::Type{Val{:before_solution}})
# example how to use preprocessor to dump matrices before solution
@debug begin
info("stiffness matrix")
dump(round(full(K), 3))
info("constraint matrix C1")
dump(round(full(C1), 3))
info("constraint matrix C2")
dump(round(full(C2), 3))
info("force vector")
dump(round(full(f)', 3))
info("constraint vector")
dump(round(full(g)', 3))
end
end
function linear_system_solver_postprocess!(solver, iter, time, K, f, C1, C2, D, g, x, la, ::Type{Val{:after_solution}})
@debug begin
info("solution vector")
dump(round(full(x)', 3))
info("reaction force vector")
dump(round(full(la)', 3))
end
end
@testset "2d frictionless contact" begin
gap = [1.0, 0.0]
nodes = Node[
[6.0, 6.0],
[6.0, 12.0]+gap,
[0.0, 0.0],
[6.0, 0.0],
[6.0, 0.0]+gap,
[18.0, 0.0]+gap]
fel1 = Tri3([1, 3, 4])
fel2 = Tri3([2, 5, 6])
force = Seg2([3, 1])
bnd1 = Seg2([3, 4])
bnd2 = Seg2([5, 6])
sel = Seg2([1, 4])
mel = Seg2([2, 5])
update!([fel1, fel2, force, sel, mel], "geometry", nodes)
update!([bnd1, bnd2], "geometry", nodes)
prob = FieldProblem(PlaneStressLinearElasticityProblem, "bodies", 2)
push!(prob, fel1, fel2)
push!(prob, force)
update!([fel1, fel2], "youngs modulus", 90.0)
update!([fel1, fel2], "poissons ratio", 0.25)
update!([force], "displacement traction force 1", 2*6/sqrt(2))
bc = BoundaryProblem(DirichletProblem, "support", "displacement", 2)
push!(bc, bnd1, bnd2)
update!(get_elements(bc), "displacement", 0.0 => Vector{Float64}[[0.0, 0.0], [0.0, 0.0]])
cont = BoundaryProblem(MortarProblem, "contact", "displacement", 2)
push!(cont, sel, mel)
calculate_normal_tangential_coordinates!(sel, 0.0)
nt = sel("normal-tangential coordinates", [0.0], 0.0)
info("normal direction = $(nt)")
sel["master elements"] = [mel]
# remove coefficients from node 4 (dofs 7-8) because this conflicts with dirichlet bc.
# add_postprocessor!(cont, :remove_constraint_from_dofs, [7, 8])
# remove tangential direction constraints
# add_postprocessor!(cont, :remove_tangential_constraints)
# apply PDASS
add_postprocessor!(cont, :primal_dual_active_set_strategy)
@debug begin
info("fel1.fields = $(fel1.fields)")
end
solver = DirectSolver()
push!(solver, prob)
push!(solver, bc)
push!(solver, cont)
solver.solve_residual = false
set_linear_system_solver!(solver, :UMFPACK)
set_nonlinear_max_iterations!(solver, 5)
add_linear_system_solver_preprocessor!(solver, :before_solution)
add_linear_system_solver_postprocessor!(solver, :after_solution)
add_linear_system_solver_preprocessor!(solver, :dump_matrices)
time = 0.0
call(solver, time)
@debug begin
u = calculate_nodal_vector("displacement", 2, get_elements(prob), time)
info("solution vector")
dump(reshape(round(u, 8), 2, 6))
# la = calculate_nodal_vector("reaction force", 2, get_elements(prob), time)
# info("reaction force")
# dump(reshape(round(la, 8), 2, 6))
end
end
+162 -2
View File
@@ -99,7 +99,8 @@ end
#=
# TODO: if one forget plane_stress solver gives singular exception and it's
hard to trace to the source of problem
@testset "expect clear error when trying to solve 2d model in 3d setting" begin
p1, p2, p3, p4 = get_test_model()
# p1.properties.formulation = :plane_stress
@@ -115,5 +116,164 @@ end
info("u = $u")
@test isapprox(u, [0.0, 0.05])
end
=#
function JuliaFEM.get_mesh(::Type{Val{Symbol("1x1 block splitted to upper and lower")}})
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
upper.elements = create_elements(mesh, "UPPER")
update!(upper.elements, "youngs modulus", 100.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", 100.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.1)
update!(bc_upper.elements, "displacement 2", -0.1)
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)
bc_corner = Problem(Dirichlet, "fix model from lower left corner to prevent singularity", 2, "displacement")
node_ids = find_nearest_nodes(mesh, [0.0, 0.0])
bc_corner.elements = [Element(Poi1, node_ids)]
update!(bc_corner.elements, "geometry", mesh.nodes)
update!(bc_corner.elements, "displacement 1", 0.0)
interface = Problem(Mortar, "interface between upper and lower block", 2, "displacement")
interface_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]
solver = Solver(Nonlinear)
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
call(solver)
@test solver.properties.iteration == 2
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
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)
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.tolerance = tolerance
interface.properties.rotate_normals = rotate_normals
interface.properties.dual_basis = dual_basis
solver = Solver(Nonlinear)
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)
call(solver)
interface = solver["interface between upper and lower block"]
@test solver.properties.iteration == 2
@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)
call(solver)
interface = solver["interface between upper and lower block"]
@test solver.properties.iteration == 2
# differs -- why?
@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)
call(solver)
interface = solver["interface between upper and lower block"]
@test solver.properties.iteration == 2
@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)
call(solver)
interface = solver["interface between upper and lower block"]
@test solver.properties.iteration == 2
@test isapprox(norm(interface.assembly.u), 0.34318800698017704)
end
+82
View File
@@ -0,0 +1,82 @@
# 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.Test
function JuliaFEM.get_model(::Type{Val{Symbol("1x1 plane stress quad4 block")}})
X = Dict{Int, Vector{Float64}}(
1 => [0.0, 0.0],
2 => [1.0, 0.0],
3 => [1.0, 1.0],
4 => [0.0, 1.0])
body = Problem(Elasticity, "body", 2)
body.properties.formulation = :plane_stress
body.elements = [Element(Quad4, [1, 2, 3, 4])]
update!(body.elements, "geometry", X)
update!(body.elements, "youngs modulus", 288.0)
update!(body.elements, "poissons ratio", 1/3)
# boundary conditions
bc_13 = Problem(Dirichlet, "symmetry 13", 2, "displacement")
bc_13.properties.dual_basis = true
bc_13.elements = [Element(Seg2, [1, 2])]
update!(bc_13.elements, "geometry", X)
update!(bc_13.elements, "displacement 2", 0.0)
bc_23 = Problem(Dirichlet, "symmetry 23", 2, "displacement")
bc_23.properties.dual_basis = true
bc_23.elements = [Element(Seg2, [4, 1])]
update!(bc_23.elements, "geometry", X)
update!(bc_23, "displacement 1", 0.0)
solver = Solver(Nonlinear, "1x1 plane stress quad4 block")
push!(solver, body, bc_13, bc_23)
return solver
end
@testset "test dirichlet spc in point" begin
X = Dict{Int, Vector{Float64}}(
1 => [0.0, 0.0],
2 => [1.0, 0.0],
3 => [1.0, 1.0],
4 => [0.0, 1.0])
solver = get_model("1x1 plane stress quad4 block")
update!(solver["symmetry 13"], "displacement 1", 0.0)
update!(solver["symmetry 23"], "displacement 2", 0.0)
nodal_bc = Problem(Dirichlet, "dx=0.5", 2, "displacement")
nodal_bc.elements = [Element(Poi1, [3])]
update!(nodal_bc, "geometry", X)
update!(nodal_bc, "displacement 1", 0.5)
update!(nodal_bc, "displacement 2", 0.0)
push!(solver, nodal_bc)
call(solver)
pel = nodal_bc.elements[1]
la = pel("reaction force", [0.0], 0.0)
info("reaction force: $la")
info(solver["body"].assembly.u)
@test isapprox(pel("displacement", [], 0.0), [0.5, 0.0])
end
@testset "test nodal point force" begin
X = Dict{Int, Vector{Float64}}(
1 => [0.0, 0.0],
2 => [1.0, 0.0],
3 => [1.0, 1.0],
4 => [0.0, 1.0])
solver = get_model("1x1 plane stress quad4 block")
update!(solver["symmetry 13"], "displacement 1", 0.0)
update!(solver["symmetry 23"], "displacement 2", 0.0)
point_load = Element(Poi1, [3])
update!(point_load, "geometry", X)
update!(point_load, "displacement traction force 1", 72.0)
update!(point_load, "displacement traction force 2", 27.0)
push!(solver["body"], point_load)
call(solver)
@test isapprox(point_load("displacement", [], 0.0), [0.5, 0.0])
end
+38 -3
View File
@@ -106,11 +106,46 @@ end
@test mesh["connectivity"][2] == (:SE2, :GRP1, [3, 4])
end
@testset "test reading aster .med file" begin
fn = Pkg.dir("JuliaFEM")*"/geometry/2d_block/BLOCK_1elem.med"
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)
@test haskey(mesh.element_sets, "BLOCK")
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
+4
View File
@@ -121,6 +121,7 @@ function test_von_mises_3D_basic()
info("Calculation finished")
#PyPlot.plot(ee, ss)
#=
plot3D(eig_vals[:, 1], eig_vals[:, 2], eig_vals[:, 3], color="red")
PyPlot.title("Stress path and von Mises yield surface")
PyPlot.xlabel("Eig Stress 1")
@@ -128,6 +129,7 @@ function test_von_mises_3D_basic()
PyPlot.zlabel("Eig Stress 3")
PyPlot.grid()
PyPlot.show()
=#
end
function test_von_mises_planestress_basic()
@@ -228,10 +230,12 @@ function test_von_mises_planestress_basic()
push!(x_vals, s11)
push!(y_vals, s22)
end
#=
PyPlot.plot(x_vals, y_vals)
PyPlot.plot(ee, ss)
PyPlot.grid()
PyPlot.show()
=#
end
# test_von_mises_3D_basic()
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.