Source code for gustaf.volumes

"""gustaf/gustaf/volumes.py."""

import numpy as np

from gustaf import helpers, settings, show, utils
from gustaf.faces import Faces
from gustaf.helpers.options import Option


[docs] class VolumesShowOption(helpers.options.ShowOption): """ Show options for vertices. """ _valid_options = helpers.options.make_valid_options( *helpers.options.vedo_common_options, Option("vedo", "lw", "Width of edges (lines) in pixel units.", (int,)), Option( "vedo", "lc", "Color of edges (lines).", (int, str, tuple, list) ), ) _helps = "Volumes" def _initialize_vedo_showable(self): """ Initialize volumes as vedo.UGrid or visually equivalent vedo.Mesh Parameters ---------- None Returns ------- volumes: vedo.UGrid or vedo.Mesh """ # without a data to plot on the surface, return vedo.UGrid # if vertex_ids is on, we will go with mesh if ( self.get("data_name", None) is None and not self.get("vertex_ids", False) and not self.get("arrow_data", False) ): from vtk import VTK_HEXAHEDRON as herr_hexa from vtk import VTK_TETRA as frau_tetra to_vtktype = {"tet": frau_tetra, "hexa": herr_hexa} grid_type = to_vtktype[self._helpee.whatami] u_grid = show.vedo.UGrid( [ self._helpee.const_vertices, self._helpee.const_volumes, [grid_type] * len(self._helpee.const_volumes), ] ) for option in ["lw", "lc"]: val = self.get(option, False) if val: getattr(u_grid, option)(val) return u_grid.c("hotpink") # to show data, let's use Faces. This will plot all the elements # as well as invisible ones. This will at least try to avoid # duplicating faces. If you wanna see inside faces, try # as_shrunk_faces = volumes.to_faces(unique=False).shrink(.8) faces = self._helpee.to_faces(unique=True) self.copy_valid_options(faces.show_options) return faces.show_options._initialize_vedo_showable()
[docs] class Volumes(Faces): kind = "volume" const_faces = helpers.raise_if.invalid_inherited_attr( "Faces.const_faces", __qualname__, property_=True, ) update_faces = helpers.raise_if.invalid_inherited_attr( "Faces.update_edges", __qualname__, property_=False, ) __slots__ = ( "_volumes", "_const_volumes", ) __show_option__ = VolumesShowOption __boundary_class__ = Faces def __init__( self, vertices=None, volumes=None, elements=None, copy=True, ): """Volumes. It has vertices and volumes. Volumes could be tetrahedrons or hexahedrons. Parameters ----------- vertices: (n, d) np.ndarray volumes: (n, 4) or (n, 8) np.ndarray """ super().__init__(vertices=vertices, copy=copy) if volumes is not None: self.volumes = volumes elif elements is not None: self.volumes = elements
[docs] @helpers.data.ComputedMeshData.depends_on(["elements"]) def faces(self): """Faces here aren't main property. So this needs to be computed. Parameters ----------- None Returns -------- faces: (n, 3) or (n, 4) np.ndarray """ whatami = self.whatami faces = None if whatami.startswith("tet"): faces = utils.connec.tet_to_tri(self.volumes) elif whatami.startswith("hexa"): faces = utils.connec.hexa_to_quad(self.volumes) return faces
[docs] @classmethod def whatareyou(cls, volume_obj): """overwrites Faces.whatareyou to tell you is this volume is tet or hexa. Parameters ----------- volume_obj: Volumes Returns -------- whatareyou: str """ if not cls.kind.startswith(volume_obj.kind): raise TypeError("Given obj is not {cls.__qualname__}") if volume_obj.volumes.shape[1] == 4: return "tet" elif volume_obj.volumes.shape[1] == 8: return "hexa" else: raise ValueError( "Invalid volumes connectivity shape. It should be (n, 4) " f"or (n, 8), but given: {volume_obj.volumes.shape}" )
@property def volumes(self): """Returns volumes. Parameters ----------- None Returns -------- volumes: (n, 4) or (n, 8) np.ndarray """ return self._volumes @volumes.setter def volumes(self, vols): """volumes setter. Similar to vertices, this will be a tracked array. Parameters ----------- vols: (n, 4) or (n, 8) np.ndarray Returns -------- None """ self._volumes = helpers.data.make_tracked_array( vols, settings.INT_DTYPE, self.setter_copies, ) if vols is not None: utils.arr.is_one_of_shapes( vols, ((-1, 4), (-1, 8)), strict=True, ) # same, but non-writeable view of tracked array self._const_volumes = self._volumes.view() self._const_volumes.flags.writeable = False @property def const_volumes(self): """Returns non-writeable view of volumes. Parameters ----------- None Returns -------- const_volumes: (n, 4) or (n, 8) np.ndarray """ return self._const_volumes
[docs] @helpers.data.ComputedMeshData.depends_on(["elements"]) def sorted_volumes(self): """Sort volumes along axis=1. Parameters ----------- None Returns -------- volumes_sorted: (volumes.shape) np.ndarray """ volumes = self._get_attr("volumes") return np.sort(volumes, axis=1)
[docs] @helpers.data.ComputedMeshData.depends_on(["elements"]) def unique_volumes(self): """Returns a namedtuple of unique volumes info. Similar to unique_edges. Parameters ----------- None Returns -------- unique_info: Unique2DIntegers valid attributes are {values, ids, inverse, counts} """ unique_info = utils.connec.sorted_unique( self.sorted_volumes(), sorted_=True, ) volumes = self._get_attr("volumes") unique_info.values[:] = volumes[unique_info.ids] return unique_info
[docs] def update_volumes(self, *args, **kwargs): """Alias to update_elements.""" self.update_elements(*args, **kwargs)
[docs] def to_faces(self, unique=True): """Returns Faces obj. Parameters ----------- unique: bool Default is True. If True, only takes unique faces. Returns -------- faces: Faces """ return Faces( self.vertices, faces=self.unique_faces().values if unique else self.faces(), )