def integrand(xi, n, n_p, l): """ Integral to compute the first term in the gof of a BFE. \hat{\rho}(x|\gamma)^2 See Equation 8 in companion notes. """ factor = (xi + 1)**(2 * l) * (1 - xi)**(2 * l + 3 / 2.) gegenbauer1 = special.gegenbauer(n, 2 * l + 3 / 2.) gegenbauer2 = special.gegenbauer(n_p, 2 * l + 3 / 2.) return factor * gegenbauer1(xi) * gegenbauer2(xi)
def compute_eigenvalues(self, mode='envelope'): """ Compute the eigenvalues of the integral operator associated with a real valud function of the pairwise distances between latent positions """ self.compute_dimensions_sphere(R=40) eigenvalues = [] beta = (self.d - 2) / 2 bd = math.gamma( self.d / 2) / (math.gamma(1 / 2) * math.gamma(self.d / 2 - 1 / 2)) for l in range(min(40, len(self.dimensions))): Gegen = gegenbauer(l, beta) if mode == 'envelope': integral = integrate.quad( lambda x: self.compute_envelope(x) * Gegen(x) * (1 - x**2)**(beta - 1 / 2), -1, 1) elif mode == 'latitude': integral = integrate.quad( lambda x: self.density_latitude(x) * Gegen(x) * (1 - x**2)**(beta - 1 / 2), -1, 1) else: integral = integrate.quad( lambda x: self.latitude_estimator(x) * Gegen(x) * (1 - x**2)**(beta - 1 / 2), -1, 1) cl = (2 * l + self.d - 2) / (self.d - 2) eigenvalues.append(cl * bd * integral[0] / self.dimensions[l]) return eigenvalues
def function_gegenbauer(self, t, spectrum): """ Returns the value at t of the function with a decomposition 'spectrum' in the gegenbauer basis """ res = 0 for i in range(len(spectrum)): coeffi = (2 * i + self.d - 2) / (self.d - 2) Gegen = gegenbauer(i, (self.d - 2) / 2) res += spectrum[i] * coeffi * Gegen(t) return res
def pure_py(xyz, Snlm, Tnlm, nmax, lmax): from scipy.special import lpmv, gegenbauer, eval_gegenbauer, gamma from math import factorial as f Plm = lambda l,m,costh: lpmv(m, l, costh) Ylmth = lambda l,m,costh: np.sqrt((2*l+1)/(4 * np.pi) * f(l-m)/f(l+m)) * Plm(l,m,costh) twopi = 2*np.pi sqrtpi = np.sqrt(np.pi) sqrt4pi = np.sqrt(4*np.pi) r = np.sqrt(np.sum(xyz**2, axis=0)) X = xyz[2]/r # cos(theta) sinth = np.sqrt(1 - X**2) phi = np.arctan2(xyz[1], xyz[0]) xsi = (r - 1) / (r + 1) density = 0 potenti = 0 gradien = np.zeros_like(xyz) sph_gradien = np.zeros_like(xyz) for l in range(lmax+1): r_term1 = r**l / (r*(1+r)**(2*l+3)) r_term2 = r**l / (1+r)**(2*l+1) for m in range(l+1): for n in range(nmax+1): Cn = gegenbauer(n, 2*l+3/2) Knl = 0.5 * n * (n+4*l+3) + (l+1)*(2*l+1) rho_nl = Knl / twopi * sqrt4pi * r_term1 * Cn(xsi) phi_nl = -sqrt4pi * r_term2 * Cn(xsi) density += rho_nl * Ylmth(l,m,X) * (Snlm[n,l,m]*np.cos(m*phi) + Tnlm[n,l,m]*np.sin(m*phi)) potenti += phi_nl * Ylmth(l,m,X) * (Snlm[n,l,m]*np.cos(m*phi) + Tnlm[n,l,m]*np.sin(m*phi)) # derivatives dphinl_dr = (2*sqrtpi*np.power(r,-1 + l)*np.power(1 + r,-3 - 2*l)* (-2*(3 + 4*l)*r*eval_gegenbauer(-1 + n,2.5 + 2*l,(-1 + r)/(1 + r)) + (1 + r)*(l*(-1 + r) + r)*eval_gegenbauer(n,1.5 + 2*l,(-1 + r)/(1 + r)))) sph_gradien[0] += dphinl_dr * Ylmth(l,m,X) * (Snlm[n,l,m]*np.cos(m*phi) + Tnlm[n,l,m]*np.sin(m*phi)) A = np.sqrt((2*l+1) / (4*np.pi)) * np.sqrt(gamma(l-m+1) / gamma(l+m+1)) dYlm_dth = A / sinth * (l*X*Plm(l,m,X) - (l+m)*Plm(l-1,m,X)) sph_gradien[1] += (1/r) * dYlm_dth * phi_nl * (Snlm[n,l,m]*np.cos(m*phi) + Tnlm[n,l,m]*np.sin(m*phi)) sph_gradien[2] += (m/(r*sinth)) * phi_nl * Ylmth(l,m,X) * (-Snlm[n,l,m]*np.sin(m*phi) + Tnlm[n,l,m]*np.cos(m*phi)) cosphi = np.cos(phi) sinphi = np.sin(phi) gradien[0] = sinth*cosphi*sph_gradien[0] + X*cosphi*sph_gradien[1] - sinphi*sph_gradien[2] gradien[1] = sinth*sinphi*sph_gradien[0] + X*sinphi*sph_gradien[1] + cosphi*sph_gradien[2] gradien[2] = X*sph_gradien[0] - sinth*sph_gradien[1] return density, potenti, gradien
def Icom(n, z): summation = 0 for k in range(math.floor(n / 2) + 1): l = n - 2 * k summation += a(n, k, 3 / 2) * ( sum([(1. / (l + 2) + 1. / (l + 1) - 2. / (l - j)) * (1 + (-1)**(l - j)) * z**j / 2. for j in range(l)]) + (1. / (l + 2) + 1. / (l + 1) + 2 * sum([1. / (j + 1) for j in range(l)])) * z**l) return 3 * gegenbauer(n, 3 / 2)(z) / 2 - summation
def pure_py(xyz, Snlm, Tnlm, nmax, lmax): from scipy.special import lpmv, gegenbauer, eval_gegenbauer, gamma from math import factorial as f def Plm(l, m, costh): return lpmv(m, l, costh) def Ylmth(l, m, costh): return np.sqrt((2*l+1)/(4 * np.pi) * f(l-m)/f(l+m)) * Plm(l, m, costh) twopi = 2*np.pi sqrtpi = np.sqrt(np.pi) sqrt4pi = np.sqrt(4*np.pi) r = np.sqrt(np.sum(xyz**2, axis=0)) X = xyz[2] / r # cos(theta) sinth = np.sqrt(1 - X**2) phi = np.arctan2(xyz[1], xyz[0]) xsi = (r - 1) / (r + 1) density = 0 potenti = 0 gradien = np.zeros_like(xyz) sph_gradien = np.zeros_like(xyz) for l in range(lmax+1): r_term1 = r**l / (r*(1+r)**(2*l+3)) r_term2 = r**l / (1+r)**(2*l+1) for m in range(l+1): for n in range(nmax+1): Cn = gegenbauer(n, 2*l+3/2) Knl = 0.5 * n * (n+4*l+3) + (l+1)*(2*l+1) rho_nl = Knl / twopi * sqrt4pi * r_term1 * Cn(xsi) phi_nl = -sqrt4pi * r_term2 * Cn(xsi) density += rho_nl * Ylmth(l, m, X) * (Snlm[n, l, m]*np.cos(m*phi) + Tnlm[n, l, m]*np.sin(m*phi)) potenti += phi_nl * Ylmth(l, m, X) * (Snlm[n, l, m]*np.cos(m*phi) + Tnlm[n, l, m]*np.sin(m*phi)) # derivatives dphinl_dr = ( 2*sqrtpi*np.power(r, -1 + l)*np.power(1 + r, -3 - 2*l) * (-2*(3 + 4*l)*r*eval_gegenbauer(-1 + n, 2.5 + 2*l, (-1 + r)/(1 + r)) + (1 + r)*(l*(-1 + r) + r)*eval_gegenbauer(n, 1.5 + 2*l, (-1 + r)/(1 + r)))) sph_gradien[0] += dphinl_dr * Ylmth(l, m, X) * (Snlm[n, l, m]*np.cos(m*phi) + Tnlm[n, l, m]*np.sin(m*phi)) A = np.sqrt((2*l+1) / (4*np.pi)) * np.sqrt(gamma(l-m+1) / gamma(l+m+1)) dYlm_dth = A / sinth * (l*X*Plm(l, m, X) - (l+m)*Plm(l-1, m, X)) sph_gradien[1] += (1/r) * dYlm_dth * phi_nl * (Snlm[n, l, m]*np.cos(m*phi) + Tnlm[n, l, m]*np.sin(m*phi)) sph_gradien[2] += (m/(r*sinth)) * phi_nl * Ylmth(l, m, X) * ( -Snlm[n, l, m]*np.sin(m*phi) + Tnlm[n, l, m]*np.cos(m*phi)) cosphi = np.cos(phi) sinphi = np.sin(phi) gradien[0] = sinth*cosphi*sph_gradien[0] + X*cosphi*sph_gradien[1] - sinphi*sph_gradien[2] gradien[1] = sinth*sinphi*sph_gradien[0] + X*sinphi*sph_gradien[1] + cosphi*sph_gradien[2] gradien[2] = X*sph_gradien[0] - sinth*sph_gradien[1] return density, potenti, gradien
def scf_compute_coeffs_nbody(pos,mass,N,L,a=1.): """ NAME: scf_compute_coeffs PURPOSE: Numerically compute the expansion coefficients for a given triaxial density INPUT: pos - Positions of particles m - mass of particles N - size of the Nth dimension of the expansion coefficients L - size of the Lth and Mth dimension of the expansion coefficients a - parameter used to shift the basis functions radial_order - Number of sample points of the radial integral. If None, radial_order=max(20, N + 3/2L + 1) costheta_order - Number of sample points of the costheta integral. If None, If costheta_order=max(20, L + 1) phi_order - Number of sample points of the phi integral. If None, If costheta_order=max(20, L + 1) OUTPUT: (Acos,Asin) - Expansion coefficients for density dens that can be given to SCFPotential.__init__ HISTORY: 2020-11-18 - Written - Morgan Bennett """ n = numpy.arange(0,N) l = numpy.arange(0,L) m = numpy.arange(0,L) r = numpy.sqrt(pos[0]**2+pos[1]**2+pos[2]**2) phi = numpy.arctan2(pos[1],pos[0]) costheta = pos[2]/r Anlm= numpy.zeros([2,L,L,L]) for i,nn in enumerate(n): for j,ll in enumerate(l): for k,mm in enumerate(m[:j+1]): Plm= lpmv(mm,ll,costheta) cosmphi= numpy.cos(phi*mm) sinmphi= numpy.sin(phi*mm) Ylm= (numpy.sqrt((2.*ll+1)*gamma(ll-mm+1)/gamma(ll+mm+1))*Plm)[None,:]*numpy.array([cosmphi,sinmphi]) Ylm= numpy.nan_to_num(Ylm) C= gegenbauer(nn,2.*ll+1.5) Cn= C((r/a-1)/(r/a+1)) phinlm= (-(r/a)**ll/(r/a+1)**(2.*ll+1)*Cn)[None,:]*Ylm Sum= numpy.sum(mass[None,:]*phinlm,axis=1) Knl= 0.5*nn*(nn+4.*ll+3.)+(ll+1)*(2.*ll+1.) Inl= (-Knl*4.*numpy.pi/2.**(8.*ll+6.)*gamma(nn+4.*ll+3.)/gamma(nn+1)/(nn+2.*ll+1.5)/gamma(2.*ll+1.5)**2) Anlm[:,i,j,k]= Inl**(-1)*Sum return 2.*Anlm
def scf_compute_coeffs_nbody( pos, mass, N, L, a=1.0, radial_order=None, costheta_order=None, phi_order=None, ): """Compute SCF Coefficients Numerically compute the expansion coefficients for a given triaxial density Parameters ---------- pos : (3, N) array Positions of particles m : scalar or (N,) array mass of particles N : int size of the Nth dimension of the expansion coefficients L : int size of the Lth and Mth dimension of the expansion coefficients a : float or Quantity parameter used to shift the basis functions Returns ------- Acos, Asin : array Expansion coefficients for density dens that can be given to ``SCFPotential.__init__`` .. versionadded:: 1.7 2020-11-18 - Written - Morgan Bennett """ mass = mass.to_value(1e12 * u.solMass) # :( ns = np.arange(0, N) ls = np.arange(0, L) ms = np.arange(0, L) ra = (np.sqrt(np.sum(np.square(pos), axis=0)) / a).to_value(u.one) phi = np.arctan2(pos[1], pos[0]) costheta = (pos[2] / ra / a).to_value(u.one) Anlm = np.zeros([2, N, L, L]) for i, nn in enumerate(ns): for j, ll in enumerate(ls): for k, mm in enumerate(ms[:j + 1]): Plm = lpmv(mm, ll, costheta) cosmphi = np.cos(phi * mm) sinmphi = np.sin(phi * mm) Ylm = (np.sqrt( (2.0 * ll + 1) * gamma(ll - mm + 1) / gamma(ll + mm + 1), ) * Plm)[None, :] * np.array([cosmphi, sinmphi]) Ylm = np.nan_to_num(Ylm) C = gegenbauer(nn, 2.0 * ll + 1.5) Cn = C( u.Quantity((ra - 1) / (ra + 1), copy=False).to_value(u.one, ), ) phinlm = (-np.power(ra, ll) / np.power(ra + 1, (2.0 * ll + 1)) * Cn)[None, :] * Ylm Sum = np.sum(mass[None, :] * phinlm, axis=1) Knl = 0.5 * nn * (nn + 4.0 * ll + 3.0) + (ll + 1) * (2.0 * ll + 1.0) Inl = (-Knl * 4.0 * np.pi / 2.0**(8.0 * ll + 6.0) * gamma(nn + 4.0 * ll + 3.0) / gamma(nn + 1) / (nn + 2.0 * ll + 1.5) / gamma(2.0 * ll + 1.5)**2) Anlm[:, i, j, k] = Inl**(-1) * Sum return 2.0 * Anlm
def Iexp(n, z): return V(n) * gegenbauer(n, 3 / 2)(z)