mortar tests, bug finding

This commit is contained in:
Jukka Aho
2015-12-17 15:33:51 +02:00
parent c4f1f7fe9c
commit fff0173c2d
16 changed files with 993 additions and 371 deletions
+3 -5
View File
@@ -1,9 +1,7 @@
julia 0.4
BaseTestNext
ForwardDiff
Logging
LightXML
Docile
Lexicon
FactCheck
HDF5
Lint
JLD
File diff suppressed because one or more lines are too long
+6
View File
@@ -6,6 +6,8 @@ This is JuliaFEM -- Finite Element Package
"""
module JuliaFEM
# include("common.jl")
""" JuliaFEM Core module. """
module Core
include("core.jl")
@@ -19,6 +21,10 @@ include("api.jl")
end
module Preprocess
macro debug(msg)
haskey(ENV, "DEBUG") || return
return msg
end
include("abaqus_reader.jl")
include("preprocess_aster_reader.jl")
end
+52
View File
@@ -0,0 +1,52 @@
# This file is a part of JuliaFEM.
# License is MIT: see https://github.com/JuliaFEM/JuliaFEM.jl/blob/master/LICENSE.md
# common routines
"""
A very simple debugging macro. It executes commands if environment variable DEBUG is set.
Usage
-----
Instead of starting session `julia file.jl`, do `DEBUG=1 julia file.jl`.
Or set `export DEBUG=1` for your `.bashrc`.
Running inside code
-------------------
julia> @debug info("moimoi heihei")
will get executed iff environment variable DEBUG is set.
Examples
--------
julia> @debug info("moimoi")
(empty)
julia> ENV["DEBUG"] = 1
julia> @debug info("moimoi")
INFO: moimoi
julia> @debug begin
... info("matrix is")
... dump([1 2; 3 4])
... end
INFO: matrix is
Array(Int64(2,2)) 2x2 Array{Int64,2}:
1 2
3 4
"""
macro debug(msg)
haskey(ENV, "DEBUG") || return
return msg
end
function set_debug_on!()
ENV["DEBUG"] = 1;
end
function set_debug_off!()
pop!(ENV, "DEBUG");
end
export @debug, set_debug_on!, set_debug_off!
+2 -23
View File
@@ -1,31 +1,10 @@
# This file is a part of JuliaFEM.
# License is MIT: see https://github.com/JuliaFEM/JuliaFEM.jl/blob/master/LICENSE.md
using JuliaFEM
import Base: +, -, /, *, push!, convert, getindex, setindex!, length, similar, call, vec, endof, append!
"""
A very simple debugging macro. It prints debug message if environment variable
JULIAFEM_DEBUG is found.
Usage: instead of starting session `julia file.jl` do `DEBUG=1 julia file.jl`.
Or set `export DEBUG=1` for your `.bashrc`.
"""
macro debug(msg)
haskey(ENV, "DEBUG") || return
# return :( println("DEBUG: ", $msg) )
return msg
end
function set_debug_on!()
ENV["DEBUG"] = 1;
end
function set_debug_off!()
pop!(ENV, "DEBUG");
end
export @debug, set_debug_on!, set_debug_off!
using ForwardDiff
autodiffcache = ForwardDiffCache()
# export derivative, jacobian, hessian
+1
View File
@@ -143,6 +143,7 @@ end
function call(solver::DirectSolver, time::Number=0.0)
info("# of field problems: $(length(solver.field_problems))")
info("# of boundary problems: $(length(solver.boundary_problems))")
(length(solver.field_problems) != 0) || error("no field problems defined.")
timing = Dict{ASCIIString, Float64}()
tic(timing, "solver")
+5 -2
View File
@@ -3,8 +3,11 @@
abstract DirichletProblem <: AbstractProblem
function DirichletProblem(parent_field_name, parent_field_dim, dim=1, elements=[])
return BoundaryProblem{DirichletProblem}(parent_field_name, parent_field_dim, dim, elements)
function DirichletProblem(parent_field_name::ASCIIString, parent_field_dim::Int, dim::Int=1, elements=Element[])
return BoundaryProblem{DirichletProblem}("dirichlet boundary", parent_field_name, parent_field_dim, dim, elements)
end
function DirichletProblem(problem_name::ASCIIString, parent_field_name::ASCIIString, parent_field_dim::Int, dim::Int=1, elements=Element[])
return BoundaryProblem{DirichletProblem}(problem_name, parent_field_name, parent_field_dim, dim, elements)
end
function assemble!(assembly::Assembly, problem::BoundaryProblem{DirichletProblem}, element::Element, time::Number)
+10
View File
@@ -264,3 +264,13 @@ function calculate_normal_tangential_coordinates!{E}(element::Element{E}, time::
element["normal-tangential coordinates"] = ntcoords
end
""" Pick values from nodes and set to element according to connectivity. """
function update(element::Element, field_name::ASCIIString, data::Union{Vector, Dict})
element[field_name] = [data[i] for i in get_connectivity(element)]
end
function update(elements::Vector{Element}, field_name::ASCIIString, data::Union{Vector, Dict})
# info("update $field_name for $(length(elements)) elements.")
for element in elements
update(element, field_name, data)
end
end
+13
View File
@@ -78,6 +78,19 @@ function get_integration_points(::TriangularElement, ::Type{Val{2}})
]
end
function get_integration_points(::TriangularElement, ::Type{Val{4}})
# http://math2.uncc.edu/~shaodeng/TEACHING/math5172/Lectures/Lect_15.PDF
# FIXME: something wrong here with weights ..?
[
IntegrationPoint([0.44594849091597, 0.44594849091597], 0.5*0.22338158967801),
IntegrationPoint([0.44594849091597, 0.10810301816807], 0.5*0.22338158967801),
IntegrationPoint([0.10810301816807, 0.44594849091597], 0.5*0.22338158967801),
IntegrationPoint([0.09157621350977, 0.09157621350977], 0.5*0.10995174365532),
IntegrationPoint([0.09157621350977, 0.81684757298046], 0.5*0.10995174365532),
IntegrationPoint([0.81684757298046, 0.09157621350977], 0.5*0.10995174365532)
]
end
function get_integration_points(::TriangularElement, ::Type{Val{5}})
# http://math2.uncc.edu/~shaodeng/TEACHING/math5172/Lectures/Lect_15.PDF
# FIXME: something wrong here with weights ..?
+13 -4
View File
@@ -5,8 +5,8 @@
abstract LinearElasticityProblem <: ElasticityProblem
function LinearElasticityProblem(dim::Int=3, elements=[])
return Problem{LinearElasticityProblem}(dim, elements)
function LinearElasticityProblem(name="linear elasticity", dim::Int=3, elements=[])
return Problem{LinearElasticityProblem}(name, dim, elements)
end
""" Elasticity equations, general 3D case. """
@@ -57,13 +57,22 @@ function assemble!{E<:CG, P<:LinearElasticityProblem}(assembly::Assembly, proble
L = w*T*N*norm(cross(JT[:,1], JT[:,2]))
add!(assembly.force_vector, gdofs, vec(L))
end
for dim in 1:problem.dim
if haskey(element, "displacement traction force $dim")
T = element("displacement traction force $dim", ip, time)
ldofs = gdofs[dim:problem.dim:end]
JT = transpose(J)
L = w*T*N*norm(cross(JT[:,1], JT[:,2]))
add!(assembly.force_vector, ldofs, vec(L))
end
end
end
end
abstract PlaneStressLinearElasticityProblem <: LinearElasticityProblem
function PlaneStressLinearElasticityProblem(dim::Int=2, elements=[])
return Problem{PlaneStressLinearElasticityProblem}(dim, elements)
function PlaneStressLinearElasticityProblem(name="plane stress linear elasticity", dim::Int=2, elements=[])
return Problem{PlaneStressLinearElasticityProblem}(name, dim, elements)
end
""" Elasticity equations, plane stress. """
+151 -35
View File
@@ -3,6 +3,13 @@
# Mortar projection calculation for 2d
macro debug(msg)
haskey(ENV, "DEBUG") || return
return msg
end
""" Find projection from slave nodes to master element, i.e. find xi2 from
master element corresponding to the xi1.
"""
@@ -617,7 +624,18 @@ function project_point_from_plane_to_surface{E}(p::Vector, x0::Vector, Q::Matrix
return theta
end
end
error("project_point_to_auxiliary_plane: did not converge in $max_iterations iterations!")
begin
info("projecting point from auxiliary plane back to surface didn't go very well.")
info("element type: $E")
info("element connectivity: $(get_connectivity(element))")
info("auxiliary plane: x0 = $x0, Q = $Q")
info("point coordinates on plane: $p")
info("element geometry: $x")
info("ph: $ph")
info("normal direction: $n")
info("parameter vector before giving up: $theta")
end
error("project_point_to_surface: did not converge in $max_iterations iterations!")
end
@@ -632,8 +650,12 @@ node_csys
"""
abstract MortarProblem <: AbstractProblem
function MortarProblem(parent_field_name, parent_field_dim, dim=1, elements=[])
return BoundaryProblem{MortarProblem}(parent_field_name, parent_field_dim, dim, elements)
function MortarProblem(parent_field_name::ASCIIString, parent_field_dim::Int, dim::Int=1, elements=[])
return BoundaryProblem{MortarProblem}("mortar problem", parent_field_name, parent_field_dim, dim, elements)
end
function MortarProblem(problem_name::ASCIIString, parent_field_name::ASCIIString, parent_field_dim::Int, dim::Int=1, elements=[])
return BoundaryProblem{MortarProblem}(problem_name, parent_field_name, parent_field_dim, dim, elements)
end
# Mortar assembly
@@ -688,6 +710,31 @@ end
typealias MortarElements3D Union{Tri3, Quad4}
""" Find master elements from list of potential master elements. """
function find_master_elements(slave_element::Element, time::Real)
x0, Q = create_auxiliary_plane(slave_element, time)
Sl = Vector{Float64}[]
for p in slave_element("geometry", time)
push!(Sl, project_point_to_auxiliary_plane(p, x0, Q))
end
S = hcat(Sl...)
master_elements = Element[]
for master_element in slave_element["master elements"]
M = Vector{Float64}[]
for p in master_element("geometry", time)
push!(M, project_point_to_auxiliary_plane(p, x0, Q))
end
M = hcat(M...)
P, neighbours = clip_polygon(S, M)
isa(P, Void) && continue # no clipping
size(P, 2) < 3 && continue # shared edge, no contribution
push!(master_elements, master_element)
end
return master_elements
end
function assemble!{E<:MortarElements3D}(assembly::Assembly, problem::BoundaryProblem{MortarProblem}, slave_element::Element{E}, time::Real)
field_dim = problem.parent_field_dim
field_name = problem.parent_field_name
@@ -702,11 +749,14 @@ function assemble!{E<:MortarElements3D}(assembly::Assembly, problem::BoundaryPro
for p in slave_element("geometry", time)
push!(Sl, project_point_to_auxiliary_plane(p, x0, Q))
end
@debug info("auxiliary plane coords and basis: origo = $x0")
@debug info("basis:")
@debug dump(round(Q, 3))
@debug begin
info("auxiliary plane coords and basis: origo = $x0")
info("basis:")
dump(round(Q, 3))
end
#S = reshape([S...;], 2, size(slave_element)[2])
S = hcat(Sl...)
slave_geom = Field(Vector{Float64}[S[:,j] for j=1:size(S,2)])
for master_element in slave_element["master elements"]
master_dofs = get_gdofs(master_element, field_dim)
@@ -717,11 +767,16 @@ function assemble!{E<:MortarElements3D}(assembly::Assembly, problem::BoundaryPro
end
#M = reshape([M...;], 2, size(master_element)[2])
M = hcat(M...)
master_geom = Field(Vector{Float64}[M[:,j] for j=1:size(M,2)])
P = nothing
neighbours = nothing
@debug info("applying polygon clip algorithm, S & M = ")
@debug dump(round(S, 3))
@debug dump(round(M, 3))
@debug begin
info("applying polygon clip algorithm, S & M = ")
dump(round(S, 3))
dump(round(M, 3))
end
try
P, neighbours = clip_polygon(S, M)
catch
@@ -735,53 +790,114 @@ function assemble!{E<:MortarElements3D}(assembly::Assembly, problem::BoundaryPro
error("cannot continue")
end
isa(P, Void) && continue # no clipping
@debug info("polygon on auxilyary plane: ")
@debug dump(round(P, 3))
C = calculate_polygon_centerpoint(P)
@debug info("center point = $C")
@debug begin
info("polygon coords on auxilyary plane: ")
dump(round(P, 3))
end
if size(P, 2) < 3
# shared edge but no shared volume. skipping
continue
info("this is not polygon at all.")
info("clipping S")
dump(S)
info("clipping M")
dump(M)
error("size(P, 2) < 3")
end
C = calculate_polygon_centerpoint(P)
npts = size(P, 2) # number of vertices in polygon
@debug info("number of vectices in polygon: $npts")
# S = zeros(3, 3)
# M = zeros(3, 3)
@debug begin
info("clip polygon info")
theta = project_point_from_plane_to_surface(C, x0, Q, slave_element, time)
CC = slave_element("geometry", theta[2:3], time)
info("center point on slave: $CC")
info("number of vectices in polygon: $npts")
on_slave = zeros(3, 0)
on_master = zeros(3, 0)
for i=1:size(P, 2)
theta = project_point_from_plane_to_surface(P[:,i], x0, Q, slave_element, time)
on_slave = [on_slave slave_element("geometry", theta[2:3], time)]
theta = project_point_from_plane_to_surface(P[:,i], x0, Q, master_element, time)
on_master = [on_master master_element("geometry", theta[2:3], time)]
end
info("polygon coords projected to slave element")
dump(round(on_slave, 3))
info("polygon coords projected to master element")
dump(round(on_master, 3))
end
for i=1:npts # loop vertices and create temporary integrate cells
xvec = [C[1], P[1, i], P[1, mod(i, npts)+1]]
yvec = [C[2], P[2, i], P[2, mod(i, npts)+1]]
X = hcat(xvec, yvec)'
@debug info("cell $i, coords = ")
@debug dump(round(X, 3))
geom = Field(Vector{Float64}[X[:,j] for j=1:size(X,2)])
@debug begin
on_slave = zeros(3, 0)
on_master = zeros(3, 0)
for j=1:size(X, 2)
theta = project_point_from_plane_to_surface(X[:,j], x0, Q, slave_element, time)
on_slave = [on_slave slave_element("geometry", theta[2:3], time)]
theta = project_point_from_plane_to_surface(X[:,j], x0, Q, master_element, time)
on_master = [on_master master_element("geometry", theta[2:3], time)]
end
info("cell $i coords projected to slave element")
dump(round(on_slave, 3))
info("cell $i coords projected to master element")
dump(round(on_master, 3))
end
# integration cell geometry, i.e., Tri3
cell = Field(Vector{Float64}[X[:,j] for j=1:size(X,2)])
# info("geom = $geom")
for ip in get_integration_points(Tri3, Val{5})
# calculate determiant of jacobian
#dN = get_dbasis(E, ip.xi)
dN = get_dbasis(Tri3, ip.xi)
J = sum([kron(dN[:,j], geom[j]') for j=1:length(geom)])
w = ip.weight*det(J)
# gauss point in auxiliary plane
#N = get_basis(E, ip.xi)
N = get_basis(Tri3, ip.xi)
x = vec(N*geom)
xi = vec(N*cell) # xi defined in auxilary plane
#xi = ip.xi
# info("x = $x")
# find projection of gauss point to master and slave elements
theta1 = project_point_from_plane_to_surface(x, x0, Q, slave_element, time)
theta2 = project_point_from_plane_to_surface(x, x0, Q, master_element, time)
theta1 = project_point_from_plane_to_surface(xi, x0, Q, slave_element, time)
theta2 = project_point_from_plane_to_surface(xi, x0, Q, master_element, time)
xi_slave = theta1[2:3]
xi_master = theta2[2:3]
@debug begin
X_slave = slave_element("geometry", xi_slave, time)
X_master = master_element("geometry", xi_master, time)
info("integration point on slave: $xi_slave => $X_slave")
info("integration point on master: $xi_master => $X_master")
end
# evaluate shape functions values in gauss point and add contribution to matrices
N1 = slave_element(theta1[2:3], time)
N2 = master_element(theta2[2:3], time)
Sm = w*N1'*N1
N1 = slave_element(xi_slave, time)
N2 = master_element(xi_master, time)
# calculate determiant of jacobian
dNC = get_dbasis(Tri3, ip.xi)
dNS = get_dbasis(Quad4, xi_slave)
dNM = get_dbasis(Quad4, xi_master)
JC = sum([kron(dNC[:,j], cell[j]') for j=1:length(cell)])
JN = sum([kron(dNS[:,j], slave_geom[j]') for j=1:length(slave_geom)])
JM = sum([kron(dNM[:,j], master_geom[j]') for j=1:length(master_geom)])
wS = det(JN)
wM = det(JM)
wC = det(JC)
@debug info("weight S = $wS, weight M = $wM, weight C = $wC")
Sm = ip.weight*N1'*N1*wC
# FIXME: master side transpose -- why?
Mm = w*(N1'*N2)'
Mm = ip.weight*(N1'*N2)'*wC
for k=1:field_dim
sd = slave_dofs[k:field_dim:end]
md = master_dofs[k:field_dim:end]
add!(assembly.stiffness_matrix, sd, sd, Sm)
add!(assembly.stiffness_matrix, sd, md, -Mm)
# info("sd = $sd")
# info("md = $md")
end
end
# info("breaking on first")
# break
end
# info("S = \n$S")
# info("M = \n$M")
end
end
+140
View File
@@ -1,6 +1,8 @@
# This file is a part of JuliaFEM.
# License is MIT: see https://github.com/JuliaFEM/JuliaFEM.jl/blob/master/LICENSE.md
using JuliaFEM
using HDF5
function aster_parse_nodes(section::ASCIIString; strip_characters=true)
@@ -53,6 +55,144 @@ function parse(mesh::ASCIIString, ::Type{Val{:CODE_ASTER_MAIL}})
return model
end
function aster_renumber_nodes_!(mesh, node_numbering)
old_nodes = mesh["nodes"]
new_nodes = typeof(old_nodes)()
for (node_id, node_coords) in old_nodes
new_node_id = node_numbering[node_id]
new_nodes[new_node_id] = node_coords
end
mesh["nodes"] = new_nodes
for (elid, (eltype, elset, elcon)) in mesh["connectivity"]
new_elcon = [node_numbering[node_id] for node_id in elcon]
mesh["connectivity"][elid] = (eltype, elset, new_elcon)
end
end
function aster_renumber_nodes!(mesh1, mesh2)
reserved_node_ids = Set(collect(keys(mesh1["nodes"])))
@debug info("already reserved node ids: $reserved_node_ids")
mesh2_node_numbering = Dict{Int64, Int64}()
# find new node ids assigned for mesh 2
k = 1
for node_id in sort(collect(keys(mesh2["nodes"])))
@debug info("mesh2: processing node $node_id")
# if node id is reserved in mesh 1, find new number
if node_id in reserved_node_ids
@debug info("node id conflict, $node_id already defined in mesh 1, renumbering")
while k in reserved_node_ids
k += 1
end
@debug info("mesh2: node $node_id -> $k")
mesh2_node_numbering[node_id] = k
push!(reserved_node_ids, k)
else
mesh2_node_numbering[node_id] = node_id
end
end
@debug info("new node numering:")
@debug println(mesh2_node_numbering)
aster_renumber_nodes_!(mesh2, mesh2_node_numbering)
#=
# create new nodes
mesh2_old_nodes = mesh2["nodes"]
mesh2_new_nodes = typeof(mesh2_old_nodes)()
for (node_id, node_coords) in mesh2_old_nodes
new_node_id = mesh2_node_numbering[node_id]
mesh2_new_nodes[new_node_id] = node_coords
end
mesh2["nodes"] = mesh2_new_nodes
# update connectivity
for (elid, (eltype, elset, elcon)) in mesh2["connectivity"]
new_elcon = [mesh2_node_numbering[node_id] for node_id in elcon]
mesh2["connectivity"][elid] = (eltype, elset, new_elcon)
end
=#
end
function aster_renumber_elements!(mesh1, mesh2)
reserved_element_ids = Set(collect(keys(mesh1["connectivity"])))
@debug info("already reserved element ids: $reserved_element_ids")
mesh2_element_numbering = Dict{Int64, Int64}()
# find new element ids assigned for mesh 2
k = 1
for element_id in sort(collect(keys(mesh2["connectivity"])))
@debug info("mesh2: processing element $element_id")
# if node id is reserved in mesh 1, find new number
if element_id in reserved_element_ids
@debug info("element id conflict, $element_id already defined in mesh 1, renumbering")
while k in reserved_element_ids
k += 1
end
@debug info("mesh2: element $element_id -> $k")
mesh2_element_numbering[element_id] = k
push!(reserved_element_ids, k)
else
mesh2_element_numbering[element_id] = element_id
end
end
@debug info("element numbering for mesh 2:")
@debug info(mesh2_element_numbering)
# create new elements
mesh2_old_elements = mesh2["connectivity"]
mesh2_new_elements = typeof(mesh2_old_elements)()
for (element_id, element_data) in mesh2_old_elements
new_element_id = mesh2_element_numbering[element_id]
mesh2_new_elements[new_element_id] = element_data
end
mesh2["connectivity"] = mesh2_new_elements
end
function aster_combine_meshes(mesh1, mesh2)
# check that meshes are ready to be combined
node_ids_mesh_1 = collect(keys(mesh1["nodes"]))
node_ids_mesh_2 = collect(keys(mesh2["nodes"]))
if length(intersect(node_ids_mesh_1, node_ids_mesh_2)) != 0
error("nodes with same id number in both meshes, failed.")
end
element_ids_mesh_1 = collect(keys(mesh1["connectivity"]))
element_ids_mesh_2 = collect(keys(mesh2["connectivity"]))
if length(intersect(element_ids_mesh_1, element_ids_mesh_2)) != 0
error("elements with same id number in both meshes, failed.")
end
@assert similar(mesh1) == similar(mesh2)
@assert similar(mesh1["nodes"]) == similar(mesh2["nodes"])
@assert similar(mesh1["connectivity"]) == similar(mesh2["connectivity"])
new_mesh = similar(mesh1)
new_mesh["nodes"] = similar(mesh1["nodes"])
new_mesh["connectivity"] = similar(mesh1["connectivity"])
for (node_id, node_coords) in mesh1["nodes"]
new_mesh["nodes"][node_id] = node_coords
end
for (node_id, node_coords) in mesh2["nodes"]
new_mesh["nodes"][node_id] = node_coords
end
for (element_id, element_data) in mesh1["connectivity"]
new_mesh["connectivity"][element_id] = element_data
end
for (element_id, element_data) in mesh2["connectivity"]
new_mesh["connectivity"][element_id] = element_data
end
return new_mesh
end
"""
Code Aster binary file (.med), which is exported from SALOME.
"""
+3
View File
@@ -4,11 +4,13 @@
abstract AbstractProblem
type Problem{T<:AbstractProblem}
name :: ASCIIString
dim :: Int
elements :: Vector{Element}
end
type BoundaryProblem{T<:AbstractProblem}
name :: ASCIIString
parent_field_name :: ASCIIString
parent_field_dim :: Int
dim :: Int
@@ -36,3 +38,4 @@ end
function Base.push!(problem::AllProblems, element::Element)
push!(problem.elements, element)
end
+2 -3
View File
@@ -1,7 +1,7 @@
# This file is a part of JuliaFEM.
# License is MIT: see https://github.com/JuliaFEM/JuliaFEM.jl/blob/master/LICENSE.md
using Base.Test
using BaseTestNext
abstract TestResult
@@ -159,5 +159,4 @@ function print_test_statistics()
return passed, failed, errors, critical
end
export @test, run_test, print_test_statistics
export @test, @testset, @test_throws, run_test, print_test_statistics
+238 -2
View File
@@ -5,7 +5,8 @@ module MortarTests
using JuliaFEM.Test
using JuliaFEM.Core: Element, Seg2, Quad4, Tri3, MortarProblem, Assembly, assemble!
using JuliaFEM.Core: Element, Seg2, Quad4, Tri3, Hex8, MortarProblem, Assembly, assemble!,
get_connectivity, update
using JuliaFEM.Core: PlaneStressElasticityProblem, DirichletProblem, DirectSolver
# 2d stuff
@@ -19,6 +20,7 @@ using JuliaFEM.Core: create_auxiliary_plane, project_point_to_auxiliary_plane,
calculate_normal_tangential_coordinates!,
is_point_inside_convex_polygon
using JuliaFEM.Core: LinearElasticityProblem
function get_test_2d_model()
# this is hand calculated and given as an example in my thesis
@@ -691,6 +693,240 @@ function test_assemble_3d_problem_quad4_3()
@test isapprox(stiffness_matrix, B)
end
test_assemble_3d_problem_quad4_3()
#test_assemble_3d_problem_quad4_3()
function test_3d_problem()
nodes = Vector{Float64}[
[0.0, 0.0, 0.0],
[1.0, 0.0, 0.0],
[1.0, 1.0, 0.0],
[0.0, 1.0, 0.0],
[0.0, 0.0, 0.5],
[1.0, 0.0, 0.5],
[1.0, 1.0, 0.5],
[0.0, 1.0, 0.5],
[0.0, 0.0, 0.5],
[1.0, 0.0, 0.5],
[1.0, 1.0, 0.5],
[0.0, 1.0, 0.5],
[0.0, 0.0, 1.0],
[1.0, 0.0, 1.0],
[1.0, 1.0, 1.0],
[0.0, 1.0, 1.0],
]
el1 = Hex8([1, 2, 3, 4, 5, 6, 7, 8])
el2 = Hex8([9, 10, 11, 12, 13, 14, 15, 16])
sym121 = Quad4([1, 2, 3, 4])
sym131 = Quad4([1, 2, 6, 5])
sym132 = Quad4([9, 10, 14, 13])
sym231 = Quad4([4, 1, 5, 8])
sym232 = Quad4([12, 9, 13, 16])
force = Quad4([14, 15, 16, 13])
l2u = Quad4([5, 6, 7, 8])
u2l = Quad4([9, 10, 11, 12])
elements = Element[el1, el2, sym121, sym131, sym132, sym231, sym232, force, l2u, u2l]
update(elements, "geometry", nodes)
el1["youngs modulus"] = el2["youngs modulus"] = 900.0
el1["poissons ratio"] = el2["poissons ratio"] = 0.25
sym121["displacement 3"] = 0.0
sym131["displacement 2"] = sym132["displacement 2"] = 0.0
sym231["displacement 1"] = sym232["displacement 1"] = 0.0
force["displacement traction force 3"] = -100.0
l2u["master elements"] = Element[u2l]
calculate_normal_tangential_coordinates!(l2u, 0.0)
fb = LinearElasticityProblem("two elastic blocks")
push!(fb, el1, el2, force)
bc = DirichletProblem("symmetry boundaries", "displacement", 3)
push!(bc, sym121, sym131, sym132, sym231, sym232)
tie = MortarProblem("tie contact between bodies", "displacement", 3)
push!(tie, l2u)
solver = DirectSolver("solution of elasticity problem")
push!(solver, fb)
push!(solver, bc)
push!(solver, tie)
solver.nonlinear_problem = false
solver.method = :UMFPACK
call(solver, 0.0)
X = el2("geometry", [1.0, 1.0, 1.0], 0.0)
u = el2("displacement", [1.0, 1.0, 1.0], 0.0)
info("displacement at $X = $u")
@test isapprox(u, 1/36*[1, 1, -4])
end
#test_3d_problem()
#=
@testset "plane quad4 projector tests" begin
a = 1/2
b = 1/3
nodes = Dict{Int64, Vector{Float64}}(
1 => [0.0, 0.0, 0.0],
2 => [1/2, 0.0, 0.0],
3 => [1.0, 0.0, 0.0],
4 => [0.0, 1.0, 0.0],
5 => [1/2, 1.0, 0.0],
6 => [1.0, 1.0, 0.0],
7 => [0.0, 0.0, 0.0],
8 => [1/3, 0.0, 0.0],
9 => [2/3, 0.0, 0.0],
10 => [1.0, 0.0, 0.0],
11 => [0.0, 1/2, 0.0],
12 => [1/3, 1/2, 0.0],
13 => [2/3, 1/2, 0.0],
14 => [1.0, 1/2, 0.0],
15 => [0.0, 1.0, 0.0],
16 => [1/3, 1.0, 0.0],
17 => [2/3, 1.0, 0.0],
18 => [1.0, 1.0, 0.0],
)
sel1 = Quad4([1, 2, 5, 4])
sel2 = Quad4([2, 3, 6, 5])
mel1 = Quad4([7, 8, 12, 11])
mel2 = Quad4([8, 9, 13, 12])
mel3 = Quad4([9, 10, 14, 13])
mel4 = Quad4([11, 12, 16, 15])
mel5 = Quad4([12, 13, 17, 16])
mel6 = Quad4([13, 14, 18, 17])
update(Element[sel1, sel2, mel1, mel2, mel3, mel4, mel5, mel6], "geometry", nodes)
calculate_normal_tangential_coordinates!(sel1, 0.0)
calculate_normal_tangential_coordinates!(sel2, 0.0)
prob = MortarProblem("temperature", 1)
push!(prob, sel1)
push!(prob, sel2)
sel1["master elements"] = [mel1, mel2, mel4, mel5]
sel2["master elements"] = [mel2, mel3, mel5, mel6]
stiffness_matrix = full(assemble(prob, 0.0).stiffness_matrix)*2592*6
info("interface matrix:")
dump(round(stiffness_matrix, 3))
B = [
864 432 0 432 216 0 -420 -375 -15 0 -504 -450 -18 0 -84 -75 -3 0
432 1728 432 216 864 216 -120 -690 -690 -120 -144 -828 -828 -144 -24 -138 -138 -24
0 432 864 0 216 432 0 -15 -375 -420 0 -18 -450 -504 0 -3 -75 -84
432 216 0 864 432 0 -84 -75 -3 0 -504 -450 -18 0 -420 -375 -15 0
216 864 216 432 1728 432 -24 -138 -138 -24 -144 -828 -828 -144 -120 -690 -690 -120
0 216 432 0 432 864 0 -3 -75 -84 0 -18 -450 -504 0 -15 -375 -420
]
info("expected interface matrix:")
dump(round(B, 3))
@test isapprox(stiffness_matrix, B)
end
=#
@testset "plane quad4 projector master 3x3 slave 2x2" begin
a = 1/2
b = 1/3
nodes = Dict{Int64, Vector{Float64}}(
1 => [0*a, 0*a, 0.0],
2 => [1*a, 0*a, 0.0],
3 => [2*a, 0*a, 0.0],
4 => [0*a, 1*a, 0.0],
5 => [1*a, 1*a, 0.0],
6 => [2*a, 1*a, 0.0],
7 => [0*a, 2*a, 0.0],
8 => [1*a, 2*a, 0.0],
9 => [2*a, 2*a, 0.0],
10 => [0*b, 0*b, 0.0],
11 => [1*b, 0*b, 0.0],
12 => [2*b, 0*b, 0.0],
13 => [3*b, 0*b, 0.0],
14 => [0*b, 1*b, 0.0],
15 => [1*b, 1*b, 0.0],
16 => [2*b, 1*b, 0.0],
17 => [3*b, 1*b, 0.0],
18 => [0*b, 2*b, 0.0],
19 => [1*b, 2*b, 0.0],
20 => [2*b, 2*b, 0.0],
21 => [3*b, 2*b, 0.0],
22 => [0*b, 3*b, 0.0],
23 => [1*b, 3*b, 0.0],
24 => [2*b, 3*b, 0.0],
25 => [3*b, 3*b, 0.0],
)
sel1 = Quad4([1, 2, 5, 4])
sel2 = Quad4([2, 3, 6, 5])
sel3 = Quad4([4, 5, 8, 7])
sel4 = Quad4([5, 6, 9, 8])
mel1 = Quad4([10, 11, 15, 14])
mel2 = Quad4([11, 12, 16, 15])
mel3 = Quad4([12, 13, 17, 16])
mel4 = Quad4([14, 15, 19, 18])
mel5 = Quad4([15, 16, 20, 19])
mel6 = Quad4([16, 17, 21, 20])
mel7 = Quad4([18, 19, 23, 22])
mel8 = Quad4([19, 20, 24, 23])
mel9 = Quad4([20, 21, 25, 24])
update(Element[sel1, sel2, sel3, sel4, mel1, mel2, mel3,
mel4, mel5, mel6, mel7, mel8, mel9], "geometry", nodes)
calculate_normal_tangential_coordinates!(sel1, 0.0)
calculate_normal_tangential_coordinates!(sel2, 0.0)
calculate_normal_tangential_coordinates!(sel3, 0.0)
calculate_normal_tangential_coordinates!(sel4, 0.0)
prob = MortarProblem("temperature", 1)
push!(prob, sel1)
push!(prob, sel2)
push!(prob, sel3)
push!(prob, sel4)
master_elements = [mel1, mel2, mel3, mel4, mel5, mel6, mel7, mel8, mel9]
sel1["master elements"] = master_elements
sel2["master elements"] = master_elements
sel3["master elements"] = master_elements
sel4["master elements"] = master_elements
B = sparse(assemble(prob, 0.0).stiffness_matrix, 25, 25)*46656
B = full(B)
D = B[1:9,1:9]
M = B[1:9,10:end]
info("interface matrix D:")
dump(round(D, 3))
info("interface matrix M:")
dump(round(M, 3))
D_expected = [
1296 648 0 648 324 0 0 0 0
648 2592 648 324 1296 324 0 0 0
0 648 1296 0 324 648 0 0 0
648 324 0 2592 1296 0 648 324 0
324 1296 324 1296 5184 1296 324 1296 324
0 324 648 0 1296 2592 0 324 648
0 0 0 648 324 0 1296 648 0
0 0 0 324 1296 324 648 2592 648
0 0 0 0 324 648 0 648 1296]
M_expected = [
-784 -700 -28 0 -700 -625 -25 0 -28 -25 -1 0 0 0 0 0
-224 -1288 -1288 -224 -200 -1150 -1150 -200 -8 -46 -46 -8 0 0 0 0
0 -28 -700 -784 0 -25 -625 -700 0 -1 -25 -28 0 0 0 0
-224 -200 -8 0 -1288 -1150 -46 0 -1288 -1150 -46 0 -224 -200 -8 0
-64 -368 -368 -64 -368 -2116 -2116 -368 -368 -2116 -2116 -368 -64 -368 -368 -64
0 -8 -200 -224 0 -46 -1150 -1288 0 -46 -1150 -1288 0 -8 -200 -224
0 0 0 0 -28 -25 -1 0 -700 -625 -25 0 -784 -700 -28 0
0 0 0 0 -8 -46 -46 -8 -200 -1150 -1150 -200 -224 -1288 -1288 -224
0 0 0 0 0 -1 -25 -28 0 -25 -625 -700 0 -28 -700 -784]
info("D - D_expected")
dump(D - D_expected)
info("M - M_expected")
dump(M - M_expected)
@test isapprox(D, D_expected)
@test isapprox(M, M_expected)
#=
B = [
864 432 0 432 216 0 -420 -375 -15 0 -504 -450 -18 0 -84 -75 -3 0
432 1728 432 216 864 216 -120 -690 -690 -120 -144 -828 -828 -144 -24 -138 -138 -24
0 432 864 0 216 432 0 -15 -375 -420 0 -18 -450 -504 0 -3 -75 -84
432 216 0 864 432 0 -84 -75 -3 0 -504 -450 -18 0 -420 -375 -15 0
216 864 216 432 1728 432 -24 -138 -138 -24 -144 -828 -828 -144 -120 -690 -690 -120
0 216 432 0 432 864 0 -3 -75 -84 0 -18 -450 -504 0 -15 -375 -420
]
info("expected interface matrix:")
dump(round(B, 3))
@test isapprox(stiffness_matrix, B)
=#
end
end
+45 -2
View File
@@ -7,7 +7,8 @@ using JuliaFEM
using JuliaFEM.Test
#using JuliaFEM: parse
using JuliaFEM.Preprocess: aster_parse_nodes
using JuliaFEM.Preprocess: aster_parse_nodes, aster_renumber_nodes!,
aster_renumber_elements!, aster_combine_meshes
function test_read_mesh()
mesh = """
@@ -69,6 +70,48 @@ function test_parse_nodes()
@test nodes[8] == Float64[0.0, 1.0, 1.0]
@test length(nodes) == 8
end
test_parse_nodes()
#test_parse_nodes()
@testset "test combining meshes" begin
mesh1 = Dict{ASCIIString, Any}(
"nodes" => Dict{Int64, Vector{Float64}}(
1 => [1.0, 2.0],
2 => [2.0, 3.0]
),
"connectivity" => Dict{Int64, Tuple{Symbol, Symbol, Vector{Int64}}}(
1 => (:SE2, :GRP1, [1, 2])
),
)
mesh2 = Dict{ASCIIString, Any}(
"nodes" => Dict{Int64, Vector{Float64}}(
1 => [3.0, 4.0],
2 => [4.0, 5.0]
),
"connectivity" => Dict{Int64, Tuple{Symbol, Symbol, Vector{Int64}}}(
1 => (:SE2, :GRP1, [1, 2])
),
)
aster_renumber_nodes!(mesh1, mesh2)
@test length(mesh2["nodes"]) == 2
@test 3 in keys(mesh2["nodes"])
@test 4 in keys(mesh2["nodes"])
@test mesh2["connectivity"][1] == (:SE2, :GRP1, [3, 4])
aster_renumber_elements!(mesh1, mesh2)
@test length(mesh2["connectivity"]) == 1
@test mesh2["connectivity"][2] == (:SE2, :GRP1, [3, 4])
mesh = aster_combine_meshes(mesh1, mesh2)
@test length(mesh["nodes"]) == 4
@test mesh["nodes"][1] == [1.0, 2.0]
@test mesh["nodes"][2] == [2.0, 3.0]
@test mesh["nodes"][3] == [3.0, 4.0]
@test mesh["nodes"][4] == [4.0, 5.0]
@test mesh["connectivity"][1] == (:SE2, :GRP1, [1, 2])
@test mesh["connectivity"][2] == (:SE2, :GRP1, [3, 4])
end
end