def make_linear_combiner(self, result_dtype, scalar_dtype, sample_vec, arg_count): """ :param result_dtype: dtype of the desired result. :param scalar_dtype: dtype of the scalars. :param sample_vec: must match states and right hand sides in shape, object array composition, and dtypes. :returns: a function that accepts `arg_count` arguments *((factor0, vec0), (factor1, vec1), ...)* and returns `factor0*vec0 + factor1*vec1`. """ from hedge.tools import is_obj_array sample_is_obj_array = is_obj_array(sample_vec) if sample_is_obj_array: sample_vec = sample_vec[0] if isinstance(sample_vec, numpy.ndarray) and sample_vec.dtype != object: kernel = NumpyLinearCombiner(result_dtype, scalar_dtype, sample_vec, arg_count) else: kernel = self.make_special_linear_combiner( result_dtype, scalar_dtype, sample_vec, arg_count) if kernel is None: from warnings import warn warn("using unoptimized linear combination routine" + _NO_VPF_SUGGESTION) kernel = UnoptimizedLinearCombiner(result_dtype, scalar_dtype) if sample_is_obj_array: kernel = ObjectArrayLinearCombinationWrapper(kernel) return kernel
def add_data(self, silo, variables=[], scalars=[], vectors=[], expressions=[], time=None, step=None, scale_factor=1): if scalars or vectors: import warnings warnings.warn("`scalars' and `vectors' arguments are deprecated", DeprecationWarning) variables = scalars + vectors from pyvisfile.silo import DB_NODECENT, DBOPT_DTIME, DBOPT_CYCLE # put mesh coordinates mesh_opts = {} if time is not None: mesh_opts[DBOPT_DTIME] = float(time) if step is not None: mesh_opts[DBOPT_CYCLE] = int(step) if self.dim == 1: for name, field in variables: from hedge.tools import is_obj_array if is_obj_array(field): AXES = ["x", "y", "z", "w"] for i, f_i in enumerate(field): silo.put_curve(name + AXES[i], self.xvals, scale_factor * f_i, mesh_opts) else: silo.put_curve(name, self.xvals, scale_factor * field, mesh_opts) else: self.fine_mesh.put_mesh(silo, "finezonelist", "finemesh", mesh_opts) self.coarse_mesh.put_mesh(silo, "coarsezonelist", "mesh", mesh_opts) from hedge.tools import log_shape # put data for name, field in variables: ls = log_shape(field) if ls != () and ls[0] > 1: assert len(ls) == 1 silo.put_ucdvar( name, "finemesh", ["%s_comp%d" % (name, i) for i in range(ls[0])], scale_factor * field, DB_NODECENT) else: if ls != (): field = field[0] silo.put_ucdvar1(name, "finemesh", scale_factor * field, DB_NODECENT) if expressions: silo.put_defvars("defvars", expressions)
def sub_bdry_into_flux(expr): if isinstance(expr, FieldComponent) and not expr.is_interior: if expr.index == 0 and not is_obj_array(bdry_field): return new_bdry_field else: return new_bdry_field[expr.index] else: return None
def dot_dataflow_graph(code, max_node_label_length=30, label_wrap_width=50): origins = {} node_names = {} result = [ "initial [label=\"initial\"]" "result [label=\"result\"]"] for num, insn in enumerate(code.instructions): node_name = "node%d" % num node_names[insn] = node_name node_label = str(insn) if max_node_label_length is not None: node_label = node_label[:max_node_label_length] if label_wrap_width is not None: from pytools import word_wrap node_label = word_wrap(node_label, label_wrap_width, wrap_using="\n ") node_label = node_label.replace("\n", "\\l") + "\\l" result.append("%s [ label=\"p%d: %s\" shape=box ];" % ( node_name, insn.priority, node_label)) for assignee in insn.get_assignees(): origins[assignee] = node_name def get_orig_node(expr): from pymbolic.primitives import Variable if isinstance(expr, Variable): return origins.get(expr.name, "initial") else: return "initial" def gen_expr_arrow(expr, target_node): result.append("%s -> %s [label=\"%s\"];" % (get_orig_node(expr), target_node, expr)) for insn in code.instructions: for dep in insn.get_dependencies(): gen_expr_arrow(dep, node_names[insn]) from hedge.tools import is_obj_array if is_obj_array(code.result): for subexp in code.result: gen_expr_arrow(subexp, "result") else: gen_expr_arrow(code.result, "result") return "digraph dataflow {\n%s\n}\n" % "\n".join(result)
def dot_dataflow_graph(code, max_node_label_length=30, label_wrap_width=50): origins = {} node_names = {} result = ["initial [label=\"initial\"]" "result [label=\"result\"]"] for num, insn in enumerate(code.instructions): node_name = "node%d" % num node_names[insn] = node_name node_label = str(insn) if max_node_label_length is not None: node_label = node_label[:max_node_label_length] if label_wrap_width is not None: from pytools import word_wrap node_label = word_wrap(node_label, label_wrap_width, wrap_using="\n ") node_label = node_label.replace("\n", "\\l") + "\\l" result.append("%s [ label=\"p%d: %s\" shape=box ];" % (node_name, insn.priority, node_label)) for assignee in insn.get_assignees(): origins[assignee] = node_name def get_orig_node(expr): from pymbolic.primitives import Variable if isinstance(expr, Variable): return origins.get(expr.name, "initial") else: return "initial" def gen_expr_arrow(expr, target_node): result.append("%s -> %s [label=\"%s\"];" % (get_orig_node(expr), target_node, expr)) for insn in code.instructions: for dep in insn.get_dependencies(): gen_expr_arrow(dep, node_names[insn]) from hedge.tools import is_obj_array if is_obj_array(code.result): for subexp in code.result: gen_expr_arrow(subexp, "result") else: gen_expr_arrow(code.result, "result") return "digraph dataflow {\n%s\n}\n" % "\n".join(result)
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 hedge.tools import is_obj_array from hedge.optemplate import VectorFluxOperator, FluxOperator if is_obj_array(flux): return VectorFluxOperator(flux) else: return FluxOperator(flux)
def add_data(self, silo, variables=[], scalars=[], vectors=[], expressions=[], time=None, step=None, scale_factor=1): if scalars or vectors: import warnings warnings.warn("`scalars' and `vectors' arguments are deprecated", DeprecationWarning) variables = scalars + vectors from pyvisfile.silo import DB_NODECENT, DBOPT_DTIME, DBOPT_CYCLE # put mesh coordinates mesh_opts = {} if time is not None: mesh_opts[DBOPT_DTIME] = float(time) if step is not None: mesh_opts[DBOPT_CYCLE] = int(step) if self.dim == 1: for name, field in variables: from hedge.tools import is_obj_array if is_obj_array(field): AXES = ["x", "y", "z", "w"] for i, f_i in enumerate(field): silo.put_curve(name+AXES[i], self.xvals, scale_factor*f_i, mesh_opts) else: silo.put_curve(name, self.xvals, scale_factor*field, mesh_opts) else: self.fine_mesh.put_mesh(silo, "finezonelist", "finemesh", mesh_opts) self.coarse_mesh.put_mesh(silo, "coarsezonelist", "mesh", mesh_opts) from hedge.tools import log_shape # put data for name, field in variables: ls = log_shape(field) if ls != () and ls[0] > 1: assert len(ls) == 1 silo.put_ucdvar(name, "finemesh", ["%s_comp%d" % (name, i) for i in range(ls[0])], scale_factor*field, DB_NODECENT) else: if ls != (): field = field[0] silo.put_ucdvar1( name, "finemesh", scale_factor*field, DB_NODECENT) if expressions: silo.put_defvars("defvars", expressions)
def func_on_scalar_or_vector(func, arg_fields): # No CSE necessary here--the compiler CSE's these # automatically. from hedge.tools import is_obj_array, make_obj_array if is_obj_array(arg_fields): # arg_fields (as an object array) isn't hashable # --make it so by turning it into a tuple arg_fields = tuple(arg_fields) return make_obj_array([ func(i, arg_fields) for i in range(len(arg_fields)) ]) else: return func(0, (arg_fields, ))
def func_on_scalar_or_vector(func, arg_fields): # No CSE necessary here--the compiler CSE's these # automatically. from hedge.tools import is_obj_array, make_obj_array if is_obj_array(arg_fields): # arg_fields (as an object array) isn't hashable # --make it so by turning it into a tuple arg_fields = tuple(arg_fields) return make_obj_array([ func(i, arg_fields) for i in range(len(arg_fields))]) else: return func(0, (arg_fields,))
def make_inner_product(self, sample_vec): from hedge.tools import is_obj_array sample_is_obj_array = is_obj_array(sample_vec) if sample_is_obj_array: sample_vec = sample_vec[0] if isinstance(sample_vec, numpy.ndarray) and sample_vec.dtype != object: kernel = numpy.dot else: kernel = self.make_special_inner_product(sample_vec) if kernel is None: raise RuntimeError("could not find an inner product routine for " "the given sample vector" + _NO_VPF_SUGGESTION) if sample_is_obj_array: kernel = ObjectArrayInnerProductWrapper(kernel) return kernel
def map_field_component(self, expr): if expr.is_interior: prefix = "a" f_expr = self.int_field_expr else: prefix = "b" f_expr = self.ext_field_expr from hedge.tools import is_obj_array, is_zero from pymbolic import var if is_obj_array(f_expr): f_expr = f_expr[expr.index] if is_zero(f_expr): return 0 return var("val_%s_field%d" % (prefix, self.dep_to_index[f_expr])) else: assert expr.index == 0, repr(f_expr) if is_zero(f_expr): return 0 return var("val_%s_field%d" % (prefix, self.dep_to_index[f_expr]))
def make_maximum_norm(self, sample_vec): from hedge.tools import is_obj_array sample_is_obj_array = is_obj_array(sample_vec) if sample_is_obj_array: sample_vec = sample_vec[0] if isinstance(sample_vec, numpy.ndarray) and sample_vec.dtype != object: kernel = numpy.max else: kernel = self.make_special_maximum_norm(sample_vec) if kernel is None: raise RuntimeError("could not find a maximum norm routine for " "the given sample vector" + _NO_VPF_SUGGESTION) if sample_is_obj_array: kernel = ObjectArrayMaximumNormWrapper(kernel) return kernel
def maybe_index(fld, index): from hedge.tools import is_obj_array if is_obj_array(fld): return fld[inf.index] else: return fld
def get_flux_var_info(fluxes): from pytools import Record class FluxVariableInfo(Record): pass scalar_parameters = set() fvi = FluxVariableInfo( scalar_parameters=None, arg_specs=[], arg_names=[], flux_idx_and_dep_to_arg_name={}, # or 0 if zero ) field_expr_to_arg_name = {} from hedge.flux import \ FieldComponent, FluxDependencyMapper, \ FluxScalarParameter from hedge.optemplate import BoundaryPair for flux_idx, flux_binding in enumerate(fluxes): for dep in FluxDependencyMapper(include_calls=False)(flux_binding.op.flux): if isinstance(dep, FluxScalarParameter): scalar_parameters.add(dep) elif isinstance(dep, FieldComponent): is_bdry = isinstance(flux_binding.field, BoundaryPair) if is_bdry: if dep.is_interior: this_field_expr = flux_binding.field.field else: this_field_expr = flux_binding.field.bfield else: this_field_expr = flux_binding.field from hedge.tools import is_obj_array if is_obj_array(this_field_expr): fc_field_expr = this_field_expr[dep.index] else: assert dep.index == 0 fc_field_expr = this_field_expr def set_or_check(dict_instance, key, value): try: existing_value = dict_instance[key] except KeyError: dict_instance[key] = value else: assert existing_value == value from pymbolic.primitives import is_zero if is_zero(fc_field_expr): fvi.flux_idx_and_dep_to_arg_name[flux_idx, dep] = 0 else: if fc_field_expr not in field_expr_to_arg_name: arg_name = "arg%d" % len(fvi.arg_specs) field_expr_to_arg_name[fc_field_expr] = arg_name fvi.arg_names.append(arg_name) fvi.arg_specs.append((fc_field_expr, dep.is_interior)) else: arg_name = field_expr_to_arg_name[fc_field_expr] set_or_check( fvi.flux_idx_and_dep_to_arg_name, (flux_idx, dep), arg_name) if not is_bdry: # Interior fluxes are used flipped as well. # Make sure we have assigned arg names for the # flipped case as well. set_or_check( fvi.flux_idx_and_dep_to_arg_name, (flux_idx, FieldComponent(dep.index, not dep.is_interior)), arg_name) else: raise ValueError("unknown flux dependency type: %s" % dep) fvi.scalar_parameters = list(scalar_parameters) return fvi
def aggregate_assignments(self, instructions, result): from pymbolic.primitives import Variable # {{{ aggregation helpers def get_complete_origins_set(insn, skip_levels=0): if skip_levels < 0: skip_levels = 0 result = set() for dep in insn.get_dependencies(): if isinstance(dep, Variable): dep_origin = origins_map.get(dep.name, None) if dep_origin is not None: if skip_levels <= 0: result.add(dep_origin) result |= get_complete_origins_set( dep_origin, skip_levels - 1) return result var_assignees_cache = {} def get_var_assignees(insn): try: return var_assignees_cache[insn] except KeyError: result = set( Variable(assignee) for assignee in insn.get_assignees()) var_assignees_cache[insn] = result return result def aggregate_two_assignments(ass_1, ass_2): names = ass_1.names + ass_2.names from pymbolic.primitives import Variable deps = (ass_1.get_dependencies() | ass_2.get_dependencies()) \ - set(Variable(name) for name in names) return Assign(names=names, exprs=ass_1.exprs + ass_2.exprs, _dependencies=deps, dep_mapper_factory=self.dep_mapper_factory, priority=max(ass_1.priority, ass_2.priority)) # }}} # {{{ main aggregation pass origins_map = dict((assignee, insn) for insn in instructions for assignee in insn.get_assignees()) from pytools import partition unprocessed_assigns, other_insns = partition( lambda insn: isinstance(insn, Assign) and not insn. is_scalar_valued, instructions) # filter out zero-flop-count assigns--no need to bother with those processed_assigns, unprocessed_assigns = partition( lambda ass: ass.flop_count() == 0, unprocessed_assigns) # filter out zero assignments from pytools import any from hedge.tools import is_zero i = 0 while i < len(unprocessed_assigns): my_assign = unprocessed_assigns[i] if any(is_zero(expr) for expr in my_assign.exprs): processed_assigns.append(unprocessed_assigns.pop()) else: i += 1 # greedy aggregation while unprocessed_assigns: my_assign = unprocessed_assigns.pop() my_deps = my_assign.get_dependencies() my_assignees = get_var_assignees(my_assign) agg_candidates = [] for i, other_assign in enumerate(unprocessed_assigns): other_deps = other_assign.get_dependencies() other_assignees = get_var_assignees(other_assign) if ((my_deps & other_deps or my_deps & other_assignees or other_deps & my_assignees) and my_assign.priority == other_assign.priority): agg_candidates.append((i, other_assign)) did_work = False if agg_candidates: my_indirect_origins = get_complete_origins_set(my_assign, skip_levels=1) for other_assign_index, other_assign in agg_candidates: if self.max_vectors_in_batch_expr is not None: new_assignee_count = len( set(my_assign.get_assignees()) | set(other_assign.get_assignees())) new_dep_count = len( my_assign.get_dependencies(each_vector=True) | other_assign.get_dependencies(each_vector=True)) if (new_assignee_count + new_dep_count > self.max_vectors_in_batch_expr): continue other_indirect_origins = get_complete_origins_set( other_assign, skip_levels=1) if (my_assign not in other_indirect_origins and other_assign not in my_indirect_origins): did_work = True # aggregate the two assignments new_assignment = aggregate_two_assignments( my_assign, other_assign) del unprocessed_assigns[other_assign_index] unprocessed_assigns.append(new_assignment) for assignee in new_assignment.get_assignees(): origins_map[assignee] = new_assignment break if not did_work: processed_assigns.append(my_assign) externally_used_names = set(expr for insn in processed_assigns + other_insns for expr in insn.get_dependencies()) from hedge.tools import is_obj_array if is_obj_array(result): externally_used_names |= set(expr for expr in result) else: externally_used_names |= set([result]) def schedule_and_finalize_assignment(ass): dep_mapper = self.dep_mapper_factory() names_exprs = zip(ass.names, ass.exprs) my_assignees = set(name for name, expr in names_exprs) names_exprs_deps = [ (name, expr, set(dep.name for dep in dep_mapper(expr) if isinstance(dep, Variable)) & my_assignees) for name, expr in names_exprs ] ordered_names_exprs = [] available_names = set() while names_exprs_deps: schedulable = [] i = 0 while i < len(names_exprs_deps): name, expr, deps = names_exprs_deps[i] unsatisfied_deps = deps - available_names if not unsatisfied_deps: schedulable.append((str(expr), name, expr)) del names_exprs_deps[i] else: i += 1 # make sure these come out in a constant order schedulable.sort() if schedulable: for key, name, expr in schedulable: ordered_names_exprs.append((name, expr)) available_names.add(name) else: raise RuntimeError("aggregation resulted in an " "impossible assignment") return self.finalize_multi_assign( names=[name for name, expr in ordered_names_exprs], exprs=[expr for name, expr in ordered_names_exprs], do_not_return=[ Variable(name) not in externally_used_names for name, expr in ordered_names_exprs ], priority=ass.priority) return [ schedule_and_finalize_assignment(ass) for ass in processed_assigns ] + other_insns
def get_deps(field): if is_obj_array(field): return set(field) else: return set([field])
def aggregate_assignments(self, instructions, result): from pymbolic.primitives import Variable # aggregation helpers ------------------------------------------------- def get_complete_origins_set(insn, skip_levels=0): if skip_levels < 0: skip_levels = 0 result = set() for dep in insn.get_dependencies(): if isinstance(dep, Variable): dep_origin = origins_map.get(dep.name, None) if dep_origin is not None: if skip_levels <= 0: result.add(dep_origin) result |= get_complete_origins_set( dep_origin, skip_levels-1) return result var_assignees_cache = {} def get_var_assignees(insn): try: return var_assignees_cache[insn] except KeyError: result = set(Variable(assignee) for assignee in insn.get_assignees()) var_assignees_cache[insn] = result return result def aggregate_two_assignments(ass_1, ass_2): names = ass_1.names + ass_2.names from pymbolic.primitives import Variable deps = (ass_1.get_dependencies() | ass_2.get_dependencies()) \ - set(Variable(name) for name in names) return Assign( names=names, exprs=ass_1.exprs + ass_2.exprs, _dependencies=deps, dep_mapper_factory=self.dep_mapper_factory, priority=max(ass_1.priority, ass_2.priority)) # main aggregation pass ----------------------------------------------- origins_map = dict( (assignee, insn) for insn in instructions for assignee in insn.get_assignees()) from pytools import partition unprocessed_assigns, other_insns = partition( lambda insn: isinstance(insn, Assign), instructions) # filter out zero-flop-count assigns--no need to bother with those processed_assigns, unprocessed_assigns = partition( lambda ass: ass.flop_count() == 0, unprocessed_assigns) # filter out zero assignments from pytools import any from hedge.tools import is_zero i = 0 while i < len(unprocessed_assigns): my_assign = unprocessed_assigns[i] if any(is_zero(expr) for expr in my_assign.exprs): processed_assigns.append(unprocessed_assigns.pop()) else: i += 1 # greedy aggregation while unprocessed_assigns: my_assign = unprocessed_assigns.pop() my_deps = my_assign.get_dependencies() my_assignees = get_var_assignees(my_assign) agg_candidates = [] for i, other_assign in enumerate(unprocessed_assigns): other_deps = other_assign.get_dependencies() other_assignees = get_var_assignees(other_assign) if ((my_deps & other_deps or my_deps & other_assignees or other_deps & my_assignees) and my_assign.priority == other_assign.priority): agg_candidates.append((i, other_assign)) did_work = False if agg_candidates: my_indirect_origins = get_complete_origins_set( my_assign, skip_levels=1) for other_assign_index, other_assign in agg_candidates: if self.max_vectors_in_batch_expr is not None: new_assignee_count = len( set(my_assign.get_assignees()) | set(other_assign.get_assignees())) new_dep_count = len( my_assign.get_dependencies( each_vector=True) | other_assign.get_dependencies( each_vector=True)) if (new_assignee_count + new_dep_count \ > self.max_vectors_in_batch_expr): continue other_indirect_origins = get_complete_origins_set( other_assign, skip_levels=1) if (my_assign not in other_indirect_origins and other_assign not in my_indirect_origins): did_work = True # aggregate the two assignments new_assignment = aggregate_two_assignments( my_assign, other_assign) del unprocessed_assigns[other_assign_index] unprocessed_assigns.append(new_assignment) for assignee in new_assignment.get_assignees(): origins_map[assignee] = new_assignment break if not did_work: processed_assigns.append(my_assign) externally_used_names = set( expr for insn in processed_assigns + other_insns for expr in insn.get_dependencies()) from hedge.tools import is_obj_array if is_obj_array(result): externally_used_names |= set(expr for expr in result) else: externally_used_names |= set([result]) def schedule_and_finalize_assignment(ass): dep_mapper = self.dep_mapper_factory() names_exprs = zip(ass.names, ass.exprs) my_assignees = set(name for name, expr in names_exprs) names_exprs_deps = [ (name, expr, set(dep.name for dep in dep_mapper(expr) if isinstance(dep, Variable)) & my_assignees) for name, expr in names_exprs] ordered_names_exprs = [] available_names = set() while names_exprs_deps: schedulable = [] i = 0 while i < len(names_exprs_deps): name, expr, deps = names_exprs_deps[i] unsatisfied_deps = deps - available_names if not unsatisfied_deps: schedulable.append((str(expr), name, expr)) del names_exprs_deps[i] else: i += 1 # make sure these come out in a constant order schedulable.sort() if schedulable: for key, name, expr in schedulable: ordered_names_exprs.append((name, expr)) available_names.add(name) else: raise RuntimeError("aggregation resulted in an " "impossible assignment") return self.finalize_multi_assign( names=[name for name, expr in ordered_names_exprs], exprs=[expr for name, expr in ordered_names_exprs], do_not_return=[Variable(name) not in externally_used_names for name, expr in ordered_names_exprs], priority=ass.priority) return [schedule_and_finalize_assignment(ass) for ass in processed_assigns] + other_insns
def map_operator_binding(self, expr): from hedge.optemplate.operators import FluxOperatorBase from hedge.optemplate.primitives import BoundaryPair from hedge.flux import FluxSubstitutionMapper, FieldComponent if not (isinstance(expr.op, FluxOperatorBase) and isinstance(expr.field, BoundaryPair)): return IdentityMapper.map_operator_binding(self, expr) bpair = expr.field vol_field = bpair.field bdry_field = bpair.bfield flux = expr.op.flux bdry_dependencies = DependencyMapper( include_calls="descend_args", include_operator_bindings=True)(bdry_field) vol_dependencies = DependencyMapper( include_operator_bindings=True)(vol_field) vol_bdry_intersection = bdry_dependencies & vol_dependencies if vol_bdry_intersection: raise RuntimeError( "Variables are being used as both " "boundary and volume quantities: %s" % ", ".join(str(v) for v in vol_bdry_intersection)) # Step 1: Find maximal flux-evaluable subexpression of boundary field # in given BoundaryPair. class MaxBoundaryFluxEvaluableExpressionFinder(IdentityMapper, OperatorReducerMixin): def __init__(self, vol_expr_list, expensive_bdry_op_detector): self.vol_expr_list = vol_expr_list self.vol_expr_to_idx = dict( (vol_expr, idx) for idx, vol_expr in enumerate(vol_expr_list)) self.bdry_expr_list = [] self.bdry_expr_to_idx = {} self.expensive_bdry_op_detector = expensive_bdry_op_detector # {{{ expression registration def register_boundary_expr(self, expr): try: return self.bdry_expr_to_idx[expr] except KeyError: idx = len(self.bdry_expr_to_idx) self.bdry_expr_to_idx[expr] = idx self.bdry_expr_list.append(expr) return idx def register_volume_expr(self, expr): try: return self.vol_expr_to_idx[expr] except KeyError: idx = len(self.vol_expr_to_idx) self.vol_expr_to_idx[expr] = idx self.vol_expr_list.append(expr) return idx # }}} # {{{ map_xxx routines @memoize_method def map_common_subexpression(self, expr): # Here we need to decide whether this CSE should be turned into # a flux CSE or not. This is a good idea if the transformed # expression only contains "bare" volume or boundary # expressions. However, as soon as an operator is applied # somewhere in the subexpression, the CSE should not be touched # in order to avoid redundant evaluation of that operator. # # Observe that at the time of this writing (Feb 2010), the only # operators that may occur in boundary expressions are # quadrature-related. has_expensive_operators = \ self.expensive_bdry_op_detector(expr.child) if has_expensive_operators: return FieldComponent(self.register_boundary_expr(expr), is_interior=False) else: return IdentityMapper.map_common_subexpression(self, expr) def map_normal(self, expr): raise RuntimeError( "Your operator template contains a flux normal. " "You may find this confusing, but you can't do that. " "It turns out that you need to use " "hedge.optemplate.make_normal() for normals in boundary " "terms of operator templates.") def map_normal_component(self, expr): if expr.boundary_tag != bpair.tag: raise RuntimeError( "BoundaryNormalComponent and BoundaryPair " "do not agree about boundary tag: %s vs %s" % (expr.boundary_tag, bpair.tag)) from hedge.flux import Normal return Normal(expr.axis) def map_variable(self, expr): return FieldComponent(self.register_boundary_expr(expr), is_interior=False) map_subscript = map_variable def map_operator_binding(self, expr): from hedge.optemplate import (BoundarizeOperator, FluxExchangeOperator, QuadratureGridUpsampler, QuadratureBoundaryGridUpsampler) if isinstance(expr.op, BoundarizeOperator): if expr.op.tag != bpair.tag: raise RuntimeError( "BoundarizeOperator and BoundaryPair " "do not agree about boundary tag: %s vs %s" % (expr.op.tag, bpair.tag)) return FieldComponent(self.register_volume_expr( expr.field), is_interior=True) elif isinstance(expr.op, FluxExchangeOperator): from hedge.mesh import TAG_RANK_BOUNDARY op_tag = TAG_RANK_BOUNDARY(expr.op.rank) if bpair.tag != op_tag: raise RuntimeError( "BoundarizeOperator and " "FluxExchangeOperator do not agree about " "boundary tag: %s vs %s" % (op_tag, bpair.tag)) return FieldComponent(self.register_boundary_expr(expr), is_interior=False) elif isinstance(expr.op, QuadratureBoundaryGridUpsampler): if bpair.tag != expr.op.boundary_tag: raise RuntimeError( "BoundarizeOperator " "and QuadratureBoundaryGridUpsampler " "do not agree about boundary tag: %s vs %s" % (expr.op.boundary_tag, bpair.tag)) return FieldComponent(self.register_boundary_expr(expr), is_interior=False) elif isinstance(expr.op, QuadratureGridUpsampler): # We're invoked before operator specialization, so we may # see these instead of QuadratureBoundaryGridUpsampler. return FieldComponent(self.register_boundary_expr(expr), is_interior=False) else: raise RuntimeError( "Found '%s' in a boundary term. " "To the best of my knowledge, no hedge operator applies " "directly to boundary data, so this is likely in error." % expr.op) def map_flux_exchange(self, expr): return FieldComponent(self.register_boundary_expr(expr), is_interior=False) # }}} from hedge.tools import is_obj_array if not is_obj_array(vol_field): vol_field = [vol_field] mbfeef = MaxBoundaryFluxEvaluableExpressionFinder( list(vol_field), self.expensive_bdry_op_detector) #from hedge.optemplate.tools import pretty #print pretty(bdry_field) #raw_input("YO") new_bdry_field = mbfeef(bdry_field) # Step II: Substitute the new_bdry_field into the flux. def sub_bdry_into_flux(expr): if isinstance(expr, FieldComponent) and not expr.is_interior: if expr.index == 0 and not is_obj_array(bdry_field): return new_bdry_field else: return new_bdry_field[expr.index] else: return None new_flux = FluxSubstitutionMapper(sub_bdry_into_flux)(flux) from hedge.tools import is_zero, make_obj_array if is_zero(new_flux): return 0 else: return type(expr.op)(new_flux, *expr.op.__getinitargs__()[1:])( BoundaryPair( make_obj_array([self.rec(e) for e in mbfeef.vol_expr_list]), make_obj_array( [self.rec(e) for e in mbfeef.bdry_expr_list]), bpair.tag))
def map_operator_binding(self, expr): from hedge.optemplate.operators import FluxOperatorBase from hedge.optemplate.primitives import BoundaryPair from hedge.flux import FluxSubstitutionMapper, FieldComponent if not (isinstance(expr.op, FluxOperatorBase) and isinstance(expr.field, BoundaryPair)): return IdentityMapper.map_operator_binding(self, expr) bpair = expr.field vol_field = bpair.field bdry_field = bpair.bfield flux = expr.op.flux bdry_dependencies = DependencyMapper( include_calls="descend_args", include_operator_bindings=True)(bdry_field) vol_dependencies = DependencyMapper( include_operator_bindings=True)(vol_field) vol_bdry_intersection = bdry_dependencies & vol_dependencies if vol_bdry_intersection: raise RuntimeError("Variables are being used as both " "boundary and volume quantities: %s" % ", ".join(str(v) for v in vol_bdry_intersection)) # Step 1: Find maximal flux-evaluable subexpression of boundary field # in given BoundaryPair. class MaxBoundaryFluxEvaluableExpressionFinder( IdentityMapper, OperatorReducerMixin): def __init__(self, vol_expr_list, expensive_bdry_op_detector): self.vol_expr_list = vol_expr_list self.vol_expr_to_idx = dict((vol_expr, idx) for idx, vol_expr in enumerate(vol_expr_list)) self.bdry_expr_list = [] self.bdry_expr_to_idx = {} self.expensive_bdry_op_detector = expensive_bdry_op_detector # {{{ expression registration def register_boundary_expr(self, expr): try: return self.bdry_expr_to_idx[expr] except KeyError: idx = len(self.bdry_expr_to_idx) self.bdry_expr_to_idx[expr] = idx self.bdry_expr_list.append(expr) return idx def register_volume_expr(self, expr): try: return self.vol_expr_to_idx[expr] except KeyError: idx = len(self.vol_expr_to_idx) self.vol_expr_to_idx[expr] = idx self.vol_expr_list.append(expr) return idx # }}} # {{{ map_xxx routines @memoize_method def map_common_subexpression(self, expr): # Here we need to decide whether this CSE should be turned into # a flux CSE or not. This is a good idea if the transformed # expression only contains "bare" volume or boundary # expressions. However, as soon as an operator is applied # somewhere in the subexpression, the CSE should not be touched # in order to avoid redundant evaluation of that operator. # # Observe that at the time of this writing (Feb 2010), the only # operators that may occur in boundary expressions are # quadrature-related. has_expensive_operators = \ self.expensive_bdry_op_detector(expr.child) if has_expensive_operators: return FieldComponent( self.register_boundary_expr(expr), is_interior=False) else: return IdentityMapper.map_common_subexpression(self, expr) def map_normal(self, expr): raise RuntimeError("Your operator template contains a flux normal. " "You may find this confusing, but you can't do that. " "It turns out that you need to use " "hedge.optemplate.make_normal() for normals in boundary " "terms of operator templates.") def map_normal_component(self, expr): if expr.boundary_tag != bpair.tag: raise RuntimeError("BoundaryNormalComponent and BoundaryPair " "do not agree about boundary tag: %s vs %s" % (expr.boundary_tag, bpair.tag)) from hedge.flux import Normal return Normal(expr.axis) def map_variable(self, expr): return FieldComponent( self.register_boundary_expr(expr), is_interior=False) map_subscript = map_variable def map_operator_binding(self, expr): from hedge.optemplate import (BoundarizeOperator, FluxExchangeOperator, QuadratureGridUpsampler, QuadratureBoundaryGridUpsampler) if isinstance(expr.op, BoundarizeOperator): if expr.op.tag != bpair.tag: raise RuntimeError("BoundarizeOperator and BoundaryPair " "do not agree about boundary tag: %s vs %s" % (expr.op.tag, bpair.tag)) return FieldComponent( self.register_volume_expr(expr.field), is_interior=True) elif isinstance(expr.op, FluxExchangeOperator): from hedge.mesh import TAG_RANK_BOUNDARY op_tag = TAG_RANK_BOUNDARY(expr.op.rank) if bpair.tag != op_tag: raise RuntimeError("BoundarizeOperator and FluxExchangeOperator " "do not agree about boundary tag: %s vs %s" % (op_tag, bpair.tag)) return FieldComponent( self.register_boundary_expr(expr), is_interior=False) elif isinstance(expr.op, QuadratureBoundaryGridUpsampler): if bpair.tag != expr.op.boundary_tag: raise RuntimeError("BoundarizeOperator " "and QuadratureBoundaryGridUpsampler " "do not agree about boundary tag: %s vs %s" % (expr.op.boundary_tag, bpair.tag)) return FieldComponent( self.register_boundary_expr(expr), is_interior=False) elif isinstance(expr.op, QuadratureGridUpsampler): # We're invoked before operator specialization, so we may # see these instead of QuadratureBoundaryGridUpsampler. return FieldComponent( self.register_boundary_expr(expr), is_interior=False) else: raise RuntimeError("Found '%s' in a boundary term. " "To the best of my knowledge, no hedge operator applies " "directly to boundary data, so this is likely in error." % expr.op) def map_flux_exchange(self, expr): return FieldComponent( self.register_boundary_expr(expr), is_interior=False) # }}} from hedge.tools import is_obj_array if not is_obj_array(vol_field): vol_field = [vol_field] mbfeef = MaxBoundaryFluxEvaluableExpressionFinder(list(vol_field), self.expensive_bdry_op_detector) #from hedge.optemplate.tools import pretty_print_optemplate #print pretty_print_optemplate(bdry_field) #raw_input("YO") new_bdry_field = mbfeef(bdry_field) # Step II: Substitute the new_bdry_field into the flux. def sub_bdry_into_flux(expr): if isinstance(expr, FieldComponent) and not expr.is_interior: if expr.index == 0 and not is_obj_array(bdry_field): return new_bdry_field else: return new_bdry_field[expr.index] else: return None new_flux = FluxSubstitutionMapper(sub_bdry_into_flux)(flux) from hedge.tools import is_zero, make_obj_array if is_zero(new_flux): return 0 else: return type(expr.op)(new_flux, *expr.op.__getinitargs__()[1:])( BoundaryPair( make_obj_array([self.rec(e) for e in mbfeef.vol_expr_list]), make_obj_array([self.rec(e) for e in mbfeef.bdry_expr_list]), bpair.tag))
def get_flux_var_info(fluxes): from pytools import Record class FluxVariableInfo(Record): pass scalar_parameters = set() fvi = FluxVariableInfo( scalar_parameters=None, arg_specs=[], arg_names=[], flux_idx_and_dep_to_arg_name={}, # or 0 if zero ) field_expr_to_arg_name = {} from hedge.flux import \ FieldComponent, FluxDependencyMapper, \ FluxScalarParameter from hedge.optemplate import BoundaryPair for flux_idx, flux_binding in enumerate(fluxes): from hedge.optemplate.primitives import CFunction for dep in FluxDependencyMapper(include_calls=False)(flux_binding.op.flux): if isinstance(dep, FluxScalarParameter): scalar_parameters.add(dep) elif isinstance(dep, FieldComponent): is_bdry = isinstance(flux_binding.field, BoundaryPair) if is_bdry: if dep.is_interior: this_field_expr = flux_binding.field.field else: this_field_expr = flux_binding.field.bfield else: this_field_expr = flux_binding.field from hedge.tools import is_obj_array if is_obj_array(this_field_expr): fc_field_expr = this_field_expr[dep.index] else: assert dep.index == 0 fc_field_expr = this_field_expr def set_or_check(dict_instance, key, value): try: existing_value = dict_instance[key] except KeyError: dict_instance[key] = value else: assert existing_value == value from pymbolic.primitives import is_zero if is_zero(fc_field_expr): fvi.flux_idx_and_dep_to_arg_name[flux_idx, dep] = 0 else: if fc_field_expr not in field_expr_to_arg_name: arg_name = "arg%d" % len(fvi.arg_specs) field_expr_to_arg_name[fc_field_expr] = arg_name fvi.arg_names.append(arg_name) fvi.arg_specs.append((fc_field_expr, dep.is_interior)) else: arg_name = field_expr_to_arg_name[fc_field_expr] set_or_check( fvi.flux_idx_and_dep_to_arg_name, (flux_idx, dep), arg_name) if not is_bdry: # Interior fluxes are used flipped as well. # Make sure we have assigned arg names for the # flipped case as well. set_or_check( fvi.flux_idx_and_dep_to_arg_name, (flux_idx, FieldComponent(dep.index, not dep.is_interior)), arg_name) elif isinstance(dep, CFunction): pass else: raise ValueError("unknown flux dependency type: %s" % dep) fvi.scalar_parameters = list(scalar_parameters) return fvi