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 project(*args, **kwargs): """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 pyadjoint. 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).""" ad_block_tag = kwargs.pop("ad_block_tag", None) annotate = annotate_tape(kwargs) with stop_annotating(): output = backend.project(*args, **kwargs) output = create_overloaded_object(output) if annotate: bcs = kwargs.pop("bcs", []) sb_kwargs = ProjectBlock.pop_kwargs(kwargs) sb_kwargs.update(kwargs) block = ProjectBlock(args[0], args[1], output, bcs, ad_block_tag=ad_block_tag, **sb_kwargs) tape = get_working_tape() tape.add_block(block) block.add_output(output.block_variable) return output
def project_firedrake(*args, **kwargs): try: annotate = kwargs["annotate"] kwargs.pop("annotate") except KeyError: annotate = None to_annotate = utils.to_annotate(annotate) if isinstance(args[0], backend.Expression) and (annotate is not True): to_annotate = False if isinstance(args[0], backend.Constant) and (annotate is not True): to_annotate = False if to_annotate: result = backend.project(*args, **kwargs) else: flag = misc.pause_annotation() result = backend.project(*args, **kwargs) misc.continue_annotation(flag) return result
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 evaluate_tlm_component(self, inputs, tlm_inputs, block_variable, idx, prepared=None): bc = block_variable.saved_output for bv in self.get_dependencies(): tlm_input = bv.tlm_value if tlm_input is None: continue if self.function_space != self.parent_space and not isinstance( tlm_input, ufl.Coefficient): tlm_input = backend.project(tlm_input, self.collapsed_space) # TODO: This is gonna crash for dirichletbcs with multiple dependencies (can't add two bcs) # However, if there is multiple dependencies, we need to AD the expression (i.e if value=f*g then # dvalue = tlm_f * g + f * tlm_g). Right now we can only handle value=f => dvalue = tlm_f. m = compat.create_bc(bc, value=tlm_input) return m
def evaluate_hessian_component(self, inputs, hessian_inputs, adj_inputs, block_variable, idx, relevant_dependencies, prepared=None): hessian_inputs = hessian_inputs[0] adj_inputs = adj_inputs[0] c1 = block_variable.output if c1 not in self.expression.user_defined_derivatives: return None first_deriv = self.expression.user_defined_derivatives[c1] for key in self.expression._ad_attributes_dict: if key not in self.expression.ad_ignored_attributes: setattr(first_deriv, key, self.expression._ad_attributes_dict[key]) hessian_output = None for _, bo2 in relevant_dependencies: c2 = bo2.output tlm_input = bo2.tlm_value if tlm_input is None: continue if c2 not in first_deriv.user_defined_derivatives: continue second_deriv = first_deriv.user_defined_derivatives[c2] for key in self.expression._ad_attributes_dict: if key not in self.expression.ad_ignored_attributes: setattr(second_deriv, key, self.expression._ad_attributes_dict[key]) for adj_pair in adj_inputs: adj_input = adj_pair[0] V = adj_pair[1] if hessian_output is None: hessian_output = 0.0 # TODO: Seems we can only project and not interpolate ufl.algebra.Product in dolfin. # Consider the difference and which actually makes sense here. interp = backend.project(tlm_input * second_deriv, V) if isinstance(c1, (backend.Constant, AdjFloat)): hessian_output += adj_input.inner(interp.vector()) else: vec = adj_input * interp.vector() hessian_func = backend.Function(V, vec) num_sub_spaces = V.num_sub_spaces() if num_sub_spaces > 1: for i in range(num_sub_spaces): hessian_output += backend.interpolate( hessian_func.sub(i), c1.function_space()).vector() else: hessian_output += backend.interpolate( hessian_func, c1.function_space()).vector() for hessian_pair in hessian_inputs: if hessian_output is None: hessian_output = 0.0 hessian_input = hessian_pair[0] V = hessian_pair[1] interp = backend.interpolate(first_deriv, V) if isinstance(c1, (backend.Constant, AdjFloat)): hessian_output += hessian_input.inner(interp.vector()) else: vec = hessian_input * interp.vector() hessian_func = backend.Function(V, vec) num_sub_spaces = V.num_sub_spaces() if num_sub_spaces > 1: for i in range(num_sub_spaces): hessian_output += backend.interpolate( hessian_func.sub(i), c1.function_space()).vector() else: hessian_output += backend.interpolate( hessian_func, c1.function_space()).vector() return hessian_output
def project_dolfin(v, V=None, bcs=None, mesh=None, solver_type="lu", 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 project(args): backend.project(args.phid)
def axpy(self, alpha, x): if hasattr(x, 'nonlinear_form'): self.nonlinear_form = x.nonlinear_form self.nonlinear_u = x.nonlinear_u self.nonlinear_bcs = x.nonlinear_bcs self.nonlinear_J = x.nonlinear_J if x.zero: return if (self.data is None): # self is an empty form. if isinstance(x.data, backend.Function): self.data = x.data.copy(deepcopy=True) self.data.vector()._scale(alpha) if isinstance(x.data, backend.MultiMeshFunction): self.data = backend.MultiMeshFunction(x.data.function_space(), x.data.vector()) self.data.vector()._scale(alpha) else: self.data=alpha*x.data elif x.data is None: pass elif isinstance(self.data, backend.Coefficient): if isinstance(x.data, backend.Coefficient): try: self.data.vector().axpy(alpha, x.data.vector()) except: # Handle subfunctions # Fixme: use FunctionAssigner instead of a projection #assigner = backend.FunctionAssigner(self.data.function_space, # x.data.function_space() x = backend.project(x.data, self.data.function_space()) self.data.vector().axpy(alpha, x.vector()) else: # This occurs when adding a RHS derivative to an adjoint equation # corresponding to the initial conditions. if ((len(x.data.coefficients())>0) and hasattr(x.data.coefficients()[0], '_V')): self.data.vector().axpy(alpha, backend.assemble_multimesh(x.data)) else: self.data.vector().axpy(alpha, backend.assemble(x.data)) self.data.form = alpha * x.data elif isinstance(x.data, ufl.form.Form) and isinstance(self.data, ufl.form.Form): # Let's do a bit of argument shuffling, shall we? xargs = ufl.algorithms.extract_arguments(x.data) sargs = ufl.algorithms.extract_arguments(self.data) if xargs != sargs: # OK, let's check that all of the function spaces are happy and so on. for i in range(len(xargs)): assert xargs[i].element() == sargs[i].element() assert xargs[i].function_space() == sargs[i].function_space() # Now that we are happy, let's replace the xargs with the sargs ones. x_form = backend.replace(x.data, dict(zip(xargs, sargs))) else: x_form = x.data self.data+=alpha*x_form elif isinstance(self.data, ufl.form.Form) and isinstance(x.data, backend.Function): #print "axpy assembling FormFunc. self.data is a %s; x.data is a %s" % (self.data.__class__, x.data.__class__) x_vec = x.data.vector().copy() self_vec = backend.assemble(self.data) self_vec.axpy(alpha, x_vec) new_fn = backend.Function(x.data.function_space()) new_fn.vector()[:] = self_vec self.data = new_fn self.fn_space = self.data.function_space() elif isinstance(self.data, backend.MultiMeshFunction): raise NotImplementedError else: print "self.data.__class__: ", self.data.__class__ print "x.data.__class__: ", x.data.__class__ assert False self.zero = False
def derivative(self, adjointer, variable, dependencies, values): if self.verbose: for dep in dependencies: print(variable.timestep, "derive wrt ", dep.name) if self.regform is not None and variable.name == self.regform.coefficients( )[0].name(): # derivative wrt the contorl if self.verbose: " derivatives wrt the controls " raise RuntimeError("""The derivative of a regularisation term doesn't work properly and shouldn't be used""" ) return self.regfunc.derivative(adjointer, variable, dependencies, values) else: # transate finish_time: UGLY!! if "FINISH_TIME" in self.times: final_time = _time_levels(adjointer, adjointer.timestep_count - 1)[1] self.times[self.times.index("FINISH_TIME")] = final_time if self.verbose: print("derive ", variable.timestep, " num values ", len(values)) timesteps = self._derivative_timesteps(adjointer, variable) ff = [backend.Constant(0.0)] * len(self.coords) for i in range(len(self.coords)): if self.skip[i]: if self.verbose: print("skipped") else: if len(timesteps ) is 1: # only occurs at start and finish time tsoi = timesteps[-1] if tsoi is 0: toi = _time_levels(adjointer, tsoi)[0] ind = -1 else: toi = _time_levels(adjointer, tsoi)[-1] ind = 0 else: if len(values) is 1: # one value (easy) tsoi = timesteps[-1] toi = _time_levels(adjointer, tsoi)[0] ind = 0 elif len(values) is 2: # two values (hard) tsoi = timesteps[-1] toi = _time_levels(adjointer, tsoi)[0] if _time_levels(adjointer, tsoi)[1] in self.times: ind = 0 else: ind = 1 else: # three values (easy) tsoi = timesteps[1] toi = _time_levels(adjointer, tsoi)[0] ind = 1 coef = values[ind].data ref = self.refs[i][self.times.index(toi)] if self.index[i] is None: solu = coef(self.coords[i]) else: solu = coef(self.coords[i])[self.index[i]] ff[i] = backend.Constant(self.alpha * 2.0 * (solu - float(ref))) # Set up linear combinations to be projected form = ff[0] * self.basis[0] for i in range(1, len(self.coords)): form += ff[i] * self.basis[i] v = backend.project(form, self.func.function_space()) return adjlinalg.Vector(v)