Ejercicio 1: generar diagrama de Hertzprung-Russell
Los diagramas de Hertzprung-Russell son herramientas gráficas utilizadas en astronomía, las cuales relacionan la luminosidad de una estrella con su temperatura (o bien su tipo espectral con su magnitud absoluta). Son muy útiles para el estudio de la evolución y clasificación estelar.
Para este ejercicio, se propone generar un diagrama de Hertzsprung-Russell con las escalas de los ejes, textos y etiquetas adecuadas. Para ello, se usa la información de temperatura en Kelvin, Luminosidad en Luminosidades solares y radio en unidades arbitrarias para cuatro grupos de estrellas, que se puede encontrar en sus respectivos archivos dentro de la carpeta data. Además, los tamaños y colores de las estrellas deben ser representativos.
Referencia:
(https://socratic.org/questions/what-is-the-hertzsprung-russell-diagram-and-why-is-it-so-important-to-astronomy-#277707)
Ejercicio 2: generar animación para el diagrama de Hertzprung-Russell
Con base en el diagrama del ejercicio 1 se debe generar una animación, en la cual se reproduzca el mismo gráfico de antes pero las estrellas vayan apareciendo progresivamente.
Ejercicio 1
Para hacer el diagrama se necesitan algunas librerías de Python: pandas para leer la data; matplotlib.pyplot para generar el gráfico y matplotlib.animation para generar la animación.
import pandas as pd
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
from matplotlib import rcParams
Para escribir las unidades en el gráfico, se usará letra en cursiva, la cual se obtiene de rcParams.
rcParams['mathtext.it'] = 'DejaVu Sans:italic'
rcParams['mathtext.rm'] = 'DejaVu Sans'
dwarfs = pd.read_csv('data/dwarfs.csv')
ms = pd.read_csv('data/ms.csv')
giants = pd.read_csv('data/giants.txt', sep=" ", header=0,
keep_default_na=False)
supergiants = pd.read_csv('data/supergiants.txt', sep=" ", header=0,
keep_default_na=False)
# Primero, uno la data que viene de archivos .csv
data_csv = pd.DataFrame.append(ms, dwarfs)
# Segundo, la data que viene de archivos .txt
data_txt = pd.DataFrame.append(giants, supergiants)
# Tercero, nombro la data de todas las estrellas juntas como data
data = pd.DataFrame.append(data_csv, data_txt)
Cada estrella será representada por un círculo proporcional a su tamaño, el cual tendrá un color de acuerdo a su data correspondiente. Para evitar asignar los colores uno a uno, se usa un mapa de colores y de él, se obtiene el mismo número de colores que de estrellas hay en la data. En esta variante a la referencia, se usan los colores del mapa 'coolwarm', que resultan útiles para representar temperaturas. Sin embargo, como las estrellas de mayor temperatura son azules y las de menor temperatura son rojas, se necesita el reverso de este mapa de colores, dado por coolwarm_r.
# Consulto la data para conocer el número de estrellas
data
lum | temp | radius | |
---|---|---|---|
0 | 0.000776 | 3577.003926 | 0.814703 |
1 | 0.002638 | 3691.168543 | 1.209778 |
2 | 0.006823 | 3793.506494 | 1.630027 |
3 | 0.019733 | 3862.471423 | 2.361574 |
4 | 0.040402 | 3963.530109 | 2.910924 |
... | ... | ... | ... |
0 | 359749.335156 | 3801.042587 | 278.055832 |
1 | 416869.383470 | 4398.962354 | 190.278395 |
2 | 1000000.000000 | 5465.163392 | 140.809113 |
3 | 920449.571753 | 7837.395137 | 46.187556 |
4 | 779830.110523 | 10200.701561 | 19.604244 |
106 rows × 3 columns
# Obtengo los 106 colores del mapa y les asigno un nombre.
colors = plt.get_cmap('coolwarm_r', 106)
Algunos factores a tomar en cuenta para el plot del diagrama:
a) Los colores claros de colors se perciben mejor en fondo negro.
b) El eje x representa la temperatura en Kelvin ('temp' en data).
c) El eje y representa la luminosidad en Luminosidades solares ('lum').
d) El marcador marker para representar un punto en la gráfica debe ser un círculo cuyo tamaño s=size viene dado por el radio ('radius') y de color proporcional a 'temp', proveniente de colors.
e) La data debe estar etiquetada en el gráfico, de acuerdo a la clasificación de sus estrellas.
f) El eje x debe estar invertido, la temperatura decrece hacia la derecha.
g) Las escalas de los ejes son logarítmicas.
# Creando la figura considerando a
plt.style.use('dark_background')
fig = plt.figure(figsize=(10,5))
ax = fig.add_subplot(111)
ax.set_title('\nHomework 2 - María Ramos \nHertzsprung - Russell Diagram\n',
fontsize=25)
# Considerando b, c, d
ax.scatter(x=data['temp'], y=data['lum'], marker='o', s=data['radius'],
c=data['temp'], cmap=colors, alpha=1)
# Etiquetando de acuerdo b, c
plt.xlabel(r'Temperature $\mathit{(K)}$', fontsize=18)
plt.ylabel(r'Luminosity $\mathit{(L_{\odot})}$', fontsize=18)
# Etiquetando las estrellas según e. La ubicación viene dada por algún punto
# de su data más un valor arbitrario para mejorar la estética.
label_dwarfs = ('White dwarfs')
plt.annotate(label_dwarfs, (dwarfs['temp'][2]+150, dwarfs['lum'][2]+0.0004),
fontsize=15)
label_ms = ('Main Sequence')
plt.annotate(label_ms, (ms['temp'][75], ms['lum'][75]),
fontsize=15)
label_giants = ('Giants')
plt.annotate(label_giants, (giants['temp'][3], giants['lum'][3]), fontsize=15)
label_supergiants= ('Super Giants')
plt.annotate(label_supergiants, (supergiants['temp'][2],
supergiants['lum'][2]-950000), fontsize=15)
# Redefiniendo los ejes por f, g
plt.gca().invert_xaxis()
plt.xscale('log')
plt.yscale('log')
# Para mostrar el plot
plt.show()
fig.savefig('HR_Diagram.png')
Ejercicio 2
Para generar la animación se ha importado FuncAnimation; se repite el código del diagrama pero asignando size y opacity como variables, y al final se anexará la función update para usar en la animación. En esta variante, para que las estrellas aparezcan, se cambiarán los parámetros s y alpha del comando scatter por cada frame creado. Alpha es un float entre 0 y 1, donde 0 es transparente (no se ven los marcadores) y 1 es opaco (se ven los marcadores). Note que el valor de size y opacity en la función update depende del número de frames y que en size se mantiene la dependencia con el radio.
# Repitiendo el código para generar el diagrama original
plt.style.use('dark_background')
# Excepción: redimensionar la figura porque en el primer gif
# que obtuve no aparecía el título.
fig = plt.figure(figsize=(20,12))
ax = fig.add_subplot(111)
# Se asignan size y opacity iniciales
size = 0
opacity = 0
# Se define opacidad 0 en un comienzo para no visualizar estrellas
ax.set_title('\nHomework 2 - María Ramos \nHertzsprung - Russell Diagram\n',
fontsize=25)
star = ax.scatter(x=data['temp'], y=data['lum'], marker='o', s=size,
c=data['temp'], cmap=colors, alpha=opacity)
plt.xlabel(r'Temperature $\mathit{(K)}$', fontsize=22)
plt.ylabel(r'Luminosity $\mathit{(L_{\odot})}$', fontsize=22)
label_dwarfs = ('White dwarfs')
plt.annotate(label_dwarfs, (dwarfs['temp'][2]+150, dwarfs['lum'][2]+0.0004),
fontsize=20)
label_ms = ('Main Sequence')
plt.annotate(label_ms, (ms['temp'][75], ms['lum'][75]), fontsize=20)
label_giants = ('Giants')
plt.annotate(label_giants, (giants['temp'][3], giants['lum'][3]),
fontsize=20)
label_supergiants= ('Super Giants')
plt.annotate(label_supergiants, (supergiants['temp'][2],
supergiants['lum'][2]-950000), fontsize=20)
plt.gca().invert_xaxis()
plt.xscale('log')
plt.yscale('log')
# Se crea la función update que cambia opacidad y size por cada frame
def update(frame_number):
# Para que las estrellas aparezcan gradualmente
if frame_number <= 100:
opacity = 0.01*frame_number
star.set_alpha(opacity)
# El tamaño es mayor para compensar el cambio de dimensiones
size = 0.1*frame_number*data['radius']
star.set_sizes(size)
# Para agregar un efecto de titilar
elif frame_number % 2 == 0:
star.set_alpha(0.8)
else:
star.set_alpha(1)
return
# Se genera la animación
anim = FuncAnimation(fig, update, frames=150, interval=200)
plt.close()
anim.save('HRD_animation.gif')
MovieWriter ffmpeg unavailable; using Pillow instead.