warp-lang 1.9.0__py3-none-win_amd64.whl → 1.10.0rc2__py3-none-win_amd64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of warp-lang might be problematic. Click here for more details.

Files changed (350) hide show
  1. warp/__init__.py +301 -287
  2. warp/__init__.pyi +2220 -313
  3. warp/_src/__init__.py +14 -0
  4. warp/_src/autograd.py +1075 -0
  5. warp/_src/build.py +618 -0
  6. warp/_src/build_dll.py +640 -0
  7. warp/{builtins.py → _src/builtins.py} +1497 -226
  8. warp/_src/codegen.py +4359 -0
  9. warp/{config.py → _src/config.py} +178 -169
  10. warp/_src/constants.py +57 -0
  11. warp/_src/context.py +8294 -0
  12. warp/_src/dlpack.py +462 -0
  13. warp/_src/fabric.py +355 -0
  14. warp/_src/fem/__init__.py +14 -0
  15. warp/_src/fem/adaptivity.py +508 -0
  16. warp/_src/fem/cache.py +687 -0
  17. warp/_src/fem/dirichlet.py +188 -0
  18. warp/{fem → _src/fem}/domain.py +40 -30
  19. warp/_src/fem/field/__init__.py +131 -0
  20. warp/_src/fem/field/field.py +701 -0
  21. warp/{fem → _src/fem}/field/nodal_field.py +30 -15
  22. warp/{fem → _src/fem}/field/restriction.py +1 -1
  23. warp/{fem → _src/fem}/field/virtual.py +53 -27
  24. warp/_src/fem/geometry/__init__.py +32 -0
  25. warp/{fem → _src/fem}/geometry/adaptive_nanogrid.py +77 -163
  26. warp/_src/fem/geometry/closest_point.py +97 -0
  27. warp/{fem → _src/fem}/geometry/deformed_geometry.py +14 -22
  28. warp/{fem → _src/fem}/geometry/element.py +32 -10
  29. warp/{fem → _src/fem}/geometry/geometry.py +48 -20
  30. warp/{fem → _src/fem}/geometry/grid_2d.py +12 -23
  31. warp/{fem → _src/fem}/geometry/grid_3d.py +12 -23
  32. warp/{fem → _src/fem}/geometry/hexmesh.py +40 -63
  33. warp/{fem → _src/fem}/geometry/nanogrid.py +255 -248
  34. warp/{fem → _src/fem}/geometry/partition.py +121 -63
  35. warp/{fem → _src/fem}/geometry/quadmesh.py +26 -45
  36. warp/{fem → _src/fem}/geometry/tetmesh.py +40 -63
  37. warp/{fem → _src/fem}/geometry/trimesh.py +26 -45
  38. warp/{fem → _src/fem}/integrate.py +164 -158
  39. warp/_src/fem/linalg.py +383 -0
  40. warp/_src/fem/operator.py +396 -0
  41. warp/_src/fem/polynomial.py +229 -0
  42. warp/{fem → _src/fem}/quadrature/pic_quadrature.py +15 -20
  43. warp/{fem → _src/fem}/quadrature/quadrature.py +95 -47
  44. warp/_src/fem/space/__init__.py +248 -0
  45. warp/{fem → _src/fem}/space/basis_function_space.py +20 -11
  46. warp/_src/fem/space/basis_space.py +679 -0
  47. warp/{fem → _src/fem}/space/dof_mapper.py +3 -3
  48. warp/{fem → _src/fem}/space/function_space.py +14 -13
  49. warp/{fem → _src/fem}/space/grid_2d_function_space.py +4 -7
  50. warp/{fem → _src/fem}/space/grid_3d_function_space.py +4 -4
  51. warp/{fem → _src/fem}/space/hexmesh_function_space.py +4 -10
  52. warp/{fem → _src/fem}/space/nanogrid_function_space.py +3 -9
  53. warp/{fem → _src/fem}/space/partition.py +117 -60
  54. warp/{fem → _src/fem}/space/quadmesh_function_space.py +4 -10
  55. warp/{fem → _src/fem}/space/restriction.py +66 -33
  56. warp/_src/fem/space/shape/__init__.py +152 -0
  57. warp/{fem → _src/fem}/space/shape/cube_shape_function.py +9 -9
  58. warp/{fem → _src/fem}/space/shape/shape_function.py +8 -9
  59. warp/{fem → _src/fem}/space/shape/square_shape_function.py +6 -6
  60. warp/{fem → _src/fem}/space/shape/tet_shape_function.py +3 -3
  61. warp/{fem → _src/fem}/space/shape/triangle_shape_function.py +3 -3
  62. warp/{fem → _src/fem}/space/tetmesh_function_space.py +3 -9
  63. warp/_src/fem/space/topology.py +459 -0
  64. warp/{fem → _src/fem}/space/trimesh_function_space.py +3 -9
  65. warp/_src/fem/types.py +112 -0
  66. warp/_src/fem/utils.py +486 -0
  67. warp/_src/jax.py +186 -0
  68. warp/_src/jax_experimental/__init__.py +14 -0
  69. warp/_src/jax_experimental/custom_call.py +387 -0
  70. warp/_src/jax_experimental/ffi.py +1284 -0
  71. warp/_src/jax_experimental/xla_ffi.py +656 -0
  72. warp/_src/marching_cubes.py +708 -0
  73. warp/_src/math.py +414 -0
  74. warp/_src/optim/__init__.py +14 -0
  75. warp/_src/optim/adam.py +163 -0
  76. warp/_src/optim/linear.py +1606 -0
  77. warp/_src/optim/sgd.py +112 -0
  78. warp/_src/paddle.py +406 -0
  79. warp/_src/render/__init__.py +14 -0
  80. warp/_src/render/imgui_manager.py +289 -0
  81. warp/_src/render/render_opengl.py +3636 -0
  82. warp/_src/render/render_usd.py +937 -0
  83. warp/_src/render/utils.py +160 -0
  84. warp/_src/sparse.py +2716 -0
  85. warp/_src/tape.py +1206 -0
  86. warp/{thirdparty → _src/thirdparty}/unittest_parallel.py +9 -2
  87. warp/_src/torch.py +391 -0
  88. warp/_src/types.py +5870 -0
  89. warp/_src/utils.py +1693 -0
  90. warp/autograd.py +12 -1054
  91. warp/bin/warp-clang.dll +0 -0
  92. warp/bin/warp.dll +0 -0
  93. warp/build.py +8 -588
  94. warp/build_dll.py +6 -471
  95. warp/codegen.py +6 -4246
  96. warp/constants.py +6 -39
  97. warp/context.py +12 -7851
  98. warp/dlpack.py +6 -444
  99. warp/examples/distributed/example_jacobi_mpi.py +4 -5
  100. warp/examples/fem/example_adaptive_grid.py +1 -1
  101. warp/examples/fem/example_apic_fluid.py +1 -1
  102. warp/examples/fem/example_burgers.py +8 -8
  103. warp/examples/fem/example_diffusion.py +1 -1
  104. warp/examples/fem/example_distortion_energy.py +1 -1
  105. warp/examples/fem/example_mixed_elasticity.py +2 -2
  106. warp/examples/fem/example_navier_stokes.py +1 -1
  107. warp/examples/fem/example_nonconforming_contact.py +7 -7
  108. warp/examples/fem/example_stokes.py +1 -1
  109. warp/examples/fem/example_stokes_transfer.py +1 -1
  110. warp/examples/fem/utils.py +2 -2
  111. warp/examples/interop/example_jax_callable.py +1 -1
  112. warp/examples/interop/example_jax_ffi_callback.py +1 -1
  113. warp/examples/interop/example_jax_kernel.py +3 -2
  114. warp/examples/tile/example_tile_mcgp.py +191 -0
  115. warp/fabric.py +6 -337
  116. warp/fem/__init__.py +159 -97
  117. warp/fem/adaptivity.py +7 -489
  118. warp/fem/cache.py +9 -648
  119. warp/fem/dirichlet.py +6 -184
  120. warp/fem/field/__init__.py +8 -109
  121. warp/fem/field/field.py +7 -652
  122. warp/fem/geometry/__init__.py +7 -18
  123. warp/fem/geometry/closest_point.py +11 -77
  124. warp/fem/linalg.py +18 -366
  125. warp/fem/operator.py +11 -369
  126. warp/fem/polynomial.py +9 -209
  127. warp/fem/space/__init__.py +5 -211
  128. warp/fem/space/basis_space.py +6 -662
  129. warp/fem/space/shape/__init__.py +41 -118
  130. warp/fem/space/topology.py +6 -437
  131. warp/fem/types.py +6 -81
  132. warp/fem/utils.py +11 -444
  133. warp/jax.py +8 -165
  134. warp/jax_experimental/__init__.py +14 -1
  135. warp/jax_experimental/custom_call.py +8 -342
  136. warp/jax_experimental/ffi.py +17 -853
  137. warp/jax_experimental/xla_ffi.py +5 -596
  138. warp/marching_cubes.py +5 -689
  139. warp/math.py +16 -393
  140. warp/native/array.h +385 -37
  141. warp/native/builtin.h +316 -39
  142. warp/native/bvh.cpp +43 -9
  143. warp/native/bvh.cu +62 -27
  144. warp/native/bvh.h +310 -309
  145. warp/native/clang/clang.cpp +102 -97
  146. warp/native/coloring.cpp +0 -1
  147. warp/native/crt.h +208 -0
  148. warp/native/exports.h +156 -0
  149. warp/native/hashgrid.cu +2 -0
  150. warp/native/intersect.h +24 -1
  151. warp/native/intersect_tri.h +44 -35
  152. warp/native/mat.h +1456 -276
  153. warp/native/mesh.cpp +4 -4
  154. warp/native/mesh.cu +4 -2
  155. warp/native/mesh.h +176 -61
  156. warp/native/quat.h +0 -52
  157. warp/native/scan.cu +2 -0
  158. warp/native/sort.cu +22 -13
  159. warp/native/sort.h +2 -0
  160. warp/native/sparse.cu +7 -3
  161. warp/native/spatial.h +12 -0
  162. warp/native/tile.h +837 -70
  163. warp/native/tile_radix_sort.h +1 -1
  164. warp/native/tile_reduce.h +394 -46
  165. warp/native/tile_scan.h +4 -4
  166. warp/native/vec.h +469 -53
  167. warp/native/version.h +23 -0
  168. warp/native/volume.cpp +1 -1
  169. warp/native/volume.cu +1 -0
  170. warp/native/volume.h +1 -1
  171. warp/native/volume_builder.cu +2 -0
  172. warp/native/warp.cpp +60 -32
  173. warp/native/warp.cu +313 -201
  174. warp/native/warp.h +14 -11
  175. warp/optim/__init__.py +6 -3
  176. warp/optim/adam.py +6 -145
  177. warp/optim/linear.py +14 -1585
  178. warp/optim/sgd.py +6 -94
  179. warp/paddle.py +6 -388
  180. warp/render/__init__.py +8 -4
  181. warp/render/imgui_manager.py +7 -267
  182. warp/render/render_opengl.py +6 -3616
  183. warp/render/render_usd.py +6 -918
  184. warp/render/utils.py +6 -142
  185. warp/sparse.py +37 -2563
  186. warp/tape.py +6 -1188
  187. warp/tests/__main__.py +1 -1
  188. warp/tests/cuda/test_async.py +4 -4
  189. warp/tests/cuda/test_conditional_captures.py +1 -1
  190. warp/tests/cuda/test_multigpu.py +1 -1
  191. warp/tests/cuda/test_streams.py +58 -1
  192. warp/tests/geometry/test_bvh.py +157 -22
  193. warp/tests/geometry/test_hash_grid.py +38 -0
  194. warp/tests/geometry/test_marching_cubes.py +0 -1
  195. warp/tests/geometry/test_mesh.py +5 -3
  196. warp/tests/geometry/test_mesh_query_aabb.py +5 -12
  197. warp/tests/geometry/test_mesh_query_point.py +5 -2
  198. warp/tests/geometry/test_mesh_query_ray.py +15 -3
  199. warp/tests/geometry/test_volume_write.py +5 -5
  200. warp/tests/interop/test_dlpack.py +14 -14
  201. warp/tests/interop/test_jax.py +1382 -79
  202. warp/tests/interop/test_paddle.py +1 -1
  203. warp/tests/test_adam.py +0 -1
  204. warp/tests/test_arithmetic.py +9 -9
  205. warp/tests/test_array.py +529 -100
  206. warp/tests/test_array_reduce.py +3 -3
  207. warp/tests/test_atomic.py +12 -8
  208. warp/tests/test_atomic_bitwise.py +209 -0
  209. warp/tests/test_atomic_cas.py +4 -4
  210. warp/tests/test_bool.py +2 -2
  211. warp/tests/test_builtins_resolution.py +5 -571
  212. warp/tests/test_codegen.py +34 -15
  213. warp/tests/test_conditional.py +1 -1
  214. warp/tests/test_context.py +6 -6
  215. warp/tests/test_copy.py +242 -161
  216. warp/tests/test_ctypes.py +3 -3
  217. warp/tests/test_devices.py +24 -2
  218. warp/tests/test_examples.py +16 -84
  219. warp/tests/test_fabricarray.py +35 -35
  220. warp/tests/test_fast_math.py +0 -2
  221. warp/tests/test_fem.py +60 -14
  222. warp/tests/test_fixedarray.py +3 -3
  223. warp/tests/test_func.py +8 -5
  224. warp/tests/test_generics.py +1 -1
  225. warp/tests/test_indexedarray.py +24 -24
  226. warp/tests/test_intersect.py +39 -9
  227. warp/tests/test_large.py +1 -1
  228. warp/tests/test_lerp.py +3 -1
  229. warp/tests/test_linear_solvers.py +1 -1
  230. warp/tests/test_map.py +49 -4
  231. warp/tests/test_mat.py +52 -62
  232. warp/tests/test_mat_constructors.py +4 -5
  233. warp/tests/test_mat_lite.py +1 -1
  234. warp/tests/test_mat_scalar_ops.py +121 -121
  235. warp/tests/test_math.py +34 -0
  236. warp/tests/test_module_aot.py +4 -4
  237. warp/tests/test_modules_lite.py +28 -2
  238. warp/tests/test_print.py +11 -11
  239. warp/tests/test_quat.py +93 -58
  240. warp/tests/test_runlength_encode.py +1 -1
  241. warp/tests/test_scalar_ops.py +38 -10
  242. warp/tests/test_smoothstep.py +1 -1
  243. warp/tests/test_sparse.py +126 -15
  244. warp/tests/test_spatial.py +105 -87
  245. warp/tests/test_special_values.py +6 -6
  246. warp/tests/test_static.py +7 -7
  247. warp/tests/test_struct.py +13 -2
  248. warp/tests/test_triangle_closest_point.py +48 -1
  249. warp/tests/test_tuple.py +96 -0
  250. warp/tests/test_types.py +82 -9
  251. warp/tests/test_utils.py +52 -52
  252. warp/tests/test_vec.py +29 -29
  253. warp/tests/test_vec_constructors.py +5 -5
  254. warp/tests/test_vec_scalar_ops.py +97 -97
  255. warp/tests/test_version.py +75 -0
  256. warp/tests/tile/test_tile.py +239 -0
  257. warp/tests/tile/test_tile_atomic_bitwise.py +403 -0
  258. warp/tests/tile/test_tile_cholesky.py +7 -4
  259. warp/tests/tile/test_tile_load.py +26 -2
  260. warp/tests/tile/test_tile_mathdx.py +3 -3
  261. warp/tests/tile/test_tile_matmul.py +1 -1
  262. warp/tests/tile/test_tile_mlp.py +2 -4
  263. warp/tests/tile/test_tile_reduce.py +214 -13
  264. warp/tests/unittest_suites.py +6 -14
  265. warp/tests/unittest_utils.py +10 -9
  266. warp/tests/walkthrough_debug.py +3 -1
  267. warp/torch.py +6 -373
  268. warp/types.py +29 -5750
  269. warp/utils.py +10 -1659
  270. {warp_lang-1.9.0.dist-info → warp_lang-1.10.0rc2.dist-info}/METADATA +47 -103
  271. warp_lang-1.10.0rc2.dist-info/RECORD +468 -0
  272. warp_lang-1.10.0rc2.dist-info/licenses/licenses/Gaia-LICENSE.txt +6 -0
  273. warp_lang-1.10.0rc2.dist-info/licenses/licenses/appdirs-LICENSE.txt +22 -0
  274. warp_lang-1.10.0rc2.dist-info/licenses/licenses/asset_pixel_jpg-LICENSE.txt +3 -0
  275. warp_lang-1.10.0rc2.dist-info/licenses/licenses/cuda-LICENSE.txt +1582 -0
  276. warp_lang-1.10.0rc2.dist-info/licenses/licenses/dlpack-LICENSE.txt +201 -0
  277. warp_lang-1.10.0rc2.dist-info/licenses/licenses/fp16-LICENSE.txt +28 -0
  278. warp_lang-1.10.0rc2.dist-info/licenses/licenses/libmathdx-LICENSE.txt +220 -0
  279. warp_lang-1.10.0rc2.dist-info/licenses/licenses/llvm-LICENSE.txt +279 -0
  280. warp_lang-1.10.0rc2.dist-info/licenses/licenses/moller-LICENSE.txt +16 -0
  281. warp_lang-1.10.0rc2.dist-info/licenses/licenses/nanovdb-LICENSE.txt +2 -0
  282. warp_lang-1.10.0rc2.dist-info/licenses/licenses/nvrtc-LICENSE.txt +1592 -0
  283. warp_lang-1.10.0rc2.dist-info/licenses/licenses/svd-LICENSE.txt +23 -0
  284. warp_lang-1.10.0rc2.dist-info/licenses/licenses/unittest_parallel-LICENSE.txt +21 -0
  285. warp_lang-1.10.0rc2.dist-info/licenses/licenses/usd-LICENSE.txt +213 -0
  286. warp_lang-1.10.0rc2.dist-info/licenses/licenses/windingnumber-LICENSE.txt +21 -0
  287. warp/examples/assets/cartpole.urdf +0 -110
  288. warp/examples/assets/crazyflie.usd +0 -0
  289. warp/examples/assets/nv_ant.xml +0 -92
  290. warp/examples/assets/nv_humanoid.xml +0 -183
  291. warp/examples/assets/quadruped.urdf +0 -268
  292. warp/examples/optim/example_bounce.py +0 -266
  293. warp/examples/optim/example_cloth_throw.py +0 -228
  294. warp/examples/optim/example_drone.py +0 -870
  295. warp/examples/optim/example_inverse_kinematics.py +0 -182
  296. warp/examples/optim/example_inverse_kinematics_torch.py +0 -191
  297. warp/examples/optim/example_softbody_properties.py +0 -400
  298. warp/examples/optim/example_spring_cage.py +0 -245
  299. warp/examples/optim/example_trajectory.py +0 -227
  300. warp/examples/sim/example_cartpole.py +0 -143
  301. warp/examples/sim/example_cloth.py +0 -225
  302. warp/examples/sim/example_cloth_self_contact.py +0 -316
  303. warp/examples/sim/example_granular.py +0 -130
  304. warp/examples/sim/example_granular_collision_sdf.py +0 -202
  305. warp/examples/sim/example_jacobian_ik.py +0 -244
  306. warp/examples/sim/example_particle_chain.py +0 -124
  307. warp/examples/sim/example_quadruped.py +0 -203
  308. warp/examples/sim/example_rigid_chain.py +0 -203
  309. warp/examples/sim/example_rigid_contact.py +0 -195
  310. warp/examples/sim/example_rigid_force.py +0 -133
  311. warp/examples/sim/example_rigid_gyroscopic.py +0 -115
  312. warp/examples/sim/example_rigid_soft_contact.py +0 -140
  313. warp/examples/sim/example_soft_body.py +0 -196
  314. warp/examples/tile/example_tile_walker.py +0 -327
  315. warp/sim/__init__.py +0 -74
  316. warp/sim/articulation.py +0 -793
  317. warp/sim/collide.py +0 -2570
  318. warp/sim/graph_coloring.py +0 -307
  319. warp/sim/import_mjcf.py +0 -791
  320. warp/sim/import_snu.py +0 -227
  321. warp/sim/import_urdf.py +0 -579
  322. warp/sim/import_usd.py +0 -898
  323. warp/sim/inertia.py +0 -357
  324. warp/sim/integrator.py +0 -245
  325. warp/sim/integrator_euler.py +0 -2000
  326. warp/sim/integrator_featherstone.py +0 -2101
  327. warp/sim/integrator_vbd.py +0 -2487
  328. warp/sim/integrator_xpbd.py +0 -3295
  329. warp/sim/model.py +0 -4821
  330. warp/sim/particles.py +0 -121
  331. warp/sim/render.py +0 -431
  332. warp/sim/utils.py +0 -431
  333. warp/tests/sim/disabled_kinematics.py +0 -244
  334. warp/tests/sim/test_cloth.py +0 -863
  335. warp/tests/sim/test_collision.py +0 -743
  336. warp/tests/sim/test_coloring.py +0 -347
  337. warp/tests/sim/test_inertia.py +0 -161
  338. warp/tests/sim/test_model.py +0 -226
  339. warp/tests/sim/test_sim_grad.py +0 -287
  340. warp/tests/sim/test_sim_grad_bounce_linear.py +0 -212
  341. warp/tests/sim/test_sim_kinematics.py +0 -98
  342. warp/thirdparty/__init__.py +0 -0
  343. warp_lang-1.9.0.dist-info/RECORD +0 -456
  344. /warp/{fem → _src/fem}/quadrature/__init__.py +0 -0
  345. /warp/{tests/sim → _src/thirdparty}/__init__.py +0 -0
  346. /warp/{thirdparty → _src/thirdparty}/appdirs.py +0 -0
  347. /warp/{thirdparty → _src/thirdparty}/dlpack.py +0 -0
  348. {warp_lang-1.9.0.dist-info → warp_lang-1.10.0rc2.dist-info}/WHEEL +0 -0
  349. {warp_lang-1.9.0.dist-info → warp_lang-1.10.0rc2.dist-info}/licenses/LICENSE.md +0 -0
  350. {warp_lang-1.9.0.dist-info → warp_lang-1.10.0rc2.dist-info}/top_level.txt +0 -0
@@ -1,2487 +0,0 @@
1
- # SPDX-FileCopyrightText: Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2
- # SPDX-License-Identifier: Apache-2.0
3
- #
4
- # Licensed under the Apache License, Version 2.0 (the "License");
5
- # you may not use this file except in compliance with the License.
6
- # You may obtain a copy of the License at
7
- #
8
- # http://www.apache.org/licenses/LICENSE-2.0
9
- #
10
- # Unless required by applicable law or agreed to in writing, software
11
- # distributed under the License is distributed on an "AS IS" BASIS,
12
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
- # See the License for the specific language governing permissions and
14
- # limitations under the License.
15
- import numpy as np
16
-
17
- import warp as wp
18
-
19
- from ..types import float32, matrix
20
- from .collide import (
21
- TriMeshCollisionDetector,
22
- TriMeshCollisionInfo,
23
- triangle_closest_point,
24
- )
25
- from .integrator import Integrator
26
- from .model import PARTICLE_FLAG_ACTIVE, Control, Model, ModelShapeMaterials, State
27
-
28
- VBD_DEBUG_PRINTING_OPTIONS = {
29
- # "elasticity_force_hessian",
30
- # "contact_force_hessian",
31
- # "contact_force_hessian_vt",
32
- # "contact_force_hessian_ee",
33
- # "overall_force_hessian",
34
- # "inertia_force_hessian",
35
- # "connectivity",
36
- # "contact_info",
37
- }
38
-
39
-
40
- class mat66(matrix(shape=(6, 6), dtype=float32)):
41
- pass
42
-
43
-
44
- class mat32(matrix(shape=(3, 2), dtype=float32)):
45
- pass
46
-
47
-
48
- class mat43(matrix(shape=(4, 3), dtype=float32)):
49
- pass
50
-
51
-
52
- @wp.struct
53
- class ForceElementAdjacencyInfo:
54
- r"""
55
- - vertex_adjacent_[element]: the flatten adjacency information. Its size is \sum_{i\inV} 2*N_i, where N_i is the
56
- number of vertex i's adjacent [element]. For each adjacent element it stores 2 information:
57
- - the id of the adjacent element
58
- - the order of the vertex in the element, which is essential to compute the force and hessian for the vertex
59
- - vertex_adjacent_[element]_offsets: stores where each vertex information starts in the flatten adjacency array.
60
- Its size is |V|+1 such that the number of vertex i's adjacent [element] can be computed as
61
- vertex_adjacent_[element]_offsets[i+1]-vertex_adjacent_[element]_offsets[i].
62
- """
63
-
64
- v_adj_faces: wp.array(dtype=int)
65
- v_adj_faces_offsets: wp.array(dtype=int)
66
-
67
- v_adj_edges: wp.array(dtype=int)
68
- v_adj_edges_offsets: wp.array(dtype=int)
69
-
70
- def to(self, device):
71
- if device.is_cpu:
72
- return self
73
- else:
74
- adjacency_gpu = ForceElementAdjacencyInfo()
75
- adjacency_gpu.v_adj_faces = self.v_adj_faces.to(device)
76
- adjacency_gpu.v_adj_faces_offsets = self.v_adj_faces_offsets.to(device)
77
-
78
- adjacency_gpu.v_adj_edges = self.v_adj_edges.to(device)
79
- adjacency_gpu.v_adj_edges_offsets = self.v_adj_edges_offsets.to(device)
80
-
81
- return adjacency_gpu
82
-
83
-
84
- @wp.func
85
- def get_vertex_num_adjacent_edges(adjacency: ForceElementAdjacencyInfo, vertex: wp.int32):
86
- return (adjacency.v_adj_edges_offsets[vertex + 1] - adjacency.v_adj_edges_offsets[vertex]) >> 1
87
-
88
-
89
- @wp.func
90
- def get_vertex_adjacent_edge_id_order(adjacency: ForceElementAdjacencyInfo, vertex: wp.int32, edge: wp.int32):
91
- offset = adjacency.v_adj_edges_offsets[vertex]
92
- return adjacency.v_adj_edges[offset + edge * 2], adjacency.v_adj_edges[offset + edge * 2 + 1]
93
-
94
-
95
- @wp.func
96
- def get_vertex_num_adjacent_faces(adjacency: ForceElementAdjacencyInfo, vertex: wp.int32):
97
- return (adjacency.v_adj_faces_offsets[vertex + 1] - adjacency.v_adj_faces_offsets[vertex]) >> 1
98
-
99
-
100
- @wp.func
101
- def get_vertex_adjacent_face_id_order(adjacency: ForceElementAdjacencyInfo, vertex: wp.int32, face: wp.int32):
102
- offset = adjacency.v_adj_faces_offsets[vertex]
103
- return adjacency.v_adj_faces[offset + face * 2], adjacency.v_adj_faces[offset + face * 2 + 1]
104
-
105
-
106
- @wp.kernel
107
- def _test_compute_force_element_adjacency(
108
- adjacency: ForceElementAdjacencyInfo,
109
- edge_indices: wp.array(dtype=wp.int32, ndim=2),
110
- face_indices: wp.array(dtype=wp.int32, ndim=2),
111
- ):
112
- wp.printf("num vertices: %d\n", adjacency.v_adj_edges_offsets.shape[0] - 1)
113
- for vertex in range(adjacency.v_adj_edges_offsets.shape[0] - 1):
114
- num_adj_edges = get_vertex_num_adjacent_edges(adjacency, vertex)
115
- for i_bd in range(num_adj_edges):
116
- bd_id, v_order = get_vertex_adjacent_edge_id_order(adjacency, vertex, i_bd)
117
-
118
- if edge_indices[bd_id, v_order] != vertex:
119
- print("Error!!!")
120
- wp.printf("vertex: %d | num_adj_edges: %d\n", vertex, num_adj_edges)
121
- wp.printf("--iBd: %d | ", i_bd)
122
- wp.printf("edge id: %d | v_order: %d\n", bd_id, v_order)
123
-
124
- num_adj_faces = get_vertex_num_adjacent_faces(adjacency, vertex)
125
-
126
- for i_face in range(num_adj_faces):
127
- face, v_order = get_vertex_adjacent_face_id_order(
128
- adjacency,
129
- vertex,
130
- i_face,
131
- )
132
-
133
- if face_indices[face, v_order] != vertex:
134
- print("Error!!!")
135
- wp.printf("vertex: %d | num_adj_faces: %d\n", vertex, num_adj_faces)
136
- wp.printf("--i_face: %d | face id: %d | v_order: %d\n", i_face, face, v_order)
137
- wp.printf(
138
- "--face: %d %d %d\n",
139
- face_indices[face, 0],
140
- face_indices[face, 1],
141
- face_indices[face, 2],
142
- )
143
-
144
-
145
- @wp.func
146
- def build_orthonormal_basis(n: wp.vec3):
147
- """
148
- Builds an orthonormal basis given a normal vector `n`. Return the two axes that is perpendicular to `n`.
149
-
150
- :param n: A 3D vector (list or array-like) representing the normal vector
151
- """
152
- b1 = wp.vec3()
153
- b2 = wp.vec3()
154
- if n[2] < 0.0:
155
- a = 1.0 / (1.0 - n[2])
156
- b = n[0] * n[1] * a
157
- b1[0] = 1.0 - n[0] * n[0] * a
158
- b1[1] = -b
159
- b1[2] = n[0]
160
-
161
- b2[0] = b
162
- b2[1] = n[1] * n[1] * a - 1.0
163
- b2[2] = -n[1]
164
- else:
165
- a = 1.0 / (1.0 + n[2])
166
- b = -n[0] * n[1] * a
167
- b1[0] = 1.0 - n[0] * n[0] * a
168
- b1[1] = b
169
- b1[2] = -n[0]
170
-
171
- b2[0] = b
172
- b2[1] = 1.0 - n[1] * n[1] * a
173
- b2[2] = -n[1]
174
-
175
- return b1, b2
176
-
177
-
178
- @wp.func
179
- def calculate_triangle_deformation_gradient(
180
- face: int, tri_indices: wp.array(dtype=wp.int32, ndim=2), pos: wp.array(dtype=wp.vec3), tri_pose: wp.mat22
181
- ):
182
- F = mat32()
183
- v1 = pos[tri_indices[face, 1]] - pos[tri_indices[face, 0]]
184
- v2 = pos[tri_indices[face, 2]] - pos[tri_indices[face, 0]]
185
-
186
- F[0, 0] = v1[0]
187
- F[1, 0] = v1[1]
188
- F[2, 0] = v1[2]
189
- F[0, 1] = v2[0]
190
- F[1, 1] = v2[1]
191
- F[2, 1] = v2[2]
192
-
193
- F = F * tri_pose
194
- return F
195
-
196
-
197
- @wp.func
198
- def green_strain(F: mat32):
199
- return 0.5 * (wp.transpose(F) * F - wp.identity(n=2, dtype=float))
200
-
201
-
202
- @wp.func
203
- def assemble_membrane_hessian(h: mat66, m1: float, m2: float):
204
- h_vert = wp.mat33(
205
- m1 * (h[0, 0] * m1 + h[3, 0] * m2) + m2 * (h[0, 3] * m1 + h[3, 3] * m2),
206
- m1 * (h[0, 1] * m1 + h[3, 1] * m2) + m2 * (h[0, 4] * m1 + h[3, 4] * m2),
207
- m1 * (h[0, 2] * m1 + h[3, 2] * m2) + m2 * (h[0, 5] * m1 + h[3, 5] * m2),
208
- m1 * (h[1, 0] * m1 + h[4, 0] * m2) + m2 * (h[1, 3] * m1 + h[4, 3] * m2),
209
- m1 * (h[1, 1] * m1 + h[4, 1] * m2) + m2 * (h[1, 4] * m1 + h[4, 4] * m2),
210
- m1 * (h[1, 2] * m1 + h[4, 2] * m2) + m2 * (h[1, 5] * m1 + h[4, 5] * m2),
211
- m1 * (h[2, 0] * m1 + h[5, 0] * m2) + m2 * (h[2, 3] * m1 + h[5, 3] * m2),
212
- m1 * (h[2, 1] * m1 + h[5, 1] * m2) + m2 * (h[2, 4] * m1 + h[5, 4] * m2),
213
- m1 * (h[2, 2] * m1 + h[5, 2] * m2) + m2 * (h[2, 5] * m1 + h[5, 5] * m2),
214
- )
215
-
216
- return h_vert
217
-
218
-
219
- @wp.func
220
- def evaluate_stvk_force_hessian(
221
- face: int,
222
- v_order: int,
223
- pos: wp.array(dtype=wp.vec3),
224
- tri_indices: wp.array(dtype=wp.int32, ndim=2),
225
- tri_pose: wp.mat22,
226
- area: float,
227
- mu: float,
228
- lmbd: float,
229
- damping: float,
230
- ):
231
- D2W_DFDF = mat66()
232
- F = calculate_triangle_deformation_gradient(face, tri_indices, pos, tri_pose)
233
- G = green_strain(F)
234
-
235
- S = 2.0 * mu * G + lmbd * (G[0, 0] + G[1, 1]) * wp.identity(n=2, dtype=float)
236
-
237
- F12 = -area * F * S * wp.transpose(tri_pose)
238
-
239
- Dm_inv1_1 = tri_pose[0, 0]
240
- Dm_inv2_1 = tri_pose[1, 0]
241
- Dm_inv1_2 = tri_pose[0, 1]
242
- Dm_inv2_2 = tri_pose[1, 1]
243
-
244
- F1_1 = F[0, 0]
245
- F2_1 = F[1, 0]
246
- F3_1 = F[2, 0]
247
- F1_2 = F[0, 1]
248
- F2_2 = F[1, 1]
249
- F3_2 = F[2, 1]
250
-
251
- F1_1_sqr = F1_1 * F1_1
252
- F2_1_sqr = F2_1 * F2_1
253
- F3_1_sqr = F3_1 * F3_1
254
- F1_2_sqr = F1_2 * F1_2
255
- F2_2_sqr = F2_2 * F2_2
256
- F3_2_sqr = F3_2 * F3_2
257
-
258
- e_uu = G[0, 0]
259
- e_vv = G[1, 1]
260
- e_uv = G[0, 1]
261
- e_uuvvSum = e_uu + e_vv
262
-
263
- D2W_DFDF[0, 0] = F1_1 * (F1_1 * lmbd + 2.0 * F1_1 * mu) + 2.0 * mu * e_uu + lmbd * (e_uuvvSum) + F1_2_sqr * mu
264
-
265
- D2W_DFDF[1, 0] = F1_1 * (F2_1 * lmbd + 2.0 * F2_1 * mu) + F1_2 * F2_2 * mu
266
- D2W_DFDF[0, 1] = D2W_DFDF[1, 0]
267
-
268
- D2W_DFDF[2, 0] = F1_1 * (F3_1 * lmbd + 2.0 * F3_1 * mu) + F1_2 * F3_2 * mu
269
- D2W_DFDF[0, 2] = D2W_DFDF[2, 0]
270
-
271
- D2W_DFDF[3, 0] = 2.0 * mu * e_uv + F1_1 * F1_2 * lmbd + F1_1 * F1_2 * mu
272
- D2W_DFDF[0, 3] = D2W_DFDF[3, 0]
273
-
274
- D2W_DFDF[4, 0] = F1_1 * F2_2 * lmbd + F1_2 * F2_1 * mu
275
- D2W_DFDF[0, 4] = D2W_DFDF[4, 0]
276
-
277
- D2W_DFDF[5, 0] = F1_1 * F3_2 * lmbd + F1_2 * F3_1 * mu
278
- D2W_DFDF[0, 5] = D2W_DFDF[5, 0]
279
-
280
- D2W_DFDF[1, 1] = F2_1 * (F2_1 * lmbd + 2.0 * F2_1 * mu) + 2.0 * mu * e_uu + lmbd * (e_uuvvSum) + F2_2_sqr * mu
281
-
282
- D2W_DFDF[2, 1] = F2_1 * (F3_1 * lmbd + 2.0 * F3_1 * mu) + F2_2 * F3_2 * mu
283
- D2W_DFDF[1, 2] = D2W_DFDF[2, 1]
284
-
285
- D2W_DFDF[3, 1] = F1_2 * F2_1 * lmbd + F1_1 * F2_2 * mu
286
- D2W_DFDF[1, 3] = D2W_DFDF[3, 1]
287
-
288
- D2W_DFDF[4, 1] = 2.0 * mu * e_uv + F2_1 * F2_2 * lmbd + F2_1 * F2_2 * mu
289
- D2W_DFDF[1, 4] = D2W_DFDF[4, 1]
290
-
291
- D2W_DFDF[5, 1] = F2_1 * F3_2 * lmbd + F2_2 * F3_1 * mu
292
- D2W_DFDF[1, 5] = D2W_DFDF[5, 1]
293
-
294
- D2W_DFDF[2, 2] = F3_1 * (F3_1 * lmbd + 2.0 * F3_1 * mu) + 2.0 * mu * e_uu + lmbd * (e_uuvvSum) + F3_2_sqr * mu
295
-
296
- D2W_DFDF[3, 2] = F1_2 * F3_1 * lmbd + F1_1 * F3_2 * mu
297
- D2W_DFDF[2, 3] = D2W_DFDF[3, 2]
298
-
299
- D2W_DFDF[4, 2] = F2_2 * F3_1 * lmbd + F2_1 * F3_2 * mu
300
- D2W_DFDF[2, 4] = D2W_DFDF[4, 2]
301
-
302
- D2W_DFDF[5, 2] = 2.0 * mu * e_uv + F3_1 * F3_2 * lmbd + F3_1 * F3_2 * mu
303
- D2W_DFDF[2, 5] = D2W_DFDF[5, 2]
304
-
305
- D2W_DFDF[3, 3] = F1_2 * (F1_2 * lmbd + 2.0 * F1_2 * mu) + 2.0 * mu * e_vv + lmbd * (e_uuvvSum) + F1_1_sqr * mu
306
-
307
- D2W_DFDF[4, 3] = F1_2 * (F2_2 * lmbd + 2.0 * F2_2 * mu) + F1_1 * F2_1 * mu
308
- D2W_DFDF[3, 4] = D2W_DFDF[4, 3]
309
-
310
- D2W_DFDF[5, 3] = F1_2 * (F3_2 * lmbd + 2.0 * F3_2 * mu) + F1_1 * F3_1 * mu
311
- D2W_DFDF[3, 5] = D2W_DFDF[5, 3]
312
-
313
- D2W_DFDF[4, 4] = F2_2 * (F2_2 * lmbd + 2.0 * F2_2 * mu) + 2.0 * mu * e_vv + lmbd * (e_uuvvSum) + F2_1_sqr * mu
314
-
315
- D2W_DFDF[5, 4] = F2_2 * (F3_2 * lmbd + 2.0 * F3_2 * mu) + F2_1 * F3_1 * mu
316
- D2W_DFDF[4, 5] = D2W_DFDF[5, 4]
317
-
318
- D2W_DFDF[5, 5] = F3_2 * (F3_2 * lmbd + 2.0 * F3_2 * mu) + 2.0 * mu * e_vv + lmbd * (e_uuvvSum) + F3_1_sqr * mu
319
-
320
- D2W_DFDF = D2W_DFDF * area
321
-
322
- # m1s = wp.vec3(-Dm_inv1_1 - Dm_inv2_1, Dm_inv1_1, Dm_inv2_1)
323
- # m2s = wp.vec3(-Dm_inv1_2 - Dm_inv2_2, Dm_inv1_2, Dm_inv2_2)
324
- #
325
- # m1 = m1s[v_order]
326
- # m2 = m2s[v_order]
327
-
328
- if v_order == 0:
329
- m1 = -Dm_inv1_1 - Dm_inv2_1
330
- m2 = -Dm_inv1_2 - Dm_inv2_2
331
- f = wp.vec3(-F12[0, 0] - F12[0, 1], -F12[1, 0] - F12[1, 1], -F12[2, 0] - F12[2, 1])
332
- elif v_order == 1:
333
- m1 = Dm_inv1_1
334
- m2 = Dm_inv1_2
335
- f = wp.vec3(F12[0, 0], F12[1, 0], F12[2, 0])
336
- else:
337
- m1 = Dm_inv2_1
338
- m2 = Dm_inv2_2
339
- f = wp.vec3(F12[0, 1], F12[1, 1], F12[2, 1])
340
-
341
- h = assemble_membrane_hessian(D2W_DFDF, m1, m2)
342
-
343
- return f, h
344
-
345
-
346
- @wp.func
347
- def mat_vec_cross_from_3_basis(e1: wp.vec3, e2: wp.vec3, e3: wp.vec3, a: wp.vec3):
348
- e1_cross_a = wp.cross(e1, a)
349
- e2_cross_a = wp.cross(e2, a)
350
- e3_cross_a = wp.cross(e3, a)
351
-
352
- return wp.mat33(
353
- e1_cross_a[0],
354
- e2_cross_a[0],
355
- e3_cross_a[0],
356
- e1_cross_a[1],
357
- e2_cross_a[1],
358
- e3_cross_a[1],
359
- e1_cross_a[2],
360
- e2_cross_a[2],
361
- e3_cross_a[2],
362
- )
363
-
364
-
365
- @wp.func
366
- def mat_vec_cross(mat: wp.mat33, a: wp.vec3):
367
- e1 = wp.vec3(mat[0, 0], mat[1, 0], mat[2, 0])
368
- e2 = wp.vec3(mat[0, 1], mat[1, 1], mat[2, 1])
369
- e3 = wp.vec3(mat[0, 2], mat[1, 2], mat[2, 2])
370
-
371
- return mat_vec_cross_from_3_basis(e1, e2, e3, a)
372
-
373
-
374
- @wp.func
375
- def evaluate_dihedral_angle_based_bending_force_hessian(
376
- bending_index: int,
377
- v_order: int,
378
- pos: wp.array(dtype=wp.vec3),
379
- pos_prev: wp.array(dtype=wp.vec3),
380
- edge_indices: wp.array(dtype=wp.int32, ndim=2),
381
- edge_rest_angle: wp.array(dtype=float),
382
- edge_rest_length: wp.array(dtype=float),
383
- stiffness: float,
384
- damping: float,
385
- dt: float,
386
- ):
387
- if edge_indices[bending_index, 0] == -1 or edge_indices[bending_index, 1] == -1:
388
- return wp.vec3(0.0), wp.mat33(0.0)
389
-
390
- x1 = pos[edge_indices[bending_index, 0]]
391
- x2 = pos[edge_indices[bending_index, 2]]
392
- x3 = pos[edge_indices[bending_index, 3]]
393
- x4 = pos[edge_indices[bending_index, 1]]
394
-
395
- e1 = wp.vec3(1.0, 0.0, 0.0)
396
- e2 = wp.vec3(0.0, 1.0, 0.0)
397
- e3 = wp.vec3(0.0, 0.0, 1.0)
398
-
399
- n1 = wp.cross((x2 - x1), (x3 - x1))
400
- n2 = wp.cross((x3 - x4), (x2 - x4))
401
- n1_norm = wp.length(n1)
402
- n2_norm = wp.length(n2)
403
-
404
- # degenerated bending edge
405
- if n1_norm < 1.0e-6 or n2_norm < 1.0e-6:
406
- return wp.vec3(0.0), wp.mat33(0.0)
407
-
408
- n1_n = n1 / n1_norm
409
- n2_n = n2 / n2_norm
410
-
411
- # avoid the infinite gradient of acos at -1 or 1
412
- cos_theta = wp.dot(n1_n, n2_n)
413
- if wp.abs(cos_theta) > 0.9999:
414
- cos_theta = 0.9999 * wp.sign(cos_theta)
415
-
416
- angle_sign = wp.sign(wp.dot(wp.cross(n2, n1), x3 - x2))
417
- theta = wp.acos(cos_theta) * angle_sign
418
- rest_angle = edge_rest_angle[bending_index]
419
-
420
- dE_dtheta = stiffness * (theta - rest_angle)
421
-
422
- d_theta_d_cos_theta = angle_sign * (-1.0 / wp.sqrt(1.0 - cos_theta * cos_theta))
423
- sin_theta = angle_sign * wp.sqrt(1.0 - cos_theta * cos_theta)
424
- one_over_sin_theta = 1.0 / sin_theta
425
- d_one_over_sin_theta_d_cos_theta = cos_theta / (sin_theta * sin_theta * sin_theta)
426
-
427
- e_rest_len = edge_rest_length[bending_index]
428
-
429
- if v_order == 0:
430
- d_cos_theta_dx1 = 1.0 / n1_norm * (-wp.cross(x3 - x1, n2_n) + wp.cross(x2 - x1, n2_n))
431
- d_one_over_sin_theta_dx1 = d_cos_theta_dx1 * d_one_over_sin_theta_d_cos_theta
432
-
433
- d_theta_dx1 = d_theta_d_cos_theta * d_cos_theta_dx1
434
- d2_theta_dx1_dx1 = -wp.outer(d_one_over_sin_theta_dx1, d_cos_theta_dx1)
435
-
436
- dE_dx1 = e_rest_len * dE_dtheta * d_theta_d_cos_theta * d_cos_theta_dx1
437
-
438
- d2_E_dx1_dx1 = (
439
- e_rest_len * stiffness * (wp.outer(d_theta_dx1, d_theta_dx1) + (theta - rest_angle) * d2_theta_dx1_dx1)
440
- )
441
-
442
- bending_force = -dE_dx1
443
- bending_hessian = d2_E_dx1_dx1
444
- elif v_order == 1:
445
- d_cos_theta_dx4 = 1.0 / n2_norm * (-wp.cross(x2 - x4, n1_n) + wp.cross(x3 - x4, n1_n))
446
- d_one_over_sin_theta_dx4 = d_cos_theta_dx4 * d_one_over_sin_theta_d_cos_theta
447
-
448
- d_theta_dx4 = d_theta_d_cos_theta * d_cos_theta_dx4
449
- d2_theta_dx4_dx4 = -wp.outer(d_one_over_sin_theta_dx4, d_cos_theta_dx4)
450
-
451
- dE_dx4 = e_rest_len * dE_dtheta * d_theta_d_cos_theta * d_cos_theta_dx4
452
- d2_E_dx4_dx4 = (
453
- e_rest_len * stiffness * (wp.outer(d_theta_dx4, d_theta_dx4) + (theta - rest_angle) * (d2_theta_dx4_dx4))
454
- )
455
-
456
- bending_force = -dE_dx4
457
- bending_hessian = d2_E_dx4_dx4
458
- elif v_order == 2:
459
- d_cos_theta_dx2 = 1.0 / n1_norm * wp.cross(x3 - x1, n2_n) - 1.0 / n2_norm * wp.cross(x3 - x4, n1_n)
460
- dn1_dx2 = mat_vec_cross_from_3_basis(e1, e2, e3, x3 - x1)
461
- dn2_dx2 = -mat_vec_cross_from_3_basis(e1, e2, e3, x3 - x4)
462
- d_one_over_sin_theta_dx2 = d_cos_theta_dx2 * d_one_over_sin_theta_d_cos_theta
463
- d2_cos_theta_dx2_dx2 = -mat_vec_cross(dn2_dx2, (x3 - x1)) / (n1_norm * n2_norm) + mat_vec_cross(
464
- dn1_dx2, x3 - x4
465
- ) / (n1_norm * n2_norm)
466
-
467
- d_theta_dx2 = d_theta_d_cos_theta * d_cos_theta_dx2
468
- d2_theta_dx2_dx2 = (
469
- -wp.outer(d_one_over_sin_theta_dx2, d_cos_theta_dx2) - one_over_sin_theta * d2_cos_theta_dx2_dx2
470
- )
471
-
472
- dE_dx2 = e_rest_len * dE_dtheta * d_theta_d_cos_theta * d_cos_theta_dx2
473
- d2_E_dx2_dx2 = (
474
- e_rest_len * stiffness * (wp.outer(d_theta_dx2, d_theta_dx2) + (theta - rest_angle) * d2_theta_dx2_dx2)
475
- )
476
-
477
- bending_force = -dE_dx2
478
- bending_hessian = d2_E_dx2_dx2
479
- else:
480
- d_cos_theta_dx3 = -1.0 / n1_norm * wp.cross(x2 - x1, n2_n) + 1.0 / n2_norm * wp.cross(x2 - x4, n1_n)
481
- dn1_dx3 = -mat_vec_cross_from_3_basis(e1, e2, e3, x2 - x1)
482
- dn2_dx3 = mat_vec_cross_from_3_basis(e1, e2, e3, x2 - x4)
483
- d_one_over_sin_theta_dx3 = d_cos_theta_dx3 * d_one_over_sin_theta_d_cos_theta
484
- d2_cos_theta_dx3_dx3 = mat_vec_cross(dn2_dx3, (x2 - x1)) / (n1_norm * n2_norm) - mat_vec_cross(
485
- dn1_dx3, x2 - x4
486
- ) / (n1_norm * n2_norm)
487
-
488
- d_theta_dx3 = d_theta_d_cos_theta * d_cos_theta_dx3
489
- d2_theta_dx3_dx3 = (
490
- -wp.outer(d_one_over_sin_theta_dx3, d_cos_theta_dx3) - one_over_sin_theta * d2_cos_theta_dx3_dx3
491
- )
492
-
493
- dE_dx3 = e_rest_len * dE_dtheta * d_theta_d_cos_theta * d_cos_theta_dx3
494
-
495
- d2_E_dx3_dx3 = (
496
- e_rest_len * stiffness * (wp.outer(d_theta_dx3, d_theta_dx3) + (theta - rest_angle) * d2_theta_dx3_dx3)
497
- )
498
-
499
- bending_force = -dE_dx3
500
- bending_hessian = d2_E_dx3_dx3
501
-
502
- displacement = pos_prev[edge_indices[bending_index, v_order]] - pos[edge_indices[bending_index, v_order]]
503
- h_d = bending_hessian * (damping / dt)
504
- f_d = h_d * displacement
505
-
506
- bending_force = bending_force + f_d
507
- bending_hessian = bending_hessian + h_d
508
-
509
- return bending_force, bending_hessian
510
-
511
-
512
- @wp.func
513
- def evaluate_ground_contact_force_hessian(
514
- particle_pos: wp.vec3,
515
- particle_prev_pos: wp.vec3,
516
- particle_radius: float,
517
- ground_normal: wp.vec3,
518
- ground_level: float,
519
- soft_contact_ke: float,
520
- soft_contact_kd: float,
521
- friction_mu: float,
522
- friction_epsilon: float,
523
- dt: float,
524
- ):
525
- penetration_depth = -(wp.dot(ground_normal, particle_pos) + ground_level - particle_radius)
526
-
527
- if penetration_depth > 0:
528
- ground_contact_force_norm = penetration_depth * soft_contact_ke
529
- ground_contact_force = ground_normal * ground_contact_force_norm
530
- ground_contact_hessian = soft_contact_ke * wp.outer(ground_normal, ground_normal)
531
-
532
- dx = particle_pos - particle_prev_pos
533
-
534
- if wp.dot(dx, ground_normal) < 0:
535
- damping_hessian = (soft_contact_kd / dt) * ground_contact_hessian
536
- ground_contact_hessian = ground_contact_hessian + damping_hessian
537
- ground_contact_force = ground_contact_force - damping_hessian * dx
538
-
539
- # friction
540
- e0, e1 = build_orthonormal_basis(ground_normal)
541
-
542
- T = mat32(e0[0], e1[0], e0[1], e1[1], e0[2], e1[2])
543
-
544
- relative_translation = dx
545
- u = wp.transpose(T) * relative_translation
546
- eps_u = friction_epsilon * dt
547
-
548
- friction_force, friction_hessian = compute_friction(friction_mu, ground_contact_force_norm, T, u, eps_u)
549
- ground_contact_force = ground_contact_force + friction_force
550
- ground_contact_hessian = ground_contact_hessian + friction_hessian
551
- else:
552
- ground_contact_force = wp.vec3(0.0, 0.0, 0.0)
553
- ground_contact_hessian = wp.mat33(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0)
554
-
555
- return ground_contact_force, ground_contact_hessian
556
-
557
-
558
- @wp.func
559
- def evaluate_body_particle_contact(
560
- particle_index: int,
561
- particle_pos: wp.vec3,
562
- particle_prev_pos: wp.vec3,
563
- contact_index: int,
564
- soft_contact_ke: float,
565
- soft_contact_kd: float,
566
- friction_mu: float,
567
- friction_epsilon: float,
568
- particle_radius: wp.array(dtype=float),
569
- shape_materials: ModelShapeMaterials,
570
- shape_body: wp.array(dtype=int),
571
- body_q: wp.array(dtype=wp.transform),
572
- body_qd: wp.array(dtype=wp.spatial_vector),
573
- body_com: wp.array(dtype=wp.vec3),
574
- contact_shape: wp.array(dtype=int),
575
- contact_body_pos: wp.array(dtype=wp.vec3),
576
- contact_body_vel: wp.array(dtype=wp.vec3),
577
- contact_normal: wp.array(dtype=wp.vec3),
578
- dt: float,
579
- ):
580
- shape_index = contact_shape[contact_index]
581
- body_index = shape_body[shape_index]
582
-
583
- X_wb = wp.transform_identity()
584
- X_com = wp.vec3()
585
- if body_index >= 0:
586
- X_wb = body_q[body_index]
587
- X_com = body_com[body_index]
588
-
589
- # body position in world space
590
- bx = wp.transform_point(X_wb, contact_body_pos[contact_index])
591
- r = bx - wp.transform_point(X_wb, X_com)
592
-
593
- n = contact_normal[contact_index]
594
-
595
- penetration_depth = -(wp.dot(n, particle_pos - bx) - particle_radius[particle_index])
596
- if penetration_depth > 0:
597
- body_contact_force_norm = penetration_depth * soft_contact_ke
598
- body_contact_force = n * body_contact_force_norm
599
- body_contact_hessian = soft_contact_ke * wp.outer(n, n)
600
-
601
- mu = shape_materials.mu[shape_index]
602
-
603
- dx = particle_pos - particle_prev_pos
604
-
605
- if wp.dot(n, dx) < 0:
606
- damping_hessian = (soft_contact_kd / dt) * body_contact_hessian
607
- body_contact_hessian = body_contact_hessian + damping_hessian
608
- body_contact_force = body_contact_force - damping_hessian * dx
609
-
610
- # body velocity
611
- body_v_s = wp.spatial_vector()
612
- if body_index >= 0:
613
- body_v_s = body_qd[body_index]
614
-
615
- body_w = wp.spatial_top(body_v_s)
616
- body_v = wp.spatial_bottom(body_v_s)
617
-
618
- # compute the body velocity at the particle position
619
- bv = body_v + wp.cross(body_w, r) + wp.transform_vector(X_wb, contact_body_vel[contact_index])
620
-
621
- relative_translation = dx - bv * dt
622
-
623
- # friction
624
- e0, e1 = build_orthonormal_basis(n)
625
-
626
- T = mat32(e0[0], e1[0], e0[1], e1[1], e0[2], e1[2])
627
-
628
- u = wp.transpose(T) * relative_translation
629
- eps_u = friction_epsilon * dt
630
-
631
- friction_force, friction_hessian = compute_friction(mu, body_contact_force_norm, T, u, eps_u)
632
- body_contact_force = body_contact_force + friction_force
633
- body_contact_hessian = body_contact_hessian + friction_hessian
634
- else:
635
- body_contact_force = wp.vec3(0.0, 0.0, 0.0)
636
- body_contact_hessian = wp.mat33(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0)
637
-
638
- return body_contact_force, body_contact_hessian
639
-
640
-
641
- @wp.func
642
- def evaluate_self_contact_force_norm(dis: float, collision_radius: float, k: float):
643
- # Adjust distance and calculate penetration depth
644
-
645
- penetration_depth = collision_radius - dis
646
-
647
- # Initialize outputs
648
- dEdD = wp.float32(0.0)
649
- d2E_dDdD = wp.float32(0.0)
650
-
651
- # C2 continuity calculation
652
- tau = collision_radius * 0.5
653
- if tau > dis > 1e-5:
654
- k2 = 0.5 * tau * tau * k
655
- dEdD = -k2 / dis
656
- d2E_dDdD = k2 / (dis * dis)
657
- else:
658
- dEdD = -k * penetration_depth
659
- d2E_dDdD = k
660
-
661
- return dEdD, d2E_dDdD
662
-
663
-
664
- @wp.func
665
- def damp_collision(
666
- displacement: wp.vec3,
667
- collision_normal: wp.vec3,
668
- collision_hessian: wp.mat33,
669
- collision_damping: float,
670
- dt: float,
671
- ):
672
- if wp.dot(displacement, collision_normal) > 0:
673
- damping_hessian = (collision_damping / dt) * collision_hessian
674
- damping_force = damping_hessian * displacement
675
- return damping_force, damping_hessian
676
- else:
677
- return wp.vec3(0.0), wp.mat33(0.0)
678
-
679
-
680
- @wp.func
681
- def evaluate_edge_edge_contact(
682
- v: int,
683
- v_order: int,
684
- e1: int,
685
- e2: int,
686
- pos: wp.array(dtype=wp.vec3),
687
- pos_prev: wp.array(dtype=wp.vec3),
688
- edge_indices: wp.array(dtype=wp.int32, ndim=2),
689
- collision_radius: float,
690
- collision_stiffness: float,
691
- collision_damping: float,
692
- friction_coefficient: float,
693
- friction_epsilon: float,
694
- dt: float,
695
- edge_edge_parallel_epsilon: float,
696
- ):
697
- r"""
698
- Returns the edge-edge contact force and hessian, including the friction force.
699
- Args:
700
- v:
701
- v_order: \in {0, 1, 2, 3}, 0, 1 is vertex 0, 1 of e1, 2,3 is vertex 0, 1 of e2
702
- e0
703
- e1
704
- pos
705
- pos_prev,
706
- edge_indices
707
- collision_radius
708
- collision_stiffness
709
- dt
710
- edge_edge_parallel_epsilon: threshold to determine whether 2 edges are parallel
711
- """
712
- e1_v1 = edge_indices[e1, 2]
713
- e1_v2 = edge_indices[e1, 3]
714
-
715
- e1_v1_pos = pos[e1_v1]
716
- e1_v2_pos = pos[e1_v2]
717
-
718
- e2_v1 = edge_indices[e2, 2]
719
- e2_v2 = edge_indices[e2, 3]
720
-
721
- e2_v1_pos = pos[e2_v1]
722
- e2_v2_pos = pos[e2_v2]
723
-
724
- st = wp.closest_point_edge_edge(e1_v1_pos, e1_v2_pos, e2_v1_pos, e2_v2_pos, edge_edge_parallel_epsilon)
725
- s = st[0]
726
- t = st[1]
727
- e1_vec = e1_v2_pos - e1_v1_pos
728
- e2_vec = e2_v2_pos - e2_v1_pos
729
- c1 = e1_v1_pos + e1_vec * s
730
- c2 = e2_v1_pos + e2_vec * t
731
-
732
- # c1, c2, s, t = closest_point_edge_edge_2(e1_v1_pos, e1_v2_pos, e2_v1_pos, e2_v2_pos)
733
-
734
- diff = c1 - c2
735
- dis = st[2]
736
- collision_normal = diff / dis
737
-
738
- if dis < collision_radius:
739
- bs = wp.vec4(1.0 - s, s, -1.0 + t, -t)
740
- v_bary = bs[v_order]
741
-
742
- dEdD, d2E_dDdD = evaluate_self_contact_force_norm(dis, collision_radius, collision_stiffness)
743
-
744
- collision_force = -dEdD * v_bary * collision_normal
745
- collision_hessian = d2E_dDdD * v_bary * v_bary * wp.outer(collision_normal, collision_normal)
746
-
747
- # friction
748
- c1_prev = pos_prev[e1_v1] + (pos_prev[e1_v2] - pos_prev[e1_v1]) * s
749
- c2_prev = pos_prev[e2_v1] + (pos_prev[e2_v2] - pos_prev[e2_v1]) * t
750
-
751
- dx = (c1 - c1_prev) - (c2 - c2_prev)
752
- axis_1, axis_2 = build_orthonormal_basis(collision_normal)
753
-
754
- T = mat32(
755
- axis_1[0],
756
- axis_2[0],
757
- axis_1[1],
758
- axis_2[1],
759
- axis_1[2],
760
- axis_2[2],
761
- )
762
-
763
- u = wp.transpose(T) * dx
764
- eps_U = friction_epsilon * dt
765
-
766
- # fmt: off
767
- if wp.static("contact_force_hessian_ee" in VBD_DEBUG_PRINTING_OPTIONS):
768
- wp.printf(
769
- " collision force:\n %f %f %f,\n collision hessian:\n %f %f %f,\n %f %f %f,\n %f %f %f\n",
770
- 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],
771
- )
772
- # fmt: on
773
-
774
- friction_force, friction_hessian = compute_friction(friction_coefficient, -dEdD, T, u, eps_U)
775
- friction_force = friction_force * v_bary
776
- friction_hessian = friction_hessian * v_bary * v_bary
777
-
778
- # # fmt: off
779
- # if wp.static("contact_force_hessian_ee" in VBD_DEBUG_PRINTING_OPTIONS):
780
- # wp.printf(
781
- # " friction force:\n %f %f %f,\n friction hessian:\n %f %f %f,\n %f %f %f,\n %f %f %f\n",
782
- # 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],
783
- # )
784
- # # fmt: on
785
-
786
- if v_order == 0:
787
- displacement = pos_prev[e1_v1] - e1_v1_pos
788
- elif v_order == 1:
789
- displacement = pos_prev[e1_v2] - e1_v2_pos
790
- elif v_order == 2:
791
- displacement = pos_prev[e2_v1] - e2_v1_pos
792
- else:
793
- displacement = pos_prev[e2_v2] - e2_v2_pos
794
-
795
- collision_normal_normal_sign = wp.vec4(1.0, 1.0, -1.0, -1.0)
796
- if wp.dot(displacement, collision_normal * collision_normal_normal_sign[v_order]) > 0:
797
- damping_hessian = (collision_damping / dt) * collision_hessian
798
- collision_hessian = collision_hessian + damping_hessian
799
- collision_force = collision_force + damping_hessian * displacement
800
-
801
- collision_force = collision_force + friction_force
802
- collision_hessian = collision_hessian + friction_hessian
803
- else:
804
- collision_force = wp.vec3(0.0, 0.0, 0.0)
805
- collision_hessian = wp.mat33(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0)
806
-
807
- return collision_force, collision_hessian
808
-
809
-
810
- @wp.func
811
- def evaluate_edge_edge_contact_2_vertices(
812
- e1: int,
813
- e2: int,
814
- pos: wp.array(dtype=wp.vec3),
815
- pos_prev: wp.array(dtype=wp.vec3),
816
- edge_indices: wp.array(dtype=wp.int32, ndim=2),
817
- collision_radius: float,
818
- collision_stiffness: float,
819
- collision_damping: float,
820
- friction_coefficient: float,
821
- friction_epsilon: float,
822
- dt: float,
823
- edge_edge_parallel_epsilon: float,
824
- ):
825
- r"""
826
- Returns the edge-edge contact force and hessian, including the friction force.
827
- Args:
828
- v:
829
- v_order: \in {0, 1, 2, 3}, 0, 1 is vertex 0, 1 of e1, 2,3 is vertex 0, 1 of e2
830
- e0
831
- e1
832
- pos
833
- edge_indices
834
- collision_radius
835
- collision_stiffness
836
- dt
837
- """
838
- e1_v1 = edge_indices[e1, 2]
839
- e1_v2 = edge_indices[e1, 3]
840
-
841
- e1_v1_pos = pos[e1_v1]
842
- e1_v2_pos = pos[e1_v2]
843
-
844
- e2_v1 = edge_indices[e2, 2]
845
- e2_v2 = edge_indices[e2, 3]
846
-
847
- e2_v1_pos = pos[e2_v1]
848
- e2_v2_pos = pos[e2_v2]
849
-
850
- st = wp.closest_point_edge_edge(e1_v1_pos, e1_v2_pos, e2_v1_pos, e2_v2_pos, edge_edge_parallel_epsilon)
851
- s = st[0]
852
- t = st[1]
853
- e1_vec = e1_v2_pos - e1_v1_pos
854
- e2_vec = e2_v2_pos - e2_v1_pos
855
- c1 = e1_v1_pos + e1_vec * s
856
- c2 = e2_v1_pos + e2_vec * t
857
-
858
- # c1, c2, s, t = closest_point_edge_edge_2(e1_v1_pos, e1_v2_pos, e2_v1_pos, e2_v2_pos)
859
-
860
- diff = c1 - c2
861
- dis = st[2]
862
- collision_normal = diff / dis
863
-
864
- if dis < collision_radius:
865
- bs = wp.vec4(1.0 - s, s, -1.0 + t, -t)
866
-
867
- dEdD, d2E_dDdD = evaluate_self_contact_force_norm(dis, collision_radius, collision_stiffness)
868
-
869
- collision_force = -dEdD * collision_normal
870
- collision_hessian = d2E_dDdD * wp.outer(collision_normal, collision_normal)
871
-
872
- # friction
873
- c1_prev = pos_prev[e1_v1] + (pos_prev[e1_v2] - pos_prev[e1_v1]) * s
874
- c2_prev = pos_prev[e2_v1] + (pos_prev[e2_v2] - pos_prev[e2_v1]) * t
875
-
876
- dx = (c1 - c1_prev) - (c2 - c2_prev)
877
- axis_1, axis_2 = build_orthonormal_basis(collision_normal)
878
-
879
- T = mat32(
880
- axis_1[0],
881
- axis_2[0],
882
- axis_1[1],
883
- axis_2[1],
884
- axis_1[2],
885
- axis_2[2],
886
- )
887
-
888
- u = wp.transpose(T) * dx
889
- eps_U = friction_epsilon * dt
890
-
891
- # fmt: off
892
- if wp.static("contact_force_hessian_ee" in VBD_DEBUG_PRINTING_OPTIONS):
893
- wp.printf(
894
- " collision force:\n %f %f %f,\n collision hessian:\n %f %f %f,\n %f %f %f,\n %f %f %f\n",
895
- 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],
896
- )
897
- # fmt: on
898
-
899
- friction_force, friction_hessian = compute_friction(friction_coefficient, -dEdD, T, u, eps_U)
900
-
901
- # # fmt: off
902
- # if wp.static("contact_force_hessian_ee" in VBD_DEBUG_PRINTING_OPTIONS):
903
- # wp.printf(
904
- # " friction force:\n %f %f %f,\n friction hessian:\n %f %f %f,\n %f %f %f,\n %f %f %f\n",
905
- # 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],
906
- # )
907
- # # fmt: on
908
-
909
- displacement_0 = pos_prev[e1_v1] - e1_v1_pos
910
- displacement_1 = pos_prev[e1_v2] - e1_v2_pos
911
-
912
- collision_force_0 = collision_force * bs[0]
913
- collision_force_1 = collision_force * bs[1]
914
-
915
- collision_hessian_0 = collision_hessian * bs[0] * bs[0]
916
- collision_hessian_1 = collision_hessian * bs[1] * bs[1]
917
-
918
- collision_normal_normal_sign = wp.vec4(1.0, 1.0, -1.0, -1.0)
919
- damping_force, damping_hessian = damp_collision(
920
- displacement_0,
921
- collision_normal * collision_normal_normal_sign[0],
922
- collision_hessian_0,
923
- collision_damping,
924
- dt,
925
- )
926
-
927
- collision_force_0 += damping_force + bs[0] * friction_force
928
- collision_hessian_0 += damping_hessian + bs[0] * bs[0] * friction_hessian
929
-
930
- damping_force, damping_hessian = damp_collision(
931
- displacement_1,
932
- collision_normal * collision_normal_normal_sign[1],
933
- collision_hessian_1,
934
- collision_damping,
935
- dt,
936
- )
937
- collision_force_1 += damping_force + bs[1] * friction_force
938
- collision_hessian_1 += damping_hessian + bs[1] * bs[1] * friction_hessian
939
-
940
- return True, collision_force_0, collision_force_1, collision_hessian_0, collision_hessian_1
941
- else:
942
- collision_force = wp.vec3(0.0, 0.0, 0.0)
943
- collision_hessian = wp.mat33(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0)
944
-
945
- return False, collision_force, collision_force, collision_hessian, collision_hessian
946
-
947
-
948
- @wp.func
949
- def evaluate_vertex_triangle_collision_force_hessian(
950
- v: int,
951
- v_order: int,
952
- tri: int,
953
- pos: wp.array(dtype=wp.vec3),
954
- pos_prev: wp.array(dtype=wp.vec3),
955
- tri_indices: wp.array(dtype=wp.int32, ndim=2),
956
- collision_radius: float,
957
- collision_stiffness: float,
958
- collision_damping: float,
959
- friction_coefficient: float,
960
- friction_epsilon: float,
961
- dt: float,
962
- ):
963
- a = pos[tri_indices[tri, 0]]
964
- b = pos[tri_indices[tri, 1]]
965
- c = pos[tri_indices[tri, 2]]
966
-
967
- p = pos[v]
968
-
969
- closest_p, bary, feature_type = triangle_closest_point(a, b, c, p)
970
-
971
- diff = p - closest_p
972
- dis = wp.length(diff)
973
- collision_normal = diff / dis
974
-
975
- if dis < collision_radius:
976
- bs = wp.vec4(-bary[0], -bary[1], -bary[2], 1.0)
977
- v_bary = bs[v_order]
978
-
979
- dEdD, d2E_dDdD = evaluate_self_contact_force_norm(dis, collision_radius, collision_stiffness)
980
-
981
- collision_force = -dEdD * v_bary * collision_normal
982
- collision_hessian = d2E_dDdD * v_bary * v_bary * wp.outer(collision_normal, collision_normal)
983
-
984
- # friction force
985
- dx_v = p - pos_prev[v]
986
-
987
- closest_p_prev = (
988
- bary[0] * pos_prev[tri_indices[tri, 0]]
989
- + bary[1] * pos_prev[tri_indices[tri, 1]]
990
- + bary[2] * pos_prev[tri_indices[tri, 2]]
991
- )
992
-
993
- dx = dx_v - (closest_p - closest_p_prev)
994
-
995
- e0, e1 = build_orthonormal_basis(collision_normal)
996
-
997
- T = mat32(e0[0], e1[0], e0[1], e1[1], e0[2], e1[2])
998
-
999
- u = wp.transpose(T) * dx
1000
- eps_U = friction_epsilon * dt
1001
-
1002
- friction_force, friction_hessian = compute_friction(friction_coefficient, -dEdD, T, u, eps_U)
1003
-
1004
- # fmt: off
1005
- if wp.static("contact_force_hessian_vt" in VBD_DEBUG_PRINTING_OPTIONS):
1006
- wp.printf(
1007
- "v: %d dEdD: %f\nnormal force: %f %f %f\nfriction force: %f %f %f\n",
1008
- v,
1009
- dEdD,
1010
- collision_force[0], collision_force[1], collision_force[2], friction_force[0], friction_force[1], friction_force[2],
1011
- )
1012
- # fmt: on
1013
-
1014
- if v_order == 0:
1015
- displacement = pos_prev[tri_indices[tri, 0]] - a
1016
- elif v_order == 1:
1017
- displacement = pos_prev[tri_indices[tri, 1]] - b
1018
- elif v_order == 2:
1019
- displacement = pos_prev[tri_indices[tri, 2]] - c
1020
- else:
1021
- displacement = pos_prev[v] - p
1022
-
1023
- collision_normal_normal_sign = wp.vec4(-1.0, -1.0, -1.0, 1.0)
1024
- if wp.dot(displacement, collision_normal * collision_normal_normal_sign[v_order]) > 0:
1025
- damping_hessian = (collision_damping / dt) * collision_hessian
1026
- collision_hessian = collision_hessian + damping_hessian
1027
- collision_force = collision_force + damping_hessian * displacement
1028
-
1029
- collision_force = collision_force + v_bary * friction_force
1030
- collision_hessian = collision_hessian + v_bary * v_bary * friction_hessian
1031
- else:
1032
- collision_force = wp.vec3(0.0, 0.0, 0.0)
1033
- collision_hessian = wp.mat33(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0)
1034
-
1035
- return collision_force, collision_hessian
1036
-
1037
-
1038
- @wp.func
1039
- def evaluate_vertex_triangle_collision_force_hessian_4_vertices(
1040
- v: int,
1041
- tri: int,
1042
- pos: wp.array(dtype=wp.vec3),
1043
- pos_prev: wp.array(dtype=wp.vec3),
1044
- tri_indices: wp.array(dtype=wp.int32, ndim=2),
1045
- collision_radius: float,
1046
- collision_stiffness: float,
1047
- collision_damping: float,
1048
- friction_coefficient: float,
1049
- friction_epsilon: float,
1050
- dt: float,
1051
- ):
1052
- a = pos[tri_indices[tri, 0]]
1053
- b = pos[tri_indices[tri, 1]]
1054
- c = pos[tri_indices[tri, 2]]
1055
-
1056
- p = pos[v]
1057
-
1058
- closest_p, bary, feature_type = triangle_closest_point(a, b, c, p)
1059
-
1060
- diff = p - closest_p
1061
- dis = wp.length(diff)
1062
- collision_normal = diff / dis
1063
-
1064
- if dis < collision_radius:
1065
- bs = wp.vec4(-bary[0], -bary[1], -bary[2], 1.0)
1066
-
1067
- dEdD, d2E_dDdD = evaluate_self_contact_force_norm(dis, collision_radius, collision_stiffness)
1068
-
1069
- collision_force = -dEdD * collision_normal
1070
- collision_hessian = d2E_dDdD * wp.outer(collision_normal, collision_normal)
1071
-
1072
- # friction force
1073
- dx_v = p - pos_prev[v]
1074
-
1075
- closest_p_prev = (
1076
- bary[0] * pos_prev[tri_indices[tri, 0]]
1077
- + bary[1] * pos_prev[tri_indices[tri, 1]]
1078
- + bary[2] * pos_prev[tri_indices[tri, 2]]
1079
- )
1080
-
1081
- dx = dx_v - (closest_p - closest_p_prev)
1082
-
1083
- e0, e1 = build_orthonormal_basis(collision_normal)
1084
-
1085
- T = mat32(e0[0], e1[0], e0[1], e1[1], e0[2], e1[2])
1086
-
1087
- u = wp.transpose(T) * dx
1088
- eps_U = friction_epsilon * dt
1089
-
1090
- friction_force, friction_hessian = compute_friction(friction_coefficient, -dEdD, T, u, eps_U)
1091
-
1092
- # fmt: off
1093
- if wp.static("contact_force_hessian_vt" in VBD_DEBUG_PRINTING_OPTIONS):
1094
- wp.printf(
1095
- "v: %d dEdD: %f\nnormal force: %f %f %f\nfriction force: %f %f %f\n",
1096
- v,
1097
- dEdD,
1098
- collision_force[0], collision_force[1], collision_force[2], friction_force[0], friction_force[1],
1099
- friction_force[2],
1100
- )
1101
- # fmt: on
1102
-
1103
- displacement_0 = pos_prev[tri_indices[tri, 0]] - a
1104
- displacement_1 = pos_prev[tri_indices[tri, 1]] - b
1105
- displacement_2 = pos_prev[tri_indices[tri, 2]] - c
1106
- displacement_3 = pos_prev[v] - p
1107
-
1108
- collision_force_0 = collision_force * bs[0]
1109
- collision_force_1 = collision_force * bs[1]
1110
- collision_force_2 = collision_force * bs[2]
1111
- collision_force_3 = collision_force * bs[3]
1112
-
1113
- collision_hessian_0 = collision_hessian * bs[0] * bs[0]
1114
- collision_hessian_1 = collision_hessian * bs[1] * bs[1]
1115
- collision_hessian_2 = collision_hessian * bs[2] * bs[2]
1116
- collision_hessian_3 = collision_hessian * bs[3] * bs[3]
1117
-
1118
- collision_normal_normal_sign = wp.vec4(-1.0, -1.0, -1.0, 1.0)
1119
- damping_force, damping_hessian = damp_collision(
1120
- displacement_0,
1121
- collision_normal * collision_normal_normal_sign[0],
1122
- collision_hessian_0,
1123
- collision_damping,
1124
- dt,
1125
- )
1126
-
1127
- collision_force_0 += damping_force + bs[0] * friction_force
1128
- collision_hessian_0 += damping_hessian + bs[0] * bs[0] * friction_hessian
1129
-
1130
- damping_force, damping_hessian = damp_collision(
1131
- displacement_1,
1132
- collision_normal * collision_normal_normal_sign[1],
1133
- collision_hessian_1,
1134
- collision_damping,
1135
- dt,
1136
- )
1137
- collision_force_1 += damping_force + bs[1] * friction_force
1138
- collision_hessian_1 += damping_hessian + bs[1] * bs[1] * friction_hessian
1139
-
1140
- damping_force, damping_hessian = damp_collision(
1141
- displacement_2,
1142
- collision_normal * collision_normal_normal_sign[2],
1143
- collision_hessian_2,
1144
- collision_damping,
1145
- dt,
1146
- )
1147
- collision_force_2 += damping_force + bs[2] * friction_force
1148
- collision_hessian_2 += damping_hessian + bs[2] * bs[2] * friction_hessian
1149
-
1150
- damping_force, damping_hessian = damp_collision(
1151
- displacement_3,
1152
- collision_normal * collision_normal_normal_sign[3],
1153
- collision_hessian_3,
1154
- collision_damping,
1155
- dt,
1156
- )
1157
- collision_force_3 += damping_force + bs[3] * friction_force
1158
- collision_hessian_3 += damping_hessian + bs[3] * bs[3] * friction_hessian
1159
- return (
1160
- True,
1161
- collision_force_0,
1162
- collision_force_1,
1163
- collision_force_2,
1164
- collision_force_3,
1165
- collision_hessian_0,
1166
- collision_hessian_1,
1167
- collision_hessian_2,
1168
- collision_hessian_3,
1169
- )
1170
- else:
1171
- collision_force = wp.vec3(0.0, 0.0, 0.0)
1172
- collision_hessian = wp.mat33(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0)
1173
-
1174
- return (
1175
- False,
1176
- collision_force,
1177
- collision_force,
1178
- collision_force,
1179
- collision_force,
1180
- collision_hessian,
1181
- collision_hessian,
1182
- collision_hessian,
1183
- collision_hessian,
1184
- )
1185
-
1186
-
1187
- @wp.func
1188
- def compute_friction(mu: float, normal_contact_force: float, T: mat32, u: wp.vec2, eps_u: float):
1189
- """
1190
- Returns the 1D friction force and hessian.
1191
- Args:
1192
- mu: Friction coefficient.
1193
- normal_contact_force: normal contact force.
1194
- T: Transformation matrix (3x2 matrix).
1195
- u: 2D displacement vector.
1196
- """
1197
- # Friction
1198
- u_norm = wp.length(u)
1199
-
1200
- if u_norm > 0.0:
1201
- # IPC friction
1202
- if u_norm > eps_u:
1203
- # constant stage
1204
- f1_SF_over_x = 1.0 / u_norm
1205
- else:
1206
- # smooth transition
1207
- f1_SF_over_x = (-u_norm / eps_u + 2.0) / eps_u
1208
-
1209
- force = -mu * normal_contact_force * T * (f1_SF_over_x * u)
1210
-
1211
- # Different from IPC, we treat the contact normal as constant
1212
- # this significantly improves the stability
1213
- hessian = mu * normal_contact_force * T * (f1_SF_over_x * wp.identity(2, float)) * wp.transpose(T)
1214
- else:
1215
- force = wp.vec3(0.0, 0.0, 0.0)
1216
- hessian = wp.mat33(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0)
1217
-
1218
- return force, hessian
1219
-
1220
-
1221
- @wp.kernel
1222
- def forward_step(
1223
- dt: float,
1224
- gravity: wp.vec3,
1225
- prev_pos: wp.array(dtype=wp.vec3),
1226
- pos: wp.array(dtype=wp.vec3),
1227
- vel: wp.array(dtype=wp.vec3),
1228
- inv_mass: wp.array(dtype=float),
1229
- external_force: wp.array(dtype=wp.vec3),
1230
- particle_flags: wp.array(dtype=wp.uint32),
1231
- inertia: wp.array(dtype=wp.vec3),
1232
- ):
1233
- particle = wp.tid()
1234
-
1235
- prev_pos[particle] = pos[particle]
1236
- if not particle_flags[particle] & PARTICLE_FLAG_ACTIVE:
1237
- inertia[particle] = prev_pos[particle]
1238
- return
1239
- vel_new = vel[particle] + (gravity + external_force[particle] * inv_mass[particle]) * dt
1240
- pos[particle] = pos[particle] + vel_new * dt
1241
- inertia[particle] = pos[particle]
1242
-
1243
-
1244
- @wp.kernel
1245
- def forward_step_penetration_free(
1246
- dt: float,
1247
- gravity: wp.vec3,
1248
- prev_pos: wp.array(dtype=wp.vec3),
1249
- pos: wp.array(dtype=wp.vec3),
1250
- vel: wp.array(dtype=wp.vec3),
1251
- inv_mass: wp.array(dtype=float),
1252
- external_force: wp.array(dtype=wp.vec3),
1253
- particle_flags: wp.array(dtype=wp.uint32),
1254
- pos_prev_collision_detection: wp.array(dtype=wp.vec3),
1255
- particle_conservative_bounds: wp.array(dtype=float),
1256
- inertia: wp.array(dtype=wp.vec3),
1257
- ):
1258
- particle_index = wp.tid()
1259
-
1260
- prev_pos[particle_index] = pos[particle_index]
1261
- if not particle_flags[particle_index] & PARTICLE_FLAG_ACTIVE:
1262
- inertia[particle_index] = prev_pos[particle_index]
1263
- return
1264
- vel_new = vel[particle_index] + (gravity + external_force[particle_index] * inv_mass[particle_index]) * dt
1265
- pos_inertia = pos[particle_index] + vel_new * dt
1266
- inertia[particle_index] = pos_inertia
1267
-
1268
- pos[particle_index] = apply_conservative_bound_truncation(
1269
- particle_index, pos_inertia, pos_prev_collision_detection, particle_conservative_bounds
1270
- )
1271
-
1272
-
1273
- @wp.kernel
1274
- def compute_particle_conservative_bound(
1275
- # inputs
1276
- conservative_bound_relaxation: float,
1277
- collision_query_radius: float,
1278
- adjacency: ForceElementAdjacencyInfo,
1279
- collision_info: TriMeshCollisionInfo,
1280
- # outputs
1281
- particle_conservative_bounds: wp.array(dtype=float),
1282
- ):
1283
- particle_index = wp.tid()
1284
- min_dist = wp.min(collision_query_radius, collision_info.vertex_colliding_triangles_min_dist[particle_index])
1285
-
1286
- # bound from neighbor triangles
1287
- for i_adj_tri in range(
1288
- get_vertex_num_adjacent_faces(
1289
- adjacency,
1290
- particle_index,
1291
- )
1292
- ):
1293
- tri_index, vertex_order = get_vertex_adjacent_face_id_order(
1294
- adjacency,
1295
- particle_index,
1296
- i_adj_tri,
1297
- )
1298
- min_dist = wp.min(min_dist, collision_info.triangle_colliding_vertices_min_dist[tri_index])
1299
-
1300
- # bound from neighbor edges
1301
- for i_adj_edge in range(
1302
- get_vertex_num_adjacent_edges(
1303
- adjacency,
1304
- particle_index,
1305
- )
1306
- ):
1307
- nei_edge_index, vertex_order_on_edge = get_vertex_adjacent_edge_id_order(
1308
- adjacency,
1309
- particle_index,
1310
- i_adj_edge,
1311
- )
1312
- # vertex is on the edge; otherwise it only effects the bending energy
1313
- if vertex_order_on_edge == 2 or vertex_order_on_edge == 3:
1314
- # collisions of neighbor edges
1315
- min_dist = wp.min(min_dist, collision_info.edge_colliding_edges_min_dist[nei_edge_index])
1316
-
1317
- particle_conservative_bounds[particle_index] = conservative_bound_relaxation * min_dist
1318
-
1319
-
1320
- @wp.kernel
1321
- def validate_conservative_bound(
1322
- pos: wp.array(dtype=wp.vec3),
1323
- pos_prev_collision_detection: wp.array(dtype=wp.vec3),
1324
- particle_conservative_bounds: wp.array(dtype=float),
1325
- ):
1326
- v_index = wp.tid()
1327
-
1328
- displacement = wp.length(pos[v_index] - pos_prev_collision_detection[v_index])
1329
-
1330
- if displacement > particle_conservative_bounds[v_index] * 1.01 and displacement > 1e-5:
1331
- # wp.expect_eq(displacement <= particle_conservative_bounds[v_index] * 1.01, True)
1332
- wp.printf(
1333
- "Vertex %d has moved by %f exceeded the limit of %f\n",
1334
- v_index,
1335
- displacement,
1336
- particle_conservative_bounds[v_index],
1337
- )
1338
-
1339
-
1340
- @wp.func
1341
- def apply_conservative_bound_truncation(
1342
- v_index: wp.int32,
1343
- pos_new: wp.vec3,
1344
- pos_prev_collision_detection: wp.array(dtype=wp.vec3),
1345
- particle_conservative_bounds: wp.array(dtype=float),
1346
- ):
1347
- particle_pos_prev_collision_detection = pos_prev_collision_detection[v_index]
1348
- accumulated_displacement = pos_new - particle_pos_prev_collision_detection
1349
- conservative_bound = particle_conservative_bounds[v_index]
1350
-
1351
- accumulated_displacement_norm = wp.length(accumulated_displacement)
1352
- if accumulated_displacement_norm > conservative_bound and conservative_bound > 1e-5:
1353
- accumulated_displacement_norm_truncated = conservative_bound
1354
- accumulated_displacement = accumulated_displacement * (
1355
- accumulated_displacement_norm_truncated / accumulated_displacement_norm
1356
- )
1357
-
1358
- return particle_pos_prev_collision_detection + accumulated_displacement
1359
- else:
1360
- return pos_new
1361
-
1362
-
1363
- @wp.kernel
1364
- def VBD_solve_trimesh_no_self_contact(
1365
- dt: float,
1366
- particle_ids_in_color: wp.array(dtype=wp.int32),
1367
- prev_pos: wp.array(dtype=wp.vec3),
1368
- pos: wp.array(dtype=wp.vec3),
1369
- vel: wp.array(dtype=wp.vec3),
1370
- mass: wp.array(dtype=float),
1371
- inertia: wp.array(dtype=wp.vec3),
1372
- particle_flags: wp.array(dtype=wp.uint32),
1373
- tri_indices: wp.array(dtype=wp.int32, ndim=2),
1374
- tri_poses: wp.array(dtype=wp.mat22),
1375
- tri_materials: wp.array(dtype=float, ndim=2),
1376
- tri_areas: wp.array(dtype=float),
1377
- edge_indices: wp.array(dtype=wp.int32, ndim=2),
1378
- edge_rest_angles: wp.array(dtype=float),
1379
- edge_rest_length: wp.array(dtype=float),
1380
- edge_bending_properties: wp.array(dtype=float, ndim=2),
1381
- adjacency: ForceElementAdjacencyInfo,
1382
- particle_forces: wp.array(dtype=wp.vec3),
1383
- particle_hessians: wp.array(dtype=wp.mat33),
1384
- # contact info
1385
- soft_contact_ke: float,
1386
- soft_contact_kd: float,
1387
- friction_mu: float,
1388
- friction_epsilon: float,
1389
- # ground-particle contact
1390
- has_ground: bool,
1391
- ground: wp.array(dtype=float),
1392
- particle_radius: wp.array(dtype=float),
1393
- # output
1394
- pos_new: wp.array(dtype=wp.vec3),
1395
- ):
1396
- tid = wp.tid()
1397
-
1398
- particle_index = particle_ids_in_color[tid]
1399
- particle_pos = pos[particle_index]
1400
-
1401
- if not particle_flags[particle_index] & PARTICLE_FLAG_ACTIVE:
1402
- pos_new[particle_index] = particle_pos
1403
- return
1404
-
1405
- particle_prev_pos = pos[particle_index]
1406
-
1407
- dt_sqr_reciprocal = 1.0 / (dt * dt)
1408
-
1409
- # inertia force and hessian
1410
- f = mass[particle_index] * (inertia[particle_index] - pos[particle_index]) * (dt_sqr_reciprocal)
1411
- h = mass[particle_index] * dt_sqr_reciprocal * wp.identity(n=3, dtype=float)
1412
-
1413
- # elastic force and hessian
1414
- for i_adj_tri in range(get_vertex_num_adjacent_faces(adjacency, particle_index)):
1415
- tri_id, particle_order = get_vertex_adjacent_face_id_order(adjacency, particle_index, i_adj_tri)
1416
-
1417
- # fmt: off
1418
- if wp.static("connectivity" in VBD_DEBUG_PRINTING_OPTIONS):
1419
- wp.printf(
1420
- "particle: %d | num_adj_faces: %d | ",
1421
- particle_index,
1422
- get_vertex_num_adjacent_faces(particle_index, adjacency),
1423
- )
1424
- wp.printf("i_face: %d | face id: %d | v_order: %d | ", i_adj_tri, tri_id, particle_order)
1425
- wp.printf(
1426
- "face: %d %d %d\n",
1427
- tri_indices[tri_id, 0],
1428
- tri_indices[tri_id, 1],
1429
- tri_indices[tri_id, 2],
1430
- )
1431
- # fmt: on
1432
-
1433
- f_tri, h_tri = evaluate_stvk_force_hessian(
1434
- tri_id,
1435
- particle_order,
1436
- pos,
1437
- tri_indices,
1438
- tri_poses[tri_id],
1439
- tri_areas[tri_id],
1440
- tri_materials[tri_id, 0],
1441
- tri_materials[tri_id, 1],
1442
- tri_materials[tri_id, 2],
1443
- )
1444
- # compute damping
1445
- k_d = tri_materials[tri_id, 2]
1446
- h_d = h_tri * (k_d / dt)
1447
-
1448
- f_d = h_d * (prev_pos[particle_index] - pos[particle_index])
1449
-
1450
- f = f + f_tri + f_d
1451
- h = h + h_tri + h_d
1452
-
1453
- # fmt: off
1454
- if wp.static("elasticity_force_hessian" in VBD_DEBUG_PRINTING_OPTIONS):
1455
- wp.printf(
1456
- "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",
1457
- particle_index,
1458
- i_adj_tri,
1459
- particle_order,
1460
- 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],
1461
- )
1462
- # fmt: on
1463
-
1464
- for i_adj_edge in range(get_vertex_num_adjacent_edges(adjacency, particle_index)):
1465
- nei_edge_index, vertex_order_on_edge = get_vertex_adjacent_edge_id_order(adjacency, particle_index, i_adj_edge)
1466
- f_edge, h_edge = evaluate_dihedral_angle_based_bending_force_hessian(
1467
- nei_edge_index,
1468
- vertex_order_on_edge,
1469
- pos,
1470
- prev_pos,
1471
- edge_indices,
1472
- edge_rest_angles,
1473
- edge_rest_length,
1474
- edge_bending_properties[nei_edge_index, 0],
1475
- edge_bending_properties[nei_edge_index, 1],
1476
- dt,
1477
- )
1478
-
1479
- f = f + f_edge
1480
- h = h + h_edge
1481
-
1482
- if has_ground:
1483
- ground_normal = wp.vec3(ground[0], ground[1], ground[2])
1484
- ground_level = ground[3]
1485
- ground_contact_force, ground_contact_hessian = evaluate_ground_contact_force_hessian(
1486
- particle_pos,
1487
- particle_prev_pos,
1488
- particle_radius[particle_index],
1489
- ground_normal,
1490
- ground_level,
1491
- soft_contact_ke,
1492
- soft_contact_kd,
1493
- friction_mu,
1494
- friction_epsilon,
1495
- dt,
1496
- )
1497
-
1498
- f += ground_contact_force
1499
- h += ground_contact_hessian
1500
-
1501
- f += particle_forces[particle_index]
1502
- h += particle_hessians[particle_index]
1503
- if abs(wp.determinant(h)) > 1e-5:
1504
- hInv = wp.inverse(h)
1505
- pos_new[particle_index] = particle_pos + hInv * f
1506
-
1507
-
1508
- @wp.kernel
1509
- def VBD_copy_particle_positions_back(
1510
- particle_ids_in_color: wp.array(dtype=wp.int32),
1511
- pos: wp.array(dtype=wp.vec3),
1512
- pos_new: wp.array(dtype=wp.vec3),
1513
- ):
1514
- tid = wp.tid()
1515
- particle = particle_ids_in_color[tid]
1516
-
1517
- pos[particle] = pos_new[particle]
1518
-
1519
-
1520
- @wp.kernel
1521
- def update_velocity(
1522
- dt: float, prev_pos: wp.array(dtype=wp.vec3), pos: wp.array(dtype=wp.vec3), vel: wp.array(dtype=wp.vec3)
1523
- ):
1524
- particle = wp.tid()
1525
- vel[particle] = (pos[particle] - prev_pos[particle]) / dt
1526
-
1527
-
1528
- @wp.kernel
1529
- def convert_body_particle_contact_data_kernel(
1530
- # inputs
1531
- body_particle_contact_buffer_pre_alloc: int,
1532
- soft_contact_particle: wp.array(dtype=int),
1533
- contact_count: wp.array(dtype=int),
1534
- contact_max: int,
1535
- # outputs
1536
- body_particle_contact_buffer: wp.array(dtype=int),
1537
- body_particle_contact_count: wp.array(dtype=int),
1538
- ):
1539
- contact_index = wp.tid()
1540
- count = min(contact_max, contact_count[0])
1541
- if contact_index >= count:
1542
- return
1543
-
1544
- particle_index = soft_contact_particle[contact_index]
1545
- offset = particle_index * body_particle_contact_buffer_pre_alloc
1546
-
1547
- contact_counter = wp.atomic_add(body_particle_contact_count, particle_index, 1)
1548
- if contact_counter < body_particle_contact_buffer_pre_alloc:
1549
- body_particle_contact_buffer[offset + contact_counter] = contact_index
1550
-
1551
-
1552
- @wp.kernel
1553
- def VBD_accumulate_contact_force_and_hessian(
1554
- # inputs
1555
- dt: float,
1556
- current_color: int,
1557
- pos_prev: wp.array(dtype=wp.vec3),
1558
- pos: wp.array(dtype=wp.vec3),
1559
- particle_colors: wp.array(dtype=int),
1560
- tri_indices: wp.array(dtype=wp.int32, ndim=2),
1561
- edge_indices: wp.array(dtype=wp.int32, ndim=2),
1562
- # self contact
1563
- collision_info_array: wp.array(dtype=TriMeshCollisionInfo),
1564
- collision_radius: float,
1565
- soft_contact_ke: float,
1566
- soft_contact_kd: float,
1567
- friction_mu: float,
1568
- friction_epsilon: float,
1569
- edge_edge_parallel_epsilon: float,
1570
- # body-particle contact
1571
- particle_radius: wp.array(dtype=float),
1572
- soft_contact_particle: wp.array(dtype=int),
1573
- contact_count: wp.array(dtype=int),
1574
- contact_max: int,
1575
- shape_materials: ModelShapeMaterials,
1576
- shape_body: wp.array(dtype=int),
1577
- body_q: wp.array(dtype=wp.transform),
1578
- body_qd: wp.array(dtype=wp.spatial_vector),
1579
- body_com: wp.array(dtype=wp.vec3),
1580
- contact_shape: wp.array(dtype=int),
1581
- contact_body_pos: wp.array(dtype=wp.vec3),
1582
- contact_body_vel: wp.array(dtype=wp.vec3),
1583
- contact_normal: wp.array(dtype=wp.vec3),
1584
- # outputs: particle force and hessian
1585
- particle_forces: wp.array(dtype=wp.vec3),
1586
- particle_hessians: wp.array(dtype=wp.mat33),
1587
- ):
1588
- t_id = wp.tid()
1589
- collision_info = collision_info_array[0]
1590
-
1591
- # process edge-edge collisions
1592
- if t_id * 2 < collision_info.edge_colliding_edges.shape[0]:
1593
- e1_idx = collision_info.edge_colliding_edges[2 * t_id]
1594
- e2_idx = collision_info.edge_colliding_edges[2 * t_id + 1]
1595
-
1596
- if e1_idx != -1 and e2_idx != -1:
1597
- e1_v1 = edge_indices[e1_idx, 2]
1598
- e1_v2 = edge_indices[e1_idx, 3]
1599
- if particle_colors[e1_v1] == current_color or particle_colors[e1_v2] == current_color:
1600
- has_contact, collision_force_0, collision_force_1, collision_hessian_0, collision_hessian_1 = (
1601
- evaluate_edge_edge_contact_2_vertices(
1602
- e1_idx,
1603
- e2_idx,
1604
- pos,
1605
- pos_prev,
1606
- edge_indices,
1607
- collision_radius,
1608
- soft_contact_ke,
1609
- soft_contact_kd,
1610
- friction_mu,
1611
- friction_epsilon,
1612
- dt,
1613
- edge_edge_parallel_epsilon,
1614
- )
1615
- )
1616
-
1617
- if has_contact:
1618
- # here we only handle the e1 side, because e2 will also detection this contact and add force and hessian on its own
1619
- if particle_colors[e1_v1] == current_color:
1620
- wp.atomic_add(particle_forces, e1_v1, collision_force_0)
1621
- wp.atomic_add(particle_hessians, e1_v1, collision_hessian_0)
1622
- if particle_colors[e1_v2] == current_color:
1623
- wp.atomic_add(particle_forces, e1_v2, collision_force_1)
1624
- wp.atomic_add(particle_hessians, e1_v2, collision_hessian_1)
1625
-
1626
- # process vertex-triangle collisions
1627
- if t_id * 2 < collision_info.vertex_colliding_triangles.shape[0]:
1628
- particle_idx = collision_info.vertex_colliding_triangles[2 * t_id]
1629
- tri_idx = collision_info.vertex_colliding_triangles[2 * t_id + 1]
1630
-
1631
- if particle_idx != -1 and tri_idx != -1:
1632
- tri_a = tri_indices[tri_idx, 0]
1633
- tri_b = tri_indices[tri_idx, 1]
1634
- tri_c = tri_indices[tri_idx, 2]
1635
- if (
1636
- particle_colors[particle_idx] == current_color
1637
- or particle_colors[tri_a] == current_color
1638
- or particle_colors[tri_b] == current_color
1639
- or particle_colors[tri_c] == current_color
1640
- ):
1641
- (
1642
- has_contact,
1643
- collision_force_0,
1644
- collision_force_1,
1645
- collision_force_2,
1646
- collision_force_3,
1647
- collision_hessian_0,
1648
- collision_hessian_1,
1649
- collision_hessian_2,
1650
- collision_hessian_3,
1651
- ) = evaluate_vertex_triangle_collision_force_hessian_4_vertices(
1652
- particle_idx,
1653
- tri_idx,
1654
- pos,
1655
- pos_prev,
1656
- tri_indices,
1657
- collision_radius,
1658
- soft_contact_ke,
1659
- soft_contact_kd,
1660
- friction_mu,
1661
- friction_epsilon,
1662
- dt,
1663
- )
1664
-
1665
- if has_contact:
1666
- # particle
1667
- if particle_colors[particle_idx] == current_color:
1668
- wp.atomic_add(particle_forces, particle_idx, collision_force_3)
1669
- wp.atomic_add(particle_hessians, particle_idx, collision_hessian_3)
1670
-
1671
- # tri_a
1672
- if particle_colors[tri_a] == current_color:
1673
- wp.atomic_add(particle_forces, tri_a, collision_force_0)
1674
- wp.atomic_add(particle_hessians, tri_a, collision_hessian_0)
1675
-
1676
- # tri_b
1677
- if particle_colors[tri_b] == current_color:
1678
- wp.atomic_add(particle_forces, tri_b, collision_force_1)
1679
- wp.atomic_add(particle_hessians, tri_b, collision_hessian_1)
1680
-
1681
- # tri_c
1682
- if particle_colors[tri_c] == current_color:
1683
- wp.atomic_add(particle_forces, tri_c, collision_force_2)
1684
- wp.atomic_add(particle_hessians, tri_c, collision_hessian_2)
1685
-
1686
- particle_body_contact_count = min(contact_max, contact_count[0])
1687
-
1688
- if t_id < particle_body_contact_count:
1689
- particle_idx = soft_contact_particle[t_id]
1690
-
1691
- if particle_colors[particle_idx] == current_color:
1692
- body_contact_force, body_contact_hessian = evaluate_body_particle_contact(
1693
- particle_idx,
1694
- pos[particle_idx],
1695
- pos_prev[particle_idx],
1696
- t_id,
1697
- soft_contact_ke,
1698
- soft_contact_kd,
1699
- friction_mu,
1700
- friction_epsilon,
1701
- particle_radius,
1702
- shape_materials,
1703
- shape_body,
1704
- body_q,
1705
- body_qd,
1706
- body_com,
1707
- contact_shape,
1708
- contact_body_pos,
1709
- contact_body_vel,
1710
- contact_normal,
1711
- dt,
1712
- )
1713
- wp.atomic_add(particle_forces, particle_idx, body_contact_force)
1714
- wp.atomic_add(particle_hessians, particle_idx, body_contact_hessian)
1715
-
1716
-
1717
- @wp.kernel
1718
- def VBD_accumulate_contact_force_and_hessian_no_self_contact(
1719
- # inputs
1720
- dt: float,
1721
- current_color: int,
1722
- pos_prev: wp.array(dtype=wp.vec3),
1723
- pos: wp.array(dtype=wp.vec3),
1724
- particle_colors: wp.array(dtype=int),
1725
- # body-particle contact
1726
- soft_contact_ke: float,
1727
- soft_contact_kd: float,
1728
- friction_mu: float,
1729
- friction_epsilon: float,
1730
- particle_radius: wp.array(dtype=float),
1731
- soft_contact_particle: wp.array(dtype=int),
1732
- contact_count: wp.array(dtype=int),
1733
- contact_max: int,
1734
- shape_materials: ModelShapeMaterials,
1735
- shape_body: wp.array(dtype=int),
1736
- body_q: wp.array(dtype=wp.transform),
1737
- body_qd: wp.array(dtype=wp.spatial_vector),
1738
- body_com: wp.array(dtype=wp.vec3),
1739
- contact_shape: wp.array(dtype=int),
1740
- contact_body_pos: wp.array(dtype=wp.vec3),
1741
- contact_body_vel: wp.array(dtype=wp.vec3),
1742
- contact_normal: wp.array(dtype=wp.vec3),
1743
- # outputs: particle force and hessian
1744
- particle_forces: wp.array(dtype=wp.vec3),
1745
- particle_hessians: wp.array(dtype=wp.mat33),
1746
- ):
1747
- t_id = wp.tid()
1748
-
1749
- particle_body_contact_count = min(contact_max, contact_count[0])
1750
-
1751
- if t_id < particle_body_contact_count:
1752
- particle_idx = soft_contact_particle[t_id]
1753
-
1754
- if particle_colors[particle_idx] == current_color:
1755
- body_contact_force, body_contact_hessian = evaluate_body_particle_contact(
1756
- particle_idx,
1757
- pos[particle_idx],
1758
- pos_prev[particle_idx],
1759
- t_id,
1760
- soft_contact_ke,
1761
- soft_contact_kd,
1762
- friction_mu,
1763
- friction_epsilon,
1764
- particle_radius,
1765
- shape_materials,
1766
- shape_body,
1767
- body_q,
1768
- body_qd,
1769
- body_com,
1770
- contact_shape,
1771
- contact_body_pos,
1772
- contact_body_vel,
1773
- contact_normal,
1774
- dt,
1775
- )
1776
- wp.atomic_add(particle_forces, particle_idx, body_contact_force)
1777
- wp.atomic_add(particle_hessians, particle_idx, body_contact_hessian)
1778
-
1779
-
1780
- @wp.kernel
1781
- def VBD_solve_trimesh_with_self_contact_penetration_free(
1782
- dt: float,
1783
- particle_ids_in_color: wp.array(dtype=wp.int32),
1784
- pos_prev: wp.array(dtype=wp.vec3),
1785
- pos: wp.array(dtype=wp.vec3),
1786
- vel: wp.array(dtype=wp.vec3),
1787
- mass: wp.array(dtype=float),
1788
- inertia: wp.array(dtype=wp.vec3),
1789
- particle_flags: wp.array(dtype=wp.uint32),
1790
- tri_indices: wp.array(dtype=wp.int32, ndim=2),
1791
- tri_poses: wp.array(dtype=wp.mat22),
1792
- tri_materials: wp.array(dtype=float, ndim=2),
1793
- tri_areas: wp.array(dtype=float),
1794
- edge_indices: wp.array(dtype=wp.int32, ndim=2),
1795
- edge_rest_angles: wp.array(dtype=float),
1796
- edge_rest_length: wp.array(dtype=float),
1797
- edge_bending_properties: wp.array(dtype=float, ndim=2),
1798
- adjacency: ForceElementAdjacencyInfo,
1799
- particle_forces: wp.array(dtype=wp.vec3),
1800
- particle_hessians: wp.array(dtype=wp.mat33),
1801
- pos_prev_collision_detection: wp.array(dtype=wp.vec3),
1802
- particle_conservative_bounds: wp.array(dtype=float),
1803
- # ground-particle contact
1804
- has_ground: bool,
1805
- ground: wp.array(dtype=float),
1806
- soft_contact_ke: float,
1807
- soft_contact_kd: float,
1808
- friction_mu: float,
1809
- friction_epsilon: float,
1810
- particle_radius: wp.array(dtype=float),
1811
- # output
1812
- pos_new: wp.array(dtype=wp.vec3),
1813
- ):
1814
- t_id = wp.tid()
1815
-
1816
- particle_index = particle_ids_in_color[t_id]
1817
- particle_pos = pos[particle_index]
1818
- particle_prev_pos = pos_prev[particle_index]
1819
-
1820
- if not particle_flags[particle_index] & PARTICLE_FLAG_ACTIVE:
1821
- pos_new[particle_index] = particle_pos
1822
- return
1823
-
1824
- dt_sqr_reciprocal = 1.0 / (dt * dt)
1825
-
1826
- # inertia force and hessian
1827
- f = mass[particle_index] * (inertia[particle_index] - pos[particle_index]) * (dt_sqr_reciprocal)
1828
- h = particle_hessians[particle_index] + mass[particle_index] * dt_sqr_reciprocal * wp.identity(n=3, dtype=float)
1829
-
1830
- # fmt: off
1831
- if wp.static("inertia_force_hessian" in VBD_DEBUG_PRINTING_OPTIONS):
1832
- wp.printf(
1833
- "particle: %d after accumulate inertia\nforce:\n %f %f %f, \nhessian:, \n%f %f %f, \n%f %f %f, \n%f %f %f\n",
1834
- particle_index,
1835
- 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],
1836
- )
1837
-
1838
- # elastic force and hessian
1839
- for i_adj_tri in range(get_vertex_num_adjacent_faces(adjacency, particle_index)):
1840
- tri_index, vertex_order = get_vertex_adjacent_face_id_order(adjacency, particle_index, i_adj_tri)
1841
-
1842
- # fmt: off
1843
- if wp.static("connectivity" in VBD_DEBUG_PRINTING_OPTIONS):
1844
- wp.printf(
1845
- "particle: %d | num_adj_faces: %d | ",
1846
- particle_index,
1847
- get_vertex_num_adjacent_faces(particle_index, adjacency),
1848
- )
1849
- wp.printf("i_face: %d | face id: %d | v_order: %d | ", i_adj_tri, tri_index, vertex_order)
1850
- wp.printf(
1851
- "face: %d %d %d\n",
1852
- tri_indices[tri_index, 0],
1853
- tri_indices[tri_index, 1],
1854
- tri_indices[tri_index, 2],
1855
- )
1856
- # fmt: on
1857
-
1858
- f_tri, h_tri = evaluate_stvk_force_hessian(
1859
- tri_index,
1860
- vertex_order,
1861
- pos,
1862
- tri_indices,
1863
- tri_poses[tri_index],
1864
- tri_areas[tri_index],
1865
- tri_materials[tri_index, 0],
1866
- tri_materials[tri_index, 1],
1867
- tri_materials[tri_index, 2],
1868
- )
1869
- # compute damping
1870
- k_d = tri_materials[tri_index, 2]
1871
- h_d = h_tri * (k_d / dt)
1872
-
1873
- f_d = h_d * (particle_prev_pos - particle_pos)
1874
-
1875
- f = f + f_tri + f_d
1876
- h = h + h_tri + h_d
1877
-
1878
-
1879
- for i_adj_edge in range(get_vertex_num_adjacent_edges(adjacency, particle_index)):
1880
- nei_edge_index, vertex_order_on_edge = get_vertex_adjacent_edge_id_order(adjacency, particle_index, i_adj_edge)
1881
- # vertex is on the edge; otherwise it only effects the bending energy n
1882
- if edge_bending_properties[nei_edge_index, 0] != 0:
1883
- f_edge, h_edge = evaluate_dihedral_angle_based_bending_force_hessian(
1884
- nei_edge_index, vertex_order_on_edge, pos, pos_prev, edge_indices, edge_rest_angles, edge_rest_length,
1885
- edge_bending_properties[nei_edge_index, 0], edge_bending_properties[nei_edge_index, 1], dt
1886
- )
1887
-
1888
- f = f + f_edge
1889
- h = h + h_edge
1890
-
1891
- if has_ground:
1892
- ground_normal = wp.vec3(ground[0], ground[1], ground[2])
1893
- ground_level = ground[3]
1894
- ground_contact_force, ground_contact_hessian = evaluate_ground_contact_force_hessian(
1895
- particle_pos,
1896
- particle_prev_pos,
1897
- particle_radius[particle_index],
1898
- ground_normal,
1899
- ground_level,
1900
- soft_contact_ke,
1901
- soft_contact_kd,
1902
- friction_mu,
1903
- friction_epsilon,
1904
- dt,
1905
- )
1906
-
1907
- f = f + ground_contact_force
1908
- h = h + ground_contact_hessian
1909
-
1910
- # fmt: off
1911
- if wp.static("overall_force_hessian" in VBD_DEBUG_PRINTING_OPTIONS):
1912
- wp.printf(
1913
- "vertex: %d final\noverall force:\n %f %f %f, \noverall hessian:, \n%f %f %f, \n%f %f %f, \n%f %f %f\n",
1914
- particle_index,
1915
- 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],
1916
- )
1917
-
1918
- # # fmt: on
1919
- h = h + particle_hessians[particle_index]
1920
- f = f + particle_forces[particle_index]
1921
- if abs(wp.determinant(h)) > 1e-5:
1922
-
1923
- h_inv = wp.inverse(h)
1924
- particle_pos_new = pos[particle_index] + h_inv * f
1925
-
1926
- pos_new[particle_index] = apply_conservative_bound_truncation(
1927
- particle_index, particle_pos_new, pos_prev_collision_detection, particle_conservative_bounds
1928
- )
1929
-
1930
-
1931
- class VBDIntegrator(Integrator):
1932
- """An implicit integrator using Vertex Block Descent (VBD) for cloth simulation.
1933
-
1934
- References:
1935
- - Anka He Chen, Ziheng Liu, Yin Yang, and Cem Yuksel. 2024. Vertex Block Descent. ACM Trans. Graph. 43, 4, Article 116 (July 2024), 16 pages. https://doi.org/10.1145/3658179
1936
-
1937
- Note that VBDIntegrator's constructor requires a :class:`Model` object as input, so that it can do some precomputation and preallocate the space.
1938
- After construction, you must provide the same :class:`Model` object that you used that was used during construction.
1939
- Currently, you must manually provide particle coloring and assign it to `model.particle_color_groups` to make VBD work.
1940
-
1941
- VBDIntegrator.simulate accepts three arguments: class:`Model`, :class:`State`, and :class:`Control` (optional) objects, this time-integrator
1942
- may be used to advance the simulation state forward in time.
1943
-
1944
- Example
1945
- -------
1946
-
1947
- .. code-block:: python
1948
-
1949
- model.particle_color_groups = # load or generate particle coloring
1950
- integrator = wp.VBDIntegrator(model)
1951
-
1952
- # simulation loop
1953
- for i in range(100):
1954
- state = integrator.simulate(model, state_in, state_out, dt, control)
1955
-
1956
- """
1957
-
1958
- def __init__(
1959
- self,
1960
- model: Model,
1961
- iterations=10,
1962
- handle_self_contact=False,
1963
- penetration_free_conservative_bound_relaxation=0.42,
1964
- friction_epsilon=1e-2,
1965
- body_particle_contact_buffer_pre_alloc=4,
1966
- vertex_collision_buffer_pre_alloc=32,
1967
- edge_collision_buffer_pre_alloc=64,
1968
- triangle_collision_buffer_pre_alloc=32,
1969
- edge_edge_parallel_epsilon=1e-5,
1970
- ):
1971
- self.device = model.device
1972
- self.model = model
1973
- self.iterations = iterations
1974
-
1975
- # add new attributes for VBD solve
1976
- self.particle_q_prev = wp.zeros_like(model.particle_q, device=self.device)
1977
- self.inertia = wp.zeros_like(model.particle_q, device=self.device)
1978
-
1979
- self.adjacency = self.compute_force_element_adjacency(model).to(self.device)
1980
-
1981
- self.body_particle_contact_count = wp.zeros((model.particle_count,), dtype=wp.int32, device=self.device)
1982
-
1983
- self.handle_self_contact = handle_self_contact
1984
-
1985
- if handle_self_contact:
1986
- if self.model.soft_contact_margin < self.model.soft_contact_radius:
1987
- raise ValueError(
1988
- "model.soft_contact_margin is smaller than self.model.soft_contact_radius, this will result in missing contacts and cause instability. \n"
1989
- "It is advisable to make model.soft_contact_margin 1.5~2 times larger than self.model.soft_contact_radius."
1990
- )
1991
-
1992
- self.conservative_bound_relaxation = penetration_free_conservative_bound_relaxation
1993
- self.pos_prev_collision_detection = wp.zeros_like(model.particle_q, device=self.device)
1994
- self.particle_conservative_bounds = wp.full((model.particle_count,), dtype=float, device=self.device)
1995
-
1996
- self.trimesh_collision_detector = TriMeshCollisionDetector(
1997
- self.model,
1998
- vertex_collision_buffer_pre_alloc=vertex_collision_buffer_pre_alloc,
1999
- edge_collision_buffer_pre_alloc=edge_collision_buffer_pre_alloc,
2000
- triangle_collision_buffer_pre_alloc=triangle_collision_buffer_pre_alloc,
2001
- edge_edge_parallel_epsilon=edge_edge_parallel_epsilon,
2002
- )
2003
-
2004
- self.trimesh_collision_info = wp.array(
2005
- [self.trimesh_collision_detector.collision_info], dtype=TriMeshCollisionInfo, device=self.device
2006
- )
2007
-
2008
- self.collision_evaluation_kernel_launch_size = max(
2009
- self.trimesh_collision_detector.vertex_colliding_triangles.shape[0] // 2,
2010
- self.trimesh_collision_detector.edge_colliding_edges.shape[0] // 2,
2011
- self.model.soft_contact_max,
2012
- )
2013
- else:
2014
- self.collision_evaluation_kernel_launch_size = self.model.soft_contact_max
2015
-
2016
- # spaces for particle force and hessian
2017
- self.particle_forces = wp.zeros(self.model.particle_count, dtype=wp.vec3, device=self.device)
2018
- self.particle_hessians = wp.zeros(self.model.particle_count, dtype=wp.mat33, device=self.device)
2019
-
2020
- self.friction_epsilon = friction_epsilon
2021
-
2022
- if len(self.model.particle_color_groups) == 0:
2023
- raise ValueError(
2024
- "model.particle_color_groups is empty! When using the VBDIntegrator you must call ModelBuilder.color() "
2025
- "or ModelBuilder.set_coloring() before calling ModelBuilder.finalize()."
2026
- )
2027
-
2028
- # tests
2029
- # wp.launch(kernel=_test_compute_force_element_adjacency,
2030
- # inputs=[self.adjacency, model.edge_indices, model.tri_indices],
2031
- # dim=1, device=self.device)
2032
-
2033
- def compute_force_element_adjacency(self, model):
2034
- adjacency = ForceElementAdjacencyInfo()
2035
- edges_array = model.edge_indices.to("cpu")
2036
-
2037
- if edges_array.size:
2038
- # build vertex-edge adjacency data
2039
- num_vertex_adjacent_edges = wp.zeros(shape=(self.model.particle_count,), dtype=wp.int32, device="cpu")
2040
-
2041
- wp.launch(
2042
- kernel=self.count_num_adjacent_edges,
2043
- inputs=[edges_array, num_vertex_adjacent_edges],
2044
- dim=1,
2045
- device="cpu",
2046
- )
2047
-
2048
- num_vertex_adjacent_edges = num_vertex_adjacent_edges.numpy()
2049
- vertex_adjacent_edges_offsets = np.empty(shape=(self.model.particle_count + 1,), dtype=wp.int32)
2050
- vertex_adjacent_edges_offsets[1:] = np.cumsum(2 * num_vertex_adjacent_edges)[:]
2051
- vertex_adjacent_edges_offsets[0] = 0
2052
- adjacency.v_adj_edges_offsets = wp.array(vertex_adjacent_edges_offsets, dtype=wp.int32, device="cpu")
2053
-
2054
- # temporal variables to record how much adjacent edges has been filled to each vertex
2055
- vertex_adjacent_edges_fill_count = wp.zeros(
2056
- shape=(self.model.particle_count,), dtype=wp.int32, device="cpu"
2057
- )
2058
-
2059
- edge_adjacency_array_size = 2 * num_vertex_adjacent_edges.sum()
2060
- # vertex order: o0: 0, o1: 1, v0: 2, v1: 3,
2061
- adjacency.v_adj_edges = wp.empty(shape=(edge_adjacency_array_size,), dtype=wp.int32, device="cpu")
2062
-
2063
- wp.launch(
2064
- kernel=self.fill_adjacent_edges,
2065
- inputs=[
2066
- edges_array,
2067
- adjacency.v_adj_edges_offsets,
2068
- vertex_adjacent_edges_fill_count,
2069
- adjacency.v_adj_edges,
2070
- ],
2071
- dim=1,
2072
- device="cpu",
2073
- )
2074
- else:
2075
- adjacency.v_adj_edges_offsets = wp.empty(shape=(0,), dtype=wp.int32, device="cpu")
2076
- adjacency.v_adj_edges = wp.empty(shape=(0,), dtype=wp.int32, device="cpu")
2077
-
2078
- # compute adjacent triangles
2079
-
2080
- # count number of adjacent faces for each vertex
2081
- face_indices = model.tri_indices.to("cpu")
2082
- num_vertex_adjacent_faces = wp.zeros(shape=(self.model.particle_count,), dtype=wp.int32, device="cpu")
2083
- wp.launch(
2084
- kernel=self.count_num_adjacent_faces, inputs=[face_indices, num_vertex_adjacent_faces], dim=1, device="cpu"
2085
- )
2086
-
2087
- # preallocate memory based on counting results
2088
- num_vertex_adjacent_faces = num_vertex_adjacent_faces.numpy()
2089
- vertex_adjacent_faces_offsets = np.empty(shape=(self.model.particle_count + 1,), dtype=wp.int32)
2090
- vertex_adjacent_faces_offsets[1:] = np.cumsum(2 * num_vertex_adjacent_faces)[:]
2091
- vertex_adjacent_faces_offsets[0] = 0
2092
- adjacency.v_adj_faces_offsets = wp.array(vertex_adjacent_faces_offsets, dtype=wp.int32, device="cpu")
2093
-
2094
- vertex_adjacent_faces_fill_count = wp.zeros(shape=(self.model.particle_count,), dtype=wp.int32, device="cpu")
2095
-
2096
- face_adjacency_array_size = 2 * num_vertex_adjacent_faces.sum()
2097
- # (face, vertex_order) * num_adj_faces * num_particles
2098
- # vertex order: v0: 0, v1: 1, o0: 2, v2: 3
2099
- adjacency.v_adj_faces = wp.empty(shape=(face_adjacency_array_size,), dtype=wp.int32, device="cpu")
2100
-
2101
- wp.launch(
2102
- kernel=self.fill_adjacent_faces,
2103
- inputs=[
2104
- face_indices,
2105
- adjacency.v_adj_faces_offsets,
2106
- vertex_adjacent_faces_fill_count,
2107
- adjacency.v_adj_faces,
2108
- ],
2109
- dim=1,
2110
- device="cpu",
2111
- )
2112
-
2113
- return adjacency
2114
-
2115
- def simulate(self, model: Model, state_in: State, state_out: State, dt: float, control: Control = None):
2116
- if model is not self.model:
2117
- raise ValueError("model must be the one used to initialize VBDIntegrator")
2118
-
2119
- if self.handle_self_contact:
2120
- self.simulate_one_step_with_collisions_penetration_free(model, state_in, state_out, dt, control)
2121
- else:
2122
- self.simulate_one_step_no_self_contact(model, state_in, state_out, dt, control)
2123
-
2124
- def simulate_one_step_no_self_contact(
2125
- self, model: Model, state_in: State, state_out: State, dt: float, control: Control = None
2126
- ):
2127
- wp.launch(
2128
- kernel=forward_step,
2129
- inputs=[
2130
- dt,
2131
- model.gravity,
2132
- self.particle_q_prev,
2133
- state_in.particle_q,
2134
- state_in.particle_qd,
2135
- self.model.particle_inv_mass,
2136
- state_in.particle_f,
2137
- self.model.particle_flags,
2138
- self.inertia,
2139
- ],
2140
- dim=self.model.particle_count,
2141
- device=self.device,
2142
- )
2143
-
2144
- for _iter in range(self.iterations):
2145
- self.particle_forces.zero_()
2146
- self.particle_hessians.zero_()
2147
- for color in range(len(self.model.particle_color_groups)):
2148
- wp.launch(
2149
- kernel=VBD_accumulate_contact_force_and_hessian_no_self_contact,
2150
- dim=self.collision_evaluation_kernel_launch_size,
2151
- inputs=[
2152
- dt,
2153
- color,
2154
- self.particle_q_prev,
2155
- state_in.particle_q,
2156
- self.model.particle_colors,
2157
- # body-particle contact
2158
- self.model.soft_contact_ke,
2159
- self.model.soft_contact_kd,
2160
- self.model.soft_contact_mu,
2161
- self.friction_epsilon,
2162
- self.model.particle_radius,
2163
- self.model.soft_contact_particle,
2164
- self.model.soft_contact_count,
2165
- self.model.soft_contact_max,
2166
- self.model.shape_materials,
2167
- self.model.shape_body,
2168
- self.model.body_q,
2169
- self.model.body_qd,
2170
- self.model.body_com,
2171
- self.model.soft_contact_shape,
2172
- self.model.soft_contact_body_pos,
2173
- self.model.soft_contact_body_vel,
2174
- self.model.soft_contact_normal,
2175
- ],
2176
- outputs=[self.particle_forces, self.particle_hessians],
2177
- device=self.device,
2178
- )
2179
-
2180
- wp.launch(
2181
- kernel=VBD_solve_trimesh_no_self_contact,
2182
- inputs=[
2183
- dt,
2184
- self.model.particle_color_groups[color],
2185
- self.particle_q_prev,
2186
- state_in.particle_q,
2187
- state_in.particle_qd,
2188
- self.model.particle_mass,
2189
- self.inertia,
2190
- self.model.particle_flags,
2191
- self.model.tri_indices,
2192
- self.model.tri_poses,
2193
- self.model.tri_materials,
2194
- self.model.tri_areas,
2195
- self.model.edge_indices,
2196
- self.model.edge_rest_angle,
2197
- self.model.edge_rest_length,
2198
- self.model.edge_bending_properties,
2199
- self.adjacency,
2200
- self.particle_forces,
2201
- self.particle_hessians,
2202
- self.model.soft_contact_ke,
2203
- self.model.soft_contact_kd,
2204
- self.model.soft_contact_mu,
2205
- self.friction_epsilon,
2206
- # ground-particle contact
2207
- self.model.ground,
2208
- self.model.ground_plane,
2209
- self.model.particle_radius,
2210
- ],
2211
- outputs=[
2212
- state_out.particle_q,
2213
- ],
2214
- dim=self.model.particle_color_groups[color].size,
2215
- device=self.device,
2216
- )
2217
-
2218
- wp.launch(
2219
- kernel=VBD_copy_particle_positions_back,
2220
- inputs=[self.model.particle_color_groups[color], state_in.particle_q, state_out.particle_q],
2221
- dim=self.model.particle_color_groups[color].size,
2222
- device=self.device,
2223
- )
2224
-
2225
- wp.launch(
2226
- kernel=update_velocity,
2227
- inputs=[dt, self.particle_q_prev, state_out.particle_q, state_out.particle_qd],
2228
- dim=self.model.particle_count,
2229
- device=self.device,
2230
- )
2231
-
2232
- def simulate_one_step_with_collisions_penetration_free(
2233
- self, model: Model, state_in: State, state_out: State, dt: float, control: Control = None
2234
- ):
2235
- # collision detection before initialization to compute conservative bounds for initialization
2236
- self.collision_detection_penetration_free(state_in, dt)
2237
-
2238
- wp.launch(
2239
- kernel=forward_step_penetration_free,
2240
- inputs=[
2241
- dt,
2242
- model.gravity,
2243
- self.particle_q_prev,
2244
- state_in.particle_q,
2245
- state_in.particle_qd,
2246
- self.model.particle_inv_mass,
2247
- state_in.particle_f,
2248
- self.model.particle_flags,
2249
- self.pos_prev_collision_detection,
2250
- self.particle_conservative_bounds,
2251
- self.inertia,
2252
- ],
2253
- dim=self.model.particle_count,
2254
- device=self.device,
2255
- )
2256
-
2257
- # after initialization, we do another collision detection to update the bounds
2258
- self.collision_detection_penetration_free(state_in, dt)
2259
-
2260
- for _iter in range(self.iterations):
2261
- self.particle_forces.zero_()
2262
- self.particle_hessians.zero_()
2263
-
2264
- for color in range(len(self.model.particle_color_groups)):
2265
- wp.launch(
2266
- kernel=VBD_accumulate_contact_force_and_hessian,
2267
- dim=self.collision_evaluation_kernel_launch_size,
2268
- inputs=[
2269
- dt,
2270
- color,
2271
- self.particle_q_prev,
2272
- state_in.particle_q,
2273
- self.model.particle_colors,
2274
- self.model.tri_indices,
2275
- self.model.edge_indices,
2276
- # self-contact
2277
- self.trimesh_collision_info,
2278
- self.model.soft_contact_radius,
2279
- self.model.soft_contact_ke,
2280
- self.model.soft_contact_kd,
2281
- self.model.soft_contact_mu,
2282
- self.friction_epsilon,
2283
- self.trimesh_collision_detector.edge_edge_parallel_epsilon,
2284
- # body-particle contact
2285
- self.model.particle_radius,
2286
- self.model.soft_contact_particle,
2287
- self.model.soft_contact_count,
2288
- self.model.soft_contact_max,
2289
- self.model.shape_materials,
2290
- self.model.shape_body,
2291
- self.model.body_q,
2292
- self.model.body_qd,
2293
- self.model.body_com,
2294
- self.model.soft_contact_shape,
2295
- self.model.soft_contact_body_pos,
2296
- self.model.soft_contact_body_vel,
2297
- self.model.soft_contact_normal,
2298
- ],
2299
- outputs=[self.particle_forces, self.particle_hessians],
2300
- device=self.device,
2301
- )
2302
-
2303
- wp.launch(
2304
- kernel=VBD_solve_trimesh_with_self_contact_penetration_free,
2305
- dim=self.model.particle_color_groups[color].shape[0],
2306
- inputs=[
2307
- dt,
2308
- self.model.particle_color_groups[color],
2309
- self.particle_q_prev,
2310
- state_in.particle_q,
2311
- state_in.particle_qd,
2312
- self.model.particle_mass,
2313
- self.inertia,
2314
- self.model.particle_flags,
2315
- self.model.tri_indices,
2316
- self.model.tri_poses,
2317
- self.model.tri_materials,
2318
- self.model.tri_areas,
2319
- self.model.edge_indices,
2320
- self.model.edge_rest_angle,
2321
- self.model.edge_rest_length,
2322
- self.model.edge_bending_properties,
2323
- self.adjacency,
2324
- self.particle_forces,
2325
- self.particle_hessians,
2326
- self.pos_prev_collision_detection,
2327
- self.particle_conservative_bounds,
2328
- self.model.ground,
2329
- self.model.ground_plane,
2330
- self.model.soft_contact_ke,
2331
- self.model.soft_contact_kd,
2332
- self.model.soft_contact_mu,
2333
- self.friction_epsilon,
2334
- self.model.particle_radius,
2335
- ],
2336
- outputs=[
2337
- state_out.particle_q,
2338
- ],
2339
- device=self.device,
2340
- )
2341
-
2342
- wp.launch(
2343
- kernel=VBD_copy_particle_positions_back,
2344
- inputs=[self.model.particle_color_groups[color], state_in.particle_q, state_out.particle_q],
2345
- dim=self.model.particle_color_groups[color].size,
2346
- device=self.device,
2347
- )
2348
-
2349
- wp.launch(
2350
- kernel=update_velocity,
2351
- inputs=[dt, self.particle_q_prev, state_out.particle_q, state_out.particle_qd],
2352
- dim=self.model.particle_count,
2353
- device=self.device,
2354
- )
2355
-
2356
- def collision_detection_penetration_free(self, current_state, dt):
2357
- self.trimesh_collision_detector.refit(current_state.particle_q)
2358
- self.trimesh_collision_detector.vertex_triangle_collision_detection(self.model.soft_contact_margin)
2359
- self.trimesh_collision_detector.edge_edge_collision_detection(self.model.soft_contact_margin)
2360
-
2361
- self.pos_prev_collision_detection.assign(current_state.particle_q)
2362
- wp.launch(
2363
- kernel=compute_particle_conservative_bound,
2364
- inputs=[
2365
- self.conservative_bound_relaxation,
2366
- self.model.soft_contact_margin,
2367
- self.adjacency,
2368
- self.trimesh_collision_detector.collision_info,
2369
- ],
2370
- outputs=[
2371
- self.particle_conservative_bounds,
2372
- ],
2373
- dim=self.model.particle_count,
2374
- device=self.device,
2375
- )
2376
-
2377
- def rebuild_bvh(self, state: State):
2378
- """This function will rebuild the BVHs used for detecting self-contacts using the input `state`.
2379
-
2380
- When the simulated object deforms significantly, simply refitting the BVH can lead to deterioration of the BVH's
2381
- quality. In these cases, rebuilding the entire tree is necessary to achieve better querying efficiency.
2382
-
2383
- Args:
2384
- state (wp.sim.State): The state whose particle positions (:attr:`State.particle_q`) will be used for rebuilding the BVHs.
2385
- """
2386
- self.trimesh_collision_detector.rebuild(state.particle_q)
2387
-
2388
- @wp.kernel
2389
- def count_num_adjacent_edges(
2390
- edges_array: wp.array(dtype=wp.int32, ndim=2), num_vertex_adjacent_edges: wp.array(dtype=wp.int32)
2391
- ):
2392
- for edge_id in range(edges_array.shape[0]):
2393
- o0 = edges_array[edge_id, 0]
2394
- o1 = edges_array[edge_id, 1]
2395
-
2396
- v0 = edges_array[edge_id, 2]
2397
- v1 = edges_array[edge_id, 3]
2398
-
2399
- num_vertex_adjacent_edges[v0] = num_vertex_adjacent_edges[v0] + 1
2400
- num_vertex_adjacent_edges[v1] = num_vertex_adjacent_edges[v1] + 1
2401
-
2402
- if o0 != -1:
2403
- num_vertex_adjacent_edges[o0] = num_vertex_adjacent_edges[o0] + 1
2404
- if o1 != -1:
2405
- num_vertex_adjacent_edges[o1] = num_vertex_adjacent_edges[o1] + 1
2406
-
2407
- @wp.kernel
2408
- def fill_adjacent_edges(
2409
- edges_array: wp.array(dtype=wp.int32, ndim=2),
2410
- vertex_adjacent_edges_offsets: wp.array(dtype=wp.int32),
2411
- vertex_adjacent_edges_fill_count: wp.array(dtype=wp.int32),
2412
- vertex_adjacent_edges: wp.array(dtype=wp.int32),
2413
- ):
2414
- for edge_id in range(edges_array.shape[0]):
2415
- v0 = edges_array[edge_id, 2]
2416
- v1 = edges_array[edge_id, 3]
2417
-
2418
- fill_count_v0 = vertex_adjacent_edges_fill_count[v0]
2419
- buffer_offset_v0 = vertex_adjacent_edges_offsets[v0]
2420
- vertex_adjacent_edges[buffer_offset_v0 + fill_count_v0 * 2] = edge_id
2421
- vertex_adjacent_edges[buffer_offset_v0 + fill_count_v0 * 2 + 1] = 2
2422
- vertex_adjacent_edges_fill_count[v0] = fill_count_v0 + 1
2423
-
2424
- fill_count_v1 = vertex_adjacent_edges_fill_count[v1]
2425
- buffer_offset_v1 = vertex_adjacent_edges_offsets[v1]
2426
- vertex_adjacent_edges[buffer_offset_v1 + fill_count_v1 * 2] = edge_id
2427
- vertex_adjacent_edges[buffer_offset_v1 + fill_count_v1 * 2 + 1] = 3
2428
- vertex_adjacent_edges_fill_count[v1] = fill_count_v1 + 1
2429
-
2430
- o0 = edges_array[edge_id, 0]
2431
- if o0 != -1:
2432
- fill_count_o0 = vertex_adjacent_edges_fill_count[o0]
2433
- buffer_offset_o0 = vertex_adjacent_edges_offsets[o0]
2434
- vertex_adjacent_edges[buffer_offset_o0 + fill_count_o0 * 2] = edge_id
2435
- vertex_adjacent_edges[buffer_offset_o0 + fill_count_o0 * 2 + 1] = 0
2436
- vertex_adjacent_edges_fill_count[o0] = fill_count_o0 + 1
2437
-
2438
- o1 = edges_array[edge_id, 1]
2439
- if o1 != -1:
2440
- fill_count_o1 = vertex_adjacent_edges_fill_count[o1]
2441
- buffer_offset_o1 = vertex_adjacent_edges_offsets[o1]
2442
- vertex_adjacent_edges[buffer_offset_o1 + fill_count_o1 * 2] = edge_id
2443
- vertex_adjacent_edges[buffer_offset_o1 + fill_count_o1 * 2 + 1] = 1
2444
- vertex_adjacent_edges_fill_count[o1] = fill_count_o1 + 1
2445
-
2446
- @wp.kernel
2447
- def count_num_adjacent_faces(
2448
- face_indices: wp.array(dtype=wp.int32, ndim=2), num_vertex_adjacent_faces: wp.array(dtype=wp.int32)
2449
- ):
2450
- for face in range(face_indices.shape[0]):
2451
- v0 = face_indices[face, 0]
2452
- v1 = face_indices[face, 1]
2453
- v2 = face_indices[face, 2]
2454
-
2455
- num_vertex_adjacent_faces[v0] = num_vertex_adjacent_faces[v0] + 1
2456
- num_vertex_adjacent_faces[v1] = num_vertex_adjacent_faces[v1] + 1
2457
- num_vertex_adjacent_faces[v2] = num_vertex_adjacent_faces[v2] + 1
2458
-
2459
- @wp.kernel
2460
- def fill_adjacent_faces(
2461
- face_indices: wp.array(dtype=wp.int32, ndim=2),
2462
- vertex_adjacent_faces_offsets: wp.array(dtype=wp.int32),
2463
- vertex_adjacent_faces_fill_count: wp.array(dtype=wp.int32),
2464
- vertex_adjacent_faces: wp.array(dtype=wp.int32),
2465
- ):
2466
- for face in range(face_indices.shape[0]):
2467
- v0 = face_indices[face, 0]
2468
- v1 = face_indices[face, 1]
2469
- v2 = face_indices[face, 2]
2470
-
2471
- fill_count_v0 = vertex_adjacent_faces_fill_count[v0]
2472
- buffer_offset_v0 = vertex_adjacent_faces_offsets[v0]
2473
- vertex_adjacent_faces[buffer_offset_v0 + fill_count_v0 * 2] = face
2474
- vertex_adjacent_faces[buffer_offset_v0 + fill_count_v0 * 2 + 1] = 0
2475
- vertex_adjacent_faces_fill_count[v0] = fill_count_v0 + 1
2476
-
2477
- fill_count_v1 = vertex_adjacent_faces_fill_count[v1]
2478
- buffer_offset_v1 = vertex_adjacent_faces_offsets[v1]
2479
- vertex_adjacent_faces[buffer_offset_v1 + fill_count_v1 * 2] = face
2480
- vertex_adjacent_faces[buffer_offset_v1 + fill_count_v1 * 2 + 1] = 1
2481
- vertex_adjacent_faces_fill_count[v1] = fill_count_v1 + 1
2482
-
2483
- fill_count_v2 = vertex_adjacent_faces_fill_count[v2]
2484
- buffer_offset_v2 = vertex_adjacent_faces_offsets[v2]
2485
- vertex_adjacent_faces[buffer_offset_v2 + fill_count_v2 * 2] = face
2486
- vertex_adjacent_faces[buffer_offset_v2 + fill_count_v2 * 2 + 1] = 2
2487
- vertex_adjacent_faces_fill_count[v2] = fill_count_v2 + 1