Skip to content

Fix evaluate() NameError with mixed MeshVariable + coordinate expressions#61

Merged
lmoresi merged 1 commit intodevelopmentfrom
bugfix/poisson-natural-bc
Mar 1, 2026
Merged

Fix evaluate() NameError with mixed MeshVariable + coordinate expressions#61
lmoresi merged 1 commit intodevelopmentfrom
bugfix/poisson-natural-bc

Conversation

@lmoresi
Copy link
Member

@lmoresi lmoresi commented Mar 1, 2026

Summary

  • Fixed NameError: name '_uw_x' is not defined when evaluating expressions that mix MeshVariable symbols with coordinate system vectors (e.g., D.sym * mesh.CoordinateSystem.unit_e_0)
  • Root cause: lambdify uses object identity to match argument symbols, but expressions contained both UWCoordinate and BaseScalar instances for the same coordinates — lambdify treated them as different variables
  • Fix: canonicalize all coordinate symbols to Dummy symbols before lambdify, avoiding the identity mismatch entirely

Test plan

  • Original issue scenario: D.sym * mesh.CoordinateSystem.unit_e_0 on Annulus mesh
  • Value verification: result matches expected radial unit vector
  • unit_e_1 variant also works
  • Mixed expressions: D.sym[0] * x + y
  • Cartesian box regression: no changes to existing behavior
  • Pure coordinate expressions: still use optimized path
  • 42 existing tests pass (vector calculus, Poisson, Stokes)

Closes #57

Underworld development team with AI support from Claude Code

…ions

When evaluating expressions like `D.sym * mesh.CoordinateSystem.unit_e_0`,
lambdify failed with `NameError: name '_uw_x' is not defined`.

Root cause: the expression contained both UWCoordinate objects (from
unit_e_0) and BaseScalar objects (from zero_matrix additions). Since
lambdify uses object identity to match argument symbols, it didn't
recognize UWCoordinate as the same variable as the BaseScalar argument,
producing generated code with unresolvable `_uw_x`/`_uw_y` names.

Fix: canonicalize all coordinate symbols (both UWCoordinate and
BaseScalar variants) to a single set of Dummy symbols before lambdify.

Closes #57

Underworld development team with AI support from Claude Code
Copilot AI review requested due to automatic review settings March 1, 2026 09:49
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR addresses a SymPy lambdify argument-matching failure in uw.function.evaluate() that produced NameError: name '_uw_x' is not defined when mixing MeshVariable symbols with coordinate-system vector expressions (notably on curvilinear meshes like Annulus).

Changes:

  • Adds a pre-lambdify canonicalization step that replaces coordinate-like symbols in the expression with per-axis sympy.Dummy symbols.
  • Ensures the same canonical Dummy symbols are used as the lambdify coordinate arguments, avoiding object-identity mismatches.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +309 to +311
idx = sym._id[0]
if idx < dim:
coord_subs[sym] = coord_dummies[idx]
Copy link

Copilot AI Mar 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The coordinate canonicalization currently replaces all sympy.vector.scalar.BaseScalar symbols based only on their index (sym._id[0]). This can unintentionally rewrite BaseScalars that belong to a different coordinate system than N (e.g., mesh.Gamma_N.x vs mesh.N.x) and make them evaluate against coords_list silently instead of failing, producing incorrect results. Consider restricting replacements to BaseScalars whose _id[1] matches the CoordSys3D instance N (or otherwise validating the symbol’s coordinate system) before substituting to Dummy.

Suggested change
idx = sym._id[0]
if idx < dim:
coord_subs[sym] = coord_dummies[idx]
# sym._id is typically a tuple (index, CoordSys3D). Only
# canonicalize BaseScalars that belong to the coordinate system N.
try:
coord_idx = sym._id[0]
coord_sys = sym._id[1]
except (AttributeError, IndexError, TypeError):
# If _id is not in the expected form, skip this symbol.
continue
if coord_sys is not N:
# Do not rewrite BaseScalars from other coordinate systems.
continue
if coord_idx < dim:
coord_subs[sym] = coord_dummies[coord_idx]

Copilot uses AI. Check for mistakes.
Comment on lines +295 to +315
# 2b. Canonicalize coordinate symbols for lambdify.
# The expression may contain UWCoordinate objects (from mesh.X or
# mesh.CoordinateSystem.unit_e_0) alongside BaseScalar objects. Since
# lambdify uses object identity to map arguments to generated code,
# we must ensure only ONE set of coordinate objects appears.
# Strategy: collect all coordinate-like symbols from the expression,
# group by index, and replace all variants with a single canonical
# sympy.Dummy symbol per coordinate. Use the same Dummy as the
# lambdify argument.
from sympy.vector.scalar import BaseScalar
coord_dummies = [sympy.Dummy(f"_coord_{i}") for i in range(dim)]
coord_subs = {}
for sym in subbedexpr.free_symbols:
if isinstance(sym, BaseScalar):
idx = sym._id[0]
if idx < dim:
coord_subs[sym] = coord_dummies[idx]
if coord_subs:
subbedexpr = subbedexpr.xreplace(coord_subs)
r = coord_dummies

Copy link

Copilot AI Mar 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change fixes a specific mixed MeshVariable + coordinate-system-unit-vector evaluation failure, but there’s no regression test added to ensure it doesn’t reappear. Please add a test that reproduces issue #57 (e.g., evaluating D.sym * mesh.CoordinateSystem.unit_e_0 / unit_e_1 on an Annulus mesh) and asserts the numeric result, so future changes to the lambdify path don’t regress.

Copilot uses AI. Check for mistakes.
@lmoresi lmoresi merged commit 3a22eea into development Mar 1, 2026
5 checks passed
@NengLu
Copy link
Contributor

NengLu commented Mar 1, 2026

Thank you, Louis. Your solution fixed my problem.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants