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/sim/collide.py CHANGED
@@ -232,11 +232,11 @@ def box_sdf_grad(upper: wp.vec3, p: wp.vec3):
232
232
  sz = wp.sign(p[2])
233
233
 
234
234
  # x projection
235
- if qx > qy and qx > qz or qy == 0.0 and qz == 0.0:
235
+ if (qx > qy and qx > qz) or (qy == 0.0 and qz == 0.0):
236
236
  return wp.vec3(sx, 0.0, 0.0)
237
237
 
238
238
  # y projection
239
- if qy > qx and qy > qz or qx == 0.0 and qz == 0.0:
239
+ if (qy > qx and qy > qz) or (qx == 0.0 and qz == 0.0):
240
240
  return wp.vec3(0.0, sy, 0.0)
241
241
 
242
242
  # z projection
@@ -341,9 +341,9 @@ def closest_point_box(upper: wp.vec3, point: wp.vec3):
341
341
  sy = wp.abs(wp.abs(point[1]) - upper[1])
342
342
  sz = wp.abs(wp.abs(point[2]) - upper[2])
343
343
  # return closest point on closest side, handle corner cases
344
- if sx < sy and sx < sz or sy == 0.0 and sz == 0.0:
344
+ if (sx < sy and sx < sz) or (sy == 0.0 and sz == 0.0):
345
345
  x = wp.sign(point[0]) * upper[0]
346
- elif sy < sx and sy < sz or sx == 0.0 and sz == 0.0:
346
+ elif (sy < sx and sy < sz) or (sx == 0.0 and sz == 0.0):
347
347
  y = wp.sign(point[1]) * upper[1]
348
348
  else:
349
349
  z = wp.sign(point[2]) * upper[2]
@@ -1236,8 +1236,7 @@ def handle_contact_pairs(
1236
1236
  p_b_body = closest_point_box(geo_scale_b, query_b)
1237
1237
  p_b_world = wp.transform_point(X_ws_b, p_b_body)
1238
1238
  diff = p_a_world - p_b_world
1239
- # use center of box A to query normal to make sure we are not inside B
1240
- query_b = wp.transform_point(X_sw_b, wp.transform_get_translation(X_ws_a))
1239
+
1241
1240
  normal = wp.transform_vector(X_ws_b, box_sdf_grad(geo_scale_b, query_b))
1242
1241
  distance = wp.dot(diff, normal)
1243
1242
 
@@ -1516,11 +1515,8 @@ def handle_contact_pairs(
1516
1515
  diff = p_a_world - p_b_world
1517
1516
 
1518
1517
  # if the plane is infinite or the point is within the plane we fix the normal to prevent intersections
1519
- if (
1520
- geo_scale_b[0] == 0.0
1521
- and geo_scale_b[1] == 0.0
1522
- or wp.abs(query_b[0]) < geo_scale_b[0]
1523
- and wp.abs(query_b[2]) < geo_scale_b[1]
1518
+ if (geo_scale_b[0] == 0.0 and geo_scale_b[1] == 0.0) or (
1519
+ wp.abs(query_b[0]) < geo_scale_b[0] and wp.abs(query_b[2]) < geo_scale_b[1]
1524
1520
  ):
1525
1521
  normal = wp.transform_vector(X_ws_b, wp.vec3(0.0, 1.0, 0.0))
1526
1522
  distance = wp.dot(diff, normal)
@@ -1618,7 +1614,7 @@ def collide(
1618
1614
  device=model.device,
1619
1615
  )
1620
1616
 
1621
- if model.shape_contact_pair_count or model.ground and model.shape_ground_contact_pair_count:
1617
+ if model.shape_contact_pair_count or (model.ground and model.shape_ground_contact_pair_count):
1622
1618
  # clear old count
1623
1619
  model.rigid_contact_count.zero_()
1624
1620
 
@@ -1683,7 +1679,7 @@ def collide(
1683
1679
  record_tape=False,
1684
1680
  )
1685
1681
 
1686
- if model.shape_contact_pair_count or model.ground and model.shape_ground_contact_pair_count:
1682
+ if model.shape_contact_pair_count or (model.ground and model.shape_ground_contact_pair_count):
1687
1683
  if requires_grad:
1688
1684
  model.rigid_contact_point0 = wp.empty_like(model.rigid_contact_point0)
1689
1685
  model.rigid_contact_point1 = wp.empty_like(model.rigid_contact_point1)
@@ -1850,7 +1846,7 @@ def vertex_triangle_collision_detection_kernel(
1850
1846
  """
1851
1847
  This function applies discrete collision detection between vertices and triangles. It uses pre-allocated spaces to
1852
1848
  record the collision data. This collision detector works both ways, i.e., it records vertices' colliding triangles to
1853
- vertex_colliding_triangles, and records each triangles colliding vertices to triangle_colliding_vertices.
1849
+ `vertex_colliding_triangles`, and records each triangles colliding vertices to `triangle_colliding_vertices`.
1854
1850
 
1855
1851
  This function assumes that all the vertices are on triangles, and can be indexed from the pos argument.
1856
1852
 
@@ -1870,7 +1866,8 @@ def vertex_triangle_collision_detection_kernel(
1870
1866
  vertex_colliding_triangles_offsets (array): where each vertex' collision buffer starts
1871
1867
  vertex_colliding_triangles_buffer_sizes (array): size of each vertex' collision buffer, will be modified if resizing is needed
1872
1868
  vertex_colliding_triangles_min_dist (array): each vertex' min distance to all (non-neighbor) triangles
1873
- triangle_colliding_vertices (array): positions of all the triangles' collision vertices
1869
+ triangle_colliding_vertices (array): positions of all the triangles' collision vertices, every two elements
1870
+ records the vertex index and a triangle index it collides to
1874
1871
  triangle_colliding_vertices_count (array): number of triangles each vertex collides
1875
1872
  triangle_colliding_vertices_offsets (array): where each triangle's collision buffer starts
1876
1873
  triangle_colliding_vertices_buffer_sizes (array): size of each triangle's collision buffer, will be modified if resizing is needed
@@ -1880,6 +1877,8 @@ def vertex_triangle_collision_detection_kernel(
1880
1877
 
1881
1878
  v_index = wp.tid()
1882
1879
  v = pos[v_index]
1880
+ vertex_buffer_offset = vertex_colliding_triangles_offsets[v_index]
1881
+ vertex_buffer_size = vertex_colliding_triangles_offsets[v_index + 1] - vertex_buffer_offset
1883
1882
 
1884
1883
  lower = wp.vec3(v[0] - query_radius, v[1] - query_radius, v[2] - query_radius)
1885
1884
  upper = wp.vec3(v[0] + query_radius, v[1] + query_radius, v[2] + query_radius)
@@ -1905,13 +1904,11 @@ def vertex_triangle_collision_detection_kernel(
1905
1904
  dist = wp.length(closest_p - v)
1906
1905
 
1907
1906
  if dist < query_radius:
1908
- vertex_buffer_offset = vertex_colliding_triangles_offsets[v_index]
1909
- vertex_buffer_size = vertex_colliding_triangles_offsets[v_index + 1] - vertex_buffer_offset
1910
-
1911
1907
  # record v-f collision to vertex
1912
1908
  min_dis_to_tris = wp.min(min_dis_to_tris, dist)
1913
1909
  if vertex_num_collisions < vertex_buffer_size:
1914
- vertex_colliding_triangles[vertex_buffer_offset + vertex_num_collisions] = tri_index
1910
+ vertex_colliding_triangles[2 * (vertex_buffer_offset + vertex_num_collisions)] = v_index
1911
+ vertex_colliding_triangles[2 * (vertex_buffer_offset + vertex_num_collisions) + 1] = tri_index
1915
1912
  else:
1916
1913
  resize_flags[VERTEX_COLLISION_BUFFER_OVERFLOW_INDEX] = 1
1917
1914
 
@@ -1932,6 +1929,94 @@ def vertex_triangle_collision_detection_kernel(
1932
1929
  vertex_colliding_triangles_min_dist[v_index] = min_dis_to_tris
1933
1930
 
1934
1931
 
1932
+ @wp.kernel
1933
+ def vertex_triangle_collision_detection_no_triangle_buffers_kernel(
1934
+ query_radius: float,
1935
+ bvh_id: wp.uint64,
1936
+ pos: wp.array(dtype=wp.vec3),
1937
+ tri_indices: wp.array(dtype=wp.int32, ndim=2),
1938
+ vertex_colliding_triangles_offsets: wp.array(dtype=wp.int32),
1939
+ vertex_colliding_triangles_buffer_sizes: wp.array(dtype=wp.int32),
1940
+ # outputs
1941
+ vertex_colliding_triangles: wp.array(dtype=wp.int32),
1942
+ vertex_colliding_triangles_count: wp.array(dtype=wp.int32),
1943
+ vertex_colliding_triangles_min_dist: wp.array(dtype=float),
1944
+ triangle_colliding_vertices_min_dist: wp.array(dtype=float),
1945
+ resize_flags: wp.array(dtype=wp.int32),
1946
+ ):
1947
+ """
1948
+ This function applies discrete collision detection between vertices and triangles. It uses pre-allocated spaces to
1949
+ record the collision data. Unlike `vertex_triangle_collision_detection_kernel`, this collision detection kernel
1950
+ works only in one way, i.e., it only records vertices' colliding triangles to `vertex_colliding_triangles`.
1951
+
1952
+ This function assumes that all the vertices are on triangles, and can be indexed from the pos argument.
1953
+
1954
+ Note:
1955
+
1956
+ The collision date buffer is pre-allocated and cannot be changed during collision detection, therefore, the space
1957
+ may not be enough. If the space is not enough to record all the collision information, the function will set a
1958
+ certain element in resized_flag to be true. The user can reallocate the buffer based on vertex_colliding_triangles_count
1959
+ and vertex_colliding_triangles_count.
1960
+
1961
+ Attributes:
1962
+ bvh_id (int): the bvh id you want to collide with
1963
+ query_radius (float): the contact radius. vertex-triangle pairs whose distance are less than this will get detected
1964
+ pos (array): positions of all the vertices that make up triangles
1965
+ vertex_colliding_triangles (array): flattened buffer of vertices' collision triangles, every two elements records
1966
+ the vertex index and a triangle index it collides to
1967
+ vertex_colliding_triangles_count (array): number of triangles each vertex collides
1968
+ vertex_colliding_triangles_offsets (array): where each vertex' collision buffer starts
1969
+ vertex_colliding_triangles_buffer_sizes (array): size of each vertex' collision buffer, will be modified if resizing is needed
1970
+ vertex_colliding_triangles_min_dist (array): each vertex' min distance to all (non-neighbor) triangles
1971
+ triangle_colliding_vertices_min_dist (array): each triangle's min distance to all (non-self) vertices
1972
+ resized_flag (array): size == 3, (vertex_buffer_resize_required, triangle_buffer_resize_required, edge_buffer_resize_required)
1973
+ """
1974
+
1975
+ v_index = wp.tid()
1976
+ v = pos[v_index]
1977
+ vertex_buffer_offset = vertex_colliding_triangles_offsets[v_index]
1978
+ vertex_buffer_size = vertex_colliding_triangles_offsets[v_index + 1] - vertex_buffer_offset
1979
+
1980
+ lower = wp.vec3(v[0] - query_radius, v[1] - query_radius, v[2] - query_radius)
1981
+ upper = wp.vec3(v[0] + query_radius, v[1] + query_radius, v[2] + query_radius)
1982
+
1983
+ query = wp.bvh_query_aabb(bvh_id, lower, upper)
1984
+
1985
+ tri_index = wp.int32(0)
1986
+ vertex_num_collisions = wp.int32(0)
1987
+ min_dis_to_tris = query_radius
1988
+ while wp.bvh_query_next(query, tri_index):
1989
+ t1 = tri_indices[tri_index, 0]
1990
+ t2 = tri_indices[tri_index, 1]
1991
+ t3 = tri_indices[tri_index, 2]
1992
+ if vertex_adjacent_to_triangle(v_index, t1, t2, t3):
1993
+ continue
1994
+
1995
+ u1 = pos[t1]
1996
+ u2 = pos[t2]
1997
+ u3 = pos[t3]
1998
+
1999
+ closest_p, bary, feature_type = triangle_closest_point(u1, u2, u3, v)
2000
+
2001
+ dist = wp.length(closest_p - v)
2002
+
2003
+ if dist < query_radius:
2004
+ # record v-f collision to vertex
2005
+ min_dis_to_tris = wp.min(min_dis_to_tris, dist)
2006
+ if vertex_num_collisions < vertex_buffer_size:
2007
+ vertex_colliding_triangles[2 * (vertex_buffer_offset + vertex_num_collisions)] = v_index
2008
+ vertex_colliding_triangles[2 * (vertex_buffer_offset + vertex_num_collisions) + 1] = tri_index
2009
+ else:
2010
+ resize_flags[VERTEX_COLLISION_BUFFER_OVERFLOW_INDEX] = 1
2011
+
2012
+ vertex_num_collisions = vertex_num_collisions + 1
2013
+
2014
+ wp.atomic_min(triangle_colliding_vertices_min_dist, tri_index, dist)
2015
+
2016
+ vertex_colliding_triangles_count[v_index] = vertex_num_collisions
2017
+ vertex_colliding_triangles_min_dist[v_index] = min_dis_to_tris
2018
+
2019
+
1935
2020
  @wp.kernel
1936
2021
  def edge_colliding_edges_detection_kernel(
1937
2022
  query_radius: float,
@@ -2001,7 +2086,8 @@ def edge_colliding_edges_detection_kernel(
2001
2086
  # record e-e collision to e0, and leave e1; e1 will detect this collision from its own thread
2002
2087
  min_dis_to_edges = wp.min(min_dis_to_edges, dist)
2003
2088
  if edge_num_collisions < edge_buffer_size:
2004
- edge_colliding_edges[edge_buffer_offset + edge_num_collisions] = colliding_edge_index
2089
+ edge_colliding_edges[2 * (edge_buffer_offset + edge_num_collisions)] = e_index
2090
+ edge_colliding_edges[2 * (edge_buffer_offset + edge_num_collisions) + 1] = colliding_edge_index
2005
2091
  else:
2006
2092
  resize_flags[EDGE_COLLISION_BUFFER_OVERFLOW_INDEX] = 1
2007
2093
 
@@ -2068,6 +2154,9 @@ def triangle_triangle_collision_detection_kernel(
2068
2154
 
2069
2155
  @wp.struct
2070
2156
  class TriMeshCollisionInfo:
2157
+ vertex_indices: wp.array(dtype=wp.int32)
2158
+ # size: 2 x sum(vertex_colliding_triangles_buffer_sizes)
2159
+ # every two elements records the vertex index and a triangle index it collides to
2071
2160
  vertex_colliding_triangles: wp.array(dtype=wp.int32)
2072
2161
  vertex_colliding_triangles_offsets: wp.array(dtype=wp.int32)
2073
2162
  vertex_colliding_triangles_buffer_sizes: wp.array(dtype=wp.int32)
@@ -2080,6 +2169,8 @@ class TriMeshCollisionInfo:
2080
2169
  triangle_colliding_vertices_count: wp.array(dtype=wp.int32)
2081
2170
  triangle_colliding_vertices_min_dist: wp.array(dtype=float)
2082
2171
 
2172
+ # size: 2 x sum(edge_colliding_edges_buffer_sizes)
2173
+ # every two elements records the edge index and an edge index it collides to
2083
2174
  edge_colliding_edges: wp.array(dtype=wp.int32)
2084
2175
  edge_colliding_edges_offsets: wp.array(dtype=wp.int32)
2085
2176
  edge_colliding_edges_buffer_sizes: wp.array(dtype=wp.int32)
@@ -2095,7 +2186,13 @@ def get_vertex_colliding_triangles_count(col_info: TriMeshCollisionInfo, v: int)
2095
2186
  @wp.func
2096
2187
  def get_vertex_colliding_triangles(col_info: TriMeshCollisionInfo, v: int, i_collision: int):
2097
2188
  offset = col_info.vertex_colliding_triangles_offsets[v]
2098
- return col_info.vertex_colliding_triangles[offset + i_collision]
2189
+ return col_info.vertex_colliding_triangles[2 * (offset + i_collision) + 1]
2190
+
2191
+
2192
+ @wp.func
2193
+ def get_vertex_collision_buffer_vertex_index(col_info: TriMeshCollisionInfo, v: int, i_collision: int):
2194
+ offset = col_info.vertex_colliding_triangles_offsets[v]
2195
+ return col_info.vertex_colliding_triangles[2 * (offset + i_collision)]
2099
2196
 
2100
2197
 
2101
2198
  @wp.func
@@ -2119,13 +2216,20 @@ def get_edge_colliding_edges_count(col_info: TriMeshCollisionInfo, e: int):
2119
2216
  @wp.func
2120
2217
  def get_edge_colliding_edges(col_info: TriMeshCollisionInfo, e: int, i_collision: int):
2121
2218
  offset = col_info.edge_colliding_edges_offsets[e]
2122
- return col_info.edge_colliding_edges[offset + i_collision]
2219
+ return col_info.edge_colliding_edges[2 * (offset + i_collision) + 1]
2220
+
2221
+
2222
+ @wp.func
2223
+ def get_edge_collision_buffer_edge_index(col_info: TriMeshCollisionInfo, e: int, i_collision: int):
2224
+ offset = col_info.edge_colliding_edges_offsets[e]
2225
+ return col_info.edge_colliding_edges[2 * (offset + i_collision)]
2123
2226
 
2124
2227
 
2125
2228
  class TriMeshCollisionDetector:
2126
2229
  def __init__(
2127
2230
  self,
2128
2231
  model: Model,
2232
+ record_triangle_contacting_vertices=False,
2129
2233
  vertex_positions=None,
2130
2234
  vertex_collision_buffer_pre_alloc=8,
2131
2235
  vertex_collision_buffer_max_alloc=256,
@@ -2138,6 +2242,7 @@ class TriMeshCollisionDetector:
2138
2242
  edge_edge_parallel_epsilon=1e-5,
2139
2243
  ):
2140
2244
  self.model = model
2245
+ self.record_triangle_contacting_vertices = record_triangle_contacting_vertices
2141
2246
  self.vertex_positions = model.particle_q if vertex_positions is None else vertex_positions
2142
2247
  self.device = model.device
2143
2248
  self.vertex_collision_buffer_pre_alloc = vertex_collision_buffer_pre_alloc
@@ -2166,7 +2271,9 @@ class TriMeshCollisionDetector:
2166
2271
 
2167
2272
  # vertex collision buffers
2168
2273
  self.vertex_colliding_triangles = wp.zeros(
2169
- shape=(model.particle_count * self.vertex_collision_buffer_pre_alloc,), dtype=wp.int32, device=self.device
2274
+ shape=(2 * model.particle_count * self.vertex_collision_buffer_pre_alloc,),
2275
+ dtype=wp.int32,
2276
+ device=self.device,
2170
2277
  )
2171
2278
  self.vertex_colliding_triangles_count = wp.array(
2172
2279
  shape=(model.particle_count,), dtype=wp.int32, device=self.device
@@ -2187,26 +2294,39 @@ class TriMeshCollisionDetector:
2187
2294
  self.vertex_colliding_triangles_buffer_sizes, self.vertex_colliding_triangles_offsets
2188
2295
  )
2189
2296
 
2190
- # triangle collision buffers
2191
- self.triangle_colliding_vertices = wp.zeros(
2192
- shape=(model.tri_count * self.triangle_collision_buffer_pre_alloc,), dtype=wp.int32, device=self.device
2193
- )
2194
- self.triangle_colliding_vertices_count = wp.zeros(shape=(model.tri_count,), dtype=wp.int32, device=self.device)
2195
- self.triangle_colliding_vertices_buffer_sizes = wp.full(
2196
- shape=(model.tri_count,), value=self.triangle_collision_buffer_pre_alloc, dtype=wp.int32, device=self.device
2197
- )
2198
- self.triangle_colliding_vertices_min_dist = wp.array(shape=(model.tri_count,), dtype=float, device=self.device)
2297
+ if record_triangle_contacting_vertices:
2298
+ # triangle collision buffers
2299
+ self.triangle_colliding_vertices = wp.zeros(
2300
+ shape=(model.tri_count * self.triangle_collision_buffer_pre_alloc,), dtype=wp.int32, device=self.device
2301
+ )
2302
+ self.triangle_colliding_vertices_count = wp.zeros(
2303
+ shape=(model.tri_count,), dtype=wp.int32, device=self.device
2304
+ )
2305
+ self.triangle_colliding_vertices_buffer_sizes = wp.full(
2306
+ shape=(model.tri_count,),
2307
+ value=self.triangle_collision_buffer_pre_alloc,
2308
+ dtype=wp.int32,
2309
+ device=self.device,
2310
+ )
2199
2311
 
2200
- self.triangle_colliding_vertices_offsets = wp.array(
2201
- shape=(model.tri_count + 1,), dtype=wp.int32, device=self.device
2202
- )
2203
- self.compute_collision_buffer_offsets(
2204
- self.triangle_colliding_vertices_buffer_sizes, self.triangle_colliding_vertices_offsets
2205
- )
2312
+ self.triangle_colliding_vertices_offsets = wp.array(
2313
+ shape=(model.tri_count + 1,), dtype=wp.int32, device=self.device
2314
+ )
2315
+ self.compute_collision_buffer_offsets(
2316
+ self.triangle_colliding_vertices_buffer_sizes, self.triangle_colliding_vertices_offsets
2317
+ )
2318
+ else:
2319
+ self.triangle_colliding_vertices = None
2320
+ self.triangle_colliding_vertices_count = None
2321
+ self.triangle_colliding_vertices_buffer_sizes = None
2322
+ self.triangle_colliding_vertices_offsets = None
2323
+
2324
+ # this is need regardless of whether we record triangle contacting vertices
2325
+ self.triangle_colliding_vertices_min_dist = wp.array(shape=(model.tri_count,), dtype=float, device=self.device)
2206
2326
 
2207
2327
  # edge collision buffers
2208
2328
  self.edge_colliding_edges = wp.zeros(
2209
- shape=(model.edge_count * self.edge_collision_buffer_pre_alloc,), dtype=wp.int32, device=self.device
2329
+ shape=(2 * model.edge_count * self.edge_collision_buffer_pre_alloc,), dtype=wp.int32, device=self.device
2210
2330
  )
2211
2331
  self.edge_colliding_edges_count = wp.zeros(shape=(model.edge_count,), dtype=wp.int32, device=self.device)
2212
2332
  self.edge_colliding_edges_buffer_sizes = wp.full(
@@ -2248,10 +2368,12 @@ class TriMeshCollisionDetector:
2248
2368
  collision_info.vertex_colliding_triangles_count = self.vertex_colliding_triangles_count
2249
2369
  collision_info.vertex_colliding_triangles_min_dist = self.vertex_colliding_triangles_min_dist
2250
2370
 
2251
- collision_info.triangle_colliding_vertices = self.triangle_colliding_vertices
2252
- collision_info.triangle_colliding_vertices_offsets = self.triangle_colliding_vertices_offsets
2253
- collision_info.triangle_colliding_vertices_buffer_sizes = self.triangle_colliding_vertices_buffer_sizes
2254
- collision_info.triangle_colliding_vertices_count = self.triangle_colliding_vertices_count
2371
+ if self.record_triangle_contacting_vertices:
2372
+ collision_info.triangle_colliding_vertices = self.triangle_colliding_vertices
2373
+ collision_info.triangle_colliding_vertices_offsets = self.triangle_colliding_vertices_offsets
2374
+ collision_info.triangle_colliding_vertices_buffer_sizes = self.triangle_colliding_vertices_buffer_sizes
2375
+ collision_info.triangle_colliding_vertices_count = self.triangle_colliding_vertices_count
2376
+
2255
2377
  collision_info.triangle_colliding_vertices_min_dist = self.triangle_colliding_vertices_min_dist
2256
2378
 
2257
2379
  collision_info.edge_colliding_edges = self.edge_colliding_edges
@@ -2272,6 +2394,31 @@ class TriMeshCollisionDetector:
2272
2394
 
2273
2395
  offsets.assign(offsets_np)
2274
2396
 
2397
+ def rebuild(self, new_pos=None):
2398
+ if new_pos is not None:
2399
+ self.vertex_positions = new_pos
2400
+
2401
+ wp.launch(
2402
+ kernel=compute_tri_aabbs,
2403
+ inputs=[
2404
+ self.vertex_positions,
2405
+ self.model.tri_indices,
2406
+ ],
2407
+ outputs=[self.lower_bounds_tris, self.upper_bounds_tris],
2408
+ dim=self.model.tri_count,
2409
+ device=self.model.device,
2410
+ )
2411
+ self.bvh_tris = wp.Bvh(self.lower_bounds_tris, self.upper_bounds_tris)
2412
+
2413
+ wp.launch(
2414
+ kernel=compute_edge_aabbs,
2415
+ inputs=[self.vertex_positions, self.model.edge_indices],
2416
+ outputs=[self.lower_bounds_edges, self.upper_bounds_edges],
2417
+ dim=self.model.edge_count,
2418
+ device=self.model.device,
2419
+ )
2420
+ self.bvh_edges = wp.Bvh(self.lower_bounds_edges, self.upper_bounds_edges)
2421
+
2275
2422
  def refit(self, new_pos=None):
2276
2423
  if new_pos is not None:
2277
2424
  self.vertex_positions = new_pos
@@ -2298,46 +2445,72 @@ class TriMeshCollisionDetector:
2298
2445
  self.bvh_edges.refit()
2299
2446
 
2300
2447
  def vertex_triangle_collision_detection(self, query_radius):
2301
- wp.launch(
2302
- kernel=init_triangle_collision_data_kernel,
2303
- inputs=[
2304
- query_radius,
2305
- ],
2306
- outputs=[
2307
- self.triangle_colliding_vertices_count,
2308
- self.triangle_colliding_vertices_min_dist,
2309
- self.resize_flags,
2310
- ],
2311
- dim=self.model.tri_count,
2312
- device=self.model.device,
2313
- )
2448
+ self.vertex_colliding_triangles.fill_(-1)
2314
2449
 
2315
- wp.launch(
2316
- kernel=vertex_triangle_collision_detection_kernel,
2317
- inputs=[
2318
- query_radius,
2319
- self.bvh_tris.id,
2320
- self.vertex_positions,
2321
- self.model.tri_indices,
2322
- self.vertex_colliding_triangles_offsets,
2323
- self.vertex_colliding_triangles_buffer_sizes,
2324
- self.triangle_colliding_vertices_offsets,
2325
- self.triangle_colliding_vertices_buffer_sizes,
2326
- ],
2327
- outputs=[
2328
- self.vertex_colliding_triangles,
2329
- self.vertex_colliding_triangles_count,
2330
- self.vertex_colliding_triangles_min_dist,
2331
- self.triangle_colliding_vertices,
2332
- self.triangle_colliding_vertices_count,
2333
- self.triangle_colliding_vertices_min_dist,
2334
- self.resize_flags,
2335
- ],
2336
- dim=self.model.particle_count,
2337
- device=self.model.device,
2338
- )
2450
+ if self.record_triangle_contacting_vertices:
2451
+ wp.launch(
2452
+ kernel=init_triangle_collision_data_kernel,
2453
+ inputs=[
2454
+ query_radius,
2455
+ ],
2456
+ outputs=[
2457
+ self.triangle_colliding_vertices_count,
2458
+ self.triangle_colliding_vertices_min_dist,
2459
+ self.resize_flags,
2460
+ ],
2461
+ dim=self.model.tri_count,
2462
+ device=self.model.device,
2463
+ )
2464
+
2465
+ wp.launch(
2466
+ kernel=vertex_triangle_collision_detection_kernel,
2467
+ inputs=[
2468
+ query_radius,
2469
+ self.bvh_tris.id,
2470
+ self.vertex_positions,
2471
+ self.model.tri_indices,
2472
+ self.vertex_colliding_triangles_offsets,
2473
+ self.vertex_colliding_triangles_buffer_sizes,
2474
+ self.triangle_colliding_vertices_offsets,
2475
+ self.triangle_colliding_vertices_buffer_sizes,
2476
+ ],
2477
+ outputs=[
2478
+ self.vertex_colliding_triangles,
2479
+ self.vertex_colliding_triangles_count,
2480
+ self.vertex_colliding_triangles_min_dist,
2481
+ self.triangle_colliding_vertices,
2482
+ self.triangle_colliding_vertices_count,
2483
+ self.triangle_colliding_vertices_min_dist,
2484
+ self.resize_flags,
2485
+ ],
2486
+ dim=self.model.particle_count,
2487
+ device=self.model.device,
2488
+ )
2489
+ else:
2490
+ self.triangle_colliding_vertices_min_dist.fill_(query_radius)
2491
+ wp.launch(
2492
+ kernel=vertex_triangle_collision_detection_no_triangle_buffers_kernel,
2493
+ inputs=[
2494
+ query_radius,
2495
+ self.bvh_tris.id,
2496
+ self.vertex_positions,
2497
+ self.model.tri_indices,
2498
+ self.vertex_colliding_triangles_offsets,
2499
+ self.vertex_colliding_triangles_buffer_sizes,
2500
+ ],
2501
+ outputs=[
2502
+ self.vertex_colliding_triangles,
2503
+ self.vertex_colliding_triangles_count,
2504
+ self.vertex_colliding_triangles_min_dist,
2505
+ self.triangle_colliding_vertices_min_dist,
2506
+ self.resize_flags,
2507
+ ],
2508
+ dim=self.model.particle_count,
2509
+ device=self.model.device,
2510
+ )
2339
2511
 
2340
2512
  def edge_edge_collision_detection(self, query_radius):
2513
+ self.edge_colliding_edges.fill_(-1)
2341
2514
  wp.launch(
2342
2515
  kernel=edge_colliding_edges_detection_kernel,
2343
2516
  inputs=[
@@ -178,6 +178,13 @@ def color_trimesh(
178
178
  algorithm: the parameter passed to `color_graph`, see `color_graph`'s document
179
179
 
180
180
  """
181
+ if num_nodes == 0:
182
+ return []
183
+
184
+ if trimesh_edge_indices.shape[0] == 0:
185
+ # no edge, all the particle can have same color
186
+ return [np.arange(0, num_nodes, dtype=int)]
187
+
181
188
  if include_bending_energy:
182
189
  graph_edge_indices = construct_trimesh_graph_edges(trimesh_edge_indices, return_wp_array=True)
183
190
  else:
@@ -218,7 +225,7 @@ def color_graph(
218
225
  Ordered Greedy: Ton-That, Q. M., Kry, P. G., & Andrews, S. (2023). Parallel block Neo-Hookean XPBD using graph clustering. Computers & Graphics, 110, 1-10.
219
226
  """
220
227
  if num_nodes == 0:
221
- return
228
+ return []
222
229
 
223
230
  particle_colors = wp.empty(shape=(num_nodes), dtype=wp.int32, device="cpu")
224
231
 
warp/sim/import_mjcf.py CHANGED
@@ -13,11 +13,12 @@
13
13
  # See the License for the specific language governing permissions and
14
14
  # limitations under the License.
15
15
 
16
+ from __future__ import annotations
17
+
16
18
  import math
17
19
  import os
18
20
  import re
19
21
  import xml.etree.ElementTree as ET
20
- from typing import Union
21
22
 
22
23
  import numpy as np
23
24
 
@@ -30,7 +31,7 @@ def parse_mjcf(
30
31
  builder,
31
32
  xform=None,
32
33
  floating=False,
33
- base_joint: Union[dict, str, None] = None,
34
+ base_joint: dict | str | None = None,
34
35
  density=1000.0,
35
36
  stiffness=100.0,
36
37
  damping=10.0,
@@ -447,7 +448,7 @@ def parse_mjcf(
447
448
 
448
449
  return shapes
449
450
 
450
- def parse_body(body, parent, incoming_defaults: dict, childclass: str = None):
451
+ def parse_body(body, parent, incoming_defaults: dict, childclass: str | None = None):
451
452
  body_class = body.get("class")
452
453
  if body_class is None:
453
454
  body_class = childclass
warp/sim/import_usd.py CHANGED
@@ -13,6 +13,8 @@
13
13
  # See the License for the specific language governing permissions and
14
14
  # limitations under the License.
15
15
 
16
+ from __future__ import annotations
17
+
16
18
  import re
17
19
 
18
20
  import numpy as np
@@ -810,17 +812,19 @@ def parse_usd(
810
812
  }
811
813
 
812
814
 
813
- def resolve_usd_from_url(url: str, target_folder_name: str = None, export_usda: bool = False):
814
- """
815
- Downloads a USD file from a URL and resolves all references to other USD files to be downloaded to the given target folder.
815
+ def resolve_usd_from_url(url: str, target_folder_name: str | None = None, export_usda: bool = False) -> str:
816
+ """Download a USD file from a URL and resolves all references to other USD files to be downloaded to the given target folder.
816
817
 
817
818
  Args:
818
- url (str): URL to the USD file.
819
- target_folder_name (str): Target folder name. If None, a timestamped folder will be created in the current directory.
820
- export_usda (bool): If True, converts each downloaded USD file to USDA and saves the additional USDA file in the target folder with the same base name as the original USD file.
819
+ url: URL to the USD file.
820
+ target_folder_name: Target folder name. If ``None``, a time-stamped
821
+ folder will be created in the current directory.
822
+ export_usda: If ``True``, converts each downloaded USD file to USDA and
823
+ saves the additional USDA file in the target folder with the same
824
+ base name as the original USD file.
821
825
 
822
826
  Returns:
823
- str: File path to the downloaded USD file.
827
+ File path to the downloaded USD file.
824
828
  """
825
829
  import datetime
826
830
  import os
warp/sim/integrator.py CHANGED
@@ -32,11 +32,14 @@ def integrate_particles(
32
32
  v_new: wp.array(dtype=wp.vec3),
33
33
  ):
34
34
  tid = wp.tid()
35
+ x0 = x[tid]
36
+ v0 = v[tid]
37
+
35
38
  if (particle_flags[tid] & PARTICLE_FLAG_ACTIVE) == 0:
39
+ x_new[tid] = x0
40
+ v_new[tid] = wp.vec3(0.0)
36
41
  return
37
42
 
38
- x0 = x[tid]
39
- v0 = v[tid]
40
43
  f0 = f[tid]
41
44
 
42
45
  inv_mass = w[tid]
@@ -1781,7 +1781,7 @@ def eval_tetrahedral_forces(model: Model, state: State, control: Control, partic
1781
1781
 
1782
1782
  def eval_body_contact_forces(model: Model, state: State, particle_f: wp.array, friction_smoothing: float = 1.0):
1783
1783
  if model.rigid_contact_max and (
1784
- model.ground and model.shape_ground_contact_pair_count or model.shape_contact_pair_count
1784
+ (model.ground and model.shape_ground_contact_pair_count) or model.shape_contact_pair_count
1785
1785
  ):
1786
1786
  wp.launch(
1787
1787
  kernel=eval_rigid_contacts,
@@ -1825,7 +1825,7 @@ class FeatherstoneIntegrator(Integrator):
1825
1825
  )
1826
1826
 
1827
1827
  if model.rigid_contact_max and (
1828
- model.ground and model.shape_ground_contact_pair_count or model.shape_contact_pair_count
1828
+ (model.ground and model.shape_ground_contact_pair_count) or model.shape_contact_pair_count
1829
1829
  ):
1830
1830
  wp.launch(
1831
1831
  kernel=eval_rigid_contacts,