def add(self, form): '''self[i][j] += form with i, j extracted''' if len(form.integrals()) > 1: return map(self.add, [ufl.Form((i, )) for i in form.integrals()]) # Linear if self.arity == 1: assert trial_function(form) == () test_f, = test_function(form) # Only one # Look for the index V = test_f.function_space() i, _ = next( dropwhile(lambda (i, Vi), V=V: Vi != V, enumerate(self.V0))) self[i] += form return self # Bilinear test_f, = test_function(form) # Only one trial_f, = trial_function(form) # Only one V = trial_f.function_space() col, _ = next( dropwhile(lambda (i, Vi), V=V: Vi != V, enumerate(self.V0))) V = test_f.function_space() row, _ = next( dropwhile(lambda (i, Vi), V=V: Vi != V, enumerate(self.V0))) self[row][col] += form return self
def coarsen_form(form, coefficient_mapping=None): """Return a coarse mesh version of a form :arg form: The :class:`~ufl.classes.Form` to coarsen. :kwarg mapping: an optional map from coefficients to their coarsened equivalents. This maps over the form and replaces coefficients and arguments with their coarse mesh equivalents.""" if form is None: return None mapper = CoarsenIntegrand(coefficient_mapping) integrals = [] for it in form.integrals(): integrand = map_expr_dag(mapper, it.integrand()) mesh = it.ufl_domain() hierarchy, level = utils.get_level(mesh) new_mesh = hierarchy[level-1] if isinstance(integrand, ufl.classes.Zero): continue if it.subdomain_data() is not None: raise CoarseningError("Don't know how to coarsen subdomain data") new_itg = it.reconstruct(integrand=integrand, domain=new_mesh) integrals.append(new_itg) return ufl.Form(integrals)
def form_adjoint(expr): '''Like UFL adjoint but keeping track of ii attributes''' if is_number(expr): return expr if isinstance(expr, ufl.Form): return ufl.Form(map(form_adjoint, expr.integrals())) if isinstance(expr, ufl.Integral): return expr.reconstruct(integrand=form_adjoint(expr.integrand())) # For adjoint we need one trial and one test function. The idea is # then exchange the two while keeping track of trace attributes arguments = set( filter(lambda f: isinstance(f, Argument), traverse_terminals(expr))) ii_attrs = ('trace_', 'restriction_', 'average_') adj_type = {0: TrialFunction, 1: TestFunction} for arg in arguments: adj_arg = adj_type[arg.number()](arg.function_space()) # Record trace attributes attrs = [] for attr in ii_attrs: if hasattr(arg, attr): setattr(adj_arg, attr, getattr(arg, attr)) attrs.append(attr) # Substitute expr = ii_replace(expr, arg, adj_arg, attrs) return expr
def adjoint_derivative_action(self, nl_deps, dep_index, adj_x): # Derived from EquationSolver.derivative_action (see dolfin-adjoint # reference below). Code first added 2017-12-07. # Re-written 2018-01-28 # Updated to adjoint only form 2018-01-29 eq_deps = self.dependencies() if dep_index < 0 or dep_index >= len(eq_deps): raise EquationException("dep_index out of bounds") elif dep_index == 0: return adj_x dep = eq_deps[dep_index] dF = derivative(self._rhs, dep) dF = ufl.algorithms.expand_derivatives(dF) dF = eliminate_zeros(dF) if dF.empty(): return None dF = self._nonlinear_replace(dF, nl_deps) if self._rank == 0: dF = ufl.Form([integral.reconstruct(integrand=ufl.conj(integral.integrand())) # noqa: E501 for integral in dF.integrals()]) # dF = adjoint(dF) dF = assemble( dF, form_compiler_parameters=self._form_compiler_parameters) return (-function_scalar_value(adj_x), dF) else: assert self._rank == 1 dF = assemble( ufl.action(adjoint(dF), coefficient=adj_x), form_compiler_parameters=self._form_compiler_parameters) return (-1.0, dF)
def test_expression_derivative(V1, V2, squaremesh_5): u1, du1, v1 = dolfinx.fem.Function(V1), ufl.TrialFunction( V1), ufl.TestFunction(V1) u2, du2, v2 = dolfinx.fem.Function(V2), ufl.TrialFunction( V2), ufl.TestFunction(V2) dx = ufl.dx(squaremesh_5) # Test derivative of expressions assert dolfiny.expression.derivative(1 * u1, u1, du1) == 1 * du1 assert dolfiny.expression.derivative(2 * u1 + u2, u1, du1) == 2 * du1 assert dolfiny.expression.derivative(u1**2 + u2, u1, du1) == du1 * 2 * u1 assert dolfiny.expression.derivative(u1 + u2, [u1, u2], [v1, v2]) == v1 + v2 assert dolfiny.expression.derivative([u1, u2], u1, v1) == [v1, 0] assert dolfiny.expression.derivative([u1, u2], [u1, u2], [v1, v2]) == [v1, v2] # Test derivative of forms assert dolfiny.expression.derivative(1 * u1 * dx, u1, du1) == 1 * du1 * dx assert dolfiny.expression.derivative(2 * u1 * dx + u2 * dx, u1, du1) == 2 * du1 * dx assert dolfiny.expression.derivative(u1**2 * dx + u2 * dx, u1, du2) == du2 * 2 * u1 * dx assert dolfiny.expression.derivative(u1 * dx + u2 * dx, [u1, u2], [v1, v2]) == v1 * dx + v2 * dx assert dolfiny.expression.derivative([u1 * dx, u2 * dx], u1, v1) == [v1 * dx, ufl.Form([])] assert dolfiny.expression.derivative([u1 * dx, u2 * dx], [u1, u2], [v1, v2]) == [v1 * dx, v2 * dx] # Test derivative of expressions at u0 u10, u20 = dolfinx.fem.Function(V1), dolfinx.fem.Function(V2) assert dolfiny.expression.derivative(1 * u1, u1, du1, u0=u10) == 1 * du1 assert dolfiny.expression.derivative(2 * u1 + u2, u1, du1, u0=u10) == 2 * du1 assert dolfiny.expression.derivative(u1**2 + u2, u1, du1, u0=u10) == du1 * 2 * u10 assert dolfiny.expression.derivative( u1**2 + u2**2, [u1, u2], [v1, v2], [u10, u20]) == v1 * 2 * u10 + v2 * 2 * u20 assert dolfiny.expression.derivative([u1**2, u2], u1, v1, u0=u10) == [v1 * 2 * u10, 0] assert dolfiny.expression.derivative( [u1**2 + u2, u2], [u1, u2], [v1, v2], [u10, u20]) == [v1 * 2 * u10 + v2, v2]
def adaptform_OLD(form, mesh, adapt_coefficients=False): oldmesh = form.domain().data() if (oldmesh.id() == mesh.id()): return form # define new domain newdomain = mesh.ufl_domain() # adapt meshfunctions newsubdata = form.subdomain_data().values()[0] # assuming single domain for k, meshfunction in newsubdata.items(): newsubdata[k] = adaptmeshfunction(meshfunction, mesh) # replace domain, meshfunctions in integrals integrals = [] for itg in form.integrals(): newitg = itg.reconstruct( domain=newdomain, subdomain_data=newsubdata[itg.integral_type()]) integrals.append(newitg) newform = ufl.Form(integrals) # replace arguments and coefficients in form mapping = {} # adapt arguments for argument in form.arguments(): newargument = adaptargument(argument, mesh) mapping[argument] = newargument if (adapt_coefficients): # adapt coefficients for coeff in form.coefficients(): adaptcoefficient(coeff, mesh) #MOD #newcoeff = adaptcoefficient(coeff,mesh) #mapping[coeff] = newcoeff # TODO: is there more? is there a better way to ensure everything is adapted? # adapt FacetNormal mapping[FacetNormal(oldmesh)] = FacetNormal(mesh) # adapt CellSize -- have to use ufl.Circumradius or replace() complains mapping[ufl.Circumradius(oldmesh)] = ufl.Circumradius(mesh) newform = replace(newform, mapping) return newform
def ii_derivative(f, x): '''DOLFIN's derivative df/dx extended to handle trace stuff''' test_f = is_okay_functional(f) # FIXME: for now don't allow diffing wrt compound expressions, in particular # restricted args. assert isinstance(x, ufl.Coefficient) and not is_restricted(x) # So now we have L(arg, v) where arg = (u, ..., T[u], Pi[u], ...) and the idea # is to define derivative w.r.t to x by doing # J = sum_{arg} (partial L / partial arg){arg=T[u]}(partial arg / partial x). J = 0 # NOTE: in the following I try to avoid assembly of zero forms because # these might not be well-defined for xii assembler. Also, assembling # zeros is useless for fi in [c for c in f.coefficients() if not isinstance(c, df.Constant)]: # Short circuit if (partial fi)/(partial x) is 0 if not ((fi == x) or fi.vector().id() == x.vector().id()): continue if is_restricted(fi): rtype = restriction_type(fi) attributes = (rtype, ) else: rtype = '' attributes = None fi_sub = df.Function(fi.function_space()) # To recreate the form for partial we sub in every integral of f sub_form_integrals = [] for integral in f.integrals(): integrand = ii_replace(integral.integrand(), fi, fi_sub, attributes) # If the substitution is do nothing then there's no need to diff if integrand != integral.integrand(): sub_form_integrals.append( integral.reconstruct(integrand=integrand)) sub_form = ufl.Form(sub_form_integrals) # Partial wrt to substituated argument df_dfi = df.derivative(sub_form, fi_sub) # Substitue back the original form argument sub_form_integrals = [] for integral in df_dfi.integrals(): integrand = ii_replace(integral.integrand(), fi_sub, fi) assert integrand != integral.integrand() sub_form_integrals.append( integral.reconstruct(integrand=integrand)) df_dfi = ufl.Form(sub_form_integrals) # As d Tu / dx = T(x) we now need to restrict the trial function if rtype: trial_f = get_trialfunction(df_dfi) setattr(trial_f, rtype, getattr(fi, rtype)) # Since we only allos diff wrt to coef then in the case rtype == '' # we have dfi/dx = 1 J += df_dfi # Done return J