Firedrake connection
This creates a new submodule meshmode.interop to interface with Firedrake. The content is an adaption of https://github.com/benSepanski/fd2mm so that hopefully we can write something like
from meshmode.interop.firedrake import FiredrakeConnection
V = FunctionSpace("DG", fdrake_mesh, 4)
fdrake_conn = FiredrakeConnection(cl_ctx, V)
# defines from_fdrake.discr, from_fdrake.discr.mesh
u_fdrake = interpolate(V, ...)
u_mm = fdrake_conn.from_firedrake(u_fdrake)
u_2_fdrake = fdrake_conn.to_firedrake(u_mm)
interop works with two primary classes, ExternalImportHandler and ExternalExportHandler. I've only done Import handlers since I've been working firedrake -> meshmode. In either case, they are bare classes with a data attribute that is used to implement __eq__, __hash__, etc.
I implemented ExternalImportHandlers for several FInAT, fiat, and firedrake classes. interop's organization follows Firedrake's, which makes the conversion process a lot easier. Firedrake has a non-obvious function space organization detailed in this firedrake pull request. The tl;dr is:
- Reference elements are handled by
FInAT/fiat. These are used to determine unit nodes, firedrake meshes only use vertices - A
MeshTopologywhich stores only connectivity information of vertices, no coordinates etc. - A
FunctionSpacebuilt from aMeshTopologyandFInATelement used to define functions whose domain have no coordinates - A
CoordinatelessFunction(i.e. its domain is coordinateless) which lives in someFunctionSpace - A
MeshGeometrycreated from someMeshTopologytand aCoordinatelessFunctionwhich lives in aFunctionSpacedefined ontmapping each point to its coordinates- Some data is cached on the
MeshTopologyinfiredrake. For us, the shared data lives on aMeshGeometry. This way,ExternalImportHandlers for two different function spaces on the same mesh don't duplicate too much work (this comes up more than you would think sincefiredrakemakes you create different function spaces if your range is a vector instead of a scalar)
- Some data is cached on the
- A
WithGeometry... this is what thefiredrakefunctionFunctionSpacereally creates. It is aFunctionSpacewhich lives on aMeshGeometry. - A
Functioncan be defined on aWithGeometry
Each of these data structures has a corresponding ExternalImportHandler in meshmode.interop that takes the firedrake data and transforms it into the requisite form for meshmode (e.g. handling different reference elements, unit node locations, element orientations, creating node/facial adjacency objects, copying over boundary tags).
All of the above organization is for the developer. A user would be using the FiredrakeConnection class in meshmode/interop/firedrake/__init__.py. This connection is created from a firedrake function space and allows transporting functions back and forth from a corresponding meshmode discretization.
Nb:
- We should be sure the
ExternalImportHandler/ExternalExportHandlerstructure is what we want since it will affect any future development ininterop - There is some cleanup still to do, but I wanted to see what people thought of the overall design before I get into the details