mirror of
https://github.com/JuliaFEM/JuliaFEM.jl.git
synced 2026-09-27 20:26:58 +00:00
Bug/mortar discretization (#88)
* new mortar segmentation tests which are failing * test_problems_mortar_3d.jl: first test (Tet4) pass * solvers.jl: diagonal of A is now properly filled, if that option is used. Another option is to remove zero rows from matrix system, which is on by default * problems_mortar.jl: added new function diagnose_interface to calculate quantities from interface hopefully revealing bugs in calculation * problems_mortar_3d.jl: added docstring for check_orientation! and removed flooding debug messages not helping to debug anything * solvers.jl: Another way to solve Ax = b * Refactored code to make implementation of Tri6 assemble! easier * Patch test with linear Tet4 elements and quadratic Tet10 elements pass When using quadratic elements, in polygon clipping algorithm element is divided to linear sub-elements as proposed in [Puso2008]. Interpolation of Lagrange multiplier space is done using quadratic shape functions. References ---------- [Puso2008] Puso, Michael A., T. A. Laursen, and Jerome Solberg. "A segment-to-segment mortar contact method for quadratic elements and large deformations." Computer Methods in Applied Mechanics and Engineering 197.6 (2008): 555-566. * increased coverage by adding diagnose_interface * test using dual basis, failing for unknown reason * Fixed dual basis construction for Mortar/Tet4 The coefficient matrix Ae for one particular slave element e is the result performing numerical integration on *all* integration cells associated with this element [Popp2013]. Ae cannot be calculated "cell-wise" like it was done before. Now patch test will pass also using `interface.properties.dual_basis = true` option. Partially integrated slave elements are supported as well. References ---------- [Popp2013] Popp, Alexander, et al. "Improved robustness and consistency of 3D contact algorithms based on a dual mortar approach." Computer Methods in Applied Mechanics and Engineering 264 (2013): 67-80. * Minor modifications to preprocess.jl - removed two functions which are unimplemented (but maybe planned in future) - added function create_node_set_from_element_set!, which can be used, like name suggests, to create a node set from nodes belonging to some set of elements. * solvers.jl: now prints a list of overconstrained nodes which can be easily copy-pasted to problem.assembly.removed_dofs list to solver overconstrained situation manually * Increase code coverage Added a new test which tests dual basis 3d mortar + adjust option when using Tet4 in elasticity problem. * Tet10 + Dual basis still failing, others are working * mortar 3d low level tests * linear surface element projection tests pass * Introduced basis transform constant alpha Tet10 + dual basis patch test still failing, but single element low level routine tests gives expected results with alpha=0.2 * added new integration rule FPG12 for triangular elements * added drop_tolerance option to remove very small values from constraint matrices * Introduced a basis transform matrix T Constructing bi-orthogonal basis for quadratic surfaces is ill-conditioned. By doing a basis transform N' = N*T for slave side displacement vector it's possible to construct a bi-orthogonal basis in a same way than with linear elements. Setting alpha=0.2 ensures that quadratic basis functions are strictly positive in practical cases. * fix 3d clipping test routine, accepts only 3d vertices * dropped number of integration poitns from 12 to 7 in quadratic mortar surfaces intrestingly gives more accurate results, maybe something numerical error in FPG12 integration rule..? * added two displacement patch tests + output writing for all cases * %s/Int64/Int/g * Changed test data location * Fine tuning of logging levels
This commit is contained in:
committed by
Tero Frondelius
parent
a5c093c1d6
commit
f275ce3767
+194
-3
@@ -42,12 +42,14 @@ type Mortar <: BoundaryProblem
|
||||
linear_surface_elements :: Bool
|
||||
split_quadratic_slave_elements :: Bool
|
||||
split_quadratic_master_elements :: Bool
|
||||
alpha :: Float64
|
||||
drop_tolerance :: Float64
|
||||
store_fields :: Vector{Symbol}
|
||||
end
|
||||
|
||||
function Mortar()
|
||||
default_fields = []
|
||||
return Mortar(-1, false, false, false, false, Inf, true, true, true, default_fields)
|
||||
return Mortar(-1, false, false, false, false, Inf, true, true, true, 0.0, 1.0e-9, default_fields)
|
||||
end
|
||||
|
||||
function get_unknown_field_name(problem::Problem{Mortar})
|
||||
@@ -63,13 +65,202 @@ function get_formulation_type(problem::Problem{Mortar})
|
||||
end
|
||||
|
||||
function assemble!(problem::Problem{Mortar}, time::Float64)
|
||||
if length(problem.elements) == 0
|
||||
warn("No elements defined in interface $(problem.name), this will result empty assembly!")
|
||||
return
|
||||
end
|
||||
if problem.properties.dimension == -1
|
||||
problem.properties.dimension = dim = size(first(problem.elements), 1)
|
||||
info("assuming dimension of mesh tie surface is $dim")
|
||||
info("if this is wrong set is manually using problem.properties.dimension")
|
||||
info("Assuming dimension of mesh tie surface is $dim. If this is wrong set is manually using problem.properties.dimension")
|
||||
end
|
||||
dimension = Val{problem.properties.dimension}
|
||||
use_forwarddiff = Val{problem.properties.use_forwarddiff}
|
||||
assemble!(problem, time, dimension, use_forwarddiff)
|
||||
end
|
||||
|
||||
""" Given a CCW ordered set of vertices, calculate area of polygon.
|
||||
|
||||
Examples
|
||||
--------
|
||||
|
||||
julia> P = Vector[[1/3, 5/12, 1/2], [1/3, 1/2, 1/2], [1/2, 1/2, 1/2], [1/2, 1/3, 1/2], [5/12, 1/3, 1/2]]
|
||||
5-element Array{Array{T,1},1}:
|
||||
[0.333333,0.416667,0.5]
|
||||
[0.333333,0.5,0.5]
|
||||
[0.5,0.5,0.5]
|
||||
[0.5,0.333333,0.5]
|
||||
[0.416667,0.333333,0.5]
|
||||
|
||||
julia> A = calculate_polygon_area(P)
|
||||
0.02430555555555556
|
||||
|
||||
julia> isapprox(A, 7/288)
|
||||
true
|
||||
|
||||
"""
|
||||
function calculate_polygon_area(P)
|
||||
N_P = length(P)
|
||||
A = sum([norm(1/2*cross(P[i]-P[1], P[mod(i,N_P)+1]-P[1])) for i=2:N_P])
|
||||
return A
|
||||
end
|
||||
|
||||
""" Function to print useful debug information from interface to find bugs. """
|
||||
function diagnose_interface(problem::Problem{Mortar}, time::Float64)
|
||||
info("Diagnosing Mortar interface...")
|
||||
props = problem.properties
|
||||
field_dim = get_unknown_field_dimension(problem)
|
||||
field_name = get_parent_field_name(problem)
|
||||
slave_elements = get_slave_elements(problem)
|
||||
|
||||
I_area = 0.0
|
||||
|
||||
if props.split_quadratic_slave_elements
|
||||
info("props.split_quadratic_slave_elements = true")
|
||||
if !props.linear_surface_elements
|
||||
warn("Mortar3D: split_quadratic_surfaces = true and linear_surface_elements = false maybe have unexpected behavior")
|
||||
end
|
||||
slave_elements = split_quadratic_elements(slave_elements, time)
|
||||
end
|
||||
info("Number of slave elements in interface: $(length(slave_elements))")
|
||||
|
||||
# 1. calculate nodal normals and tangents for slave element nodes j ∈ S
|
||||
normals = calculate_normals(slave_elements, time, Val{2};
|
||||
rotate_normals=props.rotate_normals)
|
||||
update!(slave_elements, "normal", time => normals)
|
||||
|
||||
S_areas = []
|
||||
C_areas = []
|
||||
P_areas = []
|
||||
|
||||
for slave_element in slave_elements
|
||||
|
||||
info(repeat("-", 80))
|
||||
info("Processing slave element $(slave_element.id), type = $(get_element_type(slave_element))")
|
||||
info(repeat("-", 80))
|
||||
|
||||
S_area = 0.0
|
||||
S_area_in_contact = 0.0
|
||||
for ip in get_integration_points(slave_element)
|
||||
S_area += ip.weight*slave_element(ip, time, Val{:detJ})
|
||||
end
|
||||
info("Total area of slave element = $S_area")
|
||||
|
||||
|
||||
if props.linear_surface_elements
|
||||
info("Converting slave element to linear surface element")
|
||||
slave_element = convert_to_linear_element(slave_element)
|
||||
end
|
||||
|
||||
slave_element_nodes = get_connectivity(slave_element)
|
||||
info("Slave element connectivity = $slave_element_nodes")
|
||||
nsl = length(slave_element)
|
||||
X1 = slave_element("geometry", time)
|
||||
n1 = Field([normals[j] for j in slave_element_nodes])
|
||||
|
||||
# project slave nodes to auxiliary plane (x0, Q)
|
||||
xi = mean(get_reference_coordinates(slave_element))
|
||||
N = vec(get_basis(slave_element, xi, time))
|
||||
x0 = N*X1
|
||||
n0 = N*n1
|
||||
info("Auxiliary plane x0 = $x0, n0 = $n0")
|
||||
S = Vector[project_vertex_to_auxiliary_plane(X1[i], x0, n0) for i=1:nsl]
|
||||
check_orientation!(S, n0)
|
||||
info("Slave element $(slave_element.id) vertices in auxiliary plane: $S")
|
||||
|
||||
# 3. loop all master elements
|
||||
master_elements = slave_element("master elements", time)
|
||||
if props.split_quadratic_master_elements
|
||||
master_elements = split_quadratic_elements(master_elements, time)
|
||||
end
|
||||
|
||||
for master_element in master_elements
|
||||
|
||||
if props.linear_surface_elements
|
||||
master_element = convert_to_linear_element(master_element)
|
||||
end
|
||||
|
||||
master_element_nodes = get_connectivity(master_element)
|
||||
nm = length(master_element)
|
||||
X2 = master_element("geometry", time)
|
||||
|
||||
if norm(mean(X1) - mean(X2)) > problem.properties.distval
|
||||
# elements are "far enough"
|
||||
continue
|
||||
end
|
||||
|
||||
# 3.1 project master nodes to auxiliary plane and create polygon clipping
|
||||
M = Vector[project_vertex_to_auxiliary_plane(X2[i], x0, n0) for i=1:nm]
|
||||
check_orientation!(M, n0)
|
||||
P = get_polygon_clip(S, M, n0)
|
||||
if length(P) < 3
|
||||
if length(P) == 0
|
||||
continue
|
||||
end
|
||||
if length(P) == 1
|
||||
info("length(P) == 1, shared vertex")
|
||||
end
|
||||
if length(P) == 2
|
||||
info("length(P) == 2, shared edge")
|
||||
end
|
||||
continue
|
||||
end
|
||||
info("Master element $(master_element.id) vertices in auxiliary plane = $M")
|
||||
check_orientation!(P, n0)
|
||||
P_area_ = calculate_polygon_area(P)
|
||||
info("Polygon clip found, P=$P, N_P = $(length(P)), area of polygon = $P_area_")
|
||||
if isapprox(P_area_, 0.0)
|
||||
error("Polygon P has zero area: $P_area_")
|
||||
end
|
||||
|
||||
P_area = 0.0
|
||||
|
||||
C0 = calculate_centroid(P)
|
||||
info("Centroid of polygon = $C0")
|
||||
|
||||
# 4. loop integration cells
|
||||
all_cells = get_cells(P, C0)
|
||||
info("Polygon is splitted to $(length(all_cells)) integration cells.")
|
||||
for (cell_id, cell) in enumerate(all_cells)
|
||||
C_area = 0.0
|
||||
virtual_element = Element(Tri3, Int[])
|
||||
update!(virtual_element, "geometry", cell)
|
||||
|
||||
# 5. loop integration point of integration cell
|
||||
for ip in get_integration_points(virtual_element, 3)
|
||||
N = vec(get_basis(virtual_element, ip, time))
|
||||
detJ = virtual_element(ip, time, Val{:detJ})
|
||||
w = ip.weight*detJ
|
||||
# project gauss point from auxiliary plane to master and slave element
|
||||
x_gauss = virtual_element("geometry", ip, time)
|
||||
xi_s, alpha = project_vertex_to_surface(x_gauss, x0, n0, slave_element, X1, time)
|
||||
xi_m, alpha = project_vertex_to_surface(x_gauss, x0, n0, master_element, X2, time)
|
||||
C_area += w
|
||||
end # integration points done
|
||||
info("Cell $cell_id has area of $C_area")
|
||||
P_area += C_area
|
||||
push!(C_areas, C_area)
|
||||
end # integration cells done
|
||||
|
||||
if !isapprox(P_area, P_area_)
|
||||
error("P_area = $P_area, should be $P_area_")
|
||||
end
|
||||
|
||||
S_area_in_contact += P_area
|
||||
push!(P_areas, P_area)
|
||||
|
||||
end # master elements done
|
||||
|
||||
S_perc = S_area_in_contact / S_area * 100.0
|
||||
push!(S_areas, S_area_in_contact)
|
||||
info("Area of slave element in contact: $S_area_in_contact, it's $S_perc % of total element area")
|
||||
|
||||
I_area += S_area_in_contact
|
||||
|
||||
end # slave elements done, contact virtual work ready
|
||||
|
||||
info("Area of interface: $I_area")
|
||||
info("Smallest cell area: $(minimum(C_areas))")
|
||||
info("Smallest polygon area: $(minimum(P_areas))")
|
||||
info("Smallest slave element area in contact: $(minimum(S_areas))")
|
||||
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user