File size: 14,730 Bytes
66c9c8a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
warp.fem
=====================

.. currentmodule:: warp.fem

The ``warp.fem`` module is designed to facilitate solving physical systems described as differential 
equations. For example, it can solve PDEs for diffusion, convection, fluid flow, and elasticity problems 
using finite-element-based (FEM) Galerkin methods, and allows users to quickly experiment with various FEM
formulations and discretization schemes.

Integrands
----------

The core functionality of the FEM toolkit is the ability to integrate constant, linear, and bilinear forms 
over various domains and using arbitrary interpolation basis.

The main mechanism is the :py:func:`.integrand` decorator, for instance: ::

    @integrand
    def linear_form(
        s: Sample,
        v: Field,
    ):
        return v(s)


    @integrand
    def diffusion_form(s: Sample, u: Field, v: Field, nu: float):
        return nu * wp.dot(
            grad(u, s),
            grad(v, s),
        )

Integrands are normal warp kernels, meaning any usual warp function can be used. 
However, they accept a few special parameters:

  - :class:`.Sample` contains information about the current integration sample point, such as the element index and coordinates in element.
  - :class:`.Field` designates an abstract field, which will be replaced at call time by the actual field type: for instance a :class:`.DiscreteField`, :class:`.field.TestField` or :class:`.field.TrialField` defined over an arbitrary :class:`.FunctionSpace`.
    A field `u` can be evaluated at a given sample `s` using the :func:`.inner` operator, i.e, ``inner(u, s)``, or as a shortcut using the usual call operator, ``u(s)``.
    Several other operators are available, such as :func:`.grad`; see the :ref:`Operators` section.
  - :class:`.Domain` designates an abstract integration domain. Several operators are also provided for domains, for example in the example below evaluating the normal at the current sample position: ::
    
            @integrand
            def boundary_form(
                s: Sample,
                domain: Domain,
                u: Field,
            ):
                nor = normal(domain, s)
                return wp.dot(u(s), nor)

Integrands cannot be used directly with :func:`warp.launch`, but must be called through :func:`.integrate` or :func:`.interpolate` instead.
The root integrand (`integrand` argument passed to :func:`integrate` or :func:`interpolate` call) will automatically get passed :class:`.Sample` and :class:`.Domain` parameters.
:class:`.Field` parameters must be passed as a dictionary in the `fields` argument of the launcher function, and all other standard Warp types arguments must be
passed as a dictionary in the `values` argument of the launcher function, for instance: ::
    
    integrate(diffusion_form, fields={"u": trial, "v": test}, values={"nu": viscosity})


Basic workflow
--------------

The typical steps for solving a linear PDE are as follow:

 - Define a :class:`.Geometry` (grid, mesh, etc). At the moment, 2D and 3D regular grids, triangle, quadrilateral, tetrahedron and hexahedron meshes are supported.
 - Define one or more :class:`.FunctionSpace`, by equipping the geometry elements with shape functions. See :func:`.make_polynomial_space`. At the moment, continuous/discontinuous Lagrange (:math:`P_{k[d]}, Q_{k[d]}`) and Serendipity (:math:`S_k`) shape functions of order :math:`k \leq 3` are supported.
 - Define an integration :class:`.GeometryDomain`, for instance the geometry's cells (:class:`.Cells`) or boundary sides (:class:`.BoundarySides`).
 - Integrate linear forms to build the system's right-hand-side. Define a test function over the function space using :func:`.make_test`,
   a :class:`.Quadrature` formula (or let the module choose one based on the function space degree), and call :func:`.integrate` with the linear form integrand.
   The result is a :class:`warp.array` containing the integration result for each of the function space degrees of freedom.
 - Integrate bilinear forms to build the system's left-hand-side. Define a trial function over the function space using :func:`.make_trial`,
   then call :func:`.integrate` with the bilinear form integrand.
   The result is a :class:`warp.sparse.BsrMatrix` containing the integration result for each pair of test and trial function space degrees of freedom.
   Note that the trial and test functions do not have to be defined over the same function space, so that Mixed FEM is supported.
 - Solve the resulting linear system using the solver of your choice


The following excerpt from the introductory example ``examples/fem/example_diffusion.py`` outlines this procedure: ::

    # Grid geometry
    geo = Grid2D(n=50, cell_size=2)

    # Domain and function spaces
    domain = Cells(geometry=geo)
    scalar_space = make_polynomial_space(geo, degree=3)

    # Right-hand-side (forcing term)
    test = make_test(space=scalar_space, domain=domain)
    rhs = integrate(linear_form, fields={"v": test})

    # Weakly-imposed boundary conditions on Y sides
    boundary = BoundarySides(geo)
    bd_test = make_test(space=scalar_space, domain=boundary)
    bd_trial = make_trial(space=scalar_space, domain=boundary)
    bd_matrix = integrate(y_mass_form, fields={"u": bd_trial, "v": bd_test})

    # Diffusion form
    trial = make_trial(space=scalar_space, domain=domain)
    matrix = integrate(diffusion_form, fields={"u": trial, "v": test}, values={"nu": viscosity})

    # Assemble linear system (add diffusion and boundary condition matrices)
    bsr_axpy(x=bd_matrix, y=matrix, alpha=boundary_strength, beta=1)

    # Solve linear system using Conjugate Gradient
    x = wp.zeros_like(rhs)
    bsr_cg(matrix, b=rhs, x=x)


.. note::
    The :func:`.integrate` function does not check that the passed integrands are actually linear or bilinear forms; it is up to the user to ensure that they are.
    To solve non-linear PDEs, one can use an iterative procedure and pass the current value of the studied function :class:`.DiscreteField` argument to the integrand, on which
    arbitrary operations are permitted. However, the result of the form must remain linear in the test and trial fields.

Introductory examples
---------------------

``warp.fem`` ships with a list of examples in the ``examples/fem`` directory illustrating common model problems.

 - ``example_diffusion.py``: 2D diffusion with homogenous Neumann and Dirichlet boundary conditions
     * ``example_diffusion_3d.py``: 3D variant of the diffusion problem
 - ``example_convection_diffusion.py``: 2D convection-diffusion using semi-Lagrangian advection
     * ``example_diffusion_dg0.py``: 2D convection-diffusion using finite-volume and upwind transport
     * ``example_diffusion_dg.py``: 2D convection-diffusion using Discontinuous Galerkin with upwind transport and Symmetric Interior Penalty
 - ``example_stokes.py``: 2D incompressible Stokes flow using mixed :math:`P_k/P_{k-1}` or :math:`Q_k/P_{(k-1)d}` elements
 - ``example_navier_stokes.py``: 2D Navier-Stokes flow using mixed :math:`P_k/P_{k-1}` elements
 - ``example_mixed_elasticity.py``: 2D linear elasticity using mixed continuous/discontinuous :math:`S_k/P_{(k-1)d}` elements


Advanced usages
---------------

High-order (curved) geometries
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

It is possible to convert any :class:`.Geometry` (grids and explicit meshes) into a curved, high-order variant by deforming them 
with an arbitrary-order displacement field using the :meth:`~.DiscreteField.make_deformed_geometry` method. 
The process looks as follow: ::

   # Define a base geometry
   base_geo = fem.Grid3D(res=resolution)

   # Define a displacement field on the base geometry
   deformation_space = fem.make_polynomial_space(base_geo, degree=deformation_degree, dtype=wp.vec3)
   deformation_field = deformation_space.make_field()

   # Populate the field value by interpolating an expression
   fem.interpolate(deformation_field_expr, dest=deformation_field)

   # Construct the deformed geometry from the displacement field
   deform_geo = deformation_field.make_deformed_geometry()

   # Define new function spaces on the deformed geometry
   scalar_space = fem.make_polynomial_space(deformed_geo, degree=scalar_space_degree)

See also ``example_deformed_geometry.py`` for a complete example.

Particle-based quadrature
^^^^^^^^^^^^^^^^^^^^^^^^^

The :class:`.PicQuadrature` provides a way to define Particle-In-Cell quadratures from a set or arbitrary particles,
which can be helpful to develop MPM-like methods.
The particles are automatically bucketed to the geometry cells when the quadrature is initialized.
This is illustrated by the ``example_stokes_transfer.py`` and ``example_apic_fluid.py`` examples.

Partitioning
^^^^^^^^^^^^

The FEM toolkit makes it possible to perform integration on a subset of the domain elements, 
possibly re-indexing degrees of freedom so that the linear system contains the local ones only.
This is useful for distributed computation (see ``examples/fem/example_diffusion_mgpu.py``), or simply to limit the simulation domain to a subset of active cells (see ``examples/fem/example_stokes_transfer.py``).

A partition of the simulation geometry can be defined using subclasses of :class:`.GeometryPartition`
such as :class:`.LinearGeometryPartition`  or :class:`.ExplicitGeometryPartition`.

Function spaces can then be partitioned according to the geometry partition using :func:`.make_space_partition`. 
The resulting :class:`.SpacePartition` object allows translating between space-wide and partition-wide node indices, 
and differentiating interior, frontier and exterior nodes.

Memory management
^^^^^^^^^^^^^^^^^

Several ``warp.fem`` functions require allocating temporary buffers to perform their computations. 
If such functions are called many times in a tight loop, those many allocations and de-allocations may degrade performance.
To overcome this issue, a :class:`.cache.TemporaryStore` object may be created to persist and re-use temporary allocations across calls,
either globally using :func:`set_default_temporary_store` or at a per-function granularity using the corresponding argument.

.. _Operators:

Operators
---------
.. autofunction:: position(domain: Domain, s: Sample)
.. autofunction:: normal(domain: Domain, s: Sample)
.. autofunction:: lookup(domain: Domain, x)
.. autofunction:: measure(domain: Domain, s: Sample)
.. autofunction:: measure_ratio(domain: Domain, s: Sample)
.. autofunction:: deformation_gradient(domain: Domain, s: Sample)

.. autofunction:: degree(f: Field)
.. autofunction:: inner(f: Field, s: Sample)
.. autofunction:: outer(f: Field, s: Sample)
.. autofunction:: grad(f: Field, s: Sample)
.. autofunction:: grad_outer(f: Field, s: Sample)
.. autofunction:: div(f: Field, s: Sample)
.. autofunction:: div_outer(f: Field, s: Sample)
.. autofunction:: at_node(f: Field, s: Sample)

.. autofunction:: D(f: Field, s: Sample)
.. autofunction:: curl(f: Field, s: Sample)
.. autofunction:: jump(f: Field, s: Sample)
.. autofunction:: average(f: Field, s: Sample)
.. autofunction:: grad_jump(f: Field, s: Sample)
.. autofunction:: grad_average(f: Field, s: Sample)

.. autofunction:: warp.fem.operator.operator

Integration
-----------

.. autofunction:: integrate
.. autofunction:: interpolate

.. autofunction:: integrand

.. class:: Sample

   Per-sample point context for evaluating fields and related operators in integrands.

.. autoclass:: Field 

.. autoclass:: Domain 

Geometry
--------

.. autoclass:: Grid2D
   :show-inheritance:

.. autoclass:: Trimesh2D
   :show-inheritance:

.. autoclass:: Quadmesh2D
   :show-inheritance:

.. autoclass:: Grid3D
   :show-inheritance:

.. autoclass:: Tetmesh
   :show-inheritance:

.. autoclass:: Hexmesh
   :show-inheritance:

.. autoclass:: LinearGeometryPartition

.. autoclass:: ExplicitGeometryPartition

.. autoclass:: Cells
   :show-inheritance:

.. autoclass:: Sides
   :show-inheritance:

.. autoclass:: BoundarySides
   :show-inheritance:

.. autoclass:: FrontierSides
   :show-inheritance:

.. autoclass:: Polynomial
   :members:

.. autoclass:: RegularQuadrature
   :show-inheritance:

.. autoclass:: NodalQuadrature
   :show-inheritance:

.. autoclass:: ExplicitQuadrature
   :show-inheritance:

.. autoclass:: PicQuadrature
   :show-inheritance:

Function Spaces
---------------

.. autofunction:: make_polynomial_space

.. autofunction:: make_polynomial_basis_space

.. autofunction:: make_collocated_function_space

.. autofunction:: make_space_partition

.. autofunction:: make_space_restriction

.. autoclass:: ElementBasis
   :members:

.. autoclass:: SymmetricTensorMapper
   :show-inheritance:

.. autoclass:: SkewSymmetricTensorMapper
   :show-inheritance:

.. autoclass:: PointBasisSpace
   :show-inheritance:

Fields
------

.. autofunction:: make_test

.. autofunction:: make_trial

.. autofunction:: make_restriction

Boundary Conditions
-------------------

.. autofunction:: normalize_dirichlet_projector

.. autofunction:: project_linear_system

Memory management
-----------------

.. autofunction:: set_default_temporary_store

.. autofunction:: borrow_temporary

.. autofunction:: borrow_temporary_like


Interfaces
----------

Interface classes are not meant to be constructed directly, but can be derived from extend the built-in functionality.

.. autoclass:: Geometry
   :members: cell_count, side_count, boundary_side_count

.. autoclass:: GeometryPartition
   :members: cell_count, side_count, boundary_side_count, frontier_side_count

.. autoclass:: GeometryDomain
   :members: ElementKind, element_kind, dimension, element_count

.. autoclass:: Quadrature
   :members: domain, total_point_count

.. autoclass:: FunctionSpace
   :members: dtype, topology, geometry, dimension, degree, trace, make_field

.. autoclass:: SpaceTopology
   :members: dimension, geometry, node_count, element_node_indices, trace

.. autoclass:: BasisSpace
   :members: topology, geometry, node_positions

.. autoclass:: warp.fem.space.shape.ShapeFunction

.. autoclass:: SpacePartition
   :members: node_count, owned_node_count, interior_node_count, space_node_indices

.. autoclass:: SpaceRestriction
   :members: node_count

.. autoclass:: DofMapper

.. autoclass:: FieldLike

.. autoclass:: DiscreteField
   :show-inheritance:
   :members: dof_values, trace, make_deformed_geometry

.. autoclass:: warp.fem.field.FieldRestriction

.. autoclass:: warp.fem.field.SpaceField
   :show-inheritance:

.. autoclass:: warp.fem.field.TestField
   :show-inheritance:

.. autoclass:: warp.fem.field.TrialField
   :show-inheritance:

.. autoclass:: TemporaryStore
   :members: clear

.. autoclass:: warp.fem.cache.Temporary
   :members: array, detach, release