diff --git a/examples/wave/wave-min.py b/examples/wave/wave-min.py index 8fefcd6c89f0636f0789012b3ceae3b66419ee51..274b9b26b35d7cfa252e80e7869e145604a2e678 100644 --- a/examples/wave/wave-min.py +++ b/examples/wave/wave-min.py @@ -51,7 +51,7 @@ def main(write_output=True): sym_source_center_dist = sym_x - source_center sym_sin = sym.CFunction("sin") sym_exp = sym.CFunction("sin") - sym_t = sym.ScalarParameter("t") + sym_t = sym.ScalarVariable("t") from grudge.models.wave import StrongWaveOperator from meshmode.mesh import BTAG_ALL, BTAG_NONE diff --git a/grudge/__init__.py b/grudge/__init__.py index e156a2a9f7eba187b93e1f544b79f8ce7130fb2d..c6a5d6b777c5fad20e14cbad4aa2b21a32b8f81b 100644 --- a/grudge/__init__.py +++ b/grudge/__init__.py @@ -23,8 +23,5 @@ THE SOFTWARE. """ import grudge.sym # noqa -import grudge.symbolic.flux.primitives as sym_flux # noqa - from grudge.execution import bind # noqa - from grudge.discretization import Discretization # noqa diff --git a/grudge/discretization.py b/grudge/discretization.py index fe847a3cf31fe5468bbd69d81570a6844ddd8f2b..c992e471d392d163876dc51aafad474e29fee482 100644 --- a/grudge/discretization.py +++ b/grudge/discretization.py @@ -24,6 +24,7 @@ THE SOFTWARE. from pytools import memoize_method +from grudge import sym class Discretization(object): @@ -36,6 +37,9 @@ class Discretization(object): .. automethod :: interior_faces_connection .. automethod :: interior_faces_discr + .. automethod :: all_faces_connection + .. automethod :: all_faces_discr + .. autoattribute :: cl_context .. autoattribute :: dim .. autoattribute :: ambient_dim @@ -64,44 +68,101 @@ class Discretization(object): self.order = order self.quad_min_degrees = quad_min_degrees + # {{{ boundary + @memoize_method def boundary_connection(self, boundary_tag, quadrature_tag=None): from meshmode.discretization.poly_element import \ PolynomialWarpAndBlendGroupFactory - if quadrature_tag is not None: + if quadrature_tag is not sym.QTAG_NONE: + # FIXME raise NotImplementedError("quadrature") from meshmode.discretization.connection import make_face_restriction - _, _, boundary_connection = \ - make_face_restriction( + return make_face_restriction( self.volume_discr, PolynomialWarpAndBlendGroupFactory(order=self.order), boundary_tag=boundary_tag) - return self.bounds - def boundary_discr(self, boundary_tag, quadrature_tag=None): return self.boundary_connection(boundary_tag, quadrature_tag).to_discr + # }}} + + # {{{ interior faces + @memoize_method def interior_faces_connection(self, quadrature_tag=None): from meshmode.discretization.poly_element import \ PolynomialWarpAndBlendGroupFactory - if quadrature_tag is not None: + if quadrature_tag is not sym.QTAG_NONE: + # FIXME raise NotImplementedError("quadrature") - from meshmode.discretization.connection import make_face_restriction - _, _, boundary_connection = \ - make_face_restriction( + from meshmode.discretization.connection import ( + make_face_restriction, FRESTR_INTERIOR_FACES) + return make_face_restriction( self.volume_discr, - PolynomialWarpAndBlendGroupFactory(order=self.order)) + PolynomialWarpAndBlendGroupFactory(order=self.order), + FRESTR_INTERIOR_FACES, - return self.bounds + # FIXME: This will need to change as soon as we support + # pyramids or other elements with non-identical face + # types. + per_face_groups=False) - def interior_faces_discr(self, boundary_tag, quadrature_tag=None): - return self.boundary_connection(boundary_tag, quadrature_tag).to_discr + def interior_faces_discr(self, quadrature_tag=None): + return self.interior_faces_connection(quadrature_tag).to_discr + + # }}} + + # {{{ all-faces + + @memoize_method + def all_faces_discr(self, quadrature_tag=None): + if quadrature_tag is not sym.QTAG_NONE: + # FIXME + raise NotImplementedError("quadrature") + + from meshmode.discretization.poly_element import \ + PolynomialWarpAndBlendGroupFactory + from meshmode.discretization.connection import ( + make_face_restriction, FRESTR_ALL_FACES) + return make_face_restriction( + self.volume_discr, + PolynomialWarpAndBlendGroupFactory(order=self.order), + FRESTR_ALL_FACES, + + # FIXME: This will need to change as soon as we support + # pyramids or other elements with non-identical face + # types. + per_face_groups=False) + + @memoize_method + def all_faces_connection(self, boundary_tag, quadrature_tag=None): + """Return a + :class:`meshmode.discretization.connection.DiscretizationConnection` + that goes from either + :meth:`interior_faces_discr` (if *boundary_tag* is None) + or + :meth:`boundary_discr` (if *boundary_tag* is not None) + to a discretization containing all the faces of the volume + discretization. + """ + from meshmode.discretization.connection import \ + make_face_to_all_faces_embedding + + if boundary_tag is None: + faces_conn = self.interior_faces_connection(quadrature_tag) + else: + faces_conn = self.boundary_connection(boundary_tag, quadrature_tag) + + return make_face_to_all_faces_embedding( + faces_conn, self.all_faces_discr(quadrature_tag)) + + # }}} @property def cl_context(self): diff --git a/grudge/execution.py b/grudge/execution.py index 2cf24b316cbfdf80b1859a2f00ff9123d213822e..7531698f323578615533c6171938d14743223821 100644 --- a/grudge/execution.py +++ b/grudge/execution.py @@ -47,27 +47,38 @@ class ExecutionMapper(mappers.Evaluator, self.bound_op = bound_op self.queue = queue + def get_discr(self, dd): + qtag = dd.quadrature_tag + if qtag is None: + # FIXME: Remove once proper quadrature support arrives + qtag = sym.QTAG_NONE + + if dd.is_volume(): + if qtag is not sym.QTAG_NONE: + # FIXME + raise NotImplementedError("quadrature") + return self.discr.volume_discr + + elif dd.domain_tag is sym.FRESTR_ALL_FACES: + return self.discr.all_faces_discr(qtag) + elif dd.domain_tag is sym.FRESTR_INTERIOR_FACES: + return self.discr.interior_faces_discr(qtag) + elif dd.is_boundary(): + return self.discr.boundary_connection(dd.domain_tag, qtag) + else: + raise ValueError("DOF desc tag not understood: " + str(dd)) + # {{{ expression mappings ------------------------------------------------- def map_ones(self, expr): - if expr.quadrature_tag is not None: - # FIXME - raise NotImplementedError("ones on quad. grids") + discr = self.get_discr(expr.dd) - result = self.discr.empty(self.queue, allocator=self.bound_op.allocator) + result = discr.empty(self.queue, allocator=self.bound_op.allocator) result.fill(1) return result def map_node_coordinate_component(self, expr): - if expr.quadrature_tag is not None: - # FIXME - raise NotImplementedError("node coordinate components on quad. grids") - - if self.discr.is_volume_where(expr.where): - discr = self.discr.volume_discr - else: - discr = self.discr.boundary_discr - + discr = self.get_discr(expr.dd) return discr.nodes()[expr.axis].with_queue(self.queue) def map_boundarize(self, op, field_expr): @@ -75,7 +86,7 @@ class ExecutionMapper(mappers.Evaluator, self.rec(field_expr), tag=op.tag, kind=self.discr.compute_kind) - def map_scalar_parameter(self, expr): + def map_grudge_variable(self, expr): return self.context[expr.name] def map_call(self, expr): @@ -127,7 +138,7 @@ class ExecutionMapper(mappers.Evaluator, """{[k,i,j]: 0<=k<nelements and 0<=i,j<ndiscr_nodes}""", - "result[k,i] = sum(j, diff_mat[i, j] * vec[k, j])", + "result[k,i] = sum(j, mat[i, j] * vec[k, j])", default_offset=lp.auto, name="diff") knl = lp.split_iname(knl, "i", 16, inner_tag="l.0") @@ -154,7 +165,7 @@ class ExecutionMapper(mappers.Evaluator, self.bound_op.elwise_linear_cache[cache_key] = matrix - knl()(self.queue, diff_mat=matrix, result=grp.view(result), + knl()(self.queue, mat=matrix, result=grp.view(result), vec=grp.view(field)) return result @@ -283,134 +294,10 @@ class ExecutionMapper(mappers.Evaluator, return zip(compiled.result_names(), compiled(self, stats_callback)), [] - def exec_flux_batch_assign(self, insn): - from pymbolic.primitives import is_zero - - class ZeroSpec: - pass - - class BoundaryZeros(ZeroSpec): - pass - - class VolumeZeros(ZeroSpec): - pass - - def eval_arg(arg_spec): - arg_expr, is_int = arg_spec - arg = self.rec(arg_expr) - if is_zero(arg): - if insn.is_boundary and not is_int: - return BoundaryZeros() - else: - return VolumeZeros() - else: - return arg - - args = [eval_arg(arg_expr) - for arg_expr in insn.flux_var_info.arg_specs] - - from pytools import common_dtype - max_dtype = common_dtype( - [a.dtype for a in args if not isinstance(a, ZeroSpec)], - self.discr.default_scalar_type) - - def cast_arg(arg): - if isinstance(arg, BoundaryZeros): - return self.discr.boundary_zeros( - insn.repr_op.boundary_tag, dtype=max_dtype) - elif isinstance(arg, VolumeZeros): - return self.discr.volume_zeros( - dtype=max_dtype) - elif isinstance(arg, np.ndarray): - return np.asarray(arg, dtype=max_dtype) - else: - return arg - - args = [cast_arg(arg) for arg in args] - - if insn.quadrature_tag is None: - if insn.is_boundary: - face_groups = self.discr.get_boundary(insn.repr_op.boundary_tag)\ - .face_groups - else: - face_groups = self.discr.face_groups - else: - if insn.is_boundary: - face_groups = self.discr.get_boundary(insn.repr_op.boundary_tag)\ - .get_quadrature_info(insn.quadrature_tag).face_groups - else: - face_groups = self.discr.get_quadrature_info(insn.quadrature_tag) \ - .face_groups - - result = [] - - for fg in face_groups: - # grab module - module = insn.get_module(self.discr, max_dtype) - func = module.gather_flux - - # set up argument structure - arg_struct = module.ArgStruct() - for arg_name, arg in zip(insn.flux_var_info.arg_names, args): - setattr(arg_struct, arg_name, arg) - for arg_num, scalar_arg_expr in enumerate( - insn.flux_var_info.scalar_parameters): - setattr(arg_struct, - "_scalar_arg_%d" % arg_num, - self.rec(scalar_arg_expr)) - - fof_shape = (fg.face_count*fg.face_length()*fg.element_count(),) - all_fluxes_on_faces = [ - np.zeros(fof_shape, dtype=max_dtype) - for f in insn.expressions] - for i, fof in enumerate(all_fluxes_on_faces): - setattr(arg_struct, "flux%d_on_faces" % i, fof) - - # make sure everything ended up in Boost.Python attributes - # (i.e. empty __dict__) - assert not arg_struct.__dict__, arg_struct.__dict__.keys() - - # perform gather - func(fg, arg_struct) - - # do lift, produce output - for name, flux_bdg, fluxes_on_faces in zip(insn.names, insn.expressions, - all_fluxes_on_faces): - - if insn.quadrature_tag is None: - if flux_bdg.op.is_lift: - mat = fg.ldis_loc.lifting_matrix() - scaling = fg.local_el_inverse_jacobians - else: - mat = fg.ldis_loc.multi_face_mass_matrix() - scaling = None - else: - assert not flux_bdg.op.is_lift - mat = fg.ldis_loc_quad_info.multi_face_mass_matrix() - scaling = None - - out = self.discr.volume_zeros(dtype=fluxes_on_faces.dtype) - self.bound_op.lift_flux(fg, mat, scaling, fluxes_on_faces, out) - - if self.discr.instrumented: - from grudge.tools import lift_flops - - # correct for quadrature, too. - self.discr.lift_flop_counter.add(lift_flops(fg)) - - result.append((name, out)) - - if not face_groups: - # No face groups? Still assign context variables. - for name, flux_bdg in zip(insn.names, insn.expressions): - result.append((name, self.discr.volume_zeros())) - - return result, [] - def exec_diff_batch_assign(self, insn): field = self.rec(insn.field) repr_op = insn.operators[0] - if not isinstance(repr_op, sym.ReferenceDifferentiationOperator): + if not isinstance(repr_op, sym.RefDiffOperator): # FIXME raise NotImplementedError() @@ -418,10 +305,8 @@ class ExecutionMapper(mappers.Evaluator, # execution-wise. # This should be unified with map_elementwise_linear, which should # be extended to support batching. - if self.discr.is_volume_where(repr_op.where): - discr = self.discr.volume_discr - else: - discr = self.discr.boundary_discr + + discr = self.get_discr(repr_op.dd_in) return [ (name, discr.num_reference_derivative( @@ -429,8 +314,6 @@ class ExecutionMapper(mappers.Evaluator, .with_queue(self.queue)) for name, op in zip(insn.names, insn.operators)], [] - exec_quad_diff_batch_assign = exec_diff_batch_assign - # }}} # }}} @@ -469,12 +352,9 @@ class BoundOperator(object): # {{{ process_sym_operator function def process_sym_operator(sym_operator, post_bind_mapper=None, - dumper=lambda name, sym_operator: None, mesh=None, - type_hints={}): + dumper=lambda name, sym_operator: None, mesh=None): import grudge.symbolic.mappers as mappers - from grudge.symbolic.mappers.bc_to_flux import BCToFluxRewriter - from grudge.symbolic.mappers.type_inference import TypeInferrer dumper("before-bind", sym_operator) sym_operator = mappers.OperatorBinder()(sym_operator) @@ -492,22 +372,19 @@ def process_sym_operator(sym_operator, post_bind_mapper=None, dumper("before-cfold", sym_operator) sym_operator = mappers.CommutativeConstantFoldingMapper()(sym_operator) - dumper("before-bc2flux", sym_operator) - sym_operator = BCToFluxRewriter()(sym_operator) - # Ordering restriction: # # - Must run constant fold before first type inference pass, because zeros, # while allowed, violate typing constraints (because they can't be assigned # a unique type), and need to be killed before the type inferrer sees them. - # - # - Must run BC-to-flux before first type inferrer run so that zeros in - # flux arguments can be removed. - dumper("before-specializer", sym_operator) - sym_operator = mappers.OperatorSpecializer( - TypeInferrer()(sym_operator, type_hints) - )(sym_operator) + # FIXME: Reenable type inference + + # from grudge.symbolic.mappers.type_inference import TypeInferrer + # dumper("before-specializer", sym_operator) + # sym_operator = mappers.OperatorSpecializer( + # TypeInferrer()(sym_operator) + # )(sym_operator) # Ordering restriction: # @@ -531,11 +408,9 @@ def process_sym_operator(sym_operator, post_bind_mapper=None, dumper("before-cfold-2", sym_operator) sym_operator = mappers.CommutativeConstantFoldingMapper()(sym_operator) - dumper("before-derivative-join", sym_operator) - sym_operator = mappers.DerivativeJoiner()(sym_operator) - - dumper("before-boundary-combiner", sym_operator) - sym_operator = mappers.BoundaryCombiner(mesh)(sym_operator) + # FIXME: Reenable derivative joiner + # dumper("before-derivative-join", sym_operator) + # sym_operator = mappers.DerivativeJoiner()(sym_operator) dumper("process-finished", sym_operator) @@ -544,7 +419,7 @@ def process_sym_operator(sym_operator, post_bind_mapper=None, # }}} -def bind(discr, sym_operator, post_bind_mapper=lambda x: x, type_hints={}, +def bind(discr, sym_operator, post_bind_mapper=lambda x: x, debug_flags=set(), allocator=None): # from grudge.symbolic.mappers import QuadratureUpsamplerRemover # sym_operator = QuadratureUpsamplerRemover(self.quad_min_degrees)( @@ -563,12 +438,10 @@ def bind(discr, sym_operator, post_bind_mapper=lambda x: x, type_hints={}, sym_operator = process_sym_operator(sym_operator, post_bind_mapper=post_bind_mapper, dumper=dump_optemplate, - mesh=discr.mesh, - type_hints=type_hints) + mesh=discr.mesh) from grudge.symbolic.compiler import OperatorCompiler - code = OperatorCompiler()(sym_operator, - type_hints=type_hints) + code = OperatorCompiler()(sym_operator) bound_op = BoundOperator(discr, code, debug_flags=debug_flags, allocator=allocator) diff --git a/grudge/models/wave.py b/grudge/models/wave.py index 9591573e0b7cc16b2d86f4b55632b719a5e371bd..8d55942694e90cead770a2985956d992618e6738 100644 --- a/grudge/models/wave.py +++ b/grudge/models/wave.py @@ -31,7 +31,7 @@ import numpy as np from grudge.models import HyperbolicOperator from grudge.models.second_order import CentralSecondDerivative from meshmode.mesh import BTAG_ALL, BTAG_NONE -from grudge import sym, sym_flux +from grudge import sym from pytools.obj_array import join_fields @@ -55,16 +55,16 @@ class StrongWaveOperator(HyperbolicOperator): :math:`c` is assumed to be constant across all space. """ - def __init__(self, c, dimensions, source_f=0, + def __init__(self, c, ambient_dim, source_f=0, flux_type="upwind", dirichlet_tag=BTAG_ALL, dirichlet_bc_f=0, neumann_tag=BTAG_NONE, radiation_tag=BTAG_NONE): - assert isinstance(dimensions, int) + assert isinstance(ambient_dim, int) self.c = c - self.dimensions = dimensions + self.ambient_dim = ambient_dim self.source_f = source_f if self.c > 0: @@ -80,12 +80,10 @@ class StrongWaveOperator(HyperbolicOperator): self.flux_type = flux_type - def flux(self): - dim = self.dimensions - w = sym_flux.FluxVectorPlaceholder(1+dim) + def flux(self, w): u = w[0] v = w[1:] - normal = sym_flux.normal(dim) + normal = sym.normal(w.dd, self.ambient_dim) flux_weak = join_fields( np.dot(v.avg, normal), @@ -108,17 +106,17 @@ class StrongWaveOperator(HyperbolicOperator): return -self.c*flux_strong def sym_operator(self): - d = self.dimensions + d = self.ambient_dim - w = sym.make_sym_vector("w", d+1) + w = sym.make_sym_array("w", d+1) u = w[0] v = w[1:] # boundary conditions ------------------------------------------------- # dirichlet BCs ------------------------------------------------------- - dir_u = sym.RestrictToBoundary(self.dirichlet_tag) * u - dir_v = sym.RestrictToBoundary(self.dirichlet_tag) * v + dir_u = sym.cse(sym.interp("vol", self.dirichlet_tag)(u)) + dir_v = sym.cse(sym.interp("vol", self.dirichlet_tag)(v)) if self.dirichlet_bc_f: # FIXME from warnings import warn @@ -130,25 +128,30 @@ class StrongWaveOperator(HyperbolicOperator): else: dir_bc = join_fields(-dir_u, dir_v) + dir_bc = sym.cse(dir_bc, "dir_bc") + # neumann BCs --------------------------------------------------------- - neu_u = sym.RestrictToBoundary(self.neumann_tag) * u - neu_v = sym.RestrictToBoundary(self.neumann_tag) * v - neu_bc = join_fields(neu_u, -neu_v) + neu_u = sym.cse(sym.interp("vol", self.neumann_tag)(u)) + neu_v = sym.cse(sym.interp("vol", self.neumann_tag)(v)) + neu_bc = sym.cse(join_fields(neu_u, -neu_v), "neu_bc") # radiation BCs ------------------------------------------------------- rad_normal = sym.normal(self.radiation_tag, d) - rad_u = sym.RestrictToBoundary(self.radiation_tag) * u - rad_v = sym.RestrictToBoundary(self.radiation_tag) * v + rad_u = sym.cse(sym.interp("vol", self.radiation_tag)(u)) + rad_v = sym.cse(sym.interp("vol", self.radiation_tag)(v)) - rad_bc = join_fields( + rad_bc = sym.cse(join_fields( 0.5*(rad_u - self.sign*np.dot(rad_normal, rad_v)), 0.5*rad_normal*(np.dot(rad_normal, rad_v) - self.sign*rad_u) - ) + ), "rad_bc") # entire operator ----------------------------------------------------- nabla = sym.nabla(d) - flux_op = sym.get_flux_operator(self.flux()) + + def flux(pair): + return sym.interp(pair.dd, "all_faces")( + self.flux(pair)) result = ( - join_fields( @@ -156,12 +159,13 @@ class StrongWaveOperator(HyperbolicOperator): -self.c*(nabla*u) ) + - sym.InverseMassOperator() * ( - flux_op(w) - + flux_op(sym.BoundaryPair(w, dir_bc, self.dirichlet_tag)) - + flux_op(sym.BoundaryPair(w, neu_bc, self.neumann_tag)) - + flux_op(sym.BoundaryPair(w, rad_bc, self.radiation_tag)) - )) + sym.InverseMassOperator()( + sym.FaceMassOperator()( + flux(sym.int_fpair(w)) + + flux(sym.bv_fpair(self.dirichlet_tag, w, dir_bc)) + + flux(sym.bv_fpair(self.neumann_tag, w, neu_bc)) + + flux(sym.bv_fpair(self.radiation_tag, w, rad_bc)) + ))) result[0] += self.source_f @@ -196,7 +200,7 @@ class VariableVelocityStrongWaveOperator(HyperbolicOperator): """ def __init__( - self, c, dimensions, source=0, + self, c, ambient_dim, source=0, flux_type="upwind", dirichlet_tag=BTAG_ALL, neumann_tag=BTAG_NONE, @@ -207,11 +211,11 @@ class VariableVelocityStrongWaveOperator(HyperbolicOperator): ): """*c* and *source* are optemplate expressions. """ - assert isinstance(dimensions, int) + assert isinstance(ambient_dim, int) self.c = c self.time_sign = time_sign - self.dimensions = dimensions + self.ambient_dim = ambient_dim self.source = source self.dirichlet_tag = dirichlet_tag @@ -224,13 +228,12 @@ class VariableVelocityStrongWaveOperator(HyperbolicOperator): self.diffusion_scheme = diffusion_scheme # {{{ flux ---------------------------------------------------------------- - def flux(self): - dim = self.dimensions - w = sym_flux.FluxVectorPlaceholder(2+dim) - c = w[0] - u = w[1] - v = w[2:] - normal = sym_flux.normal(dim) + def flux(self, w, c_vol): + u = w[0] + v = w[1:] + normal = sym.normal(w.tag, self.ambient_dim) + + c = sym.RestrictToBoundary(w.tag)(c_vol) flux = self.time_sign*1/2*join_fields( c.ext * np.dot(v.ext, normal) @@ -263,9 +266,9 @@ class VariableVelocityStrongWaveOperator(HyperbolicOperator): return do def sym_operator(self, with_sensor=False): - d = self.dimensions + d = self.ambient_dim - w = sym.make_sym_vector("w", d+1) + w = sym.make_sym_array("w", d+1) u = w[0] v = w[1:] @@ -320,14 +323,14 @@ class VariableVelocityStrongWaveOperator(HyperbolicOperator): # strong_form here allows the reuse the value of grad u. grad_tgt = SecondDerivativeTarget( - self.dimensions, strong_form=True, + self.ambient_dim, strong_form=True, operand=arg) self.diffusion_scheme.grad(grad_tgt, bc_getter=None, dirichlet_tags=[], neumann_tags=[]) div_tgt = SecondDerivativeTarget( - self.dimensions, strong_form=False, + self.ambient_dim, strong_form=False, operand=diffusion_coeff*grad_tgt.minv_all) self.diffusion_scheme.div(div_tgt, diff --git a/grudge/symbolic/compiler.py b/grudge/symbolic/compiler.py index 55c5fa6d598c40c7bed6a5751e02b7944add9c62..37dba74606cc7efbf441aa766af8ff59bd39a83a 100644 --- a/grudge/symbolic/compiler.py +++ b/grudge/symbolic/compiler.py @@ -25,7 +25,7 @@ THE SOFTWARE. """ -import six +import six # noqa from six.moves import zip, reduce from pytools import Record, memoize_method, memoize from grudge import sym @@ -140,67 +140,6 @@ class Assign(Instruction): return exec_mapper.exec_assign -class FluxBatchAssign(Instruction): - """ - .. attribute:: names - .. attribute:: expressions - - A list of :class:`grudge.symbolic.primitives.OperatorBinding` - instances bound to flux operators. - - .. note :: - - All operators in :attr:`expressions` are guaranteed to - yield the same operator from - :meth:`grudge.symbolic.operators.FluxOperatorBase.repr_op`. - - .. attribute:: repr_op - - The *repr_op* on which all operators agree. - - .. attribute:: quadrature_tag - """ - - def get_assignees(self): - return set(self.names) - - @memoize_method - def get_dependencies(self): - deps = set() - for wdflux in self.expressions: - deps |= set(wdflux.interior_deps) - deps |= set(wdflux.boundary_deps) - - dep_mapper = _make_dep_mapper(include_subscripts=False) - - from pytools import flatten - return set(flatten(dep_mapper(dep) for dep in deps)) - - def __str__(self): - from grudge.symbolic.flux.mappers import PrettyFluxStringifyMapper as PFSM - flux_strifier = PFSM() - op_strifier = mappers.StringifyMapper(flux_stringify_mapper=flux_strifier) - - from pymbolic.mapper.stringifier import PREC_NONE - - lines = [] - lines.append("{ /* %s */" % self.repr_op) - - lines_expr = [] - for n, f in zip(self.names, self.expressions): - lines_expr.append(" %s <- %s" % (n, op_strifier(f, PREC_NONE))) - - for n, str_f in getattr(flux_strifier, "cse_name_list", []): - lines.append(" (flux-local) %s <- %s" % (n, str_f)) - - lines.extend(lines_expr) - lines.append("}") - return "\n".join(lines) - - def get_execution_method(self, exec_mapper): - return exec_mapper.exec_flux_batch_assign - - class DiffBatchAssign(Instruction): """ :ivar names: @@ -240,11 +179,6 @@ class DiffBatchAssign(Instruction): return exec_mapper.exec_diff_batch_assign -class QuadratureDiffBatchAssign(DiffBatchAssign): - def get_execution_method(self, exec_mapper): - return exec_mapper.exec_quad_diff_batch_assign - - class FluxExchangeBatchAssign(Instruction): """ .. attribute:: names @@ -605,12 +539,6 @@ class Code(object): # {{{ compiler class OperatorCompiler(mappers.IdentityMapper): - class FluxRecord(Record): - __slots__ = ["flux_expr", "dependencies", "repr_op"] - - class FluxBatch(Record): - __slots__ = ["flux_exprs", "repr_op"] - def __init__(self, prefix="_expr", max_vectors_in_batch_expr=None): super(OperatorCompiler, self).__init__() self.prefix = prefix @@ -622,94 +550,25 @@ class OperatorCompiler(mappers.IdentityMapper): self.assigned_names = set() - # {{{ collecting various optemplate components ---------------------------- - def get_contained_fluxes(self, expr): - """Recursively enumerate all flux expressions in the expression tree - `expr`. The returned list consists of `ExecutionPlanner.FluxRecord` - instances with fields `flux_expr` and `dependencies`. - """ - - contained_flux_ops = mappers.FluxCollector()(expr) - - from pytools import all - assert all(isinstance(op, sym.WholeDomainFluxOperator) - for op in contained_flux_ops), \ - "not all flux operators were of the expected type" - - return [self.FluxRecord( - flux_expr=wdflux, - dependencies=set(wdflux.interior_deps) | set(wdflux.boundary_deps), - repr_op=wdflux.repr_op()) - for wdflux in contained_flux_ops] + # {{{ collect various optemplate components def collect_diff_ops(self, expr): - return mappers.BoundOperatorCollector(sym.ReferenceDiffOperatorBase)(expr) - - def collect_flux_exchange_ops(self, expr): - return mappers.FluxExchangeCollector()(expr) + return mappers.BoundOperatorCollector(sym.RefDiffOperatorBase)(expr) # }}} - # {{{ top-level driver ---------------------------------------------------- - def __call__(self, expr, type_hints={}): + # {{{ top-level driver + + def __call__(self, expr): # Put the result expressions into variables as well. expr = sym.cse(expr, "_result") - from grudge.symbolic.mappers.type_inference import TypeInferrer - self.typedict = TypeInferrer()(expr, type_hints) - - # {{{ flux batching - # Fluxes can be evaluated faster in batches. Here, we find flux - # batches that we can evaluate together. - - # For each FluxRecord, find the other fluxes its flux depends on. - flux_queue = self.get_contained_fluxes(expr) - for fr in flux_queue: - fr.dependencies = set(sf.flux_expr - for sf in self.get_contained_fluxes(fr.flux_expr)) \ - - set([fr.flux_expr]) - - # Then figure out batches of fluxes to evaluate - self.flux_batches = [] - admissible_deps = set() - while flux_queue: - present_batch = set() - i = 0 - while i < len(flux_queue): - fr = flux_queue[i] - if fr.dependencies <= admissible_deps: - present_batch.add(fr) - flux_queue.pop(i) - else: - i += 1 - - if present_batch: - # bin batched operators by representative operator - batches_by_repr_op = {} - for fr in present_batch: - batches_by_repr_op[fr.repr_op] = \ - batches_by_repr_op.get(fr.repr_op, set()) \ - | set([fr.flux_expr]) - - for repr_op, batch in six.iteritems(batches_by_repr_op): - self.flux_batches.append( - self.FluxBatch( - repr_op=repr_op, - flux_exprs=list(batch))) - - admissible_deps |= set(fr.flux_expr for fr in present_batch) - else: - raise RuntimeError("cannot resolve flux evaluation order") - - # }}} + # from grudge.symbolic.mappers.type_inference import TypeInferrer + # self.typedict = TypeInferrer()(expr) # Used for diff batching - self.diff_ops = self.collect_diff_ops(expr) - # Flux exchange also works better when batched. - self.flux_exchange_ops = self.collect_flux_exchange_ops(expr) - # Finally, walk the expression and build the code. result = super(OperatorCompiler, self).__call__(expr) @@ -719,7 +578,8 @@ class OperatorCompiler(mappers.IdentityMapper): # }}} - # {{{ variables and names ------------------------------------------------- + # {{{ variables and names + def get_var_name(self, prefix=None): def generate_suffixes(): yield "" @@ -747,8 +607,7 @@ class OperatorCompiler(mappers.IdentityMapper): self.assigned_names.add(name) return name - def assign_to_new_var(self, expr, priority=0, prefix=None, - is_scalar_valued=False): + def assign_to_new_var(self, expr, priority=0, prefix=None): from pymbolic.primitives import Variable, Subscript # Observe that the only things that can be legally subscripted in @@ -759,7 +618,7 @@ class OperatorCompiler(mappers.IdentityMapper): new_name = self.get_var_name(prefix) self.code.append(self.make_assign( - new_name, expr, priority, is_scalar_valued)) + new_name, expr, priority)) return Variable(new_name) @@ -773,9 +632,6 @@ class OperatorCompiler(mappers.IdentityMapper): except KeyError: priority = getattr(expr, "priority", 0) - from grudge.symbolic.mappers.type_inference import type_info - is_scalar_valued = isinstance(self.typedict[expr], type_info.Scalar) - if isinstance(expr.child, sym.OperatorBinding): # We need to catch operator bindings here and # treat them specially. They get assigned to their @@ -788,24 +644,14 @@ class OperatorCompiler(mappers.IdentityMapper): rec_child = self.rec(expr.child) cse_var = self.assign_to_new_var(rec_child, - priority=priority, prefix=expr.prefix, - is_scalar_valued=is_scalar_valued) + priority=priority, prefix=expr.prefix) self.expr_to_var[expr.child] = cse_var return cse_var def map_operator_binding(self, expr, name_hint=None): - from grudge.symbolic.operators import ( - ReferenceDiffOperatorBase, - FluxOperatorBase) - - if isinstance(expr.op, ReferenceDiffOperatorBase): + if isinstance(expr.op, sym.RefDiffOperatorBase): return self.map_ref_diff_op_binding(expr) - elif isinstance(expr.op, FluxOperatorBase): - raise RuntimeError("OperatorCompiler encountered a flux operator.\n\n" - "We are expecting flux operators to be converted to custom " - "flux assignment instructions, but the subclassed compiler " - "does not seem to have done this.") else: # make sure operator assignments stand alone and don't get muddled # up in vector math @@ -859,15 +705,8 @@ class OperatorCompiler(mappers.IdentityMapper): from pytools import single_valued op_class = single_valued(type(d.op) for d in all_diffs) - from grudge.symbolic.operators import \ - ReferenceQuadratureStiffnessTOperator - if isinstance(op_class, ReferenceQuadratureStiffnessTOperator): - assign_class = QuadratureDiffBatchAssign - else: - assign_class = DiffBatchAssign - self.code.append( - assign_class( + DiffBatchAssign( names=names, op_class=op_class, operators=[d.op for d in all_diffs], @@ -880,84 +719,13 @@ class OperatorCompiler(mappers.IdentityMapper): return self.expr_to_var[expr] - def map_flux_exchange(self, expr): - try: - return self.expr_to_var[expr] - except KeyError: - from pytools.obj_array import obj_array_equal - all_flux_xchgs = [fe - for fe in self.flux_exchange_ops - if obj_array_equal(fe.arg_fields, expr.arg_fields)] - - assert len(all_flux_xchgs) > 0 - - names = [self.get_var_name() for d in all_flux_xchgs] - self.code.append( - FluxExchangeBatchAssign( - names=names, - indices_and_ranks=[ - (fe.index, fe.rank) - for fe in all_flux_xchgs], - arg_fields=[ - self.rec(arg_field) - for arg_field in fe.arg_fields])) - - from pymbolic import var - for n, d in zip(names, all_flux_xchgs): - self.expr_to_var[d] = var(n) - - return self.expr_to_var[expr] - - def map_planned_flux(self, expr): - try: - return self.expr_to_var[expr] - except KeyError: - for fb in self.flux_batches: - try: - idx = fb.flux_exprs.index(expr) - except ValueError: - pass - else: - # found at idx - mapped_fluxes = [ - self.internal_map_flux(f) - for f in fb.flux_exprs] - - names = [self.get_var_name() for f in mapped_fluxes] - self.code.append( - self.make_flux_batch_assign( - names, mapped_fluxes, fb.repr_op)) - - from pymbolic import var - for n, f in zip(names, fb.flux_exprs): - self.expr_to_var[f] = var(n) - - return var(names[idx]) - - raise RuntimeError("flux '%s' not in any flux batch" % expr) - # }}} # {{{ instruction producers - def make_assign(self, name, expr, priority, is_scalar_valued=False): + def make_assign(self, name, expr, priority): return Assign(names=[name], exprs=[expr], - priority=priority, - is_scalar_valued=is_scalar_valued) - - def make_flux_batch_assign(self, names, expressions, repr_op): - return FluxBatchAssign(names=names, expressions=expressions, repr_op=repr_op) - - # from CUDA backend: - # def make_flux_batch_assign(self, names, expressions, repr_op): - # from pytools import single_valued - # quadrature_tag = single_valued( - # wdflux.quadrature_tag - # for wdflux in expressions) - - # return CUDAFluxBatchAssign( - # names=names, expressions=expressions, repr_op=repr_op, - # quadrature_tag=quadrature_tag) + priority=priority) # }}} @@ -1164,22 +932,6 @@ class OperatorCompiler(mappers.IdentityMapper): # }}} - def internal_map_flux(self, wdflux): - return sym.WholeDomainFluxOperator( - wdflux.is_lift, - [wdflux.InteriorInfo( - flux_expr=ii.flux_expr, - field_expr=self.rec(ii.field_expr)) - for ii in wdflux.interiors], - [wdflux.BoundaryInfo( - flux_expr=bi.flux_expr, - bpair=self.rec(bi.bpair)) - for bi in wdflux.boundaries], - wdflux.quadrature_tag) - - def map_whole_domain_flux(self, wdflux): - return self.map_planned_flux(wdflux) - def finalize_multi_assign(self, names, exprs, do_not_return, priority): return VectorExprAssign(names=names, exprs=exprs, do_not_return=do_not_return, diff --git a/grudge/symbolic/flux/__init__.py b/grudge/symbolic/flux/__init__.py deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/grudge/symbolic/flux/mappers.py b/grudge/symbolic/flux/mappers.py deleted file mode 100644 index 1533274ae9041c5741a57f5d7390aab74673285f..0000000000000000000000000000000000000000 --- a/grudge/symbolic/flux/mappers.py +++ /dev/null @@ -1,188 +0,0 @@ -"""Building blocks for flux computation. Flux compilation.""" - -__copyright__ = "Copyright (C) 2007 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 pymbolic.mapper.collector -import pymbolic.mapper.flattener -import pymbolic.mapper.substitutor -import pymbolic.mapper.constant_folder -import pymbolic.mapper.flop_counter - - -class FluxIdentityMapperMixin(object): - def map_field_component(self, expr): - return expr - - def map_normal(self, expr): - # a leaf - return expr - - map_element_jacobian = map_normal - map_face_jacobian = map_normal - map_element_order = map_normal - map_local_mesh_size = map_normal - map_c_function = map_normal - - def map_scalar_parameter(self, expr): - return expr - - -class FluxIdentityMapper( - pymbolic.mapper.IdentityMapper, - FluxIdentityMapperMixin): - pass - - -class FluxSubstitutionMapper(pymbolic.mapper.substitutor.SubstitutionMapper, - FluxIdentityMapperMixin): - def map_field_component(self, expr): - result = self.subst_func(expr) - if result is not None: - return result - else: - return expr - - map_normal = map_field_component - - -class FluxStringifyMapper(pymbolic.mapper.stringifier.StringifyMapper): - def map_field_component(self, expr, enclosing_prec): - if expr.is_interior: - return "Int[%d]" % expr.index - else: - return "Ext[%d]" % expr.index - - def map_normal(self, expr, enclosing_prec): - return "Normal(%d)" % expr.axis - - def map_element_jacobian(self, expr, enclosing_prec): - return "ElJac" - - def map_face_jacobian(self, expr, enclosing_prec): - return "FJac" - - def map_element_order(self, expr, enclosing_prec): - return "N" - - def map_local_mesh_size(self, expr, enclosing_prec): - return "h" - - -class PrettyFluxStringifyMapper( - pymbolic.mapper.stringifier.CSESplittingStringifyMapperMixin, - FluxStringifyMapper): - pass - - -class FluxFlattenMapper(pymbolic.mapper.flattener.FlattenMapper, - FluxIdentityMapperMixin): - pass - - -class FluxDependencyMapper(pymbolic.mapper.dependency.DependencyMapper): - def map_field_component(self, expr): - return set([expr]) - - def map_normal(self, expr): - return set() - - map_element_jacobian = map_normal - map_face_jacobian = map_normal - map_element_order = map_normal - map_local_mesh_size = map_normal - map_c_function = map_normal - - def map_scalar_parameter(self, expr): - return set([expr]) - - -class FluxTermCollector(pymbolic.mapper.collector.TermCollector, - FluxIdentityMapperMixin): - pass - - -class FluxAllDependencyMapper(FluxDependencyMapper): - def map_normal(self, expr): - return set([expr]) - - def map_element_order(self, expr): - return set([expr]) - - def map_local_mesh_size(self, expr): - return set([expr]) - - -class FluxNormalizationMapper(pymbolic.mapper.collector.TermCollector, - FluxIdentityMapperMixin): - def get_dependencies(self, expr): - return FluxAllDependencyMapper()(expr) - - def map_constant_flux(self, expr): - if expr.local_c == expr.neighbor_c: - return expr.local_c - else: - return expr - - -class FluxCCFMapper(pymbolic.mapper.constant_folder.CommutativeConstantFoldingMapper, - FluxIdentityMapperMixin): - def is_constant(self, expr): - return not bool(FluxAllDependencyMapper()(expr)) - - -class FluxFlipper(FluxIdentityMapper): - def map_field_component(self, expr): - return expr.__class__(expr.index, not expr.is_interior) - - def map_normal(self, expr): - return -expr - - def map_element_jacobian(self, expr): - return expr.__class__(not expr.is_interior) - - map_element_order = map_element_jacobian - - -class FluxFlopCounter(pymbolic.mapper.flop_counter.FlopCounter): - def map_normal(self, expr): - return 0 - - map_element_jacobian = map_normal - map_face_jacobian = map_normal - map_element_order = map_normal - map_local_mesh_size = map_normal - - def map_field_component(self, expr): - return 0 - - def map_function_symbol(self, expr): - return 1 - - def map_c_function(self, expr): - return 1 - - def map_scalar_parameter(self, expr): - return 0 - -# vim: foldmethod=marker diff --git a/grudge/symbolic/flux/primitives.py b/grudge/symbolic/flux/primitives.py deleted file mode 100644 index e2629fbe43f12bed850792a46f37d0b14b619f50..0000000000000000000000000000000000000000 --- a/grudge/symbolic/flux/primitives.py +++ /dev/null @@ -1,256 +0,0 @@ -"""Building blocks for flux computation. Flux compilation.""" - -__copyright__ = "Copyright (C) 2007 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 -import pymbolic.primitives - - -class Flux(pymbolic.primitives.AlgebraicLeaf): - def stringifier(self): - from grudge.symbolic.flux.mappers import FluxStringifyMapper - return FluxStringifyMapper - - -class FluxScalarParameter(pymbolic.primitives.Variable): - def __init__(self, name, is_complex=False): - pymbolic.primitives.Variable.__init__(self, name) - self.is_complex = is_complex - - def get_mapper_method(self, mapper): - return mapper.map_scalar_parameter - - -class FieldComponent(Flux): - def __init__(self, index, is_interior): - self.index = index - self.is_interior = is_interior - - def is_equal(self, other): - return (isinstance(other, FieldComponent) - and self.index == other.index - and self.is_interior == other.is_interior - ) - - def __getinitargs__(self): - return self.index, self.is_interior - - def get_hash(self): - return hash(( - self.__class__, - self.index, - self.is_interior)) - - def get_mapper_method(self, mapper): - return mapper.map_field_component - - -class Normal(Flux): - def __init__(self, axis): - self.axis = axis - - def __getinitargs__(self): - return self.axis, - - def is_equal(self, other): - return isinstance(other, Normal) and self.axis == other.axis - - def get_hash(self): - return hash(( - self.__class__, - self.axis)) - - def get_mapper_method(self, mapper): - return mapper.map_normal - - -class _StatelessFlux(Flux): - def __getinitargs__(self): - return () - - def is_equal(self, other): - return isinstance(other, self.__class__) - - def get_hash(self): - return hash(self.__class__) - - -class _SidedFlux(Flux): - def __init__(self, is_interior=True): - self.is_interior = is_interior - - def __getinitargs__(self): - return (self.is_interior,) - - def is_equal(self, other): - return (isinstance(other, self.__class__) - and self.is_interior == other.is_interior) - - def get_hash(self): - return hash((self.__class__, self.is_interior)) - - -class FaceJacobian(_StatelessFlux): - def get_mapper_method(self, mapper): - return mapper.map_face_jacobian - - -class ElementJacobian(_SidedFlux): - def get_mapper_method(self, mapper): - return mapper.map_element_jacobian - - -class ElementOrder(_SidedFlux): - def get_mapper_method(self, mapper): - return mapper.map_element_order - - -class LocalMeshSize(_StatelessFlux): - def get_mapper_method(self, mapper): - return mapper.map_local_mesh_size - - -def make_penalty_term(power=1): - from pymbolic.primitives import CommonSubexpression - return CommonSubexpression( - (ElementOrder()**2/LocalMeshSize())**power, - "penalty") - - -PenaltyTerm = make_penalty_term - - -class FluxFunctionSymbol(pymbolic.primitives.FunctionSymbol): - pass - - -class Abs(FluxFunctionSymbol): - arg_count = 1 - - -class Max(FluxFunctionSymbol): - arg_count = 2 - - -class Min(FluxFunctionSymbol): - arg_count = 2 - -flux_abs = Abs() -flux_max = Max() -flux_min = Min() - - -def norm(v): - return numpy.dot(v, v)**0.5 - - -def normal(dimensions): - return numpy.array([Normal(i) for i in range(dimensions)], dtype=object) - - -class FluxConstantPlaceholder(object): - def __init__(self, constant): - self.constant = constant - - @property - def int(self): - return self.constant - - @property - def ext(self): - return self.constant - - @property - def avg(self): - return self.constant - - -class FluxZeroPlaceholder(FluxConstantPlaceholder): - def __init__(self): - FluxConstantPlaceholder.__init__(self, 0) - - -class FluxScalarPlaceholder(object): - def __init__(self, component=0): - self.component = component - - def __str__(self): - return "FSP(%d)" % self.component - - @property - def int(self): - return FieldComponent(self.component, True) - - @property - def ext(self): - return FieldComponent(self.component, False) - - @property - def avg(self): - return 0.5*(self.int+self.ext) - - -class FluxVectorPlaceholder(object): - def __init__(self, components=None, scalars=None): - if not (components is not None or scalars is not None): - raise ValueError("either components or scalars must be specified") - if components is not None and scalars is not None: - raise ValueError("only one of components and scalars " - "may be specified") - - # make them arrays for the better indexing - if components: - self.scalars = numpy.array([ - FluxScalarPlaceholder(i) - for i in range(components)]) - else: - self.scalars = numpy.array(scalars) - - def __len__(self): - return len(self.scalars) - - def __str__(self): - return "%s(%s)" % (self.__class__.__name__, self.scalars) - - def __getitem__(self, idx): - if isinstance(idx, int): - return self.scalars[idx] - else: - return FluxVectorPlaceholder(scalars=self.scalars.__getitem__(idx)) - - @property - def int(self): - return numpy.array([scalar.int for scalar in self.scalars]) - - @property - def ext(self): - return numpy.array([scalar.ext for scalar in self.scalars]) - - @property - def avg(self): - return numpy.array([scalar.avg for scalar in self.scalars]) - -# }}} - -# vim: foldmethod=marker diff --git a/grudge/symbolic/flux/tools.py b/grudge/symbolic/flux/tools.py deleted file mode 100644 index baaee7d5960e1fca2e4cc18e4ec689f42bc98220..0000000000000000000000000000000000000000 --- a/grudge/symbolic/flux/tools.py +++ /dev/null @@ -1,85 +0,0 @@ -"""Flux creation helpers.""" - -from __future__ import division - -__copyright__ = "Copyright (C) 2009 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. -""" - - - - - -def make_lax_friedrichs_flux(wave_speed, state, fluxes, bdry_tags_states_and_fluxes, - strong): - from pytools.obj_array import join_fields - from grudge.flux import make_normal, FluxVectorPlaceholder, flux_max - - n = len(state) - d = len(fluxes) - normal = make_normal(d) - fvph = FluxVectorPlaceholder(len(state)*(1+d)+1) - - wave_speed_ph = fvph[0] - state_ph = fvph[1:1+n] - fluxes_ph = [fvph[1+i*n:1+(i+1)*n] for i in range(1, d+1)] - - penalty = flux_max(wave_speed_ph.int,wave_speed_ph.ext)*(state_ph.ext-state_ph.int) - - if not strong: - num_flux = 0.5*(sum(n_i*(f_i.int+f_i.ext) for n_i, f_i in zip(normal, fluxes_ph)) - - penalty) - else: - num_flux = 0.5*(sum(n_i*(f_i.int-f_i.ext) for n_i, f_i in zip(normal, fluxes_ph)) - + penalty) - - from grudge.optemplate import get_flux_operator - flux_op = get_flux_operator(num_flux) - int_operand = join_fields(wave_speed, state, *fluxes) - - from grudge.optemplate import BoundaryPair - return (flux_op(int_operand) - + sum( - flux_op(BoundaryPair(int_operand, - join_fields(0, bdry_state, *bdry_fluxes), tag)) - for tag, bdry_state, bdry_fluxes in bdry_tags_states_and_fluxes)) - - - - -def make_flux_bilinear_form(testee_expr, test_expr): - """Create a grudge flux expression for the bilinear form - - .. math:: - - \int_{\Gamma} u f(l_i^+,l_i^-), - - where :math:`u` is the (vector or scalar) unknown, given in *testee_epxr*, - :math:`f` is a function given by *test_expr* in terms of the Lagrange polynomials - :math:`l^_i^{\pm}` against which we are testing. In *test_expr*, :math:`l_i^{\pm}` - are represented by :class:`grudge.flux.FieldComponent(-1, True/False)`. - :math:`f` is required to be linear in :math:`l_i^{\pm}`. - """ - # keeping this stub to not lose the design work that went into its signature. - raise NotImplementedError - - - diff --git a/grudge/symbolic/mappers/__init__.py b/grudge/symbolic/mappers/__init__.py index 815e624455d6421883a17d7c2703d36da47776ca..06518a3be1cbb2ffa4be3004ccd8d11a0611f456 100644 --- a/grudge/symbolic/mappers/__init__.py +++ b/grudge/symbolic/mappers/__init__.py @@ -38,7 +38,6 @@ import pymbolic.mapper.flop_counter from pymbolic.mapper import CSECachingMapperMixin from grudge import sym -from grudge import sym_flux import grudge.symbolic.operators as op @@ -128,29 +127,34 @@ class OperatorReducerMixin(LocalOpReducerMixin, FluxOpReducerMixin): def _map_op_base(self, expr, *args, **kwargs): return self.map_operator(expr, *args, **kwargs) + map_elementwise_linear = _map_op_base + + map_interpolation = _map_op_base + map_nodal_sum = _map_op_base - map_nodal_max = _map_op_base map_nodal_min = _map_op_base + map_nodal_max = _map_op_base - map_diff_base = _map_op_base - map_ref_diff_base = _map_op_base - map_elementwise_linear = _map_op_base - map_flux_base = _map_op_base - map_elementwise_max = _map_op_base - map_boundarize = _map_op_base - map_flux_exchange = _map_op_base - map_quad_grid_upsampler = _map_op_base - map_quad_int_faces_grid_upsampler = _map_op_base - map_quad_bdry_grid_upsampler = _map_op_base + map_stiffness = _map_op_base + map_diff = _map_op_base + map_stiffness_t = _map_op_base + + map_ref_diff = _map_op_base + map_ref_stiffness_t = _map_op_base + + map_mass = _map_op_base + map_inverse_mass = _map_op_base + map_ref_mass = _map_op_base + map_ref_inverse_mass = _map_op_base + + map_opposite_interior_face_swap = _map_op_base + map_face_mass_operator = _map_op_base class CombineMapperMixin(object): def map_operator_binding(self, expr): return self.combine([self.rec(expr.op), self.rec(expr.field)]) - def map_boundary_pair(self, expr): - return self.combine([self.rec(expr.field), self.rec(expr.bfield)]) - class IdentityMapperMixin(LocalOpReducerMixin, FluxOpReducerMixin): def map_operator_binding(self, expr, *args, **kwargs): @@ -158,19 +162,11 @@ class IdentityMapperMixin(LocalOpReducerMixin, FluxOpReducerMixin): "IdentityMapper instances cannot be combined with " \ "the BoundOpMapperMixin" - return expr.__class__( + return type(expr)( self.rec(expr.op, *args, **kwargs), self.rec(expr.field, *args, **kwargs)) - def map_boundary_pair(self, expr, *args, **kwargs): - assert not isinstance(self, BoundOpMapperMixin), \ - "IdentityMapper instances cannot be combined with " \ - "the BoundOpMapperMixin" - - return expr.__class__( - self.rec(expr.field, *args, **kwargs), - self.rec(expr.bfield, *args, **kwargs), - expr.tag) + # {{{ operators def map_elementwise_linear(self, expr, *args, **kwargs): assert not isinstance(self, BoundOpMapperMixin), \ @@ -180,30 +176,41 @@ class IdentityMapperMixin(LocalOpReducerMixin, FluxOpReducerMixin): # it's a leaf--no changing children return expr - def map_scalar_parameter(self, expr, *args, **kwargs): - # it's a leaf--no changing children - return expr - - map_c_function = map_scalar_parameter - - map_ones = map_scalar_parameter - map_node_coordinate_component = map_scalar_parameter + map_interpolation = map_elementwise_linear map_nodal_sum = map_elementwise_linear map_nodal_min = map_elementwise_linear map_nodal_max = map_elementwise_linear - map_mass_base = map_elementwise_linear - map_ref_mass_base = map_elementwise_linear - map_diff_base = map_elementwise_linear - map_ref_diff_base = map_elementwise_linear - map_flux_base = map_elementwise_linear - map_elementwise_max = map_elementwise_linear - map_boundarize = map_elementwise_linear - map_flux_exchange = map_elementwise_linear - map_quad_grid_upsampler = map_elementwise_linear - map_quad_int_faces_grid_upsampler = map_elementwise_linear - map_quad_bdry_grid_upsampler = map_elementwise_linear + map_stiffness = map_elementwise_linear + map_diff = map_elementwise_linear + map_stiffness_t = map_elementwise_linear + + map_ref_diff = map_elementwise_linear + map_ref_stiffness_t = map_elementwise_linear + + map_mass = map_elementwise_linear + map_inverse_mass = map_elementwise_linear + map_ref_mass = map_elementwise_linear + map_ref_inverse_mass = map_elementwise_linear + + map_opposite_interior_face_swap = map_elementwise_linear + map_face_mass_operator = map_elementwise_linear + + # }}} + + # {{{ primitives + + def map_grudge_variable(self, expr, *args, **kwargs): + # it's a leaf--no changing children + return expr + + map_c_function = map_grudge_variable + + map_ones = map_grudge_variable + map_node_coordinate_component = map_grudge_variable + + # }}} class BoundOpMapperMixin(object): @@ -247,14 +254,13 @@ class DependencyMapper( def map_operator(self, expr): return set() - def map_scalar_parameter(self, expr): + def map_grudge_variable(self, expr): return set([expr]) def _map_leaf(self, expr): return set() map_ones = _map_leaf - map_node_coordinate_component = _map_leaf @@ -377,12 +383,13 @@ class OperatorSpecializer(CSECachingMapperMixin, IdentityMapper): # operators. # {{{ elementwise operators + if isinstance(expr.op, op.MassOperator) and has_quad_operand: return op.QuadratureMassOperator( field_repr_tag.quadrature_tag)(self.rec(expr.field)) - elif isinstance(expr.op, op.ReferenceMassOperator) and has_quad_operand: - return op.ReferenceQuadratureMassOperator( + elif isinstance(expr.op, op.RefMassOperator) and has_quad_operand: + return op.RefQuadratureMassOperator( field_repr_tag.quadrature_tag)(self.rec(expr.field)) elif (isinstance(expr.op, op.StiffnessTOperator) and has_quad_operand): @@ -390,9 +397,9 @@ class OperatorSpecializer(CSECachingMapperMixin, IdentityMapper): expr.op.xyz_axis, field_repr_tag.quadrature_tag)( self.rec(expr.field)) - elif (isinstance(expr.op, op.ReferenceStiffnessTOperator) + elif (isinstance(expr.op, op.RefStiffnessTOperator) and has_quad_operand): - return op.ReferenceQuadratureStiffnessTOperator( + return op.RefQuadratureStiffnessTOperator( expr.op.xyz_axis, field_repr_tag.quadrature_tag)( self.rec(expr.field)) @@ -489,20 +496,12 @@ class GlobalToReferenceMapper(CSECachingMapperMixin, IdentityMapper): # if we encounter non-quadrature operators here, we know they # must be nodal. - jac_notag = sym.area_element(self.ambient_dim, self.dim, - where=expr.op.where, quadrature_tag=None) - - def rewrite_derivative(ref_class, field, where, - input_quadrature_tag=None, with_jacobian=True): - jac_tag = sym.area_element(self.ambient_dim, self.dim, - where=expr.op.where, quadrature_tag=input_quadrature_tag) - - if input_quadrature_tag is not None: - diff_kwargs = dict(input_quadrature_tag=input_quadrature_tag) - else: - diff_kwargs = {} + jac_in = sym.area_element(self.ambient_dim, self.dim, dd=expr.op.dd_in) + jac_noquad = sym.area_element(self.ambient_dim, self.dim, + dd=expr.op.dd_in.with_qtag(sym.QTAG_NONE)) - diff_kwargs["where"] = where + def rewrite_derivative(ref_class, field, dd_in, with_jacobian=True): + jac_tag = sym.area_element(self.ambient_dim, self.dim, dd=dd_in) rec_field = self.rec(field) if with_jacobian: @@ -511,45 +510,31 @@ class GlobalToReferenceMapper(CSECachingMapperMixin, IdentityMapper): 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) + ref_class(rst_axis, dd_in=dd_in)(rec_field) for rst_axis in range(self.dim)) if isinstance(expr.op, op.MassOperator): - return op.ReferenceMassOperator()( - 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, expr.op.where)( - jac_tag * self.rec(expr.field)) + return op.RefMassOperator(expr.op.dd_in, expr.op.dd_out)( + jac_in * self.rec(expr.field)) elif isinstance(expr.op, op.InverseMassOperator): - return op.ReferenceInverseMassOperator(expr.op.where)( - 1/jac_notag * self.rec(expr.field)) + return op.RefInverseMassOperator(expr.op.dd_in, expr.op.dd_out)( + 1/jac_in * self.rec(expr.field)) elif isinstance(expr.op, op.StiffnessOperator): - return op.ReferenceMassOperator(expr.op.where)(jac_notag * + return op.RefMassOperator()(jac_noquad * self.rec( - op.DifferentiationOperator(expr.op.xyz_axis)(expr.field))) + op.DiffOperator(expr.op.xyz_axis)(expr.field))) - elif isinstance(expr.op, op.DifferentiationOperator): + elif isinstance(expr.op, op.DiffOperator): return rewrite_derivative( - op.ReferenceDifferentiationOperator, - expr.field, where=expr.op.where, with_jacobian=False) + op.RefDiffOperator, + expr.field, dd_in=expr.op.dd_in, with_jacobian=False) elif isinstance(expr.op, op.StiffnessTOperator): return rewrite_derivative( - op.ReferenceStiffnessTOperator, - expr.field, where=expr.op.where) - - elif isinstance(expr.op, op.QuadratureStiffnessTOperator): - return rewrite_derivative( - op.ReferenceQuadratureStiffnessTOperator, - expr.field, input_quadrature_tag=expr.op.input_quadrature_tag, - where=expr.op.where) + op.RefStiffnessTOperator, + expr.field, dd_in=expr.op.dd_in) elif isinstance(expr.op, op.MInvSTOperator): return self.rec( @@ -565,72 +550,67 @@ class GlobalToReferenceMapper(CSECachingMapperMixin, IdentityMapper): # {{{ stringification --------------------------------------------------------- class StringifyMapper(pymbolic.mapper.stringifier.StringifyMapper): - def _format_btag(self, tag): - if isinstance(tag, type): - return tag.__name__ + def _format_dd(self, dd): + def fmt(s): + if isinstance(s, type): + return s.__name__ + else: + return repr(s) + + from meshmode.discretization.connection import ( + FRESTR_ALL_FACES, FRESTR_INTERIOR_FACES) + if dd.domain_tag is None: + result = "?" + elif dd.domain_tag is sym.DTAG_VOLUME_ALL: + result = "vol" + elif dd.domain_tag is sym.DTAG_SCALAR: + result = "scalar" + elif dd.domain_tag is FRESTR_ALL_FACES: + result = "all_faces" + elif dd.domain_tag is FRESTR_INTERIOR_FACES: + result = "int_faces" else: - return repr(tag) + result = fmt(dd.domain_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 + if dd.quadrature_tag is None: + pass + elif dd.quadrature_tag is sym.QTAG_NONE: + result += "q" + else: + result += "Q"+fmt(dd.quadrature_tag) return result - def __init__(self, constant_mapper=str, flux_stringify_mapper=None): - pymbolic.mapper.stringifier.StringifyMapper.__init__( - self, constant_mapper=constant_mapper) - - if flux_stringify_mapper is None: - from grudge.symbolic.flux.mappers import FluxStringifyMapper - flux_stringify_mapper = FluxStringifyMapper() - - self.flux_stringify_mapper = flux_stringify_mapper - - def map_boundary_pair(self, expr, enclosing_prec): - from pymbolic.mapper.stringifier import PREC_NONE - return "BPair(%s, %s, %s)" % ( - self.rec(expr.field, PREC_NONE), - self.rec(expr.bfield, PREC_NONE), - self._format_btag(expr.tag)) + def _format_op_dd(self, op): + return "[%s->%s]" % (self._format_dd(op.dd_in), self._format_dd(op.dd_out)) # {{{ nodal ops def map_nodal_sum(self, expr, enclosing_prec): - return "NodalSum" + return "NodalSum" + self._format_op_dd(expr) def map_nodal_max(self, expr, enclosing_prec): - return "NodalMax" + return "NodalMax" + self._format_op_dd(expr) def map_nodal_min(self, expr, enclosing_prec): - return "NodalMin" + return "NodalMin" + self._format_op_dd(expr) # }}} # {{{ global differentiation def map_diff(self, expr, enclosing_prec): - return "Diffx%d%s" % (expr.xyz_axis, self._format_op_props(expr)) + return "Diffx%d%s" % (expr.xyz_axis, self._format_op_dd(expr)) def map_minv_st(self, expr, enclosing_prec): - return "MInvSTx%d%s" % (expr.xyz_axis, self._format_op_props(expr)) + return "MInvSTx%d%s" % (expr.xyz_axis, self._format_op_dd(expr)) def map_stiffness(self, expr, enclosing_prec): - return "Stiffx%d%s" % (expr.xyz_axis, self._format_op_props(expr)) + return "Stiffx%d%s" % (expr.xyz_axis, self._format_op_dd(expr)) def map_stiffness_t(self, expr, enclosing_prec): - return "StiffTx%d%s" % (expr.xyz_axis, self._format_op_props(expr)) + return "StiffTx%d%s" % (expr.xyz_axis, self._format_op_dd(expr)) - def map_quad_stiffness_t(self, expr, enclosing_prec): - return "StiffTx%d%s" % ( - expr.input_quadrature_tag, expr.xyz_axis, - self._format_op_props(expr)) # }}} # {{{ global mass @@ -641,107 +621,49 @@ class StringifyMapper(pymbolic.mapper.stringifier.StringifyMapper): def map_inverse_mass(self, expr, enclosing_prec): return "InvM" - def map_quad_mass(self, expr, enclosing_prec): - return "Q[%s]M" % expr.quadrature_tag - # }}} # {{{ reference differentiation def map_ref_diff(self, expr, enclosing_prec): - return "Diffr%d%s" % (expr.rst_axis, self._format_op_props(expr)) + return "Diffr%d%s" % (expr.rst_axis, self._format_op_dd(expr)) def map_ref_stiffness_t(self, expr, enclosing_prec): - return "StiffTr%d%s" % (expr.rst_axis, self._format_op_props(expr)) + return "StiffTr%d%s" % (expr.rst_axis, self._format_op_dd(expr)) - def map_ref_quad_stiffness_t(self, expr, enclosing_prec): - 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" + self._format_op_props(expr) + return "RefM" + self._format_op_dd(expr) def map_ref_inverse_mass(self, expr, enclosing_prec): - return "RefInvM" + self._format_op_props(expr) - - def map_ref_quad_mass(self, expr, enclosing_prec): - return "RefM" + self._format_op_props(expr) + return "RefInvM" + self._format_op_dd(expr) # }}} def map_elementwise_linear(self, expr, enclosing_prec): return "ElWLin:%s%s" % ( expr.__class__.__name__, - self._format_op_props(expr)) + self._format_op_dd(expr)) # {{{ flux - def map_flux(self, expr, enclosing_prec): - from pymbolic.mapper.stringifier import PREC_NONE - return "%s(%s)" % ( - expr.get_flux_or_lift_text(), - self.flux_stringify_mapper(expr.flux, PREC_NONE)) + def map_face_mass_operator(self, expr, enclosing_prec): + return "FaceM" + self._format_op_dd(expr) - def map_bdry_flux(self, expr, enclosing_prec): - from pymbolic.mapper.stringifier import PREC_NONE - return "B[%s]%s(%s)" % ( - self._format_btag(expr.boundary_tag), - expr.get_flux_or_lift_text(), - self.flux_stringify_mapper(expr.flux, PREC_NONE)) + def map_opposite_interior_face_swap(self, expr, enclosing_prec): + return "OppSwap" + self._format_op_dd(expr) - def map_quad_flux(self, expr, enclosing_prec): - from pymbolic.mapper.stringifier import PREC_NONE - return "Q[%s]%s(%s)" % ( - expr.quadrature_tag, - expr.get_flux_or_lift_text(), - self.flux_stringify_mapper(expr.flux, PREC_NONE)) - - def map_quad_bdry_flux(self, expr, enclosing_prec): - from pymbolic.mapper.stringifier import PREC_NONE - return "Q[%s]B[%s]%s(%s)" % ( - expr.quadrature_tag, - self._format_btag(expr.boundary_tag), - expr.get_flux_or_lift_text(), - self.flux_stringify_mapper(expr.flux, PREC_NONE)) - - def map_whole_domain_flux(self, expr, enclosing_prec): - # used from grudge.backends.cuda.optemplate - if expr.is_lift: - opname = "WLift" - else: - opname = "WFlux" - - from pymbolic.mapper.stringifier import PREC_NONE - return "%s(%s)" % (opname, - self.rec(expr.rebuild_optemplate(), PREC_NONE)) # }}} - def map_elementwise_max(self, expr, enclosing_prec): - return "ElWMax" + self._format_op_props(expr) - - def map_boundarize(self, expr, enclosing_prec): - 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 - return "FExch<idx=%s,rank=%d>(%s)" % (expr.index, expr.rank, - ", ".join(self.rec(arg, PREC_NONE) for arg in expr.arg_fields)) - def map_ones(self, expr, enclosing_prec): return "Ones" + self._format_op_props(expr) # {{{ geometry data def map_node_coordinate_component(self, expr, enclosing_prec): - if expr.quadrature_tag is None: - return "x[%d]" % expr.axis - else: - return ("Q[%s]x[%d]" % (expr.quadrature_tag, expr.axis)) + return "x[%d]@%s" % (expr.axis, self._format_dd(expr.dd)) # }}} @@ -754,100 +676,17 @@ class StringifyMapper(pymbolic.mapper.stringifier.StringifyMapper): def map_c_function(self, expr, enclosing_prec): return expr.name - def map_scalar_parameter(self, expr, enclosing_prec): - return "ScalarPar[%s]" % expr.name - - def map_quad_grid_upsampler(self, expr, enclosing_prec): - return "Upsamp" + self._format_op_props(expr) + def map_grudge_variable(self, expr, enclosing_prec): + return "%s:%s" % (expr.name, self._format_dd(expr.dd)) - def map_quad_int_faces_grid_upsampler(self, expr, enclosing_prec): - return "ToIntFaceQ[%s]" % expr.quadrature_tag + def map_interpolation(self, expr, enclosing_prec): + return "Interp" + self._format_op_dd(expr) class PrettyStringifyMapper( pymbolic.mapper.stringifier.CSESplittingStringifyMapperMixin, StringifyMapper): - def __init__(self): - pymbolic.mapper.stringifier.CSESplittingStringifyMapperMixin.__init__(self) - StringifyMapper.__init__(self) - - self.flux_to_number = {} - self.flux_string_list = [] - - self.bc_to_number = {} - self.bc_string_list = [] - - from grudge.symbolic.flux.mappers import PrettyFluxStringifyMapper - self.flux_stringify_mapper = PrettyFluxStringifyMapper() - - def get_flux_number(self, flux): - try: - return self.flux_to_number[flux] - except KeyError: - from pymbolic.mapper.stringifier import PREC_NONE - str_flux = self.flux_stringify_mapper(flux, PREC_NONE) - - flux_number = len(self.flux_to_number) - self.flux_string_list.append(str_flux) - self.flux_to_number[flux] = flux_number - return flux_number - - def map_boundary_pair(self, expr, enclosing_prec): - try: - bc_number = self.bc_to_number[expr] - except KeyError: - from pymbolic.mapper.stringifier import PREC_NONE - str_bc = StringifyMapper.map_boundary_pair(self, expr, PREC_NONE) - - bc_number = len(self.bc_to_number) - self.bc_string_list.append(str_bc) - self.bc_to_number[expr] = bc_number - - return "BC%d@%s" % (bc_number, self._format_btag(expr.tag)) - - def map_operator_binding(self, expr, enclosing_prec): - if isinstance(expr.op, op.RestrictToBoundary): - from pymbolic.mapper.stringifier import PREC_CALL, PREC_SUM - return self.parenthesize_if_needed( - "%s@%s" % ( - self.rec(expr.field, PREC_CALL), - self._format_btag(expr.op.tag)), - enclosing_prec, PREC_SUM) - else: - return StringifyMapper.map_operator_binding( - self, expr, enclosing_prec) - - def get_bc_strings(self): - return ["BC%d : %s" % (i, bc_str) - for i, bc_str in enumerate(self.bc_string_list)] - - def get_flux_strings(self): - return ["Flux%d : %s" % (i, flux_str) - for i, flux_str in enumerate(self.flux_string_list)] - - def map_flux(self, expr, enclosing_prec): - return "%s%d" % ( - expr.get_flux_or_lift_text(), - self.get_flux_number(expr.flux)) - - def map_bdry_flux(self, expr, enclosing_prec): - return "B[%s]%s%d" % ( - expr.boundary_tag, - expr.get_flux_or_lift_text(), - self.get_flux_number(expr.flux)) - - def map_quad_flux(self, expr, enclosing_prec): - return "Q[%s]%s%d" % ( - expr.quadrature_tag, - expr.get_flux_or_lift_text(), - self.get_flux_number(expr.flux)) - - def map_quad_bdry_flux(self, expr, enclosing_prec): - return "Q[%s]B[%s]%s%d" % ( - expr.quadrature_tag, - expr.boundary_tag, - expr.get_flux_or_lift_text(), - self.get_flux_number(expr.flux)) + pass class NoCSEStringifyMapper(StringifyMapper): @@ -974,115 +813,7 @@ class CommutativeConstantFoldingMapper( if is_zero(field): return 0 - if isinstance(expr.op, op.FluxOperatorBase): - if isinstance(field, sym.BoundaryPair): - return self.remove_zeros_from_boundary_flux(expr.op, field) - else: - return self.remove_zeros_from_interior_flux(expr.op, field) - else: - return expr.op(field) - - # {{{ remove zeros from interior flux - def remove_zeros_from_interior_flux(self, op, vol_field): - from pytools.obj_array import is_obj_array, make_obj_array - if not is_obj_array(vol_field): - vol_field = [vol_field] - - subst_map = {} - - from grudge.tools import is_zero - - # process volume field - new_vol_field = [] - new_idx = 0 - for i, flux_arg in enumerate(vol_field): - flux_arg = self.rec(flux_arg) - - if is_zero(flux_arg): - subst_map[sym_flux.FieldComponent(i, is_interior=True)] = 0 - subst_map[sym_flux.FieldComponent(i, is_interior=False)] = 0 - else: - new_vol_field.append(flux_arg) - subst_map[sym_flux.FieldComponent(i, is_interior=True)] = \ - sym_flux.FieldComponent(new_idx, is_interior=True) - subst_map[sym_flux.FieldComponent(i, is_interior=False)] = \ - sym_flux.FieldComponent(new_idx, is_interior=False) - new_idx += 1 - - # substitute results into flux - def sub_flux(expr): - return subst_map.get(expr, expr) - - from grudge.symbolic.flux.mappers import FluxSubstitutionMapper - new_flux = FluxSubstitutionMapper(sub_flux)(op.flux) - - if is_zero(new_flux): - return 0 - else: - return type(op)(new_flux, *op.__getinitargs__()[1:])( - make_obj_array(new_vol_field)) - - # }}} - - # {{{ remove zeros from boundary flux - def remove_zeros_from_boundary_flux(self, op, bpair): - vol_field = bpair.field - bdry_field = bpair.bfield - from pytools.obj_array import is_obj_array, make_obj_array - if not is_obj_array(vol_field): - vol_field = [vol_field] - if not is_obj_array(bdry_field): - bdry_field = [bdry_field] - - subst_map = {} - - # process volume field - from grudge.tools import is_zero - new_vol_field = [] - new_idx = 0 - for i, flux_arg in enumerate(vol_field): - fc = sym_flux.FieldComponent(i, is_interior=True) - flux_arg = self.rec(flux_arg) - - if is_zero(flux_arg): - subst_map[fc] = 0 - else: - new_vol_field.append(flux_arg) - subst_map[fc] = sym_flux.FieldComponent(new_idx, is_interior=True) - new_idx += 1 - - # process boundary field - new_bdry_field = [] - new_idx = 0 - for i, flux_arg in enumerate(bdry_field): - fc = sym_flux.FieldComponent(i, is_interior=False) - flux_arg = self.rec(flux_arg) - - if is_zero(flux_arg): - subst_map[fc] = 0 - else: - new_bdry_field.append(flux_arg) - subst_map[fc] = sym_flux.FieldComponent(new_idx, is_interior=False) - new_idx += 1 - - # substitute results into flux - def sub_flux(expr): - return subst_map.get(expr, expr) - - from grudge.symbolic.flux.mappers import FluxSubstitutionMapper - new_flux = FluxSubstitutionMapper(sub_flux)(op.flux) - - if is_zero(new_flux): - return 0 - else: - from grudge.symbolic.primitives import BoundaryPair - return type(op)(new_flux, *op.__getinitargs__()[1:])( - BoundaryPair( - make_obj_array(new_vol_field), - make_obj_array(new_bdry_field), - bpair.tag)) - - # }}} + return expr.op(field) class EmptyFluxKiller(CSECachingMapperMixin, IdentityMapper): @@ -1094,8 +825,13 @@ class EmptyFluxKiller(CSECachingMapperMixin, IdentityMapper): IdentityMapper.map_common_subexpression def map_operator_binding(self, expr): - if (isinstance(expr.op, op.BoundaryFluxOperatorBase) and - len(self.mesh.tag_to_boundary.get(expr.op.boundary_tag, [])) == 0): + from meshmode.mesh import is_boundary_tag_empty + if (isinstance(expr.op, sym.InterpolationOperator) + and expr.op.dd_out.is_boundary() + and expr.op.dd_out.domain_tag not in [ + sym.FRESTR_ALL_FACES, sym.FRESTR_INTERIOR_FACES] + and is_boundary_tag_empty(self.mesh, + expr.op.dd_out.domain_tag)): return 0 else: return IdentityMapper.map_operator_binding(self, expr) @@ -1139,7 +875,7 @@ class _InnerDerivativeJoiner(pymbolic.mapper.RecursiveMapper): else: return self.rec(expr, derivatives) - for operator, operands in six.iteritem(sub_derivatives): + for operator, operands in six.iteritems(sub_derivatives): for operand in operands: derivatives.setdefault(operator, []).append( factor*operand) @@ -1283,9 +1019,6 @@ class InverseMassContractor(CSECachingMapperMixin, IdentityMapper): map_common_subexpression_uncached = \ IdentityMapper.map_common_subexpression - def map_boundary_pair(self, bp): - return sym.BoundaryPair(self.rec(bp.field), self.rec(bp.bfield), bp.tag) - def map_operator_binding(self, binding): # we only care about bindings of inverse mass operators @@ -1333,62 +1066,23 @@ class ErrorChecker(CSECachingMapperMixin, IdentityMapper): # }}} -# {{{ collectors for various optemplate components +# {{{ collectors for various symbolic operator components class CollectorMixin(OperatorReducerMixin, LocalOpReducerMixin, FluxOpReducerMixin): def combine(self, values): from pytools import flatten return set(flatten(values)) - def map_constant(self, bpair): + def map_constant(self, expr, *args, **kwargs): return set() - map_operator = map_constant - map_variable = map_constant - map_ones = map_constant - map_node_coordinate_component = map_constant - map_scalar_parameter = map_constant - map_c_function = map_constant - - def map_whole_domain_flux(self, expr): - result = set() - - for ii in expr.interiors: - result.update(self.rec(ii.field_expr)) - - for bi in expr.boundaries: - result.update(self.rec(bi.bpair.field)) - result.update(self.rec(bi.bpair.bfield)) - - return result - - -class FluxCollector(CSECachingMapperMixin, CollectorMixin, CombineMapper): - map_common_subexpression_uncached = \ - CombineMapper.map_common_subexpression - - def map_operator_binding(self, expr): - if isinstance(expr.op, op.FluxOperatorBase): - result = set([expr]) - else: - result = set() - - return result | CombineMapper.map_operator_binding(self, expr) - - def map_whole_domain_flux(self, wdflux): - result = set([wdflux]) - - for intr in wdflux.interiors: - result |= self.rec(intr.field_expr) - for bdry in wdflux.boundaries: - result |= self.rec(bdry.bpair) - - return result + map_grudge_variable = map_constant + map_c_function = map_grudge_variable + map_ones = map_grudge_variable + map_node_coordinate_component = map_grudge_variable -class BoundaryTagCollector(CollectorMixin, CombineMapper): - def map_boundary_pair(self, bpair): - return set([bpair.tag]) + map_operator = map_grudge_variable # I'm not sure this works still. @@ -1425,113 +1119,7 @@ class FluxExchangeCollector(CSECachingMapperMixin, CollectorMixin, CombineMapper # {{{ evaluation class Evaluator(pymbolic.mapper.evaluator.EvaluationMapper): - def map_boundary_pair(self, bp): - return sym.BoundaryPair(self.rec(bp.field), self.rec(bp.bfield), bp.tag) -# }}} - - -# {{{ boundary combiner - -class BoundaryCombiner(CSECachingMapperMixin, IdentityMapper): - """Combines inner fluxes and boundary fluxes into a - single, whole-domain operator of type - :class:`grudge.symbolic.operators.WholeDomainFluxOperator`. - """ - def __init__(self, mesh): - self.mesh = mesh - - map_common_subexpression_uncached = \ - IdentityMapper.map_common_subexpression - - def gather_one_wdflux(self, expressions): - from grudge.symbolic.primitives import OperatorBinding, BoundaryPair - - interiors = [] - boundaries = [] - is_lift = None - # Since None is a valid value of quadrature tags, use - # the empty list to symbolize "not known", and add to - # list once something is known. - quad_tag = [] - - rest = [] - - for ch in expressions: - if (isinstance(ch, OperatorBinding) - and isinstance(ch.op, op.FluxOperatorBase)): - skip = False - - my_is_lift = ch.op.is_lift - - if is_lift is None: - is_lift = my_is_lift - else: - if is_lift != my_is_lift: - skip = True - - if isinstance(ch.op, op.QuadratureFluxOperatorBase): - my_quad_tag = ch.op.quadrature_tag - else: - my_quad_tag = None - - if quad_tag: - if quad_tag[0] != my_quad_tag: - skip = True - else: - quad_tag.append(my_quad_tag) - - if skip: - rest.append(ch) - continue - - if isinstance(ch.field, BoundaryPair): - bpair = self.rec(ch.field) - from meshmode.mesh import is_boundary_tag_empty - if not is_boundary_tag_empty(self.mesh, bpair.tag): - boundaries.append(op.WholeDomainFluxOperator.BoundaryInfo( - flux_expr=ch.op.flux, - bpair=bpair)) - else: - interiors.append(op.WholeDomainFluxOperator.InteriorInfo( - flux_expr=ch.op.flux, - field_expr=self.rec(ch.field))) - else: - rest.append(ch) - - if interiors or boundaries: - wdf = op.WholeDomainFluxOperator( - is_lift=is_lift, - interiors=interiors, - boundaries=boundaries, - quadrature_tag=quad_tag[0]) - else: - wdf = None - - return wdf, rest - - def map_operator_binding(self, expr): - if isinstance(expr.op, op.FluxOperatorBase): - wdf, rest = self.gather_one_wdflux([expr]) - assert not rest - return wdf - else: - return IdentityMapper \ - .map_operator_binding(self, expr) - - def map_sum(self, expr): - # FIXME: With flux joining now in the compiler, this is - # probably now unnecessary. - - from pymbolic.primitives import flattened_sum - - result = 0 - expressions = expr.children - while True: - wdf, expressions = self.gather_one_wdflux(expressions) - if wdf is not None: - result += wdf - else: - return result + flattened_sum(self.rec(r_i) for r_i in expressions) + pass # }}} diff --git a/grudge/symbolic/operators.py b/grudge/symbolic/operators.py index de96dba9ecd57c15c0fdabc2f27f4fe584b4d852..631fac36f31c1ed39a95947f219e19da10cfb862 100644 --- a/grudge/symbolic/operators.py +++ b/grudge/symbolic/operators.py @@ -27,23 +27,36 @@ THE SOFTWARE. from six.moves import intern import numpy as np -import numpy.linalg as la +import numpy.linalg as la # noqa import pymbolic.primitives -from pytools import Record, memoize_method + + +def _sym(): + # A hack to make referring to grudge.sym possible without + # circular imports and tons of typing. + + from grudge import sym + return sym # {{{ base classes -class Operator(pymbolic.primitives.Leaf): +class Operator(pymbolic.primitives.Expression): """ - .. attribute:: where + .. attribute:: dd_in + + an instance of :class:`grudge.sym.DOFDesc` describing the + input discretization. + + .. attribute:: dd_out - *None* for the default volume discretization or a boundary - tag for an operation on the denoted part of the boundary. + an instance of :class:`grudge.sym.DOFDesc` describing the + output discretization. """ - def __init__(self, where=None): - self.where = where + def __init__(self, dd_in, dd_out): + self.dd_in = _sym().as_dofdesc(dd_in) + self.dd_out = _sym().as_dofdesc(dd_out) def stringifier(self): from grudge.symbolic.mappers import StringifyMapper @@ -62,23 +75,47 @@ class Operator(pymbolic.primitives.Leaf): return with_object_array_or_scalar(bind_one, expr) - def get_hash(self): - return hash((self.__class__,) + (self.__getinitargs__())) - - def is_equal(self, other): - return self.__class__ == other.__class__ and \ - self.__getinitargs__() == other.__getinitargs__() + def with_dd(self, dd_in=None, dd_out=None): + """Return a copy of *self*, modified to the given DOF descriptors. + """ + return type(self)( + *self.__getinitargs__()[:-2], + dd_in=dd_in or self.dd_in, + dd_out=dd_out or self.dd_out) def __getinitargs__(self): - return (self.where,) + return (self.dd_in, self.dd_out,) # }}} -# {{{ sum, integral, max +class ElementwiseLinearOperator(Operator): + def matrix(self, element_group): + raise NotImplementedError + + mapper_method = intern("map_elementwise_linear") + + +class InterpolationOperator(Operator): + mapper_method = intern("map_interpolation") + +interp = InterpolationOperator + + +class ElementwiseMaxOperator(Operator): + mapper_method = intern("map_elementwise_max") + + +# {{{ nodal reduction: sum, integral, max class NodalReductionOperator(Operator): - pass + def __init__(self, dd_in, dd_out=None): + if dd_out is None: + dd_out = _sym().DD_SCALAR + + assert dd_out.is_scalar() + + super(NodalReductionOperator, self).__init__(dd_out=dd_out, dd_in=dd_in) class NodalSum(NodalReductionOperator): @@ -100,16 +137,18 @@ class NodalMin(NodalReductionOperator): # {{{ global differentiation class DiffOperatorBase(Operator): - def __init__(self, xyz_axis): - Operator.__init__(self) + def __init__(self, xyz_axis, dd_in=None, dd_out=None): + if dd_in is None: + dd_in = _sym().DD_VOLUME + if dd_out is None: + dd_out = dd_in.with_qtag(_sym().QTAG_NONE) + + super(DiffOperatorBase, self).__init__(dd_in, dd_out) self.xyz_axis = xyz_axis def __getinitargs__(self): - return (self.xyz_axis,) - - def preimage_ranges(self, eg): - return eg.ranges + return (self.xyz_axis, self.dd_in, self.dd_out) def equal_except_for_axis(self, other): return (type(self) == type(other) @@ -129,7 +168,7 @@ class StiffnessOperator(StrongFormDiffOperatorBase): mapper_method = intern("map_stiffness") -class DifferentiationOperator(StrongFormDiffOperatorBase): +class DiffOperator(StrongFormDiffOperatorBase): mapper_method = intern("map_diff") @@ -140,46 +179,24 @@ class StiffnessTOperator(WeakFormDiffOperatorBase): class MInvSTOperator(WeakFormDiffOperatorBase): mapper_method = intern("map_minv_st") - -class QuadratureStiffnessTOperator(DiffOperatorBase): - """ - .. note:: - - This operator is purely for internal use. It is inserted - by :class:`grudge.symbolic.mappers.OperatorSpecializer` - when a :class:`StiffnessTOperator` is applied to a quadrature - field, and then eliminated by - :class:`grudge.symbolic.mappers.GlobalToReferenceMapper` - in favor of operators on the reference element. - """ - - 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.input_quadrature_tag, self.where) - - mapper_method = intern("map_quad_stiffness_t") - - -def DiffOperatorVector(els): - from grudge.tools import join_fields - return join_fields(*els) - # }}} # {{{ reference-element differentiation -class ReferenceDiffOperatorBase(Operator): - def __init__(self, rst_axis, where=None): - super(ReferenceDiffOperatorBase, self).__init__(where) +class RefDiffOperatorBase(ElementwiseLinearOperator): + def __init__(self, rst_axis, dd_in=None, dd_out=None): + if dd_in is None: + dd_in = _sym().DD_VOLUME + if dd_out is None: + dd_out = dd_in.with_qtag(_sym().QTAG_NONE) + + super(RefDiffOperatorBase, self).__init__(dd_in, dd_out) self.rst_axis = rst_axis def __getinitargs__(self): - return (self.rst_axis, self.where) + return (self.rst_axis, self.dd_in, self.dd_out) def equal_except_for_axis(self, other): return (type(self) == type(other) @@ -187,93 +204,22 @@ class ReferenceDiffOperatorBase(Operator): and self.__getinitargs__()[1:] == other.__getinitargs__()[1:]) -class ReferenceDifferentiationOperator(ReferenceDiffOperatorBase): +class RefDiffOperator(RefDiffOperatorBase): mapper_method = intern("map_ref_diff") -class ReferenceStiffnessTOperator(ReferenceDiffOperatorBase): +class RefStiffnessTOperator(RefDiffOperatorBase): mapper_method = intern("map_ref_stiffness_t") - -class ReferenceQuadratureStiffnessTOperator(ReferenceDiffOperatorBase): - """ - .. note:: - - This operator is purely for internal use. It is inserted - by :class:`grudge.symbolic.mappers.OperatorSpecializer` - when a :class:`StiffnessTOperator` is applied to a quadrature field. - """ - - 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.input_quadrature_tag, - self.where) - - mapper_method = intern("map_ref_quad_stiffness_t") - -# }}} - # }}} - -# {{{ elementwise operators - -class ElementwiseLinearOperator(Operator): - def matrix(self, element_group): - raise NotImplementedError - - mapper_method = intern("map_elementwise_linear") - - -class ElementwiseMaxOperator(Operator): - mapper_method = intern("map_elementwise_max") - - -# {{{ quadrature upsamplers - -class QuadratureGridUpsampler(Operator): - """In a user-specified optemplate, this operator can be used to interpolate - volume and boundary data to their corresponding quadrature grids. - - In pre-processing, the boundary quad interpolation is specialized to - a separate operator, :class:`QuadratureBoundaryGridUpsampler`. - """ - def __init__(self, quadrature_tag, where=None): - self.quadrature_tag = quadrature_tag - self.where = where - - def __getinitargs__(self): - return (self.quadrature_tag,) - - mapper_method = intern("map_quad_grid_upsampler") - - -class QuadratureInteriorFacesGridUpsampler(Operator): - """Interpolates nodal volume data to interior face data on a quadrature - grid. - - Note that the "interior faces" grid includes faces lying opposite to the - boundary. - """ - def __init__(self, quadrature_tag): - self.quadrature_tag = quadrature_tag - - def __getinitargs__(self): - return (self.quadrature_tag,) - - mapper_method = intern("map_quad_int_faces_grid_upsampler") - # }}} # {{{ various elementwise linear operators class FilterOperator(ElementwiseLinearOperator): - def __init__(self, mode_response_func): + def __init__(self, mode_response_func, dd_in=None, dd_out=None): """ :param mode_response_func: A function mapping ``(mode_tuple, local_discretization)`` to a float indicating the @@ -281,12 +227,27 @@ class FilterOperator(ElementwiseLinearOperator): (For example an instance of :class:`ExponentialFilterResponseFunction`. """ + if dd_in is None: + dd_in = _sym().DD_VOLUME + if dd_out is None: + dd_out = dd_in + + if dd_in.uses_quadrature(): + raise ValueError("dd_in may not use quadrature") + if dd_in != dd_out: + raise ValueError("dd_in and dd_out must be identical") + + super(FilterOperator, self).__init__(dd_in, dd_out) + self.mode_response_func = mode_response_func def __getinitargs__(self): - return (self.mode_response_func,) + return (self.mode_response_func, self.dd_in, self.dd_out) def matrix(self, eg): + # FIXME + raise NotImplementedError() + ldis = eg.local_discretization filter_coeffs = [self.mode_response_func(mid, ldis) @@ -303,19 +264,27 @@ class FilterOperator(ElementwiseLinearOperator): return mat -class OnesOperator(ElementwiseLinearOperator): - def matrix(self, eg): - ldis = eg.local_discretization +class AveragingOperator(ElementwiseLinearOperator): + def __init__(self, dd_in=None, dd_out=None): + if dd_in is None: + dd_in = _sym().DD_VOLUME + if dd_out is None: + dd_out = dd_in - node_count = ldis.node_count() - return np.ones((node_count, node_count), dtype=np.float64) + if dd_in.uses_quadrature(): + raise ValueError("dd_in may not use quadrature") + if dd_in != dd_out: + raise ValueError("dd_in and dd_out must be identical") + super(FilterOperator, self).__init__(dd_in, dd_out) -class AveragingOperator(ElementwiseLinearOperator): def matrix(self, eg): # average matrix, so that AVE*fields = cellaverage(fields) # see Hesthaven and Warburton page 227 + # FIXME + raise NotImplementedError() + mmat = eg.local_discretization.mass_matrix() standard_el_vol = np.sum(np.dot(mmat, np.ones(mmat.shape[0]))) avg_mat_row = np.sum(mmat, 0)/standard_el_vol @@ -326,17 +295,13 @@ class AveragingOperator(ElementwiseLinearOperator): class InverseVandermondeOperator(ElementwiseLinearOperator): - def matrix(self, eg): - return np.asarray( - la.inv(eg.local_discretization.vandermonde()), - order="C") + def matrix(self, element_group): + raise NotImplementedError() # FIXME class VandermondeOperator(ElementwiseLinearOperator): - def matrix(self, eg): - return np.asarray( - eg.local_discretization.vandermonde(), - order="C") + def matrix(self, element_group): + raise NotImplementedError() # FIXME # }}} @@ -346,7 +311,19 @@ class VandermondeOperator(ElementwiseLinearOperator): # {{{ mass operators class MassOperatorBase(Operator): - pass + def __init__(self, dd_in=None, dd_out=None): + if dd_in is None: + dd_in = _sym().DD_VOLUME + if dd_out is None: + dd_out = dd_in + + if not dd_in.is_volume(): + raise ValueError("mass operators only work on volume " + "discretization") + if dd_out != dd_in: + raise ValueError("dd_out and dd_in must be identical") + + super(MassOperatorBase, self).__init__(dd_in, dd_out) class MassOperator(MassOperatorBase): @@ -357,51 +334,11 @@ class InverseMassOperator(MassOperatorBase): mapper_method = intern("map_inverse_mass") -class QuadratureMassOperator(Operator): - """ - .. note:: - - This operator is purely for internal use. It is inserted - by :class:`grudge.symbolic.mappers.OperatorSpecializer` - when a :class:`StiffnessTOperator` is applied to a quadrature - field, and then eliminated by - :class:`grudge.symbolic.mappers.GlobalToReferenceMapper` - in favor of operators on the reference element. - """ - - def __init__(self, quadrature_tag): - self.quadrature_tag = quadrature_tag - - def __getinitargs__(self): - return (self.quadrature_tag,) - - mapper_method = intern("map_quad_mass") - - -class ReferenceQuadratureMassOperator(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, where=None): - super(Operator, self).__init__(self.where) - self.quadrature_tag = quadrature_tag - - def __getinitargs__(self): - return (self.quadrature_tag, self.where) - - mapper_method = intern("map_ref_quad_mass") - - -class ReferenceMassOperatorBase(MassOperatorBase): +class RefMassOperatorBase(ElementwiseLinearOperator): pass -class ReferenceMassOperator(ReferenceMassOperatorBase): +class RefMassOperator(RefMassOperatorBase): @staticmethod def matrix(element_group): import modepy as mp @@ -412,7 +349,7 @@ class ReferenceMassOperator(ReferenceMassOperatorBase): mapper_method = intern("map_ref_mass") -class ReferenceInverseMassOperator(ReferenceMassOperatorBase): +class RefInverseMassOperator(RefMassOperatorBase): @staticmethod def matrix(element_group): import modepy as mp @@ -427,319 +364,53 @@ class ReferenceInverseMassOperator(ReferenceMassOperatorBase): # {{{ boundary-related operators -class RestrictToBoundary(Operator): - def __init__(self, tag): - self.tag = tag - - def __getinitargs__(self): - return (self.tag,) - - mapper_method = intern("map_boundarize") - - -class FluxExchangeOperator(pymbolic.primitives.AlgebraicLeaf): - """An operator that results in the sending and receiving of - boundary information for its argument fields. - """ - - def __init__(self, idx, rank, arg_fields): - self.index = idx - self.rank = rank - self.arg_fields = arg_fields - - # only tuples are hashable - if not isinstance(arg_fields, tuple): - raise TypeError("FluxExchangeOperator: arg_fields must be a tuple") - - def __getinitargs__(self): - return (self.index, self.rank, self.arg_fields) - - def get_hash(self): - return hash((self.__class__, self.index, self.rank, self.arg_fields)) - - mapper_method = intern("map_flux_exchange") +class OppositeInteriorFaceSwap(Operator): + def __init__(self, dd_in=None, dd_out=None): + sym = _sym() - def is_equal(self, other): - return self.__class__ == other.__class__ and \ - self.__getinitargs__() == other.__getinitargs__() + if dd_in is None: + dd_in = sym.DOFDesc(sym.FRESTR_INTERIOR_FACES, None) + if dd_out is None: + dd_out = dd_in -# }}} - - -# {{{ flux-like operators + if dd_in.domain_tag is not sym.FRESTR_INTERIOR_FACES: + raise ValueError("dd_in must be an interior faces domain") + if dd_out != dd_in: + raise ValueError("dd_out and dd_in must be identical") -class FluxOperatorBase(Operator): - def __init__(self, flux, is_lift=False): - Operator.__init__(self) - self.flux = flux - self.is_lift = is_lift + super(OppositeInteriorFaceSwap, self).__init__(dd_in, dd_out) - def get_flux_or_lift_text(self): - if self.is_lift: - return "Lift" - else: - return "Flux" + mapper_method = intern("map_opposite_interior_face_swap") - def repr_op(self): - """Return an equivalent operator with the flux expression set to 0.""" - return type(self)(0, *self.__getinitargs__()[1:]) - def __call__(self, arg): - # override to suppress apply-operator-to-each-operand - # behavior from superclass +class FaceMassOperator(ElementwiseLinearOperator): + def __init__(self, dd_in=None, dd_out=None): + sym = _sym() - from grudge.symbolic.primitives import OperatorBinding - return OperatorBinding(self, arg) + if dd_in is None: + dd_in = sym.DOFDesc(sym.FRESTR_ALL_FACES, None) - def __mul__(self, arg): - from warnings import warn - warn("Multiplying by a flux operator is deprecated. " - "Use the less ambiguous parenthesized syntax instead.", - DeprecationWarning, stacklevel=2) - return self.__call__(arg) + if dd_out is None: + dd_out = sym.DOFDesc("vol", None) + if not dd_out.is_volume(): + raise ValueError("dd_out must be a volume domain") + if dd_in.domain_tag is not sym.FRESTR_ALL_FACES: + raise ValueError("dd_in must be an interior faces domain") -class QuadratureFluxOperatorBase(FluxOperatorBase): - pass - + super(FaceMassOperator, self).__init__(dd_in, dd_out) -class BoundaryFluxOperatorBase(FluxOperatorBase): - pass - - -class FluxOperator(FluxOperatorBase): - def __getinitargs__(self): - return (self.flux, self.is_lift) - - mapper_method = intern("map_flux") - - -class BoundaryFluxOperator(BoundaryFluxOperatorBase): - """ - .. note:: - - This operator is purely for internal use. It is inserted - by :class:`grudge.symbolic.mappers.OperatorSpecializer` - when a :class:`FluxOperator` is applied to a boundary field. - """ - def __init__(self, flux, boundary_tag, is_lift=False): - FluxOperatorBase.__init__(self, flux, is_lift) - self.boundary_tag = boundary_tag - - def __getinitargs__(self): - return (self.flux, self.boundary_tag, self.is_lift) - - mapper_method = intern("map_bdry_flux") - - -class QuadratureFluxOperator(QuadratureFluxOperatorBase): - """ - .. note:: - - This operator is purely for internal use. It is inserted - by :class:`grudge.symbolic.mappers.OperatorSpecializer` - when a :class:`FluxOperator` is applied to a quadrature field. - """ - - def __init__(self, flux, quadrature_tag): - FluxOperatorBase.__init__(self, flux, is_lift=False) - - self.quadrature_tag = quadrature_tag - - def __getinitargs__(self): - return (self.flux, self.quadrature_tag) - - mapper_method = intern("map_quad_flux") - - -class QuadratureBoundaryFluxOperator( - QuadratureFluxOperatorBase, BoundaryFluxOperatorBase): - """ - .. note:: - - This operator is purely for internal use. It is inserted - by :class:`grudge.symbolic.mappers.OperatorSpecializer` - when a :class:`FluxOperator` is applied to a quadrature - boundary field. - """ - def __init__(self, flux, quadrature_tag, boundary_tag): - FluxOperatorBase.__init__(self, flux, is_lift=False) - self.quadrature_tag = quadrature_tag - self.boundary_tag = boundary_tag - - def __getinitargs__(self): - return (self.flux, self.quadrature_tag, self.boundary_tag) - - mapper_method = intern("map_quad_bdry_flux") - - -class VectorFluxOperator(object): - """Note that this isn't an actual operator. It's just a placeholder that pops - out a vector of FluxOperators when applied to an operand. - """ - def __init__(self, fluxes): - self.fluxes = fluxes - - def __call__(self, arg): - if isinstance(arg, int) and arg == 0: - return 0 - from pytools.obj_array import make_obj_array - from grudge.symbolic.primitives import OperatorBinding - - return make_obj_array( - [OperatorBinding(FluxOperator(f), arg) - for f in self.fluxes]) - - def __mul__(self, arg): - from warnings import warn - warn("Multiplying by a vector flux operator is deprecated. " - "Use the less ambiguous parenthesized syntax instead.", - DeprecationWarning, stacklevel=2) - return self.__call__(arg) - - -class WholeDomainFluxOperator(pymbolic.primitives.AlgebraicLeaf): - """Used by the CUDA backend to represent a flux computation on the - whole domain--interior and boundary. - - Unlike other operators, :class:`WholeDomainFluxOperator` instances - are not bound. - """ - - class FluxInfo(Record): - __slots__ = [] - - def __repr__(self): - # override because we want flux_expr in infix - return "%s(%s)" % ( - self.__class__.__name__, - ", ".join("%s=%s" % (fld, getattr(self, fld)) - for fld in self.__class__.fields - if hasattr(self, fld))) - - class InteriorInfo(FluxInfo): - # attributes: flux_expr, field_expr, - - @property - @memoize_method - def dependencies(self): - from grudge.symbolic.tools import get_flux_dependencies - return set(get_flux_dependencies( - self.flux_expr, self.field_expr)) - - class BoundaryInfo(FluxInfo): - # attributes: flux_expr, bpair - - @property - @memoize_method - def int_dependencies(self): - from grudge.symbolic.tools import get_flux_dependencies - return set(get_flux_dependencies( - self.flux_expr, self.bpair, bdry="int")) - - @property - @memoize_method - def ext_dependencies(self): - from grudge.symbolic.tools import get_flux_dependencies - return set(get_flux_dependencies( - self.flux_expr, self.bpair, bdry="ext")) - - def __init__(self, is_lift, interiors, boundaries, - quadrature_tag): - from grudge.symbolic.tools import get_flux_dependencies - - self.is_lift = is_lift - - self.interiors = tuple(interiors) - self.boundaries = tuple(boundaries) - self.quadrature_tag = quadrature_tag - - from pytools import set_sum - interior_deps = set_sum(iflux.dependencies - for iflux in interiors) - boundary_int_deps = set_sum(bflux.int_dependencies - for bflux in boundaries) - boundary_ext_deps = set_sum(bflux.ext_dependencies - for bflux in boundaries) - - self.interior_deps = list(interior_deps) - self.boundary_int_deps = list(boundary_int_deps) - self.boundary_ext_deps = list(boundary_ext_deps) - self.boundary_deps = list(boundary_int_deps | boundary_ext_deps) - - self.dep_to_tag = {} - for bflux in boundaries: - for dep in get_flux_dependencies( - bflux.flux_expr, bflux.bpair, bdry="ext"): - self.dep_to_tag[dep] = bflux.bpair.tag - - def stringifier(self): - from grudge.symbolic.mappers import StringifyMapper - return StringifyMapper - - def repr_op(self): - return type(self)(False, [], [], self.quadrature_tag) - - @memoize_method - def rebuild_optemplate(self): - def generate_summands(): - for i in self.interiors: - if self.quadrature_tag is None: - yield FluxOperator( - i.flux_expr, self.is_lift)(i.field_expr) - else: - yield QuadratureFluxOperator( - i.flux_expr, self.quadrature_tag)(i.field_expr) - for b in self.boundaries: - if self.quadrature_tag is None: - yield BoundaryFluxOperator( - b.flux_expr, b.bpair.tag, self.is_lift)(b.bpair) - else: - yield QuadratureBoundaryFluxOperator( - b.flux_expr, self.quadrature_tag, - b.bpair.tag)(b.bpair) - - from pymbolic.primitives import flattened_sum - return flattened_sum(generate_summands()) - - # infrastructure interaction - def get_hash(self): - return hash((self.__class__, self.rebuild_optemplate())) - - def is_equal(self, other): - return (other.__class__ == WholeDomainFluxOperator - and self.rebuild_optemplate() == other.rebuild_optemplate()) - - def __getinitargs__(self): - return (self.is_lift, self.interiors, self.boundaries, - self.quadrature_tag) - - mapper_method = intern("map_whole_domain_flux") + mapper_method = intern("map_face_mass_operator") # }}} # {{{ convenience functions for operator creation -def get_flux_operator(flux): - """Return a flux operator that can be multiplied with - a volume field to obtain the interior fluxes - or with a :class:`BoundaryPair` to obtain the lifted boundary - flux. - """ - from pytools.obj_array import is_obj_array - from grudge.symbolic.operators import VectorFluxOperator, FluxOperator - - if is_obj_array(flux): - return VectorFluxOperator(flux) - else: - return FluxOperator(flux) - - def nabla(dim): from pytools.obj_array import make_obj_array return make_obj_array( - [DifferentiationOperator(i) for i in range(dim)]) + [DiffOperator(i) for i in range(dim)]) def minv_stiffness_t(dim): @@ -760,19 +431,25 @@ def stiffness_t(dim): [StiffnessTOperator(i) for i in range(dim)]) -def integral(arg): +def integral(arg, dd=None): + if dd is None: + dd = _sym().DD_VOLUME + from grudge import sym - return sym.NodalSum()(sym.MassOperator()(sym.Ones())*arg) + return sym.NodalSum(dd)(sym.MassOperator()(sym.Ones(dd))*arg) -def norm(p, arg): +def norm(p, arg, dd=None): """ :arg arg: is assumed to be a vector, i.e. have shape ``(n,)``. """ import grudge.symbolic as sym + if dd is None: + dd = _sym().DD_VOLUME + if p == 2: - comp_norm_squared = sym.NodalSum()( + comp_norm_squared = sym.NodalSum(dd_in=dd)( sym.CFunction("fabs")( arg * sym.MassOperator()(arg))) return sym.CFunction("sqrt")(sum(comp_norm_squared)) @@ -783,7 +460,7 @@ def norm(p, arg): return reduce(Max, comp_norm) else: - return sum(sym.NodalSum()(sym.CFunction("fabs")(arg)**p))**(1/p) + return sum(sym.NodalSum(dd_in=dd)(sym.CFunction("fabs")(arg)**p))**(1/p) # }}} diff --git a/grudge/symbolic/primitives.py b/grudge/symbolic/primitives.py index 5be9bfda989b1e0a13bc848b215dd87e1eb88426..0411e888a1b198a3970e763a80e78afd4974decf 100644 --- a/grudge/symbolic/primitives.py +++ b/grudge/symbolic/primitives.py @@ -29,6 +29,8 @@ from six.moves import range, intern import numpy as np import pymbolic.primitives from meshmode.mesh import BTAG_ALL, BTAG_NONE # noqa +from meshmode.discretization.connection import ( # noqa + FRESTR_ALL_FACES, FRESTR_INTERIOR_FACES) from pymbolic.primitives import ( # noqa cse_scope as cse_scope_base, @@ -37,24 +39,40 @@ from pymbolic.geometric_algebra import MultiVector from pytools.obj_array import join_fields, make_obj_array # noqa -class LeafBase(pymbolic.primitives.AlgebraicLeaf): +class ExpressionBase(pymbolic.primitives.Expression): def stringifier(self): from grudge.symbolic.mappers import StringifyMapper return StringifyMapper -class VTAG_ALL: - """This is used in a 'where' field to denote the volume discretization.""" - pass +def _sym(): + # A hack to make referring to grudge.sym possible without + # circular imports and tons of typing. + + from grudge import sym + return sym __doc__ = """ + +.. currentmodule:: grudge.sym + +DOF description +^^^^^^^^^^^^^^^ + +.. autoclass:: DTAG_SCALAR +.. autoclass:: DTAG_VOLUME_ALL +.. autoclass:: QTAG_NONE +.. autoclass:: DOFDesc +.. data:: DD_SCALAR +.. data:: DD_VOLUME + Symbols ^^^^^^^ -.. autoclass:: Field -.. autoclass:: make_sym_vector +.. autoclass:: Variable .. autoclass:: make_sym_array +.. autoclass:: make_sym_mv .. autoclass:: ScalarParameter .. autoclass:: CFunction @@ -82,33 +100,205 @@ Geometry data """ +# {{{ DOF description + +class DTAG_SCALAR: + pass + + +class DTAG_VOLUME_ALL: + pass + + +class QTAG_NONE: + pass + + +class DOFDesc(object): + """Describes the meaning of degrees of freedom. + + .. attribute:: domain_tag + .. attribute:: quadrature_tag + .. automethod:: is_scalar + .. automethod:: is_discretized + .. automethod:: is_volume + .. automethod:: is_boundary + .. automethod:: uses_quadrature + .. automethod:: with_qtag + .. automethod:: with_dtag + .. automethod:: __eq__ + .. automethod:: __ne__ + .. automethod:: __hash__ + """ + + def __init__(self, domain_tag, quadrature_tag=None): + """ + :arg domain_tag: One of the following: + :class:`grudge.sym.DTAG_SCALAR` (or the string ``"scalar"``), + :class:`grudge.sym.DTAG_VOLUME_ALL` (or the string ``"vol"``) + for the default volume discretization, + :class:`meshmode.discretization.connection.FRESTR_ALL_FACES` + (or the string ``"all_faces"``), + or + :class:`meshmode.discretization.connection.FRESTR_INTERIOR_FACES` + (or the string ``"int_faces"``), + or *None* to indicate that the geometry is not yet known. + + :arg quadrature_tag: + *None* to indicate that the quadrature grid is not known,or + :class:`QTAG_NONE` to indicate the use of the basic discretization + grid, or a string to indicate the use of the thus-tagged quadratue + grid. + """ + if domain_tag == "scalar": + domain_tag = DTAG_SCALAR + elif domain_tag is DTAG_SCALAR: + domain_tag = DTAG_SCALAR + elif domain_tag == "vol": + domain_tag = DTAG_VOLUME_ALL + elif domain_tag is DTAG_VOLUME_ALL: + pass + elif domain_tag == "all_faces": + domain_tag = FRESTR_ALL_FACES + elif domain_tag is FRESTR_ALL_FACES: + pass + elif domain_tag == "int_faces": + domain_tag = FRESTR_INTERIOR_FACES + elif domain_tag is FRESTR_INTERIOR_FACES: + pass + elif domain_tag is None: + pass + + if domain_tag is DTAG_SCALAR and quadrature_tag is not None: + raise ValueError("cannot have nontrivial quadrature tag on scalar") + + self.domain_tag = domain_tag + self.quadrature_tag = quadrature_tag + + def is_scalar(self): + return self.domain_tag is DTAG_SCALAR + + def is_discretized(self): + return not self.is_scalar() + + def is_volume(self): + return self.domain_tag is DTAG_VOLUME_ALL + + def is_boundary(self): + return not self.is_volume() + + def uses_quadrature(self): + if self.quadrature_tag is None: + return False + if self.quadrature_tag is QTAG_NONE: + return False + + return True + + def with_qtag(self, qtag): + return type(self)(domain_tag=self.domain_tag, quadrature_tag=qtag) + + def with_dtag(self, dtag): + return type(self)(domain_tag=dtag, quadrature_tag=self.quadrature_tag) + + def __eq__(self, other): + return (type(self) == type(other) + and self.domain_tag == other.domain_tag + and self.quadrature_tag == other.quadrature_tag) + + def __ne__(self, other): + return not self.__eq__(other) + + def __hash__(self): + return hash((type(self), self.domain_tag, self.quadrature_tag)) + + def __repr__(self): + def fmt(s): + if isinstance(s, type): + return s.__name__ + else: + return repr(s) + + return "DOFDesc(%s, %s)" % (fmt(self.domain_tag), fmt(self.quadrature_tag)) + + +DD_SCALAR = DOFDesc(DTAG_SCALAR, None) + +DD_VOLUME = DOFDesc(DTAG_VOLUME_ALL, None) + + +def as_dofdesc(dd): + if isinstance(dd, DOFDesc): + return dd + else: + return DOFDesc(dd, None) + +# }}} + + +# {{{ has-dof-desc mix-in + +class HasDOFDesc(object): + """ + .. attribute:: dd + + an instance of :class:`grudge.sym.DOFDesc` describing the + discretization on which this property is given. + """ + + def __init__(self, dd): + self.dd = dd + + def __getinitargs__(self): + return (self.dd,) + + def with_dd(self, dd): + """Return a copy of *self*, modified to the given DOF descriptor. + """ + return type(self)( + *self.__getinitargs__()[:-1], + dd=dd or self.dd) + +# }}} + + # {{{ variables class cse_scope(cse_scope_base): # noqa DISCRETIZATION = "grudge_discretization" -Field = pymbolic.primitives.Variable +class Variable(HasDOFDesc, ExpressionBase, pymbolic.primitives.Variable): + def __init__(self, name, dd=None): + if dd is None: + dd = DD_VOLUME -make_sym_vector = pymbolic.primitives.make_sym_vector -make_sym_array = pymbolic.primitives.make_sym_array + HasDOFDesc.__init__(self, dd) + pymbolic.primitives.Variable.__init__(self, name) + def __getinitargs__(self): + return (self.name, self.dd,) -def make_field(var_or_string): - if not isinstance(var_or_string, pymbolic.primitives.Expression): - return Field(var_or_string) - else: - return var_or_string + mapper_method = "map_grudge_variable" +var = Variable -class ScalarParameter(pymbolic.primitives.Variable): - """A placeholder for a user-supplied scalar variable.""" - def stringifier(self): - from grudge.symbolic.mappers import StringifyMapper - return StringifyMapper +class ScalarVariable(Variable): + def __init__(self, name): + super(ScalarVariable, self).__init__(name, DD_SCALAR) + - mapper_method = intern("map_scalar_parameter") +def make_sym_array(name, shape, dd=None): + def var_factory(name): + return Variable(name, dd) + + return pymbolic.primitives.make_sym_array(name, shape, var_factory) + + +def make_sym_mv(name, dim, var_factory=None): + return MultiVector( + make_sym_array(name, dim, var_factory)) class CFunction(pymbolic.primitives.Variable): @@ -139,7 +329,7 @@ cos = CFunction("cos") # {{{ technical helpers -class OperatorBinding(LeafBase): +class OperatorBinding(ExpressionBase): def __init__(self, op, field): self.op = op self.field = field @@ -179,99 +369,47 @@ class PrioritizedSubexpression(pymbolic.primitives.CommonSubexpression): def get_extra_properties(self): return {"priority": self.priority} - -class BoundaryPair(LeafBase): - """Represents a pairing of a volume and a boundary field, used for the - application of boundary fluxes. - """ - - def __init__(self, field, bfield, tag=BTAG_ALL): - self.field = field - self.bfield = bfield - self.tag = tag - - mapper_method = intern("map_boundary_pair") - - def __getinitargs__(self): - return (self.field, self.bfield, self.tag) - - def get_hash(self): - from pytools.obj_array import obj_array_to_hashable - - return hash((self.__class__, - obj_array_to_hashable(self.field), - obj_array_to_hashable(self.bfield), - self.tag)) - - def is_equal(self, other): - from pytools.obj_array import obj_array_equal - return (self.__class__ == other.__class__ - and obj_array_equal(other.field, self.field) - and obj_array_equal(other.bfield, self.bfield) - and other.tag == self.tag) - # }}} -class Ones(LeafBase): - def __init__(self, quadrature_tag=None, where=None): - self.where = where - self.quadrature_tag = quadrature_tag - - def __getinitargs__(self): - return (self.where, self.quadrature_tag,) - +class Ones(ExpressionBase, HasDOFDesc): mapper_method = intern("map_ones") # {{{ geometry data -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. - - .. attribute:: quadrature_tag - - quadrature tag for the grid on - which this geometric factor is needed, or None for - nodal representation. - """ - - def __init__(self, quadrature_tag, where=None): - self.quadrature_tag = quadrature_tag - self.where = where - - def __getinitargs__(self): - return (self.quadrature_tag, self.where) +class DiscretizationProperty(ExpressionBase, HasDOFDesc): + pass class NodeCoordinateComponent(DiscretizationProperty): + def __init__(self, axis, dd=None): + if dd is None: + dd = DD_VOLUME + + if not dd.is_discretized(): + raise ValueError("dd must be a discretization for " + "NodeCoordinateComponent") - def __init__(self, axis, quadrature_tag=None, where=None): - super(NodeCoordinateComponent, self).__init__(quadrature_tag, where) + super(NodeCoordinateComponent, self).__init__(dd) self.axis = axis def __getinitargs__(self): - return (self.axis, self.quadrature_tag) + return (self.axis, self.dd) mapper_method = intern("map_node_coordinate_component") -def nodes(ambient_dim, quadrature_tag=None, where=None): - return np.array([NodeCoordinateComponent(i, quadrature_tag, where) +def nodes(ambient_dim, dd=None): + return np.array([NodeCoordinateComponent(i, dd) for i in range(ambient_dim)], dtype=object) -def mv_nodes(ambient_dim, quadrature_tag=None, where=None): - return MultiVector( - nodes(ambient_dim, quadrature_tag=quadrature_tag, where=where)) +def mv_nodes(ambient_dim, dd=None): + return MultiVector(nodes(ambient_dim, dd)) -def forward_metric_derivative(xyz_axis, rst_axis, where=None, - quadrature_tag=None): +def forward_metric_derivative(xyz_axis, rst_axis, dd=None): r""" Pointwise metric derivatives representing @@ -279,15 +417,17 @@ def forward_metric_derivative(xyz_axis, rst_axis, where=None, \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) + if dd is None: + dd = DD_VOLUME + + inner_dd = dd.with_qtag(QTAG_NONE) - result = diff_op(NodeCoordinateComponent(xyz_axis, where=where)) + diff_op = _sym().RefDiffOperator(rst_axis, inner_dd) - if quadrature_tag is not None: - result = QuadratureGridUpsampler(quadrature_tag, where)(result) + result = diff_op(NodeCoordinateComponent(xyz_axis, inner_dd)) + + if dd.uses_quadrature(): + result = _sym().interp(inner_dd, dd)(result) return cse( result, @@ -295,35 +435,29 @@ def forward_metric_derivative(xyz_axis, rst_axis, where=None, scope=cse_scope.DISCRETIZATION) -def forward_metric_derivative_vector(ambient_dim, rst_axis, where=None, - quadrature_tag=None): +def forward_metric_derivative_vector(ambient_dim, rst_axis, dd=None): return make_obj_array([ - forward_metric_derivative( - i, rst_axis, where=where, quadrature_tag=quadrature_tag) + forward_metric_derivative(i, rst_axis, dd=dd) for i in range(ambient_dim)]) -def forward_metric_derivative_mv(ambient_dim, rst_axis, where=None, - quadrature_tag=None): +def forward_metric_derivative_mv(ambient_dim, rst_axis, dd=None): return MultiVector( - forward_metric_derivative_vector( - ambient_dim, rst_axis, where=where, quadrature_tag=quadrature_tag)) + forward_metric_derivative_vector(ambient_dim, rst_axis, dd=dd)) -def parametrization_derivative(ambient_dim, dim=None, where=None, - quadrature_tag=None): +def parametrization_derivative(ambient_dim, dim=None, dd=None): if dim is None: dim = ambient_dim from pytools import product return product( - forward_metric_derivative_mv( - ambient_dim, rst_axis, where, quadrature_tag) + forward_metric_derivative_mv(ambient_dim, rst_axis, dd) for rst_axis in range(dim)) def inverse_metric_derivative(rst_axis, xyz_axis, ambient_dim, dim=None, - where=None, quadrature_tag=None): + dd=None): if dim is None: dim = ambient_dim @@ -332,8 +466,7 @@ def inverse_metric_derivative(rst_axis, xyz_axis, ambient_dim, dim=None, "the derivative matrix is not square") par_vecs = [ - forward_metric_derivative_mv( - ambient_dim, rst, where, quadrature_tag) + forward_metric_derivative_mv(ambient_dim, rst, dd) for rst in range(dim)] # Yay Cramer's rule! (o rly?) @@ -352,7 +485,7 @@ def inverse_metric_derivative(rst_axis, xyz_axis, ambient_dim, dim=None, volume_pseudoscalar_inv = cse(outerprod( forward_metric_derivative_mv( - ambient_dim, rst_axis, where, quadrature_tag) + ambient_dim, rst_axis, dd=dd) for rst_axis in range(dim)).inv()) return cse( @@ -362,20 +495,17 @@ def inverse_metric_derivative(rst_axis, xyz_axis, ambient_dim, dim=None, scope=cse_scope.DISCRETIZATION) -def forward_metric_derivative_mat(ambient_dim, dim=None, - where=None, quadrature_tag=None): +def forward_metric_derivative_mat(ambient_dim, dim=None, dd=None): if dim is None: dim = ambient_dim result = np.zeros((ambient_dim, dim), dtype=np.object) for j in range(dim): - result[:, j] = forward_metric_derivative_vector(ambient_dim, j, - where=where, quadrature_tag=quadrature_tag) + result[:, j] = forward_metric_derivative_vector(ambient_dim, j, dd=dd) return result -def inverse_metric_derivative_mat(ambient_dim, dim=None, - where=None, quadrature_tag=None): +def inverse_metric_derivative_mat(ambient_dim, dim=None, dd=None): if dim is None: dim = ambient_dim @@ -383,34 +513,37 @@ def inverse_metric_derivative_mat(ambient_dim, dim=None, for i in range(dim): for j in range(ambient_dim): result[i, j] = inverse_metric_derivative( - i, j, ambient_dim, dim, - where=where, quadrature_tag=quadrature_tag) + i, j, ambient_dim, dim, dd=dd) return result -def pseudoscalar(ambient_dim, dim=None, where=None, quadrature_tag=None): +def pseudoscalar(ambient_dim, dim=None, dd=None): if dim is None: dim = ambient_dim return cse( - parametrization_derivative(ambient_dim, dim, where=where, - quadrature_tag=quadrature_tag) + parametrization_derivative(ambient_dim, dim, dd=dd) .project_max_grade(), "pseudoscalar", cse_scope.DISCRETIZATION) -def area_element(ambient_dim, dim=None, where=None, quadrature_tag=None): +def area_element(ambient_dim, dim=None, dd=None): return cse( sqrt( - pseudoscalar(ambient_dim, dim, where, quadrature_tag=quadrature_tag) + pseudoscalar(ambient_dim, dim, dd=dd) .norm_squared()), "area_element", cse_scope.DISCRETIZATION) -def mv_normal(tag, ambient_dim, dim=None, quadrature_tag=None): +def mv_normal(dd, ambient_dim, dim=None): """Exterior unit normal as a MultiVector.""" + dd = as_dofdesc(dd) + + if not dd.is_boundary(): + raise ValueError("may only request normals on boundaries") + if dim is None: dim = ambient_dim - 1 @@ -418,16 +551,95 @@ def mv_normal(tag, ambient_dim, dim=None, quadrature_tag=None): # exterior normals for positively oriented curves. pder = ( - pseudoscalar(ambient_dim, dim, tag, quadrature_tag=quadrature_tag) + pseudoscalar(ambient_dim, dim, dd=dd) / - area_element(ambient_dim, dim, tag, quadrature_tag=quadrature_tag)) + area_element(ambient_dim, dim, dd=dd)) return cse(pder.I | pder, "normal", cse_scope.DISCRETIZATION) -def normal(tag, ambient_dim, dim=None, quadrature_tag=None): - return mv_normal(tag, ambient_dim, dim, - quadrature_tag=quadrature_tag).as_vector() +def normal(dd, ambient_dim, dim=None, quadrature_tag=None): + return mv_normal(dd, ambient_dim, dim).as_vector() + +# }}} + + +# {{{ flux parir + +class FluxPair: + """ + .. attribute:: dd + + an instance of :class:`grudge.sym.DOFDesc` describing the + discretization on which :attr:`interior` and :attr:`exterior` + live. + + .. attribute:: interior + + an expression representing the interior value to + be used for the flux. + + .. attribute:: exterior + + an expression representing the exterior value to + be used for the flux. + """ + def __init__(self, dd, interior, exterior): + """ + """ + + self.dd = as_dofdesc(dd) + self.interior = interior + self.exterior = exterior + + def __getitem__(self, index): + return FluxPair( + self.dd, + self.exterior[index], + self.interior[index]) + + @property + def int(self): + return self.interior + + @property + def ext(self): + return self.exterior + + @property + def avg(self): + return 0.5*(self.int + self.ext) + + +def int_fpair(expression): + i = cse(_sym().interp("vol", "int_faces")(expression)) + e = cse(_sym().OppositeInteriorFaceSwap()(i)) + return FluxPair("int_faces", i, e) + + +def bdry_fpair(dd, interior, exterior): + """ + :arg interior: an expression that already lives on the boundary + representing the interior value to be used + for the flux. + :arg exterior: an expression that already lives on the boundary + representing the exterior value to be used + for the flux. + """ + return FluxPair(dd, interior, exterior) + + +def bv_fpair(dd, interior, exterior): + """ + :arg interior: an expression that lives in the volume + and will be restricted to the boundary identified by + *tag* before use. + :arg exterior: an expression that already lives on the boundary + representing the exterior value to be used + for the flux. + """ + interior = _sym().cse(_sym().interp("vol", dd)(interior)) + return FluxPair(dd, interior, exterior) # }}} diff --git a/grudge/symbolic/tools.py b/grudge/symbolic/tools.py index 524b7cefeda8942d1802dc091187ee5bcb396f75..55a654d619e9f908f851482388191732d9774bb7 100644 --- a/grudge/symbolic/tools.py +++ b/grudge/symbolic/tools.py @@ -33,48 +33,10 @@ import numpy as np def is_scalar(expr): from grudge import sym - return isinstance(expr, (int, float, complex, sym.ScalarParameter)) + if isinstance(expr, sym.Variable) and expr.dd.domain_tag is sym.DTAG_SCALAR: + return True - -def get_flux_dependencies(flux, field, bdry="all"): - from grudge.symbolic.flux.mappers import FluxDependencyMapper - from grudge.symbolic.flux.primitives import FieldComponent - in_fields = list(FluxDependencyMapper( - include_calls=False)(flux)) - - # check that all in_fields are FieldComponent instances - assert not [in_field - for in_field in in_fields - if not isinstance(in_field, FieldComponent)] - - def maybe_index(fld, index): - from pytools.obj_array import is_obj_array - if is_obj_array(fld): - return fld[inf.index] - else: - return fld - - from grudge.tools import is_zero - from grudge import sym - if isinstance(field, sym.BoundaryPair): - for inf in in_fields: - if inf.is_interior: - if bdry in ["all", "int"]: - value = maybe_index(field.field, inf.index) - - if not is_zero(value): - yield value - else: - if bdry in ["all", "ext"]: - value = maybe_index(field.bfield, inf.index) - - if not is_zero(value): - yield value - else: - for inf in in_fields: - value = maybe_index(field, inf.index) - if not is_zero(value): - yield value + return isinstance(expr, (int, float, complex)) def split_sym_operator_for_multirate(state_vector, sym_operator, @@ -169,22 +131,10 @@ def pretty(sym_operator): splitter = "="*75 + "\n" - bc_strs = stringify_mapper.get_bc_strings() - if bc_strs: - result = "\n".join(bc_strs)+"\n"+splitter+result - cse_strs = stringify_mapper.get_cse_strings() if cse_strs: result = "\n".join(cse_strs)+"\n"+splitter+result - flux_strs = stringify_mapper.get_flux_strings() - if flux_strs: - result = "\n".join(flux_strs)+"\n"+splitter+result - - flux_cses = stringify_mapper.flux_stringify_mapper.get_cse_strings() - if flux_cses: - result = "\n".join("flux "+fs for fs in flux_cses)+"\n\n"+result - return result # }}} diff --git a/test/test_grudge.py b/test/test_grudge.py index 3754f49f4b596b335004ab3c4574a9e2619b302c..c137fe26660fca0263dd38030e31bcfa3fa80646 100644 --- a/test/test_grudge.py +++ b/test/test_grudge.py @@ -102,13 +102,13 @@ def test_1d_mass_mat_trig(ctx_getter): x = sym.nodes(1) f = bind(discr, sym.cos(x[0])**2)(queue) - ones = bind(discr, sym.Ones())(queue) + ones = bind(discr, sym.Ones(sym.DD_VOLUME))(queue) - mass_op = bind(discr, sym.MassOperator()(sym.Field("f"))) + mass_op = bind(discr, sym.MassOperator()(sym.var("f"))) num_integral_1 = np.dot(ones.get(), mass_op(queue, f=f).get()) num_integral_2 = np.dot(f.get(), mass_op(queue, f=ones).get()) - num_integral_3 = bind(discr, sym.integral(sym.Field("f")))(queue, f=f).get() + num_integral_3 = bind(discr, sym.integral(sym.var("f")))(queue, f=f).get() true_integral = 13*np.pi/2 err_1 = abs(num_integral_1-true_integral) @@ -148,7 +148,7 @@ def test_tri_diff_mat(ctx_getter, dim, order=4): f = bind(discr, sym.sin(3*x[axis]))(queue) df = bind(discr, 3*sym.cos(3*x[axis]))(queue) - sym_op = nabla[axis](sym.Field("f")) + sym_op = nabla[axis](sym.var("f")) bound_op = bind(discr, sym_op) df_num = bound_op(queue, f=f)