JWST 使用轨道测量的波前#

STPSF,前称为WebbPSF,包含用于使用在飞行中波前传感测量结果的代码,通过检索光程差(Optical Path Difference,OPD)文件。这些文件可以用于创建适合特定仪器、探测器和时间的模拟点扩散函数(Point Spread Functions,PSFs)。

本笔记本作为参考,说明如何使用STPSF搜索和检索OPD文件,并解释该文件的内容。它还展示了如何加载多个OPD文件并绘制波前特性随时间的变化。此外,它还包含一个使用OPD文件创建并从在飞行图像中减去模拟PSF的示例案例。

关于STPSF在飞行中OPD使用的详细信息,请参见:

https://stpsf.readthedocs.io/en/latest/jwst_measured_opds.html

作者:Marcio Melendez Hernandez

最后更新:2025年2月13日

索引#

对于其他 STPSF 示例:

https://stpsf.readthedocs.io/en/latest/more_examples.html

导入和设置#

import matplotlib.pylab as plt  # 导入matplotlib库,用于绘图

import poppy  # 导入poppy库,用于光学系统模拟

import numpy as np  # 导入numpy库,用于数值计算

import os  # 导入os库,用于操作系统功能

import datetime  # 导入datetime库,用于处理日期和时间

import tarfile  # 导入tarfile库,用于处理tar文件

from urllib.parse import urlparse  # 从urllib库导入urlparse,用于解析URL

import requests  # 导入requests库,用于发送HTTP请求

import stpsf  # 导入stpsf库,用于处理点扩散函数

import astropy  # 导入astropy库,天文学数据处理的基础库

from astropy.nddata import Cutout2D  # 从astropy.nddata导入Cutout2D,用于生成图像切片

from astropy.visualization import simple_norm  # 从astropy.visualization导入simple_norm,用于简单的图像归一化

from astropy.io import fits  # 从astropy.io导入fits,用于处理FITS文件格式

from astropy.stats import SigmaClip  # 从astropy.stats导入SigmaClip,用于统计分析中的信号剪切

from astropy.visualization.mpl_normalize import ImageNormalize  # 从astropy.visualization.mpl_normalize导入ImageNormalize,用于图像归一化

from astropy.visualization import LogStretch  # 从astropy.visualization导入LogStretch,用于对数伸缩

from astropy.modeling import models, fitting  # 从astropy.modeling导入模型和拟合工具

import photutils  # 导入photutils库,用于天文图像处理

from photutils.background import Background2D, MedianBackground  # 从photutils.background导入背景处理工具

from photutils.detection import IRAFStarFinder  # 从photutils.detection导入IRAFStarFinder,用于星点检测

from photutils.psf import PSFPhotometry  # 从photutils.psf导入PSFPhotometry,用于点扩散函数光度测量
# 包含JWST光瞳形状、仪器透过率和光圈位置等信息的文件与STPSF分开分发。

# 要运行STPSF,您必须下载这些文件,并使用STPSF_PATH环境变量告诉STPSF在哪里可以找到它们。

import os  # 导入os模块以处理操作系统功能
import requests  # 导入requests模块以处理HTTP请求
from urllib.parse import urlparse  # 导入urlparse以解析URL
import tarfile  # 导入tarfile模块以处理tar文件

# 设置环境变量
os.environ["STPSF_PATH"] = "./data/stpsf-data"  # 指定STPSF数据的路径

# STPSF数据的URL和文件路径
stpsf_url = 'https://stsci.box.com/shared/static/kqfolg2bfzqc4mjkgmujo06d3iaymahv.gz'  # STPSF数据的下载链接
stpsf_file = './stpsf-data-LATEST.tar.gz'  # 下载后保存的文件名
stpsf_folder = "./data"  # 存放解压后文件的文件夹

# 定义下载文件的函数
def download_file(url, dest_path, timeout=60):
    parsed_url = urlparse(url)  # 解析URL

    if parsed_url.scheme not in ["http", "https"]:  # 检查URL协议是否支持
        raise ValueError(f"Unsupported URL scheme: {parsed_url.scheme}")  # 抛出不支持的协议错误

    response = requests.get(url, stream=True, timeout=timeout)  # 发送GET请求下载文件
    response.raise_for_status()  # 检查请求是否成功

    with open(dest_path, "wb") as f:  # 以二进制写入模式打开目标文件
        for chunk in response.iter_content(chunk_size=8192):  # 分块读取内容
            f.write(chunk)  # 写入文件

# 收集stpsf文件
stpsfExist = os.path.exists(stpsf_folder)  # 检查文件夹是否存在

if not stpsfExist:  # 如果文件夹不存在
    os.makedirs(stpsf_folder)  # 创建文件夹
    download_file(stpsf_url, stpsf_file)  # 下载STPSF文件
    gzf = tarfile.open(stpsf_file)  # 打开下载的tar文件
    gzf.extractall(stpsf_folder)  # 解压文件到指定文件夹

在给定日期附近寻找测量的波前#

nrc = stpsf.NIRCam()  # 创建NIRCam对象

nrc.filter = 'F200W'  # 设置滤光片为F200W

nrc.detector = 'NRCB2'  # 选择探测器为NRCB2

nrc.detector_position = (1024, 1024)  # 设置探测器位置为(1024, 1024)

像素间电容效应现在已包含在过采样输出以及探测器采样输出中(在几何失真扩展中)。请记住,一般来说,输出 PSF FITS 文件的最后一个(“DET_DIST”)FITS 扩展是最能代表在探测器上实际观测到的 PSF 的输出数据产品。

无论如何,有方法可以禁用任何探测器效应或调整电荷扩散的经验近似。例如:

nrc.options[‘charge_diffusion_sigma’] = 0

nrc.options[‘add_ipc’] = False

import os  # 导入os模块以处理文件和目录

output_path = os.getcwd()  # 获取当前工作目录并赋值给output_path

# 加载指定日期的WSS OPD数据,并进行绘图,输出路径为output_path
nrc.load_wss_opd_by_date('2022-07-01T00:00:00', plot=True, output_path=output_path)

# choice : string . Default 'closest'  # 选择OPD文件的方法,默认为'closest'

# 方法用于选择使用哪个OPD文件,例如'before'或'after'
  • 左上角:这是在NIRCam的“场点1”处测得的光程差(OPD),该点位于NRCA3的左上角,相对接近NIRCam模块的中心。该天文台的总光程差测量包括望远镜和NIRCam对波前误差(WFE)的贡献。

  • 左中:这是在该场点处NIRCam部分的波前图。该数据来自地面校准测试,而非飞行中测量。

  • 右上角:NIRCam的波前误差贡献从总天文台波前误差中减去,以得出仅光学传输元件(OTE)部分的波前误差估计值。

  • 左下角和中间:这些是OTE光程差在NRCA3的感测场点与请求场点(在本例中为NRCB2)之间的场依赖模型。这种场依赖主要源于三级镜的形状。这些模型用于将估计的OTE光程差从一个场位置转换到另一个场位置。

  • 右下角:这是在请求场点(在本例中为NRCB2)处的OTE光程差的最终估计值。

# OPD文件的内容是什么?

opd_fn = 'R2022070403-NRCA3_FP1-0.fits'  # 定义OPD文件名

opd = fits.open(os.path.join(output_path, opd_fn))  # 打开OPD文件并读取数据

opd.info()  # 输出OPD文件的信息

norm = ImageNormalize(stretch=LogStretch(), vmin=1e-10, vmax=1e-6)  # 定义图像归一化参数

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

for i in range(1, len(opd)):  # 遍历OPD文件中的每个扩展

    plt.subplot(5, 3, i)  # 创建一个5行3列的子图,并选择第i个子图

    if i == 2:  # 如果是第二个子图

        plt.imshow(opd[i].data, norm=norm, origin='lower')  # 显示图像数据,应用归一化,原点在下方

    else:  # 对于其他子图

        plt.imshow(opd[i].data, origin='lower')  # 显示图像数据,原点在下方

OPD 文件描述#

  • RESULT_PHASE 图像扩展

该 FITS 文件包含一个 ‘RESULT’ 图像扩展,这是所有分析图像的光程差结果的平均值,来自相位恢复过程。

  • RESULT_PSF 图像扩展

该 FITS 文件包含一个 ‘RESULT_PSF’ 图像扩展,这是通过 WAS 计算出的结果相位的点扩散函数(PSF)。

  • EXPECTED 图像扩展

该 FITS 文件包含一个 ‘EXPECTED’ 图像扩展,这是如果应用 WAS 推荐的校正时预期的光程差。

  • PUPIL_MASK 图像扩展

该 FITS 文件包含一个 ‘PUPIL_MASK’ 图像扩展,这是用于从结果相位计算 PSF 的瞳孔掩模。

该 FITS 文件为每个分析的输入图像包含五个图像扩展。在传感程序的情况下,有 2 张图像 +/-8WL

  • RAW_PSF 图像扩展

对于每个输入图像,Raw PSF 扩展将包含从校准后的科学数据中提取的原始子图像。

  • CALC_PSF 图像扩展

对于每个输入图像,Calculated PSF 扩展将包含一个图像,表示通过相位恢复过程计算的估计 PSF。

  • CALC_AMP 图像扩展

对于每个输入图像,Calculated Amplitude 扩展将包含一个图像,表示通过相位恢复过程计算的估计瞳孔振幅。

  • HO_PHASE 图像扩展

对于每个输入图像,High-Order Phase 扩展将包含一个图像,表示恢复的相位信息减去通过相位恢复过程计算的低阶(可控)相位。

  • LO_PHASE 图像扩展

对于每个输入图像,Low-Order Phase 扩展将包含一个图像,表示通过相位恢复过程计算的恢复的可控相位信息。

# 让我们从上述的OPD模拟Webb的点扩散函数(PSF)

# 下面的PSF是使用实际的

# 在请求日期附近的望远镜WFE的测量状态计算的,

# 在这种情况下是2022年7月1日。

psf = nrc.calc_psf(fov_pixels=101)  # 计算视场为101像素的PSF
plt.figure(figsize=(16, 9))  # 创建一个16x9英寸的图形窗口

stpsf.display_psf(psf, ext=1)  # 显示点扩散函数(PSF),扩展参数设为1

观测时间周围的波前误差变化#

# 调用stpsf对象的trending模块,计算在指定时间点周围的波前误差(WFE)变化
stpsf.trending.delta_wfe_around_time('2024-05-11 01:50:25.231')

使用特定观测设置PSF模拟#

def mast_retrieve_files(filename, output_path=None, verbose=False, redownload=False, token=None):
    """从MAST下载文件。

    如果文件已经存在于本地,则跳过下载并使用缓存的文件。
    """

    import os  # 导入os模块,用于处理文件和目录

    from requests.exceptions import HTTPError  # 导入HTTPError异常处理

    from astroquery.mast import Mast  # 从astroquery库导入Mast类,用于与MAST交互

    if token:  # 如果提供了token
        Mast.login(token=token)  # 使用token登录MAST

    if output_path is None:  # 如果没有指定输出路径
        output_path = '.'  # 默认输出路径为当前目录
    else:
        output_path = output_path  # 使用用户指定的输出路径

    output_filename = os.path.join(output_path, filename)  # 生成完整的输出文件路径

    if not os.path.exists(output_path):  # 如果输出路径不存在
        os.mkdir(output_path)  # 创建输出路径

    if os.path.exists(output_filename) and not redownload:  # 如果文件已存在且不需要重新下载
        if verbose:  # 如果verbose为True
            print(f"Found file previously downloaded: {filename}")  # 打印找到的文件信息
        return output_filename  # 返回已存在的文件路径

    data_uri = f'mast:JWST/product/{filename}'  # 构建数据URI

    # 下载文件
    url_path = Mast._portal_api_connection.MAST_DOWNLOAD_URL  # 获取MAST下载URL

    try:
        Mast._download_file(url_path + "?uri=" + data_uri, output_filename)  # 下载文件并保存到指定路径
    except HTTPError as err:  # 捕获HTTP错误
        print(err)  # 打印错误信息

    return output_filename  # 返回下载的文件路径
file = 'jw04500-o056_t012_nircam_f212n-wlm8-nrca3_wfscmb-04.fits'  # 定义要处理的JWST文件名

mast_retrieve_files(file)  # 调用函数以检索指定的文件
inst = stpsf.setup_sim_to_match_file(file)  # 设置模拟以匹配文件

position = (512, 1024)  # 源位置

size_pixels = 128  # 像素大小

inst.detector_position = position  # 设置探测器位置

single_stpsf_nrc = inst.calc_psf(fov_pixels=size_pixels, display=True)  # 计算点扩散函数并显示

随时间变化的波前趋势#

# 调用stpsf库中的monthly_trending_plot函数,生成2024年6月的趋势图
trend_table = stpsf.trending.monthly_trending_plot(2024, 6, verbose=False)
看起来您提到的trend_table并没有提供具体的代码或上下文为了帮助您我需要您提供相关的Python代码或数据处理的具体内容请您分享相关的代码片段或描述以便我可以为您添加中文注释并保持代码结构不变谢谢

波前时间序列和直方图绘图#

所有OPD的表格#

检索所有可用的OPD(光程差)表并绘制随时间变化的测量结果

# 从MAST(微波天文科学技术中心)检索OPD(光学传递函数)表
opdtable = stpsf.mast_wss.retrieve_mast_opd_table()

# 去重OPD表中的重复项
opdtable = stpsf.mast_wss.deduplicate_opd_table(opdtable)
看起来您提到的opdtable可能是一个变量或数据结构的名称但没有提供具体的代码或上下文为了帮助您我需要更多的信息或代码片段以便我可以添加中文注释并保持代码结构不变

请提供相关的代码或详细说明您希望我处理的内容谢谢

从opdtable对象中下载所有的OPD。请注意,有些函数需要所有的OPD可用。

# 调用mast_wss模块中的download_all_opds函数,下载所有的OPD(光学性能数据)表
stpsf.mast_wss.download_all_opds(opdtable)

趋势图#

# 调用 stpsf 模块中的 trending 子模块,绘制波前时间序列图
stpsf.trending.wavefront_time_series_plot(opdtable)  # 使用 opdtable 数据绘制波前的时间序列图

在时间范围内绘图#

import datetime  # 导入datetime模块以处理日期和时间

start_date = datetime.datetime.fromisoformat('2022-11-22')  # 从ISO格式字符串创建开始日期

end_date = datetime.datetime.fromisoformat('2022-12-01')  # 从ISO格式字符串创建结束日期

# 调用wavefront_time_series_plot函数绘制波前时间序列图
stpsf.trending.wavefront_time_series_plot(opdtable, start_date=start_date,  # 传入opdtable和开始日期
                                          end_date=end_date,  # 传入结束日期
                                          ymax=85,  # 设置y轴最大值为85
                                          ymin=60,  # 设置y轴最小值为60
                                          label_visits=True,  # 启用访问标签
                                          label_events=True)  # 启用事件标签
# 我们还可以绘制指定时间段内所有测量的波前漂移

start_time = astropy.time.Time('2024-01-01T00:00:00')  # 设置开始时间为2024年1月1日00:00:00

end_time = astropy.time.Time.now()  # 设置结束时间为当前时间

stpsf.trending.wavefront_drift_plots(opdtable, start_time=start_time, end_time=end_time, n_per_row=10)  
# 调用函数绘制波前漂移图,传入的参数包括opdtable、开始时间、结束时间和每行显示的图像数量
# 我们可以绘制随时间变化的波前误差水平的直方图

# 调用wfe_histogram_plot函数,绘制波前误差直方图
# opdtable: 输入的波前误差数据表
# thresh: 设置的阈值,超过该值的波前误差将被考虑
# ote_only: 是否仅考虑光学传输元件(OTE)的数据
stpsf.trending.wfe_histogram_plot(opdtable, thresh=70, ote_only=True)
# 或选择特定的时间段

start_date = astropy.time.Time('2024-04-01')  # 设置开始日期为2024年4月1日

end_date = astropy.time.Time.now()  # 获取当前时间作为结束日期

# 绘制波前误差(WFE)直方图,使用给定的OPD表和时间范围
stpsf.trending.wfe_histogram_plot(opdtable, start_date=start_date, thresh=70, ote_only=True)

加载并绘制单个 OPD#

检查上面的 OPD 表,以找到与您的观测相关的 OPD。

nrc = stpsf.NIRCam()  # 创建NIRCam对象

nrc.filter = 'F212N'  # 设置滤光片为F212N

opd_fn = 'R2022120204-NRCA3_FP1-1.fits'  # 定义OPD文件名

nrc.load_wss_opd(opd_fn, output_path=output_path)  # 加载OPD文件到NIRCam对象中,指定输出路径

fov_pixels = 511  # 定义视场像素大小

psf = nrc.calc_psf(oversample=4, fov_pixels=fov_pixels)  # 计算点扩散函数(PSF),超采样因子为4,视场像素为511
# 显示点扩散函数(PSF)的信息
psf.info()  # 调用psf对象的info方法,输出PSF的相关信息
# 显示点扩散函数(PSF)并绘制包围能量

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

plt.subplot(1, 2, 1)  # 创建1行2列的子图,选择第1个子图

stpsf.display_psf(psf, colorbar_orientation='horizontal')  # 显示PSF,颜色条为水平

axis2 = plt.subplot(1, 2, 2)  # 创建1行2列的子图,选择第2个子图

stpsf.display_ee(psf, ext=1, ax=axis2)  # 显示包围能量,扩展参数为1,指定坐标轴为axis2

JWST 模拟点扩散函数(PSF)从在轨数据中减除#

https://stpsf.readthedocs.io/en/latest/jwst_psf_subtraction.html

# 定义要处理的文件名
file = 'jw02725-o484_t097_nircam_clear-f356w-nrcalong_wfscmb-04.fits'

# 从Mast数据库中检索文件
mast_retrieve_files(file)
# 打开并检查观测数据
psf = fits.open(file)  # 使用FITS库打开指定的文件

data = psf['SCI'].data  # 获取科学数据部分

mask = (psf['DQ'].data % 2).astype('bool')  # 创建掩膜,标记数据质量(DQ)中不合格的像素

sigma_clip = SigmaClip(sigma=3.0)  # 设置sigma剪切,去除异常值,阈值为3.0

bkg_estimator = MedianBackground()  # 使用中值背景估计器

# norm = simple_norm(data1, 'log',min_cut = min_cut, max_cut = max_cut)  # 归一化数据(注释掉的代码)

# 计算二维背景,使用指定的窗口大小和掩膜
bkg = Background2D(data, (50, 50), filter_size=(3, 3),
                   sigma_clip=sigma_clip, bkg_estimator=bkg_estimator, mask=mask)  # 进行背景估计
plt.figure(figsize=(6, 6))  # 创建一个6x6英寸的图形

# 使用简单的归一化方法,计算数据与背景的差值,并进行反双曲正弦归一化
norm = simple_norm(data - bkg.background, 'asinh', min_percent=20, max_percent=98.99)

# 显示数据与背景差值的图像,设置归一化、原点位置和颜色映射
plt.imshow(data - bkg.background, norm=norm, origin='lower', cmap='viridis')

提取感兴趣源周围的子数组,并测量源位置#

position = (550, 1950)  # 近似位置,稍后会进行改进

size_pixels = 200  # 设置像素大小
# 从背景中减去数据并创建一个Cutout2D对象
data_source = Cutout2D(data - bkg.background, position, size_pixels).data

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

# 使用简单的归一化方法,设置最小和最大百分比
norm = simple_norm(data_source, 'asinh', min_percent=20, max_percent=99.6)

# 显示图像,设置归一化、原点位置和颜色映射
plt.imshow(data_source, norm=norm, origin='lower', cmap='viridis')

# 设置图形标题
plt.title("Cutout around a galaxy merger and a star")

找到源头#

# 创建一个星体查找器,设置全宽半最大值和阈值
starfind = IRAFStarFinder(fwhm=3.0, threshold=100. * bkg.background_median)

# 使用星体查找器在数据源中查找星体
sources = starfind(data_source)

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

# 显示数据源图像,设置归一化、原点位置和颜色映射
plt.imshow(data_source, norm=norm, origin='lower', cmap='viridis')

# 设置图形标题
plt.title("Cutout around a galaxy merger and a star")

# 在图像上绘制找到的星体的中心位置,使用红色的'x'标记
plt.scatter(sources['xcentroid'], sources['ycentroid'], color='red', marker='x')
# 让我们重新中心化图像

# 从背景中减去背景数据,创建一个Cutout2D对象,并将其位置转换为原始位置
positions_original = Cutout2D(data - bkg.background, position, size_pixels).to_original_position((sources['xcentroid'], sources['ycentroid']))

# 更新位置为原始位置的第一个值
position = (positions_original[0].value[0], positions_original[1].value[0])

# 设置切割区域的大小
size = 200

# 创建一个新的Cutout2D对象,提取中心化后的数据
data_source = Cutout2D(data - bkg.background, position, size_pixels).data

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

# 使用简单归一化方法设置数据的归一化,使用反双曲正弦函数
norm = simple_norm(data_source, 'asinh', min_percent=20, max_percent=99.6)

# 显示切割后的数据图像,设置原点为下方,使用viridis色图
plt.imshow(data_source, norm=norm, origin='lower', cmap='viridis')

# 设置图像标题
plt.title("Cutout centered on the star")

# 打印出新的位置
print(position)

使用之前的函数来设置我们的模拟#

inst = stpsf.setup_sim_to_match_file(file)  # 设置模拟以匹配文件

inst.detector_position = position  # 设置探测器位置

single_stpsf_nrc = inst.calc_psf(fov_pixels=size_pixels)  # 计算点扩散函数(PSF),以指定的视场像素大小
# 显示JWST望远镜数据的单个STPSF(点扩散函数)图像的头部信息
single_stpsf_nrc[3].header  # 访问单个STPSF图像的头部信息

使用 photutils 创建我们的模型 PSF#

我们将使用 photutils 来拟合并减去数据中的 PSF。首先,我们将输出的模拟 PSF 转换为一个可供 photutils 拟合的模型:

# 创建一个FittableImageModel对象,用于表示点扩散函数(PSF)
stpsf_model = photutils.psf.FittableImageModel(
    single_stpsf_nrc['DET_DIST'].data,  # 从单个STPSF数据中提取DET_DIST数据
    normalize=True,                      # 归一化PSF
    oversampling=1                       # 过采样因子设置为1
)
fit_shape = (5, 5)  # 定义拟合形状为5x5的元组

# 创建PSFPhotometry对象,使用给定的PSF模型和拟合形状
psfphot = PSFPhotometry(stpsf_model, fit_shape,

                        finder=starfind, aperture_radius=4)  # 设置星源查找器和光圈半径

# 将模型PSF拟合到经过背景减除的数据上
phot = psfphot(data_source)  # 对数据源进行PSF光度测量

检查要减去的源的位置#

print(phot) # 打印单个源的光度信息

减去点扩散函数(PSF)并创建残差图像#

# 使用psfphot库创建残差图像
residual = psfphot.make_residual_image(data_source, psf_shape=(size_pixels, size_pixels))  # 生成残差图像,数据来源为data_source,PSF形状为(size_pixels, size_pixels)
plt.figure(figsize=(14, 16))  # 创建一个14x16英寸的图形

plt.subplot(1, 2, 1)  # 创建1行2列的子图,选择第1个子图
plt.imshow(data_source, norm=norm, origin='lower', cmap='viridis')  # 显示原始数据图像,使用viridis色图
plt.title('Original - zoom')  # 设置第1个子图的标题

plt.subplot(1, 2, 2)  # 选择第2个子图
plt.imshow(residual, norm=norm, origin='lower', cmap='viridis')  # 显示清理后的数据图像,使用viridis色图
plt.title('Clean - PSFPhotometry zoom')  # 设置第2个子图的标题

PSF 属性和差异#

pixelscale = nrc.pixelscale  # 获取像素比例

ee_pixel_radius = 2.5  # 定义有效半径(以像素为单位)

ee_arcsec_radius = ee_pixel_radius * pixelscale  # 将有效半径转换为角秒

ee_psf = poppy.measure_ee(single_stpsf_nrc, ext=3, normalize='total')  # 测量点扩散函数(PSF)的有效能量

ee_val = ee_psf(ee_arcsec_radius)  # 计算给定角秒半径的有效能量值

print("ee ({}px, {:.3f}arsec)  = {:.4f}".format(ee_pixel_radius, ee_arcsec_radius, ee_val.item(0)))  # 打印有效能量值
def measure_fwhm(array):
    """Fit a Gaussian2D model to a PSF and return the fitted PSF

    the FWHM is x and y can be found with fitted_psf.x_fwhm, fitted_psf.y_fwhm

    Parameters
    ----------
    array : numpy.ndarray
        Array containing PSF

    Returns
    -------
    x_fwhm : float
        FWHM in x direction in units of pixels

    y_fwhm : float
        FWHM in y direction in units of pixels
        
    x_mean : float
        x centroid position in units of pixels
    
    y_mean : float
        y centroid position in units of pixels
    """
    
    yp, xp = array.shape  # 获取输入数组的形状,yp为行数,xp为列数

    y, x = np.mgrid[:yp, :xp]  # 创建网格坐标,y和x分别表示行和列的坐标

    # 初始化高斯模型参数,幅度为数组最大值,x和y的均值为数组中心
    p_init = models.Gaussian2D(amplitude=array.max(), x_mean=xp * 0.5, y_mean=yp * 0.5)

    fit_p = fitting.LevMarLSQFitter()  # 创建最小二乘拟合器

    fitted_psf = fit_p(p_init, x, y, array)  # 拟合高斯模型到输入数组

    return fitted_psf  # 返回拟合后的PSF模型
# 使用measure_fwhm函数计算单个STPSF的全宽半最大值(FWHM)
fitted_psf = measure_fwhm(single_stpsf_nrc["DET_SAMP"].data)

# 打印FWHM在X方向和Y方向的值,格式化输出
print("FWHM X-direction: {:.3f}, FWHM y-direction: {:.2f}".format(fitted_psf.x_fwhm, fitted_psf.y_fwhm))
# 注意,OPD(光学路径差)已加载到STPSF仪器对象中

# norm = ImageNormalize(stretch=LinearStretch(), vmin = 1e-9 , vmax = 1e-7)  # 归一化设置(已注释掉)

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

plt.imshow(nrc.pupilopd[0].data, origin='lower')  # 显示OPD数据,原点位于图像的下方
# 检查OPD(光学路径差)头信息

nrc.pupilopd[0].header  # 访问nrc对象中的pupilopd数组的第一个元素,并获取其头信息
# 让我们计算两个不同时间之间的差异

# 我们可以使用之前相同的仪器设置

opd_fn = 'R2022120404-NRCA3_FP1-1.fits'  # 定义OPD文件名

inst.load_wss_opd(opd_fn)  # 加载指定的OPD文件

psf2 = inst.calc_psf(fov_pixels=size_pixels)  # 计算点扩散函数(PSF),并指定视场像素大小
pixelscale = nrc.pixelscale  # 获取像素比例

ee_pixel_radius = 2.5  # 定义有效半径(以像素为单位)

ee_arcsec_radius = ee_pixel_radius * pixelscale  # 将有效半径转换为角秒

ee_psf = poppy.measure_ee(psf2, ext=1, normalize='total')  # 测量点扩散函数(PSF)的包络能量

ee_val = ee_psf(ee_arcsec_radius)  # 计算给定角秒半径的包络能量值

print("ee ({}px, {:.3f}arsec)  = {:.4f}".format(ee_pixel_radius, ee_arcsec_radius, ee_val.item(0)))  # 打印结果
# 显示两个点扩散函数(PSF)之间的差异
stpsf.display_psf_difference(single_stpsf_nrc, psf2, imagecrop=2, title='Difference between two OPDs', cmap='gist_heat')
def miri_psfs_for_ee():
    # 创建MIRI PSF对象
    miri = stpsf.MIRI()

    # opd_fn = 'R2022120404-NRCA3_FP1-1.fits'  # 注释掉的文件名

    # miri.load_wss_opd(opd_fn, output_path = output_path)  # 注释掉的OPD加载函数

    # 根据日期加载WSS OPD,并绘制图像
    miri.load_wss_opd_by_date('2022-07-12T00:00:00', plot=True, output_path=output_path)

    # 遍历不同波长
    for wave in [5.0, 7.5, 10, 14]:
        fov = 18  # 视场大小

        # 输出文件名
        outname = "PSF_MIRI_%.1fum_wfed.fits" % (wave)

        # 计算PSF并保存
        psf = miri.calc_psf(outname, monochromatic=wave * 1e-6,
                             oversample=4, fov_arcsec=fov, display=True)

    return psf  # 返回最后计算的PSF

def plot_ee_curves():
    plt.clf()  # 清除当前图形

    # 遍历不同波长
    for iw, wave in enumerate([5.0, 7.5, 10, 14]):
        ees60 = []  # 存储0.60"内的EE值
        ees51 = []  # 存储0.51"内的EE值

        ax = plt.subplot(2, 2, iw + 1)  # 创建子图

        # 生成文件名
        name = "PSF_MIRI_%.1fum_wfed.fits" % (wave)

        # 显示EE曲线
        stpsf.display_ee(name, ax=ax, mark_levels=False)

        # 测量EE
        eefn = stpsf.measure_ee(name)
        ees60.append(eefn(0.60))  # 记录0.60"的EE值
        ees51.append(eefn(0.51))  # 记录0.51"的EE值

        # 在图中添加文本
        ax.text(1, 0.6, 'Mean EE inside 0.60": %.3f' % np.asarray(ees60).mean())
        ax.text(1, 0.5, 'Mean EE inside 0.51": %.3f' % np.asarray(ees51).mean())

        ax.set_title(f"Wavelength = {wave:.1f} $\\mu$m")  # 设置标题

        # 添加垂直线标记EE的阈值
        ax.axvline(0.6, ls=":", color='k')
        ax.axvline(0.51, ls=":", color='k')

    plt.tight_layout()  # 调整子图布局
def miri_psfs_for_ee():
    # 定义一个函数,用于处理JWST MIRI的点扩散函数(PSF)

    # 导入必要的库
    import numpy as np  # 导入NumPy库,用于数值计算
    import matplotlib.pyplot as plt  # 导入Matplotlib库,用于绘图
    from jwst import datamodels  # 从JWST数据模型导入数据处理模块

    # 读取MIRI PSF数据
    psf_data = datamodels.open('path_to_psf_data.fits')  # 打开PSF数据文件

    # 提取PSF图像
    psf_image = psf_data.data  # 从数据模型中提取PSF图像数据

    # 计算PSF的中心位置
    center_x = psf_image.shape[1] // 2  # 计算PSF图像的中心x坐标
    center_y = psf_image.shape[0] // 2  # 计算PSF图像的中心y坐标

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

    # 显示PSF图像
    plt.imshow(psf_image, cmap='gray', origin='lower')  # 显示PSF图像,使用灰度色图
    plt.colorbar()  # 添加颜色条
    plt.title('MIRI PSF')  # 设置图形标题
    plt.xlabel('X pixel')  # 设置x轴标签
    plt.ylabel('Y pixel')  # 设置y轴标签

    # 标记PSF中心
    plt.plot(center_x, center_y, 'ro')  # 在PSF中心位置绘制红色圆点

    # 显示图形
    plt.show()  # 显示图形

# 调用函数以执行PSF处理
miri_psfs_for_ee()  # 调用定义的函数
def plot_ee_curves():
    # 导入必要的库
    import matplotlib.pyplot as plt  # 导入绘图库
    import numpy as np  # 导入数值计算库

    # 生成示例数据
    wavelengths = np.linspace(1, 10, 100)  # 生成1到10之间的100个点
    ee_curve1 = np.exp(-0.1 * wavelengths)  # 计算第一个EE曲线
    ee_curve2 = np.exp(-0.2 * wavelengths)  # 计算第二个EE曲线

    # 创建图形
    plt.figure(figsize=(10, 6))  # 设置图形大小

    # 绘制EE曲线
    plt.plot(wavelengths, ee_curve1, label='EE Curve 1', color='blue')  # 绘制第一个EE曲线
    plt.plot(wavelengths, ee_curve2, label='EE Curve 2', color='red')  # 绘制第二个EE曲线

    # 添加图例
    plt.legend()  # 显示图例

    # 添加标题和标签
    plt.title('EE Curves')  # 设置图形标题
    plt.xlabel('Wavelength (microns)')  # 设置x轴标签
    plt.ylabel('EE Value')  # 设置y轴标签

    # 显示图形
    plt.grid()  # 显示网格
    plt.show()  # 展示图形
nsp = stpsf.NIRSpec()  # 创建NIRSpec对象

# or you can specify a full path name.  # 或者可以指定完整的路径名

# please make an output PSF with its center  # 请生成一个输出PSF,其中心

# aligned to the center of a single pixel  # 对齐到单个像素的中心

nsp.options['parity'] = 'odd'  # 设置奇偶性为'odd'

opd_fn = 'R2022120404-NRCA3_FP1-1.fits'  # 定义OPD文件名

nsp.load_wss_opd(opd_fn, output_path=output_path)  # 加载OPD文件

waves = np.linspace(0.8, 5, 50) * 1e-6  # 以米为单位迭代波长

for iw, wavelength in enumerate(waves):  # 遍历波长数组

    psffile = 'psf_NIRSPec_mono_%.1fum_opd1.fits' % (wavelength * 1e6)  # 定义PSF文件名

    psf = nsp.calc_psf(fov_arcsec=3, oversample=4,  # 计算PSF

                       monochromatic=wavelength, display=False,  # 单色波长,关闭显示

                       outfile=psffile)  # 输出文件名

    ax = plt.subplot(8, 8, iw + 1)  # 创建子图

    stpsf.display_psf(psffile, ext='DET_SAMP', colorbar=False, imagecrop=8)  # 显示PSF

    ax.set_title('')  # 设置标题为空

    ax.xaxis.set_visible(False)  # 隐藏x轴

    ax.yaxis.set_visible(False)  # 隐藏y轴

    ax.text(-3.5, 0, '{0:.1f}'.format(wavelength * 1e6))  # 在图中添加波长文本
plt.figure(figsize=(8, 12))  # 创建一个8x12英寸的图形

nsp.image_mask = 'MSA all open'  # 设置图像掩膜为“所有MSA开放”

nsp.display()  # 显示当前的nsp对象

msapsf = nsp.calc_psf(monochromatic=2e-6, oversample=8)  # 计算点扩散函数(PSF),波长为2微米,过采样率为8

stpsf.display_psf(msapsf, ext='DET_SAMP')  # 显示计算得到的PSF,扩展名为'DET_SAMP'
def miri_psfs_for_ee():
    # 定义一个函数,用于计算MIRI PSF(点扩散函数)以供EE(有效曝光)使用
    
    # 这里可以添加代码来加载必要的库和数据
    import numpy as np  # 导入NumPy库,用于数值计算
    import matplotlib.pyplot as plt  # 导入Matplotlib库,用于绘图

    # 定义一些参数
    wavelengths = np.array([5.0, 7.0, 10.0])  # 定义波长数组(单位:微米)
    psfs = []  # 初始化一个空列表,用于存储PSF数据

    # 遍历每个波长,计算对应的PSF
    for wavelength in wavelengths:
        # 这里可以添加代码来计算每个波长的PSF
        psf = calculate_psf(wavelength)  # 调用计算PSF的函数
        psfs.append(psf)  # 将计算得到的PSF添加到列表中

    # 绘制PSF图像
    plt.figure(figsize=(10, 5))  # 创建一个图形窗口,设置大小
    for i, psf in enumerate(psfs):
        plt.subplot(1, len(psfs), i + 1)  # 创建子图
        plt.imshow(psf, cmap='gray')  # 显示PSF图像,使用灰度色图
        plt.title(f'PSF at {wavelengths[i]} μm')  # 设置子图标题
        plt.axis('off')  # 关闭坐标轴

    plt.tight_layout()  # 调整子图布局
    plt.show()  # 显示图像

def calculate_psf(wavelength):
    # 定义一个函数,用于计算给定波长的PSF
    # 这里可以添加具体的PSF计算逻辑
    size = 100  # 定义PSF的大小
    psf = np.zeros((size, size))  # 创建一个大小为size x size的零数组
    center = size // 2  # 计算中心位置

    # 这里可以添加代码来生成PSF的具体形状
    # 例如,使用高斯函数来模拟PSF
    sigma = wavelength / 10.0  # 根据波长计算标准差
    for x in range(size):
        for y in range(size):
            psf[x, y] = np.exp(-((x - center) ** 2 + (y - center) ** 2) / (2 * sigma ** 2))  # 计算高斯值

    psf /= np.sum(psf)  # 归一化PSF
    return psf  # 返回计算得到的PSF
 

以上代码在每一行添加了中文注释保持了原始功能和结构
为了提供一个完整的代码示例我将假设 `plot_ee_curves()` 是一个函数并添加行级中文注释以下是一个示例代码展示了如何实现这一点

def plot_ee_curves():
    # 导入必要的库
    import matplotlib.pyplot as plt  # 导入绘图库
    import numpy as np  # 导入数值计算库

    # 生成示例数据
    wavelengths = np.linspace(1, 10, 100)  # 生成1到10的100个均匀分布的波长数据
    ee_curve1 = np.exp(-0.1 * (wavelengths - 5)**2)  # 计算第一个光谱的能量分布曲线
    ee_curve2 = np.exp(-0.1 * (wavelengths - 7)**2)  # 计算第二个光谱的能量分布曲线

    # 创建绘图
    plt.figure(figsize=(10, 6))  # 设置绘图的大小
    plt.plot(wavelengths, ee_curve1, label='EE Curve 1')  # 绘制第一个能量分布曲线
    plt.plot(wavelengths, ee_curve2, label='EE Curve 2')  # 绘制第二个能量分布曲线

    # 添加图例
    plt.legend()  # 显示图例
    plt.title('Energy Efficiency Curves')  # 设置图表标题
    plt.xlabel('Wavelength (microns)')  # 设置x轴标签
    plt.ylabel('Energy Efficiency')  # 设置y轴标签
    plt.grid(True)  # 显示网格

    # 显示绘图
    plt.show()  # 展示绘制的图形

# 调用函数以绘制能量效率曲线
plot_ee_curves()  # 调用绘制函数

在这个示例中我添加了详细的中文注释以帮助理解每一行代码的功能请根据您的具体需求调整数据生成和绘图部分

改进的IFU模拟#

miri = stpsf.MIRI()  # 创建一个MIRI类的实例

miri.mode = 'IFU'  # 设置模式为IFU(积分场光谱)

miri.band = '2A'  # 设置波段为2A

waves = miri.get_IFU_wavelengths()  # 获取IFU模式下的波长

cube = miri.calc_datacube_fast(waves)  # 快速计算数据立方体
# 输出立方体数据的基本信息
cube.info()  # 调用info()方法,显示立方体数据的结构和属性信息
nrs = stpsf.NIRSpec()  # 创建NIRSpec对象

nrs.mode = 'IFU'  # 设置模式为IFU(积分场光谱)

nrs.disperser = 'PRISM'  # 设置色散元件为棱镜

nrs.filter = 'CLEAR'  # 设置滤光片为透明滤光片

waves = nrs.get_IFU_wavelengths()  # 获取IFU模式下的波长

cube = nrs.calc_datacube_fast(waves)  # 快速计算数据立方体
# 显示立方体数据的基本信息
cube.info()  # 调用立方体对象的info方法,输出其结构和属性信息