warp-lang 1.4.2__py3-none-macosx_10_13_universal2.whl → 1.5.1__py3-none-macosx_10_13_universal2.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/libwarp.dylib +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 +160 -133
- {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
warp/tests/test_fem.py
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
import math
|
|
9
9
|
import unittest
|
|
10
|
+
from typing import Any
|
|
10
11
|
|
|
11
12
|
import numpy as np
|
|
12
13
|
|
|
@@ -14,8 +15,8 @@ import warp as wp
|
|
|
14
15
|
import warp.fem as fem
|
|
15
16
|
from warp.fem import Coords, D, Domain, Field, Sample, curl, div, grad, integrand, normal
|
|
16
17
|
from warp.fem.cache import dynamic_kernel
|
|
17
|
-
from warp.fem.geometry import DeformedGeometry
|
|
18
18
|
from warp.fem.geometry.closest_point import project_on_tet_at_origin, project_on_tri_at_origin
|
|
19
|
+
from warp.fem.linalg import inverse_qr, spherical_part, symmetric_eigenvalues_qr, symmetric_part
|
|
19
20
|
from warp.fem.space import shape
|
|
20
21
|
from warp.fem.types import make_free_sample
|
|
21
22
|
from warp.fem.utils import (
|
|
@@ -23,8 +24,6 @@ from warp.fem.utils import (
|
|
|
23
24
|
grid_to_quads,
|
|
24
25
|
grid_to_tets,
|
|
25
26
|
grid_to_tris,
|
|
26
|
-
inverse_qr,
|
|
27
|
-
symmetric_eigenvalues_qr,
|
|
28
27
|
)
|
|
29
28
|
from warp.tests.unittest_utils import *
|
|
30
29
|
|
|
@@ -37,6 +36,17 @@ def linear_form(s: Sample, u: Field):
|
|
|
37
36
|
return u(s)
|
|
38
37
|
|
|
39
38
|
|
|
39
|
+
@integrand
|
|
40
|
+
def scaled_linear_form(s: Sample, u: Field, scale: wp.array(dtype=float)):
|
|
41
|
+
return u(s) * scale[0]
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@wp.kernel
|
|
45
|
+
def atomic_sum(v: wp.array(dtype=float), sum: wp.array(dtype=float)):
|
|
46
|
+
i = wp.tid()
|
|
47
|
+
wp.atomic_add(sum, 0, v[i])
|
|
48
|
+
|
|
49
|
+
|
|
40
50
|
def test_integrate_gradient(test, device):
|
|
41
51
|
with wp.ScopedDevice(device):
|
|
42
52
|
# Grid geometry
|
|
@@ -51,21 +61,59 @@ def test_integrate_gradient(test, device):
|
|
|
51
61
|
u = scalar_space.make_field()
|
|
52
62
|
u.dof_values = wp.zeros_like(u.dof_values, requires_grad=True)
|
|
53
63
|
|
|
54
|
-
result = wp.empty(dtype=wp.
|
|
55
|
-
|
|
64
|
+
result = wp.empty(dtype=wp.float32, shape=(1), requires_grad=True)
|
|
56
65
|
tape = wp.Tape()
|
|
57
66
|
|
|
58
67
|
# forward pass
|
|
59
68
|
with tape:
|
|
60
69
|
fem.integrate(linear_form, quadrature=quadrature, fields={"u": u}, output=result)
|
|
61
|
-
|
|
62
70
|
tape.backward(result)
|
|
63
71
|
|
|
64
72
|
test_field = fem.make_test(space=scalar_space, domain=domain)
|
|
65
|
-
rhs = fem.integrate(linear_form, quadrature=quadrature, fields={"u": test_field})
|
|
66
73
|
|
|
67
|
-
|
|
68
|
-
|
|
74
|
+
u_adj = wp.empty_like(u.dof_values, requires_grad=True)
|
|
75
|
+
scale = wp.ones(1, requires_grad=True)
|
|
76
|
+
loss = wp.zeros(1, requires_grad=True)
|
|
77
|
+
|
|
78
|
+
tape2 = wp.Tape()
|
|
79
|
+
with tape2:
|
|
80
|
+
fem.integrate(
|
|
81
|
+
scaled_linear_form,
|
|
82
|
+
quadrature=quadrature,
|
|
83
|
+
fields={"u": test_field},
|
|
84
|
+
values={"scale": scale},
|
|
85
|
+
assembly="generic",
|
|
86
|
+
output=u_adj,
|
|
87
|
+
)
|
|
88
|
+
wp.launch(atomic_sum, dim=u_adj.shape, inputs=[u_adj, loss])
|
|
89
|
+
|
|
90
|
+
# gradient of scalar integral w.r.t dofs should be equal to linear form vector
|
|
91
|
+
assert_np_equal(u_adj.numpy(), u.dof_values.grad.numpy(), tol=1.0e-8)
|
|
92
|
+
test.assertAlmostEqual(loss.numpy()[0], 1.0, places=4)
|
|
93
|
+
|
|
94
|
+
# Check gradient of linear form vec w.r.t value params
|
|
95
|
+
tape.zero()
|
|
96
|
+
tape2.backward(loss=loss)
|
|
97
|
+
|
|
98
|
+
test.assertAlmostEqual(loss.numpy()[0], scale.grad.numpy()[0], places=4)
|
|
99
|
+
tape2.zero()
|
|
100
|
+
test.assertEqual(scale.grad.numpy()[0], 0.0)
|
|
101
|
+
|
|
102
|
+
# Same, with dispatched assembly
|
|
103
|
+
tape2.reset()
|
|
104
|
+
loss.zero_()
|
|
105
|
+
with tape2:
|
|
106
|
+
fem.integrate(
|
|
107
|
+
scaled_linear_form,
|
|
108
|
+
quadrature=quadrature,
|
|
109
|
+
fields={"u": test_field},
|
|
110
|
+
values={"scale": scale},
|
|
111
|
+
assembly="dispatch",
|
|
112
|
+
output=u_adj,
|
|
113
|
+
)
|
|
114
|
+
wp.launch(atomic_sum, dim=u_adj.shape, inputs=[u_adj, loss])
|
|
115
|
+
tape2.backward(loss=loss)
|
|
116
|
+
test.assertAlmostEqual(loss.numpy()[0], scale.grad.numpy()[0], places=4)
|
|
69
117
|
|
|
70
118
|
|
|
71
119
|
@fem.integrand
|
|
@@ -121,7 +169,7 @@ def test_interpolate_gradient(test, device):
|
|
|
121
169
|
vector_field.dof_values.grad.assign([1.0, 0.0])
|
|
122
170
|
tape.backward()
|
|
123
171
|
|
|
124
|
-
assert_np_equal(scalar_field.dof_values.grad.numpy(), np.array([0.0, 0.0, 0.0, 0.0, 0.0, -0.5, 0.0, 0.5
|
|
172
|
+
assert_np_equal(scalar_field.dof_values.grad.numpy(), np.array([0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -0.5, 0.0, 0.5]))
|
|
125
173
|
assert_np_equal(
|
|
126
174
|
geo.positions.grad.numpy(),
|
|
127
175
|
np.array(
|
|
@@ -143,7 +191,7 @@ def test_interpolate_gradient(test, device):
|
|
|
143
191
|
vector_field.dof_values.grad.assign([0.0, 1.0])
|
|
144
192
|
tape.backward()
|
|
145
193
|
|
|
146
|
-
assert_np_equal(scalar_field.dof_values.grad.numpy(), np.array([0.0, 0.0, 0.0, 0.0, -0.5, 0.0, 0.5, 0.0
|
|
194
|
+
assert_np_equal(scalar_field.dof_values.grad.numpy(), np.array([0.0, 0.0, 0.0, 0.0, 0.0, -0.5, 0.0, 0.5, 0.0]))
|
|
147
195
|
assert_np_equal(
|
|
148
196
|
geo.positions.grad.numpy(),
|
|
149
197
|
np.array(
|
|
@@ -345,13 +393,13 @@ def test_grad_decomposition(test, device):
|
|
|
345
393
|
test.assertLess(err, 1.0e-8)
|
|
346
394
|
|
|
347
395
|
|
|
348
|
-
def _gen_trimesh(
|
|
349
|
-
x = np.linspace(0.0, 1.0,
|
|
350
|
-
y = np.linspace(0.0, 1.0,
|
|
396
|
+
def _gen_trimesh(Nx, Ny):
|
|
397
|
+
x = np.linspace(0.0, 1.0, Nx + 1)
|
|
398
|
+
y = np.linspace(0.0, 1.0, Ny + 1)
|
|
351
399
|
|
|
352
|
-
positions = np.transpose(np.meshgrid(x, y, indexing="ij")).reshape(-1, 2)
|
|
400
|
+
positions = np.transpose(np.meshgrid(x, y, indexing="ij"), axes=(1, 2, 0)).reshape(-1, 2)
|
|
353
401
|
|
|
354
|
-
vidx = grid_to_tris(
|
|
402
|
+
vidx = grid_to_tris(Nx, Ny)
|
|
355
403
|
|
|
356
404
|
return wp.array(positions, dtype=wp.vec2), wp.array(vidx, dtype=int)
|
|
357
405
|
|
|
@@ -360,21 +408,21 @@ def _gen_quadmesh(N):
|
|
|
360
408
|
x = np.linspace(0.0, 1.0, N + 1)
|
|
361
409
|
y = np.linspace(0.0, 1.0, N + 1)
|
|
362
410
|
|
|
363
|
-
positions = np.transpose(np.meshgrid(x, y, indexing="ij")).reshape(-1, 2)
|
|
411
|
+
positions = np.transpose(np.meshgrid(x, y, indexing="ij"), axes=(1, 2, 0)).reshape(-1, 2)
|
|
364
412
|
|
|
365
413
|
vidx = grid_to_quads(N, N)
|
|
366
414
|
|
|
367
415
|
return wp.array(positions, dtype=wp.vec2), wp.array(vidx, dtype=int)
|
|
368
416
|
|
|
369
417
|
|
|
370
|
-
def _gen_tetmesh(
|
|
371
|
-
x = np.linspace(0.0, 1.0,
|
|
372
|
-
y = np.linspace(0.0, 1.0,
|
|
373
|
-
z = np.linspace(0.0, 1.0,
|
|
418
|
+
def _gen_tetmesh(Nx, Ny, Nz):
|
|
419
|
+
x = np.linspace(0.0, 1.0, Nx + 1)
|
|
420
|
+
y = np.linspace(0.0, 1.0, Ny + 1)
|
|
421
|
+
z = np.linspace(0.0, 1.0, Nz + 1)
|
|
374
422
|
|
|
375
|
-
positions = np.transpose(np.meshgrid(x, y, z, indexing="ij")).reshape(-1, 3)
|
|
423
|
+
positions = np.transpose(np.meshgrid(x, y, z, indexing="ij"), axes=(1, 2, 3, 0)).reshape(-1, 3)
|
|
376
424
|
|
|
377
|
-
vidx = grid_to_tets(
|
|
425
|
+
vidx = grid_to_tets(Nx, Ny, Nz)
|
|
378
426
|
|
|
379
427
|
return wp.array(positions, dtype=wp.vec3), wp.array(vidx, dtype=int)
|
|
380
428
|
|
|
@@ -384,107 +432,98 @@ def _gen_hexmesh(N):
|
|
|
384
432
|
y = np.linspace(0.0, 1.0, N + 1)
|
|
385
433
|
z = np.linspace(0.0, 1.0, N + 1)
|
|
386
434
|
|
|
387
|
-
positions = np.transpose(np.meshgrid(x, y, z, indexing="ij")).reshape(-1, 3)
|
|
435
|
+
positions = np.transpose(np.meshgrid(x, y, z, indexing="ij"), axes=(1, 2, 3, 0)).reshape(-1, 3)
|
|
388
436
|
|
|
389
437
|
vidx = grid_to_hexes(N, N, N)
|
|
390
438
|
|
|
391
439
|
return wp.array(positions, dtype=wp.vec3), wp.array(vidx, dtype=int)
|
|
392
440
|
|
|
393
441
|
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
):
|
|
402
|
-
cell_index, q = wp.tid()
|
|
403
|
-
|
|
404
|
-
coords = qps[q]
|
|
405
|
-
s = make_free_sample(cell_index, coords)
|
|
442
|
+
@fem.integrand(kernel_options={"enable_backward": False})
|
|
443
|
+
def _test_geo_cells(
|
|
444
|
+
s: fem.Sample,
|
|
445
|
+
domain: fem.Domain,
|
|
446
|
+
cell_measures: wp.array(dtype=float),
|
|
447
|
+
):
|
|
448
|
+
wp.atomic_add(cell_measures, s.element_index, fem.measure(domain, s) * s.qp_weight)
|
|
406
449
|
|
|
407
|
-
wp.atomic_add(cell_measures, cell_index, geo.cell_measure(cell_arg, s) * qp_weights[q])
|
|
408
450
|
|
|
409
|
-
|
|
451
|
+
@fem.integrand(kernel_options={"enable_backward": False, "max_unroll": 1})
|
|
452
|
+
def _test_geo_sides(
|
|
453
|
+
s: fem.Sample,
|
|
454
|
+
domain: fem.Domain,
|
|
455
|
+
ref_measure: float,
|
|
456
|
+
side_measures: wp.array(dtype=float),
|
|
457
|
+
):
|
|
458
|
+
side_index = s.element_index
|
|
459
|
+
coords = s.element_coords
|
|
410
460
|
|
|
411
|
-
|
|
412
|
-
def test_geo_sides_kernel(
|
|
413
|
-
side_arg: geo.SideArg,
|
|
414
|
-
qps: wp.array(dtype=Coords),
|
|
415
|
-
qp_weights: wp.array(dtype=float),
|
|
416
|
-
side_measures: wp.array(dtype=float),
|
|
417
|
-
):
|
|
418
|
-
side_index, q = wp.tid()
|
|
461
|
+
cells = fem.cells(domain)
|
|
419
462
|
|
|
420
|
-
|
|
421
|
-
|
|
463
|
+
inner_s = fem.to_inner_cell(domain, s)
|
|
464
|
+
outer_s = fem.to_outer_cell(domain, s)
|
|
422
465
|
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
inner_cell_coords = geo.side_inner_cell_coords(side_arg, side_index, coords)
|
|
427
|
-
outer_cell_coords = geo.side_outer_cell_coords(side_arg, side_index, coords)
|
|
466
|
+
pos_side = domain(s)
|
|
467
|
+
pos_inner = cells(inner_s)
|
|
468
|
+
pos_outer = cells(outer_s)
|
|
428
469
|
|
|
429
|
-
|
|
430
|
-
|
|
470
|
+
for k in range(type(pos_side).length):
|
|
471
|
+
wp.expect_near(pos_side[k], pos_inner[k], 0.0001)
|
|
472
|
+
wp.expect_near(pos_side[k], pos_outer[k], 0.0001)
|
|
431
473
|
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
pos_outer = geo.cell_position(cell_arg, outer_s)
|
|
474
|
+
inner_side_s = fem.to_cell_side(domain, inner_s, side_index)
|
|
475
|
+
outer_side_s = fem.to_cell_side(domain, outer_s, side_index)
|
|
435
476
|
|
|
436
|
-
|
|
437
|
-
|
|
477
|
+
wp.expect_near(coords, inner_side_s.element_coords, 0.0001)
|
|
478
|
+
wp.expect_near(coords, outer_side_s.element_coords, 0.0001)
|
|
438
479
|
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
wp.expect_near(pos_side[k], pos_outer[k], 0.0001)
|
|
480
|
+
area = fem.measure(domain, s)
|
|
481
|
+
wp.atomic_add(side_measures, side_index, area * s.qp_weight)
|
|
442
482
|
|
|
443
|
-
|
|
444
|
-
|
|
483
|
+
F = fem.deformation_gradient(domain, s)
|
|
484
|
+
F_det = fem.Geometry._element_measure(F)
|
|
485
|
+
wp.expect_near(F_det * ref_measure, area)
|
|
445
486
|
|
|
446
|
-
wp.expect_near(coords, inner_side_coords, 0.0001)
|
|
447
|
-
wp.expect_near(coords, outer_side_coords, 0.0001)
|
|
448
487
|
|
|
449
|
-
|
|
450
|
-
|
|
488
|
+
@fem.integrand(kernel_options={"enable_backward": False, "max_unroll": 1})
|
|
489
|
+
def _test_side_normals(
|
|
490
|
+
s: fem.Sample,
|
|
491
|
+
domain: fem.Domain,
|
|
492
|
+
):
|
|
493
|
+
# test consistency of side normal, measure, and deformation gradient
|
|
494
|
+
F = fem.deformation_gradient(domain, s)
|
|
451
495
|
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
F_det = DeformedGeometry._side_measure(F)
|
|
455
|
-
wp.expect_near(F_det * REF_MEASURE, area)
|
|
496
|
+
nor = fem.normal(domain, s)
|
|
497
|
+
F_cross = fem.Geometry._element_normal(F)
|
|
456
498
|
|
|
457
|
-
|
|
458
|
-
F_cross
|
|
499
|
+
for k in range(type(nor).length):
|
|
500
|
+
wp.expect_near(F_cross[k], nor[k], 0.0001)
|
|
459
501
|
|
|
460
|
-
for k in range(type(pos_side).length):
|
|
461
|
-
wp.expect_near(F_cross[k], nor[k], 0.0001)
|
|
462
502
|
|
|
503
|
+
def _launch_test_geometry_kernel(geo: fem.Geometry, device):
|
|
463
504
|
cell_measures = wp.zeros(dtype=float, device=device, shape=geo.cell_count())
|
|
464
|
-
|
|
465
505
|
cell_quadrature = fem.RegularQuadrature(fem.Cells(geo), order=2)
|
|
466
|
-
cell_qps = wp.array(cell_quadrature.points, dtype=Coords, device=device)
|
|
467
|
-
cell_qp_weights = wp.array(cell_quadrature.weights, dtype=float, device=device)
|
|
468
|
-
|
|
469
|
-
wp.launch(
|
|
470
|
-
kernel=test_geo_cells_kernel,
|
|
471
|
-
dim=(geo.cell_count(), cell_qps.shape[0]),
|
|
472
|
-
inputs=[geo.cell_arg_value(device), cell_qps, cell_qp_weights, cell_measures],
|
|
473
|
-
device=device,
|
|
474
|
-
)
|
|
475
506
|
|
|
476
507
|
side_measures = wp.zeros(dtype=float, device=device, shape=geo.side_count())
|
|
477
|
-
|
|
478
508
|
side_quadrature = fem.RegularQuadrature(fem.Sides(geo), order=2)
|
|
479
|
-
side_qps = wp.array(side_quadrature.points, dtype=Coords, device=device)
|
|
480
|
-
side_qp_weights = wp.array(side_quadrature.weights, dtype=float, device=device)
|
|
481
509
|
|
|
482
|
-
wp.
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
510
|
+
with wp.ScopedDevice(device):
|
|
511
|
+
fem.interpolate(
|
|
512
|
+
_test_geo_cells,
|
|
513
|
+
quadrature=cell_quadrature,
|
|
514
|
+
values={"cell_measures": cell_measures},
|
|
515
|
+
)
|
|
516
|
+
fem.interpolate(
|
|
517
|
+
_test_geo_sides,
|
|
518
|
+
quadrature=side_quadrature,
|
|
519
|
+
values={"side_measures": side_measures, "ref_measure": geo.reference_side().measure()},
|
|
520
|
+
)
|
|
521
|
+
|
|
522
|
+
if geo.side_normal is not None:
|
|
523
|
+
fem.interpolate(
|
|
524
|
+
_test_side_normals,
|
|
525
|
+
quadrature=side_quadrature,
|
|
526
|
+
)
|
|
488
527
|
|
|
489
528
|
return side_measures, cell_measures
|
|
490
529
|
|
|
@@ -509,7 +548,7 @@ def test_triangle_mesh(test, device):
|
|
|
509
548
|
N = 3
|
|
510
549
|
|
|
511
550
|
with wp.ScopedDevice(device):
|
|
512
|
-
positions, tri_vidx = _gen_trimesh(N)
|
|
551
|
+
positions, tri_vidx = _gen_trimesh(N, N)
|
|
513
552
|
|
|
514
553
|
geo = fem.Trimesh2D(tri_vertex_indices=tri_vidx, positions=positions)
|
|
515
554
|
|
|
@@ -523,6 +562,24 @@ def test_triangle_mesh(test, device):
|
|
|
523
562
|
assert_np_equal(cell_measures.numpy(), np.full(cell_measures.shape, 0.5 / (N**2)), tol=1.0e-4)
|
|
524
563
|
test.assertAlmostEqual(np.sum(side_measures.numpy()), 2 * (N + 1) + N * math.sqrt(2.0), places=4)
|
|
525
564
|
|
|
565
|
+
# 3d
|
|
566
|
+
|
|
567
|
+
positions = positions.numpy()
|
|
568
|
+
positions = np.hstack((positions, np.ones((positions.shape[0], 1))))
|
|
569
|
+
positions = wp.array(positions, device=device, dtype=wp.vec3)
|
|
570
|
+
|
|
571
|
+
geo = fem.Trimesh3D(tri_vertex_indices=tri_vidx, positions=positions)
|
|
572
|
+
|
|
573
|
+
test.assertEqual(geo.cell_count(), 2 * (N) ** 2)
|
|
574
|
+
test.assertEqual(geo.vertex_count(), (N + 1) ** 2)
|
|
575
|
+
test.assertEqual(geo.side_count(), 2 * (N + 1) * N + (N**2))
|
|
576
|
+
test.assertEqual(geo.boundary_side_count(), 4 * N)
|
|
577
|
+
|
|
578
|
+
side_measures, cell_measures = _launch_test_geometry_kernel(geo, device)
|
|
579
|
+
|
|
580
|
+
assert_np_equal(cell_measures.numpy(), np.full(cell_measures.shape, 0.5 / (N**2)), tol=1.0e-4)
|
|
581
|
+
test.assertAlmostEqual(np.sum(side_measures.numpy()), 2 * (N + 1) + N * math.sqrt(2.0), places=4)
|
|
582
|
+
|
|
526
583
|
|
|
527
584
|
def test_quad_mesh(test, device):
|
|
528
585
|
N = 3
|
|
@@ -542,6 +599,24 @@ def test_quad_mesh(test, device):
|
|
|
542
599
|
assert_np_equal(side_measures.numpy(), np.full(side_measures.shape, 1.0 / (N)), tol=1.0e-4)
|
|
543
600
|
assert_np_equal(cell_measures.numpy(), np.full(cell_measures.shape, 1.0 / (N**2)), tol=1.0e-4)
|
|
544
601
|
|
|
602
|
+
# 3d
|
|
603
|
+
|
|
604
|
+
positions = positions.numpy()
|
|
605
|
+
positions = np.hstack((positions, np.ones((positions.shape[0], 1))))
|
|
606
|
+
positions = wp.array(positions, device=device, dtype=wp.vec3)
|
|
607
|
+
|
|
608
|
+
geo = fem.Quadmesh3D(quad_vertex_indices=quad_vidx, positions=positions)
|
|
609
|
+
|
|
610
|
+
test.assertEqual(geo.cell_count(), N**2)
|
|
611
|
+
test.assertEqual(geo.vertex_count(), (N + 1) ** 2)
|
|
612
|
+
test.assertEqual(geo.side_count(), 2 * (N + 1) * N)
|
|
613
|
+
test.assertEqual(geo.boundary_side_count(), 4 * N)
|
|
614
|
+
|
|
615
|
+
side_measures, cell_measures = _launch_test_geometry_kernel(geo, device)
|
|
616
|
+
|
|
617
|
+
assert_np_equal(side_measures.numpy(), np.full(side_measures.shape, 1.0 / (N)), tol=1.0e-4)
|
|
618
|
+
assert_np_equal(cell_measures.numpy(), np.full(cell_measures.shape, 1.0 / (N**2)), tol=1.0e-4)
|
|
619
|
+
|
|
545
620
|
|
|
546
621
|
def test_grid_3d(test, device):
|
|
547
622
|
N = 3
|
|
@@ -564,7 +639,7 @@ def test_tet_mesh(test, device):
|
|
|
564
639
|
N = 3
|
|
565
640
|
|
|
566
641
|
with wp.ScopedDevice(device):
|
|
567
|
-
positions, tet_vidx = _gen_tetmesh(N)
|
|
642
|
+
positions, tet_vidx = _gen_tetmesh(N, N, N)
|
|
568
643
|
|
|
569
644
|
geo = fem.Tetmesh(tet_vertex_indices=tet_vidx, positions=positions)
|
|
570
645
|
|
|
@@ -692,7 +767,7 @@ def test_deformed_geometry(test, device):
|
|
|
692
767
|
N = 3
|
|
693
768
|
|
|
694
769
|
with wp.ScopedDevice(device):
|
|
695
|
-
positions, tet_vidx = _gen_tetmesh(N)
|
|
770
|
+
positions, tet_vidx = _gen_tetmesh(N, N, N)
|
|
696
771
|
|
|
697
772
|
geo = fem.Tetmesh(tet_vertex_indices=tet_vidx, positions=positions)
|
|
698
773
|
|
|
@@ -935,6 +1010,17 @@ def test_dof_mapper(test, device):
|
|
|
935
1010
|
test.assertAlmostEqual(frob_norm2, 1.0, places=6)
|
|
936
1011
|
|
|
937
1012
|
|
|
1013
|
+
@wp.func
|
|
1014
|
+
def _expect_near(a: Any, b: Any, tol: float):
|
|
1015
|
+
wp.expect_near(a, b, tol)
|
|
1016
|
+
|
|
1017
|
+
|
|
1018
|
+
@wp.func
|
|
1019
|
+
def _expect_near(a: wp.vec2, b: wp.vec2, tol: float):
|
|
1020
|
+
for k in range(2):
|
|
1021
|
+
wp.expect_near(a[k], b[k], tol)
|
|
1022
|
+
|
|
1023
|
+
|
|
938
1024
|
def test_shape_function_weight(test, shape: shape.ShapeFunction, coord_sampler, CENTER_COORDS):
|
|
939
1025
|
NODE_COUNT = shape.NODES_PER_ELEMENT
|
|
940
1026
|
weight_fn = shape.make_element_inner_weight()
|
|
@@ -974,11 +1060,11 @@ def test_shape_function_weight(test, shape: shape.ShapeFunction, coord_sampler,
|
|
|
974
1060
|
coords = coord_sampler(rng_state)
|
|
975
1061
|
|
|
976
1062
|
# sum of node weights anywhere should be 1.0
|
|
977
|
-
w_sum =
|
|
1063
|
+
w_sum = type(weight_fn(coords, 0))(0.0)
|
|
978
1064
|
for n in range(NODE_COUNT):
|
|
979
1065
|
w_sum += weight_fn(coords, n)
|
|
980
1066
|
|
|
981
|
-
wp.
|
|
1067
|
+
_expect_near(wp.abs(w_sum), type(w_sum)(1.0), 0.0001)
|
|
982
1068
|
|
|
983
1069
|
n_samples = 100
|
|
984
1070
|
wp.launch(partition_of_unity_test, dim=n_samples, inputs=[])
|
|
@@ -1011,10 +1097,27 @@ def test_shape_function_trace(test, shape: shape.ShapeFunction, CENTER_COORDS):
|
|
|
1011
1097
|
wp.launch(trace_node_quadrature_unity_test, dim=1, inputs=[])
|
|
1012
1098
|
|
|
1013
1099
|
|
|
1014
|
-
def test_shape_function_gradient(
|
|
1100
|
+
def test_shape_function_gradient(
|
|
1101
|
+
test,
|
|
1102
|
+
shape: shape.ShapeFunction,
|
|
1103
|
+
coord_sampler,
|
|
1104
|
+
coord_delta_sampler,
|
|
1105
|
+
pure_curl: bool = False,
|
|
1106
|
+
pure_spherical: bool = False,
|
|
1107
|
+
):
|
|
1015
1108
|
weight_fn = shape.make_element_inner_weight()
|
|
1016
1109
|
weight_gradient_fn = shape.make_element_inner_weight_gradient()
|
|
1017
1110
|
|
|
1111
|
+
@wp.func
|
|
1112
|
+
def scalar_delta(avg_grad: Any, param_delta: Any):
|
|
1113
|
+
return wp.dot(avg_grad, param_delta)
|
|
1114
|
+
|
|
1115
|
+
@wp.func
|
|
1116
|
+
def vector_delta(avg_grad: Any, param_delta: Any):
|
|
1117
|
+
return avg_grad * param_delta
|
|
1118
|
+
|
|
1119
|
+
grad_delta_fn = scalar_delta if shape.value == shape.Value.Scalar else vector_delta
|
|
1120
|
+
|
|
1018
1121
|
@dynamic_kernel(suffix=shape.name, kernel_options={"enable_backward": False})
|
|
1019
1122
|
def finite_difference_test():
|
|
1020
1123
|
i, n = wp.tid()
|
|
@@ -1034,10 +1137,15 @@ def test_shape_function_gradient(test, shape: shape.ShapeFunction, coord_sampler
|
|
|
1034
1137
|
# 2nd-order finite-difference test
|
|
1035
1138
|
# See Schroeder 2019, Practical course on computing derivatives in code
|
|
1036
1139
|
delta_ref = w_p - w_m
|
|
1037
|
-
delta_est =
|
|
1140
|
+
delta_est = grad_delta_fn(gp + gm, param_delta)
|
|
1141
|
+
_expect_near(delta_ref, delta_est, 0.0001)
|
|
1142
|
+
|
|
1143
|
+
if wp.static(pure_curl):
|
|
1144
|
+
wp.expect_near(wp.ddot(symmetric_part(gp), symmetric_part(gp)), gp.dtype(0.0))
|
|
1038
1145
|
|
|
1039
|
-
|
|
1040
|
-
|
|
1146
|
+
if wp.static(pure_spherical):
|
|
1147
|
+
deviatoric_part = gp - spherical_part(gp)
|
|
1148
|
+
wp.expect_near(wp.ddot(deviatoric_part, deviatoric_part), gp.dtype(0.0))
|
|
1041
1149
|
|
|
1042
1150
|
n_samples = 100
|
|
1043
1151
|
wp.launch(finite_difference_test, dim=(n_samples, shape.NODES_PER_ELEMENT), inputs=[])
|
|
@@ -1102,6 +1210,11 @@ def test_square_shape_functions(test, device):
|
|
|
1102
1210
|
test_shape_function_gradient(test, P_c2, square_coord_sampler, square_coord_delta_sampler)
|
|
1103
1211
|
test_shape_function_gradient(test, P_c3, square_coord_sampler, square_coord_delta_sampler)
|
|
1104
1212
|
|
|
1213
|
+
N1_1 = shape.SquareNedelecFirstKindShapeFunctions(degree=1)
|
|
1214
|
+
test_shape_function_gradient(test, N1_1, square_coord_sampler, square_coord_delta_sampler)
|
|
1215
|
+
RT_1 = shape.SquareRaviartThomasShapeFunctions(degree=1)
|
|
1216
|
+
test_shape_function_gradient(test, RT_1, square_coord_sampler, square_coord_delta_sampler)
|
|
1217
|
+
|
|
1105
1218
|
wp.synchronize()
|
|
1106
1219
|
|
|
1107
1220
|
|
|
@@ -1164,6 +1277,11 @@ def test_cube_shape_functions(test, device):
|
|
|
1164
1277
|
test_shape_function_gradient(test, P_c2, cube_coord_sampler, cube_coord_delta_sampler)
|
|
1165
1278
|
test_shape_function_gradient(test, P_c3, cube_coord_sampler, cube_coord_delta_sampler)
|
|
1166
1279
|
|
|
1280
|
+
N1_1 = shape.CubeNedelecFirstKindShapeFunctions(degree=1)
|
|
1281
|
+
test_shape_function_gradient(test, N1_1, cube_coord_sampler, cube_coord_delta_sampler)
|
|
1282
|
+
RT_1 = shape.CubeRaviartThomasShapeFunctions(degree=1)
|
|
1283
|
+
test_shape_function_gradient(test, RT_1, cube_coord_sampler, cube_coord_delta_sampler)
|
|
1284
|
+
|
|
1167
1285
|
wp.synchronize()
|
|
1168
1286
|
|
|
1169
1287
|
|
|
@@ -1184,9 +1302,9 @@ def test_tri_shape_functions(test, device):
|
|
|
1184
1302
|
b = param_delta[1]
|
|
1185
1303
|
return param_delta, Coords(-a - b, a, b)
|
|
1186
1304
|
|
|
1187
|
-
P_1 = shape.
|
|
1188
|
-
P_2 = shape.
|
|
1189
|
-
P_3 = shape.
|
|
1305
|
+
P_1 = shape.TrianglePolynomialShapeFunctions(degree=1)
|
|
1306
|
+
P_2 = shape.TrianglePolynomialShapeFunctions(degree=2)
|
|
1307
|
+
P_3 = shape.TrianglePolynomialShapeFunctions(degree=3)
|
|
1190
1308
|
|
|
1191
1309
|
test_shape_function_weight(test, P_1, tri_coord_sampler, TRI_CENTER_COORDS)
|
|
1192
1310
|
test_shape_function_weight(test, P_2, tri_coord_sampler, TRI_CENTER_COORDS)
|
|
@@ -1198,9 +1316,9 @@ def test_tri_shape_functions(test, device):
|
|
|
1198
1316
|
test_shape_function_gradient(test, P_2, tri_coord_sampler, tri_coord_delta_sampler)
|
|
1199
1317
|
test_shape_function_gradient(test, P_3, tri_coord_sampler, tri_coord_delta_sampler)
|
|
1200
1318
|
|
|
1201
|
-
P_1d = shape.
|
|
1202
|
-
P_2d = shape.
|
|
1203
|
-
P_3d = shape.
|
|
1319
|
+
P_1d = shape.TriangleNonConformingPolynomialShapeFunctions(degree=1)
|
|
1320
|
+
P_2d = shape.TriangleNonConformingPolynomialShapeFunctions(degree=2)
|
|
1321
|
+
P_3d = shape.TriangleNonConformingPolynomialShapeFunctions(degree=3)
|
|
1204
1322
|
|
|
1205
1323
|
test_shape_function_weight(test, P_1d, tri_coord_sampler, TRI_CENTER_COORDS)
|
|
1206
1324
|
test_shape_function_weight(test, P_2d, tri_coord_sampler, TRI_CENTER_COORDS)
|
|
@@ -1209,6 +1327,12 @@ def test_tri_shape_functions(test, device):
|
|
|
1209
1327
|
test_shape_function_gradient(test, P_2d, tri_coord_sampler, tri_coord_delta_sampler)
|
|
1210
1328
|
test_shape_function_gradient(test, P_3d, tri_coord_sampler, tri_coord_delta_sampler)
|
|
1211
1329
|
|
|
1330
|
+
N1_1 = shape.TriangleNedelecFirstKindShapeFunctions(degree=1)
|
|
1331
|
+
test_shape_function_gradient(test, N1_1, tri_coord_sampler, tri_coord_delta_sampler, pure_curl=True)
|
|
1332
|
+
|
|
1333
|
+
RT_1 = shape.TriangleNedelecFirstKindShapeFunctions(degree=1)
|
|
1334
|
+
test_shape_function_gradient(test, RT_1, tri_coord_sampler, tri_coord_delta_sampler, pure_spherical=True)
|
|
1335
|
+
|
|
1212
1336
|
wp.synchronize()
|
|
1213
1337
|
|
|
1214
1338
|
|
|
@@ -1250,6 +1374,12 @@ def test_tet_shape_functions(test, device):
|
|
|
1250
1374
|
test_shape_function_gradient(test, P_2d, tet_coord_sampler, tet_coord_delta_sampler)
|
|
1251
1375
|
test_shape_function_gradient(test, P_3d, tet_coord_sampler, tet_coord_delta_sampler)
|
|
1252
1376
|
|
|
1377
|
+
N1_1 = shape.TetrahedronNedelecFirstKindShapeFunctions(degree=1)
|
|
1378
|
+
test_shape_function_gradient(test, N1_1, tet_coord_sampler, tet_coord_delta_sampler, pure_curl=True)
|
|
1379
|
+
|
|
1380
|
+
RT_1 = shape.TetrahedronRaviartThomasShapeFunctions(degree=1)
|
|
1381
|
+
test_shape_function_gradient(test, RT_1, tet_coord_sampler, tet_coord_delta_sampler, pure_spherical=True)
|
|
1382
|
+
|
|
1253
1383
|
wp.synchronize()
|
|
1254
1384
|
|
|
1255
1385
|
|
|
@@ -1508,6 +1638,186 @@ def test_implicit_fields(test, device):
|
|
|
1508
1638
|
assert_np_equal(discrete_field2.dof_values.numpy(), np.array([2.0] + [5.0] * 3))
|
|
1509
1639
|
|
|
1510
1640
|
|
|
1641
|
+
@fem.integrand
|
|
1642
|
+
def _expect_pure_curl(s: fem.Sample, field: fem.Field):
|
|
1643
|
+
sym_grad = fem.D(field, s)
|
|
1644
|
+
wp.expect_near(wp.ddot(sym_grad, sym_grad), 0.0)
|
|
1645
|
+
return 0.0
|
|
1646
|
+
|
|
1647
|
+
|
|
1648
|
+
@fem.integrand
|
|
1649
|
+
def _expect_pure_spherical(s: fem.Sample, field: fem.Field):
|
|
1650
|
+
grad = fem.grad(field, s)
|
|
1651
|
+
deviatoric_part = grad - spherical_part(grad)
|
|
1652
|
+
wp.expect_near(wp.ddot(deviatoric_part, deviatoric_part), 0.0)
|
|
1653
|
+
return 0.0
|
|
1654
|
+
|
|
1655
|
+
|
|
1656
|
+
@fem.integrand
|
|
1657
|
+
def _expect_normal_continuity(s: fem.Sample, domain: fem.Domain, field: fem.Field):
|
|
1658
|
+
nor = fem.normal(domain, s)
|
|
1659
|
+
wp.expect_near(wp.dot(fem.inner(field, s), nor), wp.dot(fem.outer(field, s), nor), 0.0001)
|
|
1660
|
+
return 0.0
|
|
1661
|
+
|
|
1662
|
+
|
|
1663
|
+
@fem.integrand
|
|
1664
|
+
def _expect_tangential_continuity(s: fem.Sample, domain: fem.Domain, field: fem.Field):
|
|
1665
|
+
nor = fem.normal(domain, s)
|
|
1666
|
+
in_s = fem.inner(field, s)
|
|
1667
|
+
out_s = fem.outer(field, s)
|
|
1668
|
+
in_t = in_s - wp.dot(in_s, nor) * nor
|
|
1669
|
+
out_t = out_s - wp.dot(out_s, nor) * nor
|
|
1670
|
+
|
|
1671
|
+
_expect_near(in_t, out_t, 0.0001)
|
|
1672
|
+
return 0.0
|
|
1673
|
+
|
|
1674
|
+
|
|
1675
|
+
def test_vector_spaces(test, device):
|
|
1676
|
+
# Test covariant / contravariant mappings
|
|
1677
|
+
|
|
1678
|
+
with wp.ScopedDevice(device):
|
|
1679
|
+
positions, hex_vidx = _gen_quadmesh(3)
|
|
1680
|
+
|
|
1681
|
+
geo = fem.Quadmesh2D(quad_vertex_indices=hex_vidx, positions=positions)
|
|
1682
|
+
|
|
1683
|
+
curl_space = fem.make_polynomial_space(geo, element_basis=fem.ElementBasis.NEDELEC_FIRST_KIND)
|
|
1684
|
+
curl_test = fem.make_test(curl_space)
|
|
1685
|
+
|
|
1686
|
+
curl_field = curl_space.make_field()
|
|
1687
|
+
curl_field.dof_values = wp.array(np.linspace(0.0, 1.0, curl_space.node_count()), dtype=float)
|
|
1688
|
+
|
|
1689
|
+
fem.interpolate(
|
|
1690
|
+
_expect_tangential_continuity,
|
|
1691
|
+
quadrature=fem.RegularQuadrature(fem.Sides(geo), order=2),
|
|
1692
|
+
fields={"field": curl_field.trace()},
|
|
1693
|
+
)
|
|
1694
|
+
|
|
1695
|
+
div_space = fem.make_polynomial_space(geo, element_basis=fem.ElementBasis.RAVIART_THOMAS)
|
|
1696
|
+
div_test = fem.make_test(div_space)
|
|
1697
|
+
|
|
1698
|
+
div_field = div_space.make_field()
|
|
1699
|
+
div_field.dof_values = wp.array(np.linspace(0.0, 1.0, div_space.node_count()), dtype=float)
|
|
1700
|
+
|
|
1701
|
+
fem.interpolate(
|
|
1702
|
+
_expect_normal_continuity,
|
|
1703
|
+
quadrature=fem.RegularQuadrature(fem.Sides(geo), order=2),
|
|
1704
|
+
fields={"field": div_field.trace()},
|
|
1705
|
+
)
|
|
1706
|
+
|
|
1707
|
+
with wp.ScopedDevice(device):
|
|
1708
|
+
positions, hex_vidx = _gen_hexmesh(3)
|
|
1709
|
+
|
|
1710
|
+
geo = fem.Hexmesh(hex_vertex_indices=hex_vidx, positions=positions)
|
|
1711
|
+
|
|
1712
|
+
curl_space = fem.make_polynomial_space(geo, element_basis=fem.ElementBasis.NEDELEC_FIRST_KIND)
|
|
1713
|
+
curl_test = fem.make_test(curl_space)
|
|
1714
|
+
|
|
1715
|
+
curl_field = curl_space.make_field()
|
|
1716
|
+
curl_field.dof_values = wp.array(np.linspace(0.0, 1.0, curl_space.node_count()), dtype=float)
|
|
1717
|
+
|
|
1718
|
+
fem.interpolate(
|
|
1719
|
+
_expect_tangential_continuity,
|
|
1720
|
+
quadrature=fem.RegularQuadrature(fem.Sides(geo), order=2),
|
|
1721
|
+
fields={"field": curl_field.trace()},
|
|
1722
|
+
)
|
|
1723
|
+
|
|
1724
|
+
div_space = fem.make_polynomial_space(geo, element_basis=fem.ElementBasis.RAVIART_THOMAS)
|
|
1725
|
+
div_test = fem.make_test(div_space)
|
|
1726
|
+
|
|
1727
|
+
div_field = div_space.make_field()
|
|
1728
|
+
div_field.dof_values = wp.array(np.linspace(0.0, 1.0, div_space.node_count()), dtype=float)
|
|
1729
|
+
|
|
1730
|
+
fem.interpolate(
|
|
1731
|
+
_expect_normal_continuity,
|
|
1732
|
+
quadrature=fem.RegularQuadrature(fem.Sides(geo), order=2),
|
|
1733
|
+
fields={"field": div_field.trace()},
|
|
1734
|
+
)
|
|
1735
|
+
|
|
1736
|
+
return
|
|
1737
|
+
|
|
1738
|
+
with wp.ScopedDevice(device):
|
|
1739
|
+
positions, tri_vidx = _gen_trimesh(3, 5)
|
|
1740
|
+
|
|
1741
|
+
geo = fem.Trimesh2D(tri_vertex_indices=tri_vidx, positions=positions)
|
|
1742
|
+
|
|
1743
|
+
curl_space = fem.make_polynomial_space(geo, element_basis=fem.ElementBasis.NEDELEC_FIRST_KIND)
|
|
1744
|
+
curl_test = fem.make_test(curl_space)
|
|
1745
|
+
|
|
1746
|
+
fem.integrate(_expect_pure_curl, fields={"field": curl_test}, assembly="generic")
|
|
1747
|
+
|
|
1748
|
+
curl_field = curl_space.make_field()
|
|
1749
|
+
curl_field.dof_values.fill_(1.0)
|
|
1750
|
+
fem.interpolate(
|
|
1751
|
+
_expect_pure_curl, quadrature=fem.RegularQuadrature(fem.Cells(geo), order=2), fields={"field": curl_field}
|
|
1752
|
+
)
|
|
1753
|
+
|
|
1754
|
+
fem.interpolate(
|
|
1755
|
+
_expect_tangential_continuity,
|
|
1756
|
+
quadrature=fem.RegularQuadrature(fem.Sides(geo), order=2),
|
|
1757
|
+
fields={"field": curl_field.trace()},
|
|
1758
|
+
)
|
|
1759
|
+
|
|
1760
|
+
div_space = fem.make_polynomial_space(geo, element_basis=fem.ElementBasis.RAVIART_THOMAS)
|
|
1761
|
+
div_test = fem.make_test(div_space)
|
|
1762
|
+
|
|
1763
|
+
fem.integrate(_expect_pure_spherical, fields={"field": div_test}, assembly="generic")
|
|
1764
|
+
|
|
1765
|
+
div_field = div_space.make_field()
|
|
1766
|
+
div_field.dof_values.fill_(1.0)
|
|
1767
|
+
fem.interpolate(
|
|
1768
|
+
_expect_pure_spherical,
|
|
1769
|
+
quadrature=fem.RegularQuadrature(fem.Cells(geo), order=2),
|
|
1770
|
+
fields={"field": div_field},
|
|
1771
|
+
)
|
|
1772
|
+
|
|
1773
|
+
fem.interpolate(
|
|
1774
|
+
_expect_normal_continuity,
|
|
1775
|
+
quadrature=fem.RegularQuadrature(fem.Sides(geo), order=2),
|
|
1776
|
+
fields={"field": div_field.trace()},
|
|
1777
|
+
)
|
|
1778
|
+
|
|
1779
|
+
with wp.ScopedDevice(device):
|
|
1780
|
+
positions, tet_vidx = _gen_tetmesh(3, 5, 7)
|
|
1781
|
+
|
|
1782
|
+
geo = fem.Tetmesh(tet_vertex_indices=tet_vidx, positions=positions)
|
|
1783
|
+
|
|
1784
|
+
curl_space = fem.make_polynomial_space(geo, element_basis=fem.ElementBasis.NEDELEC_FIRST_KIND)
|
|
1785
|
+
curl_test = fem.make_test(curl_space)
|
|
1786
|
+
|
|
1787
|
+
fem.integrate(_expect_pure_curl, fields={"field": curl_test}, assembly="generic")
|
|
1788
|
+
|
|
1789
|
+
curl_field = curl_space.make_field()
|
|
1790
|
+
curl_field.dof_values.fill_(1.0)
|
|
1791
|
+
fem.interpolate(
|
|
1792
|
+
_expect_pure_curl, quadrature=fem.RegularQuadrature(fem.Cells(geo), order=2), fields={"field": curl_field}
|
|
1793
|
+
)
|
|
1794
|
+
|
|
1795
|
+
fem.interpolate(
|
|
1796
|
+
_expect_tangential_continuity,
|
|
1797
|
+
quadrature=fem.RegularQuadrature(fem.Sides(geo), order=1),
|
|
1798
|
+
fields={"field": curl_field.trace()},
|
|
1799
|
+
)
|
|
1800
|
+
|
|
1801
|
+
div_space = fem.make_polynomial_space(geo, element_basis=fem.ElementBasis.RAVIART_THOMAS)
|
|
1802
|
+
div_test = fem.make_test(div_space)
|
|
1803
|
+
|
|
1804
|
+
fem.integrate(_expect_pure_spherical, fields={"field": div_test}, assembly="generic")
|
|
1805
|
+
|
|
1806
|
+
div_field = div_space.make_field()
|
|
1807
|
+
div_field.dof_values.fill_(1.0)
|
|
1808
|
+
fem.interpolate(
|
|
1809
|
+
_expect_pure_spherical,
|
|
1810
|
+
quadrature=fem.RegularQuadrature(fem.Cells(geo), order=2),
|
|
1811
|
+
fields={"field": div_field},
|
|
1812
|
+
)
|
|
1813
|
+
|
|
1814
|
+
fem.interpolate(
|
|
1815
|
+
_expect_normal_continuity,
|
|
1816
|
+
quadrature=fem.RegularQuadrature(fem.Sides(geo), order=0),
|
|
1817
|
+
fields={"field": div_field.trace()},
|
|
1818
|
+
)
|
|
1819
|
+
|
|
1820
|
+
|
|
1511
1821
|
@wp.kernel
|
|
1512
1822
|
def test_qr_eigenvalues():
|
|
1513
1823
|
tol = 1.0e-8
|
|
@@ -1588,6 +1898,28 @@ def test_qr_inverse():
|
|
|
1588
1898
|
wp.expect_near(wp.ddot(Err, Err), 0.0, tol)
|
|
1589
1899
|
|
|
1590
1900
|
|
|
1901
|
+
def test_array_axpy(test, device):
|
|
1902
|
+
N = 10
|
|
1903
|
+
alpha = 0.5
|
|
1904
|
+
beta = 4.0
|
|
1905
|
+
|
|
1906
|
+
x = wp.full(N, 2.0, device=device, dtype=float, requires_grad=True)
|
|
1907
|
+
y = wp.array(np.arange(N), device=device, dtype=wp.float64, requires_grad=True)
|
|
1908
|
+
|
|
1909
|
+
tape = wp.Tape()
|
|
1910
|
+
with tape:
|
|
1911
|
+
fem.utils.array_axpy(x=x, y=y, alpha=alpha, beta=beta)
|
|
1912
|
+
|
|
1913
|
+
assert_np_equal(x.numpy(), np.full(N, 2.0))
|
|
1914
|
+
assert_np_equal(y.numpy(), alpha * x.numpy() + beta * np.arange(N))
|
|
1915
|
+
|
|
1916
|
+
y.grad.fill_(1.0)
|
|
1917
|
+
tape.backward()
|
|
1918
|
+
|
|
1919
|
+
assert_np_equal(x.grad.numpy(), alpha * np.ones(N))
|
|
1920
|
+
assert_np_equal(y.grad.numpy(), beta * np.ones(N))
|
|
1921
|
+
|
|
1922
|
+
|
|
1591
1923
|
devices = get_test_devices()
|
|
1592
1924
|
cuda_devices = get_selected_cuda_test_devices()
|
|
1593
1925
|
|
|
@@ -1612,13 +1944,21 @@ add_function_test(TestFem, "test_hex_mesh", test_hex_mesh, devices=devices)
|
|
|
1612
1944
|
add_function_test(TestFem, "test_nanogrid", test_nanogrid, devices=cuda_devices)
|
|
1613
1945
|
add_function_test(TestFem, "test_adaptive_nanogrid", test_adaptive_nanogrid, devices=cuda_devices)
|
|
1614
1946
|
add_function_test(TestFem, "test_deformed_geometry", test_deformed_geometry, devices=devices)
|
|
1947
|
+
add_function_test(TestFem, "test_vector_spaces", test_vector_spaces, devices=devices)
|
|
1615
1948
|
add_function_test(TestFem, "test_dof_mapper", test_dof_mapper)
|
|
1616
1949
|
add_function_test(TestFem, "test_point_basis", test_point_basis)
|
|
1617
1950
|
add_function_test(TestFem, "test_particle_quadratures", test_particle_quadratures)
|
|
1618
1951
|
add_function_test(TestFem, "test_nodal_quadrature", test_nodal_quadrature)
|
|
1619
1952
|
add_function_test(TestFem, "test_implicit_fields", test_implicit_fields)
|
|
1620
|
-
|
|
1621
|
-
|
|
1953
|
+
|
|
1954
|
+
|
|
1955
|
+
class TestFemUtilities(unittest.TestCase):
|
|
1956
|
+
pass
|
|
1957
|
+
|
|
1958
|
+
|
|
1959
|
+
add_kernel_test(TestFemUtilities, test_qr_eigenvalues, dim=1, devices=devices)
|
|
1960
|
+
add_kernel_test(TestFemUtilities, test_qr_inverse, dim=100, devices=devices)
|
|
1961
|
+
add_function_test(TestFemUtilities, "test_array_axpy", test_array_axpy)
|
|
1622
1962
|
|
|
1623
1963
|
|
|
1624
1964
|
class TestFemShapeFunctions(unittest.TestCase):
|