warp-lang 1.2.2__py3-none-manylinux2014_aarch64.whl → 1.3.0__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.
- warp/__init__.py +8 -6
- warp/autograd.py +823 -0
- warp/bin/warp.so +0 -0
- warp/build.py +6 -2
- warp/builtins.py +1410 -886
- warp/codegen.py +503 -166
- warp/config.py +48 -18
- warp/context.py +400 -198
- warp/dlpack.py +8 -0
- warp/examples/assets/bunny.usd +0 -0
- warp/examples/benchmarks/benchmark_cloth_warp.py +1 -1
- warp/examples/benchmarks/benchmark_interop_torch.py +158 -0
- warp/examples/benchmarks/benchmark_launches.py +1 -1
- warp/examples/core/example_cupy.py +78 -0
- warp/examples/fem/example_apic_fluid.py +17 -36
- warp/examples/fem/example_burgers.py +9 -18
- warp/examples/fem/example_convection_diffusion.py +7 -17
- warp/examples/fem/example_convection_diffusion_dg.py +27 -47
- warp/examples/fem/example_deformed_geometry.py +11 -22
- warp/examples/fem/example_diffusion.py +7 -18
- warp/examples/fem/example_diffusion_3d.py +24 -28
- warp/examples/fem/example_diffusion_mgpu.py +7 -14
- warp/examples/fem/example_magnetostatics.py +190 -0
- warp/examples/fem/example_mixed_elasticity.py +111 -80
- warp/examples/fem/example_navier_stokes.py +30 -34
- warp/examples/fem/example_nonconforming_contact.py +290 -0
- warp/examples/fem/example_stokes.py +17 -32
- warp/examples/fem/example_stokes_transfer.py +12 -21
- warp/examples/fem/example_streamlines.py +350 -0
- warp/examples/fem/utils.py +936 -0
- warp/fabric.py +5 -2
- warp/fem/__init__.py +13 -3
- warp/fem/cache.py +161 -11
- warp/fem/dirichlet.py +37 -28
- warp/fem/domain.py +105 -14
- warp/fem/field/__init__.py +14 -3
- warp/fem/field/field.py +454 -11
- warp/fem/field/nodal_field.py +33 -18
- warp/fem/geometry/deformed_geometry.py +50 -15
- warp/fem/geometry/hexmesh.py +12 -24
- warp/fem/geometry/nanogrid.py +106 -31
- warp/fem/geometry/quadmesh_2d.py +6 -11
- warp/fem/geometry/tetmesh.py +103 -61
- warp/fem/geometry/trimesh_2d.py +98 -47
- warp/fem/integrate.py +231 -186
- warp/fem/operator.py +14 -9
- warp/fem/quadrature/pic_quadrature.py +35 -9
- warp/fem/quadrature/quadrature.py +119 -32
- warp/fem/space/basis_space.py +98 -22
- warp/fem/space/collocated_function_space.py +3 -1
- warp/fem/space/function_space.py +7 -2
- warp/fem/space/grid_2d_function_space.py +3 -3
- warp/fem/space/grid_3d_function_space.py +4 -4
- warp/fem/space/hexmesh_function_space.py +3 -2
- warp/fem/space/nanogrid_function_space.py +12 -14
- warp/fem/space/partition.py +45 -47
- warp/fem/space/restriction.py +19 -16
- warp/fem/space/shape/cube_shape_function.py +91 -3
- warp/fem/space/shape/shape_function.py +7 -0
- warp/fem/space/shape/square_shape_function.py +32 -0
- warp/fem/space/shape/tet_shape_function.py +11 -7
- warp/fem/space/shape/triangle_shape_function.py +10 -1
- warp/fem/space/topology.py +116 -42
- warp/fem/types.py +8 -1
- warp/fem/utils.py +301 -83
- warp/native/array.h +16 -0
- warp/native/builtin.h +0 -15
- warp/native/cuda_util.cpp +14 -6
- warp/native/exports.h +1348 -1308
- warp/native/quat.h +79 -0
- warp/native/rand.h +27 -4
- warp/native/sparse.cpp +83 -81
- warp/native/sparse.cu +381 -453
- warp/native/vec.h +64 -0
- warp/native/volume.cpp +40 -49
- warp/native/volume_builder.cu +2 -3
- warp/native/volume_builder.h +12 -17
- warp/native/warp.cu +3 -3
- warp/native/warp.h +69 -59
- warp/render/render_opengl.py +17 -9
- warp/sim/articulation.py +117 -17
- warp/sim/collide.py +35 -29
- warp/sim/model.py +123 -18
- warp/sim/render.py +3 -1
- warp/sparse.py +867 -203
- warp/stubs.py +312 -541
- warp/tape.py +29 -1
- warp/tests/disabled_kinematics.py +1 -1
- warp/tests/test_adam.py +1 -1
- warp/tests/test_arithmetic.py +1 -1
- warp/tests/test_array.py +58 -1
- warp/tests/test_array_reduce.py +1 -1
- warp/tests/test_async.py +1 -1
- warp/tests/test_atomic.py +1 -1
- warp/tests/test_bool.py +1 -1
- warp/tests/test_builtins_resolution.py +1 -1
- warp/tests/test_bvh.py +6 -1
- warp/tests/test_closest_point_edge_edge.py +1 -1
- warp/tests/test_codegen.py +66 -1
- warp/tests/test_compile_consts.py +1 -1
- warp/tests/test_conditional.py +1 -1
- warp/tests/test_copy.py +1 -1
- warp/tests/test_ctypes.py +1 -1
- warp/tests/test_dense.py +1 -1
- warp/tests/test_devices.py +1 -1
- warp/tests/test_dlpack.py +1 -1
- warp/tests/test_examples.py +33 -4
- warp/tests/test_fabricarray.py +5 -2
- warp/tests/test_fast_math.py +1 -1
- warp/tests/test_fem.py +213 -6
- warp/tests/test_fp16.py +1 -1
- warp/tests/test_func.py +1 -1
- warp/tests/test_future_annotations.py +90 -0
- warp/tests/test_generics.py +1 -1
- warp/tests/test_grad.py +1 -1
- warp/tests/test_grad_customs.py +1 -1
- warp/tests/test_grad_debug.py +247 -0
- warp/tests/test_hash_grid.py +6 -1
- warp/tests/test_implicit_init.py +354 -0
- warp/tests/test_import.py +1 -1
- warp/tests/test_indexedarray.py +1 -1
- warp/tests/test_intersect.py +1 -1
- warp/tests/test_jax.py +1 -1
- warp/tests/test_large.py +1 -1
- warp/tests/test_launch.py +1 -1
- warp/tests/test_lerp.py +1 -1
- warp/tests/test_linear_solvers.py +1 -1
- warp/tests/test_lvalue.py +1 -1
- warp/tests/test_marching_cubes.py +5 -2
- warp/tests/test_mat.py +34 -35
- warp/tests/test_mat_lite.py +2 -1
- warp/tests/test_mat_scalar_ops.py +1 -1
- warp/tests/test_math.py +1 -1
- warp/tests/test_matmul.py +20 -16
- warp/tests/test_matmul_lite.py +1 -1
- warp/tests/test_mempool.py +1 -1
- warp/tests/test_mesh.py +5 -2
- warp/tests/test_mesh_query_aabb.py +1 -1
- warp/tests/test_mesh_query_point.py +1 -1
- warp/tests/test_mesh_query_ray.py +1 -1
- warp/tests/test_mlp.py +1 -1
- warp/tests/test_model.py +1 -1
- warp/tests/test_module_hashing.py +77 -1
- warp/tests/test_modules_lite.py +1 -1
- warp/tests/test_multigpu.py +1 -1
- warp/tests/test_noise.py +1 -1
- warp/tests/test_operators.py +1 -1
- warp/tests/test_options.py +1 -1
- warp/tests/test_overwrite.py +542 -0
- warp/tests/test_peer.py +1 -1
- warp/tests/test_pinned.py +1 -1
- warp/tests/test_print.py +1 -1
- warp/tests/test_quat.py +15 -1
- warp/tests/test_rand.py +1 -1
- warp/tests/test_reload.py +1 -1
- warp/tests/test_rounding.py +1 -1
- warp/tests/test_runlength_encode.py +1 -1
- warp/tests/test_scalar_ops.py +95 -0
- warp/tests/test_sim_grad.py +1 -1
- warp/tests/test_sim_kinematics.py +1 -1
- warp/tests/test_smoothstep.py +1 -1
- warp/tests/test_sparse.py +82 -15
- warp/tests/test_spatial.py +1 -1
- warp/tests/test_special_values.py +2 -11
- warp/tests/test_streams.py +11 -1
- warp/tests/test_struct.py +1 -1
- warp/tests/test_tape.py +1 -1
- warp/tests/test_torch.py +194 -1
- warp/tests/test_transient_module.py +1 -1
- warp/tests/test_types.py +1 -1
- warp/tests/test_utils.py +1 -1
- warp/tests/test_vec.py +15 -63
- warp/tests/test_vec_lite.py +2 -1
- warp/tests/test_vec_scalar_ops.py +65 -1
- warp/tests/test_verify_fp.py +1 -1
- warp/tests/test_volume.py +28 -2
- warp/tests/test_volume_write.py +1 -1
- warp/tests/unittest_serial.py +1 -1
- warp/tests/unittest_suites.py +9 -1
- warp/tests/walkthrough_debug.py +1 -1
- warp/thirdparty/unittest_parallel.py +2 -5
- warp/torch.py +103 -41
- warp/types.py +341 -224
- warp/utils.py +11 -2
- {warp_lang-1.2.2.dist-info → warp_lang-1.3.0.dist-info}/METADATA +99 -46
- warp_lang-1.3.0.dist-info/RECORD +368 -0
- warp/examples/fem/bsr_utils.py +0 -378
- warp/examples/fem/mesh_utils.py +0 -133
- warp/examples/fem/plot_utils.py +0 -292
- warp_lang-1.2.2.dist-info/RECORD +0 -359
- {warp_lang-1.2.2.dist-info → warp_lang-1.3.0.dist-info}/LICENSE.md +0 -0
- {warp_lang-1.2.2.dist-info → warp_lang-1.3.0.dist-info}/WHEEL +0 -0
- {warp_lang-1.2.2.dist-info → warp_lang-1.3.0.dist-info}/top_level.txt +0 -0
warp/fem/field/field.py
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
|
-
from typing import Any
|
|
1
|
+
from typing import Any, Dict, Optional
|
|
2
2
|
|
|
3
3
|
import warp as wp
|
|
4
|
+
from warp.fem import cache
|
|
5
|
+
from warp.fem.domain import GeometryDomain, Sides
|
|
4
6
|
from warp.fem.geometry import DeformedGeometry, Geometry
|
|
7
|
+
from warp.fem.operator import integrand
|
|
5
8
|
from warp.fem.space import FunctionSpace, SpacePartition
|
|
6
|
-
from warp.fem.types import Sample
|
|
9
|
+
from warp.fem.types import NULL_ELEMENT_INDEX, ElementKind, Sample
|
|
7
10
|
|
|
8
11
|
|
|
9
12
|
class FieldLike:
|
|
@@ -36,6 +39,14 @@ class FieldLike:
|
|
|
36
39
|
"""Value of arguments to be passed to device functions"""
|
|
37
40
|
raise NotImplementedError
|
|
38
41
|
|
|
42
|
+
def gradient_valid(self) -> bool:
|
|
43
|
+
"""Whether the gradient operator is implemented for this field."""
|
|
44
|
+
return False
|
|
45
|
+
|
|
46
|
+
def divergence_valid(self) -> bool:
|
|
47
|
+
"""Whether the divergence operator is implemented for this field."""
|
|
48
|
+
return False
|
|
49
|
+
|
|
39
50
|
@staticmethod
|
|
40
51
|
def eval_inner(args: "ElementEvalArg", s: "Sample"): # noqa: F821
|
|
41
52
|
"""Device function evaluating the inner field value at a sample point"""
|
|
@@ -66,14 +77,64 @@ class FieldLike:
|
|
|
66
77
|
"""Device function evaluating the outer field divergence at a sample point"""
|
|
67
78
|
raise NotImplementedError
|
|
68
79
|
|
|
80
|
+
@staticmethod
|
|
81
|
+
def eval_degree(args: "ElementEvalArg"): # noqa: F821
|
|
82
|
+
"""Polynomial degree of the field is applicable, or hint for determination of interpolation order"""
|
|
83
|
+
raise NotImplementedError
|
|
84
|
+
|
|
69
85
|
|
|
70
|
-
class
|
|
86
|
+
class GeometryField(FieldLike):
|
|
87
|
+
"""Base class for fields defined over a geometry"""
|
|
88
|
+
|
|
89
|
+
@property
|
|
90
|
+
def geometry(self) -> Geometry:
|
|
91
|
+
"""Geometry over which the field is expressed"""
|
|
92
|
+
raise NotImplementedError
|
|
93
|
+
|
|
94
|
+
@property
|
|
95
|
+
def element_kind(self) -> ElementKind:
|
|
96
|
+
"""Kind of element over which the field is expressed"""
|
|
97
|
+
raise NotImplementedError
|
|
98
|
+
|
|
99
|
+
@staticmethod
|
|
100
|
+
def eval_reference_grad_inner(args: "ElementEvalArg", s: "Sample"): # noqa: F821
|
|
101
|
+
"""Device function evaluating the inner field gradient with respect to reference element coordinates at a sample point"""
|
|
102
|
+
raise NotImplementedError
|
|
103
|
+
|
|
104
|
+
@staticmethod
|
|
105
|
+
def eval_reference_grad_outer(args: "ElementEvalArg", s: "Sample"): # noqa: F821
|
|
106
|
+
"""Device function evaluating the outer field gradient with respect to reference element coordinates at a sample point"""
|
|
107
|
+
raise NotImplementedError
|
|
108
|
+
|
|
109
|
+
def trace(self) -> FieldLike:
|
|
110
|
+
"""Trace of this field over lower-dimensional elements"""
|
|
111
|
+
raise NotImplementedError
|
|
112
|
+
|
|
113
|
+
def make_deformed_geometry(self, relative=True) -> Geometry:
|
|
114
|
+
"""Returns a deformed version of the underlying geometry, with positions displaced according to this field's values.
|
|
115
|
+
|
|
116
|
+
Args:
|
|
117
|
+
relative: If ``True``, the field is intepreted as a relative displacement over the original geometry. If ``False``, the field values are intepreted as absolute positions.
|
|
118
|
+
|
|
119
|
+
"""
|
|
120
|
+
return DeformedGeometry(self, relative=relative)
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
class SpaceField(GeometryField):
|
|
71
124
|
"""Base class for fields defined over a function space"""
|
|
72
125
|
|
|
73
126
|
def __init__(self, space: FunctionSpace, space_partition: SpacePartition):
|
|
74
127
|
self._space = space
|
|
75
128
|
self._space_partition = space_partition
|
|
76
129
|
|
|
130
|
+
@property
|
|
131
|
+
def geometry(self) -> Geometry:
|
|
132
|
+
return self._space.geometry
|
|
133
|
+
|
|
134
|
+
@property
|
|
135
|
+
def element_kind(self) -> ElementKind:
|
|
136
|
+
return self._space.element_kind
|
|
137
|
+
|
|
77
138
|
@property
|
|
78
139
|
def space(self) -> FunctionSpace:
|
|
79
140
|
return self._space
|
|
@@ -108,7 +169,6 @@ class SpaceField(FieldLike):
|
|
|
108
169
|
|
|
109
170
|
def _make_eval_degree(self):
|
|
110
171
|
ORDER = self.space.ORDER
|
|
111
|
-
from warp.fem import cache
|
|
112
172
|
|
|
113
173
|
@cache.dynamic_func(suffix=self.name)
|
|
114
174
|
def degree(args: self.ElementEvalArg):
|
|
@@ -130,10 +190,6 @@ class DiscreteField(SpaceField):
|
|
|
130
190
|
"""Sets degrees of freedom values from an array"""
|
|
131
191
|
raise NotImplementedError
|
|
132
192
|
|
|
133
|
-
def trace(self) -> "DiscreteField":
|
|
134
|
-
"""Trace of this field over a lower-dimensional function space"""
|
|
135
|
-
raise NotImplementedError
|
|
136
|
-
|
|
137
193
|
@staticmethod
|
|
138
194
|
def set_node_value(args: "FieldLike.EvalArg", node_index: int, value: Any):
|
|
139
195
|
"""Device function setting the value at given node"""
|
|
@@ -143,6 +199,393 @@ class DiscreteField(SpaceField):
|
|
|
143
199
|
def name(self) -> str:
|
|
144
200
|
return f"{self.__class__.__qualname__}_{self.space.name}_{self.space_partition.name}"
|
|
145
201
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
202
|
+
|
|
203
|
+
class ImplicitField(GeometryField):
|
|
204
|
+
"""Field defined from an arbitrary function over a domain.
|
|
205
|
+
Does not support autodiff yet, so if gradient/divergence evaluation is required corresponding functions must be provided.
|
|
206
|
+
|
|
207
|
+
Args:
|
|
208
|
+
domain: Domain over which the field is defined
|
|
209
|
+
func: Warp function evaluating the field at a given position. Must accept at least one argument, with the first argument being the evaluation position (``wp.vec2`` or ``wp.vec3``).
|
|
210
|
+
values: Optional dictionary of additional argument values to be passed to the evaluation function.
|
|
211
|
+
grad_func: Optional gradient evaluation function; must take same arguments as `func`
|
|
212
|
+
div_func: Optional divergence evaluation function; must take same arguments as `func`
|
|
213
|
+
degree: Optional hint for automatic determination of quadrature orders when integrating this field
|
|
214
|
+
"""
|
|
215
|
+
|
|
216
|
+
def __init__(
|
|
217
|
+
self,
|
|
218
|
+
domain: GeometryDomain,
|
|
219
|
+
func: wp.Function,
|
|
220
|
+
values: Optional[Dict[str, Any]] = None,
|
|
221
|
+
grad_func: Optional[wp.Function] = None,
|
|
222
|
+
div_func: Optional[wp.Function] = None,
|
|
223
|
+
degree=0,
|
|
224
|
+
):
|
|
225
|
+
self.domain = domain
|
|
226
|
+
self._degree = degree
|
|
227
|
+
|
|
228
|
+
if not isinstance(func, wp.Function):
|
|
229
|
+
raise ValueError("Implicit field function must be a warp Function (decorated with `wp.func`)")
|
|
230
|
+
|
|
231
|
+
self._func = func
|
|
232
|
+
self._grad_func = grad_func
|
|
233
|
+
self._div_func = div_func
|
|
234
|
+
|
|
235
|
+
argspec = integrand(func.func).argspec
|
|
236
|
+
arg_types = argspec.annotations
|
|
237
|
+
|
|
238
|
+
pos_arg_type = arg_types.pop(argspec.args[0]) if arg_types else None
|
|
239
|
+
if not pos_arg_type or not wp.types.types_equal(
|
|
240
|
+
pos_arg_type, wp.vec(length=domain.geometry.dimension, dtype=float), match_generic=True
|
|
241
|
+
):
|
|
242
|
+
raise ValueError(
|
|
243
|
+
f"Implicit field function '{func.func.__name__}' must accept a position as its first argument"
|
|
244
|
+
)
|
|
245
|
+
|
|
246
|
+
self.EvalArg = cache.get_argument_struct(arg_types)
|
|
247
|
+
self.values = values
|
|
248
|
+
|
|
249
|
+
self.ElementEvalArg = self._make_element_eval_arg()
|
|
250
|
+
self.eval_degree = self._make_eval_degree()
|
|
251
|
+
|
|
252
|
+
self.eval_inner = self._make_eval_func(func)
|
|
253
|
+
self.eval_grad_inner = self._make_eval_func(grad_func)
|
|
254
|
+
self.eval_div_inner = self._make_eval_func(div_func)
|
|
255
|
+
self.eval_reference_grad_inner = self._make_eval_reference_grad()
|
|
256
|
+
|
|
257
|
+
self.eval_outer = self.eval_inner
|
|
258
|
+
self.eval_grad_outer = self.eval_grad_inner
|
|
259
|
+
self.eval_div_outer = self.eval_div_inner
|
|
260
|
+
self.eval_reference_grad_outer = self.eval_reference_grad_inner
|
|
261
|
+
|
|
262
|
+
@property
|
|
263
|
+
def values(self):
|
|
264
|
+
return self._func_arg
|
|
265
|
+
|
|
266
|
+
@values.setter
|
|
267
|
+
def values(self, v):
|
|
268
|
+
self._func_arg = cache.populate_argument_struct(self.EvalArg, v, self._func.func.__name__)
|
|
269
|
+
|
|
270
|
+
@property
|
|
271
|
+
def geometry(self) -> Geometry:
|
|
272
|
+
return self.domain.geometry
|
|
273
|
+
|
|
274
|
+
@property
|
|
275
|
+
def element_kind(self) -> ElementKind:
|
|
276
|
+
return self.domain.element_kind
|
|
277
|
+
|
|
278
|
+
def eval_arg_value(self, device):
|
|
279
|
+
return self._func_arg
|
|
280
|
+
|
|
281
|
+
@property
|
|
282
|
+
def degree(self) -> int:
|
|
283
|
+
return self._degree
|
|
284
|
+
|
|
285
|
+
@property
|
|
286
|
+
def name(self) -> str:
|
|
287
|
+
return f"Implicit_{self.domain.name}_{self.degree}_{self.EvalArg.key}"
|
|
288
|
+
|
|
289
|
+
def _make_eval_func(self, func):
|
|
290
|
+
if func is None:
|
|
291
|
+
return None
|
|
292
|
+
|
|
293
|
+
@cache.dynamic_func(
|
|
294
|
+
suffix=f"{self.name}_{func.key}",
|
|
295
|
+
code_transformers=[cache.ExpandStarredArgumentStruct({"args.eval_arg": self.EvalArg})],
|
|
296
|
+
)
|
|
297
|
+
def eval_inner(args: self.ElementEvalArg, s: Sample):
|
|
298
|
+
pos = self.domain.element_position(args.elt_arg, s)
|
|
299
|
+
return func(pos, *args.eval_arg)
|
|
300
|
+
|
|
301
|
+
return eval_inner
|
|
302
|
+
|
|
303
|
+
def _make_eval_reference_grad(self):
|
|
304
|
+
if self.eval_grad_inner is None:
|
|
305
|
+
return None
|
|
306
|
+
|
|
307
|
+
@cache.dynamic_func(suffix=f"{self.eval_grad_inner.key}")
|
|
308
|
+
def eval_reference_grad_inner(args: self.ElementEvalArg, s: Sample):
|
|
309
|
+
return self.eval_grad_inner(args, s) * self.domain.element_deformation_gradient(args.elt_arg, s)
|
|
310
|
+
|
|
311
|
+
return eval_reference_grad_inner
|
|
312
|
+
|
|
313
|
+
def _make_element_eval_arg(self):
|
|
314
|
+
@cache.dynamic_struct(suffix=self.name)
|
|
315
|
+
class ElementEvalArg:
|
|
316
|
+
elt_arg: self.domain.ElementArg
|
|
317
|
+
eval_arg: self.EvalArg
|
|
318
|
+
|
|
319
|
+
return ElementEvalArg
|
|
320
|
+
|
|
321
|
+
def _make_eval_degree(self):
|
|
322
|
+
ORDER = wp.constant(self._degree)
|
|
323
|
+
|
|
324
|
+
@cache.dynamic_func(suffix=self.name)
|
|
325
|
+
def degree(args: self.ElementEvalArg):
|
|
326
|
+
return ORDER
|
|
327
|
+
|
|
328
|
+
return degree
|
|
329
|
+
|
|
330
|
+
def trace(self):
|
|
331
|
+
if self.element_kind == ElementKind.SIDE:
|
|
332
|
+
raise RuntimeError("Trace only available for field defined on cell elements")
|
|
333
|
+
|
|
334
|
+
return ImplicitField(
|
|
335
|
+
domain=Sides(self.domain.geometry_partition),
|
|
336
|
+
func=self._func,
|
|
337
|
+
values={name: getattr(self.values, name) for name in self.EvalArg.vars},
|
|
338
|
+
grad_func=self._grad_func,
|
|
339
|
+
div_func=self._div_func,
|
|
340
|
+
degree=self._degree,
|
|
341
|
+
)
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
class UniformField(GeometryField):
|
|
345
|
+
"""Field defined as a constant value over a domain.
|
|
346
|
+
|
|
347
|
+
Args:
|
|
348
|
+
domain: Domain over which the field is defined
|
|
349
|
+
value: Uniform value over the domain
|
|
350
|
+
"""
|
|
351
|
+
|
|
352
|
+
def __init__(self, domain: GeometryDomain, value: Any):
|
|
353
|
+
self.domain = domain
|
|
354
|
+
|
|
355
|
+
if not wp.types.is_value(value):
|
|
356
|
+
raise ValueError("value must be a Warp scalar, vector or matrix")
|
|
357
|
+
|
|
358
|
+
self.dtype = wp.types.type_to_warp(type(value))
|
|
359
|
+
self._value = self.dtype(value)
|
|
360
|
+
|
|
361
|
+
scalar_type = wp.types.type_scalar_type(self.dtype)
|
|
362
|
+
if wp.types.type_is_vector(self.dtype):
|
|
363
|
+
grad_type = wp.mat(shape=(wp.types.type_length(self.dtype), self.geometry.dimension), dtype=scalar_type)
|
|
364
|
+
div_type = scalar_type
|
|
365
|
+
elif wp.types.type_is_matrix(self.dtype):
|
|
366
|
+
grad_type = None
|
|
367
|
+
div_type = wp.vec(length=(wp.types.type_length(self.dtype) // self.geometry.dimension), dtype=scalar_type)
|
|
368
|
+
else:
|
|
369
|
+
div_type = None
|
|
370
|
+
grad_type = wp.vec(length=self.geometry.dimension, dtype=scalar_type)
|
|
371
|
+
|
|
372
|
+
self.EvalArg = self._make_eval_arg()
|
|
373
|
+
self.ElementEvalArg = self._make_element_eval_arg()
|
|
374
|
+
self.eval_degree = self._make_eval_degree()
|
|
375
|
+
|
|
376
|
+
self.eval_inner = self._make_eval_inner()
|
|
377
|
+
self.eval_grad_inner = self._make_eval_zero(grad_type)
|
|
378
|
+
self.eval_div_inner = self._make_eval_zero(div_type)
|
|
379
|
+
self.eval_reference_grad_inner = self.eval_grad_inner
|
|
380
|
+
|
|
381
|
+
self.eval_outer = self.eval_inner
|
|
382
|
+
self.eval_grad_outer = self.eval_grad_inner
|
|
383
|
+
self.eval_div_outer = self.eval_div_inner
|
|
384
|
+
self.eval_reference_grad_outer = self.eval_reference_grad_inner
|
|
385
|
+
|
|
386
|
+
@property
|
|
387
|
+
def value(self):
|
|
388
|
+
return self._value
|
|
389
|
+
|
|
390
|
+
@value.setter
|
|
391
|
+
def value(self, v):
|
|
392
|
+
value_type = wp.types.type_to_warp(type(v))
|
|
393
|
+
assert wp.types.types_equal(value_type, self.dtype)
|
|
394
|
+
self._value = self.dtype(v)
|
|
395
|
+
|
|
396
|
+
@property
|
|
397
|
+
def geometry(self) -> Geometry:
|
|
398
|
+
return self.domain.geometry
|
|
399
|
+
|
|
400
|
+
@property
|
|
401
|
+
def element_kind(self) -> ElementKind:
|
|
402
|
+
return self.domain.element_kind
|
|
403
|
+
|
|
404
|
+
def eval_arg_value(self, device):
|
|
405
|
+
arg = self.EvalArg()
|
|
406
|
+
arg.value = self.value
|
|
407
|
+
return arg
|
|
408
|
+
|
|
409
|
+
@property
|
|
410
|
+
def degree(self) -> int:
|
|
411
|
+
return 0
|
|
412
|
+
|
|
413
|
+
@property
|
|
414
|
+
def name(self) -> str:
|
|
415
|
+
return f"Uniform{self.domain.name}_{wp.types.get_type_code(self.dtype)}"
|
|
416
|
+
|
|
417
|
+
def _make_eval_inner(self):
|
|
418
|
+
@cache.dynamic_func(suffix=self.name)
|
|
419
|
+
def eval_inner(args: self.ElementEvalArg, s: Sample):
|
|
420
|
+
return args.eval_arg.value
|
|
421
|
+
|
|
422
|
+
return eval_inner
|
|
423
|
+
|
|
424
|
+
def _make_eval_zero(self, dtype):
|
|
425
|
+
if dtype is None:
|
|
426
|
+
return None
|
|
427
|
+
|
|
428
|
+
scalar_type = wp.types.type_scalar_type(dtype)
|
|
429
|
+
|
|
430
|
+
@cache.dynamic_func(suffix=f"{self.name}_{wp.types.get_type_code(dtype)}")
|
|
431
|
+
def eval_zero(args: self.ElementEvalArg, s: Sample):
|
|
432
|
+
return dtype(scalar_type(0.0))
|
|
433
|
+
|
|
434
|
+
return eval_zero
|
|
435
|
+
|
|
436
|
+
def _make_eval_arg(self):
|
|
437
|
+
@cache.dynamic_struct(suffix=self.name)
|
|
438
|
+
class EvalArg:
|
|
439
|
+
value: self.dtype
|
|
440
|
+
|
|
441
|
+
return EvalArg
|
|
442
|
+
|
|
443
|
+
def _make_element_eval_arg(self):
|
|
444
|
+
@cache.dynamic_struct(suffix=self.name)
|
|
445
|
+
class ElementEvalArg:
|
|
446
|
+
elt_arg: self.domain.ElementArg
|
|
447
|
+
eval_arg: self.EvalArg
|
|
448
|
+
|
|
449
|
+
return ElementEvalArg
|
|
450
|
+
|
|
451
|
+
def _make_eval_degree(self):
|
|
452
|
+
@cache.dynamic_func(suffix=self.name)
|
|
453
|
+
def degree(args: self.ElementEvalArg):
|
|
454
|
+
return 0
|
|
455
|
+
|
|
456
|
+
return degree
|
|
457
|
+
|
|
458
|
+
def trace(self):
|
|
459
|
+
if self.element_kind == ElementKind.SIDE:
|
|
460
|
+
raise RuntimeError("Trace only available for field defined on cell elements")
|
|
461
|
+
|
|
462
|
+
return UniformField(domain=Sides(self.domain.geometry_partition), value=self.value)
|
|
463
|
+
|
|
464
|
+
|
|
465
|
+
class NonconformingField(GeometryField):
|
|
466
|
+
"""Field defined as the map of a DiscreteField over a non-conforming geometry.
|
|
467
|
+
|
|
468
|
+
Args:
|
|
469
|
+
domain: The new domain over which the nonconforming field will be evaluated
|
|
470
|
+
field: Nonconforming discrete field
|
|
471
|
+
background: Uniform value or domain-conforming field determining the value outside of the geometry of definition of `field`
|
|
472
|
+
"""
|
|
473
|
+
|
|
474
|
+
_LOOKUP_EPS = wp.constant(1.0e-6)
|
|
475
|
+
|
|
476
|
+
def __init__(self, domain: GeometryDomain, field: DiscreteField, background: Any = 0.0):
|
|
477
|
+
self.domain = domain
|
|
478
|
+
|
|
479
|
+
self.field = field
|
|
480
|
+
self.dtype = field.dtype
|
|
481
|
+
|
|
482
|
+
if not isinstance(background, GeometryField):
|
|
483
|
+
background = UniformField(domain, self.dtype(background))
|
|
484
|
+
elif background.geometry != domain.geometry or background.element_kind != domain.element_kind:
|
|
485
|
+
raise ValueError("Background field must be conforming to the domain")
|
|
486
|
+
self.background = background
|
|
487
|
+
|
|
488
|
+
self.EvalArg = self._make_eval_arg()
|
|
489
|
+
self.ElementEvalArg = self._make_element_eval_arg()
|
|
490
|
+
self.eval_degree = self._make_eval_degree()
|
|
491
|
+
|
|
492
|
+
self.eval_inner = self._make_nonconforming_eval("eval_inner")
|
|
493
|
+
self.eval_grad_inner = self._make_nonconforming_eval("eval_grad_inner")
|
|
494
|
+
self.eval_div_inner = self._make_nonconforming_eval("eval_div_inner")
|
|
495
|
+
self.eval_reference_grad_inner = self._make_eval_reference_grad()
|
|
496
|
+
|
|
497
|
+
# Nonconforming evaluation is position based, does not handle discontinuous fields
|
|
498
|
+
self.eval_outer = self.eval_inner
|
|
499
|
+
self.eval_grad_outer = self.eval_grad_inner
|
|
500
|
+
self.eval_div_outer = self.eval_div_inner
|
|
501
|
+
self.eval_reference_grad_outer = self.eval_reference_grad_inner
|
|
502
|
+
|
|
503
|
+
@property
|
|
504
|
+
def geometry(self) -> Geometry:
|
|
505
|
+
return self.domain.geometry
|
|
506
|
+
|
|
507
|
+
@property
|
|
508
|
+
def element_kind(self) -> ElementKind:
|
|
509
|
+
return self.domain.element_kind
|
|
510
|
+
|
|
511
|
+
@cache.cached_arg_value
|
|
512
|
+
def eval_arg_value(self, device):
|
|
513
|
+
arg = self.EvalArg()
|
|
514
|
+
arg.field_cell_eval_arg = self.field.ElementEvalArg()
|
|
515
|
+
arg.field_cell_eval_arg.elt_arg = self.field.geometry.cell_arg_value(device)
|
|
516
|
+
arg.field_cell_eval_arg.eval_arg = self.field.eval_arg_value(device)
|
|
517
|
+
arg.background_arg = self.background.eval_arg_value(device)
|
|
518
|
+
return arg
|
|
519
|
+
|
|
520
|
+
@property
|
|
521
|
+
def degree(self) -> int:
|
|
522
|
+
return self.field.degree
|
|
523
|
+
|
|
524
|
+
@property
|
|
525
|
+
def name(self) -> str:
|
|
526
|
+
return f"{self.domain.name}_{self.field.name}_{self.background.name}"
|
|
527
|
+
|
|
528
|
+
def _make_nonconforming_eval(self, eval_func_name):
|
|
529
|
+
field_eval = getattr(self.field, eval_func_name)
|
|
530
|
+
bg_eval = getattr(self.background, eval_func_name)
|
|
531
|
+
|
|
532
|
+
if field_eval is None or bg_eval is None:
|
|
533
|
+
return None
|
|
534
|
+
|
|
535
|
+
@cache.dynamic_func(suffix=f"{eval_func_name}_{self.name}")
|
|
536
|
+
def eval_nc(args: self.ElementEvalArg, s: Sample):
|
|
537
|
+
pos = self.domain.element_position(args.elt_arg, s)
|
|
538
|
+
cell_arg = args.eval_arg.field_cell_eval_arg.elt_arg
|
|
539
|
+
nonconforming_s = self.field.geometry.cell_lookup(cell_arg, pos)
|
|
540
|
+
if (
|
|
541
|
+
nonconforming_s.element_index == NULL_ELEMENT_INDEX
|
|
542
|
+
or wp.length_sq(pos - self.field.geometry.cell_position(cell_arg, nonconforming_s))
|
|
543
|
+
> NonconformingField._LOOKUP_EPS
|
|
544
|
+
):
|
|
545
|
+
return bg_eval(self.background.ElementEvalArg(args.elt_arg, args.eval_arg.background_arg), s)
|
|
546
|
+
return field_eval(
|
|
547
|
+
self.field.ElementEvalArg(cell_arg, args.eval_arg.field_cell_eval_arg.eval_arg), nonconforming_s
|
|
548
|
+
)
|
|
549
|
+
|
|
550
|
+
return eval_nc
|
|
551
|
+
|
|
552
|
+
def _make_eval_reference_grad(self):
|
|
553
|
+
if self.eval_grad_inner is None:
|
|
554
|
+
return None
|
|
555
|
+
|
|
556
|
+
@cache.dynamic_func(suffix=f"{self.eval_grad_inner.key}")
|
|
557
|
+
def eval_reference_grad_inner(args: self.ElementEvalArg, s: Sample):
|
|
558
|
+
return self.eval_grad_inner(args, s) * self.domain.element_deformation_gradient(args.elt_arg, s)
|
|
559
|
+
|
|
560
|
+
return eval_reference_grad_inner
|
|
561
|
+
|
|
562
|
+
def _make_eval_arg(self):
|
|
563
|
+
@cache.dynamic_struct(suffix=self.name)
|
|
564
|
+
class EvalArg:
|
|
565
|
+
field_cell_eval_arg: self.field.ElementEvalArg
|
|
566
|
+
background_arg: self.background.EvalArg
|
|
567
|
+
|
|
568
|
+
return EvalArg
|
|
569
|
+
|
|
570
|
+
def _make_element_eval_arg(self):
|
|
571
|
+
@cache.dynamic_struct(suffix=self.name)
|
|
572
|
+
class ElementEvalArg:
|
|
573
|
+
elt_arg: self.domain.ElementArg
|
|
574
|
+
eval_arg: self.EvalArg
|
|
575
|
+
|
|
576
|
+
return ElementEvalArg
|
|
577
|
+
|
|
578
|
+
def _make_eval_degree(self):
|
|
579
|
+
@cache.dynamic_func(suffix=self.name)
|
|
580
|
+
def degree(args: self.ElementEvalArg):
|
|
581
|
+
return self.field.eval_degree(args.eval_arg.field_cell_eval_arg)
|
|
582
|
+
|
|
583
|
+
return degree
|
|
584
|
+
|
|
585
|
+
def trace(self):
|
|
586
|
+
if self.element_kind == ElementKind.SIDE:
|
|
587
|
+
raise RuntimeError("Trace only available for field defined on cell elements")
|
|
588
|
+
|
|
589
|
+
return NonconformingField(
|
|
590
|
+
domain=Sides(self.domain.geometry_partition), field=self.field, background=self.background.trace()
|
|
591
|
+
)
|
warp/fem/field/nodal_field.py
CHANGED
|
@@ -28,6 +28,7 @@ class NodalFieldBase(DiscreteField):
|
|
|
28
28
|
self.eval_div_outer = self._make_eval_div_outer()
|
|
29
29
|
|
|
30
30
|
self.set_node_value = self._make_set_node_value()
|
|
31
|
+
self.node_partition_index = self._make_node_partition_index()
|
|
31
32
|
|
|
32
33
|
def _make_eval_arg(self):
|
|
33
34
|
@cache.dynamic_struct(suffix=self.name)
|
|
@@ -62,14 +63,15 @@ class NodalFieldBase(DiscreteField):
|
|
|
62
63
|
return read_node_value
|
|
63
64
|
|
|
64
65
|
def _make_eval_inner(self):
|
|
65
|
-
NODES_PER_ELEMENT = self.space.topology.NODES_PER_ELEMENT
|
|
66
|
-
|
|
67
66
|
@cache.dynamic_func(suffix=self.name)
|
|
68
67
|
def eval_inner(args: self.ElementEvalArg, s: Sample):
|
|
69
68
|
res = self.space.element_inner_weight(
|
|
70
69
|
args.elt_arg, args.eval_arg.space_arg, s.element_index, s.element_coords, 0
|
|
71
70
|
) * self._read_node_value(args, s.element_index, 0)
|
|
72
|
-
|
|
71
|
+
node_count = self.space.topology.element_node_count(
|
|
72
|
+
args.elt_arg, args.eval_arg.topology_arg, s.element_index
|
|
73
|
+
)
|
|
74
|
+
for k in range(1, node_count):
|
|
73
75
|
res += self.space.element_inner_weight(
|
|
74
76
|
args.elt_arg, args.eval_arg.space_arg, s.element_index, s.element_coords, k
|
|
75
77
|
) * self._read_node_value(args, s.element_index, k)
|
|
@@ -78,8 +80,6 @@ class NodalFieldBase(DiscreteField):
|
|
|
78
80
|
return eval_inner
|
|
79
81
|
|
|
80
82
|
def _make_eval_grad_inner(self, world_space: bool):
|
|
81
|
-
NODES_PER_ELEMENT = self.space.topology.NODES_PER_ELEMENT
|
|
82
|
-
|
|
83
83
|
if not self.gradient_valid():
|
|
84
84
|
return None
|
|
85
85
|
|
|
@@ -91,7 +91,10 @@ class NodalFieldBase(DiscreteField):
|
|
|
91
91
|
args.elt_arg, args.eval_arg.space_arg, s.element_index, s.element_coords, 0
|
|
92
92
|
),
|
|
93
93
|
)
|
|
94
|
-
|
|
94
|
+
node_count = self.space.topology.element_node_count(
|
|
95
|
+
args.elt_arg, args.eval_arg.topology_arg, s.element_index
|
|
96
|
+
)
|
|
97
|
+
for k in range(1, node_count):
|
|
95
98
|
res += utils.generalized_outer(
|
|
96
99
|
self._read_node_value(args, s.element_index, k),
|
|
97
100
|
self.space.element_inner_weight_gradient(
|
|
@@ -109,8 +112,6 @@ class NodalFieldBase(DiscreteField):
|
|
|
109
112
|
return eval_grad_inner_world_space if world_space else eval_grad_inner_ref_space
|
|
110
113
|
|
|
111
114
|
def _make_eval_div_inner(self):
|
|
112
|
-
NODES_PER_ELEMENT = self.space.topology.NODES_PER_ELEMENT
|
|
113
|
-
|
|
114
115
|
if not self.divergence_valid():
|
|
115
116
|
return None
|
|
116
117
|
|
|
@@ -128,7 +129,10 @@ class NodalFieldBase(DiscreteField):
|
|
|
128
129
|
),
|
|
129
130
|
)
|
|
130
131
|
|
|
131
|
-
|
|
132
|
+
node_count = self.space.topology.element_node_count(
|
|
133
|
+
args.elt_arg, args.eval_arg.topology_arg, s.element_index
|
|
134
|
+
)
|
|
135
|
+
for k in range(1, node_count):
|
|
132
136
|
res += utils.generalized_inner(
|
|
133
137
|
self._read_node_value(args, s.element_index, k),
|
|
134
138
|
utils.apply_right(
|
|
@@ -143,8 +147,6 @@ class NodalFieldBase(DiscreteField):
|
|
|
143
147
|
return eval_div_inner
|
|
144
148
|
|
|
145
149
|
def _make_eval_outer(self):
|
|
146
|
-
NODES_PER_ELEMENT = self.space.topology.NODES_PER_ELEMENT
|
|
147
|
-
|
|
148
150
|
@cache.dynamic_func(suffix=self.name)
|
|
149
151
|
def eval_outer(args: self.ElementEvalArg, s: Sample):
|
|
150
152
|
res = self.space.element_outer_weight(
|
|
@@ -154,7 +156,10 @@ class NodalFieldBase(DiscreteField):
|
|
|
154
156
|
s.element_coords,
|
|
155
157
|
0,
|
|
156
158
|
) * self._read_node_value(args, s.element_index, 0)
|
|
157
|
-
|
|
159
|
+
node_count = self.space.topology.element_node_count(
|
|
160
|
+
args.elt_arg, args.eval_arg.topology_arg, s.element_index
|
|
161
|
+
)
|
|
162
|
+
for k in range(1, node_count):
|
|
158
163
|
res += self.space.element_outer_weight(
|
|
159
164
|
args.elt_arg,
|
|
160
165
|
args.eval_arg.space_arg,
|
|
@@ -167,8 +172,6 @@ class NodalFieldBase(DiscreteField):
|
|
|
167
172
|
return eval_outer
|
|
168
173
|
|
|
169
174
|
def _make_eval_grad_outer(self, world_space: bool):
|
|
170
|
-
NODES_PER_ELEMENT = self.space.topology.NODES_PER_ELEMENT
|
|
171
|
-
|
|
172
175
|
if not self.gradient_valid():
|
|
173
176
|
return None
|
|
174
177
|
|
|
@@ -180,7 +183,10 @@ class NodalFieldBase(DiscreteField):
|
|
|
180
183
|
args.elt_arg, args.eval_arg.space_arg, s.element_index, s.element_coords, 0
|
|
181
184
|
),
|
|
182
185
|
)
|
|
183
|
-
|
|
186
|
+
node_count = self.space.topology.element_node_count(
|
|
187
|
+
args.elt_arg, args.eval_arg.topology_arg, s.element_index
|
|
188
|
+
)
|
|
189
|
+
for k in range(1, node_count):
|
|
184
190
|
res += utils.generalized_outer(
|
|
185
191
|
self._read_node_value(args, s.element_index, k),
|
|
186
192
|
self.space.element_outer_weight_gradient(
|
|
@@ -198,8 +204,6 @@ class NodalFieldBase(DiscreteField):
|
|
|
198
204
|
return eval_grad_outer_world_space if world_space else eval_grad_outer_ref_space
|
|
199
205
|
|
|
200
206
|
def _make_eval_div_outer(self):
|
|
201
|
-
NODES_PER_ELEMENT = self.space.topology.NODES_PER_ELEMENT
|
|
202
|
-
|
|
203
207
|
if not self.divergence_valid():
|
|
204
208
|
return None
|
|
205
209
|
|
|
@@ -216,7 +220,11 @@ class NodalFieldBase(DiscreteField):
|
|
|
216
220
|
grad_transform,
|
|
217
221
|
),
|
|
218
222
|
)
|
|
219
|
-
|
|
223
|
+
|
|
224
|
+
node_count = self.space.topology.element_node_count(
|
|
225
|
+
args.elt_arg, args.eval_arg.topology_arg, s.element_index
|
|
226
|
+
)
|
|
227
|
+
for k in range(1, node_count):
|
|
220
228
|
res += utils.generalized_inner(
|
|
221
229
|
self._read_node_value(args, s.element_index, k),
|
|
222
230
|
utils.apply_right(
|
|
@@ -237,6 +245,13 @@ class NodalFieldBase(DiscreteField):
|
|
|
237
245
|
|
|
238
246
|
return set_node_value
|
|
239
247
|
|
|
248
|
+
def _make_node_partition_index(self):
|
|
249
|
+
@cache.dynamic_func(suffix=self.name)
|
|
250
|
+
def node_partition_index(args: self.EvalArg, node_index: int):
|
|
251
|
+
return self.space_partition.partition_node_index(args.eval_arg.partition_arg, node_index)
|
|
252
|
+
|
|
253
|
+
return node_partition_index
|
|
254
|
+
|
|
240
255
|
|
|
241
256
|
class NodalField(NodalFieldBase):
|
|
242
257
|
"""A field holding values for all degrees of freedom at each node of the underlying function space partition
|