From 3cb3511c6234f43d45a0e26a25e20222c40403b4 Mon Sep 17 00:00:00 2001 From: Dominic Kempf <dominic.kempf@iwr.uni-heidelberg.de> Date: Mon, 10 Dec 2018 11:40:31 +0100 Subject: [PATCH] Implement sumfact_multilinear Geometry Mixin --- python/dune/codegen/options.py | 2 +- python/dune/codegen/pdelab/__init__.py | 13 - python/dune/codegen/sumfact/geometry.py | 577 ++++++------------ python/dune/codegen/sumfact/quadrature.py | 12 +- python/dune/codegen/ufl/visitor.py | 6 +- .../poisson/poisson_2d_unstructured.mini | 1 + .../poisson/poisson_3d_unstructured.mini | 1 + test/sumfact/poisson/poisson_dg_2d_gmsh.mini | 1 + .../poisson/poisson_dg_2d_unstructured.mini | 1 + test/sumfact/poisson/poisson_dg_3d_gmsh.mini | 1 + .../poisson/poisson_dg_3d_unstructured.mini | 1 + .../poisson/poisson_fastdg_3d_gmsh.mini | 1 + 12 files changed, 199 insertions(+), 418 deletions(-) diff --git a/python/dune/codegen/options.py b/python/dune/codegen/options.py index 32ede8d4..4a5a913d 100644 --- a/python/dune/codegen/options.py +++ b/python/dune/codegen/options.py @@ -113,7 +113,7 @@ class CodegenFormOptionsArray(ImmutableRecord): geometry_mixins = CodegenOption(default="generic", helpstr="A comma separated list of mixin identifiers to use for geometries. Currently implemented mixins: generic, axiparallel, equidistant, sumfact_multilinear, sumfact_axiparallel, sumfact_equidistant") quadrature_mixins = CodegenOption(default="generic", helpstr="A comma separated list of mixin identifiers to use for quadrature. Currently implemented: generic, sumfact") basis_mixins = CodegenOption(default="generic", helpstr="A comma separated list of mixin identifiers to use for basis function evaluation. Currently implemented: generic, sumfact") - accumulation_mixins = CodegenOption(default="generic", helpstr="A comma separated list of mixin identifiers to use for accumulation. Currently implemented: generic") + accumulation_mixins = CodegenOption(default="generic", helpstr="A comma separated list of mixin identifiers to use for accumulation. Currently implemented: generic, sumfact") enable_volume = CodegenOption(default=True, helpstr="Whether to assemble volume integrals") enable_skeleton = CodegenOption(default=True, helpstr="Whether to assemble skeleton integrals") enable_boundary = CodegenOption(default=True, helpstr="Whether to assemble boundary integrals") diff --git a/python/dune/codegen/pdelab/__init__.py b/python/dune/codegen/pdelab/__init__.py index 604cacf0..7ca90b96 100644 --- a/python/dune/codegen/pdelab/__init__.py +++ b/python/dune/codegen/pdelab/__init__.py @@ -20,19 +20,6 @@ class PDELabInterface(object): # The visitor instance will be registered by its init method self.visitor = None - # Accumulation interfaces - def get_accumulation_info(self, expr, visitor): - from dune.codegen.pdelab.localoperator import get_accumulation_info - return get_accumulation_info(expr, visitor) - - def list_accumulation_infos(self, expr, visitor): - from dune.codegen.pdelab.localoperator import list_accumulation_infos - return list_accumulation_infos(expr, visitor) - - def generate_accumulation_instruction(self, expr, visitor): - from dune.codegen.pdelab.localoperator import generate_accumulation_instruction - return generate_accumulation_instruction(expr, visitor) - # # TODO: The following ones are actually entirely PDELab independent! # They should be placed elsewhere and be used directly in the visitor. diff --git a/python/dune/codegen/sumfact/geometry.py b/python/dune/codegen/sumfact/geometry.py index e4c23caf..ade47c57 100644 --- a/python/dune/codegen/sumfact/geometry.py +++ b/python/dune/codegen/sumfact/geometry.py @@ -17,7 +17,8 @@ from dune.codegen.generation import (backend, valuearg, ) from dune.codegen.options import get_option -from dune.codegen.pdelab.geometry import (local_dimension, +from dune.codegen.pdelab.geometry import (enforce_boundary_restriction, + local_dimension, world_dimension, name_cell_geometry, name_geometry, @@ -32,8 +33,7 @@ from dune.codegen.pdelab.localoperator import (name_ansatz_gfs_constructor_param from dune.codegen.pdelab.restriction import restricted_name from dune.codegen.sumfact.accumulation import basis_sf_kernels from dune.codegen.sumfact.basis import construct_basis_matrix_sequence -from dune.codegen.sumfact.quadrature import (additional_inames, - default_quadrature_inames) +from dune.codegen.sumfact.quadrature import additional_inames from dune.codegen.sumfact.switch import get_facedir, get_facemod from dune.codegen.sumfact.symbolic import SumfactKernelInterfaceBase, SumfactKernel from dune.codegen.tools import get_pymbolic_basename @@ -46,15 +46,191 @@ from loopy.match import Writes import pymbolic.primitives as prim import numpy as np +import loopy as lp @geometry_mixin("sumfact_multilinear") class SumfactMultiLinearGeometryMixin(GenericPDELabGeometryMixin): - def pymbolic_unit_outer_normal(self): - return pymbolic_unit_outer_normal(self) + def _jacobian_inverse(self): + return "jit" - def pymbolic_jacobian_inverse(self, i, j, restriction): - return pymbolic_jacobian_inverse(i, j, restriction, self.visitor) + def jacobian_inverse(self, o): + # This differs from the generic one by not falling back to the + # transpose of the jacobian inverse. + restriction = enforce_boundary_restriction(self) + + assert(len(self.indices) == 2) + i, j = self.indices + self.indices = None + + name = restricted_name(self._jacobian_inverse(), restriction) + self.define_jacobian_inverse(name, restriction) + + return prim.Subscript(prim.Variable(name), (i,j)) + + def _jacobian_determinant(self): + return "detjac" + + def define_jacobian_determinant(self, o): + restriction = Restriction.NONE if self.measure == "cell" else Restriction.POSITIVE + self.define_jacobian_inverse(self._jacobian_inverse(), restriction) + return prim.Variable(self._jacobian_determinant()) + + def define_jacobian_inverse(self, name, restriction): + """Return jacobian inverse of the geometry mapping (of a cell) + + At the moment this only works for geometry mappings of cells and not for + intersection. We only consider this case as it greatly simplifies code + generation for unstructured grids by making the grid edge consistent and + avoiding the face-twist problem. + """ + # Calculate the jacobian of the geometry mapping using sum factorization + dim = world_dimension() + names_jacobian = [] + for j in range(dim): + for i in range(dim): + name_jacobian = restricted_name("jacobian_geometry_{}{}".format(i, j), restriction) + temporary_variable(name_jacobian) + assignee = prim.Variable(name_jacobian) + expression = _name_jacobian(i, j, restriction, self) + inames = self.quadrature_inames() + additional_inames(self) + instruction(assignee=assignee, + expression=expression, + within_inames=frozenset(inames), + depends_on=frozenset({lp.match.Tagged("sumfact_stage1")}) + ) + names_jacobian.append(name_jacobian) + + # The sum factorization kernels from the geometry evaluation of the + # jacobians will never appear in the expression for the input of + # stage 3. This way the SumfactCollectMapper will never see them + # and they will be marked as inactive. Here we explicitly mark the + # as used. + basis_sf_kernels(expression.aggregate) + + # Calculate the inverse of the jacobian of the geometry mapping and the + # determinant by calling a c++ function. Note: The result will be column + # major -> fortran style. + name_detjac = self._jacobian_determinant() + temporary_variable(name_detjac, shape=()) + ftags = "f,f" + temporary_variable(name, shape=(dim, dim), dim_tags=ftags, managed=True) + include_file('dune/codegen/sumfact/invertgeometry.hh', filetag='operatorfile') + code = "{} = invert_and_return_determinant({}, {});".format(name_detjac, + ", ".join(names_jacobian), + name) + silenced_warning("read_no_write({})".format(name_detjac)) + inames = self.quadrature_inames() + additional_inames(self) + return instruction(code=code, + assignees=frozenset([name, name_detjac]), + read_variables=frozenset(names_jacobian), + inames=frozenset(inames), + ) + + def facet_jacobian_determinant(self, o): + """Absolute value of determinant of jacobian of facet geometry mapping + + Calculate the absolute value of the determinant of the jacobian of the + geometry mapping from the reference cell of the intersection to the + intersection: + + |\det(\nabla \mu_F)| + + This is done using the surface metric tensor lemma from the lecture notes + of Jean-Luc Guermond: + + |\det(\nabla \mu_F)| = \| \tilde{n} \| |\det(\nabla \mu_{T_s})| + + Here \tilde{n} is the outer normal defined by: + + \tilde{n} = (\nabla \mu_{T_s})^{-T} \hat{n}_s + + where \hat{n}_s is the unit outer normal of the corresponding face of the + reference cell. + """ + detjac = self.jacobian_determinant(None) + norm_of_outer_normal = normalize(self.outer_normal(), world_dimension()) + + return prim.Product((detjac, norm_of_outer_normal)) + + def outer_normal(self): + """ This is the *unnormalized* outer normal """ + name = "outer_normal" + facedir_s = get_facedir(Restriction.POSITIVE) + facemod_s = get_facemod(Restriction.POSITIVE) + + temporary_variable(name, shape=(world_dimension(),)) + for i in range(world_dimension()): + assignee = prim.Subscript(prim.Variable(name), i) + self.indices = (facedir_s, i) + ji = self.jacobian_inverse(None) + + # Note: 2*facemod_s-1 because of + # -1 if facemod_s = 0 + # +1 if facemod_s = 1 + expression = ji * (2 * facemod_s - 1) + + inames = self.quadrature_inames() + additional_inames(self) + instruction(assignee=assignee, + expression=expression, + within_inames=frozenset(inames), + ) + + return prim.Variable(name) + + def facet_normal(self, o): + index, = self.indices + self.indices = None + + outer_normal = self.outer_normal() + norm = normalize(outer_normal, world_dimension()) + + return prim.Subscript(outer_normal, (index,)) / norm + + def spatial_coordinate(self, o): + """Calculate global coordinates of quadrature points for multilinear geometry mappings + + The evalualation is done using sum factorization techniques. + + On facets we use the geometry mapping of the self cell to get a global + evaluation of the quadrature points. Avoiding the geometry mapping of the + face itself greatly simplifies the generation of code for unstructured + grids. Instead of looking at all the possible embeddings of the reference + element of the face into the reference element of the cell we can make the + grid edge consistent and choose a suitable convention for the order of + directions in our sum factorization kernels. + """ + assert len(self.indices) == 1 + restriction = enforce_boundary_restriction(self) + + # Generate sum factorization kernel and add vectorization info + matrix_sequence = construct_basis_matrix_sequence(facedir=get_facedir(restriction), + facemod=get_facemod(restriction), + basis_size=(2,) * world_dimension()) + inp = GeoCornersInput(self.indices[0], restriction) + sf = SumfactKernel(matrix_sequence=matrix_sequence, + interface=inp, + ) + + from dune.codegen.sumfact.vectorization import attach_vectorization_info + vsf = attach_vectorization_info(sf) + + # If this sum factorization kernel was not used in the dry run we + # just return 0 + if vsf == 0: + self.indices = None + return 0 + + # Add a sum factorization kernel that implements the evaluation of + # the basis functions at quadrature points (stage 1) + from dune.codegen.sumfact.realization import realize_sum_factorization_kernel + var, _ = realize_sum_factorization_kernel(vsf) + + # The results of this function is already the right component of the + # spatial coordinate and we don't need to index this in the visitor + self.indices = None + + return prim.Subscript(var, vsf.quadrature_index(sf, self)) @geometry_mixin("sumfact_axiparallel") @@ -108,10 +284,10 @@ class SumfactEqudistantGeometryMixin(EquidistantGeometryMixin, SumfactAxiParalle index, = self.indices # Urgh: *SOMEHOW* construct a face direction. This is not breaking in the unstructured - # case, because + # case, because we do not enter this code path... from dune.codegen.pdelab.restriction import Restriction restriction = Restriction.NONE - if get_global_context_value("integral_type") == "interior_facet": + if self.measure == "interior_facet": restriction = Restriction.POSITIVE from dune.codegen.sumfact.switch import get_facedir face = get_facedir(restriction) @@ -209,57 +385,6 @@ class GeoCornersInput(SumfactKernelInterfaceBase, ImmutableRecord): return insn_dep.union(frozenset({insn})) -@backend(interface="spatial_coordinate", name="default") -def pymbolic_spatial_coordinate_multilinear(do_predicates, visitor): - """Calculate global coordinates of quadrature points for multilinear geometry mappings - - The evalualation is done using sum factorization techniques. - - On facets we use the geometry mapping of the self cell to get a global - evaluation of the quadrature points. Avoiding the geometry mapping of the - face itself greatly simplifies the generation of code for unstructured - grids. Instead of looking at all the possible embeddings of the reference - element of the face into the reference element of the cell we can make the - grid edge consistent and choose a suitable convention for the order of - directions in our sum factorization kernels. - """ - assert len(visitor.indices) == 1 - - # Adjust restriction for boundary facets - restriction = visitor.restriction - if get_global_context_value("integral_type") == "exterior_facet": - restriction = 1 - - # Generate sum factorization kernel and add vectorization info - matrix_sequence = construct_basis_matrix_sequence(facedir=get_facedir(restriction), - facemod=get_facemod(restriction), - basis_size=(2,) * world_dimension()) - inp = GeoCornersInput(visitor.indices[0], restriction) - sf = SumfactKernel(matrix_sequence=matrix_sequence, - interface=inp, - ) - - from dune.codegen.sumfact.vectorization import attach_vectorization_info - vsf = attach_vectorization_info(sf) - - # If this sum factorization kernel was not used in the dry run we - # just return 0 - if vsf == 0: - visitor.indices = None - return 0 - - # Add a sum factorization kernel that implements the evaluation of - # the basis functions at quadrature points (stage 1) - from dune.codegen.sumfact.realization import realize_sum_factorization_kernel - var, _ = realize_sum_factorization_kernel(vsf) - - # The results of this function is already the right component of the - # spatial coordinate and we don't need to index this in the visitor - visitor.indices = None - - return prim.Subscript(var, vsf.quadrature_index(sf, visitor)) - - @preamble def define_corner(name, low): geo = name_geometry() @@ -303,260 +428,6 @@ def name_meshwidth(): return name -@backend(interface="spatial_coordinate", name="diagonal_transformation_matrix") -def pymbolic_spatial_coordinate_axiparallel(do_predicates, visitor): - assert len(visitor.indices) == 1 - index, = visitor.indices - - # Urgh: *SOMEHOW* construct a face direction - from dune.codegen.pdelab.restriction import Restriction - restriction = Restriction.NONE - if get_global_context_value("integral_type") == "interior_facet": - restriction = Restriction.POSITIVE - from dune.codegen.sumfact.switch import get_facedir - face = get_facedir(restriction) - - lowcorner = name_lowerleft_corner() - meshwidth = name_meshwidth() - - # If we have to decide which boundary condition to take for this - # intersection we always take the boundary condition of the center - # of the intersection. We assume that there are no cells with more - # than one boundary condition. - if do_predicates: - x = 0.5 - elif index == face: - x = 0 - else: - iindex = index - if face is not None and index > face: - iindex = iindex - 1 - from dune.codegen.sumfact.quadrature import pymbolic_indexed_quadrature_position - x = pymbolic_indexed_quadrature_position(iindex, visitor) - - visitor.indices = None - return prim.Subscript(prim.Variable(lowcorner), (index,)) + x * prim.Subscript(prim.Variable(meshwidth), (index,)) - - -def define_outer_normal(name, visitor): - """Calculate outer normal (length not necessarily 1) - - This will be used to calculate the unit outer normal and the determinant of - the Jacobian of the geometry mapping of the face. - - The outer normal is calculated by multiplying the Jacobian inverse - transposed of the geometry mapping of the 'self'-cell with the unit outer - normal \hat{n_s} of the face corresponding to the intersetcion in the - reference element of the 'self'-cell: - - n = (\nabla \mu_{T_s})^{-T} \hat{n_s} - - Instead of setting up \hat{n_s} we just look at facedir_s and facemod_s. - """ - facedir_s = get_facedir(Restriction.POSITIVE) - facemod_s = get_facemod(Restriction.POSITIVE) - temporary_variable(name, shape=(world_dimension(),)) - for i in range(world_dimension()): - assignee = prim.Subscript(prim.Variable(name), i) - ji = pymbolic_jacobian_inverse(facedir_s, i, Restriction.POSITIVE, visitor) - - # Note: 2*facemod_s-1 because of - # -1 if facemod_s = 0 - # +1 if facemod_s = 1 - expression = ji * (2 * facemod_s - 1) - - inames = default_quadrature_inames(visitor) + additional_inames(visitor) - instruction(assignee=assignee, - expression=expression, - within_inames=frozenset(inames), - ) - - -def name_outer_normal(visitor): - name = "outer_normal" - define_outer_normal(name, visitor) - return name - - -def pymbolic_outer_normal(visitor): - name = name_outer_normal(visitor) - return prim.Variable(name) - - -def define_norm_of_outer_normal(name, visitor): - outer_normal = pymbolic_outer_normal(visitor) - - # Calculate the norm of outer_normal - temporary_variable(name, shape=()) - expression = 0 - for i in range(world_dimension()): - expression = prim.Sum((expression, prim.Subscript(outer_normal, i) * prim.Subscript(outer_normal, i))) - expression = prim.Call(prim.Variable("sqrt"), (expression,)) - inames = default_quadrature_inames(visitor) + additional_inames(visitor) - - # Apparently the call of sqrt shadows the dependency on outer normal. Add manually. - instruction(assignee=prim.Variable(name), - expression=expression, - depends_on=frozenset({Writes(get_pymbolic_basename(outer_normal))}), - within_inames=frozenset(inames), - ) - - -def name_norm_of_outer_normal(visitor): - name = "norm_of_outer_normal" - define_norm_of_outer_normal(name, visitor) - return name - - -def pymbolic_norm_of_outer_normal(visitor): - name = name_norm_of_outer_normal(visitor) - return prim.Variable(name) - - -def pymbolic_unit_outer_normal(visitor): - assert (len(visitor.indices) is 1) - index = visitor.indices[0] - assert isinstance(index, int) - if get_form_option("diagonal_transformation_matrix"): - # In this case we just return -1, 0 or 1 depending on the current - # facemod and facedir. We don't need to (and cannot) index these ints - # so we set the indices of the visitor to None. - visitor.indices = None - - # Use facemod_s and facedir_s - from dune.codegen.sumfact.switch import get_facedir, get_facemod - if index == get_facedir(Restriction.POSITIVE): - if get_facemod(Restriction.POSITIVE): - return 1 - else: - return -1 - else: - return 0 - else: - # In pymbolic_outer_normal we need to create the - # jacobian_inverse_transposed which doesn't play nicely with the - # visitor having indices. Maybe this should be improved. For now let's - # just set them to None and restore them afterwards. - visitor.indices = None - outer_normal = pymbolic_outer_normal(visitor) - norm = pymbolic_norm_of_outer_normal(visitor) - visitor.indices = (index,) - - # Create unit_outer_normal by scaling outer_normal to length 1 - name = "unit_outer_normal" - temporary_variable(name, shape=(world_dimension(),)) - inames = default_quadrature_inames(visitor) + additional_inames(visitor) - for i in range(world_dimension()): - instruction(assignee=prim.Subscript(prim.Variable(name), i), - expression=prim.Subscript(outer_normal, i) / norm, - within_inames=frozenset(inames), - ) - - return prim.Variable(name) - - -def pymbolic_unit_inner_normal(visitor_indices): - # Note: The implementation of this was adjusted for unstructured grids but - # it was not properly tested. If you need the unit inner normal just remove - # the assert(False) statement but make sure to verify that this does the - # right thing. - assert(False) - - assert (len(visitor.indices) is 1) - index = visitor.indices[0] - assert isinstance(index, int) - if get_form_option("diagonal_transformation_matrix"): - # In this case we just return -1, 0 or 1 depending on the current - # facemod and facedir. We don't need to (and cannot) index these ints - # so we set the indices of the visitor to None. - visitor.indices = None - if index == get_facedir(Restriction.POSITIVE): - if get_facemod(Restriction.POSITIVE): - return -1 - else: - return 1 - else: - return 0 - else: - # In pymbolic_outer_normal we need to create the - # jacobian_inverse_transposed which doesn't play nicely with the - # visitor having indices. Maybe this should be improved. For now let's - # just set them to None and restore them afterwards. - visitor.indices = None - outer_normal = pymbolic_outer_normal(visitor) - norm = pymbolic_norm_of_outer_normal(visitor) - visitor.indices = (index,) - - # Create unit_outer_normal by scaling outer_normal to length 1 - name = "unit_outer_normal" - temporary_variable(name, shape=(world_dimension(),)) - - inames = default_quadrature_inames(visitor) + additional_inames(visitor) - for i in range(world_dimension()): - instruction(assignee=prim.Subscript(prim.Variable(name), i), - expression=-1 * prim.Subscript(outer_normal, i) / norm, - within_inames=frozenset(inames), - ) - - return prim.Variable(name) - - -def define_facet_jacobian_determinant(name, visitor): - """Absolute value of determinant of jacobian of facet geometry mapping - - Calculate the absolute value of the determinant of the jacobian of the - geometry mapping from the reference cell of the intersection to the - intersection: - - |\det(\nabla \mu_F)| - - This is done using the surface metric tensor lemma from the lecture notes - of Jean-Luc Guermond: - - |\det(\nabla \mu_F)| = \| \tilde{n} \| |\det(\nabla \mu_{T_s})| - - Here \tilde{n} is the outer normal defined by: - - \tilde{n} = (\nabla \mu_{T_s})^{-T} \hat{n}_s - - where \hat{n}_s is the unit outer normal of the corresponding face of the - reference cell. - """ - # Calculate norm of outer normal and determinant of jacobian of geometry - # mapping of the inside cell -> set restriction to 1 for boundary facets - if get_global_context_value("integral_type") == "exterior_facet": - assert visitor.restriction is 0 - visitor.restriction = 1 - norm_of_outer_normal = pymbolic_norm_of_outer_normal(visitor) - detjac = pymbolic_jacobian_determinant(visitor) - if get_global_context_value("integral_type") == "exterior_facet": - visitor.restriction = 0 - - # Multiply detjac and norm_of_outer_normal - temporary_variable(name, shape=()) - inames = default_quadrature_inames(visitor) + additional_inames(visitor) - instruction(assignee=prim.Variable(name), - expression=detjac * norm_of_outer_normal, - within_inames=frozenset(inames), - ) - - -def name_facet_jacobian_determinant(visitor): - name = "fdetjac" - define_facet_jacobian_determinant(name, visitor) - return name - - -def pymbolic_facet_jacobian_determinant(visitor): - name = name_facet_jacobian_determinant(visitor) - return prim.Variable(name) - - -def pymbolic_facet_area(visitor): - from dune.codegen.pdelab.geometry import pymbolic_facet_area as nonconstant_facet_area - return nonconstant_facet_area() - - def _name_jacobian(i, j, restriction, visitor): """Return the (i, j) component of the jacobian of the geometry mapping @@ -599,79 +470,5 @@ def _name_jacobian(i, j, restriction, visitor): return prim.Subscript(var, vsf.quadrature_index(sf, visitor)) -def define_jacobian_inverse(name, restriction, visitor): - """Return jacobian inverse of the geometry mapping (of a cell) - - At the moment this only works for geometry mappings of cells and not for - intersection. We only consider this case as it greatly simplifies code - generation for unstructured grids by making the grid edge consistent and - avoiding the face-twist problem. - """ - # Calculate the jacobian of the geometry mapping using sum factorization - dim = world_dimension() - names_jacobian = [] - for j in range(dim): - for i in range(dim): - name_jacobian = restricted_name("jacobian_geometry_{}{}".format(i, j), restriction) - temporary_variable(name_jacobian) - assignee = prim.Variable(name_jacobian) - expression = _name_jacobian(i, j, restriction, visitor) - inames = default_quadrature_inames(visitor) + additional_inames(visitor) - instruction(assignee=assignee, - expression=expression, - within_inames=frozenset(inames), - ) - names_jacobian.append(name_jacobian) - - # The sum factorization kernels from the geometry evaluation of the - # jacobians will never appear in the expression for the input of - # stage 3. This way the SumfactCollectMapper will never see them - # and they will be marked as inactive. Here we explicitly mark the - # as used. - basis_sf_kernels(expression.aggregate) - - # Calculate the inverse of the jacobian of the geometry mapping and the - # determinant by calling a c++ function. Note: The result will be column - # major -> fortran style. - name_detjac = name_jacobian_determinant(visitor) - temporary_variable(name_detjac, shape=()) - ftags = ",".join(["f"] * 2) - temporary_variable(name, shape=(dim, dim), dim_tags=ftags, managed=True) - include_file('dune/codegen/sumfact/invertgeometry.hh', filetag='operatorfile') - code = "{} = invert_and_return_determinant({}, {});".format(name_detjac, - ", ".join(names_jacobian), - name) - silenced_warning("read_no_write({})".format(name_detjac)) - inames = default_quadrature_inames(visitor) + additional_inames(visitor) - return instruction(code=code, - assignees=frozenset([name, name_detjac]), - read_variables=frozenset(names_jacobian), - inames=frozenset(inames), - ) - - -def name_jacobian_inverse(restriction, visitor): - name = restricted_name("jacobian_inverse", restriction) - define_jacobian_inverse(name, restriction, visitor) - return name - - -def pymbolic_jacobian_inverse(i, j, restriction, visitor): - name = name_jacobian_inverse(restriction, visitor) - return prim.Subscript(prim.Variable(name), (i, j)) - - -def name_jacobian_determinant(visitor): - name = "detjac" - return name - - -def pymbolic_jacobian_determinant(visitor): - # The calculation of the jacobian determinant happens in this function - if visitor.measure == 'cell': - restriction = 0 - else: - restriction = 1 - name_jacobian_inverse(restriction, visitor) - - return prim.Variable(name_jacobian_determinant(visitor)) +def normalize(expr, dim): + return prim.Call(prim.Variable("sqrt"), (prim.Sum(tuple(expr[i]*expr[i] for i in range(dim))),)) diff --git a/python/dune/codegen/sumfact/quadrature.py b/python/dune/codegen/sumfact/quadrature.py index 92a598d9..86982443 100644 --- a/python/dune/codegen/sumfact/quadrature.py +++ b/python/dune/codegen/sumfact/quadrature.py @@ -141,15 +141,6 @@ def base_weight_function_mangler(target, func, dtypes): return CallMangleInfo(func.name, (NumpyType(dtype_floatingpoint()),), ()) -def default_quadrature_inames(visitor): - info = visitor.current_info[1] - if info is None: - element = None - else: - element = info.element - return quadrature_inames(element) - - @kernel_cached def _quadrature_inames(element): if element is None: @@ -193,8 +184,7 @@ def additional_inames(visitor): restriction = Restriction.NONE if get_global_context_value("integral_type") == "interior_facet": restriction = Restriction.POSITIVE - from dune.codegen.sumfact.basis import lfs_inames - lfs_inames = lfs_inames(element, restriction) + lfs_inames = visitor.lfs_inames(element, restriction) return lfs_inames else: return () diff --git a/python/dune/codegen/ufl/visitor.py b/python/dune/codegen/ufl/visitor.py index 10afd7bf..376fe9d5 100644 --- a/python/dune/codegen/ufl/visitor.py +++ b/python/dune/codegen/ufl/visitor.py @@ -56,14 +56,14 @@ class UFL2LoopyVisitor(ModifiedTerminalTracker): return self._call(o, do_predicates) def accumulate(self, o): - for info in self.interface.list_accumulation_infos(o, self): + for info in self.list_accumulation_infos(o): self.current_info = info expr = self._call(o, False) if expr != 0: if get_form_option("simplify"): from dune.codegen.sympy import simplify_pymbolic_expression expr = simplify_pymbolic_expression(expr) - self.interface.generate_accumulation_instruction(expr, self) + self.generate_accumulation_instruction(expr) def _call(self, o, do_predicates): # Reset state variables @@ -96,7 +96,7 @@ class UFL2LoopyVisitor(ModifiedTerminalTracker): def argument(self, o): self.interface.initialize_function_spaces(o, self) # Update the information on where to accumulate this - info = self.interface.get_accumulation_info(o, self) + info = self.get_accumulation_info(o) if o.number() == 0: if info != self.current_info[0]: self.indices = None diff --git a/test/sumfact/poisson/poisson_2d_unstructured.mini b/test/sumfact/poisson/poisson_2d_unstructured.mini index b7de167f..5eabe782 100644 --- a/test/sumfact/poisson/poisson_2d_unstructured.mini +++ b/test/sumfact/poisson/poisson_2d_unstructured.mini @@ -30,6 +30,7 @@ numerical_jacobian = 1, 0 | expand num sumfact = 1 vectorization_quadloop = 1, 0 | expand quad vectorization_strategy = explicit, none | expand grad +geometry_mixins = sumfact_multilinear [formcompiler.ufl_variants] degree = 1, 2 | expand deg diff --git a/test/sumfact/poisson/poisson_3d_unstructured.mini b/test/sumfact/poisson/poisson_3d_unstructured.mini index 0d8b4690..0cbf2814 100644 --- a/test/sumfact/poisson/poisson_3d_unstructured.mini +++ b/test/sumfact/poisson/poisson_3d_unstructured.mini @@ -30,6 +30,7 @@ numerical_jacobian = 1, 0 | expand num sumfact = 1 vectorization_quadloop = 1, 0 | expand quad vectorization_strategy = explicit, none | expand grad +geometry_mixins = sumfact_multilinear [formcompiler.ufl_variants] degree = 1, 2 | expand deg diff --git a/test/sumfact/poisson/poisson_dg_2d_gmsh.mini b/test/sumfact/poisson/poisson_dg_2d_gmsh.mini index 79df9ea4..054a2481 100644 --- a/test/sumfact/poisson/poisson_dg_2d_gmsh.mini +++ b/test/sumfact/poisson/poisson_dg_2d_gmsh.mini @@ -29,6 +29,7 @@ sumfact = 1 sumfact_regular_jacobians = 1 vectorization_quadloop = 1, 0 | expand quad vectorization_strategy = explicit, none | expand grad +geometry_mixins = sumfact_multilinear [formcompiler.ufl_variants] degree = 1, 2 | expand deg diff --git a/test/sumfact/poisson/poisson_dg_2d_unstructured.mini b/test/sumfact/poisson/poisson_dg_2d_unstructured.mini index fec1c93d..55336ae7 100644 --- a/test/sumfact/poisson/poisson_dg_2d_unstructured.mini +++ b/test/sumfact/poisson/poisson_dg_2d_unstructured.mini @@ -30,6 +30,7 @@ numerical_jacobian = 1, 0 | expand num sumfact = 1 vectorization_quadloop = 1, 0 | expand quad vectorization_strategy = explicit, none | expand grad +geometry_mixins = sumfact_multilinear [formcompiler.ufl_variants] degree = 1, 2 | expand deg diff --git a/test/sumfact/poisson/poisson_dg_3d_gmsh.mini b/test/sumfact/poisson/poisson_dg_3d_gmsh.mini index eb320a2d..0216093f 100644 --- a/test/sumfact/poisson/poisson_dg_3d_gmsh.mini +++ b/test/sumfact/poisson/poisson_dg_3d_gmsh.mini @@ -31,6 +31,7 @@ sumfact = 1 sumfact_regular_jacobians = 1 vectorization_quadloop = 1, 0 | expand quad vectorization_strategy = explicit, none | expand grad +geometry_mixins = sumfact_multilinear [formcompiler.ufl_variants] degree = 1, 2 | expand deg diff --git a/test/sumfact/poisson/poisson_dg_3d_unstructured.mini b/test/sumfact/poisson/poisson_dg_3d_unstructured.mini index 271b1f54..c42be3b9 100644 --- a/test/sumfact/poisson/poisson_dg_3d_unstructured.mini +++ b/test/sumfact/poisson/poisson_dg_3d_unstructured.mini @@ -31,6 +31,7 @@ sumfact = 1 sumfact_regular_jacobians = 1 vectorization_quadloop = 1, 0 | expand quad vectorization_strategy = explicit, none | expand grad +geometry_mixins = sumfact_multilinear [formcompiler.ufl_variants] degree = 1, 2 | expand deg diff --git a/test/sumfact/poisson/poisson_fastdg_3d_gmsh.mini b/test/sumfact/poisson/poisson_fastdg_3d_gmsh.mini index 52de26ab..a6374d18 100644 --- a/test/sumfact/poisson/poisson_fastdg_3d_gmsh.mini +++ b/test/sumfact/poisson/poisson_fastdg_3d_gmsh.mini @@ -30,6 +30,7 @@ fastdg = 1 sumfact_regular_jacobians = 1 vectorization_quadloop = 1, 0 | expand quad vectorization_strategy = explicit, none | expand grad +geometry_mixins = sumfact_multilinear [formcompiler.ufl_variants] degree = 1, 2 | expand deg -- GitLab