diff --git a/patches/apply_patches.sh b/patches/apply_patches.sh index f95f1f6a28289d3ef0b62fdef5e818d5b87530c4..0ac07e6614e86c9a439584a71d6f17b0a1a8a90d 100755 --- a/patches/apply_patches.sh +++ b/patches/apply_patches.sh @@ -12,3 +12,7 @@ popd pushd python/ufl git apply ../../patches/ufl/conditional-uflid.patch popd + +pushd python/ufl +git apply ../../patches/ufl/tensor-product-element.patch +popd diff --git a/patches/ufl/tensor-product-element.patch b/patches/ufl/tensor-product-element.patch new file mode 100644 index 0000000000000000000000000000000000000000..9fc64f124e95bcbf28391ed5be4c87e1040a4e28 --- /dev/null +++ b/patches/ufl/tensor-product-element.patch @@ -0,0 +1,19 @@ +commit f87dcd18d765b0200808b79b2e7374f82a0c6199 +Author: René Heß <rene.hess@iwr.uni-heidelberg.de> +Date: Tue Aug 29 14:56:17 2017 +0200 + + Patch for TensorProductElements + +diff --git a/ufl/algorithms/compute_form_data.py b/ufl/algorithms/compute_form_data.py +index 3388bbfc..1cef3924 100644 +--- a/ufl/algorithms/compute_form_data.py ++++ b/ufl/algorithms/compute_form_data.py +@@ -56,7 +56,7 @@ def _auto_select_degree(elements): + """ + # Use max degree of all elements, at least 1 (to work with + # Lagrange elements) +- return max({e.degree() for e in elements} - {None} | {1}) ++ return max({e.degree() if not isinstance(e.degree(), tuple) else max(e.degree()) for e in elements} - {None} | {1}) + + + def _compute_element_mapping(form): diff --git a/python/dune/perftool/generation/loopy.py b/python/dune/perftool/generation/loopy.py index 171ba6709c27fa8e88876da6180ef1b9debd26f4..fb3dbaba5ab97dbc7f3f40b1bae02bf15777695e 100644 --- a/python/dune/perftool/generation/loopy.py +++ b/python/dune/perftool/generation/loopy.py @@ -50,10 +50,21 @@ def valuearg(name, **kw): @generator_factory(item_tags=("domain",), context_tags="kernel") def domain(iname, shape): - if isinstance(iname, tuple): + if isinstance(iname, tuple) and isinstance(shape, tuple): + assert(len(iname) == len(shape)) + condition = "" + for index, (i, s) in enumerate(zip(iname, shape)): + if index > 0: + condition += " and " + else: + condition += " " + condition += "0<={}<{}".format(i, s) iname = ",".join(iname) - if isinstance(shape, str): - valuearg(shape) + return "{{ [{}] : {} }}".format(iname, condition) + elif isinstance(iname, tuple): + iname = ",".join(iname) + if isinstance(shape, str): + valuearg(shape) return "{{ [{0}] : 0<={0}<{1} }}".format(iname, shape) diff --git a/python/dune/perftool/loopy/transformations/vectorize_quad.py b/python/dune/perftool/loopy/transformations/vectorize_quad.py index 149002c4b872ba28f21e790a4df6c2a044ae5b40..abc7294086f52f9281090fcbe36de59bfd162ce4 100644 --- a/python/dune/perftool/loopy/transformations/vectorize_quad.py +++ b/python/dune/perftool/loopy/transformations/vectorize_quad.py @@ -136,6 +136,7 @@ def _vectorize_quadrature_loop(knl, inames, suffix): # Do precomputation of the quantity prec_quantity = "{}_precomputed".format(quantity) prec_quantities.append(prec_quantity) + knl = lp.precompute(knl, subst_name, inames, temporary_name=prec_quantity, ) diff --git a/python/dune/perftool/options.py b/python/dune/perftool/options.py index 49e6941a443493ac7d82294bc461a2a79b5c6c1d..ed37897178ab6fa5b74b987e38ce81f341eefa9a 100644 --- a/python/dune/perftool/options.py +++ b/python/dune/perftool/options.py @@ -63,8 +63,8 @@ class PerftoolOptionsArray(ImmutableRecord): # Arguments that are mainly to be set by logic depending on other options max_vector_width = PerftoolOption(default=256, helpstr=None) - unroll_dimension_loops = PerftoolOption(default=False, helpstr="whether loops over the gemetric dimension should be unrolled.") - precompute_quadrature_info = PerftoolOption(default=True, helpstr="whether loops over the gemetric dimension should be unrolled.") + unroll_dimension_loops = PerftoolOption(default=False, helpstr="whether loops over the geometric dimension should be unrolled") + precompute_quadrature_info = PerftoolOption(default=True, helpstr="compute quadrature points and weights in the constructor of the local operator") blockstructured = PerftoolOption(default=False, helpstr="Use block structure") number_of_blocks = PerftoolOption(default=1, helpstr="Number of sub blocks in one direction") diff --git a/python/dune/perftool/pdelab/driver/__init__.py b/python/dune/perftool/pdelab/driver/__init__.py index 81ebce1ea48efc25b88d9e3863df15535afc96db..2c38881f4a7351a199bc43f1515d8fc55c9914d2 100644 --- a/python/dune/perftool/pdelab/driver/__init__.py +++ b/python/dune/perftool/pdelab/driver/__init__.py @@ -136,7 +136,7 @@ def isDG(fem): def FEM_name_mangling(fem): - from ufl import MixedElement, VectorElement, FiniteElement, TensorElement + from ufl import MixedElement, VectorElement, FiniteElement, TensorElement, TensorProductElement if isinstance(fem, VectorElement): return FEM_name_mangling(fem.sub_elements()[0]) + "_" + str(fem.num_sub_elements()) if isinstance(fem, TensorElement): @@ -155,6 +155,14 @@ def FEM_name_mangling(fem): return "Q" + str(fem._degree) if isDG(fem): return "DG" + str(fem._degree) + if isinstance(fem, TensorProductElement): + assert(len(set(subel._short_name for subel in fem.sub_elements())) == 1) + + if isLagrange(fem.sub_elements()[0]): + return "TensorQ" + '_'.join(map(str, fem._degree)) + if isDG(fem.sub_elements()[0]): + return "TensorDG" + '_'.join(map(str, fem._degree)) + raise NotImplementedError("fem name mangling") raise NotImplementedError("FEM NAME MANGLING") diff --git a/python/dune/perftool/pdelab/driver/constraints.py b/python/dune/perftool/pdelab/driver/constraints.py index 2052532851c119d49bcb47284cb0e40531f3b573..9158fc6be52d5c95c976eb08041fc3ad5b4a5da6 100644 --- a/python/dune/perftool/pdelab/driver/constraints.py +++ b/python/dune/perftool/pdelab/driver/constraints.py @@ -14,7 +14,7 @@ from dune.perftool.pdelab.driver.gridfunctionspace import (name_gfs, preprocess_leaf_data, ) -from ufl import FiniteElement, MixedElement, TensorElement, VectorElement +from ufl import FiniteElement, MixedElement, TensorElement, VectorElement, TensorProductElement def name_assembled_constraints(): @@ -53,7 +53,7 @@ def name_bctype_function(element, is_dirichlet): define_composite_bctype_function(element, is_dirichlet, name, tuple(childs)) return name else: - assert isinstance(element, FiniteElement) + assert isinstance(element, (FiniteElement, TensorProductElement)) name = "{}_bctype".format(FEM_name_mangling(element).lower()) define_bctype_function(element, is_dirichlet[0], name) return name diff --git a/python/dune/perftool/pdelab/driver/gridfunctionspace.py b/python/dune/perftool/pdelab/driver/gridfunctionspace.py index e442c08ed9fc7b5f7b12f7505fdbd86287918bd3..bf47920a94513d0075b92d3b65bc8357081e371e 100644 --- a/python/dune/perftool/pdelab/driver/gridfunctionspace.py +++ b/python/dune/perftool/pdelab/driver/gridfunctionspace.py @@ -7,6 +7,7 @@ from dune.perftool.pdelab.driver import (FEM_name_mangling, get_dimension, get_test_element, get_trial_element, + isLagrange, isDG, isPk, isQk, @@ -16,7 +17,7 @@ from dune.perftool.pdelab.driver import (FEM_name_mangling, preprocess_leaf_data, ) -from ufl import FiniteElement, MixedElement, TensorElement, VectorElement +from ufl import FiniteElement, MixedElement, TensorElement, VectorElement, TensorProductElement @preamble @@ -124,24 +125,52 @@ def typedef_fem(element, name): df = type_domainfield() r = type_range() dim = get_dimension() + if get_option("blockstructured"): include_file("dune/perftool/blockstructured/blockstructuredqkfem.hh", filetag="driver") degree = element.degree() * get_option("number_of_blocks") - return "using {} = Dune::PDELab::BlockstructuredQkLocalFiniteElementMap<{}, {}, {}, {}>;".format(name, gv, df, r, degree) - if isQk(element): + return "using {} = Dune::PDELab::BlockstructuredQkLocalFiniteElementMap<{}, {}, {}, {}>;" \ + .format(name, gv, df, r, degree) + + if isinstance(element, TensorProductElement): + # Only allow TensorProductElements where all subelements are + # of the same type ('CG' or 'DG') + assert(len(set(subel._short_name for subel in element.sub_elements())) == 1) + + # Anisotropic degree is not yet supported in Dune + degrees = element.degree() + for deg in degrees: + assert (deg == degrees[0]) + + # TensorProductElements have Qk structure -> no Pk + if isLagrange(element.sub_elements()[0]): + include_file("dune/pdelab/finiteelementmap/qkfem.hh", filetag="driver") + return "using {} = Dune::PDELab::QkLocalFiniteElementMap<{}, {}, {}, {}>;" \ + .format(name, gv, df, r, degrees[0]) + elif isDG(element.sub_elements()[0]): + include_file("dune/pdelab/finiteelementmap/qkdg.hh", filetag="driver") + # TODO allow switching the basis here! + return "using {} = Dune::PDELab::QkDGLocalFiniteElementMap<{}, {}, {}, {}>;" \ + .format(name, df, r, degrees[0], dim) + raise NotImplementedError("FEM not implemented in dune-perftool") + elif isQk(element): include_file("dune/pdelab/finiteelementmap/qkfem.hh", filetag="driver") - return "using {} = Dune::PDELab::QkLocalFiniteElementMap<{}, {}, {}, {}>;".format(name, gv, df, r, element.degree()) - if isPk(element): + return "using {} = Dune::PDELab::QkLocalFiniteElementMap<{}, {}, {}, {}>;" \ + .format(name, gv, df, r, element.degree()) + elif isPk(element): include_file("dune/pdelab/finiteelementmap/pkfem.hh", filetag="driver") - return "using {} = Dune::PDELab::PkLocalFiniteElementMap<{}, {}, {}, {}>;".format(name, gv, df, r, element.degree()) - if isDG(element): + return "using {} = Dune::PDELab::PkLocalFiniteElementMap<{}, {}, {}, {}>;" \ + .format(name, gv, df, r, element.degree()) + elif isDG(element): if isQuadrilateral(element.cell()): include_file("dune/pdelab/finiteelementmap/qkdg.hh", filetag="driver") # TODO allow switching the basis here! - return "using {} = Dune::PDELab::QkDGLocalFiniteElementMap<{}, {}, {}, {}>;".format(name, df, r, element.degree(), dim) + return "using {} = Dune::PDELab::QkDGLocalFiniteElementMap<{}, {}, {}, {}>;" \ + .format(name, df, r, element.degree(), dim) if isSimplical(element.cell()): include_file("dune/pdelab/finiteelementmap/opbfem.hh", filetag="driver") - return "using {} = Dune::PDELab::OPBLocalFiniteElementMap<{}, {}, {}, {}, Dune::GeometryType::simplex>;".format(name, df, r, element.degree(), dim) + return "using {} = Dune::PDELab::OPBLocalFiniteElementMap<{}, {}, {}, {}, Dune::GeometryType::simplex>;" \ + .format(name, df, r, element.degree(), dim) raise NotImplementedError("Geometry type not known in code generation") raise NotImplementedError("FEM not implemented in dune-perftool") @@ -157,15 +186,26 @@ def type_fem(element): def define_fem(element, name): femtype = type_fem(element) from dune.perftool.pdelab.driver import isDG - if isDG(element): + if isinstance(element, TensorProductElement): + # Only allow TensorProductElements where all subelements are + # of the same type ('CG' or 'DG') + assert(len(set(subel._short_name for subel in element.sub_elements())) == 1) + if isDG(element.sub_elements()[0]): + return "{} {};".format(femtype, name) + else: + assert(isLagrange(element.sub_elements()[0])) + gv = name_leafview() + return "{} {}({});".format(femtype, name, gv) + elif isDG(element): return "{} {};".format(femtype, name) else: + assert(isLagrange(element)) gv = name_leafview() return "{} {}({});".format(femtype, name, gv) def name_fem(element): - assert isinstance(element, FiniteElement) + assert isinstance(element, (FiniteElement, TensorProductElement)) name = "{}_fem".format(FEM_name_mangling(element).lower()) define_fem(element, name) return name @@ -192,7 +232,7 @@ def name_gfs(element, is_dirichlet, treepath=()): "_".join(str(t) for t in treepath)) define_power_gfs(element, is_dirichlet, name, subgfs) return name - if isinstance(element, MixedElement): + elif isinstance(element, MixedElement): k = 0 subgfs = [] for i, subel in enumerate(element.sub_elements()): @@ -203,7 +243,7 @@ def name_gfs(element, is_dirichlet, treepath=()): define_composite_gfs(element, is_dirichlet, name, tuple(subgfs)) return name else: - assert isinstance(element, FiniteElement) + assert isinstance(element, (FiniteElement, TensorProductElement)) name = "{}{}_gfs_{}".format(FEM_name_mangling(element).lower(), "_dirichlet" if is_dirichlet[0] else "", "_".join(str(t) for t in treepath)) @@ -230,7 +270,7 @@ def type_gfs(element, is_dirichlet): name = "{}_POW{}GFS".format(subgfs, element.num_sub_elements()) typedef_power_gfs(element, is_dirichlet, name, subgfs) return name - if isinstance(element, MixedElement): + elif isinstance(element, MixedElement): k = 0 subgfs = [] for subel in element.sub_elements(): @@ -240,7 +280,7 @@ def type_gfs(element, is_dirichlet): typedef_composite_gfs(element, name, tuple(subgfs)) return name else: - assert isinstance(element, FiniteElement) + assert isinstance(element, (FiniteElement, TensorProductElement)) name = "{}{}_GFS".format(FEM_name_mangling(element).upper(), "_dirichlet" if is_dirichlet[0] else "", ) diff --git a/python/dune/perftool/pdelab/driver/interpolate.py b/python/dune/perftool/pdelab/driver/interpolate.py index cacb75aa1642180a9c8a014380e46b842faed09a..f3cc2afbeb3f083e41ed0c66a4060c08a2b0e5a1 100644 --- a/python/dune/perftool/pdelab/driver/interpolate.py +++ b/python/dune/perftool/pdelab/driver/interpolate.py @@ -15,7 +15,7 @@ from dune.perftool.pdelab.driver.gridfunctionspace import (name_trial_gfs, ) from dune.perftool.pdelab.driver.gridoperator import (name_parameters,) -from ufl import FiniteElement, MixedElement, TensorElement, VectorElement +from ufl import FiniteElement, MixedElement, TensorElement, VectorElement, TensorProductElement def _do_interpolate(dirichlet): @@ -55,7 +55,7 @@ def name_boundary_function(element, func): define_compositegfs_parameterfunction(name, tuple(childs)) return name else: - assert isinstance(element, FiniteElement) + assert isinstance(element, (FiniteElement, TensorProductElement)) name = get_counted_variable("func") define_boundary_function(name, func[0]) return name diff --git a/python/dune/perftool/pdelab/driver/vtk.py b/python/dune/perftool/pdelab/driver/vtk.py index 7edf92f357533393c9fb81370743904808885133..824f24ded82a193caaa924e6bfee5c4fa552faf3 100644 --- a/python/dune/perftool/pdelab/driver/vtk.py +++ b/python/dune/perftool/pdelab/driver/vtk.py @@ -41,7 +41,10 @@ def type_vtkwriter(): @preamble def define_subsamplinglevel(name): ini = name_initree() - return "int {} = {}.get<int>(\"vtk.subsamplinglevel\", {});".format(name, ini, max(get_trial_element().degree() - 1, 0)) + degree = get_trial_element().degree() + if isinstance(degree, tuple): + degree = max(degree) + return "int {} = {}.get<int>(\"vtk.subsamplinglevel\", {});".format(name, ini, max(degree - 1, 0)) def name_subsamplinglevel(): diff --git a/python/dune/perftool/pdelab/localoperator.py b/python/dune/perftool/pdelab/localoperator.py index a1f8e04417e2caae95135090578dc0fb9f69e01b..90b320db0cfb13fabfc2ab31f7389042145ea46e 100644 --- a/python/dune/perftool/pdelab/localoperator.py +++ b/python/dune/perftool/pdelab/localoperator.py @@ -775,9 +775,9 @@ def generate_localoperator_kernels(formdata, data): operator_kernels[(measure, 'residual')] = kernel - logger.info("generate_localoperator_kernels: create jacobian methods") # Generate the necessary jacobian methods if not get_option("numerical_jacobian"): + logger.info("generate_localoperator_kernels: create jacobian methods") from ufl import derivative jacform = derivative(formdata.original_form, formdata.original_form.coefficients()[0]) diff --git a/python/dune/perftool/pdelab/quadrature.py b/python/dune/perftool/pdelab/quadrature.py index 8356ecd22e28809825f334e03f6cf4e58734dbdb..4f28b9c52c8a2fe51b0b31ce7c34145ea63a7985 100644 --- a/python/dune/perftool/pdelab/quadrature.py +++ b/python/dune/perftool/pdelab/quadrature.py @@ -189,9 +189,25 @@ def _estimate_quadrature_order(): # Estimate polynomial degree of integrals of current type (eg 'Cell') integral_type = get_global_context_value("integral_type") integrals = form.integrals_by_type(integral_type) - polynomial_degree = 0 + + # Degree could be a tuple (for TensorProductElements) + degree = integrals[0].metadata()['estimated_polynomial_degree'] + if isinstance(degree, int): + degree = (degree,) + polynomial_degree = [0, ] * len(degree) + for i in integrals: - polynomial_degree = max(polynomial_degree, i.metadata()['estimated_polynomial_degree']) + degree = i.metadata()['estimated_polynomial_degree'] + if isinstance(degree, int): + degree = [degree, ] + assert(len(polynomial_degree) == len(degree)) + for i in range(len(polynomial_degree)): + polynomial_degree[i] = max(polynomial_degree[i], degree[i]) + + # Return either a tuple or an int + polynomial_degree = tuple(polynomial_degree) + if len(polynomial_degree) == 1: + polynomial_degree = polynomial_degree[0] return polynomial_degree @@ -199,14 +215,31 @@ def _estimate_quadrature_order(): def quadrature_order(): """Get quadrature order - Note: In PDELab quadrature order m means that integration of - polynomials of degree m is excat. + Notes: + + - In PDELab quadrature order m means that integration of + polynomials of degree m is exact. + + - If you use sum factorization and TensorProductElement it is + possible to use a different quadrature_order per direction. """ if get_option("quadrature_order"): - quadrature_order = int(get_option("quadrature_order")) + quadrature_order = tuple(map(int, get_option("quadrature_order").split(','))) else: quadrature_order = _estimate_quadrature_order() + # TensorProductElements can have different quadrature order for + # every direction so quadrature_order may be a tuple. If we do not + # use TensorProductElements we want to return an int. + if isinstance(quadrature_order, tuple): + if len(quadrature_order) == 1: + quadrature_order = quadrature_order[0] + if isinstance(quadrature_order, tuple): + if not get_option('sumfact'): + raise NotImplementedError("Different quadrature order per direction is only implemented for kernels using sum factorization.") + from dune.perftool.pdelab.geometry import world_dimension + assert(len(quadrature_order) == world_dimension()) + return quadrature_order diff --git a/python/dune/perftool/pdelab/spaces.py b/python/dune/perftool/pdelab/spaces.py index c9883dd67763fdccd37a25859bdf1804423ce6d1..706be5b91a186b990719227fe61f1336d01313a0 100644 --- a/python/dune/perftool/pdelab/spaces.py +++ b/python/dune/perftool/pdelab/spaces.py @@ -6,6 +6,7 @@ from dune.perftool.generation import (class_member, generator_factory, include_file, preamble, + valuearg, ) from dune.perftool.pdelab.restriction import restricted_name from dune.perftool.ufl.modified_terminals import Restriction @@ -73,6 +74,7 @@ def define_lfs(name, father, child): @preamble def define_lfs_size(lfs, element, restriction): name = name_lfs_bound(lfs) + valuearg(name, dtype=numpy.int32) return "auto {} = {}.size();".format(name, lfs) diff --git a/python/dune/perftool/sumfact/accumulation.py b/python/dune/perftool/sumfact/accumulation.py index fa48689f1034952acf21fcab15ca88775d24ea8f..50f4d169935cae6ec421da62ba432506d055f298 100644 --- a/python/dune/perftool/sumfact/accumulation.py +++ b/python/dune/perftool/sumfact/accumulation.py @@ -41,6 +41,7 @@ import loopy as lp import numpy as np import pymbolic.primitives as prim import ufl.classes as uc +from ufl import FiniteElement, MixedElement, TensorProductElement @iname @@ -138,8 +139,7 @@ def get_accumulation_info(expr, visitor): def _get_childs(element): - from ufl import FiniteElement - if isinstance(element, FiniteElement): + if isinstance(element, (FiniteElement, TensorProductElement)): yield (0, element) else: for i in range(element.value_size()): @@ -199,10 +199,18 @@ def generate_accumulation_instruction(expr, visitor): test_info = visitor.test_info trial_info = visitor.trial_info + # Number of basis functions per direction leaf_element = test_info.element - if leaf_element.num_sub_elements() > 0: + from ufl import MixedElement + if isinstance(leaf_element, MixedElement): leaf_element = leaf_element.extract_component(test_info.element_index)[1] - basis_size = leaf_element.degree() + 1 + degree = leaf_element._degree + if isinstance(degree, int): + degree = (degree,) * dim + basis_size = tuple(deg + 1 for deg in degree) + + # Anisotropic finite elements are not (yet) supported by Dune + assert(size == basis_size[0] for size in basis_size) from dune.perftool.pdelab.localoperator import boundary_predicates predicates = boundary_predicates(expr, @@ -316,7 +324,7 @@ def generate_accumulation_instruction(expr, visitor): # Collect the lfs and lfs indices for the accumulate call test_lfs.index = flatten_index(tuple(prim.Variable(i) for i in inames), - (basis_size,) * dim, + basis_size, order="f" ) @@ -327,7 +335,7 @@ def generate_accumulation_instruction(expr, visitor): from dune.perftool.sumfact.basis import _basis_functions_per_direction ansatz_lfs.index = flatten_index(tuple(prim.Variable(jacobian_inames[i]) for i in range(world_dimension())), - (_basis_functions_per_direction(trial_leaf_element),) * dim, + _basis_functions_per_direction(trial_leaf_element), order="f" ) diff --git a/python/dune/perftool/sumfact/basis.py b/python/dune/perftool/sumfact/basis.py index a58a7bb7df83123d5756d2ba4b9bb8f7a3d0be33..8e745c7daf65294db5e9f34b03c7a19736918f8c 100644 --- a/python/dune/perftool/sumfact/basis.py +++ b/python/dune/perftool/sumfact/basis.py @@ -42,7 +42,7 @@ from dune.perftool.tools import maybe_wrap_subscript from dune.perftool.pdelab.basis import shape_as_pymbolic from dune.perftool.sumfact.accumulation import sumfact_iname -from ufl import VectorElement, TensorElement +from ufl import MixedElement, VectorElement, TensorElement, TensorProductElement from pytools import product, ImmutableRecord @@ -96,20 +96,30 @@ class LFSSumfactKernelInput(SumfactKernelInputBase, ImmutableRecord): def _basis_functions_per_direction(element): """Number of basis functions per direction """ - from ufl import FiniteElement - assert isinstance(element, FiniteElement) - return element.degree() + 1 + from ufl import FiniteElement, TensorProductElement + assert isinstance(element, (FiniteElement, TensorProductElement)) + degree = element.degree() + if isinstance(degree, int): + degree = (degree,) * world_dimension() + + basis_size = tuple(deg + 1 for deg in degree) + + # Anisotropic finite elements are not (yet) supported by Dune + for size in basis_size: + assert(size == basis_size[0]) + + return basis_size @kernel_cached def pymbolic_coefficient_gradient(element, restriction, index, coeff_func, visitor_indices): sub_element = element grad_index = visitor_indices[0] - if element.num_sub_elements() > 0: + if isinstance(element, MixedElement): sub_element = element.extract_component(index)[1] from ufl import FiniteElement - assert isinstance(sub_element, FiniteElement) + assert isinstance(sub_element, (FiniteElement, TensorProductElement)) # Number of basis functions per direction basis_size = _basis_functions_per_direction(sub_element) @@ -182,6 +192,7 @@ def pymbolic_coefficient(element, restriction, index, coeff_func, visitor_indice @iname def sumfact_lfs_iname(element, bound, dim): + assert(isinstance(bound, int)) from dune.perftool.pdelab.driver import FEM_name_mangling name = "sumfac_lfs_{}_{}".format(FEM_name_mangling(element), dim) domain(name, bound) @@ -190,13 +201,13 @@ def sumfact_lfs_iname(element, bound, dim): @backend(interface="lfs_inames", name="sumfact") def lfs_inames(element, restriction, number=1, context=''): - from ufl import FiniteElement - assert isinstance(element, FiniteElement) + from ufl import FiniteElement, TensorProductElement + assert isinstance(element, (FiniteElement, TensorProductElement)) if number == 0: return () else: dim = world_dimension() - return tuple(sumfact_lfs_iname(element, _basis_functions_per_direction(element), d) for d in range(dim)) + return tuple(sumfact_lfs_iname(element, _basis_functions_per_direction(element)[d], d) for d in range(dim)) @backend(interface="evaluate_basis", name="sumfact") @@ -209,10 +220,18 @@ def evaluate_basis(element, name, restriction): # Collect the pairs of lfs/quad inames that are in use # On facets, the normal direction of the facet is excluded - tab = BasisTabulationMatrix(basis_size=_basis_functions_per_direction(element)) - prod = tuple(tab.pymbolic((prim.Variable(i), prim.Variable(j))) - for (i, j) in zip(quad_inames, tuple(iname for i, iname in enumerate(inames) if i != facedir)) - ) + # + # If facedir is not none, the length of inames and quad_inames is + # different. For inames we want to skip the facedir direction, for + # quad_inames we need all entries. Thats the reason for the + # help_index. + basis_per_dir = _basis_functions_per_direction(element) + prod = () + help_index = 0 + for direction in range(len(inames)): + if direction != facedir: + prod = prod + (BasisTabulationMatrix(basis_size=basis_per_dir[direction]).pymbolic((prim.Variable(quad_inames[help_index]), prim.Variable(inames[direction]))),) + help_index += 1 # Add the missing direction on facedirs by evaluating at either 0 or 1 if facedir is not None: @@ -263,7 +282,7 @@ def evaluate_reference_gradient(element, name, restriction, index): prod = [] for i in range(dim): if i != facedir: - tab = BasisTabulationMatrix(basis_size=_basis_functions_per_direction(element), + tab = BasisTabulationMatrix(basis_size=_basis_functions_per_direction(element)[i], derivative=index == i) prod.append(tab.pymbolic((prim.Variable(quadinamemapping[i]), prim.Variable(inames[i])))) diff --git a/python/dune/perftool/sumfact/quadrature.py b/python/dune/perftool/sumfact/quadrature.py index a5ede367812b2c0c50153538d7bb893f8c077b37..d42296f6aa533049402a6d8f3f45695cddf90339 100644 --- a/python/dune/perftool/sumfact/quadrature.py +++ b/python/dune/perftool/sumfact/quadrature.py @@ -10,13 +10,16 @@ from dune.perftool.generation import (backend, ) from dune.perftool.sumfact.tabulation import (quadrature_points_per_direction, + local_quadrature_points_per_direction, name_oned_quadrature_points, name_oned_quadrature_weights, ) from dune.perftool.pdelab.argument import name_accumulation_variable from dune.perftool.pdelab.geometry import (local_dimension, + world_dimension, ) from dune.perftool.options import get_option +from dune.perftool.sumfact.switch import get_facedir from loopy import CallMangleInfo from loopy.symbolic import FunctionIdentifier @@ -80,52 +83,59 @@ def quadrature_inames(element): if element is None: names = tuple("quad_{}".format(d) for d in range(local_dimension())) else: - from ufl import FiniteElement - assert isinstance(element, FiniteElement) + from ufl import FiniteElement, TensorProductElement + assert isinstance(element, (FiniteElement, TensorProductElement)) from dune.perftool.pdelab.driver import FEM_name_mangling names = tuple("quad_{}_{}".format(FEM_name_mangling(element), d) for d in range(local_dimension())) - domain(names, quadrature_points_per_direction()) + + local_qps_per_dir = local_quadrature_points_per_direction() + domain(names, local_qps_per_dir) return names @iname(kernel="operator") def constructor_quad_iname(name, d, bound): - name = "{}_{}".format(name, d) - domain(name, quadrature_points_per_direction(), kernel="operator") + name = "{}_localdim{}".format(name, d) + domain(name, bound, kernel="operator") return name -def constructor_quadrature_inames(dim, num1d): - name = "quadiname_dim{}_num{}".format(dim, num1d) - return tuple(constructor_quad_iname(name, d, quadrature_points_per_direction()) for d in range(local_dimension())) +def constructor_quadrature_inames(dim): + local_qps_per_dir = local_quadrature_points_per_direction() + local_qps_per_dir_str = '_'.join(map(str, local_qps_per_dir)) + name = "quadiname_dim{}_num{}".format(dim, local_qps_per_dir_str) + return tuple(constructor_quad_iname(name, d, local_qps_per_dir[d]) for d in range(local_dimension())) -def define_recursive_quadrature_weight(visitor, name, dir): +def define_recursive_quadrature_weight(visitor, name, direction): info = visitor.current_info[1] if info is None: element = None else: element = info.element - iname = quadrature_inames(element)[dir] + iname = quadrature_inames(element)[direction] temporary_variable(name, shape=(), shape_impl=()) - instruction(expression=Product((recursive_quadrature_weight(dir + 1), - Subscript(Variable(name_oned_quadrature_weights()), + + qps_per_dir = quadrature_points_per_direction() + qp_bound = qps_per_dir[direction] + instruction(expression=Product((recursive_quadrature_weight(direction + 1), + Subscript(Variable(name_oned_quadrature_weights(qp_bound)), (Variable(iname),) )) ), assignee=Variable(name), - forced_iname_deps=frozenset(quadrature_inames()[dir:]), + forced_iname_deps=frozenset(quadrature_inames()[direction:]), forced_iname_deps_is_final=True, tags=frozenset({"quad"}), ) -def recursive_quadrature_weight(visitor, dir=0): - if dir == local_dimension(): +def recursive_quadrature_weight(visitor, direction=0): + if direction == local_dimension(): return pymbolic_base_weight() else: - name = 'weight_{}'.format(dir) - define_recursive_quadrature_weight(visitor, name, dir) + name = 'weight_{}'.format(direction) + define_recursive_quadrature_weight(visitor, name, direction) return Variable(name) @@ -134,14 +144,16 @@ def quadrature_weight(visitor): if not get_option("precompute_quadrature_info"): return recursive_quadrature_weight(visitor) + # Quadrature points per (local) direction dim = local_dimension() - num1d = quadrature_points_per_direction() - name = "quad_weights_dim{}_num{}".format(dim, num1d) + local_qps_per_dir = local_quadrature_points_per_direction() + local_qps_per_dir_str = '_'.join(map(str, local_qps_per_dir)) + name = "quad_weights_dim{}_num{}".format(dim, local_qps_per_dir_str) # Add a class member loopy_class_member(name, dtype=np.float64, - shape=(num1d,) * dim, + shape=local_qps_per_dir, classtag="operator", dim_tags=",".join(["f"] * dim), managed=True, @@ -149,9 +161,12 @@ def quadrature_weight(visitor): ) # Precompute it in the constructor - inames = constructor_quadrature_inames(dim, num1d) - instruction(assignee=prim.Subscript(prim.Variable(name), tuple(prim.Variable(i) for i in inames)), - expression=prim.Product(tuple(Subscript(Variable(name_oned_quadrature_weights()), (prim.Variable(i),)) for i in inames)), + inames = constructor_quadrature_inames(dim) + assignee = prim.Subscript(prim.Variable(name), tuple(prim.Variable(i) for i in inames)) + expression = prim.Product(tuple(Subscript(Variable(name_oned_quadrature_weights(local_qps_per_dir[index])), + (prim.Variable(iname),)) for index, iname in enumerate(inames))) + instruction(assignee=assignee, + expression=expression, within_inames=frozenset(inames), kernel="operator", ) @@ -165,7 +180,11 @@ def quadrature_weight(visitor): def define_quadrature_position(name, index): - instruction(expression=Subscript(Variable(name_oned_quadrature_points()), (Variable(quadrature_inames()[index]),)), + qps_per_dir = quadrature_points_per_direction() + qp_bound = qps_per_dir[index] + expression = Subscript(Variable(name_oned_quadrature_points(qp_bound)), + (Variable(quadrature_inames()[index]),)) + instruction(expression=expression, assignee=Subscript(Variable(name), (index,)), forced_iname_deps=frozenset(quadrature_inames(element)), forced_iname_deps_is_final=True, @@ -184,12 +203,14 @@ def pymbolic_quadrature_position(index, visitor): assert isinstance(index, int) dim = local_dimension() - num1d = quadrature_points_per_direction() - name = "quad_points_dim{}_num{}_dir{}".format(dim, num1d, index) + qps_per_dir = quadrature_points_per_direction() + local_qps_per_dir = local_quadrature_points_per_direction() + local_qps_per_dir_str = '_'.join(map(str, local_qps_per_dir)) + name = "quad_points_dim{}_num{}_dir{}".format(dim, local_qps_per_dir_str, index) loopy_class_member(name, dtype=np.float64, - shape=(num1d,) * dim, + shape=local_qps_per_dir, classtag="operator", dim_tags=",".join(["f"] * dim), managed=True, @@ -197,9 +218,12 @@ def pymbolic_quadrature_position(index, visitor): ) # Precompute it in the constructor - inames = constructor_quadrature_inames(dim, num1d) - instruction(assignee=prim.Subscript(prim.Variable(name), tuple(prim.Variable(i) for i in inames)), - expression=Subscript(Variable(name_oned_quadrature_points()), (prim.Variable(inames[index]))), + inames = constructor_quadrature_inames(dim) + assignee = prim.Subscript(prim.Variable(name), tuple(prim.Variable(i) for i in inames)) + expression = Subscript(Variable(name_oned_quadrature_points(qps_per_dir[index])), + (prim.Variable(inames[index]))) + instruction(assignee=assignee, + expression=expression, within_inames=frozenset(inames), kernel="operator", ) diff --git a/python/dune/perftool/sumfact/symbolic.py b/python/dune/perftool/sumfact/symbolic.py index 288374daeebaa7780862bb33e2565486e2f7c3f1..a0cc1054afac0a67f312667f7fa4243700c1ccac 100644 --- a/python/dune/perftool/sumfact/symbolic.py +++ b/python/dune/perftool/sumfact/symbolic.py @@ -4,7 +4,7 @@ from dune.perftool.options import get_option from dune.perftool.generation import get_counted_variable from dune.perftool.pdelab.geometry import local_dimension, world_dimension from dune.perftool.sumfact.quadrature import quadrature_inames -from dune.perftool.sumfact.tabulation import BasisTabulationMatrixArray +from dune.perftool.sumfact.tabulation import BasisTabulationMatrixBase, BasisTabulationMatrixArray from pytools import ImmutableRecord, product @@ -107,7 +107,6 @@ class SumfactKernel(SumfactKernelBase, ImmutableRecord, prim.Variable): """ # Assert the inputs! assert isinstance(matrix_sequence, tuple) - from dune.perftool.sumfact.tabulation import BasisTabulationMatrixBase assert all(isinstance(m, BasisTabulationMatrixBase) for m in matrix_sequence) assert stage in (1, 3) diff --git a/python/dune/perftool/sumfact/tabulation.py b/python/dune/perftool/sumfact/tabulation.py index 736f64b282a0bce3a06c4d682e55213ebac9f1ea..e8518668c227c7528ab72712bbd9e2cba92c147f 100644 --- a/python/dune/perftool/sumfact/tabulation.py +++ b/python/dune/perftool/sumfact/tabulation.py @@ -3,7 +3,7 @@ from dune.perftool.ufl.modified_terminals import Restriction from dune.perftool.options import get_option from dune.perftool.pdelab.argument import name_coefficientcontainer -from dune.perftool.pdelab.geometry import world_dimension +from dune.perftool.pdelab.geometry import world_dimension, local_dimension from dune.perftool.generation import (class_member, domain, function_mangler, @@ -51,11 +51,13 @@ class BasisTabulationMatrix(BasisTabulationMatrixBase, ImmutableRecord): slice_size=None, slice_index=None, ): + assert(isinstance(basis_size, int)) if quadrature_size is None: quadrature_size = quadrature_points_per_direction() + assert(qp == quadrature_size[0] for qp in quadrature_size) + quadrature_size = quadrature_size[0] if slice_size is not None: quadrature_size = ceildiv(quadrature_size, slice_size) - ImmutableRecord.__init__(self, quadrature_size=quadrature_size, basis_size=basis_size, @@ -81,13 +83,14 @@ class BasisTabulationMatrix(BasisTabulationMatrixBase, ImmutableRecord): return self.basis_size def pymbolic(self, indices): - name = "{}{}Theta{}{}_qp{}_dof{}".format("face{}_".format(self.face) if self.face is not None else "", - "d" if self.derivative else "", - "T" if self.transpose else "", - "_slice{}".format(self.slice_index) if self.slice_size is not None else "", - self.quadrature_size, - self.basis_size, - ) + name = "{}{}Theta{}{}_qp{}_dof{}" \ + .format("face{}_".format(self.face) if self.face is not None else "", + "d" if self.derivative else "", + "T" if self.transpose else "", + "_slice{}".format(self.slice_index) if self.slice_size is not None else "", + self.quadrature_size, + self.basis_size, + ) define_theta(name, self) return prim.Subscript(prim.Variable(name), indices) @@ -199,51 +202,68 @@ class BasisTabulationMatrixArray(BasisTabulationMatrixBase): def quadrature_points_per_direction(): - # Quadrature order + # Quadrature order per direction q = quadrature_order() + if isinstance(q, int): + q = (q,) * world_dimension() # Quadrature points in per direction - nb_qp = q // 2 + 1 + nb_qp = tuple(order // 2 + 1 for order in q) return nb_qp +def local_quadrature_points_per_direction(): + """On a volume this simply gives the number of quadrature points per + direction. On a face it only returns the worlddim - 1 number of + quadrature points belonging to this face. (Delete normal direction). + """ + qps_per_dir = quadrature_points_per_direction() + if local_dimension() != world_dimension(): + facedir = get_global_context_value('facedir_s') + assert(get_global_context_value('facedir_s') == get_global_context_value('facedir_n') or + get_global_context_value('integral_type') == 'exterior_facet') + qps_per_dir = qps_per_dir[:facedir] + qps_per_dir[facedir + 1:] + return qps_per_dir + + def polynomial_degree(): form = get_global_context_value("formdata").preprocessed_form - return form.coefficients()[0].ufl_element()._degree + degree = form.coefficients()[0].ufl_element().degree() + if isinstance(degree, int): + degree = (degree,) * world_dimension() + return degree def basis_functions_per_direction(): - return polynomial_degree() + 1 + return tuple(degree + 1 for degree in polynomial_degree()) -def define_oned_quadrature_weights(name): +def define_oned_quadrature_weights(name, bound): loopy_class_member(name, dtype=np.float64, classtag="operator", - shape=(quadrature_points_per_direction(),), + shape=(bound,), ) -def name_oned_quadrature_weights(): - num = quadrature_points_per_direction() - name = "qw_num{}".format(num) - define_oned_quadrature_weights(name) +def name_oned_quadrature_weights(bound): + name = "qw_num{}".format(bound) + define_oned_quadrature_weights(name, bound) return name -def define_oned_quadrature_points(name): +def define_oned_quadrature_points(name, bound): loopy_class_member(name, dtype=np.float64, classtag="operator", - shape=(quadrature_points_per_direction(),), + shape=(bound,), ) -def name_oned_quadrature_points(): - num = quadrature_points_per_direction() - name = "qp_num{}".format(num) - define_oned_quadrature_points(name) +def name_oned_quadrature_points(bound): + name = "qp_num{}".format(bound) + define_oned_quadrature_points(name, bound) return name @@ -284,12 +304,12 @@ def name_polynomials(degree): @preamble(kernel="operator") -def sort_quadrature_points_weights(qp, qw): +def sort_quadrature_points_weights(qp, qw, bound): range_field = lop_template_range_field() domain_field = name_domain_field() - number_qp = quadrature_points_per_direction() include_file("dune/perftool/sumfact/onedquadrature.hh", filetag="operatorfile") - return "onedQuadraturePointsWeights<{}, {}, {}>({}, {});".format(range_field, domain_field, number_qp, qp, qw) + return "onedQuadraturePointsWeights<{}, {}, {}>({}, {});" \ + .format(range_field, domain_field, bound, qp, qw) @iname(kernel="operator") @@ -325,9 +345,12 @@ def polynomial_lookup_mangler(target, func, dtypes): def define_theta(name, tabmat, additional_indices=(), width=None): assert isinstance(tabmat, BasisTabulationMatrix) - qp = name_oned_quadrature_points() - qw = name_oned_quadrature_weights() - sort_quadrature_points_weights(qp, qw) + bound = tabmat.quadrature_size + if tabmat.slice_size is not None: + bound *= tabmat.slice_size + qp = name_oned_quadrature_points(bound) + qw = name_oned_quadrature_weights(bound) + sort_quadrature_points_weights(qp, qw, bound) degree = tabmat.basis_size - 1 polynomials = name_polynomials(degree) @@ -370,17 +393,18 @@ def define_theta(name, tabmat, additional_indices=(), width=None): def construct_basis_matrix_sequence(transpose=False, derivative=None, facedir=None, facemod=None, basis_size=None): dim = world_dimension() result = [None] * dim + quadrature_size = quadrature_points_per_direction() + assert (basis_size is not None) + if facedir is not None: + quadrature_size = quadrature_size[:facedir] + (1,) + quadrature_size[facedir:] for i in range(dim): - quadrature_size = quadrature_points_per_direction() - onface = None if facedir == i: - quadrature_size = 1 onface = facemod - result[i] = BasisTabulationMatrix(quadrature_size=quadrature_size, - basis_size=basis_size, + result[i] = BasisTabulationMatrix(quadrature_size=quadrature_size[i], + basis_size=basis_size[i], transpose=transpose, derivative=derivative == i, face=onface) diff --git a/python/dune/perftool/sumfact/vectorization.py b/python/dune/perftool/sumfact/vectorization.py index 790fa59a1f052078800785cf10ed35fbf988da03..d5e855291816e27cc6627591a54bfa32b43bc1a6 100644 --- a/python/dune/perftool/sumfact/vectorization.py +++ b/python/dune/perftool/sumfact/vectorization.py @@ -9,7 +9,6 @@ from dune.perftool.generation import (generator_factory, from dune.perftool.pdelab.restriction import (Restriction, restricted_name, ) -from dune.perftool.sumfact.tabulation import BasisTabulationMatrixArray from dune.perftool.error import PerftoolError from dune.perftool.options import get_option diff --git a/python/dune/perftool/ufl/visitor.py b/python/dune/perftool/ufl/visitor.py index b4283031662228c785235c4e5f69fd21f032c0e9..7e6187e0bf30b79a6d770a80c44492ee2d6835be 100644 --- a/python/dune/perftool/ufl/visitor.py +++ b/python/dune/perftool/ufl/visitor.py @@ -23,7 +23,9 @@ from pymbolic.primitives import (Call, from ufl.algorithms import MultiFunction from ufl.checks import is_cellwise_constant from ufl import (VectorElement, + MixedElement, TensorElement, + TensorProductElement, ) from ufl.classes import (FixedIndex, IndexSum, @@ -98,7 +100,7 @@ class UFL2LoopyVisitor(ModifiedTerminalTracker): leaf_element = o.ufl_element() # Select the correct leaf element in the case of this being a mixed finite element - if o.ufl_element().num_sub_elements() > 0: + if isinstance(o.ufl_element(), MixedElement): index = self.indices[0] assert isinstance(index, int) self.indices = self.indices[1:] @@ -127,7 +129,7 @@ class UFL2LoopyVisitor(ModifiedTerminalTracker): self.interface.initialize_function_spaces(o, self) index = None - if o.ufl_element().num_sub_elements() > 0: + if isinstance(o.ufl_element(), MixedElement): index = self.indices[0] assert isinstance(index, int) self.indices = self.indices[1:] diff --git a/test/sumfact/poisson/poisson_2d.mini b/test/sumfact/poisson/poisson_2d.mini index ea68946824a53387f3f6ce6f19f0349ef309af05..129c2ff373937cd9fecd86dcdf38b78723fee339 100644 --- a/test/sumfact/poisson/poisson_2d.mini +++ b/test/sumfact/poisson/poisson_2d.mini @@ -18,6 +18,7 @@ numerical_jacobian = 1, 0 | expand num compare_l2errorsquared = 4e-5, 4e-9 | expand deg sumfact = 1 vectorize_grads = 1, 0 | expand grad +quadrature_order = 2, 4 [formcompiler.ufl_variants] degree = 1, 2 | expand deg diff --git a/test/sumfact/poisson/poisson_2d.ufl b/test/sumfact/poisson/poisson_2d.ufl index 0d494b70453fa4cc15bae46080540c39d550c520..d2c78a8d2a1a928ae1f941db4e1a337c8d308bbb 100644 --- a/test/sumfact/poisson/poisson_2d.ufl +++ b/test/sumfact/poisson/poisson_2d.ufl @@ -5,7 +5,10 @@ c = (0.5-x[0])**2 + (0.5-x[1])**2 g = exp(-1.*c) f = 2*(2.-2*c)*g -V = FiniteElement("CG", cell, degree) +V_0 = FiniteElement("CG", interval, degree) +V_1 = FiniteElement("CG", interval, degree) +V = TensorProductElement(V_0, V_1, cell=cell) + u = TrialFunction(V) v = TestFunction(V)