From 0d1d4b5ca1cb2fbacf9f68c8d587c7f2fa079b27 Mon Sep 17 00:00:00 2001 From: Thomas Gibson <gibsonthomas1120@hotmail.com> Date: Wed, 16 Jun 2021 13:41:56 -0500 Subject: [PATCH] Reductions: elementwise min/max and allow nodal reductions to take obj arrays --- grudge/op.py | 2 + grudge/reductions.py | 100 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 102 insertions(+) diff --git a/grudge/op.py b/grudge/op.py index 74c0d95b..867cd2cb 100644 --- a/grudge/op.py +++ b/grudge/op.py @@ -80,6 +80,8 @@ from grudge.reductions import ( # noqa: F401 nodal_max_loc, integral, elementwise_sum, + elementwise_max, + elementwise_min, elementwise_integral, ) diff --git a/grudge/reductions.py b/grudge/reductions.py index 5fefc169..5337349e 100644 --- a/grudge/reductions.py +++ b/grudge/reductions.py @@ -27,6 +27,8 @@ Elementwise reductions ---------------------- .. autofunction:: elementwise_sum +.. autofunction:: elementwise_max +.. autofunction:: elementwise_min .. autofunction:: elementwise_integral """ @@ -138,6 +140,10 @@ def nodal_sum(dcoll: DiscretizationCollection, dd, vec) -> float: :arg vec: a :class:`~meshmode.dof_array.DOFArray`. :returns: a scalar denoting the nodal sum. """ + if isinstance(vec, np.ndarray): + return sum(nodal_sum(dcoll, dd, vec[idx]) + for idx in np.ndindex(vec.shape)) + comm = dcoll.mpi_communicator if comm is None: return nodal_sum_loc(dcoll, dd, vec) @@ -168,6 +174,10 @@ def nodal_min(dcoll: DiscretizationCollection, dd, vec) -> float: :arg vec: a :class:`~meshmode.dof_array.DOFArray`. :returns: a scalar denoting the nodal minimum. """ + if isinstance(vec, np.ndarray): + return min(nodal_min(dcoll, dd, vec[idx]) + for idx in np.ndindex(vec.shape)) + comm = dcoll.mpi_communicator if comm is None: return nodal_min_loc(dcoll, dd, vec) @@ -200,6 +210,10 @@ def nodal_max(dcoll: DiscretizationCollection, dd, vec) -> float: :arg vec: a :class:`~meshmode.dof_array.DOFArray`. :returns: a scalar denoting the nodal maximum. """ + if isinstance(vec, np.ndarray): + return max(nodal_max(dcoll, dd, vec[idx]) + for idx in np.ndindex(vec.shape)) + comm = dcoll.mpi_communicator if comm is None: return nodal_max_loc(dcoll, dd, vec) @@ -305,6 +319,92 @@ def elementwise_sum(dcoll: DiscretizationCollection, *args) -> DOFArray: ) +def elementwise_max(dcoll: DiscretizationCollection, *args) -> DOFArray: + r"""Returns a vector of DOFs with all entries on each element set + to the maximum over all DOFs on that element. + + May be called with ``(dcoll, vec)`` or ``(dcoll, dd, vec)``. + + :arg dcoll: a :class:`grudge.discretization.DiscretizationCollection`. + :arg dd: a :class:`~grudge.dof_desc.DOFDesc`, or a value convertible to one. + Defaults to the base volume discretization if not provided. + :arg vec: a :class:`~meshmode.dof_array.DOFArray` + :returns: a :class:`~meshmode.dof_array.DOFArray` whose entries + denote the element-wise max of *vec*. + """ + + if len(args) == 1: + vec, = args + dd = dof_desc.DOFDesc("vol", dof_desc.DISCR_TAG_BASE) + elif len(args) == 2: + dd, vec = args + else: + raise TypeError("invalid number of arguments") + + dd = dof_desc.as_dofdesc(dd) + + if isinstance(vec, np.ndarray): + return obj_array_vectorize( + lambda vi: elementwise_max(dcoll, dd, vi), vec + ) + + actx = vec.array_context + + return DOFArray( + actx, + tuple( + actx.call_loopy( + _map_elementwise_reduction(actx, "max"), + operand=vec_i + )["result"] + for vec_i in vec + ) + ) + + +def elementwise_min(dcoll: DiscretizationCollection, *args) -> DOFArray: + r"""Returns a vector of DOFs with all entries on each element set + to the minimum over all DOFs on that element. + + May be called with ``(dcoll, vec)`` or ``(dcoll, dd, vec)``. + + :arg dcoll: a :class:`grudge.discretization.DiscretizationCollection`. + :arg dd: a :class:`~grudge.dof_desc.DOFDesc`, or a value convertible to one. + Defaults to the base volume discretization if not provided. + :arg vec: a :class:`~meshmode.dof_array.DOFArray` + :returns: a :class:`~meshmode.dof_array.DOFArray` whose entries + denote the element-wise min of *vec*. + """ + + if len(args) == 1: + vec, = args + dd = dof_desc.DOFDesc("vol", dof_desc.DISCR_TAG_BASE) + elif len(args) == 2: + dd, vec = args + else: + raise TypeError("invalid number of arguments") + + dd = dof_desc.as_dofdesc(dd) + + if isinstance(vec, np.ndarray): + return obj_array_vectorize( + lambda vi: elementwise_min(dcoll, dd, vi), vec + ) + + actx = vec.array_context + + return DOFArray( + actx, + tuple( + actx.call_loopy( + _map_elementwise_reduction(actx, "min"), + operand=vec_i + )["result"] + for vec_i in vec + ) + ) + + def elementwise_integral(dcoll: DiscretizationCollection, dd, vec) -> DOFArray: """Numerically integrates a function represented by a :class:`~meshmode.dof_array.DOFArray` of degrees of freedom in -- GitLab