Autor: Eduardo Martín Calleja
Tras mi anterior entrada en este blog, dedicada al tama del color de los objetos celestes, esta entrada constituye su continuación, en la cual examinaremos los datos de un catálogo de estrellas que contenga datos fotométricos y espectrográficos y formaremos con ellos gráficos color-color en los que se tratará de diferenciar entre las diferentes clases estelares. Es decir, si el post anterior introducía una serie de conceptos teóricos, ahora vamos a aplicar estos conceptos a datos reales.
Importaciones y referencias¶
El catálogo que vamos a utilizar es el "Bright Star Catalogue", el cual incluye prácticamente todas las estrellas visibles a simple vista, hasta la magnitud 6.5 o más brillantes. Aquí hay una primera descripción.
Este catálogo está disponible online aquí. Para seguir los ejercicios de esta entrada habrá que descargar de este sitio los tres ficheros de los que consta el catálogo:
- bsc5.dat El catálogo
- bsc5.readme Una descripción del catálogo
- bsc5.notes Notas del catálogo
También será util este artículo de la wikipedia dedicado a la clasificación de las estrellas por las características de su espectro.
Como de costumbre, esta entrada está escrita íntegramente con el Notabook de IPython, y se utilizarán una serie de librerías de Python que a continuación importamos:
%matplotlib inline
from __future__ import division
import quantities as pq
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
# Generar un cuadro con versiones de las librerías utilizadas en este notebook
#https://github.com/jrjohansson/version_information
%load_ext version_information
%version_information numpy, matplotlib, quantities, pandas
Lectura de los datos del "Bright Star Catalogue" (BSC)¶
Una vez se han descargado los tres ficheros que componen el BSC, Vamos a leer una serie de campos importandolos en un dataframe de Pandas. Sobre esto dos observaciones:
- La estructura de las entradas en el BSC viene detallada en el archivo bsc5.readme, junto con una somera descripción.
- No vamos a leer todos los campos, sino tan solo aquellos que vamos a utilizar más adelante.
- Al tratarse de registros con campos de ancho fijo, utilizaremos la función read_fwf() de la librería Pandas que permite proporcionar una descripción de la posición de comienzo y longitud de cada campo.
colspecs = [(0,4), (4,14), (25,31), (31,37), (41,42), (43,44), (51,60),
(90,96), (96,102), (102,107), (109,114), (115,120), (127,147)]
labels =['HR', 'Name', 'HD', 'SAO', 'IRflag', 'Multiple', 'VarID',
'GLON', 'GLAT', 'Vmag', 'B-V', 'U-B', 'SpType']
bsc = pd.read_fwf('../datos/bsc5.dat', header= None, colspecs=colspecs,
names=labels, index_col=0)
Veamos una muestra de los datos:
bsc.head()
bsc.tail()
Diagrama color-color de la secuencia principal¶
El propósito final de este post es hacer diagramas color-color de los diferentes tipos de estrellas, es decir, clases espectrales, para lo cual comenzaremos con las estrellas en la secuencia principal. Utilizaremos los índices de color del sistema UBV, al ser estos los recogidos en el BSC
Se denominan así las estrellas (las cuales constituyen aproximádamente el 80% de la población estelar) que utilizan hidrógeno como combustible en sus procesos de fusión nuclear.
Para diferenciar estas estrellas del resto nos servremos del campo de tipo espectral del BSC (la columna del dataframe con el nombre SpType). El BSC utiliza el sistema de clasificación de Morgan-Keenan (MK), el cual incluye la clasificación espectral en el esquema de clasificación de Harvard (O B A F G K M más un numeral 0-9), al cual se añade una clasificación basada en la luminosidad, que utiliza numerales romanos I-VI, a los cuales se añade en ocasiones una letra minúscula "a" o "b". Ejemplos de la clasificación resultante son: "B5III", "A5Ia", "K0V", etc. En particular, la "V" significa "main sequence", que es lo que nos va a permitir reconocer las estrellas pertenecientes a la secuencia principal.
Observación importante: téngase en cuenta que el "Bright Star Catalogue" solo incluye las estrellas más brillantes, básicamente aquellas que son visibles a simple vista, por lo que el número de estrellas de una determinada clase espectral en el catálogo no es en modo alguno representativa de su abundancia en el universo. Las estrellas menos brillantes o más lejanas no estarán representadas en el BSC.
¿Cuantos tipos espectrales existen en el catálogo?
bsc['SpType'].nunique()
¿Hay algunos tipos espectrales sin datos (marcados como NaN en Pandas)?
pd.isnull(bsc['SpType']).sum()
Hay 14. Eliminemos del dataframe estas entradas
b = pd.notnull(bsc['SpType'])
bsc = bsc[b]
bsc.info()
En el resumen anterior comprobamos que el dataframe tiene ya 14 entradas menos, y el campo SpType carece de valores no nulos.
A cpntinuación vamos a seleccionar las estrellas en la secuencia principal, creando un nuevo dataframe al cual llamaremos main(). Estas son aquellas que en la clasificación MK tienen una clase de luminosidad 'V':
b1 = bsc['SpType'].str.contains('V')
b2 = bsc['SpType'].str.contains('IV')
b3 = bsc['SpType'].str.contains('VI')
# Clases de luminosidad de tipo "V", no "IV", ni "VI" ni "VII"
b = np.logical_and(b1,np.logical_and(np.logical_not(b2),np.logical_not(b3)))
main = bsc[b]
main.shape
Hagamos una primera comprobación visual:
main[0:10]
Ahora queremos comprobar que la primera letra de estas clases espectrales está en la secuencia O, B, A, F, G, K, M
main['SpType'].map(lambda s:s[0]).unique()
Preparamos ahora un diccionario de Python con los códigos de colores que vamos a asignar a cada clase espectral, los cuales irán del azul (tipo O) al rojo (tipo M):
colors = {'O':'#0000FF', 'B':'#CCCCFF', 'A':'#FFFFFF', 'F':'#FFFFB2', 'G':'#FFFF00', 'K':'#FFA500', 'M':'#FF0000'}
Y generamos el diagrama color-color con Python Matplotlib
fig, ax = plt.subplots(figsize=(10, 10))
ax.set_axis_bgcolor('0.6')
ax.grid()
ax.set_title(u'Color-color secuencia principal catálogo BSC')
ax.title.set_fontsize(20)
ax.set_xlabel('B-V')
ax.xaxis.label.set_fontsize(20)
ax.set_ylabel('U-B')
ax.yaxis.label.set_fontsize(20)
for cls in 'OBAFGKM':
f = lambda s: s[0] == cls
b = main['SpType'].astype('string').map(f)
x = main[b]['B-V']
y = main[b]['U-B']
c = colors[cls]
ax.scatter(x, y, c = c, s=6, edgecolors='none', label = cls)
legend = ax.legend(scatterpoints=1,markerscale = 6, shadow=True)
frame = legend.get_frame()
frame.set_facecolor('0.90')
Se ve que de la clase M hay muy poquitas estrellas, ¡pero esto es porque solo estamos considerando aquellas visibles a simple vista! y son poco luminosas. De hecho son las más abundantes con diferencia.
Diagrama color-color de las estrellas gigantes¶
Previamente, vamos a ver si en el BSC existen estrellas enanas, entendiendo por tal las correspondientes a las clases de luminosidad VI y VII
b = bsc['SpType'].str.contains('VI')
bsc[b].shape
Vemos que no hay estrellas de estos tipos en el catálogo BSC, lo que era de esperar al tratarse de un catálogo de las estrellas más brillantes del cielo. Esto significa que en el campo SpType no hay valores "VI" ni VII", lo que será tenido en cuenta al hacer la selección.
Vamos pues a incorporar al gráfico las estrellas pertenecientes a clases de luminosidad de tipos gigantes:
- I Supergigantes
- II Gigantes brillantes
- III Gigantes normales
- IV Subgigantes
Vamos a ver cuantas estrellas hay de estas clases en el bsc:
b = bsc['SpType'].str.contains('I')
giants = bsc[b]
giants.shape
Y veamos a continuación si entre estas aparecen nuevas clases espectrales además de las habituales O, B, A, F, G, K, M
giants['SpType'].map(lambda s:s[0]).unique()
Vemos que en efecto aparecen nuevas clases espectrales más exóticas: W (estrellas azules y brillantes que poseen sobre todo helio en lugar de hidrógeno en sus atmósferas), y las estrellas de carbono de tipos C y S, gigantes y supergigantes rojas en fases finales de su evolución, de modo que vamos a ampliar nuestro diccionario de colores para acomodar estos nuevos tipos
colors = {'O':'#0000FF', 'B':'#CCCCFF', 'A':'#FFFFFF', 'F':'#FFFFB2', 'G':'#FFFF00', 'K':'#FFA500', 'M':'#FF0000',
'W':'#000099' ,'S':'#B80000', 'C':'#780000'}
A continuación vamos a ver cual es la incidencia de los nuevos tipos W, S y C. En el gráfico a continuación veremos que su presencia en el BSC es muy escasa, y en el caso de las estrellas de carbono su posición en un esquema color-color es ocupando posiciones muy extremas (magnitudes muy elevadas, y por lo tanto baja temperatura y color rojo intenso).
fig, ax = plt.subplots(figsize=(10, 10))
ax.set_axis_bgcolor('0.6')
ax.grid()
ax.set_title(u'Color-color secuencia principal catálogo BSC')
ax.title.set_fontsize(20)
ax.set_xlabel('B-V')
ax.xaxis.label.set_fontsize(20)
ax.set_ylabel('U-B')
ax.yaxis.label.set_fontsize(20)
for cls in 'WSC':
f = lambda s: s[0] == cls
b = giants['SpType'].astype('string').map(f)
x = giants[b]['B-V']
y = giants[b]['U-B']
c = colors[cls]
ax.scatter(x, y, c = c, s=30,
edgecolors='None', label = cls)
legend = ax.legend(scatterpoints=1,markerscale = 2, shadow=True)
frame = legend.get_frame()
frame.set_facecolor('0.90')
Dada la escasez de estrellas de las clases W, S y C en el catálogo BSC, una vez hemos visto donde se ubican en los gráficos color-color, vamos a omitir su representación, ya que su inclusión alteraria mucho la escala de los ejes. A continuación generaremos un gráfico en el que vamos a superponer las estrellas de la secuencia principal y las gigantes, con excepción de las de tipo W, S y C. Con el fin de diferenciarlas, las estrellas de la secuencia principal se van a representar ahora con puntos pequeños de color negro, mientras que las estrellas gigantes (tipos I, II, III y IV) se van a mostrar con los colores convencionales, y discos semitransparentes.
En el mismo gráfico vamos a superponer la línea recta correspondiente al diagrama color-color "ideal" que correspondería a un cuerpo negro. para ello utilizaremos las funciones definidas en el post anterior.
def B(wl,T):
'''wl es un array de longitudes de onda con unidades de longitud
T es una temperatura expresada en Kelvin
el resultado es un array de valores de la radiancia espectral
con unidades W/(m**2 * nm * sr)
'''
I = 2 * pq.constants.h * (pq.c)**2 / wl**5 * \
1 / (np.exp((pq.constants.h*pq.c \
/ (wl*pq.constants.k*T)).simplified)-1)
return I.rescale(pq.watt/(pq.m**2 * pq.nm *pq.sr))
# Definición de las constantes del sistema fotométrico UBV
lambda_u = 365 * pq.nm
delta_u = 68 * pq.nm
lambda_b = 440 * pq.nm
delta_b = 98 * pq.nm
lambda_v = 550 * pq.nm
delta_v = 89 * pq.nm
# Cálculo de Cu-b
T = 42000*pq.kelvin
F = B(lambda_u, T) * delta_u/(B(lambda_b, T)* delta_b)
Cub = -1.19 + 2.5 * np.log10(F)
# Cálculo de Cb-v
F = B(lambda_b, T) * delta_b/(B(lambda_v, T)* delta_v)
Cbv = -0.33 + 2.5 * np.log10(F)
# Funciones para calcular los índices de color del cuerpo negro
def get_UB(T):
F = B(lambda_u, T) * delta_u/(B(lambda_b, T)* delta_b)
return -2.5 * np.log10(F) + Cub
def get_BV(T):
F = B(lambda_b, T) * delta_b/(B(lambda_v, T)* delta_v)
return -2.5 * np.log10(F) + Cbv
Y generamos el diagrama:
fig, ax = plt.subplots(figsize=(10, 10))
ax.set_axis_bgcolor('0.6')
#ax.set_xlim(-1, 6)
#ax.set_ylim(-1, 2.5)
ax.grid()
ax.set_title(u'Color-color secuencia principal y gigantes catálogo BSC')
ax.title.set_fontsize(20)
ax.set_xlabel('B-V')
ax.xaxis.label.set_fontsize(20)
ax.set_ylabel('U-B')
ax.yaxis.label.set_fontsize(20)
for cls in 'OBAFGKM':
f = lambda s: s[0] == cls
b = giants['SpType'].astype('string').map(f)
x = giants[b]['B-V']
y = giants[b]['U-B']
c = colors[cls]
ax.scatter(x, y, c = c, s=50, alpha = 0.5,
edgecolors='None', label = cls)
x = main['B-V']
y = main['U-B']
ax.scatter(x, y, c = 'black', s=6, edgecolors='none',
label='Main')
legend = ax.legend(scatterpoints=1, markerscale = 2,shadow=True)
frame = legend.get_frame()
frame.set_facecolor('0.90')
T1 = 2500 * pq.kelvin
T2 = 25000 * pq.kelvin
BminusV_1 = get_BV(T1)
UminusB_1 = get_UB(T1)
BminusV_2 = get_BV(T2)
UminusB_2 = get_UB(T2)
# Basta con dos puntos para determinar la recta
ax.plot([BminusV_1, BminusV_2], [UminusB_1, UminusB_2], lw = 3, c='k')
ax.text(0.9,0.4,'blackbody', fontsize=20, rotation = 35);
La figura anterior presenta características interesantes. La primera, que la radiación emitida por las estrellas se aparta en cierta medida de la correspondiente a un cuerpo negro (recordemos que esta es una línea recta). Las estrellas no son cuerpos negros ideales. La mejor aproximación es la correspondiente a las estrellas más calientes (O y B en la figura).
No obstante, la característica más sobresaliente es la brusca desviación de las estrellas de la clase A respecto de la línea de emisión del cuerpo negro. Si nos fijamos en la secuencia principal, esta describe una curva en forma de U invertida que se separa y luego vuelve a aproximarse a la línea del cuerpo negro. Esta característica ilustra el fenómeno conocido como "Balmer jump" o "salto de Balmer".
Básicamente, cuando la temperatura superficial de la estrella alcanza un valor crítico, lo que ocurre con las estrellas de clase espectral a partir de A0, se produce una intensa absorción de los fotones emitidos por el interior de la estrella en la región del ultravioleta. Dichos fotones son absorbidos por los átomos de hidrógeno en la superficie de la estrella excitando sus electrones, lo que se traduce en la aparición de las líneas de absorción de la serie de Balmer del hidrógeno en el espectro de estas estrellas. Esto implica que, como efecto de la absorción de estos fotones, se debilita la emisión de radiación por la estrella en la región del ultravioleta, y la magnitud U-B crece (recordemos que a menor flujo radiante recibido mayor magnitud), es decir, toma valores positivos mas deprisa de lo que le correspondería a un cuerpo negro, separandose bruscamente de dicha línea como se aprecia en la figura.
A partir de la clase espectral A5 la temperatura superficial de la estrella disminuye y hay menos átomos de hidrógeno en los niveles energéticos que les permiten capturar dichos fotones. Consecuentemente la radiación en el ultravioleta se recupera y el índice de color U-B deja de crecer. La curva se aproxima de nuevo, también bruscamente, a la línea correspondiente a la radiación de un cuerpo negro.
Este fenómeno está perfectamente explicado en el libro de Keith Robinson: Starlight: An Introduction to Stellar Physics for Amateurs
Diagramas color-color de las clases de luminosidad MK¶
Para terminar este post, ya un tanto largo, realizaremos un conjunto de diagramas color-color para representar el comportamiento en estos diagramas de las distintas clases de luminosidad del esquema MK
# Creación de un dataframe con solo estrellas clase IV
bIV = giants['SpType'].str.contains('IV')
giantsIV = giants[bIV]
giantsIV.shape
# Creación de un dataframe con solo estrellas clase III
bIII = giants['SpType'].str.contains('III')
giantsIII = giants[bIII]
giantsIII.shape
# Creación de un dataframe con solo estrellas clase II
bII = giants['SpType'].str.contains('II')
b = np.logical_and(bII, np.logical_not(bIII))
giantsII = giants[b]
giantsII.shape
# Creación de un dataframe con solo estrellas clase I
bI = giants['SpType'].str.contains('I')
b = np.logical_and(bI, np.logical_not(bII))
giantsI = giants[b]
giantsI.shape
fig = plt.figure(figsize=(10,15))
ax1 = fig.add_subplot(321)
x = main['B-V']
y = main['U-B']
ax1.scatter(x, y, c = 'black', s=0.1)
ax1.set_xlim(-0.5, 2.5)
ax1.set_ylim(-1.5, 3)
ax1.set_xlabel('B-V')
ax1.set_ylabel('U-B')
ax1.grid()
ax1.set_title('Secuencia principal (MK V)');
ax2 = fig.add_subplot(322)
x = giantsIV['B-V']
y = giantsIV['U-B']
ax2.scatter(x, y, c = 'black', s=0.1)
ax2.set_xlim(-0.5, 2.5)
ax2.set_ylim(-1.5, 3)
ax2.set_xlabel('B-V')
ax2.set_ylabel('U-B')
ax2.grid()
ax2.set_title('Subgigantes (MK IV)');
ax3 = fig.add_subplot(323)
x = giantsIII['B-V']
y = giantsIII['U-B']
ax3.scatter(x, y, c = 'black', s=0.1)
ax3.set_xlim(-0.5, 2.5)
ax3.set_ylim(-1.5, 3)
ax3.set_xlabel('B-V')
ax3.set_ylabel('U-B')
ax3.grid()
ax3.set_title('Gigantes (MK III)');
ax4 = fig.add_subplot(324)
x = giantsII['B-V']
y = giantsII['U-B']
ax4.scatter(x, y, c = 'black', s=0.1)
ax4.set_xlim(-0.5, 2.5)
ax4.set_ylim(-1.5, 3)
ax4.set_xlabel('B-V')
ax4.set_ylabel('U-B')
ax4.grid()
ax4.set_title('Gigantes luminosas (MK II)');
ax5 = fig.add_subplot(325)
x = giantsI['B-V']
y = giantsI['U-B']
ax5.scatter(x, y, c = 'black', s=0.1)
ax5.set_xlim(-0.5, 2.5)
ax5.set_ylim(-1.5, 3)
ax5.set_xlabel('B-V')
ax5.set_ylabel('U-B')
ax5.grid()
ax5.set_title('Supergigantes (MK I)');
Esto es todo por ahora: ¡hasta el próximo post!