warp-lang 1.0.0b2__py3-none-win_amd64.whl → 1.0.0b6__py3-none-win_amd64.whl

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

Potentially problematic release.


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

Files changed (271) hide show
  1. docs/conf.py +17 -5
  2. examples/env/env_ant.py +1 -1
  3. examples/env/env_cartpole.py +1 -1
  4. examples/env/env_humanoid.py +1 -1
  5. examples/env/env_usd.py +4 -1
  6. examples/env/environment.py +8 -9
  7. examples/example_dem.py +34 -33
  8. examples/example_diffray.py +364 -337
  9. examples/example_fluid.py +32 -23
  10. examples/example_jacobian_ik.py +97 -93
  11. examples/example_marching_cubes.py +6 -16
  12. examples/example_mesh.py +6 -16
  13. examples/example_mesh_intersect.py +16 -14
  14. examples/example_nvdb.py +14 -16
  15. examples/example_raycast.py +14 -13
  16. examples/example_raymarch.py +16 -23
  17. examples/example_render_opengl.py +19 -10
  18. examples/example_sim_cartpole.py +82 -78
  19. examples/example_sim_cloth.py +45 -48
  20. examples/example_sim_fk_grad.py +51 -44
  21. examples/example_sim_fk_grad_torch.py +47 -40
  22. examples/example_sim_grad_bounce.py +108 -133
  23. examples/example_sim_grad_cloth.py +99 -113
  24. examples/example_sim_granular.py +5 -6
  25. examples/{example_sim_sdf_shape.py → example_sim_granular_collision_sdf.py} +37 -26
  26. examples/example_sim_neo_hookean.py +51 -55
  27. examples/example_sim_particle_chain.py +4 -4
  28. examples/example_sim_quadruped.py +126 -81
  29. examples/example_sim_rigid_chain.py +54 -61
  30. examples/example_sim_rigid_contact.py +66 -70
  31. examples/example_sim_rigid_fem.py +3 -3
  32. examples/example_sim_rigid_force.py +1 -1
  33. examples/example_sim_rigid_gyroscopic.py +3 -4
  34. examples/example_sim_rigid_kinematics.py +28 -39
  35. examples/example_sim_trajopt.py +112 -110
  36. examples/example_sph.py +9 -8
  37. examples/example_wave.py +7 -7
  38. examples/fem/bsr_utils.py +30 -17
  39. examples/fem/example_apic_fluid.py +85 -69
  40. examples/fem/example_convection_diffusion.py +97 -93
  41. examples/fem/example_convection_diffusion_dg.py +142 -149
  42. examples/fem/example_convection_diffusion_dg0.py +141 -136
  43. examples/fem/example_deformed_geometry.py +146 -0
  44. examples/fem/example_diffusion.py +115 -84
  45. examples/fem/example_diffusion_3d.py +116 -86
  46. examples/fem/example_diffusion_mgpu.py +102 -79
  47. examples/fem/example_mixed_elasticity.py +139 -100
  48. examples/fem/example_navier_stokes.py +175 -162
  49. examples/fem/example_stokes.py +143 -111
  50. examples/fem/example_stokes_transfer.py +186 -157
  51. examples/fem/mesh_utils.py +59 -97
  52. examples/fem/plot_utils.py +138 -17
  53. tools/ci/publishing/build_nodes_info.py +54 -0
  54. warp/__init__.py +4 -3
  55. warp/__init__.pyi +1 -0
  56. warp/bin/warp-clang.dll +0 -0
  57. warp/bin/warp.dll +0 -0
  58. warp/build.py +5 -3
  59. warp/build_dll.py +29 -9
  60. warp/builtins.py +836 -492
  61. warp/codegen.py +864 -553
  62. warp/config.py +3 -1
  63. warp/context.py +389 -172
  64. warp/fem/__init__.py +24 -6
  65. warp/fem/cache.py +318 -25
  66. warp/fem/dirichlet.py +7 -3
  67. warp/fem/domain.py +14 -0
  68. warp/fem/field/__init__.py +30 -38
  69. warp/fem/field/field.py +149 -0
  70. warp/fem/field/nodal_field.py +244 -138
  71. warp/fem/field/restriction.py +8 -6
  72. warp/fem/field/test.py +127 -59
  73. warp/fem/field/trial.py +117 -60
  74. warp/fem/geometry/__init__.py +5 -1
  75. warp/fem/geometry/deformed_geometry.py +271 -0
  76. warp/fem/geometry/element.py +24 -1
  77. warp/fem/geometry/geometry.py +86 -14
  78. warp/fem/geometry/grid_2d.py +112 -54
  79. warp/fem/geometry/grid_3d.py +134 -65
  80. warp/fem/geometry/hexmesh.py +953 -0
  81. warp/fem/geometry/partition.py +85 -33
  82. warp/fem/geometry/quadmesh_2d.py +532 -0
  83. warp/fem/geometry/tetmesh.py +451 -115
  84. warp/fem/geometry/trimesh_2d.py +197 -92
  85. warp/fem/integrate.py +534 -268
  86. warp/fem/operator.py +58 -31
  87. warp/fem/polynomial.py +11 -0
  88. warp/fem/quadrature/__init__.py +1 -1
  89. warp/fem/quadrature/pic_quadrature.py +150 -58
  90. warp/fem/quadrature/quadrature.py +209 -57
  91. warp/fem/space/__init__.py +230 -53
  92. warp/fem/space/basis_space.py +489 -0
  93. warp/fem/space/collocated_function_space.py +105 -0
  94. warp/fem/space/dof_mapper.py +49 -2
  95. warp/fem/space/function_space.py +90 -39
  96. warp/fem/space/grid_2d_function_space.py +149 -496
  97. warp/fem/space/grid_3d_function_space.py +173 -538
  98. warp/fem/space/hexmesh_function_space.py +352 -0
  99. warp/fem/space/partition.py +129 -76
  100. warp/fem/space/quadmesh_2d_function_space.py +369 -0
  101. warp/fem/space/restriction.py +46 -34
  102. warp/fem/space/shape/__init__.py +15 -0
  103. warp/fem/space/shape/cube_shape_function.py +738 -0
  104. warp/fem/space/shape/shape_function.py +103 -0
  105. warp/fem/space/shape/square_shape_function.py +611 -0
  106. warp/fem/space/shape/tet_shape_function.py +567 -0
  107. warp/fem/space/shape/triangle_shape_function.py +429 -0
  108. warp/fem/space/tetmesh_function_space.py +132 -1039
  109. warp/fem/space/topology.py +295 -0
  110. warp/fem/space/trimesh_2d_function_space.py +104 -742
  111. warp/fem/types.py +13 -11
  112. warp/fem/utils.py +335 -60
  113. warp/native/array.h +120 -34
  114. warp/native/builtin.h +101 -72
  115. warp/native/bvh.cpp +73 -325
  116. warp/native/bvh.cu +406 -23
  117. warp/native/bvh.h +22 -40
  118. warp/native/clang/clang.cpp +1 -0
  119. warp/native/crt.h +2 -0
  120. warp/native/cuda_util.cpp +8 -3
  121. warp/native/cuda_util.h +1 -0
  122. warp/native/exports.h +1522 -1243
  123. warp/native/intersect.h +19 -4
  124. warp/native/intersect_adj.h +8 -8
  125. warp/native/mat.h +76 -17
  126. warp/native/mesh.cpp +33 -108
  127. warp/native/mesh.cu +114 -18
  128. warp/native/mesh.h +395 -40
  129. warp/native/noise.h +272 -329
  130. warp/native/quat.h +51 -8
  131. warp/native/rand.h +44 -34
  132. warp/native/reduce.cpp +1 -1
  133. warp/native/sparse.cpp +4 -4
  134. warp/native/sparse.cu +163 -155
  135. warp/native/spatial.h +2 -2
  136. warp/native/temp_buffer.h +18 -14
  137. warp/native/vec.h +103 -21
  138. warp/native/warp.cpp +2 -1
  139. warp/native/warp.cu +28 -3
  140. warp/native/warp.h +4 -3
  141. warp/render/render_opengl.py +261 -109
  142. warp/sim/__init__.py +1 -2
  143. warp/sim/articulation.py +385 -185
  144. warp/sim/import_mjcf.py +59 -48
  145. warp/sim/import_urdf.py +15 -15
  146. warp/sim/import_usd.py +174 -102
  147. warp/sim/inertia.py +17 -18
  148. warp/sim/integrator_xpbd.py +4 -3
  149. warp/sim/model.py +330 -250
  150. warp/sim/render.py +1 -1
  151. warp/sparse.py +625 -152
  152. warp/stubs.py +341 -309
  153. warp/tape.py +9 -6
  154. warp/tests/__main__.py +3 -6
  155. warp/tests/assets/curlnoise_golden.npy +0 -0
  156. warp/tests/assets/pnoise_golden.npy +0 -0
  157. warp/tests/{test_class_kernel.py → aux_test_class_kernel.py} +9 -1
  158. warp/tests/aux_test_conditional_unequal_types_kernels.py +21 -0
  159. warp/tests/{test_dependent.py → aux_test_dependent.py} +2 -2
  160. warp/tests/{test_reference.py → aux_test_reference.py} +1 -1
  161. warp/tests/aux_test_unresolved_func.py +14 -0
  162. warp/tests/aux_test_unresolved_symbol.py +14 -0
  163. warp/tests/disabled_kinematics.py +239 -0
  164. warp/tests/run_coverage_serial.py +31 -0
  165. warp/tests/test_adam.py +103 -106
  166. warp/tests/test_arithmetic.py +94 -74
  167. warp/tests/test_array.py +82 -101
  168. warp/tests/test_array_reduce.py +57 -23
  169. warp/tests/test_atomic.py +64 -28
  170. warp/tests/test_bool.py +22 -12
  171. warp/tests/test_builtins_resolution.py +1292 -0
  172. warp/tests/test_bvh.py +18 -18
  173. warp/tests/test_closest_point_edge_edge.py +54 -57
  174. warp/tests/test_codegen.py +165 -134
  175. warp/tests/test_compile_consts.py +28 -20
  176. warp/tests/test_conditional.py +108 -24
  177. warp/tests/test_copy.py +10 -12
  178. warp/tests/test_ctypes.py +112 -88
  179. warp/tests/test_dense.py +21 -14
  180. warp/tests/test_devices.py +98 -0
  181. warp/tests/test_dlpack.py +75 -75
  182. warp/tests/test_examples.py +237 -0
  183. warp/tests/test_fabricarray.py +22 -24
  184. warp/tests/test_fast_math.py +15 -11
  185. warp/tests/test_fem.py +1034 -124
  186. warp/tests/test_fp16.py +23 -16
  187. warp/tests/test_func.py +187 -86
  188. warp/tests/test_generics.py +194 -49
  189. warp/tests/test_grad.py +123 -181
  190. warp/tests/test_grad_customs.py +176 -0
  191. warp/tests/test_hash_grid.py +35 -34
  192. warp/tests/test_import.py +10 -23
  193. warp/tests/test_indexedarray.py +24 -25
  194. warp/tests/test_intersect.py +18 -9
  195. warp/tests/test_large.py +141 -0
  196. warp/tests/test_launch.py +14 -41
  197. warp/tests/test_lerp.py +64 -65
  198. warp/tests/test_lvalue.py +493 -0
  199. warp/tests/test_marching_cubes.py +12 -13
  200. warp/tests/test_mat.py +517 -2898
  201. warp/tests/test_mat_lite.py +115 -0
  202. warp/tests/test_mat_scalar_ops.py +2889 -0
  203. warp/tests/test_math.py +103 -9
  204. warp/tests/test_matmul.py +304 -69
  205. warp/tests/test_matmul_lite.py +410 -0
  206. warp/tests/test_mesh.py +60 -22
  207. warp/tests/test_mesh_query_aabb.py +21 -25
  208. warp/tests/test_mesh_query_point.py +111 -22
  209. warp/tests/test_mesh_query_ray.py +12 -24
  210. warp/tests/test_mlp.py +30 -22
  211. warp/tests/test_model.py +92 -89
  212. warp/tests/test_modules_lite.py +39 -0
  213. warp/tests/test_multigpu.py +88 -114
  214. warp/tests/test_noise.py +12 -11
  215. warp/tests/test_operators.py +16 -20
  216. warp/tests/test_options.py +11 -11
  217. warp/tests/test_pinned.py +17 -18
  218. warp/tests/test_print.py +32 -11
  219. warp/tests/test_quat.py +275 -129
  220. warp/tests/test_rand.py +18 -16
  221. warp/tests/test_reload.py +38 -34
  222. warp/tests/test_rounding.py +50 -43
  223. warp/tests/test_runlength_encode.py +168 -20
  224. warp/tests/test_smoothstep.py +9 -11
  225. warp/tests/test_snippet.py +143 -0
  226. warp/tests/test_sparse.py +261 -63
  227. warp/tests/test_spatial.py +276 -243
  228. warp/tests/test_streams.py +110 -85
  229. warp/tests/test_struct.py +268 -63
  230. warp/tests/test_tape.py +39 -21
  231. warp/tests/test_torch.py +90 -86
  232. warp/tests/test_transient_module.py +10 -12
  233. warp/tests/test_types.py +363 -0
  234. warp/tests/test_utils.py +451 -0
  235. warp/tests/test_vec.py +354 -2050
  236. warp/tests/test_vec_lite.py +73 -0
  237. warp/tests/test_vec_scalar_ops.py +2099 -0
  238. warp/tests/test_volume.py +418 -376
  239. warp/tests/test_volume_write.py +124 -134
  240. warp/tests/unittest_serial.py +35 -0
  241. warp/tests/unittest_suites.py +291 -0
  242. warp/tests/unittest_utils.py +342 -0
  243. warp/tests/{test_misc.py → unused_test_misc.py} +13 -5
  244. warp/tests/{test_debug.py → walkthough_debug.py} +3 -17
  245. warp/thirdparty/appdirs.py +36 -45
  246. warp/thirdparty/unittest_parallel.py +589 -0
  247. warp/types.py +622 -211
  248. warp/utils.py +54 -393
  249. warp_lang-1.0.0b6.dist-info/METADATA +238 -0
  250. warp_lang-1.0.0b6.dist-info/RECORD +409 -0
  251. {warp_lang-1.0.0b2.dist-info → warp_lang-1.0.0b6.dist-info}/WHEEL +1 -1
  252. examples/example_cache_management.py +0 -40
  253. examples/example_multigpu.py +0 -54
  254. examples/example_struct.py +0 -65
  255. examples/fem/example_stokes_transfer_3d.py +0 -210
  256. warp/bin/warp-clang.so +0 -0
  257. warp/bin/warp.so +0 -0
  258. warp/fem/field/discrete_field.py +0 -80
  259. warp/fem/space/nodal_function_space.py +0 -233
  260. warp/tests/test_all.py +0 -223
  261. warp/tests/test_array_scan.py +0 -60
  262. warp/tests/test_base.py +0 -208
  263. warp/tests/test_unresolved_func.py +0 -7
  264. warp/tests/test_unresolved_symbol.py +0 -7
  265. warp_lang-1.0.0b2.dist-info/METADATA +0 -26
  266. warp_lang-1.0.0b2.dist-info/RECORD +0 -380
  267. /warp/tests/{test_compile_consts_dummy.py → aux_test_compile_consts_dummy.py} +0 -0
  268. /warp/tests/{test_reference_reference.py → aux_test_reference_reference.py} +0 -0
  269. /warp/tests/{test_square.py → aux_test_square.py} +0 -0
  270. {warp_lang-1.0.0b2.dist-info → warp_lang-1.0.0b6.dist-info}/LICENSE.md +0 -0
  271. {warp_lang-1.0.0b2.dist-info → warp_lang-1.0.0b6.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,532 @@
1
+ from typing import Optional
2
+
3
+ import warp as wp
4
+ from warp.fem.cache import (
5
+ TemporaryStore,
6
+ borrow_temporary,
7
+ borrow_temporary_like,
8
+ cached_arg_value,
9
+ )
10
+ from warp.fem.types import OUTSIDE, Coords, ElementIndex, Sample, make_free_sample
11
+
12
+ from .element import LinearEdge, Square
13
+ from .geometry import Geometry
14
+
15
+ # from .closest_point import project_on_tet_at_origin
16
+
17
+
18
+ @wp.struct
19
+ class Quadmesh2DCellArg:
20
+ quad_vertex_indices: wp.array2d(dtype=int)
21
+ positions: wp.array(dtype=wp.vec2)
22
+
23
+ # for neighbor cell lookup
24
+ vertex_quad_offsets: wp.array(dtype=int)
25
+ vertex_quad_indices: wp.array(dtype=int)
26
+
27
+
28
+ @wp.struct
29
+ class Quadmesh2DSideArg:
30
+ cell_arg: Quadmesh2DCellArg
31
+ edge_vertex_indices: wp.array(dtype=wp.vec2i)
32
+ edge_quad_indices: wp.array(dtype=wp.vec2i)
33
+
34
+
35
+ class Quadmesh2D(Geometry):
36
+ """Two-dimensional quadrilateral mesh geometry"""
37
+
38
+ dimension = 2
39
+
40
+ def __init__(
41
+ self, quad_vertex_indices: wp.array, positions: wp.array, temporary_store: Optional[TemporaryStore] = None
42
+ ):
43
+ """
44
+ Constructs a two-dimensional quadrilateral mesh.
45
+
46
+ Args:
47
+ quad_vertex_indices: warp array of shape (num_tris, 4) containing vertex indices for each quad, in counter-clockwise order
48
+ positions: warp array of shape (num_vertices, 2) containing 2d position for each vertex
49
+ temporary_store: shared pool from which to allocate temporary arrays
50
+ """
51
+
52
+ self.quad_vertex_indices = quad_vertex_indices
53
+ self.positions = positions
54
+
55
+ self._edge_vertex_indices: wp.array = None
56
+ self._edge_quad_indices: wp.array = None
57
+ self._vertex_quad_offsets: wp.array = None
58
+ self._vertex_quad_indices: wp.array = None
59
+ self._build_topology(temporary_store)
60
+
61
+ def cell_count(self):
62
+ return self.quad_vertex_indices.shape[0]
63
+
64
+ def vertex_count(self):
65
+ return self.positions.shape[0]
66
+
67
+ def side_count(self):
68
+ return self._edge_vertex_indices.shape[0]
69
+
70
+ def boundary_side_count(self):
71
+ return self._boundary_edge_indices.shape[0]
72
+
73
+ def reference_cell(self) -> Square:
74
+ return Square()
75
+
76
+ def reference_side(self) -> LinearEdge:
77
+ return LinearEdge()
78
+
79
+ @property
80
+ def edge_quad_indices(self) -> wp.array:
81
+ return self._edge_quad_indices
82
+
83
+ @property
84
+ def edge_vertex_indices(self) -> wp.array:
85
+ return self._edge_vertex_indices
86
+
87
+ CellArg = Quadmesh2DCellArg
88
+ SideArg = Quadmesh2DSideArg
89
+
90
+ @wp.struct
91
+ class SideIndexArg:
92
+ boundary_edge_indices: wp.array(dtype=int)
93
+
94
+ # Geometry device interface
95
+
96
+ @cached_arg_value
97
+ def cell_arg_value(self, device) -> CellArg:
98
+ args = self.CellArg()
99
+
100
+ args.quad_vertex_indices = self.quad_vertex_indices.to(device)
101
+ args.positions = self.positions.to(device)
102
+ args.vertex_quad_offsets = self._vertex_quad_offsets.to(device)
103
+ args.vertex_quad_indices = self._vertex_quad_indices.to(device)
104
+
105
+ return args
106
+
107
+ @wp.func
108
+ def cell_position(args: CellArg, s: Sample):
109
+ quad_idx = args.quad_vertex_indices[s.element_index]
110
+
111
+ w_p = s.element_coords
112
+ w_m = Coords(1.0) - s.element_coords
113
+
114
+ # 0 : m m
115
+ # 1 : p m
116
+ # 2 : p p
117
+ # 3 : m p
118
+
119
+ return (
120
+ w_m[0] * w_m[1] * args.positions[quad_idx[0]]
121
+ + w_p[0] * w_m[1] * args.positions[quad_idx[1]]
122
+ + w_p[0] * w_p[1] * args.positions[quad_idx[2]]
123
+ + w_m[0] * w_p[1] * args.positions[quad_idx[3]]
124
+ )
125
+
126
+ @wp.func
127
+ def cell_deformation_gradient(cell_arg: CellArg, s: Sample):
128
+ """Deformation gradient at `coords`"""
129
+ quad_idx = cell_arg.quad_vertex_indices[s.element_index]
130
+
131
+ w_p = s.element_coords
132
+ w_m = Coords(1.0) - s.element_coords
133
+
134
+ return (
135
+ wp.outer(cell_arg.positions[quad_idx[0]], wp.vec2(-w_m[1], -w_m[0]))
136
+ + wp.outer(cell_arg.positions[quad_idx[1]], wp.vec2(w_m[1], -w_p[0]))
137
+ + wp.outer(cell_arg.positions[quad_idx[2]], wp.vec2(w_p[1], w_p[0]))
138
+ + wp.outer(cell_arg.positions[quad_idx[3]], wp.vec2(-w_p[1], w_m[0]))
139
+ )
140
+
141
+ @wp.func
142
+ def cell_inverse_deformation_gradient(cell_arg: CellArg, s: Sample):
143
+ return wp.inverse(Quadmesh2D.cell_deformation_gradient(cell_arg, s))
144
+
145
+ @wp.func
146
+ def cell_measure(args: CellArg, s: Sample):
147
+ return wp.abs(wp.determinant(Quadmesh2D.cell_deformation_gradient(args, s)))
148
+
149
+ @wp.func
150
+ def cell_normal(args: CellArg, s: Sample):
151
+ return wp.vec2(0.0)
152
+
153
+ @cached_arg_value
154
+ def side_index_arg_value(self, device) -> SideIndexArg:
155
+ args = self.SideIndexArg()
156
+
157
+ args.boundary_edge_indices = self._boundary_edge_indices.to(device)
158
+
159
+ return args
160
+
161
+ @wp.func
162
+ def boundary_side_index(args: SideIndexArg, boundary_side_index: int):
163
+ """Boundary side to side index"""
164
+
165
+ return args.boundary_edge_indices[boundary_side_index]
166
+
167
+ @cached_arg_value
168
+ def side_arg_value(self, device) -> CellArg:
169
+ args = self.SideArg()
170
+
171
+ args.cell_arg = self.cell_arg_value(device)
172
+ args.edge_vertex_indices = self._edge_vertex_indices.to(device)
173
+ args.edge_quad_indices = self._edge_quad_indices.to(device)
174
+
175
+ return args
176
+
177
+ @wp.func
178
+ def side_position(args: SideArg, s: Sample):
179
+ edge_idx = args.edge_vertex_indices[s.element_index]
180
+ return (1.0 - s.element_coords[0]) * args.cell_arg.positions[edge_idx[0]] + s.element_coords[
181
+ 0
182
+ ] * args.cell_arg.positions[edge_idx[1]]
183
+
184
+ @wp.func
185
+ def side_deformation_gradient(args: SideArg, s: Sample):
186
+ edge_idx = args.edge_vertex_indices[s.element_index]
187
+ v0 = args.cell_arg.positions[edge_idx[0]]
188
+ v1 = args.cell_arg.positions[edge_idx[1]]
189
+ return v1 - v0
190
+
191
+ @wp.func
192
+ def side_inner_inverse_deformation_gradient(args: SideArg, s: Sample):
193
+ cell_index = Quadmesh2D.side_inner_cell_index(args, s.element_index)
194
+ cell_coords = Quadmesh2D.side_inner_cell_coords(args, s.element_index, s.element_coords)
195
+ return Quadmesh2D.cell_inverse_deformation_gradient(args.cell_arg, make_free_sample(cell_index, cell_coords))
196
+
197
+ @wp.func
198
+ def side_outer_inverse_deformation_gradient(args: SideArg, s: Sample):
199
+ cell_index = Quadmesh2D.side_outer_cell_index(args, s.element_index)
200
+ cell_coords = Quadmesh2D.side_outer_cell_coords(args, s.element_index, s.element_coords)
201
+ return Quadmesh2D.cell_inverse_deformation_gradient(args.cell_arg, make_free_sample(cell_index, cell_coords))
202
+
203
+ @wp.func
204
+ def side_measure(args: SideArg, s: Sample):
205
+ edge_idx = args.edge_vertex_indices[s.element_index]
206
+ v0 = args.cell_arg.positions[edge_idx[0]]
207
+ v1 = args.cell_arg.positions[edge_idx[1]]
208
+ return wp.length(v1 - v0)
209
+
210
+ @wp.func
211
+ def side_measure_ratio(args: SideArg, s: Sample):
212
+ inner = Quadmesh2D.side_inner_cell_index(args, s.element_index)
213
+ outer = Quadmesh2D.side_outer_cell_index(args, s.element_index)
214
+ inner_coords = Quadmesh2D.side_inner_cell_coords(args, s.element_index, s.element_coords)
215
+ outer_coords = Quadmesh2D.side_outer_cell_coords(args, s.element_index, s.element_coords)
216
+ return Quadmesh2D.side_measure(args, s) / wp.min(
217
+ Quadmesh2D.cell_measure(args.cell_arg, make_free_sample(inner, inner_coords)),
218
+ Quadmesh2D.cell_measure(args.cell_arg, make_free_sample(outer, outer_coords)),
219
+ )
220
+
221
+ @wp.func
222
+ def side_normal(args: SideArg, s: Sample):
223
+ edge_idx = args.edge_vertex_indices[s.element_index]
224
+ v0 = args.cell_arg.positions[edge_idx[0]]
225
+ v1 = args.cell_arg.positions[edge_idx[1]]
226
+ e = v1 - v0
227
+
228
+ return wp.normalize(wp.vec2(-e[1], e[0]))
229
+
230
+ @wp.func
231
+ def side_inner_cell_index(arg: SideArg, side_index: ElementIndex):
232
+ return arg.edge_quad_indices[side_index][0]
233
+
234
+ @wp.func
235
+ def side_outer_cell_index(arg: SideArg, side_index: ElementIndex):
236
+ return arg.edge_quad_indices[side_index][1]
237
+
238
+ @wp.func
239
+ def edge_to_quad_coords(args: SideArg, side_index: ElementIndex, quad_index: ElementIndex, side_coords: Coords):
240
+ edge_vidx = args.edge_vertex_indices[side_index]
241
+ quad_vidx = args.cell_arg.quad_vertex_indices[quad_index]
242
+
243
+ vs = edge_vidx[0]
244
+ ve = edge_vidx[1]
245
+
246
+ s = side_coords[0]
247
+
248
+ if vs == quad_vidx[0]:
249
+ return wp.select(ve == quad_vidx[1], Coords(0.0, s, 0.0), Coords(s, 0.0, 0.0))
250
+ elif vs == quad_vidx[1]:
251
+ return wp.select(ve == quad_vidx[2], Coords(1.0 - s, 0.0, 0.0), Coords(1.0, s, 0.0))
252
+ elif vs == quad_vidx[2]:
253
+ return wp.select(ve == quad_vidx[3], Coords(1.0, 1.0 - s, 0.0), Coords(1.0 - s, 1.0, 0.0))
254
+
255
+ return wp.select(ve == quad_vidx[0], Coords(s, 1.0, 0.0), Coords(0.0, 1.0 - s, 0.0))
256
+
257
+ @wp.func
258
+ def side_inner_cell_coords(args: SideArg, side_index: ElementIndex, side_coords: Coords):
259
+ inner_cell_index = Quadmesh2D.side_inner_cell_index(args, side_index)
260
+ return Quadmesh2D.edge_to_quad_coords(args, side_index, inner_cell_index, side_coords)
261
+
262
+ @wp.func
263
+ def side_outer_cell_coords(args: SideArg, side_index: ElementIndex, side_coords: Coords):
264
+ outer_cell_index = Quadmesh2D.side_outer_cell_index(args, side_index)
265
+ return Quadmesh2D.edge_to_quad_coords(args, side_index, outer_cell_index, side_coords)
266
+
267
+ @wp.func
268
+ def side_from_cell_coords(
269
+ args: SideArg,
270
+ side_index: ElementIndex,
271
+ quad_index: ElementIndex,
272
+ quad_coords: Coords,
273
+ ):
274
+ edge_vidx = args.edge_vertex_indices[side_index]
275
+ quad_vidx = args.cell_arg.quad_vertex_indices[quad_index]
276
+
277
+ vs = edge_vidx[0]
278
+ ve = edge_vidx[1]
279
+
280
+ cx = quad_coords[0]
281
+ cy = quad_coords[1]
282
+
283
+ if vs == quad_vidx[0]:
284
+ oc = wp.select(ve == quad_vidx[1], cx, cy)
285
+ ec = wp.select(ve == quad_vidx[1], cy, cx)
286
+ elif vs == quad_vidx[1]:
287
+ oc = wp.select(ve == quad_vidx[2], cy, 1.0 - cx)
288
+ ec = wp.select(ve == quad_vidx[2], 1.0 - cx, cy)
289
+ elif vs == quad_vidx[2]:
290
+ oc = wp.select(ve == quad_vidx[3], 1.0 - cx, 1.0 - cy)
291
+ ec = wp.select(ve == quad_vidx[3], 1.0 - cy, 1.0 - cx)
292
+ else:
293
+ oc = wp.select(ve == quad_vidx[0], 1.0 - cy, cx)
294
+ ec = wp.select(ve == quad_vidx[0], cx, 1.0 - cy)
295
+ return wp.select(oc == 0.0, Coords(OUTSIDE), Coords(ec, 0.0, 0.0))
296
+
297
+ @wp.func
298
+ def side_to_cell_arg(side_arg: SideArg):
299
+ return side_arg.cell_arg
300
+
301
+ def _build_topology(self, temporary_store: TemporaryStore):
302
+ from warp.fem.utils import compress_node_indices, masked_indices
303
+ from warp.utils import array_scan
304
+
305
+ device = self.quad_vertex_indices.device
306
+
307
+ vertex_quad_offsets, vertex_quad_indices, _, __ = compress_node_indices(
308
+ self.vertex_count(), self.quad_vertex_indices, temporary_store=temporary_store
309
+ )
310
+ self._vertex_quad_offsets = vertex_quad_offsets.detach()
311
+ self._vertex_quad_indices = vertex_quad_indices.detach()
312
+
313
+ vertex_start_edge_count = borrow_temporary(temporary_store, dtype=int, device=device, shape=self.vertex_count())
314
+ vertex_start_edge_count.array.zero_()
315
+ vertex_start_edge_offsets = borrow_temporary_like(vertex_start_edge_count, temporary_store=temporary_store)
316
+
317
+ vertex_edge_ends = borrow_temporary(temporary_store, dtype=int, device=device, shape=(4 * self.cell_count()))
318
+ vertex_edge_quads = borrow_temporary(
319
+ temporary_store, dtype=int, device=device, shape=(4 * self.cell_count(), 2)
320
+ )
321
+
322
+ # Count face edges starting at each vertex
323
+ wp.launch(
324
+ kernel=Quadmesh2D._count_starting_edges_kernel,
325
+ device=device,
326
+ dim=self.cell_count(),
327
+ inputs=[self.quad_vertex_indices, vertex_start_edge_count.array],
328
+ )
329
+
330
+ array_scan(in_array=vertex_start_edge_count.array, out_array=vertex_start_edge_offsets.array, inclusive=False)
331
+
332
+ # Count number of unique edges (deduplicate across faces)
333
+ vertex_unique_edge_count = vertex_start_edge_count
334
+ wp.launch(
335
+ kernel=Quadmesh2D._count_unique_starting_edges_kernel,
336
+ device=device,
337
+ dim=self.vertex_count(),
338
+ inputs=[
339
+ self._vertex_quad_offsets,
340
+ self._vertex_quad_indices,
341
+ self.quad_vertex_indices,
342
+ vertex_start_edge_offsets.array,
343
+ vertex_unique_edge_count.array,
344
+ vertex_edge_ends.array,
345
+ vertex_edge_quads.array,
346
+ ],
347
+ )
348
+
349
+ vertex_unique_edge_offsets = borrow_temporary_like(vertex_start_edge_offsets, temporary_store=temporary_store)
350
+ array_scan(in_array=vertex_start_edge_count.array, out_array=vertex_unique_edge_offsets.array, inclusive=False)
351
+
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
358
+ )
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])
363
+
364
+ self._edge_vertex_indices = wp.empty(shape=(edge_count,), dtype=wp.vec2i, device=device)
365
+ self._edge_quad_indices = wp.empty(shape=(edge_count,), dtype=wp.vec2i, device=device)
366
+
367
+ boundary_mask = borrow_temporary(temporary_store=temporary_store, shape=(edge_count,), dtype=int, device=device)
368
+
369
+ # Compress edge data
370
+ wp.launch(
371
+ kernel=Quadmesh2D._compress_edges_kernel,
372
+ device=device,
373
+ dim=self.vertex_count(),
374
+ inputs=[
375
+ vertex_start_edge_offsets.array,
376
+ vertex_unique_edge_offsets.array,
377
+ vertex_unique_edge_count.array,
378
+ vertex_edge_ends.array,
379
+ vertex_edge_quads.array,
380
+ self._edge_vertex_indices,
381
+ self._edge_quad_indices,
382
+ boundary_mask.array,
383
+ ],
384
+ )
385
+
386
+ vertex_start_edge_offsets.release()
387
+ vertex_unique_edge_offsets.release()
388
+ vertex_unique_edge_count.release()
389
+ vertex_edge_ends.release()
390
+ vertex_edge_quads.release()
391
+
392
+ # Flip normals if necessary
393
+ wp.launch(
394
+ kernel=Quadmesh2D._flip_edge_normals,
395
+ device=device,
396
+ dim=self.side_count(),
397
+ inputs=[self._edge_vertex_indices, self._edge_quad_indices, self.quad_vertex_indices, self.positions],
398
+ )
399
+
400
+ boundary_edge_indices, _ = masked_indices(boundary_mask.array, temporary_store=temporary_store)
401
+ self._boundary_edge_indices = boundary_edge_indices.detach()
402
+
403
+ boundary_mask.release()
404
+
405
+ @wp.kernel
406
+ def _count_starting_edges_kernel(
407
+ quad_vertex_indices: wp.array2d(dtype=int), vertex_start_edge_count: wp.array(dtype=int)
408
+ ):
409
+ t = wp.tid()
410
+ for k in range(4):
411
+ v0 = quad_vertex_indices[t, k]
412
+ v1 = quad_vertex_indices[t, (k + 1) % 4]
413
+
414
+ if v0 < v1:
415
+ wp.atomic_add(vertex_start_edge_count, v0, 1)
416
+ else:
417
+ wp.atomic_add(vertex_start_edge_count, v1, 1)
418
+
419
+ @wp.func
420
+ def _find(
421
+ needle: int,
422
+ values: wp.array(dtype=int),
423
+ beg: int,
424
+ end: int,
425
+ ):
426
+ for i in range(beg, end):
427
+ if values[i] == needle:
428
+ return i
429
+
430
+ return -1
431
+
432
+ @wp.kernel
433
+ def _count_unique_starting_edges_kernel(
434
+ vertex_quad_offsets: wp.array(dtype=int),
435
+ vertex_quad_indices: wp.array(dtype=int),
436
+ quad_vertex_indices: wp.array2d(dtype=int),
437
+ vertex_start_edge_offsets: wp.array(dtype=int),
438
+ vertex_start_edge_count: wp.array(dtype=int),
439
+ edge_ends: wp.array(dtype=int),
440
+ edge_quads: wp.array2d(dtype=int),
441
+ ):
442
+ v = wp.tid()
443
+
444
+ edge_beg = vertex_start_edge_offsets[v]
445
+
446
+ quad_beg = vertex_quad_offsets[v]
447
+ quad_end = vertex_quad_offsets[v + 1]
448
+
449
+ edge_cur = edge_beg
450
+
451
+ for quad in range(quad_beg, quad_end):
452
+ q = vertex_quad_indices[quad]
453
+
454
+ for k in range(4):
455
+ v0 = quad_vertex_indices[q, k]
456
+ v1 = quad_vertex_indices[q, (k + 1) % 4]
457
+
458
+ if v == wp.min(v0, v1):
459
+ other_v = wp.max(v0, v1)
460
+
461
+ # Check if other_v has been seen
462
+ seen_idx = Quadmesh2D._find(other_v, edge_ends, edge_beg, edge_cur)
463
+
464
+ if seen_idx == -1:
465
+ edge_ends[edge_cur] = other_v
466
+ edge_quads[edge_cur, 0] = q
467
+ edge_quads[edge_cur, 1] = q
468
+ edge_cur += 1
469
+ else:
470
+ edge_quads[seen_idx, 1] = q
471
+
472
+ vertex_start_edge_count[v] = edge_cur - edge_beg
473
+
474
+ @wp.kernel
475
+ def _compress_edges_kernel(
476
+ vertex_start_edge_offsets: wp.array(dtype=int),
477
+ vertex_unique_edge_offsets: wp.array(dtype=int),
478
+ vertex_unique_edge_count: wp.array(dtype=int),
479
+ uncompressed_edge_ends: wp.array(dtype=int),
480
+ uncompressed_edge_quads: wp.array2d(dtype=int),
481
+ edge_vertex_indices: wp.array(dtype=wp.vec2i),
482
+ edge_quad_indices: wp.array(dtype=wp.vec2i),
483
+ boundary_mask: wp.array(dtype=int),
484
+ ):
485
+ v = wp.tid()
486
+
487
+ start_beg = vertex_start_edge_offsets[v]
488
+ unique_beg = vertex_unique_edge_offsets[v]
489
+ unique_count = vertex_unique_edge_count[v]
490
+
491
+ for e in range(unique_count):
492
+ src_index = start_beg + e
493
+ edge_index = unique_beg + e
494
+
495
+ edge_vertex_indices[edge_index] = wp.vec2i(v, uncompressed_edge_ends[src_index])
496
+
497
+ q0 = uncompressed_edge_quads[src_index, 0]
498
+ q1 = uncompressed_edge_quads[src_index, 1]
499
+ edge_quad_indices[edge_index] = wp.vec2i(q0, q1)
500
+ if q0 == q1:
501
+ boundary_mask[edge_index] = 1
502
+ else:
503
+ boundary_mask[edge_index] = 0
504
+
505
+ @wp.kernel
506
+ def _flip_edge_normals(
507
+ edge_vertex_indices: wp.array(dtype=wp.vec2i),
508
+ edge_quad_indices: wp.array(dtype=wp.vec2i),
509
+ quad_vertex_indices: wp.array2d(dtype=int),
510
+ positions: wp.array(dtype=wp.vec2),
511
+ ):
512
+ e = wp.tid()
513
+
514
+ tri = edge_quad_indices[e][0]
515
+
516
+ quad_vidx = quad_vertex_indices[tri]
517
+ edge_vidx = edge_vertex_indices[e]
518
+
519
+ quad_centroid = (
520
+ positions[quad_vidx[0]] + positions[quad_vidx[1]] + positions[quad_vidx[2]] + positions[quad_vidx[3]]
521
+ ) / 4.0
522
+
523
+ v0 = positions[edge_vidx[0]]
524
+ v1 = positions[edge_vidx[1]]
525
+
526
+ edge_center = 0.5 * (v1 + v0)
527
+ edge_vec = v1 - v0
528
+ edge_normal = wp.vec2(-edge_vec[1], edge_vec[0])
529
+
530
+ # if edge normal points toward first triangle centroid, flip indices
531
+ if wp.dot(quad_centroid - edge_center, edge_normal) > 0.0:
532
+ edge_vertex_indices[e] = wp.vec2i(edge_vidx[1], edge_vidx[0])