diff --git a/doc/array.rst b/doc/array.rst new file mode 100644 index 0000000000000000000000000000000000000000..a722c4799ace3d3d6f4d10b38333b68404bbbcc0 --- /dev/null +++ b/doc/array.rst @@ -0,0 +1,9 @@ +DOF (Degree-of-Freedom) Arrays +============================== + +.. automodule:: meshmode.dof_array + +Array Contexts +============== + +.. automodule:: meshmode.array_context diff --git a/doc/array_context.rst b/doc/array_context.rst deleted file mode 100644 index df9b375f1864c9a9fd21afe1ed49b6a261e44c5a..0000000000000000000000000000000000000000 --- a/doc/array_context.rst +++ /dev/null @@ -1,4 +0,0 @@ -Array Contexts -============== - -.. automodule:: meshmode.array_context diff --git a/doc/index.rst b/doc/index.rst index af5305e6b60b18871c19f9b7556b7744cf4f64b1..a5e39233edd23cf2ae9fc7a3ec6757d5fffd8fe1 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -7,7 +7,7 @@ Contents: :maxdepth: 2 mesh - array_context + array discretization connection misc diff --git a/examples/simple-dg.py b/examples/simple-dg.py index deef8b09808628510382e2f70bf7bd4c38c2080e..2858bb4c234e77c3f7c84fb2f4aeafe2af8a487e 100644 --- a/examples/simple-dg.py +++ b/examples/simple-dg.py @@ -32,7 +32,7 @@ from pytools.obj_array import ( flat_obj_array, make_obj_array, obj_array_vectorize) from meshmode.mesh import BTAG_ALL, BTAG_NONE # noqa -from meshmode.discretization import DOFArray, freeze, thaw +from meshmode.dof_array import DOFArray, freeze, thaw from meshmode.array_context import PyOpenCLArrayContext, make_loopy_program diff --git a/meshmode/discretization/__init__.py b/meshmode/discretization/__init__.py index 74a2b9d8bdae53170431b9e349db4551230c27d2..c96e9aaec49c98ab80e0500c84bb1ee2d7869541 100644 --- a/meshmode/discretization/__init__.py +++ b/meshmode/discretization/__init__.py @@ -22,20 +22,17 @@ THE SOFTWARE. import numpy as np -from functools import partial -from typing import Optional - -from pytools import memoize_in, memoize_method, single_valued -from pytools.obj_array import make_obj_array, obj_array_vectorize +from pytools import memoize_in, memoize_method +from pytools.obj_array import make_obj_array from meshmode.array_context import ArrayContext, make_loopy_program +# underscored because it shouldn't be imported from here. +from meshmode.dof_array import DOFArray as _DOFArray + __doc__ = """ .. autoclass:: ElementGroupBase .. autoclass:: InterpolatoryElementGroupBase .. autoclass:: ElementGroupFactory -.. autoclass:: DOFArray -.. autofunction:: thaw -.. autofunction:: freeze .. autoclass:: Discretization """ @@ -173,89 +170,6 @@ class OrderBasedGroupFactory(ElementGroupFactory): # }}} -# {{{ DOFArray - -class DOFArray(np.ndarray): - """This array type is a subclass of :class:`numpy.ndarray` that only - offers :meth:`from_list` as additional functionality. Its intended use - is for degree-of-freedom arrays associated with a discretization, - with one entry per element group. - - The main purpose of this class is to better describe the data structure, - i.e. when a :class:`DOFArray` occurs inside of a numpy object array, - the level representing the array of element groups will be more - easily recognized. - - .. attribute:: entry_dtype - .. automethod:: from_list - """ - - # Follows https://numpy.org/devdocs/user/basics.subclassing.html - - def __new__(cls, actx: Optional[ArrayContext], input_array): - if not (actx is None or isinstance(actx, ArrayContext)): - raise TypeError("actx must be of type ArrayContext") - - result = np.asarray(input_array).view(cls) - if len(result.shape) != 1: - raise ValueError("DOFArray instances must have one-dimensional " - "shape, with one entry per element group") - - result.array_context = actx - return result - - def __array_finalize__(self, obj): - if obj is None: - return - self.array_context = getattr(obj, 'array_context', None) - - @property - def entry_dtype(self): - return single_valued(subary.dtype for subary in self.flat) - - @classmethod - def from_list(cls, actx: Optional[ArrayContext], res_list): - if not (actx is None or isinstance(actx, ArrayContext)): - raise TypeError("actx must be of type ArrayContext") - - result = np.empty((len(res_list),), dtype=object).view(cls) - result[:] = res_list - result.array_context = actx - return result - - -def thaw(actx: ArrayContext, ary): - if (isinstance(ary, np.ndarray) - and ary.dtype.char == "O" - and not isinstance(ary, DOFArray)): - return obj_array_vectorize(partial(thaw, actx), ary) - - if ary.array_context is not None: - raise ValueError("DOFArray passed to thaw is not frozen") - - return DOFArray.from_list(actx, [ - actx.thaw(subary) - for subary in ary - ]) - - -def freeze(ary): - if (isinstance(ary, np.ndarray) - and ary.dtype.char == "O" - and not isinstance(ary, DOFArray)): - return obj_array_vectorize(freeze, ary) - - if ary.array_context is None: - raise ValueError("DOFArray passed to freeze is already frozen") - - return DOFArray.from_list(None, [ - ary.array_context.freeze(subary) - for subary in ary - ]) - -# }}} - - class Discretization(object): """An unstructured composite discretization. @@ -276,16 +190,13 @@ class Discretization(object): .. automethod:: empty_like .. automethod:: zeros_like - .. method:: nodes() - - An object array of shape ``(ambient_dim,)`` containing - :class:`DOFArray`s of node coordinates. + .. automethod:: nodes() .. method:: num_reference_derivative(queue, ref_axes, vec) .. method:: quad_weights(queue) - A :class:`DOFArray` with quadrature weights. + A :class:`~meshmode.dof_array.DOFArray` with quadrature weights. """ def __init__(self, actx: ArrayContext, mesh, group_factory, @@ -335,12 +246,12 @@ class Discretization(object): else: dtype = np.dtype(dtype) - return DOFArray.from_list(actx, [ + return _DOFArray.from_list(actx, [ creation_func(shape=(grp.nelements, grp.nunit_dofs), dtype=dtype) for grp in self.groups]) def empty(self, actx: ArrayContext, dtype=None): - """Return an empty DOF vector. + """Return an empty :class:`~meshmode.dof_array.DOFArray`. :arg dtype: type special value 'c' will result in a vector of dtype :attr:`self.complex_dtype`. If @@ -353,7 +264,7 @@ class Discretization(object): return self._new_array(actx, actx.empty, dtype=dtype) def zeros(self, actx: ArrayContext, dtype=None): - """Return a zero-initilaized DOF vector. + """Return a zero-initialized :class:`~meshmode.dof_array.DOFArray`. :arg dtype: type special value 'c' will result in a vector of dtype :attr:`self.complex_dtype`. If @@ -365,10 +276,10 @@ class Discretization(object): return self._new_array(actx, actx.zeros, dtype=dtype) - def empty_like(self, array: DOFArray): + def empty_like(self, array: _DOFArray): return self.empty(array.array_context, dtype=array.entry_dtype) - def zeros_like(self, array: DOFArray): + def zeros_like(self, array: _DOFArray): return self.zeros(array.array_context, dtype=array.entry_dtype) def num_reference_derivative(self, ref_axes, vec): @@ -394,7 +305,7 @@ class Discretization(object): return mat - return DOFArray.from_list(actx, [ + return _DOFArray.from_list(actx, [ actx.call_loopy( prg(), diff_mat=actx.from_numpy(get_mat(grp)), vec=vec[grp.index] )["result"] @@ -411,7 +322,7 @@ class Discretization(object): actx = self._setup_actx - return DOFArray(None, [ + return _DOFArray(None, [ actx.freeze( actx.call_loopy( prg(), weights=actx.from_numpy(grp.weights) @@ -420,6 +331,11 @@ class Discretization(object): @memoize_method def nodes(self): + """ + :returns: object array of shape ``(ambient_dim,)`` containing + :class:`~meshmode.dof_array.DOFArray`s of node coordinates. + """ + @memoize_in(self, "nodes_prg") def prg(): return make_loopy_program( @@ -436,7 +352,7 @@ class Discretization(object): actx = self._setup_actx return make_obj_array([ - DOFArray.from_list(None, [ + _DOFArray.from_list(None, [ actx.freeze( actx.call_loopy( prg(), diff --git a/meshmode/discretization/connection/direct.py b/meshmode/discretization/connection/direct.py index 018e651fd8d3e7489ec99f62d1af5280287acf24..d7616bf00c3fc0fa1b212e312179463341ca0f24 100644 --- a/meshmode/discretization/connection/direct.py +++ b/meshmode/discretization/connection/direct.py @@ -353,7 +353,7 @@ class DirectDiscretizationConnection(DiscretizationConnection): return knl - from meshmode.discretization import DOFArray + from meshmode.dof_array import DOFArray if not isinstance(ary, DOFArray): raise TypeError("non-array passed to discretization connection") diff --git a/meshmode/discretization/visualization.py b/meshmode/discretization/visualization.py index 604e85636b03ac83a1b3e9421c812867d54c63ab..da1dddb0309e45e6ae020f064f7184d3d81b5315 100644 --- a/meshmode/discretization/visualization.py +++ b/meshmode/discretization/visualization.py @@ -22,7 +22,7 @@ THE SOFTWARE. import numpy as np from pytools import memoize_method, Record -from meshmode.discretization import DOFArray +from meshmode.dof_array import DOFArray __doc__ = """ diff --git a/meshmode/dof_array.py b/meshmode/dof_array.py new file mode 100644 index 0000000000000000000000000000000000000000..4e3927b57b78a01371e5056a51d219cdb559abd1 --- /dev/null +++ b/meshmode/dof_array.py @@ -0,0 +1,134 @@ +__copyright__ = "Copyright (C) 2020 Andreas Kloeckner" + +__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 +from typing import Optional +from functools import partial + +from pytools import single_valued +from pytools.obj_array import obj_array_vectorize + +from meshmode.array_context import ArrayContext + +__doc__ = """ +.. autoclass:: DOFArray +.. autofunction:: thaw +.. autofunction:: freeze +.. autofunction:: flatten +.. autofunction:: unflatten +""" + + +# {{{ DOFArray + +class DOFArray(np.ndarray): + """This array type is a subclass of :class:`numpy.ndarray` intended to hold + degree-of-freedom arrays for use with + :class:`~meshmode.discretization.Discretization`, + with one entry in the :class:`DOFArray` per element group. + It is derived from :class:`numpy.ndarray` with dtype object ("an object array") + + The main purpose of this class is to better describe the data structure, + i.e. when a :class:`DOFArray` occurs inside of further numpy object array, + the level representing the array of element groups can be recognized (by + people and programs). + + .. attribute:: array_context + .. attribute:: entry_dtype + .. automethod:: from_list + """ + + # Follows https://numpy.org/devdocs/user/basics.subclassing.html + + def __new__(cls, actx: Optional[ArrayContext], input_array): + if not (actx is None or isinstance(actx, ArrayContext)): + raise TypeError("actx must be of type ArrayContext") + + result = np.asarray(input_array).view(cls) + if len(result.shape) != 1: + raise ValueError("DOFArray instances must have one-dimensional " + "shape, with one entry per element group") + + result.array_context = actx + return result + + def __array_finalize__(self, obj): + if obj is None: + return + self.array_context = getattr(obj, 'array_context', None) + + @property + def entry_dtype(self): + return single_valued(subary.dtype for subary in self.flat) + + @classmethod + def from_list(cls, actx: Optional[ArrayContext], res_list): + if not (actx is None or isinstance(actx, ArrayContext)): + raise TypeError("actx must be of type ArrayContext") + + result = np.empty((len(res_list),), dtype=object).view(cls) + result[:] = res_list + result.array_context = actx + return result + +# }}} + + +def thaw(actx: ArrayContext, ary): + if (isinstance(ary, np.ndarray) + and ary.dtype.char == "O" + and not isinstance(ary, DOFArray)): + return obj_array_vectorize(partial(thaw, actx), ary) + + if ary.array_context is not None: + raise ValueError("DOFArray passed to thaw is not frozen") + + return DOFArray.from_list(actx, [ + actx.thaw(subary) + for subary in ary + ]) + + +def freeze(ary): + if (isinstance(ary, np.ndarray) + and ary.dtype.char == "O" + and not isinstance(ary, DOFArray)): + return obj_array_vectorize(freeze, ary) + + if ary.array_context is None: + raise ValueError("DOFArray passed to freeze is already frozen") + + return DOFArray.from_list(None, [ + ary.array_context.freeze(subary) + for subary in ary + ]) + + +def flatten_dof_array(ary: DOFArray): + pass + + +def unflatten_dof_array(actx: ArrayContext, ary): + pass + + +# vim: foldmethod=marker