The Karush-Kuhn-Tucker conditions of mechanical contact are:
where is the gap and is the contact pressure, a Lagrange multiplier variable living on the secondary face. Per (Wohlmuth, 2011) and (Popp and Wall, 2014), the variationally consistent, discretized version of the KKT conditions are:
where indicates the normal direction, denotes the j'th secondary contact interface node, and is the discrete weighted gap, computed by:
where denotes the secondary contact interface, is the j'th lagrange multiplier test function, and is the discretized version of the gap function.
The KKT conditions are enforced using a nonlinear complementarity problem (NCP) function, in this case the most simple such function, , where (implemented with the input parameter c
) is used to balance the size of the gap and the normal contact pressure. If the contact pressure is of order 10000, and the gap is of order .01, then c
should be set to 1e6 in order to bring components of the NCP function onto the same level and achieve optimal convergence in the non-linear solve.
The ComputeWeightedGapCartesianLMMechanicalContact
object computes the weighted gap and applies the KKT conditions using Lagrange multipliers defined in a global Cartesian reference frame. As a consequence, the number of contact constraints at each node will be two, in two-dimensional problems, and three, in three-dimensional problems. The normal contact pressure is obtained by projecting the Lagrange multiplier vector along the normal vector computed from the mortar generation objects. The result is a normal contact constraint, which, in general, will be a function of all (two or three) Cartesian Lagrange multipliers. This methodology only constrains one degree of freedom. The other degree(s) of freedom are constrained by enforcing that tangential tractions are identically zero. Note that, if friction with Cartesian Lagrange multipliers is chosen via ComputeFrictionalForceCartesianLMMechanicalContact, those remaining nodal degrees of freedom are constraint using Coulomb constraints within a semi-smooth Newton approach. Usage of Cartesian Lagrange multipliers is recommended when condensing Lagrange multipliers via the variable condensation preconditioner (VCP) VariableCondensationPreconditioner.
The user can also employ locally oriented Lagrange multipliers ComputeWeightedGapLMMechanicalContact, which minimizes the number of contact constraints for frictionless problems.
Computes the weighted gap that will later be used to enforce the zero-penetration mechanical contact conditions
(modules/contact/test/tests/mortar_cartesian_lms/frictionless-weighted-gap-lm.i)
starting_point = 2e-1
offset = 1e-2
[GlobalParams]
displacements = 'disp_x disp_y'
diffusivity = 1e0
scaling = 1e0
[]
[Mesh]
file = long-bottom-block-1elem-blocks.e
[]
[Variables]
[disp_x]
block = '1 2'
[]
[disp_y]
block = '1 2'
[]
[lm_x]
block = 3
[]
[lm_y]
block = 3
[]
[]
[ICs]
[disp_y]
block = 2
variable = disp_y
value = '${fparse starting_point + offset}'
type = ConstantIC
[]
[]
[Kernels]
[disp_x]
type = MatDiffusion
variable = disp_x
[]
[disp_y]
type = MatDiffusion
variable = disp_y
[]
[]
[Constraints]
[weighted_gap_lm]
type = ComputeWeightedGapCartesianLMMechanicalContact
primary_boundary = 20
secondary_boundary = 10
primary_subdomain = 4
secondary_subdomain = 3
lm_x = lm_x
lm_y = lm_y
variable = lm_x # Shouldn't be needed, but forced by framework
disp_x = disp_x
disp_y = disp_y
use_displaced_mesh = true
c = 1
[]
[normal_x]
type = CartesianMortarMechanicalContact
primary_boundary = 20
secondary_boundary = 10
primary_subdomain = 4
secondary_subdomain = 3
variable = lm_x
secondary_variable = disp_x
component = x
use_displaced_mesh = true
compute_lm_residuals = false
[]
[normal_y]
type = CartesianMortarMechanicalContact
primary_boundary = 20
secondary_boundary = 10
primary_subdomain = 4
secondary_subdomain = 3
variable = lm_y
secondary_variable = disp_y
component = y
use_displaced_mesh = true
compute_lm_residuals = false
[]
[]
[BCs]
[botx]
type = DirichletBC
variable = disp_x
boundary = 40
value = 0.0
[]
[boty]
type = DirichletBC
variable = disp_y
boundary = 40
value = 0.0
[]
[topy]
type = FunctionDirichletBC
variable = disp_y
boundary = 30
function = '${starting_point} * cos(2 * pi / 40 * t) + ${offset}'
[]
[leftx]
type = FunctionDirichletBC
variable = disp_x
boundary = 50
function = '1e-2 * t'
[]
[]
[Executioner]
type = Transient
end_time = 200
dt = 5
dtmin = 5
solve_type = 'PJFNK'
petsc_options = '-snes_converged_reason -ksp_converged_reason -pc_svd_monitor '
'-snes_linesearch_monitor'
petsc_options_iname = '-pc_type -pc_factor_shift_type -pc_factor_shift_amount -mat_mffd_err'
petsc_options_value = 'lu NONZERO 1e-15 1e-5'
l_max_its = 30
l_tol = 1e-03
nl_max_its = 20
line_search = 'none'
snesmf_reuse_base = true
[]
[Debug]
show_var_residual_norms = true
[]
[Outputs]
exodus = true
[]
[Preconditioning]
[smp]
type = SMP
full = true
[]
[]
[Postprocessors]
active = 'num_nl cumulative contact'
[num_nl]
type = NumNonlinearIterations
[]
[cumulative]
type = CumulativeValuePostprocessor
postprocessor = num_nl
[]
[contact]
type = ContactDOFSetSize
variable = lm_y
subdomain = '3'
execute_on = 'nonlinear timestep_end'
[]
[]
(modules/contact/test/tests/mortar_aux_kernels/pressure-aux-frictionless.i)
[GlobalParams]
displacements = 'disp_x disp_y'
volumetric_locking_correction = true
[]
[Mesh]
[left_block]
type = GeneratedMeshGenerator
dim = 2
xmin = -0.35
xmax = -0.05
ymin = -1
ymax = 0
nx = 1
ny = 3
elem_type = QUAD4
[]
[left_block_sidesets]
type = RenameBoundaryGenerator
input = left_block
old_boundary = '0 1 2 3'
new_boundary = '10 11 12 13'
[]
[left_block_sideset_names]
type = RenameBoundaryGenerator
input = left_block_sidesets
old_boundary = '10 11 12 13'
new_boundary = 'l_bottom l_right l_top l_left'
[]
[left_block_id]
type = SubdomainIDGenerator
input = left_block_sideset_names
subdomain_id = 1
[]
[right_block]
type = GeneratedMeshGenerator
dim = 2
xmin = 0
xmax = 0.3
ymin = -1
ymax = 0
nx = 1
ny = 2
elem_type = QUAD4
[]
[right_block_sidesets]
type = RenameBoundaryGenerator
input = right_block
old_boundary = '0 1 2 3'
new_boundary = '20 21 22 23'
[]
[right_block_sideset_names]
type = RenameBoundaryGenerator
input = right_block_sidesets
old_boundary = '20 21 22 23'
new_boundary = 'r_bottom r_right r_top r_left'
[]
[right_block_id]
type = SubdomainIDGenerator
input = right_block_sideset_names
subdomain_id = 2
[]
[combined_mesh]
type = MeshCollectionGenerator
inputs = 'left_block_id right_block_id'
[]
[left_lower]
type = LowerDBlockFromSidesetGenerator
input = combined_mesh
sidesets = '11'
new_block_id = '10001'
new_block_name = 'secondary_lower'
[]
[right_lower]
type = LowerDBlockFromSidesetGenerator
input = left_lower
sidesets = '23'
new_block_id = '10000'
new_block_name = 'primary_lower'
[]
uniform_refine = 1
[]
[Variables]
[lm_x]
block = 'secondary_lower'
use_dual = true
[]
[lm_y]
block = 'secondary_lower'
use_dual = true
[]
[]
[AuxVariables]
[normal_lm]
family = LAGRANGE
order = FIRST
[]
[]
[AuxKernels]
[normal_lm]
type = MortarPressureComponentAux
variable = normal_lm
primary_boundary = '23'
secondary_boundary = '11'
lm_var_x = lm_x
lm_var_y = lm_y
component = 'NORMAL'
boundary = '11'
[]
[]
[Modules/TensorMechanics/Master]
[all]
strain = FINITE
incremental = true
add_variables = true
block = '1 2'
[]
[]
[Functions]
[horizontal_movement]
type = ParsedFunction
expression = '0.1 * t'
[]
[vertical_movement]
type = ConstantFunction
value = '0.0'
[]
[]
[BCs]
[push_left_x]
type = FunctionDirichletBC
variable = disp_x
boundary = 13
function = horizontal_movement
[]
[fix_right_x]
type = DirichletBC
variable = disp_x
boundary = 21
value = 0.0
[]
[fix_right_y]
type = DirichletBC
variable = disp_y
boundary = 21
value = 0.0
[]
[push_left_y]
type = FunctionDirichletBC
variable = disp_y
boundary = 13
function = vertical_movement
[]
[]
[Materials]
[elasticity_tensor_left]
type = ComputeIsotropicElasticityTensor
block = 1
youngs_modulus = 1.0e6
poissons_ratio = 0.3
[]
[stress_left]
type = ComputeFiniteStrainElasticStress
block = 1
[]
[elasticity_tensor_right]
type = ComputeIsotropicElasticityTensor
block = 2
youngs_modulus = 1.0e6
poissons_ratio = 0.3
[]
[stress_right]
type = ComputeFiniteStrainElasticStress
block = 2
[]
[]
[Constraints]
[weighted_gap_lm]
type = ComputeWeightedGapCartesianLMMechanicalContact
primary_boundary = '23'
secondary_boundary = '11'
primary_subdomain = 'primary_lower'
secondary_subdomain = 'secondary_lower'
lm_x = lm_x
lm_y = lm_y
variable = lm_x # This can be anything really
disp_x = disp_x
disp_y = disp_y
use_displaced_mesh = true
correct_edge_dropping = true
interpolate_normals = false
[]
[normal_x]
type = CartesianMortarMechanicalContact
primary_boundary = '23'
secondary_boundary = '11'
primary_subdomain = 'primary_lower'
secondary_subdomain = 'secondary_lower'
variable = lm_x
secondary_variable = disp_x
component = x
use_displaced_mesh = true
compute_lm_residuals = false
correct_edge_dropping = true
[]
[normal_y]
type = CartesianMortarMechanicalContact
primary_boundary = '23'
secondary_boundary = '11'
primary_subdomain = 'primary_lower'
secondary_subdomain = 'secondary_lower'
variable = lm_y
secondary_variable = disp_y
component = y
use_displaced_mesh = true
compute_lm_residuals = false
correct_edge_dropping = true
[]
[]
[Preconditioning]
[smp]
type = SMP
full = true
[]
[]
[Executioner]
type = Transient
solve_type = 'NEWTON'
petsc_options_iname = '-pc_type -pc_factor_mat_solver_package -mat_mffd_err -pc_factor_shift_type '
'-pc_factor_shift_amount'
petsc_options_value = 'lu superlu_dist 1e-5 NONZERO 1e-10'
line_search = none
dt = 0.1
dtmin = 0.1
end_time = 1.0
l_max_its = 100
nl_max_its = 20
nl_rel_tol = 1e-6
snesmf_reuse_base = false
[]
[Outputs]
exodus = false
csv = true
execute_on = 'FINAL'
[]
[VectorPostprocessors]
[normal_lm]
type = NodalValueSampler
block = 'secondary_lower'
variable = normal_lm
sort_by = 'id'
[]
[]
(modules/contact/test/tests/mortar_aux_kernels/pressure-aux-frictionless-3d.i)
starting_point = 0.25
offset = 0.00
[GlobalParams]
displacements = 'disp_x disp_y disp_z'
diffusivity = 1e0
scaling = 1e0
[]
[Problem]
# error_on_jacobian_nonzero_reallocation = true
[]
[Mesh]
second_order = false
[top_block]
type = GeneratedMeshGenerator
dim = 3
nx = 3
ny = 3
nz = 3
xmin = -0.25
xmax = 0.25
ymin = -0.25
ymax = 0.25
zmin = -0.25
zmax = 0.25
elem_type = HEX8
[]
[rotate_top_block]
type = TransformGenerator
input = top_block
transform = ROTATE
vector_value = '0 0 0'
[]
[top_block_sidesets]
type = RenameBoundaryGenerator
input = rotate_top_block
old_boundary = '0 1 2 3 4 5'
new_boundary = 'top_bottom top_back top_right top_front top_left top_top'
[]
[top_block_id]
type = SubdomainIDGenerator
input = top_block_sidesets
subdomain_id = 1
[]
[bottom_block]
type = GeneratedMeshGenerator
dim = 3
nx = 10
ny = 10
nz = 2
xmin = -.5
xmax = .5
ymin = -.5
ymax = .5
zmin = -.3
zmax = -.25
elem_type = HEX8
[]
[bottom_block_id]
type = SubdomainIDGenerator
input = bottom_block
subdomain_id = 2
[]
[bottom_block_change_boundary_id]
type = RenameBoundaryGenerator
input = bottom_block_id
old_boundary = '0 1 2 3 4 5'
new_boundary = '100 101 102 103 104 105'
[]
[combined]
type = MeshCollectionGenerator
inputs = 'top_block_id bottom_block_change_boundary_id'
[]
[block_rename]
type = RenameBlockGenerator
input = combined
old_block = '1 2'
new_block = 'top_block bottom_block'
[]
[bottom_right_sideset]
type = SideSetsAroundSubdomainGenerator
input = block_rename
new_boundary = bottom_right
block = bottom_block
normal = '1 0 0'
[]
[bottom_left_sideset]
type = SideSetsAroundSubdomainGenerator
input = bottom_right_sideset
new_boundary = bottom_left
block = bottom_block
normal = '-1 0 0'
[]
[bottom_top_sideset]
type = SideSetsAroundSubdomainGenerator
input = bottom_left_sideset
new_boundary = bottom_top
block = bottom_block
normal = '0 0 1'
[]
[bottom_bottom_sideset]
type = SideSetsAroundSubdomainGenerator
input = bottom_top_sideset
new_boundary = bottom_bottom
block = bottom_block
normal = '0 0 -1'
[]
[bottom_front_sideset]
type = SideSetsAroundSubdomainGenerator
input = bottom_bottom_sideset
new_boundary = bottom_front
block = bottom_block
normal = '0 1 0'
[]
[bottom_back_sideset]
type = SideSetsAroundSubdomainGenerator
input = bottom_front_sideset
new_boundary = bottom_back
block = bottom_block
normal = '0 -1 0'
[]
[secondary]
input = bottom_back_sideset
type = LowerDBlockFromSidesetGenerator
sidesets = 'top_bottom' # top_back top_left'
new_block_id = '10001'
new_block_name = 'secondary_lower'
[]
[primary]
input = secondary
type = LowerDBlockFromSidesetGenerator
sidesets = 'bottom_top'
new_block_id = '10000'
new_block_name = 'primary_lower'
[]
[]
[Variables]
[disp_x]
block = '1 2'
[]
[disp_y]
block = '1 2'
[]
[disp_z]
block = '1 2'
[]
[lm_x]
block = 'secondary_lower'
use_dual = true
[]
[lm_y]
block = 'secondary_lower'
use_dual = true
[]
[lm_z]
block = 'secondary_lower'
use_dual = true
[]
[]
[AuxVariables]
[normal_lm]
family = LAGRANGE
order = FIRST
[]
[]
[AuxKernels]
[normal_lm]
type = MortarPressureComponentAux
variable = normal_lm
primary_boundary = 'bottom_top'
secondary_boundary = 'top_bottom'
lm_var_x = lm_x
lm_var_y = lm_y
lm_var_z = lm_z
component = 'NORMAL'
boundary = 'top_bottom'
[]
[]
[ICs]
[disp_z]
block = 1
variable = disp_z
value = '${fparse offset}'
type = ConstantIC
[]
[disp_x]
block = 1
variable = disp_x
value = 0
type = ConstantIC
[]
[disp_y]
block = 1
variable = disp_y
value = 0
type = ConstantIC
[]
[]
[Kernels]
[disp_x]
type = MatDiffusion
variable = disp_x
[]
[disp_y]
type = MatDiffusion
variable = disp_y
[]
[disp_z]
type = MatDiffusion
variable = disp_z
[]
[]
[Constraints]
[weighted_gap_lm]
type = ComputeWeightedGapCartesianLMMechanicalContact
primary_boundary = 'bottom_top'
secondary_boundary = 'top_bottom'
primary_subdomain = 'primary_lower'
secondary_subdomain = 'secondary_lower'
lm_x = lm_x
lm_y = lm_y
lm_z = lm_z
variable = lm_x # This can be anything really
disp_x = disp_x
disp_y = disp_y
disp_z = disp_z
use_displaced_mesh = true
correct_edge_dropping = true
c = 1e+02
[]
[normal_x]
type = CartesianMortarMechanicalContact
primary_boundary = 'bottom_top'
secondary_boundary = 'top_bottom'
primary_subdomain = 'primary_lower'
secondary_subdomain = 'secondary_lower'
variable = lm_x
secondary_variable = disp_x
component = x
use_displaced_mesh = true
compute_lm_residuals = false
correct_edge_dropping = true
[]
[normal_y]
type = CartesianMortarMechanicalContact
primary_boundary = 'bottom_top'
secondary_boundary = 'top_bottom'
primary_subdomain = 'primary_lower'
secondary_subdomain = 'secondary_lower'
variable = lm_y
secondary_variable = disp_y
component = y
use_displaced_mesh = true
compute_lm_residuals = false
correct_edge_dropping = true
[]
[normal_z]
type = CartesianMortarMechanicalContact
primary_boundary = 'bottom_top'
secondary_boundary = 'top_bottom'
primary_subdomain = 'primary_lower'
secondary_subdomain = 'secondary_lower'
variable = lm_z
secondary_variable = disp_z
component = z
use_displaced_mesh = true
compute_lm_residuals = false
correct_edge_dropping = true
[]
[]
[BCs]
[botx]
type = DirichletBC
variable = disp_x
boundary = 'bottom_left bottom_right bottom_front bottom_back'
value = 0.0
[]
[boty]
type = DirichletBC
variable = disp_y
boundary = 'bottom_left bottom_right bottom_front bottom_back'
value = 0.0
[]
[botz]
type = DirichletBC
variable = disp_z
boundary = 'bottom_left bottom_right bottom_front bottom_back'
value = 0.0
[]
[topx]
type = DirichletBC
variable = disp_x
boundary = 'top_top'
value = 0.0
[]
[topy]
type = DirichletBC
variable = disp_y
boundary = 'top_top'
value = 0.0
[]
[topz]
type = FunctionDirichletBC
variable = disp_z
boundary = 'top_top'
function = '-${starting_point} * sin(2 * pi / 40 * t) + ${offset}'
[]
[]
[Preconditioning]
[smp]
type = SMP
full = true
[]
[]
[Executioner]
type = Transient
solve_type = 'NEWTON'
petsc_options_iname = '-pc_type -pc_factor_mat_solver_package -mat_mffd_err -pc_factor_shift_type '
'-pc_factor_shift_amount'
petsc_options_value = 'lu superlu_dist 1e-5 NONZERO 1e-10'
end_time = 1
dt = .5
dtmin = .01
l_max_its = 100
nl_max_its = 30
# nl_rel_tol = 1e-6
nl_abs_tol = 1e-12
line_search = 'none'
snesmf_reuse_base = false
[]
[Debug]
show_var_residual_norms = true
[]
[Outputs]
exodus = false
csv = true
execute_on = 'FINAL'
[]
[VectorPostprocessors]
[normal_lm]
type = NodalValueSampler
block = secondary_lower
variable = normal_lm
sort_by = 'id'
[]
[]
(modules/contact/test/tests/mortar_cartesian_lms/frictionless-mortar-3d.i)
starting_point = 0.25
offset = 0.00
[GlobalParams]
displacements = 'disp_x disp_y disp_z'
diffusivity = 1e0
scaling = 1e0
[]
[Mesh]
second_order = false
[top_block]
type = GeneratedMeshGenerator
dim = 3
nx = 3
ny = 3
nz = 3
xmin = -0.25
xmax = 0.25
ymin = -0.25
ymax = 0.25
zmin = -0.25
zmax = 0.25
elem_type = HEX8
[]
[rotate_top_block]
type = TransformGenerator
input = top_block
transform = ROTATE
vector_value = '0 0 0'
[]
[top_block_sidesets]
type = RenameBoundaryGenerator
input = rotate_top_block
old_boundary = '0 1 2 3 4 5'
new_boundary = 'top_bottom top_back top_right top_front top_left top_top'
[]
[top_block_id]
type = SubdomainIDGenerator
input = top_block_sidesets
subdomain_id = 1
[]
[bottom_block]
type = GeneratedMeshGenerator
dim = 3
nx = 10
ny = 10
nz = 2
xmin = -.5
xmax = .5
ymin = -.5
ymax = .5
zmin = -.3
zmax = -.25
elem_type = HEX8
[]
[bottom_block_id]
type = SubdomainIDGenerator
input = bottom_block
subdomain_id = 2
[]
[bottom_block_change_boundary_id]
type = RenameBoundaryGenerator
input = bottom_block_id
old_boundary = '0 1 2 3 4 5'
new_boundary = '100 101 102 103 104 105'
[]
[combined]
type = MeshCollectionGenerator
inputs = 'top_block_id bottom_block_change_boundary_id'
[]
[block_rename]
type = RenameBlockGenerator
input = combined
old_block = '1 2'
new_block = 'top_block bottom_block'
[]
[bottom_right_sideset]
type = SideSetsAroundSubdomainGenerator
input = block_rename
new_boundary = bottom_right
block = bottom_block
normal = '1 0 0'
[]
[bottom_left_sideset]
type = SideSetsAroundSubdomainGenerator
input = bottom_right_sideset
new_boundary = bottom_left
block = bottom_block
normal = '-1 0 0'
[]
[bottom_top_sideset]
type = SideSetsAroundSubdomainGenerator
input = bottom_left_sideset
new_boundary = bottom_top
block = bottom_block
normal = '0 0 1'
[]
[bottom_bottom_sideset]
type = SideSetsAroundSubdomainGenerator
input = bottom_top_sideset
new_boundary = bottom_bottom
block = bottom_block
normal = '0 0 -1'
[]
[bottom_front_sideset]
type = SideSetsAroundSubdomainGenerator
input = bottom_bottom_sideset
new_boundary = bottom_front
block = bottom_block
normal = '0 1 0'
[]
[bottom_back_sideset]
type = SideSetsAroundSubdomainGenerator
input = bottom_front_sideset
new_boundary = bottom_back
block = bottom_block
normal = '0 -1 0'
[]
[secondary]
input = bottom_back_sideset
type = LowerDBlockFromSidesetGenerator
sidesets = 'top_bottom' # top_back top_left'
new_block_id = '10001'
new_block_name = 'secondary_lower'
[]
[primary]
input = secondary
type = LowerDBlockFromSidesetGenerator
sidesets = 'bottom_top'
new_block_id = '10000'
new_block_name = 'primary_lower'
[]
[]
[Variables]
[disp_x]
block = '1 2'
[]
[disp_y]
block = '1 2'
[]
[disp_z]
block = '1 2'
[]
[lm_x]
block = 'secondary_lower'
use_dual = true
[]
[lm_y]
block = 'secondary_lower'
use_dual = true
[]
[lm_z]
block = 'secondary_lower'
use_dual = true
[]
[]
[ICs]
[disp_z]
block = 1
variable = disp_z
value = '${fparse offset}'
type = ConstantIC
[]
[disp_x]
block = 1
variable = disp_x
value = 0
type = ConstantIC
[]
[disp_y]
block = 1
variable = disp_y
value = 0
type = ConstantIC
[]
[]
[Kernels]
[disp_x]
type = MatDiffusion
variable = disp_x
[]
[disp_y]
type = MatDiffusion
variable = disp_y
[]
[disp_z]
type = MatDiffusion
variable = disp_z
[]
[]
[Constraints]
[weighted_gap_lm]
type = ComputeWeightedGapCartesianLMMechanicalContact
primary_boundary = 'bottom_top'
secondary_boundary = 'top_bottom'
primary_subdomain = 'primary_lower'
secondary_subdomain = 'secondary_lower'
lm_x = lm_x
lm_y = lm_y
lm_z = lm_z
variable = lm_x # This can be anything really
disp_x = disp_x
disp_y = disp_y
disp_z = disp_z
use_displaced_mesh = true
correct_edge_dropping = true
c = 1e+02
[]
[normal_x]
type = CartesianMortarMechanicalContact
primary_boundary = 'bottom_top'
secondary_boundary = 'top_bottom'
primary_subdomain = 'primary_lower'
secondary_subdomain = 'secondary_lower'
variable = lm_x
secondary_variable = disp_x
component = x
use_displaced_mesh = true
compute_lm_residuals = false
correct_edge_dropping = true
[]
[normal_y]
type = CartesianMortarMechanicalContact
primary_boundary = 'bottom_top'
secondary_boundary = 'top_bottom'
primary_subdomain = 'primary_lower'
secondary_subdomain = 'secondary_lower'
variable = lm_y
secondary_variable = disp_y
component = y
use_displaced_mesh = true
compute_lm_residuals = false
correct_edge_dropping = true
[]
[normal_z]
type = CartesianMortarMechanicalContact
primary_boundary = 'bottom_top'
secondary_boundary = 'top_bottom'
primary_subdomain = 'primary_lower'
secondary_subdomain = 'secondary_lower'
variable = lm_z
secondary_variable = disp_z
component = z
use_displaced_mesh = true
compute_lm_residuals = false
correct_edge_dropping = true
[]
[]
[BCs]
[botx]
type = DirichletBC
variable = disp_x
boundary = 'bottom_left bottom_right bottom_front bottom_back'
value = 0.0
[]
[boty]
type = DirichletBC
variable = disp_y
boundary = 'bottom_left bottom_right bottom_front bottom_back'
value = 0.0
[]
[botz]
type = DirichletBC
variable = disp_z
boundary = 'bottom_left bottom_right bottom_front bottom_back'
value = 0.0
[]
[topx]
type = DirichletBC
variable = disp_x
boundary = 'top_top'
value = 0.0
[]
[topy]
type = DirichletBC
variable = disp_y
boundary = 'top_top'
value = 0.0
[]
[topz]
type = FunctionDirichletBC
variable = disp_z
boundary = 'top_top'
function = '-${starting_point} * sin(2 * pi / 40 * t) + ${offset}'
[]
[]
[Preconditioning]
[vcp]
type = VCP
full = true
lm_variable = 'lm_x lm_y lm_z'
primary_variable = 'disp_x disp_y disp_z'
preconditioner = 'LU'
is_lm_coupling_diagonal = true
adaptive_condensation = true
[]
[]
[Executioner]
type = Transient
end_time = 1
dt = .5
dtmin = .01
solve_type = 'NEWTON'
petsc_options_iname = '-mat_mffd_err -pc_factor_shift_type -pc_factor_shift_amount'
petsc_options_value = '1e-5 NONZERO 1e-10'
l_max_its = 100
nl_max_its = 30
# nl_rel_tol = 1e-6
nl_abs_tol = 1e-12
line_search = 'none'
snesmf_reuse_base = false
[]
[Debug]
show_var_residual_norms = true
[]
[Outputs]
perf_graph = true
exodus = true
csv = true
[]
[Postprocessors]
active = 'num_nl cumulative contact'
[num_nl]
type = NumNonlinearIterations
[]
[cumulative]
type = CumulativeValuePostprocessor
postprocessor = num_nl
[]
[contact]
type = ContactDOFSetSize
variable = lm_z
subdomain = 'secondary_lower'
execute_on = 'nonlinear timestep_end'
[]
[]
[VectorPostprocessors]
[contact-pressure]
type = NodalValueSampler
block = secondary_lower
variable = lm_z
sort_by = 'id'
execute_on = NONLINEAR
[]
[]
(modules/contact/test/tests/mortar_cartesian_lms/two_block_1st_order_constraint_lm_xy.i)
[GlobalParams]
displacements = 'disp_x disp_y'
volumetric_locking_correction = true
[]
theta = 0
velocity = 0.1
[Mesh]
[left_block]
type = GeneratedMeshGenerator
dim = 2
xmin = -0.35
xmax = -0.05
ymin = -1
ymax = 0
nx = 1
ny = 3
elem_type = QUAD4
[]
[left_block_sidesets]
type = RenameBoundaryGenerator
input = left_block
old_boundary = '0 1 2 3'
new_boundary = '10 11 12 13'
[]
[left_block_sideset_names]
type = RenameBoundaryGenerator
input = left_block_sidesets
old_boundary = '10 11 12 13'
new_boundary = 'l_bottom l_right l_top l_left'
[]
[left_block_id]
type = SubdomainIDGenerator
input = left_block_sideset_names
subdomain_id = 1
[]
[right_block]
type = GeneratedMeshGenerator
dim = 2
xmin = 0
xmax = 0.3
ymin = -1
ymax = 0
nx = 1
ny = 2
elem_type = QUAD4
[]
[right_block_sidesets]
type = RenameBoundaryGenerator
input = right_block
old_boundary = '0 1 2 3'
new_boundary = '20 21 22 23'
[]
[right_block_sideset_names]
type = RenameBoundaryGenerator
input = right_block_sidesets
old_boundary = '20 21 22 23'
new_boundary = 'r_bottom r_right r_top r_left'
[]
[right_block_id]
type = SubdomainIDGenerator
input = right_block_sideset_names
subdomain_id = 2
[]
[combined_mesh]
type = MeshCollectionGenerator
inputs = 'left_block_id right_block_id'
[]
[left_lower]
type = LowerDBlockFromSidesetGenerator
input = combined_mesh
sidesets = '11'
new_block_id = '10001'
new_block_name = 'secondary_lower'
[]
[right_lower]
type = LowerDBlockFromSidesetGenerator
input = left_lower
sidesets = '23'
new_block_id = '10000'
new_block_name = 'primary_lower'
[]
[rotate_mesh]
type = TransformGenerator
input = right_lower
transform = ROTATE
vector_value = '0 0 ${theta}'
[]
[]
[Variables]
[lm_x]
block = 'secondary_lower'
use_dual = true
[]
[lm_y]
block = 'secondary_lower'
use_dual = true
[]
[]
[Modules/TensorMechanics/Master]
[all]
strain = FINITE
incremental = true
add_variables = true
block = '1 2'
[]
[]
[Functions]
[horizontal_movement]
type = ParsedFunction
expression = '${velocity} * t * cos(${theta}/180*pi)'
[]
[vertical_movement]
type = ParsedFunction
expression = '${velocity} * t * sin(${theta}/180*pi)'
[]
[]
[BCs]
[push_left_x]
type = FunctionDirichletBC
variable = disp_x
boundary = 13
function = horizontal_movement
[]
[fix_right_x]
type = DirichletBC
variable = disp_x
boundary = 21
value = 0.0
[]
[fix_right_y]
type = DirichletBC
variable = disp_y
boundary = 21
value = 0.0
[]
[push_left_y]
type = FunctionDirichletBC
variable = disp_y
boundary = 13
function = vertical_movement
[]
[]
[Materials]
[elasticity_tensor_left]
type = ComputeIsotropicElasticityTensor
block = 1
youngs_modulus = 1.0e6
poissons_ratio = 0.3
[]
[stress_left]
type = ComputeFiniteStrainElasticStress
block = 1
[]
[elasticity_tensor_right]
type = ComputeIsotropicElasticityTensor
block = 2
youngs_modulus = 1.0e6
poissons_ratio = 0.3
[]
[stress_right]
type = ComputeFiniteStrainElasticStress
block = 2
[]
[]
[Constraints]
[weighted_gap_lm]
type = ComputeWeightedGapCartesianLMMechanicalContact
primary_boundary = '23'
secondary_boundary = '11'
primary_subdomain = 'primary_lower'
secondary_subdomain = 'secondary_lower'
lm_x = lm_x
lm_y = lm_y
variable = lm_x # This can be anything really
disp_x = disp_x
disp_y = disp_y
use_displaced_mesh = true
correct_edge_dropping = true
[]
[normal_x]
type = CartesianMortarMechanicalContact
primary_boundary = '23'
secondary_boundary = '11'
primary_subdomain = 'primary_lower'
secondary_subdomain = 'secondary_lower'
variable = lm_x
secondary_variable = disp_x
component = x
use_displaced_mesh = true
compute_lm_residuals = false
correct_edge_dropping = true
[]
[normal_y]
type = CartesianMortarMechanicalContact
primary_boundary = '23'
secondary_boundary = '11'
primary_subdomain = 'primary_lower'
secondary_subdomain = 'secondary_lower'
variable = lm_y
secondary_variable = disp_y
component = y
use_displaced_mesh = true
compute_lm_residuals = false
correct_edge_dropping = true
[]
[]
[Preconditioning]
[smp]
type = SMP
full = true
[]
[]
[Executioner]
type = Transient
solve_type = 'NEWTON'
petsc_options_iname = '-pc_type -pc_factor_mat_solver_package -pc_factor_shift_type -pc_factor_shift_amount'
petsc_options_value = 'lu superlu_dist NONZERO 1e-10'
line_search = none
dt = 0.1
dtmin = 0.1
end_time = 1.0
l_max_its = 100
nl_max_its = 20
nl_rel_tol = 1e-6
snesmf_reuse_base = false
[]
[Outputs]
exodus = false
file_base = './output/1st_order_${theta}_degree_out'
[comp]
type = CSV
show = 'tot_lin_it tot_nonlin_it'
execute_on = 'FINAL'
[]
[]
[Postprocessors]
[avg_disp_x]
type = ElementAverageValue
variable = disp_x
block = '1 2'
[]
[avg_disp_y]
type = ElementAverageValue
variable = disp_y
block = '1 2'
[]
[max_disp_x]
type = ElementExtremeValue
variable = disp_x
block = '1 2'
[]
[max_disp_y]
type = ElementExtremeValue
variable = disp_y
block = '1 2'
[]
[min_disp_x]
type = ElementExtremeValue
variable = disp_x
block = '1 2'
value_type = min
[]
[min_disp_y]
type = ElementExtremeValue
variable = disp_y
block = '1 2'
value_type = min
[]
[num_lin_it]
type = NumLinearIterations
[]
[num_nonlin_it]
type = NumNonlinearIterations
[]
[tot_lin_it]
type = CumulativeValuePostprocessor
postprocessor = num_lin_it
[]
[tot_nonlin_it]
type = CumulativeValuePostprocessor
postprocessor = num_nonlin_it
[]
[]
(modules/contact/include/constraints/ComputeFrictionalForceCartesianLMMechanicalContact.h)
// This file is part of the MOOSE framework
// https://www.mooseframework.org
//
// All rights reserved, see COPYRIGHT for full restrictions
// https://github.com/idaholab/moose/blob/master/COPYRIGHT
//
// Licensed under LGPL 2.1, please see LICENSE for details
// https://www.gnu.org/licenses/lgpl-2.1.html
#pragma once
#include "ComputeWeightedGapCartesianLMMechanicalContact.h"
#include <unordered_map>
/**
* Computes the weighted gap that will later be used to enforce the
* zero-penetration mechanical contact conditions
*/
class ComputeFrictionalForceCartesianLMMechanicalContact
: public ComputeWeightedGapCartesianLMMechanicalContact
{
public:
static InputParameters validParams();
ComputeFrictionalForceCartesianLMMechanicalContact(const InputParameters & parameters);
void residualSetup() override;
void post() override;
/**
* Copy of the post routine but that skips assembling inactive nodes
*/
void
incorrectEdgeDroppingPost(const std::unordered_set<const Node *> & inactive_lm_nodes) override;
protected:
/**
* Computes properties that are functions only of the current quadrature point (\p _qp), e.g.
* indepedent of shape functions
*/
virtual void computeQpProperties() override;
/**
* Computes properties that are functions both of \p _qp and \p _i, for example the weighted gap
*/
virtual void computeQpIProperties() override;
/**
* Method called from \p post(). Used to enforce node-associated constraints. E.g. for the base \p
* ComputeWeightedGapLMMechanicalContact we enforce the zero-penetration constraint in this method
* using an NCP function. This is also where we actually feed the node-based constraint
* information into the system residual and Jacobian
*/
virtual void enforceConstraintOnDof(const DofObject * const dof) override;
/// A map from node to two weighted tangential velocities
std::unordered_map<const DofObject *, std::array<ADReal, 2>> _dof_to_weighted_tangential_velocity;
/// An array of two pointers to avoid copies
std::array<const ADReal *, 2> _tangential_vel_ptr = {{nullptr, nullptr}};
/// The value of the tangential velocity vectors at the current node
ADRealVectorValue _qp_tangential_velocity_nodal;
/// Numerical factor used in the tangential constraints for convergence purposes
const Real _c_t;
/// x-velocity on the secondary face
const ADVariableValue & _secondary_x_dot;
/// x-velocity on the primary face
const ADVariableValue & _primary_x_dot;
/// y-velocity on the secondary face
const ADVariableValue & _secondary_y_dot;
/// y-velocity on the primary face
const ADVariableValue & _primary_y_dot;
/// z-velocity on the secondary face
const ADVariableValue * const _secondary_z_dot;
/// z-velocity on the primary face
const ADVariableValue * const _primary_z_dot;
/// Friction coefficient
const Real _mu;
/// Small contact pressure value to trigger computation of frictional forces
const Real _epsilon;
};