Source code for landlab.graph.structured_quad.structured_quad

from abc import ABC
from abc import abstractmethod
from functools import cached_property

import numpy as np

from ...grid.linkorientation import LinkOrientation
from ...utils.decorators import read_only_array
from ..graph import Graph


[docs] class StructuredQuadLayout(ABC):
[docs] @staticmethod def corner_nodes(shape): n_rows, n_cols = shape return (n_rows * n_cols - 1, (n_rows - 1) * n_cols, 0, n_cols - 1)
[docs] @staticmethod @abstractmethod def perimeter_nodes(shape): ...
[docs] @staticmethod @abstractmethod def patches_at_node(shape): ...
[docs] class StructuredQuadLayoutCython(StructuredQuadLayout):
[docs] @staticmethod def perimeter_nodes(shape): from .ext.at_node import fill_perimeter_nodes n_perimeter_nodes = 2 * shape[0] + 2 * (shape[1] - 2) perimeter_nodes = np.empty(n_perimeter_nodes, dtype=int) fill_perimeter_nodes(shape, perimeter_nodes) return perimeter_nodes
[docs] @staticmethod def patches_at_node(shape): from .ext.at_node import fill_patches_at_node n_nodes = shape[0] * shape[1] patches_at_node = np.empty((n_nodes, 4), dtype=int) fill_patches_at_node(shape, patches_at_node) return patches_at_node
[docs] class StructuredQuadLayoutPython(StructuredQuadLayout):
[docs] @staticmethod def perimeter_nodes(shape): n_rows, n_cols = shape ( northeast, northwest, southwest, southeast, ) = StructuredQuadLayout.corner_nodes(shape) return np.concatenate( ( np.arange(southeast, northeast, n_cols), np.arange(northeast, northwest, -1), np.arange(northwest, southwest, -n_cols), np.arange(southwest, southeast, 1), ) )
[docs] @staticmethod def patches_at_node(shape): n_rows, n_cols = shape patches_at_node = np.empty((4, n_rows * n_cols), dtype=int) ne = (slice(n_rows - 1), slice(n_cols - 1)) nw = (slice(n_rows - 1), slice(1, n_cols)) sw = (slice(1, n_rows), slice(1, n_cols)) se = (slice(1, n_rows), slice(n_cols - 1)) patches = np.arange((n_rows - 1) * (n_cols - 1), dtype=int).reshape( (n_rows - 1, n_cols - 1) ) for col, nodes in enumerate((ne, nw, sw, se)): patches_at_node[col].reshape((n_rows, n_cols))[nodes] = patches ( northeast, northwest, southwest, southeast, ) = StructuredQuadLayout.corner_nodes(shape) bottom = slice(southwest, southeast + 1) top = slice(northwest, northeast + 1) left = slice(southwest, northwest + n_cols, n_cols) right = slice(southeast, northeast + n_cols, n_cols) patches_at_node[0, right] = -1 patches_at_node[0, top] = -1 patches_at_node[1, left] = -1 patches_at_node[1, top] = -1 patches_at_node[2, left] = -1 patches_at_node[2, bottom] = -1 patches_at_node[3, right] = -1 patches_at_node[3, bottom] = -1 return patches_at_node.T
[docs] class StructuredQuadGraphTopology: _layout = StructuredQuadLayoutCython
[docs] def __init__(self, shape): self._shape = tuple(shape)
@property def shape(self): return self._shape @property def number_of_node_rows(self): return self._shape[0] @property def number_of_node_columns(self): return self._shape[1]
[docs] @cached_property @read_only_array def nodes(self): """A shaped array of node ids. Returns ------- ndarray Node IDs in an array shaped as *number_of_node_rows* by *number_of_node_columns*. """ return np.arange(self.shape[0] * self.shape[1]).reshape(self.shape)
[docs] @cached_property @read_only_array def nodes_at_right_edge(self): return np.arange(self.shape[1] - 1, np.prod(self.shape), self.shape[1])
[docs] @cached_property @read_only_array def nodes_at_top_edge(self): return np.arange(self.number_of_nodes - self.shape[1], np.prod(self.shape))
[docs] @cached_property @read_only_array def nodes_at_left_edge(self): return np.arange(0, np.prod(self.shape), self.shape[1])
[docs] @cached_property @read_only_array def nodes_at_bottom_edge(self): return np.arange(self.shape[1])
[docs] def nodes_at_edge(self, edge): if edge not in ("right", "top", "left", "bottom"): raise ValueError("value for edge not understood") return getattr(self, f"nodes_at_{edge}_edge")
@property def nodes_at_corners_of_grid(self): """Nodes at corners of grid. The nodes at at the corners of the grid. The nodes are returned counterclockwise starting with the upper-right. Return ------ tuple of int Nodes at the four corners. Examples -------- >>> from landlab.graph import UniformRectilinearGraph >>> graph = UniformRectilinearGraph((4, 5)) >>> graph.nodes_at_corners_of_grid (19, 15, 0, 4) """ return ( self.number_of_nodes - 1, self.number_of_nodes - self.number_of_node_columns, 0, self.number_of_node_columns - 1, ) @property def corner_nodes(self): n_rows, n_cols = self.shape return np.asarray( (n_rows * n_cols - 1, (n_rows - 1) * n_cols, 0, n_cols - 1), dtype=int )
[docs] @cached_property def perimeter_nodes(self): return self._layout.perimeter_nodes(self.shape)
[docs] @cached_property @read_only_array def patches_at_node(self): return self._layout.patches_at_node(self.shape)
[docs] class StructuredQuadGraphExtras(StructuredQuadGraphTopology, Graph):
[docs] def __init__(self, node_y_and_x, sort=False): StructuredQuadGraphTopology.__init__(self, node_y_and_x[0].shape) Graph.__init__( self, node_y_and_x, links=StructuredQuadLayoutCython.nodes_at_link(self.shape), patches=StructuredQuadLayoutCython.links_at_patch(self.shape), sort=sort, )
@property def nodes_at_link(self): return self.ds["nodes_at_link"].values
[docs] class StructuredQuadGraph(StructuredQuadGraphExtras):
[docs] def __init__(self, coords, shape=None, sort=False): node_y, node_x = ( np.asarray(coords[0], dtype=float), np.asarray(coords[1], dtype=float), ) if shape: node_y.shape = shape node_x.shape = shape else: node_x.shape = node_y.shape if node_y.shape != node_x.shape: raise ValueError("shape mismatch in node x and y coordinates") StructuredQuadGraphExtras.__init__(self, (node_y, node_x), sort=sort)
[docs] @staticmethod def setup_node_y_and_x(yx_at_node, shape=None): node_y = np.asarray(yx_at_node[0], dtype=float) node_x = np.asarray(yx_at_node[1], dtype=float) if shape: node_y.shape = shape node_x.shape = shape else: node_x.shape = node_y.shape if node_y.shape != node_x.shape: raise ValueError("shape mismatch in node x and y coordinates") return (node_y, node_x)
[docs] class RectilinearGraph(StructuredQuadGraphExtras): """Graph of a rectlinear grid of nodes. Examples -------- >>> from landlab.graph import RectilinearGraph >>> graph = RectilinearGraph(([0, 1, 2, 3], [1, 4, 8])) >>> graph.number_of_nodes 12 >>> graph.y_of_node.reshape(graph.shape) array([[0., 0., 0.], [1., 1., 1.], [2., 2., 2.], [3., 3., 3.]]) >>> graph.x_of_node.reshape(graph.shape) array([[1., 4., 8.], [1., 4., 8.], [1., 4., 8.], [1., 4., 8.]]) """
[docs] def __init__(self, nodes, sort=False): rows = np.asarray(nodes[0], dtype=float) cols = np.asarray(nodes[1], dtype=float) node_y_and_x = np.meshgrid(rows, cols, indexing="ij") StructuredQuadGraphExtras.__init__(self, node_y_and_x, sort=sort)
[docs] @staticmethod def setup_node_y_and_x(coords): rows = np.asarray(coords[0], dtype=float) cols = np.asarray(coords[1], dtype=float) return np.meshgrid(rows, cols, indexing="ij")
[docs] class UniformRectilinearGraph(StructuredQuadGraphExtras): """Graph of a structured grid of quadrilaterals. Examples -------- >>> from landlab.graph import UniformRectilinearGraph >>> graph = UniformRectilinearGraph((4, 3), spacing=(1, 2), origin=(-1, 0)) >>> graph.number_of_nodes 12 >>> graph.y_of_node.reshape(graph.shape) array([[-1., -1., -1.], [ 0., 0., 0.], [ 1., 1., 1.], [ 2., 2., 2.]]) >>> graph.x_of_node.reshape(graph.shape) array([[0., 2., 4.], [0., 2., 4.], [0., 2., 4.], [0., 2., 4.]]) >>> graph.links_at_node array([[ 0, 2, -1, -1], [ 1, 3, 0, -1], [-1, 4, 1, -1], [ 5, 7, -1, 2], [ 6, 8, 5, 3], [-1, 9, 6, 4], [10, 12, -1, 7], [11, 13, 10, 8], [-1, 14, 11, 9], [15, -1, -1, 12], [16, -1, 15, 13], [-1, -1, 16, 14]]) >>> graph.link_dirs_at_node array([[-1, -1, 0, 0], [-1, -1, 1, 0], [ 0, -1, 1, 0], [-1, -1, 0, 1], [-1, -1, 1, 1], [ 0, -1, 1, 1], [-1, -1, 0, 1], [-1, -1, 1, 1], [ 0, -1, 1, 1], [-1, 0, 0, 1], [-1, 0, 1, 1], [ 0, 0, 1, 1]], dtype=int8) >>> graph.nodes_at_link array([[ 0, 1], [ 1, 2], [ 0, 3], [ 1, 4], [ 2, 5], [ 3, 4], [ 4, 5], [ 3, 6], [ 4, 7], [ 5, 8], [ 6, 7], [ 7, 8], [ 6, 9], [ 7, 10], [ 8, 11], [ 9, 10], [10, 11]]) >>> graph.links_at_patch array([[ 3, 5, 2, 0], [ 4, 6, 3, 1], [ 8, 10, 7, 5], [ 9, 11, 8, 6], [13, 15, 12, 10], [14, 16, 13, 11]]) >>> graph.nodes_at_patch array([[ 4, 3, 0, 1], [ 5, 4, 1, 2], [ 7, 6, 3, 4], [ 8, 7, 4, 5], [10, 9, 6, 7], [11, 10, 7, 8]]) """
[docs] def __init__(self, shape, spacing=1.0, origin=0.0, sort=False): spacing = np.broadcast_to(spacing, 2) origin = np.broadcast_to(origin, 2) rows = np.arange(shape[0], dtype=float) * spacing[0] + origin[0] cols = np.arange(shape[1], dtype=float) * spacing[1] + origin[1] node_y_and_x = np.meshgrid(rows, cols, indexing="ij") StructuredQuadGraphExtras.__init__(self, node_y_and_x, sort=sort) self._spacing = tuple(spacing) self._origin = tuple(origin)
@property def spacing(self): return self._spacing @property def origin(self): return self._origin @property def dx(self): return self._spacing[1] @property def dy(self): return self._spacing[0]