Contact API Boundaries#

This page fixes terminology, ownership, and scope for contact APIs.

Object Model#

  • contact: geometric and pairing description of the interface. It owns master/slave surfaces, trace pairing/supermesh, normals, and interface quadrature.

  • multiplier: discrete LM space definition for constraint-family assembly. It owns LM family (nodal/p0), side, value dimension, and optional facet connectivity.

  • formulation: enforcement/formulation intent used by assembly routing. Typical values are multiplier-family (mortar/KKT) and penalty-family (Nitsche-like).

  • ops: assembled operator bundle to pass into system builders. For constraint-family: coupling/B/Kuu (+ optional residual/jacobian metadata). For penalty-family: residual/jacobian (+ metadata).

Dependency Direction#

The intended dependency chain is:

contact -> multiplier -> formulation/assembly -> ops -> CoupledSystemBuilder

contact and multiplier are defined separately, but are coupled at assembly time.

Axes: family / enforcement / formulation#

Current public interpretation:

  • family: high-level route (constraint or penalty)

  • enforcement: concrete route (mortar or nitsche)

  • formulation: variant within the route (examples: multiplier / augmented_lagrangian / penalty_consistent)

Rule: keep one meaning per axis and use the same terms in builder/docstrings/tutorials.

Configuration Scope#

Settings are scoped as follows:

  • contact scope: geometry, pairing/supermesh, quadrature, backend and Jacobian assembly behavior.

  • multiplier scope: LM discretization parameters (family/side/value_dim/facet_conn).

  • formulation scope: enforcement-specific parameters and weak-form variant choices.

  • builder.add_contact(...) call scope: routing choice and per-contact runtime options; each call can use different values.

For multiple contacts:

  • Separate add_contact(...) calls can use different family/enforcement/formulation/multiplier choices.

  • OneToManyContactSurfaceSpace groups multiple pair contacts under one contact object; settings passed when creating that object are shared by that object.

Known Constraint: p0 Side#

ContactMultiplierSpace(family="p0") currently supports side="master" only. This is an implementation limitation in the current code path, not a mathematical requirement.

Use side="master" for now and treat side="slave" for p0 as future work.

Guarantees Covered by Tests#

Current tests explicitly target these guarantees:

  • multiple contact contributions can be added to one coupled system,

  • mortar/KKT assembly remains valid when different contacts have different lambda sizes,

  • route consistency for penalty vs constraint contact operators.

Backend Direction#

The intended backend direction for contact is numpy/jax parity on the main public paths.

The reason is split by use case:

  • numpy is the preferred lightweight reference/debug backend

  • jax is the preferred backend for autodiff, Jacobians, and nonlinear contact assembly

Implementation priority should follow this order:

  1. pair contact

  2. one-to-many contact

  3. one-sided contact

  4. full weak-form Jacobian parity

Until that work is complete, treat the auto-selected JAX path as the default route for advanced contact assembly and use explicit backend="numpy" primarily where parity has already been validated.

Coordinate Differentiation#

Coordinate differentiation is not yet a general guarantee for contact.

Current practical status:

  • volume assembly already has working JAX-based examples of differentiation with respect to coordinates

  • surface linear-form assembly also has direct coordinate-differentiation tests

  • contact assembly still contains several NumPy-only geometry conversion paths, so geometry tracing is not yet robust across the main contact routes

In particular, the current contact interface code still relies on NumPy geometry materialization in several places such as:

  • np.asarray(surface_a.coords, dtype=float)

  • np.asarray(surface_b.coords, dtype=float)

  • facet-area and facet-shape helper loops that build NumPy arrays from quadrature-point geometry data

So the current rule should be:

  • assume coordinate differentiation is available for JAX volume/surface paths

  • do not assume it for contact until the geometry pipeline is made tracer-safe

The natural implementation order is:

  1. remove np.asarray(...coords...) barriers from pair contact geometry prep

  2. replace NumPy facet-shape/facet-area accumulation loops on the pair path

  3. validate JAX coordinate gradients on pair contact before extending to one-to-many and one-sided paths