Metadata-Version: 2.4
Name: pyvoro-rimoli
Version: 1.4.0
Summary: 2D and 3D Voronoi tessellations: a Python entry point for the Voro++ library, built with the current Voro++.
Author-email: Joe Jordan <joe.jordan@imperial.ac.uk>
Maintainer: Julian Rimoli
License-Expression: BSD-3-Clause-LBNL
Project-URL: Homepage, https://github.com/rimoli/pyvoro
Project-URL: Changelog, https://github.com/rimoli/pyvoro/blob/master/CHANGELOG.md
Project-URL: Original pyvoro, https://github.com/joe-jordan/pyvoro
Project-URL: Voro++, https://github.com/chr1shr/voro
Keywords: geometry,mathematics,Voronoi,Laguerre,radical tessellation
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Science/Research
Classifier: Operating System :: MacOS
Classifier: Operating System :: Microsoft :: Windows
Classifier: Operating System :: POSIX :: Linux
Classifier: Programming Language :: C++
Classifier: Programming Language :: Cython
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Scientific/Engineering :: Mathematics
Classifier: Topic :: Scientific/Engineering :: Physics
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Dynamic: license-file

pyvoro-rimoli
=============

2D and 3D Voronoi and radical (Laguerre) tessellations in Python, with the
current [Voro++](https://github.com/chr1shr/voro).

This is a maintained fork of [pyvoro](https://github.com/joe-jordan/pyvoro),
by way of [pyvoro-mmalahe](https://github.com/mmalahe/pyvoro). The Python
interface is unchanged: the package is still imported as `pyvoro`, and code
written for pyvoro works as before. What changes is the bundled Voro++, which
was version 0.4.6 (2013) and is now the current upstream version.

Why this fork
-------------

Voro++ 0.4.6 has a bug in the radical tessellation: in some configurations a
cell is returned uncut, and the cells overlap instead of partitioning the
domain. Four discs of radius 0.1 at the quarter points of a unit square, for
example, must give four cells of area 0.25:

```python
import pyvoro
cells = pyvoro.compute_2d_voronoi(
    [[0.25, 0.25], [0.75, 0.25], [0.25, 0.75], [0.75, 0.75]],
    [[0.0, 1.0], [0.0, 1.0]], 0.5, radii=[0.1] * 4)
print([c['volume'] for c in cells])
# Voro++ 0.4.6:  [1.0, 1.0, 1.0, 1.0]
# this fork:     [0.25, 0.25, 0.25, 0.25]
```

The bug was fixed upstream in February 2026 (Voro++ commits 04fecfa and
b0dac57), and this fork bundles Voro++ at commit b0dac57. The fork was made
for [MicroStructPy](https://github.com/kip-hart/MicroStructPy), whose
microstructures are radical tessellations, but it can replace pyvoro in any
project.

Installation
------------

    pip install pyvoro-rimoli

Wheels are provided for Linux (x86_64 and aarch64), macOS (Intel and Apple
silicon) and Windows (x86_64), for Python 3.9 to 3.14. On other platforms,
pip builds the package from source, which needs a C++ compiler.

pyvoro, pyvoro-mmalahe and pyvoro-rimoli all install the same `pyvoro`
module, so only one of them should be installed. To switch:

    pip uninstall pyvoro pyvoro-mmalahe
    pip install pyvoro-rimoli

To install from a clone of this repository and run the tests:

    pip install . pytest
    pytest tests

Usage
-----

```python
import pyvoro
pyvoro.compute_voronoi( ... )
pyvoro.compute_2d_voronoi( ... )
```

Features:

* *Radical (weighted) option*, which weights the Voronoi cell sizes according
  to a set of supplied radius values.
* *Periodic boundary support*. Note that each cell is returned in the frame of
  reference of its source point, so points can (and will) be outside the
  bounding box.
* *2D helper*, which translates the results of a 3D tessellation of points on
  the plane back into 2D vectors and cells (see below for an example).
* *Support for numpy arrays* (thanks to a contribution from
  @christopherpoole): you can pass in a 2D (Nx3 or Nx2) numpy array.

### Example

```python
import pyvoro
pyvoro.compute_voronoi(
  [[1.0, 2.0, 3.0], [4.0, 5.5, 6.0]], # point positions
  [[0.0, 10.0], [0.0, 10.0], [0.0, 10.0]], # limits
  2.0, # block size
  radii=[1.3, 1.4] # particle radii -- optional, and keyword-compatible arg.
)
```

returning an array of voronoi cells in the form:

```python
{ # (note, this cell is not calculated using the above example)
  'volume': 6.07031902214448,
  'faces': [
    {'adjacent_cell': 1, 'vertices': [1, 5, 8, 3]},
    {'adjacent_cell': -3, 'vertices': [1, 0, 2, 6, 5]},
    {'adjacent_cell': -5, 'vertices': [1, 3, 9, 7, 0]},
    {'adjacent_cell': 146, 'vertices': [2, 4, 11, 10, 6]},
    {'adjacent_cell': -1, 'vertices': [2, 0, 7, 4]},
    {'adjacent_cell': 9, 'vertices': [3, 8, 10, 11, 9]},
    {'adjacent_cell': 11, 'vertices': [4, 7, 9, 11]},
    {'adjacent_cell': 139, 'vertices': [5, 6, 10, 8]}
  ],
  'adjacency': [
    [1, 2, 7],
    [5, 0, 3],
    [4, 0, 6],
    [8, 1, 9],
    [11, 7, 2],
    [6, 1, 8],
    [2, 5, 10],
    [9, 0, 4],
    [5, 3, 10],
    [11, 3, 7],
    [6, 8, 11],
    [10, 9, 4]
  ],
  'original': [1.58347382116, 0.830481034382, 0.84264445125],
  'vertices': [
    [0.0, 0.0, 0.0],
    [2.6952010660213537, 0.0, 0.0],
    [0.0, 0.0, 1.3157105644765856],
    [2.6796085747800173, 0.9893738662896467, 0.0],
    [0.0, 1.1577688788929044, 0.9667194826924593],
    [2.685575135451888, 0.0, 1.2139446383811037],
    [1.5434724537773115, 0.0, 2.064891808748473],
    [0.0, 1.2236852383897006, 0.0],
    [2.6700186049990116, 1.0246853171897545, 1.1392273839598812],
    [1.6298653128290692, 1.8592211309121414, 0.0],
    [1.8470793965350985, 1.7199178301499591, 1.6938166537039874],
    [1.7528279426840703, 1.7963648490662445, 1.625024494263244]
  ]
}
```

Note that this particle was the closest to the coord system origin - hence
(unimportantly) lots of vertex positions that are zero or roughly zero, and
(importantly) **negative cell ids** which correspond to the boundaries (of which
there are three at the corner of a box, specifically ids `1`, `3` and `5`, (the
`x_i = 0` boundaries, represented with negative ids hence `-1`, `-3` and `-5` --
this is voro++'s conventional way of referring to boundary interfaces.)

### 2D tessellation

You can run a simpler function to get the 2D cells around your points, with
all the details handled for you:

```python
import pyvoro
cells = pyvoro.compute_2d_voronoi(
  [[5.0, 7.0], [1.7, 3.2], ...], # point positions, 2D vectors this time.
  [[0.0, 10.0], [0.0, 10.0]], # box size, again only 2D this time.
  2.0, # block size; same as before.
  radii=[1.2, 0.9, ...] # particle radii -- optional and keyword-compatible.
)
```

the output follows the same schema as the 3D for now, since this is not as annoying as having a
whole new schema to handle. The adjacency is now a bit redundant since the cell is a polygon and the
vertices are returned in the correct order. The cells look like a list of these:

```python
{ # note that again, this is computed with a different example
  'adjacency': [
    [5, 1],
    [0, 2],
    [1, 3],
    [2, 4],
    [3, 5],
    [4, 0]
  ],
  'faces': [
    { 'adjacent_cell': 23, 'vertices': [0, 5]},
    { 'adjacent_cell': -2, 'vertices': [0, 1]},
    { 'adjacent_cell': 39, 'vertices': [2, 1]},
    { 'adjacent_cell': 25, 'vertices': [2, 3]},
    { 'adjacent_cell': 12, 'vertices': [4, 3]},
    { 'adjacent_cell': 9, 'vertices': [5, 4]}
  ],
  'original': [8.168525781010283, 5.943711239620341],
  'vertices': [
    [10.0, 5.324580764844442],
    [10.0, 6.442713105218478],
    [9.088894888250326, 7.118847221681966],
    [6.740750220282158, 6.444386346261051],
    [6.675322891805883, 5.678806294642725],
    [7.77400067532073, 5.02320427474993]
  ],
  'volume': 5.102702932807149
}
```

*(note that the edges will now be indexed -1 to -4, and the 'volume' key is in fact the area.)*

Note on compilation: if a cython .pyx file is being compiled in C++ mode, all
cython-visible code must be compiled "as c++" - this will not be compatible
with any C functions declared `extern "C" { ... }`. In this library, the author
just used c++ functions for everything, in order to be able to utilise the c++
`std::vector<T>` classes to represent the (ridiculously non-specific) geometry
of a Voronoi cell.

Releasing
---------

1. Update the version in `pyproject.toml` and the list of changes in
   `CHANGELOG.md`, commit, and push. The Wheels workflow builds and tests the
   wheels on every push.
2. On GitHub, publish a release with the tag `vX.Y.Z`. The Wheels workflow
   builds and tests the wheels and the source distribution again, and uploads
   them to PyPI.

The upload uses PyPI's
[trusted publishing](https://docs.pypi.org/trusted-publishers/), so no token
is stored in the repository. It needs a one-time setup by the owner of the
PyPI account: in *Your account > Publishing*, add a pending publisher for a
GitHub repository with

* PyPI project name: `pyvoro-rimoli`
* owner: `rimoli`, repository name: `pyvoro`
* workflow name: `wheels.yml`
* environment name: `pypi`

The first release then creates the project on PyPI.

License
-------

pyvoro is an extension of Voro++ and is released under the same modified BSD
license (see `LICENSE`). Voro++ Copyright (c) 2008, The Regents of the
University of California, through Lawrence Berkeley National Laboratory.

Credits
-------

* Voro++: Chris H. Rycroft.
* pyvoro: Joe Jordan, with contributions from Andrey Sobolev, Christopher
  Poole and Wendell Smith.
* pyvoro-mmalahe (Python 3 packaging): Michael Malahe, with a contribution
  from Oromion.
* pyvoro-rimoli (current Voro++, wheels, tests): Julian Rimoli.
