def translate_from(self, src_expansion, src_coeff_exprs, src_rscale, dvec, tgt_rscale): logger.info("building translation operator: %s(%d) -> %s(%d): start" % (type(src_expansion).__name__, src_expansion.order, type(self).__name__, self.order)) if not self.use_rscale: src_rscale = 1 tgt_rscale = 1 from sumpy.expansion.multipole import VolumeTaylorMultipoleExpansionBase if isinstance(src_expansion, VolumeTaylorMultipoleExpansionBase): # We know the general form of the multipole expansion is: # # coeff0 * diff(kernel, mi0) + coeff1 * diff(kernel, mi1) + ... # # To get the local expansion coefficients, we take derivatives of # the multipole expansion. # # This code speeds up derivative taking by caching all kernel # derivatives. taker = src_expansion.get_kernel_derivative_taker(dvec) from sumpy.tools import add_mi result = [] for deriv in self.get_coefficient_identifiers(): local_result = [] for coeff, term in zip( src_coeff_exprs, src_expansion.get_coefficient_identifiers()): kernel_deriv = (src_expansion.get_scaled_multipole( taker.diff(add_mi(deriv, term)), dvec, src_rscale, nderivatives=sum(deriv) + sum(term), nderivatives_for_scaling=sum(term))) local_result.append(coeff * kernel_deriv * tgt_rscale**sum(deriv)) result.append(sym.Add(*local_result)) else: from sumpy.tools import MiDerivativeTaker expr = src_expansion.evaluate(src_coeff_exprs, dvec, rscale=src_rscale) taker = MiDerivativeTaker(expr, dvec) # Rscale/operand magnitude is fairly sensitive to the order of # operations--which is something we don't have fantastic control # over at the symbolic level. The '.expand()' below moves the two # canceling "rscales" closer to each other in the hope of helping # with that. result = [(taker.diff(mi) * tgt_rscale**sum(mi)).expand() for mi in self.get_coefficient_identifiers()] logger.info("building translation operator: done") return result
def coefficients_from_source(self, avec, bvec, rscale): from sumpy.tools import MiDerivativeTaker ppkernel = self.kernel.postprocess_at_source( self.kernel.get_expression(avec), avec) taker = MiDerivativeTaker(ppkernel, avec) return [ taker.diff(mi) * rscale ** sum(mi) for mi in self.get_coefficient_identifiers()]
def coefficients_from_source(self, avec, bvec, rscale): from sumpy.tools import MiDerivativeTaker ppkernel = self.kernel.postprocess_at_source( self.kernel.get_expression(avec), avec) taker = MiDerivativeTaker(ppkernel, avec) return [ taker.diff(mi) * rscale**sum(mi) for mi in self.get_coefficient_identifiers() ]
def coefficients_from_source(self, avec, bvec, rscale): # no point in heeding rscale here--just ignore it if bvec is None: raise RuntimeError( "cannot use line-Taylor expansions in a setting " "where the center-target vector is not known at coefficient " "formation") tau = sym.Symbol("tau") avec_line = avec + tau * bvec line_kernel = self.kernel.get_expression(avec_line) from sumpy.symbolic import USE_SYMENGINE if USE_SYMENGINE: from sumpy.tools import MiDerivativeTaker, my_syntactic_subs deriv_taker = MiDerivativeTaker(line_kernel, (tau, )) return [ my_syntactic_subs( self.kernel.postprocess_at_target( self.kernel.postprocess_at_source( deriv_taker.diff(i), avec), bvec), {tau: 0}) for i in self.get_coefficient_identifiers() ] else: # Workaround for sympy. The automatic distribution after # single-variable diff makes the expressions very large # (https://github.com/sympy/sympy/issues/4596), so avoid doing # single variable diff. # # See also https://gitlab.tiker.net/inducer/pytential/merge_requests/12 return [ self.kernel.postprocess_at_target( self.kernel.postprocess_at_source( line_kernel.diff("tau", i), avec), bvec).subs("tau", 0) for i in self.get_coefficient_identifiers() ]
def coefficients_from_source(self, avec, bvec, rscale): # no point in heeding rscale here--just ignore it if bvec is None: raise RuntimeError("cannot use line-Taylor expansions in a setting " "where the center-target vector is not known at coefficient " "formation") tau = sym.Symbol("tau") avec_line = avec + tau*bvec line_kernel = self.kernel.get_expression(avec_line) from sumpy.symbolic import USE_SYMENGINE if USE_SYMENGINE: from sumpy.tools import MiDerivativeTaker, my_syntactic_subs deriv_taker = MiDerivativeTaker(line_kernel, (tau,)) return [my_syntactic_subs( self.kernel.postprocess_at_target( self.kernel.postprocess_at_source( deriv_taker.diff(i), avec), bvec), {tau: 0}) for i in self.get_coefficient_identifiers()] else: # Workaround for sympy. The automatic distribution after # single-variable diff makes the expressions very large # (https://github.com/sympy/sympy/issues/4596), so avoid doing # single variable diff. # # See also https://gitlab.tiker.net/inducer/pytential/merge_requests/12 return [self.kernel.postprocess_at_target( self.kernel.postprocess_at_source( line_kernel.diff("tau", i), avec), bvec) .subs("tau", 0) for i in self.get_coefficient_identifiers()]
def get_derivative_taker(self, expr, dvec): return MiDerivativeTaker(expr, dvec)
def get_kernel_derivative_taker(self, bvec): from sumpy.tools import MiDerivativeTaker return MiDerivativeTaker(self.kernel.get_expression(bvec), bvec)
def translate_from(self, src_expansion, src_coeff_exprs, src_rscale, dvec, tgt_rscale): logger.info("building translation operator: %s(%d) -> %s(%d): start" % (type(src_expansion).__name__, src_expansion.order, type(self).__name__, self.order)) if not self.use_rscale: src_rscale = 1 tgt_rscale = 1 from sumpy.expansion.multipole import VolumeTaylorMultipoleExpansionBase if isinstance(src_expansion, VolumeTaylorMultipoleExpansionBase): # We know the general form of the multipole expansion is: # # coeff0 * diff(kernel, mi0) + coeff1 * diff(kernel, mi1) + ... # # To get the local expansion coefficients, we take derivatives of # the multipole expansion. # # This code speeds up derivative taking by caching all kernel # derivatives. taker = src_expansion.get_kernel_derivative_taker(dvec) from sumpy.tools import add_mi result = [] for deriv in self.get_coefficient_identifiers(): local_result = [] for coeff, term in zip( src_coeff_exprs, src_expansion.get_coefficient_identifiers()): kernel_deriv = ( src_expansion.get_scaled_multipole( taker.diff(add_mi(deriv, term)), dvec, src_rscale, nderivatives=sum(deriv) + sum(term), nderivatives_for_scaling=sum(term))) local_result.append( coeff * kernel_deriv * tgt_rscale**sum(deriv)) result.append(sym.Add(*local_result)) else: from sumpy.tools import MiDerivativeTaker expr = src_expansion.evaluate(src_coeff_exprs, dvec, rscale=src_rscale) taker = MiDerivativeTaker(expr, dvec) # Rscale/operand magnitude is fairly sensitive to the order of # operations--which is something we don't have fantastic control # over at the symbolic level. The '.expand()' below moves the two # canceling "rscales" closer to each other in the hope of helping # with that. result = [ (taker.diff(mi) * tgt_rscale**sum(mi)).expand() for mi in self.get_coefficient_identifiers()] logger.info("building translation operator: done") return result
def translate_from(self, src_expansion, src_coeff_exprs, src_rscale, dvec, tgt_rscale, sac=None): logger.info("building translation operator: %s(%d) -> %s(%d): start" % (type(src_expansion).__name__, src_expansion.order, type(self).__name__, self.order)) if not self.use_rscale: src_rscale = 1 tgt_rscale = 1 from sumpy.expansion.multipole import VolumeTaylorMultipoleExpansionBase if isinstance(src_expansion, VolumeTaylorMultipoleExpansionBase): # We know the general form of the multipole expansion is: # # coeff0 * diff(kernel, mi0) + coeff1 * diff(kernel, mi1) + ... # # To get the local expansion coefficients, we take derivatives of # the multipole expansion. # # This code speeds up derivative taking by caching all kernel # derivatives. taker = src_expansion.get_kernel_derivative_taker(dvec) from sumpy.tools import add_mi # Calculate a elementwise maximum multi-index because the number # of multi-indices needed is much less than # gnitstam(src_order + tgt order) when PDE conforming expansions # are used. For full Taylor, there's no difference. def calc_max_mi(mis): return (max(mi[i] for mi in mis) for i in range(self.dim)) src_max_mi = calc_max_mi( src_expansion.get_coefficient_identifiers()) tgt_max_mi = calc_max_mi(self.get_coefficient_identifiers()) max_mi = add_mi(src_max_mi, tgt_max_mi) # Create a expansion terms wrangler for derivatives up to order # (tgt order)+(src order) including a corresponding reduction matrix tgtplusderiv_exp_terms_wrangler = \ src_expansion.expansion_terms_wrangler.copy( order=self.order + src_expansion.order, max_mi=tuple(max_mi)) tgtplusderiv_coeff_ids = \ tgtplusderiv_exp_terms_wrangler.get_coefficient_identifiers() tgtplusderiv_full_coeff_ids = \ tgtplusderiv_exp_terms_wrangler.get_full_coefficient_identifiers() tgtplusderiv_ident_to_index = { ident: i for i, ident in enumerate(tgtplusderiv_full_coeff_ids) } result = [] for lexp_mi in self.get_coefficient_identifiers(): lexp_mi_terms = [] # Embed the source coefficients in the coefficient array # for the (tgt order)+(src order) wrangler, offset by lexp_mi. embedded_coeffs = [0] * len(tgtplusderiv_full_coeff_ids) for coeff, term in zip( src_coeff_exprs, src_expansion.get_coefficient_identifiers()): embedded_coeffs[ tgtplusderiv_ident_to_index[add_mi(lexp_mi, term)]] \ = coeff # Compress the embedded coefficient set stored_coeffs = tgtplusderiv_exp_terms_wrangler \ .get_stored_mpole_coefficients_from_full( embedded_coeffs, src_rscale, sac=sac) # Sum the embedded coefficient set for tgtplusderiv_coeff_id, coeff in zip( tgtplusderiv_coeff_ids, stored_coeffs): if coeff == 0: continue nderivatives_for_scaling = \ sum(tgtplusderiv_coeff_id)-sum(lexp_mi) kernel_deriv = (src_expansion.get_scaled_multipole( taker.diff(tgtplusderiv_coeff_id), dvec, src_rscale, nderivatives=sum(tgtplusderiv_coeff_id), nderivatives_for_scaling=nderivatives_for_scaling)) lexp_mi_terms.append(coeff * kernel_deriv * tgt_rscale**sum(lexp_mi)) result.append(sym.Add(*lexp_mi_terms)) else: from sumpy.tools import MiDerivativeTaker # Rscale/operand magnitude is fairly sensitive to the order of # operations--which is something we don't have fantastic control # over at the symbolic level. Scaling dvec, then differentiating, # and finally rescaling dvec leaves the expression needing a scaling # to compensate for differentiating which is done at the end. # This moves the two cancelling "rscales" closer to each other at # the end in the hope of helping rscale magnitude. dvec_scaled = [d * src_rscale for d in dvec] expr = src_expansion.evaluate(src_coeff_exprs, dvec_scaled, rscale=src_rscale, sac=sac) replace_dict = dict((d, d / src_rscale) for d in dvec) taker = MiDerivativeTaker(expr, dvec) rscale_ratio = sym.UnevaluatedExpr(tgt_rscale / src_rscale) result = [ (taker.diff(mi).xreplace(replace_dict) * rscale_ratio**sum(mi)) for mi in self.get_coefficient_identifiers() ] logger.info("building translation operator: done") return result