From 35a71f8699396fb546d800b5291748eaedd1101a Mon Sep 17 00:00:00 2001
From: benSepanski <ben_sepanski@alumni.baylor.edu>
Date: Wed, 24 Jun 2020 16:31:02 -0500
Subject: [PATCH] Moved useful methods from FInAT and FIAT classes into
 functions in reference_cell.py

---
 meshmode/interop/firedrake/reference_cell.py | 134 +++++++++++++++++++
 1 file changed, 134 insertions(+)
 create mode 100644 meshmode/interop/firedrake/reference_cell.py

diff --git a/meshmode/interop/firedrake/reference_cell.py b/meshmode/interop/firedrake/reference_cell.py
new file mode 100644
index 00000000..100f426f
--- /dev/null
+++ b/meshmode/interop/firedrake/reference_cell.py
@@ -0,0 +1,134 @@
+__copyright__ = "Copyright (C) 2020 Benjamin Sepanski"
+
+__license__ = """
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+"""
+
+import numpy as np
+import numpy.linalg as la
+
+
+__doc__ = """
+.. autofunction:: get_affine_reference_simplex_mapping
+.. autofunction:: get_finat_element_unit_nodes
+"""
+
+
+# {{{ Map between reference simplices
+
+def get_affine_reference_simplex_mapping(self, spat_dim, firedrake_to_meshmode=True):
+    """
+    Returns a function which takes a numpy array points
+    on one reference cell and maps each
+    point to another using a positive affine map.
+
+    :param spat_dim: The spatial dimension
+    :param firedrake_to_meshmode: If true, the returned function maps from
+        the firedrake reference element to
+        meshmode, if false maps from
+        meshmode to firedrake. More specifically,
+        :mod:`firedrake` uses the standard :mod:`FIAT`
+        simplex and :mod:`meshmode` uses
+        :mod:`modepy`'s
+        `unit coordinates <https://documen.tician.de/modepy/nodes.html>`_.
+    :return: A function which takes a numpy array of *n* points with
+             shape *(dim, n)* on one reference cell and maps
+             each point to another using a positive affine map.
+             Note that the returned function performs
+             no input validation.
+    """
+    # validate input
+    assert isinstance(spat_dim, int)
+    assert spat_dim >= 0
+    assert isinstance(firedrake_to_meshmode, bool)
+
+    from FIAT.reference_element import ufc_simplex
+    from modepy.tools import unit_vertices
+    # Get the unit vertices from each system,
+    # each stored with shape *(dim, nunit_vertices)*
+    firedrake_unit_vertices = np.array(ufc_simplex(spat_dim).vertices).T
+    modepy_unit_vertices = unit_vertices(spat_dim).T
+
+    if firedrake_to_meshmode:
+        from_verts = firedrake_unit_vertices
+        to_verts = modepy_unit_vertices
+    else:
+        from_verts = modepy_unit_vertices
+        to_verts = firedrake_unit_vertices
+
+    # Compute matrix A and vector b so that A f_i + b -> t_i
+    # for each "from" vertex f_i and corresponding "to" vertex t_i
+    assert from_verts.shape == to_verts.shape
+    dim, nvects = from_verts.shape
+
+    # If only have on vertex, have A = I and b = to_vert - from_vert
+    if nvects == 1:
+        shift = to_verts[:, 0] - from_verts[:, 0]
+
+        def affine_map(points):
+            return points + shift[:, np.newaxis]
+    # Otherwise, we have to solve for A and b
+    else:
+        # span verts: v1 - v0, v2 - v0, ...
+        from_span_verts = from_verts[:, 1:] - from_verts[:, 0, np.newaxis]
+        to_span_verts = to_verts[:, 1:] - to_verts[:, 0, np.newaxis]
+        # mat maps (fj - f0) -> (tj - t0), our "A"
+        mat = la.solve(from_span_verts, to_span_verts)
+        # A f0 + b -> t0 so b = t0 - A f0
+        shift = to_verts[:, 0] - np.matmul(mat, from_verts[:, 0])
+
+        # Explicitly ensure A is positive
+        if la.det(mat) < 0:
+            from meshmode.mesh.processing import get_simplex_element_flip_matrix
+            flip_matrix = get_simplex_element_flip_matrix(1, to_verts)
+            mat = np.matmul(flip_matrix, mat)
+
+        def affine_map(points):
+            return np.matmul(mat, points) + shift[:, np.newaxis]
+
+    return affine_map
+
+# }}}
+
+
+# {{{ Get firedrake unit nodes
+
+def get_finat_element_unit_nodes(finat_element):
+    """
+    Returns the unit nodes used by the FInAT element in firedrake's
+    (equivalently, FInAT/FIAT's) reference coordinates
+
+    :param finat_element: A :class:`finat.finiteelementbase.FiniteElementBase`
+        instance (i.e. a firedrake function space's reference element)
+    :return: A numpy array of shape *(dim, nunit_nodes)* holding the unit
+             nodes used by this element. *dim* is the dimension spanned
+             by the finat element's reference element
+             (see its ``cell`` attribute)
+    """
+    # point evaluators is a list of functions *p_0,...,p_{n-1}*.
+    # *p_i(f)* evaluates function *f* at node *i* (stored as a tuple),
+    # so to recover node *i* we need to evaluate *p_i* at the identity
+    # function
+    point_evaluators = finat_element._element.dual.nodes
+    unit_nodes = [p(lambda x: x) for p in point_evaluators]
+    unit_nodes = np.array(unit_nodes).T
+
+    return unit_nodes
+
+# }}}
-- 
GitLab