def _analyze_form(form, parameters): "Analyze form, returning form data." # Check that form is not empty ffc_assert(not form.empty(), "Form (%s) seems to be zero: cannot compile it." % str(form)) # Compute form metadata if parameters["representation"] == "uflacs": # Temporary workaround to let uflacs have a different preprocessing pipeline # than the legacy representations quadrature and tensor. This approach imposes # a limitation that e.g. uflacs and tensor representation cannot be mixed in the same form. from ufl.classes import Jacobian form_data = compute_form_data(form, do_apply_function_pullbacks=True, do_apply_integral_scaling=True, do_apply_geometry_lowering=True, preserve_geometry_types=(Jacobian,), do_apply_restrictions=True, ) else: form_data = compute_form_data(form) info("") info(str(form_data)) # Attach integral meta data _attach_integral_metadata(form_data, parameters) return form_data
def _analyze_form(form, parameters): "Analyze form, returning form data." # Check that form is not empty if form.empty(): error("Form (%s) seems to be zero: cannot compile it." % str(form)) # Hack to override representation with environment variable forced_r = os.environ.get("FFC_FORCE_REPRESENTATION") if forced_r: warning( "representation: forced by $FFC_FORCE_REPRESENTATION to '%s'" % forced_r) r = forced_r else: # Check representation parameters to figure out how to # preprocess r = _extract_representation_family(form, parameters) debug("Preprocessing form using '%s' representation family." % r) # Compute form metadata if r == "uflacs": # Temporary workaround to let uflacs have a different # preprocessing pipeline than the legacy quadrature # representation. This approach imposes a limitation that, # e.g. uflacs and qudrature, representations cannot be mixed # in the same form. from ufl.classes import Jacobian form_data = compute_form_data(form, do_apply_function_pullbacks=True, do_apply_integral_scaling=True, do_apply_geometry_lowering=True, preserve_geometry_types=(Jacobian, ), do_apply_restrictions=True) elif r == "tsfc": try: # TSFC provides compute_form_data wrapper using correct # kwargs from tsfc.ufl_utils import compute_form_data as tsfc_compute_form_data except ImportError: error( "Failed to import tsfc.ufl_utils.compute_form_data when asked " "for tsfc representation.") form_data = tsfc_compute_form_data(form) elif r == "quadrature": # quadrature representation form_data = compute_form_data(form) else: error("Unexpected representation family '%s' for form preprocessing." % r) info("") info(str(form_data)) # Attach integral meta data _attach_integral_metadata(form_data, r, parameters) _validate_representation_choice(form_data, r) return form_data
def assertEqualBySampling(actual, expected): ad = compute_form_data(actual * dx) a = ad.preprocessed_form.integrals_by_type("cell")[0].integrand() bd = compute_form_data(expected * dx) b = bd.preprocessed_form.integrals_by_type("cell")[0].integrand() assert ([ ad.function_replace_map[ac] for ac in ad.reduced_coefficients ] == [bd.function_replace_map[bc] for bc in bd.reduced_coefficients]) n = ad.num_coefficients def make_value(c): if isinstance(c, Coefficient): z = 0.3 m = c.count() else: z = 0.7 m = c.number() if c.ufl_shape == (): return z * (0.1 + 0.9 * m / n) elif len(c.ufl_shape) == 1: return tuple( (z * (j + 0.1 + 0.9 * m / n) for j in range(c.ufl_shape[0]))) else: raise NotImplementedError( "Tensor valued expressions not supported here.") amapping = dict((c, make_value(c)) for c in chain( ad.original_form.coefficients(), ad.original_form.arguments())) bmapping = dict((c, make_value(c)) for c in chain( bd.original_form.coefficients(), bd.original_form.arguments())) acell = actual.ufl_domain().ufl_cell() bcell = expected.ufl_domain().ufl_cell() assert acell == bcell if acell.geometric_dimension() == 1: x = (0.3, ) elif acell.geometric_dimension() == 2: x = (0.3, 0.4) elif acell.geometric_dimension() == 3: x = (0.3, 0.4, 0.5) av = a(x, amapping) bv = b(x, bmapping) if not av == bv: print("Tried to sample expressions to compare but failed:") print() print((str(a))) print(av) print() print((str(b))) print(bv) print() assert av == bv
def _analyze_form(form, parameters): "Analyze form, returning form data." # Check that form is not empty if form.empty(): error("Form (%s) seems to be zero: cannot compile it." % str(form)) # Hack to override representation with environment variable forced_r = os.environ.get("FFC_FORCE_REPRESENTATION") if forced_r: warning("representation: forced by $FFC_FORCE_REPRESENTATION to '%s'" % forced_r) r = forced_r else: # Check representation parameters to figure out how to # preprocess r = _extract_representation_family(form, parameters) debug("Preprocessing form using '%s' representation family." % r) # Compute form metadata if r == "uflacs": # Temporary workaround to let uflacs have a different # preprocessing pipeline than the legacy quadrature # representation. This approach imposes a limitation that, # e.g. uflacs and qudrature, representations cannot be mixed # in the same form. from ufl.classes import Jacobian form_data = compute_form_data(form, do_apply_function_pullbacks=True, do_apply_integral_scaling=True, do_apply_geometry_lowering=True, preserve_geometry_types=(Jacobian,), do_apply_restrictions=True) elif r == "tsfc": try: # TSFC provides compute_form_data wrapper using correct # kwargs from tsfc.ufl_utils import compute_form_data as tsfc_compute_form_data except ImportError: error("Failed to import tsfc.ufl_utils.compute_form_data when asked " "for tsfc representation.") form_data = tsfc_compute_form_data(form) elif r == "quadrature": # quadrature representation form_data = compute_form_data(form) else: error("Unexpected representation family '%s' for form preprocessing." % r) info("") info(str(form_data)) # Attach integral meta data _attach_integral_metadata(form_data, r, parameters) _validate_representation_choice(form_data, r) return form_data
def assertEqualBySampling(actual, expected): ad = compute_form_data(actual*dx) a = ad.preprocessed_form.integrals_by_type("cell")[0].integrand() bd = compute_form_data(expected*dx) b = bd.preprocessed_form.integrals_by_type("cell")[0].integrand() assert ([ad.function_replace_map[ac] for ac in ad.reduced_coefficients] == [bd.function_replace_map[bc] for bc in bd.reduced_coefficients]) n = ad.num_coefficients def make_value(c): if isinstance(c, Coefficient): z = 0.3 m = c.count() else: z = 0.7 m = c.number() if c.ufl_shape == (): return z * (0.1 + 0.9 * m / n) elif len(c.ufl_shape) == 1: return tuple((z * (j + 0.1 + 0.9 * m / n) for j in range(c.ufl_shape[0]))) else: raise NotImplementedError("Tensor valued expressions not supported here.") amapping = dict((c, make_value(c)) for c in chain(ad.original_form.coefficients(), ad.original_form.arguments())) bmapping = dict((c, make_value(c)) for c in chain(bd.original_form.coefficients(), bd.original_form.arguments())) acell = actual.ufl_domain().ufl_cell() bcell = expected.ufl_domain().ufl_cell() assert acell == bcell if acell.geometric_dimension() == 1: x = (0.3,) elif acell.geometric_dimension() == 2: x = (0.3, 0.4) elif acell.geometric_dimension() == 3: x = (0.3, 0.4, 0.5) av = a(x, amapping) bv = b(x, bmapping) if not av == bv: print("Tried to sample expressions to compare but failed:") print() print((str(a))) print(av) print() print((str(b))) print(bv) print() assert av == bv
def test_segregated_derivative_of_convection(self): cell = tetrahedron V = FiniteElement("CG", cell, 1) W = VectorElement("CG", cell, 1) u = Coefficient(W) v = Coefficient(W) du = TrialFunction(V) dv = TestFunction(V) L = dot(dot(u, nabla_grad(u)), v) Lv = {} Lvu = {} for i in range(cell.geometric_dimension()): Lv[i] = derivative(L, v[i], dv) for j in range(cell.geometric_dimension()): Lvu[i, j] = derivative(Lv[i], u[j], du) for i in range(cell.geometric_dimension()): for j in range(cell.geometric_dimension()): form = Lvu[i, j]*dx fd = compute_form_data(form) pf = fd.preprocessed_form a = expand_indices(pf) # print (i,j), str(a) k = Index() for i in range(cell.geometric_dimension()): for j in range(cell.geometric_dimension()): actual = Lvu[i, j] expected = du*u[i].dx(j)*dv + u[k]*du.dx(k)*dv assertEqualBySampling(actual, expected)
def test_vector_coefficient_derivatives_of_product(self): V = VectorElement("Lagrange", triangle, 1) VV = TensorElement("Lagrange", triangle, 1) dv = TestFunction(V) df = Coefficient(VV, count=0) g = Coefficient(V, count=1) dg = Coefficient(VV, count=2) f = Coefficient(V, count=3) u = Coefficient(V, count=4) cd = {f: df, g: dg} integrand = f[i]*g[i] i0, i1, i2, i3, i4 = [Index(count=c) for c in range(5)] expected = as_tensor(df[i2, i1]*dv[i1], (i2,))[i0]*g[i0] +\ f[i0]*as_tensor(dg[i4, i3]*dv[i3], (i4,))[i0] F = integrand*dx J = derivative(F, u, dv, cd) fd = compute_form_data(J) actual = fd.preprocessed_form.integrals()[0].integrand() # Tricky case! These are equal in representation except # that the outermost sum/indexsum are swapped. # Sampling the expressions instead of comparing representations. x = (0, 0) funcs = {dv: (13, 14), f: (1, 2), g: (3, 4), df: ((5, 6), (7, 8)), dg: ((9, 10), (11, 12))} self.assertEqual(replace(actual, fd.function_replace_map)(x, funcs), expected(x, funcs))
def __init__(self, form, name, parameters): """A wrapper object for one or more FFC kernels compiled from a given :class:`~Form`. :arg form: the :class:`~Form` from which to compile the kernels. :arg name: a prefix to be applied to the compiled kernel names. This is primarily useful for debugging. :arg parameters: a dict of parameters to pass to the form compiler. """ if self._initialized: return incl = PreprocessNode('#include "firedrake_geometry.h"\n') inc = [path.dirname(__file__)] try: ffc_tree = ffc_compile_form(form, prefix=name, parameters=parameters) kernels = [] # need compute_form_data here to get preproc form integrals fd = compute_form_data(form) elements = fd.elements needs_orientations = self._needs_orientations(elements) for it, kernel in zip(fd.preprocessed_form.integrals(), ffc_tree): # Set optimization options opts = {} if it.integral_type() not in ['cell'] else default_parameters["coffee"] kernels.append((Kernel(Root([incl, kernel]), '%s_%s_integral_0_%s' % (name, it.integral_type(), it.subdomain_id()), opts, inc), needs_orientations)) self.kernels = tuple(kernels) self._empty = False except EmptyIntegrandError: # FFC noticed that the integrand was zero and simplified # it, catch this here and set a flag telling us to ignore # the kernel when returning it in compile_form self._empty = True self._initialized = True
def ufl2unicode(expression): "Generate Unicode string for a UFL expression or form." if isinstance(expression, Form): form_data = compute_form_data(expression) preprocessed_form = form_data.preprocessed_form return form2unicode(preprocessed_form, form_data) else: return expression2unicode(expression)
def valid_forms(forms_list): forms = [] form_datas = [] for f in forms_list: fd = None try: fd = compute_form_data(f) except: fd = None if fd is not None: forms.append(f) form_datas.append(fd) return forms, form_datas
def testFormData(): element = FiniteElement("Lagrange", "tetrahedron", 3) v = TestFunction(element) u = TrialFunction(element) a = v * u * dx form_data = compute_form_data(a) form_data_pickle = pickle.dumps(form_data, p) form_data_restore = pickle.loads(form_data_pickle) assert(str(form_data) == str(form_data_restore))
def testFormData(): element = FiniteElement("Lagrange", "tetrahedron", 3) v = TestFunction(element) u = TrialFunction(element) a = v * u * dx form_data = compute_form_data(a) form_data_pickle = pickle.dumps(form_data, p) form_data_restore = pickle.loads(form_data_pickle) assert (str(form_data) == str(form_data_restore))
def test_index_simplification_handles_repeated_indices(self): mesh = Mesh(VectorElement("P", quadrilateral, 1)) V = FunctionSpace(mesh, TensorElement("DQ", quadrilateral, 0)) K = JacobianInverse(mesh) G = outer(Identity(2), Identity(2)) i, j, k, l, m, n = indices(6) A = as_tensor(K[m, i] * K[n, j] * G[i, j, k, l], (m, n, k, l)) i, j = indices(2) # Can't use A[i, i, j, j] because UFL automagically index-sums # repeated indices in the __getitem__ call. Adiag = Indexed(A, MultiIndex((i, i, j, j))) A = as_tensor(Adiag, (i, j)) v = TestFunction(V) f = inner(A, v)*dx fd = compute_form_data(f, do_apply_geometry_lowering=True) integral, = fd.preprocessed_form.integrals() assert integral.integrand().ufl_free_indices == ()
def _analyze_form(form, parameters): "Analyze form, returning form data." # Check that form is not empty ffc_assert(not form.empty(), "Form (%s) seems to be zero: cannot compile it." % str(form)) # Compute form metadata form_data = compute_form_data(form) info("") info(str(form_data)) # Attach integral meta data _attach_integral_metadata(form_data, parameters) return form_data
def test_everywhere_integrals_with_backwards_compatibility(): D = Mesh(triangle) V = FunctionSpace(D, FiniteElement("CG", triangle, 1)) f = Coefficient(V) a = f * dx ida, = compute_form_data(a).integral_data # Check some integral data assert ida.integral_type == "cell" assert ida.subdomain_id == "otherwise" assert ida.metadata == {} # Integrands are not equal because of renumbering itg1 = ida.integrals[0].integrand() itg2 = a.integrals()[0].integrand() assert type(itg1) == type(itg2) assert itg1.ufl_element() == itg2.ufl_element()
def test_coefficient_derivatives(self): V = FiniteElement("Lagrange", triangle, 1) dv = TestFunction(V) f = Coefficient(V, count=0) g = Coefficient(V, count=1) df = Coefficient(V, count=2) dg = Coefficient(V, count=3) u = Coefficient(V, count=4) cd = {f: df, g: dg} integrand = inner(f, g) expected = (df*dv)*g + f*(dg*dv) F = integrand*dx J = derivative(F, u, dv, cd) fd = compute_form_data(J) actual = fd.preprocessed_form.integrals()[0].integrand() assert (actual*dx).signature() == (expected*dx).signature() self.assertEqual(replace(actual, fd.function_replace_map), expected)
def __init__(self, form, name, parameters): """A wrapper object for one or more FFC kernels compiled from a given :class:`~ufl.classes.Form`. :arg form: the :class:`~ufl.classes.Form` from which to compile the kernels. :arg name: a prefix to be applied to the compiled kernel names. This is primarily useful for debugging. :arg parameters: a dict of parameters to pass to the form compiler. """ if self._initialized: return incl = [PreprocessNode('#include "firedrake_geometry.h"\n')] inc = [path.dirname(__file__)] try: ffc_tree = ffc_compile_form(form, prefix=name, parameters=parameters) if len(ffc_tree) == 0: raise EmptyIntegrandError kernels = [] # need compute_form_data here to get preproc form integrals fd = compute_form_data(form) elements = fd.unique_elements needs_orientations = self._needs_orientations(elements) for it, kernel in zip(fd.preprocessed_form.integrals(), ffc_tree): # Set optimization options opts = default_parameters["coffee"] _kernel = kernel if not parameters.get( "assemble_inverse", False) else _inverse(kernel) kernels.append((Kernel( Root(incl + [_kernel]), '%s_%s_integral_0_%s' % (name, it.integral_type(), it.subdomain_id()), opts, inc), needs_orientations)) self.kernels = tuple(kernels) self._empty = False except EmptyIntegrandError: # FFC noticed that the integrand was zero and simplified # it, catch this here and set a flag telling us to ignore # the kernel when returning it in compile_form self._empty = True self._initialized = True
def test_vector_coefficient_derivatives(self): V = VectorElement("Lagrange", triangle, 1) VV = TensorElement("Lagrange", triangle, 1) dv = TestFunction(V) df = Coefficient(VV, count=0) g = Coefficient(V, count=1) f = Coefficient(V, count=2) u = Coefficient(V, count=3) cd = {f: df} integrand = inner(f, g) i0, i1, i2, i3, i4 = [Index(count=c) for c in range(5)] expected = as_tensor(df[i2, i1]*dv[i1], (i2,))[i0]*g[i0] F = integrand*dx J = derivative(F, u, dv, cd) fd = compute_form_data(J) actual = fd.preprocessed_form.integrals()[0].integrand() assert (actual*dx).signature() == (expected*dx).signature()
def testHyperElasticity(self): cell = interval element = FiniteElement("CG", cell, 2) w = Coefficient(element) v = TestFunction(element) u = TrialFunction(element) b = Constant(cell) K = Constant(cell) dw = w.dx(0) dv = v.dx(0) du = v.dx(0) E = dw + dw**2 / 2 E = variable(E) Q = b*E**2 psi = K*(exp(Q)-1) f = psi*dx F = derivative(f, w, v) J = derivative(F, w, u) form_data_f = compute_form_data(f) form_data_F = compute_form_data(F) form_data_J = compute_form_data(J) f = form_data_f.preprocessed_form F = form_data_F.preprocessed_form J = form_data_J.preprocessed_form f_expression = strip_variables(f.integrals_by_type("cell")[0].integrand()) F_expression = strip_variables(F.integrals_by_type("cell")[0].integrand()) J_expression = strip_variables(J.integrals_by_type("cell")[0].integrand()) # classes = set(c.__class__ for c in post_traversal(f_expression)) Kv = .2 bv = .3 dw = .5 dv = .7 du = .11 E = dw + dw**2 / 2. Q = bv*E**2 expQ = float(exp(Q)) psi = Kv*(expQ-1) fv = psi Fv = 2*Kv*bv*E*(1+dw)*expQ*dv Jv = 2*Kv*bv*expQ*dv*du*(E + (1+dw)**2*(2*bv*E**2 + 1)) def Nv(x, derivatives): assert derivatives == (0,) return dv def Nu(x, derivatives): assert derivatives == (0,) return du def Nw(x, derivatives): assert derivatives == (0,) return dw mapping = {K: Kv, b: bv, w: Nw} fv2 = f_expression((0,), mapping) self.assertAlmostEqual(fv, fv2) v, = form_data_F.original_form.arguments() mapping = {K: Kv, b: bv, v: Nv, w: Nw} Fv2 = F_expression((0,), mapping) self.assertAlmostEqual(Fv, Fv2) v, u = form_data_J.original_form.arguments() mapping = {K: Kv, b: bv, v: Nv, u: Nu, w: Nw} Jv2 = J_expression((0,), mapping) self.assertAlmostEqual(Jv, Jv2)
def compile_form(form, name, parameters=None): """Compile a form using FFC. :arg form: the :class:`ufl.Form` to compile. :arg name: a prefix for the generated kernel functions. :arg parameters: optional dict of parameters to pass to the form compiler. If not provided, parameters are read from the :data:`form_compiler` slot of the Firedrake :data:`~.parameters` dictionary (which see). Returns a tuple of tuples of (index, integral type, subdomain id, coordinates, coefficients, needs_orientations, :class:`Kernels <pyop2.op2.Kernel>`). ``needs_orientations`` indicates whether the form requires cell orientation information (for correctly pulling back to reference elements on embedded manifolds). The coordinates are extracted from the UFL :class:`~ufl.domain.Domain` of the integral. """ # Check that we get a Form if not isinstance(form, Form): raise RuntimeError("Unable to convert object to a UFL form: %s" % repr(form)) if parameters is None: parameters = default_parameters["form_compiler"] else: # Override defaults with user-specified values _ = parameters parameters = default_parameters["form_compiler"].copy() parameters.update(_) # We stash the compiled kernels on the form so we don't have to recompile # if we assemble the same form again with the same optimisations if hasattr(form, "_kernels"): # Save both kernels and FFC params so we can tell if this # cached version is valid (the FFC parameters might have changed) kernels, params = form._kernels if kernels[0][-1]._opts == default_parameters["coffee"] and \ kernels[0][-1].name.startswith(name) and \ params == parameters: return kernels # need compute_form_data since we use preproc. form integrals later fd = compute_form_data(form) # If there is no mixed element involved, return the kernels FFC produces if all(isinstance(e, (FiniteElement, VectorElement)) for e in fd.unique_sub_elements): kernels = [((0, 0), it.integral_type(), it.subdomain_id(), it.domain().data().coordinates, fd.preprocessed_form.coefficients(), needs_orientations, kernel) for it, (kernel, needs_orientations) in zip(fd.preprocessed_form.integrals(), FFCKernel(form, name, parameters).kernels)] form._kernels = (kernels, parameters) return kernels # Otherwise pre-split the form into mixed blocks before calling FFC kernels = [] for forms in FormSplitter().split(form): for (i, j), f in forms: ffc_kernel = FFCKernel(f, name + str(i) + str(j), parameters) # FFC noticed the integrand was zero, so don't bother # using this kernel (it's invalid anyway) if ffc_kernel._empty: continue ((kernel, needs_orientations), ) = ffc_kernel.kernels # need compute_form_data here to get preproc integrals fd = compute_form_data(f) it = fd.preprocessed_form.integrals()[0] kernels.append(((i, j), it.integral_type(), it.subdomain_id(), it.domain().data().coordinates, fd.preprocessed_form.coefficients(), needs_orientations, kernel)) form._kernels = (kernels, parameters) return kernels
def assertEqualAfterPreprocessing(self, a, b): a2 = compute_form_data(a*dx).preprocessed_form b2 = compute_form_data(b*dx).preprocessed_form self.assertEqual(a2, b2)
def testHyperElasticity(self): cell = interval element = FiniteElement("CG", cell, 2) w = Coefficient(element) v = TestFunction(element) u = TrialFunction(element) b = Constant(cell) K = Constant(cell) dw = w.dx(0) dv = v.dx(0) du = v.dx(0) E = dw + dw**2 / 2 E = variable(E) Q = b*E**2 psi = K*(exp(Q)-1) f = psi*dx F = derivative(f, w, v) J = derivative(F, w, u) form_data_f = compute_form_data(f) form_data_F = compute_form_data(F) form_data_J = compute_form_data(J) f = form_data_f.preprocessed_form F = form_data_F.preprocessed_form J = form_data_J.preprocessed_form f_expression = strip_variables(f.integrals_by_type("cell")[0].integrand()) F_expression = strip_variables(F.integrals_by_type("cell")[0].integrand()) J_expression = strip_variables(J.integrals_by_type("cell")[0].integrand()) # classes = set(c.__class__ for c in post_traversal(f_expression)) Kv = .2 bv = .3 dw = .5 dv = .7 du = .11 E = dw + dw**2 / 2. Q = bv*E**2 expQ = float(exp(Q)) psi = Kv*(expQ-1) fv = psi Fv = 2*Kv*bv*E*(1+dw)*expQ*dv Jv = 2*Kv*bv*expQ*dv*du*(E + (1+dw)**2*(2*bv*E**2 + 1)) def Nv(x, derivatives): assert derivatives == (0,) return dv def Nu(x, derivatives): assert derivatives == (0,) return du def Nw(x, derivatives): assert derivatives == (0,) return dw w, b, K = form_data_f.original_form.coefficients() mapping = {K: Kv, b: bv, w: Nw} fv2 = f_expression((0,), mapping) self.assertAlmostEqual(fv, fv2) w, b, K = form_data_F.original_form.coefficients() v, = form_data_F.original_form.arguments() mapping = {K: Kv, b: bv, v: Nv, w: Nw} Fv2 = F_expression((0,), mapping) self.assertAlmostEqual(Fv, Fv2) w, b, K = form_data_J.original_form.coefficients() v, u = form_data_J.original_form.arguments() mapping = {K: Kv, b: bv, v: Nv, u: Nu, w: Nw} Jv2 = J_expression((0,), mapping) self.assertAlmostEqual(Jv, Jv2)
def compile_form(form, name, parameters=None, inverse=False): """Compile a form using FFC. :arg form: the :class:`~ufl.classes.Form` to compile. :arg name: a prefix for the generated kernel functions. :arg parameters: optional dict of parameters to pass to the form compiler. If not provided, parameters are read from the ``form_compiler`` slot of the Firedrake :data:`~.parameters` dictionary (which see). :arg inverse: If True then assemble the inverse of the local tensor. Returns a tuple of tuples of (index, integral type, subdomain id, coordinates, coefficients, needs_orientations, :class:`Kernels <pyop2.op2.Kernel>`). ``needs_orientations`` indicates whether the form requires cell orientation information (for correctly pulling back to reference elements on embedded manifolds). The coordinates are extracted from the domain of the integral (a :func:`~.Mesh`) """ # Check that we get a Form if not isinstance(form, Form): raise RuntimeError("Unable to convert object to a UFL form: %s" % repr(form)) if parameters is None: parameters = default_parameters["form_compiler"].copy() else: # Override defaults with user-specified values _ = parameters parameters = default_parameters["form_compiler"].copy() parameters.update(_) # We stash the compiled kernels on the form so we don't have to recompile # if we assemble the same form again with the same optimisations if "firedrake_kernels" in form._cache: # Save both kernels and FFC params so we can tell if this # cached version is valid (the FFC parameters might have changed) kernels, params = form._cache["firedrake_kernels"] if kernels[0][-1]._opts == default_parameters["coffee"] and \ kernels[0][-1].name.startswith(name) and \ params == parameters: return kernels # need compute_form_data since we use preproc. form integrals later fd = compute_form_data(form) # If there is no mixed element involved, return the kernels FFC produces # Note: using type rather than isinstance because UFL's VectorElement, # TensorElement and OPVectorElement all inherit from MixedElement if not any(type(e) is MixedElement for e in fd.unique_sub_elements): kernels = [((0, 0), it.integral_type(), it.ufl_domain(), form.subdomain_data()[it.ufl_domain()][it.integral_type()], it.subdomain_id(), fd.preprocessed_form.coefficients(), needs_orientations, kernel) for it, (kernel, needs_orientations ) in zip(fd.preprocessed_form.integrals(), FFCKernel(form, name, parameters).kernels) ] form._cache["firedrake_kernels"] = (kernels, parameters) return kernels # Otherwise pre-split the form into mixed blocks before calling FFC kernels = [] for forms in FormSplitter().split(form): for (i, j), f in forms: ffc_kernel = FFCKernel(f, name + str(i) + str(j), parameters) # FFC noticed the integrand was zero, so don't bother # using this kernel (it's invalid anyway) if ffc_kernel._empty: continue ((kernel, needs_orientations), ) = ffc_kernel.kernels # need compute_form_data here to get preproc integrals fd = compute_form_data(f) it = fd.preprocessed_form.integrals()[0] kernels.append(((i, j), it.integral_type(), it.ufl_domain(), it.subdomain_data(), it.subdomain_id(), fd.preprocessed_form.coefficients(), needs_orientations, kernel)) form._cache["firedrake_kernels"] = (kernels, parameters) return kernels
def _test_grad_div_curl_properties(self, cell): d = cell.geometric_dimension() S = FiniteElement("CG", cell, 1) V = VectorElement("CG", cell, 1) T = TensorElement("CG", cell, 1) cs = Constant(cell) cv = VectorConstant(cell) ct = TensorConstant(cell) s = Coefficient(S) v = Coefficient(V) t = Coefficient(T) def eval_s(x, derivatives=()): return sum(derivatives) def eval_v(x, derivatives=()): return tuple(float(k)+sum(derivatives) for k in range(d)) def eval_t(x, derivatives=()): return tuple(tuple(float(i*j)+sum(derivatives) for i in range(d)) for j in range(d)) mapping = {cs: eval_s, s: eval_s, cv: eval_v, v: eval_v, ct: eval_t, t: eval_t, } x = tuple(1.0+float(k) for k in range(d)) assert s.ufl_shape == () assert v.ufl_shape == (d,) assert t.ufl_shape == (d, d) assert cs.ufl_shape == () assert cv.ufl_shape == (d,) assert ct.ufl_shape == (d, d) self.assertEqual(s(x, mapping=mapping), eval_s(x)) self.assertEqual(v(x, mapping=mapping), eval_v(x)) self.assertEqual(t(x, mapping=mapping), eval_t(x)) assert grad(s).ufl_shape == (d,) assert grad(v).ufl_shape == (d, d) assert grad(t).ufl_shape == (d, d, d) assert grad(cs).ufl_shape == (d,) assert grad(cv).ufl_shape == (d, d) assert grad(ct).ufl_shape == (d, d, d) self.assertEqual(grad(s)[0](x, mapping=mapping), eval_s(x, (0,))) self.assertEqual(grad(v)[d-1, d-1](x, mapping=mapping), eval_v(x, derivatives=(d-1,))[d-1]) self.assertEqual(grad(t)[d-1, d-1, d-1](x, mapping=mapping), eval_t(x, derivatives=(d-1,))[d-1][d-1]) assert div(grad(cs)).ufl_shape == () assert div(grad(cv)).ufl_shape == (d,) assert div(grad(ct)).ufl_shape == (d, d) assert s.dx(0).ufl_shape == () assert v.dx(0).ufl_shape == (d,) assert t.dx(0).ufl_shape == (d, d) assert s.dx(0 == 0).ufl_shape, () assert v.dx(0 == 0).ufl_shape, (d,) assert t.dx(0 == 0).ufl_shape, (d, d) i, j = indices(2) assert s.dx(i).ufl_shape == () assert v.dx(i).ufl_shape == (d,) assert t.dx(i).ufl_shape == (d, d) assert s.dx(i).ufl_free_indices == (i.count(),) assert v.dx(i).ufl_free_indices == (i.count(),) assert t.dx(i).ufl_free_indices == (i.count(),) self.assertEqual(s.dx(i, j).ufl_shape, ()) self.assertEqual(v.dx(i, j).ufl_shape, (d,)) self.assertEqual(t.dx(i, j).ufl_shape, (d, d)) self.assertTrue(s.dx(i, j).ufl_free_indices == tuple(sorted([i.count(), j.count()]))) self.assertTrue(v.dx(i, j).ufl_free_indices == tuple(sorted([i.count(), j.count()]))) self.assertTrue(t.dx(i, j).ufl_free_indices == tuple(sorted([i.count(), j.count()]))) a0 = s.dx(0)*dx a1 = s.dx(0)**2*dx a2 = v.dx(0)**2*dx a3 = t.dx(0)**2*dx a4 = inner(grad(s), grad(s))*dx a5 = inner(grad(v), grad(v))*dx a6 = inner(grad(t), grad(t))*dx a7 = inner(div(grad(s)), s)*dx a8 = inner(div(grad(v)), v)*dx a9 = inner(div(grad(t)), t)*dx fd0 = compute_form_data(a0) fd1 = compute_form_data(a1) fd2 = compute_form_data(a2) fd3 = compute_form_data(a3) fd4 = compute_form_data(a4) fd5 = compute_form_data(a5) fd6 = compute_form_data(a6) fd7 = compute_form_data(a7) fd8 = compute_form_data(a8) fd9 = compute_form_data(a9)
def assertEqualAfterPreprocessing(self, a, b): a2 = compute_form_data(a * dx).preprocessed_form b2 = compute_form_data(b * dx).preprocessed_form self.assertEqual(a2, b2)