201.14. IsolatedStarStellarMotions table#
201.14. IsolatedStarStellarMotions table¶
For the Rubin Science Platform at data.lsst.cloud.
Data Release: Data Preview 2
Container Size: large
LSST Science Pipelines version: r30.0.10
Last verified to run: 2026-07-22
Repository: github.com/lsst/tutorial-notebooks
Learning objective: To understand the contents of the IsolatedStarStellarMotions table and how to access it.
LSST data products: IsolatedStarStellarMotions, Object
Packages: lsst.rsp.RSPDiscovery, lsst.daf.butler
Credit: Originally developed by the Rubin Community Science team. Please consider acknowledging them if this notebook is used for the preparation of journal articles, software releases, or other notebooks.
Get Support: Everyone is encouraged to ask questions or raise issues in the Support Category of the Rubin Community Forum. Rubin staff will respond to all questions posted there.
1. Introduction¶
The IsolatedStarStellarMotions table provides calibrated positions, proper motions, and parallaxes for isolated point sources across the survey footprint. Following single-frame processing, point sources with no nearby neighbors within a $2^{\prime\prime}$ radius are identified across all input science images. These isolated sources are cross-matched across multiple overlapping visits and tied to an external reference catalog (Gaia DR3). The global astrometric model is computed jointly across the matched visits using gbdesAstrometricFitTask. Each distinct physical star in this catalog is tracked across visits by a unique identifier, isolated_star_id.
- TAP table name:
dp2.IsolatedStarStellarMotions - butler table name:
isolated_star_stellar_motions - columns: 28
- rows: 318,429,415
Related tutorials: The TAP and Butler data access services are demonstrated in the 100-level "How to" tutorials.
1.1. Import packages¶
Import standard python packages re, numpy, matplotlib, and astropy.
From the lsst package, import modules for the TAP service and the butler.
import re
import numpy as np
import matplotlib.pyplot as plt
import astropy.units as u
from lsst.rsp import RSPDiscovery
from lsst.daf.butler import Butler
1.2. Define parameters and functions¶
Instantiate RSPDiscovery with the DP2 release, and create an instance of the TAP service.
discovery = RSPDiscovery("dp2")
service = discovery.get_tap_client()
Instantiate a Butler for DP2.
butler = Butler('dp2', collections="dp2")
2. Schema (columns)¶
To browse the table schema visit the Rubin schema browser, or use the TAP service via the Portal Aspect or as demonstrated in Section 2.1.
2.1. Retrieve table schema¶
To retrieve the table schema, define a query for the schema columns of the IsolatedStarStellarMotions table and run the query job.
query = """
SELECT column_name, datatype, description, unit
FROM tap_schema.columns
WHERE table_name = 'dp2.IsolatedStarStellarMotions'
"""
job = service.submit_job(query)
job.run()
job.wait(phases=['COMPLETED', 'ERROR'])
print('Job phase is', job.phase)
if job.phase == 'ERROR':
job.raise_if_error()
Job phase is COMPLETED
Retrieve the query results and save them as an astropy table with the to_table() attribute.
results = job.fetch_result().to_table()
results
| column_name | datatype | description | unit |
|---|---|---|---|
| str64 | str64 | str512 | str64 |
| dec | double | Position in declination at the reference epoch. | deg |
| dec_decPM_Cov | double | Covariance between declination and proper motion in declination. | deg*mas/yr |
| decErr | double | Error in declination at the reference epoch. | deg |
| dec_parallax_Cov | double | Covariance between declination and parallax. | deg*mas |
| decPM | double | Proper motion in declination. | mas/yr |
| decPMErr | double | Error in proper motion in declination. | mas/yr |
| decPM_parallax_Cov | double | Covariance between proper motion in declination and parallax. | mas**2/yr |
| dec_raPM_Cov | double | Covariance between declination and proper motion in right ascension. | deg*mas/yr |
| epoch | double | Epoch in MJD to which the position corresponds. | d |
| isolated_star_id | long | Unique id of isolated star | |
| parallax | double | Parallax | mas |
| parallaxErr | double | Error in parallax. | mas |
| ra | double | Position in right ascension at the reference epoch. | deg |
| ra_dec_Cov | double | Covariance between right ascension (RA*cos(Dec)) and declination. | deg**2 |
| ra_decPM_Cov | double | Covariance between right ascension and proper motion in declination. | deg*mas/yr |
| raErr | double | Angular error in right ascension (∂RA*cos(Dec)) at the reference epoch. | deg |
| ra_parallax_Cov | double | Covariance between right ascension and parallax. | deg*mas |
| raPM | double | Proper motion in right ascension. | mas/yr |
| raPM_decPM_Cov | double | Covariance between proper motion in right ascension and proper motion in declination. | mas**2/yr**2 |
| raPMErr | double | Error in proper motion in right ascension. | mas/yr |
| raPM_parallax_Cov | double | Covariance between proper motion in right ascension and parallax. | mas**2/yr |
| ra_raPM_Cov | double | Covariance between right ascension and proper motion in right ascension. | deg*mas/yr |
| ref_dec | double | Position of the reference object in declination at the reference epoch. | deg |
| ref_decPM | double | Proper motion of the reference object in declination. | mas/yr |
| referenceId | long | ID of the reference object if available, from Gaia DR3. | |
| ref_parallax | double | Parallax of the reference object | mas |
| ref_ra | double | Position of the reference object in right ascension at the reference epoch. | deg |
| ref_raPM | double | Proper motion of the reference object in right ascension. | mas/yr |
The table displayed above has been truncated.
Option to print every column name as a list.
# for col in results['column_name']:
# print(col)
Option to use the regular expressions package re to find all column names containing the string "PM".
# for col in results['column_name']:
# if re.search('PM', col):
# print(col)
Option to search column names that contain a given string that has been assigned to the variable temp.
# temp = 'parallax'
# temp = 'ref'
# temp = 'ra'
# temp = 'dec'
# for col in results['column_name']:
# if re.search(temp, col):
# print(col)
Delete the job, but not the results.
del query
job.delete()
2.2.2. Coordinates and epoch¶
The sky coordinates and their uncertainties in decimal degrees for each object at the reference epoch.
ra,raErrdec,decErr
The reference epoch in MJD to which the sky coordinates corresponds.
epoch
The sky coordinates of the Gaia DR3 reference object at the reference epoch in decimal degrees.
ref_ra,ref_dec
2.2.3. Astrometric solutions¶
Parallax
The gbdes astrometric solution for parallax measured in milliarcseconds (mas).
parallax,parallaxErr
Proper motion
The gbdes astrometric solution for proper motion in right ascension and declination, measured in mas per year (mas/yr). The right ascension proper motion components include the $cos(\delta)$ factor.
raPM,raPMErrdecPM,decPMErr
Gaia solutions
The reference parallax and proper motion from the Gaia DR3 gaia_source table.
ref_parallaxref_raPM,ref_decPM
2.3. Descriptions and units¶
For a subset of the key columns show the table of their descriptions and units.
col_list = ['isolated_star_id', 'ra', 'dec', 'epoch',
'parallax', 'parallax','raPM', 'decPM', 'raPMErr', 'decPMErr',
'referenceId', 'ref_parallax', 'ref_raPM', 'ref_decPM']
tx = [i for i, item in enumerate(results['column_name']) if item in col_list]
results[tx]
| column_name | datatype | description | unit |
|---|---|---|---|
| str64 | str64 | str512 | str64 |
| dec | double | Position in declination at the reference epoch. | deg |
| decPM | double | Proper motion in declination. | mas/yr |
| decPMErr | double | Error in proper motion in declination. | mas/yr |
| epoch | double | Epoch in MJD to which the position corresponds. | d |
| isolated_star_id | long | Unique id of isolated star | |
| parallax | double | Parallax | mas |
| ra | double | Position in right ascension at the reference epoch. | deg |
| raPM | double | Proper motion in right ascension. | mas/yr |
| raPMErr | double | Error in proper motion in right ascension. | mas/yr |
| ref_decPM | double | Proper motion of the reference object in declination. | mas/yr |
| referenceId | long | ID of the reference object if available, from Gaia DR3. | |
| ref_parallax | double | Parallax of the reference object | mas |
| ref_raPM | double | Proper motion of the reference object in right ascension. | mas/yr |
Clean up.
del tx, results
3. Data access¶
The IsolatedStarStellarMotions table is available via the TAP service and the butler.
Recommended access method: TAP.
3.1. Advisory: avoid full-table queries¶
Avoid full-table queries. Always include spatial constraints whenever possible, particularly when running quick tests.
This table contains measurements for 318,429,415 stars. Thus, full-table TAP queries can take a very long time to complete.
3.2. TAP (Table Access Protocol)¶
The IsolatedStarStellarMontions table is stored in Qserv and accessible via the TAP services using ADQL queries.
Include spatial constraints:
Qserv stores catalog data sharded by coordinate (RA, Dec), so ADQL query statements that include constraints by coordinate do not require a whole-catalog search and are typically faster (and can be much faster) than ADQL query statements which only include constraints for other columns.
Use either an ADQL cone or polygon search for faster queries (do not use WHERE ... BETWEEN statements to set boundaries on RA and Dec).
3.2.1. Demo query¶
Define a query to return the few "key columns" from Section 2.2.
Impose spatial constraints: search within 0.1 degrees of the center of RA, Dec = $53.01 -28.35$.
query = """
SELECT ra, dec, raPM, decPM, raPMErr, decPMErr,
ref_raPM, ref_decPM
FROM dp2.IsolatedStarStellarMotions
WHERE CONTAINS(POINT('ICRS', ra, dec), CIRCLE('ICRS', 53.01, -28.35, 0.1))=1
"""
job = service.submit_job(query)
job.run()
job.wait(phases=['COMPLETED', 'ERROR'])
print('Job phase is', job.phase)
if job.phase == 'ERROR':
job.raise_if_error()
Job phase is COMPLETED
Fetch the results as an astropy table.
results = job.fetch_result().to_table()
print(len(results))
202
Option to display the table.
# results
As an example, generate a plot comparing the measured stellar proper motions from the dp2.IsolatedStarStellarMotions catalog against
their corresponding reference values from the Gaia DR3 (ref_raPM, ref_decPM).
fig, axes = plt.subplots(1, 2, figsize=(8, 4), sharey=True)
ax1 = axes[0]
h = ax1.hist(results['raPM'] - results['ref_raPM'], bins=50)
ax1.set_xlabel(r'Measured - Refrence PM in RA ($\mathrm{mas\cdot yr^{-1}}$)')
ax1.set_ylabel('N')
ax1.set_title('RA Proper Motion Comparison', fontweight='bold')
ax2 = axes[1]
h = ax2.hist(results['decPM']- results['ref_decPM'], bins=50)
ax2.set_xlabel(r'Measured - Reference PM in Dec ($\mathrm{mas\cdot yr^{-1}}$)')
ax2.set_title('Dec Proper Motion Comparison', fontweight='bold')
plt.tight_layout()
plt.show()
Figure 1: Comparison of measured and reference proper motions for isolated stars. (Left) Histogram of difference in right ascension proper motion between measured and Gaia DR3 reference values. (Right) Histogram of difference in declination proper motion between measured and Gaia DR3 reference values. Small differences in this comparison indicate good agreement between the Rubin DP2 solution and Gaia DR3 values. This is expected, as the Gaia solutions were used in the fit.
Clean up.
job.delete()
del results
3.2.2. Joinable tables¶
While the isolatedStarStellarMotions table stores ensemble astrometric measurement outputs generated from overlapping visit cross-matches, it can be joined to the Object table via spatial matching. The Object table contains measurements made on the deep_coadd image for unique objects.
Modify the query used in Section 3.2.1, add a table join to the Object table, and retrieve the PSF $g$-band magnitude column g_psfMag.
query = """
SELECT istar.raPMErr, istar.decPMErr, obj.g_psfMag
FROM dp2.IsolatedStarStellarMotions AS istar
JOIN dp2.Object as obj
ON DISTANCE(POINT('ICRS', obj.coord_ra, obj.coord_dec),
POINT('ICRS', istar.ra, istar.dec)) < 0.00027778
WHERE CONTAINS(POINT('ICRS', istar.ra, istar.dec),
CIRCLE('ICRS', 53.01, -28.35, 0.1))=1
"""
job = service.submit_job(query)
job.run()
job.wait(phases=['COMPLETED', 'ERROR'])
print('Job phase is', job.phase)
if job.phase == 'ERROR':
job.raise_if_error()
Job phase is COMPLETED
Fetch the results as an astropy table.
results = job.fetch_result().to_table()
print(len(results))
202
Plot the proper motion errors as a function of $g$-band magnitude.
fig, axes = plt.subplots(1, 2, figsize=(8, 4), sharey=True)
ax1 = axes[0]
ax1.scatter(results['g_psfMag'], results['raPMErr'], s=3)
ax1.set_xlabel(r'$g$-band psfMag (mag)')
ax1.set_ylabel(r'PM error in RA ($\mathrm{mas\cdot yr^{-1}}$)')
ax1.set_yscale('log')
ax2 = axes[1]
ax2.scatter(results['g_psfMag'], results['decPMErr'], s=3)
ax2.set_xlabel(r'$g$-band psfMag (mag)')
ax2.set_ylabel(r'PM error in Dec ($\mathrm{mas\cdot yr^{-1}}$)')
plt.tight_layout()
plt.show()
Figure 2: Proper motion uncertainties in right ascension (left) and declination (right) as a function of $g$-band PSF magnitude. Errors are plotted on a logarithmic scale to illustrate the large astrometric noise toward fainter magnitudes. Bright sources (g < 21 mag) benefit from Gaia DR3 reference solutions in the global astrometric fit, achieving small proper motion errors (<1 $\mathrm{mas\cdot yr^{-1}}$). For fainter stars, however, the short observational baseline of DP2 leads to a rapid increase in proper motion uncertainties, reaching 10–100 $\mathrm{mas\cdot yr^{-1}}$.
3.3. Butler¶
TAP is the recommended way to access the isolated_star_stellar_motions table, but the Butler is a convenient way to retrieve all the
isolated stars in a given tract.
Show that the only dimension for the isolated_star_stellar_motions table is the skymap's tract, and that it is required.
butler.get_dataset_type('isolated_star_stellar_motions')
DatasetType('isolated_star_stellar_motions', {instrument, skymap, tract}, ArrowAstropy)
butler.get_dataset_type('isolated_star_stellar_motions').dimensions.required
{instrument, skymap, tract}
3.3.1. Demo query¶
Include spatial constraints:
The Butler isolated_star_stellar_motions table contents are stored and retrieved by individual tract.
Retrieve all dataset_refs for isolated_star_stellar_motions tables for tracts that overlap the coordinates near the center of RA, Dec = $53.01 -28.35$.
query = "tract.region OVERLAPS POINT(53.01, -28.35)"
refs = butler.query_datasets('isolated_star_stellar_motions', where=query)
Show the tracts that overlap with the target coordinates.
for ref in refs:
print(ref.dataId)
{instrument: 'LSSTCam', skymap: 'lsst_cells_v2', tract: 4848}
{instrument: 'LSSTCam', skymap: 'lsst_cells_v2', tract: 4849}
{instrument: 'LSSTCam', skymap: 'lsst_cells_v2', tract: 5063}
Get the data for the first tract from the butler. Recycle col_list defined in Section 2.3.
results = butler.get(refs[0],
parameters={'columns': col_list})
Option to display the results.
# results
Construct a spatial proper motion vector map showing the direction of stellar motion across the field of view (downsampled by a stride factor to maintain clarity) and a proper motion map to identify comoving stellar structures or kinematic clustering in proper motion space.
fig, axes = plt.subplots(1, 2, figsize=(11, 4.5))
stride = 15
sample_label = "Stars" if stride == 1 else f"Stars (1/{stride} sampled)"
ax1 = axes[0]
ax1.scatter(results['ra'], results['dec'], s=1, color='lightgray', alpha=0.5, label='Catalog Stars')
q = ax1.quiver(
results['ra'][::stride], results['dec'][::stride],
results['raPM'][::stride], results['decPM'][::stride],
color='teal', alpha=0.8, width=0.003, headwidth=3, scale=500,
label=sample_label
)
ax1.quiverkey(
q, X=0.15, Y=0.90, U=100,
label=r'$100\mathrm{\ mas\cdot yr^{-1}}$',
labelpos='N', coordinates='axes',
color='m', labelcolor='m', labelsep=0.05,
fontproperties={'size': 11, 'weight': 'bold'}
)
ax1.set_xlabel('RA (deg)')
ax1.set_ylabel('Dec (deg)')
ax1.invert_xaxis()
ax1.legend(loc='lower right', frameon=True, fontsize='small')
ax1.set_aspect('equal', adjustable='box')
ax1.set_title('Spatial Distribution & Vector Scale')
ax2 = axes[1]
ax2.scatter(results['raPM'], results['decPM'], s=2, color='crimson', alpha=0.1)
ax2.set_xlabel(r'$\mu_{\alpha}^*$ ($\mathrm{mas\cdot yr^{-1}}$)')
ax2.set_ylabel(r'$\mu_{\delta}$ ($\mathrm{mas\cdot yr^{-1}}$)')
ax2.axhline(0, color='gray', linestyle='--', linewidth=0.5)
ax2.axvline(0, color='gray', linestyle='--', linewidth=0.5)
ax2.set_xlim(-50, 50)
ax2.set_ylim(-50, 50)
ax2.set_aspect('equal', adjustable='box')
ax2.set_title('Proper Motion Diagram')
plt.tight_layout()
plt.show()
Figure 3: (Left) Spatial distribution of isolated stars in the target field, where arrows represent the proper motion vectors (
raPM,decPM) for a downsampled subset (1/15). (Right) The corresponding proper motion map tracking the dense velocity distribution of the entire sample in mas⋅yr$^{−1}$.
del query, refs, col_list, results