def reference_plane(self, time): """Compute the orbit at specified times in the two-body barycentric frame aligned with the reference plane coordinate system (XYZ). Parameters ---------- time : array_like, `astropy.time.Time` Array of times as barycentric MJD values, or an Astropy `~astropy.time.Time` object containing the times to evaluate at. """ xyz = self.orbital_plane(time) vxyz = xyz.differentials['s'] xyz = xyz.without_differentials() # Construct rotation matrix to take the orbit from the orbital plane # system (xyz) to the reference plane system (XYZ): R1 = rotation_matrix(-self.omega, axis='z') R2 = rotation_matrix(self.i, axis='x') R3 = rotation_matrix(self.Omega, axis='z') Rot = matrix_product(R3, R2, R1) # Rotate to the reference plane system XYZ = coord.CartesianRepresentation(matrix_product(Rot, xyz.xyz)) VXYZ = coord.CartesianDifferential(matrix_product(Rot, vxyz.d_xyz)) XYZ = XYZ.with_differentials(VXYZ) kw = dict() if self.barycenter is not None: kw['origin'] = self.barycenter.origin return ReferencePlaneFrame(XYZ, **kw)
def get_galactocentric2019(): """ References ---------- Galactic center sky position: - `Reid & Brunthaler (2004) <https://ui.adsabs.harvard.edu/abs/2004ApJ...616..872R/abstract>`_ Galactic center distance: - `GRAVITY collaboration (2018) <https://ui.adsabs.harvard.edu/abs/2018A%26A...615L..15G/abstract>`_ Solar velocity: - `Drimmel & Poggio (2018) <https://ui.adsabs.harvard.edu/abs/2018RNAAS...2d.210D/abstract>`_ - `Reid & Brunthaler (2004) <https://ui.adsabs.harvard.edu/abs/2004ApJ...616..872R/abstract>`_ - `GRAVITY collaboration (2018) <https://ui.adsabs.harvard.edu/abs/2018A%26A...615L..15G/abstract>`_ Solar height above midplane: - `Bennett & Bovy (2019) <https://ui.adsabs.harvard.edu/abs/2019MNRAS.482.1417B/abstract>`_ Returns ------- galcen_frame : `~astropy.coordinates.Galactocentric` The modern (2019) Galactocentric reference frame. """ rsun = 8.122 * u.kpc vsun = coord.CartesianDifferential([12.9, 245.6, 7.78] * u.km / u.s) zsun = 20.8 * u.pc return coord.Galactocentric(galcen_distance=rsun, galcen_v_sun=vsun, z_sun=zsun)
def correct_pm0(ra, dec, pmra, pmdec, dist, vlsr=vlsr0, vz=0): """Corrects the proper motion for the speed of the Sun Arguments: ra - RA in deg dec -- Declination in deg pmra -- pm in RA in mas/yr pmdec -- pm in declination in mas/yr dist -- distance in kpc Returns: (pmra,pmdec) the tuple with the proper motions corrected for the Sun's motion """ C = acoo.ICRS(ra=ra * auni.deg, dec=dec * auni.deg, radial_velocity=0 * auni.km / auni.s, distance=dist * auni.kpc, pm_ra_cosdec=pmra * auni.mas / auni.year, pm_dec=pmdec * auni.mas / auni.year) kw = dict(galcen_v_sun=acoo.CartesianDifferential( np.array([11.1, vlsr + 12.24, vz + 7.25]) * auni.km / auni.s)) frame = acoo.Galactocentric(**kw) Cg = C.transform_to(frame) Cg1 = acoo.Galactocentric(x=Cg.x, y=Cg.y, z=Cg.z, v_x=Cg.v_x * 0, v_y=Cg.v_y * 0, v_z=Cg.v_z * 0, **kw) C1 = Cg1.transform_to(acoo.ICRS()) return ((C.pm_ra_cosdec - C1.pm_ra_cosdec).to_value(auni.mas / auni.year), (C.pm_dec - C1.pm_dec).to_value(auni.mas / auni.year))
def evaluate( x: u.kpc, y: u.kpc, z: u.kpc, v_x: u.km / u.s, v_y: u.km / u.s, v_z: u.km / u.s, ): # TODO using actual mechanics of transformation d_xyz = coord.CartesianDifferential(d_x=v_x, d_y=v_y, d_z=v_z) xyz = coord.CartesianRepresentation(x=x, y=y, z=z, differentials=d_xyz) sph = xyz.represent_as( coord.SphericalRepresentation, differential_class=coord.SphericalDifferential, ) d_sph = sph.differentials["s"] return ( sph.lon, sph.lat, sph.distance, d_sph.d_lon, d_sph.d_lat, d_sph.d_distance, )
def get_gc_frame(): v_sun = coord.CartesianDifferential([11.1, 250, 7.25] * u.km / u.s) #gc_frame = coord.Galactocentric(galcen_distance=8.3*u.kpc, # z_sun=0*u.pc, # galcen_v_sun=v_sun) gc_frame = coord.Galactocentric() return gc_frame
def setup_class(cls): """Setup fixtures for testing.""" cls.num = 40 cls.affine = np.linspace(0, 10, num=cls.num) * u.Myr cls.rep = coord.CartesianRepresentation( x=np.linspace(0, 1, num=cls.num) * u.kpc, y=np.linspace(1, 2, num=cls.num) * u.kpc, z=np.linspace(2, 3, num=cls.num) * u.kpc, differentials=coord.CartesianDifferential( d_x=np.linspace(3, 4, num=cls.num) * u.km / u.s, d_y=np.linspace(4, 5, num=cls.num) * u.km / u.s, d_z=np.linspace(5, 6, num=cls.num) * u.km / u.s, ), ) if cls.klass is icoord.InterpolatedRepresentationOrDifferential: class SubClass(cls.klass): pass # so not abstract & can instantiate cls.inst = SubClass(cls.rep, affine=cls.affine) else: cls.inst = cls.klass(cls.rep, affine=cls.affine)
def test_infer_derivative_type(): """Test ``_infer_derivative_type``.""" # ---------------------------- # Test when rep is a differential rep = coord.CartesianDifferential(d_x=1, d_y=2, d_z=3) dif = icoord._infer_derivative_type(rep, u.s) assert dif.__name__ == "GenericCartesian2ndDifferential" assert dif is gcoord.GenericCartesian2ndDifferential # ---------------------------- # Test when non-time dif unit rep = coord.CartesianRepresentation(x=1, y=2, z=3) dif = icoord._infer_derivative_type(rep, u.deg) assert dif.__name__ == "GenericCartesianDifferential" assert dif is gcoord.GenericCartesianDifferential # ---------------------------- # Test when Rep & time rep = coord.CartesianRepresentation(x=1, y=2, z=3) dif = icoord._infer_derivative_type(rep, u.s) assert dif is coord.CartesianDifferential
def setup_class(cls): """Setup fixtures for testing.""" cls.num = 40 cls.affine = np.linspace(0, 10, num=cls.num) * u.Myr cls.frame = coord.Galactocentric cls.rep = coord.CartesianRepresentation( x=np.linspace(0, 1, num=cls.num) * u.kpc, y=np.linspace(1, 2, num=cls.num) * u.kpc, z=np.linspace(2, 3, num=cls.num) * u.kpc, differentials=coord.CartesianDifferential( d_x=np.linspace(3, 4, num=cls.num) * u.km / u.s, d_y=np.linspace(4, 5, num=cls.num) * u.km / u.s, d_z=np.linspace(5, 6, num=cls.num) * u.km / u.s, ), ) cls.irep = icoord.InterpolatedRepresentation( cls.rep, affine=cls.affine, ) cls.coord = cls.frame(cls.irep) cls.icoord = icoord.InterpolatedCoordinateFrame(cls.coord) cls.inst = cls.klass(cls.icoord)
def correct_vel(ra, dec, vel, vlsr=vlsr0): """Corrects the proper motion for the speed of the Sun Arguments: ra - RA in deg dec -- Declination in deg pmra -- pm in RA in mas/yr pmdec -- pm in declination in mas/yr dist -- distance in kpc Returns: (pmra,pmdec) the tuple with the proper motions corrected for the Sun's motion """ C = acoo.ICRS(ra=ra * auni.deg, dec=dec * auni.deg, radial_velocity=vel * auni.km / auni.s, distance=np.ones_like(vel) * auni.kpc, pm_ra_cosdec=np.zeros_like(vel) * auni.mas / auni.year, pm_dec=np.zeros_like(vel) * auni.mas / auni.year) #frame = acoo.Galactocentric (galcen_vsun = np.array([ 11.1, vlsr+12.24, 7.25])*auni.km/auni.s) kw = dict(galcen_v_sun=acoo.CartesianDifferential( np.array([11.1, vlsr + 12.24, 7.25]) * auni.km / auni.s)) frame = acoo.Galactocentric(**kw) Cg = C.transform_to(frame) Cg1 = acoo.Galactocentric(x=Cg.x, y=Cg.y, z=Cg.z, v_x=Cg.v_x * 0, v_y=Cg.v_y * 0, v_z=Cg.v_z * 0, **kw) C1 = Cg1.transform_to(acoo.ICRS) return np.asarray(((C.radial_velocity - C1.radial_velocity) / (auni.km / auni.s)).decompose())
def pmlb_solar_correction(l, b, dist, pml, pmb, vsun=(11.1, 12.2, 7.25), vlsr=235, vrad=0): vsun=np.array(vsun) vsun[1]=vsun[1]+vlsr c = coord.SkyCoord(l=l*u.deg, b=b*u.deg, distance=dist*u.kpc, pm_l_cosb=pml*u.mas/u.yr, pm_b=pmb*u.mas/u.yr, radial_velocity=0*u.km/u.s, frame='galactic') vsun = coord.CartesianDifferential(vsun*u.km/u.s) gc_frame = coord.Galactocentric(galcen_v_sun=vsun, z_sun=0*u.kpc) ccorr=gala.reflex_correct(c,gc_frame) return ccorr.pm_l_cosb.value, ccorr.pm_b.value
def setup_class(cls): """Setup fixtures for testing.""" cls.num = 40 cls.affine = np.linspace(0, 10, num=cls.num) * u.Myr cls.rep = coord.CartesianDifferential( d_x=np.linspace(3, 4, num=cls.num) * u.km / u.s, d_y=np.linspace(4, 5, num=cls.num) * u.km / u.s, d_z=np.linspace(5, 6, num=cls.num) * u.km / u.s, ) cls.inst = cls.klass(rep=cls.rep, affine=cls.affine)
def get_solar_and_R_gal(): # Set up Solar position and motion. sun_xyz = [-8.122, 0, 0] * u.kpc sun_vxyz = [12.9, 245.6, 7.78] * u.km/u.s sun_vxyzCD = coord.CartesianDifferential(12.9, 245.6, 7.78, u.km/u.s) galcen_frame = coord.Galactocentric(galcen_distance=np.abs(sun_xyz[0]), galcen_v_sun=sun_vxyzCD, z_sun=sun_xyz[2].value*1e3*u.pc) # Rotation matrix from Galactocentric to ICRS R_gal, _ = get_matrix_vectors(galcen_frame, inverse=True) return sun_xyz, sun_vxyz, R_gal, galcen_frame
def pmradec_solar_correction(ra, dec, dist, pmra, pmdec,vsun=(11.1, 12.2, 7.25),vlsr=235, vrad=0): vsun=np.array(vsun) vsun[1]=vsun[1]+vlsr c = coord.SkyCoord(ra=ra*u.deg, dec=dec*u.deg, distance=dist*u.kpc, pm_ra_cosdec=pmra*u.mas/u.yr, pm_dec=pmdec*u.mas/u.yr, radial_velocity=0*u.km/u.s) vsun = coord.CartesianDifferential(vsun*u.km/u.s) gc_frame = coord.Galactocentric(galcen_v_sun=vsun, z_sun=0*u.kpc) ccorr=gala.reflex_correct(c,gc_frame) return ccorr.pm_ra_cosdec.value, ccorr.pm_dec.value
def gc(ra, dec, p, pmra, pmdec, rv): # Coordinate transformation (ICRS to Galactic) coordinates = coord.ICRS(ra=np.multiply(ra, u.degree), dec=np.multiply(dec, u.degree), distance=(list(p.values) * u.mas).to(u.pc, u.parallax()), pm_ra_cosdec=np.multiply(pmra, u.mas/u.yr), pm_dec=np.multiply(pmdec, u.mas/u.yr), radial_velocity=np.multiply(rv, u.km/u.s)) # Returns (x, y, z) spatial coordinates wrt galactic center, thus distance is the square difference # Returns (v_x, v_y, v_z) velocity coordinates wrt galactic center, thus velocity is the square difference # Velocity of the Sun v_sun = coord.CartesianDifferential([12.4, 236, 7.7]*u.km/u.s) # Galactocentric coordinates return coordinates.transform_to(coord.Galactocentric(galcen_distance=8 * u.kpc, galcen_v_sun=v_sun, z_sun=17 * u.pc))
def __call__(self, n: int = 1, representation_type: TH.OptRepresentationLikeType = None, random: RandomLike = None, **kwargs) -> TH.SkyCoordType: """Sample. Parameters ---------- n : int number of samples frame : frame-like or None output frame of samples **kwargs: ignored. Returns ------- SkyCoord """ # Get preferred frame and representation frame = self.frame representation_type = self._infer_representation(representation_type) # TODO accepts a potential parameter. what does this do? # TODO confirm random seed. with self._random_context(random): pos, masses = self._potential.sample(n=n) # process the position and mass if np.shape(pos)[1] == 6: pos, vel = pos[:, :3], pos[:, 3:] # TODO: vel differentials = dict(s=coord.CartesianDifferential(*vel.T * u.km / u.s), ) else: differentials = None rep = coord.CartesianRepresentation(*pos.T * u.kpc, differentials=differentials) if representation_type is None: representation_type = rep.__class__ samples = SkyCoord( frame.realize_frame(rep, representation_type=representation_type), copy=False, ) samples.cache["mass"] = masses * u.solMass samples.cache["potential"] = self.potential return samples
def ecef_to_eci(ecef_positions, ecef_velocities, posix_times): """Converts one or multiple Cartesian vectors in the ECEF to a GCRS frame at the given time. When converting multiple objects, each object should have a ECEF position, ECEF velocity and a reference time in the same index of the appropriate input list Args: ecef_positions (list of tuples): A list of the Cartesian coordinate tuples of the objects in the ECCF frame (m) ecef_velocities (list of tuples): A list of the velocity tuples of the objects in the EECF frame (m/s) time_posix (int): A list of times to be used as reference frame time for objects Returns: A list of tuples, each tuple has the following format: (Position Vector3D(x,y,z), Velocity Vector3D(x,y,z)) Position Vector: x = GCRS X-coordinate (m) y = GCRS Y-coordinate (m) z = GCRS Z-coordinate (m) Velocity Vector x = GCRS X-velocity (m/s) y = GCRS Y-velocity (m/s) z = GCRS Z-velocity (m/s) Note: Unlike the rest of the software that uses J2000 FK5, the ECI frame used here is GCRS; This can potentially introduce around 200m error for locations on surface of Earth. """ posix_times = Time(posix_times, format='unix') cart_diff = coordinates.CartesianDifferential(ecef_velocities, unit='m/s', copy=False) cart_rep = coordinates.CartesianRepresentation(ecef_positions, unit='m', differentials=cart_diff, copy=False) ecef = coordinates.ITRS(cart_rep, obstime=posix_times) gcrs = ecef.transform_to(coordinates.GCRS(obstime=posix_times)) # pylint: disable=no-member positions = array(transpose(gcrs.cartesian.xyz.value), ndmin=2) velocities = array(transpose(gcrs.cartesian.differentials.values()[0].d_xyz .to(units.m / units.s).value), ndmin=2) # pylint: enable=no-member ret_pairs = [(Vector3D(*pos), Vector3D(*vel)) for pos, vel in zip(positions, velocities)] return ret_pairs
def _add_galactic_cartesian_data(input_data, reverse=False, gal_coor=True): """ :param input_data: :param reverse: :param gal_coor: :return: """ output_data = deepcopy(input_data) # input ra/dec coordinates of object(s) ra_dec = coord.ICRS(ra=input_data['ra'] * un.deg, dec=input_data['dec'] * un.deg, distance=1e3 / input_data['parallax'] * un.pc, pm_ra_cosdec=input_data['pmra'] * un.mas / un.yr, pm_dec=input_data['pmdec'] * un.mas / un.yr, radial_velocity=input_data['rv'] * un.km / un.s) # convert to galactic cartesian values if not gal_coor: cartesian_coord = ra_dec.transform_to(coord.Galactic).cartesian else: # again same velocities and distances as used by JBH in his paper v_sun = coord.CartesianDifferential([11., 238. + 10., 7.25] * un.km / un.s) gc_frame = coord.Galactocentric(galcen_distance=8.2 * un.kpc, galcen_v_sun=v_sun, z_sun=25 * un.pc) cartesian_coord = ra_dec.transform_to(gc_frame).cartesian # store computed positional values back to the output_data['x'] = cartesian_coord.x.value output_data['y'] = cartesian_coord.y.value output_data['z'] = cartesian_coord.z.value # get differential (velocities) cartesian values cartesian_vel = cartesian_coord.differentials cartesian_vel = cartesian_vel[list(cartesian_vel.keys())[0]] # convert them to pc/yr for easier computation if reverse: vel_multi = -1. else: vel_multi = 1. output_data['d_x'] = vel_multi * cartesian_vel.d_x.to(un.pc / un.yr).value output_data['d_y'] = vel_multi * cartesian_vel.d_y.to(un.pc / un.yr).value output_data['d_z'] = vel_multi * cartesian_vel.d_z.to(un.pc / un.yr).value return output_data
def getXYZUVW(ra, dec, distance, pm_ra_cosdec, pm_dec, radial_velocity, v_sun=pmsun, galcen_distance=Rs): #degree,degree,kpc,mas/yr,mas/yr,km/s,km/s,kpc c1=coord.ICRS(ra=ra*u.degree,dec=dec*u.degree,distance=distance*u.kpc,\ pm_ra_cosdec=pm_ra_cosdec*u.mas/u.yr,pm_dec=pm_dec*u.mas/u.yr,\ radial_velocity=radial_velocity*u.km/u.s) gc_frame=coord.Galactocentric(galcen_distance=galcen_distance*u.kpc,\ galcen_v_sun=coord.CartesianDifferential(v_sun*u.km/u.s)) gc2 = c1.transform_to(gc_frame) #kpc,kpc,kpc,km/s,km/s,km/s return [ gc2.x.value, gc2.y.value, gc2.z.value, gc2.v_x.value, gc2.v_y.value, gc2.v_z.value ]
def test_reflex(): c = coord.SkyCoord(ra=162 * u.deg, dec=-17 * u.deg, distance=172 * u.pc, pm_ra_cosdec=-11 * u.mas / u.yr, pm_dec=4 * u.mas / u.yr, radial_velocity=110 * u.km / u.s) # First, test execution but don't validate reflex_correct(c) with coord.galactocentric_frame_defaults.set('v4.0'): reflex_correct(c, coord.Galactocentric(z_sun=0 * u.pc)) # Reflext correct the observed, Reid & Brunthaler (2004) Sgr A* measurements # and make sure the corrected velocity is close to zero # https://ui.adsabs.harvard.edu/abs/2004ApJ...616..872R/abstract # also using # https://ui.adsabs.harvard.edu/abs/2018RNAAS...2d.210D/abstract # https://ui.adsabs.harvard.edu/abs/2018A%26A...615L..15G/abstract vsun = coord.CartesianDifferential([12.9, 245.6, 7.78] * u.km / u.s) with coord.galactocentric_frame_defaults.set('v4.0'): galcen_fr = coord.Galactocentric(galcen_distance=8.122 * u.kpc, galcen_v_sun=vsun, z_sun=20.8 * u.pc) sgr_Astar_obs = coord.SkyCoord(ra=galcen_fr.galcen_coord.ra, dec=galcen_fr.galcen_coord.dec, distance=galcen_fr.galcen_distance, pm_ra_cosdec=-3.151 * u.mas / u.yr, pm_dec=-5.547 * u.mas / u.yr, radial_velocity=-12.9 * u.km / u.s) new_c = reflex_correct(sgr_Astar_obs, galcen_fr) assert u.allclose(new_c.pm_ra_cosdec, 0 * u.mas / u.yr, atol=1e-2 * u.mas / u.yr) assert u.allclose(new_c.pm_dec, 0 * u.mas / u.yr, atol=1e-2 * u.mas / u.yr) assert u.allclose(new_c.radial_velocity, 0 * u.km / u.s, atol=1e-1 * u.km / u.s)
def orbital_plane(self, time): """Compute the orbit at specified times in the two-body barycentric frame aligned with the orbital plane (xyz). Parameters ---------- time : array_like, `astropy.time.Time` Array of times as barycentric MJD values, or an Astropy `~astropy.time.Time` object containing the times to evaluate at. """ # mean anomaly with u.set_enabled_equivalencies(u.dimensionless_angles()): M = 2*pi * (time.tcb - self.t0.tcb) / self.P - self.M0 M = M.to(u.radian) # eccentric anomaly E = eccentric_anomaly_from_mean_anomaly(M, self.e) # true anomaly f = true_anomaly_from_eccentric_anomaly(E, self.e) # distance from center of mass to orbiting body r = self.a * (1. - self.e * np.cos(E)) # compute the orbit in the cartesian, orbital plane system (xyz): x = r * np.cos(f) y = r * np.sin(f) z = np.zeros_like(x) fac = 2*pi * self.a / self.P / np.sqrt(1 - self.e**2) vx = -fac * np.sin(f) vy = fac * (np.cos(f) + self.e) vz = np.zeros_like(vx) xyz = coord.CartesianRepresentation(x=x, y=y, z=z) vxyz = coord.CartesianDifferential(d_x=vx, d_y=vy, d_z=vz) return xyz.with_differentials(vxyz)
def el2rv(inc, raan, ecc, argp, mean_anomaly, mean_motion, epoch): """ Converts mean orbital elements to state vector """ time_tle = epoch.jd - 2433281.5 sat = Satrec() sat.sgp4init( WGS84, "i", 0, time_tle, 0.0, 0.0, 0.0, ecc, argp, inc, mean_anomaly, mean_motion, raan, ) errorCode, rTEME, vTEME = sat.sgp4(epoch.jd1, epoch.jd2) if errorCode != 0: raise RuntimeError(SGP4_ERRORS[errorCode]) pTEME = coord.CartesianRepresentation(rTEME * u.km) vTEME = coord.CartesianDifferential(vTEME * u.km / u.s) svTEME = TEME(pTEME.with_differentials(vTEME), obstime=epoch) svITRS = svTEME.transform_to(coord.ITRS(obstime=epoch)) orb = Orbit.from_coords(Earth, svITRS) return orb.r, orb.v
icrs = coord.ICRS(ra=star["ra"] * u.deg, dec=star["dec"] * u.deg, distance=1.0 / star["parallax"] * u.kpc, pm_ra_cosdec=star["pmra"] * u.mas / u.yr, pm_dec=star["pmdec"] * u.mas / u.yr, radial_velocity=star['radial_velocity'] * u.km / u.s) """ icrs_err = coord.ICRS(ra=0*u.deg, dec=0*u.deg, distance=6*u.kpc, pm_ra_cosdec=0.009*u.mas/u.yr, pm_dec=0.009*u.mas/u.yr, radial_velocity=0.1*u.km/u.s) """ v_sun = coord.CartesianDifferential([0, 0, 0] * u.km / u.s) gc_frame = coord.Galactocentric(galcen_distance=8.3 * u.kpc, z_sun=0 * u.pc, galcen_v_sun=v_sun) gc = icrs.transform_to(gc_frame) w0 = gd.PhaseSpacePosition(gc.data) if (star["Ba_only_candidate"] == 'true' and star["Ba_Sr_candidate"] == 'false'): Ba_velx.append(w0.v_x.to(u.km / u.s).value) Ba_vely.append(w0.v_y.to(u.km / u.s).value) Ba_velz.append(w0.v_z.to(u.km / u.s).value) Ba_y.append(np.sqrt(Ba_velx[-1]**2 + Ba_velz[-1]**2))
print(gc1.v_x, gc1.v_y, gc1.v_z) ############################################################################## # The default parameters for the `~astropy.coordinates.Galactocentric` frame # are detailed in the linked documentation, but we can modify the most commonly # changes values using the keywords ``galcen_distance``, ``galcen_v_sun``, and # ``z_sun`` which set the sun-Galactic center distance, the 3D velocity vector # of the sun, and the height of the sun above the Galactic midplane, # respectively. The velocity of the sun must be specified as a # `~astropy.coordinates.CartesianDifferential` instance, as in the example # below. Note that, as with the positions, the Galactocentric frame is a # right-handed system - the x-axis is positive towards the Galactic center, so # ``v_x`` is opposite of the Galactocentric radial velocity: v_sun = coord.CartesianDifferential([11.1, 244, 7.25] * u.km / u.s) gc_frame = coord.Galactocentric(galcen_distance=8 * u.kpc, galcen_v_sun=v_sun, z_sun=0 * u.pc) ############################################################################## # We can then transform to this frame instead, with our custom parameters: gc2 = c1.transform_to(gc_frame) print(gc2.v_x, gc2.v_y, gc2.v_z) ############################################################################## # It's sometimes useful to specify the solar motion using the `proper motion # of Sgr A* <https://arxiv.org/abs/astro-ph/0408107>`_ instead of Cartesian # velocity components. With an assumed distance, we can convert proper motion # components to Cartesian velocity components using `astropy.units`:
pm_dec=pm_dec, radial_velocity=rv) def compute_orbit(icrs, dt=-0.1 * u.Myr, n_steps=50000): """Integrate the orbit.""" c_icrs = icrs.transform_to(gc_frame).cartesian object_phase_space = gd.PhaseSpacePosition( pos=c_icrs.xyz, vel=c_icrs.differentials['s'].d_xyz) return gp.Hamiltonian(pot).integrate_orbit(object_phase_space, dt=dt, n_steps=n_steps) pot = gp.MilkyWayPotential() v_sun = coord.CartesianDifferential([11, 248, 7.25] * u.km / u.s) gc_frame = coord.Galactocentric(galcen_distance=8.2 * u.kpc, z_sun=25. * u.pc, galcen_v_sun=v_sun) icrs = coord.ICRS(ra=262.806 * u.deg, dec=-39.822 * u.deg, distance=11.5 * u.kpc, pm_ra_cosdec=-2.85 * u.mas / u.yr, pm_dec=2.55 * u.mas / u.yr, radial_velocity=227 * u.km / u.s) icrs_err = coord.ICRS(ra=0 * u.deg, dec=0 * u.deg, distance=1. * u.kpc, pm_ra_cosdec=0.1 * u.mas / u.yr, pm_dec=0.1 * u.mas / u.yr,
nloop = args.n dt = args.dt * u.Myr noerr = args.no_errors node = args.node totnodes = args.tot if (nproc > 1): print('multithreading activated, using ', nproc, ' processors') # defines reference frame rsun = 8 * u.kpc zsun = 0.025 * u.kpc vsun = [11.1, 232.24, 7.25] * u.km / u.s gc_frame = coord.Galactocentric( galcen_distance=rsun, galcen_v_sun=coord.CartesianDifferential(*vsun), z_sun=zsun) # load in data, load MW potential with warnings.catch_warnings(record=True): warnings.simplefilter("ignore") gaiadata = GaiaData(filein) hdul = fits.open(filein) sttable = Table(hdul[1].data) if (totnodes > 1): thiskeys = np.array_split(range(len(sttable['source_id'])), totnodes)[node] gaiadata = gaiadata[thiskeys] sttable = sttable[thiskeys] mw = gp.MilkyWayPotential()
axes[1, 0].set_xlabel('Pmra'); axes[1, 1].hist(sample[:, 4], bins='fd') axes[1, 1].set_xlabel('Pmdec'); axes[1, 2].hist(sample[:, 5], bins='fd') axes[1, 2].set_xlabel('RV'); pp.savefig(bbox_inches='tight') pp.close() assert np.all(sample[:, 2] > 0) # Calculate dynamics print('Get dynamics') R0 = 8.34 * u.kpc # Reid et al 2014 V0 = 240 * u.km/u.s # Reid et al 2014 z_sun = 27 * u.pc # Chen et al 2001 v_sun = coord.CartesianDifferential([11.1, -(240+12.24), 7.25] * u.km/u.s) # Schonrich et al 2010 # NB: Here I just use the inverse parallax as dist cs = coord.SkyCoord(ra=sample[:, 0] * u.deg, dec=sample[:, 1] * u.deg, distance=coord.Distance(parallax=sample[:, 2] * u.mas), pm_ra_cosdec=sample[:, 3] * u.mas / u.yr, pm_dec=sample[:, 4] * u.mas / u.yr, radial_velocity=sample[:, 5] * u.km / u.s, galcen_distance=R0, galcen_v_sun=v_sun, z_sun=z_sun) # Define Galactocentric frame gc = coord.Galactocentric(galcen_distance=R0, galcen_v_sun=v_sun, # 240 is from Reid etal 2014 z_sun=z_sun) # Default, Chen et al 2001
import matplotlib.patches as mp from astropy.time import Time #import plotting_dicts as pod ########### #Galactic parameters from Gaia collaboration (D. Katz et al. 2018) "Mapping the Milky Way Disc Kinematics" sun_height = 27. * u.pc sun_dist = 8.34 * u.kpc circular_v_at_sun = 240. * u.km / u.s v_sun_pec = [11.1, 12.24, 7.25] * u.km / u.s v_sun_bobylev = [ 7.90, 11.73, 7.39 ] * u.km / u.s #Bobylev 2017 value, used by Torres et al. 2019 v_sun_tot = coord.CartesianDifferential( [v_sun_pec[0], v_sun_pec[1] + circular_v_at_sun, v_sun_pec[2]]) v_sun_bobylev_diff = coord.CartesianDifferential(v_sun_bobylev) gc_frame = coord.Galactocentric(galcen_distance=sun_dist, galcen_v_sun=v_sun_tot, z_sun=sun_height) galcart = coord.Galactic(representation_type=coord.CartesianRepresentation, differential_type=coord.CartesianDifferential) #altGalacticLSR=coord.GalacticLSR(v_bary=v_sun_bobylev_diff) altGalacticLSR = coord.GalacticLSR(v_bary=v_sun_bobylev_diff, differential_type='cartesian') #galLSR_base=coord.GalacticLSR
def astrometric_excess_noise(t, P, m1, m2, f1=None, f2=None, e=0, t0=None, omega=0 * u.deg, i=0 * u.deg, Omega=0 * u.deg, origin=None, **kwargs): """ Calculate the astrometric excess noise for a binary system with given properties that was observed at certain times from the given origin position. # TODO: There are a number of assumptions that we look over here :param t: The times that the system was observed. :param P: The period of the binary system. :param m1: The mass of the primary body. :param m2: The mass of the secondary body. :param f1: [optional] The flux of the primary body. If `None` is given then $M_1^{3.5}$ will be assumed. :param f2: [optional] The flux of the secondary body. If `None` is given then $M_2^{3.5}$ will be assumed. :param e: [optional] The eccentricity of the system (default: 0). # TODO: more docs pls """ # TODO: Re-factor this behemoth by applying the weights after calculating positions? if f1 is None: f1 = m1.to(u.solMass).value**3.5 if f2 is None: f2 = m2.to(u.solMass).value**3.5 if t0 is None: t0 = Time('J2015.5') N = t.size # Compute orbital positions. with u.set_enabled_equivalencies(u.dimensionless_angles()): M1 = (2 * np.pi * (t.tcb - t0.tcb) / P).to(u.radian) # Set secondary to have opposite phase. M2 = (2 * np.pi * (t.tcb - t0.tcb) / P - np.pi).to(u.radian) # eccentric anomaly E1 = twobody.eccentric_anomaly_from_mean_anomaly(M1, e) E2 = twobody.eccentric_anomaly_from_mean_anomaly(M2, e) # mean anomaly F1 = twobody.true_anomaly_from_eccentric_anomaly(E1, e) F2 = twobody.true_anomaly_from_eccentric_anomaly(E2, e) # Calc a1/a2. m_total = m1 + m2 a = twobody.P_m_to_a(P, m_total) a1 = m2 * a / m_total a2 = m1 * a / m_total r1 = (a1 * (1. - e * np.cos(E1))).to(u.au).value r2 = (a2 * (1. - e * np.cos(E2))).to(u.au).value # Calculate xy positions in orbital plane. x = np.vstack([ r1 * np.cos(F1), r2 * np.cos(F2), ]).value y = np.vstack([r1 * np.sin(F1), r2 * np.sin(F2)]).value # Calculate photocenter in orbital plane. w = np.atleast_2d([f1, f2]) / (f1 + f2) x, y = np.vstack([w @ x, w @ y]) z = np.zeros_like(x) # Calculate photocenter velocities in orbital plane. fac = (2 * np.pi * a / P / np.sqrt(1 - e**2)).to(u.au / u.s).value vx = np.vstack([-fac * np.sin(F1), -fac * np.sin(F2)]).value vy = np.vstack([fac * (np.cos(F1) + e), fac * (np.cos(F2) + e)]).value vx, vy = np.vstack([w @ vx, w @ vy]) vz = np.zeros_like(vx) # TODO: handle units better w/ dot product x, y, z = (x * u.au, y * u.au, z * u.au) vx, vy, vz = (vx * u.au / u.s, vy * u.au / u.s, vz * u.au / u.s) xyz = coord.CartesianRepresentation(x=x, y=y, z=z) vxyz = coord.CartesianDifferential(d_x=vx, d_y=vy, d_z=vz) xyz = xyz.with_differentials(vxyz) vxyz = xyz.differentials["s"] xyz = xyz.without_differentials() # Construct rotation matrix from orbital plane system to reference plane system. R1 = rotation_matrix(-omega, axis='z') R2 = rotation_matrix(i, axis='x') R3 = rotation_matrix(Omega, axis='z') Rot = matrix_product(R3, R2, R1) # Rotate photocenters to the reference plane system. XYZ = coord.CartesianRepresentation(matrix_product(Rot, xyz.xyz)) VXYZ = coord.CartesianDifferential(matrix_product(Rot, vxyz.d_xyz)) XYZ = XYZ.with_differentials(VXYZ) barycenter = twobody.Barycenter(origin=origin, t0=t0) kw = dict(origin=barycenter.origin) rp = twobody.ReferencePlaneFrame(XYZ, **kw) # Calculate the ICRS positions. icrs_cart = rp.transform_to(coord.ICRS).cartesian icrs_pos = icrs_cart.without_differentials() icrs_vel = icrs_cart.differentials["s"] bary_cart = barycenter.origin.cartesian bary_vel = bary_cart.differentials["s"] dt = t - barycenter.t0 dx = (bary_vel * dt).to_cartesian() pos = icrs_pos + dx vel = icrs_vel + bary_vel icrs = coord.ICRS(pos.with_differentials(vel)) positions = np.array([icrs.ra.deg, icrs.dec.deg]) mean_position = np.mean(positions, axis=1) assert mean_position.size == 2 ''' __intrinsic_ra_error = 0.029 # mas __intrinsic_dec_error = 0.026 # mas __intrinsic_ra_error /= 10 __intrinsic_dec_error /= 10 chi2 = N * rms_in_mas.to(u.mas).value**2 / np.sqrt(__intrinsic_ra_error**2 + __intrinsic_dec_error**2) approx_ruwe = np.sqrt(chi2/(N - 2)) ''' # Calculate on sky RMS. astrometric_rms = np.sqrt(np.sum((positions.T - mean_position)**2) / N) astrometric_rms *= u.deg diff = ((positions.T - mean_position) * u.deg).to(u.mas) #chi2 = diff**2 / (__intrinsic_ra_error**2 + __intrinsic_dec_error**2) ruwe = np.sqrt( np.sum((diff.T[0] / __intrinsic_ra_error)**2 + (diff.T[1] / __intrinsic_dec_error)**2) / (N - 2)).value meta = dict() return (ruwe, meta)
# Galactocentric position of the Sun: X_GC_sun_kpc = 8.122 #[kpc] # (Gravity collaboration 2018) Z_GC_sun_kpc = 0.025 #[kpc] (e.g. Juric et al. 2008) # Galactocentric velocity of the Sun: vX_GC_sun_kms = -11.1 # [km/s] (e.g. Schoenrich et al. 2009) vY_GC_sun_kms = 245.8 # [km/s] (combined with Sgr A* proper motions from Reid & Brunnthaler 2004) vZ_GC_sun_kms = 7.8 # [km/s] # ------------------------------------------------------------------------------- # transformation # ------------------------------------------------------------------------------- gc = coord.Galactocentric( galcen_distance=X_GC_sun_kpc * u.kpc, galcen_v_sun=coord.CartesianDifferential( [-vX_GC_sun_kms, vY_GC_sun_kms, vZ_GC_sun_kms] * u.km / u.s), z_sun=Z_GC_sun_kpc * u.kpc) galcen = cs.transform_to(gc) xs, ys, zs = galcen.x.to(u.kpc), galcen.y.to(u.kpc), galcen.z.to(u.kpc) vxs, vys, vzs = galcen.v_x, galcen.v_y, galcen.v_z XS = np.vstack([xs, ys, zs, vxs, vys, vzs]).T.value Xlimits = [[-30, 10], [-10, 30], [-20, 20], [-200, 200], [-200, 200], [-200, 200]] Xlabels = ['$x$', '$y$', '$z$', r'$v_x$', r'$v_y$', r'$v_z$'] d2d = np.sqrt(XS[:, 0]**2 + XS[:, 1]**2) units = XS[:, 0:2] / d2d[:, None] perps = np.zeros_like(units) perps[:, 0] = units[:, 1]
# radial_velocity=-58.7*u.km/u.s) # These are from fitting the sky track of the stream pal5_c = coord.SkyCoord(phi1=0*u.deg, phi2=1.53066768e-02*u.deg, distance=22.4957324*u.kpc, pm_phi1_cosphi2=3.07244231*u.mas/u.yr, pm_phi2=0.637385934*u.mas/u.yr, radial_velocity=-56.2232384*u.km/u.s, frame=gc.Pal5PriceWhelan18).transform_to(coord.ICRS) pal5_M = 14404 * u.Msun # Setting Sun's params - should update distance v_lsr = [11.1, 12.24, 7.25]*u.km/u.s v_circ = 220 * u.km/u.s v_sun = coord.CartesianDifferential(v_lsr + [0, 1, 0] * v_circ) galcen_frame = coord.Galactocentric(galcen_distance=8.1*u.kpc, galcen_v_sun=v_sun) trail_a = [230, 242] trail_d = [0.75, 6.6] trail_epts = coord.SkyCoord(ra=trail_a*u.deg, dec=trail_d*u.deg) lead_a = [228, 225] lead_d = [-1.2, -4.3] lead_epts = coord.SkyCoord(ra=lead_a*u.deg, dec=lead_d*u.deg) pal5_lead_frame = gc.GreatCircleICRSFrame.from_endpoints(lead_epts[0], lead_epts[1], ra0=pal5_c.ra) pal5_trail_frame = gc.GreatCircleICRSFrame.from_endpoints(trail_epts[0], trail_epts[1],