Sources. Thesis §4.3.3.5–4.3.3.6 (pp. 111–112, Algorithm 2, eqs. 4.41–4.44), §4.3.4.4–4.3.4.5 (pp. 122–123, Algorithm 3, eqs. 4.73–4.79), Algorithm 1 (line search); code:
custom_strategies/custom_strategies/residualbased_newton_raphson_contact_strategy.h,line_search_contact_strategy.h,residualbased_newton_raphson_mpc_contact_strategy.h,custom_strategies/custom_convergencecriterias/*.h(18 headers),custom_utilities/active_set_utilities.cpp,custom_python/add_custom_strategies_to_python.cpp,python_scripts/contact_convergence_criteria_factory.py,python_scripts/auxiliary_methods_solvers.py,python_scripts/contact_structural_mechanics_static_solver.py,python_scripts/mpc_contact_structural_mechanics_static_solver.py.
The contact problem is solved with a semi-smooth Newton method: the non-linearity of the contact constraints (which nodes are in contact, which of them stick and which slip) is handled by a primal-dual active-set update that runs together with the standard Newton–Raphson iterations of the structural problem. In the ContactStructuralMechanicsApplication this is split into two families of header-only classes living in custom_strategies/:
- the strategies (
custom_strategies/custom_strategies/) decide how the Newton loop is organized: whether the active set is updated inside every iteration or in an outer loop, whether the time step is split when the iterations fail, how the solution is predicted, and how the line search treats displacements and Lagrange multipliers; - the convergence criteria (
custom_strategies/custom_convergencecriterias/) decide when the loop stops: they measure the displacement / residual / multiplier norms, but also update the mortar quantities (normals, tangents, weighted gap) and perform the active-set check itself.
All of them are templates on TSparseSpace, TDenseSpace[, TLinearSolver] instantiated only for the Ublas spaces in add_custom_strategies_to_python.cpp; no time-integration scheme is defined by the application (the SCHEME CLASSES block of that file is empty), the schemes of the StructuralMechanicsApplication are reused. How the pieces are wired from ProjectParameters.json is summarized at the end of this page and detailed in the Solver settings reference; the mathematics is in Frictionless contact and Frictional contact.
Figure: one time step of the contact solution loop and where the strategy and the convergence criteria intervene.
Strategies
| Class | File | Name() |
Python name | Base class | Created by |
|---|---|---|---|---|---|
ResidualBasedNewtonRaphsonContactStrategy |
residualbased_newton_raphson_contact_strategy.h |
newton_raphson_contact_strategy |
ResidualBasedNewtonRaphsonContactStrategy |
ResidualBasedNewtonRaphsonStrategy |
auxiliary_methods_solvers.AuxiliaryNewton when solving_strategy_settings.type is newton_raphson and contact_settings.mortar_type is not empty |
LineSearchContactStrategy |
line_search_contact_strategy.h |
line_search_contact_strategy |
LineSearchContactStrategy |
LineSearchStrategy |
auxiliary_methods_solvers.AuxiliaryLineSearch when solving_strategy_settings.type is line_search |
ResidualBasedNewtonRaphsonMPCContactStrategy |
residualbased_newton_raphson_mpc_contact_strategy.h |
newton_raphson_mpc_contact_strategy |
ResidualBasedNewtonRaphsonMPCContactStrategy |
ResidualBasedNewtonRaphsonStrategy |
auxiliary_methods_solvers.AuxiliaryMPCNewton from the MPCContactStaticSolver / MPCContactImplicitMechanicalSolver |
The Name() string is the value of the "name" key of GetDefaultParameters(); it is the identifier the Kratos strategy factories use through Create(rModelPart, ThisParameters). The arc_length option of solving_strategy_settings.type falls back to the structural _create_arc_length_strategy, which is not contact-aware.
ResidualBasedNewtonRaphsonContactStrategy
Source. It derives from the core ResidualBasedNewtonRaphsonStrategy and keeps all of its constructors (with or without a builder-and-solver, with Parameters), adding two optional trailing arguments pMyProcesses and pPostProcesses of type ProcessFactoryUtility::Pointer (ProcessesListType). Its default parameters are merged recursively with the base-class defaults:
{
"name" : "newton_raphson_contact_strategy",
"adaptative_strategy" : false,
"split_factor" : 10.0,
"max_number_splits" : 3,
"inner_loop_iterations" : 5
}
The Python solvers copy exactly these four keys (adaptative_strategy, split_factor, max_number_splits, inner_loop_iterations) from contact_settings into the strategy parameters (AuxiliaryNewton, auxiliary_methods_solvers.py). What the class changes with respect to the base strategy:
Contact-aware Predict() (residualbased_newton_raphson_contact_strategy.h:313-348). The base predictor is not called (the call is commented out with the remark “May cause problems in dynamics”). Instead, if the Contact sub-model-part nodes carry WEIGHTED_GAP, the strategy
- sets
WEIGHTED_GAPto zero on theContactnodes, andWEIGHTED_SLIPto zero too when the model partIs(SLIP)(frictional problem); - calls
ContactUtilities::ComputeExplicitContributionConditionsonComputingContact, so that every pair condition integrates its mortar operators and accumulates the current weighted gap \(\tilde{g}_n\) on the slave nodes; - moves the nodal coordinates by the displacement increment:
DISPLACEMENTatSTEP == 1,DISPLACEMENT - DISPLACEMENT(1)afterwards.
No prediction of the Lagrange multipliers is made here (the corresponding block is commented out); the predict_correct_lagrange_multiplier option of the search process takes care of it.
Two ways of running the semi-smooth Newton loop (SolveSolutionStep, lines 465-515). The choice is made by the INTERACTION flag of the computing model part, which ContactStaticMechanicalSolver.Initialize sets to true unless contact_settings.simplified_semi_smooth_newton is true:
| Computing model part | Loop | Active-set check |
|---|---|---|
Is(INTERACTION) (default) |
One call to BaseSolveSolutionStep(); INNER_LOOP_ITERATION = 1 |
Inside every Newton iteration, by the mortar criterion’s PostCriteria (full semi-smooth Newton, thesis §4.3.3.6) |
IsNot(INTERACTION) (simplified_semi_smooth_newton) |
Up to inner_loop_iterations outer passes; each pass resets NL_ITERATION_NUMBER = 1, stores the pass counter in INNER_LOOP_ITERATION, runs a complete BaseSolveSolutionStep() with a frozen active set, then calls mpConvergenceCriteria->PostCriteria (echo temporarily silenced) to update the set and decide whether another pass is needed |
Only at the end of each inner pass; ActiveSetUtilities only re-evaluates the set when rModelPart.Is(INTERACTION) || NL_ITERATION_NUMBER == 1 |
Own Newton loop BaseSolveSolutionStep() (lines 615-790). It is a copy of the core Newton–Raphson loop instrumented for contact: PreCriteria is evaluated before the first build, InitializeNonLinearIteration / FinalizeNonLinearIteration of the criteria are called around every iteration, the RHS is rebuilt before PostCriteria when GetActualizeRHSflag() is set (the factory sets SetActualizeRHSFlag(True)), and the geometry is checked for inversion when adaptative_strategy is on. The echo of the convergence criterion is silenced during InitializeSolutionStep, and the mFinalizeWasPerformed guard ensures that FinalizeSolutionStep runs once even when the step has been split.
Adaptive time-step splitting (AdaptativeStep(), lines 796-925; SplitTimeStep, UnMoveMesh, CoutSplittingTime). Only when adaptative_strategy is true and the step did not converge. The strategy restores TIME to the beginning of the step, divides DELTA_TIME by split_factor, and marches through the original interval in sub-steps; the process is repeated (dividing again) up to max_number_splits times. Each sub-step increments STEP, overwrites/clones the solution-step data in the nodal buffer and the ProcessInfo, and runs the full sequence InitializeSolutionStep → Predict → SolveSolutionStep → FinalizeSolutionStep. Around each sub-step the two ProcessFactoryUtility lists are driven explicitly: ExecuteInitializeSolutionStep, ExecuteFinalizeSolutionStep, ExecuteBeforeOutputStep, PrintOutput() (post processes) and ExecuteAfterOutputStep. This is why ContactStaticMechanicalSolver.AddProcessesList(processes_list) and AddPostProcess(post_process) exist: they wrap the Python process lists in a ProcessFactoryUtility so that boundary conditions depending on time and the output are still applied for the sub-steps. If the lists are missing, the strategy warns that the adaptive strategy “will be USELESS” (echo level greater than 0). After the last split the original DELTA_TIME is restored; if the splits are exhausted, MaxIterationsAndSplitsExceeded() prints a warning box.
Inverted-element guard (CheckGeometryInverted(), lines 928-964). Also only with adaptative_strategy. Before the first solve of the step and after every UpdateDatabase, the strategy loops over the elements (serially) and returns true if any DeterminantOfJacobian(0) is negative or if any DEFORMATION_GRADIENT computed on the integration points has a negative determinant. In that case STEP is decremented, a KRATOS_WARNING("Element inverted") is issued and BaseSolveSolutionStep returns false, so the adaptive splitting kicks in.
Extra Python methods. Besides the usual Initialize, Solve, Predict, SolveSolutionStep, … inherited from SolvingStrategy, the binding exposes SetMaxIterationNumber, GetMaxIterationNumber, SetKeepSystemConstantDuringIterations and GetKeepSystemConstantDuringIterations (the latter controls whether BuildAndSolve or BuildRHSAndSolve is called in the iterations).
LineSearchContactStrategy
Source. It derives from the core LineSearchStrategy (thesis Algorithm 1) and has only one extra default parameter:
{
"name" : "line_search_contact_strategy"
}
The core line search fits one parabola to the norm of the whole residual at the relaxation factors 0, ½ and 1. In a mixed displacement / Lagrange-multiplier system the two blocks have very different magnitudes, so the contact version overrides UpdateDatabase (lines 350-403) with a split line search:
ComputeSplitDx(Dx, DxDisp, DxLM)separates the solution increment by DoF variable:DISPLACEMENT_X/Y/Zgo toDxDisp, everything else (the multipliers) toDxLM.ComputeMixedResidual(b, normDisp, normLM)returns two residual norms with the same split. The residual is evaluated without update (\(r_o\)), after half of the increment (\(r_h\)) and after the full increment (\(r_f\)), each time rebuilding the RHS withBuildRHS.ComputeParabolais called once per block with the triple \((r_f, r_o, r_h)\) and fits \(y = a x^2 + b x + c\) with \(c = r_o\), \(b = 4 r_h - r_f - 3 r_o\), \(a = 2 r_f - 4 r_h + 2 r_o\). If \(a \gt 0\) the minimum \(x = -b/(2a)\) is taken and clamped to \([-1, 1]\); otherwise the full step is kept when \(r_f \lt r_o\) and the lower boundXmin(\(10^{-3}\), “should be zero, but otherwise it will stagnate”) is used when it is not.- A final
UpdateDatabaseapplies \(-(1 - X_{disp})\,\Delta\mathbf{u}\) and \(-(1 - X_{LM})\,\Delta\boldsymbol{\lambda}\) on top of the already applied full step, so that the two blocks end up with independent relaxation factors.
Note: the signature is ComputeParabola(double& Xmax, double& Xmin, rf, ro, rh) but the two calls pass (XminDisp, XmaxDisp, ...) and (XminLM, XmaxLM, ...), so the optimum is written into the Xmin* variables while the final correction uses XmaxDisp / XmaxLM, which stay at 1.0 (except in the degenerate branch, where Xmin is assigned the value 1.0 of the swapped argument). In practice the final correction is therefore zero and the full Newton step is kept; the split parabola machinery is evaluated but does not alter the update. The member mRecalculateFactor is declared but not used by the override. The strategy also exposes SetMaxIterationNumber, GetMaxIterationNumber, SetKeepSystemConstantDuringIterations and GetKeepSystemConstantDuringIterations to Python; the Python wrapper AuxiliaryLineSearch passes an empty Parameters object and ignores the processes lists.
ResidualBasedNewtonRaphsonMPCContactStrategy
Source. This is the strategy of the multi-point-constraint (MPC) contact route, in which the contact constraints are imposed as ContactMasterSlaveConstraint objects instead of Lagrange multipliers or penalties (see Frictional laws and MPC constraint). Defaults:
{
"name" : "newton_raphson_mpc_contact_strategy",
"inner_loop_iterations" : 5,
"update_each_nl_iteration" : false,
"enforce_ntn" : false
}
AuxiliaryMPCNewton copies inner_loop_iterations, update_each_nl_iteration and enforce_ntn from mpc_contact_settings (whose own default for inner_loop_iterations is 10). Behavior:
- Internal active-set criterion. Every constructor creates its own
MPCContactCriteria(mpMPCContactCriteria), used independently of the user criterion for the active-set check. The Python solver additionally wraps the structural criterion and a secondMPCContactCriteriainto aKM.AndCriteria, so the mapped-reaction check is performed twice per iteration. Predict()(lines 311-345): calls the base predictor and then performs one fullBuildAndSolvefollowed bympMPCContactCriteria->PostCriteria(...), so that the active set of the step starts from the reactions of a first linear solve rather than from the search-based guess.ComputeNodalWeights()(lines 823-860): onInitialize,InitializeSolutionStepand at every non-linear iteration, it resets and recomputes two non-historical nodal values on theContactsub-model-part from theSLAVEconditions:NODAL_PAUX(number of slave conditions sharing the node) andNODAL_MAUX(lumped area,LumpingFactors × DomainSize).MPCMortarContactConditiondivides the constraint rows byNODAL_PAUXandMPCContactCriteriadivides the reactions byNODAL_MAUXto obtain pressures.- Loop structure (
SolveSolutionStep, lines 412-457): here theINTERACTIONflag is read from the process info (set byMPCContactStaticSolver.Initializewhenmpc_contact_settings.simplified_semi_smooth_newtonistrue). If set, up toinner_loop_iterationspasses ofAuxiliarySolveSolutionStep()are run, each followed bympMPCContactCriteria->PostCriteria; otherwise a single pass.AuxiliarySolveSolutionStepis the instrumented Newton loop. update_each_nl_iteration: sets theINTERACTIONflag on the conditions ofComputingContact(VariableUtils().SetFlag(INTERACTION, update_each_nl_iteration, ...)), which is what makesMPCMortarContactCondition::InitializeNonLinearIterationrebuild its constraint, and inside the Newton loop callsSetUpDofSet,SetUpSystemandResizeAndInitializeVectorsat every iteration, because the set of active constraints (hence the DoF numbering of the builder-and-solver with constraints) changes with the active set.enforce_ntn(node-to-node enforcement): the parameter is accepted and validated, but every call toEnforcingNTN()is commented out, the method body itself is commented out, andComputeNodalWeightshard-codesconst bool enforce_ntn = false;. Note: settingenforce_ntntotruetherefore has no effect in the current code.
Convergence criteria
Eighteen criteria live in custom_strategies/custom_convergencecriterias/. All of them expose GetDefaultParameters(), Name() and Create(Parameters); the Python names coincide with the class names except for BaseMortarConvergenceCriteria, which is not exposed. Every default block below is merged with the base ConvergenceCriteria defaults (RecursivelyAddMissingParameters).
| Class | Header | Name() |
Base | What it checks |
|---|---|---|---|---|
BaseMortarConvergenceCriteria |
base_mortar_criteria.h |
base_mortar_criteria |
ConvergenceCriteria |
Nothing by itself (always true); updates normals, tangents, weighted gap, dynamic factor and adapted penalty |
ALMFrictionlessMortarConvergenceCriteria |
alm_frictionless_mortar_criteria.h |
alm_frictionless_mortar_criteria |
BaseMortarConvergenceCriteria |
Active set, scalar ALM |
ALMFrictionlessComponentsMortarConvergenceCriteria |
alm_frictionless_components_mortar_criteria.h |
alm_frictionless_components_mortar_criteria |
BaseMortarConvergenceCriteria |
Active set, vector (components) ALM |
ALMFrictionalMortarConvergenceCriteria |
alm_frictional_mortar_criteria.h |
alm_frictional_mortar_criteria |
BaseMortarConvergenceCriteria |
Active set and stick/slip set, ALM frictional |
PenaltyFrictionlessMortarConvergenceCriteria |
penalty_frictionless_mortar_criteria.h |
penalty_frictionless_mortar_criteria |
BaseMortarConvergenceCriteria |
Active set, penalty frictionless |
PenaltyFrictionalMortarConvergenceCriteria |
penalty_frictional_mortar_criteria.h |
penalty_frictional_mortar_criteria |
BaseMortarConvergenceCriteria |
Active set and stick/slip set, penalty frictional |
MeshTyingMortarConvergenceCriteria |
mesh_tying_mortar_criteria.h |
mesh_tying_mortar_criteria |
ConvergenceCriteria |
Nothing (table hook, always true) |
MPCContactCriteria |
mpc_contact_criteria.h |
mpc_contact_criteria |
ConvergenceCriteria |
Active set (and stick/slip) from mapped reactions, MPC route |
MortarAndConvergenceCriteria |
mortar_and_criteria.h |
mortar_and_criteria |
And_Criteria |
Both children; prints the iteration table and optionally the condition number |
DisplacementContactCriteria |
displacement_contact_criteria.h |
displacement_contact_criteria |
ConvergenceCriteria |
Displacement (and rotation) increment |
DisplacementResidualContactCriteria |
displacement_residual_contact_criteria.h |
displacement_residual_contact_criteria |
ConvergenceCriteria |
Displacement (and rotation) residual |
DisplacementLagrangeMultiplierContactCriteria |
displacement_lagrangemultiplier_contact_criteria.h |
displacement_lagrangemultiplier_contact_criteria |
ConvergenceCriteria |
Displacement increment and Lagrange-multiplier increment |
DisplacementLagrangeMultiplierResidualContactCriteria |
displacement_lagrangemultiplier_residual_contact_criteria.h |
displacement_lagrangemultiplier_residual_contact_criteria |
ConvergenceCriteria |
Displacement residual and Lagrange-multiplier residual |
DisplacementLagrangeMultiplierMixedContactCriteria |
displacement_lagrangemultiplier_mixed_contact_criteria.h |
displacement_lagrange_multiplier_mixed_contact_criteria |
ConvergenceCriteria |
Displacement residual and Lagrange-multiplier increment |
DisplacementLagrangeMultiplierFrictionalContactCriteria |
displacement_lagrangemultiplier_frictional_contact_criteria.h |
displacement_lagrangemultiplier_frictional_contact_criteria |
ConvergenceCriteria |
Displacement increment; multiplier increment split into normal, stick-tangent and slip-tangent |
DisplacementLagrangeMultiplierResidualFrictionalContactCriteria |
displacement_lagrangemultiplier_residual_frictional_contact_criteria.h |
displacement_lagrangemultiplier_ressidual_frictional_contact_criteria (sic) |
ConvergenceCriteria |
Residual version of the previous one |
DisplacementLagrangeMultiplierMixedFrictionalContactCriteria |
displacement_lagrangemultiplier_mixed_frictional_contact_criteria.h |
displacement_lagrangemultiplier_mixed_frictional_contact_criteria |
ConvergenceCriteria |
Mixed version of the previous one |
ContactErrorMeshCriteria |
contact_error_mesh_criteria.h |
contact_error_mesh_criteria |
ConvergenceCriteria |
Mesh discretization error (SPR); “converged” means no remeshing needed |
Note: the Name() of DisplacementLagrangeMultiplierResidualFrictionalContactCriteria is misspelled ..._ressidual_... in the source (displacement_lagrangemultiplier_residual_frictional_contact_criteria.h:625,653); any factory-based creation must use the misspelled string.
The following conventions are shared by the whole family:
- Relative and absolute checks. Every tolerance comes in a pair
*_relative_tolerance/*_absolute_tolerance; a block converges when either the ratio \(\sqrt{\Vert\Delta x\Vert^2/\Vert x\Vert^2}\) (or the residual ratio with respect to the residual of the first iteration) is below the relative tolerance or the absolute value \(\sqrt{\Vert\Delta x\Vert^2}/n_{dof}\) is below the absolute tolerance. All defaults are \(10^{-4}\) (relative) and \(10^{-9}\) (absolute). - DoF classification by variable. The DoF set is partitioned with the variable of each DoF:
DISPLACEMENT_X/Y/Zform the displacement block,ROTATION_X/Y/Zthe rotation block (only when the model part has rotation DoFs, local flagROTATION_DOF_IS_CONSIDERED),LAGRANGE_MULTIPLIER_CONTACT_PRESSUREorVECTOR_LAGRANGE_MULTIPLIER_X/Y/Zthe contact block. In the frictional variants the vector multiplier of each slave node is projected on the nodalNORMALto obtain the normal component, and the tangential remainder is assigned to the stick or slip block according to theSLIPflag of the node (or to slip for everybody whenpure_slipistrue). ensure_contact(local flagENSURE_CONTACT). Whenfalse(default) and the norm of the multipliers is zero (no active node), the multiplier block is considered converged; whentruea zero multiplier norm raisesKRATOS_ERRORwith the message “CONTACT LOST::ARE YOU SURE YOU ARE SUPPOSED TO HAVE CONTACT?”.ratio_normal_tangent_threshold(frictional variants). A tangential block that fails its own tolerances is still accepted if the ratio between its absolute norm and the absolute norm of the normal block is below this threshold (default \(10^{-4}\)), because the tangential multipliers are orders of magnitude smaller than the normal ones and would otherwise stall the iterations.print_convergence_criterion(local flagPRINTING_OUTPUT) selects between the plainKRATOS_INFOlines and the colored/bold output; when theProcessInfocarries aTABLE_UTILITY(created byAuxiliaryCreateConvergenceParameterswhencontact_settings.fancy_convergence_criterionistrue) each criterion adds its columns to the shared table inInitialize(guarded byTABLE_IS_INITIALIZED) and writes its values inPostCriteria.
BaseMortarConvergenceCriteria
Source. Base of the five active-set criteria; its own PreCriteria and PostCriteria return true, they exist to keep the mortar quantities consistent with the current iterate. Defaults:
{
"name" : "base_mortar_criteria",
"compute_dynamic_factor" : false,
"gidio_debug" : false,
"pure_slip" : false
}
The three booleans are stored in the local flags COMPUTE_DYNAMIC_FACTOR, IO_DEBUG and PURE_SLIP; the Python factory passes them positionally from contact_settings.compute_dynamic_factor, contact_settings.gidio_debug and the pure-slip detection ("PureSlip" in mortar_type, or AuxiliaryPureSlipCheck returning true when the sum of the FRICTION_COEFFICIENT of all properties is zero).
PreCriteria (lines 168-232), executed before the system is built in every iteration:
- If
ProcessInfo[CONSIDER_NORMAL_VARIATION]is notNO_DERIVATIVES_COMPUTATION,ComputeNodesMeanNormalModelPartWithPairedNormalrecomputes the nodal normals of theContactsub-model-part and refreshes the paired normal stored in everyPairedCondition. - If the model part
Is(SLIP)(frictional problem), the nodal tangents are updated byMortarUtilities::ComputeNodesTangentModelPart: from the tangential multiplier when the model part hasVECTOR_LAGRANGE_MULTIPLIERand the problem is not pure slip, fromWEIGHTED_SLIPotherwise. Tangents must be updated even when the normal is constant. - If
ProcessInfo[ADAPT_PENALTY]istrueor the problem is dynamic (VELOCITYis a nodal solution-step variable), the weighted gap is reset (ResetWeightedGap) and recomputed withContactUtilities::ComputeExplicitContributionConditions("ComputingContact"). - In the dynamic case with
COMPUTE_DYNAMIC_FACTOR,ComputeDynamicFactorProcessis executed onContact(it computes the nodalDYNAMIC_FACTORused to scale the gap). - With
ADAPT_PENALTY,AALMAdaptPenaltyValueProcessupdates the nodalINITIAL_PENALTY(adaptive augmented Lagrangian, thesis App. D.4.3.1).
PostCriteria (lines 235-290), executed after the update of the database:
- Copies the current
WEIGHTED_GAPof everyContactnode into buffer position 1 (needed by the frictional formulation and byMPCContactCriteria). - Resets
WEIGHTED_GAP(andWEIGHTED_SLIP) and recomputes them withComputeExplicitContributionConditions, so that the active-set check that follows in the derived class is performed with the gap of the updated configuration. - If
IO_DEBUGis set, aGidIOframe labelled withNL_ITERATION_NUMBERis written with the flagsINTERFACE,ACTIVE,SLAVE,ISOLATED(andSLIP), the nodalNORMAL,DYNAMIC_FACTOR,AUGMENTED_NORMAL_CONTACT_PRESSURE(andAUGMENTED_TANGENT_CONTACT_PRESSURE),DISPLACEMENT,VELOCITY/ACCELERATIONwhen present, the multipliers (LAGRANGE_MULTIPLIER_CONTACT_PRESSUREorVECTOR_LAGRANGE_MULTIPLIER) andWEIGHTED_GAP(andWEIGHTED_SLIP). This is the fastest way to debug an oscillating active set.
Initialize of the derived classes adds the columns ACTIVE SET CONV (and SLIP/STICK CONV for the frictional ones) to the shared table.
The five active-set criteria
The derived criteria override PostCriteria, call the base version first and then call the corresponding function of the ActiveSetUtilities namespace (active_set_utilities.cpp). Each function loops over the SLAVE nodes of the Contact sub-model-part, evaluates the nodal complementarity function, toggles the flags and returns the number of nodes that changed state; the criterion converges when that number is zero. The result is stored in ProcessInfo[ACTIVE_SET_CONVERGED] (and SLIP_SET_CONVERGED), and the ACTIVE_SET_COMPUTED flag prevents the ALM criteria from evaluating the set twice in the same iteration. The set is only re-evaluated when rModelPart.Is(INTERACTION) || NL_ITERATION_NUMBER == 1, which is the mechanism behind the two loop modes of the strategy. Defaults of all five (only "name" differs):
{
"name" : "alm_frictionless_mortar_criteria",
"print_convergence_criterion" : false
}
With \(\varepsilon\) the penalty (nodal INITIAL_PENALTY if present, otherwise ProcessInfo[INITIAL_PENALTY]), \(k\) the scale factor SCALE_FACTOR, \(c_\tau\) the TANGENT_FACTOR, \(\tilde{g}_n\) the weighted gap WEIGHTED_GAP, \(\tilde{\mathbf{g}}_\tau\) the weighted slip WEIGHTED_SLIP and \(\mu\) the nodal FRICTION_COEFFICIENT, the checks are:
| Criterion | ActiveSetUtilities function |
Augmented normal pressure \(\bar{\lambda}_n\) (stored in AUGMENTED_NORMAL_CONTACT_PRESSURE) |
Active if | Stick/slip |
|---|---|---|---|---|
ALMFrictionlessMortarConvergenceCriteria |
ComputeALMFrictionlessActiveSet(rModelPart) |
\(k\lambda_n + \varepsilon\tilde{g}_n\) with \(\lambda_n\) = LAGRANGE_MULTIPLIER_CONTACT_PRESSURE |
\(\bar{\lambda}_n \lt 0\); on activation \(\lambda_n \leftarrow \bar{\lambda}_n/k\) | – |
ALMFrictionlessComponentsMortarConvergenceCriteria |
ComputeALMFrictionlessComponentsActiveSet(rModelPart) |
\(k\,\mathbf{n}\cdot\boldsymbol{\lambda} + \varepsilon\tilde{g}_n\) with \(\boldsymbol{\lambda}\) = VECTOR_LAGRANGE_MULTIPLIER |
\(\bar{\lambda}_n \lt 0\); on activation \(\boldsymbol{\lambda} \leftarrow \mathbf{n}\,\bar{\lambda}_n/k\) | – |
ALMFrictionalMortarConvergenceCriteria |
ComputeALMFrictionalActiveSet(rModelPart, PureSlip, EchoLevel) |
\(k\,\mathbf{n}\cdot\boldsymbol{\lambda} + \varepsilon\tilde{g}_n\) | \(\bar{\lambda}_n \lt 0\); on activation \(\boldsymbol{\lambda} \leftarrow \mathbf{n}\bar{\lambda}_n/k + \bar{\boldsymbol{\lambda}}_\tau/k\) (tangent part omitted if \(\mu = 0\)) | see below |
PenaltyFrictionlessMortarConvergenceCriteria |
ComputePenaltyFrictionlessActiveSet(rModelPart) |
\(\varepsilon\tilde{g}_n\) | \(\bar{\lambda}_n \lt 0\) | – |
PenaltyFrictionalMortarConvergenceCriteria |
ComputePenaltyFrictionalActiveSet(rModelPart, PureSlip, EchoLevel) |
\(\varepsilon\tilde{g}_n\) | \(\bar{\lambda}_n \lt 0\) | see below |
Deactivated nodes reset WEIGHTED_SLIP and the SLIP flag. This is exactly the nodal NCP function of thesis eq. 4.44, \(\mathcal{C}_{\lambda_n} = k\lambda_n - \max(0, k\lambda_n + \varepsilon\tilde{g}_n)\), evaluated with the sign convention “negative pressure means compression”.
For the frictional checks the tangential multiplier of an active node is \(\boldsymbol{\lambda}_\tau = \boldsymbol{\lambda} - (\mathbf{n}\cdot\boldsymbol{\lambda})\mathbf{n}\) and the augmented tangent pressure (stored in AUGMENTED_TANGENT_CONTACT_PRESSURE) is
$$\bar{\boldsymbol{\lambda}}_\tau = k\boldsymbol{\lambda}_\tau + c_\tau\,\varepsilon\,\tilde{\mathbf{g}}_\tau \quad\text{(stick node)},\qquad \bar{\boldsymbol{\lambda}}_\tau = k\boldsymbol{\lambda}_\tau + c_{slip}\,c_\tau\,\varepsilon\,\tilde{\mathbf{g}}_\tau \quad\text{(slip node)}$$
where \(c_{slip}\) is ProcessInfo[SLIP_AUGMENTATION_COEFFICIENT] (0 if absent). The node is set to slip when \(\Vert\bar{\boldsymbol{\lambda}}_\tau\Vert / (-\mu\bar{\lambda}_n) \gt \theta\) with \(\theta = 1\) for a stick node and \(\theta = 1 -\) SLIP_THRESHOLD for a node that is already slipping (a small hysteresis that avoids chattering), and to stick otherwise; in the slip case AUGMENTED_TANGENT_CONTACT_PRESSURE is overwritten by the Coulomb limit \(-\mu\bar{\lambda}_n\,\boldsymbol{\lambda}_\tau/\Vert\boldsymbol{\lambda}_\tau\Vert\) (thesis eqs. 4.75–4.77). With PureSlip every active node is forced to SLIP and a stick detection only produces a warning. The penalty frictional version uses \(\bar{\boldsymbol{\lambda}}_\tau = c_\tau\varepsilon\tilde{\mathbf{g}}_\tau\) and the stick condition \(\Vert\bar{\boldsymbol{\lambda}}_\tau\Vert \le -\mu\bar{\lambda}_n\). The frictional utilities return an array_1d<std::size_t, 2> with the number of active-set changes and the number of stick/slip changes; the ALM criterion reports both separately, the penalty one requires the sum to vanish.
MPCContactCriteria
Source. Defaults: {"name" : "mpc_contact_criteria"}. In the MPC route there are no multipliers, so the contact pressure has to be recovered from the reactions:
PreCriteria(lines 146-175) resets the non-historicalCONTACT_FORCEof theContactnodes, savesWEIGHTED_GAPin buffer position 1, recomputes the weighted gap (ComputeWeightedGap) and zeroesNODAL_AREA.PostCriteria(lines 187-380), whenNL_ITERATION_NUMBER > 0: recomputes the weighted gap andNODAL_AREA; for every pairContactSub<i>it mapsREACTIONfromMasterSubModelPart<i>toSlaveSubModelPart<i>with aSimpleMortarMapperProcessWrapperconfigured as{"distance_threshold" : 1.0e24, "update_interface" : false, "origin_variable" : "REACTION", "mapping_coefficient" : -1.0e0}(the threshold is replaced byProcessInfo[DISTANCE_THRESHOLD]when available), so that the slave reaction accumulates the (sign-inverted) master reaction. Then, for everySLAVEnode, the normal contact force is \(f_n = \mathbf{R}\cdot\mathbf{n}\), the contact pressure is \(p_n = f_n/\)NODAL_MAUX, the nodal gap is \(g = \tilde{g}_n/\)NODAL_AREA, and the node is active if \(p_n \lt -(\texttt{REACTION\_CHECK\_STIFFNESS\_FACTOR}\cdot E)\) or \(g \lt 0\), where \(E\) is theYOUNG_MODULUSof the first element properties (the threshold is 0 when no Young modulus is found; the factor defaults to \(10^{-12}\) in the criterion and is set tompc_contact_settings.reaction_check_stiffness_factor, default \(10^{-10}\), byMPCContactProcess). Active nodes storeCONTACT_FORCE\(= f_n\mathbf{n}/\)NODAL_PAUXandNORMAL_CONTACT_STRESS\(= p_n\). In the frictional case (model partIs(SLIP)) the tangential pressure \(\Vert\mathbf{R} - f_n\mathbf{n}\Vert/\)NODAL_MAUXis compared with \(-\mu f_n\) to toggleSLIPand to storeTANGENTIAL_CONTACT_STRESS.- Finally every condition of
ComputingContactwhose slave nodes are all inactive is set inactive together with its constraint (CONSTRAINT_POINTER, error if missing): this is the “tension check” that releases the constraint when the interface would work in traction. Convergence is reached when no node changed its active (or slip) state.
MortarAndConvergenceCriteria
Source. A specialization of the core And_Criteria that combines the mechanical criterion (first argument) and the mortar active-set criterion (second argument). Defaults:
{
"name" : "mortar_and_criteria",
"print_convergence_criterion" : false
}
The constructor takes an optional ConditionNumberUtility::Pointer. What it adds to the plain And_Criteria:
Initializeadds the leading columnITER(andCOND.NUM.when a condition-number utility is given) to theTABLE_UTILITYtable, then lets the children add theirs;InitializeSolutionStepprints the table header.PostCriteriawritesNL_ITERATION_NUMBERin the row, delegates toAnd_Criteria::PostCriteria, and, if a condition-number utility exists, copies the system matrix and printsGetConditionNumber(A)(computed with the power-iteration eigenvalue solverspower_iteration_highest_eigenvalue_solver/power_iteration_eigenvalue_solverbuilt by the Python factory whencontact_settings.condn_convergence_criterionistrue; this is expensive and meant for diagnosis only). On convergence the table footer is printed.
This is the object the Python factory always returns for the contact_* criteria, which is why the console shows the characteristic table with the displacement, multiplier and active-set columns (thesis Figs. 4.18 and 4.23).
Displacement, residual, mixed and frictional families
These nine criteria are the mechanical half of the MortarAndConvergenceCriteria. They differ in what is measured (increment Dx, residual b, or residual for displacements and increment for the multipliers), and in how the multiplier block is split. Each family has a version without multipliers (used with the penalty formulations, where the only unknowns are displacements), with one multiplier block (scalar ALM, components ALM, mesh tying) and with three multiplier blocks (frictional ALM: normal, stick-tangent, slip-tangent).
| Measured quantity | No multipliers (penalty) | One multiplier block | Normal + stick + slip blocks |
|---|---|---|---|
Increment (Dx) |
DisplacementContactCriteria |
DisplacementLagrangeMultiplierContactCriteria |
DisplacementLagrangeMultiplierFrictionalContactCriteria |
Residual (b) |
DisplacementResidualContactCriteria |
DisplacementLagrangeMultiplierResidualContactCriteria |
DisplacementLagrangeMultiplierResidualFrictionalContactCriteria |
| Mixed (residual for \(\mathbf{u}\), increment for \(\boldsymbol{\lambda}\)) | – (the factory reuses DisplacementResidualContactCriteria) |
DisplacementLagrangeMultiplierMixedContactCriteria |
DisplacementLagrangeMultiplierMixedFrictionalContactCriteria |
The residual versions store the norm of the first iteration (INITIAL_RESIDUAL_IS_SET) and use it as reference for the ratio; they require the RHS to be up to date, which is why SetActualizeRHSFlag(True) is set on the composite. The mixed versions are usually the most robust choice for ALM: the multiplier residual is the weak gap, which is not a good convergence indicator while the active set is still changing.
displacement_contact_criteria.h:
{
"name" : "displacement_contact_criteria",
"ensure_contact" : false,
"print_convergence_criterion" : false,
"displacement_relative_tolerance" : 1.0e-4,
"displacement_absolute_tolerance" : 1.0e-9,
"rotation_relative_tolerance" : 1.0e-4,
"rotation_absolute_tolerance" : 1.0e-9
}
displacement_residual_contact_criteria.h:
{
"name" : "displacement_residual_contact_criteria",
"ensure_contact" : false,
"print_convergence_criterion" : false,
"residual_relative_tolerance" : 1.0e-4,
"residual_absolute_tolerance" : 1.0e-9,
"rotation_residual_relative_tolerance" : 1.0e-4,
"rotation_residual_absolute_tolerance" : 1.0e-9
}
displacement_lagrangemultiplier_contact_criteria.h:
{
"name" : "displacement_lagrangemultiplier_contact_criteria",
"ensure_contact" : false,
"print_convergence_criterion" : false,
"displacement_relative_tolerance" : 1.0e-4,
"displacement_absolute_tolerance" : 1.0e-9,
"rotation_relative_tolerance" : 1.0e-4,
"rotation_absolute_tolerance" : 1.0e-9,
"contact_displacement_relative_tolerance" : 1.0e-4,
"contact_displacement_absolute_tolerance" : 1.0e-9
}
displacement_lagrangemultiplier_residual_contact_criteria.h:
{
"name" : "displacement_lagrangemultiplier_residual_contact_criteria",
"ensure_contact" : false,
"print_convergence_criterion" : false,
"residual_relative_tolerance" : 1.0e-4,
"residual_absolute_tolerance" : 1.0e-9,
"rotation_residual_relative_tolerance" : 1.0e-4,
"rotation_residual_absolute_tolerance" : 1.0e-9,
"contact_residual_relative_tolerance" : 1.0e-4,
"contact_residual_absolute_tolerance" : 1.0e-9
}
displacement_lagrangemultiplier_mixed_contact_criteria.h:
{
"name" : "displacement_lagrange_multiplier_mixed_contact_criteria",
"ensure_contact" : false,
"print_convergence_criterion" : false,
"residual_relative_tolerance" : 1.0e-4,
"residual_absolute_tolerance" : 1.0e-9,
"rotation_residual_relative_tolerance" : 1.0e-4,
"rotation_residual_absolute_tolerance" : 1.0e-9,
"contact_displacement_relative_tolerance" : 1.0e-4,
"contact_displacement_absolute_tolerance" : 1.0e-9
}
displacement_lagrangemultiplier_frictional_contact_criteria.h:
{
"name" : "displacement_lagrangemultiplier_frictional_contact_criteria",
"ensure_contact" : false,
"pure_slip" : false,
"print_convergence_criterion" : false,
"displacement_relative_tolerance" : 1.0e-4,
"displacement_absolute_tolerance" : 1.0e-9,
"rotation_relative_tolerance" : 1.0e-4,
"rotation_absolute_tolerance" : 1.0e-9,
"contact_displacement_relative_tolerance" : 1.0e-4,
"contact_displacement_absolute_tolerance" : 1.0e-9,
"frictional_stick_contact_displacement_relative_tolerance" : 1.0e-4,
"frictional_stick_contact_displacement_absolute_tolerance" : 1.0e-9,
"frictional_slip_contact_displacement_relative_tolerance" : 1.0e-4,
"frictional_slip_contact_displacement_absolute_tolerance" : 1.0e-9,
"ratio_normal_tangent_threshold" : 1.0e-4
}
displacement_lagrangemultiplier_residual_frictional_contact_criteria.h:
{
"name" : "displacement_lagrangemultiplier_ressidual_frictional_contact_criteria",
"ensure_contact" : false,
"pure_slip" : false,
"print_convergence_criterion" : false,
"residual_relative_tolerance" : 1.0e-4,
"residual_absolute_tolerance" : 1.0e-9,
"rotation_residual_relative_tolerance" : 1.0e-4,
"rotation_residual_absolute_tolerance" : 1.0e-9,
"contact_residual_relative_tolerance" : 1.0e-4,
"contact_residual_absolute_tolerance" : 1.0e-9,
"frictional_stick_contact_residual_relative_tolerance" : 1.0e-4,
"frictional_stick_contact_residual_absolute_tolerance" : 1.0e-9,
"frictional_slip_contact_residual_relative_tolerance" : 1.0e-4,
"frictional_slip_contact_residual_absolute_tolerance" : 1.0e-9
}
displacement_lagrangemultiplier_mixed_frictional_contact_criteria.h:
{
"name" : "displacement_lagrangemultiplier_mixed_frictional_contact_criteria",
"ensure_contact" : false,
"pure_slip" : false,
"print_convergence_criterion" : false,
"residual_relative_tolerance" : 1.0e-4,
"residual_absolute_tolerance" : 1.0e-9,
"rotation_residual_relative_tolerance" : 1.0e-4,
"rotation_residual_absolute_tolerance" : 1.0e-9,
"contact_displacement_relative_tolerance" : 1.0e-4,
"contact_displacement_absolute_tolerance" : 1.0e-9,
"frictional_stick_contact_displacement_relative_tolerance" : 1.0e-4,
"frictional_stick_contact_residual_relative_tolerance" : 1.0e-9,
"frictional_slip_contact_displacement_relative_tolerance" : 1.0e-4,
"frictional_slip_contact_residual_relative_tolerance" : 1.0e-9,
"ratio_normal_tangent_threshold" : 1.0e-4
}
Note: in the mixed frictional block the keys frictional_stick_contact_residual_relative_tolerance and frictional_slip_contact_residual_relative_tolerance play the role of the absolute tolerances of the tangential blocks (their default is \(10^{-9}\)); the Python factory maps them accordingly (FSTCR_AT, FSLCR_AT). In the frictional criteria the convergence of the multiplier block is
$$\text{conv}_\lambda = \text{conv}(\lambda_n) \;\wedge\; \big[\text{conv}(\lambda_{\tau,stick}) \vee r_{stick}/r_n \le \theta_{nt}\big] \;\wedge\; \big[\text{conv}(\lambda_{\tau,slip}) \vee r_{slip}/r_n \le \theta_{nt}\big]$$
with \(\theta_{nt}\) the ratio_normal_tangent_threshold and \(r\) the absolute norms (thesis eq. 4.78 checks the four residuals \(\mathbf{r}_u\), \(\mathbf{r}_{\lambda_n}\), \(\mathbf{r}_{\lambda_\tau^{sl}}\), \(\mathbf{r}_{\lambda_\tau^{st}}\) separately for the same reason).
ContactErrorMeshCriteria
Source. Used only by the adaptive-remeshing analysis stage (adaptive_remeshing/adaptative_remeshing_contact_structural_mechanics_utilities.py, when the error criterion is adaptative_remesh_criteria), never by the plain solvers; it needs the MeshingApplication. Defaults:
{
"name" : "contact_error_mesh_criteria",
"error_mesh_tolerance" : 5.0e-3,
"error_mesh_constant" : 5.0e-3,
"compute_error_extra_parameters":
{
"penalty_normal" : 1.0e4,
"penalty_tangential" : 1.0e4,
"echo_level" : 0
}
}
PostCriteria flags the nodes and conditions of Contact with CONTACT, runs ContactSPRErrorProcess<2> or <3> (a superconvergent-patch-recovery error estimator that accounts for the contact tractions through the two penalties) and compares ProcessInfo[ERROR_RATIO] with error_mesh_tolerance. Returning true means that the discretization error is below the tolerance and no remeshing is required. See Adaptive remeshing.
MeshTyingMortarConvergenceCriteria
Source. Defaults: {"name" : "mesh_tying_mortar_criteria"}. Mesh tying has no inequality, hence no active set; the class only participates in the table printing and always returns true. It is what GetMortarCriteria returns when "MeshTying" in mortar_type, so that the same MortarAndConvergenceCriteria composition works for tying and for contact (see Mesh tying).
How the Python factory composes the criteria
ContactConvergenceCriteriaFactory (contact_convergence_criteria_factory.py) receives the parameters assembled by auxiliary_methods_solvers.AuxiliaryCreateConvergenceParameters: the standard keys of solver_settings (convergence_criterion, rotation_dofs, echo_level, displacement_* and residual_* tolerances) plus the contact-specific keys copied from contact_settings (all the rotation_*, contact_*, frictional_* tolerances, ratio_normal_tangent_threshold, mortar_type, condn_convergence_criterion, print_convergence_criterion, ensure_contact, frictional_decomposed, compute_dynamic_factor, gidio_debug). The composition depends on the convergence_criterion string:
convergence_criterion |
mortar_type contains Penalty |
ALMContactFrictional* and frictional_decomposed |
Otherwise (scalar / components ALM, mesh tying) |
|---|---|---|---|
contact_displacement_criterion |
DisplacementContactCriteria |
DisplacementLagrangeMultiplierFrictionalContactCriteria |
DisplacementLagrangeMultiplierContactCriteria |
contact_residual_criterion |
DisplacementResidualContactCriteria |
DisplacementLagrangeMultiplierResidualFrictionalContactCriteria |
DisplacementLagrangeMultiplierResidualContactCriteria |
contact_mixed_criterion |
DisplacementResidualContactCriteria (no rotation tolerances) |
DisplacementLagrangeMultiplierMixedFrictionalContactCriteria |
DisplacementLagrangeMultiplierMixedContactCriteria |
contact_and_criterion |
KM.AndCriteria(DisplacementResidualContactCriteria, DisplacementContactCriteria) |
(same as “otherwise”) | KM.AndCriteria(DisplacementLagrangeMultiplierResidualContactCriteria, DisplacementLagrangeMultiplierContactCriteria) |
contact_or_criterion |
KM.OrCriteria(...) of the same pair |
(same as “otherwise”) | KM.OrCriteria(...) of the same pair |
adaptative_remesh_criteria |
None (the adaptive-remeshing analysis builds ContactErrorMeshCriteria itself) |
||
any other value (displacement_criterion, residual_criterion, and_criterion, or_criterion, …) |
The StructuralMechanicsApplication convergence_criteria_factory builds the criterion; if mortar_type contains ALMContact or MeshTying it is combined with the mortar criterion in a plain KM.AndCriteria (no table) with SetActualizeRHSFlag(True) |
For every contact_* value the mechanical criterion is then wrapped as CSMA.MortarAndConvergenceCriteria(mechanical, Mortar, print_convergence_criterion, condition_number_utility), where Mortar = GetMortarCriteria() is chosen from mortar_type:
mortar_type |
Mortar criterion |
|---|---|
ALMContactFrictionless |
ALMFrictionlessMortarConvergenceCriteria(print, compute_dynamic_factor, gidio_debug) |
ALMContactFrictionlessComponents |
ALMFrictionlessComponentsMortarConvergenceCriteria(...) |
contains ALMContactFrictional |
ALMFrictionalMortarConvergenceCriteria(pure_slip, ...) |
PenaltyContactFrictionless |
PenaltyFrictionlessMortarConvergenceCriteria(...) |
contains PenaltyContactFrictional |
PenaltyFrictionalMortarConvergenceCriteria(pure_slip, ...) |
contains MeshTying |
MeshTyingMortarConvergenceCriteria() |
pure_slip is True when mortar_type contains PureSlip, otherwise it is detected by AuxiliaryPureSlipCheck (all FRICTION_COEFFICIENT values of the properties are zero). The MPC solvers do not use this factory: MPCContactStaticSolver._CreateConvergenceCriterion builds the structural criterion and combines it with MPCContactCriteria() in a KM.AndCriteria.
Relation to the thesis algorithms
The code above is the implementation of thesis Algorithm 2 (frictionless, §4.3.3.5) and Algorithm 3 (frictional, §4.3.4.4), whose active-set strategy is the primal-dual active-set / semi-smooth Newton method of §4.3.3.6 and §4.3.4.5. In pseudo-code, annotated with the classes that perform each line:
Algorithm 2 (thesis) — frictionless contact Implementation
1 t = 0, i = 0; u^0 = 0, lambda^0 = 0 StructuralMechanicsAnalysis, AuxiliaryAddDofs
2 Initialize the active set A_1^0, I_1^0 search (ACTIVE_CHECK_FACTOR) in SearchBaseProcess
3 while t < t_end:
4 t = t + dt, i = i + 1; Delta u_1^i = 0 strategy InitializeSolutionStep / Predict
5 search contact pairs, update pairs and active set SearchBaseProcess.ExecuteInitializeSolutionStep
6 conv = false
7 while not conv: ResidualBasedNewtonRaphsonContactStrategy::BaseSolveSolutionStep
8 PreCriteria: normals, tangents, gap, penalty BaseMortarConvergenceCriteria::PreCriteria
9 solve the linearized system (thesis 4.3.3.4.3) BuilderAndSolver::BuildAndSolve (+ MixedULMLinearSolver)
10 u^i_{n+1} = u^i_n + Delta u, lambda likewise UpdateDatabase
11 update active set with threshold (eq. 4.41-4.42) ActiveSetUtilities::Compute*ActiveSet via PostCriteria
A := {j : k*lambda_n + eps*g_n < 0}
I := {j : k*lambda_n + eps*g_n >= 0}
12 check ||r_u|| < tol_u, ||r_lambda|| < tol_lambda Displacement*ContactCriteria::PostCriteria
13 conv = (A, I unchanged) and residuals converged MortarAndConvergenceCriteria (And)
14 FinalizeSolutionStep; output strategy, SearchBaseProcess.ExecuteFinalizeSolutionStep
Algorithm 3 (thesis) — frictional contact Differences with respect to Algorithm 2
2 also initialize the slip/stick sets A_sl, A_st SLIP flag (search pre-activation, slip_step_reset_frequency)
8 tangents are recomputed every iteration MortarUtilities::ComputeNodesTangentModelPart in PreCriteria
11 update A, I (eq. 4.73-4.74) and then A_sl, A_st ActiveSetUtilities::ComputeALMFrictionalActiveSet
with the frictional threshold F = mu*(k n.lambda + eps*g_n) (eq. 4.76) and the
tangent stress t = ||k tau.lambda + eps_tau u_tau|| (eq. 4.77)
12 four residuals: r_u, r_lambda_n, r_lambda_tau^sl, r_lambda_tau^st (eq. 4.78)
DisplacementLagrangeMultiplier*FrictionalContactCriteria
13 conv = (A, I, A_sl, A_st unchanged) and residuals ALMFrictionalMortarConvergenceCriteria reports
ACTIVE SET CONV and SLIP/STICK CONV separately
The only structural deviation from the thesis algorithms is the simplified_semi_smooth_newton mode, in which line 11 is executed after a complete Newton loop instead of inside every iteration; it trades robustness (the active set cannot oscillate within the Newton iterations) for cost (each active-set update costs a full Newton solve) and is the mode of choice when the full semi-smooth Newton chatters, see Tips, troubleshooting and limitations.
Figure: the active-set flowchart, mapping thesis Algorithms 2 and 3 to the strategy, the criteria and ActiveSetUtilities.
The theory behind the thresholds (augmented pressure, NCP functions, Figs. 4.19 and 4.24 of the thesis) is developed in Frictionless contact and Frictional contact; the linear systems solved in line 9 and the way the multipliers are condensed are in Builder and solvers and linear solvers.
Quick reference: which settings reach which class
solver_settings key |
Consumed by |
|---|---|
solving_strategy_settings.type (newton_raphson, line_search, arc_length) |
ContactStaticMechanicalSolver._CreateSolutionStrategy → AuxiliaryNewton / AuxiliaryLineSearch |
max_iteration, compute_reactions, reform_dofs_at_each_step, move_mesh_flag |
Strategy constructors (positional arguments) |
convergence_criterion, displacement_*, residual_*, rotation_dofs, echo_level |
ContactConvergenceCriteriaFactory |
contact_settings.simplified_semi_smooth_newton |
INTERACTION flag of the computing model part (contact solvers) or of its ProcessInfo (MPC solvers) |
contact_settings.adaptative_strategy, split_factor, max_number_splits, inner_loop_iterations |
ResidualBasedNewtonRaphsonContactStrategy |
contact_settings.fancy_convergence_criterion |
Creates the TABLE_UTILITY (TableStreamUtility) read by every criterion |
contact_settings.condn_convergence_criterion |
ConditionNumberUtility passed to MortarAndConvergenceCriteria |
contact_settings.print_convergence_criterion, ensure_contact, frictional_decomposed, compute_dynamic_factor, gidio_debug, contact_* / frictional_* tolerances, ratio_normal_tangent_threshold |
The mechanical and mortar criteria through the factory |
contact_settings.silent_strategy |
ContactStaticMechanicalSolver.Initialize sets the strategy echo level to 0 right after the base Initialize has created it |
mpc_contact_settings.inner_loop_iterations, update_each_nl_iteration, enforce_ntn, simplified_semi_smooth_newton |
ResidualBasedNewtonRaphsonMPCContactStrategy |
The complete list of keys with their defaults is in the Solver settings reference.