warp-lang 1.4.2__py3-none-manylinux2014_aarch64.whl → 1.5.1__py3-none-manylinux2014_aarch64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of warp-lang might be problematic. Click here for more details.
- warp/__init__.py +4 -0
- warp/autograd.py +43 -8
- warp/bin/warp-clang.so +0 -0
- warp/bin/warp.so +0 -0
- warp/build.py +21 -2
- warp/build_dll.py +23 -6
- warp/builtins.py +1819 -7
- warp/codegen.py +197 -61
- warp/config.py +2 -2
- warp/context.py +379 -107
- warp/examples/assets/pixel.jpg +0 -0
- warp/examples/benchmarks/benchmark_cloth_paddle.py +86 -0
- warp/examples/benchmarks/benchmark_gemm.py +121 -0
- warp/examples/benchmarks/benchmark_interop_paddle.py +158 -0
- warp/examples/benchmarks/benchmark_tile.py +179 -0
- warp/examples/fem/example_adaptive_grid.py +37 -10
- warp/examples/fem/example_apic_fluid.py +3 -2
- warp/examples/fem/example_convection_diffusion_dg.py +4 -5
- warp/examples/fem/example_deformed_geometry.py +1 -1
- warp/examples/fem/example_diffusion_3d.py +47 -4
- warp/examples/fem/example_distortion_energy.py +220 -0
- warp/examples/fem/example_magnetostatics.py +127 -85
- warp/examples/fem/example_nonconforming_contact.py +5 -5
- warp/examples/fem/example_stokes.py +3 -1
- warp/examples/fem/example_streamlines.py +12 -19
- warp/examples/fem/utils.py +38 -15
- warp/examples/sim/example_cloth.py +4 -25
- warp/examples/sim/example_quadruped.py +2 -1
- warp/examples/tile/example_tile_convolution.py +58 -0
- warp/examples/tile/example_tile_fft.py +47 -0
- warp/examples/tile/example_tile_filtering.py +105 -0
- warp/examples/tile/example_tile_matmul.py +79 -0
- warp/examples/tile/example_tile_mlp.py +375 -0
- warp/fem/__init__.py +8 -0
- warp/fem/cache.py +16 -12
- warp/fem/dirichlet.py +1 -1
- warp/fem/domain.py +44 -1
- warp/fem/field/__init__.py +1 -2
- warp/fem/field/field.py +31 -19
- warp/fem/field/nodal_field.py +101 -49
- warp/fem/field/virtual.py +794 -0
- warp/fem/geometry/__init__.py +2 -2
- warp/fem/geometry/deformed_geometry.py +3 -105
- warp/fem/geometry/element.py +13 -0
- warp/fem/geometry/geometry.py +165 -7
- warp/fem/geometry/grid_2d.py +3 -6
- warp/fem/geometry/grid_3d.py +31 -28
- warp/fem/geometry/hexmesh.py +3 -46
- warp/fem/geometry/nanogrid.py +3 -2
- warp/fem/geometry/{quadmesh_2d.py → quadmesh.py} +280 -159
- warp/fem/geometry/tetmesh.py +2 -43
- warp/fem/geometry/{trimesh_2d.py → trimesh.py} +354 -186
- warp/fem/integrate.py +683 -261
- warp/fem/linalg.py +404 -0
- warp/fem/operator.py +101 -18
- warp/fem/polynomial.py +5 -5
- warp/fem/quadrature/quadrature.py +45 -21
- warp/fem/space/__init__.py +45 -11
- warp/fem/space/basis_function_space.py +451 -0
- warp/fem/space/basis_space.py +58 -11
- warp/fem/space/function_space.py +146 -5
- warp/fem/space/grid_2d_function_space.py +80 -66
- warp/fem/space/grid_3d_function_space.py +113 -68
- warp/fem/space/hexmesh_function_space.py +96 -108
- warp/fem/space/nanogrid_function_space.py +62 -110
- warp/fem/space/quadmesh_function_space.py +208 -0
- warp/fem/space/shape/__init__.py +45 -7
- warp/fem/space/shape/cube_shape_function.py +328 -54
- warp/fem/space/shape/shape_function.py +10 -1
- warp/fem/space/shape/square_shape_function.py +328 -60
- warp/fem/space/shape/tet_shape_function.py +269 -19
- warp/fem/space/shape/triangle_shape_function.py +238 -19
- warp/fem/space/tetmesh_function_space.py +69 -37
- warp/fem/space/topology.py +38 -0
- warp/fem/space/trimesh_function_space.py +179 -0
- warp/fem/utils.py +6 -331
- warp/jax_experimental.py +3 -1
- warp/native/array.h +15 -0
- warp/native/builtin.h +66 -26
- warp/native/bvh.h +4 -0
- warp/native/coloring.cpp +604 -0
- warp/native/cuda_util.cpp +68 -51
- warp/native/cuda_util.h +2 -1
- warp/native/fabric.h +8 -0
- warp/native/hashgrid.h +4 -0
- warp/native/marching.cu +8 -0
- warp/native/mat.h +14 -3
- warp/native/mathdx.cpp +59 -0
- warp/native/mesh.h +4 -0
- warp/native/range.h +13 -1
- warp/native/reduce.cpp +9 -1
- warp/native/reduce.cu +7 -0
- warp/native/runlength_encode.cpp +9 -1
- warp/native/runlength_encode.cu +7 -1
- warp/native/scan.cpp +8 -0
- warp/native/scan.cu +8 -0
- warp/native/scan.h +8 -1
- warp/native/sparse.cpp +8 -0
- warp/native/sparse.cu +8 -0
- warp/native/temp_buffer.h +7 -0
- warp/native/tile.h +1854 -0
- warp/native/tile_gemm.h +341 -0
- warp/native/tile_reduce.h +210 -0
- warp/native/volume_builder.cu +8 -0
- warp/native/volume_builder.h +8 -0
- warp/native/warp.cpp +10 -2
- warp/native/warp.cu +369 -15
- warp/native/warp.h +12 -2
- warp/optim/adam.py +39 -4
- warp/paddle.py +29 -12
- warp/render/render_opengl.py +140 -67
- warp/sim/graph_coloring.py +292 -0
- warp/sim/import_urdf.py +8 -8
- warp/sim/integrator_euler.py +4 -2
- warp/sim/integrator_featherstone.py +115 -44
- warp/sim/integrator_vbd.py +6 -0
- warp/sim/model.py +109 -32
- warp/sparse.py +1 -1
- warp/stubs.py +569 -4
- warp/tape.py +12 -7
- warp/tests/assets/pixel.npy +0 -0
- warp/tests/aux_test_instancing_gc.py +18 -0
- warp/tests/test_array.py +39 -0
- warp/tests/test_codegen.py +81 -1
- warp/tests/test_codegen_instancing.py +30 -0
- warp/tests/test_collision.py +110 -0
- warp/tests/test_coloring.py +251 -0
- warp/tests/test_context.py +34 -0
- warp/tests/test_examples.py +21 -5
- warp/tests/test_fem.py +453 -113
- warp/tests/test_func.py +34 -4
- warp/tests/test_generics.py +52 -0
- warp/tests/test_iter.py +68 -0
- warp/tests/test_lerp.py +13 -87
- warp/tests/test_mat_scalar_ops.py +1 -1
- warp/tests/test_matmul.py +6 -9
- warp/tests/test_matmul_lite.py +6 -11
- warp/tests/test_mesh_query_point.py +1 -1
- warp/tests/test_module_hashing.py +23 -0
- warp/tests/test_overwrite.py +45 -0
- warp/tests/test_paddle.py +27 -87
- warp/tests/test_print.py +56 -1
- warp/tests/test_smoothstep.py +17 -83
- warp/tests/test_spatial.py +1 -1
- warp/tests/test_static.py +3 -3
- warp/tests/test_tile.py +744 -0
- warp/tests/test_tile_mathdx.py +144 -0
- warp/tests/test_tile_mlp.py +383 -0
- warp/tests/test_tile_reduce.py +374 -0
- warp/tests/test_tile_shared_memory.py +190 -0
- warp/tests/test_vbd.py +12 -20
- warp/tests/test_volume.py +43 -0
- warp/tests/unittest_suites.py +19 -2
- warp/tests/unittest_utils.py +4 -2
- warp/types.py +340 -74
- warp/utils.py +23 -3
- {warp_lang-1.4.2.dist-info → warp_lang-1.5.1.dist-info}/METADATA +32 -7
- {warp_lang-1.4.2.dist-info → warp_lang-1.5.1.dist-info}/RECORD +161 -134
- {warp_lang-1.4.2.dist-info → warp_lang-1.5.1.dist-info}/WHEEL +1 -1
- warp/fem/field/test.py +0 -180
- warp/fem/field/trial.py +0 -183
- warp/fem/space/collocated_function_space.py +0 -102
- warp/fem/space/quadmesh_2d_function_space.py +0 -261
- warp/fem/space/trimesh_2d_function_space.py +0 -153
- {warp_lang-1.4.2.dist-info → warp_lang-1.5.1.dist-info}/LICENSE.md +0 -0
- {warp_lang-1.4.2.dist-info → warp_lang-1.5.1.dist-info}/top_level.txt +0 -0
|
@@ -3,14 +3,9 @@ from typing import Union
|
|
|
3
3
|
import warp as wp
|
|
4
4
|
from warp.fem import cache
|
|
5
5
|
from warp.fem.geometry import AdaptiveNanogrid, Nanogrid
|
|
6
|
-
from warp.fem.polynomial import is_closed
|
|
7
6
|
from warp.fem.types import ElementIndex
|
|
8
7
|
|
|
9
|
-
from .shape import
|
|
10
|
-
CubeSerendipityShapeFunctions,
|
|
11
|
-
CubeTripolynomialShapeFunctions,
|
|
12
|
-
ShapeFunction,
|
|
13
|
-
)
|
|
8
|
+
from .shape import CubeShapeFunction
|
|
14
9
|
from .topology import SpaceTopology, forward_base_topology
|
|
15
10
|
|
|
16
11
|
|
|
@@ -31,19 +26,17 @@ class NanogridSpaceTopology(SpaceTopology):
|
|
|
31
26
|
def __init__(
|
|
32
27
|
self,
|
|
33
28
|
grid: Union[Nanogrid, AdaptiveNanogrid],
|
|
34
|
-
shape:
|
|
35
|
-
need_edge_indices: bool = True,
|
|
36
|
-
need_face_indices: bool = True,
|
|
29
|
+
shape: CubeShapeFunction,
|
|
37
30
|
):
|
|
38
|
-
|
|
39
|
-
raise ValueError("A closed polynomial family is required to define a continuous function space")
|
|
40
|
-
|
|
31
|
+
self._shape = shape
|
|
41
32
|
super().__init__(grid, shape.NODES_PER_ELEMENT)
|
|
42
33
|
self._grid = grid
|
|
43
|
-
self._shape = shape
|
|
44
34
|
|
|
45
35
|
self._vertex_grid = grid.vertex_grid.id
|
|
46
36
|
|
|
37
|
+
need_edge_indices = shape.EDGE_NODE_COUNT > 0
|
|
38
|
+
need_face_indices = shape.FACE_NODE_COUNT > 0
|
|
39
|
+
|
|
47
40
|
if isinstance(grid, Nanogrid):
|
|
48
41
|
self._edge_grid = grid.edge_grid.id if need_edge_indices else -1
|
|
49
42
|
self._face_grid = grid.face_grid.id if need_face_indices else -1
|
|
@@ -55,6 +48,12 @@ class NanogridSpaceTopology(SpaceTopology):
|
|
|
55
48
|
self._edge_count = grid.stacked_edge_count() if need_edge_indices else 0
|
|
56
49
|
self._face_count = grid.stacked_face_count() if need_face_indices else 0
|
|
57
50
|
|
|
51
|
+
self.element_node_index = self._make_element_node_index()
|
|
52
|
+
|
|
53
|
+
@property
|
|
54
|
+
def name(self):
|
|
55
|
+
return f"{self.geometry.name}_{self._shape.name}"
|
|
56
|
+
|
|
58
57
|
@cache.cached_arg_value
|
|
59
58
|
def topo_arg_value(self, device):
|
|
60
59
|
arg = NanogridTopologyArg()
|
|
@@ -97,51 +96,19 @@ class NanogridSpaceTopology(SpaceTopology):
|
|
|
97
96
|
|
|
98
97
|
return element_node_index_adaptive
|
|
99
98
|
|
|
100
|
-
|
|
101
|
-
@wp.func
|
|
102
|
-
def _cell_vertex_coord(cell_ijk: wp.vec3i, cell_level: int, n: int):
|
|
103
|
-
return cell_ijk + AdaptiveNanogrid.fine_ijk(wp.vec3i((n & 4) >> 2, (n & 2) >> 1, n & 1), cell_level)
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
@wp.func
|
|
107
|
-
def _cell_edge_coord(cell_ijk: wp.vec3i, cell_level: int, axis: int, offset: int):
|
|
108
|
-
e_ijk = AdaptiveNanogrid.coarse_ijk(cell_ijk, cell_level)
|
|
109
|
-
e_ijk[(axis + 1) % 3] += offset >> 1
|
|
110
|
-
e_ijk[(axis + 2) % 3] += offset & 1
|
|
111
|
-
return AdaptiveNanogrid.encode_axis_and_level(e_ijk, axis, cell_level)
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
@wp.func
|
|
115
|
-
def _cell_face_coord(cell_ijk: wp.vec3i, cell_level: int, axis: int, offset: int):
|
|
116
|
-
f_ijk = AdaptiveNanogrid.coarse_ijk(cell_ijk, cell_level)
|
|
117
|
-
f_ijk[axis] += offset
|
|
118
|
-
return AdaptiveNanogrid.encode_axis_and_level(f_ijk, axis, cell_level)
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
class NanogridTripolynomialSpaceTopology(NanogridSpaceTopology):
|
|
122
|
-
def __init__(self, grid: Union[Nanogrid, AdaptiveNanogrid], shape: CubeTripolynomialShapeFunctions):
|
|
123
|
-
super().__init__(grid, shape, need_edge_indices=shape.ORDER >= 2, need_face_indices=shape.ORDER >= 2)
|
|
124
|
-
|
|
125
|
-
self.element_node_index = self._make_element_node_index()
|
|
126
|
-
|
|
127
99
|
def node_count(self) -> int:
|
|
128
|
-
ORDER = self._shape.ORDER
|
|
129
|
-
INTERIOR_NODES_PER_EDGE = max(0, ORDER - 1)
|
|
130
|
-
INTERIOR_NODES_PER_FACE = INTERIOR_NODES_PER_EDGE**2
|
|
131
|
-
INTERIOR_NODES_PER_CELL = INTERIOR_NODES_PER_EDGE**3
|
|
132
|
-
|
|
133
100
|
return (
|
|
134
|
-
self._grid.vertex_count()
|
|
135
|
-
+ self._edge_count *
|
|
136
|
-
+ self._face_count *
|
|
137
|
-
+ self._grid.cell_count() *
|
|
101
|
+
self._grid.vertex_count() * self._shape.VERTEX_NODE_COUNT
|
|
102
|
+
+ self._edge_count * self._shape.EDGE_NODE_COUNT
|
|
103
|
+
+ self._face_count * self._shape.FACE_NODE_COUNT
|
|
104
|
+
+ self._grid.cell_count() * self._shape.INTERIOR_NODE_COUNT
|
|
138
105
|
)
|
|
139
106
|
|
|
140
107
|
def _make_element_node_index_generic(self):
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
108
|
+
VERTEX_NODE_COUNT = self._shape.VERTEX_NODE_COUNT
|
|
109
|
+
EDGE_NODE_COUNT = self._shape.EDGE_NODE_COUNT
|
|
110
|
+
FACE_NODE_COUNT = self._shape.FACE_NODE_COUNT
|
|
111
|
+
INTERIOR_NODE_COUNT = self._shape.INTERIOR_NODE_COUNT
|
|
145
112
|
|
|
146
113
|
@cache.dynamic_func(suffix=self.name)
|
|
147
114
|
def element_node_index_generic(
|
|
@@ -153,82 +120,67 @@ class NanogridTripolynomialSpaceTopology(NanogridSpaceTopology):
|
|
|
153
120
|
):
|
|
154
121
|
node_type, type_instance, type_index = self._shape.node_type_and_type_index(node_index_in_elt)
|
|
155
122
|
|
|
156
|
-
if
|
|
157
|
-
|
|
158
|
-
|
|
123
|
+
if wp.static(VERTEX_NODE_COUNT > 0):
|
|
124
|
+
if node_type == CubeShapeFunction.VERTEX:
|
|
125
|
+
n_ijk = _cell_vertex_coord(ijk, level, type_instance)
|
|
126
|
+
return (
|
|
127
|
+
wp.volume_lookup_index(topo_arg.vertex_grid, n_ijk[0], n_ijk[1], n_ijk[2]) * VERTEX_NODE_COUNT
|
|
128
|
+
+ type_index
|
|
129
|
+
)
|
|
159
130
|
|
|
160
|
-
offset = topo_arg.vertex_count
|
|
131
|
+
offset = topo_arg.vertex_count * VERTEX_NODE_COUNT
|
|
161
132
|
|
|
162
|
-
if
|
|
163
|
-
|
|
164
|
-
|
|
133
|
+
if wp.static(EDGE_NODE_COUNT > 0):
|
|
134
|
+
if node_type == CubeShapeFunction.EDGE:
|
|
135
|
+
axis = type_instance >> 2
|
|
136
|
+
node_offset = type_instance & 3
|
|
165
137
|
|
|
166
|
-
|
|
138
|
+
n_ijk = _cell_edge_coord(ijk, level, axis, node_offset)
|
|
167
139
|
|
|
168
|
-
|
|
169
|
-
|
|
140
|
+
edge_index = wp.volume_lookup_index(topo_arg.edge_grid, n_ijk[0], n_ijk[1], n_ijk[2])
|
|
141
|
+
return offset + EDGE_NODE_COUNT * edge_index + type_index
|
|
170
142
|
|
|
171
|
-
|
|
143
|
+
offset += EDGE_NODE_COUNT * topo_arg.edge_count
|
|
172
144
|
|
|
173
|
-
if
|
|
174
|
-
|
|
175
|
-
|
|
145
|
+
if wp.static(FACE_NODE_COUNT > 0):
|
|
146
|
+
if node_type == CubeShapeFunction.FACE:
|
|
147
|
+
axis = type_instance >> 1
|
|
148
|
+
node_offset = type_instance & 1
|
|
176
149
|
|
|
177
|
-
|
|
150
|
+
n_ijk = _cell_face_coord(ijk, level, axis, node_offset)
|
|
178
151
|
|
|
179
|
-
|
|
180
|
-
|
|
152
|
+
face_index = wp.volume_lookup_index(topo_arg.face_grid, n_ijk[0], n_ijk[1], n_ijk[2])
|
|
153
|
+
return offset + FACE_NODE_COUNT * face_index + type_index
|
|
181
154
|
|
|
182
|
-
|
|
155
|
+
offset += FACE_NODE_COUNT * topo_arg.face_count
|
|
183
156
|
|
|
184
|
-
return offset +
|
|
157
|
+
return offset + INTERIOR_NODE_COUNT * element_index + type_index
|
|
185
158
|
|
|
186
159
|
return element_node_index_generic
|
|
187
160
|
|
|
188
161
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
self.element_node_index = self._make_element_node_index()
|
|
194
|
-
|
|
195
|
-
def node_count(self) -> int:
|
|
196
|
-
return self.geometry.vertex_count() + (self._shape.ORDER - 1) * self._edge_count
|
|
197
|
-
|
|
198
|
-
def _make_element_node_index_generic(self):
|
|
199
|
-
ORDER = self._shape.ORDER
|
|
200
|
-
|
|
201
|
-
@cache.dynamic_func(suffix=self.name)
|
|
202
|
-
def element_node_index_generic(
|
|
203
|
-
topo_arg: NanogridTopologyArg,
|
|
204
|
-
element_index: ElementIndex,
|
|
205
|
-
node_index_in_elt: int,
|
|
206
|
-
ijk: wp.vec3i,
|
|
207
|
-
level: int,
|
|
208
|
-
):
|
|
209
|
-
node_type, type_index = self._shape.node_type_and_type_index(node_index_in_elt)
|
|
210
|
-
|
|
211
|
-
if node_type == CubeSerendipityShapeFunctions.VERTEX:
|
|
212
|
-
n_ijk = _cell_vertex_coord(ijk, level, type_index)
|
|
213
|
-
return wp.volume_lookup_index(topo_arg.vertex_grid, n_ijk[0], n_ijk[1], n_ijk[2])
|
|
214
|
-
|
|
215
|
-
type_instance, index_in_edge = CubeSerendipityShapeFunctions._cube_edge_index(node_type, type_index)
|
|
216
|
-
axis = type_instance >> 2
|
|
217
|
-
node_offset = type_instance & 3
|
|
162
|
+
@wp.func
|
|
163
|
+
def _cell_vertex_coord(cell_ijk: wp.vec3i, cell_level: int, n: int):
|
|
164
|
+
return cell_ijk + AdaptiveNanogrid.fine_ijk(wp.vec3i((n & 4) >> 2, (n & 2) >> 1, n & 1), cell_level)
|
|
218
165
|
|
|
219
|
-
n_ijk = _cell_edge_coord(ijk, level, axis, node_offset)
|
|
220
166
|
|
|
221
|
-
|
|
222
|
-
|
|
167
|
+
@wp.func
|
|
168
|
+
def _cell_edge_coord(cell_ijk: wp.vec3i, cell_level: int, axis: int, offset: int):
|
|
169
|
+
e_ijk = AdaptiveNanogrid.coarse_ijk(cell_ijk, cell_level)
|
|
170
|
+
e_ijk[(axis + 1) % 3] += offset >> 1
|
|
171
|
+
e_ijk[(axis + 2) % 3] += offset & 1
|
|
172
|
+
return AdaptiveNanogrid.encode_axis_and_level(e_ijk, axis, cell_level)
|
|
223
173
|
|
|
224
|
-
return element_node_index_generic
|
|
225
174
|
|
|
175
|
+
@wp.func
|
|
176
|
+
def _cell_face_coord(cell_ijk: wp.vec3i, cell_level: int, axis: int, offset: int):
|
|
177
|
+
f_ijk = AdaptiveNanogrid.coarse_ijk(cell_ijk, cell_level)
|
|
178
|
+
f_ijk[axis] += offset
|
|
179
|
+
return AdaptiveNanogrid.encode_axis_and_level(f_ijk, axis, cell_level)
|
|
226
180
|
|
|
227
|
-
def make_nanogrid_space_topology(grid: Union[Nanogrid, AdaptiveNanogrid], shape: ShapeFunction):
|
|
228
|
-
if isinstance(shape, CubeSerendipityShapeFunctions):
|
|
229
|
-
return forward_base_topology(NanogridSerendipitySpaceTopology, grid, shape)
|
|
230
181
|
|
|
231
|
-
|
|
232
|
-
|
|
182
|
+
def make_nanogrid_space_topology(grid: Union[Nanogrid, AdaptiveNanogrid], shape: CubeShapeFunction):
|
|
183
|
+
if isinstance(shape, CubeShapeFunction):
|
|
184
|
+
return forward_base_topology(NanogridSpaceTopology, grid, shape)
|
|
233
185
|
|
|
234
186
|
raise ValueError(f"Unsupported shape function {shape.name}")
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import warp as wp
|
|
2
|
+
from warp.fem import cache
|
|
3
|
+
from warp.fem.geometry import Quadmesh2D
|
|
4
|
+
from warp.fem.polynomial import is_closed
|
|
5
|
+
from warp.fem.types import NULL_NODE_INDEX, ElementIndex
|
|
6
|
+
|
|
7
|
+
from .shape import SquareShapeFunction
|
|
8
|
+
from .topology import SpaceTopology, forward_base_topology
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@wp.struct
|
|
12
|
+
class Quadmesh2DTopologyArg:
|
|
13
|
+
edge_vertex_indices: wp.array(dtype=wp.vec2i)
|
|
14
|
+
quad_edge_indices: wp.array2d(dtype=int)
|
|
15
|
+
|
|
16
|
+
vertex_count: int
|
|
17
|
+
edge_count: int
|
|
18
|
+
cell_count: int
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class QuadmeshSpaceTopology(SpaceTopology):
|
|
22
|
+
TopologyArg = Quadmesh2DTopologyArg
|
|
23
|
+
|
|
24
|
+
def __init__(self, mesh: Quadmesh2D, shape: SquareShapeFunction):
|
|
25
|
+
if shape.value == SquareShapeFunction.Value.Scalar and not is_closed(shape.family):
|
|
26
|
+
raise ValueError("A closed polynomial family is required to define a continuous function space")
|
|
27
|
+
|
|
28
|
+
self._shape = shape
|
|
29
|
+
super().__init__(mesh, shape.NODES_PER_ELEMENT)
|
|
30
|
+
self._mesh = mesh
|
|
31
|
+
|
|
32
|
+
self._compute_quad_edge_indices()
|
|
33
|
+
self.element_node_index = self._make_element_node_index()
|
|
34
|
+
self.element_node_sign = self._make_element_node_sign()
|
|
35
|
+
|
|
36
|
+
@property
|
|
37
|
+
def name(self):
|
|
38
|
+
return f"{self.geometry.name}_{self._shape.name}"
|
|
39
|
+
|
|
40
|
+
@cache.cached_arg_value
|
|
41
|
+
def topo_arg_value(self, device):
|
|
42
|
+
arg = Quadmesh2DTopologyArg()
|
|
43
|
+
arg.quad_edge_indices = self._quad_edge_indices.to(device)
|
|
44
|
+
arg.edge_vertex_indices = self._mesh.edge_vertex_indices.to(device)
|
|
45
|
+
|
|
46
|
+
arg.vertex_count = self._mesh.vertex_count()
|
|
47
|
+
arg.edge_count = self._mesh.side_count()
|
|
48
|
+
arg.cell_count = self._mesh.cell_count()
|
|
49
|
+
return arg
|
|
50
|
+
|
|
51
|
+
def _compute_quad_edge_indices(self):
|
|
52
|
+
self._quad_edge_indices = wp.empty(
|
|
53
|
+
dtype=int, device=self._mesh.quad_vertex_indices.device, shape=(self._mesh.cell_count(), 4)
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
wp.launch(
|
|
57
|
+
kernel=QuadmeshSpaceTopology._compute_quad_edge_indices_kernel,
|
|
58
|
+
dim=self._mesh.edge_quad_indices.shape,
|
|
59
|
+
device=self._mesh.quad_vertex_indices.device,
|
|
60
|
+
inputs=[
|
|
61
|
+
self._mesh.edge_quad_indices,
|
|
62
|
+
self._mesh.edge_vertex_indices,
|
|
63
|
+
self._mesh.quad_vertex_indices,
|
|
64
|
+
self._quad_edge_indices,
|
|
65
|
+
],
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
@wp.func
|
|
69
|
+
def _find_edge_index_in_quad(
|
|
70
|
+
edge_vtx: wp.vec2i,
|
|
71
|
+
quad_vtx: wp.vec4i,
|
|
72
|
+
):
|
|
73
|
+
for k in range(3):
|
|
74
|
+
if (edge_vtx[0] == quad_vtx[k] and edge_vtx[1] == quad_vtx[k + 1]) or (
|
|
75
|
+
edge_vtx[1] == quad_vtx[k] and edge_vtx[0] == quad_vtx[k + 1]
|
|
76
|
+
):
|
|
77
|
+
return k
|
|
78
|
+
return 3
|
|
79
|
+
|
|
80
|
+
@wp.kernel
|
|
81
|
+
def _compute_quad_edge_indices_kernel(
|
|
82
|
+
edge_quad_indices: wp.array(dtype=wp.vec2i),
|
|
83
|
+
edge_vertex_indices: wp.array(dtype=wp.vec2i),
|
|
84
|
+
quad_vertex_indices: wp.array2d(dtype=int),
|
|
85
|
+
quad_edge_indices: wp.array2d(dtype=int),
|
|
86
|
+
):
|
|
87
|
+
e = wp.tid()
|
|
88
|
+
|
|
89
|
+
edge_vtx = edge_vertex_indices[e]
|
|
90
|
+
edge_quads = edge_quad_indices[e]
|
|
91
|
+
|
|
92
|
+
q0 = edge_quads[0]
|
|
93
|
+
q0_vtx = wp.vec4i(
|
|
94
|
+
quad_vertex_indices[q0, 0],
|
|
95
|
+
quad_vertex_indices[q0, 1],
|
|
96
|
+
quad_vertex_indices[q0, 2],
|
|
97
|
+
quad_vertex_indices[q0, 3],
|
|
98
|
+
)
|
|
99
|
+
q0_edge = QuadmeshSpaceTopology._find_edge_index_in_quad(edge_vtx, q0_vtx)
|
|
100
|
+
quad_edge_indices[q0, q0_edge] = e
|
|
101
|
+
|
|
102
|
+
q1 = edge_quads[1]
|
|
103
|
+
if q1 != q0:
|
|
104
|
+
t1_vtx = wp.vec4i(
|
|
105
|
+
quad_vertex_indices[q1, 0],
|
|
106
|
+
quad_vertex_indices[q1, 1],
|
|
107
|
+
quad_vertex_indices[q1, 2],
|
|
108
|
+
quad_vertex_indices[q1, 3],
|
|
109
|
+
)
|
|
110
|
+
t1_edge = QuadmeshSpaceTopology._find_edge_index_in_quad(edge_vtx, t1_vtx)
|
|
111
|
+
quad_edge_indices[q1, t1_edge] = e
|
|
112
|
+
|
|
113
|
+
def node_count(self) -> int:
|
|
114
|
+
return (
|
|
115
|
+
self.geometry.vertex_count() * self._shape.VERTEX_NODE_COUNT
|
|
116
|
+
+ self.geometry.side_count() * self._shape.EDGE_NODE_COUNT
|
|
117
|
+
+ self.geometry.cell_count() * self._shape.INTERIOR_NODE_COUNT
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
def _make_element_node_index(self):
|
|
121
|
+
VERTEX_NODE_COUNT = self._shape.VERTEX_NODE_COUNT
|
|
122
|
+
EDGE_NODE_COUNT = self._shape.EDGE_NODE_COUNT
|
|
123
|
+
INTERIOR_NODE_COUNT = self._shape.INTERIOR_NODE_COUNT
|
|
124
|
+
|
|
125
|
+
SHAPE_TO_QUAD_IDX = wp.constant(wp.vec4i([0, 3, 1, 2]))
|
|
126
|
+
|
|
127
|
+
@cache.dynamic_func(suffix=self.name)
|
|
128
|
+
def element_node_index(
|
|
129
|
+
cell_arg: self._mesh.CellArg,
|
|
130
|
+
topo_arg: QuadmeshSpaceTopology.TopologyArg,
|
|
131
|
+
element_index: ElementIndex,
|
|
132
|
+
node_index_in_elt: int,
|
|
133
|
+
):
|
|
134
|
+
node_type, type_instance, type_index = self._shape.node_type_and_type_index(node_index_in_elt)
|
|
135
|
+
|
|
136
|
+
if wp.static(VERTEX_NODE_COUNT > 0):
|
|
137
|
+
if node_type == SquareShapeFunction.VERTEX:
|
|
138
|
+
return (
|
|
139
|
+
cell_arg.topology.quad_vertex_indices[element_index, SHAPE_TO_QUAD_IDX[type_instance]]
|
|
140
|
+
* VERTEX_NODE_COUNT
|
|
141
|
+
+ type_index
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
global_offset = topo_arg.vertex_count * VERTEX_NODE_COUNT
|
|
145
|
+
|
|
146
|
+
if wp.static(INTERIOR_NODE_COUNT > 0):
|
|
147
|
+
if node_type == SquareShapeFunction.INTERIOR:
|
|
148
|
+
return global_offset + element_index * INTERIOR_NODE_COUNT + type_index
|
|
149
|
+
|
|
150
|
+
global_offset += INTERIOR_NODE_COUNT * topo_arg.cell_count
|
|
151
|
+
|
|
152
|
+
if wp.static(EDGE_NODE_COUNT > 0):
|
|
153
|
+
# EDGE_X, EDGE_Y
|
|
154
|
+
side_start = wp.select(
|
|
155
|
+
node_type == SquareShapeFunction.EDGE_X,
|
|
156
|
+
wp.select(type_instance == 0, 1, 3),
|
|
157
|
+
wp.select(type_instance == 0, 2, 0),
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
side_index = topo_arg.quad_edge_indices[element_index, side_start]
|
|
161
|
+
local_vs = cell_arg.topology.quad_vertex_indices[element_index, side_start]
|
|
162
|
+
global_vs = topo_arg.edge_vertex_indices[side_index][0]
|
|
163
|
+
|
|
164
|
+
# Flip indexing direction
|
|
165
|
+
flipped = int(side_start >= 2) ^ int(local_vs != global_vs)
|
|
166
|
+
index_in_side = wp.select(flipped, type_index, EDGE_NODE_COUNT - 1 - type_index)
|
|
167
|
+
|
|
168
|
+
return global_offset + EDGE_NODE_COUNT * side_index + index_in_side
|
|
169
|
+
|
|
170
|
+
return NULL_NODE_INDEX # should never happen
|
|
171
|
+
|
|
172
|
+
return element_node_index
|
|
173
|
+
|
|
174
|
+
def _make_element_node_sign(self):
|
|
175
|
+
@cache.dynamic_func(suffix=self.name)
|
|
176
|
+
def element_node_sign(
|
|
177
|
+
cell_arg: self._mesh.CellArg,
|
|
178
|
+
topo_arg: QuadmeshSpaceTopology.TopologyArg,
|
|
179
|
+
element_index: ElementIndex,
|
|
180
|
+
node_index_in_elt: int,
|
|
181
|
+
):
|
|
182
|
+
node_type, type_instance, type_index = self._shape.node_type_and_type_index(node_index_in_elt)
|
|
183
|
+
|
|
184
|
+
if node_type == SquareShapeFunction.EDGE_X or node_type == SquareShapeFunction.EDGE_Y:
|
|
185
|
+
side_start = wp.select(
|
|
186
|
+
node_type == SquareShapeFunction.EDGE_X,
|
|
187
|
+
wp.select(type_instance == 0, 1, 3),
|
|
188
|
+
wp.select(type_instance == 0, 2, 0),
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
side_index = topo_arg.quad_edge_indices[element_index, side_start]
|
|
192
|
+
local_vs = cell_arg.topology.quad_vertex_indices[element_index, side_start]
|
|
193
|
+
global_vs = topo_arg.edge_vertex_indices[side_index][0]
|
|
194
|
+
|
|
195
|
+
# Flip indexing direction
|
|
196
|
+
flipped = int(side_start >= 2) ^ int(local_vs != global_vs)
|
|
197
|
+
return wp.select(flipped, 1.0, -1.0)
|
|
198
|
+
|
|
199
|
+
return 1.0
|
|
200
|
+
|
|
201
|
+
return element_node_sign
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
def make_quadmesh_space_topology(mesh: Quadmesh2D, shape: SquareShapeFunction):
|
|
205
|
+
if isinstance(shape, SquareShapeFunction):
|
|
206
|
+
return forward_base_topology(QuadmeshSpaceTopology, mesh, shape)
|
|
207
|
+
|
|
208
|
+
raise ValueError(f"Unsupported shape function {shape.name}")
|
warp/fem/space/shape/__init__.py
CHANGED
|
@@ -5,29 +5,51 @@ from warp.fem.geometry import element as _element
|
|
|
5
5
|
from warp.fem.polynomial import Polynomial
|
|
6
6
|
|
|
7
7
|
from .cube_shape_function import (
|
|
8
|
+
CubeNedelecFirstKindShapeFunctions,
|
|
8
9
|
CubeNonConformingPolynomialShapeFunctions,
|
|
10
|
+
CubeRaviartThomasShapeFunctions,
|
|
9
11
|
CubeSerendipityShapeFunctions,
|
|
12
|
+
CubeShapeFunction,
|
|
10
13
|
CubeTripolynomialShapeFunctions,
|
|
11
14
|
)
|
|
12
15
|
from .shape_function import ConstantShapeFunction, ShapeFunction
|
|
13
16
|
from .square_shape_function import (
|
|
14
17
|
SquareBipolynomialShapeFunctions,
|
|
18
|
+
SquareNedelecFirstKindShapeFunctions,
|
|
15
19
|
SquareNonConformingPolynomialShapeFunctions,
|
|
20
|
+
SquareRaviartThomasShapeFunctions,
|
|
16
21
|
SquareSerendipityShapeFunctions,
|
|
22
|
+
SquareShapeFunction,
|
|
23
|
+
)
|
|
24
|
+
from .tet_shape_function import (
|
|
25
|
+
TetrahedronNedelecFirstKindShapeFunctions,
|
|
26
|
+
TetrahedronNonConformingPolynomialShapeFunctions,
|
|
27
|
+
TetrahedronPolynomialShapeFunctions,
|
|
28
|
+
TetrahedronRaviartThomasShapeFunctions,
|
|
29
|
+
TetrahedronShapeFunction,
|
|
30
|
+
)
|
|
31
|
+
from .triangle_shape_function import (
|
|
32
|
+
TriangleNedelecFirstKindShapeFunctions,
|
|
33
|
+
TriangleNonConformingPolynomialShapeFunctions,
|
|
34
|
+
TrianglePolynomialShapeFunctions,
|
|
35
|
+
TriangleRaviartThomasShapeFunctions,
|
|
36
|
+
TriangleShapeFunction,
|
|
17
37
|
)
|
|
18
|
-
from .tet_shape_function import TetrahedronNonConformingPolynomialShapeFunctions, TetrahedronPolynomialShapeFunctions
|
|
19
|
-
from .triangle_shape_function import Triangle2DNonConformingPolynomialShapeFunctions, Triangle2DPolynomialShapeFunctions
|
|
20
38
|
|
|
21
39
|
|
|
22
40
|
class ElementBasis(Enum):
|
|
23
41
|
"""Choice of basis function to equip individual elements"""
|
|
24
42
|
|
|
25
|
-
LAGRANGE =
|
|
43
|
+
LAGRANGE = "P"
|
|
26
44
|
"""Lagrange basis functions :math:`P_k` for simplices, tensor products :math:`Q_k` for squares and cubes"""
|
|
27
|
-
SERENDIPITY =
|
|
45
|
+
SERENDIPITY = "S"
|
|
28
46
|
"""Serendipity elements :math:`S_k`, corresponding to Lagrange nodes with interior points removed (for degree <= 3)"""
|
|
29
|
-
NONCONFORMING_POLYNOMIAL =
|
|
47
|
+
NONCONFORMING_POLYNOMIAL = "dP"
|
|
30
48
|
"""Simplex Lagrange basis functions :math:`P_{kd}` embedded into non conforming reference elements (e.g. squares or cubes). Discontinuous only."""
|
|
49
|
+
NEDELEC_FIRST_KIND = "N1"
|
|
50
|
+
"""Nédélec (first kind) H(curl) shape functions. Should be used with covariant function space."""
|
|
51
|
+
RAVIART_THOMAS = "RT"
|
|
52
|
+
"""Raviart-Thomas H(div) shape functions. Should be used with contravariant function space."""
|
|
31
53
|
|
|
32
54
|
|
|
33
55
|
def get_shape_function(
|
|
@@ -58,6 +80,10 @@ def get_shape_function(
|
|
|
58
80
|
family = Polynomial.LOBATTO_GAUSS_LEGENDRE
|
|
59
81
|
|
|
60
82
|
if isinstance(element, _element.Square):
|
|
83
|
+
if element_basis == ElementBasis.NEDELEC_FIRST_KIND:
|
|
84
|
+
return SquareNedelecFirstKindShapeFunctions(degree=degree)
|
|
85
|
+
if element_basis == ElementBasis.RAVIART_THOMAS:
|
|
86
|
+
return SquareRaviartThomasShapeFunctions(degree=degree)
|
|
61
87
|
if element_basis == ElementBasis.NONCONFORMING_POLYNOMIAL:
|
|
62
88
|
return SquareNonConformingPolynomialShapeFunctions(degree=degree)
|
|
63
89
|
if element_basis == ElementBasis.SERENDIPITY and degree > 1:
|
|
@@ -65,14 +91,22 @@ def get_shape_function(
|
|
|
65
91
|
|
|
66
92
|
return SquareBipolynomialShapeFunctions(degree=degree, family=family)
|
|
67
93
|
if isinstance(element, _element.Triangle):
|
|
94
|
+
if element_basis == ElementBasis.NEDELEC_FIRST_KIND:
|
|
95
|
+
return TriangleNedelecFirstKindShapeFunctions(degree=degree)
|
|
96
|
+
if element_basis == ElementBasis.RAVIART_THOMAS:
|
|
97
|
+
return TriangleRaviartThomasShapeFunctions(degree=degree)
|
|
68
98
|
if element_basis == ElementBasis.NONCONFORMING_POLYNOMIAL:
|
|
69
|
-
return
|
|
99
|
+
return TriangleNonConformingPolynomialShapeFunctions(degree=degree)
|
|
70
100
|
if element_basis == ElementBasis.SERENDIPITY and degree > 2:
|
|
71
101
|
raise NotImplementedError("Serendipity variant not implemented yet for Triangle elements")
|
|
72
102
|
|
|
73
|
-
return
|
|
103
|
+
return TrianglePolynomialShapeFunctions(degree=degree)
|
|
74
104
|
|
|
75
105
|
if isinstance(element, _element.Cube):
|
|
106
|
+
if element_basis == ElementBasis.NEDELEC_FIRST_KIND:
|
|
107
|
+
return CubeNedelecFirstKindShapeFunctions(degree=degree)
|
|
108
|
+
if element_basis == ElementBasis.RAVIART_THOMAS:
|
|
109
|
+
return CubeRaviartThomasShapeFunctions(degree=degree)
|
|
76
110
|
if element_basis == ElementBasis.NONCONFORMING_POLYNOMIAL:
|
|
77
111
|
return CubeNonConformingPolynomialShapeFunctions(degree=degree)
|
|
78
112
|
if element_basis == ElementBasis.SERENDIPITY and degree > 1:
|
|
@@ -80,6 +114,10 @@ def get_shape_function(
|
|
|
80
114
|
|
|
81
115
|
return CubeTripolynomialShapeFunctions(degree=degree, family=family)
|
|
82
116
|
if isinstance(element, _element.Tetrahedron):
|
|
117
|
+
if element_basis == ElementBasis.NEDELEC_FIRST_KIND:
|
|
118
|
+
return TetrahedronNedelecFirstKindShapeFunctions(degree=degree)
|
|
119
|
+
if element_basis == ElementBasis.RAVIART_THOMAS:
|
|
120
|
+
return TetrahedronRaviartThomasShapeFunctions(degree=degree)
|
|
83
121
|
if element_basis == ElementBasis.NONCONFORMING_POLYNOMIAL:
|
|
84
122
|
return TetrahedronNonConformingPolynomialShapeFunctions(degree=degree)
|
|
85
123
|
if element_basis == ElementBasis.SERENDIPITY and degree > 2:
|