Skip to content

Zone/overlap resolution is non-deterministic on z_order ties #13

Description

@rhugman

Two places resolve z_order ties with a non-stable sort, so the result depends on pandas' internal ordering rather than on anything deterministic. z_order defaults to 0, so ties are the common case, not an edge case.

Site 1 — ConceptualMesh._resolve_overlaps (src/vorflow/blueprint.py:135 on develop)

df = df.sort_values(by='z_order', ascending=False)

DataFrame.sort_values defaults to kind='quicksort', which is not stable. The loop that follows is a cookie-cutter: each polygon subtracts the union of everything processed before it. So for two polygons with equal z_order and a genuine overlap, which one keeps the overlapping area is arbitrary, and can change between pandas versions or with unrelated changes to row order.

Site 2 — VoronoiTessellator.generate (src/vorflow/tessellator.py:287 on develop)

joined = joined.sort_values('z_order', ascending=False)
joined = joined.drop_duplicates(subset='node_id')

Same problem, plus there is no secondary key. A generator point sitting exactly on a shared zone border matches several zones in the sjoin, and which zone_id survives drop_duplicates is arbitrary.

Suggested fix

MeshGenerator._assign_zones_to_elements, added in #12, already does this correctly:

joined = joined.sort_values(sort_cols, ascending=ascending, kind="mergesort")

with sort_cols = ["element_tag", "z_order", "index_right"] — a stable sort plus a deterministic secondary key. Applying the same pattern to both sites above would make all three paths agree.

Worth noting the consequence of the current mismatch: because #12's element grid uses the deterministic rule and the Voronoi path does not, a single run can assign the same border cell to different zones in the Voronoi grid and in the triangular element grid.

Verification

A regression test with two equal-z_order overlapping polygons asserting a fixed partition covers site 1; a generator point placed exactly on a shared border covers site 2.

Pre-existing on develop — not introduced by #12, raised separately so it does not block that PR.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions