301.0. Descripcion general de DP1#
301.0. Descripción general de DP1¶
Divulgación de Datos: Vista Previa de Datos 1
Tamaño del contenedor (Container size): large
Versión de las Pipelines Científicas de LSST: r29.2.0
Última verificación de ejecución: 2025-09-17
Repositorio: github.com/lsst/tutorial-notebooks
Objetivo de aprendizaje: Obtener una descripción general de los campos objetivo de los datos de la Vista Previa de Datos 1 (DP1) obtenidos con LSSTComCam.
Productos de datos LSST:
Paquetes: lsst.daf.butler, lsst.rsp, lsst.geom
Créditos: Desarrollado originalmente por Andrés A. Plazas Malagón y por el equipo científico de la comunidad de Rubin. Las secciones 3, 4 y 5 están basadas en notebooks desarrollados para el artículo de DP1 en github.com/lsst/rtn-095. Por favor, considerar reconocer su trabajo si este notebook se utiliza para la preparación de artículos de revistas, lanzamientos de software u otros notebooks.
Soporte: Se invita a toda la comunidad a hacer preguntas o plantear problemas en la Categoría de asistencia del Foro de la Comunidad de Rubin. El equipo de Rubin responderá a todas las preguntas publicadas allí.
1. Introducción¶
Este notebook ofrece una descripción general y un análisis exploratorio de los siete campos objetivo observados durante la campaña de puesta a punto en el cielo (on-sky commissioning campaign) de la ComCam del Observatorio Vera C. Rubin.
Entre octubre y diciembre de 2024, la ComCam se utilizó para adquirir ∼16,000 exposiciones para ingeniería de sistemas, validación del rendimiento científico y pruebas tempranas de procesamiento de datos (SITCOMTN-149). Un subconjunto de estas exposiciones formó la base de la Vista Previa de Datos 1 (DP1), ofreciendo a la comunidad científica la oportunidad de adquirir experiencia con datos y herramientas reales similares a los de LSST. Cada campo fue seleccionado para respaldar objetivos de validación específicos y casos de uso científico de LSST, cubriendo una amplia gama de posiciones en el cielo, densidades de objetos y superposición con conjuntos de datos externos.
Tutoriales relacionados: Otros tutoriales de la serie 301 sobre campos de la ComCam durante la puesta a punto en el cielo.
1.1. Descripción de los campos¶
47 Tuc
Un campo estelar densamente poblado; 47 Tucanae fue seleccionado para las primeras pruebas de calidad de imagen y de rendimiento de alineación del Sistema de Óptica Activa (AOS - Active Optic System). La alta densidad estelar de este campo lo hace ideal para probar pipelines de fotometría en campos abarrotados.
Campo de Baja Latitud Eclíptica (Rubin SV 38 7)
Este campo de baja latitud eclíptica fue seleccionado para optimizar el estudio científico del Sistema Solar. Se observó utilizando un patrón de grilla con dithering de 2x2 para facilitar el seguimiento de objetos en movimiento a lo largo de una región más amplia. Este campo respalda la ciencia del dominio temporal y pruebas relacionadas con la asociación de Objetos del Sistema Solar.
Galaxia Esferoidal Enana (dSph) de Fornax
Centrado en la Galaxia Esferoidal Enana de Fornax, este campo es ideal para estudiar poblaciones estelares resueltas en una galaxia enana del Grupo Local.
Campo Profundo Sur de Chandra Extendido (ECDFS)
El Campo Profundo Sur de Chandra Extendido (ECDFS - Extended Chandra Deep Field South) recibió la cobertura más densa y temporalmente consistente entre todos los campos, con cientos de visitas en los seis filtros. Se utilizó como el campo principal para la validación de la calibración fotométrica usando FGCM (Forward Global Calibration Method - método de calibración global directo), e incluye la estrella CalSpec C26202.
Campo Profundo Sur de Euclid (EDFS)
Este campo se superpone con el Campo Profundo Sur de Euclid (Euclid Deep Field South) y fue seleccionado para probar pipelines de fotometría profunda y de lentes gravitacionales débiles. Aunque su cobertura en filtros es más limitada que la del ECDFS, es altamente relevante para ciencia extragaláctica y futuras comparaciones entre diferentes relevamientos (borrador del artículo de DP1, §2.1).
Campo de Baja Latitud Galáctica (Rubin SV 95 -25)
Ubicado en una región de baja latitud Galáctica, este campo tiene una densidad estelar muy alta y es relevante para estudios de estructura Galáctica.
Nebulosa de la Gaviota
Apuntando a una región cercana a la Nebulosa de la Gaviota (Seagull Nebula), este campo respalda estudios del medio interestelar (ISM) y formación estelar. Tiene una densidad estelar moderadamente alta y se incluyó para explorar fondos estructurados y variaciones en la calidad de imagen y calibración.
1.1.1. Densidades estelares¶
Las densidades estelares varían significativamente entre los campos y fueron una consideración importante durante la selección de los mismos. Campos como 47 Tuc y Rubin SV 95 -25 contienen regiones de alta densidad estelar, poniendo a prueba los límites de las pipelines fotométricas y astrométricas bajo condiciones de campos abarrotados. En contraste, ECDFS y EDFS son campos extragalácticos con poblaciones estelares escasas, lo que permite coagregaciones profundas y ciencia de lentes gravitacionales débiles.
1.1.2. Cadencia temporal¶
La cadencia temporal también varía según el campo y estuvo determinada por los objetivos científicos y la factibilidad operativa. ECDFS tuvo el muestreo más uniforme y con periodicidad nocturna, permitiendo estudios de imágenes diferenciales y de calibración fotométrica. Rubin SV 38 7 fue visitado con un patrón de dithering estructurado a lo largo de múltiples noches para facilitar la investigación científica del Sistema Solar. Otros campos, como Gaviota (Seagull) y Fornax, recibieron una cobertura más dispersa pero igualmente valiosa a lo largo de distintos filtros y noches.
1.1.3. Tabla resumen¶
Véase también la figura en la Sección 3.3.
| Campo | Uso científico sugerido | Bandas | Densidad estelar | Cadencia relativa | Datos externos |
|---|---|---|---|---|---|
| 47 Tuc | Fotometría en campos abarrotados | griy | Alta | Dispersa | GAIA |
| Rubin SV 38 7 | Objetos del Sistema Solar | griz | Media | Baja | — |
| Fornax dSph | Galaxias enanas resueltas | gri | Alta | Muy dispersa | — |
| ECDFS | Extragaláctico, dominio temporal | ugrizy | Baja | Máxima | HST, DECam, The Monster |
| EDFS | Extragaláctico, lentes gravitacionales débiles | ugrizy | Baja | Alta | Euclid |
| Rubin SV 95 -25 | Ciencia del plano Galáctico | ugrizy | Media | Media | — |
| Seagull | Formación estelar, ISM | ugrz | Media | Dispersa | — |
1.2. Importar paquetes¶
Importar numpy, un paquete fundamental para la computación científica con arreglos en Python
(numpy.org), y
matplotlib, una biblioteca completa para visualización de datos
(matplotlib.org;
galería de matplotlib).
Del paquete lsst, importar módulos para acceder al servicio del Protocolo de Acceso a Datos Tabulados TAP (Table Access Protocol),
Butler y las herramientas de geometría de las Pipelines Científicas de LSST
(pipelines.lsst.io).
De astropy, importar módulos para transformaciones de coordenadas celestes, unidades y tablas
(astropy.org), y astroplan para trabajar con objetos astronómicos.
Usar skyproj, un paquete especializado en proyecciones del cielo, para crear mapas celestes consistentes
(skyproj).
Utilizar las herramientas de representación gráfica en lsst.utils.plotting para acceder a esquemas de colores, símbolos y estilos de línea estándar de LSST para visualizaciones multibanda.
import numpy as np
import matplotlib.pyplot as plt
from astropy.coordinates import SkyCoord
import astropy.units as u
import skyproj
from lsst.rsp import get_tap_service
from lsst.daf.butler import Butler
from lsst.utils.plotting import (
get_multiband_plot_colors,
get_multiband_plot_symbols,
get_multiband_plot_linestyles
)
1.3. Definir parámetros y funciones¶
Instanciar Butler con el repositorio, la colección y el mapa del cielo apropiados para DP1.
butler = Butler('dp1', collections='LSSTComCam/DP1')
assert butler is not None
Instanciar el servicio TAP.
service = get_tap_service("tap")
assert service is not None
Definir colores, símbolos y estilos de línea para representar los seis filtros de LSST, $ugrizy$, según lo definido en RTN-045.
filter_names = ['u', 'g', 'r', 'i', 'z', 'y']
filter_colors = get_multiband_plot_colors()
filter_symbols = get_multiband_plot_symbols()
filter_linestyles = get_multiband_plot_linestyles()
Definir un diccionario con el nombre y las coordenadas centrales de cada campo, Ascensión Recta y Declinación, según lo definido en SITCOMTN-149. Las siguientes son coordenadas del Sistema Internacional de Referencia Celeste (ICRS), expresadas en grados decimales.
field_centers = {
"47 Tuc": [6.02, -72.08],
"SV 38 7": [37.86, 6.98],
"Fornax": [40.00, -34.45],
"ECDFS": [53.13, -28.10],
"EDFS": [59.10, -48.73],
"SV 95 -25": [95.00, -25.00],
"Seagull": [106.23, -10.51],
}
field_names = list(field_centers.keys())
2. Mapa del cielo¶
Usar skyproj para visualizar los campos en la esfera celeste.
fig, ax = plt.subplots(figsize=(8, 4))
sp = skyproj.McBrydeSkyproj(ax=ax, celestial=True, galactic=False, gridlines=True)
sp.ax.set_xlabel("Ascensión Recta", fontsize=14, fontweight="bold", labelpad=12)
sp.ax.set_ylabel("Declinación", fontsize=15, fontweight="bold", labelpad=12)
for name, (ra, dec) in field_centers.items():
sp.ax.plot(ra, dec, marker="o", color="red", markersize=11.5)
x, y = sp.proj(ra, dec)
sp.ax.annotate(name, (x, y), fontsize=10, fontweight="bold",
textcoords="offset points", xytext=(3.4, 3.4))
ecl_lon = np.linspace(0, 360, 1000)
ecl_lat = np.zeros_like(ecl_lon)
ecl_eq = SkyCoord(ecl_lon * u.deg, ecl_lat * u.deg,
frame="geocentrictrueecliptic").transform_to("icrs")
ecl_ra = ecl_eq.ra.deg
ecl_dec = ecl_eq.dec.deg
sp.ax.plot(ecl_ra, ecl_dec, color="gray", linestyle="--", lw=1.3, zorder=5)
sp.ax.text(*sp.proj(260, 50), "Eclíptica", fontsize=12, color="gray",
ha="left", va="bottom")
l_vals = np.linspace(0, 360, 1000)
for b in [0, 10, -10]:
gal = SkyCoord(l_vals * u.deg, b * u.deg, frame="galactic")
eq = gal.transform_to("icrs")
sp.ax.plot(eq.ra.deg, eq.dec.deg,
color="black" if b == 0 else "gray",
linestyle="-" if b == 0 else "--",
lw=1.0 if b == 0 else 0.8, alpha=0.6)
sp.ax.gridlines.set_edgecolor("black")
sp.ax.gridlines.set_linestyle("--")
sp.ax.gridlines.set_alpha(0.3)
sp.ax.gridlines.zorder = 0
plt.tight_layout()
plt.show()
Figura 1: Una vista de todo el cielo con los siete campos de DP1 marcados con círculos rojos y etiquetados por nombre.
3. Visitas¶
Usar el servicio TAP para obtener el identificador de visita, coordenadas, banda y el MJD de todas las visitas.
query = """SELECT visit, ra, dec, band, expMidptMJD FROM dp1.Visit"""
job = service.submit_job(query)
job.run()
job.wait(phases=['COMPLETED', 'ERROR'])
print('La fase del trabajo es', job.phase)
if job.phase == 'ERROR':
job.raise_if_error()
La fase del trabajo es COMPLETED
assert job.phase == 'COMPLETED'
visits = job.fetch_result().to_table()
job.delete()
del query
3.1. Distribuciones de filtros¶
Imprimir la cantidad de visitas por filtro, por campo, y el total por campo.
print('Campo visitas por filtro total')
print(' u g r i z y ')
for i in range(len(field_names)):
name = field_names[i]
ra, dec = field_centers[name]
offsets = np.sqrt((visits['ra']-ra)**2
+ (visits['dec']-dec)**2)
tx = np.where(offsets < 3.0)[0]
tmp = np.zeros(6, dtype='int')
for f, filt in enumerate(filter_names):
tmp[f] = len(np.where(visits['band'][tx] == filt)[0])
print('%-10s %3i %3i %3i %3i %3i %3i %3i' %
(name, tmp[0], tmp[1], tmp[2], tmp[3], tmp[4], tmp[5], len(tx)))
del name, ra, dec, offsets, tx, tmp
Campo visitas por filtro total
u g r i z y
47 Tuc 0 10 32 19 0 5 66
SV 38 7 0 44 40 55 20 0 159
Fornax 0 5 25 12 0 0 42
ECDFS 43 230 237 162 153 30 855
EDFS 20 61 87 42 42 20 272
SV 95 -25 33 82 84 23 60 10 292
Seagull 10 37 43 0 10 0 100
3.2. Épocas (noches)¶
Imprimir la cantidad de noches en las que se observó el campo y el número promedio de visitas por noche.
print('Campo # noches media visitas/noche')
for i in range(len(field_names)):
name = field_names[i]
ra, dec = field_centers[name]
offsets = np.sqrt((visits['ra']-ra)**2
+ (visits['dec']-dec)**2)
tx = np.where(offsets < 3.0)[0]
days = np.floor(visits['expMidptMJD'][tx])
values, counts = np.unique(days, return_counts=True)
tmp1 = len(values)
tmp2 = np.mean(counts)
print('%-10s %3i %2.1f' % (name, tmp1, tmp2))
del name, ra, dec, offsets, tx
del days, values, counts, tmp1, tmp2
Campo # noches media visitas/noche 47 Tuc 4 16.5 SV 38 7 5 31.8 Fornax 2 21.0 ECDFS 21 40.7 EDFS 9 30.2 SV 95 -25 10 29.2 Seagull 4 25.0
3.3. Cadencia temporal¶
plt.figure(figsize=(8, 6))
spacing = 0.6
for i in range(len(field_names)):
name = field_names[i]
ra, dec = field_centers[name]
offsets = np.sqrt((visits['ra']-ra)**2
+ (visits['dec']-dec)**2)
tx = np.where(offsets < 3.0)[0]
for f, filt in enumerate(filter_names):
fx = np.where(visits['band'][tx] == filt)[0]
if len(fx) > 0:
yval = (i * spacing) - (f * 0.12) + 0.3
for j in range(len(fx)):
plt.plot(visits['expMidptMJD'][tx[fx[j]]], yval,
filter_symbols[filt], alpha=0.5,
color=filter_colors[filt],
label=filt if (j == 0) & (i == 3) else None)
yticks = [i * spacing for i in range(len(field_names))]
plt.yticks(yticks, [t for t in field_names], fontsize=12)
plt.xlabel("Punto Medio de Exposición (MJD)", fontsize=12)
plt.grid(axis='x', color='0.8', linewidth=1)
plt.legend(loc='upper left', ncol=3)
plt.ylim(-spacing / 1.5, (len(field_names) - 1) * spacing + spacing / 1.5)
plt.tight_layout()
4. Calidad de imagen¶
Usar el servicio TAP para obtener el seeing (la visibilidad astronómica) y los límites de magnitud que se evaluaron para todas las visit_images (todos los detectores) de la tabla CcdVisit.
query = """SELECT visitId, ra, dec, band, seeing, magLim FROM dp1.CcdVisit"""
job = service.submit_job(query)
job.run()
job.wait(phases=['COMPLETED', 'ERROR'])
print('La fase del trabajo es', job.phase)
if job.phase == 'ERROR':
job.raise_if_error()
La fase del trabajo es COMPLETED
assert job.phase == 'COMPLETED'
ccdvisits = job.fetch_result().to_table()
job.delete()
del query
4.1. Seeing (Visibilidad astronómica)¶
Calcular el seeing (visibilidad astronómica) promedio sobre todos los detectores por filtro, y para todos los filtros.
print('Campo seeing por filtro media')
print(' u g r i z y ')
for i in range(len(field_names)):
name = field_names[i]
ra, dec = field_centers[name]
offsets = np.sqrt((ccdvisits['ra']-ra)**2
+ (ccdvisits['dec']-dec)**2)
tx = np.where(offsets < 3.0)[0]
tmp = []
for f, filt in enumerate(filter_names):
fx = np.where(ccdvisits['band'][tx] == filt)[0]
if len(fx) > 0:
tmp.append(str(np.round(np.mean(ccdvisits['seeing'][tx[fx]]), 2)))
else:
tmp.append(' - ')
tmp.append(str(np.round(np.mean(ccdvisits['seeing'][tx]), 2)))
print('%-10s %-4s %-4s %-4s %-4s %-4s %-4s %-4s' %
(name, tmp[0], tmp[1], tmp[2], tmp[3], tmp[4], tmp[5], tmp[6]))
del name, ra, dec, offsets, tx
Campo seeing por filtro media
u g r i z y
47 Tuc - 1.47 1.26 1.11 - 1.34 1.25
SV 38 7 - 1.16 1.13 1.11 1.23 - 1.14
Fornax - 1.16 0.83 0.87 - - 0.88
ECDFS 1.43 1.17 1.09 1.01 1.0 1.07 1.1
EDFS 1.88 1.2 1.2 1.05 1.17 0.98 1.21
SV 95 -25 1.48 1.24 1.13 0.99 1.23 0.83 1.2
Seagull 1.53 1.26 1.12 - 1.21 - 1.22
4.2. Límites de magnitud¶
Calcular el límite de magnitud promedio sobre todos los detectores por filtro, y para todos los filtros.
print('Campo mag lim por filtro media')
print(' u g r i z y ')
for i in range(len(field_names)):
name = field_names[i]
ra, dec = field_centers[name]
offsets = np.sqrt((ccdvisits['ra']-ra)**2
+ (ccdvisits['dec']-dec)**2)
tx = np.where(offsets < 3.0)[0]
tmp = []
for f, filt in enumerate(filter_names):
fx = np.where(ccdvisits['band'][tx] == filt)[0]
if len(fx) > 0:
tmp.append(str(np.round(np.mean(ccdvisits['magLim'][tx[fx]]), 2)))
else:
tmp.append(' - ')
tmp.append(str(np.round(np.mean(ccdvisits['magLim'][tx]), 2)))
print('%-10s %-5s %-5s %-5s %-5s %-5s %-5s %-5s' %
(name, tmp[0], tmp[1], tmp[2], tmp[3], tmp[4], tmp[5], tmp[6]))
del name, ra, dec, offsets, tx
Campo mag lim por filtro media
u g r i z y
47 Tuc - 24.16 24.28 23.99 - 21.7 23.98
SV 38 7 - 24.45 24.19 23.74 22.9 - 23.94
Fornax - 24.78 24.74 24.15 - - 24.58
ECDFS 23.5 24.49 24.15 23.9 23.14 21.94 23.9
EDFS 23.3 24.57 24.15 23.87 23.18 22.15 23.84
SV 95 -25 23.54 24.63 24.36 23.95 23.12 22.17 23.98
Seagull 23.38 24.02 23.76 - 23.18 - 23.76
5. Profundidad coagregada¶
Utilizar los mapas de propiedades del relevamiento para imprimir la profundidad coagregada en cada banda en el centro de cada campo.
Cargar los límites de magnitud PSF en cada filtro.
u_maglim = butler.get('deepCoadd_psf_maglim_consolidated_map_weighted_mean',
band='u')
g_maglim = butler.get('deepCoadd_psf_maglim_consolidated_map_weighted_mean',
band='g')
r_maglim = butler.get('deepCoadd_psf_maglim_consolidated_map_weighted_mean',
band='r')
i_maglim = butler.get('deepCoadd_psf_maglim_consolidated_map_weighted_mean',
band='i')
z_maglim = butler.get('deepCoadd_psf_maglim_consolidated_map_weighted_mean',
band='z')
y_maglim = butler.get('deepCoadd_psf_maglim_consolidated_map_weighted_mean',
band='y')
Imprimir el valor por filtro en el centro de cada campo. Cuando no haya datos, imprimir "nan". Observar que todas las profundidades para 47 Tuc se evalúan como "nan", y la razón de esto se analiza en la Sección 5.1.
print('Campo mag lim coagregada por filtro ')
print(' u g r i z y')
for i in range(len(field_names)):
name = field_names[i]
ra, dec = field_centers[name]
lims = np.asarray([u_maglim.get_values_pos(ra, dec),
g_maglim.get_values_pos(ra, dec),
r_maglim.get_values_pos(ra, dec),
i_maglim.get_values_pos(ra, dec),
z_maglim.get_values_pos(ra, dec),
y_maglim.get_values_pos(ra, dec)], dtype='float')
tx = np.where(lims < 0.0)[0]
lims[tx] = float('NaN')
print('%-10s %5.2f %5.2f %5.2f %5.2f %5.2f %5.2f' %
(name, lims[0], lims[1], lims[2], lims[3], lims[4], lims[5]))
Campo mag lim coagregada por filtro
u g r i z y
47 Tuc nan nan nan nan nan nan
SV 38 7 nan 25.29 25.06 24.72 23.49 nan
Fornax nan nan 25.08 25.31 nan nan
ECDFS 25.18 27.36 27.09 26.50 25.61 23.57
EDFS 23.89 26.53 26.47 25.73 24.98 23.57
SV 95 -25 25.12 26.74 26.53 25.44 25.14 23.28
Seagull 24.32 25.83 25.57 nan 24.09 nan
¡Advertencia! La profundidad coagregada en el centro de 47 Tuc no se evalúa (no es un número, "nan"). Pasar a la siguiente sección para comprender porqué.
5.1. Mapas de profundidad¶
Graficar los mapas de profundidad en banda r para cada campo. Observar que 47 Tuc tiene un “agujero” en el centro, donde el campo era demasiado denso como para que las imágenes pudieran procesarse para la Vista Previa de Datos 1 (DP1). Se están realizando mejoras de software y ajustes de configuración para poder manejar ese tipo de campos tan densamente poblados.
for i in range(len(field_names)):
name = field_names[i]
ra_cen, dec_cen = field_centers[name]
span_dec = 0.75
if i == 1:
span_dec = 1.00
span_ra = span_dec / np.cos(np.deg2rad(dec_cen))
ra = np.linspace(ra_cen-span_ra, ra_cen+span_ra, 250)
dec = np.linspace(dec_cen-span_dec, dec_cen+span_dec, 250)
x, y = np.meshgrid(ra, dec)
values = r_maglim.get_values_pos(x, y)
row, col = np.where(values < 0.0)
values[row, col] = 'NaN'
fig = plt.figure(figsize=(4, 3))
plt.pcolormesh(x, y, values, vmin=24.0, vmax=27)
plt.xlabel("Ascensión Recta (deg)")
plt.ylabel("Declinación (deg)")
plt.title(name)
plt.colorbar(label="Mag PSF Límite (banda r)")
plt.gca().invert_xaxis()
plt.show()
Figura 2: Siete paneles que muestran el mapa de propiedad de profundidad en banda r para cada campo.