warp-lang 0.11.0__py3-none-manylinux2014_x86_64.whl → 1.0.0__py3-none-manylinux2014_x86_64.whl

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

Potentially problematic release.


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

Files changed (170) hide show
  1. warp/__init__.py +8 -0
  2. warp/bin/warp-clang.so +0 -0
  3. warp/bin/warp.so +0 -0
  4. warp/build.py +7 -6
  5. warp/build_dll.py +70 -79
  6. warp/builtins.py +10 -6
  7. warp/codegen.py +51 -19
  8. warp/config.py +7 -8
  9. warp/constants.py +3 -0
  10. warp/context.py +948 -245
  11. warp/dlpack.py +198 -113
  12. warp/examples/assets/bunny.usd +0 -0
  13. warp/examples/assets/cartpole.urdf +110 -0
  14. warp/examples/assets/crazyflie.usd +0 -0
  15. warp/examples/assets/cube.usda +42 -0
  16. warp/examples/assets/nv_ant.xml +92 -0
  17. warp/examples/assets/nv_humanoid.xml +183 -0
  18. warp/examples/assets/quadruped.urdf +268 -0
  19. warp/examples/assets/rocks.nvdb +0 -0
  20. warp/examples/assets/rocks.usd +0 -0
  21. warp/examples/assets/sphere.usda +56 -0
  22. warp/examples/assets/torus.usda +105 -0
  23. warp/examples/benchmarks/benchmark_api.py +383 -0
  24. warp/examples/benchmarks/benchmark_cloth.py +279 -0
  25. warp/examples/benchmarks/benchmark_cloth_cupy.py +88 -0
  26. warp/examples/benchmarks/benchmark_cloth_jax.py +100 -0
  27. warp/examples/benchmarks/benchmark_cloth_numba.py +142 -0
  28. warp/examples/benchmarks/benchmark_cloth_numpy.py +77 -0
  29. warp/examples/benchmarks/benchmark_cloth_pytorch.py +86 -0
  30. warp/examples/benchmarks/benchmark_cloth_taichi.py +112 -0
  31. warp/examples/benchmarks/benchmark_cloth_warp.py +146 -0
  32. warp/examples/benchmarks/benchmark_launches.py +295 -0
  33. warp/examples/core/example_dem.py +221 -0
  34. warp/examples/core/example_fluid.py +267 -0
  35. warp/examples/core/example_graph_capture.py +129 -0
  36. warp/examples/core/example_marching_cubes.py +177 -0
  37. warp/examples/core/example_mesh.py +154 -0
  38. warp/examples/core/example_mesh_intersect.py +193 -0
  39. warp/examples/core/example_nvdb.py +169 -0
  40. warp/examples/core/example_raycast.py +89 -0
  41. warp/examples/core/example_raymarch.py +178 -0
  42. warp/examples/core/example_render_opengl.py +141 -0
  43. warp/examples/core/example_sph.py +389 -0
  44. warp/examples/core/example_torch.py +181 -0
  45. warp/examples/core/example_wave.py +249 -0
  46. warp/examples/fem/bsr_utils.py +380 -0
  47. warp/examples/fem/example_apic_fluid.py +391 -0
  48. warp/examples/fem/example_convection_diffusion.py +168 -0
  49. warp/examples/fem/example_convection_diffusion_dg.py +209 -0
  50. warp/examples/fem/example_convection_diffusion_dg0.py +194 -0
  51. warp/examples/fem/example_deformed_geometry.py +159 -0
  52. warp/examples/fem/example_diffusion.py +173 -0
  53. warp/examples/fem/example_diffusion_3d.py +152 -0
  54. warp/examples/fem/example_diffusion_mgpu.py +214 -0
  55. warp/examples/fem/example_mixed_elasticity.py +222 -0
  56. warp/examples/fem/example_navier_stokes.py +243 -0
  57. warp/examples/fem/example_stokes.py +192 -0
  58. warp/examples/fem/example_stokes_transfer.py +249 -0
  59. warp/examples/fem/mesh_utils.py +109 -0
  60. warp/examples/fem/plot_utils.py +287 -0
  61. warp/examples/optim/example_bounce.py +248 -0
  62. warp/examples/optim/example_cloth_throw.py +210 -0
  63. warp/examples/optim/example_diffray.py +535 -0
  64. warp/examples/optim/example_drone.py +850 -0
  65. warp/examples/optim/example_inverse_kinematics.py +169 -0
  66. warp/examples/optim/example_inverse_kinematics_torch.py +170 -0
  67. warp/examples/optim/example_spring_cage.py +234 -0
  68. warp/examples/optim/example_trajectory.py +201 -0
  69. warp/examples/sim/example_cartpole.py +128 -0
  70. warp/examples/sim/example_cloth.py +184 -0
  71. warp/examples/sim/example_granular.py +113 -0
  72. warp/examples/sim/example_granular_collision_sdf.py +185 -0
  73. warp/examples/sim/example_jacobian_ik.py +213 -0
  74. warp/examples/sim/example_particle_chain.py +106 -0
  75. warp/examples/sim/example_quadruped.py +179 -0
  76. warp/examples/sim/example_rigid_chain.py +191 -0
  77. warp/examples/sim/example_rigid_contact.py +176 -0
  78. warp/examples/sim/example_rigid_force.py +126 -0
  79. warp/examples/sim/example_rigid_gyroscopic.py +97 -0
  80. warp/examples/sim/example_rigid_soft_contact.py +124 -0
  81. warp/examples/sim/example_soft_body.py +178 -0
  82. warp/fabric.py +29 -20
  83. warp/fem/cache.py +0 -1
  84. warp/fem/dirichlet.py +0 -2
  85. warp/fem/integrate.py +0 -1
  86. warp/jax.py +45 -0
  87. warp/jax_experimental.py +339 -0
  88. warp/native/builtin.h +12 -0
  89. warp/native/bvh.cu +18 -18
  90. warp/native/clang/clang.cpp +8 -3
  91. warp/native/cuda_util.cpp +94 -5
  92. warp/native/cuda_util.h +35 -6
  93. warp/native/cutlass_gemm.cpp +1 -1
  94. warp/native/cutlass_gemm.cu +4 -1
  95. warp/native/error.cpp +66 -0
  96. warp/native/error.h +27 -0
  97. warp/native/mesh.cu +2 -2
  98. warp/native/reduce.cu +4 -4
  99. warp/native/runlength_encode.cu +2 -2
  100. warp/native/scan.cu +2 -2
  101. warp/native/sparse.cu +0 -1
  102. warp/native/temp_buffer.h +2 -2
  103. warp/native/warp.cpp +95 -60
  104. warp/native/warp.cu +1053 -218
  105. warp/native/warp.h +49 -32
  106. warp/optim/linear.py +33 -16
  107. warp/render/render_opengl.py +202 -101
  108. warp/render/render_usd.py +82 -40
  109. warp/sim/__init__.py +13 -4
  110. warp/sim/articulation.py +4 -5
  111. warp/sim/collide.py +320 -175
  112. warp/sim/import_mjcf.py +25 -30
  113. warp/sim/import_urdf.py +94 -63
  114. warp/sim/import_usd.py +51 -36
  115. warp/sim/inertia.py +3 -2
  116. warp/sim/integrator.py +233 -0
  117. warp/sim/integrator_euler.py +447 -469
  118. warp/sim/integrator_featherstone.py +1991 -0
  119. warp/sim/integrator_xpbd.py +1420 -640
  120. warp/sim/model.py +765 -487
  121. warp/sim/particles.py +2 -1
  122. warp/sim/render.py +35 -13
  123. warp/sim/utils.py +222 -11
  124. warp/stubs.py +8 -0
  125. warp/tape.py +16 -1
  126. warp/tests/aux_test_grad_customs.py +23 -0
  127. warp/tests/test_array.py +190 -1
  128. warp/tests/test_async.py +656 -0
  129. warp/tests/test_bool.py +50 -0
  130. warp/tests/test_dlpack.py +164 -11
  131. warp/tests/test_examples.py +166 -74
  132. warp/tests/test_fem.py +8 -1
  133. warp/tests/test_generics.py +15 -5
  134. warp/tests/test_grad.py +1 -1
  135. warp/tests/test_grad_customs.py +172 -12
  136. warp/tests/test_jax.py +254 -0
  137. warp/tests/test_large.py +29 -6
  138. warp/tests/test_launch.py +25 -0
  139. warp/tests/test_linear_solvers.py +20 -3
  140. warp/tests/test_matmul.py +61 -16
  141. warp/tests/test_matmul_lite.py +13 -13
  142. warp/tests/test_mempool.py +186 -0
  143. warp/tests/test_multigpu.py +3 -0
  144. warp/tests/test_options.py +16 -2
  145. warp/tests/test_peer.py +137 -0
  146. warp/tests/test_print.py +3 -1
  147. warp/tests/test_quat.py +23 -0
  148. warp/tests/test_sim_kinematics.py +97 -0
  149. warp/tests/test_snippet.py +126 -3
  150. warp/tests/test_streams.py +108 -79
  151. warp/tests/test_torch.py +16 -8
  152. warp/tests/test_utils.py +32 -27
  153. warp/tests/test_verify_fp.py +65 -0
  154. warp/tests/test_volume.py +1 -1
  155. warp/tests/unittest_serial.py +2 -0
  156. warp/tests/unittest_suites.py +12 -0
  157. warp/tests/unittest_utils.py +14 -7
  158. warp/thirdparty/unittest_parallel.py +15 -3
  159. warp/torch.py +10 -8
  160. warp/types.py +363 -246
  161. warp/utils.py +143 -19
  162. warp_lang-1.0.0.dist-info/LICENSE.md +126 -0
  163. warp_lang-1.0.0.dist-info/METADATA +394 -0
  164. {warp_lang-0.11.0.dist-info → warp_lang-1.0.0.dist-info}/RECORD +167 -86
  165. warp/sim/optimizer.py +0 -138
  166. warp_lang-0.11.0.dist-info/LICENSE.md +0 -36
  167. warp_lang-0.11.0.dist-info/METADATA +0 -238
  168. /warp/tests/{walkthough_debug.py → walkthrough_debug.py} +0 -0
  169. {warp_lang-0.11.0.dist-info → warp_lang-1.0.0.dist-info}/WHEEL +0 -0
  170. {warp_lang-0.11.0.dist-info → warp_lang-1.0.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,178 @@
1
+ # Copyright (c) 2022 NVIDIA CORPORATION. All rights reserved.
2
+ # NVIDIA CORPORATION and its licensors retain all intellectual property
3
+ # and proprietary rights in and to this software, related documentation
4
+ # and any modifications thereto. Any use, reproduction, disclosure or
5
+ # distribution of this software and related documentation without an express
6
+ # license agreement from NVIDIA CORPORATION is strictly prohibited.
7
+
8
+ ###########################################################################
9
+ # Example Ray March
10
+ #
11
+ # Shows how to implement an SDF ray marching based renderer. Please see
12
+ # https://iquilezles.org/www/articles/distfunctions/distfunctions.htm
13
+ # for reference on different distance functions.
14
+ #
15
+ ###########################################################################
16
+
17
+
18
+ import warp as wp
19
+
20
+ wp.init()
21
+
22
+
23
+ @wp.func
24
+ def sdf_sphere(p: wp.vec3, r: float):
25
+ return wp.length(p) - r
26
+
27
+
28
+ @wp.func
29
+ def sdf_box(upper: wp.vec3, p: wp.vec3):
30
+ qx = wp.abs(p[0]) - upper[0]
31
+ qy = wp.abs(p[1]) - upper[1]
32
+ qz = wp.abs(p[2]) - upper[2]
33
+
34
+ e = wp.vec3(wp.max(qx, 0.0), wp.max(qy, 0.0), wp.max(qz, 0.0))
35
+
36
+ return wp.length(e) + wp.min(wp.max(qx, wp.max(qy, qz)), 0.0)
37
+
38
+
39
+ @wp.func
40
+ def sdf_plane(p: wp.vec3, plane: wp.vec4):
41
+ return plane[0] * p[0] + plane[1] * p[1] + plane[2] * p[2] + plane[3]
42
+
43
+
44
+ @wp.func
45
+ def op_union(d1: float, d2: float):
46
+ return wp.min(d1, d2)
47
+
48
+
49
+ @wp.func
50
+ def op_subtract(d1: float, d2: float):
51
+ return wp.max(-d1, d2)
52
+
53
+
54
+ @wp.func
55
+ def op_intersect(d1: float, d2: float):
56
+ return wp.max(d1, d2)
57
+
58
+
59
+ # simple scene
60
+ @wp.func
61
+ def sdf(p: wp.vec3):
62
+ sphere_1 = wp.vec3(0.0, 0.75, 0.0)
63
+
64
+ d = op_subtract(sdf_sphere(p - sphere_1, 0.75), sdf_box(wp.vec3(1.0, 0.5, 0.5), p))
65
+
66
+ # ground plane
67
+ d = op_union(d, sdf_plane(p, wp.vec4(0.0, 1.0, 0.0, 1.0)))
68
+
69
+ return d
70
+
71
+
72
+ @wp.func
73
+ def normal(p: wp.vec3):
74
+ eps = 1.0e-5
75
+
76
+ # compute gradient of the SDF using finite differences
77
+ dx = sdf(p + wp.vec3(eps, 0.0, 0.0)) - sdf(p - wp.vec3(eps, 0.0, 0.0))
78
+ dy = sdf(p + wp.vec3(0.0, eps, 0.0)) - sdf(p - wp.vec3(0.0, eps, 0.0))
79
+ dz = sdf(p + wp.vec3(0.0, 0.0, eps)) - sdf(p - wp.vec3(0.0, 0.0, eps))
80
+
81
+ return wp.normalize(wp.vec3(dx, dy, dz))
82
+
83
+
84
+ @wp.func
85
+ def shadow(ro: wp.vec3, rd: wp.vec3):
86
+ t = float(0.0)
87
+ s = float(1.0)
88
+
89
+ for _ in range(64):
90
+ d = sdf(ro + t * rd)
91
+ t = t + wp.clamp(d, 0.0001, 2.0)
92
+
93
+ h = wp.clamp(4.0 * d / t, 0.0, 1.0)
94
+ s = wp.min(s, h * h * (3.0 - 2.0 * h))
95
+
96
+ if t > 8.0:
97
+ return 1.0
98
+
99
+ return s
100
+
101
+
102
+ @wp.kernel
103
+ def draw(cam_pos: wp.vec3, cam_rot: wp.quat, width: int, height: int, pixels: wp.array(dtype=wp.vec3)):
104
+ tid = wp.tid()
105
+
106
+ x = tid % width
107
+ y = tid // width
108
+
109
+ # compute pixel coordinates
110
+ sx = (2.0 * float(x) - float(width)) / float(height)
111
+ sy = (2.0 * float(y) - float(height)) / float(height)
112
+
113
+ # compute view ray
114
+ ro = cam_pos
115
+ rd = wp.quat_rotate(cam_rot, wp.normalize(wp.vec3(sx, sy, -2.0)))
116
+
117
+ t = float(0.0)
118
+
119
+ # ray march
120
+ for _ in range(128):
121
+ d = sdf(ro + rd * t)
122
+ t = t + d
123
+
124
+ if d < 0.01:
125
+ p = ro + rd * t
126
+ n = normal(p)
127
+ l = wp.normalize(wp.vec3(0.6, 0.4, 0.5))
128
+
129
+ # half-vector
130
+ h = wp.normalize(l - rd)
131
+
132
+ diffuse = wp.dot(n, l)
133
+ specular = wp.clamp(wp.dot(n, h), 0.0, 1.0) ** 80.0
134
+ fresnel = 0.04 + 0.96 * wp.clamp(1.0 - wp.dot(h, l), 0.0, 1.0) ** 5.0
135
+
136
+ intensity = 2.0
137
+ result = (
138
+ wp.vec3(0.85, 0.9, 0.95) * (diffuse * (1.0 - fresnel) + specular * fresnel * 10.0) * shadow(p, l) * intensity
139
+ )
140
+
141
+ # gamma
142
+ pixels[tid] = wp.vec3(wp.clamp(result[0] ** 2.2, 0.0, 1.0), wp.clamp(result[1] ** 2.2, 0.0, 1.0), wp.clamp(result[2] ** 2.2, 0.0, 1.0))
143
+
144
+ else:
145
+ pixels[tid] = wp.vec3(0.4, 0.45, 0.5) * 1.5
146
+
147
+
148
+ class Example:
149
+ def __init__(self, **kwargs):
150
+ self.width = 2048
151
+ self.height = 1024
152
+ self.cam_pos = (-1.25, 1.0, 2.0)
153
+ self.cam_rot = wp.quat_rpy(-0.5, -0.5, 0.0)
154
+
155
+ self.pixels = wp.zeros(self.width * self.height, dtype=wp.vec3)
156
+
157
+ def step(self):
158
+ pass
159
+
160
+ def render(self):
161
+ with wp.ScopedTimer("render"):
162
+ wp.launch(
163
+ kernel=draw,
164
+ dim=self.width * self.height,
165
+ inputs=[self.cam_pos, self.cam_rot, self.width, self.height, self.pixels],
166
+ )
167
+
168
+
169
+ if __name__ == "__main__":
170
+ import matplotlib.pyplot as plt
171
+
172
+ example = Example()
173
+ example.render()
174
+
175
+ plt.imshow(
176
+ example.pixels.numpy().reshape((example.height, example.width, 3)), origin="lower", interpolation="antialiased"
177
+ )
178
+ plt.show()
@@ -0,0 +1,141 @@
1
+ # Copyright (c) 2022 NVIDIA CORPORATION. All rights reserved.
2
+ # NVIDIA CORPORATION and its licensors retain all intellectual property
3
+ # and proprietary rights in and to this software, related documentation
4
+ # and any modifications thereto. Any use, reproduction, disclosure or
5
+ # distribution of this software and related documentation without an express
6
+ # license agreement from NVIDIA CORPORATION is strictly prohibited.
7
+
8
+ ###########################################################################
9
+ # OpenGL renderer example
10
+ #
11
+ # Demonstrates how to set up tiled rendering and retrieves the pixels from
12
+ # OpenGLRenderer as a Warp array while keeping all memory on the GPU.
13
+ #
14
+ ###########################################################################
15
+
16
+ import warp as wp
17
+ import warp.render
18
+ import numpy as np
19
+
20
+ wp.init()
21
+
22
+ # number of viewports to render in a single frame
23
+ num_tiles = 4
24
+ # whether to split tiles into subplots
25
+ split_up_tiles = True
26
+ # whether to apply custom tile arrangement
27
+ custom_tile_arrangement = False
28
+ # whether to display the pixels in a matplotlib figure
29
+ show_plot = True
30
+ # whether to render depth image to a Warp array
31
+ render_mode = "depth"
32
+
33
+ renderer = wp.render.OpenGLRenderer(vsync=False)
34
+ instance_ids = []
35
+
36
+ if custom_tile_arrangement:
37
+ positions = []
38
+ sizes = []
39
+ else:
40
+ positions = None
41
+ sizes = None
42
+
43
+ if num_tiles > 1:
44
+ # set up instances to hide one of the capsules in each tile
45
+ for i in range(num_tiles):
46
+ instances = [j for j in np.arange(13) if j != i + 2]
47
+ instance_ids.append(instances)
48
+ if custom_tile_arrangement:
49
+ angle = np.pi * 2.0 / num_tiles * i
50
+ positions.append((int(np.cos(angle) * 150 + 250), int(np.sin(angle) * 150 + 250)))
51
+ sizes.append((150, 150))
52
+ renderer.setup_tiled_rendering(instance_ids, tile_positions=positions, tile_sizes=sizes)
53
+
54
+ renderer.render_ground()
55
+
56
+ channels = 1 if render_mode == "depth" else 3
57
+ if show_plot:
58
+ import matplotlib.pyplot as plt
59
+
60
+ if split_up_tiles:
61
+ pixels = wp.zeros((num_tiles, renderer.tile_height, renderer.tile_width, channels), dtype=wp.float32)
62
+ ncols = int(np.ceil(np.sqrt(num_tiles)))
63
+ nrows = int(np.ceil(num_tiles / float(ncols)))
64
+ img_plots = []
65
+ aspect_ratio = renderer.tile_height / renderer.tile_width
66
+ fig, axes = plt.subplots(
67
+ ncols=ncols,
68
+ nrows=nrows,
69
+ constrained_layout=True,
70
+ figsize=(ncols * 3.5, nrows * 3.5 * aspect_ratio),
71
+ squeeze=False,
72
+ sharex=True,
73
+ sharey=True,
74
+ num=1,
75
+ )
76
+ tile_temp = np.zeros((renderer.tile_height, renderer.tile_width, channels), dtype=np.float32)
77
+ for dim in range(ncols * nrows):
78
+ ax = axes[dim // ncols, dim % ncols]
79
+ if dim >= num_tiles:
80
+ ax.axis("off")
81
+ continue
82
+ if render_mode == "depth":
83
+ img_plots.append(ax.imshow(tile_temp, vmin=renderer.camera_near_plane, vmax=renderer.camera_far_plane))
84
+ else:
85
+ img_plots.append(ax.imshow(tile_temp))
86
+ else:
87
+ fig = plt.figure(1)
88
+ pixels = wp.zeros((renderer.screen_height, renderer.screen_width, channels), dtype=wp.float32)
89
+ if render_mode == "depth":
90
+ img_plot = plt.imshow(pixels.numpy(), vmin=renderer.camera_near_plane, vmax=renderer.camera_far_plane)
91
+ else:
92
+ img_plot = plt.imshow(pixels.numpy())
93
+
94
+ plt.ion()
95
+ plt.show()
96
+
97
+ while renderer.is_running():
98
+ time = renderer.clock_time
99
+ renderer.begin_frame(time)
100
+ for i in range(10):
101
+ renderer.render_capsule(
102
+ f"capsule_{i}", [i - 5.0, np.sin(time + i * 0.2), -3.0], [0.0, 0.0, 0.0, 1.0], radius=0.5, half_height=0.8
103
+ )
104
+ renderer.render_cylinder(
105
+ "cylinder",
106
+ [3.2, 1.0, np.sin(time + 0.5)],
107
+ np.array(wp.quat_from_axis_angle(wp.vec3(1.0, 0.0, 0.0), wp.sin(time + 0.5))),
108
+ radius=0.5,
109
+ half_height=0.8,
110
+ )
111
+ renderer.render_cone(
112
+ "cone",
113
+ [-1.2, 1.0, 0.0],
114
+ np.array(wp.quat_from_axis_angle(wp.vec3(0.707, 0.707, 0.0), time)),
115
+ radius=0.5,
116
+ half_height=0.8,
117
+ )
118
+ renderer.end_frame()
119
+
120
+ if show_plot and plt.fignum_exists(1):
121
+ if split_up_tiles:
122
+ pixel_shape = (num_tiles, renderer.tile_height, renderer.tile_width, channels)
123
+ else:
124
+ pixel_shape = (renderer.screen_height, renderer.screen_width, channels)
125
+
126
+ if pixel_shape != pixels.shape:
127
+ # make sure we resize the pixels array to the right dimensions if the user resizes the window
128
+ pixels = wp.zeros(pixel_shape, dtype=wp.float32)
129
+
130
+ renderer.get_pixels(pixels, split_up_tiles=split_up_tiles, mode=render_mode)
131
+
132
+ if split_up_tiles:
133
+ pixels_np = pixels.numpy()
134
+ for i, img_plot in enumerate(img_plots):
135
+ img_plot.set_data(pixels_np[i])
136
+ else:
137
+ img_plot.set_data(pixels.numpy())
138
+ fig.canvas.draw()
139
+ fig.canvas.flush_events()
140
+
141
+ renderer.clear()
@@ -0,0 +1,389 @@
1
+ # Copyright (c) 2022 NVIDIA CORPORATION. All rights reserved.
2
+ # NVIDIA CORPORATION and its licensors retain all intellectual property
3
+ # and proprietary rights in and to this software, related documentation
4
+ # and any modifications thereto. Any use, reproduction, disclosure or
5
+ # distribution of this software and related documentation without an express
6
+ # license agreement from NVIDIA CORPORATION is strictly prohibited.
7
+
8
+ ###########################################################################
9
+ # Example Smoothed Particle Hydrodynamics
10
+ #
11
+ # Shows how to implement a SPH fluid simulation.
12
+ #
13
+ # Neighbors are found using the wp.HashGrid class, and
14
+ # wp.hash_grid_query(), wp.hash_grid_query_next() kernel methods.
15
+ #
16
+ # Reference Publication
17
+ # Matthias Müller, David Charypar, and Markus H. Gross.
18
+ # "Particle-based fluid simulation for interactive applications."
19
+ # Symposium on Computer animation. Vol. 2. 2003.
20
+ #
21
+ ###########################################################################
22
+
23
+ import os
24
+
25
+ import numpy as np
26
+
27
+ import warp as wp
28
+ import warp.render
29
+
30
+ wp.init()
31
+
32
+
33
+ @wp.func
34
+ def square(x: float):
35
+ return x * x
36
+
37
+
38
+ @wp.func
39
+ def cube(x: float):
40
+ return x * x * x
41
+
42
+
43
+ @wp.func
44
+ def fifth(x: float):
45
+ return x * x * x * x * x
46
+
47
+
48
+ @wp.func
49
+ def density_kernel(xyz: wp.vec3, smoothing_length: float):
50
+ # calculate distance
51
+ distance = wp.dot(xyz, xyz)
52
+
53
+ return wp.max(cube(square(smoothing_length) - distance), 0.0)
54
+
55
+
56
+ @wp.func
57
+ def diff_pressure_kernel(
58
+ xyz: wp.vec3, pressure: float, neighbor_pressure: float, neighbor_rho: float, smoothing_length: float
59
+ ):
60
+ # calculate distance
61
+ distance = wp.sqrt(wp.dot(xyz, xyz))
62
+
63
+ if distance < smoothing_length:
64
+ # calculate terms of kernel
65
+ term_1 = -xyz / distance
66
+ term_2 = (neighbor_pressure + pressure) / (2.0 * neighbor_rho)
67
+ term_3 = square(smoothing_length - distance)
68
+ return term_1 * term_2 * term_3
69
+ else:
70
+ return wp.vec3()
71
+
72
+
73
+ @wp.func
74
+ def diff_viscous_kernel(xyz: wp.vec3, v: wp.vec3, neighbor_v: wp.vec3, neighbor_rho: float, smoothing_length: float):
75
+ # calculate distance
76
+ distance = wp.sqrt(wp.dot(xyz, xyz))
77
+
78
+ # calculate terms of kernel
79
+ if distance < smoothing_length:
80
+ term_1 = (neighbor_v - v) / neighbor_rho
81
+ term_2 = smoothing_length - distance
82
+ return term_1 * term_2
83
+ else:
84
+ return wp.vec3()
85
+
86
+
87
+ @wp.kernel
88
+ def compute_density(
89
+ grid: wp.uint64,
90
+ particle_x: wp.array(dtype=wp.vec3),
91
+ particle_rho: wp.array(dtype=float),
92
+ density_normalization: float,
93
+ smoothing_length: float,
94
+ ):
95
+ tid = wp.tid()
96
+
97
+ # order threads by cell
98
+ i = wp.hash_grid_point_id(grid, tid)
99
+
100
+ # get local particle variables
101
+ x = particle_x[i]
102
+
103
+ # store density
104
+ rho = float(0.0)
105
+
106
+ # particle contact
107
+ neighbors = wp.hash_grid_query(grid, x, smoothing_length)
108
+
109
+ # loop through neighbors to compute density
110
+ for index in neighbors:
111
+ # compute distance
112
+ distance = x - particle_x[index]
113
+
114
+ # compute kernel derivative
115
+ rho += density_kernel(distance, smoothing_length)
116
+
117
+ # add external potential
118
+ particle_rho[i] = density_normalization * rho
119
+
120
+
121
+ @wp.kernel
122
+ def get_acceleration(
123
+ grid: wp.uint64,
124
+ particle_x: wp.array(dtype=wp.vec3),
125
+ particle_v: wp.array(dtype=wp.vec3),
126
+ particle_rho: wp.array(dtype=float),
127
+ particle_a: wp.array(dtype=wp.vec3),
128
+ isotropic_exp: float,
129
+ base_density: float,
130
+ gravity: float,
131
+ pressure_normalization: float,
132
+ viscous_normalization: float,
133
+ smoothing_length: float,
134
+ ):
135
+ tid = wp.tid()
136
+
137
+ # order threads by cell
138
+ i = wp.hash_grid_point_id(grid, tid)
139
+
140
+ # get local particle variables
141
+ x = particle_x[i]
142
+ v = particle_v[i]
143
+ rho = particle_rho[i]
144
+ pressure = isotropic_exp * (rho - base_density)
145
+
146
+ # store forces
147
+ pressure_force = wp.vec3()
148
+ viscous_force = wp.vec3()
149
+
150
+ # particle contact
151
+ neighbors = wp.hash_grid_query(grid, x, smoothing_length)
152
+
153
+ # loop through neighbors to compute acceleration
154
+ for index in neighbors:
155
+ if index != i:
156
+ # get neighbor velocity
157
+ neighbor_v = particle_v[index]
158
+
159
+ # get neighbor density and pressures
160
+ neighbor_rho = particle_rho[index]
161
+ neighbor_pressure = isotropic_exp * (neighbor_rho - base_density)
162
+
163
+ # compute relative position
164
+ relative_position = particle_x[index] - x
165
+
166
+ # calculate pressure force
167
+ pressure_force += diff_pressure_kernel(
168
+ relative_position, pressure, neighbor_pressure, neighbor_rho, smoothing_length
169
+ )
170
+
171
+ # compute kernel derivative
172
+ viscous_force += diff_viscous_kernel(relative_position, v, neighbor_v, neighbor_rho, smoothing_length)
173
+
174
+ # sum all forces
175
+ force = pressure_normalization * pressure_force + viscous_normalization * viscous_force
176
+
177
+ # add external potential
178
+ particle_a[i] = force / rho + wp.vec3(0.0, gravity, 0.0)
179
+
180
+
181
+ @wp.kernel
182
+ def apply_bounds(
183
+ particle_x: wp.array(dtype=wp.vec3),
184
+ particle_v: wp.array(dtype=wp.vec3),
185
+ damping_coef: float,
186
+ width: float,
187
+ height: float,
188
+ length: float,
189
+ ):
190
+ tid = wp.tid()
191
+
192
+ # get pos and velocity
193
+ x = particle_x[tid]
194
+ v = particle_v[tid]
195
+
196
+ # clamp x left
197
+ if x[0] < 0.0:
198
+ x = wp.vec3(0.0, x[1], x[2])
199
+ v = wp.vec3(v[0] * damping_coef, v[1], v[2])
200
+
201
+ # clamp x right
202
+ if x[0] > width:
203
+ x = wp.vec3(width, x[1], x[2])
204
+ v = wp.vec3(v[0] * damping_coef, v[1], v[2])
205
+
206
+ # clamp y bot
207
+ if x[1] < 0.0:
208
+ x = wp.vec3(x[0], 0.0, x[2])
209
+ v = wp.vec3(v[0], v[1] * damping_coef, v[2])
210
+
211
+ # clamp z left
212
+ if x[2] < 0.0:
213
+ x = wp.vec3(x[0], x[1], 0.0)
214
+ v = wp.vec3(v[0], v[1], v[2] * damping_coef)
215
+
216
+ # clamp z right
217
+ if x[2] > length:
218
+ x = wp.vec3(x[0], x[1], length)
219
+ v = wp.vec3(v[0], v[1], v[2] * damping_coef)
220
+
221
+ # apply clamps
222
+ particle_x[tid] = x
223
+ particle_v[tid] = v
224
+
225
+
226
+ @wp.kernel
227
+ def kick(particle_v: wp.array(dtype=wp.vec3), particle_a: wp.array(dtype=wp.vec3), dt: float):
228
+ tid = wp.tid()
229
+ v = particle_v[tid]
230
+ particle_v[tid] = v + particle_a[tid] * dt
231
+
232
+
233
+ @wp.kernel
234
+ def drift(particle_x: wp.array(dtype=wp.vec3), particle_v: wp.array(dtype=wp.vec3), dt: float):
235
+ tid = wp.tid()
236
+ x = particle_x[tid]
237
+ particle_x[tid] = x + particle_v[tid] * dt
238
+
239
+
240
+ @wp.kernel
241
+ def initialize_particles(
242
+ particle_x: wp.array(dtype=wp.vec3), smoothing_length: float, width: float, height: float, length: float
243
+ ):
244
+ tid = wp.tid()
245
+
246
+ # grid size
247
+ nr_x = wp.int32(width / 4.0 / smoothing_length)
248
+ nr_y = wp.int32(height / smoothing_length)
249
+ nr_z = wp.int32(length / 4.0 / smoothing_length)
250
+
251
+ # calculate particle position
252
+ z = wp.float(tid % nr_z)
253
+ y = wp.float((tid // nr_z) % nr_y)
254
+ x = wp.float((tid // (nr_z * nr_y)) % nr_x)
255
+ pos = smoothing_length * wp.vec3(x, y, z)
256
+
257
+ # add small jitter
258
+ state = wp.rand_init(123, tid)
259
+ pos = pos + 0.001 * smoothing_length * wp.vec3(wp.randn(state), wp.randn(state), wp.randn(state))
260
+
261
+ # set position
262
+ particle_x[tid] = pos
263
+
264
+
265
+ class Example:
266
+ def __init__(self, stage):
267
+ # render params
268
+ self.frame_dt = 1.0 / 60.0
269
+ self.frame_count = 600
270
+ self.sim_time = 0.0
271
+
272
+ # simulation params
273
+ self.smoothing_length = 0.8 # NOTE change this to adjust number of particles
274
+ self.width = 80.0 # x
275
+ self.height = 80.0 # y
276
+ self.length = 80.0 # z
277
+ self.isotropic_exp = 20
278
+ self.base_density = 1.0
279
+ self.particle_mass = 0.01 * self.smoothing_length**3 # reduce according to smoothing length
280
+ self.dt = 0.01 * self.smoothing_length # decrease sim dt by smoothing length
281
+ self.dynamic_visc = 0.025
282
+ self.damping_coef = -0.95
283
+ self.gravity = -0.1
284
+ self.n = int(
285
+ self.height * (self.width / 4.0) * (self.height / 4.0) / (self.smoothing_length**3)
286
+ ) # number particles (small box in corner)
287
+ self.sim_step_to_frame_ratio = int(32 / self.smoothing_length)
288
+
289
+ # constants
290
+ self.density_normalization = (315.0 * self.particle_mass) / (
291
+ 64.0 * np.pi * self.smoothing_length**9
292
+ ) # integrate density kernel
293
+ self.pressure_normalization = -(45.0 * self.particle_mass) / (np.pi * self.smoothing_length**6)
294
+ self.viscous_normalization = (45.0 * self.dynamic_visc * self.particle_mass) / (
295
+ np.pi * self.smoothing_length**6
296
+ )
297
+
298
+ # allocate arrays
299
+ self.x = wp.empty(tuple([self.n]), dtype=wp.vec3)
300
+ self.v = wp.zeros(tuple([self.n]), dtype=wp.vec3)
301
+ self.rho = wp.zeros(tuple([self.n]), dtype=float)
302
+ self.a = wp.zeros(tuple([self.n]), dtype=wp.vec3)
303
+
304
+ # set random positions
305
+ wp.launch(
306
+ kernel=initialize_particles,
307
+ dim=self.n,
308
+ inputs=[self.x, self.smoothing_length, self.width, self.height, self.length],
309
+ ) # initialize in small area
310
+
311
+ # create hash array
312
+ grid_size = int(self.height / (4.0 * self.smoothing_length))
313
+ self.grid = wp.HashGrid(grid_size, grid_size, grid_size)
314
+
315
+ # renderer
316
+ self.renderer = None
317
+ if stage:
318
+ self.renderer = wp.render.UsdRenderer(stage)
319
+
320
+ def step(self):
321
+ with wp.ScopedTimer("step", active=True):
322
+ for _ in range(self.sim_step_to_frame_ratio):
323
+ with wp.ScopedTimer("grid build", active=False):
324
+ # build grid
325
+ self.grid.build(self.x, self.smoothing_length)
326
+
327
+ with wp.ScopedTimer("forces", active=False):
328
+ # compute density of points
329
+ wp.launch(
330
+ kernel=compute_density,
331
+ dim=self.n,
332
+ inputs=[self.grid.id, self.x, self.rho, self.density_normalization, self.smoothing_length],
333
+ )
334
+
335
+ # get new acceleration
336
+ wp.launch(
337
+ kernel=get_acceleration,
338
+ dim=self.n,
339
+ inputs=[
340
+ self.grid.id,
341
+ self.x,
342
+ self.v,
343
+ self.rho,
344
+ self.a,
345
+ self.isotropic_exp,
346
+ self.base_density,
347
+ self.gravity,
348
+ self.pressure_normalization,
349
+ self.viscous_normalization,
350
+ self.smoothing_length,
351
+ ],
352
+ )
353
+
354
+ # apply bounds
355
+ wp.launch(
356
+ kernel=apply_bounds,
357
+ dim=self.n,
358
+ inputs=[self.x, self.v, self.damping_coef, self.width, self.height, self.length],
359
+ )
360
+
361
+ # kick
362
+ wp.launch(kernel=kick, dim=self.n, inputs=[self.v, self.a, self.dt])
363
+
364
+ # drift
365
+ wp.launch(kernel=drift, dim=self.n, inputs=[self.x, self.v, self.dt])
366
+
367
+ self.sim_time += self.frame_dt
368
+
369
+ def render(self):
370
+ if self.renderer is None:
371
+ return
372
+
373
+ with wp.ScopedTimer("render", active=True):
374
+ self.renderer.begin_frame(self.sim_time)
375
+ self.renderer.render_points(points=self.x.numpy(), radius=self.smoothing_length, name="points", colors=((0.8, 0.3, 0.2),) * len(self.x))
376
+ self.renderer.end_frame()
377
+
378
+
379
+ if __name__ == "__main__":
380
+ stage_path = os.path.join(os.path.dirname(__file__), "example_sph.usd")
381
+
382
+ example = Example(stage_path)
383
+
384
+ for i in range(example.frame_count):
385
+ example.render()
386
+ example.step()
387
+
388
+ if example.renderer:
389
+ example.renderer.save()