warp-lang 1.2.2__py3-none-manylinux2014_aarch64.whl → 1.3.1__py3-none-manylinux2014_aarch64.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 (193) hide show
  1. warp/__init__.py +8 -6
  2. warp/autograd.py +823 -0
  3. warp/bin/warp.so +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
@@ -8,77 +8,101 @@
8
8
  ###########################################################################
9
9
  # Example Mixed Elasticity
10
10
  #
11
- # This example illustrates using Mixed FEM to solve a
12
- # 2D linear elasticity problem:
11
+ # This example illustrates using Mixed FEM to solve a nonlinear static elasticity equilibrium problem:
13
12
  #
14
- # Div[ E: D(u) ] = 0
13
+ # Div[ d/dF Psi(F(u)) ] = 0
15
14
  #
16
- # with Dirichlet boundary conditions on horizontal sides,
17
- # and E the elasticity rank-4 tensor
15
+ # with Dirichlet boundary conditions on vertical sides,
16
+ # and Psi an elastic potential function of the deformation gradient (here Neo-Hookean)
17
+ #
18
+ # which we write as a sequence of Newton iterations:
19
+ # int {sigma : grad v} = 0 for all displacement test functions v
20
+ # int {sigma : tau} = int{dPsi/dF : tau} + int{grad du : d2 Psi/dF2 : tau} for all stress test functions tau
18
21
  ###########################################################################
19
22
 
20
23
  import numpy as np
21
24
 
22
25
  import warp as wp
26
+ import warp.examples.fem.utils as fem_example_utils
23
27
  import warp.fem as fem
24
- from warp.sparse import bsr_mm, bsr_transposed
25
-
26
- try:
27
- from .bsr_utils import bsr_cg, invert_diagonal_bsr_mass_matrix
28
- from .mesh_utils import gen_quadmesh, gen_trimesh
29
- from .plot_utils import Plot
30
- except ImportError:
31
- from bsr_utils import bsr_cg, invert_diagonal_bsr_mass_matrix
32
- from mesh_utils import gen_quadmesh, gen_trimesh
33
- from plot_utils import Plot
34
-
35
-
36
- @wp.func
37
- def compute_stress(tau: wp.mat22, E: wp.mat33):
38
- """Strain to stress computation"""
39
- tau_sym = wp.vec3(tau[0, 0], tau[1, 1], tau[0, 1] + tau[1, 0])
40
- sig_sym = E * tau_sym
41
- return wp.mat22(sig_sym[0], 0.5 * sig_sym[2], 0.5 * sig_sym[2], sig_sym[1])
42
28
 
43
29
 
44
30
  @fem.integrand
45
- def symmetric_grad_form(
31
+ def displacement_gradient_form(
46
32
  s: fem.Sample,
47
33
  u: fem.Field,
48
34
  tau: fem.Field,
49
35
  ):
50
- """D(u) : tau"""
51
- return wp.ddot(tau(s), fem.D(u, s))
36
+ """grad(u) : tau"""
37
+ return wp.ddot(tau(s), fem.grad(u, s))
52
38
 
53
39
 
54
40
  @fem.integrand
55
- def stress_form(s: fem.Sample, u: fem.Field, tau: fem.Field, E: wp.mat33):
56
- """(E : D(u)) : tau"""
57
- return wp.ddot(tau(s), compute_stress(fem.D(u, s), E))
41
+ def nh_stress_form(s: fem.Sample, tau: fem.Field, u_cur: fem.Field, lame: wp.vec2):
42
+ """d Psi/dF : tau"""
43
+
44
+ F = wp.identity(n=2, dtype=float) + fem.grad(u_cur, s)
45
+
46
+ J = wp.determinant(F)
47
+ mu_nh = 2.0 * lame[1]
48
+ lambda_nh = lame[0] + lame[1]
49
+ gamma = 1.0 + mu_nh / lambda_nh
50
+
51
+ dJ_dS = wp.mat22(F[1, 1], -F[1, 0], -F[0, 1], F[0, 0])
52
+ nh_stress = mu_nh * F + lambda_nh * (J - gamma) * dJ_dS
53
+
54
+ return wp.ddot(tau(s), nh_stress)
55
+
56
+
57
+ @fem.integrand
58
+ def nh_stress_delta_form(s: fem.Sample, tau: fem.Field, u: fem.Field, u_cur: fem.Field, lame: wp.vec2):
59
+ """grad(u) : d2 Psi/dF2 : tau"""
60
+
61
+ tau_s = tau(s)
62
+ sigma_s = fem.grad(u, s)
63
+
64
+ F = wp.identity(n=2, dtype=float) + fem.grad(u_cur, s)
65
+
66
+ dJ_dF = wp.mat22(F[1, 1], -F[1, 0], -F[0, 1], F[0, 0])
67
+
68
+ mu_nh = 2.0 * lame[1]
69
+ lambda_nh = lame[0] + lame[1]
70
+
71
+ dpsi_dpsi = mu_nh * wp.ddot(tau_s, sigma_s) + lambda_nh * wp.ddot(dJ_dF * tau_s, dJ_dF * sigma_s)
72
+
73
+ # positive part of d2J_dS2
74
+ gamma = 1.0 + mu_nh / lambda_nh
75
+ J = wp.determinant(F)
76
+ if J >= gamma:
77
+ d2J_dF_sig = wp.mat22(sigma_s[1, 1], 0.0, 0.0, sigma_s[0, 0])
78
+ else:
79
+ d2J_dF_sig = wp.mat22(0.0, -sigma_s[1, 0], -sigma_s[0, 1], 0.0)
80
+
81
+ return dpsi_dpsi + lambda_nh * (J - gamma) * wp.ddot(d2J_dF_sig, tau_s)
58
82
 
59
83
 
60
84
  @fem.integrand
61
- def horizontal_boundary_projector_form(
85
+ def vertical_boundary_projector_form(
62
86
  s: fem.Sample,
63
87
  domain: fem.Domain,
64
88
  u: fem.Field,
65
89
  v: fem.Field,
66
90
  ):
67
- # non zero on horizontal boundary of domain only
91
+ # non zero on vertical boundary of domain only
68
92
  nor = fem.normal(domain, s)
69
- return wp.dot(u(s), v(s)) * wp.abs(nor[1])
93
+ return wp.dot(u(s), v(s)) * wp.abs(nor[0])
70
94
 
71
95
 
72
96
  @fem.integrand
73
- def horizontal_displacement_form(
97
+ def vertical_displacement_form(
74
98
  s: fem.Sample,
75
99
  domain: fem.Domain,
76
100
  v: fem.Field,
77
101
  displacement: float,
78
102
  ):
79
- # opposed to normal on horizontal boundary of domain only
103
+ # opposed to normal on vertical boundary of domain only
80
104
  nor = fem.normal(domain, s)
81
- return -wp.abs(nor[1]) * displacement * wp.dot(nor, v(s))
105
+ return -wp.abs(nor[0]) * displacement * wp.dot(nor, v(s))
82
106
 
83
107
 
84
108
  @fem.integrand
@@ -98,7 +122,6 @@ class Example:
98
122
  resolution=25,
99
123
  mesh="grid",
100
124
  displacement=0.1,
101
- young_modulus=1.0,
102
125
  poisson_ratio=0.5,
103
126
  nonconforming_stresses=False,
104
127
  ):
@@ -106,49 +129,45 @@ class Example:
106
129
 
107
130
  self._displacement = displacement
108
131
 
109
- # Grid or triangle mesh geometry
132
+ # Grid or mesh geometry
110
133
  if mesh == "tri":
111
- positions, tri_vidx = gen_trimesh(res=wp.vec2i(resolution))
134
+ positions, tri_vidx = fem_example_utils.gen_trimesh(res=wp.vec2i(resolution))
112
135
  self._geo = fem.Trimesh2D(tri_vertex_indices=tri_vidx, positions=positions)
113
136
  elif mesh == "quad":
114
- positions, quad_vidx = gen_quadmesh(res=wp.vec2i(resolution))
137
+ positions, quad_vidx = fem_example_utils.gen_quadmesh(res=wp.vec2i(resolution))
115
138
  self._geo = fem.Quadmesh2D(quad_vertex_indices=quad_vidx, positions=positions)
116
139
  else:
117
140
  self._geo = fem.Grid2D(res=wp.vec2i(resolution))
118
141
 
119
- # Strain-stress matrix
120
- young = young_modulus
121
- poisson = poisson_ratio
122
- self._elasticity_mat = wp.mat33(
123
- young
124
- / (1.0 - poisson * poisson)
125
- * np.array(
126
- [
127
- [1.0, poisson, 0.0],
128
- [poisson, 1.0, 0.0],
129
- [0.0, 0.0, (2.0 * (1.0 + poisson)) * (1.0 - poisson * poisson)],
130
- ]
131
- )
132
- )
142
+ # Lame coefficients from Young modulus and Poisson ratio
143
+ self._lame = wp.vec2(1.0 / (1.0 + poisson_ratio) * np.array([poisson_ratio / (1.0 - poisson_ratio), 0.5]))
133
144
 
134
- # Function spaces -- S_k for displacement, Q_{k-1}d for stress
145
+ # Function spaces -- S_k for displacement, Q_k or P_{k-1}d for stress
135
146
  self._u_space = fem.make_polynomial_space(
136
147
  self._geo, degree=degree, dtype=wp.vec2, element_basis=fem.ElementBasis.SERENDIPITY
137
148
  )
138
149
 
139
- # Store stress degrees of freedom as symmetric tensors (3 dof) rather than full 2x2 matrices
140
- tau_basis = fem.ElementBasis.NONCONFORMING_POLYNOMIAL if nonconforming_stresses else fem.ElementBasis.LAGRANGE
150
+ if isinstance(self._geo.reference_cell(), fem.geometry.element.Triangle):
151
+ # triangle elements
152
+ tau_basis = fem.ElementBasis.NONCONFORMING_POLYNOMIAL
153
+ tau_degree = degree - 1
154
+ else:
155
+ # square elements
156
+ tau_basis = fem.ElementBasis.LAGRANGE
157
+ tau_degree = degree
158
+
141
159
  self._tau_space = fem.make_polynomial_space(
142
160
  self._geo,
143
- degree=degree - 1,
161
+ degree=tau_degree,
144
162
  discontinuous=True,
145
163
  element_basis=tau_basis,
146
- dof_mapper=fem.SymmetricTensorMapper(wp.mat22),
164
+ family=fem.Polynomial.GAUSS_LEGENDRE,
165
+ dtype=wp.mat22,
147
166
  )
148
167
 
149
168
  self._u_field = self._u_space.make_field()
150
169
 
151
- self.renderer = Plot()
170
+ self.renderer = fem_example_utils.Plot()
152
171
 
153
172
  def step(self):
154
173
  boundary = fem.BoundarySides(self._geo)
@@ -158,14 +177,14 @@ class Example:
158
177
  u_bd_test = fem.make_test(space=self._u_space, domain=boundary)
159
178
  u_bd_trial = fem.make_trial(space=self._u_space, domain=boundary)
160
179
  u_bd_rhs = fem.integrate(
161
- horizontal_displacement_form,
180
+ vertical_displacement_form,
162
181
  fields={"v": u_bd_test},
163
182
  values={"displacement": self._displacement},
164
183
  nodal=True,
165
184
  output_dtype=wp.vec2d,
166
185
  )
167
186
  u_bd_matrix = fem.integrate(
168
- horizontal_boundary_projector_form, fields={"u": u_bd_trial, "v": u_bd_test}, nodal=True
187
+ vertical_boundary_projector_form, fields={"u": u_bd_trial, "v": u_bd_test}, nodal=True
169
188
  )
170
189
 
171
190
  # Stress/velocity coupling
@@ -173,30 +192,44 @@ class Example:
173
192
  tau_test = fem.make_test(space=self._tau_space, domain=domain)
174
193
  tau_trial = fem.make_trial(space=self._tau_space, domain=domain)
175
194
 
176
- sym_grad_matrix = fem.integrate(symmetric_grad_form, fields={"u": u_trial, "tau": tau_test})
177
- stress_matrix = fem.integrate(
178
- stress_form, fields={"u": u_trial, "tau": tau_test}, values={"E": self._elasticity_mat}
179
- )
195
+ gradient_matrix = fem.integrate(displacement_gradient_form, fields={"u": u_trial, "tau": tau_test}).transpose()
180
196
 
181
197
  # Compute inverse of the (block-diagonal) tau mass matrix
182
198
  tau_inv_mass_matrix = fem.integrate(tensor_mass_form, fields={"sig": tau_trial, "tau": tau_test}, nodal=True)
183
- invert_diagonal_bsr_mass_matrix(tau_inv_mass_matrix)
199
+ fem_example_utils.invert_diagonal_bsr_matrix(tau_inv_mass_matrix)
200
+
201
+ # Newton iterations (without line-search for simplicity)
202
+ for newton_iteration in range(5):
203
+ stress_matrix = fem.integrate(
204
+ nh_stress_delta_form,
205
+ fields={"u_cur": self._u_field, "u": u_trial, "tau": tau_test},
206
+ values={"lame": self._lame},
207
+ )
208
+
209
+ stress_rhs = fem.integrate(
210
+ nh_stress_form,
211
+ fields={"u_cur": self._u_field, "tau": tau_test},
212
+ values={"lame": self._lame},
213
+ output_dtype=wp.vec(length=stress_matrix.block_shape[0], dtype=wp.float64),
214
+ )
184
215
 
185
- # Assemble system matrix
186
- u_matrix = bsr_mm(bsr_transposed(sym_grad_matrix), bsr_mm(tau_inv_mass_matrix, stress_matrix))
216
+ # Assemble system matrix
217
+ u_matrix = gradient_matrix @ tau_inv_mass_matrix @ stress_matrix
187
218
 
188
- # Enforce boundary conditions
189
- u_rhs = wp.zeros_like(u_bd_rhs)
190
- fem.project_linear_system(u_matrix, u_rhs, u_bd_matrix, u_bd_rhs)
219
+ # Enforce boundary conditions (apply displacement only at first iteration)
220
+ u_rhs = -gradient_matrix @ (tau_inv_mass_matrix @ stress_rhs)
221
+ fem.project_linear_system(u_matrix, u_rhs, u_bd_matrix, u_bd_rhs if newton_iteration == 0 else None)
191
222
 
192
- x = wp.zeros_like(u_rhs)
193
- bsr_cg(u_matrix, b=u_rhs, x=x, tol=1.0e-16, quiet=self._quiet)
223
+ x = wp.zeros_like(u_rhs)
224
+ fem_example_utils.bsr_cg(u_matrix, b=u_rhs, x=x, quiet=self._quiet)
194
225
 
195
- # Extract result
196
- self._u_field.dof_values = x
226
+ # Extract result -- cast to float32 and accumulate to displacement field
227
+ delta_u = wp.empty_like(self._u_field.dof_values)
228
+ wp.utils.array_cast(in_array=x, out_array=delta_u)
229
+ fem.utils.array_axpy(x=delta_u, y=self._u_field.dof_values)
197
230
 
198
231
  def render(self):
199
- self.renderer.add_surface_vector("solution", self._u_field)
232
+ self.renderer.add_field("solution", self._u_field)
200
233
 
201
234
 
202
235
  if __name__ == "__main__":
@@ -208,8 +241,7 @@ if __name__ == "__main__":
208
241
  parser.add_argument("--device", type=str, default=None, help="Override the default Warp device.")
209
242
  parser.add_argument("--resolution", type=int, default=25, help="Grid resolution.")
210
243
  parser.add_argument("--degree", type=int, default=2, help="Polynomial degree of shape functions.")
211
- parser.add_argument("--displacement", type=float, default=0.1)
212
- parser.add_argument("--young_modulus", type=float, default=1.0)
244
+ parser.add_argument("--displacement", type=float, default=-0.5)
213
245
  parser.add_argument("--poisson_ratio", type=float, default=0.5)
214
246
  parser.add_argument("--mesh", choices=("grid", "tri", "quad"), default="grid", help="Mesh type")
215
247
  parser.add_argument(
@@ -231,7 +263,6 @@ if __name__ == "__main__":
231
263
  resolution=args.resolution,
232
264
  mesh=args.mesh,
233
265
  displacement=args.displacement,
234
- young_modulus=args.young_modulus,
235
266
  poisson_ratio=args.poisson_ratio,
236
267
  nonconforming_stresses=args.nonconforming_stresses,
237
268
  )
@@ -239,4 +270,4 @@ if __name__ == "__main__":
239
270
  example.render()
240
271
 
241
272
  if not args.headless:
242
- example.renderer.plot()
273
+ example.renderer.plot(options={"solution": {"displacement": {}}})
@@ -18,27 +18,18 @@
18
18
  ###########################################################################
19
19
 
20
20
  import warp as wp
21
+ import warp.examples.fem.utils as fem_example_utils
21
22
  import warp.fem as fem
22
23
  from warp.fem.utils import array_axpy
23
- from warp.sparse import bsr_copy, bsr_mm, bsr_mv
24
24
 
25
- try:
26
- from .bsr_utils import SaddleSystem, bsr_solve_saddle
27
- from .mesh_utils import gen_trimesh
28
- from .plot_utils import Plot
29
- except ImportError:
30
- from bsr_utils import SaddleSystem, bsr_solve_saddle
31
- from mesh_utils import gen_trimesh
32
- from plot_utils import Plot
33
25
 
34
-
35
- @fem.integrand
36
- def u_boundary_value(s: fem.Sample, domain: fem.Domain, v: fem.Field, top_vel: float):
26
+ @wp.func
27
+ def u_boundary_value(x: wp.vec2, box_height: float, top_velocity: float):
37
28
  # Horizontal velocity on top of domain, zero elsewhere
38
- if domain(s)[1] == 1.0:
39
- return wp.dot(wp.vec2f(top_vel, 0.0), v(s))
29
+ if x[1] >= box_height:
30
+ return wp.vec2(top_velocity, 0.0)
40
31
 
41
- return wp.dot(wp.vec2f(0.0, 0.0), v(s))
32
+ return wp.vec2(0.0, 0.0)
42
33
 
43
34
 
44
35
  @fem.integrand
@@ -100,8 +91,8 @@ class Example:
100
91
  viscosity = top_velocity / Re
101
92
 
102
93
  if tri_mesh:
103
- positions, tri_vidx = gen_trimesh(res=wp.vec2i(res))
104
- geo = fem.Trimesh2D(tri_vertex_indices=tri_vidx, positions=positions)
94
+ positions, tri_vidx = fem_example_utils.gen_trimesh(res=wp.vec2i(res))
95
+ geo = fem.Trimesh2D(tri_vertex_indices=tri_vidx, positions=positions, build_bvh=True)
105
96
  else:
106
97
  geo = fem.Grid2D(res=wp.vec2i(res))
107
98
 
@@ -132,10 +123,14 @@ class Example:
132
123
  u_bd_test = fem.make_test(space=u_space, domain=boundary)
133
124
  u_bd_trial = fem.make_trial(space=u_space, domain=boundary)
134
125
  u_bd_projector = fem.integrate(mass_form, fields={"u": u_bd_trial, "v": u_bd_test}, nodal=True)
126
+
127
+ # Define an implicit field for our boundary condition value and integrate
128
+ u_bd_field = fem.ImplicitField(
129
+ domain=boundary, func=u_boundary_value, values={"top_velocity": top_velocity, "box_height": 1.0}
130
+ )
135
131
  u_bd_value = fem.integrate(
136
- u_boundary_value,
137
- fields={"v": u_bd_test},
138
- values={"top_vel": top_velocity},
132
+ mass_form,
133
+ fields={"u": u_bd_field, "v": u_bd_test},
139
134
  nodal=True,
140
135
  output_dtype=wp.vec2d,
141
136
  )
@@ -145,15 +140,11 @@ class Example:
145
140
  u_bd_rhs = wp.zeros_like(u_bd_value)
146
141
  fem.project_linear_system(u_matrix, u_bd_rhs, u_bd_projector, u_bd_value, normalize_projector=False)
147
142
 
148
- # div_bd_rhs = div_matrix * u_bd_rhs
149
- div_bd_rhs = wp.zeros(shape=(div_matrix.nrow,), dtype=div_matrix.scalar_type)
150
- bsr_mv(div_matrix, u_bd_value, y=div_bd_rhs, alpha=-1.0)
151
-
152
- # div_matrix = div_matrix - div_matrix * bd_projector
153
- bsr_mm(x=bsr_copy(div_matrix), y=u_bd_projector, z=div_matrix, alpha=-1.0, beta=1.0)
143
+ div_bd_rhs = -div_matrix @ u_bd_value
144
+ div_matrix -= div_matrix @ u_bd_projector
154
145
 
155
146
  # Assemble saddle system
156
- self._saddle_system = SaddleSystem(u_matrix, div_matrix)
147
+ self._saddle_system = fem_example_utils.SaddleSystem(u_matrix, div_matrix)
157
148
 
158
149
  # Save data for computing time steps rhs
159
150
  self._u_bd_projector = u_bd_projector
@@ -165,8 +156,8 @@ class Example:
165
156
  self._u_field = u_space.make_field()
166
157
  self._p_field = p_space.make_field()
167
158
 
168
- self.renderer = Plot()
169
- self.renderer.add_surface_vector("velocity", self._u_field)
159
+ self.renderer = fem_example_utils.Plot()
160
+ self.renderer.add_field("velocity", self._u_field)
170
161
 
171
162
  def step(self):
172
163
  self.current_frame += 1
@@ -180,7 +171,7 @@ class Example:
180
171
 
181
172
  # Apply boundary conditions
182
173
  # u_rhs = (I - P) * u_rhs + u_bd_rhs
183
- bsr_mv(self._u_bd_projector, x=u_rhs, y=u_rhs, alpha=-1.0, beta=1.0)
174
+ wp.sparse.bsr_mv(self._u_bd_projector, x=u_rhs, y=u_rhs, alpha=-1.0, beta=1.0)
184
175
  array_axpy(x=self._u_bd_rhs, y=u_rhs, alpha=1.0, beta=1.0)
185
176
 
186
177
  p_rhs = self._div_bd_rhs
@@ -190,7 +181,7 @@ class Example:
190
181
  wp.utils.array_cast(out_array=x_u, in_array=self._u_field.dof_values)
191
182
  wp.utils.array_cast(out_array=x_p, in_array=self._p_field.dof_values)
192
183
 
193
- bsr_solve_saddle(
184
+ fem_example_utils.bsr_solve_saddle(
194
185
  saddle_system=self._saddle_system,
195
186
  tol=1.0e-6,
196
187
  x_u=x_u,
@@ -205,7 +196,7 @@ class Example:
205
196
 
206
197
  def render(self):
207
198
  self.renderer.begin_frame(time=self.current_frame * self.sim_dt)
208
- self.renderer.add_surface_vector("velocity", self._u_field)
199
+ self.renderer.add_field("velocity", self._u_field)
209
200
  self.renderer.end_frame()
210
201
 
211
202
 
@@ -251,7 +242,12 @@ if __name__ == "__main__":
251
242
  example.step()
252
243
  example.render()
253
244
 
254
- example.renderer.add_surface_vector("velocity_final", example._u_field)
245
+ example.renderer.add_field("velocity_final", example._u_field)
255
246
 
256
247
  if not args.headless:
257
- example.renderer.plot(streamlines={"velocity_final"})
248
+ example.renderer.plot(
249
+ options={
250
+ "velocity": {"arrows": {"glyph_scale": 0.25}},
251
+ "velocity_final": {"streamlines": {"density": 2.0}},
252
+ }
253
+ )