Esempio n. 1
0
    def form(self, x: PETSc.Vec):
        """This function is called before the residual or Jacobian is
        computed. This is usually used to update ghost values.

        Parameters
        ----------
        x
            The vector containing the latest solution
        """
        x.ghostUpdate(addv=PETSc.InsertMode.INSERT, mode=PETSc.ScatterMode.FORWARD)
Esempio n. 2
0
def _(b: PETSc.Vec,
      L: typing.List[FormMetaClass],
      a,
      bcs: typing.List[DirichletBCMetaClass] = [],
      x0: typing.Optional[PETSc.Vec] = None,
      scale: float = 1.0,
      constants_L=None,
      coeffs_L=None,
      constants_a=None,
      coeffs_a=None) -> PETSc.Vec:
    """Assemble linear forms into a monolithic vector. The vector is not
    zeroed and it is not finalised, i.e. ghost values are not
    accumulated.

    """
    maps = [(form.function_spaces[0].dofmap.index_map,
             form.function_spaces[0].dofmap.index_map_bs) for form in L]
    if x0 is not None:
        x0_local = _cpp.la.petsc.get_local_vectors(x0, maps)
        x0_sub = x0_local
    else:
        x0_local = []
        x0_sub = [None] * len(maps)

    constants_L = [form and _pack_constants(form)
                   for form in L] if constants_L is None else constants_L
    coeffs_L = [{} if form is None else _pack_coefficients(form)
                for form in L] if coeffs_L is None else coeffs_L
    constants_a = [[form and _pack_constants(form) for form in forms]
                   for forms in a] if constants_a is None else constants_a
    coeffs_a = [[{} if form is None else _pack_coefficients(form)
                 for form in forms]
                for forms in a] if coeffs_a is None else coeffs_a

    bcs1 = _bcs_by_block(_extract_spaces(a, 1), bcs)
    b_local = _cpp.la.petsc.get_local_vectors(b, maps)
    for b_sub, L_sub, a_sub, const_L, coeff_L, const_a, coeff_a in zip(
            b_local, L, a, constants_L, coeffs_L, constants_a, coeffs_a):
        _cpp.fem.assemble_vector(b_sub, L_sub, const_L, coeff_L)
        _cpp.fem.apply_lifting(b_sub, a_sub, const_a, coeff_a, bcs1, x0_local,
                               scale)

    _cpp.la.petsc.scatter_local_vectors(b, b_local, maps)
    b.ghostUpdate(addv=PETSc.InsertMode.ADD, mode=PETSc.ScatterMode.REVERSE)

    bcs0 = _bcs_by_block(_extract_spaces(L), bcs)
    offset = 0
    b_array = b.getArray(readonly=False)
    for submap, bc, _x0 in zip(maps, bcs0, x0_sub):
        size = submap[0].size_local * submap[1]
        _cpp.fem.set_bc(b_array[offset:offset + size], bc, _x0, scale)
        offset += size

    return b
 def update(self, solver: dolfinx.nls.petsc.NewtonSolver, dx: PETSc.Vec,
            x: PETSc.Vec):
     # We need to use a vector created on the MPC's space to update ghosts
     self.u_mpc.vector.array = x.array_r
     self.u_mpc.vector.axpy(-1.0, dx)
     self.u_mpc.vector.ghostUpdate(addv=PETSc.InsertMode.INSERT,
                                   mode=PETSc.ScatterMode.FORWARD)
     self.mpc.homogenize(self.u_mpc.vector)
     self.mpc.backsubstitution(self.u_mpc.vector)
     x.array = self.u_mpc.vector.array_r
     x.ghostUpdate(addv=PETSc.InsertMode.INSERT,
                   mode=PETSc.ScatterMode.FORWARD)
Esempio n. 4
0
def eigfind(mac):
    n = mac.getSize()
    Dt = Vec()
    mac.getDiagonal(Dt)
    Dt.reciprocal()
    Dpt = PETSc.Mat().createAIJ([n, n])
    Dpt.setUp()
    Dpt.setDiagonal(Dt)
    Dpt.assemblyBegin()
    Dpt.assemblyEnd()
    tarmat = Dpt * mac

    def solve_eigensystem(Ac, problem_type=SLEPc.EPS.ProblemType.HEP):

        # Create the result vectors
        xr, xi = Ac.createVecs()

        # Setup the eigensolver
        E = SLEPc.EPS().create()
        E.setOperators(Ac, None)
        E.setDimensions(2, PETSc.DECIDE)
        E.setProblemType(problem_type)
        E.setFromOptions()

        # Solve the eigensystem
        E.solve()

        print("")
        its = E.getIterationNumber()
        print("Number of iterations of the method: %i" % its)
        sol_type = E.getType()
        print("Solution method: %s" % sol_type)
        nev, ncv, mpd = E.getDimensions()
        print("Number of requested eigenvalues: %i" % nev)
        tol, maxit = E.getTolerances()
        print("Stopping condition: tol=%.4g, maxit=%d" % (tol, maxit))
        nconv = E.getConverged()
        print("Number of converged eigenpairs: %d" % nconv)
        if nconv > 0:
            print("")
            print("        k          ||Ax-kx||/||kx|| ")
            print("----------------- ------------------")
            for i in range(nconv):
                k = E.getEigenpair(i, xr, xi)
                error = E.computeError(i)
                if k.imag != 0.0:
                    print(" %9f%+9f j  %12g" % (k.real, k.imag, error))
                else:
                    print(" %12f       %12g" % (k.real, error))
            print("")

    solve_eigensystem(tarmat)
Esempio n. 5
0
def _(b: PETSc.Vec, L: typing.Union[Form, cpp.fem.Form]) -> PETSc.Vec:
    """Assemble linear form into an existing vector.

    The vector must already have the correct size and parallel layout
    and wll be zeroed. The returned vector is finalised, i.e. ghost
    values accumulated across MPI processes.

    """
    L_cpp = _create_cpp_form(L)
    with b.localForm() as b_local:
        b_local.set(0.0)
    cpp.fem.assemble_vector(b, L_cpp)
    b.ghostUpdate(addv=PETSc.InsertMode.ADD, mode=PETSc.ScatterMode.REVERSE)
    return b
    def F(self, x: PETSc.Vec, F: PETSc.Vec):
        with F.localForm() as F_local:
            F_local.set(0.0)
        dolfinx_mpc.assemble_vector(self._L, self.mpc, b=F)

        # Apply boundary condition
        dolfinx_mpc.apply_lifting(F, [self._a],
                                  bcs=[self.bcs],
                                  constraint=self.mpc,
                                  x0=[x],
                                  scale=-1.0)
        F.ghostUpdate(addv=PETSc.InsertMode.ADD,
                      mode=PETSc.ScatterMode.REVERSE)
        dolfinx.fem.petsc.set_bc(F, self.bcs, x, -1.0)
Esempio n. 7
0
    def homogenize(self, vector: _PETSc.Vec) -> None:
        """
        For a vector, homogenize (set to zero) the vector components at
        the multi-point constraint slave DoF indices. This is particularly useful
        for nonlinear problems.

        Parameters
        ----------
        vector
            The input vector
        """
        with vector.localForm() as vector_local:
            self._cpp_object.homogenize(vector_local.array_w)
        vector.ghostUpdate(addv=_PETSc.InsertMode.INSERT, mode=_PETSc.ScatterMode.FORWARD)
Esempio n. 8
0
def makenew(Att, phere, emaxhere):
    nhere = Att.getSize()
    Dt = Vec()
    Att.getDiagonal(Dt)
    Dt.reciprocal()
    Dpt = PETSc.Mat().createAIJ([nhere, nhere])
    Dpt.setUp()
    Dpt.setDiagonal(Dt)
    Dpt.assemblyBegin()
    Dpt.assemblyEnd()
    omega = -4.0 / 3.0 / emaxhere
    second = omega * Dpt * Att * phere
    tmat = phere + second
    return tmat
Esempio n. 9
0
    def backsubstitution(self, vector: _PETSc.Vec) -> None:
        """
        For a vector, impose the multi-point constraint by backsubstiution.
        This function is used after solving the reduced problem to obtain the values
        at the slave degrees of freedom

        Parameters
        ----------
        vector
            The input vector
        """
        # Unravel data from constraint
        with vector.localForm() as vector_local:
            self._cpp_object.backsubstitution(vector_local.array_w)
        vector.ghostUpdate(addv=_PETSc.InsertMode.INSERT, mode=_PETSc.ScatterMode.FORWARD)
Esempio n. 10
0
def assemble_vector(form: ufl.form.Form,
                    constraint: MultiPointConstraint,
                    b: _PETSc.Vec = None) -> _PETSc.Vec:
    """
    Assemble a linear form into vector b with corresponding multi point constraint

    Parameters
    ----------
    form
        The linear form
    constraint
        The multi point constraint
    b
        PETSc vector to assemble into (optional)

    Returns
    -------
    PETSc.Vec
        The assembled linear form
    """

    if b is None:
        b = _cpp.la.petsc.create_vector(
            constraint.function_space.dofmap.index_map,
            constraint.function_space.dofmap.index_map_bs)
    t = Timer("~MPC: Assemble vector (C++)")
    with b.localForm() as b_local:
        b_local.set(0.0)
        dolfinx_mpc.cpp.mpc.assemble_vector(b_local, form,
                                            constraint._cpp_object)
    t.stop()
    return b
Esempio n. 11
0
def apply_lifting(b: PETSc.Vec,
                  a: typing.List[typing.Union[Form, cpp.fem.Form]],
                  bcs: typing.List[typing.List[DirichletBC]],
                  x0: typing.Optional[typing.List[PETSc.Vec]] = [],
                  scale: float = 1.0) -> None:
    """Modify RHS vector b for lifting of Dirichlet boundary conditions.
    It modifies b such that:

        b <- b - scale * A_j (g_j - x0_j)

    where j is a block (nest) index. For a non-blocked problem j = 0.
    The boundary conditions bcs are on the trial spaces V_j. The forms
    in [a] must have the same test space as L (from which b was built),
    but the trial space may differ. If x0 is not supplied, then it is
    treated as zero.

    Ghost contributions are not accumulated (not sent to owner). Caller
    is responsible for calling VecGhostUpdateBegin/End.
    """
    with contextlib.ExitStack() as stack:
        x0 = [stack.enter_context(x.localForm()) for x in x0]
        x0_r = [x.array_r for x in x0]
        b_local = stack.enter_context(b.localForm())
        cpp.fem.apply_lifting(b_local.array_w, _create_cpp_form(a), bcs, x0_r,
                              scale)
Esempio n. 12
0
 def F(self, x: PETSc.Vec, b: PETSc.Vec):
     """Assemble the residual F into the vector b.
     Parameters
     ----------
     x
         The vector containing the latest solution
     b
         Vector to assemble the residual into
     """
     # Reset the residual vector
     with b.localForm() as b_local:
         b_local.set(0.0)
     fem.assemble_vector(b, self._L)
     # Apply boundary condition
     fem.apply_lifting(b, [self._a], [self.bcs], [x], -1.0)
     b.ghostUpdate(addv=PETSc.InsertMode.ADD, mode=PETSc.ScatterMode.REVERSE)
     fem.set_bc(b, self.bcs, x, -1.0)
Esempio n. 13
0
    def F(self, snes: PETSc.SNES, x: PETSc.Vec, b: PETSc.Vec):
        """Assemble the residual F into the vector b.

        Parameters
        ==========
        snes: the snes object
        x: Vector containing the latest solution.
        b: Vector to assemble the residual into.
        """
        # We need to assign the vector to the function

        x.ghostUpdate(addv=PETSc.InsertMode.INSERT,
                      mode=PETSc.ScatterMode.FORWARD)
        x.copy(self.u.vector)
        self.u.vector.ghostUpdate(addv=PETSc.InsertMode.INSERT,
                                  mode=PETSc.ScatterMode.FORWARD)

        # Zero the residual vector
        # import pdb; pdb.set_trace()
        with b.localForm() as b_local:
            b_local.set(0.0)
        assemble_vector(b, self.F_form)

        # Apply boundary conditions
        apply_lifting(b, [self.J_form], [self.bcs], [x], -1.0)
        b.ghostUpdate(addv=PETSc.InsertMode.ADD,
                      mode=PETSc.ScatterMode.REVERSE)
        set_bc(b, self.bcs, x, -1.0)
Esempio n. 14
0
def _(b: PETSc.Vec, L: typing.Union[Form, cpp.fem.Form]) -> PETSc.Vec:
    """Assemble linear form into an existing PETSc vector. The vector is not
    zeroed before assembly and it is not finalised, qi.e. ghost values are
    not accumulated on the owning processes.

    """
    with b.localForm() as b_local:
        cpp.fem.assemble_vector(b_local.array_w, _create_cpp_form(L))
    return b
Esempio n. 15
0
def set_bc_nest(b: PETSc.Vec,
                bcs: typing.List[typing.List[DirichletBCMetaClass]],
                x0: typing.Optional[PETSc.Vec] = None,
                scale: float = 1.0) -> None:
    """Apply the function :func:`dolfinx.fem.set_bc` to each sub-vector of a nested PETSc Vector."""
    _b = b.getNestSubVecs()
    x0 = len(_b) * [None] if x0 is None else x0.getNestSubVecs()
    for b_sub, bc, x_sub in zip(_b, bcs, x0):
        set_bc(b_sub, bc, x_sub, scale)
Esempio n. 16
0
def _(b: PETSc.Vec, L: typing.List[typing.Union[Form, cpp.fem.Form]]) -> PETSc.Vec:
    """Assemble linear forms into a nested PETSc (VecNest) vector. The vector is not
    zeroed before assembly and it is not finalised, qi.e. ghost values
    are not accumulated on the owning processes.

    """
    for b_sub, L_sub in zip(b.getNestSubVecs(), _create_cpp_form(L)):
        cpp.fem.assemble_vector(b_sub, L_sub)
    return b
Esempio n. 17
0
def assemble_vector_nest(b: _PETSc.Vec, L: Sequence[_fem.FormMetaClass],
                         constraints: Sequence[MultiPointConstraint]):
    """
    Assemble a linear form into a PETSc vector of type "nest"

    Parameters
    ----------
    b
        A PETSc vector of type "nest"
    L
        A sequence of linear forms
    constraints
        An ordered list of multi point constraints
    """
    assert len(constraints) == len(L)
    assert b.getType() == "nest"

    b_sub_vecs = b.getNestSubVecs()
    for i, L_row in enumerate(L):
        assemble_vector(L_row, constraints[i], b=b_sub_vecs[i])
Esempio n. 18
0
def smoother_local(Ah, bh, ugh, Acor, poordof):
    '''
    Local correction smoother. Ah is the whole matrix,
    bh is the whole rhs, ugh is the initial guess or input.
    Also need a vector B which contains the indices of all bad nodes.
    fixa is the submatrix, fixr is the corresponding residual part and
    fixu is the error obtained.
    '''
    rh = bh - Ah * ugh
    badpet = PETSc.IS()
    badpet.createGeneral(poordof)
    rcor = Vec()
    rh.getSubVector(badpet, rcor)
    nb = rcor.getSize()
    ecor = direct(Acor, rcor)
    for i in range(nb):
        row = poordof[i]
        ugh[row] += ecor.getValue(i)

    return ugh
Esempio n. 19
0
def _(b: PETSc.Vec,
      L: typing.List[typing.Union[Form, cpp.fem.Form]],
      a,
      bcs: typing.List[DirichletBC] = [],
      x0: typing.Optional[PETSc.Vec] = None,
      scale: float = 1.0) -> PETSc.Vec:
    """Assemble linear forms into a monolithic vector. The vector is not
    zeroed and it is not finalised, i.e. ghost values are not
    accumulated.

    """
    maps = [
        form.function_spaces[0].dofmap.index_map
        for form in _create_cpp_form(L)
    ]
    if x0 is not None:
        x0_local = cpp.la.get_local_vectors(x0, maps)
        x0_sub = x0_local
    else:
        x0_local = []
        x0_sub = [None] * len(maps)

    bcs1 = cpp.fem.bcs_cols(_create_cpp_form(a), bcs)
    b_local = cpp.la.get_local_vectors(b, maps)
    for b_sub, L_sub, a_sub, bc in zip(b_local, L, a, bcs1):
        cpp.fem.assemble_vector(b_sub, _create_cpp_form(L_sub))
        cpp.fem.apply_lifting(b_sub, _create_cpp_form(a_sub), bc, x0_local,
                              scale)

    cpp.la.scatter_local_vectors(b, b_local, maps)
    b.ghostUpdate(addv=PETSc.InsertMode.ADD, mode=PETSc.ScatterMode.REVERSE)

    bcs0 = cpp.fem.bcs_rows(_create_cpp_form(L), bcs)
    offset = 0
    b_array = b.getArray(readonly=False)
    for submap, bc, _x0 in zip(maps, bcs0, x0_sub):
        size = submap.size_local * submap.block_size
        cpp.fem.set_bc(b_array[offset:offset + size], bc, _x0, scale)
        offset += size

    return b
Esempio n. 20
0
def _(b: PETSc.Vec, L: FormMetaClass, coeffs=Coefficients(None,
                                                          None)) -> PETSc.Vec:
    """Assemble linear form into an existing PETSc vector. The vector is
    not zeroed before assembly and it is not finalised, i.e. ghost
    values are not accumulated on the owning processes.

    """
    c = (coeffs[0] if coeffs[0] is not None else pack_constants(L),
         coeffs[1] if coeffs[1] is not None else pack_coefficients(L))
    with b.localForm() as b_local:
        _cpp.fem.assemble_vector(b_local.array_w, L, c[0], c[1])
    return b
Esempio n. 21
0
def local_correction(Ah, bh, ugh, Acc, btt, bset):
    '''
    Local correction smoother. Ah is the whole matrix,
    bh is the whole rhs, ugh is the initial guess or input.
    Also need a vector B which contains the indices of all bad nodes.
    fixa is the submatrix, fixr is the corresponding residual part and
    fixu is the error obtained.
    '''
    rh = bh - Ah * ugh
    bcc = Vec()
    #ucc = Vec()
    rh.getSubVector(btt, bcc)
    nb = bcc.getSize()
    #ugh.getSubVector(btt,ucc)
    #ucc = cg(Acc,bcc,10)
    ucc = direct(Acc, bcc)
    for i in range(nb):
        row = bset[i]
        ugh[row] += ucc.getValue(i)

    return ugh
Esempio n. 22
0
def apply_lifting(b: PETSc.Vec,
                  a: typing.List[FormMetaClass],
                  bcs: typing.List[typing.List[DirichletBCMetaClass]],
                  x0: typing.Optional[typing.List[PETSc.Vec]] = [],
                  scale: float = 1.0,
                  constants=None,
                  coeffs=None) -> None:
    """Apply the function :func:`dolfinx.fem.apply_lifting` to a PETSc Vector."""
    with contextlib.ExitStack() as stack:
        x0 = [stack.enter_context(x.localForm()) for x in x0]
        x0_r = [x.array_r for x in x0]
        b_local = stack.enter_context(b.localForm())
        assemble.apply_lifting(b_local.array_w, a, bcs, x0_r, scale, constants,
                               coeffs)
Esempio n. 23
0
def apply_lifting_nest(b: PETSc.Vec,
                       a: typing.List[typing.List[typing.Union[Form, cpp.fem.Form]]],
                       bcs: typing.List[DirichletBC],
                       x0: typing.Optional[PETSc.Vec] = None,
                       scale: float = 1.0) -> PETSc.Vec:
    """Modify nested vector for lifting of Dirichlet boundary conditions.

    """
    x0 = [] if x0 is None else x0.getNestSubVecs()
    _a = _create_cpp_form(a)
    bcs1 = cpp.fem.bcs_cols(_a, bcs)
    for b_sub, a_sub, bc1 in zip(b.getNestSubVecs(), _a, bcs1):
        apply_lifting(b_sub, a_sub, bc1, x0, scale)
    return b
Esempio n. 24
0
def set_bc_nest(b: PETSc.Vec,
                bcs: typing.List[typing.List[DirichletBC]],
                x0: typing.Optional[PETSc.Vec] = None,
                scale: float = 1.0) -> None:
    """Insert boundary condition values into nested vector. Only local (owned)
    entries are set, hence communication after calling this function is
    not required unless the ghost entries need to be updated to the
    boundary condition value.

    """
    _b = b.getNestSubVecs()
    x0 = len(_b) * [None] if x0 is None else x0.getNestSubVecs()
    for b_sub, bc, x_sub in zip(_b, bcs, x0):
        set_bc(b_sub, bc, x_sub, scale)
Esempio n. 25
0
def _(
    b: PETSc.Vec,
    L: typing.List[FormMetaClass],
    coeffs=Coefficients(None, None)) -> PETSc.Vec:
    """Assemble linear forms into a nested PETSc (VecNest) vector. The
    vector is not zeroed before assembly and it is not finalised, i.e.
    ghost values are not accumulated on the owning processes.

    """
    c = (coeffs[0] if coeffs[0] is not None else pack_constants(L),
         coeffs[1] if coeffs[1] is not None else pack_coefficients(L))
    for b_sub, L_sub, constant, coeff in zip(b.getNestSubVecs(), L, c[0],
                                             c[1]):
        with b_sub.localForm() as b_local:
            _cpp.fem.assemble_vector(b_local.array_w, L_sub, constant, coeff)
    return b
Esempio n. 26
0
def _(b: PETSc.Vec,
      L: typing.List[FormMetaClass],
      constants=None,
      coeffs=None) -> PETSc.Vec:
    """Assemble linear forms into a nested PETSc (VecNest) vector. The
    vector is not zeroed before assembly and it is not finalised, i.e.
    ghost values are not accumulated on the owning processes.

    """
    constants = [None] * len(L) if constants is None else constants
    coeffs = [None] * len(L) if coeffs is None else coeffs
    for b_sub, L_sub, const, coeff in zip(b.getNestSubVecs(), L, constants,
                                          coeffs):
        with b_sub.localForm() as b_local:
            assemble.assemble_vector(b_local.array_w, L_sub, const, coeff)
    return b
Esempio n. 27
0
def apply_lifting_nest(
    b: PETSc.Vec,
    a: typing.List[typing.List[FormMetaClass]],
    bcs: typing.List[DirichletBCMetaClass],
    x0: typing.Optional[PETSc.Vec] = None,
    scale: float = 1.0,
    coeffs=Coefficients(None, None)) -> PETSc.Vec:
    """Modify nested vector for lifting of Dirichlet boundary conditions.

    """
    x0 = [] if x0 is None else x0.getNestSubVecs()
    c = (coeffs[0] if coeffs[0] is not None else pack_constants(a),
         coeffs[1] if coeffs[1] is not None else pack_coefficients(a))
    bcs1 = bcs_by_block(extract_function_spaces(a, 1), bcs)
    for b_sub, a_sub, constants, coeffs in zip(b.getNestSubVecs(), a, c[0],
                                               c[1]):
        apply_lifting(b_sub, a_sub, bcs1, x0, scale, (constants, coeffs))
    return b
Esempio n. 28
0
def apply_lifting_nest(b: PETSc.Vec,
                       a: typing.List[typing.List[FormMetaClass]],
                       bcs: typing.List[DirichletBCMetaClass],
                       x0: typing.Optional[PETSc.Vec] = None,
                       scale: float = 1.0,
                       constants=None,
                       coeffs=None) -> PETSc.Vec:
    """Apply the function :func:`dolfinx.fem.apply_lifting` to each sub-vector in a nested PETSc Vector."""

    x0 = [] if x0 is None else x0.getNestSubVecs()
    bcs1 = _bcs_by_block(_extract_spaces(a, 1), bcs)

    constants = [[form and _pack_constants(form) for form in forms]
                 for forms in a] if constants is None else constants
    coeffs = [[{} if form is None else _pack_coefficients(form)
               for form in forms] for forms in a] if coeffs is None else coeffs
    for b_sub, a_sub, const, coeff in zip(b.getNestSubVecs(), a, constants,
                                          coeffs):
        apply_lifting(b_sub, a_sub, bcs1, x0, scale, const, coeff)
    return b
Esempio n. 29
0
def apply_lifting(b: _PETSc.Vec,
                  form: List[_fem.FormMetaClass],
                  bcs: List[List[_fem.DirichletBCMetaClass]],
                  constraint: MultiPointConstraint,
                  x0: List[_PETSc.Vec] = [],
                  scale: float = 1.0):
    """
    Apply lifting to vector b, i.e.

    .. math::
        b <- b - scale * K^T (A_j (g_j - x0_j))

    Parameters
    ----------
    b
        PETSc vector to assemble into
    form
        The linear form
    bcs
        List of Dirichlet boundary conditions
    constraint
        The multi point constraint
    x0
        List of vectors
    scale
        Scaling for lifting

    Returns
    -------
    PETSc.Vec
        The assembled linear form
    """
    t = Timer("~MPC: Apply lifting (C++)")
    with contextlib.ExitStack() as stack:
        x0 = [stack.enter_context(x.localForm()) for x in x0]
        x0_r = [x.array_r for x in x0]
        b_local = stack.enter_context(b.localForm())
        dolfinx_mpc.cpp.mpc.apply_lifting(b_local.array_w, form, bcs, x0_r,
                                          scale, constraint._cpp_object)
    t.stop()
    def f1(self, t: float, u: PETSc.Vec, v: PETSc.Vec, result: PETSc.Vec) -> PETSc.Vec:
        """For dv/dt = f(t, u, v), compute and return f"""
        # # Update boundary condition
        # with self.g.vector.localForm() as g_local:
        #     g_local.set(-2 * self.omega / self.c * np.cos(self.omega * t))

        # Set up plane wave incident field - it's made of two parts, a cosine
        # and a sine, which are multiplied by cos(omega*t) and sin(omega*t)
        # later on
        with self.g1.vector.localForm() as g1_local:
            g1_local.set(np.cos(self.omega * t))

        with self.g2.vector.localForm() as g2_local:
            g2_local.set(np.sin(self.omega * t))

        # Update fields that f depends on
        u.copy(result=self.u.vector)
        self.u.vector.ghostUpdate(addv=PETSc.InsertMode.INSERT,
                                  mode=PETSc.ScatterMode.FORWARD)
        v.copy(result=self.v.vector)
        self.v.vector.ghostUpdate(addv=PETSc.InsertMode.INSERT,
                                  mode=PETSc.ScatterMode.FORWARD)
        # Assemble b
        if self.b is None:
            self.b = dolfinx.fem.assemble_vector(self.L1)
        else:
            with self.b.localForm() as b_local:
                b_local.set(0.0)
            dolfinx.fem.assemble_vector(self.b, self.L1)
        self.b.ghostUpdate(addv=PETSc.InsertMode.ADD,
                           mode=PETSc.ScatterMode.REVERSE)

        if result is None:
            result = v.duplicate()

        # Solve
        if self.lumped:
            result.pointwiseDivide(self.b, self.M)
        else:
            self.solver.solve(self.b, result)

        return result