warp-lang 0.15.0__py3-none-manylinux2014_x86_64.whl → 1.0.0__py3-none-manylinux2014_x86_64.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 (80) hide show
  1. warp/__init__.py +1 -0
  2. warp/codegen.py +7 -3
  3. warp/config.py +2 -1
  4. warp/constants.py +3 -0
  5. warp/context.py +44 -21
  6. warp/examples/assets/bunny.usd +0 -0
  7. warp/examples/assets/cartpole.urdf +110 -0
  8. warp/examples/assets/crazyflie.usd +0 -0
  9. warp/examples/assets/cube.usda +42 -0
  10. warp/examples/assets/nv_ant.xml +92 -0
  11. warp/examples/assets/nv_humanoid.xml +183 -0
  12. warp/examples/assets/quadruped.urdf +268 -0
  13. warp/examples/assets/rocks.nvdb +0 -0
  14. warp/examples/assets/rocks.usd +0 -0
  15. warp/examples/assets/sphere.usda +56 -0
  16. warp/examples/assets/torus.usda +105 -0
  17. warp/examples/core/example_dem.py +6 -6
  18. warp/examples/core/example_fluid.py +3 -3
  19. warp/examples/core/example_graph_capture.py +3 -6
  20. warp/examples/optim/example_bounce.py +9 -8
  21. warp/examples/optim/example_cloth_throw.py +12 -8
  22. warp/examples/optim/example_diffray.py +10 -12
  23. warp/examples/optim/example_drone.py +31 -14
  24. warp/examples/optim/example_spring_cage.py +10 -15
  25. warp/examples/optim/example_trajectory.py +7 -24
  26. warp/examples/sim/example_cartpole.py +3 -9
  27. warp/examples/sim/example_cloth.py +10 -10
  28. warp/examples/sim/example_granular.py +3 -3
  29. warp/examples/sim/example_granular_collision_sdf.py +9 -4
  30. warp/examples/sim/example_jacobian_ik.py +0 -10
  31. warp/examples/sim/example_particle_chain.py +4 -4
  32. warp/examples/sim/example_quadruped.py +15 -11
  33. warp/examples/sim/example_rigid_chain.py +13 -8
  34. warp/examples/sim/example_rigid_contact.py +4 -4
  35. warp/examples/sim/example_rigid_force.py +7 -7
  36. warp/examples/sim/example_rigid_soft_contact.py +4 -4
  37. warp/examples/sim/example_soft_body.py +3 -3
  38. warp/jax.py +45 -0
  39. warp/jax_experimental.py +339 -0
  40. warp/render/render_opengl.py +188 -95
  41. warp/render/render_usd.py +34 -10
  42. warp/sim/__init__.py +13 -4
  43. warp/sim/articulation.py +4 -5
  44. warp/sim/collide.py +320 -175
  45. warp/sim/import_mjcf.py +25 -30
  46. warp/sim/import_urdf.py +94 -63
  47. warp/sim/import_usd.py +51 -36
  48. warp/sim/inertia.py +3 -2
  49. warp/sim/integrator.py +233 -0
  50. warp/sim/integrator_euler.py +447 -469
  51. warp/sim/integrator_featherstone.py +1991 -0
  52. warp/sim/integrator_xpbd.py +1420 -640
  53. warp/sim/model.py +741 -487
  54. warp/sim/particles.py +2 -1
  55. warp/sim/render.py +18 -2
  56. warp/sim/utils.py +222 -11
  57. warp/stubs.py +1 -0
  58. warp/tape.py +6 -9
  59. warp/tests/test_examples.py +87 -20
  60. warp/tests/test_grad_customs.py +122 -0
  61. warp/tests/test_jax.py +254 -0
  62. warp/tests/test_options.py +13 -53
  63. warp/tests/test_quat.py +23 -0
  64. warp/tests/test_snippet.py +2 -0
  65. warp/tests/test_utils.py +31 -26
  66. warp/tests/test_verify_fp.py +65 -0
  67. warp/tests/unittest_suites.py +4 -0
  68. warp/utils.py +50 -1
  69. {warp_lang-0.15.0.dist-info → warp_lang-1.0.0.dist-info}/METADATA +1 -1
  70. {warp_lang-0.15.0.dist-info → warp_lang-1.0.0.dist-info}/RECORD +73 -64
  71. warp/examples/env/__init__.py +0 -0
  72. warp/examples/env/env_ant.py +0 -61
  73. warp/examples/env/env_cartpole.py +0 -63
  74. warp/examples/env/env_humanoid.py +0 -65
  75. warp/examples/env/env_usd.py +0 -97
  76. warp/examples/env/environment.py +0 -526
  77. warp/sim/optimizer.py +0 -138
  78. {warp_lang-0.15.0.dist-info → warp_lang-1.0.0.dist-info}/LICENSE.md +0 -0
  79. {warp_lang-0.15.0.dist-info → warp_lang-1.0.0.dist-info}/WHEEL +0 -0
  80. {warp_lang-0.15.0.dist-info → warp_lang-1.0.0.dist-info}/top_level.txt +0 -0
warp/sim/particles.py CHANGED
@@ -11,6 +11,7 @@ from .model import PARTICLE_FLAG_ACTIVE
11
11
 
12
12
  @wp.func
13
13
  def particle_force(n: wp.vec3, v: wp.vec3, c: float, k_n: float, k_d: float, k_f: float, k_mu: float):
14
+ # compute normal and tangential friction force for a single contact
14
15
  vn = wp.dot(n, v)
15
16
  jn = c * k_n
16
17
  jd = min(vn, 0.0) * k_d
@@ -89,7 +90,7 @@ def eval_particle_forces_kernel(
89
90
 
90
91
 
91
92
  def eval_particle_forces(model, state, forces):
92
- if model.particle_max_radius > 0.0:
93
+ if model.particle_count > 1 and model.particle_max_radius > 0.0:
93
94
  wp.launch(
94
95
  kernel=eval_particle_forces_kernel,
95
96
  dim=model.particle_count,
warp/sim/render.py CHANGED
@@ -23,6 +23,7 @@ NAN = wp.constant(-1.0e8)
23
23
  def compute_contact_points(
24
24
  body_q: wp.array(dtype=wp.transform),
25
25
  shape_body: wp.array(dtype=int),
26
+ contact_count: wp.array(dtype=int),
26
27
  contact_shape0: wp.array(dtype=int),
27
28
  contact_shape1: wp.array(dtype=int),
28
29
  contact_point0: wp.array(dtype=wp.vec3),
@@ -32,6 +33,11 @@ def compute_contact_points(
32
33
  contact_pos1: wp.array(dtype=wp.vec3),
33
34
  ):
34
35
  tid = wp.tid()
36
+ count = contact_count[0]
37
+ if tid >= count:
38
+ contact_pos0[tid] = wp.vec3(NAN, NAN, NAN)
39
+ contact_pos1[tid] = wp.vec3(NAN, NAN, NAN)
40
+ return
35
41
  shape_a = contact_shape0[tid]
36
42
  shape_b = contact_shape1[tid]
37
43
  if shape_a == shape_b:
@@ -124,6 +130,7 @@ def CreateSimRenderer(renderer):
124
130
  shape_geo_thickness = model.shape_geo.thickness.numpy()
125
131
  shape_geo_is_solid = model.shape_geo.is_solid.numpy()
126
132
  shape_transform = model.shape_transform.numpy()
133
+ shape_visible = model.shape_visible.numpy()
127
134
 
128
135
  p = np.zeros(3, dtype=np.float32)
129
136
  q = np.array([0.0, 0.0, 0.0, 1.0], dtype=np.float32)
@@ -211,7 +218,9 @@ def CreateSimRenderer(renderer):
211
218
 
212
219
  self.geo_shape[geo_hash] = shape
213
220
 
214
- self.add_shape_instance(name, shape, body, X_bs.p, X_bs.q, scale)
221
+ if shape_visible[s]:
222
+ # TODO support dynamic visibility
223
+ self.add_shape_instance(name, shape, body, X_bs.p, X_bs.q, scale, custom_index=s, visible=shape_visible[s])
215
224
  self.instance_count += 1
216
225
 
217
226
  if self.show_joints and model.joint_count:
@@ -275,7 +284,7 @@ def CreateSimRenderer(renderer):
275
284
  self.instance_count += 1
276
285
 
277
286
  if model.ground:
278
- self.render_ground()
287
+ self.render_ground(plane=model.ground_plane_params)
279
288
 
280
289
  if hasattr(self, "complete_setup"):
281
290
  self.complete_setup()
@@ -284,6 +293,12 @@ def CreateSimRenderer(renderer):
284
293
  return tab10_color_map(self.instance_count)
285
294
 
286
295
  def render(self, state: warp.sim.State):
296
+ """
297
+ Updates the renderer with the given simulation state.
298
+
299
+ Args:
300
+ state (warp.sim.State): The simulation state to render.
301
+ """
287
302
  if self.skip_rendering:
288
303
  return
289
304
 
@@ -354,6 +369,7 @@ def CreateSimRenderer(renderer):
354
369
  inputs=[
355
370
  state.body_q,
356
371
  self.model.shape_body,
372
+ self.model.rigid_contact_count,
357
373
  self.model.rigid_contact_shape0,
358
374
  self.model.rigid_contact_shape1,
359
375
  self.model.rigid_contact_point0,
warp/sim/utils.py CHANGED
@@ -1,13 +1,20 @@
1
1
  import warp as wp
2
+ import numpy as np
2
3
 
3
- PI = wp.constant(3.14159265359)
4
- PI_2 = wp.constant(1.57079632679)
4
+ from typing import Tuple, List
5
5
 
6
6
 
7
7
  @wp.func
8
8
  def velocity_at_point(qd: wp.spatial_vector, r: wp.vec3):
9
9
  """
10
10
  Returns the velocity of a point relative to the frame with the given spatial velocity.
11
+
12
+ Args:
13
+ qd (spatial_vector): The spatial velocity of the frame.
14
+ r (vec3): The position of the point relative to the frame.
15
+
16
+ Returns:
17
+ vec3: The velocity of the point.
11
18
  """
12
19
  return wp.cross(wp.spatial_top(qd), r) + wp.spatial_bottom(qd)
13
20
 
@@ -52,7 +59,7 @@ def quat_decompose(q: wp.quat):
52
59
  phi = wp.atan2(R[1, 2], R[2, 2])
53
60
  sinp = -R[0, 2]
54
61
  if wp.abs(sinp) >= 1.0:
55
- theta = 1.57079632679 * wp.sign(sinp)
62
+ theta = wp.HALF_PI * wp.sign(sinp)
56
63
  else:
57
64
  theta = wp.asin(-R[0, 2])
58
65
  psi = wp.atan2(R[0, 1], R[0, 0])
@@ -63,7 +70,7 @@ def quat_decompose(q: wp.quat):
63
70
  @wp.func
64
71
  def quat_to_rpy(q: wp.quat):
65
72
  """
66
- Convert a quaternion into euler angles (roll, pitch, yaw)
73
+ Convert a quaternion into Euler angles (roll, pitch, yaw)
67
74
  roll is rotation around x in radians (counterclockwise)
68
75
  pitch is rotation around y in radians (counterclockwise)
69
76
  yaw is rotation around z in radians (counterclockwise)
@@ -90,11 +97,27 @@ def quat_to_rpy(q: wp.quat):
90
97
  @wp.func
91
98
  def quat_to_euler(q: wp.quat, i: int, j: int, k: int) -> wp.vec3:
92
99
  """
93
- Convert a quaternion into euler angles
94
- i, j, k are the indices in [1,2,3] of the axes to use
95
- (i != j, j != k)
100
+ Convert a quaternion into Euler angles.
101
+
102
+ :math:`i, j, k` are the indices in :math:`[0, 1, 2]` of the axes to use
103
+ (:math:`i \\neq j, j \\neq k`).
104
+
105
+ Reference: https://journals.plos.org/plosone/article?id=10.1371/journal.pone.0276302
106
+
107
+ Args:
108
+ q (quat): The quaternion to convert
109
+ i (int): The index of the first axis
110
+ j (int): The index of the second axis
111
+ k (int): The index of the third axis
112
+
113
+ Returns:
114
+ vec3: The Euler angles (in radians)
96
115
  """
97
- # https://journals.plos.org/plosone/article?id=10.1371/journal.pone.0276302
116
+ # i, j, k are actually assumed to follow 1-based indexing but
117
+ # we want to be compatible with quat_from_euler
118
+ i += 1
119
+ j += 1
120
+ k += 1
98
121
  not_proper = True
99
122
  if i == k:
100
123
  not_proper = False
@@ -116,17 +139,54 @@ def quat_to_euler(q: wp.quat, i: int, j: int, k: int) -> wp.vec3:
116
139
  t3 = 0.0
117
140
  if wp.abs(t2) < 1e-6:
118
141
  t3 = 2.0 * tp - t1
119
- elif wp.abs(t2 - PI_2) < 1e-6:
142
+ elif wp.abs(t2 - wp.HALF_PI) < 1e-6:
120
143
  t3 = 2.0 * tm + t1
121
144
  else:
122
145
  t1 = tp - tm
123
146
  t3 = tp + tm
124
147
  if not_proper:
125
- t2 -= PI_2
148
+ t2 -= wp.HALF_PI
126
149
  t3 *= e
127
150
  return wp.vec3(t1, t2, t3)
128
151
 
129
152
 
153
+ @wp.func
154
+ def quat_from_euler(e: wp.vec3, i: int, j: int, k: int) -> wp.quat:
155
+ """
156
+ Convert Euler angles to a quaternion.
157
+
158
+ :math:`i, j, k` are the indices in :math:`[0, 1, 2]` of the axes in which the Euler angles are provided
159
+ (:math:`i \\neq j, j \\neq k`), e.g. (0, 1, 2) for Euler sequence XYZ.
160
+
161
+ Args:
162
+ e (vec3): The Euler angles (in radians)
163
+ i (int): The index of the first axis
164
+ j (int): The index of the second axis
165
+ k (int): The index of the third axis
166
+
167
+ Returns:
168
+ quat: The quaternion
169
+ """
170
+ # Half angles
171
+ half_e = e / 2.0
172
+
173
+ # Precompute sines and cosines of half angles
174
+ cr = wp.cos(half_e[i])
175
+ sr = wp.sin(half_e[i])
176
+ cp = wp.cos(half_e[j])
177
+ sp = wp.sin(half_e[j])
178
+ cy = wp.cos(half_e[k])
179
+ sy = wp.sin(half_e[k])
180
+
181
+ # Components of the quaternion based on the rotation sequence
182
+ return wp.quat(
183
+ (cy * sr * cp - sy * cr * sp),
184
+ (cy * cr * sp + sy * sr * cp),
185
+ (sy * cr * cp - cy * sr * sp),
186
+ (cy * cr * cp + sy * sr * sp),
187
+ )
188
+
189
+
130
190
  @wp.func
131
191
  def transform_twist(t: wp.transform, x: wp.spatial_vector):
132
192
  # Frank & Park definition 3.20, pg 100
@@ -181,6 +241,35 @@ def transform_inertia(t: wp.transform, I: wp.spatial_matrix):
181
241
  return wp.mul(wp.mul(wp.transpose(T), I), T)
182
242
 
183
243
 
244
+ @wp.func
245
+ def boltzmann(a: float, b: float, alpha: float):
246
+ e1 = wp.exp(alpha * a)
247
+ e2 = wp.exp(alpha * b)
248
+ return (a * e1 + b * e2) / (e1 + e2)
249
+
250
+
251
+ @wp.func
252
+ def smooth_max(a: float, b: float, eps: float):
253
+ d = a - b
254
+ return 0.5 * (a + b + wp.sqrt(d * d + eps))
255
+
256
+
257
+ @wp.func
258
+ def smooth_min(a: float, b: float, eps: float):
259
+ d = a - b
260
+ return 0.5 * (a + b - wp.sqrt(d * d + eps))
261
+
262
+
263
+ @wp.func
264
+ def leaky_max(a: float, b: float):
265
+ return smooth_max(a, b, 1e-5)
266
+
267
+
268
+ @wp.func
269
+ def leaky_min(a: float, b: float):
270
+ return smooth_min(a, b, 1e-5)
271
+
272
+
184
273
  @wp.func
185
274
  def vec_min(a: wp.vec3, b: wp.vec3):
186
275
  return wp.vec3(wp.min(a[0], b[0]), wp.min(a[1], b[1]), wp.min(a[2], b[2]))
@@ -191,9 +280,131 @@ def vec_max(a: wp.vec3, b: wp.vec3):
191
280
  return wp.vec3(wp.max(a[0], b[0]), wp.max(a[1], b[1]), wp.max(a[2], b[2]))
192
281
 
193
282
 
283
+ @wp.func
284
+ def vec_leaky_min(a: wp.vec3, b: wp.vec3):
285
+ return wp.vec3(leaky_min(a[0], b[0]), leaky_min(a[1], b[1]), leaky_min(a[2], b[2]))
286
+
287
+
288
+ @wp.func
289
+ def vec_leaky_max(a: wp.vec3, b: wp.vec3):
290
+ return wp.vec3(leaky_max(a[0], b[0]), leaky_max(a[1], b[1]), leaky_max(a[2], b[2]))
291
+
292
+
194
293
  @wp.func
195
294
  def vec_abs(a: wp.vec3):
196
295
  return wp.vec3(wp.abs(a[0]), wp.abs(a[1]), wp.abs(a[2]))
197
296
 
198
297
 
199
-
298
+ def load_mesh(filename: str, method: str = None):
299
+ """
300
+ Loads a 3D triangular surface mesh from a file.
301
+
302
+ Args:
303
+ filename (str): The path to the 3D model file (obj, and other formats supported by the different methods) to load.
304
+ method (str): The method to use for loading the mesh (default None). Can be either `"trimesh"`, `"meshio"`, `"pcu"`, or `"openmesh"`. If None, every method is tried and the first successful mesh import where the number of vertices is greater than 0 is returned.
305
+
306
+ Returns:
307
+ Tuple of (mesh_points, mesh_indices), where mesh_points is a Nx3 numpy array of vertex positions (float32),
308
+ and mesh_indices is a Mx3 numpy array of vertex indices (int32) for the triangular faces.
309
+ """
310
+ import os
311
+
312
+ if not os.path.exists(filename):
313
+ raise ValueError(f"File not found: {filename}")
314
+
315
+ def load_mesh_with_method(method):
316
+ if method == "meshio":
317
+ import meshio
318
+
319
+ m = meshio.read(filename)
320
+ mesh_points = np.array(m.points)
321
+ mesh_indices = np.array(m.cells[0].data, dtype=np.int32)
322
+ elif method == "openmesh":
323
+ import openmesh
324
+
325
+ m = openmesh.read_trimesh(filename)
326
+ mesh_points = np.array(m.points())
327
+ mesh_indices = np.array(m.face_vertex_indices(), dtype=np.int32)
328
+ elif method == "pcu":
329
+ import point_cloud_utils as pcu
330
+
331
+ mesh_points, mesh_indices = pcu.load_mesh_vf(filename)
332
+ mesh_indices = mesh_indices.flatten()
333
+ else:
334
+ import trimesh
335
+
336
+ m = trimesh.load(filename)
337
+ if hasattr(m, "geometry"):
338
+ # multiple meshes are contained in a scene; combine to one mesh
339
+ mesh_points = []
340
+ mesh_indices = []
341
+ index_offset = 0
342
+ for geom in m.geometry.values():
343
+ vertices = np.array(geom.vertices, dtype=np.float32)
344
+ faces = np.array(geom.faces.flatten(), dtype=np.int32)
345
+ mesh_points.append(vertices)
346
+ mesh_indices.append(faces + index_offset)
347
+ index_offset += len(vertices)
348
+ mesh_points = np.concatenate(mesh_points, axis=0)
349
+ mesh_indices = np.concatenate(mesh_indices)
350
+ else:
351
+ # a single mesh
352
+ mesh_points = np.array(m.vertices, dtype=np.float32)
353
+ mesh_indices = np.array(m.faces.flatten(), dtype=np.int32)
354
+ return mesh_points, mesh_indices
355
+
356
+ if method is None:
357
+ methods = ["trimesh", "meshio", "pcu", "openmesh"]
358
+ for method in methods:
359
+ try:
360
+ mesh = load_mesh_with_method(method)
361
+ if mesh is not None and len(mesh[0]) > 0:
362
+ return mesh
363
+ except Exception:
364
+ pass
365
+ raise ValueError(f"Failed to load mesh using any of the methods: {methods}")
366
+ else:
367
+ mesh = load_mesh_with_method(method)
368
+ if mesh is None or len(mesh[0]) == 0:
369
+ raise ValueError(f"Failed to load mesh using method {method}")
370
+ return mesh
371
+
372
+
373
+ def visualize_meshes(
374
+ meshes: List[Tuple[list, list]], num_cols=0, num_rows=0, titles=[], scale_axes=True, show_plot=True
375
+ ):
376
+ # render meshes in a grid with matplotlib
377
+ import matplotlib.pyplot as plt
378
+ from mpl_toolkits.mplot3d import Axes3D
379
+
380
+ num_cols = min(num_cols, len(meshes))
381
+ num_rows = min(num_rows, len(meshes))
382
+ if num_cols and not num_rows:
383
+ num_rows = int(np.ceil(len(meshes) / num_cols))
384
+ elif num_rows and not num_cols:
385
+ num_cols = int(np.ceil(len(meshes) / num_rows))
386
+ else:
387
+ num_cols = len(meshes)
388
+ num_rows = 1
389
+
390
+ vertices = [np.array(v).reshape((-1, 3)) for v, _ in meshes]
391
+ faces = [np.array(f, dtype=np.int32).reshape((-1, 3)) for _, f in meshes]
392
+ if scale_axes:
393
+ ranges = np.array([v.max(axis=0) - v.min(axis=0) for v in vertices])
394
+ max_range = ranges.max()
395
+ mid_points = np.array([v.max(axis=0) + v.min(axis=0) for v in vertices]) * 0.5
396
+
397
+ fig = plt.figure(figsize=(12, 6))
398
+ for i, (vertices, faces) in enumerate(meshes):
399
+ ax = fig.add_subplot(num_rows, num_cols, i + 1, projection="3d")
400
+ if i < len(titles):
401
+ ax.set_title(titles[i])
402
+ ax.plot_trisurf(vertices[:, 0], vertices[:, 1], vertices[:, 2], triangles=faces, edgecolor="k")
403
+ if scale_axes:
404
+ mid = mid_points[i]
405
+ ax.set_xlim(mid[0] - max_range, mid[0] + max_range)
406
+ ax.set_ylim(mid[1] - max_range, mid[1] + max_range)
407
+ ax.set_zlim(mid[2] - max_range, mid[2] + max_range)
408
+ if show_plot:
409
+ plt.show()
410
+ return fig
warp/stubs.py CHANGED
@@ -84,6 +84,7 @@ from warp.context import is_peer_access_supported, is_peer_access_enabled, set_p
84
84
  from warp.tape import Tape
85
85
  from warp.utils import ScopedTimer, ScopedDevice, ScopedStream
86
86
  from warp.utils import ScopedMempool, ScopedMempoolAccess, ScopedPeerAccess
87
+ from warp.utils import ScopedCapture
87
88
  from warp.utils import transform_expand, quat_between_vectors
88
89
 
89
90
  from warp.torch import from_torch, to_torch
warp/tape.py CHANGED
@@ -95,16 +95,17 @@ class Tape:
95
95
  # existing code before we added wp.array.grad attribute
96
96
  if grads:
97
97
  for a, g in grads.items():
98
- a.grad = g
98
+ if a.grad is None:
99
+ a.grad = g
100
+ else:
101
+ # ensure we can capture this backward pass in a CUDA graph
102
+ a.grad.assign(g)
99
103
  self.const_gradients.add(a)
100
104
 
101
105
  # run launches backwards
102
106
  for launch in reversed(self.launches):
103
107
  if callable(launch):
104
- module_enable_backward = wp.get_module(launch.__module__).options.get("enable_backward")
105
- if module_enable_backward is False:
106
- msg = f"Running the tape backwards may produce incorrect gradients because recorded kernel {launch.__name__} is defined in a module with the option 'enable_backward=False' set."
107
- wp.utils.warn(msg)
108
+ launch()
108
109
 
109
110
  else:
110
111
  # kernel option takes precedence over module option
@@ -118,10 +119,6 @@ class Tape:
118
119
  msg = f"Running the tape backwards may produce incorrect gradients because recorded kernel {launch[0].key} is defined in a module with the option 'enable_backward=False' set."
119
120
  wp.utils.warn(msg)
120
121
 
121
- if callable(launch):
122
- launch()
123
-
124
- else:
125
122
  kernel = launch[0]
126
123
  dim = launch[1]
127
124
  max_blocks = launch[2]
@@ -128,29 +128,42 @@ class TestSimExamples(unittest.TestCase):
128
128
  pass
129
129
 
130
130
 
131
+ warp_sim_modules = [
132
+ "warp.sim.integrator",
133
+ "warp.sim.integrator_euler",
134
+ "warp.sim.particles",
135
+ "warp.sim.collide",
136
+ "warp.sim.articulation",
137
+ "warp.sim.integrator_xpbd",
138
+ "warp.sim.integrator_featherstone",
139
+ "warp.sim.integrator_euler",
140
+ "warp.sim.integrator",
141
+ "warp.sim.utils",
142
+ ]
143
+
131
144
  add_example_test(
132
145
  TestSimExamples,
133
146
  name="optim.example_bounce",
134
147
  devices=cuda_test_devices,
135
- options={"load_modules": ["warp.sim.integrator_euler", "warp.sim.particles"]},
148
+ options={"load_modules": warp_sim_modules},
136
149
  )
137
150
  add_example_test(
138
151
  TestSimExamples,
139
152
  name="sim.example_cartpole",
140
153
  devices=cuda_test_devices,
141
- options={"load_modules": ["warp.sim.collide", "warp.sim.integrator_euler", "warp.sim.articulation"]},
154
+ options={"load_modules": warp_sim_modules},
142
155
  )
143
156
  add_example_test(
144
157
  TestSimExamples,
145
158
  name="sim.example_cloth",
146
159
  devices=cuda_test_devices,
147
- options={"load_modules": ["warp.sim.collide", "warp.sim.integrator_euler", "warp.sim.particles"]},
160
+ options={"load_modules": warp_sim_modules},
148
161
  )
149
162
  add_example_test(
150
163
  TestSimExamples,
151
164
  name="optim.example_cloth_throw",
152
165
  devices=cuda_test_devices,
153
- options={"load_modules": ["warp.sim.integrator_euler", "warp.sim.particles"]},
166
+ options={"load_modules": warp_sim_modules},
154
167
  )
155
168
  add_example_test(TestSimExamples, name="optim.example_diffray", devices=cuda_test_devices)
156
169
  add_example_test(
@@ -158,51 +171,105 @@ add_example_test(
158
171
  name="optim.example_drone",
159
172
  devices=cuda_test_devices,
160
173
  options={
161
- "load_modules": ["warp.sim.collide", "warp.sim.integrator_euler", "warp.optim.sgd"],
174
+ "load_modules": warp_sim_modules + ["warp.optim.sgd"],
162
175
  "num_frames": 1,
163
176
  "drone_path": os.path.join(os.path.dirname(__file__), "..", "examples", "assets", "crazyflie.usd"),
164
177
  },
165
178
  )
166
- add_example_test(TestSimExamples, name="sim.example_granular", devices=cuda_test_devices)
167
- add_example_test(TestSimExamples, name="sim.example_granular_collision_sdf", devices=cuda_test_devices)
168
- add_example_test(TestSimExamples, name="optim.example_inverse_kinematics", devices=cuda_test_devices)
179
+ add_example_test(
180
+ TestSimExamples,
181
+ name="sim.example_granular",
182
+ devices=cuda_test_devices,
183
+ options={
184
+ "load_modules": warp_sim_modules,
185
+ },
186
+ )
187
+ add_example_test(
188
+ TestSimExamples,
189
+ name="sim.example_granular_collision_sdf",
190
+ devices=cuda_test_devices,
191
+ options={
192
+ "load_modules": warp_sim_modules,
193
+ },
194
+ )
195
+ add_example_test(
196
+ TestSimExamples,
197
+ name="optim.example_inverse_kinematics",
198
+ devices=cuda_test_devices,
199
+ options={
200
+ "load_modules": warp_sim_modules,
201
+ },
202
+ )
169
203
  add_example_test(
170
204
  TestSimExamples,
171
205
  name="optim.example_inverse_kinematics_torch",
172
206
  devices=cuda_test_devices,
173
- options={"torch_cuda_required": True},
207
+ options={
208
+ "torch_cuda_required": True,
209
+ "load_modules": warp_sim_modules,
210
+ },
174
211
  )
175
212
  add_example_test(TestExamples, name="sim.example_jacobian_ik", devices=cuda_test_devices)
176
213
  add_example_test(
177
214
  TestSimExamples,
178
215
  name="sim.example_particle_chain",
179
216
  devices=cuda_test_devices,
180
- options={"load_modules": ["warp.sim.integrator_xpbd"]},
217
+ options={"load_modules": warp_sim_modules},
181
218
  )
182
219
  add_example_test(
183
220
  TestSimExamples,
184
221
  name="sim.example_quadruped",
185
222
  devices=cuda_test_devices,
186
- options={"load_modules": ["warp.sim.integrator_xpbd", "warp.sim.integrator_euler"]},
223
+ options={"load_modules": warp_sim_modules},
187
224
  )
188
225
  add_example_test(
189
226
  TestSimExamples,
190
227
  name="sim.example_rigid_chain",
191
228
  devices=cuda_test_devices,
192
- options={"load_modules": ["warp.sim.integrator_xpbd", "warp.sim.integrator_euler"]},
229
+ options={"load_modules": warp_sim_modules},
193
230
  )
194
231
  add_example_test(
195
232
  TestSimExamples,
196
233
  name="sim.example_rigid_contact",
197
234
  devices=cuda_test_devices,
198
- options={"load_modules": ["warp.sim.integrator_euler"]},
199
- )
200
- add_example_test(TestSimExamples, name="sim.example_rigid_soft_contact", devices=cuda_test_devices)
201
- add_example_test(TestSimExamples, name="sim.example_rigid_force", devices=cuda_test_devices)
202
- add_example_test(TestSimExamples, name="sim.example_rigid_gyroscopic", devices=cuda_test_devices)
203
- add_example_test(TestSimExamples, name="sim.example_soft_body", devices=cuda_test_devices)
204
- add_example_test(TestSimExamples, name="optim.example_spring_cage", devices=cuda_test_devices)
205
- add_example_test(TestSimExamples, name="optim.example_trajectory", devices=cuda_test_devices)
235
+ options={"load_modules": warp_sim_modules},
236
+ )
237
+ add_example_test(
238
+ TestSimExamples,
239
+ name="sim.example_rigid_soft_contact",
240
+ devices=cuda_test_devices,
241
+ options={"load_modules": warp_sim_modules},
242
+ )
243
+ add_example_test(
244
+ TestSimExamples,
245
+ name="sim.example_rigid_force",
246
+ devices=cuda_test_devices,
247
+ options={"load_modules": warp_sim_modules},
248
+ )
249
+ add_example_test(
250
+ TestSimExamples,
251
+ name="sim.example_rigid_gyroscopic",
252
+ devices=cuda_test_devices,
253
+ options={"load_modules": warp_sim_modules},
254
+ )
255
+ add_example_test(
256
+ TestSimExamples,
257
+ name="sim.example_soft_body",
258
+ devices=cuda_test_devices,
259
+ options={"load_modules": warp_sim_modules},
260
+ )
261
+ add_example_test(
262
+ TestSimExamples,
263
+ name="optim.example_spring_cage",
264
+ devices=cuda_test_devices,
265
+ options={"load_modules": warp_sim_modules},
266
+ )
267
+ add_example_test(
268
+ TestSimExamples,
269
+ name="optim.example_trajectory",
270
+ devices=cuda_test_devices,
271
+ options={"load_modules": warp_sim_modules},
272
+ )
206
273
 
207
274
 
208
275
  class TestFemExamples(unittest.TestCase):