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,222 @@
|
|
|
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 Mixed Elasticity
|
|
10
|
+
#
|
|
11
|
+
# This example illustrates using Mixed FEM to solve a
|
|
12
|
+
# 2D linear elasticity problem:
|
|
13
|
+
#
|
|
14
|
+
# Div[ E: D(u) ] = 0
|
|
15
|
+
#
|
|
16
|
+
# with Dirichlet boundary conditions on horizontal sides,
|
|
17
|
+
# and E the elasticity rank-4 tensor
|
|
18
|
+
###########################################################################
|
|
19
|
+
|
|
20
|
+
import argparse
|
|
21
|
+
|
|
22
|
+
import warp as wp
|
|
23
|
+
import numpy as np
|
|
24
|
+
|
|
25
|
+
import warp.fem as fem
|
|
26
|
+
|
|
27
|
+
from warp.sparse import bsr_transposed, bsr_mm
|
|
28
|
+
|
|
29
|
+
try:
|
|
30
|
+
from .plot_utils import Plot
|
|
31
|
+
from .bsr_utils import bsr_cg, invert_diagonal_bsr_mass_matrix
|
|
32
|
+
from .mesh_utils import gen_trimesh, gen_quadmesh
|
|
33
|
+
except ImportError:
|
|
34
|
+
from plot_utils import Plot
|
|
35
|
+
from bsr_utils import bsr_cg, invert_diagonal_bsr_mass_matrix
|
|
36
|
+
from mesh_utils import gen_trimesh, gen_quadmesh
|
|
37
|
+
|
|
38
|
+
wp.init()
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@wp.func
|
|
42
|
+
def compute_stress(tau: wp.mat22, E: wp.mat33):
|
|
43
|
+
"""Strain to stress computation"""
|
|
44
|
+
tau_sym = wp.vec3(tau[0, 0], tau[1, 1], tau[0, 1] + tau[1, 0])
|
|
45
|
+
sig_sym = E * tau_sym
|
|
46
|
+
return wp.mat22(sig_sym[0], 0.5 * sig_sym[2], 0.5 * sig_sym[2], sig_sym[1])
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@fem.integrand
|
|
50
|
+
def symmetric_grad_form(
|
|
51
|
+
s: fem.Sample,
|
|
52
|
+
u: fem.Field,
|
|
53
|
+
tau: fem.Field,
|
|
54
|
+
):
|
|
55
|
+
"""D(u) : tau"""
|
|
56
|
+
return wp.ddot(tau(s), fem.D(u, s))
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
@fem.integrand
|
|
60
|
+
def stress_form(s: fem.Sample, u: fem.Field, tau: fem.Field, E: wp.mat33):
|
|
61
|
+
"""(E : D(u)) : tau"""
|
|
62
|
+
return wp.ddot(tau(s), compute_stress(fem.D(u, s), E))
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
@fem.integrand
|
|
66
|
+
def horizontal_boundary_projector_form(
|
|
67
|
+
s: fem.Sample,
|
|
68
|
+
domain: fem.Domain,
|
|
69
|
+
u: fem.Field,
|
|
70
|
+
v: fem.Field,
|
|
71
|
+
):
|
|
72
|
+
# non zero on horizontal boundary of domain only
|
|
73
|
+
nor = fem.normal(domain, s)
|
|
74
|
+
return wp.dot(u(s), v(s)) * wp.abs(nor[1])
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
@fem.integrand
|
|
78
|
+
def horizontal_displacement_form(
|
|
79
|
+
s: fem.Sample,
|
|
80
|
+
domain: fem.Domain,
|
|
81
|
+
v: fem.Field,
|
|
82
|
+
displacement: float,
|
|
83
|
+
):
|
|
84
|
+
# opposed to normal on horizontal boundary of domain only
|
|
85
|
+
nor = fem.normal(domain, s)
|
|
86
|
+
return -wp.abs(nor[1]) * displacement * wp.dot(nor, v(s))
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
@fem.integrand
|
|
90
|
+
def tensor_mass_form(
|
|
91
|
+
s: fem.Sample,
|
|
92
|
+
sig: fem.Field,
|
|
93
|
+
tau: fem.Field,
|
|
94
|
+
):
|
|
95
|
+
return wp.ddot(tau(s), sig(s))
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
class Example:
|
|
99
|
+
parser = argparse.ArgumentParser()
|
|
100
|
+
parser.add_argument("--resolution", type=int, default=25)
|
|
101
|
+
parser.add_argument("--degree", type=int, default=2)
|
|
102
|
+
parser.add_argument("--displacement", type=float, default=0.1)
|
|
103
|
+
parser.add_argument("--young_modulus", type=float, default=1.0)
|
|
104
|
+
parser.add_argument("--poisson_ratio", type=float, default=0.5)
|
|
105
|
+
parser.add_argument("--mesh", choices=("grid", "tri", "quad"), default="grid", help="Mesh type")
|
|
106
|
+
parser.add_argument(
|
|
107
|
+
"--nonconforming_stresses", action="store_true", help="For grid, use non-conforming stresses (Q_d/P_d)"
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
def __init__(self, stage=None, quiet=False, args=None, **kwargs):
|
|
111
|
+
if args is None:
|
|
112
|
+
# Read args from kwargs, add default arg values from parser
|
|
113
|
+
args = argparse.Namespace(**kwargs)
|
|
114
|
+
args = Example.parser.parse_args(args=[], namespace=args)
|
|
115
|
+
self._args = args
|
|
116
|
+
self._quiet = quiet
|
|
117
|
+
|
|
118
|
+
# Grid or triangle mesh geometry
|
|
119
|
+
if args.mesh == "tri":
|
|
120
|
+
positions, tri_vidx = gen_trimesh(res=wp.vec2i(args.resolution))
|
|
121
|
+
self._geo = fem.Trimesh2D(tri_vertex_indices=tri_vidx, positions=positions)
|
|
122
|
+
elif args.mesh == "quad":
|
|
123
|
+
positions, quad_vidx = gen_quadmesh(res=wp.vec2i(args.resolution))
|
|
124
|
+
self._geo = fem.Quadmesh2D(quad_vertex_indices=quad_vidx, positions=positions)
|
|
125
|
+
else:
|
|
126
|
+
self._geo = fem.Grid2D(res=wp.vec2i(args.resolution))
|
|
127
|
+
|
|
128
|
+
# Strain-stress matrix
|
|
129
|
+
young = args.young_modulus
|
|
130
|
+
poisson = args.poisson_ratio
|
|
131
|
+
self._elasticity_mat = wp.mat33(
|
|
132
|
+
young
|
|
133
|
+
/ (1.0 - poisson * poisson)
|
|
134
|
+
* np.array(
|
|
135
|
+
[
|
|
136
|
+
[1.0, poisson, 0.0],
|
|
137
|
+
[poisson, 1.0, 0.0],
|
|
138
|
+
[0.0, 0.0, (2.0 * (1.0 + poisson)) * (1.0 - poisson * poisson)],
|
|
139
|
+
]
|
|
140
|
+
)
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
# Function spaces -- S_k for displacement, Q_{k-1}d for stress
|
|
144
|
+
self._u_space = fem.make_polynomial_space(
|
|
145
|
+
self._geo, degree=args.degree, dtype=wp.vec2, element_basis=fem.ElementBasis.SERENDIPITY
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
# Store stress degrees of freedom as symmetric tensors (3 dof) rather than full 2x2 matrices
|
|
149
|
+
tau_basis = (
|
|
150
|
+
fem.ElementBasis.NONCONFORMING_POLYNOMIAL if args.nonconforming_stresses else fem.ElementBasis.LAGRANGE
|
|
151
|
+
)
|
|
152
|
+
self._tau_space = fem.make_polynomial_space(
|
|
153
|
+
self._geo,
|
|
154
|
+
degree=args.degree - 1,
|
|
155
|
+
discontinuous=True,
|
|
156
|
+
element_basis=tau_basis,
|
|
157
|
+
dof_mapper=fem.SymmetricTensorMapper(wp.mat22),
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
self._u_field = self._u_space.make_field()
|
|
161
|
+
|
|
162
|
+
self.renderer = Plot(stage)
|
|
163
|
+
|
|
164
|
+
def step(self):
|
|
165
|
+
boundary = fem.BoundarySides(self._geo)
|
|
166
|
+
domain = fem.Cells(geometry=self._geo)
|
|
167
|
+
|
|
168
|
+
# Displacement boundary conditions
|
|
169
|
+
u_bd_test = fem.make_test(space=self._u_space, domain=boundary)
|
|
170
|
+
u_bd_trial = fem.make_trial(space=self._u_space, domain=boundary)
|
|
171
|
+
u_bd_rhs = fem.integrate(
|
|
172
|
+
horizontal_displacement_form,
|
|
173
|
+
fields={"v": u_bd_test},
|
|
174
|
+
values={"displacement": self._args.displacement},
|
|
175
|
+
nodal=True,
|
|
176
|
+
output_dtype=wp.vec2d,
|
|
177
|
+
)
|
|
178
|
+
u_bd_matrix = fem.integrate(
|
|
179
|
+
horizontal_boundary_projector_form, fields={"u": u_bd_trial, "v": u_bd_test}, nodal=True
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
# Stress/velocity coupling
|
|
183
|
+
u_trial = fem.make_trial(space=self._u_space, domain=domain)
|
|
184
|
+
tau_test = fem.make_test(space=self._tau_space, domain=domain)
|
|
185
|
+
tau_trial = fem.make_trial(space=self._tau_space, domain=domain)
|
|
186
|
+
|
|
187
|
+
sym_grad_matrix = fem.integrate(symmetric_grad_form, fields={"u": u_trial, "tau": tau_test})
|
|
188
|
+
stress_matrix = fem.integrate(
|
|
189
|
+
stress_form, fields={"u": u_trial, "tau": tau_test}, values={"E": self._elasticity_mat}
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
# Compute inverse of the (block-diagonal) tau mass matrix
|
|
193
|
+
tau_inv_mass_matrix = fem.integrate(tensor_mass_form, fields={"sig": tau_trial, "tau": tau_test}, nodal=True)
|
|
194
|
+
invert_diagonal_bsr_mass_matrix(tau_inv_mass_matrix)
|
|
195
|
+
|
|
196
|
+
# Assemble system matrix
|
|
197
|
+
u_matrix = bsr_mm(bsr_transposed(sym_grad_matrix), bsr_mm(tau_inv_mass_matrix, stress_matrix))
|
|
198
|
+
|
|
199
|
+
# Enforce boundary conditions
|
|
200
|
+
u_rhs = wp.zeros_like(u_bd_rhs)
|
|
201
|
+
fem.project_linear_system(u_matrix, u_rhs, u_bd_matrix, u_bd_rhs)
|
|
202
|
+
|
|
203
|
+
x = wp.zeros_like(u_rhs)
|
|
204
|
+
bsr_cg(u_matrix, b=u_rhs, x=x, tol=1.0e-16, quiet=self._quiet)
|
|
205
|
+
|
|
206
|
+
# Extract result
|
|
207
|
+
self._u_field.dof_values = x
|
|
208
|
+
|
|
209
|
+
def render(self):
|
|
210
|
+
self.renderer.add_surface_vector("solution", self._u_field)
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
if __name__ == "__main__":
|
|
214
|
+
wp.set_module_options({"enable_backward": False})
|
|
215
|
+
|
|
216
|
+
args = Example.parser.parse_args()
|
|
217
|
+
|
|
218
|
+
example = Example(args=args)
|
|
219
|
+
example.step()
|
|
220
|
+
example.render()
|
|
221
|
+
|
|
222
|
+
example.renderer.plot()
|
|
@@ -0,0 +1,243 @@
|
|
|
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 Navier Stokes
|
|
10
|
+
#
|
|
11
|
+
# This example solves a 2D Navier-Stokes flow problem:
|
|
12
|
+
#
|
|
13
|
+
# Du/dt -nu D(u) + grad p = 0
|
|
14
|
+
# Div u = 0
|
|
15
|
+
#
|
|
16
|
+
# with (hard) velocity-Dirichlet boundary conditions
|
|
17
|
+
# and using semi-Lagrangian advection
|
|
18
|
+
###########################################################################
|
|
19
|
+
|
|
20
|
+
import argparse
|
|
21
|
+
|
|
22
|
+
import warp as wp
|
|
23
|
+
import warp.fem as fem
|
|
24
|
+
|
|
25
|
+
from warp.fem.utils import array_axpy
|
|
26
|
+
|
|
27
|
+
from warp.sparse import bsr_mm, bsr_mv, bsr_copy
|
|
28
|
+
|
|
29
|
+
try:
|
|
30
|
+
from .bsr_utils import bsr_solve_saddle, SaddleSystem
|
|
31
|
+
from .plot_utils import Plot
|
|
32
|
+
from .mesh_utils import gen_trimesh
|
|
33
|
+
except ImportError:
|
|
34
|
+
from bsr_utils import bsr_solve_saddle, SaddleSystem
|
|
35
|
+
from plot_utils import Plot
|
|
36
|
+
from mesh_utils import gen_trimesh
|
|
37
|
+
|
|
38
|
+
wp.init()
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@fem.integrand
|
|
42
|
+
def u_boundary_value(s: fem.Sample, domain: fem.Domain, v: fem.Field, top_vel: float):
|
|
43
|
+
# Horizontal velocity on top of domain, zero elsewhere
|
|
44
|
+
if domain(s)[1] == 1.0:
|
|
45
|
+
return wp.dot(wp.vec2f(top_vel, 0.0), v(s))
|
|
46
|
+
|
|
47
|
+
return wp.dot(wp.vec2f(0.0, 0.0), v(s))
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
@fem.integrand
|
|
51
|
+
def mass_form(
|
|
52
|
+
s: fem.Sample,
|
|
53
|
+
u: fem.Field,
|
|
54
|
+
v: fem.Field,
|
|
55
|
+
):
|
|
56
|
+
return wp.dot(u(s), v(s))
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
@fem.integrand
|
|
60
|
+
def inertia_form(s: fem.Sample, u: fem.Field, v: fem.Field, dt: float):
|
|
61
|
+
return mass_form(s, u, v) / dt
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
@fem.integrand
|
|
65
|
+
def viscosity_form(s: fem.Sample, u: fem.Field, v: fem.Field, nu: float):
|
|
66
|
+
return 2.0 * nu * wp.ddot(fem.D(u, s), fem.D(v, s))
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
@fem.integrand
|
|
70
|
+
def viscosity_and_inertia_form(s: fem.Sample, u: fem.Field, v: fem.Field, dt: float, nu: float):
|
|
71
|
+
return inertia_form(s, u, v, dt) + viscosity_form(s, u, v, nu)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
@fem.integrand
|
|
75
|
+
def transported_inertia_form(s: fem.Sample, domain: fem.Domain, u: fem.Field, v: fem.Field, dt: float):
|
|
76
|
+
pos = domain(s)
|
|
77
|
+
vel = u(s)
|
|
78
|
+
|
|
79
|
+
conv_pos = pos - 0.5 * vel * dt
|
|
80
|
+
conv_s = fem.lookup(domain, conv_pos, s)
|
|
81
|
+
conv_vel = u(conv_s)
|
|
82
|
+
|
|
83
|
+
conv_pos = conv_pos - 0.5 * conv_vel * dt
|
|
84
|
+
conv_vel = u(fem.lookup(domain, conv_pos, conv_s))
|
|
85
|
+
|
|
86
|
+
return wp.dot(conv_vel, v(s)) / dt
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
@fem.integrand
|
|
90
|
+
def div_form(
|
|
91
|
+
s: fem.Sample,
|
|
92
|
+
u: fem.Field,
|
|
93
|
+
q: fem.Field,
|
|
94
|
+
):
|
|
95
|
+
return -q(s) * fem.div(u, s)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
class Example:
|
|
99
|
+
parser = argparse.ArgumentParser()
|
|
100
|
+
parser.add_argument("--resolution", type=int, default=25)
|
|
101
|
+
parser.add_argument("--degree", type=int, default=2)
|
|
102
|
+
parser.add_argument("--num_frames", type=int, default=1000)
|
|
103
|
+
parser.add_argument("--top_velocity", type=float, default=1.0)
|
|
104
|
+
parser.add_argument("--Re", type=float, default=1000.0)
|
|
105
|
+
parser.add_argument("--tri_mesh", action="store_true", help="Use a triangular mesh")
|
|
106
|
+
|
|
107
|
+
def __init__(self, stage=None, quiet=False, args=None, **kwargs):
|
|
108
|
+
if args is None:
|
|
109
|
+
# Read args from kwargs, add default arg values from parser
|
|
110
|
+
args = argparse.Namespace(**kwargs)
|
|
111
|
+
args = Example.parser.parse_args(args=[], namespace=args)
|
|
112
|
+
self._args = args
|
|
113
|
+
self._quiet = quiet
|
|
114
|
+
|
|
115
|
+
res = args.resolution
|
|
116
|
+
self.sim_dt = 1.0 / args.resolution
|
|
117
|
+
self.current_frame = 0
|
|
118
|
+
|
|
119
|
+
viscosity = args.top_velocity / args.Re
|
|
120
|
+
|
|
121
|
+
if args.tri_mesh:
|
|
122
|
+
positions, tri_vidx = gen_trimesh(res=wp.vec2i(res))
|
|
123
|
+
geo = fem.Trimesh2D(tri_vertex_indices=tri_vidx, positions=positions)
|
|
124
|
+
else:
|
|
125
|
+
geo = fem.Grid2D(res=wp.vec2i(res))
|
|
126
|
+
|
|
127
|
+
domain = fem.Cells(geometry=geo)
|
|
128
|
+
boundary = fem.BoundarySides(geo)
|
|
129
|
+
|
|
130
|
+
# Functions spaces: Q(d)-Q(d-1)
|
|
131
|
+
u_degree = args.degree
|
|
132
|
+
u_space = fem.make_polynomial_space(geo, degree=u_degree, dtype=wp.vec2)
|
|
133
|
+
p_space = fem.make_polynomial_space(geo, degree=u_degree - 1)
|
|
134
|
+
|
|
135
|
+
# Viscosity and inertia
|
|
136
|
+
u_test = fem.make_test(space=u_space, domain=domain)
|
|
137
|
+
u_trial = fem.make_trial(space=u_space, domain=domain)
|
|
138
|
+
|
|
139
|
+
u_matrix = fem.integrate(
|
|
140
|
+
viscosity_and_inertia_form,
|
|
141
|
+
fields={"u": u_trial, "v": u_test},
|
|
142
|
+
values={"nu": viscosity, "dt": self.sim_dt},
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
# Pressure-velocity coupling
|
|
146
|
+
p_test = fem.make_test(space=p_space, domain=domain)
|
|
147
|
+
div_matrix = fem.integrate(div_form, fields={"u": u_trial, "q": p_test})
|
|
148
|
+
|
|
149
|
+
# Enforcing the Dirichlet boundary condition the hard way;
|
|
150
|
+
# build projector for velocity left- and right-hand-sides
|
|
151
|
+
u_bd_test = fem.make_test(space=u_space, domain=boundary)
|
|
152
|
+
u_bd_trial = fem.make_trial(space=u_space, domain=boundary)
|
|
153
|
+
u_bd_projector = fem.integrate(mass_form, fields={"u": u_bd_trial, "v": u_bd_test}, nodal=True)
|
|
154
|
+
u_bd_value = fem.integrate(
|
|
155
|
+
u_boundary_value,
|
|
156
|
+
fields={"v": u_bd_test},
|
|
157
|
+
values={"top_vel": args.top_velocity},
|
|
158
|
+
nodal=True,
|
|
159
|
+
output_dtype=wp.vec2d,
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
fem.normalize_dirichlet_projector(u_bd_projector, u_bd_value)
|
|
163
|
+
|
|
164
|
+
u_bd_rhs = wp.zeros_like(u_bd_value)
|
|
165
|
+
fem.project_linear_system(u_matrix, u_bd_rhs, u_bd_projector, u_bd_value, normalize_projector=False)
|
|
166
|
+
|
|
167
|
+
# div_bd_rhs = div_matrix * u_bd_rhs
|
|
168
|
+
div_bd_rhs = wp.zeros(shape=(div_matrix.nrow,), dtype=div_matrix.scalar_type)
|
|
169
|
+
bsr_mv(div_matrix, u_bd_value, y=div_bd_rhs, alpha=-1.0)
|
|
170
|
+
|
|
171
|
+
# div_matrix = div_matrix - div_matrix * bd_projector
|
|
172
|
+
bsr_mm(x=bsr_copy(div_matrix), y=u_bd_projector, z=div_matrix, alpha=-1.0, beta=1.0)
|
|
173
|
+
|
|
174
|
+
# Assemble saddle system
|
|
175
|
+
self._saddle_system = SaddleSystem(u_matrix, div_matrix)
|
|
176
|
+
|
|
177
|
+
# Save data for computing time steps rhs
|
|
178
|
+
self._u_bd_projector = u_bd_projector
|
|
179
|
+
self._u_bd_rhs = u_bd_rhs
|
|
180
|
+
self._u_test = u_test
|
|
181
|
+
self._div_bd_rhs = div_bd_rhs
|
|
182
|
+
|
|
183
|
+
# Velocitiy and pressure fields
|
|
184
|
+
self._u_field = u_space.make_field()
|
|
185
|
+
self._p_field = p_space.make_field()
|
|
186
|
+
|
|
187
|
+
self.renderer = Plot(stage)
|
|
188
|
+
self.renderer.add_surface_vector("velocity", self._u_field)
|
|
189
|
+
|
|
190
|
+
def step(self):
|
|
191
|
+
self.current_frame += 1
|
|
192
|
+
|
|
193
|
+
u_rhs = fem.integrate(
|
|
194
|
+
transported_inertia_form,
|
|
195
|
+
fields={"u": self._u_field, "v": self._u_test},
|
|
196
|
+
values={"dt": self.sim_dt},
|
|
197
|
+
output_dtype=wp.vec2d,
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
# Apply boundary conditions
|
|
201
|
+
# u_rhs = (I - P) * u_rhs + u_bd_rhs
|
|
202
|
+
bsr_mv(self._u_bd_projector, x=u_rhs, y=u_rhs, alpha=-1.0, beta=1.0)
|
|
203
|
+
array_axpy(x=self._u_bd_rhs, y=u_rhs, alpha=1.0, beta=1.0)
|
|
204
|
+
|
|
205
|
+
p_rhs = self._div_bd_rhs
|
|
206
|
+
|
|
207
|
+
x_u = wp.empty_like(u_rhs)
|
|
208
|
+
x_p = wp.empty_like(p_rhs)
|
|
209
|
+
wp.utils.array_cast(out_array=x_u, in_array=self._u_field.dof_values)
|
|
210
|
+
wp.utils.array_cast(out_array=x_p, in_array=self._p_field.dof_values)
|
|
211
|
+
|
|
212
|
+
bsr_solve_saddle(
|
|
213
|
+
saddle_system=self._saddle_system,
|
|
214
|
+
tol=1.0e-6,
|
|
215
|
+
x_u=x_u,
|
|
216
|
+
x_p=x_p,
|
|
217
|
+
b_u=u_rhs,
|
|
218
|
+
b_p=p_rhs,
|
|
219
|
+
quiet=self._quiet,
|
|
220
|
+
)
|
|
221
|
+
|
|
222
|
+
wp.utils.array_cast(in_array=x_u, out_array=self._u_field.dof_values)
|
|
223
|
+
wp.utils.array_cast(in_array=x_p, out_array=self._p_field.dof_values)
|
|
224
|
+
|
|
225
|
+
def render(self):
|
|
226
|
+
self.renderer.begin_frame(time=self.current_frame * self.sim_dt)
|
|
227
|
+
self.renderer.add_surface_vector("velocity", self._u_field)
|
|
228
|
+
self.renderer.end_frame()
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
if __name__ == "__main__":
|
|
232
|
+
wp.set_module_options({"enable_backward": False})
|
|
233
|
+
|
|
234
|
+
args = Example.parser.parse_args()
|
|
235
|
+
|
|
236
|
+
example = Example(args=args)
|
|
237
|
+
for k in range(args.num_frames):
|
|
238
|
+
print(f"Frame {k}:")
|
|
239
|
+
example.step()
|
|
240
|
+
example.render()
|
|
241
|
+
|
|
242
|
+
example.renderer.add_surface_vector("velocity_final", example._u_field)
|
|
243
|
+
example.renderer.plot(streamlines=set(["velocity_final"]))
|
|
@@ -0,0 +1,192 @@
|
|
|
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 Stokes
|
|
10
|
+
#
|
|
11
|
+
# This example solves a 2D Stokes flow problem
|
|
12
|
+
#
|
|
13
|
+
# -nu D(u) + grad p = 0
|
|
14
|
+
# Div u = 0
|
|
15
|
+
#
|
|
16
|
+
# with (soft) velocity-Dirichlet boundary conditions
|
|
17
|
+
###########################################################################
|
|
18
|
+
|
|
19
|
+
import argparse
|
|
20
|
+
|
|
21
|
+
import warp as wp
|
|
22
|
+
|
|
23
|
+
import warp.fem as fem
|
|
24
|
+
from warp.fem.utils import array_axpy
|
|
25
|
+
|
|
26
|
+
import warp.sparse as sparse
|
|
27
|
+
|
|
28
|
+
try:
|
|
29
|
+
from .plot_utils import Plot
|
|
30
|
+
from .bsr_utils import bsr_solve_saddle, SaddleSystem
|
|
31
|
+
from .mesh_utils import gen_trimesh, gen_quadmesh
|
|
32
|
+
except ImportError:
|
|
33
|
+
from plot_utils import Plot
|
|
34
|
+
from bsr_utils import bsr_solve_saddle, SaddleSystem
|
|
35
|
+
from mesh_utils import gen_trimesh, gen_quadmesh
|
|
36
|
+
|
|
37
|
+
wp.init()
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
@fem.integrand
|
|
41
|
+
def constant_form(val: wp.vec2):
|
|
42
|
+
return val
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@fem.integrand
|
|
46
|
+
def viscosity_form(s: fem.Sample, u: fem.Field, v: fem.Field, nu: float):
|
|
47
|
+
return nu * wp.ddot(fem.D(u, s), fem.D(v, s))
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
@fem.integrand
|
|
51
|
+
def top_mass_form(
|
|
52
|
+
s: fem.Sample,
|
|
53
|
+
domain: fem.Domain,
|
|
54
|
+
u: fem.Field,
|
|
55
|
+
v: fem.Field,
|
|
56
|
+
):
|
|
57
|
+
# non zero on top boundary of domain only
|
|
58
|
+
nor = fem.normal(domain, s)
|
|
59
|
+
return wp.dot(u(s), v(s)) * wp.max(0.0, nor[1])
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
@fem.integrand
|
|
63
|
+
def mass_form(
|
|
64
|
+
s: fem.Sample,
|
|
65
|
+
u: fem.Field,
|
|
66
|
+
v: fem.Field,
|
|
67
|
+
):
|
|
68
|
+
return wp.dot(u(s), v(s))
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
@fem.integrand
|
|
72
|
+
def div_form(
|
|
73
|
+
s: fem.Sample,
|
|
74
|
+
u: fem.Field,
|
|
75
|
+
q: fem.Field,
|
|
76
|
+
):
|
|
77
|
+
return q(s) * fem.div(u, s)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
class Example:
|
|
81
|
+
parser = argparse.ArgumentParser()
|
|
82
|
+
parser.add_argument("--resolution", type=int, default=50)
|
|
83
|
+
parser.add_argument("--degree", type=int, default=2)
|
|
84
|
+
parser.add_argument("--top_velocity", type=float, default=1.0)
|
|
85
|
+
parser.add_argument("--viscosity", type=float, default=1.0)
|
|
86
|
+
parser.add_argument("--boundary_strength", type=float, default=100.0)
|
|
87
|
+
parser.add_argument("--mesh", choices=("grid", "tri", "quad"), default="grid", help="Mesh type")
|
|
88
|
+
parser.add_argument(
|
|
89
|
+
"--nonconforming_pressures", action="store_true", help="For grid, use non-conforming pressure (Q_d/P_{d-1})"
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
def __init__(self, stage=None, quiet=False, args=None, **kwargs):
|
|
93
|
+
if args is None:
|
|
94
|
+
# Read args from kwargs, add default arg values from parser
|
|
95
|
+
args = argparse.Namespace(**kwargs)
|
|
96
|
+
args = Example.parser.parse_args(args=[], namespace=args)
|
|
97
|
+
self._args = args
|
|
98
|
+
self._quiet = quiet
|
|
99
|
+
|
|
100
|
+
# Grid or triangle mesh geometry
|
|
101
|
+
if args.mesh == "tri":
|
|
102
|
+
positions, tri_vidx = gen_trimesh(res=wp.vec2i(args.resolution))
|
|
103
|
+
geo = fem.Trimesh2D(tri_vertex_indices=tri_vidx, positions=positions)
|
|
104
|
+
elif args.mesh == "quad":
|
|
105
|
+
positions, quad_vidx = gen_quadmesh(res=wp.vec2i(args.resolution))
|
|
106
|
+
geo = fem.Quadmesh2D(quad_vertex_indices=quad_vidx, positions=positions)
|
|
107
|
+
else:
|
|
108
|
+
geo = fem.Grid2D(res=wp.vec2i(args.resolution))
|
|
109
|
+
|
|
110
|
+
# Function spaces -- Q_d for vel, P_{d-1} for pressure
|
|
111
|
+
u_space = fem.make_polynomial_space(geo, degree=args.degree, dtype=wp.vec2)
|
|
112
|
+
if args.mesh != "tri" and args.nonconforming_pressures:
|
|
113
|
+
p_space = fem.make_polynomial_space(
|
|
114
|
+
geo, degree=args.degree - 1, element_basis=fem.ElementBasis.NONCONFORMING_POLYNOMIAL
|
|
115
|
+
)
|
|
116
|
+
else:
|
|
117
|
+
p_space = fem.make_polynomial_space(geo, degree=args.degree - 1)
|
|
118
|
+
|
|
119
|
+
# Vector and scalar fields
|
|
120
|
+
self._u_field = u_space.make_field()
|
|
121
|
+
self._p_field = p_space.make_field()
|
|
122
|
+
|
|
123
|
+
# Interpolate initial condition on boundary (for example purposes)
|
|
124
|
+
self._bd_field = u_space.make_field()
|
|
125
|
+
f_boundary = fem.make_restriction(self._bd_field, domain=fem.BoundarySides(geo))
|
|
126
|
+
top_velocity = wp.vec2(args.top_velocity, 0.0)
|
|
127
|
+
fem.interpolate(constant_form, dest=f_boundary, values={"val": top_velocity})
|
|
128
|
+
|
|
129
|
+
self.renderer = Plot(stage)
|
|
130
|
+
|
|
131
|
+
def step(self):
|
|
132
|
+
args = self._args
|
|
133
|
+
u_space = self._u_field.space
|
|
134
|
+
p_space = self._p_field.space
|
|
135
|
+
geo = u_space.geometry
|
|
136
|
+
|
|
137
|
+
domain = fem.Cells(geometry=geo)
|
|
138
|
+
boundary = fem.BoundarySides(geo)
|
|
139
|
+
|
|
140
|
+
# Viscosity
|
|
141
|
+
u_test = fem.make_test(space=u_space, domain=domain)
|
|
142
|
+
u_trial = fem.make_trial(space=u_space, domain=domain)
|
|
143
|
+
|
|
144
|
+
u_visc_matrix = fem.integrate(
|
|
145
|
+
viscosity_form,
|
|
146
|
+
fields={"u": u_trial, "v": u_test},
|
|
147
|
+
values={"nu": args.viscosity},
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
# Weak velocity boundary conditions
|
|
151
|
+
u_bd_test = fem.make_test(space=u_space, domain=boundary)
|
|
152
|
+
u_bd_trial = fem.make_trial(space=u_space, domain=boundary)
|
|
153
|
+
u_rhs = fem.integrate(
|
|
154
|
+
top_mass_form, fields={"u": self._bd_field.trace(), "v": u_bd_test}, output_dtype=wp.vec2d
|
|
155
|
+
)
|
|
156
|
+
u_bd_matrix = fem.integrate(mass_form, fields={"u": u_bd_trial, "v": u_bd_test})
|
|
157
|
+
|
|
158
|
+
# Pressure-velocity coupling
|
|
159
|
+
p_test = fem.make_test(space=p_space, domain=domain)
|
|
160
|
+
div_matrix = fem.integrate(div_form, fields={"u": u_trial, "q": p_test})
|
|
161
|
+
|
|
162
|
+
# Define and solve the saddle-point system
|
|
163
|
+
u_matrix = u_visc_matrix
|
|
164
|
+
sparse.bsr_axpy(x=u_bd_matrix, y=u_matrix, alpha=args.boundary_strength, beta=1.0)
|
|
165
|
+
array_axpy(x=u_rhs, y=u_rhs, alpha=0.0, beta=args.boundary_strength)
|
|
166
|
+
|
|
167
|
+
p_rhs = wp.zeros(p_space.node_count(), dtype=wp.float64)
|
|
168
|
+
x_u = wp.zeros_like(u_rhs)
|
|
169
|
+
x_p = wp.zeros_like(p_rhs)
|
|
170
|
+
|
|
171
|
+
bsr_solve_saddle(
|
|
172
|
+
SaddleSystem(A=u_matrix, B=div_matrix), x_u=x_u, x_p=x_p, b_u=u_rhs, b_p=p_rhs, quiet=self._quiet
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
wp.utils.array_cast(in_array=x_u, out_array=self._u_field.dof_values)
|
|
176
|
+
wp.utils.array_cast(in_array=x_p, out_array=self._p_field.dof_values)
|
|
177
|
+
|
|
178
|
+
def render(self):
|
|
179
|
+
self.renderer.add_surface("pressure", self._p_field)
|
|
180
|
+
self.renderer.add_surface_vector("velocity", self._u_field)
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
if __name__ == "__main__":
|
|
184
|
+
wp.set_module_options({"enable_backward": False})
|
|
185
|
+
|
|
186
|
+
args = Example.parser.parse_args()
|
|
187
|
+
|
|
188
|
+
example = Example(args=args)
|
|
189
|
+
example.step()
|
|
190
|
+
example.render()
|
|
191
|
+
|
|
192
|
+
example.renderer.plot()
|