def _z2deriv(self,R,z,phi=0.,t=0.): if not self.isNonAxi: phi= 0. x,y,z= bovy_coords.cyl_to_rect(R,phi-self._pa-self._omegab*t,z) # if not self._aligned: # raise NotImplementedError("2nd potential derivatives of TwoPowerTriaxialPotential not implemented for rotated coordinated frames (non-trivial zvec and pa)") return self._2ndderiv_xyz(x,y,z,2,2)
def _dens(self, R, z, phi=0., t=0.): """ NAME: _dens PURPOSE: evaluate the density for this potential INPUT: R - Galactocentric cylindrical radius z - vertical height phi - azimuth t - time OUTPUT: the density HISTORY: 2016-05-31 - Written - Bovy (UofT) """ x, y, z = bovy_coords.cyl_to_rect(R, phi, z) if self._aligned: xp, yp, zp = x, y, z else: xyzp = numpy.dot(self._rot, numpy.array([x, y, z])) xp, yp, zp = xyzp[0], xyzp[1], xyzp[2] m = numpy.sqrt(xp**2. + yp**2. / self._b2 + zp**2. / self._c2) return (self.a / m)**self.alpha / (1. + m / self.a)**( self.beta - self.alpha) / 4. / numpy.pi / self.a**3.
def add_rotation(cluster, qrot): """Add a degree of rotation to an already generated StarCluster Parameters ---------- cluster : class StarCluster qrot : float fraction of stars with v_phi < 0 that are switched to vphi > 0 Returns ------- x,y,z,vx,vy,vz : float stellar positions and velocities (now with rotation) History ------- 2019 - Written - Webb (UofT) """ r, theta, z = bovy_coords.rect_to_cyl(cluster.x, cluster.y, cluster.z) vr, vtheta, vz = bovy_coords.rect_to_cyl_vec(cluster.vx, cluster.vy, cluster.vz, cluster.x, cluster.y, cluster.z) indx = vtheta < 0.0 rindx = np.random.rand(cluster.ntot) < qrot vtheta[indx * rindx] = np.sqrt(vtheta[indx * rindx] * vtheta[indx * rindx]) x, y, z = bovy_coords.cyl_to_rect(r, theta, z) vx, vy, vz = bovy_coords.cyl_to_rect_vec(vr, vtheta, vz, theta) return x, y, z, vx, vy, vz
def _zforce(self,R,z,phi=0.,t=0.): """ NAME: _zforce PURPOSE: evaluate the vertical force for this potential INPUT: R - Galactocentric cylindrical radius z - vertical height phi - azimuth t - time OUTPUT: the vertical force HISTORY: 2016-06-09 - Written - Bovy (UofT) """ if not self.isNonAxi: phi= 0. x,y,z= bovy_coords.cyl_to_rect(R,phi,z) xy= np.dot(self.rot(t),np.array([x,y])) x,y= xy[0],xy[1] # Compute all rectangular forces new_hash= hashlib.md5(np.array([x,y,z])).hexdigest() if new_hash == self._force_hash: Fz= self._cached_Fz else: Fz= self._zforce_xyz(x,y,z) self._force_hash= new_hash self._cached_Fz= Fz return Fz
def _evaluate(self,R,z,phi=0.,t=0.): if not self.isNonAxi: phi= 0. x,y,z= bovy_coords.cyl_to_rect(R,phi,z) xy= numpy.dot(self.rot(t),numpy.array([x,y])) x,y= xy[0],xy[1] return self._evaluate_xyz(x,y,z)
def _phi2deriv(self,R,z,phi=0.,t=0.): """ NAME: _phi2deriv PURPOSE: evaluate the second azimuthal derivative for this potential INPUT: R - Galactocentric cylindrical radius z - vertical height phi - azimuth t - time OUTPUT: the second azimuthal derivative HISTORY: 2016-06-15 - Written - Bovy (UofT) """ if not self.isNonAxi: phi= 0. x,y,z= bovy_coords.cyl_to_rect(R,phi-self._omegab*t,z) # if not self._aligned: # raise NotImplementedError("2nd potential derivatives of TwoPowerTriaxialPotential not implemented for rotated coordinated frames (non-trivial zvec and pa)") Fx= self._xforce_xyz(x,y,z) Fy= self._yforce_xyz(x,y,z) phixx= self._2ndderiv_xyz(x,y,z,0,0) phixy= self._2ndderiv_xyz(x,y,z,0,1) phiyy= self._2ndderiv_xyz(x,y,z,1,1) return R*R*(np.sin(phi)*np.sin(phi)*phixx+np.cos(phi)*np.cos(phi)*phiyy\ -2.*np.cos(phi)*np.sin(phi)*phixy)\ +R*(np.cos(phi)*Fx+np.sin(phi)*Fy)
def _Rphideriv(self,R,z,phi=0.,t=0.): """ NAME: _Rphideriv PURPOSE: evaluate the mixed radial, azimuthal derivative for this potential INPUT: R - Galactocentric cylindrical radius z - vertical height phi - azimuth t - time OUTPUT: the mixed radial, azimuthal derivative HISTORY: 2016-06-15 - Written - Bovy (UofT) """ if not self.isNonAxi: phi= 0. x,y,z= bovy_coords.cyl_to_rect(R,phi,z) if not self._aligned: raise NotImplementedError("2nd potential derivatives of TwoPowerTriaxialPotential not implemented for rotated coordinated frames (non-trivial zvec and pa)") Fx= self._xforce_xyz(x,y,z) Fy= self._yforce_xyz(x,y,z) phixx= self._2ndderiv_xyz(x,y,z,0,0) phixy= self._2ndderiv_xyz(x,y,z,0,1) phiyy= self._2ndderiv_xyz(x,y,z,1,1) return R*numpy.cos(phi)*numpy.sin(phi)*\ (phiyy-phixx)+R*numpy.cos(2.*phi)*phixy\ +numpy.sin(phi)*Fx-numpy.cos(phi)*Fy
def _dens(self,R,z,phi=0.,t=0.): x,y,z= bovy_coords.cyl_to_rect(R,phi,z) xy= numpy.dot(self.rot(t),numpy.array([x,y])) x,y= xy[0],xy[1] m2 = x**2/self._a2+y**2/self._b2+z**2/self._c2 if m2 < 1: return 1/(4*numpy.pi*self.a**3)*(1-m2/self.a**2)**self.n else: return 0
def _R2deriv(self,R,z,phi=0.,t=0.): if not self.isNonAxi: phi= 0. x,y,z= bovy_coords.cyl_to_rect(R,phi-self._pa-self._omegab*t,z) # if not self._aligned: # raise NotImplementedError("2nd potential derivatives of TwoPowerTriaxialPotential not implemented for rotated coordinated frames (non-trivial zvec and pa)") phixx= self._2ndderiv_xyz(x,y,z,0,0) phixy= self._2ndderiv_xyz(x,y,z,0,1) phiyy= self._2ndderiv_xyz(x,y,z,1,1) return numpy.cos(phi)**2.*phixx+numpy.sin(phi)**2.*phiyy\ +2.*numpy.cos(phi)*numpy.sin(phi)*phixy
def eig(filename): # compute direction in cylindrical data = np.loadtxt(dir+filename) thetar = data[:,6] thetar = (np.pi+(thetar-np.median(thetar))) % (2.*np.pi) indx = np.fabs(thetar-np.pi) > (5.*np.median(np.fabs(thetar-np.median(thetar)))) #Frequencies Or = data[:,3] Op = data[:,4] Oz = data[:,5] dOr = Or[indx]-np.median(Or) dOp = Op[indx]-np.median(Op) dOz = Oz[indx]-np.median(Oz) dO = np.vstack((dOr,dOp,dOz))*bovy_conversion.freq_in_Gyr(220.,8.) dO4dir = copy.copy(dO) dO4dir[:,dO4dir[:,0] < 0.]*= -1. dOdir = np.median(dO4dir,axis=1) dOdir /= np.sqrt(np.sum(dOdir**2.)) # direction in cylindrical Or, Ophi, Oz = dOdir[0], dOdir[1], dOdir[2] # cheking for cylindrical if (Ophi<(2*np.pi)): Ophi -= (2*np.pi) # convert cylindrical to cartesian x,y,z = bovy_coords.cyl_to_rect(Or, Ophi, Oz) # convert cartesian to spherical R, theta_sph, phi_sph = cart_spher(x,y,z) # checking for spherical if (theta_sph> (2*np.pi)): theta_sph -= (2*np.pi) elif (theta_sph< (-2*np.pi)): theta_sph += (2*np.pi) if (phi_sph>np.pi): phi_sph -= np.pi elif (phi_sph<-np.pi): phi_sph += np.pi val = np.array([R, theta_sph, phi_sph]) return val
def _zforce(self,R,z,phi=0.,t=0.): if not self.isNonAxi: phi= 0. x,y,z= bovy_coords.cyl_to_rect(R,phi,z) xy= numpy.dot(self.rot(t),numpy.array([x,y])) x,y= xy[0],xy[1] # Compute all rectangular forces new_hash= hashlib.md5(numpy.array([x,y,z])).hexdigest() if new_hash == self._force_hash: Fz= self._cached_Fz else: Fz= self._zforce_xyz(x,y,z) self._force_hash= new_hash self._cached_Fz= Fz return Fz
def _zforce(self,R,z,phi=0.,t=0.): """ NAME: _zforce PURPOSE: evaluate the vertical force for this potential INPUT: R - Galactocentric cylindrical radius z - vertical height phi - azimuth t - time OUTPUT: the vertical force HISTORY: 2016-06-09 - Written - Bovy (UofT) """ if not self.isNonAxi: phi= 0. x,y,z= bovy_coords.cyl_to_rect(R,phi,z) # Compute all rectangular forces new_hash= hashlib.md5(numpy.array([x,y,z])).hexdigest() if new_hash == self._force_hash: Fx= self._cached_Fx Fy= self._cached_Fy Fz= self._cached_Fz else: if self._aligned: xp, yp, zp= x, y, z else: xyzp= numpy.dot(self._rot,numpy.array([x,y,z])) xp, yp, zp= xyzp[0], xyzp[1], xyzp[2] Fx= self._xforce_xyz(xp,yp,zp) Fy= self._yforce_xyz(xp,yp,zp) Fz= self._zforce_xyz(xp,yp,zp) self._force_hash= new_hash self._cached_Fx= Fx self._cached_Fy= Fy self._cached_Fz= Fz if not self._aligned: Fxyz= numpy.dot(self._rot.T,numpy.array([Fx,Fy,Fz])) Fz= Fxyz[2] return Fz
def _evaluate(self, R, z, phi=0., t=0.): """ NAME: _evaluate PURPOSE: evaluate the potential at R,z INPUT: R - Galactocentric cylindrical radius z - vertical height phi - azimuth t - time OUTPUT: Phi(R,z) """ if not self.isNonAxi: phi = 0. x, y, z = bovy_coords.cyl_to_rect(R, phi, z) xy = np.dot(self.rot(t), np.array([x, y])) x, y = xy[0], xy[1] return self._evaluate_xyz(x, y, z)
def _evaluate(self,R,z,phi=0.,t=0.): """ NAME: _evaluate PURPOSE: evaluate the potential at R,z INPUT: R - Galactocentric cylindrical radius z - vertical height phi - azimuth t - time OUTPUT: Phi(R,z) """ if not self.isNonAxi: phi= 0. x,y,z= bovy_coords.cyl_to_rect(R,phi,z) xy= np.dot(self.rot(t),np.array([x,y])) x,y= xy[0],xy[1] return self._evaluate_xyz(x,y,z)
def _z2deriv(self,R,z,phi=0.,t=0.): """ NAME: _z2deriv PURPOSE: evaluate the second vertical derivative for this potential INPUT: R - Galactocentric cylindrical radius z - vertical height phi - azimuth t - time OUTPUT: the second vertical derivative HISTORY: 2016-06-15 - Written - Bovy (UofT) """ if not self.isNonAxi: phi= 0. x,y,z= bovy_coords.cyl_to_rect(R,phi,z) if not self._aligned: raise NotImplementedError("2nd potential derivatives of TwoPowerTriaxialPotential not implemented for rotated coordinated frames (non-trivial zvec and pa)") return self._2ndderiv_xyz(x,y,z,2,2)
def _phiforce(self,R,z,phi=0.,t=0.): """ NAME: _phiforce PURPOSE: evaluate the azimuthal force for this potential INPUT: R - Galactocentric cylindrical radius z - vertical height phi - azimuth t - time OUTPUT: the azimuthal force HISTORY: 2016-06-09 - Written - Bovy (UofT) """ if not self.isNonAxi: phi= 0. x,y,z= bovy_coords.cyl_to_rect(R,phi,z) xy= np.dot(self.rot(t),np.array([x,y])) x,y= xy[0],xy[1] # Compute all rectangular forces new_hash= hashlib.md5(np.array([x,y,z])).hexdigest() if new_hash == self._force_hash: Fx= self._cached_Fx Fy= self._cached_Fy Fz= self._cached_Fz else: Fx= self._xforce_xyz(x,y,z) Fy= self._yforce_xyz(x,y,z) Fz= self._zforce_xyz(x,y,z) self._force_hash= new_hash self._cached_Fx= Fx self._cached_Fy= Fy self._cached_Fz= Fz Fxy= np.dot(self.rot(t, transposed = True),np.array([Fx,Fy])) Fx, Fy= Fxy[0], Fxy[1] return R*(-np.sin(phi)*Fx+np.cos(phi)*Fy)
def _phiforce(self,R,z,phi=0.,t=0.): if not self.isNonAxi: phi= 0. x,y,z= bovy_coords.cyl_to_rect(R,phi,z) xy= numpy.dot(self.rot(t),numpy.array([x,y])) x,y= xy[0],xy[1] # Compute all rectangular forces new_hash= hashlib.md5(numpy.array([x,y,z])).hexdigest() if new_hash == self._force_hash: Fx= self._cached_Fx Fy= self._cached_Fy Fz= self._cached_Fz else: Fx= self._xforce_xyz(x,y,z) Fy= self._yforce_xyz(x,y,z) Fz= self._zforce_xyz(x,y,z) self._force_hash= new_hash self._cached_Fx= Fx self._cached_Fy= Fy self._cached_Fz= Fz Fxy= numpy.dot(self.rot(t, transposed = True),numpy.array([Fx,Fy])) Fx, Fy= Fxy[0], Fxy[1] return R*(-numpy.sin(phi)*Fx+numpy.cos(phi)*Fy)
def plot_model(model, params, minmax=[-50, 50], nside=150): """ plot a given model in x-y,x-z and y-z projection """ xyzgrid = np.mgrid[minmax[0]:minmax[1]:nside * 1j, minmax[0]:minmax[1]:nside * 1j, minmax[0]:minmax[1]:nside * 1j] shape = np.shape(xyzgrid.T) xyzgrid = xyzgrid.T.reshape(np.product(shape[:3]), shape[3]) rphizgrid = bovy_coords.rect_to_cyl(xyzgrid[:, 0], xyzgrid[:, 1], xyzgrid[:, 2]) rphizgrid = np.dstack([rphizgrid[0], rphizgrid[1], rphizgrid[2]])[0] rphizgrid = rphizgrid.reshape(nside, nside, nside, 3).T denstxyz = model(rphizgrid[0], rphizgrid[1], rphizgrid[2], params=params) fig, ax = plt.subplots(1, 3) fig.set_size_inches(10, 3.4) ax[0].contour(np.rot90(np.log10(np.sum(denstxyz, axis=0))), extent=[minmax[0], minmax[1], minmax[0], minmax[1]], cmap=plt.cm.cividis) ax[1].contour(np.rot90(np.log10(np.sum(denstxyz, axis=1))), extent=[minmax[0], minmax[1], minmax[0], minmax[1]], cmap=plt.cm.cividis) ax[2].contour(np.rot90(np.log10(np.sum(denstxyz, axis=2))), extent=[minmax[0], minmax[1], minmax[0], minmax[1]], cmap=plt.cm.cividis) xdat, ydat, zdat = bovy_coords.cyl_to_rect(Rphiz[:, 0], Rphiz[:, 1], Rphiz[:, 2]) ax[0].set_xlabel(r'y') ax[0].set_ylabel(r'z') ax[1].set_xlabel(r'x') ax[1].set_ylabel(r'z') ax[2].set_xlabel(r'x') ax[2].set_ylabel(r'y') for axis in ax: axis.set_ylim(minmax[0], minmax[1]) axis.set_xlim(minmax[0], minmax[1]) fig.tight_layout()
def _dens(self,R,z,phi=0.,t=0.): """ NAME: _dens PURPOSE: evaluate the density for this potential INPUT: R - Galactocentric cylindrical radius z - vertical height phi - azimuth t - time OUTPUT: the density HISTORY: 2016-05-31 - Written - Bovy (UofT) """ x,y,z= bovy_coords.cyl_to_rect(R,phi,z) if self._aligned: xp, yp, zp= x, y, z else: xyzp= numpy.dot(self._rot,numpy.array([x,y,z])) xp, yp, zp= xyzp[0], xyzp[1], xyzp[2] m= numpy.sqrt(xp**2.+yp**2./self._b2+zp**2./self._c2) return (self.a/m)**self.alpha/(1.+m/self.a)**(self.beta-self.alpha)/4./numpy.pi/self.a**3.
def _Rzderiv(self,R,z,phi=0.,t=0.): """ NAME: _Rzderiv PURPOSE: evaluate the mixed radial, vertical derivative for this potential INPUT: R - Galactocentric cylindrical radius z - vertical height phi - azimuth t - time OUTPUT: the mixed radial, vertical derivative HISTORY: 2016-06-15 - Written - Bovy (UofT) """ if not self.isNonAxi: phi= 0. x,y,z= bovy_coords.cyl_to_rect(R,phi-self._pa-self._omegab*t,z) # if not self._aligned: # raise NotImplementedError("2nd potential derivatives of TwoPowerTriaxialPotential not implemented for rotated coordinated frames (non-trivial zvec and pa)") phixz= self._2ndderiv_xyz(x,y,z,0,2) phiyz= self._2ndderiv_xyz(x,y,z,1,2) return np.cos(phi)*phixz+np.sin(phi)*phiyz
def _dens(self,R,z,phi=0.,t=0.): """ NAME: _dens PURPOSE: evaluate the density for this potential INPUT: R - Galactocentric cylindrical radius z - vertical height phi - azimuth t - time OUTPUT: the density HISTORY: 2016-05-31 - Written - Bovy (UofT) """ x,y,z= bovy_coords.cyl_to_rect(R,phi,z) xy= np.dot(self.rot(t),np.array([x,y])) x,y= xy[0],xy[1] m2 = x*x/self._a2+y*y/self._b2+z*z/self._c2 if m2 < 1: return 1/(4*np.pi*self.a*self.a*self.a)*(1-m2/self._a2)**self.n else: return 0
def _evaluate(self,R,z,phi=0.,t=0.): """ NAME: _evaluate PURPOSE: evaluate the potential at R,z INPUT: R - Galactocentric cylindrical radius z - vertical height phi - azimuth t - time OUTPUT: Phi(R,z) HISTORY: 2016-05-30 - Started - Bovy (UofT) """ if not self.isNonAxi: phi= 0. x,y,z= bovy_coords.cyl_to_rect(R,phi,z) if self._aligned: return self._evaluate_xyz(x,y,z) else: xyzp= numpy.dot(self._rot,numpy.array([x,y,z])) return self._evaluate_xyz(xyzp[0],xyzp[1],xyzp[2])
def newNFWDensity(R, z, phi=0, a=2.): x,y,z = cyl_to_rect(R,phi,z) xi = (x**2 + y**2 + .5*z**2)**.5 return (4*nu.pi*(a**3)*(xi/a)*(1. + (xi/a))**2)**-1
def sample(self, n, returndt=False, integrate=True, xy=False, lb=False): """ NAME: sample PURPOSE: sample from the DF INPUT: n - number of points to return returndt= (False) if True, also return the time since the star was stripped integrate= (True) if True, integrate the orbits to the present time, if False, return positions at stripping (probably want to combine with returndt=True then to make sense of them!) xy= (False) if True, return Galactocentric rectangular coordinates lb= (False) if True, return Galactic l,b,d,vlos,pmll,pmbb coordinates OUTPUT: (R,vR,vT,z,vz,phi) of points on the stream in 6,N array HISTORY: 2018-07-31 - Written - Bovy (IAS) """ if xy or lb: raise NotImplementedError( "xy=True and lb=True options currently not implemented") # First sample times dt = numpy.random.uniform(size=n) * self._tdisrupt # Build all rotation matrices rot, rot_inv = self._setup_rot(dt) # Compute progenitor position in the instantaneous frame xyzpt = numpy.einsum( 'ijk,ik->ij', rot, numpy.array([ self._progenitor.x(-dt), self._progenitor.y(-dt), self._progenitor.z(-dt) ]).T) vxyzpt = numpy.einsum( 'ijk,ik->ij', rot, numpy.array([ self._progenitor.vx(-dt), self._progenitor.vy(-dt), self._progenitor.vz(-dt) ]).T) Rpt, phipt, Zpt = bovy_coords.rect_to_cyl(xyzpt[:, 0], xyzpt[:, 1], xyzpt[:, 2]) vRpt, vTpt, vZpt = bovy_coords.rect_to_cyl_vec(vxyzpt[:, 0], vxyzpt[:, 1], vxyzpt[:, 2], Rpt, phipt, Zpt, cyl=True) # Sample positions and velocities in the instantaneous frame k = self._meankvec + numpy.random.normal( size=n)[:, numpy.newaxis] * self._sigkvec try: rtides = rtide(self._rtpot, Rpt, Zpt, phi=phipt, t=-dt, M=self._progenitor_mass, use_physical=False) vcs = numpy.sqrt(-Rpt * evaluateRforces( self._rtpot, Rpt, Zpt, phi=phipt, t=-dt, use_physical=False)) except (ValueError, TypeError): rtides = numpy.array([ rtide(self._rtpot, Rpt[ii], Zpt[ii], phi=phipt[ii], t=-dt[ii], M=self._progenitor_mass, use_physical=False) for ii in range(len(Rpt)) ]) vcs = numpy.array([ numpy.sqrt(-Rpt[ii] * evaluateRforces(self._rtpot, Rpt[ii], Zpt[ii], phi=phipt[ii], t=-dt[ii], use_physical=False)) for ii in range(len(Rpt)) ]) rtides_as_frac = rtides / Rpt RpZst = numpy.array([ Rpt + k[:, 0] * rtides, phipt + k[:, 5] * rtides_as_frac, k[:, 3] * rtides_as_frac ]).T vRTZst = numpy.array([ vRpt * (1. + k[:, 1]), vTpt + k[:, 2] * vcs * rtides_as_frac, k[:, 4] * vcs * rtides_as_frac ]).T # Now rotate these back to the galactocentric frame xst, yst, zst = bovy_coords.cyl_to_rect(RpZst[:, 0], RpZst[:, 1], RpZst[:, 2]) vxst, vyst, vzst = bovy_coords.cyl_to_rect_vec(vRTZst[:, 0], vRTZst[:, 1], vRTZst[:, 2], RpZst[:, 1]) xyzs = numpy.einsum('ijk,ik->ij', rot_inv, numpy.array([xst, yst, zst]).T) vxyzs = numpy.einsum('ijk,ik->ij', rot_inv, numpy.array([vxst, vyst, vzst]).T) Rs, phis, Zs = bovy_coords.rect_to_cyl(xyzs[:, 0], xyzs[:, 1], xyzs[:, 2]) vRs, vTs, vZs = bovy_coords.rect_to_cyl_vec(vxyzs[:, 0], vxyzs[:, 1], vxyzs[:, 2], Rs, phis, Zs, cyl=True) out = numpy.empty((6, n)) if integrate: # Now integrate the orbits for ii in range(n): o = Orbit( [Rs[ii], vRs[ii], vTs[ii], Zs[ii], vZs[ii], phis[ii]]) o.integrate(numpy.linspace(-dt[ii], 0., 10001), self._pot) o = o(0.) out[:, ii] = [o.R(), o.vR(), o.vT(), o.z(), o.vz(), o.phi()] else: out[0] = Rs out[1] = vRs out[2] = vTs out[3] = Zs out[4] = vZs out[5] = phis if returndt: return (out, dt) else: return out
def _compute_xyz(self,R,phi,z,t): return bovy_coords.cyl_to_rect(R,phi-self._pa-self._omegab*t,z)
def _compute_xyz(self, R, phi, z, t): return bovy_coords.cyl_to_rect(R, phi - self._pa - self._omegab * t, z)