warp-lang 1.3.3__py3-none-macosx_10_13_universal2.whl → 1.4.1__py3-none-macosx_10_13_universal2.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of warp-lang might be problematic. Click here for more details.

Files changed (110) hide show
  1. warp/__init__.py +6 -0
  2. warp/autograd.py +59 -6
  3. warp/bin/libwarp.dylib +0 -0
  4. warp/build_dll.py +8 -10
  5. warp/builtins.py +103 -3
  6. warp/codegen.py +447 -53
  7. warp/config.py +1 -1
  8. warp/context.py +682 -405
  9. warp/dlpack.py +2 -0
  10. warp/examples/benchmarks/benchmark_cloth.py +10 -0
  11. warp/examples/core/example_render_opengl.py +12 -10
  12. warp/examples/fem/example_adaptive_grid.py +251 -0
  13. warp/examples/fem/example_apic_fluid.py +1 -1
  14. warp/examples/fem/example_diffusion_3d.py +2 -2
  15. warp/examples/fem/example_magnetostatics.py +1 -1
  16. warp/examples/fem/example_streamlines.py +1 -0
  17. warp/examples/fem/utils.py +25 -5
  18. warp/examples/sim/example_cloth.py +50 -6
  19. warp/fem/__init__.py +2 -0
  20. warp/fem/adaptivity.py +493 -0
  21. warp/fem/field/field.py +2 -1
  22. warp/fem/field/nodal_field.py +18 -26
  23. warp/fem/field/test.py +4 -4
  24. warp/fem/field/trial.py +4 -4
  25. warp/fem/geometry/__init__.py +1 -0
  26. warp/fem/geometry/adaptive_nanogrid.py +843 -0
  27. warp/fem/geometry/nanogrid.py +55 -28
  28. warp/fem/space/__init__.py +1 -1
  29. warp/fem/space/nanogrid_function_space.py +69 -35
  30. warp/fem/utils.py +118 -107
  31. warp/jax_experimental.py +28 -15
  32. warp/native/array.h +0 -1
  33. warp/native/builtin.h +103 -6
  34. warp/native/bvh.cu +4 -2
  35. warp/native/cuda_util.cpp +14 -0
  36. warp/native/cuda_util.h +2 -0
  37. warp/native/error.cpp +4 -2
  38. warp/native/exports.h +99 -0
  39. warp/native/mat.h +97 -0
  40. warp/native/mesh.cpp +36 -0
  41. warp/native/mesh.cu +52 -1
  42. warp/native/mesh.h +1 -0
  43. warp/native/quat.h +43 -0
  44. warp/native/range.h +11 -2
  45. warp/native/spatial.h +6 -0
  46. warp/native/vec.h +74 -0
  47. warp/native/warp.cpp +2 -1
  48. warp/native/warp.cu +10 -3
  49. warp/native/warp.h +8 -1
  50. warp/paddle.py +382 -0
  51. warp/sim/__init__.py +1 -0
  52. warp/sim/collide.py +519 -0
  53. warp/sim/integrator_euler.py +18 -5
  54. warp/sim/integrator_featherstone.py +5 -5
  55. warp/sim/integrator_vbd.py +1026 -0
  56. warp/sim/integrator_xpbd.py +2 -6
  57. warp/sim/model.py +50 -25
  58. warp/sparse.py +9 -7
  59. warp/stubs.py +459 -0
  60. warp/tape.py +2 -0
  61. warp/tests/aux_test_dependent.py +1 -0
  62. warp/tests/aux_test_name_clash1.py +32 -0
  63. warp/tests/aux_test_name_clash2.py +32 -0
  64. warp/tests/aux_test_square.py +1 -0
  65. warp/tests/test_array.py +188 -0
  66. warp/tests/test_async.py +3 -3
  67. warp/tests/test_atomic.py +6 -0
  68. warp/tests/test_closest_point_edge_edge.py +93 -1
  69. warp/tests/test_codegen.py +93 -15
  70. warp/tests/test_codegen_instancing.py +1457 -0
  71. warp/tests/test_collision.py +486 -0
  72. warp/tests/test_compile_consts.py +3 -28
  73. warp/tests/test_dlpack.py +170 -0
  74. warp/tests/test_examples.py +22 -8
  75. warp/tests/test_fast_math.py +10 -4
  76. warp/tests/test_fem.py +81 -1
  77. warp/tests/test_func.py +46 -0
  78. warp/tests/test_implicit_init.py +49 -0
  79. warp/tests/test_jax.py +58 -0
  80. warp/tests/test_mat.py +84 -0
  81. warp/tests/test_mesh_query_point.py +188 -0
  82. warp/tests/test_model.py +13 -0
  83. warp/tests/test_module_hashing.py +40 -0
  84. warp/tests/test_multigpu.py +3 -3
  85. warp/tests/test_overwrite.py +8 -0
  86. warp/tests/test_paddle.py +852 -0
  87. warp/tests/test_print.py +89 -0
  88. warp/tests/test_quat.py +111 -0
  89. warp/tests/test_reload.py +31 -1
  90. warp/tests/test_scalar_ops.py +2 -0
  91. warp/tests/test_static.py +568 -0
  92. warp/tests/test_streams.py +64 -3
  93. warp/tests/test_struct.py +4 -4
  94. warp/tests/test_torch.py +24 -0
  95. warp/tests/test_triangle_closest_point.py +137 -0
  96. warp/tests/test_types.py +1 -1
  97. warp/tests/test_vbd.py +386 -0
  98. warp/tests/test_vec.py +143 -0
  99. warp/tests/test_vec_scalar_ops.py +139 -0
  100. warp/tests/unittest_suites.py +12 -0
  101. warp/tests/unittest_utils.py +9 -5
  102. warp/thirdparty/dlpack.py +3 -1
  103. warp/types.py +167 -36
  104. warp/utils.py +37 -14
  105. {warp_lang-1.3.3.dist-info → warp_lang-1.4.1.dist-info}/METADATA +10 -8
  106. {warp_lang-1.3.3.dist-info → warp_lang-1.4.1.dist-info}/RECORD +109 -97
  107. warp/tests/test_point_triangle_closest_point.py +0 -143
  108. {warp_lang-1.3.3.dist-info → warp_lang-1.4.1.dist-info}/LICENSE.md +0 -0
  109. {warp_lang-1.3.3.dist-info → warp_lang-1.4.1.dist-info}/WHEEL +0 -0
  110. {warp_lang-1.3.3.dist-info → warp_lang-1.4.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,843 @@
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
+ from .nanogrid import (
12
+ Nanogrid,
13
+ _add_axis_flag,
14
+ _build_node_grid,
15
+ _extract_axis_flag,
16
+ _get_boundary_mask,
17
+ _make_face_flags,
18
+ _mat32,
19
+ )
20
+
21
+ _FACE_LEVEL_BIT = wp.constant(wp.uint8(4)) # follows nanogrid.FACE_OUTER_OFFSET_BIT
22
+ _GRID_LEVEL_BIT = wp.constant(wp.int32(19)) # follows nanogrid.GRID_AXIS_FLAG
23
+
24
+
25
+ @wp.struct
26
+ class AdaptiveNanogridCellArg:
27
+ # Utility device functions
28
+ cell_grid: wp.uint64
29
+ cell_ijk: wp.array(dtype=wp.vec3i)
30
+ cell_level: wp.array(dtype=wp.uint8)
31
+ inverse_transform: wp.mat33
32
+ cell_volume: float
33
+ level_count: int
34
+
35
+
36
+ @wp.struct
37
+ class AdaptiveNanogridSideArg:
38
+ # Utility device functions
39
+ cell_arg: AdaptiveNanogridCellArg
40
+ face_ijk: wp.array(dtype=wp.vec3i)
41
+ face_cell_indices: wp.array(dtype=wp.vec2i)
42
+ face_flags: wp.array(dtype=wp.uint8)
43
+ face_areas: wp.vec3
44
+
45
+
46
+ class AdaptiveNanogrid(Geometry):
47
+ """Adaptive sparse grid"""
48
+
49
+ dimension = 3
50
+
51
+ def __init__(
52
+ self,
53
+ cell_grid: wp.Volume,
54
+ cell_level: wp.array,
55
+ level_count: int,
56
+ temporary_store: cache.TemporaryStore,
57
+ ):
58
+ """
59
+ Constructs an adaptive sparse grid geometry from an in-memory NanoVDB volume and a list of levels.
60
+
61
+ It is not recommended to use this constructor directly; see the helper functions :func:`warp.fem.adaptive_nanogrid_from_field` and :func:`warp.fem.adaptive_nanogrid_from_hierarchy`
62
+
63
+ Args:
64
+ cell_grid: A warp volume (ideally backed by an index grid) whose voxels coordinates correspond to the lowest fine-resolution voxel of each cell.
65
+ The cell's extent is then given by the `cell_level` array. For instance, a voxel at coordinates ``ijk`` and level ``0`` corresponds to a fine cell at the same coordinates,
66
+ a voxel at coordinates ``2*ijk`` and level ``1`` corresponds to a cell spanning ``2^3`` voxels from ``2*ijk`` to ``2*ijk + (1,1,1)``, etc.
67
+ cell_level: Refinement level for each voxel of the volume. Level 0 is the finest, level ``level_count-1`` is the coarsest.
68
+ level_count: Number of levels in the grid
69
+ """
70
+
71
+ if level_count > 8:
72
+ raise ValueError("Too many refinement levels, max 8 supported")
73
+
74
+ self.level_count = level_count
75
+ self._cell_grid = cell_grid
76
+ self._cell_level = cell_level
77
+
78
+ device = self._cell_grid.device
79
+ self._cell_ijk = wp.array(dtype=wp.vec3i, shape=(cell_grid.get_voxel_count(),), device=device)
80
+ self._cell_grid.get_voxels(out=self._cell_ijk)
81
+ self._cell_grid_info = self._cell_grid.get_grid_info()
82
+
83
+ self._node_grid = _build_node_grid(self._cell_ijk, self._cell_level, self._cell_grid, temporary_store)
84
+ node_count = self._node_grid.get_voxel_count()
85
+ self._node_ijk = wp.array(shape=(node_count,), dtype=wp.vec3i, device=device)
86
+ self._node_grid.get_voxels(out=self._node_ijk)
87
+
88
+ self._face_grid = None
89
+ self._face_ijk = None
90
+
91
+ self._stacked_edge_grid = None
92
+ self._stacked_edge_count = 0
93
+ self._stacked_face_grid = None
94
+ self._stacked_face_count = 0
95
+
96
+ @property
97
+ def cell_grid(self) -> wp.Volume:
98
+ return self._cell_grid
99
+
100
+ @property
101
+ def vertex_grid(self) -> wp.Volume:
102
+ return self._node_grid
103
+
104
+ @property
105
+ def face_grid(self) -> wp.Volume:
106
+ self._ensure_face_grid()
107
+ return self._face_grid
108
+
109
+ def cell_count(self):
110
+ return self._cell_ijk.shape[0]
111
+
112
+ def vertex_count(self):
113
+ return self._node_ijk.shape[0]
114
+
115
+ def side_count(self):
116
+ self._ensure_face_grid()
117
+ return self._face_ijk.shape[0]
118
+
119
+ def boundary_side_count(self):
120
+ self._ensure_face_grid()
121
+ return self._boundary_face_indices.shape[0]
122
+
123
+ @property
124
+ def stacked_face_grid(self) -> wp.Volume:
125
+ self._ensure_stacked_face_grid()
126
+ return self._stacked_face_grid
127
+
128
+ def stacked_face_count(self):
129
+ self._ensure_stacked_face_grid()
130
+ return self._stacked_face_count
131
+
132
+ @property
133
+ def stacked_edge_grid(self) -> wp.Volume:
134
+ self._ensure_stacked_edge_grid()
135
+ return self._stacked_edge_grid
136
+
137
+ def stacked_edge_count(self):
138
+ self._ensure_stacked_edge_grid()
139
+ return self._stacked_edge_count
140
+
141
+ def reference_cell(self) -> Cube:
142
+ return Cube()
143
+
144
+ def reference_side(self) -> Square:
145
+ return Square()
146
+
147
+ @property
148
+ def transform(self):
149
+ return np.array(self._cell_grid_info.transform_matrix).reshape(3, 3)
150
+
151
+ CellArg = AdaptiveNanogridCellArg
152
+
153
+ @cache.cached_arg_value
154
+ def cell_arg_value(self, device) -> CellArg:
155
+ args = self.CellArg()
156
+ args.cell_grid = self._cell_grid.id
157
+ args.cell_ijk = self._cell_ijk
158
+ args.cell_level = self._cell_level
159
+
160
+ transform = self.transform
161
+ args.inverse_transform = wp.mat33f(np.linalg.inv(transform))
162
+ args.cell_volume = abs(np.linalg.det(transform))
163
+ args.level_count = self.level_count
164
+
165
+ return args
166
+
167
+ @wp.func
168
+ def _cell_scale(args: CellArg, cell_index: int):
169
+ return float(1 << int(args.cell_level[cell_index]))
170
+
171
+ @wp.func
172
+ def cell_position(args: CellArg, s: Sample):
173
+ scale = AdaptiveNanogrid._cell_scale(args, s.element_index)
174
+ uvw = wp.vec3(args.cell_ijk[s.element_index]) + s.element_coords * scale
175
+ return wp.volume_index_to_world(args.cell_grid, uvw - wp.vec3(0.5))
176
+
177
+ @wp.func
178
+ def cell_deformation_gradient(args: CellArg, s: Sample):
179
+ scale = AdaptiveNanogrid._cell_scale(args, s.element_index)
180
+ return wp.inverse(args.inverse_transform) * scale
181
+
182
+ @wp.func
183
+ def cell_inverse_deformation_gradient(args: CellArg, s: Sample):
184
+ scale = AdaptiveNanogrid._cell_scale(args, s.element_index)
185
+ return args.inverse_transform / scale
186
+
187
+ @wp.func
188
+ def _make_sample(args: CellArg, cell_index: int, uvw: wp.vec3):
189
+ ijk = args.cell_ijk[cell_index]
190
+ return make_free_sample(cell_index, (uvw - wp.vec3(ijk)) / AdaptiveNanogrid._cell_scale(args, cell_index))
191
+
192
+ @wp.func
193
+ def cell_lookup(args: CellArg, pos: wp.vec3):
194
+ uvw = wp.volume_world_to_index(args.cell_grid, pos) + wp.vec3(0.5)
195
+ ijk = wp.vec3i(int(wp.floor(uvw[0])), int(wp.floor(uvw[1])), int(wp.floor(uvw[2])))
196
+ cell_index = AdaptiveNanogrid.find_cell(args.cell_grid, ijk, args.level_count, args.cell_level)
197
+
198
+ if cell_index == -1:
199
+ coords = uvw - wp.vec3(ijk)
200
+
201
+ if wp.min(coords) == 0.0 or wp.max(coords) == 1.0:
202
+ il = wp.select(coords[0] > 0.5, -1, 0)
203
+ jl = wp.select(coords[1] > 0.5, -1, 0)
204
+ kl = wp.select(coords[2] > 0.5, -1, 0)
205
+
206
+ for n in range(8):
207
+ ni = n >> 2
208
+ nj = (n & 2) >> 1
209
+ nk = n & 1
210
+ nijk = ijk + wp.vec3i(ni + il, nj + jl, nk + kl)
211
+
212
+ coords = uvw - wp.vec3(nijk)
213
+ if wp.min(coords) >= 0.0 and wp.max(coords) <= 1.0:
214
+ cell_index = AdaptiveNanogrid.find_cell(args.cell_grid, nijk, args.level_count, args.cell_level)
215
+ if cell_index != -1:
216
+ return AdaptiveNanogrid._make_sample(args, cell_index, uvw)
217
+
218
+ return make_free_sample(NULL_ELEMENT_INDEX, Coords(OUTSIDE))
219
+
220
+ return AdaptiveNanogrid._make_sample(args, cell_index, uvw)
221
+
222
+ @wp.func
223
+ def cell_lookup(args: CellArg, pos: wp.vec3, guess: Sample):
224
+ s_global = AdaptiveNanogrid.cell_lookup(args, pos)
225
+
226
+ if s_global.element_index != NULL_ELEMENT_INDEX:
227
+ return s_global
228
+
229
+ closest_voxel = int(NULL_ELEMENT_INDEX)
230
+ closest_coords = Coords(OUTSIDE)
231
+ closest_dist = float(1.0e8)
232
+
233
+ # project to closest in stencil
234
+ uvw = wp.volume_world_to_index(args.cell_grid, pos) + wp.vec3(0.5)
235
+ cell_ijk = args.cell_ijk[guess.element_index]
236
+ for ni in range(-1, 2):
237
+ for nj in range(-1, 2):
238
+ for nk in range(-1, 2):
239
+ nijk = cell_ijk + wp.vec3i(ni, nj, nk)
240
+ cell_idx = AdaptiveNanogrid.find_cell(args.cell_grid, nijk, args.level_count, args.cell_level)
241
+ if cell_idx != -1:
242
+ nijk = args.cell_ijk[cell_idx]
243
+ scale = AdaptiveNanogrid._cell_scale(args, cell_idx)
244
+ coords = (uvw - wp.vec3(nijk)) / scale
245
+ dist, proj_coords = Nanogrid._project_on_voxel_at_origin(coords)
246
+ dist *= scale
247
+ if dist <= closest_dist:
248
+ closest_dist = dist
249
+ closest_voxel = cell_idx
250
+ closest_coords = proj_coords
251
+
252
+ return make_free_sample(closest_voxel, closest_coords)
253
+
254
+ @wp.func
255
+ def cell_measure(args: CellArg, s: Sample):
256
+ scale = AdaptiveNanogrid._cell_scale(args, s.element_index)
257
+ return args.cell_volume * scale * scale * scale
258
+
259
+ @wp.func
260
+ def cell_normal(args: CellArg, s: Sample):
261
+ return wp.vec3(0.0)
262
+
263
+ SideIndexArg = Nanogrid.SideIndexArg
264
+ side_index_arg_value = Nanogrid.side_index_arg_value
265
+
266
+ SideArg = AdaptiveNanogridSideArg
267
+
268
+ @wp.func
269
+ def side_to_cell_arg(side_arg: SideArg):
270
+ return side_arg.cell_arg
271
+
272
+ @cache.cached_arg_value
273
+ def side_arg_value(self, device) -> SideArg:
274
+ self._ensure_face_grid()
275
+
276
+ args = self.SideArg()
277
+ args.cell_arg = self.cell_arg_value(device)
278
+ args.face_ijk = self._face_ijk.to(device)
279
+ args.face_flags = self._face_flags.to(device)
280
+ args.face_cell_indices = self._face_cell_indices.to(device)
281
+ transform = self.transform
282
+ args.face_areas = wp.vec3(
283
+ tuple(np.linalg.norm(np.cross(transform[:, k - 2], transform[:, k - 1])) for k in range(3))
284
+ )
285
+
286
+ return args
287
+
288
+ @wp.func
289
+ def boundary_side_index(args: SideIndexArg, boundary_side_index: int):
290
+ return args.boundary_face_indices[boundary_side_index]
291
+
292
+ @wp.func
293
+ def _get_face_level(flags: wp.uint8):
294
+ return wp.int32(flags >> _FACE_LEVEL_BIT)
295
+
296
+ @wp.func
297
+ def _get_face_scale(flags: wp.uint8):
298
+ return float(1 << AdaptiveNanogrid._get_face_level(flags))
299
+
300
+ @wp.func
301
+ def side_position(args: SideArg, s: Sample):
302
+ ijk = args.face_ijk[s.element_index]
303
+ flags = args.face_flags[s.element_index]
304
+ axis = Nanogrid._get_face_axis(flags)
305
+ scale = AdaptiveNanogrid._get_face_scale(flags)
306
+
307
+ uvw = wp.vec3(ijk) + scale * Nanogrid._side_to_cell_coords(axis, 0.0, s.element_coords)
308
+
309
+ cell_grid = args.cell_arg.cell_grid
310
+ return wp.volume_index_to_world(cell_grid, uvw - wp.vec3(0.5))
311
+
312
+ @wp.func
313
+ def side_deformation_gradient(args: SideArg, s: Sample):
314
+ flags = args.face_flags[s.element_index]
315
+ axis = Nanogrid._get_face_axis(flags)
316
+ flip = Nanogrid._get_face_inner_offset(flags)
317
+ scale = AdaptiveNanogrid._get_face_scale(flags)
318
+ v1, v2 = Nanogrid._face_tangent_vecs(args.cell_arg.cell_grid, axis, flip)
319
+ return _mat32(v1, v2) * scale
320
+
321
+ @wp.func
322
+ def side_inner_inverse_deformation_gradient(args: SideArg, s: Sample):
323
+ s_cell = make_free_sample(AdaptiveNanogrid.side_inner_cell_index(args, s.element_index), Coords())
324
+ return AdaptiveNanogrid.cell_inverse_deformation_gradient(args.cell_arg, s_cell)
325
+
326
+ @wp.func
327
+ def side_outer_inverse_deformation_gradient(args: SideArg, s: Sample):
328
+ s_cell = make_free_sample(AdaptiveNanogrid.side_outer_cell_index(args, s.element_index), Coords())
329
+ return AdaptiveNanogrid.cell_inverse_deformation_gradient(args.cell_arg, s_cell)
330
+
331
+ @wp.func
332
+ def side_measure(args: SideArg, s: Sample):
333
+ flags = args.face_flags[s.element_index]
334
+ axis = Nanogrid._get_face_axis(flags)
335
+ scale = AdaptiveNanogrid._get_face_scale(flags)
336
+ return args.face_areas[axis] * scale * scale
337
+
338
+ @wp.func
339
+ def side_measure_ratio(args: SideArg, s: Sample):
340
+ flags = args.face_flags[s.element_index]
341
+ axis = Nanogrid._get_face_axis(flags)
342
+ scale = AdaptiveNanogrid._get_face_scale(flags)
343
+ return args.face_areas[axis] / (args.cell_arg.cell_volume * scale)
344
+
345
+ @wp.func
346
+ def side_normal(args: SideArg, s: Sample):
347
+ flags = args.face_flags[s.element_index]
348
+ axis = Nanogrid._get_face_axis(flags)
349
+ flip = Nanogrid._get_face_inner_offset(flags)
350
+
351
+ v1, v2 = Nanogrid._face_tangent_vecs(args.cell_arg.cell_grid, axis, flip)
352
+ return wp.cross(v1, v2) / args.face_areas[axis]
353
+
354
+ @wp.func
355
+ def side_inner_cell_index(args: SideArg, side_index: ElementIndex):
356
+ return args.face_cell_indices[side_index][0]
357
+
358
+ @wp.func
359
+ def side_outer_cell_index(args: SideArg, side_index: ElementIndex):
360
+ return args.face_cell_indices[side_index][1]
361
+
362
+ @wp.func
363
+ def _coarse_cell_coords(
364
+ fine_ijk: wp.vec3i,
365
+ fine_level: int,
366
+ fine_coords: Coords,
367
+ coarse_ijk: wp.vec3i,
368
+ coarse_level: int,
369
+ ):
370
+ return (wp.vec3f(fine_ijk - coarse_ijk) + fine_coords * float(1 << fine_level)) / float(1 << coarse_level)
371
+
372
+ @wp.func
373
+ def side_inner_cell_coords(args: SideArg, side_index: ElementIndex, side_coords: Coords):
374
+ flags = args.face_flags[side_index]
375
+ axis = Nanogrid._get_face_axis(flags)
376
+ offset = Nanogrid._get_face_inner_offset(flags)
377
+
378
+ same_level_cell_coords = Nanogrid._side_to_cell_coords(axis, 1.0 - float(offset), side_coords)
379
+ same_level_cell_ijk = args.face_ijk[side_index]
380
+ side_level = AdaptiveNanogrid._get_face_level(flags)
381
+ same_level_cell_ijk[axis] += (offset - 1) << side_level
382
+
383
+ cell_index = AdaptiveNanogrid.side_inner_cell_index(args, side_index)
384
+ cell_level = int(args.cell_arg.cell_level[cell_index])
385
+ cell_ijk = args.cell_arg.cell_ijk[cell_index]
386
+
387
+ return AdaptiveNanogrid._coarse_cell_coords(
388
+ same_level_cell_ijk, side_level, same_level_cell_coords, cell_ijk, cell_level
389
+ )
390
+
391
+ @wp.func
392
+ def side_outer_cell_coords(args: SideArg, side_index: ElementIndex, side_coords: Coords):
393
+ flags = args.face_flags[side_index]
394
+ axis = Nanogrid._get_face_axis(flags)
395
+ offset = Nanogrid._get_face_outer_offset(flags)
396
+
397
+ same_level_cell_coords = Nanogrid._side_to_cell_coords(axis, float(offset), side_coords)
398
+ same_level_cell_ijk = args.face_ijk[side_index]
399
+ side_level = AdaptiveNanogrid._get_face_level(flags)
400
+ same_level_cell_ijk[axis] -= offset << side_level
401
+
402
+ cell_index = AdaptiveNanogrid.side_outer_cell_index(args, side_index)
403
+ cell_level = int(args.cell_arg.cell_level[cell_index])
404
+ cell_ijk = args.cell_arg.cell_ijk[cell_index]
405
+
406
+ return AdaptiveNanogrid._coarse_cell_coords(
407
+ same_level_cell_ijk, side_level, same_level_cell_coords, cell_ijk, cell_level
408
+ )
409
+
410
+ @wp.func
411
+ def side_from_cell_coords(
412
+ args: SideArg,
413
+ side_index: ElementIndex,
414
+ element_index: ElementIndex,
415
+ element_coords: Coords,
416
+ ):
417
+ flags = args.face_flags[side_index]
418
+ axis = Nanogrid._get_face_axis(flags)
419
+ side_level = AdaptiveNanogrid._get_face_level(flags)
420
+ cell_level = int(args.cell_arg.cell_level[element_index])
421
+
422
+ cell_ijk = args.cell_arg.cell_ijk[element_index]
423
+ side_ijk = args.face_ijk[side_index]
424
+
425
+ same_level_cell_coords = AdaptiveNanogrid._coarse_cell_coords(
426
+ cell_ijk, cell_level, element_coords, side_ijk, side_level
427
+ )
428
+
429
+ on_side = (
430
+ same_level_cell_coords[axis] == 0.0
431
+ and wp.min(same_level_cell_coords) >= 0.0
432
+ and wp.max(same_level_cell_coords) <= 1.0
433
+ )
434
+
435
+ return wp.select(
436
+ on_side,
437
+ Coords(OUTSIDE),
438
+ Coords(same_level_cell_coords[(axis + 1) % 3], same_level_cell_coords[(axis + 2) % 3], 0.0),
439
+ )
440
+
441
+ def _build_face_grid(self, temporary_store: Optional[cache.TemporaryStore] = None):
442
+ device = self._cell_grid.device
443
+
444
+ # Create a first grid with faces from cells
445
+ cell_face_grid = _build_cell_face_grid(self._cell_ijk, self._cell_level, self._cell_grid, temporary_store)
446
+
447
+ # Complete faces at resolution boundaries
448
+ self._face_grid = _build_completed_face_grid(
449
+ cell_face_grid, self._cell_grid, self.level_count, self._cell_level, temporary_store
450
+ )
451
+
452
+ face_count = self._face_grid.get_voxel_count()
453
+ self._face_ijk = wp.array(shape=(face_count,), dtype=wp.vec3i, device=device)
454
+ self._face_grid.get_voxels(out=self._face_ijk)
455
+
456
+ # Finalize our faces, cache flags and neighbour indices
457
+ self._face_cell_indices = wp.array(shape=(face_count,), dtype=wp.vec2i, device=device)
458
+ self._face_flags = wp.array(shape=(face_count,), dtype=wp.uint8, device=device)
459
+ boundary_face_mask = cache.borrow_temporary(temporary_store, shape=(face_count,), dtype=wp.int32, device=device)
460
+
461
+ wp.launch(
462
+ _build_face_indices_and_flags,
463
+ dim=face_count,
464
+ device=device,
465
+ inputs=[
466
+ self._cell_grid.id,
467
+ self.level_count,
468
+ self._cell_level,
469
+ self._face_ijk,
470
+ self._face_cell_indices,
471
+ self._face_flags,
472
+ boundary_face_mask.array,
473
+ ],
474
+ )
475
+ boundary_face_indices, _ = utils.masked_indices(boundary_face_mask.array)
476
+ self._boundary_face_indices = boundary_face_indices.detach()
477
+
478
+ def _ensure_face_grid(self):
479
+ if self._face_ijk is None:
480
+ self._build_face_grid()
481
+
482
+ def _ensure_stacked_edge_grid(self):
483
+ if self._stacked_edge_grid is None:
484
+ self._stacked_edge_grid = _build_stacked_edge_grid(
485
+ self._cell_ijk, self._cell_level, self._cell_grid, temporary_store=None
486
+ )
487
+ self._stacked_edge_count = self._stacked_edge_grid.get_voxel_count()
488
+
489
+ def _ensure_stacked_face_grid(self):
490
+ if self._stacked_face_grid is None:
491
+ self._stacked_face_grid = _build_stacked_face_grid(
492
+ self._cell_ijk, self._cell_level, self._cell_grid, temporary_store=None
493
+ )
494
+ self._stacked_face_count = self._stacked_face_grid.get_voxel_count()
495
+
496
+ @wp.func
497
+ def coarse_ijk(ijk: wp.vec3i, level: int):
498
+ # technically implementation-defined, but
499
+ # right-shifting negative numbers 1-fills on all our platforms
500
+ return wp.vec3i(ijk[0] >> level, ijk[1] >> level, ijk[2] >> level)
501
+
502
+ @wp.func
503
+ def fine_ijk(ijk: wp.vec3i, level: int):
504
+ # Our coords cannot exceed 1<<21,so no worries about overwriting the sign bit
505
+ return wp.vec3i(ijk[0] << level, ijk[1] << level, ijk[2] << level)
506
+
507
+ @wp.func
508
+ def encode_axis_and_level(ijk: wp.vec3i, axis: int, level: int):
509
+ # Embed a 3-bit level in the voxel coordinates
510
+ # by switching the _GRID_LEVEL_BIT for each axis
511
+
512
+ for ax in range(3):
513
+ coord = ijk[ax]
514
+ level_flag = ((level >> ax) & 1) << _GRID_LEVEL_BIT
515
+ ijk[ax] = wp.select(coord < 0, coord | level_flag, coord & ~level_flag)
516
+
517
+ return _add_axis_flag(ijk, axis)
518
+
519
+ @wp.func
520
+ def find_cell(
521
+ cell_grid: wp.uint64,
522
+ ijk: wp.vec3i,
523
+ level_count: int,
524
+ cell_level: wp.array(dtype=wp.uint8),
525
+ ):
526
+ for l in range(level_count):
527
+ mask = ~((1 << l) - 1)
528
+ cell_index = wp.volume_lookup_index(cell_grid, ijk[0] & mask, ijk[1] & mask, ijk[2] & mask)
529
+ if cell_index != -1:
530
+ if int(cell_level[cell_index]) >= l:
531
+ return cell_index
532
+
533
+ return -1
534
+
535
+
536
+ @wp.kernel
537
+ def _cell_node_indices(
538
+ cell_ijk: wp.array(dtype=wp.vec3i),
539
+ cell_level: wp.array(dtype=wp.uint8),
540
+ node_ijk: wp.array2d(dtype=wp.vec3i),
541
+ ):
542
+ cell, n = wp.tid()
543
+ level = int(cell_level[cell])
544
+ offset = AdaptiveNanogrid.fine_ijk(wp.vec3i((n & 4) >> 2, (n & 2) >> 1, n & 1), level)
545
+ node_ijk[cell, n] = cell_ijk[cell] + offset
546
+
547
+
548
+ @wp.kernel
549
+ def _cell_face_indices(
550
+ cell_ijk: wp.array(dtype=wp.vec3i),
551
+ cell_level: wp.array(dtype=wp.uint8),
552
+ node_ijk: wp.array2d(dtype=wp.vec3i),
553
+ ):
554
+ cell = wp.tid()
555
+ ijk = cell_ijk[cell]
556
+ node_ijk[cell, 0] = _add_axis_flag(ijk, 0)
557
+ node_ijk[cell, 1] = _add_axis_flag(ijk, 1)
558
+ node_ijk[cell, 2] = _add_axis_flag(ijk, 2)
559
+
560
+ offset = 1 << int(cell_level[cell])
561
+
562
+ node_ijk[cell, 3] = _add_axis_flag(ijk + wp.vec3i(offset, 0, 0), 0)
563
+ node_ijk[cell, 4] = _add_axis_flag(ijk + wp.vec3i(0, offset, 0), 1)
564
+ node_ijk[cell, 5] = _add_axis_flag(ijk + wp.vec3i(0, 0, offset), 2)
565
+
566
+
567
+ @wp.kernel
568
+ def _cell_stacked_face_indices(
569
+ cell_ijk: wp.array(dtype=wp.vec3i),
570
+ cell_level: wp.array(dtype=wp.uint8),
571
+ node_ijk: wp.array2d(dtype=wp.vec3i),
572
+ ):
573
+ cell = wp.tid()
574
+
575
+ level = int(cell_level[cell])
576
+ ijk = AdaptiveNanogrid.coarse_ijk(cell_ijk[cell], level)
577
+
578
+ node_ijk[cell, 0] = AdaptiveNanogrid.encode_axis_and_level(ijk, 0, level)
579
+ node_ijk[cell, 1] = AdaptiveNanogrid.encode_axis_and_level(ijk, 1, level)
580
+ node_ijk[cell, 2] = AdaptiveNanogrid.encode_axis_and_level(ijk, 2, level)
581
+
582
+ node_ijk[cell, 3] = AdaptiveNanogrid.encode_axis_and_level(ijk + wp.vec3i(1, 0, 0), 0, level)
583
+ node_ijk[cell, 4] = AdaptiveNanogrid.encode_axis_and_level(ijk + wp.vec3i(0, 1, 0), 1, level)
584
+ node_ijk[cell, 5] = AdaptiveNanogrid.encode_axis_and_level(ijk + wp.vec3i(0, 0, 1), 2, level)
585
+
586
+
587
+ @wp.kernel
588
+ def _cell_stacked_edge_indices(
589
+ cell_ijk: wp.array(dtype=wp.vec3i),
590
+ cell_level: wp.array(dtype=wp.uint8),
591
+ edge_ijk: wp.array2d(dtype=wp.vec3i),
592
+ ):
593
+ cell = wp.tid()
594
+ level = int(cell_level[cell])
595
+ ijk = AdaptiveNanogrid.coarse_ijk(cell_ijk[cell], level)
596
+
597
+ edge_ijk[cell, 0] = AdaptiveNanogrid.encode_axis_and_level(ijk, 0, level)
598
+ edge_ijk[cell, 1] = AdaptiveNanogrid.encode_axis_and_level(ijk, 1, level)
599
+ edge_ijk[cell, 2] = AdaptiveNanogrid.encode_axis_and_level(ijk, 2, level)
600
+
601
+ edge_ijk[cell, 3] = AdaptiveNanogrid.encode_axis_and_level(ijk + wp.vec3i(0, 1, 0), 0, level)
602
+ edge_ijk[cell, 4] = AdaptiveNanogrid.encode_axis_and_level(ijk + wp.vec3i(0, 0, 1), 1, level)
603
+ edge_ijk[cell, 5] = AdaptiveNanogrid.encode_axis_and_level(ijk + wp.vec3i(1, 0, 0), 2, level)
604
+
605
+ edge_ijk[cell, 6] = AdaptiveNanogrid.encode_axis_and_level(ijk + wp.vec3i(0, 1, 1), 0, level)
606
+ edge_ijk[cell, 7] = AdaptiveNanogrid.encode_axis_and_level(ijk + wp.vec3i(1, 0, 1), 1, level)
607
+ edge_ijk[cell, 8] = AdaptiveNanogrid.encode_axis_and_level(ijk + wp.vec3i(1, 1, 0), 2, level)
608
+
609
+ edge_ijk[cell, 9] = AdaptiveNanogrid.encode_axis_and_level(ijk + wp.vec3i(0, 0, 1), 0, level)
610
+ edge_ijk[cell, 10] = AdaptiveNanogrid.encode_axis_and_level(ijk + wp.vec3i(1, 0, 0), 1, level)
611
+ edge_ijk[cell, 11] = AdaptiveNanogrid.encode_axis_and_level(ijk + wp.vec3i(0, 1, 0), 2, level)
612
+
613
+
614
+ def _build_node_grid(cell_ijk, cell_level, cell_grid: wp.Volume, temporary_store: cache.TemporaryStore):
615
+ cell_count = cell_ijk.shape[0]
616
+
617
+ cell_nodes = cache.borrow_temporary(temporary_store, shape=(cell_count, 8), dtype=wp.vec3i, device=cell_ijk.device)
618
+ wp.launch(
619
+ _cell_node_indices,
620
+ dim=cell_nodes.array.shape,
621
+ inputs=[cell_ijk, cell_level, cell_nodes.array],
622
+ device=cell_ijk.device,
623
+ )
624
+ node_grid = wp.Volume.allocate_by_voxels(
625
+ cell_nodes.array.flatten(), voxel_size=cell_grid.get_voxel_size()[0], device=cell_ijk.device
626
+ )
627
+
628
+ return node_grid
629
+
630
+
631
+ def _build_cell_face_grid(cell_ijk, cell_level, grid: wp.Volume, temporary_store: cache.TemporaryStore):
632
+ cell_count = cell_ijk.shape[0]
633
+
634
+ cell_faces = cache.borrow_temporary(temporary_store, shape=(cell_count, 6), dtype=wp.vec3i, device=cell_ijk.device)
635
+ wp.launch(
636
+ _cell_face_indices, dim=cell_count, inputs=[cell_ijk, cell_level, cell_faces.array], device=cell_ijk.device
637
+ )
638
+ face_grid = wp.Volume.allocate_by_voxels(
639
+ cell_faces.array.flatten(), voxel_size=grid.get_voxel_size()[0], device=cell_ijk.device
640
+ )
641
+
642
+ return face_grid
643
+
644
+
645
+ def _build_completed_face_grid(
646
+ cell_face_grid: wp.Volume,
647
+ cell_grid: wp.Volume,
648
+ level_count: int,
649
+ cell_level: wp.array,
650
+ temporary_store: cache.TemporaryStore,
651
+ ):
652
+ device = cell_grid.device
653
+
654
+ cell_face_count = cell_face_grid.get_voxel_count()
655
+ cell_face_ijk = cache.borrow_temporary(temporary_store, shape=(cell_face_count,), dtype=wp.vec3i, device=device)
656
+
657
+ additional_face_count = cache.borrow_temporary(temporary_store, shape=1, dtype=int, device=device)
658
+
659
+ # Count the number of supplemental faces we need to add at resolution boundaries
660
+ cell_face_grid.get_voxels(out=cell_face_ijk.array)
661
+ additional_face_count.array.zero_()
662
+ wp.launch(
663
+ _count_multires_faces,
664
+ dim=cell_face_count,
665
+ device=device,
666
+ inputs=[
667
+ cell_grid.id,
668
+ level_count,
669
+ cell_level,
670
+ cell_face_ijk.array,
671
+ additional_face_count.array,
672
+ ],
673
+ )
674
+
675
+ # Cat these new faces with the original ones
676
+ cat_face_count = cell_face_count + int(additional_face_count.array.numpy()[0])
677
+ cat_face_ijk = cache.borrow_temporary(temporary_store, shape=(cat_face_count,), dtype=wp.vec3i, device=device)
678
+ wp.copy(src=cell_face_ijk.array, dest=cat_face_ijk.array, dest_offset=cat_face_count - cell_face_count)
679
+
680
+ wp.launch(
681
+ _fill_multires_faces,
682
+ dim=cell_face_count,
683
+ device=device,
684
+ inputs=[
685
+ cell_grid.id,
686
+ level_count,
687
+ cell_level,
688
+ cell_face_ijk.array,
689
+ additional_face_count.array,
690
+ cat_face_ijk.array,
691
+ ],
692
+ )
693
+
694
+ # Now recreate a new grid with all those faces
695
+ face_grid = wp.Volume.allocate_by_voxels(
696
+ cat_face_ijk.array.flatten(), voxel_size=cell_face_grid.get_voxel_size(), device=device
697
+ )
698
+
699
+ return face_grid
700
+
701
+
702
+ def _build_stacked_face_grid(cell_ijk, cell_level, grid: wp.Volume, temporary_store: cache.TemporaryStore):
703
+ cell_count = cell_ijk.shape[0]
704
+
705
+ cell_faces = cache.borrow_temporary(temporary_store, shape=(cell_count, 6), dtype=wp.vec3i, device=cell_ijk.device)
706
+ wp.launch(
707
+ _cell_stacked_face_indices,
708
+ dim=cell_count,
709
+ inputs=[cell_ijk, cell_level, cell_faces.array],
710
+ device=cell_ijk.device,
711
+ )
712
+ face_grid = wp.Volume.allocate_by_voxels(
713
+ cell_faces.array.flatten(), voxel_size=grid.get_voxel_size()[0], device=cell_ijk.device
714
+ )
715
+
716
+ return face_grid
717
+
718
+
719
+ def _build_stacked_edge_grid(cell_ijk, cell_level, grid: wp.Volume, temporary_store: cache.TemporaryStore):
720
+ cell_count = cell_ijk.shape[0]
721
+
722
+ cell_edges = cache.borrow_temporary(temporary_store, shape=(cell_count, 12), dtype=wp.vec3i, device=cell_ijk.device)
723
+ wp.launch(
724
+ _cell_stacked_edge_indices,
725
+ dim=cell_count,
726
+ inputs=[cell_ijk, cell_level, cell_edges.array],
727
+ device=cell_ijk.device,
728
+ )
729
+ edge_grid = wp.Volume.allocate_by_voxels(
730
+ cell_edges.array.flatten(), voxel_size=grid.get_voxel_size()[0], device=cell_ijk.device
731
+ )
732
+
733
+ return edge_grid
734
+
735
+
736
+ @wp.func
737
+ def _find_face_neighbours(
738
+ cell_grid: wp.uint64,
739
+ ijk: wp.vec3i,
740
+ axis: int,
741
+ level_count: int,
742
+ cell_level: wp.array(dtype=wp.uint8),
743
+ ):
744
+ ijk_minus = ijk
745
+ ijk_minus[axis] -= 1
746
+
747
+ plus_cell_index = AdaptiveNanogrid.find_cell(cell_grid, ijk, level_count, cell_level)
748
+ minus_cell_index = AdaptiveNanogrid.find_cell(cell_grid, ijk_minus, level_count, cell_level)
749
+ return plus_cell_index, minus_cell_index
750
+
751
+
752
+ @wp.kernel
753
+ def _count_multires_faces(
754
+ cell_grid: wp.uint64,
755
+ level_count: int,
756
+ cell_level: wp.array(dtype=wp.uint8),
757
+ face_ijk: wp.array(dtype=wp.vec3i),
758
+ count: wp.array(dtype=int),
759
+ ):
760
+ face = wp.tid()
761
+
762
+ axis, ijk = _extract_axis_flag(face_ijk[face])
763
+
764
+ plus_cell_index, minus_cell_index = _find_face_neighbours(cell_grid, ijk, axis, level_count, cell_level)
765
+
766
+ if plus_cell_index == -1 or minus_cell_index == -1:
767
+ return
768
+
769
+ plus_level = int(cell_level[plus_cell_index])
770
+ minus_level = int(cell_level[minus_cell_index])
771
+ level_diff = wp.abs(plus_level - minus_level)
772
+
773
+ if level_diff != 0:
774
+ fine_face_count = 1 << (2 * level_diff)
775
+ wp.atomic_add(count, 0, fine_face_count)
776
+
777
+
778
+ @wp.kernel
779
+ def _fill_multires_faces(
780
+ cell_grid: wp.uint64,
781
+ level_count: int,
782
+ cell_level: wp.array(dtype=wp.uint8),
783
+ face_ijk: wp.array(dtype=wp.vec3i),
784
+ count: wp.array(dtype=int),
785
+ added_ijk: wp.array(dtype=wp.vec3i),
786
+ ):
787
+ face = wp.tid()
788
+
789
+ axis, ijk = _extract_axis_flag(face_ijk[face])
790
+ plus_cell_index, minus_cell_index = _find_face_neighbours(cell_grid, ijk, axis, level_count, cell_level)
791
+
792
+ if plus_cell_index == -1 or minus_cell_index == -1:
793
+ return
794
+
795
+ plus_level = int(cell_level[plus_cell_index])
796
+ minus_level = int(cell_level[minus_cell_index])
797
+ level_diff = wp.abs(plus_level - minus_level)
798
+
799
+ if level_diff != 0:
800
+ fine_face_count = 1 << (2 * level_diff)
801
+ side_mask = (1 << level_diff) - 1
802
+
803
+ fine_level = min(plus_level, minus_level)
804
+ base_level = max(plus_level, minus_level)
805
+
806
+ base_mask = ~((1 << base_level) - 1)
807
+ base_ijk = wp.vec3i(ijk[0] & base_mask, ijk[1] & base_mask, ijk[2] & base_mask)
808
+
809
+ offset = wp.atomic_sub(count, 0, fine_face_count) - fine_face_count
810
+ for f in range(fine_face_count):
811
+ f_ijk = base_ijk
812
+ f_ijk[(axis + 1) % 3] |= (f & side_mask) << fine_level
813
+ f_ijk[(axis + 2) % 3] |= (f >> level_diff) << fine_level
814
+ added_ijk[offset + f] = _add_axis_flag(f_ijk, axis)
815
+
816
+
817
+ @wp.kernel
818
+ def _build_face_indices_and_flags(
819
+ cell_grid: wp.uint64,
820
+ level_count: int,
821
+ cell_level: wp.array(dtype=wp.uint8),
822
+ face_ijk: wp.array(dtype=wp.vec3i),
823
+ face_cell_indices: wp.array(dtype=wp.vec2i),
824
+ face_flags: wp.array(dtype=wp.uint8),
825
+ boundary_face_mask: wp.array(dtype=int),
826
+ ):
827
+ face = wp.tid()
828
+
829
+ axis, ijk = _extract_axis_flag(face_ijk[face])
830
+
831
+ plus_cell_index, minus_cell_index = _find_face_neighbours(cell_grid, ijk, axis, level_count, cell_level)
832
+
833
+ inner_cell = wp.select(minus_cell_index == -1, minus_cell_index, plus_cell_index)
834
+ outer_cell = wp.select(plus_cell_index == -1, plus_cell_index, minus_cell_index)
835
+
836
+ face_level = wp.min(cell_level[inner_cell], cell_level[outer_cell])
837
+
838
+ face_ijk[face] = ijk
839
+ flags = _make_face_flags(axis, plus_cell_index, minus_cell_index) | (face_level << _FACE_LEVEL_BIT)
840
+ face_flags[face] = flags
841
+ boundary_face_mask[face] = _get_boundary_mask(flags)
842
+
843
+ face_cell_indices[face] = wp.vec2i(inner_cell, outer_cell)