Source code for fenicsx_compat.ufl

import ufl


[docs] def domain_of(expr): """Get the single UFL domain an expression is defined on. `ufl.domain.extract_domains` is available across the whole 0.10+ floor; the `expr.ufl_domain()` fallback found duplicated across the source repos (some with a fallback, some without — see spec §5.6) is below-floor legacy and is not ported. """ domains = ufl.domain.extract_domains(expr) if len(domains) != 1: raise ValueError(f"Expected exactly one domain, got {len(domains)}") return domains[0]
def _inverse_identity(expr, domain): return expr def _inverse_contravariant_piola(expr, domain): """`u = J u_ref / det J`, inverted: `u_ref = det J K u`.""" return ufl.JacobianDeterminant(domain) * ufl.dot(ufl.JacobianInverse(domain), expr) def _inverse_covariant_piola(expr, domain): """`u = K^T u_ref`, inverted: `u_ref = J^T u`.""" return ufl.dot(ufl.transpose(ufl.Jacobian(domain)), expr) _INVERSE_PULLBACKS = { "IdentityPullback": _inverse_identity, "ContravariantPiola": _inverse_contravariant_piola, "CovariantPiola": _inverse_covariant_piola, }
[docs] def apply_pullback_inverse(pullback, expr, domain): """Map `expr` from the physical cell to the reference cell. Uses `pullback.apply_inverse`, added in FEniCS/ufl PR #511 (first released in UFL 2026.2.0), when the installed UFL has it. The UFL releases dolfinx 0.10 and 0.11 pin (2025.2, 2026.1) lack it, so a closed form is used for the identity, contravariant Piola and covariant Piola pullbacks; any other pullback raises `NotImplementedError` there. Dispatches on `hasattr`. """ if hasattr(pullback, "apply_inverse"): return pullback.apply_inverse(expr, domain) name = type(pullback).__name__ try: return _INVERSE_PULLBACKS[name](expr, domain) except KeyError: raise NotImplementedError( f"This UFL has no {name}.apply_inverse (added in FEniCS/ufl PR #511), and " "fenicsx-compat has no closed form for it. Upgrade UFL." ) from None