warp-lang 1.9.1__py3-none-manylinux_2_34_aarch64.whl → 1.10.0__py3-none-manylinux_2_34_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 (346) hide show
  1. warp/__init__.py +301 -287
  2. warp/__init__.pyi +882 -305
  3. warp/_src/__init__.py +14 -0
  4. warp/_src/autograd.py +1077 -0
  5. warp/_src/build.py +620 -0
  6. warp/_src/build_dll.py +642 -0
  7. warp/{builtins.py → _src/builtins.py} +1435 -379
  8. warp/_src/codegen.py +4361 -0
  9. warp/{config.py → _src/config.py} +178 -169
  10. warp/_src/constants.py +59 -0
  11. warp/_src/context.py +8352 -0
  12. warp/_src/dlpack.py +464 -0
  13. warp/_src/fabric.py +362 -0
  14. warp/_src/fem/__init__.py +14 -0
  15. warp/_src/fem/adaptivity.py +510 -0
  16. warp/_src/fem/cache.py +689 -0
  17. warp/_src/fem/dirichlet.py +190 -0
  18. warp/{fem → _src/fem}/domain.py +42 -30
  19. warp/_src/fem/field/__init__.py +131 -0
  20. warp/_src/fem/field/field.py +703 -0
  21. warp/{fem → _src/fem}/field/nodal_field.py +32 -15
  22. warp/{fem → _src/fem}/field/restriction.py +3 -1
  23. warp/{fem → _src/fem}/field/virtual.py +55 -27
  24. warp/_src/fem/geometry/__init__.py +32 -0
  25. warp/{fem → _src/fem}/geometry/adaptive_nanogrid.py +79 -163
  26. warp/_src/fem/geometry/closest_point.py +99 -0
  27. warp/{fem → _src/fem}/geometry/deformed_geometry.py +16 -22
  28. warp/{fem → _src/fem}/geometry/element.py +34 -10
  29. warp/{fem → _src/fem}/geometry/geometry.py +50 -20
  30. warp/{fem → _src/fem}/geometry/grid_2d.py +14 -23
  31. warp/{fem → _src/fem}/geometry/grid_3d.py +14 -23
  32. warp/{fem → _src/fem}/geometry/hexmesh.py +42 -63
  33. warp/{fem → _src/fem}/geometry/nanogrid.py +256 -247
  34. warp/{fem → _src/fem}/geometry/partition.py +123 -63
  35. warp/{fem → _src/fem}/geometry/quadmesh.py +28 -45
  36. warp/{fem → _src/fem}/geometry/tetmesh.py +42 -63
  37. warp/{fem → _src/fem}/geometry/trimesh.py +28 -45
  38. warp/{fem → _src/fem}/integrate.py +166 -158
  39. warp/_src/fem/linalg.py +385 -0
  40. warp/_src/fem/operator.py +398 -0
  41. warp/_src/fem/polynomial.py +231 -0
  42. warp/{fem → _src/fem}/quadrature/pic_quadrature.py +17 -20
  43. warp/{fem → _src/fem}/quadrature/quadrature.py +97 -47
  44. warp/_src/fem/space/__init__.py +248 -0
  45. warp/{fem → _src/fem}/space/basis_function_space.py +22 -11
  46. warp/_src/fem/space/basis_space.py +681 -0
  47. warp/{fem → _src/fem}/space/dof_mapper.py +5 -3
  48. warp/{fem → _src/fem}/space/function_space.py +16 -13
  49. warp/{fem → _src/fem}/space/grid_2d_function_space.py +6 -7
  50. warp/{fem → _src/fem}/space/grid_3d_function_space.py +6 -4
  51. warp/{fem → _src/fem}/space/hexmesh_function_space.py +6 -10
  52. warp/{fem → _src/fem}/space/nanogrid_function_space.py +5 -9
  53. warp/{fem → _src/fem}/space/partition.py +119 -60
  54. warp/{fem → _src/fem}/space/quadmesh_function_space.py +6 -10
  55. warp/{fem → _src/fem}/space/restriction.py +68 -33
  56. warp/_src/fem/space/shape/__init__.py +152 -0
  57. warp/{fem → _src/fem}/space/shape/cube_shape_function.py +11 -9
  58. warp/{fem → _src/fem}/space/shape/shape_function.py +10 -9
  59. warp/{fem → _src/fem}/space/shape/square_shape_function.py +8 -6
  60. warp/{fem → _src/fem}/space/shape/tet_shape_function.py +5 -3
  61. warp/{fem → _src/fem}/space/shape/triangle_shape_function.py +5 -3
  62. warp/{fem → _src/fem}/space/tetmesh_function_space.py +5 -9
  63. warp/_src/fem/space/topology.py +461 -0
  64. warp/{fem → _src/fem}/space/trimesh_function_space.py +5 -9
  65. warp/_src/fem/types.py +114 -0
  66. warp/_src/fem/utils.py +488 -0
  67. warp/_src/jax.py +188 -0
  68. warp/_src/jax_experimental/__init__.py +14 -0
  69. warp/_src/jax_experimental/custom_call.py +389 -0
  70. warp/_src/jax_experimental/ffi.py +1286 -0
  71. warp/_src/jax_experimental/xla_ffi.py +658 -0
  72. warp/_src/marching_cubes.py +710 -0
  73. warp/_src/math.py +416 -0
  74. warp/_src/optim/__init__.py +14 -0
  75. warp/_src/optim/adam.py +165 -0
  76. warp/_src/optim/linear.py +1608 -0
  77. warp/_src/optim/sgd.py +114 -0
  78. warp/_src/paddle.py +408 -0
  79. warp/_src/render/__init__.py +14 -0
  80. warp/_src/render/imgui_manager.py +291 -0
  81. warp/_src/render/render_opengl.py +3638 -0
  82. warp/_src/render/render_usd.py +939 -0
  83. warp/_src/render/utils.py +162 -0
  84. warp/_src/sparse.py +2718 -0
  85. warp/_src/tape.py +1208 -0
  86. warp/{thirdparty → _src/thirdparty}/unittest_parallel.py +9 -2
  87. warp/_src/torch.py +393 -0
  88. warp/_src/types.py +5888 -0
  89. warp/_src/utils.py +1695 -0
  90. warp/autograd.py +12 -1054
  91. warp/bin/warp-clang.so +0 -0
  92. warp/bin/warp.so +0 -0
  93. warp/build.py +8 -588
  94. warp/build_dll.py +6 -721
  95. warp/codegen.py +6 -4251
  96. warp/constants.py +6 -39
  97. warp/context.py +12 -8062
  98. warp/dlpack.py +6 -444
  99. warp/examples/distributed/example_jacobi_mpi.py +4 -5
  100. warp/examples/fem/example_adaptive_grid.py +1 -1
  101. warp/examples/fem/example_apic_fluid.py +1 -1
  102. warp/examples/fem/example_burgers.py +8 -8
  103. warp/examples/fem/example_diffusion.py +1 -1
  104. warp/examples/fem/example_distortion_energy.py +1 -1
  105. warp/examples/fem/example_mixed_elasticity.py +2 -2
  106. warp/examples/fem/example_navier_stokes.py +1 -1
  107. warp/examples/fem/example_nonconforming_contact.py +7 -7
  108. warp/examples/fem/example_stokes.py +1 -1
  109. warp/examples/fem/example_stokes_transfer.py +1 -1
  110. warp/examples/fem/utils.py +2 -2
  111. warp/examples/interop/example_jax_callable.py +1 -1
  112. warp/examples/interop/example_jax_ffi_callback.py +1 -1
  113. warp/examples/interop/example_jax_kernel.py +1 -1
  114. warp/examples/tile/example_tile_mcgp.py +191 -0
  115. warp/fabric.py +6 -337
  116. warp/fem/__init__.py +159 -97
  117. warp/fem/adaptivity.py +7 -489
  118. warp/fem/cache.py +9 -648
  119. warp/fem/dirichlet.py +6 -184
  120. warp/fem/field/__init__.py +8 -109
  121. warp/fem/field/field.py +7 -652
  122. warp/fem/geometry/__init__.py +7 -18
  123. warp/fem/geometry/closest_point.py +11 -77
  124. warp/fem/linalg.py +18 -366
  125. warp/fem/operator.py +11 -369
  126. warp/fem/polynomial.py +9 -209
  127. warp/fem/space/__init__.py +5 -211
  128. warp/fem/space/basis_space.py +6 -662
  129. warp/fem/space/shape/__init__.py +41 -118
  130. warp/fem/space/topology.py +6 -437
  131. warp/fem/types.py +6 -81
  132. warp/fem/utils.py +11 -444
  133. warp/jax.py +8 -165
  134. warp/jax_experimental/__init__.py +14 -1
  135. warp/jax_experimental/custom_call.py +8 -365
  136. warp/jax_experimental/ffi.py +17 -873
  137. warp/jax_experimental/xla_ffi.py +5 -605
  138. warp/marching_cubes.py +5 -689
  139. warp/math.py +16 -393
  140. warp/native/array.h +385 -37
  141. warp/native/builtin.h +314 -37
  142. warp/native/bvh.cpp +43 -9
  143. warp/native/bvh.cu +62 -27
  144. warp/native/bvh.h +310 -309
  145. warp/native/clang/clang.cpp +102 -97
  146. warp/native/coloring.cpp +0 -1
  147. warp/native/crt.h +208 -0
  148. warp/native/exports.h +156 -0
  149. warp/native/hashgrid.cu +2 -0
  150. warp/native/intersect.h +24 -1
  151. warp/native/intersect_tri.h +44 -35
  152. warp/native/mat.h +1456 -276
  153. warp/native/mesh.cpp +4 -4
  154. warp/native/mesh.cu +4 -2
  155. warp/native/mesh.h +176 -61
  156. warp/native/quat.h +0 -52
  157. warp/native/scan.cu +2 -0
  158. warp/native/sparse.cu +7 -3
  159. warp/native/spatial.h +12 -0
  160. warp/native/tile.h +681 -89
  161. warp/native/tile_radix_sort.h +3 -3
  162. warp/native/tile_reduce.h +394 -46
  163. warp/native/tile_scan.h +4 -4
  164. warp/native/vec.h +469 -0
  165. warp/native/version.h +23 -0
  166. warp/native/volume.cpp +1 -1
  167. warp/native/volume.cu +1 -0
  168. warp/native/volume.h +1 -1
  169. warp/native/volume_builder.cu +2 -0
  170. warp/native/warp.cpp +57 -29
  171. warp/native/warp.cu +521 -250
  172. warp/native/warp.h +11 -8
  173. warp/optim/__init__.py +6 -3
  174. warp/optim/adam.py +6 -145
  175. warp/optim/linear.py +14 -1585
  176. warp/optim/sgd.py +6 -94
  177. warp/paddle.py +6 -388
  178. warp/render/__init__.py +8 -4
  179. warp/render/imgui_manager.py +7 -267
  180. warp/render/render_opengl.py +6 -3618
  181. warp/render/render_usd.py +6 -919
  182. warp/render/utils.py +6 -142
  183. warp/sparse.py +37 -2563
  184. warp/tape.py +6 -1188
  185. warp/tests/__main__.py +1 -1
  186. warp/tests/cuda/test_async.py +4 -4
  187. warp/tests/cuda/test_conditional_captures.py +1 -1
  188. warp/tests/cuda/test_multigpu.py +1 -1
  189. warp/tests/cuda/test_streams.py +58 -1
  190. warp/tests/geometry/test_bvh.py +157 -22
  191. warp/tests/geometry/test_marching_cubes.py +0 -1
  192. warp/tests/geometry/test_mesh.py +5 -3
  193. warp/tests/geometry/test_mesh_query_aabb.py +5 -12
  194. warp/tests/geometry/test_mesh_query_point.py +5 -2
  195. warp/tests/geometry/test_mesh_query_ray.py +15 -3
  196. warp/tests/geometry/test_volume_write.py +5 -5
  197. warp/tests/interop/test_dlpack.py +18 -17
  198. warp/tests/interop/test_jax.py +772 -49
  199. warp/tests/interop/test_paddle.py +1 -1
  200. warp/tests/test_adam.py +0 -1
  201. warp/tests/test_arithmetic.py +9 -9
  202. warp/tests/test_array.py +578 -100
  203. warp/tests/test_array_reduce.py +3 -3
  204. warp/tests/test_atomic.py +12 -8
  205. warp/tests/test_atomic_bitwise.py +209 -0
  206. warp/tests/test_atomic_cas.py +4 -4
  207. warp/tests/test_bool.py +2 -2
  208. warp/tests/test_builtins_resolution.py +5 -571
  209. warp/tests/test_codegen.py +33 -14
  210. warp/tests/test_conditional.py +1 -1
  211. warp/tests/test_context.py +6 -6
  212. warp/tests/test_copy.py +242 -161
  213. warp/tests/test_ctypes.py +3 -3
  214. warp/tests/test_devices.py +24 -2
  215. warp/tests/test_examples.py +16 -84
  216. warp/tests/test_fabricarray.py +35 -35
  217. warp/tests/test_fast_math.py +0 -2
  218. warp/tests/test_fem.py +56 -10
  219. warp/tests/test_fixedarray.py +3 -3
  220. warp/tests/test_func.py +8 -5
  221. warp/tests/test_generics.py +1 -1
  222. warp/tests/test_indexedarray.py +24 -24
  223. warp/tests/test_intersect.py +39 -9
  224. warp/tests/test_large.py +1 -1
  225. warp/tests/test_lerp.py +3 -1
  226. warp/tests/test_linear_solvers.py +1 -1
  227. warp/tests/test_map.py +35 -4
  228. warp/tests/test_mat.py +52 -62
  229. warp/tests/test_mat_constructors.py +4 -5
  230. warp/tests/test_mat_lite.py +1 -1
  231. warp/tests/test_mat_scalar_ops.py +121 -121
  232. warp/tests/test_math.py +34 -0
  233. warp/tests/test_module_aot.py +4 -4
  234. warp/tests/test_modules_lite.py +28 -2
  235. warp/tests/test_print.py +11 -11
  236. warp/tests/test_quat.py +93 -58
  237. warp/tests/test_runlength_encode.py +1 -1
  238. warp/tests/test_scalar_ops.py +38 -10
  239. warp/tests/test_smoothstep.py +1 -1
  240. warp/tests/test_sparse.py +126 -15
  241. warp/tests/test_spatial.py +105 -87
  242. warp/tests/test_special_values.py +6 -6
  243. warp/tests/test_static.py +7 -7
  244. warp/tests/test_struct.py +13 -2
  245. warp/tests/test_triangle_closest_point.py +48 -1
  246. warp/tests/test_types.py +27 -15
  247. warp/tests/test_utils.py +52 -52
  248. warp/tests/test_vec.py +29 -29
  249. warp/tests/test_vec_constructors.py +5 -5
  250. warp/tests/test_vec_scalar_ops.py +97 -97
  251. warp/tests/test_version.py +75 -0
  252. warp/tests/tile/test_tile.py +178 -0
  253. warp/tests/tile/test_tile_atomic_bitwise.py +403 -0
  254. warp/tests/tile/test_tile_cholesky.py +7 -4
  255. warp/tests/tile/test_tile_load.py +26 -2
  256. warp/tests/tile/test_tile_mathdx.py +3 -3
  257. warp/tests/tile/test_tile_matmul.py +1 -1
  258. warp/tests/tile/test_tile_mlp.py +2 -4
  259. warp/tests/tile/test_tile_reduce.py +214 -13
  260. warp/tests/unittest_suites.py +6 -14
  261. warp/tests/unittest_utils.py +10 -9
  262. warp/tests/walkthrough_debug.py +3 -1
  263. warp/torch.py +6 -373
  264. warp/types.py +29 -5764
  265. warp/utils.py +10 -1659
  266. {warp_lang-1.9.1.dist-info → warp_lang-1.10.0.dist-info}/METADATA +46 -99
  267. warp_lang-1.10.0.dist-info/RECORD +468 -0
  268. warp_lang-1.10.0.dist-info/licenses/licenses/Gaia-LICENSE.txt +6 -0
  269. warp_lang-1.10.0.dist-info/licenses/licenses/appdirs-LICENSE.txt +22 -0
  270. warp_lang-1.10.0.dist-info/licenses/licenses/asset_pixel_jpg-LICENSE.txt +3 -0
  271. warp_lang-1.10.0.dist-info/licenses/licenses/cuda-LICENSE.txt +1582 -0
  272. warp_lang-1.10.0.dist-info/licenses/licenses/dlpack-LICENSE.txt +201 -0
  273. warp_lang-1.10.0.dist-info/licenses/licenses/fp16-LICENSE.txt +28 -0
  274. warp_lang-1.10.0.dist-info/licenses/licenses/libmathdx-LICENSE.txt +220 -0
  275. warp_lang-1.10.0.dist-info/licenses/licenses/llvm-LICENSE.txt +279 -0
  276. warp_lang-1.10.0.dist-info/licenses/licenses/moller-LICENSE.txt +16 -0
  277. warp_lang-1.10.0.dist-info/licenses/licenses/nanovdb-LICENSE.txt +2 -0
  278. warp_lang-1.10.0.dist-info/licenses/licenses/nvrtc-LICENSE.txt +1592 -0
  279. warp_lang-1.10.0.dist-info/licenses/licenses/svd-LICENSE.txt +23 -0
  280. warp_lang-1.10.0.dist-info/licenses/licenses/unittest_parallel-LICENSE.txt +21 -0
  281. warp_lang-1.10.0.dist-info/licenses/licenses/usd-LICENSE.txt +213 -0
  282. warp_lang-1.10.0.dist-info/licenses/licenses/windingnumber-LICENSE.txt +21 -0
  283. warp/examples/assets/cartpole.urdf +0 -110
  284. warp/examples/assets/crazyflie.usd +0 -0
  285. warp/examples/assets/nv_ant.xml +0 -92
  286. warp/examples/assets/nv_humanoid.xml +0 -183
  287. warp/examples/assets/quadruped.urdf +0 -268
  288. warp/examples/optim/example_bounce.py +0 -266
  289. warp/examples/optim/example_cloth_throw.py +0 -228
  290. warp/examples/optim/example_drone.py +0 -870
  291. warp/examples/optim/example_inverse_kinematics.py +0 -182
  292. warp/examples/optim/example_inverse_kinematics_torch.py +0 -191
  293. warp/examples/optim/example_softbody_properties.py +0 -400
  294. warp/examples/optim/example_spring_cage.py +0 -245
  295. warp/examples/optim/example_trajectory.py +0 -227
  296. warp/examples/sim/example_cartpole.py +0 -143
  297. warp/examples/sim/example_cloth.py +0 -225
  298. warp/examples/sim/example_cloth_self_contact.py +0 -316
  299. warp/examples/sim/example_granular.py +0 -130
  300. warp/examples/sim/example_granular_collision_sdf.py +0 -202
  301. warp/examples/sim/example_jacobian_ik.py +0 -244
  302. warp/examples/sim/example_particle_chain.py +0 -124
  303. warp/examples/sim/example_quadruped.py +0 -203
  304. warp/examples/sim/example_rigid_chain.py +0 -203
  305. warp/examples/sim/example_rigid_contact.py +0 -195
  306. warp/examples/sim/example_rigid_force.py +0 -133
  307. warp/examples/sim/example_rigid_gyroscopic.py +0 -115
  308. warp/examples/sim/example_rigid_soft_contact.py +0 -140
  309. warp/examples/sim/example_soft_body.py +0 -196
  310. warp/examples/tile/example_tile_walker.py +0 -327
  311. warp/sim/__init__.py +0 -74
  312. warp/sim/articulation.py +0 -793
  313. warp/sim/collide.py +0 -2570
  314. warp/sim/graph_coloring.py +0 -307
  315. warp/sim/import_mjcf.py +0 -791
  316. warp/sim/import_snu.py +0 -227
  317. warp/sim/import_urdf.py +0 -579
  318. warp/sim/import_usd.py +0 -898
  319. warp/sim/inertia.py +0 -357
  320. warp/sim/integrator.py +0 -245
  321. warp/sim/integrator_euler.py +0 -2000
  322. warp/sim/integrator_featherstone.py +0 -2101
  323. warp/sim/integrator_vbd.py +0 -2487
  324. warp/sim/integrator_xpbd.py +0 -3295
  325. warp/sim/model.py +0 -4821
  326. warp/sim/particles.py +0 -121
  327. warp/sim/render.py +0 -431
  328. warp/sim/utils.py +0 -431
  329. warp/tests/sim/disabled_kinematics.py +0 -244
  330. warp/tests/sim/test_cloth.py +0 -863
  331. warp/tests/sim/test_collision.py +0 -743
  332. warp/tests/sim/test_coloring.py +0 -347
  333. warp/tests/sim/test_inertia.py +0 -161
  334. warp/tests/sim/test_model.py +0 -226
  335. warp/tests/sim/test_sim_grad.py +0 -287
  336. warp/tests/sim/test_sim_grad_bounce_linear.py +0 -212
  337. warp/tests/sim/test_sim_kinematics.py +0 -98
  338. warp/thirdparty/__init__.py +0 -0
  339. warp_lang-1.9.1.dist-info/RECORD +0 -456
  340. /warp/{fem → _src/fem}/quadrature/__init__.py +0 -0
  341. /warp/{tests/sim → _src/thirdparty}/__init__.py +0 -0
  342. /warp/{thirdparty → _src/thirdparty}/appdirs.py +0 -0
  343. /warp/{thirdparty → _src/thirdparty}/dlpack.py +0 -0
  344. {warp_lang-1.9.1.dist-info → warp_lang-1.10.0.dist-info}/WHEEL +0 -0
  345. {warp_lang-1.9.1.dist-info → warp_lang-1.10.0.dist-info}/licenses/LICENSE.md +0 -0
  346. {warp_lang-1.9.1.dist-info → warp_lang-1.10.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,703 @@
1
+ # SPDX-FileCopyrightText: Copyright (c) 2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2
+ # SPDX-License-Identifier: Apache-2.0
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ from functools import cached_property
17
+ from typing import Any, ClassVar, Dict, Optional, Set
18
+
19
+ import warp as wp
20
+ from warp._src.codegen import Struct
21
+ from warp._src.fem import cache
22
+ from warp._src.fem.domain import GeometryDomain, Sides
23
+ from warp._src.fem.geometry import DeformedGeometry, Geometry
24
+ from warp._src.fem.operator import Operator, integrand
25
+ from warp._src.fem.space import FunctionSpace, SpacePartition
26
+ from warp._src.fem.types import NULL_ELEMENT_INDEX, ElementKind, Sample
27
+ from warp._src.fem.utils import type_zero_element
28
+ from warp._src.types import (
29
+ is_value,
30
+ type_is_matrix,
31
+ type_is_quaternion,
32
+ type_is_vector,
33
+ type_repr,
34
+ type_scalar_type,
35
+ type_size,
36
+ type_to_warp,
37
+ types_equal,
38
+ )
39
+
40
+ _wp_module_name_ = "warp.fem.field.field"
41
+
42
+
43
+ class FieldLike:
44
+ """Base class for integrable fields"""
45
+
46
+ EvalArg: Struct
47
+ """Structure containing field-level arguments passed to device functions for field evaluation"""
48
+
49
+ ElementEvalArg: Struct
50
+ """Structure combining geometry-level and field-level arguments passed to device functions for field evaluation"""
51
+
52
+ def eval_arg_value(self, device) -> "EvalArg": # noqa: F821
53
+ """Value of the field-level arguments to be passed to device functions"""
54
+ args = self.EvalArg()
55
+ self.fill_eval_arg(args, device)
56
+ return args
57
+
58
+ def fill_eval_arg(self, arg: "FieldLike.EvalArg", device):
59
+ """Fill the field-level arguments to be passed to device functions"""
60
+ if self.eval_arg_value is __class__.eval_arg_value:
61
+ raise NotImplementedError()
62
+ arg.assign(self.eval_arg_value(device))
63
+
64
+ @property
65
+ def degree(self) -> int:
66
+ """Polynomial degree of the field, used to estimate necessary quadrature order"""
67
+ raise NotImplementedError()
68
+
69
+ @property
70
+ def name(self) -> str:
71
+ raise NotImplementedError()
72
+
73
+ @property
74
+ def __str__(self) -> str:
75
+ return self.name
76
+
77
+ def gradient_valid(self) -> bool:
78
+ """Whether the gradient operator is implemented for this field."""
79
+ return False
80
+
81
+ def divergence_valid(self) -> bool:
82
+ """Whether the divergence operator is implemented for this field."""
83
+ return False
84
+
85
+ @staticmethod
86
+ def eval_inner(args: "ElementEvalArg", s: Sample): # noqa: F821
87
+ """Device function evaluating the inner field value at a sample point"""
88
+ raise NotImplementedError()
89
+
90
+ @staticmethod
91
+ def eval_grad_inner(args: "ElementEvalArg", s: Sample): # noqa: F821
92
+ """Device function evaluating the inner field gradient at a sample point"""
93
+ raise NotImplementedError()
94
+
95
+ @staticmethod
96
+ def eval_div_inner(args: "ElementEvalArg", s: Sample): # noqa: F821
97
+ """Device function evaluating the inner field divergence at a sample point"""
98
+ raise NotImplementedError()
99
+
100
+ @staticmethod
101
+ def eval_outer(args: "ElementEvalArg", s: Sample): # noqa: F821
102
+ """Device function evaluating the outer field value at a sample point"""
103
+ raise NotImplementedError()
104
+
105
+ @staticmethod
106
+ def eval_grad_outer(args: "ElementEvalArg", s: Sample): # noqa: F821
107
+ """Device function evaluating the outer field gradient at a sample point"""
108
+ raise NotImplementedError()
109
+
110
+ @staticmethod
111
+ def eval_div_outer(args: "ElementEvalArg", s: Sample): # noqa: F821
112
+ """Device function evaluating the outer field divergence at a sample point"""
113
+ raise NotImplementedError()
114
+
115
+ @staticmethod
116
+ def eval_degree(args: "ElementEvalArg"): # noqa: F821
117
+ """Polynomial degree of the field is applicable, or hint for determination of interpolation order"""
118
+ raise NotImplementedError()
119
+
120
+ def notify_operator_usage(self, ops: Set[Operator]):
121
+ """Makes the Domain aware that the operators `ops` will be applied"""
122
+ pass
123
+
124
+
125
+ class GeometryField(FieldLike):
126
+ """Base class for fields defined over a geometry"""
127
+
128
+ @property
129
+ def geometry(self) -> Geometry:
130
+ """Geometry over which the field is expressed"""
131
+ raise NotImplementedError
132
+
133
+ @property
134
+ def element_kind(self) -> ElementKind:
135
+ """Kind of element over which the field is expressed"""
136
+ raise NotImplementedError
137
+
138
+ @property
139
+ def dtype(self) -> type:
140
+ raise NotImplementedError
141
+
142
+ @staticmethod
143
+ def eval_reference_grad_inner(args: "ElementEvalArg", s: Sample): # noqa: F821
144
+ """Device function evaluating the inner field gradient with respect to reference element coordinates at a sample point"""
145
+ raise NotImplementedError
146
+
147
+ @staticmethod
148
+ def eval_reference_grad_outer(args: "ElementEvalArg", s: Sample): # noqa: F821
149
+ """Device function evaluating the outer field gradient with respect to reference element coordinates at a sample point"""
150
+ raise NotImplementedError
151
+
152
+ def trace(self) -> FieldLike:
153
+ """Trace of this field over lower-dimensional elements"""
154
+ raise NotImplementedError
155
+
156
+ def make_deformed_geometry(self, relative=True) -> Geometry:
157
+ """Returns a deformed version of the underlying geometry, with positions displaced according to this field's values.
158
+
159
+ Args:
160
+ relative: If ``True``, the field is interpreted as a relative displacement over the original geometry.
161
+ If ``False``, the field values are interpreted as absolute positions.
162
+
163
+ """
164
+ return DeformedGeometry(self, relative=relative)
165
+
166
+ @cached_property
167
+ def gradient_dtype(self):
168
+ """Return type of the (world space) gradient operator. Assumes self.gradient_valid()"""
169
+ if type_is_matrix(self.dtype):
170
+ return None
171
+
172
+ if type_is_vector(self.dtype):
173
+ return cache.cached_mat_type(
174
+ shape=(type_size(self.dtype), self.geometry.dimension),
175
+ dtype=type_scalar_type(self.dtype),
176
+ )
177
+ if type_is_quaternion(self.dtype):
178
+ return cache.cached_mat_type(
179
+ shape=(4, self.geometry.dimension),
180
+ dtype=type_scalar_type(self.dtype),
181
+ )
182
+ return cache.cached_vec_type(length=self.geometry.dimension, dtype=type_scalar_type(self.dtype))
183
+
184
+ @cached_property
185
+ def reference_gradient_dtype(self):
186
+ """Return type of the reference space gradient operator. Assumes self.gradient_valid()"""
187
+ if type_is_matrix(self.dtype):
188
+ return None
189
+
190
+ if type_is_vector(self.dtype):
191
+ return cache.cached_mat_type(
192
+ shape=(type_size(self.dtype), self.geometry.cell_dimension),
193
+ dtype=type_scalar_type(self.dtype),
194
+ )
195
+ if type_is_quaternion(self.dtype):
196
+ return cache.cached_mat_type(
197
+ shape=(4, self.geometry.cell_dimension),
198
+ dtype=type_scalar_type(self.dtype),
199
+ )
200
+ return cache.cached_vec_type(length=self.geometry.cell_dimension, dtype=type_scalar_type(self.dtype))
201
+
202
+ @cached_property
203
+ def divergence_dtype(self):
204
+ """Return type of the divergence operator. Assumes self.divergence_valid()"""
205
+ if type_is_vector(self.dtype):
206
+ return type_scalar_type(self.dtype)
207
+ if type_is_matrix(self.dtype):
208
+ return cache.cached_vec_type(length=self.dtype._shape_[1], dtype=type_scalar_type(self.dtype))
209
+ return None
210
+
211
+
212
+ class SpaceField(GeometryField):
213
+ """Base class for fields defined over a function space"""
214
+
215
+ def __init__(self, space: FunctionSpace, space_partition: SpacePartition):
216
+ self._space = space
217
+ self._space_partition = space_partition
218
+
219
+ self.gradient_valid = self.space.gradient_valid
220
+ self.divergence_valid = self.space.divergence_valid
221
+
222
+ @property
223
+ def geometry(self) -> Geometry:
224
+ return self._space.geometry
225
+
226
+ @property
227
+ def element_kind(self) -> ElementKind:
228
+ return self._space.element_kind
229
+
230
+ @property
231
+ def space(self) -> FunctionSpace:
232
+ return self._space
233
+
234
+ @property
235
+ def space_partition(self) -> SpacePartition:
236
+ return self._space_partition
237
+
238
+ @property
239
+ def degree(self) -> int:
240
+ return self.space.degree
241
+
242
+ @property
243
+ def dtype(self) -> type:
244
+ return self.space.dtype
245
+
246
+ @property
247
+ def dof_dtype(self) -> type:
248
+ return self.space.dof_dtype
249
+
250
+ def _make_eval_degree(self):
251
+ ORDER = self.space.ORDER
252
+
253
+ @cache.dynamic_func(suffix=self.name)
254
+ def degree(args: self.ElementEvalArg):
255
+ return ORDER
256
+
257
+ return degree
258
+
259
+
260
+ class DiscreteField(SpaceField):
261
+ """Explicitly-valued field defined over a partition of a discrete function space"""
262
+
263
+ @property
264
+ def dof_values(self) -> wp.array:
265
+ """Array of degrees of freedom values"""
266
+ raise NotImplementedError
267
+
268
+ @dof_values.setter
269
+ def dof_values(self, values: wp.array):
270
+ """Sets degrees of freedom values from an array"""
271
+ raise NotImplementedError
272
+
273
+ @staticmethod
274
+ def set_node_value(args: "FieldLike.EvalArg", node_index: int, value: Any):
275
+ """Device function setting the value at given node"""
276
+ raise NotImplementedError
277
+
278
+ @cached_property
279
+ def name(self) -> str:
280
+ return f"{self.__class__.__qualname__}_{self.space.name}_{self.space_partition.name}"
281
+
282
+
283
+ class ImplicitField(GeometryField):
284
+ """Field defined from an arbitrary function over a domain.
285
+ Does not support autodiff yet, so if gradient/divergence evaluation is required corresponding functions must be provided.
286
+
287
+ Args:
288
+ domain: Domain over which the field is defined
289
+ 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``).
290
+ values: Optional dictionary of additional argument values to be passed to the evaluation function.
291
+ grad_func: Optional gradient evaluation function; must take same arguments as `func`
292
+ div_func: Optional divergence evaluation function; must take same arguments as `func`
293
+ degree: Optional hint for automatic determination of quadrature orders when integrating this field
294
+ """
295
+
296
+ _dynamic_attribute_constructors: ClassVar = {
297
+ "ElementEvalArg": lambda obj: obj._make_element_eval_arg(),
298
+ "eval_degree": lambda obj: obj._make_eval_degree(),
299
+ "eval_inner": lambda obj: obj._make_eval_func(obj._func),
300
+ "eval_grad_inner": lambda obj: obj._make_eval_func(obj._grad_func),
301
+ "eval_div_inner": lambda obj: obj._make_eval_func(obj._div_func),
302
+ "eval_reference_grad_inner": lambda obj: obj._make_eval_reference_grad(),
303
+ "eval_outer": lambda obj: obj.eval_inner,
304
+ "eval_grad_outer": lambda obj: obj.eval_grad_inner,
305
+ "eval_div_outer": lambda obj: obj.eval_div_inner,
306
+ "eval_reference_grad_outer": lambda obj: obj.eval_reference_grad_inner,
307
+ }
308
+
309
+ def __init__(
310
+ self,
311
+ domain: GeometryDomain,
312
+ func: wp.Function,
313
+ values: Optional[Dict[str, Any]] = None,
314
+ grad_func: Optional[wp.Function] = None,
315
+ div_func: Optional[wp.Function] = None,
316
+ degree=0,
317
+ ):
318
+ self.domain = domain
319
+ self._degree = degree
320
+
321
+ if not isinstance(func, wp.Function):
322
+ raise ValueError("Implicit field function must be a warp Function (decorated with `wp.func`)")
323
+
324
+ self._func = func
325
+ self._grad_func = grad_func
326
+ self._div_func = div_func
327
+
328
+ argspec = integrand(func.func).argspec
329
+ arg_types = {**argspec.annotations} # make a mutable copy
330
+
331
+ try:
332
+ first_arg_type = arg_types.pop(argspec.args[0])
333
+ if types_equal(first_arg_type, wp.vec(length=domain.geometry.dimension, dtype=float), match_generic=True):
334
+ self._qp_based = False
335
+ elif type_to_warp(first_arg_type) == wp.int32:
336
+ self._qp_based = True
337
+ else:
338
+ raise TypeError(f"Unsupported argument type `{type_repr(first_arg_type)}`")
339
+ except Exception as err:
340
+ raise ValueError(
341
+ f"Implicit field function '{func.func.__name__}' must accept either a position or a quadrature point index as its first argument"
342
+ ) from err
343
+
344
+ self.EvalArg = cache.get_argument_struct(arg_types)
345
+ self._func_arg = self.EvalArg()
346
+ self.values = values
347
+
348
+ cache.setup_dynamic_attributes(self)
349
+
350
+ @property
351
+ def values(self):
352
+ return self._func_arg
353
+
354
+ @values.setter
355
+ def values(self, v):
356
+ self._values = v
357
+ cache.populate_argument_struct(self._func_arg, v, self._func.func.__name__)
358
+
359
+ @property
360
+ def geometry(self) -> Geometry:
361
+ return self.domain.geometry
362
+
363
+ @property
364
+ def element_kind(self) -> ElementKind:
365
+ return self.domain.element_kind
366
+
367
+ @property
368
+ def dtype(self):
369
+ # Cannot determine dtype from function
370
+ return None
371
+
372
+ def eval_arg_value(self, device):
373
+ return self._func_arg
374
+
375
+ @property
376
+ def degree(self) -> int:
377
+ return self._degree
378
+
379
+ @property
380
+ def name(self) -> str:
381
+ return f"Implicit_{self.domain.name}_{self.degree}_{self.EvalArg.key}"
382
+
383
+ def gradient_valid(self) -> bool:
384
+ return self._grad_func is not None
385
+
386
+ def divergence_valid(self) -> bool:
387
+ return self._div_func is not None
388
+
389
+ def _make_eval_func(self, func):
390
+ if func is None:
391
+ return None
392
+
393
+ @cache.dynamic_func(
394
+ suffix=(self.name, func.key),
395
+ code_transformers=[cache.ExpandStarredArgumentStruct({"args.eval_arg": self.EvalArg})],
396
+ )
397
+ def eval_inner(args: self.ElementEvalArg, s: Sample):
398
+ if wp.static(self._qp_based):
399
+ qp_index = s.qp_index
400
+ return func(qp_index, *args.eval_arg)
401
+ else:
402
+ pos = self.domain.element_position(args.elt_arg, s)
403
+ return func(pos, *args.eval_arg)
404
+
405
+ return eval_inner
406
+
407
+ def _make_eval_reference_grad(self):
408
+ if self.eval_grad_inner is None:
409
+ return None
410
+
411
+ @cache.dynamic_func(suffix=f"{self.eval_grad_inner.key}")
412
+ def eval_reference_grad_inner(args: self.ElementEvalArg, s: Sample):
413
+ return self.eval_grad_inner(args, s) * self.domain.element_deformation_gradient(args.elt_arg, s)
414
+
415
+ return eval_reference_grad_inner
416
+
417
+ def _make_element_eval_arg(self):
418
+ @cache.dynamic_struct(suffix=self.name)
419
+ class ElementEvalArg:
420
+ elt_arg: self.domain.ElementArg
421
+ eval_arg: self.EvalArg
422
+
423
+ return ElementEvalArg
424
+
425
+ def _make_eval_degree(self):
426
+ ORDER = wp.constant(self._degree)
427
+
428
+ @cache.dynamic_func(suffix=self.name)
429
+ def degree(args: self.ElementEvalArg):
430
+ return ORDER
431
+
432
+ return degree
433
+
434
+ def trace(self):
435
+ if self.element_kind == ElementKind.SIDE:
436
+ raise RuntimeError("Trace only available for field defined on cell elements")
437
+
438
+ return ImplicitField(
439
+ domain=Sides(self.domain.geometry_partition),
440
+ func=self._func,
441
+ values={name: getattr(self.values, name) for name in self.EvalArg.vars},
442
+ grad_func=self._grad_func,
443
+ div_func=self._div_func,
444
+ degree=self._degree,
445
+ )
446
+
447
+
448
+ class UniformField(GeometryField):
449
+ """Field defined as a constant value over a domain.
450
+
451
+ Args:
452
+ domain: Domain over which the field is defined
453
+ value: Uniform value over the domain
454
+ """
455
+
456
+ _dynamic_attribute_constructors: ClassVar = {
457
+ "EvalArg": lambda obj: obj._make_eval_arg(),
458
+ "ElementEvalArg": lambda obj: obj._make_element_eval_arg(),
459
+ "eval_degree": lambda obj: obj._make_eval_degree(),
460
+ "eval_inner": lambda obj: obj._make_eval_inner(),
461
+ "eval_grad_inner": lambda obj: obj._make_eval_zero(obj.gradient_dtype),
462
+ "eval_div_inner": lambda obj: obj._make_eval_zero(obj.divergence_dtype),
463
+ "eval_reference_grad_inner": lambda obj: obj._make_eval_zero(obj.reference_gradient_dtype),
464
+ "eval_outer": lambda obj: obj.eval_inner,
465
+ "eval_grad_outer": lambda obj: obj.eval_grad_inner,
466
+ "eval_div_outer": lambda obj: obj.eval_div_inner,
467
+ "eval_reference_grad_outer": lambda obj: obj.eval_reference_grad_inner,
468
+ }
469
+
470
+ def __init__(self, domain: GeometryDomain, value: Any):
471
+ self.domain = domain
472
+
473
+ if not is_value(value):
474
+ raise ValueError("value must be a Warp scalar, vector or matrix")
475
+
476
+ value_type = type_to_warp(type(value))
477
+ self._value = value_type(value)
478
+
479
+ cache.setup_dynamic_attributes(self)
480
+
481
+ @property
482
+ def value(self):
483
+ return self._value
484
+
485
+ @value.setter
486
+ def value(self, v):
487
+ value_type = type_to_warp(type(v))
488
+ assert types_equal(value_type, self.dtype)
489
+ self._value = self.dtype(v)
490
+
491
+ @property
492
+ def geometry(self) -> Geometry:
493
+ return self.domain.geometry
494
+
495
+ @property
496
+ def element_kind(self) -> ElementKind:
497
+ return self.domain.element_kind
498
+
499
+ @property
500
+ def dtype(self) -> type:
501
+ return type(self.value)
502
+
503
+ def fill_eval_arg(self, arg, device):
504
+ arg.value = self.value
505
+
506
+ @property
507
+ def degree(self) -> int:
508
+ return 0
509
+
510
+ def gradient_valid(self) -> bool:
511
+ return self.gradient_dtype is not None
512
+
513
+ def divergence_valid(self) -> bool:
514
+ return self.divergence_dtype is not None
515
+
516
+ @cached_property
517
+ def name(self) -> str:
518
+ return f"Uniform{self.domain.name}_{cache.pod_type_key(self.dtype)}"
519
+
520
+ def _make_eval_inner(self):
521
+ @cache.dynamic_func(suffix=self.name)
522
+ def eval_inner(args: self.ElementEvalArg, s: Sample):
523
+ return args.eval_arg.value
524
+
525
+ return eval_inner
526
+
527
+ def _make_eval_zero(self, dtype):
528
+ if dtype is None:
529
+ return None
530
+
531
+ zero_element = type_zero_element(dtype)
532
+
533
+ @cache.dynamic_func(suffix=f"{self.name}_{cache.pod_type_key(dtype)}")
534
+ def eval_zero(args: self.ElementEvalArg, s: Sample):
535
+ return zero_element()
536
+
537
+ return eval_zero
538
+
539
+ def _make_eval_arg(self):
540
+ @cache.dynamic_struct(suffix=self.name)
541
+ class EvalArg:
542
+ value: self.dtype
543
+
544
+ return EvalArg
545
+
546
+ def _make_element_eval_arg(self):
547
+ @cache.dynamic_struct(suffix=self.name)
548
+ class ElementEvalArg:
549
+ elt_arg: self.domain.ElementArg
550
+ eval_arg: self.EvalArg
551
+
552
+ return ElementEvalArg
553
+
554
+ def _make_eval_degree(self):
555
+ @cache.dynamic_func(suffix=self.name)
556
+ def degree(args: self.ElementEvalArg):
557
+ return 0
558
+
559
+ return degree
560
+
561
+ def trace(self):
562
+ if self.element_kind == ElementKind.SIDE:
563
+ raise RuntimeError("Trace only available for field defined on cell elements")
564
+
565
+ return UniformField(domain=Sides(self.domain.geometry_partition), value=self.value)
566
+
567
+
568
+ class NonconformingField(GeometryField):
569
+ """Field defined as the map of a DiscreteField over a non-conforming geometry.
570
+
571
+ Args:
572
+ domain: The new domain over which the nonconforming field will be evaluated
573
+ field: Nonconforming discrete field
574
+ background: Uniform value or domain-conforming field determining the value outside of the geometry of definition of `field`
575
+ """
576
+
577
+ _LOOKUP_EPS = wp.constant(1.0e-6)
578
+
579
+ _dynamic_attribute_constructors: ClassVar = {
580
+ "EvalArg": lambda obj: obj._make_eval_arg(),
581
+ "ElementEvalArg": lambda obj: obj._make_element_eval_arg(),
582
+ "eval_degree": lambda obj: obj._make_eval_degree(),
583
+ "eval_inner": lambda obj: obj._make_nonconforming_eval("eval_inner"),
584
+ "eval_grad_inner": lambda obj: obj._make_nonconforming_eval("eval_grad_inner"),
585
+ "eval_div_inner": lambda obj: obj._make_nonconforming_eval("eval_div_inner"),
586
+ "eval_reference_grad_inner": lambda obj: obj._make_eval_reference_grad(),
587
+ "eval_outer": lambda obj: obj.eval_inner,
588
+ "eval_grad_outer": lambda obj: obj.eval_grad_inner,
589
+ "eval_div_outer": lambda obj: obj.eval_div_inner,
590
+ "eval_reference_grad_outer": lambda obj: obj.eval_reference_grad_inner,
591
+ }
592
+
593
+ def __init__(self, domain: GeometryDomain, field: DiscreteField, background: Any = 0.0):
594
+ self.domain = domain
595
+
596
+ self.field = field
597
+
598
+ if not isinstance(background, GeometryField):
599
+ background = UniformField(domain, self.dtype(background))
600
+ elif background.geometry != domain.geometry or background.element_kind != domain.element_kind:
601
+ raise ValueError("Background field must be conforming to the domain")
602
+ self.background = background
603
+
604
+ cache.setup_dynamic_attributes(self)
605
+
606
+ @property
607
+ def geometry(self) -> Geometry:
608
+ return self.domain.geometry
609
+
610
+ @property
611
+ def element_kind(self) -> ElementKind:
612
+ return self.domain.element_kind
613
+
614
+ @property
615
+ def dtype(self) -> type:
616
+ return self.field.dtype
617
+
618
+ def fill_eval_arg(self, arg, device):
619
+ self.field.fill_eval_arg(arg.field_cell_eval_arg.eval_arg, device)
620
+ self.field.geometry.fill_cell_arg(arg.field_cell_eval_arg.elt_arg, device)
621
+ self.background.fill_eval_arg(arg.background_arg, device)
622
+
623
+ @property
624
+ def degree(self) -> int:
625
+ return self.field.degree
626
+
627
+ def gradient_valid(self) -> bool:
628
+ return self.field.gradient_valid() and self.background.gradient_valid()
629
+
630
+ def divergence_valid(self) -> bool:
631
+ return self.field.divergence_valid() and self.background.divergence_valid()
632
+
633
+ @cached_property
634
+ def name(self) -> str:
635
+ return f"{self.domain.name}_{self.field.name}_{self.background.name}"
636
+
637
+ def _make_nonconforming_eval(self, eval_func_name):
638
+ field_eval = getattr(self.field, eval_func_name)
639
+ bg_eval = getattr(self.background, eval_func_name)
640
+
641
+ if field_eval is None or bg_eval is None:
642
+ return None
643
+
644
+ cell_lookup = self.field.geometry.cell_lookup
645
+
646
+ @cache.dynamic_func(suffix=f"{eval_func_name}_{self.name}")
647
+ def eval_nc(args: self.ElementEvalArg, s: Sample):
648
+ pos = self.domain.element_position(args.elt_arg, s)
649
+ cell_arg = args.eval_arg.field_cell_eval_arg.elt_arg
650
+ nonconforming_s = cell_lookup(cell_arg, pos, NonconformingField._LOOKUP_EPS)
651
+ if nonconforming_s.element_index != NULL_ELEMENT_INDEX:
652
+ if (
653
+ wp.length_sq(pos - self.field.geometry.cell_position(cell_arg, nonconforming_s))
654
+ <= NonconformingField._LOOKUP_EPS
655
+ ):
656
+ return field_eval(
657
+ self.field.ElementEvalArg(cell_arg, args.eval_arg.field_cell_eval_arg.eval_arg), nonconforming_s
658
+ )
659
+
660
+ return bg_eval(self.background.ElementEvalArg(args.elt_arg, args.eval_arg.background_arg), s)
661
+
662
+ return eval_nc
663
+
664
+ def _make_eval_reference_grad(self):
665
+ if self.eval_grad_inner is None:
666
+ return None
667
+
668
+ @cache.dynamic_func(suffix=f"{self.eval_grad_inner.key}")
669
+ def eval_reference_grad_inner(args: self.ElementEvalArg, s: Sample):
670
+ return self.eval_grad_inner(args, s) * self.domain.element_deformation_gradient(args.elt_arg, s)
671
+
672
+ return eval_reference_grad_inner
673
+
674
+ def _make_eval_arg(self):
675
+ @cache.dynamic_struct(suffix=self.name)
676
+ class EvalArg:
677
+ field_cell_eval_arg: self.field.ElementEvalArg
678
+ background_arg: self.background.EvalArg
679
+
680
+ return EvalArg
681
+
682
+ def _make_element_eval_arg(self):
683
+ @cache.dynamic_struct(suffix=self.name)
684
+ class ElementEvalArg:
685
+ elt_arg: self.domain.ElementArg
686
+ eval_arg: self.EvalArg
687
+
688
+ return ElementEvalArg
689
+
690
+ def _make_eval_degree(self):
691
+ @cache.dynamic_func(suffix=self.name)
692
+ def degree(args: self.ElementEvalArg):
693
+ return self.field.eval_degree(args.eval_arg.field_cell_eval_arg)
694
+
695
+ return degree
696
+
697
+ def trace(self):
698
+ if self.element_kind == ElementKind.SIDE:
699
+ raise RuntimeError("Trace only available for field defined on cell elements")
700
+
701
+ return NonconformingField(
702
+ domain=Sides(self.domain.geometry_partition), field=self.field, background=self.background.trace()
703
+ )