diff --git a/python/dune/perftool/loopy/transformer.py b/python/dune/perftool/loopy/transformer.py index 0a0fde9abc31dbd7447a53e1918084b16df5c9de..9dec35bc7bb3e39d438a0d441a0f25e82b5be471 100644 --- a/python/dune/perftool/loopy/transformer.py +++ b/python/dune/perftool/loopy/transformer.py @@ -31,11 +31,11 @@ from pymbolic.primitives import Subscript, Variable class UFL2LoopyVisitor(ModifiedTerminalTracker, UFL2PymbolicMapper, GeometryMapper): - def __init__(self, measure, subdomain_id, dimension_index_aliases): + def __init__(self, measure, subdomain_id, dimension_indices): # Some variables describing the integral measure of this integral self.measure = measure self.subdomain_id = subdomain_id - self.dimension_index_aliases = dimension_index_aliases + self.dimension_indices = dimension_indices # Call base class constructors super(UFL2LoopyVisitor, self).__init__() @@ -96,7 +96,8 @@ class UFL2LoopyVisitor(ModifiedTerminalTracker, UFL2PymbolicMapper, GeometryMapp if len(subel.value_shape()) != 0: from dune.perftool.pdelab.geometry import dimension_iname from dune.perftool.pdelab.basis import lfs_child - lfs = lfs_child(lfs, dimension_iname(context='arg')) + idims = tuple(dimension_iname(context='arg', count=i) for i in range(len(subel.value_shape()))) + lfs = lfs_child(lfs, idims, shape=subel.value_shape()) residual_shape[icount] = name_lfs_bound(name_leaf_lfs(subel.sub_elements()[0], ma.restriction)) subel = subel.sub_elements()[0] else: @@ -181,8 +182,8 @@ class UFL2LoopyVisitor(ModifiedTerminalTracker, UFL2PymbolicMapper, GeometryMapp # Now treat the case of this being a vector finite element if element.num_sub_elements() > 0: # I cannot handle general mixed elements here... - from ufl import VectorElement - assert isinstance(element, VectorElement) + from ufl import VectorElement, TensorElement + assert isinstance(element, (VectorElement, TensorElement)) # Determine whether this is a non-scalar subargument. This information is later # used by the Indexed-Mapper to make some indices loop indices! @@ -191,7 +192,7 @@ class UFL2LoopyVisitor(ModifiedTerminalTracker, UFL2PymbolicMapper, GeometryMapp # If this is a vector element, we need add an additional accumulation loop iname from dune.perftool.pdelab.geometry import dimension_iname for i in range(self.argshape): - self.inames.append(dimension_iname(context='arg')) + self.inames.append(dimension_iname(context='arg', count=i)) # For the purpose of basis evaluation, we need to take the leaf element leaf_element = element.sub_elements()[0] @@ -286,7 +287,7 @@ class UFL2LoopyVisitor(ModifiedTerminalTracker, UFL2PymbolicMapper, GeometryMapp # Recurse to get the summation expression term = self.call(o.ufl_operands[0]) - self.redinames = tuple(i for i in self.redinames if i not in self.dimension_index_aliases) + self.redinames = tuple(i for i in self.redinames if i not in self.dimension_indices) if len(self.redinames) > 0: ret = Reduction("sum", tuple(name_index(ind) for ind in self.redinames), term) name = get_temporary_name() @@ -314,10 +315,10 @@ class UFL2LoopyVisitor(ModifiedTerminalTracker, UFL2PymbolicMapper, GeometryMapp else: from pymbolic.primitives import Variable from dune.perftool.pdelab import name_index - if index in self.dimension_index_aliases: + if index in self.dimension_indices: from dune.perftool.pdelab.geometry import dimension_iname - self.inames.append(dimension_iname(context='arg')) - return Variable(dimension_iname(context='arg')) + self.inames.append(self.dimension_indices[index]) + return Variable(self.dimension_indices[index]) else: return Variable(name_index(index)) diff --git a/python/dune/perftool/pdelab/basis.py b/python/dune/perftool/pdelab/basis.py index 9a45e0d297efe75c63e5e24c451a183b47d5cb41..7e001b4af62a7c1c650158ce19c089595e5d075a 100644 --- a/python/dune/perftool/pdelab/basis.py +++ b/python/dune/perftool/pdelab/basis.py @@ -75,11 +75,26 @@ def define_lfs(name, father, child): return "auto {} = child({}, _{});".format(name, father, child) -def lfs_child(lfs, child): - from pymbolic.primitives import Call +def lfs_child(lfs, children, shape=None): + from pymbolic.primitives import Call, Product, Sum + # Old pre-TensorElement implementation kept for comaptibility + if shape is None: + indices = (Variable(children[0]),) + else: + children = tuple(Variable(c) for c in children) + offset = Product(tuple()) + indices = (Sum( + tuple( + Product( + (Product(tuple(shape[j] for j in range(i)) + ), + child) + ) + for i, child in enumerate(children)) + ),) + from dune.perftool.loopy.functions import LFSChild - return Call(LFSChild(lfs), (Variable(child),)) -# return "{}.child({})".format(lfs, child) + return Call(LFSChild(lfs), indices) @generator_factory(cache_key_generator=lambda e, r, **kw: (e, r)) @@ -307,6 +322,14 @@ def name_basis_gradient(leaf_element, restriction): return name +def shape_as_pymbolic(shape): + def _shape_as_pymbolic(s): + if isinstance(s, str): + return Variable(s) + else: + return s + return tuple(_shape_as_pymbolic(s) for s in shape) + @cached def evaluate_trialfunction(element, name, restriction, component): from ufl.functionview import select_subelement @@ -314,28 +337,28 @@ def evaluate_trialfunction(element, name, restriction, component): # Determine the rank of the trialfunction tensor rank = len(sub_element.value_shape()) - assert rank in (0, 1) - shape = (name_dimension(),) * rank - shape_impl = ('fv', ) * rank + shape = sub_element.value_shape() + shape_impl = ('arr', ) * rank idims = tuple(dimension_iname(count=i) for i in range(rank)) leaf_element = sub_element - from ufl import VectorElement - if isinstance(sub_element, VectorElement): + from ufl import VectorElement, TensorElement + if isinstance(sub_element, (VectorElement, TensorElement)): leaf_element = sub_element.sub_elements()[0] temporary_variable(name, shape=shape, shape_impl=shape_impl) lfs = name_lfs(element, restriction, component) index = lfs_iname(leaf_element, restriction, context='trial') basis = name_basis(leaf_element, restriction) - assignee = Variable(name) - if isinstance(sub_element, VectorElement): - lfs = lfs_child(lfs, idims[0]) - assignee = Subscript(assignee, Variable(idims[0])) + if isinstance(sub_element, (VectorElement, TensorElement)): + # TODO we need a concept of symmetry here + lfs = lfs_child(lfs, idims, shape=shape_as_pymbolic(shape)) from dune.perftool.pdelab.argument import pymbolic_coefficient coeff = pymbolic_coefficient(lfs, index, restriction) + assignee = Subscript(Variable(name), tuple(Variable(i) for i in idims)) + reduction_expr = Product((coeff, Subscript(Variable(basis), Variable(index)))) instruction(expression=Reduction("sum", index, reduction_expr, allow_simultaneous=True), assignee=assignee, @@ -350,18 +373,15 @@ def evaluate_trialfunction_gradient(element, name, restriction, component): from ufl.functionview import select_subelement sub_element = select_subelement(element, component) rank = len(sub_element.value_shape()) + 1 - assert rank in (1, 2) # We do then set some variables accordingly - shape = (name_dimension(),) * rank - if rank == 1: - shape_impl = ('fv',) - else: - shape_impl = ('fm',) + shape = sub_element.value_shape() + (element.cell().geometric_dimension(),) + shape_impl = ('arr',) * rank + idims = tuple(dimension_iname(count=i) for i in range(rank)) leaf_element = sub_element - from ufl import VectorElement - if isinstance(sub_element, VectorElement): + from ufl import VectorElement, TensorElement + if isinstance(sub_element, (VectorElement, TensorElement)): leaf_element = sub_element.sub_elements()[0] # and proceed to call the necessary generator functions @@ -370,8 +390,9 @@ def evaluate_trialfunction_gradient(element, name, restriction, component): index = lfs_iname(leaf_element, restriction, context='trialgrad') basis = name_basis_gradient(leaf_element, restriction) - if isinstance(sub_element, VectorElement): - lfs = lfs_child(lfs, idims[0]) + 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])) from dune.perftool.pdelab.argument import pymbolic_coefficient coeff = pymbolic_coefficient(lfs, index, restriction) diff --git a/python/dune/perftool/pdelab/driver.py b/python/dune/perftool/pdelab/driver.py index 33d179fd7fc2b06924fa5e0470c65a96e0410007..a32841f879f0dcba39cf229ae5a2a94c22536a6a 100644 --- a/python/dune/perftool/pdelab/driver.py +++ b/python/dune/perftool/pdelab/driver.py @@ -73,11 +73,17 @@ def isQk(fem): def isDG(fem): return fem._short_name is 'DG' +def flatten_tensor(expr): + from ufl import TensorElement + assert isinstance(expr, TensorElement) + def FEM_name_mangling(fem): - from ufl import MixedElement, VectorElement, FiniteElement + from ufl import MixedElement, VectorElement, FiniteElement, TensorElement if isinstance(fem, VectorElement): return FEM_name_mangling(fem.sub_elements()[0]) + "_" + str(fem.num_sub_elements()) + if isinstance(fem, TensorElement): + return FEM_name_mangling(fem.sub_elements()[0]) + "_" + "_".join(str(i) for i in fem.value_shape()) if isinstance(fem, MixedElement): name = "" for elem in fem.sub_elements(): @@ -413,8 +419,8 @@ def define_compositegfs_constraints(name, *children): @symbol def name_bctype_function(expr): - from ufl import MixedElement, VectorElement - if isinstance(expr, VectorElement): + from ufl import MixedElement, VectorElement, TensorElement + if isinstance(expr, (VectorElement, TensorElement)): element, (dirichlet, _) = get_constraints_metadata(expr) # get the correct name from the corresponding UFL file from dune.perftool.generation import get_global_context_value @@ -461,16 +467,17 @@ def name_assembled_constraints(expr): @preamble def typedef_gfs(element, dirichlet, name): vb = type_vectorbackend() - from ufl import FiniteElement, MixedElement, VectorElement, EnrichedElement, RestrictedElement + from ufl import FiniteElement, MixedElement, VectorElement, EnrichedElement, RestrictedElement, TensorElement if isinstance(element, FiniteElement): gv = type_leafview() fem = type_fem(element) cass = type_constraintsassembler(dirichlet) return "typedef Dune::PDELab::GridFunctionSpace<{}, {}, {}, {}> {};".format(gv, fem, cass, vb, name) - if isinstance(element, VectorElement): + if isinstance(element, (VectorElement, TensorElement)): include_file("dune/pdelab/gridfunctionspace/vectorgridfunctionspace.hh", filetag="driver") gv = type_leafview() fem = type_fem(element.sub_elements()[0]) + # TODO implement symmetry here dim = element.num_sub_elements() cass = type_constraintsassembler(dirichlet) return "typedef Dune::PDELab::VectorGridFunctionSpace<{}, {}, {}, {}, {}, {}> {};".format(gv, fem, dim, vb, vb, cass, name) @@ -495,12 +502,12 @@ def type_gfs(expr): @preamble def define_gfs(element, dirichlet, name): gfstype = type_gfs(element) - from ufl import FiniteElement, MixedElement, VectorElement, EnrichedElement, RestrictedElement + from ufl import FiniteElement, MixedElement, VectorElement, EnrichedElement, RestrictedElement, TensorElement if isinstance(element, FiniteElement): gv = name_leafview() fem = name_fem(element) return "{} {}({}, {});".format(gfstype, name, gv, fem) - if isinstance(element, VectorElement): + if isinstance(element, (VectorElement, TensorElement)): gv = name_leafview() fem = name_fem(element.sub_elements()[0]) return "{} {}({}, {});".format(gfstype, name, gv, fem) @@ -705,8 +712,8 @@ def define_compositegfs_parameterfunction(name, *children): @symbol def name_boundary_function(expr): - from ufl import MixedElement, VectorElement - if isinstance(expr, VectorElement): + from ufl import MixedElement, VectorElement, TensorElement + if isinstance(expr, (VectorElement, TensorElement)): element, (_, boundary) = get_constraints_metadata(expr) from dune.perftool.generation import get_global_context_value name = get_global_context_value("namedata").get(id(boundary), "zero") @@ -881,8 +888,8 @@ def print_matrix(): @preamble def define_gfs_name(element, prefix=()): - from ufl import MixedElement, VectorElement - if isinstance(element, VectorElement): + from ufl import MixedElement, VectorElement, TensorElement + if isinstance(element, (VectorElement, TensorElement)): gfs = name_gfs(element) return "{}.name(\"data_{}\");".format(gfs, "_".join(str(p) for p in prefix)) if isinstance(element, MixedElement): diff --git a/python/dune/perftool/pdelab/localoperator.py b/python/dune/perftool/pdelab/localoperator.py index 42a9d4e124dc6506b4d17f72bd5cded9925352c5..85fef5fa2781caaceb50f9dde0dd6c3b6bdf26bf 100644 --- a/python/dune/perftool/pdelab/localoperator.py +++ b/python/dune/perftool/pdelab/localoperator.py @@ -161,8 +161,8 @@ def generate_kernel(integrals): subdomain_id = integral.subdomain_id() subdomain_data = integral.subdomain_data() - from dune.perftool.ufl.dimensionindex import collect_dimension_index_aliases - dimension_index_aliases = collect_dimension_index_aliases(integrand) + from dune.perftool.ufl.dimensionindex import dimension_index_mapping + dimension_indices = dimension_index_mapping(integrand) # Now split the given integrand into accumulation expressions from dune.perftool.ufl.transformations.extract_accumulation_terms import split_into_accumulation_terms @@ -170,7 +170,7 @@ def generate_kernel(integrals): # Get a transformer instance for this kernel from dune.perftool.loopy.transformer import UFL2LoopyVisitor - visitor = UFL2LoopyVisitor(measure, subdomain_id, dimension_index_aliases) + visitor = UFL2LoopyVisitor(measure, subdomain_id, dimension_indices) # Iterate over the terms and generate a kernel for term in accterms: diff --git a/python/dune/perftool/ufl/dimensionindex.py b/python/dune/perftool/ufl/dimensionindex.py index 1446bd4dc8f9b71fcc27d884b3af513c634eaf66..e7c48a25581fb950095ce121514336ef14212944 100644 --- a/python/dune/perftool/ufl/dimensionindex.py +++ b/python/dune/perftool/ufl/dimensionindex.py @@ -3,30 +3,32 @@ from ufl.algorithms import MultiFunction -class _CollectDimensionIndexAliases(MultiFunction): +class _DimensionIndexMapping(MultiFunction): call = MultiFunction.__call__ def __call__(self, o): self.shape = 0 - return self.call(o) + self.dimension_index_dict = {} + self.call(o) - def expr(self, o): - return frozenset({}).union(*tuple(self.call(op) for op in o.ufl_operands)) + return self.dimension_index_dict - def terminal(self, o): - return frozenset({}) + def expr(self, o): + for op in o.ufl_operands: + self.call(op) def function_view(self, o): - self.shape = len(o.ufl_operands[1]) - return frozenset({}) + from ufl.functionview import select_subelement + subelement = select_subelement(o.ufl_operands[0].ufl_element(), o.ufl_operands[1]) + self.shape = len(subelement.value_shape()) def indexed(self, o): - ret = self.call(o.ufl_operands[0]) - if self.shape: - ret = ret.union(frozenset({o.ufl_operands[1][:self.shape][0]})) + self.call(o.ufl_operands[0]) + for i in range(self.shape): + from dune.perftool.pdelab.geometry import dimension_iname + self.dimension_index_dict[o.ufl_operands[1][i]] = dimension_iname(context='arg', count=i) self.shape = 0 - return ret -def collect_dimension_index_aliases(expr): - return _CollectDimensionIndexAliases()(expr) +def dimension_index_mapping(expr): + return _DimensionIndexMapping()(expr)