solver: mortar contact in 2D models

Plane strain, plane stress and axisymmetric models take contact pairs and
general (self) contact on the linear edges of tri3 and quad4 elements:
edges oriented out of their elements, the edge mortar of fem with the
thickness or 2 pi r in the weights, the element's area over the edge's
length as the thickness under it, and the in-plane rows of the tangent
blocks. Verified by the contact patch test in plane strain and
axisymmetric (to 1e-9 small sliding, 1e-4 finite), Hertz's line contact
on quad4 edges (Johnson 1985, 4.3; equal to the hex8 model's to 1e-5),
and a hybrid rubber C-ring closing on itself that moves node for node as
the one-layer solid ring.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UhhZMBTviDQxZvwnyP89fo
This commit is contained in:
jyvikoandClaude Opus 5.5 authored and Irakli Kourtis committed 2026-10-14 16:30:44 -04:00
1 parent 6baa189231
commit 227c4544ed
20 files changed
+1579 -81

No files matched your search

+8 -9
View File
@@ -123,7 +123,7 @@ use crate::constraints::Reduction;
use crate::contact::{self, ContactResult, Interface};
use crate::ldlt::{Kind, Symmetric};
use crate::sparse::{Csr, NodeGraph, add_block, add_entry};
use crate::system::{Mesh, add_element_thermal_loads, loads, temperature_loads, translations};
use crate::system::{Mesh, add_element_thermal_loads, loads, temperature_loads, translation_block, translations};
use crate::{Error, Limits, Result};
/// One Newton iteration of an increment.
@@ -445,8 +445,10 @@ impl Problem<'_> {
}
});
if let Some(k) = k.as_mut() {
// Contact blocks are over X, Y and Z per node; a 2D model keeps the in-plane rows.
let kinds = self.mesh.translation_kinds();
for (nodes, data) in &blocks {
add_block(k, layout, nodes, self.mesh.translation_kinds(), data)?;
add_block(k, layout, nodes, kinds, &translation_block(data, nodes.len(), kinds))?;
}
}
let mut history = Vec::with_capacity(self.mesh.elements.len());
@@ -945,13 +947,10 @@ fn check_supported(model: &Model, mesh: &Mesh, step: &Step, settings: &Nonlinear
return Err(at(format!("centrifugal loads (load `{name}`), which depend on the deformed position")));
}
plasticity(model, step, settings, warnings)?;
if let Some(name) = model.contacts().keys().next() {
if mesh.theory.is_some() {
return Err(at(format!("contact (`{name}`) in 2D models")));
}
if settings.arc_length.is_some() {
return Err(at(format!("contact (`{name}`) with arc-length control")));
}
if let Some(name) = model.contacts().keys().next()
&& settings.arc_length.is_some()
{
return Err(at(format!("contact (`{name}`) with arc-length control")));
}
if settings.nonlinear_geometry
&& let Some(name) = model.couplings().keys().next()