def __init__(self, module, F, M, u): """ *Arguments* module (Python module) The module to use for specific form manipulations (typically ufl or dolfin) F (tuple or Form) tuple of (bilinear, linear) forms or linear form M (Form) functional or linear form u (Coefficient) The coefficient considered as the unknown. """ # Store module self.module = module # Store solution Coefficient/Function self.u = u # Extract the lhs (bilinear form), rhs (linear form), goal # (functional), weak residual (linear form) linear_case = (isinstance(F, (tuple, list)) and len(F) == 2) if linear_case: self.lhs, self.rhs = F try: self.goal = action(M, u) except: self.goal = M # Allow functionals as input as well self.weak_residual = self.rhs - action(self.lhs, u) else: self.lhs = self.module.derivative(F, u) self.rhs = F self.goal = M self.weak_residual = -F # At least check that final forms have correct rank assert (len(self.lhs.arguments()) == 2) assert (len(self.rhs.arguments()) == 1) assert (len(self.goal.arguments()) == 0) assert (len(self.weak_residual.arguments()) == 1) # Get the domain, assuming there's only one assert (len(self.weak_residual.domains()) == 1) self.domain, = self.weak_residual.domains() # Store map from identifiers to names for forms and generated # coefficients self.ec_names = {} # Use predefined names for the forms in the primal problem self.ec_names[id(self.lhs)] = "lhs" self.ec_names[id(self.rhs)] = "rhs" self.ec_names[id(self.goal)] = "goal" # Initialize other required data self.initialize_data()
def __init__(self, module, F, M, u): """ *Arguments* module (Python module) The module to use for specific form manipulations (typically ufl or dolfin) F (tuple or Form) tuple of (bilinear, linear) forms or linear form M (Form) functional or linear form u (Coefficient) The coefficient considered as the unknown. """ # Store module self.module = module # Store solution Coefficient/Function self.u = u # Extract the lhs (bilinear form), rhs (linear form), goal # (functional), weak residual (linear form) linear_case = (isinstance(F, (tuple, list)) and len(F) == 2) if linear_case: self.lhs, self.rhs = F try: self.goal = action(M, u) except Exception: self.goal = M # Allow functionals as input as well self.weak_residual = self.rhs - action(self.lhs, u) else: self.lhs = self.module.derivative(F, u) self.rhs = F self.goal = M self.weak_residual = - F # At least check that final forms have correct rank assert(len(self.lhs.arguments()) == 2) assert(len(self.rhs.arguments()) == 1) assert(len(self.goal.arguments()) == 0) assert(len(self.weak_residual.arguments()) == 1) # Get the domain self.domain = self.weak_residual.ufl_domain() # Store map from identifiers to names for forms and generated # coefficients self.ec_names = {} # Use predefined names for the forms in the primal problem self.ec_names[id(self.lhs)] = "lhs" self.ec_names[id(self.rhs)] = "rhs" self.ec_names[id(self.goal)] = "goal" # Initialize other required data self.initialize_data()
def subtract_adjoint_derivative_actions(self, adj_x, nl_deps, dep_Bs): for dep_index, dep_B in dep_Bs.items(): if dep_index not in self._adjoint_dF_cache: dep = self.dependencies()[dep_index] dF = derivative(self._F, dep) dF = ufl.algorithms.expand_derivatives(dF) dF = eliminate_zeros(dF) if dF.empty(): dF = None else: dF = adjoint(dF) self._adjoint_dF_cache[dep_index] = dF dF = self._adjoint_dF_cache[dep_index] if dF is not None: if dep_index not in self._adjoint_action_cache: if self._cache_rhs_assembly \ and isinstance(adj_x, backend_Function) \ and is_cached(dF): # Cached matrix action self._adjoint_action_cache[dep_index] = CacheRef() elif self._defer_adjoint_assembly: # Cached form, deferred assembly self._adjoint_action_cache[dep_index] = None else: # Cached form, immediate assembly self._adjoint_action_cache[dep_index] = unbound_form( ufl.action(dF, coefficient=adj_x), list(self.nonlinear_dependencies()) + [adj_x]) cache = self._adjoint_action_cache[dep_index] if cache is None: # Cached form, deferred assembly replace_map = dict(zip(self.nonlinear_dependencies(), nl_deps)) dep_B.sub(ufl.action(ufl.replace(dF, replace_map), coefficient=adj_x)) elif isinstance(cache, CacheRef): # Cached matrix action mat_bc = cache() if mat_bc is None: replace_map = dict(zip(self.nonlinear_dependencies(), nl_deps)) self._adjoint_action_cache[dep_index], (mat, _) = \ assembly_cache().assemble( dF, form_compiler_parameters=self._form_compiler_parameters, # noqa: E501 replace_map=replace_map) else: mat, _ = mat_bc dep_B.sub(matrix_multiply(mat, function_vector(adj_x))) else: # Cached form, immediate assembly assert isinstance(cache, ufl.classes.Form) bind_form(cache, list(nl_deps) + [adj_x]) dep_B.sub(assemble( cache, form_compiler_parameters=self._form_compiler_parameters)) # noqa: E501 unbind_form(cache)
def __init__(self, form, target, source, rowbcs, colbcs): self._twoform = form self._rowbcs = rowbcs self._target = target self._source = source self._colbcs = colbcs self._x = Function(target, name='internalx') self._y = Function(source, name='internaly') self._form = ufl.action(form, self._x) self._formT = ufl.action(adjoint(form), self._y) self._assembled = False if len(rowbcs) > 0: self._xbcs = Function(target) if len(colbcs) > 0: self._ybcs = Function(source) idx_kernels = compile_form(self._form) self.local_assembly_idx = [] self.local_assembly_kernels = [] for idx, kernels in idx_kernels: self.local_assembly_idx.append(idx) self.local_assembly_kernels.append(kernels) idx_kernels = compile_form(self._formT) self.Tlocal_assembly_idx = [] self.Tlocal_assembly_kernels = [] for idx, kernels in idx_kernels: self.Tlocal_assembly_idx.append(idx) self.Tlocal_assembly_kernels.append(kernels) # create matrix mlist = [] nlist = [] for si1 in range(target.nspaces): tspace = target.get_space(si1) for ci1 in range(tspace.ncomp): m = tspace.get_localndofs(ci1) mlist.append(m) for si2 in range(source.nspaces): sspace = source.get_space(si2) for ci2 in range(sspace.ncomp): n = sspace.get_localndofs(ci2) nlist.append(n) M = np.sum(np.array(mlist, dtype=np.int32)) N = np.sum(np.array(nlist, dtype=np.int32)) self.petscmat = PETSc.Mat() self.petscmat.create(PETSc.COMM_WORLD) self.petscmat.setSizes(((M, None), (N, None))) self.petscmat.setType('python') self.petscmat.setPythonContext(self) self.petscmat.setUp() self.petscmat.assemblyBegin() self.petscmat.assemblyEnd()
def _assemble_system(A_form, b_form=None, bcs=[], form_compiler_parameters={}, *args, **kwargs): if isinstance(bcs, backend_DirichletBC): bcs = (bcs, ) if b_form is None: bind_forms(A_form) else: bind_forms(A_form, b_form) A = _assemble(A_form, bcs=bcs, form_compiler_parameters=form_compiler_parameters, *args, **kwargs) if len(bcs) > 0: F = backend_Function(A_form.arguments()[0].function_space()) for bc in bcs: bc.apply(F) if b_form is None: b = _assemble(-ufl.action(A_form, F), bcs=bcs, form_compiler_parameters=form_compiler_parameters, *args, **kwargs) with b.dat.vec_ro as b_v: if b_v.norm(norm_type=PETSc.NormType.NORM_INFINITY) == 0.0: b = None else: b = _assemble(b_form - ufl.action(A_form, F), bcs=bcs, form_compiler_parameters=form_compiler_parameters, *args, **kwargs) else: if b_form is None: b = None else: b = _assemble(b_form, form_compiler_parameters=form_compiler_parameters, *args, **kwargs) A._tlm_adjoint__lift_bcs = False if b_form is None: unbind_forms(A_form) else: unbind_forms(A_form, b_form) return A, b
def __init__(self, a, row_bcs=[], col_bcs=[], fc_params=None, appctx=None): self.a = a self.aT = a.T if isinstance(self.a, slate.TensorBase) else adjoint(a) self.fc_params = fc_params self.appctx = appctx self.row_bcs = row_bcs self.col_bcs = col_bcs # create functions from test and trial space to help # with 1-form assembly test_space, trial_space = [ a.arguments()[i].function_space() for i in (0, 1) ] from firedrake import function self._y = function.Function(test_space) self._x = function.Function(trial_space) # These are temporary storage for holding the BC # values during matvec application. _xbc is for # the action and ._ybc is for transpose. if len(self.row_bcs) > 0: self._xbc = function.Function(trial_space) if len(self.col_bcs) > 0: self._ybc = function.Function(test_space) # Get size information from template vecs on test and trial spaces trial_vec = trial_space.dof_dset.layout_vec test_vec = test_space.dof_dset.layout_vec self.col_sizes = trial_vec.getSizes() self.row_sizes = test_vec.getSizes() self.block_size = (test_vec.getBlockSize(), trial_vec.getBlockSize()) if isinstance(self.a, slate.TensorBase): self.action = self.a * slate.AssembledVector(self._x) self.actionT = self.aT * slate.AssembledVector(self._y) else: self.action = action(self.a, self._x) self.actionT = action(self.aT, self._y) from firedrake.assemble import create_assembly_callable self._assemble_action = create_assembly_callable( self.action, tensor=self._y, form_compiler_parameters=self.fc_params) self._assemble_actionT = create_assembly_callable( self.actionT, tensor=self._x, form_compiler_parameters=self.fc_params)
def __init__(self, a, L, u, bcs=None, aP=None, form_compiler_parameters=None, constant_jacobian=True): """ :param a: the bilinear form :param L: the linear form :param u: the :class:`.Function` to solve for :param bcs: the boundary conditions (optional) :param aP: an optional operator to assemble to precondition the system (if not provided a preconditioner may be computed from ``a``) :param dict form_compiler_parameters: parameters to pass to the form compiler (optional) :param constant_jacobian: (optional) flag indicating that the Jacobian is constant (i.e. does not depend on varying fields). If your Jacobian can change, set this flag to ``False``. """ # In the linear case, the Jacobian is the equation LHS. J = a # Jacobian is checked in superclass, but let's check L here. if not isinstance(L, ufl.Form): raise TypeError("Provided RHS is a '%s', not a Form" % type(L).__name__) if len(L.arguments()) != 1: raise ValueError("Provided RHS is not a linear form") F = ufl.action(J, u) - L super(LinearVariationalProblem, self).__init__(F, u, bcs, J, aP, form_compiler_parameters=form_compiler_parameters) self._constant_jacobian = constant_jacobian
def laplace_coeff_action_forms(mesh, el, coeff_el): Q = dolfin.function.functionspace.FunctionSpace(mesh, el) u = dolfin.function.argument.TrialFunction(Q) v = dolfin.function.argument.TestFunction(Q) """ def boundary(x): return np.sum(np.logical_or(x < DOLFIN_EPS, x > 1.0 - DOLFIN_EPS), axis=1) > 0 """ # u_bc = dolfin.function.constant.Constant(50.0) # bc = dolfin.fem.dirichletbc.DirichletBC(Q, u_bc, boundary) c = dolfin.function.expression.Expression("3.14*x[0]", element=coeff_el) f = dolfin.function.expression.Expression("0.4*x[1]*x[2]", element=el) a = inner(c * grad(u), grad(v)) * dx L = f * v * dx w = dolfin.Function(Q) w.vector().set(1.2) L = ufl.action(a, w) return None, L, None
def test_mass_action(form, cell, order): degrees = numpy.arange(4, 10) flops = [ count_flops(action(form(cell, int(degree)))) for degree in degrees ] rates = numpy.diff(numpy.log(flops)) / numpy.diff(numpy.log(degrees + 1)) assert (rates < order).all()
def __init__(self, a, L, u, bcs=None, aP=None, form_compiler_parameters=None, nest=None, constant_jacobian=True): """ :param a: the bilinear form :param L: the linear form :param u: the :class:`.Function` to solve for :param bcs: the boundary conditions (optional) :param aP: an optional operator to assemble to precondition the system (if not provided a preconditioner may be computed from ``a``) :param dict form_compiler_parameters: parameters to pass to the form compiler (optional) :param nest: indicate if matrices on mixed spaces should be built as monolithic operators (suitable for direct solves), or as nested blocks (suitable for fieldsplit preconditioning). If not provided, uses the default given by :data:`parameters["matnest"]`. :param constant_jacobian: (optional) flag indicating that the Jacobian is constant (i.e. does not depend on varying fields). If your Jacobian can change, set this flag to :data:`False`. """ # In the linear case, the Jacobian is the equation LHS. J = a F = ufl.action(J, u) - L super(LinearVariationalProblem, self).__init__(F, u, bcs, J, aP, form_compiler_parameters=form_compiler_parameters, nest=nest) self._constant_jacobian = constant_jacobian
def error_estimate(self): """ Generate and return functional defining error estimate """ # Error estimate defined as r(Ez_h): eta_h = action(self.weak_residual, self._Ez_h) return eta_h
def verify_assembly(J, rhs, J_mat, b, bcs, form_compiler_parameters, linear_solver_parameters, J_tolerance, b_tolerance): if J_mat is not None and not np.isposinf(J_tolerance): J_mat_debug = backend_assemble( J, bcs=bcs, **assemble_arguments(2, form_compiler_parameters, linear_solver_parameters)) assert J_mat.petscmat.assembled J_error = J_mat.petscmat.copy() J_error.axpy(-1.0, J_mat_debug.petscmat) assert J_error.assembled assert J_error.norm(norm_type=PETSc.NormType.NORM_INFINITY) \ <= J_tolerance * J_mat.petscmat.norm(norm_type=PETSc.NormType.NORM_INFINITY) # noqa: E501 if b is not None and not np.isposinf(b_tolerance): F = backend_Function(rhs.arguments()[0].function_space()) for bc in bcs: bc.apply(F) b_debug = backend_assemble( rhs - ufl.action(J, F), bcs=bcs, form_compiler_parameters=form_compiler_parameters) b_error = b.copy(deepcopy=True) with b_error.dat.vec as b_error_v, b_debug.dat.vec_ro as b_debug_v: b_error_v.axpy(-1.0, b_debug_v) with b_error.dat.vec_ro as b_error_v, b.dat.vec_ro as b_v: assert b_error_v.norm(norm_type=PETSc.NormType.NORM_INFINITY) \ <= b_tolerance * b_v.norm(norm_type=PETSc.NormType.NORM_INFINITY) # noqa: E501
def __init__(self, a, L, u, bcs=None, aP=None, form_compiler_parameters=None, constant_jacobian=True): """ :param a: the bilinear form :param L: the linear form :param u: the :class:`.Function` to solve for :param bcs: the boundary conditions (optional) :param aP: an optional operator to assemble to precondition the system (if not provided a preconditioner may be computed from ``a``) :param dict form_compiler_parameters: parameters to pass to the form compiler (optional) :param constant_jacobian: (optional) flag indicating that the Jacobian is constant (i.e. does not depend on varying fields). If your Jacobian can change, set this flag to :data:`False`. """ # In the linear case, the Jacobian is the equation LHS. J = a F = ufl.action(J, u) - L super(LinearVariationalProblem, self).__init__(F, u, bcs, J, aP, form_compiler_parameters=form_compiler_parameters) self._constant_jacobian = constant_jacobian
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 = ufl.replace(dF, dict(zip(self.nonlinear_dependencies(), nl_deps))) if self._rank == 0: dF = assemble( dF, form_compiler_parameters=self._form_compiler_parameters) return (-real_function_value(adj_x), dF) else: assert self._rank == 1 dF = assemble( ufl.action(adjoint(dF), adj_x), form_compiler_parameters=self._form_compiler_parameters) return (-1.0, dF)
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_rhs(cell, order): degrees = list(range(3, 8)) if cell == TensorProductCell(triangle, interval): degrees = list(range(3, 6)) flops = [count_flops(action(helmholtz(cell, degree))) for degree in degrees] rates = numpy.diff(numpy.log(flops)) / numpy.diff(numpy.log(degrees)) assert (rates < order).all()
def __init__(self, a, row_bcs=[], col_bcs=[], fc_params=None, appctx=None): self.a = a self.aT = adjoint(a) self.fc_params = fc_params self.appctx = appctx self.row_bcs = row_bcs self.col_bcs = col_bcs # create functions from test and trial space to help # with 1-form assembly test_space, trial_space = [ a.arguments()[i].function_space() for i in (0, 1) ] from firedrake import function self._y = function.Function(test_space) self._x = function.Function(trial_space) # These are temporary storage for holding the BC # values during matvec application. _xbc is for # the action and ._ybc is for transpose. if len(self.row_bcs) > 0: self._xbc = function.Function(trial_space) if len(self.col_bcs) > 0: self._ybc = function.Function(test_space) # Get size information from template vecs on test and trial spaces trial_vec = trial_space.dof_dset.layout_vec test_vec = test_space.dof_dset.layout_vec self.col_sizes = trial_vec.getSizes() self.row_sizes = test_vec.getSizes() self.block_size = (test_vec.getBlockSize(), trial_vec.getBlockSize()) self.action = action(self.a, self._x) self.actionT = action(self.aT, self._y) from firedrake.assemble import create_assembly_callable self._assemble_action = create_assembly_callable(self.action, tensor=self._y, form_compiler_parameters=self.fc_params) self._assemble_actionT = create_assembly_callable(self.actionT, tensor=self._x, form_compiler_parameters=self.fc_params)
def test_vector_laplace_action(cell, order): degrees = numpy.arange(3, 8) if cell == TensorProductCell(triangle, interval): degrees = numpy.arange(3, 6) flops = [[count_flops(action(form)) for form in split_vector_laplace(cell, int(degree))] for degree in degrees] rates = numpy.diff(numpy.log(flops).T) / numpy.diff(numpy.log(degrees)) assert (rates < order).all()
def vjp_solve_eval_impl( g: np.array, fenics_solution: fenics.Function, fenics_residual: ufl.Form, fenics_inputs: List[FenicsVariable], bcs: List[fenics.DirichletBC], ) -> Tuple[np.array]: """Computes the gradients of the output with respect to the inputs.""" # Convert tangent covector (adjoint) to a FEniCS variable adj_value = numpy_to_fenics(g, fenics_solution) adj_value = adj_value.vector() F = fenics_residual u = fenics_solution V = u.function_space() dFdu = fenics.derivative(F, u) adFdu = ufl.adjoint( dFdu, reordered_arguments=ufl.algorithms.extract_arguments(dFdu) ) u_adj = fenics.Function(V) adj_F = ufl.action(adFdu, u_adj) adj_F = ufl.replace(adj_F, {u_adj: fenics.TrialFunction(V)}) adj_F_assembled = fenics.assemble(adj_F) if len(bcs) != 0: for bc in bcs: bc.homogenize() hbcs = bcs for bc in hbcs: bc.apply(adj_F_assembled) bc.apply(adj_value) fenics.solve(adj_F_assembled, u_adj.vector(), adj_value) fenics_grads = [] for fenics_input in fenics_inputs: if isinstance(fenics_input, fenics.Function): V = fenics_input.function_space() dFdm = fenics.derivative(F, fenics_input, fenics.TrialFunction(V)) adFdm = fenics.adjoint(dFdm) result = fenics.assemble(-adFdm * u_adj) if isinstance(fenics_input, fenics.Constant): fenics_grad = fenics.Constant(result.sum()) else: # fenics.Function fenics_grad = fenics.Function(V, result) fenics_grads.append(fenics_grad) # Convert FEniCS gradients to jax array representation jax_grads = ( None if fg is None else np.asarray(fenics_to_numpy(fg)) for fg in fenics_grads ) jax_grad_tuple = tuple(jax_grads) return jax_grad_tuple
def _rhs(self): from firedrake.assemble import create_assembly_callable u = function.Function(self.trial_space) b = function.Function(self.test_space) if isinstance(self.A.a, slate.TensorBase): expr = -self.A.a * slate.AssembledVector(u) else: expr = -ufl.action(self.A.a, u) return u, create_assembly_callable(expr, tensor=b), b
def _Abcs(self): """A function storing the action of the operator on a zero Function satisfying the BCs. Used in the presence of BCs. """ b = function.Function(self._W) for bc in self.A.bcs: bc.apply(b) return assemble._assemble(ufl.action(self.A.a, b))
def _Matrix_mul(self, other): return_value = backend_Matrix._tlm_adjoint__orig___mul__(self, other) if hasattr(self, "_tlm_adjoint__form") \ and hasattr(other, "_tlm_adjoint__function") \ and len(self._tlm_adjoint__bcs) == 0: return_value._tlm_adjoint__form = ufl.action( self._tlm_adjoint__form, coefficient=other._tlm_adjoint__function) return_value._tlm_adjoint__bcs = [] return_value._tlm_adjoint__form_compiler_parameters \ = self._tlm_adjoint__form_compiler_parameters return return_value
def _Abcs(self): """A function storing the action of the operator on a zero Function satisfying the BCs. Used in the presence of BCs. """ b = function.Function(self._W) for bc in self.A.bcs: bc.apply(b) from firedrake.assemble import _assemble return _assemble(ufl.action(self.A.a, b))
def forward(x): u = fn.TrialFunction(V) w = fn.TestFunction(V) sigma = lmbda * tr(sym(grad(u))) * Identity(2) + 2 * G * sym( grad(u)) # Stress R = simp(x) * inner(sigma, grad(w)) * dx - dot(b, w) * dx a, L = ufl.lhs(R), ufl.rhs(R) u = fn.Function(V) F = L - ufl.action(a, u) fn.solve(a == L, u, bcs) return u, F, bcs
def action(form, coefficient): """Compute the action of a form on a coefficient. :arg form: A UFL form, or a Slate tensor. :arg coefficient: The :class:`~.Function` to act on. :returns: a symbolic expression for the action. """ if isinstance(form, firedrake.slate.TensorBase): if form.rank == 0: raise ValueError("Can't take action of rank-0 tensor") return form * firedrake.AssembledVector(coefficient) else: return ufl.action(form, coefficient)
def _form_action(self, u): """Assemble the form action of this :class:`Matrix`' bilinear form onto the :class:`Function` ``u``. .. note:: This is the form **without** any boundary conditions.""" if not hasattr(self, '_a_action'): self._a_action = ufl.action(self._a, u) if hasattr(self, '_a_action_coeff'): self._a_action = ufl.replace(self._a_action, {self._a_action_coeff: u}) self._a_action_coeff = u # Since we assemble the cached form, the kernels will already have # been compiled and stashed on the form the second time round return assemble._assemble(self._a_action)
def _Abcs(self): """A function storing the action of the operator on a zero Function satisfying the BCs. Used in the presence of BCs. """ b = function.Function(self._W) for bc in self.A.bcs: bc.apply(b) from firedrake.assemble import _assemble if isinstance(self.A.a, slate.TensorBase): return _assemble(self.A.a * b) else: return _assemble(ufl.action(self.A.a, b))
def _Abcs(self): """A function storing the action of the operator on a zero Function satisfying the BCs. Used in the presence of BCs. """ b = function.Function(self._W) for bc in self.A.bcs: bc.apply(b) from firedrake.assemble import _assemble if isinstance(self.A.a, slate.TensorBase): return _assemble(self.A.a * slate.AssembledVector(b)) else: return _assemble(ufl.action(self.A.a, b))
def __init__(self, a, L, u, bcs=None, aP=None, form_compiler_parameters=None, nest=None, constant_jacobian=True): """ :param a: the bilinear form :param L: the linear form :param u: the :class:`.Function` to solve for :param bcs: the boundary conditions (optional) :param aP: an optional operator to assemble to precondition the system (if not provided a preconditioner may be computed from ``a``) :param dict form_compiler_parameters: parameters to pass to the form compiler (optional) :param nest: indicate if matrices on mixed spaces should be built as monolithic operators (suitable for direct solves), or as nested blocks (suitable for fieldsplit preconditioning). If not provided, uses the default given by ``parameters["matnest"]``. :param constant_jacobian: (optional) flag indicating that the Jacobian is constant (i.e. does not depend on varying fields). If your Jacobian can change, set this flag to ``False``. """ # In the linear case, the Jacobian is the equation LHS. J = a # Jacobian is checked in superclass, but let's check L here. if not isinstance(L, ufl.Form): raise TypeError("Provided RHS is a '%s', not a Form" % type(L).__name__) if len(L.arguments()) != 1: raise ValueError("Provided RHS is not a linear form") F = ufl.action(J, u) - L super(LinearVariationalProblem, self).__init__(F, u, bcs, J, aP, form_compiler_parameters=form_compiler_parameters, nest=nest) self._constant_jacobian = constant_jacobian
def _form_action(self, u): """Assemble the form action of this :class:`Matrix`' bilinear form onto the :class:`Function` ``u``. .. note:: This is the form **without** any boundary conditions.""" if not hasattr(self, '_a_action'): self._a_action = ufl.action(self._a, u) if hasattr(self, '_a_action_coeff'): self._a_action = ufl.replace(self._a_action, {self._a_action_coeff: u}) self._a_action_coeff = u # Since we assemble the cached form, the kernels will already have # been compiled and stashed on the form the second time round from firedrake.assemble import _assemble return _assemble(self._a_action)
def __init__(self, a, L, u, bcs=None, form_compiler_parameters=None): """ :param a: the bilinear form :param L: the linear form :param u: the :class:`.Function` to solve for :param bcs: the boundary conditions (optional) :param dict form_compiler_parameters: parameters to pass to the form compiler (optional) """ # In the linear case, the Jacobian is the equation LHS. J = a F = ufl.action(J, u) - L super(LinearVariationalProblem, self).__init__(F, u, bcs, J, form_compiler_parameters)
def hyperelasticity_action_forms(mesh, vec_el): cell = mesh.ufl_cell() Q = dolfin.FunctionSpace(mesh, vec_el) # Coefficients v = dolfin.function.argument.TestFunction(Q) # Test function du = dolfin.function.argument.TrialFunction(Q) # Incremental displacement u = dolfin.Function(Q) # Displacement from previous iteration u.vector().set(0.5) B = dolfin.Constant((0.0, -0.5, 0.0), cell) # Body force per unit volume T = dolfin.Constant((0.1, 0.0, 0.0), cell) # Traction force on the boundary # Kinematics d = u.geometric_dimension() F = ufl.Identity(d) + grad(u) # Deformation gradient C = F.T * F # Right Cauchy-Green tensor # Invariants of deformation tensors Ic = tr(C) J = det(F) # Elasticity parameters E, nu = 10.0, 0.3 mu = dolfin.Constant(E / (2 * (1 + nu)), cell) lmbda = dolfin.Constant(E * nu / ((1 + nu) * (1 - 2 * nu)), cell) # Stored strain energy density (compressible neo-Hookean model) psi = (mu / 2) * (Ic - 3) - mu * ln(J) + (lmbda / 2) * (ln(J)) ** 2 # Total potential energy Pi = psi * dx - dot(B, u) * dx - dot(T, u) * ds # Compute first variation of Pi (directional derivative about u in the direction of v) F = ufl.derivative(Pi, u, v) # Compute Jacobian of F J = ufl.derivative(F, u, du) w = dolfin.Function(Q) w.vector().set(1.2) L = ufl.action(J, w) return None, L, None
def jvp_solve_eval( fenics_function: Callable, fenics_templates: Iterable[FenicsVariable], primals: Tuple[np.array], tangents: Tuple[np.array], ) -> Tuple[np.array]: """Computes the tangent linear model """ ( numpy_output_primal, fenics_solution_primal, residual_form, fenics_primals, bcs, ) = solve_eval(fenics_function, fenics_templates, *primals) # Now tangent evaluation! F = residual_form u = fenics_solution_primal V = u.function_space() if len(bcs) != 0: for bc in bcs: bc.homogenize() hbcs = bcs fenics_tangents = convert_all_to_fenics(fenics_primals, *tangents) fenics_output_tangents = [] for fp, ft in zip(fenics_primals, fenics_tangents): dFdu = fenics.derivative(F, u) dFdm = fenics.derivative(F, fp, ft) u_tlm = fenics.Function(V) tlm_F = ufl.action(dFdu, u_tlm) + dFdm tlm_F = ufl.replace(tlm_F, {u_tlm: fenics.TrialFunction(V)}) fenics.solve(ufl.lhs(tlm_F) == ufl.rhs(tlm_F), u_tlm, bcs=hbcs) fenics_output_tangents.append(u_tlm) jax_output_tangents = (fenics_to_numpy(ft) for ft in fenics_output_tangents) jax_output_tangent = sum(jax_output_tangents) return numpy_output_primal, jax_output_tangent
# # Author: Martin Sandve Alnes # Date: 2008-10-30 # from ufl import (Coefficient, FiniteElement, action, adjoint, derivative, dx, grad, inner, triangle) element = FiniteElement("Lagrange", triangle, 1) w = Coefficient(element) # H1 semi-norm f = inner(grad(w), grad(w)) / 2 * dx # grad(w) : grad(v) b = derivative(f, w) # stiffness matrix, grad(u) : grad(v) a = derivative(b, w) # adjoint, grad(v) : grad(u) astar = adjoint(a) # action of adjoint, grad(v) : grad(w) astaraction = action(astar) forms = [f, b, a, astar, astaraction]
def __init__(self, eq, x, bcs=[], J=None, form_compiler_parameters={}, solver_parameters={}, adjoint_solver_parameters=None, tlm_solver_parameters=None, initial_guess=None, cache_jacobian=None, cache_adjoint_jacobian=None, cache_tlm_jacobian=None, cache_rhs_assembly=None, match_quadrature=None, defer_adjoint_assembly=None): if isinstance(bcs, backend_DirichletBC): bcs = (bcs,) else: bcs = tuple(bcs) if cache_jacobian is None: if not parameters["tlm_adjoint"]["EquationSolver"]["enable_jacobian_caching"]: # noqa: E501 cache_jacobian = False if cache_rhs_assembly is None: cache_rhs_assembly = parameters["tlm_adjoint"]["EquationSolver"]["cache_rhs_assembly"] # noqa: E501 if match_quadrature is None: match_quadrature = parameters["tlm_adjoint"]["EquationSolver"]["match_quadrature"] # noqa: E501 if defer_adjoint_assembly is None: defer_adjoint_assembly = parameters["tlm_adjoint"]["EquationSolver"]["defer_adjoint_assembly"] # noqa: E501 if match_quadrature and defer_adjoint_assembly: raise EquationException("Cannot both match quadrature and defer adjoint assembly") # noqa: E501 lhs, rhs = eq.lhs, eq.rhs del eq lhs = ufl.classes.Form(lhs.integrals()) linear = isinstance(rhs, ufl.classes.Form) if linear: rhs = ufl.classes.Form(rhs.integrals()) if J is not None: J = ufl.classes.Form(J.integrals()) if linear: if x in lhs.coefficients() or x in rhs.coefficients(): raise EquationException("Invalid non-linear dependency") F = ufl.action(lhs, coefficient=x) + form_neg(rhs) nl_solve_J = None J = lhs else: F = lhs if rhs != 0: raise EquationException("Invalid right-hand-side") nl_solve_J = J J = derivative(F, x) J = ufl.algorithms.expand_derivatives(J) deps, nl_deps = extract_dependencies(F) if nl_solve_J is not None: for dep in nl_solve_J.coefficients(): if is_function(dep): dep_id = function_id(dep) if dep_id not in deps: deps[dep_id] = dep if initial_guess is not None: warnings.warn("'initial_guess' argument is deprecated", DeprecationWarning, stacklevel=2) if initial_guess == x: initial_guess = None else: initial_guess_id = function_id(initial_guess) if initial_guess_id not in deps: deps[initial_guess_id] = initial_guess deps = list(deps.values()) if x in deps: deps.remove(x) deps.insert(0, x) nl_deps = tuple(nl_deps.values()) hbcs = tuple(homogenized_bc(bc) for bc in bcs) if cache_jacobian is None: cache_jacobian = is_cached(J) and bcs_is_cached(bcs) if cache_adjoint_jacobian is None: cache_adjoint_jacobian = cache_jacobian if cache_tlm_jacobian is None: cache_tlm_jacobian = cache_jacobian if nl_solve_J is None: (solver_parameters, linear_solver_parameters, ic, J_ic) = process_solver_parameters(solver_parameters, linear) else: (solver_parameters, _, ic, _) = process_solver_parameters(solver_parameters, linear) (_, linear_solver_parameters, _, J_ic) = process_solver_parameters(solver_parameters, linear) if adjoint_solver_parameters is None: adjoint_solver_parameters = process_adjoint_solver_parameters(linear_solver_parameters) # noqa: E501 adj_ic = J_ic else: (_, adjoint_solver_parameters, adj_ic, _) = process_solver_parameters(adjoint_solver_parameters, linear=True) # noqa: E501 if tlm_solver_parameters is not None: (_, tlm_solver_parameters, _, _) = process_solver_parameters(tlm_solver_parameters, linear=True) # noqa: E501 form_compiler_parameters_ = copy_parameters_dict(parameters["form_compiler"]) # noqa: E501 update_parameters_dict(form_compiler_parameters_, form_compiler_parameters) form_compiler_parameters = form_compiler_parameters_ if match_quadrature: update_parameters_dict( form_compiler_parameters, form_form_compiler_parameters(F, form_compiler_parameters)) super().__init__(x, deps, nl_deps=nl_deps, ic=initial_guess is None and ic, adj_ic=adj_ic) self._F = F self._lhs, self._rhs = lhs, rhs self._bcs = bcs self._hbcs = hbcs self._J = J self._nl_solve_J = nl_solve_J self._form_compiler_parameters = form_compiler_parameters self._solver_parameters = solver_parameters self._linear_solver_parameters = linear_solver_parameters self._adjoint_solver_parameters = adjoint_solver_parameters self._tlm_solver_parameters = tlm_solver_parameters if initial_guess is None: self._initial_guess_index = None else: self._initial_guess_index = deps.index(initial_guess) self._linear = linear self._cache_jacobian = cache_jacobian self._cache_adjoint_jacobian = cache_adjoint_jacobian self._cache_tlm_jacobian = cache_tlm_jacobian self._cache_rhs_assembly = cache_rhs_assembly self._defer_adjoint_assembly = defer_adjoint_assembly self._forward_eq = None self._forward_J_solver = CacheRef() self._forward_b_pa = None self._adjoint_dF_cache = {} self._adjoint_action_cache = {} self._adjoint_J_solver = CacheRef() self._adjoint_J = None
def _compileUFL(integrands, form, *args, tempVars=True): if isinstance(form, Equation): form = form.lhs - form.rhs if not isinstance(form, Form): raise ValueError("ufl.Form or ufl.Equation expected.") # added for dirichlet treatment same as conservationlaw model dirichletBCs = [arg for arg in args if isinstance(arg, DirichletBC)] uflExpr = [form] + [bc.ufl_value for bc in dirichletBCs] if len(form.arguments()) < 2: raise ValueError( "Integrands model requires form with at least two arguments.") x = SpatialCoordinate(form.ufl_cell()) n = FacetNormal(form.ufl_cell()) cellVolume = CellVolume(form.ufl_cell()) maxCellEdgeLength = MaxCellEdgeLength(form.ufl_cell()) minCellEdgeLength = MinCellEdgeLength(form.ufl_cell()) facetArea = FacetArea(form.ufl_cell()) maxFacetEdgeLength = MaxFacetEdgeLength(form.ufl_cell()) minFacetEdgeLength = MinFacetEdgeLength(form.ufl_cell()) phi, u = form.arguments() ubar = Coefficient(u.ufl_function_space()) derivatives = gatherDerivatives(form, [phi, u]) derivatives_phi = derivatives[0] derivatives_u = derivatives[1] derivatives_ubar = map_expr_dags(Replacer({u: ubar}), derivatives_u) try: integrands.field = u.ufl_function_space().field except AttributeError: pass integrals = splitForm(form, [phi]) dform = apply_derivatives(derivative(action(form, ubar), ubar, u)) linearizedIntegrals = splitForm(dform, [phi, u]) if not set( integrals.keys()) <= {'cell', 'exterior_facet', 'interior_facet'}: raise Exception('unknown integral encountered in ' + str(set(integrals.keys())) + '.') if 'cell' in integrals.keys(): arg = Variable(integrands.domainValueTuple, 'u') predefined = { derivatives_u[i]: arg[i] for i in range(len(derivatives_u)) } predefined[x] = integrands.spatialCoordinate('x') predefined[cellVolume] = integrands.cellVolume() predefined[maxCellEdgeLength] = maxEdgeLength( integrands.cellGeometry()) predefined[minCellEdgeLength] = minEdgeLength( integrands.cellGeometry()) integrands.predefineCoefficients(predefined, False) integrands.interior = generateUnaryCode(predefined, derivatives_phi, integrals['cell'], tempVars=tempVars) predefined = { derivatives_ubar[i]: arg[i] for i in range(len(derivatives_u)) } predefined[x] = integrands.spatialCoordinate('x') predefined[cellVolume] = integrands.cellVolume() predefined[maxCellEdgeLength] = maxEdgeLength( integrands.cellGeometry()) predefined[minCellEdgeLength] = minEdgeLength( integrands.cellGeometry()) integrands.predefineCoefficients(predefined, False) integrands.linearizedInterior = generateUnaryLinearizedCode( predefined, derivatives_phi, derivatives_u, linearizedIntegrals.get('cell'), tempVars=tempVars) if 'exterior_facet' in integrals.keys(): arg = Variable(integrands.domainValueTuple, 'u') predefined = { derivatives_u[i]: arg[i] for i in range(len(derivatives_u)) } predefined[x] = integrands.spatialCoordinate('x') predefined[n] = integrands.facetNormal('x') predefined[cellVolume] = integrands.cellVolume() predefined[maxCellEdgeLength] = maxEdgeLength( integrands.cellGeometry()) predefined[minCellEdgeLength] = minEdgeLength( integrands.cellGeometry()) predefined[facetArea] = integrands.facetArea() predefined[maxFacetEdgeLength] = maxEdgeLength( integrands.facetGeometry()) predefined[minFacetEdgeLength] = minEdgeLength( integrands.facetGeometry()) integrands.predefineCoefficients(predefined, False) integrands.boundary = generateUnaryCode(predefined, derivatives_phi, integrals['exterior_facet'], tempVars=tempVars) predefined = { derivatives_ubar[i]: arg[i] for i in range(len(derivatives_u)) } predefined[x] = integrands.spatialCoordinate('x') predefined[n] = integrands.facetNormal('x') predefined[cellVolume] = integrands.cellVolume() predefined[maxCellEdgeLength] = maxEdgeLength( integrands.cellGeometry()) predefined[minCellEdgeLength] = minEdgeLength( integrands.cellGeometry()) predefined[facetArea] = integrands.facetArea() predefined[maxFacetEdgeLength] = maxEdgeLength( integrands.facetGeometry()) predefined[minFacetEdgeLength] = minEdgeLength( integrands.facetGeometry()) integrands.predefineCoefficients(predefined, False) integrands.linearizedBoundary = generateUnaryLinearizedCode( predefined, derivatives_phi, derivatives_u, linearizedIntegrals.get('exterior_facet'), tempVars=tempVars) if 'interior_facet' in integrals.keys(): argIn = Variable(integrands.domainValueTuple, 'uIn') argOut = Variable(integrands.domainValueTuple, 'uOut') predefined = { derivatives_u[i](s): arg[i] for i in range(len(derivatives_u)) for s, arg in (('+', argIn), ('-', argOut)) } predefined[x] = integrands.spatialCoordinate('xIn') predefined[n('+')] = integrands.facetNormal('xIn') predefined[cellVolume('+')] = integrands.cellVolume('Side::in') predefined[cellVolume('-')] = integrands.cellVolume('Side::out') predefined[maxCellEdgeLength('+')] = maxEdgeLength( integrands.cellGeometry('Side::in')) predefined[maxCellEdgeLength('-')] = maxEdgeLength( integrands.cellGeometry('Side::out')) predefined[minCellEdgeLength('+')] = minEdgeLength( integrands.cellGeometry('Side::in')) predefined[minCellEdgeLength('-')] = minEdgeLength( integrands.cellGeometry('Side::out')) predefined[facetArea] = integrands.facetArea() predefined[maxFacetEdgeLength] = maxEdgeLength( integrands.facetGeometry()) predefined[minFacetEdgeLength] = minEdgeLength( integrands.facetGeometry()) integrands.predefineCoefficients(predefined, True) integrands.skeleton = generateBinaryCode(predefined, derivatives_phi, integrals['interior_facet'], tempVars=tempVars) predefined = { derivatives_ubar[i](s): arg[i] for i in range(len(derivatives_u)) for s, arg in (('+', argIn), ('-', argOut)) } predefined[x] = integrands.spatialCoordinate('xIn') predefined[n('+')] = integrands.facetNormal('xIn') predefined[cellVolume('+')] = integrands.cellVolume('Side::in') predefined[cellVolume('-')] = integrands.cellVolume('Side::out') predefined[maxCellEdgeLength('+')] = maxEdgeLength( integrands.cellGeometry('Side::in')) predefined[maxCellEdgeLength('-')] = maxEdgeLength( integrands.cellGeometry('Side::out')) predefined[minCellEdgeLength('+')] = minEdgeLength( integrands.cellGeometry('Side::in')) predefined[minCellEdgeLength('-')] = minEdgeLength( integrands.cellGeometry('Side::out')) predefined[facetArea] = integrands.facetArea() predefined[maxFacetEdgeLength] = maxEdgeLength( integrands.facetGeometry()) predefined[minFacetEdgeLength] = minEdgeLength( integrands.facetGeometry()) integrands.predefineCoefficients(predefined, True) integrands.linearizedSkeleton = generateBinaryLinearizedCode( predefined, derivatives_phi, derivatives_u, linearizedIntegrals.get('interior_facet'), tempVars=tempVars) if dirichletBCs: integrands.hasDirichletBoundary = True predefined = {} # predefined[x] = UnformattedExpression('auto', 'entity().geometry().global( Dune::Fem::coordinate( ' + integrands.arg_x.name + ' ) )') predefined[x] = UnformattedExpression( 'auto', 'intersection.geometry().center( )') integrands.predefineCoefficients(predefined, False) maxId = 0 codeDomains = [] bySubDomain = dict() neuman = [] wholeDomain = None for bc in dirichletBCs: if bc.subDomain in bySubDomain: raise Exception( 'Multiply defined Dirichlet boundary for subdomain ' + str(bc.subDomain)) if not isinstance(bc.functionSpace, (FunctionSpace, FiniteElementBase)): raise Exception( 'Function space must either be a ufl.FunctionSpace or a ufl.FiniteElement' ) if isinstance(bc.functionSpace, FunctionSpace) and ( bc.functionSpace != u.ufl_function_space()): raise Exception( 'Space of trial function and dirichlet boundary function must be the same - note that boundary conditions on subspaces are not available, yet' ) if isinstance(bc.functionSpace, FiniteElementBase) and ( bc.functionSpace != u.ufl_element()): raise Exception( 'Cannot handle boundary conditions on subspaces, yet') if isinstance(bc.value, list): neuman = [i for i, x in enumerate(bc.value) if x == None] else: neuman = [] value = ExprTensor(u.ufl_shape) for key in value.keys(): value[key] = Indexed( bc.ufl_value, MultiIndex(tuple(FixedIndex(k) for k in key))) if bc.subDomain is None: wholeDomain = value, neuman elif isinstance(bc.subDomain, int): bySubDomain[bc.subDomain] = value, neuman maxId = max(maxId, bc.subDomain) else: domain = ExprTensor(()) for key in domain.keys(): domain[key] = Indexed( bc.subDomain, MultiIndex(tuple(FixedIndex(k) for k in key))) codeDomains.append((value, neuman, domain)) defaultCode = [] if len(codeDomains) > 0: defaultCode.append(Declaration(Variable('int', 'domainId'))) # defaultCode.append(Declaration(Variable('auto', 'x'), # initializer=UnformattedExpression('auto','intersection.geometry().center()'))) for i, v in enumerate(codeDomains): block = Block() block.append( generateDirichletDomainCode(predefined, v[2], tempVars=tempVars)) block.append('if (domainId)') ifBlock = UnformattedBlock() ifBlock.append( 'std::fill( dirichletComponent.begin(), dirichletComponent.end(), ' + str(maxId + i + 2) + ' );') if len(v[1]) > 0: [ ifBlock.append('dirichletComponent[' + str(c) + '] = 0;') for c in v[1] ] ifBlock.append('return true;') block.append(ifBlock) defaultCode.append(block) if wholeDomain is not None: block = UnformattedBlock() block.append( 'std::fill( dirichletComponent.begin(), dirichletComponent.end(), ' + str(maxId + 1) + ' );') if len(wholeDomain[1]) > 0: [ block.append('dirichletComponent[' + str(c) + '] = 0;') for c in wholeDomain[1] ] block.append('return true;') defaultCode.append(block) defaultCode.append(return_(False)) bndId = Variable('const int', 'bndId') getBndId = UnformattedExpression( 'int', 'BoundaryIdProviderType::boundaryId( ' + integrands.arg_i.name + ' )') # getBndId = UnformattedExpression('int', 'boundaryIdGetter_.boundaryId( ' + integrands.arg_i.name + ' )') switch = SwitchStatement(bndId, default=defaultCode) for i, v in bySubDomain.items(): code = [] if len(v[1]) > 0: [ code.append('dirichletComponent[' + str(c) + '] = 0;') for c in v[1] ] code.append(return_(True)) switch.append(i, code) integrands.isDirichletIntersection = [ Declaration(bndId, initializer=getBndId), UnformattedBlock( 'std::fill( dirichletComponent.begin(), dirichletComponent.end(), ' + bndId.name + ' );'), switch ] predefined[x] = UnformattedExpression( 'auto', 'entity().geometry().global( Dune::Fem::coordinate( ' + integrands.arg_x.name + ' ) )') if wholeDomain is None: defaultCode = assign(integrands.arg_r, construct("RRangeType", 0)) else: defaultCode = generateDirichletCode(predefined, wholeDomain[0], tempVars=tempVars) switch = SwitchStatement(integrands.arg_bndId, default=defaultCode) for i, v in bySubDomain.items(): switch.append( i, generateDirichletCode(predefined, v[0], tempVars=tempVars)) for i, v in enumerate(codeDomains): switch.append( i + maxId + 2, generateDirichletCode(predefined, v[0], tempVars=tempVars)) integrands.dirichlet = [switch] return integrands
def _action(A, x): A = ufl.algorithms.expand_derivatives(A) if A.integrals() != (): # form is not empty: return ufl.action(A, x) else: return A # form is empty, doesn't matter
coord_element = VectorElement("Lagrange", triangle, 1) mesh = Mesh(coord_element) # Function Space element = FiniteElement("Lagrange", triangle, 2) V = FunctionSpace(mesh, element) # Trial and test functions u = TrialFunction(V) v = TestFunction(V) # Define a constant RHS f = Constant(V) # Define the bilinear and linear forms according to the # variational formulation of the equations:: a = inner(grad(u), grad(v)) * dx L = inner(f, v) * dx # Define linear form representing the action of the form "a" on # the coefficient "ui" ui = Coefficient(V) M = action(a, ui) # Define form to compute the L2 norm of the error usol = Coefficient(V) uexact = Coefficient(V) E = inner(usol - uexact, usol - uexact) * dx forms = [M, L, E]
def compileUFL(form, patch, *args, **kwargs): if isinstance(form, Equation): form = form.lhs - form.rhs if not isinstance(form, Form): raise Exception("ufl.Form expected.") if len(form.arguments()) < 2: raise Exception("ConservationLaw model requires form with at least two arguments.") phi_, u_ = form.arguments() if phi_.ufl_function_space().scalar: phi = TestFunction(phi_.ufl_function_space().toVectorSpace()) form = replace(form,{phi_:phi[0]}) else: phi = phi_ if u_.ufl_function_space().scalar: u = TrialFunction(u_.ufl_function_space().toVectorSpace()) form = replace(form,{u_:u[0]}) else: u = u_ _, coeff_ = extract_arguments_and_coefficients(form) coeff_ = set(coeff_) # added for dirichlet treatment same as conservationlaw model dirichletBCs = [arg for arg in args if isinstance(arg, DirichletBC)] # remove the dirichletBCs arg = [arg for arg in args if not isinstance(arg, DirichletBC)] for dBC in dirichletBCs: _, coeff__ = extract_arguments_and_coefficients(dBC.ufl_value) coeff_ |= set(coeff__) if patch is not None: for a in patch: try: _, coeff__ = extract_arguments_and_coefficients(a) coeff_ |= set(coeff__) except: pass # a might be a float/int and not a ufl expression coeff = {c : c.toVectorCoefficient()[0] for c in coeff_ if len(c.ufl_shape) == 0 and not c.is_cellwise_constant()} form = replace(form,coeff) for bc in dirichletBCs: bc.ufl_value = replace(bc.ufl_value, coeff) if patch is not None: patch = [a if not isinstance(a, Expr) else replace(a,coeff) for a in patch] phi = form.arguments()[0] dimRange = phi.ufl_shape[0] u = form.arguments()[1] du = Grad(u) d2u = Grad(du) ubar = Coefficient(u.ufl_function_space()) dubar = Grad(ubar) d2ubar = Grad(dubar) dimDomain = u.ufl_shape[0] x = SpatialCoordinate(form.ufl_cell()) try: field = u.ufl_function_space().field except AttributeError: field = "double" # if exact solution is passed in subtract a(u,.) from the form if "exact" in kwargs: b = replace(form, {u: as_vector(kwargs["exact"])} ) form = form - b dform = apply_derivatives(derivative(action(form, ubar), ubar, u)) source, flux, boundarySource = splitUFLForm(form) linSource, linFlux, linBoundarySource = splitUFLForm(dform) fluxDivergence, _, _ = splitUFLForm(inner(source.as_ufl() - div(flux.as_ufl()), phi) * dx(0)) # split linNVSource off linSource # linSources = splitUFL2(u, du, d2u, linSource) # linNVSource = linSources[2] # linSource = linSources[0] + linSources[1] if patch is not None: model = ConservationLawModel(dimDomain, dimRange, u, modelSignature(form,*patch,*args)) else: model = ConservationLawModel(dimDomain, dimRange, u, modelSignature(form,None,*args)) model._replaceCoeff = coeff model.hasNeumanBoundary = not boundarySource.is_zero() #expandform = expand_indices(expand_derivatives(expand_compounds(equation.lhs))) #if expandform == adjoint(expandform): # model.symmetric = 'true' model.field = field dirichletBCs = [arg for arg in args if isinstance(arg, DirichletBC)] # deprecated # if "dirichlet" in kwargs: # dirichletBCs += [DirichletBC(u.ufl_function_space(), as_vector(value), bndId) for bndId, value in kwargs["dirichlet"].items()] uflCoefficients = set(form.coefficients()) for bc in dirichletBCs: _, c = extract_arguments_and_coefficients(bc.ufl_value) uflCoefficients |= set(c) if patch is not None: for a in patch: if isinstance(a, Expr): _, c = extract_arguments_and_coefficients(a) uflCoefficients |= set(c) constants = dict() coefficients = dict() for coefficient in uflCoefficients: try: name = getattr(coefficient, "name") except AttributeError: name = str(coefficient) if coefficient.is_cellwise_constant(): try: parameter = getattr(coefficient, "parameter") except AttributeError: parameter = None if len(coefficient.ufl_shape) == 0: constants[coefficient] = model.addConstant('double', name=name, parameter=parameter) elif len(coefficient.ufl_shape) == 1: constants[coefficient] = model.addConstant('Dune::FieldVector< double, ' + str(coefficient.ufl_shape[0]) + ' >', name=name, parameter=parameter) else: Exception('Currently, only scalars and vectors are supported as constants') else: shape = coefficient.ufl_shape[0] try: coefficients[coefficient] = model.addCoefficient( shape, coefficient.cppTypeName, name=name, field=coefficient.ufl_function_space().field) except AttributeError: coefficients[coefficient] = model.addCoefficient( shape, coefficient.cppTypeName, name=name) model.coefficients = coefficients model.constants = constants tempVars = kwargs.get("tempVars", True) predefined = {u: model.arg_u, du: model.arg_du, d2u: model.arg_d2u} predefined[x] = UnformattedExpression('auto', 'entity().geometry().global( Dune::Fem::coordinate( ' + model.arg_x.name + ' ) )') model.predefineCoefficients(predefined,'x') model.source = generateCode(predefined, source, tempVars=tempVars) model.flux = generateCode(predefined, flux, tempVars=tempVars) predefined.update({ubar: model.arg_ubar, dubar: model.arg_dubar, d2ubar: model.arg_d2ubar}) model.linSource = generateCode(predefined, linSource, tempVars=tempVars) model.linFlux = generateCode(predefined, linFlux, tempVars=tempVars) # model.linNVSource = generateCode({u: arg, du: darg, d2u: d2arg, ubar: argbar, dubar: dargbar, d2ubar: d2argbar}, linNVSource, model.coefficients, tempVars) predefined = {u: model.arg_u} predefined[x] = UnformattedExpression('auto', 'entity().geometry().global( Dune::Fem::coordinate( ' + model.arg_x.name + ' ) )') model.predefineCoefficients(predefined,'x') model.alpha = generateCode(predefined, boundarySource, tempVars=tempVars) predefined.update({ubar: model.arg_ubar}) model.linAlpha = generateCode(predefined, linBoundarySource, tempVars=tempVars) predefined = {u: model.arg_u, du: model.arg_du, d2u: model.arg_d2u} predefined[x] = UnformattedExpression('auto', 'entity().geometry().global( Dune::Fem::coordinate( ' + model.arg_x.name + ' ) )') model.predefineCoefficients(predefined,'x') model.fluxDivergence = generateCode(predefined, fluxDivergence, tempVars=tempVars) if dirichletBCs: model.hasDirichletBoundary = True predefined = {} predefined[x] = UnformattedExpression('auto', 'entity().geometry().global( Dune::Fem::coordinate( ' + model.arg_x.name + ' ) )') model.predefineCoefficients(predefined,'x') maxId = 0 codeDomains = [] bySubDomain = dict() neuman = [] for bc in dirichletBCs: if bc.subDomain in bySubDomain: raise Exception('Multiply defined Dirichlet boundary for subdomain ' + str(bc.subDomain)) if not isinstance(bc.functionSpace, (FunctionSpace, FiniteElementBase)): raise Exception('Function space must either be a ufl.FunctionSpace or a ufl.FiniteElement') if isinstance(bc.functionSpace, FunctionSpace) and (bc.functionSpace != u.ufl_function_space()): raise Exception('Space of trial function and dirichlet boundary function must be the same - note that boundary conditions on subspaces are not available, yet') if isinstance(bc.functionSpace, FiniteElementBase) and (bc.functionSpace != u.ufl_element()): raise Exception('Cannot handle boundary conditions on subspaces, yet') if isinstance(bc.value, list): neuman = [i for i, x in enumerate(bc.value) if x == None] else: neuman = [] value = ExprTensor(u.ufl_shape) for key in value.keys(): value[key] = Indexed(bc.ufl_value, MultiIndex(tuple(FixedIndex(k) for k in key))) if isinstance(bc.subDomain,int): bySubDomain[bc.subDomain] = value,neuman maxId = max(maxId, bc.subDomain) else: domain = ExprTensor(()) for key in domain.keys(): domain[key] = Indexed(bc.subDomain, MultiIndex(tuple(FixedIndex(k) for k in key))) codeDomains.append( (value,neuman,domain) ) defaultCode = [] defaultCode.append(Declaration(Variable('int', 'domainId'))) defaultCode.append(Declaration(Variable('auto', 'tmp0'), initializer=UnformattedExpression('auto','intersection.geometry().center()'))) for i,v in enumerate(codeDomains): block = Block() defaultCode.append( generateDirichletDomainCode(predefined, v[2], tempVars=tempVars)) defaultCode.append('if (domainId)') block = UnformattedBlock() block.append('std::fill( dirichletComponent.begin(), dirichletComponent.end(), ' + str(maxId+i+1) + ' );') if len(v[1])>0: [block.append('dirichletComponent[' + str(c) + '] = 0;') for c in v[1]] block.append('return true;') defaultCode.append(block) defaultCode.append(return_(False)) bndId = Variable('const int', 'bndId') getBndId = UnformattedExpression('int', 'BoundaryIdProviderType::boundaryId( ' + model.arg_i.name + ' )') switch = SwitchStatement(bndId, default=defaultCode) for i,v in bySubDomain.items(): code = [] if len(v[1])>0: [code.append('dirichletComponent[' + str(c) + '] = 0;') for c in v[1]] code.append(return_(True)) switch.append(i, code) model.isDirichletIntersection = [Declaration(bndId, initializer=getBndId), UnformattedBlock('std::fill( dirichletComponent.begin(), dirichletComponent.end(), ' + bndId.name + ' );'), switch ] switch = SwitchStatement(model.arg_bndId, default=assign(model.arg_r, construct("RRangeType", 0))) for i, v in bySubDomain.items(): switch.append(i, generateDirichletCode(predefined, v[0], tempVars=tempVars)) for i,v in enumerate(codeDomains): switch.append(i+maxId+1, generateDirichletCode(predefined, v[0], tempVars=tempVars)) model.dirichlet = [switch] return model
def test_mass_action(form, cell, order): degrees = numpy.arange(4, 10) flops = [count_flops(action(form(cell, int(degree)))) for degree in degrees] rates = numpy.diff(numpy.log(flops)) / numpy.diff(numpy.log(degrees + 1)) assert (rates < order).all()