Source code for subsurface.structs.unstructured_elements

"""These are classes that are point based and managed via Pandas DataFrames.

All data is tracked within internal DataFrames that we validate.

This is for holding general "mesh"-like data structures: point clouds,
linesets, triangulated surfaces, tetrahedralized volumes, octree grids, etc.

Regularly gridded dataset will NOT be managed by these classes but will use
``xarray`` under the hood.

"""

import numpy as np
from .base_structures import UnstructuredData, StructuredData


__all__ = ['PointSet', 'TriSurf', 'LineSet', 'TetraMesh']


[docs]class PointSet: """Class for pointset based data structures. This class uses UnstructuredData.vertex as cloud of points and the associated attributes. UnstructuredData.cells are not used. Args: data (UnstructuredData): Base object for unstructured data. """ def __init__(self, data: UnstructuredData): if data.cells.shape[1] > 1: raise AttributeError('data.cells must be of the format' 'NDArray[(Any, 0), IntX] or NDArray[(Any, 1), IntX]') self.data = data @property def points(self): """Fetch the points/vertices dataframe.""" return self.data.vertex @property def n_points(self): return self.data.vertex.shape[0] @property def point_data(self): """Fetch the scalar data associated with the vertices.""" return self.data.attributes @property def point_data_dict(self): """Fetch the point data as a dictionary of numpy arrays.""" return self.data.attributes_to_dict
[docs]class TriSurf: """PointSet with triangle cells. This dataset defines cell/element connectivity between points to create triangulated surface. Uses UnstructuredData.cells for the face connectivity. Args: mesh (UnstructuredData): Base object for unstructured data. data.cells represent the point indices for each triangle in the mesh. Each column corresponds to a triangle edge. texture (StructuredData): 2D StructuredData with data to be mapped on the mesh Keyword Args: texture_origin : tuple(float) Length 3 iterable of floats defining the XYZ coordinates of the BOTTOM LEFT CORNER of the plane texture_point_u : tuple(float) Length 3 iterable of floats defining the XYZ coordinates of the BOTTOM RIGHT CORNER of the plane texture_point_v : tuple(float) Length 3 iterable of floats defining the XYZ coordinates of the TOP LEFT CORNER of the plane """ def __init__(self, mesh: UnstructuredData, texture: StructuredData = None, **kwargs ): if mesh.cells.shape[1] != 3: raise AttributeError('data.cells must be of the format' 'NDArray[(Any, 3), IntX]') self.mesh: UnstructuredData = mesh self.texture: StructuredData = texture self.texture_origin = kwargs.get('texture_origin', None) self.texture_point_u = kwargs.get('texture_point_u', None) self.texture_point_v = kwargs.get('texture_point_v', None) @property def triangles(self): return self.mesh.cells @property def n_triangles(self): return self.mesh.cells.shape[0]
[docs]class LineSet: """PointSet with line cells. This dataset defines cell connectivity between points to create line segments. Args: data (UnstructuredData): Base object for unstructured data. data.cells represent the indices of the end points for each line segment in the mesh. Each column corresponds to a line segment. If not specified, the vertices are connected in order, equivalent to ``segments=[[0, 1], [1, 2], [2, 3], ...]`` radius (float): Thickness of the line set """ def __init__(self, data: UnstructuredData, radius: float = 1): self.data: UnstructuredData = data self.radius = radius if data.cells is None or data.cells.shape[1] < 2: self.generate_default_cells() elif data.cells.shape[1] != 2: raise AttributeError('data.cells must be of the format' 'NDArray[(Any, 2), IntX]') # TODO: these must all be integer dtypes!
[docs] def generate_default_cells(self): """ Method to generate cells based on the order of the vertex. This only works if the LineSet only represents one single line Returns: np.ndarray[(Any, 2), IntX] """ a = np.arange(0, self.data.n_points - 1, dtype=np.int_) b = np.arange(1, self.data.n_points, dtype=np.int_) return np.vstack([a, b]).T
@property def segments(self): return self.data.cells @property def n_segments(self): return self.segments.shape[0]
[docs]class TetraMesh: """PointSet with tetrahedron cells. This dataset defines cell connectivity between points to create tetrahedrons. This is volumetric. Args: data (UnstructuredData): Base object for unstructured data. data.cells represent the indices of the points for each tetrahedron in the mesh. Each column corresponds to a tetrahedron. Every tetrahedron is defined by the four points; where the first three (0,1,2) are the base of the tetrahedron which, using the right hand rule, forms a triangle whose normal points in the direction of the fourth point. """ def __init__(self, data: UnstructuredData): if data.cells.shape[1] != 4: raise AttributeError('data.cells must be of the format' 'NDArray[(Any, 4), IntX]') self.data = data @property def tetrahedrals(self): return self.data.cells @property def n_tetrahedrals(self): return self.tetrahedrals.shape[0]