warp-lang 1.4.2__py3-none-manylinux2014_x86_64.whl → 1.5.1__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 +4 -0
- warp/autograd.py +43 -8
- warp/bin/warp-clang.so +0 -0
- warp/bin/warp.so +0 -0
- warp/build.py +21 -2
- warp/build_dll.py +23 -6
- warp/builtins.py +1819 -7
- warp/codegen.py +197 -61
- warp/config.py +2 -2
- warp/context.py +379 -107
- warp/examples/assets/pixel.jpg +0 -0
- warp/examples/benchmarks/benchmark_cloth_paddle.py +86 -0
- warp/examples/benchmarks/benchmark_gemm.py +121 -0
- warp/examples/benchmarks/benchmark_interop_paddle.py +158 -0
- warp/examples/benchmarks/benchmark_tile.py +179 -0
- warp/examples/fem/example_adaptive_grid.py +37 -10
- warp/examples/fem/example_apic_fluid.py +3 -2
- warp/examples/fem/example_convection_diffusion_dg.py +4 -5
- warp/examples/fem/example_deformed_geometry.py +1 -1
- warp/examples/fem/example_diffusion_3d.py +47 -4
- warp/examples/fem/example_distortion_energy.py +220 -0
- warp/examples/fem/example_magnetostatics.py +127 -85
- warp/examples/fem/example_nonconforming_contact.py +5 -5
- warp/examples/fem/example_stokes.py +3 -1
- warp/examples/fem/example_streamlines.py +12 -19
- warp/examples/fem/utils.py +38 -15
- warp/examples/sim/example_cloth.py +4 -25
- warp/examples/sim/example_quadruped.py +2 -1
- warp/examples/tile/example_tile_convolution.py +58 -0
- warp/examples/tile/example_tile_fft.py +47 -0
- warp/examples/tile/example_tile_filtering.py +105 -0
- warp/examples/tile/example_tile_matmul.py +79 -0
- warp/examples/tile/example_tile_mlp.py +375 -0
- warp/fem/__init__.py +8 -0
- warp/fem/cache.py +16 -12
- warp/fem/dirichlet.py +1 -1
- warp/fem/domain.py +44 -1
- warp/fem/field/__init__.py +1 -2
- warp/fem/field/field.py +31 -19
- warp/fem/field/nodal_field.py +101 -49
- warp/fem/field/virtual.py +794 -0
- warp/fem/geometry/__init__.py +2 -2
- warp/fem/geometry/deformed_geometry.py +3 -105
- warp/fem/geometry/element.py +13 -0
- warp/fem/geometry/geometry.py +165 -7
- warp/fem/geometry/grid_2d.py +3 -6
- warp/fem/geometry/grid_3d.py +31 -28
- warp/fem/geometry/hexmesh.py +3 -46
- warp/fem/geometry/nanogrid.py +3 -2
- warp/fem/geometry/{quadmesh_2d.py → quadmesh.py} +280 -159
- warp/fem/geometry/tetmesh.py +2 -43
- warp/fem/geometry/{trimesh_2d.py → trimesh.py} +354 -186
- warp/fem/integrate.py +683 -261
- warp/fem/linalg.py +404 -0
- warp/fem/operator.py +101 -18
- warp/fem/polynomial.py +5 -5
- warp/fem/quadrature/quadrature.py +45 -21
- warp/fem/space/__init__.py +45 -11
- warp/fem/space/basis_function_space.py +451 -0
- warp/fem/space/basis_space.py +58 -11
- warp/fem/space/function_space.py +146 -5
- warp/fem/space/grid_2d_function_space.py +80 -66
- warp/fem/space/grid_3d_function_space.py +113 -68
- warp/fem/space/hexmesh_function_space.py +96 -108
- warp/fem/space/nanogrid_function_space.py +62 -110
- warp/fem/space/quadmesh_function_space.py +208 -0
- warp/fem/space/shape/__init__.py +45 -7
- warp/fem/space/shape/cube_shape_function.py +328 -54
- warp/fem/space/shape/shape_function.py +10 -1
- warp/fem/space/shape/square_shape_function.py +328 -60
- warp/fem/space/shape/tet_shape_function.py +269 -19
- warp/fem/space/shape/triangle_shape_function.py +238 -19
- warp/fem/space/tetmesh_function_space.py +69 -37
- warp/fem/space/topology.py +38 -0
- warp/fem/space/trimesh_function_space.py +179 -0
- warp/fem/utils.py +6 -331
- warp/jax_experimental.py +3 -1
- warp/native/array.h +15 -0
- warp/native/builtin.h +66 -26
- warp/native/bvh.h +4 -0
- warp/native/coloring.cpp +604 -0
- warp/native/cuda_util.cpp +68 -51
- warp/native/cuda_util.h +2 -1
- warp/native/fabric.h +8 -0
- warp/native/hashgrid.h +4 -0
- warp/native/marching.cu +8 -0
- warp/native/mat.h +14 -3
- warp/native/mathdx.cpp +59 -0
- warp/native/mesh.h +4 -0
- warp/native/range.h +13 -1
- warp/native/reduce.cpp +9 -1
- warp/native/reduce.cu +7 -0
- warp/native/runlength_encode.cpp +9 -1
- warp/native/runlength_encode.cu +7 -1
- warp/native/scan.cpp +8 -0
- warp/native/scan.cu +8 -0
- warp/native/scan.h +8 -1
- warp/native/sparse.cpp +8 -0
- warp/native/sparse.cu +8 -0
- warp/native/temp_buffer.h +7 -0
- warp/native/tile.h +1854 -0
- warp/native/tile_gemm.h +341 -0
- warp/native/tile_reduce.h +210 -0
- warp/native/volume_builder.cu +8 -0
- warp/native/volume_builder.h +8 -0
- warp/native/warp.cpp +10 -2
- warp/native/warp.cu +369 -15
- warp/native/warp.h +12 -2
- warp/optim/adam.py +39 -4
- warp/paddle.py +29 -12
- warp/render/render_opengl.py +140 -67
- warp/sim/graph_coloring.py +292 -0
- warp/sim/import_urdf.py +8 -8
- warp/sim/integrator_euler.py +4 -2
- warp/sim/integrator_featherstone.py +115 -44
- warp/sim/integrator_vbd.py +6 -0
- warp/sim/model.py +109 -32
- warp/sparse.py +1 -1
- warp/stubs.py +569 -4
- warp/tape.py +12 -7
- warp/tests/assets/pixel.npy +0 -0
- warp/tests/aux_test_instancing_gc.py +18 -0
- warp/tests/test_array.py +39 -0
- warp/tests/test_codegen.py +81 -1
- warp/tests/test_codegen_instancing.py +30 -0
- warp/tests/test_collision.py +110 -0
- warp/tests/test_coloring.py +251 -0
- warp/tests/test_context.py +34 -0
- warp/tests/test_examples.py +21 -5
- warp/tests/test_fem.py +453 -113
- warp/tests/test_func.py +34 -4
- warp/tests/test_generics.py +52 -0
- warp/tests/test_iter.py +68 -0
- warp/tests/test_lerp.py +13 -87
- warp/tests/test_mat_scalar_ops.py +1 -1
- warp/tests/test_matmul.py +6 -9
- warp/tests/test_matmul_lite.py +6 -11
- warp/tests/test_mesh_query_point.py +1 -1
- warp/tests/test_module_hashing.py +23 -0
- warp/tests/test_overwrite.py +45 -0
- warp/tests/test_paddle.py +27 -87
- warp/tests/test_print.py +56 -1
- warp/tests/test_smoothstep.py +17 -83
- warp/tests/test_spatial.py +1 -1
- warp/tests/test_static.py +3 -3
- warp/tests/test_tile.py +744 -0
- warp/tests/test_tile_mathdx.py +144 -0
- warp/tests/test_tile_mlp.py +383 -0
- warp/tests/test_tile_reduce.py +374 -0
- warp/tests/test_tile_shared_memory.py +190 -0
- warp/tests/test_vbd.py +12 -20
- warp/tests/test_volume.py +43 -0
- warp/tests/unittest_suites.py +19 -2
- warp/tests/unittest_utils.py +4 -2
- warp/types.py +340 -74
- warp/utils.py +23 -3
- {warp_lang-1.4.2.dist-info → warp_lang-1.5.1.dist-info}/METADATA +32 -7
- {warp_lang-1.4.2.dist-info → warp_lang-1.5.1.dist-info}/RECORD +161 -134
- {warp_lang-1.4.2.dist-info → warp_lang-1.5.1.dist-info}/WHEEL +1 -1
- warp/fem/field/test.py +0 -180
- warp/fem/field/trial.py +0 -183
- warp/fem/space/collocated_function_space.py +0 -102
- warp/fem/space/quadmesh_2d_function_space.py +0 -261
- warp/fem/space/trimesh_2d_function_space.py +0 -153
- {warp_lang-1.4.2.dist-info → warp_lang-1.5.1.dist-info}/LICENSE.md +0 -0
- {warp_lang-1.4.2.dist-info → warp_lang-1.5.1.dist-info}/top_level.txt +0 -0
|
@@ -88,7 +88,7 @@ class Example:
|
|
|
88
88
|
geo,
|
|
89
89
|
discontinuous=True,
|
|
90
90
|
degree=degree,
|
|
91
|
-
family=fem.Polynomial.
|
|
91
|
+
family=fem.Polynomial.LOBATTO_GAUSS_LEGENDRE,
|
|
92
92
|
)
|
|
93
93
|
|
|
94
94
|
# Assemble transport, diffusion and inertia matrices
|
|
@@ -110,23 +110,22 @@ class Example:
|
|
|
110
110
|
|
|
111
111
|
side_test = fem.make_test(space=scalar_space, domain=sides)
|
|
112
112
|
side_trial = fem.make_trial(space=scalar_space, domain=sides)
|
|
113
|
-
|
|
114
|
-
matrix_transport += fem.integrate(
|
|
113
|
+
fem.integrate(
|
|
115
114
|
upwind_transport_form,
|
|
116
115
|
fields={"phi": side_trial, "psi": side_test},
|
|
117
116
|
values={"ang_vel": ang_vel},
|
|
117
|
+
output=matrix_transport,
|
|
118
|
+
add=True,
|
|
118
119
|
)
|
|
119
120
|
|
|
120
121
|
matrix_diffusion = fem.integrate(
|
|
121
122
|
diffusion_form,
|
|
122
123
|
fields={"u": trial, "v": self._test},
|
|
123
124
|
)
|
|
124
|
-
|
|
125
125
|
matrix_diffusion += fem.integrate(
|
|
126
126
|
sip_diffusion_form,
|
|
127
127
|
fields={"phi": side_trial, "psi": side_test},
|
|
128
128
|
)
|
|
129
|
-
|
|
130
129
|
self._matrix = matrix_inertia + matrix_transport + viscosity * matrix_diffusion
|
|
131
130
|
|
|
132
131
|
# Initial condition
|
|
@@ -16,6 +16,8 @@
|
|
|
16
16
|
# and homogeneous Dirichlet boundary conditions other sides.
|
|
17
17
|
###########################################################################
|
|
18
18
|
|
|
19
|
+
import numpy as np
|
|
20
|
+
|
|
19
21
|
import warp as wp
|
|
20
22
|
import warp.examples.fem.utils as fem_example_utils
|
|
21
23
|
import warp.fem as fem
|
|
@@ -24,7 +26,7 @@ from warp.sparse import bsr_axpy
|
|
|
24
26
|
|
|
25
27
|
|
|
26
28
|
@fem.integrand
|
|
27
|
-
def
|
|
29
|
+
def vertical_boundary_projector_form(
|
|
28
30
|
s: fem.Sample,
|
|
29
31
|
domain: fem.Domain,
|
|
30
32
|
u: fem.Field,
|
|
@@ -36,6 +38,18 @@ def vert_boundary_projector_form(
|
|
|
36
38
|
return w * u(s) * v(s)
|
|
37
39
|
|
|
38
40
|
|
|
41
|
+
@fem.integrand
|
|
42
|
+
def y_boundary_projector_form(
|
|
43
|
+
s: fem.Sample,
|
|
44
|
+
domain: fem.Domain,
|
|
45
|
+
u: fem.Field,
|
|
46
|
+
v: fem.Field,
|
|
47
|
+
):
|
|
48
|
+
# Constrain Y edges
|
|
49
|
+
tangent = fem.deformation_gradient(domain, s)
|
|
50
|
+
return wp.abs(tangent[1]) * u(s) * v(s)
|
|
51
|
+
|
|
52
|
+
|
|
39
53
|
class Example:
|
|
40
54
|
def __init__(
|
|
41
55
|
self,
|
|
@@ -77,6 +91,28 @@ class Example:
|
|
|
77
91
|
bounds_hi=bounds_hi,
|
|
78
92
|
)
|
|
79
93
|
self._geo = fem.Nanogrid(volume)
|
|
94
|
+
elif mesh == "tri":
|
|
95
|
+
pos, quad_vtx_indices = fem_example_utils.gen_trimesh(
|
|
96
|
+
res=res,
|
|
97
|
+
bounds_lo=bounds_lo,
|
|
98
|
+
bounds_hi=bounds_hi,
|
|
99
|
+
)
|
|
100
|
+
pos = pos.numpy()
|
|
101
|
+
pos_z = np.cos(3.0 * pos[:, 0]) * np.sin(4.0 * pos[:, 1])
|
|
102
|
+
pos = np.hstack((pos, np.expand_dims(pos_z, axis=1)))
|
|
103
|
+
pos = wp.array(pos, dtype=wp.vec3)
|
|
104
|
+
self._geo = fem.Trimesh3D(quad_vtx_indices, pos)
|
|
105
|
+
elif mesh == "quad":
|
|
106
|
+
pos, quad_vtx_indices = fem_example_utils.gen_quadmesh(
|
|
107
|
+
res=res,
|
|
108
|
+
bounds_lo=bounds_lo,
|
|
109
|
+
bounds_hi=bounds_hi,
|
|
110
|
+
)
|
|
111
|
+
pos = pos.numpy()
|
|
112
|
+
pos_z = np.cos(3.0 * pos[:, 0]) * np.sin(4.0 * pos[:, 1])
|
|
113
|
+
pos = np.hstack((pos, np.expand_dims(pos_z, axis=1)))
|
|
114
|
+
pos = wp.array(pos, dtype=wp.vec3)
|
|
115
|
+
self._geo = fem.Quadmesh3D(quad_vtx_indices, pos)
|
|
80
116
|
else:
|
|
81
117
|
self._geo = fem.Grid3D(
|
|
82
118
|
res=res,
|
|
@@ -108,7 +144,12 @@ class Example:
|
|
|
108
144
|
|
|
109
145
|
bd_test = fem.make_test(space=self._scalar_space, domain=boundary)
|
|
110
146
|
bd_trial = fem.make_trial(space=self._scalar_space, domain=boundary)
|
|
111
|
-
|
|
147
|
+
|
|
148
|
+
# Pick boundary conditions depending on whether our geometry is a 3d surface or a volume
|
|
149
|
+
boundary_projector_form = (
|
|
150
|
+
vertical_boundary_projector_form if self._geo.cell_dimension == 3 else y_boundary_projector_form
|
|
151
|
+
)
|
|
152
|
+
bd_matrix = fem.integrate(boundary_projector_form, fields={"u": bd_trial, "v": bd_test}, nodal=True)
|
|
112
153
|
|
|
113
154
|
# Diffusion form
|
|
114
155
|
trial = fem.make_trial(space=self._scalar_space, domain=domain)
|
|
@@ -146,7 +187,9 @@ if __name__ == "__main__":
|
|
|
146
187
|
parser.add_argument(
|
|
147
188
|
"--boundary_compliance", type=float, default=0.0, help="Dirichlet boundary condition compliance."
|
|
148
189
|
)
|
|
149
|
-
parser.add_argument(
|
|
190
|
+
parser.add_argument(
|
|
191
|
+
"--mesh", choices=("grid", "tet", "hex", "nano", "anano", "tri", "quad"), default="grid", help="Mesh type."
|
|
192
|
+
)
|
|
150
193
|
parser.add_argument(
|
|
151
194
|
"--headless",
|
|
152
195
|
action="store_true",
|
|
@@ -171,4 +214,4 @@ if __name__ == "__main__":
|
|
|
171
214
|
example.render()
|
|
172
215
|
|
|
173
216
|
if not args.headless:
|
|
174
|
-
example.renderer.plot(
|
|
217
|
+
example.renderer.plot()
|
|
@@ -0,0 +1,220 @@
|
|
|
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 Distortion Energy
|
|
10
|
+
#
|
|
11
|
+
# This example illustrates using a Newton loop to minimize distortion of a
|
|
12
|
+
# 3D surface (u,v) parameterization under a Symmetric Dirichlet energy,
|
|
13
|
+
#
|
|
14
|
+
# E(F) = 1/2 |F|^2 + |F^{-1}|^2
|
|
15
|
+
#
|
|
16
|
+
# with F := dx/du
|
|
17
|
+
###########################################################################
|
|
18
|
+
|
|
19
|
+
import numpy as np
|
|
20
|
+
|
|
21
|
+
import warp as wp
|
|
22
|
+
import warp.examples.fem.utils as fem_example_utils
|
|
23
|
+
import warp.fem as fem
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@fem.integrand
|
|
27
|
+
def distortion_gradient_form(s: fem.Sample, u_cur: fem.Field, v: fem.Field):
|
|
28
|
+
# Symmetric Dirichlet energy gradient (linear form)
|
|
29
|
+
# E = 1/2 (F:F + F^-T:F^-T)
|
|
30
|
+
|
|
31
|
+
F = fem.grad(u_cur, s)
|
|
32
|
+
|
|
33
|
+
F_inv_sq = wp.inverse(F * wp.transpose(F))
|
|
34
|
+
F_inv = F_inv_sq * F
|
|
35
|
+
|
|
36
|
+
dE_dF = F - F_inv_sq * F_inv
|
|
37
|
+
|
|
38
|
+
return wp.ddot(fem.grad(v, s), dE_dF)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@fem.integrand
|
|
42
|
+
def distortion_hessian_form(s: fem.Sample, u_cur: fem.Field, u: fem.Field, v: fem.Field):
|
|
43
|
+
# Symmetric Dirichlet energy approximate hessian (bilinear form)
|
|
44
|
+
|
|
45
|
+
# F:F term
|
|
46
|
+
H = wp.ddot(fem.grad(v, s), fem.grad(u, s))
|
|
47
|
+
|
|
48
|
+
# F^-T:F^-T term
|
|
49
|
+
F = fem.grad(u_cur, s)
|
|
50
|
+
F_inv_sq = wp.inverse(F * wp.transpose(F))
|
|
51
|
+
|
|
52
|
+
# Gauss--Newton (ignore F^-2 derivative)
|
|
53
|
+
H += wp.ddot(F_inv_sq * fem.grad(v, s), F_inv_sq * F_inv_sq * fem.grad(u, s))
|
|
54
|
+
|
|
55
|
+
return H
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
@fem.integrand
|
|
59
|
+
def initial_guess(
|
|
60
|
+
s: fem.Sample,
|
|
61
|
+
domain: fem.Domain,
|
|
62
|
+
):
|
|
63
|
+
# initialization for UV parameter
|
|
64
|
+
x = domain(s)
|
|
65
|
+
return wp.vec2(x[0], x[1])
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
@fem.integrand
|
|
69
|
+
def boundary_projector_form(
|
|
70
|
+
s: fem.Sample,
|
|
71
|
+
domain: fem.Domain,
|
|
72
|
+
u: fem.Field,
|
|
73
|
+
v: fem.Field,
|
|
74
|
+
):
|
|
75
|
+
# Fix a single point
|
|
76
|
+
# (underconstrained, solution up to a rotation in UV space)
|
|
77
|
+
w = wp.select(s.qp_index == 0, 0.0, 1.0)
|
|
78
|
+
return w * wp.dot(u(s), v(s))
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
@fem.integrand
|
|
82
|
+
def checkerboard(s: fem.Sample, domain: fem.Domain, u: fem.Field):
|
|
83
|
+
# checkerboard pattern for parameter visualization
|
|
84
|
+
u_s = u(s)
|
|
85
|
+
return wp.sign(wp.cos(16.0 * u_s[0]) * wp.sin(16.0 * u_s[1]))
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
class Example:
|
|
89
|
+
def __init__(
|
|
90
|
+
self,
|
|
91
|
+
quiet=False,
|
|
92
|
+
degree=2,
|
|
93
|
+
resolution=25,
|
|
94
|
+
mesh="grid",
|
|
95
|
+
nonconforming_stresses=False,
|
|
96
|
+
):
|
|
97
|
+
self._quiet = quiet
|
|
98
|
+
|
|
99
|
+
def deform_along_z(positions, z_scale=1.0):
|
|
100
|
+
pos = positions.numpy()
|
|
101
|
+
pos_z = z_scale * np.cos(3.0 * pos[:, 0]) * np.sin(4.0 * pos[:, 1])
|
|
102
|
+
pos = np.hstack((pos, np.expand_dims(pos_z, axis=1)))
|
|
103
|
+
return wp.array(pos, dtype=wp.vec3)
|
|
104
|
+
|
|
105
|
+
# Grid or mesh geometry
|
|
106
|
+
if mesh == "tri":
|
|
107
|
+
positions, tri_vidx = fem_example_utils.gen_trimesh(res=wp.vec2i(resolution))
|
|
108
|
+
self._uv_geo = fem.Trimesh2D(tri_vertex_indices=tri_vidx, positions=wp.zeros_like(positions))
|
|
109
|
+
|
|
110
|
+
positions = deform_along_z(positions)
|
|
111
|
+
self._geo = fem.Trimesh3D(tri_vertex_indices=tri_vidx, positions=positions)
|
|
112
|
+
elif mesh == "quad":
|
|
113
|
+
positions, quad_vidx = fem_example_utils.gen_quadmesh(res=wp.vec2i(resolution))
|
|
114
|
+
self._uv_geo = fem.Quadmesh2D(quad_vertex_indices=quad_vidx, positions=wp.zeros_like(positions))
|
|
115
|
+
|
|
116
|
+
positions = deform_along_z(positions)
|
|
117
|
+
self._geo = fem.Quadmesh3D(quad_vertex_indices=quad_vidx, positions=positions)
|
|
118
|
+
else:
|
|
119
|
+
positions, quad_vidx = fem_example_utils.gen_quadmesh(res=wp.vec2i(resolution))
|
|
120
|
+
self._uv_geo = fem.Quadmesh2D(quad_vertex_indices=quad_vidx, positions=wp.zeros_like(positions))
|
|
121
|
+
|
|
122
|
+
undef_positions = deform_along_z(positions, z_scale=0.0)
|
|
123
|
+
flat_geo = fem.Quadmesh3D(quad_vertex_indices=quad_vidx, positions=undef_positions)
|
|
124
|
+
|
|
125
|
+
deformation_field = fem.make_discrete_field(fem.make_polynomial_space(flat_geo, dtype=wp.vec3))
|
|
126
|
+
deformation_field.dof_values = deform_along_z(positions)
|
|
127
|
+
|
|
128
|
+
self._geo = deformation_field.make_deformed_geometry(relative=False)
|
|
129
|
+
|
|
130
|
+
# parameter space
|
|
131
|
+
self._u_space = fem.make_polynomial_space(self._geo, degree=degree, dtype=wp.vec2)
|
|
132
|
+
self._u_field = self._u_space.make_field()
|
|
133
|
+
self._du_field = self._u_space.make_field()
|
|
134
|
+
fem.interpolate(initial_guess, dest=self._u_field)
|
|
135
|
+
|
|
136
|
+
# scalar parameter visualization function
|
|
137
|
+
viz_space = fem.make_polynomial_space(self._geo, degree=3, dtype=float)
|
|
138
|
+
self.viz_field = viz_space.make_field()
|
|
139
|
+
# For visualization of uv in 2D space
|
|
140
|
+
uv_space = fem.make_polynomial_space(self._uv_geo, degree=degree, dtype=wp.vec2)
|
|
141
|
+
self._uv_field = uv_space.make_field()
|
|
142
|
+
|
|
143
|
+
self.renderer = fem_example_utils.Plot()
|
|
144
|
+
|
|
145
|
+
def step(self):
|
|
146
|
+
boundary = fem.BoundarySides(self._geo)
|
|
147
|
+
domain = fem.Cells(geometry=self._geo)
|
|
148
|
+
|
|
149
|
+
# Parameter boundary conditions
|
|
150
|
+
u_bd_test = fem.make_test(space=self._u_space, domain=boundary)
|
|
151
|
+
u_bd_trial = fem.make_trial(space=self._u_space, domain=boundary)
|
|
152
|
+
u_bd_matrix = fem.integrate(
|
|
153
|
+
boundary_projector_form, fields={"u": u_bd_trial, "v": u_bd_test}, nodal=True, output_dtype=float
|
|
154
|
+
)
|
|
155
|
+
fem.normalize_dirichlet_projector(u_bd_matrix)
|
|
156
|
+
|
|
157
|
+
u_test = fem.make_test(space=self._u_space, domain=domain)
|
|
158
|
+
u_trial = fem.make_trial(space=self._u_space, domain=domain)
|
|
159
|
+
|
|
160
|
+
# Newton iterations (without line-search for simplicity)
|
|
161
|
+
for _newton_iteration in range(10):
|
|
162
|
+
u_matrix = fem.integrate(
|
|
163
|
+
distortion_hessian_form, fields={"u_cur": self._u_field, "u": u_trial, "v": u_test}, output_dtype=float
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
u_rhs = fem.integrate(
|
|
167
|
+
distortion_gradient_form, fields={"u_cur": self._u_field, "v": u_test}, output_dtype=wp.vec2
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
fem.project_linear_system(u_matrix, u_rhs, u_bd_matrix, normalize_projector=False)
|
|
171
|
+
|
|
172
|
+
# Solve for uv increment
|
|
173
|
+
du = self._du_field.dof_values
|
|
174
|
+
du.zero_()
|
|
175
|
+
fem_example_utils.bsr_cg(u_matrix, b=u_rhs, x=du, quiet=self._quiet)
|
|
176
|
+
|
|
177
|
+
# Accumulate to UV field
|
|
178
|
+
fem.utils.array_axpy(x=du, y=self._u_field.dof_values, alpha=-1.0, beta=1.0)
|
|
179
|
+
|
|
180
|
+
def render(self):
|
|
181
|
+
# Visualization
|
|
182
|
+
fem.interpolate(checkerboard, fields={"u": self._u_field}, dest=self.viz_field)
|
|
183
|
+
|
|
184
|
+
self._uv_field.dof_values = wp.clone(self._u_field.dof_values)
|
|
185
|
+
|
|
186
|
+
self.renderer.add_field("pattern", self.viz_field)
|
|
187
|
+
self.renderer.add_field("uv", self._uv_field)
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
if __name__ == "__main__":
|
|
191
|
+
import argparse
|
|
192
|
+
|
|
193
|
+
wp.set_module_options({"enable_backward": False})
|
|
194
|
+
|
|
195
|
+
parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter)
|
|
196
|
+
parser.add_argument("--device", type=str, default=None, help="Override the default Warp device.")
|
|
197
|
+
parser.add_argument("--resolution", type=int, default=25, help="Grid resolution.")
|
|
198
|
+
parser.add_argument("--degree", type=int, default=1, help="Polynomial degree of shape functions.")
|
|
199
|
+
parser.add_argument("--mesh", choices=("tri", "quad", "deformed"), default="tri", help="Mesh type")
|
|
200
|
+
parser.add_argument(
|
|
201
|
+
"--headless",
|
|
202
|
+
action="store_true",
|
|
203
|
+
help="Run in headless mode, suppressing the opening of any graphical windows.",
|
|
204
|
+
)
|
|
205
|
+
parser.add_argument("--quiet", action="store_true", help="Suppresses the printing out of iteration residuals.")
|
|
206
|
+
|
|
207
|
+
args = parser.parse_known_args()[0]
|
|
208
|
+
|
|
209
|
+
with wp.ScopedDevice(args.device):
|
|
210
|
+
example = Example(
|
|
211
|
+
quiet=args.quiet,
|
|
212
|
+
degree=args.degree,
|
|
213
|
+
resolution=args.resolution,
|
|
214
|
+
mesh=args.mesh,
|
|
215
|
+
)
|
|
216
|
+
example.step()
|
|
217
|
+
example.render()
|
|
218
|
+
|
|
219
|
+
if not args.headless:
|
|
220
|
+
example.renderer.plot(options={"uv": {"displacement": {}}})
|
|
@@ -8,17 +8,18 @@
|
|
|
8
8
|
###########################################################################
|
|
9
9
|
# Example Magnetostatics
|
|
10
10
|
#
|
|
11
|
-
# This example demonstrates solving
|
|
12
|
-
#
|
|
11
|
+
# This example demonstrates solving a 3d magnetostatics problem
|
|
12
|
+
# (a copper coil with radial current around a cylindrical iron core)
|
|
13
|
+
# using a curl-curl formulation and H(curl)-conforming function space
|
|
13
14
|
#
|
|
14
15
|
# 1/mu Curl B + j = 0
|
|
15
16
|
# Div. B = 0
|
|
16
17
|
#
|
|
17
|
-
# solved over field A such that B = Curl A,
|
|
18
|
-
# and
|
|
18
|
+
# solved over field A such that B = Curl A,
|
|
19
|
+
# and Direchlet homogeneous essential boundary conditions
|
|
19
20
|
#
|
|
20
|
-
# This example also illustrates using an ImplictField to warp a
|
|
21
|
-
# to a
|
|
21
|
+
# This example also illustrates using an ImplictField to warp a grid mesh
|
|
22
|
+
# to a cylindrical domain
|
|
22
23
|
###########################################################################
|
|
23
24
|
|
|
24
25
|
import numpy as np
|
|
@@ -27,138 +28,179 @@ import warp as wp
|
|
|
27
28
|
import warp.examples.fem.utils as fem_example_utils
|
|
28
29
|
import warp.fem as fem
|
|
29
30
|
|
|
30
|
-
#
|
|
31
|
-
MU_0 = wp.constant(np.pi * 4.0e-7)
|
|
32
|
-
|
|
31
|
+
# Physics constants
|
|
32
|
+
MU_0 = wp.constant(np.pi * 4.0e-7) # Vacuum magnetic permeability
|
|
33
|
+
MU_c = wp.constant(1.25e-6) # Copper magnetic permeability
|
|
34
|
+
MU_i = wp.constant(6.0e-3) # Iron magnetic permeability
|
|
33
35
|
|
|
34
36
|
|
|
35
37
|
@wp.func
|
|
36
|
-
def
|
|
38
|
+
def cube_to_cylinder(x: wp.vec3):
|
|
37
39
|
# mapping from unit square to unit disk
|
|
38
|
-
|
|
40
|
+
pos_xz = wp.vec3(x[0], 0.0, x[2])
|
|
41
|
+
return wp.max(wp.abs(pos_xz)) * wp.normalize(pos_xz) + wp.vec3(0.0, x[1], 0.0)
|
|
39
42
|
|
|
40
43
|
|
|
41
44
|
@wp.func
|
|
42
|
-
def
|
|
45
|
+
def cube_to_cylinder_grad(x: wp.vec3):
|
|
43
46
|
# gradient of mapping from unit square to unit disk
|
|
44
|
-
|
|
45
|
-
|
|
47
|
+
pos_xz = wp.vec3(x[0], 0.0, x[2])
|
|
48
|
+
if pos_xz == wp.vec3(0.0):
|
|
49
|
+
grad = wp.mat33(0.0)
|
|
50
|
+
else:
|
|
51
|
+
dir_xz = wp.normalize(pos_xz)
|
|
52
|
+
dir_grad = (wp.identity(n=3, dtype=float) - wp.outer(dir_xz, dir_xz)) / wp.length(pos_xz)
|
|
53
|
+
|
|
54
|
+
abs_xz = wp.abs(pos_xz)
|
|
55
|
+
xinf_grad = wp.select(
|
|
56
|
+
abs_xz[0] > abs_xz[2], wp.vec3(0.0, 0.0, wp.sign(pos_xz[2])), wp.vec(wp.sign(pos_xz[0]), 0.0, 0.0)
|
|
57
|
+
)
|
|
58
|
+
grad = dir_grad * wp.max(abs_xz) + wp.outer(dir_xz, xinf_grad)
|
|
46
59
|
|
|
47
|
-
|
|
48
|
-
|
|
60
|
+
grad[1, 1] = 1.0
|
|
61
|
+
return grad
|
|
49
62
|
|
|
50
|
-
ax = wp.abs(x)
|
|
51
|
-
xinf = wp.max(ax)
|
|
52
63
|
|
|
53
|
-
|
|
64
|
+
@wp.func
|
|
65
|
+
def permeability_field(
|
|
66
|
+
pos: wp.vec3,
|
|
67
|
+
core_radius: float,
|
|
68
|
+
core_height: float,
|
|
69
|
+
coil_internal_radius: float,
|
|
70
|
+
coil_external_radius: float,
|
|
71
|
+
coil_height: float,
|
|
72
|
+
):
|
|
73
|
+
x = wp.abs(pos[0])
|
|
74
|
+
y = wp.abs(pos[1])
|
|
75
|
+
z = wp.abs(pos[2])
|
|
54
76
|
|
|
55
|
-
|
|
77
|
+
r = wp.sqrt(x * x + z * z)
|
|
56
78
|
|
|
79
|
+
if r <= core_radius:
|
|
80
|
+
return wp.select(y < core_height, MU_0, MU_i)
|
|
57
81
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
# space-varying permeability
|
|
82
|
+
if r >= coil_internal_radius and r <= coil_external_radius:
|
|
83
|
+
return wp.select(y < coil_height, MU_0, MU_c)
|
|
61
84
|
|
|
62
|
-
|
|
63
|
-
y = wp.abs(pos[1])
|
|
64
|
-
return wp.select(
|
|
65
|
-
y < coil_height and x > coil_internal_radius and x < coil_external_radius,
|
|
66
|
-
MU_0,
|
|
67
|
-
MU_C,
|
|
68
|
-
)
|
|
85
|
+
return MU_0
|
|
69
86
|
|
|
70
87
|
|
|
71
88
|
@wp.func
|
|
72
89
|
def current_field(
|
|
73
|
-
pos: wp.
|
|
90
|
+
pos: wp.vec3,
|
|
91
|
+
current: float,
|
|
92
|
+
coil_internal_radius: float,
|
|
93
|
+
coil_external_radius: float,
|
|
94
|
+
coil_height: float,
|
|
74
95
|
):
|
|
75
|
-
|
|
76
|
-
x = wp.abs(pos[0])
|
|
96
|
+
x = pos[0]
|
|
77
97
|
y = wp.abs(pos[1])
|
|
78
|
-
|
|
79
|
-
wp.select(
|
|
80
|
-
y < coil_height and x > coil_internal_radius and x < coil_external_radius,
|
|
81
|
-
0.0,
|
|
82
|
-
wp.sign(pos[0]),
|
|
83
|
-
)
|
|
84
|
-
* current
|
|
85
|
-
)
|
|
98
|
+
z = pos[2]
|
|
86
99
|
|
|
100
|
+
r = wp.sqrt(x * x + z * z)
|
|
87
101
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
102
|
+
return wp.select(
|
|
103
|
+
y < coil_height and r >= coil_internal_radius and r <= coil_external_radius,
|
|
104
|
+
wp.vec3(0.0),
|
|
105
|
+
wp.vec3(z, 0.0, -x) * current / r,
|
|
106
|
+
)
|
|
93
107
|
|
|
94
108
|
|
|
95
109
|
@fem.integrand
|
|
96
110
|
def curl_curl_form(s: fem.Sample, domain: fem.Domain, u: fem.Field, v: fem.Field, mu: fem.Field):
|
|
97
|
-
return wp.dot(
|
|
111
|
+
return wp.dot(fem.curl(u, s), fem.curl(v, s)) / mu(s)
|
|
98
112
|
|
|
99
113
|
|
|
100
114
|
@fem.integrand
|
|
101
115
|
def mass_form(s: fem.Sample, domain: fem.Domain, v: fem.Field, u: fem.Field):
|
|
102
|
-
return u(s)
|
|
116
|
+
return wp.dot(u(s), v(s))
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
@fem.integrand
|
|
120
|
+
def curl_expr(s: fem.Sample, u: fem.Field):
|
|
121
|
+
return fem.curl(u, s)
|
|
103
122
|
|
|
104
123
|
|
|
105
124
|
class Example:
|
|
106
|
-
def __init__(self, quiet=False,
|
|
125
|
+
def __init__(self, quiet=False, mesh: str = "grid", resolution=32, domain_radius=2.0, current=1.0e6):
|
|
107
126
|
# We mesh the unit disk by first meshing the unit square, then building a deformed geometry
|
|
108
127
|
# from an implicit mapping field
|
|
109
|
-
square_geo = fem.Grid2D(
|
|
110
|
-
bounds_lo=wp.vec2(-domain_radius, -domain_radius),
|
|
111
|
-
bounds_hi=wp.vec2(domain_radius, domain_radius),
|
|
112
|
-
res=wp.vec2i(resolution, resolution),
|
|
113
|
-
)
|
|
114
128
|
|
|
115
|
-
|
|
116
|
-
|
|
129
|
+
if mesh == "hex":
|
|
130
|
+
positions, hex_vidx = fem_example_utils.gen_hexmesh(
|
|
131
|
+
bounds_lo=wp.vec3(-domain_radius, -domain_radius, -domain_radius),
|
|
132
|
+
bounds_hi=wp.vec3(domain_radius, domain_radius, domain_radius),
|
|
133
|
+
res=wp.vec3i(resolution, resolution, resolution),
|
|
134
|
+
)
|
|
135
|
+
cube_geo = fem.Hexmesh(hex_vertex_indices=hex_vidx, positions=positions)
|
|
136
|
+
elif mesh == "tet":
|
|
137
|
+
positions, tet_vidx = fem_example_utils.gen_tetmesh(
|
|
138
|
+
bounds_lo=wp.vec3(-domain_radius, -domain_radius, -domain_radius),
|
|
139
|
+
bounds_hi=wp.vec3(domain_radius, domain_radius, domain_radius),
|
|
140
|
+
res=wp.vec3i(resolution, resolution, resolution),
|
|
141
|
+
)
|
|
142
|
+
cube_geo = fem.Tetmesh(tet_vertex_indices=tet_vidx, positions=positions)
|
|
143
|
+
elif mesh == "nano":
|
|
144
|
+
vol = fem_example_utils.gen_volume(
|
|
145
|
+
bounds_lo=wp.vec3(-domain_radius, -domain_radius, -domain_radius),
|
|
146
|
+
bounds_hi=wp.vec3(domain_radius, domain_radius, domain_radius),
|
|
147
|
+
res=wp.vec3i(resolution, resolution, resolution),
|
|
148
|
+
)
|
|
149
|
+
cube_geo = fem.Nanogrid(grid=vol)
|
|
150
|
+
else:
|
|
151
|
+
cube_geo = fem.Grid3D(
|
|
152
|
+
bounds_lo=wp.vec3(-domain_radius, -domain_radius, -domain_radius),
|
|
153
|
+
bounds_hi=wp.vec3(domain_radius, domain_radius, domain_radius),
|
|
154
|
+
res=wp.vec3i(resolution, resolution, resolution),
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
def_field = fem.ImplicitField(
|
|
158
|
+
domain=fem.Cells(cube_geo), func=cube_to_cylinder, grad_func=cube_to_cylinder_grad
|
|
159
|
+
)
|
|
160
|
+
sim_geo = def_field.make_deformed_geometry(relative=False)
|
|
117
161
|
|
|
118
|
-
coil_config = {"coil_height":
|
|
162
|
+
coil_config = {"coil_height": 0.25, "coil_internal_radius": 0.3, "coil_external_radius": 0.4}
|
|
163
|
+
core_config = {"core_height": 1.0, "core_radius": 0.2}
|
|
119
164
|
|
|
120
|
-
domain = fem.Cells(
|
|
121
|
-
self._permeability_field = fem.ImplicitField(
|
|
165
|
+
domain = fem.Cells(sim_geo)
|
|
166
|
+
self._permeability_field = fem.ImplicitField(
|
|
167
|
+
domain, func=permeability_field, values=dict(**coil_config, **core_config)
|
|
168
|
+
)
|
|
122
169
|
self._current_field = fem.ImplicitField(domain, func=current_field, values=dict(current=current, **coil_config))
|
|
123
170
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
disk_geo, degree=degree, element_basis=fem.ElementBasis.LAGRANGE, dtype=wp.vec2
|
|
171
|
+
A_space = fem.make_polynomial_space(
|
|
172
|
+
sim_geo, degree=1, element_basis=fem.ElementBasis.NEDELEC_FIRST_KIND, dtype=wp.vec3
|
|
127
173
|
)
|
|
174
|
+
self.A_field = A_space.make_field()
|
|
128
175
|
|
|
129
|
-
|
|
130
|
-
self.B_field =
|
|
176
|
+
B_space = fem.make_polynomial_space(sim_geo, degree=1, element_basis=fem.ElementBasis.LAGRANGE, dtype=wp.vec3)
|
|
177
|
+
self.B_field = B_space.make_field()
|
|
131
178
|
|
|
132
179
|
self.renderer = fem_example_utils.Plot()
|
|
133
180
|
|
|
134
181
|
def step(self):
|
|
135
|
-
|
|
136
|
-
|
|
182
|
+
A_space = self.A_field.space
|
|
183
|
+
sim_geo = A_space.geometry
|
|
137
184
|
|
|
138
|
-
u = fem.make_trial(space=
|
|
139
|
-
v = fem.make_test(space=
|
|
140
|
-
lhs = fem.integrate(curl_curl_form, fields={"u": u, "v": v, "mu": self._permeability_field})
|
|
141
|
-
rhs = fem.integrate(mass_form, fields={"v": v, "u": self._current_field})
|
|
185
|
+
u = fem.make_trial(space=A_space)
|
|
186
|
+
v = fem.make_test(space=A_space)
|
|
187
|
+
lhs = fem.integrate(curl_curl_form, fields={"u": u, "v": v, "mu": self._permeability_field}, output_dtype=float)
|
|
188
|
+
rhs = fem.integrate(mass_form, fields={"v": v, "u": self._current_field}, output_dtype=float)
|
|
142
189
|
|
|
143
190
|
# Dirichlet BC
|
|
144
|
-
boundary = fem.BoundarySides(
|
|
145
|
-
u_bd = fem.make_trial(space=
|
|
146
|
-
v_bd = fem.make_test(space=
|
|
147
|
-
dirichlet_bd_proj = fem.integrate(mass_form, fields={"u": u_bd, "v": v_bd}, nodal=True)
|
|
191
|
+
boundary = fem.BoundarySides(sim_geo)
|
|
192
|
+
u_bd = fem.make_trial(space=A_space, domain=boundary)
|
|
193
|
+
v_bd = fem.make_test(space=A_space, domain=boundary)
|
|
194
|
+
dirichlet_bd_proj = fem.integrate(mass_form, fields={"u": u_bd, "v": v_bd}, nodal=True, output_dtype=float)
|
|
148
195
|
fem.project_linear_system(lhs, rhs, dirichlet_bd_proj)
|
|
149
196
|
|
|
150
|
-
|
|
151
|
-
fem_example_utils.bsr_cg(lhs, b=rhs, x=
|
|
152
|
-
|
|
153
|
-
# make sure result is exactly zero outside of circle
|
|
154
|
-
wp.sparse.bsr_mv(dirichlet_bd_proj, x=x, y=x, alpha=-1.0, beta=1.0)
|
|
155
|
-
wp.utils.array_cast(in_array=x, out_array=self.A_field.dof_values)
|
|
197
|
+
# solve using Conjugate Residual (numerically rhs may not be in image of lhs)
|
|
198
|
+
fem_example_utils.bsr_cg(lhs, b=rhs, x=self.A_field.dof_values, method="cr", max_iters=250, quiet=False)
|
|
156
199
|
|
|
157
200
|
# compute B as curl(A)
|
|
158
|
-
fem.interpolate(
|
|
201
|
+
fem.interpolate(curl_expr, dest=self.B_field, fields={"u": self.A_field})
|
|
159
202
|
|
|
160
203
|
def render(self):
|
|
161
|
-
self.renderer.add_field("A", self.A_field)
|
|
162
204
|
self.renderer.add_field("B", self.B_field)
|
|
163
205
|
|
|
164
206
|
|
|
@@ -170,7 +212,7 @@ if __name__ == "__main__":
|
|
|
170
212
|
parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter)
|
|
171
213
|
parser.add_argument("--device", type=str, default=None, help="Override the default Warp device.")
|
|
172
214
|
parser.add_argument("--resolution", type=int, default=32, help="Grid resolution.")
|
|
173
|
-
parser.add_argument("--
|
|
215
|
+
parser.add_argument("--mesh", type=str, default="grid", choices=["tet", "hex", "grid", "nano"], help="Mesh type.")
|
|
174
216
|
parser.add_argument("--radius", type=float, default=2.0, help="Radius of simulation domain.")
|
|
175
217
|
parser.add_argument(
|
|
176
218
|
"--headless",
|
|
@@ -182,9 +224,9 @@ if __name__ == "__main__":
|
|
|
182
224
|
args = parser.parse_known_args()[0]
|
|
183
225
|
|
|
184
226
|
with wp.ScopedDevice(args.device):
|
|
185
|
-
example = Example(quiet=args.quiet,
|
|
227
|
+
example = Example(quiet=args.quiet, mesh=args.mesh, resolution=args.resolution, domain_radius=args.radius)
|
|
186
228
|
example.step()
|
|
187
229
|
example.render()
|
|
188
230
|
|
|
189
231
|
if not args.headless:
|
|
190
|
-
example.renderer.plot({"
|
|
232
|
+
example.renderer.plot({"B": {"streamlines": {"density": 1.0}}})
|
|
@@ -210,11 +210,11 @@ class Example:
|
|
|
210
210
|
|
|
211
211
|
# Add boundary stress from other solid field
|
|
212
212
|
other_stress_field = fem.field.field.NonconformingField(boundary, other_stress_field)
|
|
213
|
-
fem.
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
213
|
+
fem.integrate(
|
|
214
|
+
boundary_stress_form,
|
|
215
|
+
fields={"u": u_bd_test, "tau": other_stress_field},
|
|
216
|
+
output=u_rhs,
|
|
217
|
+
add=True,
|
|
218
218
|
)
|
|
219
219
|
|
|
220
220
|
# Enforce boundary conditions
|