GarmentCode / NvidiaWarp-GarmentCode /warp /fem /space /grid_3d_function_space.py
qbhf2's picture
added NvidiaWarp and GarmentCode repos
66c9c8a
import warp as wp
import numpy as np
from warp.fem.types import ElementIndex, Coords
from warp.fem.polynomial import Polynomial, is_closed
from warp.fem.geometry import Grid3D
from warp.fem import cache
from .topology import SpaceTopology, DiscontinuousSpaceTopologyMixin, forward_base_topology
from .basis_space import ShapeBasisSpace, TraceBasisSpace
from .shape import ShapeFunction, ConstantShapeFunction
from .shape.cube_shape_function import (
CubeTripolynomialShapeFunctions,
CubeSerendipityShapeFunctions,
CubeNonConformingPolynomialShapeFunctions,
)
class Grid3DSpaceTopology(SpaceTopology):
def __init__(self, grid: Grid3D, shape: ShapeFunction):
super().__init__(grid, shape.NODES_PER_ELEMENT)
self._shape = shape
@wp.func
def _vertex_coords(vidx_in_cell: int):
x = vidx_in_cell // 4
y = (vidx_in_cell - 4 * x) // 2
z = vidx_in_cell - 4 * x - 2 * y
return wp.vec3i(x, y, z)
@wp.func
def _vertex_index(cell_arg: Grid3D.CellArg, cell_index: ElementIndex, vidx_in_cell: int):
res = cell_arg.res
strides = wp.vec2i((res[1] + 1) * (res[2] + 1), res[2] + 1)
corner = Grid3D.get_cell(res, cell_index) + Grid3DSpaceTopology._vertex_coords(vidx_in_cell)
return Grid3D._from_3d_index(strides, corner)
class Grid3DDiscontinuousSpaceTopology(
DiscontinuousSpaceTopologyMixin,
Grid3DSpaceTopology,
):
pass
class Grid3DBasisSpace(ShapeBasisSpace):
def __init__(self, topology: Grid3DSpaceTopology, shape: ShapeFunction):
super().__init__(topology, shape)
self._grid: Grid3D = topology.geometry
class Grid3DPiecewiseConstantBasis(Grid3DBasisSpace):
def __init__(self, grid: Grid3D):
shape = ConstantShapeFunction(grid.reference_cell(), space_dimension=3)
topology = Grid3DDiscontinuousSpaceTopology(grid, shape)
super().__init__(shape=shape, topology=topology)
if isinstance(grid, Grid3D):
self.node_grid = self._node_grid
def _node_grid(self):
X = (np.arange(0, self.geometry.res[0], dtype=float) + 0.5) * self._grid.cell_size[0] + self._grid.bounds_lo[0]
Y = (np.arange(0, self.geometry.res[1], dtype=float) + 0.5) * self._grid.cell_size[1] + self._grid.bounds_lo[1]
Z = (np.arange(0, self.geometry.res[2], dtype=float) + 0.5) * self._grid.cell_size[2] + self._grid.bounds_lo[2]
return np.meshgrid(X, Y, Z, indexing="ij")
class Trace(TraceBasisSpace):
@wp.func
def _node_coords_in_element(
side_arg: Grid3D.SideArg,
basis_arg: Grid3DBasisSpace.BasisArg,
element_index: ElementIndex,
node_index_in_element: int,
):
return Coords(0.5, 0.5, 0.0)
def make_node_coords_in_element(self):
return self._node_coords_in_element
def trace(self):
return Grid3DPiecewiseConstantBasis.Trace(self)
class GridTripolynomialSpaceTopology(Grid3DSpaceTopology):
def __init__(self, grid: Grid3D, shape: CubeTripolynomialShapeFunctions):
super().__init__(grid, shape)
self.element_node_index = self._make_element_node_index()
def node_count(self) -> int:
return (
(self.geometry.res[0] * self._shape.ORDER + 1)
* (self.geometry.res[1] * self._shape.ORDER + 1)
* (self.geometry.res[2] * self._shape.ORDER + 1)
)
def _make_element_node_index(self):
ORDER = self._shape.ORDER
@cache.dynamic_func(suffix=self.name)
def element_node_index(
cell_arg: Grid3D.CellArg,
topo_arg: Grid3DSpaceTopology.TopologyArg,
element_index: ElementIndex,
node_index_in_elt: int,
):
res = cell_arg.res
cell = Grid3D.get_cell(res, element_index)
node_i, node_j, node_k = self._shape._node_ijk(node_index_in_elt)
node_x = ORDER * cell[0] + node_i
node_y = ORDER * cell[1] + node_j
node_z = ORDER * cell[2] + node_k
node_pitch_y = (res[2] * ORDER) + 1
node_pitch_x = node_pitch_y * ((res[1] * ORDER) + 1)
node_index = node_pitch_x * node_x + node_pitch_y * node_y + node_z
return node_index
return element_node_index
class GridTripolynomialBasisSpace(Grid3DBasisSpace):
def __init__(
self,
grid: Grid3D,
degree: int,
family: Polynomial,
):
if family is None:
family = Polynomial.LOBATTO_GAUSS_LEGENDRE
if not is_closed(family):
raise ValueError("A closed polynomial family is required to define a continuous function space")
shape = CubeTripolynomialShapeFunctions(degree, family=family)
topology = forward_base_topology(GridTripolynomialSpaceTopology, grid, shape)
super().__init__(topology, shape)
if isinstance(grid, Grid3D):
self.node_grid = self._node_grid
def _node_grid(self):
res = self._grid.res
cell_coords = np.array(self._shape.LOBATTO_COORDS)[:-1]
grid_coords_x = np.repeat(np.arange(0, res[0], dtype=float), len(cell_coords)) + np.tile(
cell_coords, reps=res[0]
)
grid_coords_x = np.append(grid_coords_x, res[0])
X = grid_coords_x * self._grid.cell_size[0] + self._grid.origin[0]
grid_coords_y = np.repeat(np.arange(0, res[1], dtype=float), len(cell_coords)) + np.tile(
cell_coords, reps=res[1]
)
grid_coords_y = np.append(grid_coords_y, res[1])
Y = grid_coords_y * self._grid.cell_size[1] + self._grid.origin[1]
grid_coords_z = np.repeat(np.arange(0, res[2], dtype=float), len(cell_coords)) + np.tile(
cell_coords, reps=res[2]
)
grid_coords_z = np.append(grid_coords_z, res[2])
Z = grid_coords_z * self._grid.cell_size[2] + self._grid.origin[2]
return np.meshgrid(X, Y, Z, indexing="ij")
class GridDGTripolynomialBasisSpace(Grid3DBasisSpace):
def __init__(
self,
grid: Grid3D,
degree: int,
family: Polynomial,
):
if family is None:
family = Polynomial.LOBATTO_GAUSS_LEGENDRE
shape = CubeTripolynomialShapeFunctions(degree, family=family)
topology = Grid3DDiscontinuousSpaceTopology(grid, shape)
super().__init__(shape=shape, topology=topology)
def node_grid(self):
res = self._grid.res
cell_coords = np.array(self._shape.LOBATTO_COORDS)
grid_coords_x = np.repeat(np.arange(0, res[0], dtype=float), len(cell_coords)) + np.tile(
cell_coords, reps=res[0]
)
X = grid_coords_x * self._grid.cell_size[0] + self._grid.origin[0]
grid_coords_y = np.repeat(np.arange(0, res[1], dtype=float), len(cell_coords)) + np.tile(
cell_coords, reps=res[1]
)
Y = grid_coords_y * self._grid.cell_size[1] + self._grid.origin[1]
grid_coords_z = np.repeat(np.arange(0, res[2], dtype=float), len(cell_coords)) + np.tile(
cell_coords, reps=res[2]
)
Z = grid_coords_z * self._grid.cell_size[2] + self._grid.origin[2]
return np.meshgrid(X, Y, Z, indexing="ij")
class Grid3DSerendipitySpaceTopology(Grid3DSpaceTopology):
def __init__(self, grid: Grid3D, shape: CubeSerendipityShapeFunctions):
super().__init__(grid, shape)
self.element_node_index = self._make_element_node_index()
def node_count(self) -> int:
return self.geometry.vertex_count() + (self._shape.ORDER - 1) * self.geometry.edge_count()
def _make_element_node_index(self):
ORDER = self._shape.ORDER
@cache.dynamic_func(suffix=self.name)
def element_node_index(
cell_arg: Grid3D.CellArg,
topo_arg: Grid3DSpaceTopology.TopologyArg,
element_index: ElementIndex,
node_index_in_elt: int,
):
res = cell_arg.res
cell = Grid3D.get_cell(res, element_index)
node_type, type_index = self._shape.node_type_and_type_index(node_index_in_elt)
if node_type == CubeSerendipityShapeFunctions.VERTEX:
return Grid3DSpaceTopology._vertex_index(cell_arg, element_index, type_index)
axis = CubeSerendipityShapeFunctions._edge_axis(node_type)
node_all = CubeSerendipityShapeFunctions._edge_coords(type_index)
res = cell_arg.res
edge_index = 0
if axis > 0:
edge_index += (res[1] + 1) * (res[2] + 1) * res[0]
if axis > 1:
edge_index += (res[0] + 1) * (res[2] + 1) * res[1]
res_loc = Grid3D._world_to_local(axis, res)
cell_loc = Grid3D._world_to_local(axis, cell)
edge_index += (res_loc[1] + 1) * (res_loc[2] + 1) * cell_loc[0]
edge_index += (res_loc[2] + 1) * (cell_loc[1] + node_all[1])
edge_index += cell_loc[2] + node_all[2]
vertex_count = (res[0] + 1) * (res[1] + 1) * (res[2] + 1)
return vertex_count + (ORDER - 1) * edge_index + (node_all[0] - 1)
return element_node_index
class Grid3DSerendipityBasisSpace(Grid3DBasisSpace):
def __init__(
self,
grid: Grid3D,
degree: int,
family: Polynomial,
):
if family is None:
family = Polynomial.LOBATTO_GAUSS_LEGENDRE
shape = CubeSerendipityShapeFunctions(degree, family=family)
topology = forward_base_topology(Grid3DSerendipitySpaceTopology, grid, shape=shape)
super().__init__(topology=topology, shape=shape)
class Grid3DDGSerendipityBasisSpace(Grid3DBasisSpace):
def __init__(
self,
grid: Grid3D,
degree: int,
family: Polynomial,
):
if family is None:
family = Polynomial.LOBATTO_GAUSS_LEGENDRE
shape = CubeSerendipityShapeFunctions(degree, family=family)
topology = Grid3DDiscontinuousSpaceTopology(grid, shape=shape)
super().__init__(topology=topology, shape=shape)
class Grid3DDGPolynomialBasisSpace(Grid3DBasisSpace):
def __init__(
self,
grid: Grid3D,
degree: int,
):
shape = CubeNonConformingPolynomialShapeFunctions(degree)
topology = Grid3DDiscontinuousSpaceTopology(grid, shape=shape)
super().__init__(topology=topology, shape=shape)