warp-lang 1.6.2__py3-none-macosx_10_13_universal2.whl → 1.7.1__py3-none-macosx_10_13_universal2.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 (191) hide show
  1. warp/__init__.py +7 -1
  2. warp/autograd.py +12 -2
  3. warp/bin/libwarp-clang.dylib +0 -0
  4. warp/bin/libwarp.dylib +0 -0
  5. warp/build.py +410 -0
  6. warp/build_dll.py +6 -14
  7. warp/builtins.py +463 -372
  8. warp/codegen.py +196 -124
  9. warp/config.py +42 -6
  10. warp/context.py +496 -271
  11. warp/dlpack.py +8 -6
  12. warp/examples/assets/nonuniform.usd +0 -0
  13. warp/examples/assets/nvidia_logo.png +0 -0
  14. warp/examples/benchmarks/benchmark_cloth.py +1 -1
  15. warp/examples/benchmarks/benchmark_tile_load_store.py +103 -0
  16. warp/examples/core/example_sample_mesh.py +300 -0
  17. warp/examples/distributed/example_jacobi_mpi.py +507 -0
  18. warp/examples/fem/example_apic_fluid.py +1 -1
  19. warp/examples/fem/example_burgers.py +2 -2
  20. warp/examples/fem/example_deformed_geometry.py +1 -1
  21. warp/examples/fem/example_distortion_energy.py +1 -1
  22. warp/examples/fem/example_magnetostatics.py +6 -6
  23. warp/examples/fem/utils.py +9 -3
  24. warp/examples/interop/example_jax_callable.py +116 -0
  25. warp/examples/interop/example_jax_ffi_callback.py +132 -0
  26. warp/examples/interop/example_jax_kernel.py +205 -0
  27. warp/examples/optim/example_fluid_checkpoint.py +497 -0
  28. warp/examples/tile/example_tile_matmul.py +2 -4
  29. warp/fem/__init__.py +11 -1
  30. warp/fem/adaptivity.py +4 -4
  31. warp/fem/field/field.py +11 -1
  32. warp/fem/field/nodal_field.py +56 -88
  33. warp/fem/field/virtual.py +62 -23
  34. warp/fem/geometry/adaptive_nanogrid.py +16 -13
  35. warp/fem/geometry/closest_point.py +1 -1
  36. warp/fem/geometry/deformed_geometry.py +5 -2
  37. warp/fem/geometry/geometry.py +5 -0
  38. warp/fem/geometry/grid_2d.py +12 -12
  39. warp/fem/geometry/grid_3d.py +12 -15
  40. warp/fem/geometry/hexmesh.py +5 -7
  41. warp/fem/geometry/nanogrid.py +9 -11
  42. warp/fem/geometry/quadmesh.py +13 -13
  43. warp/fem/geometry/tetmesh.py +3 -4
  44. warp/fem/geometry/trimesh.py +7 -20
  45. warp/fem/integrate.py +262 -93
  46. warp/fem/linalg.py +5 -5
  47. warp/fem/quadrature/pic_quadrature.py +37 -22
  48. warp/fem/quadrature/quadrature.py +194 -25
  49. warp/fem/space/__init__.py +1 -1
  50. warp/fem/space/basis_function_space.py +4 -2
  51. warp/fem/space/basis_space.py +25 -18
  52. warp/fem/space/hexmesh_function_space.py +2 -2
  53. warp/fem/space/partition.py +6 -2
  54. warp/fem/space/quadmesh_function_space.py +8 -8
  55. warp/fem/space/shape/cube_shape_function.py +23 -23
  56. warp/fem/space/shape/square_shape_function.py +12 -12
  57. warp/fem/space/shape/triangle_shape_function.py +1 -1
  58. warp/fem/space/tetmesh_function_space.py +3 -3
  59. warp/fem/space/trimesh_function_space.py +2 -2
  60. warp/fem/utils.py +12 -6
  61. warp/jax.py +14 -1
  62. warp/jax_experimental/__init__.py +16 -0
  63. warp/{jax_experimental.py → jax_experimental/custom_call.py} +28 -29
  64. warp/jax_experimental/ffi.py +702 -0
  65. warp/jax_experimental/xla_ffi.py +602 -0
  66. warp/math.py +89 -0
  67. warp/native/array.h +13 -0
  68. warp/native/builtin.h +29 -3
  69. warp/native/bvh.cpp +3 -1
  70. warp/native/bvh.cu +42 -14
  71. warp/native/bvh.h +2 -1
  72. warp/native/clang/clang.cpp +30 -3
  73. warp/native/cuda_util.cpp +14 -0
  74. warp/native/cuda_util.h +2 -0
  75. warp/native/exports.h +68 -63
  76. warp/native/intersect.h +26 -26
  77. warp/native/intersect_adj.h +33 -33
  78. warp/native/marching.cu +1 -1
  79. warp/native/mat.h +513 -9
  80. warp/native/mesh.h +10 -10
  81. warp/native/quat.h +99 -11
  82. warp/native/rand.h +6 -0
  83. warp/native/sort.cpp +122 -59
  84. warp/native/sort.cu +152 -15
  85. warp/native/sort.h +8 -1
  86. warp/native/sparse.cpp +43 -22
  87. warp/native/sparse.cu +52 -17
  88. warp/native/svd.h +116 -0
  89. warp/native/tile.h +312 -116
  90. warp/native/tile_reduce.h +46 -3
  91. warp/native/vec.h +68 -7
  92. warp/native/volume.cpp +85 -113
  93. warp/native/volume_builder.cu +25 -10
  94. warp/native/volume_builder.h +6 -0
  95. warp/native/warp.cpp +5 -6
  96. warp/native/warp.cu +100 -11
  97. warp/native/warp.h +19 -10
  98. warp/optim/linear.py +10 -10
  99. warp/render/render_opengl.py +19 -17
  100. warp/render/render_usd.py +93 -3
  101. warp/sim/articulation.py +4 -4
  102. warp/sim/collide.py +32 -19
  103. warp/sim/import_mjcf.py +449 -155
  104. warp/sim/import_urdf.py +32 -12
  105. warp/sim/inertia.py +189 -156
  106. warp/sim/integrator_euler.py +8 -5
  107. warp/sim/integrator_featherstone.py +3 -10
  108. warp/sim/integrator_vbd.py +207 -2
  109. warp/sim/integrator_xpbd.py +8 -5
  110. warp/sim/model.py +71 -25
  111. warp/sim/render.py +4 -0
  112. warp/sim/utils.py +2 -2
  113. warp/sparse.py +642 -555
  114. warp/stubs.py +217 -20
  115. warp/tests/__main__.py +0 -15
  116. warp/tests/assets/torus.usda +1 -1
  117. warp/tests/cuda/__init__.py +0 -0
  118. warp/tests/{test_mempool.py → cuda/test_mempool.py} +39 -0
  119. warp/tests/{test_streams.py → cuda/test_streams.py} +71 -0
  120. warp/tests/geometry/__init__.py +0 -0
  121. warp/tests/{test_mesh_query_point.py → geometry/test_mesh_query_point.py} +66 -63
  122. warp/tests/{test_mesh_query_ray.py → geometry/test_mesh_query_ray.py} +1 -1
  123. warp/tests/{test_volume.py → geometry/test_volume.py} +41 -6
  124. warp/tests/interop/__init__.py +0 -0
  125. warp/tests/{test_dlpack.py → interop/test_dlpack.py} +28 -5
  126. warp/tests/sim/__init__.py +0 -0
  127. warp/tests/{disabled_kinematics.py → sim/disabled_kinematics.py} +9 -10
  128. warp/tests/{test_collision.py → sim/test_collision.py} +236 -205
  129. warp/tests/sim/test_inertia.py +161 -0
  130. warp/tests/{test_model.py → sim/test_model.py} +40 -0
  131. warp/tests/{flaky_test_sim_grad.py → sim/test_sim_grad.py} +4 -0
  132. warp/tests/{test_sim_kinematics.py → sim/test_sim_kinematics.py} +2 -1
  133. warp/tests/sim/test_vbd.py +597 -0
  134. warp/tests/sim/test_xpbd.py +399 -0
  135. warp/tests/test_bool.py +1 -1
  136. warp/tests/test_codegen.py +24 -3
  137. warp/tests/test_examples.py +40 -38
  138. warp/tests/test_fem.py +98 -14
  139. warp/tests/test_linear_solvers.py +0 -11
  140. warp/tests/test_mat.py +577 -156
  141. warp/tests/test_mat_scalar_ops.py +4 -4
  142. warp/tests/test_overwrite.py +0 -60
  143. warp/tests/test_quat.py +356 -151
  144. warp/tests/test_rand.py +44 -37
  145. warp/tests/test_sparse.py +47 -6
  146. warp/tests/test_spatial.py +75 -0
  147. warp/tests/test_static.py +1 -1
  148. warp/tests/test_utils.py +84 -4
  149. warp/tests/test_vec.py +336 -178
  150. warp/tests/tile/__init__.py +0 -0
  151. warp/tests/{test_tile.py → tile/test_tile.py} +136 -51
  152. warp/tests/{test_tile_load.py → tile/test_tile_load.py} +98 -1
  153. warp/tests/{test_tile_mathdx.py → tile/test_tile_mathdx.py} +9 -6
  154. warp/tests/{test_tile_mlp.py → tile/test_tile_mlp.py} +25 -14
  155. warp/tests/{test_tile_reduce.py → tile/test_tile_reduce.py} +60 -1
  156. warp/tests/{test_tile_view.py → tile/test_tile_view.py} +1 -1
  157. warp/tests/unittest_serial.py +1 -0
  158. warp/tests/unittest_suites.py +45 -62
  159. warp/tests/unittest_utils.py +2 -1
  160. warp/thirdparty/unittest_parallel.py +3 -1
  161. warp/types.py +175 -666
  162. warp/utils.py +137 -72
  163. {warp_lang-1.6.2.dist-info → warp_lang-1.7.1.dist-info}/METADATA +46 -12
  164. {warp_lang-1.6.2.dist-info → warp_lang-1.7.1.dist-info}/RECORD +184 -171
  165. {warp_lang-1.6.2.dist-info → warp_lang-1.7.1.dist-info}/WHEEL +1 -1
  166. {warp_lang-1.6.2.dist-info → warp_lang-1.7.1.dist-info/licenses}/LICENSE.md +0 -26
  167. warp/examples/optim/example_walker.py +0 -317
  168. warp/native/cutlass_gemm.cpp +0 -43
  169. warp/native/cutlass_gemm.cu +0 -382
  170. warp/tests/test_matmul.py +0 -511
  171. warp/tests/test_matmul_lite.py +0 -411
  172. warp/tests/test_vbd.py +0 -386
  173. warp/tests/unused_test_misc.py +0 -77
  174. /warp/tests/{test_async.py → cuda/test_async.py} +0 -0
  175. /warp/tests/{test_ipc.py → cuda/test_ipc.py} +0 -0
  176. /warp/tests/{test_multigpu.py → cuda/test_multigpu.py} +0 -0
  177. /warp/tests/{test_peer.py → cuda/test_peer.py} +0 -0
  178. /warp/tests/{test_pinned.py → cuda/test_pinned.py} +0 -0
  179. /warp/tests/{test_bvh.py → geometry/test_bvh.py} +0 -0
  180. /warp/tests/{test_hash_grid.py → geometry/test_hash_grid.py} +0 -0
  181. /warp/tests/{test_marching_cubes.py → geometry/test_marching_cubes.py} +0 -0
  182. /warp/tests/{test_mesh.py → geometry/test_mesh.py} +0 -0
  183. /warp/tests/{test_mesh_query_aabb.py → geometry/test_mesh_query_aabb.py} +0 -0
  184. /warp/tests/{test_volume_write.py → geometry/test_volume_write.py} +0 -0
  185. /warp/tests/{test_jax.py → interop/test_jax.py} +0 -0
  186. /warp/tests/{test_paddle.py → interop/test_paddle.py} +0 -0
  187. /warp/tests/{test_torch.py → interop/test_torch.py} +0 -0
  188. /warp/tests/{test_coloring.py → sim/test_coloring.py} +0 -0
  189. /warp/tests/{test_sim_grad_bounce_linear.py → sim/test_sim_grad_bounce_linear.py} +0 -0
  190. /warp/tests/{test_tile_shared_memory.py → tile/test_tile_shared_memory.py} +0 -0
  191. {warp_lang-1.6.2.dist-info → warp_lang-1.7.1.dist-info}/top_level.txt +0 -0
warp/sim/import_urdf.py CHANGED
@@ -20,7 +20,6 @@ from typing import Union
20
20
  import numpy as np
21
21
 
22
22
  import warp as wp
23
- import warp.sim
24
23
  from warp.sim.model import Mesh
25
24
 
26
25
 
@@ -46,6 +45,7 @@ def parse_urdf(
46
45
  joint_limit_lower=-1e6,
47
46
  joint_limit_upper=1e6,
48
47
  scale=1.0,
48
+ hide_visuals=False,
49
49
  parse_visuals_as_colliders=False,
50
50
  force_show_colliders=False,
51
51
  enable_self_collisions=True,
@@ -79,6 +79,7 @@ def parse_urdf(
79
79
  joint_limit_lower (float): The default lower joint limit if not specified in the URDF.
80
80
  joint_limit_upper (float): The default upper joint limit if not specified in the URDF.
81
81
  scale (float): The scaling factor to apply to the imported mechanism.
82
+ hide_visuals (bool): If True, hide visual shapes.
82
83
  parse_visuals_as_colliders (bool): If True, the geometry defined under the `<visual>` tags is used for collision handling instead of the `<collision>` geometries.
83
84
  force_show_colliders (bool): If True, the collision shapes are always shown, even if there are visual shapes.
84
85
  enable_self_collisions (bool): If True, self-collisions are enabled.
@@ -173,6 +174,22 @@ def parse_urdf(
173
174
  )
174
175
  shapes.append(s)
175
176
 
177
+ for capsule in geo.findall("capsule"):
178
+ s = builder.add_shape_capsule(
179
+ body=link,
180
+ pos=wp.vec3(tf.p),
181
+ rot=wp.quat(tf.q),
182
+ radius=float(capsule.get("radius") or "1") * scale,
183
+ half_height=float(capsule.get("height") or "1") * 0.5 * scale,
184
+ density=density,
185
+ up_axis=2, # capsules in URDF are aligned with z-axis
186
+ is_visible=visible,
187
+ has_ground_collision=not just_visual,
188
+ has_shape_collision=not just_visual,
189
+ **contact_vars,
190
+ )
191
+ shapes.append(s)
192
+
176
193
  for mesh in geo.findall("mesh"):
177
194
  filename = mesh.get("filename")
178
195
  if filename is None:
@@ -219,15 +236,15 @@ def parse_urdf(
219
236
  scaling = np.array([float(x) * scale for x in scaling.split()])
220
237
  if hasattr(m, "geometry"):
221
238
  # multiple meshes are contained in a scene
222
- for geom in m.geometry.values():
223
- geom_vertices = np.array(geom.vertices, dtype=np.float32) * scaling
224
- geom_faces = np.array(geom.faces.flatten(), dtype=np.int32)
225
- geom_mesh = Mesh(geom_vertices, geom_faces)
239
+ for m_geom in m.geometry.values():
240
+ m_vertices = np.array(m_geom.vertices, dtype=np.float32) * scaling
241
+ m_faces = np.array(m_geom.faces.flatten(), dtype=np.int32)
242
+ m_mesh = Mesh(m_vertices, m_faces)
226
243
  s = builder.add_shape_mesh(
227
244
  body=link,
228
245
  pos=wp.vec3(tf.p),
229
246
  rot=wp.quat(tf.q),
230
- mesh=geom_mesh,
247
+ mesh=m_mesh,
231
248
  density=density,
232
249
  is_visible=visible,
233
250
  has_ground_collision=not just_visual,
@@ -278,7 +295,7 @@ def parse_urdf(
278
295
  if parse_visuals_as_colliders:
279
296
  colliders = visuals
280
297
  else:
281
- s = parse_shapes(link, visuals, density=0.0, just_visual=True)
298
+ s = parse_shapes(link, visuals, density=0.0, just_visual=True, visible=not hide_visuals)
282
299
  visual_shapes.extend(s)
283
300
 
284
301
  show_colliders = force_show_colliders
@@ -309,10 +326,13 @@ def parse_urdf(
309
326
  I_m = rot @ wp.mat33(I_m)
310
327
  m = float(inertial.find("mass").get("value") or "0")
311
328
  builder.body_mass[link] = m
312
- builder.body_inv_mass[link] = 1.0 / m
329
+ builder.body_inv_mass[link] = 1.0 / m if m > 0.0 else 0.0
313
330
  builder.body_com[link] = com
314
331
  builder.body_inertia[link] = I_m
315
- builder.body_inv_inertia[link] = wp.inverse(I_m)
332
+ if any(x for x in I_m):
333
+ builder.body_inv_inertia[link] = wp.inverse(I_m)
334
+ else:
335
+ builder.body_inv_inertia[link] = I_m
316
336
  if m == 0.0 and ensure_nonstatic_links:
317
337
  # set the mass to something nonzero to ensure the body is dynamic
318
338
  m = static_link_mass
@@ -369,7 +389,7 @@ def parse_urdf(
369
389
 
370
390
  # topological sorting of joints because the FK solver will resolve body transforms
371
391
  # in joint order and needs the parent link transform to be resolved before the child
372
- visited = {name: False for name in link_index.keys()}
392
+ visited = dict.fromkeys(link_index.keys(), False)
373
393
  sorted_joints = []
374
394
 
375
395
  # depth-first search
@@ -520,7 +540,7 @@ def parse_urdf(
520
540
 
521
541
  builder.add_joint_d6(
522
542
  linear_axes=[
523
- warp.sim.JointAxis(
543
+ wp.sim.JointAxis(
524
544
  u,
525
545
  limit_lower=lower * scale,
526
546
  limit_upper=upper * scale,
@@ -530,7 +550,7 @@ def parse_urdf(
530
550
  target_kd=joint_damping,
531
551
  mode=joint_mode,
532
552
  ),
533
- warp.sim.JointAxis(
553
+ wp.sim.JointAxis(
534
554
  v,
535
555
  limit_lower=lower * scale,
536
556
  limit_upper=upper * scale,
warp/sim/inertia.py CHANGED
@@ -15,131 +15,14 @@
15
15
 
16
16
  """Helper functions for computing rigid body inertia properties."""
17
17
 
18
- import math
19
- from typing import List, Union
18
+ from __future__ import annotations
20
19
 
21
20
  import numpy as np
22
21
 
23
22
  import warp as wp
24
23
 
25
24
 
26
- @wp.func
27
- def triangle_inertia(
28
- p: wp.vec3,
29
- q: wp.vec3,
30
- r: wp.vec3,
31
- density: float,
32
- com: wp.vec3,
33
- # outputs
34
- mass: wp.array(dtype=float, ndim=1),
35
- inertia: wp.array(dtype=wp.mat33, ndim=1),
36
- ):
37
- pcom = p - com
38
- qcom = q - com
39
- rcom = r - com
40
-
41
- Dm = wp.mat33(pcom[0], qcom[0], rcom[0], pcom[1], qcom[1], rcom[1], pcom[2], qcom[2], rcom[2])
42
-
43
- volume = wp.abs(wp.determinant(Dm) / 6.0)
44
-
45
- # accumulate mass
46
- wp.atomic_add(mass, 0, 4.0 * density * volume)
47
-
48
- alpha = wp.sqrt(5.0) / 5.0
49
- mid = (com + p + q + r) / 4.0
50
- off_mid = mid - com
51
-
52
- # displacement of quadrature point from COM
53
- d0 = alpha * (p - mid) + off_mid
54
- d1 = alpha * (q - mid) + off_mid
55
- d2 = alpha * (r - mid) + off_mid
56
- d3 = alpha * (com - mid) + off_mid
57
-
58
- # accumulate inertia
59
- identity = wp.mat33(1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0)
60
- I = wp.dot(d0, d0) * identity - wp.outer(d0, d0)
61
- I += wp.dot(d1, d1) * identity - wp.outer(d1, d1)
62
- I += wp.dot(d2, d2) * identity - wp.outer(d2, d2)
63
- I += wp.dot(d3, d3) * identity - wp.outer(d3, d3)
64
-
65
- wp.atomic_add(inertia, 0, (density * volume) * I)
66
-
67
- return volume
68
-
69
-
70
- @wp.kernel
71
- def compute_solid_mesh_inertia(
72
- # inputs
73
- com: wp.vec3,
74
- weight: float,
75
- indices: wp.array(dtype=int, ndim=1),
76
- vertices: wp.array(dtype=wp.vec3, ndim=1),
77
- # outputs
78
- mass: wp.array(dtype=float, ndim=1),
79
- inertia: wp.array(dtype=wp.mat33, ndim=1),
80
- volume: wp.array(dtype=float, ndim=1),
81
- ):
82
- i = wp.tid()
83
-
84
- p = vertices[indices[i * 3 + 0]]
85
- q = vertices[indices[i * 3 + 1]]
86
- r = vertices[indices[i * 3 + 2]]
87
-
88
- vol = triangle_inertia(p, q, r, weight, com, mass, inertia)
89
- wp.atomic_add(volume, 0, vol)
90
-
91
-
92
- @wp.kernel
93
- def compute_hollow_mesh_inertia(
94
- # inputs
95
- com: wp.vec3,
96
- density: float,
97
- indices: wp.array(dtype=int, ndim=1),
98
- vertices: wp.array(dtype=wp.vec3, ndim=1),
99
- thickness: wp.array(dtype=float, ndim=1),
100
- # outputs
101
- mass: wp.array(dtype=float, ndim=1),
102
- inertia: wp.array(dtype=wp.mat33, ndim=1),
103
- volume: wp.array(dtype=float, ndim=1),
104
- ):
105
- tid = wp.tid()
106
- i = indices[tid * 3 + 0]
107
- j = indices[tid * 3 + 1]
108
- k = indices[tid * 3 + 2]
109
-
110
- vi = vertices[i]
111
- vj = vertices[j]
112
- vk = vertices[k]
113
-
114
- normal = -wp.normalize(wp.cross(vj - vi, vk - vi))
115
- ti = normal * thickness[i]
116
- tj = normal * thickness[j]
117
- tk = normal * thickness[k]
118
-
119
- # wedge vertices
120
- vi0 = vi - ti
121
- vi1 = vi + ti
122
- vj0 = vj - tj
123
- vj1 = vj + tj
124
- vk0 = vk - tk
125
- vk1 = vk + tk
126
-
127
- triangle_inertia(vi0, vj0, vk0, density, com, mass, inertia)
128
- triangle_inertia(vj0, vk1, vk0, density, com, mass, inertia)
129
- triangle_inertia(vj0, vj1, vk1, density, com, mass, inertia)
130
- triangle_inertia(vj0, vi1, vj1, density, com, mass, inertia)
131
- triangle_inertia(vj0, vi0, vi1, density, com, mass, inertia)
132
- triangle_inertia(vj1, vi1, vk1, density, com, mass, inertia)
133
- triangle_inertia(vi1, vi0, vk0, density, com, mass, inertia)
134
- triangle_inertia(vi1, vk0, vk1, density, com, mass, inertia)
135
-
136
- # compute volume
137
- a = wp.length(wp.cross(vj - vi, vk - vi)) * 0.5
138
- vol = a * (thickness[i] + thickness[j] + thickness[k]) / 3.0
139
- wp.atomic_add(volume, 0, vol)
140
-
141
-
142
- def compute_sphere_inertia(density: float, r: float) -> tuple:
25
+ def compute_sphere_inertia(density: float, r: float) -> tuple[float, wp.vec3, wp.mat33]:
143
26
  """Helper to compute mass and inertia of a solid sphere
144
27
 
145
28
  Args:
@@ -151,7 +34,7 @@ def compute_sphere_inertia(density: float, r: float) -> tuple:
151
34
  A tuple of (mass, inertia) with inertia specified around the origin
152
35
  """
153
36
 
154
- v = 4.0 / 3.0 * math.pi * r * r * r
37
+ v = 4.0 / 3.0 * wp.pi * r * r * r
155
38
 
156
39
  m = density * v
157
40
  Ia = 2.0 / 5.0 * m * r * r
@@ -161,7 +44,7 @@ def compute_sphere_inertia(density: float, r: float) -> tuple:
161
44
  return (m, wp.vec3(), I)
162
45
 
163
46
 
164
- def compute_capsule_inertia(density: float, r: float, h: float) -> tuple:
47
+ def compute_capsule_inertia(density: float, r: float, h: float) -> tuple[float, wp.vec3, wp.mat33]:
165
48
  """Helper to compute mass and inertia of a solid capsule extending along the y-axis
166
49
 
167
50
  Args:
@@ -174,8 +57,8 @@ def compute_capsule_inertia(density: float, r: float, h: float) -> tuple:
174
57
  A tuple of (mass, inertia) with inertia specified around the origin
175
58
  """
176
59
 
177
- ms = density * (4.0 / 3.0) * math.pi * r * r * r
178
- mc = density * math.pi * r * r * h
60
+ ms = density * (4.0 / 3.0) * wp.pi * r * r * r
61
+ mc = density * wp.pi * r * r * h
179
62
 
180
63
  # total mass
181
64
  m = ms + mc
@@ -189,7 +72,7 @@ def compute_capsule_inertia(density: float, r: float, h: float) -> tuple:
189
72
  return (m, wp.vec3(), I)
190
73
 
191
74
 
192
- def compute_cylinder_inertia(density: float, r: float, h: float) -> tuple:
75
+ def compute_cylinder_inertia(density: float, r: float, h: float) -> tuple[float, wp.vec3, wp.mat33]:
193
76
  """Helper to compute mass and inertia of a solid cylinder extending along the y-axis
194
77
 
195
78
  Args:
@@ -202,7 +85,7 @@ def compute_cylinder_inertia(density: float, r: float, h: float) -> tuple:
202
85
  A tuple of (mass, inertia) with inertia specified around the origin
203
86
  """
204
87
 
205
- m = density * math.pi * r * r * h
88
+ m = density * wp.pi * r * r * h
206
89
 
207
90
  Ia = 1 / 12 * m * (3 * r * r + h * h)
208
91
  Ib = 1 / 2 * m * r * r
@@ -212,7 +95,7 @@ def compute_cylinder_inertia(density: float, r: float, h: float) -> tuple:
212
95
  return (m, wp.vec3(), I)
213
96
 
214
97
 
215
- def compute_cone_inertia(density: float, r: float, h: float) -> tuple:
98
+ def compute_cone_inertia(density: float, r: float, h: float) -> tuple[float, wp.vec3, wp.mat33]:
216
99
  """Helper to compute mass and inertia of a solid cone extending along the y-axis
217
100
 
218
101
  Args:
@@ -225,7 +108,7 @@ def compute_cone_inertia(density: float, r: float, h: float) -> tuple:
225
108
  A tuple of (mass, inertia) with inertia specified around the origin
226
109
  """
227
110
 
228
- m = density * math.pi * r * r * h / 3.0
111
+ m = density * wp.pi * r * r * h / 3.0
229
112
 
230
113
  Ia = 1 / 20 * m * (3 * r * r + 2 * h * h)
231
114
  Ib = 3 / 10 * m * r * r
@@ -235,7 +118,7 @@ def compute_cone_inertia(density: float, r: float, h: float) -> tuple:
235
118
  return (m, wp.vec3(), I)
236
119
 
237
120
 
238
- def compute_box_inertia(density: float, w: float, h: float, d: float) -> tuple:
121
+ def compute_box_inertia(density: float, w: float, h: float, d: float) -> tuple[float, wp.vec3, wp.mat33]:
239
122
  """Helper to compute mass and inertia of a solid box
240
123
 
241
124
  Args:
@@ -261,63 +144,213 @@ def compute_box_inertia(density: float, w: float, h: float, d: float) -> tuple:
261
144
  return (m, wp.vec3(), I)
262
145
 
263
146
 
147
+ @wp.func
148
+ def triangle_inertia(
149
+ v0: wp.vec3,
150
+ v1: wp.vec3,
151
+ v2: wp.vec3,
152
+ ):
153
+ vol = wp.dot(v0, wp.cross(v1, v2)) / 6.0 # tetra volume (0,v0,v1,v2)
154
+ first = vol * (v0 + v1 + v2) / 4.0 # first-order integral
155
+
156
+ # second-order integral (symmetric)
157
+ o00, o11, o22 = wp.outer(v0, v0), wp.outer(v1, v1), wp.outer(v2, v2)
158
+ o01, o02, o12 = wp.outer(v0, v1), wp.outer(v0, v2), wp.outer(v1, v2)
159
+ o01t, o02t, o12t = wp.transpose(o01), wp.transpose(o02), wp.transpose(o12)
160
+
161
+ second = (vol / 10.0) * (o00 + o11 + o22)
162
+ second += (vol / 20.0) * (o01 + o01t + o02 + o02t + o12 + o12t)
163
+
164
+ return vol, first, second
165
+
166
+
167
+ @wp.kernel
168
+ def compute_solid_mesh_inertia(
169
+ indices: wp.array(dtype=int),
170
+ vertices: wp.array(dtype=wp.vec3),
171
+ # outputs
172
+ volume: wp.array(dtype=float),
173
+ first: wp.array(dtype=wp.vec3),
174
+ second: wp.array(dtype=wp.mat33),
175
+ ):
176
+ i = wp.tid()
177
+ p = vertices[indices[i * 3 + 0]]
178
+ q = vertices[indices[i * 3 + 1]]
179
+ r = vertices[indices[i * 3 + 2]]
180
+
181
+ v, f, s = triangle_inertia(p, q, r)
182
+ wp.atomic_add(volume, 0, v)
183
+ wp.atomic_add(first, 0, f)
184
+ wp.atomic_add(second, 0, s)
185
+
186
+
187
+ @wp.kernel
188
+ def compute_hollow_mesh_inertia(
189
+ indices: wp.array(dtype=int),
190
+ vertices: wp.array(dtype=wp.vec3),
191
+ thickness: wp.array(dtype=float),
192
+ # outputs
193
+ volume: wp.array(dtype=float),
194
+ first: wp.array(dtype=wp.vec3),
195
+ second: wp.array(dtype=wp.mat33),
196
+ ):
197
+ tid = wp.tid()
198
+ i = indices[tid * 3 + 0]
199
+ j = indices[tid * 3 + 1]
200
+ k = indices[tid * 3 + 2]
201
+
202
+ vi = vertices[i]
203
+ vj = vertices[j]
204
+ vk = vertices[k]
205
+
206
+ normal = -wp.normalize(wp.cross(vj - vi, vk - vi))
207
+ ti = normal * thickness[i]
208
+ tj = normal * thickness[j]
209
+ tk = normal * thickness[k]
210
+
211
+ # wedge vertices
212
+ vi0 = vi - ti
213
+ vi1 = vi + ti
214
+ vj0 = vj - tj
215
+ vj1 = vj + tj
216
+ vk0 = vk - tk
217
+ vk1 = vk + tk
218
+
219
+ v_total = 0.0
220
+ f_total = wp.vec3(0.0)
221
+ s_total = wp.mat33(0.0)
222
+
223
+ v, f, s = triangle_inertia(vi0, vj0, vk0)
224
+ v_total += v
225
+ f_total += f
226
+ s_total += s
227
+ v, f, s = triangle_inertia(vj0, vk1, vk0)
228
+ v_total += v
229
+ f_total += f
230
+ s_total += s
231
+ v, f, s = triangle_inertia(vj0, vj1, vk1)
232
+ v_total += v
233
+ f_total += f
234
+ s_total += s
235
+ v, f, s = triangle_inertia(vj0, vi1, vj1)
236
+ v_total += v
237
+ f_total += f
238
+ s_total += s
239
+ v, f, s = triangle_inertia(vj0, vi0, vi1)
240
+ v_total += v
241
+ f_total += f
242
+ s_total += s
243
+ v, f, s = triangle_inertia(vj1, vi1, vk1)
244
+ v_total += v
245
+ f_total += f
246
+ s_total += s
247
+ v, f, s = triangle_inertia(vi1, vi0, vk0)
248
+ v_total += v
249
+ f_total += f
250
+ s_total += s
251
+ v, f, s = triangle_inertia(vi1, vk0, vk1)
252
+ v_total += v
253
+ f_total += f
254
+ s_total += s
255
+
256
+ wp.atomic_add(volume, 0, v_total)
257
+ wp.atomic_add(first, 0, f_total)
258
+ wp.atomic_add(second, 0, s_total)
259
+
260
+
264
261
  def compute_mesh_inertia(
265
- density: float, vertices: list, indices: list, is_solid: bool = True, thickness: Union[List[float], float] = 0.001
266
- ) -> tuple:
267
- """Computes mass, center of mass, 3x3 inertia matrix, and volume for a mesh."""
268
- com = wp.vec3(np.mean(vertices, 0))
262
+ density: float,
263
+ vertices: list,
264
+ indices: list,
265
+ is_solid: bool = True,
266
+ thickness: list[float] | float = 0.001,
267
+ ) -> tuple[float, wp.vec3, wp.mat33, float]:
268
+ """
269
+ Compute the mass, center of mass, inertia, and volume of a triangular mesh.
270
+
271
+ Args:
272
+ density: The density of the mesh material.
273
+ vertices: A list of vertex positions (3D coordinates).
274
+ indices: A list of triangle indices (each triangle is defined by 3 vertex indices).
275
+ is_solid: If True, compute inertia for a solid mesh; if False, for a hollow mesh using the given thickness.
276
+ thickness: Thickness of the mesh if it is hollow. Can be a single value or a list of values for each vertex.
277
+
278
+ Returns:
279
+ A tuple containing:
280
+ - mass: The mass of the mesh.
281
+ - com: The center of mass (3D coordinates).
282
+ - I: The inertia tensor (3x3 matrix).
283
+ - volume: The signed volume of the mesh.
284
+ """
269
285
 
270
286
  indices = np.array(indices).flatten()
271
287
  num_tris = len(indices) // 3
272
288
 
273
- # compute signed inertia for each tetrahedron
274
- # formed with the interior point, using an order-2
275
- # quadrature: https://www.sciencedirect.com/science/article/pii/S0377042712001604#br000040
276
-
277
289
  # Allocating for mass and inertia
290
+ com_warp = wp.zeros(1, dtype=wp.vec3)
278
291
  I_warp = wp.zeros(1, dtype=wp.mat33)
279
- mass_warp = wp.zeros(1, dtype=float)
280
292
  vol_warp = wp.zeros(1, dtype=float)
281
293
 
294
+ wp_vertices = wp.array(vertices, dtype=wp.vec3)
295
+ wp_indices = wp.array(indices, dtype=int)
296
+
282
297
  if is_solid:
283
- weight = 0.25
284
- # alpha = math.sqrt(5.0) / 5.0
285
298
  wp.launch(
286
299
  kernel=compute_solid_mesh_inertia,
287
300
  dim=num_tris,
288
301
  inputs=[
289
- com,
290
- weight,
291
- wp.array(indices, dtype=int),
292
- wp.array(vertices, dtype=wp.vec3),
302
+ wp_indices,
303
+ wp_vertices,
304
+ ],
305
+ outputs=[
306
+ vol_warp,
307
+ com_warp,
308
+ I_warp,
293
309
  ],
294
- outputs=[mass_warp, I_warp, vol_warp],
295
310
  )
296
311
  else:
297
- weight = 0.25 * density
298
312
  if isinstance(thickness, float):
299
313
  thickness = [thickness] * len(vertices)
300
314
  wp.launch(
301
315
  kernel=compute_hollow_mesh_inertia,
302
316
  dim=num_tris,
303
317
  inputs=[
304
- com,
305
- weight,
306
- wp.array(indices, dtype=int),
307
- wp.array(vertices, dtype=wp.vec3),
318
+ wp_indices,
319
+ wp_vertices,
308
320
  wp.array(thickness, dtype=float),
309
321
  ],
310
- outputs=[mass_warp, I_warp, vol_warp],
322
+ outputs=[
323
+ vol_warp,
324
+ com_warp,
325
+ I_warp,
326
+ ],
311
327
  )
312
328
 
313
- # Extract mass and inertia and save to class attributes.
314
- mass = float(mass_warp.numpy()[0] * density)
315
- I = wp.mat33(*(I_warp.numpy()[0] * density))
316
- volume = float(vol_warp.numpy()[0])
317
- return mass, com, I, volume
329
+ V_tot = float(vol_warp.numpy()[0]) # signed volume
330
+ F_tot = com_warp.numpy()[0] # first moment
331
+ S_tot = I_warp.numpy()[0] # second moment
332
+
333
+ # If the winding is inward, flip signs
334
+ if V_tot < 0:
335
+ V_tot = -V_tot
336
+ F_tot = -F_tot
337
+ S_tot = -S_tot
338
+
339
+ mass = density * V_tot
340
+ if V_tot > 0.0:
341
+ com = F_tot / V_tot
342
+ else:
343
+ com = F_tot
344
+
345
+ S_tot *= density # include density
346
+ I_origin = np.trace(S_tot) * np.eye(3) - S_tot # inertia about origin
347
+ r = com
348
+ I_com = I_origin - mass * ((r @ r) * np.eye(3) - np.outer(r, r))
349
+
350
+ return mass, wp.vec3(*com), wp.mat33(*I_com), V_tot
318
351
 
319
352
 
320
- def transform_inertia(m, I, p, q):
353
+ def transform_inertia(m, I, p, q) -> wp.mat33:
321
354
  R = wp.quat_to_matrix(q)
322
355
 
323
356
  # Steiner's theorem
@@ -42,6 +42,9 @@ def eval_springs(
42
42
  i = spring_indices[tid * 2 + 0]
43
43
  j = spring_indices[tid * 2 + 1]
44
44
 
45
+ if i == -1 or j == -1:
46
+ return
47
+
45
48
  ke = spring_stiffness[tid]
46
49
  kd = spring_damping[tid]
47
50
  rest = spring_rest_lengths[tid]
@@ -559,7 +562,7 @@ def eval_tetrahedra(
559
562
  v20 = v2 - v0
560
563
  v30 = v3 - v0
561
564
 
562
- Ds = wp.mat33(x10, x20, x30)
565
+ Ds = wp.matrix_from_cols(x10, x20, x30)
563
566
  Dm = pose[tid]
564
567
 
565
568
  inv_rest_volume = wp.determinant(Dm) * 6.0
@@ -574,7 +577,7 @@ def eval_tetrahedra(
574
577
 
575
578
  # F = Xs*Xm^-1
576
579
  F = Ds * Dm
577
- dFdt = wp.mat33(v10, v20, v30) * Dm
580
+ dFdt = wp.matrix_from_cols(v10, v20, v30) * Dm
578
581
 
579
582
  col1 = wp.vec3(F[0, 0], F[1, 0], F[2, 0])
580
583
  col2 = wp.vec3(F[0, 1], F[1, 1], F[2, 1])
@@ -660,9 +663,9 @@ def eval_tetrahedra(
660
663
 
661
664
  # alpha = 1.0
662
665
 
663
- # I = wp.mat33(wp.vec3(1.0, 0.0, 0.0),
664
- # wp.vec3(0.0, 1.0, 0.0),
665
- # wp.vec3(0.0, 0.0, 1.0))
666
+ # I = wp.matrix_from_cols(wp.vec3(1.0, 0.0, 0.0),
667
+ # wp.vec3(0.0, 1.0, 0.0),
668
+ # wp.vec3(0.0, 0.0, 1.0))
666
669
 
667
670
  # P = (F + wp.transpose(F) + I*(0.0-2.0))*k_mu
668
671
  # H = P * wp.transpose(Dm)
@@ -128,7 +128,7 @@ def spatial_transform_inertia(t: wp.transform, I: wp.spatial_matrix):
128
128
  r2 = wp.quat_rotate(q, wp.vec3(0.0, 1.0, 0.0))
129
129
  r3 = wp.quat_rotate(q, wp.vec3(0.0, 0.0, 1.0))
130
130
 
131
- R = wp.mat33(r1, r2, r3)
131
+ R = wp.matrix_from_cols(r1, r2, r3)
132
132
  S = wp.skew(p) @ R
133
133
 
134
134
  T = spatial_adjoint(R, S)
@@ -298,7 +298,7 @@ def jcalc_motion(
298
298
  if type == wp.sim.JOINT_UNIVERSAL:
299
299
  axis_0 = joint_axis[axis_start + 0]
300
300
  axis_1 = joint_axis[axis_start + 1]
301
- q_off = wp.quat_from_matrix(wp.mat33(axis_0, axis_1, wp.cross(axis_0, axis_1)))
301
+ q_off = wp.quat_from_matrix(wp.matrix_from_cols(axis_0, axis_1, wp.cross(axis_0, axis_1)))
302
302
  local_0 = wp.quat_rotate(q_off, wp.vec3(1.0, 0.0, 0.0))
303
303
  local_1 = wp.quat_rotate(q_off, wp.vec3(0.0, 1.0, 0.0))
304
304
 
@@ -319,7 +319,7 @@ def jcalc_motion(
319
319
  axis_0 = joint_axis[axis_start + 0]
320
320
  axis_1 = joint_axis[axis_start + 1]
321
321
  axis_2 = joint_axis[axis_start + 2]
322
- q_off = wp.quat_from_matrix(wp.mat33(axis_0, axis_1, axis_2))
322
+ q_off = wp.quat_from_matrix(wp.matrix_from_cols(axis_0, axis_1, axis_2))
323
323
  local_0 = wp.quat_rotate(q_off, wp.vec3(1.0, 0.0, 0.0))
324
324
  local_1 = wp.quat_rotate(q_off, wp.vec3(0.0, 1.0, 0.0))
325
325
  local_2 = wp.quat_rotate(q_off, wp.vec3(0.0, 0.0, 1.0))
@@ -2066,13 +2066,6 @@ class FeatherstoneIntegrator(Integrator):
2066
2066
  ],
2067
2067
  device=model.device,
2068
2068
  )
2069
- # if wp.context.runtime.tape:
2070
- # wp.context.runtime.tape.record_func(
2071
- # backward=lambda: adj_matmul(
2072
- # a, b, c, a.grad, b.grad, c.grad, d.grad, alpha, beta, allow_tf32x3_arith, device
2073
- # ),
2074
- # arrays=[a, b, c, d],
2075
- # )
2076
2069
  # print("joint_qdd:")
2077
2070
  # print(state_aug.joint_qdd.numpy())
2078
2071
  # print("\n\n")