mirror of
https://github.com/AllenDowney/AstronomicalData.git
synced 2025-12-05 20:40:17 -08:00
957 lines
25 KiB
Plaintext
957 lines
25 KiB
Plaintext
{
|
|
"cells": [
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"# 2. Coordinates and Units\n",
|
|
"\n",
|
|
"In the previous lesson, we wrote ADQL queries and used them to select and download data from the Gaia server.\n",
|
|
"\n",
|
|
"In this lesson, we'll pick up where we left off and write a query to select stars from a particular region of the sky."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Outline\n",
|
|
"\n",
|
|
"We'll start with an example that does a \"cone search\"; that is, it selects stars that appear in a circular region of the sky.\n",
|
|
"\n",
|
|
"Then, to select stars in the vicinity of GD-1, we'll:\n",
|
|
"\n",
|
|
"* Use `Quantity` objects to represent measurements with units.\n",
|
|
"\n",
|
|
"* Use Astropy to convert coordinates from one frame to another.\n",
|
|
"\n",
|
|
"* Use the ADQL keywords `POLYGON`, `CONTAINS`, and `POINT` to select stars that fall within a polygonal region.\n",
|
|
"\n",
|
|
"* Submit a query and download the results.\n",
|
|
"\n",
|
|
"* Store the results in a FITS file.\n",
|
|
"\n",
|
|
"After completing this lesson, you should be able to\n",
|
|
"\n",
|
|
"* Use Python string formatting to compose more complex ADQL queries.\n",
|
|
"\n",
|
|
"* Work with coordinates and other quantities that have units.\n",
|
|
"\n",
|
|
"* Download the results of a query and store them in a file."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {
|
|
"tags": []
|
|
},
|
|
"source": [
|
|
"## Installing libraries\n",
|
|
"\n",
|
|
"If you are running this notebook on Colab, you can run the following cell to install the libraries we'll use.\n",
|
|
"\n",
|
|
"If you are running this notebook on your own computer, you might have to install these libraries yourself. See the instructions in the preface."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 1,
|
|
"metadata": {
|
|
"tags": []
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"# If we're running on Colab, install libraries\n",
|
|
"\n",
|
|
"# TODO: When Colab can install gala, switch from astro-gala\n",
|
|
"\n",
|
|
"import sys\n",
|
|
"IN_COLAB = 'google.colab' in sys.modules\n",
|
|
"\n",
|
|
"if IN_COLAB:\n",
|
|
" !pip install astroquery astro-gala"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Working with Units\n",
|
|
"\n",
|
|
"The measurements we will work with are physical quantities, which means that they have two parts, a value and a unit.\n",
|
|
"For example, the coordinate $30^{\\circ}$ has value 30 and its units are degrees.\n",
|
|
"\n",
|
|
"Until recently, most scientific computation was done with values only; units were left out of the program altogether, [often with catastrophic results](https://en.wikipedia.org/wiki/Mars_Climate_Orbiter#Cause_of_failure).\n",
|
|
"\n",
|
|
"Astropy provides tools for including units explicitly in computations, which makes it possible to detect errors before they cause disasters.\n",
|
|
"\n",
|
|
"To use Astropy units, we import them like this:"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 2,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"import astropy.units as u"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"`u` is an object that contains most common units and all SI units.\n",
|
|
"\n",
|
|
"You can use `dir` to list them, but you should also [read the documentation](https://docs.astropy.org/en/stable/units/)."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 3,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"dir(u)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"To create a quantity, we multiply a value by a unit."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 4,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"angle = 10 * u.degree\n",
|
|
"type(angle)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"The result is a `Quantity` object.\n",
|
|
"Jupyter knows how to display `Quantities` like this:"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 5,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"angle"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"Quantities provide a method called `to` that converts to other units. For example, we can compute the number of arcminutes in `angle`:"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 6,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"angle_arcmin = angle.to(u.arcmin)\n",
|
|
"angle_arcmin"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"If you add quantities, Astropy converts them to compatible units, if possible:"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 7,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"angle + 30 * u.arcmin"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"If the units are not compatible, you get an error.\n",
|
|
"For example:\n",
|
|
"\n",
|
|
"```\n",
|
|
"angle + 5 * u.second\n",
|
|
"```\n",
|
|
"\n",
|
|
"causes a `UnitConversionError`."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"### Exercise\n",
|
|
"\n",
|
|
"Create a quantity that represents 5 [arcminutes](https://en.wikipedia.org/wiki/Minute_and_second_of_arc) and assign it to a variable called `radius`.\n",
|
|
"\n",
|
|
"Then convert it to degrees."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 8,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"# Solution goes here"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Selecting a Region\n",
|
|
"\n",
|
|
"One of the most common ways to restrict a query is to select stars in a particular region of the sky.\n",
|
|
"For example, here's a query from the [Gaia archive documentation](https://gea.esac.esa.int/archive-help/adql/examples/index.html) that selects objects in a circular region centered at (88.8, 7.4) with a search radius of 5 arcmin (0.08333 deg)."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 9,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"query_cone = \"\"\"SELECT \n",
|
|
"TOP 10 \n",
|
|
"source_id\n",
|
|
"FROM gaiadr2.gaia_source\n",
|
|
"WHERE 1=CONTAINS(\n",
|
|
" POINT(ra, dec),\n",
|
|
" CIRCLE(88.8, 7.4, 0.08333333))\n",
|
|
"\"\"\""
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"This query uses three keywords that are specific to ADQL (not SQL):\n",
|
|
"\n",
|
|
"* `POINT`: a location in [ICRS coordinates](https://en.wikipedia.org/wiki/International_Celestial_Reference_System), specified in degrees of right ascension and declination.\n",
|
|
"\n",
|
|
"* `CIRCLE`: a circle where the first two values are the coordinates of the center and the third is the radius in degrees.\n",
|
|
"\n",
|
|
"* `CONTAINS`: a function that returns `1` if a `POINT` is contained in a shape and `0` otherwise.\n",
|
|
"\n",
|
|
"Here is the [documentation of `CONTAINS`](http://www.ivoa.net/documents/ADQL/20180112/PR-ADQL-2.1-20180112.html#tth_sEc4.2.12).\n",
|
|
"\n",
|
|
"A query like this is called a cone search because it selects stars in a cone.\n",
|
|
"Here's how we run it."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 10,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"from astroquery.gaia import Gaia\n",
|
|
"\n",
|
|
"job = Gaia.launch_job(query_cone)\n",
|
|
"job"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 11,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"results = job.get_results()\n",
|
|
"results"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"### Exercise\n",
|
|
"\n",
|
|
"When you are debugging queries like this, you can use `TOP` to limit the size of the results, but then you still don't know how big the results will be.\n",
|
|
"\n",
|
|
"An alternative is to use `COUNT`, which asks for the number of rows that would be selected, but it does not return them.\n",
|
|
"\n",
|
|
"In the previous query, replace `TOP 10 source_id` with `COUNT(source_id)` and run the query again. How many stars has Gaia identified in the cone we searched?"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 12,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"# Solution goes here"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Getting GD-1 Data\n",
|
|
"\n",
|
|
"From the Price-Whelan and Bonaca paper, we will try to reproduce Figure 1, which includes this representation of stars likely to belong to GD-1:\n",
|
|
"\n",
|
|
"<img src=\"https://github.com/datacarpentry/astronomy-python/raw/gh-pages/fig/gd1-4.png\">"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"The axes of this figure are defined so the x-axis is aligned with the stars in GD-1, and the y-axis is perpendicular.\n",
|
|
"\n",
|
|
"* Along the x-axis ($\\phi_1$) the figure extends from -100 to 20 degrees.\n",
|
|
"\n",
|
|
"* Along the y-axis ($\\phi_2$) the figure extends from about -8 to 4 degrees.\n",
|
|
"\n",
|
|
"Ideally, we would select all stars from this rectangle, but there are more than 10 million of them, so\n",
|
|
"\n",
|
|
"* That would be difficult to work with,\n",
|
|
"\n",
|
|
"* As anonymous Gaia users, we are limited to 3 million rows in a single query, and\n",
|
|
"\n",
|
|
"* While we are developing and testing code, it will be faster to work with a smaller dataset.\n",
|
|
"\n",
|
|
"So we'll start by selecting stars in a smaller rectangle near the center of GD-1, from -55 to -45 degrees $\\phi_1$ and -8 to 4 degrees $\\phi_2$.\n",
|
|
"\n",
|
|
"But first we let's see how to represent these coordinates with Astropy."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Transforming coordinates\n",
|
|
"\n",
|
|
"Astropy provides a `SkyCoord` object that represents sky coordinates relative to a specified frame.\n",
|
|
"\n",
|
|
"The following example creates a `SkyCoord` object that represents the approximate coordinates of [Betelgeuse](http://simbad.u-strasbg.fr/simbad/sim-basic?Ident=Betelgeuse) (alf Ori) in the ICRS frame.\n",
|
|
"\n",
|
|
"[ICRS](https://www.iers.org/IERS/EN/Science/ICRS/ICRS.html) is the\n",
|
|
"\"International Celestial Reference System\", adopted in 1997 by the International Astronomical Union."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 13,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"from astropy.coordinates import SkyCoord\n",
|
|
"\n",
|
|
"ra = 88.8 * u.degree\n",
|
|
"dec = 7.4 * u.degree\n",
|
|
"coord_icrs = SkyCoord(ra=ra, dec=dec, frame='icrs')\n",
|
|
"\n",
|
|
"coord_icrs"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"`SkyCoord` provides a function that transforms to other frames.\n",
|
|
"For example, we can transform `coords_icrs` to Galactic coordinates like this:"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 14,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"coord_galactic = coord_icrs.transform_to('galactic')\n",
|
|
"coord_galactic"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"Notice that in the Galactic frame, the coordinates are called `l` and `b`, not `ra` and `dec`.\n",
|
|
"\n",
|
|
"To transform to and from GD-1 coordinates, we'll use a frame defined by [Gala](https://gala-astro.readthedocs.io/en/latest/), which is an Astropy-affiliated library that provides tools for galactic dynamics.\n",
|
|
"\n",
|
|
"Gala provides [`GD1Koposov10`](https://gala-astro.readthedocs.io/en/latest/_modules/gala/coordinates/gd1.html), which is \"a Heliocentric spherical coordinate system defined by the orbit of the GD-1 stream\"."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 17,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"from gala.coordinates import GD1Koposov10\n",
|
|
"\n",
|
|
"gd1_frame = GD1Koposov10()\n",
|
|
"gd1_frame"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"We can use it to find the coordinates of Betelgeuse in the GD-1 frame, like this:"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 18,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"coord_gd1 = coord_icrs.transform_to(gd1_frame)\n",
|
|
"coord_gd1"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"Notice that the coordinates are called `phi1` and `phi2`.\n",
|
|
"These are the coordinates shown in the figure from the paper, above."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"### Exercise\n",
|
|
"\n",
|
|
"Let's find the location of GD-1 in ICRS coordinates.\n",
|
|
"\n",
|
|
"1. Create a `SkyCoord` object at 0°, 0° in the GD-1 frame.\n",
|
|
"\n",
|
|
"2. Transform it to the ICRS frame.\n",
|
|
"\n",
|
|
"Hint: Because ICRS is built into Astropy, you can specify it by name, `icrs` (as we did with `galactic`). "
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 19,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"# Solution goes here"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"Notice that the origin of the GD-1 frame maps to `ra=200`, exactly, in ICRS. That's by design."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Selecting a rectangle\n",
|
|
"\n",
|
|
"Now we'll use these coordinate transformations to define a rectangle in the GD-1 frame and transform it to ICRS. \n",
|
|
"\n",
|
|
"The following variables define the boundaries of the rectangle in $\\phi_1$ and $\\phi_2$."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 20,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"phi1_min = -55 * u.degree\n",
|
|
"phi1_max = -45 * u.degree\n",
|
|
"phi2_min = -8 * u.degree\n",
|
|
"phi2_max = 4 * u.degree"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"To create a rectangle, we'll use the following function, which takes the lower and upper bounds as parameters."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 21,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"def make_rectangle(x1, x2, y1, y2):\n",
|
|
" \"\"\"Return the corners of a rectangle.\"\"\"\n",
|
|
" xs = [x1, x1, x2, x2, x1]\n",
|
|
" ys = [y1, y2, y2, y1, y1]\n",
|
|
" return xs, ys"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"The return value is a tuple containing a list of coordinates in `phi1` followed by a list of coordinates in `phi2`. "
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 22,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"phi1_rect, phi2_rect = make_rectangle(\n",
|
|
" phi1_min, phi1_max, phi2_min, phi2_max)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"`phi1_rect` and `phi2_rect` contains the coordinates of the corners of a rectangle in the GD-1 frame.\n",
|
|
"\n",
|
|
"In order to use them in a Gaia query, we have to convert them to ICRS. First we'll put them into a `SkyCoord` object."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 23,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"corners = SkyCoord(phi1=phi1_rect, phi2=phi2_rect, frame=gd1_frame)\n",
|
|
"corners"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"Now we can use `transform_to` to convert to ICRS coordinates."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 24,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"corners_icrs = corners.transform_to('icrs')\n",
|
|
"corners_icrs"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"Notice that a rectangle in one coordinate system is not necessarily a rectangle in another. In this example, the result is a (non-rectangular) polygon."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Defining a polygon\n",
|
|
"\n",
|
|
"In order to use this polygon as part of an ADQL query, we have to convert it to a string with a comma-separated list of coordinates, as in this example:\n",
|
|
"\n",
|
|
"```\n",
|
|
"\"\"\"\n",
|
|
"POLYGON(143.65, 20.98, \n",
|
|
" 134.46, 26.39, \n",
|
|
" 140.58, 34.85, \n",
|
|
" 150.16, 29.01)\n",
|
|
"\"\"\"\n",
|
|
"```\n",
|
|
"\n",
|
|
"`SkyCoord` provides `to_string`, which produces a list of strings."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 25,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"t = corners_icrs.to_string()\n",
|
|
"t"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"We can use the Python string function `join` to join `t` into a single string (with spaces between the pairs):"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 24,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"s = ' '.join(t)\n",
|
|
"s"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"That's almost what we need, but we have to replace the spaces with commas."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 25,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"s.replace(' ', ', ')"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"The following function combines these steps."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 26,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"def skycoord_to_string(skycoord):\n",
|
|
" \"\"\"Convert SkyCoord to string.\"\"\"\n",
|
|
" t = skycoord.to_string()\n",
|
|
" s = ' '.join(t)\n",
|
|
" return s.replace(' ', ', ')"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"Here's how we use it."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 27,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"point_list = skycoord_to_string(corners_icrs)\n",
|
|
"point_list"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Assembling the query\n",
|
|
"\n",
|
|
"Now we're ready to assemble the query. \n",
|
|
"We need `columns` again (as we saw in the previous lesson)."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 28,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"columns = 'source_id, ra, dec, pmra, pmdec, parallax'"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"And here's the query base we used in the previous lesson:"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 29,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"query3_base = \"\"\"SELECT \n",
|
|
"TOP 10 \n",
|
|
"{columns}\n",
|
|
"FROM gaiadr2.gaia_source\n",
|
|
"WHERE parallax < 1\n",
|
|
" AND bp_rp BETWEEN -0.75 AND 2\n",
|
|
"\"\"\""
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"Now we'll add a `WHERE` clause to select stars in the polygon we defined."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 30,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"query4_base = \"\"\"SELECT\n",
|
|
"TOP 10\n",
|
|
"{columns}\n",
|
|
"FROM gaiadr2.gaia_source\n",
|
|
"WHERE parallax < 1\n",
|
|
" AND bp_rp BETWEEN -0.75 AND 2 \n",
|
|
" AND 1 = CONTAINS(POINT(ra, dec), \n",
|
|
" POLYGON({point_list}))\n",
|
|
"\"\"\""
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"The query base contains format specifiers for `columns` and `point_list`.\n",
|
|
"\n",
|
|
"We'll use `format` to fill in these values."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 31,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"query4 = query4_base.format(columns=columns, \n",
|
|
" point_list=point_list)\n",
|
|
"print(query4)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"As always, we should take a minute to proof-read the query before we launch it."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 32,
|
|
"metadata": {
|
|
"scrolled": true
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"job = Gaia.launch_job_async(query4)\n",
|
|
"print(job)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"Here are the results."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 33,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"results = job.get_results()\n",
|
|
"results"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"Finally, we can remove `TOP 10` run the query again.\n",
|
|
"\n",
|
|
"The result is bigger than our previous queries, so it will take a little longer."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 34,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"query5_base = \"\"\"SELECT\n",
|
|
"{columns}\n",
|
|
"FROM gaiadr2.gaia_source\n",
|
|
"WHERE parallax < 1\n",
|
|
" AND bp_rp BETWEEN -0.75 AND 2 \n",
|
|
" AND 1 = CONTAINS(POINT(ra, dec), \n",
|
|
" POLYGON({point_list}))\n",
|
|
"\"\"\""
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 35,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"query5 = query5_base.format(columns=columns, \n",
|
|
" point_list=point_list)\n",
|
|
"print(query5)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 36,
|
|
"metadata": {
|
|
"scrolled": true
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"job = Gaia.launch_job_async(query5)\n",
|
|
"print(job)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 37,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"results = job.get_results()\n",
|
|
"len(results)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"There are more than 100,000 stars in this polygon, but that's a manageable size to work with."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Saving results\n",
|
|
"\n",
|
|
"This is the set of stars we'll work with in the next step. But since we have a substantial dataset now, this is a good time to save it.\n",
|
|
"\n",
|
|
"Storing the data in a file means we can shut down this notebook and pick up where we left off without running the previous query again.\n",
|
|
"\n",
|
|
"Astropy `Table` objects provide `write`, which writes the table to disk."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 38,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"filename = 'gd1_results.fits'\n",
|
|
"results.write(filename, overwrite=True)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"Because the filename ends with `fits`, the table is written in the [FITS format](https://en.wikipedia.org/wiki/FITS), which preserves the metadata associated with the table.\n",
|
|
"\n",
|
|
"If the file already exists, the `overwrite` argument causes it to be overwritten.\n",
|
|
"\n",
|
|
"We can use `getsize` to confirm that the file exists and check the size:"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 41,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"from os.path import getsize\n",
|
|
"\n",
|
|
"MB = 1024 * 1024\n",
|
|
"getsize(filename) / MB"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Summary\n",
|
|
"\n",
|
|
"In this notebook, we composed more complex queries to select stars within a polygonal region of the sky. Then we downloaded the results and saved them in a FITS file.\n",
|
|
"\n",
|
|
"In the next notebook, we'll reload the data from this file and replicate the next step in the analysis, using proper motion to identify stars likely to be in GD-1."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Best practices\n",
|
|
"\n",
|
|
"* For measurements with units, use `Quantity` objects that represent units explicitly and check for errors.\n",
|
|
"\n",
|
|
"* Use the `format` function to compose queries; code written this way is easier to read and less error-prone.\n",
|
|
"\n",
|
|
"* Develop queries incrementally: start with something simple, test it, and add a little bit at a time.\n",
|
|
"\n",
|
|
"* Once you have a query working, save the data in a local file. If you shut down the notebook and come back to it later, you can reload the file; you don't have to run the query again."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "raw",
|
|
"metadata": {},
|
|
"source": []
|
|
}
|
|
],
|
|
"metadata": {
|
|
"celltoolbar": "Tags",
|
|
"kernelspec": {
|
|
"display_name": "Python 3",
|
|
"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.8.8"
|
|
}
|
|
},
|
|
"nbformat": 4,
|
|
"nbformat_minor": 2
|
|
}
|