Source code for galpy.actionAngle.actionAngleAdiabaticGrid

###############################################################################
#   actionAngle: a Python module to calculate  actions, angles, and frequencies
#
#      class: actionAngleAdiabaticGrid
#
#             build grid in integrals of motion to quickly evaluate 
#             actionAngleAdiabatic
#
#      methods:
#             __call__: returns (jr,lz,jz)
#
###############################################################################
from __future__ import print_function
import numpy
from scipy import interpolate
from .actionAngleAdiabatic import actionAngleAdiabatic
from .actionAngle import actionAngle, UnboundError
from .. import potential
from ..potential.Potential import _evaluatePotentials
from ..potential.Potential import flatten as flatten_potential
from ..util import multi
_PRINTOUTSIDEGRID= False
[docs]class actionAngleAdiabaticGrid(actionAngle): """Action-angle formalism for axisymmetric potentials using the adiabatic approximation, grid-based interpolation"""
[docs] def __init__(self,pot=None,zmax=1.,gamma=1.,Rmax=5., nR=16,nEz=16,nEr=31,nLz=31,numcores=1, **kwargs): """ NAME: __init__ PURPOSE: initialize an actionAngleAdiabaticGrid object INPUT: pot= potential or list of potentials zmax= zmax for building Ez grid Rmax = Rmax for building grids gamma= (default=1.) replace Lz by Lz+gamma Jz in effective potential nEz=, nEr=, nLz, nR= grid size numcores= number of cpus to use to parallellize c= if True, use C to calculate actions ro= distance from vantage point to GC (kpc; can be Quantity) vo= circular velocity at ro (km/s; can be Quantity) +scipy.integrate.quad keywords OUTPUT: instance HISTORY: 2012-07-27 - Written - Bovy (IAS@MPIA) """ actionAngle.__init__(self, ro=kwargs.get('ro',None),vo=kwargs.get('vo',None)) if pot is None: #pragma: no cover raise IOError("Must specify pot= for actionAngleAxi") self._c= kwargs.pop('c',False) self._gamma= gamma self._pot= flatten_potential(pot) self._zmax= zmax self._Rmax= Rmax self._Rmin= 0.01 #Set up the actionAngleAdiabatic object that we will use to interpolate self._aA= actionAngleAdiabatic(pot=self._pot,gamma=self._gamma, c=self._c) #Build grid for Ez, first calculate Ez(zmax;R) function self._Rs= numpy.linspace(self._Rmin,self._Rmax,nR) self._EzZmaxs= _evaluatePotentials(self._pot,self._Rs, self._zmax*numpy.ones(nR))\ -_evaluatePotentials(self._pot,self._Rs,numpy.zeros(nR)) self._EzZmaxsInterp= interpolate.InterpolatedUnivariateSpline(self._Rs,numpy.log(self._EzZmaxs),k=3) y= numpy.linspace(0.,1.,nEz) jz= numpy.zeros((nR,nEz)) jzEzzmax= numpy.zeros(nR) thisRs= (numpy.tile(self._Rs,(nEz,1)).T).flatten() thisEzZmaxs= (numpy.tile(self._EzZmaxs,(nEz,1)).T).flatten() thisy= (numpy.tile(y,(nR,1))).flatten() if self._c: jz= self._aA(thisRs, numpy.zeros(len(thisRs)), numpy.ones(len(thisRs)),#these two r dummies numpy.zeros(len(thisRs)), numpy.sqrt(2.*thisy*thisEzZmaxs), **kwargs)[2] jz= numpy.reshape(jz,(nR,nEz)) jzEzzmax[0:nR]= jz[:,nEz-1] else: if numcores > 1: jz= multi.parallel_map((lambda x: self._aA(thisRs[x],0.,1.,#these two r dummies 0.,numpy.sqrt(2.*thisy[x]*thisEzZmaxs[x]), _justjz=True, **kwargs)[2]), range(nR*nEz),numcores=numcores) jz= numpy.reshape(jz,(nR,nEz)) jzEzzmax[0:nR]= jz[:,nEz-1] else: for ii in range(nR): for jj in range(nEz): #Calculate Jz jz[ii,jj]= self._aA(self._Rs[ii],0.,1.,#these two r dummies 0.,numpy.sqrt(2.*y[jj]*self._EzZmaxs[ii]), _justjz=True,**kwargs)[2] if jj == nEz-1: jzEzzmax[ii]= jz[ii,jj] for ii in range(nR): jz[ii,:]/= jzEzzmax[ii] #First interpolate Ez=Ezmax self._jzEzmaxInterp= interpolate.InterpolatedUnivariateSpline(self._Rs,numpy.log(jzEzzmax+10.**-5.),k=3) self._jz= jz self._jzInterp= interpolate.RectBivariateSpline(self._Rs, y, jz, kx=3,ky=3,s=0.) #JR grid self._Lzmin= 0.01 self._Lzs= numpy.linspace(self._Lzmin, self._Rmax\ *potential.vcirc(self._pot,self._Rmax), nLz) self._Lzmax= self._Lzs[-1] #Calculate ER(vr=0,R=RL) self._RL= numpy.array([potential.rl(self._pot,l) for l in self._Lzs]) self._RLInterp= interpolate.InterpolatedUnivariateSpline(self._Lzs, self._RL,k=3) self._ERRL= _evaluatePotentials(self._pot,self._RL,numpy.zeros(nLz)) +self._Lzs**2./2./self._RL**2. self._ERRLmax= numpy.amax(self._ERRL)+1. self._ERRLInterp= interpolate.InterpolatedUnivariateSpline(self._Lzs, numpy.log(-(self._ERRL-self._ERRLmax)),k=3) self._Ramax= 99. self._ERRa= _evaluatePotentials(self._pot,self._Ramax,0.) +self._Lzs**2./2./self._Ramax**2. self._ERRamax= numpy.amax(self._ERRa)+1. self._ERRaInterp= interpolate.InterpolatedUnivariateSpline(self._Lzs, numpy.log(-(self._ERRa-self._ERRamax)),k=3) y= numpy.linspace(0.,1.,nEr) jr= numpy.zeros((nLz,nEr)) jrERRa= numpy.zeros(nLz) thisRL= (numpy.tile(self._RL,(nEr-1,1)).T).flatten() thisLzs= (numpy.tile(self._Lzs,(nEr-1,1)).T).flatten() thisERRL= (numpy.tile(self._ERRL,(nEr-1,1)).T).flatten() thisERRa= (numpy.tile(self._ERRa,(nEr-1,1)).T).flatten() thisy= (numpy.tile(y[0:-1],(nLz,1))).flatten() if self._c: mjr= self._aA(thisRL, numpy.sqrt(2.*(thisERRa+thisy*(thisERRL-thisERRa)-_evaluatePotentials(self._pot,thisRL,numpy.zeros((nEr-1)*nLz)))-thisLzs**2./thisRL**2.), thisLzs/thisRL, numpy.zeros(len(thisRL)), numpy.zeros(len(thisRL)), **kwargs)[0] jr[:,0:-1]= numpy.reshape(mjr,(nLz,nEr-1)) jrERRa[0:nLz]= jr[:,0] else: if numcores > 1: mjr= multi.parallel_map((lambda x: self._aA(thisRL[x], numpy.sqrt(2.*(thisERRa[x]+thisy[x]*(thisERRL[x]-thisERRa[x])-_evaluatePotentials(self._pot,thisRL[x],0.))-thisLzs[x]**2./thisRL[x]**2.), thisLzs[x]/thisRL[x], 0.,0., _justjr=True, **kwargs)[0]), range((nEr-1)*nLz), numcores=numcores) jr[:,0:-1]= numpy.reshape(mjr,(nLz,nEr-1)) jrERRa[0:nLz]= jr[:,0] else: for ii in range(nLz): for jj in range(nEr-1): #Last one is zero by construction try: jr[ii,jj]= self._aA(self._RL[ii], numpy.sqrt(2.*(self._ERRa[ii]+y[jj]*(self._ERRL[ii]-self._ERRa[ii])-_evaluatePotentials(self._pot,self._RL[ii],0.))-self._Lzs[ii]**2./self._RL[ii]**2.), self._Lzs[ii]/self._RL[ii], 0.,0., _justjr=True, **kwargs)[0] except UnboundError: #pragma: no cover raise if jj == 0: jrERRa[ii]= jr[ii,jj] for ii in range(nLz): jr[ii,:]/= jrERRa[ii] #First interpolate Ez=Ezmax self._jr= jr self._jrERRaInterp= interpolate.InterpolatedUnivariateSpline(self._Lzs, numpy.log(jrERRa+10.**-5.),k=3) self._jrInterp= interpolate.RectBivariateSpline(self._Lzs, y, jr, kx=3,ky=3,s=0.) # Check the units self._check_consistent_units() return None
def _evaluate(self,*args,**kwargs): """ NAME: __call__ (_evaluate) PURPOSE: evaluate the actions (jr,lz,jz) INPUT: Either: a) R,vR,vT,z,vz[,phi]: 1) floats: phase-space value for single object (phi is optional) (each can be a Quantity) 2) numpy.ndarray: [N] phase-space values for N objects (each can be a Quantity) b) Orbit instance: initial condition used if that's it, orbit(t) if there is a time given as well as the second argument scipy.integrate.quadrature keywords (used when directly evaluating a point off the grid) OUTPUT: (jr,lz,jz) HISTORY: 2012-07-27 - Written - Bovy (IAS@MPIA) NOTE: For a Miyamoto-Nagai potential, this seems accurate to 0.1% and takes ~0.13 ms For a MWPotential, this takes ~ 0.17 ms """ if len(args) == 5: #R,vR.vT, z, vz R,vR,vT, z, vz= args elif len(args) == 6: #R,vR.vT, z, vz, phi R,vR,vT, z, vz, phi= args else: self._parse_eval_args(*args) R= self._eval_R vR= self._eval_vR vT= self._eval_vT z= self._eval_z vz= self._eval_vz #First work on the vertical action Phi= _evaluatePotentials(self._pot,R,z) try: Phio= _evaluatePotentials(self._pot,R,numpy.zeros(len(R))) except TypeError: Phio= _evaluatePotentials(self._pot,R,0.) Ez= Phi-Phio+vz**2./2. #Bigger than Ezzmax? thisEzZmax= numpy.exp(self._EzZmaxsInterp(R)) if isinstance(R,numpy.ndarray): indx= (R > self._Rmax) indx+= (R < self._Rmin) indx+= (Ez != 0.)*(numpy.log(Ez) > thisEzZmax) indxc= True^indx jz= numpy.empty(R.shape) if numpy.sum(indxc) > 0: jz[indxc]= (self._jzInterp.ev(R[indxc],Ez[indxc]/thisEzZmax[indxc])\ *(numpy.exp(self._jzEzmaxInterp(R[indxc]))-10.**-5.)) if numpy.sum(indx) > 0: jz[indx]= self._aA(R[indx], numpy.zeros(numpy.sum(indx)), numpy.ones(numpy.sum(indx)),#these two r dummies numpy.zeros(numpy.sum(indx)), numpy.sqrt(2.*Ez[indx]), _justjz=True, **kwargs)[2] else: if R > self._Rmax or R < self._Rmin or (Ez != 0 and numpy.log(Ez) > thisEzZmax): #Outside of the grid if _PRINTOUTSIDEGRID: #pragma: no cover print("Outside of grid in Ez", R > self._Rmax , R < self._Rmin , (Ez != 0 and numpy.log(Ez) > thisEzZmax)) jz= self._aA(R,0.,1.,#these two r dummies 0.,numpy.sqrt(2.*Ez), _justjz=True, **kwargs)[2] else: jz= (self._jzInterp(R,Ez/thisEzZmax)\ *(numpy.exp(self._jzEzmaxInterp(R))-10.**-5.))[0][0] #Radial action ERLz= numpy.fabs(R*vT)+self._gamma*jz ER= Phio+vR**2./2.+ERLz**2./2./R**2. thisRL= self._RLInterp(ERLz) thisERRL= -numpy.exp(self._ERRLInterp(ERLz))+self._ERRLmax thisERRa= -numpy.exp(self._ERRaInterp(ERLz))+self._ERRamax if isinstance(R,numpy.ndarray): indx= ((ER-thisERRa)/(thisERRL-thisERRa) > 1.)\ *(((ER-thisERRa)/(thisERRL-thisERRa)-1.) < 10.**-2.) ER[indx]= thisERRL[indx] indx= ((ER-thisERRa)/(thisERRL-thisERRa) < 0.)\ *((ER-thisERRa)/(thisERRL-thisERRa) > -10.**-2.) ER[indx]= thisERRa[indx] indx= (ERLz < self._Lzmin) indx+= (ERLz > self._Lzmax) indx+= ((ER-thisERRa)/(thisERRL-thisERRa) > 1.) indx+= ((ER-thisERRa)/(thisERRL-thisERRa) < 0.) indxc= True^indx jr= numpy.empty(R.shape) if numpy.sum(indxc) > 0: jr[indxc]= (self._jrInterp.ev(ERLz[indxc], (ER[indxc]-thisERRa[indxc])/(thisERRL[indxc]-thisERRa[indxc]))\ *(numpy.exp(self._jrERRaInterp(ERLz[indxc]))-10.**-5.)) if numpy.sum(indx) > 0: jr[indx]= self._aA(thisRL[indx], numpy.sqrt(2.*(ER[indx]-_evaluatePotentials(self._pot,thisRL[indx],0.))-ERLz[indx]**2./thisRL[indx]**2.), ERLz[indx]/thisRL[indx], numpy.zeros(len(thisRL)), numpy.zeros(len(thisRL)), _justjr=True, **kwargs)[0] else: if (ER-thisERRa)/(thisERRL-thisERRa) > 1. \ and ((ER-thisERRa)/(thisERRL-thisERRa)-1.) < 10.**-2.: ER= thisERRL elif (ER-thisERRa)/(thisERRL-thisERRa) < 0. \ and (ER-thisERRa)/(thisERRL-thisERRa) > -10.**-2.: ER= thisERRa #Outside of grid? if ERLz < self._Lzmin or ERLz > self._Lzmax \ or (ER-thisERRa)/(thisERRL-thisERRa) > 1. \ or (ER-thisERRa)/(thisERRL-thisERRa) < 0.: if _PRINTOUTSIDEGRID: #pragma: no cover print("Outside of grid in ER/Lz", ERLz < self._Lzmin , ERLz > self._Lzmax \ , (ER-thisERRa)/(thisERRL-thisERRa) > 1. \ , (ER-thisERRa)/(thisERRL-thisERRa) < 0., ER, thisERRL, thisERRa, (ER-thisERRa)/(thisERRL-thisERRa)) jr= self._aA(thisRL[0], numpy.sqrt(2.*(ER-_evaluatePotentials(self._pot,thisRL,0.))-ERLz**2./thisRL**2.)[0], (ERLz/thisRL)[0], 0.,0., _justjr=True, **kwargs)[0] else: jr= (self._jrInterp(ERLz, (ER-thisERRa)/(thisERRL-thisERRa))\ *(numpy.exp(self._jrERRaInterp(ERLz))-10.**-5.))[0][0] return (jr,R*vT,jz) def Jz(self,*args,**kwargs): """ NAME: Jz PURPOSE: evaluate the action jz INPUT: Either: a) R,vR,vT,z,vz b) Orbit instance: initial condition used if that's it, orbit(t) if there is a time given as well scipy.integrate.quadrature keywords OUTPUT: jz HISTORY: 2012-07-30 - Written - Bovy (IAS@MPIA) """ self._parse_eval_args(*args) Phi= _evaluatePotentials(self._pot,self._eval_R,self._eval_z) Phio= _evaluatePotentials(self._pot,self._eval_R,0.) Ez= Phi-Phio+self._eval_vz**2./2. #Bigger than Ezzmax? thisEzZmax= numpy.exp(self._EzZmaxsInterp(self._eval_R)) if self._eval_R > self._Rmax or self._eval_R < self._Rmin or (Ez != 0. and numpy.log(Ez) > thisEzZmax): #Outside of the grid if _PRINTOUTSIDEGRID: #pragma: no cover print("Outside of grid in Ez") jz= self._aA(self._eval_R,0.,1.,#these two r dummies 0.,numpy.sqrt(2.*Ez), _justjz=True, **kwargs)[2] else: jz= (self._jzInterp(self._eval_R,Ez/thisEzZmax)\ *(numpy.exp(self._jzEzmaxInterp(self._eval_R))-10.**-5.))[0][0] return jz