Skip to content

Self-assigning a constitutive Parameter (read then write back) recurses to stack death #447

Description

@lmoresi

Reading a constitutive Parameters value and assigning it straight back is not a no-op — it nests a UWexpression inside itself, and the next .value access recurses until the stack dies.

cm = stokes.constitutive_model
saved = cm.Parameters.yield_stress      # read
...
cm.Parameters.yield_stress = saved      # write back  -> RecursionError later

Traceback (abridged):

constitutive_models.py:_apply_floor      return uw.maths.smooth_max(value, floor, rounding)
maths/functions.py:smooth_max            return (a + b + sympy.sqrt((a - b) ** 2 + epsilon**2)) / 2
function/expressions.py:__add__          result_value = self.value + other.value
function/expressions.py:947 value        if hasattr(self._sym, 'value'):
  [Previous line repeated 977 more times]
RecursionError: maximum recursion depth exceeded

UWexpression.value (expressions.py:947) walks self._sym looking for a nested .value, and self-assignment makes _sym reference the expression that owns it, so the walk never terminates.

Why it bites in practice. Save/restore around a temporary parameter change is the natural way to probe a model without disturbing it — e.g. removing a regularisation to measure a residual against the unregularised law, then putting it back. That pattern is silently a trap. The workaround is to re-derive the value rather than restore the object, which is only possible if you know how it was built.

Two things worth considering:

  • the setter could unwrap an incoming UWexpression that is already this parameter's own expression (or, more generally, refuse to nest an expression inside itself);
  • UWexpression.value could carry a cycle guard so the failure is a clear error rather than a stack overflow 977 frames deep.

Related: the same class of self-referential blow-up was seen with sympy.Max(<UWexpression>, x) recursing to stack death during the Drucker–Prager consistent-Jacobian work, which is why uw.maths.smooth_max exists.

Found while measuring true hard-Min residuals during viscoplastic continuation work (PETSc 3.25, development).

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions