def __init__(self, space):
        """
        Initialise limiter

        :param space : FunctionSpace instance
        """

        self.P1DG = space
        self.P1CG = FunctionSpace(self.P1DG.mesh(), 'CG',
                                  1)  # for min/max limits
        self.P0 = FunctionSpace(self.P1DG.mesh(), 'DG', 0)  # for centroids

        # Storage containers for cell means, max and mins
        self.centroids = Function(self.P0)
        self.centroids_rhs = Function(self.P0)
        self.max_field = Function(self.P1CG)
        self.min_field = Function(self.P1CG)

        self.centroid_solver = self._construct_centroid_solver()

        # Update min and max loop
        self._min_max_loop = """
for(int i = 0; i < maxq.dofs; i++) {
    maxq[i][0] = fmax(maxq[i][0],q[0][0]);
    minq[i][0] = fmin(minq[i][0],q[0][0]);
}
                             """
        # Perform limiting loop
        self._limit_kernel = """
Esempio n. 2
0
    def __init__(self, space):
        """
        Initialise limiter

        :param space : FunctionSpace instance
        """

        if utils.complex_mode:
            raise ValueError(
                "We haven't decided what limiting complex valued fields means. Please get in touch if you have need."
            )

        self.P1DG = space
        self.P1CG = FunctionSpace(self.P1DG.mesh(), 'CG',
                                  1)  # for min/max limits
        self.P0 = FunctionSpace(self.P1DG.mesh(), 'DG', 0)  # for centroids

        # Storage containers for cell means, max and mins
        self.centroids = Function(self.P0)
        self.centroids_rhs = Function(self.P0)
        self.max_field = Function(self.P1CG)
        self.min_field = Function(self.P1CG)

        self.centroid_solver = self._construct_centroid_solver()

        # Update min and max loop
        domain = "{[i]: 0 <= i < maxq.dofs}"
        instructions = """
        for i
            maxq[i] = fmax(maxq[i], q[0])
            minq[i] = fmin(minq[i], q[0])
        end
        """
        self._min_max_loop = (domain, instructions)

        # Perform limiting loop
        domain = "{[i, ii]: 0 <= i < q.dofs and 0 <= ii < q.dofs}"
        instructions = """
        <float64> alpha = 1
        <float64> qavg = qbar[0, 0]
        for i
            <float64> _alpha1 = fmin(alpha, fmin(1, (qmax[i] - qavg)/(q[i] - qavg)))
            <float64> _alpha2 = fmin(alpha, fmin(1, (qavg - qmin[i])/(qavg - q[i])))
            alpha = _alpha1 if q[i] > qavg else (_alpha2 if q[i] < qavg else  alpha)
        end
        for ii
            q[ii] = qavg + alpha * (q[ii] - qavg)
        end
        """
        self._limit_kernel = (domain, instructions)
Esempio n. 3
0
    def __init__(self, equation):
        """
        Initialise limiter

        :param space : equation, as we need the broken space attached to it
        """

        self.Vt = equation.space
        # check this is the right space, only currently working for 2D extruded mesh
        if self.Vt.extruded and self.Vt.mesh().topological_dimension() == 2:
            # check that horizontal degree is 1 and vertical degree is 2
            if self.Vt.ufl_element().degree()[0] is not 1 or \
               self.Vt.ufl_element().degree()[1] is not 2:
                raise ValueError('This is not the right limiter for this space.')
            # check that continuity of the spaces is correct
            # this will fail if the space does not use broken elements
            if self.Vt.ufl_element()._element.sobolev_space()[0].name is not 'L2' or \
               self.Vt.ufl_element()._element.sobolev_space()[1].name is not 'H1':
                raise ValueError('This is not the right limiter for this space.')
        else:
            logger.warning('This limiter may not work for the space you are using.')

        self.Q1DG = FunctionSpace(self.Vt.mesh(), 'DG', 1)  # space with only vertex DOFs
        self.vertex_limiter = VertexBasedLimiter(self.Q1DG)
        self.theta_hat = Function(self.Q1DG)  # theta function with only vertex DOFs
        self.w = Function(self.Vt)
        self.result = Function(self.Vt)
        par_loop(_weight_kernel, dx, {"weight": (self.w, INC)})
Esempio n. 4
0
    def initialize(self, obj):
        if complex_mode:
            raise NotImplementedError("HypreAMS preconditioner not yet implemented in complex mode")

        Citations().register("Kolev2009")
        A, P = obj.getOperators()
        prefix = obj.getOptionsPrefix()
        V = get_function_space(obj.getDM())
        mesh = V.mesh()

        family = str(V.ufl_element().family())
        degree = V.ufl_element().degree()
        if family != 'Nedelec 1st kind H(curl)' or degree != 1:
            raise ValueError("Hypre AMS requires lowest order Nedelec elements! (not %s of degree %d)" % (family, degree))

        P1 = FunctionSpace(mesh, "Lagrange", 1)
        G = Interpolator(grad(TestFunction(P1)), V).callable().handle

        pc = PETSc.PC().create(comm=obj.comm)
        pc.incrementTabLevel(1, parent=obj)
        pc.setOptionsPrefix(prefix + "hypre_ams_")
        pc.setOperators(A, P)

        pc.setType('hypre')
        pc.setHYPREType('ams')
        pc.setHYPREDiscreteGradient(G)
        zero_beta = PETSc.Options(prefix).getBool("pc_hypre_ams_zero_beta_poisson", default=False)
        if zero_beta:
            pc.setHYPRESetBetaPoissonMatrix(None)

        VectorP1 = VectorFunctionSpace(mesh, "Lagrange", 1)
        pc.setCoordinates(interpolate(SpatialCoordinate(mesh), VectorP1).dat.data_ro.copy())
        pc.setUp()

        self.pc = pc
    def __init__(self, space):
        """
        Initialise limiter

        :param space : FunctionSpace instance
        """

        self.P1DG = space
        self.P1CG = FunctionSpace(self.P1DG.mesh(), 'CG',
                                  1)  # for min/max limits
        self.P0 = FunctionSpace(self.P1DG.mesh(), 'DG', 0)  # for centroids

        # Storage containers for cell means, max and mins
        self.centroids = Function(self.P0)
        self.centroids_rhs = Function(self.P0)
        self.max_field = Function(self.P1CG)
        self.min_field = Function(self.P1CG)

        self.centroid_solver = self._construct_centroid_solver()

        # Update min and max loop
        domain = "{[i]: 0 <= i < maxq.dofs}"
        instructions = """
        for i
            maxq[i] = fmax(maxq[i], q[0])
            minq[i] = fmin(minq[i], q[0])
        end
        """
        self._min_max_loop = (domain, instructions)

        # Perform limiting loop
        domain = "{[i, ii]: 0 <= i < q.dofs and 0 <= ii < q.dofs}"
        instructions = """
        <float64> alpha = 1
        <float64> qavg = qbar[0, 0]
        for i
            <float64> _alpha1 = fmin(alpha, fmin(1, (qmax[i] - qavg)/(q[i] - qavg)))
            <float64> _alpha2 = fmin(alpha, fmin(1, (qavg - qmin[i])/(qavg - q[i])))
            alpha = if(q[i] > qavg, _alpha1, if(q[i] < qavg, _alpha2, alpha))
        end
        for ii
            q[ii] = qavg + alpha * (q[ii] - qavg)
        end
        """
        self._limit_kernel = (domain, instructions)
Esempio n. 6
0
    def _split_arguments(self):
        """Splits the function space and stores the component
        spaces determined by the indices.
        """
        from firedrake.functionspace import FunctionSpace, MixedFunctionSpace
        from firedrake.ufl_expr import Argument

        tensor, = self.operands
        nargs = []
        for i, arg in enumerate(tensor.arguments()):
            V = arg.function_space()
            V_is = V.split()
            idx = as_tuple(self._blocks[i])
            if len(idx) == 1:
                fidx, = idx
                W = V_is[fidx]
                W = FunctionSpace(W.mesh(), W.ufl_element())
            else:
                W = MixedFunctionSpace([V_is[fidx] for fidx in idx])

            nargs.append(Argument(W, arg.number(), part=arg.part()))

        return tuple(nargs)
Esempio n. 7
0
    def _split_arguments(self):
        """Splits the function space and stores the component
        spaces determined by the indices.
        """
        from firedrake.functionspace import FunctionSpace, MixedFunctionSpace
        from firedrake.ufl_expr import Argument

        tensor, = self.operands
        nargs = []
        for i, arg in enumerate(tensor.arguments()):
            V = arg.function_space()
            V_is = V.split()
            idx = as_tuple(self._blocks[i])
            if len(idx) == 1:
                fidx, = idx
                W = V_is[fidx]
                W = FunctionSpace(W.mesh(), W.ufl_element())
            else:
                W = MixedFunctionSpace([V_is[fidx] for fidx in idx])

            nargs.append(Argument(W, arg.number(), part=arg.part()))

        return tuple(nargs)
Esempio n. 8
0
    def initialize(self, obj):
        A, P = obj.getOperators()
        prefix = obj.getOptionsPrefix()
        V = get_function_space(obj.getDM())
        mesh = V.mesh()

        family = str(V.ufl_element().family())
        degree = V.ufl_element().degree()
        if family != 'Raviart-Thomas' or degree != 1:
            raise ValueError(
                "Hypre ADS requires lowest order RT elements! (not %s of degree %d)"
                % (family, degree))

        P1 = FunctionSpace(mesh, "Lagrange", 1)
        NC1 = FunctionSpace(mesh, "N1curl", 1)
        # DiscreteGradient
        G = Interpolator(grad(TestFunction(P1)), NC1).callable().handle
        # DiscreteCurl
        C = Interpolator(curl(TestFunction(NC1)), V).callable().handle

        pc = PETSc.PC().create(comm=obj.comm)
        pc.incrementTabLevel(1, parent=obj)
        pc.setOptionsPrefix(prefix + "hypre_ads_")
        pc.setOperators(A, P)

        pc.setType('hypre')
        pc.setHYPREType('ads')
        pc.setHYPREDiscreteGradient(G)
        pc.setHYPREDiscreteCurl(C)
        V = VectorFunctionSpace(mesh, "Lagrange", 1)
        linear_coordinates = interpolate(SpatialCoordinate(mesh),
                                         V).dat.data_ro.copy()
        pc.setCoordinates(linear_coordinates)

        pc.setUp()
        self.pc = pc
Esempio n. 9
0
    def sort_entities(self, dm, axis, dir, ndiv):
        # compute
        # [(pStart, (x, y, z)), (pEnd, (x, y, z))]

        mesh = dm.getAttr("__firedrake_mesh__")
        ele = mesh.coordinates.function_space().ufl_element()
        V = mesh.coordinates.function_space()
        if V.finat_element.entity_dofs(
        ) == V.finat_element.entity_closure_dofs():
            # We're using DG or DQ for our coordinates, so we got
            # a periodic mesh. We need to interpolate to CGk
            # with access descriptor MAX to define a consistent opinion
            # about where the vertices are.
            CGkele = ele.reconstruct(family="Lagrange")
            # Need to supply the actual mesh to the FunctionSpace constructor,
            # not its weakref proxy (the variable `mesh`)
            # as interpolation fails because they are not hashable
            CGk = FunctionSpace(mesh.coordinates.function_space().mesh(),
                                CGkele)
            coordinates = interpolate(mesh.coordinates, CGk, access=op2.MAX)
        else:
            coordinates = mesh.coordinates

        select = partial(select_entity, dm=dm, exclude="pyop2_ghost")
        entities = [(p, self.coords(dm, p, coordinates))
                    for p in filter(select, range(*dm.getChart()))]

        minx = min(entities, key=lambda z: z[1][axis])[1][axis]
        maxx = max(entities, key=lambda z: z[1][axis])[1][axis]

        def keyfunc(z):
            coords = tuple(z[1])
            return (coords[axis], ) + tuple(coords[:axis] + coords[axis + 1:])

        s = sorted(entities, key=keyfunc, reverse=(dir == -1))

        divisions = numpy.linspace(minx, maxx, ndiv + 1)
        (entities, coords) = zip(*s)
        coords = [c[axis] for c in coords]
        indices = numpy.searchsorted(coords[::dir], divisions)

        out = []
        for k in range(ndiv):
            out.append(entities[indices[k]:indices[k + 1]])
        out.append(entities[indices[-1]:])

        return out
Esempio n. 10
0
    def initialize(self, obj):
        if complex_mode:
            raise NotImplementedError(
                "HypreAMS preconditioner not yet implemented in complex mode")

        Citations().register("Kolev2009")
        A, P = obj.getOperators()
        prefix = obj.getOptionsPrefix()
        V = get_function_space(obj.getDM())
        mesh = V.mesh()

        family = str(V.ufl_element().family())
        degree = V.ufl_element().degree()
        if family != 'Nedelec 1st kind H(curl)' or degree != 1:
            raise ValueError(
                "Hypre AMS requires lowest order Nedelec elements! (not %s of degree %d)"
                % (family, degree))

        P1 = FunctionSpace(mesh, "Lagrange", 1)
        G = Interpolator(grad(TestFunction(P1)), V).callable().handle

        pc = PETSc.PC().create(comm=obj.comm)
        pc.incrementTabLevel(1, parent=obj)
        pc.setOptionsPrefix(prefix + "hypre_ams_")
        pc.setOperators(A, P)

        pc.setType('hypre')
        pc.setHYPREType('ams')
        pc.setHYPREDiscreteGradient(G)
        zero_beta = PETSc.Options(prefix).getBool(
            "pc_hypre_ams_zero_beta_poisson", default=False)
        if zero_beta:
            pc.setHYPRESetBetaPoissonMatrix(None)

        # Build constants basis for the Nedelec space
        cvecs = []
        for i in range(mesh.cell_dimension()):
            direction = [
                1.0 if i == j else 0.0 for j in range(mesh.cell_dimension())
            ]
            c = project(Constant(direction), V)
            with c.vector().dat.vec_ro as cvec:
                cvecs.append(cvec)
        pc.setHYPRESetEdgeConstantVectors(*cvecs)
        pc.setUp()

        self.pc = pc
def test_InterpolationMatrix_str():
    "test the str method of InterpolationMatrix"

    nx = 10

    coords = np.array([[0.75], [0.5], [0.25], [0.1]])
    nd = len(coords)

    mesh = UnitIntervalMesh(nx)
    V = FunctionSpace(mesh, "CG", 1)

    im = InterpolationMatrix(V, coords)

    assert str(
        im
    ) == "Interpolation matrix from {} mesh points to {} data points".format(
        nx + 1, nd)
def test_InterpolationMatrix_interp_mesh_to_data(fs, coords, meshcoords):
    "test method to interpolate from distributed mesh to data gathered at root"

    # simple 1D test

    nd = len(coords)

    im = InterpolationMatrix(fs, coords)
    im.assemble()

    input_ordered = np.array([3., 2., 7., 4., 0., 0., 2., 1., 1., 1., 5.])

    f = Function(fs).vector()

    meshcoords_ordered = np.linspace(0., 1., 11)

    with f.dat.vec as vec:
        imin, imax = vec.getOwnershipRange()
        for i in range(imin, imax):
            vec.setValue(
                i,
                input_ordered[np.where(meshcoords_ordered == meshcoords[i])])

    if COMM_WORLD.rank == 0:
        expected = np.array([1., 0., 5.5, 3.25])
    else:
        expected = np.zeros(0)

    out = im.interp_mesh_to_data(f)

    assert_allclose(out, expected, atol=1.e-10)

    # failure due to bad input sizes

    mesh2 = UnitIntervalMesh(12)
    V2 = FunctionSpace(mesh2, "CG", 1)

    f2 = Function(V2).vector()
    f2.set_local(np.ones(f2.local_size()))

    with pytest.raises(AssertionError):
        im.interp_mesh_to_data(f2)

    im.destroy()
Esempio n. 13
0
    def initialize(self, pc):
        from firedrake.assemble import allocate_matrix, create_assembly_callable

        _, P = pc.getOperators()

        if pc.getType() != "python":
            raise ValueError("Expecting PC type python")
        opc = pc
        appctx = self.get_appctx(pc)
        fcp = appctx.get("form_compiler_parameters")

        V = get_function_space(pc.getDM())
        if len(V) == 1:
            V = FunctionSpace(V.mesh(), V.ufl_element())
        else:
            V = MixedFunctionSpace([V_ for V_ in V])
        test = TestFunction(V)
        trial = TrialFunction(V)

        if P.type == "python":
            context = P.getPythonContext()
            # It only makes sense to preconditioner/invert a diagonal
            # block in general.  That's all we're going to allow.
            if not context.on_diag:
                raise ValueError("Only makes sense to invert diagonal block")

        prefix = pc.getOptionsPrefix()
        options_prefix = prefix + self._prefix

        mat_type = PETSc.Options().getString(options_prefix + "mat_type", "aij")

        (a, bcs) = self.form(pc, test, trial)

        self.P = allocate_matrix(a, bcs=bcs,
                                 form_compiler_parameters=fcp,
                                 mat_type=mat_type,
                                 options_prefix=options_prefix)
        self._assemble_P = create_assembly_callable(a, tensor=self.P,
                                                    bcs=bcs,
                                                    form_compiler_parameters=fcp,
                                                    mat_type=mat_type)
        self._assemble_P()
        self.P.force_evaluation()

        # Transfer nullspace over
        Pmat = self.P.petscmat
        Pmat.setNullSpace(P.getNullSpace())
        tnullsp = P.getTransposeNullSpace()
        if tnullsp.handle != 0:
            Pmat.setTransposeNullSpace(tnullsp)

        # Internally, we just set up a PC object that the user can configure
        # however from the PETSc command line.  Since PC allows the user to specify
        # a KSP, we can do iterative by -assembled_pc_type ksp.
        pc = PETSc.PC().create(comm=opc.comm)
        pc.incrementTabLevel(1, parent=opc)

        # We set a DM and an appropriate SNESContext on the constructed PC so one
        # can do e.g. multigrid or patch solves.
        from firedrake.variational_solver import NonlinearVariationalProblem
        from firedrake.solving_utils import _SNESContext
        dm = opc.getDM()
        octx = get_appctx(dm)
        oproblem = octx._problem
        nproblem = NonlinearVariationalProblem(oproblem.F, oproblem.u, bcs, J=a, form_compiler_parameters=fcp)
        nctx = _SNESContext(nproblem, mat_type, mat_type, octx.appctx)
        push_appctx(dm, nctx)
        self._ctx_ref = nctx
        pc.setDM(dm)

        pc.setOptionsPrefix(options_prefix)
        pc.setOperators(Pmat, Pmat)
        pc.setFromOptions()
        pc.setUp()
        self.pc = pc
        pop_appctx(dm)
Esempio n. 14
0
def assemble_mixed_mass_matrix(V_A, V_B):
    """
    Construct the mixed mass matrix of two function spaces,
    using the TrialFunction from V_A and the TestFunction
    from V_B.
    """

    if len(V_A) > 1 or len(V_B) > 1:
        raise NotImplementedError(
            "Sorry, only implemented for non-mixed spaces")

    if V_A.ufl_element().mapping() != "identity" or V_B.ufl_element().mapping(
    ) != "identity":
        msg = """
Sorry, only implemented for affine maps for now. To do non-affine, we'd need to
import much more of the assembly engine of UFL/TSFC/etc to do the assembly on
each supermesh cell.
"""
        raise NotImplementedError(msg)

    mesh_A = V_A.mesh()
    mesh_B = V_B.mesh()

    dim = mesh_A.geometric_dimension()
    assert dim == mesh_B.geometric_dimension()
    assert dim == mesh_A.topological_dimension()
    assert dim == mesh_B.topological_dimension()

    (mh_A, level_A) = get_level(mesh_A)
    (mh_B, level_B) = get_level(mesh_B)

    if mesh_A is mesh_B:

        def likely(cell_A):
            return [cell_A]
    else:
        if (mh_A is None or mh_B is None) or (mh_A is not mh_B):

            # No mesh hierarchy structure, call libsupermesh for
            # intersection finding
            intersections = intersection_finder(mesh_A, mesh_B)
            likely = intersections.__getitem__
        else:
            # We do have a mesh hierarchy, use it

            if abs(level_A - level_B) > 1:
                raise NotImplementedError(
                    "Only works for transferring between adjacent levels for now."
                )

            # What are the cells of B that (probably) intersect with a given cell in A?
            if level_A > level_B:
                cell_map = mh_A.fine_to_coarse_cells[level_A]

                def likely(cell_A):
                    return cell_map[cell_A]

            elif level_A < level_B:
                cell_map = mh_A.coarse_to_fine_cells[level_A]

                def likely(cell_A):
                    return cell_map[cell_A]

    assert V_A.value_size == V_B.value_size
    orig_value_size = V_A.value_size
    if V_A.value_size > 1:
        V_A = firedrake.FunctionSpace(mesh_A,
                                      V_A.ufl_element().sub_elements()[0])
    if V_B.value_size > 1:
        V_B = firedrake.FunctionSpace(mesh_B,
                                      V_B.ufl_element().sub_elements()[0])

    assert V_A.value_size == 1
    assert V_B.value_size == 1

    preallocator = PETSc.Mat().create(comm=mesh_A.comm)
    preallocator.setType(PETSc.Mat.Type.PREALLOCATOR)

    rset = V_B.dof_dset
    cset = V_A.dof_dset

    nrows = rset.layout_vec.getSizes()
    ncols = cset.layout_vec.getSizes()

    preallocator.setLGMap(rmap=rset.scalar_lgmap, cmap=cset.scalar_lgmap)
    preallocator.setSizes(size=(nrows, ncols), bsize=1)
    preallocator.setUp()

    zeros = numpy.zeros((V_B.cell_node_map().arity, V_A.cell_node_map().arity),
                        dtype=ScalarType)
    for cell_A, dofs_A in enumerate(V_A.cell_node_map().values):
        for cell_B in likely(cell_A):
            dofs_B = V_B.cell_node_map().values_with_halo[cell_B, :]
            preallocator.setValuesLocal(dofs_B, dofs_A, zeros)
    preallocator.assemble()

    dnnz, onnz = get_preallocation(preallocator, nrows[0])

    # Unroll from block to AIJ
    dnnz = dnnz * cset.cdim
    dnnz = numpy.repeat(dnnz, rset.cdim)
    onnz = onnz * cset.cdim
    onnz = numpy.repeat(onnz, cset.cdim)
    preallocator.destroy()

    assert V_A.value_size == V_B.value_size
    rdim = V_B.dof_dset.cdim
    cdim = V_A.dof_dset.cdim

    #
    # Preallocate M_AB.
    #
    mat = PETSc.Mat().create(comm=mesh_A.comm)
    mat.setType(PETSc.Mat.Type.AIJ)
    rsizes = tuple(n * rdim for n in nrows)
    csizes = tuple(c * cdim for c in ncols)
    mat.setSizes(size=(rsizes, csizes), bsize=(rdim, cdim))
    mat.setPreallocationNNZ((dnnz, onnz))
    mat.setLGMap(rmap=rset.lgmap, cmap=cset.lgmap)
    # TODO: Boundary conditions not handled.
    mat.setOption(mat.Option.IGNORE_OFF_PROC_ENTRIES, False)
    mat.setOption(mat.Option.NEW_NONZERO_ALLOCATION_ERR, True)
    mat.setOption(mat.Option.KEEP_NONZERO_PATTERN, True)
    mat.setOption(mat.Option.UNUSED_NONZERO_LOCATION_ERR, False)
    mat.setOption(mat.Option.IGNORE_ZERO_ENTRIES, True)
    mat.setUp()

    evaluate_kernel_A = compile_element(ufl.Coefficient(V_A),
                                        name="evaluate_kernel_A")
    evaluate_kernel_B = compile_element(ufl.Coefficient(V_B),
                                        name="evaluate_kernel_B")

    # We only need one of these since we assume that the two meshes both have CG1 coordinates
    to_reference_kernel = to_reference_coordinates(
        mesh_A.coordinates.ufl_element())

    if dim == 2:
        reference_mesh = UnitTriangleMesh(comm=COMM_SELF)
    else:
        reference_mesh = UnitTetrahedronMesh(comm=COMM_SELF)
    evaluate_kernel_S = compile_element(ufl.Coefficient(
        reference_mesh.coordinates.function_space()),
                                        name="evaluate_kernel_S")

    V_S_A = FunctionSpace(reference_mesh, V_A.ufl_element())
    V_S_B = FunctionSpace(reference_mesh, V_B.ufl_element())
    M_SS = assemble(inner(TrialFunction(V_S_A), TestFunction(V_S_B)) * dx)
    M_SS = M_SS.M.handle[:, :]
    node_locations_A = utils.physical_node_locations(
        V_S_A).dat.data_ro_with_halos
    node_locations_B = utils.physical_node_locations(
        V_S_B).dat.data_ro_with_halos
    num_nodes_A = node_locations_A.shape[0]
    num_nodes_B = node_locations_B.shape[0]

    to_reference_kernel = to_reference_coordinates(
        mesh_A.coordinates.ufl_element())

    supermesh_kernel_str = """
    #include "libsupermesh-c.h"
    #include <petsc.h>
    %(to_reference)s
    %(evaluate_S)s
    %(evaluate_A)s
    %(evaluate_B)s
#define complex_mode %(complex_mode)s

    #define PrintInfo(...) do { if (PetscLogPrintInfo) printf(__VA_ARGS__); } while (0)
    static void print_array(PetscScalar *arr, int d)
    {
        for(int j=0; j<d; j++)
            PrintInfo(stderr, "%%+.2f ", arr[j]);
    }
    static void print_coordinates(PetscScalar *simplex, int d)
    {
        for(int i=0; i<d+1; i++)
        {
            PrintInfo("\t");
            print_array(&simplex[d*i], d);
            PrintInfo("\\n");
        }
    }
#if complex_mode
    static void seperate_real_and_imag(PetscScalar *simplex, double *real_simplex, double *imag_simplex, int d)
    {
        for(int i=0; i<d+1; i++)
        {
            for(int j=0; j<d; j++)
            {
                real_simplex[d*i+j] = creal(simplex[d*i+j]);
                imag_simplex[d*i+j] = cimag(simplex[d*i+j]);
            }
        }
    }
    static void merge_back_to_simplex(PetscScalar* simplex, double* real_simplex, double* imag_simplex, int d)
    {
        print_coordinates(simplex,d);
        for(int i=0; i<d+1; i++)
        {
            for(int j=0; j<d; j++)
            {
                simplex[d*i+j] = real_simplex[d*i+j]+imag_simplex[d*i+j]*_Complex_I;
            }
        }
    }
#endif
    int supermesh_kernel(PetscScalar* simplex_A, PetscScalar* simplex_B, PetscScalar* simplices_C,  PetscScalar* nodes_A,  PetscScalar* nodes_B,  PetscScalar* M_SS, PetscScalar* outptr, int num_ele)
    {
#define d %(dim)s
#define num_nodes_A %(num_nodes_A)s
#define num_nodes_B %(num_nodes_B)s

        double simplex_ref_measure;
        PrintInfo("simplex_A coordinates\\n");
        print_coordinates(simplex_A, d);
        PrintInfo("simplex_B coordinates\\n");
        print_coordinates(simplex_B, d);
        int num_elements = num_ele;

        if (d == 2) simplex_ref_measure = 0.5;
        else if (d == 3) simplex_ref_measure = 1.0/6;

        PetscScalar R_AS[num_nodes_A][num_nodes_A];
        PetscScalar R_BS[num_nodes_B][num_nodes_B];
        PetscScalar coeffs_A[%(num_nodes_A)s] = {0.};
        PetscScalar coeffs_B[%(num_nodes_B)s] = {0.};

        PetscScalar reference_nodes_A[num_nodes_A][d];
        PetscScalar reference_nodes_B[num_nodes_B][d];

#if complex_mode
        double real_simplex_A[d*(d+1)];
        double imag_simplex_A[d*(d+1)];
        seperate_real_and_imag(simplex_A, real_simplex_A, imag_simplex_A, d);
        double real_simplex_B[d*(d+1)];
        double imag_simplex_B[d*(d+1)];
        seperate_real_and_imag(simplex_B, real_simplex_B, imag_simplex_B, d);

        double real_simplices_C[num_elements*d*(d+1)];
        double imag_simplices_C[num_elements*d*(d+1)];
        for (int ii=0; ii<num_elements*d*(d+1); ++ii) imag_simplices_C[ii] = 0.;

        %(libsupermesh_intersect_simplices)s(real_simplex_A, real_simplex_B, real_simplices_C, &num_elements);

        merge_back_to_simplex(simplex_A, real_simplex_A, imag_simplex_A, d);
        merge_back_to_simplex(simplex_B, real_simplex_B, imag_simplex_B, d);
        for(int s=0; s<num_elements; s++)
        {
            PetscScalar* simplex_C = &simplices_C[s * d * (d+1)];
            double* real_simplex_C = &real_simplices_C[s * d * (d+1)];
            double* imag_simplex_C = &imag_simplices_C[s * d * (d+1)];
            merge_back_to_simplex(simplex_C, real_simplex_C, imag_simplex_C, d);
        }
#else
        %(libsupermesh_intersect_simplices)s(simplex_A, simplex_B, simplices_C, &num_elements);
#endif
        PrintInfo("Supermesh consists of %%i elements\\n", num_elements);

        // would like to do this
        //PetscScalar MAB[%(num_nodes_A)s][%(num_nodes_B)s] = (PetscScalar (*)[%(num_nodes_B)s])outptr;
        // but have to do this instead because we don't grok C
        PetscScalar (*MAB)[num_nodes_A] = (PetscScalar (*)[num_nodes_A])outptr;
        PetscScalar (*MSS)[num_nodes_A] = (PetscScalar (*)[num_nodes_A])M_SS; // note the underscore

        for ( int i = 0; i < num_nodes_B; i++ ) {
            for (int j = 0; j < num_nodes_A; j++) {
                MAB[i][j] = 0.0;
            }
        }

        for(int s=0; s<num_elements; s++)
        {
            PetscScalar* simplex_S = &simplices_C[s * d * (d+1)];
            double simplex_S_measure;
#if complex_mode
            double real_simplex_S[d*(d+1)];
            double imag_simplex_S[d*(d+1)];
            seperate_real_and_imag(simplex_S, real_simplex_S, imag_simplex_S, d);

            %(libsupermesh_simplex_measure)s(real_simplex_S, &simplex_S_measure);

            merge_back_to_simplex(simplex_S, real_simplex_S, imag_simplex_S, d);
#else
            %(libsupermesh_simplex_measure)s(simplex_S, &simplex_S_measure);
#endif
            PrintInfo("simplex_S coordinates with measure %%f\\n", simplex_S_measure);
            print_coordinates(simplex_S, d);

            PrintInfo("Start mapping nodes for V_A\\n");
            PetscScalar physical_nodes_A[num_nodes_A][d];
            for(int n=0; n < num_nodes_A; n++) {
                PetscScalar* reference_node_location = &nodes_A[n*d];
                PetscScalar* physical_node_location = physical_nodes_A[n];
                for (int j=0; j < d; j++) physical_node_location[j] = 0.0;
                pyop2_kernel_evaluate_kernel_S(physical_node_location, simplex_S, reference_node_location);
                PrintInfo("\\tNode ");
                print_array(reference_node_location, d);
                PrintInfo(" mapped to ");
                print_array(physical_node_location, d);
                PrintInfo("\\n");
            }
            PrintInfo("Start mapping nodes for V_B\\n");
            PetscScalar physical_nodes_B[num_nodes_B][d];
            for(int n=0; n < num_nodes_B; n++) {
                PetscScalar* reference_node_location = &nodes_B[n*d];
                PetscScalar* physical_node_location = physical_nodes_B[n];
                for (int j=0; j < d; j++) physical_node_location[j] = 0.0;
                pyop2_kernel_evaluate_kernel_S(physical_node_location, simplex_S, reference_node_location);
                PrintInfo("\\tNode ");
                print_array(reference_node_location, d);
                PrintInfo(" mapped to ");
                print_array(physical_node_location, d);
                PrintInfo("\\n");
            }
            PrintInfo("==========================================================\\n");
            PrintInfo("Start pulling back dof from S into reference space for A.\\n");
            for(int n=0; n < num_nodes_A; n++) {
                for(int i=0; i<d; i++) reference_nodes_A[n][i] = 0.;
                to_reference_coords_kernel(reference_nodes_A[n], physical_nodes_A[n], simplex_A);
                PrintInfo("Pulling back ");
                print_array(physical_nodes_A[n], d);
                PrintInfo(" to ");
                print_array(reference_nodes_A[n], d);
                PrintInfo("\\n");
            }
            PrintInfo("Start pulling back dof from S into reference space for B.\\n");
            for(int n=0; n < num_nodes_B; n++) {
                for(int i=0; i<d; i++) reference_nodes_B[n][i] = 0.;
                to_reference_coords_kernel(reference_nodes_B[n], physical_nodes_B[n], simplex_B);
                PrintInfo("Pulling back ");
                print_array(physical_nodes_B[n], d);
                PrintInfo(" to ");
                print_array(reference_nodes_B[n], d);
                PrintInfo("\\n");
            }

            PrintInfo("Start evaluating basis functions of V_A at dofs for V_A on S\\n");
            for(int i=0; i<num_nodes_A; i++) {
                coeffs_A[i] = 1.;
                for(int j=0; j<num_nodes_A; j++) {
                    R_AS[i][j] = 0.;
                    pyop2_kernel_evaluate_kernel_A(&R_AS[i][j], coeffs_A, reference_nodes_A[j]);
                }
                print_array(R_AS[i], num_nodes_A);
                PrintInfo("\\n");
                coeffs_A[i] = 0.;
            }
            PrintInfo("Start evaluating basis functions of V_B at dofs for V_B on S\\n");
            for(int i=0; i<num_nodes_B; i++) {
                coeffs_B[i] = 1.;
                for(int j=0; j<num_nodes_B; j++) {
                    R_BS[i][j] = 0.;
                    pyop2_kernel_evaluate_kernel_B(&R_BS[i][j], coeffs_B, reference_nodes_B[j]);
                }
                print_array(R_BS[i], num_nodes_B);
                PrintInfo("\\n");
                coeffs_B[i] = 0.;
            }
            PrintInfo("Start doing the matmatmat mult\\n");

            for ( int i = 0; i < num_nodes_B; i++ ) {
                for (int j = 0; j < num_nodes_A; j++) {
                    for ( int k = 0; k < num_nodes_B; k++) {
                        for ( int l = 0; l < num_nodes_A; l++) {
                            MAB[i][j] += (simplex_S_measure/simplex_ref_measure) * R_BS[i][k] * MSS[k][l] * R_AS[j][l];
                        }
                    }
                }
            }
        }
        return num_elements;
    }
    """ % {
        "evaluate_S":
        str(evaluate_kernel_S),
        "evaluate_A":
        str(evaluate_kernel_A),
        "evaluate_B":
        str(evaluate_kernel_B),
        "to_reference":
        str(to_reference_kernel),
        "num_nodes_A":
        num_nodes_A,
        "num_nodes_B":
        num_nodes_B,
        "libsupermesh_simplex_measure":
        "libsupermesh_triangle_area"
        if dim == 2 else "libsupermesh_tetrahedron_volume",
        "libsupermesh_intersect_simplices":
        "libsupermesh_intersect_tris_real"
        if dim == 2 else "libsupermesh_intersect_tets_real",
        "dim":
        dim,
        "complex_mode":
        1 if complex_mode else 0
    }

    dirs = get_petsc_dir() + (sys.prefix, )
    includes = ["-I%s/include" % d for d in dirs]
    libs = ["-L%s/lib" % d for d in dirs]
    libs = libs + ["-Wl,-rpath,%s/lib" % d
                   for d in dirs] + ["-lpetsc", "-lsupermesh"]
    lib = load(supermesh_kernel_str,
               "c",
               "supermesh_kernel",
               cppargs=includes,
               ldargs=libs,
               argtypes=[
                   ctypes.c_voidp, ctypes.c_voidp, ctypes.c_voidp,
                   ctypes.c_voidp, ctypes.c_voidp, ctypes.c_voidp,
                   ctypes.c_voidp
               ],
               restype=ctypes.c_int)

    ammm(V_A, V_B, likely, node_locations_A, node_locations_B, M_SS,
         ctypes.addressof(lib), mat)
    if orig_value_size == 1:
        return mat
    else:
        (lrows, grows), (lcols, gcols) = mat.getSizes()
        lrows *= orig_value_size
        grows *= orig_value_size
        lcols *= orig_value_size
        gcols *= orig_value_size
        size = ((lrows, grows), (lcols, gcols))
        context = BlockMatrix(mat, orig_value_size)
        blockmat = PETSc.Mat().createPython(size,
                                            context=context,
                                            comm=mat.comm)
        blockmat.setUp()
        return blockmat
Esempio n. 15
0
    def initialize(self, pc):
        from firedrake.assemble import allocate_matrix, create_assembly_callable

        _, P = pc.getOperators()

        if pc.getType() != "python":
            raise ValueError("Expecting PC type python")
        opc = pc
        appctx = self.get_appctx(pc)
        fcp = appctx.get("form_compiler_parameters")

        V = get_function_space(pc.getDM())
        if len(V) == 1:
            V = FunctionSpace(V.mesh(), V.ufl_element())
        else:
            V = MixedFunctionSpace([V_ for V_ in V])
        test = TestFunction(V)
        trial = TrialFunction(V)

        if P.type == "python":
            context = P.getPythonContext()
            # It only makes sense to preconditioner/invert a diagonal
            # block in general.  That's all we're going to allow.
            if not context.on_diag:
                raise ValueError("Only makes sense to invert diagonal block")

        prefix = pc.getOptionsPrefix()
        options_prefix = prefix + self._prefix

        mat_type = PETSc.Options().getString(options_prefix + "mat_type", "aij")

        (a, bcs) = self.form(pc, test, trial)

        self.P = allocate_matrix(a, bcs=bcs,
                                 form_compiler_parameters=fcp,
                                 mat_type=mat_type,
                                 options_prefix=options_prefix)
        self._assemble_P = create_assembly_callable(a, tensor=self.P,
                                                    bcs=bcs,
                                                    form_compiler_parameters=fcp,
                                                    mat_type=mat_type)
        self._assemble_P()
        self.P.force_evaluation()

        # Transfer nullspace over
        Pmat = self.P.petscmat
        Pmat.setNullSpace(P.getNullSpace())
        tnullsp = P.getTransposeNullSpace()
        if tnullsp.handle != 0:
            Pmat.setTransposeNullSpace(tnullsp)

        # Internally, we just set up a PC object that the user can configure
        # however from the PETSc command line.  Since PC allows the user to specify
        # a KSP, we can do iterative by -assembled_pc_type ksp.
        pc = PETSc.PC().create(comm=opc.comm)
        pc.incrementTabLevel(1, parent=opc)

        # We set a DM and an appropriate SNESContext on the constructed PC so one
        # can do e.g. multigrid or patch solves.
        from firedrake.variational_solver import NonlinearVariationalProblem
        from firedrake.solving_utils import _SNESContext
        dm = opc.getDM()
        octx = get_appctx(dm)
        oproblem = octx._problem
        nproblem = NonlinearVariationalProblem(oproblem.F, oproblem.u, bcs, J=a, form_compiler_parameters=fcp)
        nctx = _SNESContext(nproblem, mat_type, mat_type, octx.appctx)
        push_appctx(dm, nctx)
        pc.setDM(dm)

        pc.setOptionsPrefix(options_prefix)
        pc.setOperators(Pmat, Pmat)
        pc.setFromOptions()
        pc.setUp()
        self.pc = pc
        pop_appctx(dm)
Esempio n. 16
0
 def _ad_function_space(self, mesh):
     element = self.ufl_element()
     fs_element = element.reconstruct(cell=mesh.ufl_cell())
     return FunctionSpace(mesh, fs_element)
Esempio n. 17
0
    def initialize(self, pc):
        """Set up the problem context. This takes the incoming
        three-field hybridized system and constructs the static
        condensation operators using Slate expressions.

        A KSP is created for the reduced system for the Lagrange
        multipliers. The scalar and flux fields are reconstructed
        locally.
        """
        from firedrake.assemble import (allocate_matrix,
                                        create_assembly_callable)
        from firedrake.bcs import DirichletBC
        from firedrake.function import Function
        from firedrake.functionspace import FunctionSpace
        from firedrake.interpolation import interpolate

        prefix = pc.getOptionsPrefix() + "hybrid_sc_"
        _, P = pc.getOperators()
        self.cxt = P.getPythonContext()
        if not isinstance(self.cxt, ImplicitMatrixContext):
            raise ValueError("Context must be an ImplicitMatrixContext")

        # Retrieve the mixed function space, which is expected to
        # be of the form: W = (DG_k)^n \times DG_k \times DG_trace
        W = self.cxt.a.arguments()[0].function_space()
        if len(W) != 3:
            raise RuntimeError("Expecting three function spaces.")

        # Assert a specific ordering of the spaces
        # TODO: Clean this up
        assert W[2].ufl_element().family() == "HDiv Trace"

        # Extract trace space
        T = W[2]

        # Need to duplicate a trace space which is NOT
        # associated with a subspace of a mixed space.
        Tr = FunctionSpace(T.mesh(), T.ufl_element())
        bcs = []
        cxt_bcs = self.cxt.row_bcs
        for bc in cxt_bcs:
            assert bc.function_space() == T, (
                "BCs should be imposing vanishing conditions on traces")
            if isinstance(bc.function_arg, Function):
                bc_arg = interpolate(bc.function_arg, Tr)
            else:
                # Constants don't need to be interpolated
                bc_arg = bc.function_arg
            bcs.append(DirichletBC(Tr, bc_arg, bc.sub_domain))

        mat_type = PETSc.Options().getString(prefix + "mat_type", "aij")

        self.r_lambda = Function(T)
        self.residual = Function(W)
        self.solution = Function(W)

        # Perform symbolics only once
        S_expr, r_lambda_expr, u_h_expr, q_h_expr = self._slate_expressions

        self.S = allocate_matrix(S_expr,
                                 bcs=bcs,
                                 form_compiler_parameters=self.cxt.fc_params,
                                 mat_type=mat_type)
        self._assemble_S = create_assembly_callable(
            S_expr,
            tensor=self.S,
            bcs=bcs,
            form_compiler_parameters=self.cxt.fc_params,
            mat_type=mat_type)

        self._assemble_S()
        Smat = self.S.petscmat

        # Set up ksp for the trace problem
        trace_ksp = PETSc.KSP().create(comm=pc.comm)
        trace_ksp.incrementTabLevel(1, parent=pc)
        trace_ksp.setOptionsPrefix(prefix)
        trace_ksp.setOperators(Smat)
        trace_ksp.setUp()
        trace_ksp.setFromOptions()
        self.trace_ksp = trace_ksp

        self._assemble_Srhs = create_assembly_callable(
            r_lambda_expr,
            tensor=self.r_lambda,
            form_compiler_parameters=self.cxt.fc_params)

        q_h, u_h, lambda_h = self.solution.split()

        # Assemble u_h using lambda_h
        self._assemble_u = create_assembly_callable(
            u_h_expr, tensor=u_h, form_compiler_parameters=self.cxt.fc_params)

        # Recover q_h using both u_h and lambda_h
        self._assemble_q = create_assembly_callable(
            q_h_expr, tensor=q_h, form_compiler_parameters=self.cxt.fc_params)
Esempio n. 18
0
    def initialize(self, pc):
        """Set up the problem context. This takes the incoming
        three-field system and constructs the static
        condensation operators using Slate expressions.

        A KSP is created for the reduced system. The eliminated
        variables are recovered via back-substitution.
        """

        from firedrake.assemble import (allocate_matrix,
                                        create_assembly_callable)
        from firedrake.bcs import DirichletBC
        from firedrake.function import Function
        from firedrake.functionspace import FunctionSpace
        from firedrake.interpolation import interpolate

        prefix = pc.getOptionsPrefix() + "condensed_field_"
        A, P = pc.getOperators()
        self.cxt = A.getPythonContext()
        if not isinstance(self.cxt, ImplicitMatrixContext):
            raise ValueError("Context must be an ImplicitMatrixContext")

        self.bilinear_form = self.cxt.a

        # Retrieve the mixed function space
        W = self.bilinear_form.arguments()[0].function_space()
        if len(W) > 3:
            raise NotImplementedError("Only supports up to three function spaces.")

        elim_fields = PETSc.Options().getString(pc.getOptionsPrefix()
                                                + "pc_sc_eliminate_fields",
                                                None)
        if elim_fields:
            elim_fields = [int(i) for i in elim_fields.split(',')]
        else:
            # By default, we condense down to the last field in the
            # mixed space.
            elim_fields = [i for i in range(0, len(W) - 1)]

        condensed_fields = list(set(range(len(W))) - set(elim_fields))
        if len(condensed_fields) != 1:
            raise NotImplementedError("Cannot condense to more than one field")

        c_field, = condensed_fields

        # Need to duplicate a space which is NOT
        # associated with a subspace of a mixed space.
        Vc = FunctionSpace(W.mesh(), W[c_field].ufl_element())
        bcs = []
        cxt_bcs = self.cxt.row_bcs
        for bc in cxt_bcs:
            if bc.function_space().index != c_field:
                raise NotImplementedError("Strong BC set on unsupported space")
            if isinstance(bc.function_arg, Function):
                bc_arg = interpolate(bc.function_arg, Vc)
            else:
                # Constants don't need to be interpolated
                bc_arg = bc.function_arg
            bcs.append(DirichletBC(Vc, bc_arg, bc.sub_domain))

        mat_type = PETSc.Options().getString(prefix + "mat_type", "aij")

        self.c_field = c_field
        self.condensed_rhs = Function(Vc)
        self.residual = Function(W)
        self.solution = Function(W)

        # Get expressions for the condensed linear system
        A_tensor = Tensor(self.bilinear_form)
        reduced_sys = self.condensed_system(A_tensor, self.residual, elim_fields)
        S_expr = reduced_sys.lhs
        r_expr = reduced_sys.rhs

        # Construct the condensed right-hand side
        self._assemble_Srhs = create_assembly_callable(
            r_expr,
            tensor=self.condensed_rhs,
            form_compiler_parameters=self.cxt.fc_params)

        # Allocate and set the condensed operator
        self.S = allocate_matrix(S_expr,
                                 bcs=bcs,
                                 form_compiler_parameters=self.cxt.fc_params,
                                 mat_type=mat_type,
                                 options_prefix=prefix,
                                 appctx=self.get_appctx(pc))

        self._assemble_S = create_assembly_callable(
            S_expr,
            tensor=self.S,
            bcs=bcs,
            form_compiler_parameters=self.cxt.fc_params,
            mat_type=mat_type)

        self._assemble_S()
        Smat = self.S.petscmat

        # If a different matrix is used for preconditioning,
        # assemble this as well
        if A != P:
            self.cxt_pc = P.getPythonContext()
            P_tensor = Tensor(self.cxt_pc.a)
            P_reduced_sys = self.condensed_system(P_tensor,
                                                  self.residual,
                                                  elim_fields)
            S_pc_expr = P_reduced_sys.lhs
            self.S_pc_expr = S_pc_expr

            # Allocate and set the condensed operator
            self.S_pc = allocate_matrix(S_expr,
                                        bcs=bcs,
                                        form_compiler_parameters=self.cxt.fc_params,
                                        mat_type=mat_type,
                                        options_prefix=prefix,
                                        appctx=self.get_appctx(pc))

            self._assemble_S_pc = create_assembly_callable(
                S_pc_expr,
                tensor=self.S_pc,
                bcs=bcs,
                form_compiler_parameters=self.cxt.fc_params,
                mat_type=mat_type)

            self._assemble_S_pc()
            Smat_pc = self.S_pc.petscmat

        else:
            self.S_pc_expr = S_expr
            Smat_pc = Smat

        # Get nullspace for the condensed operator (if any).
        # This is provided as a user-specified callback which
        # returns the basis for the nullspace.
        nullspace = self.cxt.appctx.get("condensed_field_nullspace", None)
        if nullspace is not None:
            nsp = nullspace(Vc)
            Smat.setNullSpace(nsp.nullspace(comm=pc.comm))

        # Create a SNESContext for the DM associated with the trace problem
        self._ctx_ref = self.new_snes_ctx(pc,
                                          S_expr,
                                          bcs,
                                          mat_type,
                                          self.cxt.fc_params,
                                          options_prefix=prefix)

        # Push new context onto the dm associated with the condensed problem
        c_dm = Vc.dm

        # Set up ksp for the condensed problem
        c_ksp = PETSc.KSP().create(comm=pc.comm)
        c_ksp.incrementTabLevel(1, parent=pc)

        # Set the dm for the condensed solver
        c_ksp.setDM(c_dm)
        c_ksp.setDMActive(False)
        c_ksp.setOptionsPrefix(prefix)
        c_ksp.setOperators(A=Smat, P=Smat_pc)
        self.condensed_ksp = c_ksp

        with dmhooks.add_hooks(c_dm, self,
                               appctx=self._ctx_ref,
                               save=False):
            c_ksp.setFromOptions()

        # Set up local solvers for backwards substitution
        self.local_solvers = self.local_solver_calls(A_tensor,
                                                     self.residual,
                                                     self.solution,
                                                     elim_fields)