What happens
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 own
U = 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.
🤖 Generated with Claude Code
https://claude.ai/code/session_018T2VHUGaZiQVJ95qQ4DiSL
What happens
UWexpressionreportsis_zero,is_positiveandis_negativefrom its current value (expressions.py, the assumption properties delegate toself._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: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 velocityexp(-c) * x * (1 - x), the Stokes and Navier-Stokes solvers keep the lid atexp(0)afterc.sym = 2.0; withccreated 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,Piecewiseconditions): a body force, a source, a ramped boundary value, all of which naturally start at zero.Where
src/underworld3/function/expressions.py, theis_zero/is_positive/is_negative/is_finiteproperties (added in 9b738ea for the units work).is_comparablewas already made unconditionallyFalsefor 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.🤖 Generated with Claude Code
https://claude.ai/code/session_018T2VHUGaZiQVJ95qQ4DiSL