Example #1
0
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
Example #2
0
    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
Example #3
0
    def _determine_impact_coordtransform(self,deltaAngleTrackImpact,
                                         nTrackChunksImpact,
                                         timpact,impact_angle):
        """Function that sets up the transformation between (x,v) and (O,theta)"""
        # Integrate the progenitor backward to the time of impact
        self._gap_progenitor_setup()
        # Sign of delta angle tells us whether the impact happens to the
        # leading or trailing arm, self._sigMeanSign contains this info
        if impact_angle > 0.:
            self._gap_leading= True
        else:
            self._gap_leading= False
        if (self._gap_leading and not self._leading) \
                or (not self._gap_leading and self._leading):
            raise ValueError('Modeling leading (trailing) impact for trailing (leading) arm; this is not allowed because it is nonsensical in this framework')
        self._impact_angle= numpy.fabs(impact_angle)
        self._gap_sigMeanSign= 1.
        if (self._gap_leading and self._progenitor_Omega_along_dOmega/self._sigMeanSign < 0.) \
                or (not self._gap_leading and self._progenitor_Omega_along_dOmega/self._sigMeanSign > 0.):
            self._gap_sigMeanSign= -1.
        # Determine how much orbital time is necessary for the progenitor's orbit at the time of impact to cover the part of the stream near the impact; we cover the whole leading (or trailing) part of the stream
        if nTrackChunksImpact is None:
            #default is floor(self._deltaAngleTrackImpact/0.15)+1
            self._nTrackChunksImpact= int(numpy.floor(self._deltaAngleTrackImpact/0.15))+1
        else:
            self._nTrackChunksImpact= nTrackChunksImpact
        dt= self._deltaAngleTrackImpact\
            /self._progenitor_Omega_along_dOmega\
            /self._sigMeanSign*self._gap_sigMeanSign
        self._gap_trackts= numpy.linspace(0.,2*dt,2*self._nTrackChunksImpact-1) #to be sure that we cover it
        #Instantiate an auxiliaryTrack, which is an Orbit instance at the mean frequency of the stream, and zero angle separation wrt the progenitor; prog_stream_offset is the offset between this track and the progenitor at zero angle (same as in streamdf, but just done at the time of impact rather than the current time)
        prog_stream_offset=\
            _determine_stream_track_single(self._aA,
                                           self._gap_progenitor,
                                           self._timpact, # around the t of imp
                                           self._progenitor_angle-self._timpact*self._progenitor_Omega,
                                           self._gap_sigMeanSign,
                                           self._dsigomeanProgDirection,
                                           lambda da: super(streamgapdf,self).meanOmega(da,offset_sign=self._gap_sigMeanSign,tdisrupt=self._tdisrupt-self._timpact),
                                           0.) #angle = 0
        auxiliaryTrack= Orbit(prog_stream_offset[3])
        if dt < 0.:
            self._gap_trackts= numpy.linspace(0.,-2.*dt,2.*self._nTrackChunksImpact-1)
            #Flip velocities before integrating
            auxiliaryTrack= auxiliaryTrack.flip()
        auxiliaryTrack.integrate(self._gap_trackts,self._pot)
        if dt < 0.:
            #Flip velocities again
            auxiliaryTrack._orb.orbit[:,1]= -auxiliaryTrack._orb.orbit[:,1]
            auxiliaryTrack._orb.orbit[:,2]= -auxiliaryTrack._orb.orbit[:,2]
            auxiliaryTrack._orb.orbit[:,4]= -auxiliaryTrack._orb.orbit[:,4]
        #Calculate the actions, frequencies, and angle for this auxiliary orbit
        acfs= self._aA.actionsFreqs(auxiliaryTrack(0.),maxn=3)
        auxiliary_Omega= numpy.array([acfs[3],acfs[4],acfs[5]]).reshape(3\
)
        auxiliary_Omega_along_dOmega= \
            numpy.dot(auxiliary_Omega,self._dsigomeanProgDirection)
        # compute the transformation using _determine_stream_track_single
        allAcfsTrack= numpy.empty((self._nTrackChunksImpact,9))
        alljacsTrack= numpy.empty((self._nTrackChunksImpact,6,6))
        allinvjacsTrack= numpy.empty((self._nTrackChunksImpact,6,6))
        thetasTrack= numpy.linspace(0.,self._deltaAngleTrackImpact,
                                    self._nTrackChunksImpact)
        ObsTrack= numpy.empty((self._nTrackChunksImpact,6))
        ObsTrackAA= numpy.empty((self._nTrackChunksImpact,6))
        detdOdJps= numpy.empty((self._nTrackChunksImpact))
        if self._multi is None:
            for ii in range(self._nTrackChunksImpact):
                multiOut= _determine_stream_track_single(self._aA,
                                           auxiliaryTrack,
                                           self._gap_trackts[ii]*numpy.fabs(self._progenitor_Omega_along_dOmega/auxiliary_Omega_along_dOmega), #this factor accounts for the difference in frequency between the progenitor and the auxiliary track, no timpact bc gap_tracks is relative to timpact
                                           self._progenitor_angle-self._timpact*self._progenitor_Omega,
                                           self._gap_sigMeanSign,
                                           self._dsigomeanProgDirection,
                                           lambda da: super(streamgapdf,self).meanOmega(da,offset_sign=self._gap_sigMeanSign,tdisrupt=self._tdisrupt-self._timpact),
                                           thetasTrack[ii])
                allAcfsTrack[ii,:]= multiOut[0]
                alljacsTrack[ii,:,:]= multiOut[1]
                allinvjacsTrack[ii,:,:]= multiOut[2]
                ObsTrack[ii,:]= multiOut[3]
                ObsTrackAA[ii,:]= multiOut[4]
                detdOdJps[ii]= multiOut[5]
        else:
            multiOut= multi.parallel_map(\
                (lambda x: _determine_stream_track_single(self._aA,
                                           auxiliaryTrack,
                                           self._gap_trackts[x]*numpy.fabs(self._progenitor_Omega_along_dOmega/auxiliary_Omega_along_dOmega), #this factor accounts for the difference in frequency between the progenitor and the auxiliary track, no timpact bc gap_tracks is relative to timpact
                                           self._progenitor_angle-self._timpact*self._progenitor_Omega,
                                           self._gap_sigMeanSign,
                                           self._dsigomeanProgDirection,
                                           lambda da: super(streamgapdf,self).meanOmega(da,offset_sign=self._gap_sigMeanSign,tdisrupt=self._tdisrupt-self._timpact),
                                           thetasTrack[x])),
                range(self._nTrackChunksImpact),
                numcores=numpy.amin([self._nTrackChunksImpact,
                                     multiprocessing.cpu_count(),
                                     self._multi]))
            for ii in range(self._nTrackChunksImpact):
                allAcfsTrack[ii,:]= multiOut[ii][0]
                alljacsTrack[ii,:,:]= multiOut[ii][1]
                allinvjacsTrack[ii,:,:]= multiOut[ii][2]
                ObsTrack[ii,:]= multiOut[ii][3]
                ObsTrackAA[ii,:]= multiOut[ii][4]
                detdOdJps[ii]= multiOut[ii][5]
        #Repeat the track calculation using the previous track, to get closer to it
        for nn in range(self.nTrackIterations):
            if self._multi is None:
                for ii in range(self._nTrackChunksImpact):
                    multiOut= _determine_stream_track_single(self._aA,
                                                             Orbit(ObsTrack[ii,:]),
                                                             0.,
                                                             self._progenitor_angle-self._timpact*self._progenitor_Omega,
                                                             self._gap_sigMeanSign,
                                                             self._dsigomeanProgDirection,
                                                             lambda da: super(streamgapdf,self).meanOmega(da,offset_sign=self._gap_sigMeanSign,tdisrupt=self._tdisrupt-self._timpact),
                                                             thetasTrack[ii])
                    allAcfsTrack[ii,:]= multiOut[0]
                    alljacsTrack[ii,:,:]= multiOut[1]
                    allinvjacsTrack[ii,:,:]= multiOut[2]
                    ObsTrack[ii,:]= multiOut[3]
                    ObsTrackAA[ii,:]= multiOut[4]
                    detdOdJps[ii]= multiOut[5]
            else:
                multiOut= multi.parallel_map(\
                    (lambda x: _determine_stream_track_single(self._aA,Orbit(ObsTrack[x,:]),0.,
                                                              self._progenitor_angle-self._timpact*self._progenitor_Omega,
                                                              self._gap_sigMeanSign,
                                                              self._dsigomeanProgDirection,
                                                              lambda da: super(streamgapdf,self).meanOmega(da,offset_sign=self._gap_sigMeanSign,tdisrupt=self._tdisrupt-self._timpact),
                                                              thetasTrack[x])),
                    range(self._nTrackChunksImpact),
                    numcores=numpy.amin([self._nTrackChunksImpact,
                                         multiprocessing.cpu_count(),
                                         self._multi]))
                for ii in range(self._nTrackChunksImpact):
                    allAcfsTrack[ii,:]= multiOut[ii][0]
                    alljacsTrack[ii,:,:]= multiOut[ii][1]
                    allinvjacsTrack[ii,:,:]= multiOut[ii][2]
                    ObsTrack[ii,:]= multiOut[ii][3]
                    ObsTrackAA[ii,:]= multiOut[ii][4]
                    detdOdJps[ii]= multiOut[ii][5]
        #Store the track
        self._gap_thetasTrack= thetasTrack
        self._gap_ObsTrack= ObsTrack
        self._gap_ObsTrackAA= ObsTrackAA
        self._gap_allAcfsTrack= allAcfsTrack
        self._gap_alljacsTrack= alljacsTrack
        self._gap_allinvjacsTrack= allinvjacsTrack
        self._gap_detdOdJps= detdOdJps
        self._gap_meandetdOdJp= numpy.mean(self._gap_detdOdJps)
        self._gap_logmeandetdOdJp= numpy.log(self._gap_meandetdOdJp)
        #Also calculate _ObsTrackXY in XYZ,vXYZ coordinates
        self._gap_ObsTrackXY= numpy.empty_like(self._gap_ObsTrack)
        TrackX= self._gap_ObsTrack[:,0]*numpy.cos(self._gap_ObsTrack[:,5])
        TrackY= self._gap_ObsTrack[:,0]*numpy.sin(self._gap_ObsTrack[:,5])
        TrackZ= self._gap_ObsTrack[:,3]
        TrackvX, TrackvY, TrackvZ=\
            bovy_coords.cyl_to_rect_vec(self._gap_ObsTrack[:,1],
                                        self._gap_ObsTrack[:,2],
                                        self._gap_ObsTrack[:,4],
                                        self._gap_ObsTrack[:,5])
        self._gap_ObsTrackXY[:,0]= TrackX
        self._gap_ObsTrackXY[:,1]= TrackY
        self._gap_ObsTrackXY[:,2]= TrackZ
        self._gap_ObsTrackXY[:,3]= TrackvX
        self._gap_ObsTrackXY[:,4]= TrackvY
        self._gap_ObsTrackXY[:,5]= TrackvZ
        return None
Example #4
0
 def _interpolate_stream_track_kick(self):
     """Build interpolations of the stream track near the kick"""
     if hasattr(self,'_kick_interpolatedThetasTrack'): #pragma: no cover
         return None #Already did this
     # Setup the trackpoints where the kick will be computed, covering the
     # full length of the stream
     self._kick_interpolatedThetasTrack= \
         numpy.linspace(self._gap_thetasTrack[0],
                        self._gap_thetasTrack[-1],
                        self._nKickPoints)
     TrackX= self._gap_ObsTrack[:,0]*numpy.cos(self._gap_ObsTrack[:,5])
     TrackY= self._gap_ObsTrack[:,0]*numpy.sin(self._gap_ObsTrack[:,5])
     TrackZ= self._gap_ObsTrack[:,3]
     TrackvX, TrackvY, TrackvZ=\
         bovy_coords.cyl_to_rect_vec(self._gap_ObsTrack[:,1],
                                     self._gap_ObsTrack[:,2],
                                     self._gap_ObsTrack[:,4],
                                     self._gap_ObsTrack[:,5])
     #Interpolate
     self._kick_interpTrackX=\
         interpolate.InterpolatedUnivariateSpline(self._gap_thetasTrack,
                                                  TrackX,k=3)
     self._kick_interpTrackY=\
         interpolate.InterpolatedUnivariateSpline(self._gap_thetasTrack,
                                                  TrackY,k=3)
     self._kick_interpTrackZ=\
         interpolate.InterpolatedUnivariateSpline(self._gap_thetasTrack,
                                                  TrackZ,k=3)
     self._kick_interpTrackvX=\
         interpolate.InterpolatedUnivariateSpline(self._gap_thetasTrack,
                                                  TrackvX,k=3)
     self._kick_interpTrackvY=\
         interpolate.InterpolatedUnivariateSpline(self._gap_thetasTrack,
                                                  TrackvY,k=3)
     self._kick_interpTrackvZ=\
         interpolate.InterpolatedUnivariateSpline(self._gap_thetasTrack,
                                                  TrackvZ,k=3)
     #Now store an interpolated version of the stream track
     self._kick_interpolatedObsTrackXY= numpy.empty((len(self._kick_interpolatedThetasTrack),6))
     self._kick_interpolatedObsTrackXY[:,0]=\
         self._kick_interpTrackX(self._kick_interpolatedThetasTrack)
     self._kick_interpolatedObsTrackXY[:,1]=\
         self._kick_interpTrackY(self._kick_interpolatedThetasTrack)
     self._kick_interpolatedObsTrackXY[:,2]=\
         self._kick_interpTrackZ(self._kick_interpolatedThetasTrack)
     self._kick_interpolatedObsTrackXY[:,3]=\
         self._kick_interpTrackvX(self._kick_interpolatedThetasTrack)
     self._kick_interpolatedObsTrackXY[:,4]=\
         self._kick_interpTrackvY(self._kick_interpolatedThetasTrack)
     self._kick_interpolatedObsTrackXY[:,5]=\
         self._kick_interpTrackvZ(self._kick_interpolatedThetasTrack)
     #Also in cylindrical coordinates
     self._kick_interpolatedObsTrack= \
         numpy.empty((len(self._kick_interpolatedThetasTrack),6))
     tR,tphi,tZ= bovy_coords.rect_to_cyl(self._kick_interpolatedObsTrackXY[:,0],
                                         self._kick_interpolatedObsTrackXY[:,1],
                                         self._kick_interpolatedObsTrackXY[:,2])
     tvR,tvT,tvZ=\
         bovy_coords.rect_to_cyl_vec(self._kick_interpolatedObsTrackXY[:,3],
                                     self._kick_interpolatedObsTrackXY[:,4],
                                     self._kick_interpolatedObsTrackXY[:,5],
                                     tR,tphi,tZ,cyl=True)
     self._kick_interpolatedObsTrack[:,0]= tR
     self._kick_interpolatedObsTrack[:,1]= tvR
     self._kick_interpolatedObsTrack[:,2]= tvT
     self._kick_interpolatedObsTrack[:,3]= tZ
     self._kick_interpolatedObsTrack[:,4]= tvZ
     self._kick_interpolatedObsTrack[:,5]= tphi
     # Also store (x,v) for the point of closest approach
     self._kick_ObsTrackXY_closest= numpy.array([\
             self._kick_interpTrackX(self._impact_angle),
             self._kick_interpTrackY(self._impact_angle),
             self._kick_interpTrackZ(self._impact_angle),
             self._kick_interpTrackvX(self._impact_angle),
             self._kick_interpTrackvY(self._impact_angle),
             self._kick_interpTrackvZ(self._impact_angle)])
     return None