# -*- coding: utf-8 -*-
# @Time    : 04/12/2024 17.16
# @Author  : ljc
# @FileName: uly_tgm_init.py
# @Software: PyCharm


# 1. 简介
"""
 Python conversion of the IDL uly_tgm_init.pro .
目的:
    初始化一个 TGM (Teff-log g-[Fe/H]) 组件.
函数:
    1) uly_tgm_coef_load
    2) uly_tgm_init
解释:
    1) uly_tgm_coef_load 函数: 读取 TGM 系数, 并返回 TGM 模型信息的字典结构.
    2) uly_tgm_init 函数: 初始化 TGM 组件, 并返回初始化后的 cmp 组件字典结构.
"""


# 2. 导库
import numpy as np
from astropy.io import fits
from WRS.xrebin import xrebin
from file_paths import TGM_MODEL_FILE
import warnings
warnings.filterwarnings("ignore")


# 3. 获取包含 TGM 模型系数、波长等信息的字典结构
def uly_tgm_coef_load(filename=None) -> dict:

    """
      读取 TGM 文件, 并返回包含 TGM 模型系数、波长等信息的字典结构.

      输入参数：
      -----------
      filename:
               TGM 模型 (ELODIE 3.2 版本) 文件地址.

      输出参数:
      -----------
      s:
        包含 TGM 模型系数、波长等信息的字典结构.
    """

    # 3.1 指定 TGM 文件位置
    if filename is None:
        filename = TGM_MODEL_FILE()
   
    # 3.2 读取 TGM 文件
    with fits.open(filename) as hdul:
        # 3.2.1 warm 恒星 TGM 系数
        warm = hdul[0].data
        # 3.2.2 hot 恒星 TGM 系数
        hot = hdul[1].data
        # 3.2.3 cold 恒星 TGM 系数
        cold = hdul[2].data
        # 3.2.4 合并 warm、hot 和 cold 数组作为系数数组
        spec_coef = np.stack([warm, hot, cold], axis=-1)
        # 3.2.5 检查是否存在非有限值(如 NaN)
        spec_coef = np.nan_to_num(spec_coef)
        # 3.2.6 读取头信息
        hdr = hdul[0].header
        # 3.2.7 从头信息中获取必要的参数
        # 3.2.7.1 起始波长
        crval1 = hdr.get('CRVAL1', 0.0)
        # 3.2.7.2 步长
        cdelt1 = hdr.get('CDELT1', 1.0)
        # 3.2.7.3 流量维度
        naxis1 = hdr.get('NAXIS1', 0)
        # 3.2.7.4 空气波长, 单位是埃、线性的
        ctype1 = hdr.get('CTYPE1', 'AWAV')

    # 3.3 TGM 使用的是空气波长, samp=0 为非对数下的波长, 即线性的
    if ctype1 == 'AWAV':
        samp = 0
    # 3.4 1 为 ln 对数波长
    if ctype1 == 'AWAV-LOG':
        samp = 1

    # 3.5 MASK 掉 NaD 线, 默认的 MASK 表. 需要注意: LASP-MPFit 没有使用官方 fits 文件的 MASK 数组
    # 3.5.1 TGM 光谱的波长
    wl = crval1 + np.linspace(start=0, stop=naxis1 - 1, num=naxis1) * cdelt1
    # 3.5.2 一共 162 个点需要 MASK
    bad = np.where((wl >= 5876.87) & (wl <= 5909.35))[0].tolist()
    mask = np.zeros(naxis1, dtype=np.uint8) + 1
    mask[bad] = 0
    goodpix = np.where(mask == 1)[0].tolist()
    cnt = len(goodpix)
    if cnt == 0:
        None

    # 3.6 uly_tgm_init.pro 中第 1个 s 输出:
    # s=uly_spect_alloc(START=crval1, STEP=cdelt1, SAMPLING=samp, DATA=spec_coef, GOODPIX=goodpix)
    # 3.6.1 使用字典结构存储 TGM 模型信息
    s = {"title": "",              # 标题, 无.
         "hdr": hdr,               # TGM 文件的头文件信息.
         "data": spec_coef,        # TGM 模型系数.
         "err": "",                # 流量误差, 无.
         "wavelen": "",            # 非均匀采样, 无.
         "goodpix": goodpix,       # TGM 模型的默认好坏像素点标记.
         "start": crval1,          # TGM 模型波长起点 start.
         "step": cdelt1,           # TGM 模型波长步长 step.
         "naxis1": naxis1,         # TGM 模型的流量维度.
         "sampling": samp,         # TGM 模型的波长采样 sampling.
         "dof_factor": 1           # 自由度因子.
         }

    # 3.7 返回 TGM 模型信息
    return s


# 4. 初始化 TGM 组件的字典结构
def uly_tgm_init(cmp, LAMOST_lamrange=None, t_guess=None, l_guess=None, z_guess=None, velscale=69.029764, sampling=None, step=None, sampling_function=None) -> dict:

    """
      调用拟合各组件的初始化函数, 并返回初始化后的 cmp 组件字典结构.
      1) 单组件 cmp: 恒星参数初始猜测仅有 1 组.
      2) 多组件 cmp: 恒星参数初始猜测有多组 (目前支持 1 组).
      3) ULySS 多组件情况时, 最终推断的恒星参数不是选择各组件卡方最小时的参数, 而是基于多组参数加权得到的.
      因此, 使用多组件时, 需要设置权重 weight.
      4) LASP 根据 CFI 初始化恒星参数, 即初始猜测大多使用 1 组, 因此该代码适用于单组件情况! 如需要多组件设置, 请参考 IDL 原始代码.

      输入参数：
      -----------
      cmp:
          使用 uly_tgm 定义的组件, 为字典格式. 存储 TGM、待测参数初始值、勒让德多项式默认值等信息.
          cmp 是 1 个字典而不是字典列表, 因此 cmp 的长度记为 1.
      LAMOST_lamrange:
                      波长范围, 单位为埃.
      t_guess:
              Teff 的初始猜测.
      l_guess:
              log g 的初始猜测.
      z_guess:
              [Fe/H] 的初始猜测.
      velscale:
               LAMOST 波长间隔所对应的速度相关的尺度因子, 单位为 km/s.
               velscale = ln_step * 299792.458 = log10_step * ln(10) * 299792.458.
      sampling:
               波长采样.
               1) sampling == 0: 表示输入光谱结构的波长为线性采样.
               2) sampling == 1: 表示输入光谱结构的波长为 ln 对数采样.
               3) sampling == 2: 非均匀采样.
      sampling_function:
                       插值方法. 可输入 "splinf", "cubic", "slinear", "quadratic", "linear". 默认使用 "linear" 插值方法.
      step:
           波长间隔.

      输出参数:
      -----------
      cmp:
          更新后的 cmp 字典结构.
    """

    # 4.1 使用字典定义 uly_tgm.pro 中的数据结构, 并设置一些默认值
    if cmp is None:
        cmp = {"name": "",                                                 # TGM 模型地址与名称.
               "init_fun": "uly_tgm_init",                                 # 初始化函数
               "init_data": {"model": TGM_MODEL_FILE(),                    # 传递给初始化函数的数据, 包含模型文件、LSF 文件及重采样选项.
                             "lsf_file": "no_lsf",                         # 降低分辨率的文件, LASP 没有使用线宽函数.
                             "rebin_coef": 0},                             # 重采样选项.
               "eval_fun": "",                                             # 计算 TGM 光谱的评估函数, 调用 uly_tgm_eval 函数.
               "eval_data": "",                                            # 传递给评估函数的数据, 存储 TGM 模型信息, 如系数、波长等.
               "para": "",                                                 # 待测恒星参数.
               "t_limits": np.log([3100.0, 40000.]),                       # Teff 默认范围.
               "l_limits": [-0.25, 5.9],                                   # log g 默认范围.
               "z_limits": [-2.5, 1.0],                                    # [Fe/H] 默认范围.
               "t_guess": "",                                              # Teff 的初始猜测.
               "l_guess": "",                                              # log g 的初始猜测.
               "z_guess": "",                                              # [Fe/H]的初始猜测.
               "start": 0,                                                 # 待测光谱的波长起点, 初始设置, 后续会更新.
               "step": 0,                                                  # 待测光谱的波长步长, 初始设置, 后续会更新.
               "npix": 0,                                                  # 待测光谱的流量维度, 初始设置, 后续会更新.
               "sampling": -1,                                             # TGM 模型波长采样模式(0: 线性, 1: 对数, 2: 非均匀波长).
               "mask": "",                                                 # 像素掩码, 标记好坏像素点.
               "weight": 0,                                                # LASP 使用的是单组件, 不是多组件, 即权重设置不起作用.
               "e_weight": 0,                                              # 权重误差.
               "l_weight": 0,                                              # 流量比重.
               "lim_weight": np.finfo(np.float64).max * np.array([0, 1]),  # 权重上下限.
               "mod_samp": "",                                             # 模型波长采样模式.
               "mod_start": "",                                            # 模型波长起点.
               "mod_step": "",                                             # 模型波长步长.
               "lsf": "no_lsf",                                            # 降低分辨率的文件, LASP 没有使用线宽函数.
               "version": 2,                                               # LASP 所用 ELODIE 插值器版本.
               "calibration": "B",                                         # 勒让德多项式默认值.
               "spec_coef": "",                                            # TGM 系数.
               }
    
    # 4.2 初始化 cmp 字典
    init_data = cmp["init_data"]
    cmp["eval_fun"] = 'uly_tgm_eval'

    # 4.3 判断 TGM 模型是否存在
    if len(init_data["model"]) < 1:
        raise ValueError('Error, model file does not exist!')

    # 4.4 读取 TGM 模型的 fits 文件
    s = uly_tgm_coef_load()
    # 4.4.1 获取 TGM 头文件中的波长信息
    uly_type = s["hdr"].get('ULY_TYPE', None)
    if uly_type is not None:
        uly_type = uly_type.strip()
        cnt = len(uly_type)
        if (cnt == 1) & (uly_type != 'TGM'):
            raise ValueError('Invalid model file, expect ULY_TYPE=TGM, get ' + uly_type + '!')
    # 4.4.2 获取 TGM 头文件中的波长信息
    naxis1 = s["hdr"].get('NAXIS1', None)
    naxis2 = s["hdr"].get('NAXIS2', None)
    ctype1 = s["hdr"].get('CTYPE1', None)
    crval1 = s["hdr"].get('CRVAL1', None)
    cdelt1 = s["hdr"].get('CDELT1', None)
    version = s["hdr"].get('INTRP_V', None)
    # 4.4.3 判断版本号
    if version == None:
        version = 1
    calibration = s["hdr"].get('INTRP_C', None)
    if version != None:
        if int(version) <= 2:
            if naxis2 < 23:
                raise ValueError('Invalid interpolator format for version=' + str(version) + 'naxis2=' + str(naxis2) + ' (' + init_data["model"] + ')!')
            
        if int(version) == 3:
            if naxis2 < 26:
                raise ValueError('Invalid interpolator format for version=' + str(version) + '!')

    # 4.5 设置恒星参数边界
    teffrange, loggrange, fehrange = np.log([3100.0, 40000.]), [-0.25, 5.9], [-2.5, 1.0]
   
    # 4.6 设置波长起始点
    wr = [s["start"], s["start"] + (s["naxis1"]-1) * s["step"]]
    if ctype1 == "AWAV-LOG":
        # 4.6.1 将波长转换为线性波长
        wr = np.exp(wr)
    if len(LAMOST_lamrange) > 0:
        # 4.6.2 获取 TGM 光谱波长与设置的 LAMOST 波长的交集
        wr[0] = max([LAMOST_lamrange[0], wr[0]])
    if len(LAMOST_lamrange) > 1:
        # 4.6.3 获取 TGM 光谱波长与设置的 LAMOST 波长的交集
        wr[1] = min([LAMOST_lamrange[1], wr[1]])

    # 4.7 TGM 包含暖、热、冷星
    # 注意: 下属代码块主要实现 TGM 模型光谱按 LAMOST 波长范围进行重采样
    # 1) 获得 TGM 模型波长、流量、波长范围、波长步长、波长采样模式等信息
    # 2) 获得 TGM 模型波长范围, 计算光谱积分
    # 3) 获得 LAMOST 光谱波长范围, 并在积分谱中重采样
    # 4) 差分得到真实 LAMOST 波长的重采样光谱
    # 5) 更新 TGM 模型波长范围、波长步长、波长采样模式等信息, 并返回初始化后的 cmp 组件字典结构
    ndim = 3
    if len(wr) != 0:
        if len(wr) > 2:
            raise ValueError('WAVERANGE must be a 1 or 2 elements list!')
        # 4.7.1 波长范围
        range_ = [s["start"], s["start"] + (s["naxis1"]-1) * s["step"]]
        if range_[1] < wr[0]:
            raise ValueError("WAVERANGE is out of bounds!")
        if len(wr) == 2:
            if wr[0] > wr[1]:
                raise ValueError('WAVERANGE must be ordered low < high!')
        if len(wr) == 2:
            if range_[0] > wr[1]:
                raise ValueError('WAVERANGE is out of bounds!')

        # 4.7.2 线性或 ln 对数波长
        if (s["sampling"] == 0) | (s["sampling"] == 1):
            if s["sampling"] == 1:
                wr = np.log(wr)
            # 4.7.2.1 光谱左侧 MASK 多少点, 根据 ln 对数波长计算
            # 注意: 
            # 1) step 为 LAMOST 的 log10 对数波长间隔转为了 ln 对数波长间隔, 即 ln_step = ln(10) * log10_step
            # 2) 为了避免多删除流量点, 使用 np.floor 向下取整, 而不是 np.ceil 向上取整
            # 3) np.floor 向下取整, 为了避免计算误差, 形如 1.999999 被取为 1 的情况, 加上 0.01 作为补偿
            nummin = int(np.floor((wr[0] - s["start"]) / s["step"] + 0.01))
            if nummin < 0:
                nummin = 0
            npix = s["data"].shape[1]
            # 4.7.2.2 光谱右侧 MASK 多少点, 根据 ln 对数波长计算
            # 注意: 
            # 1) 为了避免多删除流量点, 使用 np.ceil 向上取整, 而不是 np.floor 向下取整
            # 2) np.ceil 向上取整, 为了避免计算误差, 形如 2.0000001 被取为 3 的情况, 减去 0.01 作为补偿
            if len(wr) == 2:
                nummax = int(np.ceil((wr[1] - s["start"]) / s["step"] - 0.01))
                if nummax >= s["naxis1"]:
                    nummax = int(s["naxis1"]) - 1
            # 4.7.2.3 更新TGM波长起点
            s["start"] = s["start"] + nummin * s["step"]

            # 4.7.2.4 好坏像素点标记
            if len(s["goodpix"]) != 0:
                msk = np.zeros(s["data"].shape[1], dtype=np.uint8)
                if len(s["goodpix"]) > 0:
                    msk[s["goodpix"]] = 1
                else:
                    msk = msk + 1
                msk = msk[nummin: nummax + 1]
                # 4.7.2.4.1 更新 TGM 的好像素点
                s["goodpix"] = np.where(msk == 1)[0].tolist()
                # 4.7.2.4.2 更新 TGM 的系数矩阵 spec_coef, 从 (26, 14501, 3) 到 (26, 7506, 3)
                s["data"] = s["data"][:, nummin: nummax + 1, :]

        # 4.7.2.5 cmp 中存储的模型数据描述(波长范围、采样和步长)
        # 4.7.2.5.1 TGM 模型波长采样模式
        mod_samp = s["sampling"]
        # 4.7.2.5.2 更新后的 TGM 波长起点
        mod_start = s["start"]
        # 4.7.2.5.3 更新后的 TGM 波长步长
        mod_step = s["step"]
        # 4.7.2.5.4 更新后的 TGM 系数
        spec_coef = s["data"]
        # 4.7.2.5.5 待测光谱使用 ln 对数波长
        cmp_sampling = 1
        if sampling != None:
            cmp_sampling = sampling
        if cmp_sampling == mod_samp:
            cmp_step = mod_step
        if velscale != None:
            if cmp_sampling != 1:
                raise ValueError("Inconsistency in the arguments!")
            # 4.7.2.5.6 ln 对数下的波长间隔
            cmp_step = velscale/299792.458
        if step != None:
            cmp_step = step

        # 4.7.2.5.7 TGM 光谱的波长重采样插值到 LAMOST
        resam = 0
        if len(LAMOST_lamrange) > 0:
            if wr[0] != LAMOST_lamrange[0]:
                resam = 1
        if len(LAMOST_lamrange) > 1:
            if wr[1] != LAMOST_lamrange[1]:
                resam = 1
        # 4.7.2.5.8 插值到 LAMOST 波长, mod_step=0.2, cmp_sampling=1, mod_step=0.2, cmp_step=0.0002302585092994046, resam=0
        if (mod_step != cmp_sampling) | (mod_step != cmp_step) | (resam == 1):
            pos, ndim = 0, s["data"].shape[2]
            if (pos is not None) & (ndim == 1):
                if pos != 0:
                    raise ValueError("Invalid ONED position!")
            if pos is not None:
                if (1 >= ndim) & (ndim > 1):
                    raise ValueError("Invalid ONED position!")
                # 4.7.2.5.8.1 形状 [7506, 26,3]
                dim = [s["data"].shape[1], s["data"].shape[0], s["data"].shape[2]]
                sdim = [1, 26, 78]
                # 4.7.2.5.8.2 形状 7506 * 26 * 3
                ntot = int(s["data"].shape[0] * s["data"].shape[1] * s["data"].shape[2])
                # 4.7.2.5.8.3 索引
                ind = sum([pos] * sdim[0] * sdim[1] * sdim[2])
                # 4.7.2.5.8.4 形状 (26, 7506, 3)
                spec_coef_0, spec_coef_1, spec_coef_2 = s["data"].shape[0], s["data"].shape[1], s["data"].shape[2]
                # 4.7.2.5.8.5 形状 7506 * 1
                s["data"] = s["data"][0, :, ind].reshape(-1, ind + 1)
                # 4.7.2.5.8.6 形状 7506 * 1
                msk = np.zeros(s["data"].shape[0] * s["data"].shape[1], dtype=np.uint8)
                if len(s["goodpix"]) > 0:
                    msk[s["goodpix"]] = 1
                else:
                    msk = msk + 1
                if len(msk) == ntot:
                    msk = msk.reshape(ntot/spec_coef_1, -1)[:, ind]
                    s["goodpix"] = np.where(msk == 1)[0].tolist()

            # 4.7.2.5.9 线性波长
            if cmp_sampling == 0:
                del step
                if cmp_step != 0:
                    step = cmp_step

            # 4.7.2.5.10 待测光谱使用 ln 对数波长(LAMOST 使用 log10 对数波长, 需要转换)
            if cmp_sampling == 1:
                del velscale
                if cmp_step != 0:
                    # 4.7.2.5.10.1 光速
                    c = 299792.458
                    # 4.7.2.5.10.2 光速 * ln 对数波长间隔
                    velscale = cmp_step * c
                    # 4.7.2.5.10.3 TGM 模型光谱的流量维度
                    npix = s["data"].shape[0]
                    # 4.7.2.5.10.4 TGM 模型默认是线性等间隔波长!!!!!!
                    # 注意: cmp_sampling == 1 表示待测光谱使用 ln 对数波长
                    # 1) s["sampling"] == 0 表示 TGM 模型是线性等间隔波长, 此时 s["start"] 和 s["step"] 是线性波长起点与间隔, 间隔为 0.2
                    # 2) s["sampling"] == 1 表示 TGM 模型是 ln 对数波长, 此时 s["start"] 和 s["step"] 是 ln 对数波长起点与间隔
                    # 3) 无论 s["sampling"] 是 0 还是 1, 使用 borders 作为模型波长边界进行插值
                    # 4) 波长重采样分为 4 步: 尽可能保证重采样光谱的流量积分与原始光谱的流量积分值相等, 可参考文献 https://arxiv.org/pdf/1705.05165
                        # 4.1) 计算原始波长边界与新波长边界: 确定每个像素的波长边界, 而不仅仅是中心波长
                        # 4.2) 计算原始波长的流量积分: 构建累积通量函数, 将离散数据转换为连续表示(见 xrebin.py 中的 xrebin 函数)
                        # 4.3) 将流量积分插值到新波长边界: 在新的波长网格边界上采样累积通量函数(见 xrebin.py 中的 xrebin 函数)
                        # 4.4) 差分得到重采样光谱: 计算相邻边界点间的通量差值并除以波长间隔, 得到新网格上的光谱强度(见 xrebin.py 中的 xrebin 函数)

                    # 4.7.2.5.10.5 线性波长
                    if s["sampling"] == 0:
                        # 计算波长边界
                        indxarr = np.linspace(start=0, stop=npix - 1, num=npix)
                        borders = s["start"] + np.array([-0.5, *(indxarr + 0.5)]) * s["step"]
                        bordersLog = np.log(borders)
                    # 4.7.2.5.10.6 ln 对数波长
                    if s["sampling"] == 1:
                        # 计算波长边界
                        indxarr = np.linspace(start=0, stop=npix - 1, num=npix)
                        bordersLog = s["start"] + np.array([-0.5, *(indxarr + 0.5)]) * s["step"]
                        borders = np.exp(bordersLog)

                    # 4.7.2.5.10.7 TGM 模型的线性波长转为 ln 对数波长
                    logScale = velscale / c
                    # logRange 是 borders 的波长范围
                    logRange = s["start"] + np.array([-0.5, (spec_coef_1 - 0.5)]) * s["step"]
                    if s["sampling"] == 0:
                        logRange = np.log(logRange)
                    # 将 TGM 模型光谱的 ln 对数波长左右收缩 0.5 倍的 LAMOST ln 对数波长间隔
                    logRange += np.array([0.5, -0.5]) * logScale
                    # 4.7.2.5.10.8 匹配 logRange 与 LAMOST_lamrange 的共同波长范围
                    if len(LAMOST_lamrange) > 0:
                        # 4.7.2.5.10.8.1 计算 logRange 与 LAMOST_lamrange 的共同波长范围
                        nshift = np.ceil(np.max(np.array([0, (logRange[0] - np.log(LAMOST_lamrange[0])) / logScale - 10**(-7)])))
                        logRange[0] = np.log(LAMOST_lamrange[0]) + logScale * nshift
                        if len(LAMOST_lamrange) == 2:
                            logRange[1] = np.min([np.log(LAMOST_lamrange[1]), logRange[1]])
                        if logRange[1] < logRange[0]:
                            raise ValueError("waverange is not valid!")
                    # 4.7.2.5.10.9 待重采样的 LAMOST 光谱的波长范围
                    nout = np.round((logRange[1] - logRange[0]) / logScale + 1)
                    # 待重采样的 LAMOST 光谱的波长起点
                    logStart = logRange[0]

                    if s["sampling"] < 2:
                        if s["sampling"] == 0:
                            logRange = np.exp(logRange)
                        # 流量维度
                        nin = np.round((logRange[1] - logRange[0]) / s["step"] + 1)
                    # 4.7.2.5.10.10 自由度因子, 如: 1327 / 7498 = 0.17698053
                    dof_factor = nout / nin

                    # 4.7.2.5.10.11 判断 logStart 是否在 TGM 模型的波长范围内
                    if (logStart - logScale / 2) > bordersLog[npix]:
                        raise ValueError("start value is not in the valid range!")

                    # 4.7.2.5.10.12 重采样后的 TGM 光谱的波长范围
                    NewBorders = np.exp(logStart + (np.arange(nout + 1) - 0.5) * logScale)
                    dim = s["data"].shape[0]
                    n_data = s["data"].shape[0] * s["data"].shape[1]
                    n_dim = s["data"].shape[1]
                
                    # LASP-MPFit 使用的样条插值
                    # 注意: 
                    # 1) 样条插值与线性差值所推断的恒星参数几乎无差异!!!!!!
                    # 2) flat 确定用于处理像素大小变化的转换因子/向量. 
                    # 在对数重采样过程中, 像素的波长范围会发生变化. 在对数采样中, 像素的波长范围是指数增长的, 而在线性采样中, 像素的波长范围是线性增长的.
                    # 因此, 在对数重采样过程中, 需要使用 flat 来处理像素大小变化的转换因子/向量. flat 变量就是用来校正这种像素大小变化导致的强度变化的转换因子.
                    if s["sampling"] == 0:
                      flat = np.exp(logStart + np.arange(nout) * logScale) * logScale / s["step"]

                    # 4.7.2.5.10.13 重采样后的 TGM 光谱
                    # s["data"] = xrebin(borders, s["data"], NewBorders, sampling_function="splinf") / flat
                    s["data"] = xrebin(borders,                             # 原始波长边界
                                       s["data"],                           # 原始光谱
                                       NewBorders,                          # 新波长边界
                                       sampling_function=sampling_function  # 插值函数
                                       ) / flat

                    # 4.7.2.5.10.14 对 TGM 的 MASK 插值
                    if len(s["goodpix"]) > 0:
                        maskI = np.zeros(n_data, dtype=np.uint8)
                        maskI[s["goodpix"]] = 1
                        # maskO = xrebin(borders, maskI.reshape(n_data, -1), NewBorders, sampling_function="splinf") / flat
                        maskO = xrebin(borders,                             # 原始波长边界
                                       maskI.reshape(n_data, -1),           # 原始 MASK
                                       NewBorders,                          # 新波长边界
                                       sampling_function=sampling_function  # 插值函数
                                       ) / flat
                        s["goodpix"] = np.where(np.abs(maskO - 1) < 0.1)[0].tolist()

                    # 4.7.2.5.10.15 更新 TGM 的波长起点: 线性转为 ln 对数波长起点
                    s["start"] = logStart
                    # 4.7.2.5.10.16 更新 TGM 的波长步长: 线性转为 ln 对数波长步长
                    s["step"] = logScale
                    # 4.7.2.5.10.17 更新 TGM 的波长采样模式: 线性转为 ln 对数波长采样模式
                    s["sampling"] = 1
                    # 4.7.2.5.10.18 更新 TGM 的自由度因子
                    s["dof_factor"] = max([1,  s["dof_factor"] * dof_factor])

                    # 4.7.2.5.10.19 更新 TGM 的 MASK
                    if len(s["goodpix"]) > 0:
                        cmp_mask = np.zeros(s["data"].shape[0], dtype=np.uint8)
                        cmp_mask[s["goodpix"]] = 1

    # 4.8 初始化输入评估函数 uly_tgm_eval 的数据
    eval_data = {"spec_coef": spec_coef,    # TGM 模型光谱系数.
                 "start": s["start"],       # 更新后的 TGM 模型的 ln 对数波长起点.
                 "step": s["step"],         # 更新后的 TGM 模型的 ln 对数波长步长.
                 "npix": s["data"].shape[0],# 重采样后 TGM 模型的流量维度.
                 "sampling": s["sampling"], # 重采样后 TGM 模型的波长采样模式.
                 "mod_samp": mod_samp,      # TGM 线性模型波长采样模式.
                 "mod_start": mod_start,    # TGM 线性模型波长起点.
                 "mod_step": mod_step,      # TGM 线性模型波长步长.
                 "lsf": "no_lsf",           # 无 LSF.
                 "version": version,        # ELODIE 版本.
                 "calibration": calibration # 校准.
                 }

    # 4.9 初始化 cmp 组件字典信息
    cmp["eval_data"] = eval_data            # 评估函数 uly_tgm_eval 的数据.
    cmp["start"] = s["start"]               # 更新后的 TGM 模型的 ln 对数波长起点.
    cmp["step"] = s["step"]                 # 更新后的 TGM 模型的 ln 对数波长步长.
    cmp["npix"] = s["data"].shape[0]        # 重采样后 TGM 模型的流量维度.
    cmp["sampling"] = s["sampling"]         # 重采样后 TGM 模型的波长采样模式.
    cmp["mask"] = cmp_mask                  # 重采样后 TGM 模型的 MASK.
    cmp["t_limits"] = np.log(teffrange)     # Teff 边界.
    cmp["l_limits"] = loggrange             # log g 边界.
    cmp["z_limits"] = fehrange              # [Fe/H] 边界.
    cmp["t_guess"] = t_guess                # Teff 初始猜测.
    cmp["l_guess"] = l_guess                # log g 初始猜测.
    cmp["z_guess"] = z_guess                # [Fe/H] 初始猜测.
    cmp["init_fun"] = "uly_tgm_init"        # 初始化函数.

    # 4.10 返回已初始化的 cmp 组件信息
    return cmp