diff --git a/python/dune/perftool/compile.py b/python/dune/perftool/compile.py index 499dcdebde8e4fc6eb20e5ff3a4c4c45639ac337..dfcaeba86bdd21bff5b956aa330f016243e2d7c6 100644 --- a/python/dune/perftool/compile.py +++ b/python/dune/perftool/compile.py @@ -36,7 +36,15 @@ def read_ufl(uflfile): formdatas = [] forms = data.forms for index, form in enumerate(forms): - formdatas.append(compute_form_data(form)) + from dune.perftool.ufl.execution import (JacobianDeterminant, + JacobianInverse) + formdatas.append(compute_form_data(form, + do_apply_function_pullbacks=True, + do_apply_geometry_lowering=True, + do_apply_integral_scaling=True, + preserve_geometry_types=(JacobianDeterminant, JacobianInverse) + ) + ) forms[index] = formdatas[index].preprocessed_form # We expect at least one form diff --git a/python/dune/perftool/loopy/transformer.py b/python/dune/perftool/loopy/transformer.py index bf8fa8968ce2383558722f7f133985c962e20be6..05008cba2c986a74d761df93dfb9924078be1fbe 100644 --- a/python/dune/perftool/loopy/transformer.py +++ b/python/dune/perftool/loopy/transformer.py @@ -42,6 +42,7 @@ class UFL2LoopyVisitor(ModifiedTerminalTracker, UFL2PymbolicMapper, GeometryMapp def __call__(self, o): # Reset some state variables that are reinitialized for each accumulation term self.argshape = 0 + self.transpose_necessary = False self.redinames = () self.inames = [] self.substitution_rules = [] @@ -118,9 +119,6 @@ class UFL2LoopyVisitor(ModifiedTerminalTracker, UFL2PymbolicMapper, GeometryMapp from dune.perftool.pdelab.argument import name_accumulation_variable accumvar = name_accumulation_variable(arg_restr) - from dune.perftool.pdelab.quadrature import name_factor - factor = name_factor() - predicates = frozenset({}) # Maybe wrap this instruction into a condiditional. This mostly happens with mixed boundary conditions @@ -159,9 +157,8 @@ class UFL2LoopyVisitor(ModifiedTerminalTracker, UFL2PymbolicMapper, GeometryMapp predicates = predicates.union(['{} == {}'.format(name, self.subdomain_id)]) from dune.perftool.loopy.functions import PDELabAccumulationFunction - from pymbolic.primitives import Call, Product - expr = Product((pymbolic_expr, Variable(factor))) - expr = Call(PDELabAccumulationFunction(accumvar, len(test_ma)), tuple(a for a in accumargs) + (expr,)) + from pymbolic.primitives import Call + expr = Call(PDELabAccumulationFunction(accumvar, len(test_ma)), tuple(a for a in accumargs) + (pymbolic_expr,)) instruction(assignees=(), expression=expr, @@ -199,16 +196,19 @@ class UFL2LoopyVisitor(ModifiedTerminalTracker, UFL2PymbolicMapper, GeometryMapp # For the purpose of basis evaluation, we need to take the leaf element leaf_element = element.sub_elements()[0] + if self.grad: + raise ValueError("Gradients should have been transformed to reference gradients!!!") + # Have the issued instruction depend on the iname for this localfunction space iname = lfs_iname(leaf_element, restriction, o.number()) self.inames.append(iname) - if self.grad: - from dune.perftool.pdelab.argument import name_testfunction_gradient - return Subscript(Variable(name_testfunction_gradient(leaf_element, restriction)), (Variable(iname),)) + if self.reference_grad: + from dune.perftool.pdelab.basis import name_reference_gradient + return Subscript(Variable(name_reference_gradient(leaf_element, restriction)), (Variable(iname),)) else: - from dune.perftool.pdelab.argument import name_testfunction - return Subscript(Variable(name_testfunction(leaf_element, restriction)), (Variable(iname),)) + from dune.perftool.pdelab.basis import name_basis + return Subscript(Variable(name_basis(leaf_element, restriction)), (Variable(iname),)) def coefficient(self, o): # Do something different for trial function and coefficients from jacobian apply @@ -219,6 +219,9 @@ class UFL2LoopyVisitor(ModifiedTerminalTracker, UFL2PymbolicMapper, GeometryMapp restriction = Restriction.NEGATIVE if self.grad: + raise ValueError("Gradients should have been transformed to reference gradients!!!") + + if self.reference_grad: if o.count() == 0: from dune.perftool.pdelab.argument import name_trialfunction_gradient return Variable(name_trialfunction_gradient(o.ufl_element(), restriction, self.component)) @@ -262,11 +265,16 @@ class UFL2LoopyVisitor(ModifiedTerminalTracker, UFL2PymbolicMapper, GeometryMapp """ Although indexed is something that we want to handle in the pymbolic mapper we need to handle it here, as we have to give special treatment to indices into vector elements: they become a loop index! """ - # Set the generated index for the argument handler to have it available - self.last_index = self.call(o.ufl_operands[1]) aggr = self.call(o.ufl_operands[0]) + indices = self.call(o.ufl_operands[1]) + + if self.transpose_necessary: + assert(len(indices) == 2) + assert(self.argshape == 0) + indices = (indices[1], indices[0]) + self.transpose_necessary = False - use_indices = self.last_index[self.argshape:] + use_indices = indices[self.argshape:] self.argshape = 0 if isinstance(aggr, Subscript): diff --git a/python/dune/perftool/options.py b/python/dune/perftool/options.py index ef6db4e89d56b951b3272397991c237bcb43e6ef..9e345954f24a313d216af9a6061f54edd2e45b81 100644 --- a/python/dune/perftool/options.py +++ b/python/dune/perftool/options.py @@ -100,8 +100,12 @@ def set_option(key, value): def get_option(key, default=None): - """Return the value corresponding to key from option dictionary""" - # Make sure form compile arguments were read - init_option_dict() - - return _option_dict.get(key, default) + try: + __IPYTHON__ + return default + except: + """Return the value corresponding to key from option dictionary""" + # Make sure form compile arguments were read + init_option_dict() + + return _option_dict.get(key, default) diff --git a/python/dune/perftool/pdelab/argument.py b/python/dune/perftool/pdelab/argument.py index 094d37b8a2f3dad5353b3a168b9b0a84068aa983..611f996d7b21ec8821af5a8ed9aa2b03edcd1fcf 100644 --- a/python/dune/perftool/pdelab/argument.py +++ b/python/dune/perftool/pdelab/argument.py @@ -17,7 +17,6 @@ from dune.perftool.pdelab.basis import (evaluate_coefficient, evaluate_coefficient_gradient, lfs_iname, name_basis, - name_basis_gradient, name_lfs_bound, ) from dune.perftool import Restriction @@ -139,15 +138,6 @@ def name_argumentspace(ma): assert False -def name_argument(ma): - if ma.argexpr.number() == 0: - return name_testfunction(ma) - if ma.argexpr.number() == 1: - return name_trialfunction(ma) - # We should never encounter an argument other than 0 or 1 - assert False - - def name_jacobian(restriction1, restriction2): # Restrictions may only differ if NONE if (restriction1 == Restriction.NONE) or (restriction2 == Restriction.NONE): diff --git a/python/dune/perftool/pdelab/basis.py b/python/dune/perftool/pdelab/basis.py index 23f0a4300446b42d6206fb202d583faca2d6794e..912de6ab6165b39810a77e59f9e39a822295fef8 100644 --- a/python/dune/perftool/pdelab/basis.py +++ b/python/dune/perftool/pdelab/basis.py @@ -370,7 +370,6 @@ def evaluate_coefficient(element, name, container, restriction, component): basis = name_basis(leaf_element, restriction) if isinstance(sub_element, (VectorElement, TensorElement)): - # TODO we need a concept of symmetry here lfs = lfs_child(lfs, idims, shape=shape_as_pymbolic(shape), symmetry=element.symmetry) from dune.perftool.pdelab.argument import pymbolic_coefficient @@ -406,10 +405,9 @@ def evaluate_coefficient_gradient(element, name, container, restriction, compone temporary_variable(name, shape=shape, shape_impl=shape_impl) lfs = name_lfs(element, restriction, component) index = lfs_iname(leaf_element, restriction, context='trialgrad') - basis = name_basis_gradient(leaf_element, restriction) + basis = name_reference_gradient(leaf_element, restriction) if isinstance(sub_element, (VectorElement, TensorElement)): - # TODO we need a concept of symmetry here lfs = lfs_child(lfs, idims[:-1], shape=shape_as_pymbolic(shape[:-1]), symmetry=element.symmetry) from dune.perftool.pdelab.argument import pymbolic_coefficient diff --git a/python/dune/perftool/pdelab/driver.py b/python/dune/perftool/pdelab/driver.py index 5bf9b95c949e46abf6e88c9e982d0879c3ecedb2..d0b403633da5c79610b261be51a8242446f44a73 100644 --- a/python/dune/perftool/pdelab/driver.py +++ b/python/dune/perftool/pdelab/driver.py @@ -1076,7 +1076,7 @@ def name_timing_stream(): @preamble def dune_solve(): # Test if form is linear in ansatzfunction - linear = is_linear(_driver_data['form']) + linear = is_linear(_driver_data['formdata'].original_form) # Test wether we want to do matrix free operator evaluation matrix_free = get_option('matrix_free') @@ -1499,7 +1499,7 @@ def time_loop(): def solve_instationary(): # Test if form is linear in ansatzfunction - linear = is_linear(_driver_data['form']) + linear = is_linear(_driver_data['formdata'].original_form) # Test wether we want to do matrix free operator evaluation matrix_free = get_option('matrix_free') diff --git a/python/dune/perftool/pdelab/geometry.py b/python/dune/perftool/pdelab/geometry.py index 8a27c2e179e5e8a7bc4cb568ce258066612853a3..143a1356214859963345328a0529c07e9800156d 100644 --- a/python/dune/perftool/pdelab/geometry.py +++ b/python/dune/perftool/pdelab/geometry.py @@ -11,6 +11,7 @@ from dune.perftool.pdelab.quadrature import (name_quadrature_position, quadrature_preamble, ) from ufl.algorithms import MultiFunction +from pymbolic.primitives import Variable class GeometryMapper(MultiFunction): @@ -38,10 +39,23 @@ class GeometryMapper(MultiFunction): # TODO This one was just copied over so, it might need some tweaking def facet_area(self, o): - from pymbolic.primitives import Variable from dune.perftool.pdelab.geometry import name_facetarea return Variable(name_facetarea()) + def quadrature_weight(self, o): + from dune.perftool.pdelab.quadrature import name_quadrature_weight + return Variable(name_quadrature_weight()) + + def jacobian_determinant(self, o): + return Variable(name_jacobian_determinant()) + + def jacobian_inverse(self, o): + self.transpose_necessary = True + return Variable(name_jacobian_inverse_transposed(self.restriction)) + + def jacobian(self, o): + raise NotImplementedError("How did you get Jacobian into your form? We only support JacobianInverse right now. Report!") + @iname def _dimension_iname(context, count): @@ -307,3 +321,23 @@ def name_jacobian_inverse_transposed(restriction): name = restricted_name("jit", restriction) define_jacobian_inverse_transposed(name, restriction) return name + + +def define_jacobian_determinant(name): + temporary_variable(name, shape=()) + geo = name_geometry() + pos = name_quadrature_position() + code = "{} = {}.integrationElement({});".format(name, + geo, + pos, + ) + return quadrature_preamble(code, + assignees=frozenset({name}), + read_variables=frozenset({pos}), + ) + + +def name_jacobian_determinant(): + name = 'detjac' + define_jacobian_determinant(name) + return name diff --git a/python/dune/perftool/pdelab/localoperator.py b/python/dune/perftool/pdelab/localoperator.py index 9bd174b0f7abff14c71bdf2e0159a1e94544be21..1b65c0a33653917aa1065d86789fdbe7e86cb799 100644 --- a/python/dune/perftool/pdelab/localoperator.py +++ b/python/dune/perftool/pdelab/localoperator.py @@ -187,7 +187,7 @@ def assembly_routine_signature(formdata): # Check if form is linear from dune.perftool.pdelab.driver import is_linear - linear = is_linear(formdata.preprocessed_form) + linear = is_linear(formdata.original_form) if form_type == 'residual': if integral_type == 'cell': @@ -509,8 +509,17 @@ def generate_localoperator_kernels(formdata, data): # Generate the necessary jacobian methods if not get_option("numerical_jacobian"): from ufl import derivative - from ufl.algorithms import expand_derivatives - jacform = expand_derivatives(derivative(form, form.coefficients()[0])) + from ufl.algorithms import compute_form_data + from dune.perftool.ufl.execution import (JacobianDeterminant, + JacobianInverse, + ) + jacform = derivative(formdata.original_form, formdata.original_form.coefficients()[0]) + jacform = compute_form_data(jacform, + do_apply_function_pullbacks=True, + do_apply_geometry_lowering=True, + do_apply_integral_scaling=True, + preserve_geometry_types=(JacobianDeterminant, JacobianInverse), + ).preprocessed_form with global_context(form_type="jacobian"): for measure in set(i.integral_type() for i in jacform.integrals()): diff --git a/python/dune/perftool/pdelab/quadrature.py b/python/dune/perftool/pdelab/quadrature.py index 464251fe33bceb7b528cd7cce8eabcff79ad4b42..5db88540ad6e001d6a7753414924e17ebc543b8d 100644 --- a/python/dune/perftool/pdelab/quadrature.py +++ b/python/dune/perftool/pdelab/quadrature.py @@ -57,27 +57,6 @@ def name_quadrature_weight(): return "{}.weight()".format(qp) -def define_quadrature_factor(fac): - weight = name_quadrature_weight() - from dune.perftool.pdelab.geometry import name_geometry - geo = name_geometry() - pos = name_quadrature_position() - code = "{} = {}*{}.integrationElement({});".format(fac, - weight, - geo, - pos, - ) - return quadrature_preamble(code, - assignees=frozenset({fac}), - read_variables=frozenset({pos}), - ) - - -def name_factor(): - temporary_variable("fac", shape=()) - define_quadrature_factor("fac") - return "fac" - def name_order(): # TODO how to determine integration order in a generic fashion here? diff --git a/python/dune/perftool/pymbolic/uflmapper.py b/python/dune/perftool/pymbolic/uflmapper.py index 5eae05a7121eed8ddc692f66052cc80868bf55a7..c8f10c797d698ac2ce1e8305c5c04dcd986d0a96 100644 --- a/python/dune/perftool/pymbolic/uflmapper.py +++ b/python/dune/perftool/pymbolic/uflmapper.py @@ -8,7 +8,7 @@ This is mainly intended as a base class for anything mapping ufl to pymbolic. from __future__ import absolute_import from ufl.algorithms import MultiFunction -from pymbolic.primitives import Product, Quotient, Subscript, Sum, Variable +from pymbolic.primitives import Product, Quotient, Subscript, Sum, Variable, Call # The constructed pymbolic expressions use n-ary operators instead of ufls binary operators from dune.perftool.ufl.flatoperators import get_operands @@ -39,3 +39,10 @@ class UFL2PymbolicMapper(MultiFunction): def zero(self, o): return 0 + + def abs(self, o): + from ufl.classes import JacobianDeterminant + if isinstance(o.ufl_operands[0], JacobianDeterminant): + return self.call(o.ufl_operands[0]) + else: + return Call('abs', self.call(o.ufl_operands[0])) diff --git a/python/dune/perftool/ufl/modified_terminals.py b/python/dune/perftool/ufl/modified_terminals.py index 82bb8eee94b2c0ec9e3a46835faae0e0dc610e70..97e4c2c75e1a8171cfbf9bb6f192763e8f983b65 100644 --- a/python/dune/perftool/ufl/modified_terminals.py +++ b/python/dune/perftool/ufl/modified_terminals.py @@ -13,6 +13,7 @@ class ModifiedTerminalTracker(MultiFunction): def __init__(self): MultiFunction.__init__(self) self.grad = False + self.reference = True self.reference_grad = False self.restriction = Restriction.NONE self.component = MultiIndex(()) @@ -51,12 +52,19 @@ class ModifiedTerminalTracker(MultiFunction): self.component = MultiIndex(()) return ret + def reference_value(self, o): + self.reference = True + ret = self.call(o.ufl_operands[0]) + self.reference = False + return ret + class ModifiedArgumentDescriptor(MultiFunction): def __init__(self, e): MultiFunction.__init__(self) self.grad = False + self.reference = False self.reference_grad = False self.index = None self.restriction = Restriction.NONE @@ -77,6 +85,10 @@ class ModifiedArgumentDescriptor(MultiFunction): self.reference_grad = True self(o.ufl_operands[0]) + def reference_value(self, o): + self.reference = True + self(o.ufl_operands[0]) + def positive_restricted(self, o): self.restriction = Restriction.POSITIVE self(o.ufl_operands[0]) @@ -129,6 +141,7 @@ class _ModifiedArgumentExtractor(MultiFunction): grad = pass_on reference_grad = pass_on function_view = pass_on + reference_value = pass_on def argument(self, o): if self.argnumber is None or o.number() == self.argnumber: