warp-lang 1.2.2__py3-none-macosx_10_13_universal2.whl → 1.3.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.
Files changed (193) hide show
  1. warp/__init__.py +8 -6
  2. warp/autograd.py +823 -0
  3. warp/bin/libwarp.dylib +0 -0
  4. warp/build.py +6 -2
  5. warp/builtins.py +1412 -888
  6. warp/codegen.py +503 -166
  7. warp/config.py +48 -18
  8. warp/context.py +400 -198
  9. warp/dlpack.py +8 -0
  10. warp/examples/assets/bunny.usd +0 -0
  11. warp/examples/benchmarks/benchmark_cloth_warp.py +1 -1
  12. warp/examples/benchmarks/benchmark_interop_torch.py +158 -0
  13. warp/examples/benchmarks/benchmark_launches.py +1 -1
  14. warp/examples/core/example_cupy.py +78 -0
  15. warp/examples/fem/example_apic_fluid.py +17 -36
  16. warp/examples/fem/example_burgers.py +9 -18
  17. warp/examples/fem/example_convection_diffusion.py +7 -17
  18. warp/examples/fem/example_convection_diffusion_dg.py +27 -47
  19. warp/examples/fem/example_deformed_geometry.py +11 -22
  20. warp/examples/fem/example_diffusion.py +7 -18
  21. warp/examples/fem/example_diffusion_3d.py +24 -28
  22. warp/examples/fem/example_diffusion_mgpu.py +7 -14
  23. warp/examples/fem/example_magnetostatics.py +190 -0
  24. warp/examples/fem/example_mixed_elasticity.py +111 -80
  25. warp/examples/fem/example_navier_stokes.py +30 -34
  26. warp/examples/fem/example_nonconforming_contact.py +290 -0
  27. warp/examples/fem/example_stokes.py +17 -32
  28. warp/examples/fem/example_stokes_transfer.py +12 -21
  29. warp/examples/fem/example_streamlines.py +350 -0
  30. warp/examples/fem/utils.py +936 -0
  31. warp/fabric.py +5 -2
  32. warp/fem/__init__.py +13 -3
  33. warp/fem/cache.py +161 -11
  34. warp/fem/dirichlet.py +37 -28
  35. warp/fem/domain.py +105 -14
  36. warp/fem/field/__init__.py +14 -3
  37. warp/fem/field/field.py +454 -11
  38. warp/fem/field/nodal_field.py +33 -18
  39. warp/fem/geometry/deformed_geometry.py +50 -15
  40. warp/fem/geometry/hexmesh.py +12 -24
  41. warp/fem/geometry/nanogrid.py +106 -31
  42. warp/fem/geometry/quadmesh_2d.py +6 -11
  43. warp/fem/geometry/tetmesh.py +103 -61
  44. warp/fem/geometry/trimesh_2d.py +98 -47
  45. warp/fem/integrate.py +231 -186
  46. warp/fem/operator.py +14 -9
  47. warp/fem/quadrature/pic_quadrature.py +35 -9
  48. warp/fem/quadrature/quadrature.py +119 -32
  49. warp/fem/space/basis_space.py +98 -22
  50. warp/fem/space/collocated_function_space.py +3 -1
  51. warp/fem/space/function_space.py +7 -2
  52. warp/fem/space/grid_2d_function_space.py +3 -3
  53. warp/fem/space/grid_3d_function_space.py +4 -4
  54. warp/fem/space/hexmesh_function_space.py +3 -2
  55. warp/fem/space/nanogrid_function_space.py +12 -14
  56. warp/fem/space/partition.py +45 -47
  57. warp/fem/space/restriction.py +19 -16
  58. warp/fem/space/shape/cube_shape_function.py +91 -3
  59. warp/fem/space/shape/shape_function.py +7 -0
  60. warp/fem/space/shape/square_shape_function.py +32 -0
  61. warp/fem/space/shape/tet_shape_function.py +11 -7
  62. warp/fem/space/shape/triangle_shape_function.py +10 -1
  63. warp/fem/space/topology.py +116 -42
  64. warp/fem/types.py +8 -1
  65. warp/fem/utils.py +301 -83
  66. warp/native/array.h +16 -0
  67. warp/native/builtin.h +0 -15
  68. warp/native/cuda_util.cpp +14 -6
  69. warp/native/exports.h +1348 -1308
  70. warp/native/quat.h +79 -0
  71. warp/native/rand.h +27 -4
  72. warp/native/sparse.cpp +83 -81
  73. warp/native/sparse.cu +381 -453
  74. warp/native/vec.h +64 -0
  75. warp/native/volume.cpp +40 -49
  76. warp/native/volume_builder.cu +2 -3
  77. warp/native/volume_builder.h +12 -17
  78. warp/native/warp.cu +3 -3
  79. warp/native/warp.h +69 -59
  80. warp/render/render_opengl.py +17 -9
  81. warp/sim/articulation.py +117 -17
  82. warp/sim/collide.py +35 -29
  83. warp/sim/model.py +123 -18
  84. warp/sim/render.py +3 -1
  85. warp/sparse.py +867 -203
  86. warp/stubs.py +312 -541
  87. warp/tape.py +29 -1
  88. warp/tests/disabled_kinematics.py +1 -1
  89. warp/tests/test_adam.py +1 -1
  90. warp/tests/test_arithmetic.py +1 -1
  91. warp/tests/test_array.py +58 -1
  92. warp/tests/test_array_reduce.py +1 -1
  93. warp/tests/test_async.py +1 -1
  94. warp/tests/test_atomic.py +1 -1
  95. warp/tests/test_bool.py +1 -1
  96. warp/tests/test_builtins_resolution.py +1 -1
  97. warp/tests/test_bvh.py +6 -1
  98. warp/tests/test_closest_point_edge_edge.py +1 -1
  99. warp/tests/test_codegen.py +91 -1
  100. warp/tests/test_compile_consts.py +1 -1
  101. warp/tests/test_conditional.py +1 -1
  102. warp/tests/test_copy.py +1 -1
  103. warp/tests/test_ctypes.py +1 -1
  104. warp/tests/test_dense.py +1 -1
  105. warp/tests/test_devices.py +1 -1
  106. warp/tests/test_dlpack.py +1 -1
  107. warp/tests/test_examples.py +33 -4
  108. warp/tests/test_fabricarray.py +5 -2
  109. warp/tests/test_fast_math.py +1 -1
  110. warp/tests/test_fem.py +213 -6
  111. warp/tests/test_fp16.py +1 -1
  112. warp/tests/test_func.py +1 -1
  113. warp/tests/test_future_annotations.py +90 -0
  114. warp/tests/test_generics.py +1 -1
  115. warp/tests/test_grad.py +1 -1
  116. warp/tests/test_grad_customs.py +1 -1
  117. warp/tests/test_grad_debug.py +247 -0
  118. warp/tests/test_hash_grid.py +6 -1
  119. warp/tests/test_implicit_init.py +354 -0
  120. warp/tests/test_import.py +1 -1
  121. warp/tests/test_indexedarray.py +1 -1
  122. warp/tests/test_intersect.py +1 -1
  123. warp/tests/test_jax.py +1 -1
  124. warp/tests/test_large.py +1 -1
  125. warp/tests/test_launch.py +1 -1
  126. warp/tests/test_lerp.py +1 -1
  127. warp/tests/test_linear_solvers.py +1 -1
  128. warp/tests/test_lvalue.py +1 -1
  129. warp/tests/test_marching_cubes.py +5 -2
  130. warp/tests/test_mat.py +34 -35
  131. warp/tests/test_mat_lite.py +2 -1
  132. warp/tests/test_mat_scalar_ops.py +1 -1
  133. warp/tests/test_math.py +1 -1
  134. warp/tests/test_matmul.py +20 -16
  135. warp/tests/test_matmul_lite.py +1 -1
  136. warp/tests/test_mempool.py +1 -1
  137. warp/tests/test_mesh.py +5 -2
  138. warp/tests/test_mesh_query_aabb.py +1 -1
  139. warp/tests/test_mesh_query_point.py +1 -1
  140. warp/tests/test_mesh_query_ray.py +1 -1
  141. warp/tests/test_mlp.py +1 -1
  142. warp/tests/test_model.py +1 -1
  143. warp/tests/test_module_hashing.py +77 -1
  144. warp/tests/test_modules_lite.py +1 -1
  145. warp/tests/test_multigpu.py +1 -1
  146. warp/tests/test_noise.py +1 -1
  147. warp/tests/test_operators.py +1 -1
  148. warp/tests/test_options.py +1 -1
  149. warp/tests/test_overwrite.py +542 -0
  150. warp/tests/test_peer.py +1 -1
  151. warp/tests/test_pinned.py +1 -1
  152. warp/tests/test_print.py +1 -1
  153. warp/tests/test_quat.py +15 -1
  154. warp/tests/test_rand.py +1 -1
  155. warp/tests/test_reload.py +1 -1
  156. warp/tests/test_rounding.py +1 -1
  157. warp/tests/test_runlength_encode.py +1 -1
  158. warp/tests/test_scalar_ops.py +95 -0
  159. warp/tests/test_sim_grad.py +1 -1
  160. warp/tests/test_sim_kinematics.py +1 -1
  161. warp/tests/test_smoothstep.py +1 -1
  162. warp/tests/test_sparse.py +82 -15
  163. warp/tests/test_spatial.py +1 -1
  164. warp/tests/test_special_values.py +2 -11
  165. warp/tests/test_streams.py +11 -1
  166. warp/tests/test_struct.py +1 -1
  167. warp/tests/test_tape.py +1 -1
  168. warp/tests/test_torch.py +194 -1
  169. warp/tests/test_transient_module.py +1 -1
  170. warp/tests/test_types.py +1 -1
  171. warp/tests/test_utils.py +1 -1
  172. warp/tests/test_vec.py +15 -63
  173. warp/tests/test_vec_lite.py +2 -1
  174. warp/tests/test_vec_scalar_ops.py +65 -1
  175. warp/tests/test_verify_fp.py +1 -1
  176. warp/tests/test_volume.py +28 -2
  177. warp/tests/test_volume_write.py +1 -1
  178. warp/tests/unittest_serial.py +1 -1
  179. warp/tests/unittest_suites.py +9 -1
  180. warp/tests/walkthrough_debug.py +1 -1
  181. warp/thirdparty/unittest_parallel.py +2 -5
  182. warp/torch.py +103 -41
  183. warp/types.py +341 -224
  184. warp/utils.py +11 -2
  185. {warp_lang-1.2.2.dist-info → warp_lang-1.3.1.dist-info}/METADATA +99 -46
  186. warp_lang-1.3.1.dist-info/RECORD +368 -0
  187. warp/examples/fem/bsr_utils.py +0 -378
  188. warp/examples/fem/mesh_utils.py +0 -133
  189. warp/examples/fem/plot_utils.py +0 -292
  190. warp_lang-1.2.2.dist-info/RECORD +0 -359
  191. {warp_lang-1.2.2.dist-info → warp_lang-1.3.1.dist-info}/LICENSE.md +0 -0
  192. {warp_lang-1.2.2.dist-info → warp_lang-1.3.1.dist-info}/WHEEL +0 -0
  193. {warp_lang-1.2.2.dist-info → warp_lang-1.3.1.dist-info}/top_level.txt +0 -0
@@ -10,18 +10,32 @@ _mat32 = wp.mat(shape=(3, 2), dtype=float)
10
10
 
11
11
 
12
12
  class DeformedGeometry(Geometry):
13
- def __init__(self, field):
14
- """Constructs a Deformed Geometry from a displacement field defined over a base geometry"""
15
-
16
- from warp.fem.field import DiscreteField
17
-
18
- self.field: DiscreteField = field
19
- self.base = self.field.space.geometry
13
+ def __init__(self, field: "wp.fem.field.GeometryField", relative: bool = True):
14
+ """Constructs a Deformed Geometry from a displacement or absolute position field defined over a base geometry.
15
+ The deformation field does not need to be isoparameteric.
16
+
17
+ See also: :meth:`warp.fem.DiscreteField.make_deformed_geometry`
18
+ """
19
+
20
+ from warp.fem.field import DiscreteField, GeometryField
21
+
22
+ if isinstance(field, DiscreteField):
23
+ if (
24
+ not wp.types.type_is_vector(field.dtype)
25
+ or wp.types.type_length(field.dtype) != field.geometry.dimension
26
+ ):
27
+ raise ValueError(
28
+ "Invalid value type for position field, must be vector-valued with same dimension as underlying geometry"
29
+ )
30
+ if field.eval_grad_inner is None:
31
+ raise ValueError("Gradient evaluation is not supported on the passed field")
32
+
33
+ self._relative = relative
34
+
35
+ self.field: GeometryField = field
36
+ self.base = self.field.geometry
20
37
  self.dimension = self.base.dimension
21
38
 
22
- if not wp.types.type_is_vector(field.dtype) or wp.types.type_length(field.dtype) != self.dimension:
23
- raise ValueError("Invalid value type for position field")
24
-
25
39
  self.CellArg = self.field.ElementEvalArg
26
40
 
27
41
  self.field_trace = field.trace()
@@ -60,7 +74,7 @@ class DeformedGeometry(Geometry):
60
74
 
61
75
  @property
62
76
  def name(self):
63
- return f"DefGeo_{self.field.name}"
77
+ return f"DefGeo_{self.field.name}_{'rel' if self._relative else 'abs'}"
64
78
 
65
79
  # Geometry device interface
66
80
 
@@ -74,20 +88,28 @@ class DeformedGeometry(Geometry):
74
88
  return args
75
89
 
76
90
  def _make_cell_position(self):
91
+ @cache.dynamic_func(suffix=self.name)
92
+ def cell_position_absolute(cell_arg: self.CellArg, s: Sample):
93
+ return self.field.eval_inner(cell_arg, s)
94
+
77
95
  @cache.dynamic_func(suffix=self.name)
78
96
  def cell_position(cell_arg: self.CellArg, s: Sample):
79
97
  return self.field.eval_inner(cell_arg, s) + self.base.cell_position(cell_arg.elt_arg, s)
80
98
 
81
- return cell_position
99
+ return cell_position if self._relative else cell_position_absolute
82
100
 
83
101
  def _make_cell_deformation_gradient(self):
102
+ @cache.dynamic_func(suffix=self.name)
103
+ def cell_deformation_gradient_absolute(cell_arg: self.CellArg, s: Sample):
104
+ return self.field.eval_reference_grad_inner(cell_arg, s)
105
+
84
106
  @cache.dynamic_func(suffix=self.name)
85
107
  def cell_deformation_gradient(cell_arg: self.CellArg, s: Sample):
86
108
  return self.field.eval_reference_grad_inner(cell_arg, s) + self.base.cell_deformation_gradient(
87
109
  cell_arg.elt_arg, s
88
110
  )
89
111
 
90
- return cell_deformation_gradient
112
+ return cell_deformation_gradient if self._relative else cell_deformation_gradient_absolute
91
113
 
92
114
  def _make_cell_inverse_deformation_gradient(self):
93
115
  @cache.dynamic_func(suffix=self.name)
@@ -129,14 +151,27 @@ class DeformedGeometry(Geometry):
129
151
  return args
130
152
 
131
153
  def _make_side_position(self):
154
+ @cache.dynamic_func(suffix=self.name)
155
+ def side_position_absolute(args: self.SideArg, s: Sample):
156
+ trace_arg = self.field_trace.ElementEvalArg(args.base_arg, args.trace_arg)
157
+ return self.field_trace.eval_inner(trace_arg, s)
158
+
132
159
  @cache.dynamic_func(suffix=self.name)
133
160
  def side_position(args: self.SideArg, s: Sample):
134
161
  trace_arg = self.field_trace.ElementEvalArg(args.base_arg, args.trace_arg)
135
162
  return self.field_trace.eval_inner(trace_arg, s) + self.base.side_position(args.base_arg, s)
136
163
 
137
- return side_position
164
+ return side_position if self._relative else side_position_absolute
138
165
 
139
166
  def _make_side_deformation_gradient(self):
167
+ @cache.dynamic_func(suffix=self.name)
168
+ def side_deformation_gradient_absolute(args: self.SideArg, s: Sample):
169
+ base_def_grad = self.base.side_deformation_gradient(args.base_arg, s)
170
+ trace_arg = self.field_trace.ElementEvalArg(args.base_arg, args.trace_arg)
171
+
172
+ Du = self.field_trace.eval_grad_inner(trace_arg, s)
173
+ return Du * base_def_grad
174
+
140
175
  @cache.dynamic_func(suffix=self.name)
141
176
  def side_deformation_gradient(args: self.SideArg, s: Sample):
142
177
  base_def_grad = self.base.side_deformation_gradient(args.base_arg, s)
@@ -145,7 +180,7 @@ class DeformedGeometry(Geometry):
145
180
  Du = self.field_trace.eval_grad_inner(trace_arg, s)
146
181
  return base_def_grad + Du * base_def_grad
147
182
 
148
- return side_deformation_gradient
183
+ return side_deformation_gradient if self._relative else side_deformation_gradient_absolute
149
184
 
150
185
  def _make_side_inner_inverse_deformation_gradient(self):
151
186
  @cache.dynamic_func(suffix=self.name)
@@ -65,7 +65,7 @@ EDGE_VERTEX_INDICES = wp.constant(
65
65
  )
66
66
  )
67
67
 
68
- # orthogal transform for face coordinates given first vertex + winding
68
+ # orthogonal transform for face coordinates given first vertex + winding
69
69
  # (two rows per entry)
70
70
 
71
71
  FACE_ORIENTATION = [
@@ -439,12 +439,12 @@ class Hexmesh(Geometry):
439
439
  return side_arg.cell_arg
440
440
 
441
441
  def _build_topology(self, temporary_store: TemporaryStore):
442
- from warp.fem.utils import compress_node_indices, masked_indices
442
+ from warp.fem.utils import compress_node_indices, host_read_at_index, masked_indices
443
443
  from warp.utils import array_scan
444
444
 
445
445
  device = self.hex_vertex_indices.device
446
446
 
447
- vertex_hex_offsets, vertex_hex_indices, _, __ = compress_node_indices(
447
+ vertex_hex_offsets, vertex_hex_indices = compress_node_indices(
448
448
  self.vertex_count(), self.hex_vertex_indices, temporary_store=temporary_store
449
449
  )
450
450
  self._vertex_hex_offsets = vertex_hex_offsets.detach()
@@ -492,16 +492,11 @@ class Hexmesh(Geometry):
492
492
  array_scan(in_array=vertex_start_face_count.array, out_array=vertex_unique_face_offsets.array, inclusive=False)
493
493
 
494
494
  # Get back edge count to host
495
- if device.is_cuda:
496
- face_count = borrow_temporary(temporary_store, shape=(1,), dtype=int, device="cpu", pinned=True)
497
- # Last vertex will not own any edge, so its count will be zero; just fetching last prefix count is ok
498
- wp.copy(
499
- dest=face_count.array, src=vertex_unique_face_offsets.array, src_offset=self.vertex_count() - 1, count=1
495
+ face_count = int(
496
+ host_read_at_index(
497
+ vertex_unique_face_offsets.array, self.vertex_count() - 1, temporary_store=temporary_store
500
498
  )
501
- wp.synchronize_stream(wp.get_stream(device))
502
- face_count = int(face_count.array.numpy()[0])
503
- else:
504
- face_count = int(vertex_unique_face_offsets.array.numpy()[self.vertex_count() - 1])
499
+ )
505
500
 
506
501
  self._face_vertex_indices = wp.empty(shape=(face_count,), dtype=wp.vec4i, device=device)
507
502
  self._face_hex_indices = wp.empty(shape=(face_count,), dtype=wp.vec2i, device=device)
@@ -557,6 +552,7 @@ class Hexmesh(Geometry):
557
552
  self._boundary_face_indices = boundary_face_indices.detach()
558
553
 
559
554
  def _compute_hex_edges(self, temporary_store: Optional[TemporaryStore] = None):
555
+ from warp.fem.utils import host_read_at_index
560
556
  from warp.utils import array_scan
561
557
 
562
558
  device = self.hex_vertex_indices.device
@@ -599,19 +595,11 @@ class Hexmesh(Geometry):
599
595
  array_scan(in_array=vertex_start_edge_count.array, out_array=vertex_unique_edge_offsets.array, inclusive=False)
600
596
 
601
597
  # Get back edge count to host
602
- if device.is_cuda:
603
- edge_count = borrow_temporary(temporary_store, shape=(1,), dtype=int, device="cpu", pinned=True)
604
- # Last vertex will not own any edge, so its count will be zero; just fetching last prefix count is ok
605
- wp.copy(
606
- dest=edge_count.array,
607
- src=vertex_unique_edge_offsets.array,
608
- src_offset=self.vertex_count() - 1,
609
- count=1,
598
+ self._edge_count = int(
599
+ host_read_at_index(
600
+ vertex_unique_edge_offsets.array, self.vertex_count() - 1, temporary_store=temporary_store
610
601
  )
611
- wp.synchronize_stream(wp.get_stream(device))
612
- self._edge_count = int(edge_count.array.numpy()[0])
613
- else:
614
- self._edge_count = int(vertex_unique_edge_offsets.array.numpy()[self.vertex_count() - 1])
602
+ )
615
603
 
616
604
  self._hex_edge_indices = wp.empty(
617
605
  dtype=int, device=self.hex_vertex_indices.device, shape=(self.cell_count(), 12)
@@ -60,9 +60,21 @@ class NanogridSideArg:
60
60
 
61
61
 
62
62
  class Nanogrid(Geometry):
63
+ """Sparse grid geometry"""
64
+
63
65
  dimension = 3
64
66
 
65
67
  def __init__(self, grid: wp.Volume, temporary_store: Optional[cache.TemporaryStore] = None):
68
+ """
69
+ Constructs a sparse grid geometry from an in-memory NanoVDB volume.
70
+
71
+ Args:
72
+ grid: The NanoVDB volume. Any type is accepted, but for indexing efficiency an index grid is recommended.
73
+ If `grid` is an 'on' index grid, cells will be created for active voxels only, otherwise cells will
74
+ be created for all leaf voxels.
75
+ temporary_store: shared pool from which to allocate temporary arrays
76
+ """
77
+
66
78
  self._cell_grid = grid
67
79
  self._cell_grid_info = grid.get_grid_info()
68
80
 
@@ -77,31 +89,29 @@ class Nanogrid(Geometry):
77
89
  self._node_ijk = wp.array(shape=(node_count,), dtype=wp.vec3i, device=device)
78
90
  self._node_grid.get_voxels(out=self._node_ijk)
79
91
 
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()
92
+ self._face_grid = None
93
+ self._face_ijk = None
96
94
 
97
95
  self._edge_grid = None
98
96
  self._edge_ijk = None
99
97
 
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)
98
+ @property
99
+ def cell_grid(self) -> wp.Volume:
100
+ return self._cell_grid
101
+
102
+ @property
103
+ def vertex_grid(self) -> wp.Volume:
104
+ return self._node_grid
105
+
106
+ @property
107
+ def face_grid(self) -> wp.Volume:
108
+ self._ensure_face_grid()
109
+ return self._face_grid
110
+
111
+ @property
112
+ def edge_grid(self) -> wp.Volume:
113
+ self._ensure_edge_grid()
114
+ return self._edge_grid
105
115
 
106
116
  def cell_count(self):
107
117
  return self._cell_ijk.shape[0]
@@ -110,17 +120,17 @@ class Nanogrid(Geometry):
110
120
  return self._node_ijk.shape[0]
111
121
 
112
122
  def side_count(self):
123
+ self._ensure_face_grid()
113
124
  return self._face_ijk.shape[0]
114
125
 
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
126
  def boundary_side_count(self):
127
+ self._ensure_face_grid()
122
128
  return self._boundary_face_indices.shape[0]
123
129
 
130
+ def edge_count(self):
131
+ self._ensure_edge_grid()
132
+ return self._edge_ijk.shape[0]
133
+
124
134
  def reference_cell(self) -> Cube:
125
135
  return Cube()
126
136
 
@@ -144,7 +154,7 @@ class Nanogrid(Geometry):
144
154
  @wp.func
145
155
  def cell_position(args: CellArg, s: Sample):
146
156
  uvw = wp.vec3(args.cell_ijk[s.element_index]) + s.element_coords
147
- return wp.volume_index_to_world(args.cell_grid, uvw)
157
+ return wp.volume_index_to_world(args.cell_grid, uvw - wp.vec3(0.5))
148
158
 
149
159
  @wp.func
150
160
  def cell_deformation_gradient(args: CellArg, s: Sample):
@@ -156,7 +166,7 @@ class Nanogrid(Geometry):
156
166
 
157
167
  @wp.func
158
168
  def cell_lookup(args: CellArg, pos: wp.vec3):
159
- uvw = wp.volume_world_to_index(args.cell_grid, pos)
169
+ uvw = wp.volume_world_to_index(args.cell_grid, pos) + wp.vec3(0.5)
160
170
  ijk = wp.vec3i(int(wp.floor(uvw[0])), int(wp.floor(uvw[1])), int(wp.floor(uvw[2])))
161
171
  element_index = wp.volume_lookup_index(args.cell_grid, ijk[0], ijk[1], ijk[2])
162
172
 
@@ -166,9 +176,37 @@ class Nanogrid(Geometry):
166
176
  make_free_sample(NULL_ELEMENT_INDEX, Coords(OUTSIDE)),
167
177
  )
168
178
 
179
+ @wp.func
180
+ def _project_on_voxel_at_origin(coords: wp.vec3):
181
+ proj_coords = wp.min(wp.max(coords, wp.vec3(0.0)), wp.vec3(1.0))
182
+ return wp.length_sq(coords - proj_coords), proj_coords
183
+
169
184
  @wp.func
170
185
  def cell_lookup(args: CellArg, pos: wp.vec3, guess: Sample):
171
- return Nanogrid.cell_lookup(args, pos)
186
+ s_global = Nanogrid.cell_lookup(args, pos)
187
+
188
+ if s_global.element_index != NULL_ELEMENT_INDEX:
189
+ return s_global
190
+
191
+ closest_voxel = int(NULL_ELEMENT_INDEX)
192
+ closest_coords = Coords(OUTSIDE)
193
+ closest_dist = float(1.0e8)
194
+
195
+ # project to closest in stencil
196
+ uvw = wp.volume_world_to_index(args.cell_grid, pos) + wp.vec3(0.5)
197
+ cell_ijk = args.cell_ijk[guess.element_index]
198
+ for ni in range(-1, 2):
199
+ for nj in range(-1, 2):
200
+ for nk in range(-1, 2):
201
+ nijk = cell_ijk + wp.vec3i(ni, nj, nk)
202
+ cell_idx = wp.volume_lookup_index(args.cell_grid, nijk[0], nijk[1], nijk[2])
203
+ dist, coords = Nanogrid._project_on_voxel_at_origin(uvw - wp.vec3(nijk))
204
+ if cell_idx != -1 and dist <= closest_dist:
205
+ closest_dist = dist
206
+ closest_voxel = cell_idx
207
+ closest_coords = coords
208
+
209
+ return make_free_sample(closest_voxel, closest_coords)
172
210
 
173
211
  @wp.func
174
212
  def cell_measure(args: CellArg, s: Sample):
@@ -182,6 +220,8 @@ class Nanogrid(Geometry):
182
220
 
183
221
  @cache.cached_arg_value
184
222
  def side_arg_value(self, device) -> SideArg:
223
+ self._ensure_face_grid()
224
+
185
225
  args = self.SideArg()
186
226
  args.cell_arg = self.cell_arg_value(device)
187
227
  args.face_ijk = self._face_ijk.to(device)
@@ -199,6 +239,8 @@ class Nanogrid(Geometry):
199
239
 
200
240
  @cache.cached_arg_value
201
241
  def side_index_arg_value(self, device) -> SideIndexArg:
242
+ self._ensure_face_grid()
243
+
202
244
  args = self.SideIndexArg()
203
245
  args.boundary_face_indices = self._boundary_face_indices.to(device)
204
246
  return args
@@ -235,7 +277,7 @@ class Nanogrid(Geometry):
235
277
  uvw = wp.vec3(ijk) + Nanogrid._side_to_cell_coords(axis, 0.0, s.element_coords)
236
278
 
237
279
  cell_grid = args.cell_arg.cell_grid
238
- return wp.volume_index_to_world(cell_grid, uvw)
280
+ return wp.volume_index_to_world(cell_grid, uvw - wp.vec3(0.5))
239
281
 
240
282
  @wp.func
241
283
  def _face_tangent_vecs(args: SideArg, axis: int, flip: int):
@@ -342,6 +384,39 @@ class Nanogrid(Geometry):
342
384
  def side_to_cell_arg(side_arg: SideArg):
343
385
  return side_arg.cell_arg
344
386
 
387
+ def _build_face_grid(self, temporary_store: Optional[cache.TemporaryStore] = None):
388
+ device = self._cell_grid.device
389
+ self._face_grid = _build_face_grid(self._cell_ijk, self._cell_grid, temporary_store)
390
+ face_count = self._face_grid.get_voxel_count()
391
+ self._face_ijk = wp.array(shape=(face_count,), dtype=wp.vec3i, device=device)
392
+ self._face_grid.get_voxels(out=self._face_ijk)
393
+
394
+ self._face_flags = wp.array(shape=(face_count,), dtype=wp.uint8, device=device)
395
+ boundary_face_mask = cache.borrow_temporary(temporary_store, shape=(face_count,), dtype=wp.int32, device=device)
396
+
397
+ wp.launch(
398
+ _build_face_flags,
399
+ dim=face_count,
400
+ device=device,
401
+ inputs=[self._cell_grid.id, self._face_ijk, self._face_flags, boundary_face_mask.array],
402
+ )
403
+ boundary_face_indices, _ = utils.masked_indices(boundary_face_mask.array)
404
+ self._boundary_face_indices = boundary_face_indices.detach()
405
+
406
+ def _build_edge_grid(self, temporary_store: Optional[cache.TemporaryStore] = None):
407
+ self._edge_grid = _build_edge_grid(self._cell_ijk, self._cell_grid, temporary_store)
408
+ edge_count = self._edge_grid.get_voxel_count()
409
+ self._edge_ijk = wp.array(shape=(edge_count,), dtype=wp.vec3i, device=self._edge_grid.device)
410
+ self._edge_grid.get_voxels(out=self._edge_ijk)
411
+
412
+ def _ensure_face_grid(self):
413
+ if self._face_ijk is None:
414
+ self._build_face_grid()
415
+
416
+ def _ensure_edge_grid(self):
417
+ if self._edge_ijk is None:
418
+ self._build_edge_grid()
419
+
345
420
 
346
421
  @wp.kernel
347
422
  def _cell_node_indices(
@@ -299,12 +299,12 @@ class Quadmesh2D(Geometry):
299
299
  return side_arg.cell_arg
300
300
 
301
301
  def _build_topology(self, temporary_store: TemporaryStore):
302
- from warp.fem.utils import compress_node_indices, masked_indices
302
+ from warp.fem.utils import compress_node_indices, host_read_at_index, masked_indices
303
303
  from warp.utils import array_scan
304
304
 
305
305
  device = self.quad_vertex_indices.device
306
306
 
307
- vertex_quad_offsets, vertex_quad_indices, _, __ = compress_node_indices(
307
+ vertex_quad_offsets, vertex_quad_indices = compress_node_indices(
308
308
  self.vertex_count(), self.quad_vertex_indices, temporary_store=temporary_store
309
309
  )
310
310
  self._vertex_quad_offsets = vertex_quad_offsets.detach()
@@ -350,16 +350,11 @@ class Quadmesh2D(Geometry):
350
350
  array_scan(in_array=vertex_start_edge_count.array, out_array=vertex_unique_edge_offsets.array, inclusive=False)
351
351
 
352
352
  # Get back edge count to host
353
- if device.is_cuda:
354
- edge_count = borrow_temporary(temporary_store, shape=(1,), dtype=int, device="cpu", pinned=True)
355
- # Last vertex will not own any edge, so its count will be zero; just fetching last prefix count is ok
356
- wp.copy(
357
- dest=edge_count.array, src=vertex_unique_edge_offsets.array, src_offset=self.vertex_count() - 1, count=1
353
+ edge_count = int(
354
+ host_read_at_index(
355
+ vertex_unique_edge_offsets.array, self.vertex_count() - 1, temporary_store=temporary_store
358
356
  )
359
- wp.synchronize_stream(wp.get_stream(device))
360
- edge_count = int(edge_count.array.numpy()[0])
361
- else:
362
- edge_count = int(vertex_unique_edge_offsets.array.numpy()[self.vertex_count() - 1])
357
+ )
363
358
 
364
359
  self._edge_vertex_indices = wp.empty(shape=(edge_count,), dtype=wp.vec2i, device=device)
365
360
  self._edge_quad_indices = wp.empty(shape=(edge_count,), dtype=wp.vec2i, device=device)