warp-lang 1.5.0__py3-none-manylinux2014_x86_64.whl → 1.6.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 (132) hide show
  1. warp/__init__.py +5 -0
  2. warp/autograd.py +414 -191
  3. warp/bin/warp-clang.so +0 -0
  4. warp/bin/warp.so +0 -0
  5. warp/build.py +40 -12
  6. warp/build_dll.py +13 -6
  7. warp/builtins.py +1124 -497
  8. warp/codegen.py +261 -136
  9. warp/config.py +1 -1
  10. warp/context.py +357 -119
  11. warp/examples/assets/square_cloth.usd +0 -0
  12. warp/examples/benchmarks/benchmark_gemm.py +27 -18
  13. warp/examples/benchmarks/benchmark_interop_paddle.py +3 -3
  14. warp/examples/benchmarks/benchmark_interop_torch.py +3 -3
  15. warp/examples/core/example_torch.py +18 -34
  16. warp/examples/fem/example_apic_fluid.py +1 -0
  17. warp/examples/fem/example_mixed_elasticity.py +1 -1
  18. warp/examples/optim/example_bounce.py +1 -1
  19. warp/examples/optim/example_cloth_throw.py +1 -1
  20. warp/examples/optim/example_diffray.py +4 -15
  21. warp/examples/optim/example_drone.py +1 -1
  22. warp/examples/optim/example_softbody_properties.py +392 -0
  23. warp/examples/optim/example_trajectory.py +1 -3
  24. warp/examples/optim/example_walker.py +5 -0
  25. warp/examples/sim/example_cartpole.py +0 -2
  26. warp/examples/sim/example_cloth.py +3 -1
  27. warp/examples/sim/example_cloth_self_contact.py +260 -0
  28. warp/examples/sim/example_granular_collision_sdf.py +4 -5
  29. warp/examples/sim/example_jacobian_ik.py +0 -2
  30. warp/examples/sim/example_quadruped.py +5 -2
  31. warp/examples/tile/example_tile_cholesky.py +79 -0
  32. warp/examples/tile/example_tile_convolution.py +2 -2
  33. warp/examples/tile/example_tile_fft.py +2 -2
  34. warp/examples/tile/example_tile_filtering.py +3 -3
  35. warp/examples/tile/example_tile_matmul.py +4 -4
  36. warp/examples/tile/example_tile_mlp.py +12 -12
  37. warp/examples/tile/example_tile_nbody.py +180 -0
  38. warp/examples/tile/example_tile_walker.py +319 -0
  39. warp/fem/geometry/geometry.py +0 -2
  40. warp/math.py +147 -0
  41. warp/native/array.h +12 -0
  42. warp/native/builtin.h +0 -1
  43. warp/native/bvh.cpp +149 -70
  44. warp/native/bvh.cu +287 -68
  45. warp/native/bvh.h +195 -85
  46. warp/native/clang/clang.cpp +5 -1
  47. warp/native/coloring.cpp +5 -1
  48. warp/native/cuda_util.cpp +91 -53
  49. warp/native/cuda_util.h +5 -0
  50. warp/native/exports.h +40 -40
  51. warp/native/intersect.h +17 -0
  52. warp/native/mat.h +41 -0
  53. warp/native/mathdx.cpp +19 -0
  54. warp/native/mesh.cpp +25 -8
  55. warp/native/mesh.cu +153 -101
  56. warp/native/mesh.h +482 -403
  57. warp/native/quat.h +40 -0
  58. warp/native/solid_angle.h +7 -0
  59. warp/native/sort.cpp +85 -0
  60. warp/native/sort.cu +34 -0
  61. warp/native/sort.h +3 -1
  62. warp/native/spatial.h +11 -0
  63. warp/native/tile.h +1187 -669
  64. warp/native/tile_reduce.h +8 -6
  65. warp/native/vec.h +41 -0
  66. warp/native/warp.cpp +8 -1
  67. warp/native/warp.cu +263 -40
  68. warp/native/warp.h +19 -5
  69. warp/optim/linear.py +22 -4
  70. warp/render/render_opengl.py +130 -64
  71. warp/sim/__init__.py +6 -1
  72. warp/sim/collide.py +270 -26
  73. warp/sim/import_urdf.py +8 -8
  74. warp/sim/integrator_euler.py +25 -7
  75. warp/sim/integrator_featherstone.py +154 -35
  76. warp/sim/integrator_vbd.py +842 -40
  77. warp/sim/model.py +134 -72
  78. warp/sparse.py +1 -1
  79. warp/stubs.py +265 -132
  80. warp/tape.py +28 -30
  81. warp/tests/aux_test_module_unload.py +15 -0
  82. warp/tests/{test_sim_grad.py → flaky_test_sim_grad.py} +104 -63
  83. warp/tests/test_array.py +74 -0
  84. warp/tests/test_assert.py +242 -0
  85. warp/tests/test_codegen.py +14 -61
  86. warp/tests/test_collision.py +2 -2
  87. warp/tests/test_coloring.py +12 -2
  88. warp/tests/test_examples.py +12 -1
  89. warp/tests/test_func.py +21 -4
  90. warp/tests/test_grad_debug.py +87 -2
  91. warp/tests/test_hash_grid.py +1 -1
  92. warp/tests/test_ipc.py +116 -0
  93. warp/tests/test_lerp.py +13 -87
  94. warp/tests/test_mat.py +138 -167
  95. warp/tests/test_math.py +47 -1
  96. warp/tests/test_matmul.py +17 -16
  97. warp/tests/test_matmul_lite.py +10 -15
  98. warp/tests/test_mesh.py +84 -60
  99. warp/tests/test_mesh_query_aabb.py +165 -0
  100. warp/tests/test_mesh_query_point.py +328 -286
  101. warp/tests/test_mesh_query_ray.py +134 -121
  102. warp/tests/test_mlp.py +2 -2
  103. warp/tests/test_operators.py +43 -0
  104. warp/tests/test_overwrite.py +47 -2
  105. warp/tests/test_quat.py +77 -0
  106. warp/tests/test_reload.py +29 -0
  107. warp/tests/test_sim_grad_bounce_linear.py +204 -0
  108. warp/tests/test_smoothstep.py +17 -83
  109. warp/tests/test_static.py +19 -3
  110. warp/tests/test_tape.py +25 -0
  111. warp/tests/test_tile.py +178 -191
  112. warp/tests/test_tile_load.py +356 -0
  113. warp/tests/test_tile_mathdx.py +61 -8
  114. warp/tests/test_tile_mlp.py +17 -17
  115. warp/tests/test_tile_reduce.py +24 -18
  116. warp/tests/test_tile_shared_memory.py +66 -17
  117. warp/tests/test_tile_view.py +165 -0
  118. warp/tests/test_torch.py +35 -0
  119. warp/tests/test_utils.py +36 -24
  120. warp/tests/test_vec.py +110 -0
  121. warp/tests/unittest_suites.py +29 -4
  122. warp/tests/unittest_utils.py +30 -13
  123. warp/thirdparty/unittest_parallel.py +2 -2
  124. warp/types.py +411 -101
  125. warp/utils.py +10 -7
  126. {warp_lang-1.5.0.dist-info → warp_lang-1.6.0.dist-info}/METADATA +92 -69
  127. {warp_lang-1.5.0.dist-info → warp_lang-1.6.0.dist-info}/RECORD +130 -119
  128. {warp_lang-1.5.0.dist-info → warp_lang-1.6.0.dist-info}/WHEEL +1 -1
  129. warp/examples/benchmarks/benchmark_tile.py +0 -179
  130. warp/native/tile_gemm.h +0 -341
  131. {warp_lang-1.5.0.dist-info → warp_lang-1.6.0.dist-info}/LICENSE.md +0 -0
  132. {warp_lang-1.5.0.dist-info → warp_lang-1.6.0.dist-info}/top_level.txt +0 -0
@@ -9,9 +9,31 @@ import numpy as np
9
9
  import warp as wp
10
10
 
11
11
  from ..types import float32, matrix
12
+ from .collide import (
13
+ TriMeshCollisionDetector,
14
+ TriMeshCollisionInfo,
15
+ get_edge_colliding_edges,
16
+ get_edge_colliding_edges_count,
17
+ get_triangle_colliding_vertices,
18
+ get_triangle_colliding_vertices_count,
19
+ get_vertex_colliding_triangles,
20
+ get_vertex_colliding_triangles_count,
21
+ triangle_closest_point,
22
+ )
12
23
  from .integrator import Integrator
13
24
  from .model import PARTICLE_FLAG_ACTIVE, Control, Model, ModelShapeMaterials, State
14
25
 
26
+ wp.set_module_options({"enable_backward": False})
27
+
28
+ VBD_DEBUG_PRINTING_OPTIONS = {
29
+ # "elasticity_force_hessian",
30
+ # "contact_force_hessian",
31
+ # "overall_force_hessian",
32
+ # "inertia_force_hessian",
33
+ # "connectivity",
34
+ # "contact_info",
35
+ }
36
+
15
37
 
16
38
  class mat66(matrix(shape=(6, 6), dtype=float32)):
17
39
  pass
@@ -54,23 +76,23 @@ class ForceElementAdjacencyInfo:
54
76
 
55
77
 
56
78
  @wp.func
57
- def get_vertex_num_adjacent_edges(vertex: wp.int32, adjacency: ForceElementAdjacencyInfo):
79
+ def get_vertex_num_adjacent_edges(adjacency: ForceElementAdjacencyInfo, vertex: wp.int32):
58
80
  return (adjacency.v_adj_edges_offsets[vertex + 1] - adjacency.v_adj_edges_offsets[vertex]) >> 1
59
81
 
60
82
 
61
83
  @wp.func
62
- def get_vertex_adjacent_edge_id_order(vertex: wp.int32, edge: wp.int32, adjacency: ForceElementAdjacencyInfo):
84
+ def get_vertex_adjacent_edge_id_order(adjacency: ForceElementAdjacencyInfo, vertex: wp.int32, edge: wp.int32):
63
85
  offset = adjacency.v_adj_edges_offsets[vertex]
64
86
  return adjacency.v_adj_edges[offset + edge * 2], adjacency.v_adj_edges[offset + edge * 2 + 1]
65
87
 
66
88
 
67
89
  @wp.func
68
- def get_vertex_num_adjacent_faces(vertex: wp.int32, adjacency: ForceElementAdjacencyInfo):
90
+ def get_vertex_num_adjacent_faces(adjacency: ForceElementAdjacencyInfo, vertex: wp.int32):
69
91
  return (adjacency.v_adj_faces_offsets[vertex + 1] - adjacency.v_adj_faces_offsets[vertex]) >> 1
70
92
 
71
93
 
72
94
  @wp.func
73
- def get_vertex_adjacent_face_id_order(vertex: wp.int32, face: wp.int32, adjacency: ForceElementAdjacencyInfo):
95
+ def get_vertex_adjacent_face_id_order(adjacency: ForceElementAdjacencyInfo, vertex: wp.int32, face: wp.int32):
74
96
  offset = adjacency.v_adj_faces_offsets[vertex]
75
97
  return adjacency.v_adj_faces[offset + face * 2], adjacency.v_adj_faces[offset + face * 2 + 1]
76
98
 
@@ -83,9 +105,9 @@ def _test_compute_force_element_adjacency(
83
105
  ):
84
106
  wp.printf("num vertices: %d\n", adjacency.v_adj_edges_offsets.shape[0] - 1)
85
107
  for vertex in range(adjacency.v_adj_edges_offsets.shape[0] - 1):
86
- num_adj_edges = get_vertex_num_adjacent_edges(vertex, adjacency)
108
+ num_adj_edges = get_vertex_num_adjacent_edges(adjacency, vertex)
87
109
  for i_bd in range(num_adj_edges):
88
- bd_id, v_order = get_vertex_adjacent_edge_id_order(vertex, i_bd, adjacency)
110
+ bd_id, v_order = get_vertex_adjacent_edge_id_order(adjacency, vertex, i_bd)
89
111
 
90
112
  if edge_indices[bd_id, v_order] != vertex:
91
113
  print("Error!!!")
@@ -93,10 +115,14 @@ def _test_compute_force_element_adjacency(
93
115
  wp.printf("--iBd: %d | ", i_bd)
94
116
  wp.printf("edge id: %d | v_order: %d\n", bd_id, v_order)
95
117
 
96
- num_adj_faces = get_vertex_num_adjacent_faces(vertex, adjacency)
118
+ num_adj_faces = get_vertex_num_adjacent_faces(adjacency, vertex)
97
119
 
98
120
  for i_face in range(num_adj_faces):
99
- face, v_order = get_vertex_adjacent_face_id_order(vertex, i_face, adjacency)
121
+ face, v_order = get_vertex_adjacent_face_id_order(
122
+ adjacency,
123
+ vertex,
124
+ i_face,
125
+ )
100
126
 
101
127
  if face_indices[face, v_order] != vertex:
102
128
  print("Error!!!")
@@ -200,16 +226,6 @@ def evaluate_stvk_force_hessian(
200
226
  F = calculate_triangle_deformation_gradient(face, tri_indices, pos, tri_pose)
201
227
  G = green_strain(F)
202
228
 
203
- # wp.printf("face: %d, \nF12:\n, %f %f\n, %f %f\n, %f %f\nG:\n%f %f\n, %f %f\n",
204
- # face,
205
- # F[0, 0], F[0, 1],
206
- # F[1, 0], F[1, 1],
207
- # F[2, 0], F[2, 1],
208
- #
209
- # G[0, 0], G[0, 1],
210
- # G[1, 0], G[1, 1],
211
- # )
212
-
213
229
  S = 2.0 * mu * G + lmbd * (G[0, 0] + G[1, 1]) * wp.identity(n=2, dtype=float)
214
230
 
215
231
  F12 = -area * F * S * wp.transpose(tri_pose)
@@ -323,8 +339,8 @@ def evaluate_stvk_force_hessian(
323
339
 
324
340
  @wp.func
325
341
  def evaluate_ground_contact_force_hessian(
326
- vertex_pos: wp.vec3,
327
- vertex_prev_pos: wp.vec3,
342
+ particle_pos: wp.vec3,
343
+ particle_prev_pos: wp.vec3,
328
344
  particle_radius: float,
329
345
  ground_normal: wp.vec3,
330
346
  ground_level: float,
@@ -333,14 +349,14 @@ def evaluate_ground_contact_force_hessian(
333
349
  friction_epsilon: float,
334
350
  dt: float,
335
351
  ):
336
- penetration_depth = -(wp.dot(ground_normal, vertex_pos) + ground_level - particle_radius)
352
+ penetration_depth = -(wp.dot(ground_normal, particle_pos) + ground_level - particle_radius)
337
353
 
338
354
  if penetration_depth > 0:
339
355
  ground_contact_force_norm = penetration_depth * soft_contact_ke
340
356
  ground_contact_force = ground_normal * ground_contact_force_norm
341
357
  ground_contact_hessian = soft_contact_ke * wp.outer(ground_normal, ground_normal)
342
358
 
343
- dx = vertex_pos - vertex_prev_pos
359
+ dx = particle_pos - particle_prev_pos
344
360
 
345
361
  # friction
346
362
  e0, e1 = build_orthonormal_basis(ground_normal)
@@ -438,10 +454,221 @@ def evaluate_body_particle_contact(
438
454
  return body_contact_force, body_contact_hessian
439
455
 
440
456
 
457
+ @wp.func
458
+ def evaluate_self_contact_force_norm(dis: float, collision_radius: float, k: float):
459
+ # Adjust distance and calculate penetration depth
460
+
461
+ penetration_depth = collision_radius - dis
462
+
463
+ # Initialize outputs
464
+ dEdD = wp.float32(0.0)
465
+ d2E_dDdD = wp.float32(0.0)
466
+
467
+ # C2 continuity calculation
468
+ tau = collision_radius * 0.5
469
+ if tau > dis > 1e-5:
470
+ k2 = 0.5 * tau * tau * k
471
+ dEdD = -k2 / dis
472
+ d2E_dDdD = k2 / (dis * dis)
473
+ else:
474
+ dEdD = -k * penetration_depth
475
+ d2E_dDdD = k
476
+
477
+ return dEdD, d2E_dDdD
478
+
479
+
480
+ @wp.func
481
+ def evaluate_edge_edge_contact(
482
+ v: int,
483
+ v_order: int,
484
+ e1: int,
485
+ e2: int,
486
+ pos: wp.array(dtype=wp.vec3),
487
+ pos_prev: wp.array(dtype=wp.vec3),
488
+ edge_indices: wp.array(dtype=wp.int32, ndim=2),
489
+ collision_radius: float,
490
+ collision_stiffness: float,
491
+ friction_coefficient: float,
492
+ friction_epsilon: float,
493
+ dt: float,
494
+ edge_edge_parallel_epsilon: float,
495
+ ):
496
+ r"""
497
+ Returns the edge-edge contact force and hessian, including the friction force.
498
+ Args:
499
+ v:
500
+ v_order: \in {0, 1, 2, 3}, 0, 1 is vertex 0, 1 of e1, 2,3 is vertex 0, 1 of e2
501
+ e0
502
+ e1
503
+ pos
504
+ edge_indices
505
+ collision_radius
506
+ collision_stiffness
507
+ dt
508
+ """
509
+ e1_v1 = edge_indices[e1, 2]
510
+ e1_v2 = edge_indices[e1, 3]
511
+
512
+ e1_v1_pos = pos[e1_v1]
513
+ e1_v2_pos = pos[e1_v2]
514
+
515
+ e2_v1 = edge_indices[e2, 2]
516
+ e2_v2 = edge_indices[e2, 3]
517
+
518
+ e2_v1_pos = pos[e2_v1]
519
+ e2_v2_pos = pos[e2_v2]
520
+
521
+ st = wp.closest_point_edge_edge(e1_v1_pos, e1_v2_pos, e2_v1_pos, e2_v2_pos, edge_edge_parallel_epsilon)
522
+ s = st[0]
523
+ t = st[1]
524
+ e1_vec = e1_v2_pos - e1_v1_pos
525
+ e2_vec = e2_v2_pos - e2_v1_pos
526
+ c1 = e1_v1_pos + e1_vec * s
527
+ c2 = e2_v1_pos + e2_vec * t
528
+
529
+ # c1, c2, s, t = closest_point_edge_edge_2(e1_v1_pos, e1_v2_pos, e2_v1_pos, e2_v2_pos)
530
+
531
+ diff = c1 - c2
532
+ dis = st[2]
533
+ collision_normal = diff / dis
534
+
535
+ if dis < collision_radius:
536
+ bs = wp.vec4(1.0 - s, s, -1.0 + t, -t)
537
+ v_bary = bs[v_order]
538
+
539
+ dEdD, d2E_dDdD = evaluate_self_contact_force_norm(dis, collision_radius, collision_stiffness)
540
+
541
+ collision_force = -dEdD * v_bary * collision_normal
542
+ collision_hessian = d2E_dDdD * v_bary * v_bary * wp.outer(collision_normal, collision_normal)
543
+
544
+ # friction
545
+ c1_prev = pos_prev[e1_v1] + (pos_prev[e1_v2] - pos_prev[e1_v1]) * s
546
+ c2_prev = pos_prev[e2_v1] + (pos_prev[e2_v2] - pos_prev[e2_v1]) * t
547
+
548
+ dx = (c1 - c1_prev) - (c2 - c2_prev)
549
+ e1_vec_normalized = wp.normalize(e1_vec)
550
+ axis_2 = wp.normalize(wp.cross(e1_vec_normalized, collision_normal))
551
+
552
+ T = mat32(
553
+ e1_vec_normalized[0],
554
+ axis_2[0],
555
+ e1_vec_normalized[1],
556
+ axis_2[1],
557
+ e1_vec_normalized[2],
558
+ axis_2[2],
559
+ )
560
+
561
+ u = wp.transpose(T) * dx
562
+ eps_U = friction_epsilon * dt
563
+
564
+ # fmt: off
565
+ if wp.static("contact_force_hessian" in VBD_DEBUG_PRINTING_OPTIONS):
566
+ wp.printf(
567
+ " collision force:\n %f %f %f,\n collision hessian:\n %f %f %f,\n %f %f %f,\n %f %f %f\n",
568
+ collision_force[0], collision_force[1], collision_force[2], collision_hessian[0, 0], collision_hessian[0, 1], collision_hessian[0, 2], collision_hessian[1, 0], collision_hessian[1, 1], collision_hessian[1, 2], collision_hessian[2, 0], collision_hessian[2, 1], collision_hessian[2, 2],
569
+ )
570
+ # fmt: on
571
+
572
+ friction_force, friction_hessian = compute_friction(friction_coefficient, -dEdD, T, u, eps_U)
573
+ friction_force = friction_force * v_bary
574
+ friction_hessian = friction_hessian * v_bary * v_bary
575
+
576
+ # fmt: off
577
+ if wp.static("contact_force_hessian" in VBD_DEBUG_PRINTING_OPTIONS):
578
+ wp.printf(
579
+ " friction force:\n %f %f %f,\n friction hessian:\n %f %f %f,\n %f %f %f,\n %f %f %f\n",
580
+ friction_force[0], friction_force[1], friction_force[2], friction_hessian[0, 0], friction_hessian[0, 1], friction_hessian[0, 2], friction_hessian[1, 0], friction_hessian[1, 1], friction_hessian[1, 2], friction_hessian[2, 0], friction_hessian[2, 1], friction_hessian[2, 2],
581
+ )
582
+ # fmt: on
583
+
584
+ collision_force = collision_force + friction_force
585
+ collision_hessian = collision_hessian + friction_hessian
586
+ else:
587
+ collision_force = wp.vec3(0.0, 0.0, 0.0)
588
+ collision_hessian = wp.mat33(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0)
589
+
590
+ return collision_force, collision_hessian
591
+
592
+
593
+ @wp.func
594
+ def evaluate_vertex_triangle_collision_force_hessian(
595
+ v: int,
596
+ v_order: int,
597
+ tri: int,
598
+ pos: wp.array(dtype=wp.vec3),
599
+ pos_prev: wp.array(dtype=wp.vec3),
600
+ tri_indices: wp.array(dtype=wp.int32, ndim=2),
601
+ collision_radius: float,
602
+ collision_stiffness: float,
603
+ friction_coefficient: float,
604
+ friction_epsilon: float,
605
+ dt: float,
606
+ ):
607
+ a = pos[tri_indices[tri, 0]]
608
+ b = pos[tri_indices[tri, 1]]
609
+ c = pos[tri_indices[tri, 2]]
610
+
611
+ p = pos[v]
612
+
613
+ closest_p, bary, feature_type = triangle_closest_point(a, b, c, p)
614
+
615
+ diff = p - closest_p
616
+ dis = wp.length(diff)
617
+ collision_normal = diff / dis
618
+
619
+ if dis < collision_radius:
620
+ bs = wp.vec4(-bary[0], -bary[1], -bary[2], 1.0)
621
+ v_bary = bs[v_order]
622
+
623
+ dEdD, d2E_dDdD = evaluate_self_contact_force_norm(dis, collision_radius, collision_stiffness)
624
+
625
+ collision_force = -dEdD * v_bary * collision_normal
626
+ collision_hessian = d2E_dDdD * v_bary * v_bary * wp.outer(collision_normal, collision_normal)
627
+
628
+ # friction force
629
+ dx_v = p - pos_prev[v]
630
+
631
+ closest_p_prev = (
632
+ bary[0] * pos_prev[tri_indices[tri, 0]]
633
+ + bary[1] * pos_prev[tri_indices[tri, 1]]
634
+ + bary[2] * pos_prev[tri_indices[tri, 2]]
635
+ )
636
+
637
+ dx = dx_v - (closest_p - closest_p_prev)
638
+
639
+ e0 = wp.normalize(b - a)
640
+ e1 = wp.normalize(wp.cross(e0, collision_normal))
641
+
642
+ T = mat32(e0[0], e1[0], e0[1], e1[1], e0[2], e1[2])
643
+
644
+ u = wp.transpose(T) * dx
645
+ eps_U = friction_epsilon * dt
646
+
647
+ friction_force, friction_hessian = compute_friction(friction_coefficient, -dEdD, T, u, eps_U)
648
+
649
+ # fmt: off
650
+ if wp.static("contact_force_hessian" in VBD_DEBUG_PRINTING_OPTIONS):
651
+ wp.printf(
652
+ "v: %d dEdD: %f\nnormal force: %f %f %f\nfriction force: %f %f %f\n",
653
+ v,
654
+ dEdD,
655
+ collision_force[0], collision_force[1], collision_force[2], friction_force[0], friction_force[1], friction_force[2],
656
+ )
657
+ # fmt: on
658
+
659
+ collision_force = collision_force + v_bary * friction_force
660
+ collision_hessian = collision_hessian + v_bary * v_bary * friction_hessian
661
+ else:
662
+ collision_force = wp.vec3(0.0, 0.0, 0.0)
663
+ collision_hessian = wp.mat33(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0)
664
+
665
+ return collision_force, collision_hessian
666
+
667
+
441
668
  @wp.func
442
669
  def compute_friction(mu: float, normal_contact_force: float, T: mat32, u: wp.vec2, eps_u: float):
443
670
  """
444
- Returns the friction force and hessian.
671
+ Returns the 1D friction force and hessian.
445
672
  Args:
446
673
  mu: Friction coefficient.
447
674
  normal_contact_force: normal contact force.
@@ -496,7 +723,126 @@ def forward_step(
496
723
 
497
724
 
498
725
  @wp.kernel
499
- def VBD_solve_trimesh(
726
+ def forward_step_penetration_free(
727
+ dt: float,
728
+ gravity: wp.vec3,
729
+ prev_pos: wp.array(dtype=wp.vec3),
730
+ pos: wp.array(dtype=wp.vec3),
731
+ vel: wp.array(dtype=wp.vec3),
732
+ inv_mass: wp.array(dtype=float),
733
+ external_force: wp.array(dtype=wp.vec3),
734
+ particle_flags: wp.array(dtype=wp.uint32),
735
+ pos_prev_collision_detection: wp.array(dtype=wp.vec3),
736
+ particle_conservative_bounds: wp.array(dtype=float),
737
+ inertia: wp.array(dtype=wp.vec3),
738
+ ):
739
+ particle_index = wp.tid()
740
+
741
+ prev_pos[particle_index] = pos[particle_index]
742
+ if not particle_flags[particle_index] & PARTICLE_FLAG_ACTIVE:
743
+ inertia[particle_index] = prev_pos[particle_index]
744
+ return
745
+ vel_new = vel[particle_index] + (gravity + external_force[particle_index] * inv_mass[particle_index]) * dt
746
+ pos_inertia = pos[particle_index] + vel_new * dt
747
+ inertia[particle_index] = pos_inertia
748
+
749
+ pos[particle_index] = apply_conservative_bound_truncation(
750
+ particle_index, pos_inertia, pos_prev_collision_detection, particle_conservative_bounds
751
+ )
752
+
753
+
754
+ @wp.kernel
755
+ def compute_particle_conservative_bound(
756
+ # inputs
757
+ conservative_bound_relaxation: float,
758
+ collision_query_radius: float,
759
+ adjacency: ForceElementAdjacencyInfo,
760
+ collision_info: TriMeshCollisionInfo,
761
+ # outputs
762
+ particle_conservative_bounds: wp.array(dtype=float),
763
+ ):
764
+ particle_index = wp.tid()
765
+ min_dist = wp.min(collision_query_radius, collision_info.vertex_colliding_triangles_min_dist[particle_index])
766
+
767
+ # bound from neighbor triangles
768
+ for i_adj_tri in range(
769
+ get_vertex_num_adjacent_faces(
770
+ adjacency,
771
+ particle_index,
772
+ )
773
+ ):
774
+ tri_index, vertex_order = get_vertex_adjacent_face_id_order(
775
+ adjacency,
776
+ particle_index,
777
+ i_adj_tri,
778
+ )
779
+ min_dist = wp.min(min_dist, collision_info.triangle_colliding_vertices_min_dist[tri_index])
780
+
781
+ # bound from neighbor edges
782
+ for i_adj_edge in range(
783
+ get_vertex_num_adjacent_edges(
784
+ adjacency,
785
+ particle_index,
786
+ )
787
+ ):
788
+ nei_edge_index, vertex_order_on_edge = get_vertex_adjacent_edge_id_order(
789
+ adjacency,
790
+ particle_index,
791
+ i_adj_edge,
792
+ )
793
+ # vertex is on the edge; otherwise it only effects the bending energy
794
+ if vertex_order_on_edge == 2 or vertex_order_on_edge == 3:
795
+ # collisions of neighbor edges
796
+ min_dist = wp.min(min_dist, collision_info.edge_colliding_edges_min_dist[nei_edge_index])
797
+
798
+ particle_conservative_bounds[particle_index] = conservative_bound_relaxation * min_dist
799
+
800
+
801
+ @wp.kernel
802
+ def validate_conservative_bound(
803
+ pos: wp.array(dtype=wp.vec3),
804
+ pos_prev_collision_detection: wp.array(dtype=wp.vec3),
805
+ particle_conservative_bounds: wp.array(dtype=float),
806
+ ):
807
+ v_index = wp.tid()
808
+
809
+ displacement = wp.length(pos[v_index] - pos_prev_collision_detection[v_index])
810
+
811
+ if displacement > particle_conservative_bounds[v_index] * 1.01 and displacement > 1e-5:
812
+ # wp.expect_eq(displacement <= particle_conservative_bounds[v_index] * 1.01, True)
813
+ wp.printf(
814
+ "Vertex %d has moved by %f exceeded the limit of %f\n",
815
+ v_index,
816
+ displacement,
817
+ particle_conservative_bounds[v_index],
818
+ )
819
+
820
+
821
+ @wp.func
822
+ def apply_conservative_bound_truncation(
823
+ v_index: wp.int32,
824
+ pos_new: wp.vec3,
825
+ pos_prev_collision_detection: wp.array(dtype=wp.vec3),
826
+ particle_conservative_bounds: wp.array(dtype=float),
827
+ ):
828
+ particle_pos_prev_collision_detection = pos_prev_collision_detection[v_index]
829
+ accumulated_displacement = pos_new - particle_pos_prev_collision_detection
830
+ conservative_bound = particle_conservative_bounds[v_index]
831
+
832
+ accumulated_displacement_norm = wp.length(accumulated_displacement)
833
+ if accumulated_displacement_norm > conservative_bound and conservative_bound > 1e-5:
834
+ accumulated_displacement_norm_truncated = conservative_bound
835
+ accumulated_displacement = accumulated_displacement * (
836
+ accumulated_displacement_norm_truncated / accumulated_displacement_norm
837
+ )
838
+
839
+ return particle_pos_prev_collision_detection + accumulated_displacement
840
+ else:
841
+ return pos_new
842
+
843
+
844
+ @wp.kernel
845
+ def VBD_solve_trimesh_no_self_contact(
500
846
  dt: float,
501
847
  particle_ids_in_color: wp.array(dtype=wp.int32),
502
848
  prev_pos: wp.array(dtype=wp.vec3),
@@ -538,7 +884,6 @@ def VBD_solve_trimesh(
538
884
  tid = wp.tid()
539
885
 
540
886
  particle_index = particle_ids_in_color[tid]
541
- # wp.printf("vId: %d\n", particle)
542
887
 
543
888
  if not particle_flags[particle_index] & PARTICLE_FLAG_ACTIVE:
544
889
  return
@@ -553,12 +898,24 @@ def VBD_solve_trimesh(
553
898
  h = mass[particle_index] * dt_sqr_reciprocal * wp.identity(n=3, dtype=float)
554
899
 
555
900
  # elastic force and hessian
556
- for i_adj_tri in range(get_vertex_num_adjacent_faces(particle_index, adjacency)):
557
- # wp.printf("particle: %d | num_adj_faces: %d | ", particle, get_particle_num_adjacent_faces(particle, adjacency))
558
- tri_id, particle_order = get_vertex_adjacent_face_id_order(particle_index, i_adj_tri, adjacency)
559
-
560
- # wp.printf("i_face: %d | face id: %d | v_order: %d | ", i_adj_tri, tri_id, particle_order)
561
- # wp.printf("face: %d %d %d\n", tri_indices[tri_id, 0], tri_indices[tri_id, 1], tri_indices[tri_id, 2], )
901
+ for i_adj_tri in range(get_vertex_num_adjacent_faces(adjacency, particle_index)):
902
+ tri_id, particle_order = get_vertex_adjacent_face_id_order(adjacency, particle_index, i_adj_tri)
903
+
904
+ # fmt: off
905
+ if wp.static("connectivity" in VBD_DEBUG_PRINTING_OPTIONS):
906
+ wp.printf(
907
+ "particle: %d | num_adj_faces: %d | ",
908
+ particle_index,
909
+ get_vertex_num_adjacent_faces(particle_index, adjacency),
910
+ )
911
+ wp.printf("i_face: %d | face id: %d | v_order: %d | ", i_adj_tri, tri_id, particle_order)
912
+ wp.printf(
913
+ "face: %d %d %d\n",
914
+ tri_indices[tri_id, 0],
915
+ tri_indices[tri_id, 1],
916
+ tri_indices[tri_id, 2],
917
+ )
918
+ # fmt: on
562
919
 
563
920
  f_tri, h_tri = evaluate_stvk_force_hessian(
564
921
  tri_id,
@@ -580,13 +937,16 @@ def VBD_solve_trimesh(
580
937
  f = f + f_tri + f_d
581
938
  h = h + h_tri + h_d
582
939
 
583
- # wp.printf("particle: %d, i_adj_tri: %d, particle_order: %d, \nforce:\n %f %f %f, \nhessian:, \n%f %f %f, \n%f %f %f, \n%f %f %f\n",
584
- # particle, i_adj_tri, particle_order,
585
- # f[0], f[1], f[2],
586
- # h[0, 0], h[0, 1], h[0, 2],
587
- # h[1, 0], h[1, 1], h[1, 2],
588
- # h[2, 0], h[2, 1], h[2, 2],
589
- # )
940
+ # fmt: off
941
+ if wp.static("elasticity_force_hessian" in VBD_DEBUG_PRINTING_OPTIONS):
942
+ wp.printf(
943
+ "particle: %d, i_adj_tri: %d, particle_order: %d, \nforce:\n %f %f %f, \nhessian:, \n%f %f %f, \n%f %f %f, \n%f %f %f\n",
944
+ particle_index,
945
+ i_adj_tri,
946
+ particle_order,
947
+ f[0], f[1], f[2], h[0, 0], h[0, 1], h[0, 2], h[1, 0], h[1, 1], h[1, 2], h[2, 0], h[2, 1], h[2, 2],
948
+ )
949
+ # fmt: on
590
950
 
591
951
  # body-particle contact
592
952
  particle_contact_count = min(body_particle_contact_count[particle_index], body_particle_contact_buffer_pre_alloc)
@@ -687,6 +1047,297 @@ def convert_body_particle_contact_data_kernel(
687
1047
  body_particle_contact_buffer[offset + contact_counter] = contact_index
688
1048
 
689
1049
 
1050
+ @wp.kernel
1051
+ def VBD_solve_trimesh_with_self_contact_penetration_free(
1052
+ dt: float,
1053
+ particle_ids_in_color: wp.array(dtype=wp.int32),
1054
+ pos_prev: wp.array(dtype=wp.vec3),
1055
+ pos: wp.array(dtype=wp.vec3),
1056
+ pos_new: wp.array(dtype=wp.vec3),
1057
+ vel: wp.array(dtype=wp.vec3),
1058
+ mass: wp.array(dtype=float),
1059
+ inertia: wp.array(dtype=wp.vec3),
1060
+ particle_flags: wp.array(dtype=wp.uint32),
1061
+ tri_indices: wp.array(dtype=wp.int32, ndim=2),
1062
+ tri_poses: wp.array(dtype=wp.mat22),
1063
+ tri_materials: wp.array(dtype=float, ndim=2),
1064
+ tri_areas: wp.array(dtype=float),
1065
+ edge_indices: wp.array(dtype=wp.int32, ndim=2),
1066
+ adjacency: ForceElementAdjacencyInfo,
1067
+ # contact info
1068
+ # self contact
1069
+ collision_info: TriMeshCollisionInfo,
1070
+ collision_radius: float,
1071
+ soft_contact_ke: float,
1072
+ friction_mu: float,
1073
+ friction_epsilon: float,
1074
+ pos_prev_collision_detection: wp.array(dtype=wp.vec3),
1075
+ particle_conservative_bounds: wp.array(dtype=float),
1076
+ edge_edge_parallel_epsilon: float,
1077
+ # body-particle contact
1078
+ particle_radius: wp.array(dtype=float),
1079
+ body_particle_contact_buffer_pre_alloc: int,
1080
+ body_particle_contact_buffer: wp.array(dtype=int),
1081
+ body_particle_contact_count: wp.array(dtype=int),
1082
+ shape_materials: ModelShapeMaterials,
1083
+ shape_body: wp.array(dtype=int),
1084
+ body_q: wp.array(dtype=wp.transform),
1085
+ body_qd: wp.array(dtype=wp.spatial_vector),
1086
+ body_com: wp.array(dtype=wp.vec3),
1087
+ contact_shape: wp.array(dtype=int),
1088
+ contact_body_pos: wp.array(dtype=wp.vec3),
1089
+ contact_body_vel: wp.array(dtype=wp.vec3),
1090
+ contact_normal: wp.array(dtype=wp.vec3),
1091
+ # ground-particle contact
1092
+ has_ground: bool,
1093
+ ground: wp.array(dtype=float),
1094
+ ):
1095
+ t_id = wp.tid()
1096
+
1097
+ particle_index = particle_ids_in_color[t_id]
1098
+ particle_pos = pos[particle_index]
1099
+ particle_prev_pos = pos_prev[particle_index]
1100
+
1101
+ if not particle_flags[particle_index] & PARTICLE_FLAG_ACTIVE:
1102
+ return
1103
+
1104
+ dt_sqr_reciprocal = 1.0 / (dt * dt)
1105
+
1106
+ # inertia force and hessian
1107
+ f = mass[particle_index] * (inertia[particle_index] - pos[particle_index]) * (dt_sqr_reciprocal)
1108
+ h = mass[particle_index] * dt_sqr_reciprocal * wp.identity(n=3, dtype=float)
1109
+
1110
+ # fmt: off
1111
+ if wp.static("inertia_force_hessian" in VBD_DEBUG_PRINTING_OPTIONS):
1112
+ wp.printf(
1113
+ "particle: %d after accumulate inertia\nforce:\n %f %f %f, \nhessian:, \n%f %f %f, \n%f %f %f, \n%f %f %f\n",
1114
+ particle_index,
1115
+ f[0], f[1], f[2], h[0, 0], h[0, 1], h[0, 2], h[1, 0], h[1, 1], h[1, 2], h[2, 0], h[2, 1], h[2, 2],
1116
+ )
1117
+
1118
+ # v-side of the v-f collision force
1119
+ if wp.static("contact_info" in VBD_DEBUG_PRINTING_OPTIONS):
1120
+ wp.printf("Has %d colliding triangles\n", get_vertex_colliding_triangles_count(collision_info, particle_index))
1121
+ for i_v_collision in range(get_vertex_colliding_triangles_count(collision_info, particle_index)):
1122
+ colliding_t = get_vertex_colliding_triangles(collision_info, particle_index, i_v_collision)
1123
+ if wp.static("contact_info" in VBD_DEBUG_PRINTING_OPTIONS):
1124
+ wp.printf(
1125
+ "vertex %d is colliding with triangle: %d-(%d, %d, %d)",
1126
+ particle_index,
1127
+ colliding_t,
1128
+ tri_indices[colliding_t, 0],
1129
+ tri_indices[colliding_t, 1],
1130
+ tri_indices[colliding_t, 2],
1131
+ )
1132
+ # fmt: on
1133
+
1134
+ collision_force, collision_hessian = evaluate_vertex_triangle_collision_force_hessian(
1135
+ particle_index,
1136
+ 3,
1137
+ colliding_t,
1138
+ pos,
1139
+ pos_prev,
1140
+ tri_indices,
1141
+ collision_radius,
1142
+ soft_contact_ke,
1143
+ friction_mu,
1144
+ friction_epsilon,
1145
+ dt,
1146
+ )
1147
+ f = f + collision_force
1148
+ h = h + collision_hessian
1149
+
1150
+ # fmt: off
1151
+ if wp.static("contact_force_hessian" in VBD_DEBUG_PRINTING_OPTIONS):
1152
+ wp.printf(
1153
+ "vertex: %d collision %d:\nforce:\n %f %f %f, \nhessian:, \n%f %f %f, \n%f %f %f, \n%f %f %f\n",
1154
+ particle_index,
1155
+ i_v_collision,
1156
+ collision_force[0], collision_force[1], collision_force[2], collision_hessian[0, 0], collision_hessian[0, 1], collision_hessian[0, 2], collision_hessian[1, 0], collision_hessian[1, 1], collision_hessian[1, 2], collision_hessian[2, 0], collision_hessian[2, 1], collision_hessian[2, 2],
1157
+ )
1158
+ # fmt: on
1159
+
1160
+ # elastic force and hessian
1161
+ for i_adj_tri in range(get_vertex_num_adjacent_faces(adjacency, particle_index)):
1162
+ tri_index, vertex_order = get_vertex_adjacent_face_id_order(adjacency, particle_index, i_adj_tri)
1163
+
1164
+ # fmt: off
1165
+ if wp.static("connectivity" in VBD_DEBUG_PRINTING_OPTIONS):
1166
+ wp.printf(
1167
+ "particle: %d | num_adj_faces: %d | ",
1168
+ particle_index,
1169
+ get_vertex_num_adjacent_faces(particle_index, adjacency),
1170
+ )
1171
+ wp.printf("i_face: %d | face id: %d | v_order: %d | ", i_adj_tri, tri_index, vertex_order)
1172
+ wp.printf(
1173
+ "face: %d %d %d\n",
1174
+ tri_indices[tri_index, 0],
1175
+ tri_indices[tri_index, 1],
1176
+ tri_indices[tri_index, 2],
1177
+ )
1178
+ # fmt: on
1179
+
1180
+ f_tri, h_tri = evaluate_stvk_force_hessian(
1181
+ tri_index,
1182
+ vertex_order,
1183
+ pos,
1184
+ tri_indices,
1185
+ tri_poses[tri_index],
1186
+ tri_areas[tri_index],
1187
+ tri_materials[tri_index, 0],
1188
+ tri_materials[tri_index, 1],
1189
+ tri_materials[tri_index, 2],
1190
+ )
1191
+ # compute damping
1192
+ k_d = tri_materials[tri_index, 2]
1193
+ h_d = h_tri * (k_d / dt)
1194
+
1195
+ f_d = h_d * (pos_prev[particle_index] - pos[particle_index])
1196
+
1197
+ f = f + f_tri + f_d
1198
+ h = h + h_tri + h_d
1199
+
1200
+ # t-side of vt-collision from the neighbor triangles
1201
+ # fmt: off
1202
+ if wp.static("contact_info" in VBD_DEBUG_PRINTING_OPTIONS):
1203
+ wp.printf(
1204
+ "Nei triangle %d has %d colliding vertice\n",
1205
+ tri_index,
1206
+ get_triangle_colliding_vertices_count(collision_info, tri_index),
1207
+ )
1208
+ # fmt: on
1209
+ for i_t_collision in range(get_triangle_colliding_vertices_count(collision_info, tri_index)):
1210
+ colliding_v = get_triangle_colliding_vertices(collision_info, tri_index, i_t_collision)
1211
+
1212
+ collision_force, collision_hessian = evaluate_vertex_triangle_collision_force_hessian(
1213
+ colliding_v,
1214
+ vertex_order,
1215
+ tri_index,
1216
+ pos,
1217
+ pos_prev,
1218
+ tri_indices,
1219
+ collision_radius,
1220
+ soft_contact_ke,
1221
+ friction_mu,
1222
+ friction_epsilon,
1223
+ dt,
1224
+ )
1225
+
1226
+ f = f + collision_force
1227
+ h = h + collision_hessian
1228
+
1229
+ # edge-edge collision force and hessian
1230
+ for i_adj_edge in range(get_vertex_num_adjacent_edges(adjacency, particle_index)):
1231
+ nei_edge_index, vertex_order_on_edge = get_vertex_adjacent_edge_id_order(adjacency, particle_index, i_adj_edge)
1232
+ # vertex is on the edge; otherwise it only effects the bending energy n
1233
+ if vertex_order_on_edge == 2 or vertex_order_on_edge == 3:
1234
+ # collisions of neighbor triangles
1235
+ if wp.static("contact_info" in VBD_DEBUG_PRINTING_OPTIONS):
1236
+ wp.printf(
1237
+ "Nei edge %d has %d colliding edge\n",
1238
+ nei_edge_index,
1239
+ get_edge_colliding_edges_count(collision_info, nei_edge_index),
1240
+ )
1241
+ for i_e_collision in range(get_edge_colliding_edges_count(collision_info, nei_edge_index)):
1242
+ colliding_e = get_edge_colliding_edges(collision_info, nei_edge_index, i_e_collision)
1243
+
1244
+ collision_force, collision_hessian = evaluate_edge_edge_contact(
1245
+ particle_index,
1246
+ vertex_order_on_edge - 2,
1247
+ nei_edge_index,
1248
+ colliding_e,
1249
+ pos,
1250
+ pos_prev,
1251
+ edge_indices,
1252
+ collision_radius,
1253
+ soft_contact_ke,
1254
+ friction_mu,
1255
+ friction_epsilon,
1256
+ dt,
1257
+ edge_edge_parallel_epsilon,
1258
+ )
1259
+ f = f + collision_force
1260
+ h = h + collision_hessian
1261
+
1262
+ # fmt: off
1263
+ if wp.static("contact_force_hessian" in VBD_DEBUG_PRINTING_OPTIONS):
1264
+ wp.printf(
1265
+ "vertex: %d edge %d collision %d:\nforce:\n %f %f %f, \nhessian:, \n%f %f %f, \n%f %f %f, \n%f %f %f\n",
1266
+ particle_index,
1267
+ nei_edge_index,
1268
+ i_e_collision,
1269
+ collision_force[0], collision_force[1], collision_force[2], collision_hessian[0, 0], collision_hessian[0, 1], collision_hessian[0, 2], collision_hessian[1, 0], collision_hessian[1, 1], collision_hessian[1, 2], collision_hessian[2, 0], collision_hessian[2, 1], collision_hessian[2, 2],
1270
+ )
1271
+ # fmt: on
1272
+
1273
+ # body-particle contact
1274
+ particle_contact_count = min(body_particle_contact_count[particle_index], body_particle_contact_buffer_pre_alloc)
1275
+
1276
+ offset = body_particle_contact_buffer_pre_alloc * particle_index
1277
+ for contact_counter in range(particle_contact_count):
1278
+ # the index to access body-particle data, which is size-variable and only contains active contact
1279
+ contact_index = body_particle_contact_buffer[offset + contact_counter]
1280
+
1281
+ body_contact_force, body_contact_hessian = evaluate_body_particle_contact(
1282
+ particle_index,
1283
+ particle_pos,
1284
+ particle_prev_pos,
1285
+ contact_index,
1286
+ soft_contact_ke,
1287
+ friction_mu,
1288
+ friction_epsilon,
1289
+ particle_radius,
1290
+ shape_materials,
1291
+ shape_body,
1292
+ body_q,
1293
+ body_qd,
1294
+ body_com,
1295
+ contact_shape,
1296
+ contact_body_pos,
1297
+ contact_body_vel,
1298
+ contact_normal,
1299
+ dt,
1300
+ )
1301
+
1302
+ f = f + body_contact_force
1303
+ h = h + body_contact_hessian
1304
+
1305
+ if has_ground:
1306
+ ground_normal = wp.vec3(ground[0], ground[1], ground[2])
1307
+ ground_level = ground[3]
1308
+ ground_contact_force, ground_contact_hessian = evaluate_ground_contact_force_hessian(
1309
+ particle_pos,
1310
+ particle_prev_pos,
1311
+ particle_radius[particle_index],
1312
+ ground_normal,
1313
+ ground_level,
1314
+ soft_contact_ke,
1315
+ friction_mu,
1316
+ friction_epsilon,
1317
+ dt,
1318
+ )
1319
+
1320
+ f = f + ground_contact_force
1321
+ h = h + ground_contact_hessian
1322
+
1323
+ # fmt: off
1324
+ if wp.static("overall_force_hessian" in VBD_DEBUG_PRINTING_OPTIONS):
1325
+ wp.printf(
1326
+ "vertex: %d final\noverall force:\n %f %f %f, \noverall hessian:, \n%f %f %f, \n%f %f %f, \n%f %f %f\n",
1327
+ particle_index,
1328
+ f[0], f[1], f[2], h[0, 0], h[0, 1], h[0, 2], h[1, 0], h[1, 1], h[1, 2], h[2, 0], h[2, 1], h[2, 2],
1329
+ )
1330
+ # fmt: on
1331
+
1332
+ if abs(wp.determinant(h)) > 1e-5:
1333
+ h_inv = wp.inverse(h)
1334
+ particle_pos_new = pos[particle_index] + h_inv * f
1335
+
1336
+ pos_new[particle_index] = apply_conservative_bound_truncation(
1337
+ particle_index, particle_pos_new, pos_prev_collision_detection, particle_conservative_bounds
1338
+ )
1339
+
1340
+
690
1341
  class VBDIntegrator(Integrator):
691
1342
  """An implicit integrator using Vertex Block Descent (VBD) for cloth simulation.
692
1343
 
@@ -718,8 +1369,14 @@ class VBDIntegrator(Integrator):
718
1369
  self,
719
1370
  model: Model,
720
1371
  iterations=10,
721
- body_particle_contact_buffer_pre_alloc=4,
1372
+ handle_self_contact=False,
1373
+ penetration_free_conservative_bound_relaxation=0.42,
722
1374
  friction_epsilon=1e-2,
1375
+ body_particle_contact_buffer_pre_alloc=4,
1376
+ vertex_collision_buffer_pre_alloc=32,
1377
+ edge_collision_buffer_pre_alloc=64,
1378
+ triangle_collision_buffer_pre_alloc=32,
1379
+ edge_edge_parallel_epsilon=1e-5,
723
1380
  ):
724
1381
  self.device = model.device
725
1382
  self.model = model
@@ -731,6 +1388,7 @@ class VBDIntegrator(Integrator):
731
1388
 
732
1389
  self.adjacency = self.compute_force_element_adjacency(model).to(self.device)
733
1390
 
1391
+ # data for body-particle collision
734
1392
  self.body_particle_contact_buffer_pre_alloc = body_particle_contact_buffer_pre_alloc
735
1393
  self.body_particle_contact_buffer = wp.zeros(
736
1394
  (self.body_particle_contact_buffer_pre_alloc * model.particle_count,),
@@ -738,6 +1396,28 @@ class VBDIntegrator(Integrator):
738
1396
  device=self.device,
739
1397
  )
740
1398
  self.body_particle_contact_count = wp.zeros((model.particle_count,), dtype=wp.int32, device=self.device)
1399
+
1400
+ self.handle_self_contact = handle_self_contact
1401
+
1402
+ if handle_self_contact:
1403
+ if self.model.soft_contact_margin < self.model.soft_contact_radius:
1404
+ raise ValueError(
1405
+ "model.soft_contact_margin is smaller than self.model.soft_contact_radius, this will result in missing contacts and cause instability. \n"
1406
+ "It is advisable to make model.soft_contact_margin 1.5~2 times larger than self.model.soft_contact_radius."
1407
+ )
1408
+
1409
+ self.conservative_bound_relaxation = penetration_free_conservative_bound_relaxation
1410
+ self.pos_prev_collision_detection = wp.zeros_like(model.particle_q, device=self.device)
1411
+ self.particle_conservative_bounds = wp.full((model.particle_count,), dtype=float, device=self.device)
1412
+
1413
+ self.trimesh_collision_detector = TriMeshCollisionDetector(
1414
+ self.model,
1415
+ vertex_collision_buffer_pre_alloc=vertex_collision_buffer_pre_alloc,
1416
+ edge_collision_buffer_pre_alloc=edge_collision_buffer_pre_alloc,
1417
+ triangle_collision_buffer_pre_alloc=triangle_collision_buffer_pre_alloc,
1418
+ edge_edge_parallel_epsilon=edge_edge_parallel_epsilon,
1419
+ )
1420
+
741
1421
  self.friction_epsilon = friction_epsilon
742
1422
 
743
1423
  if len(self.model.particle_coloring) == 0:
@@ -837,6 +1517,14 @@ class VBDIntegrator(Integrator):
837
1517
  if model is not self.model:
838
1518
  raise ValueError("model must be the one used to initialize VBDIntegrator")
839
1519
 
1520
+ if self.handle_self_contact:
1521
+ self.simulate_one_step_with_collisions_penetration_free(model, state_in, state_out, dt, control)
1522
+ else:
1523
+ self.simulate_one_step_no_self_contact(model, state_in, state_out, dt, control)
1524
+
1525
+ def simulate_one_step_no_self_contact(
1526
+ self, model: Model, state_in: State, state_out: State, dt: float, control: Control = None
1527
+ ):
840
1528
  self.convert_body_particle_contact_data()
841
1529
 
842
1530
  wp.launch(
@@ -859,7 +1547,7 @@ class VBDIntegrator(Integrator):
859
1547
  for _iter in range(self.iterations):
860
1548
  for color_counter in range(len(self.model.particle_coloring)):
861
1549
  wp.launch(
862
- kernel=VBD_solve_trimesh,
1550
+ kernel=VBD_solve_trimesh_no_self_contact,
863
1551
  inputs=[
864
1552
  dt,
865
1553
  self.model.particle_coloring[color_counter],
@@ -914,6 +1602,120 @@ class VBDIntegrator(Integrator):
914
1602
  device=self.device,
915
1603
  )
916
1604
 
1605
+ def simulate_one_step_with_collisions_penetration_free(
1606
+ self, model: Model, state_in: State, state_out: State, dt: float, control: Control = None
1607
+ ):
1608
+ self.convert_body_particle_contact_data()
1609
+ # collision detection before initialization to compute conservative bounds for initialization
1610
+ self.collision_detection_penetration_free(state_in, dt)
1611
+
1612
+ wp.launch(
1613
+ kernel=forward_step_penetration_free,
1614
+ inputs=[
1615
+ dt,
1616
+ model.gravity,
1617
+ self.particle_q_prev,
1618
+ state_in.particle_q,
1619
+ state_in.particle_qd,
1620
+ self.model.particle_inv_mass,
1621
+ state_in.particle_f,
1622
+ self.model.particle_flags,
1623
+ self.pos_prev_collision_detection,
1624
+ self.particle_conservative_bounds,
1625
+ self.inertia,
1626
+ ],
1627
+ dim=self.model.particle_count,
1628
+ device=self.device,
1629
+ )
1630
+
1631
+ # after initialization, we do another collision detection to update the bounds
1632
+ self.collision_detection_penetration_free(state_in, dt)
1633
+
1634
+ for _iter in range(self.iterations):
1635
+ for i_color in range(len(self.model.particle_coloring)):
1636
+ wp.launch(
1637
+ kernel=VBD_solve_trimesh_with_self_contact_penetration_free,
1638
+ inputs=[
1639
+ dt,
1640
+ self.model.particle_coloring[i_color],
1641
+ self.particle_q_prev,
1642
+ state_in.particle_q,
1643
+ state_out.particle_q,
1644
+ state_in.particle_qd,
1645
+ self.model.particle_mass,
1646
+ self.inertia,
1647
+ self.model.particle_flags,
1648
+ self.model.tri_indices,
1649
+ self.model.tri_poses,
1650
+ self.model.tri_materials,
1651
+ self.model.tri_areas,
1652
+ self.model.edge_indices,
1653
+ self.adjacency,
1654
+ # self-contact
1655
+ self.trimesh_collision_detector.collision_info,
1656
+ self.model.soft_contact_radius,
1657
+ self.model.soft_contact_ke,
1658
+ self.model.soft_contact_mu,
1659
+ self.friction_epsilon,
1660
+ self.pos_prev_collision_detection,
1661
+ self.particle_conservative_bounds,
1662
+ self.trimesh_collision_detector.edge_edge_parallel_epsilon,
1663
+ # body-particle contact
1664
+ self.model.particle_radius,
1665
+ self.body_particle_contact_buffer_pre_alloc,
1666
+ self.body_particle_contact_buffer,
1667
+ self.body_particle_contact_count,
1668
+ self.model.shape_materials,
1669
+ self.model.shape_body,
1670
+ self.model.body_q,
1671
+ self.model.body_qd,
1672
+ self.model.body_com,
1673
+ self.model.soft_contact_shape,
1674
+ self.model.soft_contact_body_pos,
1675
+ self.model.soft_contact_body_vel,
1676
+ self.model.soft_contact_normal,
1677
+ self.model.ground,
1678
+ self.model.ground_plane,
1679
+ ],
1680
+ dim=self.model.particle_coloring[i_color].shape[0],
1681
+ device=self.device,
1682
+ )
1683
+
1684
+ wp.launch(
1685
+ kernel=VBD_copy_particle_positions_back,
1686
+ inputs=[self.model.particle_coloring[i_color], state_in.particle_q, state_out.particle_q],
1687
+ dim=self.model.particle_coloring[i_color].size,
1688
+ device=self.device,
1689
+ )
1690
+
1691
+ wp.launch(
1692
+ kernel=update_velocity,
1693
+ inputs=[dt, self.particle_q_prev, state_out.particle_q, state_out.particle_qd],
1694
+ dim=self.model.particle_count,
1695
+ device=self.device,
1696
+ )
1697
+
1698
+ def collision_detection_penetration_free(self, current_state, dt):
1699
+ self.trimesh_collision_detector.refit(current_state.particle_q)
1700
+ self.trimesh_collision_detector.vertex_triangle_collision_detection(self.model.soft_contact_margin)
1701
+ self.trimesh_collision_detector.edge_edge_collision_detection(self.model.soft_contact_margin)
1702
+
1703
+ self.pos_prev_collision_detection.assign(current_state.particle_q)
1704
+ wp.launch(
1705
+ kernel=compute_particle_conservative_bound,
1706
+ inputs=[
1707
+ self.conservative_bound_relaxation,
1708
+ self.model.soft_contact_margin,
1709
+ self.adjacency,
1710
+ self.trimesh_collision_detector.collision_info,
1711
+ ],
1712
+ outputs=[
1713
+ self.particle_conservative_bounds,
1714
+ ],
1715
+ dim=self.model.particle_count,
1716
+ device=self.device,
1717
+ )
1718
+
917
1719
  def convert_body_particle_contact_data(self):
918
1720
  self.body_particle_contact_count.zero_()
919
1721