Proxy mesh variables inherit the swarm variable's units (#439) - #450
Conversation
`SwarmVariable._create_proxy_variable` did not forward `units=` to the
proxy MeshVariable. Since `var.sym` resolves to the proxy, reading a
proxied variable through the symbolic path returned the NON-DIMENSIONAL
number with no units attached, presented as though it were the answer.
Measured before, with density=3300 kg/m^3 as the reference quantity and a
stored non-dimensional value of 1.0:
var.units kilogram / meter ** 3
var._meshVar.units None
var.array 3300.0
var._meshVar.array 1.0 <- disagree
evaluate(var.sym) ndarray 1.0, no units <- silently wrong
after:
var._meshVar.units kilogram / meter ** 3
var.array 3300.0
var._meshVar.array 3300.0
evaluate(var.sym) UnitAwareArray 3300.0 kg/m^3
Stored data stays non-dimensional on both sides; only what the proxy
advertises changes, so the transfer itself is untouched.
This is the half of #426 that PR #434 deliberately left out, split off at
the time as #439 because it needed a units-boundary decision rather than a
coordinate fix. Maintainer decision was to forward the units and measure
the blast radius.
Blast radius: none. level_1 571 passed (567 + the 4 new tests), level_2
319 passed, np=2 parallel 4 passed, zero failures. No existing test both
declares units on a swarm variable and reads it symbolically -- which is
also why the defect survived, and is itself worth recording.
Variables that declare no units keep a dimensionless proxy, asserted by
test.
Underworld development team with AI support from Claude Code
Adversarial review"Blast radius: none" is a weaker result than it reads571/319/4 passing means no existing test exercises the changed path at all, not that the change is safe. The condition to trip it is: a swarm variable that declares So the honest claim is "nothing regressed that is tested". Anything downstream that composed a proxied symbol and relied on it being dimensionless — arithmetic against a bare float, a constitutive term assembled without unit checking — now sees a unit-aware operand. The suite cannot tell us whether such code exists, and the The place I would expect trouble is multi-material constitutive assembly, where a density proxy multiplies a level-set fraction. The type of
|
Fixes #439.
The defect
SwarmVariable._create_proxy_variabledid not forwardunits=to the proxyMeshVariable. Sincevar.symresolves to the proxy, reading a proxied variable through the symbolic path returned the non-dimensional number with no units attached, presented as though it were the answer.Measured, with
density=3300 kg/m³as the reference quantity and a stored non-dimensional value of1.0:var.unitskg/m³kg/m³var._meshVar.unitsNonekg/m³var.arrayvar._meshVar.arrayevaluate(var.sym)ndarray1.0, no unitsUnitAwareArray3300.0 kg/m³The two rows in bold are the bug: the swarm variable and its own proxy disagreed about the value, and
evaluatehanded back the raw non-dimensional number silently.Stored data stays non-dimensional on both sides — only what the proxy advertises changes, so the transfer itself is untouched (asserted by test).
Context
This is the half of #426 that #434 deliberately left out. It was split off at the time because attaching units to the proxy makes
var.symunit-aware everywhere proxies are composed into expressions, which is a units-boundary decision rather than a coordinate fix. Maintainer decision was to forward the units and measure what moves.Blast radius: none
tier_a/tier_b: 571 passed (567 baseline + the 4 new tests), 0 failedtier_a/tier_b: 319 passed, 0 failedNo existing test both declares units on a swarm variable and reads it symbolically. That is why the change moves nothing — and also why the defect survived. Worth recording as a coverage gap in its own right, not just as a convenient result.
Tests added
To
tests/test_0116_swarm_proxy_units.py(created by #434):.unitsequals the variable's.units.arrayon both sides reaches the same dimensional number, while.datastays non-dimensional on bothevaluate(var.sym)returns aUnitAwareArraywith the right units and value — the silent-wrong-value caseUnderworld development team with AI support from Claude Code