diff --git a/python/dune/perftool/pdelab/driver.py b/python/dune/perftool/pdelab/driver.py index 27a0b2450e8c8a58a16322e69518999397263055..872f6bd809ade33a923ab59907b3c58474ecd2d5 100644 --- a/python/dune/perftool/pdelab/driver.py +++ b/python/dune/perftool/pdelab/driver.py @@ -6,7 +6,30 @@ Currently, these are hardcoded as strings. It would be possible to switch these to cgen expression. OTOH, there is not much to be gained there. """ -from dune.perftool.generation import include_file, symbol, preamble +from dune.perftool.generation import (generator_factory, + include_file, + symbol, + preamble, + ) + + +# Those symbol generators that depend on the meta data we hacked into +# the ufl.FiniteElement should use fem_metadata_dependent_symbol instead of symbol. +def get_constraints_metadata(element): + """ + Normally, we suppress the constraints information on a finite element. + However, we might want to use it explicitly in those case where it matters + This function does the job! + """ + dc = getattr(element, 'dirichlet_constraints', None) + de = getattr(element, 'dirichlet_expression', None) + return (element, (dc, de)) + + +fem_metadata_dependent_symbol = generator_factory(item_tags=("symbol",), + cache_key_generator=get_constraints_metadata, + ) + # Have a global variable with the entire form data. This allows functions that depend # deterministically on the entire data set to directly access it instead of passing it @@ -267,28 +290,37 @@ def type_orderingtag(): @preamble -def typedef_constraintsassembler(name): +def typedef_dirichlet_constraintsassembler(name): include_file("dune/pdelab/constraints/conforming.hh", filetag="driver") return "typedef Dune::PDELab::ConformingDirichletConstraints {};".format(name) +@preamble +def typedef_no_constraintsassembler(name): + return "typedef Dune::PDELab::NoConstraints {};".format(name) + + @symbol -def type_constraintsassembler(): - typedef_constraintsassembler("ConstraintsAssembler") - return "ConstraintsAssembler" +def type_constraintsassembler(dirichlet): + if dirichlet: + typedef_dirichlet_constraintsassembler("DirichletConstraintsAssember") + return "DirichletConstraintsAssember" + else: + typedef_no_constraintsassembler("NoConstraintsAssembler") + return "NoConstraintsAssembler" @preamble -def typedef_constraintscontainer(expr, name): - gfs = type_gfs(expr) +def typedef_constraintscontainer(element, name): + gfs = type_gfs(element) r = type_range() return "typedef {}::ConstraintsContainer<{}>::Type {};".format(gfs, r, name) @symbol -def type_constraintscontainer(expr): - name = "{}_cc".format(FEM_name_mangling(expr)).upper() - typedef_constraintscontainer(expr, name) +def type_constraintscontainer(element): + name = "{}_cc".format(FEM_name_mangling(element)).upper() + typedef_constraintscontainer(element, name) return name @@ -299,20 +331,76 @@ def define_constraintscontainer(expr, name): @symbol -def name_constraintscontainer(expr): - name = "{}_cc".format(FEM_name_mangling(expr)).lower() - define_constraintscontainer(expr, name) +def name_constraintscontainer(element, dirichlet): + name = "{}{}_cc".format(FEM_name_mangling(element), "_dirichlet" if dirichlet else "").lower() + define_constraintscontainer(element, name) return name @preamble -def typedef_gfs(expr, name): +def define_cell_lambda(expression, name): + param = name_parameters() + return "auto {} = [&](const auto& e, const auto& x){{ return {}; }};".format(name, expression) + + +@preamble +def define_intersection_lambda(expression, name): + return "auto {} = [&](const auto& is, const auto& x){{ return {}; }};".format(name, expression) + + +@symbol +def name_bctype_lambda(dirichlet): + define_intersection_lambda(dirichlet, "glambda") + # TODO get access to the name data dict here! + return "glambda" + + +@preamble +def define_bctype_function(dirichlet, name): + gv = name_leafview() + bctype_lambda = name_bctype_lambda(dirichlet) + include_file('dune/pdelab/function/callableadapter.hh', filetag='driver') + return "auto {} = Dune::PDELab::makeBoundaryConditionFromCallable({}, {});".format(name, + gv, + bctype_lambda, + ) + + +@symbol +def name_bctype_function(dirichlet): + define_bctype_function(dirichlet, "g") + # TODO get access to the name data dict here! + return "g" + + +@preamble +def assemble_constraints(name, element, dirichlet): + assert dirichlet + bctype_function = name_bctype_function(dirichlet) + gfs = name_gfs(element) + return "Dune::PDELab::constraints({}, {}, {});".format(bctype_function, + gfs, + name, + ) + + +@fem_metadata_dependent_symbol +def name_assembled_constraints(expr): + element, (dirichlet, _) = get_constraints_metadata(expr) + name = name_constraintscontainer(element, dirichlet) + if dirichlet: + assemble_constraints(name, element, dirichlet) + return name + + +@preamble +def typedef_gfs(element, dirichlet, name): vb = type_vectorbackend() from ufl import FiniteElement, MixedElement, VectorElement, EnrichedElement, RestrictedElement - if isinstance(expr, FiniteElement): + if isinstance(element, FiniteElement): gv = type_leafview() - fem = type_fem(expr) - cass = type_constraintsassembler() + fem = type_fem(element) + cass = type_constraintsassembler(dirichlet) return "typedef Dune::PDELab::GridFunctionSpace<{}, {}, {}, {}> {};".format(gv, fem, cass, vb, name) if isinstance(expr, MixedElement): ot = type_orderingtag() @@ -323,7 +411,7 @@ def typedef_gfs(expr, name): gv = type_leafview() fem = type_fem(expr._sub_elements[0]) dim = name_dimension() - cass = type_constraintsassembler() + cass = type_constraintsassembler(dirichlet) return "typedef Dune::PDELab::GridFunctionSpace<{}, {}, {}, {}, {}, {}> {};".format(gv, fem, dim, vb, vb, cass, name) if isinstance(expr, EnrichedElement): raise NotImplementedError("Dune does not support enriched elements!") @@ -331,38 +419,37 @@ def typedef_gfs(expr, name): raise NotImplementedError("Dune does not support restricted elements!") -@symbol +@fem_metadata_dependent_symbol def type_gfs(expr): - name = "{}_GFS".format(FEM_name_mangling(expr).upper()) - typedef_gfs(expr, name) + element, dirichlet = get_constraints_metadata(expr) + name = "{}{}_gfs".format(FEM_name_mangling(element), "_dirichlet" if dirichlet else "").upper() + typedef_gfs(element, dirichlet, name) return name @preamble -def define_gfs(expr, name): - gfstype = type_gfs(expr) +def define_gfs(element, dirichlet, name): + gfstype = type_gfs(element) from ufl import FiniteElement, MixedElement, VectorElement, EnrichedElement, RestrictedElement - if isinstance(expr, FiniteElement): + if isinstance(element, FiniteElement): gv = name_leafview() - fem = name_fem(expr) + fem = name_fem(element) return "{} {}({}, {});".format(gfstype, name, gv, fem) if isinstance(expr, MixedElement): - args = ", ".join(name_gfs(childgfs) for childgfs in expr._sub_elements) + args = ", ".join(name_gfs(childgfs) for childgfs in element._sub_elements) return "{} {}({});".format(gfstype, name, args) if isinstance(expr, VectorElement): gv = name_leafview() - fem = name_fem(expr._sub_elements[0]) + fem = name_fem(element._sub_elements[0]) return "{} {}({}, {});".format(gfstype, name, gv, fem) - if isinstance(expr, EnrichedElement): - raise NotImplementedError("Dune does not support enriched elements!") - if isinstance(expr, RestrictedElement): - raise NotImplementedError("Dune does not support restricted elements!") + raise NotImplementedError("Dune does not support this type of finite element!") -@symbol +@fem_metadata_dependent_symbol def name_gfs(expr): - name = "{}_gfs".format(FEM_name_mangling(expr)).lower() - define_gfs(expr, name) + element, dirichlet = get_constraints_metadata(expr) + name = "{}{}_gfs".format(FEM_name_mangling(element), "_dirichlet" if dirichlet else "").lower() + define_gfs(element, dirichlet, name) return name @@ -483,9 +570,9 @@ def type_gridoperator(): def define_gridoperator(name): gotype = type_gridoperator() ugfs = name_gfs(_form.coefficients()[0].element()) - ucc = name_constraintscontainer(_form.coefficients()[0].element()) + ucc = name_assembled_constraints(_form.coefficients()[0].element()) vgfs = name_gfs(_form.arguments()[0].element()) - vcc = name_constraintscontainer(_form.arguments()[0].element()) + vcc = name_assembled_constraints(_form.arguments()[0].element()) lop = name_localoperator() mb = name_matrixbackend() return ["{} {}({}, {}, {}, {}, {}, {});".format(gotype, name, ugfs, ucc, vgfs, vcc, lop, mb), @@ -518,9 +605,49 @@ def define_vector(name): return ["{} {}({});".format(vtype, name, gfs), "{} = 0.0;".format(name)] +@preamble +def define_boundary_lambda(boundary, name): + return "auto {} = [&](const auto& x){{ return {}; }};".format(name, boundary) + + +@symbol +def name_boundary_lambda(boundary, name): + define_boundary_lambda(boundary, name + "lambda") + return name + "lambda" + + +@preamble +def define_boundary_function(boundary, name): + gv = name_leafview() + lambdaname = name_boundary_lambda(boundary, name) + return "auto {} = Dune::PDELab::makeGridFunctionFromCallable({}, {});".format(name, + gv, + lambdaname, + ) + + +@symbol +def name_boundary_function(boundary): + define_boundary_function(boundary, "b") + # TODO add namespace data here + return "b" + + +@preamble +def interpolate_vector(name): + define_vector(name) + element, (_, boundary) = get_constraints_metadata(_form.coefficients()[0].element()) + b = name_boundary_function(boundary) + gfs = name_gfs(element) + return "Dune::PDELab::interpolate({}, {}, {});".format(b, + gfs, + name, + ) + + @symbol def name_vector(): - define_vector("x") + interpolate_vector("x") return "x" diff --git a/python/dune/perftool/ufl/execution.py b/python/dune/perftool/ufl/execution.py index a2cd7a143290f0ff461056ef24122e3d0de21ad0..59a8d05f70f1aeb2a0aaf33dc04741fc6c730413 100644 --- a/python/dune/perftool/ufl/execution.py +++ b/python/dune/perftool/ufl/execution.py @@ -4,9 +4,19 @@ UFL. """ import ufl + from ufl import * from ufl.split_functions import split +# Monkey patch to the FiniteElementBase +# We want to subclass finite elements in order to allow meta data +# connected to constraints. This is an intermediate solution until +# we have a proper notion of (global) function spaces in UFL. As is, +# two finite elements with different meta data would not compare equally, +# but they should in order to allow hashing generation magic based on +# them (as done everywhere) +ufl.FiniteElementBase.__eq__ = lambda s, o: repr(s) == repr(o) + class TrialFunction(ufl.Coefficient): """ A coefficient that always takes the reserved index 0 """ @@ -42,3 +52,11 @@ class Expression(ufl.Coefficient): # Initialize a coefficient with a dummy finite element map. ufl.Coefficient.__init__(self, FiniteElement("DG", "triangle", 0)) + + +class FiniteElement(ufl.FiniteElement): + def __init__(self, *args, **kwargs): + if 'dirichlet_constraints' or 'dirichlet_expression' in kwargs: + self.dirichlet_constraints = kwargs.pop('dirichlet_constraints', 'true') + self.dirichlet_expression = kwargs.pop('dirichlet_expression', '0.0') + ufl.FiniteElement.__init__(self, *args, **kwargs)