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 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 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 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 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 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 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 __call__(self, adjointer, i, dependencies, values, variable): form = adjresidual.get_residual(i) if form is not None: 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 * float(self.coeff) diff_form = ufl.algorithms.expand_derivatives( backend.derivative(form, get_constant(self.a), dparam)) return adjlinalg.Vector(diff_form) else: return None
def __call__(self, adjointer, i, dependencies, values, variable): diff_form = None assert self.dv is not None, "Need a perturbation direction to use in the TLM." 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 for (a, da) in zip(self.v, self.dv): out_form = da * backend.derivative(form, a, dparam) if diff_form is None: diff_form = out_form else: diff_form += out_form return adjlinalg.Vector(diff_form)
def _ad_function_space(self, mesh): element = self.ufl_element() fs_element = element.reconstruct(cell=mesh.ufl_cell()) return backend.FunctionSpace(mesh, fs_element)
def _ad_function_space(self, mesh): if mesh not in self._cached_fs: element = self.ufl_element() fs_element = element.reconstruct(cell=mesh.ufl_cell()) self._cached_fs[mesh] = backend.FunctionSpace(mesh, fs_element) return self._cached_fs[mesh]
def _ad_function_space(self): if self._ad_coordinate_space is None: self._ad_coordinate_space = backend.FunctionSpace(self, self.ufl_coordinate_element()) return self._ad_coordinate_space
def evaluate_hessian_component(self, inputs, hessian_inputs, adj_inputs, block_variable, idx, relevant_dependencies, prepared=None): c = block_variable.output if c == self.func and not self.linear: return None adj_sol2 = prepared["adj_sol2"] adj_sol2_bdy = prepared["adj_sol2_bdy"] F_form = prepared["form"] adj_sol = prepared["adj_sol"] fwd_block_variable = self.get_outputs()[0] tlm_output = fwd_block_variable.tlm_value c_rep = block_variable.saved_output # If m = DirichletBC then d^2F(u,m)/dm^2 = 0 and d^2F(u,m)/dudm = 0, # so we only have the term dF(u,m)/dm * adj_sol2 if isinstance(c, backend.DirichletBC): tmp_bc = compat.create_bc(c, value=extract_subfunction( adj_sol2_bdy, c.function_space())) return [tmp_bc] if isinstance(c_rep, backend.Constant): mesh = compat.extract_mesh_from_form(F_form) W = c._ad_function_space(mesh) elif isinstance(c, compat.ExpressionType): mesh = F_form.ufl_domain().ufl_cargo() W = c._ad_function_space(mesh) elif isinstance(c, compat.MeshType): X = backend.SpatialCoordinate(c) element = X.ufl_domain().ufl_coordinate_element() W = backend.FunctionSpace(c, element) else: W = c.function_space() dc = backend.TestFunction(W) form_adj = backend.action(F_form, adj_sol) form_adj2 = backend.action(F_form, adj_sol2) if isinstance(c, compat.MeshType): dFdm_adj = backend.derivative(form_adj, X, dc) dFdm_adj2 = backend.derivative(form_adj2, X, dc) else: dFdm_adj = backend.derivative(form_adj, c_rep, dc) dFdm_adj2 = backend.derivative(form_adj2, c_rep, dc) # TODO: Old comment claims this might break on split. Confirm if true or not. d2Fdudm = ufl.algorithms.expand_derivatives( backend.derivative(dFdm_adj, fwd_block_variable.saved_output, tlm_output)) hessian_output = 0 # We need to add terms from every other dependency # i.e. the terms d^2F/dm_1dm_2 for _, bv in relevant_dependencies: c2 = bv.output c2_rep = bv.saved_output if isinstance(c2, backend.DirichletBC): continue tlm_input = bv.tlm_value if tlm_input is None: continue if c2 == self.func and not self.linear: continue # TODO: If tlm_input is a Sum, this crashes in some instances? if isinstance(c2_rep, compat.MeshType): X = backend.SpatialCoordinate(c2_rep) d2Fdm2 = ufl.algorithms.expand_derivatives( backend.derivative(dFdm_adj, X, tlm_input)) else: d2Fdm2 = ufl.algorithms.expand_derivatives( backend.derivative(dFdm_adj, c2_rep, tlm_input)) if d2Fdm2.empty(): continue hessian_output -= compat.assemble_adjoint_value(d2Fdm2) if not d2Fdudm.empty(): # FIXME: This can be empty in the multimesh case, ask sebastian hessian_output -= compat.assemble_adjoint_value(d2Fdudm) hessian_output -= compat.assemble_adjoint_value(dFdm_adj2) if isinstance(c, compat.ExpressionType): return [(hessian_output, W)] else: return hessian_output