星系外场的MOS光谱#

用例: 对一维光谱进行发射线测量和模板匹配。

数据: CEERS NIRSpec观测数据

工具: specutils, astropy, matplotlib, jdaviz。

跨仪器:

文档: 本笔记本是STScI更大后处理数据分析工具生态系统的一部分。

介绍#

在本笔记本中,我们将检查一组光谱,并对一个示例光谱进行一系列光谱分析,包括连续光谱拟合和减去、谱线识别、质心测量和通量测量、高斯拟合、等效宽度和模板拟合。我们将使用交互式的jdaviz包和命令行来完成这些工作。我们将使用来自CEERS计划的JWST/NIRSpec光谱数据集。

笔记本的目标#

本笔记本的目的是展示如何使用可视化工具Jdaviz或结合Specutils+Matplotlib来测量NIRSpec光谱中OII发射线的特性。

工作流程#

  • 在Mosviz中可视化光谱数据集

  • 选择一个星系(s02904)并在Specviz2d中可视化

  • 使用Specviz2d中的光谱提取插件对亮伴星进行一维提取

  • 将波长轴附加到提取的一维光谱上

  • 选择OII发射线并进行测量

    • 源的红移

    • 发射线的特性

  • 拟合一个包含连续光谱+高斯的模型到OII发射线

  • 使用Specutils和Matplotlib而不是Jdaviz执行相同的任务

  • 找到观察光谱的最佳拟合模板

系统要求#

首先,我们创建一个包含所有所需光谱包的jdaviz环境。

conda create -n jdaviz python

conda activate jdaviz

pip install jdaviz

导入#

# general os
import zipfile  # 导入zipfile模块,用于处理ZIP文件
import urllib.request  # 导入urllib.request模块,用于处理URL请求
from pathlib import Path  # 从pathlib导入Path类,用于处理文件路径

# general plotting
from matplotlib import pyplot as plt  # 从matplotlib导入pyplot模块,用于绘图

# table/math handling
import numpy as np  # 导入numpy库,用于数值计算

# astropy
import astropy  # 导入astropy库,天文学相关工具
import astropy.units as u  # 导入astropy.units模块,用于单位处理
from astropy.io import fits, ascii  # 从astropy.io导入fits和ascii模块,用于读写数据
from astropy.nddata import StdDevUncertainty  # 从astropy.nddata导入StdDevUncertainty类,用于处理不确定性
from astropy.modeling import models  # 从astropy.modeling导入models模块,用于模型处理
from astropy.visualization import quantity_support  # 从astropy.visualization导入quantity_support,用于支持单位可视化

# specutils
import specutils  # 导入specutils库,用于光谱数据处理
from specutils import Spectrum1D, SpectralRegion  # 从specutils导入Spectrum1D和SpectralRegion类
from specutils.fitting import fit_generic_continuum  # 从specutils.fitting导入fit_generic_continuum函数,用于拟合连续谱
from specutils.fitting import find_lines_threshold  # 从specutils.fitting导入find_lines_threshold函数,用于查找谱线
from specutils.fitting import fit_lines  # 从specutils.fitting导入fit_lines函数,用于拟合谱线
from specutils.manipulation import extract_region  # 从specutils.manipulation导入extract_region函数,用于提取光谱区域
from specutils.analysis import centroid  # 从specutils.analysis导入centroid函数,用于计算质心
from specutils.analysis import line_flux  # 从specutils.analysis导入line_flux函数,用于计算谱线通量
from specutils.analysis import equivalent_width  # 从specutils.analysis导入equivalent_width函数,用于计算等效宽度
from specutils.analysis import template_comparison  # 从specutils.analysis导入template_comparison函数,用于模板比较

# jdaviz
import jdaviz  # 导入jdaviz库,用于数据可视化
from jdaviz import Mosviz, Specviz2d, Specviz  # 从jdaviz导入Mosviz、Specviz2d和Specviz类,用于不同的可视化工具

# glue
from glue.core.roi import XRangeROI  # 从glue.core.roi导入XRangeROI类,用于定义X轴范围的感兴趣区域

np.seterr(all='ignore')  # 设置numpy忽略所有错误,隐藏与除零相关的警告
quantity_support()  # 启用单位支持,使matplotlib自动识别单位

# Matplotlib parameters
params = {'legend.fontsize': '18',  # 设置图例字体大小
          'axes.labelsize': '18',  # 设置坐标轴标签字体大小
          'axes.titlesize': '18',  # 设置坐标轴标题字体大小
          'xtick.labelsize': '18',  # 设置X轴刻度标签字体大小
          'ytick.labelsize': '18',  # 设置Y轴刻度标签字体大小
          'lines.linewidth': 2,  # 设置线条宽度
          'axes.linewidth': 2,  # 设置坐标轴线宽度
          'animation.html': 'html5'}  # 设置动画输出格式为html5
plt.rcParams.update(params)  # 更新matplotlib的参数设置
plt.rcParams.update({'figure.max_open_warning': 0})  # 禁用最大打开图形的警告

检查版本。最新的工作环境是:#

Numpy: 2.1.0

Astropy: 6.1.2

Specutils: 1.16.0

Jdaviz: 3.10.3

# 打印Numpy库的版本
print("Numpy: ", np.__version__)

# 打印Astropy库的版本
print("Astropy: ", astropy.__version__)

# 打印Specutils库的版本
print("Specutils: ", specutils.__version__)

# 打印Jdaviz库的版本
print("Jdaviz: ", jdaviz.__version__)

设置数据路径并从链接下载#

# 从旧版本的笔记本下载数据。它们包含拟合的模板

boxlink = 'https://data.science.stsci.edu/redirect/JWST/jwst-data_analysis_tools/mos_spectroscopy/mos_spectroscopy.zip'  # 数据链接

boxfile = Path('./mos_spectroscopy.zip')  # 定义下载文件的路径

urllib.request.urlretrieve(boxlink, boxfile)  # 下载文件并保存到指定路径

zf = zipfile.ZipFile(boxfile, 'r')  # 打开下载的zip文件

zf.extractall()  # 解压缩zip文件中的所有内容
# 下载s2d和x1d文件

boxlink_ceers = 'https://data.science.stsci.edu/redirect/JWST/jwst-data_analysis_tools/mos_spectroscopy/mos_ceers_data.zip'  # 数据文件的链接

boxfile_ceers = Path('./mos_ceers_data.zip')  # 本地保存的文件路径

urllib.request.urlretrieve(boxlink_ceers, boxfile_ceers)  # 从链接下载文件并保存到本地

zf = zipfile.ZipFile(boxfile_ceers, 'r')  # 打开下载的zip文件

zf.extractall()  # 解压缩zip文件中的所有内容

开发者注释

JWST 数据可以直接从 MAST 下载,但观测数据被组织在不同的文件夹中,而 Mosviz 希望将数据放在一个文件夹中。因此,我们选择了盒子下载。

# pathtodata = Path('/home/shared/preloaded-fits/jdaviz_data/mos_ceers_data')  # 原始数据路径(已注释)

pathtodata = Path('./data')  # 设置数据路径为当前目录下的data文件夹

Mosviz中的开放数据#

在Mosviz中,我们可以探索文件夹中的所有数据产品。这个单元运行需要一到两分钟。由于我们不包含图像,我们可以将2D/1D光谱查看器扩展到使用GUI的全宽度。我们还可以保持插件托盘打开在元数据上,以检查我们正在查看的文件的具体信息。

Mosviz,包含其二维光谱、单维光谱和表格查看器。

选择一个星系#

我们选择 s02904,因为我们想提取目标下方的非常明亮的伴星。

# 定义第一个文件的路径,包含JWST NIRSpec一维数据
file1d = pathtodata / 'jw01345-o064_s02904_nirspec_f100lp-g140m_x1d.fits'

# 定义第二个文件的路径,包含JWST NIRSpec二维数据
file2d = pathtodata / 'jw01345-o064_s02904_nirspec_f100lp-g140m_s2d.fits'

使用Specviz2d提取更好的1D光谱#

specviz2d = Specviz2d()  # 创建一个Specviz2d对象,用于处理和可视化光谱数据

specviz2d.load_data(file2d)  # 加载二维光谱数据文件

specviz2d.show()  # 显示加载的光谱数据

开发者备注

有没有办法从提取中获得不确定性数组?

这个问题正在处理之中。

我们打开光谱提取插件,并选择适当的轨迹(多项式,3阶,在像素2),背景(手动,在像素8,宽度2,统计量平均),以及提取(来自插件,Horne)。然后我们点击提取,并在1D查看器中检查提取的光谱。

Specviz2d光谱提取插件打开,显示轨迹参数。 Specviz2d光谱提取插件打开,显示背景参数。 Specviz2d光谱提取插件打开,显示提取参数。
# 从Specviz2d中提取光谱数据

spectra = specviz2d.get_data('Spectrum 1D')  # 获取名为'Spectrum 1D'的光谱数据

spectra  # 输出提取的光谱数据
# 包含一些假设的不确定性,目前使用的是一个小的常数值

spec1d = Spectrum1D(spectral_axis=spectra.spectral_axis,  # 创建一维光谱对象,光谱轴来自输入数据
                    flux=spectra.flux,  # 光谱的流量数据
                    uncertainty=StdDevUncertainty((np.zeros(len(spectra.flux)) + 1E-13) * spectra.unit))  # 创建不确定性对象,使用一个小的常数值作为不确定性

spec1d  # 返回创建的光谱对象
# 导入Specviz库并初始化
specviz = Specviz()  # 创建Specviz实例

# 加载一维光谱数据,并为其指定标签
specviz.load_data(spec1d, data_label='spec1d calibrated')  # 加载经过校准的一维光谱数据

# 显示Specviz界面
specviz.show()  # 展示Specviz窗口

数据中仍然存在一些伪影,但我们可以选择一个子集,屏蔽这些伪影,从而获得没有不必要尖峰的光谱。我们可以使用工具选择子集,选择顶部工具栏中的“添加”选项,以将多个区域作为单个子集进行选择。

Specviz展示如何选择子集并选择添加选项。
# 如果尚未手动创建感兴趣区域,则创建一个子集

try:
    # 获取标记为'spec1d calibrated'的数据,并选择名为'Subset 1'的光谱子集
    region1 = specviz.get_data(data_label='spec1d calibrated', spectral_subset='Subset 1')

    print(region1)  # 打印获取的子集信息

    region1_exists = True  # 标记子集存在

except Exception:
    print("There are no subsets selected.")  # 如果没有选择子集,打印提示信息
    region1_exists = False  # 标记子集不存在

# 如果子集不存在,则为掩蔽[OII]附近的伪影定义光谱区域
if not region1_exists:
    sv = specviz.app.get_viewer('spectrum-viewer')  # 获取光谱查看器

    sv.toolbar_active_subset.selected = []  # 清空当前选中的子集

    sv.apply_roi(XRangeROI(1., 1.27))  # 应用感兴趣区域,范围为1到1.27
# 获取带掩膜的光谱

spec1d_region = specviz.get_spectral_regions()  # 从specviz中获取光谱区域

spec1d_masked = extract_region(spec1d, spec1d_region['Subset 1'], return_single_spectrum=True)  # 提取指定区域的光谱并返回单一光谱
# 导入specviz库以加载光谱数据
specviz.load_data(spec1d_masked, data_label='spec1d masked')  # 加载被掩蔽的1D光谱数据,并设置数据标签为'spec1d masked'
# 将提取的光谱写入一个fits文件

file_extracted = Path('./extracted_spectrum.fits')  # 定义输出文件路径

spec1d_masked.write(file_extracted, overwrite=True)  # 将光谱数据写入fits文件,若文件已存在则覆盖
# 检查文件是否包含所有必要的信息

hdu = fits.open(file_extracted)  # 打开提取的FITS文件

hdu.info()  # 输出FITS文件的基本信息
# 访问HDU(层次数据单元)列表中的第一个HDU
hdu[1].data  # 获取第二个HDU的数据部分

通过API调用的工作流程#

我可以使用图形用户界面(GUI)中的插件对光谱进行一些分析。为了确保可重复性,我可以通过API以编程方式更改插件中的参数,执行相同的操作。

选择区域#

# 选择光谱中的一个区域

sv = specviz.app.get_viewer('spectrum-viewer')  # 获取名为'spectrum-viewer'的光谱查看器
# 仅用于逐行分析的区域

sv.toolbar_active_subset.selected = []  # 清空当前活动工具栏的选定项

sv.apply_roi(XRangeROI(1.124, 1.131))  # 应用一个范围为1.124到1.131的区域兴趣(ROI)
# 区域包含一些连续数据以进行高斯拟合

sv.toolbar_active_subset.selected = []  # 清空当前活动工具栏的选择

sv.apply_roi(XRangeROI(1.05, 1.25))  # 应用一个范围ROI,范围从1.05到1.25

线分析#

Specviz 显示 [OII] 线区域和线分析插件。
# 打开线分析插件

plugin_la = specviz.plugins['Line Analysis']  # 从specviz插件中获取“线分析”插件

plugin_la.open_in_tray()  # 在插件托盘中打开线分析插件
# 列出数据菜单中的内容

specviz.data_labels  # 获取并显示specviz对象中的数据标签
# 输入适当的光谱和区域

plugin_la.dataset = 'spec1d masked'  # 设置数据集为 'spec1d masked'

plugin_la.spectral_subset = 'Subset 2'  # 设置光谱子集为 'Subset 2'
# 输入连续体的值

plugin_la.continuum = 'Surrounding'  # 设置连续体为“周围”区域

plugin_la.continuum_width = 7  # 设置连续体宽度为7
# 返回线分析结果

plugin_la.get_results()  # 调用插件的get_results方法以获取分析结果

线列表#

Specviz 显示线列表插件和识别的红移 [OII] 双线。
# 打开谱线列表插件

plugin_ll = specviz.plugins['Line Lists']  # 获取名为'Line Lists'的插件

plugin_ll.open_in_tray()  # 在托盘中打开该插件

开发者说明

目前,行列表插件尚无法通过笔记本访问。不过,我可以在图形用户界面(GUI)中进行操作。

打开线列表插件。选择SDSS IV线列表。加载氧II(Oxygen II)线和Hb。返回线分析插件,将氧II线与我们刚刚分析的线关联起来。

模型拟合#

Specviz 显示了对连续谱和 [OII] 线的模型拟合。
# 打开模型拟合插件

plugin_mf = specviz.plugins['Model Fitting']  # 获取模型拟合插件

plugin_mf.open_in_tray()  # 在插件托盘中打开模型拟合插件
# 输入适当的数据集
plugin_mf.dataset = 'spec1d masked'  # 设置数据集为 'spec1d masked'

plugin_mf.spectral_subset = 'Subset 3'  # 设置光谱子集为 'Subset 3'
# 输入模型组件

# 创建一个多项式模型组件,阶数为2,标签为'P2'
plugin_mf.create_model_component(model_component='Polynomial1D',
                                 poly_order=2,
                                 model_component_label='P2')

# 创建一个高斯模型组件,标签为'G'
plugin_mf.create_model_component(model_component='Gaussian1D',
                                 model_component_label='G')
# 获取模型组件'G'
plugin_mf.get_model_component('G')  # 调用插件mf的get_model_component方法,传入参数'G'以获取相应的模型组件
# 设置模型组件 'G' 的标准差为 0.001
plugin_mf.set_model_component('G', 'stddev', 0.001)

# 设置模型组件 'G' 的均值为 1.128
plugin_mf.set_model_component('G', 'mean', 1.128)
# 模型方程自动填充
plugin_mf.equation = 'P2+G'  # 设置模型方程为 'P2+G'
# 运行此代码后,我们将进入图形用户界面(GUI)检查拟合结果是否合理

plugin_mf.calculate_fit()  # 调用插件的计算拟合函数
# 调用specviz模块中的get_model_parameters函数
specviz.get_model_parameters()  # 获取模型参数

使用specutils的相同工作流程(旧工作流程)#

可以直接使用specutils包(在jdaviz中底层使用)以及使用matplotlib进行静态可视化,来实现相同的工作流程。

拟合并减去连续谱#

# 使用通用连续谱拟合函数对掩蔽后的1D光谱进行拟合
cont_spec1d = fit_generic_continuum(spec1d_masked)

# 通过拟合的连续谱函数计算掩蔽后的光谱轴上的连续谱值
cont_fit = cont_spec1d(spec1d_masked.spectral_axis)
plt.figure(figsize=[10, 6])  # 创建一个10x6英寸的图形

plt.plot(spec1d_masked.spectral_axis, spec1d_masked.flux, label="data")  # 绘制数据的光谱轴和通量

plt.plot(spec1d_masked.spectral_axis, cont_fit, label="modeled continuum")  # 绘制拟合的连续谱

plt.xlabel("wavelength ({:latex})".format(spec1d_masked.spectral_axis.unit))  # 设置x轴标签为波长

plt.ylabel("flux ({:latex})".format(spec1d_masked.flux.unit))  # 设置y轴标签为通量

plt.legend()  # 显示图例

plt.title("Observed spectrum and fitted continuum")  # 设置图形标题

plt.show()  # 显示图形

plt.figure(figsize=[10, 6])  # 创建一个10x6英寸的图形

plt.plot(spec1d_masked.spectral_axis, spec1d_masked.uncertainty.array, label="data")  # 绘制不确定性的光谱轴和不确定性数组

plt.xlabel("wavelength ({:latex})".format(spec1d_masked.spectral_axis.unit))  # 设置x轴标签为波长

plt.ylabel("uncertainty ({:latex})".format(spec1d_masked.uncertainty.unit))  # 设置y轴标签为不确定性

plt.legend()  # 显示图例

plt.title("Uncertianty of observed spectrum")  # 设置图形标题

plt.show()  # 显示图形

创建去连续谱#

Specutils 将会处理不确定性的问题!

# 从掩蔽的光谱数据中减去连续背景拟合
spec1d_sub = spec1d_masked - cont_fit

# 输出减去背景后的光谱数据
spec1d_sub
# 创建一个新的图形,设置大小为10x6英寸
plt.figure(figsize=[10, 6])

# 绘制一维光谱数据,x轴为光谱轴,y轴为光通量,标签为"data"
plt.plot(spec1d_sub.spectral_axis, spec1d_sub.flux, label="data")

# 设置x轴标签,单位为光谱轴的单位
plt.xlabel("wavelength ({:latex})".format(spec1d_sub.spectral_axis.unit))

# 设置y轴标签,单位为光通量的单位
plt.ylabel("flux ({:latex})".format(spec1d_sub.flux.unit))

# 显示图例
plt.legend()

# 设置图形标题
plt.title("Continuum-subracted spectrum")

# 显示图形
plt.show()

# 创建另一个新的图形,设置大小为10x6英寸
plt.figure(figsize=[10, 6])

# 绘制一维光谱的不确定性数据,x轴为光谱轴,y轴为不确定性,标签为"data"
plt.plot(spec1d_sub.spectral_axis, spec1d_sub.uncertainty.array, label="data")

# 设置x轴标签,单位为光谱轴的单位
plt.xlabel("wavelength ({:latex})".format(spec1d_sub.spectral_axis.unit))

# 设置y轴标签,单位为不确定性的单位
plt.ylabel("uncertainty ({:latex})".format(spec1d_sub.uncertainty.unit))

# 显示图例
plt.legend()

# 设置图形标题
plt.title("Uncertainty of continuum-subracted spectrum")

# 显示图形
plt.show()

查找发射线和吸收线#

# 使用阈值方法查找光谱中的谱线
lines = find_lines_threshold(spec1d_sub, noise_factor=3)  # 调用函数,传入光谱数据和噪声因子

lines  # 输出找到的谱线

在光谱上绘制发射线。

plt.figure(figsize=[10, 6])  # 创建一个10x6英寸的图形

plt.plot(spec1d_sub.spectral_axis, spec1d_sub.flux, label="data")  # 绘制光谱数据

plt.axvline(lines['line_center'][0].value, color="red", alpha=0.5, label='emission lines')  # 绘制第一个发射线的垂直线

for line in lines:  # 遍历所有线

    if line['line_type'] == 'emission':  # 如果线的类型是发射线

        plt.axvline(line['line_center'].value, color='red', alpha=0.5)  # 绘制发射线的垂直线

plt.xlabel("wavelength ({:latex})".format(spec1d_sub.spectral_axis.unit))  # 设置x轴标签为波长

plt.ylabel("flux ({:latex})".format(spec1d_sub.flux.unit))  # 设置y轴标签为通量

plt.legend()  # 显示图例

plt.title("Continuum-subtracted spectrum and marked lines using find_lines_threshold")  # 设置图表标题

plt.show()  # 显示图形

手动在单行上工作。

# 定义绘图的x轴最小值
x_min = 1.1

# 定义绘图的x轴最大值
x_max = 1.16

# 定义线性区域的最小值
line_min = 1.124 * u.um  # 使用微米单位

# 定义线性区域的最大值
line_max = 1.131 * u.um  # 使用微米单位
plt.figure(figsize=[10, 6])  # 创建一个10x6英寸的图形

plt.plot(spec1d_sub.spectral_axis, spec1d_sub.flux, label="data")  # 绘制光谱数据

plt.scatter(spec1d_sub.spectral_axis, spec1d_sub.flux, label=None)  # 在光谱数据上添加散点图

plt.axvline(lines['line_center'][0].value, color="red", alpha=0.5, label='[OII]')  # 绘制[OII]线的垂直线

for line in lines:  # 遍历所有线
    if line['line_type'] == 'emission':  # 如果线是发射线
        plt.axvline(line['line_center'].value, alpha=0.5, color='red')  # 绘制发射线的垂直线

plt.xlim(x_min, x_max)  # 设置x轴的范围

plt.xlabel("wavelength ({:latex})".format(spec1d_sub.spectral_axis.unit))  # 设置x轴标签,显示波长单位

plt.ylabel("flux ({:latex})".format(spec1d_sub.flux.unit))  # 设置y轴标签,显示通量单位

plt.legend()  # 显示图例

plt.title("Continuum-subtracted spectrum zoomed on [OII]")  # 设置图形标题

plt.show()  # 显示图形

测量谱线中心和通量#

# 使用centroid函数计算指定光谱区域的质心
centroid(spec1d_sub, SpectralRegion(line_min, line_max))  # 计算光谱区域(line_min, line_max)的质心
sline = centroid(spec1d_sub, SpectralRegion(line_min, line_max))  # 计算指定波段内的光谱中心

plt.figure(figsize=[10, 6])  # 创建一个10x6英寸的图形

plt.plot(spec1d_sub.spectral_axis, spec1d_sub.flux, label="data")  # 绘制光谱数据

plt.scatter(spec1d_sub.spectral_axis, spec1d_sub.flux, label=None)  # 在光谱数据上绘制散点图

plt.axvline(sline.value, color='red', label="[OII]")  # 绘制红色竖线表示[OII]的中心位置

plt.axhline(0, color='black', label='flux = 0')  # 绘制黑色水平线表示flux为0的位置

plt.xlim(x_min, x_max)  # 设置x轴的范围

plt.xlabel("wavelength ({:latex})".format(spec1d_sub.spectral_axis.unit))  # 设置x轴标签为波长

plt.ylabel("flux ({:latex})".format(spec1d_sub.flux.unit))  # 设置y轴标签为flux

plt.legend()  # 显示图例

plt.title("Continuum-subtracted spectrum zoomed on [OII]")  # 设置图表标题

plt.show()  # 显示图表
# 计算指定光谱区域的线通量
line_flux(spec1d_sub, SpectralRegion(line_min, line_max))  # 传入光谱数据和光谱区域

用高斯函数拟合直线#

# 初始化一个一维高斯模型,均值为1.1278909微米,标准差为0.001微米
g_init = models.Gaussian1D(mean=1.1278909*u.um, stddev=0.001*u.um)

# 使用给定的高斯模型拟合一维光谱数据
g_fit = fit_lines(spec1d_sub, g_init)

# 使用拟合后的模型生成拟合光谱
spec1d_fit = g_fit(spec1d_sub.spectral_axis)

# 输出拟合结果
g_fit
plt.figure(figsize=[10, 6])  # 创建一个10x6英寸的图形

plt.plot(spec1d_sub.spectral_axis, spec1d_sub.flux, label='data')  # 绘制原始数据的光谱轴和通量

plt.plot(spec1d_sub.spectral_axis, spec1d_fit, color='darkorange', label='Gaussian fit')  # 绘制高斯拟合曲线,颜色为深橙色

plt.xlim(x_min, x_max)  # 设置x轴的范围

plt.xlabel("wavelength ({:latex})".format(spec1d_sub.spectral_axis.unit))  # 设置x轴标签,显示波长单位

plt.ylabel("flux ({:latex})".format(spec1d_sub.flux.unit))  # 设置y轴标签,显示通量单位

plt.legend()  # 显示图例

plt.title('Gaussian fit to the [OII] line')  # 设置图形标题

plt.show()  # 显示图形

测量谱线的等效宽度#

这需要对光谱进行连续体归一化。

# 归一化一维光谱数据,将掩蔽后的光谱数据除以连续谱拟合结果
spec1d_norm = spec1d_masked / cont_fit
plt.figure(figsize=[10, 6])  # 创建一个10x6英寸的图形

plt.plot(spec1d_norm.spectral_axis, spec1d_norm.flux, label='data')  # 绘制归一化光谱数据

plt.axhline(1, color='black', label='flux = 1')  # 添加水平线,y=1,表示归一化的基准线

plt.xlabel("wavelength ({:latex})".format(spec1d_norm.spectral_axis.unit))  # 设置x轴标签,显示波长单位

plt.ylabel("flux (normalized)")  # 设置y轴标签,表示归一化的光通量

plt.xlim(x_min, x_max)  # 设置x轴的范围

plt.legend()  # 显示图例

plt.title("Continuum-normalized spectrum, zoomed on [OII]")  # 设置图形标题

plt.show()  # 显示图形

plt.figure(figsize=[10, 6])  # 创建另一个10x6英寸的图形

plt.plot(spec1d_norm.spectral_axis, spec1d_norm.uncertainty.array, label='data')  # 绘制归一化光谱的不确定性数据

plt.xlabel("wavelength ({:latex})".format(spec1d_norm.spectral_axis.unit))  # 设置x轴标签,显示波长单位

plt.ylabel("uncertainty (normalized)")  # 设置y轴标签,表示归一化的不确定性

plt.xlim(x_min, x_max)  # 设置x轴的范围

plt.legend()  # 显示图例

plt.title("Uncertainty of continuum-normalized spectrum, zoomed on [OII]")  # 设置图形标题

plt.show()  # 显示图形
# 计算给定光谱的等效宽度
equivalent_width(spec1d_norm, regions=SpectralRegion(line_min, line_max))  # 使用规范化的一维光谱和指定的谱线区域计算等效宽度

寻找最佳拟合模板#

它需要一个模板列表和观测星系的红移(redshift)。对于模板,我使用了一组由Bruzual & Charlot恒星种群模型生成的模型SED(光谱能量分布),包括发射线(emission lines)和尘埃衰减(dust attenuation),具体描述见Pacifici等人(2012)。

开发者注释

也许有办法加快这个过程(例如使用 astropy 的 model_sets)?这个拟合是用 100 个模型运行的,但理想情况下,如果我们想从中提取物理参数,我们至少需要 10,000 个模型。使用具有有意义键(例如相关物理参数的元组)的字典结构可能比列表更好?这可以使后续分析比从列表索引映射回相关参数更清晰。

templatedir = './mos_spectroscopy/templates/'  # 定义模板目录的路径
# 从Specviz分析中获取的红移值
zz = 2.0256

# 定义光谱单位,单位为 erg/s/cm²/Å
f_lamb_units = u.erg / u.s / (u.cm**2) / u.AA

# 初始化模板列表
templatelist = []

# 为了提高速度,仅处理100个中的30个
for i in range(1, 30):
    # 构建模板文件的路径,格式为"templatedir" + "i"的五位数格式
    template_file = "{0}{1:05d}.dat".format(templatedir, i)

    # 读取模板文件
    template = ascii.read(template_file)

    # 创建Spectrum1D对象,光谱轴为模板的第一列(单位转换为微米),通量为第二列(单位为f_lamb_units)
    temp1d = Spectrum1D(spectral_axis=(template['col1']/1E4)*u.um, flux=template['col2']*f_lamb_units)

    # 将创建的Spectrum1D对象添加到模板列表中
    templatelist.append(temp1d)
# 将观测光谱的单位更改为与模板匹配

spec1d_masked_flamb = spec1d_masked.new_flux_unit(f_lamb_units)  # 使用新的光谱单位创建新的光谱对象

# new_flux_unit函数不会更改不确定性,导致specviz出现不匹配的错误

# 因此,我们像上面几个单元格中那样重新添加不确定性

spec1d_masked_flamb_unc = Spectrum1D(spectral_axis=spec1d_masked_flamb.spectral_axis,  # 保持光谱轴不变
                                     flux=spec1d_masked_flamb.flux,  # 保持光谱数据不变
                                     uncertainty=StdDevUncertainty((np.zeros(len(spec1d_masked_flamb.flux)) + 1E-20) * spec1d_masked_flamb.unit))  # 添加小的标准偏差不确定性
# 查看观测光谱和在正确红移下的一个模板

mean_obs = np.mean(spec1d_masked_flamb_unc.flux)  # 计算观测光谱的平均值

mean_temp = np.mean(templatelist[0].flux)  # 计算模板光谱的平均值

# 创建一个用于绘图的模板光谱,考虑红移
temp_for_plot = Spectrum1D(spectral_axis=templatelist[0].spectral_axis * (1.+zz),  # 应用红移调整波长
                           flux=templatelist[0].flux * mean_obs / mean_temp)  # 归一化模板光谱

plt.figure(figsize=[10, 6])  # 创建一个新的图形,设置大小

plt.plot(spec1d_masked_flamb_unc.spectral_axis, spec1d_masked_flamb_unc.flux, label='data')  # 绘制观测数据

plt.plot(temp_for_plot.spectral_axis, temp_for_plot.flux, label='model', alpha=0.6)  # 绘制模板模型,设置透明度

plt.xlabel("wavelength ({:latex})".format(spec1d_masked_flamb_unc.spectral_axis.unit))  # 设置x轴标签,显示波长单位

plt.ylabel("flux (normalized)")  # 设置y轴标签,显示归一化后的通量

plt.xlim(1.1, 1.7)  # 设置x轴范围

plt.ylim(0, 2e-18)  # 设置y轴范围

plt.legend()  # 显示图例

plt.title("Observed spectrum compared to one template at correct redshift")  # 设置图形标题

plt.show()  # 显示图形
# 调用模板比较函数进行模板匹配
tm_results = template_comparison.template_match(
    observed_spectrum=spec1d_masked_flamb_unc,  # 传入观测光谱,已进行掩蔽和不确定性处理
    spectral_templates=templatelist,             # 传入光谱模板列表
    resample_method="flux_conserving",           # 选择重采样方法为“保留通量”
    redshift=zz                                   # 传入红移值
)

# 输出模板匹配结果的第一个元素
tm_results[0]
plt.figure(figsize=[10, 6])  # 创建一个10x6英寸的图形

plt.plot(spec1d_masked_flamb_unc.spectral_axis, spec1d_masked_flamb_unc.flux, label="data")  # 绘制观测数据的光谱轴和通量

plt.plot(tm_results[0].spectral_axis, tm_results[0].flux, color='r', alpha=0.5, label='model')  # 绘制模型的光谱轴和通量,红色,透明度0.5

plt.xlim(1.0, 1.7)  # 设置x轴范围为1.0到1.7

plt.ylim(0, 5e-19)  # 设置y轴范围为0到5e-19

plt.xlabel("wavelength ({:latex})".format(spec1d_masked_flamb_unc.spectral_axis.unit))  # 设置x轴标签,单位为光谱轴单位

plt.ylabel("flux ({:latex})".format(spec1d_masked_flamb_unc.flux.unit))  # 设置y轴标签,单位为通量单位

plt.legend()  # 显示图例

plt.title("Observed spectrum and best-fitting model template")  # 设置图形标题

plt.show()  # 显示图形

新实例的Specviz与光谱和模板#

传递具有不同(但兼容)单位的光谱。Specviz采用第一个光谱并适当地转换第二个光谱。

specviz_2 = Specviz()  # 创建一个Specviz实例

specviz_2.load_data(spec1d_masked, data_label='observed')  # 加载观测数据,单位为MJy

specviz_2.load_data(tm_results[0], data_label='model')  # 加载模型数据,单位为erg/(s cm^2 A)

specviz_2.show()  # 显示数据
空间望远镜标志

由Camilla Pacifici创建的笔记本 (cpacifici@stsci.edu)

更新于2024年8月28日