forked from 1b-agent/fieldcraft-upstream
solver: mortar contact in nonlinear static steps
Frictionless contact pairs enforced by an augmented Lagrangian: nodal pressures from the mortar weighted gaps, Uzawa updates after each converged increment until the gaps are within 1e-5 of the element thickness, and the consistent tangent (outer products of the weighted gap gradients and the pressure-weighted Hessians from the automatic differentiation of the mortar integrals). Finite sliding searches and integrates on the current configuration every evaluation; without nonlinear geometry the pairs and their mortar integrals are fixed in the reference configuration (small sliding, linear and exact). The tangent's pattern takes the face pairs' blocks; the step cuts back when the active set chatters. Initial overclosure is offset or refused; general contact, 2D models and arc length are refused by name; the linear analyses refuse contact by name instead of the mesh. With contact the convergence force scale comes from the converged states, the loads and the current state. The loads-and-reactions balance check takes the nodal reactions into its scale (no spurious warning for a part squeezed between held faces). Tests: the contact patch test on non-matching hex8/tet4 meshes (exact with small sliding, to 1e-4 with finite sliding), Hertz cylinder on a flat (peak pressure within 3%, half-width bracketed) with quadratic Newton convergence, overclosure, thread-count determinism and the refusals; Hertz sphere on a flat (heavy). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UhhZMBTviDQxZvwnyP89fo
This commit is contained in:
1 parent
37f338c9e8
commit
8871f6721d
8 files changed
+1392
-25
No files matched your search
@@ -426,11 +426,6 @@ impl Mesh {
|
||||
|
||||
/// Build it from the model and its resolved view, refusing what the solver cannot represent.
|
||||
pub fn new(model: &Model, resolved: &Resolved, max_dofs: usize) -> Result<Mesh> {
|
||||
if let Some(name) = model.contacts().keys().next() {
|
||||
return Err(Error::NotSupported(format!(
|
||||
"contact (`{name}`) in implicit analyses (it comes with M7): a model's contacts apply to all its steps, so solve its explicit dynamic steps, or remove the contacts to solve the implicit ones"
|
||||
)));
|
||||
}
|
||||
let mut elements = Vec::new();
|
||||
let mut trusses = Vec::new();
|
||||
let mut structural = Vec::new();
|
||||
@@ -661,6 +656,24 @@ impl Mesh {
|
||||
}
|
||||
}
|
||||
|
||||
/// The scale of the loads-and-reactions balance check: the largest load or reaction, nodal ones
|
||||
/// too, since reactions that cancel out (a part squeezed between held faces) still set it.
|
||||
pub(crate) fn balance_scale(applied: &[f64; 3], total: &[f64; 3], reaction: &[[f64; 3]]) -> f64 {
|
||||
let largest = reaction.iter().flatten().fold(0.0f64, |m, v| m.max(v.abs()));
|
||||
applied.iter().chain(total).fold(largest, |m, v| m.max(v.abs()))
|
||||
}
|
||||
|
||||
/// Refuse contact in an analysis that cannot take it (linear static, frequency, buckling): a
|
||||
/// model's contacts apply to all its steps.
|
||||
pub fn refuse_contact(model: &Model, analysis: &str) -> Result<()> {
|
||||
match model.contacts().keys().next() {
|
||||
Some(name) => Err(Error::NotSupported(format!(
|
||||
"contact (`{name}`) in {analysis} steps: a model's contacts apply to all its steps, so solve its nonlinear static or explicit dynamic steps, or remove the contacts to solve this one"
|
||||
))),
|
||||
None => Ok(()),
|
||||
}
|
||||
}
|
||||
|
||||
/// The prescribed value of every degree of freedom (`None` = free) under a step's boundary
|
||||
/// conditions; `values: false` holds them at zero (frequency steps). Rotations of solid nodes do
|
||||
/// not exist and are ignored, like the directions of a reference point no coupling ties
|
||||
|
||||
Reference in new issue
Block a user