From cf8035b12aa69ca59628b587fef7534e0751e7d5 Mon Sep 17 00:00:00 2001
From: Andreas Kloeckner <inform@tiker.net>
Date: Sun, 29 Aug 2021 23:36:31 -0500
Subject: [PATCH] Memoize normals instead of recomputing them, tweak other
 metric memoizations

---
 grudge/geometry/metrics.py | 97 +++++++++++++++++++++-----------------
 1 file changed, 53 insertions(+), 44 deletions(-)

diff --git a/grudge/geometry/metrics.py b/grudge/geometry/metrics.py
index fe0030dd..919bdf46 100644
--- a/grudge/geometry/metrics.py
+++ b/grudge/geometry/metrics.py
@@ -58,10 +58,12 @@ THE SOFTWARE.
 
 import numpy as np
 
-from grudge import DiscretizationCollection
 from arraycontext import thaw, freeze, ArrayContext
 from meshmode.dof_array import DOFArray
 
+from grudge import DiscretizationCollection
+import grudge.dof_desc as dof_desc
+
 from grudge.dof_desc import (
     DD_VOLUME, DOFDesc, DISCR_TAG_BASE
 )
@@ -72,6 +74,10 @@ from pytools.obj_array import make_obj_array
 from pytools import memoize_in
 
 
+from arraycontext import register_multivector_as_array_container
+register_multivector_as_array_container()
+
+
 # {{{ Metric computations
 
 def forward_metric_nth_derivative(
@@ -388,9 +394,12 @@ def inverse_surface_metric_derivative(
     dim = dcoll.dim
     ambient_dim = dcoll.ambient_dim
 
+    if dd is None:
+        dd = DD_VOLUME
+    dd = dof_desc.as_dofdesc(dd)
+
     @memoize_in(dcoll, (inverse_surface_metric_derivative, dd,
-                        "inv_metric_deriv_rst%s_xyz%s_adim%s_gdim%s"
-                        % (rst_axis, xyz_axis, ambient_dim, dim)))
+                        rst_axis, xyz_axis))
     def _inv_surf_metric_deriv():
         if ambient_dim == dim:
             imd = inverse_metric_derivative(
@@ -404,6 +413,7 @@ def inverse_surface_metric_derivative(
                 ) for d in range(dim)
             )
         return freeze(imd, actx)
+
     return thaw(_inv_surf_metric_deriv(), actx)
 
 
@@ -492,11 +502,7 @@ def area_element(
     if dd is None:
         dd = DD_VOLUME
 
-    dim = dcoll.discr_from_dd(dd).dim
-
-    @memoize_in(dcoll, (area_element, dd,
-                        "area_elements_adim%s_gdim%s"
-                        % (dcoll.ambient_dim, dim)))
+    @memoize_in(dcoll, (area_element, dd, actx.supports_nonscalar_broadcasting))
     def _area_elements():
         return freeze(actx.np.sqrt(
             pseudoscalar(actx, dcoll, dd=dd).norm_squared()), actx)
@@ -516,7 +522,6 @@ def rel_mv_normal(
 
     :arg dd: a :class:`~grudge.dof_desc.DOFDesc`, or a value convertible to one.
     """
-    import grudge.dof_desc as dof_desc
 
     dd = dof_desc.as_dofdesc(dd)
 
@@ -543,46 +548,50 @@ def mv_normal(
     :returns: a :class:`~pymbolic.geometric_algebra.MultiVector`
         containing the unit normals.
     """
-    import grudge.dof_desc as dof_desc
-
     dd = dof_desc.as_dofdesc(dd)
 
-    dim = dcoll.discr_from_dd(dd).dim
-    ambient_dim = dcoll.ambient_dim
+    @memoize_in(dcoll, (mv_normal, dd))
+    def _normal():
+        dim = dcoll.discr_from_dd(dd).dim
+        ambient_dim = dcoll.ambient_dim
 
-    if dim == ambient_dim:
-        raise ValueError("may only request normals on domains whose topological "
-                f"dimension ({dim}) differs from "
-                f"their ambient dimension ({ambient_dim})")
-
-    if dim == ambient_dim - 1:
-        return rel_mv_normal(actx, dcoll, dd=dd)
-
-    # NOTE: In the case of (d - 2)-dimensional curves, we don't really have
-    # enough information on the face to decide what an "exterior face normal"
-    # is (e.g the "normal" to a 1D curve in 3D space is actually a
-    # "normal plane")
-    #
-    # The trick done here is that we take the surface normal, move it to the
-    # face and then take a cross product with the face tangent to get the
-    # correct exterior face normal vector.
-    assert dim == ambient_dim - 2
-
-    from grudge.op import project
-    import grudge.dof_desc as dof_desc
-
-    volm_normal = MultiVector(
-        project(dcoll, dof_desc.DD_VOLUME, dd,
-                rel_mv_normal(
-                    actx, dcoll,
-                    dd=dof_desc.DD_VOLUME
-                ).as_vector(dtype=object))
-    )
-    pder = pseudoscalar(actx, dcoll, dd=dd)
+        if dim == ambient_dim:
+            raise ValueError("may only request normals on domains whose topological "
+                    f"dimension ({dim}) differs from "
+                    f"their ambient dimension ({ambient_dim})")
+
+        if dim == ambient_dim - 1:
+            result = rel_mv_normal(actx, dcoll, dd=dd)
+        else:
+            # NOTE: In the case of (d - 2)-dimensional curves, we don't really have
+            # enough information on the face to decide what an "exterior face normal"
+            # is (e.g the "normal" to a 1D curve in 3D space is actually a
+            # "normal plane")
+            #
+            # The trick done here is that we take the surface normal, move it to the
+            # face and then take a cross product with the face tangent to get the
+            # correct exterior face normal vector.
+            assert dim == ambient_dim - 2
+
+            from grudge.op import project
+
+            volm_normal = MultiVector(
+                project(dcoll, dof_desc.DD_VOLUME, dd,
+                        rel_mv_normal(
+                            actx, dcoll,
+                            dd=dof_desc.DD_VOLUME
+                        ).as_vector(dtype=object))
+            )
+            pder = pseudoscalar(actx, dcoll, dd=dd)
+
+            mv = -(volm_normal ^ pder) << volm_normal.I.inv()
+
+            result = mv / actx.np.sqrt(mv.norm_squared())
 
-    mv = -(volm_normal ^ pder) << volm_normal.I.inv()
+        return freeze(result, actx)
 
-    return mv / actx.np.sqrt(mv.norm_squared())
+    n = _normal()
+    return thaw(n, actx)
 
 
 def normal(actx: ArrayContext, dcoll: DiscretizationCollection, dd):
-- 
GitLab