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
@@ -113,6 +113,8 @@ pub struct StaticResult {
|
||||
/// The increments of a nonlinear static step (its stresses are Cauchy stresses and its
|
||||
/// strains logarithmic strains).
|
||||
pub nonlinear: Option<crate::nonlinear::History>,
|
||||
/// Each contact's state at the end of a nonlinear static step, by name.
|
||||
pub contacts: Vec<crate::contact::ContactResult>,
|
||||
}
|
||||
|
||||
/// One natural mode.
|
||||
@@ -362,6 +364,7 @@ pub fn solve_static(model: &Model, step: Option<&str>, limits: &Limits) -> Resul
|
||||
|p| matches!(p, Procedure::Static {} | Procedure::CoupledTemperatureDisplacement {}),
|
||||
"static or coupled temperature–displacement",
|
||||
)?;
|
||||
crate::system::refuse_contact(model, "linear static")?;
|
||||
let mesh = Mesh::new(model, &resolved, limits.max_dofs)?;
|
||||
let mut warnings: Vec<String> = resolved.warnings.iter().map(ToString::to_string).chain(mesh.warnings.iter().cloned()).collect();
|
||||
warnings.extend(inelastic_warnings(model));
|
||||
@@ -497,7 +500,7 @@ pub fn solve_static(model: &Model, step: Option<&str>, limits: &Limits) -> Resul
|
||||
}
|
||||
let stress: Vec<[f64; 6]> = sum.iter().zip(&count).map(|(s, c)| s.map(|v| if *c > 0 { v / f64::from(*c) } else { 0.0 })).collect();
|
||||
let von_mises = stress.iter().map(von_mises).collect();
|
||||
let scale = applied_total.iter().chain(&reaction_total).fold(0.0f64, |m, v| m.max(v.abs()));
|
||||
let scale = crate::system::balance_scale(&applied_total, &reaction_total, &reaction);
|
||||
let ground = mesh.discrete.ground_force(&u, &mesh);
|
||||
let imbalance = (0..3).map(|c| (applied_total[c] + reaction_total[c] + ground[c]).abs()).fold(0.0, f64::max);
|
||||
if scale > 0.0 && imbalance > 1e-6 * scale {
|
||||
@@ -522,6 +525,7 @@ pub fn solve_static(model: &Model, step: Option<&str>, limits: &Limits) -> Resul
|
||||
warnings,
|
||||
temperature,
|
||||
nonlinear: None,
|
||||
contacts: Vec::new(),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -568,6 +572,7 @@ pub fn solve_frequency(model: &Model, step: Option<&str>, options: &FrequencyOpt
|
||||
{
|
||||
return Err(Error::Invalid(format!("the shift must be a finite number, got {s}")));
|
||||
}
|
||||
crate::system::refuse_contact(model, "frequency")?;
|
||||
let mesh = Mesh::new(model, &resolved, limits.max_dofs)?;
|
||||
let mut warnings: Vec<String> = resolved.warnings.iter().map(ToString::to_string).chain(mesh.warnings.iter().cloned()).collect();
|
||||
warnings.extend(inelastic_warnings(model));
|
||||
|
||||
Reference in new issue
Block a user