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.
- warp/__init__.py +8 -0
- warp/bin/warp-clang.so +0 -0
- warp/bin/warp.so +0 -0
- warp/build.py +7 -6
- warp/build_dll.py +70 -79
- warp/builtins.py +10 -6
- warp/codegen.py +51 -19
- warp/config.py +7 -8
- warp/constants.py +3 -0
- warp/context.py +948 -245
- warp/dlpack.py +198 -113
- warp/examples/assets/bunny.usd +0 -0
- warp/examples/assets/cartpole.urdf +110 -0
- warp/examples/assets/crazyflie.usd +0 -0
- warp/examples/assets/cube.usda +42 -0
- warp/examples/assets/nv_ant.xml +92 -0
- warp/examples/assets/nv_humanoid.xml +183 -0
- warp/examples/assets/quadruped.urdf +268 -0
- warp/examples/assets/rocks.nvdb +0 -0
- warp/examples/assets/rocks.usd +0 -0
- warp/examples/assets/sphere.usda +56 -0
- warp/examples/assets/torus.usda +105 -0
- warp/examples/benchmarks/benchmark_api.py +383 -0
- warp/examples/benchmarks/benchmark_cloth.py +279 -0
- warp/examples/benchmarks/benchmark_cloth_cupy.py +88 -0
- warp/examples/benchmarks/benchmark_cloth_jax.py +100 -0
- warp/examples/benchmarks/benchmark_cloth_numba.py +142 -0
- warp/examples/benchmarks/benchmark_cloth_numpy.py +77 -0
- warp/examples/benchmarks/benchmark_cloth_pytorch.py +86 -0
- warp/examples/benchmarks/benchmark_cloth_taichi.py +112 -0
- warp/examples/benchmarks/benchmark_cloth_warp.py +146 -0
- warp/examples/benchmarks/benchmark_launches.py +295 -0
- warp/examples/core/example_dem.py +221 -0
- warp/examples/core/example_fluid.py +267 -0
- warp/examples/core/example_graph_capture.py +129 -0
- warp/examples/core/example_marching_cubes.py +177 -0
- warp/examples/core/example_mesh.py +154 -0
- warp/examples/core/example_mesh_intersect.py +193 -0
- warp/examples/core/example_nvdb.py +169 -0
- warp/examples/core/example_raycast.py +89 -0
- warp/examples/core/example_raymarch.py +178 -0
- warp/examples/core/example_render_opengl.py +141 -0
- warp/examples/core/example_sph.py +389 -0
- warp/examples/core/example_torch.py +181 -0
- warp/examples/core/example_wave.py +249 -0
- warp/examples/fem/bsr_utils.py +380 -0
- warp/examples/fem/example_apic_fluid.py +391 -0
- warp/examples/fem/example_convection_diffusion.py +168 -0
- warp/examples/fem/example_convection_diffusion_dg.py +209 -0
- warp/examples/fem/example_convection_diffusion_dg0.py +194 -0
- warp/examples/fem/example_deformed_geometry.py +159 -0
- warp/examples/fem/example_diffusion.py +173 -0
- warp/examples/fem/example_diffusion_3d.py +152 -0
- warp/examples/fem/example_diffusion_mgpu.py +214 -0
- warp/examples/fem/example_mixed_elasticity.py +222 -0
- warp/examples/fem/example_navier_stokes.py +243 -0
- warp/examples/fem/example_stokes.py +192 -0
- warp/examples/fem/example_stokes_transfer.py +249 -0
- warp/examples/fem/mesh_utils.py +109 -0
- warp/examples/fem/plot_utils.py +287 -0
- warp/examples/optim/example_bounce.py +248 -0
- warp/examples/optim/example_cloth_throw.py +210 -0
- warp/examples/optim/example_diffray.py +535 -0
- warp/examples/optim/example_drone.py +850 -0
- warp/examples/optim/example_inverse_kinematics.py +169 -0
- warp/examples/optim/example_inverse_kinematics_torch.py +170 -0
- warp/examples/optim/example_spring_cage.py +234 -0
- warp/examples/optim/example_trajectory.py +201 -0
- warp/examples/sim/example_cartpole.py +128 -0
- warp/examples/sim/example_cloth.py +184 -0
- warp/examples/sim/example_granular.py +113 -0
- warp/examples/sim/example_granular_collision_sdf.py +185 -0
- warp/examples/sim/example_jacobian_ik.py +213 -0
- warp/examples/sim/example_particle_chain.py +106 -0
- warp/examples/sim/example_quadruped.py +179 -0
- warp/examples/sim/example_rigid_chain.py +191 -0
- warp/examples/sim/example_rigid_contact.py +176 -0
- warp/examples/sim/example_rigid_force.py +126 -0
- warp/examples/sim/example_rigid_gyroscopic.py +97 -0
- warp/examples/sim/example_rigid_soft_contact.py +124 -0
- warp/examples/sim/example_soft_body.py +178 -0
- warp/fabric.py +29 -20
- warp/fem/cache.py +0 -1
- warp/fem/dirichlet.py +0 -2
- warp/fem/integrate.py +0 -1
- warp/jax.py +45 -0
- warp/jax_experimental.py +339 -0
- warp/native/builtin.h +12 -0
- warp/native/bvh.cu +18 -18
- warp/native/clang/clang.cpp +8 -3
- warp/native/cuda_util.cpp +94 -5
- warp/native/cuda_util.h +35 -6
- warp/native/cutlass_gemm.cpp +1 -1
- warp/native/cutlass_gemm.cu +4 -1
- warp/native/error.cpp +66 -0
- warp/native/error.h +27 -0
- warp/native/mesh.cu +2 -2
- warp/native/reduce.cu +4 -4
- warp/native/runlength_encode.cu +2 -2
- warp/native/scan.cu +2 -2
- warp/native/sparse.cu +0 -1
- warp/native/temp_buffer.h +2 -2
- warp/native/warp.cpp +95 -60
- warp/native/warp.cu +1053 -218
- warp/native/warp.h +49 -32
- warp/optim/linear.py +33 -16
- warp/render/render_opengl.py +202 -101
- warp/render/render_usd.py +82 -40
- warp/sim/__init__.py +13 -4
- warp/sim/articulation.py +4 -5
- warp/sim/collide.py +320 -175
- warp/sim/import_mjcf.py +25 -30
- warp/sim/import_urdf.py +94 -63
- warp/sim/import_usd.py +51 -36
- warp/sim/inertia.py +3 -2
- warp/sim/integrator.py +233 -0
- warp/sim/integrator_euler.py +447 -469
- warp/sim/integrator_featherstone.py +1991 -0
- warp/sim/integrator_xpbd.py +1420 -640
- warp/sim/model.py +765 -487
- warp/sim/particles.py +2 -1
- warp/sim/render.py +35 -13
- warp/sim/utils.py +222 -11
- warp/stubs.py +8 -0
- warp/tape.py +16 -1
- warp/tests/aux_test_grad_customs.py +23 -0
- warp/tests/test_array.py +190 -1
- warp/tests/test_async.py +656 -0
- warp/tests/test_bool.py +50 -0
- warp/tests/test_dlpack.py +164 -11
- warp/tests/test_examples.py +166 -74
- warp/tests/test_fem.py +8 -1
- warp/tests/test_generics.py +15 -5
- warp/tests/test_grad.py +1 -1
- warp/tests/test_grad_customs.py +172 -12
- warp/tests/test_jax.py +254 -0
- warp/tests/test_large.py +29 -6
- warp/tests/test_launch.py +25 -0
- warp/tests/test_linear_solvers.py +20 -3
- warp/tests/test_matmul.py +61 -16
- warp/tests/test_matmul_lite.py +13 -13
- warp/tests/test_mempool.py +186 -0
- warp/tests/test_multigpu.py +3 -0
- warp/tests/test_options.py +16 -2
- warp/tests/test_peer.py +137 -0
- warp/tests/test_print.py +3 -1
- warp/tests/test_quat.py +23 -0
- warp/tests/test_sim_kinematics.py +97 -0
- warp/tests/test_snippet.py +126 -3
- warp/tests/test_streams.py +108 -79
- warp/tests/test_torch.py +16 -8
- warp/tests/test_utils.py +32 -27
- warp/tests/test_verify_fp.py +65 -0
- warp/tests/test_volume.py +1 -1
- warp/tests/unittest_serial.py +2 -0
- warp/tests/unittest_suites.py +12 -0
- warp/tests/unittest_utils.py +14 -7
- warp/thirdparty/unittest_parallel.py +15 -3
- warp/torch.py +10 -8
- warp/types.py +363 -246
- warp/utils.py +143 -19
- warp_lang-1.0.0.dist-info/LICENSE.md +126 -0
- warp_lang-1.0.0.dist-info/METADATA +394 -0
- {warp_lang-0.11.0.dist-info → warp_lang-1.0.0.dist-info}/RECORD +167 -86
- warp/sim/optimizer.py +0 -138
- warp_lang-0.11.0.dist-info/LICENSE.md +0 -36
- warp_lang-0.11.0.dist-info/METADATA +0 -238
- /warp/tests/{walkthough_debug.py → walkthrough_debug.py} +0 -0
- {warp_lang-0.11.0.dist-info → warp_lang-1.0.0.dist-info}/WHEEL +0 -0
- {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()
|