You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
A uw.function.expression whose value is zero is folded out of expressions by sympy at construction, so a runtime constant that starts at zero never updates #696
UWexpression reports is_zero, is_positive and is_negative from its current value (expressions.py, the assumption properties delegate to self._sym). Sympy uses those assumptions in automatic evaluation, so an expression whose value is zero when a formula is built is evaluated away before any JIT sees it, and changing the value later has no effect:
t=uw.function.expression(r"t", 0.0, "time")
F=sympy.exp(-0.02*t) # exp(-0.02*t): survives on its ownU=sympy.Matrix([[-sympy.sin(x) *sympy.cos(y) *F, 0]]) # -> -sin(x)*cos(y): F is gone
(-0.02*t).is_zero# True, from the value
A solver boundary condition built this way is frozen at the value it had at construction. With c = expression("c", 0.0) and a lid velocity exp(-c) * x * (1 - x), the Stokes and Navier-Stokes solvers keep the lid at exp(0) after c.sym = 2.0; with c created at 1.0 the same code follows the value exactly. We found it on the Taylor-Green vortex benchmark, where a time-dependent exact Dirichlet velocity built from a time expression created at t = 0 gave the same 4.7e-3 error on 1/16, 1/32 and 1/64 meshes at every time step, with the decay 10% too slow; freezing the time deliberately gave the identical answer to four digits.
The same folding will hit any formula in which a runtime constant multiplies or feeds a function that sympy evaluates on a zero argument (Mul, exp, Pow, Piecewise conditions): a body force, a source, a ramped boundary value, all of which naturally start at zero.
Where
src/underworld3/function/expressions.py, the is_zero / is_positive / is_negative / is_finite properties (added in 9b738ea for the units work). is_comparable was already made unconditionally False for a related reason (#415: Max/Min tried to compare the contents).
Not patched here
Reported rather than changed: the assumption properties may be relied on elsewhere (the rotated free-slip path tests sympify(conds).is_zero, and possibly the units and non-dimensionalisation code), so the choice of what a runtime constant should advertise to sympy is a design decision. The Taylor-Green driver works around it by creating the time expression at a non-zero value.
Fixed in 92ed8ac on #688: UWexpression.is_zero, is_positive and is_negative answer None instead of reading the current value, so sympy no longer folds exp(c) (or a sign-dependent simplification) on a runtime constant created at zero. Control in tests/test_0503_integral_expression_constants.py::test_a_constant_created_at_zero_still_reaches_the_kernel (fails before the fix, passes after); the level-1 suite passes (1704). Reopen if #688 does not land.
Activity
Design note: the cylinder drag was the missing viscous traction (#695)…
UWexpression never reports is_zero, is_positive or is_negative from i…
lmoresi commentedon Sep 7, 2026
Fixed in 92ed8ac on #688:
UWexpression.is_zero,is_positiveandis_negativeanswer None instead of reading the current value, so sympy no longer foldsexp(c)(or a sign-dependent simplification) on a runtime constant created at zero. Control intests/test_0503_integral_expression_constants.py::test_a_constant_created_at_zero_still_reaches_the_kernel(fails before the fix, passes after); the level-1 suite passes (1704). Reopen if #688 does not land.🤖 Generated with Claude Code
https://claude.ai/code/session_018T2VHUGaZiQVJ95qQ4DiSL
Navier-Stokes with Eulerian SUPG momentum transport, and a partition-…
Parameters given with units are known real (underworldcode#823)