warp-lang 1.7.2rc1__py3-none-win_amd64.whl → 1.8.1__py3-none-win_amd64.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 +3 -1
- warp/__init__.pyi +3489 -1
- warp/autograd.py +45 -122
- warp/bin/warp-clang.dll +0 -0
- warp/bin/warp.dll +0 -0
- warp/build.py +241 -252
- warp/build_dll.py +130 -26
- warp/builtins.py +1907 -384
- warp/codegen.py +272 -104
- warp/config.py +12 -1
- warp/constants.py +1 -1
- warp/context.py +770 -238
- warp/dlpack.py +1 -1
- warp/examples/benchmarks/benchmark_cloth.py +2 -2
- warp/examples/benchmarks/benchmark_tile_sort.py +155 -0
- warp/examples/core/example_sample_mesh.py +1 -1
- warp/examples/core/example_spin_lock.py +93 -0
- warp/examples/core/example_work_queue.py +118 -0
- warp/examples/fem/example_adaptive_grid.py +5 -5
- warp/examples/fem/example_apic_fluid.py +1 -1
- warp/examples/fem/example_burgers.py +1 -1
- warp/examples/fem/example_convection_diffusion.py +9 -6
- warp/examples/fem/example_darcy_ls_optimization.py +489 -0
- warp/examples/fem/example_deformed_geometry.py +1 -1
- warp/examples/fem/example_diffusion.py +2 -2
- warp/examples/fem/example_diffusion_3d.py +1 -1
- warp/examples/fem/example_distortion_energy.py +1 -1
- warp/examples/fem/example_elastic_shape_optimization.py +387 -0
- warp/examples/fem/example_magnetostatics.py +5 -3
- warp/examples/fem/example_mixed_elasticity.py +5 -3
- warp/examples/fem/example_navier_stokes.py +11 -9
- warp/examples/fem/example_nonconforming_contact.py +5 -3
- warp/examples/fem/example_streamlines.py +8 -3
- warp/examples/fem/utils.py +9 -8
- warp/examples/interop/example_jax_callable.py +34 -4
- warp/examples/interop/example_jax_ffi_callback.py +2 -2
- warp/examples/interop/example_jax_kernel.py +27 -1
- warp/examples/optim/example_drone.py +1 -1
- warp/examples/sim/example_cloth.py +1 -1
- warp/examples/sim/example_cloth_self_contact.py +48 -54
- warp/examples/tile/example_tile_block_cholesky.py +502 -0
- warp/examples/tile/example_tile_cholesky.py +2 -1
- warp/examples/tile/example_tile_convolution.py +1 -1
- warp/examples/tile/example_tile_filtering.py +1 -1
- warp/examples/tile/example_tile_matmul.py +1 -1
- warp/examples/tile/example_tile_mlp.py +2 -0
- warp/fabric.py +7 -7
- warp/fem/__init__.py +5 -0
- warp/fem/adaptivity.py +1 -1
- warp/fem/cache.py +152 -63
- warp/fem/dirichlet.py +2 -2
- warp/fem/domain.py +136 -6
- warp/fem/field/field.py +141 -99
- warp/fem/field/nodal_field.py +85 -39
- warp/fem/field/virtual.py +99 -52
- warp/fem/geometry/adaptive_nanogrid.py +91 -86
- warp/fem/geometry/closest_point.py +13 -0
- warp/fem/geometry/deformed_geometry.py +102 -40
- warp/fem/geometry/element.py +56 -2
- warp/fem/geometry/geometry.py +323 -22
- warp/fem/geometry/grid_2d.py +157 -62
- warp/fem/geometry/grid_3d.py +116 -20
- warp/fem/geometry/hexmesh.py +86 -20
- warp/fem/geometry/nanogrid.py +166 -86
- warp/fem/geometry/partition.py +59 -25
- warp/fem/geometry/quadmesh.py +86 -135
- warp/fem/geometry/tetmesh.py +47 -119
- warp/fem/geometry/trimesh.py +77 -270
- warp/fem/integrate.py +181 -95
- warp/fem/linalg.py +25 -58
- warp/fem/operator.py +124 -27
- warp/fem/quadrature/pic_quadrature.py +36 -14
- warp/fem/quadrature/quadrature.py +40 -16
- warp/fem/space/__init__.py +1 -1
- warp/fem/space/basis_function_space.py +66 -46
- warp/fem/space/basis_space.py +17 -4
- warp/fem/space/dof_mapper.py +1 -1
- warp/fem/space/function_space.py +2 -2
- warp/fem/space/grid_2d_function_space.py +4 -1
- warp/fem/space/hexmesh_function_space.py +4 -2
- warp/fem/space/nanogrid_function_space.py +3 -1
- warp/fem/space/partition.py +11 -2
- warp/fem/space/quadmesh_function_space.py +4 -1
- warp/fem/space/restriction.py +5 -2
- warp/fem/space/shape/__init__.py +10 -8
- warp/fem/space/tetmesh_function_space.py +4 -1
- warp/fem/space/topology.py +52 -21
- warp/fem/space/trimesh_function_space.py +4 -1
- warp/fem/utils.py +53 -8
- warp/jax.py +1 -2
- warp/jax_experimental/ffi.py +210 -67
- warp/jax_experimental/xla_ffi.py +37 -24
- warp/math.py +171 -1
- warp/native/array.h +103 -4
- warp/native/builtin.h +182 -35
- warp/native/coloring.cpp +6 -2
- warp/native/cuda_util.cpp +1 -1
- warp/native/exports.h +118 -63
- warp/native/intersect.h +5 -5
- warp/native/mat.h +8 -13
- warp/native/mathdx.cpp +11 -5
- warp/native/matnn.h +1 -123
- warp/native/mesh.h +1 -1
- warp/native/quat.h +34 -6
- warp/native/rand.h +7 -7
- warp/native/sparse.cpp +121 -258
- warp/native/sparse.cu +181 -274
- warp/native/spatial.h +305 -17
- warp/native/svd.h +23 -8
- warp/native/tile.h +603 -73
- warp/native/tile_radix_sort.h +1112 -0
- warp/native/tile_reduce.h +239 -13
- warp/native/tile_scan.h +240 -0
- warp/native/tuple.h +189 -0
- warp/native/vec.h +10 -20
- warp/native/warp.cpp +36 -4
- warp/native/warp.cu +588 -52
- warp/native/warp.h +47 -74
- warp/optim/linear.py +5 -1
- warp/paddle.py +7 -8
- warp/py.typed +0 -0
- warp/render/render_opengl.py +110 -80
- warp/render/render_usd.py +124 -62
- warp/sim/__init__.py +9 -0
- warp/sim/collide.py +253 -80
- warp/sim/graph_coloring.py +8 -1
- warp/sim/import_mjcf.py +4 -3
- warp/sim/import_usd.py +11 -7
- warp/sim/integrator.py +5 -2
- warp/sim/integrator_euler.py +1 -1
- warp/sim/integrator_featherstone.py +1 -1
- warp/sim/integrator_vbd.py +761 -322
- warp/sim/integrator_xpbd.py +1 -1
- warp/sim/model.py +265 -260
- warp/sim/utils.py +10 -7
- warp/sparse.py +303 -166
- warp/tape.py +54 -51
- warp/tests/cuda/test_conditional_captures.py +1046 -0
- warp/tests/cuda/test_streams.py +1 -1
- warp/tests/geometry/test_volume.py +2 -2
- warp/tests/interop/test_dlpack.py +9 -9
- warp/tests/interop/test_jax.py +0 -1
- warp/tests/run_coverage_serial.py +1 -1
- warp/tests/sim/disabled_kinematics.py +2 -2
- warp/tests/sim/{test_vbd.py → test_cloth.py} +378 -112
- warp/tests/sim/test_collision.py +159 -51
- warp/tests/sim/test_coloring.py +91 -2
- warp/tests/test_array.py +254 -2
- warp/tests/test_array_reduce.py +2 -2
- warp/tests/test_assert.py +53 -0
- warp/tests/test_atomic_cas.py +312 -0
- warp/tests/test_codegen.py +142 -19
- warp/tests/test_conditional.py +47 -1
- warp/tests/test_ctypes.py +0 -20
- warp/tests/test_devices.py +8 -0
- warp/tests/test_fabricarray.py +4 -2
- warp/tests/test_fem.py +58 -25
- warp/tests/test_func.py +42 -1
- warp/tests/test_grad.py +1 -1
- warp/tests/test_lerp.py +1 -3
- warp/tests/test_map.py +481 -0
- warp/tests/test_mat.py +23 -24
- warp/tests/test_quat.py +28 -15
- warp/tests/test_rounding.py +10 -38
- warp/tests/test_runlength_encode.py +7 -7
- warp/tests/test_smoothstep.py +1 -1
- warp/tests/test_sparse.py +83 -2
- warp/tests/test_spatial.py +507 -1
- warp/tests/test_static.py +48 -0
- warp/tests/test_struct.py +2 -2
- warp/tests/test_tape.py +38 -0
- warp/tests/test_tuple.py +265 -0
- warp/tests/test_types.py +2 -2
- warp/tests/test_utils.py +24 -18
- warp/tests/test_vec.py +38 -408
- warp/tests/test_vec_constructors.py +325 -0
- warp/tests/tile/test_tile.py +438 -131
- warp/tests/tile/test_tile_mathdx.py +518 -14
- warp/tests/tile/test_tile_matmul.py +179 -0
- warp/tests/tile/test_tile_reduce.py +307 -5
- warp/tests/tile/test_tile_shared_memory.py +136 -7
- warp/tests/tile/test_tile_sort.py +121 -0
- warp/tests/unittest_suites.py +14 -6
- warp/types.py +462 -308
- warp/utils.py +647 -86
- {warp_lang-1.7.2rc1.dist-info → warp_lang-1.8.1.dist-info}/METADATA +20 -6
- {warp_lang-1.7.2rc1.dist-info → warp_lang-1.8.1.dist-info}/RECORD +190 -176
- warp/stubs.py +0 -3381
- warp/tests/sim/test_xpbd.py +0 -399
- warp/tests/test_mlp.py +0 -282
- {warp_lang-1.7.2rc1.dist-info → warp_lang-1.8.1.dist-info}/WHEEL +0 -0
- {warp_lang-1.7.2rc1.dist-info → warp_lang-1.8.1.dist-info}/licenses/LICENSE.md +0 -0
- {warp_lang-1.7.2rc1.dist-info → warp_lang-1.8.1.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,489 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
|
2
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
#
|
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
# you may not use this file except in compliance with the License.
|
|
6
|
+
# You may obtain a copy of the License at
|
|
7
|
+
#
|
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
#
|
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
# See the License for the specific language governing permissions and
|
|
14
|
+
# limitations under the License.
|
|
15
|
+
|
|
16
|
+
###########################################################################
|
|
17
|
+
# Example: Shape Optimization of 2D Darcy Flow using Level Set Method
|
|
18
|
+
#
|
|
19
|
+
# This example demonstrates the use of a level set-based shape optimization technique
|
|
20
|
+
# to maximize the total Darcy flow (inflow) through a 2D square domain. The domain
|
|
21
|
+
# contains a material region whose shape is implicitly represented by a level set function.
|
|
22
|
+
#
|
|
23
|
+
# Physical Setup:
|
|
24
|
+
# - The computational domain is a unit square, discretized using either a structured grid
|
|
25
|
+
# or a triangular mesh.
|
|
26
|
+
# - The material region within the domain is defined by the zero level set of a scalar field.
|
|
27
|
+
# - The permeability of the domain is a smooth function of the level set, with high permeability
|
|
28
|
+
# inside the material region and low permeability outside.
|
|
29
|
+
# - Boundary conditions are set such that one side of the domain has a prescribed inflow pressure
|
|
30
|
+
# and the opposite side has a prescribed outflow (Dirichlet) pressure, driving Darcy flow
|
|
31
|
+
# through the material region.
|
|
32
|
+
#
|
|
33
|
+
# Optimization Goal:
|
|
34
|
+
# - The objective is to optimize the shape of the material region (by evolving the level set)
|
|
35
|
+
# to maximize the total inflow (Darcy flux) across the inflow boundary, subject to a constraint
|
|
36
|
+
# on the total volume of the material region.
|
|
37
|
+
#
|
|
38
|
+
# Numerical Approach:
|
|
39
|
+
# - The pressure field is solved using the finite element method (FEM) for the current material
|
|
40
|
+
# configuration.
|
|
41
|
+
# - The level set function is updated using the adjoint method: the gradient of the objective
|
|
42
|
+
# with respect to the level set is computed via automatic differentiation, and the level set
|
|
43
|
+
# is advected in the direction of increasing inflow.
|
|
44
|
+
# - The optimization is performed iteratively, with each iteration consisting of a forward
|
|
45
|
+
# pressure solve, loss evaluation, backward (adjoint) computation, and level set update.
|
|
46
|
+
# - The code supports both continuous and discontinuous Galerkin formulations for the level set
|
|
47
|
+
# advection step.
|
|
48
|
+
#
|
|
49
|
+
# Visualization:
|
|
50
|
+
# - The script provides visualization of the velocity field and the evolving material region.
|
|
51
|
+
#
|
|
52
|
+
###########################################################################
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
import warp as wp
|
|
56
|
+
import warp.examples.fem.utils as fem_example_utils
|
|
57
|
+
import warp.fem as fem
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
@fem.integrand
|
|
61
|
+
def classify_boundary_sides(
|
|
62
|
+
s: fem.Sample,
|
|
63
|
+
domain: fem.Domain,
|
|
64
|
+
dirichlet: wp.array(dtype=int),
|
|
65
|
+
inflow: wp.array(dtype=int),
|
|
66
|
+
):
|
|
67
|
+
"""Assign boundary sides to inflow or Dirichlet subdomains based on normal direction"""
|
|
68
|
+
nor = fem.normal(domain, s)
|
|
69
|
+
|
|
70
|
+
if nor[0] < -0.5:
|
|
71
|
+
inflow[s.qp_index] = 1
|
|
72
|
+
dirichlet[s.qp_index] = 1
|
|
73
|
+
elif nor[0] > 0.5:
|
|
74
|
+
dirichlet[s.qp_index] = 1
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
@wp.func
|
|
78
|
+
def initial_level_set(x: wp.vec2, radius: float):
|
|
79
|
+
"""Initial level set function for the material region -- three circles"""
|
|
80
|
+
|
|
81
|
+
return (
|
|
82
|
+
wp.min(
|
|
83
|
+
wp.vec3(
|
|
84
|
+
wp.length(x - wp.vec2(0.667, 0.5)),
|
|
85
|
+
wp.length(x - wp.vec2(0.333, 0.333)),
|
|
86
|
+
wp.length(x - wp.vec2(0.333, 0.667)),
|
|
87
|
+
)
|
|
88
|
+
)
|
|
89
|
+
- radius
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
@fem.integrand
|
|
94
|
+
def identity_form(
|
|
95
|
+
s: fem.Sample,
|
|
96
|
+
p: fem.Field,
|
|
97
|
+
q: fem.Field,
|
|
98
|
+
):
|
|
99
|
+
return p(s) * q(s)
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
@fem.integrand
|
|
103
|
+
def material_fraction(s: fem.Sample, level_set: fem.Field, smoothing: float):
|
|
104
|
+
"""Sigmoid approximation of the level set interior"""
|
|
105
|
+
return 1.0 / (1.0 + wp.exp(-level_set(s) / smoothing))
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
@fem.integrand
|
|
109
|
+
def permeability(s: fem.Sample, level_set: fem.Field, smoothing: float):
|
|
110
|
+
"""Define permeability as strictly proportional to material fraction (arbitrary choice)"""
|
|
111
|
+
return material_fraction(s, level_set, smoothing)
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
@fem.integrand
|
|
115
|
+
def velocity_field(s: fem.Sample, level_set: fem.Field, p: fem.Field, smoothing: float):
|
|
116
|
+
"""Velocity field based on permeability and pressure gradient according to Darcy's law"""
|
|
117
|
+
return -permeability(s, level_set, smoothing) * fem.grad(p, s)
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
@fem.integrand
|
|
121
|
+
def diffusion_form(s: fem.Sample, level_set: fem.Field, p: fem.Field, q: fem.Field, smoothing: float, scale: float):
|
|
122
|
+
"""Inhomogeneous diffusion form"""
|
|
123
|
+
return scale * wp.dot(velocity_field(s, level_set, p, smoothing), fem.grad(q, s))
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
@fem.integrand
|
|
127
|
+
def inflow_velocity(s: fem.Sample, domain: fem.Domain, level_set: fem.Field, p: fem.Field, smoothing: float):
|
|
128
|
+
return wp.dot(velocity_field(s, level_set, p, smoothing), fem.normal(domain, s))
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
@fem.integrand
|
|
132
|
+
def volume_form(s: fem.Sample, level_set: fem.Field, smoothing: float):
|
|
133
|
+
return material_fraction(s, level_set, smoothing)
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
@wp.kernel
|
|
137
|
+
def combine_losses(
|
|
138
|
+
loss: wp.array(dtype=wp.float32),
|
|
139
|
+
vol: wp.array(dtype=wp.float32),
|
|
140
|
+
target_vol: wp.float32,
|
|
141
|
+
vol_weight: wp.float32,
|
|
142
|
+
):
|
|
143
|
+
loss[0] += vol_weight * (vol[0] - target_vol) * (vol[0] - target_vol)
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
@fem.integrand
|
|
147
|
+
def advected_level_set_semi_lagrangian(
|
|
148
|
+
s: fem.Sample, domain: fem.Domain, level_set: fem.Field, velocity: fem.Field, dt: float
|
|
149
|
+
):
|
|
150
|
+
x_prev = domain(s) - velocity(s) * dt
|
|
151
|
+
s_prev = fem.lookup(domain, x_prev, guess=s)
|
|
152
|
+
return level_set(s_prev)
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
# Discontinuous Galerkin variant of level set advection
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
@fem.integrand
|
|
159
|
+
def level_set_transport_form(s: fem.Sample, level_set: fem.Field, psi: fem.Field, velocity: fem.Field, dt: float):
|
|
160
|
+
return dt * wp.dot(fem.grad(level_set, s), velocity(s)) * psi(s)
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
@fem.integrand
|
|
164
|
+
def level_set_transport_form_upwind(
|
|
165
|
+
s: fem.Sample, domain: fem.Domain, level_set: fem.Field, psi: fem.Field, velocity: fem.Field, dt: float
|
|
166
|
+
):
|
|
167
|
+
vel = dt * velocity(s)
|
|
168
|
+
vel_n = wp.dot(vel, fem.normal(domain, s))
|
|
169
|
+
return fem.jump(level_set, s) * (-fem.average(psi, s) * vel_n + 0.5 * fem.jump(psi, s) * wp.abs(vel_n))
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
@fem.integrand
|
|
173
|
+
def advected_level_set_upwind(
|
|
174
|
+
s: fem.Sample, domain: fem.Domain, level_set: fem.Field, transport_integrals: wp.array(dtype=float)
|
|
175
|
+
):
|
|
176
|
+
return level_set(s) - transport_integrals[s.qp_index] / (fem.measure(domain, s) * s.qp_weight)
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
class Example:
|
|
180
|
+
def __init__(
|
|
181
|
+
self, quiet=False, degree=2, resolution=25, mesh: str = "grid", dt: float = 1.0, discontinuous: bool = False
|
|
182
|
+
):
|
|
183
|
+
self._quiet = quiet
|
|
184
|
+
|
|
185
|
+
self._smoothing = 0.5 / resolution # smoothing for level set interface approximation as sigmoid
|
|
186
|
+
self._dt = dt # level set advection time step (~gradient step size)
|
|
187
|
+
self._discontinuous = discontinuous
|
|
188
|
+
|
|
189
|
+
if mesh == "tri":
|
|
190
|
+
positions, tri_vidx = fem_example_utils.gen_trimesh(res=wp.vec2i(resolution, resolution))
|
|
191
|
+
self._geo = fem.Trimesh2D(tri_vertex_indices=tri_vidx, positions=positions, build_bvh=True)
|
|
192
|
+
else:
|
|
193
|
+
self._geo = fem.Grid2D(res=wp.vec2i(resolution, resolution))
|
|
194
|
+
|
|
195
|
+
# Pressure, level set, and level set velocity spaces
|
|
196
|
+
self._p_space = fem.make_polynomial_space(self._geo, degree=degree, dtype=float)
|
|
197
|
+
self._ls_space = fem.make_polynomial_space(self._geo, degree=degree, dtype=float, discontinuous=discontinuous)
|
|
198
|
+
self._v_space = fem.make_polynomial_space(self._geo, degree=degree, dtype=wp.vec2)
|
|
199
|
+
|
|
200
|
+
# pressure field
|
|
201
|
+
self._p_field = fem.make_discrete_field(space=self._p_space)
|
|
202
|
+
self._p_field.dof_values.requires_grad = True
|
|
203
|
+
|
|
204
|
+
# level set field
|
|
205
|
+
self._level_set_field = fem.make_discrete_field(space=self._ls_space)
|
|
206
|
+
self._level_set_field.dof_values.requires_grad = True
|
|
207
|
+
|
|
208
|
+
# level set advection velocity field
|
|
209
|
+
self._level_set_velocity_field = fem.make_discrete_field(space=self._v_space)
|
|
210
|
+
self._level_set_velocity_field.dof_values.requires_grad = True
|
|
211
|
+
|
|
212
|
+
fem.interpolate(
|
|
213
|
+
fem.ImplicitField(fem.Cells(self._geo), initial_level_set, values={"radius": 0.125}),
|
|
214
|
+
dest=self._level_set_field,
|
|
215
|
+
)
|
|
216
|
+
|
|
217
|
+
# recording initial volume, we want to preserve that when doing optimization
|
|
218
|
+
self._target_vol = fem.integrate(
|
|
219
|
+
volume_form,
|
|
220
|
+
domain=fem.Cells(self._geo),
|
|
221
|
+
fields={"level_set": self._level_set_field},
|
|
222
|
+
values={"smoothing": self._smoothing},
|
|
223
|
+
)
|
|
224
|
+
|
|
225
|
+
# Trial and test functions for pressure solve
|
|
226
|
+
self._p_test = fem.make_test(space=self._p_space)
|
|
227
|
+
self._p_trial = fem.make_trial(space=self._p_space)
|
|
228
|
+
|
|
229
|
+
# For discontinuous level set advection
|
|
230
|
+
if self._discontinuous:
|
|
231
|
+
self._ls_test = fem.make_test(space=self._ls_space)
|
|
232
|
+
self._ls_sides_test = fem.make_test(space=self._ls_space, domain=fem.Sides(self._geo))
|
|
233
|
+
|
|
234
|
+
# Identify inflow and outflow sides
|
|
235
|
+
boundary = fem.BoundarySides(self._geo)
|
|
236
|
+
|
|
237
|
+
inflow_mask = wp.zeros(shape=boundary.element_count(), dtype=int)
|
|
238
|
+
dirichlet_mask = wp.zeros(shape=boundary.element_count(), dtype=int)
|
|
239
|
+
|
|
240
|
+
fem.interpolate(
|
|
241
|
+
classify_boundary_sides,
|
|
242
|
+
quadrature=fem.RegularQuadrature(boundary, order=0),
|
|
243
|
+
values={"inflow": inflow_mask, "dirichlet": dirichlet_mask},
|
|
244
|
+
)
|
|
245
|
+
|
|
246
|
+
self._inflow = fem.Subdomain(boundary, element_mask=inflow_mask)
|
|
247
|
+
self._dirichlet = fem.Subdomain(boundary, element_mask=dirichlet_mask)
|
|
248
|
+
|
|
249
|
+
# Build projector for the inflow and outflow homogeneous Dirichlet condition
|
|
250
|
+
p_dirichlet_bd_test = fem.make_test(space=self._p_space, domain=self._dirichlet)
|
|
251
|
+
p_dirichlet_bd_trial = fem.make_trial(space=self._p_space, domain=self._dirichlet)
|
|
252
|
+
p_dirichlet_bd_matrix = fem.integrate(
|
|
253
|
+
identity_form,
|
|
254
|
+
fields={"p": p_dirichlet_bd_trial, "q": p_dirichlet_bd_test},
|
|
255
|
+
assembly="nodal",
|
|
256
|
+
output_dtype=float,
|
|
257
|
+
)
|
|
258
|
+
|
|
259
|
+
# Inflow prescribed pressure
|
|
260
|
+
p_inflow_bd_test = fem.make_test(space=self._p_space, domain=self._inflow)
|
|
261
|
+
p_inflow_bd_value = fem.integrate(
|
|
262
|
+
identity_form,
|
|
263
|
+
fields={"p": fem.UniformField(self._inflow, 1.0), "q": p_inflow_bd_test},
|
|
264
|
+
assembly="nodal",
|
|
265
|
+
output_dtype=float,
|
|
266
|
+
)
|
|
267
|
+
fem.normalize_dirichlet_projector(p_dirichlet_bd_matrix, p_inflow_bd_value)
|
|
268
|
+
|
|
269
|
+
self._bd_projector = p_dirichlet_bd_matrix
|
|
270
|
+
self._bd_prescribed_value = p_inflow_bd_value
|
|
271
|
+
|
|
272
|
+
self.renderer = fem_example_utils.Plot()
|
|
273
|
+
|
|
274
|
+
def step(self):
|
|
275
|
+
p = self._p_field.dof_values
|
|
276
|
+
p.zero_()
|
|
277
|
+
v = self._level_set_velocity_field.dof_values
|
|
278
|
+
v.zero_()
|
|
279
|
+
|
|
280
|
+
# Advected level set field, used in adjoint computations
|
|
281
|
+
advected_level_set = fem.make_discrete_field(space=self._ls_space)
|
|
282
|
+
advected_level_set.dof_values.assign(self._level_set_field.dof_values)
|
|
283
|
+
advected_level_set.dof_values.requires_grad = True
|
|
284
|
+
advected_level_set_restriction = fem.make_restriction(advected_level_set, domain=self._p_test.domain)
|
|
285
|
+
|
|
286
|
+
# Forward step, record adjoint tape for forces
|
|
287
|
+
p_rhs = wp.empty(self._p_space.node_count(), dtype=wp.float32, requires_grad=True)
|
|
288
|
+
|
|
289
|
+
tape = wp.Tape()
|
|
290
|
+
with tape:
|
|
291
|
+
# Dummy advection step, so backward pass can compute adjoint w.r.t advection velocity
|
|
292
|
+
self.advect_level_set(
|
|
293
|
+
level_set_in=self._level_set_field,
|
|
294
|
+
level_set_out=advected_level_set_restriction,
|
|
295
|
+
velocity=self._level_set_velocity_field,
|
|
296
|
+
dt=1.0,
|
|
297
|
+
)
|
|
298
|
+
|
|
299
|
+
# Left-hand-side of implicit solve (zero if p=0, but required for adjoint computation through implicit function theorem)
|
|
300
|
+
fem.integrate(
|
|
301
|
+
diffusion_form,
|
|
302
|
+
fields={
|
|
303
|
+
"level_set": advected_level_set,
|
|
304
|
+
"p": self._p_field,
|
|
305
|
+
"q": self._p_test,
|
|
306
|
+
},
|
|
307
|
+
values={"smoothing": self._smoothing, "scale": -1.0},
|
|
308
|
+
output=p_rhs,
|
|
309
|
+
)
|
|
310
|
+
|
|
311
|
+
# Diffusion matrix (inhomogeneous Poisson)
|
|
312
|
+
p_matrix = fem.integrate(
|
|
313
|
+
diffusion_form,
|
|
314
|
+
fields={
|
|
315
|
+
"level_set": advected_level_set,
|
|
316
|
+
"p": self._p_trial,
|
|
317
|
+
"q": self._p_test,
|
|
318
|
+
},
|
|
319
|
+
values={"smoothing": self._smoothing, "scale": 1.0},
|
|
320
|
+
output_dtype=float,
|
|
321
|
+
)
|
|
322
|
+
|
|
323
|
+
# Project to enforce Dirichlet boundary conditions then solve linear system
|
|
324
|
+
fem.project_linear_system(
|
|
325
|
+
p_matrix, p_rhs, self._bd_projector, self._bd_prescribed_value, normalize_projector=False
|
|
326
|
+
)
|
|
327
|
+
|
|
328
|
+
fem_example_utils.bsr_cg(p_matrix, b=p_rhs, x=p, quiet=self._quiet, tol=1e-6, max_iters=1000)
|
|
329
|
+
|
|
330
|
+
# Record adjoint of linear solve
|
|
331
|
+
def solve_linear_system():
|
|
332
|
+
fem_example_utils.bsr_cg(p_matrix, b=p.grad, x=p_rhs.grad, quiet=self._quiet, tol=1e-6, max_iters=1000)
|
|
333
|
+
p_rhs.grad -= self._bd_projector @ p_rhs.grad
|
|
334
|
+
|
|
335
|
+
tape.record_func(solve_linear_system, arrays=(p_rhs, p))
|
|
336
|
+
|
|
337
|
+
# Evaluate losses
|
|
338
|
+
loss = wp.empty(shape=1, dtype=wp.float32, requires_grad=True)
|
|
339
|
+
vol = wp.empty(shape=1, dtype=wp.float32, requires_grad=True)
|
|
340
|
+
|
|
341
|
+
with tape:
|
|
342
|
+
# Main objective: inflow flux
|
|
343
|
+
fem.integrate(
|
|
344
|
+
inflow_velocity,
|
|
345
|
+
fields={"level_set": advected_level_set.trace(), "p": self._p_field.trace()},
|
|
346
|
+
values={"smoothing": self._smoothing},
|
|
347
|
+
domain=self._inflow,
|
|
348
|
+
output=loss,
|
|
349
|
+
)
|
|
350
|
+
|
|
351
|
+
# Add penalization term enforcing constant volume
|
|
352
|
+
fem.integrate(
|
|
353
|
+
volume_form,
|
|
354
|
+
fields={"level_set": advected_level_set},
|
|
355
|
+
values={"smoothing": self._smoothing},
|
|
356
|
+
domain=self._p_test.domain,
|
|
357
|
+
output=vol,
|
|
358
|
+
)
|
|
359
|
+
|
|
360
|
+
print("Total inflow", loss, "Volume", vol)
|
|
361
|
+
|
|
362
|
+
vol_loss_weight = 1000.0
|
|
363
|
+
wp.launch(
|
|
364
|
+
combine_losses,
|
|
365
|
+
dim=1,
|
|
366
|
+
inputs=(loss, vol, self._target_vol, vol_loss_weight),
|
|
367
|
+
)
|
|
368
|
+
|
|
369
|
+
# perform backward step
|
|
370
|
+
tape.backward(loss=loss)
|
|
371
|
+
|
|
372
|
+
# Advect level set with velocity field adjoint
|
|
373
|
+
v.assign(v.grad)
|
|
374
|
+
self.advect_level_set(
|
|
375
|
+
level_set_in=advected_level_set,
|
|
376
|
+
level_set_out=self._level_set_field,
|
|
377
|
+
velocity=self._level_set_velocity_field,
|
|
378
|
+
dt=-self._dt,
|
|
379
|
+
)
|
|
380
|
+
|
|
381
|
+
# Zero-out gradients used in tape
|
|
382
|
+
tape.zero()
|
|
383
|
+
|
|
384
|
+
def advect_level_set(self, level_set_in: fem.Field, level_set_out: fem.Field, velocity: fem.Field, dt: float):
|
|
385
|
+
if self._discontinuous:
|
|
386
|
+
# Discontinuous Galerkin version with (explicit) upwind transport:
|
|
387
|
+
# Integrate in-cell transport + side flux
|
|
388
|
+
transport_integrals = wp.empty(
|
|
389
|
+
shape=self._ls_space.node_count(),
|
|
390
|
+
dtype=float,
|
|
391
|
+
requires_grad=True,
|
|
392
|
+
)
|
|
393
|
+
fem.integrate(
|
|
394
|
+
level_set_transport_form,
|
|
395
|
+
fields={"psi": self._ls_test, "level_set": level_set_in, "velocity": velocity},
|
|
396
|
+
values={"dt": dt},
|
|
397
|
+
output=transport_integrals,
|
|
398
|
+
)
|
|
399
|
+
|
|
400
|
+
fem.integrate(
|
|
401
|
+
level_set_transport_form_upwind,
|
|
402
|
+
fields={"level_set": level_set_in.trace(), "psi": self._ls_sides_test, "velocity": velocity.trace()},
|
|
403
|
+
values={"dt": dt},
|
|
404
|
+
output=transport_integrals,
|
|
405
|
+
add=True,
|
|
406
|
+
)
|
|
407
|
+
|
|
408
|
+
# Divide by mass matrix and write back to advected field
|
|
409
|
+
out_field = level_set_out if isinstance(level_set_out, fem.DiscreteField) else level_set_out.field
|
|
410
|
+
fem.interpolate(
|
|
411
|
+
advected_level_set_upwind,
|
|
412
|
+
fields={"level_set": level_set_in, "velocity": velocity},
|
|
413
|
+
values={"transport_integrals": transport_integrals},
|
|
414
|
+
quadrature=fem.NodalQuadrature(self._p_test.domain, self._ls_space),
|
|
415
|
+
dest=out_field.dof_values,
|
|
416
|
+
)
|
|
417
|
+
else:
|
|
418
|
+
# Continuous Galerkin version with semi-Lagrangian transport:
|
|
419
|
+
fem.interpolate(
|
|
420
|
+
advected_level_set_semi_lagrangian,
|
|
421
|
+
fields={"level_set": level_set_in, "velocity": velocity},
|
|
422
|
+
values={"dt": dt},
|
|
423
|
+
dest=level_set_out,
|
|
424
|
+
)
|
|
425
|
+
|
|
426
|
+
def render(self):
|
|
427
|
+
# velocity field
|
|
428
|
+
u_space = fem.make_polynomial_space(self._geo, degree=self._p_space.degree, dtype=wp.vec2)
|
|
429
|
+
u_field = fem.make_discrete_field(space=u_space)
|
|
430
|
+
fem.interpolate(
|
|
431
|
+
velocity_field,
|
|
432
|
+
fields={"level_set": self._level_set_field, "p": self._p_field},
|
|
433
|
+
values={"smoothing": self._smoothing},
|
|
434
|
+
dest=u_field,
|
|
435
|
+
)
|
|
436
|
+
|
|
437
|
+
# material fraction field
|
|
438
|
+
mat_field = fem.make_discrete_field(space=self._ls_space)
|
|
439
|
+
fem.interpolate(
|
|
440
|
+
material_fraction,
|
|
441
|
+
fields={"level_set": self._level_set_field},
|
|
442
|
+
values={"smoothing": self._smoothing},
|
|
443
|
+
dest=mat_field,
|
|
444
|
+
)
|
|
445
|
+
|
|
446
|
+
self.renderer.add_field("velocity", u_field)
|
|
447
|
+
self.renderer.add_field("material", mat_field)
|
|
448
|
+
|
|
449
|
+
|
|
450
|
+
if __name__ == "__main__":
|
|
451
|
+
import argparse
|
|
452
|
+
|
|
453
|
+
parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter)
|
|
454
|
+
parser.add_argument("--device", type=str, default=None, help="Override the default Warp device.")
|
|
455
|
+
parser.add_argument("--resolution", type=int, default=100, help="Grid resolution.")
|
|
456
|
+
parser.add_argument("--degree", type=int, default=1, help="Polynomial degree of shape functions.")
|
|
457
|
+
parser.add_argument("--discontinuous", action="store_true", help="Use discontinuous level set advection.")
|
|
458
|
+
parser.add_argument("--mesh", type=str, default="grid", help="Mesh type.")
|
|
459
|
+
parser.add_argument("--num_iters", type=int, default=100, help="Number of iterations.")
|
|
460
|
+
parser.add_argument("--dt", type=float, default=0.05, help="Level set update time step.")
|
|
461
|
+
parser.add_argument(
|
|
462
|
+
"--headless",
|
|
463
|
+
action="store_true",
|
|
464
|
+
help="Run in headless mode, suppressing the opening of any graphical windows.",
|
|
465
|
+
)
|
|
466
|
+
|
|
467
|
+
args = parser.parse_known_args()[0]
|
|
468
|
+
|
|
469
|
+
with wp.ScopedDevice(args.device):
|
|
470
|
+
example = Example(
|
|
471
|
+
quiet=True,
|
|
472
|
+
degree=args.degree,
|
|
473
|
+
resolution=args.resolution,
|
|
474
|
+
discontinuous=args.discontinuous,
|
|
475
|
+
mesh=args.mesh,
|
|
476
|
+
dt=args.dt,
|
|
477
|
+
)
|
|
478
|
+
|
|
479
|
+
for _iter in range(args.num_iters):
|
|
480
|
+
example.step()
|
|
481
|
+
example.render()
|
|
482
|
+
|
|
483
|
+
if not args.headless:
|
|
484
|
+
example.renderer.plot(
|
|
485
|
+
options={
|
|
486
|
+
"velocity": {"arrows": {"glyph_scale": 0.1}},
|
|
487
|
+
"material": {"contours": {"levels": [0.0, 0.5, 1.0001]}},
|
|
488
|
+
},
|
|
489
|
+
)
|
|
@@ -119,7 +119,7 @@ class Example:
|
|
|
119
119
|
bd_test = fem.make_test(space=self._scalar_space, domain=boundary)
|
|
120
120
|
bd_trial = fem.make_trial(space=self._scalar_space, domain=boundary)
|
|
121
121
|
|
|
122
|
-
bd_matrix = fem.integrate(boundary_projector_form, fields={"u": bd_trial, "v": bd_test}, nodal
|
|
122
|
+
bd_matrix = fem.integrate(boundary_projector_form, fields={"u": bd_trial, "v": bd_test}, assembly="nodal")
|
|
123
123
|
|
|
124
124
|
fem.project_linear_system(matrix, rhs, bd_matrix)
|
|
125
125
|
|
|
@@ -125,9 +125,9 @@ class Example:
|
|
|
125
125
|
bd_test = fem.make_test(space=self._scalar_space, domain=boundary)
|
|
126
126
|
bd_trial = fem.make_trial(space=self._scalar_space, domain=boundary)
|
|
127
127
|
|
|
128
|
-
bd_matrix = fem.integrate(y_boundary_projector_form, fields={"u": bd_trial, "v": bd_test}, nodal
|
|
128
|
+
bd_matrix = fem.integrate(y_boundary_projector_form, fields={"u": bd_trial, "v": bd_test}, assembly="nodal")
|
|
129
129
|
bd_rhs = fem.integrate(
|
|
130
|
-
y_boundary_value_form, fields={"v": bd_test}, values={"val": self._boundary_value}, nodal
|
|
130
|
+
y_boundary_value_form, fields={"v": bd_test}, values={"val": self._boundary_value}, assembly="nodal"
|
|
131
131
|
)
|
|
132
132
|
|
|
133
133
|
# Assemble linear system
|
|
@@ -157,7 +157,7 @@ class Example:
|
|
|
157
157
|
boundary_projector_form = (
|
|
158
158
|
vertical_boundary_projector_form if self._geo.cell_dimension == 3 else y_boundary_projector_form
|
|
159
159
|
)
|
|
160
|
-
bd_matrix = fem.integrate(boundary_projector_form, fields={"u": bd_trial, "v": bd_test}, nodal
|
|
160
|
+
bd_matrix = fem.integrate(boundary_projector_form, fields={"u": bd_trial, "v": bd_test}, assembly="nodal")
|
|
161
161
|
|
|
162
162
|
# Diffusion form
|
|
163
163
|
trial = fem.make_trial(space=self._scalar_space, domain=domain)
|
|
@@ -158,7 +158,7 @@ class Example:
|
|
|
158
158
|
u_bd_test = fem.make_test(space=self._u_space, domain=boundary)
|
|
159
159
|
u_bd_trial = fem.make_trial(space=self._u_space, domain=boundary)
|
|
160
160
|
u_bd_matrix = fem.integrate(
|
|
161
|
-
boundary_projector_form, fields={"u": u_bd_trial, "v": u_bd_test}, nodal
|
|
161
|
+
boundary_projector_form, fields={"u": u_bd_trial, "v": u_bd_test}, assembly="nodal", output_dtype=float
|
|
162
162
|
)
|
|
163
163
|
fem.normalize_dirichlet_projector(u_bd_matrix)
|
|
164
164
|
|