diff --git a/python/dune/codegen/blockstructured/accumulation.py b/python/dune/codegen/blockstructured/accumulation.py index 69e7c901029f8cf0fe051b0fd00830a5bdd449b7..89fee47c1dd5e14ffa4f1a276f73d9731144604c 100644 --- a/python/dune/codegen/blockstructured/accumulation.py +++ b/python/dune/codegen/blockstructured/accumulation.py @@ -2,11 +2,11 @@ from dune.codegen.blockstructured.tools import sub_element_inames from dune.codegen.generation import accumulation_mixin, instruction from dune.codegen.loopy.target import dtype_floatingpoint from dune.codegen.options import get_form_option -from dune.codegen.pdelab.geometry import world_dimension +from dune.codegen.pdelab.geometry import world_dimension, name_intersection_geometry_wrapper from dune.codegen.pdelab.localoperator import determine_accumulation_space, GenericAccumulationMixin from dune.codegen.pdelab.argument import name_accumulation_variable from dune.codegen.pdelab.localoperator import boundary_predicates -from dune.codegen.generation.loopy import function_mangler, globalarg +from dune.codegen.generation.loopy import function_mangler, globalarg, temporary_variable import loopy as lp import pymbolic.primitives as prim @@ -17,9 +17,9 @@ from loopy.match import Writes class BlockStructuredAccumulationMixin(GenericAccumulationMixin): def generate_accumulation_instruction(self, expr): if get_form_option('vectorization_blockstructured'): - return generate_accumulation_instruction(expr, self) + return generate_accumulation_instruction_vectorized(expr, self) else: - return GenericAccumulationMixin.generate_accumulation_instruction(self, expr) + return generate_accumulation_instruction(expr, self) def name_accumulation_alias(container, accumspace): @@ -50,17 +50,77 @@ def residual_weight_mangler(knl, func, arg_dtypes): return lp.CallMangleInfo(func, (lp.types.NumpyType(dtype_floatingpoint()),), ()) +def blockstructured_boundary_predicated(measure, subdomain_id): + predicates = [] + + if subdomain_id not in ['everywhere', 'otherwise']: + subelem_inames = sub_element_inames() + + face_id = "face_id" + temporary_variable(face_id, shape=()) + instruction(code="{} = {}.indexInInside();".format(face_id, name_intersection_geometry_wrapper()), + assignees=(face_id,)) + + def face_id_equals(id): + return prim.Comparison(prim.Variable(face_id), "==", id) + + k = get_form_option("number_of_blocks") + + if(world_dimension() == 2): + predicates.append(prim.If(face_id_equals(0), prim.Comparison(prim.Variable(subelem_inames[0]), "==", 0), True)) + predicates.append(prim.If(face_id_equals(1), prim.Comparison(prim.Variable(subelem_inames[0]), "==", k - 1), True)) + predicates.append(prim.If(face_id_equals(2), prim.Comparison(prim.Variable(subelem_inames[1]), "==", 0), True)) + predicates.append(prim.If(face_id_equals(3), prim.Comparison(prim.Variable(subelem_inames[1]), "==", k - 1), True)) + else: + raise NotImplementedError() + return frozenset(predicates) + + def generate_accumulation_instruction(expr, visitor): # Collect the lfs and lfs indices for the accumulate call test_lfs = determine_accumulation_space(visitor.test_info, 0) # In the jacobian case, also determine the space for the ansatz space ansatz_lfs = determine_accumulation_space(visitor.trial_info, 1) + # Collect the lfs and lfs indices for the accumulate call + accumvar = name_accumulation_variable(test_lfs.get_restriction() + ansatz_lfs.get_restriction()) + + predicates = boundary_predicates(visitor.measure, visitor.subdomain_id) + predicates = predicates.union(blockstructured_boundary_predicated(visitor.measure, visitor.subdomain_id)) + + rank = 1 if ansatz_lfs.lfs is None else 2 + + from dune.codegen.pdelab.argument import PDELabAccumulationFunction + from pymbolic.primitives import Call + accexpr = Call(PDELabAccumulationFunction(accumvar, rank), + (test_lfs.get_args() + ansatz_lfs.get_args() + (expr,)) + ) + + from dune.codegen.generation import instruction + quad_inames = visitor.quadrature_inames() + lfs_inames = frozenset(visitor.test_info.inames) + if visitor.trial_info: + lfs_inames = lfs_inames.union(visitor.trial_info.inames) + + instruction(assignees=(), + expression=accexpr, + forced_iname_deps=lfs_inames.union(frozenset(quad_inames)), + forced_iname_deps_is_final=True, + predicates=predicates + ) + +def generate_accumulation_instruction_vectorized(expr, visitor): + # Collect the lfs and lfs indices for the accumulate call + test_lfs = determine_accumulation_space(visitor.test_info, 0) + # In the jacobian case, also determine the space for the ansatz space + ansatz_lfs = determine_accumulation_space(visitor.trial_info, 1) + # Collect the lfs and lfs indices for the accumulate call accumvar = name_accumulation_variable(test_lfs.get_restriction() + ansatz_lfs.get_restriction()) accumvar_alias = name_accumulation_alias(accumvar, test_lfs) predicates = boundary_predicates(visitor.measure, visitor.subdomain_id) + predicates.append(blockstructured_boundary_predicated(visitor.measure, visitor.subdomain_id)) quad_inames = visitor.quadrature_inames() lfs_inames = visitor.test_info.inames diff --git a/python/dune/codegen/blockstructured/geometry.py b/python/dune/codegen/blockstructured/geometry.py index d11bd8d2e915ce3c8a2c70d776250b43c77df092..080624770df0f1fa3666ade3cad1d600ad804b5d 100644 --- a/python/dune/codegen/blockstructured/geometry.py +++ b/python/dune/codegen/blockstructured/geometry.py @@ -15,9 +15,12 @@ from dune.codegen.pdelab.geometry import (AxiparallelGeometryMixin, component_iname, enforce_boundary_restriction, restricted_name, - name_geometry, + name_cell_geometry ) -from dune.codegen.blockstructured.tools import sub_element_inames +from dune.codegen.blockstructured.tools import (sub_element_inames, + name_point_in_macro, + ) +from dune.codegen.ufl.modified_terminals import Restriction import pymbolic.primitives as prim from loopy.match import Writes @@ -25,22 +28,27 @@ from loopy.match import Writes @geometry_mixin("blockstructured_multilinear") class BlockStructuredGeometryMixin(GenericPDELabGeometryMixin): def spatial_coordinate(self, o): - return self.to_global(self.quadrature_position_in_macro()) + if self.measure == 'cell': + return self.to_global(self.quadrature_position_in_macro()) + else: + assert self.measure == 'exterior_facet' or self.measure == 'interior_facet' + micro_qp = self.to_cell(self.quadrature_position_in_micro()) + + macro_qp = prim.Variable(name_point_in_macro(micro_qp, self), ) + + return self.to_global(macro_qp) def to_global(self, local): assert isinstance(local, prim.Expression) name = get_pymbolic_basename(local) + "_global" - if self.measure == 'cell': - compute_multilinear_to_global_transformation(name, local, self) - return prim.Variable(name) - elif self.measure == 'exterior_facet' or self.measure == 'interior_facet': - return GenericPDELabGeometryMixin.to_global(self, local) - else: - raise NotImplementedError + # TODO need to assert somehow that local is of codim 0 + compute_multilinear_to_global_transformation(name, local, self) + return prim.Variable(name) def facet_jacobian_determinant(self, o): - raise NotImplementedError("I think this case was never really implemented") + return prim.Quotient(GenericPDELabGeometryMixin.facet_jacobian_determinant(self, o), + prim.Power(get_form_option("number_of_blocks"), local_dimension())) def jacobian_determinant(self, o): name = 'detjac' @@ -87,16 +95,9 @@ class AxiparallelBlockStructuredGeometryMixin(AxiparallelGeometryMixin, BlockStr assert isinstance(local, prim.Expression) name = get_pymbolic_basename(local) + "_global" - if self.measure == 'cell': - compute_axiparallel_to_global_transformation(name, local, self) - return prim.Variable(name) - elif self.measure == 'exterior_facet' or self.measure == 'interior_facet': - return AxiparallelGeometryMixin.to_global(self, local) - else: - raise NotImplementedError - - def facet_jacobian_determinant(self, o): - raise NotImplementedError("I think this case was never really implemented") + # TODO need to assert somehow that local is of codim 0 + compute_axiparallel_to_global_transformation(name, local, self) + return prim.Variable(name) def jacobian_determinant(self, o): return prim.Quotient(AxiparallelGeometryMixin.jacobian_determinant(self, o), @@ -328,59 +329,6 @@ def name_jacobian_inverse_transposed(restriction, visitor): define_jacobian_inverse_transposed(name, visitor) return name -# -# # scale Jacobian according to the order of the blockstructure -# def pymbolic_jacobian_inverse(i, j, restriction): -# if get_form_option("constant_transformation_matrix"): -# from dune.codegen.pdelab.geometry import name_constant_jacobian_inverse_transposed -# name_jit = name_constant_jacobian_inverse_transposed(restriction) -# else: -# name_jit = name_jacobian_inverse_transposed(restriction) -# return prim.Product((get_form_option("number_of_blocks"), -# prim.Subscript(prim.Variable(name_jit), (j, i)))) -# -# -# # scale determinant according to the order of the blockstructure -# def pymbolic_facet_jacobian_determinant(): -# from dune.codegen.pdelab.geometry import name_facet_jacobian_determinant -# return prim.Quotient(prim.Variable(name_facet_jacobian_determinant()), -# prim.Power(get_form_option("number_of_blocks"), local_dimension())) - - -# translate a point in the micro element into macro coordinates -def define_point_in_macro(name, point_in_micro, visitor): - dim = local_dimension() - if get_form_option('vectorization_blockstructured'): - temporary_variable(name, shape=(dim,), managed=True) - else: - temporary_variable(name, shape=(dim,), shape_impl=('fv',)) - - # point_macro = (point_micro + index_micro) / number_of_blocks - # where index_micro = tensor index of the micro element - subelem_inames = sub_element_inames() - for i in range(dim): - if isinstance(point_in_micro, prim.Subscript): - expr = prim.Subscript(point_in_micro.aggregate, point_in_micro.index + (i,)) - else: - expr = prim.Subscript(point_in_micro, (i,)) - expr = prim.Sum((expr, prim.Variable(subelem_inames[i]),)) - expr = prim.Quotient(expr, get_form_option('number_of_blocks')) - # TODO relax within inames - instruction(assignee=prim.Subscript(prim.Variable(name), (i,)), - expression=expr, - within_inames=frozenset(subelem_inames + visitor.quadrature_inames()), - tags=frozenset({subelem_inames[i]}) - ) - - -# TODO add subelem inames if this function gets called -# TODO change input parameter to string -def name_point_in_macro(point_in_micro, visitor): - assert isinstance(point_in_micro, prim.Expression) - name = get_pymbolic_basename(point_in_micro) + "_macro" - define_point_in_macro(name, point_in_micro, visitor) - return name - def compute_multilinear_to_global_transformation(name, local, visitor): dim = world_dimension() @@ -445,12 +393,20 @@ def compute_axiparallel_to_global_transformation(name, local, visitor): def define_element_corners(name): from dune.codegen.pdelab.driver import get_form n_corners = get_form().ufl_cell().num_vertices() - temporary_variable(name, shape_impl=('fv', 'fv'), shape=(n_corners, local_dimension())) + temporary_variable(name, shape_impl=('fv', 'fv'), shape=(n_corners, world_dimension())) iname = "i_corner" domain(iname, n_corners) - from dune.codegen.generation import instruction - instruction(code="{0}[{1}] = {2}.corner({3});".format(name, iname, name_geometry(), iname), + + it = get_global_context_value("integral_type") + if it == 'cell': + restriction = Restriction.NONE + elif it == 'exterior_facet': + restriction = Restriction.POSITIVE + else: + raise NotImplementedError() + + instruction(code="{0}[{1}] = {2}.corner({3});".format(name, iname, name_cell_geometry(restriction), iname), assignees=frozenset({name}), within_inames=frozenset({iname}), within_inames_is_final=True) diff --git a/python/dune/codegen/blockstructured/tools.py b/python/dune/codegen/blockstructured/tools.py index adf2b7ffc98d99d9676114fb794d3c7694975efa..d636e2cdb368f2dd690f31c5cb6f3a9f196ace8a 100644 --- a/python/dune/codegen/blockstructured/tools.py +++ b/python/dune/codegen/blockstructured/tools.py @@ -2,14 +2,10 @@ from dune.codegen.ufl.modified_terminals import Restriction from dune.codegen.tools import get_pymbolic_basename from dune.codegen.generation import (iname, domain, - get_global_context_value, temporary_variable, instruction) -from dune.codegen.pdelab.geometry import (local_dimension, - world_dimension, - name_localcenter,) - -from dune.codegen.generation.counter import get_counted_variable +from dune.codegen.pdelab.geometry import (world_dimension, + ) from dune.codegen.options import get_form_option import pymbolic.primitives as prim @@ -19,7 +15,7 @@ import pymbolic.primitives as prim @iname def sub_element_inames(): name = "subel" - dim = local_dimension() + dim = world_dimension() dim_names = ["x", "y", "z"] + [str(i) for i in range(4, dim + 1)] inames = tuple() for i in range(dim): @@ -28,72 +24,6 @@ def sub_element_inames(): return inames -# define inames for boundary integration -# In the case of boundary integrationsince we have only d-1 loops, -# but we need always d inames to compute the macro index. -# Therefore we must find out which iname is constant. -# Since loo.py cannot handle if-else blocks very well, this whole -# computation seems a bit cumbersome. -def sub_facet_inames(): - subelem_inames = sub_element_inames() - - center = pymbolic_in_cell_coordinates(prim.Variable(name_localcenter()), Restriction.POSITIVE) - - # check if iname[index] must be constant or not - def predicate(index): - return prim.Comparison(prim.Subscript(center, index), "==", 0.5) - - def conditional_instruction(index): - # instruction for not constant iname - # special case for the third iname - instruction(assignee=prim.Variable(inames[index]), - expression=prim.Variable(subelem_inames[1 if index == 2 else 0]), - within_inames=frozenset(subelem_inames).union(frozenset(quadrature_inames())), - predicates=frozenset([predicate(index)]) - ) - # instruction for constant iname - instruction(assignee=prim.Variable(inames[index]), - expression=prim.Product(((k - 1), prim.Subscript(center, (index,)))), - within_inames=frozenset(subelem_inames).union(frozenset(quadrature_inames())), - predicates=frozenset([prim.LogicalNot(predicate(index))]) - ) - - k = get_form_option("number_of_blocks") - - inames = ("x",) - temporary_variable(inames[0]) - conditional_instruction(0) - if world_dimension() < 3: - inames = inames + ("y",) - temporary_variable(inames[1]) - conditional_instruction(1) - else: - inames = inames + ("y",) - temporary_variable(inames[1]) - # one additional condition is needed in 3d for the second iname - instruction(assignee=prim.Variable(inames[1]), - expression=prim.Variable(subelem_inames[0]), - within_inames=frozenset(subelem_inames).union(frozenset(quadrature_inames())), - predicates=frozenset([prim.LogicalAnd((predicate(1), predicate(2)))]) - ) - instruction(assignee=prim.Variable(inames[1]), - expression=prim.Variable(subelem_inames[1]), - within_inames=frozenset(subelem_inames).union(frozenset(quadrature_inames())), - predicates=frozenset([prim.LogicalAnd((predicate(1), predicate(0)))]) - ) - instruction(assignee=prim.Variable(inames[1]), - expression=prim.Product(((k - 1), prim.Subscript(center, (1,)))), - within_inames=frozenset(subelem_inames).union(frozenset(quadrature_inames())), - predicates=frozenset([prim.LogicalNot(predicate(1))]) - ) - - inames = inames + ("z",) - temporary_variable(inames[2]) - conditional_instruction(2) - - return inames - - # compute sequential index for given tensor index, the same as index in base-k to base-10 def tensor_index_to_sequential_index(indices, k): return prim.Sum(tuple(prim.Variable(index) * k ** i for i, index in enumerate(indices))) @@ -106,11 +36,7 @@ def sequential_index_to_tensor_index(iname, k): # compute index for higher order FEM for a given Q1 index def micro_index_to_macro_index(element, inames): - it = get_global_context_value("integral_type") - if it == "cell": - subelem_inames = sub_element_inames() - elif it == "exterior_facet" or it == "interior_facet": - subelem_inames = sub_facet_inames() + subelem_inames = sub_element_inames() k = get_form_option("number_of_blocks") p = element.degree() @@ -120,7 +46,8 @@ def micro_index_to_macro_index(element, inames): # translate a point in the micro element into macro coordinates def define_point_in_macro(name, point_in_micro, visitor): - dim = local_dimension() + # TODO this won't work for 2d mannifolds + dim = world_dimension() if get_form_option('vectorization_blockstructured'): temporary_variable(name, shape=(dim,), managed=True) else: