warp-lang 1.1.0__py3-none-macosx_10_13_universal2.whl → 1.2.0__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.

Files changed (218) hide show
  1. warp/bin/libwarp-clang.dylib +0 -0
  2. warp/bin/libwarp.dylib +0 -0
  3. warp/build.py +10 -37
  4. warp/build_dll.py +2 -2
  5. warp/builtins.py +274 -6
  6. warp/codegen.py +51 -4
  7. warp/config.py +2 -2
  8. warp/constants.py +4 -0
  9. warp/context.py +418 -203
  10. warp/examples/benchmarks/benchmark_api.py +0 -2
  11. warp/examples/benchmarks/benchmark_cloth_warp.py +0 -1
  12. warp/examples/benchmarks/benchmark_launches.py +0 -2
  13. warp/examples/core/example_dem.py +0 -2
  14. warp/examples/core/example_fluid.py +0 -2
  15. warp/examples/core/example_graph_capture.py +0 -2
  16. warp/examples/core/example_marching_cubes.py +0 -2
  17. warp/examples/core/example_mesh.py +0 -2
  18. warp/examples/core/example_mesh_intersect.py +0 -2
  19. warp/examples/core/example_nvdb.py +0 -2
  20. warp/examples/core/example_raycast.py +0 -2
  21. warp/examples/core/example_raymarch.py +0 -2
  22. warp/examples/core/example_render_opengl.py +0 -2
  23. warp/examples/core/example_sph.py +0 -2
  24. warp/examples/core/example_torch.py +0 -3
  25. warp/examples/core/example_wave.py +0 -2
  26. warp/examples/fem/example_apic_fluid.py +140 -115
  27. warp/examples/fem/example_burgers.py +262 -0
  28. warp/examples/fem/example_convection_diffusion.py +0 -2
  29. warp/examples/fem/example_convection_diffusion_dg.py +0 -2
  30. warp/examples/fem/example_deformed_geometry.py +0 -2
  31. warp/examples/fem/example_diffusion.py +0 -2
  32. warp/examples/fem/example_diffusion_3d.py +5 -4
  33. warp/examples/fem/example_diffusion_mgpu.py +0 -2
  34. warp/examples/fem/example_mixed_elasticity.py +0 -2
  35. warp/examples/fem/example_navier_stokes.py +0 -2
  36. warp/examples/fem/example_stokes.py +0 -2
  37. warp/examples/fem/example_stokes_transfer.py +0 -2
  38. warp/examples/optim/example_bounce.py +0 -2
  39. warp/examples/optim/example_cloth_throw.py +0 -2
  40. warp/examples/optim/example_diffray.py +0 -2
  41. warp/examples/optim/example_drone.py +0 -2
  42. warp/examples/optim/example_inverse_kinematics.py +0 -2
  43. warp/examples/optim/example_inverse_kinematics_torch.py +0 -2
  44. warp/examples/optim/example_spring_cage.py +0 -2
  45. warp/examples/optim/example_trajectory.py +0 -2
  46. warp/examples/optim/example_walker.py +0 -2
  47. warp/examples/sim/example_cartpole.py +0 -2
  48. warp/examples/sim/example_cloth.py +0 -2
  49. warp/examples/sim/example_granular.py +0 -2
  50. warp/examples/sim/example_granular_collision_sdf.py +0 -2
  51. warp/examples/sim/example_jacobian_ik.py +0 -2
  52. warp/examples/sim/example_particle_chain.py +0 -2
  53. warp/examples/sim/example_quadruped.py +0 -2
  54. warp/examples/sim/example_rigid_chain.py +0 -2
  55. warp/examples/sim/example_rigid_contact.py +0 -2
  56. warp/examples/sim/example_rigid_force.py +0 -2
  57. warp/examples/sim/example_rigid_gyroscopic.py +0 -2
  58. warp/examples/sim/example_rigid_soft_contact.py +0 -2
  59. warp/examples/sim/example_soft_body.py +0 -2
  60. warp/fem/__init__.py +1 -0
  61. warp/fem/cache.py +3 -1
  62. warp/fem/geometry/__init__.py +1 -0
  63. warp/fem/geometry/element.py +4 -0
  64. warp/fem/geometry/grid_3d.py +0 -4
  65. warp/fem/geometry/nanogrid.py +455 -0
  66. warp/fem/integrate.py +63 -9
  67. warp/fem/space/__init__.py +43 -158
  68. warp/fem/space/basis_space.py +34 -0
  69. warp/fem/space/collocated_function_space.py +1 -1
  70. warp/fem/space/grid_2d_function_space.py +13 -132
  71. warp/fem/space/grid_3d_function_space.py +16 -154
  72. warp/fem/space/hexmesh_function_space.py +37 -134
  73. warp/fem/space/nanogrid_function_space.py +202 -0
  74. warp/fem/space/quadmesh_2d_function_space.py +12 -119
  75. warp/fem/space/restriction.py +4 -1
  76. warp/fem/space/shape/__init__.py +77 -0
  77. warp/fem/space/shape/cube_shape_function.py +5 -15
  78. warp/fem/space/tetmesh_function_space.py +6 -76
  79. warp/fem/space/trimesh_2d_function_space.py +6 -76
  80. warp/native/array.h +12 -3
  81. warp/native/builtin.h +48 -5
  82. warp/native/bvh.cpp +14 -10
  83. warp/native/bvh.cu +23 -15
  84. warp/native/bvh.h +1 -0
  85. warp/native/clang/clang.cpp +2 -1
  86. warp/native/crt.cpp +11 -1
  87. warp/native/crt.h +18 -1
  88. warp/native/exports.h +187 -0
  89. warp/native/mat.h +47 -0
  90. warp/native/mesh.cpp +1 -1
  91. warp/native/mesh.cu +1 -2
  92. warp/native/nanovdb/GridHandle.h +366 -0
  93. warp/native/nanovdb/HostBuffer.h +590 -0
  94. warp/native/nanovdb/NanoVDB.h +3999 -2157
  95. warp/native/nanovdb/PNanoVDB.h +936 -99
  96. warp/native/quat.h +28 -1
  97. warp/native/rand.h +5 -1
  98. warp/native/vec.h +45 -1
  99. warp/native/volume.cpp +335 -103
  100. warp/native/volume.cu +39 -13
  101. warp/native/volume.h +725 -303
  102. warp/native/volume_builder.cu +381 -360
  103. warp/native/volume_builder.h +16 -1
  104. warp/native/volume_impl.h +61 -0
  105. warp/native/warp.cu +8 -2
  106. warp/native/warp.h +15 -7
  107. warp/render/render_opengl.py +191 -52
  108. warp/sim/integrator_featherstone.py +10 -3
  109. warp/sim/integrator_xpbd.py +16 -22
  110. warp/sparse.py +89 -27
  111. warp/stubs.py +83 -0
  112. warp/tests/assets/test_index_grid.nvdb +0 -0
  113. warp/tests/aux_test_dependent.py +0 -2
  114. warp/tests/aux_test_grad_customs.py +0 -2
  115. warp/tests/aux_test_reference.py +0 -2
  116. warp/tests/aux_test_reference_reference.py +0 -2
  117. warp/tests/aux_test_square.py +0 -2
  118. warp/tests/disabled_kinematics.py +0 -2
  119. warp/tests/test_adam.py +0 -2
  120. warp/tests/test_arithmetic.py +0 -36
  121. warp/tests/test_array.py +9 -11
  122. warp/tests/test_array_reduce.py +0 -2
  123. warp/tests/test_async.py +0 -2
  124. warp/tests/test_atomic.py +0 -2
  125. warp/tests/test_bool.py +58 -50
  126. warp/tests/test_builtins_resolution.py +0 -2
  127. warp/tests/test_bvh.py +0 -2
  128. warp/tests/test_closest_point_edge_edge.py +0 -1
  129. warp/tests/test_codegen.py +0 -4
  130. warp/tests/test_compile_consts.py +130 -10
  131. warp/tests/test_conditional.py +0 -2
  132. warp/tests/test_copy.py +0 -2
  133. warp/tests/test_ctypes.py +6 -8
  134. warp/tests/test_dense.py +0 -2
  135. warp/tests/test_devices.py +0 -2
  136. warp/tests/test_dlpack.py +9 -11
  137. warp/tests/test_examples.py +42 -39
  138. warp/tests/test_fabricarray.py +0 -3
  139. warp/tests/test_fast_math.py +0 -2
  140. warp/tests/test_fem.py +75 -54
  141. warp/tests/test_fp16.py +0 -2
  142. warp/tests/test_func.py +0 -2
  143. warp/tests/test_generics.py +27 -2
  144. warp/tests/test_grad.py +147 -8
  145. warp/tests/test_grad_customs.py +0 -2
  146. warp/tests/test_hash_grid.py +1 -3
  147. warp/tests/test_import.py +0 -2
  148. warp/tests/test_indexedarray.py +0 -2
  149. warp/tests/test_intersect.py +0 -2
  150. warp/tests/test_jax.py +0 -2
  151. warp/tests/test_large.py +11 -9
  152. warp/tests/test_launch.py +0 -2
  153. warp/tests/test_lerp.py +10 -54
  154. warp/tests/test_linear_solvers.py +3 -5
  155. warp/tests/test_lvalue.py +0 -2
  156. warp/tests/test_marching_cubes.py +0 -2
  157. warp/tests/test_mat.py +0 -2
  158. warp/tests/test_mat_lite.py +0 -2
  159. warp/tests/test_mat_scalar_ops.py +0 -2
  160. warp/tests/test_math.py +0 -2
  161. warp/tests/test_matmul.py +35 -37
  162. warp/tests/test_matmul_lite.py +29 -31
  163. warp/tests/test_mempool.py +0 -2
  164. warp/tests/test_mesh.py +0 -3
  165. warp/tests/test_mesh_query_aabb.py +0 -2
  166. warp/tests/test_mesh_query_point.py +0 -2
  167. warp/tests/test_mesh_query_ray.py +0 -2
  168. warp/tests/test_mlp.py +0 -2
  169. warp/tests/test_model.py +0 -2
  170. warp/tests/test_module_hashing.py +111 -0
  171. warp/tests/test_modules_lite.py +0 -3
  172. warp/tests/test_multigpu.py +0 -2
  173. warp/tests/test_noise.py +0 -4
  174. warp/tests/test_operators.py +0 -2
  175. warp/tests/test_options.py +0 -2
  176. warp/tests/test_peer.py +0 -2
  177. warp/tests/test_pinned.py +0 -2
  178. warp/tests/test_print.py +0 -2
  179. warp/tests/test_quat.py +0 -2
  180. warp/tests/test_rand.py +41 -5
  181. warp/tests/test_reload.py +0 -10
  182. warp/tests/test_rounding.py +0 -2
  183. warp/tests/test_runlength_encode.py +0 -2
  184. warp/tests/test_sim_grad.py +0 -2
  185. warp/tests/test_sim_kinematics.py +0 -2
  186. warp/tests/test_smoothstep.py +0 -2
  187. warp/tests/test_snippet.py +0 -2
  188. warp/tests/test_sparse.py +0 -2
  189. warp/tests/test_spatial.py +0 -2
  190. warp/tests/test_special_values.py +362 -0
  191. warp/tests/test_streams.py +0 -2
  192. warp/tests/test_struct.py +0 -2
  193. warp/tests/test_tape.py +0 -2
  194. warp/tests/test_torch.py +0 -2
  195. warp/tests/test_transient_module.py +0 -2
  196. warp/tests/test_types.py +0 -2
  197. warp/tests/test_utils.py +0 -2
  198. warp/tests/test_vec.py +0 -2
  199. warp/tests/test_vec_lite.py +0 -2
  200. warp/tests/test_vec_scalar_ops.py +0 -2
  201. warp/tests/test_verify_fp.py +0 -2
  202. warp/tests/test_volume.py +237 -13
  203. warp/tests/test_volume_write.py +86 -3
  204. warp/tests/unittest_serial.py +10 -9
  205. warp/tests/unittest_suites.py +6 -2
  206. warp/tests/unittest_utils.py +2 -171
  207. warp/tests/unused_test_misc.py +0 -2
  208. warp/tests/walkthrough_debug.py +1 -1
  209. warp/thirdparty/unittest_parallel.py +37 -40
  210. warp/types.py +514 -77
  211. {warp_lang-1.1.0.dist-info → warp_lang-1.2.0.dist-info}/METADATA +57 -30
  212. warp_lang-1.2.0.dist-info/RECORD +359 -0
  213. warp/examples/fem/example_convection_diffusion_dg0.py +0 -204
  214. warp/native/nanovdb/PNanoVDBWrite.h +0 -295
  215. warp_lang-1.1.0.dist-info/RECORD +0 -352
  216. {warp_lang-1.1.0.dist-info → warp_lang-1.2.0.dist-info}/LICENSE.md +0 -0
  217. {warp_lang-1.1.0.dist-info → warp_lang-1.2.0.dist-info}/WHEEL +0 -0
  218. {warp_lang-1.1.0.dist-info → warp_lang-1.2.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,455 @@
1
+ from typing import Optional
2
+
3
+ import numpy as np
4
+
5
+ import warp as wp
6
+ from warp.fem import cache, utils
7
+ from warp.fem.types import NULL_ELEMENT_INDEX, OUTSIDE, Coords, ElementIndex, Sample, make_free_sample
8
+
9
+ from .element import Cube, Square
10
+ from .geometry import Geometry
11
+
12
+ # Flag used for building edge/face grids to disambiguiate axis within the grid
13
+ GRID_AXIS_FLAG = wp.constant(wp.int32(1 << 20))
14
+ FACE_AXIS_MASK = wp.constant(wp.uint8((1 << 3) - 1))
15
+ FACE_INNER_OFFSET_BIT = wp.constant(wp.uint8(3))
16
+ FACE_OUTER_OFFSET_BIT = wp.constant(wp.uint8(4))
17
+
18
+ _mat32 = wp.mat(shape=(3, 2), dtype=float)
19
+
20
+
21
+ @wp.func
22
+ def _add_axis_flag(ijk: wp.vec3i, axis: int):
23
+ coord = ijk[axis]
24
+ ijk[axis] = wp.select(coord < 0, coord | GRID_AXIS_FLAG, coord & (~GRID_AXIS_FLAG))
25
+ return ijk
26
+
27
+
28
+ @wp.func
29
+ def _extract_axis_flag(ijk: wp.vec3i):
30
+ for ax in range(3):
31
+ coord = ijk[ax]
32
+ if coord < 0:
33
+ if (ijk[ax] & GRID_AXIS_FLAG) == 0:
34
+ ijk[ax] = ijk[ax] | GRID_AXIS_FLAG
35
+ return ax, ijk
36
+ else:
37
+ if (ijk[ax] & GRID_AXIS_FLAG) != 0:
38
+ ijk[ax] = ijk[ax] & (~GRID_AXIS_FLAG)
39
+ return ax, ijk
40
+
41
+ return -1, ijk
42
+
43
+
44
+ @wp.struct
45
+ class NanogridCellArg:
46
+ # Utility device functions
47
+ cell_grid: wp.uint64
48
+ cell_ijk: wp.array(dtype=wp.vec3i)
49
+ inverse_transform: wp.mat33
50
+ cell_volume: float
51
+
52
+
53
+ @wp.struct
54
+ class NanogridSideArg:
55
+ # Utility device functions
56
+ cell_arg: NanogridCellArg
57
+ face_ijk: wp.array(dtype=wp.vec3i)
58
+ face_flags: wp.array(dtype=wp.uint8)
59
+ face_areas: wp.vec3
60
+
61
+
62
+ class Nanogrid(Geometry):
63
+ dimension = 3
64
+
65
+ def __init__(self, grid: wp.Volume, temporary_store: Optional[cache.TemporaryStore] = None):
66
+ self._cell_grid = grid
67
+ self._cell_grid_info = grid.get_grid_info()
68
+
69
+ device = grid.device
70
+
71
+ cell_count = grid.get_voxel_count()
72
+ self._cell_ijk = wp.array(shape=(cell_count,), dtype=wp.vec3i, device=device)
73
+ grid.get_voxels(out=self._cell_ijk)
74
+
75
+ self._node_grid = _build_node_grid(self._cell_ijk, grid, temporary_store)
76
+ node_count = self._node_grid.get_voxel_count()
77
+ self._node_ijk = wp.array(shape=(node_count,), dtype=wp.vec3i, device=device)
78
+ self._node_grid.get_voxels(out=self._node_ijk)
79
+
80
+ self._face_grid = _build_face_grid(self._cell_ijk, grid, temporary_store)
81
+ face_count = self._face_grid.get_voxel_count()
82
+ self._face_ijk = wp.array(shape=(face_count,), dtype=wp.vec3i, device=device)
83
+ self._face_grid.get_voxels(out=self._face_ijk)
84
+
85
+ self._face_flags = wp.array(shape=(face_count,), dtype=wp.uint8, device=device)
86
+ boundary_face_mask = cache.borrow_temporary(temporary_store, shape=(face_count,), dtype=wp.int32, device=device)
87
+
88
+ wp.launch(
89
+ _build_face_flags,
90
+ dim=face_count,
91
+ device=device,
92
+ inputs=[grid.id, self._face_ijk, self._face_flags, boundary_face_mask.array],
93
+ )
94
+ boundary_face_indices, _ = utils.masked_indices(boundary_face_mask.array)
95
+ self._boundary_face_indices = boundary_face_indices.detach()
96
+
97
+ self._edge_grid = None
98
+ self._edge_ijk = None
99
+
100
+ def _build_edge_grid(self, temporary_store: Optional[cache.TemporaryStore] = None):
101
+ self._edge_grid = _build_edge_grid(self._cell_ijk, self._cell_grid, temporary_store)
102
+ edge_count = self._edge_grid.get_voxel_count()
103
+ self._edge_ijk = wp.array(shape=(edge_count,), dtype=wp.vec3i, device=self._edge_grid.device)
104
+ self._edge_grid.get_voxels(out=self._edge_ijk)
105
+
106
+ def cell_count(self):
107
+ return self._cell_ijk.shape[0]
108
+
109
+ def vertex_count(self):
110
+ return self._node_ijk.shape[0]
111
+
112
+ def side_count(self):
113
+ return self._face_ijk.shape[0]
114
+
115
+ def edge_count(self):
116
+ if self._edge_ijk is None:
117
+ self._build_edge_grid()
118
+
119
+ return self._edge_ijk.shape[0]
120
+
121
+ def boundary_side_count(self):
122
+ return self._boundary_face_indices.shape[0]
123
+
124
+ def reference_cell(self) -> Cube:
125
+ return Cube()
126
+
127
+ def reference_side(self) -> Square:
128
+ return Square()
129
+
130
+ CellArg = NanogridCellArg
131
+
132
+ @cache.cached_arg_value
133
+ def cell_arg_value(self, device) -> CellArg:
134
+ args = self.CellArg()
135
+ args.cell_grid = self._cell_grid.id
136
+ args.cell_ijk = self._cell_ijk
137
+
138
+ transform = np.array(self._cell_grid_info.transform_matrix).reshape(3, 3)
139
+ args.inverse_transform = wp.mat33f(np.linalg.inv(transform))
140
+ args.cell_volume = abs(np.linalg.det(transform))
141
+
142
+ return args
143
+
144
+ @wp.func
145
+ def cell_position(args: CellArg, s: Sample):
146
+ uvw = wp.vec3(args.cell_ijk[s.element_index]) + s.element_coords
147
+ return wp.volume_index_to_world(args.cell_grid, uvw)
148
+
149
+ @wp.func
150
+ def cell_deformation_gradient(args: CellArg, s: Sample):
151
+ return wp.inverse(args.inverse_transform)
152
+
153
+ @wp.func
154
+ def cell_inverse_deformation_gradient(args: CellArg, s: Sample):
155
+ return args.inverse_transform
156
+
157
+ @wp.func
158
+ def cell_lookup(args: CellArg, pos: wp.vec3):
159
+ uvw = wp.volume_world_to_index(args.cell_grid, pos)
160
+ ijk = wp.vec3i(int(wp.floor(uvw[0])), int(wp.floor(uvw[1])), int(wp.floor(uvw[2])))
161
+ element_index = wp.volume_lookup_index(args.cell_grid, ijk[0], ijk[1], ijk[2])
162
+
163
+ return wp.select(
164
+ element_index == -1,
165
+ make_free_sample(element_index, uvw - wp.vec3(ijk)),
166
+ make_free_sample(NULL_ELEMENT_INDEX, Coords(OUTSIDE)),
167
+ )
168
+
169
+ @wp.func
170
+ def cell_lookup(args: CellArg, pos: wp.vec3, guess: Sample):
171
+ return Nanogrid.cell_lookup(args, pos)
172
+
173
+ @wp.func
174
+ def cell_measure(args: CellArg, s: Sample):
175
+ return args.cell_volume
176
+
177
+ @wp.func
178
+ def cell_normal(args: CellArg, s: Sample):
179
+ return wp.vec3(0.0)
180
+
181
+ SideArg = NanogridSideArg
182
+
183
+ @cache.cached_arg_value
184
+ def side_arg_value(self, device) -> SideArg:
185
+ args = self.SideArg()
186
+ args.cell_arg = self.cell_arg_value(device)
187
+ args.face_ijk = self._face_ijk.to(device)
188
+ args.face_flags = self._face_flags.to(device)
189
+ transform = np.array(self._cell_grid_info.transform_matrix).reshape(3, 3)
190
+ args.face_areas = wp.vec3(
191
+ tuple(np.linalg.norm(np.cross(transform[:, k - 2], transform[:, k - 1])) for k in range(3))
192
+ )
193
+
194
+ return args
195
+
196
+ @wp.struct
197
+ class SideIndexArg:
198
+ boundary_face_indices: wp.array(dtype=int)
199
+
200
+ @cache.cached_arg_value
201
+ def side_index_arg_value(self, device) -> SideIndexArg:
202
+ args = self.SideIndexArg()
203
+ args.boundary_face_indices = self._boundary_face_indices.to(device)
204
+ return args
205
+
206
+ @wp.func
207
+ def boundary_side_index(args: SideIndexArg, boundary_side_index: int):
208
+ return args.boundary_face_indices[boundary_side_index]
209
+
210
+ @wp.func
211
+ def _side_to_cell_coords(axis: int, inner: float, side_coords: Coords):
212
+ uvw = wp.vec3()
213
+ uvw[axis] = inner
214
+ uvw[(axis + 1) % 3] = side_coords[0]
215
+ uvw[(axis + 2) % 3] = side_coords[1]
216
+ return uvw
217
+
218
+ @wp.func
219
+ def _get_face_axis(flags: wp.uint8):
220
+ return wp.int32(flags & FACE_AXIS_MASK)
221
+
222
+ @wp.func
223
+ def _get_face_inner_offset(flags: wp.uint8):
224
+ return wp.int32(flags >> FACE_INNER_OFFSET_BIT) & 1
225
+
226
+ @wp.func
227
+ def _get_face_outer_offset(flags: wp.uint8):
228
+ return wp.int32(flags >> FACE_OUTER_OFFSET_BIT) & 1
229
+
230
+ @wp.func
231
+ def side_position(args: SideArg, s: Sample):
232
+ ijk = args.face_ijk[s.element_index]
233
+ axis = Nanogrid._get_face_axis(args.face_flags[s.element_index])
234
+
235
+ uvw = wp.vec3(ijk) + Nanogrid._side_to_cell_coords(axis, 0.0, s.element_coords)
236
+
237
+ cell_grid = args.cell_arg.cell_grid
238
+ return wp.volume_index_to_world(cell_grid, uvw)
239
+
240
+ @wp.func
241
+ def _face_tangent_vecs(args: SideArg, axis: int, flip: int):
242
+ u_axis = utils.unit_element(wp.vec3(), (axis + 1 + flip) % 3)
243
+ v_axis = utils.unit_element(wp.vec3(), (axis + 2 - flip) % 3)
244
+
245
+ cell_grid = args.cell_arg.cell_grid
246
+
247
+ return wp.volume_index_to_world_dir(cell_grid, u_axis), wp.volume_index_to_world_dir(cell_grid, v_axis)
248
+
249
+ @wp.func
250
+ def side_deformation_gradient(args: SideArg, s: Sample):
251
+ flags = args.face_flags[s.element_index]
252
+ axis = Nanogrid._get_face_axis(flags)
253
+ flip = Nanogrid._get_face_inner_offset(flags)
254
+ v1, v2 = Nanogrid._face_tangent_vecs(args, axis, flip)
255
+ return _mat32(v1, v2)
256
+
257
+ @wp.func
258
+ def side_inner_inverse_deformation_gradient(args: SideArg, s: Sample):
259
+ return Nanogrid.cell_inverse_deformation_gradient(args.cell_arg, s)
260
+
261
+ @wp.func
262
+ def side_outer_inverse_deformation_gradient(args: SideArg, s: Sample):
263
+ return Nanogrid.cell_inverse_deformation_gradient(args.cell_arg, s)
264
+
265
+ @wp.func
266
+ def side_measure(args: SideArg, s: Sample):
267
+ axis = Nanogrid._get_face_axis(args.face_flags[s.element_index])
268
+ return args.face_areas[axis]
269
+
270
+ @wp.func
271
+ def side_measure_ratio(args: SideArg, s: Sample):
272
+ axis = Nanogrid._get_face_axis(args.face_flags[s.element_index])
273
+ return args.face_areas[axis] / args.cell_arg.cell_volume
274
+
275
+ @wp.func
276
+ def side_normal(args: SideArg, s: Sample):
277
+ flags = args.face_flags[s.element_index]
278
+ axis = Nanogrid._get_face_axis(flags)
279
+ flip = Nanogrid._get_face_inner_offset(flags)
280
+
281
+ v1, v2 = Nanogrid._face_tangent_vecs(args, axis, flip)
282
+ return wp.cross(v1, v2) / args.face_areas[axis]
283
+
284
+ @wp.func
285
+ def side_inner_cell_index(args: SideArg, side_index: ElementIndex):
286
+ ijk = args.face_ijk[side_index]
287
+ flags = args.face_flags[side_index]
288
+ axis = Nanogrid._get_face_axis(flags)
289
+ offset = Nanogrid._get_face_inner_offset(flags)
290
+
291
+ ijk[axis] += offset - 1
292
+ cell_grid = args.cell_arg.cell_grid
293
+
294
+ return wp.volume_lookup_index(cell_grid, ijk[0], ijk[1], ijk[2])
295
+
296
+ @wp.func
297
+ def side_outer_cell_index(args: SideArg, side_index: ElementIndex):
298
+ ijk = args.face_ijk[side_index]
299
+ flags = args.face_flags[side_index]
300
+ axis = Nanogrid._get_face_axis(flags)
301
+ offset = Nanogrid._get_face_outer_offset(flags)
302
+
303
+ ijk[axis] -= offset
304
+ cell_grid = args.cell_arg.cell_grid
305
+
306
+ return wp.volume_lookup_index(cell_grid, ijk[0], ijk[1], ijk[2])
307
+
308
+ @wp.func
309
+ def side_inner_cell_coords(args: SideArg, side_index: ElementIndex, side_coords: Coords):
310
+ flags = args.face_flags[side_index]
311
+ axis = Nanogrid._get_face_axis(flags)
312
+ offset = float(Nanogrid._get_face_inner_offset(flags))
313
+ return Nanogrid._side_to_cell_coords(axis, 1.0 - offset, side_coords)
314
+
315
+ @wp.func
316
+ def side_outer_cell_coords(args: SideArg, side_index: ElementIndex, side_coords: Coords):
317
+ flags = args.face_flags[side_index]
318
+ axis = Nanogrid._get_face_axis(flags)
319
+ offset = float(Nanogrid._get_face_outer_offset(flags))
320
+ return Nanogrid._side_to_cell_coords(axis, offset, side_coords)
321
+
322
+ @wp.func
323
+ def side_from_cell_coords(
324
+ args: SideArg,
325
+ side_index: ElementIndex,
326
+ element_index: ElementIndex,
327
+ element_coords: Coords,
328
+ ):
329
+ flags = args.face_flags[side_index]
330
+ axis = Nanogrid._get_face_axis(flags)
331
+
332
+ cell_ijk = args.cell_arg.cell_ijk[element_index]
333
+ side_ijk = args.face_ijk[side_index]
334
+
335
+ on_side = float(side_ijk[axis] - cell_ijk[axis]) == element_coords[axis]
336
+
337
+ return wp.select(
338
+ on_side, Coords(OUTSIDE), Coords(element_coords[(axis + 1) % 3], element_coords[(axis + 2) % 3], 0.0)
339
+ )
340
+
341
+ @wp.func
342
+ def side_to_cell_arg(side_arg: SideArg):
343
+ return side_arg.cell_arg
344
+
345
+
346
+ @wp.kernel
347
+ def _cell_node_indices(
348
+ cell_ijk: wp.array(dtype=wp.vec3i),
349
+ node_ijk: wp.array2d(dtype=wp.vec3i),
350
+ ):
351
+ cell, n = wp.tid()
352
+ node_ijk[cell, n] = cell_ijk[cell] + wp.vec3i((n & 4) >> 2, (n & 2) >> 1, n & 1)
353
+
354
+
355
+ @wp.kernel
356
+ def _cell_face_indices(
357
+ cell_ijk: wp.array(dtype=wp.vec3i),
358
+ node_ijk: wp.array2d(dtype=wp.vec3i),
359
+ ):
360
+ cell = wp.tid()
361
+ ijk = cell_ijk[cell]
362
+ node_ijk[cell, 0] = _add_axis_flag(ijk, 0)
363
+ node_ijk[cell, 1] = _add_axis_flag(ijk, 1)
364
+ node_ijk[cell, 2] = _add_axis_flag(ijk, 2)
365
+
366
+ node_ijk[cell, 3] = _add_axis_flag(ijk + wp.vec3i(1, 0, 0), 0)
367
+ node_ijk[cell, 4] = _add_axis_flag(ijk + wp.vec3i(0, 1, 0), 1)
368
+ node_ijk[cell, 5] = _add_axis_flag(ijk + wp.vec3i(0, 0, 1), 2)
369
+
370
+
371
+ @wp.kernel
372
+ def _cell_edge_indices(
373
+ cell_ijk: wp.array(dtype=wp.vec3i),
374
+ edge_ijk: wp.array2d(dtype=wp.vec3i),
375
+ ):
376
+ cell = wp.tid()
377
+ ijk = cell_ijk[cell]
378
+ edge_ijk[cell, 0] = _add_axis_flag(ijk, 0)
379
+ edge_ijk[cell, 1] = _add_axis_flag(ijk, 1)
380
+ edge_ijk[cell, 2] = _add_axis_flag(ijk, 2)
381
+
382
+ edge_ijk[cell, 3] = _add_axis_flag(ijk + wp.vec3i(0, 1, 0), 0)
383
+ edge_ijk[cell, 4] = _add_axis_flag(ijk + wp.vec3i(0, 0, 1), 1)
384
+ edge_ijk[cell, 5] = _add_axis_flag(ijk + wp.vec3i(1, 0, 0), 2)
385
+
386
+ edge_ijk[cell, 6] = _add_axis_flag(ijk + wp.vec3i(0, 1, 1), 0)
387
+ edge_ijk[cell, 7] = _add_axis_flag(ijk + wp.vec3i(1, 0, 1), 1)
388
+ edge_ijk[cell, 8] = _add_axis_flag(ijk + wp.vec3i(1, 1, 0), 2)
389
+
390
+ edge_ijk[cell, 9] = _add_axis_flag(ijk + wp.vec3i(0, 0, 1), 0)
391
+ edge_ijk[cell, 10] = _add_axis_flag(ijk + wp.vec3i(1, 0, 0), 1)
392
+ edge_ijk[cell, 11] = _add_axis_flag(ijk + wp.vec3i(0, 1, 0), 2)
393
+
394
+
395
+ def _build_node_grid(cell_ijk, grid: wp.Volume, temporary_store: cache.TemporaryStore):
396
+ cell_count = cell_ijk.shape[0]
397
+
398
+ cell_nodes = cache.borrow_temporary(temporary_store, shape=(cell_count, 8), dtype=wp.vec3i, device=cell_ijk.device)
399
+ wp.launch(
400
+ _cell_node_indices, dim=cell_nodes.array.shape, inputs=[cell_ijk, cell_nodes.array], device=cell_ijk.device
401
+ )
402
+ node_grid = wp.Volume.allocate_by_voxels(
403
+ cell_nodes.array.flatten(), voxel_size=grid.get_voxel_size()[0], device=cell_ijk.device
404
+ )
405
+
406
+ return node_grid
407
+
408
+
409
+ def _build_face_grid(cell_ijk, grid: wp.Volume, temporary_store: cache.TemporaryStore):
410
+ cell_count = cell_ijk.shape[0]
411
+
412
+ cell_faces = cache.borrow_temporary(temporary_store, shape=(cell_count, 6), dtype=wp.vec3i, device=cell_ijk.device)
413
+ wp.launch(_cell_face_indices, dim=cell_count, inputs=[cell_ijk, cell_faces.array], device=cell_ijk.device)
414
+ face_grid = wp.Volume.allocate_by_voxels(
415
+ cell_faces.array.flatten(), voxel_size=grid.get_voxel_size()[0], device=cell_ijk.device
416
+ )
417
+
418
+ return face_grid
419
+
420
+
421
+ def _build_edge_grid(cell_ijk, grid: wp.Volume, temporary_store: cache.TemporaryStore):
422
+ cell_count = cell_ijk.shape[0]
423
+
424
+ cell_edges = cache.borrow_temporary(temporary_store, shape=(cell_count, 12), dtype=wp.vec3i, device=cell_ijk.device)
425
+ wp.launch(_cell_edge_indices, dim=cell_count, inputs=[cell_ijk, cell_edges.array], device=cell_ijk.device)
426
+ edge_grid = wp.Volume.allocate_by_voxels(
427
+ cell_edges.array.flatten(), voxel_size=grid.get_voxel_size()[0], device=cell_ijk.device
428
+ )
429
+
430
+ return edge_grid
431
+
432
+
433
+ @wp.kernel
434
+ def _build_face_flags(
435
+ cell_grid: wp.uint64,
436
+ face_ijk: wp.array(dtype=wp.vec3i),
437
+ face_flags: wp.array(dtype=wp.uint8),
438
+ boundary_face_mask: wp.array(dtype=int),
439
+ ):
440
+ face = wp.tid()
441
+
442
+ axis, ijk = _extract_axis_flag(face_ijk[face])
443
+
444
+ ijk_minus = ijk
445
+ ijk_minus[axis] -= 1
446
+
447
+ plus_cell_index = wp.volume_lookup_index(cell_grid, ijk[0], ijk[1], ijk[2])
448
+ minus_cell_index = wp.volume_lookup_index(cell_grid, ijk_minus[0], ijk_minus[1], ijk_minus[2])
449
+
450
+ plus_boundary = wp.uint8(wp.select(plus_cell_index == -1, 0, 1)) << FACE_OUTER_OFFSET_BIT
451
+ minus_boundary = wp.uint8(wp.select(minus_cell_index == -1, 0, 1)) << FACE_INNER_OFFSET_BIT
452
+
453
+ face_ijk[face] = ijk
454
+ face_flags[face] = wp.uint8(axis) | plus_boundary | minus_boundary
455
+ boundary_face_mask[face] = wp.select((plus_boundary | minus_boundary) == 0, 1, 0)
warp/fem/integrate.py CHANGED
@@ -9,6 +9,7 @@ from warp.fem.field import (
9
9
  DiscreteField,
10
10
  FieldLike,
11
11
  FieldRestriction,
12
+ SpaceField,
12
13
  TestField,
13
14
  TrialField,
14
15
  make_restriction,
@@ -195,7 +196,7 @@ def _get_integrand_field_arguments(
195
196
  arg_type = argspec.annotations[arg]
196
197
  if arg_type == Field:
197
198
  if arg not in fields:
198
- raise ValueError(f"Missing field for argument '{arg}'")
199
+ raise ValueError(f"Missing field for argument '{arg}' of integrand '{integrand.name}'")
199
200
  field_args[arg] = fields[arg]
200
201
  elif arg_type == Domain:
201
202
  domain_name = arg
@@ -208,6 +209,52 @@ def _get_integrand_field_arguments(
208
209
  return field_args, value_args, domain_name, sample_name
209
210
 
210
211
 
212
+ def _check_field_compat(
213
+ integrand: Integrand,
214
+ fields: Dict[str, FieldLike],
215
+ field_args: Dict[str, FieldLike],
216
+ domain: GeometryDomain = None,
217
+ ):
218
+ # Check field compatilibity
219
+ for name, field in fields.items():
220
+ if name not in field_args:
221
+ raise ValueError(
222
+ f"Passed field argument '{name}' does not match any parameter of integrand '{integrand.name}'"
223
+ )
224
+
225
+ if isinstance(field, SpaceField) and domain is not None:
226
+ space = field.space
227
+ if space.geometry != domain.geometry:
228
+ raise ValueError(f"Field '{name}' must be defined on the same geometry as the integration domain")
229
+ if space.dimension != domain.dimension:
230
+ raise ValueError(
231
+ f"Field '{name}' dimension ({space.dimension}) does not match that of the integration domain ({domain.dimension}). Maybe a forgotten `.trace()`?"
232
+ )
233
+
234
+
235
+ def _populate_value_struct(ValueStruct: wp.codegen.Struct, values: Dict[str, Any], integrand_name: str):
236
+ value_struct_values = ValueStruct()
237
+ for k, v in values.items():
238
+ try:
239
+ setattr(value_struct_values, k, v)
240
+ except Exception as err:
241
+ if k not in ValueStruct.vars:
242
+ raise ValueError(
243
+ f"Passed value argument '{k}' does not match any of the integrand '{integrand_name}' parameters"
244
+ ) from err
245
+ raise ValueError(
246
+ f"Passed value argument '{k}' of type '{wp.types.type_repr(v)}' is incompatible with the integrand '{integrand_name}' parameter of type '{wp.types.type_repr(ValueStruct.vars[k].type)}'"
247
+ ) from err
248
+
249
+ missing_values = ValueStruct.vars.keys() - values.keys()
250
+ if missing_values:
251
+ wp.utils.warn(
252
+ f"Missing values for parameter(s) '{', '.join(missing_values)}' of the integrand '{integrand_name}', will be zero-initialized"
253
+ )
254
+
255
+ return value_struct_values
256
+
257
+
211
258
  def _get_test_and_trial_fields(
212
259
  fields: Dict[str, FieldLike],
213
260
  ):
@@ -217,14 +264,17 @@ def _get_test_and_trial_fields(
217
264
  trial_name = None
218
265
 
219
266
  for name, field in fields.items():
267
+ if not isinstance(field, FieldLike):
268
+ raise ValueError(f"Passed field argument '{name}' is not a proper Field")
269
+
220
270
  if isinstance(field, TestField):
221
271
  if test is not None:
222
- raise ValueError("Duplicate test field argument")
272
+ raise ValueError(f"More than one test field argument: '{test_name}' and '{name}'")
223
273
  test = field
224
274
  test_name = name
225
275
  elif isinstance(field, TrialField):
226
276
  if trial is not None:
227
- raise ValueError("Duplicate test field argument")
277
+ raise ValueError(f"More than one trial field argument: '{trial_name}' and '{name}'")
228
278
  trial = field
229
279
  trial_name = name
230
280
 
@@ -759,6 +809,8 @@ def _generate_integrate_kernel(
759
809
 
760
810
  # Not found in cache, transform integrand and generate kernel
761
811
 
812
+ _check_field_compat(integrand, fields, field_args, domain)
813
+
762
814
  integrand_func = _translate_integrand(
763
815
  integrand,
764
816
  field_args,
@@ -843,6 +895,7 @@ def _generate_integrate_kernel(
843
895
 
844
896
 
845
897
  def _launch_integrate_kernel(
898
+ integrand: Integrand,
846
899
  kernel: wp.Kernel,
847
900
  FieldStruct: wp.codegen.Struct,
848
901
  ValueStruct: wp.codegen.Struct,
@@ -870,9 +923,7 @@ def _launch_integrate_kernel(
870
923
  for k, v in fields.items():
871
924
  setattr(field_arg_values, k, v.eval_arg_value(device=device))
872
925
 
873
- value_struct_values = ValueStruct()
874
- for k, v in values.items():
875
- setattr(value_struct_values, k, v)
926
+ value_struct_values = _populate_value_struct(ValueStruct, values, integrand_name=integrand.name)
876
927
 
877
928
  # Constant form
878
929
  if test is None and trial is None:
@@ -1211,6 +1262,7 @@ def integrate(
1211
1262
  )
1212
1263
 
1213
1264
  return _launch_integrate_kernel(
1265
+ integrand=integrand,
1214
1266
  kernel=kernel,
1215
1267
  FieldStruct=FieldStruct,
1216
1268
  ValueStruct=ValueStruct,
@@ -1428,6 +1480,8 @@ def _generate_interpolate_kernel(
1428
1480
  if kernel is not None:
1429
1481
  return kernel, FieldStruct, ValueStruct
1430
1482
 
1483
+ _check_field_compat(integrand, fields, field_args, domain)
1484
+
1431
1485
  # Generate interpolation kernel
1432
1486
  if isinstance(dest, FieldRestriction):
1433
1487
  # need to split into kernel + function for diffferentiability
@@ -1499,6 +1553,7 @@ def _generate_interpolate_kernel(
1499
1553
 
1500
1554
 
1501
1555
  def _launch_interpolate_kernel(
1556
+ integrand: Integrand,
1502
1557
  kernel: wp.kernel,
1503
1558
  FieldStruct: wp.codegen.Struct,
1504
1559
  ValueStruct: wp.codegen.Struct,
@@ -1517,9 +1572,7 @@ def _launch_interpolate_kernel(
1517
1572
  for k, v in fields.items():
1518
1573
  setattr(field_arg_values, k, v.eval_arg_value(device=device))
1519
1574
 
1520
- value_struct_values = ValueStruct()
1521
- for k, v in values.items():
1522
- setattr(value_struct_values, k, v)
1575
+ value_struct_values = _populate_value_struct(ValueStruct, values, integrand_name=integrand.name)
1523
1576
 
1524
1577
  if isinstance(dest, FieldRestriction):
1525
1578
  dest_node_arg = dest.space_restriction.node_arg(device=device)
@@ -1618,6 +1671,7 @@ def interpolate(
1618
1671
  )
1619
1672
 
1620
1673
  return _launch_interpolate_kernel(
1674
+ integrand=integrand,
1621
1675
  kernel=kernel,
1622
1676
  FieldStruct=FieldStruct,
1623
1677
  ValueStruct=ValueStruct,