warp-lang 1.8.0__py3-none-win_amd64.whl → 1.9.0__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 +282 -103
- warp/__init__.pyi +482 -110
- warp/bin/warp-clang.dll +0 -0
- warp/bin/warp.dll +0 -0
- warp/build.py +93 -30
- warp/build_dll.py +48 -63
- warp/builtins.py +955 -137
- warp/codegen.py +327 -209
- warp/config.py +1 -1
- warp/context.py +1363 -800
- warp/examples/core/example_marching_cubes.py +1 -0
- warp/examples/core/example_render_opengl.py +100 -3
- warp/examples/fem/example_apic_fluid.py +98 -52
- warp/examples/fem/example_convection_diffusion_dg.py +25 -4
- warp/examples/fem/example_diffusion_mgpu.py +8 -3
- warp/examples/fem/utils.py +68 -22
- warp/examples/interop/example_jax_callable.py +34 -4
- warp/examples/interop/example_jax_kernel.py +27 -1
- warp/fabric.py +1 -1
- warp/fem/cache.py +27 -19
- warp/fem/domain.py +2 -2
- warp/fem/field/nodal_field.py +2 -2
- warp/fem/field/virtual.py +266 -166
- warp/fem/geometry/geometry.py +5 -5
- warp/fem/integrate.py +200 -91
- warp/fem/space/restriction.py +4 -0
- warp/fem/space/shape/tet_shape_function.py +3 -10
- warp/jax_experimental/custom_call.py +1 -1
- warp/jax_experimental/ffi.py +203 -54
- warp/marching_cubes.py +708 -0
- warp/native/array.h +103 -8
- warp/native/builtin.h +90 -9
- warp/native/bvh.cpp +64 -28
- warp/native/bvh.cu +58 -58
- warp/native/bvh.h +2 -2
- warp/native/clang/clang.cpp +7 -7
- warp/native/coloring.cpp +13 -3
- warp/native/crt.cpp +2 -2
- warp/native/crt.h +3 -5
- warp/native/cuda_util.cpp +42 -11
- warp/native/cuda_util.h +10 -4
- warp/native/exports.h +1842 -1908
- warp/native/fabric.h +2 -1
- warp/native/hashgrid.cpp +37 -37
- warp/native/hashgrid.cu +2 -2
- warp/native/initializer_array.h +1 -1
- warp/native/intersect.h +4 -4
- warp/native/mat.h +1913 -119
- warp/native/mathdx.cpp +43 -43
- warp/native/mesh.cpp +24 -24
- warp/native/mesh.cu +26 -26
- warp/native/mesh.h +5 -3
- warp/native/nanovdb/GridHandle.h +179 -12
- warp/native/nanovdb/HostBuffer.h +8 -7
- warp/native/nanovdb/NanoVDB.h +517 -895
- warp/native/nanovdb/NodeManager.h +323 -0
- warp/native/nanovdb/PNanoVDB.h +2 -2
- warp/native/quat.h +337 -16
- warp/native/rand.h +7 -7
- warp/native/range.h +7 -1
- warp/native/reduce.cpp +10 -10
- warp/native/reduce.cu +13 -14
- warp/native/runlength_encode.cpp +2 -2
- warp/native/runlength_encode.cu +5 -5
- warp/native/scan.cpp +3 -3
- warp/native/scan.cu +4 -4
- warp/native/sort.cpp +10 -10
- warp/native/sort.cu +22 -22
- warp/native/sparse.cpp +8 -8
- warp/native/sparse.cu +14 -14
- warp/native/spatial.h +366 -17
- warp/native/svd.h +23 -8
- warp/native/temp_buffer.h +2 -2
- warp/native/tile.h +303 -70
- warp/native/tile_radix_sort.h +5 -1
- warp/native/tile_reduce.h +16 -25
- warp/native/tuple.h +2 -2
- warp/native/vec.h +385 -18
- warp/native/volume.cpp +54 -54
- warp/native/volume.cu +1 -1
- warp/native/volume.h +2 -1
- warp/native/volume_builder.cu +30 -37
- warp/native/warp.cpp +150 -149
- warp/native/warp.cu +337 -193
- warp/native/warp.h +227 -226
- warp/optim/linear.py +736 -271
- warp/render/imgui_manager.py +289 -0
- warp/render/render_opengl.py +137 -57
- warp/render/render_usd.py +0 -1
- warp/sim/collide.py +1 -2
- warp/sim/graph_coloring.py +2 -2
- warp/sim/integrator_vbd.py +10 -2
- warp/sparse.py +559 -176
- warp/tape.py +2 -0
- warp/tests/aux_test_module_aot.py +7 -0
- warp/tests/cuda/test_async.py +3 -3
- warp/tests/cuda/test_conditional_captures.py +101 -0
- warp/tests/geometry/test_marching_cubes.py +233 -12
- warp/tests/sim/test_cloth.py +89 -6
- warp/tests/sim/test_coloring.py +82 -7
- warp/tests/test_array.py +56 -5
- warp/tests/test_assert.py +53 -0
- warp/tests/test_atomic_cas.py +127 -114
- warp/tests/test_codegen.py +3 -2
- warp/tests/test_context.py +8 -15
- warp/tests/test_enum.py +136 -0
- warp/tests/test_examples.py +2 -2
- warp/tests/test_fem.py +45 -2
- warp/tests/test_fixedarray.py +229 -0
- warp/tests/test_func.py +18 -15
- warp/tests/test_future_annotations.py +7 -5
- warp/tests/test_linear_solvers.py +30 -0
- warp/tests/test_map.py +1 -1
- warp/tests/test_mat.py +1540 -378
- warp/tests/test_mat_assign_copy.py +178 -0
- warp/tests/test_mat_constructors.py +574 -0
- warp/tests/test_module_aot.py +287 -0
- warp/tests/test_print.py +69 -0
- warp/tests/test_quat.py +162 -34
- warp/tests/test_quat_assign_copy.py +145 -0
- warp/tests/test_reload.py +2 -1
- warp/tests/test_sparse.py +103 -0
- warp/tests/test_spatial.py +140 -34
- warp/tests/test_spatial_assign_copy.py +160 -0
- warp/tests/test_static.py +48 -0
- warp/tests/test_struct.py +43 -3
- warp/tests/test_tape.py +38 -0
- warp/tests/test_types.py +0 -20
- warp/tests/test_vec.py +216 -441
- warp/tests/test_vec_assign_copy.py +143 -0
- warp/tests/test_vec_constructors.py +325 -0
- warp/tests/tile/test_tile.py +206 -152
- warp/tests/tile/test_tile_cholesky.py +605 -0
- warp/tests/tile/test_tile_load.py +169 -0
- warp/tests/tile/test_tile_mathdx.py +2 -558
- warp/tests/tile/test_tile_matmul.py +179 -0
- warp/tests/tile/test_tile_mlp.py +1 -1
- warp/tests/tile/test_tile_reduce.py +100 -11
- warp/tests/tile/test_tile_shared_memory.py +16 -16
- warp/tests/tile/test_tile_sort.py +59 -55
- warp/tests/unittest_suites.py +16 -0
- warp/tests/walkthrough_debug.py +1 -1
- warp/thirdparty/unittest_parallel.py +108 -9
- warp/types.py +554 -264
- warp/utils.py +68 -86
- {warp_lang-1.8.0.dist-info → warp_lang-1.9.0.dist-info}/METADATA +28 -65
- {warp_lang-1.8.0.dist-info → warp_lang-1.9.0.dist-info}/RECORD +150 -138
- warp/native/marching.cpp +0 -19
- warp/native/marching.cu +0 -514
- warp/native/marching.h +0 -19
- {warp_lang-1.8.0.dist-info → warp_lang-1.9.0.dist-info}/WHEEL +0 -0
- {warp_lang-1.8.0.dist-info → warp_lang-1.9.0.dist-info}/licenses/LICENSE.md +0 -0
- {warp_lang-1.8.0.dist-info → warp_lang-1.9.0.dist-info}/top_level.txt +0 -0
warp/tape.py
CHANGED
warp/tests/cuda/test_async.py
CHANGED
|
@@ -426,13 +426,13 @@ def copy_template(test, src_ctor, dst_ctor, src_device, dst_device, n, params: C
|
|
|
426
426
|
if expected_error_type is not None:
|
|
427
427
|
# disable error output from Warp if we expect an exception
|
|
428
428
|
try:
|
|
429
|
-
saved_error_output_enabled = wp.context.runtime.core.
|
|
430
|
-
wp.context.runtime.core.
|
|
429
|
+
saved_error_output_enabled = wp.context.runtime.core.wp_is_error_output_enabled()
|
|
430
|
+
wp.context.runtime.core.wp_set_error_output_enabled(False)
|
|
431
431
|
with test.assertRaisesRegex(expected_error_type, expected_error_regex):
|
|
432
432
|
with Capturable(use_graph=params.use_graph, stream=stream):
|
|
433
433
|
wp.copy(dst, src, stream=stream_arg)
|
|
434
434
|
finally:
|
|
435
|
-
wp.context.runtime.core.
|
|
435
|
+
wp.context.runtime.core.wp_set_error_output_enabled(saved_error_output_enabled)
|
|
436
436
|
wp.synchronize()
|
|
437
437
|
|
|
438
438
|
# print(f"SUCCESSFUL ERROR PREDICTION: {expected_error_regex}")
|
|
@@ -979,6 +979,94 @@ def test_graph_debug_dot_print(test, device):
|
|
|
979
979
|
os.remove(dot_file)
|
|
980
980
|
|
|
981
981
|
|
|
982
|
+
# ================================================================================================================
|
|
983
|
+
# test exceptions
|
|
984
|
+
# ================================================================================================================
|
|
985
|
+
|
|
986
|
+
|
|
987
|
+
# body graphs with allocations are not supported
|
|
988
|
+
def body_with_alloc():
|
|
989
|
+
wp.zeros(10)
|
|
990
|
+
|
|
991
|
+
|
|
992
|
+
@unittest.skipUnless(check_conditional_graph_support(), "Conditional graph nodes not supported")
|
|
993
|
+
def test_error_alloc_if(test, device):
|
|
994
|
+
with wp.ScopedDevice(device):
|
|
995
|
+
cond = wp.ones(1, dtype=wp.int32)
|
|
996
|
+
with test.assertRaisesRegex(
|
|
997
|
+
RuntimeError, r"Conditional body graph contains an unsupported operation \(memory allocation\)"
|
|
998
|
+
):
|
|
999
|
+
with wp.ScopedCapture():
|
|
1000
|
+
wp.capture_if(condition=cond, on_true=body_with_alloc)
|
|
1001
|
+
|
|
1002
|
+
|
|
1003
|
+
@unittest.skipUnless(check_conditional_graph_support(), "Conditional graph nodes not supported")
|
|
1004
|
+
def test_error_alloc_else(test, device):
|
|
1005
|
+
with wp.ScopedDevice(device):
|
|
1006
|
+
cond = wp.ones(1, dtype=wp.int32)
|
|
1007
|
+
with test.assertRaisesRegex(
|
|
1008
|
+
RuntimeError, r"Conditional body graph contains an unsupported operation \(memory allocation\)"
|
|
1009
|
+
):
|
|
1010
|
+
with wp.ScopedCapture():
|
|
1011
|
+
wp.capture_if(condition=cond, on_false=body_with_alloc)
|
|
1012
|
+
|
|
1013
|
+
|
|
1014
|
+
@unittest.skipUnless(check_conditional_graph_support(), "Conditional graph nodes not supported")
|
|
1015
|
+
def test_error_alloc_while(test, device):
|
|
1016
|
+
with wp.ScopedDevice(device):
|
|
1017
|
+
cond = wp.ones(1, dtype=wp.int32)
|
|
1018
|
+
with test.assertRaisesRegex(
|
|
1019
|
+
RuntimeError, r"Conditional body graph contains an unsupported operation \(memory allocation\)"
|
|
1020
|
+
):
|
|
1021
|
+
with wp.ScopedCapture():
|
|
1022
|
+
wp.capture_while(condition=cond, while_body=body_with_alloc)
|
|
1023
|
+
|
|
1024
|
+
|
|
1025
|
+
@unittest.skipUnless(check_conditional_graph_support(), "Conditional graph nodes not supported")
|
|
1026
|
+
def test_error_alloc_if_subgraph(test, device):
|
|
1027
|
+
with wp.ScopedDevice(device):
|
|
1028
|
+
# capture body subgraph
|
|
1029
|
+
with wp.ScopedCapture() as body_capture:
|
|
1030
|
+
body_with_alloc()
|
|
1031
|
+
|
|
1032
|
+
cond = wp.ones(1, dtype=wp.int32)
|
|
1033
|
+
with test.assertRaisesRegex(
|
|
1034
|
+
RuntimeError, r"Child graph contains an unsupported operation \(memory allocation\)"
|
|
1035
|
+
):
|
|
1036
|
+
with wp.ScopedCapture():
|
|
1037
|
+
wp.capture_if(condition=cond, on_true=body_capture.graph)
|
|
1038
|
+
|
|
1039
|
+
|
|
1040
|
+
@unittest.skipUnless(check_conditional_graph_support(), "Conditional graph nodes not supported")
|
|
1041
|
+
def test_error_alloc_else_subgraph(test, device):
|
|
1042
|
+
with wp.ScopedDevice(device):
|
|
1043
|
+
# capture body subgraph
|
|
1044
|
+
with wp.ScopedCapture() as body_capture:
|
|
1045
|
+
body_with_alloc()
|
|
1046
|
+
|
|
1047
|
+
cond = wp.ones(1, dtype=wp.int32)
|
|
1048
|
+
with test.assertRaisesRegex(
|
|
1049
|
+
RuntimeError, r"Child graph contains an unsupported operation \(memory allocation\)"
|
|
1050
|
+
):
|
|
1051
|
+
with wp.ScopedCapture():
|
|
1052
|
+
wp.capture_if(condition=cond, on_false=body_capture.graph)
|
|
1053
|
+
|
|
1054
|
+
|
|
1055
|
+
@unittest.skipUnless(check_conditional_graph_support(), "Conditional graph nodes not supported")
|
|
1056
|
+
def test_error_alloc_while_subgraph(test, device):
|
|
1057
|
+
with wp.ScopedDevice(device):
|
|
1058
|
+
# capture body subgraph
|
|
1059
|
+
with wp.ScopedCapture() as body_capture:
|
|
1060
|
+
body_with_alloc()
|
|
1061
|
+
|
|
1062
|
+
cond = wp.ones(1, dtype=wp.int32)
|
|
1063
|
+
with test.assertRaisesRegex(
|
|
1064
|
+
RuntimeError, r"Child graph contains an unsupported operation \(memory allocation\)"
|
|
1065
|
+
):
|
|
1066
|
+
with wp.ScopedCapture():
|
|
1067
|
+
wp.capture_while(condition=cond, while_body=body_capture.graph)
|
|
1068
|
+
|
|
1069
|
+
|
|
982
1070
|
devices = get_test_devices()
|
|
983
1071
|
cuda_devices = get_cuda_test_devices()
|
|
984
1072
|
|
|
@@ -1040,6 +1128,19 @@ add_function_test(
|
|
|
1040
1128
|
TestConditionalCaptures, "test_graph_debug_dot_print", test_graph_debug_dot_print, devices=cuda_devices
|
|
1041
1129
|
)
|
|
1042
1130
|
|
|
1131
|
+
add_function_test(TestConditionalCaptures, "test_error_alloc_if", test_error_alloc_if, devices=cuda_devices)
|
|
1132
|
+
add_function_test(TestConditionalCaptures, "test_error_alloc_else", test_error_alloc_else, devices=cuda_devices)
|
|
1133
|
+
add_function_test(TestConditionalCaptures, "test_error_alloc_while", test_error_alloc_while, devices=cuda_devices)
|
|
1134
|
+
add_function_test(
|
|
1135
|
+
TestConditionalCaptures, "test_error_alloc_if_subgraph", test_error_alloc_if_subgraph, devices=cuda_devices
|
|
1136
|
+
)
|
|
1137
|
+
add_function_test(
|
|
1138
|
+
TestConditionalCaptures, "test_error_alloc_else_subgraph", test_error_alloc_else_subgraph, devices=cuda_devices
|
|
1139
|
+
)
|
|
1140
|
+
add_function_test(
|
|
1141
|
+
TestConditionalCaptures, "test_error_alloc_while_subgraph", test_error_alloc_while_subgraph, devices=cuda_devices
|
|
1142
|
+
)
|
|
1143
|
+
|
|
1043
1144
|
|
|
1044
1145
|
if __name__ == "__main__":
|
|
1045
1146
|
wp.clear_kernel_cache()
|
|
@@ -22,7 +22,9 @@ from warp.tests.unittest_utils import *
|
|
|
22
22
|
|
|
23
23
|
|
|
24
24
|
@wp.kernel
|
|
25
|
-
def
|
|
25
|
+
def make_field_sphere_sdf(field: wp.array3d(dtype=float), center: wp.vec3, radius: float):
|
|
26
|
+
"""Make a sphere SDF for nodes on the integer domain with node coordinates 0,1,2,3,..."""
|
|
27
|
+
|
|
26
28
|
i, j, k = wp.tid()
|
|
27
29
|
|
|
28
30
|
p = wp.vec3(float(i), float(j), float(k))
|
|
@@ -32,31 +34,242 @@ def make_field(field: wp.array3d(dtype=float), center: wp.vec3, radius: float):
|
|
|
32
34
|
field[i, j, k] = d
|
|
33
35
|
|
|
34
36
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
37
|
+
@wp.kernel
|
|
38
|
+
def make_field_sphere_sdf_unit_domain(
|
|
39
|
+
field: wp.array3d(dtype=float), center: wp.vec3, radius: wp.array(dtype=wp.float32)
|
|
40
|
+
):
|
|
41
|
+
"""Makes a sphere SDF for nodes on the unit domain [-1, 1]^3."""
|
|
42
|
+
i, j, k = wp.tid()
|
|
39
43
|
|
|
40
|
-
|
|
44
|
+
nx, ny, nz = field.shape[0], field.shape[1], field.shape[2]
|
|
45
|
+
|
|
46
|
+
p = wp.vec3(
|
|
47
|
+
2.0 * wp.float32(i) / (wp.float32(nx) - 1.0) - 1.0,
|
|
48
|
+
2.0 * wp.float32(j) / (wp.float32(ny) - 1.0) - 1.0,
|
|
49
|
+
2.0 * wp.float32(k) / (wp.float32(nz) - 1.0) - 1.0,
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
d = wp.length(p - center) - radius[0]
|
|
53
|
+
|
|
54
|
+
field[i, j, k] = d
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
@wp.kernel
|
|
58
|
+
def compute_surface_area(
|
|
59
|
+
verts: wp.array(dtype=wp.vec3), faces: wp.array(dtype=wp.int32), out_area: wp.array(dtype=wp.float32)
|
|
60
|
+
):
|
|
61
|
+
tid = wp.tid()
|
|
62
|
+
vi = faces[3 * tid + 0]
|
|
63
|
+
vj = faces[3 * tid + 1]
|
|
64
|
+
vk = faces[3 * tid + 2]
|
|
65
|
+
|
|
66
|
+
p0 = verts[vi]
|
|
67
|
+
p1 = verts[vj]
|
|
68
|
+
p2 = verts[vk]
|
|
69
|
+
|
|
70
|
+
# Heron's formula for triangle area
|
|
71
|
+
a = wp.length(p1 - p0)
|
|
72
|
+
b = wp.length(p2 - p0)
|
|
73
|
+
c = wp.length(p2 - p1)
|
|
74
|
+
s = (a + b + c) / 2.0
|
|
75
|
+
area = wp.sqrt(s * (s - a) * (s - b) * (s - c))
|
|
41
76
|
|
|
42
|
-
|
|
77
|
+
wp.atomic_add(out_area, 0, area)
|
|
43
78
|
|
|
44
|
-
radius = dim / 4.0
|
|
45
79
|
|
|
46
|
-
|
|
80
|
+
def validate_marching_cubes_output(test, verts_np, faces_np, check_nonempty=True):
|
|
81
|
+
# check that the face array seems valid
|
|
82
|
+
if check_nonempty:
|
|
83
|
+
test.assertGreater(faces_np.shape[0], 0) # at least one face
|
|
84
|
+
test.assertEqual(faces_np.shape[1], 3) # all faces triangular
|
|
85
|
+
test.assertTrue((faces_np >= 0).all()) # all face inds nonnegative
|
|
86
|
+
test.assertTrue((faces_np < verts_np.shape[0]).all()) # all face inds are in-bounds on the vertex array
|
|
87
|
+
test.assertTrue((faces_np[:, 0] != faces_np[:, 1]).all()) # all faces have unique vertices
|
|
88
|
+
test.assertTrue((faces_np[:, 0] != faces_np[:, 2]).all()) # all faces have unique vertices
|
|
89
|
+
test.assertTrue((faces_np[:, 1] != faces_np[:, 2]).all()) # all faces have unique vertices
|
|
90
|
+
test.assertTrue(
|
|
91
|
+
(np.unique(faces_np.flatten()) == np.arange(verts_np.shape[0])).all()
|
|
92
|
+
) # all vertices are used in at least one face
|
|
93
|
+
|
|
94
|
+
# check that the vertex array seems valid
|
|
95
|
+
if check_nonempty:
|
|
96
|
+
test.assertGreater(verts_np.shape[0], 0) # at least one vertex
|
|
97
|
+
test.assertEqual(verts_np.shape[1], 3) # all vertices are 3D
|
|
98
|
+
test.assertTrue(np.isfinite(verts_np).all()) # all vertices are finite
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def test_marching_cubes(test, device):
|
|
102
|
+
"""Basic test of typical usage."""
|
|
103
|
+
node_dim = 64
|
|
104
|
+
cell_dim = node_dim - 1
|
|
105
|
+
field = wp.zeros(shape=(node_dim, node_dim, node_dim), dtype=float, device=device)
|
|
106
|
+
bounds_low = (0.0, 0.0, 0.0)
|
|
107
|
+
bounds_high = (float(cell_dim), float(cell_dim), float(cell_dim))
|
|
108
|
+
|
|
109
|
+
iso = wp.MarchingCubes(
|
|
110
|
+
nx=node_dim,
|
|
111
|
+
ny=node_dim,
|
|
112
|
+
nz=node_dim,
|
|
113
|
+
device=device,
|
|
114
|
+
domain_bounds_lower_corner=bounds_low,
|
|
115
|
+
domain_bounds_upper_corner=bounds_high,
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
radius = node_dim / 4.0
|
|
119
|
+
|
|
120
|
+
wp.launch(
|
|
121
|
+
make_field_sphere_sdf,
|
|
122
|
+
dim=field.shape,
|
|
123
|
+
inputs=[field, wp.vec3(node_dim / 2, node_dim / 2, node_dim / 2), radius],
|
|
124
|
+
device=device,
|
|
125
|
+
)
|
|
47
126
|
|
|
48
127
|
iso.surface(field=field, threshold=0.0)
|
|
128
|
+
verts_np = iso.verts.numpy()
|
|
129
|
+
faces_np = iso.indices.numpy().reshape(-1, 3)
|
|
130
|
+
test.assertEqual(
|
|
131
|
+
iso.indices.dtype, wp.int32
|
|
132
|
+
) # make sure we are following Warp convention of using a flat array of indices
|
|
133
|
+
validate_marching_cubes_output(test, verts_np, faces_np)
|
|
49
134
|
|
|
50
135
|
# check that all returned vertices lie on the surface of the sphere
|
|
51
|
-
length = np.linalg.norm(
|
|
136
|
+
length = np.linalg.norm(verts_np - np.array([node_dim / 2, node_dim / 2, node_dim / 2]), axis=1)
|
|
52
137
|
error = np.abs(length - radius)
|
|
138
|
+
test.assertTrue(np.max(error) < 1.0)
|
|
139
|
+
|
|
140
|
+
iso.resize(nx=node_dim * 2, ny=node_dim * 2, nz=node_dim * 2) # smoke test for deprecated function
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def test_marching_cubes_functional(test, device):
|
|
144
|
+
"""Ensure the single-function interface works as expected."""
|
|
145
|
+
node_dim = 64
|
|
146
|
+
cell_dim = node_dim - 1
|
|
147
|
+
field = wp.zeros(shape=(node_dim, node_dim, node_dim), dtype=float, device=device)
|
|
148
|
+
bounds_low = (0.0, 0.0, 0.0)
|
|
149
|
+
bounds_high = (float(cell_dim), float(cell_dim), float(cell_dim))
|
|
150
|
+
|
|
151
|
+
radius = node_dim / 4.0
|
|
152
|
+
wp.launch(
|
|
153
|
+
make_field_sphere_sdf,
|
|
154
|
+
dim=field.shape,
|
|
155
|
+
inputs=[field, wp.vec3(node_dim / 2, node_dim / 2, node_dim / 2), radius],
|
|
156
|
+
device=device,
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
# call via the functional interface
|
|
160
|
+
verts, faces = wp.MarchingCubes.extract_surface_marching_cubes(
|
|
161
|
+
field, threshold=0.0, domain_bounds_lower_corner=bounds_low, domain_bounds_upper_corner=bounds_high
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
verts_np = verts.numpy()
|
|
165
|
+
faces_np = faces.numpy().reshape(-1, 3)
|
|
166
|
+
validate_marching_cubes_output(test, verts_np, faces_np)
|
|
53
167
|
|
|
168
|
+
# check that all returned vertices lie on the surface of the sphere
|
|
169
|
+
length = np.linalg.norm(verts_np - np.array([node_dim / 2, node_dim / 2, node_dim / 2]), axis=1)
|
|
170
|
+
error = np.abs(length - radius)
|
|
54
171
|
test.assertTrue(np.max(error) < 1.0)
|
|
55
172
|
|
|
56
|
-
|
|
173
|
+
|
|
174
|
+
def test_marching_cubes_nonuniform(test, device):
|
|
175
|
+
"""Test the logic for when the dimensions of the grid are not uniform."""
|
|
176
|
+
|
|
177
|
+
dimX = 64
|
|
178
|
+
dimY = 48
|
|
179
|
+
dimZ = 72
|
|
180
|
+
field = wp.zeros(shape=(dimX, dimY, dimZ), dtype=float, device=device)
|
|
181
|
+
|
|
182
|
+
bounds_low = wp.vec3(0.0, 0.0, 0.0)
|
|
183
|
+
bounds_high = wp.vec3(float(dimX), float(dimY), float(dimZ))
|
|
184
|
+
|
|
185
|
+
iso = wp.MarchingCubes(
|
|
186
|
+
nx=dimX,
|
|
187
|
+
ny=dimY,
|
|
188
|
+
nz=dimZ,
|
|
189
|
+
device=device,
|
|
190
|
+
domain_bounds_lower_corner=bounds_low,
|
|
191
|
+
domain_bounds_upper_corner=bounds_high,
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
radius = dimX / 4.0
|
|
195
|
+
wp.launch(
|
|
196
|
+
make_field_sphere_sdf,
|
|
197
|
+
dim=field.shape,
|
|
198
|
+
inputs=[field, wp.vec3(dimX / 2, dimY / 2, dimZ / 2), radius],
|
|
199
|
+
device=device,
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
iso.surface(field=field, threshold=0.0)
|
|
203
|
+
verts_np = iso.verts.numpy()
|
|
204
|
+
faces_np = iso.indices.numpy().reshape(-1, 3)
|
|
205
|
+
validate_marching_cubes_output(test, verts_np, faces_np)
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
def test_marching_cubes_empty_output(test, device):
|
|
209
|
+
"""Make sure we handle the empty-output case correctly."""
|
|
210
|
+
|
|
211
|
+
dim = 64
|
|
212
|
+
field = wp.zeros(shape=(dim, dim, dim), dtype=float, device=device)
|
|
213
|
+
|
|
214
|
+
iso = wp.MarchingCubes(nx=dim, ny=dim, nz=dim, device=device)
|
|
215
|
+
|
|
216
|
+
wp.launch(make_field_sphere_sdf, dim=field.shape, inputs=[field, wp.vec3(0.5, 0.5, 0.5), 0.25], device=device)
|
|
217
|
+
|
|
218
|
+
iso.surface(field=field, threshold=1000.0) # set threshold to a large value so that no vertices are generated
|
|
219
|
+
verts_np = iso.verts.numpy()
|
|
220
|
+
faces_np = iso.indices.numpy().reshape(-1, 3)
|
|
221
|
+
validate_marching_cubes_output(test, verts_np, faces_np, check_nonempty=False)
|
|
222
|
+
|
|
223
|
+
test.assertEqual(faces_np.shape[0], 0) # no faces
|
|
224
|
+
test.assertEqual(verts_np.shape[0], 0) # no vertices
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
def test_marching_cubes_differentiable(test, device):
|
|
228
|
+
"""Check that marching cubes has reasonable gradients.
|
|
229
|
+
|
|
230
|
+
This test constructs an SDF of sphere, extracts a surface, computes its
|
|
231
|
+
surface area, and then differentiates the surface area with respect to
|
|
232
|
+
the sphere's radius.
|
|
233
|
+
"""
|
|
234
|
+
node_dim = 64
|
|
235
|
+
cell_dim = node_dim - 1
|
|
236
|
+
bounds_low = wp.vec3(-1.0, -1.0, -1.0)
|
|
237
|
+
bounds_high = wp.vec3(1.0, 1.0, 1.0)
|
|
238
|
+
|
|
239
|
+
radius = 0.5
|
|
240
|
+
radius_wp = wp.full((1,), value=0.5, dtype=wp.float32, device=device, requires_grad=True)
|
|
241
|
+
|
|
242
|
+
with wp.Tape() as tape:
|
|
243
|
+
field = wp.zeros(shape=(node_dim, node_dim, node_dim), dtype=float, device=device, requires_grad=True)
|
|
244
|
+
wp.launch(
|
|
245
|
+
make_field_sphere_sdf_unit_domain,
|
|
246
|
+
dim=field.shape,
|
|
247
|
+
inputs=[field, wp.vec3(0.0, 0.0, 0.0), radius_wp],
|
|
248
|
+
device=device,
|
|
249
|
+
)
|
|
250
|
+
|
|
251
|
+
# call via the functional interface
|
|
252
|
+
verts, faces = wp.MarchingCubes.extract_surface_marching_cubes(
|
|
253
|
+
field, threshold=0.0, domain_bounds_lower_corner=bounds_low, domain_bounds_upper_corner=bounds_high
|
|
254
|
+
)
|
|
255
|
+
|
|
256
|
+
# compute surface area
|
|
257
|
+
area = wp.zeros(shape=(1,), dtype=float, device=device, requires_grad=True)
|
|
258
|
+
wp.launch(compute_surface_area, dim=faces.shape[0] // 3, inputs=[verts, faces, area], device=device)
|
|
259
|
+
|
|
260
|
+
# confirm surface area is correct vs. the analytical ground truth
|
|
261
|
+
area_np = area.numpy()[0]
|
|
262
|
+
test.assertTrue(np.abs(area_np - 4.0 * np.pi * radius * radius) < 1e-2)
|
|
263
|
+
|
|
264
|
+
# compute the gradient of the surface area with respect to the radius
|
|
265
|
+
tape.backward(area)
|
|
266
|
+
|
|
267
|
+
# confirm the gradient is correct vs. the analytical ground truth
|
|
268
|
+
grad_np = radius_wp.grad.numpy()[0]
|
|
269
|
+
test.assertTrue(np.abs(grad_np - 8.0 * np.pi * radius) < 1e-2)
|
|
57
270
|
|
|
58
271
|
|
|
59
|
-
devices =
|
|
272
|
+
devices = get_test_devices()
|
|
60
273
|
|
|
61
274
|
|
|
62
275
|
class TestMarchingCubes(unittest.TestCase):
|
|
@@ -67,6 +280,14 @@ class TestMarchingCubes(unittest.TestCase):
|
|
|
67
280
|
|
|
68
281
|
|
|
69
282
|
add_function_test(TestMarchingCubes, "test_marching_cubes", test_marching_cubes, devices=devices)
|
|
283
|
+
add_function_test(TestMarchingCubes, "test_marching_cubes_functional", test_marching_cubes_functional, devices=devices)
|
|
284
|
+
add_function_test(TestMarchingCubes, "test_marching_cubes_nonuniform", test_marching_cubes_nonuniform, devices=devices)
|
|
285
|
+
add_function_test(
|
|
286
|
+
TestMarchingCubes, "test_marching_cubes_empty_output", test_marching_cubes_empty_output, devices=devices
|
|
287
|
+
)
|
|
288
|
+
add_function_test(
|
|
289
|
+
TestMarchingCubes, "test_marching_cubes_differentiable", test_marching_cubes_differentiable, devices=devices
|
|
290
|
+
)
|
|
70
291
|
|
|
71
292
|
|
|
72
293
|
if __name__ == "__main__":
|
warp/tests/sim/test_cloth.py
CHANGED
|
@@ -25,6 +25,7 @@ import warp.sim.integrator_euler
|
|
|
25
25
|
import warp.sim.integrator_vbd
|
|
26
26
|
import warp.sim.integrator_xpbd
|
|
27
27
|
import warp.sim.particles
|
|
28
|
+
import warp.sim.render
|
|
28
29
|
from warp.sim.model import PARTICLE_FLAG_ACTIVE
|
|
29
30
|
from warp.tests.unittest_utils import *
|
|
30
31
|
|
|
@@ -299,14 +300,16 @@ CLOTH_FACES = [
|
|
|
299
300
|
|
|
300
301
|
# fmt: on
|
|
301
302
|
class ClothSim:
|
|
302
|
-
def __init__(self, device, solver, use_cuda_graph=False):
|
|
303
|
+
def __init__(self, device, solver, use_cuda_graph=False, do_rendering=False):
|
|
303
304
|
self.frame_dt = 1 / 60
|
|
304
305
|
self.num_test_frames = 50
|
|
305
306
|
self.iterations = 5
|
|
307
|
+
self.do_rendering = do_rendering
|
|
306
308
|
self.device = device
|
|
307
309
|
self.use_cuda_graph = self.device.is_cuda and use_cuda_graph
|
|
308
310
|
self.builder = wp.sim.ModelBuilder()
|
|
309
311
|
self.solver = solver
|
|
312
|
+
self.renderer_scale_factor = 1.0
|
|
310
313
|
|
|
311
314
|
if solver != "semi_implicit":
|
|
312
315
|
self.num_substeps = 10
|
|
@@ -434,7 +437,7 @@ class ClothSim:
|
|
|
434
437
|
|
|
435
438
|
self.finalize()
|
|
436
439
|
|
|
437
|
-
def
|
|
440
|
+
def set_self_collision_experiment(self):
|
|
438
441
|
elasticity_ke = 1e4
|
|
439
442
|
elasticity_kd = 1e-6
|
|
440
443
|
|
|
@@ -487,6 +490,44 @@ class ClothSim:
|
|
|
487
490
|
self.model.gravity = wp.vec3(0.0, -1000.0, 0)
|
|
488
491
|
self.num_test_frames = 30
|
|
489
492
|
|
|
493
|
+
def set_body_collision_experiment(self, handle_self_contact=False):
|
|
494
|
+
self.renderer_scale_factor = 1.0
|
|
495
|
+
elasticity_ke = 1e4
|
|
496
|
+
elasticity_kd = 1e-6
|
|
497
|
+
|
|
498
|
+
vs1 = [wp.vec3(v) for v in [[0, 0, 0], [1, 0, 0], [1, 0, 1], [0, 0, 1]]]
|
|
499
|
+
fs1 = [0, 1, 2, 0, 2, 3]
|
|
500
|
+
|
|
501
|
+
self.builder.add_cloth_mesh(
|
|
502
|
+
pos=wp.vec3(0.0, 0.0, 0.0),
|
|
503
|
+
rot=wp.quat_from_axis_angle(wp.vec3(1.0, 0.0, 0.0), 0.0),
|
|
504
|
+
scale=1.0,
|
|
505
|
+
vertices=vs1,
|
|
506
|
+
indices=fs1,
|
|
507
|
+
vel=wp.vec3(0.0, 0.0, 0.0),
|
|
508
|
+
density=0.02,
|
|
509
|
+
tri_ke=elasticity_ke,
|
|
510
|
+
tri_ka=elasticity_ke,
|
|
511
|
+
tri_kd=elasticity_kd,
|
|
512
|
+
add_springs=self.solver == "xpbd",
|
|
513
|
+
spring_ke=1.0e3,
|
|
514
|
+
spring_kd=0.0,
|
|
515
|
+
particle_radius=0.1,
|
|
516
|
+
)
|
|
517
|
+
|
|
518
|
+
self.builder.add_shape_box(-1, pos=wp.vec3(0, -3.3, 0), hx=3, hy=3, hz=3)
|
|
519
|
+
|
|
520
|
+
self.fixed_particles = []
|
|
521
|
+
|
|
522
|
+
self.finalize(handle_self_contact=handle_self_contact, ground=False)
|
|
523
|
+
self.model.soft_contact_radius = 0.1
|
|
524
|
+
self.model.soft_contact_margin = 0.1
|
|
525
|
+
self.model.soft_contact_ke = 1e4
|
|
526
|
+
self.model.soft_contact_kd = 1e-3
|
|
527
|
+
self.model.soft_contact_mu = 0.2
|
|
528
|
+
self.model.gravity = wp.vec3(0.0, -1000.0, 0)
|
|
529
|
+
self.num_test_frames = 30
|
|
530
|
+
|
|
490
531
|
def set_up_non_zero_rest_angle_bending_experiment(self):
|
|
491
532
|
# fmt: off
|
|
492
533
|
vs =[
|
|
@@ -629,6 +670,9 @@ class ClothSim:
|
|
|
629
670
|
wp.load_module(warp.sim.integrator_euler, device=self.device)
|
|
630
671
|
wp.load_module(warp.sim.particles, device=self.device)
|
|
631
672
|
wp.load_module(warp.sim.integrator, device=self.device)
|
|
673
|
+
collide_module = importlib.import_module("warp.sim.collide")
|
|
674
|
+
wp.set_module_options({"block_dim": 256}, collide_module)
|
|
675
|
+
wp.load_module(collide_module, device=self.device)
|
|
632
676
|
wp.load_module(device=self.device)
|
|
633
677
|
with wp.ScopedCapture(device=self.device, force_module_load=False) as capture:
|
|
634
678
|
self.simulate()
|
|
@@ -637,17 +681,30 @@ class ClothSim:
|
|
|
637
681
|
def simulate(self):
|
|
638
682
|
for _step in range(self.num_substeps):
|
|
639
683
|
self.state0.clear_forces()
|
|
684
|
+
wp.sim.collide(self.model, self.state0)
|
|
640
685
|
|
|
641
686
|
self.integrator.simulate(self.model, self.state0, self.state1, self.dt, None)
|
|
642
687
|
(self.state0, self.state1) = (self.state1, self.state0)
|
|
643
688
|
|
|
644
689
|
def run(self):
|
|
645
690
|
self.sim_time = 0.0
|
|
691
|
+
|
|
692
|
+
if self.do_rendering:
|
|
693
|
+
self.renderer = wp.sim.render.SimRendererOpenGL(self.model, "cloth_sim", scaling=self.renderer_scale_factor)
|
|
694
|
+
else:
|
|
695
|
+
self.renderer = None
|
|
696
|
+
|
|
646
697
|
for _frame in range(self.num_test_frames):
|
|
647
698
|
if self.graph:
|
|
648
699
|
wp.capture_launch(self.graph)
|
|
649
700
|
else:
|
|
650
701
|
self.simulate()
|
|
702
|
+
|
|
703
|
+
if self.renderer is not None:
|
|
704
|
+
self.renderer.begin_frame()
|
|
705
|
+
self.renderer.render(self.state0)
|
|
706
|
+
self.renderer.end_frame()
|
|
707
|
+
|
|
651
708
|
self.sim_time = self.sim_time + self.frame_dt
|
|
652
709
|
|
|
653
710
|
def set_points_fixed(self, model, fixed_particles):
|
|
@@ -702,16 +759,41 @@ def test_cloth_bending_non_zero_rest_angle_bending(test, device, solver):
|
|
|
702
759
|
test.assertTrue((example.init_pos != final_pos).any())
|
|
703
760
|
|
|
704
761
|
|
|
705
|
-
def
|
|
762
|
+
def test_cloth_body_collision(test, device, solver):
|
|
763
|
+
example = ClothSim(device, solver, use_cuda_graph=True)
|
|
764
|
+
example.set_body_collision_experiment(handle_self_contact=False)
|
|
765
|
+
|
|
766
|
+
example.run()
|
|
767
|
+
|
|
768
|
+
# examine that the velocity has died out
|
|
769
|
+
final_vel = example.state0.particle_qd.numpy()
|
|
770
|
+
final_pos = example.state0.particle_q.numpy()
|
|
771
|
+
test.assertTrue((np.linalg.norm(final_vel, axis=1) < 1.0).all())
|
|
772
|
+
# examine that the simulation has moved
|
|
773
|
+
test.assertTrue((example.init_pos[:, 1] != final_pos[:, 1]).any())
|
|
774
|
+
|
|
775
|
+
# run again with handle_self_contact=True
|
|
776
|
+
example = ClothSim(device, solver, use_cuda_graph=True)
|
|
777
|
+
example.set_body_collision_experiment(handle_self_contact=True)
|
|
778
|
+
example.run()
|
|
779
|
+
# examine that the velocity has died out
|
|
780
|
+
final_vel = example.state0.particle_qd.numpy()
|
|
781
|
+
final_pos = example.state0.particle_q.numpy()
|
|
782
|
+
test.assertTrue((np.linalg.norm(final_vel, axis=1) < 1.0).all())
|
|
783
|
+
# examine that the simulation has moved
|
|
784
|
+
test.assertTrue((example.init_pos[:, 1] != final_pos[:, 1]).any())
|
|
785
|
+
|
|
786
|
+
|
|
787
|
+
def test_cloth_self_collision(test, device, solver):
|
|
706
788
|
example = ClothSim(device, solver, use_cuda_graph=True)
|
|
707
|
-
example.
|
|
789
|
+
example.set_self_collision_experiment()
|
|
708
790
|
|
|
709
791
|
example.run()
|
|
710
792
|
|
|
711
793
|
# examine that the velocity has died out
|
|
712
794
|
final_vel = example.state0.particle_qd.numpy()
|
|
713
795
|
final_pos = example.state0.particle_q.numpy()
|
|
714
|
-
test.assertTrue((np.linalg.norm(final_vel, axis=
|
|
796
|
+
test.assertTrue((np.linalg.norm(final_vel, axis=1) < 1.0).all())
|
|
715
797
|
# examine that the simulation has moved
|
|
716
798
|
test.assertTrue((example.init_pos != final_pos).any())
|
|
717
799
|
|
|
@@ -763,7 +845,8 @@ tests_to_run = {
|
|
|
763
845
|
test_cloth_free_fall,
|
|
764
846
|
test_cloth_sagging,
|
|
765
847
|
test_cloth_bending,
|
|
766
|
-
|
|
848
|
+
test_cloth_self_collision,
|
|
849
|
+
test_cloth_body_collision,
|
|
767
850
|
test_cloth_bending_non_zero_rest_angle_bending,
|
|
768
851
|
],
|
|
769
852
|
}
|