warp-lang 0.11.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 (170) hide show
  1. warp/__init__.py +8 -0
  2. warp/bin/warp-clang.so +0 -0
  3. warp/bin/warp.so +0 -0
  4. warp/build.py +7 -6
  5. warp/build_dll.py +70 -79
  6. warp/builtins.py +10 -6
  7. warp/codegen.py +51 -19
  8. warp/config.py +7 -8
  9. warp/constants.py +3 -0
  10. warp/context.py +948 -245
  11. warp/dlpack.py +198 -113
  12. warp/examples/assets/bunny.usd +0 -0
  13. warp/examples/assets/cartpole.urdf +110 -0
  14. warp/examples/assets/crazyflie.usd +0 -0
  15. warp/examples/assets/cube.usda +42 -0
  16. warp/examples/assets/nv_ant.xml +92 -0
  17. warp/examples/assets/nv_humanoid.xml +183 -0
  18. warp/examples/assets/quadruped.urdf +268 -0
  19. warp/examples/assets/rocks.nvdb +0 -0
  20. warp/examples/assets/rocks.usd +0 -0
  21. warp/examples/assets/sphere.usda +56 -0
  22. warp/examples/assets/torus.usda +105 -0
  23. warp/examples/benchmarks/benchmark_api.py +383 -0
  24. warp/examples/benchmarks/benchmark_cloth.py +279 -0
  25. warp/examples/benchmarks/benchmark_cloth_cupy.py +88 -0
  26. warp/examples/benchmarks/benchmark_cloth_jax.py +100 -0
  27. warp/examples/benchmarks/benchmark_cloth_numba.py +142 -0
  28. warp/examples/benchmarks/benchmark_cloth_numpy.py +77 -0
  29. warp/examples/benchmarks/benchmark_cloth_pytorch.py +86 -0
  30. warp/examples/benchmarks/benchmark_cloth_taichi.py +112 -0
  31. warp/examples/benchmarks/benchmark_cloth_warp.py +146 -0
  32. warp/examples/benchmarks/benchmark_launches.py +295 -0
  33. warp/examples/core/example_dem.py +221 -0
  34. warp/examples/core/example_fluid.py +267 -0
  35. warp/examples/core/example_graph_capture.py +129 -0
  36. warp/examples/core/example_marching_cubes.py +177 -0
  37. warp/examples/core/example_mesh.py +154 -0
  38. warp/examples/core/example_mesh_intersect.py +193 -0
  39. warp/examples/core/example_nvdb.py +169 -0
  40. warp/examples/core/example_raycast.py +89 -0
  41. warp/examples/core/example_raymarch.py +178 -0
  42. warp/examples/core/example_render_opengl.py +141 -0
  43. warp/examples/core/example_sph.py +389 -0
  44. warp/examples/core/example_torch.py +181 -0
  45. warp/examples/core/example_wave.py +249 -0
  46. warp/examples/fem/bsr_utils.py +380 -0
  47. warp/examples/fem/example_apic_fluid.py +391 -0
  48. warp/examples/fem/example_convection_diffusion.py +168 -0
  49. warp/examples/fem/example_convection_diffusion_dg.py +209 -0
  50. warp/examples/fem/example_convection_diffusion_dg0.py +194 -0
  51. warp/examples/fem/example_deformed_geometry.py +159 -0
  52. warp/examples/fem/example_diffusion.py +173 -0
  53. warp/examples/fem/example_diffusion_3d.py +152 -0
  54. warp/examples/fem/example_diffusion_mgpu.py +214 -0
  55. warp/examples/fem/example_mixed_elasticity.py +222 -0
  56. warp/examples/fem/example_navier_stokes.py +243 -0
  57. warp/examples/fem/example_stokes.py +192 -0
  58. warp/examples/fem/example_stokes_transfer.py +249 -0
  59. warp/examples/fem/mesh_utils.py +109 -0
  60. warp/examples/fem/plot_utils.py +287 -0
  61. warp/examples/optim/example_bounce.py +248 -0
  62. warp/examples/optim/example_cloth_throw.py +210 -0
  63. warp/examples/optim/example_diffray.py +535 -0
  64. warp/examples/optim/example_drone.py +850 -0
  65. warp/examples/optim/example_inverse_kinematics.py +169 -0
  66. warp/examples/optim/example_inverse_kinematics_torch.py +170 -0
  67. warp/examples/optim/example_spring_cage.py +234 -0
  68. warp/examples/optim/example_trajectory.py +201 -0
  69. warp/examples/sim/example_cartpole.py +128 -0
  70. warp/examples/sim/example_cloth.py +184 -0
  71. warp/examples/sim/example_granular.py +113 -0
  72. warp/examples/sim/example_granular_collision_sdf.py +185 -0
  73. warp/examples/sim/example_jacobian_ik.py +213 -0
  74. warp/examples/sim/example_particle_chain.py +106 -0
  75. warp/examples/sim/example_quadruped.py +179 -0
  76. warp/examples/sim/example_rigid_chain.py +191 -0
  77. warp/examples/sim/example_rigid_contact.py +176 -0
  78. warp/examples/sim/example_rigid_force.py +126 -0
  79. warp/examples/sim/example_rigid_gyroscopic.py +97 -0
  80. warp/examples/sim/example_rigid_soft_contact.py +124 -0
  81. warp/examples/sim/example_soft_body.py +178 -0
  82. warp/fabric.py +29 -20
  83. warp/fem/cache.py +0 -1
  84. warp/fem/dirichlet.py +0 -2
  85. warp/fem/integrate.py +0 -1
  86. warp/jax.py +45 -0
  87. warp/jax_experimental.py +339 -0
  88. warp/native/builtin.h +12 -0
  89. warp/native/bvh.cu +18 -18
  90. warp/native/clang/clang.cpp +8 -3
  91. warp/native/cuda_util.cpp +94 -5
  92. warp/native/cuda_util.h +35 -6
  93. warp/native/cutlass_gemm.cpp +1 -1
  94. warp/native/cutlass_gemm.cu +4 -1
  95. warp/native/error.cpp +66 -0
  96. warp/native/error.h +27 -0
  97. warp/native/mesh.cu +2 -2
  98. warp/native/reduce.cu +4 -4
  99. warp/native/runlength_encode.cu +2 -2
  100. warp/native/scan.cu +2 -2
  101. warp/native/sparse.cu +0 -1
  102. warp/native/temp_buffer.h +2 -2
  103. warp/native/warp.cpp +95 -60
  104. warp/native/warp.cu +1053 -218
  105. warp/native/warp.h +49 -32
  106. warp/optim/linear.py +33 -16
  107. warp/render/render_opengl.py +202 -101
  108. warp/render/render_usd.py +82 -40
  109. warp/sim/__init__.py +13 -4
  110. warp/sim/articulation.py +4 -5
  111. warp/sim/collide.py +320 -175
  112. warp/sim/import_mjcf.py +25 -30
  113. warp/sim/import_urdf.py +94 -63
  114. warp/sim/import_usd.py +51 -36
  115. warp/sim/inertia.py +3 -2
  116. warp/sim/integrator.py +233 -0
  117. warp/sim/integrator_euler.py +447 -469
  118. warp/sim/integrator_featherstone.py +1991 -0
  119. warp/sim/integrator_xpbd.py +1420 -640
  120. warp/sim/model.py +765 -487
  121. warp/sim/particles.py +2 -1
  122. warp/sim/render.py +35 -13
  123. warp/sim/utils.py +222 -11
  124. warp/stubs.py +8 -0
  125. warp/tape.py +16 -1
  126. warp/tests/aux_test_grad_customs.py +23 -0
  127. warp/tests/test_array.py +190 -1
  128. warp/tests/test_async.py +656 -0
  129. warp/tests/test_bool.py +50 -0
  130. warp/tests/test_dlpack.py +164 -11
  131. warp/tests/test_examples.py +166 -74
  132. warp/tests/test_fem.py +8 -1
  133. warp/tests/test_generics.py +15 -5
  134. warp/tests/test_grad.py +1 -1
  135. warp/tests/test_grad_customs.py +172 -12
  136. warp/tests/test_jax.py +254 -0
  137. warp/tests/test_large.py +29 -6
  138. warp/tests/test_launch.py +25 -0
  139. warp/tests/test_linear_solvers.py +20 -3
  140. warp/tests/test_matmul.py +61 -16
  141. warp/tests/test_matmul_lite.py +13 -13
  142. warp/tests/test_mempool.py +186 -0
  143. warp/tests/test_multigpu.py +3 -0
  144. warp/tests/test_options.py +16 -2
  145. warp/tests/test_peer.py +137 -0
  146. warp/tests/test_print.py +3 -1
  147. warp/tests/test_quat.py +23 -0
  148. warp/tests/test_sim_kinematics.py +97 -0
  149. warp/tests/test_snippet.py +126 -3
  150. warp/tests/test_streams.py +108 -79
  151. warp/tests/test_torch.py +16 -8
  152. warp/tests/test_utils.py +32 -27
  153. warp/tests/test_verify_fp.py +65 -0
  154. warp/tests/test_volume.py +1 -1
  155. warp/tests/unittest_serial.py +2 -0
  156. warp/tests/unittest_suites.py +12 -0
  157. warp/tests/unittest_utils.py +14 -7
  158. warp/thirdparty/unittest_parallel.py +15 -3
  159. warp/torch.py +10 -8
  160. warp/types.py +363 -246
  161. warp/utils.py +143 -19
  162. warp_lang-1.0.0.dist-info/LICENSE.md +126 -0
  163. warp_lang-1.0.0.dist-info/METADATA +394 -0
  164. {warp_lang-0.11.0.dist-info → warp_lang-1.0.0.dist-info}/RECORD +167 -86
  165. warp/sim/optimizer.py +0 -138
  166. warp_lang-0.11.0.dist-info/LICENSE.md +0 -36
  167. warp_lang-0.11.0.dist-info/METADATA +0 -238
  168. /warp/tests/{walkthough_debug.py → walkthrough_debug.py} +0 -0
  169. {warp_lang-0.11.0.dist-info → warp_lang-1.0.0.dist-info}/WHEEL +0 -0
  170. {warp_lang-0.11.0.dist-info → warp_lang-1.0.0.dist-info}/top_level.txt +0 -0
@@ -12,112 +12,13 @@ models + state forward in time.
12
12
 
13
13
  import warp as wp
14
14
 
15
+ from .integrator import Integrator
15
16
  from .collide import triangle_closest_point_barycentric
16
- from .model import PARTICLE_FLAG_ACTIVE, ModelShapeGeometry, ModelShapeMaterials
17
- from .optimizer import Optimizer
17
+ from .model import Model, State, Control, PARTICLE_FLAG_ACTIVE, ModelShapeGeometry, ModelShapeMaterials
18
18
  from .particles import eval_particle_forces
19
19
  from .utils import quat_decompose, quat_twist
20
20
 
21
21
 
22
- @wp.kernel
23
- def integrate_particles(
24
- x: wp.array(dtype=wp.vec3),
25
- v: wp.array(dtype=wp.vec3),
26
- f: wp.array(dtype=wp.vec3),
27
- w: wp.array(dtype=float),
28
- particle_flags: wp.array(dtype=wp.uint32),
29
- gravity: wp.vec3,
30
- dt: float,
31
- v_max: float,
32
- x_new: wp.array(dtype=wp.vec3),
33
- v_new: wp.array(dtype=wp.vec3),
34
- ):
35
- tid = wp.tid()
36
- if (particle_flags[tid] & PARTICLE_FLAG_ACTIVE) == 0:
37
- return
38
-
39
- x0 = x[tid]
40
- v0 = v[tid]
41
- f0 = f[tid]
42
-
43
- inv_mass = w[tid]
44
-
45
- # simple semi-implicit Euler. v1 = v0 + a dt, x1 = x0 + v1 dt
46
- v1 = v0 + (f0 * inv_mass + gravity * wp.step(0.0 - inv_mass)) * dt
47
- # enforce velocity limit to prevent instability
48
- v1_mag = wp.length(v1)
49
- if v1_mag > v_max:
50
- v1 *= v_max / v1_mag
51
- x1 = x0 + v1 * dt
52
-
53
- x_new[tid] = x1
54
- v_new[tid] = v1
55
-
56
-
57
- # semi-implicit Euler integration
58
- @wp.kernel
59
- def integrate_bodies(
60
- body_q: wp.array(dtype=wp.transform),
61
- body_qd: wp.array(dtype=wp.spatial_vector),
62
- body_f: wp.array(dtype=wp.spatial_vector),
63
- body_com: wp.array(dtype=wp.vec3),
64
- m: wp.array(dtype=float),
65
- I: wp.array(dtype=wp.mat33),
66
- inv_m: wp.array(dtype=float),
67
- inv_I: wp.array(dtype=wp.mat33),
68
- gravity: wp.vec3,
69
- angular_damping: float,
70
- dt: float,
71
- # outputs
72
- body_q_new: wp.array(dtype=wp.transform),
73
- body_qd_new: wp.array(dtype=wp.spatial_vector),
74
- ):
75
- tid = wp.tid()
76
-
77
- # positions
78
- q = body_q[tid]
79
- qd = body_qd[tid]
80
- f = body_f[tid]
81
-
82
- # masses
83
- mass = m[tid]
84
- inv_mass = inv_m[tid] # 1 / mass
85
-
86
- inertia = I[tid]
87
- inv_inertia = inv_I[tid] # inverse of 3x3 inertia matrix
88
-
89
- # unpack transform
90
- x0 = wp.transform_get_translation(q)
91
- r0 = wp.transform_get_rotation(q)
92
-
93
- # unpack spatial twist
94
- w0 = wp.spatial_top(qd)
95
- v0 = wp.spatial_bottom(qd)
96
-
97
- # unpack spatial wrench
98
- t0 = wp.spatial_top(f)
99
- f0 = wp.spatial_bottom(f)
100
-
101
- x_com = x0 + wp.quat_rotate(r0, body_com[tid])
102
-
103
- # linear part
104
- v1 = v0 + (f0 * inv_mass + gravity * wp.nonzero(inv_mass)) * dt
105
- x1 = x_com + v1 * dt
106
-
107
- # angular part (compute in body frame)
108
- wb = wp.quat_rotate_inv(r0, w0)
109
- tb = wp.quat_rotate_inv(r0, t0) - wp.cross(wb, inertia * wb) # coriolis forces
110
-
111
- w1 = wp.quat_rotate(r0, wb + inv_inertia * tb * dt)
112
- r1 = wp.normalize(r0 + wp.quat(w1, 0.0) * r0 * 0.5 * dt)
113
-
114
- # angular damping
115
- w1 *= 1.0 - angular_damping * dt
116
-
117
- body_q_new[tid] = wp.transform(x1 - wp.quat_rotate(r1, body_com[tid]), r1)
118
- body_qd_new[tid] = wp.spatial_vector(w1, v1)
119
-
120
-
121
22
  @wp.kernel
122
23
  def eval_springs(
123
24
  x: wp.array(dtype=wp.vec3),
@@ -155,7 +56,7 @@ def eval_springs(
155
56
  c = l - rest
156
57
  dcdt = wp.dot(dir, vij)
157
58
 
158
- # damping based on relative velocity.
59
+ # damping based on relative velocity
159
60
  fs = dir * (ke * c + kd * dcdt)
160
61
 
161
62
  wp.atomic_sub(f, i, fs)
@@ -281,7 +182,7 @@ def eval_triangles(
281
182
  # -----------------------------
282
183
  # Area Damping
283
184
 
284
- dcdt = dot(dcdq, v1) + dot(dcdr, v2) - dot(dcdq + dcdr, v0)
185
+ dcdt = wp.dot(dcdq, v1) + wp.dot(dcdr, v2) - wp.dot(dcdq + dcdr, v0)
285
186
  f_damp = k_damp * dcdt
286
187
 
287
188
  f1 = f1 + dcdq * (f_area + f_damp)
@@ -295,9 +196,8 @@ def eval_triangles(
295
196
  vdir = wp.normalize(vmid)
296
197
 
297
198
  f_drag = vmid * (k_drag * area * wp.abs(wp.dot(n, vmid)))
298
- f_lift = n * (k_lift * area * (1.57079 - wp.acos(wp.dot(n, vdir)))) * dot(vmid, vmid)
199
+ f_lift = n * (k_lift * area * (wp.HALF_PI - wp.acos(wp.dot(n, vdir)))) * wp.dot(vmid, vmid)
299
200
 
300
- # note reversed sign due to atomic_add below.. need to write the unary op -
301
201
  f0 = f0 - f_drag - f_lift
302
202
  f1 = f1 + f_drag + f_lift
303
203
  f2 = f2 + f_drag + f_lift
@@ -363,8 +263,6 @@ def eval_triangles_contact(
363
263
  x: wp.array(dtype=wp.vec3),
364
264
  v: wp.array(dtype=wp.vec3),
365
265
  indices: wp.array2d(dtype=int),
366
- pose: wp.array(dtype=wp.mat22),
367
- activation: wp.array(dtype=float),
368
266
  materials: wp.array2d(dtype=float),
369
267
  f: wp.array(dtype=wp.vec3),
370
268
  ):
@@ -493,7 +391,7 @@ def eval_triangles_body_contacts(
493
391
  dist = wp.dot(diff, diff) # squared distance
494
392
  n = wp.normalize(diff) # points into the object
495
393
  c = wp.min(dist - 0.05, 0.0) # 0 unless within 0.05 of surface
496
- # c = wp.leaky_min(dot(n, x0)-0.01, 0.0, 0.0)
394
+ # c = wp.leaky_min(wp.dot(n, x0)-0.01, 0.0, 0.0)
497
395
  # fn = n * c * 1e6 # points towards cloth (both n and c are negative)
498
396
 
499
397
  # wp.atomic_sub(tri_f, particle_no, fn)
@@ -503,29 +401,29 @@ def eval_triangles_body_contacts(
503
401
  vtri = vp * bary[0] + vq * bary[1] + vr * bary[2] # bad approximation for centroid velocity
504
402
  vrel = vtri - dpdt
505
403
 
506
- vn = dot(n, vrel) # velocity component of body in negative normal direction
404
+ vn = wp.dot(n, vrel) # velocity component of body in negative normal direction
507
405
  vt = vrel - n * vn # velocity component not in normal direction
508
406
 
509
407
  # contact damping
510
- fd = 0.0 - wp.max(vn, 0.0) * kd * wp.step(c) # again, negative, into the ground
408
+ fd = -wp.max(vn, 0.0) * kd * wp.step(c) # again, negative, into the ground
511
409
 
512
410
  # # viscous friction
513
411
  # ft = vt*kf
514
412
 
515
413
  # Coulomb friction (box)
516
414
  lower = mu * (fn + fd)
517
- upper = 0.0 - lower # workaround because no unary ops yet
415
+ upper = -lower
518
416
 
519
- nx = cross(n, vec3(0.0, 0.0, 1.0)) # basis vectors for tangent
520
- nz = cross(n, vec3(1.0, 0.0, 0.0))
417
+ nx = wp.cross(n, wp.vec3(0.0, 0.0, 1.0)) # basis vectors for tangent
418
+ nz = wp.cross(n, wp.vec3(1.0, 0.0, 0.0))
521
419
 
522
- vx = wp.clamp(dot(nx * kf, vt), lower, upper)
523
- vz = wp.clamp(dot(nz * kf, vt), lower, upper)
420
+ vx = wp.clamp(wp.dot(nx * kf, vt), lower, upper)
421
+ vz = wp.clamp(wp.dot(nz * kf, vt), lower, upper)
524
422
 
525
- ft = (nx * vx + nz * vz) * (0.0 - wp.step(c)) # wp.vec3(vx, 0.0, vz)*wp.step(c)
423
+ ft = (nx * vx + nz * vz) * (-wp.step(c)) # wp.vec3(vx, 0.0, vz)*wp.step(c)
526
424
 
527
425
  # # Coulomb friction (smooth, but gradients are numerically unstable around |vt| = 0)
528
- # #ft = wp.normalize(vt)*wp.min(kf*wp.length(vt), 0.0 - mu*c*ke)
426
+ # #ft = wp.normalize(vt)*wp.min(kf*wp.length(vt), -mu*c*ke)
529
427
 
530
428
  f_total = n * (fn + fd) + ft
531
429
 
@@ -600,7 +498,7 @@ def eval_bending(
600
498
  f_damp = kd * (wp.dot(d1, v1) + wp.dot(d2, v2) + wp.dot(d3, v3) + wp.dot(d4, v4))
601
499
 
602
500
  # total force, proportional to edge length
603
- f_total = 0.0 - e_length * (f_elastic + f_damp)
501
+ f_total = -e_length * (f_elastic + f_damp)
604
502
 
605
503
  wp.atomic_add(f, i, d1 * f_total)
606
504
  wp.atomic_add(f, j, d2 * f_total)
@@ -673,7 +571,7 @@ def eval_tetrahedra(
673
571
  # -----------------------------
674
572
  # Neo-Hookean (with rest stability [Smith et al 2018])
675
573
 
676
- Ic = dot(col1, col1) + dot(col2, col2) + dot(col3, col3)
574
+ Ic = wp.dot(col1, col1) + wp.dot(col2, col2) + wp.dot(col3, col3)
677
575
 
678
576
  # deviatoric part
679
577
  P = F * k_mu * (1.0 - 1.0 / (Ic + 1.0)) + dFdt * k_damp
@@ -778,7 +676,7 @@ def eval_tetrahedra(
778
676
  f1 = f1 + dJdx1 * f_total
779
677
  f2 = f2 + dJdx2 * f_total
780
678
  f3 = f3 + dJdx3 * f_total
781
- f0 = (f1 + f2 + f3) * (0.0 - 1.0)
679
+ f0 = -(f1 + f2 + f3)
782
680
 
783
681
  # apply forces
784
682
  wp.atomic_sub(f, i, f0)
@@ -928,7 +826,7 @@ def eval_particle_contacts(
928
826
 
929
827
  # Coulomb friction (box)
930
828
  # lower = mu * c * ke
931
- # upper = 0.0 - lower
829
+ # upper = -lower
932
830
 
933
831
  # vx = wp.clamp(wp.dot(wp.vec3(kf, 0.0, 0.0), vt), lower, upper)
934
832
  # vz = wp.clamp(wp.dot(wp.vec3(0.0, 0.0, kf), vt), lower, upper)
@@ -954,59 +852,64 @@ def eval_rigid_contacts(
954
852
  body_com: wp.array(dtype=wp.vec3),
955
853
  shape_materials: ModelShapeMaterials,
956
854
  geo: ModelShapeGeometry,
855
+ shape_body: wp.array(dtype=int),
957
856
  contact_count: wp.array(dtype=int),
958
- contact_body0: wp.array(dtype=int),
959
- contact_body1: wp.array(dtype=int),
960
857
  contact_point0: wp.array(dtype=wp.vec3),
961
858
  contact_point1: wp.array(dtype=wp.vec3),
962
859
  contact_normal: wp.array(dtype=wp.vec3),
963
860
  contact_shape0: wp.array(dtype=int),
964
861
  contact_shape1: wp.array(dtype=int),
862
+ force_in_world_frame: bool,
965
863
  # outputs
966
864
  body_f: wp.array(dtype=wp.spatial_vector),
967
865
  ):
968
866
  tid = wp.tid()
969
- if contact_shape0[tid] == contact_shape1[tid]:
970
- return
971
867
 
972
868
  count = contact_count[0]
973
869
  if tid >= count:
974
870
  return
975
871
 
976
872
  # retrieve contact thickness, compute average contact material properties
977
- ke = 0.0 # restitution coefficient
873
+ ke = 0.0 # contact normal force stiffness
978
874
  kd = 0.0 # damping coefficient
979
- kf = 0.0 # friction coefficient
980
- mu = 0.0 # coulomb friction
875
+ kf = 0.0 # friction force stiffness
876
+ ka = 0.0 # adhesion distance
877
+ mu = 0.0 # friction coefficient
981
878
  mat_nonzero = 0
982
879
  thickness_a = 0.0
983
880
  thickness_b = 0.0
984
881
  shape_a = contact_shape0[tid]
985
882
  shape_b = contact_shape1[tid]
883
+ if shape_a == shape_b:
884
+ return
885
+ body_a = -1
886
+ body_b = -1
986
887
  if shape_a >= 0:
987
888
  mat_nonzero += 1
988
889
  ke += shape_materials.ke[shape_a]
989
890
  kd += shape_materials.kd[shape_a]
990
891
  kf += shape_materials.kf[shape_a]
892
+ ka += shape_materials.ka[shape_a]
991
893
  mu += shape_materials.mu[shape_a]
992
894
  thickness_a = geo.thickness[shape_a]
895
+ body_a = shape_body[shape_a]
993
896
  if shape_b >= 0:
994
897
  mat_nonzero += 1
995
898
  ke += shape_materials.ke[shape_b]
996
899
  kd += shape_materials.kd[shape_b]
997
900
  kf += shape_materials.kf[shape_b]
901
+ ka += shape_materials.ka[shape_b]
998
902
  mu += shape_materials.mu[shape_b]
999
903
  thickness_b = geo.thickness[shape_b]
904
+ body_b = shape_body[shape_b]
1000
905
  if mat_nonzero > 0:
1001
- ke = ke / float(mat_nonzero)
1002
- kd = kd / float(mat_nonzero)
1003
- kf = kf / float(mat_nonzero)
1004
- mu = mu / float(mat_nonzero)
906
+ ke /= float(mat_nonzero)
907
+ kd /= float(mat_nonzero)
908
+ kf /= float(mat_nonzero)
909
+ ka /= float(mat_nonzero)
910
+ mu /= float(mat_nonzero)
1005
911
 
1006
- body_a = contact_body0[tid]
1007
- body_b = contact_body1[tid]
1008
-
1009
- # body position in world space
912
+ # contact normal in world space
1010
913
  n = contact_normal[tid]
1011
914
  bx_a = contact_point0[tid]
1012
915
  bx_b = contact_point1[tid]
@@ -1024,7 +927,7 @@ def eval_rigid_contacts(
1024
927
 
1025
928
  d = wp.dot(n, bx_a - bx_b)
1026
929
 
1027
- if d >= 0.0:
930
+ if d >= ka:
1028
931
  return
1029
932
 
1030
933
  # compute contact point velocity
@@ -1034,13 +937,19 @@ def eval_rigid_contacts(
1034
937
  body_v_s_a = body_qd[body_a]
1035
938
  body_w_a = wp.spatial_top(body_v_s_a)
1036
939
  body_v_a = wp.spatial_bottom(body_v_s_a)
1037
- bv_a = body_v_a + wp.cross(body_w_a, r_a)
940
+ if force_in_world_frame:
941
+ bv_a = body_v_a + wp.cross(body_w_a, bx_a)
942
+ else:
943
+ bv_a = body_v_a + wp.cross(body_w_a, r_a)
1038
944
 
1039
945
  if body_b >= 0:
1040
946
  body_v_s_b = body_qd[body_b]
1041
947
  body_w_b = wp.spatial_top(body_v_s_b)
1042
948
  body_v_b = wp.spatial_bottom(body_v_s_b)
1043
- bv_b = body_v_b + wp.cross(body_w_b, r_b)
949
+ if force_in_world_frame:
950
+ bv_b = body_v_b + wp.cross(body_w_b, bx_b)
951
+ else:
952
+ bv_b = body_v_b + wp.cross(body_w_b, r_b)
1044
953
 
1045
954
  # relative velocity
1046
955
  v = bv_a - bv_b
@@ -1062,7 +971,7 @@ def eval_rigid_contacts(
1062
971
 
1063
972
  # Coulomb friction (box)
1064
973
  # lower = mu * d * ke
1065
- # upper = 0.0 - lower
974
+ # upper = -lower
1066
975
 
1067
976
  # vx = wp.clamp(wp.dot(wp.vec3(kf, 0.0, 0.0), vt), lower, upper)
1068
977
  # vz = wp.clamp(wp.dot(wp.vec3(0.0, 0.0, kf), vt), lower, upper)
@@ -1071,48 +980,65 @@ def eval_rigid_contacts(
1071
980
 
1072
981
  # Coulomb friction (smooth, but gradients are numerically unstable around |vt| = 0)
1073
982
  # ft = wp.normalize(vt)*wp.min(kf*wp.length(vt), abs(mu*d*ke))
1074
- ft = wp.normalize(vt) * wp.min(kf * wp.length(vt), 0.0 - mu * (fn + fd))
983
+ ft = wp.vec3(0.0)
984
+ if d < 0.0:
985
+ ft = wp.normalize(vt) * wp.min(kf * wp.length(vt), -mu * (fn + fd))
1075
986
 
1076
- # f_total = fn + (fd + ft)
1077
987
  f_total = n * (fn + fd) + ft
1078
- # t_total = wp.cross(r, f_total)
1079
-
1080
- # print("apply contact force")
1081
- # print(f_total)
988
+ # f_total = n * fn
1082
989
 
1083
990
  if body_a >= 0:
1084
- wp.atomic_sub(body_f, body_a, wp.spatial_vector(wp.cross(r_a, f_total), f_total))
991
+ if force_in_world_frame:
992
+ wp.atomic_add(body_f, body_a, wp.spatial_vector(wp.cross(bx_a, f_total), f_total))
993
+ else:
994
+ wp.atomic_sub(body_f, body_a, wp.spatial_vector(wp.cross(r_a, f_total), f_total))
995
+
1085
996
  if body_b >= 0:
1086
- wp.atomic_add(body_f, body_b, wp.spatial_vector(wp.cross(r_b, f_total), f_total))
997
+ if force_in_world_frame:
998
+ wp.atomic_sub(body_f, body_b, wp.spatial_vector(wp.cross(bx_b, f_total), f_total))
999
+ else:
1000
+ wp.atomic_add(body_f, body_b, wp.spatial_vector(wp.cross(r_b, f_total), f_total))
1087
1001
 
1088
1002
 
1089
1003
  @wp.func
1090
1004
  def eval_joint_force(
1091
1005
  q: float,
1092
1006
  qd: float,
1093
- target: float,
1007
+ act: float,
1094
1008
  target_ke: float,
1095
1009
  target_kd: float,
1096
- act: float,
1097
1010
  limit_lower: float,
1098
1011
  limit_upper: float,
1099
1012
  limit_ke: float,
1100
1013
  limit_kd: float,
1101
- axis: wp.vec3,
1102
- ):
1014
+ mode: wp.int32,
1015
+ ) -> float:
1016
+ """Joint force evaluation for a single degree of freedom."""
1017
+
1103
1018
  limit_f = 0.0
1019
+ damping_f = 0.0
1020
+ target_f = 0.0
1021
+
1022
+ if mode == wp.sim.JOINT_MODE_FORCE:
1023
+ target_f = act
1024
+ elif mode == wp.sim.JOINT_MODE_TARGET_POSITION:
1025
+ target_f = target_ke * (act - q) - target_kd * qd
1026
+ elif mode == wp.sim.JOINT_MODE_TARGET_VELOCITY:
1027
+ target_f = target_ke * (act - qd)
1104
1028
 
1105
1029
  # compute limit forces, damping only active when limit is violated
1106
1030
  if q < limit_lower:
1107
- limit_f = limit_ke * (limit_lower - q) - limit_kd * min(qd, 0.0)
1108
-
1109
- if q > limit_upper:
1110
- limit_f = limit_ke * (limit_upper - q) - limit_kd * max(qd, 0.0)
1111
-
1112
- # joint dynamics
1113
- total_f = (target_ke * (q - target) + target_kd * qd + act - limit_f) * axis
1031
+ limit_f = limit_ke * (limit_lower - q)
1032
+ damping_f = -limit_kd * qd
1033
+ if mode == wp.sim.JOINT_MODE_TARGET_VELOCITY:
1034
+ target_f = 0.0 # override target force when limit is violated
1035
+ elif q > limit_upper:
1036
+ limit_f = limit_ke * (limit_upper - q)
1037
+ damping_f = -limit_kd * qd
1038
+ if mode == wp.sim.JOINT_MODE_TARGET_VELOCITY:
1039
+ target_f = 0.0 # override target force when limit is violated
1114
1040
 
1115
- return total_f
1041
+ return limit_f + damping_f + target_f
1116
1042
 
1117
1043
 
1118
1044
  @wp.kernel
@@ -1120,7 +1046,6 @@ def eval_body_joints(
1120
1046
  body_q: wp.array(dtype=wp.transform),
1121
1047
  body_qd: wp.array(dtype=wp.spatial_vector),
1122
1048
  body_com: wp.array(dtype=wp.vec3),
1123
- joint_q_start: wp.array(dtype=int),
1124
1049
  joint_qd_start: wp.array(dtype=int),
1125
1050
  joint_type: wp.array(dtype=int),
1126
1051
  joint_enabled: wp.array(dtype=int),
@@ -1131,7 +1056,7 @@ def eval_body_joints(
1131
1056
  joint_axis: wp.array(dtype=wp.vec3),
1132
1057
  joint_axis_start: wp.array(dtype=int),
1133
1058
  joint_axis_dim: wp.array(dtype=int, ndim=2),
1134
- joint_target: wp.array(dtype=float),
1059
+ joint_axis_mode: wp.array(dtype=int),
1135
1060
  joint_act: wp.array(dtype=float),
1136
1061
  joint_target_ke: wp.array(dtype=float),
1137
1062
  joint_target_kd: wp.array(dtype=float),
@@ -1181,19 +1106,12 @@ def eval_body_joints(
1181
1106
  v_c = wp.spatial_bottom(twist_c) + wp.cross(w_c, r_c)
1182
1107
 
1183
1108
  # joint properties (for 1D joints)
1184
- q_start = joint_q_start[tid]
1109
+ # q_start = joint_q_start[tid]
1185
1110
  qd_start = joint_qd_start[tid]
1186
1111
  axis_start = joint_axis_start[tid]
1187
1112
 
1188
- target = joint_target[axis_start]
1189
- target_ke = joint_target_ke[axis_start]
1190
- target_kd = joint_target_kd[axis_start]
1191
- limit_ke = joint_limit_ke[axis_start]
1192
- limit_kd = joint_limit_kd[axis_start]
1193
- limit_lower = joint_limit_lower[axis_start]
1194
- limit_upper = joint_limit_upper[axis_start]
1195
-
1196
- act = joint_act[qd_start]
1113
+ lin_axis_count = joint_axis_dim[tid, 0]
1114
+ ang_axis_count = joint_axis_dim[tid, 1]
1197
1115
 
1198
1116
  x_p = wp.transform_get_translation(X_wp)
1199
1117
  x_c = wp.transform_get_translation(X_wc)
@@ -1231,9 +1149,19 @@ def eval_body_joints(
1231
1149
  # evaluate joint coordinates
1232
1150
  q = wp.dot(x_err, axis_p)
1233
1151
  qd = wp.dot(v_err, axis_p)
1234
-
1235
- f_total = eval_joint_force(
1236
- q, qd, target, target_ke, target_kd, act, limit_lower, limit_upper, limit_ke, limit_kd, axis_p
1152
+ act = joint_act[axis_start]
1153
+
1154
+ f_total = axis_p * -eval_joint_force(
1155
+ q,
1156
+ qd,
1157
+ act,
1158
+ joint_target_ke[axis_start],
1159
+ joint_target_kd[axis_start],
1160
+ joint_limit_lower[axis_start],
1161
+ joint_limit_upper[axis_start],
1162
+ joint_limit_ke[axis_start],
1163
+ joint_limit_kd[axis_start],
1164
+ joint_axis_mode[axis_start],
1237
1165
  )
1238
1166
 
1239
1167
  # attachment dynamics
@@ -1256,9 +1184,19 @@ def eval_body_joints(
1256
1184
 
1257
1185
  q = wp.acos(twist[3]) * 2.0 * wp.sign(wp.dot(axis, wp.vec3(twist[0], twist[1], twist[2])))
1258
1186
  qd = wp.dot(w_err, axis_p)
1259
-
1260
- t_total = eval_joint_force(
1261
- q, qd, target, target_ke, target_kd, act, limit_lower, limit_upper, limit_ke, limit_kd, axis_p
1187
+ act = joint_act[axis_start]
1188
+
1189
+ t_total = axis_p * -eval_joint_force(
1190
+ q,
1191
+ qd,
1192
+ act,
1193
+ joint_target_ke[axis_start],
1194
+ joint_target_kd[axis_start],
1195
+ joint_limit_lower[axis_start],
1196
+ joint_limit_upper[axis_start],
1197
+ joint_limit_ke[axis_start],
1198
+ joint_limit_kd[axis_start],
1199
+ joint_axis_mode[axis_start],
1262
1200
  )
1263
1201
 
1264
1202
  # attachment dynamics
@@ -1270,8 +1208,9 @@ def eval_body_joints(
1270
1208
  if type == wp.sim.JOINT_BALL:
1271
1209
  ang_err = wp.normalize(wp.vec3(r_err[0], r_err[1], r_err[2])) * wp.acos(r_err[3]) * 2.0
1272
1210
 
1273
- # todo: joint limits
1274
- t_total += target_kd * w_err + target_ke * wp.transform_vector(X_wp, ang_err)
1211
+ # TODO joint limits
1212
+ # TODO expose target_kd or target_ke for ball joints
1213
+ # t_total += target_kd * w_err + target_ke * wp.transform_vector(X_wp, ang_err)
1275
1214
  f_total += x_err * joint_attach_ke + v_err * joint_attach_kd
1276
1215
 
1277
1216
  if type == wp.sim.JOINT_COMPOUND:
@@ -1288,59 +1227,55 @@ def eval_body_joints(
1288
1227
  q_1 = wp.quat_from_axis_angle(axis_1, angles[1])
1289
1228
 
1290
1229
  axis_2 = wp.quat_rotate(q_1 * q_0, wp.vec3(0.0, 0.0, 1.0))
1291
- q_2 = wp.quat_from_axis_angle(axis_2, angles[2])
1230
+ # q_2 = wp.quat_from_axis_angle(axis_2, angles[2])
1292
1231
 
1293
- q_w = q_p
1232
+ # q_w = q_p
1294
1233
 
1295
1234
  axis_0 = wp.transform_vector(X_wp, axis_0)
1296
1235
  axis_1 = wp.transform_vector(X_wp, axis_1)
1297
1236
  axis_2 = wp.transform_vector(X_wp, axis_2)
1298
1237
 
1299
1238
  # joint dynamics
1300
- t_total = wp.vec3()
1301
1239
  # # TODO remove wp.quat_rotate(q_w, ...)?
1302
1240
  # t_total += eval_joint_force(angles[0], wp.dot(wp.quat_rotate(q_w, axis_0), w_err), joint_target[axis_start+0], joint_target_ke[axis_start+0],joint_target_kd[axis_start+0], joint_act[axis_start+0], joint_limit_lower[axis_start+0], joint_limit_upper[axis_start+0], joint_limit_ke[axis_start+0], joint_limit_kd[axis_start+0], wp.quat_rotate(q_w, axis_0))
1303
1241
  # t_total += eval_joint_force(angles[1], wp.dot(wp.quat_rotate(q_w, axis_1), w_err), joint_target[axis_start+1], joint_target_ke[axis_start+1],joint_target_kd[axis_start+1], joint_act[axis_start+1], joint_limit_lower[axis_start+1], joint_limit_upper[axis_start+1], joint_limit_ke[axis_start+1], joint_limit_kd[axis_start+1], wp.quat_rotate(q_w, axis_1))
1304
1242
  # t_total += eval_joint_force(angles[2], wp.dot(wp.quat_rotate(q_w, axis_2), w_err), joint_target[axis_start+2], joint_target_ke[axis_start+2],joint_target_kd[axis_start+2], joint_act[axis_start+2], joint_limit_lower[axis_start+2], joint_limit_upper[axis_start+2], joint_limit_ke[axis_start+2], joint_limit_kd[axis_start+2], wp.quat_rotate(q_w, axis_2))
1305
1243
 
1306
- t_total += eval_joint_force(
1244
+ t_total += axis_0 * -eval_joint_force(
1307
1245
  angles[0],
1308
1246
  wp.dot(axis_0, w_err),
1309
- joint_target[axis_start + 0],
1247
+ joint_act[axis_start + 0],
1310
1248
  joint_target_ke[axis_start + 0],
1311
1249
  joint_target_kd[axis_start + 0],
1312
- joint_act[axis_start + 0],
1313
1250
  joint_limit_lower[axis_start + 0],
1314
1251
  joint_limit_upper[axis_start + 0],
1315
1252
  joint_limit_ke[axis_start + 0],
1316
1253
  joint_limit_kd[axis_start + 0],
1317
- axis_0,
1254
+ joint_axis_mode[axis_start + 0],
1318
1255
  )
1319
- t_total += eval_joint_force(
1256
+ t_total += axis_1 * -eval_joint_force(
1320
1257
  angles[1],
1321
1258
  wp.dot(axis_1, w_err),
1322
- joint_target[axis_start + 1],
1259
+ joint_act[axis_start + 1],
1323
1260
  joint_target_ke[axis_start + 1],
1324
1261
  joint_target_kd[axis_start + 1],
1325
- joint_act[axis_start + 1],
1326
1262
  joint_limit_lower[axis_start + 1],
1327
1263
  joint_limit_upper[axis_start + 1],
1328
1264
  joint_limit_ke[axis_start + 1],
1329
1265
  joint_limit_kd[axis_start + 1],
1330
- axis_1,
1266
+ joint_axis_mode[axis_start + 1],
1331
1267
  )
1332
- t_total += eval_joint_force(
1268
+ t_total += axis_2 * -eval_joint_force(
1333
1269
  angles[2],
1334
1270
  wp.dot(axis_2, w_err),
1335
- joint_target[axis_start + 2],
1271
+ joint_act[axis_start + 2],
1336
1272
  joint_target_ke[axis_start + 2],
1337
1273
  joint_target_kd[axis_start + 2],
1338
- joint_act[axis_start + 2],
1339
1274
  joint_limit_lower[axis_start + 2],
1340
1275
  joint_limit_upper[axis_start + 2],
1341
1276
  joint_limit_ke[axis_start + 2],
1342
1277
  joint_limit_kd[axis_start + 2],
1343
- axis_2,
1278
+ joint_axis_mode[axis_start + 2],
1344
1279
  )
1345
1280
 
1346
1281
  f_total += x_err * joint_attach_ke + v_err * joint_attach_kd
@@ -1359,55 +1294,40 @@ def eval_body_joints(
1359
1294
  q_1 = wp.quat_from_axis_angle(axis_1, angles[1])
1360
1295
 
1361
1296
  axis_2 = wp.quat_rotate(q_1 * q_0, wp.vec3(0.0, 0.0, 1.0))
1362
- q_2 = wp.quat_from_axis_angle(axis_2, angles[2])
1363
-
1364
- q_w = q_p
1365
1297
 
1366
1298
  axis_0 = wp.transform_vector(X_wp, axis_0)
1367
1299
  axis_1 = wp.transform_vector(X_wp, axis_1)
1368
1300
  axis_2 = wp.transform_vector(X_wp, axis_2)
1369
1301
 
1370
1302
  # joint dynamics
1371
- t_total = wp.vec3()
1372
-
1373
- # free axes
1374
- # # TODO remove wp.quat_rotate(q_w, ...)?
1375
- # t_total += eval_joint_force(angles[0], wp.dot(wp.quat_rotate(q_w, axis_0), w_err), joint_target[axis_start+0], joint_target_ke[axis_start+0],joint_target_kd[axis_start+0], joint_act[axis_start+0], joint_limit_lower[axis_start+0], joint_limit_upper[axis_start+0], joint_limit_ke[axis_start+0], joint_limit_kd[axis_start+0], wp.quat_rotate(q_w, axis_0))
1376
- # t_total += eval_joint_force(angles[1], wp.dot(wp.quat_rotate(q_w, axis_1), w_err), joint_target[axis_start+1], joint_target_ke[axis_start+1],joint_target_kd[axis_start+1], joint_act[axis_start+1], joint_limit_lower[axis_start+1], joint_limit_upper[axis_start+1], joint_limit_ke[axis_start+1], joint_limit_kd[axis_start+1], wp.quat_rotate(q_w, axis_1))
1377
1303
 
1378
- # # last axis (fixed)
1379
- # t_total += eval_joint_force(angles[2], wp.dot(wp.quat_rotate(q_w, axis_2), w_err), 0.0, joint_attach_ke, joint_attach_kd*angular_damping_scale, 0.0, 0.0, 0.0, 0.0, 0.0, wp.quat_rotate(q_w, axis_2))
1380
-
1381
- # TODO remove wp.quat_rotate(q_w, ...)?
1382
- t_total += eval_joint_force(
1304
+ t_total += axis_0 * -eval_joint_force(
1383
1305
  angles[0],
1384
1306
  wp.dot(axis_0, w_err),
1385
- joint_target[axis_start + 0],
1307
+ joint_act[axis_start + 0],
1386
1308
  joint_target_ke[axis_start + 0],
1387
1309
  joint_target_kd[axis_start + 0],
1388
- joint_act[axis_start + 0],
1389
1310
  joint_limit_lower[axis_start + 0],
1390
1311
  joint_limit_upper[axis_start + 0],
1391
1312
  joint_limit_ke[axis_start + 0],
1392
1313
  joint_limit_kd[axis_start + 0],
1393
- axis_0,
1314
+ joint_axis_mode[axis_start + 0],
1394
1315
  )
1395
- t_total += eval_joint_force(
1316
+ t_total += axis_1 * -eval_joint_force(
1396
1317
  angles[1],
1397
1318
  wp.dot(axis_1, w_err),
1398
- joint_target[axis_start + 1],
1319
+ joint_act[axis_start + 1],
1399
1320
  joint_target_ke[axis_start + 1],
1400
1321
  joint_target_kd[axis_start + 1],
1401
- joint_act[axis_start + 1],
1402
1322
  joint_limit_lower[axis_start + 1],
1403
1323
  joint_limit_upper[axis_start + 1],
1404
1324
  joint_limit_ke[axis_start + 1],
1405
1325
  joint_limit_kd[axis_start + 1],
1406
- axis_1,
1326
+ joint_axis_mode[axis_start + 1],
1407
1327
  )
1408
1328
 
1409
1329
  # last axis (fixed)
1410
- t_total += eval_joint_force(
1330
+ t_total += axis_2 * -eval_joint_force(
1411
1331
  angles[2],
1412
1332
  wp.dot(axis_2, w_err),
1413
1333
  0.0,
@@ -1417,12 +1337,243 @@ def eval_body_joints(
1417
1337
  0.0,
1418
1338
  0.0,
1419
1339
  0.0,
1420
- 0.0,
1421
- axis_2,
1340
+ wp.sim.JOINT_MODE_FORCE,
1422
1341
  )
1423
1342
 
1424
1343
  f_total += x_err * joint_attach_ke + v_err * joint_attach_kd
1425
1344
 
1345
+ if type == wp.sim.JOINT_D6:
1346
+ pos = wp.vec3(0.0)
1347
+ vel = wp.vec3(0.0)
1348
+ if lin_axis_count >= 1:
1349
+ axis_0 = wp.transform_vector(X_wp, joint_axis[axis_start + 0])
1350
+ q0 = wp.dot(x_err, axis_0)
1351
+ qd0 = wp.dot(v_err, axis_0)
1352
+
1353
+ f_total += axis_0 * -eval_joint_force(
1354
+ q0,
1355
+ qd0,
1356
+ joint_act[axis_start + 0],
1357
+ joint_target_ke[axis_start + 0],
1358
+ joint_target_kd[axis_start + 0],
1359
+ joint_limit_lower[axis_start + 0],
1360
+ joint_limit_upper[axis_start + 0],
1361
+ joint_limit_ke[axis_start + 0],
1362
+ joint_limit_kd[axis_start + 0],
1363
+ joint_axis_mode[axis_start + 0],
1364
+ )
1365
+
1366
+ pos += q0 * axis_0
1367
+ vel += qd0 * axis_0
1368
+
1369
+ if lin_axis_count >= 2:
1370
+ axis_1 = wp.transform_vector(X_wp, joint_axis[axis_start + 1])
1371
+ q1 = wp.dot(x_err, axis_1)
1372
+ qd1 = wp.dot(v_err, axis_1)
1373
+
1374
+ f_total += axis_1 * -eval_joint_force(
1375
+ q1,
1376
+ qd1,
1377
+ joint_act[axis_start + 1],
1378
+ joint_target_ke[axis_start + 1],
1379
+ joint_target_kd[axis_start + 1],
1380
+ joint_limit_lower[axis_start + 1],
1381
+ joint_limit_upper[axis_start + 1],
1382
+ joint_limit_ke[axis_start + 1],
1383
+ joint_limit_kd[axis_start + 1],
1384
+ joint_axis_mode[axis_start + 1],
1385
+ )
1386
+
1387
+ pos += q1 * axis_1
1388
+ vel += qd1 * axis_1
1389
+
1390
+ if lin_axis_count == 3:
1391
+ axis_2 = wp.transform_vector(X_wp, joint_axis[axis_start + 2])
1392
+ q2 = wp.dot(x_err, axis_2)
1393
+ qd2 = wp.dot(v_err, axis_2)
1394
+
1395
+ f_total += axis_2 * -eval_joint_force(
1396
+ q2,
1397
+ qd2,
1398
+ joint_act[axis_start + 2],
1399
+ joint_target_ke[axis_start + 2],
1400
+ joint_target_kd[axis_start + 2],
1401
+ joint_limit_lower[axis_start + 2],
1402
+ joint_limit_upper[axis_start + 2],
1403
+ joint_limit_ke[axis_start + 2],
1404
+ joint_limit_kd[axis_start + 2],
1405
+ joint_axis_mode[axis_start + 2],
1406
+ )
1407
+
1408
+ pos += q2 * axis_2
1409
+ vel += qd2 * axis_2
1410
+
1411
+ f_total += (x_err - pos) * joint_attach_ke + (v_err - vel) * joint_attach_kd
1412
+
1413
+ if ang_axis_count == 0:
1414
+ ang_err = wp.normalize(wp.vec3(r_err[0], r_err[1], r_err[2])) * wp.acos(r_err[3]) * 2.0
1415
+ t_total += (
1416
+ wp.transform_vector(X_wp, ang_err) * joint_attach_ke + w_err * joint_attach_kd * angular_damping_scale
1417
+ )
1418
+
1419
+ i_0 = lin_axis_count + axis_start + 0
1420
+ i_1 = lin_axis_count + axis_start + 1
1421
+ i_2 = lin_axis_count + axis_start + 2
1422
+
1423
+ if ang_axis_count == 1:
1424
+ axis = joint_axis[i_0]
1425
+
1426
+ axis_p = wp.transform_vector(X_wp, axis)
1427
+ axis_c = wp.transform_vector(X_wc, axis)
1428
+
1429
+ # swing twist decomposition
1430
+ twist = quat_twist(axis, r_err)
1431
+
1432
+ q = wp.acos(twist[3]) * 2.0 * wp.sign(wp.dot(axis, wp.vec3(twist[0], twist[1], twist[2])))
1433
+ qd = wp.dot(w_err, axis_p)
1434
+
1435
+ t_total = axis_p * -eval_joint_force(
1436
+ q,
1437
+ qd,
1438
+ joint_act[i_0],
1439
+ joint_target_ke[i_0],
1440
+ joint_target_kd[i_0],
1441
+ joint_limit_lower[i_0],
1442
+ joint_limit_upper[i_0],
1443
+ joint_limit_ke[i_0],
1444
+ joint_limit_kd[i_0],
1445
+ joint_axis_mode[i_0],
1446
+ )
1447
+
1448
+ # attachment dynamics
1449
+ swing_err = wp.cross(axis_p, axis_c)
1450
+
1451
+ t_total += swing_err * joint_attach_ke + (w_err - qd * axis_p) * joint_attach_kd * angular_damping_scale
1452
+
1453
+ if ang_axis_count == 2:
1454
+ q_pc = wp.quat_inverse(q_p) * q_c
1455
+
1456
+ # decompose to a compound rotation each axis
1457
+ angles = quat_decompose(q_pc)
1458
+
1459
+ orig_axis_0 = joint_axis[i_0]
1460
+ orig_axis_1 = joint_axis[i_1]
1461
+ orig_axis_2 = wp.cross(orig_axis_0, orig_axis_1)
1462
+
1463
+ # reconstruct rotation axes
1464
+ axis_0 = orig_axis_0
1465
+ q_0 = wp.quat_from_axis_angle(axis_0, angles[0])
1466
+
1467
+ axis_1 = wp.quat_rotate(q_0, orig_axis_1)
1468
+ q_1 = wp.quat_from_axis_angle(axis_1, angles[1])
1469
+
1470
+ axis_2 = wp.quat_rotate(q_1 * q_0, orig_axis_2)
1471
+
1472
+ axis_0 = wp.transform_vector(X_wp, axis_0)
1473
+ axis_1 = wp.transform_vector(X_wp, axis_1)
1474
+ axis_2 = wp.transform_vector(X_wp, axis_2)
1475
+
1476
+ # joint dynamics
1477
+
1478
+ t_total += axis_0 * -eval_joint_force(
1479
+ angles[0],
1480
+ wp.dot(axis_0, w_err),
1481
+ joint_act[i_0],
1482
+ joint_target_ke[i_0],
1483
+ joint_target_kd[i_0],
1484
+ joint_limit_lower[i_0],
1485
+ joint_limit_upper[i_0],
1486
+ joint_limit_ke[i_0],
1487
+ joint_limit_kd[i_0],
1488
+ joint_axis_mode[i_0],
1489
+ )
1490
+ t_total += axis_1 * -eval_joint_force(
1491
+ angles[1],
1492
+ wp.dot(axis_1, w_err),
1493
+ joint_act[i_1],
1494
+ joint_target_ke[i_1],
1495
+ joint_target_kd[i_1],
1496
+ joint_limit_lower[i_1],
1497
+ joint_limit_upper[i_1],
1498
+ joint_limit_ke[i_1],
1499
+ joint_limit_kd[i_1],
1500
+ joint_axis_mode[i_1],
1501
+ )
1502
+
1503
+ # last axis (fixed)
1504
+ t_total += axis_2 * -eval_joint_force(
1505
+ angles[2],
1506
+ wp.dot(axis_2, w_err),
1507
+ 0.0,
1508
+ joint_attach_ke,
1509
+ joint_attach_kd * angular_damping_scale,
1510
+ 0.0,
1511
+ 0.0,
1512
+ 0.0,
1513
+ 0.0,
1514
+ wp.sim.JOINT_MODE_FORCE,
1515
+ )
1516
+
1517
+ if ang_axis_count == 3:
1518
+ q_pc = wp.quat_inverse(q_p) * q_c
1519
+
1520
+ # decompose to a compound rotation each axis
1521
+ angles = quat_decompose(q_pc)
1522
+
1523
+ orig_axis_0 = joint_axis[i_0]
1524
+ orig_axis_1 = joint_axis[i_1]
1525
+ orig_axis_2 = joint_axis[i_2]
1526
+
1527
+ # reconstruct rotation axes
1528
+ axis_0 = orig_axis_0
1529
+ q_0 = wp.quat_from_axis_angle(axis_0, angles[0])
1530
+
1531
+ axis_1 = wp.quat_rotate(q_0, orig_axis_1)
1532
+ q_1 = wp.quat_from_axis_angle(axis_1, angles[1])
1533
+
1534
+ axis_2 = wp.quat_rotate(q_1 * q_0, orig_axis_2)
1535
+
1536
+ axis_0 = wp.transform_vector(X_wp, axis_0)
1537
+ axis_1 = wp.transform_vector(X_wp, axis_1)
1538
+ axis_2 = wp.transform_vector(X_wp, axis_2)
1539
+
1540
+ t_total += axis_0 * -eval_joint_force(
1541
+ angles[0],
1542
+ wp.dot(axis_0, w_err),
1543
+ joint_act[i_0],
1544
+ joint_target_ke[i_0],
1545
+ joint_target_kd[i_0],
1546
+ joint_limit_lower[i_0],
1547
+ joint_limit_upper[i_0],
1548
+ joint_limit_ke[i_0],
1549
+ joint_limit_kd[i_0],
1550
+ joint_axis_mode[i_0],
1551
+ )
1552
+ t_total += axis_1 * -eval_joint_force(
1553
+ angles[1],
1554
+ wp.dot(axis_1, w_err),
1555
+ joint_act[i_1],
1556
+ joint_target_ke[i_1],
1557
+ joint_target_kd[i_1],
1558
+ joint_limit_lower[i_1],
1559
+ joint_limit_upper[i_1],
1560
+ joint_limit_ke[i_1],
1561
+ joint_limit_kd[i_1],
1562
+ joint_axis_mode[i_1],
1563
+ )
1564
+ t_total += axis_2 * -eval_joint_force(
1565
+ angles[2],
1566
+ wp.dot(axis_2, w_err),
1567
+ joint_act[i_2],
1568
+ joint_target_ke[i_2],
1569
+ joint_target_kd[i_2],
1570
+ joint_limit_lower[i_2],
1571
+ joint_limit_upper[i_2],
1572
+ joint_limit_ke[i_2],
1573
+ joint_limit_kd[i_2],
1574
+ joint_axis_mode[i_2],
1575
+ )
1576
+
1426
1577
  # write forces
1427
1578
  if c_parent >= 0:
1428
1579
  wp.atomic_add(body_f, c_parent, wp.spatial_vector(t_total + wp.cross(r_p, f_total), f_total))
@@ -1464,8 +1615,6 @@ def compute_muscle_force(
1464
1615
  wp.atomic_sub(body_f_s, link_0, wp.spatial_vector(f, wp.cross(pos_0, f)))
1465
1616
  wp.atomic_add(body_f_s, link_1, wp.spatial_vector(f, wp.cross(pos_1, f)))
1466
1617
 
1467
- return 0
1468
-
1469
1618
 
1470
1619
  @wp.kernel
1471
1620
  def eval_muscles(
@@ -1491,8 +1640,7 @@ def eval_muscles(
1491
1640
  compute_muscle_force(i, body_X_s, body_v_s, body_com, muscle_links, muscle_points, activation, body_f_s)
1492
1641
 
1493
1642
 
1494
- def compute_forces(model, state, particle_f, body_f, requires_grad):
1495
- # damped springs
1643
+ def eval_spring_forces(model: Model, state: State, particle_f: wp.array):
1496
1644
  if model.spring_count:
1497
1645
  wp.launch(
1498
1646
  kernel=eval_springs,
@@ -1509,11 +1657,8 @@ def compute_forces(model, state, particle_f, body_f, requires_grad):
1509
1657
  device=model.device,
1510
1658
  )
1511
1659
 
1512
- # particle-particle interactions
1513
- if model.particle_count:
1514
- eval_particle_forces(model, state, particle_f)
1515
1660
 
1516
- # triangle elastic and lift/drag forces
1661
+ def eval_triangle_forces(model: Model, state: State, control: Control, particle_f: wp.array):
1517
1662
  if model.tri_count:
1518
1663
  wp.launch(
1519
1664
  kernel=eval_triangles,
@@ -1523,15 +1668,16 @@ def compute_forces(model, state, particle_f, body_f, requires_grad):
1523
1668
  state.particle_qd,
1524
1669
  model.tri_indices,
1525
1670
  model.tri_poses,
1526
- model.tri_activations,
1671
+ control.tri_activations,
1527
1672
  model.tri_materials,
1528
1673
  ],
1529
1674
  outputs=[particle_f],
1530
1675
  device=model.device,
1531
1676
  )
1532
1677
 
1533
- # triangle/triangle contacts
1534
- if model.enable_tri_collisions and model.tri_count:
1678
+
1679
+ def eval_triangle_contact_forces(model: Model, state: State, particle_f: wp.array):
1680
+ if model.enable_tri_collisions:
1535
1681
  wp.launch(
1536
1682
  kernel=eval_triangles_contact,
1537
1683
  dim=model.tri_count * model.particle_count,
@@ -1540,15 +1686,14 @@ def compute_forces(model, state, particle_f, body_f, requires_grad):
1540
1686
  state.particle_q,
1541
1687
  state.particle_qd,
1542
1688
  model.tri_indices,
1543
- model.tri_poses,
1544
- model.tri_activations,
1545
1689
  model.tri_materials,
1546
1690
  ],
1547
1691
  outputs=[particle_f],
1548
1692
  device=model.device,
1549
1693
  )
1550
1694
 
1551
- # triangle bending
1695
+
1696
+ def eval_bending_forces(model: Model, state: State, particle_f: wp.array):
1552
1697
  if model.edge_count:
1553
1698
  wp.launch(
1554
1699
  kernel=eval_bending,
@@ -1564,7 +1709,8 @@ def compute_forces(model, state, particle_f, body_f, requires_grad):
1564
1709
  device=model.device,
1565
1710
  )
1566
1711
 
1567
- # particle ground contact
1712
+
1713
+ def eval_particle_ground_contact_forces(model: Model, state: State, particle_f: wp.array):
1568
1714
  if model.ground and model.particle_count:
1569
1715
  wp.launch(
1570
1716
  kernel=eval_particle_ground_contacts,
@@ -1584,7 +1730,8 @@ def compute_forces(model, state, particle_f, body_f, requires_grad):
1584
1730
  device=model.device,
1585
1731
  )
1586
1732
 
1587
- # tetrahedral FEM
1733
+
1734
+ def eval_tetrahedral_forces(model: Model, state: State, control: Control, particle_f: wp.array):
1588
1735
  if model.tet_count:
1589
1736
  wp.launch(
1590
1737
  kernel=eval_tetrahedra,
@@ -1594,13 +1741,15 @@ def compute_forces(model, state, particle_f, body_f, requires_grad):
1594
1741
  state.particle_qd,
1595
1742
  model.tet_indices,
1596
1743
  model.tet_poses,
1597
- model.tet_activations,
1744
+ control.tet_activations,
1598
1745
  model.tet_materials,
1599
1746
  ],
1600
1747
  outputs=[particle_f],
1601
1748
  device=model.device,
1602
1749
  )
1603
1750
 
1751
+
1752
+ def eval_body_contact_forces(model: Model, state: State, particle_f: wp.array):
1604
1753
  if model.rigid_contact_max and (
1605
1754
  model.ground and model.shape_ground_contact_pair_count or model.shape_contact_pair_count
1606
1755
  ):
@@ -1613,19 +1762,21 @@ def compute_forces(model, state, particle_f, body_f, requires_grad):
1613
1762
  model.body_com,
1614
1763
  model.shape_materials,
1615
1764
  model.shape_geo,
1765
+ model.shape_body,
1616
1766
  model.rigid_contact_count,
1617
- model.rigid_contact_body0,
1618
- model.rigid_contact_body1,
1619
1767
  model.rigid_contact_point0,
1620
1768
  model.rigid_contact_point1,
1621
1769
  model.rigid_contact_normal,
1622
1770
  model.rigid_contact_shape0,
1623
1771
  model.rigid_contact_shape1,
1772
+ False,
1624
1773
  ],
1625
- outputs=[body_f],
1774
+ outputs=[state.body_f],
1626
1775
  device=model.device,
1627
1776
  )
1628
1777
 
1778
+
1779
+ def eval_body_joint_forces(model: Model, state: State, control: Control, body_f: wp.array):
1629
1780
  if model.joint_count:
1630
1781
  wp.launch(
1631
1782
  kernel=eval_body_joints,
@@ -1634,7 +1785,6 @@ def compute_forces(model, state, particle_f, body_f, requires_grad):
1634
1785
  state.body_q,
1635
1786
  state.body_qd,
1636
1787
  model.body_com,
1637
- model.joint_q_start,
1638
1788
  model.joint_qd_start,
1639
1789
  model.joint_type,
1640
1790
  model.joint_enabled,
@@ -1645,8 +1795,8 @@ def compute_forces(model, state, particle_f, body_f, requires_grad):
1645
1795
  model.joint_axis,
1646
1796
  model.joint_axis_start,
1647
1797
  model.joint_axis_dim,
1648
- model.joint_target,
1649
- model.joint_act,
1798
+ model.joint_axis_mode,
1799
+ control.joint_act,
1650
1800
  model.joint_target_ke,
1651
1801
  model.joint_target_kd,
1652
1802
  model.joint_limit_lower,
@@ -1660,7 +1810,8 @@ def compute_forces(model, state, particle_f, body_f, requires_grad):
1660
1810
  device=model.device,
1661
1811
  )
1662
1812
 
1663
- # particle shape contact
1813
+
1814
+ def eval_particle_body_contact_forces(model: Model, state: State, particle_f: wp.array, body_f: wp.array):
1664
1815
  if model.particle_count and model.shape_count > 1:
1665
1816
  wp.launch(
1666
1817
  kernel=eval_particle_contacts,
@@ -1693,8 +1844,9 @@ def compute_forces(model, state, particle_f, body_f, requires_grad):
1693
1844
  device=model.device,
1694
1845
  )
1695
1846
 
1696
- # evaluate muscle actuation
1697
- if False and model.muscle_count:
1847
+
1848
+ def eval_muscle_forces(model: Model, state: State, control: Control, body_f: wp.array):
1849
+ if model.muscle_count:
1698
1850
  wp.launch(
1699
1851
  kernel=eval_muscles,
1700
1852
  dim=model.muscle_count,
@@ -1706,50 +1858,50 @@ def compute_forces(model, state, particle_f, body_f, requires_grad):
1706
1858
  model.muscle_params,
1707
1859
  model.muscle_bodies,
1708
1860
  model.muscle_points,
1709
- model.muscle_activation,
1861
+ control.muscle_activations,
1710
1862
  ],
1711
1863
  outputs=[body_f],
1712
1864
  device=model.device,
1713
1865
  )
1714
1866
 
1715
- # if (model.articulation_count):
1716
-
1717
- # # evaluate joint torques
1718
- # wp.launch(
1719
- # kernel=eval_body_tau,
1720
- # dim=model.articulation_count,
1721
- # inputs=[
1722
- # model.articulation_joint_start,
1723
- # model.joint_type,
1724
- # model.joint_parent,
1725
- # model.joint_q_start,
1726
- # model.joint_qd_start,
1727
- # state.joint_q,
1728
- # state.joint_qd,
1729
- # state.joint_act,
1730
- # model.joint_target,
1731
- # model.joint_target_ke,
1732
- # model.joint_target_kd,
1733
- # model.joint_limit_lower,
1734
- # model.joint_limit_upper,
1735
- # model.joint_limit_ke,
1736
- # model.joint_limit_kd,
1737
- # model.joint_axis,
1738
- # state.joint_S_s,
1739
- # state.body_f_s
1740
- # ],
1741
- # outputs=[
1742
- # state.body_ft_s,
1743
- # state.joint_tau
1744
- # ],
1745
- # device=model.device,
1746
- # preserve_output=True)
1747
-
1748
- state.particle_f = particle_f
1749
- state.body_f = body_f
1750
-
1751
-
1752
- class SemiImplicitIntegrator:
1867
+
1868
+ def compute_forces(model: Model, state: State, control: Control, particle_f: wp.array, body_f: wp.array, dt: float):
1869
+ # damped springs
1870
+ eval_spring_forces(model, state, particle_f)
1871
+
1872
+ # triangle elastic and lift/drag forces
1873
+ eval_triangle_forces(model, state, control, particle_f)
1874
+
1875
+ # triangle/triangle contacts
1876
+ eval_triangle_contact_forces(model, state, particle_f)
1877
+
1878
+ # triangle bending
1879
+ eval_bending_forces(model, state, particle_f)
1880
+
1881
+ # tetrahedral FEM
1882
+ eval_tetrahedral_forces(model, state, control, particle_f)
1883
+
1884
+ # body joints
1885
+ eval_body_joint_forces(model, state, control, body_f)
1886
+
1887
+ # particle-particle interactions
1888
+ eval_particle_forces(model, state, particle_f)
1889
+
1890
+ # particle ground contacts
1891
+ eval_particle_ground_contact_forces(model, state, particle_f)
1892
+
1893
+ # body contacts
1894
+ eval_body_contact_forces(model, state, particle_f)
1895
+
1896
+ # particle shape contact
1897
+ eval_particle_body_contact_forces(model, state, particle_f, body_f)
1898
+
1899
+ # muscles
1900
+ if False:
1901
+ eval_muscle_forces(model, state, control, body_f)
1902
+
1903
+
1904
+ class SemiImplicitIntegrator(Integrator):
1753
1905
  """A semi-implicit integrator using symplectic Euler
1754
1906
 
1755
1907
  After constructing `Model` and `State` objects this time-integrator
@@ -1774,10 +1926,14 @@ class SemiImplicitIntegrator:
1774
1926
 
1775
1927
  """
1776
1928
 
1777
- def __init__(self, angular_damping=0.05):
1929
+ def __init__(self, angular_damping: float = 0.05):
1930
+ """
1931
+ Args:
1932
+ angular_damping (float, optional): Angular damping factor. Defaults to 0.05.
1933
+ """
1778
1934
  self.angular_damping = angular_damping
1779
1935
 
1780
- def simulate(self, model, state_in, state_out, dt, requires_grad=False):
1936
+ def simulate(self, model: Model, state_in: State, state_out: State, dt: float, control: Control = None):
1781
1937
  with wp.ScopedTimer("simulate", False):
1782
1938
  particle_f = None
1783
1939
  body_f = None
@@ -1788,191 +1944,13 @@ class SemiImplicitIntegrator:
1788
1944
  if state_in.body_count:
1789
1945
  body_f = state_in.body_f
1790
1946
 
1791
- compute_forces(model, state_in, particle_f, body_f, requires_grad=requires_grad)
1792
-
1793
- # -------------------------------------
1794
- # integrate bodies
1795
-
1796
- if model.body_count:
1797
- wp.launch(
1798
- kernel=integrate_bodies,
1799
- dim=model.body_count,
1800
- inputs=[
1801
- state_in.body_q,
1802
- state_in.body_qd,
1803
- state_in.body_f,
1804
- model.body_com,
1805
- model.body_mass,
1806
- model.body_inertia,
1807
- model.body_inv_mass,
1808
- model.body_inv_inertia,
1809
- model.gravity,
1810
- self.angular_damping,
1811
- dt,
1812
- ],
1813
- outputs=[state_out.body_q, state_out.body_qd],
1814
- device=model.device,
1815
- )
1816
-
1817
- # ----------------------------
1818
- # integrate particles
1819
-
1820
- if model.particle_count:
1821
- wp.launch(
1822
- kernel=integrate_particles,
1823
- dim=model.particle_count,
1824
- inputs=[
1825
- state_in.particle_q,
1826
- state_in.particle_qd,
1827
- state_in.particle_f,
1828
- model.particle_inv_mass,
1829
- model.particle_flags,
1830
- model.gravity,
1831
- dt,
1832
- model.particle_max_velocity,
1833
- ],
1834
- outputs=[state_out.particle_q, state_out.particle_qd],
1835
- device=model.device,
1836
- )
1837
-
1838
- return state_out
1839
-
1840
-
1841
- @wp.kernel
1842
- def compute_particle_residual(
1843
- particle_qd_0: wp.array(dtype=wp.vec3),
1844
- particle_qd_1: wp.array(dtype=wp.vec3),
1845
- particle_f: wp.array(dtype=wp.vec3),
1846
- particle_m: wp.array(dtype=float),
1847
- particle_flags: wp.array(dtype=wp.uint32),
1848
- gravity: wp.vec3,
1849
- dt: float,
1850
- residual: wp.array(dtype=wp.vec3),
1851
- ):
1852
- tid = wp.tid()
1853
- if (particle_flags[tid] & PARTICLE_FLAG_ACTIVE) == 0:
1854
- return
1855
-
1856
- m = particle_m[tid]
1857
- v1 = particle_qd_1[tid]
1858
- v0 = particle_qd_0[tid]
1859
- f = particle_f[tid]
1860
-
1861
- err = wp.vec3()
1862
-
1863
- if m > 0.0:
1864
- err = (v1 - v0) * m - f * dt - gravity * dt * m
1865
-
1866
- residual[tid] = err
1867
-
1868
-
1869
- @wp.kernel
1870
- def update_particle_position(
1871
- particle_q_0: wp.array(dtype=wp.vec3),
1872
- particle_q_1: wp.array(dtype=wp.vec3),
1873
- particle_qd_1: wp.array(dtype=wp.vec3),
1874
- x: wp.array(dtype=wp.vec3),
1875
- particle_flags: wp.array(dtype=wp.uint32),
1876
- dt: float,
1877
- ):
1878
- tid = wp.tid()
1879
- if (particle_flags[tid] & PARTICLE_FLAG_ACTIVE) == 0:
1880
- return
1881
-
1882
- qd_1 = x[tid]
1883
-
1884
- q_0 = particle_q_0[tid]
1885
- q_1 = q_0 + qd_1 * dt
1886
-
1887
- particle_q_1[tid] = q_1
1888
- particle_qd_1[tid] = qd_1
1889
-
1890
-
1891
- def compute_residual(model, state_in, state_out, particle_f, residual, dt):
1892
- wp.launch(
1893
- kernel=compute_particle_residual,
1894
- dim=model.particle_count,
1895
- inputs=[
1896
- state_in.particle_qd,
1897
- state_out.particle_qd,
1898
- particle_f,
1899
- model.particle_mass,
1900
- model.particle_flags,
1901
- model.gravity,
1902
- dt,
1903
- residual.astype(dtype=wp.vec3),
1904
- ],
1905
- device=model.device,
1906
- )
1907
-
1908
-
1909
- def init_state(model, state_in, state_out, dt):
1910
- wp.launch(
1911
- kernel=integrate_particles,
1912
- dim=model.particle_count,
1913
- inputs=[
1914
- state_in.particle_q,
1915
- state_in.particle_qd,
1916
- state_in.particle_f,
1917
- model.particle_inv_mass,
1918
- model.particle_flags,
1919
- model.gravity,
1920
- dt,
1921
- model.particle_max_velocity,
1922
- ],
1923
- outputs=[state_out.particle_q, state_out.particle_qd],
1924
- device=model.device,
1925
- )
1926
-
1927
-
1928
- # compute the final positions given output velocity (x)
1929
- def update_state(model, state_in, state_out, x, dt):
1930
- wp.launch(
1931
- kernel=update_particle_position,
1932
- dim=model.particle_count,
1933
- inputs=[state_in.particle_q, state_out.particle_q, state_out.particle_qd, x, model.particle_flags, dt],
1934
- device=model.device,
1935
- )
1936
-
1937
-
1938
- class VariationalImplicitIntegrator:
1939
- def __init__(self, model, solver="gd", alpha=0.1, max_iters=32, report=False):
1940
- self.solver = solver
1941
- self.alpha = alpha
1942
- self.max_iters = max_iters
1943
- self.report = report
1944
-
1945
- self.opt = Optimizer(model.particle_count * 3, mode=self.solver, device=model.device)
1946
-
1947
- # allocate temporary space for evaluating particle forces
1948
- self.particle_f = wp.zeros(model.particle_count, dtype=wp.vec3, device=model.device)
1949
-
1950
- def simulate(self, model, state_in, state_out, dt, requires_grad=False):
1951
- if state_in is state_out:
1952
- raise RuntimeError("Implicit integrators require state objects to not alias each other")
1953
-
1954
- with wp.ScopedTimer("simulate", False):
1955
- # alloc particle force buffer
1956
- if model.particle_count:
1957
-
1958
- def residual_func(x, dfdx):
1959
- self.particle_f.zero_()
1960
-
1961
- update_state(model, state_in, state_out, x.astype(wp.vec3), dt)
1962
- compute_forces(model, state_out, self.particle_f, None)
1963
- compute_residual(model, state_in, state_out, self.particle_f, dfdx, dt)
1964
-
1965
- # initialize output state using the input velocity to create 'predicted state'
1966
- init_state(model, state_in, state_out, dt)
1947
+ if control is None:
1948
+ control = model.control(clone_variables=False)
1967
1949
 
1968
- # our optimization variable
1969
- x = state_out.particle_qd.astype(dtype=float)
1950
+ compute_forces(model, state_in, control, particle_f, body_f, dt)
1970
1951
 
1971
- self.opt.solve(
1972
- x=x, grad_func=residual_func, max_iters=self.max_iters, alpha=self.alpha, report=self.report
1973
- )
1952
+ self.integrate_bodies(model, state_in, state_out, dt, self.angular_damping)
1974
1953
 
1975
- # final update to output state with solved velocity
1976
- update_state(model, state_in, state_out, x.astype(wp.vec3), dt)
1954
+ self.integrate_particles(model, state_in, state_out, dt)
1977
1955
 
1978
1956
  return state_out