diff --git a/examples/geometry.py b/examples/geometry.py new file mode 100644 index 0000000000000000000000000000000000000000..f332741576f12174241e6ba706df9a226189fa46 --- /dev/null +++ b/examples/geometry.py @@ -0,0 +1,60 @@ +"""Minimal example of a grudge driver.""" + +from __future__ import division, print_function + +__copyright__ = "Copyright (C) 2015 Andreas Kloeckner" + +__license__ = """ +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +""" + +import numpy as np # noqa +import pyopencl as cl +from grudge import sym, bind, Discretization, shortcuts + + +def main(write_output=True): + cl_ctx = cl.create_some_context() + queue = cl.CommandQueue(cl_ctx) + + from meshmode.mesh.generation import generate_regular_rect_mesh + mesh = generate_regular_rect_mesh(a=(-0.5, -0.5), b=(0.5, 0.5)) + + discr = Discretization(cl_ctx, mesh, order=4) + + sym_op = sym.normal(sym.BTAG_ALL, mesh.dim) + #sym_op = sym.nodes(mesh.dim, where=sym.BTAG_ALL) + print(sym.pretty(sym_op)) + op = bind(discr, sym_op) + print() + print(op.code) + + vec = op(queue) + + bvis = shortcuts.make_boundary_visualizer(discr, 4) + + print(vec[0].shape) + print(discr.zeros(queue).shape) + bvis.write_vtk_file("geo.vtu", [ + ("normals", vec) + ]) + + +if __name__ == "__main__": + main() diff --git a/grudge/discretization.py b/grudge/discretization.py index c9d988448ac8c11043dfa9dc2ad75f3983366b5a..7fb4562df39a35c8c5278f642b47b8fd642bc654 100644 --- a/grudge/discretization.py +++ b/grudge/discretization.py @@ -89,5 +89,9 @@ class Discretization(object): def zeros(self, queue, dtype=None, extra_dims=None): return self.volume_discr.zeros(queue, dtype, extra_dims=None) + def is_volume_where(self, where): + from grudge import sym + return where == sym.VTAG_ALL + # vim: foldmethod=marker diff --git a/grudge/execution.py b/grudge/execution.py index df8f0724e116fdddef294b68d764f523a3e787fd..030ab447bd3918347b3df1fde2d835520565015a 100644 --- a/grudge/execution.py +++ b/grudge/execution.py @@ -22,8 +22,10 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ +import six import numpy as np import grudge.symbolic.mappers as mappers +from grudge import sym import logging logger = logging.getLogger(__name__) @@ -56,13 +58,12 @@ class ExecutionMapper(mappers.Evaluator, if expr.quadrature_tag is not None: raise NotImplementedError("node coordinate components on quad. grids") - return self.discr.volume_discr.nodes()[expr.axis] \ - .with_queue(self.queue) + if self.discr.is_volume_where(expr.where): + discr = self.discr.volume_discr + else: + discr = self.discr.boundary_discr - def map_normal_component(self, expr): - if expr.quadrature_tag is not None: - raise NotImplementedError("normal components on quad. grids") - return self.discr.boundary_normals(expr.boundary_tag)[expr.axis] + return discr.nodes()[expr.axis].with_queue(self.queue) def map_boundarize(self, op, field_expr): return self.discr.boundarize_volume_field( @@ -72,26 +73,13 @@ class ExecutionMapper(mappers.Evaluator, def map_scalar_parameter(self, expr): return self.context[expr.name] - def map_jacobian(self, expr): - return self.discr.volume_jacobians(expr.quadrature_tag) - - def map_forward_metric_derivative(self, expr): - return (self.discr.forward_metric_derivatives(expr.quadrature_tag) - [expr.xyz_axis][expr.rst_axis]) - - def map_inverse_metric_derivative(self, expr): - return (self.discr.inverse_metric_derivatives(expr.quadrature_tag) - [expr.xyz_axis][expr.rst_axis]) - def map_call(self, expr): from pymbolic.primitives import Variable assert isinstance(expr.function, Variable) - func_name = expr.function.name - try: - func = self.discr.exec_functions[func_name] - except KeyError: - func = getattr(np, expr.function.name) + # FIXME: Make a way to register functions + import pyopencl.clmath as clmath + func = getattr(clmath, expr.function.name) return func(*[self.rec(p) for p in expr.parameters]) @@ -241,7 +229,8 @@ class ExecutionMapper(mappers.Evaluator, # }}} - # {{{ code execution functions -------------------------------------------- + # {{{ code execution functions + def exec_assign(self, insn): return [(name, self.rec(expr)) for name, expr in zip(insn.names, insn.exprs)], [] @@ -254,7 +243,8 @@ class ExecutionMapper(mappers.Evaluator, else: stats_callback = None - if insn.flop_count() == 0: + # FIXME: Reenable compiled vector exprs + if True: # insn.flop_count() == 0: return [(name, self(expr)) for name, expr in zip(insn.names, insn.exprs)], [] else: @@ -387,9 +377,22 @@ class ExecutionMapper(mappers.Evaluator, return result, [] def exec_diff_batch_assign(self, insn): - rst_diff = self.executor.diff(insn.operators, self.rec(insn.field)) + field = self.rec(insn.field) + repr_op = insn.operators[0] + if not isinstance(repr_op, sym.ReferenceDifferentiationOperator): + # FIXME + raise NotImplementedError() + + if self.discr.is_volume_where(repr_op.where): + discr = self.discr.volume_discr + else: + discr = self.discr.boundary_discr - return [(name, diff) for name, diff in zip(insn.names, rst_diff)], [] + return [ + (name, discr.num_reference_derivative( + self.queue, (op.rst_axis,), field) + .with_queue(self.queue)) + for name, op in zip(insn.names, insn.operators)], [] exec_quad_diff_batch_assign = exec_diff_batch_assign @@ -496,7 +499,21 @@ class Executor(object): coeffs, matrix, field, out) def __call__(self, queue, **context): - return self.code.execute(ExecutionMapper(queue, context, self)) + import pyopencl.array as cl_array + + def replace_queue(a): + if isinstance(a, cl_array.Array): + return a.with_queue(queue) + else: + return a + + from pytools.obj_array import with_object_array_or_scalar + + new_context = {} + for name, var in six.iteritems(context): + new_context[name] = with_object_array_or_scalar(replace_queue, var) + + return self.code.execute(ExecutionMapper(queue, new_context, self)) # }}} diff --git a/grudge/shortcuts.py b/grudge/shortcuts.py index 0de88a50df5527cd9834401f548cbe9f24c2c1c1..0e6940ff6f120018a8b5c3c9af29de04eb1bddcb 100644 --- a/grudge/shortcuts.py +++ b/grudge/shortcuts.py @@ -25,6 +25,9 @@ THE SOFTWARE. """ +import pyopencl as cl + + def set_up_rk4(field_var_name, dt, fields, rhs, t_start=0): from leap.rk import LSRK4Method from dagrt.codegen import PythonCodeGenerator @@ -39,3 +42,15 @@ def set_up_rk4(field_var_name, dt, fields, rhs, t_start=0): context={dt_method.component_id: fields}) return dt_stepper + + +def make_visualizer(discr, vis_order): + from meshmode.discretization.visualization import make_visualizer + with cl.CommandQueue(discr.cl_context) as queue: + return make_visualizer(queue, discr.volume_discr, vis_order) + + +def make_boundary_visualizer(discr, vis_order): + from meshmode.discretization.visualization import make_visualizer + with cl.CommandQueue(discr.cl_context) as queue: + return make_visualizer(queue, discr.boundary_discr, vis_order) diff --git a/grudge/symbolic/compiler.py b/grudge/symbolic/compiler.py index 2b367255e01eae3236f0f5853502971023646b40..cfbec3c3d7e41e7045bf9c8d157a8312e5821b27 100644 --- a/grudge/symbolic/compiler.py +++ b/grudge/symbolic/compiler.py @@ -526,7 +526,7 @@ class Code(object): if self.static_schedule_attempts: self.last_schedule = schedule - from grudge.tools import with_object_array_or_scalar + from pytools.obj_array import with_object_array_or_scalar return with_object_array_or_scalar(exec_mapper, self.result) # }}} @@ -642,7 +642,7 @@ class OperatorCompiler(mappers.IdentityMapper): # {{{ top-level driver ---------------------------------------------------- def __call__(self, expr, type_hints={}): # Put the result expressions into variables as well. - expr = sym.make_common_subexpression(expr, "_result") + expr = sym.cse(expr, "_result") from grudge.symbolic.mappers.type_inference import TypeInferrer self.typedict = TypeInferrer()(expr, type_hints) @@ -702,7 +702,9 @@ class OperatorCompiler(mappers.IdentityMapper): # Finally, walk the expression and build the code. result = super(OperatorCompiler, self).__call__(expr) - return Code(self.aggregate_assignments(self.code, result), result) + # FIXME: Reenable assignment aggregation + #return Code(self.aggregate_assignments(self.code, result), result) + return Code(self.code, result) # }}} diff --git a/grudge/symbolic/mappers/__init__.py b/grudge/symbolic/mappers/__init__.py index ed8ff2341fed41a0476003eae49f5a5c944e09e2..14fea40a18c1edf788ee660d80a479a12a3e4bfa 100644 --- a/grudge/symbolic/mappers/__init__.py +++ b/grudge/symbolic/mappers/__init__.py @@ -186,10 +186,6 @@ class IdentityMapperMixin(LocalOpReducerMixin, FluxOpReducerMixin): map_ones = map_scalar_parameter map_node_coordinate_component = map_scalar_parameter - map_normal_component = map_elementwise_linear - map_jacobian = map_scalar_parameter - map_inverse_metric_derivative = map_scalar_parameter - map_forward_metric_derivative = map_scalar_parameter map_nodal_sum = map_elementwise_linear map_nodal_min = map_elementwise_linear @@ -258,11 +254,6 @@ class DependencyMapper( map_ones = _map_leaf map_node_coordinate_component = _map_leaf - map_normal_component = _map_leaf - - map_jacobian = _map_leaf - map_forward_metric_derivative = _map_leaf - map_inverse_metric_derivative = _map_leaf class FlopCounter( @@ -283,13 +274,6 @@ class FlopCounter( def map_node_coordinate_component(self, expr): return 0 - def map_normal_component(self, expr): - return 0 - - map_jacobian = map_normal_component - map_forward_metric_derivative = map_normal_component - map_inverse_metric_derivative = map_normal_component - class IdentityMapper( IdentityMapperMixin, @@ -475,27 +459,6 @@ class OperatorSpecializer(CSECachingMapperMixin, IdentityMapper): else: return IdentityMapper.map_operator_binding(self, expr) - def map_normal_component(self, expr): - from grudge.symbolic.mappers.type_inference import ( - NodalRepresentation) - - expr_type = self.typedict[expr] - if not isinstance( - expr_type.repr_tag, - NodalRepresentation): - from grudge.symbolic.primitives import ( - BoundaryNormalComponent) - - # for now, parts of this are implemented. - raise NotImplementedError("normal components on quad. grids") - - return BoundaryNormalComponent( - expr.boundary_tag, expr.axis, - expr_type.repr_tag.quadrature_tag) - - # a leaf, doesn't change - return expr - # }}} @@ -506,25 +469,32 @@ class GlobalToReferenceMapper(CSECachingMapperMixin, IdentityMapper): reference elements, together with explicit multiplication by geometric factors. """ - def __init__(self, dimensions): + def __init__(self, ambient_dim, dim=None): CSECachingMapperMixin.__init__(self) IdentityMapper.__init__(self) - self.dimensions = dimensions + if dim is None: + dim = ambient_dim + + self.ambient_dim = ambient_dim + self.dim = dim map_common_subexpression_uncached = \ IdentityMapper.map_common_subexpression def map_operator_binding(self, expr): - from grudge.symbolic.primitives import ( - Jacobian, InverseMetricDerivative) - # Global-to-reference is run after operator specialization, so # if we encounter non-quadrature operators here, we know they # must be nodal. - def rewrite_derivative(ref_class, field, quadrature_tag=None, + jac_notag = sym.area_element(self.ambient_dim, self.dim, + where=expr.op.where, quadrature_tag=None) + + def rewrite_derivative(ref_class, field, quadrature_tag, where, with_jacobian=True): + jac_tag = sym.area_element(self.ambient_dim, self.dim, + where=expr.op.where, quadrature_tag=quadrature_tag) + if quadrature_tag is not None: diff_kwargs = dict(quadrature_tag=quadrature_tag) else: @@ -532,43 +502,50 @@ class GlobalToReferenceMapper(CSECachingMapperMixin, IdentityMapper): rec_field = self.rec(field) if with_jacobian: - rec_field = Jacobian(quadrature_tag) * rec_field - return sum(InverseMetricDerivative(None, rst_axis, expr.op.xyz_axis) * + rec_field = jac_tag * rec_field + return sum( + sym.inverse_metric_derivative( + rst_axis, expr.op.xyz_axis, + ambient_dim=self.ambient_dim, dim=self.dim) * ref_class(rst_axis, **diff_kwargs)(rec_field) for rst_axis in range(self.dimensions)) if isinstance(expr.op, op.MassOperator): return op.ReferenceMassOperator()( - Jacobian(None) * self.rec(expr.field)) + jac_notag * self.rec(expr.field)) elif isinstance(expr.op, op.QuadratureMassOperator): + jac_tag = sym.area_element(self.ambient_dim, self.dim, + where=expr.op.where, + quadrature_tag=expr.op.quadrature_tag) return op.ReferenceQuadratureMassOperator( - expr.op.quadrature_tag)( - Jacobian(expr.op.quadrature_tag) * self.rec(expr.field)) + expr.op.quadrature_tag, expr.op.where)( + jac_tag * self.rec(expr.field)) elif isinstance(expr.op, op.InverseMassOperator): - return op.ReferenceInverseMassOperator()( - 1/Jacobian(None) * self.rec(expr.field)) + return op.ReferenceInverseMassOperator(expr.op.where)( + 1/jac_notag * self.rec(expr.field)) elif isinstance(expr.op, op.StiffnessOperator): - return op.ReferenceMassOperator()(Jacobian(None) * + return op.ReferenceMassOperator(expr.op.where)(jac_notag * self.rec( op.DifferentiationOperator(expr.op.xyz_axis)(expr.field))) elif isinstance(expr.op, op.DifferentiationOperator): return rewrite_derivative( op.ReferenceDifferentiationOperator, - expr.field, with_jacobian=False) + expr.field, where=expr.op.where, with_jacobian=False) elif isinstance(expr.op, op.StiffnessTOperator): return rewrite_derivative( op.ReferenceStiffnessTOperator, - expr.field) + expr.field, where=expr.op.where) elif isinstance(expr.op, op.QuadratureStiffnessTOperator): return rewrite_derivative( op.ReferenceQuadratureStiffnessTOperator, - expr.field, quadrature_tag=expr.op.quadrature_tag) + expr.field, quadrature_tag=expr.op.quadrature_tag, + where=expr.op.where) elif isinstance(expr.op, op.MInvSTOperator): return self.rec( @@ -590,6 +567,18 @@ class StringifyMapper(pymbolic.mapper.stringifier.StringifyMapper): else: return repr(tag) + def _format_op_props(self, op): + result = "" + + if hasattr(op, "where") and op.where is not None: + result += "@"+self._format_btag(op) + + if (hasattr(op, "quadrature_tag") + and op.quadrature_tag is not None): + result += "Q[%s]" % self.quadrature_tag + + return result + def __init__(self, constant_mapper=str, flux_stringify_mapper=None): pymbolic.mapper.stringifier.StringifyMapper.__init__( self, constant_mapper=constant_mapper) @@ -623,20 +612,21 @@ class StringifyMapper(pymbolic.mapper.stringifier.StringifyMapper): # {{{ global differentiation def map_diff(self, expr, enclosing_prec): - return "Diffx%d" % expr.xyz_axis + return "Diffx%d%s" % (expr.xyz_axi, self._format_op_props(expr)) def map_minv_st(self, expr, enclosing_prec): - return "MInvSTx%d" % expr.xyz_axis + return "MInvSTx%d%s" % (expr.xyz_axi, self._format_op_props(expr)) def map_stiffness(self, expr, enclosing_prec): - return "Stiffx%d" % expr.xyz_axis + return "Stiffx%d%s" % (expr.xyz_axi, self._format_op_props(expr)) def map_stiffness_t(self, expr, enclosing_prec): - return "StiffTx%d" % expr.xyz_axis + return "StiffTx%d%s" % (expr.xyz_axis, self._format_op_props(expr)) def map_quad_stiffness_t(self, expr, enclosing_prec): - return "Q[%s]StiffTx%d" % ( - expr.quadrature_tag, expr.xyz_axis) + return "StiffTx%d%s" % ( + expr.input_quadrature_tag, expr.xyz_axis, + self._format_op_props(expr)) # }}} # {{{ global mass @@ -654,31 +644,34 @@ class StringifyMapper(pymbolic.mapper.stringifier.StringifyMapper): # {{{ reference differentiation def map_ref_diff(self, expr, enclosing_prec): - return "Diffr%d" % expr.rst_axis + return "Diffr%d%s" % (expr.rst_axis, self._format_op_props(expr)) def map_ref_stiffness_t(self, expr, enclosing_prec): - return "StiffTr%d" % expr.rst_axis + return "StiffTr%d%s" % (expr.rst_axis, self._format_op_props(expr)) def map_ref_quad_stiffness_t(self, expr, enclosing_prec): - return "Q[%s]StiffTr%d" % ( - expr.quadrature_tag, expr.rst_axis) + return "StiffTr%dQ[%s]%s" % ( + expr.input_quadrature_tag, expr.rst_axis, + self._format_op_props(expr)) # }}} # {{{ reference mass def map_ref_mass(self, expr, enclosing_prec): - return "RefM" + return "RefM" + self._format_op_props(expr) def map_ref_inverse_mass(self, expr, enclosing_prec): - return "RefInvM" + return "RefInvM" + self._format_op_props(expr) def map_ref_quad_mass(self, expr, enclosing_prec): - return "RefQ[%s]M" % expr.quadrature_tag + return "RefM" + self._format_op_props(expr) # }}} def map_elementwise_linear(self, expr, enclosing_prec): - return "ElWLin:%s" % expr.__class__.__name__ + return "ElWLin:%s%s" % ( + expr.__class__.__name__, + self._format_op_props(expr)) # {{{ flux @@ -723,10 +716,12 @@ class StringifyMapper(pymbolic.mapper.stringifier.StringifyMapper): # }}} def map_elementwise_max(self, expr, enclosing_prec): - return "ElWMax" + return "ElWMax" + self._format_op_props(expr) def map_boundarize(self, expr, enclosing_prec): - return "Boundarize<tag=%s>" % self._format_btag(expr.tag) + return "Boundarize<tag=%s>%s" % ( + self._format_btag(expr.tag), + self._format_op_props(expr)) def map_flux_exchange(self, expr, enclosing_prec): from pymbolic.mapper.stringifier import PREC_NONE @@ -734,10 +729,7 @@ class StringifyMapper(pymbolic.mapper.stringifier.StringifyMapper): ", ".join(self.rec(arg, PREC_NONE) for arg in expr.arg_fields)) def map_ones(self, expr, enclosing_prec): - if expr.quadrature_tag is None: - return "Ones" - else: - return "Q[%s]Ones" % expr.quadrature_tag + return "Ones" + self._format_op_props(expr) # {{{ geometry data @@ -747,33 +739,6 @@ class StringifyMapper(pymbolic.mapper.stringifier.StringifyMapper): else: return ("Q[%s]x[%d]" % (expr.quadrature_tag, expr.axis)) - def map_normal_component(self, expr, enclosing_prec): - if expr.quadrature_tag is None: - return ("Normal<tag=%s>[%d]" - % (self._format_btag(expr.boundary_tag), expr.axis)) - else: - return ("Q[%s]Normal<tag=%s>[%d]" - % (expr.quadrature_tag, self._format_btag(expr.boundary_tag), - expr.axis)) - - def map_jacobian(self, expr, enclosing_prec): - if expr.quadrature_tag is None: - return "Jac" - else: - return "JacQ[%s]" % expr.quadrature_tag - - def map_forward_metric_derivative(self, expr, enclosing_prec): - result = "dx%d/dr%d" % (expr.xyz_axis, expr.rst_axis) - if expr.quadrature_tag is not None: - result += "Q[%s]" % expr.quadrature_tag - return result - - def map_inverse_metric_derivative(self, expr, enclosing_prec): - result = "dr%d/dx%d" % (expr.rst_axis, expr.xyz_axis) - if expr.quadrature_tag is not None: - result += "Q[%s]" % expr.quadrature_tag - return result - # }}} def map_operator_binding(self, expr, enclosing_prec): @@ -789,14 +754,11 @@ class StringifyMapper(pymbolic.mapper.stringifier.StringifyMapper): return "ScalarPar[%s]" % expr.name def map_quad_grid_upsampler(self, expr, enclosing_prec): - return "ToQ[%s]" % expr.quadrature_tag + return "Upsamp" + self._format_op_props(expr) def map_quad_int_faces_grid_upsampler(self, expr, enclosing_prec): return "ToIntFaceQ[%s]" % expr.quadrature_tag - def map_quad_bdry_grid_upsampler(self, expr, enclosing_prec): - return "ToBdryQ[%s,%s]" % (expr.quadrature_tag, expr.boundary_tag) - class PrettyStringifyMapper( pymbolic.mapper.stringifier.CSESplittingStringifyMapperMixin, @@ -1200,10 +1162,6 @@ class _InnerDerivativeJoiner(pymbolic.mapper.RecursiveMapper): return DerivativeJoiner()(expr) map_node_coordinate_component = map_algebraic_leaf - map_normal_component = map_algebraic_leaf - map_jacobian = map_algebraic_leaf - map_inverse_metric_derivative = map_algebraic_leaf - map_forward_metric_derivative = map_algebraic_leaf class DerivativeJoiner(CSECachingMapperMixin, IdentityMapper): @@ -1385,10 +1343,6 @@ class CollectorMixin(OperatorReducerMixin, LocalOpReducerMixin, FluxOpReducerMix map_variable = map_constant map_ones = map_constant map_node_coordinate_component = map_constant - map_normal_component = map_constant - map_jacobian = map_constant - map_forward_metric_derivative = map_constant - map_inverse_metric_derivative = map_constant map_scalar_parameter = map_constant map_c_function = map_constant @@ -1433,12 +1387,9 @@ class BoundaryTagCollector(CollectorMixin, CombineMapper): return set([bpair.tag]) -class GeometricFactorCollector(CollectorMixin, CombineMapper): - def map_jacobian(self, expr): - return set([expr]) - - map_forward_metric_derivative = map_jacobian - map_inverse_metric_derivative = map_jacobian +# I'm not sure this works still. +#class GeometricFactorCollector(CollectorMixin, CombineMapper): +# pass class BoundOperatorCollector(CSECachingMapperMixin, CollectorMixin, CombineMapper): diff --git a/grudge/symbolic/operators.py b/grudge/symbolic/operators.py index c74b083bbde5a2b9afefc638378099c624be2175..923092142e1d7ecf7ee11d596aced56d32c79054 100644 --- a/grudge/symbolic/operators.py +++ b/grudge/symbolic/operators.py @@ -35,6 +35,16 @@ from pytools import Record, memoize_method # {{{ base classes class Operator(pymbolic.primitives.Leaf): + """ + .. attribute:: where + + *None* for the default volume discretization or a boundary + tag for an operation on the denoted part of the boundary. + """ + + def __init__(self, where=None): + self.where = where + def stringifier(self): from grudge.symbolic.mappers import StringifyMapper return StringifyMapper @@ -73,17 +83,15 @@ class Operator(pymbolic.primitives.Leaf): return self.__class__ == other.__class__ and \ self.__getinitargs__() == other.__getinitargs__() - -class StatelessOperator(Operator): def __getinitargs__(self): - return () + return (self.where,) # }}} # {{{ sum, integral, max -class NodalReductionOperator(StatelessOperator): +class NodalReductionOperator(Operator): pass @@ -159,12 +167,12 @@ class QuadratureStiffnessTOperator(DiffOperatorBase): in favor of operators on the reference element. """ - def __init__(self, xyz_axis, quadrature_tag): - DiffOperatorBase.__init__(self, xyz_axis) - self.quadrature_tag = quadrature_tag + def __init__(self, xyz_axis, input_quadrature_tag, where=None): + super(QuadratureStiffnessTOperator, self).__init__(xyz_axis, where=where) + self.input_quadrature_tag = input_quadrature_tag def __getinitargs__(self): - return (self.xyz_axis, self.quadrature_tag) + return (self.xyz_axis, self.input_quadrature_tag, self.where) mapper_method = intern("map_quad_stiffness_t") @@ -179,16 +187,15 @@ def DiffOperatorVector(els): # {{{ reference-element differentiation class ReferenceDiffOperatorBase(Operator): - def __init__(self, rst_axis): - Operator.__init__(self) + def __init__(self, rst_axis, where=None): + super(ReferenceDiffOperatorBase, self).__init__(where) self.rst_axis = rst_axis + if self.where is None: + 1/0 def __getinitargs__(self): - return (self.rst_axis,) - - def preimage_ranges(self, eg): - return eg.ranges + return (self.rst_axis, self.where) def equal_except_for_axis(self, other): return (type(self) == type(other) @@ -197,18 +204,10 @@ class ReferenceDiffOperatorBase(Operator): class ReferenceDifferentiationOperator(ReferenceDiffOperatorBase): - @staticmethod - def matrices(element_group): - return element_group.differentiation_matrices - mapper_method = intern("map_ref_diff") class ReferenceStiffnessTOperator(ReferenceDiffOperatorBase): - @staticmethod - def matrices(element_group): - return element_group.stiffness_t_matrices - mapper_method = intern("map_ref_stiffness_t") @@ -221,22 +220,17 @@ class ReferenceQuadratureStiffnessTOperator(ReferenceDiffOperatorBase): when a :class:`StiffnessTOperator` is applied to a quadrature field. """ - def __init__(self, rst_axis, quadrature_tag): - ReferenceDiffOperatorBase.__init__(self, rst_axis) - self.quadrature_tag = quadrature_tag + def __init__(self, rst_axis, input_quadrature_tag, where=None): + ReferenceDiffOperatorBase.__init__(self, rst_axis, where) + self.input_quadrature_tag = input_quadrature_tag def __getinitargs__(self): - return (self.rst_axis, self.quadrature_tag) + return (self.rst_axis, + self.input_quadrature_tag, + self.where) mapper_method = intern("map_ref_quad_stiffness_t") - def preimage_ranges(self, eg): - return eg.quadrature_info[self.quadrature_tag].ranges - - def matrices(self, element_group): - return element_group.quadrature_info[self.quadrature_tag] \ - .ldis_quad_info.stiffness_t_matrices() - # }}} # }}} @@ -254,7 +248,7 @@ class ElementwiseLinearOperator(Operator): mapper_method = intern("map_elementwise_linear") -class ElementwiseMaxOperator(StatelessOperator): +class ElementwiseMaxOperator(Operator): mapper_method = intern("map_elementwise_max") @@ -267,8 +261,9 @@ class QuadratureGridUpsampler(Operator): In pre-processing, the boundary quad interpolation is specialized to a separate operator, :class:`QuadratureBoundaryGridUpsampler`. """ - def __init__(self, quadrature_tag): + def __init__(self, quadrature_tag, where=None): self.quadrature_tag = quadrature_tag + self.where = where def __getinitargs__(self): return (self.quadrature_tag,) @@ -291,24 +286,6 @@ class QuadratureInteriorFacesGridUpsampler(Operator): mapper_method = intern("map_quad_int_faces_grid_upsampler") - -class QuadratureBoundaryGridUpsampler(Operator): - """ - .. note:: - - This operator is purely for internal use. It is inserted - by :class:`grudge.symbolic.mappers.OperatorSpecializer` - when a :class:`MassOperator` is applied to a quadrature field. - """ - def __init__(self, quadrature_tag, boundary_tag): - self.quadrature_tag = quadrature_tag - self.boundary_tag = boundary_tag - - def __getinitargs__(self): - return (self.quadrature_tag, self.boundary_tag) - - mapper_method = intern("map_quad_bdry_grid_upsampler") - # }}} @@ -345,7 +322,7 @@ class FilterOperator(ElementwiseLinearOperator): return mat -class OnesOperator(ElementwiseLinearOperator, StatelessOperator): +class OnesOperator(ElementwiseLinearOperator): def matrix(self, eg): ldis = eg.local_discretization @@ -353,7 +330,7 @@ class OnesOperator(ElementwiseLinearOperator, StatelessOperator): return np.ones((node_count, node_count), dtype=np.float64) -class AveragingOperator(ElementwiseLinearOperator, StatelessOperator): +class AveragingOperator(ElementwiseLinearOperator): def matrix(self, eg): # average matrix, so that AVE*fields = cellaverage(fields) # see Hesthaven and Warburton page 227 @@ -367,14 +344,14 @@ class AveragingOperator(ElementwiseLinearOperator, StatelessOperator): return avg_mat -class InverseVandermondeOperator(ElementwiseLinearOperator, StatelessOperator): +class InverseVandermondeOperator(ElementwiseLinearOperator): def matrix(self, eg): return np.asarray( la.inv(eg.local_discretization.vandermonde()), order="C") -class VandermondeOperator(ElementwiseLinearOperator, StatelessOperator): +class VandermondeOperator(ElementwiseLinearOperator): def matrix(self, eg): return np.asarray( eg.local_discretization.vandermonde(), @@ -387,7 +364,7 @@ class VandermondeOperator(ElementwiseLinearOperator, StatelessOperator): # {{{ mass operators -class MassOperatorBase(ElementwiseLinearOperator, StatelessOperator): +class MassOperatorBase(ElementwiseLinearOperator): pass @@ -445,11 +422,12 @@ class ReferenceQuadratureMassOperator(Operator): when a :class:`MassOperator` is applied to a quadrature field. """ - def __init__(self, quadrature_tag): + def __init__(self, quadrature_tag, where=None): + super(Operator, self).__init__(self.where) self.quadrature_tag = quadrature_tag def __getinitargs__(self): - return (self.quadrature_tag,) + return (self.quadrature_tag, self.where) mapper_method = intern("map_ref_quad_mass") diff --git a/grudge/symbolic/primitives.py b/grudge/symbolic/primitives.py index 67543ed20a8eeae22a08fdfab5a8aa2a99775ed7..e5c8c2e276dde519c706e274a85f0d3edbb6980a 100644 --- a/grudge/symbolic/primitives.py +++ b/grudge/symbolic/primitives.py @@ -28,10 +28,13 @@ from six.moves import range import numpy as np import pymbolic.primitives -from meshmode.mesh import BTAG_ALL +from meshmode.mesh import BTAG_ALL, BTAG_NONE # noqa from pymbolic.primitives import ( # noqa - make_common_subexpression, If, Comparison) + cse_scope as cse_scope_base, + make_common_subexpression as cse, If, Comparison) +from pymbolic.geometric_algebra import MultiVector +from pytools.obj_array import join_fields, make_obj_array # noqa class LeafBase(pymbolic.primitives.AlgebraicLeaf): @@ -40,8 +43,48 @@ class LeafBase(pymbolic.primitives.AlgebraicLeaf): return StringifyMapper +class VTAG_ALL: + """This is used in a 'where' field to denote the volume discretization.""" + pass + + +__doc__ = """ +Symbols +^^^^^^^ + +.. autoclass:: Field +.. autoclass:: make_sym_vector +.. autoclass:: make_sym_array +.. autoclass:: ScalarParameter +.. autoclass:: CFunction + +Helpers +^^^^^^^ + +.. autoclass:: OperatorBinding +.. autoclass:: PrioritizedSubexpression +.. autoclass:: BoundaryPair +.. autoclass:: Ones + +Geometry data +^^^^^^^^^^^^^ +.. autoclass:: NodeCoordinateComponent +.. autofunction:: nodes +.. autofunction:: mv_nodes +.. autofunction:: forward_metric_derivative +.. autofunction:: inverse_metric_derivative +.. autofunction:: area_element +.. autofunction:: mv_normal +.. autofunction:: normal +""" + + # {{{ variables +class cse_scope(cse_scope_base): # noqa + DISCRETIZATION = "grudge_discretization" + + Field = pymbolic.primitives.Variable make_sym_vector = pymbolic.primitives.make_sym_vector @@ -162,68 +205,64 @@ class BoundaryPair(LeafBase): class Ones(LeafBase): - def __init__(self, quadrature_tag=None): + def __init__(self, quadrature_tag=None, where=None): + self.where = where self.quadrature_tag = quadrature_tag def __getinitargs__(self): - return (self.quadrature_tag,) + return (self.where, self.quadrature_tag,) mapper_method = intern("map_ones") # {{{ geometry data -class NodeCoordinateComponent(LeafBase): - def __init__(self, axis, quadrature_tag=None): - self.axis = axis - self.quadrature_tag = quadrature_tag - - def __getinitargs__(self): - return (self.axis, self.quadrature_tag) - - mapper_method = intern("map_node_coordinate_component") +class DiscretizationProperty(LeafBase): + """ + .. attribute:: where + *None* for the default volume discretization or a boundary + tag for an operation on the denoted part of the boundary. -def nodes(dim, quadrature_tag=None): - return np.array([NodeCoordinateComponent(i, quadrature_tag) - for i in range(dim)], dtype=object) + .. attribute:: quadrature_tag + quadrature tag for the grid on + which this geometric factor is needed, or None for + nodal representation. + """ -class BoundaryNormalComponent(LeafBase): - def __init__(self, boundary_tag, axis, quadrature_tag=None): - self.boundary_tag = boundary_tag - self.axis = axis + def __init__(self, quadrature_tag, where=None): self.quadrature_tag = quadrature_tag + self.where = where def __getinitargs__(self): - return (self.boundary_tag, self.axis, self.quadrature_tag) + return (self.quadrature_tag, self.where) - mapper_method = intern("map_normal_component") +class NodeCoordinateComponent(DiscretizationProperty): -def normal(tag, dimensions): - return np.array([BoundaryNormalComponent(tag, i) - for i in range(dimensions)], dtype=object) + def __init__(self, axis, quadrature_tag=None, where=None): + super(NodeCoordinateComponent, self).__init__(quadrature_tag, where) + self.axis = axis + + def __getinitargs__(self): + return (self.axis, self.quadrature_tag) + mapper_method = intern("map_node_coordinate_component") -class GeometricFactorBase(LeafBase): - def __init__(self, quadrature_tag): - """ - :param quadrature_tag: quadrature tag for the grid on - which this geometric factor is needed, or None for - nodal representation. - """ - self.quadrature_tag = quadrature_tag - def __getinitargs__(self): - return (self.quadrature_tag,) +def nodes(ambient_dim, quadrature_tag=None, where=None): + return np.array([NodeCoordinateComponent(i, quadrature_tag, where) + for i in range(ambient_dim)], dtype=object) -class Jacobian(GeometricFactorBase): - mapper_method = intern("map_jacobian") +def mv_nodes(ambient_dim, quadrature_tag=None, where=None): + return MultiVector( + nodes(ambient_dim, quadrature_tag=quadrature_tag, where=where)) -class ForwardMetricDerivative(GeometricFactorBase): +def forward_metric_derivative(xyz_axis, rst_axis, where=None, + quadrature_tag=None): r""" Pointwise metric derivatives representing @@ -231,48 +270,87 @@ class ForwardMetricDerivative(GeometricFactorBase): \frac{d x_{\mathtt{xyz\_axis}} }{d r_{\mathtt{rst\_axis}} } """ + from grudge.symbolic.operators import ( + ReferenceDifferentiationOperator, QuadratureGridUpsampler) + diff_op = ReferenceDifferentiationOperator( + rst_axis, where=where) - def __init__(self, quadrature_tag, xyz_axis, rst_axis): - """ - :param quadrature_tag: quadrature tag for the grid on - which this geometric factor is needed, or None for - nodal representation. - """ + result = diff_op(NodeCoordinateComponent(xyz_axis, where=where)) - GeometricFactorBase.__init__(self, quadrature_tag) - self.xyz_axis = xyz_axis - self.rst_axis = rst_axis + if quadrature_tag is not None: + result = QuadratureGridUpsampler(quadrature_tag, where)(result) - def __getinitargs__(self): - return (self.quadrature_tag, self.xyz_axis, self.rst_axis) + return cse( + result, + prefix="dx%d_dr%d" % (xyz_axis, rst_axis), + scope=cse_scope.DISCRETIZATION) - mapper_method = intern("map_forward_metric_derivative") +def parametrization_derivative(ambient_dim, dim=None, where=None, + quadrature_tag=None): + if dim is None: + dim = ambient_dim -class InverseMetricDerivative(GeometricFactorBase): - r""" - Pointwise metric derivatives representing + par_grad = np.zeros((ambient_dim, dim), np.object) + for i in range(ambient_dim): + for j in range(dim): + par_grad[i, j] = forward_metric_derivative( + i, j, where=where, quadrature_tag=quadrature_tag) - .. math:: + from pytools import product + return product(MultiVector(vec) for vec in par_grad.T) - \frac{d r_{\mathtt{rst\_axis}} }{d x_{\mathtt{xyz\_axis}} } - """ - def __init__(self, quadrature_tag, rst_axis, xyz_axis): - """ - :param quadrature_tag: quadrature tag for the grid on - which this geometric factor is needed, or None for - nodal representation. - """ +def inverse_metric_derivative(rst_axis, xyz_axis, ambient_dim, dim=None, + quadrature_tag=None): + if dim is None: + dim = ambient_dim - GeometricFactorBase.__init__(self, quadrature_tag) - self.rst_axis = rst_axis - self.xyz_axis = xyz_axis + if dim != ambient_dim: + raise ValueError("not clear what inverse_metric_derivative means if " + "the derivative matrix is not square") + raise NotImplementedError() + + +def pseudoscalar(ambient_dim, dim=None, where=None, quadrature_tag=None): + if dim is None: + dim = ambient_dim + + return cse( + parametrization_derivative(ambient_dim, dim, where=where, + quadrature_tag=quadrature_tag) + .project_max_grade(), + "pseudoscalar", cse_scope.DISCRETIZATION) + + +def area_element(ambient_dim, dim=None, where=None, quadrature_tag=None): + return cse( + CFunction("sqrt")( + pseudoscalar(ambient_dim, dim, where, quadrature_tag=quadrature_tag) + .norm_squared()), + "area_element", cse_scope.DISCRETIZATION) + + +def mv_normal(tag, ambient_dim, dim=None, quadrature_tag=None): + """Exterior unit normal as a MultiVector.""" + + if dim is None: + dim = ambient_dim - 1 + + # Don't be tempted to add a sign here. As it is, it produces + # exterior normals for positively oriented curves. + + pder = ( + pseudoscalar(ambient_dim, dim, tag, quadrature_tag=quadrature_tag) + / + area_element(ambient_dim, dim, tag, quadrature_tag=quadrature_tag)) + return cse(pder.I | pder, "normal", + cse_scope.DISCRETIZATION) - def __getinitargs__(self): - return (self.quadrature_tag, self.rst_axis, self.xyz_axis) - mapper_method = intern("map_inverse_metric_derivative") +def normal(tag, ambient_dim, dim=None, quadrature_tag=None): + return mv_normal(tag, ambient_dim, dim, + quadrature_tag=quadrature_tag).as_vector() # }}}