Astrofísica Extragaláctica - Actividad Guiada I
Jorge Martínez Garrido
September 3, 2023
El presente informe contiene las soluciones a los ejercicios propuestos en la Actividad Guiada I de la asignatura “Astrofísica Extragaláctica”.
El informe presentan dos casos prácticos relacionados con la astronomía. En el primero, se utiliza el Telescopio Espacial Hubble para calcular la distancia a la galaxia M100 mediante el método de las Cefeidas, acompañado de la creación de un diagrama que representa la relación entre el período y la luminosidad de estas estrellas variables. En el segundo caso, se emplea la herramienta Aladin para determinar la distancia a la galaxia M31 y se compara esta medición con datos previamente publicados en artículos científicos de Astronomía y Astrofísica, nuevamente generando un diagrama Periodo-Luminosidad. Finalmente, se destaca que, a partir de las distancias calculadas a estas galaxias, se obtiene una estimación de la constante de Hubble y se aproxima la edad del Universo, proporcionando una perspectiva valiosa sobre la medición de distancias cósmicas y la comprensión del universo en su conjunto.
Cálculo de la distancia a la galaxia M100
Para resolver los ejercicios se han utilizado los datos del Hubble Space Telescope (HST). Dichos datos se encuentran comprimidos, por lo que es necesario descomprimirlos para su posterior procesado:
!unzip -u data/HST.zip -d data/ >/dev/null 2>&1
Representación de la curva de luz de cada estrella Cefeida
Como resultado de la extracción previa, se han generado varios archivos TXT que almacenan datos cruciales. Estos archivos presentan una estructura de dos columnas: la primera columna registra el tiempo en días, mientras que la segunda columna proporciona la magnitud de la estrella en estudio.
A partir de estos datos, es posible construir y representar gráficamente la curva de luz de cada estrella. Esta representación visual permite una observación más detallada de las fluctuaciones en la magnitud de las estrellas a lo largo del tiempo, dado que se trata de estrellas Cefeidas, conocidas por sus variaciones características.
import matplotlib.pyplot as plt
import pandas as pd
from pathlib import Path
data = Path('data')
stars_df_list = [
pd.read_csv(file, sep='\t', header=None, names=['Time', 'Magnitude'])
for file in data.glob("*.txt")
]
fig, axes = plt.subplots(3, 4, figsize=(15, 10))
fig.tight_layout(pad=3.0)
for i, df in enumerate(stars_df_list):
row = i // 4
col = i % 4
ax = axes[row, col]
# Plot the data
ax.plot(df['Time'], df['Magnitude'], "r-*", label=f'Star {i+1}')
ax.invert_yaxis() # Invert the Y-axis
ax.set_title(f'Star {i+1}')
ax.set_xlabel('Time (days)')
ax.set_ylabel('Magnitude (mag)')
ax.grid(True)
ax.legend()
fig.suptitle('Figura 1 - Evolución de la magnitud en el tiempo para estrellas cefeidas')
plt.subplots_adjust(top=0.90)
plt.show()
Cálculo del período de oscilación
El período de oscilación de cada estrella se puede calcular como la diferencia de tiempo entre dos mínimos consecutivos en la magnitud.
import pandas as pd
def find_period_from_mag_and_time_df(df):
min_magnitudes = df.nsmallest(2, 'Magnitude')
min_times = min_magnitudes["Time"].values
return abs(min_times[0] - min_times[1])
stars_periods = [find_period_from_mag_and_time_df(df) for df in stars_df_list]
stars_periods_df = pd.DataFrame({'Cefeida': list(range(1, len(stars_periods) + 1)),
'Periodo (dias)': list(map(int, stars_periods))})
print("Tabla 1 - Período de oscilación de cada estrella")
stars_periods_df
Tabla 1 - Período de oscilación de cada estrella
Cefeida | Periodo (dias) | |
---|---|---|
0 | 1 | 52 |
1 | 2 | 24 |
2 | 3 | 26 |
3 | 4 | 25 |
4 | 5 | 39 |
5 | 6 | 42 |
6 | 7 | 31 |
7 | 8 | 26 |
8 | 9 | 34 |
9 | 10 | 21 |
10 | 11 | 44 |
11 | 12 | 30 |
Cálculo de la magnitud abosluta
Conocido el período de oscilación, es posible utilizar la siguiente relación de Feast y Catchpole para calcular la magnitud absoluta $M_V$ de una Cefeida:
$$ M_V = -1.43 - 2.81 \cdot \log{(P)} $$
donde $P$ es el período en dias.
import numpy as np
def find_mag_absolute_from_period(period):
return -1.43 - 2.81 * np.log10(period)
stars_absolute_magnitudes = [find_mag_absolute_from_period(P) for P in stars_periods]
stars_absolute_magnitudes_df = pd.DataFrame(
{
'Cefeida': list(range(1, len(stars_absolute_magnitudes) + 1)),
'Mv (mag)': list(np.round(stars_absolute_magnitudes, 2))
}
)
print("Tabla 2 - Magnitud absoluta de cada estrella")
stars_absolute_magnitudes_df
Tabla 2 - Magnitud absoluta de cada estrella
Cefeida | Mv (mag) | |
---|---|---|
0 | 1 | -6.27 |
1 | 2 | -5.32 |
2 | 3 | -5.43 |
3 | 4 | -5.38 |
4 | 5 | -5.90 |
5 | 6 | -6.00 |
6 | 7 | -5.62 |
7 | 8 | -5.42 |
8 | 9 | -5.76 |
9 | 10 | -5.19 |
10 | 11 | -6.05 |
11 | 12 | -5.59 |
Cálculo de la magnitud aparente
Es posible calcular la magnitud aparente $m$ a partir de los datos de las curvas de luz. Para ello, se aplica el procedimiento estándar originado en el siglo XX. Se obtienen los valores mínimo y máximo de la magnitud, y luego se calcula la media de estos valores. Ese resultado se considera como el valor válido de la magnitud aparente.
def find_mag_apparent_from_df(df):
min_mag = df['Magnitude'].min()
max_mag = df['Magnitude'].max()
return (min_mag + max_mag) / 2
stars_apparent_magnitude = [find_mag_apparent_from_df(df) for df in stars_df_list]
stars_apparent_magnitude_df = pd.DataFrame(
{
'Cefeida': list(range(1, len(stars_apparent_magnitude) + 1)),
'm (mag)': list(np.round(stars_apparent_magnitude, 2))
}
)
print("Tabla 3 - Magnitud aparente de cada estrella")
stars_apparent_magnitude_df
Tabla 3 - Magnitud aparente de cada estrella
Cefeida | m (mag) | |
---|---|---|
0 | 1 | 24.87 |
1 | 2 | 26.21 |
2 | 3 | 25.67 |
3 | 4 | 25.42 |
4 | 5 | 25.39 |
5 | 6 | 25.64 |
6 | 7 | 26.38 |
7 | 8 | 26.36 |
8 | 9 | 26.33 |
9 | 10 | 26.23 |
10 | 11 | 25.26 |
11 | 12 | 26.44 |
Cálculo de la distancia de cada estrella a la Tierra
Una vez que tenemos conocida tanto la magnitud absoluta (M) como la magnitud aparente (m) de una estrella, podemos aplicar la siguiente relación:
$$ m - M = -5 + 5 \cdot \log{(D)} $$
donde $D$ representa la distancia a la estrella. Esta fórmula nos permite determinar la distancia a la estrella en función de sus magnitudes. Para obtener la distancia de manera explícita, podemos reescribir la fórmula de la siguiente manera:
$$ D=10^{\left(1 + \frac{m−M}{5}\right)} $$
Esta expresión nos brinda una forma más clara y directa de calcular la distancia a una estrella en función de las magnitudes observadas.
from astropy import units as u
def find_distance_from_mag(m, M):
return (10 ** (1 + (m - M) / 5)) * u.pc
stars_distances = [
find_distance_from_mag(m, M)
for m, M in zip(stars_apparent_magnitude, stars_absolute_magnitudes)
]
stars_distances_df = pd.DataFrame(
{
'Cefeida': list(range(1, len(stars_distances) + 1)),
'Distancia (Mpc)': [
np.round(d.to_value(u.Mpc), 2)
for d in stars_distances]
}
)
print("Tabla 4 - Distancia de cada estrella a la Tierra en Mpc")
stars_distances_df
Tabla 4 - Distancia de cada estrella a la Tierra en Mpc
Cefeida | Distancia (Mpc) | |
---|---|---|
0 | 1 | 16.93 |
1 | 2 | 20.25 |
2 | 3 | 16.62 |
3 | 4 | 14.46 |
4 | 5 | 18.15 |
5 | 6 | 21.35 |
6 | 7 | 25.09 |
7 | 8 | 22.68 |
8 | 9 | 26.14 |
9 | 10 | 19.25 |
10 | 11 | 18.25 |
11 | 12 | 25.43 |
Cálculo de la distancia media a M100 a la Tierra
Para obtener la distancia media de M100 a la Tierra, se puede utilizar el valor medio de la distancia de la Tierra con las estrellas estudiadas:
m100_distance = np.mean([d.to_value(u.Mpc) for d in stars_distances]) * u.Mpc
print(f"Distancia media a M100 = {m100_distance:.1f}")
Distancia media a M100 = 20.4 Mpc
Comparacion de la distancia obtenida con los resultados oficiales
El valor de la distancia obtenido por Freedman es de $17.1\pm 1.8$ megaparsecs. Por otro lado, el valor obtenido en este informe es de $20.4$ megaparsecs.
Si bien, el valor calculado se encuentra fuera del margen de error de Freedman, es importante incurrir en el hecho de que no se ha considerado el polvo estelar. Las mediciones astronómicas pueden variar debido a diferentes técnicas y enfoques utilizados en diferentes estudios. Además, la distancia a galaxias distantes como M100 puede ser complicada de determinar con precisión debido a varios factores, como el efecto de la expansión del universo y la presencia de materia interestelar (como el polvo), que puede afectar las mediciones de distancia.
Cálculo de la distancia a Andrómeda (M31)
Para calcular la distancia a M31, hemos empleado los datos proporcionados por Joshi, específicamente los incluidos en la tabla 4, que se pueden consultar en https://cdsarc.cds.unistra.fr/viz-bin/nph-Cat/txt?J/A+A/402/113/table4.dat.
Dicha tabla incluye valores de posición, magnitud, y período, entre otros, para un total de 26 estrellas Cefeidas. Siguiendo los mismos pasos del ejercicio anterior, hemos calculado la distancia a M31.
Lectura de los datos
Para poder procesar los datos de la tabla, se utiliza la siguiente rutina escrita en Python:
file_path = 'data/joshi.dat'
joshi_data = pd.read_csv(
file_path, delimiter='|', skipinitialspace=True,
header=0, na_values=['***', '---']
)
joshi_data.rename(columns=lambda x: x.strip(), inplace=True)
joshi_data = joshi_data.iloc[1:]
joshi_data.reset_index(drop=True, inplace=True)
Cálculo de la magnitud abosluta y relativa
Aplicamos la relación de Joshi para obtener la magnitud absoluta en la banda I, conocida como $M_I$:
$$ M_I = -1.94 - 2.96 \cdot \log(P) $$
Teniendo en cuenta que los datos incluyen el error de observacion, aplicamos la propagacion de errores:
$$ \Delta M_I = \sqrt{\left(\frac{-2.96}{P} \right)^2 \cdot (\Delta P)^2} $$
Para la magnitud relativa en esta banda, utilizaremos los datos de la columna Icmag
.
def find_mag_absolute_from_period(period):
return -1.97 - 2.81 * np.log10(period)
def find_mag_absolute_err_from_period_err(period, error):
return np.sqrt((-2.96 / period) ** 2 * error ** 2)
stars_names = [f"V{ith_star}" for ith_star in range(1, len(joshi_data["ID"]) + 1)]
stars_absolute_magnitudes = [
find_mag_absolute_from_period(P)
for P in joshi_data["Per"].astype(float)
]
stars_absolute_magnitudes_err = [
find_mag_absolute_err_from_period_err(float(P), float(error))
for P, error in zip(joshi_data["Per"], joshi_data["e_Per"])
]
stars_apparent_magnitude = joshi_data["Icmag"].astype(float)
stars_apparent_magnitude_df = pd.DataFrame({
"Estrella": stars_names,
"M_I": np.round(stars_absolute_magnitudes, 3),
"ΔM_I": np.round(stars_absolute_magnitudes_err, 3),
"m_I": stars_apparent_magnitude,
})
print("Tabla 5 - Magnitudes de cada estrella")
stars_apparent_magnitude_df
Tabla 5 - Magnitudes de cada estrella
Estrella | M_I | ΔM_I | m_I | |
---|---|---|---|---|
0 | V1 | -4.422 | 0.001 | 19.98 |
1 | V2 | -4.591 | 0.001 | 19.69 |
2 | V3 | -4.629 | 0.001 | 20.28 |
3 | V4 | -4.673 | 0.003 | 19.54 |
4 | V5 | -4.754 | 0.002 | 20.04 |
5 | V6 | -4.826 | 0.003 | 19.76 |
6 | V7 | -4.840 | 0.001 | 20.27 |
7 | V8 | -4.915 | 0.003 | 19.55 |
8 | V9 | -5.171 | 0.001 | 19.60 |
9 | V10 | -5.227 | 0.001 | 19.84 |
10 | V11 | -5.296 | 0.002 | 18.87 |
11 | V12 | -5.312 | 0.002 | 20.08 |
12 | V13 | -5.335 | 0.002 | 19.46 |
13 | V14 | -5.346 | 0.002 | 19.58 |
14 | V15 | -5.350 | 0.002 | 19.91 |
15 | V16 | -5.382 | 0.004 | 19.74 |
16 | V17 | -5.399 | 0.002 | 19.60 |
17 | V18 | -5.479 | 0.002 | 19.09 |
18 | V19 | -5.486 | 0.005 | 19.60 |
19 | V20 | -5.631 | 0.001 | 18.99 |
20 | V21 | -5.711 | 0.003 | 19.31 |
21 | V22 | -5.992 | 0.004 | 19.19 |
22 | V23 | -6.070 | 0.002 | 18.92 |
23 | V24 | -6.313 | 0.004 | 19.57 |
24 | V25 | -6.575 | 0.005 | 18.35 |
25 | V26 | -6.883 | 0.004 | 18.82 |
Cálculo de la distancia de cada estrella a la Tierra
Aplicamos la formula $m - M = -5 + 5 \cdot \log{(D)}$ para calcular la distancia a M31. Dado que esta formula devuelve el valor en parsecs, se aplica la conversión a millones de años luz para la distancia:
stars_distances = [
find_distance_from_mag(m, M)
for m, M in zip(stars_apparent_magnitude, stars_absolute_magnitudes)
]
Generamos el histograma:
fig, ax = plt.subplots()
ax.hist(
[d.to_value(u.Mlyr) for d in stars_distances],
bins=10, color='red', edgecolor='black', alpha=0.7
)
ax.set_xlabel('Distancia (millones de años luz)')
ax.set_ylabel('Frecuencia')
ax.set_title('Figura 2 - Histograma de distancias')
plt.show()
El histograma de la figura 2 es fundamental para conocer la distribución de los datos, ya que no siguen una distribución normal. Esto puede deberse a los factores citados por Joshi en su artículo:
Podría haber una incertidumbre adicional en la determinación de la distancia debido al efecto de mezcla en las Ceféidas, así como a la extinción variable dentro de la región observada.
Por ello, eliminamos los valores más extremos y aplicamos la media al nuevo espacio muestral:
trim_size = int(0.5 * len(stars_distances))
m31_distance = np.mean(
[
d.to_value(u.Mlyr)
for d in sorted(stars_distances)[0:-trim_size]
]
) * u.Mlyr
print(f"Distancia reducida media a M31 = {m31_distance:.1f}")
Distancia reducida media a M31 = 2.7 Mlyr
Cálculo de la curva de luz para las estrellas estudiadas de M31
Es posible representar la curva de luz de las estrellas utilizando las columnas del período y de la magnitud aparente para la banda infrarroja:
from matplotlib.ticker import MultipleLocator
fig, ax = plt.subplots()
periods=np.log10(joshi_data["Per"].astype(float))
magnitudes=joshi_data["Icmag"].astype(float)
ax.scatter(periods, magnitudes, marker="o", color="r", s=25)
for i in range(len(stars_names)):
plt.annotate(
stars_names[i], (periods[i], magnitudes[i]),
textcoords="offset points", xytext=(0, 10), ha='center'
)
ax.set_title("Figura 3 - Magnitud aparente en el filtro infrarrojo en función del periodo")
ax.set_xlabel("log10(P) (days)")
ax.set_ylabel("I (mag)")
ax.set_xticks([1.0, 1.2, 1.4, 1.6])
ax.set_yticks([21, 20, 19, 18])
ax.xaxis.set_minor_locator(MultipleLocator(0.20))
ax.yaxis.set_minor_locator(MultipleLocator(0.20))
ax.invert_yaxis()
Estimando $H_0$ y la edad $t$ del Universo
Una vez conocidas la distancia a las galaxias M31 y M100, es posible utilizar este dato para calcular la constante de Hubble $H_0$ y la edad del Universo $t$.
Cálculo de $H_0$
Es posible calcular la constante de Hubble conocidas la distancia y la velocidad de expansión de una galaxia. La relación es la siguiente:
$$ H_0 = \frac{v}{D} $$
siendo $v$ la velocidad de expansión y $D$ la distancia a la que se encuentra la galaxia.
Para obtener la velocidad de expansión, se han tomado los datos proporcionados por https://ned.ipac.caltech.edu.
data = pd.DataFrame({
"Name": ["M31", "M100"],
"Distance": [np.round(m31_distance, 2), np.round(m100_distance, 2)],
"Velocity": [297 * u.km / u.s, 1570 * u.km / u.s],
"DeltaV": [23 * u.km / u.s, 23 * u.km / u.s],
})
data
Name | Distance | Velocity | DeltaV | |
---|---|---|---|---|
0 | M31 | 2.7 Mlyr | 297.0 km / s | 23.0 km / s |
1 | M100 | 20.38 Mpc | 1570.0 km / s | 23.0 km / s |
data["H0"] = [np.round(value) for value in data["Velocity"] / data["Distance"]]
data
Name | Distance | Velocity | DeltaV | H0 | |
---|---|---|---|---|---|
0 | M31 | 2.7 Mlyr | 297.0 km / s | 23.0 km / s | 110.0 km / (Mlyr s) |
1 | M100 | 20.38 Mpc | 1570.0 km / s | 23.0 km / s | 77.0 km / (Mpc s) |
Calculando la media para la constante de Hubble, se encuentra:
H0 = data["H0"].mean()
print(f"Constante de Hubble {H0.to(u.km / u.Mpc / u.s):.3f}")
Constante de Hubble 217.886 km / (Mpc s)
Cálculo de $t$
La edad del Universo puede calcularse a través de la relación:
$$ t = \frac{1}{H_0} $$
Aplicando dicha relación se obtiene:
data["t"] = [np.round((1 / H0).to(u.Myr), 2) for H0 in data["H0"]]
data
Name | Distance | Velocity | DeltaV | H0 | t | |
---|---|---|---|---|---|---|
0 | M31 | 2.7 Mlyr | 297.0 km / s | 23.0 km / s | 110.0 km / (Mlyr s) | 2725.39 Myr |
1 | M100 | 20.38 Mpc | 1570.0 km / s | 23.0 km / s | 77.0 km / (Mpc s) | 12698.6 Myr |
Calculando la media para la edad, es encuentra:
universe_age = data["t"].mean()
print(f"Edad media del universo {universe_age.to_value(u.Myr):.0f} millones de años")
Edad media del universo 7712 millones de años
Conocida la edad de la Tierra, es posible calcular cúan mayor es el Universo:
earth_age = 4543 * u.Mlyr
delta_age = (universe_age / earth_age).value
print(f"La edad del Universo es {delta_age:.2f} veces mayor que la edad de la Tierra")
La edad del Universo es 1.70 veces mayor que la edad de la Tierra
Es importante indicar que los datos anteriores son escasos para obtener resultados precisos. Sin embargo, el proceso demuestra que es posible medir la edad del universo a partir de la distancia obtenida gracias al estudio de las Cefeidas y la velocidad de expansión de la galaxia en la que se encuentran.
Conclusión
El informe nos proporciona un método eficiente para estimar la edad del Universo mediante el estudio de las Cefeidas. Estas estrellas exhiben una variación periódica en su brillo, lo que nos permite calcular con precisión su distancia. Esta distancia se emplea posteriormente para determinar la distancia promedio a las galaxias en las que residen estas estrellas. Al medir la distancia a múltiples galaxias y considerar su velocidad de expansión, podemos calcular la constante de Hubble y, en última instancia, deducir la edad del Universo.