def __init__(self, *args, **kwargs): """ NAME: __init__ PURPOSE: initialize an actionAngleStaeckel object INPUT: pot= potential or list of potentials (3D) delta= focus (can be Quantity) useu0 - use u0 to calculate dV (NOT recommended) c= if True, always use C for calculations order= (10) number of points to use in the Gauss-Legendre numerical integration of the relevant action, frequency, and angle integrals ro= distance from vantage point to GC (kpc; can be Quantity) vo= circular velocity at ro (km/s; can be Quantity) OUTPUT: instance HISTORY: 2012-11-27 - Written - Bovy (IAS) """ actionAngle.__init__(self, ro=kwargs.get('ro', None), vo=kwargs.get('vo', None)) if not 'pot' in kwargs: #pragma: no cover raise IOError("Must specify pot= for actionAngleStaeckel") self._pot = flatten_potential(kwargs['pot']) if self._pot == MWPotential: warnings.warn( "Use of MWPotential as a Milky-Way-like potential is deprecated; galpy.potential.MWPotential2014, a potential fit to a large variety of dynamical constraints (see Bovy 2015), is the preferred Milky-Way-like potential in galpy", galpyWarning) if not 'delta' in kwargs: #pragma: no cover raise IOError("Must specify delta= for actionAngleStaeckel") if ext_loaded and (('c' in kwargs and kwargs['c']) or not 'c' in kwargs): self._c = _check_c(self._pot) if 'c' in kwargs and kwargs['c'] and not self._c: warnings.warn( "C module not used because potential does not have a C implementation", galpyWarning) #pragma: no cover else: self._c = False self._useu0 = kwargs.get('useu0', False) self._delta = kwargs['delta'] self._order = kwargs.get('order', 10) if _APY_LOADED and isinstance(self._delta, units.Quantity): self._delta = self._delta.to(units.kpc).value / self._ro # Check the units self._check_consistent_units() return None
def _integrateRZOrbit(vxvv,pot,t,method,dt): """ NAME: _integrateRZOrbit PURPOSE: integrate an orbit in a Phi(R,z) potential in the (R,z) plane INPUT: vxvv - array with the initial conditions stacked like [R,vR,vT,z,vz]; vR outward! pot - Potential instance t - list of times at which to output (0 has to be in this!) method - 'odeint' or 'leapfrog' dt - if set, force the integrator to use this basic stepsize; must be an integer divisor of output stepsize OUTPUT: [:,5] array of [R,vR,vT,z,vz] at each t HISTORY: 2010-04-16 - Written - Bovy (NYU) """ #First check that the potential has C if '_c' in method: if not ext_loaded or not _check_c(pot): if ('leapfrog' in method or 'symplec' in method): method= 'leapfrog' else: method= 'odeint' if not ext_loaded: # pragma: no cover warnings.warn("Cannot use C integration because C extension not loaded (using %s instead)" % (method), galpyWarning) else: warnings.warn("Cannot use C integration because some of the potentials are not implemented in C (using %s instead)" % (method), galpyWarning) if method.lower() == 'leapfrog' \ or method.lower() == 'leapfrog_c' or method.lower() == 'rk4_c' \ or method.lower() == 'rk6_c' or method.lower() == 'symplec4_c' \ or method.lower() == 'symplec6_c' or method.lower() == 'dopr54_c' or method.lower() == 'dop853_c': #We hack this by upgrading to a FullOrbit this_vxvv= nu.zeros(len(vxvv)+1) this_vxvv[0:len(vxvv)]= vxvv tmp_out= _integrateFullOrbit(this_vxvv,pot,t,method,dt) #tmp_out is (nt,6) out= tmp_out[:,0:5] elif method.lower() == 'odeint' or method.lower() == 'dop853': l= vxvv[0]*vxvv[2] l2= l**2. init= [vxvv[0],vxvv[1],vxvv[3],vxvv[4]] if method.lower() == "dop853": intOut = dop853(_RZEOM, init, t, args=(pot, l2)) else: intOut = integrate.odeint(_RZEOM, init, t, args=(pot, l2), rtol=10. ** -8.) # ,mxstep=100000000) out= nu.zeros((len(t),5)) out[:,0]= intOut[:,0] out[:,1]= intOut[:,1] out[:,3]= intOut[:,2] out[:,4]= intOut[:,3] out[:,2]= l/out[:,0] #post-process to remove negative radii neg_radii= (out[:,0] < 0.) out[neg_radii,0]= -out[neg_radii,0] return out
def __init__(self,*args,**kwargs): """ NAME: __init__ PURPOSE: initialize an actionAngleStaeckel object INPUT: pot= potential or list of potentials (3D) delta= focus (can be Quantity) useu0 - use u0 to calculate dV (NOT recommended) c= if True, always use C for calculations order= (10) number of points to use in the Gauss-Legendre numerical integration of the relevant action, frequency, and angle integrals ro= distance from vantage point to GC (kpc; can be Quantity) vo= circular velocity at ro (km/s; can be Quantity) OUTPUT: instance HISTORY: 2012-11-27 - Written - Bovy (IAS) """ actionAngle.__init__(self, ro=kwargs.get('ro',None),vo=kwargs.get('vo',None)) if not 'pot' in kwargs: #pragma: no cover raise IOError("Must specify pot= for actionAngleStaeckel") self._pot= flatten_potential(kwargs['pot']) if self._pot == MWPotential: warnings.warn("Use of MWPotential as a Milky-Way-like potential is deprecated; galpy.potential.MWPotential2014, a potential fit to a large variety of dynamical constraints (see Bovy 2015), is the preferred Milky-Way-like potential in galpy", galpyWarning) if not 'delta' in kwargs: #pragma: no cover raise IOError("Must specify delta= for actionAngleStaeckel") if ext_loaded and (('c' in kwargs and kwargs['c']) or not 'c' in kwargs): self._c= _check_c(self._pot) if 'c' in kwargs and kwargs['c'] and not self._c: warnings.warn("C module not used because potential does not have a C implementation",galpyWarning) #pragma: no cover else: self._c= False self._useu0= kwargs.get('useu0',False) self._delta= kwargs['delta'] self._order= kwargs.get('order',10) if _APY_LOADED and isinstance(self._delta,units.Quantity): self._delta= self._delta.to(units.kpc).value/self._ro # Check the units self._check_consistent_units() return None
def __init__(self, *args, **kwargs): """ NAME: __init__ PURPOSE: initialize an actionAngleTorus object INPUT: pot= potential or list of potentials (3D) tol= default tolerance to use when fitting tori (|dJ|/J) dJ= default action difference when computing derivatives (Hessian or Jacobian) OUTPUT: instance HISTORY: 2015-08-07 - Written - Bovy (UofT) """ if not 'pot' in kwargs: #pragma: no cover raise IOError("Must specify pot= for actionAngleTorus") self._pot = flatten_potential(kwargs['pot']) if _isNonAxi(self._pot): raise RuntimeError( "actionAngleTorus for non-axisymmetric potentials is not supported" ) if self._pot == MWPotential: warnings.warn( "Use of MWPotential as a Milky-Way-like potential is deprecated; galpy.potential.MWPotential2014, a potential fit to a large variety of dynamical constraints (see Bovy 2015), is the preferred Milky-Way-like potential in galpy", galpyWarning) if ext_loaded: self._c = _check_c(self._pot) if not self._c: raise RuntimeError( 'The given potential is not fully implemented in C; using the actionAngleTorus code is not supported in pure Python' ) else: # pragma: no cover raise RuntimeError( 'actionAngleTorus instances cannot be used, because the actionAngleTorus_c extension failed to load' ) self._tol = kwargs.get('tol', 0.001) self._dJ = kwargs.get('dJ', 0.001) return None
def __init__(self, *args, **kwargs): """ NAME: __init__ PURPOSE: initialize an actionAngleAdiabatic object INPUT: pot= potential or list of potentials (planarPotentials) gamma= (default=1.) replace Lz by Lz+gamma Jz in effective potential ro= distance from vantage point to GC (kpc; can be Quantity) vo= circular velocity at ro (km/s; can be Quantity) OUTPUT: instance HISTORY: 2012-07-26 - Written - Bovy (IAS@MPIA) """ actionAngle.__init__(self, ro=kwargs.get('ro', None), vo=kwargs.get('vo', None)) if not 'pot' in kwargs: #pragma: no cover raise IOError("Must specify pot= for actionAngleAxi") self._pot = flatten_potential(kwargs['pot']) if self._pot == MWPotential: warnings.warn( "Use of MWPotential as a Milky-Way-like potential is deprecated; galpy.potential.MWPotential2014, a potential fit to a large variety of dynamical constraints (see Bovy 2015), is the preferred Milky-Way-like potential in galpy", galpyWarning) if ext_loaded and 'c' in kwargs and kwargs['c']: self._c = _check_c(self._pot) if 'c' in kwargs and kwargs['c'] and not self._c: warnings.warn( "C module not used because potential does not have a C implementation", galpyWarning) #pragma: no cover else: self._c = False self._gamma = kwargs.get('gamma', 1.) # Check the units self._check_consistent_units() return None
def __init__(self,*args,**kwargs): """ NAME: __init__ PURPOSE: initialize an actionAngleTorus object INPUT: pot= potential or list of potentials (3D) tol= default tolerance to use when fitting tori (|dJ|/J) dJ= default action difference when computing derivatives (Hessian or Jacobian) OUTPUT: instance HISTORY: 2015-08-07 - Written - Bovy (UofT) """ if not 'pot' in kwargs: #pragma: no cover raise IOError("Must specify pot= for actionAngleTorus") self._pot= flatten_potential(kwargs['pot']) if _isNonAxi(self._pot): raise RuntimeError("actionAngleTorus for non-axisymmetric potentials is not supported") if self._pot == MWPotential: warnings.warn("Use of MWPotential as a Milky-Way-like potential is deprecated; galpy.potential.MWPotential2014, a potential fit to a large variety of dynamical constraints (see Bovy 2015), is the preferred Milky-Way-like potential in galpy", galpyWarning) if ext_loaded: self._c= _check_c(self._pot) if not self._c: raise RuntimeError('The given potential is not fully implemented in C; using the actionAngleTorus code is not supported in pure Python') else:# pragma: no cover raise RuntimeError('actionAngleTorus instances cannot be used, because the actionAngleTorus_c extension failed to load') self._tol= kwargs.get('tol',0.001) self._dJ= kwargs.get('dJ',0.001) return None
def getPotInstance(pot_name): """ Try to get an instance of a give pot_name Return ---------- pot_module: module object of pot_name, if it is a combined list, return None pot_instance: module instance, if it is not 3D or not available, return None """ pot_module = None pot_instance=None if (pot_name in dir(galpy.potential)) & ('Potential' in pot_name): pot_module = galpy.potential.__getattribute__(pot_name) if (type(pot_module) == list): pot_instance = pot_module pot_module = None elif (type(pot_module) == type): # get instance with warnings.catch_warnings(): warnings.simplefilter("ignore") try: pot_instance = pot_module() except (ValueError, TypeError, AttributeError, RuntimeWarning): pot_module = None pot_instance = None else: pot_instance = pot_module pot_module = type(pot_module) if (pot_instance != None): # remove 2D models if (galpy.potential._dim(pot_instance)!=3): pot_instance = None # remove potential without c support if (not _check_c(pot_instance)): pot_instance = None return pot_module, pot_instance
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 delta= (object-wide default) can be used to override the object-wide focal length; can also be an array with length N to allow different delta for different phase-space points u0= (None) if object-wide option useu0 is set, u0 to use (if useu0 and useu0 is None, a good value will be computed) c= (object-wide default, bool) True/False to override the object-wide setting for whether or not to use the C implementation order= (object-wide default, int) number of points to use in the Gauss-Legendre numerical integration of the relevant action integrals When not using C: fixed_quad= (False) if True, use Gaussian quadrature (scipy.integrate.fixed_quad instead of scipy.integrate.quad) scipy.integrate.fixed_quad or .quad keywords OUTPUT: (jr,lz,jz) HISTORY: 2012-11-27 - Written - Bovy (IAS) 2017-12-27 - Allowed individual delta for each point - Bovy (UofT) """ delta = kwargs.pop('delta', self._delta) order = kwargs.get('order', self._order) 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 if isinstance(R, float): R = nu.array([R]) vR = nu.array([vR]) vT = nu.array([vT]) z = nu.array([z]) vz = nu.array([vz]) if ((self._c and not ('c' in kwargs and not kwargs['c']))\ or (ext_loaded and (('c' in kwargs and kwargs['c'])))) \ and _check_c(self._pot): Lz = R * vT if self._useu0: #First calculate u0 if 'u0' in kwargs: u0 = nu.asarray(kwargs['u0']) else: E = nu.array([ _evaluatePotentials(self._pot, R[ii], z[ii]) + vR[ii]**2. / 2. + vz[ii]**2. / 2. + vT[ii]**2. / 2. for ii in range(len(R)) ]) u0= actionAngleStaeckel_c.actionAngleStaeckel_calcu0(\ E,Lz,self._pot,delta)[0] kwargs.pop('u0', None) else: u0 = None jr, jz, err= actionAngleStaeckel_c.actionAngleStaeckel_c(\ self._pot,delta,R,vR,vT,z,vz,u0=u0,order=order) if err == 0: return (jr, Lz, jz) else: #pragma: no cover raise RuntimeError( "C-code for calculation actions failed; try with c=False") else: if 'c' in kwargs and kwargs['c'] and not self._c: #pragma: no cover warnings.warn( "C module not used because potential does not have a C implementation", galpyWarning) kwargs.pop('c', None) if len(R) > 1: ojr = nu.zeros((len(R))) olz = nu.zeros((len(R))) ojz = nu.zeros((len(R))) for ii in range(len(R)): targs = (R[ii], vR[ii], vT[ii], z[ii], vz[ii]) tkwargs = copy.copy(kwargs) try: tkwargs['delta'] = delta[ii] except TypeError: tkwargs['delta'] = delta tjr, tlz, tjz = self(*targs, **tkwargs) ojr[ii] = tjr ojz[ii] = tjz olz[ii] = tlz return (ojr, olz, ojz) else: #Set up the actionAngleStaeckelSingle object aASingle = actionAngleStaeckelSingle(R[0], vR[0], vT[0], z[0], vz[0], pot=self._pot, delta=delta) return (nu.atleast_1d(aASingle.JR(**copy.copy(kwargs))), nu.atleast_1d(aASingle._R * aASingle._vT), nu.atleast_1d(aASingle.Jz(**copy.copy(kwargs))))
def _uminumaxvmin(self, *args, **kwargs): """ NAME: _uminumaxvmin PURPOSE: evaluate u_min, u_max, and v_min 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 c= True/False; overrides the object's c= keyword to use C or not OUTPUT: (umin,umax,vmin) HISTORY: 2017-12-12 - Written - Bovy (UofT) """ delta = kwargs.pop('delta', self._delta) 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 if isinstance(R, float): R = nu.array([R]) vR = nu.array([vR]) vT = nu.array([vT]) z = nu.array([z]) vz = nu.array([vz]) if ((self._c and not ('c' in kwargs and not kwargs['c']))\ or (ext_loaded and (('c' in kwargs and kwargs['c'])))) \ and _check_c(self._pot): Lz = R * vT if self._useu0: #First calculate u0 if 'u0' in kwargs: u0 = nu.asarray(kwargs['u0']) else: E = nu.array([ _evaluatePotentials(self._pot, R[ii], z[ii]) + vR[ii]**2. / 2. + vz[ii]**2. / 2. + vT[ii]**2. / 2. for ii in range(len(R)) ]) u0= actionAngleStaeckel_c.actionAngleStaeckel_calcu0(\ E,Lz,self._pot,delta)[0] kwargs.pop('u0', None) else: u0 = None umin, umax, vmin, err= \ actionAngleStaeckel_c.actionAngleUminUmaxVminStaeckel_c(\ self._pot,delta,R,vR,vT,z,vz,u0=u0) if err == 0: return (umin, umax, vmin) else: #pragma: no cover raise RuntimeError( "C-code for calculation actions failed; try with c=False") else: if 'c' in kwargs and kwargs['c'] and not self._c: #pragma: no cover warnings.warn( "C module not used because potential does not have a C implementation", galpyWarning) kwargs.pop('c', None) if len(R) > 1: oumin = nu.zeros((len(R))) oumax = nu.zeros((len(R))) ovmin = nu.zeros((len(R))) for ii in range(len(R)): targs = (R[ii], vR[ii], vT[ii], z[ii], vz[ii]) tkwargs = copy.copy(kwargs) try: tkwargs['delta'] = delta[ii] except TypeError: tkwargs['delta'] = delta tumin, tumax, tvmin = self._uminumaxvmin(*targs, **tkwargs) oumin[ii] = tumin oumax[ii] = tumax ovmin[ii] = tvmin return (oumin, oumax, ovmin) else: #Set up the actionAngleStaeckelSingle object aASingle = actionAngleStaeckelSingle(R[0], vR[0], vT[0], z[0], vz[0], pot=self._pot, delta=delta) umin, umax = aASingle.calcUminUmax() vmin = aASingle.calcVmin() return (nu.atleast_1d(umin), nu.atleast_1d(umax), nu.atleast_1d(vmin))
def _integrateOrbit_dxdv(vxvv,dxdv,pot,t,method,rectIn,rectOut): """ NAME: _integrateOrbit_dxdv PURPOSE: integrate an orbit and area of phase space in a Phi(R) potential in the (R,phi)-plane INPUT: vxvv - array with the initial conditions stacked like [R,vR,vT,phi]; vR outward! dxdv - difference to integrate [dR,dvR,dvT,dphi] pot - Potential instance t - list of times at which to output (0 has to be in this!) method - 'odeint' or 'leapfrog' rectIn= (False) if True, input dxdv is in rectangular coordinates rectOut= (False) if True, output dxdv (that in orbit_dxdv) is in rectangular coordinates OUTPUT: [:,8] array of [R,vR,vT,phi,dR,dvR,dvT,dphi] at each t error message from integrator HISTORY: 2010-10-17 - Written - Bovy (IAS) """ #First check that the potential has C if '_c' in method: allHasC= _check_c(pot) and _check_c(pot,dxdv=True) if not ext_loaded or \ (not allHasC and not 'leapfrog' in method and not 'symplec' in method): method= 'odeint' if not ext_loaded: # pragma: no cover warnings.warn("Using odeint because C extension not loaded",galpyWarning) else: warnings.warn("Using odeint because not all used potential have adequate C implementations to integrate phase-space volumes",galpyWarning) #go to the rectangular frame this_vxvv= nu.array([vxvv[0]*nu.cos(vxvv[3]), vxvv[0]*nu.sin(vxvv[3]), vxvv[1]*nu.cos(vxvv[3])-vxvv[2]*nu.sin(vxvv[3]), vxvv[2]*nu.cos(vxvv[3])+vxvv[1]*nu.sin(vxvv[3])]) if not rectIn: this_dxdv= nu.array([nu.cos(vxvv[3])*dxdv[0] -vxvv[0]*nu.sin(vxvv[3])*dxdv[3], nu.sin(vxvv[3])*dxdv[0] +vxvv[0]*nu.cos(vxvv[3])*dxdv[3], -(vxvv[1]*nu.sin(vxvv[3]) +vxvv[2]*nu.cos(vxvv[3]))*dxdv[3] +nu.cos(vxvv[3])*dxdv[1]-nu.sin(vxvv[3])*dxdv[2], (vxvv[1]*nu.cos(vxvv[3]) -vxvv[2]*nu.sin(vxvv[3]))*dxdv[3] +nu.sin(vxvv[3])*dxdv[1]+nu.cos(vxvv[3])*dxdv[2]]) else: this_dxdv= dxdv if 'leapfrog' in method.lower() or 'symplec' in method.lower(): raise TypeError('Symplectic integration for phase-space volume is not possible') elif ext_loaded and \ (method.lower() == 'rk4_c' or method.lower() == 'rk6_c' \ or method.lower() == 'dopr54_c' or method.lower() == 'dop853_c'): warnings.warn("Using C implementation to integrate orbits",galpyWarningVerbose) #integrate tmp_out, msg= integratePlanarOrbit_dxdv_c(pot,this_vxvv,this_dxdv, t,method) elif method.lower() == 'odeint' or not ext_loaded or method.lower() == 'dop853': init= [this_vxvv[0],this_vxvv[1],this_vxvv[2],this_vxvv[3], this_dxdv[0],this_dxdv[1],this_dxdv[2],this_dxdv[3]] #integrate if method.lower() == "dop853": tmp_out = dop853(_EOM_dxdv, init, t, args=(pot,)) else: tmp_out= integrate.odeint(_EOM_dxdv,init,t,args=(pot,), rtol=10.**-8.)#,mxstep=100000000) msg= 0 else: raise NotImplementedError("requested integration method does not exist") #go back to the cylindrical frame R= nu.sqrt(tmp_out[:,0]**2.+tmp_out[:,1]**2.) phi= nu.arccos(tmp_out[:,0]/R) phi[(tmp_out[:,1] < 0.)]= 2.*nu.pi-phi[(tmp_out[:,1] < 0.)] vR= tmp_out[:,2]*nu.cos(phi)+tmp_out[:,3]*nu.sin(phi) vT= tmp_out[:,3]*nu.cos(phi)-tmp_out[:,2]*nu.sin(phi) cp= nu.cos(phi) sp= nu.sin(phi) dR= cp*tmp_out[:,4]+sp*tmp_out[:,5] dphi= (cp*tmp_out[:,5]-sp*tmp_out[:,4])/R dvR= cp*tmp_out[:,6]+sp*tmp_out[:,7]+vT*dphi dvT= cp*tmp_out[:,7]-sp*tmp_out[:,6]-vR*dphi out= nu.zeros((len(t),8)) out[:,0]= R out[:,1]= vR out[:,2]= vT out[:,3]= phi if rectOut: out[:,4:]= tmp_out[:,4:] else: out[:,4]= dR out[:,7]= dphi out[:,5]= dvR out[:,6]= dvT _parse_warnmessage(msg) return (out,msg)
def _actionsFreqsAngles(self, *args, **kwargs): """ NAME: actionsFreqsAngles (_actionsFreqsAngles) PURPOSE: evaluate the actions, frequencies, and angles (jr,lz,jz,Omegar,Omegaphi,Omegaz,angler,anglephi,anglez) 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 delta= (object-wide default) can be used to override the object-wide focal length; can also be an array with length N to allow different delta for different phase-space points u0= (None) if object-wide option useu0 is set, u0 to use (if useu0 and useu0 is None, a good value will be computed) c= (object-wide default, bool) True/False to override the object-wide setting for whether or not to use the C implementation order= (10) number of points to use in the Gauss-Legendre numerical integration of the relevant action, frequency, and angle integrals When not using C: fixed_quad= (False) if True, use Gaussian quadrature (scipy.integrate.fixed_quad instead of scipy.integrate.quad) scipy.integrate.fixed_quad or .quad keywords OUTPUT: (jr,lz,jz,Omegar,Omegaphi,Omegaz,angler,anglephi,anglez) HISTORY: 2013-08-28 - Written - Bovy (IAS) """ delta = kwargs.pop('delta', self._delta) order = kwargs.get('order', self._order) if ((self._c and not ('c' in kwargs and not kwargs['c']))\ or (ext_loaded and (('c' in kwargs and kwargs['c'])))) \ and _check_c(self._pot): if len(args) == 5: #R,vR.vT, z, vz pragma: no cover raise IOError("Must specify phi") 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 phi = self._eval_phi if isinstance(R, float): R = nu.array([R]) vR = nu.array([vR]) vT = nu.array([vT]) z = nu.array([z]) vz = nu.array([vz]) phi = nu.array([phi]) Lz = R * vT if self._useu0: #First calculate u0 if 'u0' in kwargs: u0 = nu.asarray(kwargs['u0']) else: E = nu.array([ _evaluatePotentials(self._pot, R[ii], z[ii]) + vR[ii]**2. / 2. + vz[ii]**2. / 2. + vT[ii]**2. / 2. for ii in range(len(R)) ]) u0= actionAngleStaeckel_c.actionAngleStaeckel_calcu0(\ E,Lz,self._pot,delta)[0] kwargs.pop('u0', None) else: u0 = None jr, jz, Omegar, Omegaphi, Omegaz, angler, anglephi,anglez, err= actionAngleStaeckel_c.actionAngleFreqAngleStaeckel_c(\ self._pot,delta,R,vR,vT,z,vz,phi,u0=u0,order=order) # Adjustements for close-to-circular orbits indx = nu.isnan(Omegar) * (jr < 10.**-3.) + nu.isnan(Omegaz) * ( jz < 10.**-3. ) #Close-to-circular and close-to-the-plane orbits if nu.sum(indx) > 0: Omegar[indx] = [ epifreq(self._pot, r, use_physical=False) for r in R[indx] ] Omegaphi[indx] = [ omegac(self._pot, r, use_physical=False) for r in R[indx] ] Omegaz[indx] = [ verticalfreq(self._pot, r, use_physical=False) for r in R[indx] ] if err == 0: return (jr, Lz, jz, Omegar, Omegaphi, Omegaz, angler, anglephi, anglez) else: raise RuntimeError( "C-code for calculation actions failed; try with c=False" ) #pragma: no cover else: #pragma: no cover if 'c' in kwargs and kwargs['c'] and not self._c: #pragma: no cover warnings.warn( "C module not used because potential does not have a C implementation", galpyWarning) raise NotImplementedError( "actionsFreqs with c=False not implemented")
def _integrateOrbit_dxdv(vxvv, dxdv, pot, t, method, rectIn, rectOut): """ NAME: _integrateOrbit_dxdv PURPOSE: integrate an orbit and area of phase space in a Phi(R) potential in the (R,phi)-plane INPUT: vxvv - array with the initial conditions stacked like [R,vR,vT,phi]; vR outward! dxdv - difference to integrate [dR,dvR,dvT,dphi] pot - Potential instance t - list of times at which to output (0 has to be in this!) method - 'odeint' or 'leapfrog' rectIn= (False) if True, input dxdv is in rectangular coordinates rectOut= (False) if True, output dxdv (that in orbit_dxdv) is in rectangular coordinates OUTPUT: [:,8] array of [R,vR,vT,phi,dR,dvR,dvT,dphi] at each t error message from integrator HISTORY: 2010-10-17 - Written - Bovy (IAS) """ #First check that the potential has C if '_c' in method: allHasC = _check_c(pot) and _check_c(pot, dxdv=True) if not ext_loaded or \ (not allHasC and not 'leapfrog' in method and not 'symplec' in method): method = 'odeint' if not ext_loaded: # pragma: no cover warnings.warn("Using odeint because C extension not loaded", galpyWarning) else: warnings.warn( "Using odeint because not all used potential have adequate C implementations to integrate phase-space volumes", galpyWarning) #go to the rectangular frame this_vxvv = nu.array([ vxvv[0] * nu.cos(vxvv[3]), vxvv[0] * nu.sin(vxvv[3]), vxvv[1] * nu.cos(vxvv[3]) - vxvv[2] * nu.sin(vxvv[3]), vxvv[2] * nu.cos(vxvv[3]) + vxvv[1] * nu.sin(vxvv[3]) ]) if not rectIn: this_dxdv = nu.array([ nu.cos(vxvv[3]) * dxdv[0] - vxvv[0] * nu.sin(vxvv[3]) * dxdv[3], nu.sin(vxvv[3]) * dxdv[0] + vxvv[0] * nu.cos(vxvv[3]) * dxdv[3], -(vxvv[1] * nu.sin(vxvv[3]) + vxvv[2] * nu.cos(vxvv[3])) * dxdv[3] + nu.cos(vxvv[3]) * dxdv[1] - nu.sin(vxvv[3]) * dxdv[2], (vxvv[1] * nu.cos(vxvv[3]) - vxvv[2] * nu.sin(vxvv[3])) * dxdv[3] + nu.sin(vxvv[3]) * dxdv[1] + nu.cos(vxvv[3]) * dxdv[2] ]) else: this_dxdv = dxdv if 'leapfrog' in method.lower() or 'symplec' in method.lower(): raise TypeError( 'Symplectic integration for phase-space volume is not possible') elif ext_loaded and \ (method.lower() == 'rk4_c' or method.lower() == 'rk6_c' \ or method.lower() == 'dopr54_c'): warnings.warn("Using C implementation to integrate orbits", galpyWarningVerbose) #integrate tmp_out, msg = integratePlanarOrbit_dxdv_c(pot, this_vxvv, this_dxdv, t, method) elif method.lower() == 'odeint' or not ext_loaded: init = [ this_vxvv[0], this_vxvv[1], this_vxvv[2], this_vxvv[3], this_dxdv[0], this_dxdv[1], this_dxdv[2], this_dxdv[3] ] #integrate tmp_out = integrate.odeint(_EOM_dxdv, init, t, args=(pot, ), rtol=10.**-8.) #,mxstep=100000000) msg = 0 else: raise NotImplementedError( "requested integration method does not exist") #go back to the cylindrical frame R = nu.sqrt(tmp_out[:, 0]**2. + tmp_out[:, 1]**2.) phi = nu.arccos(tmp_out[:, 0] / R) phi[(tmp_out[:, 1] < 0.)] = 2. * nu.pi - phi[(tmp_out[:, 1] < 0.)] vR = tmp_out[:, 2] * nu.cos(phi) + tmp_out[:, 3] * nu.sin(phi) vT = tmp_out[:, 3] * nu.cos(phi) - tmp_out[:, 2] * nu.sin(phi) cp = nu.cos(phi) sp = nu.sin(phi) dR = cp * tmp_out[:, 4] + sp * tmp_out[:, 5] dphi = (cp * tmp_out[:, 5] - sp * tmp_out[:, 4]) / R dvR = cp * tmp_out[:, 6] + sp * tmp_out[:, 7] + vT * dphi dvT = cp * tmp_out[:, 7] - sp * tmp_out[:, 6] - vR * dphi out = nu.zeros((len(t), 8)) out[:, 0] = R out[:, 1] = vR out[:, 2] = vT out[:, 3] = phi if rectOut: out[:, 4:] = tmp_out[:, 4:] else: out[:, 4] = dR out[:, 7] = dphi out[:, 5] = dvR out[:, 6] = dvT _parse_warnmessage(msg) return (out, msg)
def _integrateOrbit(vxvv, pot, t, method, dt): """ NAME: _integrateOrbit PURPOSE: integrate an orbit in a Phi(R) potential in the (R,phi)-plane INPUT: vxvv - array with the initial conditions stacked like [R,vR,vT,phi]; vR outward! pot - Potential instance t - list of times at which to output (0 has to be in this!) method - 'odeint' or 'leapfrog' dt- if set, force the integrator to use this basic stepsize; must be an integer divisor of output stepsize OUTPUT: [:,4] array of [R,vR,vT,phi] at each t HISTORY: 2010-07-20 - Written - Bovy (NYU) """ #First check that the potential has C if '_c' in method: if not ext_loaded or not _check_c(pot): if ('leapfrog' in method or 'symplec' in method): method = 'leapfrog' else: method = 'odeint' if not ext_loaded: # pragma: no cover warnings.warn( "Cannot use C integration because C extension not loaded (using %s instead)" % (method), galpyWarning) else: warnings.warn( "Cannot use C integration because some of the potentials are not implemented in C (using %s instead)" % (method), galpyWarning) if method.lower() == 'leapfrog': #go to the rectangular frame this_vxvv = nu.array([ vxvv[0] * nu.cos(vxvv[3]), vxvv[0] * nu.sin(vxvv[3]), vxvv[1] * nu.cos(vxvv[3]) - vxvv[2] * nu.sin(vxvv[3]), vxvv[2] * nu.cos(vxvv[3]) + vxvv[1] * nu.sin(vxvv[3]) ]) #integrate tmp_out = symplecticode.leapfrog(_rectForce, this_vxvv, t, args=(pot, ), rtol=10.**-8) #go back to the cylindrical frame R = nu.sqrt(tmp_out[:, 0]**2. + tmp_out[:, 1]**2.) phi = nu.arccos(tmp_out[:, 0] / R) phi[(tmp_out[:, 1] < 0.)] = 2. * nu.pi - phi[(tmp_out[:, 1] < 0.)] vR = tmp_out[:, 2] * nu.cos(phi) + tmp_out[:, 3] * nu.sin(phi) vT = tmp_out[:, 3] * nu.cos(phi) - tmp_out[:, 2] * nu.sin(phi) out = nu.zeros((len(t), 4)) out[:, 0] = R out[:, 1] = vR out[:, 2] = vT out[:, 3] = phi msg = 0 elif ext_loaded and \ (method.lower() == 'leapfrog_c' or method.lower() == 'rk4_c' \ or method.lower() == 'rk6_c' or method.lower() == 'symplec4_c' \ or method.lower() == 'symplec6_c' or method.lower() == 'dopr54_c'): warnings.warn("Using C implementation to integrate orbits", galpyWarningVerbose) #go to the rectangular frame this_vxvv = nu.array([ vxvv[0] * nu.cos(vxvv[3]), vxvv[0] * nu.sin(vxvv[3]), vxvv[1] * nu.cos(vxvv[3]) - vxvv[2] * nu.sin(vxvv[3]), vxvv[2] * nu.cos(vxvv[3]) + vxvv[1] * nu.sin(vxvv[3]) ]) #integrate tmp_out, msg = integratePlanarOrbit_c(pot, this_vxvv, t, method, dt=dt) #go back to the cylindrical frame R = nu.sqrt(tmp_out[:, 0]**2. + tmp_out[:, 1]**2.) phi = nu.arccos(tmp_out[:, 0] / R) phi[(tmp_out[:, 1] < 0.)] = 2. * nu.pi - phi[(tmp_out[:, 1] < 0.)] vR = tmp_out[:, 2] * nu.cos(phi) + tmp_out[:, 3] * nu.sin(phi) vT = tmp_out[:, 3] * nu.cos(phi) - tmp_out[:, 2] * nu.sin(phi) out = nu.zeros((len(t), 4)) out[:, 0] = R out[:, 1] = vR out[:, 2] = vT out[:, 3] = phi elif method.lower() == 'odeint' or not ext_loaded: vphi = vxvv[2] / vxvv[0] init = [vxvv[0], vxvv[1], vxvv[3], vphi] intOut = integrate.odeint(_EOM, init, t, args=(pot, ), rtol=10.**-8.) #,mxstep=100000000) out = nu.zeros((len(t), 4)) out[:, 0] = intOut[:, 0] out[:, 1] = intOut[:, 1] out[:, 3] = intOut[:, 2] out[:, 2] = out[:, 0] * intOut[:, 3] msg = 0 else: raise NotImplementedError( "requested integration method does not exist") #post-process to remove negative radii neg_radii = (out[:, 0] < 0.) out[neg_radii, 0] = -out[neg_radii, 0] out[neg_radii, 3] += m.pi _parse_warnmessage(msg) return (out, msg)
def _integrateROrbit(vxvv, pot, t, method, dt): """ NAME: _integrateROrbit PURPOSE: integrate an orbit in a Phi(R) potential in the R-plane INPUT: vxvv - array with the initial conditions stacked like [R,vR,vT]; vR outward! pot - Potential instance t - list of times at which to output (0 has to be in this!) method - 'odeint' or 'leapfrog' dt - if set, force the integrator to use this basic stepsize; must be an integer divisor of output stepsize OUTPUT: [:,3] array of [R,vR,vT] at each t HISTORY: 2010-07-20 - Written - Bovy (NYU) """ #First check that the potential has C if '_c' in method: if not ext_loaded or not _check_c(pot): if ('leapfrog' in method or 'symplec' in method): method = 'leapfrog' else: method = 'odeint' if not ext_loaded: # pragma: no cover warnings.warn( "Cannot use C integration because C extension not loaded (using %s instead)" % (method), galpyWarning) else: warnings.warn( "Cannot use C integration because some of the potentials are not implemented in C (using %s instead)" % (method), galpyWarning) if method.lower() == 'leapfrog': #We hack this by putting in a dummy phi this_vxvv = nu.zeros(len(vxvv) + 1) this_vxvv[0:len(vxvv)] = vxvv tmp_out, msg = _integrateOrbit(this_vxvv, pot, t, method, dt) #tmp_out is (nt,4) out = tmp_out[:, 0:3] elif ext_loaded and \ (method.lower() == 'leapfrog_c' or method.lower() == 'rk4_c' \ or method.lower() == 'rk6_c' or method.lower() == 'symplec4_c' \ or method.lower() == 'symplec6_c' or method.lower() == 'dopr54_c'): #We hack this by putting in a dummy phi this_vxvv = nu.zeros(len(vxvv) + 1) this_vxvv[0:len(vxvv)] = vxvv tmp_out, msg = _integrateOrbit(this_vxvv, pot, t, method, dt) #tmp_out is (nt,4) out = tmp_out[:, 0:3] elif method.lower() == 'odeint' or not ext_loaded: l = vxvv[0] * vxvv[2] l2 = l**2. init = [vxvv[0], vxvv[1]] intOut = integrate.odeint(_REOM, init, t, args=(pot, l2), rtol=10.**-8.) #,mxstep=100000000) out = nu.zeros((len(t), 3)) out[:, 0] = intOut[:, 0] out[:, 1] = intOut[:, 1] out[:, 2] = l / out[:, 0] msg = 0 #post-process to remove negative radii neg_radii = (out[:, 0] < 0.) out[neg_radii, 0] = -out[neg_radii, 0] _parse_warnmessage(msg) return (out, msg)
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 delta= (object-wide default) can be used to override the object-wide focal length; can also be an array with length N to allow different delta for different phase-space points u0= (None) if object-wide option useu0 is set, u0 to use (if useu0 and useu0 is None, a good value will be computed) c= (object-wide default, bool) True/False to override the object-wide setting for whether or not to use the C implementation order= (object-wide default, int) number of points to use in the Gauss-Legendre numerical integration of the relevant action integrals When not using C: fixed_quad= (False) if True, use Gaussian quadrature (scipy.integrate.fixed_quad instead of scipy.integrate.quad) scipy.integrate.fixed_quad or .quad keywords OUTPUT: (jr,lz,jz) HISTORY: 2012-11-27 - Written - Bovy (IAS) 2017-12-27 - Allowed individual delta for each point - Bovy (UofT) """ delta= kwargs.pop('delta',self._delta) order= kwargs.get('order',self._order) if ((self._c and not ('c' in kwargs and not kwargs['c']))\ or (ext_loaded and (('c' in kwargs and kwargs['c'])))) \ and _check_c(self._pot): 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 if isinstance(R,float): R= nu.array([R]) vR= nu.array([vR]) vT= nu.array([vT]) z= nu.array([z]) vz= nu.array([vz]) Lz= R*vT if self._useu0: #First calculate u0 if 'u0' in kwargs: u0= nu.asarray(kwargs['u0']) else: E= nu.array([_evaluatePotentials(self._pot,R[ii],z[ii]) +vR[ii]**2./2.+vz[ii]**2./2.+vT[ii]**2./2. for ii in range(len(R))]) u0= actionAngleStaeckel_c.actionAngleStaeckel_calcu0(\ E,Lz,self._pot,delta)[0] kwargs.pop('u0',None) else: u0= None jr, jz, err= actionAngleStaeckel_c.actionAngleStaeckel_c(\ self._pot,delta,R,vR,vT,z,vz,u0=u0,order=order) if err == 0: return (jr,Lz,jz) else: #pragma: no cover raise RuntimeError("C-code for calculation actions failed; try with c=False") else: if 'c' in kwargs and kwargs['c'] and not self._c: #pragma: no cover warnings.warn("C module not used because potential does not have a C implementation",galpyWarning) kwargs.pop('c',None) if (len(args) == 5 or len(args) == 6) \ and isinstance(args[0],nu.ndarray): ojr= nu.zeros((len(args[0]))) olz= nu.zeros((len(args[0]))) ojz= nu.zeros((len(args[0]))) for ii in range(len(args[0])): if len(args) == 5: targs= (args[0][ii],args[1][ii],args[2][ii], args[3][ii],args[4][ii]) elif len(args) == 6: targs= (args[0][ii],args[1][ii],args[2][ii], args[3][ii],args[4][ii],args[5][ii]) tkwargs= copy.copy(kwargs) try: tkwargs['delta']= delta[ii] except TypeError: tkwargs['delta']= delta tjr,tlz,tjz= self(*targs,**tkwargs) ojr[ii]= tjr ojz[ii]= tjz olz[ii]= tlz return (ojr,olz,ojz) else: #Set up the actionAngleStaeckelSingle object aASingle= actionAngleStaeckelSingle(*args,pot=self._pot, delta=delta) return (aASingle.JR(**copy.copy(kwargs)), aASingle._R*aASingle._vT, aASingle.Jz(**copy.copy(kwargs)))
def _integrateOrbit(vxvv,pot,t,method,dt): """ NAME: _integrateOrbit PURPOSE: integrate an orbit in a Phi(R) potential in the (R,phi)-plane INPUT: vxvv - array with the initial conditions stacked like [R,vR,vT,phi]; vR outward! pot - Potential instance t - list of times at which to output (0 has to be in this!) method - 'odeint' or 'leapfrog' dt- if set, force the integrator to use this basic stepsize; must be an integer divisor of output stepsize OUTPUT: [:,4] array of [R,vR,vT,phi] at each t HISTORY: 2010-07-20 - Written - Bovy (NYU) """ #First check that the potential has C if '_c' in method: if not ext_loaded or not _check_c(pot): if ('leapfrog' in method or 'symplec' in method): method= 'leapfrog' else: method= 'odeint' if not ext_loaded: # pragma: no cover warnings.warn("Cannot use C integration because C extension not loaded (using %s instead)" % (method), galpyWarning) else: warnings.warn("Cannot use C integration because some of the potentials are not implemented in C (using %s instead)" % (method), galpyWarning) if method.lower() == 'leapfrog': #go to the rectangular frame this_vxvv= nu.array([vxvv[0]*nu.cos(vxvv[3]), vxvv[0]*nu.sin(vxvv[3]), vxvv[1]*nu.cos(vxvv[3])-vxvv[2]*nu.sin(vxvv[3]), vxvv[2]*nu.cos(vxvv[3])+vxvv[1]*nu.sin(vxvv[3])]) #integrate tmp_out= symplecticode.leapfrog(_rectForce,this_vxvv, t,args=(pot,),rtol=10.**-8) #go back to the cylindrical frame R= nu.sqrt(tmp_out[:,0]**2.+tmp_out[:,1]**2.) phi= nu.arccos(tmp_out[:,0]/R) phi[(tmp_out[:,1] < 0.)]= 2.*nu.pi-phi[(tmp_out[:,1] < 0.)] vR= tmp_out[:,2]*nu.cos(phi)+tmp_out[:,3]*nu.sin(phi) vT= tmp_out[:,3]*nu.cos(phi)-tmp_out[:,2]*nu.sin(phi) out= nu.zeros((len(t),4)) out[:,0]= R out[:,1]= vR out[:,2]= vT out[:,3]= phi msg= 0 elif ext_loaded and \ (method.lower() == 'leapfrog_c' or method.lower() == 'rk4_c' \ or method.lower() == 'rk6_c' or method.lower() == 'symplec4_c' \ or method.lower() == 'symplec6_c' or method.lower() == 'dopr54_c' \ or method.lower() == 'dop853_c'): warnings.warn("Using C implementation to integrate orbits",galpyWarningVerbose) #go to the rectangular frame this_vxvv= nu.array([vxvv[0]*nu.cos(vxvv[3]), vxvv[0]*nu.sin(vxvv[3]), vxvv[1]*nu.cos(vxvv[3])-vxvv[2]*nu.sin(vxvv[3]), vxvv[2]*nu.cos(vxvv[3])+vxvv[1]*nu.sin(vxvv[3])]) #integrate tmp_out, msg= integratePlanarOrbit_c(pot,this_vxvv, t,method,dt=dt) #go back to the cylindrical frame R= nu.sqrt(tmp_out[:,0]**2.+tmp_out[:,1]**2.) phi= nu.arccos(tmp_out[:,0]/R) phi[(tmp_out[:,1] < 0.)]= 2.*nu.pi-phi[(tmp_out[:,1] < 0.)] vR= tmp_out[:,2]*nu.cos(phi)+tmp_out[:,3]*nu.sin(phi) vT= tmp_out[:,3]*nu.cos(phi)-tmp_out[:,2]*nu.sin(phi) out= nu.zeros((len(t),4)) out[:,0]= R out[:,1]= vR out[:,2]= vT out[:,3]= phi elif method.lower() == 'odeint' or method.lower() == 'dop853' or not ext_loaded: vphi= vxvv[2]/vxvv[0] init= [vxvv[0],vxvv[1],vxvv[3],vphi] if method == 'dop853': intOut = dop853(_EOM, init, t, args=(pot,)) else: intOut= integrate.odeint(_EOM,init,t,args=(pot,), rtol=10.**-8.)#,mxstep=100000000) out= nu.zeros((len(t),4)) out[:,0]= intOut[:,0] out[:,1]= intOut[:,1] out[:,3]= intOut[:,2] out[:,2]= out[:,0]*intOut[:,3] msg= 0 else: raise NotImplementedError("requested integration method does not exist") #post-process to remove negative radii neg_radii= (out[:,0] < 0.) out[neg_radii,0]= -out[neg_radii,0] out[neg_radii,3]+= m.pi _parse_warnmessage(msg) return (out,msg)
def _actionsFreqsAngles(self,*args,**kwargs): """ NAME: actionsFreqsAngles (_actionsFreqsAngles) PURPOSE: evaluate the actions, frequencies, and angles (jr,lz,jz,Omegar,Omegaphi,Omegaz,angler,anglephi,anglez) 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 delta= (object-wide default) can be used to override the object-wide focal length; can also be an array with length N to allow different delta for different phase-space points u0= (None) if object-wide option useu0 is set, u0 to use (if useu0 and useu0 is None, a good value will be computed) c= (object-wide default, bool) True/False to override the object-wide setting for whether or not to use the C implementation order= (10) number of points to use in the Gauss-Legendre numerical integration of the relevant action, frequency, and angle integrals When not using C: fixed_quad= (False) if True, use Gaussian quadrature (scipy.integrate.fixed_quad instead of scipy.integrate.quad) scipy.integrate.fixed_quad or .quad keywords OUTPUT: (jr,lz,jz,Omegar,Omegaphi,Omegaz,angler,anglephi,anglez) HISTORY: 2013-08-28 - Written - Bovy (IAS) """ delta= kwargs.pop('delta',self._delta) order= kwargs.get('order',self._order) if ((self._c and not ('c' in kwargs and not kwargs['c']))\ or (ext_loaded and (('c' in kwargs and kwargs['c'])))) \ and _check_c(self._pot): if len(args) == 5: #R,vR.vT, z, vz pragma: no cover raise IOError("Must specify phi") 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 phi= self._eval_phi if isinstance(R,float): R= nu.array([R]) vR= nu.array([vR]) vT= nu.array([vT]) z= nu.array([z]) vz= nu.array([vz]) phi= nu.array([phi]) Lz= R*vT if self._useu0: #First calculate u0 if 'u0' in kwargs: u0= nu.asarray(kwargs['u0']) else: E= nu.array([_evaluatePotentials(self._pot,R[ii],z[ii]) +vR[ii]**2./2.+vz[ii]**2./2.+vT[ii]**2./2. for ii in range(len(R))]) u0= actionAngleStaeckel_c.actionAngleStaeckel_calcu0(\ E,Lz,self._pot,delta)[0] kwargs.pop('u0',None) else: u0= None jr, jz, Omegar, Omegaphi, Omegaz, angler, anglephi,anglez, err= actionAngleStaeckel_c.actionAngleFreqAngleStaeckel_c(\ self._pot,delta,R,vR,vT,z,vz,phi,u0=u0,order=order) # Adjustements for close-to-circular orbits indx= nu.isnan(Omegar)*(jr < 10.**-3.)+nu.isnan(Omegaz)*(jz < 10.**-3.) #Close-to-circular and close-to-the-plane orbits if nu.sum(indx) > 0: Omegar[indx]= [epifreq(self._pot,r,use_physical=False) for r in R[indx]] Omegaphi[indx]= [omegac(self._pot,r,use_physical=False) for r in R[indx]] Omegaz[indx]= [verticalfreq(self._pot,r,use_physical=False) for r in R[indx]] if err == 0: return (jr,Lz,jz,Omegar,Omegaphi,Omegaz,angler,anglephi,anglez) else: raise RuntimeError("C-code for calculation actions failed; try with c=False") #pragma: no cover else: #pragma: no cover if 'c' in kwargs and kwargs['c'] and not self._c: #pragma: no cover warnings.warn("C module not used because potential does not have a C implementation",galpyWarning) raise NotImplementedError("actionsFreqs with c=False not implemented")
def test_dynamfric_c(): import copy from galpy.orbit import Orbit from galpy.potential.Potential import _check_c from galpy.potential.mwpotentials import McMillan17 #Basic parameters for the test times = numpy.linspace(0., -100., 1001) #~3 Gyr at the Solar circle integrator = 'dop853_c' py_integrator = 'dop853' #Define all of the potentials (by hand, because need reasonable setup) MWPotential3021 = copy.deepcopy(potential.MWPotential2014) MWPotential3021[2] *= 1.5 # Increase mass by 50% pots= [potential.LogarithmicHaloPotential(normalize=1), potential.LogarithmicHaloPotential(normalize=1.3, q=0.9,b=0.7), #nonaxi potential.NFWPotential(normalize=1.,a=1.5), potential.MiyamotoNagaiPotential(normalize=.02,a=10.,b=10.), potential.MiyamotoNagaiPotential(normalize=.6,a=0.,b=3.), # special case potential.PowerSphericalPotential(alpha=2.3,normalize=2.), potential.DehnenSphericalPotential(normalize=4.,alpha=1.2), potential.DehnenCoreSphericalPotential(normalize=4.), potential.HernquistPotential(normalize=1.,a=3.5), potential.JaffePotential(normalize=1.,a=20.5), potential.DoubleExponentialDiskPotential(normalize=0.2, hr=3.,hz=0.6), potential.FlattenedPowerPotential(normalize=3.), potential.FlattenedPowerPotential(normalize=3.,alpha=0), #special case potential.IsochronePotential(normalize=2.), potential.PowerSphericalPotentialwCutoff(normalize=0.3,rc=10.), potential.PlummerPotential(normalize=.6,b=3.), potential.PseudoIsothermalPotential(normalize=.1,a=3.), potential.BurkertPotential(normalize=.2,a=2.5), potential.TriaxialHernquistPotential(normalize=1.,a=3.5, b=0.8,c=0.9), potential.TriaxialNFWPotential(normalize=1.,a=1.5,b=0.8,c=0.9), potential.TriaxialJaffePotential(normalize=1.,a=20.5,b=0.8,c=1.4), potential.PerfectEllipsoidPotential(normalize=.3,a=3.,b=0.7,c=1.5), potential.PerfectEllipsoidPotential(normalize=.3,a=3.,b=0.7,c=1.5, pa=3.,zvec=[0.,1.,0.]), #rotated potential.HomogeneousSpherePotential(normalize=0.02,R=82./8), # make sure to go to dens = 0 part, potential.interpSphericalPotential(\ rforce=potential.HomogeneousSpherePotential(normalize=0.02, R=82./8.), rgrid=numpy.linspace(0.,82./8.,201)), potential.TriaxialGaussianPotential(normalize=.03,sigma=4.,b=0.8,c=1.5,pa=3.,zvec=[1.,0.,0.]), potential.SCFPotential(Acos=numpy.array([[[1.]]]), # same as Hernquist normalize=1.,a=3.5), potential.SCFPotential(Acos=numpy.array([[[1.,0.],[.3,0.]]]), # nonaxi Asin=numpy.array([[[0.,0.],[1e-1,0.]]]), normalize=1.,a=3.5), MWPotential3021, McMillan17 # SCF + DiskSCF ] #tolerances in log10 tol = {} tol['default'] = -7. # Following are a little more difficult tol['DoubleExponentialDiskPotential'] = -4.5 tol['TriaxialHernquistPotential'] = -6. tol['TriaxialNFWPotential'] = -6. tol['TriaxialJaffePotential'] = -6. tol['MWPotential3021'] = -6. tol['HomogeneousSpherePotential'] = -6. tol['interpSphericalPotential'] = -6. # == HomogeneousSpherePotential tol['McMillan17'] = -6. for p in pots: if not _check_c(p, dens=True): continue # dynamfric not in C! pname = type(p).__name__ if pname == 'list': if isinstance(p[0],potential.PowerSphericalPotentialwCutoff) \ and len(p) > 1 \ and isinstance(p[1],potential.MiyamotoNagaiPotential) \ and len(p) > 2 \ and isinstance(p[2],potential.NFWPotential): pname = 'MWPotential3021' # Must be! else: pname = 'McMillan17' #print(pname) if pname in list(tol.keys()): ttol = tol[pname] else: ttol = tol['default'] # Setup orbit, ~ LMC o = Orbit([ 5.13200034, 1.08033051, 0.23323391, -3.48068653, 0.94950884, -1.54626091 ]) # Setup dynamical friction object if pname == 'McMillan17': cdf= potential.ChandrasekharDynamicalFrictionForce(\ GMs=0.5553870441722593,rhm=5./8.,dens=p,maxr=500./8,nr=101) ttimes = numpy.linspace(0., -30., 1001) #~1 Gyr at the Solar circle else: cdf= potential.ChandrasekharDynamicalFrictionForce(\ GMs=0.5553870441722593,rhm=5./8.,dens=p,maxr=500./8,nr=201) ttimes = times # Integrate in C o.integrate(ttimes, p + cdf, method=integrator) # Integrate in Python op = o() op.integrate(ttimes, p + cdf, method=py_integrator) # Compare r (most important) assert numpy.amax(numpy.fabs(o.r(ttimes)-op.r(ttimes))) < 10**ttol, \ 'Dynamical friction in C does not agree with dynamical friction in Python for potential {}'.format(pname) 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 c= (object-wide default, bool) True/False to override the object-wide setting for whether or not to use the C implementation scipy.integrate.quadrature keywords _justjr, _justjz= if True, only calculate the radial or vertical action (internal use) OUTPUT: (jr,lz,jz) HISTORY: 2012-07-26 - Written - Bovy (IAS@MPIA) """ 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 if isinstance(R, float): R = nu.array([R]) vR = nu.array([vR]) vT = nu.array([vT]) z = nu.array([z]) vz = nu.array([vz]) if ((self._c and not ('c' in kwargs and not kwargs['c']))\ or (ext_loaded and (('c' in kwargs and kwargs['c'])))) \ and _check_c(self._pot): Lz = R * vT jr, jz, err= actionAngleAdiabatic_c.actionAngleAdiabatic_c(\ self._pot,self._gamma,R,vR,vT,z,vz) if err == 0: return (jr, Lz, jz) else: #pragma: no cover raise RuntimeError( "C-code for calculation actions failed; try with c=False") else: if 'c' in kwargs and kwargs['c'] and not self._c: warnings.warn( "C module not used because potential does not have a C implementation", galpyWarning) #pragma: no cover kwargs.pop('c', None) if len(R) > 1: ojr = nu.zeros((len(R))) olz = nu.zeros((len(R))) ojz = nu.zeros((len(R))) for ii in range(len(R)): targs = (R[ii], vR[ii], vT[ii], z[ii], vz[ii]) tjr, tlz, tjz = self(*targs, **copy.copy(kwargs)) ojr[ii] = tjr ojz[ii] = tjz olz[ii] = tlz return (ojr, olz, ojz) else: #Set up the actionAngleAxi object thispot = toPlanarPotential(self._pot) thisverticalpot = toVerticalPotential(self._pot, R[0]) aAAxi = actionAngleAxi(R[0], vR[0], vT[0], z[0], vz[0], pot=thispot, verticalPot=thisverticalpot, gamma=self._gamma) if kwargs.get('_justjr', False): kwargs.pop('_justjr') return (aAAxi.JR(**kwargs), nu.nan, nu.nan) elif kwargs.get('_justjz', False): kwargs.pop('_justjz') return (nu.atleast_1d(nu.nan), nu.atleast_1d(nu.nan), nu.atleast_1d(aAAxi.Jz(**kwargs))) else: return (nu.atleast_1d(aAAxi.JR(**kwargs)), nu.atleast_1d(aAAxi._R * aAAxi._vT), nu.atleast_1d(aAAxi.Jz(**kwargs)))
def _EccZmaxRperiRap(self, *args, **kwargs): """ NAME: EccZmaxRperiRap (_EccZmaxRperiRap) PURPOSE: evaluate the eccentricity, maximum height above the plane, peri- and apocenter in the adiabatic approximation 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 c= (object-wide default, bool) True/False to override the object-wide setting for whether or not to use the C implementation OUTPUT: (e,zmax,rperi,rap) HISTORY: 2017-12-21 - Written - Bovy (UofT) """ 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 if isinstance(R, float): R = nu.array([R]) vR = nu.array([vR]) vT = nu.array([vT]) z = nu.array([z]) vz = nu.array([vz]) if ((self._c and not ('c' in kwargs and not kwargs['c']))\ or (ext_loaded and (('c' in kwargs and kwargs['c'])))) \ and _check_c(self._pot): rperi,Rap,zmax, err= actionAngleAdiabatic_c.actionAngleRperiRapZmaxAdiabatic_c(\ self._pot,self._gamma,R,vR,vT,z,vz) if err == 0: rap = nu.sqrt(Rap**2. + zmax**2.) ecc = (rap - rperi) / (rap + rperi) return (ecc, zmax, rperi, rap) else: #pragma: no cover raise RuntimeError( "C-code for calculation actions failed; try with c=False") else: if 'c' in kwargs and kwargs['c'] and not self._c: warnings.warn( "C module not used because potential does not have a C implementation", galpyWarning) #pragma: no cover kwargs.pop('c', None) if len(R) > 1: oecc = nu.zeros((len(R))) orperi = nu.zeros((len(R))) orap = nu.zeros((len(R))) ozmax = nu.zeros((len(R))) for ii in range(len(R)): targs = (R[ii], vR[ii], vT[ii], z[ii], vz[ii]) tecc, tzmax, trperi,trap= self._EccZmaxRperiRap(\ *targs,**copy.copy(kwargs)) oecc[ii] = tecc ozmax[ii] = tzmax orperi[ii] = trperi orap[ii] = trap return (oecc, ozmax, orperi, orap) else: #Set up the actionAngleAxi object thispot = toPlanarPotential(self._pot) thisverticalpot = toVerticalPotential(self._pot, R[0]) aAAxi = actionAngleAxi(R[0], vR[0], vT[0], z[0], vz[0], pot=thispot, verticalPot=thisverticalpot, gamma=self._gamma) rperi, Rap = aAAxi.calcRapRperi(**kwargs) zmax = aAAxi.calczmax(**kwargs) rap = nu.sqrt(Rap**2. + zmax**2.) return (nu.atleast_1d( (rap - rperi) / (rap + rperi)), nu.atleast_1d(zmax), nu.atleast_1d(rperi), nu.atleast_1d(rap))
def _integrateFullOrbit(vxvv, pot, t, method, dt): """ NAME: _integrateFullOrbit PURPOSE: integrate an orbit in a Phi(R,z,phi) potential INPUT: vxvv - array with the initial conditions stacked like [R,vR,vT,z,vz,phi]; vR outward! pot - Potential instance t - list of times at which to output (0 has to be in this!) method - 'odeint' or 'leapfrog' dt - if set, force the integrator to use this basic stepsize; must be an integer divisor of output stepsize OUTPUT: [:,5] array of [R,vR,vT,z,vz,phi] at each t HISTORY: 2010-08-01 - Written - Bovy (NYU) """ #First check that the potential has C if '_c' in method: if not ext_loaded or not _check_c(pot): if ('leapfrog' in method or 'symplec' in method): method = 'leapfrog' else: method = 'odeint' if not ext_loaded: # pragma: no cover warnings.warn( "Cannot use C integration because C extension not loaded (using %s instead)" % (method), galpyWarning) else: warnings.warn( "Cannot use C integration because some of the potentials are not implemented in C (using %s instead)" % (method), galpyWarning) # Now check that we aren't trying to integrate a dissipative force # with a symplectic integrator if _isDissipative(pot) and ('leapfrog' in method or 'symplec' in method): if '_c' in method: method = 'dopr54_c' else: method = 'odeint' warnings.warn( "Cannot use symplectic integration because some of the included forces are dissipative (using non-symplectic integrator %s instead)" % (method), galpyWarning) if method.lower() == 'leapfrog': #go to the rectangular frame this_vxvv = nu.array([ vxvv[0] * nu.cos(vxvv[5]), vxvv[0] * nu.sin(vxvv[5]), vxvv[3], vxvv[1] * nu.cos(vxvv[5]) - vxvv[2] * nu.sin(vxvv[5]), vxvv[2] * nu.cos(vxvv[5]) + vxvv[1] * nu.sin(vxvv[5]), vxvv[4] ]) #integrate out = symplecticode.leapfrog(_rectForce, this_vxvv, t, args=(pot, ), rtol=10.**-8) #go back to the cylindrical frame R = nu.sqrt(out[:, 0]**2. + out[:, 1]**2.) phi = nu.arccos(out[:, 0] / R) phi[(out[:, 1] < 0.)] = 2. * nu.pi - phi[(out[:, 1] < 0.)] vR = out[:, 3] * nu.cos(phi) + out[:, 4] * nu.sin(phi) vT = out[:, 4] * nu.cos(phi) - out[:, 3] * nu.sin(phi) out[:, 3] = out[:, 2] out[:, 4] = out[:, 5] out[:, 0] = R out[:, 1] = vR out[:, 2] = vT out[:, 5] = phi elif ext_loaded and \ (method.lower() == 'leapfrog_c' or method.lower() == 'rk4_c' \ or method.lower() == 'rk6_c' or method.lower() == 'symplec4_c' \ or method.lower() == 'symplec6_c' or method.lower() == 'dopr54_c'): warnings.warn("Using C implementation to integrate orbits", galpyWarningVerbose) #go to the rectangular frame this_vxvv = nu.array([ vxvv[0] * nu.cos(vxvv[5]), vxvv[0] * nu.sin(vxvv[5]), vxvv[3], vxvv[1] * nu.cos(vxvv[5]) - vxvv[2] * nu.sin(vxvv[5]), vxvv[2] * nu.cos(vxvv[5]) + vxvv[1] * nu.sin(vxvv[5]), vxvv[4] ]) #integrate tmp_out, msg = integrateFullOrbit_c(pot, this_vxvv, t, method, dt=dt) #go back to the cylindrical frame R = nu.sqrt(tmp_out[:, 0]**2. + tmp_out[:, 1]**2.) phi = nu.arccos(tmp_out[:, 0] / R) phi[(tmp_out[:, 1] < 0.)] = 2. * nu.pi - phi[(tmp_out[:, 1] < 0.)] vR = tmp_out[:, 3] * nu.cos(phi) + tmp_out[:, 4] * nu.sin(phi) vT = tmp_out[:, 4] * nu.cos(phi) - tmp_out[:, 3] * nu.sin(phi) out = nu.zeros((len(t), 6)) out[:, 0] = R out[:, 1] = vR out[:, 2] = vT out[:, 5] = phi out[:, 3] = tmp_out[:, 2] out[:, 4] = tmp_out[:, 5] elif method.lower() == 'odeint' or not ext_loaded: vphi = vxvv[2] / vxvv[0] init = [vxvv[0], vxvv[1], vxvv[5], vphi, vxvv[3], vxvv[4]] intOut = integrate.odeint(_FullEOM, init, t, args=(pot, ), rtol=10.**-8.) #,mxstep=100000000) out = nu.zeros((len(t), 6)) out[:, 0] = intOut[:, 0] out[:, 1] = intOut[:, 1] out[:, 2] = out[:, 0] * intOut[:, 3] out[:, 3] = intOut[:, 4] out[:, 4] = intOut[:, 5] out[:, 5] = intOut[:, 2] #post-process to remove negative radii neg_radii = (out[:, 0] < 0.) out[neg_radii, 0] = -out[neg_radii, 0] out[neg_radii, 5] += m.pi return out
def _uminumaxvmin(self,*args,**kwargs): """ NAME: _uminumaxvmin PURPOSE: evaluate u_min, u_max, and v_min 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 c= True/False; overrides the object's c= keyword to use C or not OUTPUT: (umin,umax,vmin) HISTORY: 2017-12-12 - Written - Bovy (UofT) """ delta= kwargs.pop('delta',self._delta) if ((self._c and not ('c' in kwargs and not kwargs['c']))\ or (ext_loaded and (('c' in kwargs and kwargs['c'])))) \ and _check_c(self._pot): 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 if isinstance(R,float): R= nu.array([R]) vR= nu.array([vR]) vT= nu.array([vT]) z= nu.array([z]) vz= nu.array([vz]) Lz= R*vT if self._useu0: #First calculate u0 if 'u0' in kwargs: u0= nu.asarray(kwargs['u0']) else: E= nu.array([_evaluatePotentials(self._pot,R[ii],z[ii]) +vR[ii]**2./2.+vz[ii]**2./2.+vT[ii]**2./2. for ii in range(len(R))]) u0= actionAngleStaeckel_c.actionAngleStaeckel_calcu0(\ E,Lz,self._pot,delta)[0] kwargs.pop('u0',None) else: u0= None umin, umax, vmin, err= \ actionAngleStaeckel_c.actionAngleUminUmaxVminStaeckel_c(\ self._pot,delta,R,vR,vT,z,vz,u0=u0) if err == 0: return (umin,umax,vmin) else: #pragma: no cover raise RuntimeError("C-code for calculation actions failed; try with c=False") else: if 'c' in kwargs and kwargs['c'] and not self._c: #pragma: no cover warnings.warn("C module not used because potential does not have a C implementation",galpyWarning) kwargs.pop('c',None) if (len(args) == 5 or len(args) == 6) \ and isinstance(args[0],nu.ndarray): oumin= nu.zeros((len(args[0]))) oumax= nu.zeros((len(args[0]))) ovmin= nu.zeros((len(args[0]))) for ii in range(len(args[0])): if len(args) == 5: targs= (args[0][ii],args[1][ii],args[2][ii], args[3][ii],args[4][ii]) elif len(args) == 6: targs= (args[0][ii],args[1][ii],args[2][ii], args[3][ii],args[4][ii],args[5][ii]) tkwargs= copy.copy(kwargs) try: tkwargs['delta']= delta[ii] except TypeError: tkwargs['delta']= delta tumin,tumax,tvmin= self._uminumaxvmin(\ *targs,**tkwargs) oumin[ii]= tumin oumax[ii]= tumax ovmin[ii]= tvmin return (oumin,oumax,ovmin) else: #Set up the actionAngleStaeckelSingle object aASingle= actionAngleStaeckelSingle(*args,pot=self._pot, delta=delta) umin, umax= aASingle.calcUminUmax() vmin= aASingle.calcVmin() return (umin,umax,vmin)