Files
JuliaFEM.jl/src/mortar.jl
T
2015-12-14 02:09:33 +02:00

783 lines
22 KiB
Julia
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# This file is a part of JuliaFEM.
# License is MIT: see https://github.com/JuliaFEM/JuliaFEM.jl/blob/master/LICENSE.md
# 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{S,M}(slave::Element{S}, master::Element{M}, 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("normal-tangential coordinates", xi1, time)[:,1]
# master side geometry at xi2
#master_basis = master.basis.data.basis
#master_dbasis = master.basis.data.dbasis
master_basis(xi) = get_basis(M, [xi])
master_dbasis(xi) = get_dbasis(M, [xi])
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{S,M}(slave::Element{S}, master::Element{M}, 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("normal-tangential coordinates")(time)
#slave_basis = slave.basis.data.basis
#slave_dbasis = slave.basis.data.dbasis
slave_basis(xi) = get_basis(S, [xi])
slave_dbasis(xi) = get_dbasis(S, [xi])
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("normal-tangential coordinates", [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
#info("dxi1 = $dxi1, xi1 = $xi1, norm(dxi1) = $(norm(dxi1))")
if norm(dxi1) < tol
return Float64[xi1]
end
end
error("find projection from master to slave: did not converge")
end
### Mortar projection calculation for 3d cases
"""
Construct auxiliary plane for surface.
Parameters
----------
x::Array{Float64, 2}
Node coordinates
ximp::Array{Float64, 1}
Element mid-point in dimensionless mother element coordinates ξ
normals::Array{Float64, 2}
Normal directions in nodes
Returns
-------
x0, Q
x0::Array{Float64, 1} - origo of auxiliary plane
Q::Array{Float64, 2} - orthogonal basis, first vector is normal direction
and two rest vectors create orthonormal right-handed basis.
Examples
--------
Calculate auxiliary plane given nodal coordinates, midpoint of mother element,
node normals and suitable function space:
julia> xquad = [
... -2.5 2.5 2.0 -2.0
... -2.0 -2.0 2.3 2.0
... 1.0 0.7 0.0 1.0]
julia> m_midpoint = [0.0, 0.0]
julia> normals = [
... 0.05989060 0.0590504 0.225612 0.2445800
... -0.00748633 0.1670810 0.182034 -0.0305725
... 0.99817700 0.9841730 0.957059 0.9691470]
julia> basis(xi) = [
... (1-xi[1])(1-xi[2])/4
... (1+xi[1])(1-xi[2])/4
... (1+xi[1])(1+xi[2])/4
... (1-xi[1])(1+xi[2])/4]'
julia> x0, Q = create_auxiliary_plane(xquad, mmidpoint, normals, basis)
julia> x0
3-element Array{Float64,1}:
0.0
0.075
0.675
julia> Q
3x3 Array{Float64,2}:
0.148586 0.988899 0.0
0.0784519 -0.0117877 0.996848
0.985783 -0.148118 -0.0793325
Notes
-----
- Midpoint in mother element typically (0, 0) for quadrangles and (1/3, 1/3)
for triangles.
- Uses Gram-Schmidt process to find orthogonal basis
- [1](http://www.math.umn.edu/~olver/aims_/qr.pdf)
- [2](http://www.ecs.umass.edu/ece/ece313/Online_help/gram.pdf)
- [3](http://www.terathon.com/code/tangent.html)
"""
# function create_auxiliary_plane(x, ximp, normals, basis)
function create_auxiliary_plane{E}(element::Element{E}, time::Real)
# proj(u, v) = dot(v, u) / dot(u, u) * u
# xi = [1.0/3.0, 1.0/3.0]
xi = get_reference_element_midpoint(E)
x0 = element("geometry", xi, time)
ntbasis = element("normal-tangential coordinates", xi, time)
return x0, ntbasis
#=
n = element("normal-tangential coordinates", xi, time)[:, 1]
n /= norm(n)
# gram-schmidt
u1 = n
j = indmax(abs(u1))
v2 = zeros(3)
v2[mod(j,3)+1] = 1.0
u2 = v2 - proj(u1, v2)
u3 = cross(u1, u2)
t1 = u2/norm(u2)
t2 = u3/norm(u3)
new_basis = [n t1 t2]
return x0, new_basis
=#
end
"""
Project point q onto a plane given by a point p and normal n.
Parameters
----------
q::Array{Float64, 2}
point to project (row vector)
x0::Array{Float64, 2}
origo of plane
n::Array{Float64, 2}
normal vector of plane
Returns
-------
y::Array{Float64, 2}
projected point
Examples
--------
julia> p = [-0.5 -1.0 4.0]'
julia> x0 = [0.0 0.075 0.675]'
julia> n = [0.1485860 0.0784519 0.9857830]'
julia> project_node_to_auxiliary_plane(p, x0, n)
3-element Array{Float64,1}:
0.963455
-1.2447
0.925247
Notes
-----
[1](http://stackoverflow.com/questions/8942950/how-do-i-find-the-orthogonal-projection-of-a-point-onto-a-plane)
"""
function project_point_to_auxiliary_plane(p::Vector, x0::Vector, Q::Matrix)
n = Q[:,1]
ph = p - dot(p-x0, n)*n
qproj = Q'*(ph-x0)
if !isapprox(qproj[1], 0.0; atol=1.0e-12)
info("project_point_to_auxiliary_plane(): point not projected correctly.")
info("p: $p")
info("x0: $x0")
info("Q: \n$Q")
info("qproj: $qproj")
error("Failed to project point to auxiliary plane.")
end
return qproj[2:3]
end
"""
Find edge intersections of two planar arbitrary shape polygons.
Parameters
----------
S::Array{Float64,2}
M::Array{Float64,2}
Matrices with size (2, n) where n is number of vertices of each polygon.
Returns
-------
P::Array{Float64,2}
Intersection points of polygons
n::Array{Float64,2}
Neighbour info matrix with size (ns, mn). This keeps information which
edges of polygons are intersecting. See further explanation in example
below.
Examples
--------
Find intersection points of two triangles:
julia> S = [0 0; 3 0; 0 3]'
julia> M = [-1 1; 2 -1/2; 1 3/2]'
julia> P, n = get_edge_intersections(S, M)
julia> P
2x4 Array{Float64,2}:
1.0 1.75 0.0 0.0
0.0 0.0 0.5 1.25
julia> n
3x3 Array{Int64,2}:
1 1 0
0 0 0
1 0 1)
So intersection points are: (1.00, 0.00), (1.75, 0.00), (0.00, 0.50), (0.00, 1.25).
"Neighbour matrix" can be interpreted as following:
1 1 0 <--> First edge of S intersects edges 1 and 2 of M
0 0 0 <--> Second edge of S doesn't intersect at all
1 0 1 <--> Third edge of S intersects with edges 1 and 3 of M
"""
function get_edge_intersections(S::Matrix, M::Matrix)
ns = size(S, 2)
nm = size(M, 2)
P = zeros(2, 0)
n = zeros(Int64, ns, nm)
k = 0
for i=1:ns
for j=1:nm
b = M[:,j]-S[:,i]
A = [S[:,mod(i,ns)+1]-S[:,i] -M[:,mod(j,nm)+1]+M[:,j]]
if rank(A) == 2
r = A\b
if (r[1]>=0) & (r[1]<=1) & (r[2]>=0) & (r[2]<=1) # intersection found
k += 1
f = S[:,i]+r[1]*(S[:,mod(i,ns)+1] - S[:,i])
f = f''
P = hcat(P, f)
n[i, j] = 1
end
end
end
end
return P, n
end
"""
Find any points laying inside or border of triangle.
Parameters
----------
Y::Array{Float64, 2}
Triangle coordinates in 2×3 matrix
X::Array{Float64, 2}
List of points to test in 2×n matrix
Returns
-------
P::Array{Float64, 2}
List of points in triangle in 2×m matrix, where m is number of points inside triangle
Examples
--------
julia> S = [0.0 0.0; 3.0 0.0; 0.0 3.0]' # triangle corner points
julia> pts = [-1.0 1.0; 2.0 -0.5; 1.0 1.5; 0.5 1.5]' # points to tests
julia> points_in_triangle(S, pts)
2x2 Array{Float64,2}:
1.0 0.5
1.5 1.5
"""
function get_points_inside_triangle(Y::Matrix, X::Matrix)
@assert size(Y, 2) == 3 # "Point in TRIANGLE..."
P = zeros(2, 0)
v0 = Y[:,2] - Y[:,1]
v1 = Y[:,3] - Y[:,1] # find interior points of X in Y
d00 = (v0'*v0)[1]
d01 = (v0'*v1)[1]
d11 = (v1'*v1)[1] # using baricentric coordinates
id = 1/(d00*d11 - d01*d01)
for i=1:size(X, 2)
v2 = X[:,i] - Y[:,1]
d02 = (v0'*v2)[1]
d12 = (v1'*v2)[1]
u = (d11*d02-d01*d12)*id
v = (d00*d12-d01*d02)*id
if (u>=0) & (v>=0) & (u+v<=1) # also include nodes on the boundary
P = hcat(P, X[:,i]'')
end
end
return P
end
"""
Determine is point P inside or on boudary of polygon X.
http://paulbourke.net/geometry/polygonmesh/#insidepoly
"""
function is_point_inside_convex_polygon(P, X)
x, y = P
for i=1:length(X)
x0, y0 = X[i]
x1, y1 = X[mod(i, length(X))+1]
if (y-y0)*(x1-x0) - (x-x0)*(y1-y0) < 0
return false
end
end
return true
end
function get_points_inside_convex_polygon(pts, X)
# TODO: Make more readable
X2 = [X[:,i] for i=1:size(X,2)]
c = filter(P->is_point_inside_convex_polygon(P, X2), [pts[:,i] for i=1:size(pts, 2)])
return length(c) == 0 ? zeros(2, 0) : hcat(c...)
end
""" Return unique objects with some given tolerance. This is used in next function
because traditional unique() command returns row vectors as non-unique if they
differs only a "little".
"""
function uniquetol(P, dim::Int; args...)
@assert dim == 2
items = Vector{Float64}[P[:,i] for i=1:size(P,dim)]
new_items = Vector{Float64}[]
for item in items
has_found = false
for new_item in new_items
if isapprox(item, new_item; args...)
has_found = true
break
end
end
if !has_found
push!(new_items, item)
end
end
return reshape([new_items...;], length(new_items[]), length(new_items))
end
"""
Make polygon clipping of shapes S and M.
Parameters
----------
S::Array{Float64, 2}
M::Array{Float64, 2}
Shapes to clip. Needs to be triangles at the moment.
Returns
-------
Array{Float64, 2}, Array{Float64, 2}
- Polygon vertices in 2×n matrix, sorted in counter-clockwise order.
- 3×3 "neighbouring" matrix, see example.
Examples
--------
julia> S = [0 0; 3 0; 0 3]'
julia> M = [-1 1; 2 -1/2; 2 2]'
julia> P, n = clip_polygon(S, M)
julia> P
2x6 Array{Float64,2}:
0.0 1.0 2.0 2.0 1.25 0.0
0.5 0.0 0.0 1.0 1.75 1.33333,
julia> n
3x3 Array{Int64,2}:
1 0 1 <- first edge of M ([-1 1; 2 -1/2]') intersects with edges 1 and 3 of S ([0 0; 3 0]' and [0 3; 0 0]')
1 1 0 <- second edge of M ([2 -1/2; 2 2]') intersects with edges 1 and 2 of S
0 1 1 <- third edge of M ([2 2; -1 1]') intersects with edgse 2 and 3 of S
"""
function clip_polygon(S::Matrix, M::Matrix)
P1, neighbours = get_edge_intersections(M, S)
#P2 = get_points_inside_triangle(M, S)
#P3 = get_points_inside_triangle(S, M)
P2 = get_points_inside_convex_polygon(M, S)
P3 = get_points_inside_convex_polygon(S, M)
# info("polygon clipping: P1 = $P1")
# info("polygon clipping: P2 = $P2")
# info("polygon clipping: P3 = $P3")
# info("hcat P = $P")
P = hcat(P1, P2, P3)
if length(P) == 0
return nothing, nothing
end
P = uniquetol(P, 2)
meanval = mean(P, 2)
tmp = P .- meanval
angles = atan2(tmp[2,:], tmp[1,:])
angles = reshape(angles, length(angles))
order = sortperm(angles)
return P[:, order], neighbours
end
"""
Calculate polygon geometric center point
Parameters
----------
P::Array{Float64, 2}
Polygon vertices in 2×n matrix
Returns
-------
Array{Float63, 2}
Center point
Examples
--------
julia> P
2x6 Array{Float64,2}:
0.0 1.0 2.0 2.0 1.25 0.0
0.5 0.0 0.0 1.0 1.75 1.33333,
julia> C = get_polygon_cp(P)
2x1 Array{Float64,2}:
1.039740
0.804701
"""
function calculate_polygon_centerpoint(P::Matrix)
n = size(P, 2)
A = 0.0
for i=1:n
A += 1/2*(P[1,i]*P[2,mod(i,n)+1] - P[1,mod(i,n)+1]*P[2,i])
end
Cx = 0.0
Cy = 0.0
for i=1:n
inext = mod(i, n)+1
Cx += 1/(6*A)*(P[1,i] + P[1,inext])*(P[1,i]*P[2,inext] - P[1,inext]*P[2,i])
Cy += 1/(6*A)*(P[2,i] + P[2,inext])*(P[1,i]*P[2,inext] - P[1,inext]*P[2,i])
end
return Float64[Cx, Cy]
end
"""
Project point from auxiliary plane to parametric surface given by (ξ₁, ξ₂)
Parameters
----------
p::Array{Float64,1}
point in auxiliary plane, in (n,t1,t2) coordinate system
x0::Array{Float64,1}
origo of auxiliary plane cs
Q::Array{Float64,2}
basis of auxiliary plane cs
x::Array{Float64,2}
surface node coords
basis::Array{Float64,2}
surface basis functions
dbasis::Array{Float64,2}
partial derivatives of surface basis functions
Returns
-------
Array{Float64,2}
solution vector (d, ξ₁, ξ₂) where d is distance to surface
Examples
--------
Define surface with node points, basis + dbasis
julia> xquad = [
... -2.5 -2.0 1.0
... 2.5 -2.0 0.7
... 2.0 2.3 0.0
... -2.0 2.0 1.0]'
julia> basis(xi) = [
... (1-xi[1])(1-xi[2])/4
... (1+xi[1])(1-xi[2])/4
... (1+xi[1])(1+xi[2])/4
... (1-xi[1])(1+xi[2])/4]
julia> dbasis(xi) = [
... -(1-xi[2])/4 -(1-xi[1])/4
... (1-xi[2])/4 -(1+xi[1])/4
... (1+xi[2])/4 (1+xi[1])/4
... -(1+xi[2])/4 (1-xi[1])/4]
We aim to find point p, which we first project to auxiliary plane defined as following
julia> p = [-2.5 -2.0 1.0]'
julia> x0 = [0.0 0.075 0.675]'
julia> Q = [
... 0.1485860 0.9888990 0.0000000
... 0.0784519 -0.0117877 0.9968480
... 0.9857830 -0.1481180 -0.0793325]
Our projected point is therefore
julia> n = Q[:,1] # first component is normal direction
julia> ph = project_node_to_auxiliary_plane(p, x0, n)
julia> ph = Q'(ph-x0)
julia> ph
3x1 Array{Float64,2}:
1.33264e-7
-2.49593
-2.09424
Our point ph is now in auxiliary plane in n,t1,t2 coordinate system. Next we
project it back to surface defined by xquad*basis
julia> theta = project_point_from_plane_to_surface(ph, x0, Q, xquad, basis, dbasis)
julia> theta
3x1 Array{Float64,2}:
-0.213874
-0.999999
-1.0
We see that our ξ₁ = ξ₂ = -1 so we found first point of xquad
[-2.5 -2.0 1.0]' correctly.
julia> xquad*basis(theta[2:3])
3-element Array{Float64,1}:
-2.5
-2.0
1.0
"""
function project_point_from_plane_to_surface{E}(p::Vector, x0::Vector, Q::Matrix, element::Element{E}, time::Real; max_iterations::Int=10, iter_tol::Float64=1.0e-9)
basis(xi) = get_basis(E, xi)
dbasis(xi) = get_dbasis(E, xi)
x = element("geometry", time)
ph = Q*[0; p] + x0
theta = Float64[0.0, 0.0, 0.0]
n = Q[:,1]
for i=1:max_iterations
b = ph + theta[1]*n - basis(theta[2:3])*x
J = [n -dbasis(theta[2:3])*x]
dtheta = J \ -b
theta += dtheta
if norm(dtheta) < iter_tol
return theta
end
end
error("project_point_to_auxiliary_plane: did not converge in $max_iterations iterations!")
end
### Mortar problem
"""
Parameters
----------
node_csys
coordinate system in node, normal + tangent + "binormal"
in 3d 3x3 matrix, in 2d 2x2 matrix, respectively
"""
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)
end
# Mortar assembly
typealias MortarElements2D Union{Seg2, Seg3}
function assemble!{E<:MortarElements2D}(assembly::Assembly, problem::BoundaryProblem{MortarProblem}, slave_element::Element{E}, time::Real)
# get dimension and name of PARENT field
field_dim = problem.parent_field_dim
field_name = problem.parent_field_name
slave_dofs = get_gdofs(slave_element, field_dim)
for master_element in slave_element["master elements"]
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)
l = 1/2*(xi1[2]-xi1[1])
if abs(l) < 1.0e-6
warn("No contribution")
continue # no contribution
end
master_dofs = get_gdofs(master_element, field_dim)
for ip in get_integration_points(slave_element, Val{5})
w = ip.weight*det(slave_element, ip, time)*l
# integration point on slave side segment
xi_gauss = 1/2*(1-ip.xi)*xi1[1] + 1/2*(1+ip.xi)*xi1[2]
# projected integration point
xi_projected = project_from_slave_to_master(slave_element, master_element, xi_gauss)
# add contribution to left hand side
N1 = slave_element(xi_gauss, time)
N2 = master_element(xi_projected, time)
S = w*N1'*N1
M = w*(N1'*N2)'
# M = w*N1'*N2
# FIXME: why this needs now to be transpose?
# assembly / repeat
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
end
typealias MortarElements3D Union{Tri3, Quad4}
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
slave_dofs = get_gdofs(slave_element, field_dim)
# info("Slave dofs: $slave_dofs")
# info("Field dim: $field_dim")
# create auxiliary plane and project slave nodes to it
# x0 = origo, Q = local basis
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 = reshape([S...;], 2, size(slave_element)[2])
S = hcat(Sl...)
integration_points = get_integration_points(Tri3, Val{5})
for master_element in slave_element["master elements"]
master_dofs = get_gdofs(master_element, field_dim)
# project master nodes to auxiliary plane and create polygon clipping
M = Vector{Float64}[]
for p in master_element("geometry", time)
push!(M, project_point_to_auxiliary_plane(p, x0, Q))
end
#M = reshape([M...;], 2, size(master_element)[2])
M = hcat(M...)
P = nothing
neighbours = nothing
try
P, neighbours = clip_polygon(S, M)
catch
info("polygon clipping failed")
info("S = ")
dump(S)
info("M = ")
dump(M)
info("original Sl = ")
info(Sl)
error("cannot continue")
end
isa(P, Void) && continue # no clipping
# info("polygon on auxilyary plane: ")
# dump(round(P, 3))
C = calculate_polygon_centerpoint(P)
# info("center point = $C")
npts = size(P, 2) # number of vertices in polygon
# info("number of vectices in polygon: $npts")
# S = zeros(3, 3)
# M = zeros(3, 3)
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)'
# info("cell $i, coords = ")
# dump(round(X, 3))
geom = Field(Vector{Float64}[X[:,j] for j=1:size(X,2)])
for ip in integration_points
# 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)
# 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)
# 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
Mm = w*N1'*N2
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
end
# info("S = \n$S")
# info("M = \n$M")
end
end