warp-lang 1.3.1__py3-none-manylinux2014_aarch64.whl → 1.3.3__py3-none-manylinux2014_aarch64.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/autograd.py +6 -6
- warp/bin/warp.so +0 -0
- warp/builtins.py +44 -41
- warp/codegen.py +27 -38
- warp/config.py +1 -1
- warp/context.py +159 -110
- warp/examples/fem/example_mixed_elasticity.py +33 -23
- warp/fem/field/nodal_field.py +1 -1
- warp/fem/quadrature/quadrature.py +1 -0
- warp/native/builtin.h +3 -3
- warp/native/bvh.h +1 -1
- warp/native/svd.h +22 -7
- warp/native/warp.cpp +1 -0
- warp/native/warp.cu +5 -0
- warp/native/warp.h +1 -0
- warp/sim/collide.py +1 -1
- warp/sim/model.py +16 -3
- warp/sim/utils.py +1 -1
- warp/stubs.py +112 -112
- warp/tests/test_array.py +45 -0
- warp/tests/test_async.py +3 -1
- warp/tests/test_bvh.py +33 -8
- warp/tests/test_compile_consts.py +15 -0
- warp/tests/test_examples.py +6 -1
- warp/tests/test_fem.py +51 -0
- warp/tests/test_grad_debug.py +2 -1
- warp/tests/test_model.py +55 -0
- warp/tests/test_point_triangle_closest_point.py +143 -0
- warp/tests/test_reload.py +28 -0
- warp/tests/test_struct.py +48 -30
- warp/tests/test_volume.py +30 -0
- warp/types.py +11 -8
- {warp_lang-1.3.1.dist-info → warp_lang-1.3.3.dist-info}/METADATA +14 -14
- {warp_lang-1.3.1.dist-info → warp_lang-1.3.3.dist-info}/RECORD +37 -36
- {warp_lang-1.3.1.dist-info → warp_lang-1.3.3.dist-info}/WHEEL +1 -1
- {warp_lang-1.3.1.dist-info → warp_lang-1.3.3.dist-info}/LICENSE.md +0 -0
- {warp_lang-1.3.1.dist-info → warp_lang-1.3.3.dist-info}/top_level.txt +0 -0
warp/tests/test_bvh.py
CHANGED
|
@@ -45,19 +45,19 @@ def aabb_overlap(a_lower, a_upper, b_lower, b_upper):
|
|
|
45
45
|
return 1
|
|
46
46
|
|
|
47
47
|
|
|
48
|
-
def intersect_ray_aabb(start,
|
|
49
|
-
l1 = (lower[0] - start[0]) *
|
|
50
|
-
l2 = (upper[0] - start[0]) *
|
|
48
|
+
def intersect_ray_aabb(start, rcp_dir, lower, upper):
|
|
49
|
+
l1 = (lower[0] - start[0]) * rcp_dir[0]
|
|
50
|
+
l2 = (upper[0] - start[0]) * rcp_dir[0]
|
|
51
51
|
lmin = min(l1, l2)
|
|
52
52
|
lmax = max(l1, l2)
|
|
53
53
|
|
|
54
|
-
l1 = (lower[1] - start[1]) *
|
|
55
|
-
l2 = (upper[1] - start[1]) *
|
|
54
|
+
l1 = (lower[1] - start[1]) * rcp_dir[1]
|
|
55
|
+
l2 = (upper[1] - start[1]) * rcp_dir[1]
|
|
56
56
|
lmin = max(min(l1, l2), lmin)
|
|
57
57
|
lmax = min(max(l1, l2), lmax)
|
|
58
58
|
|
|
59
|
-
l1 = (lower[2] - start[2]) *
|
|
60
|
-
l2 = (upper[2] - start[2]) *
|
|
59
|
+
l1 = (lower[2] - start[2]) * rcp_dir[2]
|
|
60
|
+
l2 = (upper[2] - start[2]) * rcp_dir[2]
|
|
61
61
|
lmin = max(min(l1, l2), lmin)
|
|
62
62
|
lmax = min(max(l1, l2), lmax)
|
|
63
63
|
|
|
@@ -108,7 +108,7 @@ def test_bvh(test, type, device):
|
|
|
108
108
|
if type == "AABB":
|
|
109
109
|
host_intersected = aabb_overlap(lower, upper, query_lower, query_upper)
|
|
110
110
|
else:
|
|
111
|
-
host_intersected = intersect_ray_aabb(query_start, query_dir, lower, upper)
|
|
111
|
+
host_intersected = intersect_ray_aabb(query_start, 1.0 / query_dir, lower, upper)
|
|
112
112
|
|
|
113
113
|
test.assertEqual(host_intersected, device_intersected[i])
|
|
114
114
|
|
|
@@ -129,6 +129,30 @@ def test_bvh_query_ray(test, device):
|
|
|
129
129
|
test_bvh(test, "ray", device)
|
|
130
130
|
|
|
131
131
|
|
|
132
|
+
def test_gh_288(test, device):
|
|
133
|
+
num_bounds = 1
|
|
134
|
+
lowers = ((0.5, -1.0, -1.0),) * num_bounds
|
|
135
|
+
uppers = ((1.0, 1.0, 1.0),) * num_bounds
|
|
136
|
+
|
|
137
|
+
device_lowers = wp.array(lowers, dtype=wp.vec3f, device=device)
|
|
138
|
+
device_uppers = wp.array(uppers, dtype=wp.vec3f, device=device)
|
|
139
|
+
|
|
140
|
+
bvh = wp.Bvh(device_lowers, device_uppers)
|
|
141
|
+
|
|
142
|
+
bounds_intersected = wp.zeros(shape=num_bounds, dtype=int, device=device)
|
|
143
|
+
|
|
144
|
+
for x in (0.0, 0.75):
|
|
145
|
+
query_start = wp.vec3(x, 0.0, 0.0)
|
|
146
|
+
query_dir = wp.vec3(1.0, 0.0, 0.0)
|
|
147
|
+
|
|
148
|
+
wp.launch(
|
|
149
|
+
kernel=bvh_query_ray, dim=1, inputs=[bvh.id, query_start, query_dir, bounds_intersected], device=device
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
device_intersected = bounds_intersected.numpy()
|
|
153
|
+
test.assertEqual(device_intersected.sum(), num_bounds)
|
|
154
|
+
|
|
155
|
+
|
|
132
156
|
devices = get_test_devices()
|
|
133
157
|
|
|
134
158
|
|
|
@@ -161,6 +185,7 @@ class TestBvh(unittest.TestCase):
|
|
|
161
185
|
|
|
162
186
|
add_function_test(TestBvh, "test_bvh_aabb", test_bvh_query_aabb, devices=devices)
|
|
163
187
|
add_function_test(TestBvh, "test_bvh_ray", test_bvh_query_ray, devices=devices)
|
|
188
|
+
add_function_test(TestBvh, "test_gh_288", test_gh_288, devices=devices)
|
|
164
189
|
|
|
165
190
|
if __name__ == "__main__":
|
|
166
191
|
wp.clear_kernel_cache()
|
|
@@ -18,6 +18,8 @@ UNIT_VEC = wp.constant(wp.vec3(SQRT3_OVER_3, SQRT3_OVER_3, SQRT3_OVER_3))
|
|
|
18
18
|
ONE_FP16 = wp.constant(wp.float16(1.0))
|
|
19
19
|
TEST_BOOL = wp.constant(True)
|
|
20
20
|
|
|
21
|
+
SHADOWED_GLOBAL = wp.constant(17)
|
|
22
|
+
|
|
21
23
|
|
|
22
24
|
class Foobar:
|
|
23
25
|
ONE = wp.constant(1)
|
|
@@ -68,6 +70,18 @@ def test_closure_capture(test, device):
|
|
|
68
70
|
wp.launch(two_closure, dim=(1), inputs=[2], device=device)
|
|
69
71
|
|
|
70
72
|
|
|
73
|
+
def test_closure_precedence(test, device):
|
|
74
|
+
"""Verifies that closure constants take precedence over globals"""
|
|
75
|
+
|
|
76
|
+
SHADOWED_GLOBAL = wp.constant(42)
|
|
77
|
+
|
|
78
|
+
@wp.kernel
|
|
79
|
+
def closure_kernel():
|
|
80
|
+
wp.expect_eq(SHADOWED_GLOBAL, 42)
|
|
81
|
+
|
|
82
|
+
wp.launch(closure_kernel, dim=1, device=device)
|
|
83
|
+
|
|
84
|
+
|
|
71
85
|
def test_hash_global_capture(test, device):
|
|
72
86
|
"""Verifies that global variables are included in the module hash"""
|
|
73
87
|
|
|
@@ -206,6 +220,7 @@ add_kernel_test(TestConstants, test_int, dim=1, inputs=[a], devices=devices)
|
|
|
206
220
|
add_kernel_test(TestConstants, test_float, dim=1, inputs=[x], devices=devices)
|
|
207
221
|
|
|
208
222
|
add_function_test(TestConstants, "test_closure_capture", test_closure_capture, devices=devices)
|
|
223
|
+
add_function_test(TestConstants, "test_closure_precedence", test_closure_precedence, devices=devices)
|
|
209
224
|
add_function_test(TestConstants, "test_hash_global_capture", test_hash_global_capture, devices=devices)
|
|
210
225
|
add_function_test(TestConstants, "test_hash_redefine_kernel", test_hash_redefine_kernel, devices=devices)
|
|
211
226
|
add_function_test(TestConstants, "test_hash_redefine_constant_only", test_hash_redefine_constant_only, devices=devices)
|
warp/tests/test_examples.py
CHANGED
|
@@ -250,7 +250,11 @@ add_example_test(
|
|
|
250
250
|
test_options_cpu={"num_frames": 10},
|
|
251
251
|
)
|
|
252
252
|
add_example_test(
|
|
253
|
-
TestOptimExamples,
|
|
253
|
+
TestOptimExamples,
|
|
254
|
+
name="optim.example_cloth_throw",
|
|
255
|
+
devices=test_devices,
|
|
256
|
+
test_options={"test_timeout": 600},
|
|
257
|
+
test_options_cpu={"train_iters": 3},
|
|
254
258
|
)
|
|
255
259
|
add_example_test(
|
|
256
260
|
TestOptimExamples,
|
|
@@ -390,6 +394,7 @@ add_example_test(
|
|
|
390
394
|
name="fem.example_mixed_elasticity",
|
|
391
395
|
devices=test_devices,
|
|
392
396
|
test_options={"nonconforming_stresses": True, "mesh": "quad", "headless": True},
|
|
397
|
+
test_options_cpu={"test_timeout": 600},
|
|
393
398
|
)
|
|
394
399
|
add_example_test(
|
|
395
400
|
TestFemExamples, name="fem.example_stokes_transfer", devices=test_devices, test_options={"headless": True}
|
warp/tests/test_fem.py
CHANGED
|
@@ -1252,6 +1252,8 @@ def test_particle_quadratures(test, device):
|
|
|
1252
1252
|
geo = fem.Grid2D(res=wp.vec2i(2))
|
|
1253
1253
|
|
|
1254
1254
|
domain = fem.Cells(geo)
|
|
1255
|
+
|
|
1256
|
+
# Explicit quadrature
|
|
1255
1257
|
points, weights = domain.reference_element().instantiate_quadrature(order=4, family=fem.Polynomial.GAUSS_LEGENDRE)
|
|
1256
1258
|
points_per_cell = len(points)
|
|
1257
1259
|
|
|
@@ -1266,9 +1268,16 @@ def test_particle_quadratures(test, device):
|
|
|
1266
1268
|
test.assertEqual(explicit_quadrature.max_points_per_element(), points_per_cell)
|
|
1267
1269
|
test.assertEqual(explicit_quadrature.total_point_count(), points_per_cell * geo.cell_count())
|
|
1268
1270
|
|
|
1271
|
+
# test integration accuracy
|
|
1269
1272
|
val = fem.integrate(_bicubic, quadrature=explicit_quadrature)
|
|
1270
1273
|
test.assertAlmostEqual(val, 1.0 / 16, places=5)
|
|
1271
1274
|
|
|
1275
|
+
# test indexing validity
|
|
1276
|
+
arr = wp.empty(explicit_quadrature.total_point_count(), dtype=float)
|
|
1277
|
+
fem.interpolate(_piecewise_constant, dest=arr, quadrature=explicit_quadrature)
|
|
1278
|
+
assert_np_equal(arr.numpy(), np.arange(geo.cell_count()).repeat(points_per_cell))
|
|
1279
|
+
|
|
1280
|
+
# PIC quadrature
|
|
1272
1281
|
element_indices = wp.array([3, 3, 2], dtype=int, device=device)
|
|
1273
1282
|
element_coords = wp.array(
|
|
1274
1283
|
[
|
|
@@ -1286,6 +1295,7 @@ def test_particle_quadratures(test, device):
|
|
|
1286
1295
|
test.assertEqual(pic_quadrature.total_point_count(), 3)
|
|
1287
1296
|
test.assertEqual(pic_quadrature.active_cell_count(), 2)
|
|
1288
1297
|
|
|
1298
|
+
# Test integration accuracy
|
|
1289
1299
|
val = fem.integrate(_piecewise_constant, quadrature=pic_quadrature)
|
|
1290
1300
|
test.assertAlmostEqual(val, 1.25, places=5)
|
|
1291
1301
|
|
|
@@ -1305,6 +1315,46 @@ def test_particle_quadratures(test, device):
|
|
|
1305
1315
|
assert_np_equal(measures.grad.numpy(), np.full(3, 4.0)) # == 1.0 / cell_area
|
|
1306
1316
|
|
|
1307
1317
|
|
|
1318
|
+
@fem.integrand
|
|
1319
|
+
def _value_at_node(s: fem.Sample, f: fem.Field, values: wp.array(dtype=float)):
|
|
1320
|
+
node_index = fem.operator.node_partition_index(f, s.qp_index)
|
|
1321
|
+
return values[node_index]
|
|
1322
|
+
|
|
1323
|
+
|
|
1324
|
+
def test_nodal_quadrature(test, device):
|
|
1325
|
+
geo = fem.Grid2D(res=wp.vec2i(2))
|
|
1326
|
+
|
|
1327
|
+
domain = fem.Cells(geo)
|
|
1328
|
+
|
|
1329
|
+
space = fem.make_polynomial_space(geo, degree=2, discontinuous=True, family=fem.Polynomial.GAUSS_LEGENDRE)
|
|
1330
|
+
nodal_quadrature = fem.NodalQuadrature(domain, space)
|
|
1331
|
+
|
|
1332
|
+
test.assertEqual(nodal_quadrature.max_points_per_element(), 9)
|
|
1333
|
+
test.assertEqual(nodal_quadrature.total_point_count(), 9 * geo.cell_count())
|
|
1334
|
+
|
|
1335
|
+
val = fem.integrate(_bicubic, quadrature=nodal_quadrature)
|
|
1336
|
+
test.assertAlmostEqual(val, 1.0 / 16, places=5)
|
|
1337
|
+
|
|
1338
|
+
# test accessing data associated to a given node
|
|
1339
|
+
|
|
1340
|
+
piecewise_constant_space = fem.make_polynomial_space(geo, degree=0)
|
|
1341
|
+
geo_partition = fem.LinearGeometryPartition(geo, 3, 4)
|
|
1342
|
+
space_partition = fem.make_space_partition(piecewise_constant_space, geo_partition)
|
|
1343
|
+
field = fem.make_discrete_field(piecewise_constant_space, space_partition=space_partition)
|
|
1344
|
+
|
|
1345
|
+
partition_domain = fem.Cells(geo_partition)
|
|
1346
|
+
partition_nodal_quadrature = fem.NodalQuadrature(partition_domain, piecewise_constant_space)
|
|
1347
|
+
|
|
1348
|
+
partition_node_values = wp.array([5.0], dtype=float)
|
|
1349
|
+
val = fem.integrate(
|
|
1350
|
+
_value_at_node,
|
|
1351
|
+
quadrature=partition_nodal_quadrature,
|
|
1352
|
+
fields={"f": field},
|
|
1353
|
+
values={"values": partition_node_values},
|
|
1354
|
+
)
|
|
1355
|
+
test.assertAlmostEqual(val, 5.0 / geo.cell_count(), places=5)
|
|
1356
|
+
|
|
1357
|
+
|
|
1308
1358
|
@wp.func
|
|
1309
1359
|
def aniso_bicubic_fn(x: wp.vec2, scale: wp.vec2):
|
|
1310
1360
|
return wp.pow(x[0] * scale[0], 3.0) * wp.pow(x[1] * scale[1], 3.0)
|
|
@@ -1485,6 +1535,7 @@ add_function_test(TestFem, "test_deformed_geometry", test_deformed_geometry, dev
|
|
|
1485
1535
|
add_function_test(TestFem, "test_dof_mapper", test_dof_mapper)
|
|
1486
1536
|
add_function_test(TestFem, "test_point_basis", test_point_basis)
|
|
1487
1537
|
add_function_test(TestFem, "test_particle_quadratures", test_particle_quadratures)
|
|
1538
|
+
add_function_test(TestFem, "test_nodal_quadrature", test_nodal_quadrature)
|
|
1488
1539
|
add_function_test(TestFem, "test_implicit_fields", test_implicit_fields)
|
|
1489
1540
|
add_kernel_test(TestFem, test_qr_eigenvalues, dim=1, devices=devices)
|
|
1490
1541
|
add_kernel_test(TestFem, test_qr_inverse, dim=100, devices=devices)
|
warp/tests/test_grad_debug.py
CHANGED
|
@@ -93,7 +93,8 @@ def test_gradcheck_3d(test, device):
|
|
|
93
93
|
inputs=[a_3d, b_3d, c_3d],
|
|
94
94
|
outputs=[out1_3d, out2_3d],
|
|
95
95
|
max_inputs_per_var=4,
|
|
96
|
-
|
|
96
|
+
# use integer indices instead of variable names
|
|
97
|
+
input_output_mask=[(0, 0), (1, 1)],
|
|
97
98
|
eps=1e-4,
|
|
98
99
|
)
|
|
99
100
|
|
warp/tests/test_model.py
CHANGED
|
@@ -102,6 +102,61 @@ class TestModel(unittest.TestCase):
|
|
|
102
102
|
assert_np_equal(np.array(builder1.edge_rest_angle), np.array(builder2.edge_rest_angle), tol=1.0e-4)
|
|
103
103
|
assert_np_equal(np.array(builder1.edge_bending_properties), np.array(builder2.edge_bending_properties))
|
|
104
104
|
|
|
105
|
+
def test_collapse_fixed_joints(self):
|
|
106
|
+
def add_three_cubes(builder: ModelBuilder, parent_body=-1):
|
|
107
|
+
unit_cube = {"hx": 0.5, "hy": 0.5, "hz": 0.5, "density": 1.0}
|
|
108
|
+
b0 = builder.add_body()
|
|
109
|
+
builder.add_shape_box(body=b0, **unit_cube)
|
|
110
|
+
builder.add_joint_fixed(parent=parent_body, child=b0, parent_xform=wp.transform(wp.vec3(1.0, 0.0, 0.0)))
|
|
111
|
+
b1 = builder.add_body()
|
|
112
|
+
builder.add_shape_box(body=b1, **unit_cube)
|
|
113
|
+
builder.add_joint_fixed(parent=parent_body, child=b1, parent_xform=wp.transform(wp.vec3(0.0, 1.0, 0.0)))
|
|
114
|
+
b2 = builder.add_body()
|
|
115
|
+
builder.add_shape_box(body=b2, **unit_cube)
|
|
116
|
+
builder.add_joint_fixed(parent=parent_body, child=b2, parent_xform=wp.transform(wp.vec3(0.0, 0.0, 1.0)))
|
|
117
|
+
return b2
|
|
118
|
+
|
|
119
|
+
builder = ModelBuilder()
|
|
120
|
+
# only fixed joints
|
|
121
|
+
builder.add_articulation()
|
|
122
|
+
add_three_cubes(builder)
|
|
123
|
+
assert builder.joint_count == 3
|
|
124
|
+
assert builder.body_count == 3
|
|
125
|
+
|
|
126
|
+
# fixed joints followed by a non-fixed joint
|
|
127
|
+
builder.add_articulation()
|
|
128
|
+
last_body = add_three_cubes(builder)
|
|
129
|
+
assert builder.joint_count == 6
|
|
130
|
+
assert builder.body_count == 6
|
|
131
|
+
assert builder.articulation_count == 2
|
|
132
|
+
b3 = builder.add_body()
|
|
133
|
+
builder.add_shape_box(body=b3, hx=0.5, hy=0.5, hz=0.5, density=1.0, pos=wp.vec3(1.0, 2.0, 3.0))
|
|
134
|
+
builder.add_joint_revolute(parent=last_body, child=b3, axis=wp.vec3(0.0, 1.0, 0.0))
|
|
135
|
+
|
|
136
|
+
# a non-fixed joint followed by fixed joints
|
|
137
|
+
builder.add_articulation()
|
|
138
|
+
b4 = builder.add_body()
|
|
139
|
+
builder.add_shape_box(body=b4, hx=0.5, hy=0.5, hz=0.5, density=1.0)
|
|
140
|
+
builder.add_joint_free(parent=-1, child=b4, parent_xform=wp.transform(wp.vec3(0.0, -1.0, 0.0)))
|
|
141
|
+
assert builder.joint_count == 8
|
|
142
|
+
assert builder.body_count == 8
|
|
143
|
+
assert builder.articulation_count == 3
|
|
144
|
+
add_three_cubes(builder, parent_body=b4)
|
|
145
|
+
|
|
146
|
+
builder.collapse_fixed_joints()
|
|
147
|
+
|
|
148
|
+
assert builder.joint_count == 2
|
|
149
|
+
assert builder.articulation_count == 2
|
|
150
|
+
assert builder.articulation_start == [0, 1]
|
|
151
|
+
assert builder.joint_type == [wp.sim.JOINT_REVOLUTE, wp.sim.JOINT_FREE]
|
|
152
|
+
assert builder.shape_count == 11
|
|
153
|
+
assert builder.shape_body == [-1, -1, -1, -1, -1, -1, 0, 1, 1, 1, 1]
|
|
154
|
+
assert builder.body_count == 2
|
|
155
|
+
assert builder.body_com[0] == wp.vec3(1.0, 2.0, 3.0)
|
|
156
|
+
assert builder.body_com[1] == wp.vec3(0.25, 0.25, 0.25)
|
|
157
|
+
assert builder.body_mass == [1.0, 4.0]
|
|
158
|
+
assert builder.body_inv_mass == [1.0, 0.25]
|
|
159
|
+
|
|
105
160
|
|
|
106
161
|
if __name__ == "__main__":
|
|
107
162
|
wp.clear_kernel_cache()
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
# Copyright (c) 2024 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
|
+
import unittest
|
|
9
|
+
|
|
10
|
+
from warp.sim.collide import triangle_closest_point_barycentric
|
|
11
|
+
from warp.tests.unittest_utils import *
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
# a-b is the edge where the closest point is located at
|
|
15
|
+
@wp.func
|
|
16
|
+
def check_edge_feasible_region(p: wp.vec3, a: wp.vec3, b: wp.vec3, c: wp.vec3, eps: float):
|
|
17
|
+
ap = p - a
|
|
18
|
+
bp = p - b
|
|
19
|
+
ab = b - a
|
|
20
|
+
|
|
21
|
+
if wp.dot(ap, ab) < -eps:
|
|
22
|
+
return False
|
|
23
|
+
|
|
24
|
+
if wp.dot(bp, ab) > eps:
|
|
25
|
+
return False
|
|
26
|
+
|
|
27
|
+
ab_sqr_norm = wp.dot(ab, ab)
|
|
28
|
+
if ab_sqr_norm < eps:
|
|
29
|
+
return False
|
|
30
|
+
|
|
31
|
+
t = wp.dot(ab, c - a) / ab_sqr_norm
|
|
32
|
+
|
|
33
|
+
perpendicular_foot = a + t * ab
|
|
34
|
+
|
|
35
|
+
if wp.dot(c - perpendicular_foot, p - perpendicular_foot) > eps:
|
|
36
|
+
return False
|
|
37
|
+
|
|
38
|
+
return True
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
# closest point is a
|
|
42
|
+
@wp.func
|
|
43
|
+
def check_vertex_feasible_region(p: wp.vec3, a: wp.vec3, b: wp.vec3, c: wp.vec3, eps: float):
|
|
44
|
+
ap = p - a
|
|
45
|
+
ba = a - b
|
|
46
|
+
ca = a - c
|
|
47
|
+
|
|
48
|
+
if wp.dot(ap, ba) < -eps:
|
|
49
|
+
return False
|
|
50
|
+
|
|
51
|
+
if wp.dot(p, ca) < -eps:
|
|
52
|
+
return False
|
|
53
|
+
|
|
54
|
+
return True
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
@wp.func
|
|
58
|
+
def return_true():
|
|
59
|
+
return True
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def test_point_triangle_closest_point(test, device):
|
|
63
|
+
passed = wp.array([True], dtype=wp.bool, device=device)
|
|
64
|
+
|
|
65
|
+
@wp.kernel
|
|
66
|
+
def test_point_triangle_closest_point_kernel(tri: wp.array(dtype=wp.vec3), passed: wp.array(dtype=wp.bool)):
|
|
67
|
+
state = wp.uint32(wp.rand_init(wp.int32(123), wp.int32(0)))
|
|
68
|
+
eps = 1e-5
|
|
69
|
+
|
|
70
|
+
for _i in range(1000):
|
|
71
|
+
l = wp.float32(0.0)
|
|
72
|
+
while l < eps:
|
|
73
|
+
p = wp.vec3(wp.randn(state), wp.randn(state), wp.randn(state))
|
|
74
|
+
l = wp.length(p)
|
|
75
|
+
|
|
76
|
+
# project to a sphere with r=2
|
|
77
|
+
p = 2.0 * p / l
|
|
78
|
+
|
|
79
|
+
bary = triangle_closest_point_barycentric(tri[0], tri[1], tri[2], p)
|
|
80
|
+
|
|
81
|
+
for dim in range(3):
|
|
82
|
+
v1_index = (dim + 1) % 3
|
|
83
|
+
v2_index = (dim + 2) % 3
|
|
84
|
+
v1 = tri[v1_index]
|
|
85
|
+
v2 = tri[v2_index]
|
|
86
|
+
v3 = tri[dim]
|
|
87
|
+
|
|
88
|
+
# on edge
|
|
89
|
+
if bary[dim] == 0.0 and bary[v1_index] != 0.0 and bary[v2_index] != 0.0:
|
|
90
|
+
if not check_edge_feasible_region(p, v1, v2, v3, eps):
|
|
91
|
+
passed[0] = False
|
|
92
|
+
return
|
|
93
|
+
|
|
94
|
+
# p-closest_p must be perpendicular to v1-v2
|
|
95
|
+
closest_p = a * bary[0] + b * bary[1] + c * bary[2]
|
|
96
|
+
e = v1 - v2
|
|
97
|
+
err = wp.dot(e, closest_p - p)
|
|
98
|
+
if wp.abs(err) > eps:
|
|
99
|
+
passed[0] = False
|
|
100
|
+
return
|
|
101
|
+
|
|
102
|
+
if bary[v1_index] == 0.0 and bary[v2_index] == 0.0:
|
|
103
|
+
if not check_vertex_feasible_region(p, v3, v1, v2, eps):
|
|
104
|
+
passed[0] = False
|
|
105
|
+
return
|
|
106
|
+
|
|
107
|
+
if bary[dim] != 0.0 and bary[v1_index] != 0.0 and bary[v2_index] != 0.0:
|
|
108
|
+
closest_p = a * bary[0] + b * bary[1] + c * bary[2]
|
|
109
|
+
e1 = v1 - v2
|
|
110
|
+
e2 = v1 - v3
|
|
111
|
+
if wp.abs(wp.dot(e1, closest_p - p)) > eps or wp.abs(wp.dot(e2, closest_p - p)) > eps:
|
|
112
|
+
passed[0] = False
|
|
113
|
+
return
|
|
114
|
+
|
|
115
|
+
a = wp.vec3(1.0, 0.0, 0.0)
|
|
116
|
+
b = wp.vec3(0.0, 0.0, 0.0)
|
|
117
|
+
c = wp.vec3(0.0, 1.0, 0.0)
|
|
118
|
+
|
|
119
|
+
tri = wp.array([a, b, c], dtype=wp.vec3, device=device)
|
|
120
|
+
wp.launch(test_point_triangle_closest_point_kernel, dim=1, inputs=[tri, passed], device=device)
|
|
121
|
+
passed = passed.numpy()
|
|
122
|
+
|
|
123
|
+
test.assertTrue(passed.all())
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
devices = get_test_devices()
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
class TestTriangleClosestPoint(unittest.TestCase):
|
|
130
|
+
pass
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
add_function_test(
|
|
134
|
+
TestTriangleClosestPoint,
|
|
135
|
+
"test_point_triangle_closest_point",
|
|
136
|
+
test_point_triangle_closest_point,
|
|
137
|
+
devices=devices,
|
|
138
|
+
check_output=True,
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
if __name__ == "__main__":
|
|
142
|
+
wp.clear_kernel_cache()
|
|
143
|
+
unittest.main(verbosity=2)
|
warp/tests/test_reload.py
CHANGED
|
@@ -189,7 +189,32 @@ def test_reload_references(test, device):
|
|
|
189
189
|
test_dependent.run(expect=4.0, device=device) # 2 * 2 = 4
|
|
190
190
|
|
|
191
191
|
|
|
192
|
+
def test_graph_launch_after_module_reload(test, device):
|
|
193
|
+
@wp.kernel
|
|
194
|
+
def foo(a: wp.array(dtype=int)):
|
|
195
|
+
a[0] = 42
|
|
196
|
+
|
|
197
|
+
with wp.ScopedDevice(device):
|
|
198
|
+
a = wp.zeros(1, dtype=int)
|
|
199
|
+
|
|
200
|
+
# preload module before graph capture
|
|
201
|
+
wp.load_module(device=device)
|
|
202
|
+
|
|
203
|
+
# capture a launch
|
|
204
|
+
with wp.ScopedCapture(force_module_load=False) as capture:
|
|
205
|
+
wp.launch(foo, dim=1, inputs=[a])
|
|
206
|
+
|
|
207
|
+
# unload the module
|
|
208
|
+
foo.module.unload()
|
|
209
|
+
|
|
210
|
+
# launch previously captured graph
|
|
211
|
+
wp.capture_launch(capture.graph)
|
|
212
|
+
|
|
213
|
+
test.assertEqual(a.numpy()[0], 42)
|
|
214
|
+
|
|
215
|
+
|
|
192
216
|
devices = get_test_devices()
|
|
217
|
+
cuda_devices = get_cuda_test_devices()
|
|
193
218
|
|
|
194
219
|
|
|
195
220
|
class TestReload(unittest.TestCase):
|
|
@@ -200,6 +225,9 @@ add_function_test(TestReload, "test_redefine", test_redefine, devices=devices)
|
|
|
200
225
|
add_function_test(TestReload, "test_reload", test_reload, devices=devices)
|
|
201
226
|
add_function_test(TestReload, "test_reload_class", test_reload_class, devices=devices)
|
|
202
227
|
add_function_test(TestReload, "test_reload_references", test_reload_references, devices=devices)
|
|
228
|
+
add_function_test(
|
|
229
|
+
TestReload, "test_graph_launch_after_module_reload", test_graph_launch_after_module_reload, devices=cuda_devices
|
|
230
|
+
)
|
|
203
231
|
|
|
204
232
|
|
|
205
233
|
if __name__ == "__main__":
|
warp/tests/test_struct.py
CHANGED
|
@@ -589,6 +589,52 @@ def test_dependent_module_import(c: DependentModuleImport_C):
|
|
|
589
589
|
wp.tid() # nop, we're just testing codegen
|
|
590
590
|
|
|
591
591
|
|
|
592
|
+
def test_struct_array_content_hash(test, device):
|
|
593
|
+
# Ensure that the memory address of the struct does not affect the content hash
|
|
594
|
+
|
|
595
|
+
@wp.struct
|
|
596
|
+
class ContentHashStruct:
|
|
597
|
+
i: int
|
|
598
|
+
|
|
599
|
+
@wp.kernel
|
|
600
|
+
def dummy_kernel(a: wp.array(dtype=ContentHashStruct)):
|
|
601
|
+
i = wp.tid()
|
|
602
|
+
|
|
603
|
+
module_hash_0 = wp.get_module(dummy_kernel.__module__).hash_module()
|
|
604
|
+
|
|
605
|
+
# Redefine ContentHashStruct to have the same members as before but a new memory address
|
|
606
|
+
@wp.struct
|
|
607
|
+
class ContentHashStruct:
|
|
608
|
+
i: int
|
|
609
|
+
|
|
610
|
+
@wp.kernel
|
|
611
|
+
def dummy_kernel(a: wp.array(dtype=ContentHashStruct)):
|
|
612
|
+
i = wp.tid()
|
|
613
|
+
|
|
614
|
+
module_hash_1 = wp.get_module(dummy_kernel.__module__).hash_module(recompute_content_hash=True)
|
|
615
|
+
|
|
616
|
+
test.assertEqual(
|
|
617
|
+
module_hash_1,
|
|
618
|
+
module_hash_0,
|
|
619
|
+
"Module hash should be unchanged when ContentHashStruct is redefined but unchanged.",
|
|
620
|
+
)
|
|
621
|
+
|
|
622
|
+
# Redefine ContentHashStruct to have different members. This time we should get a new hash.
|
|
623
|
+
@wp.struct
|
|
624
|
+
class ContentHashStruct:
|
|
625
|
+
i: float
|
|
626
|
+
|
|
627
|
+
@wp.kernel
|
|
628
|
+
def dummy_kernel(a: wp.array(dtype=ContentHashStruct)):
|
|
629
|
+
i = wp.tid()
|
|
630
|
+
|
|
631
|
+
module_hash_2 = wp.get_module(dummy_kernel.__module__).hash_module(recompute_content_hash=True)
|
|
632
|
+
|
|
633
|
+
test.assertNotEqual(
|
|
634
|
+
module_hash_2, module_hash_0, "Module hash should be different when ContentHashStruct redefined and changed."
|
|
635
|
+
)
|
|
636
|
+
|
|
637
|
+
|
|
592
638
|
devices = get_test_devices()
|
|
593
639
|
|
|
594
640
|
|
|
@@ -636,36 +682,6 @@ add_kernel_test(
|
|
|
636
682
|
devices=devices,
|
|
637
683
|
)
|
|
638
684
|
|
|
639
|
-
add_kernel_test(
|
|
640
|
-
TestStruct,
|
|
641
|
-
name="test_struct_mutate_attributes",
|
|
642
|
-
kernel=test_struct_mutate_attributes_kernel,
|
|
643
|
-
dim=1,
|
|
644
|
-
inputs=[],
|
|
645
|
-
devices=devices,
|
|
646
|
-
)
|
|
647
|
-
add_kernel_test(
|
|
648
|
-
TestStruct,
|
|
649
|
-
kernel=test_uninitialized,
|
|
650
|
-
name="test_uninitialized",
|
|
651
|
-
dim=1,
|
|
652
|
-
inputs=[Uninitialized()],
|
|
653
|
-
devices=devices,
|
|
654
|
-
)
|
|
655
|
-
add_kernel_test(TestStruct, kernel=test_return, name="test_return", dim=1, inputs=[], devices=devices)
|
|
656
|
-
add_function_test(TestStruct, "test_nested_struct", test_nested_struct, devices=devices)
|
|
657
|
-
add_function_test(TestStruct, "test_nested_array_struct", test_nested_array_struct, devices=devices)
|
|
658
|
-
add_function_test(TestStruct, "test_nested_empty_struct", test_nested_empty_struct, devices=devices)
|
|
659
|
-
add_function_test(TestStruct, "test_struct_math_conversions", test_struct_math_conversions, devices=devices)
|
|
660
|
-
add_kernel_test(
|
|
661
|
-
TestStruct,
|
|
662
|
-
name="test_struct_default_attributes",
|
|
663
|
-
kernel=test_struct_default_attributes_kernel,
|
|
664
|
-
dim=1,
|
|
665
|
-
inputs=[],
|
|
666
|
-
devices=devices,
|
|
667
|
-
)
|
|
668
|
-
|
|
669
685
|
add_kernel_test(
|
|
670
686
|
TestStruct,
|
|
671
687
|
name="test_struct_mutate_attributes",
|
|
@@ -702,6 +718,8 @@ add_kernel_test(
|
|
|
702
718
|
devices=devices,
|
|
703
719
|
)
|
|
704
720
|
|
|
721
|
+
add_function_test(TestStruct, "test_struct_array_content_hash", test_struct_array_content_hash, devices=None)
|
|
722
|
+
|
|
705
723
|
|
|
706
724
|
if __name__ == "__main__":
|
|
707
725
|
wp.clear_kernel_cache()
|
warp/tests/test_volume.py
CHANGED
|
@@ -843,6 +843,33 @@ def test_volume_from_numpy(test, device):
|
|
|
843
843
|
test.assertIsNone(sphere_vdb_array.deleter)
|
|
844
844
|
|
|
845
845
|
|
|
846
|
+
def test_volume_from_numpy_3d(test, device):
|
|
847
|
+
# Volume.allocate_from_tiles() is only available with CUDA
|
|
848
|
+
mins = np.array([-3.0, -3.0, -3.0])
|
|
849
|
+
voxel_size = 0.2
|
|
850
|
+
maxs = np.array([3.0, 3.0, 3.0])
|
|
851
|
+
nums = np.ceil((maxs - mins) / (voxel_size)).astype(dtype=int)
|
|
852
|
+
centers = np.array([[-1.0, -1.0, -1.0], [0.0, 0.0, 0.0], [1.0, 1.0, 1.0]])
|
|
853
|
+
rad = 2.5
|
|
854
|
+
sphere_sdf_np = np.zeros(tuple(nums) + (3,))
|
|
855
|
+
for x in range(nums[0]):
|
|
856
|
+
for y in range(nums[1]):
|
|
857
|
+
for z in range(nums[2]):
|
|
858
|
+
for k in range(3):
|
|
859
|
+
pos = mins + voxel_size * np.array([x, y, z])
|
|
860
|
+
dis = np.linalg.norm(pos - centers[k])
|
|
861
|
+
sphere_sdf_np[x, y, z, k] = dis - rad
|
|
862
|
+
sphere_vdb = wp.Volume.load_from_numpy(
|
|
863
|
+
sphere_sdf_np, mins, voxel_size, (rad + 3.0 * voxel_size,) * 3, device=device
|
|
864
|
+
)
|
|
865
|
+
|
|
866
|
+
test.assertNotEqual(sphere_vdb.id, 0)
|
|
867
|
+
|
|
868
|
+
sphere_vdb_array = sphere_vdb.array()
|
|
869
|
+
test.assertEqual(sphere_vdb_array.dtype, wp.uint8)
|
|
870
|
+
test.assertIsNone(sphere_vdb_array.deleter)
|
|
871
|
+
|
|
872
|
+
|
|
846
873
|
def test_volume_aniso_transform(test, device):
|
|
847
874
|
# XY-rotation + z scale
|
|
848
875
|
transform = [
|
|
@@ -894,6 +921,9 @@ add_function_test(TestVolume, "test_volume_introspection", test_volume_introspec
|
|
|
894
921
|
add_function_test(
|
|
895
922
|
TestVolume, "test_volume_from_numpy", test_volume_from_numpy, devices=get_selected_cuda_test_devices()
|
|
896
923
|
)
|
|
924
|
+
add_function_test(
|
|
925
|
+
TestVolume, "test_volume_from_numpy_3d", test_volume_from_numpy_3d, devices=get_selected_cuda_test_devices()
|
|
926
|
+
)
|
|
897
927
|
add_function_test(
|
|
898
928
|
TestVolume, "test_volume_aniso_transform", test_volume_aniso_transform, devices=get_selected_cuda_test_devices()
|
|
899
929
|
)
|
warp/types.py
CHANGED
|
@@ -1576,7 +1576,7 @@ class array(Array):
|
|
|
1576
1576
|
|
|
1577
1577
|
Args:
|
|
1578
1578
|
data (Union[list, tuple, ndarray]): An object to construct the array from, can be a Tuple, List, or generally any type convertible to an np.array
|
|
1579
|
-
dtype (Union): One of the
|
|
1579
|
+
dtype (Union): One of the available `data types <#data-types>`_, such as :class:`warp.float32`, :class:`warp.mat33`, or a custom `struct <#structs>`_. If dtype is ``Any`` and data is an ndarray, then it will be inferred from the array data type
|
|
1580
1580
|
shape (tuple): Dimensions of the array
|
|
1581
1581
|
strides (tuple): Number of bytes in each dimension between successive elements of the array
|
|
1582
1582
|
length (int): Number of elements of the data type (deprecated, users should use `shape` argument)
|
|
@@ -1601,6 +1601,9 @@ class array(Array):
|
|
|
1601
1601
|
self._array_interface = None
|
|
1602
1602
|
self.is_transposed = False
|
|
1603
1603
|
|
|
1604
|
+
# reference to other array
|
|
1605
|
+
self._ref = None
|
|
1606
|
+
|
|
1604
1607
|
# canonicalize dtype
|
|
1605
1608
|
if dtype == int:
|
|
1606
1609
|
dtype = int32
|
|
@@ -1614,7 +1617,9 @@ class array(Array):
|
|
|
1614
1617
|
if isinstance(shape, int):
|
|
1615
1618
|
shape = (shape,)
|
|
1616
1619
|
else:
|
|
1617
|
-
shape
|
|
1620
|
+
# The type of shape's elements are eventually passed onto capacity which is used to allocate memory. We
|
|
1621
|
+
# explicitly enforce that shape is a tuple of (64-bit) ints to ensure that capacity is 64-bit.
|
|
1622
|
+
shape = tuple(int(x) for x in shape)
|
|
1618
1623
|
if len(shape) > ARRAY_MAX_DIMS:
|
|
1619
1624
|
raise RuntimeError(
|
|
1620
1625
|
f"Failed to create array with shape {shape}, the maximum number of dimensions is {ARRAY_MAX_DIMS}"
|
|
@@ -1650,9 +1655,6 @@ class array(Array):
|
|
|
1650
1655
|
if requires_grad:
|
|
1651
1656
|
self._alloc_grad()
|
|
1652
1657
|
|
|
1653
|
-
# reference to other array
|
|
1654
|
-
self._ref = None
|
|
1655
|
-
|
|
1656
1658
|
def _init_from_data(self, data, dtype, shape, device, copy, pinned):
|
|
1657
1659
|
if not hasattr(data, "__len__"):
|
|
1658
1660
|
raise RuntimeError(f"Data must be a sequence or array, got scalar {data}")
|
|
@@ -2989,7 +2991,7 @@ class Mesh:
|
|
|
2989
2991
|
|
|
2990
2992
|
Args:
|
|
2991
2993
|
points (:class:`warp.array`): Array of vertex positions of type :class:`warp.vec3`
|
|
2992
|
-
indices (:class:`warp.array`): Array of triangle indices of type :class:`warp.int32`, should be a 1d array with shape (num_tris
|
|
2994
|
+
indices (:class:`warp.array`): Array of triangle indices of type :class:`warp.int32`, should be a 1d array with shape (num_tris * 3)
|
|
2993
2995
|
velocities (:class:`warp.array`): Array of vertex velocities of type :class:`warp.vec3` (optional)
|
|
2994
2996
|
support_winding_number (bool): If true the mesh will build additional datastructures to support `wp.mesh_query_point_sign_winding_number()` queries
|
|
2995
2997
|
"""
|
|
@@ -3527,8 +3529,9 @@ class Volume:
|
|
|
3527
3529
|
)
|
|
3528
3530
|
if hasattr(bg_value, "__len__"):
|
|
3529
3531
|
# vec3, assuming the numpy array is 4D
|
|
3530
|
-
padded_array = np.
|
|
3531
|
-
|
|
3532
|
+
padded_array = np.full(
|
|
3533
|
+
shape=(target_shape[0], target_shape[1], target_shape[2], 3), fill_value=bg_value, dtype=np.single
|
|
3534
|
+
)
|
|
3532
3535
|
padded_array[0 : ndarray.shape[0], 0 : ndarray.shape[1], 0 : ndarray.shape[2], :] = ndarray
|
|
3533
3536
|
else:
|
|
3534
3537
|
padded_amount = (
|