warp-lang 1.0.0b2__py3-none-manylinux2014_x86_64.whl → 1.0.0b6__py3-none-manylinux2014_x86_64.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 (269) 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.so +0 -0
  57. warp/bin/warp.so +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/fem/field/discrete_field.py +0 -80
  257. warp/fem/space/nodal_function_space.py +0 -233
  258. warp/tests/test_all.py +0 -223
  259. warp/tests/test_array_scan.py +0 -60
  260. warp/tests/test_base.py +0 -208
  261. warp/tests/test_unresolved_func.py +0 -7
  262. warp/tests/test_unresolved_symbol.py +0 -7
  263. warp_lang-1.0.0b2.dist-info/METADATA +0 -26
  264. warp_lang-1.0.0b2.dist-info/RECORD +0 -378
  265. /warp/tests/{test_compile_consts_dummy.py → aux_test_compile_consts_dummy.py} +0 -0
  266. /warp/tests/{test_reference_reference.py → aux_test_reference_reference.py} +0 -0
  267. /warp/tests/{test_square.py → aux_test_square.py} +0 -0
  268. {warp_lang-1.0.0b2.dist-info → warp_lang-1.0.0b6.dist-info}/LICENSE.md +0 -0
  269. {warp_lang-1.0.0b2.dist-info → warp_lang-1.0.0b6.dist-info}/top_level.txt +0 -0
@@ -13,152 +13,184 @@ import warp as wp
13
13
 
14
14
  import numpy as np
15
15
 
16
- from warp.fem.types import *
17
- from warp.fem.geometry import Grid2D, Trimesh2D
18
- from warp.fem.field import make_test, make_trial, make_restriction
19
- from warp.fem.space import make_polynomial_space
20
- from warp.fem.domain import Cells, BoundarySides
21
- from warp.fem.integrate import integrate, interpolate
22
- from warp.fem.operator import normal, integrand, D, div
23
-
24
- from plot_utils import plot_velocities, plot_surface
25
- from bsr_utils import bsr_to_scipy
26
- from mesh_utils import gen_trimesh
27
-
16
+ import warp.fem as fem
17
+
18
+ try:
19
+ from .plot_utils import Plot
20
+ from .bsr_utils import bsr_to_scipy
21
+ from .mesh_utils import gen_trimesh, gen_quadmesh
22
+ except ImportError:
23
+ from plot_utils import Plot
24
+ from bsr_utils import bsr_to_scipy
25
+ from mesh_utils import gen_trimesh, gen_quadmesh
26
+
27
+ # Need to solve a saddle-point system, use scipy for simplicity
28
28
  from scipy.sparse import bmat
29
29
  from scipy.sparse.linalg import spsolve
30
30
 
31
- import matplotlib.pyplot as plt
32
-
33
31
 
34
- @integrand
32
+ @fem.integrand
35
33
  def constant_form(val: wp.vec2):
36
34
  return val
37
35
 
38
36
 
39
- @integrand
40
- def viscosity_form(s: Sample, u: Field, v: Field, nu: float):
41
- return nu * wp.ddot(D(u, s), D(v, s))
37
+ @fem.integrand
38
+ def viscosity_form(s: fem.Sample, u: fem.Field, v: fem.Field, nu: float):
39
+ return nu * wp.ddot(fem.D(u, s), fem.D(v, s))
42
40
 
43
41
 
44
- @integrand
42
+ @fem.integrand
45
43
  def top_mass_form(
46
- s: Sample,
47
- domain: Domain,
48
- u: Field,
49
- v: Field,
44
+ s: fem.Sample,
45
+ domain: fem.Domain,
46
+ u: fem.Field,
47
+ v: fem.Field,
50
48
  ):
51
49
  # non zero on top boundary of domain only
52
- nor = normal(domain, s)
50
+ nor = fem.normal(domain, s)
53
51
  return wp.dot(u(s), v(s)) * wp.max(0.0, nor[1])
54
52
 
55
53
 
56
- @integrand
54
+ @fem.integrand
57
55
  def mass_form(
58
- s: Sample,
59
- u: Field,
60
- v: Field,
56
+ s: fem.Sample,
57
+ u: fem.Field,
58
+ v: fem.Field,
61
59
  ):
62
60
  return wp.dot(u(s), v(s))
63
61
 
64
62
 
65
- @integrand
63
+ @fem.integrand
66
64
  def div_form(
67
- s: Sample,
68
- u: Field,
69
- q: Field,
65
+ s: fem.Sample,
66
+ u: fem.Field,
67
+ q: fem.Field,
70
68
  ):
71
- return q(s) * div(u, s)
72
-
73
-
74
- if __name__ == "__main__":
75
- wp.init()
76
- wp.set_module_options({"enable_backward": False})
69
+ return q(s) * fem.div(u, s)
77
70
 
78
71
 
72
+ class Example:
79
73
  parser = argparse.ArgumentParser()
80
74
  parser.add_argument("--resolution", type=int, default=50)
81
75
  parser.add_argument("--degree", type=int, default=2)
82
76
  parser.add_argument("--top_velocity", type=float, default=1.0)
83
77
  parser.add_argument("--viscosity", type=float, default=1.0)
84
78
  parser.add_argument("--boundary_strength", type=float, default=100.0)
85
- parser.add_argument("--tri_mesh", action="store_true", help="Use a triangular mesh")
86
- args = parser.parse_args()
87
-
88
- top_velocity = wp.vec2(args.top_velocity, 0.0)
89
-
90
- if args.tri_mesh:
91
- positions, tri_vidx = gen_trimesh(res=vec2i(args.resolution))
92
- geo = Trimesh2D(tri_vertex_indices=tri_vidx, positions=positions)
93
- else:
94
- geo = Grid2D(res=vec2i(args.resolution))
95
-
96
- domain = Cells(geometry=geo)
97
- boundary = BoundarySides(geo)
98
-
99
- # Function spaces -- Q_d for vel, Q_{d-1} for pressure
100
- u_space = make_polynomial_space(geo, degree=args.degree, dtype=wp.vec2)
101
- p_space = make_polynomial_space(geo, degree=args.degree-1)
102
-
103
- # Interpolate initial condition on boundary (mostly for testing)
104
- f = u_space.make_field()
105
- f_boundary = make_restriction(f, domain=boundary)
106
- interpolate(constant_form, dest=f_boundary, values={"val": top_velocity})
107
-
108
- # Viscosity
109
- u_test = make_test(space=u_space, domain=domain)
110
- u_trial = make_trial(space=u_space, domain=domain)
111
-
112
- u_visc_matrix = integrate(
113
- viscosity_form,
114
- fields={"u": u_trial, "v": u_test},
115
- values={"nu": args.viscosity},
79
+ parser.add_argument("--mesh", choices=("grid", "tri", "quad"), default="grid", help="Mesh type")
80
+ parser.add_argument(
81
+ "--nonconforming_pressures", action="store_true", help="For grid, use non-conforming pressure (Q_d/P_{d-1})"
116
82
  )
117
83
 
118
- # Weak velocity boundary conditions
119
- u_bd_test = make_test(space=u_space, domain=boundary)
120
- u_bd_trial = make_trial(space=u_space, domain=boundary)
121
- u_rhs = integrate(top_mass_form, fields={"u": f.trace(), "v": u_bd_test})
122
- u_bd_matrix = integrate(mass_form, fields={"u": u_bd_trial, "v": u_bd_test})
123
-
124
- # Pressure-velocity coupling
125
- p_test = make_test(space=p_space, domain=domain)
126
- div_matrix = integrate(div_form, fields={"u": u_trial, "q": p_test})
127
-
128
- # Solve with scipy
129
- # Assemble saddle-point system with velocity, pressure, and zero-average-pressure constraint
130
- u_rhs = u_rhs.numpy() * args.boundary_strength
131
- u_matrix = bsr_to_scipy(u_visc_matrix) + args.boundary_strength * bsr_to_scipy(u_bd_matrix)
132
-
133
- div_matrix = bsr_to_scipy(div_matrix)
134
-
135
- ones = np.ones(shape=(p_space.node_count(), 1), dtype=float)
136
- saddle_system = bmat(
137
- [
138
- [u_matrix, div_matrix.transpose(), None],
139
- [div_matrix, None, ones],
140
- [None, ones.transpose(), None],
141
- ],
142
- format="csr",
143
- )
144
-
145
- saddle_rhs = np.zeros(saddle_system.shape[0])
146
- u_slice = slice(0, 2 * u_space.node_count())
147
- p_slice = slice(
148
- 2 * u_space.node_count(), 2 * u_space.node_count() + p_space.node_count()
149
- )
150
- saddle_rhs[u_slice] = u_rhs.flatten()
84
+ def __init__(self, stage=None, quiet=False, args=None, **kwargs):
85
+ if args is None:
86
+ # Read args from kwargs, add default arg values from parser
87
+ args = argparse.Namespace(**kwargs)
88
+ args = Example.parser.parse_args(args=[], namespace=args)
89
+ self._args = args
90
+ self._quiet = quiet
91
+
92
+ # Grid or triangle mesh geometry
93
+ if args.mesh == "tri":
94
+ positions, tri_vidx = gen_trimesh(res=wp.vec2i(args.resolution))
95
+ geo = fem.Trimesh2D(tri_vertex_indices=tri_vidx, positions=positions)
96
+ elif args.mesh == "quad":
97
+ positions, quad_vidx = gen_quadmesh(res=wp.vec2i(args.resolution))
98
+ geo = fem.Quadmesh2D(quad_vertex_indices=quad_vidx, positions=positions)
99
+ else:
100
+ geo = fem.Grid2D(res=wp.vec2i(args.resolution))
101
+
102
+ # Function spaces -- Q_d for vel, P_{d-1} for pressure
103
+ u_space = fem.make_polynomial_space(geo, degree=args.degree, dtype=wp.vec2)
104
+ if args.mesh != "tri" and args.nonconforming_pressures:
105
+ p_space = fem.make_polynomial_space(
106
+ geo, degree=args.degree - 1, element_basis=fem.ElementBasis.NONCONFORMING_POLYNOMIAL
107
+ )
108
+ else:
109
+ p_space = fem.make_polynomial_space(geo, degree=args.degree - 1)
110
+
111
+ # Vector and scalar fields
112
+ self._u_field = u_space.make_field()
113
+ self._p_field = p_space.make_field()
114
+
115
+ # Interpolate initial condition on boundary (for example purposes)
116
+ self._bd_field = u_space.make_field()
117
+ f_boundary = fem.make_restriction(self._bd_field, domain=fem.BoundarySides(geo))
118
+ top_velocity = wp.vec2(args.top_velocity, 0.0)
119
+ fem.interpolate(constant_form, dest=f_boundary, values={"val": top_velocity})
120
+
121
+ self.renderer = Plot(stage)
122
+
123
+ def update(self):
124
+ args = self._args
125
+ u_space = self._u_field.space
126
+ p_space = self._p_field.space
127
+ geo = u_space.geometry
128
+
129
+ domain = fem.Cells(geometry=geo)
130
+ boundary = fem.BoundarySides(geo)
131
+
132
+ # Viscosity
133
+ u_test = fem.make_test(space=u_space, domain=domain)
134
+ u_trial = fem.make_trial(space=u_space, domain=domain)
135
+
136
+ u_visc_matrix = fem.integrate(
137
+ viscosity_form,
138
+ fields={"u": u_trial, "v": u_test},
139
+ values={"nu": args.viscosity},
140
+ )
141
+
142
+ # Weak velocity boundary conditions
143
+ u_bd_test = fem.make_test(space=u_space, domain=boundary)
144
+ u_bd_trial = fem.make_trial(space=u_space, domain=boundary)
145
+ u_rhs = fem.integrate(top_mass_form, fields={"u": self._bd_field.trace(), "v": u_bd_test})
146
+ u_bd_matrix = fem.integrate(mass_form, fields={"u": u_bd_trial, "v": u_bd_test})
147
+
148
+ # Pressure-velocity coupling
149
+ p_test = fem.make_test(space=p_space, domain=domain)
150
+ div_matrix = fem.integrate(div_form, fields={"u": u_trial, "q": p_test})
151
+
152
+ # Solve with scipy
153
+ # Assemble saddle-point system with velocity, pressure, and zero-average-pressure constraint
154
+ u_rhs = u_rhs.numpy() * args.boundary_strength
155
+ u_matrix = bsr_to_scipy(u_visc_matrix) + args.boundary_strength * bsr_to_scipy(u_bd_matrix)
156
+
157
+ div_matrix = bsr_to_scipy(div_matrix)
158
+
159
+ ones = np.ones(shape=(p_space.node_count(), 1), dtype=float)
160
+ saddle_system = bmat(
161
+ [
162
+ [u_matrix, div_matrix.transpose(), None],
163
+ [div_matrix, None, ones],
164
+ [None, ones.transpose(), None],
165
+ ],
166
+ format="csr",
167
+ )
168
+
169
+ saddle_rhs = np.zeros(saddle_system.shape[0])
170
+ u_slice = slice(0, 2 * u_space.node_count())
171
+ p_slice = slice(2 * u_space.node_count(), 2 * u_space.node_count() + p_space.node_count())
172
+ saddle_rhs[u_slice] = u_rhs.flatten()
173
+
174
+ x = spsolve(saddle_system, saddle_rhs)
175
+
176
+ # Extract result
177
+
178
+ self._u_field.dof_values = x[u_slice].reshape((-1, 2))
179
+ self._p_field.dof_values = x[p_slice]
180
+
181
+ def render(self):
182
+ self.renderer.add_surface("pressure", self._p_field)
183
+ self.renderer.add_surface_vector("velocity", self._u_field)
151
184
 
152
- x = spsolve(saddle_system, saddle_rhs)
153
185
 
154
- # Extract result
155
- u_field = u_space.make_field()
156
- p_field = p_space.make_field()
186
+ if __name__ == "__main__":
187
+ wp.init()
188
+ wp.set_module_options({"enable_backward": False})
157
189
 
158
- u_field.dof_values = x[u_slice].reshape((-1, 2))
159
- p_field.dof_values = x[p_slice]
190
+ args = Example.parser.parse_args()
160
191
 
161
- plot_surface(p_field)
162
- plot_velocities(u_field)
192
+ example = Example(args=args)
193
+ example.update()
194
+ example.render()
163
195
 
164
- plt.show()
196
+ example.renderer.plot()
@@ -9,65 +9,62 @@ import math
9
9
  import warp as wp
10
10
  import numpy as np
11
11
 
12
- from warp.fem.types import *
13
- from warp.fem.geometry import Grid2D, ExplicitGeometryPartition
14
- from warp.fem.field import make_test, make_trial
15
- from warp.fem.space import make_polynomial_space, make_space_partition
16
- from warp.fem.domain import Cells
17
- from warp.fem.integrate import integrate, interpolate
18
- from warp.fem.operator import integrand, D, div
19
- from warp.fem.quadrature import PicQuadrature
20
- from warp.fem.utils import array_axpy
12
+ import warp.fem as fem
21
13
 
22
14
  from warp.utils import array_cast
23
- from warp.sparse import bsr_transposed, bsr_mm, bsr_axpy
24
-
25
- from plot_utils import plot_grid_streamlines, plot_grid_surface
26
- from bsr_utils import bsr_cg, bsr_mv
15
+ from warp.fem.utils import array_axpy
16
+ from warp.sparse import bsr_transposed, bsr_mm, bsr_axpy, bsr_mv
27
17
 
28
- import matplotlib.pyplot as plt
18
+ # Import example utilities
19
+ # Make sure that works both when imported as module and run as standalone file
20
+ try:
21
+ from .bsr_utils import bsr_cg
22
+ from .plot_utils import Plot
23
+ except ImportError:
24
+ from bsr_utils import bsr_cg
25
+ from plot_utils import Plot
29
26
 
30
27
 
31
- @integrand
32
- def vel_from_particles_form(s: Sample, particle_vel: wp.array(dtype=wp.vec2), v: Field):
28
+ @fem.integrand
29
+ def vel_from_particles_form(s: fem.Sample, particle_vel: wp.array(dtype=wp.vec2), v: fem.Field):
33
30
  vel = particle_vel[s.qp_index]
34
31
  return wp.dot(vel, v(s))
35
32
 
36
33
 
37
- @integrand
38
- def viscosity_form(s: Sample, u: Field, v: Field, nu: float):
39
- return nu * wp.ddot(D(u, s), D(v, s))
34
+ @fem.integrand
35
+ def viscosity_form(s: fem.Sample, u: fem.Field, v: fem.Field, nu: float):
36
+ return nu * wp.ddot(fem.D(u, s), fem.D(v, s))
40
37
 
41
38
 
42
- @integrand
39
+ @fem.integrand
43
40
  def mass_form(
44
- s: Sample,
45
- u: Field,
46
- v: Field,
41
+ s: fem.Sample,
42
+ u: fem.Field,
43
+ v: fem.Field,
47
44
  ):
48
45
  return wp.dot(u(s), v(s))
49
46
 
50
47
 
51
- @integrand
48
+ @fem.integrand
52
49
  def scalar_mass_form(
53
- s: Sample,
54
- p: Field,
55
- q: Field,
50
+ s: fem.Sample,
51
+ p: fem.Field,
52
+ q: fem.Field,
56
53
  ):
57
54
  return p(s) * q(s)
58
55
 
59
56
 
60
- @integrand
57
+ @fem.integrand
61
58
  def div_form(
62
- s: Sample,
63
- u: Field,
64
- q: Field,
59
+ s: fem.Sample,
60
+ u: fem.Field,
61
+ q: fem.Field,
65
62
  ):
66
- return q(s) * div(u, s)
63
+ return q(s) * fem.div(u, s)
67
64
 
68
65
 
69
- @integrand
70
- def cell_activity(s: Sample, domain: Domain, c1: wp.vec2, c2: wp.vec2, radius: float):
66
+ @fem.integrand
67
+ def cell_activity(s: fem.Sample, domain: fem.Domain, c1: wp.vec2, c2: wp.vec2, radius: float):
71
68
  pos = domain(s)
72
69
  if wp.length(pos - c1) < radius:
73
70
  return 0.0
@@ -81,130 +78,162 @@ def inverse_array_kernel(m: wp.array(dtype=wp.float64)):
81
78
  m[wp.tid()] = wp.float64(1.0) / m[wp.tid()]
82
79
 
83
80
 
81
+ class Example:
82
+ def __init__(self, stage=None, quiet=False):
83
+ self._quiet = quiet
84
+
85
+ self.res = 50
86
+ self.cell_size = 1.0 / self.res
87
+
88
+ self.vel = 1.0
89
+ self.viscosity = 100.0
90
+ self.compliance = 0.01
91
+ self.bd_strength = 100000.0
92
+
93
+ geo = fem.Grid2D(res=wp.vec2i(self.res))
94
+
95
+ # Displacement boundary conditions are defined by two circles going in opposite directions
96
+ # Sample particles along those
97
+ circle_radius = 0.15
98
+ c1_center = wp.vec2(0.25, 0.5)
99
+ c2_center = wp.vec2(0.75, 0.5)
100
+ particles, particle_areas, particle_velocities = self._gen_particles(circle_radius, c1_center, c2_center)
101
+
102
+ # Disable cells that are interior to the circles
103
+ cell_space = fem.make_polynomial_space(geo, degree=0)
104
+ activity = cell_space.make_field()
105
+ fem.interpolate(
106
+ cell_activity,
107
+ dest=activity,
108
+ values={"c1": c1_center, "c2": c2_center, "radius": circle_radius - self.cell_size},
109
+ )
110
+
111
+ # Explicitly define the active geometry partition from those cells
112
+ self._active_partition = fem.ExplicitGeometryPartition(geo, wp.array(activity.dof_values.numpy(), dtype=int))
113
+ if not self._quiet:
114
+ print("Active cells:", self._active_partition.cell_count())
115
+
116
+ # Function spaces -- Q1 for vel, Q0 for pressure
117
+ u_space = fem.make_polynomial_space(geo, degree=1, dtype=wp.vec2)
118
+ p_space = fem.make_polynomial_space(geo, degree=0)
119
+
120
+ self._active_space_partition = fem.make_space_partition(
121
+ space=u_space, geometry_partition=self._active_partition
122
+ )
123
+ self._active_p_space_partition = fem.make_space_partition(
124
+ space=p_space, geometry_partition=self._active_partition
125
+ )
126
+
127
+ self._u_field = u_space.make_field()
128
+ self._p_field = p_space.make_field()
129
+
130
+ # Particle-based quadrature rule over active cells
131
+ domain = fem.Cells(geometry=self._active_partition)
132
+ self._pic_quadrature = fem.PicQuadrature(domain, particles, particle_areas)
133
+ self._particle_velocities = particle_velocities
134
+
135
+ self.renderer = Plot(stage)
136
+
137
+ def update(self):
138
+ u_space = self._u_field.space
139
+ p_space = self._p_field.space
140
+
141
+ # Weakly-enforced boundary condition on particles
142
+ u_test = fem.make_test(space=u_space, space_partition=self._active_space_partition)
143
+ u_trial = fem.make_trial(space=u_space, space_partition=self._active_space_partition)
144
+
145
+ u_rhs = fem.integrate(
146
+ vel_from_particles_form,
147
+ quadrature=self._pic_quadrature,
148
+ fields={"v": u_test},
149
+ values={"particle_vel": self._particle_velocities},
150
+ output_dtype=wp.vec2d,
151
+ )
152
+ u_bd_matrix = fem.integrate(mass_form, quadrature=self._pic_quadrature, fields={"u": u_trial, "v": u_test})
153
+
154
+ # Viscosity
155
+ u_visc_matrix = fem.integrate(
156
+ viscosity_form,
157
+ fields={"u": u_trial, "v": u_test},
158
+ values={"nu": self.viscosity},
159
+ )
160
+
161
+ # Pressure-velocity coupling
162
+ p_test = fem.make_test(space=p_space, space_partition=self._active_p_space_partition)
163
+ p_trial = fem.make_trial(space=p_space, space_partition=self._active_p_space_partition)
164
+
165
+ div_matrix = fem.integrate(div_form, fields={"u": u_trial, "q": p_test})
166
+ inv_p_mass_matrix = fem.integrate(scalar_mass_form, fields={"p": p_trial, "q": p_test})
167
+ wp.launch(
168
+ kernel=inverse_array_kernel,
169
+ dim=inv_p_mass_matrix.values.shape,
170
+ device=inv_p_mass_matrix.values.device,
171
+ inputs=[inv_p_mass_matrix.values],
172
+ )
173
+
174
+ # Assemble linear system
175
+ u_matrix = u_visc_matrix
176
+ bsr_axpy(u_bd_matrix, u_matrix, alpha=self.bd_strength)
177
+
178
+ div_matrix_t = bsr_transposed(div_matrix)
179
+ gradient_matrix = bsr_mm(div_matrix_t, inv_p_mass_matrix)
180
+ bsr_mm(gradient_matrix, div_matrix, u_matrix, alpha=1.0 / self.compliance, beta=1.0)
181
+
182
+ array_axpy(u_rhs, u_rhs, alpha=0.0, beta=self.bd_strength)
183
+
184
+ # Solve for displacement
185
+ u_res = wp.zeros_like(u_rhs)
186
+ bsr_cg(u_matrix, x=u_res, b=u_rhs, quiet=self._quiet)
187
+
188
+ # Compute pressure from displacement
189
+ div_u = bsr_mv(A=div_matrix, x=u_res)
190
+ p_res = bsr_mv(A=inv_p_mass_matrix, x=div_u, alpha=-1)
191
+
192
+ # Copy to fields
193
+ u_nodes = wp.indexedarray(self._u_field.dof_values, indices=self._active_space_partition.space_node_indices())
194
+ p_nodes = wp.indexedarray(self._p_field.dof_values, indices=self._active_p_space_partition.space_node_indices())
195
+
196
+ array_cast(in_array=u_res, out_array=u_nodes)
197
+ array_cast(in_array=p_res, out_array=p_nodes)
198
+
199
+ def render(self):
200
+ self.renderer.add_surface("pressure", self._p_field)
201
+ self.renderer.add_surface_vector("velocity", self._u_field)
202
+
203
+ def _gen_particles(self, circle_radius, c1_center, c2_center):
204
+ """Generate some particles along two circles defining velocity boundary conditions"""
205
+
206
+ # Generate particles defining the transfer displacement
207
+ particles_per_circle = int(2.0 * math.pi * circle_radius * self.res)
208
+
209
+ angles = np.linspace(0, 2.0 * math.pi, particles_per_circle, endpoint=False)
210
+
211
+ n_particles = 2 * particles_per_circle
212
+ particles = np.empty((n_particles, 2), dtype=float)
213
+
214
+ particles[:particles_per_circle, 0] = c1_center[0] + circle_radius * np.cos(angles)
215
+ particles[:particles_per_circle, 1] = c1_center[1] + circle_radius * np.sin(angles)
216
+ particles[particles_per_circle:, 0] = c2_center[0] + circle_radius * np.cos(angles)
217
+ particles[particles_per_circle:, 1] = c2_center[1] + circle_radius * np.sin(angles)
218
+
219
+ particle_areas = np.ones(n_particles) * self.cell_size**2
220
+ particle_velocities = np.zeros_like(particles)
221
+ particle_velocities[:particles_per_circle, 0] = self.vel
222
+ particle_velocities[particles_per_circle:, 0] = -self.vel
223
+
224
+ particles = wp.array(particles, dtype=wp.vec2)
225
+ particle_areas = wp.array(particle_areas, dtype=float)
226
+ particle_velocities = wp.array(particle_velocities, dtype=wp.vec2)
227
+
228
+ return particles, particle_areas, particle_velocities
229
+
230
+
84
231
  if __name__ == "__main__":
85
232
  wp.init()
86
233
  wp.set_module_options({"enable_backward": False})
87
234
 
88
- res = 50
89
- cell_size = 1.0 / res
90
- geo = Grid2D(res=vec2i(res))
91
-
92
- vel = 1.0
93
- viscosity = 100.0
94
- compliance = 0.01
95
- bd_strength = 100000.0
96
-
97
- # Generate particles defining the transfer displacement
98
- circle_radius = 0.15
99
- c1_center = wp.vec2(0.25, 0.5)
100
- c2_center = wp.vec2(0.75, 0.5)
101
- particles_per_circle = int(2.0 * math.pi * circle_radius * res)
102
-
103
- angles = np.linspace(0, 2.0 * math.pi, particles_per_circle, endpoint=False)
104
-
105
- n_particles = 2 * particles_per_circle
106
- particles = np.empty((n_particles, 2), dtype=float)
107
-
108
- particles[:particles_per_circle, 0] = c1_center[0] + circle_radius * np.cos(angles)
109
- particles[:particles_per_circle, 1] = c1_center[1] + circle_radius * np.sin(angles)
110
- particles[particles_per_circle:, 0] = c2_center[0] + circle_radius * np.cos(angles)
111
- particles[particles_per_circle:, 1] = c2_center[1] + circle_radius * np.sin(angles)
112
-
113
- particle_areas = np.ones(n_particles) * cell_size**2
114
- particle_velocities = np.zeros_like(particles)
115
- particle_velocities[:particles_per_circle, 0] = vel
116
- particle_velocities[particles_per_circle:, 0] = -vel
117
-
118
- particles = wp.array(particles, dtype=wp.vec2)
119
- particle_areas = wp.array(particle_areas, dtype=float)
120
- particle_velocities = wp.array(particle_velocities, dtype=wp.vec2)
121
-
122
- # Disable cells that are interior to the circles
123
- cell_space = make_polynomial_space(geo, degree=0)
124
- activity = cell_space.make_field()
125
- interpolate(
126
- cell_activity,
127
- dest=activity,
128
- values={"c1": c1_center, "c2": c2_center, "radius": circle_radius - cell_size},
129
- )
130
-
131
- active_partition = ExplicitGeometryPartition(geo, wp.array(activity.dof_values.numpy(), dtype=int))
132
- print("Active cells:", active_partition.cell_count())
133
-
134
- # Function spaces -- Q1 for vel, Q0 for pressure
135
- u_space = make_polynomial_space(geo, degree=1, dtype=wp.vec2)
136
- p_space = make_polynomial_space(geo, degree=0)
137
- active_space_partition = make_space_partition(space=u_space, geometry_partition=active_partition)
138
- active_p_space_partition = make_space_partition(space=p_space, geometry_partition=active_partition)
139
-
140
- domain = Cells(geometry=active_partition)
141
- pic_quadrature = PicQuadrature(domain, particles, particle_areas)
142
-
143
- # Boundary condition on particles
144
- u_test = make_test(space=u_space, space_partition=active_space_partition, domain=domain)
145
- u_trial = make_trial(space=u_space, space_partition=active_space_partition, domain=domain)
146
-
147
- u_rhs = integrate(
148
- vel_from_particles_form,
149
- quadrature=pic_quadrature,
150
- fields={"v": u_test},
151
- values={"particle_vel": particle_velocities},
152
- output_dtype=wp.vec2d
153
- )
154
-
155
- u_bd_matrix = integrate(mass_form, quadrature=pic_quadrature, fields={"u": u_trial, "v": u_test})
156
-
157
- # Viscosity
158
- u_visc_matrix = integrate(
159
- viscosity_form,
160
- fields={"u": u_trial, "v": u_test},
161
- values={"nu": viscosity},
162
- )
163
-
164
- # Pressure-velocity coupling
165
- p_test = make_test(space=p_space, space_partition=active_p_space_partition, domain=domain)
166
- p_trial = make_trial(space=p_space, space_partition=active_p_space_partition, domain=domain)
167
-
168
- div_matrix = integrate(div_form, fields={"u": u_trial, "q": p_test})
169
- inv_p_mass_matrix = integrate(scalar_mass_form, fields={"p": p_trial, "q": p_test})
170
- wp.launch(
171
- kernel=inverse_array_kernel,
172
- dim=inv_p_mass_matrix.values.shape,
173
- device=inv_p_mass_matrix.values.device,
174
- inputs=[inv_p_mass_matrix.values],
175
- )
176
-
177
- # Assemble linear system
178
- u_matrix = u_visc_matrix
179
- bsr_axpy(u_bd_matrix, u_matrix, alpha=bd_strength)
180
-
181
- div_matrix_t = bsr_transposed(div_matrix)
182
- gradient_matrix = bsr_mm(div_matrix_t, inv_p_mass_matrix)
183
- bsr_mm(gradient_matrix, div_matrix, u_matrix, alpha=1.0 / compliance, beta=1.0)
184
-
185
- array_axpy(u_rhs, u_rhs, alpha=0.0, beta=bd_strength)
186
-
187
- # Solve for displacement
188
- u_res = wp.zeros_like(u_rhs)
189
- bsr_cg(u_matrix, x=u_res, b=u_rhs)
190
-
191
- # Recompute pressure
192
- p_res = wp.zeros(n=active_p_space_partition.node_count(), dtype=wp.float64)
193
- p_tmp = wp.empty_like(p_res)
194
- bsr_mv(A=div_matrix, x=u_res, y=p_tmp)
195
- bsr_mv(A=inv_p_mass_matrix, x=p_tmp, y=p_res, alpha=-1)
196
-
197
- # Display result
198
- u_field = u_space.make_field()
199
- p_field = p_space.make_field()
200
-
201
- u_nodes = wp.indexedarray(u_field.dof_values, indices=active_space_partition.space_node_indices())
202
- p_nodes = wp.indexedarray(p_field.dof_values, indices=active_p_space_partition.space_node_indices())
203
-
204
- array_cast(in_array=u_res, out_array=u_nodes)
205
- array_cast(in_array=p_res, out_array=p_nodes)
206
-
207
- plot_grid_surface(p_field)
208
- plot_grid_streamlines(u_field)
209
-
210
- plt.show()
235
+ example = Example()
236
+ example.update()
237
+ example.render()
238
+
239
+ example.renderer.plot(streamlines=["velocity"])