Skip to content

Enforced constraint pipelines¤

Pipelines compose and apply enforced-constraint ansätze.

For the underlying ansatz constructors (enforce_dirichlet, enforce_neumann, etc.), see Enforced constraint ansätze.

Note

Key notes:

  • An EnforcedConstraintPipeline stages overlays in the order boundary → initial → interior data.
  • EnforcedConstraintPipelines topologically orders multi-field dependencies (co_vars).
  • For a detailed mathematical treatment of the PCI pipeline (including BVH-weighted boundary blending, boundary–initial gating, and the interior anchor/data stage), see Appendix → Physics-Constrained Interpolation.

phydrax.solver.SingleFieldEnforcedConstraint ¤

Enforced constraint term acting on a single field.

An enforced constraint is a transformation that builds an ansatz \(\tilde u\) from a base field \(u\) such that some condition is satisfied by construction. Conceptually this is a map

\[ \mathcal{H}: u \mapsto \tilde u \]

where \(\tilde u\) is intended to satisfy, e.g., Dirichlet boundary conditions \(\tilde u|_{\partial\Omega}=g\), initial conditions \(\tilde u(\cdot,t_0)=u_0\), or similar constraints.

SingleFieldEnforcedConstraint stores the metadata needed to stage and compose these transformations inside EnforcedConstraintPipeline.

co_vars property ¤

Names of co-dependent fields (always empty for single-field terms).

__init__(field: str, component: DomainComponent, apply: Callable[[DomainFunction], DomainFunction], /, *, max_derivative_order: int = 0, time_derivative_order: int = 0, initial_target: DomainFunction | ArrayLike | None = None) ¤

Create a single-field enforced constraint.

Arguments:

  • field: Name of the field being modified (e.g. "u").
  • component: The domain subset where the constraint is defined (boundary, initial, etc.).
  • apply: The enforcement map \(\mathcal{H}\) implemented as a callable that returns a new DomainFunction.

Keyword arguments:

  • max_derivative_order: Maximum spatial derivative order that the constraint construction expects to be well-defined (used for staging and gating in composite pipelines).
  • time_derivative_order: When the constraint represents an initial derivative target, this is the derivative order \(k\) for \(\partial_t^k u(\cdot,t_0)\).
  • initial_target: Optional target for the initial derivative order: provide \(g_k\) for \(\partial_t^k u(\cdot,t_0)=g_k\).

Notes:

  • If initial_target is provided, EnforcedConstraintPipeline will group targets for orders \(k=0,\dots,K\) and build a single initial overlay.

phydrax.solver.MultiFieldEnforcedConstraint ¤

Enforced constraint term that depends on other fields.

Some enforced constraint constructions require access to co-variables (other fields) when enforcing a given field. For example, a boundary condition for a stress-like quantity might depend on both displacement and material parameters.

This stores an apply function of the form

\[ \tilde u = \mathcal{H}\big(u,\ \{v\}_{v\in\texttt{co\_vars}}\big), \]

where get_field(name) supplies the current (possibly already enforced) DomainFunction for each co-variable.

__init__(field: str, component: DomainComponent, co_vars: Sequence[str], apply: Callable[[DomainFunction, Callable[[str], DomainFunction]], DomainFunction], /, *, max_derivative_order: int = 0, time_derivative_order: int = 0) ¤

Create a multi-field enforced constraint.

Arguments:

  • field: Name of the field being modified.
  • component: The domain subset where the constraint is defined.
  • co_vars: Names of fields that apply depends on.
  • apply: An enforcement map \(\mathcal{H}\) implemented as apply(u, get_field) -> DomainFunction.

Keyword arguments:

  • max_derivative_order: Maximum spatial derivative order expected by the constraint.
  • time_derivative_order: Optional initial-derivative order metadata.

phydrax.solver.EnforcedInteriorData ¤

Interior data source for enforced data overlays.

EnforcedInteriorData represents measurements (or anchor constraints) that you want to enforce by construction in the interior of a domain, without competing with boundary/initial enforced constraints.

Two input modes are supported:

1) Anchor points: a set of points \(z_j\) with values \(y_j\).

  • points: mapping from domain label to coordinate arrays.
  • values: an array of values with leading dimension \(N\).

2) Sensor tracks: a set of fixed sensors \(x_m\) observed over times \(t_n\).

  • sensors: array with shape \((M,d)\) (or \((d,)\) for a single sensor).
  • times: array with shape \((N,)\).
  • sensor_values: array with shape \((M,N)\) or \((M,N,C)\).

The enforced overlay built from this data uses inverse-distance weights (IDW) to compute a correction term \(\Delta u(z)\) from residuals \(r_j = y_j - u(z_j)\), while multiplying by a gate \(M(z)\) that vanishes on constrained sets (boundary / initial time) so the overlay does not destroy those conditions.

See EnforcedConstraintPipeline for how this integrates with other enforced stages.

__init__(field: str, /, *, points: Mapping[str, ArrayLike] | None = None, values: ArrayLike | None = None, sensors: ArrayLike | None = None, times: ArrayLike | None = None, sensor_values: ArrayLike | None = None, idw_exponent: float = 2.0, eps_snap: float = 1e-12, lengthscales: Mapping[str, float] | None = None, use_envelope: bool = False, envelope_scale: float = 1.0, space_label: str = 'x', time_label: str = 't', time_interp: Literal[idw, hermite] = 'idw') ¤

Create an enforced interior data source.

Arguments:

  • field: Name of the field that this data applies to.

Anchor mode:

  • points: Mapping {label: coords} giving anchor coordinates per domain label. Geometry labels should use shape (N,d) and scalar labels shape (N,).
  • values: Anchor values with shape (N,) or (N,C).

Sensor-track mode:

  • sensors: Sensor locations, shape (M,d) (or (d,)).
  • times: Observation times, shape (N,).
  • sensor_values: Observations, shape (M,N) or (M,N,C).
  • space_label: Domain label corresponding to space.
  • time_label: Domain label corresponding to time.
  • time_interp:
    • "idw" flattens the \((x_m,t_n)\) grid into anchors in \((x,t)\) and uses IDW in the full domain.
    • "hermite" uses a cubic Hermite spline in time and IDW only in space (requires a 2-factor domain with (space_label, time_label)).

IDW details:

  • idw_exponent: Power \(p\) in weights \(w_j(z)\propto (\|z-z_j\|^2+\varepsilon)^{-p/2}\).
  • lengthscales: Optional per-label lengthscales \(\ell_\alpha\) used inside the distance metric: \(\|z-z_j\|^2=\sum_\alpha \|(z_\alpha-z_{j,\alpha})/\ell_\alpha\|^2\).
  • eps_snap: Snap threshold: when \(z\) is closer than eps_snap to an anchor, the overlay uses a one-hot weight so that \(u(z)\) matches the anchor exactly.

Envelope (optional):

  • use_envelope: If enabled, multiplies IDW weights by a source-local envelope.
  • envelope_scale: Envelope scale \(s\) in \(\psi(z)=\exp(-d(z)^2/s^2)\).

phydrax.solver.EnforcedConstraintPipeline ¤

Compose enforced overlays for a single field.

A pipeline takes a base field \(u\) and returns an enforced field \(\tilde u\) after applying a sequence of enforced transformations:

  1. Boundary overlays (if any), typically enforcing conditions on \(\partial\Omega\) using smooth blend weights.
  2. Initial overlays (if any), enforcing values and/or time-derivative targets at \(t=t_0\).
  3. Interior data overlays (optional), enforcing interior anchors/tracks while preserving the boundary/initial conditions via a multiplicative gate that vanishes on the constrained sets.

Boundary/initial stages are blended using a boundary gate \(\gamma(z)\), where \(\gamma=0\) on the constrained boundary and \(\gamma\approx 1\) away from it:

\[ u \leftarrow u + \gamma\,(u_{\text{next}}-u). \]

This ensures that later stages do not re-violate boundary enforced constraints.

__init__(u_base: DomainFunction, /, *, field: str, constraints: Sequence[SingleFieldEnforcedConstraint | MultiFieldEnforcedConstraint] = (), interior_data: Sequence[EnforcedInteriorData] = (), evolution_var: str = 't', include_identity_remainder: bool = True, num_reference: int = 3000000, sampler: str = 'latin_hypercube', key: Key[Array, ''] = jr.key(0)) ¤

Build a pipeline for one field.

Arguments:

  • u_base: Base DomainFunction for the field.

Keyword arguments:

  • field: Field name (used to match constraints/data to the field).
  • constraints: Enforced constraint terms (single-field or multi-field).
  • interior_data: Interior data sources used to build an IDW-based enforced overlay.
  • evolution_var: Name of the time-like label used to detect initial constraints (default "t").
  • include_identity_remainder: When blending multiple boundary pieces, include a remainder weight for the identity map (keeps \(u\) unchanged away from all pieces).
  • num_reference: Reference sample count used to normalize boundary blend weights.
  • sampler: Sampler used to draw reference points.
  • key: PRNG key used to draw reference points.

Notes:

  • Boundary staging currently requires boundary constraints to specify exactly one geometry boundary label, shared across all boundary pieces.
apply(u_base: DomainFunction, /, *, get_field: Callable[[str], DomainFunction]) -> DomainFunction ¤

phydrax.solver.EnforcedConstraintPipelines ¤

Enforced constraint pipelines for multiple fields.

When enforcing multiple fields \(\{u^{(k)}\}\), some enforced constraints may require access to co-variables (other fields). This object builds a directed acyclic graph (DAG) from MultiFieldEnforcedConstraint.co_vars and applies per-field EnforcedConstraintPipelines in a topological order so that dependencies are available when needed.

__init__(pipelines: Mapping[str, EnforcedConstraintPipeline], /, *, field_order: Sequence[str]) ¤

Create a multi-field pipeline orchestrator.

Arguments:

  • pipelines: Mapping {field: EnforcedConstraintPipeline}.

Keyword arguments:

  • field_order: Preferred ordering for tie-breaking in the toposort (typically tuple(functions.keys())).
build(*, functions: Mapping[str, DomainFunction], constraints: Sequence[SingleFieldEnforcedConstraint | MultiFieldEnforcedConstraint] = (), interior_data: Sequence[EnforcedInteriorData] = (), evolution_var: str = 't', include_identity_remainder: bool = True, num_reference: int = 3000000, sampler: str = 'latin_hypercube', key: Key[Array, ''] = jr.key(0)) -> EnforcedConstraintPipelines classmethod ¤
apply(functions: Mapping[str, DomainFunction]) -> frozendict[str, DomainFunction] ¤

Apply all pipelines and return an enforced field mapping.

Pipelines are applied in a dependency-respecting order. If a pipeline for field \(u\) requires co-variables \(\{v\}\), then those \(v\) are taken from the current enforced mapping as the iteration proceeds.