def test_dens_in_msolpc3(): #Test the scaling, as a 2nd derivative of the potential / G, should scale as velocity^2/position^2 vofid, rofid = 200., 8. assert numpy.fabs(4. * bovy_conversion.dens_in_msolpc3(vofid, rofid) / bovy_conversion.dens_in_msolpc3(2. * vofid, rofid) - 1. ) < 10.**-10., 'dens_in_msolpc3 did not work as expected' assert numpy.fabs(.25 * bovy_conversion.dens_in_msolpc3(vofid, rofid) / bovy_conversion.dens_in_msolpc3(vofid, 2 * rofid) - 1. ) < 10.**-10., 'dens_in_msolpc3 did not work as expected' return None
def test_units(): import galpy.util.bovy_conversion as conversion print(conversion.force_in_pcMyr2(220.,8.))#pc/Myr^2 assert numpy.fabs(conversion.force_in_pcMyr2(220.,8.)-6.32793804994) < 10.**-4., 'unit conversion has changed' print(conversion.dens_in_msolpc3(220.,8.))#Msolar/pc^3 assert numpy.fabs(conversion.dens_in_msolpc3(220.,8.)-0.175790330079) < 10.**-4., 'unit conversion has changed' print(conversion.surfdens_in_msolpc2(220.,8.))#Msolar/pc^2 assert numpy.fabs(conversion.surfdens_in_msolpc2(220.,8.)-1406.32264063) < 10.**-4., 'unit conversion has changed' print(conversion.mass_in_1010msol(220.,8.))#10^10 Msolar assert numpy.fabs(conversion.mass_in_1010msol(220.,8.)-9.00046490005) < 10.**-4., 'unit conversion has changed' print(conversion.freq_in_Gyr(220.,8.))#1/Gyr assert numpy.fabs(conversion.freq_in_Gyr(220.,8.)-28.1245845523) < 10.**-4., 'unit conversion has changed' print(conversion.time_in_Gyr(220.,8.))#Gyr assert numpy.fabs(conversion.time_in_Gyr(220.,8.)-0.0355560807712) < 10.**-4., 'unit conversion has changed' return None
def test_units(): import galpy.util.bovy_conversion as conversion print(conversion.force_in_pcMyr2(220.,8.))#pc/Myr^2 assert numpy.fabs(conversion.force_in_pcMyr2(220.,8.)-6.32793804994) < 10.**-4., 'unit conversion has changed' print(conversion.dens_in_msolpc3(220.,8.))#Msolar/pc^3 assert numpy.fabs((conversion.dens_in_msolpc3(220.,8.)-0.175790330079)/0.175790330079) < 5.*10.**-5., 'unit conversion has changed' print(conversion.surfdens_in_msolpc2(220.,8.))#Msolar/pc^2 assert numpy.fabs((conversion.surfdens_in_msolpc2(220.,8.)-1406.32264063)/1406.32264063) < 5.*10.**-5., 'unit conversion has changed' print(conversion.mass_in_1010msol(220.,8.))#10^10 Msolar assert numpy.fabs((conversion.mass_in_1010msol(220.,8.)-9.00046490005)/9.00046490005) < 5.*10.**-5., 'unit conversion has changed' print(conversion.freq_in_Gyr(220.,8.))#1/Gyr assert numpy.fabs(conversion.freq_in_Gyr(220.,8.)-28.1245845523) < 10.**-4., 'unit conversion has changed' print(conversion.time_in_Gyr(220.,8.))#Gyr assert numpy.fabs(conversion.time_in_Gyr(220.,8.)-0.0355560807712) < 10.**-4., 'unit conversion has changed' return None
def __init__(self,K=1.15,F=0.03,D=1.8,amp=1.,ro=None,vo=None): """ NAME: __init__ PURPOSE: Initialize a KGPotential INPUT: K= K parameter (= :math:`2\\pi \\Sigma_{\\mathrm{disk}}`; specify either as surface density or directly as force [i.e., including :math:`2\\pi G`]; can be Quantity) F= F parameter (= :math:`4\\pi\\rho_{\\mathrm{halo}}`; specify either as density or directly as second potential derivative [i.e., including :math:`4\\pi G`]; can be Quantity) D= D parameter (natural units or Quantity length units) amp - an overall amplitude OUTPUT: instance HISTORY: 2010-07-12 - Written - Bovy (NYU) """ linearPotential.__init__(self,amp=amp,ro=ro,vo=vo) if _APY_LOADED and isinstance(D,units.Quantity): D= D.to(units.kpc).value/self._ro if _APY_LOADED and isinstance(K,units.Quantity): try: K= K.to(units.pc/units.Myr**2).value\ /bovy_conversion.force_in_pcMyr2(self._vo,self._ro) except units.UnitConversionError: pass if _APY_LOADED and isinstance(K,units.Quantity): try: K= K.to(units.Msun/units.pc**2).value\ /bovy_conversion.force_in_2piGmsolpc2(self._vo,self._ro) except units.UnitConversionError: raise units.UnitConversionError("Units for K not understood; should be force or surface density") if _APY_LOADED and isinstance(F,units.Quantity): try: F= F.to(units.Msun/units.pc**3).value\ /bovy_conversion.dens_in_msolpc3(self._vo,self._ro)*4.*sc.pi except units.UnitConversionError: pass if _APY_LOADED and isinstance(F,units.Quantity): try: F= F.to(units.km**2/units.s**2/units.kpc**2).value\ *ro**2/vo**2 except units.UnitConversionError: raise units.UnitConversionError("Units for F not understood; should be density") self._K= K self._F= F self._D= D self._D2= self._D**2. self.hasC= True
def __init__(self,Rho,Sigma): try: self.sig= [s.to(u.km/u.s).value/self.InternalUnits['vo'] for s in Sigma] except: #print('''You did not specify units for the velocity dispersion, #so they have been assumed to be in galpy internal units.''') self.sig= Sigma try: self.rho0= [r.to(u.Msun/(u.pc**3)).value/dens_in_msolpc3(self.InternalUnits['vo'], self.InternalUnits['ro']) for r in Rho] except: #print('''You did not specify units for the mid-plane density, #so they have been assumed to be in galpy internal units.''') self.rho0= Rho
def compute_Acos_Asin(n=9, l=19, a=1. / ro, radial_order=40, costheta_order=40, phi_order=40): Acos, Asin = potential.scf_compute_coeffs( lambda R, z, p: rho_bar_cyl(R * 8., z * 8., p) / (10**9. * bovy_conversion.dens_in_msolpc3(220., 8.)), N=n + 1, L=l + 1, a=a, radial_order=radial_order, costheta_order=costheta_order, phi_order=phi_order) return (Acos, Asin)
def writeTable(pot,params,tablename,options,fzrd): outfile= open(tablename,'w') delimiter= ' & ' #First list the explicit parameters printline= '$R_0\,(\kpc)$%s$8$%sfixed\\\\\n' % (delimiter,delimiter) outfile.write(printline) printline= '$v_c(R_0)\,(\kms)$%s$220$%sfixed\\\\\n' % (delimiter,delimiter) outfile.write(printline) if options.twodisks: printline= '$f_{b}$%s$%.2f$%s\ldots\\\\\n' % (delimiter,1.-params[0]-params[1]-params[2],delimiter) outfile.write(printline) printline= '$f_{d,1}$%s$%.2f$%s\ldots\\\\\n' % (delimiter,params[0],delimiter) outfile.write(printline) printline= '$f_{d,2}$%s$%.2f$%s\ldots\\\\\n' % (delimiter,params[1],delimiter) outfile.write(printline) printline= '$f_{h}$%s$%.2f$%s\ldots\\\\\n' % (delimiter,params[2],delimiter) outfile.write(printline) else: printline= '$f_{b}$%s$%.2f$%s\ldots\\\\\n' % (delimiter,1.-params[0]-params[1],delimiter) outfile.write(printline) printline= '$f_{d}$%s%.2f%s\ldots\\\\\n' % (delimiter,params[0],delimiter) outfile.write(printline) printline= '$f_{h}$%s$%.2f$%s\ldots\\\\\n' % (delimiter,params[1],delimiter) outfile.write(printline) #Bulge power-law and rc printline= '$\mathrm{Bulge\ power}$%s$-1.8$%sfixed\\\\\n' % (delimiter,delimiter) outfile.write(printline) printline= '$\mathrm{Bulge\ cut}\,(\kpc)$%s$1.9$%sfixed\\\\\n' % (delimiter,delimiter) outfile.write(printline) #Disk scale length and height printline= '$a\,(\kpc)$%s$%.1f$%s\ldots\\\\\n' % (delimiter,numpy.exp(params[2+options.twodisks])*_REFR0,delimiter) outfile.write(printline) printline= '$b\,(\pc)$%s$%.0f$%s\ldots\\\\\n' % (delimiter,1000.*numpy.exp(params[3+options.twodisks])*_REFR0,delimiter) outfile.write(printline) printline= '$\mathrm{Halo}\ r_s\,(\kpc)$%s$%.0f$%s\ldots\\\\\n' % (delimiter,numpy.exp(params[4+options.twodisks])*_REFR0,delimiter) outfile.write(printline) outfile.write('%s%s\\\\\n' % (delimiter,delimiter)) #Now the constraints printline= '$\sigma_b\,(\kms)$%s$%.0f$%s$117\pm15$\\\\\n' % (delimiter,bulge_dispersion(pot),delimiter) outfile.write(printline) printline= '$F_Z(R_0,1.1\kpc)\,(2\pi G\,M_\odot\pc^{-2})$%s$%.0f$%s$67\pm6$\\\\\n' % (delimiter,-potential.evaluatezforces(1.,1.1/_REFR0,pot)*bovy_conversion.force_in_2piGmsolpc2(_REFV0,_REFR0),delimiter) outfile.write(printline) printline= '$\Sigma_{\mathrm{vis}}(R_0)\,(M_\odot\pc^{-2})$%s$%.0f$%s$55\pm5$\\\\\n' % (delimiter,visible_dens(pot,options),delimiter) outfile.write(printline) printline= '$F_Z\ \mathrm{scale\ length}\,(\kpc)$%s%.1f%s$2.7\pm0.1$\\\\\n' % (delimiter,fzrd*_REFR0,delimiter) outfile.write(printline) printline= '$\\rho(R_0,z=0)\,(M_\odot\pc^{-3})$%s$%.2f$%s$0.10\pm0.01$\\\\\n' % (delimiter,potential.evaluateDensities(1.,0.,pot)*bovy_conversion.dens_in_msolpc3(_REFV0,_REFR0),delimiter) outfile.write(printline) printline= '$(\dd \ln v_c/\dd \ln R)|_{R_0}$%s$%.2f$%s$-0.2\ \mathrm{to}\ 0$\\\\\n' % (delimiter,potential.dvcircdR(pot,1.),delimiter) outfile.write(printline) printline= '$M(r<60\kpc)\,(10^{11}\,M_\odot)$%s$%.1f$%s$4.0\pm0.7$\\\\\n' % (delimiter,mass60(pot,options),delimiter) outfile.write(printline) outfile.write('%s%s\\\\\n' % (delimiter,delimiter)) #Now some derived properties printline= '$M_b\,(10^{10}\,M_\odot)$%s$%.1f$%s\ldots\\\\\n' % (delimiter,pot[0].mass(10.)*bovy_conversion.mass_in_1010msol(_REFV0,_REFR0),delimiter) outfile.write(printline) if options.twodisks: printline= '$M_d\,(10^{10}\,M_\odot)$%s$%.1f$%s\ldots\\\\\n' % (delimiter,(pot[1]._amp+pot[2]._amp)*bovy_conversion.mass_in_1010msol(_REFV0,_REFR0),delimiter) else: printline= '$M_d\,(10^{10}\,M_\odot)$%s$%.1f$%s\ldots\\\\\n' % (delimiter,pot[1]._amp*bovy_conversion.mass_in_1010msol(_REFV0,_REFR0),delimiter) outfile.write(printline) krs= numpy.linspace(4./_REFR0,9./_REFR0,101) disksurf= numpy.array([visible_dens(pot,options,r=kr) for kr in krs]) p= numpy.polyfit(krs,numpy.log(disksurf),1) rd= -1./p[0]*_REFR0 printline= '$R_{d}\,(\kpc)$%s$%.1f$%s\ldots\\\\\n' % (delimiter,rd,delimiter) outfile.write(printline) rvir= pot[2+options.twodisks].rvir(_REFV0,_REFR0,wrtcrit=True,overdens=96.7) printline= '$\\rho_{\mathrm{DM}}(R_0)\,(M_\odot\pc^{-3})$%s$%.3f$%s\\\\\n' % (delimiter,potential.evaluateDensities(1.,0.,pot[2+options.twodisks])*bovy_conversion.dens_in_msolpc3(_REFV0,_REFR0),delimiter) outfile.write(printline) printline= '$M_{\mathrm{vir}}\,(10^{12}\,M_\odot)$%s$%.1f$%s\ldots\\\\\n' % (delimiter,pot[2+options.twodisks].mass(rvir)*bovy_conversion.mass_in_1010msol(_REFV0,_REFR0)/100.,delimiter) outfile.write(printline) printline= '$r_{\mathrm{vir}}\,(\kpc)$%s$%.0f$%s\ldots\\\\\n' % (delimiter,rvir*_REFR0,delimiter) outfile.write(printline) printline= '$\mathrm{Concentration}$%s$%.1f$%s\ldots\\\\\n' % (delimiter,rvir/pot[2+options.twodisks].a,delimiter) outfile.write(printline) printline= '$v_{\mathrm{esc}}(R_0)\,(\kms)$%s$%.0f$%s\ldots\n' % (delimiter,potential.vesc(pot,1.)*_REFV0,delimiter) outfile.write(printline) #printline= '$r_{\mathrm{vir}}\,(\kpc)$%s$%.0f$%s\ldots\\\\\n' % (delimiter,delimiter) #outfile.write(printline) outfile.write('\\enddata\n') pass
def __init__(self, K=1.15, F=0.03, D=1.8, amp=1., ro=None, vo=None): """ NAME: __init__ PURPOSE: Initialize a KGPotential INPUT: K= K parameter (= :math:`2\\pi \\Sigma_{\\mathrm{disk}}`; specify either as surface density or directly as force [i.e., including :math:`2\\pi G`]; can be Quantity) F= F parameter (= :math:`4\\pi\\rho_{\\mathrm{halo}}`; specify either as density or directly as second potential derivative [i.e., including :math:`4\\pi G`]; can be Quantity) D= D parameter (natural units or Quantity length units) amp - an overall amplitude OUTPUT: instance HISTORY: 2010-07-12 - Written - Bovy (NYU) """ linearPotential.__init__(self, amp=amp, ro=ro, vo=vo) if _APY_LOADED and isinstance(D, units.Quantity): D = D.to(units.kpc).value / self._ro if _APY_LOADED and isinstance(K, units.Quantity): try: K= K.to(units.pc/units.Myr**2).value\ /bovy_conversion.force_in_pcMyr2(self._vo,self._ro) except units.UnitConversionError: pass if _APY_LOADED and isinstance(K, units.Quantity): try: K= K.to(units.Msun/units.pc**2).value\ /bovy_conversion.force_in_2piGmsolpc2(self._vo,self._ro) except units.UnitConversionError: raise units.UnitConversionError( "Units for K not understood; should be force or surface density" ) if _APY_LOADED and isinstance(F, units.Quantity): try: F= F.to(units.Msun/units.pc**3).value\ /bovy_conversion.dens_in_msolpc3(self._vo,self._ro)*4.*sc.pi except units.UnitConversionError: pass if _APY_LOADED and isinstance(F, units.Quantity): try: F= F.to(units.km**2/units.s**2/units.kpc**2).value\ *ro**2/vo**2 except units.UnitConversionError: raise units.UnitConversionError( "Units for F not understood; should be density") self._K = K self._F = F self._D = D self._D2 = self._D**2.
def rlimiting(cluster, pot=MWPotential2014, rgc=None, ro=8.0, vo=220.0, nrad=20, projected=False, plot=False, **kwargs): """Calculate limiting radius of the cluster - The limiting radius is defined to be where the cluster's density reaches the local background density of the host galaxy - for cases where the cluster's orbital parameters are not set, it is possible to manually set rgc which is assumed to be in kpc. Parameters ---------- cluster : class StarCluster pot : class GALPY potential used to calculate actions rgc : Manually set galactocentric distance in kpc at which the tidal radius is to be evaluated (default: None) ro : float GALPY radius scaling parameter vo : float GALPY velocity scaling parameter nrad : int number of radial bins used to calculate density profile (Default: 20) projected : bool use projected values (default: False) plot : bool plot the density profile and mark the limiting radius of the cluster (default: False) Returns ------- rl : float limiting radius Other Parameters ---------------- kwargs : str key words for plotting History ------- 2019 - Written - Webb (UofT) """ units0, origin0 = save_cluster(cluster) cluster.to_centre() cluster.to_galpy() if rgc != None: R = rgc / ro z = 0.0 else: R = np.sqrt(cluster.xgc**2.0 + cluster.ygc**2.0) z = cluster.zgc # Calculate local density: rho_local = potential.evaluateDensities( pot, R, z, ro=ro, vo=vo, use_physical=False) / bovy_conversion.dens_in_msolpc3(ro=ro, vo=vo) rprof, pprof, nprof = _rho_prof(cluster, nrad=nrad, projected=projected) if pprof[-1] > rho_local: rl = rprof[-1] elif pprof[0] < rho_local: rl = 0.0 else: indx = np.argwhere(pprof < rho_local)[0][0] r1 = (rprof[indx - 1], pprof[indx - 1]) r2 = (rprof[indx], pprof[indx]) rl = interpolate(r1, r2, y=rho_local) if units0 == "pckms": rl *= 1000.0 * ro elif units0 == "kpckms": rl *= ro elif units0 == "nbody": rl *= 1000.0 * ro / cluster.rbar return_cluster(cluster, units0, origin0, do_order=True, do_key_params=True) if plot: filename = kwargs.pop("filename", None) overplot = kwargs.pop("overplot", False) if cluster.units == "nbody": rprof *= ro * 1000.0 / cluster.rbar pprof *= (bovy_conversion.dens_in_msolpc3(ro=ro, vo=vo) * (cluster.rbar**3.0) / cluster.zmbar) rho_local *= (bovy_conversion.dens_in_msolpc3(ro=ro, vo=vo) * (cluster.rbar**3.0) / cluster.zmbar) xunits = " (NBODY)" yunits = " (NBODY)" elif cluster.units == "pckms": rprof *= ro * 1000.0 pprof *= bovy_conversion.dens_in_msolpc3(ro=ro, vo=vo) rho_local *= bovy_conversion.dens_in_msolpc3(ro=ro, vo=vo) xunits = " (pc)" if projected: yunits = " Msun/pc^2" else: yunits = " Msun/pc^3" elif cluster.units == "kpckms": rprof *= ro pprof *= bovy_conversion.dens_in_msolpc3(ro=ro, vo=vo) * (1000.0** 3.0) rho_local *= bovy_conversion.dens_in_msolpc3(ro=ro, vo=vo) * (1000.0**3.0) xunits = " (kpc)" if projected: yunits = " Msun/kpc^2" else: yunits = " Msun/kpc^3" elif cluster.units == "galpy": xunits = " (GALPY)" yunits = " (GALPY)" else: xunits = "" yunits = "" x, y, n = rprof, pprof, nprof _lplot( x, y, xlabel=r"$R %s$" % (xunits), ylabel=r"$\rho %s$" % (yunits), title="Time = %f" % cluster.tphys, log=True, overplot=overplot, filename=filename, ) _lplot(x, np.ones(len(x)) * rho_local, "--", overplot=True) _lplot(np.ones(len(y)) * rl, y, "--", overplot=True) if filename != None: plt.savefig(filename) return rl
import numpy as np from galpy.potential import DiskSCFPotential, NFWPotential, \ SCFPotential, scf_compute_coeffs_axi from galpy.util import bovy_conversion from SCF_derivs import mySCFPotential, myDiskSCFPotential ro = 8.21 vo = 233.1 sigo = bovy_conversion.surfdens_in_msolpc2(vo=vo, ro=ro) rhoo = bovy_conversion.dens_in_msolpc3(vo=vo, ro=ro) #gas disk parameters (fixed in McMillan (2017)...) Rd_HI = 7/ro Rm_HI = 4/ro zd_HI = 0.085/ro Sigma0_HI = 53.1/sigo Rd_H2 = 1.5/ro Rm_H2 = 12/ro zd_H2 = 0.045/ro Sigma0_H2 = 2180/sigo #parameters of best-fitting model in McMillan (2017) #stellar disks Sigma0_thin = 896/sigo Rd_thin = 2.5/ro zd_thin = 0.3/ro Sigma0_thick = 183/sigo Rd_thick = 3.02/ro zd_thick = 0.9/ro #bulge rho0_bulge = 98.4/rhoo
## Basic import numpy as np import sys, os, pdb ## galpy from galpy import potential from galpy.util import bovy_conversion as gpconv # ---------------------------------------------------------------------------- # Module globals ro=8.21 vo=233.1 sigo = gpconv.surfdens_in_msolpc2(vo=vo, ro=ro) rhoo = gpconv.dens_in_msolpc3(vo=vo, ro=ro) #gas disk parameters (fixed in McMillan (2017)...) Rd_HI = 7/ro Rm_HI = 4/ro zd_HI = 0.085/ro Sigma0_HI = 53.1/sigo Rd_H2 = 1.5/ro Rm_H2 = 12/ro zd_H2 = 0.045/ro Sigma0_H2 = 2180/sigo #parameters of best-fitting model in McMillan (2017) #stellar disks Sigma0_thin = 896/sigo Rd_thin = 2.5/ro
def test_dens_in_msolpc3(): #Test the scaling, as a 2nd derivative of the potential / G, should scale as velocity^2/position^2 vofid, rofid= 200., 8. assert numpy.fabs(4.*bovy_conversion.dens_in_msolpc3(vofid,rofid)/bovy_conversion.dens_in_msolpc3(2.*vofid,rofid)-1.) < 10.**-10., 'dens_in_msolpc3 did not work as expected' assert numpy.fabs(.25*bovy_conversion.dens_in_msolpc3(vofid,rofid)/bovy_conversion.dens_in_msolpc3(vofid,2*rofid)-1.) < 10.**-10., 'dens_in_msolpc3 did not work as expected' return None
def like_func(params,c,surfrs,kzs,kzerrs,termdata,termsigma,fitc,fitvoro, dblexp,addpal5,addgd1,ro,vo,addgas): #Check ranges if params[0] < 0. or params[0] > 1.: return numpy.finfo(numpy.dtype(numpy.float64)).max if params[1] < 0. or params[1] > 1.: return numpy.finfo(numpy.dtype(numpy.float64)).max if (1.-params[0]-params[1]) < 0. or (1.-params[0]-params[1]) > 1.: return numpy.finfo(numpy.dtype(numpy.float64)).max if params[2] < numpy.log(1./_REFR0) or params[2] > numpy.log(8./_REFR0): return numpy.finfo(numpy.dtype(numpy.float64)).max if params[3] < numpy.log(0.05/_REFR0) or params[3] > numpy.log(1./_REFR0): return numpy.finfo(numpy.dtype(numpy.float64)).max if fitvoro and (params[7] <= 150./_REFV0 or params[7] > 290./_REFV0): return numpy.finfo(numpy.dtype(numpy.float64)).max if fitvoro and (params[8] <= 7./_REFR0 or params[8] > 9.4/_REFR0): return numpy.finfo(numpy.dtype(numpy.float64)).max if fitc and (params[7+2*fitvoro] <= 0. or params[7+2*fitvoro] > 4.): return numpy.finfo(numpy.dtype(numpy.float64)).max if fitvoro: ro, vo= _REFR0*params[8], _REFV0*params[7] #Setup potential pot= setup_potential(params,c,fitc,dblexp,ro,vo,fitvoro=fitvoro, addgas=addgas) #Calculate model surface density at surfrs modelkzs= numpy.empty_like(surfrs) for ii in range(len(surfrs)): modelkzs[ii]= -potential.evaluatezforces(pot, (ro-8.+surfrs[ii])/ro, 1.1/ro, phi=0.)*bovy_conversion.force_in_2piGmsolpc2(vo,ro) out= 0.5*numpy.sum((kzs-modelkzs)**2./kzerrs**2.) #Add terminal velocities vrsun= params[5] vtsun= params[6] cl_glon, cl_vterm, cl_corr, mc_glon, mc_vterm, mc_corr= termdata #Calculate terminal velocities at data glon cl_vterm_model= numpy.zeros_like(cl_vterm) for ii in range(len(cl_glon)): cl_vterm_model[ii]= potential.vterm(pot,cl_glon[ii]) cl_vterm_model+= vrsun*numpy.cos(cl_glon/180.*numpy.pi)\ -vtsun*numpy.sin(cl_glon/180.*numpy.pi) mc_vterm_model= numpy.zeros_like(mc_vterm) for ii in range(len(mc_glon)): mc_vterm_model[ii]= potential.vterm(pot,mc_glon[ii]) mc_vterm_model+= vrsun*numpy.cos(mc_glon/180.*numpy.pi)\ -vtsun*numpy.sin(mc_glon/180.*numpy.pi) cl_dvterm= (cl_vterm-cl_vterm_model*vo)/termsigma mc_dvterm= (mc_vterm-mc_vterm_model*vo)/termsigma out+= 0.5*numpy.sum(cl_dvterm*numpy.dot(cl_corr,cl_dvterm)) out+= 0.5*numpy.sum(mc_dvterm*numpy.dot(mc_corr,mc_dvterm)) #Rotation curve constraint out-= logprior_dlnvcdlnr(potential.dvcircdR(pot,1.,phi=0.)) #K dwarfs, Kz out+= 0.5*(-potential.evaluatezforces(pot,1.,1.1/ro,phi=0.)*bovy_conversion.force_in_2piGmsolpc2(vo,ro)-67.)**2./36. #K dwarfs, visible out+= 0.5*(visible_dens(pot,ro,vo)-55.)**2./25. #Local density prior localdens= potential.evaluateDensities(pot,1.,0.,phi=0.)*bovy_conversion.dens_in_msolpc3(vo,ro) out+= 0.5*(localdens-0.102)**2./0.01**2. #Bulge velocity dispersion out+= 0.5*(bulge_dispersion(pot,ro,vo)-117.)**2./225. #Mass at 60 kpc out+= 0.5*(mass60(pot,ro,vo)-4.)**2./0.7**2. #Pal5 if addpal5: # q = 0.94 +/- 0.05 + add'l fp5= force_pal5(pot,23.46,ro,vo) out+= 0.5*(numpy.sqrt(2.*fp5[0]/fp5[1])-0.94)**2./0.05**2. out+= 0.5*(0.94**2.*(fp5[0]+0.8)+2.*(fp5[1]+1.82)+0.2)**2./0.6**2. #GD-1 if addgd1: # q = 0.95 +/- 0.04 + add'l fg1= force_gd1(pot,ro,vo) out+= 0.5*(numpy.sqrt(6.675/12.5*fg1[0]/fg1[1])-0.95)**2./0.04**2. out+= 0.5*(0.95**2.*(fg1[0]+2.51)+6.675/12.5*(fg1[1]+1.47)+0.05)**2./0.3**2. # vc and ro measurements: vc=218 +/- 10 km/s, ro= 8.1 +/- 0.1 kpc out+= (vo-218.)**2./200.+(ro-8.1)**2./0.02 if numpy.isnan(out): return numpy.finfo(numpy.dtype(numpy.float64)).max else: return out
def __init__(self, amp=1., ro=None, vo=None, amp_units=None): """ NAME: __init__ PURPOSE: Initialize Force INPUT: amp - amplitude to be applied when evaluating the potential and its forces ro - physical distance scale (in kpc or as Quantity) vo - physical velocity scale (in km/s or as Quantity) amp_units - ('mass', 'velocity2', 'density') type of units that amp should have if it has units OUTPUT: HISTORY: 2018-03-18 - Written to generalize Potential to force that may or may not be conservative - Bovy (UofT) """ self._amp = amp # Parse ro and vo if ro is None: self._ro = config.__config__.getfloat('normalization', 'ro') self._roSet = False else: if _APY_LOADED and isinstance(ro, units.Quantity): ro = ro.to(units.kpc).value self._ro = ro self._roSet = True if vo is None: self._vo = config.__config__.getfloat('normalization', 'vo') self._voSet = False else: if _APY_LOADED and isinstance(vo, units.Quantity): vo = vo.to(units.km / units.s).value self._vo = vo self._voSet = True # Parse amp if it has units if _APY_LOADED and isinstance(self._amp, units.Quantity): # Try a bunch of possible units unitFound = False # velocity^2 try: self._amp= self._amp.to(units.km**2/units.s**2).value\ /self._vo**2. except units.UnitConversionError: pass else: unitFound = True if not amp_units == 'velocity2': raise units.UnitConversionError( 'amp= parameter of %s should have units of %s, but has units of velocity2 instead' % (type(self).__name__, amp_units)) if not unitFound: # mass try: self._amp= self._amp.to(units.Msun).value\ /bovy_conversion.mass_in_msol(self._vo,self._ro) except units.UnitConversionError: pass else: unitFound = True if not amp_units == 'mass': raise units.UnitConversionError( 'amp= parameter of %s should have units of %s, but has units of mass instead' % (type(self).__name__, amp_units)) if not unitFound: # G x mass try: self._amp= self._amp.to(units.pc*units.km**2/units.s**2)\ .value\ /bovy_conversion.mass_in_msol(self._vo,self._ro)\ /bovy_conversion._G except units.UnitConversionError: pass else: unitFound = True if not amp_units == 'mass': raise units.UnitConversionError( 'amp= parameter of %s should have units of %s, but has units of G x mass instead' % (type(self).__name__, amp_units)) if not unitFound: # density try: self._amp= self._amp.to(units.Msun/units.pc**3).value\ /bovy_conversion.dens_in_msolpc3(self._vo,self._ro) except units.UnitConversionError: pass else: unitFound = True if not amp_units == 'density': raise units.UnitConversionError( 'amp= parameter of %s should have units of %s, but has units of density instead' % (type(self).__name__, amp_units)) if not unitFound: # G x density try: self._amp= self._amp.to(units.km**2/units.s**2\ /units.pc**2).value\ /bovy_conversion.dens_in_msolpc3(self._vo,self._ro)\ /bovy_conversion._G except units.UnitConversionError: pass else: unitFound = True if not amp_units == 'density': raise units.UnitConversionError( 'amp= parameter of %s should have units of %s, but has units of G x density instead' % (type(self).__name__, amp_units)) if not unitFound: # surface density try: self._amp= self._amp.to(units.Msun/units.pc**2).value\ /bovy_conversion.surfdens_in_msolpc2(self._vo,self._ro) except units.UnitConversionError: pass else: unitFound = True if not amp_units == 'surfacedensity': raise units.UnitConversionError( 'amp= parameter of %s should have units of %s, but has units of surface density instead' % (type(self).__name__, amp_units)) if not unitFound: # G x surface density try: self._amp= self._amp.to(units.km**2/units.s**2\ /units.pc).value\ /bovy_conversion.surfdens_in_msolpc2(self._vo,self._ro)\ /bovy_conversion._G except units.UnitConversionError: pass else: unitFound = True if not amp_units == 'surfacedensity': raise units.UnitConversionError( 'amp= parameter of %s should have units of %s, but has units of G x surface density instead' % (type(self).__name__, amp_units)) if not unitFound: raise units.UnitConversionError( 'amp= parameter of %s should have units of %s; given units are not understood' % (type(self).__name__, amp_units)) else: # When amplitude is given with units, turn on physical output self._roSet = True self._voSet = True return None
def like_func(params,surfrs,kzs,kzerrs, termdata,options): #Check ranges if params[0] < 0. or params[0] > 1.: return numpy.finfo(numpy.dtype(numpy.float64)).max if params[1] < 0. or params[1] > 1.: return numpy.finfo(numpy.dtype(numpy.float64)).max if options.twodisks and (params[2] < 0. or params[2] > 1.): return numpy.finfo(numpy.dtype(numpy.float64)).max if (1.-params[0]-params[1]-options.twodisks*params[2]) < 0. or (1.-params[0]-params[1]-options.twodisks*params[2]) > 1.: return numpy.finfo(numpy.dtype(numpy.float64)).max if params[2+options.twodisks] < numpy.log(1./_REFR0) or params[2+options.twodisks] > numpy.log(8./_REFR0): return numpy.finfo(numpy.dtype(numpy.float64)).max if params[3+options.twodisks] < numpy.log(0.05/_REFR0) or params[3+options.twodisks] > numpy.log(1./_REFR0): return numpy.finfo(numpy.dtype(numpy.float64)).max if options.twodisks: if params[5] < numpy.log(1./_REFR0) or params[5] > numpy.log(8./_REFR0): return numpy.finfo(numpy.dtype(numpy.float64)).max if params[6] < numpy.log(0.05/_REFR0) or params[6] > numpy.log(1./_REFR0): return numpy.finfo(numpy.dtype(numpy.float64)).max #Setup potential if options.twodisks: pot= [potential.PowerSphericalPotentialwCutoff(normalize=1.-params[0]-params[1]-params[2], alpha=1.8,rc=1.9/_REFR0), potential.MiyamotoNagaiPotential(normalize=params[0], a=numpy.exp(params[3]), b=numpy.exp(params[4])), potential.MiyamotoNagaiPotential(normalize=params[1], a=numpy.exp(params[5]), b=numpy.exp(params[6])), potential.NFWPotential(normalize=params[2], a=numpy.exp(params[7]))] else: pot= [potential.PowerSphericalPotentialwCutoff(normalize=1.-params[0]-params[1], alpha=1.8,rc=1.9/_REFR0), potential.MiyamotoNagaiPotential(normalize=params[0], a=numpy.exp(params[2]), b=numpy.exp(params[3])), potential.NFWPotential(normalize=params[1],a=numpy.exp(params[4]))] #Calculate model surface density at surfrs modelkzs= numpy.empty_like(surfrs) for ii in range(len(surfrs)): modelkzs[ii]= -potential.evaluatezforces(surfrs[ii]/_REFR0,1.1/_REFR0, pot)*bovy_conversion.force_in_2piGmsolpc2(_REFV0,_REFR0) out= 0.5*numpy.sum((kzs-modelkzs)**2./kzerrs**2.) #Add terminal velocities vrsun= params[5+3*options.twodisks] vtsun= params[6+3*options.twodisks] cl_glon, cl_vterm, cl_corr, mc_glon, mc_vterm, mc_corr= termdata #Calculate terminal velocities at data glon cl_vterm_model= numpy.zeros_like(cl_vterm) for ii in range(len(cl_glon)): cl_vterm_model[ii]= potential.vterm(pot,cl_glon[ii]) cl_vterm_model+= vrsun*numpy.cos(cl_glon/180.*numpy.pi)\ -vtsun*numpy.sin(cl_glon/180.*numpy.pi) mc_vterm_model= numpy.zeros_like(mc_vterm) for ii in range(len(mc_glon)): mc_vterm_model[ii]= potential.vterm(pot,mc_glon[ii]) mc_vterm_model+= vrsun*numpy.cos(mc_glon/180.*numpy.pi)\ -vtsun*numpy.sin(mc_glon/180.*numpy.pi) cl_dvterm= (cl_vterm-cl_vterm_model)/options.termsigma*_REFV0 mc_dvterm= (mc_vterm-mc_vterm_model)/options.termsigma*_REFV0 out+= 0.5*numpy.sum(cl_dvterm*numpy.dot(cl_corr,cl_dvterm)) out+= 0.5*numpy.sum(mc_dvterm*numpy.dot(mc_corr,mc_dvterm)) #Rotation curve constraint out-= logprior_dlnvcdlnr(potential.dvcircdR(pot,1.)) #K dwarfs, Kz out+= 0.5*(-potential.evaluatezforces(1.,1.1/_REFR0,pot)*bovy_conversion.force_in_2piGmsolpc2(_REFV0,_REFR0)-67.)**2./36. #K dwarfs, visible out+= 0.5*(visible_dens(pot,options)-55.)**2./25. #Local density prior localdens= potential.evaluateDensities(1.,0.,pot)*bovy_conversion.dens_in_msolpc3(_REFV0,_REFR0) out+= 0.5*(localdens-0.102)**2./0.01**2. #Bulge velocity dispersion out+= 0.5*(bulge_dispersion(pot)-117.)**2./225. #Mass at 60 kpc out+= 0.5*(mass60(pot,options)-4.)**2./0.7**2. #Concentration prior? return out
def __init__(self,amp=1.,ro=None,vo=None,amp_units=None): """ NAME: __init__ PURPOSE: Initialize Force INPUT: amp - amplitude to be applied when evaluating the potential and its forces ro - physical distance scale (in kpc or as Quantity) vo - physical velocity scale (in km/s or as Quantity) amp_units - ('mass', 'velocity2', 'density') type of units that amp should have if it has units OUTPUT: HISTORY: 2018-03-18 - Written to generalize Potential to force that may or may not be conservative - Bovy (UofT) """ self._amp= amp # Parse ro and vo if ro is None: self._ro= config.__config__.getfloat('normalization','ro') self._roSet= False else: if _APY_LOADED and isinstance(ro,units.Quantity): ro= ro.to(units.kpc).value self._ro= ro self._roSet= True if vo is None: self._vo= config.__config__.getfloat('normalization','vo') self._voSet= False else: if _APY_LOADED and isinstance(vo,units.Quantity): vo= vo.to(units.km/units.s).value self._vo= vo self._voSet= True # Parse amp if it has units if _APY_LOADED and isinstance(self._amp,units.Quantity): # Try a bunch of possible units unitFound= False # velocity^2 try: self._amp= self._amp.to(units.km**2/units.s**2).value\ /self._vo**2. except units.UnitConversionError: pass else: unitFound= True if not amp_units == 'velocity2': raise units.UnitConversionError('amp= parameter of %s should have units of %s, but has units of velocity2 instead' % (type(self).__name__,amp_units)) if not unitFound: # mass try: self._amp= self._amp.to(units.Msun).value\ /bovy_conversion.mass_in_msol(self._vo,self._ro) except units.UnitConversionError: pass else: unitFound= True if not amp_units == 'mass': raise units.UnitConversionError('amp= parameter of %s should have units of %s, but has units of mass instead' % (type(self).__name__,amp_units)) if not unitFound: # G x mass try: self._amp= self._amp.to(units.pc*units.km**2/units.s**2)\ .value\ /bovy_conversion.mass_in_msol(self._vo,self._ro)\ /bovy_conversion._G except units.UnitConversionError: pass else: unitFound= True if not amp_units == 'mass': raise units.UnitConversionError('amp= parameter of %s should have units of %s, but has units of G x mass instead' % (type(self).__name__,amp_units)) if not unitFound: # density try: self._amp= self._amp.to(units.Msun/units.pc**3).value\ /bovy_conversion.dens_in_msolpc3(self._vo,self._ro) except units.UnitConversionError: pass else: unitFound= True if not amp_units == 'density': raise units.UnitConversionError('amp= parameter of %s should have units of %s, but has units of density instead' % (type(self).__name__,amp_units)) if not unitFound: # G x density try: self._amp= self._amp.to(units.km**2/units.s**2\ /units.pc**2).value\ /bovy_conversion.dens_in_msolpc3(self._vo,self._ro)\ /bovy_conversion._G except units.UnitConversionError: pass else: unitFound= True if not amp_units == 'density': raise units.UnitConversionError('amp= parameter of %s should have units of %s, but has units of G x density instead' % (type(self).__name__,amp_units)) if not unitFound: # surface density try: self._amp= self._amp.to(units.Msun/units.pc**2).value\ /bovy_conversion.surfdens_in_msolpc2(self._vo,self._ro) except units.UnitConversionError: pass else: unitFound= True if not amp_units == 'surfacedensity': raise units.UnitConversionError('amp= parameter of %s should have units of %s, but has units of surface density instead' % (type(self).__name__,amp_units)) if not unitFound: # G x surface density try: self._amp= self._amp.to(units.km**2/units.s**2\ /units.pc).value\ /bovy_conversion.surfdens_in_msolpc2(self._vo,self._ro)\ /bovy_conversion._G except units.UnitConversionError: pass else: unitFound= True if not amp_units == 'surfacedensity': raise units.UnitConversionError('amp= parameter of %s should have units of %s, but has units of G x surface density instead' % (type(self).__name__,amp_units)) if not unitFound: raise units.UnitConversionError('amp= parameter of %s should have units of %s; given units are not understood' % (type(self).__name__,amp_units)) else: # When amplitude is given with units, turn on physical output self._roSet= True self._voSet= True return None
def like_func( params: Sequence, c: float, surfrs: list, kzs, kzerrs, termdata, termsigma, fitc, fitvoro, dblexp, addpal5, addgd1, ro: float, vo: float, addgas: bool, ): """Likelihood Function. Parameters ---------- params: list c: float surfrs: list kzs kzerrs termdata termsigma fitc fitvoro dblexp addpal5 addgd1 ro: float vo: float addgas: bool Returns ------- float """ # -------------------------------------------------------------------- from .potential import setup_potential # TODO how do not internally? # TODO use a more generic switcher so can use any stream from ..streams.pal5.pal5_util import force_pal5 from ..streams.gd1.gd1_util import force_gd1 # -------------------------------------------------------------------- # Check ranges if params[0] < 0.0 or params[0] > 1.0: return np.finfo(np.dtype(np.float64)).max elif params[1] < 0.0 or params[1] > 1.0: return np.finfo(np.dtype(np.float64)).max elif (1.0 - params[0] - params[1]) < 0.0 or (1.0 - params[0] - params[1]) > 1.0: return np.finfo(np.dtype(np.float64)).max elif params[2] < np.log(1.0 / REFR0) or params[2] > np.log(8.0 / REFR0): return np.finfo(np.dtype(np.float64)).max elif params[3] < np.log(0.05 / REFR0) or params[3] > np.log(1.0 / REFR0): return np.finfo(np.dtype(np.float64)).max elif fitvoro and (params[7] <= 150.0 / REFV0 or params[7] > 290.0 / REFV0): return np.finfo(np.dtype(np.float64)).max elif fitvoro and (params[8] <= 7.0 / REFR0 or params[8] > 9.4 / REFR0): return np.finfo(np.dtype(np.float64)).max elif fitc and (params[7 + 2 * fitvoro] <= 0.0 or params[7 + 2 * fitvoro] > 4.0): return np.finfo(np.dtype(np.float64)).max # -------------------------------------------------------------------- if fitvoro: ro, vo = REFR0 * params[8], REFV0 * params[7] # Setup potential pot = setup_potential( params, c, fitc, dblexp, ro, vo, fitvoro=fitvoro, addgas=addgas ) # Calculate model surface density at surfrs modelkzs = np.empty_like(surfrs) for ii in range(len(surfrs)): modelkzs[ii] = -potential.evaluatezforces( pot, (ro - 8.0 + surfrs[ii]) / ro, 1.1 / ro, phi=0.0 ) * bovy_conversion.force_in_2piGmsolpc2(vo, ro) out = 0.5 * np.sum((kzs - modelkzs) ** 2.0 / kzerrs ** 2.0) # Add terminal velocities vrsun = params[5] vtsun = params[6] cl_glon, cl_vterm, cl_corr, mc_glon, mc_vterm, mc_corr = termdata # Calculate terminal velocities at data glon cl_vterm_model = np.zeros_like(cl_vterm) for ii in range(len(cl_glon)): cl_vterm_model[ii] = potential.vterm(pot, cl_glon[ii]) cl_vterm_model += vrsun * np.cos(cl_glon / 180.0 * np.pi) - vtsun * np.sin( cl_glon / 180.0 * np.pi ) mc_vterm_model = np.zeros_like(mc_vterm) for ii in range(len(mc_glon)): mc_vterm_model[ii] = potential.vterm(pot, mc_glon[ii]) mc_vterm_model += vrsun * np.cos(mc_glon / 180.0 * np.pi) - vtsun * np.sin( mc_glon / 180.0 * np.pi ) cl_dvterm = (cl_vterm - cl_vterm_model * vo) / termsigma mc_dvterm = (mc_vterm - mc_vterm_model * vo) / termsigma out += 0.5 * np.sum(cl_dvterm * np.dot(cl_corr, cl_dvterm)) out += 0.5 * np.sum(mc_dvterm * np.dot(mc_corr, mc_dvterm)) # Rotation curve constraint out -= logprior_dlnvcdlnr(potential.dvcircdR(pot, 1.0, phi=0.0)) # K dwarfs, Kz out += ( 0.5 * ( -potential.evaluatezforces(pot, 1.0, 1.1 / ro, phi=0.0) * bovy_conversion.force_in_2piGmsolpc2(vo, ro) - 67.0 ) ** 2.0 / 36.0 ) # K dwarfs, visible out += 0.5 * (visible_dens(pot, ro, vo) - 55.0) ** 2.0 / 25.0 # Local density prior localdens = potential.evaluateDensities( pot, 1.0, 0.0, phi=0.0 ) * bovy_conversion.dens_in_msolpc3(vo, ro) out += 0.5 * (localdens - 0.102) ** 2.0 / 0.01 ** 2.0 # Bulge velocity dispersion out += 0.5 * (bulge_dispersion(pot, ro, vo) - 117.0) ** 2.0 / 225.0 # Mass at 60 kpc out += 0.5 * (mass60(pot, ro, vo) - 4.0) ** 2.0 / 0.7 ** 2.0 # Pal5 if addpal5: # q = 0.94 +/- 0.05 + add'l fp5 = force_pal5(pot, 23.46, ro, vo) out += 0.5 * (np.sqrt(2.0 * fp5[0] / fp5[1]) - 0.94) ** 2.0 / 0.05 ** 2.0 out += ( 0.5 * (0.94 ** 2.0 * (fp5[0] + 0.8) + 2.0 * (fp5[1] + 1.82) + 0.2) ** 2.0 / 0.6 ** 2.0 ) # GD-1 if addgd1: # q = 0.95 +/- 0.04 + add'l fg1 = force_gd1(pot, ro, vo) out += ( 0.5 * (np.sqrt(6.675 / 12.5 * fg1[0] / fg1[1]) - 0.95) ** 2.0 / 0.04 ** 2.0 ) out += ( 0.5 * (0.95 ** 2.0 * (fg1[0] + 2.51) + 6.675 / 12.5 * (fg1[1] + 1.47) + 0.05) ** 2.0 / 0.3 ** 2.0 ) # vc and ro measurements: vc=218 +/- 10 km/s, ro= 8.1 +/- 0.1 kpc out += (vo - 218.0) ** 2.0 / 200.0 + (ro - 8.1) ** 2.0 / 0.02 if np.isnan(out): return np.finfo(np.dtype(np.float64)).max else: return out
def rlimiting( cluster, pot=MWPotential2014, rgc=None, r0=8.0, v0=220.0, nrad=20, projected=False, plot=False, verbose=False, **kwargs ): """ NAME: rlimiting PURPOSE: Calculate limiting radius of the cluster --> The limiting radius is defined to be where the cluster's density reaches the local background density of the host galaxy INPUT: cluster - StarCluster instance pot - GALPY potential used to calculate actions rgc - Set galactocentric distance at which the tidal radius is to be evaluated r0,v0 - GALPY scaling parameters nrad - number of radial bins used to calculate density profile (Default: 20) projected - use projected values (Default: False) plot - plot the density profile and mark the limiting radius of the cluster (Default: False) KWARGS: Same as ..util.plots.nplot OUTPUT: rl HISTORY: 2019 - Written - Webb (UofT) """ units0, origin0 = save_cluster(cluster) cluster.to_centre() cluster.to_galpy() if rgc != None: R = rgc / r0 z = 0.0 else: R = np.sqrt(cluster.xgc ** 2.0 + cluster.ygc ** 2.0) z = cluster.zgc # Calculate local density: rho_local = potential.evaluateDensities( pot, R, z, ro=r0, vo=v0, use_physical=False ) / bovy_conversion.dens_in_msolpc3(ro=r0, vo=v0) rprof, pprof, nprof = rho_prof(cluster, nrad=nrad, projected=projected) if pprof[-1] > rho_local: rl = rprof[-1] elif pprof[0] < rho_local: rl = 0.0 else: indx = np.argwhere(pprof < rho_local)[0][0] r1 = (rprof[indx - 1], pprof[indx - 1]) r2 = (rprof[indx], pprof[indx]) rl = interpolate(r1, r2, y=rho_local) if verbose: print("FINAL RL: ", rl * r0 * 1000.0, "pc") if units0 == "realpc": rl *= 1000.0 * r0 elif units0 == "realkpc": rl *= r0 elif units0 == "nbody": rl *= 1000.0 * r0 / cluster.rbar cluster.rl = rl return_cluster(cluster, units0, origin0, do_order=True, do_key_params=True) if plot: if verbose: print("LOCAL DENSITY = ", rho_local) filename = kwargs.pop("filename", None) overplot = kwargs.pop("overplot", False) if cluster.units == "nbody": rprof *= r0 * 1000.0 / cluster.rbar pprof *= ( bovy_conversion.dens_in_msolpc3(ro=r0, vo=v0) * (cluster.rbar ** 3.0) / cluster.zmbar ) rho_local *= ( bovy_conversion.dens_in_msolpc3(ro=r0, vo=v0) * (cluster.rbar ** 3.0) / cluster.zmbar ) xunits = " (NBODY)" yunits = " (NBODY)" elif cluster.units == "realpc": rprof *= r0 * 1000.0 pprof *= bovy_conversion.dens_in_msolpc3(ro=r0, vo=v0) rho_local *= bovy_conversion.dens_in_msolpc3(ro=r0, vo=v0) xunits = " (pc)" if projected: yunits = " Msun/pc^2" else: yunits = " Msun/pc^3" elif cluster.units == "realkpc": rprof *= r0 pprof *= bovy_conversion.dens_in_msolpc3(ro=r0, vo=v0) * (1000.0 ** 3.0) rho_local *= bovy_conversion.dens_in_msolpc3(ro=r0, vo=v0) * (1000.0 ** 3.0) xunits = " (kpc)" if projected: yunits = " Msun/kpc^2" else: yunits = " Msun/kpc^3" elif cluster.units == "galpy": xunits = " (GALPY)" yunits = " (GALPY)" else: xunits = "" yunits = "" x, y, n = rprof, pprof, nprof nlplot( x, y, xlabel=r"$R %s$" % (xunits), ylabel=r"$\rho %s$" % (yunits), title="Time = %f" % cluster.tphys, log=True, overplot=overplot, filename=filename, ) nlplot(x, np.ones(len(x)) * rho_local, "--", overplot=True) nlplot(np.ones(len(y)) * rl, y, "--", overplot=True) if filename != None: plt.savefig(filename) return rl