GarmentCode / NvidiaWarp-GarmentCode /warp /fem /space /grid_2d_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 Grid2D
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 import (
SquareBipolynomialShapeFunctions,
SquareSerendipityShapeFunctions,
SquareNonConformingPolynomialShapeFunctions,
)
class Grid2DSpaceTopology(SpaceTopology):
def __init__(self, grid: Grid2D, 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 // 2
y = vidx_in_cell - 2 * x
return wp.vec2i(x, y)
@wp.func
def _vertex_index(cell_arg: Grid2D.CellArg, cell_index: ElementIndex, vidx_in_cell: int):
res = cell_arg.res
x_stride = res[1] + 1
corner = Grid2D.get_cell(res, cell_index) + Grid2DSpaceTopology._vertex_coords(vidx_in_cell)
return Grid2D._from_2d_index(x_stride, corner)
class Grid2DDiscontinuousSpaceTopology(
DiscontinuousSpaceTopologyMixin,
Grid2DSpaceTopology,
):
pass
class Grid2DBasisSpace(ShapeBasisSpace):
def __init__(self, topology: Grid2DSpaceTopology, shape: ShapeFunction):
super().__init__(topology, shape)
self._grid: Grid2D = topology.geometry
class GridPiecewiseConstantBasis(Grid2DBasisSpace):
def __init__(self, grid: Grid2D):
shape = ConstantShapeFunction(grid.reference_cell(), space_dimension=2)
topology = Grid2DDiscontinuousSpaceTopology(grid, shape)
super().__init__(shape=shape, topology=topology)
if isinstance(grid, Grid2D):
self.node_grid = self._node_grid
def _node_grid(self):
res = self._grid.res
X = (np.arange(0, res[0], dtype=float) + 0.5) * self._grid.cell_size[0] + self._grid.origin[0]
Y = (np.arange(0, res[1], dtype=float) + 0.5) * self._grid.cell_size[1] + self._grid.origin[1]
return np.meshgrid(X, Y, indexing="ij")
class Trace(TraceBasisSpace):
@wp.func
def _node_coords_in_element(
side_arg: Grid2D.SideArg,
basis_arg: Grid2DBasisSpace.BasisArg,
element_index: ElementIndex,
node_index_in_element: int,
):
return Coords(0.5, 0.0, 0.0)
def make_node_coords_in_element(self):
return self._node_coords_in_element
def trace(self):
return GridPiecewiseConstantBasis.Trace(self)
class GridBipolynomialSpaceTopology(Grid2DSpaceTopology):
def __init__(self, grid: Grid2D, shape: SquareBipolynomialShapeFunctions):
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)
def _make_element_node_index(self):
ORDER = self._shape.ORDER
@cache.dynamic_func(suffix=self.name)
def element_node_index(
cell_arg: Grid2D.CellArg,
topo_arg: Grid2DSpaceTopology.TopologyArg,
element_index: ElementIndex,
node_index_in_elt: int,
):
res = cell_arg.res
cell = Grid2D.get_cell(res, element_index)
node_i = node_index_in_elt // (ORDER + 1)
node_j = node_index_in_elt - (ORDER + 1) * node_i
node_x = ORDER * cell[0] + node_i
node_y = ORDER * cell[1] + node_j
node_pitch = (res[1] * ORDER) + 1
node_index = node_pitch * node_x + node_y
return node_index
return element_node_index
class GridBipolynomialBasisSpace(Grid2DBasisSpace):
def __init__(
self,
grid: Grid2D,
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 = SquareBipolynomialShapeFunctions(degree, family=family)
topology = forward_base_topology(GridBipolynomialSpaceTopology, grid, shape)
super().__init__(topology, shape)
if isinstance(grid, Grid2D):
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]
return np.meshgrid(X, Y, indexing="ij")
class GridDGBipolynomialBasisSpace(Grid2DBasisSpace):
def __init__(
self,
grid: Grid2D,
degree: int,
family: Polynomial,
):
if family is None:
family = Polynomial.LOBATTO_GAUSS_LEGENDRE
shape = SquareBipolynomialShapeFunctions(degree, family=family)
topology = Grid2DDiscontinuousSpaceTopology(grid, shape)
super().__init__(shape=shape, topology=topology)
class GridSerendipitySpaceTopology(Grid2DSpaceTopology):
def __init__(self, grid: Grid2D, shape: SquareSerendipityShapeFunctions):
super().__init__(grid, shape)
self.element_node_index = self._make_element_node_index()
TopologyArg = Grid2D.SideArg
def topo_arg_value(self, device):
return self.geometry.side_arg_value(device)
def node_count(self) -> int:
return self.geometry.vertex_count() + (self._shape.ORDER - 1) * self.geometry.side_count()
def _make_element_node_index(self):
ORDER = self._shape.ORDER
@cache.dynamic_func(suffix=self.name)
def element_node_index(
cell_arg: Grid2D.CellArg,
topo_arg: Grid2D.SideArg,
element_index: ElementIndex,
node_index_in_elt: int,
):
node_type, type_index = self._shape.node_type_and_type_index(node_index_in_elt)
if node_type == SquareSerendipityShapeFunctions.VERTEX:
return Grid2DSpaceTopology._vertex_index(cell_arg, element_index, type_index)
side_offset, index_in_side = SquareSerendipityShapeFunctions.side_offset_and_index(type_index)
axis = 1 - (node_type - SquareSerendipityShapeFunctions.EDGE_X)
cell = Grid2D.get_cell(cell_arg.res, element_index)
origin = wp.vec2i(cell[Grid2D.ROTATION[axis, 0]] + side_offset, cell[Grid2D.ROTATION[axis, 1]])
side = Grid2D.Side(axis, origin)
side_index = Grid2D.side_index(topo_arg, side)
res = cell_arg.res
vertex_count = (res[0] + 1) * (res[1] + 1)
return vertex_count + (ORDER - 1) * side_index + index_in_side
return element_node_index
class GridSerendipityBasisSpace(Grid2DBasisSpace):
def __init__(
self,
grid: Grid2D,
degree: int,
family: Polynomial,
):
if family is None:
family = Polynomial.LOBATTO_GAUSS_LEGENDRE
shape = SquareSerendipityShapeFunctions(degree, family=family)
topology = forward_base_topology(GridSerendipitySpaceTopology, grid, shape=shape)
super().__init__(topology=topology, shape=shape)
class GridDGSerendipityBasisSpace(Grid2DBasisSpace):
def __init__(
self,
grid: Grid2D,
degree: int,
family: Polynomial,
):
if family is None:
family = Polynomial.LOBATTO_GAUSS_LEGENDRE
shape = SquareSerendipityShapeFunctions(degree, family=family)
topology = Grid2DDiscontinuousSpaceTopology(grid, shape=shape)
super().__init__(topology=topology, shape=shape)
class GridDGPolynomialBasisSpace(Grid2DBasisSpace):
def __init__(
self,
grid: Grid2D,
degree: int,
):
shape = SquareNonConformingPolynomialShapeFunctions(degree)
topology = Grid2DDiscontinuousSpaceTopology(grid, shape=shape)
super().__init__(topology=topology, shape=shape)