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.
Two places resolve
z_orderties with a non-stable sort, so the result depends on pandas' internal ordering rather than on anything deterministic.z_orderdefaults to0, so ties are the common case, not an edge case.Site 1 —
ConceptualMesh._resolve_overlaps(src/vorflow/blueprint.py:135ondevelop)DataFrame.sort_valuesdefaults tokind='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 equalz_orderand 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:287ondevelop)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_idsurvivesdrop_duplicatesis arbitrary.Suggested fix
MeshGenerator._assign_zones_to_elements, added in #12, already does this correctly: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_orderoverlapping 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.