warp-lang 1.0.2__py3-none-macosx_10_13_universal2.whl → 1.1.0__py3-none-macosx_10_13_universal2.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 +108 -97
- warp/__init__.pyi +1 -1
- warp/bin/libwarp-clang.dylib +0 -0
- warp/bin/libwarp.dylib +0 -0
- warp/build.py +115 -113
- warp/build_dll.py +383 -375
- warp/builtins.py +3425 -3354
- warp/codegen.py +2878 -2792
- warp/config.py +40 -36
- warp/constants.py +45 -45
- warp/context.py +5194 -5102
- warp/dlpack.py +442 -442
- warp/examples/__init__.py +16 -16
- warp/examples/assets/bear.usd +0 -0
- warp/examples/assets/bunny.usd +0 -0
- warp/examples/assets/cartpole.urdf +110 -110
- warp/examples/assets/crazyflie.usd +0 -0
- warp/examples/assets/cube.usd +0 -0
- warp/examples/assets/nv_ant.xml +92 -92
- warp/examples/assets/nv_humanoid.xml +183 -183
- warp/examples/assets/quadruped.urdf +267 -267
- warp/examples/assets/rocks.nvdb +0 -0
- warp/examples/assets/rocks.usd +0 -0
- warp/examples/assets/sphere.usd +0 -0
- warp/examples/benchmarks/benchmark_api.py +383 -383
- warp/examples/benchmarks/benchmark_cloth.py +278 -277
- warp/examples/benchmarks/benchmark_cloth_cupy.py +88 -88
- warp/examples/benchmarks/benchmark_cloth_jax.py +97 -100
- warp/examples/benchmarks/benchmark_cloth_numba.py +146 -142
- warp/examples/benchmarks/benchmark_cloth_numpy.py +77 -77
- warp/examples/benchmarks/benchmark_cloth_pytorch.py +86 -86
- warp/examples/benchmarks/benchmark_cloth_taichi.py +112 -112
- warp/examples/benchmarks/benchmark_cloth_warp.py +146 -146
- warp/examples/benchmarks/benchmark_launches.py +295 -295
- warp/examples/browse.py +29 -29
- warp/examples/core/example_dem.py +234 -219
- warp/examples/core/example_fluid.py +293 -267
- warp/examples/core/example_graph_capture.py +144 -126
- warp/examples/core/example_marching_cubes.py +188 -174
- warp/examples/core/example_mesh.py +174 -155
- warp/examples/core/example_mesh_intersect.py +205 -193
- warp/examples/core/example_nvdb.py +176 -170
- warp/examples/core/example_raycast.py +105 -90
- warp/examples/core/example_raymarch.py +199 -178
- warp/examples/core/example_render_opengl.py +185 -141
- warp/examples/core/example_sph.py +405 -387
- warp/examples/core/example_torch.py +222 -181
- warp/examples/core/example_wave.py +263 -248
- warp/examples/fem/bsr_utils.py +378 -380
- warp/examples/fem/example_apic_fluid.py +407 -389
- warp/examples/fem/example_convection_diffusion.py +182 -168
- warp/examples/fem/example_convection_diffusion_dg.py +219 -209
- warp/examples/fem/example_convection_diffusion_dg0.py +204 -194
- warp/examples/fem/example_deformed_geometry.py +177 -159
- warp/examples/fem/example_diffusion.py +201 -173
- warp/examples/fem/example_diffusion_3d.py +177 -152
- warp/examples/fem/example_diffusion_mgpu.py +221 -214
- warp/examples/fem/example_mixed_elasticity.py +244 -222
- warp/examples/fem/example_navier_stokes.py +259 -243
- warp/examples/fem/example_stokes.py +220 -192
- warp/examples/fem/example_stokes_transfer.py +265 -249
- warp/examples/fem/mesh_utils.py +133 -109
- warp/examples/fem/plot_utils.py +292 -287
- warp/examples/optim/example_bounce.py +260 -246
- warp/examples/optim/example_cloth_throw.py +222 -209
- warp/examples/optim/example_diffray.py +566 -536
- warp/examples/optim/example_drone.py +864 -835
- warp/examples/optim/example_inverse_kinematics.py +176 -168
- warp/examples/optim/example_inverse_kinematics_torch.py +185 -169
- warp/examples/optim/example_spring_cage.py +239 -231
- warp/examples/optim/example_trajectory.py +223 -199
- warp/examples/optim/example_walker.py +306 -293
- warp/examples/sim/example_cartpole.py +139 -129
- warp/examples/sim/example_cloth.py +196 -186
- warp/examples/sim/example_granular.py +124 -111
- warp/examples/sim/example_granular_collision_sdf.py +197 -186
- warp/examples/sim/example_jacobian_ik.py +236 -214
- warp/examples/sim/example_particle_chain.py +118 -105
- warp/examples/sim/example_quadruped.py +193 -180
- warp/examples/sim/example_rigid_chain.py +197 -187
- warp/examples/sim/example_rigid_contact.py +189 -177
- warp/examples/sim/example_rigid_force.py +127 -125
- warp/examples/sim/example_rigid_gyroscopic.py +109 -95
- warp/examples/sim/example_rigid_soft_contact.py +134 -122
- warp/examples/sim/example_soft_body.py +190 -177
- warp/fabric.py +337 -335
- warp/fem/__init__.py +60 -27
- warp/fem/cache.py +401 -388
- warp/fem/dirichlet.py +178 -179
- warp/fem/domain.py +262 -263
- warp/fem/field/__init__.py +100 -101
- warp/fem/field/field.py +148 -149
- warp/fem/field/nodal_field.py +298 -299
- warp/fem/field/restriction.py +22 -21
- warp/fem/field/test.py +180 -181
- warp/fem/field/trial.py +183 -183
- warp/fem/geometry/__init__.py +15 -19
- warp/fem/geometry/closest_point.py +69 -70
- warp/fem/geometry/deformed_geometry.py +270 -271
- warp/fem/geometry/element.py +744 -744
- warp/fem/geometry/geometry.py +184 -186
- warp/fem/geometry/grid_2d.py +380 -373
- warp/fem/geometry/grid_3d.py +441 -435
- warp/fem/geometry/hexmesh.py +953 -953
- warp/fem/geometry/partition.py +374 -376
- warp/fem/geometry/quadmesh_2d.py +532 -532
- warp/fem/geometry/tetmesh.py +840 -840
- warp/fem/geometry/trimesh_2d.py +577 -577
- warp/fem/integrate.py +1630 -1615
- warp/fem/operator.py +190 -191
- warp/fem/polynomial.py +214 -213
- warp/fem/quadrature/__init__.py +2 -2
- warp/fem/quadrature/pic_quadrature.py +243 -245
- warp/fem/quadrature/quadrature.py +295 -294
- warp/fem/space/__init__.py +294 -292
- warp/fem/space/basis_space.py +488 -489
- warp/fem/space/collocated_function_space.py +100 -105
- warp/fem/space/dof_mapper.py +236 -236
- warp/fem/space/function_space.py +148 -145
- warp/fem/space/grid_2d_function_space.py +267 -267
- warp/fem/space/grid_3d_function_space.py +305 -306
- warp/fem/space/hexmesh_function_space.py +350 -352
- warp/fem/space/partition.py +350 -350
- warp/fem/space/quadmesh_2d_function_space.py +368 -369
- warp/fem/space/restriction.py +158 -160
- warp/fem/space/shape/__init__.py +13 -15
- warp/fem/space/shape/cube_shape_function.py +738 -738
- warp/fem/space/shape/shape_function.py +102 -103
- warp/fem/space/shape/square_shape_function.py +611 -611
- warp/fem/space/shape/tet_shape_function.py +565 -567
- warp/fem/space/shape/triangle_shape_function.py +429 -429
- warp/fem/space/tetmesh_function_space.py +294 -292
- warp/fem/space/topology.py +297 -295
- warp/fem/space/trimesh_2d_function_space.py +223 -221
- warp/fem/types.py +77 -77
- warp/fem/utils.py +495 -495
- warp/jax.py +166 -141
- warp/jax_experimental.py +341 -339
- warp/native/array.h +1072 -1025
- warp/native/builtin.h +1560 -1560
- warp/native/bvh.cpp +398 -398
- warp/native/bvh.cu +525 -525
- warp/native/bvh.h +429 -429
- warp/native/clang/clang.cpp +495 -464
- warp/native/crt.cpp +31 -31
- warp/native/crt.h +334 -334
- warp/native/cuda_crt.h +1049 -1049
- warp/native/cuda_util.cpp +549 -540
- warp/native/cuda_util.h +288 -203
- warp/native/cutlass_gemm.cpp +34 -34
- warp/native/cutlass_gemm.cu +372 -372
- warp/native/error.cpp +66 -66
- warp/native/error.h +27 -27
- warp/native/fabric.h +228 -228
- warp/native/hashgrid.cpp +301 -278
- warp/native/hashgrid.cu +78 -77
- warp/native/hashgrid.h +227 -227
- warp/native/initializer_array.h +32 -32
- warp/native/intersect.h +1204 -1204
- warp/native/intersect_adj.h +365 -365
- warp/native/intersect_tri.h +322 -322
- warp/native/marching.cpp +2 -2
- warp/native/marching.cu +497 -497
- warp/native/marching.h +2 -2
- warp/native/mat.h +1498 -1498
- warp/native/matnn.h +333 -333
- warp/native/mesh.cpp +203 -203
- warp/native/mesh.cu +293 -293
- warp/native/mesh.h +1887 -1887
- warp/native/nanovdb/NanoVDB.h +4782 -4782
- warp/native/nanovdb/PNanoVDB.h +2553 -2553
- warp/native/nanovdb/PNanoVDBWrite.h +294 -294
- warp/native/noise.h +850 -850
- warp/native/quat.h +1084 -1084
- warp/native/rand.h +299 -299
- warp/native/range.h +108 -108
- warp/native/reduce.cpp +156 -156
- warp/native/reduce.cu +348 -348
- warp/native/runlength_encode.cpp +61 -61
- warp/native/runlength_encode.cu +46 -46
- warp/native/scan.cpp +30 -30
- warp/native/scan.cu +36 -36
- warp/native/scan.h +7 -7
- warp/native/solid_angle.h +442 -442
- warp/native/sort.cpp +94 -94
- warp/native/sort.cu +97 -97
- warp/native/sort.h +14 -14
- warp/native/sparse.cpp +337 -337
- warp/native/sparse.cu +544 -544
- warp/native/spatial.h +630 -630
- warp/native/svd.h +562 -562
- warp/native/temp_buffer.h +30 -30
- warp/native/vec.h +1132 -1132
- warp/native/volume.cpp +297 -297
- warp/native/volume.cu +32 -32
- warp/native/volume.h +538 -538
- warp/native/volume_builder.cu +425 -425
- warp/native/volume_builder.h +19 -19
- warp/native/warp.cpp +1057 -1052
- warp/native/warp.cu +2943 -2828
- warp/native/warp.h +313 -305
- warp/optim/__init__.py +9 -9
- warp/optim/adam.py +120 -120
- warp/optim/linear.py +1104 -939
- warp/optim/sgd.py +104 -92
- warp/render/__init__.py +10 -10
- warp/render/render_opengl.py +3217 -3204
- warp/render/render_usd.py +768 -749
- warp/render/utils.py +152 -150
- warp/sim/__init__.py +52 -59
- warp/sim/articulation.py +685 -685
- warp/sim/collide.py +1594 -1590
- warp/sim/import_mjcf.py +489 -481
- warp/sim/import_snu.py +220 -221
- warp/sim/import_urdf.py +536 -516
- warp/sim/import_usd.py +887 -881
- warp/sim/inertia.py +316 -317
- warp/sim/integrator.py +234 -233
- warp/sim/integrator_euler.py +1956 -1956
- warp/sim/integrator_featherstone.py +1910 -1991
- warp/sim/integrator_xpbd.py +3294 -3312
- warp/sim/model.py +4473 -4314
- warp/sim/particles.py +113 -112
- warp/sim/render.py +417 -403
- warp/sim/utils.py +413 -410
- warp/sparse.py +1227 -1227
- warp/stubs.py +2109 -2469
- warp/tape.py +1162 -225
- warp/tests/__init__.py +1 -1
- warp/tests/__main__.py +4 -4
- warp/tests/assets/torus.usda +105 -105
- warp/tests/aux_test_class_kernel.py +26 -26
- warp/tests/aux_test_compile_consts_dummy.py +10 -10
- warp/tests/aux_test_conditional_unequal_types_kernels.py +21 -21
- warp/tests/aux_test_dependent.py +22 -22
- warp/tests/aux_test_grad_customs.py +23 -23
- warp/tests/aux_test_reference.py +11 -11
- warp/tests/aux_test_reference_reference.py +10 -10
- warp/tests/aux_test_square.py +17 -17
- warp/tests/aux_test_unresolved_func.py +14 -14
- warp/tests/aux_test_unresolved_symbol.py +14 -14
- warp/tests/disabled_kinematics.py +239 -239
- warp/tests/run_coverage_serial.py +31 -31
- warp/tests/test_adam.py +157 -157
- warp/tests/test_arithmetic.py +1124 -1124
- warp/tests/test_array.py +2417 -2326
- warp/tests/test_array_reduce.py +150 -150
- warp/tests/test_async.py +668 -656
- warp/tests/test_atomic.py +141 -141
- warp/tests/test_bool.py +204 -149
- warp/tests/test_builtins_resolution.py +1292 -1292
- warp/tests/test_bvh.py +164 -171
- warp/tests/test_closest_point_edge_edge.py +228 -228
- warp/tests/test_codegen.py +566 -553
- warp/tests/test_compile_consts.py +97 -101
- warp/tests/test_conditional.py +246 -246
- warp/tests/test_copy.py +232 -215
- warp/tests/test_ctypes.py +632 -632
- warp/tests/test_dense.py +67 -67
- warp/tests/test_devices.py +91 -98
- warp/tests/test_dlpack.py +530 -529
- warp/tests/test_examples.py +400 -378
- warp/tests/test_fabricarray.py +955 -955
- warp/tests/test_fast_math.py +62 -54
- warp/tests/test_fem.py +1277 -1278
- warp/tests/test_fp16.py +130 -130
- warp/tests/test_func.py +338 -337
- warp/tests/test_generics.py +571 -571
- warp/tests/test_grad.py +746 -640
- warp/tests/test_grad_customs.py +333 -336
- warp/tests/test_hash_grid.py +210 -164
- warp/tests/test_import.py +39 -39
- warp/tests/test_indexedarray.py +1134 -1134
- warp/tests/test_intersect.py +67 -67
- warp/tests/test_jax.py +307 -307
- warp/tests/test_large.py +167 -164
- warp/tests/test_launch.py +354 -354
- warp/tests/test_lerp.py +261 -261
- warp/tests/test_linear_solvers.py +191 -171
- warp/tests/test_lvalue.py +421 -493
- warp/tests/test_marching_cubes.py +65 -65
- warp/tests/test_mat.py +1801 -1827
- warp/tests/test_mat_lite.py +115 -115
- warp/tests/test_mat_scalar_ops.py +2907 -2889
- warp/tests/test_math.py +126 -193
- warp/tests/test_matmul.py +500 -499
- warp/tests/test_matmul_lite.py +410 -410
- warp/tests/test_mempool.py +188 -190
- warp/tests/test_mesh.py +284 -324
- warp/tests/test_mesh_query_aabb.py +228 -241
- warp/tests/test_mesh_query_point.py +692 -702
- warp/tests/test_mesh_query_ray.py +292 -303
- warp/tests/test_mlp.py +276 -276
- warp/tests/test_model.py +110 -110
- warp/tests/test_modules_lite.py +39 -39
- warp/tests/test_multigpu.py +163 -163
- warp/tests/test_noise.py +248 -248
- warp/tests/test_operators.py +250 -250
- warp/tests/test_options.py +123 -125
- warp/tests/test_peer.py +133 -137
- warp/tests/test_pinned.py +78 -78
- warp/tests/test_print.py +54 -54
- warp/tests/test_quat.py +2086 -2086
- warp/tests/test_rand.py +288 -288
- warp/tests/test_reload.py +217 -217
- warp/tests/test_rounding.py +179 -179
- warp/tests/test_runlength_encode.py +190 -190
- warp/tests/test_sim_grad.py +243 -0
- warp/tests/test_sim_kinematics.py +91 -97
- warp/tests/test_smoothstep.py +168 -168
- warp/tests/test_snippet.py +305 -266
- warp/tests/test_sparse.py +468 -460
- warp/tests/test_spatial.py +2148 -2148
- warp/tests/test_streams.py +486 -473
- warp/tests/test_struct.py +710 -675
- warp/tests/test_tape.py +173 -148
- warp/tests/test_torch.py +743 -743
- warp/tests/test_transient_module.py +87 -87
- warp/tests/test_types.py +556 -659
- warp/tests/test_utils.py +490 -499
- warp/tests/test_vec.py +1264 -1268
- warp/tests/test_vec_lite.py +73 -73
- warp/tests/test_vec_scalar_ops.py +2099 -2099
- warp/tests/test_verify_fp.py +94 -94
- warp/tests/test_volume.py +737 -736
- warp/tests/test_volume_write.py +255 -265
- warp/tests/unittest_serial.py +37 -37
- warp/tests/unittest_suites.py +363 -359
- warp/tests/unittest_utils.py +603 -578
- warp/tests/unused_test_misc.py +71 -71
- warp/tests/walkthrough_debug.py +85 -85
- warp/thirdparty/appdirs.py +598 -598
- warp/thirdparty/dlpack.py +143 -143
- warp/thirdparty/unittest_parallel.py +566 -561
- warp/torch.py +321 -295
- warp/types.py +4504 -4450
- warp/utils.py +1008 -821
- {warp_lang-1.0.2.dist-info → warp_lang-1.1.0.dist-info}/LICENSE.md +126 -126
- {warp_lang-1.0.2.dist-info → warp_lang-1.1.0.dist-info}/METADATA +338 -400
- warp_lang-1.1.0.dist-info/RECORD +352 -0
- warp/examples/assets/cube.usda +0 -42
- warp/examples/assets/sphere.usda +0 -56
- warp/examples/assets/torus.usda +0 -105
- warp_lang-1.0.2.dist-info/RECORD +0 -352
- {warp_lang-1.0.2.dist-info → warp_lang-1.1.0.dist-info}/WHEEL +0 -0
- {warp_lang-1.0.2.dist-info → warp_lang-1.1.0.dist-info}/top_level.txt +0 -0
|
@@ -1,738 +1,738 @@
|
|
|
1
|
-
import math
|
|
2
|
-
|
|
3
|
-
import
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
from warp.fem
|
|
7
|
-
from warp.fem.
|
|
8
|
-
from warp.fem.
|
|
9
|
-
from warp.fem import
|
|
10
|
-
|
|
11
|
-
from .tet_shape_function import TetrahedronPolynomialShapeFunctions
|
|
12
|
-
|
|
13
|
-
_CUBE_EDGE_INDICES = wp.constant(
|
|
14
|
-
wp.mat(shape=(3, 4), dtype=int)(
|
|
15
|
-
[
|
|
16
|
-
[0, 4, 2, 6],
|
|
17
|
-
[3, 1, 7, 5],
|
|
18
|
-
[8, 11, 9, 10],
|
|
19
|
-
]
|
|
20
|
-
)
|
|
21
|
-
)
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
class CubeTripolynomialShapeFunctions:
|
|
25
|
-
VERTEX = 0
|
|
26
|
-
EDGE = 1
|
|
27
|
-
FACE = 2
|
|
28
|
-
INTERIOR = 3
|
|
29
|
-
|
|
30
|
-
def __init__(self, degree: int, family: Polynomial):
|
|
31
|
-
self.family = family
|
|
32
|
-
|
|
33
|
-
self.ORDER = wp.constant(degree)
|
|
34
|
-
self.NODES_PER_ELEMENT = wp.constant((degree + 1) ** 3)
|
|
35
|
-
self.NODES_PER_EDGE = wp.constant(degree + 1)
|
|
36
|
-
|
|
37
|
-
lobatto_coords, lobatto_weight = quadrature_1d(point_count=degree + 1, family=family)
|
|
38
|
-
lagrange_scale = lagrange_scales(lobatto_coords)
|
|
39
|
-
|
|
40
|
-
NodeVec = wp.types.vector(length=degree + 1, dtype=wp.float32)
|
|
41
|
-
self.LOBATTO_COORDS = wp.constant(NodeVec(lobatto_coords))
|
|
42
|
-
self.LOBATTO_WEIGHT = wp.constant(NodeVec(lobatto_weight))
|
|
43
|
-
self.LAGRANGE_SCALE = wp.constant(NodeVec(lagrange_scale))
|
|
44
|
-
self.ORDER_PLUS_ONE = wp.constant(self.ORDER + 1)
|
|
45
|
-
|
|
46
|
-
self._node_ijk = self._make_node_ijk()
|
|
47
|
-
self.node_type_and_type_index = self._make_node_type_and_type_index()
|
|
48
|
-
|
|
49
|
-
@property
|
|
50
|
-
def name(self) -> str:
|
|
51
|
-
return f"Cube_Q{self.ORDER}_{self.family}"
|
|
52
|
-
|
|
53
|
-
@wp.func
|
|
54
|
-
def _vertex_coords_f(vidx_in_cell: int):
|
|
55
|
-
x = vidx_in_cell // 4
|
|
56
|
-
y = (vidx_in_cell - 4 * x) // 2
|
|
57
|
-
z = vidx_in_cell - 4 * x - 2 * y
|
|
58
|
-
return wp.vec3(float(x), float(y), float(z))
|
|
59
|
-
|
|
60
|
-
def _make_node_ijk(self):
|
|
61
|
-
ORDER_PLUS_ONE = self.ORDER_PLUS_ONE
|
|
62
|
-
|
|
63
|
-
def node_ijk(
|
|
64
|
-
node_index_in_elt: int,
|
|
65
|
-
):
|
|
66
|
-
node_i = node_index_in_elt // (ORDER_PLUS_ONE * ORDER_PLUS_ONE)
|
|
67
|
-
node_jk = node_index_in_elt - ORDER_PLUS_ONE * ORDER_PLUS_ONE * node_i
|
|
68
|
-
node_j = node_jk // ORDER_PLUS_ONE
|
|
69
|
-
node_k = node_jk - ORDER_PLUS_ONE * node_j
|
|
70
|
-
return node_i, node_j, node_k
|
|
71
|
-
|
|
72
|
-
return cache.get_func(node_ijk, self.name)
|
|
73
|
-
|
|
74
|
-
def _make_node_type_and_type_index(self):
|
|
75
|
-
ORDER = self.ORDER
|
|
76
|
-
|
|
77
|
-
@cache.dynamic_func(suffix=self.name)
|
|
78
|
-
def node_type_and_type_index(
|
|
79
|
-
node_index_in_elt: int,
|
|
80
|
-
):
|
|
81
|
-
i, j, k = self._node_ijk(node_index_in_elt)
|
|
82
|
-
|
|
83
|
-
zi = wp.select(i == 0, 0, 1)
|
|
84
|
-
zj = wp.select(j == 0, 0, 1)
|
|
85
|
-
zk = wp.select(k == 0, 0, 1)
|
|
86
|
-
|
|
87
|
-
mi = wp.select(i == ORDER, 0, 1)
|
|
88
|
-
mj = wp.select(j == ORDER, 0, 1)
|
|
89
|
-
mk = wp.select(k == ORDER, 0, 1)
|
|
90
|
-
|
|
91
|
-
if zi + mi == 1:
|
|
92
|
-
if zj + mj == 1:
|
|
93
|
-
if zk + mk == 1:
|
|
94
|
-
# vertex
|
|
95
|
-
type_instance = mi * 4 + mj * 2 + mk
|
|
96
|
-
return CubeTripolynomialShapeFunctions.VERTEX, type_instance, 0
|
|
97
|
-
|
|
98
|
-
# z edge
|
|
99
|
-
type_instance = _CUBE_EDGE_INDICES[2, mi * 2 + mj]
|
|
100
|
-
type_index = k - 1
|
|
101
|
-
return CubeTripolynomialShapeFunctions.EDGE, type_instance, type_index
|
|
102
|
-
|
|
103
|
-
if zk + mk == 1:
|
|
104
|
-
# y edge
|
|
105
|
-
type_instance = _CUBE_EDGE_INDICES[1, mk * 2 + mi]
|
|
106
|
-
type_index = j - 1
|
|
107
|
-
return CubeTripolynomialShapeFunctions.EDGE, type_instance, type_index
|
|
108
|
-
|
|
109
|
-
# x face
|
|
110
|
-
type_instance = mi
|
|
111
|
-
type_index = wp.select(mi == 1, (j - 1) * (ORDER - 1) + k - 1, (k - 1) * (ORDER - 1) + j - 1)
|
|
112
|
-
return CubeTripolynomialShapeFunctions.FACE, type_instance, type_index
|
|
113
|
-
|
|
114
|
-
if zj + mj == 1:
|
|
115
|
-
if zk + mk == 1:
|
|
116
|
-
# x edge
|
|
117
|
-
type_instance = _CUBE_EDGE_INDICES[0, mj * 2 + mk]
|
|
118
|
-
type_index = i - 1
|
|
119
|
-
return CubeTripolynomialShapeFunctions.EDGE, type_instance, type_index
|
|
120
|
-
|
|
121
|
-
# y face
|
|
122
|
-
type_instance = 2 + mj
|
|
123
|
-
type_index = wp.select(mj == 1, (i - 1) * (ORDER - 1) + k - 1, (k - 1) * (ORDER - 1) + i - 1)
|
|
124
|
-
return CubeTripolynomialShapeFunctions.FACE, type_instance, type_index
|
|
125
|
-
|
|
126
|
-
if zk + mk == 1:
|
|
127
|
-
# z face
|
|
128
|
-
type_instance = 4 + mk
|
|
129
|
-
type_index = wp.select(mk == 1, (j - 1) * (ORDER - 1) + i - 1, (i - 1) * (ORDER - 1) + j - 1)
|
|
130
|
-
return CubeTripolynomialShapeFunctions.FACE, type_instance, type_index
|
|
131
|
-
|
|
132
|
-
type_index = ((i - 1) * (ORDER - 1) + (j - 1)) * (ORDER - 1) + k - 1
|
|
133
|
-
return CubeTripolynomialShapeFunctions.INTERIOR, 0, type_index
|
|
134
|
-
|
|
135
|
-
return node_type_and_type_index
|
|
136
|
-
|
|
137
|
-
def make_node_coords_in_element(self):
|
|
138
|
-
LOBATTO_COORDS = self.LOBATTO_COORDS
|
|
139
|
-
|
|
140
|
-
@cache.dynamic_func(suffix=self.name)
|
|
141
|
-
def node_coords_in_element(
|
|
142
|
-
node_index_in_elt: int,
|
|
143
|
-
):
|
|
144
|
-
node_i, node_j, node_k = self._node_ijk(node_index_in_elt)
|
|
145
|
-
return Coords(LOBATTO_COORDS[node_i], LOBATTO_COORDS[node_j], LOBATTO_COORDS[node_k])
|
|
146
|
-
|
|
147
|
-
return node_coords_in_element
|
|
148
|
-
|
|
149
|
-
def make_node_quadrature_weight(self):
|
|
150
|
-
ORDER = self.ORDER
|
|
151
|
-
LOBATTO_WEIGHT = self.LOBATTO_WEIGHT
|
|
152
|
-
|
|
153
|
-
def node_quadrature_weight(
|
|
154
|
-
node_index_in_elt: int,
|
|
155
|
-
):
|
|
156
|
-
node_i, node_j, node_k = self._node_ijk(node_index_in_elt)
|
|
157
|
-
return LOBATTO_WEIGHT[node_i] * LOBATTO_WEIGHT[node_j] * LOBATTO_WEIGHT[node_k]
|
|
158
|
-
|
|
159
|
-
def node_quadrature_weight_linear(
|
|
160
|
-
node_index_in_elt: int,
|
|
161
|
-
):
|
|
162
|
-
return 0.125
|
|
163
|
-
|
|
164
|
-
if ORDER == 1:
|
|
165
|
-
return cache.get_func(node_quadrature_weight_linear, self.name)
|
|
166
|
-
|
|
167
|
-
return cache.get_func(node_quadrature_weight, self.name)
|
|
168
|
-
|
|
169
|
-
def make_trace_node_quadrature_weight(self):
|
|
170
|
-
ORDER = self.ORDER
|
|
171
|
-
LOBATTO_WEIGHT = self.LOBATTO_WEIGHT
|
|
172
|
-
|
|
173
|
-
def trace_node_quadrature_weight(
|
|
174
|
-
node_index_in_elt: int,
|
|
175
|
-
):
|
|
176
|
-
# We're either on a side interior or at a vertex
|
|
177
|
-
# If we find one index at extremum, pick the two other
|
|
178
|
-
|
|
179
|
-
node_i, node_j, node_k = self._node_ijk(node_index_in_elt)
|
|
180
|
-
|
|
181
|
-
if node_i == 0 or node_i == ORDER:
|
|
182
|
-
return LOBATTO_WEIGHT[node_j] * LOBATTO_WEIGHT[node_k]
|
|
183
|
-
|
|
184
|
-
if node_j == 0 or node_j == ORDER:
|
|
185
|
-
return LOBATTO_WEIGHT[node_i] * LOBATTO_WEIGHT[node_k]
|
|
186
|
-
|
|
187
|
-
return LOBATTO_WEIGHT[node_i] * LOBATTO_WEIGHT[node_j]
|
|
188
|
-
|
|
189
|
-
def trace_node_quadrature_weight_linear(
|
|
190
|
-
node_index_in_elt: int,
|
|
191
|
-
):
|
|
192
|
-
return 0.25
|
|
193
|
-
|
|
194
|
-
def trace_node_quadrature_weight_open(
|
|
195
|
-
node_index_in_elt: int,
|
|
196
|
-
):
|
|
197
|
-
return 0.0
|
|
198
|
-
|
|
199
|
-
if not is_closed(self.family):
|
|
200
|
-
return cache.get_func(trace_node_quadrature_weight_open, self.name)
|
|
201
|
-
|
|
202
|
-
if ORDER == 1:
|
|
203
|
-
return cache.get_func(trace_node_quadrature_weight_linear, self.name)
|
|
204
|
-
|
|
205
|
-
return cache.get_func(trace_node_quadrature_weight, self.name)
|
|
206
|
-
|
|
207
|
-
def make_element_inner_weight(self):
|
|
208
|
-
ORDER_PLUS_ONE = self.ORDER_PLUS_ONE
|
|
209
|
-
LOBATTO_COORDS = self.LOBATTO_COORDS
|
|
210
|
-
LAGRANGE_SCALE = self.LAGRANGE_SCALE
|
|
211
|
-
|
|
212
|
-
def element_inner_weight(
|
|
213
|
-
coords: Coords,
|
|
214
|
-
node_index_in_elt: int,
|
|
215
|
-
):
|
|
216
|
-
node_i, node_j, node_k = self._node_ijk(node_index_in_elt)
|
|
217
|
-
|
|
218
|
-
w = float(1.0)
|
|
219
|
-
for k in range(ORDER_PLUS_ONE):
|
|
220
|
-
if k != node_i:
|
|
221
|
-
w *= coords[0] - LOBATTO_COORDS[k]
|
|
222
|
-
if k != node_j:
|
|
223
|
-
w *= coords[1] - LOBATTO_COORDS[k]
|
|
224
|
-
if k != node_k:
|
|
225
|
-
w *= coords[2] - LOBATTO_COORDS[k]
|
|
226
|
-
|
|
227
|
-
w *= LAGRANGE_SCALE[node_i] * LAGRANGE_SCALE[node_j] * LAGRANGE_SCALE[node_k]
|
|
228
|
-
|
|
229
|
-
return w
|
|
230
|
-
|
|
231
|
-
def element_inner_weight_linear(
|
|
232
|
-
coords: Coords,
|
|
233
|
-
node_index_in_elt: int,
|
|
234
|
-
):
|
|
235
|
-
v = CubeTripolynomialShapeFunctions._vertex_coords_f(node_index_in_elt)
|
|
236
|
-
|
|
237
|
-
wx = (1.0 - coords[0]) * (1.0 - v[0]) + v[0] * coords[0]
|
|
238
|
-
wy = (1.0 - coords[1]) * (1.0 - v[1]) + v[1] * coords[1]
|
|
239
|
-
wz = (1.0 - coords[2]) * (1.0 - v[2]) + v[2] * coords[2]
|
|
240
|
-
return wx * wy * wz
|
|
241
|
-
|
|
242
|
-
if self.ORDER == 1 and is_closed(self.family):
|
|
243
|
-
return cache.get_func(element_inner_weight_linear, self.name)
|
|
244
|
-
|
|
245
|
-
return cache.get_func(element_inner_weight, self.name)
|
|
246
|
-
|
|
247
|
-
def make_element_inner_weight_gradient(self):
|
|
248
|
-
ORDER_PLUS_ONE = self.ORDER_PLUS_ONE
|
|
249
|
-
LOBATTO_COORDS = self.LOBATTO_COORDS
|
|
250
|
-
LAGRANGE_SCALE = self.LAGRANGE_SCALE
|
|
251
|
-
|
|
252
|
-
def element_inner_weight_gradient(
|
|
253
|
-
coords: Coords,
|
|
254
|
-
node_index_in_elt: int,
|
|
255
|
-
):
|
|
256
|
-
node_i, node_j, node_k = self._node_ijk(node_index_in_elt)
|
|
257
|
-
|
|
258
|
-
prefix_xy = float(1.0)
|
|
259
|
-
prefix_yz = float(1.0)
|
|
260
|
-
prefix_zx = float(1.0)
|
|
261
|
-
for k in range(ORDER_PLUS_ONE):
|
|
262
|
-
if k != node_i:
|
|
263
|
-
prefix_yz *= coords[0] - LOBATTO_COORDS[k]
|
|
264
|
-
if k != node_j:
|
|
265
|
-
prefix_zx *= coords[1] - LOBATTO_COORDS[k]
|
|
266
|
-
if k != node_k:
|
|
267
|
-
prefix_xy *= coords[2] - LOBATTO_COORDS[k]
|
|
268
|
-
|
|
269
|
-
prefix_x = prefix_zx * prefix_xy
|
|
270
|
-
prefix_y = prefix_yz * prefix_xy
|
|
271
|
-
prefix_z = prefix_zx * prefix_yz
|
|
272
|
-
|
|
273
|
-
grad_x = float(0.0)
|
|
274
|
-
grad_y = float(0.0)
|
|
275
|
-
grad_z = float(0.0)
|
|
276
|
-
|
|
277
|
-
for k in range(ORDER_PLUS_ONE):
|
|
278
|
-
if k != node_i:
|
|
279
|
-
delta_x = coords[0] - LOBATTO_COORDS[k]
|
|
280
|
-
grad_x = grad_x * delta_x + prefix_x
|
|
281
|
-
prefix_x *= delta_x
|
|
282
|
-
if k != node_j:
|
|
283
|
-
delta_y = coords[1] - LOBATTO_COORDS[k]
|
|
284
|
-
grad_y = grad_y * delta_y + prefix_y
|
|
285
|
-
prefix_y *= delta_y
|
|
286
|
-
if k != node_k:
|
|
287
|
-
delta_z = coords[2] - LOBATTO_COORDS[k]
|
|
288
|
-
grad_z = grad_z * delta_z + prefix_z
|
|
289
|
-
prefix_z *= delta_z
|
|
290
|
-
|
|
291
|
-
grad = (
|
|
292
|
-
LAGRANGE_SCALE[node_i]
|
|
293
|
-
* LAGRANGE_SCALE[node_j]
|
|
294
|
-
* LAGRANGE_SCALE[node_k]
|
|
295
|
-
* wp.vec3(
|
|
296
|
-
grad_x,
|
|
297
|
-
grad_y,
|
|
298
|
-
grad_z,
|
|
299
|
-
)
|
|
300
|
-
)
|
|
301
|
-
|
|
302
|
-
return grad
|
|
303
|
-
|
|
304
|
-
def element_inner_weight_gradient_linear(
|
|
305
|
-
coords: Coords,
|
|
306
|
-
node_index_in_elt: int,
|
|
307
|
-
):
|
|
308
|
-
v = CubeTripolynomialShapeFunctions._vertex_coords_f(node_index_in_elt)
|
|
309
|
-
|
|
310
|
-
wx = (1.0 - coords[0]) * (1.0 - v[0]) + v[0] * coords[0]
|
|
311
|
-
wy = (1.0 - coords[1]) * (1.0 - v[1]) + v[1] * coords[1]
|
|
312
|
-
wz = (1.0 - coords[2]) * (1.0 - v[2]) + v[2] * coords[2]
|
|
313
|
-
|
|
314
|
-
dx = 2.0 * v[0] - 1.0
|
|
315
|
-
dy = 2.0 * v[1] - 1.0
|
|
316
|
-
dz = 2.0 * v[2] - 1.0
|
|
317
|
-
|
|
318
|
-
return wp.vec3(dx * wy * wz, dy * wz * wx, dz * wx * wy)
|
|
319
|
-
|
|
320
|
-
if self.ORDER == 1 and is_closed(self.family):
|
|
321
|
-
return cache.get_func(element_inner_weight_gradient_linear, self.name)
|
|
322
|
-
|
|
323
|
-
return cache.get_func(element_inner_weight_gradient, self.name)
|
|
324
|
-
|
|
325
|
-
def element_node_hexes(self):
|
|
326
|
-
from warp.fem.utils import grid_to_hexes
|
|
327
|
-
|
|
328
|
-
return grid_to_hexes(self.ORDER, self.ORDER, self.ORDER)
|
|
329
|
-
|
|
330
|
-
def element_node_tets(self):
|
|
331
|
-
from warp.fem.utils import grid_to_tets
|
|
332
|
-
|
|
333
|
-
return grid_to_tets(self.ORDER, self.ORDER, self.ORDER)
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
class CubeSerendipityShapeFunctions:
|
|
337
|
-
"""
|
|
338
|
-
Serendipity element ~ tensor product space without interior nodes
|
|
339
|
-
Edge shape functions are usual Lagrange shape functions times a bilinear function in the normal directions
|
|
340
|
-
Corner shape functions are trilinear shape functions times a function of (x^{d-1} + y^{d-1})
|
|
341
|
-
"""
|
|
342
|
-
|
|
343
|
-
# Node categories
|
|
344
|
-
VERTEX = wp.constant(0)
|
|
345
|
-
EDGE_X = wp.constant(1)
|
|
346
|
-
EDGE_Y = wp.constant(2)
|
|
347
|
-
|
|
348
|
-
def __init__(self, degree: int, family: Polynomial):
|
|
349
|
-
if not is_closed(family):
|
|
350
|
-
raise ValueError("A closed polynomial family is required to define serendipity elements")
|
|
351
|
-
|
|
352
|
-
if degree not in [2, 3]:
|
|
353
|
-
raise NotImplementedError("Serendipity element only implemented for order 2 or 3")
|
|
354
|
-
|
|
355
|
-
self.family = family
|
|
356
|
-
|
|
357
|
-
self.ORDER = wp.constant(degree)
|
|
358
|
-
self.NODES_PER_ELEMENT = wp.constant(8 + 12 * (degree - 1))
|
|
359
|
-
self.NODES_PER_EDGE = wp.constant(degree + 1)
|
|
360
|
-
|
|
361
|
-
lobatto_coords, lobatto_weight = quadrature_1d(point_count=degree + 1, family=family)
|
|
362
|
-
lagrange_scale = lagrange_scales(lobatto_coords)
|
|
363
|
-
|
|
364
|
-
NodeVec = wp.types.vector(length=degree + 1, dtype=wp.float32)
|
|
365
|
-
self.LOBATTO_COORDS = wp.constant(NodeVec(lobatto_coords))
|
|
366
|
-
self.LOBATTO_WEIGHT = wp.constant(NodeVec(lobatto_weight))
|
|
367
|
-
self.LAGRANGE_SCALE = wp.constant(NodeVec(lagrange_scale))
|
|
368
|
-
self.ORDER_PLUS_ONE = wp.constant(self.ORDER + 1)
|
|
369
|
-
|
|
370
|
-
self.node_type_and_type_index = self._get_node_type_and_type_index()
|
|
371
|
-
self._node_lobatto_indices = self._get_node_lobatto_indices()
|
|
372
|
-
|
|
373
|
-
@property
|
|
374
|
-
def name(self) -> str:
|
|
375
|
-
return f"Cube_S{self.ORDER}_{self.family}"
|
|
376
|
-
|
|
377
|
-
def _get_node_type_and_type_index(self):
|
|
378
|
-
@cache.dynamic_func(suffix=self.name)
|
|
379
|
-
def node_type_and_index(
|
|
380
|
-
node_index_in_elt: int,
|
|
381
|
-
):
|
|
382
|
-
if node_index_in_elt < 8:
|
|
383
|
-
return CubeSerendipityShapeFunctions.VERTEX, node_index_in_elt
|
|
384
|
-
|
|
385
|
-
type_index = (node_index_in_elt - 8) // 3
|
|
386
|
-
side = node_index_in_elt - 8 - 3 * type_index
|
|
387
|
-
return CubeSerendipityShapeFunctions.EDGE_X + side, type_index
|
|
388
|
-
|
|
389
|
-
return node_type_and_index
|
|
390
|
-
|
|
391
|
-
@wp.func
|
|
392
|
-
def _vertex_coords(vidx_in_cell: int):
|
|
393
|
-
x = vidx_in_cell // 4
|
|
394
|
-
y = (vidx_in_cell - 4 * x) // 2
|
|
395
|
-
z = vidx_in_cell - 4 * x - 2 * y
|
|
396
|
-
return wp.vec3i(x, y, z)
|
|
397
|
-
|
|
398
|
-
@wp.func
|
|
399
|
-
def _edge_coords(type_index: int):
|
|
400
|
-
index_in_side = type_index // 4
|
|
401
|
-
side_offset = type_index - 4 * index_in_side
|
|
402
|
-
return (wp.vec3i(index_in_side + 1, side_offset // 2, side_offset % 2),)
|
|
403
|
-
|
|
404
|
-
@wp.func
|
|
405
|
-
def _edge_axis(node_type: int):
|
|
406
|
-
return node_type - CubeSerendipityShapeFunctions.EDGE_X
|
|
407
|
-
|
|
408
|
-
@wp.func
|
|
409
|
-
def _cube_edge_index(node_type: int, type_index: int):
|
|
410
|
-
index_in_side = type_index // 4
|
|
411
|
-
side_offset = type_index - 4 * index_in_side
|
|
412
|
-
|
|
413
|
-
return _CUBE_EDGE_INDICES[node_type - CubeSerendipityShapeFunctions.EDGE_X, side_offset], index_in_side
|
|
414
|
-
|
|
415
|
-
def _get_node_lobatto_indices(self):
|
|
416
|
-
ORDER = self.ORDER
|
|
417
|
-
|
|
418
|
-
@cache.dynamic_func(suffix=self.name)
|
|
419
|
-
def node_lobatto_indices(node_type: int, type_index: int):
|
|
420
|
-
if node_type == CubeSerendipityShapeFunctions.VERTEX:
|
|
421
|
-
return CubeSerendipityShapeFunctions._vertex_coords(type_index) * ORDER
|
|
422
|
-
|
|
423
|
-
axis = CubeSerendipityShapeFunctions._edge_axis(node_type)
|
|
424
|
-
local_coords = CubeSerendipityShapeFunctions._edge_coords(type_index)
|
|
425
|
-
|
|
426
|
-
local_indices = wp.vec3i(local_coords[0], local_coords[1] * ORDER, local_coords[2] * ORDER)
|
|
427
|
-
|
|
428
|
-
return Grid3D._local_to_world(axis, local_indices)
|
|
429
|
-
|
|
430
|
-
return node_lobatto_indices
|
|
431
|
-
|
|
432
|
-
def make_node_coords_in_element(self):
|
|
433
|
-
LOBATTO_COORDS = self.LOBATTO_COORDS
|
|
434
|
-
|
|
435
|
-
@cache.dynamic_func(suffix=self.name)
|
|
436
|
-
def node_coords_in_element(
|
|
437
|
-
node_index_in_elt: int,
|
|
438
|
-
):
|
|
439
|
-
node_type, type_index = self.node_type_and_type_index(node_index_in_elt)
|
|
440
|
-
node_coords = self._node_lobatto_indices(node_type, type_index)
|
|
441
|
-
return Coords(
|
|
442
|
-
LOBATTO_COORDS[node_coords[0]], LOBATTO_COORDS[node_coords[1]], LOBATTO_COORDS[node_coords[2]]
|
|
443
|
-
)
|
|
444
|
-
|
|
445
|
-
return node_coords_in_element
|
|
446
|
-
|
|
447
|
-
def make_node_quadrature_weight(self):
|
|
448
|
-
ORDER = self.ORDER
|
|
449
|
-
|
|
450
|
-
@cache.dynamic_func(suffix=self.name)
|
|
451
|
-
def node_quadrature_weight(
|
|
452
|
-
node_index_in_elt: int,
|
|
453
|
-
):
|
|
454
|
-
node_type, type_index = self.node_type_and_type_index(node_index_in_elt)
|
|
455
|
-
if node_type == CubeSerendipityShapeFunctions.VERTEX:
|
|
456
|
-
return 1.0 / float(8 * ORDER * ORDER * ORDER)
|
|
457
|
-
|
|
458
|
-
return (1.0 - 1.0 / float(ORDER * ORDER * ORDER)) / float(12 * (ORDER - 1))
|
|
459
|
-
|
|
460
|
-
return node_quadrature_weight
|
|
461
|
-
|
|
462
|
-
def make_trace_node_quadrature_weight(self):
|
|
463
|
-
ORDER = self.ORDER
|
|
464
|
-
|
|
465
|
-
@cache.dynamic_func(suffix=self.name)
|
|
466
|
-
def trace_node_quadrature_weight(
|
|
467
|
-
node_index_in_elt: int,
|
|
468
|
-
):
|
|
469
|
-
node_type, type_index = self.node_type_and_type_index(node_index_in_elt)
|
|
470
|
-
if node_type == CubeSerendipityShapeFunctions.VERTEX:
|
|
471
|
-
return 0.25 / float(ORDER * ORDER)
|
|
472
|
-
|
|
473
|
-
return (0.25 - 0.25 / float(ORDER * ORDER)) / float(ORDER - 1)
|
|
474
|
-
|
|
475
|
-
return trace_node_quadrature_weight
|
|
476
|
-
|
|
477
|
-
def make_element_inner_weight(self):
|
|
478
|
-
ORDER = self.ORDER
|
|
479
|
-
ORDER_PLUS_ONE = self.ORDER_PLUS_ONE
|
|
480
|
-
|
|
481
|
-
LOBATTO_COORDS = self.LOBATTO_COORDS
|
|
482
|
-
LAGRANGE_SCALE = self.LAGRANGE_SCALE
|
|
483
|
-
|
|
484
|
-
DEGREE_3_SPHERE_RAD = wp.constant(2 * 0.5**2 + (0.5 - LOBATTO_COORDS[1]) ** 2)
|
|
485
|
-
DEGREE_3_SPHERE_SCALE = 1.0 / (0.75 - DEGREE_3_SPHERE_RAD)
|
|
486
|
-
|
|
487
|
-
@cache.dynamic_func(suffix=self.name)
|
|
488
|
-
def element_inner_weight(
|
|
489
|
-
coords: Coords,
|
|
490
|
-
node_index_in_elt: int,
|
|
491
|
-
):
|
|
492
|
-
node_type, type_index = self.node_type_and_type_index(node_index_in_elt)
|
|
493
|
-
|
|
494
|
-
if node_type == CubeSerendipityShapeFunctions.VERTEX:
|
|
495
|
-
node_ijk = CubeSerendipityShapeFunctions._vertex_coords(type_index)
|
|
496
|
-
|
|
497
|
-
cx = wp.select(node_ijk[0] == 0, coords[0], 1.0 - coords[0])
|
|
498
|
-
cy = wp.select(node_ijk[1] == 0, coords[1], 1.0 - coords[1])
|
|
499
|
-
cz = wp.select(node_ijk[2] == 0, coords[2], 1.0 - coords[2])
|
|
500
|
-
|
|
501
|
-
w = cx * cy * cz
|
|
502
|
-
|
|
503
|
-
if ORDER == 2:
|
|
504
|
-
w *= cx + cy + cz - 3.0 + LOBATTO_COORDS[1]
|
|
505
|
-
return w * LAGRANGE_SCALE[0]
|
|
506
|
-
if ORDER == 3:
|
|
507
|
-
w *= (
|
|
508
|
-
(cx - 0.5) * (cx - 0.5)
|
|
509
|
-
+ (cy - 0.5) * (cy - 0.5)
|
|
510
|
-
+ (cz - 0.5) * (cz - 0.5)
|
|
511
|
-
- DEGREE_3_SPHERE_RAD
|
|
512
|
-
)
|
|
513
|
-
return w * DEGREE_3_SPHERE_SCALE
|
|
514
|
-
|
|
515
|
-
axis = CubeSerendipityShapeFunctions._edge_axis(node_type)
|
|
516
|
-
|
|
517
|
-
node_all = CubeSerendipityShapeFunctions._edge_coords(type_index)
|
|
518
|
-
|
|
519
|
-
local_coords = Grid3D._world_to_local(axis, coords)
|
|
520
|
-
|
|
521
|
-
w = float(1.0)
|
|
522
|
-
w *= wp.select(node_all[1] == 0, local_coords[1], 1.0 - local_coords[1])
|
|
523
|
-
w *= wp.select(node_all[2] == 0, local_coords[2], 1.0 - local_coords[2])
|
|
524
|
-
|
|
525
|
-
for k in range(ORDER_PLUS_ONE):
|
|
526
|
-
if k != node_all[0]:
|
|
527
|
-
w *= local_coords[0] - LOBATTO_COORDS[k]
|
|
528
|
-
w *= LAGRANGE_SCALE[node_all[0]]
|
|
529
|
-
|
|
530
|
-
return w
|
|
531
|
-
|
|
532
|
-
return element_inner_weight
|
|
533
|
-
|
|
534
|
-
def make_element_inner_weight_gradient(self):
|
|
535
|
-
ORDER = self.ORDER
|
|
536
|
-
ORDER_PLUS_ONE = self.ORDER_PLUS_ONE
|
|
537
|
-
LOBATTO_COORDS = self.LOBATTO_COORDS
|
|
538
|
-
LAGRANGE_SCALE = self.LAGRANGE_SCALE
|
|
539
|
-
|
|
540
|
-
DEGREE_3_SPHERE_RAD = wp.constant(2 * 0.5**2 + (0.5 - LOBATTO_COORDS[1]) ** 2)
|
|
541
|
-
DEGREE_3_SPHERE_SCALE = 1.0 / (0.75 - DEGREE_3_SPHERE_RAD)
|
|
542
|
-
|
|
543
|
-
@cache.dynamic_func(suffix=self.name)
|
|
544
|
-
def element_inner_weight_gradient(
|
|
545
|
-
coords: Coords,
|
|
546
|
-
node_index_in_elt: int,
|
|
547
|
-
):
|
|
548
|
-
node_type, type_index = self.node_type_and_type_index(node_index_in_elt)
|
|
549
|
-
|
|
550
|
-
if node_type == CubeSerendipityShapeFunctions.VERTEX:
|
|
551
|
-
node_ijk = CubeSerendipityShapeFunctions._vertex_coords(type_index)
|
|
552
|
-
|
|
553
|
-
cx = wp.select(node_ijk[0] == 0, coords[0], 1.0 - coords[0])
|
|
554
|
-
cy = wp.select(node_ijk[1] == 0, coords[1], 1.0 - coords[1])
|
|
555
|
-
cz = wp.select(node_ijk[2] == 0, coords[2], 1.0 - coords[2])
|
|
556
|
-
|
|
557
|
-
gx = wp.select(node_ijk[0] == 0, 1.0, -1.0)
|
|
558
|
-
gy = wp.select(node_ijk[1] == 0, 1.0, -1.0)
|
|
559
|
-
gz = wp.select(node_ijk[2] == 0, 1.0, -1.0)
|
|
560
|
-
|
|
561
|
-
if ORDER == 2:
|
|
562
|
-
w = cx + cy + cz - 3.0 + LOBATTO_COORDS[1]
|
|
563
|
-
grad_x = cy * cz * gx * (w + cx)
|
|
564
|
-
grad_y = cz * cx * gy * (w + cy)
|
|
565
|
-
grad_z = cx * cy * gz * (w + cz)
|
|
566
|
-
|
|
567
|
-
return wp.vec3(grad_x, grad_y, grad_z) * LAGRANGE_SCALE[0]
|
|
568
|
-
|
|
569
|
-
if ORDER == 3:
|
|
570
|
-
w = (
|
|
571
|
-
(cx - 0.5) * (cx - 0.5)
|
|
572
|
-
+ (cy - 0.5) * (cy - 0.5)
|
|
573
|
-
+ (cz - 0.5) * (cz - 0.5)
|
|
574
|
-
- DEGREE_3_SPHERE_RAD
|
|
575
|
-
)
|
|
576
|
-
|
|
577
|
-
dw_dcx = 2.0 * cx - 1.0
|
|
578
|
-
dw_dcy = 2.0 * cy - 1.0
|
|
579
|
-
dw_dcz = 2.0 * cz - 1.0
|
|
580
|
-
grad_x = cy * cz * gx * (w + dw_dcx * cx)
|
|
581
|
-
grad_y = cz * cx * gy * (w + dw_dcy * cy)
|
|
582
|
-
grad_z = cx * cy * gz * (w + dw_dcz * cz)
|
|
583
|
-
|
|
584
|
-
return wp.vec3(grad_x, grad_y, grad_z) * DEGREE_3_SPHERE_SCALE
|
|
585
|
-
|
|
586
|
-
axis = CubeSerendipityShapeFunctions._edge_axis(node_type)
|
|
587
|
-
node_all = CubeSerendipityShapeFunctions._edge_coords(type_index)
|
|
588
|
-
|
|
589
|
-
local_coords = Grid3D._world_to_local(axis, coords)
|
|
590
|
-
|
|
591
|
-
w_long = wp.select(node_all[1] == 0, local_coords[1], 1.0 - local_coords[1])
|
|
592
|
-
w_lat = wp.select(node_all[2] == 0, local_coords[2], 1.0 - local_coords[2])
|
|
593
|
-
|
|
594
|
-
g_long = wp.select(node_all[1] == 0, 1.0, -1.0)
|
|
595
|
-
g_lat = wp.select(node_all[2] == 0, 1.0, -1.0)
|
|
596
|
-
|
|
597
|
-
w_alt = LAGRANGE_SCALE[node_all[0]]
|
|
598
|
-
g_alt = float(0.0)
|
|
599
|
-
prefix_alt = LAGRANGE_SCALE[node_all[0]]
|
|
600
|
-
for k in range(ORDER_PLUS_ONE):
|
|
601
|
-
if k != node_all[0]:
|
|
602
|
-
delta_alt = local_coords[0] - LOBATTO_COORDS[k]
|
|
603
|
-
w_alt *= delta_alt
|
|
604
|
-
g_alt = g_alt * delta_alt + prefix_alt
|
|
605
|
-
prefix_alt *= delta_alt
|
|
606
|
-
|
|
607
|
-
local_grad = wp.vec3(g_alt * w_long * w_lat, w_alt * g_long * w_lat, w_alt * w_long * g_lat)
|
|
608
|
-
|
|
609
|
-
return Grid3D._local_to_world(axis, local_grad)
|
|
610
|
-
|
|
611
|
-
return element_inner_weight_gradient
|
|
612
|
-
|
|
613
|
-
def element_node_tets(self):
|
|
614
|
-
from warp.fem.utils import grid_to_tets
|
|
615
|
-
|
|
616
|
-
if self.ORDER == 2:
|
|
617
|
-
element_tets = np.array(
|
|
618
|
-
[
|
|
619
|
-
[0, 8, 9, 10],
|
|
620
|
-
[1, 11, 10, 15],
|
|
621
|
-
[2, 9, 14, 13],
|
|
622
|
-
[3, 15, 13, 17],
|
|
623
|
-
[4, 12, 8, 16],
|
|
624
|
-
[5, 18, 16, 11],
|
|
625
|
-
[6, 14, 12, 19],
|
|
626
|
-
[7, 19, 18, 17],
|
|
627
|
-
[16, 12, 18, 11],
|
|
628
|
-
[8, 16, 12, 11],
|
|
629
|
-
[12, 19, 18, 14],
|
|
630
|
-
[14, 19, 17, 18],
|
|
631
|
-
[10, 9, 15, 8],
|
|
632
|
-
[10, 8, 11, 15],
|
|
633
|
-
[9, 13, 15, 14],
|
|
634
|
-
[13, 14, 17, 15],
|
|
635
|
-
]
|
|
636
|
-
)
|
|
637
|
-
|
|
638
|
-
middle_hex = np.array([8, 11, 9, 15, 12, 18, 14, 17])
|
|
639
|
-
middle_tets = middle_hex[grid_to_tets(1, 1, 1)]
|
|
640
|
-
|
|
641
|
-
return np.concatenate((element_tets, middle_tets))
|
|
642
|
-
|
|
643
|
-
raise NotImplementedError()
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
class CubeNonConformingPolynomialShapeFunctions:
|
|
647
|
-
# embeds the largest regular tet centered at (0.5, 0.5, 0.5) into the reference cube
|
|
648
|
-
|
|
649
|
-
_tet_height = 2.0 / 3.0
|
|
650
|
-
_tet_side = math.sqrt(3.0 / 2.0) * _tet_height
|
|
651
|
-
_tet_face_height = math.sqrt(3.0) / 2.0 * _tet_side
|
|
652
|
-
|
|
653
|
-
_tet_to_cube = np.array(
|
|
654
|
-
[
|
|
655
|
-
[_tet_side, _tet_side / 2.0, _tet_side / 2.0],
|
|
656
|
-
[0.0, _tet_face_height, _tet_face_height / 3.0],
|
|
657
|
-
[0.0, 0.0, _tet_height],
|
|
658
|
-
]
|
|
659
|
-
)
|
|
660
|
-
|
|
661
|
-
_TET_OFFSET = wp.constant(wp.vec3(0.5 - 0.5 * _tet_side, 0.5 - _tet_face_height / 3.0, 0.5 - 0.25 * _tet_height))
|
|
662
|
-
|
|
663
|
-
def __init__(self, degree: int):
|
|
664
|
-
self._tet_shape = TetrahedronPolynomialShapeFunctions(degree=degree)
|
|
665
|
-
self.ORDER = self._tet_shape.ORDER
|
|
666
|
-
self.NODES_PER_ELEMENT = self._tet_shape.NODES_PER_ELEMENT
|
|
667
|
-
|
|
668
|
-
self.element_node_tets = self._tet_shape.element_node_tets
|
|
669
|
-
|
|
670
|
-
@property
|
|
671
|
-
def name(self) -> str:
|
|
672
|
-
return f"Cube_P{self.ORDER}d"
|
|
673
|
-
|
|
674
|
-
def make_node_coords_in_element(self):
|
|
675
|
-
node_coords_in_tet = self._tet_shape.make_node_coords_in_element()
|
|
676
|
-
|
|
677
|
-
TET_TO_CUBE = wp.constant(wp.mat33(self._tet_to_cube))
|
|
678
|
-
|
|
679
|
-
@cache.dynamic_func(suffix=self.name)
|
|
680
|
-
def node_coords_in_element(
|
|
681
|
-
node_index_in_elt: int,
|
|
682
|
-
):
|
|
683
|
-
tet_coords = node_coords_in_tet(node_index_in_elt)
|
|
684
|
-
return TET_TO_CUBE * tet_coords + CubeNonConformingPolynomialShapeFunctions._TET_OFFSET
|
|
685
|
-
|
|
686
|
-
return node_coords_in_element
|
|
687
|
-
|
|
688
|
-
def make_node_quadrature_weight(self):
|
|
689
|
-
NODES_PER_ELEMENT = self.NODES_PER_ELEMENT
|
|
690
|
-
|
|
691
|
-
@cache.dynamic_func(suffix=self.name)
|
|
692
|
-
def node_uniform_quadrature_weight(
|
|
693
|
-
node_index_in_elt: int,
|
|
694
|
-
):
|
|
695
|
-
return 1.0 / float(NODES_PER_ELEMENT)
|
|
696
|
-
|
|
697
|
-
return node_uniform_quadrature_weight
|
|
698
|
-
|
|
699
|
-
def make_trace_node_quadrature_weight(self):
|
|
700
|
-
# Non-conforming, zero measure on sides
|
|
701
|
-
|
|
702
|
-
@wp.func
|
|
703
|
-
def zero(node_index_in_elt: int):
|
|
704
|
-
return 0.0
|
|
705
|
-
|
|
706
|
-
return zero
|
|
707
|
-
|
|
708
|
-
def make_element_inner_weight(self):
|
|
709
|
-
tet_inner_weight = self._tet_shape.make_element_inner_weight()
|
|
710
|
-
|
|
711
|
-
CUBE_TO_TET = wp.constant(wp.mat33(np.linalg.inv(self._tet_to_cube)))
|
|
712
|
-
|
|
713
|
-
@cache.dynamic_func(suffix=self.name)
|
|
714
|
-
def element_inner_weight(
|
|
715
|
-
coords: Coords,
|
|
716
|
-
node_index_in_elt: int,
|
|
717
|
-
):
|
|
718
|
-
tet_coords = CUBE_TO_TET * (coords - CubeNonConformingPolynomialShapeFunctions._TET_OFFSET)
|
|
719
|
-
|
|
720
|
-
return tet_inner_weight(tet_coords, node_index_in_elt)
|
|
721
|
-
|
|
722
|
-
return element_inner_weight
|
|
723
|
-
|
|
724
|
-
def make_element_inner_weight_gradient(self):
|
|
725
|
-
tet_inner_weight_gradient = self._tet_shape.make_element_inner_weight_gradient()
|
|
726
|
-
|
|
727
|
-
CUBE_TO_TET = wp.constant(wp.mat33(np.linalg.inv(self._tet_to_cube)))
|
|
728
|
-
|
|
729
|
-
@cache.dynamic_func(suffix=self.name)
|
|
730
|
-
def element_inner_weight_gradient(
|
|
731
|
-
coords: Coords,
|
|
732
|
-
node_index_in_elt: int,
|
|
733
|
-
):
|
|
734
|
-
tet_coords = CUBE_TO_TET * (coords - CubeNonConformingPolynomialShapeFunctions._TET_OFFSET)
|
|
735
|
-
grad = tet_inner_weight_gradient(tet_coords, node_index_in_elt)
|
|
736
|
-
return wp.transpose(CUBE_TO_TET) * grad
|
|
737
|
-
|
|
738
|
-
return element_inner_weight_gradient
|
|
1
|
+
import math
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
|
|
5
|
+
import warp as wp
|
|
6
|
+
from warp.fem import cache
|
|
7
|
+
from warp.fem.geometry import Grid3D
|
|
8
|
+
from warp.fem.polynomial import Polynomial, is_closed, lagrange_scales, quadrature_1d
|
|
9
|
+
from warp.fem.types import Coords
|
|
10
|
+
|
|
11
|
+
from .tet_shape_function import TetrahedronPolynomialShapeFunctions
|
|
12
|
+
|
|
13
|
+
_CUBE_EDGE_INDICES = wp.constant(
|
|
14
|
+
wp.mat(shape=(3, 4), dtype=int)(
|
|
15
|
+
[
|
|
16
|
+
[0, 4, 2, 6],
|
|
17
|
+
[3, 1, 7, 5],
|
|
18
|
+
[8, 11, 9, 10],
|
|
19
|
+
]
|
|
20
|
+
)
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class CubeTripolynomialShapeFunctions:
|
|
25
|
+
VERTEX = 0
|
|
26
|
+
EDGE = 1
|
|
27
|
+
FACE = 2
|
|
28
|
+
INTERIOR = 3
|
|
29
|
+
|
|
30
|
+
def __init__(self, degree: int, family: Polynomial):
|
|
31
|
+
self.family = family
|
|
32
|
+
|
|
33
|
+
self.ORDER = wp.constant(degree)
|
|
34
|
+
self.NODES_PER_ELEMENT = wp.constant((degree + 1) ** 3)
|
|
35
|
+
self.NODES_PER_EDGE = wp.constant(degree + 1)
|
|
36
|
+
|
|
37
|
+
lobatto_coords, lobatto_weight = quadrature_1d(point_count=degree + 1, family=family)
|
|
38
|
+
lagrange_scale = lagrange_scales(lobatto_coords)
|
|
39
|
+
|
|
40
|
+
NodeVec = wp.types.vector(length=degree + 1, dtype=wp.float32)
|
|
41
|
+
self.LOBATTO_COORDS = wp.constant(NodeVec(lobatto_coords))
|
|
42
|
+
self.LOBATTO_WEIGHT = wp.constant(NodeVec(lobatto_weight))
|
|
43
|
+
self.LAGRANGE_SCALE = wp.constant(NodeVec(lagrange_scale))
|
|
44
|
+
self.ORDER_PLUS_ONE = wp.constant(self.ORDER + 1)
|
|
45
|
+
|
|
46
|
+
self._node_ijk = self._make_node_ijk()
|
|
47
|
+
self.node_type_and_type_index = self._make_node_type_and_type_index()
|
|
48
|
+
|
|
49
|
+
@property
|
|
50
|
+
def name(self) -> str:
|
|
51
|
+
return f"Cube_Q{self.ORDER}_{self.family}"
|
|
52
|
+
|
|
53
|
+
@wp.func
|
|
54
|
+
def _vertex_coords_f(vidx_in_cell: int):
|
|
55
|
+
x = vidx_in_cell // 4
|
|
56
|
+
y = (vidx_in_cell - 4 * x) // 2
|
|
57
|
+
z = vidx_in_cell - 4 * x - 2 * y
|
|
58
|
+
return wp.vec3(float(x), float(y), float(z))
|
|
59
|
+
|
|
60
|
+
def _make_node_ijk(self):
|
|
61
|
+
ORDER_PLUS_ONE = self.ORDER_PLUS_ONE
|
|
62
|
+
|
|
63
|
+
def node_ijk(
|
|
64
|
+
node_index_in_elt: int,
|
|
65
|
+
):
|
|
66
|
+
node_i = node_index_in_elt // (ORDER_PLUS_ONE * ORDER_PLUS_ONE)
|
|
67
|
+
node_jk = node_index_in_elt - ORDER_PLUS_ONE * ORDER_PLUS_ONE * node_i
|
|
68
|
+
node_j = node_jk // ORDER_PLUS_ONE
|
|
69
|
+
node_k = node_jk - ORDER_PLUS_ONE * node_j
|
|
70
|
+
return node_i, node_j, node_k
|
|
71
|
+
|
|
72
|
+
return cache.get_func(node_ijk, self.name)
|
|
73
|
+
|
|
74
|
+
def _make_node_type_and_type_index(self):
|
|
75
|
+
ORDER = self.ORDER
|
|
76
|
+
|
|
77
|
+
@cache.dynamic_func(suffix=self.name)
|
|
78
|
+
def node_type_and_type_index(
|
|
79
|
+
node_index_in_elt: int,
|
|
80
|
+
):
|
|
81
|
+
i, j, k = self._node_ijk(node_index_in_elt)
|
|
82
|
+
|
|
83
|
+
zi = wp.select(i == 0, 0, 1)
|
|
84
|
+
zj = wp.select(j == 0, 0, 1)
|
|
85
|
+
zk = wp.select(k == 0, 0, 1)
|
|
86
|
+
|
|
87
|
+
mi = wp.select(i == ORDER, 0, 1)
|
|
88
|
+
mj = wp.select(j == ORDER, 0, 1)
|
|
89
|
+
mk = wp.select(k == ORDER, 0, 1)
|
|
90
|
+
|
|
91
|
+
if zi + mi == 1:
|
|
92
|
+
if zj + mj == 1:
|
|
93
|
+
if zk + mk == 1:
|
|
94
|
+
# vertex
|
|
95
|
+
type_instance = mi * 4 + mj * 2 + mk
|
|
96
|
+
return CubeTripolynomialShapeFunctions.VERTEX, type_instance, 0
|
|
97
|
+
|
|
98
|
+
# z edge
|
|
99
|
+
type_instance = _CUBE_EDGE_INDICES[2, mi * 2 + mj]
|
|
100
|
+
type_index = k - 1
|
|
101
|
+
return CubeTripolynomialShapeFunctions.EDGE, type_instance, type_index
|
|
102
|
+
|
|
103
|
+
if zk + mk == 1:
|
|
104
|
+
# y edge
|
|
105
|
+
type_instance = _CUBE_EDGE_INDICES[1, mk * 2 + mi]
|
|
106
|
+
type_index = j - 1
|
|
107
|
+
return CubeTripolynomialShapeFunctions.EDGE, type_instance, type_index
|
|
108
|
+
|
|
109
|
+
# x face
|
|
110
|
+
type_instance = mi
|
|
111
|
+
type_index = wp.select(mi == 1, (j - 1) * (ORDER - 1) + k - 1, (k - 1) * (ORDER - 1) + j - 1)
|
|
112
|
+
return CubeTripolynomialShapeFunctions.FACE, type_instance, type_index
|
|
113
|
+
|
|
114
|
+
if zj + mj == 1:
|
|
115
|
+
if zk + mk == 1:
|
|
116
|
+
# x edge
|
|
117
|
+
type_instance = _CUBE_EDGE_INDICES[0, mj * 2 + mk]
|
|
118
|
+
type_index = i - 1
|
|
119
|
+
return CubeTripolynomialShapeFunctions.EDGE, type_instance, type_index
|
|
120
|
+
|
|
121
|
+
# y face
|
|
122
|
+
type_instance = 2 + mj
|
|
123
|
+
type_index = wp.select(mj == 1, (i - 1) * (ORDER - 1) + k - 1, (k - 1) * (ORDER - 1) + i - 1)
|
|
124
|
+
return CubeTripolynomialShapeFunctions.FACE, type_instance, type_index
|
|
125
|
+
|
|
126
|
+
if zk + mk == 1:
|
|
127
|
+
# z face
|
|
128
|
+
type_instance = 4 + mk
|
|
129
|
+
type_index = wp.select(mk == 1, (j - 1) * (ORDER - 1) + i - 1, (i - 1) * (ORDER - 1) + j - 1)
|
|
130
|
+
return CubeTripolynomialShapeFunctions.FACE, type_instance, type_index
|
|
131
|
+
|
|
132
|
+
type_index = ((i - 1) * (ORDER - 1) + (j - 1)) * (ORDER - 1) + k - 1
|
|
133
|
+
return CubeTripolynomialShapeFunctions.INTERIOR, 0, type_index
|
|
134
|
+
|
|
135
|
+
return node_type_and_type_index
|
|
136
|
+
|
|
137
|
+
def make_node_coords_in_element(self):
|
|
138
|
+
LOBATTO_COORDS = self.LOBATTO_COORDS
|
|
139
|
+
|
|
140
|
+
@cache.dynamic_func(suffix=self.name)
|
|
141
|
+
def node_coords_in_element(
|
|
142
|
+
node_index_in_elt: int,
|
|
143
|
+
):
|
|
144
|
+
node_i, node_j, node_k = self._node_ijk(node_index_in_elt)
|
|
145
|
+
return Coords(LOBATTO_COORDS[node_i], LOBATTO_COORDS[node_j], LOBATTO_COORDS[node_k])
|
|
146
|
+
|
|
147
|
+
return node_coords_in_element
|
|
148
|
+
|
|
149
|
+
def make_node_quadrature_weight(self):
|
|
150
|
+
ORDER = self.ORDER
|
|
151
|
+
LOBATTO_WEIGHT = self.LOBATTO_WEIGHT
|
|
152
|
+
|
|
153
|
+
def node_quadrature_weight(
|
|
154
|
+
node_index_in_elt: int,
|
|
155
|
+
):
|
|
156
|
+
node_i, node_j, node_k = self._node_ijk(node_index_in_elt)
|
|
157
|
+
return LOBATTO_WEIGHT[node_i] * LOBATTO_WEIGHT[node_j] * LOBATTO_WEIGHT[node_k]
|
|
158
|
+
|
|
159
|
+
def node_quadrature_weight_linear(
|
|
160
|
+
node_index_in_elt: int,
|
|
161
|
+
):
|
|
162
|
+
return 0.125
|
|
163
|
+
|
|
164
|
+
if ORDER == 1:
|
|
165
|
+
return cache.get_func(node_quadrature_weight_linear, self.name)
|
|
166
|
+
|
|
167
|
+
return cache.get_func(node_quadrature_weight, self.name)
|
|
168
|
+
|
|
169
|
+
def make_trace_node_quadrature_weight(self):
|
|
170
|
+
ORDER = self.ORDER
|
|
171
|
+
LOBATTO_WEIGHT = self.LOBATTO_WEIGHT
|
|
172
|
+
|
|
173
|
+
def trace_node_quadrature_weight(
|
|
174
|
+
node_index_in_elt: int,
|
|
175
|
+
):
|
|
176
|
+
# We're either on a side interior or at a vertex
|
|
177
|
+
# If we find one index at extremum, pick the two other
|
|
178
|
+
|
|
179
|
+
node_i, node_j, node_k = self._node_ijk(node_index_in_elt)
|
|
180
|
+
|
|
181
|
+
if node_i == 0 or node_i == ORDER:
|
|
182
|
+
return LOBATTO_WEIGHT[node_j] * LOBATTO_WEIGHT[node_k]
|
|
183
|
+
|
|
184
|
+
if node_j == 0 or node_j == ORDER:
|
|
185
|
+
return LOBATTO_WEIGHT[node_i] * LOBATTO_WEIGHT[node_k]
|
|
186
|
+
|
|
187
|
+
return LOBATTO_WEIGHT[node_i] * LOBATTO_WEIGHT[node_j]
|
|
188
|
+
|
|
189
|
+
def trace_node_quadrature_weight_linear(
|
|
190
|
+
node_index_in_elt: int,
|
|
191
|
+
):
|
|
192
|
+
return 0.25
|
|
193
|
+
|
|
194
|
+
def trace_node_quadrature_weight_open(
|
|
195
|
+
node_index_in_elt: int,
|
|
196
|
+
):
|
|
197
|
+
return 0.0
|
|
198
|
+
|
|
199
|
+
if not is_closed(self.family):
|
|
200
|
+
return cache.get_func(trace_node_quadrature_weight_open, self.name)
|
|
201
|
+
|
|
202
|
+
if ORDER == 1:
|
|
203
|
+
return cache.get_func(trace_node_quadrature_weight_linear, self.name)
|
|
204
|
+
|
|
205
|
+
return cache.get_func(trace_node_quadrature_weight, self.name)
|
|
206
|
+
|
|
207
|
+
def make_element_inner_weight(self):
|
|
208
|
+
ORDER_PLUS_ONE = self.ORDER_PLUS_ONE
|
|
209
|
+
LOBATTO_COORDS = self.LOBATTO_COORDS
|
|
210
|
+
LAGRANGE_SCALE = self.LAGRANGE_SCALE
|
|
211
|
+
|
|
212
|
+
def element_inner_weight(
|
|
213
|
+
coords: Coords,
|
|
214
|
+
node_index_in_elt: int,
|
|
215
|
+
):
|
|
216
|
+
node_i, node_j, node_k = self._node_ijk(node_index_in_elt)
|
|
217
|
+
|
|
218
|
+
w = float(1.0)
|
|
219
|
+
for k in range(ORDER_PLUS_ONE):
|
|
220
|
+
if k != node_i:
|
|
221
|
+
w *= coords[0] - LOBATTO_COORDS[k]
|
|
222
|
+
if k != node_j:
|
|
223
|
+
w *= coords[1] - LOBATTO_COORDS[k]
|
|
224
|
+
if k != node_k:
|
|
225
|
+
w *= coords[2] - LOBATTO_COORDS[k]
|
|
226
|
+
|
|
227
|
+
w *= LAGRANGE_SCALE[node_i] * LAGRANGE_SCALE[node_j] * LAGRANGE_SCALE[node_k]
|
|
228
|
+
|
|
229
|
+
return w
|
|
230
|
+
|
|
231
|
+
def element_inner_weight_linear(
|
|
232
|
+
coords: Coords,
|
|
233
|
+
node_index_in_elt: int,
|
|
234
|
+
):
|
|
235
|
+
v = CubeTripolynomialShapeFunctions._vertex_coords_f(node_index_in_elt)
|
|
236
|
+
|
|
237
|
+
wx = (1.0 - coords[0]) * (1.0 - v[0]) + v[0] * coords[0]
|
|
238
|
+
wy = (1.0 - coords[1]) * (1.0 - v[1]) + v[1] * coords[1]
|
|
239
|
+
wz = (1.0 - coords[2]) * (1.0 - v[2]) + v[2] * coords[2]
|
|
240
|
+
return wx * wy * wz
|
|
241
|
+
|
|
242
|
+
if self.ORDER == 1 and is_closed(self.family):
|
|
243
|
+
return cache.get_func(element_inner_weight_linear, self.name)
|
|
244
|
+
|
|
245
|
+
return cache.get_func(element_inner_weight, self.name)
|
|
246
|
+
|
|
247
|
+
def make_element_inner_weight_gradient(self):
|
|
248
|
+
ORDER_PLUS_ONE = self.ORDER_PLUS_ONE
|
|
249
|
+
LOBATTO_COORDS = self.LOBATTO_COORDS
|
|
250
|
+
LAGRANGE_SCALE = self.LAGRANGE_SCALE
|
|
251
|
+
|
|
252
|
+
def element_inner_weight_gradient(
|
|
253
|
+
coords: Coords,
|
|
254
|
+
node_index_in_elt: int,
|
|
255
|
+
):
|
|
256
|
+
node_i, node_j, node_k = self._node_ijk(node_index_in_elt)
|
|
257
|
+
|
|
258
|
+
prefix_xy = float(1.0)
|
|
259
|
+
prefix_yz = float(1.0)
|
|
260
|
+
prefix_zx = float(1.0)
|
|
261
|
+
for k in range(ORDER_PLUS_ONE):
|
|
262
|
+
if k != node_i:
|
|
263
|
+
prefix_yz *= coords[0] - LOBATTO_COORDS[k]
|
|
264
|
+
if k != node_j:
|
|
265
|
+
prefix_zx *= coords[1] - LOBATTO_COORDS[k]
|
|
266
|
+
if k != node_k:
|
|
267
|
+
prefix_xy *= coords[2] - LOBATTO_COORDS[k]
|
|
268
|
+
|
|
269
|
+
prefix_x = prefix_zx * prefix_xy
|
|
270
|
+
prefix_y = prefix_yz * prefix_xy
|
|
271
|
+
prefix_z = prefix_zx * prefix_yz
|
|
272
|
+
|
|
273
|
+
grad_x = float(0.0)
|
|
274
|
+
grad_y = float(0.0)
|
|
275
|
+
grad_z = float(0.0)
|
|
276
|
+
|
|
277
|
+
for k in range(ORDER_PLUS_ONE):
|
|
278
|
+
if k != node_i:
|
|
279
|
+
delta_x = coords[0] - LOBATTO_COORDS[k]
|
|
280
|
+
grad_x = grad_x * delta_x + prefix_x
|
|
281
|
+
prefix_x *= delta_x
|
|
282
|
+
if k != node_j:
|
|
283
|
+
delta_y = coords[1] - LOBATTO_COORDS[k]
|
|
284
|
+
grad_y = grad_y * delta_y + prefix_y
|
|
285
|
+
prefix_y *= delta_y
|
|
286
|
+
if k != node_k:
|
|
287
|
+
delta_z = coords[2] - LOBATTO_COORDS[k]
|
|
288
|
+
grad_z = grad_z * delta_z + prefix_z
|
|
289
|
+
prefix_z *= delta_z
|
|
290
|
+
|
|
291
|
+
grad = (
|
|
292
|
+
LAGRANGE_SCALE[node_i]
|
|
293
|
+
* LAGRANGE_SCALE[node_j]
|
|
294
|
+
* LAGRANGE_SCALE[node_k]
|
|
295
|
+
* wp.vec3(
|
|
296
|
+
grad_x,
|
|
297
|
+
grad_y,
|
|
298
|
+
grad_z,
|
|
299
|
+
)
|
|
300
|
+
)
|
|
301
|
+
|
|
302
|
+
return grad
|
|
303
|
+
|
|
304
|
+
def element_inner_weight_gradient_linear(
|
|
305
|
+
coords: Coords,
|
|
306
|
+
node_index_in_elt: int,
|
|
307
|
+
):
|
|
308
|
+
v = CubeTripolynomialShapeFunctions._vertex_coords_f(node_index_in_elt)
|
|
309
|
+
|
|
310
|
+
wx = (1.0 - coords[0]) * (1.0 - v[0]) + v[0] * coords[0]
|
|
311
|
+
wy = (1.0 - coords[1]) * (1.0 - v[1]) + v[1] * coords[1]
|
|
312
|
+
wz = (1.0 - coords[2]) * (1.0 - v[2]) + v[2] * coords[2]
|
|
313
|
+
|
|
314
|
+
dx = 2.0 * v[0] - 1.0
|
|
315
|
+
dy = 2.0 * v[1] - 1.0
|
|
316
|
+
dz = 2.0 * v[2] - 1.0
|
|
317
|
+
|
|
318
|
+
return wp.vec3(dx * wy * wz, dy * wz * wx, dz * wx * wy)
|
|
319
|
+
|
|
320
|
+
if self.ORDER == 1 and is_closed(self.family):
|
|
321
|
+
return cache.get_func(element_inner_weight_gradient_linear, self.name)
|
|
322
|
+
|
|
323
|
+
return cache.get_func(element_inner_weight_gradient, self.name)
|
|
324
|
+
|
|
325
|
+
def element_node_hexes(self):
|
|
326
|
+
from warp.fem.utils import grid_to_hexes
|
|
327
|
+
|
|
328
|
+
return grid_to_hexes(self.ORDER, self.ORDER, self.ORDER)
|
|
329
|
+
|
|
330
|
+
def element_node_tets(self):
|
|
331
|
+
from warp.fem.utils import grid_to_tets
|
|
332
|
+
|
|
333
|
+
return grid_to_tets(self.ORDER, self.ORDER, self.ORDER)
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
class CubeSerendipityShapeFunctions:
|
|
337
|
+
"""
|
|
338
|
+
Serendipity element ~ tensor product space without interior nodes
|
|
339
|
+
Edge shape functions are usual Lagrange shape functions times a bilinear function in the normal directions
|
|
340
|
+
Corner shape functions are trilinear shape functions times a function of (x^{d-1} + y^{d-1})
|
|
341
|
+
"""
|
|
342
|
+
|
|
343
|
+
# Node categories
|
|
344
|
+
VERTEX = wp.constant(0)
|
|
345
|
+
EDGE_X = wp.constant(1)
|
|
346
|
+
EDGE_Y = wp.constant(2)
|
|
347
|
+
|
|
348
|
+
def __init__(self, degree: int, family: Polynomial):
|
|
349
|
+
if not is_closed(family):
|
|
350
|
+
raise ValueError("A closed polynomial family is required to define serendipity elements")
|
|
351
|
+
|
|
352
|
+
if degree not in [2, 3]:
|
|
353
|
+
raise NotImplementedError("Serendipity element only implemented for order 2 or 3")
|
|
354
|
+
|
|
355
|
+
self.family = family
|
|
356
|
+
|
|
357
|
+
self.ORDER = wp.constant(degree)
|
|
358
|
+
self.NODES_PER_ELEMENT = wp.constant(8 + 12 * (degree - 1))
|
|
359
|
+
self.NODES_PER_EDGE = wp.constant(degree + 1)
|
|
360
|
+
|
|
361
|
+
lobatto_coords, lobatto_weight = quadrature_1d(point_count=degree + 1, family=family)
|
|
362
|
+
lagrange_scale = lagrange_scales(lobatto_coords)
|
|
363
|
+
|
|
364
|
+
NodeVec = wp.types.vector(length=degree + 1, dtype=wp.float32)
|
|
365
|
+
self.LOBATTO_COORDS = wp.constant(NodeVec(lobatto_coords))
|
|
366
|
+
self.LOBATTO_WEIGHT = wp.constant(NodeVec(lobatto_weight))
|
|
367
|
+
self.LAGRANGE_SCALE = wp.constant(NodeVec(lagrange_scale))
|
|
368
|
+
self.ORDER_PLUS_ONE = wp.constant(self.ORDER + 1)
|
|
369
|
+
|
|
370
|
+
self.node_type_and_type_index = self._get_node_type_and_type_index()
|
|
371
|
+
self._node_lobatto_indices = self._get_node_lobatto_indices()
|
|
372
|
+
|
|
373
|
+
@property
|
|
374
|
+
def name(self) -> str:
|
|
375
|
+
return f"Cube_S{self.ORDER}_{self.family}"
|
|
376
|
+
|
|
377
|
+
def _get_node_type_and_type_index(self):
|
|
378
|
+
@cache.dynamic_func(suffix=self.name)
|
|
379
|
+
def node_type_and_index(
|
|
380
|
+
node_index_in_elt: int,
|
|
381
|
+
):
|
|
382
|
+
if node_index_in_elt < 8:
|
|
383
|
+
return CubeSerendipityShapeFunctions.VERTEX, node_index_in_elt
|
|
384
|
+
|
|
385
|
+
type_index = (node_index_in_elt - 8) // 3
|
|
386
|
+
side = node_index_in_elt - 8 - 3 * type_index
|
|
387
|
+
return CubeSerendipityShapeFunctions.EDGE_X + side, type_index
|
|
388
|
+
|
|
389
|
+
return node_type_and_index
|
|
390
|
+
|
|
391
|
+
@wp.func
|
|
392
|
+
def _vertex_coords(vidx_in_cell: int):
|
|
393
|
+
x = vidx_in_cell // 4
|
|
394
|
+
y = (vidx_in_cell - 4 * x) // 2
|
|
395
|
+
z = vidx_in_cell - 4 * x - 2 * y
|
|
396
|
+
return wp.vec3i(x, y, z)
|
|
397
|
+
|
|
398
|
+
@wp.func
|
|
399
|
+
def _edge_coords(type_index: int):
|
|
400
|
+
index_in_side = type_index // 4
|
|
401
|
+
side_offset = type_index - 4 * index_in_side
|
|
402
|
+
return (wp.vec3i(index_in_side + 1, side_offset // 2, side_offset % 2),)
|
|
403
|
+
|
|
404
|
+
@wp.func
|
|
405
|
+
def _edge_axis(node_type: int):
|
|
406
|
+
return node_type - CubeSerendipityShapeFunctions.EDGE_X
|
|
407
|
+
|
|
408
|
+
@wp.func
|
|
409
|
+
def _cube_edge_index(node_type: int, type_index: int):
|
|
410
|
+
index_in_side = type_index // 4
|
|
411
|
+
side_offset = type_index - 4 * index_in_side
|
|
412
|
+
|
|
413
|
+
return _CUBE_EDGE_INDICES[node_type - CubeSerendipityShapeFunctions.EDGE_X, side_offset], index_in_side
|
|
414
|
+
|
|
415
|
+
def _get_node_lobatto_indices(self):
|
|
416
|
+
ORDER = self.ORDER
|
|
417
|
+
|
|
418
|
+
@cache.dynamic_func(suffix=self.name)
|
|
419
|
+
def node_lobatto_indices(node_type: int, type_index: int):
|
|
420
|
+
if node_type == CubeSerendipityShapeFunctions.VERTEX:
|
|
421
|
+
return CubeSerendipityShapeFunctions._vertex_coords(type_index) * ORDER
|
|
422
|
+
|
|
423
|
+
axis = CubeSerendipityShapeFunctions._edge_axis(node_type)
|
|
424
|
+
local_coords = CubeSerendipityShapeFunctions._edge_coords(type_index)
|
|
425
|
+
|
|
426
|
+
local_indices = wp.vec3i(local_coords[0], local_coords[1] * ORDER, local_coords[2] * ORDER)
|
|
427
|
+
|
|
428
|
+
return Grid3D._local_to_world(axis, local_indices)
|
|
429
|
+
|
|
430
|
+
return node_lobatto_indices
|
|
431
|
+
|
|
432
|
+
def make_node_coords_in_element(self):
|
|
433
|
+
LOBATTO_COORDS = self.LOBATTO_COORDS
|
|
434
|
+
|
|
435
|
+
@cache.dynamic_func(suffix=self.name)
|
|
436
|
+
def node_coords_in_element(
|
|
437
|
+
node_index_in_elt: int,
|
|
438
|
+
):
|
|
439
|
+
node_type, type_index = self.node_type_and_type_index(node_index_in_elt)
|
|
440
|
+
node_coords = self._node_lobatto_indices(node_type, type_index)
|
|
441
|
+
return Coords(
|
|
442
|
+
LOBATTO_COORDS[node_coords[0]], LOBATTO_COORDS[node_coords[1]], LOBATTO_COORDS[node_coords[2]]
|
|
443
|
+
)
|
|
444
|
+
|
|
445
|
+
return node_coords_in_element
|
|
446
|
+
|
|
447
|
+
def make_node_quadrature_weight(self):
|
|
448
|
+
ORDER = self.ORDER
|
|
449
|
+
|
|
450
|
+
@cache.dynamic_func(suffix=self.name)
|
|
451
|
+
def node_quadrature_weight(
|
|
452
|
+
node_index_in_elt: int,
|
|
453
|
+
):
|
|
454
|
+
node_type, type_index = self.node_type_and_type_index(node_index_in_elt)
|
|
455
|
+
if node_type == CubeSerendipityShapeFunctions.VERTEX:
|
|
456
|
+
return 1.0 / float(8 * ORDER * ORDER * ORDER)
|
|
457
|
+
|
|
458
|
+
return (1.0 - 1.0 / float(ORDER * ORDER * ORDER)) / float(12 * (ORDER - 1))
|
|
459
|
+
|
|
460
|
+
return node_quadrature_weight
|
|
461
|
+
|
|
462
|
+
def make_trace_node_quadrature_weight(self):
|
|
463
|
+
ORDER = self.ORDER
|
|
464
|
+
|
|
465
|
+
@cache.dynamic_func(suffix=self.name)
|
|
466
|
+
def trace_node_quadrature_weight(
|
|
467
|
+
node_index_in_elt: int,
|
|
468
|
+
):
|
|
469
|
+
node_type, type_index = self.node_type_and_type_index(node_index_in_elt)
|
|
470
|
+
if node_type == CubeSerendipityShapeFunctions.VERTEX:
|
|
471
|
+
return 0.25 / float(ORDER * ORDER)
|
|
472
|
+
|
|
473
|
+
return (0.25 - 0.25 / float(ORDER * ORDER)) / float(ORDER - 1)
|
|
474
|
+
|
|
475
|
+
return trace_node_quadrature_weight
|
|
476
|
+
|
|
477
|
+
def make_element_inner_weight(self):
|
|
478
|
+
ORDER = self.ORDER
|
|
479
|
+
ORDER_PLUS_ONE = self.ORDER_PLUS_ONE
|
|
480
|
+
|
|
481
|
+
LOBATTO_COORDS = self.LOBATTO_COORDS
|
|
482
|
+
LAGRANGE_SCALE = self.LAGRANGE_SCALE
|
|
483
|
+
|
|
484
|
+
DEGREE_3_SPHERE_RAD = wp.constant(2 * 0.5**2 + (0.5 - LOBATTO_COORDS[1]) ** 2)
|
|
485
|
+
DEGREE_3_SPHERE_SCALE = 1.0 / (0.75 - DEGREE_3_SPHERE_RAD)
|
|
486
|
+
|
|
487
|
+
@cache.dynamic_func(suffix=self.name)
|
|
488
|
+
def element_inner_weight(
|
|
489
|
+
coords: Coords,
|
|
490
|
+
node_index_in_elt: int,
|
|
491
|
+
):
|
|
492
|
+
node_type, type_index = self.node_type_and_type_index(node_index_in_elt)
|
|
493
|
+
|
|
494
|
+
if node_type == CubeSerendipityShapeFunctions.VERTEX:
|
|
495
|
+
node_ijk = CubeSerendipityShapeFunctions._vertex_coords(type_index)
|
|
496
|
+
|
|
497
|
+
cx = wp.select(node_ijk[0] == 0, coords[0], 1.0 - coords[0])
|
|
498
|
+
cy = wp.select(node_ijk[1] == 0, coords[1], 1.0 - coords[1])
|
|
499
|
+
cz = wp.select(node_ijk[2] == 0, coords[2], 1.0 - coords[2])
|
|
500
|
+
|
|
501
|
+
w = cx * cy * cz
|
|
502
|
+
|
|
503
|
+
if ORDER == 2:
|
|
504
|
+
w *= cx + cy + cz - 3.0 + LOBATTO_COORDS[1]
|
|
505
|
+
return w * LAGRANGE_SCALE[0]
|
|
506
|
+
if ORDER == 3:
|
|
507
|
+
w *= (
|
|
508
|
+
(cx - 0.5) * (cx - 0.5)
|
|
509
|
+
+ (cy - 0.5) * (cy - 0.5)
|
|
510
|
+
+ (cz - 0.5) * (cz - 0.5)
|
|
511
|
+
- DEGREE_3_SPHERE_RAD
|
|
512
|
+
)
|
|
513
|
+
return w * DEGREE_3_SPHERE_SCALE
|
|
514
|
+
|
|
515
|
+
axis = CubeSerendipityShapeFunctions._edge_axis(node_type)
|
|
516
|
+
|
|
517
|
+
node_all = CubeSerendipityShapeFunctions._edge_coords(type_index)
|
|
518
|
+
|
|
519
|
+
local_coords = Grid3D._world_to_local(axis, coords)
|
|
520
|
+
|
|
521
|
+
w = float(1.0)
|
|
522
|
+
w *= wp.select(node_all[1] == 0, local_coords[1], 1.0 - local_coords[1])
|
|
523
|
+
w *= wp.select(node_all[2] == 0, local_coords[2], 1.0 - local_coords[2])
|
|
524
|
+
|
|
525
|
+
for k in range(ORDER_PLUS_ONE):
|
|
526
|
+
if k != node_all[0]:
|
|
527
|
+
w *= local_coords[0] - LOBATTO_COORDS[k]
|
|
528
|
+
w *= LAGRANGE_SCALE[node_all[0]]
|
|
529
|
+
|
|
530
|
+
return w
|
|
531
|
+
|
|
532
|
+
return element_inner_weight
|
|
533
|
+
|
|
534
|
+
def make_element_inner_weight_gradient(self):
|
|
535
|
+
ORDER = self.ORDER
|
|
536
|
+
ORDER_PLUS_ONE = self.ORDER_PLUS_ONE
|
|
537
|
+
LOBATTO_COORDS = self.LOBATTO_COORDS
|
|
538
|
+
LAGRANGE_SCALE = self.LAGRANGE_SCALE
|
|
539
|
+
|
|
540
|
+
DEGREE_3_SPHERE_RAD = wp.constant(2 * 0.5**2 + (0.5 - LOBATTO_COORDS[1]) ** 2)
|
|
541
|
+
DEGREE_3_SPHERE_SCALE = 1.0 / (0.75 - DEGREE_3_SPHERE_RAD)
|
|
542
|
+
|
|
543
|
+
@cache.dynamic_func(suffix=self.name)
|
|
544
|
+
def element_inner_weight_gradient(
|
|
545
|
+
coords: Coords,
|
|
546
|
+
node_index_in_elt: int,
|
|
547
|
+
):
|
|
548
|
+
node_type, type_index = self.node_type_and_type_index(node_index_in_elt)
|
|
549
|
+
|
|
550
|
+
if node_type == CubeSerendipityShapeFunctions.VERTEX:
|
|
551
|
+
node_ijk = CubeSerendipityShapeFunctions._vertex_coords(type_index)
|
|
552
|
+
|
|
553
|
+
cx = wp.select(node_ijk[0] == 0, coords[0], 1.0 - coords[0])
|
|
554
|
+
cy = wp.select(node_ijk[1] == 0, coords[1], 1.0 - coords[1])
|
|
555
|
+
cz = wp.select(node_ijk[2] == 0, coords[2], 1.0 - coords[2])
|
|
556
|
+
|
|
557
|
+
gx = wp.select(node_ijk[0] == 0, 1.0, -1.0)
|
|
558
|
+
gy = wp.select(node_ijk[1] == 0, 1.0, -1.0)
|
|
559
|
+
gz = wp.select(node_ijk[2] == 0, 1.0, -1.0)
|
|
560
|
+
|
|
561
|
+
if ORDER == 2:
|
|
562
|
+
w = cx + cy + cz - 3.0 + LOBATTO_COORDS[1]
|
|
563
|
+
grad_x = cy * cz * gx * (w + cx)
|
|
564
|
+
grad_y = cz * cx * gy * (w + cy)
|
|
565
|
+
grad_z = cx * cy * gz * (w + cz)
|
|
566
|
+
|
|
567
|
+
return wp.vec3(grad_x, grad_y, grad_z) * LAGRANGE_SCALE[0]
|
|
568
|
+
|
|
569
|
+
if ORDER == 3:
|
|
570
|
+
w = (
|
|
571
|
+
(cx - 0.5) * (cx - 0.5)
|
|
572
|
+
+ (cy - 0.5) * (cy - 0.5)
|
|
573
|
+
+ (cz - 0.5) * (cz - 0.5)
|
|
574
|
+
- DEGREE_3_SPHERE_RAD
|
|
575
|
+
)
|
|
576
|
+
|
|
577
|
+
dw_dcx = 2.0 * cx - 1.0
|
|
578
|
+
dw_dcy = 2.0 * cy - 1.0
|
|
579
|
+
dw_dcz = 2.0 * cz - 1.0
|
|
580
|
+
grad_x = cy * cz * gx * (w + dw_dcx * cx)
|
|
581
|
+
grad_y = cz * cx * gy * (w + dw_dcy * cy)
|
|
582
|
+
grad_z = cx * cy * gz * (w + dw_dcz * cz)
|
|
583
|
+
|
|
584
|
+
return wp.vec3(grad_x, grad_y, grad_z) * DEGREE_3_SPHERE_SCALE
|
|
585
|
+
|
|
586
|
+
axis = CubeSerendipityShapeFunctions._edge_axis(node_type)
|
|
587
|
+
node_all = CubeSerendipityShapeFunctions._edge_coords(type_index)
|
|
588
|
+
|
|
589
|
+
local_coords = Grid3D._world_to_local(axis, coords)
|
|
590
|
+
|
|
591
|
+
w_long = wp.select(node_all[1] == 0, local_coords[1], 1.0 - local_coords[1])
|
|
592
|
+
w_lat = wp.select(node_all[2] == 0, local_coords[2], 1.0 - local_coords[2])
|
|
593
|
+
|
|
594
|
+
g_long = wp.select(node_all[1] == 0, 1.0, -1.0)
|
|
595
|
+
g_lat = wp.select(node_all[2] == 0, 1.0, -1.0)
|
|
596
|
+
|
|
597
|
+
w_alt = LAGRANGE_SCALE[node_all[0]]
|
|
598
|
+
g_alt = float(0.0)
|
|
599
|
+
prefix_alt = LAGRANGE_SCALE[node_all[0]]
|
|
600
|
+
for k in range(ORDER_PLUS_ONE):
|
|
601
|
+
if k != node_all[0]:
|
|
602
|
+
delta_alt = local_coords[0] - LOBATTO_COORDS[k]
|
|
603
|
+
w_alt *= delta_alt
|
|
604
|
+
g_alt = g_alt * delta_alt + prefix_alt
|
|
605
|
+
prefix_alt *= delta_alt
|
|
606
|
+
|
|
607
|
+
local_grad = wp.vec3(g_alt * w_long * w_lat, w_alt * g_long * w_lat, w_alt * w_long * g_lat)
|
|
608
|
+
|
|
609
|
+
return Grid3D._local_to_world(axis, local_grad)
|
|
610
|
+
|
|
611
|
+
return element_inner_weight_gradient
|
|
612
|
+
|
|
613
|
+
def element_node_tets(self):
|
|
614
|
+
from warp.fem.utils import grid_to_tets
|
|
615
|
+
|
|
616
|
+
if self.ORDER == 2:
|
|
617
|
+
element_tets = np.array(
|
|
618
|
+
[
|
|
619
|
+
[0, 8, 9, 10],
|
|
620
|
+
[1, 11, 10, 15],
|
|
621
|
+
[2, 9, 14, 13],
|
|
622
|
+
[3, 15, 13, 17],
|
|
623
|
+
[4, 12, 8, 16],
|
|
624
|
+
[5, 18, 16, 11],
|
|
625
|
+
[6, 14, 12, 19],
|
|
626
|
+
[7, 19, 18, 17],
|
|
627
|
+
[16, 12, 18, 11],
|
|
628
|
+
[8, 16, 12, 11],
|
|
629
|
+
[12, 19, 18, 14],
|
|
630
|
+
[14, 19, 17, 18],
|
|
631
|
+
[10, 9, 15, 8],
|
|
632
|
+
[10, 8, 11, 15],
|
|
633
|
+
[9, 13, 15, 14],
|
|
634
|
+
[13, 14, 17, 15],
|
|
635
|
+
]
|
|
636
|
+
)
|
|
637
|
+
|
|
638
|
+
middle_hex = np.array([8, 11, 9, 15, 12, 18, 14, 17])
|
|
639
|
+
middle_tets = middle_hex[grid_to_tets(1, 1, 1)]
|
|
640
|
+
|
|
641
|
+
return np.concatenate((element_tets, middle_tets))
|
|
642
|
+
|
|
643
|
+
raise NotImplementedError()
|
|
644
|
+
|
|
645
|
+
|
|
646
|
+
class CubeNonConformingPolynomialShapeFunctions:
|
|
647
|
+
# embeds the largest regular tet centered at (0.5, 0.5, 0.5) into the reference cube
|
|
648
|
+
|
|
649
|
+
_tet_height = 2.0 / 3.0
|
|
650
|
+
_tet_side = math.sqrt(3.0 / 2.0) * _tet_height
|
|
651
|
+
_tet_face_height = math.sqrt(3.0) / 2.0 * _tet_side
|
|
652
|
+
|
|
653
|
+
_tet_to_cube = np.array(
|
|
654
|
+
[
|
|
655
|
+
[_tet_side, _tet_side / 2.0, _tet_side / 2.0],
|
|
656
|
+
[0.0, _tet_face_height, _tet_face_height / 3.0],
|
|
657
|
+
[0.0, 0.0, _tet_height],
|
|
658
|
+
]
|
|
659
|
+
)
|
|
660
|
+
|
|
661
|
+
_TET_OFFSET = wp.constant(wp.vec3(0.5 - 0.5 * _tet_side, 0.5 - _tet_face_height / 3.0, 0.5 - 0.25 * _tet_height))
|
|
662
|
+
|
|
663
|
+
def __init__(self, degree: int):
|
|
664
|
+
self._tet_shape = TetrahedronPolynomialShapeFunctions(degree=degree)
|
|
665
|
+
self.ORDER = self._tet_shape.ORDER
|
|
666
|
+
self.NODES_PER_ELEMENT = self._tet_shape.NODES_PER_ELEMENT
|
|
667
|
+
|
|
668
|
+
self.element_node_tets = self._tet_shape.element_node_tets
|
|
669
|
+
|
|
670
|
+
@property
|
|
671
|
+
def name(self) -> str:
|
|
672
|
+
return f"Cube_P{self.ORDER}d"
|
|
673
|
+
|
|
674
|
+
def make_node_coords_in_element(self):
|
|
675
|
+
node_coords_in_tet = self._tet_shape.make_node_coords_in_element()
|
|
676
|
+
|
|
677
|
+
TET_TO_CUBE = wp.constant(wp.mat33(self._tet_to_cube))
|
|
678
|
+
|
|
679
|
+
@cache.dynamic_func(suffix=self.name)
|
|
680
|
+
def node_coords_in_element(
|
|
681
|
+
node_index_in_elt: int,
|
|
682
|
+
):
|
|
683
|
+
tet_coords = node_coords_in_tet(node_index_in_elt)
|
|
684
|
+
return TET_TO_CUBE * tet_coords + CubeNonConformingPolynomialShapeFunctions._TET_OFFSET
|
|
685
|
+
|
|
686
|
+
return node_coords_in_element
|
|
687
|
+
|
|
688
|
+
def make_node_quadrature_weight(self):
|
|
689
|
+
NODES_PER_ELEMENT = self.NODES_PER_ELEMENT
|
|
690
|
+
|
|
691
|
+
@cache.dynamic_func(suffix=self.name)
|
|
692
|
+
def node_uniform_quadrature_weight(
|
|
693
|
+
node_index_in_elt: int,
|
|
694
|
+
):
|
|
695
|
+
return 1.0 / float(NODES_PER_ELEMENT)
|
|
696
|
+
|
|
697
|
+
return node_uniform_quadrature_weight
|
|
698
|
+
|
|
699
|
+
def make_trace_node_quadrature_weight(self):
|
|
700
|
+
# Non-conforming, zero measure on sides
|
|
701
|
+
|
|
702
|
+
@wp.func
|
|
703
|
+
def zero(node_index_in_elt: int):
|
|
704
|
+
return 0.0
|
|
705
|
+
|
|
706
|
+
return zero
|
|
707
|
+
|
|
708
|
+
def make_element_inner_weight(self):
|
|
709
|
+
tet_inner_weight = self._tet_shape.make_element_inner_weight()
|
|
710
|
+
|
|
711
|
+
CUBE_TO_TET = wp.constant(wp.mat33(np.linalg.inv(self._tet_to_cube)))
|
|
712
|
+
|
|
713
|
+
@cache.dynamic_func(suffix=self.name)
|
|
714
|
+
def element_inner_weight(
|
|
715
|
+
coords: Coords,
|
|
716
|
+
node_index_in_elt: int,
|
|
717
|
+
):
|
|
718
|
+
tet_coords = CUBE_TO_TET * (coords - CubeNonConformingPolynomialShapeFunctions._TET_OFFSET)
|
|
719
|
+
|
|
720
|
+
return tet_inner_weight(tet_coords, node_index_in_elt)
|
|
721
|
+
|
|
722
|
+
return element_inner_weight
|
|
723
|
+
|
|
724
|
+
def make_element_inner_weight_gradient(self):
|
|
725
|
+
tet_inner_weight_gradient = self._tet_shape.make_element_inner_weight_gradient()
|
|
726
|
+
|
|
727
|
+
CUBE_TO_TET = wp.constant(wp.mat33(np.linalg.inv(self._tet_to_cube)))
|
|
728
|
+
|
|
729
|
+
@cache.dynamic_func(suffix=self.name)
|
|
730
|
+
def element_inner_weight_gradient(
|
|
731
|
+
coords: Coords,
|
|
732
|
+
node_index_in_elt: int,
|
|
733
|
+
):
|
|
734
|
+
tet_coords = CUBE_TO_TET * (coords - CubeNonConformingPolynomialShapeFunctions._TET_OFFSET)
|
|
735
|
+
grad = tet_inner_weight_gradient(tet_coords, node_index_in_elt)
|
|
736
|
+
return wp.transpose(CUBE_TO_TET) * grad
|
|
737
|
+
|
|
738
|
+
return element_inner_weight_gradient
|