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 evaluate(self, coeffs, bvec, rscale, sac=None): if not self.use_rscale: rscale = 1 taker = self.get_kernel_derivative_taker(bvec) result = sym.Add(*tuple( coeff * self.get_scaled_multipole(taker.diff(mi), bvec, rscale, sum(mi)) for coeff, mi in zip(coeffs, self.get_coefficient_identifiers()))) return result
def evaluate(self, coeffs, bvec, rscale): # no point in heeding rscale here--just ignore it from pytools import factorial return sym.Add(*(coeffs[self.get_storage_index(i)] / factorial(i) for i in self.get_coefficient_identifiers()))
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