Skip to content

Commit 2720242

Browse files
committed
add python astropy notebook
edits clear outputs add source fix name typo
1 parent b1ca8f3 commit 2720242

File tree

2 files changed

+337
-1
lines changed

2 files changed

+337
-1
lines changed

requirements.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,4 +41,5 @@ nbclient
4141
torch
4242
SQLAlchemy
4343
psycopg2-binary
44-
ipykernel
44+
ipykernel
45+
astropy
Lines changed: 335 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,335 @@
1+
{
2+
"cells": [
3+
{
4+
"cell_type": "markdown",
5+
"id": "487b2dde",
6+
"metadata": {},
7+
"source": [
8+
"# Transforming positions and velocities to and from a Galactocentric frame"
9+
]
10+
},
11+
{
12+
"cell_type": "markdown",
13+
"id": "69e7f209",
14+
"metadata": {},
15+
"source": [
16+
"Source: https://docs.astropy.org/en/latest/coordinates/example_gallery_plot_galactocentric_frame.html"
17+
]
18+
},
19+
{
20+
"cell_type": "code",
21+
"execution_count": null,
22+
"id": "78b091ce",
23+
"metadata": {},
24+
"outputs": [],
25+
"source": [
26+
"def plot_positions_and_velocities(gc_rings):\n",
27+
" fig, axes = plt.subplots(1, 2, figsize=(12, 6), dpi=200)\n",
28+
" axes[0].plot(gc_rings.x.T, gc_rings.y.T, marker=\"None\", linewidth=3)\n",
29+
" axes[0].text(-8.0, 0, r\"$\\odot$\", fontsize=20)\n",
30+
" axes[0].set_xlim(-30, 30)\n",
31+
" axes[0].set_ylim(-30, 30)\n",
32+
" axes[0].set_xlabel(\"$x$ [kpc]\")\n",
33+
" axes[0].set_ylabel(\"$y$ [kpc]\")\n",
34+
" axes[0].set_title(\"Positions\")\n",
35+
" axes[1].plot(gc_rings.v_x.T, gc_rings.v_y.T, marker=\"None\", linewidth=3)\n",
36+
" axes[1].set_xlim(-250, 250)\n",
37+
" axes[1].set_ylim(-250, 250)\n",
38+
" axes[1].set_xlabel(f\"$v_x$ [{(u.km / u.s).to_string('latex_inline')}]\")\n",
39+
" axes[1].set_ylabel(f\"$v_y$ [{(u.km / u.s).to_string('latex_inline')}]\")\n",
40+
" axes[1].set_title(\"Velocities\")\n",
41+
" fig.tight_layout()"
42+
]
43+
},
44+
{
45+
"cell_type": "markdown",
46+
"id": "83278590",
47+
"metadata": {},
48+
"source": [
49+
"This example shows a few examples of how to use and customize the\n",
50+
"`Galactocentric` frame to transform Heliocentric sky\n",
51+
"positions, distance, proper motions, and radial velocities to a Galactocentric,\n",
52+
"Cartesian frame, and the same in reverse.\n",
53+
"\n",
54+
"The main configurable parameters of the `Galactocentric`\n",
55+
"frame control the position and velocity of the solar system barycenter within\n",
56+
"the Galaxy. These are specified by setting the ICRS coordinates of the\n",
57+
"Galactic center, the distance to the Galactic center (the sun-galactic center\n",
58+
"line is always assumed to be the x-axis of the Galactocentric frame), and the\n",
59+
"Cartesian 3-velocity of the sun in the Galactocentric frame. We will first\n",
60+
"demonstrate how to customize these values, then show how to set the solar motion\n",
61+
"instead by inputting the proper motion of Sgr A*.\n",
62+
"\n",
63+
"Note that, for brevity, we may refer to the solar system barycenter as just \"the\n",
64+
"sun\" in the examples below.\n",
65+
"\n",
66+
"Let's first define a barycentric coordinate and velocity in the ICRS frame.\n",
67+
"We will use the data for the star HD 39881 from the\n",
68+
"[Simbad](https://simbad.unistra.fr/simbad/) database:"
69+
]
70+
},
71+
{
72+
"cell_type": "code",
73+
"execution_count": null,
74+
"id": "fabf0fa4",
75+
"metadata": {},
76+
"outputs": [],
77+
"source": [
78+
"import astropy.coordinates as coord\n",
79+
"from astropy import units as u\n",
80+
"c1 = coord.SkyCoord(\n",
81+
" ra=89.014303 * u.degree,\n",
82+
" dec=13.924912 * u.degree,\n",
83+
" distance=(37.59 * u.mas).to(u.pc, u.parallax()),\n",
84+
" pm_ra_cosdec=372.72 * (u.mas / u.yr),\n",
85+
" pm_dec=-483.69 * (u.mas / u.yr),\n",
86+
" radial_velocity=0.37 * (u.km / u.s),\n",
87+
" frame=\"icrs\",\n",
88+
")"
89+
]
90+
},
91+
{
92+
"cell_type": "markdown",
93+
"id": "58097f73",
94+
"metadata": {},
95+
"source": [
96+
"This is a high proper-motion star; suppose we'd like to transform its position\n",
97+
"and velocity to a Galactocentric frame to see if it has a large 3D velocity\n",
98+
"as well. To use the Astropy default solar position and motion parameters, we\n",
99+
"can do the following:"
100+
]
101+
},
102+
{
103+
"cell_type": "code",
104+
"execution_count": null,
105+
"id": "b2af242d",
106+
"metadata": {},
107+
"outputs": [],
108+
"source": [
109+
"gc1 = c1.transform_to(coord.Galactocentric)"
110+
]
111+
},
112+
{
113+
"cell_type": "markdown",
114+
"id": "e8ddeafe",
115+
"metadata": {},
116+
"source": [
117+
"From here, we can access the components of the resulting\n",
118+
"`Galactocentric` instance to see the 3D Cartesian\n",
119+
"velocity components:"
120+
]
121+
},
122+
{
123+
"cell_type": "code",
124+
"execution_count": null,
125+
"id": "dd0b6eba",
126+
"metadata": {},
127+
"outputs": [],
128+
"source": [
129+
"print(gc1.v_x, gc1.v_y, gc1.v_z)\n"
130+
]
131+
},
132+
{
133+
"cell_type": "markdown",
134+
"id": "7a2aae39",
135+
"metadata": {},
136+
"source": [
137+
"The default parameters for the `Galactocentric` frame\n",
138+
"are detailed in the linked documentation, but we can modify the most commonly\n",
139+
"changed values using the keywords ``galcen_distance``, ``galcen_v_sun``, and\n",
140+
"``z_sun`` which set the sun-Galactic center distance, the 3D velocity vector\n",
141+
"of the sun, and the height of the sun above the Galactic midplane,\n",
142+
"respectively. The velocity of the sun can be specified as an\n",
143+
"`Quantity` object with velocity units and is interpreted as a\n",
144+
"Cartesian velocity, as in the example below. Note that, as with the positions,\n",
145+
"the Galactocentric frame is a right-handed system (i.e., the Sun is at negative\n",
146+
"x values) so ``v_x`` is opposite of the Galactocentric radial velocity:"
147+
]
148+
},
149+
{
150+
"cell_type": "code",
151+
"execution_count": null,
152+
"id": "2d7bef03",
153+
"metadata": {},
154+
"outputs": [],
155+
"source": [
156+
"\n",
157+
"v_sun = [11.1, 244, 7.25] * (u.km / u.s) # [vx, vy, vz]\n",
158+
"gc_frame = coord.Galactocentric(\n",
159+
" galcen_distance=8 * u.kpc, galcen_v_sun=v_sun, z_sun=0 * u.pc\n",
160+
")"
161+
]
162+
},
163+
{
164+
"cell_type": "markdown",
165+
"id": "08791399",
166+
"metadata": {},
167+
"source": [
168+
"We can then transform to this frame instead, with our custom parameters:"
169+
]
170+
},
171+
{
172+
"cell_type": "code",
173+
"execution_count": null,
174+
"id": "d8717a8b",
175+
"metadata": {},
176+
"outputs": [],
177+
"source": [
178+
"gc2 = c1.transform_to(gc_frame)\n",
179+
"print(gc2.v_x, gc2.v_y, gc2.v_z)"
180+
]
181+
},
182+
{
183+
"cell_type": "markdown",
184+
"id": "c0b353c6",
185+
"metadata": {},
186+
"source": [
187+
"It is sometimes useful to specify the solar motion using the\n",
188+
"[proper motion of Sgr A*](https://arxiv.org/abs/astro-ph/0408107)\n",
189+
"instead of Cartesian velocity components. With an assumed distance, we can convert\n",
190+
"proper motion components to Cartesian velocity components using `units`:"
191+
]
192+
},
193+
{
194+
"cell_type": "code",
195+
"execution_count": null,
196+
"id": "bccb4ce4",
197+
"metadata": {},
198+
"outputs": [],
199+
"source": [
200+
"galcen_distance = 8 * u.kpc\n",
201+
"pm_gal_sgrA = [-6.379, -0.202] * (u.mas / u.yr) # from Reid & Brunthaler 2004\n",
202+
"vy, vz = -(galcen_distance * pm_gal_sgrA).to(u.km / u.s, u.dimensionless_angles())"
203+
]
204+
},
205+
{
206+
"cell_type": "markdown",
207+
"id": "b8853799",
208+
"metadata": {},
209+
"source": [
210+
"We still have to assume a line-of-sight velocity for the Galactic center,\n",
211+
"which we will again take to be 11 km/s:"
212+
]
213+
},
214+
{
215+
"cell_type": "code",
216+
"execution_count": null,
217+
"id": "cb4ac416",
218+
"metadata": {},
219+
"outputs": [],
220+
"source": [
221+
"vx = 11.1 * (u.km / u.s)\n",
222+
"v_sun2 = u.Quantity([vx, vy, vz]) # List of Quantity -> a single Quantity\n",
223+
"gc_frame2 = coord.Galactocentric(\n",
224+
" galcen_distance=galcen_distance, galcen_v_sun=v_sun2, z_sun=0 * u.pc\n",
225+
")\n",
226+
"gc3 = c1.transform_to(gc_frame2)\n",
227+
"print(gc3.v_x, gc3.v_y, gc3.v_z)"
228+
]
229+
},
230+
{
231+
"cell_type": "markdown",
232+
"id": "4e42feb4",
233+
"metadata": {},
234+
"source": [
235+
"The transformations also work in the opposite direction. This can be useful\n",
236+
"for transforming simulated or theoretical data to observable quantities. As\n",
237+
"an example, we will generate 4 theoretical circular orbits at different\n",
238+
"Galactocentric radii with the same circular velocity, and transform them to\n",
239+
"Heliocentric coordinates:"
240+
]
241+
},
242+
{
243+
"cell_type": "code",
244+
"execution_count": null,
245+
"id": "a52595f5",
246+
"metadata": {},
247+
"outputs": [],
248+
"source": [
249+
"import matplotlib.pyplot as plt\n",
250+
"import numpy as np\n",
251+
"import astropy.coordinates as coord\n",
252+
"from astropy import units as u\n",
253+
"ring_distances = np.arange(10, 26, 5) * u.kpc\n",
254+
"circ_velocity = 220 * (u.km / u.s)\n",
255+
"phi_grid = np.linspace(90, 270, 512) * u.degree # grid of azimuths\n",
256+
"ring_rep = coord.CylindricalRepresentation(\n",
257+
" rho=ring_distances[:, np.newaxis],\n",
258+
" phi=phi_grid[np.newaxis],\n",
259+
" z=np.zeros_like(ring_distances)[:, np.newaxis],\n",
260+
")\n",
261+
"angular_velocity = (-circ_velocity / ring_distances).to(\n",
262+
" u.mas / u.yr, u.dimensionless_angles()\n",
263+
")\n",
264+
"ring_dif = coord.CylindricalDifferential(\n",
265+
" d_rho=np.zeros(phi_grid.shape)[np.newaxis] * (u.km / u.s),\n",
266+
" d_phi=angular_velocity[:, np.newaxis],\n",
267+
" d_z=np.zeros(phi_grid.shape)[np.newaxis] * (u.km / u.s),\n",
268+
")\n",
269+
"ring_rep = ring_rep.with_differentials(ring_dif)\n",
270+
"gc_rings = coord.SkyCoord(ring_rep, frame=coord.Galactocentric)"
271+
]
272+
},
273+
{
274+
"cell_type": "markdown",
275+
"id": "de802b0a",
276+
"metadata": {},
277+
"source": [
278+
"First, let's visualize the geometry in Galactocentric coordinates. Here are\n",
279+
"the positions and velocities of the rings; note that in the velocity plot,\n",
280+
"the velocities of the 4 rings are identical and thus overlaid under the same\n",
281+
"curve:"
282+
]
283+
},
284+
{
285+
"cell_type": "code",
286+
"execution_count": null,
287+
"id": "b9795d77",
288+
"metadata": {},
289+
"outputs": [],
290+
"source": [
291+
"plot_positions_and_velocities(gc_rings)"
292+
]
293+
},
294+
{
295+
"cell_type": "markdown",
296+
"id": "c83d570e",
297+
"metadata": {},
298+
"source": [
299+
"Now we can transform to Galactic coordinates and visualize the rings in\n",
300+
"observable coordinates:"
301+
]
302+
},
303+
{
304+
"cell_type": "code",
305+
"execution_count": null,
306+
"id": "e570027d",
307+
"metadata": {},
308+
"outputs": [],
309+
"source": [
310+
"gal_rings = gc_rings.transform_to(coord.Galactic)\n",
311+
"fig, ax = plt.subplots(1, 1, figsize=(8, 6), dpi=200)\n",
312+
"for i in range(len(ring_distances)):\n",
313+
" ax.plot(\n",
314+
" gal_rings[i].l.degree,\n",
315+
" gal_rings[i].pm_l_cosb.value,\n",
316+
" label=str(ring_distances[i]),\n",
317+
" marker=\"None\",\n",
318+
" linewidth=3,\n",
319+
" )\n",
320+
"ax.set_xlim(360, 0)\n",
321+
"ax.set_xlabel(\"$l$ [deg]\")\n",
322+
"ax.set_ylabel(rf'$\\mu_l \\, \\cos b$ [{(u.mas/u.yr).to_string(\"latex_inline\")}]')\n",
323+
"ax.legend()\n",
324+
"plt.draw()"
325+
]
326+
}
327+
],
328+
"metadata": {
329+
"language_info": {
330+
"name": "python"
331+
}
332+
},
333+
"nbformat": 4,
334+
"nbformat_minor": 5
335+
}

0 commit comments

Comments
 (0)