Skip to content

Changelog

All notable changes to this project are documented in this file.

The format is based on Keep a Changelog, and this project follows Semantic Versioning.

[Unreleased]

[0.8.0] - 2026-08-17

Added

  • A complete installed-distribution license payload: project LICENSE and COPYING, NOTICE.md, and a root LICENSE.voro++ byte-identical to the vendored upstream Voro++ license. Distribution and installed-package checks now reject missing or mutated license content.
  • Standard GIL-enabled CPython 3.14 source-build and package-metadata support, while retaining Python 3.10–3.13 support. The isolated build toolchain now uses Python-3.14-compatible pybind11 3 and scikit-build-core releases.
  • Source-install CI qualification for Python 3.14 on Linux, macOS, and Windows, with explicit installed-module provenance and forward/inverse smoke tests. Distribution CI now rebuilds and installs a wheel from the generated sdist in a fresh no-SciPy environment.
  • Complete standard CPython 3.10–3.14 binary wheels for manylinux x86_64, Windows AMD64, macOS arm64, and macOS x86_64, with native installed-wheel checks and aggregate validation of exactly 20 wheels plus one source distribution.

Changed

  • Synchronized current API/lifecycle, R1–R9 correctness, capability-boundary, ADR-0017 roadmap, release-note traceability, fuzz-execution, and qualification handoff documentation for the final v0.8 source-contract review. The OS-independent package classifier was removed; supported platforms are the explicit v0.8 release wheel matrix.
  • Reorganized tests by forward, inverse, integration, tooling, and fuzz responsibility, and moved private pure-Python helpers under pyvoro2._internal with explicit shared, spatial, and planar ownership. Root-owned _core and _core2d remain lazy internal native extensions; public imports, signatures, and numerical behavior are unchanged.
  • Finalized the v0.8 public API inventory, canonical API-selection and migration guidance, architecture and contributor documentation, notebook references, generated README, reference navigation, and v0.8.0 release notes against the exact current exports and distribution workflows.

Removed

  • Completed the announced v0.7 compatibility lifecycle by removing the temporary pyvoro2.powerfit facade and submodules, the lazy top-level package attribute, broad top-level separator exports, the five historical separator core aliases, PlanarComputeResult, and planar return_result=. Canonical separator APIs remain under pyvoro2.inverse and pyvoro2.inverse.separator; numerical behavior is unchanged.

Fixed

  • Made spatial and planar tessellation diagnostics severity-complete. Missing expected standard IDs, malformed/non-finite/negative cell measures, valid- measure closure gaps/overlaps, and required reciprocity defects now produce explicit errors and fail one final diagnostics.ok; hidden power IDs and optional reciprocity remain informative and nonfatal. Strict validation and public compute warning/raising now consume that final policy, marked reanalysis clears stale boundary flags, stable measure sums replace broad conversion swallowing, and warning-only planar normalized-topology findings no longer fail strict validation.
  • Made the experimental separator active-set final state atomic. Final optimal and weighted max_iter refits now recompute realization, candidate diagnostics, residual summaries, and requested tessellation diagnostics from the exact accepted weights. A no-weights final refit keeps its inner fit status without replacing an established outer self_consistent, cycle_detected, or max_outer_iter stop, and exposes weights-dependent result layers and records as None instead of reusing stale geometry or fabricating NaN rows. Active results add computed availability/final-refit convergence properties, and active reports add an availability block plus JSON-null unavailable sections while preserving the schema-1 envelope, source provenance, row IDs, and strict exact JSON round trips.
  • Replaced bounded periodic nearest-image inference with one private certified 2D/3D geometry primitive. Orthogonal and partially periodic domains use exact per-axis choices; fully periodic non-orthogonal 3D cells exact-enumerate a proof-derived finite box over the dyadic rational values represented by the supplied binary64 data. The known skewed-cell regression now selects (2, -1, -1) instead of (1, 0, -1). Exact ties are deterministic under lattice translation and pair reversal, explicit user shifts remain authoritative, and resource exhaustion fails structurally without an approximate fallback. image_search keeps its public default and signature as a correctness-neutral incumbent-seeding hint. Separator inference and periodic duplicate pair-distance evaluation share the primitive. Mandatory forward safety always wraps; disabling optional wrapping preserves the established unwrapped Cartesian user-threshold check.
  • Centralized spatial and planar generator preparation for compute, locate, and ghost_cells. Inserted generators now use half-open non-periodic containment and primary periodic coordinates, including temporary ghost generators. A fixed inclusive squared-distance floor of 1e-10 is mandatory under every public duplicate option; safe pairs above it retain optional off/warn/raise policy. Exact source-binary64 triclinic keys and inverse-basis bounds make the local candidate scan complete across bucket boundaries and periodic seams without unconditional all-pairs work on ordinary clouds; sparse bin counts retain radius locality across large spans, and positive subnormal triclinic thresholds remain valid duplicate-check inputs. DuplicateError preserves its positional contract while adding safety, policy, truncation, operation, and external-ID provenance. Direct native calls repeat containment and duplicate checks before insertion, using outward-rounded binary64 intervals for triclinic keys, shift ranges, and squared-distance lower bounds rather than relying on extended long double precision. Raw standard/power compute IDs are validated before result packaging.
  • Completed strict Python input adoption across spatial and planar forward APIs, domains, duplicate checks, normalization and diagnostics, and separator inverse workflows. Exact integer and Boolean fields no longer accept lossy conversion or truthiness; string modes reject arrays, bytes, numeric values, and arbitrary equality objects before choice comparison; NumPy string scalars are retained only as canonical built-in strings; and numerical inputs and tolerances reject non-real or non-finite values before reductions, casts, linear algebra, or solver loops. Domains now own canonical nested float/Boolean tuples, retained observation, regularization, mask, and problem arrays are owned and read-only, left-handed PeriodicCell inputs fail at construction, and remapping checks signed-int64 shift representability before conversion. Normalization now revalidates exact integer metadata in mutable raw cell records and rejects coordinate/tolerance quantization outside its finite signed-int64 key range before topology construction or in-place annotation. Public signatures, defaults, result schemas, valid numerical behavior, R1/R2 mathematics, and the R3-A native resource policy are unchanged.
  • Hardened all 12 spatial and 6 planar native container-construction routes. Native grid controls now use strict positive exact-integer semantics, point/query/radius and domain values are rejected when malformed or non-finite, derived C++ integer and floating-point constructor arithmetic is checked, and a source-derived aggregate 1-GiB cap guards known eager native allocations before construction. The spatial periodic resource estimate now bounds Voro++'s source extents max(v_y + ||v||) and max(v_z + ||v||) with an outward-rounded checked componentwise L1 bound, closing an under-estimate that could bypass the exact cap. Direct internal native calls receive the same converted-value defense; valid in-cap tessellation behavior and public signatures/defaults are unchanged.
  • Normalized vectorized numpy.ldexp exponents to NumPy's platform C-int dtype at the ufunc boundary, preserving wide internal exponent accumulation and restoring separator inverse workflows on Windows with Python 3.10 and NumPy 1.26.
  • Corrected and centralized the separator inverse objective contract: squared mismatch and the Huber quadratic branch now use 0.5 * residual**2; L2 uses 0.5 * strength * ||weights - reference||**2; reciprocal boundary penalties use a finite convex tangent continuation below epsilon; and zero-strength penalties are exact no-ops across evaluation, coupling, backend selection, and quadratic-operator availability. The existing direct dense/sparse normal system and ordinary squared-loss/L2 fitted weights remain unchanged, while inconsistent ADMM scaling is corrected. Weighted mismatch/L2 values and quadratic row curvature/RHS construction now preserve finite binary64 results without overflowing unweighted intermediates.
  • Hard separator restrictions now use a shared scale-aware float64 absolute-plus-relative measurement predicate. Its accepted interval is mapped into difference constraints without reapplying measurement tolerance to Bellman–Ford path values. Objective breakdowns and fit reports add hard_max_tolerance while retaining raw hard_max_violation; ADMM success additionally requires final hard-row satisfaction.
  • Non-finite reported soft objectives can no longer be packaged or returned as optimal/converged solver results, and supported linear-algebra failure of the optional direct ADMM warm start now falls back to the safe reference/zero initialization. Residual and ADMM convergence summaries use scale-safe norm, sum-of-squares, RMS, and mean-absolute reductions.
  • Completed universal certification of final quadratic binary64 weights after public gauge canonicalization. Exact-zero proof now checks source residuals exactly, and every nonzero direct or quadratic-ADMM candidate requires a conservative source-gradient/singular-value forward-gap bound; an uncertifiable candidate returns structured numerical_failure. Bounded exact helpers use the same continuous-objective certificate rather than an output-resolution floor or an unproved coordinatewise-rounding certificate, so private helper thresholds cannot change the meaning of optimal.
  • Separated separator solver method from linear backend. The default is now solver='direct', linear_backend='dense'; explicit ADMM really iterates whenever a component solve is required, dense routes never import SciPy, and sparse direct/ADMM routes require it. Results, termination views, and fit-report JSON record solver and linear_backend separately; no-work fits report solver='none' and linear_backend=None. ADMM numerical failures retain completed iteration counts, including failures raised during final quadratic certification. The earlier development-only values auto, analytic, and solver value sparse, along with the old ADMM keyword names, are removed.
  • Replaced the unbracketed scalar ADMM proximal Newton loop with a certified private bounded solver. One compiled term kernel now gives private solving, public evaluation, objective breakdowns, reports, and JSON the same complete- expression semantics. Positive-strength penalty rows use rigorous one-sided derivative enclosures, exact point signs or adjacent numeric-float sign brackets, direct termwise endpoint differences, and a binary64/scaled common path with exact algebraic fallback signs and bounded outward transcendental intervals. Derived twofold-ball radii now remain authoritative through cancellation, base-two accumulation, certified ln(2) range reduction, and polynomial exponential bounds; Huber endpoint comparisons use direct branch partitions instead of subtracting complete values. Heterogeneous ordinary penalty rows share a vectorized form of the same ball algebra, with every exceptional or unresolved row routed to the certified scalar path. Batch routing is invariant under caller NumPy exception policy, excludes unsupported reciprocal rows before array arithmetic, and preserves ordinary neighbor lanes when one row is exceptional. Scalar and batch certificates retain common endpoint enclosures and signs plus reconstructable direct- difference selection and fallback provenance. Exhaustion and unresolved evaluation return the existing structured numerical_failure with complete candidate/bracket/fallback detail and completed-iteration accounting; ordinary mismatch-only and zero-strength rows retain their vectorized fast path.
  • Active-set final refits no longer shift positive-L2 solutions toward the previous outer iterate. Zero-L2 component alignment is retained only when exact binary64-input checks prove that all within-component differences are unchanged, preserving the low-level optimality certificate.
  • Separator external IDs and raw observation endpoints now enforce the documented non-negative integer contract consistently, accepting Python and NumPy integers while rejecting lossy float, string, and boolean conversions.
  • Bound separator rows, fits, realizations, active diagnostics, and reports to one canonical two-layer identity contract. Every valid observation set now has deterministic source-independent row IDs and an ordered set fingerprint; resolver-backed observations additionally retain exact caller-order points, domain representation, and ID provenance, while valid direct observations remain honestly unbound until a source-aware use verifies and binds them. Shape-only and bound/unbound associations now fail. Direct construction recomputes and validates redundant geometry/measurement fields and preserves owned read-only arrays. Observation-aligned records add row_id, and all report families add schema version 1, producer, authoritative source, and observation-set records with strict finite JSON serialization. The public row-only builder chain and existing public signatures remain unchanged.
  • Distribution metadata checks now discover wheel and sdist artifacts in Python, so local and GitHub Actions Twine validation no longer depends on shell glob expansion.

[0.7.0] - 2026-07-23

Added

  • Direct mathematical weights= input for spatial and planar power diagrams, with the supplied weights, backend radii, and one common representation shift recorded in the result. Existing valid radii= calls remain supported.
  • One frozen, dimension-neutral TessellationResult for both forward namespaces. Stable input-aligned fields cover sites, IDs, measures, empty cells, representation metadata, diagnostics, and normalization while raw nested cell records remain deliberately mutable.
  • The stable high-level pyvoro2.inverse separator API and canonical implementation ownership under pyvoro2.inverse.separator. Provisional layered fit/identification/realization views and graph, incidence, Laplacian, and quadratic-operator access make solver and gauge behavior inspectable.
  • A provisional explicit SciPy-backed solver='sparse' path for static, unconstrained quadratic separator fits. SciPy remains optional, auto remains dense, backend reporting is explicit, and unsupported Huber, hard-constrained, penalty, and active-set branches reject sparse selection.
  • Repository-owned preferred-API workflows, deterministic paper-style regressions, and static sparse benchmark inputs covering external IDs, disconnected components, infeasibility witnesses, periodic wrong-image realization, active-set diagnostics, dense/sparse agreement, and a large sparse-only case that does not allocate the dense global normal matrix.
  • Reproducible notebook execution and validation with real Jupyter kernels, committed reviewed outputs, deterministic non-executing Markdown export, and notebook-only dependencies outside the ordinary runtime requirements.
  • A maintainer-approved v0.7 lifecycle inventory, migration and API-choice guides, glossary, architecture decisions, contributor workflow, and release checklist. v0.8 is fixed as a cleanup-only compatibility-removal release; prescribed measures and mixed fitting move to v0.9 and v0.10 respectively.

Changed

  • Spatial and planar compute(...) now return TessellationResult by default; output='result' selects it explicitly and output='cells' preserves the historical raw list or (cells, diagnostics) tuple as a one-line migration for v0.6.3 callers. Structured diagnostics stay inside the result.
  • match_realized_pairs(...) now accepts preferred mathematical weights= as an alternative to its retained backend-compatible radii= route.
  • Historical separator names are identity aliases to canonical definitions. pyvoro2.powerfit is a one-way shim, so preferred and compatibility imports share one numerical implementation and compatible object identity.
  • Documented the Voro++ radical/power dynamic-range limitation: finite backend radii or a successful weight conversion do not guarantee numerical resolution when absolute squared radii or genuine weight ranges dwarf squared geometric scales; no universal safe radius cutoff is promised.

Fixed

  • Made the spatial and planar native extensions genuinely lazy so plain and canonical inverse imports neither require nor eagerly load _core or _core2d; forward operations still load their backend on first use.
  • Restored the historical top-level pyvoro2.powerfit package attribute through lazy resolution, without eagerly importing the compatibility package or adding it to pyvoro2.__all__.
  • Improved numerical stability of periodic 2D edge-shift and 3D face-shift reconstruction for large backend-resolvable power inputs.
  • Standard spatial and planar compute(...) calls now reject radii= rather than silently ignoring it; radius-based power computation remains unchanged.
  • Weight/radius transforms now reject non-finite r_min values and finite inputs whose squaring or representation-shift arithmetic overflows, so successful conversions and inverse-fit results cannot contain NaN or infinite radii, weights, or shifts through this path.

Deprecated

  • pyvoro2.powerfit, broad top-level separator exports, five historical separator core aliases, PlanarComputeResult, and planar return_result= remain tested compatibility-only routes for v0.7 and are removed in v0.8. The explicit output='cells' raw route remains supported.

[0.6.3] - 2026-03-19

Added

  • Public power-fit problem export via pyvoro2.powerfit.build_power_fit_problem(...), including stable PowerFitProblem, PowerFitBounds, and PowerFitPredictions objects for external evaluation or solver experiments.
  • Public result packaging via pyvoro2.powerfit.build_power_fit_result(...), so externally computed candidate weights can be turned back into a normal PowerWeightFitResult with standard residuals, reports, and algebraic diagnostics.
  • PowerFitObjectiveBreakdown and PowerWeightFitResult.objective_breakdown, including explicit hard-bound satisfaction and per-penalty summaries for debugging or research workflows.
  • A small advanced problem-API regression suite covering problem export, read-only arrays, result repackaging, and status-detail reporting.

Changed

  • Shared public power-fit dataclasses now live in pyvoro2.powerfit.types, while pure public weight/radius conversions live in pyvoro2.powerfit.transforms.
  • Mathematical formulas for prediction, algebraic diagnostics, hard-bound conversion, gauge canonicalization, and objective evaluation are now centralized in pyvoro2.powerfit.problem instead of being split across solver/report helpers.
  • The standalone native solver, active-set layer, and report layer now consume that shared problem/evaluation machinery instead of maintaining their own private copies of the same formulas.
  • PairBisectorConstraints and the new problem-definition objects now expose read-only NumPy arrays to match their frozen-dataclass semantics more honestly.
  • PowerWeightFitResult.status is now open-ended and pairs with status_detail so externally packaged results can preserve solver-specific termination text without expanding the native status vocabulary.

Fixed

  • Internal formula drift risks between low-level fitting, active-set post-processing, and report serialization were removed by routing them all through one public problem/evaluation layer.

[0.6.2] - 2026-03-19

Added

  • PowerWeightFitResult.edge_diagnostics with theorem-facing edge-space arrays and summaries: alpha, beta, z_obs, z_fit, algebraic residuals, and weighted/unweighted algebraic inconsistency metrics.
  • Fit reports and per-constraint record exporters now serialize the new algebraic edge diagnostics alongside the existing measurement-space predictions and residuals.
  • A medium-size robust-fit regression test that checks the native Huber/ADMM path on a deterministic sparse-outlier benchmark rather than only on tiny two-point cases.

Changed

  • The iterative solver='admm' backend now splits on the predicted measurement variable itself (y = beta + alpha (w_i - w_j)) instead of on raw weight differences, so the linear solve uses the scientifically natural alpha^2 scaling of the graph Laplacian.
  • The ADMM backend now warm-starts from the quadratic analytic fit when that warm start is identifiable, improving convergence on robust Huber fits without expanding the public optimizer API.

Fixed

  • Native Huber fitting via FitModel(mismatch=HuberLoss(...)) and solver='admm' no longer stalls or blows up on the medium-size sparse-outlier benchmark family used during the JCAM paper work; the corrected native path now matches the expected measurement-space residual quality and converges in a small number of iterations on the representative failure case.

[0.6.1] - 2026-03-16

Added

  • Explicit realized-but-unaccounted pair diagnostics in both 3D and planar 2D power-fit realization, including public UnaccountedRealizedPair / UnaccountedRealizedPairError types and JSON/report export support.
  • Structured connectivity diagnostics for low-level fits and self-consistent active-set solves, covering unconstrained points, isolated points, connected components of candidate and active graphs, and whether relative offsets are identified by the data or only by gauge policy.
  • Active-set path diagnostics via result.path_summary and richer per-iteration history rows, so downstream code can distinguish final disconnectedness from transient component splits or transient candidate-absent realized pairs during optimization.
  • Repo-root notebook sources plus notebook-export / notebook-check tooling, distribution-content checks, and a one-shot tools/release_check.py helper for local publishability validation.

Changed

  • Disconnected standalone fits no longer inherit arbitrary anchor-order gauges: each effective component is centered to mean zero by default, or aligned to the regularization-reference mean when a zero-strength reference is supplied.
  • Self-consistent active-set fitting now preserves offsets per connected component of the current active effective graph by aligning each component to the previous iterate, including the final recomputed fit returned to the user.
  • weights_to_radii(...) and the fitting APIs now support an explicit weight_shift= gauge, while keeping r_min= as a backward-compatible convenience rather than the primary convention.
  • Power-fit reports now serialize connectivity diagnostics, unaccounted realized pairs, realized-diagnostics warnings, and active-set path summaries through the plain-Python report helpers.
  • The example notebooks now live in a repo-root notebooks/ directory and are exported into generated docs pages, while README.md and docs deployment are checked for sync in CI.
  • The package metadata now includes a convenience pyvoro2[all] extra for contributors who want the full optional notebook/docs/release-check stack.
  • The optional planar plot_tessellation(...) helper now accepts domain= and show_sites= to match the published guide examples.

Fixed

  • Active-set reports now nest the final low-level fit against the final active constraint subset rather than the full candidate table.
  • Periodic self-image boundaries are excluded from the new unaccounted-pair diagnostics, so wrong-shift reporting does not misclassify self-adjacencies as missing candidate pairs.

[0.6.0] - 2026-03-16

Added

  • New pyvoro2.planar namespace with the first 2D public surface: Box, RectangularCell, compute, locate, ghost_cells, duplicate checking, edge-property annotation, and optional matplotlib visualization helpers.
  • Vendored legacy Voro++ 2D backend is now wired into the build as a separate _core2d extension target.
  • New planar edge-shift reconstruction helper and pre-wheel integration tests that skip cleanly until _core2d wheels are available.
  • New planar tessellation diagnostics and strict validation helpers: analyze_tessellation(...) and validate_tessellation(...).
  • New planar normalization helpers: normalize_vertices(...), normalize_topology(...), and validate_normalized_topology(...).
  • New pyvoro2.planar.PlanarComputeResult for structured wrapper-level compute results carrying raw cells, optional tessellation diagnostics, and optional normalized outputs.
  • pyvoro2.powerfit realized-boundary matching and self-consistent active-set refinement now support planar 2D domains in addition to the original 3D path.

Changed

  • pyvoro2.planar.compute(...) now supports wrapper-level tessellation diagnostics (return_diagnostics=..., tessellation_check=...) and structured normalization convenience (normalize='vertices'|'topology', return_result=True), automatically computing temporary periodic edge shifts/geometry when needed and stripping the temporary fields back out of the raw returned cells unless they were explicitly requested.
  • tools/install_wheel_overlay.py now understands both _core and _core2d, so the editable-style wheel-overlay workflow can carry planar support once new wheels are built.
  • Package metadata, release notes, and top-level documentation now describe the frozen 0.6.0 release rather than the earlier development snapshot.
  • resolve_pair_bisector_constraints(...) now accepts both planar (2D) and spatial (3D) point sets, with dimension-aware shift validation and nearest-image resolution.
  • Power-fit reports now serialize both 2D and 3D tessellation diagnostics through a shared measure-oriented schema while preserving the existing area/volume-specific fields.

Fixed

  • Periodic 2D edge reconstruction now resolves hidden periodic adjacencies that the legacy backend can surface as negative neighbor ids, so fully periodic planar tessellations expose consistent neighbor/shift data to diagnostics and normalization utilities.

[0.5.1] - 2026-03-15

Added

  • tools/install_wheel_overlay.py to support a wheel-core + repository-source development workflow, so the compiled extension can come from an installed wheel while Python imports resolve to src/pyvoro2.
  • DEV_PLAN.md in the repository root with the planned 0.6.x refactoring and 2D implementation roadmap, including the current decision to ship planar 2D against the existing dedicated 2D backend before considering a later voro-dev migration.

Changed

  • Public API validation and block-grid resolution are now routed through shared internal helpers (_inputs.py, _domain_geometry.py) so 3D wrappers and the power-fit layer no longer duplicate the same coercion and geometry logic.
  • Project status metadata is now consistently marked as beta across the package metadata and top-level documentation.

Fixed

  • Power-fit input validation now rejects non-finite point coordinates, constraint values, confidence weights, and non-finite radius/weight conversion inputs.
  • resolve_pair_bisector_constraints(...) now validates external ids consistently, including shape/length and uniqueness checks.
  • The quadratic/analytic power-fit solver no longer crashes on zero-confidence constraints that would otherwise create singular gauge coupling.
  • Empty resolved constraint sets now respect L2 regularization and return the regularization-only solution instead of silently dropping the reference.
  • fit_power_weights(...) and the active-set driver now return the documented numerical_failure status for linear-algebra and non-finite-iterate failures instead of surfacing them as uncaught exceptions or misclassified active-set infeasibility.
  • Triclinic nearest-image resolution now warns when a chosen image touches the image_search boundary, making the search-window sensitivity explicit for skewed periodic cells.

[0.5.0] - 2026-03-14

Added

  • New pyvoro2.powerfit API for inverse power fitting from generic pairwise bisector constraints.
  • Power-fitting results now export plain-Python record rows for downstream reporting and diagnostics.
  • Hard infeasibility reporting is simplified around explicit contradiction witnesses.
  • resolve_pair_bisector_constraints(...) as a reusable low-level constraint-resolution primitive.
  • fit_power_weights(...) with configurable mismatch, hard feasibility, soft penalties, and explicit infeasibility reporting.
  • match_realized_pairs(...) for purely geometric realized-face matching with optional tessellation diagnostics.
  • solve_self_consistent_power_weights(...) for hysteretic active-set refinement driven by realized faces.
  • Rich per-constraint diagnostics, marginal-pair reporting, and optional final tessellation diagnostics.

Changed

  • The inverse-fitting surface is now math-oriented and chemistry-agnostic.
  • Documentation and examples now describe the unified power-fitting workflow.
  • The 0.5.x objective-model scope is explicitly documented around the current built-in convex model family.

[0.4.2] - 2026-03-04

Changed

  • Vendored Voro++: updated the vendored snapshot to include the upstream numeric robustness fix for power/Laguerre (radical) pruning (fixes rare cross-platform edge cases in fully periodic power tessellations).
  • Removed the previously vendored local nextafter-based max_radius inflation patch (no longer needed).

[0.4.1] - 2026-02-16

Fixed

  • Vendored Voro++: inflate the stored global max_radius by 1 ULP (via nextafter) in power/Laguerre mode to make radical pruning robust across platforms.
  • Removed a Python-side workaround that recomputed fully periodic orthorhombic power tessellations via the periodic (triclinic) backend when periodic face-shift assignment failed.
  • Updated documentation to reflect the patched vendored Voro++ snapshot.

[0.4.0] - 2026-02-15

Initial public release.

pyvoro2 wraps unmodified Voro++ and provides a Python-first interface for 3D Voronoi and power/Laguerre (radical Voronoi) tessellations, with practical utilities for periodic boundary conditions and topology/graph workflows.

Added

  • Tessellation computation:
    • compute(..., mode='standard') for standard Voronoi tessellations.
    • compute(..., mode='power') for power/Laguerre tessellations (per-site radii).
  • Domains (containers):
    • Box: finite axis-aligned bounding box.
    • OrthorhombicCell: axis-aligned cell with per-axis periodicity (useful for 1D/2D periodic systems such as wires and slabs).
    • PeriodicCell: fully periodic triclinic cell, including PeriodicCell.from_params(...) for Voro++ lower-triangular parameters.
  • Periodic neighbor-image support:
    • return_face_shifts=True to annotate faces with adjacent_shift=(na, nb, nc) lattice-image indices (for PeriodicCell and periodic OrthorhombicCell).
  • Point-query operations:
    • locate(...): batched ownership queries via Voro++ find_voronoi_cell.
    • ghost_cells(...): batched probe cells via Voro++ compute_ghost_cell.
  • Pre-processing utilities:
    • duplicate_check(...) near-duplicate point detection (Python-side).
    • compute(...), locate(...), and ghost_cells(...) can optionally run the near-duplicate pre-check via duplicate_check='raise'|'warn'.
    • PeriodicCell performs validation of lattice vectors, including near-degeneracy detection (ill-conditioned bases warn; nearly degenerate cells raise).
    • Additional input validation: non-finite coordinates are rejected; user ids must be unique and non-negative; power-mode radii are required to be finite and non-negative.
  • Post-processing utilities:
    • analyze_tessellation(...) sanity checks.
    • validate_tessellation(...) strict validation (raises on failure).
    • annotate_face_properties(...) helpers.
    • normalize.* helpers for reproducible periodic topology work.
    • validate_normalized_topology(...) strict validation for normalized topology.
  • Inverse fitting utilities (pyvoro2.inverse) to fit power weights/radii from desired separating plane positions.
  • Optional visualization helpers (pyvoro2.viz3d, extra pyvoro2[viz]) based on py3Dmol.
  • Documentation site (MkDocs Material) with a narrative guide, API reference, and example notebooks.
  • Test suite with deterministic unit tests, opt-in fuzz/property tests, and opt-in cross-checks against pyvoro.