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
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
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