#! /usr/env/python
"""Python implementation of VoronoiDelaunayGrid, a class used to create and
manage unstructured, irregular grids for 2D numerical models.
"""
import pathlib
import numpy as np
from ..graph import DualVoronoiGraph
from .base import ModelGrid
[docs]
def simple_poly_area(x, y):
"""Calculates and returns the area of a 2-D simple polygon.
Input vertices must be in sequence (clockwise or counterclockwise). *x*
and *y* are arrays that give the x- and y-axis coordinates of the
polygon's vertices.
Parameters
----------
x : ndarray
x-coordinates of of polygon vertices.
y : ndarray
y-coordinates of of polygon vertices.
Returns
-------
out : float
Area of the polygon
Examples
--------
>>> import numpy as np
>>> from landlab.grid.voronoi import simple_poly_area
>>> x = np.array([3.0, 1.0, 1.0, 3.0])
>>> y = np.array([1.5, 1.5, 0.5, 0.5])
>>> simple_poly_area(x, y)
2.0
If the input coordinate arrays are 2D, calculate the area of each polygon.
Note that when used in this mode, all polygons must have the same
number of vertices, and polygon vertices are listed column-by-column.
>>> x = np.array([[3.0, 1.0, 1.0, 3.0], [-2.0, -2.0, -1.0, -1.0]]).T
>>> y = np.array([[1.5, 1.5, 0.5, 0.5], [0.0, 1.0, 2.0, 0.0]]).T
>>> simple_poly_area(x, y)
array([2. , 1.5])
"""
# For short arrays (less than about 100 elements) it seems that the
# Python sum is faster than the numpy sum. Likewise for the Python
# built-in abs.
return 0.5 * abs(sum(x[:-1] * y[1:] - x[1:] * y[:-1]) + x[-1] * y[0] - x[0] * y[-1])
[docs]
class VoronoiDelaunayGrid(DualVoronoiGraph, ModelGrid):
"""This inherited class implements an unstructured grid in which cells are
Voronoi polygons and nodes are connected by a Delaunay triangulation. Uses
scipy.spatial module to build the triangulation.
Create an unstructured grid from points whose coordinates are given
by the arrays *x*, *y*.
Returns
-------
VoronoiDelaunayGrid
A newly-created grid.
Examples
--------
>>> import numpy as np
>>> from numpy.random import rand
>>> from landlab.grid import VoronoiDelaunayGrid
>>> x, y = rand(25), rand(25)
>>> vmg = VoronoiDelaunayGrid(x, y) # node_x_coords, node_y_coords
>>> vmg.number_of_nodes
25
>>> x = np.array(
... [
... [0, 0.1, 0.2, 0.3],
... [1, 1.1, 1.2, 1.3],
... [2, 2.1, 2.2, 2.3],
... ]
... ).flatten()
>>> y = np.array(
... [
... [0.0, 1.0, 2.0, 3.0],
... [0.0, 1.0, 2.0, 3.0],
... [0.0, 1.0, 2.0, 3.0],
... ]
... ).flatten()
>>> vmg = VoronoiDelaunayGrid(x, y)
>>> vmg.node_x
array([0. , 1. , 2. ,
0.1, 1.1, 2.1,
0.2, 1.2, 2.2,
0.3, 1.3, 2.3])
>>> vmg.node_y
array([0., 0., 0.,
1., 1., 1.,
2., 2., 2.,
3., 3., 3.])
>>> vmg.adjacent_nodes_at_node
array([[ 1, 3, -1, -1, -1, -1],
[ 2, 4, 3, 0, -1, -1],
[ 5, 4, 1, -1, -1, -1],
[ 4, 6, 0, 1, -1, -1],
[ 5, 7, 6, 3, 1, 2],
[ 8, 7, 4, 2, -1, -1],
[ 7, 9, 3, 4, -1, -1],
[ 8, 10, 9, 6, 4, 5],
[11, 10, 7, 5, -1, -1],
[10, 6, 7, -1, -1, -1],
[11, 9, 7, 8, -1, -1],
[10, 8, -1, -1, -1, -1]])
"""
[docs]
def __init__(
self,
x=None,
y=None,
reorient_links=True,
xy_of_reference=(0.0, 0.0),
xy_axis_name=("x", "y"),
xy_axis_units="-",
):
"""Create a Voronoi Delaunay grid from a set of points.
Create an unstructured grid from points whose coordinates are given
by the arrays *x*, *y*.
Parameters
----------
x : array_like
x-coordinate of points
y : array_like
y-coordinate of points
reorient_links (optional) : bool
whether to point all links to the upper-right quadrant
xy_of_reference : tuple, optional
Coordinate value in projected space of (0., 0.)
Default is (0., 0.)
Returns
-------
VoronoiDelaunayGrid
A newly-created grid.
Examples
--------
>>> from numpy.random import rand
>>> from landlab.grid import VoronoiDelaunayGrid
>>> x, y = rand(25), rand(25)
>>> vmg = VoronoiDelaunayGrid(x, y) # node_x_coords, node_y_coords
>>> vmg.number_of_nodes
25
"""
DualVoronoiGraph.__init__(self, (y, x), sort=True)
ModelGrid.__init__(
self,
xy_axis_name=xy_axis_name,
xy_axis_units=xy_axis_units,
xy_of_reference=xy_of_reference,
)
self._node_status = np.full(
self.number_of_nodes, self.BC_NODE_IS_CORE, dtype=np.uint8
)
self._node_status[self.perimeter_nodes] = self.BC_NODE_IS_FIXED_VALUE
# DualVoronoiGraph.__init__(self, (y, x), **kwds)
# ModelGrid.__init__(self, **kwds)
[docs]
@classmethod
def from_dict(cls, kwds):
args = (kwds.pop("x"), kwds.pop("y"))
return cls(*args, **kwds)
[docs]
def save(self, path, clobber=False):
"""Save a grid and fields.
This method uses pickle to save a Voronoi grid as a pickle file.
At the time of coding, this is the only convenient output format
for Voronoi grids, but support for netCDF is likely coming.
All fields will be saved, along with the grid.
The recommended suffix for the save file is '.grid'. This will
be added to your save if you don't include it.
This method is equivalent to
:py:func:`~landlab.io.native_landlab.save_grid`, and
:py:func:`~landlab.io.native_landlab.load_grid` can be used to
load these files.
Caution: Pickling can be slow, and can produce very large files.
Caution 2: Future updates to Landlab could potentially render old
saves unloadable.
Parameters
----------
path : str
Path to output file.
clobber : bool (defaults to false)
Set to true to allow overwriting
Returns
-------
str
The name of the saved file (with the ".grid" extension).
Examples
--------
>>> from landlab import VoronoiDelaunayGrid
>>> import numpy as np
>>> grid = VoronoiDelaunayGrid(np.random.rand(20), np.random.rand(20))
>>> grid.save("mytestsave.grid") # doctest: +SKIP
'mytestsave.grid'
:meta landlab: info-grid
"""
import pickle
path = pathlib.Path(path)
if path.suffix != ".grid":
path = path.with_suffix(path.suffix + ".grid")
if path.exists() and not clobber:
raise ValueError(
f"File exists: {str(path)!r}. "
"Either remove this file and try again or set the "
"'clobber' keyword to True"
)
with open(path, "wb") as fp:
pickle.dump(self, fp)
return str(path)