autodiff version works for 2d

This commit is contained in:
Jukka Aho
2016-02-24 01:20:39 +02:00
parent e2c885c608
commit dc7497c5c9
7 changed files with 100 additions and 177 deletions
+6 -6
View File
@@ -53,19 +53,19 @@ function assemble!(assembly::Assembly, problem::Problem{Dirichlet}, element::Ele
w *= norm(cross(JT[:,1], JT[:,2]))
end
N = element(ip, time)
Phi = (Ae*N')'
g_prev = element(field_name, ip, time)
#info("g_prev = $g_prev")
for i=1:field_dim
ldofs = gdofs[i:field_dim:end]
if haskey(element, field_name*" $i")
g = element(field_name*" $i", ip, time)
if get_formulation_type(problem) == :incremental
g = g - g_prev[i]
# if having incremental formulation need to add previous
# displacement to rhs (solving increment Δu !
haskey(element, "displacement") || continue
g_prev = element(field_name, ip, time)
g -= g_prev[i]
end
#info("g_new = $g")
add!(assembly.g, ldofs, w*g*Phi')
add!(assembly.g, ldofs, w*g*Ae*N')
end
end
+8 -2
View File
@@ -306,7 +306,10 @@ function Base.call(field::DCTV, time::Real)
t0 = field[i-1].time
t1 = field[i].time
if t0 < time < t1
new_data = field[i-1].data + (time-t0)/(t1-t0)*field[i].data
y0 = field[i-1].data
y1 = field[i].data
dt = t1-t0
new_data = y0*(1-(time-t0)/dt) + y1*(1-(t1-time)/dt)
return DCTI(new_data)
end
end
@@ -323,7 +326,10 @@ function Base.call(field::DVTV, time::Float64)
t0 = field[i-1].time
t1 = field[i].time
if t0 < time < t1
new_data = field[i-1].data + (time-t0)/(t1-t0)*field[i].data
y0 = field[i-1].data
y1 = field[i].data
dt = t1-t0
new_data = y0*(1-(time-t0)/dt) + y1*(1-(t1-time)/dt)
return DVTI(new_data)
end
end
+26 -42
View File
@@ -2,38 +2,33 @@
# License is MIT: see https://github.com/JuliaFEM/JuliaFEM.jl/blob/master/LICENSE.md
""" Find segment from slave element corresponding to master element nodes.
x1_, n1_
slave element geometry and normal direction
x2_ master element nodes to project onto slave
Parameters
----------
x1_, n1_
slave element geometry and normal direction
x2
master element node to project onto slave
Returns
-------
xi
dimensionless coordinate on slave corresponding to
projected master
"""
function project_from_master_to_slave{E<:MortarElements2D}(
slave_element::Element{E}, x1_::DVTI, n1_::DVTI, x2::Vector;
tol=1.0e-10, max_iterations=20)
function x1(xi1)
N = get_basis(E, xi1)
return vec(N)*x1_
end
function dx1(xi1)
dN = get_dbasis(E, xi1)
return vec(dN)*x1_
end
function n1(xi1)
N = get_basis(E, xi1)
return vec(N)*n1_
end
function dn1(xi1)
dN = get_dbasis(E, xi1)
return vec(dN)*n1_
end
x1(xi1) = vec(get_basis(E, xi1))*x1_
dx1(xi1) = vec(get_dbasis(E, xi1))*x1_
n1(xi1) = vec(get_basis(E, xi1))*n1_
dn1(xi1) = vec(get_dbasis(E, xi1))*n1_
cross2(a, b) = cross([a; 0], [b; 0])[3]
R(xi1) = cross2(x1(xi1)-x2, n1(xi1))
dR(xi1) = cross2(dx1(xi1), n1(xi1)) + cross2(x1(xi1)-x2, dn1(xi1))
xi1 = 0.0
dxi1 = 0.0
for i=1:max_iterations
@@ -58,19 +53,12 @@ function project_from_slave_to_master{E<:MortarElements2D}(
master_element::Element{E}, x1::Vector, n1::Vector, x2_::DVTI;
tol=1.0e-10, max_iterations=20)
function x2(xi2)
N = get_basis(E, xi2)
return vec(N)*x2_
end
function dx2(xi2)
dN = get_dbasis(E, xi2)
return vec(dN)*x2_
end
x2(xi2) = vec(get_basis(E, xi2))*x2_
dx2(xi2) = vec(get_dbasis(E, xi2))*x2_
cross2(a, b) = cross([a; 0], [b; 0])[3]
R(xi2) = cross2(x2(xi2)-x1, n1)
dR(xi2) = cross2(dx2(xi2), n1)
xi2 = 0.0
dxi2 = 0.0
for i=1:max_iterations
@@ -127,8 +115,7 @@ function assemble!{E<:MortarElements2D}(assembly::Assembly,
end
end
# --> slave side normals in deformed state
n1 = Field(Vector[ForwardDiff.get_value(normals[:,i]/norm(normals[:,i])) for i in slave_element_nodes])
n1 = Field(Vector[normals[:,i]/norm(normals[:,i]) for i in slave_element_nodes])
nnodes = size(slave_element, 2)
lan_tot = zeros(nnodes) # normal pressure
@@ -206,19 +193,16 @@ function assemble!{E<:MortarElements2D}(assembly::Assembly,
n = n1[i]
t = Q'*n
R = [n t]
la_nt = R*la[:,j]
info("node $j, n=$(ForwardDiff.get_value(n)) lan = $(ForwardDiff.get_value(la_nt[1])) gap = $(ForwardDiff.get_value(gap_tot[i]))")
la_nt = R'*la[:,j]
# info("node $j, n=$(ForwardDiff.get_value(n)) lan = $(ForwardDiff.get_value(la_nt[1])) gap = $(ForwardDiff.get_value(gap_tot[i]))")
# if -lan_tot[i] + gap_tot[i] < 0
if -la_nt[1] + gap_tot[i] < 0
if la_nt[1] - gap_tot[i] > 0
info("set node $j active")
C[1,j] += gap_tot[i]
# C[1,j] += la_nt[1] - max(0, la_nt[1] - gap_tot[i])
C[2,j] += la_nt[2]
else
info("set node $j inactive")
C[1,j] += la1[i][1]
C[2,j] += la1[i][2]
C[:,j] = la[:,j]
end
end
+5 -2
View File
@@ -164,6 +164,9 @@ function update_assembly!(problem, u, la)
end
# copy current solutions to previous ones and add/replace new solution
# TODO: here we have couple of options and they needs to be clarified
# for total formulation we are solving total quantity Ku=f while in
# incremental formulation we solve KΔu = f and u = u + Δu
assembly.u_prev = copy(assembly.u)
assembly.la_prev = copy(assembly.la)
if get_formulation_type(problem) == :total
@@ -173,11 +176,11 @@ function update_assembly!(problem, u, la)
elseif get_formulation_type(problem) == :incremental
info("$(problem.name): incremental formulation, adding increment to solution vector")
assembly.u += u
assembly.la = la
assembly.la += la
elseif get_formulation_type(problem) == :forwarddiff
info("$(problem.name): forwarddiff formulation, adding increment to solution vector")
assembly.u += u
assembly.la = la
assembly.la += la
else
info("$(problem.name): unknown formulation type, don't know what to do with results")
error("serious failure with problem formulation: $(get_formulation_type(problem))")
+13 -56
View File
@@ -1,55 +1,6 @@
# This file is a part of JuliaFEM.
# License is MIT: see https://github.com/JuliaFEM/JuliaFEM.jl/blob/master/LICENSE.md
"""
Solve field equations for a single problem with some dofs fixed. This can be used
to test nonlinear element formulations. Dirichlet boundary is assumed to be homogeneous
and degrees of freedom are eliminated. So if boundary condition is known in nodal
points and everything is zero this should be quite good.
"""
function solve!(problem::Problem, free_dofs::Vector{Int}, time::Float64; max_iterations::Int=10, tolerance::Float64=1.0e-12, dump_matrices::Bool=false, callback=nothing)
info("start solver")
assembly = Assembly()
# x = zeros(ga.ndofs)
# dx = fill!(similar(x), 0.0)
# FIXME: better.
x = nothing
dx = nothing
field_name = get_unknown_field_name(problem)
dim = get_unknown_field_dimension(problem)
for i=1:max_iterations
assemble!(assembly, problem, time)
A = sparse(assembly.stiffness_matrix)
b = sparse(assembly.force_vector)
if dump_matrices
dump(full(A))
dump(full(b)')
end
if isa(dx, Void)
x = zeros(length(b))
dx = zeros(length(b))
end
dx[free_dofs] = lufact(A[free_dofs,free_dofs]) \ full(b)[free_dofs]
info("Difference in solution norm: $(norm(dx))")
x += dx
if !(isa(callback, Void))
callback(x)
end
for element in get_elements(problem)
gdofs = get_gdofs(element, problem.dim)
data = full(x[gdofs])
if length(data) != length(element)
data = reshape(data, problem.dim, length(element))
data = [data[:,i] for i=1:size(data,2)]
end
push!(element[field_name], time => data)
end
norm(dx) < tolerance && return
end
error("Did not converge in $max_iterations iterations")
end
""" Simple linear solver for educational purposes. """
type LinearSolver
name :: ASCIIString
@@ -281,17 +232,19 @@ crosspoints.
function get_boundary_assembly(solver::Solver)
ndofs = solver.ndofs
@assert ndofs != 0
Kc = spzeros(ndofs, ndofs)
K = spzeros(ndofs, ndofs)
C1 = spzeros(ndofs, ndofs)
C2 = spzeros(ndofs, ndofs)
D = spzeros(ndofs, ndofs)
f = spzeros(ndofs, 1)
g = spzeros(ndofs, 1)
for problem in get_boundary_problems(solver)
assembly = problem.assembly
Kc_ = sparse(assembly.K, ndofs, ndofs)
K_ = sparse(assembly.K, ndofs, ndofs)
C1_ = sparse(assembly.C1, ndofs, ndofs)
C2_ = sparse(assembly.C2, ndofs, ndofs)
D_ = sparse(assembly.D, ndofs, ndofs)
f_ = sparse(assembly.f, ndofs, 1)
g_ = sparse(assembly.g, ndofs, 1)
# check for overconstraint situation and handle it if possible
already_constrained = get_nonzero_rows(C2)
@@ -303,13 +256,14 @@ function get_boundary_assembly(solver::Solver)
handle_overconstraint_error!(problem, overconstrained_nodes,
overconstrained_dofs, C1, C1_, C2, C2_, D, D_, g, g_)
end
Kc += Kc_
K += K_
C1 += C1_
C2 += C2_
D += D_
f += f_
g += g_
end
return Kc, C1, C2, D, g
return K, C1, C2, D, f, g
end
@@ -323,11 +277,11 @@ function solve_linear_system(solver::Solver, ::Type{Val{:DirectLinearSolver}})
K, f = get_field_assembly(solver)
# assemble boundary problems
Kc, C1, C2, D, g = get_boundary_assembly(solver)
Kb, C1, C2, D, fb, g = get_boundary_assembly(solver)
# construct global system Ax=b and solve using lu factorization
A = [K+Kc C1'; C2 D]
b = [f; g]
A = [K+Kb C1'; C2 D]
b = [f+fb; g]
nz = get_nonzero_rows(A)
x = zeros(length(b))
@@ -394,6 +348,8 @@ function call(solver::Solver)
# 2. start non-linear iterations
for solver.iteration=1:solver.nonlinear_system_max_iterations
info("Starting nonlinear iteration #$(solver.iteration)")
# 2.1 update linearized assemblies (if needed)
for problem in solver.problems
problem.assembly.changed = true # force reassembly
@@ -426,3 +382,4 @@ function call(solver::Solver)
throw(NonlinearConvergenceError(solver))
end
end
+35 -69
View File
@@ -1,39 +1,52 @@
# This file is a part of JuliaFEM.
# License is MIT: see https://github.com/JuliaFEM/JuliaFEM.jl/blob/master/LICENSE.md
module TestDirichletBoundaryCondition
using JuliaFEM.Test
using JuliaFEM.Core: Tri3, Seg2, DirichletProblem, Assembly, assemble, Node,
BiorthogonalBasis
@testset "test dirichlet boundary conditions" begin
using JuliaFEM.Core: Tri3, Seg2, Dirichlet, Assembly, assemble!, Node, Problem
#=
@testset "dirichlet problem in 1 dimension" begin
element = Seg2([1, 2])
element["geometry"] = Node[[1.0, 1.0], [0.0, 1.0]]
element["temperature"] = 0.0
problem = DirichletProblem("temperature", 1)
problem = Problem(Dirichlet, "test problem", 1, "temperature")
push!(problem, element)
assembly = assemble(problem, 0.0)
C1 = full(assembly.C1)
C2 = full(assembly.C2)
g = full(assembly.g)
assemble!(problem, 0.0)
C1 = full(problem.assembly.C1)
info("C1")
dump(C1)
C2 = full(problem.assembly.C2)
g = full(problem.assembly.g)
@test isapprox(C1, C2)
@test isapprox(C1, 1/6*[2 1; 1 2])
@test isapprox(g, [0.0, 0.0])
end
@testset "dirichlet problem using tri3 surface element" begin
element = Tri3([1, 2, 3])
element["geometry"] = Node[[0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0]]
element["temperature"] = 0.0
problem = Problem(Dirichlet, "test problem", 1, "temperature")
push!(problem, element)
assemble!(problem, 0.0)
C1 = full(problem.assembly.C1)
C2 = full(problem.assembly.C2)
@test isapprox(C1, C2)
@test isapprox(C1, 1/24*[2 1 1; 1 2 1; 1 1 2])
end
=#
@testset "dirichlet problem in 2 dimensions" begin
element = Seg2([1, 2])
element["geometry"] = Node[[1.0, 1.0], [0.0, 1.0]]
element["displacement"] = 0.0
problem = DirichletProblem("displacement", 2)
element["displacement 1"] = 0.0
element["displacement 2"] = 0.0
problem = Problem(Dirichlet, "test problem", 2, "displacement")
push!(problem, element)
assembly = assemble(problem, 0.0)
C1 = full(assembly.C1)
C2 = full(assembly.C2)
g = full(assembly.g)
assemble!(problem, 0.0)
C1 = full(problem.assembly.C1)
C2 = full(problem.assembly.C2)
g = full(problem.assembly.g)
@test isapprox(C1, C2)
C1_expected = 1/6*[2 0 1 0; 0 2 0 1; 1 0 2 0; 0 1 0 2]
@test isapprox(C1, C1_expected)
@@ -44,12 +57,12 @@ end
element = Seg2([1, 2])
element["geometry"] = Node[[1.0, 1.0], [0.0, 1.0]]
element["displacement 2"] = 0.0
problem = DirichletProblem("displacement", 2)
problem = Problem(Dirichlet, "test problem", 2, "displacement")
push!(problem, element)
assembly = assemble(problem, 0.0)
C1 = full(assembly.C1)
C2 = full(assembly.C2)
g = full(assembly.g)
assemble!(problem, 0.0)
C1 = full(problem.assembly.C1)
C2 = full(problem.assembly.C2)
g = full(problem.assembly.g)
@test isapprox(C1, C2)
C1_expected = 1/6*[
0 0 0 0
@@ -60,50 +73,3 @@ end
@test isapprox(g, [0.0, 0.0, 0.0, 0.0])
end
@testset "dirichlet problem using tri3 surface element" begin
elem = Tri3([1, 2, 3])
elem["geometry"] = Node[[0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0]]
elem["temperature"] = 0.0
prob = DirichletProblem("temperature", 1)
push!(prob, elem)
ass = assemble(prob, 0.0)
C1 = full(ass.C1)
C2 = full(ass.C2)
@test isapprox(C1, C2)
@test isapprox(C1, 1/24*[2 1 1; 1 2 1; 1 1 2])
end
@testset "dirichlet problem using biorthogonal basis" begin
elem = Tri3([1, 2, 3])
elem["geometry"] = Node[[0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0]]
elem["displacement 1"] = 1.0
prob = DirichletProblem("displacement", 3; basis=BiorthogonalBasis)
push!(prob, elem)
ass = assemble(prob, 0.0)
C1 = full(ass.C1, 9, 9)
C2 = full(ass.C2, 9, 9)
D = full(ass.D, 9, 9)
g = full(ass.g, 9, 1)
C1_expected = eye(9)*1.0/6.0
g_expected = zeros(9)
g_expected[1] = g_expected[4] = g_expected[7] = 1.0/6.0
C2_expected = zeros(9, 9)
D_expected = zeros(9, 9)
C2_expected[1, 1] = 1.0/6.0
D_expected[2, 2] = 1.0/6.0
D_expected[3, 3] = 1.0/6.0
C2_expected[4, 4] = 1.0/6.0
D_expected[5, 5] = 1.0/6.0
D_expected[6, 6] = 1.0/6.0
C2_expected[7, 7] = 1.0/6.0
D_expected[8, 8] = 1.0/6.0
D_expected[9, 9] = 1.0/6.0
@test isapprox(C1, C1_expected)
@test isapprox(C2, C2_expected)
@test isapprox(D, D_expected)
@test isapprox(g, g_expected)
end
end
end
+7
View File
@@ -16,4 +16,11 @@ using JuliaFEM.Core: DCTV, DCTI
@test isapprox(f( 0.9), DCTI(0.9))
@test isapprox(f( 1.0), DCTI(1.0))
@test isapprox(f( 1.5), DCTI(1.0))
f2 = DCTV(0.0 => 0.0, 0.25 => -0.1, 0.50 => -0.1)
@test isapprox(f2(0.0), DCTI(0.0))
@test isapprox(f2(0.25), DCTI(-0.1))
@test isapprox(f2(0.50), DCTI(-0.1))
@test isapprox(f2(0.35), DCTI(-0.1))
end