warp-lang 1.7.2rc1__py3-none-win_amd64.whl → 1.8.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 (181) 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 +125 -26
  8. warp/builtins.py +1907 -384
  9. warp/codegen.py +257 -101
  10. warp/config.py +12 -1
  11. warp/constants.py +1 -1
  12. warp/context.py +657 -223
  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_ffi_callback.py +2 -2
  36. warp/examples/optim/example_drone.py +1 -1
  37. warp/examples/sim/example_cloth.py +1 -1
  38. warp/examples/sim/example_cloth_self_contact.py +48 -54
  39. warp/examples/tile/example_tile_block_cholesky.py +502 -0
  40. warp/examples/tile/example_tile_cholesky.py +2 -1
  41. warp/examples/tile/example_tile_convolution.py +1 -1
  42. warp/examples/tile/example_tile_filtering.py +1 -1
  43. warp/examples/tile/example_tile_matmul.py +1 -1
  44. warp/examples/tile/example_tile_mlp.py +2 -0
  45. warp/fabric.py +7 -7
  46. warp/fem/__init__.py +5 -0
  47. warp/fem/adaptivity.py +1 -1
  48. warp/fem/cache.py +152 -63
  49. warp/fem/dirichlet.py +2 -2
  50. warp/fem/domain.py +136 -6
  51. warp/fem/field/field.py +141 -99
  52. warp/fem/field/nodal_field.py +85 -39
  53. warp/fem/field/virtual.py +97 -52
  54. warp/fem/geometry/adaptive_nanogrid.py +91 -86
  55. warp/fem/geometry/closest_point.py +13 -0
  56. warp/fem/geometry/deformed_geometry.py +102 -40
  57. warp/fem/geometry/element.py +56 -2
  58. warp/fem/geometry/geometry.py +323 -22
  59. warp/fem/geometry/grid_2d.py +157 -62
  60. warp/fem/geometry/grid_3d.py +116 -20
  61. warp/fem/geometry/hexmesh.py +86 -20
  62. warp/fem/geometry/nanogrid.py +166 -86
  63. warp/fem/geometry/partition.py +59 -25
  64. warp/fem/geometry/quadmesh.py +86 -135
  65. warp/fem/geometry/tetmesh.py +47 -119
  66. warp/fem/geometry/trimesh.py +77 -270
  67. warp/fem/integrate.py +107 -52
  68. warp/fem/linalg.py +25 -58
  69. warp/fem/operator.py +124 -27
  70. warp/fem/quadrature/pic_quadrature.py +36 -14
  71. warp/fem/quadrature/quadrature.py +40 -16
  72. warp/fem/space/__init__.py +1 -1
  73. warp/fem/space/basis_function_space.py +66 -46
  74. warp/fem/space/basis_space.py +17 -4
  75. warp/fem/space/dof_mapper.py +1 -1
  76. warp/fem/space/function_space.py +2 -2
  77. warp/fem/space/grid_2d_function_space.py +4 -1
  78. warp/fem/space/hexmesh_function_space.py +4 -2
  79. warp/fem/space/nanogrid_function_space.py +3 -1
  80. warp/fem/space/partition.py +11 -2
  81. warp/fem/space/quadmesh_function_space.py +4 -1
  82. warp/fem/space/restriction.py +5 -2
  83. warp/fem/space/shape/__init__.py +10 -8
  84. warp/fem/space/tetmesh_function_space.py +4 -1
  85. warp/fem/space/topology.py +52 -21
  86. warp/fem/space/trimesh_function_space.py +4 -1
  87. warp/fem/utils.py +53 -8
  88. warp/jax.py +1 -2
  89. warp/jax_experimental/ffi.py +12 -17
  90. warp/jax_experimental/xla_ffi.py +37 -24
  91. warp/math.py +171 -1
  92. warp/native/array.h +99 -0
  93. warp/native/builtin.h +174 -31
  94. warp/native/coloring.cpp +1 -1
  95. warp/native/exports.h +118 -63
  96. warp/native/intersect.h +3 -3
  97. warp/native/mat.h +5 -10
  98. warp/native/mathdx.cpp +11 -5
  99. warp/native/matnn.h +1 -123
  100. warp/native/quat.h +28 -4
  101. warp/native/sparse.cpp +121 -258
  102. warp/native/sparse.cu +181 -274
  103. warp/native/spatial.h +305 -17
  104. warp/native/tile.h +583 -72
  105. warp/native/tile_radix_sort.h +1108 -0
  106. warp/native/tile_reduce.h +237 -2
  107. warp/native/tile_scan.h +240 -0
  108. warp/native/tuple.h +189 -0
  109. warp/native/vec.h +6 -16
  110. warp/native/warp.cpp +36 -4
  111. warp/native/warp.cu +574 -51
  112. warp/native/warp.h +47 -74
  113. warp/optim/linear.py +5 -1
  114. warp/paddle.py +7 -8
  115. warp/py.typed +0 -0
  116. warp/render/render_opengl.py +58 -29
  117. warp/render/render_usd.py +124 -61
  118. warp/sim/__init__.py +9 -0
  119. warp/sim/collide.py +252 -78
  120. warp/sim/graph_coloring.py +8 -1
  121. warp/sim/import_mjcf.py +4 -3
  122. warp/sim/import_usd.py +11 -7
  123. warp/sim/integrator.py +5 -2
  124. warp/sim/integrator_euler.py +1 -1
  125. warp/sim/integrator_featherstone.py +1 -1
  126. warp/sim/integrator_vbd.py +751 -320
  127. warp/sim/integrator_xpbd.py +1 -1
  128. warp/sim/model.py +265 -260
  129. warp/sim/utils.py +10 -7
  130. warp/sparse.py +303 -166
  131. warp/tape.py +52 -51
  132. warp/tests/cuda/test_conditional_captures.py +1046 -0
  133. warp/tests/cuda/test_streams.py +1 -1
  134. warp/tests/geometry/test_volume.py +2 -2
  135. warp/tests/interop/test_dlpack.py +9 -9
  136. warp/tests/interop/test_jax.py +0 -1
  137. warp/tests/run_coverage_serial.py +1 -1
  138. warp/tests/sim/disabled_kinematics.py +2 -2
  139. warp/tests/sim/{test_vbd.py → test_cloth.py} +296 -113
  140. warp/tests/sim/test_collision.py +159 -51
  141. warp/tests/sim/test_coloring.py +15 -1
  142. warp/tests/test_array.py +254 -2
  143. warp/tests/test_array_reduce.py +2 -2
  144. warp/tests/test_atomic_cas.py +299 -0
  145. warp/tests/test_codegen.py +142 -19
  146. warp/tests/test_conditional.py +47 -1
  147. warp/tests/test_ctypes.py +0 -20
  148. warp/tests/test_devices.py +8 -0
  149. warp/tests/test_fabricarray.py +4 -2
  150. warp/tests/test_fem.py +58 -25
  151. warp/tests/test_func.py +42 -1
  152. warp/tests/test_grad.py +1 -1
  153. warp/tests/test_lerp.py +1 -3
  154. warp/tests/test_map.py +481 -0
  155. warp/tests/test_mat.py +1 -24
  156. warp/tests/test_quat.py +6 -15
  157. warp/tests/test_rounding.py +10 -38
  158. warp/tests/test_runlength_encode.py +7 -7
  159. warp/tests/test_smoothstep.py +1 -1
  160. warp/tests/test_sparse.py +51 -2
  161. warp/tests/test_spatial.py +507 -1
  162. warp/tests/test_struct.py +2 -2
  163. warp/tests/test_tuple.py +265 -0
  164. warp/tests/test_types.py +2 -2
  165. warp/tests/test_utils.py +24 -18
  166. warp/tests/tile/test_tile.py +420 -1
  167. warp/tests/tile/test_tile_mathdx.py +518 -14
  168. warp/tests/tile/test_tile_reduce.py +213 -0
  169. warp/tests/tile/test_tile_shared_memory.py +130 -1
  170. warp/tests/tile/test_tile_sort.py +117 -0
  171. warp/tests/unittest_suites.py +4 -6
  172. warp/types.py +462 -308
  173. warp/utils.py +647 -86
  174. {warp_lang-1.7.2rc1.dist-info → warp_lang-1.8.0.dist-info}/METADATA +20 -6
  175. {warp_lang-1.7.2rc1.dist-info → warp_lang-1.8.0.dist-info}/RECORD +178 -166
  176. warp/stubs.py +0 -3381
  177. warp/tests/sim/test_xpbd.py +0 -399
  178. warp/tests/test_mlp.py +0 -282
  179. {warp_lang-1.7.2rc1.dist-info → warp_lang-1.8.0.dist-info}/WHEEL +0 -0
  180. {warp_lang-1.7.2rc1.dist-info → warp_lang-1.8.0.dist-info}/licenses/LICENSE.md +0 -0
  181. {warp_lang-1.7.2rc1.dist-info → warp_lang-1.8.0.dist-info}/top_level.txt +0 -0
@@ -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)
@@ -13,9 +13,12 @@
13
13
  # See the License for the specific language governing permissions and
14
14
  # limitations under the License.
15
15
 
16
+ from typing import ClassVar
17
+
16
18
  import warp as wp
17
19
  from warp.fem import cache
18
- from warp.fem.types import Coords, ElementIndex, Sample
20
+ from warp.fem.polynomial import Polynomial
21
+ from warp.fem.types import Coords, ElementIndex, Sample, make_free_sample
19
22
 
20
23
  from .geometry import Geometry
21
24
 
@@ -23,7 +26,31 @@ _mat32 = wp.mat(shape=(3, 2), dtype=float)
23
26
 
24
27
 
25
28
  class DeformedGeometry(Geometry):
26
- def __init__(self, field: "wp.fem.field.GeometryField", relative: bool = True):
29
+ _dynamic_attribute_constructors_phase_1: ClassVar = {
30
+ "CellArg": lambda obj: obj._make_cell_arg(),
31
+ "SideArg": lambda obj: obj._make_side_arg(),
32
+ "cell_position": lambda obj: obj._make_cell_position(),
33
+ "cell_deformation_gradient": lambda obj: obj._make_cell_deformation_gradient(),
34
+ "side_to_cell_arg": lambda obj: obj._make_side_to_cell_arg(),
35
+ "side_position": lambda obj: obj._make_side_position(),
36
+ "side_deformation_gradient": lambda obj: obj._make_side_deformation_gradient(),
37
+ "side_inner_cell_index": lambda obj: obj._make_side_inner_cell_index(),
38
+ "side_outer_cell_index": lambda obj: obj._make_side_outer_cell_index(),
39
+ "side_inner_cell_coords": lambda obj: obj._make_side_inner_cell_coords(),
40
+ "side_outer_cell_coords": lambda obj: obj._make_side_outer_cell_coords(),
41
+ "side_from_cell_coords": lambda obj: obj._make_side_from_cell_coords(),
42
+ "cell_bvh_id": lambda obj: obj._make_cell_bvh_id(),
43
+ "cell_bounds": lambda obj: obj._make_cell_bounds(),
44
+ }
45
+
46
+ _dynamic_attribute_constructors_phase_2: ClassVar = {
47
+ "cell_closest_point": lambda obj: obj._make_cell_closest_point(),
48
+ "side_closest_point": lambda obj: obj._make_side_closest_point(),
49
+ "cell_coordinates": lambda obj: obj._make_cell_coordinates(),
50
+ "side_coordinates": lambda obj: obj._make_side_coordinates(),
51
+ }
52
+
53
+ def __init__(self, field: "wp.fem.field.GeometryField", relative: bool = True, build_bvh: bool = False):
27
54
  """Constructs a Deformed Geometry from a displacement or absolute position field defined over a base geometry.
28
55
  The deformation field does not need to be isoparameteric.
29
56
 
@@ -33,10 +60,7 @@ class DeformedGeometry(Geometry):
33
60
  from warp.fem.field import DiscreteField, GeometryField
34
61
 
35
62
  if isinstance(field, DiscreteField):
36
- if (
37
- not wp.types.type_is_vector(field.dtype)
38
- or wp.types.type_length(field.dtype) != field.geometry.dimension
39
- ):
63
+ if not wp.types.type_is_vector(field.dtype) or wp.types.type_size(field.dtype) != field.geometry.dimension:
40
64
  raise ValueError(
41
65
  "Invalid value type for position field, must be vector-valued with same dimension as underlying geometry"
42
66
  )
@@ -46,14 +70,10 @@ class DeformedGeometry(Geometry):
46
70
  self._relative = relative
47
71
 
48
72
  self.field: GeometryField = field
73
+ self.field_trace = field.trace()
49
74
  self.dimension = self.base.dimension
50
75
 
51
- self.CellArg = self.field.ElementEvalArg
52
-
53
- self.field_trace = field.trace()
54
- self.SideArg = self._make_side_arg()
55
76
  self.SideIndexArg = self.base.SideIndexArg
56
-
57
77
  self.cell_count = self.base.cell_count
58
78
  self.vertex_count = self.base.vertex_count
59
79
  self.side_count = self.base.side_count
@@ -62,23 +82,18 @@ class DeformedGeometry(Geometry):
62
82
  self.reference_side = self.base.reference_side
63
83
 
64
84
  self.side_index_arg_value = self.base.side_index_arg_value
65
-
66
- self.cell_position = self._make_cell_position()
67
- self.cell_deformation_gradient = self._make_cell_deformation_gradient()
68
-
85
+ self.fill_side_index_arg = self.base.fill_side_index_arg
69
86
  self.boundary_side_index = self.base.boundary_side_index
70
87
 
71
- self.side_to_cell_arg = self._make_side_to_cell_arg()
72
- self.side_position = self._make_side_position()
73
- self.side_deformation_gradient = self._make_side_deformation_gradient()
74
- self.side_inner_cell_index = self._make_side_inner_cell_index()
75
- self.side_outer_cell_index = self._make_side_outer_cell_index()
76
- self.side_inner_cell_coords = self._make_side_inner_cell_coords()
77
- self.side_outer_cell_coords = self._make_side_outer_cell_coords()
78
- self.side_from_cell_coords = self._make_side_from_cell_coords()
88
+ cache.setup_dynamic_attributes(self, constructors=self._dynamic_attribute_constructors_phase_1)
79
89
 
80
90
  self._make_default_dependent_implementations()
81
91
 
92
+ cache.setup_dynamic_attributes(self, constructors=self._dynamic_attribute_constructors_phase_2)
93
+
94
+ if build_bvh:
95
+ self.build_bvh(self.field.dof_values.device)
96
+
82
97
  @property
83
98
  def name(self) -> str:
84
99
  return f"DefGeo_{self.field.name}_{'rel' if self._relative else 'abs'}"
@@ -89,35 +104,49 @@ class DeformedGeometry(Geometry):
89
104
 
90
105
  # Geometry device interface
91
106
 
92
- @cache.cached_arg_value
93
- def cell_arg_value(self, device) -> "DeformedGeometry.CellArg":
94
- args = self.CellArg()
107
+ def _make_cell_arg(self):
108
+ @cache.dynamic_struct(suffix=self.name)
109
+ class CellArg:
110
+ base_arg: self.base.CellArg
111
+ field_arg: self.field.EvalArg
112
+ cell_bvh: wp.uint64
95
113
 
96
- args.elt_arg = self.base.cell_arg_value(device)
97
- args.eval_arg = self.field.eval_arg_value(device)
114
+ return CellArg
98
115
 
116
+ def cell_arg_value(self, device) -> "DeformedGeometry.CellArg":
117
+ args = self.CellArg()
118
+ self.fill_cell_arg(args, device)
99
119
  return args
100
120
 
121
+ def fill_cell_arg(self, args: "DeformedGeometry.CellArg", device):
122
+ self.base.fill_cell_arg(args.base_arg, device)
123
+ self.field.fill_eval_arg(args.field_arg, device)
124
+ args.cell_bvh = self.bvh_id(device)
125
+
101
126
  def _make_cell_position(self):
102
127
  @cache.dynamic_func(suffix=self.name)
103
128
  def cell_position_absolute(cell_arg: self.CellArg, s: Sample):
104
- return self.field.eval_inner(cell_arg, s)
129
+ field_arg = self.field.ElementEvalArg(cell_arg.base_arg, cell_arg.field_arg)
130
+ return self.field.eval_inner(field_arg, s)
105
131
 
106
132
  @cache.dynamic_func(suffix=self.name)
107
133
  def cell_position(cell_arg: self.CellArg, s: Sample):
108
- return self.field.eval_inner(cell_arg, s) + self.base.cell_position(cell_arg.elt_arg, s)
134
+ field_arg = self.field.ElementEvalArg(cell_arg.base_arg, cell_arg.field_arg)
135
+ return self.field.eval_inner(field_arg, s) + self.base.cell_position(cell_arg.base_arg, s)
109
136
 
110
137
  return cell_position if self._relative else cell_position_absolute
111
138
 
112
139
  def _make_cell_deformation_gradient(self):
113
140
  @cache.dynamic_func(suffix=self.name)
114
141
  def cell_deformation_gradient_absolute(cell_arg: self.CellArg, s: Sample):
115
- return self.field.eval_reference_grad_inner(cell_arg, s)
142
+ field_arg = self.field.ElementEvalArg(cell_arg.base_arg, cell_arg.field_arg)
143
+ return self.field.eval_reference_grad_inner(field_arg, s)
116
144
 
117
145
  @cache.dynamic_func(suffix=self.name)
118
146
  def cell_deformation_gradient(cell_arg: self.CellArg, s: Sample):
119
- return self.field.eval_reference_grad_inner(cell_arg, s) + self.base.cell_deformation_gradient(
120
- cell_arg.elt_arg, s
147
+ field_arg = self.field.ElementEvalArg(cell_arg.base_arg, cell_arg.field_arg)
148
+ return self.field.eval_reference_grad_inner(field_arg, s) + self.base.cell_deformation_gradient(
149
+ cell_arg.base_arg, s
121
150
  )
122
151
 
123
152
  return cell_deformation_gradient if self._relative else cell_deformation_gradient_absolute
@@ -128,19 +157,21 @@ class DeformedGeometry(Geometry):
128
157
  base_arg: self.base.SideArg
129
158
  trace_arg: self.field_trace.EvalArg
130
159
  field_arg: self.field.EvalArg
160
+ cell_bvh: wp.uint64
131
161
 
132
162
  return SideArg
133
163
 
134
- @cache.cached_arg_value
135
164
  def side_arg_value(self, device) -> "DeformedGeometry.SideArg":
136
165
  args = self.SideArg()
137
-
138
- args.base_arg = self.base.side_arg_value(device)
139
- args.field_arg = self.field.eval_arg_value(device)
140
- args.trace_arg = self.field_trace.eval_arg_value(device)
141
-
166
+ self.fill_side_arg(args, device)
142
167
  return args
143
168
 
169
+ def fill_side_arg(self, args: "DeformedGeometry.SideArg", device):
170
+ self.base.fill_side_arg(args.base_arg, device)
171
+ self.field.fill_eval_arg(args.field_arg, device)
172
+ self.field_trace.fill_eval_arg(args.trace_arg, device)
173
+ args.cell_bvh = self.bvh_id(device)
174
+
144
175
  def _make_side_position(self):
145
176
  @cache.dynamic_func(suffix=self.name)
146
177
  def side_position_absolute(args: self.SideArg, s: Sample):
@@ -216,6 +247,37 @@ class DeformedGeometry(Geometry):
216
247
  def _make_side_to_cell_arg(self):
217
248
  @cache.dynamic_func(suffix=self.name)
218
249
  def side_to_cell_arg(side_arg: self.SideArg):
219
- return self.CellArg(self.base.side_to_cell_arg(side_arg.base_arg), side_arg.field_arg)
250
+ return self.CellArg(self.base.side_to_cell_arg(side_arg.base_arg), side_arg.field_arg, side_arg.cell_bvh)
220
251
 
221
252
  return side_to_cell_arg
253
+
254
+ def _make_cell_bvh_id(self):
255
+ @cache.dynamic_func(suffix=self.name)
256
+ def cell_bvh_id(cell_arg: self.CellArg):
257
+ return cell_arg.cell_bvh
258
+
259
+ return cell_bvh_id
260
+
261
+ def _make_cell_bounds(self):
262
+ points, _weights = self.reference_cell().instantiate_quadrature(
263
+ order=self.field.degree, family=Polynomial.LOBATTO_GAUSS_LEGENDRE
264
+ )
265
+
266
+ points = cache.cached_mat_type((len(points), 3), dtype=float)(points)
267
+ point_count = len(points)
268
+
269
+ @cache.dynamic_func(suffix=self.name)
270
+ def cell_bounds(cell_arg: self.CellArg, cell_index: ElementIndex):
271
+ lower = wp.vec3(1.0e8)
272
+ upper = wp.vec3(-1.0e8)
273
+ for k in range(point_count):
274
+ pos = self.cell_position(cell_arg, make_free_sample(cell_index, points[k]))
275
+ lower = wp.min(lower, pos)
276
+ upper = wp.max(upper, pos)
277
+
278
+ # pad the BBox to account for potential overflows
279
+ pad = 0.25 * (upper - lower)
280
+
281
+ return lower - pad, upper + pad
282
+
283
+ return cell_bounds
@@ -15,14 +15,18 @@
15
15
 
16
16
  from typing import List, Tuple
17
17
 
18
+ import warp as wp
18
19
  from warp.fem.polynomial import Polynomial, quadrature_1d
19
20
  from warp.fem.types import Coords
20
21
 
22
+ _vec1 = wp.types.vector(length=1, dtype=float)
23
+
21
24
 
22
25
  class Element:
23
26
  dimension = 0
24
27
  """Intrinsic dimension of the element"""
25
28
 
29
+ @staticmethod
26
30
  def measure() -> float:
27
31
  """Measure (area, volume, ...) of the reference element"""
28
32
  raise NotImplementedError
@@ -32,10 +36,32 @@ class Element:
32
36
  """Returns a quadrature of a given order for a prototypical element"""
33
37
  raise NotImplementedError
34
38
 
35
- def center(self) -> Tuple[float]:
36
- coords, _ = self.instantiate_quadrature(order=0, family=None)
39
+ @classmethod
40
+ def center(cls) -> Coords:
41
+ """Returns the coordinates for the center of the element"""
42
+ coords, _ = cls.instantiate_quadrature(order=0, family=None)
37
43
  return coords[0]
38
44
 
45
+ @wp.func
46
+ def project(v: Coords):
47
+ """project coordinates so that they belong to the element"""
48
+ return wp.min(wp.max(v, Coords(0.0)), Coords(1.0))
49
+
50
+ @wp.func
51
+ def coord_delta(ref_delta: wp.vec3):
52
+ """Transform a delta in reference space to element coords"""
53
+ return ref_delta
54
+
55
+ @wp.func
56
+ def coord_delta(ref_delta: wp.vec2):
57
+ """Transform a delta in reference space to element coords"""
58
+ return Coords(ref_delta[0], ref_delta[1], 0.0)
59
+
60
+ @wp.func
61
+ def coord_delta(ref_delta: _vec1):
62
+ """Transform a delta in reference space to element coords"""
63
+ return Coords(ref_delta[0], 0.0, 0.0)
64
+
39
65
 
40
66
  def _point_count_from_order(order: int, family: Polynomial):
41
67
  if family == Polynomial.GAUSS_LEGENDRE:
@@ -454,6 +480,17 @@ class Triangle(Element):
454
480
 
455
481
  return coords, weights
456
482
 
483
+ @wp.func
484
+ def project(v: Coords):
485
+ n = wp.max(0.0, (v[0] + v[1] - 1.0) * 0.5)
486
+ a = wp.clamp(v[0] - n, 0.0, 1.0)
487
+ b = wp.clamp(v[1] - n, 0.0, 1.0)
488
+ return Coords(a, b, 1.0 - (a + b))
489
+
490
+ @wp.func
491
+ def coord_delta(ref_delta: wp.vec2):
492
+ return Coords(-ref_delta[0] - ref_delta[1], ref_delta[0], ref_delta[1])
493
+
457
494
 
458
495
  class Tetrahedron(Element):
459
496
  dimension = 3
@@ -774,3 +811,20 @@ class Tetrahedron(Element):
774
811
  raise NotImplementedError
775
812
 
776
813
  return coords, weights
814
+
815
+ @wp.func
816
+ def project(v: Coords):
817
+ # project on 1-2-3 half-space
818
+ n = wp.max(0.0, (v[0] + v[1] + v[2] - 1.0) / 3.0)
819
+ c = v - Coords(n)
820
+
821
+ # project on 1-2, 2-3, 3-1 half-spaces
822
+ n = wp.max(0.0, (c[0] + c[1] - 1.0) * 0.5)
823
+ c = c - Coords(n, n, 0.0)
824
+ n = wp.max(0.0, (c[1] + c[2] - 1.0) * 0.5)
825
+ c = c - Coords(0.0, n, n)
826
+ n = wp.max(0.0, (c[2] + c[0] - 1.0) * 0.5)
827
+ c = c - Coords(n, 0.0, n)
828
+
829
+ # project on cube
830
+ return Element.project(c)