-
Notifications
You must be signed in to change notification settings - Fork 95
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Calculation of SCF expansion based on an N-body representation of the density #444
Changes from 3 commits
845bb4c
04682eb
9b938e4
3167e09
5b7b653
87ce232
64a5bf4
d706ec5
e370fbd
a5915f6
141bbe1
7b9f454
ce271f3
7eeaadc
b69966b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,8 @@ | ||
import hashlib | ||
import numpy | ||
from numpy.polynomial.legendre import leggauss | ||
from scipy.special import lpmn | ||
from scipy.special import gammaln | ||
from scipy.special import lpmn,lpmv | ||
from scipy.special import gammaln, gamma, gegenbauer | ||
from ..util import coords, conversion | ||
from .Potential import Potential | ||
|
||
|
@@ -506,7 +506,10 @@ def OmegaP(self): | |
|
||
|
||
def _xiToR(xi, a =1): | ||
return a*numpy.divide((1. + xi),(1. - xi)) | ||
return a*numpy.divide((1. + xi),(1. - xi)) | ||
|
||
def _RToxi(r, a=1): | ||
return numpy.divide((r/a-1.),(r/a+1.)) | ||
|
||
|
||
def _C(xi, N,L, alpha = lambda x: 2*x + 3./2): | ||
|
@@ -546,7 +549,33 @@ def _dC(xi, N, L): | |
CC[0, :] = 0 | ||
CC *= 2*(2*l + 3./2) | ||
return CC | ||
|
||
|
||
def scf_compute_coeffs_spherical_nbody(pos,m,N,a=1.): | ||
|
||
''' | ||
INPUT: | ||
pos - position of particles in your nbody snapshot | ||
|
||
m - masses of particles | ||
|
||
N - size of expansion coefficients | ||
|
||
a - parameter used to shift the basis functions | ||
|
||
''' | ||
Acos = numpy.zeros((N,1,1), float) | ||
Asin = None | ||
|
||
r= numpy.sqrt(pos[0]**2+pos[1]**2+pos[2]**2) | ||
Cs= numpy.array([_C(_RToxi(ri,a=a),N,1)[:,0] for ri in r]) | ||
RhoSum= numpy.sum((m/(4.*numpy.pi)/(r/a+1))[:,None]*Cs,axis=0) | ||
n = numpy.arange(0,N) | ||
K = 16*numpy.pi*(n + 3./2)/((n + 2)*(n + 1)*(1 + n*(n + 3.)/2.)) | ||
Acos[n,0,0] = 2*K*RhoSum | ||
|
||
return Acos, Asin | ||
|
||
|
||
def scf_compute_coeffs_spherical(dens, N, a=1., radial_order=None): | ||
""" | ||
NAME: | ||
|
@@ -685,6 +714,108 @@ def integrand(xi, costheta): | |
Acos[:,:,0] = 2*I**-1 * integrated*constants | ||
|
||
return Acos, Asin | ||
|
||
def scf_compute_coeffs_nbody(pos,mass,N,L,a=1., radial_order=None, costheta_order=None, phi_order=None): | ||
|
||
""" | ||
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 | ||
''' | ||
Plm= numpy.zeros([len(costheta),1,L,L]) | ||
for i,ll in enumerate(l): | ||
for j,mm in enumerate(m): | ||
Plm[:,0,j,i]= lpmv(ll,mm,costheta) | ||
|
||
cosmphi= numpy.cos(phi[:,None]*m[None,:])[:,None,None,:] | ||
sinmphi= numpy.sin(phi[:,None]*m[None,:])[:,None,None,:] | ||
|
||
Ylm= (numpy.sqrt((2.*l[:,None]+1)*gamma(l[:,None]-m[None,:]+1)/gamma(l[:,None]+m[None,:]+1))[None,None,:,:]*Plm)[None,:,:,:,:]*numpy.array([cosmphi,sinmphi]) | ||
Ylm= numpy.nan_to_num(Ylm) | ||
|
||
C= [[gegenbauer(nn,2.*ll+1.5) for ll in l] for nn in n] | ||
Cn= numpy.zeros((1,len(r),N,L,1)) | ||
for i in range(N): | ||
for j in range(L): | ||
Cn[0,:,i,j,0]= C[i][j]((r/a-1)/(r/a+1)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Likewise, |
||
|
||
rl= ((r[:,None]/a)**l[None,:])[None,:,None,:,None] | ||
r12l1= ((1+(r[:,None]/a))**(2.*l[None,:]+1))[None,:,None,:,None] | ||
|
||
phinlm= -rl/r12l1*Cn*Ylm | ||
|
||
Sum= numpy.sum(mass[None,:,None,None,None]*phinlm,axis=1) | ||
Knl= 0.5*n[:,None]*(n[:,None]+4.*l[None,:]+3.)+(l[None,:]+1)*(2.*l[None,:]+1.) | ||
|
||
Inl= (-Knl*4.*numpy.pi/2.**(8.*l[None,:]+6.)*gamma(n[:,None]+4.*l[None,:]+3.)/gamma(n[:,None]+1)/(n[:,None]+2.*l[None,:]+1.5)/gamma(2.*l[None,:]+1.5)**2)[None,:,:,None] | ||
|
||
Anlm= Inl**(-1)*Sum''' | ||
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) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Astropy does not (yet) support the ufunc |
||
|
||
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)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Likewise, |
||
|
||
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(dens, N, L, a=1., radial_order=None, costheta_order=None, phi_order=None): | ||
""" | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Astropy does not (yet) support the ufunc
lpmv
, so dimensionless quantities need to be recast.The following works on both normal and astropy Quantity arrays.
costheta = u.Quantity(pos[2]/r, copy=False).to_value(u.one)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To deal with positions given with units, we'll just have to apply the relevant decorator. I think probably the one used for the 2D potential functions should work?