warp-lang 1.4.1__py3-none-win_amd64.whl → 1.5.0__py3-none-win_amd64.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.

Files changed (164) hide show
  1. warp/__init__.py +4 -0
  2. warp/autograd.py +43 -8
  3. warp/bin/warp-clang.dll +0 -0
  4. warp/bin/warp.dll +0 -0
  5. warp/build.py +21 -2
  6. warp/build_dll.py +23 -6
  7. warp/builtins.py +1920 -111
  8. warp/codegen.py +186 -62
  9. warp/config.py +2 -2
  10. warp/context.py +322 -73
  11. warp/examples/assets/pixel.jpg +0 -0
  12. warp/examples/benchmarks/benchmark_cloth_paddle.py +86 -0
  13. warp/examples/benchmarks/benchmark_gemm.py +121 -0
  14. warp/examples/benchmarks/benchmark_interop_paddle.py +158 -0
  15. warp/examples/benchmarks/benchmark_tile.py +179 -0
  16. warp/examples/core/example_dem.py +2 -1
  17. warp/examples/core/example_mesh_intersect.py +3 -3
  18. warp/examples/fem/example_adaptive_grid.py +37 -10
  19. warp/examples/fem/example_apic_fluid.py +3 -2
  20. warp/examples/fem/example_convection_diffusion_dg.py +4 -5
  21. warp/examples/fem/example_deformed_geometry.py +1 -1
  22. warp/examples/fem/example_diffusion_3d.py +47 -4
  23. warp/examples/fem/example_distortion_energy.py +220 -0
  24. warp/examples/fem/example_magnetostatics.py +127 -85
  25. warp/examples/fem/example_nonconforming_contact.py +5 -5
  26. warp/examples/fem/example_stokes.py +3 -1
  27. warp/examples/fem/example_streamlines.py +12 -19
  28. warp/examples/fem/utils.py +38 -15
  29. warp/examples/optim/example_walker.py +2 -2
  30. warp/examples/sim/example_cloth.py +2 -25
  31. warp/examples/sim/example_jacobian_ik.py +6 -2
  32. warp/examples/sim/example_quadruped.py +2 -1
  33. warp/examples/tile/example_tile_convolution.py +58 -0
  34. warp/examples/tile/example_tile_fft.py +47 -0
  35. warp/examples/tile/example_tile_filtering.py +105 -0
  36. warp/examples/tile/example_tile_matmul.py +79 -0
  37. warp/examples/tile/example_tile_mlp.py +375 -0
  38. warp/fem/__init__.py +8 -0
  39. warp/fem/cache.py +16 -12
  40. warp/fem/dirichlet.py +1 -1
  41. warp/fem/domain.py +44 -1
  42. warp/fem/field/__init__.py +1 -2
  43. warp/fem/field/field.py +31 -19
  44. warp/fem/field/nodal_field.py +101 -49
  45. warp/fem/field/virtual.py +794 -0
  46. warp/fem/geometry/__init__.py +2 -2
  47. warp/fem/geometry/deformed_geometry.py +3 -105
  48. warp/fem/geometry/element.py +13 -0
  49. warp/fem/geometry/geometry.py +165 -5
  50. warp/fem/geometry/grid_2d.py +3 -6
  51. warp/fem/geometry/grid_3d.py +31 -28
  52. warp/fem/geometry/hexmesh.py +3 -46
  53. warp/fem/geometry/nanogrid.py +3 -2
  54. warp/fem/geometry/{quadmesh_2d.py → quadmesh.py} +280 -159
  55. warp/fem/geometry/tetmesh.py +2 -43
  56. warp/fem/geometry/{trimesh_2d.py → trimesh.py} +354 -186
  57. warp/fem/integrate.py +683 -261
  58. warp/fem/linalg.py +404 -0
  59. warp/fem/operator.py +101 -18
  60. warp/fem/polynomial.py +5 -5
  61. warp/fem/quadrature/quadrature.py +45 -21
  62. warp/fem/space/__init__.py +45 -11
  63. warp/fem/space/basis_function_space.py +451 -0
  64. warp/fem/space/basis_space.py +58 -11
  65. warp/fem/space/function_space.py +146 -5
  66. warp/fem/space/grid_2d_function_space.py +80 -66
  67. warp/fem/space/grid_3d_function_space.py +113 -68
  68. warp/fem/space/hexmesh_function_space.py +96 -108
  69. warp/fem/space/nanogrid_function_space.py +62 -110
  70. warp/fem/space/quadmesh_function_space.py +208 -0
  71. warp/fem/space/shape/__init__.py +45 -7
  72. warp/fem/space/shape/cube_shape_function.py +328 -54
  73. warp/fem/space/shape/shape_function.py +10 -1
  74. warp/fem/space/shape/square_shape_function.py +328 -60
  75. warp/fem/space/shape/tet_shape_function.py +269 -19
  76. warp/fem/space/shape/triangle_shape_function.py +238 -19
  77. warp/fem/space/tetmesh_function_space.py +69 -37
  78. warp/fem/space/topology.py +38 -0
  79. warp/fem/space/trimesh_function_space.py +179 -0
  80. warp/fem/utils.py +6 -331
  81. warp/jax_experimental.py +3 -1
  82. warp/native/array.h +55 -40
  83. warp/native/builtin.h +124 -43
  84. warp/native/bvh.h +4 -0
  85. warp/native/coloring.cpp +600 -0
  86. warp/native/cuda_util.cpp +14 -0
  87. warp/native/cuda_util.h +2 -1
  88. warp/native/fabric.h +8 -0
  89. warp/native/hashgrid.h +4 -0
  90. warp/native/marching.cu +8 -0
  91. warp/native/mat.h +14 -3
  92. warp/native/mathdx.cpp +59 -0
  93. warp/native/mesh.h +4 -0
  94. warp/native/range.h +13 -1
  95. warp/native/reduce.cpp +9 -1
  96. warp/native/reduce.cu +7 -0
  97. warp/native/runlength_encode.cpp +9 -1
  98. warp/native/runlength_encode.cu +7 -1
  99. warp/native/scan.cpp +8 -0
  100. warp/native/scan.cu +8 -0
  101. warp/native/scan.h +8 -1
  102. warp/native/sparse.cpp +8 -0
  103. warp/native/sparse.cu +8 -0
  104. warp/native/temp_buffer.h +7 -0
  105. warp/native/tile.h +1857 -0
  106. warp/native/tile_gemm.h +341 -0
  107. warp/native/tile_reduce.h +210 -0
  108. warp/native/volume_builder.cu +8 -0
  109. warp/native/volume_builder.h +8 -0
  110. warp/native/warp.cpp +10 -2
  111. warp/native/warp.cu +369 -15
  112. warp/native/warp.h +12 -2
  113. warp/optim/adam.py +39 -4
  114. warp/paddle.py +29 -12
  115. warp/render/render_opengl.py +137 -65
  116. warp/sim/graph_coloring.py +292 -0
  117. warp/sim/integrator_euler.py +4 -2
  118. warp/sim/integrator_featherstone.py +115 -44
  119. warp/sim/integrator_vbd.py +6 -0
  120. warp/sim/model.py +90 -17
  121. warp/stubs.py +651 -85
  122. warp/tape.py +12 -7
  123. warp/tests/assets/pixel.npy +0 -0
  124. warp/tests/aux_test_instancing_gc.py +18 -0
  125. warp/tests/test_array.py +207 -48
  126. warp/tests/test_closest_point_edge_edge.py +8 -8
  127. warp/tests/test_codegen.py +120 -1
  128. warp/tests/test_codegen_instancing.py +30 -0
  129. warp/tests/test_collision.py +110 -0
  130. warp/tests/test_coloring.py +241 -0
  131. warp/tests/test_context.py +34 -0
  132. warp/tests/test_examples.py +18 -4
  133. warp/tests/test_fabricarray.py +33 -0
  134. warp/tests/test_fem.py +453 -113
  135. warp/tests/test_func.py +48 -1
  136. warp/tests/test_generics.py +52 -0
  137. warp/tests/test_iter.py +68 -0
  138. warp/tests/test_mat_scalar_ops.py +1 -1
  139. warp/tests/test_mesh_query_point.py +5 -4
  140. warp/tests/test_module_hashing.py +23 -0
  141. warp/tests/test_paddle.py +27 -87
  142. warp/tests/test_print.py +191 -1
  143. warp/tests/test_spatial.py +1 -1
  144. warp/tests/test_tile.py +700 -0
  145. warp/tests/test_tile_mathdx.py +144 -0
  146. warp/tests/test_tile_mlp.py +383 -0
  147. warp/tests/test_tile_reduce.py +374 -0
  148. warp/tests/test_tile_shared_memory.py +190 -0
  149. warp/tests/test_vbd.py +12 -20
  150. warp/tests/test_volume.py +43 -0
  151. warp/tests/unittest_suites.py +23 -2
  152. warp/tests/unittest_utils.py +4 -0
  153. warp/types.py +339 -73
  154. warp/utils.py +22 -1
  155. {warp_lang-1.4.1.dist-info → warp_lang-1.5.0.dist-info}/METADATA +33 -7
  156. {warp_lang-1.4.1.dist-info → warp_lang-1.5.0.dist-info}/RECORD +159 -132
  157. {warp_lang-1.4.1.dist-info → warp_lang-1.5.0.dist-info}/WHEEL +1 -1
  158. warp/fem/field/test.py +0 -180
  159. warp/fem/field/trial.py +0 -183
  160. warp/fem/space/collocated_function_space.py +0 -102
  161. warp/fem/space/quadmesh_2d_function_space.py +0 -261
  162. warp/fem/space/trimesh_2d_function_space.py +0 -153
  163. {warp_lang-1.4.1.dist-info → warp_lang-1.5.0.dist-info}/LICENSE.md +0 -0
  164. {warp_lang-1.4.1.dist-info → warp_lang-1.5.0.dist-info}/top_level.txt +0 -0
@@ -13,10 +13,10 @@ from .topology import RegularDiscontinuousSpaceTopology, SpaceTopology
13
13
 
14
14
 
15
15
  class BasisSpace:
16
- """Interface class for defining a scalar-valued basis over a geometry.
16
+ """Interface class for defining a shape function space over a geometry.
17
17
 
18
18
  A basis space makes it easy to define multiple function spaces sharing the same basis (and thus nodes) but with different valuation functions;
19
- however, it is not a required ingredient of a function space.
19
+ however, it is not a required component of a function space.
20
20
 
21
21
  See also: :func:`make_polynomial_basis_space`, :func:`make_collocated_function_space`
22
22
  """
@@ -40,6 +40,11 @@ class BasisSpace:
40
40
  """Underlying geometry of the basis space"""
41
41
  return self._topology.geometry
42
42
 
43
+ @property
44
+ def value(self) -> ShapeFunction.Value:
45
+ """Value type for the underlying shape functions"""
46
+ raise NotImplementedError()
47
+
43
48
  def basis_arg_value(self, device) -> "BasisArg":
44
49
  """Value for the argument structure to be passed to device functions"""
45
50
  return BasisSpace.BasisArg()
@@ -122,6 +127,20 @@ class BasisSpace:
122
127
  def trace(self) -> "TraceBasisSpace":
123
128
  return TraceBasisSpace(self)
124
129
 
130
+ @property
131
+ def weight_type(self):
132
+ if self.value is ShapeFunction.Value.Scalar:
133
+ return float
134
+
135
+ return cache.cached_vec_type(length=self.geometry.cell_dimension, dtype=float)
136
+
137
+ @property
138
+ def weight_gradient_type(self):
139
+ if self.value is ShapeFunction.Value.Scalar:
140
+ return wp.vec(length=self.geometry.cell_dimension, dtype=float)
141
+
142
+ return cache.cached_mat_type(shape=(self.geometry.cell_dimension, self.geometry.cell_dimension), dtype=float)
143
+
125
144
 
126
145
  class ShapeBasisSpace(BasisSpace):
127
146
  """Base class for defining shape-function-based basis spaces."""
@@ -130,6 +149,10 @@ class ShapeBasisSpace(BasisSpace):
130
149
  super().__init__(topology)
131
150
  self._shape = shape
132
151
 
152
+ if self.value is not ShapeFunction.Value.Scalar:
153
+ self.BasisArg = self.topology.TopologyArg
154
+ self.basis_arg_value = self.topology.topo_arg_value
155
+
133
156
  self.ORDER = self._shape.ORDER
134
157
 
135
158
  if hasattr(shape, "element_node_triangulation"):
@@ -148,6 +171,10 @@ class ShapeBasisSpace(BasisSpace):
148
171
  """Shape functions used for defining individual element basis"""
149
172
  return self._shape
150
173
 
174
+ @property
175
+ def value(self) -> ShapeFunction.Value:
176
+ return self.shape.value
177
+
151
178
  @property
152
179
  def name(self):
153
180
  return f"{self.topology.name}_{self._shape.name}"
@@ -169,6 +196,9 @@ class ShapeBasisSpace(BasisSpace):
169
196
  def make_node_quadrature_weight(self):
170
197
  shape_node_quadrature_weight = self._shape.make_node_quadrature_weight()
171
198
 
199
+ if shape_node_quadrature_weight is None:
200
+ return None
201
+
172
202
  @cache.dynamic_func(suffix=self.name)
173
203
  def node_quadrature_weight(
174
204
  elt_arg: self.geometry.CellArg,
@@ -191,7 +221,11 @@ class ShapeBasisSpace(BasisSpace):
191
221
  coords: Coords,
192
222
  node_index_in_elt: int,
193
223
  ):
194
- return shape_element_inner_weight(coords, node_index_in_elt)
224
+ if wp.static(self.value == ShapeFunction.Value.Scalar):
225
+ return shape_element_inner_weight(coords, node_index_in_elt)
226
+ else:
227
+ sign = self.topology.element_node_sign(elt_arg, basis_arg, element_index, node_index_in_elt)
228
+ return sign * shape_element_inner_weight(coords, node_index_in_elt)
195
229
 
196
230
  return element_inner_weight
197
231
 
@@ -206,13 +240,20 @@ class ShapeBasisSpace(BasisSpace):
206
240
  coords: Coords,
207
241
  node_index_in_elt: int,
208
242
  ):
209
- return shape_element_inner_weight_gradient(coords, node_index_in_elt)
243
+ if wp.static(self.value == ShapeFunction.Value.Scalar):
244
+ return shape_element_inner_weight_gradient(coords, node_index_in_elt)
245
+ else:
246
+ sign = self.topology.element_node_sign(elt_arg, basis_arg, element_index, node_index_in_elt)
247
+ return sign * shape_element_inner_weight_gradient(coords, node_index_in_elt)
210
248
 
211
249
  return element_inner_weight_gradient
212
250
 
213
251
  def make_trace_node_quadrature_weight(self, trace_basis):
214
252
  shape_trace_node_quadrature_weight = self._shape.make_trace_node_quadrature_weight()
215
253
 
254
+ if shape_trace_node_quadrature_weight is None:
255
+ return None
256
+
216
257
  @cache.dynamic_func(suffix=self.name)
217
258
  def trace_node_quadrature_weight(
218
259
  geo_side_arg: trace_basis.geometry.SideArg,
@@ -275,6 +316,10 @@ class TraceBasisSpace(BasisSpace):
275
316
  def name(self):
276
317
  return f"{self._basis.name}_Trace"
277
318
 
319
+ @property
320
+ def value(self) -> ShapeFunction.Value:
321
+ return self._basis.value
322
+
278
323
  def make_node_coords_in_element(self):
279
324
  node_coords_in_cell = self._basis.make_node_coords_in_element()
280
325
 
@@ -316,7 +361,7 @@ class TraceBasisSpace(BasisSpace):
316
361
  ):
317
362
  cell_index, index_in_cell = self.topology.inner_cell_index(geo_side_arg, element_index, node_index_in_elt)
318
363
  if cell_index == NULL_ELEMENT_INDEX:
319
- return 0.0
364
+ return self.weight_type(0.0)
320
365
 
321
366
  cell_coords = self.geometry.side_inner_cell_coords(geo_side_arg, element_index, coords)
322
367
 
@@ -344,7 +389,7 @@ class TraceBasisSpace(BasisSpace):
344
389
  ):
345
390
  cell_index, index_in_cell = self.topology.outer_cell_index(geo_side_arg, element_index, node_index_in_elt)
346
391
  if cell_index == NULL_ELEMENT_INDEX:
347
- return 0.0
392
+ return self.weight_type(0.0)
348
393
 
349
394
  cell_coords = self.geometry.side_outer_cell_coords(geo_side_arg, element_index, coords)
350
395
 
@@ -361,7 +406,6 @@ class TraceBasisSpace(BasisSpace):
361
406
 
362
407
  def make_element_inner_weight_gradient(self):
363
408
  cell_inner_weight_gradient = self._basis.make_element_inner_weight_gradient()
364
- grad_vec_type = wp.vec(length=self.geometry.dimension, dtype=float)
365
409
 
366
410
  @cache.dynamic_func(suffix=self._basis.name)
367
411
  def trace_element_inner_weight_gradient(
@@ -373,7 +417,7 @@ class TraceBasisSpace(BasisSpace):
373
417
  ):
374
418
  cell_index, index_in_cell = self.topology.inner_cell_index(geo_side_arg, element_index, node_index_in_elt)
375
419
  if cell_index == NULL_ELEMENT_INDEX:
376
- return grad_vec_type(0.0)
420
+ return self.weight_gradient_type(0.0)
377
421
 
378
422
  cell_coords = self.geometry.side_inner_cell_coords(geo_side_arg, element_index, coords)
379
423
  geo_cell_arg = self.geometry.side_to_cell_arg(geo_side_arg)
@@ -383,7 +427,6 @@ class TraceBasisSpace(BasisSpace):
383
427
 
384
428
  def make_element_outer_weight_gradient(self):
385
429
  cell_outer_weight_gradient = self._basis.make_element_outer_weight_gradient()
386
- grad_vec_type = wp.vec(length=self.geometry.dimension, dtype=float)
387
430
 
388
431
  @cache.dynamic_func(suffix=self._basis.name)
389
432
  def trace_element_outer_weight_gradient(
@@ -395,7 +438,7 @@ class TraceBasisSpace(BasisSpace):
395
438
  ):
396
439
  cell_index, index_in_cell = self.topology.outer_cell_index(geo_side_arg, element_index, node_index_in_elt)
397
440
  if cell_index == NULL_ELEMENT_INDEX:
398
- return grad_vec_type(0.0)
441
+ return self.weight_gradient_type(0.0)
399
442
 
400
443
  cell_coords = self.geometry.side_outer_cell_coords(geo_side_arg, element_index, coords)
401
444
  geo_cell_arg = self.geometry.side_to_cell_arg(geo_side_arg)
@@ -528,6 +571,10 @@ class PointBasisSpace(BasisSpace):
528
571
  def name(self):
529
572
  return f"{self._quadrature.name}_Point"
530
573
 
574
+ @property
575
+ def value(self) -> ShapeFunction.Value:
576
+ return ShapeFunction.Value.Scalar
577
+
531
578
  def make_node_coords_in_element(self):
532
579
  @cache.dynamic_func(suffix=self.name)
533
580
  def node_coords_in_element(
@@ -571,7 +618,7 @@ class PointBasisSpace(BasisSpace):
571
618
  return element_inner_weight
572
619
 
573
620
  def make_element_inner_weight_gradient(self):
574
- gradient_vec = cache.cached_vec_type(length=self.geometry.dimension, dtype=float)
621
+ gradient_vec = cache.cached_vec_type(length=self.geometry.cell_dimension, dtype=float)
575
622
 
576
623
  @cache.dynamic_func(suffix=self.name)
577
624
  def element_inner_weight_gradient(
@@ -1,6 +1,9 @@
1
+ from typing import Any
2
+
1
3
  import warp as wp
4
+ from warp.fem import cache
2
5
  from warp.fem.geometry import Geometry
3
- from warp.fem.types import Coords, DofIndex, ElementIndex, ElementKind
6
+ from warp.fem.types import Coords, ElementIndex, ElementKind, Sample, make_free_sample
4
7
 
5
8
  from .topology import SpaceTopology
6
9
 
@@ -8,19 +11,39 @@ from .topology import SpaceTopology
8
11
  class FunctionSpace:
9
12
  """
10
13
  Interface class for function spaces, i.e. geometry + interpolation basis
14
+
15
+ The value of a function `f` at a position `x` is generally computed as
16
+ ``f(x) = L(x)[sum_i f_i N_i(x)]``
17
+ with:
18
+ - ``f_i`` the value of the ith node's degrees-of-freedom (dof)
19
+ - ``N_i(x)`` the weight associated to the node at `x`
20
+ - ``L(x)`` local linear transformation from node-space to world-space
11
21
  """
12
22
 
13
23
  dtype: type
14
24
  """Value type of the interpolation functions"""
15
25
 
26
+ dof_dtype: type
27
+ """Data type of the degrees of freedom of each node"""
28
+
16
29
  SpaceArg: wp.codegen.Struct
17
30
  """Structure containing arguments to be passed to device function"""
18
31
 
32
+ LocalValueMap: type
33
+ """Type of the local map for transforming vector-valued functions from reference to world space"""
34
+
19
35
  VALUE_DOF_COUNT: int
36
+ """Number of degrees of freedom per value, as a Warp constant"""
37
+
38
+ NODE_DOF_COUNT: int
20
39
  """Number of degrees of freedom per node, as a Warp constant"""
21
40
 
41
+ ORDER: int
42
+ """Polynomial degree of the function space, used to determine integration order"""
43
+
22
44
  def __init__(self, topology: SpaceTopology):
23
45
  self._topology = topology
46
+ self.ElementArg = self.topology.ElementArg
24
47
 
25
48
  if self._topology.is_trace:
26
49
  self.element_inner_reference_gradient_transform = self.geometry.side_inner_inverse_deformation_gradient
@@ -31,7 +54,7 @@ class FunctionSpace:
31
54
 
32
55
  def node_count(self) -> int:
33
56
  """Number of nodes in the interpolation basis"""
34
- raise NotImplementedError
57
+ return self.topology.node_count()
35
58
 
36
59
  def space_arg_value(self, device) -> wp.codegen.StructInstance:
37
60
  """Value of the arguments to be passed to device functions"""
@@ -60,7 +83,7 @@ class FunctionSpace:
60
83
  @property
61
84
  def degree(self) -> int:
62
85
  """Maximum polynomial degree of the underlying basis"""
63
- raise NotImplementedError
86
+ return self.ORDER
64
87
 
65
88
  @property
66
89
  def name(self):
@@ -83,9 +106,50 @@ class FunctionSpace:
83
106
  """
84
107
  raise NotImplementedError
85
108
 
109
+ def gradient_valid(self) -> bool:
110
+ """Whether gradient operator can be computed. Only for scalar and vector fields as higher-order tensors are not support yet"""
111
+ return not wp.types.type_is_matrix(self.dtype)
112
+
113
+ def divergence_valid(self) -> bool:
114
+ """Whether divergence of this field can be computed. Only for vector and tensor fields with same dimension as embedding geometry"""
115
+ if wp.types.type_is_vector(self.dtype):
116
+ return wp.types.type_length(self.dtype) == self.geometry.dimension
117
+ if wp.types.type_is_matrix(self.dtype):
118
+ return self.dtype._shape_[0] == self.geometry.dimension
119
+ return False
120
+
86
121
  @staticmethod
87
- def unit_dof_value(elt_arg: "SpaceTopology.ElementArg", space_arg: "SpaceArg", dof: DofIndex): # noqa: F821
88
- """Unit value for a given degree of freedom. Typically a rank-1 tensor"""
122
+ def node_basis_element(dof_coord: int):
123
+ """Basis element for node degrees of freedom.
124
+
125
+ Assumes 0 <= dof_coord < NODE_DOF_COUNT
126
+ """
127
+ raise NotImplementedError
128
+
129
+ @staticmethod
130
+ def value_basis_element(dof_coord: int):
131
+ """Basis element for the function space values
132
+
133
+ Assumes 0 <= dof_coord < VALUE_DOF_COUNT
134
+ """
135
+ raise NotImplementedError
136
+
137
+ @staticmethod
138
+ def local_value_map_inner(
139
+ elt_arg: "SpaceTopology.ElementArg",
140
+ element_index: ElementIndex,
141
+ coords: Coords,
142
+ ):
143
+ """Builds the local value map transforming from node to world space"""
144
+ raise NotImplementedError
145
+
146
+ @staticmethod
147
+ def local_value_map_outer(
148
+ elt_arg: "SpaceTopology.ElementArg",
149
+ element_index: ElementIndex,
150
+ coords: Coords,
151
+ ):
152
+ """Builds the local value map transforming vector-valued from node to world space"""
89
153
  raise NotImplementedError
90
154
 
91
155
  @staticmethod
@@ -151,3 +215,80 @@ class FunctionSpace:
151
215
  ):
152
216
  """Outer weight gradient w.r.t reference space for a node at given coordinates"""
153
217
  raise NotImplementedError
218
+
219
+ def space_value(
220
+ dof_value: "FunctionSpace.dof_dtype",
221
+ node_weight: Any,
222
+ local_value_map: "FunctionSpace.LocalValueMap",
223
+ ):
224
+ """
225
+ Assembles the world-space value of the function space
226
+ Args:
227
+ - dof_value: node value in the degrees-of-freedom basis
228
+ - node_weight: weight associated to the node, as given per `element_(inn|out)er_weight`
229
+ - local_value_map: local transformation from node space to world space, as given per `local_map_value_(inn|out)er`
230
+ """
231
+ raise NotADirectoryError
232
+
233
+ def space_gradient(
234
+ dof_value: "FunctionSpace.dof_dtype",
235
+ node_weight: Any,
236
+ local_value_map: "FunctionSpace.LocalValueMap",
237
+ grad_transform: Any,
238
+ ):
239
+ """
240
+ Assembles the world-space gradient of the function space
241
+ Args:
242
+ - dof_value: node value in the degrees-of-freedom basis
243
+ - node_weight_gradient: gradient of the weight associated to the node, as given per `element_(inn|out)er_weight_gradient`
244
+ - local_value_map: local transformation from node space to world space, as given per `local_map_value_(inn|out)er`
245
+ - grad_transform: transform mapping the reference space gradient to worls-space gradient (inverse deformation gradient)
246
+ """
247
+ raise NotImplementedError
248
+
249
+ def space_divergence(
250
+ dof_value: "FunctionSpace.dof_dtype",
251
+ node_weight: Any,
252
+ local_value_map: "FunctionSpace.LocalValueMap",
253
+ grad_transform: Any,
254
+ ):
255
+ """ "
256
+ Assembles the world-space divergence of the function space
257
+ Args:
258
+ - dof_value: node value in the degrees-of-freedom basis
259
+ - node_weight_gradient: gradient of the weight associated to the node, as given per `element_(inn|out)er_weight_gradient`
260
+ - local_value_map: local transformation from node space to world space, as given per `local_map_value_(inn|out)er`
261
+ - grad_transform: transform mapping the reference space gradient to worls-space gradient (inverse deformation gradient)
262
+ """
263
+ raise NotImplementedError
264
+
265
+ @staticmethod
266
+ def node_dof_value(
267
+ elt_arg: "FunctionSpace.ElementArg",
268
+ space_arg: "FunctionSpace.SpaceArg",
269
+ element_index: ElementIndex,
270
+ node_index_in_elt: int,
271
+ space_value: "FunctionSpace.dtype",
272
+ ):
273
+ """Converts space value to node degrees of freedom"""
274
+ raise NotImplementedError
275
+
276
+ def _make_side_inner_inverse_deformation_gradient(self):
277
+ @cache.dynamic_func(suffix=self.name)
278
+ def side_inner_inverse_deformation_gradient(args: self.ElementArg, s: Sample):
279
+ cell_index = self.side_inner_cell_index(args, s.element_index)
280
+ cell_coords = self.side_inner_cell_coords(args, s.element_index, s.element_coords)
281
+ cell_arg = self.side_to_cell_arg(args)
282
+ return self.geometry.cell_inverse_deformation_gradient(cell_arg, make_free_sample(cell_index, cell_coords))
283
+
284
+ return side_inner_inverse_deformation_gradient
285
+
286
+ def _make_side_outer_inverse_deformation_gradient(self):
287
+ @cache.dynamic_func(suffix=self.name)
288
+ def side_outer_inverse_deformation_gradient(args: self.ElementArg, s: Sample):
289
+ cell_index = self.side_outer_cell_index(args, s.element_index)
290
+ cell_coords = self.side_outer_cell_coords(args, s.element_index, s.element_coords)
291
+ cell_arg = self.side_to_cell_arg(args)
292
+ return self.geometry.cell_inverse_deformation_gradient(cell_arg, make_free_sample(cell_index, cell_coords))
293
+
294
+ return side_outer_inverse_deformation_gradient
@@ -4,23 +4,83 @@ import warp as wp
4
4
  from warp.fem import cache
5
5
  from warp.fem.geometry import Grid2D
6
6
  from warp.fem.polynomial import is_closed
7
- from warp.fem.types import ElementIndex
7
+ from warp.fem.types import NULL_NODE_INDEX, ElementIndex
8
8
 
9
- from .shape import (
10
- ShapeFunction,
11
- SquareBipolynomialShapeFunctions,
12
- SquareSerendipityShapeFunctions,
13
- )
9
+ from .shape import SquareBipolynomialShapeFunctions, SquareShapeFunction
14
10
  from .topology import SpaceTopology, forward_base_topology
15
11
 
16
12
 
17
13
  class Grid2DSpaceTopology(SpaceTopology):
18
- def __init__(self, grid: Grid2D, shape: ShapeFunction):
19
- if not is_closed(shape.family):
20
- raise ValueError("A closed polynomial family is required to define a continuous function space")
21
-
22
- super().__init__(grid, shape.NODES_PER_ELEMENT)
14
+ def __init__(self, grid: Grid2D, shape: SquareShapeFunction):
23
15
  self._shape = shape
16
+ super().__init__(grid, shape.NODES_PER_ELEMENT)
17
+
18
+ self.element_node_index = self._make_element_node_index()
19
+
20
+ TopologyArg = Grid2D.SideArg
21
+
22
+ @property
23
+ def name(self):
24
+ return f"{self.geometry.name}_{self._shape.name}"
25
+
26
+ def topo_arg_value(self, device):
27
+ return self.geometry.side_arg_value(device)
28
+
29
+ def node_count(self) -> int:
30
+ return (
31
+ self.geometry.vertex_count() * self._shape.VERTEX_NODE_COUNT
32
+ + self.geometry.side_count() * self._shape.EDGE_NODE_COUNT
33
+ + self.geometry.cell_count() * self._shape.INTERIOR_NODE_COUNT
34
+ )
35
+
36
+ def _make_element_node_index(self):
37
+ VERTEX_NODE_COUNT = self._shape.VERTEX_NODE_COUNT
38
+ EDGE_NODE_COUNT = self._shape.EDGE_NODE_COUNT
39
+ INTERIOR_NODE_COUNT = self._shape.INTERIOR_NODE_COUNT
40
+
41
+ @cache.dynamic_func(suffix=self.name)
42
+ def element_node_index(
43
+ cell_arg: Grid2D.CellArg,
44
+ topo_arg: Grid2D.SideArg,
45
+ element_index: ElementIndex,
46
+ node_index_in_elt: int,
47
+ ):
48
+ node_type, type_instance, type_index = self._shape.node_type_and_type_index(node_index_in_elt)
49
+
50
+ if wp.static(VERTEX_NODE_COUNT > 0):
51
+ if node_type == SquareShapeFunction.VERTEX:
52
+ return (
53
+ Grid2DSpaceTopology._vertex_index(cell_arg, element_index, type_instance) * VERTEX_NODE_COUNT
54
+ + type_index
55
+ )
56
+
57
+ res = cell_arg.res
58
+ vertex_count = (res[0] + 1) * (res[1] + 1)
59
+ global_offset = vertex_count
60
+
61
+ if wp.static(INTERIOR_NODE_COUNT > 0):
62
+ if node_type == SquareShapeFunction.INTERIOR:
63
+ return global_offset + element_index * INTERIOR_NODE_COUNT + type_index
64
+
65
+ cell_count = res[0] * res[1]
66
+ global_offset += INTERIOR_NODE_COUNT * cell_count
67
+
68
+ if wp.static(EDGE_NODE_COUNT > 0):
69
+ axis = 1 - (node_type - SquareShapeFunction.EDGE_X)
70
+
71
+ cell = Grid2D.get_cell(cell_arg.res, element_index)
72
+ origin = wp.vec2i(cell[Grid2D.ROTATION[axis, 0]] + type_instance, cell[Grid2D.ROTATION[axis, 1]])
73
+
74
+ side = Grid2D.Side(axis, origin)
75
+ side_index = Grid2D.side_index(topo_arg, side)
76
+
77
+ vertex_count = (res[0] + 1) * (res[1] + 1)
78
+
79
+ return global_offset + EDGE_NODE_COUNT * side_index + type_index
80
+
81
+ return NULL_NODE_INDEX # unreachable
82
+
83
+ return element_node_index
24
84
 
25
85
  @wp.func
26
86
  def _vertex_coords(vidx_in_cell: int):
@@ -37,10 +97,10 @@ class Grid2DSpaceTopology(SpaceTopology):
37
97
  return Grid2D._from_2d_index(x_stride, corner)
38
98
 
39
99
 
40
- class GridBipolynomialSpaceTopology(Grid2DSpaceTopology):
100
+ class GridBipolynomialSpaceTopology(SpaceTopology):
41
101
  def __init__(self, grid: Grid2D, shape: SquareBipolynomialShapeFunctions):
42
- super().__init__(grid, shape)
43
-
102
+ super().__init__(grid, shape.NODES_PER_ELEMENT)
103
+ self._shape = shape
44
104
  self.element_node_index = self._make_element_node_index()
45
105
 
46
106
  def node_count(self) -> int:
@@ -52,7 +112,7 @@ class GridBipolynomialSpaceTopology(Grid2DSpaceTopology):
52
112
  @cache.dynamic_func(suffix=self.name)
53
113
  def element_node_index(
54
114
  cell_arg: Grid2D.CellArg,
55
- topo_arg: Grid2DSpaceTopology.TopologyArg,
115
+ topo_arg: self.TopologyArg,
56
116
  element_index: ElementIndex,
57
117
  node_index_in_elt: int,
58
118
  ):
@@ -92,57 +152,11 @@ class GridBipolynomialSpaceTopology(Grid2DSpaceTopology):
92
152
  return np.meshgrid(X, Y, indexing="ij")
93
153
 
94
154
 
95
- class GridSerendipitySpaceTopology(Grid2DSpaceTopology):
96
- def __init__(self, grid: Grid2D, shape: SquareSerendipityShapeFunctions):
97
- super().__init__(grid, shape)
98
-
99
- self.element_node_index = self._make_element_node_index()
100
-
101
- TopologyArg = Grid2D.SideArg
102
-
103
- def topo_arg_value(self, device):
104
- return self.geometry.side_arg_value(device)
105
-
106
- def node_count(self) -> int:
107
- return self.geometry.vertex_count() + (self._shape.ORDER - 1) * self.geometry.side_count()
108
-
109
- def _make_element_node_index(self):
110
- ORDER = self._shape.ORDER
111
-
112
- @cache.dynamic_func(suffix=self.name)
113
- def element_node_index(
114
- cell_arg: Grid2D.CellArg,
115
- topo_arg: Grid2D.SideArg,
116
- element_index: ElementIndex,
117
- node_index_in_elt: int,
118
- ):
119
- node_type, type_index = self._shape.node_type_and_type_index(node_index_in_elt)
120
-
121
- if node_type == SquareSerendipityShapeFunctions.VERTEX:
122
- return Grid2DSpaceTopology._vertex_index(cell_arg, element_index, type_index)
123
-
124
- side_offset, index_in_side = SquareSerendipityShapeFunctions.side_offset_and_index(type_index)
125
- axis = 1 - (node_type - SquareSerendipityShapeFunctions.EDGE_X)
126
-
127
- cell = Grid2D.get_cell(cell_arg.res, element_index)
128
- origin = wp.vec2i(cell[Grid2D.ROTATION[axis, 0]] + side_offset, cell[Grid2D.ROTATION[axis, 1]])
129
-
130
- side = Grid2D.Side(axis, origin)
131
- side_index = Grid2D.side_index(topo_arg, side)
132
-
133
- res = cell_arg.res
134
- vertex_count = (res[0] + 1) * (res[1] + 1)
135
-
136
- return vertex_count + (ORDER - 1) * side_index + index_in_side
137
-
138
- return element_node_index
139
-
140
-
141
- def make_grid_2d_space_topology(grid: Grid2D, shape: ShapeFunction):
142
- if isinstance(shape, SquareSerendipityShapeFunctions):
143
- return forward_base_topology(GridSerendipitySpaceTopology, grid, shape)
144
-
145
- if isinstance(shape, SquareBipolynomialShapeFunctions):
155
+ def make_grid_2d_space_topology(grid: Grid2D, shape: SquareShapeFunction):
156
+ if isinstance(shape, SquareBipolynomialShapeFunctions) and is_closed(shape.family):
146
157
  return forward_base_topology(GridBipolynomialSpaceTopology, grid, shape)
147
158
 
159
+ if isinstance(shape, SquareShapeFunction):
160
+ return forward_base_topology(Grid2DSpaceTopology, grid, shape)
161
+
148
162
  raise ValueError(f"Unsupported shape function {shape.name}")