0
# Finite Element Method (FEM)
1
2
Warp's finite element framework provides comprehensive tools for solving partial differential equations using the finite element method. It includes geometry definitions, function spaces, quadrature, field operations, and integration capabilities for numerical simulation of physical phenomena.
3
4
## Capabilities
5
6
### Geometry Types
7
8
Geometric domains for finite element discretization.
9
10
```python { .api }
11
class Grid2D:
12
"""Regular 2D grid geometry."""
13
14
def __init__(self, res_x: int, res_y: int):
15
"""Create 2D grid with specified resolution."""
16
17
class Grid3D:
18
"""Regular 3D grid geometry."""
19
20
def __init__(self, res_x: int, res_y: int, res_z: int):
21
"""Create 3D grid with specified resolution."""
22
23
class Tetmesh:
24
"""Tetrahedral mesh geometry."""
25
26
def __init__(self, vertices: array, indices: array):
27
"""
28
Create tetrahedral mesh from vertices and element connectivity.
29
30
Args:
31
vertices: Array of vertex positions
32
indices: Array of tetrahedron vertex indices
33
"""
34
35
class Hexmesh:
36
"""Hexahedral mesh geometry."""
37
38
def __init__(self, vertices: array, indices: array):
39
"""Create hexahedral mesh from vertices and connectivity."""
40
41
class Trimesh2D:
42
"""2D triangular mesh geometry."""
43
44
def __init__(self, vertices: array, indices: array):
45
"""Create 2D triangular mesh."""
46
47
class Trimesh3D:
48
"""3D triangular surface mesh geometry."""
49
50
def __init__(self, vertices: array, indices: array):
51
"""Create 3D triangular surface mesh."""
52
53
class Quadmesh2D:
54
"""2D quadrilateral mesh geometry."""
55
56
def __init__(self, vertices: array, indices: array):
57
"""Create 2D quadrilateral mesh."""
58
59
class Quadmesh3D:
60
"""3D quadrilateral surface mesh geometry."""
61
62
def __init__(self, vertices: array, indices: array):
63
"""Create 3D quadrilateral surface mesh."""
64
65
class Nanogrid:
66
"""Dense voxel grid for level set methods."""
67
68
def __init__(self, data: array, transform: transform = None):
69
"""Create nanogrid from voxel data."""
70
71
class AdaptiveNanogrid:
72
"""Adaptive sparse voxel grid."""
73
74
def __init__(self, field: Field, tolerance: float = 1e-6):
75
"""Create adaptive nanogrid from field with specified tolerance."""
76
```
77
78
### Function Spaces
79
80
Mathematical spaces defining basis functions and degrees of freedom.
81
82
```python { .api }
83
class FunctionSpace:
84
"""Function space defining basis functions over geometry."""
85
86
@property
87
def dimension(self) -> int:
88
"""Number of degrees of freedom."""
89
90
@property
91
def geometry(self) -> Geometry:
92
"""Underlying geometry."""
93
94
class BasisSpace:
95
"""Basis function space on geometry elements."""
96
97
class PointBasisSpace:
98
"""Point-based basis space for meshfree methods."""
99
100
def make_polynomial_space(geometry: Geometry,
101
degree: int,
102
dtype: type = float) -> FunctionSpace:
103
"""
104
Create polynomial function space of specified degree.
105
106
Args:
107
geometry: Geometric domain
108
degree: Polynomial degree (1=linear, 2=quadratic, etc.)
109
dtype: Scalar type for degrees of freedom
110
111
Returns:
112
Function space with polynomial basis functions
113
"""
114
115
def make_polynomial_basis_space(geometry: Geometry,
116
degree: int,
117
family: str = "lagrange") -> BasisSpace:
118
"""Create polynomial basis space with specified family."""
119
120
def make_collocated_function_space(geometry: Geometry,
121
shape: tuple,
122
degree: int = 1) -> FunctionSpace:
123
"""Create collocated vector/tensor function space."""
124
125
def make_covariant_function_space(geometry: Geometry,
126
degree: int = 1) -> FunctionSpace:
127
"""Create covariant (edge-based) function space."""
128
129
def make_contravariant_function_space(geometry: Geometry,
130
degree: int = 1) -> FunctionSpace:
131
"""Create contravariant (face-based) function space."""
132
```
133
134
### Fields
135
136
Field representations and operations over function spaces.
137
138
```python { .api }
139
class DiscreteField:
140
"""Field represented by discrete degrees of freedom."""
141
142
def __init__(self, space: FunctionSpace, dof_values: array = None):
143
"""
144
Create discrete field on function space.
145
146
Args:
147
space: Function space defining basis
148
dof_values: Degree of freedom values (allocated if None)
149
"""
150
151
class ImplicitField:
152
"""Field defined by implicit function."""
153
154
def __init__(self, func: Callable, space: FunctionSpace = None):
155
"""Create field from implicit function."""
156
157
class UniformField:
158
"""Field with constant value everywhere."""
159
160
def __init__(self, value, space: FunctionSpace = None):
161
"""Create uniform field with constant value."""
162
163
class NonconformingField:
164
"""Field with non-conforming discretization."""
165
166
def make_discrete_field(space: FunctionSpace,
167
dof_values: array = None) -> DiscreteField:
168
"""Create discrete field on function space."""
169
170
def make_test(space: FunctionSpace) -> DiscreteField:
171
"""Create test function for weak forms."""
172
173
def make_trial(space: FunctionSpace) -> DiscreteField:
174
"""Create trial function for weak forms."""
175
176
def make_restriction(field: Field, domain: Domain) -> Field:
177
"""Restrict field to subdomain."""
178
```
179
180
### Quadrature
181
182
Numerical integration schemes for finite element computations.
183
184
```python { .api }
185
class Quadrature:
186
"""Base class for quadrature rules."""
187
188
class RegularQuadrature:
189
"""Regular quadrature on structured grids."""
190
191
def __init__(self, geometry: Geometry, order: int):
192
"""
193
Create regular quadrature rule.
194
195
Args:
196
geometry: Geometric domain
197
order: Integration order (accuracy)
198
"""
199
200
class NodalQuadrature:
201
"""Quadrature using mesh nodes as integration points."""
202
203
def __init__(self, geometry: Geometry):
204
"""Create nodal quadrature rule."""
205
206
class PicQuadrature:
207
"""Particle-in-cell quadrature for particle methods."""
208
209
def __init__(self, positions: array, weights: array = None):
210
"""
211
Create PIC quadrature from particle data.
212
213
Args:
214
positions: Particle positions
215
weights: Particle weights (uniform if None)
216
"""
217
218
class ExplicitQuadrature:
219
"""Explicit quadrature with specified points and weights."""
220
221
def __init__(self, points: array, weights: array):
222
"""Create quadrature from explicit points and weights."""
223
```
224
225
### Domain and Topology
226
227
Domain definitions and topological operations.
228
229
```python { .api }
230
class GeometryDomain:
231
"""Geometric domain for finite element computations."""
232
233
def __init__(self, geometry: Geometry):
234
"""Create domain from geometry."""
235
236
class Cells:
237
"""Domain representing geometry cells/elements."""
238
239
class Sides:
240
"""Domain representing element faces/edges."""
241
242
class BoundarySides:
243
"""Domain representing boundary faces."""
244
245
class FrontierSides:
246
"""Domain representing interfaces between regions."""
247
248
class SpacePartition:
249
"""Partition of function space for parallel computation."""
250
251
class SpaceRestriction:
252
"""Restriction of function space to subdomain."""
253
254
class SpaceTopology:
255
"""Topological information for function space."""
256
257
def make_space_partition(space: FunctionSpace,
258
partitions: int) -> SpacePartition:
259
"""Partition function space for parallel computation."""
260
261
def make_space_restriction(space: FunctionSpace,
262
domain: Domain) -> SpaceRestriction:
263
"""Create space restriction to subdomain."""
264
```
265
266
### Integration and Interpolation
267
268
Core operations for finite element computations.
269
270
```python { .api }
271
def integrate(integrand: Callable,
272
domain: Domain,
273
quadrature: Quadrature = None,
274
output: Field = None) -> Field:
275
"""
276
Integrate function over domain using specified quadrature.
277
278
Args:
279
integrand: Function to integrate
280
domain: Integration domain
281
quadrature: Quadrature rule (default if None)
282
output: Output field (allocated if None)
283
284
Returns:
285
Integrated field values
286
"""
287
288
def interpolate(field: Field,
289
space: FunctionSpace,
290
domain: Domain = None) -> DiscreteField:
291
"""
292
Interpolate field onto function space.
293
294
Args:
295
field: Input field to interpolate
296
space: Target function space
297
domain: Interpolation domain (geometry if None)
298
299
Returns:
300
Discrete field on target space
301
"""
302
```
303
304
### Differential Operators
305
306
Operators for computing derivatives and differential operations.
307
308
```python { .api }
309
def grad(field: Field) -> Field:
310
"""Compute gradient of scalar field."""
311
312
def div(field: Field) -> Field:
313
"""Compute divergence of vector field."""
314
315
def curl(field: Field) -> Field:
316
"""Compute curl of vector field."""
317
318
def D(field: Field, direction: int) -> Field:
319
"""Partial derivative in specified direction."""
320
321
def grad_outer(field: Field) -> Field:
322
"""Outer product of gradient."""
323
324
def div_outer(field: Field) -> Field:
325
"""Outer divergence operation."""
326
327
def deformation_gradient(field: Field) -> Field:
328
"""Compute deformation gradient tensor."""
329
330
def grad_average(field: Field) -> Field:
331
"""Average gradient across elements."""
332
333
def grad_jump(field: Field) -> Field:
334
"""Jump in gradient across element boundaries."""
335
```
336
337
### Field Operations
338
339
Operations for manipulating and querying fields.
340
341
```python { .api }
342
def inner(a: Field, b: Field) -> Field:
343
"""Inner product of two fields."""
344
345
def outer(a: Field, b: Field) -> Field:
346
"""Outer product of two fields."""
347
348
def average(field: Field) -> Field:
349
"""Average field values across elements."""
350
351
def jump(field: Field) -> Field:
352
"""Jump in field across element boundaries."""
353
354
def lookup(field: Field, coordinates: array) -> array:
355
"""Evaluate field at specified coordinates."""
356
357
def partition_lookup(field: Field, partition: SpacePartition) -> Field:
358
"""Lookup field values in partitioned space."""
359
360
def at_node(field: Field, node_index: int):
361
"""Get field value at specific node."""
362
363
def measure(domain: Domain) -> Field:
364
"""Compute measure (area/volume) of domain."""
365
366
def measure_ratio(domain: Domain, reference: Domain) -> Field:
367
"""Ratio of domain measure to reference."""
368
369
def normal(domain: Domain) -> Field:
370
"""Outward normal vector on boundary."""
371
372
def position(domain: Domain) -> Field:
373
"""Position coordinates on domain."""
374
```
375
376
### Element Operations
377
378
Operations for working with individual elements.
379
380
```python { .api }
381
def cells(geometry: Geometry) -> Cells:
382
"""Get cells/elements of geometry."""
383
384
def element_coordinates(element: ElementIndex) -> Coords:
385
"""Get coordinates of element."""
386
387
def element_closest_point(element: ElementIndex, point: vec3) -> vec3:
388
"""Find closest point on element to query point."""
389
390
def node_count(element: ElementIndex) -> int:
391
"""Number of nodes in element."""
392
393
def node_index(element: ElementIndex, local_index: int) -> int:
394
"""Global node index from element and local index."""
395
396
def degree(space: FunctionSpace) -> int:
397
"""Polynomial degree of function space."""
398
399
def to_cell_side(side: int) -> int:
400
"""Convert side index to cell-relative index."""
401
402
def to_inner_cell(side: int) -> int:
403
"""Get inner cell adjacent to side."""
404
405
def to_outer_cell(side: int) -> int:
406
"""Get outer cell adjacent to side."""
407
```
408
409
### Caching and Memory Management
410
411
Utilities for managing temporary storage and caching.
412
413
```python { .api }
414
class TemporaryStore:
415
"""Storage manager for temporary arrays."""
416
417
def __init__(self, device: Device = None):
418
"""Create temporary storage on device."""
419
420
def borrow_temporary(shape: tuple,
421
dtype: type,
422
device: Device = None) -> array:
423
"""Borrow temporary array from pool."""
424
425
def borrow_temporary_like(arr: array,
426
dtype: type = None,
427
device: Device = None) -> array:
428
"""Borrow temporary array with same shape as existing array."""
429
430
def set_default_temporary_store(store: TemporaryStore) -> None:
431
"""Set default temporary storage."""
432
```
433
434
### Adaptivity
435
436
Adaptive mesh refinement and field operations.
437
438
```python { .api }
439
def adaptive_nanogrid_from_field(field: Field,
440
tolerance: float = 1e-6) -> AdaptiveNanogrid:
441
"""Create adaptive nanogrid from field with error tolerance."""
442
443
def adaptive_nanogrid_from_hierarchy(hierarchy,
444
level: int = -1) -> AdaptiveNanogrid:
445
"""Create adaptive nanogrid from mesh hierarchy."""
446
```
447
448
## Usage Examples
449
450
### Basic FEM Workflow
451
```python
452
import warp as wp
453
import warp.fem as fem
454
455
# Create geometry
456
grid = fem.Grid2D(res_x=32, res_y=32)
457
458
# Create function space
459
space = fem.make_polynomial_space(grid, degree=1)
460
461
# Create fields
462
u = fem.make_trial(space) # Solution field
463
v = fem.make_test(space) # Test function
464
465
# Define weak form for Poisson equation: ∫∇u·∇v dx = ∫f·v dx
466
@wp.kernel
467
def poisson_integrand(
468
coords: fem.Coords,
469
u: fem.Field,
470
v: fem.Field,
471
f: fem.Field
472
):
473
return fem.inner(fem.grad(u), fem.grad(v)) - f * v
474
475
# Integrate over domain
476
cells_domain = fem.cells(grid)
477
weak_form = fem.integrate(poisson_integrand, cells_domain)
478
```
479
480
### Working with Different Geometries
481
```python
482
# Tetrahedral mesh
483
vertices = wp.array([[0,0,0], [1,0,0], [0,1,0], [0,0,1]], dtype=wp.vec3)
484
tets = wp.array([[0,1,2,3]], dtype=wp.int32)
485
tet_mesh = fem.Tetmesh(vertices, tets)
486
487
# Function space on tetrahedral mesh
488
tet_space = fem.make_polynomial_space(tet_mesh, degree=2)
489
490
# Vector field
491
vector_space = fem.make_collocated_function_space(tet_mesh, shape=(3,), degree=1)
492
displacement = fem.make_discrete_field(vector_space)
493
```
494
495
### Quadrature and Integration
496
```python
497
# Custom quadrature
498
quadrature = fem.RegularQuadrature(grid, order=2)
499
500
# Integrate with specific quadrature
501
result = fem.integrate(
502
my_integrand_function,
503
fem.cells(grid),
504
quadrature=quadrature
505
)
506
507
# PIC quadrature for particle methods
508
particle_pos = wp.array([[0.1, 0.2], [0.8, 0.9]], dtype=wp.vec2)
509
particle_weights = wp.ones(2, dtype=float)
510
pic_quad = fem.PicQuadrature(particle_pos, particle_weights)
511
```
512
513
## Types
514
515
```python { .api }
516
# Core FEM types
517
Geometry = typing.Union[Grid2D, Grid3D, Tetmesh, Hexmesh, Trimesh2D, Trimesh3D, Quadmesh2D, Quadmesh3D]
518
Field = typing.Union[DiscreteField, ImplicitField, UniformField, NonconformingField]
519
Domain = typing.Union[GeometryDomain, Cells, Sides, BoundarySides, FrontierSides]
520
521
# Index types
522
ElementIndex = int
523
QuadraturePointIndex = int
524
NULL_ELEMENT_INDEX = -1
525
NULL_QP_INDEX = -1
526
527
# Coordinate types
528
Coords = vec3 # Element coordinates
529
530
# Sample type for field evaluation
531
class Sample:
532
element_index: ElementIndex
533
qp_index: QuadraturePointIndex
534
coords: Coords
535
536
def make_free_sample(coords: Coords) -> Sample:
537
"""Create sample at free coordinates."""
538
539
# DOF mapper for complex field structures
540
class DofMapper:
541
"""Maps between different DOF organizations."""
542
543
class SymmetricTensorMapper:
544
"""Maps symmetric tensor components to DOFs."""
545
546
class SkewSymmetricTensorMapper:
547
"""Maps skew-symmetric tensor components to DOFs."""
548
549
# Polynomial utilities
550
class Polynomial:
551
"""Polynomial representation for basis functions."""
552
553
def __init__(self, coefficients: array, degree: int):
554
"""Create polynomial from coefficients."""
555
```