def solve(self, *args, **kwargs):
        '''To disable the annotation, just pass :py:data:`annotate=False` to this routine, and it acts exactly like the
        Dolfin solve call. This is useful in cases where the solve is known to be irrelevant or diagnostic
        for the purposes of the adjoint computation (such as projecting fields to other function spaces
        for the purposes of visualisation).'''

        to_annotate = utils.to_annotate(kwargs.pop("annotate", None))

        if to_annotate:
            factory = args[0]
            vec = args[1]
            b = dolfin.as_backend_type(vec).__class__()

            factory.F(b=b, x=vec)

            F = b.form
            bcs = b.bcs

            u = vec.function
            var = adjglobals.adj_variables[u]

            solving.annotate(F == 0, u, bcs, solver_parameters={"newton_solver": self.parameters.to_dict()})

        newargs = [self] + list(args)
        out = dolfin.NewtonSolver.solve(*newargs, **kwargs)

        if to_annotate and dolfin.parameters["adjoint"]["record_all"]:
            adjglobals.adjointer.record_variable(adjglobals.adj_variables[u], libadjoint.MemoryStorage(adjlinalg.Vector(u)))

        return out
    def solve(self, annotate=None):
        """To disable the annotation, just pass :py:data:`annotate=False` to this routine, and it acts exactly like the
        Dolfin solve call. This is useful in cases where the solve is known to be irrelevant or diagnostic
        for the purposes of the adjoint computation (such as projecting fields to other function spaces
        for the purposes of visualisation)."""

        annotate = utils.to_annotate(annotate)

        if annotate:
            problem = self.problem
            solving.annotate(
                problem.F == 0,
                problem.u,
                problem.bcs,
                J=problem.J,
                solver_parameters=compatibility.to_dict(self.parameters),
            )

        out = backend.NonlinearVariationalSolver.solve(self)

        if annotate and backend.parameters["adjoint"]["record_all"]:
            adjglobals.adjointer.record_variable(
                adjglobals.adj_variables[self.problem.u], libadjoint.MemoryStorage(adjlinalg.Vector(self.problem.u))
            )

        return out
Example #3
0
def project_dolfin(v, V=None, bcs=None, mesh=None, solver_type="cg", preconditioner_type="default", form_compiler_parameters=None, annotate=None, name=None):
    '''The project call performs an equation solve, and so it too must be annotated so that the
    adjoint and tangent linear models may be constructed automatically by libadjoint.

    To disable the annotation of this function, just pass :py:data:`annotate=False`. This is useful in
    cases where the solve is known to be irrelevant or diagnostic for the purposes of the adjoint
    computation (such as projecting fields to other function spaces for the purposes of
    visualisation).'''

    to_annotate = utils.to_annotate(annotate)

    if isinstance(v, backend.Expression) and (annotate is not True):
        to_annotate = False

    if isinstance(v, backend.Constant) and (annotate is not True):
        to_annotate = False

    out = backend.project(v=v, V=V, bcs=bcs, mesh=mesh, solver_type=solver_type, preconditioner_type=preconditioner_type, form_compiler_parameters=form_compiler_parameters)
    out = utils.function_to_da_function(out)

    if name is not None:
        out.adj_name = name
        out.rename(name, "a Function from dolfin-adjoint")

    if to_annotate:
        # reproduce the logic from project. This probably isn't future-safe, but anyway

        if V is None:
            V = backend.fem.projection._extract_function_space(v, mesh)
        if mesh is None:
            mesh = V.mesh()

        # Define variational problem for projection
        w = backend.TestFunction(V)
        Pv = backend.TrialFunction(V)
        a = backend.inner(w, Pv)*backend.dx(domain=mesh)
        L = backend.inner(w, v)*backend.dx(domain=mesh)

        solving.annotate(a == L, out, bcs, solver_parameters={"linear_solver": solver_type, "preconditioner": preconditioner_type, "symmetric": True})

        if backend.parameters["adjoint"]["record_all"]:
            adjglobals.adjointer.record_variable(adjglobals.adj_variables[out], libadjoint.MemoryStorage(adjlinalg.Vector(out)))

    return out
    def solve_local(self, x_vec, b_vec, b_dofmap, **kwargs):
        # Figure out whether to annotate or not
        to_annotate = utils.to_annotate(kwargs.pop("annotate", None))
        x = x_vec.function

        if to_annotate:
            L = b_vec.form

            # Set Matrix class for solving the adjoint systems
            solving.annotate(self.a == L, x, \
                            solver_parameters={"solver_type": self.solver_type, "factorize" : self.adjoint_factorize}, \
                            matrix_class=LocalSolverMatrix)

        # Use standard local solver
        out = dolfin.LocalSolver.solve_local(self, x_vec, b_vec, b_dofmap)

        if to_annotate:
            # checkpointing
            if dolfin.parameters["adjoint"]["record_all"]:
                adjglobals.adjointer.record_variable(adjglobals.adj_variables[x],
                           libadjoint.MemoryStorage(adjlinalg.Vector(x)))

        return out
    def solve(self, *args, **kwargs):
        '''To disable the annotation, just pass :py:data:`annotate=False` to this routine, and it acts exactly like the
        Dolfin solve call. This is useful in cases where the solve is known to be irrelevant or diagnostic
        for the purposes of the adjoint computation (such as projecting fields to other function spaces
        for the purposes of visualisation).'''

        to_annotate = utils.to_annotate(kwargs.pop("annotate", None))

        if to_annotate:
            if len(args) == 3:
                A = args[0]
                x = args[1]
                b = args[2]
            elif len(args) == 2:
                A = self.operators[0]
                x = args[0]
                b = args[1]

            bcs = []
            if hasattr(A, 'bcs'):
                bcs += A.bcs
            if hasattr(b, 'bcs'):
                bcs += b.bcs
            bcs = misc.uniq(bcs)

            assemble_system = A.assemble_system

            A = A.form
            u = x.function
            b = b.form

            if self.operators[1] is not None:
                P = self.operators[1].form
            else:
                P = None

            solver_parameters = self.solver_parameters
            parameters = self.parameters.to_dict()
            fn_space = u.function_space()
            has_preconditioner = P is not None
            nsp = self.nsp
            tnsp = self.tnsp

            if nsp is not None:
                msg = """
                The transpose nullspace is not set.

                The nullspace of the PETScKrylovSolver is set. In this case,
                the transpose nullspace must also be set, use:

                  solver.set_transpose_nullspace(nullspace)
                """
                assert tnsp is not None, msg

            if self.__global_list_idx__ is None:
                self.__global_list_idx__ = len(petsc_krylov_solvers)
                petsc_krylov_solvers.append(self)
                adj_petsc_krylov_solvers.append(None)
            idx = self.__global_list_idx__

            class PETScKrylovSolverMatrix(adjlinalg.Matrix):
                def __init__(selfmat, *args, **kwargs):
                    if 'initial_guess' in kwargs:
                        selfmat.initial_guess = kwargs['initial_guess']
                        del kwargs['initial_guess']
                    else:
                        selfmat.initial_guess = None

                    replace_map = kwargs['replace_map']
                    del kwargs['replace_map']

                    adjlinalg.Matrix.__init__(selfmat, *args, **kwargs)

                    selfmat.adjoint = kwargs['adjoint']
                    if P is None:
                        selfmat.operators = (dolfin.replace(A, replace_map), None)
                    else:
                        selfmat.operators = (dolfin.replace(A, replace_map), dolfin.replace(P, replace_map))

                def axpy(selfmat, alpha, x):
                    raise libadjoint.exceptions.LibadjointErrorNotImplemented("Shouldn't ever get here")

                def solve(selfmat, var, b):
                    if selfmat.adjoint:
                        operators = transpose_operators(selfmat.operators)
                    else:
                        operators = selfmat.operators

                    # Fetch/construct the solver
                    if var.type in ['ADJ_FORWARD', 'ADJ_TLM']:
                        solver = petsc_krylov_solvers[idx]
                        need_to_set_operator = self._need_to_reset_operator
                    else:
                        if adj_petsc_krylov_solvers[idx] is None:
                            need_to_set_operator = True
                            adj_petsc_krylov_solvers[idx] = PETScKrylovSolver(*solver_parameters)
                            adj_ksp = adj_petsc_krylov_solvers[idx].ksp()
                            fwd_ksp = petsc_krylov_solvers[idx].ksp()
                            adj_ksp.setOptionsPrefix(fwd_ksp.getOptionsPrefix())
                            adj_ksp.setType(fwd_ksp.getType())
                            adj_ksp.pc.setType(fwd_ksp.pc.getType())
                            adj_ksp.setFromOptions()
                        else:
                            need_to_set_operator = self._need_to_reset_operator
                        solver = adj_petsc_krylov_solvers[idx]
                        # FIXME: work around DOLFIN bug #583
                        try:
                            solver.parameters.convergence_norm_type
                        except:
                            solver.parameters.convergence_norm_type = "preconditioned"
                        # end FIXME
                    solver.parameters.update(parameters)
                    self._need_to_reset_operator = False

                    if selfmat.adjoint:
                        (nsp_, tnsp_) = (tnsp, nsp)
                    else:
                        (nsp_, tnsp_) = (nsp, tnsp)

                    x = dolfin.Function(fn_space)
                    if selfmat.initial_guess is not None and var.type == 'ADJ_FORWARD':
                        x.vector()[:] = selfmat.initial_guess.vector()

                    if b.data is None:
                        dolfin.info_red("Warning: got zero RHS for the solve associated with variable %s" % var)
                        return adjlinalg.Vector(x)

                    if var.type in ['ADJ_TLM', 'ADJ_ADJOINT']:
                        selfmat.bcs = [utils.homogenize(bc) for bc in selfmat.bcs if isinstance(bc, dolfin.cpp.DirichletBC)] + [bc for bc in selfmat.bcs if not isinstance(bc, dolfin.cpp.DirichletBC)]

                    # This is really hideous. Sorry.
                    if isinstance(b.data, dolfin.Function):
                        rhs = b.data.vector().copy()
                        [bc.apply(rhs) for bc in selfmat.bcs]

                        if need_to_set_operator:
                            if assemble_system: # if we called assemble_system, rather than assemble
                                v = dolfin.TestFunction(fn_space)
                                (A, rhstmp) = dolfin.assemble_system(operators[0], dolfin.inner(b.data, v)*dolfin.dx, selfmat.bcs)
                                if has_preconditioner:
                                    (P, rhstmp) = dolfin.assemble_system(operators[1], dolfin.inner(b.data, v)*dolfin.dx, selfmat.bcs)
                                    solver.set_operators(A, P)
                                else:
                                    solver.set_operator(A)
                            else: # we called assemble
                                A = dolfin.assemble(operators[0])
                                [bc.apply(A) for bc in selfmat.bcs]
                                if has_preconditioner:
                                    P = dolfin.assemble(operators[1])
                                    [bc.apply(P) for bc in selfmat.bcs]
                                    solver.set_operators(A, P)
                                else:
                                    solver.set_operator(A)
                    else:

                        if assemble_system: # if we called assemble_system, rather than assemble
                            (A, rhs) = dolfin.assemble_system(operators[0], b.data, selfmat.bcs)
                            if need_to_set_operator:
                                if has_preconditioner:
                                    (P, rhstmp) = dolfin.assemble_system(operators[1], b.data, selfmat.bcs)
                                    solver.set_operators(A, P)
                                else:
                                    solver.set_operator(A)
                        else: # we called assemble
                            A = dolfin.assemble(operators[0])
                            rhs = dolfin.assemble(b.data)
                            [bc.apply(A) for bc in selfmat.bcs]
                            [bc.apply(rhs) for bc in selfmat.bcs]
                            if need_to_set_operator:
                                if has_preconditioner:
                                    P = dolfin.assemble(operators[1])
                                    [bc.apply(P) for bc in selfmat.bcs]
                                    solver.set_operators(A, P)
                                else:
                                    solver.set_operator(A)

                    if need_to_set_operator:
                        print "|A|: %.6e" % A.norm("frobenius")

                    # Set the nullspace for the linear operator
                    if nsp_ is not None and need_to_set_operator:
                        dolfin.as_backend_type(A).set_nullspace(nsp_)

                    # (Possibly override the user in) orthogonalize
                    # the right-hand-side
                    if tnsp_ is not None:
                        tnsp_.orthogonalize(rhs)

                    print "%s: |b|: %.6e" % (var, rhs.norm("l2"))
                    solver.solve(x.vector(), rhs)
                    return adjlinalg.Vector(x)

            solving.annotate(A == b, u, bcs, matrix_class=PETScKrylovSolverMatrix, initial_guess=parameters['nonzero_initial_guess'], replace_map=True)

        out = dolfin.PETScKrylovSolver.solve(self, *args, **kwargs)

        if to_annotate and dolfin.parameters["adjoint"]["record_all"]:
            adjglobals.adjointer.record_variable(adjglobals.adj_variables[u], libadjoint.MemoryStorage(adjlinalg.Vector(u)))

        return out
Example #6
0
  def solve(self, *args, **kwargs):
    '''To disable the annotation, just pass :py:data:`annotate=False` to this routine, and it acts exactly like the
    Dolfin solve call. This is useful in cases where the solve is known to be irrelevant or diagnostic
    for the purposes of the adjoint computation (such as projecting fields to other function spaces
    for the purposes of visualisation).'''

    to_annotate = utils.to_annotate(kwargs.pop("annotate", None))

    if to_annotate:
      if len(args) == 2:
        try:
          A = self.matrix.form
        except AttributeError:
          raise libadjoint.exceptions.LibadjointErrorInvalidInputs("Your matrix A has to have the .form attribute: was it assembled after from dolfin_adjoint import *?")

        try:
          self.op_bcs = self.matrix.bcs
        except AttributeError:
          self.op_bcs = []

        try:
          x = args[0].function
        except AttributeError:
          raise libadjoint.exceptions.LibadjointErrorInvalidInputs("Your solution x has to have a .function attribute; is it the .vector() of a Function?")

        try:
          b = args[1].form
        except AttributeError:
          raise libadjoint.exceptions.LibadjointErrorInvalidInputs("Your RHS b has to have the .form attribute: was it assembled after from dolfin_adjoint import *?")

        try:
          eq_bcs = misc.uniq(self.op_bcs + args[1].bcs)
        except AttributeError:
          eq_bcs = self.op_bcs

      elif len(args) == 3:
        A = args[0].form
        try:
          x = args[1].function
        except AttributeError:
          raise libadjoint.exceptions.LibadjointErrorInvalidInputs("Your solution x has to have a .function attribute; is it the .vector() of a Function?")

        try:
          self.op_bcs = A.bcs
        except AttributeError:
          self.op_bcs = []

        try:
          b = args[2].form
        except AttributeError:
          raise libadjoint.exceptions.LibadjointErrorInvalidInputs("Your RHS b has to have the .form attribute: was it assembled after from dolfin_adjoint import *?")

        try:
          eq_bcs = misc.uniq(self.op_bcs + args[2].bcs)
        except AttributeError:
          eq_bcs = self.op_bcs

      else:
        raise libadjoint.exceptions.LibadjointErrorInvalidInputs("LUSolver.solve() must be called with either (A, x, b) or (x, b).")

      if self.parameters["reuse_factorization"] and self.__global_list_idx__ is None:
        self.__global_list_idx__ = len(lu_solvers)
        lu_solvers.append(self)
        adj_lu_solvers.append(None)

      solving.annotate(A == b, x, eq_bcs, solver_parameters={"linear_solver": "lu"}, matrix_class=make_LUSolverMatrix(self.__global_list_idx__, self.parameters["reuse_factorization"]))

    out = dolfin.LUSolver.solve(self, *args, **kwargs)

    if to_annotate:
      if dolfin.parameters["adjoint"]["record_all"]:
        adjglobals.adjointer.record_variable(adjglobals.adj_variables[x], libadjoint.MemoryStorage(adjlinalg.Vector(x)))

    return out
    def solve(self, *args, **kwargs):
        '''To disable the annotation, just pass :py:data:`annotate=False` to this routine, and it acts exactly like the
        Dolfin solve call. This is useful in cases where the solve is known to be irrelevant or diagnostic
        for the purposes of the adjoint computation (such as projecting fields to other function spaces
        for the purposes of visualisation).'''

        to_annotate = utils.to_annotate(kwargs.pop("annotate", None))
        nsp = self.nsp

        if to_annotate:
            if len(args) == 3:
                A = args[0]
                x = args[1]
                b = args[2]
            elif len(args) == 2:
                A = self.operators[0]
                x = args[0]
                b = args[1]

            bcs = []
            if hasattr(A, 'bcs'):
                bcs += A.bcs
            if hasattr(b, 'bcs'):
                bcs += b.bcs
            bcs = misc.uniq(bcs)

            assemble_system = A.assemble_system

            A = A.form
            u = x.function
            b = b.form

            if self.operators[1] is not None:
                P = self.operators[1].form
            else:
                P = None

            solver_parameters = self.solver_parameters
            parameters = self.parameters.to_dict()
            fn_space = u.function_space()
            has_preconditioner = P is not None

            class LinearSolverMatrix(adjlinalg.Matrix):
                def __init__(self, *args, **kwargs):
                    if 'initial_guess' in kwargs:
                        self.initial_guess = kwargs['initial_guess']
                        del kwargs['initial_guess']
                    else:
                        self.initial_guess = None

                    replace_map = kwargs['replace_map']
                    del kwargs['replace_map']

                    adjlinalg.Matrix.__init__(self, *args, **kwargs)

                    self.adjoint = kwargs['adjoint']
                    if P is None:
                        self.operators = (dolfin.replace(A, replace_map), None)
                    else:
                        self.operators = (dolfin.replace(A, replace_map), dolfin.replace(P, replace_map))

                def axpy(self, alpha, x):
                    raise libadjoint.exceptions.LibadjointErrorNotImplemented("Shouldn't ever get here")

                def solve(self, var, b):
                    if self.adjoint:
                        operators = transpose_operators(self.operators)
                    else:
                        operators = self.operators

                    solver = dolfin.LinearSolver(*solver_parameters)
                    solver.parameters.update(parameters)

                    x = dolfin.Function(fn_space)
                    if self.initial_guess is not None and var.type == 'ADJ_FORWARD':
                        x.vector()[:] = self.initial_guess.vector()

                    if b.data is None:
                        dolfin.info_red("Warning: got zero RHS for the solve associated with variable %s" % var)
                        return adjlinalg.Vector(x)

                    if var.type in ['ADJ_TLM', 'ADJ_ADJOINT']:
                        self.bcs = [utils.homogenize(bc) for bc in self.bcs if isinstance(bc, dolfin.cpp.DirichletBC)] + [bc for bc in self.bcs if not isinstance(bc, dolfin.cpp.DirichletBC)]

                    # This is really hideous. Sorry.
                    if isinstance(b.data, dolfin.Function):
                        rhs = b.data.vector().copy()
                        [bc.apply(rhs) for bc in self.bcs]

                        if assemble_system: # if we called assemble_system, rather than assemble
                            v = dolfin.TestFunction(fn_space)
                            (A, rhstmp) = dolfin.assemble_system(operators[0], dolfin.inner(b.data, v)*dolfin.dx, self.bcs)
                            if has_preconditioner:
                                (P, rhstmp) = dolfin.assemble_system(operators[1], dolfin.inner(b.data, v)*dolfin.dx, self.bcs)
                                solver.set_operators(A, P)
                            else:
                                solver.set_operator(A)
                        else: # we called assemble
                            A = dolfin.assemble(operators[0])
                            [bc.apply(A) for bc in self.bcs]

                            # Set nullspace
                            if nsp:
                                dolfin.as_backend_type(A).set_nullspace(nsp)
                                nsp.orthogonalize(b);

                            if has_preconditioner:
                                P = dolfin.assemble(operators[1])
                                [bc.apply(P) for bc in self.bcs]
                                solver.set_operators(A, P)
                            else:
                                solver.set_operator(A)
                    else:

                        if assemble_system: # if we called assemble_system, rather than assemble
                            (A, rhs) = dolfin.assemble_system(operators[0], b.data, self.bcs)
                            if has_preconditioner:
                                (P, rhstmp) = dolfin.assemble_system(operators[1], b.data, self.bcs)
                                solver.set_operators(A, P)
                            else:
                                solver.set_operator(A)
                        else: # we called assemble
                            A = dolfin.assemble(operators[0])
                            rhs = dolfin.assemble(b.data)
                            [bc.apply(A) for bc in self.bcs]
                            [bc.apply(rhs) for bc in self.bcs]

                            # Set nullspace
                            if nsp:
                                dolfin.as_backend_type(A).set_nullspace(nsp)
                                nsp.orthogonalize(rhs);

                            if has_preconditioner:
                                P = dolfin.assemble(operators[1])
                                [bc.apply(P) for bc in self.bcs]
                                solver.set_operators(A, P)
                            else:
                                solver.set_operator(A)

                    solver.solve(x.vector(), rhs)
                    return adjlinalg.Vector(x)

            nonzero_initial_guess = parameters['nonzero_initial_guess'] if 'nonzero_initial_guess' in parameters.keys() else False
            solving.annotate(A == b, u, bcs, matrix_class=LinearSolverMatrix, initial_guess=nonzero_initial_guess, replace_map=True)

        out = dolfin.LinearSolver.solve(self, *args, **kwargs)

        if to_annotate and dolfin.parameters["adjoint"]["record_all"]:
            adjglobals.adjointer.record_variable(adjglobals.adj_variables[u], libadjoint.MemoryStorage(adjlinalg.Vector(u)))

        return out