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.jacrevoptional 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.