###############################################################################
# evolveddiskdf.py: module that builds a distribution function as a
# steady-state DF + subsequent evolution
#
# This module contains the following classes:
#
# evolveddiskdf - top-level class that represents a distribution function
###############################################################################
from __future__ import print_function
_NSIGMA= 4.
_NTS= 1000
_PROFILE= False
import sys
import copy
import time as time_module
import warnings
import numpy
from scipy import integrate
from ..util import galpyWarning
from ..orbit import Orbit
from ..potential import calcRotcurve
from .df import df
from ..potential.Potential import _check_c
from ..util.quadpack import dblquad
from ..util import plot
from ..util.conversion import physical_conversion, \
potential_physical_input, parse_time
_DEGTORAD= numpy.pi/180.
_RADTODEG= 180./numpy.pi
_NAN= numpy.nan
[docs]class evolveddiskdf(df):
"""Class that represents a diskdf as initial DF + subsequent secular evolution"""
[docs] def __init__(self,initdf,pot,to=0.):
"""
NAME:
__init__
PURPOSE:
initialize
INPUT:
initdf - the df at the start of the evolution (at to) (units are transferred)
pot - potential to integrate orbits in
to= initial time (time at which initdf is evaluated; orbits are integrated from current t back to to) (can be Quantity)
OUTPUT:
instance
HISTORY:
2011-03-30 - Written - Bovy (NYU)
"""
if initdf._roSet: ro= initdf._ro
else: ro= None
if initdf._voSet: vo= initdf._vo
else: vo= None
df.__init__(self,ro=ro,vo=vo)
self._initdf= initdf
self._pot= pot
self._to= parse_time(to,ro=self._ro,vo=self._vo)
[docs] @physical_conversion('phasespacedensity2d',pop=True)
def __call__(self,*args,**kwargs):
"""
NAME:
__call__
PURPOSE:
evaluate the distribution function
INPUT:
Orbit instance:
a) Orbit instance alone: use initial state and t=0
b) Orbit instance + t: Orbit instance *NOT* called (i.e., Orbit's initial condition is used, call Orbit yourself), t can be Quantity
If t is a list of t, DF is returned for each t, times must be in descending order and equally spaced (does not work with marginalize...)
marginalizeVperp - marginalize over perpendicular velocity (only supported with 1a) above) + nsigma, +scipy.integrate.quad keywords
marginalizeVlos - marginalize over line-of-sight velocity (only supported with 1a) above) + nsigma, +scipy.integrate.quad keywords
log= if True, return the log (not for deriv, bc that can be negative)
integrate_method= method argument of orbit.integrate
deriv= None, 'R', or 'phi': calculates derivative of the moment wrt R or phi **not with the marginalize options**
OUTPUT:
DF(orbit,t)
HISTORY:
2011-03-30 - Written - Bovy (NYU)
2011-04-15 - Added list of times option - Bovy (NYU)
"""
integrate_method= kwargs.pop('integrate_method','dopr54_c')
# Must match Python fallback for non-C potentials here, bc odeint needs
# custom t list to avoid numerically instabilities
if '_c' in integrate_method and not _check_c(self._pot):
if ('leapfrog' in integrate_method \
or 'symplec' in integrate_method):
integrate_method= 'leapfrog'
else:
integrate_method= 'odeint'
deriv= kwargs.get('deriv',None)
if isinstance(args[0],Orbit):
if len(args[0]) > 1:
raise RuntimeError('Only single-object Orbit instances can be passed to DF instances at this point') #pragma: no cover
if len(args) == 1:
t= 0.
else:
t= args[1]
else:
raise IOError("Input to __call__ not understood; this has to be an Orbit instance with optional time")
if isinstance(t,list):
t= numpy.array(t)
tlist= True
elif isinstance(t,numpy.ndarray) and \
not (hasattr(t,'isscalar') and t.isscalar):
tlist= True
else: tlist= False
t= parse_time(t,ro=self._ro,vo=self._vo)
if kwargs.pop('marginalizeVperp',False):
if tlist: raise IOError("Input times to __call__ is a list; this is not supported in conjunction with marginalizeVperp")
if kwargs.pop('log',False):
return numpy.log(self._call_marginalizevperp(args[0],integrate_method=integrate_method,**kwargs))
else:
return self._call_marginalizevperp(args[0],integrate_method=integrate_method,**kwargs)
elif kwargs.pop('marginalizeVlos',False):
if tlist: raise IOError("Input times to __call__ is a list; this is not supported in conjunction with marginalizeVlos")
if kwargs.pop('log',False):
return numpy.log(self._call_marginalizevlos(args[0],integrate_method=integrate_method,**kwargs))
else:
return self._call_marginalizevlos(args[0],integrate_method=integrate_method,**kwargs)
#Integrate back
if tlist:
if self._to == t[0]:
if kwargs.get('log',False):
return numpy.log([self._initdf(args[0],use_physical=False)])
else:
return [self._initdf(args[0],use_physical=False)]
ts= self._create_ts_tlist(t,integrate_method)
o= args[0]
#integrate orbit
if _PROFILE: #pragma: no cover
start= time_module.time()
if not deriv is None:
#Also calculate the derivative of the initial df with respect to R, phi, vR, and vT, and the derivative of Ro wrt R/phi etc., to calculate the derivative; in this case we also integrate a small area of phase space
if deriv.lower() == 'r':
dderiv= 10.**-10.
tmp= o.R(use_physical=False)+dderiv
dderiv= tmp-o.R(use_physical=False)
msg= o.integrate_dxdv([dderiv,0.,0.,0.],ts,self._pot,method=integrate_method)
elif deriv.lower() == 'phi':
dderiv= 10.**-10.
tmp= o.phi(use_physical=False)+dderiv
dderiv= tmp-o.phi(use_physical=False)
msg= o.integrate_dxdv([0.,0.,0.,dderiv],ts,self._pot,method=integrate_method)
if not msg is None and msg > 0.: # pragma: no cover
print("Warning: dxdv integration inaccurate, returning zero everywhere ... result might not be correct ...")
if kwargs.get('log',False) and deriv is None: return numpy.zeros(len(t))-numpy.finfo(numpy.dtype(numpy.float64)).max
else: return numpy.zeros(len(t))
else:
o.integrate(ts,self._pot,method=integrate_method)
if _PROFILE: #pragma: no cover
int_time= (time_module.time()-start)
#Now evaluate the DF
if _PROFILE: #pragma: no cover
start= time_module.time()
if integrate_method == 'odeint':
retval= []
os= [o(self._to+t[0]-ti,use_physical=False) for ti in t]
retval= numpy.array(self._initdf(os,use_physical=False))
else:
if len(t) == 1:
orb_array= o.getOrbit().T
orb_array= orb_array[:,1]
else:
orb_array= o.getOrbit().T
retval= self._initdf(orb_array,use_physical=False)
if (isinstance(retval,float) or len(retval.shape) == 0) \
and numpy.isnan(retval):
retval= 0.
elif not isinstance(retval,float) and len(retval.shape) > 0:
retval[(numpy.isnan(retval))]= 0.
if len(t) > 1: retval= retval[::-1]
if _PROFILE: #pragma: no cover
df_time= (time_module.time()-start)
tot_time= int_time+df_time
print(int_time/tot_time, df_time/tot_time, tot_time)
if not deriv is None:
if integrate_method == 'odeint':
dlnfdRo= numpy.array([self._initdf._dlnfdR(o.R(self._to+t[0]-ti,use_physical=False),
o.vR(self._to+t[0]-ti,use_physical=False),
o.vT(self._to+t[0]-ti,use_physical=False))
for ti in t])
dlnfdvRo= numpy.array([self._initdf._dlnfdvR(o.R(self._to+t[0]-ti,use_physical=False),
o.vR(self._to+t[0]-ti,use_physical=False),
o.vT(self._to+t[0]-ti,use_physical=False))
for ti in t])
dlnfdvTo= numpy.array([self._initdf._dlnfdvT(o.R(self._to+t[0]-ti,use_physical=False),
o.vR(self._to+t[0]-ti,use_physical=False),
o.vT(self._to+t[0]-ti,use_physical=False))
for ti in t])
dRo= numpy.array([o.getOrbit_dxdv()[list(ts).index(self._to+t[0]-ti),0] for ti in t])/dderiv
dvRo= numpy.array([o.getOrbit_dxdv()[list(ts).index(self._to+t[0]-ti),1] for ti in t])/dderiv
dvTo= numpy.array([o.getOrbit_dxdv()[list(ts).index(self._to+t[0]-ti),2] for ti in t])/dderiv
#print(dRo, dvRo, dvTo)
dlnfderiv= dlnfdRo*dRo+dlnfdvRo*dvRo+dlnfdvTo*dvTo
retval*= dlnfderiv
else:
if len(t) == 1:
dlnfdRo= self._initdf._dlnfdR(orb_array[0],
orb_array[1],
orb_array[2])
dlnfdvRo= self._initdf._dlnfdvR(orb_array[0],
orb_array[1],
orb_array[2])
dlnfdvTo= self._initdf._dlnfdvT(orb_array[0],
orb_array[1],
orb_array[2])
else:
dlnfdRo= numpy.array([self._initdf._dlnfdR(orb_array[0,ii],
orb_array[1,ii],
orb_array[2,ii])
for ii in range(len(t))])
dlnfdvRo= numpy.array([self._initdf._dlnfdvR(orb_array[0,ii],
orb_array[1,ii],
orb_array[2,ii])
for ii in range(len(t))])
dlnfdvTo= numpy.array([self._initdf._dlnfdvT(orb_array[0,ii],
orb_array[1,ii],
orb_array[2,ii])
for ii in range(len(t))])
dorb_array= o.getOrbit_dxdv().T
if len(t) == 1: dorb_array= dorb_array[:,1]
dRo= dorb_array[0]/dderiv
dvRo= dorb_array[1]/dderiv
dvTo= dorb_array[2]/dderiv
#print(dRo, dvRo, dvTo)
dlnfderiv= dlnfdRo*dRo+dlnfdvRo*dvRo+dlnfdvTo*dvTo
if len(t) > 1: dlnfderiv= dlnfderiv[::-1]
retval*= dlnfderiv
else:
if self._to == t and deriv is None:
if kwargs.get('log',False):
return numpy.log(self._initdf(args[0],use_physical=False))
else:
return self._initdf(args[0],use_physical=False)
elif self._to == t and not deriv is None:
if deriv.lower() == 'r':
return self._initdf(args[0])\
*self._initdf._dlnfdR(args[0].vxvv[...,0],
args[0].vxvv[...,1],
args[0].vxvv[...,2])
elif deriv.lower() == 'phi':
return 0.
if integrate_method == 'odeint':
ts= numpy.linspace(t,self._to,_NTS)
else:
ts= numpy.linspace(t,self._to,2)
o= args[0]
#integrate orbit
if not deriv is None:
ts= numpy.linspace(t,self._to,_NTS)
#Also calculate the derivative of the initial df with respect to R, phi, vR, and vT, and the derivative of Ro wrt R/phi etc., to calculate the derivative; in this case we also integrate a small area of phase space
if deriv.lower() == 'r':
dderiv= 10.**-10.
tmp= o.R(use_physical=False)+dderiv
dderiv= tmp-o.R(use_physical=False)
o.integrate_dxdv([dderiv,0.,0.,0.],ts,self._pot,method=integrate_method)
elif deriv.lower() == 'phi':
dderiv= 10.**-10.
tmp= o.phi(use_physical=False)+dderiv
dderiv= tmp-o.phi(use_physical=False)
o.integrate_dxdv([0.,0.,0.,dderiv],ts,self._pot,method=integrate_method)
else:
o.integrate(ts,self._pot,method=integrate_method)
#int_time= (time.time()-start)
#Now evaluate the DF
if o.R(self._to-t,use_physical=False) <= 0.:
if kwargs.get('log',False):
return -numpy.finfo(numpy.dtype(numpy.float64)).max
else:
return numpy.finfo(numpy.dtype(numpy.float64)).eps
#start= time.time()
retval= self._initdf(o(self._to-t,use_physical=False),
use_physical=False)
#print( int_time/(time.time()-start))
if numpy.isnan(retval): print(retval, o.vxvv, o(self._to-t).vxvv)
if not deriv is None:
thisorbit= o(self._to-t).vxvv[0]
dlnfdRo= self._initdf._dlnfdR(thisorbit[0],
thisorbit[1],
thisorbit[2])
dlnfdvRo= self._initdf._dlnfdvR(thisorbit[0],
thisorbit[1],
thisorbit[2])
dlnfdvTo= self._initdf._dlnfdvT(thisorbit[0],
thisorbit[1],
thisorbit[2])
indx= list(ts).index(self._to-t)
dRo= o.getOrbit_dxdv()[indx,0]/dderiv
dvRo= o.getOrbit_dxdv()[indx,1]/dderiv
dvTo= o.getOrbit_dxdv()[indx,2]/dderiv
dlnfderiv= dlnfdRo*dRo+dlnfdvRo*dvRo+dlnfdvTo*dvTo
retval*= dlnfderiv
if kwargs.get('log',False) and deriv is None:
if tlist:
out= numpy.log(retval)
out[retval == 0.]= -numpy.finfo(numpy.dtype(numpy.float64)).max
else:
if retval == 0.: out= -numpy.finfo(numpy.dtype(numpy.float64)).max
else: out= numpy.log(retval)
return out
else:
return retval
[docs] def vmomentsurfacemass(self,R,n,m,t=0.,nsigma=None,deg=False,
epsrel=1.e-02,epsabs=1.e-05,phi=0.,
grid=None,gridpoints=101,returnGrid=False,
hierarchgrid=False,nlevels=2,
print_progress=False,
integrate_method='dopr54_c',
deriv=None):
"""
NAME:
vmomentsurfacemass
PURPOSE:
calculate the an arbitrary moment of the velocity distribution at (R,phi) times the surfacmass
INPUT:
R - radius at which to calculate the moment (in natural units)
phi= azimuth (rad unless deg=True)
n - vR^n
m - vT^m
t= time at which to evaluate the DF (can be a list or ndarray; if this is the case, list needs to be in descending order and equally spaced)
nsigma - number of sigma to integrate the velocities over (based on an estimate, so be generous, but not too generous)
deg= azimuth is in degree (default=False)
epsrel, epsabs - scipy.integrate keywords (the integration calculates the ratio of this vmoment to that of the initial DF)
grid= if set to True, build a grid and use that to evaluate integrals; if set to a grid-objects (such as returned by this procedure), use this grid; if this was created for a list of times, moments are calculated for each time
gridpoints= number of points to use for the grid in 1D (default=101)
returnGrid= if True, return the grid object (default=False)
hierarchgrid= if True, use a hierarchical grid (default=False)
nlevels= number of hierarchical levels for the hierarchical grid
print_progress= if True, print progress updates
integrate_method= orbit.integrate method argument
deriv= None, 'R', or 'phi': calculates derivative of the moment wrt R or phi **onnly with grid options**
OUTPUT:
<vR^n vT^m x surface-mass> at R,phi (no support for units)
COMMENT:
grid-based calculation is the only one that is heavily tested (although the test suite also tests the direct calculation)
HISTORY:
2011-03-30 - Written - Bovy (NYU)
"""
#if we have already precalculated a grid, use that
if not grid is None and isinstance(grid,evolveddiskdfGrid):
if returnGrid:
return (self._vmomentsurfacemassGrid(n,m,grid),grid)
else:
return self._vmomentsurfacemassGrid(n,m,grid)
elif not grid is None \
and isinstance(grid,evolveddiskdfHierarchicalGrid):
if returnGrid:
return (self._vmomentsurfacemassHierarchicalGrid(n,m,grid),
grid)
else:
return self._vmomentsurfacemassHierarchicalGrid(n,m,grid)
#Otherwise we need to do some more work
if deg: az= phi*_DEGTORAD
else: az= phi
if nsigma is None: nsigma= _NSIGMA
if _PROFILE: #pragma: no cover
start= time_module.time()
if hasattr(self._initdf,'_estimatemeanvR') \
and hasattr(self._initdf,'_estimatemeanvT') \
and hasattr(self._initdf,'_estimateSigmaR2') \
and hasattr(self._initdf,'_estimateSigmaT2'):
sigmaR1= numpy.sqrt(self._initdf._estimateSigmaR2(R,phi=az))
sigmaT1= numpy.sqrt(self._initdf._estimateSigmaT2(R,phi=az))
meanvR= self._initdf._estimatemeanvR(R,phi=az)
meanvT= self._initdf._estimatemeanvT(R,phi=az)
else:
warnings.warn("No '_estimateSigmaR2' etc. functions found for initdf in evolveddf; thus using potentially slow sigmaR2 etc functions",
galpyWarning)
sigmaR1= numpy.sqrt(self._initdf.sigmaR2(R,phi=az,use_physical=False))
sigmaT1= numpy.sqrt(self._initdf.sigmaT2(R,phi=az,use_physical=False))
meanvR= self._initdf.meanvR(R,phi=az,use_physical=False)
meanvT= self._initdf.meanvT(R,phi=az,use_physical=False)
if _PROFILE: #pragma: no cover
setup_time= (time_module.time()-start)
if not grid is None and isinstance(grid,bool) and grid:
if not hierarchgrid:
if _PROFILE: #pragma: no cover
start= time_module.time()
grido= self._buildvgrid(R,az,nsigma,t,
sigmaR1,sigmaT1,meanvR,meanvT,
gridpoints,print_progress,
integrate_method,deriv)
if _PROFILE: #pragma: no cover
grid_time= (time_module.time()-start)
print(setup_time/(setup_time+grid_time), \
grid_time/(setup_time+grid_time), \
setup_time+grid_time)
if returnGrid:
return (self._vmomentsurfacemassGrid(n,m,grido),grido)
else:
return self._vmomentsurfacemassGrid(n,m,grido)
else: #hierarchical grid
grido= evolveddiskdfHierarchicalGrid(self,R,az,nsigma,t,
sigmaR1,sigmaT1,meanvR,
meanvT,
gridpoints,nlevels,deriv,
print_progress=print_progress)
if returnGrid:
return (self._vmomentsurfacemassHierarchicalGrid(n,m,
grido),
grido)
else:
return self._vmomentsurfacemassHierarchicalGrid(n,m,grido)
#Calculate the initdf moment and then calculate the ratio
initvmoment= self._initdf.vmomentsurfacemass(R,n,m,nsigma=nsigma,
phi=phi)
if initvmoment == 0.: initvmoment= 1.
norm= sigmaR1**(n+1)*sigmaT1**(m+1)*initvmoment
if isinstance(t,(list,numpy.ndarray)):
raise IOError("list of times is only supported with grid-based calculation")
return dblquad(_vmomentsurfaceIntegrand,
meanvT/sigmaT1-nsigma,
meanvT/sigmaT1+nsigma,
lambda x: meanvR/sigmaR1
-numpy.sqrt(nsigma**2.-(x-meanvT/sigmaT1)**2.),
lambda x: meanvR/sigmaR1
+numpy.sqrt(nsigma**2.-(x-meanvT/sigmaT1)**2.),
(R,az,self,n,m,sigmaR1,sigmaT1,t,initvmoment),
epsrel=epsrel,epsabs=epsabs)[0]*norm
[docs] @potential_physical_input
@physical_conversion('angle',pop=True)
def vertexdev(self,R,t=0.,nsigma=None,deg=False,
epsrel=1.e-02,epsabs=1.e-05,phi=0.,
grid=None,gridpoints=101,returnGrid=False,
sigmaR2=None,sigmaT2=None,sigmaRT=None,surfacemass=None,
hierarchgrid=False,nlevels=2,
integrate_method='dopr54_c'):
"""
NAME:
vertexdev
PURPOSE:
calculate the vertex deviation of the velocity distribution at (R,phi)
INPUT:
R - radius at which to calculate the moment (can be Quantity)
phi= azimuth (rad unless deg=True; can be Quantity)
t= time at which to evaluate the DF (can be a list or ndarray; if this is the case, list needs to be in descending order and equally spaced) (can be Quantity)
sigmaR2, sigmaT2, sigmaRT= if set the vertex deviation is simply calculated using these
nsigma - number of sigma to integrate the velocities over (based on an estimate, so be generous)
deg= azimuth is in degree (default=False); do not set this when giving phi as a Quantity
epsrel, epsabs - scipy.integrate keywords (the integration calculates the ratio of this vmoment to that of the initial DF)
grid= if set to True, build a grid and use that to evaluate integrals; if set to a grid-objects (such as returned by this procedure), use this grid
gridpoints= number of points to use for the grid in 1D (default=101)
returnGrid= if True, return the grid object (default=False)
hierarchgrid= if True, use a hierarchical grid (default=False)
nlevels= number of hierarchical levels for the hierarchical grid
integrate_method= orbit.integrate method argument
OUTPUT:
vertex deviation in rad
HISTORY:
2011-03-31 - Written - Bovy (NYU)
"""
#The following aren't actually the moments, but they are the moments
#times the surface-mass density; that drops out
if isinstance(grid,evolveddiskdfGrid) or \
isinstance(grid,evolveddiskdfHierarchicalGrid):
grido= grid
elif (sigmaR2 is None or sigmaT2 is None or sigmaRT is None) \
and isinstance(grid,bool) and grid:
#Precalculate the grid
(sigmaR2_tmp,grido)= self.vmomentsurfacemass(R,2,0,deg=deg,t=t,
nsigma=nsigma,phi=phi,
epsrel=epsrel,
epsabs=epsabs,grid=grid,
gridpoints=gridpoints,
returnGrid=True,
hierarchgrid=hierarchgrid,
nlevels=nlevels,
integrate_method=integrate_method)
else:
grido= False
if sigmaR2 is None:
sigmaR2= self.sigmaR2(R,deg=deg,t=t,phi=phi,
nsigma=nsigma,epsrel=epsrel,
epsabs=epsabs,grid=grido,
gridpoints=gridpoints,
returnGrid=False,
hierarchgrid=hierarchgrid,
nlevels=nlevels,
integrate_method=integrate_method,
use_physical=False)
if sigmaT2 is None:
sigmaT2= self.sigmaT2(R,deg=deg,t=t,phi=phi,
nsigma=nsigma,epsrel=epsrel,
epsabs=epsabs,grid=grido,
gridpoints=gridpoints,
returnGrid=False,
hierarchgrid=hierarchgrid,
nlevels=nlevels,
integrate_method=integrate_method,
use_physical=False)
if sigmaRT is None:
sigmaRT= self.sigmaRT(R,deg=deg,t=t,phi=phi,
nsigma=nsigma,epsrel=epsrel,
epsabs=epsabs,grid=grido,
gridpoints=gridpoints,
returnGrid=False,
hierarchgrid=hierarchgrid,
nlevels=nlevels,
integrate_method=integrate_method,
use_physical=False)
warnings.warn("In versions >1.3, the output unit of evolveddiskdf.vertexdev has been changed to radian (from degree before)",galpyWarning)
if returnGrid and ((isinstance(grid,bool) and grid) or
isinstance(grid,evolveddiskdfGrid) or
isinstance(grid,evolveddiskdfHierarchicalGrid)):
return (-numpy.arctan(2.*sigmaRT/(sigmaR2-sigmaT2))/2.,grido)
else:
return -numpy.arctan(2.*sigmaRT/(sigmaR2-sigmaT2))/2.
[docs] @potential_physical_input
@physical_conversion('velocity',pop=True)
def meanvR(self,R,t=0.,nsigma=None,deg=False,phi=0.,
epsrel=1.e-02,epsabs=1.e-05,
grid=None,gridpoints=101,returnGrid=False,
surfacemass=None,
hierarchgrid=False,nlevels=2,integrate_method='dopr54_c'):
"""
NAME:
meanvR
PURPOSE:
calculate the mean vR of the velocity distribution at (R,phi)
INPUT:
R - radius at which to calculate the moment(/ro) (can be Quantity)
phi= azimuth (rad unless deg=True; can be Quantity)
t= time at which to evaluate the DF (can be a list or ndarray; if this is the case, list needs to be in descending order and equally spaced) (can be Quantity)
surfacemass= if set use this pre-calculated surfacemass
nsigma - number of sigma to integrate the velocities over (based on an estimate, so be generous)
deg= azimuth is in degree (default=False); do not set this when giving phi as a Quantity
epsrel, epsabs - scipy.integrate keywords (the integration calculates the ratio of this vmoment to that of the initial DF)
grid= if set to True, build a grid and use that to evaluate integrals; if set to a grid-objects (such as returned by this procedure), use this grid
gridpoints= number of points to use for the grid in 1D (default=101)
returnGrid= if True, return the grid object (default=False)
hierarchgrid= if True, use a hierarchical grid (default=False)
nlevels= number of hierarchical levels for the hierarchical grid
integrate_method= orbit.integrate method argument
OUTPUT:
mean vR
HISTORY:
2011-03-31 - Written - Bovy (NYU)
"""
if isinstance(grid,evolveddiskdfGrid) or \
isinstance(grid,evolveddiskdfHierarchicalGrid):
grido= grid
vmomentR= self.vmomentsurfacemass(R,1,0,deg=deg,t=t,phi=phi,
nsigma=nsigma,
epsrel=epsrel,
epsabs=epsabs,grid=grid,
gridpoints=gridpoints,
returnGrid=False,
hierarchgrid=hierarchgrid,
nlevels=nlevels,
integrate_method=integrate_method)
elif isinstance(grid,bool) and grid:
#Precalculate the grid
(vmomentR,grido)= self.vmomentsurfacemass(R,1,0,deg=deg,t=t,
nsigma=nsigma,phi=phi,
epsrel=epsrel,
epsabs=epsabs,grid=grid,
gridpoints=gridpoints,
returnGrid=True,
hierarchgrid=hierarchgrid,
nlevels=nlevels,
integrate_method=integrate_method)
else:
grido= False
vmomentR= self.vmomentsurfacemass(R,1,0,deg=deg,t=t,phi=phi,
nsigma=nsigma,
epsrel=epsrel,
epsabs=epsabs,grid=grid,
gridpoints=gridpoints,
returnGrid=False,
hierarchgrid=hierarchgrid,
nlevels=nlevels,integrate_method=integrate_method)
if surfacemass is None:
surfacemass= self.vmomentsurfacemass(R,0,0,deg=deg,t=t,phi=phi,
nsigma=nsigma,epsrel=epsrel,
epsabs=epsabs,grid=grido,
gridpoints=gridpoints,
returnGrid=False,
hierarchgrid=hierarchgrid,
nlevels=nlevels,integrate_method=integrate_method)
out= vmomentR/surfacemass
if returnGrid and ((isinstance(grid,bool) and grid) or
isinstance(grid,evolveddiskdfGrid) or
isinstance(grid,evolveddiskdfHierarchicalGrid)):
return (out,grido)
else:
return out
[docs] @potential_physical_input
@physical_conversion('velocity',pop=True)
def meanvT(self,R,t=0.,nsigma=None,deg=False,phi=0.,
epsrel=1.e-02,epsabs=1.e-05,
grid=None,gridpoints=101,returnGrid=False,
surfacemass=None,
hierarchgrid=False,nlevels=2,integrate_method='dopr54_c'):
"""
NAME:
meanvT
PURPOSE:
calculate the mean vT of the velocity distribution at (R,phi)
INPUT:
R - radius at which to calculate the moment (can be Quantity)
phi= azimuth (rad unless deg=True; can be Quantity)
t= time at which to evaluate the DF (can be a list or ndarray; if this is the case, list needs to be in descending order and equally spaced) (can be Quantity)
surfacemass= if set use this pre-calculated surfacemass
nsigma - number of sigma to integrate the velocities over (based on an estimate, so be generous)
deg= azimuth is in degree (default=False); do not set this when giving phi as a Quantity
epsrel, epsabs - scipy.integrate keywords (the integration calculates the ratio of this vmoment to that of the initial DF)
grid= if set to True, build a grid and use that to evaluate integrals; if set to a grid-objects (such as returned by this procedure), use this grid
gridpoints= number of points to use for the grid in 1D (default=101)
returnGrid= if True, return the grid object (default=False)
hierarchgrid= if True, use a hierarchical grid (default=False)
nlevels= number of hierarchical levels for the hierarchical grid
integrate_method= orbit.integrate method argument
OUTPUT:
mean vT
HISTORY:
2011-03-31 - Written - Bovy (NYU)
"""
if isinstance(grid,evolveddiskdfGrid) or \
isinstance(grid,evolveddiskdfHierarchicalGrid):
grido= grid
vmomentT= self.vmomentsurfacemass(R,0,1,deg=deg,t=t,
nsigma=nsigma,phi=phi,
epsrel=epsrel,
epsabs=epsabs,grid=grid,
gridpoints=gridpoints,
returnGrid=False,
hierarchgrid=hierarchgrid,
nlevels=nlevels,
integrate_method=integrate_method)
elif isinstance(grid,bool) and grid:
#Precalculate the grid
(vmomentT,grido)= self.vmomentsurfacemass(R,0,1,deg=deg,t=t,
nsigma=nsigma,phi=phi,
epsrel=epsrel,
epsabs=epsabs,grid=grid,
gridpoints=gridpoints,
returnGrid=True,
hierarchgrid=hierarchgrid,
nlevels=nlevels,
integrate_method=integrate_method)
else:
grido= False
vmomentT= self.vmomentsurfacemass(R,0,1,deg=deg,t=t,
nsigma=nsigma,phi=phi,
epsrel=epsrel,
epsabs=epsabs,grid=grid,
gridpoints=gridpoints,
returnGrid=False,
hierarchgrid=hierarchgrid,
nlevels=nlevels,integrate_method=integrate_method)
if surfacemass is None:
surfacemass= self.vmomentsurfacemass(R,0,0,deg=deg,t=t,phi=phi,
nsigma=nsigma,epsrel=epsrel,
epsabs=epsabs,grid=grido,
gridpoints=gridpoints,
returnGrid=False,
hierarchgrid=hierarchgrid,
nlevels=nlevels,
integrate_method=integrate_method)
out= vmomentT/surfacemass
if returnGrid and ((isinstance(grid,bool) and grid) or
isinstance(grid,evolveddiskdfGrid) or
isinstance(grid,evolveddiskdfHierarchicalGrid)):
return (out,grido)
else:
return out
[docs] @potential_physical_input
@physical_conversion('velocity2',pop=True)
def sigmaR2(self,R,t=0.,nsigma=None,deg=False,phi=0.,
epsrel=1.e-02,epsabs=1.e-05,
grid=None,gridpoints=101,returnGrid=False,
surfacemass=None,meanvR=None,
hierarchgrid=False,nlevels=2,
integrate_method='dopr54_c'):
"""
NAME:
sigmaR2
PURPOSE:
calculate the radial variance of the velocity distribution at (R,phi)
INPUT:
R - radius at which to calculate the moment (can be Quantity)
phi= azimuth (rad unless deg=True; can be Quantity)
t= time at which to evaluate the DF (can be a list or ndarray; if this is the case, list needs to be in descending order and equally spaced) (can be Quantity)
surfacemass, meanvR= if set use this pre-calculated surfacemass and mean vR
nsigma - number of sigma to integrate the velocities over (based on an estimate, so be generous)
deg= azimuth is in degree (default=False); do not set this when giving phi as a Quantity
epsrel, epsabs - scipy.integrate keywords (the integration calculates the ratio of this vmoment to that of the initial DF)
grid= if set to True, build a grid and use that to evaluate integrals; if set to a grid-objects (such as returned by this procedure), use this grid
gridpoints= number of points to use for the grid in 1D (default=101)
returnGrid= if True, return the grid object (default=False)
hierarchgrid= if True, use a hierarchical grid (default=False)
nlevels= number of hierarchical levels for the hierarchical grid
integrate_method= orbit.integrate method argument
OUTPUT:
variance of vR
HISTORY:
2011-03-31 - Written - Bovy (NYU)
"""
#The following aren't actually the moments, but they are the moments
#times the surface-mass density
if isinstance(grid,evolveddiskdfGrid) or \
isinstance(grid,evolveddiskdfHierarchicalGrid):
grido= grid
sigmaR2= self.vmomentsurfacemass(R,2,0,deg=deg,t=t,phi=phi,
nsigma=nsigma,
epsrel=epsrel,
epsabs=epsabs,grid=grido,
gridpoints=gridpoints,
returnGrid=False,
hierarchgrid=hierarchgrid,
nlevels=nlevels,
integrate_method=integrate_method)
elif (meanvR is None or surfacemass is None ) \
and isinstance(grid,bool) and grid:
#Precalculate the grid
(sigmaR2,grido)= self.vmomentsurfacemass(R,2,0,deg=deg,t=t,
nsigma=nsigma,phi=phi,
epsrel=epsrel,
epsabs=epsabs,grid=grid,
gridpoints=gridpoints,
returnGrid=True,
hierarchgrid=hierarchgrid,
nlevels=nlevels,
integrate_method=integrate_method)
else:
grido= False
sigmaR2= self.vmomentsurfacemass(R,2,0,deg=deg,t=t,
nsigma=nsigma,phi=phi,
epsrel=epsrel,
epsabs=epsabs,grid=grido,
gridpoints=gridpoints,
returnGrid=False,
hierarchgrid=hierarchgrid,
nlevels=nlevels,
integrate_method=integrate_method)
if surfacemass is None:
surfacemass= self.vmomentsurfacemass(R,0,0,deg=deg,t=t,phi=phi,
nsigma=nsigma,epsrel=epsrel,
epsabs=epsabs,grid=grido,
gridpoints=gridpoints,
returnGrid=False,
hierarchgrid=hierarchgrid,
nlevels=nlevels,
integrate_method=integrate_method)
if meanvR is None:
meanvR= self.vmomentsurfacemass(R,1,0,deg=deg,t=t,phi=phi,
nsigma=nsigma,epsrel=epsrel,
epsabs=epsabs,grid=grido,
gridpoints=gridpoints,
returnGrid=False,
hierarchgrid=hierarchgrid,
nlevels=nlevels,
integrate_method=integrate_method)/surfacemass
out= sigmaR2/surfacemass-meanvR**2.
if returnGrid and ((isinstance(grid,bool) and grid) or
isinstance(grid,evolveddiskdfGrid) or
isinstance(grid,evolveddiskdfHierarchicalGrid)):
return (out,grido)
else:
return out
[docs] @potential_physical_input
@physical_conversion('velocity2',pop=True)
def sigmaT2(self,R,t=0.,nsigma=None,deg=False,phi=0.,
epsrel=1.e-02,epsabs=1.e-05,
grid=None,gridpoints=101,returnGrid=False,
surfacemass=None,meanvT=None,
hierarchgrid=False,nlevels=2,
integrate_method='dopr54_c'):
"""
NAME:
sigmaT2
PURPOSE:
calculate the tangential variance of the velocity distribution at (R,phi)
INPUT:
R - radius at which to calculate the moment (can be Quantity)
phi= azimuth (rad unless deg=True; can be Quantity)
t= time at which to evaluate the DF (can be a list or ndarray; if this is the case, list needs to be in descending order and equally spaced) (can be Quantity)
surfacemass, meanvT= if set use this pre-calculated surfacemass and mean tangential velocity
nsigma - number of sigma to integrate the velocities over (based on an estimate, so be generous)
deg= azimuth is in degree (default=False); do not set this when giving phi as a Quantity
epsrel, epsabs - scipy.integrate keywords (the integration calculates the ratio of this vmoment to that of the initial DF)
grid= if set to True, build a grid and use that to evaluate integrals; if set to a grid-objects (such as returned by this procedure), use this grid
gridpoints= number of points to use for the grid in 1D (default=101)
returnGrid= if True, return the grid object (default=False)
hierarchgrid= if True, use a hierarchical grid (default=False)
nlevels= number of hierarchical levels for the hierarchical grid
integrate_method= orbit.integrate method argument
OUTPUT:
variance of vT
HISTORY:
2011-03-31 - Written - Bovy (NYU)
"""
if isinstance(grid,evolveddiskdfGrid) or \
isinstance(grid,evolveddiskdfHierarchicalGrid):
grido= grid
sigmaT2= self.vmomentsurfacemass(R,0,2,deg=deg,t=t,phi=phi,
nsigma=nsigma,
epsrel=epsrel,
epsabs=epsabs,grid=grido,
gridpoints=gridpoints,
returnGrid=False,
hierarchgrid=hierarchgrid,
nlevels=nlevels,
integrate_method=integrate_method)
elif (meanvT is None or surfacemass is None ) \
and isinstance(grid,bool) and grid:
#Precalculate the grid
(sigmaT2,grido)= self.vmomentsurfacemass(R,0,2,deg=deg,t=t,
nsigma=nsigma,phi=phi,
epsrel=epsrel,
epsabs=epsabs,grid=grid,
gridpoints=gridpoints,
returnGrid=True,
hierarchgrid=hierarchgrid,
nlevels=nlevels,
integrate_method=integrate_method)
else:
grido= False
sigmaT2= self.vmomentsurfacemass(R,0,2,deg=deg,t=t,
nsigma=nsigma,phi=phi,
epsrel=epsrel,
epsabs=epsabs,grid=grido,
gridpoints=gridpoints,
returnGrid=False,
hierarchgrid=hierarchgrid,
nlevels=nlevels,
integrate_method=integrate_method)
if surfacemass is None:
surfacemass= self.vmomentsurfacemass(R,0,0,deg=deg,t=t,phi=phi,
nsigma=nsigma,epsrel=epsrel,
epsabs=epsabs,grid=grido,
gridpoints=gridpoints,
returnGrid=False,
hierarchgrid=hierarchgrid,
nlevels=nlevels,
integrate_method=integrate_method)
if meanvT is None:
meanvT= self.vmomentsurfacemass(R,0,1,deg=deg,t=t,phi=phi,
nsigma=nsigma,epsrel=epsrel,
epsabs=epsabs,grid=grido,
gridpoints=gridpoints,
returnGrid=False,
hierarchgrid=hierarchgrid,
nlevels=nlevels,
integrate_method=integrate_method)/surfacemass
out= sigmaT2/surfacemass-meanvT**2.
if returnGrid and ((isinstance(grid,bool) and grid) or
isinstance(grid,evolveddiskdfGrid) or
isinstance(grid,evolveddiskdfHierarchicalGrid)):
return (out,grido)
else:
return out
[docs] @potential_physical_input
@physical_conversion('velocity2',pop=True)
def sigmaRT(self,R,t=0.,nsigma=None,deg=False,
epsrel=1.e-02,epsabs=1.e-05,phi=0.,
grid=None,gridpoints=101,returnGrid=False,
surfacemass=None,meanvR=None,meanvT=None,
hierarchgrid=False,nlevels=2,
integrate_method='dopr54_c'):
"""
NAME:
sigmaRT
PURPOSE:
calculate the radial-tangential co-variance of the velocity distribution at (R,phi)
INPUT:
R - radius at which to calculate the moment (can be Quantity)
phi= azimuth (rad unless deg=True; can be Quantity)
t= time at which to evaluate the DF (can be a list or ndarray; if this is the case, list needs to be in descending order and equally spaced) (can be Quantity)
surfacemass, meanvR, meavT= if set use this pre-calculated surfacemass and mean vR and vT
nsigma - number of sigma to integrate the velocities over (based on an estimate, so be generous)
deg= azimuth is in degree (default=False); do not set this when giving phi as a Quantity
epsrel, epsabs - scipy.integrate keywords (the integration calculates the ration of this vmoment to that of the initial DF)
grid= if set to True, build a grid and use that to evaluate integrals; if set to a grid-objects (such as returned by this procedure), use this grid
gridpoints= number of points to use for the grid in 1D (default=101)
returnGrid= if True, return the grid object (default=False)
hierarchgrid= if True, use a hierarchical grid (default=False)
nlevels= number of hierarchical levels for the hierarchical grid
integrate_method= orbit.integrate method argument
OUTPUT:
covariance of vR and vT
HISTORY:
2011-03-31 - Written - Bovy (NYU)
"""
#The following aren't actually the moments, but they are the moments
#times the surface-mass density
if isinstance(grid,evolveddiskdfGrid) or \
isinstance(grid,evolveddiskdfHierarchicalGrid):
grido= grid
sigmaRT= self.vmomentsurfacemass(R,1,1,deg=deg,t=t,phi=phi,
nsigma=nsigma,
epsrel=epsrel,
epsabs=epsabs,grid=grido,
gridpoints=gridpoints,
returnGrid=False,
hierarchgrid=hierarchgrid,
nlevels=nlevels,
integrate_method=integrate_method)
elif (meanvR is None or surfacemass is None ) \
and isinstance(grid,bool) and grid:
#Precalculate the grid
(sigmaRT,grido)= self.vmomentsurfacemass(R,1,1,deg=deg,t=t,
nsigma=nsigma,phi=phi,
epsrel=epsrel,
epsabs=epsabs,grid=grid,
gridpoints=gridpoints,
returnGrid=True,
hierarchgrid=hierarchgrid,
nlevels=nlevels,
integrate_method=integrate_method)
else:
grido= False
sigmaRT= self.vmomentsurfacemass(R,1,1,deg=deg,t=t,
nsigma=nsigma,phi=phi,
epsrel=epsrel,
epsabs=epsabs,grid=grido,
gridpoints=gridpoints,
returnGrid=False,
hierarchgrid=hierarchgrid,
nlevels=nlevels,
integrate_method=integrate_method)
if surfacemass is None:
surfacemass= self.vmomentsurfacemass(R,0,0,deg=deg,t=t,phi=phi,
nsigma=nsigma,epsrel=epsrel,
epsabs=epsabs,grid=grido,
gridpoints=gridpoints,
returnGrid=False,
hierarchgrid=hierarchgrid,
nlevels=nlevels,
integrate_method=integrate_method)
if meanvR is None:
meanvR= self.vmomentsurfacemass(R,1,0,deg=deg,t=t,phi=phi,
nsigma=nsigma,epsrel=epsrel,
epsabs=epsabs,grid=grido,
gridpoints=gridpoints,
returnGrid=False,
hierarchgrid=hierarchgrid,
nlevels=nlevels,
integrate_method=integrate_method)/surfacemass
if meanvT is None:
meanvT= self.vmomentsurfacemass(R,0,1,deg=deg,t=t,phi=phi,
nsigma=nsigma,epsrel=epsrel,
epsabs=epsabs,grid=grido,
gridpoints=gridpoints,
returnGrid=False,
hierarchgrid=hierarchgrid,
nlevels=nlevels,
integrate_method=integrate_method)/surfacemass
out= sigmaRT/surfacemass-meanvR*meanvT
if returnGrid and ((isinstance(grid,bool) and grid) or
isinstance(grid,evolveddiskdfGrid) or
isinstance(grid,evolveddiskdfHierarchicalGrid)):
return (out,grido)
else:
return out
[docs] @potential_physical_input
@physical_conversion('frequency-kmskpc',pop=True)
def oortA(self,R,t=0.,nsigma=None,deg=False,phi=0.,
epsrel=1.e-02,epsabs=1.e-05,
grid=None,gridpoints=101,returnGrids=False,
derivRGrid=None,derivphiGrid=None,derivGridpoints=101,
derivHierarchgrid=False,
hierarchgrid=False,nlevels=2,integrate_method='dopr54_c'):
"""
NAME:
oortA
PURPOSE:
calculate the Oort function A at (R,phi,t)
INPUT:
R - radius at which to calculate A (can be Quantity)
phi= azimuth (rad unless deg=True; can be Quantity
t= time at which to evaluate the DF (can be a list or ndarray; if this is the case, list needs to be in descending order and equally spaced) (can be Quantity)
nsigma - number of sigma to integrate the velocities over (based on an estimate, so be generous)
deg= azimuth is in degree (default=False); do not set this when giving phi as a Quantity
epsrel, epsabs - scipy.integrate keywords
grid= if set to True, build a grid and use that to evaluate integrals; if set to a grid-objects (such as returned by this procedure), use this grid
derivRGrid, derivphiGrid= if set to True, build a grid and use that to evaluate integrals of the derivatives of the DF;if set to a grid-objects (such as returned by this procedure), use this grid
gridpoints= number of points to use for the grid in 1D (default=101)
derivGridpoints= number of points to use for the grid in 1D (default=101)
returnGrid= if True, return the grid objects (default=False)
hierarchgrid= if True, use a hierarchical grid (default=False)
derivHierarchgrid= if True, use a hierarchical grid (default=False)
nlevels= number of hierarchical levels for the hierarchical grid
integrate_method= orbit.integrate method argument
OUTPUT:
Oort A at R,phi,t
HISTORY:
2011-10-16 - Written - Bovy (NYU)
"""
#First calculate the grids if they are not given
if isinstance(grid,bool) and grid:
(surfacemass,grid)= self.vmomentsurfacemass(R,0,0,deg=deg,t=t,phi=phi,
nsigma=nsigma,epsrel=epsrel,
epsabs=epsabs,grid=True,
gridpoints=gridpoints,
returnGrid=True,
hierarchgrid=hierarchgrid,
nlevels=nlevels,
integrate_method=integrate_method)
elif isinstance(grid,evolveddiskdfGrid) or \
isinstance(grid,evolveddiskdfHierarchicalGrid):
surfacemass= self.vmomentsurfacemass(R,0,0,deg=deg,t=t,phi=phi,
nsigma=nsigma,epsrel=epsrel,
epsabs=epsabs,grid=grid,
gridpoints=gridpoints,
returnGrid=False,
hierarchgrid=hierarchgrid,
nlevels=nlevels,
integrate_method=integrate_method)
if isinstance(derivRGrid,bool) and derivRGrid:
(dsurfacemassdR,derivRGrid)= self.vmomentsurfacemass(R,0,0,deg=deg,t=t,phi=phi,
nsigma=nsigma,epsrel=epsrel,
epsabs=epsabs,grid=True,
gridpoints=derivGridpoints,
returnGrid=True,
hierarchgrid=derivHierarchgrid,
nlevels=nlevels,
integrate_method=integrate_method,deriv='R')
elif isinstance(derivRGrid,evolveddiskdfGrid) or \
isinstance(derivRGrid,evolveddiskdfHierarchicalGrid):
dsurfacemassdR= self.vmomentsurfacemass(R,0,0,deg=deg,t=t,phi=phi,
nsigma=nsigma,epsrel=epsrel,
epsabs=epsabs,grid=derivRGrid,
gridpoints=derivGridpoints,
returnGrid=False,
hierarchgrid=derivHierarchgrid,
nlevels=nlevels,
integrate_method=integrate_method,deriv='R')
if isinstance(derivphiGrid,bool) and derivphiGrid:
(dsurfacemassdphi,derivphiGrid)= self.vmomentsurfacemass(R,0,0,deg=deg,t=t,phi=phi,
nsigma=nsigma,epsrel=epsrel,
epsabs=epsabs,grid=True,
gridpoints=derivGridpoints,
returnGrid=True,
hierarchgrid=derivHierarchgrid,
nlevels=nlevels,
integrate_method=integrate_method,deriv='phi')
elif isinstance(derivphiGrid,evolveddiskdfGrid) or \
isinstance(derivphiGrid,evolveddiskdfHierarchicalGrid):
dsurfacemassdphi= self.vmomentsurfacemass(R,0,0,deg=deg,t=t,phi=phi,
nsigma=nsigma,epsrel=epsrel,
epsabs=epsabs,grid=derivphiGrid,
gridpoints=derivGridpoints,
returnGrid=False,
hierarchgrid=derivHierarchgrid,
nlevels=nlevels,
integrate_method=integrate_method,deriv='phi')
#2A= meanvT/R-dmeanvR/R/dphi-dmeanvphi/dR
#meanvT
meanvT= self.meanvT(R,t=t,nsigma=nsigma,deg=deg,phi=phi,
epsrel=epsrel,epsabs=epsabs,
grid=grid,gridpoints=gridpoints,returnGrid=False,
surfacemass=surfacemass,
hierarchgrid=hierarchgrid,
nlevels=nlevels,integrate_method=integrate_method,
use_physical=False)
dmeanvRdphi= (self.vmomentsurfacemass(R,1,0,deg=deg,t=t,phi=phi,
nsigma=nsigma,epsrel=epsrel,
epsabs=epsabs,grid=derivphiGrid,
gridpoints=derivGridpoints,
returnGrid=False,
hierarchgrid=derivHierarchgrid,
nlevels=nlevels,
integrate_method=integrate_method,deriv='phi')
/surfacemass
-self.vmomentsurfacemass(R,1,0,deg=deg,t=t,phi=phi,
nsigma=nsigma,epsrel=epsrel,
epsabs=epsabs,grid=grid,
gridpoints=gridpoints,
returnGrid=False,
hierarchgrid=hierarchgrid,
nlevels=nlevels,
integrate_method=integrate_method)
/surfacemass**2.*dsurfacemassdphi)
dmeanvTdR= (self.vmomentsurfacemass(R,0,1,deg=deg,t=t,phi=phi,
nsigma=nsigma,epsrel=epsrel,
epsabs=epsabs,grid=derivRGrid,
gridpoints=derivGridpoints,
returnGrid=False,
hierarchgrid=derivHierarchgrid,
nlevels=nlevels,
integrate_method=integrate_method,deriv='R')
/surfacemass
-self.vmomentsurfacemass(R,0,1,deg=deg,t=t,phi=phi,
nsigma=nsigma,epsrel=epsrel,
epsabs=epsabs,grid=grid,
gridpoints=gridpoints,
returnGrid=False,
hierarchgrid=hierarchgrid,
nlevels=nlevels,
integrate_method=integrate_method)
/surfacemass**2.*dsurfacemassdR)
if returnGrids:
return (0.5*(meanvT/R-dmeanvRdphi/R-dmeanvTdR),grid,
derivRGrid,derivphiGrid)
else:
return 0.5*(meanvT/R-dmeanvRdphi/R-dmeanvTdR)
[docs] @potential_physical_input
@physical_conversion('frequency-kmskpc',pop=True)
def oortB(self,R,t=0.,nsigma=None,deg=False,phi=0.,
epsrel=1.e-02,epsabs=1.e-05,
grid=None,gridpoints=101,returnGrids=False,
derivRGrid=None,derivphiGrid=None,derivGridpoints=101,
derivHierarchgrid=False,
hierarchgrid=False,nlevels=2,integrate_method='dopr54_c'):
"""
NAME:
oortB
PURPOSE:
calculate the Oort function B at (R,phi,t)
INPUT:
R - radius at which to calculate B (can be Quantity)
phi= azimuth (rad unless deg=True; can be Quantity)
t= time at which to evaluate the DF (can be a list or ndarray; if this is the case, list needs to be in descending order and equally spaced) (can be Quantity)
nsigma - number of sigma to integrate the velocities over (based on an estimate, so be generous)
deg= azimuth is in degree (default=False); do not set this when giving phi as a Quantity
epsrel, epsabs - scipy.integrate keywords
grid= if set to True, build a grid and use that to evaluate integrals; if set to a grid-objects (such as returned by this procedure), use this grid
derivRGrid, derivphiGrid= if set to True, build a grid and use that to evaluat integrals of the derivatives of the DF: if set to a grid-objects (such as returned by this procedure), use this grid
gridpoints= number of points to use for the grid in 1D (default=101)
derivGridpoints= number of points to use for the grid in 1D (default=101)
returnGrid= if True, return the grid objects (default=False)
hierarchgrid= if True, use a hierarchical grid (default=False)
derivHierarchgrid= if True, use a hierarchical grid (default=False)
nlevels= number of hierarchical levels for the hierarchical grid
integrate_method= orbit.integrate method argument
OUTPUT:
Oort B at R,phi,t
HISTORY:
2011-10-16 - Written - Bovy (NYU)
"""
#First calculate the grids if they are not given
if isinstance(grid,bool) and grid:
(surfacemass,grid)= self.vmomentsurfacemass(R,0,0,deg=deg,t=t,phi=phi,
nsigma=nsigma,epsrel=epsrel,
epsabs=epsabs,grid=True,
gridpoints=gridpoints,
returnGrid=True,
hierarchgrid=hierarchgrid,
nlevels=nlevels,
integrate_method=integrate_method)
elif isinstance(grid,evolveddiskdfGrid) or \
isinstance(grid,evolveddiskdfHierarchicalGrid):
surfacemass= self.vmomentsurfacemass(R,0,0,deg=deg,t=t,phi=phi,
nsigma=nsigma,epsrel=epsrel,
epsabs=epsabs,grid=grid,
gridpoints=gridpoints,
returnGrid=False,
hierarchgrid=hierarchgrid,
nlevels=nlevels,
integrate_method=integrate_method)
if isinstance(derivRGrid,bool) and derivRGrid:
(dsurfacemassdR,derivRGrid)= self.vmomentsurfacemass(R,0,0,deg=deg,t=t,phi=phi,
nsigma=nsigma,epsrel=epsrel,
epsabs=epsabs,grid=True,
gridpoints=derivGridpoints,
returnGrid=True,
hierarchgrid=derivHierarchgrid,
nlevels=nlevels,
integrate_method=integrate_method,deriv='R')
elif isinstance(derivRGrid,evolveddiskdfGrid) or \
isinstance(derivRGrid,evolveddiskdfHierarchicalGrid):
dsurfacemassdR= self.vmomentsurfacemass(R,0,0,deg=deg,t=t,phi=phi,
nsigma=nsigma,epsrel=epsrel,
epsabs=epsabs,grid=derivRGrid,
gridpoints=derivGridpoints,
returnGrid=False,
hierarchgrid=derivHierarchgrid,
nlevels=nlevels,
integrate_method=integrate_method,deriv='R')
if isinstance(derivphiGrid,bool) and derivphiGrid:
(dsurfacemassdphi,derivphiGrid)= self.vmomentsurfacemass(R,0,0,deg=deg,t=t,phi=phi,
nsigma=nsigma,epsrel=epsrel,
epsabs=epsabs,grid=True,
gridpoints=derivGridpoints,
returnGrid=True,
hierarchgrid=derivHierarchgrid,
nlevels=nlevels,
integrate_method=integrate_method,deriv='phi')
elif isinstance(derivphiGrid,evolveddiskdfGrid) or \
isinstance(derivphiGrid,evolveddiskdfHierarchicalGrid):
dsurfacemassdphi= self.vmomentsurfacemass(R,0,0,deg=deg,t=t,phi=phi,
nsigma=nsigma,epsrel=epsrel,
epsabs=epsabs,grid=derivphiGrid,
gridpoints=derivGridpoints,
returnGrid=False,
hierarchgrid=derivHierarchgrid,
nlevels=nlevels,
integrate_method=integrate_method,deriv='phi')
#2B= -meanvT/R+dmeanvR/R/dphi-dmeanvphi/dR
#meanvT
meanvT= self.meanvT(R,t=t,nsigma=nsigma,deg=deg,phi=phi,
epsrel=epsrel,epsabs=epsabs,
grid=grid,gridpoints=gridpoints,returnGrid=False,
surfacemass=surfacemass,
hierarchgrid=hierarchgrid,
nlevels=nlevels,integrate_method=integrate_method,
use_physical=False)
dmeanvRdphi= (self.vmomentsurfacemass(R,1,0,deg=deg,t=t,phi=phi,
nsigma=nsigma,epsrel=epsrel,
epsabs=epsabs,grid=derivphiGrid,
gridpoints=derivGridpoints,
returnGrid=False,
hierarchgrid=derivHierarchgrid,
nlevels=nlevels,
integrate_method=integrate_method,deriv='phi')
/surfacemass
-self.vmomentsurfacemass(R,1,0,deg=deg,t=t,phi=phi,
nsigma=nsigma,epsrel=epsrel,
epsabs=epsabs,grid=grid,
gridpoints=gridpoints,
returnGrid=False,
hierarchgrid=hierarchgrid,
nlevels=nlevels,
integrate_method=integrate_method)
/surfacemass**2.*dsurfacemassdphi)
dmeanvTdR= (self.vmomentsurfacemass(R,0,1,deg=deg,t=t,phi=phi,
nsigma=nsigma,epsrel=epsrel,
epsabs=epsabs,grid=derivRGrid,
gridpoints=derivGridpoints,
returnGrid=False,
hierarchgrid=derivHierarchgrid,
nlevels=nlevels,
integrate_method=integrate_method,deriv='R')
/surfacemass
-self.vmomentsurfacemass(R,0,1,deg=deg,t=t,phi=phi,
nsigma=nsigma,epsrel=epsrel,
epsabs=epsabs,grid=grid,
gridpoints=gridpoints,
returnGrid=False,
hierarchgrid=hierarchgrid,
nlevels=nlevels,
integrate_method=integrate_method)
/surfacemass**2.*dsurfacemassdR)
if returnGrids:
return (0.5*(-meanvT/R+dmeanvRdphi/R-dmeanvTdR),grid,
derivRGrid,derivphiGrid)
else:
return 0.5*(-meanvT/R+dmeanvRdphi/R-dmeanvTdR)
[docs] @potential_physical_input
@physical_conversion('frequency-kmskpc',pop=True)
def oortC(self,R,t=0.,nsigma=None,deg=False,phi=0.,
epsrel=1.e-02,epsabs=1.e-05,
grid=None,gridpoints=101,returnGrids=False,
derivRGrid=None,derivphiGrid=None,derivGridpoints=101,
derivHierarchgrid=False,
hierarchgrid=False,nlevels=2,integrate_method='dopr54_c'):
"""
NAME:
oortC
PURPOSE:
calculate the Oort function C at (R,phi,t)
INPUT:
R - radius at which to calculate C (can be Quantity)
phi= azimuth (rad unless deg=True; can be Quantity)
t= time at which to evaluate the DF (can be a list or ndarray; if this is the case, list needs to be in descending order and equally spaced) (can be Quantity)
nsigma - number of sigma to integrate the velocities over (based on an estimate, so be generous)
deg= azimuth is in degree (default=False); do not set this when giving phi as a Quantity
epsrel, epsabs - scipy.integrate keywords
grid= if set to True, build a grid and use that to evaluate integrals; if set to a grid-objects (such as returned by this procedure), use this grid
derivRGrid, derivphiGrid= if set to True, build a grid and use that to evaluate integrals of the derivatives of the DF; if set to a grid-objects (such as returned by this procedure), use this grid
gridpoints= number of points to use for the grid in 1D (default=101)
derivGridpoints= number of points to use for the grid in 1D (default=101)
returnGrid= if True, return the grid objects (default=False)
hierarchgrid= if True, use a hierarchical grid (default=False)
derivHierarchgrid= if True, use a hierarchical grid (default=False)
nlevels= number of hierarchical levels for the hierarchical grid
integrate_method= orbit.integrate method argument
OUTPUT:
Oort C at R,phi,t
HISTORY:
2011-10-16 - Written - Bovy (NYU)
"""
#First calculate the grids if they are not given
if isinstance(grid,bool) and grid:
(surfacemass,grid)= self.vmomentsurfacemass(R,0,0,deg=deg,t=t,phi=phi,
nsigma=nsigma,epsrel=epsrel,
epsabs=epsabs,grid=True,
gridpoints=gridpoints,
returnGrid=True,
hierarchgrid=hierarchgrid,
nlevels=nlevels,
integrate_method=integrate_method)
elif isinstance(grid,evolveddiskdfGrid) or \
isinstance(grid,evolveddiskdfHierarchicalGrid):
surfacemass= self.vmomentsurfacemass(R,0,0,deg=deg,t=t,phi=phi,
nsigma=nsigma,epsrel=epsrel,
epsabs=epsabs,grid=grid,
gridpoints=gridpoints,
returnGrid=False,
hierarchgrid=hierarchgrid,
nlevels=nlevels,
integrate_method=integrate_method)
if isinstance(derivRGrid,bool) and derivRGrid:
(dsurfacemassdR,derivRGrid)= self.vmomentsurfacemass(R,0,0,deg=deg,t=t,phi=phi,
nsigma=nsigma,epsrel=epsrel,
epsabs=epsabs,grid=True,
gridpoints=derivGridpoints,
returnGrid=True,
hierarchgrid=derivHierarchgrid,
nlevels=nlevels,
integrate_method=integrate_method,deriv='R')
elif isinstance(derivRGrid,evolveddiskdfGrid) or \
isinstance(derivRGrid,evolveddiskdfHierarchicalGrid):
dsurfacemassdR= self.vmomentsurfacemass(R,0,0,deg=deg,t=t,phi=phi,
nsigma=nsigma,epsrel=epsrel,
epsabs=epsabs,grid=derivRGrid,
gridpoints=derivGridpoints,
returnGrid=False,
hierarchgrid=derivHierarchgrid,
nlevels=nlevels,
integrate_method=integrate_method,deriv='R')
if isinstance(derivphiGrid,bool) and derivphiGrid:
(dsurfacemassdphi,derivphiGrid)= self.vmomentsurfacemass(R,0,0,deg=deg,t=t,phi=phi,
nsigma=nsigma,epsrel=epsrel,
epsabs=epsabs,grid=True,
gridpoints=derivGridpoints,
returnGrid=True,
hierarchgrid=derivHierarchgrid,
nlevels=nlevels,
integrate_method=integrate_method,deriv='phi')
elif isinstance(derivphiGrid,evolveddiskdfGrid) or \
isinstance(derivphiGrid,evolveddiskdfHierarchicalGrid):
dsurfacemassdphi= self.vmomentsurfacemass(R,0,0,deg=deg,t=t,phi=phi,
nsigma=nsigma,epsrel=epsrel,
epsabs=epsabs,grid=derivphiGrid,
gridpoints=derivGridpoints,
returnGrid=False,
hierarchgrid=derivHierarchgrid,
nlevels=nlevels,
integrate_method=integrate_method,deriv='phi')
#2C= -meanvR/R-dmeanvT/R/dphi+dmeanvR/dR
#meanvR
meanvR= self.meanvR(R,t=t,nsigma=nsigma,deg=deg,phi=phi,
epsrel=epsrel,epsabs=epsabs,
grid=grid,gridpoints=gridpoints,returnGrid=False,
surfacemass=surfacemass,
hierarchgrid=hierarchgrid,
nlevels=nlevels,integrate_method=integrate_method,
use_physical=False)
dmeanvTdphi= (self.vmomentsurfacemass(R,0,1,deg=deg,t=t,phi=phi,
nsigma=nsigma,epsrel=epsrel,
epsabs=epsabs,grid=derivphiGrid,
gridpoints=derivGridpoints,
returnGrid=False,
hierarchgrid=derivHierarchgrid,
nlevels=nlevels,
integrate_method=integrate_method,deriv='phi')
/surfacemass
-self.vmomentsurfacemass(R,0,1,deg=deg,t=t,phi=phi,
nsigma=nsigma,epsrel=epsrel,
epsabs=epsabs,grid=grid,
gridpoints=gridpoints,
returnGrid=False,
hierarchgrid=hierarchgrid,
nlevels=nlevels,
integrate_method=integrate_method)
/surfacemass**2.*dsurfacemassdphi)
dmeanvRdR= (self.vmomentsurfacemass(R,1,0,deg=deg,t=t,phi=phi,
nsigma=nsigma,epsrel=epsrel,
epsabs=epsabs,grid=derivRGrid,
gridpoints=derivGridpoints,
returnGrid=False,
hierarchgrid=derivHierarchgrid,
nlevels=nlevels,
integrate_method=integrate_method,deriv='R')
/surfacemass
-self.vmomentsurfacemass(R,1,0,deg=deg,t=t,phi=phi,
nsigma=nsigma,epsrel=epsrel,
epsabs=epsabs,grid=grid,
gridpoints=gridpoints,
returnGrid=False,
hierarchgrid=hierarchgrid,
nlevels=nlevels,
integrate_method=integrate_method)
/surfacemass**2.*dsurfacemassdR)
if returnGrids:
return (0.5*(-meanvR/R-dmeanvTdphi/R+dmeanvRdR),grid,
derivRGrid,derivphiGrid)
else:
return 0.5*(-meanvR/R-dmeanvTdphi/R+dmeanvRdR)
[docs] @potential_physical_input
@physical_conversion('frequency-kmskpc',pop=True)
def oortK(self,R,t=0.,nsigma=None,deg=False,phi=0.,
epsrel=1.e-02,epsabs=1.e-05,
grid=None,gridpoints=101,returnGrids=False,
derivRGrid=None,derivphiGrid=None,derivGridpoints=101,
derivHierarchgrid=False,
hierarchgrid=False,nlevels=2,integrate_method='dopr54_c'):
"""
NAME:
oortK
PURPOSE:
calculate the Oort function K at (R,phi,t)
INPUT:
R - radius at which to calculate K (can be Quantity)
phi= azimuth (rad unless deg=True; can be Quantity)
t= time at which to evaluate the DF (can be a list or ndarray; if this is the case, list needs to be in descending order and equally spaced) (can be Quantity)
nsigma - number of sigma to integrate the velocities over (based on an estimate, so be generous)
deg= azimuth is in degree (default=False); do not set this when giving phi as a Quantity
epsrel, epsabs - scipy.integrate keywords
grid= if set to True, build a grid and use that to evaluate integrals; if set to a grid-objects (such as returned by this procedure), use this grid
derivRGrid, derivphiGrid= if set to True, build a grid and use that to evaluate integrals of the derivatives of the DF; if set to a grid-objects (such as returned by this procedure), use this grid
gridpoints= number of points to use for the grid in 1D (default=101)
derivGridpoints= number of points to use for the grid in 1D (default=101)
returnGrid= if True, return the grid objects (default=False)
hierarchgrid= if True, use a hierarchical grid (default=False)
derivHierarchgrid= if True, use a hierarchical grid (default=False)
nlevels= number of hierarchical levels for the hierarchical grid
integrate_method= orbit.integrate method argument
OUTPUT:
Oort K at R,phi,t
HISTORY:
2011-10-16 - Written - Bovy (NYU)
"""
#First calculate the grids if they are not given
if isinstance(grid,bool) and grid:
(surfacemass,grid)= self.vmomentsurfacemass(R,0,0,deg=deg,t=t,phi=phi,
nsigma=nsigma,epsrel=epsrel,
epsabs=epsabs,grid=True,
gridpoints=gridpoints,
returnGrid=True,
hierarchgrid=hierarchgrid,
nlevels=nlevels,
integrate_method=integrate_method)
elif isinstance(grid,evolveddiskdfGrid) or \
isinstance(grid,evolveddiskdfHierarchicalGrid):
surfacemass= self.vmomentsurfacemass(R,0,0,deg=deg,t=t,phi=phi,
nsigma=nsigma,epsrel=epsrel,
epsabs=epsabs,grid=grid,
gridpoints=gridpoints,
returnGrid=False,
hierarchgrid=hierarchgrid,
nlevels=nlevels,
integrate_method=integrate_method)
if isinstance(derivRGrid,bool) and derivRGrid:
(dsurfacemassdR,derivRGrid)= self.vmomentsurfacemass(R,0,0,deg=deg,t=t,phi=phi,
nsigma=nsigma,epsrel=epsrel,
epsabs=epsabs,grid=True,
gridpoints=derivGridpoints,
returnGrid=True,
hierarchgrid=derivHierarchgrid,
nlevels=nlevels,
integrate_method=integrate_method,deriv='R')
elif isinstance(derivRGrid,evolveddiskdfGrid) or \
isinstance(derivRGrid,evolveddiskdfHierarchicalGrid):
dsurfacemassdR= self.vmomentsurfacemass(R,0,0,deg=deg,t=t,phi=phi,
nsigma=nsigma,epsrel=epsrel,
epsabs=epsabs,grid=derivRGrid,
gridpoints=derivGridpoints,
returnGrid=False,
hierarchgrid=derivHierarchgrid,
nlevels=nlevels,
integrate_method=integrate_method,deriv='R')
if isinstance(derivphiGrid,bool) and derivphiGrid:
(dsurfacemassdphi,derivphiGrid)= self.vmomentsurfacemass(R,0,0,deg=deg,t=t,phi=phi,
nsigma=nsigma,epsrel=epsrel,
epsabs=epsabs,grid=True,
gridpoints=derivGridpoints,
returnGrid=True,
hierarchgrid=derivHierarchgrid,
nlevels=nlevels,
integrate_method=integrate_method,deriv='phi')
elif isinstance(derivphiGrid,evolveddiskdfGrid) or \
isinstance(derivphiGrid,evolveddiskdfHierarchicalGrid):
dsurfacemassdphi= self.vmomentsurfacemass(R,0,0,deg=deg,t=t,phi=phi,
nsigma=nsigma,epsrel=epsrel,
epsabs=epsabs,grid=derivphiGrid,
gridpoints=derivGridpoints,
returnGrid=False,
hierarchgrid=derivHierarchgrid,
nlevels=nlevels,
integrate_method=integrate_method,deriv='phi')
#2K= meanvR/R+dmeanvT/R/dphi+dmeanvR/dR
#meanvR
meanvR= self.meanvR(R,t=t,nsigma=nsigma,deg=deg,phi=phi,
epsrel=epsrel,epsabs=epsabs,
grid=grid,gridpoints=gridpoints,returnGrid=False,
surfacemass=surfacemass,
hierarchgrid=hierarchgrid,
nlevels=nlevels,integrate_method=integrate_method,
use_physical=False)
dmeanvTdphi= (self.vmomentsurfacemass(R,0,1,deg=deg,t=t,phi=phi,
nsigma=nsigma,epsrel=epsrel,
epsabs=epsabs,grid=derivphiGrid,
gridpoints=derivGridpoints,
returnGrid=False,
hierarchgrid=derivHierarchgrid,
nlevels=nlevels,
integrate_method=integrate_method,deriv='phi')
/surfacemass
-self.vmomentsurfacemass(R,0,1,deg=deg,t=t,phi=phi,
nsigma=nsigma,epsrel=epsrel,
epsabs=epsabs,grid=grid,
gridpoints=gridpoints,
returnGrid=False,
hierarchgrid=hierarchgrid,
nlevels=nlevels,
integrate_method=integrate_method)
/surfacemass**2.*dsurfacemassdphi)
dmeanvRdR= (self.vmomentsurfacemass(R,1,0,deg=deg,t=t,phi=phi,
nsigma=nsigma,epsrel=epsrel,
epsabs=epsabs,grid=derivRGrid,
gridpoints=derivGridpoints,
returnGrid=False,
hierarchgrid=derivHierarchgrid,
nlevels=nlevels,
integrate_method=integrate_method,deriv='R')
/surfacemass
-self.vmomentsurfacemass(R,1,0,deg=deg,t=t,phi=phi,
nsigma=nsigma,epsrel=epsrel,
epsabs=epsabs,grid=grid,
gridpoints=gridpoints,
returnGrid=False,
hierarchgrid=hierarchgrid,
nlevels=nlevels,
integrate_method=integrate_method)
/surfacemass**2.*dsurfacemassdR)
if returnGrids:
return (0.5*(meanvR/R+dmeanvTdphi/R+dmeanvRdR),grid,
derivRGrid,derivphiGrid)
else:
return 0.5*(meanvR/R+dmeanvTdphi/R+dmeanvRdR)
def _vmomentsurfacemassGrid(self,n,m,grid):
"""Internal function to evaluate vmomentsurfacemass using a grid
rather than direct integration"""
if len(grid.df.shape) == 3: tlist= True
else: tlist= False
if tlist:
nt= grid.df.shape[2]
out= []
for ii in range(nt):
out.append(numpy.dot(grid.vRgrid**n,numpy.dot(grid.df[:,:,ii],grid.vTgrid**m))*\
(grid.vRgrid[1]-grid.vRgrid[0])*(grid.vTgrid[1]-grid.vTgrid[0]))
return numpy.array(out)
else:
return numpy.dot(grid.vRgrid**n,numpy.dot(grid.df,grid.vTgrid**m))*\
(grid.vRgrid[1]-grid.vRgrid[0])*(grid.vTgrid[1]-grid.vTgrid[0])
def _buildvgrid(self,R,phi,nsigma,t,sigmaR1,sigmaT1,meanvR,meanvT,
gridpoints,print_progress,integrate_method,deriv):
"""Internal function to grid the vDF at a given location"""
out= evolveddiskdfGrid()
out.sigmaR1= sigmaR1
out.sigmaT1= sigmaT1
out.meanvR= meanvR
out.meanvT= meanvT
out.vRgrid= numpy.linspace(meanvR-nsigma*sigmaR1,meanvR+nsigma*sigmaR1,
gridpoints)
out.vTgrid= numpy.linspace(meanvT-nsigma*sigmaT1,meanvT+nsigma*sigmaT1,
gridpoints)
if isinstance(t,(list,numpy.ndarray)):
nt= len(t)
out.df= numpy.zeros((gridpoints,gridpoints,nt))
for ii in range(gridpoints):
for jj in range(gridpoints-1,-1,-1):#Reverse, so we get the peak before we get to the extreme lags NOT NECESSARY
if print_progress: #pragma: no cover
sys.stdout.write('\r'+"Velocity gridpoint %i out of %i" % \
(jj+ii*gridpoints+1,gridpoints*gridpoints))
sys.stdout.flush()
thiso= Orbit([R,out.vRgrid[ii],out.vTgrid[jj],phi])
out.df[ii,jj,:]= self(thiso,numpy.array(t).flatten(),
integrate_method=integrate_method,
deriv=deriv,use_physical=False)
out.df[ii,jj,numpy.isnan(out.df[ii,jj,:])]= 0. #BOVY: for now
if print_progress: sys.stdout.write('\n') #pragma: no cover
else:
out.df= numpy.zeros((gridpoints,gridpoints))
for ii in range(gridpoints):
for jj in range(gridpoints):
if print_progress: #pragma: no cover
sys.stdout.write('\r'+"Velocity gridpoint %i out of %i" % \
(jj+ii*gridpoints+1,gridpoints*gridpoints))
sys.stdout.flush()
thiso= Orbit([R,out.vRgrid[ii],out.vTgrid[jj],phi])
out.df[ii,jj]= self(thiso,t,
integrate_method=integrate_method,
deriv=deriv,use_physical=False)
if numpy.isnan(out.df[ii,jj]): out.df[ii,jj]= 0. #BOVY: for now
if print_progress: sys.stdout.write('\n') #pragma: no cover
return out
def _create_ts_tlist(self,t,integrate_method):
#Check input
if not all(t == sorted(t,reverse=True)): raise IOError("List of times has to be sorted in descending order")
#Initialize
if integrate_method == 'odeint':
_NTS= 1000
tmax= numpy.amax(t)
ts= numpy.linspace(tmax,self._to,_NTS)
#Add other t
ts= list(ts)
ts.extend([self._to+tmax-ti for ti in t if ti != tmax])
else:
if len(t) == 1: #Special case this because it is confusing
ts= numpy.array([t[0],self._to])
else:
ts= -t+self._to+numpy.amax(t)
#sort
ts= list(ts)
ts.sort(reverse=True)
return numpy.array(ts)
def _call_marginalizevperp(self,o,integrate_method='dopr54_c',**kwargs):
"""Call the DF, marginalizing over perpendicular velocity"""
#Get d, l, vlos
l= o.ll(obs=[1.,0.,0.],ro=1.)*_DEGTORAD
vlos= o.vlos(ro=1.,vo=1.,obs=[1.,0.,0.,0.,0.,0.])
R= o.R(use_physical=False)
phi= o.phi(use_physical=False)
#Get local circular velocity, projected onto the los
if isinstance(self._pot,list):
vcirc= calcRotcurve([p for p in self._pot if not p.isNonAxi],R)[0]
else:
vcirc= calcRotcurve(self._pot,R)[0]
vcirclos= vcirc*numpy.sin(phi+l)
#Marginalize
alphalos= phi+l
if not 'nsigma' in kwargs or ('nsigma' in kwargs and \
kwargs['nsigma'] is None):
nsigma= _NSIGMA
else:
nsigma= kwargs['nsigma']
kwargs.pop('nsigma',None)
#BOVY: add asymmetric drift here?
if numpy.fabs(numpy.sin(alphalos)) < numpy.sqrt(1./2.):
sigmaR1= numpy.sqrt(self._initdf.sigmaT2(R,phi=phi,
use_physical=False)) #Slight abuse
cosalphalos= numpy.cos(alphalos)
tanalphalos= numpy.tan(alphalos)
return integrate.quad(_marginalizeVperpIntegrandSinAlphaSmall,
-nsigma,nsigma,
args=(self,R,cosalphalos,tanalphalos,
vlos-vcirclos,vcirc,
sigmaR1,phi),
**kwargs)[0]/numpy.fabs(cosalphalos)*sigmaR1
else:
sigmaR1= numpy.sqrt(self._initdf.sigmaR2(R,phi=phi,
use_physical=False))
sinalphalos= numpy.sin(alphalos)
cotalphalos= 1./numpy.tan(alphalos)
return integrate.quad(_marginalizeVperpIntegrandSinAlphaLarge,
-nsigma,nsigma,
args=(self,R,sinalphalos,cotalphalos,
vlos-vcirclos,vcirc,sigmaR1,phi),
**kwargs)[0]/numpy.fabs(sinalphalos)*sigmaR1
def _call_marginalizevlos(self,o,integrate_method='dopr54_c',**kwargs):
"""Call the DF, marginalizing over line-of-sight velocity"""
#Get d, l, vperp
l= o.ll(obs=[1.,0.,0.],ro=1.)*_DEGTORAD
vperp= o.vll(ro=1.,vo=1.,obs=[1.,0.,0.,0.,0.,0.])
R= o.R(use_physical=False)
phi= o.phi(use_physical=False)
#Get local circular velocity, projected onto the perpendicular
#direction
if isinstance(self._pot,list):
vcirc= calcRotcurve([p for p in self._pot if not p.isNonAxi],R)[0]
else:
vcirc= calcRotcurve(self._pot,R)[0]
vcircperp= vcirc*numpy.cos(phi+l)
#Marginalize
alphaperp= numpy.pi/2.+phi+l
if not 'nsigma' in kwargs or ('nsigma' in kwargs and \
kwargs['nsigma'] is None):
nsigma= _NSIGMA
else:
nsigma= kwargs['nsigma']
kwargs.pop('nsigma',None)
if numpy.fabs(numpy.sin(alphaperp)) < numpy.sqrt(1./2.):
sigmaR1= numpy.sqrt(self._initdf.sigmaT2(R,phi=phi,
use_physical=False)) #slight abuse
va= vcirc-self._initdf.meanvT(R,phi=phi,use_physical=False)
cosalphaperp= numpy.cos(alphaperp)
tanalphaperp= numpy.tan(alphaperp)
#we can reuse the VperpIntegrand, since it is just another angle
return integrate.quad(_marginalizeVperpIntegrandSinAlphaSmall,
-va/sigmaR1-nsigma,
-va/sigmaR1+nsigma,
args=(self,R,cosalphaperp,tanalphaperp,
vperp-vcircperp,vcirc,
sigmaR1,phi),
**kwargs)[0]/numpy.fabs(cosalphaperp)*sigmaR1
else:
sigmaR1= numpy.sqrt(self._initdf.sigmaR2(R,phi=phi,
use_physical=False))
sinalphaperp= numpy.sin(alphaperp)
cotalphaperp= 1./numpy.tan(alphaperp)
#we can reuse the VperpIntegrand, since it is just another angle
return integrate.quad(_marginalizeVperpIntegrandSinAlphaLarge,
-nsigma,nsigma,
args=(self,R,sinalphaperp,cotalphaperp,
vperp-vcircperp,vcirc,sigmaR1,phi),
**kwargs)[0]/numpy.fabs(sinalphaperp)*sigmaR1
def _vmomentsurfacemassHierarchicalGrid(self,n,m,grid):
"""Internal function to evaluate vmomentsurfacemass using a
hierarchical grid rather than direct integration,
rather unnecessary"""
return grid(n,m)
class evolveddiskdfGrid(object):
"""(not quite) Empty class since it is only used to store some stuff"""
def __init__(self):
return None
def plot(self,tt=0):
"""
NAME:
plot
PURPOSE:
plot the velocity distribution
INPUT:
t= optional time index
OUTPUT:
plot of velocity distribution to output device
HISTORY:
2011-06-27 - Written - Bovy (NYU)
"""
xrange= [self.vRgrid[0],self.vRgrid[len(self.vRgrid)-1]]
yrange= [self.vTgrid[0],self.vTgrid[len(self.vTgrid)-1]]
if len(self.df.shape) == 3:
plotthis= self.df[:,:,tt]
else:
plotthis= self.df
plot.dens2d(plotthis.T,cmap='gist_yarg',origin='lower',
aspect=(xrange[1]-xrange[0])/\
(yrange[1]-yrange[0]),
extent=[xrange[0],xrange[1],
yrange[0],yrange[1]],
xlabel=r'$v_R / v_0$',
ylabel=r'$v_T / v_0$')
class evolveddiskdfHierarchicalGrid(object):
"""Class that holds a hierarchical velocity grid"""
def __init__(self,edf,R,phi,nsigma,t,sigmaR1,sigmaT1,meanvR,meanvT,
gridpoints,nlevels,deriv,upperdxdy=None,print_progress=False,
nlevelsTotal=None):
"""
NAME:
__init__
PURPOSE:
Initialize a hierarchical grid
INPUT:
edf - evolveddiskdf instance
R - Radius
phi- azimuth
nsigma - number of sigma to integrate over
t- time
sigmaR1 - radial dispersion
sigmaT1 - tangential dispersion
meanvR - mean of radial velocity
meanvT - mean of tangential velocity
gridpoints- number of gridpoints
nlevels- number of levels to build
deriv- None, 'R', or 'phi': calculates derivative of the moment wrt
R or phi
upperdxdy= area element of previous hierarchical level
print_progress= if True, print progress on building the grid
OUTPUT:
object
HISTORY:
2011-04-21 - Written - Bovy (NYU)
"""
self.sigmaR1= sigmaR1
self.sigmaT1= sigmaT1
self.meanvR= meanvR
self.meanvT= meanvT
self.gridpoints= gridpoints
self.vRgrid= numpy.linspace(self.meanvR-nsigma*self.sigmaR1,
self.meanvR+nsigma*self.sigmaR1,
self.gridpoints)
self.vTgrid= numpy.linspace(self.meanvT-nsigma*self.sigmaT1,
self.meanvT+nsigma*self.sigmaT1,
self.gridpoints)
self.t= t
if nlevelsTotal is None:
nlevelsTotal= nlevels
self.nlevels= nlevels
self.nlevelsTotal= nlevelsTotal
if isinstance(t,(list,numpy.ndarray)):
nt= len(t)
self.df= numpy.zeros((gridpoints,gridpoints,nt))
dxdy= (self.vRgrid[1]-self.vRgrid[0])\
*(self.vTgrid[1]-self.vTgrid[0])
if nlevels > 0:
xsubmin= int(gridpoints)//4
xsubmax= gridpoints-int(gridpoints)//4
else:
xsubmin= gridpoints
xsubmax= 0
ysubmin, ysubmax= xsubmin, xsubmax
for ii in range(gridpoints):
for jj in range(gridpoints):
if print_progress: #pragma: no cover
sys.stdout.write('\r'+"Velocity gridpoint %i out of %i" % \
(jj+ii*gridpoints+1,gridpoints*gridpoints))
sys.stdout.flush()
#If this is part of a subgrid, ignore
if nlevels > 1 and ii >= xsubmin and ii < xsubmax \
and jj >= ysubmin and jj < ysubmax:
continue
thiso= Orbit([R,self.vRgrid[ii],self.vTgrid[jj],phi])
self.df[ii,jj,:]= edf(thiso,numpy.array(t).flatten(),
deriv=deriv)
self.df[ii,jj,numpy.isnan(self.df[ii,jj,:])]= 0.#BOVY: for now
#Multiply in area, somewhat tricky for edge objects
if upperdxdy is None or (ii != 0 and ii != gridpoints-1\
and jj != 0
and jj != gridpoints-1):
self.df[ii,jj,:]*= dxdy
elif ((ii == 0 or ii == gridpoints-1) and \
(jj != 0 and jj != gridpoints-1))\
or \
((jj == 0 or jj == gridpoints-1) and \
(ii != 0 and ii != gridpoints-1)): #edge
self.df[ii,jj,:]*= 1.5*dxdy/1.5 #turn this off for now
else: #corner
self.df[ii,jj,:]*= 2.25*dxdy/2.25 #turn this off for now
if print_progress: sys.stdout.write('\n') #pragma: no cover
else:
self.df= numpy.zeros((gridpoints,gridpoints))
dxdy= (self.vRgrid[1]-self.vRgrid[0])\
*(self.vTgrid[1]-self.vTgrid[0])
if nlevels > 0:
xsubmin= int(gridpoints)//4
xsubmax= gridpoints-int(gridpoints)//4
else:
xsubmin= gridpoints
xsubmax= 0
ysubmin, ysubmax= xsubmin, xsubmax
for ii in range(gridpoints):
for jj in range(gridpoints):
if print_progress: #pragma: no cover
sys.stdout.write('\r'+"Velocity gridpoint %i out of %i" % \
(jj+ii*gridpoints+1,gridpoints*gridpoints))
sys.stdout.flush()
#If this is part of a subgrid, ignore
if nlevels > 1 and ii >= xsubmin and ii < xsubmax \
and jj >= ysubmin and jj < ysubmax:
continue
thiso= Orbit([R,self.vRgrid[ii],self.vTgrid[jj],phi])
self.df[ii,jj]= edf(thiso,t,deriv=deriv)
if numpy.isnan(self.df[ii,jj]): self.df[ii,jj]= 0. #BOVY: for now
#Multiply in area, somewhat tricky for edge objects
if upperdxdy is None or (ii != 0 and ii != gridpoints-1\
and jj != 0
and jj != gridpoints-1):
self.df[ii,jj]*= dxdy
elif ((ii == 0 or ii == gridpoints-1) and \
(jj != 0 and jj != gridpoints-1))\
or \
((jj == 0 or jj == gridpoints-1) and \
(ii != 0 and ii != gridpoints-1)): #edge
self.df[ii,jj]*= 1.5*dxdy/1.5#turn this off for now
else: #corner
self.df[ii,jj]*= 2.25*dxdy/2.25#turn this off for now
if print_progress: sys.stdout.write('\n') #pragma: no cover
if nlevels > 1:
#Set up subgrid
subnsigma= (self.meanvR-self.vRgrid[xsubmin])/self.sigmaR1
self.subgrid= evolveddiskdfHierarchicalGrid(edf,R,phi,
subnsigma,t,
sigmaR1,
sigmaT1,
meanvR,
meanvT,
gridpoints,
nlevels-1,
deriv,
upperdxdy=dxdy,
print_progress=print_progress,
nlevelsTotal=nlevelsTotal)
else:
self.subgrid= None
return None
def __call__(self,n,m):
"""Call"""
if isinstance(self.t,(list,numpy.ndarray)): tlist= True
else: tlist= False
if tlist:
nt= self.df.shape[2]
out= []
for ii in range(nt):
#We already multiplied in the area
out.append(numpy.dot(self.vRgrid**n,numpy.dot(self.df[:,:,ii],
self.vTgrid**m)))
if self.subgrid is None: return numpy.array(out)
else: return numpy.array(out)+ self.subgrid(n,m)
else:
#We already multiplied in the area
thislevel= numpy.dot(self.vRgrid**n,numpy.dot(self.df,self.vTgrid**m))
if self.subgrid is None: return thislevel
else: return thislevel+self.subgrid(n,m)
def plot(self,tt=0,vmax=None,aspect=None,extent=None):
"""
NAME:
plot
PURPOSE:
plot the velocity distribution
INPUT:
t= optional time index
OUTPUT:
plot of velocity distribution to output device
HISTORY:
2011-06-27 - Written - Bovy (NYU)
"""
if vmax is None:
vmax= self.max(tt=tt)*2.
#Figure out how big of a grid we need
dvR= (self.vRgrid[1]-self.vRgrid[0])
dvT= (self.vTgrid[1]-self.vTgrid[0])
nvR= len(self.vRgrid)
nvT= len(self.vTgrid)
nUpperLevels= self.nlevelsTotal-self.nlevels
nvRTot= nvR*2**nUpperLevels
nvTTot= nvT*2**nUpperLevels
plotthis= numpy.zeros((nvRTot,nvTTot))
if len(self.df.shape) == 3:
plotdf= copy.copy(self.df[:,:,tt])
else:
plotdf= copy.copy(self.df)
plotdf[(plotdf == 0.)]= _NAN
#Fill up the grid
if nUpperLevels > 0:
xsubmin= nvRTot//2-nvR//2-1
xsubmax= nvRTot//2+nvR//2
ysubmin= nvTTot//2-nvT//2-1
ysubmax= nvTTot//2+nvT//2
#Set outside this subgrid to NaN
plotthis[:,:]= _NAN #within the grid gets filled in below
else:
xsubmin= 0
xsubmax= nvR
ysubmin= 0
ysubmax= nvT
#Fill in this level
plotthis[xsubmin:xsubmax,ysubmin:ysubmax]= plotdf/dvR/dvT/nvR/nvT
#Plot
if nUpperLevels == 0:
xrange= [self.vRgrid[0]+dvR/2.,self.vRgrid[len(self.vRgrid)-1]-dvR/2.]
yrange= [self.vTgrid[0]+dvT/2.,self.vTgrid[len(self.vTgrid)-1]-dvT/2.]
aspect=(xrange[1]-xrange[0])/\
(yrange[1]-yrange[0])
extent=[xrange[0],xrange[1],
yrange[0],yrange[1]]
plot.dens2d(plotthis.T,cmap='gist_yarg',origin='lower',
interpolation='nearest',
aspect=aspect,
extent=extent,
xlabel=r'$v_R / v_0$',
ylabel=r'$v_T / v_0$',
vmin=0.,vmax=vmax)
else:
plot.dens2d(plotthis.T,cmap='gist_yarg',origin='lower',
aspect=aspect,extent=extent,
interpolation='nearest',
overplot=True,vmin=0.,vmax=vmax)
if not self.subgrid is None:
self.subgrid.plot(tt=tt,vmax=vmax,aspect=aspect,extent=extent)
def max(self,tt=0):
if not self.subgrid is None:
if len(self.df.shape) == 3:
return numpy.amax([numpy.amax(self.df[:,:,tt]),
self.subgrid.max(tt)])
else:
return numpy.amax([numpy.amax(self.df[:,:]),
self.subgrid.max()])
else:
if len(self.df.shape) == 3:
return numpy.amax(self.df[:,:,tt])
else:
return numpy.amax(self.df[:,:])
def _vmomentsurfaceIntegrand(vR,vT,R,az,df,n,m,sigmaR1,sigmaT1,t,initvmoment):
"""Internal function that is the integrand for the velocity moment times
surface mass integration"""
o= Orbit([R,vR*sigmaR1,vT*sigmaT1,az])
return vR**n*vT**m*df(o,t)/initvmoment
def _marginalizeVperpIntegrandSinAlphaLarge(vR,df,R,sinalpha,cotalpha,
vlos,vcirc,sigma,phi):
return df(Orbit([R,vR*sigma,cotalpha*vR*sigma+vlos/sinalpha+vcirc,phi]))
def _marginalizeVperpIntegrandSinAlphaSmall(vT,df,R,cosalpha,tanalpha,
vlos,vcirc,sigma,phi):
return df(Orbit([R,tanalpha*vT*sigma-vlos/cosalpha,vT*sigma+vcirc,phi]))