Specviz 简单演示#
用例: 本笔记本演示了如何在Specviz中检查光谱,如何从笔记本的GUI中导出光谱,如何在GUI和笔记本中选择区域,以及如何在GUI中测量源的红移(redshift)。
数据: 来自NGDEEP调查的NIRISS 1D光谱。该数据集直接从经过默认JWST管道处理的MAST获取。
工具: specutils, jdaviz。
跨仪器: 所有仪器。
文档: 本笔记本是STScI更大后处理数据分析工具生态系统的一部分。
更新于: 2023/10/11
# 使用浏览器窗口的100%
from IPython.display import display, HTML # 导入显示和HTML模块
# 设置容器宽度为100%
display(HTML("<style>.container { width:100% !important; }</style>")) # 显示HTML样式
导入:
matplotlib 用于绘制数据
astropy 用于处理 FITS 文件、单位和表格
specutils 用于与 Specviz 的交互以及区域定义/提取
jdaviz 用于可视化工具 Specviz
# 绘图和表格处理
import matplotlib.pyplot as plt # 导入绘图库
# 导入astropy库
import astropy # 导入astropy库
import astropy.units as u # 导入单位模块
from astropy.io import fits # 从astropy.io导入fits模块,用于处理FITS文件
from astropy.nddata import StdDevUncertainty # 从astropy.nddata导入标准偏差不确定性类
from astropy.table import QTable # 从astropy.table导入QTable类,用于处理表格数据
# 导入specutils库
import specutils # 导入specutils库
from specutils import Spectrum1D, SpectralRegion # 从specutils导入Spectrum1D和SpectralRegion类
from specutils.manipulation import extract_region # 从specutils.manipulation导入提取区域的函数
# 导入viztools库
import jdaviz # 导入jdaviz库
from jdaviz import Specviz # 从jdaviz导入Specviz类,用于可视化光谱数据
# 自定义matplotlib样式
plt.rcParams["figure.figsize"] = (10, 5) # 设置图形的默认大小为10x5英寸
params = {
'legend.fontsize': '18', # 设置图例字体大小为18
'axes.labelsize': '18', # 设置坐标轴标签字体大小为18
'axes.titlesize': '18', # 设置坐标轴标题字体大小为18
'xtick.labelsize': '18', # 设置x轴刻度标签字体大小为18
'ytick.labelsize': '18', # 设置y轴刻度标签字体大小为18
'lines.linewidth': 2, # 设置线条宽度为2
'axes.linewidth': 2, # 设置坐标轴线宽度为2
'animation.html': 'html5', # 设置动画输出格式为html5
'figure.figsize': (8, 6) # 设置图形的默认大小为8x6英寸
}
plt.rcParams.update(params) # 更新matplotlib的配置参数
plt.rcParams.update({'figure.max_open_warning': 0}) # 禁用最大打开图形警告
检查版本
# 打印 astropy 库的版本
print('astropy:', astropy.__version__)
# 打印 specutils 库的版本
print('specutils:', specutils.__version__)
# 打印 jdaviz 库的版本
print('jdaviz:', jdaviz.__version__)
1. 加载NIRISS管道输出#
JWST/NIRISS数据存储在box上。我们使用x1d文件,该文件包含所有提取的1D光谱。
# 定义JWST数据文件的链接
filelink = 'https://data.science.stsci.edu/redirect/JWST/jwst-data_analysis_tools/specviz_notebook_gui_interaction/jw02079004002_11101_00001_nis_x1d.fits'
# 使用fits模块打开FITS文件
hdu = fits.open(filelink)
# 打印HDU列表的信息
hdu.info()
2. 打开Specviz并加载我们感兴趣的1D光谱#
viz = Specviz() # 创建一个Specviz对象,用于可视化光谱数据
viz.show() # 显示光谱可视化界面
以下单元打开x1d文件的一个扩展(75),创建一个Spectrum1D对象,并将其加载到Specviz中。设置了一个掩膜,仅保留在F150W滤光片中具有良好灵敏度的光谱部分(1.34到1.66微米)。
for i in range(74, 75): # 遍历指定范围的索引
spec_load = hdu[i+1].data # 从HDU中加载数据
wave = spec_load['WAVELENGTH'] # 获取波长数据
flux = spec_load['FLUX'] # 获取光通量数据
error = spec_load['FLUX_ERROR'] # 获取光通量误差数据
# 掩蔽波段内灵敏度较差的部分
mask = ((wave > 1.34) & (wave < 1.66)) # 创建掩蔽掩码
# 创建一维光谱对象,应用掩蔽
spec1d = Spectrum1D(spectral_axis=wave[mask]*u.um, # 设置光谱轴为掩蔽后的波长
flux=flux[mask]*u.Jy, # 设置光通量为掩蔽后的光通量
uncertainty=StdDevUncertainty(error[mask]*u.Jy)) # 设置光通量的不确定性
viz.load_data(spec1d, "NIRISS 1D {}".format(str(i+1))) # 将数据加载到可视化工具中
3. 使用GUI和笔记本选择发射线#
我选择了大约从1.58到1.63微米的发射线区域。
指令: https://jdaviz.readthedocs.io/en/latest/specviz/displaying.html#defining-spectral-regions
查看此 SpecViz 实例中使用的数据#
dataout = viz.get_spectra(apply_slider_redshift=False) # 获取光谱数据,不应用红移滑块
spec1d_line = dataout["NIRISS 1D 75"] # 从数据中提取NIRISS 1D 75光谱
# 打印变量spec1d_line的内容
print(spec1d_line)
查看在GUI中定义的子集#
我包含了一个try-except语句,以防在没有人工交互的情况下运行笔记本。
try:
# 获取光谱区域
region = viz.get_spectral_regions()
# 打印子集1的区域信息
print(region['Subset 1'])
except KeyError:
# 如果没有在GUI中定义区域,则打印错误信息
print("No region defined in the GUI")
以编程方式选择相同区域#
我可以在任意边界之间定义自己的区域(cont_region)。我选择1.598微米(um)和1.621微米(um)。然后,我可以提取该区域内的光谱。
# 定义一个光谱区域,范围从1.598微米到1.621微米
cont_region = SpectralRegion(1.598*u.um, 1.621*u.um)
# 从光谱数据中提取指定的光谱区域
spec1d_el_code = extract_region(spec1d_line, cont_region)
# 打印提取的光谱区域数据
print(spec1d_el_code)
或者,我可以提取我在图形用户界面(GUI)中定义的区域(region[‘Subset 1’])中的光谱。
try:
# 从spec1d_line中提取Subset 1区域的数据
spec1d_el_viz = extract_region(spec1d_line, region['Subset 1'])
# 打印提取的光谱数据
print(spec1d_el_viz)
except KeyError:
# 如果在GUI中未定义区域,则打印错误信息
print("Region was not defined in the GUI")
# 将spec1d_el_viz定义为spec1d_el_code
spec1d_el_viz = spec1d_el_code
使用matplotlib绘制光谱及其子集#
plt.plot(spec1d_line.spectral_axis, spec1d_line.flux, label='data') # 绘制数据的光谱轴和通量
plt.plot(spec1d_el_viz.spectral_axis, spec1d_el_viz.flux, label='subset defined in tool') # 绘制工具定义的子集的光谱轴和通量
plt.plot(spec1d_el_code.spectral_axis, spec1d_el_code.flux, label='subset defined in code') # 绘制代码定义的子集的光谱轴和通量
plt.legend() # 显示图例
plt.xlabel("wavelength ({:latex})".format(spec1d_line.spectral_axis.unit)) # 设置x轴标签为波长,单位为光谱轴的单位
plt.ylabel("flux ({:latex})".format(spec1d_line.flux.unit)) # 设置y轴标签为通量,单位为通量的单位
plt.title("NIRISS ID 75") # 设置图表标题
plt.show() # 显示图表
4. 在Specviz中使用红移滑块查找红移#
我首先打开一个新的Specviz实例,这样就不需要频繁上下滚动了。
viz2 = Specviz() # 创建一个Specviz对象,用于可视化光谱数据
viz2.show() # 显示Specviz界面
我加载了仅有趣的光谱(spec1d_line)。
# 加载NIRISS 1D光谱线数据
viz2.load_data(spec1d_line, "NIRISS 1D lines")
我可以使用现有的谱线列表或定义我自己的谱线(我知道我需要 Hb4861.3 和 [OIII]4958.9,5006.8 双线),并通过调整红移滑块来将谱线列表中的谱线与光谱中的谱线匹配。谱线列表插件可以通过点击查看器右上角的插件图标找到。要输入仅这三条谱线,我可以使用“自定义”菜单。
以下是解释谱线列表的文档: https://jdaviz.readthedocs.io/en/latest/specviz/plugins.html#line-lists
我还可以通过编程方式定义感兴趣的谱线,如下方的单元格所示。
lt = QTable() # 创建一个空的QTable对象
lt['linename'] = ['Hb', '[OIII]1', '[OIII]2'] # 添加谱线名称
lt['rest'] = [4861.3, 4958.9, 5006.8]*u.AA # 添加谱线的静止波长,并指定单位为Ångström
viz2.load_line_list(lt) # 加载谱线列表到viz2对象中
这些线条现在没有显示,因为它们的静止值超出了此处绘制的范围。我可以使用线列表插件中的红移滑块来移动这些线条。最好先在数字框中将红移设置为2,然后移动滑块以将线条放置在观测到的发射线之上。
从Spectrum1D对象中获取红移(redshift)#
# 获取应用红移滑块后的NIRISS 1D光谱数据
spec1d_redshift = viz2.get_spectra(apply_slider_redshift=True)["NIRISS 1D lines"]
# 打印获取的光谱数据
print(spec1d_redshift)
# 打印空行以增加可读性
print()
# 检查光谱的红移值是否为0.0
if spec1d_redshift.redshift != 0.0:
# 如果红移不为0.0,打印红移值
print("NIRISS 1D lines redshift=", spec1d_redshift.redshift)
else:
# 如果红移未在GUI中定义,则在此处定义红移
print("Redshift was not defined in GUI. Defining it here.")
# 设置光谱的红移为2.2138
spec1d_redshift.set_redshift_to(2.2138)
# 打印设置后的红移值
print("NIRISS 1D lines redshift=", spec1d_redshift.redshift)
5. 对光谱的连续谱进行建模#
我打开了另一个 Specviz 实例,并加载了之前使用的相同光谱。
viz3 = Specviz() # 创建一个Specviz对象,用于可视化光谱数据
viz3.show() # 显示Specviz窗口
# 加载NIRISS 1D光谱数据
viz3.load_data(spec1d_line, "NIRISS 1D lines")
我可以使用图形用户界面(GUI)选择我看到的连续区域。挑战:选择一个不连续的子集,覆盖两个区间(1.35-1.55微米和1.63-1.65微米)。提示:在子集下拉菜单附近的顶部选择“添加”。
我可以在插件图标下使用模型拟合插件来对选定区域拟合线性模型。具体说明可以在这里找到:https://jdaviz.readthedocs.io/en/latest/specviz/plugins.html#model-fitting。完成此任务的具体步骤如下:
在数据下选择子集 1
在模型下选择 Linear1D
点击添加模型
在模型标签下输入模型名称(我选择“continuum”)
点击拟合
我可以从正在使用的数据集中提取模型及其参数。
try:
dataout3 = viz3.get_spectra() # 获取光谱数据
spectrum = dataout3["NIRISS 1D lines"] # 获取NIRISS 1D光谱线数据,与之前加载的spec1d_lines相同
continuum = dataout3["continuum"] # 获取连续谱数据
model_param = viz3.get_model_parameters() # 获取模型参数
print(continuum) # 打印连续谱数据
print(model_param['continuum']) # 打印模型中的连续谱参数
except KeyError: # 捕获键错误异常
print("Continuum has not been created. Setting it to 0") # 输出错误信息,表示连续谱未创建,设置为0
continuum = Spectrum1D(spectral_axis=spectrum.spectral_axis, flux=0.*spectrum.flux) # 创建一个零值的连续谱
我可以进行连续体减法并使用matplotlib绘制结果。如果在图形用户界面(GUI)中未定义连续体,这个操作将返回未更改的原始光谱。
spectrum_sub = spectrum - continuum # 从光谱中减去连续背景,得到去除背景的光谱
plt.plot(spectrum_sub.spectral_axis, spectrum_sub.flux) # 绘制光谱数据,x轴为光谱轴,y轴为通量
plt.hlines(0, 1.3, 1.7, color='black') # 在y=0的位置绘制水平线,范围从1.3到1.7
plt.xlabel("wavelength ({:latex})".format(spectrum_sub.spectral_axis.unit)) # 设置x轴标签,显示波长单位
plt.ylabel("flux ({:latex})".format(spectrum_sub.flux.unit)) # 设置y轴标签,显示通量单位
plt.title("NIRISS ID 75") # 设置图表标题
plt.show() # 显示绘制的图表