Reduced Equation Builder#

This tutorial summarizes tutorials/craig_bampton/reduced_equation_builder_nonlinear.py and tutorials/craig_bampton/reduced_equation_contact_constraint.py. It shows the residual-first layer for future differentiable reduced formulations.

Run#

PYTHONPATH=src python tutorials/craig_bampton/reduced_equation_builder_nonlinear.py
PYTHONPATH=src python tutorials/craig_bampton/reduced_equation_contact_constraint.py

What It Does#

The script creates two Craig-Bampton-reduced fields and composes:

  • one nonlinear field-local residual per subsystem

  • one user-defined constraint residual across the retained interface coordinates

  • a global reduced residual vector

  • a global reduced Jacobian from jax.jacrev

  • optional static Newton and implicit Newmark solves on the same residual API

This complements ReducedCoupledSystemBuilder. Use the coupled-system builder for assembled linear K/M/f workflows; use ReducedEquationBuilder when the governing equation is naturally expressed as residual functions.

Key API Shape#

builder = ff.ReducedEquationBuilder()
builder.register_field("part_a", basis=cb_a)
builder.register_field("part_b", basis=cb_b)

builder.add_field_residual("part_a", residual_a)
builder.add_field_residual("part_b", residual_b)
builder.add_constraint(("part_a", "part_b"), coupling_residual)

class GapConstraint:
    fields = ("part_a", "part_b")

    def residual(self, qa, qb):
        ...

builder.add_constraint(GapConstraint())

problem = builder.build()
q, info = ff.solve_reduced_equation(problem, q0)
r = problem.residual(q)
j = problem.jacobian(q)

For transient reduced dynamics, use the same residual as the internal force:

next_state, info = ff.reduced_equation_newmark_step(
    problem, mass, damping, external_force, state, config
)

Contact constraints follow the same pattern:

class CBPlaneContactConstraint:
    fields = ("body",)

    def residual(self, q):
        u = cb.expand(q)
        return cb.project_vector(contact.residual(u))

builder.register_field("body", basis=cb)
builder.add_constraint(CBPlaneContactConstraint())

For frozen-active contact or similar stateful constraints, provide a problem factory and an update callback:

q, info = ff.solve_reduced_equation_active(
    q0, initial_state, problem_from_state, update_state
)

The same active-state callback shape is available for one implicit Newmark step:

next_state, info = ff.reduced_equation_active_newmark_step(
    problem_from_state, mass, damping, external_force, state, config,
    initial_state, update_state
)

Residual functions own the physics. The builder only handles reduced-field slicing and scattering.