From 81d08cb0ac4c2f37589c46bf9d7354259118e079 Mon Sep 17 00:00:00 2001
From: Thomas Gibson <gibsonthomas1120@hotmail.com>
Date: Wed, 28 Apr 2021 21:49:24 -0500
Subject: [PATCH] Implement local gradient operator

---
 grudge/geometry/metrics.py | 13 +++++++---
 grudge/op.py               | 50 ++++++++++++++++++++++++++++++++++++++
 2 files changed, 59 insertions(+), 4 deletions(-)

diff --git a/grudge/geometry/metrics.py b/grudge/geometry/metrics.py
index 17d07a06..4e7b07ff 100644
--- a/grudge/geometry/metrics.py
+++ b/grudge/geometry/metrics.py
@@ -168,8 +168,12 @@ def inverse_metric_derivative(actx, dcoll, rst_axis, xyz_axis, dd):
         for rst_axis in range(dim)
     ).inv()
 
-    return (outprod_with_unit(xyz_axis, rst_axis)
-            * volume_pseudoscalar_inv).as_scalar()
+    # NOTE: this always produces a DOFArray of shape (1,).
+    # the thing we want is actually inside
+    result, = (outprod_with_unit(xyz_axis, rst_axis)
+              * volume_pseudoscalar_inv).as_scalar()
+
+    return result
 
 
 @memoize_on_first_arg
@@ -186,7 +190,6 @@ def inverse_surface_metric_derivative(actx, dcoll, rst_axis, xyz_axis, dd=None):
                 actx, dcoll, d, rst_axis, dd=dd
             ) for d in range(dim)
         )
-
     return imd
 
 
@@ -236,7 +239,9 @@ def parametrization_derivative(actx, dcoll, dd):
     )
 
 
-def pseudoscalar(actx, dcoll, dd):
+def pseudoscalar(actx, dcoll, dd=None):
+    if dd is None:
+        dd = DD_VOLUME
 
     return parametrization_derivative(
         actx, dcoll, dd
diff --git a/grudge/op.py b/grudge/op.py
index 8acf3c83..bce19ed1 100644
--- a/grudge/op.py
+++ b/grudge/op.py
@@ -161,6 +161,56 @@ def local_grad(dcoll, vec):
     return _bound_grad(dcoll)(u=vec)
 
 
+def reference_derivative_matrices(actx, element_group):
+    @keyed_memoize_in(
+        actx, reference_derivative_matrices,
+        lambda grp: grp.discretization_key())
+    def get_ref_derivative_mats(grp):
+        from meshmode.discretization.poly_element import diff_matrices
+        return tuple(
+            actx.freeze(actx.from_numpy(dfmat))
+            for dfmat in diff_matrices(grp)
+        )
+
+    return get_ref_derivative_mats(element_group)
+
+
+def _compute_local_gradient(dcoll, vec, xyz_axis):
+    from grudge.geometry import \
+        inverse_surface_metric_derivative
+
+    discr = dcoll.discr_from_dd(dof_desc.DD_VOLUME)
+    actx = vec.array_context
+
+    inverse_jac_t = [
+        inverse_surface_metric_derivative(
+            actx, dcoll, rst_axis, xyz_axis
+        ) for rst_axis in range(dcoll.dim)
+    ]
+
+    data = []
+    for grp, vec_i in zip(discr.groups, vec):
+        data.append(sum(
+            actx.einsum(
+                "ij,ej,ej->ei",
+                reference_derivative_matrices(actx, grp)[rst_axis],
+                vec_i,
+                inverse_jac_t[rst_axis],
+                arg_names=("ref_diff_mat", "vec", "inv_jac_t"),
+                tagged=(HasElementwiseMatvecTag(),)
+            ) for rst_axis in range(dcoll.dim))
+        )
+    return DOFArray(actx, data=tuple(data))
+
+
+def local_gradient(dcoll, vec):
+    from pytools.obj_array import make_obj_array
+    return make_obj_array(
+        [_compute_local_gradient(dcoll, vec, xyz_axis)
+         for xyz_axis in range(dcoll.dim)]
+    )
+
+
 @memoize_on_first_arg
 def _bound_d_dx(dcoll, xyz_axis):
     return bind(dcoll, sym.nabla(dcoll.dim)[xyz_axis] * sym.Variable("u"),
-- 
GitLab