warp-lang 1.7.0__py3-none-manylinux_2_34_aarch64.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 (429) hide show
  1. warp/__init__.py +139 -0
  2. warp/__init__.pyi +1 -0
  3. warp/autograd.py +1142 -0
  4. warp/bin/warp-clang.so +0 -0
  5. warp/bin/warp.so +0 -0
  6. warp/build.py +557 -0
  7. warp/build_dll.py +405 -0
  8. warp/builtins.py +6855 -0
  9. warp/codegen.py +3969 -0
  10. warp/config.py +158 -0
  11. warp/constants.py +57 -0
  12. warp/context.py +6812 -0
  13. warp/dlpack.py +462 -0
  14. warp/examples/__init__.py +24 -0
  15. warp/examples/assets/bear.usd +0 -0
  16. warp/examples/assets/bunny.usd +0 -0
  17. warp/examples/assets/cartpole.urdf +110 -0
  18. warp/examples/assets/crazyflie.usd +0 -0
  19. warp/examples/assets/cube.usd +0 -0
  20. warp/examples/assets/nonuniform.usd +0 -0
  21. warp/examples/assets/nv_ant.xml +92 -0
  22. warp/examples/assets/nv_humanoid.xml +183 -0
  23. warp/examples/assets/nvidia_logo.png +0 -0
  24. warp/examples/assets/pixel.jpg +0 -0
  25. warp/examples/assets/quadruped.urdf +268 -0
  26. warp/examples/assets/rocks.nvdb +0 -0
  27. warp/examples/assets/rocks.usd +0 -0
  28. warp/examples/assets/sphere.usd +0 -0
  29. warp/examples/assets/square_cloth.usd +0 -0
  30. warp/examples/benchmarks/benchmark_api.py +389 -0
  31. warp/examples/benchmarks/benchmark_cloth.py +296 -0
  32. warp/examples/benchmarks/benchmark_cloth_cupy.py +96 -0
  33. warp/examples/benchmarks/benchmark_cloth_jax.py +105 -0
  34. warp/examples/benchmarks/benchmark_cloth_numba.py +161 -0
  35. warp/examples/benchmarks/benchmark_cloth_numpy.py +85 -0
  36. warp/examples/benchmarks/benchmark_cloth_paddle.py +94 -0
  37. warp/examples/benchmarks/benchmark_cloth_pytorch.py +94 -0
  38. warp/examples/benchmarks/benchmark_cloth_taichi.py +120 -0
  39. warp/examples/benchmarks/benchmark_cloth_warp.py +153 -0
  40. warp/examples/benchmarks/benchmark_gemm.py +164 -0
  41. warp/examples/benchmarks/benchmark_interop_paddle.py +166 -0
  42. warp/examples/benchmarks/benchmark_interop_torch.py +166 -0
  43. warp/examples/benchmarks/benchmark_launches.py +301 -0
  44. warp/examples/benchmarks/benchmark_tile_load_store.py +103 -0
  45. warp/examples/browse.py +37 -0
  46. warp/examples/core/example_cupy.py +86 -0
  47. warp/examples/core/example_dem.py +241 -0
  48. warp/examples/core/example_fluid.py +299 -0
  49. warp/examples/core/example_graph_capture.py +150 -0
  50. warp/examples/core/example_marching_cubes.py +194 -0
  51. warp/examples/core/example_mesh.py +180 -0
  52. warp/examples/core/example_mesh_intersect.py +211 -0
  53. warp/examples/core/example_nvdb.py +182 -0
  54. warp/examples/core/example_raycast.py +111 -0
  55. warp/examples/core/example_raymarch.py +205 -0
  56. warp/examples/core/example_render_opengl.py +193 -0
  57. warp/examples/core/example_sample_mesh.py +300 -0
  58. warp/examples/core/example_sph.py +411 -0
  59. warp/examples/core/example_torch.py +211 -0
  60. warp/examples/core/example_wave.py +269 -0
  61. warp/examples/fem/example_adaptive_grid.py +286 -0
  62. warp/examples/fem/example_apic_fluid.py +423 -0
  63. warp/examples/fem/example_burgers.py +261 -0
  64. warp/examples/fem/example_convection_diffusion.py +178 -0
  65. warp/examples/fem/example_convection_diffusion_dg.py +204 -0
  66. warp/examples/fem/example_deformed_geometry.py +172 -0
  67. warp/examples/fem/example_diffusion.py +196 -0
  68. warp/examples/fem/example_diffusion_3d.py +225 -0
  69. warp/examples/fem/example_diffusion_mgpu.py +220 -0
  70. warp/examples/fem/example_distortion_energy.py +228 -0
  71. warp/examples/fem/example_magnetostatics.py +240 -0
  72. warp/examples/fem/example_mixed_elasticity.py +291 -0
  73. warp/examples/fem/example_navier_stokes.py +261 -0
  74. warp/examples/fem/example_nonconforming_contact.py +298 -0
  75. warp/examples/fem/example_stokes.py +213 -0
  76. warp/examples/fem/example_stokes_transfer.py +262 -0
  77. warp/examples/fem/example_streamlines.py +352 -0
  78. warp/examples/fem/utils.py +1000 -0
  79. warp/examples/interop/example_jax_callable.py +116 -0
  80. warp/examples/interop/example_jax_ffi_callback.py +132 -0
  81. warp/examples/interop/example_jax_kernel.py +205 -0
  82. warp/examples/optim/example_bounce.py +266 -0
  83. warp/examples/optim/example_cloth_throw.py +228 -0
  84. warp/examples/optim/example_diffray.py +561 -0
  85. warp/examples/optim/example_drone.py +870 -0
  86. warp/examples/optim/example_fluid_checkpoint.py +497 -0
  87. warp/examples/optim/example_inverse_kinematics.py +182 -0
  88. warp/examples/optim/example_inverse_kinematics_torch.py +191 -0
  89. warp/examples/optim/example_softbody_properties.py +400 -0
  90. warp/examples/optim/example_spring_cage.py +245 -0
  91. warp/examples/optim/example_trajectory.py +227 -0
  92. warp/examples/sim/example_cartpole.py +143 -0
  93. warp/examples/sim/example_cloth.py +225 -0
  94. warp/examples/sim/example_cloth_self_contact.py +322 -0
  95. warp/examples/sim/example_granular.py +130 -0
  96. warp/examples/sim/example_granular_collision_sdf.py +202 -0
  97. warp/examples/sim/example_jacobian_ik.py +244 -0
  98. warp/examples/sim/example_particle_chain.py +124 -0
  99. warp/examples/sim/example_quadruped.py +203 -0
  100. warp/examples/sim/example_rigid_chain.py +203 -0
  101. warp/examples/sim/example_rigid_contact.py +195 -0
  102. warp/examples/sim/example_rigid_force.py +133 -0
  103. warp/examples/sim/example_rigid_gyroscopic.py +115 -0
  104. warp/examples/sim/example_rigid_soft_contact.py +140 -0
  105. warp/examples/sim/example_soft_body.py +196 -0
  106. warp/examples/tile/example_tile_cholesky.py +87 -0
  107. warp/examples/tile/example_tile_convolution.py +66 -0
  108. warp/examples/tile/example_tile_fft.py +55 -0
  109. warp/examples/tile/example_tile_filtering.py +113 -0
  110. warp/examples/tile/example_tile_matmul.py +85 -0
  111. warp/examples/tile/example_tile_mlp.py +383 -0
  112. warp/examples/tile/example_tile_nbody.py +199 -0
  113. warp/examples/tile/example_tile_walker.py +327 -0
  114. warp/fabric.py +355 -0
  115. warp/fem/__init__.py +106 -0
  116. warp/fem/adaptivity.py +508 -0
  117. warp/fem/cache.py +572 -0
  118. warp/fem/dirichlet.py +202 -0
  119. warp/fem/domain.py +411 -0
  120. warp/fem/field/__init__.py +125 -0
  121. warp/fem/field/field.py +619 -0
  122. warp/fem/field/nodal_field.py +326 -0
  123. warp/fem/field/restriction.py +37 -0
  124. warp/fem/field/virtual.py +848 -0
  125. warp/fem/geometry/__init__.py +32 -0
  126. warp/fem/geometry/adaptive_nanogrid.py +857 -0
  127. warp/fem/geometry/closest_point.py +84 -0
  128. warp/fem/geometry/deformed_geometry.py +221 -0
  129. warp/fem/geometry/element.py +776 -0
  130. warp/fem/geometry/geometry.py +362 -0
  131. warp/fem/geometry/grid_2d.py +392 -0
  132. warp/fem/geometry/grid_3d.py +452 -0
  133. warp/fem/geometry/hexmesh.py +911 -0
  134. warp/fem/geometry/nanogrid.py +571 -0
  135. warp/fem/geometry/partition.py +389 -0
  136. warp/fem/geometry/quadmesh.py +663 -0
  137. warp/fem/geometry/tetmesh.py +855 -0
  138. warp/fem/geometry/trimesh.py +806 -0
  139. warp/fem/integrate.py +2335 -0
  140. warp/fem/linalg.py +419 -0
  141. warp/fem/operator.py +293 -0
  142. warp/fem/polynomial.py +229 -0
  143. warp/fem/quadrature/__init__.py +17 -0
  144. warp/fem/quadrature/pic_quadrature.py +299 -0
  145. warp/fem/quadrature/quadrature.py +591 -0
  146. warp/fem/space/__init__.py +228 -0
  147. warp/fem/space/basis_function_space.py +468 -0
  148. warp/fem/space/basis_space.py +667 -0
  149. warp/fem/space/dof_mapper.py +251 -0
  150. warp/fem/space/function_space.py +309 -0
  151. warp/fem/space/grid_2d_function_space.py +177 -0
  152. warp/fem/space/grid_3d_function_space.py +227 -0
  153. warp/fem/space/hexmesh_function_space.py +257 -0
  154. warp/fem/space/nanogrid_function_space.py +201 -0
  155. warp/fem/space/partition.py +367 -0
  156. warp/fem/space/quadmesh_function_space.py +223 -0
  157. warp/fem/space/restriction.py +179 -0
  158. warp/fem/space/shape/__init__.py +143 -0
  159. warp/fem/space/shape/cube_shape_function.py +1105 -0
  160. warp/fem/space/shape/shape_function.py +133 -0
  161. warp/fem/space/shape/square_shape_function.py +926 -0
  162. warp/fem/space/shape/tet_shape_function.py +834 -0
  163. warp/fem/space/shape/triangle_shape_function.py +672 -0
  164. warp/fem/space/tetmesh_function_space.py +271 -0
  165. warp/fem/space/topology.py +424 -0
  166. warp/fem/space/trimesh_function_space.py +194 -0
  167. warp/fem/types.py +99 -0
  168. warp/fem/utils.py +420 -0
  169. warp/jax.py +187 -0
  170. warp/jax_experimental/__init__.py +16 -0
  171. warp/jax_experimental/custom_call.py +351 -0
  172. warp/jax_experimental/ffi.py +698 -0
  173. warp/jax_experimental/xla_ffi.py +602 -0
  174. warp/math.py +244 -0
  175. warp/native/array.h +1145 -0
  176. warp/native/builtin.h +1800 -0
  177. warp/native/bvh.cpp +492 -0
  178. warp/native/bvh.cu +791 -0
  179. warp/native/bvh.h +554 -0
  180. warp/native/clang/clang.cpp +536 -0
  181. warp/native/coloring.cpp +613 -0
  182. warp/native/crt.cpp +51 -0
  183. warp/native/crt.h +362 -0
  184. warp/native/cuda_crt.h +1058 -0
  185. warp/native/cuda_util.cpp +646 -0
  186. warp/native/cuda_util.h +307 -0
  187. warp/native/error.cpp +77 -0
  188. warp/native/error.h +36 -0
  189. warp/native/exports.h +1878 -0
  190. warp/native/fabric.h +245 -0
  191. warp/native/hashgrid.cpp +311 -0
  192. warp/native/hashgrid.cu +87 -0
  193. warp/native/hashgrid.h +240 -0
  194. warp/native/initializer_array.h +41 -0
  195. warp/native/intersect.h +1230 -0
  196. warp/native/intersect_adj.h +375 -0
  197. warp/native/intersect_tri.h +339 -0
  198. warp/native/marching.cpp +19 -0
  199. warp/native/marching.cu +514 -0
  200. warp/native/marching.h +19 -0
  201. warp/native/mat.h +2220 -0
  202. warp/native/mathdx.cpp +87 -0
  203. warp/native/matnn.h +343 -0
  204. warp/native/mesh.cpp +266 -0
  205. warp/native/mesh.cu +404 -0
  206. warp/native/mesh.h +1980 -0
  207. warp/native/nanovdb/GridHandle.h +366 -0
  208. warp/native/nanovdb/HostBuffer.h +590 -0
  209. warp/native/nanovdb/NanoVDB.h +6624 -0
  210. warp/native/nanovdb/PNanoVDB.h +3390 -0
  211. warp/native/noise.h +859 -0
  212. warp/native/quat.h +1371 -0
  213. warp/native/rand.h +342 -0
  214. warp/native/range.h +139 -0
  215. warp/native/reduce.cpp +174 -0
  216. warp/native/reduce.cu +364 -0
  217. warp/native/runlength_encode.cpp +79 -0
  218. warp/native/runlength_encode.cu +61 -0
  219. warp/native/scan.cpp +47 -0
  220. warp/native/scan.cu +53 -0
  221. warp/native/scan.h +23 -0
  222. warp/native/solid_angle.h +466 -0
  223. warp/native/sort.cpp +251 -0
  224. warp/native/sort.cu +277 -0
  225. warp/native/sort.h +33 -0
  226. warp/native/sparse.cpp +378 -0
  227. warp/native/sparse.cu +524 -0
  228. warp/native/spatial.h +657 -0
  229. warp/native/svd.h +702 -0
  230. warp/native/temp_buffer.h +46 -0
  231. warp/native/tile.h +2584 -0
  232. warp/native/tile_reduce.h +264 -0
  233. warp/native/vec.h +1426 -0
  234. warp/native/volume.cpp +501 -0
  235. warp/native/volume.cu +67 -0
  236. warp/native/volume.h +969 -0
  237. warp/native/volume_builder.cu +477 -0
  238. warp/native/volume_builder.h +52 -0
  239. warp/native/volume_impl.h +70 -0
  240. warp/native/warp.cpp +1082 -0
  241. warp/native/warp.cu +3636 -0
  242. warp/native/warp.h +381 -0
  243. warp/optim/__init__.py +17 -0
  244. warp/optim/adam.py +163 -0
  245. warp/optim/linear.py +1137 -0
  246. warp/optim/sgd.py +112 -0
  247. warp/paddle.py +407 -0
  248. warp/render/__init__.py +18 -0
  249. warp/render/render_opengl.py +3518 -0
  250. warp/render/render_usd.py +784 -0
  251. warp/render/utils.py +160 -0
  252. warp/sim/__init__.py +65 -0
  253. warp/sim/articulation.py +793 -0
  254. warp/sim/collide.py +2395 -0
  255. warp/sim/graph_coloring.py +300 -0
  256. warp/sim/import_mjcf.py +790 -0
  257. warp/sim/import_snu.py +227 -0
  258. warp/sim/import_urdf.py +579 -0
  259. warp/sim/import_usd.py +894 -0
  260. warp/sim/inertia.py +324 -0
  261. warp/sim/integrator.py +242 -0
  262. warp/sim/integrator_euler.py +1997 -0
  263. warp/sim/integrator_featherstone.py +2101 -0
  264. warp/sim/integrator_vbd.py +2048 -0
  265. warp/sim/integrator_xpbd.py +3292 -0
  266. warp/sim/model.py +4791 -0
  267. warp/sim/particles.py +121 -0
  268. warp/sim/render.py +427 -0
  269. warp/sim/utils.py +428 -0
  270. warp/sparse.py +2057 -0
  271. warp/stubs.py +3333 -0
  272. warp/tape.py +1203 -0
  273. warp/tests/__init__.py +1 -0
  274. warp/tests/__main__.py +4 -0
  275. warp/tests/assets/curlnoise_golden.npy +0 -0
  276. warp/tests/assets/mlp_golden.npy +0 -0
  277. warp/tests/assets/pixel.npy +0 -0
  278. warp/tests/assets/pnoise_golden.npy +0 -0
  279. warp/tests/assets/spiky.usd +0 -0
  280. warp/tests/assets/test_grid.nvdb +0 -0
  281. warp/tests/assets/test_index_grid.nvdb +0 -0
  282. warp/tests/assets/test_int32_grid.nvdb +0 -0
  283. warp/tests/assets/test_vec_grid.nvdb +0 -0
  284. warp/tests/assets/torus.nvdb +0 -0
  285. warp/tests/assets/torus.usda +105 -0
  286. warp/tests/aux_test_class_kernel.py +34 -0
  287. warp/tests/aux_test_compile_consts_dummy.py +18 -0
  288. warp/tests/aux_test_conditional_unequal_types_kernels.py +29 -0
  289. warp/tests/aux_test_dependent.py +29 -0
  290. warp/tests/aux_test_grad_customs.py +29 -0
  291. warp/tests/aux_test_instancing_gc.py +26 -0
  292. warp/tests/aux_test_module_unload.py +23 -0
  293. warp/tests/aux_test_name_clash1.py +40 -0
  294. warp/tests/aux_test_name_clash2.py +40 -0
  295. warp/tests/aux_test_reference.py +9 -0
  296. warp/tests/aux_test_reference_reference.py +8 -0
  297. warp/tests/aux_test_square.py +16 -0
  298. warp/tests/aux_test_unresolved_func.py +22 -0
  299. warp/tests/aux_test_unresolved_symbol.py +22 -0
  300. warp/tests/cuda/__init__.py +0 -0
  301. warp/tests/cuda/test_async.py +676 -0
  302. warp/tests/cuda/test_ipc.py +124 -0
  303. warp/tests/cuda/test_mempool.py +233 -0
  304. warp/tests/cuda/test_multigpu.py +169 -0
  305. warp/tests/cuda/test_peer.py +139 -0
  306. warp/tests/cuda/test_pinned.py +84 -0
  307. warp/tests/cuda/test_streams.py +634 -0
  308. warp/tests/geometry/__init__.py +0 -0
  309. warp/tests/geometry/test_bvh.py +200 -0
  310. warp/tests/geometry/test_hash_grid.py +221 -0
  311. warp/tests/geometry/test_marching_cubes.py +74 -0
  312. warp/tests/geometry/test_mesh.py +316 -0
  313. warp/tests/geometry/test_mesh_query_aabb.py +399 -0
  314. warp/tests/geometry/test_mesh_query_point.py +932 -0
  315. warp/tests/geometry/test_mesh_query_ray.py +311 -0
  316. warp/tests/geometry/test_volume.py +1103 -0
  317. warp/tests/geometry/test_volume_write.py +346 -0
  318. warp/tests/interop/__init__.py +0 -0
  319. warp/tests/interop/test_dlpack.py +729 -0
  320. warp/tests/interop/test_jax.py +371 -0
  321. warp/tests/interop/test_paddle.py +800 -0
  322. warp/tests/interop/test_torch.py +1001 -0
  323. warp/tests/run_coverage_serial.py +39 -0
  324. warp/tests/sim/__init__.py +0 -0
  325. warp/tests/sim/disabled_kinematics.py +244 -0
  326. warp/tests/sim/flaky_test_sim_grad.py +290 -0
  327. warp/tests/sim/test_collision.py +604 -0
  328. warp/tests/sim/test_coloring.py +258 -0
  329. warp/tests/sim/test_model.py +224 -0
  330. warp/tests/sim/test_sim_grad_bounce_linear.py +212 -0
  331. warp/tests/sim/test_sim_kinematics.py +98 -0
  332. warp/tests/sim/test_vbd.py +597 -0
  333. warp/tests/test_adam.py +163 -0
  334. warp/tests/test_arithmetic.py +1096 -0
  335. warp/tests/test_array.py +2972 -0
  336. warp/tests/test_array_reduce.py +156 -0
  337. warp/tests/test_assert.py +250 -0
  338. warp/tests/test_atomic.py +153 -0
  339. warp/tests/test_bool.py +220 -0
  340. warp/tests/test_builtins_resolution.py +1298 -0
  341. warp/tests/test_closest_point_edge_edge.py +327 -0
  342. warp/tests/test_codegen.py +810 -0
  343. warp/tests/test_codegen_instancing.py +1495 -0
  344. warp/tests/test_compile_consts.py +215 -0
  345. warp/tests/test_conditional.py +252 -0
  346. warp/tests/test_context.py +42 -0
  347. warp/tests/test_copy.py +238 -0
  348. warp/tests/test_ctypes.py +638 -0
  349. warp/tests/test_dense.py +73 -0
  350. warp/tests/test_devices.py +97 -0
  351. warp/tests/test_examples.py +482 -0
  352. warp/tests/test_fabricarray.py +996 -0
  353. warp/tests/test_fast_math.py +74 -0
  354. warp/tests/test_fem.py +2003 -0
  355. warp/tests/test_fp16.py +136 -0
  356. warp/tests/test_func.py +454 -0
  357. warp/tests/test_future_annotations.py +98 -0
  358. warp/tests/test_generics.py +656 -0
  359. warp/tests/test_grad.py +893 -0
  360. warp/tests/test_grad_customs.py +339 -0
  361. warp/tests/test_grad_debug.py +341 -0
  362. warp/tests/test_implicit_init.py +411 -0
  363. warp/tests/test_import.py +45 -0
  364. warp/tests/test_indexedarray.py +1140 -0
  365. warp/tests/test_intersect.py +73 -0
  366. warp/tests/test_iter.py +76 -0
  367. warp/tests/test_large.py +177 -0
  368. warp/tests/test_launch.py +411 -0
  369. warp/tests/test_lerp.py +151 -0
  370. warp/tests/test_linear_solvers.py +193 -0
  371. warp/tests/test_lvalue.py +427 -0
  372. warp/tests/test_mat.py +2089 -0
  373. warp/tests/test_mat_lite.py +122 -0
  374. warp/tests/test_mat_scalar_ops.py +2913 -0
  375. warp/tests/test_math.py +178 -0
  376. warp/tests/test_mlp.py +282 -0
  377. warp/tests/test_module_hashing.py +258 -0
  378. warp/tests/test_modules_lite.py +44 -0
  379. warp/tests/test_noise.py +252 -0
  380. warp/tests/test_operators.py +299 -0
  381. warp/tests/test_options.py +129 -0
  382. warp/tests/test_overwrite.py +551 -0
  383. warp/tests/test_print.py +339 -0
  384. warp/tests/test_quat.py +2315 -0
  385. warp/tests/test_rand.py +339 -0
  386. warp/tests/test_reload.py +302 -0
  387. warp/tests/test_rounding.py +185 -0
  388. warp/tests/test_runlength_encode.py +196 -0
  389. warp/tests/test_scalar_ops.py +105 -0
  390. warp/tests/test_smoothstep.py +108 -0
  391. warp/tests/test_snippet.py +318 -0
  392. warp/tests/test_sparse.py +582 -0
  393. warp/tests/test_spatial.py +2229 -0
  394. warp/tests/test_special_values.py +361 -0
  395. warp/tests/test_static.py +592 -0
  396. warp/tests/test_struct.py +734 -0
  397. warp/tests/test_tape.py +204 -0
  398. warp/tests/test_transient_module.py +93 -0
  399. warp/tests/test_triangle_closest_point.py +145 -0
  400. warp/tests/test_types.py +562 -0
  401. warp/tests/test_utils.py +588 -0
  402. warp/tests/test_vec.py +1487 -0
  403. warp/tests/test_vec_lite.py +80 -0
  404. warp/tests/test_vec_scalar_ops.py +2327 -0
  405. warp/tests/test_verify_fp.py +100 -0
  406. warp/tests/tile/__init__.py +0 -0
  407. warp/tests/tile/test_tile.py +780 -0
  408. warp/tests/tile/test_tile_load.py +407 -0
  409. warp/tests/tile/test_tile_mathdx.py +208 -0
  410. warp/tests/tile/test_tile_mlp.py +402 -0
  411. warp/tests/tile/test_tile_reduce.py +447 -0
  412. warp/tests/tile/test_tile_shared_memory.py +247 -0
  413. warp/tests/tile/test_tile_view.py +173 -0
  414. warp/tests/unittest_serial.py +47 -0
  415. warp/tests/unittest_suites.py +427 -0
  416. warp/tests/unittest_utils.py +468 -0
  417. warp/tests/walkthrough_debug.py +93 -0
  418. warp/thirdparty/__init__.py +0 -0
  419. warp/thirdparty/appdirs.py +598 -0
  420. warp/thirdparty/dlpack.py +145 -0
  421. warp/thirdparty/unittest_parallel.py +570 -0
  422. warp/torch.py +391 -0
  423. warp/types.py +5230 -0
  424. warp/utils.py +1137 -0
  425. warp_lang-1.7.0.dist-info/METADATA +516 -0
  426. warp_lang-1.7.0.dist-info/RECORD +429 -0
  427. warp_lang-1.7.0.dist-info/WHEEL +5 -0
  428. warp_lang-1.7.0.dist-info/licenses/LICENSE.md +202 -0
  429. warp_lang-1.7.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,2048 @@
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
+
16
+ import numpy as np
17
+
18
+ import warp as wp
19
+
20
+ from ..types import float32, matrix
21
+ from .collide import (
22
+ TriMeshCollisionDetector,
23
+ TriMeshCollisionInfo,
24
+ get_edge_colliding_edges,
25
+ get_edge_colliding_edges_count,
26
+ get_triangle_colliding_vertices,
27
+ get_triangle_colliding_vertices_count,
28
+ get_vertex_colliding_triangles,
29
+ get_vertex_colliding_triangles_count,
30
+ triangle_closest_point,
31
+ )
32
+ from .integrator import Integrator
33
+ from .model import PARTICLE_FLAG_ACTIVE, Control, Model, ModelShapeMaterials, State
34
+
35
+ wp.set_module_options({"enable_backward": False})
36
+
37
+ VBD_DEBUG_PRINTING_OPTIONS = {
38
+ # "elasticity_force_hessian",
39
+ # "contact_force_hessian",
40
+ # "overall_force_hessian",
41
+ # "inertia_force_hessian",
42
+ # "connectivity",
43
+ # "contact_info",
44
+ }
45
+
46
+
47
+ class mat66(matrix(shape=(6, 6), dtype=float32)):
48
+ pass
49
+
50
+
51
+ class mat32(matrix(shape=(3, 2), dtype=float32)):
52
+ pass
53
+
54
+
55
+ @wp.struct
56
+ class ForceElementAdjacencyInfo:
57
+ r"""
58
+ - vertex_adjacent_[element]: the flatten adjacency information. Its size is \sum_{i\inV} 2*N_i, where N_i is the
59
+ number of vertex i’s adjacent [element]. For each adjacent element it stores 2 information:
60
+ - the id of the adjacent element
61
+ - the order of the vertex in the element, which is essential to compute the force and hessian for the vertex
62
+ - vertex_adjacent_[element]_offsets: stores where each vertex’ information starts in the flatten adjacency array.
63
+ Its size is |V|+1 such that the number of vertex i’s adjacent [element] can be computed as
64
+ vertex_adjacent_[element]_offsets[i+1]-vertex_adjacent_[element]_offsets[i].
65
+ """
66
+
67
+ v_adj_faces: wp.array(dtype=int)
68
+ v_adj_faces_offsets: wp.array(dtype=int)
69
+
70
+ v_adj_edges: wp.array(dtype=int)
71
+ v_adj_edges_offsets: wp.array(dtype=int)
72
+
73
+ def to(self, device):
74
+ if device.is_cpu:
75
+ return self
76
+ else:
77
+ adjacency_gpu = ForceElementAdjacencyInfo()
78
+ adjacency_gpu.v_adj_faces = self.v_adj_faces.to(device)
79
+ adjacency_gpu.v_adj_faces_offsets = self.v_adj_faces_offsets.to(device)
80
+
81
+ adjacency_gpu.v_adj_edges = self.v_adj_edges.to(device)
82
+ adjacency_gpu.v_adj_edges_offsets = self.v_adj_edges_offsets.to(device)
83
+
84
+ return adjacency_gpu
85
+
86
+
87
+ @wp.func
88
+ def get_vertex_num_adjacent_edges(adjacency: ForceElementAdjacencyInfo, vertex: wp.int32):
89
+ return (adjacency.v_adj_edges_offsets[vertex + 1] - adjacency.v_adj_edges_offsets[vertex]) >> 1
90
+
91
+
92
+ @wp.func
93
+ def get_vertex_adjacent_edge_id_order(adjacency: ForceElementAdjacencyInfo, vertex: wp.int32, edge: wp.int32):
94
+ offset = adjacency.v_adj_edges_offsets[vertex]
95
+ return adjacency.v_adj_edges[offset + edge * 2], adjacency.v_adj_edges[offset + edge * 2 + 1]
96
+
97
+
98
+ @wp.func
99
+ def get_vertex_num_adjacent_faces(adjacency: ForceElementAdjacencyInfo, vertex: wp.int32):
100
+ return (adjacency.v_adj_faces_offsets[vertex + 1] - adjacency.v_adj_faces_offsets[vertex]) >> 1
101
+
102
+
103
+ @wp.func
104
+ def get_vertex_adjacent_face_id_order(adjacency: ForceElementAdjacencyInfo, vertex: wp.int32, face: wp.int32):
105
+ offset = adjacency.v_adj_faces_offsets[vertex]
106
+ return adjacency.v_adj_faces[offset + face * 2], adjacency.v_adj_faces[offset + face * 2 + 1]
107
+
108
+
109
+ @wp.kernel
110
+ def _test_compute_force_element_adjacency(
111
+ adjacency: ForceElementAdjacencyInfo,
112
+ edge_indices: wp.array(dtype=wp.int32, ndim=2),
113
+ face_indices: wp.array(dtype=wp.int32, ndim=2),
114
+ ):
115
+ wp.printf("num vertices: %d\n", adjacency.v_adj_edges_offsets.shape[0] - 1)
116
+ for vertex in range(adjacency.v_adj_edges_offsets.shape[0] - 1):
117
+ num_adj_edges = get_vertex_num_adjacent_edges(adjacency, vertex)
118
+ for i_bd in range(num_adj_edges):
119
+ bd_id, v_order = get_vertex_adjacent_edge_id_order(adjacency, vertex, i_bd)
120
+
121
+ if edge_indices[bd_id, v_order] != vertex:
122
+ print("Error!!!")
123
+ wp.printf("vertex: %d | num_adj_edges: %d\n", vertex, num_adj_edges)
124
+ wp.printf("--iBd: %d | ", i_bd)
125
+ wp.printf("edge id: %d | v_order: %d\n", bd_id, v_order)
126
+
127
+ num_adj_faces = get_vertex_num_adjacent_faces(adjacency, vertex)
128
+
129
+ for i_face in range(num_adj_faces):
130
+ face, v_order = get_vertex_adjacent_face_id_order(
131
+ adjacency,
132
+ vertex,
133
+ i_face,
134
+ )
135
+
136
+ if face_indices[face, v_order] != vertex:
137
+ print("Error!!!")
138
+ wp.printf("vertex: %d | num_adj_faces: %d\n", vertex, num_adj_faces)
139
+ wp.printf("--i_face: %d | face id: %d | v_order: %d\n", i_face, face, v_order)
140
+ wp.printf(
141
+ "--face: %d %d %d\n",
142
+ face_indices[face, 0],
143
+ face_indices[face, 1],
144
+ face_indices[face, 2],
145
+ )
146
+
147
+
148
+ @wp.func
149
+ def build_orthonormal_basis(n: wp.vec3):
150
+ """
151
+ Builds an orthonormal basis given a normal vector `n`. Return the two axes that is perpendicular to `n`.
152
+
153
+ :param n: A 3D vector (list or array-like) representing the normal vector
154
+ """
155
+ b1 = wp.vec3()
156
+ b2 = wp.vec3()
157
+ if n[2] < 0.0:
158
+ a = 1.0 / (1.0 - n[2])
159
+ b = n[0] * n[1] * a
160
+ b1[0] = 1.0 - n[0] * n[0] * a
161
+ b1[1] = -b
162
+ b1[2] = n[0]
163
+
164
+ b2[0] = b
165
+ b2[1] = n[1] * n[1] * a - 1.0
166
+ b2[2] = -n[1]
167
+ else:
168
+ a = 1.0 / (1.0 + n[2])
169
+ b = -n[0] * n[1] * a
170
+ b1[0] = 1.0 - n[0] * n[0] * a
171
+ b1[1] = b
172
+ b1[2] = -n[0]
173
+
174
+ b2[0] = b
175
+ b2[1] = 1.0 - n[1] * n[1] * a
176
+ b2[2] = -n[1]
177
+
178
+ return b1, b2
179
+
180
+
181
+ @wp.func
182
+ def calculate_triangle_deformation_gradient(
183
+ face: int, tri_indices: wp.array(dtype=wp.int32, ndim=2), pos: wp.array(dtype=wp.vec3), tri_pose: wp.mat22
184
+ ):
185
+ F = mat32()
186
+ v1 = pos[tri_indices[face, 1]] - pos[tri_indices[face, 0]]
187
+ v2 = pos[tri_indices[face, 2]] - pos[tri_indices[face, 0]]
188
+
189
+ F[0, 0] = v1[0]
190
+ F[1, 0] = v1[1]
191
+ F[2, 0] = v1[2]
192
+ F[0, 1] = v2[0]
193
+ F[1, 1] = v2[1]
194
+ F[2, 1] = v2[2]
195
+
196
+ F = F * tri_pose
197
+ return F
198
+
199
+
200
+ @wp.func
201
+ def green_strain(F: mat32):
202
+ return 0.5 * (wp.transpose(F) * F - wp.identity(n=2, dtype=float))
203
+
204
+
205
+ @wp.func
206
+ def assemble_membrane_hessian(h: mat66, m1: float, m2: float):
207
+ h_vert = wp.mat33(
208
+ m1 * (h[0, 0] * m1 + h[3, 0] * m2) + m2 * (h[0, 3] * m1 + h[3, 3] * m2),
209
+ m1 * (h[0, 1] * m1 + h[3, 1] * m2) + m2 * (h[0, 4] * m1 + h[3, 4] * m2),
210
+ m1 * (h[0, 2] * m1 + h[3, 2] * m2) + m2 * (h[0, 5] * m1 + h[3, 5] * m2),
211
+ m1 * (h[1, 0] * m1 + h[4, 0] * m2) + m2 * (h[1, 3] * m1 + h[4, 3] * m2),
212
+ m1 * (h[1, 1] * m1 + h[4, 1] * m2) + m2 * (h[1, 4] * m1 + h[4, 4] * m2),
213
+ m1 * (h[1, 2] * m1 + h[4, 2] * m2) + m2 * (h[1, 5] * m1 + h[4, 5] * m2),
214
+ m1 * (h[2, 0] * m1 + h[5, 0] * m2) + m2 * (h[2, 3] * m1 + h[5, 3] * m2),
215
+ m1 * (h[2, 1] * m1 + h[5, 1] * m2) + m2 * (h[2, 4] * m1 + h[5, 4] * m2),
216
+ m1 * (h[2, 2] * m1 + h[5, 2] * m2) + m2 * (h[2, 5] * m1 + h[5, 5] * m2),
217
+ )
218
+
219
+ return h_vert
220
+
221
+
222
+ @wp.func
223
+ def evaluate_stvk_force_hessian(
224
+ face: int,
225
+ v_order: int,
226
+ pos: wp.array(dtype=wp.vec3),
227
+ tri_indices: wp.array(dtype=wp.int32, ndim=2),
228
+ tri_pose: wp.mat22,
229
+ area: float,
230
+ mu: float,
231
+ lmbd: float,
232
+ damping: float,
233
+ ):
234
+ D2W_DFDF = mat66()
235
+ F = calculate_triangle_deformation_gradient(face, tri_indices, pos, tri_pose)
236
+ G = green_strain(F)
237
+
238
+ S = 2.0 * mu * G + lmbd * (G[0, 0] + G[1, 1]) * wp.identity(n=2, dtype=float)
239
+
240
+ F12 = -area * F * S * wp.transpose(tri_pose)
241
+
242
+ Dm_inv1_1 = tri_pose[0, 0]
243
+ Dm_inv2_1 = tri_pose[1, 0]
244
+ Dm_inv1_2 = tri_pose[0, 1]
245
+ Dm_inv2_2 = tri_pose[1, 1]
246
+
247
+ F1_1 = F[0, 0]
248
+ F2_1 = F[1, 0]
249
+ F3_1 = F[2, 0]
250
+ F1_2 = F[0, 1]
251
+ F2_2 = F[1, 1]
252
+ F3_2 = F[2, 1]
253
+
254
+ F1_1_sqr = F1_1 * F1_1
255
+ F2_1_sqr = F2_1 * F2_1
256
+ F3_1_sqr = F3_1 * F3_1
257
+ F1_2_sqr = F1_2 * F1_2
258
+ F2_2_sqr = F2_2 * F2_2
259
+ F3_2_sqr = F3_2 * F3_2
260
+
261
+ e_uu = G[0, 0]
262
+ e_vv = G[1, 1]
263
+ e_uv = G[0, 1]
264
+ e_uuvvSum = e_uu + e_vv
265
+
266
+ 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
267
+
268
+ D2W_DFDF[1, 0] = F1_1 * (F2_1 * lmbd + 2.0 * F2_1 * mu) + F1_2 * F2_2 * mu
269
+ D2W_DFDF[0, 1] = D2W_DFDF[1, 0]
270
+
271
+ D2W_DFDF[2, 0] = F1_1 * (F3_1 * lmbd + 2.0 * F3_1 * mu) + F1_2 * F3_2 * mu
272
+ D2W_DFDF[0, 2] = D2W_DFDF[2, 0]
273
+
274
+ D2W_DFDF[3, 0] = 2.0 * mu * e_uv + F1_1 * F1_2 * lmbd + F1_1 * F1_2 * mu
275
+ D2W_DFDF[0, 3] = D2W_DFDF[3, 0]
276
+
277
+ D2W_DFDF[4, 0] = F1_1 * F2_2 * lmbd + F1_2 * F2_1 * mu
278
+ D2W_DFDF[0, 4] = D2W_DFDF[4, 0]
279
+
280
+ D2W_DFDF[5, 0] = F1_1 * F3_2 * lmbd + F1_2 * F3_1 * mu
281
+ D2W_DFDF[0, 5] = D2W_DFDF[5, 0]
282
+
283
+ 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
284
+
285
+ D2W_DFDF[2, 1] = F2_1 * (F3_1 * lmbd + 2.0 * F3_1 * mu) + F2_2 * F3_2 * mu
286
+ D2W_DFDF[1, 2] = D2W_DFDF[2, 1]
287
+
288
+ D2W_DFDF[3, 1] = F1_2 * F2_1 * lmbd + F1_1 * F2_2 * mu
289
+ D2W_DFDF[1, 3] = D2W_DFDF[3, 1]
290
+
291
+ D2W_DFDF[4, 1] = 2.0 * mu * e_uv + F2_1 * F2_2 * lmbd + F2_1 * F2_2 * mu
292
+ D2W_DFDF[1, 4] = D2W_DFDF[4, 1]
293
+
294
+ D2W_DFDF[5, 1] = F2_1 * F3_2 * lmbd + F2_2 * F3_1 * mu
295
+ D2W_DFDF[1, 5] = D2W_DFDF[5, 1]
296
+
297
+ 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
298
+
299
+ D2W_DFDF[3, 2] = F1_2 * F3_1 * lmbd + F1_1 * F3_2 * mu
300
+ D2W_DFDF[2, 3] = D2W_DFDF[3, 2]
301
+
302
+ D2W_DFDF[4, 2] = F2_2 * F3_1 * lmbd + F2_1 * F3_2 * mu
303
+ D2W_DFDF[2, 4] = D2W_DFDF[4, 2]
304
+
305
+ D2W_DFDF[5, 2] = 2.0 * mu * e_uv + F3_1 * F3_2 * lmbd + F3_1 * F3_2 * mu
306
+ D2W_DFDF[2, 5] = D2W_DFDF[5, 2]
307
+
308
+ 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
309
+
310
+ D2W_DFDF[4, 3] = F1_2 * (F2_2 * lmbd + 2.0 * F2_2 * mu) + F1_1 * F2_1 * mu
311
+ D2W_DFDF[3, 4] = D2W_DFDF[4, 3]
312
+
313
+ D2W_DFDF[5, 3] = F1_2 * (F3_2 * lmbd + 2.0 * F3_2 * mu) + F1_1 * F3_1 * mu
314
+ D2W_DFDF[3, 5] = D2W_DFDF[5, 3]
315
+
316
+ 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
317
+
318
+ D2W_DFDF[5, 4] = F2_2 * (F3_2 * lmbd + 2.0 * F3_2 * mu) + F2_1 * F3_1 * mu
319
+ D2W_DFDF[4, 5] = D2W_DFDF[5, 4]
320
+
321
+ 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
322
+
323
+ D2W_DFDF = D2W_DFDF * area
324
+
325
+ # m1s = wp.vec3(-Dm_inv1_1 - Dm_inv2_1, Dm_inv1_1, Dm_inv2_1)
326
+ # m2s = wp.vec3(-Dm_inv1_2 - Dm_inv2_2, Dm_inv1_2, Dm_inv2_2)
327
+ #
328
+ # m1 = m1s[v_order]
329
+ # m2 = m2s[v_order]
330
+
331
+ if v_order == 0:
332
+ m1 = -Dm_inv1_1 - Dm_inv2_1
333
+ m2 = -Dm_inv1_2 - Dm_inv2_2
334
+ f = wp.vec3(-F12[0, 0] - F12[0, 1], -F12[1, 0] - F12[1, 1], -F12[2, 0] - F12[2, 1])
335
+ elif v_order == 1:
336
+ m1 = Dm_inv1_1
337
+ m2 = Dm_inv1_2
338
+ f = wp.vec3(F12[0, 0], F12[1, 0], F12[2, 0])
339
+ else:
340
+ m1 = Dm_inv2_1
341
+ m2 = Dm_inv2_2
342
+ f = wp.vec3(F12[0, 1], F12[1, 1], F12[2, 1])
343
+
344
+ h = assemble_membrane_hessian(D2W_DFDF, m1, m2)
345
+
346
+ return f, h
347
+
348
+
349
+ @wp.func
350
+ def mat_vec_cross_from_3_basis(e1: wp.vec3, e2: wp.vec3, e3: wp.vec3, a: wp.vec3):
351
+ e1_cross_a = wp.cross(e1, a)
352
+ e2_cross_a = wp.cross(e2, a)
353
+ e3_cross_a = wp.cross(e3, a)
354
+
355
+ return wp.mat33(
356
+ e1_cross_a[0],
357
+ e2_cross_a[0],
358
+ e3_cross_a[0],
359
+ e1_cross_a[1],
360
+ e2_cross_a[1],
361
+ e3_cross_a[1],
362
+ e1_cross_a[2],
363
+ e2_cross_a[2],
364
+ e3_cross_a[2],
365
+ )
366
+
367
+
368
+ @wp.func
369
+ def mat_vec_cross(mat: wp.mat33, a: wp.vec3):
370
+ e1 = wp.vec3(mat[0, 0], mat[1, 0], mat[2, 0])
371
+ e2 = wp.vec3(mat[0, 1], mat[1, 1], mat[2, 1])
372
+ e3 = wp.vec3(mat[0, 2], mat[1, 2], mat[2, 2])
373
+
374
+ return mat_vec_cross_from_3_basis(e1, e2, e3, a)
375
+
376
+
377
+ @wp.func
378
+ def evaluate_dihedral_angle_based_bending_force_hessian(
379
+ bending_index: int,
380
+ v_order: int,
381
+ pos: wp.array(dtype=wp.vec3),
382
+ pos_prev: wp.array(dtype=wp.vec3),
383
+ edge_indices: wp.array(dtype=wp.int32, ndim=2),
384
+ edge_rest_angle: wp.array(dtype=float),
385
+ edge_rest_length: wp.array(dtype=float),
386
+ stiffness: float,
387
+ damping: float,
388
+ dt: float,
389
+ ):
390
+ if edge_indices[bending_index, 0] == -1 or edge_indices[bending_index, 1] == -1:
391
+ return wp.vec3(0.0), wp.mat33(0.0)
392
+
393
+ x1 = pos[edge_indices[bending_index, 0]]
394
+ x2 = pos[edge_indices[bending_index, 2]]
395
+ x3 = pos[edge_indices[bending_index, 3]]
396
+ x4 = pos[edge_indices[bending_index, 1]]
397
+
398
+ e1 = wp.vec3(1.0, 0.0, 0.0)
399
+ e2 = wp.vec3(0.0, 1.0, 0.0)
400
+ e3 = wp.vec3(0.0, 0.0, 1.0)
401
+
402
+ n1 = wp.cross((x2 - x1), (x3 - x1))
403
+ n2 = wp.cross((x3 - x4), (x2 - x4))
404
+ n1_norm = wp.length(n1)
405
+ n2_norm = wp.length(n2)
406
+
407
+ # degenerated bending edge
408
+ if n1_norm < 1.0e-6 or n2_norm < 1.0e-6:
409
+ return wp.vec3(0.0), wp.mat33(0.0)
410
+
411
+ n1_n = n1 / n1_norm
412
+ n2_n = n2 / n2_norm
413
+
414
+ # avoid the infinite gradient of acos at -1 or 1
415
+ cos_theta = wp.dot(n1_n, n2_n)
416
+ if wp.abs(cos_theta) > 0.9999:
417
+ cos_theta = 0.9999 * wp.sign(cos_theta)
418
+
419
+ angle_sign = wp.sign(wp.dot(wp.cross(n2, n1), x3 - x2))
420
+ theta = wp.acos(cos_theta) * angle_sign
421
+ rest_angle = edge_rest_angle[bending_index]
422
+
423
+ dE_dtheta = stiffness * (theta - rest_angle)
424
+
425
+ d_theta_d_cos_theta = angle_sign * (-1.0 / wp.sqrt(1.0 - cos_theta * cos_theta))
426
+ sin_theta = angle_sign * wp.sqrt(1.0 - cos_theta * cos_theta)
427
+ one_over_sin_theta = 1.0 / sin_theta
428
+ d_one_over_sin_theta_d_cos_theta = cos_theta / (sin_theta * sin_theta * sin_theta)
429
+
430
+ e_rest_len = edge_rest_length[bending_index]
431
+
432
+ if v_order == 0:
433
+ d_cos_theta_dx1 = 1.0 / n1_norm * (-wp.cross(x3 - x1, n2_n) + wp.cross(x2 - x1, n2_n))
434
+ d_one_over_sin_theta_dx1 = d_cos_theta_dx1 * d_one_over_sin_theta_d_cos_theta
435
+
436
+ d_theta_dx1 = d_theta_d_cos_theta * d_cos_theta_dx1
437
+ d2_theta_dx1_dx1 = -wp.outer(d_one_over_sin_theta_dx1, d_cos_theta_dx1)
438
+
439
+ dE_dx1 = e_rest_len * dE_dtheta * d_theta_d_cos_theta * d_cos_theta_dx1
440
+
441
+ d2_E_dx1_dx1 = (
442
+ e_rest_len * stiffness * (wp.outer(d_theta_dx1, d_theta_dx1) + (theta - rest_angle) * d2_theta_dx1_dx1)
443
+ )
444
+
445
+ bending_force = -dE_dx1
446
+ bending_hessian = d2_E_dx1_dx1
447
+ elif v_order == 1:
448
+ d_cos_theta_dx4 = 1.0 / n2_norm * (-wp.cross(x2 - x4, n1_n) + wp.cross(x3 - x4, n1_n))
449
+ d_one_over_sin_theta_dx4 = d_cos_theta_dx4 * d_one_over_sin_theta_d_cos_theta
450
+
451
+ d_theta_dx4 = d_theta_d_cos_theta * d_cos_theta_dx4
452
+ d2_theta_dx4_dx4 = -wp.outer(d_one_over_sin_theta_dx4, d_cos_theta_dx4)
453
+
454
+ dE_dx4 = e_rest_len * dE_dtheta * d_theta_d_cos_theta * d_cos_theta_dx4
455
+ d2_E_dx4_dx4 = (
456
+ e_rest_len * stiffness * (wp.outer(d_theta_dx4, d_theta_dx4) + (theta - rest_angle) * (d2_theta_dx4_dx4))
457
+ )
458
+
459
+ bending_force = -dE_dx4
460
+ bending_hessian = d2_E_dx4_dx4
461
+ elif v_order == 2:
462
+ d_cos_theta_dx2 = 1.0 / n1_norm * wp.cross(x3 - x1, n2_n) - 1.0 / n2_norm * wp.cross(x3 - x4, n1_n)
463
+ dn1_dx2 = mat_vec_cross_from_3_basis(e1, e2, e3, x3 - x1)
464
+ dn2_dx2 = -mat_vec_cross_from_3_basis(e1, e2, e3, x3 - x4)
465
+ d_one_over_sin_theta_dx2 = d_cos_theta_dx2 * d_one_over_sin_theta_d_cos_theta
466
+ d2_cos_theta_dx2_dx2 = -mat_vec_cross(dn2_dx2, (x3 - x1)) / (n1_norm * n2_norm) + mat_vec_cross(
467
+ dn1_dx2, x3 - x4
468
+ ) / (n1_norm * n2_norm)
469
+
470
+ d_theta_dx2 = d_theta_d_cos_theta * d_cos_theta_dx2
471
+ d2_theta_dx2_dx2 = (
472
+ -wp.outer(d_one_over_sin_theta_dx2, d_cos_theta_dx2) - one_over_sin_theta * d2_cos_theta_dx2_dx2
473
+ )
474
+
475
+ dE_dx2 = e_rest_len * dE_dtheta * d_theta_d_cos_theta * d_cos_theta_dx2
476
+ d2_E_dx2_dx2 = (
477
+ e_rest_len * stiffness * (wp.outer(d_theta_dx2, d_theta_dx2) + (theta - rest_angle) * d2_theta_dx2_dx2)
478
+ )
479
+
480
+ bending_force = -dE_dx2
481
+ bending_hessian = d2_E_dx2_dx2
482
+ else:
483
+ d_cos_theta_dx3 = -1.0 / n1_norm * wp.cross(x2 - x1, n2_n) + 1.0 / n2_norm * wp.cross(x2 - x4, n1_n)
484
+ dn1_dx3 = -mat_vec_cross_from_3_basis(e1, e2, e3, x2 - x1)
485
+ dn2_dx3 = mat_vec_cross_from_3_basis(e1, e2, e3, x2 - x4)
486
+ d_one_over_sin_theta_dx3 = d_cos_theta_dx3 * d_one_over_sin_theta_d_cos_theta
487
+ d2_cos_theta_dx3_dx3 = mat_vec_cross(dn2_dx3, (x2 - x1)) / (n1_norm * n2_norm) - mat_vec_cross(
488
+ dn1_dx3, x2 - x4
489
+ ) / (n1_norm * n2_norm)
490
+
491
+ d_theta_dx3 = d_theta_d_cos_theta * d_cos_theta_dx3
492
+ d2_theta_dx3_dx3 = (
493
+ -wp.outer(d_one_over_sin_theta_dx3, d_cos_theta_dx3) - one_over_sin_theta * d2_cos_theta_dx3_dx3
494
+ )
495
+
496
+ dE_dx3 = e_rest_len * dE_dtheta * d_theta_d_cos_theta * d_cos_theta_dx3
497
+
498
+ d2_E_dx3_dx3 = (
499
+ e_rest_len * stiffness * (wp.outer(d_theta_dx3, d_theta_dx3) + (theta - rest_angle) * d2_theta_dx3_dx3)
500
+ )
501
+
502
+ bending_force = -dE_dx3
503
+ bending_hessian = d2_E_dx3_dx3
504
+
505
+ displacement = pos_prev[edge_indices[bending_index, v_order]] - pos[edge_indices[bending_index, v_order]]
506
+ h_d = bending_hessian * (damping / dt)
507
+ f_d = h_d * displacement
508
+
509
+ bending_force = bending_force + f_d
510
+ bending_hessian = bending_hessian + h_d
511
+
512
+ return bending_force, bending_hessian
513
+
514
+
515
+ @wp.func
516
+ def evaluate_ground_contact_force_hessian(
517
+ particle_pos: wp.vec3,
518
+ particle_prev_pos: wp.vec3,
519
+ particle_radius: float,
520
+ ground_normal: wp.vec3,
521
+ ground_level: float,
522
+ soft_contact_ke: float,
523
+ friction_mu: float,
524
+ friction_epsilon: float,
525
+ dt: float,
526
+ ):
527
+ penetration_depth = -(wp.dot(ground_normal, particle_pos) + ground_level - particle_radius)
528
+
529
+ if penetration_depth > 0:
530
+ ground_contact_force_norm = penetration_depth * soft_contact_ke
531
+ ground_contact_force = ground_normal * ground_contact_force_norm
532
+ ground_contact_hessian = soft_contact_ke * wp.outer(ground_normal, ground_normal)
533
+
534
+ dx = particle_pos - particle_prev_pos
535
+
536
+ # friction
537
+ e0, e1 = build_orthonormal_basis(ground_normal)
538
+
539
+ T = mat32(e0[0], e1[0], e0[1], e1[1], e0[2], e1[2])
540
+
541
+ relative_translation = dx
542
+ u = wp.transpose(T) * relative_translation
543
+ eps_u = friction_epsilon * dt
544
+
545
+ friction_force, friction_hessian = compute_friction(friction_mu, ground_contact_force_norm, T, u, eps_u)
546
+ ground_contact_force = ground_contact_force + friction_force
547
+ ground_contact_hessian = ground_contact_hessian + friction_hessian
548
+ else:
549
+ ground_contact_force = wp.vec3(0.0, 0.0, 0.0)
550
+ ground_contact_hessian = wp.mat33(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0)
551
+
552
+ return ground_contact_force, ground_contact_hessian
553
+
554
+
555
+ @wp.func
556
+ def evaluate_body_particle_contact(
557
+ particle_index: int,
558
+ particle_pos: wp.vec3,
559
+ particle_prev_pos: wp.vec3,
560
+ contact_index: int,
561
+ soft_contact_ke: float,
562
+ friction_mu: float,
563
+ friction_epsilon: float,
564
+ particle_radius: wp.array(dtype=float),
565
+ shape_materials: ModelShapeMaterials,
566
+ shape_body: wp.array(dtype=int),
567
+ body_q: wp.array(dtype=wp.transform),
568
+ body_qd: wp.array(dtype=wp.spatial_vector),
569
+ body_com: wp.array(dtype=wp.vec3),
570
+ contact_shape: wp.array(dtype=int),
571
+ contact_body_pos: wp.array(dtype=wp.vec3),
572
+ contact_body_vel: wp.array(dtype=wp.vec3),
573
+ contact_normal: wp.array(dtype=wp.vec3),
574
+ dt: float,
575
+ ):
576
+ shape_index = contact_shape[contact_index]
577
+ body_index = shape_body[shape_index]
578
+
579
+ X_wb = wp.transform_identity()
580
+ X_com = wp.vec3()
581
+ if body_index >= 0:
582
+ X_wb = body_q[body_index]
583
+ X_com = body_com[body_index]
584
+
585
+ # body position in world space
586
+ bx = wp.transform_point(X_wb, contact_body_pos[contact_index])
587
+ r = bx - wp.transform_point(X_wb, X_com)
588
+
589
+ n = contact_normal[contact_index]
590
+
591
+ penetration_depth = -(wp.dot(n, particle_pos - bx) - particle_radius[particle_index])
592
+ if penetration_depth > 0:
593
+ body_contact_force_norm = penetration_depth * soft_contact_ke
594
+ body_contact_force = n * body_contact_force_norm
595
+ body_contact_hessian = soft_contact_ke * wp.outer(n, n)
596
+
597
+ mu = 0.5 * (friction_mu + shape_materials.mu[shape_index])
598
+
599
+ dx = particle_pos - particle_prev_pos
600
+
601
+ # body velocity
602
+ body_v_s = wp.spatial_vector()
603
+ if body_index >= 0:
604
+ body_v_s = body_qd[body_index]
605
+
606
+ body_w = wp.spatial_top(body_v_s)
607
+ body_v = wp.spatial_bottom(body_v_s)
608
+
609
+ # compute the body velocity at the particle position
610
+ bv = body_v + wp.cross(body_w, r) + wp.transform_vector(X_wb, contact_body_vel[contact_index])
611
+
612
+ relative_translation = dx - bv * dt
613
+
614
+ # friction
615
+ e0, e1 = build_orthonormal_basis(n)
616
+
617
+ T = mat32(e0[0], e1[0], e0[1], e1[1], e0[2], e1[2])
618
+
619
+ u = wp.transpose(T) * relative_translation
620
+ eps_u = friction_epsilon * dt
621
+
622
+ friction_force, friction_hessian = compute_friction(mu, body_contact_force_norm, T, u, eps_u)
623
+ body_contact_force = body_contact_force + friction_force
624
+ body_contact_hessian = body_contact_hessian + friction_hessian
625
+ else:
626
+ body_contact_force = wp.vec3(0.0, 0.0, 0.0)
627
+ body_contact_hessian = wp.mat33(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0)
628
+
629
+ return body_contact_force, body_contact_hessian
630
+
631
+
632
+ @wp.func
633
+ def evaluate_self_contact_force_norm(dis: float, collision_radius: float, k: float):
634
+ # Adjust distance and calculate penetration depth
635
+
636
+ penetration_depth = collision_radius - dis
637
+
638
+ # Initialize outputs
639
+ dEdD = wp.float32(0.0)
640
+ d2E_dDdD = wp.float32(0.0)
641
+
642
+ # C2 continuity calculation
643
+ tau = collision_radius * 0.5
644
+ if tau > dis > 1e-5:
645
+ k2 = 0.5 * tau * tau * k
646
+ dEdD = -k2 / dis
647
+ d2E_dDdD = k2 / (dis * dis)
648
+ else:
649
+ dEdD = -k * penetration_depth
650
+ d2E_dDdD = k
651
+
652
+ return dEdD, d2E_dDdD
653
+
654
+
655
+ @wp.func
656
+ def evaluate_edge_edge_contact(
657
+ v: int,
658
+ v_order: int,
659
+ e1: int,
660
+ e2: int,
661
+ pos: wp.array(dtype=wp.vec3),
662
+ pos_prev: wp.array(dtype=wp.vec3),
663
+ edge_indices: wp.array(dtype=wp.int32, ndim=2),
664
+ collision_radius: float,
665
+ collision_stiffness: float,
666
+ friction_coefficient: float,
667
+ friction_epsilon: float,
668
+ dt: float,
669
+ edge_edge_parallel_epsilon: float,
670
+ ):
671
+ r"""
672
+ Returns the edge-edge contact force and hessian, including the friction force.
673
+ Args:
674
+ v:
675
+ v_order: \in {0, 1, 2, 3}, 0, 1 is vertex 0, 1 of e1, 2,3 is vertex 0, 1 of e2
676
+ e0
677
+ e1
678
+ pos
679
+ edge_indices
680
+ collision_radius
681
+ collision_stiffness
682
+ dt
683
+ """
684
+ e1_v1 = edge_indices[e1, 2]
685
+ e1_v2 = edge_indices[e1, 3]
686
+
687
+ e1_v1_pos = pos[e1_v1]
688
+ e1_v2_pos = pos[e1_v2]
689
+
690
+ e2_v1 = edge_indices[e2, 2]
691
+ e2_v2 = edge_indices[e2, 3]
692
+
693
+ e2_v1_pos = pos[e2_v1]
694
+ e2_v2_pos = pos[e2_v2]
695
+
696
+ st = wp.closest_point_edge_edge(e1_v1_pos, e1_v2_pos, e2_v1_pos, e2_v2_pos, edge_edge_parallel_epsilon)
697
+ s = st[0]
698
+ t = st[1]
699
+ e1_vec = e1_v2_pos - e1_v1_pos
700
+ e2_vec = e2_v2_pos - e2_v1_pos
701
+ c1 = e1_v1_pos + e1_vec * s
702
+ c2 = e2_v1_pos + e2_vec * t
703
+
704
+ # c1, c2, s, t = closest_point_edge_edge_2(e1_v1_pos, e1_v2_pos, e2_v1_pos, e2_v2_pos)
705
+
706
+ diff = c1 - c2
707
+ dis = st[2]
708
+ collision_normal = diff / dis
709
+
710
+ if dis < collision_radius:
711
+ bs = wp.vec4(1.0 - s, s, -1.0 + t, -t)
712
+ v_bary = bs[v_order]
713
+
714
+ dEdD, d2E_dDdD = evaluate_self_contact_force_norm(dis, collision_radius, collision_stiffness)
715
+
716
+ collision_force = -dEdD * v_bary * collision_normal
717
+ collision_hessian = d2E_dDdD * v_bary * v_bary * wp.outer(collision_normal, collision_normal)
718
+
719
+ # friction
720
+ c1_prev = pos_prev[e1_v1] + (pos_prev[e1_v2] - pos_prev[e1_v1]) * s
721
+ c2_prev = pos_prev[e2_v1] + (pos_prev[e2_v2] - pos_prev[e2_v1]) * t
722
+
723
+ dx = (c1 - c1_prev) - (c2 - c2_prev)
724
+ e1_vec_normalized = wp.normalize(e1_vec)
725
+ axis_2 = wp.normalize(wp.cross(e1_vec_normalized, collision_normal))
726
+
727
+ T = mat32(
728
+ e1_vec_normalized[0],
729
+ axis_2[0],
730
+ e1_vec_normalized[1],
731
+ axis_2[1],
732
+ e1_vec_normalized[2],
733
+ axis_2[2],
734
+ )
735
+
736
+ u = wp.transpose(T) * dx
737
+ eps_U = friction_epsilon * dt
738
+
739
+ # fmt: off
740
+ if wp.static("contact_force_hessian" in VBD_DEBUG_PRINTING_OPTIONS):
741
+ wp.printf(
742
+ " collision force:\n %f %f %f,\n collision hessian:\n %f %f %f,\n %f %f %f,\n %f %f %f\n",
743
+ 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],
744
+ )
745
+ # fmt: on
746
+
747
+ friction_force, friction_hessian = compute_friction(friction_coefficient, -dEdD, T, u, eps_U)
748
+ friction_force = friction_force * v_bary
749
+ friction_hessian = friction_hessian * v_bary * v_bary
750
+
751
+ # fmt: off
752
+ if wp.static("contact_force_hessian" in VBD_DEBUG_PRINTING_OPTIONS):
753
+ wp.printf(
754
+ " friction force:\n %f %f %f,\n friction hessian:\n %f %f %f,\n %f %f %f,\n %f %f %f\n",
755
+ 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],
756
+ )
757
+ # fmt: on
758
+
759
+ collision_force = collision_force + friction_force
760
+ collision_hessian = collision_hessian + friction_hessian
761
+ else:
762
+ collision_force = wp.vec3(0.0, 0.0, 0.0)
763
+ collision_hessian = wp.mat33(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0)
764
+
765
+ return collision_force, collision_hessian
766
+
767
+
768
+ @wp.func
769
+ def evaluate_vertex_triangle_collision_force_hessian(
770
+ v: int,
771
+ v_order: int,
772
+ tri: int,
773
+ pos: wp.array(dtype=wp.vec3),
774
+ pos_prev: wp.array(dtype=wp.vec3),
775
+ tri_indices: wp.array(dtype=wp.int32, ndim=2),
776
+ collision_radius: float,
777
+ collision_stiffness: float,
778
+ friction_coefficient: float,
779
+ friction_epsilon: float,
780
+ dt: float,
781
+ ):
782
+ a = pos[tri_indices[tri, 0]]
783
+ b = pos[tri_indices[tri, 1]]
784
+ c = pos[tri_indices[tri, 2]]
785
+
786
+ p = pos[v]
787
+
788
+ closest_p, bary, feature_type = triangle_closest_point(a, b, c, p)
789
+
790
+ diff = p - closest_p
791
+ dis = wp.length(diff)
792
+ collision_normal = diff / dis
793
+
794
+ if dis < collision_radius:
795
+ bs = wp.vec4(-bary[0], -bary[1], -bary[2], 1.0)
796
+ v_bary = bs[v_order]
797
+
798
+ dEdD, d2E_dDdD = evaluate_self_contact_force_norm(dis, collision_radius, collision_stiffness)
799
+
800
+ collision_force = -dEdD * v_bary * collision_normal
801
+ collision_hessian = d2E_dDdD * v_bary * v_bary * wp.outer(collision_normal, collision_normal)
802
+
803
+ # friction force
804
+ dx_v = p - pos_prev[v]
805
+
806
+ closest_p_prev = (
807
+ bary[0] * pos_prev[tri_indices[tri, 0]]
808
+ + bary[1] * pos_prev[tri_indices[tri, 1]]
809
+ + bary[2] * pos_prev[tri_indices[tri, 2]]
810
+ )
811
+
812
+ dx = dx_v - (closest_p - closest_p_prev)
813
+
814
+ e0 = wp.normalize(b - a)
815
+ e1 = wp.normalize(wp.cross(e0, collision_normal))
816
+
817
+ T = mat32(e0[0], e1[0], e0[1], e1[1], e0[2], e1[2])
818
+
819
+ u = wp.transpose(T) * dx
820
+ eps_U = friction_epsilon * dt
821
+
822
+ friction_force, friction_hessian = compute_friction(friction_coefficient, -dEdD, T, u, eps_U)
823
+
824
+ # fmt: off
825
+ if wp.static("contact_force_hessian" in VBD_DEBUG_PRINTING_OPTIONS):
826
+ wp.printf(
827
+ "v: %d dEdD: %f\nnormal force: %f %f %f\nfriction force: %f %f %f\n",
828
+ v,
829
+ dEdD,
830
+ collision_force[0], collision_force[1], collision_force[2], friction_force[0], friction_force[1], friction_force[2],
831
+ )
832
+ # fmt: on
833
+
834
+ collision_force = collision_force + v_bary * friction_force
835
+ collision_hessian = collision_hessian + v_bary * v_bary * friction_hessian
836
+ else:
837
+ collision_force = wp.vec3(0.0, 0.0, 0.0)
838
+ collision_hessian = wp.mat33(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0)
839
+
840
+ return collision_force, collision_hessian
841
+
842
+
843
+ @wp.func
844
+ def compute_friction(mu: float, normal_contact_force: float, T: mat32, u: wp.vec2, eps_u: float):
845
+ """
846
+ Returns the 1D friction force and hessian.
847
+ Args:
848
+ mu: Friction coefficient.
849
+ normal_contact_force: normal contact force.
850
+ T: Transformation matrix (3x2 matrix).
851
+ u: 2D displacement vector.
852
+ """
853
+ # Friction
854
+ u_norm = wp.length(u)
855
+
856
+ if u_norm > 0.0:
857
+ # IPC friction
858
+ if u_norm > eps_u:
859
+ # constant stage
860
+ f1_SF_over_x = 1.0 / u_norm
861
+ else:
862
+ # smooth transition
863
+ f1_SF_over_x = (-u_norm / eps_u + 2.0) / eps_u
864
+
865
+ force = -mu * normal_contact_force * T * (f1_SF_over_x * u)
866
+
867
+ # Different from IPC, we treat the contact normal as constant
868
+ # this significantly improves the stability
869
+ hessian = mu * normal_contact_force * T * (f1_SF_over_x * wp.identity(2, float)) * wp.transpose(T)
870
+ else:
871
+ force = wp.vec3(0.0, 0.0, 0.0)
872
+ hessian = wp.mat33(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0)
873
+
874
+ return force, hessian
875
+
876
+
877
+ @wp.kernel
878
+ def forward_step(
879
+ dt: float,
880
+ gravity: wp.vec3,
881
+ prev_pos: wp.array(dtype=wp.vec3),
882
+ pos: wp.array(dtype=wp.vec3),
883
+ vel: wp.array(dtype=wp.vec3),
884
+ inv_mass: wp.array(dtype=float),
885
+ external_force: wp.array(dtype=wp.vec3),
886
+ particle_flags: wp.array(dtype=wp.uint32),
887
+ inertia: wp.array(dtype=wp.vec3),
888
+ ):
889
+ particle = wp.tid()
890
+
891
+ prev_pos[particle] = pos[particle]
892
+ if not particle_flags[particle] & PARTICLE_FLAG_ACTIVE:
893
+ inertia[particle] = prev_pos[particle]
894
+ return
895
+ vel_new = vel[particle] + (gravity + external_force[particle] * inv_mass[particle]) * dt
896
+ pos[particle] = pos[particle] + vel_new * dt
897
+ inertia[particle] = pos[particle]
898
+
899
+
900
+ @wp.kernel
901
+ def forward_step_penetration_free(
902
+ dt: float,
903
+ gravity: wp.vec3,
904
+ prev_pos: wp.array(dtype=wp.vec3),
905
+ pos: wp.array(dtype=wp.vec3),
906
+ vel: wp.array(dtype=wp.vec3),
907
+ inv_mass: wp.array(dtype=float),
908
+ external_force: wp.array(dtype=wp.vec3),
909
+ particle_flags: wp.array(dtype=wp.uint32),
910
+ pos_prev_collision_detection: wp.array(dtype=wp.vec3),
911
+ particle_conservative_bounds: wp.array(dtype=float),
912
+ inertia: wp.array(dtype=wp.vec3),
913
+ ):
914
+ particle_index = wp.tid()
915
+
916
+ prev_pos[particle_index] = pos[particle_index]
917
+ if not particle_flags[particle_index] & PARTICLE_FLAG_ACTIVE:
918
+ inertia[particle_index] = prev_pos[particle_index]
919
+ return
920
+ vel_new = vel[particle_index] + (gravity + external_force[particle_index] * inv_mass[particle_index]) * dt
921
+ pos_inertia = pos[particle_index] + vel_new * dt
922
+ inertia[particle_index] = pos_inertia
923
+
924
+ pos[particle_index] = apply_conservative_bound_truncation(
925
+ particle_index, pos_inertia, pos_prev_collision_detection, particle_conservative_bounds
926
+ )
927
+
928
+
929
+ @wp.kernel
930
+ def compute_particle_conservative_bound(
931
+ # inputs
932
+ conservative_bound_relaxation: float,
933
+ collision_query_radius: float,
934
+ adjacency: ForceElementAdjacencyInfo,
935
+ collision_info: TriMeshCollisionInfo,
936
+ # outputs
937
+ particle_conservative_bounds: wp.array(dtype=float),
938
+ ):
939
+ particle_index = wp.tid()
940
+ min_dist = wp.min(collision_query_radius, collision_info.vertex_colliding_triangles_min_dist[particle_index])
941
+
942
+ # bound from neighbor triangles
943
+ for i_adj_tri in range(
944
+ get_vertex_num_adjacent_faces(
945
+ adjacency,
946
+ particle_index,
947
+ )
948
+ ):
949
+ tri_index, vertex_order = get_vertex_adjacent_face_id_order(
950
+ adjacency,
951
+ particle_index,
952
+ i_adj_tri,
953
+ )
954
+ min_dist = wp.min(min_dist, collision_info.triangle_colliding_vertices_min_dist[tri_index])
955
+
956
+ # bound from neighbor edges
957
+ for i_adj_edge in range(
958
+ get_vertex_num_adjacent_edges(
959
+ adjacency,
960
+ particle_index,
961
+ )
962
+ ):
963
+ nei_edge_index, vertex_order_on_edge = get_vertex_adjacent_edge_id_order(
964
+ adjacency,
965
+ particle_index,
966
+ i_adj_edge,
967
+ )
968
+ # vertex is on the edge; otherwise it only effects the bending energy
969
+ if vertex_order_on_edge == 2 or vertex_order_on_edge == 3:
970
+ # collisions of neighbor edges
971
+ min_dist = wp.min(min_dist, collision_info.edge_colliding_edges_min_dist[nei_edge_index])
972
+
973
+ particle_conservative_bounds[particle_index] = conservative_bound_relaxation * min_dist
974
+
975
+
976
+ @wp.kernel
977
+ def validate_conservative_bound(
978
+ pos: wp.array(dtype=wp.vec3),
979
+ pos_prev_collision_detection: wp.array(dtype=wp.vec3),
980
+ particle_conservative_bounds: wp.array(dtype=float),
981
+ ):
982
+ v_index = wp.tid()
983
+
984
+ displacement = wp.length(pos[v_index] - pos_prev_collision_detection[v_index])
985
+
986
+ if displacement > particle_conservative_bounds[v_index] * 1.01 and displacement > 1e-5:
987
+ # wp.expect_eq(displacement <= particle_conservative_bounds[v_index] * 1.01, True)
988
+ wp.printf(
989
+ "Vertex %d has moved by %f exceeded the limit of %f\n",
990
+ v_index,
991
+ displacement,
992
+ particle_conservative_bounds[v_index],
993
+ )
994
+
995
+
996
+ @wp.func
997
+ def apply_conservative_bound_truncation(
998
+ v_index: wp.int32,
999
+ pos_new: wp.vec3,
1000
+ pos_prev_collision_detection: wp.array(dtype=wp.vec3),
1001
+ particle_conservative_bounds: wp.array(dtype=float),
1002
+ ):
1003
+ particle_pos_prev_collision_detection = pos_prev_collision_detection[v_index]
1004
+ accumulated_displacement = pos_new - particle_pos_prev_collision_detection
1005
+ conservative_bound = particle_conservative_bounds[v_index]
1006
+
1007
+ accumulated_displacement_norm = wp.length(accumulated_displacement)
1008
+ if accumulated_displacement_norm > conservative_bound and conservative_bound > 1e-5:
1009
+ accumulated_displacement_norm_truncated = conservative_bound
1010
+ accumulated_displacement = accumulated_displacement * (
1011
+ accumulated_displacement_norm_truncated / accumulated_displacement_norm
1012
+ )
1013
+
1014
+ return particle_pos_prev_collision_detection + accumulated_displacement
1015
+ else:
1016
+ return pos_new
1017
+
1018
+
1019
+ @wp.kernel
1020
+ def VBD_solve_trimesh_no_self_contact(
1021
+ dt: float,
1022
+ particle_ids_in_color: wp.array(dtype=wp.int32),
1023
+ prev_pos: wp.array(dtype=wp.vec3),
1024
+ pos: wp.array(dtype=wp.vec3),
1025
+ pos_new: wp.array(dtype=wp.vec3),
1026
+ vel: wp.array(dtype=wp.vec3),
1027
+ mass: wp.array(dtype=float),
1028
+ inertia: wp.array(dtype=wp.vec3),
1029
+ particle_flags: wp.array(dtype=wp.uint32),
1030
+ tri_indices: wp.array(dtype=wp.int32, ndim=2),
1031
+ tri_poses: wp.array(dtype=wp.mat22),
1032
+ tri_materials: wp.array(dtype=float, ndim=2),
1033
+ tri_areas: wp.array(dtype=float),
1034
+ edge_indices: wp.array(dtype=wp.int32, ndim=2),
1035
+ edge_rest_angles: wp.array(dtype=float),
1036
+ edge_rest_length: wp.array(dtype=float),
1037
+ edge_bending_properties: wp.array(dtype=float, ndim=2),
1038
+ adjacency: ForceElementAdjacencyInfo,
1039
+ # contact info
1040
+ # self contact
1041
+ soft_contact_ke: float,
1042
+ friction_mu: float,
1043
+ friction_epsilon: float,
1044
+ # body-particle contact
1045
+ particle_radius: wp.array(dtype=float),
1046
+ body_particle_contact_buffer_pre_alloc: int,
1047
+ body_particle_contact_buffer: wp.array(dtype=int),
1048
+ body_particle_contact_count: wp.array(dtype=int),
1049
+ shape_materials: ModelShapeMaterials,
1050
+ shape_body: wp.array(dtype=int),
1051
+ body_q: wp.array(dtype=wp.transform),
1052
+ body_qd: wp.array(dtype=wp.spatial_vector),
1053
+ body_com: wp.array(dtype=wp.vec3),
1054
+ contact_shape: wp.array(dtype=int),
1055
+ contact_body_pos: wp.array(dtype=wp.vec3),
1056
+ contact_body_vel: wp.array(dtype=wp.vec3),
1057
+ contact_normal: wp.array(dtype=wp.vec3),
1058
+ # ground-particle contact
1059
+ has_ground: bool,
1060
+ ground: wp.array(dtype=float),
1061
+ ):
1062
+ tid = wp.tid()
1063
+
1064
+ particle_index = particle_ids_in_color[tid]
1065
+
1066
+ if not particle_flags[particle_index] & PARTICLE_FLAG_ACTIVE:
1067
+ return
1068
+
1069
+ particle_pos = pos[particle_index]
1070
+ particle_prev_pos = pos[particle_index]
1071
+
1072
+ dt_sqr_reciprocal = 1.0 / (dt * dt)
1073
+
1074
+ # inertia force and hessian
1075
+ f = mass[particle_index] * (inertia[particle_index] - pos[particle_index]) * (dt_sqr_reciprocal)
1076
+ h = mass[particle_index] * dt_sqr_reciprocal * wp.identity(n=3, dtype=float)
1077
+
1078
+ # elastic force and hessian
1079
+ for i_adj_tri in range(get_vertex_num_adjacent_faces(adjacency, particle_index)):
1080
+ tri_id, particle_order = get_vertex_adjacent_face_id_order(adjacency, particle_index, i_adj_tri)
1081
+
1082
+ # fmt: off
1083
+ if wp.static("connectivity" in VBD_DEBUG_PRINTING_OPTIONS):
1084
+ wp.printf(
1085
+ "particle: %d | num_adj_faces: %d | ",
1086
+ particle_index,
1087
+ get_vertex_num_adjacent_faces(particle_index, adjacency),
1088
+ )
1089
+ wp.printf("i_face: %d | face id: %d | v_order: %d | ", i_adj_tri, tri_id, particle_order)
1090
+ wp.printf(
1091
+ "face: %d %d %d\n",
1092
+ tri_indices[tri_id, 0],
1093
+ tri_indices[tri_id, 1],
1094
+ tri_indices[tri_id, 2],
1095
+ )
1096
+ # fmt: on
1097
+
1098
+ f_tri, h_tri = evaluate_stvk_force_hessian(
1099
+ tri_id,
1100
+ particle_order,
1101
+ pos,
1102
+ tri_indices,
1103
+ tri_poses[tri_id],
1104
+ tri_areas[tri_id],
1105
+ tri_materials[tri_id, 0],
1106
+ tri_materials[tri_id, 1],
1107
+ tri_materials[tri_id, 2],
1108
+ )
1109
+ # compute damping
1110
+ k_d = tri_materials[tri_id, 2]
1111
+ h_d = h_tri * (k_d / dt)
1112
+
1113
+ f_d = h_d * (prev_pos[particle_index] - pos[particle_index])
1114
+
1115
+ f = f + f_tri + f_d
1116
+ h = h + h_tri + h_d
1117
+
1118
+ # fmt: off
1119
+ if wp.static("elasticity_force_hessian" in VBD_DEBUG_PRINTING_OPTIONS):
1120
+ wp.printf(
1121
+ "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",
1122
+ particle_index,
1123
+ i_adj_tri,
1124
+ particle_order,
1125
+ 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],
1126
+ )
1127
+ # fmt: on
1128
+
1129
+ for i_adj_edge in range(get_vertex_num_adjacent_edges(adjacency, particle_index)):
1130
+ nei_edge_index, vertex_order_on_edge = get_vertex_adjacent_edge_id_order(adjacency, particle_index, i_adj_edge)
1131
+ f_edge, h_edge = evaluate_dihedral_angle_based_bending_force_hessian(
1132
+ nei_edge_index,
1133
+ vertex_order_on_edge,
1134
+ pos,
1135
+ prev_pos,
1136
+ edge_indices,
1137
+ edge_rest_angles,
1138
+ edge_rest_length,
1139
+ edge_bending_properties[nei_edge_index, 0],
1140
+ edge_bending_properties[nei_edge_index, 1],
1141
+ dt,
1142
+ )
1143
+
1144
+ f = f + f_edge
1145
+ h = h + h_edge
1146
+
1147
+ # body-particle contact
1148
+ particle_contact_count = min(body_particle_contact_count[particle_index], body_particle_contact_buffer_pre_alloc)
1149
+
1150
+ offset = body_particle_contact_buffer_pre_alloc * particle_index
1151
+ for contact_counter in range(particle_contact_count):
1152
+ # the index to access body-particle data, which is size-variable and only contains active contact
1153
+ contact_index = body_particle_contact_buffer[offset + contact_counter]
1154
+
1155
+ body_contact_force, body_contact_hessian = evaluate_body_particle_contact(
1156
+ particle_index,
1157
+ particle_pos,
1158
+ particle_prev_pos,
1159
+ contact_index,
1160
+ soft_contact_ke,
1161
+ friction_mu,
1162
+ friction_epsilon,
1163
+ particle_radius,
1164
+ shape_materials,
1165
+ shape_body,
1166
+ body_q,
1167
+ body_qd,
1168
+ body_com,
1169
+ contact_shape,
1170
+ contact_body_pos,
1171
+ contact_body_vel,
1172
+ contact_normal,
1173
+ dt,
1174
+ )
1175
+
1176
+ f = f + body_contact_force
1177
+ h = h + body_contact_hessian
1178
+
1179
+ if has_ground:
1180
+ ground_normal = wp.vec3(ground[0], ground[1], ground[2])
1181
+ ground_level = ground[3]
1182
+ ground_contact_force, ground_contact_hessian = evaluate_ground_contact_force_hessian(
1183
+ particle_pos,
1184
+ particle_prev_pos,
1185
+ particle_radius[particle_index],
1186
+ ground_normal,
1187
+ ground_level,
1188
+ soft_contact_ke,
1189
+ friction_mu,
1190
+ friction_epsilon,
1191
+ dt,
1192
+ )
1193
+
1194
+ f = f + ground_contact_force
1195
+ h = h + ground_contact_hessian
1196
+
1197
+ if abs(wp.determinant(h)) > 1e-5:
1198
+ hInv = wp.inverse(h)
1199
+ pos_new[particle_index] = particle_pos + hInv * f
1200
+
1201
+
1202
+ @wp.kernel
1203
+ def VBD_copy_particle_positions_back(
1204
+ particle_ids_in_color: wp.array(dtype=wp.int32),
1205
+ pos: wp.array(dtype=wp.vec3),
1206
+ pos_new: wp.array(dtype=wp.vec3),
1207
+ ):
1208
+ tid = wp.tid()
1209
+ particle = particle_ids_in_color[tid]
1210
+
1211
+ pos[particle] = pos_new[particle]
1212
+
1213
+
1214
+ @wp.kernel
1215
+ def update_velocity(
1216
+ dt: float, prev_pos: wp.array(dtype=wp.vec3), pos: wp.array(dtype=wp.vec3), vel: wp.array(dtype=wp.vec3)
1217
+ ):
1218
+ particle = wp.tid()
1219
+ vel[particle] = (pos[particle] - prev_pos[particle]) / dt
1220
+
1221
+
1222
+ @wp.kernel
1223
+ def convert_body_particle_contact_data_kernel(
1224
+ # inputs
1225
+ body_particle_contact_buffer_pre_alloc: int,
1226
+ soft_contact_particle: wp.array(dtype=int),
1227
+ contact_count: wp.array(dtype=int),
1228
+ contact_max: int,
1229
+ # outputs
1230
+ body_particle_contact_buffer: wp.array(dtype=int),
1231
+ body_particle_contact_count: wp.array(dtype=int),
1232
+ ):
1233
+ contact_index = wp.tid()
1234
+ count = min(contact_max, contact_count[0])
1235
+ if contact_index >= count:
1236
+ return
1237
+
1238
+ particle_index = soft_contact_particle[contact_index]
1239
+ offset = particle_index * body_particle_contact_buffer_pre_alloc
1240
+
1241
+ contact_counter = wp.atomic_add(body_particle_contact_count, particle_index, 1)
1242
+ if contact_counter < body_particle_contact_buffer_pre_alloc:
1243
+ body_particle_contact_buffer[offset + contact_counter] = contact_index
1244
+
1245
+
1246
+ @wp.kernel
1247
+ def VBD_solve_trimesh_with_self_contact_penetration_free(
1248
+ dt: float,
1249
+ particle_ids_in_color: wp.array(dtype=wp.int32),
1250
+ pos_prev: wp.array(dtype=wp.vec3),
1251
+ pos: wp.array(dtype=wp.vec3),
1252
+ pos_new: wp.array(dtype=wp.vec3),
1253
+ vel: wp.array(dtype=wp.vec3),
1254
+ mass: wp.array(dtype=float),
1255
+ inertia: wp.array(dtype=wp.vec3),
1256
+ particle_flags: wp.array(dtype=wp.uint32),
1257
+ tri_indices: wp.array(dtype=wp.int32, ndim=2),
1258
+ tri_poses: wp.array(dtype=wp.mat22),
1259
+ tri_materials: wp.array(dtype=float, ndim=2),
1260
+ tri_areas: wp.array(dtype=float),
1261
+ edge_indices: wp.array(dtype=wp.int32, ndim=2),
1262
+ edge_rest_angles: wp.array(dtype=float),
1263
+ edge_rest_length: wp.array(dtype=float),
1264
+ edge_bending_properties: wp.array(dtype=float, ndim=2),
1265
+ adjacency: ForceElementAdjacencyInfo,
1266
+ # contact info
1267
+ # self contact
1268
+ collision_info: TriMeshCollisionInfo,
1269
+ collision_radius: float,
1270
+ soft_contact_ke: float,
1271
+ friction_mu: float,
1272
+ friction_epsilon: float,
1273
+ pos_prev_collision_detection: wp.array(dtype=wp.vec3),
1274
+ particle_conservative_bounds: wp.array(dtype=float),
1275
+ edge_edge_parallel_epsilon: float,
1276
+ # body-particle contact
1277
+ particle_radius: wp.array(dtype=float),
1278
+ body_particle_contact_buffer_pre_alloc: int,
1279
+ body_particle_contact_buffer: wp.array(dtype=int),
1280
+ body_particle_contact_count: wp.array(dtype=int),
1281
+ shape_materials: ModelShapeMaterials,
1282
+ shape_body: wp.array(dtype=int),
1283
+ body_q: wp.array(dtype=wp.transform),
1284
+ body_qd: wp.array(dtype=wp.spatial_vector),
1285
+ body_com: wp.array(dtype=wp.vec3),
1286
+ contact_shape: wp.array(dtype=int),
1287
+ contact_body_pos: wp.array(dtype=wp.vec3),
1288
+ contact_body_vel: wp.array(dtype=wp.vec3),
1289
+ contact_normal: wp.array(dtype=wp.vec3),
1290
+ # ground-particle contact
1291
+ has_ground: bool,
1292
+ ground: wp.array(dtype=float),
1293
+ ):
1294
+ t_id = wp.tid()
1295
+
1296
+ particle_index = particle_ids_in_color[t_id]
1297
+ particle_pos = pos[particle_index]
1298
+ particle_prev_pos = pos_prev[particle_index]
1299
+
1300
+ if not particle_flags[particle_index] & PARTICLE_FLAG_ACTIVE:
1301
+ return
1302
+
1303
+ dt_sqr_reciprocal = 1.0 / (dt * dt)
1304
+
1305
+ # inertia force and hessian
1306
+ f = mass[particle_index] * (inertia[particle_index] - pos[particle_index]) * (dt_sqr_reciprocal)
1307
+ h = mass[particle_index] * dt_sqr_reciprocal * wp.identity(n=3, dtype=float)
1308
+
1309
+ # fmt: off
1310
+ if wp.static("inertia_force_hessian" in VBD_DEBUG_PRINTING_OPTIONS):
1311
+ wp.printf(
1312
+ "particle: %d after accumulate inertia\nforce:\n %f %f %f, \nhessian:, \n%f %f %f, \n%f %f %f, \n%f %f %f\n",
1313
+ particle_index,
1314
+ 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],
1315
+ )
1316
+
1317
+ # v-side of the v-f collision force
1318
+ if wp.static("contact_info" in VBD_DEBUG_PRINTING_OPTIONS):
1319
+ wp.printf("Has %d colliding triangles\n", get_vertex_colliding_triangles_count(collision_info, particle_index))
1320
+ for i_v_collision in range(get_vertex_colliding_triangles_count(collision_info, particle_index)):
1321
+ colliding_t = get_vertex_colliding_triangles(collision_info, particle_index, i_v_collision)
1322
+ if wp.static("contact_info" in VBD_DEBUG_PRINTING_OPTIONS):
1323
+ wp.printf(
1324
+ "vertex %d is colliding with triangle: %d-(%d, %d, %d)",
1325
+ particle_index,
1326
+ colliding_t,
1327
+ tri_indices[colliding_t, 0],
1328
+ tri_indices[colliding_t, 1],
1329
+ tri_indices[colliding_t, 2],
1330
+ )
1331
+ # fmt: on
1332
+
1333
+ collision_force, collision_hessian = evaluate_vertex_triangle_collision_force_hessian(
1334
+ particle_index,
1335
+ 3,
1336
+ colliding_t,
1337
+ pos,
1338
+ pos_prev,
1339
+ tri_indices,
1340
+ collision_radius,
1341
+ soft_contact_ke,
1342
+ friction_mu,
1343
+ friction_epsilon,
1344
+ dt,
1345
+ )
1346
+ f = f + collision_force
1347
+ h = h + collision_hessian
1348
+
1349
+ # fmt: off
1350
+ if wp.static("contact_force_hessian" in VBD_DEBUG_PRINTING_OPTIONS):
1351
+ wp.printf(
1352
+ "vertex: %d collision %d:\nforce:\n %f %f %f, \nhessian:, \n%f %f %f, \n%f %f %f, \n%f %f %f\n",
1353
+ particle_index,
1354
+ i_v_collision,
1355
+ 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],
1356
+ )
1357
+ # fmt: on
1358
+
1359
+ # elastic force and hessian
1360
+ for i_adj_tri in range(get_vertex_num_adjacent_faces(adjacency, particle_index)):
1361
+ tri_index, vertex_order = get_vertex_adjacent_face_id_order(adjacency, particle_index, i_adj_tri)
1362
+
1363
+ # fmt: off
1364
+ if wp.static("connectivity" in VBD_DEBUG_PRINTING_OPTIONS):
1365
+ wp.printf(
1366
+ "particle: %d | num_adj_faces: %d | ",
1367
+ particle_index,
1368
+ get_vertex_num_adjacent_faces(particle_index, adjacency),
1369
+ )
1370
+ wp.printf("i_face: %d | face id: %d | v_order: %d | ", i_adj_tri, tri_index, vertex_order)
1371
+ wp.printf(
1372
+ "face: %d %d %d\n",
1373
+ tri_indices[tri_index, 0],
1374
+ tri_indices[tri_index, 1],
1375
+ tri_indices[tri_index, 2],
1376
+ )
1377
+ # fmt: on
1378
+
1379
+ f_tri, h_tri = evaluate_stvk_force_hessian(
1380
+ tri_index,
1381
+ vertex_order,
1382
+ pos,
1383
+ tri_indices,
1384
+ tri_poses[tri_index],
1385
+ tri_areas[tri_index],
1386
+ tri_materials[tri_index, 0],
1387
+ tri_materials[tri_index, 1],
1388
+ tri_materials[tri_index, 2],
1389
+ )
1390
+ # compute damping
1391
+ k_d = tri_materials[tri_index, 2]
1392
+ h_d = h_tri * (k_d / dt)
1393
+
1394
+ f_d = h_d * (pos_prev[particle_index] - pos[particle_index])
1395
+
1396
+ f = f + f_tri + f_d
1397
+ h = h + h_tri + h_d
1398
+
1399
+ # t-side of vt-collision from the neighbor triangles
1400
+ # fmt: off
1401
+ if wp.static("contact_info" in VBD_DEBUG_PRINTING_OPTIONS):
1402
+ wp.printf(
1403
+ "Nei triangle %d has %d colliding vertice\n",
1404
+ tri_index,
1405
+ get_triangle_colliding_vertices_count(collision_info, tri_index),
1406
+ )
1407
+ # fmt: on
1408
+ for i_t_collision in range(get_triangle_colliding_vertices_count(collision_info, tri_index)):
1409
+ colliding_v = get_triangle_colliding_vertices(collision_info, tri_index, i_t_collision)
1410
+
1411
+ collision_force, collision_hessian = evaluate_vertex_triangle_collision_force_hessian(
1412
+ colliding_v,
1413
+ vertex_order,
1414
+ tri_index,
1415
+ pos,
1416
+ pos_prev,
1417
+ tri_indices,
1418
+ collision_radius,
1419
+ soft_contact_ke,
1420
+ friction_mu,
1421
+ friction_epsilon,
1422
+ dt,
1423
+ )
1424
+
1425
+ f = f + collision_force
1426
+ h = h + collision_hessian
1427
+
1428
+ # edge-edge collision force and hessian
1429
+ for i_adj_edge in range(get_vertex_num_adjacent_edges(adjacency, particle_index)):
1430
+ nei_edge_index, vertex_order_on_edge = get_vertex_adjacent_edge_id_order(adjacency, particle_index, i_adj_edge)
1431
+ # vertex is on the edge; otherwise it only effects the bending energy n
1432
+ if edge_bending_properties[nei_edge_index, 0] != 0:
1433
+ f_edge, h_edge = evaluate_dihedral_angle_based_bending_force_hessian(
1434
+ nei_edge_index, vertex_order_on_edge, pos, pos_prev, edge_indices, edge_rest_angles, edge_rest_length,
1435
+ edge_bending_properties[nei_edge_index, 0], edge_bending_properties[nei_edge_index, 1], dt
1436
+ )
1437
+
1438
+ f = f + f_edge
1439
+ h = h + h_edge
1440
+
1441
+ if vertex_order_on_edge == 2 or vertex_order_on_edge == 3:
1442
+ # collisions of neighbor triangles
1443
+ if wp.static("contact_info" in VBD_DEBUG_PRINTING_OPTIONS):
1444
+ wp.printf(
1445
+ "Nei edge %d has %d colliding edge\n",
1446
+ nei_edge_index,
1447
+ get_edge_colliding_edges_count(collision_info, nei_edge_index),
1448
+ )
1449
+ for i_e_collision in range(get_edge_colliding_edges_count(collision_info, nei_edge_index)):
1450
+ colliding_e = get_edge_colliding_edges(collision_info, nei_edge_index, i_e_collision)
1451
+
1452
+ collision_force, collision_hessian = evaluate_edge_edge_contact(
1453
+ particle_index,
1454
+ vertex_order_on_edge - 2,
1455
+ nei_edge_index,
1456
+ colliding_e,
1457
+ pos,
1458
+ pos_prev,
1459
+ edge_indices,
1460
+ collision_radius,
1461
+ soft_contact_ke,
1462
+ friction_mu,
1463
+ friction_epsilon,
1464
+ dt,
1465
+ edge_edge_parallel_epsilon,
1466
+ )
1467
+ f = f + collision_force
1468
+ h = h + collision_hessian
1469
+
1470
+ # fmt: off
1471
+ if wp.static("contact_force_hessian" in VBD_DEBUG_PRINTING_OPTIONS):
1472
+ wp.printf(
1473
+ "vertex: %d edge %d collision %d:\nforce:\n %f %f %f, \nhessian:, \n%f %f %f, \n%f %f %f, \n%f %f %f\n",
1474
+ particle_index,
1475
+ nei_edge_index,
1476
+ i_e_collision,
1477
+ 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],
1478
+ )
1479
+ # fmt: on
1480
+
1481
+ # body-particle contact
1482
+ particle_contact_count = min(body_particle_contact_count[particle_index], body_particle_contact_buffer_pre_alloc)
1483
+
1484
+ offset = body_particle_contact_buffer_pre_alloc * particle_index
1485
+ for contact_counter in range(particle_contact_count):
1486
+ # the index to access body-particle data, which is size-variable and only contains active contact
1487
+ contact_index = body_particle_contact_buffer[offset + contact_counter]
1488
+
1489
+ body_contact_force, body_contact_hessian = evaluate_body_particle_contact(
1490
+ particle_index,
1491
+ particle_pos,
1492
+ particle_prev_pos,
1493
+ contact_index,
1494
+ soft_contact_ke,
1495
+ friction_mu,
1496
+ friction_epsilon,
1497
+ particle_radius,
1498
+ shape_materials,
1499
+ shape_body,
1500
+ body_q,
1501
+ body_qd,
1502
+ body_com,
1503
+ contact_shape,
1504
+ contact_body_pos,
1505
+ contact_body_vel,
1506
+ contact_normal,
1507
+ dt,
1508
+ )
1509
+
1510
+ f = f + body_contact_force
1511
+ h = h + body_contact_hessian
1512
+
1513
+ if has_ground:
1514
+ ground_normal = wp.vec3(ground[0], ground[1], ground[2])
1515
+ ground_level = ground[3]
1516
+ ground_contact_force, ground_contact_hessian = evaluate_ground_contact_force_hessian(
1517
+ particle_pos,
1518
+ particle_prev_pos,
1519
+ particle_radius[particle_index],
1520
+ ground_normal,
1521
+ ground_level,
1522
+ soft_contact_ke,
1523
+ friction_mu,
1524
+ friction_epsilon,
1525
+ dt,
1526
+ )
1527
+
1528
+ f = f + ground_contact_force
1529
+ h = h + ground_contact_hessian
1530
+
1531
+ # fmt: off
1532
+ if wp.static("overall_force_hessian" in VBD_DEBUG_PRINTING_OPTIONS):
1533
+ wp.printf(
1534
+ "vertex: %d final\noverall force:\n %f %f %f, \noverall hessian:, \n%f %f %f, \n%f %f %f, \n%f %f %f\n",
1535
+ particle_index,
1536
+ 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],
1537
+ )
1538
+ # fmt: on
1539
+
1540
+ if abs(wp.determinant(h)) > 1e-5:
1541
+ h_inv = wp.inverse(h)
1542
+ particle_pos_new = pos[particle_index] + h_inv * f
1543
+
1544
+ pos_new[particle_index] = apply_conservative_bound_truncation(
1545
+ particle_index, particle_pos_new, pos_prev_collision_detection, particle_conservative_bounds
1546
+ )
1547
+
1548
+
1549
+ class VBDIntegrator(Integrator):
1550
+ """An implicit integrator using Vertex Block Descent (VBD) for cloth simulation.
1551
+
1552
+ References:
1553
+ - 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
1554
+
1555
+ Note that VBDIntegrator's constructor requires a :class:`Model` object as input, so that it can do some precomputation and preallocate the space.
1556
+ After construction, you must provide the same :class:`Model` object that you used that was used during construction.
1557
+ Currently, you must manually provide particle coloring and assign it to `model.particle_coloring` to make VBD work.
1558
+
1559
+ VBDIntegrator.simulate accepts three arguments: class:`Model`, :class:`State`, and :class:`Control` (optional) objects, this time-integrator
1560
+ may be used to advance the simulation state forward in time.
1561
+
1562
+ Example
1563
+ -------
1564
+
1565
+ .. code-block:: python
1566
+
1567
+ model.particle_coloring = # load or generate particle coloring
1568
+ integrator = wp.VBDIntegrator(model)
1569
+
1570
+ # simulation loop
1571
+ for i in range(100):
1572
+ state = integrator.simulate(model, state_in, state_out, dt, control)
1573
+
1574
+ """
1575
+
1576
+ def __init__(
1577
+ self,
1578
+ model: Model,
1579
+ iterations=10,
1580
+ handle_self_contact=False,
1581
+ penetration_free_conservative_bound_relaxation=0.42,
1582
+ friction_epsilon=1e-2,
1583
+ body_particle_contact_buffer_pre_alloc=4,
1584
+ vertex_collision_buffer_pre_alloc=32,
1585
+ edge_collision_buffer_pre_alloc=64,
1586
+ triangle_collision_buffer_pre_alloc=32,
1587
+ edge_edge_parallel_epsilon=1e-5,
1588
+ ):
1589
+ self.device = model.device
1590
+ self.model = model
1591
+ self.iterations = iterations
1592
+
1593
+ # add new attributes for VBD solve
1594
+ self.particle_q_prev = wp.zeros_like(model.particle_q, device=self.device)
1595
+ self.inertia = wp.zeros_like(model.particle_q, device=self.device)
1596
+
1597
+ self.adjacency = self.compute_force_element_adjacency(model).to(self.device)
1598
+
1599
+ # data for body-particle collision
1600
+ self.body_particle_contact_buffer_pre_alloc = body_particle_contact_buffer_pre_alloc
1601
+ self.body_particle_contact_buffer = wp.zeros(
1602
+ (self.body_particle_contact_buffer_pre_alloc * model.particle_count,),
1603
+ dtype=wp.int32,
1604
+ device=self.device,
1605
+ )
1606
+ self.body_particle_contact_count = wp.zeros((model.particle_count,), dtype=wp.int32, device=self.device)
1607
+
1608
+ self.handle_self_contact = handle_self_contact
1609
+
1610
+ if handle_self_contact:
1611
+ if self.model.soft_contact_margin < self.model.soft_contact_radius:
1612
+ raise ValueError(
1613
+ "model.soft_contact_margin is smaller than self.model.soft_contact_radius, this will result in missing contacts and cause instability. \n"
1614
+ "It is advisable to make model.soft_contact_margin 1.5~2 times larger than self.model.soft_contact_radius."
1615
+ )
1616
+
1617
+ self.conservative_bound_relaxation = penetration_free_conservative_bound_relaxation
1618
+ self.pos_prev_collision_detection = wp.zeros_like(model.particle_q, device=self.device)
1619
+ self.particle_conservative_bounds = wp.full((model.particle_count,), dtype=float, device=self.device)
1620
+
1621
+ self.trimesh_collision_detector = TriMeshCollisionDetector(
1622
+ self.model,
1623
+ vertex_collision_buffer_pre_alloc=vertex_collision_buffer_pre_alloc,
1624
+ edge_collision_buffer_pre_alloc=edge_collision_buffer_pre_alloc,
1625
+ triangle_collision_buffer_pre_alloc=triangle_collision_buffer_pre_alloc,
1626
+ edge_edge_parallel_epsilon=edge_edge_parallel_epsilon,
1627
+ )
1628
+
1629
+ self.friction_epsilon = friction_epsilon
1630
+
1631
+ if len(self.model.particle_coloring) == 0:
1632
+ raise ValueError(
1633
+ "model.particle_coloring is empty! When using the VBDIntegrator you must call ModelBuilder.color() "
1634
+ "or ModelBuilder.set_coloring() before calling ModelBuilder.finalize()."
1635
+ )
1636
+
1637
+ # tests
1638
+ # wp.launch(kernel=_test_compute_force_element_adjacency,
1639
+ # inputs=[self.adjacency, model.edge_indices, model.tri_indices],
1640
+ # dim=1, device=self.device)
1641
+
1642
+ def compute_force_element_adjacency(self, model):
1643
+ adjacency = ForceElementAdjacencyInfo()
1644
+ edges_array = model.edge_indices.to("cpu")
1645
+
1646
+ if edges_array.size:
1647
+ # build vertex-edge adjacency data
1648
+ num_vertex_adjacent_edges = wp.zeros(shape=(self.model.particle_count,), dtype=wp.int32, device="cpu")
1649
+
1650
+ wp.launch(
1651
+ kernel=self.count_num_adjacent_edges,
1652
+ inputs=[edges_array, num_vertex_adjacent_edges],
1653
+ dim=1,
1654
+ device="cpu",
1655
+ )
1656
+
1657
+ num_vertex_adjacent_edges = num_vertex_adjacent_edges.numpy()
1658
+ vertex_adjacent_edges_offsets = np.empty(shape=(self.model.particle_count + 1,), dtype=wp.int32)
1659
+ vertex_adjacent_edges_offsets[1:] = np.cumsum(2 * num_vertex_adjacent_edges)[:]
1660
+ vertex_adjacent_edges_offsets[0] = 0
1661
+ adjacency.v_adj_edges_offsets = wp.array(vertex_adjacent_edges_offsets, dtype=wp.int32, device="cpu")
1662
+
1663
+ # temporal variables to record how much adjacent edges has been filled to each vertex
1664
+ vertex_adjacent_edges_fill_count = wp.zeros(
1665
+ shape=(self.model.particle_count,), dtype=wp.int32, device="cpu"
1666
+ )
1667
+
1668
+ edge_adjacency_array_size = 2 * num_vertex_adjacent_edges.sum()
1669
+ # vertex order: o0: 0, o1: 1, v0: 2, v1: 3,
1670
+ adjacency.v_adj_edges = wp.empty(shape=(edge_adjacency_array_size,), dtype=wp.int32, device="cpu")
1671
+
1672
+ wp.launch(
1673
+ kernel=self.fill_adjacent_edges,
1674
+ inputs=[
1675
+ edges_array,
1676
+ adjacency.v_adj_edges_offsets,
1677
+ vertex_adjacent_edges_fill_count,
1678
+ adjacency.v_adj_edges,
1679
+ ],
1680
+ dim=1,
1681
+ device="cpu",
1682
+ )
1683
+ else:
1684
+ adjacency.v_adj_edges_offsets = wp.empty(shape=(0,), dtype=wp.int32, device="cpu")
1685
+ adjacency.v_adj_edges = wp.empty(shape=(0,), dtype=wp.int32, device="cpu")
1686
+
1687
+ # compute adjacent triangles
1688
+
1689
+ # count number of adjacent faces for each vertex
1690
+ face_indices = model.tri_indices.to("cpu")
1691
+ num_vertex_adjacent_faces = wp.zeros(shape=(self.model.particle_count,), dtype=wp.int32, device="cpu")
1692
+ wp.launch(
1693
+ kernel=self.count_num_adjacent_faces, inputs=[face_indices, num_vertex_adjacent_faces], dim=1, device="cpu"
1694
+ )
1695
+
1696
+ # preallocate memory based on counting results
1697
+ num_vertex_adjacent_faces = num_vertex_adjacent_faces.numpy()
1698
+ vertex_adjacent_faces_offsets = np.empty(shape=(self.model.particle_count + 1,), dtype=wp.int32)
1699
+ vertex_adjacent_faces_offsets[1:] = np.cumsum(2 * num_vertex_adjacent_faces)[:]
1700
+ vertex_adjacent_faces_offsets[0] = 0
1701
+ adjacency.v_adj_faces_offsets = wp.array(vertex_adjacent_faces_offsets, dtype=wp.int32, device="cpu")
1702
+
1703
+ vertex_adjacent_faces_fill_count = wp.zeros(shape=(self.model.particle_count,), dtype=wp.int32, device="cpu")
1704
+
1705
+ face_adjacency_array_size = 2 * num_vertex_adjacent_faces.sum()
1706
+ # (face, vertex_order) * num_adj_faces * num_particles
1707
+ # vertex order: v0: 0, v1: 1, o0: 2, v2: 3
1708
+ adjacency.v_adj_faces = wp.empty(shape=(face_adjacency_array_size,), dtype=wp.int32, device="cpu")
1709
+
1710
+ wp.launch(
1711
+ kernel=self.fill_adjacent_faces,
1712
+ inputs=[
1713
+ face_indices,
1714
+ adjacency.v_adj_faces_offsets,
1715
+ vertex_adjacent_faces_fill_count,
1716
+ adjacency.v_adj_faces,
1717
+ ],
1718
+ dim=1,
1719
+ device="cpu",
1720
+ )
1721
+
1722
+ return adjacency
1723
+
1724
+ def simulate(self, model: Model, state_in: State, state_out: State, dt: float, control: Control = None):
1725
+ if model is not self.model:
1726
+ raise ValueError("model must be the one used to initialize VBDIntegrator")
1727
+
1728
+ if self.handle_self_contact:
1729
+ self.simulate_one_step_with_collisions_penetration_free(model, state_in, state_out, dt, control)
1730
+ else:
1731
+ self.simulate_one_step_no_self_contact(model, state_in, state_out, dt, control)
1732
+
1733
+ def simulate_one_step_no_self_contact(
1734
+ self, model: Model, state_in: State, state_out: State, dt: float, control: Control = None
1735
+ ):
1736
+ self.convert_body_particle_contact_data()
1737
+
1738
+ wp.launch(
1739
+ kernel=forward_step,
1740
+ inputs=[
1741
+ dt,
1742
+ model.gravity,
1743
+ self.particle_q_prev,
1744
+ state_in.particle_q,
1745
+ state_in.particle_qd,
1746
+ self.model.particle_inv_mass,
1747
+ state_in.particle_f,
1748
+ self.model.particle_flags,
1749
+ self.inertia,
1750
+ ],
1751
+ dim=self.model.particle_count,
1752
+ device=self.device,
1753
+ )
1754
+
1755
+ for _iter in range(self.iterations):
1756
+ for color_counter in range(len(self.model.particle_coloring)):
1757
+ wp.launch(
1758
+ kernel=VBD_solve_trimesh_no_self_contact,
1759
+ inputs=[
1760
+ dt,
1761
+ self.model.particle_coloring[color_counter],
1762
+ self.particle_q_prev,
1763
+ state_in.particle_q,
1764
+ state_out.particle_q,
1765
+ state_in.particle_qd,
1766
+ self.model.particle_mass,
1767
+ self.inertia,
1768
+ self.model.particle_flags,
1769
+ self.model.tri_indices,
1770
+ self.model.tri_poses,
1771
+ self.model.tri_materials,
1772
+ self.model.tri_areas,
1773
+ self.model.edge_indices,
1774
+ self.model.edge_rest_angle,
1775
+ self.model.edge_rest_length,
1776
+ self.model.edge_bending_properties,
1777
+ self.adjacency,
1778
+ self.model.soft_contact_ke,
1779
+ self.model.soft_contact_mu,
1780
+ self.friction_epsilon,
1781
+ # body-particle contact
1782
+ self.model.particle_radius,
1783
+ self.body_particle_contact_buffer_pre_alloc,
1784
+ self.body_particle_contact_buffer,
1785
+ self.body_particle_contact_count,
1786
+ self.model.shape_materials,
1787
+ self.model.shape_body,
1788
+ self.model.body_q,
1789
+ self.model.body_qd,
1790
+ self.model.body_com,
1791
+ self.model.soft_contact_shape,
1792
+ self.model.soft_contact_body_pos,
1793
+ self.model.soft_contact_body_vel,
1794
+ self.model.soft_contact_normal,
1795
+ self.model.ground,
1796
+ self.model.ground_plane,
1797
+ ],
1798
+ dim=self.model.particle_coloring[color_counter].size,
1799
+ device=self.device,
1800
+ )
1801
+
1802
+ wp.launch(
1803
+ kernel=VBD_copy_particle_positions_back,
1804
+ inputs=[self.model.particle_coloring[color_counter], state_in.particle_q, state_out.particle_q],
1805
+ dim=self.model.particle_coloring[color_counter].size,
1806
+ device=self.device,
1807
+ )
1808
+
1809
+ wp.launch(
1810
+ kernel=update_velocity,
1811
+ inputs=[dt, self.particle_q_prev, state_out.particle_q, state_out.particle_qd],
1812
+ dim=self.model.particle_count,
1813
+ device=self.device,
1814
+ )
1815
+
1816
+ def simulate_one_step_with_collisions_penetration_free(
1817
+ self, model: Model, state_in: State, state_out: State, dt: float, control: Control = None
1818
+ ):
1819
+ self.convert_body_particle_contact_data()
1820
+ # collision detection before initialization to compute conservative bounds for initialization
1821
+ self.collision_detection_penetration_free(state_in, dt)
1822
+
1823
+ wp.launch(
1824
+ kernel=forward_step_penetration_free,
1825
+ inputs=[
1826
+ dt,
1827
+ model.gravity,
1828
+ self.particle_q_prev,
1829
+ state_in.particle_q,
1830
+ state_in.particle_qd,
1831
+ self.model.particle_inv_mass,
1832
+ state_in.particle_f,
1833
+ self.model.particle_flags,
1834
+ self.pos_prev_collision_detection,
1835
+ self.particle_conservative_bounds,
1836
+ self.inertia,
1837
+ ],
1838
+ dim=self.model.particle_count,
1839
+ device=self.device,
1840
+ )
1841
+
1842
+ # after initialization, we do another collision detection to update the bounds
1843
+ self.collision_detection_penetration_free(state_in, dt)
1844
+
1845
+ for _iter in range(self.iterations):
1846
+ for i_color in range(len(self.model.particle_coloring)):
1847
+ wp.launch(
1848
+ kernel=VBD_solve_trimesh_with_self_contact_penetration_free,
1849
+ inputs=[
1850
+ dt,
1851
+ self.model.particle_coloring[i_color],
1852
+ self.particle_q_prev,
1853
+ state_in.particle_q,
1854
+ state_out.particle_q,
1855
+ state_in.particle_qd,
1856
+ self.model.particle_mass,
1857
+ self.inertia,
1858
+ self.model.particle_flags,
1859
+ self.model.tri_indices,
1860
+ self.model.tri_poses,
1861
+ self.model.tri_materials,
1862
+ self.model.tri_areas,
1863
+ self.model.edge_indices,
1864
+ self.model.edge_rest_angle,
1865
+ self.model.edge_rest_length,
1866
+ self.model.edge_bending_properties,
1867
+ self.adjacency,
1868
+ # self-contact
1869
+ self.trimesh_collision_detector.collision_info,
1870
+ self.model.soft_contact_radius,
1871
+ self.model.soft_contact_ke,
1872
+ self.model.soft_contact_mu,
1873
+ self.friction_epsilon,
1874
+ self.pos_prev_collision_detection,
1875
+ self.particle_conservative_bounds,
1876
+ self.trimesh_collision_detector.edge_edge_parallel_epsilon,
1877
+ # body-particle contact
1878
+ self.model.particle_radius,
1879
+ self.body_particle_contact_buffer_pre_alloc,
1880
+ self.body_particle_contact_buffer,
1881
+ self.body_particle_contact_count,
1882
+ self.model.shape_materials,
1883
+ self.model.shape_body,
1884
+ self.model.body_q,
1885
+ self.model.body_qd,
1886
+ self.model.body_com,
1887
+ self.model.soft_contact_shape,
1888
+ self.model.soft_contact_body_pos,
1889
+ self.model.soft_contact_body_vel,
1890
+ self.model.soft_contact_normal,
1891
+ self.model.ground,
1892
+ self.model.ground_plane,
1893
+ ],
1894
+ dim=self.model.particle_coloring[i_color].shape[0],
1895
+ device=self.device,
1896
+ )
1897
+
1898
+ wp.launch(
1899
+ kernel=VBD_copy_particle_positions_back,
1900
+ inputs=[self.model.particle_coloring[i_color], state_in.particle_q, state_out.particle_q],
1901
+ dim=self.model.particle_coloring[i_color].size,
1902
+ device=self.device,
1903
+ )
1904
+
1905
+ wp.launch(
1906
+ kernel=update_velocity,
1907
+ inputs=[dt, self.particle_q_prev, state_out.particle_q, state_out.particle_qd],
1908
+ dim=self.model.particle_count,
1909
+ device=self.device,
1910
+ )
1911
+
1912
+ def collision_detection_penetration_free(self, current_state, dt):
1913
+ self.trimesh_collision_detector.refit(current_state.particle_q)
1914
+ self.trimesh_collision_detector.vertex_triangle_collision_detection(self.model.soft_contact_margin)
1915
+ self.trimesh_collision_detector.edge_edge_collision_detection(self.model.soft_contact_margin)
1916
+
1917
+ self.pos_prev_collision_detection.assign(current_state.particle_q)
1918
+ wp.launch(
1919
+ kernel=compute_particle_conservative_bound,
1920
+ inputs=[
1921
+ self.conservative_bound_relaxation,
1922
+ self.model.soft_contact_margin,
1923
+ self.adjacency,
1924
+ self.trimesh_collision_detector.collision_info,
1925
+ ],
1926
+ outputs=[
1927
+ self.particle_conservative_bounds,
1928
+ ],
1929
+ dim=self.model.particle_count,
1930
+ device=self.device,
1931
+ )
1932
+
1933
+ def convert_body_particle_contact_data(self):
1934
+ self.body_particle_contact_count.zero_()
1935
+
1936
+ wp.launch(
1937
+ kernel=convert_body_particle_contact_data_kernel,
1938
+ inputs=[
1939
+ self.body_particle_contact_buffer_pre_alloc,
1940
+ self.model.soft_contact_particle,
1941
+ self.model.soft_contact_count,
1942
+ self.model.soft_contact_max,
1943
+ ],
1944
+ outputs=[self.body_particle_contact_buffer, self.body_particle_contact_count],
1945
+ dim=self.model.soft_contact_max,
1946
+ device=self.device,
1947
+ )
1948
+
1949
+ @wp.kernel
1950
+ def count_num_adjacent_edges(
1951
+ edges_array: wp.array(dtype=wp.int32, ndim=2), num_vertex_adjacent_edges: wp.array(dtype=wp.int32)
1952
+ ):
1953
+ for edge_id in range(edges_array.shape[0]):
1954
+ o0 = edges_array[edge_id, 0]
1955
+ o1 = edges_array[edge_id, 1]
1956
+
1957
+ v0 = edges_array[edge_id, 2]
1958
+ v1 = edges_array[edge_id, 3]
1959
+
1960
+ num_vertex_adjacent_edges[v0] = num_vertex_adjacent_edges[v0] + 1
1961
+ num_vertex_adjacent_edges[v1] = num_vertex_adjacent_edges[v1] + 1
1962
+
1963
+ if o0 != -1:
1964
+ num_vertex_adjacent_edges[o0] = num_vertex_adjacent_edges[o0] + 1
1965
+ if o1 != -1:
1966
+ num_vertex_adjacent_edges[o1] = num_vertex_adjacent_edges[o1] + 1
1967
+
1968
+ @wp.kernel
1969
+ def fill_adjacent_edges(
1970
+ edges_array: wp.array(dtype=wp.int32, ndim=2),
1971
+ vertex_adjacent_edges_offsets: wp.array(dtype=wp.int32),
1972
+ vertex_adjacent_edges_fill_count: wp.array(dtype=wp.int32),
1973
+ vertex_adjacent_edges: wp.array(dtype=wp.int32),
1974
+ ):
1975
+ for edge_id in range(edges_array.shape[0]):
1976
+ v0 = edges_array[edge_id, 2]
1977
+ v1 = edges_array[edge_id, 3]
1978
+
1979
+ fill_count_v0 = vertex_adjacent_edges_fill_count[v0]
1980
+ buffer_offset_v0 = vertex_adjacent_edges_offsets[v0]
1981
+ vertex_adjacent_edges[buffer_offset_v0 + fill_count_v0 * 2] = edge_id
1982
+ vertex_adjacent_edges[buffer_offset_v0 + fill_count_v0 * 2 + 1] = 2
1983
+ vertex_adjacent_edges_fill_count[v0] = fill_count_v0 + 1
1984
+
1985
+ fill_count_v1 = vertex_adjacent_edges_fill_count[v1]
1986
+ buffer_offset_v1 = vertex_adjacent_edges_offsets[v1]
1987
+ vertex_adjacent_edges[buffer_offset_v1 + fill_count_v1 * 2] = edge_id
1988
+ vertex_adjacent_edges[buffer_offset_v1 + fill_count_v1 * 2 + 1] = 3
1989
+ vertex_adjacent_edges_fill_count[v1] = fill_count_v1 + 1
1990
+
1991
+ o0 = edges_array[edge_id, 0]
1992
+ if o0 != -1:
1993
+ fill_count_o0 = vertex_adjacent_edges_fill_count[o0]
1994
+ buffer_offset_o0 = vertex_adjacent_edges_offsets[o0]
1995
+ vertex_adjacent_edges[buffer_offset_o0 + fill_count_o0 * 2] = edge_id
1996
+ vertex_adjacent_edges[buffer_offset_o0 + fill_count_o0 * 2 + 1] = 0
1997
+ vertex_adjacent_edges_fill_count[o0] = fill_count_o0 + 1
1998
+
1999
+ o1 = edges_array[edge_id, 1]
2000
+ if o1 != -1:
2001
+ fill_count_o1 = vertex_adjacent_edges_fill_count[o1]
2002
+ buffer_offset_o1 = vertex_adjacent_edges_offsets[o1]
2003
+ vertex_adjacent_edges[buffer_offset_o1 + fill_count_o1 * 2] = edge_id
2004
+ vertex_adjacent_edges[buffer_offset_o1 + fill_count_o1 * 2 + 1] = 1
2005
+ vertex_adjacent_edges_fill_count[o1] = fill_count_o1 + 1
2006
+
2007
+ @wp.kernel
2008
+ def count_num_adjacent_faces(
2009
+ face_indices: wp.array(dtype=wp.int32, ndim=2), num_vertex_adjacent_faces: wp.array(dtype=wp.int32)
2010
+ ):
2011
+ for face in range(face_indices.shape[0]):
2012
+ v0 = face_indices[face, 0]
2013
+ v1 = face_indices[face, 1]
2014
+ v2 = face_indices[face, 2]
2015
+
2016
+ num_vertex_adjacent_faces[v0] = num_vertex_adjacent_faces[v0] + 1
2017
+ num_vertex_adjacent_faces[v1] = num_vertex_adjacent_faces[v1] + 1
2018
+ num_vertex_adjacent_faces[v2] = num_vertex_adjacent_faces[v2] + 1
2019
+
2020
+ @wp.kernel
2021
+ def fill_adjacent_faces(
2022
+ face_indices: wp.array(dtype=wp.int32, ndim=2),
2023
+ vertex_adjacent_faces_offsets: wp.array(dtype=wp.int32),
2024
+ vertex_adjacent_faces_fill_count: wp.array(dtype=wp.int32),
2025
+ vertex_adjacent_faces: wp.array(dtype=wp.int32),
2026
+ ):
2027
+ for face in range(face_indices.shape[0]):
2028
+ v0 = face_indices[face, 0]
2029
+ v1 = face_indices[face, 1]
2030
+ v2 = face_indices[face, 2]
2031
+
2032
+ fill_count_v0 = vertex_adjacent_faces_fill_count[v0]
2033
+ buffer_offset_v0 = vertex_adjacent_faces_offsets[v0]
2034
+ vertex_adjacent_faces[buffer_offset_v0 + fill_count_v0 * 2] = face
2035
+ vertex_adjacent_faces[buffer_offset_v0 + fill_count_v0 * 2 + 1] = 0
2036
+ vertex_adjacent_faces_fill_count[v0] = fill_count_v0 + 1
2037
+
2038
+ fill_count_v1 = vertex_adjacent_faces_fill_count[v1]
2039
+ buffer_offset_v1 = vertex_adjacent_faces_offsets[v1]
2040
+ vertex_adjacent_faces[buffer_offset_v1 + fill_count_v1 * 2] = face
2041
+ vertex_adjacent_faces[buffer_offset_v1 + fill_count_v1 * 2 + 1] = 1
2042
+ vertex_adjacent_faces_fill_count[v1] = fill_count_v1 + 1
2043
+
2044
+ fill_count_v2 = vertex_adjacent_faces_fill_count[v2]
2045
+ buffer_offset_v2 = vertex_adjacent_faces_offsets[v2]
2046
+ vertex_adjacent_faces[buffer_offset_v2 + fill_count_v2 * 2] = face
2047
+ vertex_adjacent_faces[buffer_offset_v2 + fill_count_v2 * 2 + 1] = 2
2048
+ vertex_adjacent_faces_fill_count[v2] = fill_count_v2 + 1