From 07c052bcfcc07fc6740feac77b6131e3534b5a43 Mon Sep 17 00:00:00 2001 From: Saima Date: Tue, 16 Aug 2022 16:22:44 -0400 Subject: [PATCH 1/5] Changes to Coordinates 3 Tutorial --- .../3_Coordinates_Velocities.ipynb | 639 ++++++++++++++++++ 1 file changed, 639 insertions(+) create mode 100644 tutorials/astropy-coordinates/3_Coordinates_Velocities.ipynb diff --git a/tutorials/astropy-coordinates/3_Coordinates_Velocities.ipynb b/tutorials/astropy-coordinates/3_Coordinates_Velocities.ipynb new file mode 100644 index 00000000..fc62874c --- /dev/null +++ b/tutorials/astropy-coordinates/3_Coordinates_Velocities.ipynb @@ -0,0 +1,639 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "K3gI522hpNR1" + }, + "source": [ + "# Astronomical Coordinates 3: Working with Velocity Data in astropy.coordinates\n", + "\n", + "## Authors\n", + "Adrian Price-Whelan, Saima Siddiqui, Luthien Liu, Zihao Chen\n", + "\n", + "## Learning Goals\n", + "* Introduce how to represent and transform velocity data in `SkyCoord` objects\n", + "* Demonstrate how to predict the position of a star at a time using its proper motion\n", + "\n", + "## Keywords\n", + "coordinates, OOP, astroquery, gaia\n", + "\n", + "\n", + "## Summary\n", + "\n", + "In the previous tutorial in this series, we showed how astronomical *positional* coordinates can be represented and transformed in Python using the `SkyCoord` object ([docs](https://docs.astropy.org/en/stable/coordinates/skycoord.html)). Many sources, especially stars (thanks to the Gaia mission), have measured velocities or measured components of their velocity (e.g., just proper motion, or just radial velocity).\n", + "\n", + "In this tutorial, we will explore how the `astropy.coordinates` package can be used to represent and transform astronomical coordinates that have associated velocity data. You may find it helpful to keep [the Astropy documentation for the coordinates package](http://docs.astropy.org/en/stable/coordinates/index.html) open alongside this tutorial for reference or additional reading. In the text below, you may also see some links that look like ([docs](http://docs.astropy.org/en/stable/coordinates/index.html)). These links will take you to parts of the documentation that are directly relevant to the cells from which they link. \n", + "\n", + "*Note: This is the 3rd tutorial in a series of tutorials about astropy.coordinates. If you are new to astropy.coordinates, you may want to start from the beginning or an earlier tutorial.*\n", + "- [Previous tutorial: Astronomical Coordinates 2 - Transforming Coordinate Systems and Representations¶](2-Coordinates-Transforms)\n", + "- [Next tutorial: Astronomical Coordinates 4 - Cross-matching Catalogs](4-Coordinates-Crossmatch)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "m3RngEEJpNR5" + }, + "source": [ + "## Imports\n", + "\n", + "We start by importing some general packages we will need below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "EI1u9VzIpNR6", + "outputId": "f7fdb1dc-4716-4449-bad0-73f6c588e94c" + }, + "outputs": [], + "source": [ + "import warnings\n", + "\n", + "import matplotlib.pyplot as plt\n", + "%matplotlib inline\n", + "import numpy as np\n", + "!pip install \"astroquery\"\n", + "\n", + "from astropy import units as u\n", + "from astropy.coordinates import SkyCoord, Distance, Galactic\n", + "import astropy.coordinates as coord\n", + "from astropy.io import fits\n", + "from astropy.table import QTable\n", + "from astropy.time import Time\n", + "from astropy.utils.data import download_file\n", + "from astropy.wcs import WCS\n", + "\n", + "from astroquery.gaia import Gaia" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Rtvp07W2pNR9" + }, + "source": [ + "## More Than Sky Positions: Including Velocity Data in `SkyCoord`\n", + "\n", + "As we have seen in the previous tutorials, the `SkyCoord` object can be used to store scalars or arrays of positional coordinate information and supports transforming between different coordinate frames and representations. But `astropy.coordinates` also supports representing and transforming *velocity* information along with positions ([docs](http://docs.astropy.org/en/latest/coordinates/velocities.html)).\n", + "\n", + "## Passing Velocity Data into `SkyCoord`\n", + "\n", + "Velocity components are passed in to `SkyCoord` in the same way that positional components are specified: As arguments to the `SkyCoord` class. For example, to create a `SkyCoord` to represent a sky position and a proper motion in the (default) ICRS coordinate frame, in addition to the position components `ra`, `dec`, we can pass in values for the proper motion components `pm_ra_cosdec`, `pm_dec` (\"pm\" for \"proper motion\"):" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "QOqwEMG3pNR9" + }, + "outputs": [], + "source": [ + "SkyCoord(\n", + " ra=10*u.deg, \n", + " dec=20*u.deg,\n", + " pm_ra_cosdec=1*u.mas/u.yr,\n", + " pm_dec=2*u.mas/u.yr)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "v6EeE4KsfSpq" + }, + "source": [ + "**Thought Questions:**\n", + "What value did we put in for our right ascension? What about our declination? What units are they in?" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "1U0966fepNR-" + }, + "source": [ + "Here, you may notice that the proper motion in right ascension has \"cosdec\" in the name: This is to explicitly note that the input here is expected to be the proper motion scaled by the cosine of the declination, which accounts for the fact that a change in longitude (right ascension) has different physical length at different latitudes (declinations).\n", + "\n", + "Like the examples in previous tutorials demonstrated for positional coordinates, we can also create an array-valued `SkyCoord` object by passing in arrays of data for all of the components. In this case, each value in the inputed array represents a quantity of an object among large data set. This method would be beneficial when dealing with large number of star collections like a star cluster:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "GnQ8m4AGpNR-" + }, + "outputs": [], + "source": [ + "SkyCoord(\n", + " ra=np.linspace(0, 10, 5)*u.deg, \n", + " dec=np.linspace(5, 20, 5)*u.deg,\n", + " pm_ra_cosdec=np.linspace(-5, 5, 5)*u.mas/u.yr,\n", + " pm_dec=np.linspace(-5, 5, 5)*u.mas/u.yr)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "x8fDaEI9pNR_" + }, + "source": [ + "However, for some of the examples below we will continue to use scalar values for brevity. \n", + "\n", + "You can also specify radial velocity data with the `radial_velocity` argument:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "C-jgFsdfpNSA" + }, + "outputs": [], + "source": [ + "velocity_coord = SkyCoord(\n", + " ra=10*u.deg, \n", + " dec=20*u.deg,\n", + " pm_ra_cosdec=1*u.mas/u.yr,\n", + " pm_dec=2*u.mas/u.yr,\n", + " radial_velocity=100*u.km/u.s)\n", + "velocity_coord" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "v7ztcRr-pNSB" + }, + "source": [ + "The component data can then be accessed using the same names used to pass in the velocity components:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "p3_7hP6LpNSB" + }, + "outputs": [], + "source": [ + "velocity_coord.pm_ra_cosdec" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "CM1TApDYpNSB" + }, + "outputs": [], + "source": [ + "velocity_coord.radial_velocity" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "gxGKcYFZpNSC" + }, + "source": [ + "A `SkyCoord` object with velocity data can be transformed to other frames just like the position-only coordinate objects we used in the previous tutorials:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "q6S7HmybpNSC" + }, + "outputs": [], + "source": [ + "velocity_coord_gal = velocity_coord.transform_to(Galactic())\n", + "velocity_coord_gal" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "K9wL2Rl7pNSD" + }, + "source": [ + "Note that, like the position components, which change from `ra`,`dec` to `l`,`b`, the proper motion component names have changed to reflect naming conventions for the component names in a given frame: `pm_ra_cosdec` and `pm_dec` have become `pm_l_cosb` and `pm_b`:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "g-35XIlnpNSD" + }, + "outputs": [], + "source": [ + "velocity_coord_gal.pm_l_cosb" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "PRZ_pt38pNSD" + }, + "outputs": [], + "source": [ + "velocity_coord_gal.pm_b" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "GthFqig5pNSE" + }, + "source": [ + "An important caveat to note when transforming a `SkyCoord` object with velocity data is that some reference frames require knowing the distances, or the full velocity vectors (i.e. proper motion components and radial velocity) in order to transform the velocities correctly. For example, a `SkyCoord` with only sky position and proper motion data **CANNOT** be transformed to a frame with a positional or velocity offset, such as the `Galactocentric` frame ([docs](https://docs.astropy.org/en/stable/coordinates/galactocentric.html))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "SKKyiObSpNSF", + "tags": [ + "raises-exception" + ] + }, + "outputs": [], + "source": [ + "# This cell will NOT work (you will receive an ConvertError warning) - this is expected!\n", + "\n", + "test_coord_1 = SkyCoord(\n", + " ra=10*u.deg, \n", + " dec=20*u.deg,\n", + " pm_ra_cosdec=1*u.mas/u.yr,\n", + " pm_dec=2*u.mas/u.yr)\n", + "\n", + "test_coord_1.transform_to(coord.Galactocentric())" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "LJv8-Vt-1DYh" + }, + "source": [ + "In order to transform to the `Galactocentric` frame, both distance and radial velocity of the object are required." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "gdjBvnIM3D1U" + }, + "outputs": [], + "source": [ + "test_coord_2 = SkyCoord(\n", + " ra=10*u.deg, \n", + " dec=20*u.deg,\n", + " distance=10*u.pc,\n", + " pm_ra_cosdec=1*u.mas/u.yr,\n", + " pm_dec=2*u.mas/u.yr ,\n", + " radial_velocity=100*u.km/u.s)\n", + "\n", + "test_coord_2.transform_to(coord.Galactocentric())" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "rvo8IV2YpNSF" + }, + "source": [ + "## Evolving Coordinate Positions Between Epochs\n", + "\n", + "For nearby or fast-moving stars, a star's position could change appreciably between two well-spaced observations of the source. For such cases, it might be necessary to compute the position of the star at a given time using the proper motion or velocity of the star. Let's demonstrate this idea by comparing the sky position of a source as measured by [*Gaia* Data Release 2](https://www.cosmos.esa.int/web/gaia/dr2) (given at the epoch J2015.5) to an image near this source from the Digitized Sky Survey (DSS; digital scans of photographic plates observed in the 1950s). \n", + "\n", + "From previous astrometric measurements, we know that the star HD 219829 has very large proper motion: Close to 0.5 arcsec/year! Between the DSS and *Gaia*, we therefore expect that the position of the star has changed by about 0.5 arcmin. Let's see if this is the case! \n", + "\n", + "To start, we will query the *Gaia* catalog to retrieve data for this star (skip the cell below if you do not have an internet connection - we have provided the table locally as well). We use a large search radius so many sources will be returned" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "oV1PLYwkpNSF" + }, + "outputs": [], + "source": [ + "# Skip this cell if you are not connected to the internet\n", + "gaia_tbl = Gaia.query_object(SkyCoord.from_name('HD 219829'), \n", + " radius=1*u.arcmin)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 358 + }, + "id": "O-ntoKvEpNSG", + "outputId": "3a437357-ac0d-4c93-bc03-b45a87def9e8" + }, + "outputs": [], + "source": [ + "# the .read() below produces some warnings that we can safely ignore\n", + "with warnings.catch_warnings(): \n", + " warnings.simplefilter('ignore', UserWarning)\n", + " \n", + " gaia_tbl = QTable.read('HD_219829_query_results.ecsv')" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "jiy-_1nhpNSG" + }, + "source": [ + "We know that HD 219829 will be the brightest source in this small region, so we can extract the row with the smallest G-band magnitude. Let's check the proper motion values for this source to make sure that they are large:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "nPYC_wKzpNSH" + }, + "outputs": [], + "source": [ + "hd219829_row = gaia_tbl[gaia_tbl['phot_g_mean_mag'].argmin()]\n", + "hd219829_row['source_id', 'pmra', 'pmdec']" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "zyhOiroApNSH" + }, + "source": [ + "Indeed, it looks like this is our source! Let's construct a `SkyCoord` object for this source using the data from the *Gaia* archive:\n", + "\n", + "*Note about the Gaia catalog proper motion column names: The names in the Gaia archive and other repositories containing Gaia data give Right Ascension proper motion values simply as \"pmra\". These components implicitly contain the `cos(dec)` term, so we do **not** have to modify these values in order to pass them in to `SkyCoord` as `pm_ra_cosdec`*" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "AE2xkDyxpNSH" + }, + "outputs": [], + "source": [ + "hd219829_coord = SkyCoord(\n", + " ra=hd219829_row['ra'], \n", + " dec=hd219829_row['dec'],\n", + " distance=Distance(parallax=hd219829_row['parallax']),\n", + " pm_ra_cosdec=hd219829_row['pmra'],\n", + " pm_dec=hd219829_row['pmdec'],\n", + " obstime=Time(hd219829_row['ref_epoch'], format='jyear'))\n", + "\n", + "hd219829_coord" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "ioOAqGh2pNSI" + }, + "source": [ + "We now have a `SkyCoord` representation of the position and proper motion of the star HD 219829 as measured by *Gaia* and reported at the epoch J2015.5. What does this mean exactly? *Gaia* actually measures the (time-dependent) position of a star every time it scans the part of the sky that contains the source, and this is how *Gaia* is able to measure proper motions of stars. However, if every star is moving and changing its sky positions, how do we ever talk about \"the sky position\" of a star as opposed to \"the sky trajectory of a star\"?! The key is that catalogs often only report the position of a source at some reference epoch. For a survey that only observes the sky once or a few times (e.g., SDSS or 2MASS), this reference epoch might be \"the time that the star was observed.\" But for a mission like *Gaia*, which scans the sky many times, they perform astrometric fits to the individual position measurements, which allow them to measure the parallax, proper motion, and the reference position at a reference time for each source. For *Gaia* data release 2, the reference time is J2015.5, and the sky positions (and other quantities) reported in the catalog for each source are at this epoch. \n", + "\n", + "In `SkyCoord`, we specify the \"epoch\" of an observation using the `obstime` argument, as we did above. Now that we have a coordinate object for HD 219829, let's now compare the position of the star as measured by *Gaia* to its apparent position in an image from the DSS. Let's now query the DSS to retrieve a FITS image of the field around this star, using the STSCI DSS image cutout service. Skip the cell below if you do not have an internet connection (we have provided the image locally as well):" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "A3T_h79VpNSI" + }, + "outputs": [], + "source": [ + "# Skip this cell if you are not connected to the internet\n", + "dss_cutout_filename = download_file(\n", + " f\"http://archive.stsci.edu/cgi-bin/dss_search?\"\n", + " f\"f=FITS&ra={hd219829_coord.ra.degree}&dec={hd219829_coord.dec.degree}\"\n", + " f\"&width=4&height=4\") # width/height in arcmin" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "CXbrurRipNSI" + }, + "outputs": [], + "source": [ + "dss_cutout_filename = 'dss_hd219829.fits'" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "TiRZicmlpNSI" + }, + "source": [ + "We can now load the FITS image of the cutout and use `astropy.visualization` to display the image using its World Coordinate System (WCS) info ([docs](http://docs.astropy.org/en/latest/visualization/wcsaxes/index.html)). By passing in the WCS information (included in the FITS cutout header), we can over-plot a marker for the *Gaia*-measured sky position of HD 219829:\n", + "\n", + "(If you are unfamiliar with the usage of FITS header and FITS image, check out these 2 tutorials having detailed explanation on these 2 topics: [FITS-Header](https://learn.astropy.org/tutorials/FITS-header.html) and [FITS-Image](https://learn.astropy.org/tutorials/FITS-images.html).)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "x0dVL_sEpNSJ" + }, + "outputs": [], + "source": [ + "hdu = fits.open(dss_cutout_filename)[0]\n", + "wcs = WCS(hdu.header)\n", + "\n", + "fig, ax = plt.subplots(1, 1, figsize=(8, 8), \n", + " subplot_kw=dict(projection=wcs))\n", + "ax.imshow(hdu.data, origin='lower', cmap='Greys_r')\n", + "ax.set_xlabel('RA')\n", + "ax.set_ylabel('Dec')\n", + "ax.set_autoscale_on(False)\n", + "\n", + "ax.scatter(hd219829_coord.ra.degree,\n", + " hd219829_coord.dec.degree,\n", + " s=500,\n", + " transform=ax.get_transform('world'),\n", + " facecolor='none', linewidth=2, color='tab:red')" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "UrTg6qBRpNSJ" + }, + "source": [ + "The brighest star (as observed by DSS) in this image is our target, and the red circle is where *Gaia* observed this star. As we excpected, it has moved quite a bit since the 1950's! We can account for this motion and predict the position of the star at around the time the DSS plate was observed. Let's assume that this plate was observed in 1950 exactly (this is not strictly correct, but should get us close enough).\n", + "\n", + "To account for the proper motion of the source and evolve the position to a new time, we can use the `SkyCoord.apply_space_motion()` method ([docs](http://docs.astropy.org/en/latest/api/astropy.coordinates.SkyCoord.html#astropy.coordinates.SkyCoord.apply_space_motion)). Because we defined the `obstime` when we defined the coordinate object for HD 219829, for example:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "AiUtgluXpNSJ" + }, + "outputs": [], + "source": [ + "hd219829_coord.obstime" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "CPyFWNZvpNSK" + }, + "source": [ + "We can now use `apply_space_motion()` by passing in a new time, `new_obstime`, to compute the coordinates at:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "MoRt0KbxpNSK" + }, + "outputs": [], + "source": [ + "# this produces some warnings that we can safely ignore\n", + "with warnings.catch_warnings(): \n", + " warnings.simplefilter('ignore', UserWarning)\n", + " \n", + " hd219829_coord_1950 = hd219829_coord.apply_space_motion(\n", + " new_obstime=Time('J1950'))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "uf4MK0zupNSK" + }, + "source": [ + "Let's now plot our predicted position for this source as it would appear in 1950 based on the *Gaia* position and proper motion:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "npJ2V4BqpNSK" + }, + "outputs": [], + "source": [ + "fig, ax = plt.subplots(1, 1, figsize=(8, 8), \n", + " subplot_kw=dict(projection=wcs))\n", + "ax.imshow(hdu.data, origin='lower', cmap='Greys_r')\n", + "ax.set_xlabel('RA')\n", + "ax.set_ylabel('Dec')\n", + "ax.set_autoscale_on(False)\n", + "\n", + "ax.scatter(hd219829_coord.ra.degree,\n", + " hd219829_coord.dec.degree,\n", + " s=500,\n", + " transform=ax.get_transform('world'),\n", + " facecolor='none', linewidth=2, color='tab:red')\n", + "\n", + "# Plot the predicted (past) position:\n", + "ax.scatter(hd219829_coord_1950.ra.degree,\n", + " hd219829_coord_1950.dec.degree,\n", + " s=500,\n", + " transform=ax.get_transform('world'),\n", + " facecolor='none', linewidth=2, color='tab:blue')" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "JOn_o0VQpNSL" + }, + "source": [ + "The red circle is the same as in the previous image and shows the position of the source in the *Gaia* catalog (in 2015.5). The blue circle shows our prediction for the position of the source in 1950 - this looks much closer to where the star is in the DSS image!" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "uVuz1hvOpNSL" + }, + "source": [ + "In this tutorial, we have introduced how to store and transform velocity data along with positional data in `astropy.coordinates`. We also demonstrated how to use the velocity of a source to predict its position at an earlier or later time. " + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "joBu6r_nlfTp" + }, + "source": [ + "## Exercises\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "tZK2ibb0kKuo" + }, + "source": [ + "Lalande 21185 is the brightest red dwarf star in the northern hemisphere and has a pretty high proper motion. Use the Gaia archive (https://gea.esac.esa.int/archive/) to find values and create a `SkyCoord` object for Lalande 21185. (Hint: earlier in the tutorial, we extracted information from a Gaia table and mentioned which Gaia column names match with our postition components)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "3Bz52TRvliZg" + }, + "outputs": [], + "source": [] + } + ], + "metadata": { + "colab": { + "name": "3-Coordinates-Velocities.ipynb", + "provenance": [] + }, + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + } + }, + "nbformat": 4, + "nbformat_minor": 1 +} From cba0b88fc345332f4c249350c957ea77781ff404 Mon Sep 17 00:00:00 2001 From: Saima Date: Tue, 16 Aug 2022 16:32:38 -0400 Subject: [PATCH 2/5] Changes to Coordinates 3 Tutorial --- .../3-Coordinates-Velocities.ipynb | 273 ++++++++++++++---- 1 file changed, 215 insertions(+), 58 deletions(-) diff --git a/tutorials/astropy-coordinates/3-Coordinates-Velocities.ipynb b/tutorials/astropy-coordinates/3-Coordinates-Velocities.ipynb index 3706ed08..fed617fb 100644 --- a/tutorials/astropy-coordinates/3-Coordinates-Velocities.ipynb +++ b/tutorials/astropy-coordinates/3-Coordinates-Velocities.ipynb @@ -2,12 +2,14 @@ "cells": [ { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "id": "K3gI522hpNR1" + }, "source": [ "# Astronomical Coordinates 3: Working with Velocity Data in astropy.coordinates\n", "\n", "## Authors\n", - "Adrian Price-Whelan\n", + "Adrian Price-Whelan, Saima Siddiqui, Luthien Liu, Zihao Chen\n", "\n", "## Learning Goals\n", "* Introduce how to represent and transform velocity data in `SkyCoord` objects\n", @@ -28,19 +30,11 @@ "- [Next tutorial: Astronomical Coordinates 4 - Cross-matching Catalogs](4-Coordinates-Crossmatch)" ] }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "with open('requirements.txt') as f:\n", - " print(f\"Required packages for this notebook:\\n{f.read()}\")" - ] - }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "id": "m3RngEEJpNR5" + }, "source": [ "## Imports\n", "\n", @@ -50,7 +44,13 @@ { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "EI1u9VzIpNR6", + "outputId": "f7fdb1dc-4716-4449-bad0-73f6c588e94c" + }, "outputs": [], "source": [ "import warnings\n", @@ -58,6 +58,7 @@ "import matplotlib.pyplot as plt\n", "%matplotlib inline\n", "import numpy as np\n", + "!pip install \"astroquery\"\n", "\n", "from astropy import units as u\n", "from astropy.coordinates import SkyCoord, Distance, Galactic\n", @@ -73,7 +74,9 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "id": "Rtvp07W2pNR9" + }, "source": [ "## More Than Sky Positions: Including Velocity Data in `SkyCoord`\n", "\n", @@ -87,7 +90,9 @@ { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "id": "QOqwEMG3pNR9" + }, "outputs": [], "source": [ "SkyCoord(\n", @@ -99,17 +104,31 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "id": "v6EeE4KsfSpq" + }, + "source": [ + "**Thought Questions:**\n", + "What value did we put in for our right ascension? What about our declination? What units are they in?" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "1U0966fepNR-" + }, "source": [ "Here, you may notice that the proper motion in right ascension has \"cosdec\" in the name: This is to explicitly note that the input here is expected to be the proper motion scaled by the cosine of the declination, which accounts for the fact that a change in longitude (right ascension) has different physical length at different latitudes (declinations).\n", "\n", - "Like the examples in previous tutorials demonstrated for positional coordinates, we can also create an array-valued `SkyCoord` object by passing in arrays of data for all of the components:" + "Like the examples in previous tutorials demonstrated for positional coordinates, we can also create an array-valued `SkyCoord` object by passing in arrays of data for all of the components. In this case, each value in the inputed array represents a quantity of an object among large data set. This method would be beneficial when dealing with large number of star collections like a star cluster:" ] }, { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "id": "GnQ8m4AGpNR-" + }, "outputs": [], "source": [ "SkyCoord(\n", @@ -121,7 +140,9 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "id": "x8fDaEI9pNR_" + }, "source": [ "However, for some of the examples below we will continue to use scalar values for brevity. \n", "\n", @@ -131,7 +152,9 @@ { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "id": "C-jgFsdfpNSA" + }, "outputs": [], "source": [ "velocity_coord = SkyCoord(\n", @@ -145,7 +168,9 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "id": "v7ztcRr-pNSB" + }, "source": [ "The component data can then be accessed using the same names used to pass in the velocity components:" ] @@ -153,7 +178,9 @@ { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "id": "p3_7hP6LpNSB" + }, "outputs": [], "source": [ "velocity_coord.pm_ra_cosdec" @@ -162,7 +189,9 @@ { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "id": "CM1TApDYpNSB" + }, "outputs": [], "source": [ "velocity_coord.radial_velocity" @@ -170,7 +199,9 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "id": "gxGKcYFZpNSC" + }, "source": [ "A `SkyCoord` object with velocity data can be transformed to other frames just like the position-only coordinate objects we used in the previous tutorials:" ] @@ -178,7 +209,9 @@ { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "id": "q6S7HmybpNSC" + }, "outputs": [], "source": [ "velocity_coord_gal = velocity_coord.transform_to(Galactic())\n", @@ -187,7 +220,9 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "id": "K9wL2Rl7pNSD" + }, "source": [ "Note that, like the position components, which change from `ra`,`dec` to `l`,`b`, the proper motion component names have changed to reflect naming conventions for the component names in a given frame: `pm_ra_cosdec` and `pm_dec` have become `pm_l_cosb` and `pm_b`:" ] @@ -195,7 +230,9 @@ { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "id": "g-35XIlnpNSD" + }, "outputs": [], "source": [ "velocity_coord_gal.pm_l_cosb" @@ -204,7 +241,9 @@ { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "id": "PRZ_pt38pNSD" + }, "outputs": [], "source": [ "velocity_coord_gal.pm_b" @@ -212,34 +251,68 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "id": "GthFqig5pNSE" + }, "source": [ - "An important caveat to note when transforming a `SkyCoord` object with velocity data is that some reference frames require knowing the distances, or the full velocity vectors (i.e. proper motion components and radial velocity) in order to transform the velocities correctly. For example, a `SkyCoord` with only sky position and proper motion data cannot be transformed to a frame with a positional or velocity offset, such as the `Galactocentric` frame ([docs](https://docs.astropy.org/en/stable/coordinates/galactocentric.html))" + "An important caveat to note when transforming a `SkyCoord` object with velocity data is that some reference frames require knowing the distances, or the full velocity vectors (i.e. proper motion components and radial velocity) in order to transform the velocities correctly. For example, a `SkyCoord` with only sky position and proper motion data **CANNOT** be transformed to a frame with a positional or velocity offset, such as the `Galactocentric` frame ([docs](https://docs.astropy.org/en/stable/coordinates/galactocentric.html))" ] }, { "cell_type": "code", "execution_count": null, "metadata": { + "id": "SKKyiObSpNSF", "tags": [ "raises-exception" ] }, "outputs": [], "source": [ - "test_coord = SkyCoord(\n", + "# This cell will NOT work (you will receive an ConvertError warning) - this is expected!\n", + "\n", + "test_coord_1 = SkyCoord(\n", " ra=10*u.deg, \n", " dec=20*u.deg,\n", " pm_ra_cosdec=1*u.mas/u.yr,\n", " pm_dec=2*u.mas/u.yr)\n", "\n", - "# This cell will raise an exception - this is expected!\n", - "test_coord.transform_to(coord.Galactocentric())" + "test_coord_1.transform_to(coord.Galactocentric())" ] }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "id": "LJv8-Vt-1DYh" + }, + "source": [ + "In order to transform to the `Galactocentric` frame, both distance and radial velocity of the object are required." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "gdjBvnIM3D1U" + }, + "outputs": [], + "source": [ + "test_coord_2 = SkyCoord(\n", + " ra=10*u.deg, \n", + " dec=20*u.deg,\n", + " distance=10*u.pc,\n", + " pm_ra_cosdec=1*u.mas/u.yr,\n", + " pm_dec=2*u.mas/u.yr,\n", + " radial_velocity=100*u.km/u.s)\n", + "\n", + "test_coord_2.transform_to(coord.Galactocentric())" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "rvo8IV2YpNSF" + }, "source": [ "## Evolving Coordinate Positions Between Epochs\n", "\n", @@ -253,7 +326,9 @@ { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "id": "oV1PLYwkpNSF" + }, "outputs": [], "source": [ "# Skip this cell if you are not connected to the internet\n", @@ -264,7 +339,14 @@ { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 358 + }, + "id": "O-ntoKvEpNSG", + "outputId": "3a437357-ac0d-4c93-bc03-b45a87def9e8" + }, "outputs": [], "source": [ "# the .read() below produces some warnings that we can safely ignore\n", @@ -276,7 +358,9 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "id": "jiy-_1nhpNSG" + }, "source": [ "We know that HD 219829 will be the brightest source in this small region, so we can extract the row with the smallest G-band magnitude. Let's check the proper motion values for this source to make sure that they are large:" ] @@ -284,7 +368,9 @@ { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "id": "nPYC_wKzpNSH" + }, "outputs": [], "source": [ "hd219829_row = gaia_tbl[gaia_tbl['phot_g_mean_mag'].argmin()]\n", @@ -293,7 +379,9 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "id": "zyhOiroApNSH" + }, "source": [ "Indeed, it looks like this is our source! Let's construct a `SkyCoord` object for this source using the data from the *Gaia* archive:\n", "\n", @@ -303,7 +391,9 @@ { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "id": "AE2xkDyxpNSH" + }, "outputs": [], "source": [ "hd219829_coord = SkyCoord(\n", @@ -319,7 +409,9 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "id": "ioOAqGh2pNSI" + }, "source": [ "We now have a `SkyCoord` representation of the position and proper motion of the star HD 219829 as measured by *Gaia* and reported at the epoch J2015.5. What does this mean exactly? *Gaia* actually measures the (time-dependent) position of a star every time it scans the part of the sky that contains the source, and this is how *Gaia* is able to measure proper motions of stars. However, if every star is moving and changing its sky positions, how do we ever talk about \"the sky position\" of a star as opposed to \"the sky trajectory of a star\"?! The key is that catalogs often only report the position of a source at some reference epoch. For a survey that only observes the sky once or a few times (e.g., SDSS or 2MASS), this reference epoch might be \"the time that the star was observed.\" But for a mission like *Gaia*, which scans the sky many times, they perform astrometric fits to the individual position measurements, which allow them to measure the parallax, proper motion, and the reference position at a reference time for each source. For *Gaia* data release 2, the reference time is J2015.5, and the sky positions (and other quantities) reported in the catalog for each source are at this epoch. \n", "\n", @@ -329,7 +421,9 @@ { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "id": "A3T_h79VpNSI" + }, "outputs": [], "source": [ "# Skip this cell if you are not connected to the internet\n", @@ -342,7 +436,9 @@ { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "id": "CXbrurRipNSI" + }, "outputs": [], "source": [ "dss_cutout_filename = 'dss_hd219829.fits'" @@ -350,15 +446,21 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "id": "TiRZicmlpNSI" + }, "source": [ - "We can now load the FITS image of the cutout and use `astropy.visualization` to display the image using its World Coordinate System (WCS) info ([docs](http://docs.astropy.org/en/latest/visualization/wcsaxes/index.html)). By passing in the WCS information (included in the FITS cutout header), we can over-plot a marker for the *Gaia*-measured sky position of HD 219829:" + "We can now load the FITS image of the cutout and use `astropy.visualization` to display the image using its World Coordinate System (WCS) info ([docs](http://docs.astropy.org/en/latest/visualization/wcsaxes/index.html)). By passing in the WCS information (included in the FITS cutout header), we can over-plot a marker for the *Gaia*-measured sky position of HD 219829:\n", + "\n", + "(If you are unfamiliar with the usage of FITS header and FITS image, check out these 2 tutorials having detailed explanation on these 2 topics: [FITS-Header](https://learn.astropy.org/tutorials/FITS-header.html) and [FITS-Image](https://learn.astropy.org/tutorials/FITS-images.html).)" ] }, { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "id": "x0dVL_sEpNSJ" + }, "outputs": [], "source": [ "hdu = fits.open(dss_cutout_filename)[0]\n", @@ -380,7 +482,9 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "id": "UrTg6qBRpNSJ" + }, "source": [ "The brighest star (as observed by DSS) in this image is our target, and the red circle is where *Gaia* observed this star. As we excpected, it has moved quite a bit since the 1950's! We can account for this motion and predict the position of the star at around the time the DSS plate was observed. Let's assume that this plate was observed in 1950 exactly (this is not strictly correct, but should get us close enough).\n", "\n", @@ -390,7 +494,9 @@ { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "id": "AiUtgluXpNSJ" + }, "outputs": [], "source": [ "hd219829_coord.obstime" @@ -398,7 +504,9 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "id": "CPyFWNZvpNSK" + }, "source": [ "We can now use `apply_space_motion()` by passing in a new time, `new_obstime`, to compute the coordinates at:" ] @@ -406,7 +514,9 @@ { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "id": "MoRt0KbxpNSK" + }, "outputs": [], "source": [ "# this produces some warnings that we can safely ignore\n", @@ -419,7 +529,9 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "id": "uf4MK0zupNSK" + }, "source": [ "Let's now plot our predicted position for this source as it would appear in 1950 based on the *Gaia* position and proper motion:" ] @@ -427,7 +539,9 @@ { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "id": "npJ2V4BqpNSK" + }, "outputs": [], "source": [ "fig, ax = plt.subplots(1, 1, figsize=(8, 8), \n", @@ -453,30 +567,73 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "id": "JOn_o0VQpNSL" + }, "source": [ "The red circle is the same as in the previous image and shows the position of the source in the *Gaia* catalog (in 2015.5). The blue circle shows our prediction for the position of the source in 1950 - this looks much closer to where the star is in the DSS image!" ] }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "id": "uVuz1hvOpNSL" + }, "source": [ "In this tutorial, we have introduced how to store and transform velocity data along with positional data in `astropy.coordinates`. We also demonstrated how to use the velocity of a source to predict its position at an earlier or later time. " ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "joBu6r_nlfTp" + }, + "source": [ + "## Exercises\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "tZK2ibb0kKuo" + }, + "source": [ + "Lalande 21185 is the brightest red dwarf star in the northern hemisphere and has a pretty high proper motion. Use the Gaia archive (https://gea.esac.esa.int/archive/) to find values and create a `SkyCoord` object for Lalande 21185. (Hint: earlier in the tutorial, we extracted information from a Gaia table and mentioned which Gaia column names match with our postition components)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "3Bz52TRvliZg" + }, + "outputs": [], + "source": [] } ], "metadata": { + "colab": { + "name": "3-Coordinates-Velocities.ipynb", + "provenance": [] + }, + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, "language_info": { "codemirror_mode": { - "name": "ipython" + "name": "ipython", + "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", - "nbconvert_exporter": "python" + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" } }, "nbformat": 4, - "nbformat_minor": 4 + "nbformat_minor": 1 } From d362c17b3d9d479fd7f72590791c623f75f1744d Mon Sep 17 00:00:00 2001 From: Saima <104094038+saimafsiddiqui@users.noreply.github.com> Date: Tue, 16 Aug 2022 21:05:24 +0000 Subject: [PATCH 3/5] Delete 3_Coordinates_Velocities.ipynb --- .../3_Coordinates_Velocities.ipynb | 639 ------------------ 1 file changed, 639 deletions(-) delete mode 100644 tutorials/astropy-coordinates/3_Coordinates_Velocities.ipynb diff --git a/tutorials/astropy-coordinates/3_Coordinates_Velocities.ipynb b/tutorials/astropy-coordinates/3_Coordinates_Velocities.ipynb deleted file mode 100644 index fc62874c..00000000 --- a/tutorials/astropy-coordinates/3_Coordinates_Velocities.ipynb +++ /dev/null @@ -1,639 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": { - "id": "K3gI522hpNR1" - }, - "source": [ - "# Astronomical Coordinates 3: Working with Velocity Data in astropy.coordinates\n", - "\n", - "## Authors\n", - "Adrian Price-Whelan, Saima Siddiqui, Luthien Liu, Zihao Chen\n", - "\n", - "## Learning Goals\n", - "* Introduce how to represent and transform velocity data in `SkyCoord` objects\n", - "* Demonstrate how to predict the position of a star at a time using its proper motion\n", - "\n", - "## Keywords\n", - "coordinates, OOP, astroquery, gaia\n", - "\n", - "\n", - "## Summary\n", - "\n", - "In the previous tutorial in this series, we showed how astronomical *positional* coordinates can be represented and transformed in Python using the `SkyCoord` object ([docs](https://docs.astropy.org/en/stable/coordinates/skycoord.html)). Many sources, especially stars (thanks to the Gaia mission), have measured velocities or measured components of their velocity (e.g., just proper motion, or just radial velocity).\n", - "\n", - "In this tutorial, we will explore how the `astropy.coordinates` package can be used to represent and transform astronomical coordinates that have associated velocity data. You may find it helpful to keep [the Astropy documentation for the coordinates package](http://docs.astropy.org/en/stable/coordinates/index.html) open alongside this tutorial for reference or additional reading. In the text below, you may also see some links that look like ([docs](http://docs.astropy.org/en/stable/coordinates/index.html)). These links will take you to parts of the documentation that are directly relevant to the cells from which they link. \n", - "\n", - "*Note: This is the 3rd tutorial in a series of tutorials about astropy.coordinates. If you are new to astropy.coordinates, you may want to start from the beginning or an earlier tutorial.*\n", - "- [Previous tutorial: Astronomical Coordinates 2 - Transforming Coordinate Systems and Representations¶](2-Coordinates-Transforms)\n", - "- [Next tutorial: Astronomical Coordinates 4 - Cross-matching Catalogs](4-Coordinates-Crossmatch)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "m3RngEEJpNR5" - }, - "source": [ - "## Imports\n", - "\n", - "We start by importing some general packages we will need below:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "id": "EI1u9VzIpNR6", - "outputId": "f7fdb1dc-4716-4449-bad0-73f6c588e94c" - }, - "outputs": [], - "source": [ - "import warnings\n", - "\n", - "import matplotlib.pyplot as plt\n", - "%matplotlib inline\n", - "import numpy as np\n", - "!pip install \"astroquery\"\n", - "\n", - "from astropy import units as u\n", - "from astropy.coordinates import SkyCoord, Distance, Galactic\n", - "import astropy.coordinates as coord\n", - "from astropy.io import fits\n", - "from astropy.table import QTable\n", - "from astropy.time import Time\n", - "from astropy.utils.data import download_file\n", - "from astropy.wcs import WCS\n", - "\n", - "from astroquery.gaia import Gaia" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "Rtvp07W2pNR9" - }, - "source": [ - "## More Than Sky Positions: Including Velocity Data in `SkyCoord`\n", - "\n", - "As we have seen in the previous tutorials, the `SkyCoord` object can be used to store scalars or arrays of positional coordinate information and supports transforming between different coordinate frames and representations. But `astropy.coordinates` also supports representing and transforming *velocity* information along with positions ([docs](http://docs.astropy.org/en/latest/coordinates/velocities.html)).\n", - "\n", - "## Passing Velocity Data into `SkyCoord`\n", - "\n", - "Velocity components are passed in to `SkyCoord` in the same way that positional components are specified: As arguments to the `SkyCoord` class. For example, to create a `SkyCoord` to represent a sky position and a proper motion in the (default) ICRS coordinate frame, in addition to the position components `ra`, `dec`, we can pass in values for the proper motion components `pm_ra_cosdec`, `pm_dec` (\"pm\" for \"proper motion\"):" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "QOqwEMG3pNR9" - }, - "outputs": [], - "source": [ - "SkyCoord(\n", - " ra=10*u.deg, \n", - " dec=20*u.deg,\n", - " pm_ra_cosdec=1*u.mas/u.yr,\n", - " pm_dec=2*u.mas/u.yr)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "v6EeE4KsfSpq" - }, - "source": [ - "**Thought Questions:**\n", - "What value did we put in for our right ascension? What about our declination? What units are they in?" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "1U0966fepNR-" - }, - "source": [ - "Here, you may notice that the proper motion in right ascension has \"cosdec\" in the name: This is to explicitly note that the input here is expected to be the proper motion scaled by the cosine of the declination, which accounts for the fact that a change in longitude (right ascension) has different physical length at different latitudes (declinations).\n", - "\n", - "Like the examples in previous tutorials demonstrated for positional coordinates, we can also create an array-valued `SkyCoord` object by passing in arrays of data for all of the components. In this case, each value in the inputed array represents a quantity of an object among large data set. This method would be beneficial when dealing with large number of star collections like a star cluster:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "GnQ8m4AGpNR-" - }, - "outputs": [], - "source": [ - "SkyCoord(\n", - " ra=np.linspace(0, 10, 5)*u.deg, \n", - " dec=np.linspace(5, 20, 5)*u.deg,\n", - " pm_ra_cosdec=np.linspace(-5, 5, 5)*u.mas/u.yr,\n", - " pm_dec=np.linspace(-5, 5, 5)*u.mas/u.yr)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "x8fDaEI9pNR_" - }, - "source": [ - "However, for some of the examples below we will continue to use scalar values for brevity. \n", - "\n", - "You can also specify radial velocity data with the `radial_velocity` argument:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "C-jgFsdfpNSA" - }, - "outputs": [], - "source": [ - "velocity_coord = SkyCoord(\n", - " ra=10*u.deg, \n", - " dec=20*u.deg,\n", - " pm_ra_cosdec=1*u.mas/u.yr,\n", - " pm_dec=2*u.mas/u.yr,\n", - " radial_velocity=100*u.km/u.s)\n", - "velocity_coord" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "v7ztcRr-pNSB" - }, - "source": [ - "The component data can then be accessed using the same names used to pass in the velocity components:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "p3_7hP6LpNSB" - }, - "outputs": [], - "source": [ - "velocity_coord.pm_ra_cosdec" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "CM1TApDYpNSB" - }, - "outputs": [], - "source": [ - "velocity_coord.radial_velocity" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "gxGKcYFZpNSC" - }, - "source": [ - "A `SkyCoord` object with velocity data can be transformed to other frames just like the position-only coordinate objects we used in the previous tutorials:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "q6S7HmybpNSC" - }, - "outputs": [], - "source": [ - "velocity_coord_gal = velocity_coord.transform_to(Galactic())\n", - "velocity_coord_gal" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "K9wL2Rl7pNSD" - }, - "source": [ - "Note that, like the position components, which change from `ra`,`dec` to `l`,`b`, the proper motion component names have changed to reflect naming conventions for the component names in a given frame: `pm_ra_cosdec` and `pm_dec` have become `pm_l_cosb` and `pm_b`:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "g-35XIlnpNSD" - }, - "outputs": [], - "source": [ - "velocity_coord_gal.pm_l_cosb" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "PRZ_pt38pNSD" - }, - "outputs": [], - "source": [ - "velocity_coord_gal.pm_b" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "GthFqig5pNSE" - }, - "source": [ - "An important caveat to note when transforming a `SkyCoord` object with velocity data is that some reference frames require knowing the distances, or the full velocity vectors (i.e. proper motion components and radial velocity) in order to transform the velocities correctly. For example, a `SkyCoord` with only sky position and proper motion data **CANNOT** be transformed to a frame with a positional or velocity offset, such as the `Galactocentric` frame ([docs](https://docs.astropy.org/en/stable/coordinates/galactocentric.html))" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "SKKyiObSpNSF", - "tags": [ - "raises-exception" - ] - }, - "outputs": [], - "source": [ - "# This cell will NOT work (you will receive an ConvertError warning) - this is expected!\n", - "\n", - "test_coord_1 = SkyCoord(\n", - " ra=10*u.deg, \n", - " dec=20*u.deg,\n", - " pm_ra_cosdec=1*u.mas/u.yr,\n", - " pm_dec=2*u.mas/u.yr)\n", - "\n", - "test_coord_1.transform_to(coord.Galactocentric())" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "LJv8-Vt-1DYh" - }, - "source": [ - "In order to transform to the `Galactocentric` frame, both distance and radial velocity of the object are required." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "gdjBvnIM3D1U" - }, - "outputs": [], - "source": [ - "test_coord_2 = SkyCoord(\n", - " ra=10*u.deg, \n", - " dec=20*u.deg,\n", - " distance=10*u.pc,\n", - " pm_ra_cosdec=1*u.mas/u.yr,\n", - " pm_dec=2*u.mas/u.yr ,\n", - " radial_velocity=100*u.km/u.s)\n", - "\n", - "test_coord_2.transform_to(coord.Galactocentric())" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "rvo8IV2YpNSF" - }, - "source": [ - "## Evolving Coordinate Positions Between Epochs\n", - "\n", - "For nearby or fast-moving stars, a star's position could change appreciably between two well-spaced observations of the source. For such cases, it might be necessary to compute the position of the star at a given time using the proper motion or velocity of the star. Let's demonstrate this idea by comparing the sky position of a source as measured by [*Gaia* Data Release 2](https://www.cosmos.esa.int/web/gaia/dr2) (given at the epoch J2015.5) to an image near this source from the Digitized Sky Survey (DSS; digital scans of photographic plates observed in the 1950s). \n", - "\n", - "From previous astrometric measurements, we know that the star HD 219829 has very large proper motion: Close to 0.5 arcsec/year! Between the DSS and *Gaia*, we therefore expect that the position of the star has changed by about 0.5 arcmin. Let's see if this is the case! \n", - "\n", - "To start, we will query the *Gaia* catalog to retrieve data for this star (skip the cell below if you do not have an internet connection - we have provided the table locally as well). We use a large search radius so many sources will be returned" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "oV1PLYwkpNSF" - }, - "outputs": [], - "source": [ - "# Skip this cell if you are not connected to the internet\n", - "gaia_tbl = Gaia.query_object(SkyCoord.from_name('HD 219829'), \n", - " radius=1*u.arcmin)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/", - "height": 358 - }, - "id": "O-ntoKvEpNSG", - "outputId": "3a437357-ac0d-4c93-bc03-b45a87def9e8" - }, - "outputs": [], - "source": [ - "# the .read() below produces some warnings that we can safely ignore\n", - "with warnings.catch_warnings(): \n", - " warnings.simplefilter('ignore', UserWarning)\n", - " \n", - " gaia_tbl = QTable.read('HD_219829_query_results.ecsv')" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "jiy-_1nhpNSG" - }, - "source": [ - "We know that HD 219829 will be the brightest source in this small region, so we can extract the row with the smallest G-band magnitude. Let's check the proper motion values for this source to make sure that they are large:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "nPYC_wKzpNSH" - }, - "outputs": [], - "source": [ - "hd219829_row = gaia_tbl[gaia_tbl['phot_g_mean_mag'].argmin()]\n", - "hd219829_row['source_id', 'pmra', 'pmdec']" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "zyhOiroApNSH" - }, - "source": [ - "Indeed, it looks like this is our source! Let's construct a `SkyCoord` object for this source using the data from the *Gaia* archive:\n", - "\n", - "*Note about the Gaia catalog proper motion column names: The names in the Gaia archive and other repositories containing Gaia data give Right Ascension proper motion values simply as \"pmra\". These components implicitly contain the `cos(dec)` term, so we do **not** have to modify these values in order to pass them in to `SkyCoord` as `pm_ra_cosdec`*" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "AE2xkDyxpNSH" - }, - "outputs": [], - "source": [ - "hd219829_coord = SkyCoord(\n", - " ra=hd219829_row['ra'], \n", - " dec=hd219829_row['dec'],\n", - " distance=Distance(parallax=hd219829_row['parallax']),\n", - " pm_ra_cosdec=hd219829_row['pmra'],\n", - " pm_dec=hd219829_row['pmdec'],\n", - " obstime=Time(hd219829_row['ref_epoch'], format='jyear'))\n", - "\n", - "hd219829_coord" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "ioOAqGh2pNSI" - }, - "source": [ - "We now have a `SkyCoord` representation of the position and proper motion of the star HD 219829 as measured by *Gaia* and reported at the epoch J2015.5. What does this mean exactly? *Gaia* actually measures the (time-dependent) position of a star every time it scans the part of the sky that contains the source, and this is how *Gaia* is able to measure proper motions of stars. However, if every star is moving and changing its sky positions, how do we ever talk about \"the sky position\" of a star as opposed to \"the sky trajectory of a star\"?! The key is that catalogs often only report the position of a source at some reference epoch. For a survey that only observes the sky once or a few times (e.g., SDSS or 2MASS), this reference epoch might be \"the time that the star was observed.\" But for a mission like *Gaia*, which scans the sky many times, they perform astrometric fits to the individual position measurements, which allow them to measure the parallax, proper motion, and the reference position at a reference time for each source. For *Gaia* data release 2, the reference time is J2015.5, and the sky positions (and other quantities) reported in the catalog for each source are at this epoch. \n", - "\n", - "In `SkyCoord`, we specify the \"epoch\" of an observation using the `obstime` argument, as we did above. Now that we have a coordinate object for HD 219829, let's now compare the position of the star as measured by *Gaia* to its apparent position in an image from the DSS. Let's now query the DSS to retrieve a FITS image of the field around this star, using the STSCI DSS image cutout service. Skip the cell below if you do not have an internet connection (we have provided the image locally as well):" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "A3T_h79VpNSI" - }, - "outputs": [], - "source": [ - "# Skip this cell if you are not connected to the internet\n", - "dss_cutout_filename = download_file(\n", - " f\"http://archive.stsci.edu/cgi-bin/dss_search?\"\n", - " f\"f=FITS&ra={hd219829_coord.ra.degree}&dec={hd219829_coord.dec.degree}\"\n", - " f\"&width=4&height=4\") # width/height in arcmin" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "CXbrurRipNSI" - }, - "outputs": [], - "source": [ - "dss_cutout_filename = 'dss_hd219829.fits'" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "TiRZicmlpNSI" - }, - "source": [ - "We can now load the FITS image of the cutout and use `astropy.visualization` to display the image using its World Coordinate System (WCS) info ([docs](http://docs.astropy.org/en/latest/visualization/wcsaxes/index.html)). By passing in the WCS information (included in the FITS cutout header), we can over-plot a marker for the *Gaia*-measured sky position of HD 219829:\n", - "\n", - "(If you are unfamiliar with the usage of FITS header and FITS image, check out these 2 tutorials having detailed explanation on these 2 topics: [FITS-Header](https://learn.astropy.org/tutorials/FITS-header.html) and [FITS-Image](https://learn.astropy.org/tutorials/FITS-images.html).)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "x0dVL_sEpNSJ" - }, - "outputs": [], - "source": [ - "hdu = fits.open(dss_cutout_filename)[0]\n", - "wcs = WCS(hdu.header)\n", - "\n", - "fig, ax = plt.subplots(1, 1, figsize=(8, 8), \n", - " subplot_kw=dict(projection=wcs))\n", - "ax.imshow(hdu.data, origin='lower', cmap='Greys_r')\n", - "ax.set_xlabel('RA')\n", - "ax.set_ylabel('Dec')\n", - "ax.set_autoscale_on(False)\n", - "\n", - "ax.scatter(hd219829_coord.ra.degree,\n", - " hd219829_coord.dec.degree,\n", - " s=500,\n", - " transform=ax.get_transform('world'),\n", - " facecolor='none', linewidth=2, color='tab:red')" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "UrTg6qBRpNSJ" - }, - "source": [ - "The brighest star (as observed by DSS) in this image is our target, and the red circle is where *Gaia* observed this star. As we excpected, it has moved quite a bit since the 1950's! We can account for this motion and predict the position of the star at around the time the DSS plate was observed. Let's assume that this plate was observed in 1950 exactly (this is not strictly correct, but should get us close enough).\n", - "\n", - "To account for the proper motion of the source and evolve the position to a new time, we can use the `SkyCoord.apply_space_motion()` method ([docs](http://docs.astropy.org/en/latest/api/astropy.coordinates.SkyCoord.html#astropy.coordinates.SkyCoord.apply_space_motion)). Because we defined the `obstime` when we defined the coordinate object for HD 219829, for example:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "AiUtgluXpNSJ" - }, - "outputs": [], - "source": [ - "hd219829_coord.obstime" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "CPyFWNZvpNSK" - }, - "source": [ - "We can now use `apply_space_motion()` by passing in a new time, `new_obstime`, to compute the coordinates at:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "MoRt0KbxpNSK" - }, - "outputs": [], - "source": [ - "# this produces some warnings that we can safely ignore\n", - "with warnings.catch_warnings(): \n", - " warnings.simplefilter('ignore', UserWarning)\n", - " \n", - " hd219829_coord_1950 = hd219829_coord.apply_space_motion(\n", - " new_obstime=Time('J1950'))" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "uf4MK0zupNSK" - }, - "source": [ - "Let's now plot our predicted position for this source as it would appear in 1950 based on the *Gaia* position and proper motion:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "npJ2V4BqpNSK" - }, - "outputs": [], - "source": [ - "fig, ax = plt.subplots(1, 1, figsize=(8, 8), \n", - " subplot_kw=dict(projection=wcs))\n", - "ax.imshow(hdu.data, origin='lower', cmap='Greys_r')\n", - "ax.set_xlabel('RA')\n", - "ax.set_ylabel('Dec')\n", - "ax.set_autoscale_on(False)\n", - "\n", - "ax.scatter(hd219829_coord.ra.degree,\n", - " hd219829_coord.dec.degree,\n", - " s=500,\n", - " transform=ax.get_transform('world'),\n", - " facecolor='none', linewidth=2, color='tab:red')\n", - "\n", - "# Plot the predicted (past) position:\n", - "ax.scatter(hd219829_coord_1950.ra.degree,\n", - " hd219829_coord_1950.dec.degree,\n", - " s=500,\n", - " transform=ax.get_transform('world'),\n", - " facecolor='none', linewidth=2, color='tab:blue')" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "JOn_o0VQpNSL" - }, - "source": [ - "The red circle is the same as in the previous image and shows the position of the source in the *Gaia* catalog (in 2015.5). The blue circle shows our prediction for the position of the source in 1950 - this looks much closer to where the star is in the DSS image!" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "uVuz1hvOpNSL" - }, - "source": [ - "In this tutorial, we have introduced how to store and transform velocity data along with positional data in `astropy.coordinates`. We also demonstrated how to use the velocity of a source to predict its position at an earlier or later time. " - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "joBu6r_nlfTp" - }, - "source": [ - "## Exercises\n" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "tZK2ibb0kKuo" - }, - "source": [ - "Lalande 21185 is the brightest red dwarf star in the northern hemisphere and has a pretty high proper motion. Use the Gaia archive (https://gea.esac.esa.int/archive/) to find values and create a `SkyCoord` object for Lalande 21185. (Hint: earlier in the tutorial, we extracted information from a Gaia table and mentioned which Gaia column names match with our postition components)." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "3Bz52TRvliZg" - }, - "outputs": [], - "source": [] - } - ], - "metadata": { - "colab": { - "name": "3-Coordinates-Velocities.ipynb", - "provenance": [] - }, - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.12" - } - }, - "nbformat": 4, - "nbformat_minor": 1 -} From 70f53d063ae9ef586252cfa37e8bef3a0a827c99 Mon Sep 17 00:00:00 2001 From: Adrian Price-Whelan <583379+adrn@users.noreply.github.com> Date: Fri, 21 Feb 2025 15:23:36 -0500 Subject: [PATCH 4/5] minor tweaks --- .../3-Coordinates-Velocities.ipynb | 45 ++++++------------- 1 file changed, 14 insertions(+), 31 deletions(-) diff --git a/tutorials/astropy-coordinates/3-Coordinates-Velocities.ipynb b/tutorials/astropy-coordinates/3-Coordinates-Velocities.ipynb index fed617fb..076d9543 100644 --- a/tutorials/astropy-coordinates/3-Coordinates-Velocities.ipynb +++ b/tutorials/astropy-coordinates/3-Coordinates-Velocities.ipynb @@ -58,7 +58,6 @@ "import matplotlib.pyplot as plt\n", "%matplotlib inline\n", "import numpy as np\n", - "!pip install \"astroquery\"\n", "\n", "from astropy import units as u\n", "from astropy.coordinates import SkyCoord, Distance, Galactic\n", @@ -96,22 +95,12 @@ "outputs": [], "source": [ "SkyCoord(\n", - " ra=10*u.deg, \n", + " ra=10*u.deg,\n", " dec=20*u.deg,\n", " pm_ra_cosdec=1*u.mas/u.yr,\n", " pm_dec=2*u.mas/u.yr)" ] }, - { - "cell_type": "markdown", - "metadata": { - "id": "v6EeE4KsfSpq" - }, - "source": [ - "**Thought Questions:**\n", - "What value did we put in for our right ascension? What about our declination? What units are they in?" - ] - }, { "cell_type": "markdown", "metadata": { @@ -132,7 +121,7 @@ "outputs": [], "source": [ "SkyCoord(\n", - " ra=np.linspace(0, 10, 5)*u.deg, \n", + " ra=np.linspace(0, 10, 5)*u.deg,\n", " dec=np.linspace(5, 20, 5)*u.deg,\n", " pm_ra_cosdec=np.linspace(-5, 5, 5)*u.mas/u.yr,\n", " pm_dec=np.linspace(-5, 5, 5)*u.mas/u.yr)" @@ -158,7 +147,7 @@ "outputs": [], "source": [ "velocity_coord = SkyCoord(\n", - " ra=10*u.deg, \n", + " ra=10*u.deg,\n", " dec=20*u.deg,\n", " pm_ra_cosdec=1*u.mas/u.yr,\n", " pm_dec=2*u.mas/u.yr,\n", @@ -272,7 +261,7 @@ "# This cell will NOT work (you will receive an ConvertError warning) - this is expected!\n", "\n", "test_coord_1 = SkyCoord(\n", - " ra=10*u.deg, \n", + " ra=10*u.deg,\n", " dec=20*u.deg,\n", " pm_ra_cosdec=1*u.mas/u.yr,\n", " pm_dec=2*u.mas/u.yr)\n", @@ -298,7 +287,7 @@ "outputs": [], "source": [ "test_coord_2 = SkyCoord(\n", - " ra=10*u.deg, \n", + " ra=10*u.deg,\n", " dec=20*u.deg,\n", " distance=10*u.pc,\n", " pm_ra_cosdec=1*u.mas/u.yr,\n", @@ -332,7 +321,7 @@ "outputs": [], "source": [ "# Skip this cell if you are not connected to the internet\n", - "gaia_tbl = Gaia.query_object(SkyCoord.from_name('HD 219829'), \n", + "gaia_tbl = Gaia.query_object(SkyCoord.from_name('HD 219829'),\n", " radius=1*u.arcmin)" ] }, @@ -350,9 +339,9 @@ "outputs": [], "source": [ "# the .read() below produces some warnings that we can safely ignore\n", - "with warnings.catch_warnings(): \n", + "with warnings.catch_warnings():\n", " warnings.simplefilter('ignore', UserWarning)\n", - " \n", + "\n", " gaia_tbl = QTable.read('HD_219829_query_results.ecsv')" ] }, @@ -397,7 +386,7 @@ "outputs": [], "source": [ "hd219829_coord = SkyCoord(\n", - " ra=hd219829_row['ra'], \n", + " ra=hd219829_row['ra'],\n", " dec=hd219829_row['dec'],\n", " distance=Distance(parallax=hd219829_row['parallax']),\n", " pm_ra_cosdec=hd219829_row['pmra'],\n", @@ -466,7 +455,7 @@ "hdu = fits.open(dss_cutout_filename)[0]\n", "wcs = WCS(hdu.header)\n", "\n", - "fig, ax = plt.subplots(1, 1, figsize=(8, 8), \n", + "fig, ax = plt.subplots(1, 1, figsize=(8, 8),\n", " subplot_kw=dict(projection=wcs))\n", "ax.imshow(hdu.data, origin='lower', cmap='Greys_r')\n", "ax.set_xlabel('RA')\n", @@ -520,9 +509,9 @@ "outputs": [], "source": [ "# this produces some warnings that we can safely ignore\n", - "with warnings.catch_warnings(): \n", + "with warnings.catch_warnings():\n", " warnings.simplefilter('ignore', UserWarning)\n", - " \n", + "\n", " hd219829_coord_1950 = hd219829_coord.apply_space_motion(\n", " new_obstime=Time('J1950'))" ] @@ -544,7 +533,7 @@ }, "outputs": [], "source": [ - "fig, ax = plt.subplots(1, 1, figsize=(8, 8), \n", + "fig, ax = plt.subplots(1, 1, figsize=(8, 8),\n", " subplot_kw=dict(projection=wcs))\n", "ax.imshow(hdu.data, origin='lower', cmap='Greys_r')\n", "ax.set_xlabel('RA')\n", @@ -616,11 +605,6 @@ "name": "3-Coordinates-Velocities.ipynb", "provenance": [] }, - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, "language_info": { "codemirror_mode": { "name": "ipython", @@ -630,8 +614,7 @@ "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.12" + "pygments_lexer": "ipython3" } }, "nbformat": 4, From e25c745005cf155a2004805346c435670ad0eedf Mon Sep 17 00:00:00 2001 From: Adrian Price-Whelan <583379+adrn@users.noreply.github.com> Date: Fri, 21 Feb 2025 15:26:58 -0500 Subject: [PATCH 5/5] fix rebase issue --- .../3-Coordinates-Velocities.ipynb | 202 +++++------------- 1 file changed, 55 insertions(+), 147 deletions(-) diff --git a/tutorials/astropy-coordinates/3-Coordinates-Velocities.ipynb b/tutorials/astropy-coordinates/3-Coordinates-Velocities.ipynb index 076d9543..c47ab6e9 100644 --- a/tutorials/astropy-coordinates/3-Coordinates-Velocities.ipynb +++ b/tutorials/astropy-coordinates/3-Coordinates-Velocities.ipynb @@ -2,9 +2,7 @@ "cells": [ { "cell_type": "markdown", - "metadata": { - "id": "K3gI522hpNR1" - }, + "metadata": {}, "source": [ "# Astronomical Coordinates 3: Working with Velocity Data in astropy.coordinates\n", "\n", @@ -30,11 +28,19 @@ "- [Next tutorial: Astronomical Coordinates 4 - Cross-matching Catalogs](4-Coordinates-Crossmatch)" ] }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "with open('requirements.txt') as f:\n", + " print(f\"Required packages for this notebook:\\n{f.read()}\")" + ] + }, { "cell_type": "markdown", - "metadata": { - "id": "m3RngEEJpNR5" - }, + "metadata": {}, "source": [ "## Imports\n", "\n", @@ -44,13 +50,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "id": "EI1u9VzIpNR6", - "outputId": "f7fdb1dc-4716-4449-bad0-73f6c588e94c" - }, + "metadata": {}, "outputs": [], "source": [ "import warnings\n", @@ -73,9 +73,7 @@ }, { "cell_type": "markdown", - "metadata": { - "id": "Rtvp07W2pNR9" - }, + "metadata": {}, "source": [ "## More Than Sky Positions: Including Velocity Data in `SkyCoord`\n", "\n", @@ -89,9 +87,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "id": "QOqwEMG3pNR9" - }, + "metadata": {}, "outputs": [], "source": [ "SkyCoord(\n", @@ -103,9 +99,7 @@ }, { "cell_type": "markdown", - "metadata": { - "id": "1U0966fepNR-" - }, + "metadata": {}, "source": [ "Here, you may notice that the proper motion in right ascension has \"cosdec\" in the name: This is to explicitly note that the input here is expected to be the proper motion scaled by the cosine of the declination, which accounts for the fact that a change in longitude (right ascension) has different physical length at different latitudes (declinations).\n", "\n", @@ -115,9 +109,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "id": "GnQ8m4AGpNR-" - }, + "metadata": {}, "outputs": [], "source": [ "SkyCoord(\n", @@ -129,9 +121,7 @@ }, { "cell_type": "markdown", - "metadata": { - "id": "x8fDaEI9pNR_" - }, + "metadata": {}, "source": [ "However, for some of the examples below we will continue to use scalar values for brevity. \n", "\n", @@ -141,9 +131,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "id": "C-jgFsdfpNSA" - }, + "metadata": {}, "outputs": [], "source": [ "velocity_coord = SkyCoord(\n", @@ -157,9 +145,7 @@ }, { "cell_type": "markdown", - "metadata": { - "id": "v7ztcRr-pNSB" - }, + "metadata": {}, "source": [ "The component data can then be accessed using the same names used to pass in the velocity components:" ] @@ -167,9 +153,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "id": "p3_7hP6LpNSB" - }, + "metadata": {}, "outputs": [], "source": [ "velocity_coord.pm_ra_cosdec" @@ -178,9 +162,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "id": "CM1TApDYpNSB" - }, + "metadata": {}, "outputs": [], "source": [ "velocity_coord.radial_velocity" @@ -188,9 +170,7 @@ }, { "cell_type": "markdown", - "metadata": { - "id": "gxGKcYFZpNSC" - }, + "metadata": {}, "source": [ "A `SkyCoord` object with velocity data can be transformed to other frames just like the position-only coordinate objects we used in the previous tutorials:" ] @@ -198,9 +178,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "id": "q6S7HmybpNSC" - }, + "metadata": {}, "outputs": [], "source": [ "velocity_coord_gal = velocity_coord.transform_to(Galactic())\n", @@ -209,9 +187,7 @@ }, { "cell_type": "markdown", - "metadata": { - "id": "K9wL2Rl7pNSD" - }, + "metadata": {}, "source": [ "Note that, like the position components, which change from `ra`,`dec` to `l`,`b`, the proper motion component names have changed to reflect naming conventions for the component names in a given frame: `pm_ra_cosdec` and `pm_dec` have become `pm_l_cosb` and `pm_b`:" ] @@ -219,9 +195,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "id": "g-35XIlnpNSD" - }, + "metadata": {}, "outputs": [], "source": [ "velocity_coord_gal.pm_l_cosb" @@ -230,9 +204,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "id": "PRZ_pt38pNSD" - }, + "metadata": {}, "outputs": [], "source": [ "velocity_coord_gal.pm_b" @@ -240,9 +212,7 @@ }, { "cell_type": "markdown", - "metadata": { - "id": "GthFqig5pNSE" - }, + "metadata": {}, "source": [ "An important caveat to note when transforming a `SkyCoord` object with velocity data is that some reference frames require knowing the distances, or the full velocity vectors (i.e. proper motion components and radial velocity) in order to transform the velocities correctly. For example, a `SkyCoord` with only sky position and proper motion data **CANNOT** be transformed to a frame with a positional or velocity offset, such as the `Galactocentric` frame ([docs](https://docs.astropy.org/en/stable/coordinates/galactocentric.html))" ] @@ -251,7 +221,6 @@ "cell_type": "code", "execution_count": null, "metadata": { - "id": "SKKyiObSpNSF", "tags": [ "raises-exception" ] @@ -271,9 +240,7 @@ }, { "cell_type": "markdown", - "metadata": { - "id": "LJv8-Vt-1DYh" - }, + "metadata": {}, "source": [ "In order to transform to the `Galactocentric` frame, both distance and radial velocity of the object are required." ] @@ -281,9 +248,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "id": "gdjBvnIM3D1U" - }, + "metadata": {}, "outputs": [], "source": [ "test_coord_2 = SkyCoord(\n", @@ -299,9 +264,7 @@ }, { "cell_type": "markdown", - "metadata": { - "id": "rvo8IV2YpNSF" - }, + "metadata": {}, "source": [ "## Evolving Coordinate Positions Between Epochs\n", "\n", @@ -315,9 +278,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "id": "oV1PLYwkpNSF" - }, + "metadata": {}, "outputs": [], "source": [ "# Skip this cell if you are not connected to the internet\n", @@ -328,14 +289,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/", - "height": 358 - }, - "id": "O-ntoKvEpNSG", - "outputId": "3a437357-ac0d-4c93-bc03-b45a87def9e8" - }, + "metadata": {}, "outputs": [], "source": [ "# the .read() below produces some warnings that we can safely ignore\n", @@ -347,9 +301,7 @@ }, { "cell_type": "markdown", - "metadata": { - "id": "jiy-_1nhpNSG" - }, + "metadata": {}, "source": [ "We know that HD 219829 will be the brightest source in this small region, so we can extract the row with the smallest G-band magnitude. Let's check the proper motion values for this source to make sure that they are large:" ] @@ -357,9 +309,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "id": "nPYC_wKzpNSH" - }, + "metadata": {}, "outputs": [], "source": [ "hd219829_row = gaia_tbl[gaia_tbl['phot_g_mean_mag'].argmin()]\n", @@ -368,9 +318,7 @@ }, { "cell_type": "markdown", - "metadata": { - "id": "zyhOiroApNSH" - }, + "metadata": {}, "source": [ "Indeed, it looks like this is our source! Let's construct a `SkyCoord` object for this source using the data from the *Gaia* archive:\n", "\n", @@ -380,9 +328,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "id": "AE2xkDyxpNSH" - }, + "metadata": {}, "outputs": [], "source": [ "hd219829_coord = SkyCoord(\n", @@ -398,9 +344,7 @@ }, { "cell_type": "markdown", - "metadata": { - "id": "ioOAqGh2pNSI" - }, + "metadata": {}, "source": [ "We now have a `SkyCoord` representation of the position and proper motion of the star HD 219829 as measured by *Gaia* and reported at the epoch J2015.5. What does this mean exactly? *Gaia* actually measures the (time-dependent) position of a star every time it scans the part of the sky that contains the source, and this is how *Gaia* is able to measure proper motions of stars. However, if every star is moving and changing its sky positions, how do we ever talk about \"the sky position\" of a star as opposed to \"the sky trajectory of a star\"?! The key is that catalogs often only report the position of a source at some reference epoch. For a survey that only observes the sky once or a few times (e.g., SDSS or 2MASS), this reference epoch might be \"the time that the star was observed.\" But for a mission like *Gaia*, which scans the sky many times, they perform astrometric fits to the individual position measurements, which allow them to measure the parallax, proper motion, and the reference position at a reference time for each source. For *Gaia* data release 2, the reference time is J2015.5, and the sky positions (and other quantities) reported in the catalog for each source are at this epoch. \n", "\n", @@ -410,9 +354,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "id": "A3T_h79VpNSI" - }, + "metadata": {}, "outputs": [], "source": [ "# Skip this cell if you are not connected to the internet\n", @@ -425,9 +367,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "id": "CXbrurRipNSI" - }, + "metadata": {}, "outputs": [], "source": [ "dss_cutout_filename = 'dss_hd219829.fits'" @@ -435,9 +375,7 @@ }, { "cell_type": "markdown", - "metadata": { - "id": "TiRZicmlpNSI" - }, + "metadata": {}, "source": [ "We can now load the FITS image of the cutout and use `astropy.visualization` to display the image using its World Coordinate System (WCS) info ([docs](http://docs.astropy.org/en/latest/visualization/wcsaxes/index.html)). By passing in the WCS information (included in the FITS cutout header), we can over-plot a marker for the *Gaia*-measured sky position of HD 219829:\n", "\n", @@ -447,9 +385,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "id": "x0dVL_sEpNSJ" - }, + "metadata": {}, "outputs": [], "source": [ "hdu = fits.open(dss_cutout_filename)[0]\n", @@ -471,9 +407,7 @@ }, { "cell_type": "markdown", - "metadata": { - "id": "UrTg6qBRpNSJ" - }, + "metadata": {}, "source": [ "The brighest star (as observed by DSS) in this image is our target, and the red circle is where *Gaia* observed this star. As we excpected, it has moved quite a bit since the 1950's! We can account for this motion and predict the position of the star at around the time the DSS plate was observed. Let's assume that this plate was observed in 1950 exactly (this is not strictly correct, but should get us close enough).\n", "\n", @@ -483,9 +417,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "id": "AiUtgluXpNSJ" - }, + "metadata": {}, "outputs": [], "source": [ "hd219829_coord.obstime" @@ -493,9 +425,7 @@ }, { "cell_type": "markdown", - "metadata": { - "id": "CPyFWNZvpNSK" - }, + "metadata": {}, "source": [ "We can now use `apply_space_motion()` by passing in a new time, `new_obstime`, to compute the coordinates at:" ] @@ -503,9 +433,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "id": "MoRt0KbxpNSK" - }, + "metadata": {}, "outputs": [], "source": [ "# this produces some warnings that we can safely ignore\n", @@ -518,9 +446,7 @@ }, { "cell_type": "markdown", - "metadata": { - "id": "uf4MK0zupNSK" - }, + "metadata": {}, "source": [ "Let's now plot our predicted position for this source as it would appear in 1950 based on the *Gaia* position and proper motion:" ] @@ -528,9 +454,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "id": "npJ2V4BqpNSK" - }, + "metadata": {}, "outputs": [], "source": [ "fig, ax = plt.subplots(1, 1, figsize=(8, 8),\n", @@ -556,36 +480,28 @@ }, { "cell_type": "markdown", - "metadata": { - "id": "JOn_o0VQpNSL" - }, + "metadata": {}, "source": [ "The red circle is the same as in the previous image and shows the position of the source in the *Gaia* catalog (in 2015.5). The blue circle shows our prediction for the position of the source in 1950 - this looks much closer to where the star is in the DSS image!" ] }, { "cell_type": "markdown", - "metadata": { - "id": "uVuz1hvOpNSL" - }, + "metadata": {}, "source": [ "In this tutorial, we have introduced how to store and transform velocity data along with positional data in `astropy.coordinates`. We also demonstrated how to use the velocity of a source to predict its position at an earlier or later time. " ] }, { "cell_type": "markdown", - "metadata": { - "id": "joBu6r_nlfTp" - }, + "metadata": {}, "source": [ "## Exercises\n" ] }, { "cell_type": "markdown", - "metadata": { - "id": "tZK2ibb0kKuo" - }, + "metadata": {}, "source": [ "Lalande 21185 is the brightest red dwarf star in the northern hemisphere and has a pretty high proper motion. Use the Gaia archive (https://gea.esac.esa.int/archive/) to find values and create a `SkyCoord` object for Lalande 21185. (Hint: earlier in the tutorial, we extracted information from a Gaia table and mentioned which Gaia column names match with our postition components)." ] @@ -593,28 +509,20 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "id": "3Bz52TRvliZg" - }, + "metadata": {}, "outputs": [], "source": [] } ], "metadata": { - "colab": { - "name": "3-Coordinates-Velocities.ipynb", - "provenance": [] - }, "language_info": { "codemirror_mode": { - "name": "ipython", - "version": 3 + "name": "ipython" }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3" + "nbconvert_exporter": "python" } }, "nbformat": 4,