2d tie contact working.

This commit is contained in:
Jukka Aho
2015-11-24 03:06:56 +02:00
parent b7af1ebcaa
commit fe55cb0faa
19 changed files with 1531 additions and 199 deletions
+19 -2
View File
@@ -3,9 +3,26 @@
# Functions to handle global assembly of problem
function assemble!(assembly::Assembly, problem::Problem, time::Number=0.0)
empty!(assembly)
function assemble!(assembly::Assembly, problem::Problem, time::Number=0.0, empty_assembly::Bool=true)
if empty_assembly
empty!(assembly)
end
for equation in get_equations(problem)
assemble!(assembly, equation, time, problem)
end
end
function assemble(problem::Problem, time::Number=0.0)
assembly = Assembly()
for equation in get_equations(problem)
assemble!(assembly, equation, time, problem)
end
return assembly
end
function Base.(:+)(ass1::Assembly, ass2::Assembly)
mass_matrix = ass1.mass_matrix + ass2.mass_matrix
stiffness_matrix = ass1.stiffness_matrix + ass2.stiffness_matrix
force_vector = ass1.force_vector + ass2.force_vector
return Assembly(mass_matrix, stiffness_matrix, force_vector)
end
+91 -60
View File
@@ -6,6 +6,7 @@
type DirectSolver <: Solver
field_problems :: Vector{FieldProblem}
boundary_problems :: Vector{BoundaryProblem}
parallel :: Bool
nonlinear_problem :: Bool
max_iterations :: Int64
tol :: Float64
@@ -21,90 +22,120 @@ end
""" Default initializer. """
function DirectSolver()
DirectSolver([], [], true, 10, 1.0e-6)
DirectSolver([], [], false, true, 10, 1.0e-6)
end
""" Call solver to solve a set of problems. """
function call(solver::DirectSolver, time::Number=0.0)
@assert length(solver.field_problems) == 1
@assert length(solver.boundary_problems) == 1
#@assert length(solver.field_problems) == 1
info("# of field problems: $(length(solver.field_problems))")
info("# of boundary problems: $(length(solver.boundary_problems))")
@assert solver.nonlinear_problem == true
problem1 = solver.field_problems[1]
problem2 = solver.boundary_problems[1]
# check that all problems are "same kind"
field_name = get_unknown_field_name(solver.field_problems[1])
field_dim = get_unknown_field_dimension(solver.field_problems[1])
for field_problem in solver.field_problems
get_unknown_field_name(field_problem) == field_name || error("several different fields not supported yet")
get_unknown_field_dimension(field_problem) == field_dim || error("several different field dimensions not supported yet")
end
x = zeros(3)
dx = zeros(3)
dims = nothing
# create initial fields for this increment
# i.e., copy last known values as initial guess
# for this increment
for field_problem in solver.field_problems
for equation in get_equations(field_problem)
element = get_element(equation)
gdofs = get_gdofs(field_problem, equation)
if !isapprox(last(element[field_name]).time, time)
last_data = copy(last(element[field_name]).data)
push!(element[field_name], time => last_data)
end
end
end
for boundary_problem in solver.boundary_problems
for equation in get_equations(boundary_problem)
element = get_element(equation)
gdofs = get_gdofs(boundary_problem, equation)
eqdim = size(equation)[2]
data = Vector{Float64}[zeros(field_dim) for i in 1:eqdim]
if !isapprox(last(element["reaction force"]).time, time)
push!(element["reaction force"], time => data)
end
end
end
dim = 0
for iter=1:solver.max_iterations
tic()
info("Starting iteration $iter")
assembly1 = Assembly()
assemble!(assembly1, problem1, time)
assembly2 = Assembly()
assemble!(assembly2, problem2, time)
A1 = sparse(assembly1.stiffness_matrix)
dims = size(A1)
b1 = sparse(assembly1.force_vector, dims[1], 1)
A2 = sparse(assembly2.stiffness_matrix, dims[1], dims[2])
b2 = sparse(assembly2.force_vector, dims[1], 1)
# create a saddle point problem
A = [A1 A2; A2' zeros(A2)]
b = [b1; b2]
mapper = solver.parallel ? pmap : map
if length(b) != length(x)
info("iter $iter: resizing solution vector")
resize!(x, length(b))
resize!(dx, length(b))
fill!(x, 0.0)
fill!(dx, 0.0)
end
# assemble boundary problems
boundary_assembly = sum(mapper((p)->assemble(p, time), solver.boundary_problems))
boundary_dofs = unique(boundary_assembly.stiffness_matrix.I)
# solve problem, update solution vector
# assemble field problems
# in principle if we want to static condensation we need to pass boundary dofs
# to field problems in order to know which dofs are interior dofs and can be
# condensated.
field_assembly = sum(mapper((p)->assemble(p, time), solver.field_problems))
field_dofs = unique(field_assembly.stiffness_matrix.I)
info("# of dofs: $(length(field_dofs)), # of interface dofs: $(length(boundary_dofs))")
# create sparse matrices and saddle point problem
K = sparse(field_assembly.stiffness_matrix)
dim = size(K, 1)
r = sparse(field_assembly.force_vector, dim, 1)
C = sparse(boundary_assembly.stiffness_matrix, dim, dim)
g = sparse(boundary_assembly.force_vector, dim, 1)
A = [K C'; C spzeros(dim, dim)]
b = [r; g]
# solve increment for linearized problem
nz = unique(rowvals(A)) # take only non-zero rows
dx[nz] = lufact(A[nz,nz]) \ full(b[nz])
x += dx
sol = zeros(b)
sol[nz] = lufact(A[nz,nz]) \ full(b[nz])
info("solved. length of solution vector = $(length(sol))")
#info(full(sol[nz]))
# get "problem-wise" solution vectors
x1 = x[1:dims[1]]
x2 = x[dims[1]+1:end]
# update field for elements in problem 1
for equation in get_equations(problem1)
element = get_element(equation)
field_name = get_unknown_field_name(problem1)
gdofs = get_gdofs(problem1, equation)
local_sol = vec(full(x1[gdofs]))
eqsize = size(equation)
if eqsize[1] != 1
# update elements in field problems
for field_problem in solver.field_problems
for equation in get_equations(field_problem)
element = get_element(equation)
gdofs = get_gdofs(field_problem, equation)
eqsize = size(equation)
local_sol = vec(full(sol[gdofs])) # incremental data for element
local_sol = reshape(local_sol, eqsize)
local_sol = Vector{Float64}[local_sol[:,i] for i=1:size(local_sol,2)]
last(element[field_name]).data += local_sol # <-- added
end
#info("problem1: pushing to $field_name")
push!(element[field_name], time => local_sol)
end
# update field for elements in problem 2 (Dirichlet boundary)
for equation in get_equations(problem2)
element = get_element(equation)
field_name = "reaction force" #get_unknown_field_name(problem2)
gdofs = get_gdofs(problem2, equation)
local_sol = vec(full(x1[gdofs]))
eqsize = size(equation)
if eqsize[1] != 1
local_sol = reshape(local_sol, eqsize)
# update elements in boundary problems
for boundary_problem in solver.boundary_problems
for equation in get_equations(boundary_problem)
element = get_element(equation)
gdofs = get_gdofs(boundary_problem, equation) + dim
eqsize = size(equation)
local_sol = vec(full(sol[gdofs]))
#info("local sol = $local_sol")
local_sol = reshape(local_sol, field_dim, eqsize[2])
local_sol = Vector{Float64}[local_sol[:,i] for i=1:size(local_sol,2)]
last(element["reaction force"]).data = local_sol # <-- replaced
end
#info("problem2: pushing to $field_name")
push!(element[field_name], time => local_sol)
end
if norm(dx[1:dims[1]]) < solver.tol
return (iter, true)
end
info("Iteration took $(toq()) seconds")
if norm(sol[1:dim]) < solver.tol
return (iter, true)
end
end
info("Warning: did not coverge in $(solver.max_iterations) iterations!")
+4 -2
View File
@@ -39,8 +39,10 @@ function Base.size(equation::DBC2D2)
end
function Base.convert(::Type{DirichletEquation}, element::Seg2)
integration_points = line3()
haskey(element, "reaction force") || (element["reaction force"] = 0.0 => zeros(2))
integration_points = get_integration_points(element, Val{3})
if !haskey(element, "reaction force")
element["reaction force"] = (0.0 => Vector{Float64}[])
end
DBC2D2(element, integration_points)
end
+2 -2
View File
@@ -122,7 +122,7 @@ function Base.size(equation::CPS4)
end
function Base.convert(::Type{PlaneStressElasticityEquation}, element::Quad4)
integration_points = get_default_integration_points(element)
integration_points = get_integration_points(element)
if !haskey(element, "displacement")
element["displacement"] = 0.0 => [zeros(2) for i=1:4]
end
@@ -140,7 +140,7 @@ function Base.size(equation::CPS2)
end
function Base.convert(::Type{PlaneStressElasticityEquation}, element::Seg2)
integration_points = get_default_integration_points(element)
integration_points = get_integration_points(element)
if !haskey(element, "displacement")
element["displacement"] = 0.0 => [zeros(2) for i=1:2]
end
+3 -3
View File
@@ -67,9 +67,9 @@ function Base.getindex(element::Element, field_name)
return element.fields[field_name]
end
function get_integration_points(element)
return get_default_integration_points(element)
end
#function get_integration_points(element)
# return get_default_integration_points(element)
#end
"""Add new Field to element.
-8
View File
@@ -11,14 +11,10 @@ type Assembly
mass_matrix :: SparseMatrixIJV
stiffness_matrix :: SparseMatrixIJV
force_vector :: SparseMatrixIJV
lhs :: SparseMatrixIJV
rhs :: SparseMatrixIJV
end
function Assembly()
return Assembly(
SparseMatrixIJV(),
SparseMatrixIJV(),
SparseMatrixIJV(),
SparseMatrixIJV(),
SparseMatrixIJV())
@@ -28,8 +24,6 @@ function Base.empty!(assembly::Assembly)
empty!(assembly.mass_matrix)
empty!(assembly.stiffness_matrix)
empty!(assembly.force_vector)
empty!(assembly.lhs)
empty!(assembly.rhs)
end
function get_mass_matrix
@@ -184,8 +178,6 @@ function assemble!(assembly::Assembly, equation::Equation, time::Number=0.0, pro
return R
end
#info("field = $field")
#info("vec(field) = $(vec(field))")
jacobian, allresults = ForwardDiff.jacobian(calc_R, vec(field), AllResults, cache=autodiffcache)
add!(assembly.stiffness_matrix, gdofs, gdofs, jacobian)
add!(assembly.force_vector, gdofs, -ForwardDiff.value(allresults))
+12
View File
@@ -208,6 +208,18 @@ function Base.similar{T}(field::DVTI, data::Vector{T})
return typeof(field)(newdata)
end
function Base.start(::DVTI)
return 1
end
function Base.next(f::DVTI, state)
return f.data[state], state+1
end
function Base.done(f::DVTI, s)
return s > length(f.data)
end
### Accessing continuous fields
function Base.call(field::CVTI, xi::Vector)
+2 -2
View File
@@ -93,13 +93,13 @@ end
# Conversions element -> equation
function Base.convert(::Type{HeatEquation}, element::Quad4)
integration_points = get_default_integration_points(element)
integration_points = get_integration_points(element)
haskey(element, "temperature") || (element["temperature"] = 0.0 => zeros(4))
DC2D4(element, integration_points)
end
function Base.convert(::Type{HeatEquation}, element::Seg2)
integration_points = get_default_integration_points(element)
integration_points = get_integration_points(element)
haskey(element, "temperature") || (element["temperature"] = 0.0 => zeros(2))
DC2D2(element, integration_points)
end
+13 -10
View File
@@ -1,8 +1,9 @@
# This file is a part of JuliaFEM.
# License is MIT: see https://github.com/JuliaFEM/JuliaFEM.jl/blob/master/LICENSE.md
# Let's drop here all integration schemes and some defaults for different element types
function get_default_integration_points(element::Quad4)
function get_integration_points(Quad4::Element)
[
IntegrationPoint(1.0/sqrt(3.0)*[-1, -1], 1.0),
IntegrationPoint(1.0/sqrt(3.0)*[ 1, -1], 1.0),
@@ -11,21 +12,22 @@ function get_default_integration_points(element::Quad4)
]
end
typealias LineElement Union{Seg2, Seg3}
function line1()
function get_integration_points(element::LineElement, ::Type{Val{1}})
[
IntegrationPoint([0.0], 2.0)
]
end
function line2()
function get_integration_points(element::LineElement, ::Type{Val{2}})
[
IntegrationPoint([-sqrt(1/3)], 1)
IntegrationPoint([+sqrt(1/3)], 1)
]
end
function line3()
function get_integration_points(element::LineElement, ::Type{Val{3}})
[
IntegrationPoint([0.0], 8/9),
IntegrationPoint([-sqrt(3/5)], 5/9),
@@ -33,7 +35,7 @@ function line3()
]
end
function line4()
function get_integration_points(element::LineElement, ::Type{Val{4}})
[
IntegrationPoint([+sqrt(3/7 - 2/7*sqrt(6/5))], (18+sqrt(30))/36)
IntegrationPoint([-sqrt(3/7 - 2/7*sqrt(6/5))], (18+sqrt(30))/36)
@@ -42,7 +44,7 @@ function line4()
]
end
function line5()
function get_integration_points(element::LineElement, ::Type{Val{5}})
[
IntegrationPoint([-1/3*sqrt(5 + 2*sqrt(10/7))], (322-13*sqrt(70))/900),
IntegrationPoint([-1/3*sqrt(5 - 2*sqrt(10/7))], (322+13*sqrt(70))/900),
@@ -52,10 +54,11 @@ function line5()
]
end
function get_default_integration_points(element::Seg2)
return line1()
function get_integration_points(element::Seg2)
return get_integration_points(element, Val{1})
end
function get_default_integration_points(element::MSeg2)
return line3()
function get_integration_points(element::Seg3)
return get_integration_points(element, Val{2})
end
+148 -24
View File
@@ -1,17 +1,132 @@
# This file is a part of JuliaFEM.
# License is MIT: see https://github.com/JuliaFEM/JuliaFEM.jl/blob/master/LICENSE.md
# Mortar equations
# Mortar projection calculation for 2d
""" Find projection from slave nodes to master element, i.e. find xi2 from
master element corresponding to the xi1.
"""
function project_from_slave_to_master(slave::Element, master::Element, xi1::Vector, time::Float64=0.0; max_iterations=5, tol=1.0e-9)
# slave_basis = get_basis(slave)
# slave side geometry and normal direction at xi1
X1 = slave("geometry", xi1, time)
N1 = slave("nodal ntsys", xi1, time)[:,1]
# master side geometry at xi2
master_basis = master.basis.data.basis
master_dbasis = master.basis.data.dbasis
master_geometry = master("geometry")(time)
function X2(xi2)
N = master_basis([xi2])
return sum([N[i]*master_geometry[i] for i=1:length(N)])
end
function dX2(xi2)
dN = master_dbasis([xi2])
return sum([dN[i]*master_geometry[i] for i=1:length(dN)])
end
# master_basis = get_basis(master)
# X2(xi2) = master_basis("geometry", [xi2], time)
# dX2(xi2) = dmaster_basis("geometry", xi2, time)
# equation to solve
R(xi2) = det([X2(xi2)-X1 N1]')
dR(xi2) = det([dX2(xi2) N1]')
# dR = ForwardDiff.derivative(R)
# go!
xi2 = 0.0
for i=1:max_iterations
dxi2 = -R(xi2) / dR(xi2)
xi2 += dxi2
if norm(dxi2) < tol
return Float64[xi2]
end
end
error("find projection from slave to master: did not converge")
end
""" Find projection from master surface to slave point, i.e. find xi1 from slave
element corresponding to the xi2. """
function project_from_master_to_slave(slave::Element, master::Element, xi2::Vector, time::Float64=0.0; max_iterations=5, tol=1.0e-9)
# slave_basis = get_basis(slave)
# slave side geometry and normal direction at xi1
slave_geometry = slave("geometry")(time)
slave_normals = slave("nodal ntsys")(time)
slave_basis = slave.basis.data.basis
slave_dbasis = slave.basis.data.dbasis
function X1(xi1)
N = slave_basis([xi1])
return sum([N[i]*slave_geometry[i] for i=1:length(N)])
end
function dX1(xi1)
dN = slave_dbasis([xi1])
return sum([dN[i]*slave_geometry[i] for i=1:length(dN)])
end
function N1(xi1)
N = slave_basis([xi1])
return sum([N[i]*slave_normals[i] for i=1:length(N)])[:,1]
end
function dN1(xi1)
dN = slave_dbasis([xi1])
return sum([dN[i]*slave_normals[i] for i=1:length(dN)])[:,1]
end
#X1(xi1) = slave_basis("geometry", [xi1], time)
#N1(xi1) = slave_basis("nodal ntsys", [xi1], time)[:,1]
#master_basis = get_basis(master)
# master side geometry at xi2
#X2 = master_basis("geometry", xi2, time)
X2 = master("geometry", xi2, time)
# equation to solve
R(xi1) = det([X1(xi1)-X2 N1(xi1)]')
dR(xi1) = det([dX1(xi1) N1(xi1)]') + det([X1(xi1)-X2 dN1(xi1)]')
#=
info("R(-1.0) = $(R(-1.0))")
info("R( 0.0) = $(R(0.0))")
info("R( 1.0) = $(R(1.0))")
info("R( 1.5) = $(R(1.5))")
info("dR(-1.0) = $(dR(-1.0))")
info("dR( 0.0) = $(dR(0.0))")
info("dR( 1.0) = $(dR(1.0))")
info("dR( 1.5) = $(dR(1.5))")
=#
#dR = ForwardDiff.derivative(R)
# go!
xi1 = 0.0
for i=1:max_iterations
dxi1 = -R(xi1) / dR(xi1)
xi1 += dxi1
if norm(dxi1) < tol
return Float64[xi1]
end
end
error("find projection from master to slave: did not converge")
end
### Mortar equations
abstract MortarEquation <: Equation
function get_unknown_field_name(equation::MortarEquation)
return "reaction force"
end
""" Mortar boundary condition element for 2-dimensional problem, 2 node line segment. """
type MBC2D2 <: MortarEquation
element :: MSeg2
element :: Seg2
integration_points :: Vector{IntegrationPoint}
end
@@ -19,11 +134,16 @@ function Base.size(equation::MBC2D2)
return (1, 2)
end
function Base.convert(::Type{MortarEquation}, element::MSeg2)
return MBC2D2(element, get_default_integration_points(element))
function Base.convert(::Type{MortarEquation}, element::Seg2)
integration_points = get_integration_points(element, Val{3})
if !haskey(element, "reaction force")
element["reaction force"] = (0.0 => Vector{Float64}[])
end
MBC2D2(element, integration_points)
end
# Mortar problem
### Mortar problem
"""
Parameters
@@ -38,26 +158,24 @@ type MortarProblem <: BoundaryProblem
equations :: Vector{MortarEquation}
end
function MortarProblem(dimension::Int=1, equations=[])
MortarProblem("reaction force", dimension, equations)
function MortarProblem(unknown_field_name, unknown_field_dimension::Int=1)
MortarProblem(unknown_field_name, unknown_field_dimension, [])
end
# Mortar projection calculation
""" Find master or "mortar" elements for this slave element. """
function get_master_elements(element::MortarElement)
return element.master_elements
end
# Mortar assembly
function assemble!(assembly::Assembly, equation::MortarEquation, time::Number=0.0, problem=nothing)
isa(problem, Void) && error("Mortar boundary problem needs problem to be defined")
field_dim = problem.unknown_field_dimension
field_name = problem.unknown_field_name
slave_element = get_element(equation)
master_elements = get_master_elements(slave_element)
slave_dofs = get_gdofs(slave_element, field_dim)
slave_basis = get_basis(slave_element)
detJ = det(slave_basis)
dim = size(equation, 1) # number of nodes
slave_dofs = get_gdofs(slave_element, dim)
for master_element in master_elements
master_dofs = get_gdofs(master_element, dim)
for master_element in slave_element["master elements"]
master_dofs = get_gdofs(master_element, field_dim)
xi1a = project_from_master_to_slave(slave_element, master_element, [-1.0])
xi1b = project_from_master_to_slave(slave_element, master_element, [ 1.0])
xi1 = clamp([xi1a xi1b], -1.0, 1.0)
@@ -78,8 +196,14 @@ function assemble!(assembly::Assembly, equation::MortarEquation, time::Number=0.
# add contribution to left hand side
N1 = slave_basis(xi_gauss, time)
N2 = master_basis(xi_projected, time)
add!(assembly.lhs, slave_dofs, slave_dofs, w*N1'*N1)
add!(assembly.lhs, slave_dofs, master_dofs, -w*N1'*N2)
S = w*N1'*N1
M = w*N1'*N2
for i=1:field_dim
sd = slave_dofs[i:field_dim:end]
md = master_dofs[i:field_dim:end]
add!(assembly.stiffness_matrix, sd, sd, S)
add!(assembly.stiffness_matrix, sd, md, -M)
end
end
end
-60
View File
@@ -21,64 +21,4 @@ function MSeg2(connectivity, master_elements=[], biorthogonal=false)
return MSeg2(connectivity, Basis(basis, dbasisdxi), FieldSet(), master_elements)
end
""" Find projection from slave nodes to master element, i.e. find xi2 from
master element corresponding to the xi1.
"""
function project_from_slave_to_master(slave::MortarElement, master::MortarElement, xi1::Vector, time::Float64=0.0; max_iterations=5, tol=1.0e-9)
slave_basis = get_basis(slave)
master_basis = get_basis(master)
# slave side geometry and normal direction at xi1
X1 = slave_basis("geometry", xi1, time)
N1 = slave_basis("nodal ntsys", xi1, time)[:,1]
# master side geometry at xi2
X2(xi2) = master_basis("geometry", [xi2], time)
# dX2(xi2) = dmaster_basis("geometry", xi2, time)
# equation to solve
R(xi2) = det([X2(xi2)-X1 N1]')
# dR(xi2) = det([dX2(xi2) N1]')
dR = ForwardDiff.derivative(R)
# go!
xi2 = 0.0
for i=1:max_iterations
dxi2 = -R(xi2) / dR(xi2)
xi2 += dxi2
if norm(dxi2) < tol
return Float64[xi2]
end
end
error("find projection from slave to master: did not converge")
end
""" Find projection from master surface to slave point, i.e. find xi1 from slave element corresponding to the xi2. """
function project_from_master_to_slave(slave::MortarElement, master::MortarElement, xi2::Vector, time::Float64=0.0; max_iterations=5, tol=1.0e-9)
slave_basis = get_basis(slave)
master_basis = get_basis(master)
# slave side geometry and normal direction at xi1
X1(xi1) = slave_basis("geometry", [xi1], time)
N1(xi1) = slave_basis("nodal ntsys", [xi1], time)[:,1]
# master side geometry at xi2
X2 = master_basis("geometry", xi2, time)
# equation to solve
R(xi1) = det([X1(xi1)-X2 N1(xi1)]')
# dR(xi1) = det([dX1(xi1) N1(xi1)]') + det([X1(xi1)-X2 dN1(xi1)]')
dR = ForwardDiff.derivative(R)
# go!
xi1 = 0.0
for i=1:max_iterations
dxi1 = -R(xi1) / dR(xi1)
xi1 += dxi1
if norm(dxi1) < tol
return Float64[xi1]
end
end
error("find projection from master to slave: did not converge")
end
+1 -1
View File
@@ -170,7 +170,7 @@ function call(solver::SimpleSolver, time::Number=0.0)
local_sol = reshape(local_sol, eqsize)
end
#info("problem2: pushing to $field_name")
push!(element[field_name], time => local_sol)
#push!(element[field_name], time => local_sol)
end
return norm(x1)
+15
View File
@@ -36,6 +36,21 @@ function Base.append!(A::SparseMatrixIJV, I::Vector{Int}, J::Vector{Int}, V::Vec
append!(A.V, V)
end
function Base.isempty(A::SparseMatrixIJV)
return isempty(A.I) && isempty(A.J) && isempty(A.V)
end
function Base.(:+)(A::SparseMatrixIJV, B::SparseMatrixIJV)
if isempty(A)
return B
end
if isempty(B)
return A
end
C = SparseMatrixIJV([A.I;B.I], [A.J;B.J], [A.V;B.V])
return C
end
function Base.full(A::SparseMatrixIJV, args...)
return full(sparse(A.I, A.J, A.V, args...))
end