def call_parameters(self, arr_shape): substitution_dict = { sym: value for sym, value in zip(self._symbolic_shape, arr_shape) if sym is not None } widths = [ end - start for start, end in zip( _get_start_from_slice(self._iterationSlice), _get_end_from_slice(self._iterationSlice, arr_shape)) ] widths = sp.Matrix(widths).subs(substitution_dict) extend_bs = (1, ) * (3 - len(self._block_size)) block_size = self._block_size + extend_bs if not self._compile_time_block_size: assert len(block_size) == 3 adapted_block_size = [] for i in range(len(widths)): factor = div_floor(prod(block_size[:i]), prod(adapted_block_size)) adapted_block_size.append( sp.Min(block_size[i] * factor, widths[i])) block_size = tuple(adapted_block_size) + extend_bs block_size = tuple( sp.Min(bs, max_bs) for bs, max_bs in zip(block_size, self._maximum_block_size)) grid = tuple( div_ceil(length, block_size) for length, block_size in zip(widths, block_size)) extend_gr = (1, ) * (3 - len(grid)) return {'block': block_size, 'grid': grid + extend_gr}
def expand_diff_products(expr): """Fully expands all derivatives by applying product rule""" if isinstance(expr, Diff): arg = expand_diff_products(expr.args[0]) if arg.func == sp.Add: new_args = [ Diff(e, target=expr.target, superscript=expr.superscript) for e in arg.args ] return sp.Add(*new_args) if arg.func not in (sp.Mul, sp.Pow): return Diff(arg, target=expr.target, superscript=expr.superscript) else: prod_list = normalize_product(arg) result = 0 for i in range(len(prod_list)): pre_factor = prod(prod_list[j] for j in range(len(prod_list)) if i != j) result += pre_factor * Diff(prod_list[i], target=expr.target, superscript=expr.superscript) return result else: new_args = [expand_diff_products(e) for e in expr.args] return expr.func(*new_args) if new_args else expr
def poly_moments(order, dim): from pystencils.sympyextensions import prod c = sp.Matrix([expanded_symbol("c", subscript=i) for i in range(dim)]) return [ prod(c_i**m_i for c_i, m_i in zip(c, m)) for m in moments_of_order(order, dim=dim) ]
def get_field_fsize(field): """Determines the size of the index coordinate. Since walberla fields only support one index dimension, pystencils fields with multiple index dimensions are linearized to a single index dimension. """ assert field.has_fixed_index_shape, \ "All Fields have to be created with fixed index coordinate shape using index_shape=(q,) " + str(field.name) if field.index_dimensions == 0: return 1 else: return prod(field.index_shape)
def expr_to_diff_decomposition(expression): """Decomposes a sp.Add node containing CeDiffs into: diff_dict: maps (target, superscript) -> [ (pre_factor, argument), ... ] i.e. a partial(b) ( a is pre-factor, b is argument) in case of partial(a) partial(b) two entries are created (0.5 partial(a), b), (0.5 partial(b), a) """ DiffInfo = namedtuple("DiffInfo", ["target", "superscript"]) class DiffSplit: def __init__(self, fac, argument): self.pre_factor = fac self.argument = argument def __repr__(self): return str((self.pre_factor, self.argument)) assert isinstance(expression, sp.Add) diff_dict = defaultdict(list) rest = 0 for term in expression.args: if isinstance(term, Diff): diff_dict[DiffInfo(term.target, term.superscript)].append( DiffSplit(1, term.arg)) else: mul_args = normalize_product(term) diffs = [d for d in mul_args if isinstance(d, Diff)] factor = prod(d for d in mul_args if not isinstance(d, Diff)) if len(diffs) == 0: rest += factor else: for i, diff in enumerate(diffs): all_but_current = [ d for j, d in enumerate(diffs) if i != j ] pre_factor = factor * prod( all_but_current) * sp.Rational(1, len(diffs)) diff_dict[DiffInfo(diff.target, diff.superscript)].append( DiffSplit(pre_factor, diff.arg)) return diff_dict, rest
def handle_mul(mul): args = normalize_product(mul) diffs = [a for a in args if isinstance(a, DiffOperator)] if len(diffs) == 0: return mul * argument if apply_to_constants else mul rest = [a for a in args if not isinstance(a, DiffOperator)] diffs.sort(key=_default_diff_sort_key) result = argument for d in reversed(diffs): result = Diff(result, target=d.target, superscript=d.superscript) return prod(rest) * result
def correction_g(c, surface_tensions, symbolic_coefficients=False): assert len(c) == surface_tensions.rows n = len(c) result = 0 for i in capital_i(4, n): for s in i: reduced_i = tuple(e for e in i if e != s) if symbolic_coefficients: coeff = sp.Symbol("Lambda_{}{}{}{}{}".format(s, *i)) else: coeff = capital_lambda(surface_tensions, reduced_i)[s] result += coeff * capital_h(c[s], prod(c[j] for j in i)) return result
def error_term_dict(self, order): error_terms = defaultdict(lambda: 0) for direction in self._stencil: weight = self.weights[tuple(direction)] x = tuple(self._dx * d_i for d_i in direction) for offset in multidimensional_sum( order, dim=self.field.spatial_dimensions): fac = sp.factorial(order) error_terms[tuple( sorted(offset))] += weight / fac * prod(x[off] for off in offset) if self._derivative in error_terms: error_terms[self._derivative] -= 1 return error_terms
def generate_benchmark(ast, likwid=False, openmp=False, timing=False): """Return C code of a benchmark program for the given kernel. Args: ast: the pystencils AST object as returned by create_kernel likwid: if True likwid markers are added to the code openmp: relevant only if likwid=True, to generated correct likwid initialization code timing: add timing output to the code, prints time per iteration to stdout Returns: C code as string """ accessed_fields = {f.name: f for f in ast.fields_accessed} constants = [] fields = [] call_parameters = [] for p in ast.get_parameters(): if not p.is_field_parameter: constants.append((p.symbol.name, str(p.symbol.dtype))) call_parameters.append(p.symbol.name) else: assert p.is_field_pointer, "Benchmark implemented only for kernels with fixed loop size" field = accessed_fields[p.field_name] dtype = str(get_base_type(p.symbol.dtype)) fields.append((p.field_name, dtype, prod(field.shape))) call_parameters.append(p.field_name) header_list = get_headers(ast) includes = "\n".join(["#include %s" % (include_file,) for include_file in header_list]) # Strip "#pragma omp parallel" from within kernel, because main function takes care of that # when likwid and openmp are enabled if likwid and openmp: if len(ast.body.args) > 0 and isinstance(ast.body.args[0], PragmaBlock): ast.body.args[0].pragma_line = '' args = { 'likwid': likwid, 'openmp': openmp, 'kernel_code': generate_c(ast, dialect='c'), 'kernelName': ast.function_name, 'fields': fields, 'constants': constants, 'call_argument_list': ",".join(call_parameters), 'includes': includes, 'timing': timing, } return benchmark_template.render(**args)
def extract_gamma(free_energy, order_parameters): """Extracts parameters before the gradient terms""" result = defaultdict(lambda: 0) free_energy = free_energy.expand() assert free_energy.func == sp.Add for product in free_energy.args: product = normalize_product(product) diff_factors = [e for e in product if e.func == Diff] if len(diff_factors) == 0: continue if len(diff_factors) != 2: raise ValueError(f"Could not determine Λ because of term {str(product)}") indices = sorted([order_parameters.index(d.args[0]) for d in diff_factors]) increment = prod(e for e in product if e.func != Diff) if diff_factors[0] == diff_factors[1]: increment *= 2 result[tuple(indices)] += increment return result
def visit(e): if not isinstance(e, sp.Tuple): e = e.expand() if e.func == Diff: result = 0 diff_args = {'target': e.target, 'superscript': e.superscript} diff_inner = e.args[0] diff_inner = visit(diff_inner) if diff_inner.func not in (sp.Add, sp.Mul): return e for term in diff_inner.args if diff_inner.func == sp.Add else [ diff_inner ]: independent_terms = 1 dependent_terms = [] for factor in normalize_product(term): if factor in functions or isinstance(factor, Diff): dependent_terms.append(factor) else: independent_terms *= factor for i in range(len(dependent_terms)): dependent_term = dependent_terms[i] other_dependent_terms = dependent_terms[: i] + dependent_terms[ i + 1:] processed_diff = normalize_diff_order( Diff(dependent_term, **diff_args)) result += independent_terms * prod( other_dependent_terms) * processed_diff return result elif isinstance(e, sp.Piecewise): return sp.Piecewise(*((expand_diff_full(a, functions, constants), b) for a, b in e.args)) elif isinstance(expr, sp.Tuple): new_args = [visit(arg) for arg in e.args] return sp.Tuple(*new_args) else: new_args = [visit(arg) for arg in e.args] return e.func(*new_args) if new_args else e
def max_threads_per_block(self): if is_integer_sequence(self._block_size): return prod(self._block_size) else: return None