Exemplo n.º 1
0
    def __init__(self, function_space, val=None, name=None, dtype=valuetype):
        """
        :param function_space: the :class:`.FunctionSpace`, or
            :class:`.MixedFunctionSpace` on which to build this
            :class:`Function`.

            Alternatively, another :class:`Function` may be passed here and its function space
            will be used to build this :class:`Function`.
        :param val: NumPy array-like (or :class:`pyop2.Dat`) providing initial values (optional).
        :param name: user-defined name for this :class:`Function` (optional).
        :param dtype: optional data type for this :class:`Function`
               (defaults to ``valuetype``).
        """
        assert isinstance(function_space, (functionspaceimpl.FunctionSpace,
                                           functionspaceimpl.MixedFunctionSpace)), \
            "Can't make a CoordinatelessFunction defined on a " + str(type(function_space))

        ufl.Coefficient.__init__(self, function_space.ufl_element())

        self._function_space = function_space
        self.uid = utils._new_uid()
        self._name = name or 'function_%d' % self.uid
        self._label = "a function"
        self._split = None

        if isinstance(val, (op2.Dat, op2.DatView)):
            self.dat = val
        else:
            self.dat = function_space.make_dat(val,
                                               dtype,
                                               self.name(),
                                               uid=self.uid)
Exemplo n.º 2
0
    def __init__(self, function_space, val=None, name=None, dtype=valuetype):
        """
        :param function_space: the :class:`.FunctionSpace`, or
            :class:`.MixedFunctionSpace` on which to build this
            :class:`Function`.

            Alternatively, another :class:`Function` may be passed here and its function space
            will be used to build this :class:`Function`.
        :param val: NumPy array-like (or :class:`pyop2.Dat`) providing initial values (optional).
        :param name: user-defined name for this :class:`Function` (optional).
        :param dtype: optional data type for this :class:`Function`
               (defaults to ``valuetype``).
        """
        assert isinstance(function_space, (functionspaceimpl.FunctionSpace,
                                           functionspaceimpl.MixedFunctionSpace)), \
            "Can't make a CoordinatelessFunction defined on a " + str(type(function_space))

        ufl.Coefficient.__init__(self, function_space.ufl_element())

        self.comm = function_space.comm
        self._function_space = function_space
        self.uid = utils._new_uid()
        self._name = name or 'function_%d' % self.uid
        self._label = "a function"
        self._split = None

        if isinstance(val, (op2.Dat, op2.DatView)):
            assert val.comm == self.comm
            self.dat = val
        else:
            self.dat = function_space.make_dat(val, dtype, self.name(), uid=self.uid)
Exemplo n.º 3
0
 def __new__(cls, element):
     """Create mesh geometry object."""
     utils._init()
     mesh = super(MeshGeometry, cls).__new__(cls)
     mesh.uid = utils._new_uid()
     assert isinstance(element, ufl.FiniteElementBase)
     ufl.Mesh.__init__(mesh, element, ufl_id=mesh.uid)
     return mesh
Exemplo n.º 4
0
 def make_dat(self, val=None, valuetype=None, name=None, uid=None):
     """Return a newly allocated :class:`pyop2.MixedDat` defined on the
     :attr:`dof_dset` of this :class:`MixedFunctionSpace`."""
     if val is not None:
         assert len(val) == len(self)
     else:
         val = [None for _ in self]
     return op2.MixedDat(s.make_dat(v, valuetype, "%s[cmpt-%d]" % (name, i), utils._new_uid())
                         for i, (s, v) in enumerate(zip(self._spaces, val)))
Exemplo n.º 5
0
 def make_dat(self, val=None, valuetype=None, name=None, uid=None):
     """Return a newly allocated :class:`pyop2.MixedDat` defined on the
     :attr:`dof_dset` of this :class:`MixedFunctionSpace`."""
     if val is not None:
         assert len(val) == len(self)
     else:
         val = [None for _ in self]
     return op2.MixedDat(s.make_dat(v, valuetype, "%s[cmpt-%d]" % (name, i), utils._new_uid())
                         for i, (s, v) in enumerate(zip(self._spaces, val)))
Exemplo n.º 6
0
    def __init__(self, function_space, val=None, name=None, dtype=valuetype):
        """
        :param function_space: the :class:`.FunctionSpace`, :class:`.VectorFunctionSpace`
            or :class:`.MixedFunctionSpace` on which to build this :class:`Function`.
            Alternatively, another :class:`Function` may be passed here and its function space
            will be used to build this :class:`Function`.
        :param val: NumPy array-like (or :class:`op2.Dat`) providing initial values (optional).
        :param name: user-defined name for this :class:`Function` (optional).
        :param dtype: optional data type for this :class:`Function`
               (defaults to :data:`valuetype`).
        """

        if isinstance(function_space, Function):
            self._function_space = function_space._function_space
        elif isinstance(function_space, functionspace.FunctionSpaceBase):
            self._function_space = function_space
        else:
            raise NotImplementedError("Can't make a Function defined on a " +
                                      str(type(function_space)))

        ufl.Coefficient.__init__(self, self._function_space.ufl_element())

        self._label = "a function"
        self.uid = utils._new_uid()
        self._name = name or 'function_%d' % self.uid

        if isinstance(val, (op2.Dat, op2.DatView)):
            self.dat = val
        else:
            self.dat = self._function_space.make_dat(val,
                                                     dtype,
                                                     self._name,
                                                     uid=self.uid)

        self._repr = None
        self._split = None

        if cachetools:
            # LRU cache for expressions assembled onto this function
            self._expression_cache = cachetools.LRUCache(maxsize=50)
        else:
            self._expression_cache = None
        if isinstance(function_space, Function):
            self.assign(function_space)
Exemplo n.º 7
0
    def __init__(self, function_space, val=None, name=None, dtype=valuetype):
        """
        :param function_space: the :class:`.FunctionSpace`, :class:`.VectorFunctionSpace`
            or :class:`.MixedFunctionSpace` on which to build this :class:`Function`.
            Alternatively, another :class:`Function` may be passed here and its function space
            will be used to build this :class:`Function`.
        :param val: NumPy array-like (or :class:`op2.Dat`) providing initial values (optional).
        :param name: user-defined name for this :class:`Function` (optional).
        :param dtype: optional data type for this :class:`Function`
               (defaults to :data:`valuetype`).
        """

        if isinstance(function_space, Function):
            self._function_space = function_space._function_space
        elif isinstance(function_space, functionspace.FunctionSpaceBase):
            self._function_space = function_space
        else:
            raise NotImplementedError("Can't make a Function defined on a "
                                      + str(type(function_space)))

        ufl.Coefficient.__init__(self, self._function_space.ufl_element())

        self._label = "a function"
        self.uid = utils._new_uid()
        self._name = name or 'function_%d' % self.uid

        if isinstance(val, (op2.Dat, op2.DatView)):
            self.dat = val
        else:
            self.dat = self._function_space.make_dat(val, dtype,
                                                     self._name, uid=self.uid)

        self._repr = None
        self._split = None

        if cachetools:
            # LRU cache for expressions assembled onto this function
            self._expression_cache = cachetools.LRUCache(maxsize=50)
        else:
            self._expression_cache = None
        if isinstance(function_space, Function):
            self.assign(function_space)
Exemplo n.º 8
0
    def __init__(self, function_space, val=None, name=None, dtype=ScalarType):
        r"""
        :param function_space: the :class:`.FunctionSpace`, or
            :class:`.MixedFunctionSpace` on which to build this
            :class:`Function`.

            Alternatively, another :class:`Function` may be passed here and its function space
            will be used to build this :class:`Function`.
        :param val: NumPy array-like (or :class:`pyop2.Dat` or
            :class:`~.Vector`) providing initial values (optional).
            This :class:`Function` will share data with the provided
            value.
        :param name: user-defined name for this :class:`Function` (optional).
        :param dtype: optional data type for this :class:`Function`
               (defaults to ``ScalarType``).
        """
        assert isinstance(function_space, (functionspaceimpl.FunctionSpace,
                                           functionspaceimpl.MixedFunctionSpace)), \
            "Can't make a CoordinatelessFunction defined on a " + str(type(function_space))

        ufl.Coefficient.__init__(self, function_space.ufl_function_space())

        self.comm = function_space.comm
        self._function_space = function_space
        self.uid = utils._new_uid()
        self._name = name or 'function_%d' % self.uid
        self._label = "a function"

        if isinstance(val, vector.Vector):
            # Allow constructing using a vector.
            val = val.dat
        if isinstance(val, (op2.Dat, op2.DatView, op2.MixedDat, op2.Global)):
            assert val.comm == self.comm
            self.dat = val
        else:
            self.dat = function_space.make_dat(val,
                                               dtype,
                                               self.name(),
                                               uid=self.uid)
Exemplo n.º 9
0
    def __init__(self, meshfile, **kwargs):
        """Construct a mesh object.

        Meshes may either be created by reading from a mesh file, or by
        providing a PETSc DMPlex object defining the mesh topology.

        :param meshfile: Mesh file name (or DMPlex object) defining
               mesh topology.  See below for details on supported mesh
               formats.
        :param dim: optional specification of the geometric dimension
               of the mesh (ignored if not reading from mesh file).
               If not supplied the geometric dimension is deduced from
               the topological dimension of entities in the mesh.
        :param reorder: optional flag indicating whether to reorder
               meshes for better cache locality.  If not supplied the
               default value in :data:`parameters["reorder_meshes"]`
               is used.
        :param periodic_coords: optional numpy array of coordinates
               used to replace those in the mesh object.  These are
               only supported in 1D and must have enough entries to be
               used as a DG1 field on the mesh.  Not supported when
               reading from file.

        When the mesh is read from a file the following mesh formats
        are supported (determined, case insensitively, from the
        filename extension):

        * GMSH: with extension `.msh`
        * Exodus: with extension `.e`, `.exo`
        * CGNS: with extension `.cgns`
        * Triangle: with extension `.node`

        .. note::

            When the mesh is created directly from a DMPlex object,
            the :data:`dim` parameter is ignored (the DMPlex already
            knows its geometric and topological dimensions).

        """

        utils._init()

        geometric_dim = kwargs.get("dim", None)
        reorder = kwargs.get("reorder", parameters["reorder_meshes"])
        periodic_coords = kwargs.get("periodic_coords", None)
        distribute = kwargs.get("distribute", True)

        if isinstance(meshfile, PETSc.DMPlex):
            name = "plexmesh"
            plex = meshfile
        else:
            name = meshfile
            basename, ext = os.path.splitext(meshfile)

            if periodic_coords is not None:
                raise RuntimeError("Periodic coordinates are unsupported when reading from file")
            if ext.lower() in ['.e', '.exo']:
                plex = _from_exodus(meshfile)
            elif ext.lower() == '.cgns':
                plex = _from_cgns(meshfile)
            elif ext.lower() == '.msh':
                plex = _from_gmsh(meshfile)
            elif ext.lower() == '.node':
                plex = _from_triangle(meshfile, geometric_dim)
            else:
                raise RuntimeError("Mesh file %s has unknown format '%s'."
                                   % (meshfile, ext[1:]))

        # Mark exterior and interior facets
        # Note.  This must come before distribution, because otherwise
        # DMPlex will consider facets on the domain boundary to be
        # exterior, which is wrong.
        with timed_region("Mesh: label facets"):
            label_boundary = op2.MPI.comm.size == 1 or distribute
            dmplex.label_facets(plex, label_boundary=label_boundary)

        # Distribute the dm to all ranks
        if op2.MPI.comm.size > 1 and distribute:
            # We distribute with overlap zero, in case we're going to
            # refine this mesh in parallel.  Later, when we actually use
            # it, we grow the halo.
            plex.distribute(overlap=0)

        # A cache of function spaces that have been built on this mesh
        self._cache = {}
        self.parent = None

        self.name = name
        self._plex = plex
        self.uid = utils._new_uid()

        topological_dim = self._plex.getDimension()
        if geometric_dim is None:
            geometric_dim = topological_dim

        cStart, cEnd = self._plex.getHeightStratum(0)  # cells
        cell_facets = self._plex.getConeSize(cStart)

        self._ufl_cell = ufl.Cell(fiat_utils._cells[topological_dim][cell_facets],
                                  geometric_dimension=geometric_dim)
        self._ufl_domain = ufl.Domain(self.ufl_cell(), data=self)
        self._grown_halos = False

        def callback(self):
            import firedrake.function as function
            import firedrake.functionspace as functionspace

            del self._callback
            if op2.MPI.comm.size > 1:
                self._plex.distributeOverlap(1)
            self._grown_halos = True

            if reorder:
                with timed_region("Mesh: reorder"):
                    old_to_new = self._plex.getOrdering(PETSc.Mat.OrderingType.RCM).indices
                    reordering = np.empty_like(old_to_new)
                    reordering[old_to_new] = np.arange(old_to_new.size, dtype=old_to_new.dtype)
            else:
                # No reordering
                reordering = None

            # Mark OP2 entities and derive the resulting Plex renumbering
            with timed_region("Mesh: renumbering"):
                dmplex.mark_entity_classes(self._plex)
                self._entity_classes = dmplex.get_entity_classes(self._plex)
                self._plex_renumbering = dmplex.plex_renumbering(self._plex,
                                                                 self._entity_classes,
                                                                 reordering)

            with timed_region("Mesh: cell numbering"):
                # Derive a cell numbering from the Plex renumbering
                entity_dofs = np.zeros(topological_dim+1, dtype=np.int32)
                entity_dofs[-1] = 1

                self._cell_numbering = self._plex.createSection([1], entity_dofs,
                                                                perm=self._plex_renumbering)
                entity_dofs[:] = 0
                entity_dofs[0] = 1
                self._vertex_numbering = self._plex.createSection([1], entity_dofs,
                                                                  perm=self._plex_renumbering)

            # Note that for bendy elements, this needs to change.
            with timed_region("Mesh: coordinate field"):
                if periodic_coords is not None:
                    if self.ufl_cell().geometric_dimension() != 1:
                        raise NotImplementedError("Periodic coordinates in more than 1D are unsupported")
                    # We've been passed a periodic coordinate field, so use that.
                    self._coordinate_fs = functionspace.VectorFunctionSpace(self, "DG", 1)
                    self.coordinates = function.Function(self._coordinate_fs,
                                                         val=periodic_coords,
                                                         name="Coordinates")
                else:
                    self._coordinate_fs = functionspace.VectorFunctionSpace(self, "Lagrange", 1)

                    coordinates = dmplex.reordered_coords(self._plex, self._coordinate_fs._global_numbering,
                                                          (self.num_vertices(), geometric_dim))
                    self.coordinates = function.Function(self._coordinate_fs,
                                                         val=coordinates,
                                                         name="Coordinates")
            self._ufl_domain = ufl.Domain(self.coordinates)
            # Build a new ufl element for this function space with the
            # correct domain.  This is necessary since this function space
            # is in the cache and will be picked up by later
            # VectorFunctionSpace construction.
            self._coordinate_fs._ufl_element = self._coordinate_fs.ufl_element().reconstruct(domain=self.ufl_domain())
            # HACK alert!
            # Replace coordinate Function by one that has a real domain on it (but don't copy values)
            self.coordinates = function.Function(self._coordinate_fs, val=self.coordinates.dat)
            # Add subdomain_data to the measure objects we store with
            # the mesh.  These are weakrefs for consistency with the
            # "global" measure objects
            self._dx = ufl.Measure('cell', subdomain_data=weakref.ref(self.coordinates))
            self._ds = ufl.Measure('exterior_facet', subdomain_data=weakref.ref(self.coordinates))
            self._dS = ufl.Measure('interior_facet', subdomain_data=weakref.ref(self.coordinates))
            # Set the subdomain_data on all the default measures to this
            # coordinate field.
            # We don't set the domain on the measure since this causes
            # an uncollectable reference in the global space (dx is
            # global).  Furthermore, it's never used anyway.
            for measure in [ufl.dx, ufl.ds, ufl.dS]:
                measure._subdomain_data = weakref.ref(self.coordinates)
        self._callback = callback
Exemplo n.º 10
0
    def __init__(self, meshfile, **kwargs):
        """Construct a mesh object.

        Meshes may either be created by reading from a mesh file, or by
        providing a PETSc DMPlex object defining the mesh topology.

        :param meshfile: Mesh file name (or DMPlex object) defining
               mesh topology.  See below for details on supported mesh
               formats.
        :param dim: optional specification of the geometric dimension
               of the mesh (ignored if not reading from mesh file).
               If not supplied the geometric dimension is deduced from
               the topological dimension of entities in the mesh.
        :param reorder: optional flag indicating whether to reorder
               meshes for better cache locality.  If not supplied the
               default value in :data:`parameters["reorder_meshes"]`
               is used.
        :param periodic_coords: optional numpy array of coordinates
               used to replace those in the mesh object.  These are
               only supported in 1D and must have enough entries to be
               used as a DG1 field on the mesh.  Not supported when
               reading from file.

        When the mesh is read from a file the following mesh formats
        are supported (determined, case insensitively, from the
        filename extension):

        * GMSH: with extension `.msh`
        * Exodus: with extension `.e`, `.exo`
        * CGNS: with extension `.cgns`
        * Triangle: with extension `.node`

        .. note::

            When the mesh is created directly from a DMPlex object,
            the :data:`dim` parameter is ignored (the DMPlex already
            knows its geometric and topological dimensions).

        """

        utils._init()

        geometric_dim = kwargs.get("dim", None)
        reorder = kwargs.get("reorder", parameters["reorder_meshes"])
        periodic_coords = kwargs.get("periodic_coords", None)
        distribute = kwargs.get("distribute", True)

        if isinstance(meshfile, PETSc.DMPlex):
            name = "plexmesh"
            plex = meshfile
        else:
            name = meshfile
            basename, ext = os.path.splitext(meshfile)

            if periodic_coords is not None:
                raise RuntimeError(
                    "Periodic coordinates are unsupported when reading from file"
                )
            if ext.lower() in ['.e', '.exo']:
                plex = _from_exodus(meshfile)
            elif ext.lower() == '.cgns':
                plex = _from_cgns(meshfile)
            elif ext.lower() == '.msh':
                plex = _from_gmsh(meshfile)
            elif ext.lower() == '.node':
                plex = _from_triangle(meshfile, geometric_dim)
            else:
                raise RuntimeError("Mesh file %s has unknown format '%s'." %
                                   (meshfile, ext[1:]))

        # Mark exterior and interior facets
        # Note.  This must come before distribution, because otherwise
        # DMPlex will consider facets on the domain boundary to be
        # exterior, which is wrong.
        with timed_region("Mesh: label facets"):
            label_boundary = op2.MPI.comm.size == 1 or distribute
            dmplex.label_facets(plex, label_boundary=label_boundary)

        # Distribute the dm to all ranks
        if op2.MPI.comm.size > 1 and distribute:
            # We distribute with overlap zero, in case we're going to
            # refine this mesh in parallel.  Later, when we actually use
            # it, we grow the halo.
            plex.distribute(overlap=0)

        # A cache of function spaces that have been built on this mesh
        self._cache = {}
        self.parent = None

        self.name = name
        self._plex = plex
        self.uid = utils._new_uid()

        topological_dim = self._plex.getDimension()
        if geometric_dim is None:
            geometric_dim = topological_dim

        cStart, cEnd = self._plex.getHeightStratum(0)  # cells
        cell_facets = self._plex.getConeSize(cStart)

        self._ufl_cell = ufl.Cell(
            fiat_utils._cells[topological_dim][cell_facets],
            geometric_dimension=geometric_dim)
        self._ufl_domain = ufl.Domain(self.ufl_cell(), data=self)
        self._grown_halos = False

        def callback(self):
            import firedrake.function as function
            import firedrake.functionspace as functionspace

            del self._callback
            if op2.MPI.comm.size > 1:
                self._plex.distributeOverlap(1)
            self._grown_halos = True

            if reorder:
                with timed_region("Mesh: reorder"):
                    old_to_new = self._plex.getOrdering(
                        PETSc.Mat.OrderingType.RCM).indices
                    reordering = np.empty_like(old_to_new)
                    reordering[old_to_new] = np.arange(old_to_new.size,
                                                       dtype=old_to_new.dtype)
            else:
                # No reordering
                reordering = None

            # Mark OP2 entities and derive the resulting Plex renumbering
            with timed_region("Mesh: renumbering"):
                dmplex.mark_entity_classes(self._plex)
                self._entity_classes = dmplex.get_entity_classes(self._plex)
                self._plex_renumbering = dmplex.plex_renumbering(
                    self._plex, self._entity_classes, reordering)

            with timed_region("Mesh: cell numbering"):
                # Derive a cell numbering from the Plex renumbering
                entity_dofs = np.zeros(topological_dim + 1, dtype=np.int32)
                entity_dofs[-1] = 1

                self._cell_numbering = self._plex.createSection(
                    [1], entity_dofs, perm=self._plex_renumbering)
                entity_dofs[:] = 0
                entity_dofs[0] = 1
                self._vertex_numbering = self._plex.createSection(
                    [1], entity_dofs, perm=self._plex_renumbering)

            # Note that for bendy elements, this needs to change.
            with timed_region("Mesh: coordinate field"):
                if periodic_coords is not None:
                    if self.ufl_cell().geometric_dimension() != 1:
                        raise NotImplementedError(
                            "Periodic coordinates in more than 1D are unsupported"
                        )
                    # We've been passed a periodic coordinate field, so use that.
                    self._coordinate_fs = functionspace.VectorFunctionSpace(
                        self, "DG", 1)
                    self.coordinates = function.Function(self._coordinate_fs,
                                                         val=periodic_coords,
                                                         name="Coordinates")
                else:
                    self._coordinate_fs = functionspace.VectorFunctionSpace(
                        self, "Lagrange", 1)

                    coordinates = dmplex.reordered_coords(
                        self._plex, self._coordinate_fs._global_numbering,
                        (self.num_vertices(), geometric_dim))
                    self.coordinates = function.Function(self._coordinate_fs,
                                                         val=coordinates,
                                                         name="Coordinates")
            self._ufl_domain = ufl.Domain(self.coordinates)
            # Build a new ufl element for this function space with the
            # correct domain.  This is necessary since this function space
            # is in the cache and will be picked up by later
            # VectorFunctionSpace construction.
            self._coordinate_fs._ufl_element = self._coordinate_fs.ufl_element(
            ).reconstruct(domain=self.ufl_domain())
            # HACK alert!
            # Replace coordinate Function by one that has a real domain on it (but don't copy values)
            self.coordinates = function.Function(self._coordinate_fs,
                                                 val=self.coordinates.dat)
            # Add subdomain_data to the measure objects we store with
            # the mesh.  These are weakrefs for consistency with the
            # "global" measure objects
            self._dx = ufl.Measure('cell',
                                   subdomain_data=weakref.ref(
                                       self.coordinates))
            self._ds = ufl.Measure('exterior_facet',
                                   subdomain_data=weakref.ref(
                                       self.coordinates))
            self._dS = ufl.Measure('interior_facet',
                                   subdomain_data=weakref.ref(
                                       self.coordinates))
            # Set the subdomain_data on all the default measures to this
            # coordinate field.
            # We don't set the domain on the measure since this causes
            # an uncollectable reference in the global space (dx is
            # global).  Furthermore, it's never used anyway.
            for measure in [ufl.dx, ufl.ds, ufl.dS]:
                measure._subdomain_data = weakref.ref(self.coordinates)

        self._callback = callback