warp-lang 1.3.3__py3-none-win_amd64.whl → 1.4.0__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 (107) hide show
  1. warp/__init__.py +6 -0
  2. warp/autograd.py +59 -6
  3. warp/bin/warp-clang.dll +0 -0
  4. warp/bin/warp.dll +0 -0
  5. warp/build_dll.py +8 -10
  6. warp/builtins.py +126 -4
  7. warp/codegen.py +435 -53
  8. warp/config.py +1 -1
  9. warp/context.py +678 -403
  10. warp/dlpack.py +2 -0
  11. warp/examples/benchmarks/benchmark_cloth.py +10 -0
  12. warp/examples/core/example_render_opengl.py +12 -10
  13. warp/examples/fem/example_adaptive_grid.py +251 -0
  14. warp/examples/fem/example_apic_fluid.py +1 -1
  15. warp/examples/fem/example_diffusion_3d.py +2 -2
  16. warp/examples/fem/example_magnetostatics.py +1 -1
  17. warp/examples/fem/example_streamlines.py +1 -0
  18. warp/examples/fem/utils.py +23 -4
  19. warp/examples/sim/example_cloth.py +50 -6
  20. warp/fem/__init__.py +2 -0
  21. warp/fem/adaptivity.py +493 -0
  22. warp/fem/field/field.py +2 -1
  23. warp/fem/field/nodal_field.py +18 -26
  24. warp/fem/field/test.py +4 -4
  25. warp/fem/field/trial.py +4 -4
  26. warp/fem/geometry/__init__.py +1 -0
  27. warp/fem/geometry/adaptive_nanogrid.py +843 -0
  28. warp/fem/geometry/nanogrid.py +55 -28
  29. warp/fem/space/__init__.py +1 -1
  30. warp/fem/space/nanogrid_function_space.py +69 -35
  31. warp/fem/utils.py +113 -107
  32. warp/jax_experimental.py +28 -15
  33. warp/native/array.h +0 -1
  34. warp/native/builtin.h +103 -6
  35. warp/native/bvh.cu +2 -0
  36. warp/native/cuda_util.cpp +14 -0
  37. warp/native/cuda_util.h +2 -0
  38. warp/native/error.cpp +4 -2
  39. warp/native/exports.h +99 -17
  40. warp/native/mat.h +97 -0
  41. warp/native/mesh.cpp +36 -0
  42. warp/native/mesh.cu +51 -0
  43. warp/native/mesh.h +1 -0
  44. warp/native/quat.h +43 -0
  45. warp/native/spatial.h +6 -0
  46. warp/native/vec.h +74 -0
  47. warp/native/warp.cpp +2 -1
  48. warp/native/warp.cu +10 -3
  49. warp/native/warp.h +8 -1
  50. warp/paddle.py +382 -0
  51. warp/sim/__init__.py +1 -0
  52. warp/sim/collide.py +519 -0
  53. warp/sim/integrator_euler.py +18 -5
  54. warp/sim/integrator_featherstone.py +5 -5
  55. warp/sim/integrator_vbd.py +1026 -0
  56. warp/sim/model.py +49 -23
  57. warp/stubs.py +459 -0
  58. warp/tape.py +2 -0
  59. warp/tests/aux_test_dependent.py +1 -0
  60. warp/tests/aux_test_name_clash1.py +32 -0
  61. warp/tests/aux_test_name_clash2.py +32 -0
  62. warp/tests/aux_test_square.py +1 -0
  63. warp/tests/test_array.py +188 -0
  64. warp/tests/test_async.py +3 -3
  65. warp/tests/test_atomic.py +6 -0
  66. warp/tests/test_closest_point_edge_edge.py +93 -1
  67. warp/tests/test_codegen.py +62 -15
  68. warp/tests/test_codegen_instancing.py +1457 -0
  69. warp/tests/test_collision.py +486 -0
  70. warp/tests/test_compile_consts.py +3 -28
  71. warp/tests/test_dlpack.py +170 -0
  72. warp/tests/test_examples.py +22 -8
  73. warp/tests/test_fast_math.py +10 -4
  74. warp/tests/test_fem.py +64 -0
  75. warp/tests/test_func.py +46 -0
  76. warp/tests/test_implicit_init.py +49 -0
  77. warp/tests/test_jax.py +58 -0
  78. warp/tests/test_mat.py +84 -0
  79. warp/tests/test_mesh_query_point.py +188 -0
  80. warp/tests/test_module_hashing.py +40 -0
  81. warp/tests/test_multigpu.py +3 -3
  82. warp/tests/test_overwrite.py +8 -0
  83. warp/tests/test_paddle.py +852 -0
  84. warp/tests/test_print.py +89 -0
  85. warp/tests/test_quat.py +111 -0
  86. warp/tests/test_reload.py +31 -1
  87. warp/tests/test_scalar_ops.py +2 -0
  88. warp/tests/test_static.py +412 -0
  89. warp/tests/test_streams.py +64 -3
  90. warp/tests/test_struct.py +4 -4
  91. warp/tests/test_torch.py +24 -0
  92. warp/tests/test_triangle_closest_point.py +137 -0
  93. warp/tests/test_types.py +1 -1
  94. warp/tests/test_vbd.py +386 -0
  95. warp/tests/test_vec.py +143 -0
  96. warp/tests/test_vec_scalar_ops.py +139 -0
  97. warp/tests/unittest_suites.py +12 -0
  98. warp/tests/unittest_utils.py +9 -5
  99. warp/thirdparty/dlpack.py +3 -1
  100. warp/types.py +150 -28
  101. warp/utils.py +37 -14
  102. {warp_lang-1.3.3.dist-info → warp_lang-1.4.0.dist-info}/METADATA +10 -8
  103. {warp_lang-1.3.3.dist-info → warp_lang-1.4.0.dist-info}/RECORD +106 -94
  104. warp/tests/test_point_triangle_closest_point.py +0 -143
  105. {warp_lang-1.3.3.dist-info → warp_lang-1.4.0.dist-info}/LICENSE.md +0 -0
  106. {warp_lang-1.3.3.dist-info → warp_lang-1.4.0.dist-info}/WHEEL +0 -0
  107. {warp_lang-1.3.3.dist-info → warp_lang-1.4.0.dist-info}/top_level.txt +0 -0
warp/dlpack.py CHANGED
@@ -124,6 +124,8 @@ def device_to_dlpack(wp_device) -> DLDevice:
124
124
 
125
125
 
126
126
  def dtype_to_dlpack(wp_dtype) -> DLDataType:
127
+ if wp_dtype == warp.bool:
128
+ return (DLDataTypeCode.kDLBool, 8, 1)
127
129
  if wp_dtype == warp.int8:
128
130
  return (DLDataTypeCode.kDLInt, 8, 1)
129
131
  elif wp_dtype == warp.uint8:
@@ -219,6 +219,16 @@ def run_benchmark(mode, dim, timers, render=False):
219
219
 
220
220
  integrator = benchmark_cloth_jax.JxIntegrator(cloth)
221
221
 
222
+ elif mode == "paddle_cpu":
223
+ import benchmark_cloth_paddle
224
+
225
+ integrator = benchmark_cloth_paddle.TrIntegrator(cloth, "cpu")
226
+
227
+ elif mode == "paddle_gpu":
228
+ import benchmark_cloth_paddle
229
+
230
+ integrator = benchmark_cloth_paddle.TrIntegrator(cloth, "gpu")
231
+
222
232
  else:
223
233
  raise RuntimeError("Unknown simulation backend")
224
234
 
@@ -21,6 +21,9 @@ import warp.render
21
21
 
22
22
  class Example:
23
23
  def __init__(self, num_tiles=4, custom_tile_arrangement=False):
24
+ if num_tiles < 1:
25
+ raise ValueError("num_tiles must be greater than or equal to 1.")
26
+
24
27
  self.renderer = wp.render.OpenGLRenderer(vsync=False)
25
28
  instance_ids = []
26
29
 
@@ -31,16 +34,15 @@ class Example:
31
34
  positions = None
32
35
  sizes = None
33
36
 
34
- if num_tiles > 1:
35
- # set up instances to hide one of the capsules in each tile
36
- for i in range(num_tiles):
37
- instances = [j for j in np.arange(13) if j != i + 2]
38
- instance_ids.append(instances)
39
- if custom_tile_arrangement:
40
- angle = np.pi * 2.0 / num_tiles * i
41
- positions.append((int(np.cos(angle) * 150 + 250), int(np.sin(angle) * 150 + 250)))
42
- sizes.append((150, 150))
43
- self.renderer.setup_tiled_rendering(instance_ids, tile_positions=positions, tile_sizes=sizes)
37
+ # set up instances to hide one of the capsules in each tile
38
+ for i in range(num_tiles):
39
+ instances = [j for j in np.arange(13) if j != i + 2]
40
+ instance_ids.append(instances)
41
+ if custom_tile_arrangement:
42
+ angle = np.pi * 2.0 / num_tiles * i
43
+ positions.append((int(np.cos(angle) * 150 + 250), int(np.sin(angle) * 150 + 250)))
44
+ sizes.append((150, 150))
45
+ self.renderer.setup_tiled_rendering(instance_ids, tile_positions=positions, tile_sizes=sizes)
44
46
 
45
47
  self.renderer.render_ground()
46
48
 
@@ -0,0 +1,251 @@
1
+ # Copyright (c) 2024 NVIDIA CORPORATION. All rights reserved.
2
+ # NVIDIA CORPORATION and its licensors retain all intellectual property
3
+ # and proprietary rights in and to this software, related documentation
4
+ # and any modifications thereto. Any use, reproduction, disclosure or
5
+ # distribution of this software and related documentation without an express
6
+ # license agreement from NVIDIA CORPORATION is strictly prohibited.
7
+
8
+ ###########################################################################
9
+ # Example Adaptive Grid
10
+ #
11
+ # Demonstrates using an adaptive grid to increase the simulation resolition
12
+ # near a collider boundary.
13
+ #
14
+ ###########################################################################
15
+ import os.path
16
+
17
+ import numpy as np
18
+
19
+ import warp as wp
20
+ import warp.examples
21
+ import warp.examples.fem.utils as fem_example_utils
22
+ import warp.fem as fem
23
+ from warp.examples.fem.example_apic_fluid import divergence_form, solve_incompressibility
24
+
25
+
26
+ @fem.integrand
27
+ def inflow_velocity(
28
+ s: fem.Sample,
29
+ domain: fem.Domain,
30
+ bounds_lo: wp.vec3,
31
+ bounds_hi: wp.vec3,
32
+ ):
33
+ x = fem.position(domain, s)
34
+
35
+ if x[1] <= bounds_lo[1] or x[2] <= bounds_lo[2] or x[1] >= bounds_hi[1] or x[2] >= bounds_hi[2]:
36
+ return wp.vec3(0.0)
37
+
38
+ if x[0] <= bounds_lo[0] or x[0] >= bounds_hi[0]:
39
+ return wp.vec3(1.0, 0.0, 0.0)
40
+
41
+ return wp.vec3(0.0)
42
+
43
+
44
+ @fem.integrand
45
+ def noslip_projector_form(
46
+ s: fem.Sample,
47
+ u: fem.Field,
48
+ v: fem.Field,
49
+ ):
50
+ return wp.dot(u(s), v(s))
51
+
52
+
53
+ @fem.integrand
54
+ def mass_form(
55
+ s: fem.Sample,
56
+ u: fem.Field,
57
+ v: fem.Field,
58
+ ):
59
+ return u(s) * v(s)
60
+
61
+
62
+ @fem.integrand
63
+ def side_divergence_form(s: fem.Sample, domain: fem.Domain, u: fem.Field, psi: fem.Field):
64
+ # normal velocity jump (non-zero at resolution boundaries)
65
+ return -wp.dot(fem.jump(u, s), fem.normal(domain, s)) * psi(s)
66
+
67
+
68
+ @wp.func
69
+ def refinement_field(xyz: wp.vec3, volume: wp.uint64):
70
+ # use distance to collider as refinement function
71
+ uvw = wp.volume_world_to_index(volume, xyz)
72
+ sdf = wp.volume_sample_f(volume, uvw, wp.Volume.LINEAR)
73
+
74
+ if sdf < 0.0:
75
+ return sdf
76
+
77
+ # combine with heuristical distance to keep coarsening past nvdb narrowband
78
+ return 0.5 * wp.max(wp.length(xyz) - 20.0, sdf)
79
+
80
+
81
+ @fem.integrand
82
+ def pressure_anomaly_field(s: fem.Sample, domain: fem.Domain, pressure: fem.Field):
83
+ # for visualization, deduce affine part such that grad P = u_x
84
+ x = domain(s)
85
+ return pressure(s) + x[0]
86
+
87
+
88
+ class Example:
89
+ def __init__(self, quiet=False, degree=2, base_resolution=8, level_count=4, headless: bool = False):
90
+ self._quiet = quiet
91
+ self._degree = degree
92
+
93
+ # Start from a coarse, dense grid
94
+ res = wp.vec3i(2 * base_resolution, base_resolution // 2, base_resolution)
95
+ bounds_lo = wp.vec3(-50.0, 0.0, -17.5)
96
+ bounds_hi = wp.vec3(50.0, 12.5, 17.5)
97
+ sim_vol = fem_example_utils.gen_volume(res=res, bounds_lo=bounds_lo, bounds_hi=bounds_hi)
98
+
99
+ # load collision volume
100
+ collider_path = os.path.join(warp.examples.get_asset_directory(), "rocks.nvdb")
101
+ with open(collider_path, "rb") as file:
102
+ # create Volume object
103
+ collider = wp.Volume.load_from_nvdb(file)
104
+
105
+ # Make adaptive grid from coarse base and refinement field
106
+ refinement = fem.ImplicitField(
107
+ domain=fem.Cells(fem.Nanogrid(sim_vol)), func=refinement_field, values={"volume": collider.id}
108
+ )
109
+ self._geo = fem.adaptivity.adaptive_nanogrid_from_field(
110
+ sim_vol, level_count, refinement_field=refinement, grading="face"
111
+ )
112
+
113
+ # Function spaces for velocity, scalars and pressure (Pk / Pk / Pk-1)
114
+ self._u_basis = fem.make_polynomial_basis_space(geo=self._geo, degree=self._degree)
115
+ u_space = fem.make_collocated_function_space(self._u_basis, dtype=wp.vec3)
116
+ p_space = fem.make_polynomial_space(geo=self._geo, degree=self._degree - 1, dtype=float)
117
+
118
+ self.pressure_field = p_space.make_field()
119
+ self.pressure_anomaly_field = p_space.make_field()
120
+ self.velocity_field = u_space.make_field()
121
+
122
+ # Initialize velocity field with BC
123
+ bounds_scale = 0.9999 # account for difference between bounds and actual grid extents
124
+ bounds_center = 0.5 * (bounds_hi + bounds_lo)
125
+ bounds_extent = 0.5 * (bounds_hi - bounds_lo)
126
+ fem.interpolate(
127
+ inflow_velocity,
128
+ dest=fem.make_restriction(self.velocity_field, domain=fem.BoundarySides(self._geo)),
129
+ values={
130
+ "bounds_lo": bounds_center - bounds_scale * bounds_extent,
131
+ "bounds_hi": bounds_center + bounds_scale * bounds_extent,
132
+ },
133
+ )
134
+
135
+ self.plot = fem_example_utils.Plot()
136
+
137
+ def render(self):
138
+ # self.renderer.add_field("solution", self.pressure_field)
139
+ self.plot.add_field("pressure_anomaly", self.pressure_anomaly_field)
140
+ self.plot.add_field("velocity", self.velocity_field)
141
+
142
+ def step(self):
143
+ u_space = self.velocity_field.space
144
+ p_space = self.pressure_field.space
145
+
146
+ # Boundary condition projector and matrices
147
+ boundary = fem.BoundarySides(self._geo)
148
+ bd_test = fem.make_test(u_space, domain=boundary)
149
+ bd_trial = fem.make_trial(u_space, domain=boundary)
150
+ dirichlet_projector = fem.integrate(
151
+ noslip_projector_form, fields={"u": bd_test, "v": bd_trial}, nodal=True, output_dtype=float
152
+ )
153
+ fem.normalize_dirichlet_projector(dirichlet_projector)
154
+
155
+ # (Diagonal) mass matrix
156
+ s_space = fem.make_collocated_function_space(self._u_basis, dtype=float)
157
+ rho_test = fem.make_test(s_space)
158
+ rho_trial = fem.make_trial(s_space)
159
+ inv_mass_matrix = fem.integrate(
160
+ mass_form, fields={"u": rho_trial, "v": rho_test}, nodal=True, output_dtype=float
161
+ )
162
+ fem_example_utils.invert_diagonal_bsr_matrix(inv_mass_matrix)
163
+
164
+ # Assemble divergence operator matrix
165
+ p_test = fem.make_test(p_space)
166
+ u_trial = fem.make_trial(u_space)
167
+ divergence_matrix = fem.integrate(
168
+ divergence_form,
169
+ fields={"u": u_trial, "psi": p_test},
170
+ output_dtype=float,
171
+ )
172
+
173
+ # need to account for discontinuities at resolution boundaries (t-junctions)
174
+ p_side_test = fem.make_test(p_space, domain=fem.Sides(self._geo))
175
+ u_side_trial = fem.make_trial(u_space, domain=fem.Sides(self._geo))
176
+ divergence_matrix += fem.integrate(
177
+ side_divergence_form,
178
+ fields={"u": u_side_trial, "psi": p_side_test},
179
+ output_dtype=float,
180
+ )
181
+
182
+ # Solve incompressibility
183
+ solve_incompressibility(
184
+ divergence_matrix,
185
+ dirichlet_projector,
186
+ inv_mass_matrix.values,
187
+ self.pressure_field.dof_values,
188
+ self.velocity_field.dof_values,
189
+ quiet=self._quiet,
190
+ )
191
+
192
+ fem.interpolate(
193
+ pressure_anomaly_field,
194
+ dest=self.pressure_anomaly_field,
195
+ fields={"pressure": self.pressure_field},
196
+ )
197
+
198
+
199
+ if __name__ == "__main__":
200
+ import argparse
201
+
202
+ wp.set_module_options({"enable_backward": False})
203
+
204
+ parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter)
205
+ parser.add_argument("--device", type=str, default=None, help="Override the default Warp device.")
206
+ parser.add_argument("--resolution", type=int, default=8, help="Grid resolution.")
207
+ parser.add_argument("--degree", type=int, default=2, help="Polynomial degree of shape functions.")
208
+ parser.add_argument("--level_count", type=int, default=4, help="Number of refinement levels.")
209
+ parser.add_argument(
210
+ "--headless",
211
+ action="store_true",
212
+ help="Run in headless mode, suppressing the opening of any graphical windows.",
213
+ )
214
+ parser.add_argument("--quiet", action="store_true", help="Suppresses the printing out of iteration residuals.")
215
+
216
+ args = parser.parse_known_args()[0]
217
+
218
+ with wp.ScopedDevice(args.device):
219
+ example = Example(
220
+ quiet=args.quiet,
221
+ degree=args.degree,
222
+ base_resolution=args.resolution,
223
+ level_count=args.level_count,
224
+ headless=args.headless,
225
+ )
226
+
227
+ example.step()
228
+ example.render()
229
+
230
+ if not args.headless:
231
+ ref_geom = None
232
+ try:
233
+ from pxr import Usd, UsdGeom
234
+
235
+ stage = Usd.Stage.Open(os.path.join(warp.examples.get_asset_directory(), "rocks.usd"))
236
+ mesh = UsdGeom.Mesh(stage.GetPrimAtPath("/root/rocks"))
237
+ points = np.array((mesh.GetPointsAttr().Get()))
238
+ counts = np.array((mesh.GetFaceVertexCountsAttr().Get()))
239
+ indices = np.array(mesh.GetFaceVertexIndicesAttr().Get())
240
+ ref_geom = (points, counts, indices)
241
+ except Exception:
242
+ pass
243
+
244
+ example.plot.plot(
245
+ {
246
+ "rows": 2,
247
+ "ref_geom": ref_geom,
248
+ "velocity": {"streamlines": {"density": 25, "glyph_scale": 0.01}},
249
+ "pressure_anomaly": {},
250
+ }
251
+ )
@@ -169,7 +169,7 @@ def solve_incompressibility(
169
169
  # For simplicity, assemble Schur complement and solve with CG
170
170
  schur = bsr_mm(divergence_mat, transposed_divergence_mat)
171
171
 
172
- fem_example_utils.bsr_cg(schur, b=rhs, x=pressure, quiet=quiet, tol=1.0e-6)
172
+ fem_example_utils.bsr_cg(schur, b=rhs, x=pressure, quiet=quiet, tol=1.0e-6, method="cr", max_iters=1000)
173
173
 
174
174
  # Apply pressure to velocity
175
175
  bsr_mv(A=transposed_divergence_mat, x=pressure, y=velocity, alpha=1.0, beta=1.0)
@@ -146,7 +146,7 @@ if __name__ == "__main__":
146
146
  parser.add_argument(
147
147
  "--boundary_compliance", type=float, default=0.0, help="Dirichlet boundary condition compliance."
148
148
  )
149
- parser.add_argument("--mesh", choices=("grid", "tet", "hex", "nano"), default="grid", help="Mesh type.")
149
+ parser.add_argument("--mesh", choices=("grid", "tet", "hex", "nano", "anano"), default="grid", help="Mesh type.")
150
150
  parser.add_argument(
151
151
  "--headless",
152
152
  action="store_true",
@@ -171,4 +171,4 @@ if __name__ == "__main__":
171
171
  example.render()
172
172
 
173
173
  if not args.headless:
174
- example.renderer.plot()
174
+ example.renderer.plot(backend="matplotlib")
@@ -150,7 +150,7 @@ class Example:
150
150
  x = wp.zeros_like(rhs)
151
151
  fem_example_utils.bsr_cg(lhs, b=rhs, x=x, tol=1.0e-8, quiet=False)
152
152
 
153
- # make sure result is exactly zero outisde of circle
153
+ # make sure result is exactly zero outside of circle
154
154
  wp.sparse.bsr_mv(dirichlet_bd_proj, x=x, y=x, alpha=-1.0, beta=1.0)
155
155
  wp.utils.array_cast(in_array=x, out_array=self.A_field.dof_values)
156
156
 
@@ -255,6 +255,7 @@ class Example:
255
255
  self.renderer.render_line_list("streamlines", vertices, indices)
256
256
  self.renderer.render_line_list("streamlines", vertices, indices, colors)
257
257
 
258
+ self.renderer.paused = True
258
259
  self.renderer.end_frame()
259
260
 
260
261
  def _generate_incompressible_flow(self):
@@ -177,7 +177,9 @@ def gen_volume(res, bounds_lo: Optional[wp.vec3] = None, bounds_hi: Optional[wp.
177
177
 
178
178
  ijk = np.transpose(np.meshgrid(x, y, z), axes=(1, 2, 3, 0)).reshape(-1, 3)
179
179
  ijk = wp.array(ijk, dtype=wp.vec3i, device=device)
180
- return wp.Volume.allocate_by_voxels(ijk, voxel_size=voxel_size, translation=0.5 * voxel_size, device=device)
180
+ return wp.Volume.allocate_by_voxels(
181
+ ijk, voxel_size=voxel_size, translation=bounds_lo + 0.5 * voxel_size, device=device
182
+ )
181
183
 
182
184
 
183
185
  #
@@ -579,6 +581,19 @@ class Plot:
579
581
 
580
582
  animate = False
581
583
 
584
+ ref_geom = options.get("ref_geom", None)
585
+ if ref_geom is not None:
586
+ if isinstance(ref_geom, tuple):
587
+ vertices, counts, indices = ref_geom
588
+ offsets = np.cumsum(counts)
589
+ ranges = np.array([offsets - counts, offsets]).T
590
+ faces = np.concatenate(
591
+ [[count] + list(indices[beg:end]) for (count, (beg, end)) in zip(counts, ranges)]
592
+ )
593
+ ref_geom = pyvista.PolyData(vertices, faces)
594
+ else:
595
+ ref_geom = pyvista.PolyData(ref_geom)
596
+
582
597
  for name, (field, values) in self._fields.items():
583
598
  cells, types = field.space.vtk_cells()
584
599
  node_pos = field.space.node_positions().numpy()
@@ -667,7 +682,7 @@ class Plot:
667
682
  density = field_args["streamlines"].get("density", 1.0)
668
683
  cell_size = 1.0 / np.sqrt(field.space.geometry.cell_count())
669
684
  lines = grid.streamlines(vectors=name, n_points=int(100 * density))
670
- marker = lines.tube(radius=0.0025 * grid_scale / density)
685
+ marker = lines.tube(radius=0.0025 * grid_scale / np.sqrt(density))
671
686
  elif "displacement" in field_args:
672
687
  grid.points = field.space.node_positions().numpy() + v
673
688
 
@@ -700,11 +715,15 @@ class Plot:
700
715
  plotter.add_mesh(grid, opacity=0.25, clim=value_range)
701
716
  plotter.view_xy()
702
717
  else:
703
- plotter.add_mesh(marker, opacity=0.25)
718
+ plotter.add_mesh(marker)
704
719
  elif field.space.dimension == 3:
705
- plotter.add_mesh_clip_plane(grid, show_edges=True, clim=value_range)
720
+ plotter.add_mesh_clip_plane(grid, show_edges=True, clim=value_range, assign_to_axis="z")
706
721
  else:
707
722
  plotter.add_mesh(grid, show_edges=True, clim=value_range)
723
+
724
+ if ref_geom:
725
+ plotter.add_mesh(ref_geom)
726
+
708
727
  plotter.show(interactive_update=animate)
709
728
 
710
729
  frame = 0
@@ -26,9 +26,33 @@ import warp.sim
26
26
  import warp.sim.render
27
27
 
28
28
 
29
+ def color_lattice_grid(num_x, num_y):
30
+ colors = []
31
+ for _i in range(4):
32
+ colors.append([])
33
+
34
+ for xi in range(num_x + 1):
35
+ for yi in range(num_y + 1):
36
+ vId = xi * (num_y + 1) + yi
37
+
38
+ a = 1 if xi % 2 else 0
39
+ b = 1 if yi % 2 else 0
40
+
41
+ c = a * 2 + b
42
+
43
+ colors[c].append(vId)
44
+
45
+ colors_wp = []
46
+ for i_color in range(len(colors)):
47
+ colors_wp.append(wp.array(colors[i_color], dtype=wp.int32))
48
+
49
+ return colors_wp
50
+
51
+
29
52
  class IntegratorType(Enum):
30
53
  EULER = "euler"
31
54
  XPBD = "xpbd"
55
+ VBD = "vbd"
32
56
 
33
57
  def __str__(self):
34
58
  return self.value
@@ -67,7 +91,7 @@ class Example:
67
91
  tri_ka=1.0e3,
68
92
  tri_kd=1.0e1,
69
93
  )
70
- else:
94
+ elif self.integrator_type == IntegratorType.XPBD:
71
95
  builder.add_cloth_grid(
72
96
  pos=wp.vec3(0.0, 4.0, 0.0),
73
97
  rot=wp.quat_from_axis_angle(wp.vec3(1.0, 0.0, 0.0), math.pi * 0.5),
@@ -83,6 +107,22 @@ class Example:
83
107
  spring_ke=1.0e3,
84
108
  spring_kd=0.0,
85
109
  )
110
+ else:
111
+ # VBD
112
+ builder.add_cloth_grid(
113
+ pos=wp.vec3(0.0, 4.0, 0.0),
114
+ rot=wp.quat_from_axis_angle(wp.vec3(1.0, 0.0, 0.0), math.pi * 0.5),
115
+ vel=wp.vec3(0.0, 0.0, 0.0),
116
+ dim_x=self.sim_width,
117
+ dim_y=self.sim_height,
118
+ cell_x=0.1,
119
+ cell_y=0.1,
120
+ mass=0.1,
121
+ fix_left=True,
122
+ tri_ke=1e4,
123
+ tri_ka=1e4,
124
+ tri_kd=1e-5,
125
+ )
86
126
 
87
127
  usd_stage = Usd.Stage.Open(os.path.join(warp.examples.get_asset_directory(), "bunny.usd"))
88
128
  usd_geom = UsdGeom.Mesh(usd_stage.GetPrimAtPath("/root/bunny"))
@@ -103,16 +143,20 @@ class Example:
103
143
  kf=1.0e1,
104
144
  )
105
145
 
106
- if self.integrator_type == IntegratorType.EULER:
107
- self.integrator = wp.sim.SemiImplicitIntegrator()
108
- else:
109
- self.integrator = wp.sim.XPBDIntegrator(iterations=1)
110
-
111
146
  self.model = builder.finalize()
112
147
  self.model.ground = True
113
148
  self.model.soft_contact_ke = 1.0e4
114
149
  self.model.soft_contact_kd = 1.0e2
115
150
 
151
+ if self.integrator_type == IntegratorType.EULER:
152
+ self.integrator = wp.sim.SemiImplicitIntegrator()
153
+ elif self.integrator_type == IntegratorType.XPBD:
154
+ self.integrator = wp.sim.XPBDIntegrator(iterations=1)
155
+ else:
156
+ self.integrator = wp.sim.VBDIntegrator(self.model, iterations=1)
157
+ # we need to give VBD coloring information
158
+ self.model.particle_coloring = color_lattice_grid(width, height)
159
+
116
160
  self.state_0 = self.model.state()
117
161
  self.state_1 = self.model.state()
118
162
 
warp/fem/__init__.py CHANGED
@@ -1,3 +1,4 @@
1
+ from .adaptivity import adaptive_nanogrid_from_field, adaptive_nanogrid_from_hierarchy
1
2
  from .cache import TemporaryStore, borrow_temporary, borrow_temporary_like, set_default_temporary_store
2
3
  from .dirichlet import normalize_dirichlet_projector, project_linear_system
3
4
  from .domain import BoundarySides, Cells, FrontierSides, GeometryDomain, Sides, Subdomain
@@ -13,6 +14,7 @@ from .field import (
13
14
  make_trial,
14
15
  )
15
16
  from .geometry import (
17
+ AdaptiveNanogrid,
16
18
  ExplicitGeometryPartition,
17
19
  Geometry,
18
20
  GeometryPartition,