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,391 @@
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 APIC Fluid Simulation
10
+ #
11
+ # Shows how to implement a apic fluid simulation.
12
+ ###########################################################################
13
+
14
+ import os
15
+
16
+ import warp as wp
17
+ import numpy as np
18
+
19
+ from warp.sim import Model, State
20
+ import warp.sim.render
21
+
22
+ import warp.fem as fem
23
+
24
+ from warp.fem import integrand, lookup, normal, grad, at_node, div
25
+ from warp.fem import Field, Sample, Domain
26
+
27
+ from warp.sparse import bsr_mv, bsr_copy, bsr_mm, bsr_transposed, bsr_zeros, BsrMatrix
28
+
29
+ try:
30
+ from .bsr_utils import bsr_cg
31
+ except ImportError:
32
+ from bsr_utils import bsr_cg
33
+
34
+ wp.init()
35
+
36
+
37
+ @integrand
38
+ def integrate_fraction(s: Sample, phi: Field):
39
+ return phi(s)
40
+
41
+
42
+ @integrand
43
+ def integrate_velocity(
44
+ s: Sample,
45
+ domain: Domain,
46
+ u: Field,
47
+ velocities: wp.array(dtype=wp.vec3),
48
+ velocity_gradients: wp.array(dtype=wp.mat33),
49
+ dt: float,
50
+ gravity: wp.vec3,
51
+ ):
52
+ """Transfer particle velocities to grid"""
53
+ node_offset = domain(at_node(u, s)) - domain(s)
54
+ vel_apic = velocities[s.qp_index] + velocity_gradients[s.qp_index] * node_offset
55
+
56
+ vel_adv = vel_apic + dt * gravity
57
+ return wp.dot(u(s), vel_adv)
58
+
59
+
60
+ @integrand
61
+ def update_particles(
62
+ s: Sample,
63
+ domain: Domain,
64
+ grid_vel: Field,
65
+ dt: float,
66
+ pos: wp.array(dtype=wp.vec3),
67
+ pos_prev: wp.array(dtype=wp.vec3),
68
+ vel: wp.array(dtype=wp.vec3),
69
+ vel_grad: wp.array(dtype=wp.mat33),
70
+ ):
71
+ """Read particle velocity from grid and advect positions"""
72
+ vel[s.qp_index] = grid_vel(s)
73
+ vel_grad[s.qp_index] = grad(grid_vel, s)
74
+
75
+ pos_adv = pos_prev[s.qp_index] + dt * vel[s.qp_index]
76
+
77
+ # Project onto domain
78
+ pos_proj = domain(lookup(domain, pos_adv))
79
+ pos[s.qp_index] = pos_proj
80
+
81
+
82
+ @integrand
83
+ def velocity_boundary_projector_form(s: Sample, domain: Domain, u: Field, v: Field):
84
+ """Projector for velocity-Dirichlet boundary conditions"""
85
+
86
+ n = normal(domain, s)
87
+ if n[1] > 0.0:
88
+ # Neuman on top
89
+ return 0.0
90
+
91
+ # Free-slip on other sides
92
+ return wp.dot(u(s), n) * wp.dot(v(s), n)
93
+
94
+
95
+ @integrand
96
+ def divergence_form(s: Sample, u: Field, psi: Field):
97
+ return div(u, s) * psi(s)
98
+
99
+
100
+ @wp.kernel
101
+ def invert_volume_kernel(values: wp.array(dtype=float)):
102
+ i = wp.tid()
103
+ m = values[i]
104
+ if m <= 1.0e-8:
105
+ values[i] = 0.0
106
+ else:
107
+ values[i] = 1.0 / m
108
+
109
+
110
+ @wp.kernel
111
+ def scalar_vector_multiply(
112
+ alpha: wp.array(dtype=float),
113
+ x: wp.array(dtype=wp.vec3),
114
+ y: wp.array(dtype=wp.vec3),
115
+ ):
116
+ i = wp.tid()
117
+ y[i] = alpha[i] * x[i]
118
+
119
+
120
+ @wp.kernel
121
+ def scale_transposed_divergence_mat(
122
+ tr_divergence_mat_offsets: wp.array(dtype=int),
123
+ tr_divergence_mat_values: wp.array(dtype=wp.mat(shape=(3, 1), dtype=float)),
124
+ inv_fraction_int: wp.array(dtype=float),
125
+ ):
126
+ u_i = wp.tid()
127
+ block_beg = tr_divergence_mat_offsets[u_i]
128
+ block_end = tr_divergence_mat_offsets[u_i + 1]
129
+
130
+ for b in range(block_beg, block_end):
131
+ tr_divergence_mat_values[b] = tr_divergence_mat_values[b] * inv_fraction_int[u_i]
132
+
133
+
134
+ def solve_incompressibility(divergence_mat: BsrMatrix, inv_volume, pressure, velocity, quiet: bool = False):
135
+ """Solve for divergence-free velocity delta:
136
+
137
+ delta_velocity = inv_volume * transpose(divergence_mat) * pressure
138
+ divergence_mat * (velocity + delta_velocity) = 0
139
+ """
140
+
141
+ # Build transposed gradient matrix, scale with inverse fraction
142
+ transposed_divergence_mat = bsr_transposed(divergence_mat)
143
+ wp.launch(
144
+ kernel=scale_transposed_divergence_mat,
145
+ dim=inv_volume.shape[0],
146
+ inputs=[
147
+ transposed_divergence_mat.offsets,
148
+ transposed_divergence_mat.values,
149
+ inv_volume,
150
+ ],
151
+ )
152
+
153
+ # For simplicity, assemble schur complement and solve with CG
154
+ schur = bsr_mm(divergence_mat, transposed_divergence_mat)
155
+
156
+ rhs = wp.zeros_like(pressure)
157
+ bsr_mv(A=divergence_mat, x=velocity, y=rhs, alpha=-1.0, beta=0.0)
158
+ bsr_cg(schur, b=rhs, x=pressure, quiet=quiet)
159
+
160
+ # Apply pressure to velocity
161
+ bsr_mv(A=transposed_divergence_mat, x=pressure, y=velocity, alpha=1.0, beta=1.0)
162
+
163
+
164
+ class Example:
165
+ def __init__(self, stage, num_frames=1000, res=[32, 64, 16], quiet=False):
166
+ self.frame_dt = 1.0 / 60
167
+ self.num_frames = num_frames
168
+ self.current_frame = 0
169
+
170
+ self.sim_substeps = 1
171
+ self.sim_dt = self.frame_dt / self.sim_substeps
172
+ self.sim_steps = self.num_frames * self.sim_substeps
173
+
174
+ self._quiet = quiet
175
+
176
+ # grid dimensions and particle emission
177
+ grid_res = np.array(res, dtype=int)
178
+ particle_fill_frac = np.array([0.5, 0.5, 1.0])
179
+ grid_lo = wp.vec3(0.0)
180
+ grid_hi = wp.vec3(50, 100, 25)
181
+
182
+ grid_cell_size = np.array(grid_hi - grid_lo) / grid_res
183
+ grid_cell_volume = np.prod(grid_cell_size)
184
+
185
+ PARTICLES_PER_CELL_DIM = 3
186
+ self.radius = float(np.max(grid_cell_size) / (2 * PARTICLES_PER_CELL_DIM))
187
+
188
+ particle_grid_res = np.array(particle_fill_frac * grid_res * PARTICLES_PER_CELL_DIM, dtype=int)
189
+ particle_grid_offset = wp.vec3(self.radius, self.radius, self.radius)
190
+
191
+ np.random.seed(0)
192
+ builder = wp.sim.ModelBuilder()
193
+ builder.add_particle_grid(
194
+ dim_x=particle_grid_res[0],
195
+ dim_y=particle_grid_res[1],
196
+ dim_z=particle_grid_res[2],
197
+ cell_x=self.radius * 2.0,
198
+ cell_y=self.radius * 2.0,
199
+ cell_z=self.radius * 2.0,
200
+ pos=wp.vec3(0.0, 0.0, 0.0) + particle_grid_offset,
201
+ rot=wp.quat_identity(),
202
+ vel=wp.vec3(0.0, 0.0, 0.0),
203
+ mass=grid_cell_volume / PARTICLES_PER_CELL_DIM**3,
204
+ jitter=self.radius * 1.0,
205
+ radius_mean=self.radius,
206
+ )
207
+
208
+ self.grid = fem.Grid3D(wp.vec3i(grid_res), grid_lo, grid_hi)
209
+
210
+ # Function spaces
211
+ self.velocity_space = fem.make_polynomial_space(self.grid, dtype=wp.vec3, degree=1)
212
+ self.fraction_space = fem.make_polynomial_space(self.grid, dtype=float, degree=1)
213
+ self.strain_space = fem.make_polynomial_space(
214
+ self.grid,
215
+ dtype=float,
216
+ degree=0,
217
+ )
218
+
219
+ self.pressure_field = self.strain_space.make_field()
220
+ self.velocity_field = self.velocity_space.make_field()
221
+
222
+ # Test and trial functions
223
+ self.domain = fem.Cells(self.grid)
224
+ self.velocity_test = fem.make_test(self.velocity_space, domain=self.domain)
225
+ self.velocity_trial = fem.make_trial(self.velocity_space, domain=self.domain)
226
+ self.fraction_test = fem.make_test(self.fraction_space, domain=self.domain)
227
+ self.strain_test = fem.make_test(self.strain_space, domain=self.domain)
228
+ self.strain_trial = fem.make_trial(self.strain_space, domain=self.domain)
229
+
230
+ # Enforcing the Dirichlet boundary condition the hard way;
231
+ # build projector for velocity left- and right-hand-sides
232
+ boundary = fem.BoundarySides(self.grid)
233
+ u_bd_test = fem.make_test(space=self.velocity_space, domain=boundary)
234
+ u_bd_trial = fem.make_trial(space=self.velocity_space, domain=boundary)
235
+ u_bd_projector = fem.integrate(
236
+ velocity_boundary_projector_form, fields={"u": u_bd_trial, "v": u_bd_test}, nodal=True, output_dtype=float
237
+ )
238
+
239
+ fem.normalize_dirichlet_projector(u_bd_projector)
240
+ self.vel_bd_projector = u_bd_projector
241
+
242
+ # Storage for temporary variables
243
+ self.temporary_store = fem.TemporaryStore()
244
+
245
+ self._divergence_matrix = bsr_zeros(
246
+ self.strain_space.node_count(),
247
+ self.velocity_space.node_count(),
248
+ block_type=wp.mat(shape=(1, 3), dtype=float),
249
+ )
250
+
251
+ # Warp.sim model
252
+ self.model: Model = builder.finalize()
253
+
254
+ if not self._quiet:
255
+ print("Particle count:", self.model.particle_count)
256
+
257
+ self.state_0: State = self.model.state()
258
+ self.state_0.particle_qd_grad = wp.zeros(shape=(self.model.particle_count), dtype=wp.mat33)
259
+
260
+ self.state_1: State = self.model.state()
261
+ self.state_1.particle_qd_grad = wp.zeros(shape=(self.model.particle_count), dtype=wp.mat33)
262
+
263
+ self.renderer = None
264
+ try:
265
+ self.renderer = warp.sim.render.SimRenderer(self.model, stage, scaling=20.0)
266
+ except Exception as err:
267
+ print(f"Could not initialize SimRenderer for stage '{stage}': {err}.")
268
+
269
+ def step(self):
270
+ fem.set_default_temporary_store(self.temporary_store)
271
+
272
+ self.current_frame = self.current_frame + 1
273
+ with wp.ScopedTimer(f"simulate frame {self.current_frame}", active=True):
274
+ for s in range(self.sim_substeps):
275
+ # Bin particles to grid cells
276
+ pic = fem.PicQuadrature(
277
+ domain=fem.Cells(self.grid), positions=self.state_0.particle_q, measures=self.model.particle_mass
278
+ )
279
+
280
+ # Borrow some temporary arrays for storing integration results
281
+ inv_volume_temporary = fem.borrow_temporary(
282
+ self.temporary_store, shape=(self.fraction_space.node_count()), dtype=float
283
+ )
284
+ velocity_int_temporary = fem.borrow_temporary(
285
+ self.temporary_store, shape=(self.velocity_space.node_count()), dtype=wp.vec3
286
+ )
287
+ inv_volume = inv_volume_temporary.array
288
+ velocity_int = velocity_int_temporary.array
289
+
290
+ # Inverse volume fraction
291
+ fem.integrate(
292
+ integrate_fraction,
293
+ quadrature=pic,
294
+ fields={"phi": self.fraction_test},
295
+ accumulate_dtype=float,
296
+ output=inv_volume,
297
+ )
298
+ wp.launch(kernel=invert_volume_kernel, dim=inv_volume.shape, inputs=[inv_volume])
299
+
300
+ # Velocity right-hand side
301
+ fem.integrate(
302
+ integrate_velocity,
303
+ quadrature=pic,
304
+ fields={"u": self.velocity_test},
305
+ values={
306
+ "velocities": self.state_0.particle_qd,
307
+ "velocity_gradients": self.state_0.particle_qd_grad,
308
+ "dt": self.sim_dt,
309
+ "gravity": self.model.gravity,
310
+ },
311
+ accumulate_dtype=float,
312
+ output=velocity_int,
313
+ )
314
+
315
+ # Compute constraint-free velocity
316
+ wp.launch(
317
+ kernel=scalar_vector_multiply,
318
+ dim=inv_volume.shape[0],
319
+ inputs=[inv_volume, velocity_int, self.velocity_field.dof_values],
320
+ )
321
+
322
+ # Apply velocity boundary conditions:
323
+ # velocity -= vel_bd_projector * velocity
324
+ wp.copy(src=self.velocity_field.dof_values, dest=velocity_int)
325
+ bsr_mv(A=self.vel_bd_projector, x=velocity_int, y=self.velocity_field.dof_values, alpha=-1.0, beta=1.0)
326
+
327
+ # Divergence matrix
328
+ fem.integrate(
329
+ divergence_form,
330
+ quadrature=pic,
331
+ fields={"u": self.velocity_trial, "psi": self.strain_test},
332
+ accumulate_dtype=float,
333
+ output=self._divergence_matrix,
334
+ )
335
+
336
+ # Project matrix to enforce boundary conditions
337
+ divergence_mat_tmp = bsr_copy(self._divergence_matrix)
338
+ bsr_mm(alpha=-1.0, x=divergence_mat_tmp, y=self.vel_bd_projector, z=self._divergence_matrix, beta=1.0)
339
+
340
+ # Solve unilateral incompressibility
341
+ solve_incompressibility(
342
+ self._divergence_matrix,
343
+ inv_volume,
344
+ self.pressure_field.dof_values,
345
+ self.velocity_field.dof_values,
346
+ quiet=self._quiet,
347
+ )
348
+
349
+ # (A)PIC advection
350
+ fem.interpolate(
351
+ update_particles,
352
+ quadrature=pic,
353
+ values={
354
+ "pos": self.state_1.particle_q,
355
+ "pos_prev": self.state_0.particle_q,
356
+ "vel": self.state_1.particle_qd,
357
+ "vel_grad": self.state_1.particle_qd_grad,
358
+ "dt": self.sim_dt,
359
+ },
360
+ fields={"grid_vel": self.velocity_field},
361
+ )
362
+
363
+ # swap states
364
+ (self.state_0, self.state_1) = (self.state_1, self.state_0)
365
+
366
+ fem.set_default_temporary_store(None)
367
+
368
+ def render(self, is_live=False):
369
+ if self.renderer is None:
370
+ return
371
+
372
+ with wp.ScopedTimer("render", active=True):
373
+ time = self.current_frame * self.frame_dt
374
+
375
+ self.renderer.begin_frame(time)
376
+ self.renderer.render(self.state_0)
377
+ self.renderer.end_frame()
378
+
379
+
380
+ if __name__ == "__main__":
381
+ wp.set_module_options({"enable_backward": False})
382
+
383
+ stage_path = os.path.join(os.path.dirname(__file__), "example_apic_fluid.usd")
384
+
385
+ example = Example(stage_path)
386
+
387
+ for i in range(example.num_frames):
388
+ example.step()
389
+ example.render()
390
+
391
+ example.renderer.save()
@@ -0,0 +1,168 @@
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 Convection Diffusion
10
+ #
11
+ # This example simulates a convection-diffusion PDE using
12
+ # semi-Lagrangian advection
13
+ #
14
+ # D phi / dt - nu d2 phi / dx^2 = 0
15
+ ###########################################################################
16
+
17
+ import argparse
18
+
19
+ import warp as wp
20
+ import warp.fem as fem
21
+
22
+ # Import example utilities
23
+ # Make sure that works both when imported as module and run as standalone file
24
+ try:
25
+ from .bsr_utils import bsr_cg
26
+ from .mesh_utils import gen_trimesh
27
+ from .plot_utils import Plot
28
+ except ImportError:
29
+ from bsr_utils import bsr_cg
30
+ from mesh_utils import gen_trimesh
31
+ from plot_utils import Plot
32
+
33
+ wp.init()
34
+
35
+
36
+ @fem.integrand
37
+ def initial_condition(domain: fem.Domain, s: fem.Sample):
38
+ """Initial condition: 1.0 in ]0.6, 0.4[ x ]0.2, 0.8[, 0.0 elsewhere"""
39
+ pos = domain(s)
40
+ if pos[0] > 0.4 and pos[0] < 0.6 and pos[1] > 0.2 and pos[1] < 0.8:
41
+ return 1.0
42
+ return 0.0
43
+
44
+
45
+ @wp.func
46
+ def velocity(pos: wp.vec2, ang_vel: float):
47
+ center = wp.vec2(0.5, 0.5)
48
+ offset = pos - center
49
+ return wp.vec2(offset[1], -offset[0]) * ang_vel
50
+
51
+
52
+ @fem.integrand
53
+ def inertia_form(s: fem.Sample, phi: fem.Field, psi: fem.Field, dt: float):
54
+ return phi(s) * psi(s) / dt
55
+
56
+
57
+ @fem.integrand
58
+ def transported_inertia_form(
59
+ s: fem.Sample, domain: fem.Domain, phi: fem.Field, psi: fem.Field, ang_vel: float, dt: float
60
+ ):
61
+ pos = domain(s)
62
+ vel = velocity(pos, ang_vel)
63
+
64
+ # semi-Lagrangian advection; evaluate phi upstream
65
+ conv_pos = pos - vel * dt
66
+ # lookup operator constructs a Sample from a world position.
67
+ # the optional last argument provides a initial guess for the lookup
68
+ conv_phi = phi(fem.lookup(domain, conv_pos, s))
69
+
70
+ return conv_phi * psi(s) / dt
71
+
72
+
73
+ @fem.integrand
74
+ def diffusion_form(
75
+ s: fem.Sample,
76
+ u: fem.Field,
77
+ v: fem.Field,
78
+ ):
79
+ return wp.dot(
80
+ fem.grad(u, s),
81
+ fem.grad(v, s),
82
+ )
83
+
84
+
85
+ @fem.integrand
86
+ def diffusion_and_inertia_form(s: fem.Sample, phi: fem.Field, psi: fem.Field, dt: float, nu: float):
87
+ return inertia_form(s, phi, psi, dt) + nu * diffusion_form(s, phi, psi)
88
+
89
+
90
+ class Example:
91
+ parser = argparse.ArgumentParser()
92
+ parser.add_argument("--resolution", type=int, default=50)
93
+ parser.add_argument("--degree", type=int, default=2)
94
+ parser.add_argument("--num_frames", type=int, default=250)
95
+ parser.add_argument("--viscosity", type=float, default=0.001)
96
+ parser.add_argument("--ang_vel", type=float, default=1.0)
97
+ parser.add_argument("--tri_mesh", action="store_true", help="Use a triangular mesh")
98
+
99
+ def __init__(self, stage=None, quiet=False, args=None, **kwargs):
100
+ if args is None:
101
+ # Read args from kwargs, add default arg values from parser
102
+ args = argparse.Namespace(**kwargs)
103
+ args = Example.parser.parse_args(args=[], namespace=args)
104
+ self._args = args
105
+ self._quiet = quiet
106
+
107
+ res = args.resolution
108
+ self.sim_dt = 1.0 / (args.ang_vel * res)
109
+ self.current_frame = 0
110
+
111
+ if args.tri_mesh:
112
+ positions, tri_vidx = gen_trimesh(res=wp.vec2i(res))
113
+ geo = fem.Trimesh2D(tri_vertex_indices=tri_vidx, positions=positions)
114
+ else:
115
+ geo = fem.Grid2D(res=wp.vec2i(res))
116
+
117
+ domain = fem.Cells(geometry=geo)
118
+ scalar_space = fem.make_polynomial_space(geo, degree=args.degree)
119
+
120
+ # Initial condition
121
+ self._phi_field = scalar_space.make_field()
122
+ fem.interpolate(initial_condition, dest=self._phi_field)
123
+
124
+ # Assemble diffusion and inertia matrix
125
+ self._test = fem.make_test(space=scalar_space, domain=domain)
126
+ self._trial = fem.make_trial(space=scalar_space, domain=domain)
127
+ self._matrix = fem.integrate(
128
+ diffusion_and_inertia_form,
129
+ fields={"phi": self._trial, "psi": self._test},
130
+ values={"nu": args.viscosity, "dt": self.sim_dt},
131
+ output_dtype=float,
132
+ )
133
+
134
+ self.renderer = Plot(stage)
135
+ self.renderer.add_surface("phi", self._phi_field)
136
+
137
+ def step(self):
138
+ self.current_frame += 1
139
+
140
+ # right-hand-side -- advected inertia
141
+ rhs = fem.integrate(
142
+ transported_inertia_form,
143
+ fields={"phi": self._phi_field, "psi": self._test},
144
+ values={"ang_vel": self._args.ang_vel, "dt": self.sim_dt},
145
+ output_dtype=float,
146
+ )
147
+
148
+ # Solve linear system
149
+ bsr_cg(self._matrix, x=self._phi_field.dof_values, b=rhs, quiet=self._quiet, tol=1.0e-12)
150
+
151
+ def render(self):
152
+ self.renderer.begin_frame(time = self.current_frame * self.sim_dt)
153
+ self.renderer.add_surface("phi", self._phi_field)
154
+ self.renderer.end_frame()
155
+
156
+
157
+ if __name__ == "__main__":
158
+ wp.set_module_options({"enable_backward": False})
159
+
160
+ args = Example.parser.parse_args()
161
+
162
+ example = Example(args=args)
163
+ for k in range(args.num_frames):
164
+ print(f"Frame {k}:")
165
+ example.step()
166
+ example.render()
167
+
168
+ example.renderer.plot()