def _assemble_and_solve_adj_eq(self, dFdu_adj_form, dJdu, compute_bdy): dJdu_copy = dJdu.copy() bcs = self._homogenize_bcs() solver = self.block_helper.adjoint_solver if solver is None: if self.assemble_system: rhs_bcs_form = backend.inner( backend.Function(self.function_space), dFdu_adj_form.arguments()[0]) * backend.dx A, _ = backend.assemble_system(dFdu_adj_form, rhs_bcs_form, bcs) else: A = compat.assemble_adjoint_value(dFdu_adj_form) [bc.apply(A) for bc in bcs] solver = backend.LUSolver(A, self.method) self.block_helper.adjoint_solver = solver solver.parameters.update(self.lu_solver_parameters) [bc.apply(dJdu) for bc in bcs] adj_sol = backend.Function(self.function_space) solver.solve(adj_sol.vector(), dJdu) adj_sol_bdy = None if compute_bdy: adj_sol_bdy = compat.function_from_vector( self.function_space, dJdu_copy - compat.assemble_adjoint_value( backend.action(dFdu_adj_form, adj_sol))) return adj_sol, adj_sol_bdy
def functional_partial_second_derivative(self, adjointer, J, timestep, m_dot): form = J.get_form(adjointer, timestep) if form is None: return None for coeff in ufl.algorithms.extract_coefficients(form): try: mesh = coeff.function_space().mesh() fn_space = backend.FunctionSpace(mesh, "R", 0) break except: pass dparam = backend.Function(fn_space) dparam.vector()[:] = 1.0 * float(self.coeff) d = backend.derivative(form, get_constant(self.a), dparam) d = ufl.algorithms.expand_derivatives(d) d2param = backend.Function(fn_space) d2param.vector()[:] = 1.0 * float(self.coeff) * m_dot d = backend.derivative(d, get_constant(self.a), d2param) d = ufl.algorithms.expand_derivatives(d) if len(d.integrals()) != 0: return backend.assemble(d) else: return None
def equation_partial_second_derivative(self, adjointer, adjoint, i, variable, m_dot): form = adjresidual.get_residual(i) if form is not None: form = -form mesh = ufl.algorithms.extract_arguments( form)[0].function_space().mesh() fn_space = backend.FunctionSpace(mesh, "R", 0) dparam = backend.Function(fn_space) dparam.vector()[:] = 1.0 * float(self.coeff) d2param = backend.Function(fn_space) d2param.vector()[:] = 1.0 * float(self.coeff) * m_dot diff_form = ufl.algorithms.expand_derivatives( backend.derivative(form, get_constant(self.a), dparam)) if diff_form is None: return None diff_form = ufl.algorithms.expand_derivatives( backend.derivative(diff_form, get_constant(self.a), d2param)) if diff_form is None: return None # Let's see if the form actually depends on the parameter m if len(diff_form.integrals()) != 0: dFdm = backend.assemble(diff_form) # actually - dF/dm assert isinstance(dFdm, backend.GenericVector) out = dFdm.inner(adjoint.vector()) return out else: return None # dF/dm is zero, return None
def petsc_vec_as_function(fs, petsc_vec): if backend.__name__ == "dolfin": return backend.Function(fs, backend.PETScVector(petsc_vec)) else: f = backend.Function(fs) with f.dat.vec as v: petsc_vec.copy(v) return f
def _assemble_and_solve_adj_eq(self, dFdu_adj_form, dJdu, compute_bdy): dJdu_copy = dJdu.copy() bcs = self._homogenize_bcs() solver = self.block_helper.adjoint_solver if solver is None: solver = backend.PETScKrylovSolver(self.method, self.preconditioner) solver.ksp().setOptionsPrefix(self.ksp_options_prefix) solver.set_from_options() if self.assemble_system: rhs_bcs_form = backend.inner(backend.Function(self.function_space), dFdu_adj_form.arguments()[0]) * backend.dx A, _ = backend.assemble_system(dFdu_adj_form, rhs_bcs_form, bcs) if self._ad_nullspace is not None: as_backend_type(A).set_nullspace(self._ad_nullspace) if self.pc_operator is not None: P = self._replace_form(self.pc_operator) P, _ = backend.assemble_system(P, rhs_bcs_form, bcs) solver.set_operators(A, P) else: solver.set_operator(A) else: A = compat.assemble_adjoint_value(dFdu_adj_form) [bc.apply(A) for bc in bcs] if self._ad_nullspace is not None: as_backend_type(A).set_nullspace(self._ad_nullspace) if self.pc_operator is not None: P = self._replace_form(self.pc_operator) P = compat.assemble_adjoint_value(P) [bc.apply(P) for bc in bcs] solver.set_operators(A, P) else: solver.set_operator(A) self.block_helper.adjoint_solver = solver solver.parameters.update(self.krylov_solver_parameters) [bc.apply(dJdu) for bc in bcs] if self._ad_nullspace is not None: if self._ad_nullspace._ad_orthogonalized: self._ad_nullspace.orthogonalize(dJdu) adj_sol = backend.Function(self.function_space) solver.solve(adj_sol.vector(), dJdu) adj_sol_bdy = None if compute_bdy: adj_sol_bdy = compat.function_from_vector(self.function_space, dJdu_copy - compat.assemble_adjoint_value( backend.action(dFdu_adj_form, adj_sol))) return adj_sol, adj_sol_bdy
def prepare_evaluate_adj(self, inputs, adj_inputs, relevant_dependencies): adj_assigner = self.assigner.adj_assigner inp_functions = [] for i in range(len(adj_inputs)): f_in = backend.Function(self.assigner.output_spaces[i]) if adj_inputs[i] is not None: f_in.vector()[:] = adj_inputs[i] inp_functions.append(f_in) out_functions = [] for j in range(len(self.assigner.input_spaces)): f_out = backend.Function(self.assigner.input_spaces[j]) out_functions.append(f_out) adj_assigner.assign(self.assigner.input_spaces.delist(out_functions), self.assigner.output_spaces.delist(inp_functions)) return out_functions
def derivative_action(self, dependencies, values, variable, contraction_vector, hermitian): idx = dependencies.index(variable) # If you want to apply boundary conditions symmetrically in the adjoint # -- and you often do -- # then we need to have a UFL representation of all the terms in the adjoint equation. # However! # Since UFL cannot represent the identity map, # we need to find an f such that when # assemble(inner(f, v)*dx) # we get the contraction_vector.data back. # This involves inverting a mass matrix. if backend.parameters["adjoint"][ "symmetric_bcs"] and backend.__version__ <= '1.2.0': backend.info_red( "Warning: symmetric BC application requested but unavailable in dolfin <= 1.2.0." ) if backend.parameters["adjoint"][ "symmetric_bcs"] and backend.__version__ > '1.2.0': V = contraction_vector.data.function_space() v = backend.TestFunction(V) if str(V) not in adjglobals.fsp_lu: u = backend.TrialFunction(V) A = backend.assemble(backend.inner(u, v) * backend.dx) solver = "mumps" if "mumps" in backend.lu_solver_methods( ).keys() else "default" lusolver = backend.LUSolver(A, solver) lusolver.parameters["symmetric"] = True lusolver.parameters["reuse_factorization"] = True adjglobals.fsp_lu[str(V)] = lusolver else: lusolver = adjglobals.fsp_lu[str(V)] riesz = backend.Function(V) lusolver.solve( riesz.vector(), self.weights[idx] * contraction_vector.data.vector()) out = (backend.inner(riesz, v) * backend.dx) else: out = backend.Function(self.fn_space) out.assign(self.weights[idx] * contraction_vector.data) return adjlinalg.Vector(out)
def boundary_to_mesh(f, mesh): """ Take a CG1 function f defined on a surface mesh and return a volume vector with same values on boundary but zero in volume """ b_mesh = f.function_space().mesh() SpaceV = backend.FunctionSpace(mesh, "CG", 1) SpaceB = backend.FunctionSpace(b_mesh, "CG", 1) F = backend.Function(SpaceV) GValues = numpy.zeros(F.vector().size()) map = b_mesh.entity_map(0) # Vertex map from boundary mesh to parent mesh d2v = backend.dof_to_vertex_map(SpaceB) v2d = backend.vertex_to_dof_map(SpaceV) dof = SpaceV.dofmap() imin, imax = dof.ownership_range() for i in range(f.vector().local_size()): GVertID = backend.Vertex( b_mesh, d2v[i]).index() # Local Vertex ID for given dof on boundary mesh PVertID = map[GVertID] # Local Vertex ID of parent mesh PDof = v2d[PVertID] # Dof on parent mesh value = f.vector()[i] # Value on local processor GValues[dof.local_to_global_index(PDof)] = value GValues = SyncSum(GValues) F.vector().set_local(GValues[imin:imax]) F.vector().apply("") return F
def _assemble_and_solve_adj_eq(self, dFdu_adj_form, dJdu, compute_bdy): dJdu_copy = dJdu.copy() bcs = self._homogenize_bcs() if self.assemble_system: rhs_bcs_form = backend.inner( backend.Function(self.function_space), dFdu_adj_form.arguments()[0]) * backend.dx A, _ = backend.assemble_system(dFdu_adj_form, rhs_bcs_form, bcs) else: A = backend.assemble(dFdu_adj_form) [bc.apply(A) for bc in bcs] [bc.apply(dJdu) for bc in bcs] adj_sol = compat.create_function(self.function_space) compat.linalg_solve(A, adj_sol.vector(), dJdu, *self.adj_args, **self.adj_kwargs) adj_sol_bdy = None if compute_bdy: adj_sol_bdy = compat.function_from_vector( self.function_space, dJdu_copy - compat.assemble_adjoint_value( backend.action(dFdu_adj_form, adj_sol))) return adj_sol, adj_sol_bdy
def visit(self, context): flags = 0 localv = [] header = [] variables, optionals, vararg = self.bindings argc = len(variables) topc = argc + len(optionals) for variable in variables: localv.append(variable.value) for variable, expr in (optionals if optionals else []): localv.append(variable.value) cond = Cond([[ self.loc, Code(self.loc, "isnull", Getvar(self.loc, variable.value)), [Setvar(self.loc, "local", variable.value, expr)] ]], None) header.append(cond) if vararg is not None: localv.append(vararg.value) flags |= 1 handle = backend.Function(len(context.closures)) context.closures.append([ header + self.body, Scope(context.scope, flags, argc, topc, localv) ]) variables, optionals, vararg = self.bindings return context.block.op(self.loc, 'func', [handle])
def mesh_to_boundary(v, b_mesh): """ Returns a the boundary representation of the CG-1 function v """ # Extract the underlying volume and boundary meshes mesh = v.function_space().mesh() # We use a Dof->Vertex mapping to create a global # array with all DOF values ordered by mesh vertices DofToVert = backend.dof_to_vertex_map(v.function_space()) VGlobal = numpy.zeros(v.vector().size()) vec = v.vector().get_local() for i in range(len(vec)): Vert = backend.MeshEntity(mesh, 0, DofToVert[i]) globalIndex = Vert.global_index() VGlobal[globalIndex] = vec[i] VGlobal = SyncSum(VGlobal) # Use the inverse mapping to se the DOF values of a boundary # function surface_space = backend.FunctionSpace(b_mesh, "CG", 1) surface_function = backend.Function(surface_space) mapa = b_mesh.entity_map(0) DofToVert = backend.dof_to_vertex_map(backend.FunctionSpace(b_mesh, "CG", 1)) LocValues = surface_function.vector().get_local() for i in range(len(LocValues)): VolVert = backend.MeshEntity(mesh, 0, mapa[int(DofToVert[i])]) GlobalIndex = VolVert.global_index() LocValues[i] = VGlobal[GlobalIndex] surface_function.vector().set_local(LocValues) surface_function.vector().apply('') return surface_function
def _forward_solve(self, lhs, rhs, func, bcs, **kwargs): solver = self.block_helper.forward_solver if solver is None: solver = backend.KrylovSolver(self.method, self.preconditioner) if self.assemble_system: A, _ = backend.assemble_system(lhs, rhs, bcs) if self.pc_operator is not None: P = self._replace_form(self.pc_operator) P, _ = backend.assemble_system(P, rhs, bcs) solver.set_operators(A, P) else: solver.set_operator(A) else: A = compat.assemble_adjoint_value(lhs) [bc.apply(A) for bc in bcs] if self.pc_operator is not None: P = self._replace_form(self.pc_operator) P = compat.assemble_adjoint_value(P) [bc.apply(P) for bc in bcs] solver.set_operators(A, P) else: solver.set_operator(A) self.block_helper.forward_solver = solver if self.assemble_system: system_assembler = backend.SystemAssembler(lhs, rhs, bcs) b = backend.Function(self.function_space).vector() system_assembler.assemble(b) else: b = compat.assemble_adjoint_value(rhs) [bc.apply(b) for bc in bcs] solver.parameters.update(self.krylov_solver_parameters) solver.solve(func.vector(), b) return func
def equation_partial_derivative(self, adjointer, adjoint, i, variable): form = adjresidual.get_residual(i) if form is None: return None else: form = -form fn_space = ufl.algorithms.extract_arguments(form)[0].function_space() dparam = backend.Function( backend.FunctionSpace(fn_space.mesh(), "R", 0)) dparam.vector()[:] = 1.0 dJdv = numpy.zeros(len(self.v)) for (i, a) in enumerate(self.v): diff_form = ufl.algorithms.expand_derivatives( backend.derivative(form, a, dparam)) dFdm = backend.assemble(diff_form) # actually - dF/dm assert isinstance(dFdm, backend.GenericVector) out = dFdm.inner(adjoint.vector()) dJdv[i] = out return dJdv
def __call__(self, dependencies, values): assert isinstance(self.form, ufl.form.Form) if hasattr(self, "ic_copy"): ic = self.ic_copy else: # by default, initialise with a blank function in the solution FunctionSpace V = self.u.function_space() ic = backend.Function(V) replace_map = {} for i in range(len(self.deps)): if self.deps[i] in dependencies: j = dependencies.index(self.deps[i]) if self.deps[i] == self.ic_var: ic = values[ j].data # ahah, we have found an initial condition! else: replace_map[self.coeffs[i]] = values[j].data current_F = backend.replace(self.F, replace_map) current_J = backend.replace(self.J, replace_map) u = ic.copy(deepcopy=True) current_F = backend.replace(current_F, {self.u: u}) current_J = backend.replace(current_J, {self.u: u}) vec = adjlinalg.Vector(None) vec.nonlinear_form = current_F vec.nonlinear_u = u vec.nonlinear_bcs = self.bcs vec.nonlinear_J = current_J return vec
def evaluate_adj_component(self, inputs, adj_inputs, block_variable, idx, prepared=None): p = backend.Point(numpy.array(self.coords)) V = inputs[0].function_space() dofs = V.dofmap() mesh = V.mesh() element = V.element() visited = [] adj_vec = backend.Function(V).vector() for cell_idx in range(len(mesh.cells())): cell = backend.Cell(mesh, cell_idx) if cell.contains(p): for ref_dof, dof in enumerate(dofs.cell_dofs(cell_idx)): if dof in visited: continue visited.append(dof) basis = element.evaluate_basis(ref_dof, p.array(), cell.get_coordinate_dofs(), cell.orientation()) adj_vec[dof] = basis.dot(adj_inputs[idx]) return adj_vec
def equation_partial_derivative(self, adjointer, adjoint, i, variable): form = adjresidual.get_residual(i) if form is not None: form = -form mesh = ufl.algorithms.extract_arguments( form)[0].function_space().mesh() fn_space = backend.FunctionSpace(mesh, "R", 0) dparam = backend.Function(fn_space) dparam.vector()[:] = 1.0 * float(self.coeff) diff_form = ufl.algorithms.expand_derivatives( backend.derivative(form, get_constant(self.a), dparam)) # Add the derivatives of Expressions wrt to the Constant diff_form = self.expression_derivative(form, diff_form) # Let's see if the form actually depends on the parameter m if len(diff_form.integrals()) != 0: dFdm = backend.assemble(diff_form) # actually - dF/dm out = adjoint.vector().inner(dFdm) else: out = None # dF/dm is zero, return None return out
def __init__(self, form, F, u, bcs, mass, solver_parameters, J): '''form is M.u - F(u). F is the nonlinear equation, F(u) := 0.''' RHS.__init__(self, form) self.F = F self.u = u self.bcs = bcs self.mass = mass self.solver_parameters = solver_parameters self.J = J or backend.derivative(F, u) # We want to mark that the RHS term /also/ depends on # the previous value of u, as that's what we need to initialise # the nonlinear solver. var = adjglobals.adj_variables[self.u] self.ic_var = None if backend.parameters["adjoint"]["fussy_replay"]: can_depend = True try: prev_var = find_previous_variable(var) except: can_depend = False if can_depend: self.ic_var = prev_var self.deps += [self.ic_var] self.coeffs += [u] else: self.ic_copy = backend.Function(u) self.ic_var = None
def functional_partial_derivative(self, adjointer, J, timestep): form = J.get_form(adjointer, timestep) if form is None: return None # OK. Now that we have the form for the functional at this timestep, let's differentiate it with respect to # my dear Constant, and be done. for coeff in ufl.algorithms.extract_coefficients(form): try: mesh = coeff.function_space().mesh() fn_space = backend.FunctionSpace(mesh, "R", 0) break except: pass dparam = backend.Function(fn_space) dparam.vector()[:] = 1.0 * float(self.coeff) d = backend.derivative(form, get_constant(self.a), dparam) d = ufl.algorithms.expand_derivatives(d) # Add the derivatives of Expressions wrt to the Constant d = self.expression_derivative(form, d) if len(d.integrals()) != 0: return backend.assemble(d) else: return None
def get_residual(i): from .adjrhs import adj_get_forward_equation (fwd_var, lhs, rhs) = adj_get_forward_equation(i) if isinstance(lhs, adjlinalg.IdentityMatrix): return None fn_space = ufl.algorithms.extract_arguments(lhs)[0].function_space() x = backend.Function(fn_space) if rhs == 0: form = lhs x = fwd_var.nonlinear_u else: form = backend.action(lhs, x) - rhs try: y = adjglobals.adjointer.get_variable_value(fwd_var).data except libadjoint.exceptions.LibadjointErrorNeedValue: info_red( "Warning: recomputing forward solution; please report this script on launchpad" ) y = adjglobals.adjointer.get_forward_solution(i)[1].data form = backend.replace(form, {x: y}) return form
def project_firedrake(v, V, **kwargs): annotate = kwargs.pop("annotate", None) 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 if isinstance(V, backend.functionspaceimpl.WithGeometry): result = utils.function_to_da_function(backend.Function(V, name=None)) elif isinstance(V, backend.function.Function): result = utils.function_to_da_function(V) else: raise ValueError("Can't project into a '%r'" % V) name = kwargs.pop("name", None) if name is not None: result.adj_name = name result.rename(name, "a Function from dolfin-adjoint") with misc.annotations(to_annotate): result = backend.project(v, result, **kwargs) return result
def prepare_recompute_component(self, inputs, relevant_outputs): out_functions = [] for output in self.get_outputs(): out_functions.append(backend.Function(output.output.function_space())) backend.FunctionAssigner.assign(self.assigner, self.assigner.output_spaces.delist(out_functions), self.assigner.input_spaces.delist(inputs)) return out_functions
def __copy_data(self, m): """Returns a deep copy of the given Function/Constant.""" if hasattr(m, "vector"): return backend.Function(m.function_space()) elif hasattr(m, "value_size"): return backend.Constant(m(())) else: raise TypeError('Unknown control type %s.' % str(type(m)))
def make_mdot(vec): if isinstance(self.m, FunctionControl): mdot = self.m.set_perturbation( backend.Function(self.m.data().function_space(), vec)) elif isinstance(self.m, ConstantControl): mdot = self.m.set_perturbation(backend.Constant(vec)) return mdot
def evaluate_tlm(self): tlm_input = self.get_dependencies()[0].tlm_value if tlm_input is None: return output = self.get_outputs()[0] fs = output.output.function_space() f = backend.Function(fs) output.add_tlm_output(backend.assign(f.sub(self.idx), tlm_input))
def evaluate_hessian(self, markings=False): hessian_input = self.get_outputs()[0].hessian_value if hessian_input is None: return W = self.get_outputs()[0].output.function_space() mesh = self.get_dependencies()[1].output hessian_value = backend.Function(W, hessian_input) hessian_output = vector_boundary_to_mesh(hessian_value, mesh) self.get_dependencies()[0].add_hessian_output(hessian_output.vector())
def evaluate_adj(self, markings=False): adj_value = self.get_outputs()[0].adj_value if adj_value is None: return f = backend.Function(backend.VectorFunctionSpace(self.get_outputs()[0].saved_output, "CG", 1)) f.vector()[:] = adj_value adj_value = vector_boundary_to_mesh(f, self.get_dependencies()[0].saved_output) self.get_dependencies()[0].add_adj_output(adj_value.vector())
def evaluate_adj(self, markings=False): adj_input = self.get_outputs()[0].adj_value if adj_input is None: return W = self.get_outputs()[0].output.function_space() b_mesh = self.get_dependencies()[1].output adj_value = backend.Function(W, adj_input) adj_output = vector_boundary_to_mesh(adj_value, b_mesh) self.get_dependencies()[0].add_adj_output(adj_output.vector())
def evaluate_hessian(self, markings=False): hessian_input = self.get_outputs()[0].hessian_value if hessian_input is None: return f = backend.Function(backend.VectorFunctionSpace(self.get_outputs()[0].saved_output, "CG", 1)) f.vector()[:] = hessian_input hessian_value = vector_boundary_to_mesh(f, self.get_dependencies()[0].saved_output) self.get_dependencies()[0].add_hessian_output(hessian_value.vector())
def vector_mesh_to_boundary(func, b_mesh): v_split = func.split(deepcopy=True) v_b = [] for v in v_split: v_b.append(mesh_to_boundary(v, b_mesh)) Vb = backend.VectorFunctionSpace(b_mesh, "CG", 1) vb_out = backend.Function(Vb) scalar_to_vec = backend.FunctionAssigner(Vb, [v.function_space() for v in v_b]) scalar_to_vec.assign(vb_out, v_b) return vb_out
def project_test(func): if isinstance(func, backend.Function): V = func.function_space() u = backend.TrialFunction(V) v = backend.TestFunction(V) M = backend.assemble(backend.inner(u, v) * backend.dx) proj = backend.Function(V) backend.solve(M, proj.vector(), func.vector()) return proj else: return func