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 rtidal( cluster, pot=MWPotential2014, rtiterate=0, rtconverge=0.9, rgc=None, ro=8.0, vo=220.0, verbose=False, ): """Calculate tidal radius of the cluster - The calculation uses Galpy (Bovy 2015_, which takes the formalism of Bertin & Varri 2008 to calculate the tidal radius -- Bertin, G. & Varri, A.L. 2008, ApJ, 689, 1005 -- Bovy J., 2015, ApJS, 216, 29 - riterate = 0 corresponds to a single calculation of the tidal radius based on the cluster's mass (cluster.mtot) -- Additional iterations take the mass within the previous iteration's calculation of the tidal radius and calculates the tidal radius again using the new mass until the change is less than 90% - 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 instance pot : class GALPY potential used to calculate tidal radius (default: MWPotential2014) rtiterate : int how many times to iterate on the calculation of r_t (default: 0) rtconverge : float criteria for tidal radius convergence within iterations (default 0.9) rgc : float 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 verbose : bool Print information about iterative calculation of rt Returns ------- rt : float tidal radius 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 rtide rt = rtide(pot, R, z, M=cluster.mtot, use_physical=False) nit = 0 for i in range(0, rtiterate): msum = 0.0 indx = cluster.r < rt msum = np.sum(cluster.m[indx]) rtnew = rtide(pot, R, z, M=msum, use_physical=False) if verbose: print(rt, rtnew, rtnew / rt, msum / cluster.mtot) if rtnew / rt >= rtconverge: break rt = rtnew nit += 1 if verbose: print( "FINAL RT: ", rt * ro * 1000.0, "pc after", nit, " of ", rtiterate, " iterations", ) if units0 == "pckms": rt *= 1000.0 * ro elif units0 == "kpckms": rt *= ro elif units0 == "nbody": rt *= 1000.0 * ro / cluster.rbar return_cluster(cluster, units0, origin0) return rt
def rtidal( cluster, pot=MWPotential2014, rtiterate=0, rtconverge=0.9, rgc=None, r0=8.0, v0=220.0, verbose=False, ): """Calculate rtidal. NAME: rtidal PURPOSE: Calculate tidal radius of the cluster --> The calculation uses Galpy, which takes the formalism of Bertin & Varri 2008 to calculate the tidal radius --> riterate = 0 corresponds to a single calculation of the tidal radius based on the cluster's mass (cluster.mtot) --> More iterations take the mass within the previous iterations calculation of the tidal radius and calculates the tidal radius again until the change is less than 90% INPUT: cluster - StarCluster instance pot - GALPY potential used to calculate actions rtiterate - how many times to iterate on the calculation of r_t rtconverge - criteria for tidal radius convergence within iterations rgc - Set galactocentric distance at which the tidal radius is to be evaluated r0,v0 - GALPY scaling parameters OUTPUT: rt 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 rtide rt = rtide(pot, R, z, M=cluster.mtot,use_physical=False) nit = 0 for i in range(0, rtiterate): msum = 0.0 indx = cluster.r < rt msum = np.sum(cluster.m[indx]) rtnew = rtide(pot, R, z, M=msum,use_physical=False) if verbose: print(rt, rtnew, rtnew / rt, msum / cluster.mtot) if rtnew / rt >= rtconverge: break rt = rtnew nit += 1 if verbose: print( "FINAL RT: ", rt * r0 * 1000.0, "pc after", nit, " of ", rtiterate, " iterations", ) if units0 == "realpc": rt *= 1000.0 * r0 elif units0 == "realkpc": rt *= r0 elif units0 == "nbody": rt *= 1000.0 * r0 / cluster.rbar cluster.rt = rt return_cluster(cluster, units0, origin0) return rt