Example #1
0
def get_node_set(mesh, nodes_per_entity):
    """Get the :class:`node set <pyop2.Set>`.

    :arg mesh: The mesh to use.
    :arg nodes_per_entity: The number of function space nodes per
        topological entity.
    :returns: A :class:`pyop2.Set` for the function space nodes.
    """
    global_numbering = get_global_numbering(mesh, nodes_per_entity)
    # Use a DM to create the halo SFs
    dm = PETSc.DMShell().create(mesh.comm)
    dm.setPointSF(mesh._plex.getPointSF())
    dm.setDefaultSection(global_numbering)
    node_classes = tuple(numpy.dot(nodes_per_entity, mesh._entity_classes))
    node_set = op2.Set(node_classes, halo=halo_mod.Halo(dm), comm=mesh.comm)
    # Don't need it any more, explicitly destroy.
    dm.destroy()
    extruded = bool(mesh.layers)
    if extruded:
        node_set = op2.ExtrudedSet(node_set, layers=mesh.layers)

    assert global_numbering.getStorageSize() == node_set.total_size
    if not extruded and node_set.total_size >= (1 <<
                                                (IntType.itemsize * 8 - 4)):
        raise RuntimeError(
            "Problems with more than %d nodes per process unsupported",
            (1 << (IntType.itemsize * 8 - 4)))
    return node_set
Example #2
0
 def _dm(self):
     """A PETSc DM describing the data layout for fieldsplit solvers."""
     dm = PETSc.DMShell().create()
     dm.setAttr('__fs__', weakref.ref(self))
     dm.setCreateFieldDecomposition(self.create_field_decomp)
     dm.setCreateSubDM(self.create_subdm)
     dm.setGlobalVector(self.dof_dset.layout_vec)
     return dm
Example #3
0
 def _dm(self):
     """A PETSc DM describing the data layout for this FunctionSpace."""
     dm = PETSc.DMShell().create()
     dm.setAttr('__fs__', weakref.ref(self))
     dm.setPointSF(self.mesh()._plex.getPointSF())
     dm.setDefaultSection(self._shared_data.global_numbering)
     dm.setGlobalVector(self.dof_dset.layout_vec)
     return dm
Example #4
0
    def __new__(cls, spaces, name=None):
        """
        :param spaces: a list (or tuple) of :class:`FunctionSpace`\s

        The function space may be created as ::

            V = MixedFunctionSpace(spaces)

        ``spaces`` may consist of multiple occurances of the same space: ::

            P1  = FunctionSpace(mesh, "CG", 1)
            P2v = VectorFunctionSpace(mesh, "Lagrange", 2)

            ME  = MixedFunctionSpace([P2v, P1, P1, P1])
        """

        # Check that function spaces are on the same mesh
        meshes = [space.mesh() for space in spaces]
        for i in xrange(1, len(meshes)):
            if meshes[i] is not meshes[0]:
                raise ValueError(
                    "All function spaces must be defined on the same mesh!")

        # Select mesh
        mesh = meshes[0]

        # Get topological spaces
        spaces = flatten(spaces)
        if mesh is mesh.topology:
            spaces = tuple(spaces)
        else:
            spaces = tuple(space.topological for space in spaces)

        # Ask object from cache
        self = ObjectCached.__new__(cls, mesh, spaces, name)
        if not self._initialized:
            self._spaces = [
                IndexedFunctionSpace(s, i, self) for i, s in enumerate(spaces)
            ]
            self._mesh = mesh.topology
            self._ufl_element = ufl.MixedElement(
                *[fs.ufl_element() for fs in spaces])
            self.name = name or '_'.join(str(s.name) for s in spaces)
            self._initialized = True
            dm = PETSc.DMShell().create()
            with self.make_dat().vec_ro as v:
                dm.setGlobalVector(v.duplicate())
            dm.setAttr('__fs__', weakref.ref(self))
            dm.setCreateFieldDecomposition(self.create_field_decomp)
            dm.setCreateSubDM(self.create_subdm)
            self._dm = dm
            self._ises = self.dof_dset.field_ises
            self._subspaces = []

        if mesh is not mesh.topology:
            self = WithGeometry(self, mesh)
        return self
Example #5
0
    def initialize(self, pc):
        # Make a new DM.
        # Hook up a (new) coarsen routine on that DM.
        # Make a new PC, of type MG.
        # Assign the DM to that PC.

        odm = pc.getDM()
        ctx = get_appctx(odm)

        test, trial = ctx.J.arguments()
        if test.function_space() != trial.function_space():
            raise NotImplementedError("test and trial spaces must be the same")

        prefix = pc.getOptionsPrefix()
        options_prefix = prefix + "pmg_"
        pdm = PETSc.DMShell().create(comm=pc.comm)
        pdm.setOptionsPrefix(options_prefix)

        # Get the coarse degree from PETSc options
        self.coarse_degree = PETSc.Options(options_prefix).getInt("mg_coarse_degree", default=1)

        # Construct a list with the elements we'll be using
        V = test.function_space()
        ele = V.ufl_element()
        elements = [ele]
        while True:
            try:
                ele_ = self.coarsen_element(ele)
                assert ele_.value_shape() == ele.value_shape()
                ele = ele_
            except ValueError:
                break
            elements.append(ele)

        sf = odm.getPointSF()
        section = odm.getDefaultSection()
        attach_hooks(pdm, level=len(elements)-1, sf=sf, section=section)
        # Now overwrite some routines on the DM
        pdm.setRefine(None)
        pdm.setCoarsen(self.coarsen)
        pdm.setCreateInterpolation(self.create_interpolation)
        # We need this for p-FAS
        pdm.setCreateInjection(self.create_injection)
        pdm.setSNESJacobian(_SNESContext.form_jacobian)
        pdm.setSNESFunction(_SNESContext.form_function)
        pdm.setKSPComputeOperators(_SNESContext.compute_operators)

        set_function_space(pdm, get_function_space(odm))

        parent = get_parent(odm)
        assert parent is not None
        add_hook(parent, setup=partial(push_parent, pdm, parent), teardown=partial(pop_parent, pdm, parent), call_setup=True)
        add_hook(parent, setup=partial(push_appctx, pdm, ctx), teardown=partial(pop_appctx, pdm, ctx), call_setup=True)

        self.ppc = self.configure_pmg(pc, pdm)
        self.ppc.setFromOptions()
        self.ppc.setUp()
Example #6
0
    def __init__(self, spaces, name=None):
        """
        :param spaces: a list (or tuple) of :class:`FunctionSpace`\s

        The function space may be created as ::

            V = MixedFunctionSpace(spaces)

        ``spaces`` may consist of multiple occurances of the same space: ::

            P1  = FunctionSpace(mesh, "CG", 1)
            P2v = VectorFunctionSpace(mesh, "Lagrange", 2)

            ME  = MixedFunctionSpace([P2v, P1, P1, P1])
        """

        if self._initialized:
            return
        self._spaces = [
            IndexedFunctionSpace(s, i, self)
            for i, s in enumerate(flatten(spaces))
        ]
        self._mesh = self._spaces[0].mesh()
        self._ufl_element = ufl.MixedElement(
            *[fs.ufl_element() for fs in self._spaces])
        self.name = name or '_'.join(str(s.name) for s in self._spaces)
        self.rank = 1
        self._index = None
        self._initialized = True
        dm = PETSc.DMShell().create()
        from firedrake.function import Function
        with Function(self).dat.vec_ro as v:
            dm.setGlobalVector(v.duplicate())
        dm.setAttr('__fs__', weakref.ref(self))
        dm.setCreateFieldDecomposition(self.create_field_decomp)
        dm.setCreateSubDM(self.create_subdm)
        self._dm = dm
        self._ises = self.dof_dset.field_ises
        self._subspaces = []
Example #7
0
def get_node_set(mesh, nodes_per_entity):
    """Get the :class:`node set <pyop2.Set>`.

    :arg mesh: The mesh to use.
    :arg nodes_per_entity: The number of function space nodes per
        topological entity.
    :returns: A :class:`pyop2.Set` for the function space nodes.
    """
    global_numbering = get_global_numbering(mesh, nodes_per_entity)
    # Use a DM to create the halo SFs
    dm = PETSc.DMShell().create()
    dm.setPointSF(mesh._plex.getPointSF())
    dm.setDefaultSection(global_numbering)
    node_classes = tuple(numpy.dot(nodes_per_entity, mesh._entity_classes))
    node_set = op2.Set(node_classes, halo=halo_mod.Halo(dm))
    # Don't need it any more, explicitly destroy.
    dm.destroy()
    extruded = bool(mesh.layers)
    if extruded:
        node_set = op2.ExtrudedSet(node_set, layers=mesh.layers)

    assert global_numbering.getStorageSize() == node_set.total_size
    return node_set
Example #8
0
 def __init__(self, dm, section):
     super(Halo, self).__init__()
     # Use a DM to create the halo SFs
     self.dm = PETSc.DMShell().create(dm.comm)
     self.dm.setPointSF(dm.getPointSF())
     self.dm.setDefaultSection(section)
Example #9
0
    def initialize(self, pc):
        # Make a new DM.
        # Hook up a (new) coarsen routine on that DM.
        # Make a new PC, of type MG.
        # Assign the DM to that PC.

        odm = pc.getDM()
        ctx = get_appctx(odm)

        test, trial = ctx.J.arguments()
        if test.function_space() != trial.function_space():
            raise NotImplementedError("test and trial spaces must be the same")

        # Construct a list with the elements we'll be using
        V = test.function_space()
        ele = V.ufl_element()
        elements = [ele]
        while True:
            try:
                ele_ = self.coarsen_element(ele)
                assert ele_.value_shape() == ele.value_shape()
                ele = ele_
            except ValueError:
                break
            elements.append(ele)

        pdm = PETSc.DMShell().create(comm=pc.comm)
        sf = odm.getPointSF()
        section = odm.getDefaultSection()
        attach_hooks(pdm, level=len(elements) - 1, sf=sf, section=section)
        # Now overwrite some routines on the DM
        pdm.setRefine(None)
        pdm.setCoarsen(self.coarsen)
        pdm.setCreateInterpolation(self.create_interpolation)
        pdm.setOptionsPrefix(pc.getOptionsPrefix() + "pmg_")
        set_function_space(pdm, get_function_space(odm))

        parent = get_parent(odm)
        assert parent is not None
        add_hook(parent,
                 setup=partial(push_parent, pdm, parent),
                 teardown=partial(pop_parent, pdm, parent),
                 call_setup=True)
        add_hook(parent,
                 setup=partial(push_appctx, pdm, ctx),
                 teardown=partial(pop_appctx, pdm, ctx),
                 call_setup=True)

        ppc = PETSc.PC().create(comm=pc.comm)
        ppc.setOptionsPrefix(pc.getOptionsPrefix() + "pmg_")
        ppc.setType("mg")
        ppc.setOperators(*pc.getOperators())
        ppc.setDM(pdm)
        ppc.incrementTabLevel(1, parent=pc)

        # PETSc unfortunately requires us to make an ugly hack.
        # We would like to use GMG for the coarse solve, at least
        # sometimes. But PETSc will use this p-DM's getRefineLevels()
        # instead of the getRefineLevels() of the MeshHierarchy to
        # decide how many levels it should use for PCMG applied to
        # the p-MG's coarse problem. So we need to set an option
        # for the user, if they haven't already; I don't know any
        # other way to get PETSc to know this at the right time.
        opts = PETSc.Options(pc.getOptionsPrefix() + "pmg_")
        if "mg_coarse_pc_mg_levels" not in opts:
            opts["mg_coarse_pc_mg_levels"] = odm.getRefineLevel() + 1

        ppc.setFromOptions()
        ppc.setUp()
        self.ppc = ppc
Example #10
0
    def __init__(self, mesh, element, name=None, dim=1, rank=0):
        """
        :param mesh: :class:`Mesh` to build this space on
        :param element: :class:`ufl.FiniteElementBase` to build this space from
        :param name: user-defined name for this space
        :param dim: vector space dimension of a :class:`.VectorFunctionSpace`
        :param rank: rank of the space, not the value rank
        """

        self._ufl_element = element

        # Compute the FIAT version of the UFL element above
        self.fiat_element = fiat_utils.fiat_from_ufl_element(element)

        if isinstance(mesh, mesh_t.ExtrudedMesh):
            # Set up some extrusion-specific things
            # The bottom layer maps will come from element_dof_list
            # dof_count is the total number of dofs in the extruded mesh

            # Get the flattened version of the FIAT element
            self.flattened_element = fiat_utils.FlattenedElement(
                self.fiat_element)

            # Compute the number of DoFs per dimension on top/bottom and sides
            entity_dofs = self.fiat_element.entity_dofs()
            top_dim = mesh._plex.getDimension()
            self._xtr_hdofs = [
                len(entity_dofs[(d, 0)][0]) for d in range(top_dim + 1)
            ]
            self._xtr_vdofs = [
                len(entity_dofs[(d, 1)][0]) for d in range(top_dim + 1)
            ]

            # Compute the dofs per column
            self.dofs_per_column = eutils.compute_extruded_dofs(
                self.fiat_element, self.flattened_element.entity_dofs(),
                mesh._layers)

            # Compute the offset for the extrusion process
            self.offset = eutils.compute_offset(
                self.fiat_element.entity_dofs(),
                self.flattened_element.entity_dofs(),
                self.fiat_element.space_dimension())

            # Compute the top and bottom masks to identify boundary dofs
            #
            # Sorting the keys of the closure entity dofs, the whole cell
            # comes last [-1], before that the horizontal facet [-2], before
            # that vertical facets [-3]. We need the horizontal facets here.
            closure_dofs = self.fiat_element.entity_closure_dofs()
            b_mask = closure_dofs[sorted(closure_dofs.keys())[-2]][0]
            t_mask = closure_dofs[sorted(closure_dofs.keys())[-2]][1]
            self.bt_masks = (b_mask, t_mask)  # conversion to tuple

            self.extruded = True

            self._dofs_per_entity = self.dofs_per_column
        else:
            # If not extruded specific, set things to None/False, etc.
            self.offset = None
            self.bt_masks = None
            self.dofs_per_column = np.zeros(1, np.int32)
            self.extruded = False

            entity_dofs = self.fiat_element.entity_dofs()
            self._dofs_per_entity = [
                len(entity[0]) for d, entity in entity_dofs.iteritems()
            ]

        self.name = name
        self._dim = dim
        self._mesh = mesh
        self._index = None

        dm = PETSc.DMShell().create()
        dm.setAttr('__fs__', weakref.ref(self))
        dm.setPointSF(mesh._plex.getPointSF())
        # Create the PetscSection mapping topological entities to DoFs
        sec = mesh._plex.createSection([1],
                                       self._dofs_per_entity,
                                       perm=mesh._plex_renumbering)
        dm.setDefaultSection(sec)
        self._global_numbering = sec
        self._dm = dm
        self._ises = None
        self._halo = halo.Halo(dm)

        # Compute entity class offsets
        self.dof_classes = [0, 0, 0, 0]
        for d in range(mesh._plex.getDimension() + 1):
            ndofs = self._dofs_per_entity[d]
            for i in range(4):
                self.dof_classes[i] += ndofs * mesh._entity_classes[d, i]

        # Tell the DM about the layout of the global vector
        from firedrake.function import Function
        with Function(self).dat.vec_ro as v:
            self._dm.setGlobalVector(v.duplicate())

        self._node_count = self._global_numbering.getStorageSize()

        self.cell_node_list = mesh.create_cell_node_list(
            self._global_numbering, self.fiat_element)

        if mesh._plex.getStratumSize("interior_facets", 1) > 0:
            self.interior_facet_node_list = \
                dmplex.get_facet_nodes(mesh.interior_facets.facet_cell,
                                       self.cell_node_list)
        else:
            self.interior_facet_node_list = np.array([], dtype=np.int32)

        if mesh._plex.getStratumSize("exterior_facets", 1) > 0:
            self.exterior_facet_node_list = \
                dmplex.get_facet_nodes(mesh.exterior_facets.facet_cell,
                                       self.cell_node_list)
        else:
            self.exterior_facet_node_list = np.array([], dtype=np.int32)

        # Note: this is the function space rank. The value rank may be different.
        self.rank = rank

        # Empty map caches. This is a sui generis cache
        # implementation because of the need to support boundary
        # conditions.
        self._cell_node_map_cache = {}
        self._exterior_facet_map_cache = {}
        self._interior_facet_map_cache = {}
Example #11
0
    def __new__(cls, mesh, element, name=None, shape=()):
        """
        :param mesh: :class:`MeshTopology` to build this space on
        :param element: :class:`ufl.FiniteElementBase` to build this space from
        :param name: user-defined name for this space
        :param shape: shape of a :class:`.VectorFunctionSpace` or :class:`.TensorFunctionSpace`
        """

        assert mesh.ufl_cell() == element.cell()

        self = super(FunctionSpaceBase, cls).__new__(cls, mesh, element, name,
                                                     shape)
        if self._initialized:
            return self

        self._mesh = mesh
        self._ufl_element = element
        self.name = name
        self._shape = shape

        # Compute the FIAT version of the UFL element above
        self.fiat_element = fiat_utils.fiat_from_ufl_element(element)

        entity_dofs = self.fiat_element.entity_dofs()
        dofs_per_entity = mesh.make_dofs_per_plex_entity(entity_dofs)

        self.extruded = bool(mesh.layers)
        self.offset = mesh.make_offset(entity_dofs,
                                       self.fiat_element.space_dimension())

        if mesh.layers:
            # Compute the top and bottom masks to identify boundary dofs
            #
            # Sorting the keys of the closure entity dofs, the whole cell
            # comes last [-1], before that the horizontal facet [-2], before
            # that vertical facets [-3]. We need the horizontal facets here.
            closure_dofs = self.fiat_element.entity_closure_dofs()
            b_mask = closure_dofs[sorted(closure_dofs.keys())[-2]][0]
            t_mask = closure_dofs[sorted(closure_dofs.keys())[-2]][1]
            self.bt_masks = {}
            self.bt_masks["topological"] = (b_mask, t_mask
                                            )  # conversion to tuple
            # Geometric facet dofs
            facet_dofs = horiz_facet_support_dofs(self.fiat_element)
            self.bt_masks["geometric"] = (facet_dofs[0], facet_dofs[1])
        else:
            self.bt_masks = None

        dm = PETSc.DMShell().create()
        dm.setAttr('__fs__', weakref.ref(self))
        dm.setPointSF(mesh._plex.getPointSF())
        # Create the PetscSection mapping topological entities to DoFs
        sec = mesh._plex.createSection([1],
                                       dofs_per_entity,
                                       perm=mesh._plex_renumbering)
        dm.setDefaultSection(sec)
        self._global_numbering = sec
        self._dm = dm
        self._ises = None
        self._halo = halo.Halo(dm)

        # Compute entity class offsets
        self.dof_classes = [0, 0, 0, 0]
        for d in range(mesh._plex.getDimension() + 1):
            ndofs = dofs_per_entity[d]
            for i in range(4):
                self.dof_classes[i] += ndofs * mesh._entity_classes[d, i]

        # Tell the DM about the layout of the global vector
        with self.make_dat().vec_ro as v:
            self._dm.setGlobalVector(v.duplicate())

        self._node_count = self._global_numbering.getStorageSize()

        self.cell_node_list = mesh.make_cell_node_list(self._global_numbering,
                                                       entity_dofs)

        if mesh._plex.getStratumSize("interior_facets", 1) > 0:
            self.interior_facet_node_list = \
                dmplex.get_facet_nodes(mesh.interior_facets.facet_cell,
                                       self.cell_node_list)
        else:
            self.interior_facet_node_list = np.array([], dtype=np.int32)

        if mesh._plex.getStratumSize("exterior_facets", 1) > 0:
            self.exterior_facet_node_list = \
                dmplex.get_facet_nodes(mesh.exterior_facets.facet_cell,
                                       self.cell_node_list)
        else:
            self.exterior_facet_node_list = np.array([], dtype=np.int32)

        # Empty map caches. This is a sui generis cache
        # implementation because of the need to support boundary
        # conditions.
        self._cell_node_map_cache = {}
        self._exterior_facet_map_cache = {}
        self._interior_facet_map_cache = {}

        self._initialized = True
        return self