Skip to content

docs: correct why IndexSwarmVariable level sets keep inverse distance - #453

Merged
lmoresi merged 1 commit into
developmentfrom
bugfix/index-swarm-levelset
Jul 28, 2026
Merged

docs: correct why IndexSwarmVariable level sets keep inverse distance#453
lmoresi merged 1 commit into
developmentfrom
bugfix/index-swarm-levelset

Conversation

@lmoresi

@lmoresi lmoresi commented Jul 28, 2026

Copy link
Copy Markdown
Member

Documentation only. Corrects the reasoning recorded in #430 for leaving IndexSwarmVariable on inverse-distance weighting. The conclusion is unchanged; the stated mechanism was wrong and the supporting measurement was unsound.

What was wrong

The argument. It claimed a material indicator is piecewise constant, so linear exactness has nothing to gain away from an interface and cannot help at one. That conflates two fields: the indicator is the particle data, but the quantity estimated at a node is the local material fraction, which near an interface is a smooth ramp — precisely what a constants-only scheme cannot reproduce. Raised by the maintainer.

The measurement. It applied a gather-form variant, which models update_type=1, while the shipped default is update_type=0 — a scatter, where each particle accumulates 1/d into its nearest node (the k=nnn query is masked by is_nearest). There is no stencil there to re-weight, so the branches must be measured separately. Its interface metric also grouped unstructured nodes into rows by y, and its maximum was set by node spacing rather than by the weights.

Re-measured

Particles take material 1 with probability p(x) = x, so the exact nodal fraction is a known linear function. Three seeds, cellSize 1/16 and 1/32:

scheme bias rms level-set range
shipped update_type=0 (scatter) ~1e-3 0.09 – 0.11 [0, 1]
shipped update_type=1 (gather) ~1e-3 0.18 [0, 1]
gather, order=1 ~1e-3 0.18 – 0.20 [-0.35, 1.32]

The actual mechanism: variance, not bias

A node estimates the fraction from a handful of integer samples, so

Var(estimate) = sum_j w_j^2 * Var(f_j)

For weights summing to one, uniform weighting minimises sum(w^2) at 1/nnn. Inverse-distance weights are positive and stay near that floor; linear-exact weights are signed and sit well above it:

dim nnn sum w² IDW sum w² order=1 1/nnn amplification
2 6 0.223 2.314 0.167 10.4x
2 20 0.079 0.922 0.050 11.7x
3 6 0.188 1.290 0.167 6.9x
3 20 0.061 0.657 0.050 10.9x

Inverse distance averages the noise down as the stencil grows; order=1 barely moves. The noise cannot be bought off with more neighbours.

The bias that linear exactness would remove is already ~1e-3 for every scheme, because a roughly symmetric stencil reproduces a linear ramp in expectation. Partition of unity holds for any weight sign (shared denominator; per-particle indicator flags sum to one) and was never the objection.

Consequence worth having

The lever for material-fraction accuracy is more samples per node, not better polynomial reproduction. That is consistent with the scatter branch — which aggregates every particle nearest to a node, typically many more than nnn — having the lowest rms of the three.

Script and output: ~/+Simulations/linear-rbf-proxy/step4_index_swarm_volume_fraction.{py,txt}. Docs build verified.

Underworld development team with AI support from Claude Code

The recorded reason was wrong. It argued that a material indicator is
piecewise constant, so linear exactness has nothing to gain away from an
interface and cannot help at one. That conflates two fields: the indicator
is the PARTICLE data, but the quantity estimated at a node is the local
material FRACTION, which near an interface is a smooth ramp -- precisely
what a constants-only scheme cannot reproduce.

The supporting measurement was also unsound: it applied a gather-form
variant, which models update_type=1, while the shipped default is
update_type=0, a scatter (each particle accumulates 1/d into its nearest
node, masked by is_nearest). There is no stencil there to re-weight, so
the branches have to be measured separately. Its interface metric grouped
unstructured nodes into rows by y, and its maximum was set by node spacing
rather than by the weights.

Re-measured against a known analytic fraction: particles take material 1
with probability p(x) = x, so the exact nodal value is p(x_node).

  scheme                              bias      rms      range
  shipped update_type=0 (scatter)     ~1e-3   0.09-0.11  [0, 1]
  shipped update_type=1 (gather)      ~1e-3   0.18       [0, 1]
  gather order=1                      ~1e-3   0.18-0.20  [-0.35, 1.32]

The conclusion stands but the mechanism is variance, not bias. A node
estimates the fraction from a handful of INTEGER samples, so
Var(estimate) = sum_j w_j^2 Var(f_j). Uniform weights minimise sum(w^2)
at 1/nnn; inverse distance stays near that floor while linear-exact
weights, being signed, sit 6-12x above it -- and, unlike inverse distance,
do not come down as the stencil grows:

  dim nnn   sum w^2 IDW   sum w^2 order=1   1/nnn   amplification
   2   6       0.223           2.314        0.167       10.4x
   2  20       0.079           0.922        0.050       11.7x
   3   6       0.188           1.290        0.167        6.9x
   3  20       0.061           0.657        0.050       10.9x

The bias linear exactness would remove is already ~1e-3 for every scheme,
because a roughly symmetric stencil reproduces a linear ramp in
expectation. Partition of unity holds for any weight sign (shared
denominator, indicator flags sum to one per particle) and was never the
objection.

Actionable consequence: the lever for material-fraction accuracy is more
samples per node, not better polynomial reproduction -- consistent with
the scatter branch, which aggregates every particle nearest to a node,
having the lowest rms.

Script and output: ~/+Simulations/linear-rbf-proxy/step4_*.

Underworld development team with AI support from Claude Code
Copilot AI review requested due to automatic review settings July 28, 2026 02:03

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@lmoresi

lmoresi commented Jul 28, 2026

Copy link
Copy Markdown
Member Author

Adversarial review

The new measurement still cannot resolve bias

Every scheme reports bias ~1e-3, and the run averages three seeds. That is not enough to claim the bias terms are equal — it is enough to say all are small compared with an rms of 0.1–0.2. If linear exactness removes a bias of, say, 5e-3 that inverse distance carries, this test would not see it, because the noise floor is two orders of magnitude larger and only three seeds are averaged.

The claim in the doc is therefore stated as "already ~1e-3 for every scheme", not "identical". The stronger statement — that a symmetric stencil reproduces a linear ramp in expectation under inverse distance — is an analytical argument, not something this measurement establishes.

sum(w^2) is the right quantity but the sampling is not the real one

The variance table uses uniform-random source points, whereas real swarms are populated per cell and are far more regular. Stencil geometry drives sum(w^2), so the amplification factor on an actual populated swarm could differ from 6–12x. The qualitative result — signed weights sit an order of magnitude above the floor and do not descend with nnn — is robust to that, but the specific numbers are for a random cloud.

Bernoulli assignment is not how materials are usually set

Real models assign material by region, not by coin flip, so the per-node sample variance in practice is zero away from interfaces and large only in the transition band. The Bernoulli construction was chosen because it makes the exact nodal fraction analytically known — but it puts noise everywhere, which flatters the variance argument relative to a model where only interface nodes are noisy.

The interface band is where it matters, and there the argument holds. Away from interfaces both schemes are exact regardless. So the conclusion survives, but the rms columns overstate how much of a typical domain is affected.

update_type=0 is not shown to be better because of sample count

The doc says the scatter having the lowest rms is "consistent with" the variance argument, and that is as far as the evidence goes. I did not measure the effective number of particles aggregated per node, which is what would turn consistency into demonstration. It is one plausible explanation; another is that is_nearest plus radius_s filtering happens to suit this test's geometry.

What holds up

  • The mechanism is now falsifiable and independently checkable: sum(w^2) is a property of the weights alone, computable without any swarm.
  • Both branches are identified correctly and measured separately, and the doc states plainly that only one has a stencil to re-weight.
  • The [0, 1] violation is reproduced on the real nodal quantity, and is the part of the case that does not depend on any statistical argument.

@lmoresi
lmoresi merged commit 967fb30 into development Jul 28, 2026
2 checks passed
@lmoresi
lmoresi deleted the bugfix/index-swarm-levelset branch July 28, 2026 02:23
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.

2 participants