warp-lang 1.7.2rc1__py3-none-win_amd64.whl → 1.8.1__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 (193) hide show
  1. warp/__init__.py +3 -1
  2. warp/__init__.pyi +3489 -1
  3. warp/autograd.py +45 -122
  4. warp/bin/warp-clang.dll +0 -0
  5. warp/bin/warp.dll +0 -0
  6. warp/build.py +241 -252
  7. warp/build_dll.py +130 -26
  8. warp/builtins.py +1907 -384
  9. warp/codegen.py +272 -104
  10. warp/config.py +12 -1
  11. warp/constants.py +1 -1
  12. warp/context.py +770 -238
  13. warp/dlpack.py +1 -1
  14. warp/examples/benchmarks/benchmark_cloth.py +2 -2
  15. warp/examples/benchmarks/benchmark_tile_sort.py +155 -0
  16. warp/examples/core/example_sample_mesh.py +1 -1
  17. warp/examples/core/example_spin_lock.py +93 -0
  18. warp/examples/core/example_work_queue.py +118 -0
  19. warp/examples/fem/example_adaptive_grid.py +5 -5
  20. warp/examples/fem/example_apic_fluid.py +1 -1
  21. warp/examples/fem/example_burgers.py +1 -1
  22. warp/examples/fem/example_convection_diffusion.py +9 -6
  23. warp/examples/fem/example_darcy_ls_optimization.py +489 -0
  24. warp/examples/fem/example_deformed_geometry.py +1 -1
  25. warp/examples/fem/example_diffusion.py +2 -2
  26. warp/examples/fem/example_diffusion_3d.py +1 -1
  27. warp/examples/fem/example_distortion_energy.py +1 -1
  28. warp/examples/fem/example_elastic_shape_optimization.py +387 -0
  29. warp/examples/fem/example_magnetostatics.py +5 -3
  30. warp/examples/fem/example_mixed_elasticity.py +5 -3
  31. warp/examples/fem/example_navier_stokes.py +11 -9
  32. warp/examples/fem/example_nonconforming_contact.py +5 -3
  33. warp/examples/fem/example_streamlines.py +8 -3
  34. warp/examples/fem/utils.py +9 -8
  35. warp/examples/interop/example_jax_callable.py +34 -4
  36. warp/examples/interop/example_jax_ffi_callback.py +2 -2
  37. warp/examples/interop/example_jax_kernel.py +27 -1
  38. warp/examples/optim/example_drone.py +1 -1
  39. warp/examples/sim/example_cloth.py +1 -1
  40. warp/examples/sim/example_cloth_self_contact.py +48 -54
  41. warp/examples/tile/example_tile_block_cholesky.py +502 -0
  42. warp/examples/tile/example_tile_cholesky.py +2 -1
  43. warp/examples/tile/example_tile_convolution.py +1 -1
  44. warp/examples/tile/example_tile_filtering.py +1 -1
  45. warp/examples/tile/example_tile_matmul.py +1 -1
  46. warp/examples/tile/example_tile_mlp.py +2 -0
  47. warp/fabric.py +7 -7
  48. warp/fem/__init__.py +5 -0
  49. warp/fem/adaptivity.py +1 -1
  50. warp/fem/cache.py +152 -63
  51. warp/fem/dirichlet.py +2 -2
  52. warp/fem/domain.py +136 -6
  53. warp/fem/field/field.py +141 -99
  54. warp/fem/field/nodal_field.py +85 -39
  55. warp/fem/field/virtual.py +99 -52
  56. warp/fem/geometry/adaptive_nanogrid.py +91 -86
  57. warp/fem/geometry/closest_point.py +13 -0
  58. warp/fem/geometry/deformed_geometry.py +102 -40
  59. warp/fem/geometry/element.py +56 -2
  60. warp/fem/geometry/geometry.py +323 -22
  61. warp/fem/geometry/grid_2d.py +157 -62
  62. warp/fem/geometry/grid_3d.py +116 -20
  63. warp/fem/geometry/hexmesh.py +86 -20
  64. warp/fem/geometry/nanogrid.py +166 -86
  65. warp/fem/geometry/partition.py +59 -25
  66. warp/fem/geometry/quadmesh.py +86 -135
  67. warp/fem/geometry/tetmesh.py +47 -119
  68. warp/fem/geometry/trimesh.py +77 -270
  69. warp/fem/integrate.py +181 -95
  70. warp/fem/linalg.py +25 -58
  71. warp/fem/operator.py +124 -27
  72. warp/fem/quadrature/pic_quadrature.py +36 -14
  73. warp/fem/quadrature/quadrature.py +40 -16
  74. warp/fem/space/__init__.py +1 -1
  75. warp/fem/space/basis_function_space.py +66 -46
  76. warp/fem/space/basis_space.py +17 -4
  77. warp/fem/space/dof_mapper.py +1 -1
  78. warp/fem/space/function_space.py +2 -2
  79. warp/fem/space/grid_2d_function_space.py +4 -1
  80. warp/fem/space/hexmesh_function_space.py +4 -2
  81. warp/fem/space/nanogrid_function_space.py +3 -1
  82. warp/fem/space/partition.py +11 -2
  83. warp/fem/space/quadmesh_function_space.py +4 -1
  84. warp/fem/space/restriction.py +5 -2
  85. warp/fem/space/shape/__init__.py +10 -8
  86. warp/fem/space/tetmesh_function_space.py +4 -1
  87. warp/fem/space/topology.py +52 -21
  88. warp/fem/space/trimesh_function_space.py +4 -1
  89. warp/fem/utils.py +53 -8
  90. warp/jax.py +1 -2
  91. warp/jax_experimental/ffi.py +210 -67
  92. warp/jax_experimental/xla_ffi.py +37 -24
  93. warp/math.py +171 -1
  94. warp/native/array.h +103 -4
  95. warp/native/builtin.h +182 -35
  96. warp/native/coloring.cpp +6 -2
  97. warp/native/cuda_util.cpp +1 -1
  98. warp/native/exports.h +118 -63
  99. warp/native/intersect.h +5 -5
  100. warp/native/mat.h +8 -13
  101. warp/native/mathdx.cpp +11 -5
  102. warp/native/matnn.h +1 -123
  103. warp/native/mesh.h +1 -1
  104. warp/native/quat.h +34 -6
  105. warp/native/rand.h +7 -7
  106. warp/native/sparse.cpp +121 -258
  107. warp/native/sparse.cu +181 -274
  108. warp/native/spatial.h +305 -17
  109. warp/native/svd.h +23 -8
  110. warp/native/tile.h +603 -73
  111. warp/native/tile_radix_sort.h +1112 -0
  112. warp/native/tile_reduce.h +239 -13
  113. warp/native/tile_scan.h +240 -0
  114. warp/native/tuple.h +189 -0
  115. warp/native/vec.h +10 -20
  116. warp/native/warp.cpp +36 -4
  117. warp/native/warp.cu +588 -52
  118. warp/native/warp.h +47 -74
  119. warp/optim/linear.py +5 -1
  120. warp/paddle.py +7 -8
  121. warp/py.typed +0 -0
  122. warp/render/render_opengl.py +110 -80
  123. warp/render/render_usd.py +124 -62
  124. warp/sim/__init__.py +9 -0
  125. warp/sim/collide.py +253 -80
  126. warp/sim/graph_coloring.py +8 -1
  127. warp/sim/import_mjcf.py +4 -3
  128. warp/sim/import_usd.py +11 -7
  129. warp/sim/integrator.py +5 -2
  130. warp/sim/integrator_euler.py +1 -1
  131. warp/sim/integrator_featherstone.py +1 -1
  132. warp/sim/integrator_vbd.py +761 -322
  133. warp/sim/integrator_xpbd.py +1 -1
  134. warp/sim/model.py +265 -260
  135. warp/sim/utils.py +10 -7
  136. warp/sparse.py +303 -166
  137. warp/tape.py +54 -51
  138. warp/tests/cuda/test_conditional_captures.py +1046 -0
  139. warp/tests/cuda/test_streams.py +1 -1
  140. warp/tests/geometry/test_volume.py +2 -2
  141. warp/tests/interop/test_dlpack.py +9 -9
  142. warp/tests/interop/test_jax.py +0 -1
  143. warp/tests/run_coverage_serial.py +1 -1
  144. warp/tests/sim/disabled_kinematics.py +2 -2
  145. warp/tests/sim/{test_vbd.py → test_cloth.py} +378 -112
  146. warp/tests/sim/test_collision.py +159 -51
  147. warp/tests/sim/test_coloring.py +91 -2
  148. warp/tests/test_array.py +254 -2
  149. warp/tests/test_array_reduce.py +2 -2
  150. warp/tests/test_assert.py +53 -0
  151. warp/tests/test_atomic_cas.py +312 -0
  152. warp/tests/test_codegen.py +142 -19
  153. warp/tests/test_conditional.py +47 -1
  154. warp/tests/test_ctypes.py +0 -20
  155. warp/tests/test_devices.py +8 -0
  156. warp/tests/test_fabricarray.py +4 -2
  157. warp/tests/test_fem.py +58 -25
  158. warp/tests/test_func.py +42 -1
  159. warp/tests/test_grad.py +1 -1
  160. warp/tests/test_lerp.py +1 -3
  161. warp/tests/test_map.py +481 -0
  162. warp/tests/test_mat.py +23 -24
  163. warp/tests/test_quat.py +28 -15
  164. warp/tests/test_rounding.py +10 -38
  165. warp/tests/test_runlength_encode.py +7 -7
  166. warp/tests/test_smoothstep.py +1 -1
  167. warp/tests/test_sparse.py +83 -2
  168. warp/tests/test_spatial.py +507 -1
  169. warp/tests/test_static.py +48 -0
  170. warp/tests/test_struct.py +2 -2
  171. warp/tests/test_tape.py +38 -0
  172. warp/tests/test_tuple.py +265 -0
  173. warp/tests/test_types.py +2 -2
  174. warp/tests/test_utils.py +24 -18
  175. warp/tests/test_vec.py +38 -408
  176. warp/tests/test_vec_constructors.py +325 -0
  177. warp/tests/tile/test_tile.py +438 -131
  178. warp/tests/tile/test_tile_mathdx.py +518 -14
  179. warp/tests/tile/test_tile_matmul.py +179 -0
  180. warp/tests/tile/test_tile_reduce.py +307 -5
  181. warp/tests/tile/test_tile_shared_memory.py +136 -7
  182. warp/tests/tile/test_tile_sort.py +121 -0
  183. warp/tests/unittest_suites.py +14 -6
  184. warp/types.py +462 -308
  185. warp/utils.py +647 -86
  186. {warp_lang-1.7.2rc1.dist-info → warp_lang-1.8.1.dist-info}/METADATA +20 -6
  187. {warp_lang-1.7.2rc1.dist-info → warp_lang-1.8.1.dist-info}/RECORD +190 -176
  188. warp/stubs.py +0 -3381
  189. warp/tests/sim/test_xpbd.py +0 -399
  190. warp/tests/test_mlp.py +0 -282
  191. {warp_lang-1.7.2rc1.dist-info → warp_lang-1.8.1.dist-info}/WHEEL +0 -0
  192. {warp_lang-1.7.2rc1.dist-info → warp_lang-1.8.1.dist-info}/licenses/LICENSE.md +0 -0
  193. {warp_lang-1.7.2rc1.dist-info → warp_lang-1.8.1.dist-info}/top_level.txt +0 -0
warp/fem/field/virtual.py CHANGED
@@ -13,7 +13,7 @@
13
13
  # See the License for the specific language governing permissions and
14
14
  # limitations under the License.
15
15
 
16
- from typing import Any, Set
16
+ from typing import Any, ClassVar, Dict, Set
17
17
 
18
18
  import warp as wp
19
19
  import warp.fem.operator as operator
@@ -23,6 +23,7 @@ from warp.fem.linalg import basis_coefficient, generalized_inner, generalized_ou
23
23
  from warp.fem.quadrature import Quadrature
24
24
  from warp.fem.space import FunctionSpace, SpacePartition, SpaceRestriction
25
25
  from warp.fem.types import NULL_NODE_INDEX, DofIndex, Sample, get_node_coord, get_node_index_in_element
26
+ from warp.fem.utils import type_zero_element
26
27
 
27
28
  from .field import SpaceField
28
29
 
@@ -30,33 +31,52 @@ from .field import SpaceField
30
31
  class AdjointField(SpaceField):
31
32
  """Adjoint of a discrete field with respect to its degrees of freedom"""
32
33
 
33
- def __init__(self, space: FunctionSpace, space_partition: SpaceRestriction):
34
+ _dynamic_attribute_constructors: ClassVar = {
35
+ "EvalArg": lambda obj: obj._make_eval_arg(),
36
+ "ElementEvalArg": lambda obj: obj._make_element_eval_arg(),
37
+ "eval_degree": lambda obj: obj._make_eval_degree(),
38
+ "eval_inner": lambda obj: obj._make_eval_inner(),
39
+ "eval_grad_inner": lambda obj: obj._make_eval_grad_inner(),
40
+ "eval_div_inner": lambda obj: obj._make_eval_div_inner(),
41
+ "eval_outer": lambda obj: obj._make_eval_outer(),
42
+ "eval_grad_outer": lambda obj: obj._make_eval_grad_outer(),
43
+ "eval_div_outer": lambda obj: obj._make_eval_div_outer(),
44
+ "node_count": lambda obj: obj._make_node_count(),
45
+ "at_node": lambda obj: obj._make_at_node(),
46
+ "node_index": lambda obj: obj._make_node_index(),
47
+ }
48
+
49
+ def __init__(self, space: FunctionSpace, space_partition: SpacePartition):
34
50
  super().__init__(space, space_partition=space_partition)
35
51
 
36
52
  self.node_dof_count = self.space.NODE_DOF_COUNT
37
53
  self.value_dof_count = self.space.VALUE_DOF_COUNT
38
54
 
39
- self.EvalArg = self.space.SpaceArg
40
- self.ElementEvalArg = self._make_element_eval_arg()
41
-
42
- self.eval_arg_value = self.space.space_arg_value
43
-
44
- self.eval_degree = self._make_eval_degree()
45
- self.eval_inner = self._make_eval_inner()
46
- self.eval_grad_inner = self._make_eval_grad_inner()
47
- self.eval_div_inner = self._make_eval_div_inner()
48
- self.eval_outer = self._make_eval_outer()
49
- self.eval_grad_outer = self._make_eval_grad_outer()
50
- self.eval_div_outer = self._make_eval_div_outer()
51
- self.at_node = self._make_at_node()
55
+ cache.setup_dynamic_attributes(self)
52
56
 
53
57
  @property
54
58
  def name(self) -> str:
55
59
  return f"{self.__class__.__name__}{self.space.name}{self._space_partition.name}"
56
60
 
57
- def _make_element_eval_arg(self):
58
- from warp.fem import cache
61
+ @cache.cached_arg_value
62
+ def eval_arg_value(self, device):
63
+ arg = self.EvalArg()
64
+ self.fill_eval_arg(arg, device)
65
+ return arg
66
+
67
+ def fill_eval_arg(self, arg, device):
68
+ self.space.fill_space_arg(arg.space_arg, device)
69
+ self.space.topology.fill_topo_arg(arg.topo_arg, device)
59
70
 
71
+ def _make_eval_arg(self):
72
+ @cache.dynamic_struct(suffix=self.name)
73
+ class EvalArg:
74
+ space_arg: self.space.SpaceArg
75
+ topo_arg: self.space.topology.TopologyArg
76
+
77
+ return EvalArg
78
+
79
+ def _make_element_eval_arg(self):
60
80
  @cache.dynamic_struct(suffix=self.name)
61
81
  class ElementEvalArg:
62
82
  elt_arg: self.space.topology.ElementArg
@@ -70,7 +90,7 @@ class AdjointField(SpaceField):
70
90
  dof = self._get_dof(s)
71
91
  node_weight = self.space.element_inner_weight(
72
92
  args.elt_arg,
73
- args.eval_arg,
93
+ args.eval_arg.space_arg,
74
94
  s.element_index,
75
95
  s.element_coords,
76
96
  get_node_index_in_element(dof),
@@ -91,7 +111,7 @@ class AdjointField(SpaceField):
91
111
  dof = self._get_dof(s)
92
112
  nabla_weight = self.space.element_inner_weight_gradient(
93
113
  args.elt_arg,
94
- args.eval_arg,
114
+ args.eval_arg.space_arg,
95
115
  s.element_index,
96
116
  s.element_coords,
97
117
  get_node_index_in_element(dof),
@@ -113,7 +133,7 @@ class AdjointField(SpaceField):
113
133
  dof = self._get_dof(s)
114
134
  nabla_weight = self.space.element_inner_weight_gradient(
115
135
  args.elt_arg,
116
- args.eval_arg,
136
+ args.eval_arg.space_arg,
117
137
  s.element_index,
118
138
  s.element_coords,
119
139
  get_node_index_in_element(dof),
@@ -132,7 +152,7 @@ class AdjointField(SpaceField):
132
152
  dof = self._get_dof(s)
133
153
  node_weight = self.space.element_outer_weight(
134
154
  args.elt_arg,
135
- args.eval_arg,
155
+ args.eval_arg.space_arg,
136
156
  s.element_index,
137
157
  s.element_coords,
138
158
  get_node_index_in_element(dof),
@@ -153,7 +173,7 @@ class AdjointField(SpaceField):
153
173
  dof = self._get_dof(s)
154
174
  nabla_weight = self.space.element_outer_weight_gradient(
155
175
  args.elt_arg,
156
- args.eval_arg,
176
+ args.eval_arg.space_arg,
157
177
  s.element_index,
158
178
  s.element_coords,
159
179
  get_node_index_in_element(dof),
@@ -175,7 +195,7 @@ class AdjointField(SpaceField):
175
195
  dof = self._get_dof(s)
176
196
  nabla_weight = self.space.element_outer_weight_gradient(
177
197
  args.elt_arg,
178
- args.eval_arg,
198
+ args.eval_arg.space_arg,
179
199
  s.element_index,
180
200
  s.element_coords,
181
201
  get_node_index_in_element(dof),
@@ -193,12 +213,30 @@ class AdjointField(SpaceField):
193
213
  def at_node(args: self.ElementEvalArg, s: Sample):
194
214
  dof = self._get_dof(s)
195
215
  node_coords = self.space.node_coords_in_element(
196
- args.elt_arg, args.eval_arg, s.element_index, get_node_index_in_element(dof)
216
+ args.elt_arg, args.eval_arg.space_arg, s.element_index, get_node_index_in_element(dof)
197
217
  )
198
218
  return Sample(s.element_index, node_coords, s.qp_index, s.qp_weight, s.test_dof, s.trial_dof)
199
219
 
200
220
  return at_node
201
221
 
222
+ def _make_node_index(self):
223
+ @cache.dynamic_func(suffix=self.name)
224
+ def node_index(args: self.ElementEvalArg, s: Sample):
225
+ dof = self._get_dof(s)
226
+ node_idx = self.space.topology.element_node_index(
227
+ args.elt_arg, args.eval_arg.topo_arg, s.element_index, get_node_index_in_element(dof)
228
+ )
229
+ return node_idx
230
+
231
+ return node_index
232
+
233
+ def _make_node_count(self):
234
+ @cache.dynamic_func(suffix=self.name)
235
+ def node_count(args: self.ElementEvalArg, s: Sample):
236
+ return self.space.topology.element_node_count(args.elt_arg, args.eval_arg.topo_arg, s.element_index)
237
+
238
+ return node_count
239
+
202
240
 
203
241
  class TestField(AdjointField):
204
242
  """Field defined over a space restriction that can be used as a test function.
@@ -269,16 +307,16 @@ class LocalAdjointField(SpaceField):
269
307
  OUTER_GRAD_DOF = wp.constant(3)
270
308
  DOF_TYPE_COUNT = wp.constant(4)
271
309
 
272
- _OP_DOF_MAP_CONTINUOUS = {
310
+ _OP_DOF_MAP_CONTINUOUS: ClassVar[Dict[operator.Operator, int]] = {
273
311
  operator.inner: INNER_DOF,
274
312
  operator.outer: INNER_DOF,
275
313
  operator.grad: INNER_GRAD_DOF,
276
- operator.grad_outer: INNER_GRAD_DOF,
314
+ operator.grad_outer: OUTER_GRAD_DOF,
277
315
  operator.div: INNER_GRAD_DOF,
278
- operator.div_outer: INNER_GRAD_DOF,
316
+ operator.div_outer: OUTER_GRAD_DOF,
279
317
  }
280
318
 
281
- _OP_DOF_MAP_DISCONTINUOUS = {
319
+ _OP_DOF_MAP_DISCONTINUOUS: ClassVar[Dict[operator.Operator, int]] = {
282
320
  operator.inner: INNER_DOF,
283
321
  operator.outer: OUTER_DOF,
284
322
  operator.grad: INNER_GRAD_DOF,
@@ -293,6 +331,18 @@ class LocalAdjointField(SpaceField):
293
331
  class EvalArg:
294
332
  pass
295
333
 
334
+ _dynamic_attribute_constructors: ClassVar = {
335
+ "ElementEvalArg": lambda obj: obj._make_element_eval_arg(),
336
+ "eval_degree": lambda obj: obj._make_eval_degree(),
337
+ "_split_dof": lambda obj: obj._make_split_dof(),
338
+ "eval_inner": lambda obj: obj._make_eval_inner(),
339
+ "eval_grad_inner": lambda obj: obj._make_eval_grad_inner(),
340
+ "eval_div_inner": lambda obj: obj._make_eval_div_inner(),
341
+ "eval_outer": lambda obj: obj._make_eval_outer(),
342
+ "eval_grad_outer": lambda obj: obj._make_eval_grad_outer(),
343
+ "eval_div_outer": lambda obj: obj._make_eval_div_outer(),
344
+ }
345
+
296
346
  def __init__(self, field: AdjointField):
297
347
  # if not isinstance(field.space, CollocatedFunctionSpace):
298
348
  # raise NotImplementedError("Local assembly only implemented for collocated function spaces")
@@ -305,9 +355,6 @@ class LocalAdjointField(SpaceField):
305
355
  self.value_dof_count = self.space.VALUE_DOF_COUNT
306
356
 
307
357
  self._dof_suffix = ""
308
-
309
- self.ElementEvalArg = self._make_element_eval_arg()
310
- self.eval_degree = self._make_eval_degree()
311
358
  self.at_node = None
312
359
 
313
360
  self._is_discontinuous = (self.space.element_inner_weight != self.space.element_outer_weight) or (
@@ -318,6 +365,8 @@ class LocalAdjointField(SpaceField):
318
365
  self._TAYLOR_DOF_COUNTS = LocalAdjointField.DofOffsets(0)
319
366
  self.TAYLOR_DOF_COUNT = 0
320
367
 
368
+ cache.setup_dynamic_attributes(self)
369
+
321
370
  def notify_operator_usage(self, ops: Set[operator.Operator]):
322
371
  # Rebuild degrees-of-freedom offsets based on used operators
323
372
 
@@ -346,21 +395,7 @@ class LocalAdjointField(SpaceField):
346
395
  self._TAYLOR_DOF_COUNTS = dof_counts
347
396
 
348
397
  self._dof_suffix = "".join(str(c) for c in dof_counts)
349
-
350
- self._split_dof = self._make_split_dof()
351
-
352
- self.eval_inner = self._make_eval_inner()
353
- self.eval_grad_inner = self._make_eval_grad_inner()
354
- self.eval_div_inner = self._make_eval_div_inner()
355
-
356
- if self._is_discontinuous:
357
- self.eval_outer = self._make_eval_outer()
358
- self.eval_grad_outer = self._make_eval_grad_outer()
359
- self.eval_div_outer = self._make_eval_div_outer()
360
- else:
361
- self.eval_outer = self.eval_inner
362
- self.eval_grad_outer = self.eval_grad_inner
363
- self.eval_div_outer = self.eval_div_inner
398
+ cache.setup_dynamic_attributes(self)
364
399
 
365
400
  @property
366
401
  def name(self) -> str:
@@ -369,6 +404,9 @@ class LocalAdjointField(SpaceField):
369
404
  def eval_arg_value(self, device):
370
405
  return LocalAdjointField.EvalArg()
371
406
 
407
+ def fill_eval_arg(self, arg, device):
408
+ pass
409
+
372
410
  def _make_element_eval_arg(self):
373
411
  from warp.fem import cache
374
412
 
@@ -392,6 +430,7 @@ class LocalAdjointField(SpaceField):
392
430
 
393
431
  def _make_eval_inner(self):
394
432
  DOF_BEGIN = wp.constant(self._TAYLOR_DOF_OFFSETS[LocalAdjointField.INNER_DOF])
433
+ zero_element = type_zero_element(self.dtype)
395
434
 
396
435
  @cache.dynamic_func(suffix=self.name)
397
436
  def eval_test_inner(args: self.ElementEvalArg, s: Sample):
@@ -399,7 +438,7 @@ class LocalAdjointField(SpaceField):
399
438
 
400
439
  local_value_map = self.space.local_value_map_inner(args.elt_arg, s.element_index, s.element_coords)
401
440
  dof_value = self.space.value_basis_element(value_dof, local_value_map)
402
- return wp.where(taylor_dof == 0, dof_value, self.dtype(0.0))
441
+ return wp.where(taylor_dof == 0, dof_value, zero_element())
403
442
 
404
443
  return eval_test_inner
405
444
 
@@ -409,13 +448,14 @@ class LocalAdjointField(SpaceField):
409
448
 
410
449
  DOF_BEGIN = wp.constant(self._TAYLOR_DOF_OFFSETS[LocalAdjointField.INNER_GRAD_DOF])
411
450
  DOF_COUNT = wp.constant(self._TAYLOR_DOF_COUNTS[LocalAdjointField.INNER_GRAD_DOF])
451
+ zero_element = type_zero_element(self.gradient_dtype)
412
452
 
413
453
  @cache.dynamic_func(suffix=self.name)
414
454
  def eval_nabla_test_inner(args: self.ElementEvalArg, s: Sample):
415
455
  value_dof, taylor_dof = self._split_dof(self._get_dof(s), DOF_BEGIN)
416
456
 
417
457
  if taylor_dof < 0 or taylor_dof >= DOF_COUNT:
418
- return self.gradient_dtype(0.0)
458
+ return zero_element()
419
459
 
420
460
  grad_transform = self.space.element_inner_reference_gradient_transform(args.elt_arg, s)
421
461
  local_value_map = self.space.local_value_map_inner(args.elt_arg, s.element_index, s.element_coords)
@@ -430,13 +470,14 @@ class LocalAdjointField(SpaceField):
430
470
 
431
471
  DOF_BEGIN = wp.constant(self._TAYLOR_DOF_OFFSETS[LocalAdjointField.INNER_GRAD_DOF])
432
472
  DOF_COUNT = wp.constant(self._TAYLOR_DOF_COUNTS[LocalAdjointField.INNER_GRAD_DOF])
473
+ zero_element = type_zero_element(self.divergence_dtype)
433
474
 
434
475
  @cache.dynamic_func(suffix=self.name)
435
476
  def eval_div_test_inner(args: self.ElementEvalArg, s: Sample):
436
477
  value_dof, taylor_dof = self._split_dof(self._get_dof(s), DOF_BEGIN)
437
478
 
438
479
  if taylor_dof < 0 or taylor_dof >= DOF_COUNT:
439
- return self.divergence_dtype(0.0)
480
+ return zero_element()
440
481
 
441
482
  grad_transform = self.space.element_inner_reference_gradient_transform(args.elt_arg, s)
442
483
  local_value_map = self.space.local_value_map_inner(args.elt_arg, s.element_index, s.element_coords)
@@ -446,7 +487,11 @@ class LocalAdjointField(SpaceField):
446
487
  return eval_div_test_inner
447
488
 
448
489
  def _make_eval_outer(self):
490
+ if not self._is_discontinuous:
491
+ return self.eval_inner
492
+
449
493
  DOF_BEGIN = wp.constant(self._TAYLOR_DOF_OFFSETS[LocalAdjointField.OUTER_DOF])
494
+ zero_element = type_zero_element(self.dtype)
450
495
 
451
496
  @cache.dynamic_func(suffix=self.name)
452
497
  def eval_test_outer(args: self.ElementEvalArg, s: Sample):
@@ -454,7 +499,7 @@ class LocalAdjointField(SpaceField):
454
499
 
455
500
  local_value_map = self.space.local_value_map_outer(args.elt_arg, s.element_index, s.element_coords)
456
501
  dof_value = self.space.value_basis_element(value_dof, local_value_map)
457
- return wp.where(taylor_dof == 0, dof_value, self.dtype(0.0))
502
+ return wp.where(taylor_dof == 0, dof_value, zero_element())
458
503
 
459
504
  return eval_test_outer
460
505
 
@@ -464,13 +509,14 @@ class LocalAdjointField(SpaceField):
464
509
 
465
510
  DOF_BEGIN = wp.constant(self._TAYLOR_DOF_OFFSETS[LocalAdjointField.OUTER_GRAD_DOF])
466
511
  DOF_COUNT = wp.constant(self._TAYLOR_DOF_COUNTS[LocalAdjointField.OUTER_GRAD_DOF])
512
+ zero_element = type_zero_element(self.gradient_dtype)
467
513
 
468
514
  @cache.dynamic_func(suffix=self.name)
469
515
  def eval_nabla_test_outer(args: self.ElementEvalArg, s: Sample):
470
516
  value_dof, taylor_dof = self._split_dof(self._get_dof(s), DOF_BEGIN)
471
517
 
472
518
  if taylor_dof < 0 or taylor_dof >= DOF_COUNT:
473
- return self.gradient_dtype(0.0)
519
+ return zero_element()
474
520
 
475
521
  grad_transform = self.space.element_outer_reference_gradient_transform(args.elt_arg, s)
476
522
  local_value_map = self.space.local_value_map_outer(args.elt_arg, s.element_index, s.element_coords)
@@ -485,13 +531,14 @@ class LocalAdjointField(SpaceField):
485
531
 
486
532
  DOF_BEGIN = wp.constant(self._TAYLOR_DOF_OFFSETS[LocalAdjointField.OUTER_GRAD_DOF])
487
533
  DOF_COUNT = wp.constant(self._TAYLOR_DOF_COUNTS[LocalAdjointField.OUTER_GRAD_DOF])
534
+ zero_element = type_zero_element(self.divergence_dtype)
488
535
 
489
536
  @cache.dynamic_func(suffix=self.name)
490
537
  def eval_div_test_outer(args: self.ElementEvalArg, s: Sample):
491
538
  value_dof, taylor_dof = self._split_dof(self._get_dof(s), DOF_BEGIN)
492
539
 
493
540
  if taylor_dof < 0 or taylor_dof >= DOF_COUNT:
494
- return self.divergence_dtype(0.0)
541
+ return zero_element()
495
542
 
496
543
  grad_transform = self.space.element_outer_reference_gradient_transform(args.elt_arg, s)
497
544
  local_value_map = self.space.local_value_map_outer(args.elt_arg, s.element_index, s.element_coords)
@@ -19,7 +19,7 @@ import numpy as np
19
19
 
20
20
  import warp as wp
21
21
  from warp.fem import cache, utils
22
- from warp.fem.types import NULL_ELEMENT_INDEX, OUTSIDE, Coords, ElementIndex, Sample, make_free_sample
22
+ from warp.fem.types import OUTSIDE, Coords, ElementIndex, Sample, make_free_sample
23
23
 
24
24
  from .element import Cube, Square
25
25
  from .geometry import Geometry
@@ -107,6 +107,13 @@ class AdaptiveNanogrid(Geometry):
107
107
  self._stacked_face_grid = None
108
108
  self._stacked_face_count = 0
109
109
 
110
+ transform = self.transform
111
+ self._inverse_transform = wp.mat33f(np.linalg.inv(transform))
112
+ self._cell_volume = abs(np.linalg.det(transform))
113
+ self._face_areas = wp.vec3(
114
+ tuple(np.linalg.norm(np.cross(transform[:, k - 2], transform[:, k - 1])) for k in range(3))
115
+ )
116
+
110
117
  @property
111
118
  def cell_grid(self) -> wp.Volume:
112
119
  return self._cell_grid
@@ -167,17 +174,17 @@ class AdaptiveNanogrid(Geometry):
167
174
  @cache.cached_arg_value
168
175
  def cell_arg_value(self, device) -> CellArg:
169
176
  args = self.CellArg()
170
- args.cell_grid = self._cell_grid.id
171
- args.cell_ijk = self._cell_ijk
172
- args.cell_level = self._cell_level
173
-
174
- transform = self.transform
175
- args.inverse_transform = wp.mat33f(np.linalg.inv(transform))
176
- args.cell_volume = abs(np.linalg.det(transform))
177
- args.level_count = self.level_count
178
-
177
+ self.fill_cell_arg(args, device)
179
178
  return args
180
179
 
180
+ def fill_cell_arg(self, arg: CellArg, device):
181
+ arg.cell_grid = self._cell_grid.id
182
+ arg.cell_ijk = self._cell_ijk
183
+ arg.cell_level = self._cell_level
184
+ arg.inverse_transform = self._inverse_transform
185
+ arg.cell_volume = self._cell_volume
186
+ arg.level_count = self.level_count
187
+
181
188
  @wp.func
182
189
  def _cell_scale(args: CellArg, cell_index: int):
183
190
  return float(1 << int(args.cell_level[cell_index]))
@@ -202,72 +209,41 @@ class AdaptiveNanogrid(Geometry):
202
209
  scale = AdaptiveNanogrid._cell_scale(args, s.element_index)
203
210
  return args.inverse_transform / scale
204
211
 
205
- @wp.func
206
- def _make_sample(args: CellArg, cell_index: int, uvw: wp.vec3):
207
- ijk = args.cell_ijk[cell_index]
208
- return make_free_sample(cell_index, (uvw - wp.vec3(ijk)) / AdaptiveNanogrid._cell_scale(args, cell_index))
212
+ def supports_cell_lookup(self, device):
213
+ return True
209
214
 
210
215
  @wp.func
211
- def cell_lookup(args: CellArg, pos: wp.vec3):
212
- uvw = wp.volume_world_to_index(args.cell_grid, pos) + wp.vec3(0.5)
213
- ijk = wp.vec3i(int(wp.floor(uvw[0])), int(wp.floor(uvw[1])), int(wp.floor(uvw[2])))
214
- cell_index = AdaptiveNanogrid.find_cell(args.cell_grid, ijk, args.level_count, args.cell_level)
215
-
216
- if cell_index == -1:
217
- coords = uvw - wp.vec3(ijk)
218
-
219
- if wp.min(coords) == 0.0 or wp.max(coords) == 1.0:
220
- il = wp.where(coords[0] > 0.5, 0, -1)
221
- jl = wp.where(coords[1] > 0.5, 0, -1)
222
- kl = wp.where(coords[2] > 0.5, 0, -1)
223
-
224
- for n in range(8):
225
- ni = n >> 2
226
- nj = (n & 2) >> 1
227
- nk = n & 1
228
- nijk = ijk + wp.vec3i(ni + il, nj + jl, nk + kl)
229
-
230
- coords = uvw - wp.vec3(nijk)
231
- if wp.min(coords) >= 0.0 and wp.max(coords) <= 1.0:
232
- cell_index = AdaptiveNanogrid.find_cell(args.cell_grid, nijk, args.level_count, args.cell_level)
233
- if cell_index != -1:
234
- return AdaptiveNanogrid._make_sample(args, cell_index, uvw)
235
-
236
- return make_free_sample(NULL_ELEMENT_INDEX, Coords(OUTSIDE))
237
-
238
- return AdaptiveNanogrid._make_sample(args, cell_index, uvw)
216
+ def _lookup_cell_index(args: AdaptiveNanogridCellArg, i: int, j: int, k: int):
217
+ return AdaptiveNanogrid.find_cell(args.cell_grid, wp.vec3i(i, j, k), args.level_count, args.cell_level)
239
218
 
240
219
  @wp.func
241
- def cell_lookup(args: CellArg, pos: wp.vec3, guess: Sample):
242
- s_global = AdaptiveNanogrid.cell_lookup(args, pos)
220
+ def _cell_coordinates_local(args: AdaptiveNanogridCellArg, cell_index: int, uvw: wp.vec3):
221
+ ijk = wp.vec3(args.cell_ijk[cell_index])
222
+ rel_pos = uvw - ijk
223
+ scale = AdaptiveNanogrid._cell_scale(args, cell_index)
224
+ return rel_pos / scale
243
225
 
244
- if s_global.element_index != NULL_ELEMENT_INDEX:
245
- return s_global
226
+ @wp.func
227
+ def _cell_closest_point_local(args: AdaptiveNanogridCellArg, cell_index: int, uvw: wp.vec3):
228
+ ijk = wp.vec3(args.cell_ijk[cell_index])
229
+ rel_pos = uvw - ijk
230
+ scale = AdaptiveNanogrid._cell_scale(args, cell_index)
231
+ coords = wp.min(wp.max(rel_pos / scale, wp.vec3(0.0)), wp.vec3(1.0))
232
+ return wp.length_sq(wp.volume_index_to_world_dir(args.cell_grid, coords * scale - rel_pos)), coords
246
233
 
247
- closest_voxel = int(NULL_ELEMENT_INDEX)
248
- closest_coords = Coords(OUTSIDE)
249
- closest_dist = float(1.0e8)
234
+ @wp.func
235
+ def cell_coordinates(args: AdaptiveNanogridCellArg, cell_index: int, pos: wp.vec3):
236
+ uvw = wp.volume_world_to_index(args.cell_grid, pos) + wp.vec3(0.5)
237
+ return AdaptiveNanogrid._cell_coordinates_local(args, cell_index, uvw)
250
238
 
251
- # project to closest in stencil
239
+ @wp.func
240
+ def cell_closest_point(args: AdaptiveNanogridCellArg, cell_index: int, pos: wp.vec3):
252
241
  uvw = wp.volume_world_to_index(args.cell_grid, pos) + wp.vec3(0.5)
253
- cell_ijk = args.cell_ijk[guess.element_index]
254
- for ni in range(-1, 2):
255
- for nj in range(-1, 2):
256
- for nk in range(-1, 2):
257
- nijk = cell_ijk + wp.vec3i(ni, nj, nk)
258
- cell_idx = AdaptiveNanogrid.find_cell(args.cell_grid, nijk, args.level_count, args.cell_level)
259
- if cell_idx != -1:
260
- nijk = args.cell_ijk[cell_idx]
261
- scale = AdaptiveNanogrid._cell_scale(args, cell_idx)
262
- coords = (uvw - wp.vec3(nijk)) / scale
263
- dist, proj_coords = Nanogrid._project_on_voxel_at_origin(coords)
264
- dist *= scale
265
- if dist <= closest_dist:
266
- closest_dist = dist
267
- closest_voxel = cell_idx
268
- closest_coords = proj_coords
269
-
270
- return make_free_sample(closest_voxel, closest_coords)
242
+ dist, coords = AdaptiveNanogrid._cell_closest_point_local(args, cell_index, uvw)
243
+ return coords, dist
244
+
245
+ def make_filtered_cell_lookup(self, filter_func: wp.Function = None):
246
+ return Nanogrid._make_filtered_cell_lookup(self, filter_func)
271
247
 
272
248
  @wp.func
273
249
  def cell_measure(args: CellArg, s: Sample):
@@ -280,6 +256,7 @@ class AdaptiveNanogrid(Geometry):
280
256
 
281
257
  SideIndexArg = Nanogrid.SideIndexArg
282
258
  side_index_arg_value = Nanogrid.side_index_arg_value
259
+ fill_side_index_arg = Nanogrid.fill_side_index_arg
283
260
 
284
261
  SideArg = AdaptiveNanogridSideArg
285
262
 
@@ -289,20 +266,20 @@ class AdaptiveNanogrid(Geometry):
289
266
 
290
267
  @cache.cached_arg_value
291
268
  def side_arg_value(self, device) -> SideArg:
292
- self._ensure_face_grid()
293
-
294
269
  args = self.SideArg()
295
- args.cell_arg = self.cell_arg_value(device)
296
- args.face_ijk = self._face_ijk.to(device)
297
- args.face_flags = self._face_flags.to(device)
298
- args.face_cell_indices = self._face_cell_indices.to(device)
299
- transform = self.transform
300
- args.face_areas = wp.vec3(
301
- tuple(np.linalg.norm(np.cross(transform[:, k - 2], transform[:, k - 1])) for k in range(3))
302
- )
270
+ self.fill_side_arg(args, device)
303
271
 
304
272
  return args
305
273
 
274
+ def fill_side_arg(self, arg: SideArg, device):
275
+ self._ensure_face_grid()
276
+
277
+ self.fill_cell_arg(arg.cell_arg, device)
278
+ arg.face_ijk = self._face_ijk.to(device)
279
+ arg.face_flags = self._face_flags.to(device)
280
+ arg.face_cell_indices = self._face_cell_indices.to(device)
281
+ arg.face_areas = self._face_areas
282
+
306
283
  @wp.func
307
284
  def boundary_side_index(args: SideIndexArg, boundary_side_index: int):
308
285
  return args.boundary_face_indices[boundary_side_index]
@@ -320,9 +297,10 @@ class AdaptiveNanogrid(Geometry):
320
297
  ijk = args.face_ijk[s.element_index]
321
298
  flags = args.face_flags[s.element_index]
322
299
  axis = Nanogrid._get_face_axis(flags)
300
+ flip = Nanogrid._get_face_inner_offset(flags)
323
301
  scale = AdaptiveNanogrid._get_face_scale(flags)
324
302
 
325
- uvw = wp.vec3(ijk) + scale * Nanogrid._side_to_cell_coords(axis, 0.0, s.element_coords)
303
+ uvw = wp.vec3(ijk) + scale * Nanogrid._side_to_cell_coords(axis, flip, 0.0, s.element_coords)
326
304
 
327
305
  cell_grid = args.cell_arg.cell_grid
328
306
  return wp.volume_index_to_world(cell_grid, uvw - wp.vec3(0.5))
@@ -391,9 +369,10 @@ class AdaptiveNanogrid(Geometry):
391
369
  def side_inner_cell_coords(args: SideArg, side_index: ElementIndex, side_coords: Coords):
392
370
  flags = args.face_flags[side_index]
393
371
  axis = Nanogrid._get_face_axis(flags)
372
+ flip = Nanogrid._get_face_inner_offset(flags)
394
373
  offset = Nanogrid._get_face_inner_offset(flags)
395
374
 
396
- same_level_cell_coords = Nanogrid._side_to_cell_coords(axis, 1.0 - float(offset), side_coords)
375
+ same_level_cell_coords = Nanogrid._side_to_cell_coords(axis, flip, 1.0 - float(offset), side_coords)
397
376
  same_level_cell_ijk = args.face_ijk[side_index]
398
377
  side_level = AdaptiveNanogrid._get_face_level(flags)
399
378
  same_level_cell_ijk[axis] += (offset - 1) << side_level
@@ -410,9 +389,10 @@ class AdaptiveNanogrid(Geometry):
410
389
  def side_outer_cell_coords(args: SideArg, side_index: ElementIndex, side_coords: Coords):
411
390
  flags = args.face_flags[side_index]
412
391
  axis = Nanogrid._get_face_axis(flags)
392
+ flip = Nanogrid._get_face_inner_offset(flags)
413
393
  offset = Nanogrid._get_face_outer_offset(flags)
414
394
 
415
- same_level_cell_coords = Nanogrid._side_to_cell_coords(axis, float(offset), side_coords)
395
+ same_level_cell_coords = Nanogrid._side_to_cell_coords(axis, flip, float(offset), side_coords)
416
396
  same_level_cell_ijk = args.face_ijk[side_index]
417
397
  side_level = AdaptiveNanogrid._get_face_level(flags)
418
398
  same_level_cell_ijk[axis] -= offset << side_level
@@ -434,6 +414,7 @@ class AdaptiveNanogrid(Geometry):
434
414
  ):
435
415
  flags = args.face_flags[side_index]
436
416
  axis = Nanogrid._get_face_axis(flags)
417
+ flip = Nanogrid._get_face_inner_offset(flags)
437
418
  side_level = AdaptiveNanogrid._get_face_level(flags)
438
419
  cell_level = int(args.cell_arg.cell_level[element_index])
439
420
 
@@ -450,11 +431,35 @@ class AdaptiveNanogrid(Geometry):
450
431
  and wp.max(same_level_cell_coords) <= 1.0
451
432
  )
452
433
 
453
- return wp.where(
454
- on_side,
455
- Coords(same_level_cell_coords[(axis + 1) % 3], same_level_cell_coords[(axis + 2) % 3], 0.0),
456
- Coords(OUTSIDE),
457
- )
434
+ return wp.where(on_side, Nanogrid._cell_to_side_coords(axis, flip, same_level_cell_coords), Coords(OUTSIDE))
435
+
436
+ @wp.func
437
+ def side_coordinates(args: SideArg, side_index: int, pos: wp.vec3):
438
+ cell_arg = args.cell_arg
439
+
440
+ ijk = args.face_ijk[side_index]
441
+ fine_cell_coords = wp.volume_world_to_index(cell_arg.cell_grid, pos) + wp.vec3(0.5) - wp.vec3(ijk)
442
+
443
+ flags = args.face_flags[side_index]
444
+ side_level = AdaptiveNanogrid._get_face_level(flags)
445
+ axis = Nanogrid._get_face_axis(flags)
446
+ flip = Nanogrid._get_face_inner_offset(flags)
447
+
448
+ return Nanogrid._cell_to_side_coords(axis, flip, fine_cell_coords / float(1 << side_level))
449
+
450
+ @wp.func
451
+ def side_closest_point(args: SideArg, side_index: int, pos: wp.vec3):
452
+ coords = AdaptiveNanogrid.side_coordinates(args, side_index, pos)
453
+
454
+ proj_coords = Coords(wp.clamp(coords[0], 0.0, 1.0), wp.clamp(coords[1], 0.0, 1.0), 0.0)
455
+
456
+ flags = args.face_flags[side_index]
457
+ axis = Nanogrid._get_face_axis(flags)
458
+ flip = Nanogrid._get_face_inner_offset(flags)
459
+ side_level = AdaptiveNanogrid._get_face_level(flags)
460
+ cell_coord_offset = Nanogrid._side_to_cell_coords(axis, flip, 0, coords - proj_coords) * float(1 << side_level)
461
+
462
+ return proj_coords, wp.length_sq(wp.volume_index_to_world_dir(args.cell_grid, cell_coord_offset))
458
463
 
459
464
  def _build_face_grid(self, temporary_store: Optional[cache.TemporaryStore] = None):
460
465
  device = self._cell_grid.device
@@ -82,3 +82,16 @@ def project_on_tet_at_origin(q: wp.vec3, e1: wp.vec3, e2: wp.vec3, e3: wp.vec3):
82
82
  return dmin, Coords(s31[2], 0.0, s31[1])
83
83
  else:
84
84
  return dmin, s123
85
+
86
+
87
+ @wp.func
88
+ def project_on_box_at_origin(coords: wp.vec3, sizes: wp.vec3):
89
+ proj_coords = wp.min(wp.max(coords, wp.vec3(0.0)), sizes)
90
+ return wp.length_sq(coords - proj_coords), wp.cw_div(proj_coords, sizes)
91
+
92
+
93
+ @wp.func
94
+ def project_on_box_at_origin(coords: wp.vec2, sizes: wp.vec2):
95
+ proj_coords = wp.min(wp.max(coords, wp.vec2(0.0)), sizes)
96
+ norm_coords = wp.cw_div(proj_coords, sizes)
97
+ return wp.length_sq(coords - proj_coords), Coords(norm_coords[0], norm_coords[1], 0.0)