#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# from scipy.integrate import simps
import math
import sys
import os
from astropy import units as u
from astropy.io import fits
from astropy.table import Table
from tqdm import tqdm
import numpy as np
from astropy.coordinates import get_sun
from astropy.time import Time
from datetime import datetime, timedelta
import pandas as pd
import galsim
import configparser as ConfigParser
from scipy import ndimage
from astropy.coordinates import SkyCoord
from scipy import interpolate
from scipy.signal import fftconvolve
import pandas
import ctypes
import astropy.coordinates as coord
from scipy.interpolate import interp1d
import scipy.spatial as spatial
from ctypes import *
# import sc
# from matplotlib import pyplot as plt
# from joblib import Parallel, delayed

"""
Created on Mon Oct 18 09:38:35 2021

@author: yan, zhaojun"""

"""
The CSST MCI Image Simulator
=============================================

This file contains an image simulator for the CSST MCI.

The approximate sequence of events in the simulator is as follows:

      #. Read in a configuration file, which defines for example,
         detector characteristics (bias, dark and readout noise, gain,
         plate scale and pixel scale, exposure time etc.).
      #. Read in another file containing charge trap definitions (for CTI modelling).
      #. Read in a file defining the cosmic rays (trail lengths and cumulative distributions).
      #
      #
      #. Loop over the number of exposures to co-add and for each object in the object catalog:
      #. Apply a multiplicative flat-field map to emulate pixel-to-pixel non-uniformity [optional].

      #. Add cosmic ray tracks onto the CCD with random positions but known distribution [optional].
      #. Apply detector charge bleeding in column direction [optional].
      #. Add constant dark current  [optional].

      #. Add photon (Poisson) noise [optional]
      #. Add cosmetic defects from an input file [optional].
      #. Add overscan regions in the serial direction [optional].
      #. Apply the CDM03 radiation damage model [optional].
      #. Apply non-linearity model to the pixel data [optional].
      #. Add readout noise selected [optional].
      #. Convert from electrons to ADUs using a given gain factor.
      #. Add a given bias level and discretise the counts (the output is going to be in 16bit unsigned integers).
      #. Finally the simulated image is converted to a FITS file, a WCS is assigned
         and the output is saved to the current working directory.
      #
      # add galaxy lensing effect to self.information['lensing'], set this parameter in .config file; update@24.10.16

.. Warning:: The code is still work in progress and new features are being added.
             The code has been tested, but nevertheless bugs may be lurking in corners, so
             please report any weird or inconsistent simulations to the author.


Contact Information: zhaojunyan@shao.ac.cn
########
------

"""

######################
try:
    from support import logger as lg
    from support import cosmicrays
    # from support import shao
    from support import sed
    from support import MCIinstrumentModel
    from mci_so import cdm03bidir
except:
    from .support import logger as lg
    from .support import cosmicrays
    # from .support import shao
    from .support import sed
    from .support import MCIinstrumentModel
    from .mci_so import cdm03bidir

####################################################


def checkInputList(input_list, n):
    if isinstance(type(input_list), list):
        # if type(input_list) != type([1, 2, 3]):
        raise TypeError("Input type is not list!", input_list)
    for i in input_list:
        if isinstance(type(i), float):      # type(i) != type(1.1):
            if isinstance(type(i), int):    # type(i) != type(1):
                raise TypeError(
                    "Input list's element is not float or int!", input_list)
    if len(input_list) != n:
        raise RuntimeError(
            "Length of input list is not equal to stars' number!", input_list)


def onOrbitObsPosition(path, input_ra_list, input_dec_list, input_pmra_list, input_pmdec_list, input_rv_list,
                       input_parallax_list, input_nstars, input_x, input_y, input_z, input_vx, input_vy,
                       input_vz, input_epoch, input_date_str, input_time_str):
    # Check input parameters
    if isinstance(type(input_nstars), type(1)):  # type(input_nstars) != type(1):
        raise TypeError("Parameter 7 is not int!", input_nstars)

    # checkInputList(input_ra_list, input_nstars)
    # checkInputList(input_dec_list, input_nstars)
    # checkInputList(input_pmra_list, input_nstars)
    # checkInputList(input_pmdec_list, input_nstars)
    # checkInputList(input_rv_list, input_nstars)
    # checkInputList(input_parallax_list, input_nstars)

    # if isinstance(type(input_x), type(1.1)):   # type(input_x) != type(1.1):
    #     raise TypeError("Parameter 8 is not double!", input_x)
    # if isinstance(type(input_y), type(1.1)):   # type(input_y) != type(1.1):
    #     raise TypeError("Parameter 9 is not double!", input_y)
    # if isinstance(type(input_z), type(1.1)):   # type(input_z) != type(1.1):
    #     raise TypeError("Parameter 10 is not double!", input_z)
    # if isinstance(type(input_vx), type(1.1)):  # type(input_vx) != type(1.1):
    #     raise TypeError("Parameter 11 is not double!", input_vx)
    # if isinstance(type(input_vy), type(1.1)):  # type(input_vy) != type(1.1):
    #     raise TypeError("Parameter 12 is not double!", input_vy)
    # if isinstance(type(input_vz), type(1.1)):  # type(input_vz) != type(1.1):
    #     raise TypeError("Parameter 13 is not double!", input_vz)

    # Convert km -> m
    input_x = input_x*1000.0
    input_y = input_y*1000.0
    input_z = input_z*1000.0
    input_vx = input_vx*1000.0
    input_vy = input_vy*1000.0
    input_vz = input_vz*1000.0

    if isinstance(type(input_date_str), type("2025-03-05")):
        # type(input_date_str) != type("2025-03-05"):
        raise TypeError("Parameter 15 is not string!", input_date_str)
    else:
        input_date_str = input_date_str.strip()
        if not (input_date_str[4] == "-" and input_date_str[7] == "-"):
            raise TypeError("Parameter 15 format error (1)!", input_date_str)
        else:
            tmp = input_date_str.split("-")
            if len(tmp) != 3:
                raise TypeError(
                    "Parameter 15 format error (2)!", input_date_str)
            input_year = int(tmp[0])
            input_month = int(tmp[1])
            input_day = int(tmp[2])
            if not (input_year >= 1900 and input_year <= 2100):
                raise TypeError(
                    "Parameter 15 year range error [1900 ~ 2100]!", input_year)
            if not (input_month >= 1 and input_month <= 12):
                raise TypeError(
                    "Parameter 15 month range error [1 ~ 12]!", input_month)
            if not (input_day >= 1 and input_day <= 31):
                raise TypeError(
                    "Parameter 15 day range error [1 ~ 31]!", input_day)

    if isinstance(type(input_time_str), type("20:15:15.15")):
        # type(input_time_str) != type("20:15:15.15"):
        raise TypeError("Parameter 16 is not string!", input_time_str)
    else:
        input_time_str = input_time_str.strip()
        if not (input_time_str[2] == ":" and input_time_str[5] == ":"):
            raise TypeError("Parameter 16 format error (1)!", input_time_str)
        else:
            tmp = input_time_str.split(":")
            if len(tmp) != 3:
                raise TypeError(
                    "Parameter 16 format error (2)!", input_time_str)
            input_hour = int(tmp[0])
            input_minute = int(tmp[1])
            input_second = float(tmp[2])
            if not (input_hour >= 0 and input_hour <= 23):
                raise TypeError(
                    "Parameter 16 hour range error [0 ~ 23]!", input_hour)
            if not (input_minute >= 0 and input_minute <= 59):
                raise TypeError(
                    "Parameter 16 minute range error [0 ~ 59]!", input_minute)
            if not (input_second >= 0 and input_second < 60.0):
                raise TypeError(
                    "Parameter 16 second range error [0 ~ 60)!", input_second)
    # Inital dynamic lib

    import os
    currfile = os.getcwd()
    print(currfile)

    shao = cdll.LoadLibrary(path+'MCI_inputData/TianCe/libshao.so')

    shao.onOrbitObs.restype = c_int

    d3 = c_double * 3
    shao.onOrbitObs.argtypes = [c_double, c_double, c_double, c_double, c_double, c_double,
                                c_int, c_int, c_int, c_int, c_int, c_double,
                                c_double, POINTER(d3), POINTER(d3),
                                c_int, c_int, c_int, c_int, c_int, c_double,
                                POINTER(c_double), POINTER(c_double)]
    output_ra_list = list()
    output_dec_list = list()
    for i in range(input_nstars):
        input_ra = c_double(input_ra_list[i])
        input_dec = c_double(input_dec_list[i])
        input_pmra = c_double(input_pmra_list[i])
        input_pmdec = c_double(input_pmdec_list[i])
        input_rv = c_double(input_rv_list[i])
        input_parallax = c_double(input_parallax_list[i])
        p3 = d3(input_x, input_y, input_z)
        v3 = d3(input_vx, input_vy, input_vz)
        input_year_c = c_int(input_year)
        input_month_c = c_int(input_month)
        input_day_c = c_int(input_day)
        input_hour_c = c_int(input_hour)
        input_minute_c = c_int(input_minute)
        input_second_c = c_double(input_second)
        DAT = c_double(37.0)
        output_ra = c_double(0.0)
        output_dec = c_double(0.0)
        rs = shao.onOrbitObs(input_ra, input_dec, input_parallax, input_pmra, input_pmdec, input_rv,
                             input_year_c, input_month_c, input_day_c, input_hour_c, input_minute_c, input_second_c,
                             DAT, byref(p3), byref(v3),
                             input_year_c, input_month_c, input_day_c, input_hour_c, input_minute_c, input_second_c,
                             byref(output_ra), byref(output_dec))
        if rs != 0:
            raise RuntimeError("Calculate error!")
        output_ra_list.append(output_ra.value)
        output_dec_list.append(output_dec.value)

    return output_ra_list, output_dec_list
# ###########################################################

# functions


def get_file_extension(filename):
    # 使用os.path.splitext()函数分割文件名和扩展名
    file_name, extension = os.path.splitext(filename)
    return extension
##########################################################


"""
Charge Transfer Inefficiency
============================
This file contains a simple class to run a CDM03 CTI model developed by Alex Short (ESA).

This now contains both the official CDM03 and a new version that allows different trap
parameters in parallel and serial direction.

:requires: NumPy
:requires: CDM03 (FORTRAN code, f2py -c -m cdm03bidir cdm03bidir.f90)

:version: 0.35
"""

# CDM03bidir


class CDM03bidir():
    """
    Class to run CDM03 CTI model, class Fortran routine to perform the actual CDM03 calculations.

     :param settings: input parameters
     :type settings: dict
     :param data: input data to be radiated
     :type data: ndarray
     :param log: instance to Python logging
     :type log: logging instance
    """

    def __init__(self, settings, data, log=None):
        """
        Class constructor.

        :param settings: input parameters
        :type settings: dict
        :param data: input data to be radiated
        :type data: ndarray
        :param log: instance to Python logging
        :type log: logging instance
        """
        self.data = data
        self.values = dict(quads=(0, 1, 2, 3), xsize=2048,
                           ysize=2066, dob=0.0, rdose=8.0e9)
        self.values.update(settings)
        self.log = log
        self._setupLogger()

        # default CDM03 settings
        self.params = dict(beta_p=0.6, beta_s=0.6, fwc=200000., vth=1.168e7, vg=6.e-11, t=20.48e-3,
                           sfwc=730000., svg=1.0e-10, st=5.0e-6, parallel=1., serial=0.0)
        # update with inputs
        self.params.update(self.values)

        # read in trap information
        trapdata = np.loadtxt(
            self.values['dir_path']+self.values['paralleltrapfile'])
        if trapdata.ndim > 1:
            self.nt_p = trapdata[:, 0]
            self.sigma_p = trapdata[:, 1]
            self.taur_p = trapdata[:, 2]
        # else:
        #     # only one trap species
        #     self.nt_p = [trapdata[0],]
        #     self.sigma_p = [trapdata[1],]
        #     self.taur_p = [trapdata[2],]

        trapdata = np.loadtxt(
            self.values['dir_path']+self.values['serialtrapfile'])
        if trapdata.ndim > 1:
            self.nt_s = trapdata[:, 0]
            self.sigma_s = trapdata[:, 1]
            self.taur_s = trapdata[:, 2]
        # else:
        #     # only one trap species
        #     self.nt_s = [trapdata[0],]
        #     self.sigma_s = [trapdata[1],]
        #     self.taur_s = [trapdata[2],]

        # # scale thibaut's values
        # if 'thibaut' in self.values['parallelTrapfile']:
        #     self.nt_p /= 0.576  # thibaut's values traps / pixel
        #     self.sigma_p *= 1.e4  # thibaut's values in m**2
        # if 'thibaut' in self.values['serialTrapfile']:
        #     self.nt_s *= 0.576  # thibaut's values traps / pixel  #should be division?
        #     self.sigma_s *= 1.e4  # thibaut's values in m**2

    def _setupLogger(self):
        """
        Set up the logger.
        """
        self.logger = True
        # if self.log is None:
        #     self.logger = False

    def applyRadiationDamage(self, data, iquadrant=0):
        """
        Apply radian damage based on FORTRAN CDM03 model. The method assumes that
        input data covers only a single quadrant defined by the iquadrant integer.

        :param data: imaging data to which the CDM03 model will be applied to.
        :type data: ndarray

        :param iquandrant: number of the quadrant to process
        :type iquandrant: int

        cdm03 - Function signature::

              sout = cdm03(sinp,iflip,jflip,dob,rdose,in_nt,in_sigma,in_tr,[xdim,ydim,zdim])
            Required arguments:
              sinp : input rank-2 array('d') with bounds (xdim,ydim)
              iflip : input int
              jflip : input int
              dob : input float
              rdose : input float
              in_nt : input rank-1 array('d') with bounds (zdim)
              in_sigma : input rank-1 array('d') with bounds (zdim)
              in_tr : input rank-1 array('d') with bounds (zdim)
            Optional arguments:
              xdim := shape(sinp,0) input int
              ydim := shape(sinp,1) input int
              zdim := len(in_nt) input int
            Return objects:
              sout : rank-2 array('d') with bounds (xdim,ydim)

        .. Note:: Because Python/NumPy arrays are different row/column based, one needs
                  to be extra careful here. NumPy.asfortranarray will be called to get
                  an array laid out in Fortran order in memory. Before returning the
                  array will be laid out in memory in C-style (row-major order).

        :return: image that has been run through the CDM03 model
        :rtype: ndarray  """""
        #
        # return data

        iflip = iquadrant / 2
        jflip = iquadrant % 2

        params = [self.params['beta_p'], self.params['beta_s'], self.params['fwc'], self.params['vth'],
                  self.params['vg'], self.params['t'], self.params['sfwc'], self.params['svg'], self.params['st'],
                  self.params['parallel'], self.params['serial']]

        if self.logger:
            self.log.info('nt_p=' + str(self.nt_p))
            self.log.info('nt_s=' + str(self.nt_s))
            self.log.info('sigma_p= ' + str(self.sigma_p))
            self.log.info('sigma_s= ' + str(self.sigma_s))
            self.log.info('taur_p= ' + str(self.taur_p))
            self.log.info('taur_s= ' + str(self.taur_s))
            self.log.info('dob=%f' % self.values['dob'])
            self.log.info('rdose=%e' % self.values['rdose'])
            self.log.info('xsize=%i' % data.shape[1])
            self.log.info('ysize=%i' % data.shape[0])
            self.log.info('quadrant=%i' % iquadrant)
            self.log.info('iflip=%i' % iflip)
            self.log.info('jflip=%i' % jflip)

#################################################################################

        CTIed = cdm03bidir.cdm03(np.asfortranarray(data),
                                 jflip, iflip,
                                 self.values['dob'], self.values['rdose'],
                                 self.nt_p, self.sigma_p, self.taur_p,
                                 self.nt_s, self.sigma_s, self.taur_s,
                                 params,
                                 [data.shape[0], data.shape[1], len(self.nt_p), len(self.nt_s), len(self.params)])

        return np.asanyarray(CTIed)

#################################################################################################################
#################################################################################################################


def transRaDec2D(ra, dec):
    # radec转为竞天程序里的ob, 赤道坐标系下的笛卡尔三维坐标xyz.
    x1 = np.cos(dec / 57.2957795) * np.cos(ra / 57.2957795)
    y1 = np.cos(dec / 57.2957795) * np.sin(ra / 57.2957795)
    z1 = np.sin(dec / 57.2957795)
    return np.array([x1, y1, z1])
###############################################################################

################################################################


def ill2flux(E, path):

    # use template from sky_bkg (background_spec_hst.dat)
    filename = path+'MCI_inputData/refs/background_spec_hst.dat'
    cat_spec = pd.read_csv(filename, sep='\\s+', header=None, comment='#')
    wave0 = cat_spec[0].values       # A
    spec0 = cat_spec[2].values      # erg/s/cm^2/A/arcsec^2

    # convert erg/s/cm^2/A/arcsec^2 to erg/s/cm^2/A/sr
    flux1 = spec0 / (1/4.25452e10)
    # convert erg/s/cm^2/A/sr to W/m^2/sr/um
    flux2 = flux1 * 10

    # 对接收面积积分，输出单位 W/m^2/nm
    D = 2   # meter
    f = 28  # meter, 焦距，转换关系来源于王维notes.
    flux3 = flux2 * np.pi * D**2 / 4 / f**2 / 10**3

    f = interp1d(wave0, flux3)
    wave_range = np.arange(3800, 7800)
    flux3_mean = f(wave_range)
    delta_lamba = 0.1   # nm
    E0 = np.sum(flux3_mean * delta_lamba)

    factor = E / E0
    spec_scaled = factor * spec0

    return wave0, spec_scaled

##############################################################


def earth_angle(time_jd, x_sat, y_sat, z_sat, ra_obj, dec_obj):

    ra_sat = np.arctan2(y_sat, x_sat) / np.pi * 180
    dec_sat = np.arctan2(z_sat, np.sqrt(x_sat**2+y_sat**2)) / np.pi * 180
    radec_sat = SkyCoord(ra=ra_sat*u.degree,
                         dec=dec_sat*u.degree, frame='gcrs')
    lb_sat = radec_sat.transform_to('geocentrictrueecliptic')

    # get the obj location
    radec_obj = SkyCoord(ra=ra_obj*u.degree,
                         dec=dec_obj*u.degree, frame='gcrs')
    lb_obj = radec_obj.transform_to('geocentrictrueecliptic')

    # calculate the angle between sub-satellite point and the earth side
    earth_radius = 6371     # km
    sat_height = np.sqrt(x_sat**2 + y_sat**2 + z_sat**2)
    angle_a = np.arcsin(earth_radius/sat_height) / np.pi * 180

    # calculate the angle between satellite position and the target position
    angle_b = lb_sat.separation(lb_obj)

    # calculat the earth angle
    angle = 180 - angle_a - angle_b.degree

    return angle
#######################################


def CCDnonLinearityModel(data, beta=6e-7):
    """
    ##
    The non-linearity is modelled based on the results presented.
    :param data: data to which the non-linearity model is being applied to
    :type data: ndarray

    :return: input data after conversion with the non-linearity model
    :rtype: float or ndarray
    """
    out = data-beta*data**2

    return out
# 33333


class StrayLight(object):

    def __init__(self, path, jtime=2460843., sat=np.array([0, 0, 0]), radec=np.array([0, 0])):
        self.path = path
        self.jtime = jtime
        self.sat = sat
        self.equator = coord.SkyCoord(
            radec[0]*u.degree, radec[1]*u.degree, frame='icrs')
        self.ecliptic = self.equator.transform_to('barycentrictrueecliptic')
        self.pointing = transRaDec2D(radec[0], radec[1])
        self.slcdll = ctypes.CDLL(
            self.path+'MCI_inputData/refs/libstraylight.so')  # dylib

        self.slcdll.Calculate.argtypes = [ctypes.c_double, ctypes.POINTER(ctypes.c_double),
                                          ctypes.POINTER(ctypes.c_double), ctypes.POINTER(
                                              ctypes.c_double),
                                          ctypes.POINTER(ctypes.c_double), ctypes.c_char_p]

        self.slcdll.PointSource.argtypes = [ctypes.c_double, ctypes.POINTER(ctypes.c_double),
                                            ctypes.POINTER(ctypes.c_double), ctypes.POINTER(
                                                ctypes.c_double),
                                            ctypes.POINTER(ctypes.c_double), ctypes.c_char_p]

        self.slcdll.EarthShine.argtypes = [ctypes.c_double, ctypes.POINTER(ctypes.c_double),
                                           ctypes.POINTER(ctypes.c_double), ctypes.POINTER(
                                               ctypes.c_double),
                                           ctypes.POINTER(ctypes.c_double)]

        self.slcdll.Zodiacal.argtypes = [ctypes.c_double, ctypes.POINTER(ctypes.c_double),
                                         ctypes.POINTER(ctypes.c_double)]
        self.slcdll.ComposeY.argtypes = [ctypes.POINTER(ctypes.c_double), ctypes.POINTER(ctypes.c_double),
                                         ctypes.POINTER(ctypes.c_double)]
        self.slcdll.Init.argtypes = [
            ctypes.c_char_p, ctypes.c_char_p, ctypes.c_char_p, ctypes.c_char_p]
        self.deFn = self.path+"MCI_inputData/refs/DE405"
        self.PSTFn = self.path+"MCI_inputData/refs/PST"
        self.RFn = self.path+"MCI_inputData/refs/R"
        self.ZolFn = self.path+"MCI_inputData/refs/Zodiacal"
        self.brightStarTabFn = self.path+"MCI_inputData/refs/BrightGaia_with_csst_mag"

        self.slcdll.Init(str.encode(self.deFn), str.encode(
            self.PSTFn), str.encode(self.RFn), str.encode(self.ZolFn))

    def caculateStarLightFilter(self, filter='i'):

        filterIndex = {'nuv': 0, 'u': 1, 'g': 2,
                       'r': 3, 'i': 4, 'z': 5, 'y': 6}

        sat = (ctypes.c_double*3)()
        sat[:] = self.sat
        ob = (ctypes.c_double*3)()
        ob[:] = self.pointing

        py1 = (ctypes.c_double*3)()
        py2 = (ctypes.c_double*3)()
        self.slcdll.ComposeY(ob, py1, py2)

        star_e1 = (ctypes.c_double*7)()
        self.slcdll.PointSource(self.jtime, sat, ob, py1,
                                star_e1, str.encode(self.brightStarTabFn))
        star_e2 = (ctypes.c_double*7)()
        self.slcdll.PointSource(self.jtime, sat, ob, py2,
                                star_e2, str.encode(self.brightStarTabFn))

        band_star_e1 = star_e1[:][filterIndex[filter]]
        band_star_e2 = star_e2[:][filterIndex[filter]]

        return max(band_star_e1, band_star_e2)

    def caculateEarthShineFilter(self, filter='i'):

        filterIndex = {'nuv': 0, 'u': 1, 'g': 2,
                       'r': 3, 'i': 4, 'z': 5, 'y': 6}
        sat = (ctypes.c_double*3)()
        sat[:] = self.sat
        ob = (ctypes.c_double*3)()
        ob[:] = self.pointing

        py1 = (ctypes.c_double*3)()
        py2 = (ctypes.c_double*3)()
        self.slcdll.ComposeY(ob, py1, py2)

        earth_e1 = (ctypes.c_double*7)()
        self.slcdll.EarthShine(self.jtime, sat, ob, py1,
                               earth_e1)          # e[7]代表7个波段的照度

        earth_e2 = (ctypes.c_double*7)()
        self.slcdll.EarthShine(self.jtime, sat, ob, py2, earth_e2)

        band_earth_e1 = earth_e1[:][filterIndex[filter]]
        band_earth_e2 = earth_e2[:][filterIndex[filter]]

        return max(band_earth_e1, band_earth_e2)

###############################################################################
###############################################################################
###############################################################################


def make_c_coor(fov, step):
    """
    Draw the mesh grids for a fov * fov box with given step .
    """

    nc = int(fov/step)

    bs = fov
    ds = bs / nc
    xx01 = np.linspace(-bs / 2.0, bs / 2.0 - ds, nc) + 0.5 * ds
    xx02 = np.linspace(-bs / 2.0, bs / 2.0 - ds, nc) + 0.5 * ds
    xg2, xg1 = np.meshgrid(xx01, xx02)
    return xg1, xg2

##############################################################################

##############################################################################

# datetime to mjd


def time2mjd(dateT):
    t0 = datetime(1858, 11, 17, 0, 0, 0, 0)   #
    mjd = (dateT-t0).days
    mjd_s = dateT.hour*3600.0+dateT.minute*60.0 + \
        dateT.second+dateT.microsecond/1000000.0
    return mjd+mjd_s/86400.0


def time2jd(dateT):
    t0 = datetime(1858, 11, 17, 0, 0, 0, 0)  #
    mjd = (dateT-t0).days
    mjd_s = dateT.hour*3600.0+dateT.minute*60.0 + \
        dateT.second+dateT.microsecond/1000000.0
    return mjd+mjd_s/86400.0++2400000.5

# mjd to datetime


def mjd2time(mjd):
    t0 = datetime(1858, 11, 17, 0, 0, 0, 0)
    return t0+timedelta(days=mjd)


def jd2time(jd):
    mjd = jd2mjd(jd)
    return mjd2time(mjd)

# mjd to jd
# def mjd2jd(mjd):
#     return mjd+2400000.5


def jd2mjd(jd):
    return jd-2400000.5


def dt2hmd(dt):
    # dt is datetime
    hour = dt.hour
    minute = dt.minute
    second = dt.second
    if hour < 10:
        str_h = '0'+str(hour)
    else:
        str_h = str(hour)

    if minute < 10:
        str_m = '0'+str(minute)
    else:
        str_m = str(minute)

    if second < 10:
        str_d = '0'+str(second+dt.microsecond*1e-6)
    else:
        str_d = str(second+dt.microsecond*1e-6)

    return str_h+':'+str_m+':'+str_d

###############################################################################


def deg2HMS(ra0, dec0):
    '''convert deg to ra's HMS and  dec's DMS'''
    c = SkyCoord(ra=ra0*u.degree, dec=dec0*u.degree)
    ss = c.to_string('hmsdms')

    # print(ss)

    for k in range(5, len(ss)-1):

        if ss[k] == 's':
            s_idx = k

        if ss[k] == 'd':
            d_idx = k

        if ss[k] == 'm':
            m_idx = k

    if c.ra.hms.s < 10 and c.ra.hms.s > 0.1:
        temp = str(c.ra.hms.s)
        ra_ss = '0'+temp[:3]
    if c.ra.hms.s > 10:
        temp = str(c.ra.hms.s)
        ra_ss = temp[:4]

    if c.ra.hms.s < 0.1:
        temp = str(c.ra.hms.s)
        ra_ss = '00.0'

    dms_d = ss[s_idx+2:d_idx]
    dms_m = ss[d_idx+1:m_idx]
    dms_s = ss[m_idx+1:m_idx+3]

    hhmmss = ss[0:2]+ss[3:5]+ra_ss
    return hhmmss+dms_d+dms_m+dms_s

###############################################################################

###############################################################################
#############################################################################
# distortion function


def distortField(ra, dec, ch):
    # % ra ,dec are the idea position in arcsec , and the center position is ra=0, dec=0
    '''MCI geometry distortion effect in channel G R and I'''
    distortA = dict()

    distortB = dict()

    distortA['g'] = np.array([0.000586224851462036, 0.999778507355332, -0.000377391397200834, -4.40403573019393e-06, 0.000147444528630966, -1.93281825050268e-06, 5.73520238714447e-07, -1.05194725549731e-08, 7.55124671251098e-07, -3.85623216175251e-09, -
                             1.36254798567168e-11, 1.36983654024573e-09, -4.53104347730230e-11, 1.50641840169747e-09, -1.05226890727366e-11, 1.06471556810228e-12, -1.42299485385165e-16, 5.90008722878028e-12, 2.36749977009538e-17, 4.11061448730916e-12, 3.39287277469805e-17])
    distortB['g'] = np.array([1.00113878750680, -0.000495107770623867, 0.999162436209357, 9.67138672907941e-05, -3.88808001621361e-06, 0.000267940195644359, -3.03504629416955e-09, 7.59508802931395e-07, -1.13952684052500e-08, 9.58672893217047e-07,
                             2.96397490595616e-10, -3.01506961685930e-11, 2.22570315950441e-09, -4.26077028191289e-11, 2.07336026666512e-09, 1.71450079597168e-16, 2.94455108664049e-12, 8.18246797239659e-17, 8.32235684990184e-12, 1.05203957047210e-17, 5.58541337682320e-12])

    distortA['r'] = np.array([0.000530712822898294, 0.999814397089242, -0.000366783811357609, -1.08650906916023e-05, 0.000146500108063480, -4.48994291741769e-06, 5.66992328511032e-07, -2.10826363791224e-08, 7.51050898843171e-07, -7.74459106063614e-09, -
                             5.78712436084704e-11, 1.36389366137393e-09, -9.50057131576629e-11, 1.49666815592076e-09, -2.16074939825761e-11, 2.07124539578673e-12, -1.32787329448528e-13, 5.78542850850562e-12, -5.82328830750271e-14, 4.04881815088026e-12, 2.46095156355282e-15])
    distortB['r'] = np.array([1.00110972320988, -0.000483253233767592, 0.999181377618578, 9.60316928622784e-05, -9.06574382424422e-06, 0.000266147076486786, -6.64513480754565e-09, 7.53794491639207e-07, -2.25340150955077e-08, 9.53032549996219e-07,
                             2.93844965469408e-10, -6.80521155195455e-11, 2.21272371816576e-09, -9.13097728847483e-11, 2.06225316601308e-09, 9.41096324034730e-14, 3.00253083795091e-12, -1.40935245750903e-13, 8.27122551593984e-12, -2.41500808188601e-13, 5.52074803508046e-12])

    distortA['i'] = np.array([0.000532645905256854, 0.999813271238129, -0.000366606839338804, -1.06782246147986e-05, 0.000146529811926289, -4.54488847969004e-06, 5.68028930846942e-07, -2.11055358580630e-08, 7.51187628288423e-07, -7.93885390157909e-09, -
                             5.63104333653064e-11, 1.36559362569725e-09, -9.64353839395137e-11, 1.50231201562648e-09, -2.04159956020294e-11, 1.91408535007684e-12, -7.46369323634455e-14, 5.86071470639700e-12, -1.65328163262914e-13, 4.07648238374705e-12, 3.40880674871652e-14])
    distortB['i'] = np.array([1.00111276400037, -0.000484310769918440, 0.999180286636823, 9.61240720951134e-05, -8.76526511019577e-06, 0.000266192433565878, -6.59462433108999e-09, 7.54391611102465e-07, -2.30957980169170e-08, 9.53612393886641e-07,
                             2.95558631849358e-10, -7.08307092409029e-11, 2.21952307845185e-09, -9.03816603147003e-11, 2.06450141393973e-09, -3.77836686311826e-14, 3.13358046393416e-12, -5.20417845240954e-14, 8.24487152802918e-12, -1.17846469609206e-13, 5.35830020655191e-12])

    x = ra/0.05*10/1000  # convert ra in arcsec to mm

    y = dec/0.05*10/1000  # convert ra in arcsec to mm

    dd = np.array([1, x, y, x*x, x*y, y*y, x**3, x**2*y, x*y**2, y**3, x**4, x**3*y,
                  x**2*y**2, x*y**3, y**4,  x**5, x**4*y, x**3*y**2, x**2*y**3, x*y**4, y**5])

    xc = np.dot(distortA[ch], dd)
    yc = np.dot(distortB[ch], dd)

    distortRa = xc*1000/10*0.05-0.00265

    distortDec = yc*1000/10*0.05-5.0055

    return distortRa, distortDec

#####################################################################################
# def world_to_pixel(sra,
#                    sdec,
#                    rotsky,
#                    tra,
#                    tdec,
#                    x_center_pixel,
#                    y_center_pixel,
#                    pixelsize=0.05):
#     """_summary_

#     Parameters
#     ----------
#     sra : np.array
#          star ra such as np.array([22,23,24]),unit:deg
#     sdec : np.array
#          stat dec such as np.array([10,20,30]),unit:deg
#     rotsky : float
#         rotation angel of the telescope,unit:deg
#     tra : float
#         telescope pointing ra,such as 222.2 deg
#     rdec : float
#         telescope pointing dec,such as 33.3 deg
#     x_center_pixel : float
#         x refer point of MCI,usually the center of the CCD,which start from (0,0)
#     y_center_pixel : float
#         y refer point of MCI,usually the center of the CCD,which start from(0,0)
#     pixelsize : float
#         pixelsize for MCI ccd, default :0.05 arcsec / pixel

#     Returns
#     -------
#     pixel_position:list with two np.array
#         such as [array([124.16937605,  99.        ,  99.        ]),
#                 array([ 97.52378661,  99.        , 100.50014483])]
#     """
#     theta_r = rotsky / 180 * np.pi
#     w = WCS(naxis=2)
#     w.wcs.crpix = [x_center_pixel + 1, y_center_pixel + 1]  # pixel from (1, 1)
#     w.wcs.cd = np.array([[
#         -np.cos(-theta_r) * pixelsize / 3600,
#         np.sin(-theta_r) * pixelsize / 3600
#     ],
#                          [
#                              np.sin(-theta_r) * pixelsize / 3600,
#                              np.cos(-theta_r) * pixelsize / 3600
#                          ]])
#     w.wcs.crval = [tra, tdec]
#     w.wcs.ctype = ["RA---TAN", "DEC--TAN"]
#     pixel_position = w.all_world2pix(sra, sdec, 0)
#     return pixel_position

###############################################################################


def cal_pos(center_ra, center_dec, rotTel, rotSky, sRa, sDec):
    # ''' center_ra: telescope pointing in ra direction, unit: degree
    #     center_dec: telescope pointing in dec direction, unit: degree
    #     rotTel:  telescope totation in degree
    #     rotSky:  telescope rotation in degree
    #     sRa:  source ra in arcsec
    #     sDec: source dec in arcsec

    boresight = galsim.CelestialCoord(
        ra=-center_ra*galsim.degrees, dec=center_dec*galsim.degrees)

    q = rotTel - rotSky
    cq, sq = np.cos(q), np.sin(q)
    jac = [cq, -sq, sq, cq]
    affine = galsim.AffineTransform(*jac)
    wcs = galsim.TanWCS(affine, boresight, units=galsim.arcsec)
    objCoord = galsim.CelestialCoord(
        ra=-sRa*galsim.arcsec, dec=sDec*galsim.arcsec)
    field_pos = wcs.toImage(objCoord)
    return -field_pos.x, field_pos.y  # return the field position

#####################################################################################

# def krebin(a, sample):
#     """ Fast Rebinning with flux conservation
#     New shape must be an integer divisor of the current shape.
#     This algorithm is much faster than rebin_array
#     Parameters
#     ----------
#     a : array_like
#         input array
#     sample :   rebinned sample 2 or 3 or 4 or other int value
#     """
#     # Klaus P's fastrebin from web
#     size=a.shape
#     shape=np.zeros(2,dtype=int)
#     shape[0]=int(size[0]/sample)
#     shape[1]=int(size[1]/sample)

#     sh = shape[0], a.shape[0] // shape[0], shape[1], a.shape[1] // shape[1]

#     return a.reshape(sh).sum(-1).sum(1)

#####################################################################
#########################################################


def centroid(data):
    '''
    calculate the centroid of the input two-dimentional image data

    Parameters
    ----------
    data : input image.

    Returns
    -------
    cx: the centroid column number,  in horizontal direction definet in python image show
    cy: the centroid row number  ,   in vertical direction
    ################# '''
    ###
    ###
    from scipy import ndimage
    cy, cx = ndimage.center_of_mass(data)
    return cx, cy

###############################################################################

###############################################################################


'''
PSF interpolation for MCI_sim
'''


# find neighbors-KDtree  ###

def findNeighbors(tx, ty, px, py, dn=5):
    """
    find nearest neighbors by 2D-KDTree

    Parameters:
        tx, ty (float, float): a given position
        px, py (numpy.array, numpy.array): position data for tree
        dr (float-optional): distance in arcsec
        dn (int-optional): nearest-N
        OnlyDistance (bool-optional): only use distance to find neighbors. Default: True

    Returns:
        indexq (numpy.array): index
    """
    datax = px
    datay = py
    tree = spatial.KDTree(list(zip(datax.ravel(), datay.ravel())))
    indexq = []
    dist, indexq = tree.query([tx, ty], dn)
    return indexq

###############################################################################
# PSF-IDW###


def psfMaker_IDW(px, py, PSFMat, cen_col, cen_row, dn=5, IDWindex=3, OnlyNeighbors=True):
    """
    psf interpolation by IDW

    Parameters:
        px, py (float, float): position of the target
        PSFMat (numpy.array): PSF matrix array at given position and wavelength
        cen_col, cen_row (numpy.array, numpy.array): potions of the psf centers in arcsec
        IDWindex (int-optional): the power index of IDW
        OnlyNeighbors (bool-optional): only neighbors are used for psf interpolation

    Returns:
        psfMaker (numpy.array)
    """

    minimum_psf_weight = 1e-8
    ref_col = px  # target position in x
    ref_row = py  # target position in y

    ngy, ngx = PSFMat[:, :, 0, 0].shape  # PSF data size

    ####################################
    #   my code  ######
    psfWeight = np.zeros([dn])

    if OnlyNeighbors:

        neigh = findNeighbors(px, py, cen_col, cen_row, dn)

    #######################################################################
    for ipsf in range(len(neigh)):

        idx = neigh[ipsf]
        dist = np.sqrt((ref_col - cen_col[idx])
                       ** 2 + (ref_row - cen_row[idx])**2)

        # if IDWindex == 1:
        psfWeight[ipsf] = dist**IDWindex
        # if IDWindex == 2:
        #     psfWeight[ipsf] = dist**2
        # if IDWindex == 3:
        #     psfWeight[ipsf] = dist**3
        # if IDWindex == 4:
        #     psfWeight[ipsf] = dist**4
        # if IDWindex == 5:
        #     psfWeight[ipsf] = dist**5

        psfWeight[ipsf] = max(psfWeight[ipsf], minimum_psf_weight)
        psfWeight[ipsf] = 1./psfWeight[ipsf]

    # 3

    psfWeight /= np.sum(psfWeight)

    psfMaker = np.zeros([ngy, ngx, 7], dtype=np.float32)

    #########################################################
    for waven in range(7):

        for ipsf in range(len(neigh)):

            idx = neigh[ipsf]

            iPSFMat = PSFMat[:, :, waven, idx].copy()

            ipsfWeight = psfWeight[ipsf]

            psfMaker[:, :, waven] += iPSFMat * ipsfWeight

        psfMaker[:, :, waven] /= np.nansum(psfMaker[:, :, waven])

    return psfMaker

###############################################################################
###############################################################################
###############################################################################


class MCIsimulator():
    """
    CSST MCI Simulator

    The image that is being build is in::

        self.image
        self.image_g    g channel
        self.image_r    r channel
        self.image_i    i channel


    :param opts: OptionParser instance
    :type opts: OptionParser instance
    """

    def __init__(self, configfile):
        """
        Class Constructor.

        :param opts: OptionParser instance
        :type opts: OptionParser instance
        """
        self.configfile = configfile
        self.section = 'TEST'  # simulation section;
        self.information = MCIinstrumentModel.MCIinformation()

        # update settings with defaults
        self.information.update(dict(quadrant=int(0),
                                     ccdx=int(0),
                                     ccdy=int(0),
                                     ccdxgap=1.643,
                                     ccdygap=8.116,
                                     xsize=2000,
                                     ysize=2000,
                                     fullwellcapacity=90000,
                                     pixel_size=0.05,
                                     dark=0.001,
                                     exptime=300.0,
                                     readouttime=4.,
                                     rdose=8.0e9,
                                     ghostCutoff=22.0,
                                     ghostratio=5.e-5,
                                     coveringfraction=0.05,  # CR:
                                     # cosmicraylengths =self.information['dir_path']+'MCI_inputData/data/cdf_cr_length.dat',
                                     # cosmicraydistance=self.information['dir_path']+'MCI_inputData/data/cdf_cr_total.dat',
                                     # parallelTrapfile =self.information['dir_path']+'MCI_inputData/data/cdm_euclid_parallel.dat',
                                     # serialTrapfile   =self.information['dir_path']+'MCI_inputData/data/cdm_euclid_serial.dat',
                                     mode='same'))

####################################################

###############################################################################
    def readConfigs(self, simnumber, source):
        """
        Reads the config file information using configParser and sets up a logger.
        """
        self.config = ConfigParser.RawConfigParser()

        if simnumber == 1:
            print('beging config file name : ')
            print(self.configfile)

        # self.config.readfp(open(self.configfile))
        self.config.read_file(open(self.configfile))

#######
###########################################################################
#######
#######
    def processConfigs(self):
        """
        Processes configuration information and save the information to a dictionary self.information.
        For explanation of each field, see /data/test.config. Note that if an input field does not exist,
        then the values are taken from the default instrument model as described in
        support.MCIinstrumentModel.VISinformation(). Any of the defaults can be overwritten by providing
        a config file with a correct field name.
        """
        # parse options and update the information dictionary
        options = self.config.options(self.section)
        settings = {}
        for option in options:
            try:
                settings[option] = self.config.getint(self.section, option)
            except ValueError:
                try:
                    settings[option] = self.config.getfloat(
                        self.section, option)
                except ValueError:
                    settings[option] = self.config.get(self.section, option)

        self.information.update(settings)

        # ghost ratio can be in engineering format, so getfloat does not capture it...
        self.information['ghostratio'] = float(
            self.config.get(self.section, 'ghostRatio'))

    ###################################################################################################
    ##################################################################################################

        # booleans to control the flow        # scale thibaut's values
        if 'thibaut' in self.information['parallelTrapfile']:
            self.nt_p /= 0.576  # thibaut's values traps / pixel
            self.sigma_p *= 1.e4  # thibaut's values in m**2
        if 'thibaut' in self.information['serialTrapfile']:
            self.nt_s *= 0.576  # thibaut's values traps / pixel  #should be division?
            self.sigma_s *= 1.e4  # thibaut's values in m**2

        self.cosmicRays = self.config.getboolean(self.section, 'cosmicRays')
        self.darknoise = self.config.getboolean(self.section, 'darknoise')
        self.cosmetics = self.config.getboolean(self.section, 'cosmetics')
        self.radiationDamage = self.config.getboolean(
            self.section, 'radiationDamage')
        self.bleeding = self.config.getboolean(self.section, 'bleeding')
        self.overscans = self.config.getboolean(self.section, 'overscans')
        self.nonlinearity = self.config.getboolean(
            self.section, 'nonlinearity')
        self.readoutNoise = self.config.getboolean(
            self.section, 'readoutNoise')
        self.skyback = self.config.getboolean(self.section, 'skyback')
        self.TianceEffect = self.config.getboolean(
            self.section, 'TianceEffect')
        self.intscale = self.config.getboolean(self.section, 'intscale')
        self.ghosts = self.config.getboolean(self.section, 'ghosts')
        self.shutterEffect = self.config.getboolean(
            self.section, 'shutterEffect')
        self.flatfieldM = self.config.getboolean(self.section, 'flatfieldm')
        self.PRNUeffect = self.config.getboolean(self.section, 'PRNUeffect')
        self.appFatt = self.config.getboolean(self.section, 'appFatt')
        self.sky_shift_rot = self.config.getboolean(
            self.section, 'sky_shift_rot')
        self.distortion = self.config.getboolean(self.section, 'distortion')
        self.sim_star = self.config.getboolean(self.section, 'sim_star')
        self.sim_galaxy = self.config.getboolean(self.section, 'sim_galaxy')

        self.save_starpsf = self.config.getboolean(
            self.section, 'save_starpsf')
        self.save_cosmicrays = self.config.getboolean(
            self.section, 'save_cosmicrays')
        self.lensing = self.config.getboolean(self.section, 'lensing')

        # #################################
        self.booleans = dict(cosmicRays=self.cosmicRays,
                             darknoise=self.darknoise,
                             cosmetics=self.cosmetics,
                             radiationDamage=self.radiationDamage,
                             bleeding=self.bleeding,
                             overscans=self.overscans,
                             nonlinearity=self.nonlinearity,
                             readoutNoise=self.readoutNoise,
                             skyback=self.skyback,
                             TianceEffect=self.TianceEffect,
                             intscale=self.intscale,
                             ghosts=self.ghosts,
                             shutterEffect=self.shutterEffect,
                             flatfieldM=self.flatfieldM,
                             PRNUeffect=self.PRNUeffect,
                             appFatt=self.appFatt,
                             sky_shift_rot=self.sky_shift_rot,
                             distortion=self.distortion,
                             sim_star=self.sim_star,
                             sim_galaxy=self.sim_galaxy,
                             save_starpsf=self.save_starpsf,
                             save_cosmicrays=self.save_cosmicrays,
                             lensing=self.lensing)
        #####################################################################

        return

# ##############################################################################
# #############################################################################

    def _createEmpty(self):
        """
        Creates and empty array of a given x and y size full of zeros.
        add g r i channel images;

        Creates lensing parameters;
        """
#############################
        self.image_g = np.zeros(
            (self.information['ysize'], self.information['xsize']), dtype=float)
        self.image_r = np.zeros(
            (self.information['ysize'], self.information['xsize']), dtype=float)
        self.image_i = np.zeros(
            (self.information['ysize'], self.information['xsize']), dtype=float)
        return

# ###############################IDWindex#####################################
# ############################################################################
    def _loadGhostModel(self):
        """
        Reads in a ghost model from a FITS file and stores the data to self.ghostModel.

        Currently assumes that the ghost model has already been properly scaled and that the pixel
        scale of the input data corresponds to the nominal VIS pixel scale. Futhermore, assumes that the
        distance to the ghost from y=0 is appropriate (given current knowledge, about 750 VIS pixels).
        """

        self.ghostOffsetX = self.information['ghostoffsetx']
        self.ghostOffsetY = self.information['ghostoffsety']

        self.ghostMax = self.information['ghostratio']
        self.log.info('Maximum in the ghost model %e' % self.ghostMax)
        return
# ##############################################

    def readCosmicRayInformation(self):
        """
        Reads in the cosmic ray track information from two input files.

        Stores the information to a dictionary called cr.
        """
        self.log.info('Reading in cosmic ray information from %s and %s' % (self.information['cosmicraylengths'],
                                                                            self.information['cosmicraydistance']))

        crLengths = np.loadtxt(
            self.information['dir_path']+self.information['cosmicraylengths'])
        crDists = np.loadtxt(
            self.information['dir_path']+self.information['cosmicraydistance'])

        self.cr = dict(cr_u=crLengths[:, 0], cr_cdf=crLengths[:, 1], cr_cdfn=np.shape(crLengths)[0],
                       cr_v=crDists[:, 0], cr_cde=crDists[:, 1], cr_cden=np.shape(crDists)[0])

        return

###############################################################################

###############################################################################
    def configure(self, simnumber, sourcein, dir_path, result_path):
        """
        Configures the simulator with input information and creates and empty array to which the final image will
        be build on.
        """
        self.readConfigs(simnumber, sourcein)

        self.processConfigs()

        self._createEmpty()

        self.information['dir_path'] = dir_path

        self.information['result_path'] = result_path

        self.source = sourcein

        # print('print information:', self.information)

        ###########################################################################

        now = datetime.utcnow()
        # data_time=now.strftime("%Y-%m-%d-%H-%M-%S")
        result_day = now.strftime("%Y-%m-%d")

        # if self.information['dir_path'] =='/nfsdata/share/simulation-unittest/mci_sim/':
        #     self.result_path = self.information['dir_path'] +'mci_sim_result/'+result_day
        # else:

        #     home_path = os.environ['HOME']

        print(self.information['result_path'])

        self.result_path = self.information['result_path'] + \
            self.source+"_"+result_day

        if os.path.isdir(self.result_path) is False:
            os.mkdir(self.result_path)
            os.mkdir(self.result_path+'/cali_Data')
            os.mkdir(self.result_path+'/log_Data')
            os.mkdir(self.result_path+'/ori_Cali')
            os.mkdir(self.result_path+'/ori_Sky')
            os.mkdir(self.result_path+'/sky_Data')
            os.mkdir(self.result_path+'/PSF_Data')

        now = datetime.utcnow()

        data_time = now.strftime("%Y-%m-%d-%H-%M-%S")

        print('simnumber', simnumber)

        self.log = lg.setUpLogger(self.result_path+'/log_Data/MCIsim_' +
                                  self.source+'_'+data_time+'_Num_'+str(simnumber)+'.log')

        self.log.info('-------STARTING A NEW SIMULATION------------')

        for key, value in self.information.items():
            self.log.info('%s = %s' % (key, value))

        self.log.info('The exposure order is  %i ' % simnumber)

        # load instrument model, these values are also stored in the FITS header

        self.information['G_filters'] = ["F275W",  "F280N", "NUV",
                                         "WU",   "CBU",   "F343N",  "u",      "F373N", "F395N",  "F336W"]
        self.information['R_filters'] = ["F487N", "F502N", "CBV",   "r",
                                         "F656N", "F658N",  "F467M",  "F555W", "F606W",  "F673N"]
        self.information['I_filters'] = ["z",      "y",    "F815N",
                                         "CBI",  "F925N", "F960M",  "F968N",  "F845M", "F850LP", "F814W"]

        # load telescope efficiency data

        self.tel_eff = np.load(
            self.information['dir_path']+'MCI_inputData/tel_eff/tel_eff.npy', allow_pickle=True).item()

        # load MCI filter data
        self.filterP = np.load(
            self.information['dir_path']+'MCI_inputData/MCI_filters/mci_filterPWTC.npy', allow_pickle=True).item()

        return

################################################################################
    def load_filter_PSF(self):
        # load filter PSF in three channels ,G , R and I
        ###
        # load filter name in three channels from information
        self.filter_g = self.information['filter_g']
        self.filter_r = self.information['filter_r']
        self.filter_i = self.information['filter_i']

        self.filter_psf = dict()

        filtername = self.filter_g
        self.filter_psf['g'] = np.load(
            self.information['dir_path']+'MCI_inputData/PSF/'+filtername+'_PSF.npy', allow_pickle=True).item()

        filtername = self.filter_r
        self.filter_psf['r'] = np.load(
            self.information['dir_path']+'MCI_inputData/PSF/'+filtername+'_PSF.npy', allow_pickle=True).item()

        filtername = self.filter_i
        self.filter_psf['i'] = np.load(
            self.information['dir_path']+'MCI_inputData/PSF/'+filtername+'_PSF.npy', allow_pickle=True).item()

        return

###############################################################################################

    def get_PSF(self, px, py, ch):
        """
        Get the PSF at a given image position

        Parameters:
        px,py        :  target position
        filternmae   :  the selected filter name
        ch           :  the MCI channle , g or r or i
        Returns:
            PSF:     PSF array at given 7 wavelength and at target position
        """
        PSFMat = self.filter_psf[ch]['psf_mat']
        cen_col = self.filter_psf[ch]['psf_field_X']
        cen_row = self.filter_psf[ch]['psf_field_Y']
        imPSF = psfMaker_IDW(px, py, PSFMat, cen_col, cen_row, IDWindex=2)

        return imPSF

###############################################################################

###############################################################################

    def cal_SED_photons(self, flux):
        # flux_arr,                input flux array with unit of  1e-17 erg/s/A/cm^2

        # set the wavelenth for MCI from 250nm to 1100nm
        wave = np.linspace(2500, 10000, 8501)
        wave = wave/10

        # calcutle photons from original spec cube data,
        # data: input data, two-dimentional, unit :  1e-17 erg/s/A/cm^2

        planckh = 6.62620*10**-27  # % erg s;
        cc = 2.99792458*10**17  # in nm/s
        telarea = 3.1415926*100*100               # in cm^2，望远镜聚光面积�?
        # convert original unit to unit of erg/s/A/cm^2
        fluxlam = 1e-17*(flux)
        # wave in nm   ;;
        # single photon energy in erg/photon;  cc与lambda单位需要一致；
        ephoton = planckh*cc/wave

        Nphoton = fluxlam/ephoton*telarea * \
            self.information['exptime']        # in unit of  photons

        # load telescope efficiency data
        teleff = dict()
        teleff['g'] = self.tel_eff['G']  # fisrt colunmm  is wavelength in nm
        teleff['r'] = self.tel_eff['R']  # second colunmm is  efficiency
        teleff['i'] = self.tel_eff['I']
        #####

        ft = dict()
        ft['g'] = self.filter_g
        ft['r'] = self.filter_r
        ft['i'] = self.filter_i

        # load filter name in three channels
        ft_wave = dict()
        ft_wave['g'] = self.filterP[ft['g']]['wave_nm']
        ft_wave['r'] = self.filterP[ft['r']]['wave_nm']
        ft_wave['i'] = self.filterP[ft['i']]['wave_nm']

        ft_eff = dict()
        ft_eff['g'] = self.filterP[ft['g']]['throughout']
        ft_eff['r'] = self.filterP[ft['r']]['throughout']
        ft_eff['i'] = self.filterP[ft['i']]['throughout']

        chlist = ['g', 'r', 'i']

        Sumphoton = dict()
        PSF_eff_weight = dict()

        for k in range(3):
            ch = chlist[k]
            tel_wavearr = teleff[ch][:, 0]
            tel_effarr = teleff[ch][:, 1]
            tel_effnew = np.interp(wave, tel_wavearr, tel_effarr)

            ft_wavearr = ft_wave[ch]
            ft_effarr = ft_eff[ch]
            ft_effnew = np.interp(wave, ft_wavearr, ft_effarr)

            # SED flux*filter_flux*tel_eff
            eff_arr = Nphoton*ft_effnew*tel_effnew

            Sumphoton[ch] = np.sum(eff_arr)

            # seven wavelength for the seleced filter
            iwave = self.filter_psf[ch]['psf_iwave'][:]

            psf_coeff = np.interp(iwave, wave, eff_arr)

            if psf_coeff.sum() == 0:
                psf_coeff = 1.0/7*np.ones(7)
            else:
                psf_coeff = psf_coeff/psf_coeff.sum()

            PSF_eff_weight[ch] = psf_coeff

        return Sumphoton, PSF_eff_weight

    ##################################################
###############################################################################

    def cal_sky_noise(self):
        # ######       add earthshine       #######
        # ######       add earthshine       #######
        # self.earthshine_wave        # A
        # self.earthshine_flux        # erg/s/cm^2/A/arcsec^2

        # set the wavelenth for MCI from 250nm to 1100nm
        wave = np.linspace(2500, 11000, 8501)

        skynoise_flux = self.earthshine_flux+self.zodiacal_flux   # erg/s/cm^2/A/arcsec^2

        wave = wave/10

        # data: input data, two-dimentional, unit :  1e-17 erg/s/A/cm^2
        planckh = 6.62620*10**-27  # % erg s;
        cc = 2.99792458*10**17  # in nm/s
        telarea = 3.1415926*100*100               # in cm^2，
        ephoton = planckh*cc/wave                 # in erg/photon,
        # each pixel will have the photons in unit of  photons/s/A
        Nphoton_flux = skynoise_flux/ephoton*telarea * \
            0.05*0.05*self.information['exptime']

        # load telescope efficiency data
        teleff = dict()
        teleff['g'] = self.tel_eff['G']  # fisrt colunmm  is wavelength in nm
        teleff['r'] = self.tel_eff['R']  # second colunmm is  efficiency
        teleff['i'] = self.tel_eff['I']
        #####

        ft = dict()
        ft['g'] = self.filter_g
        ft['r'] = self.filter_r
        ft['i'] = self.filter_i

        # load filter name in three channels
        ft_wave = dict()
        ft_wave['g'] = self.filterP[ft['g']]['wave_nm']
        ft_wave['r'] = self.filterP[ft['r']]['wave_nm']
        ft_wave['i'] = self.filterP[ft['i']]['wave_nm']

        ft_eff = dict()
        ft_eff['g'] = self.filterP[ft['g']]['throughout']
        ft_eff['r'] = self.filterP[ft['r']]['throughout']
        ft_eff['i'] = self.filterP[ft['i']]['throughout']

        chlist = ['g', 'r', 'i']

        self.Sky_Noise = dict()

        for k in range(3):
            ch = chlist[k]
            tel_wavearr = teleff[ch][:, 0]
            tel_effarr = teleff[ch][:, 1]
            tel_effnew = np.interp(wave, tel_wavearr, tel_effarr)

            ft_wavearr = ft_wave[ch]
            ft_effarr = ft_eff[ch]
            ft_effnew = np.interp(wave, ft_wavearr, ft_effarr)

            self.Sky_Noise[ch] = np.sum(Nphoton_flux*ft_effnew*tel_effnew)

        return self.Sky_Noise

    ##################################################
    ##############################################################################

    def cal_StarSED_img(self):
        #####################

        pixelscale = self.information['pixel_size']  # arcsec, pixel scale size
        ghostOffsetX = self.information['ghostoffsetx']
        ghostOffsetY = self.information['ghostoffsety']
        ghostMx = self.information['ghostratio']

        rotTelPos = self.information['rotTelPos']
        rotSkyPos = self.information['rotSkyPos']

        theta = rotTelPos - rotSkyPos

        # ########### load star data catlog #####################
        # 'selection_MACSJ0744.9 3927_20230517_concat.csv'
        starcat = self.information['starcat']

        if get_file_extension(starcat) == '.csv':

            # starcat='selection_20230517_concat.fits'
            ###################################################

            self.log.info('Stat catlog file name is %s' % (starcat))

            ##########################################
            df2 = pandas.read_csv(
                self.information['dir_path']+'MCI_inputData/star_input/'+starcat)
            ###
            #
            try:
                df2['ra_gaia'][10]+df2['dec_gaia'][10]
            except NameError:
                self.log.error(
                    'Error,no ra or dec data in the star_cat catlog file ')
                raise ValueError(
                    'Error,no ra or dec data in the star_cat catlog file ')
            #
            if df2['ra_gaia'].max() > 360 or df2['ra_gaia'].min() < 0 or df2['dec_gaia'].max() > 90 or df2['dec_gaia'].min() < -90:
                self.log.error(
                    'ra or dec data beyond definition,please check!')
                raise ValueError(
                    'ra or dec data beyond definition,please check!')
            #
            df3 = df2[(abs(df2['ra_gaia']-df2['ra_gaia'].mean()) < 400/3600.0)
                      & (abs(df2['dec_gaia']-df2['dec_gaia'].mean()) < 400/3600.0)]

            df3.index = range(len(df3))

            ###################################################

            # np.save('umag.npy', df3['umag'] )
            self.star = df3

            del df2, df3

            self.information['ra_obj'] = self.star['ra_gaia'].mean()
            self.information['dec_obj'] = self.star['dec_gaia'].mean()
            nsrcs = len(self.star['ra_gaia'])

        # if get_file_extension(starcat) == '.fits':

        #     self.log.info('Stat catlog file name is %s' % (starcat))

        #     df0 = fits.open(
        #         self.information['dir_path']+'/MCI_inputData/star_input/'+starcat)

        #     self.star = df0[1].data
        #     ###

        #     if 'ra' not in self.star.names or 'dec' not in self.star.names:
        #         print('no ra')
        #         self.log.error(
        #             'Error,no ra or dec data in the star_cat catlog file ')
        #         raise ValueError(
        #             'Error,no ra or dec data in the star_cat catlog file ')

        #     if self.star['ra'].max() > 360 or self.star['ra'].min() < 0 or self.star['dec'].max() > 90 or self.star['dec'].min() < -90:
        #         self.log.error(
        #             'ra or dec data beyond definition,please check!')
        #         raise ValueError(
        #             'ra or dec data beyond definition,please check!')

        #     self.information['ra_obj'] = self.star['ra'].mean()
        #     self.information['dec_obj'] = self.star['dec'].mean()
        #     nsrcs = len(self.star['ra'])

        ##################################

        self.information['star_ra'] = self.information['ra_obj']
        self.information['star_dec'] = self.information['dec_obj']

        center_ra = self.information['T_disRa'] + self.information['ra_obj']
        center_dec = self.information['T_disDec'] + self.information['dec_obj']

        self.information['ra_pnt0'] = center_ra
        self.information['dec_pnt0'] = center_dec

        channel = ['g', 'r', 'i']
        #######################################################################
        # ###################  cal_PSF_array   #################################

        if self.save_starpsf:

            self.log.info('calculate and save star PSF data......')

            primary_g = fits.PrimaryHDU()
            PSF_g = fits.HDUList([primary_g])

            primary_r = fits.PrimaryHDU()
            PSF_r = fits.HDUList([primary_r])

            primary_i = fits.PrimaryHDU()
            PSF_i = fits.HDUList([primary_i])

            fov = 0.05*min(self.information['xsize'],
                           self.information['ysize'])

            dec_arr, ra_arr = make_c_coor(fov, 25)

            k = 0

            #######################################################################
            for ii in range(len(ra_arr)):
                for jj in range(len(ra_arr)):

                    ##################################################################

                    galRa = ra_arr[ii, jj] + center_ra * \
                        3600     # ra  of PFS, arcsecond
                    galDec = dec_arr[ii, jj]+center_dec * \
                        3600    # dec of PSF, arcsecond

                    fsx, fsy = cal_pos(center_ra, center_dec,
                                       rotTelPos, rotSkyPos, galRa, galDec)

                    # ############  do field distottion  ##########

                    if self.distortion:

                        for i in range(3):
                            ch = channel[i]
                            fpx, fpy = distortField(fsx, fsy, ch)

                    else:

                        fpx = fsx
                        fpy = fsy

                    ###################################################################

                    psf = dict()

                    for i in range(3):

                        ch = channel[i]

                        psfmat = self.get_PSF(fpx, fpy, ch)

                        temp = 0

                        for iwave in range(7):
                            temp = temp+1/7.0*psfmat[:, :, iwave]

                        temp = temp/temp.sum()

                        # rotate the PSF data
                        if abs(theta.deg) > 0:
                            # here we choose reshape=False, the rotated image will
                            psf[ch] = ndimage.rotate(
                                temp, theta.deg, order=1, reshape=False)
                        else:
                            psf[ch] = temp
                    ###################################

                    hdu_g = fits.ImageHDU(psf['g'])
                    hdu_g.header['ra'] = ra_arr[ii, jj]
                    hdu_g.header['dec'] = dec_arr[ii, jj]
                    hdu_g.header['rot_deg'] = -theta.deg
                    PSF_g.append(hdu_g)
                    ###################################
                    hdu_r = fits.ImageHDU(psf['r'])
                    hdu_r.header['ra'] = ra_arr[ii, jj]
                    hdu_r.header['dec'] = dec_arr[ii, jj]
                    hdu_r.header['rot_deg'] = -theta.deg
                    PSF_r.append(hdu_r)
                    ###################################
                    hdu_i = fits.ImageHDU(psf['i'])
                    hdu_i.header['ra'] = ra_arr[ii, jj]
                    hdu_i.header['dec'] = dec_arr[ii, jj]
                    hdu_i.header['rot_deg'] = -theta.deg
                    PSF_i.append(hdu_i)
                    ###################################
                    del hdu_g
                    del hdu_r
                    del hdu_i
                    k = k+1

            ############################################
            file_g = self.result_path+'/PSF_Data/'+'PSF_sim_No.' + \
                str(self.information['simnumber'])+'_C1.fits'
            PSF_g.writeto(file_g, overwrite=True)

            file_r = self.result_path+'/PSF_Data/'+'PSF_sim_No.' + \
                str(self.information['simnumber'])+'_C2.fits'
            PSF_r.writeto(file_r, overwrite=True)

            file_i = self.result_path+'/PSF_Data/'+'PSF_sim_No.' + \
                str(self.information['simnumber'])+'_C3.fits'
            PSF_i.writeto(file_i, overwrite=True)

            del PSF_g
            del PSF_r
            del PSF_i
            self.log.info('finish save star PSF data......')

        # #########  finish save PSF fits  ##########################################
        # ###########################################################################

        self.log.info('load star catlog successfully')

        ##################################################################
        obj = dict()
        obj['g'] = np.zeros(3)
        obj['r'] = np.zeros(3)
        obj['i'] = np.zeros(3)

        fullimg = dict()
        star_input = dict()
        star_input['g'] = np.zeros((nsrcs, 3))
        star_input['r'] = np.zeros((nsrcs, 3))
        star_input['i'] = np.zeros((nsrcs, 3))

        star_output = dict()
        star_output['g'] = np.zeros((nsrcs, 3))
        star_output['r'] = np.zeros((nsrcs, 3))
        star_output['i'] = np.zeros((nsrcs, 3))

        final_image = dict()

        final_image['g'] = galsim.ImageF(
            int(self.information['xsize']), int(self.information['ysize']))
        final_image['r'] = galsim.ImageF(
            int(self.information['xsize']), int(self.information['ysize']))
        final_image['i'] = galsim.ImageF(
            int(self.information['xsize']), int(self.information['ysize']))

        final_image['g'].setOrigin(0, 0)
        final_image['r'].setOrigin(0, 0)
        final_image['i'].setOrigin(0, 0)
        #######################################################################
        nlayccd = 0

        ############################################

        ra = self.information['ra_pnt0']
        dec = self.information['dec_pnt0']

        time_jd = time2jd(self.dt)

        x_sat = float(self.orbit_pars[self.orbit_exp_num, 1])
        y_sat = float(self.orbit_pars[self.orbit_exp_num, 2])
        z_sat = float(self.orbit_pars[self.orbit_exp_num, 3])

        wave0, zodi0 = self.zodiacal(
            ra, dec, self.TianCe_day)     # erg/s/cm^2/A/arcsec^2

        self.log.info('dir_path:')
        self.log.info(self.information['dir_path'])

        # EarthShine from straylight
        sl = StrayLight(self.information['dir_path'], jtime=time_jd, sat=np.array(
            [x_sat, y_sat, z_sat]), radec=np.array([(ra*u.degree).value, (dec*u.degree).value]))
        earth_e = sl.caculateEarthShineFilter(filter='r')
        star_e = sl.caculateStarLightFilter(filter='r')
        angle_earth = earth_angle(time_jd, x_sat, y_sat, z_sat, ra, dec)

        if angle_earth < 0:
            earth_e = 0

        earthshine_wave0, earthshine_flux0 = ill2flux(
            earth_e+star_e, self.information['dir_path'])

        # sample as mci wavelength
        wave_mci = np.linspace(2500, 11000, 8501)  # np.arange(2500, 11000, 1)
        f1 = interp1d(wave0, zodi0)
        zodi_mci = f1(wave_mci)

        f2 = interp1d(earthshine_wave0, earthshine_flux0)
        earthshine_mci = f2(wave_mci)

        self.zodiacal_wave = wave_mci        # in A
        self.zodiacal_flux = zodi_mci

        self.earthshine_wave = wave_mci      # A
        self.earthshine_flux = earthshine_mci

        ##############################################################
        #############################################################

        self.cal_sky_noise()

        #################################

        self.information['target'] = deg2HMS(
            self.information['ra_obj'], self.information['dec_obj'])

        self.information['CRVAL1'] = self.information['ra_pnt0']
        self.information['CRVAL2'] = self.information['dec_pnt0']

        self.information['CRPIX1'] = self.information['xsize']/2-0.5
        self.information['CRPIX2'] = self.information['ysize']/2-0.5

        self.information['CD1_1'] = - \
            np.cos(-theta)*self.information['pixel_size']/3600.0
        self.information['CD1_2'] = np.sin(-theta) * \
            self.information['pixel_size']/3600.0

        self.information['CD2_1'] = np.sin(-theta) * \
            self.information['pixel_size']/3600.0
        self.information['CD2_2'] = np.cos(-theta) * \
            self.information['pixel_size']/3600.0

        #######################################################################
        if self.TianceEffect:
            if get_file_extension(starcat) == '.fits':

                ra_list = self.star['ra'].tolist()
                dec_list = self.star['dec'].tolist()

                pmra_list = [0.0 for i in range(len(ra_list))]
                pmdec_list = [0.0 for i in range(len(ra_list))]
                parallax_list = [0.0 for i in range(len(ra_list))]
                rv_list = [0.0 for i in range(len(ra_list))]
            else:

                ra_list = self.star['ra_gaia'].tolist()
                dec_list = self.star['dec_gaia'].tolist()
                pmra_list = self.star['pmra_gaia'].tolist()
                pmdec_list = self.star['pmdec_gaia'].tolist()
                parallax_list = self.star['parallax_gaia'].tolist()
                rv_list = [0.0 for i in range(len(ra_list))]

            # ###############################################
            newRa, newDec = onOrbitObsPosition(self.information['dir_path'], ra_list, dec_list, pmra_list,
                                               pmdec_list, rv_list, parallax_list, len(
                ra_list),
                self.information['pos_x'], self.information['pos_y'], self.information['pos_z'], self.information['velocity_x'], self.information['velocity_y'], self.information['velocity_z'], "J2000", self.TianCe_day, self.TianCe_exp_start)
        else:

            if get_file_extension(starcat) == '.fits':
                newRa = self.star['ra']
                newDec = self.star['dec']

            else:

                newRa = self.star['ra_gaia']
                newDec = self.star['dec_gaia']
        ######################################################

        st_ra = []
        st_dec = []
        st_phot_C1 = []
        st_phot_C2 = []
        st_phot_C3 = []

        st_posX_C1 = []
        st_posX_C2 = []
        st_posX_C3 = []

        st_posY_C1 = []
        st_posY_C2 = []
        st_posY_C3 = []

        st_magu = []
        st_magg = []
        st_magr = []
        st_magi = []
        st_magz = []

        # ###################   generate star image  ##########

        for j in tqdm(range(nsrcs)):  # nsrcs length

            if j == 0:
                self.log.info('begin iteration........')
            # 33
            # if self.debug:
            # self.log.info('j =  %i' % (j))

            starRa = 3600.0*(newRa[j])  # ra of star,  arcsecond
            starDec = 3600.0*(newDec[j])  # dec of star, arcsecond

            ###################################################################
            ###################################################################
            fsx, fsy = cal_pos(center_ra, center_dec,
                               rotTelPos, rotSkyPos, starRa, starDec)
            # row number on CCD image
            row = fsy/pixelscale+self.information['ysize']/2-0.5
            # col number on CCD image
            col = fsx/pixelscale+self.information['xsize']/2-0.5

            # print(col,row)

            if row >= self.information['ysize']+100 or row <= -100 or col >= self.information['xsize']+100 or col <= -100:
                continue

            nlayccd = nlayccd+1

            if self.debug:
                if nlayccd > 1:
                    break

            ################################################################
            for i in range(3):
                ###
                ch = channel[i]
                star_input[ch][int(nlayccd)-1, 0] = fsx  # ra
                star_input[ch][int(nlayccd)-1, 1] = fsy  # dec

            # ############  do field distottion  ##########

            if self.distortion:

                for i in range(3):

                    ch = channel[i]
                    fpx, fpy = distortField(fsx, fsy, ch)
                    obj[ch][0] = fpx
                    obj[ch][1] = fpy

                    star_output[ch][int(nlayccd)-1, 0] = fpx  # ra
                    star_output[ch][int(nlayccd)-1, 1] = fpy  # dec

            else:

                fpx = fsx
                fpy = fsy

                for i in range(3):

                    ch = channel[i]

                    obj[ch][0] = fpx
                    obj[ch][1] = fpy

                    star_output[ch][int(nlayccd)-1, 0] = fpx  # ra
                    star_output[ch][int(nlayccd)-1, 1] = fpy  # dec

            # ################################################################

            # ###### use  SED_code to generate star SED   #######
            if get_file_extension(starcat) == '.fits':
                umag = self.star['gmag'][j]
                gmag = self.star['gmag'][j]
                rmag = self.star['rmag'][j]
                imag = self.star['imag'][j]
                zmag = self.star['zmag'][j]

            # SED of j-th star
            else:
                umag = self.star['umag'][j]
                gmag = self.star['gmag'][j]
                rmag = self.star['rmag'][j]
                imag = self.star['imag'][j]
                zmag = self.star['zmag'][j]

            # if  j==426:
            #     print('debug   error')

            if np.isnan(umag) or np.isnan(gmag) or np.isnan(rmag) or np.isnan(imag) or np.isnan(zmag):
                continue

            wave = np.linspace(2500, 11000, 8501)
            # Input redshift
            redshift = 0

            # Loading galaxy SED template
            t = sed.Gal_Temp(self.log, self.information['dir_path'])
            # Calculating the magnitude (u, g, r, i, z) of each template
            t.toMag(redshift=redshift)
            # Calculating flux
            ugriz = np.array([umag, gmag, rmag, imag, zmag])
            star_flux = sed.Model_Galaxy_SED(
                wave, ugriz, redshift, t, self.information['dir_path'])

            # cal_SED_photons(self, flux_arr):
            intscales, PSF_eff_weight = self.cal_SED_photons(star_flux)

            gx = dict()
            gy = dict()

            # self.log.info('magu =%f, magg=%f, magr=%f, magi=%f,magz=%f' % (umag,gmag,rmag,imag,zmag))
            for i in range(3):
                ch = channel[i]
                gy[ch] = obj[ch][1]/pixelscale+self.information['ysize'] / \
                    2-0.5  # row number on CCD image
                gx[ch] = obj[ch][0]/pixelscale+self.information['xsize'] / \
                    2-0.5  # col number on CCD image

            # self.log.info('Channel in =%s, PosX(in pixel)=%f, PosY(in pixel)=%f' % (channel[i], gx[ch], gy[ch]))

            if self.debug:

                self.log.info('Star number on CCD is = %i' % (nlayccd))

            if nlayccd % 10 == 0:
                self.log.info('Star number on CCD is = %i' % (nlayccd))

            ######

            st_ra.append(np.float64(starRa/3600.0))
            st_dec.append(np.float64(starDec/3600.0))

            st_phot_C1.append(intscales['g'])
            st_phot_C2.append(intscales['r'])
            st_phot_C3.append(intscales['i'])

            st_posX_C1.append(gx['g'])
            st_posX_C2.append(gx['r'])
            st_posX_C3.append(gx['i'])

            st_posY_C1.append(gy['g'])
            st_posY_C2.append(gy['r'])
            st_posY_C3.append(gy['i'])

            st_magu.append(umag)
            st_magg.append(gmag)
            st_magr.append(rmag)
            st_magi.append(imag)
            st_magz.append(zmag)

            # # self.log.info('begin cal PSF')
            # #####################################################################
            psf = dict()

            for i in range(3):

                ch = channel[i]
                #######
                psfmat = self.get_PSF(fsx, fsy, ch)

                for iwave in range(7):
                    if iwave == 0:
                        temp = PSF_eff_weight[ch][iwave]*psfmat[:, :, iwave]
                    else:
                        temp = temp + \
                            PSF_eff_weight[ch][iwave]*psfmat[:, :, iwave]

                temp = temp/temp.sum()

                # rotate the PSF data
                if abs(theta.deg > 0):
                    # here we choose reshape=False, the rotated image will
                    psf[ch] = ndimage.rotate(
                        temp, theta.deg, order=1, reshape=True)
                else:
                    psf[ch] = temp

                conv = psf[ch]
                conv = conv/conv.sum()

                conv = conv*intscales[ch]
                #################################################

                stamp_img = galsim.Image(conv, copy=True)
                stamp_img.setOrigin(0, 0)
                stamp_img.scale = 0.025

                # self.log.info('begining galsim.PhotonArray.makeFromImage')

                # self.log.info('stamp_img.array.max()= %i' % (stamp_img.array.max()))

                # ##print('stamp_img.array.max(): ', stamp_img.array.max() )

                #################################################

                photons = galsim.PhotonArray.makeFromImage(
                    stamp_img, max_flux=max(1.0, stamp_img.array.max()/100000.0))

                cx0, cy0 = centroid(stamp_img.array)

                # #self.log.info('appfat= %s'%(self.appFatt))

                if self.appFatt:
                    # ##self.log.info('begin appfat ........')

                    # apply treering and bright fatter and diffusion;
                    SimpleTreeRing = galsim.SiliconSensor().simple_treerings(
                        amplitude=self.information['treering'])

                    cx, cy = centroid(conv)

                    # set subimge center as the pos
                    pos = galsim.PositionD(x=int(cx), y=int(cy))

                    siliconsensor = galsim.SiliconSensor(
                        strength=self.information['fatter'], diffusion_factor=self.information['difusion'], treering_func=SimpleTreeRing, treering_center=pos)

                    image = galsim.Image(conv.shape[0], conv.shape[1])
                    image.scale = 0.025
                    image.setOrigin(0, 0)

                    photons.x = photons.x/image.scale
                    photons.y = photons.y/image.scale

                    siliconsensor.accumulate(photons, image)

                    photons = galsim.PhotonArray.makeFromImage(
                        image, max_flux=max(1.0, stamp_img.array.max()/100000.0))

                    cx0, cy0 = centroid(image.array)

                ############################################
                ############################################
                photons.x = photons.x-cx0*0.025
                photons.y = photons.y-cy0*0.025

                ##########################################################
                ########################################################
                photons.x = photons.x/0.05+gx[ch]
                photons.y = photons.y/0.05+gy[ch]

                photons.addTo(final_image[ch])

                ###############################
                # debug code
                # fi= galsim.ImageF(int(self.information['xsize']), int(self.information['ysize']))
                # fi.setOrigin(0,0)
                # photons.addTo(fi)
                # icx,i cy=centroid(fi.array)
                # print(icx,icy)
                ###############################################################

                # ### add ghost image ####
                if self.ghosts:
                    ghostphotons = galsim.PhotonArray(
                        len(photons.x), x=photons.x, y=photons.y, flux=photons.flux)
                    ghostphotons.flux = ghostMx*ghostphotons.flux
                    ghostphotons.x = photons.x+ghostOffsetY
                    ghostphotons.y = photons.y+ghostOffsetX

                    if ghostphotons.flux.max() > 0.01:
                        ghostphotons.addTo(final_image[ch])

                ###############################################################
        print('total star nummber on CCD is: ', nlayccd)

        self.log.info('total star nummber on CCD = %i' % (nlayccd))

        tab = Table()
        tab['st_ra'] = np.asarray(st_ra)
        tab['st_dec'] = np.asarray(st_dec)

        tab['phot_C1'] = np.asarray(st_phot_C1)
        tab['phot_C2'] = np.asarray(st_phot_C2)
        tab['phot_C3'] = np.asarray(st_phot_C3)

        tab['posX_C1'] = np.asarray(st_posX_C1)
        tab['posX_C2'] = np.asarray(st_posX_C2)
        tab['posX_C3'] = np.asarray(st_posX_C3)

        tab['posY_C1'] = np.asarray(st_posY_C1)
        tab['posY_C2'] = np.asarray(st_posY_C2)
        tab['posY_C3'] = np.asarray(st_posY_C3)

        tab['st_magu'] = np.asarray(st_magu)
        tab['st_magg'] = np.asarray(st_magg)
        tab['st_magr'] = np.asarray(st_magr)
        tab['st_magi'] = np.asarray(st_magi)
        tab['st_magz'] = np.asarray(st_magz)

        # 33

        file = self.result_path+'/log_Data/star_info' + \
            '_SN_'+str(self.information['simnumber'])+'.fits'
        tab.write(file, overwrite=True)

        #################################################################
        #################################################################

        fullimg['g'] = final_image['g'].array
        fullimg['r'] = final_image['r'].array
        fullimg['i'] = final_image['i'].array

        return fullimg

################################################################################
################################################################################

    def zodiacal(self, ra, dec, time):
        """
            For given RA, DEC and TIME, return the interpolated zodical spectrum in Leinert-1998.

        :param ra: RA in unit of degree, ICRS frame
        :param dec: DEC in unit of degree, ICRS frame
        :param time: the specified string that in ISO format i.e., yyyy-mm-dd.
        :return:
            wave_A: wavelength of the zodical spectrum
            spec_mjy: flux of the zodical spectrum, in unit of MJy/sr
            spec_erg: flux of the zodical spectrum, in unit of erg/s/cm^2/A/sr

        """

        # get solar position
        dt = datetime.fromisoformat(time)
        # ##jd = julian.to_jd(dt, fmt='jd')

        jd = time2jd(dt)
        t = Time(jd, format='jd', scale='utc')

        astro_sun = get_sun(t)
        ra_sun, dec_sun = astro_sun.gcrs.ra.deg, astro_sun.gcrs.dec.deg

        radec_sun = SkyCoord(ra=ra_sun*u.degree,
                             dec=dec_sun*u.degree, frame='gcrs')
        lb_sun = radec_sun.transform_to('geocentrictrueecliptic')

        # get offsets between the target and sun.
        radec_obj = SkyCoord(ra=ra*u.degree, dec=dec*u.degree, frame='icrs')
        lb_obj = radec_obj.transform_to('geocentrictrueecliptic')

        beta = abs(lb_obj.lat.degree)
        lamda = abs(lb_obj.lon.degree - lb_sun.lon.degree)

        # interpolated zodical surface brightness at 0.5 um
        zodi = pd.read_csv(
            self.information['dir_path']+'MCI_inputData/refs/zodi_map.dat', sep='\\s+', header=None, comment='#')
        beta_angle = np.array([0, 5, 10, 15, 20, 25, 30, 45, 60, 75])
        lamda_angle = np.array([0, 5, 10, 15, 20, 25, 30, 35, 40, 45,
                                60, 75, 90, 105, 120, 135, 150, 165, 180])
        xx, yy = np.meshgrid(beta_angle, lamda_angle)
        f = interpolate.interp2d(xx, yy, zodi, kind='linear')
        zodi_obj = f(beta, lamda)       # 10^�? W m�? sr�? um�?

        # read the zodical spectrum in the ecliptic
        cat_spec = pd.read_csv(
            self.information['dir_path']+'MCI_inputData/refs/solar_spec.dat', sep='\\s+', header=None, comment='#')
        wave = cat_spec[0].values       # A
        spec0 = cat_spec[1].values      # 10^-8 W m^�? sr^�? μm^�?
        zodi_norm = 252                 # 10^-8 W m^�? sr^�? μm^�?

        spec = spec0 * (zodi_obj / zodi_norm) * 1e-8  # W m^�? sr^�? μm^�?

        # convert to the commonly used unit of MJy/sr, erg/s/cm^2/A/sr
        wave_A = wave                                               # A
        # spec_mjy = spec * 0.1 * wave_A**2 / 3e18 * 1e23 * 1e-6      # MJy/sr
        spec_erg = spec * 0.1                                       # erg/s/cm^2/A/sr
        # erg/s/cm^2/A/arcsec^2
        spec_erg2 = spec_erg / 4.25452e10

        self.zodiacal_wave = wave_A    # in A

        self.zodiacal_flux = spec_erg2

        return wave_A, spec_erg2
    ##################################################################################

    ##########################################################################
    def cal_Lensing_galaxy_img(self):
        #####################

        # arcsec, pixel scale size;
        self.dsx_arc = self.information['pixel_size']
        # arcsec, field size;
        self.bsz_arc = max(
            self.information['ysize'], self.information['xsize'])*self.dsx_arc
        self.bsz_deg = self.bsz_arc/3600.

        #############################################################
        # galaxy catlog information
        ra = 74.0563449685417
        dec = -34.155241212932374

        # halo_id = 229600100382
        if self.sim_star is False:
            self.information['star_ra'] = ra
            self.information['star_dec'] = dec

        self.information['gal_ra'] = ra
        self.information['gal_dec'] = dec

        self.information['ra_obj'] = self.information['star_ra']
        self.information['dec_obj'] = self.information['star_dec']

        losscale = self.information['pixel_size']
        rotTelPos = self.information['rotTelPos']
        rotSkyPos = self.information['rotSkyPos']

        theta = rotTelPos - rotSkyPos

        center_ra = self.information['T_disRa'] + self.information['star_ra']
        center_dec = self.information['T_disDec'] + \
            self.information['star_dec']

        self.information['ra_pnt0'] = center_ra
        self.information['dec_pnt0'] = center_dec
        ####################################################################
        # ###################################################################

        time_jd = time2jd(self.dt)

        x_sat = float(self.orbit_pars[self.orbit_exp_num, 1])
        y_sat = float(self.orbit_pars[self.orbit_exp_num, 2])
        z_sat = float(self.orbit_pars[self.orbit_exp_num, 3])

        wave0, zodi0 = self.zodiacal(
            self.information['ra_pnt0'], self.information['dec_pnt0'], self.TianCe_day)     # erg/s/cm^2/A/arcsec^2

        # EarthShine from straylight
        sl = StrayLight(self.information['dir_path'], jtime=time_jd, sat=np.array([x_sat, y_sat, z_sat]),
                        radec=np.array([(ra*u.degree).value, (dec*u.degree).value]))

        earth_e = sl.caculateEarthShineFilter(filter='r')
        star_e = sl.caculateStarLightFilter(filter='r')
        angle_earth = earth_angle(time_jd, x_sat, y_sat, z_sat, ra, dec)

        if angle_earth < 0:
            earth_e = 0

        earthshine_wave0, earthshine_flux0 = ill2flux(
            earth_e+star_e, self.information['dir_path'])

        # sample as ifs wavelength
        wave_mci = np.linspace(2500, 11000, 8501)  # np.arange(2500, 11000, 1)
        f1 = interp1d(wave0, zodi0)
        zodi_mci = f1(wave_mci)

        f2 = interp1d(earthshine_wave0, earthshine_flux0)
        earthshine_mci = f2(wave_mci)

        # df = pd.DataFrame({'wave': wave_ifs,
        #                    'zodi': zodi_ifs,
        #                    'earthshine': earthshine_ifs})

        self.zodiacal_wave = wave_mci            # in A
        self.zodiacal_flux = zodi_mci

        self.earthshine_wave = wave_mci          # A
        self.earthshine_flux = earthshine_mci

        ########################################################################################

        ##################################################################################
        self.cal_sky_noise()

        # ########### load galaxy data with SED  ############################

        self.information['target'] = deg2HMS(
            self.information['star_ra'], self.information['star_dec'])

        self.information['CRVAL1'] = self.information['ra_pnt0']
        self.information['CRVAL2'] = self.information['dec_pnt0']

        self.information['CRPIX1'] = self.information['xsize']/2-0.5
        self.information['CRPIX2'] = self.information['ysize']/2-0.5

        self.information['CD1_1'] = - \
            np.cos(-theta)*self.information['pixel_size']/3600.0
        self.information['CD1_2'] = np.sin(-theta) * \
            self.information['pixel_size']/3600.0

        self.information['CD2_1'] = np.sin(-theta) * \
            self.information['pixel_size']/3600.0
        self.information['CD2_2'] = np.cos(-theta) * \
            self.information['pixel_size']/3600.0

        obj = dict()
        obj['g'] = np.zeros(3)
        obj['r'] = np.zeros(3)
        obj['i'] = np.zeros(3)

        fullimg = dict()
        galaxy_input = dict()

        nsrcs = 72000

        galaxy_input['g'] = np.zeros((nsrcs, 3))
        galaxy_input['r'] = np.zeros((nsrcs, 3))
        galaxy_input['i'] = np.zeros((nsrcs, 3))

        galaxy_output = dict()
        galaxy_output['g'] = np.zeros((nsrcs, 3))
        galaxy_output['r'] = np.zeros((nsrcs, 3))
        galaxy_output['i'] = np.zeros((nsrcs, 3))

        channel = ['g', 'r', 'i']
        final_image = dict()

        final_image['g'] = galsim.ImageF(
            int(self.information['xsize']), int(self.information['ysize']))
        final_image['r'] = galsim.ImageF(
            int(self.information['xsize']), int(self.information['ysize']))
        final_image['i'] = galsim.ImageF(
            int(self.information['xsize']), int(self.information['ysize']))

        final_image['g'].setOrigin(0, 0)
        final_image['r'].setOrigin(0, 0)
        final_image['i'].setOrigin(0, 0)

        ####################################################################

        gal_ra = []  # .append(np.float64(galRa/3600.0))
        gal_dec = []  # .append(np.float64(galDec/3600.0))

        phot_C1 = []  # .append(intscales['g'])
        phot_C2 = []  # .append(intscales['r'])
        phot_C3 = []  # .append(intscales['i'])

        posX_C1 = []  # .append(gx['g'])
        posX_C2 = []  # .append(gx['r'])
        posX_C3 = []  # .append(gx['i'])

        posY_C1 = []  # .append(gy['g'])
        posY_C2 = []  # .append(gy['r'])
        posY_C3 = []  # .append(gy['i'])

        galID = []
        haloID = []

        gal_new_ra = []
        gal_new_dec = []

        gal_ori_ra = []
        gal_ori_dec = []

        gal_rescale = []
        gal_szs = []

        gal_magu = []
        gal_magg = []
        gal_magr = []
        gal_magi = []
        gal_magz = []

        ########################################

        nlayccd = 0

        # generate loc image

        if self.debug:
            nk2 = 1
        else:
            nk2 = 20

        for k2 in (range(nk2)):
            print('k2=', k2)

            #  Lens_SED_IMG_0.025_230606

            if self.lensing:
                filename = self.information['dir_path'] + \
                    'MCI_inputData/galaxy_Input/Lens_SED_IMG_0.025_230606/Lens_img_cut_IMG_' + \
                    str(k2+1)+'.fits'
                if not os.path.exists(filename):
                    continue

            else:
                filename = self.information['dir_path'] + \
                    'MCI_inputData/galaxy_Input/noLens_SED_IMG_0.025_230626/Lens_img_cut_IMG_' + \
                    str(k2+1)+'.fits'
                if not os.path.exists(filename):
                    continue

            self.log.info('galaxy_Input image path is: %s' % (filename))

            # if not os.path.exists(filename):
            #     print('finish load all the input galaxy image fits files')
            #     #
            #     self.log.error(
            #         'Error, when load input galaxy image fits files')
            #     raise ValueError(
            #         'Error, when load input galaxy image fits files')
            #     #
            srcs_cat = fits.open(filename)
            #
            # ### load galaxy SED fitsfile  ###

            if self.lensing:

                filename = self.information['dir_path'] + \
                    'MCI_inputData/galaxy_Input/Lens_SED_IMG_0.025_230606/Lens_img_cut_SED_' + \
                    str(k2+1)+'.fits'
                if not os.path.exists(filename):
                    continue

            else:

                filename = self.information['dir_path'] + \
                    'MCI_inputData/galaxy_Input/noLens_SED_IMG_0.025_230626/Lens_img_cut_SED_' + \
                    str(k2+1)+'.fits'
                if not os.path.exists(filename):
                    continue
            #
            srcs_sed = fits.open(filename)

            self.log.info('galaxy_Input SED path is: %s' % (filename))
            #
            if len(srcs_cat) != len(srcs_sed):
                #
                self.log.error(
                    'Error,image data do not mach sed data in the input galaxy data file ')
                raise ValueError(
                    'Error,image data do not mach sed data in the input galaxy data file ')

            # ##### do Tiance Effect ###
            ra_list = []
            dec_list = []

            for kkk in range(1, len(srcs_cat)):
                #
                # test input data
                try:
                    srcs_cat[kkk].header['new_ra']+srcs_cat[kkk].header['new_dec']
                except NameError:
                    self.log.error(
                        'Error,no ra or dec data in the input galaxy data  file ')
                    raise ValueError(
                        'Error,no ra or dec data in the input galaxy data  file ')

                if srcs_cat[kkk].header['new_ra'] > 360 or srcs_cat[kkk].header['new_ra'] < 0 or srcs_cat[kkk].header['new_dec'] > 90 or srcs_cat[kkk].header['new_dec'] < -90:
                    self.log.error(
                        'Gal ra or dec data beyond definition, please check!')
                    raise ValueError(
                        'Gal ra or dec data beyond definition, please check!')
                #
                t1 = srcs_cat[kkk].header['new_ra'] - \
                    self.information['gal_ra'] + self.information['star_ra']
                ra_list.append(float(t1))

                t2 = srcs_cat[kkk].header['new_dec'] - \
                    self.information['gal_dec'] + self.information['star_dec']
                dec_list.append(float(t2))

            if self.TianceEffect:

                pmra_list = [0.0 for i in range(len(ra_list))]
                pmdec_list = [0.0 for i in range(len(ra_list))]
                parallax_list = [0.0 for i in range(len(ra_list))]
                rv_list = [0.0 for i in range(len(ra_list))]

                ################################################
                newRa, newDec = shao.onOrbitObsPosition(self.information['dir_path'], ra_list, dec_list, pmra_list,
                                                        pmdec_list, rv_list, parallax_list, len(
                                                            ra_list),
                                                        self.information['pos_x'], self.information['pos_y'], self.information['pos_z'], self.information['velocity_x'], self.information['velocity_y'], self.information['velocity_z'], "J2000", self.TianCe_day, self.TianCe_exp_start)
            else:
                newRa = ra_list
                newDec = dec_list

            ############################################################################
            ###################################################################
            for k1 in tqdm(range(len(newRa))):

                # print('k1=', k1)

                # *(srcs_cat[k1].header['new_ra'] -self.information['gal_ra'] +self.information['star_ra'])   # ra of galaxies, arcsecond
                galRa = newRa[k1]*3600
                # (srcs_cat[k1].header['new_dec']-self.information['gal_dec']+self.information['star_dec'])  # dec of galaxies, arcsecond
                galDec = newDec[k1]*3600

                ###########################################################################
                ############################################################################
                # ## test code ####

                #################################################################
                fsx, fsy = cal_pos(center_ra, center_dec,
                                   rotTelPos, rotSkyPos, galRa, galDec)

                if abs(fsx) > 235 or abs(fsy) > 235:
                    continue

                # # SED of j-th galaxy  ,# unit of 10-17 erg/s/A/cm2
                # here is k1+1, not k1, k1 begins with 0
                gal_flux = srcs_sed[k1+1].data
                #
                if gal_flux.min() < 0 or len(gal_flux) < 100:
                    self.log.error(
                        'Galaxy SED data error, please check!')
                    raise ValueError(
                        'Galaxy SED data error, please check!')
                #
                if srcs_cat[k1+1].data.min() < 0 or srcs_cat[k1+1].data.max == 0 or srcs_cat[k1+1].data.ndim != 2:
                    self.log.error(
                        'Galaxy image data error, please check!')
                    raise ValueError(
                        'Galaxy image data error, please check!')
                #
                ################################
                # ## rotate the lensed_images_g  ###
                if abs(theta.deg) > 0:
                    # here we choose reshape=False, the rotated image will
                    lensed_images_g = ndimage.rotate(
                        srcs_cat[k1+1].data, -theta.deg, order=1, reshape=True)
                else:
                    lensed_images_g = srcs_cat[k1+1].data

                if lensed_images_g.sum() <= 0:
                    continue

                ################################################################
                for i in range(3):
                    ###
                    ch = channel[i]
                    galaxy_input[ch][int(nlayccd)-1, 0] = fsx  # ra
                    galaxy_input[ch][int(nlayccd)-1, 1] = fsy  # dec
                    # galaxy_input[ch][int(nlayccd)-1, 2]   =mag[ch]    # mag g

                # ############  do field distottion  ##########

                if self.distortion:

                    for i in range(3):

                        ch = channel[i]

                        fpx, fpy = distortField(fsx, fsy, ch)
                        obj[ch][0] = fpx
                        obj[ch][1] = fpy
                        # obj[ch][2]=mag[ch]

                        galaxy_output[ch][int(nlayccd)-1, 0] = fpx  # ra
                        galaxy_output[ch][int(nlayccd)-1, 1] = fpy  # dec
                        # galaxy_output[ch][int(nlayccd)-1, 2]   =mag[ch]    # mag g

                else:

                    fpx = fsx
                    fpy = fsy

                    for i in range(3):

                        ch = channel[i]

                        obj[ch][0] = fpx
                        obj[ch][1] = fpy
                        # obj[ch][2]=mag[ch]

                        galaxy_output[ch][int(nlayccd)-1, 0] = fpx  # ra
                        galaxy_output[ch][int(nlayccd)-1, 1] = fpy  # dec
                        # galaxy_output[ch][int(nlayccd)-1, 2]   =mag[ch]    # mag g

                #######################################################################
                # self.log.info('Galaxy number=%i, Ra(in arcsec)=%f, Dec(in arcsec)=%f' % (nlayccd, fpx, fpy))
                # self.log.info('Galaxy number=%i, Mag_g=%f, Mag_r=%f,Mag_i=%f' % (nlayccd, mag['g'],mag['r'],mag['i']))

                # print('Flux sum =', gal_flux.sum())

                # cal_SED_photons(self, flux_arr):
                intscales, PSF_eff_weight = self.cal_SED_photons(gal_flux)

                ls_imgsum = lensed_images_g.sum()

                if intscales['g'].max()/ls_imgsum < 0.1 and intscales['r'].max()/ls_imgsum < 0.1 and intscales['i'].max()/ls_imgsum < 0.1:
                    #########
                    continue
                # ####### test code   ##########

                if self.debug:
                    if nlayccd > 1:
                        #########
                        break

                # 33

                img = dict()

                lensed_images_g = lensed_images_g/ls_imgsum
                img['g'] = lensed_images_g*intscales['g']
                img['r'] = lensed_images_g*intscales['r']
                img['i'] = lensed_images_g*intscales['i']

                if img['g'].max() < 0.01 and img['r'].max() < 0.01 and img['i'].max() < 0.01:
                    #########
                    continue

                nlayccd = nlayccd+1

                ################################################

                gx = dict()
                gy = dict()

                for i in range(3):

                    ch = channel[i]

                    gy[ch] = obj[ch][1]/losscale+self.information['ysize'] / \
                        2-0.5  # row number on CCD image
                    gx[ch] = obj[ch][0]/losscale+self.information['xsize'] / \
                        2-0.5  # col number on CCD image

                ######################################################################

                gal_ra.append(np.float64(galRa/3600.0))
                gal_dec.append(np.float64(galDec/3600.0))

                phot_C1.append(intscales['g'])
                phot_C2.append(intscales['r'])
                phot_C3.append(intscales['i'])

                posX_C1.append(gx['g'])
                posX_C2.append(gx['r'])
                posX_C3.append(gx['i'])

                posY_C1.append(gy['g'])
                posY_C2.append(gy['r'])
                posY_C3.append(gy['i'])

                gal_magu.append(srcs_cat[k1+1].header['magu'])
                gal_magg.append(srcs_cat[k1+1].header['magg'])
                gal_magr.append(srcs_cat[k1+1].header['magr'])
                gal_magi.append(srcs_cat[k1+1].header['magi'])
                gal_magz.append(srcs_cat[k1+1].header['magz'])

                galID.append(srcs_cat[k1+1].header['GALAXID'])
                haloID.append(srcs_cat[k1+1].header['HALOID'])

                gal_new_ra.append(srcs_cat[k1+1].header['NEW_RA'])
                gal_new_dec.append(srcs_cat[k1+1].header['NEW_DEC'])

                gal_ori_ra.append(srcs_cat[k1+1].header['OLD_RA'])
                gal_ori_dec.append(srcs_cat[k1+1].header['OLD_DEC'])

                gal_rescale.append(srcs_cat[k1+1].header['RESCALE'])
                gal_szs.append(srcs_cat[k1+1].header['SZS'])

                # self.log.info('Galaxy number=%i, Mag_g=%f, Mag_r=%f,Mag_i=%f' % (nlayccd, mag['g'],mag['r'],mag['i']))

                if nlayccd % 1000 == 0:
                    self.log.info('Galaxy number on CCD is = %i' % (nlayccd))

                psf = dict()

                for i in range(3):

                    ch = channel[i]

                    psfmat = self.get_PSF(fsx, fsy, ch)

                    temp = 0

                    for iwave in range(7):
                        temp = temp + \
                            PSF_eff_weight[ch][iwave]*psfmat[:, :, iwave]

                    temp = temp/temp.sum()

                    # rotate the PSF data
                    if abs(theta.deg) > 0:
                        # here we choose reshape=False, the rotated image will
                        psf[ch] = ndimage.rotate(
                            temp, theta.deg, order=1, reshape=False)
                    else:
                        psf[ch] = temp

                    conv = fftconvolve(img[ch], psf[ch], mode='full')
                    # continue
                    # suppress negative numbers

                    conv[conv < 0.0] = 0.0

                    #################################################
                    stamp_img = galsim.Image(conv, copy=True)
                    stamp_img.setOrigin(0, 0)
                    stamp_img.scale = 0.025

                    photons = galsim.PhotonArray.makeFromImage(
                        stamp_img, max_flux=1.0)

                    cx0, cy0 = centroid(stamp_img.array)
                    # ########## apply fat effect to galaxy image #####
                    if self.appFatt:

                        # apply treering and bright fatter and diffusion;

                        SimpleTreeRing = galsim.SiliconSensor().simple_treerings(
                            amplitude=self.information['treering'])

                        cx, cy = centroid(conv)

                        # set subimge center as the pos
                        pos = galsim.PositionD(x=int(cx), y=int(cy))

                        siliconsensor = galsim.SiliconSensor(
                            strength=self.information['fatter'], diffusion_factor=self.information['difusion'], treering_func=SimpleTreeRing, treering_center=pos)

                        image = galsim.Image(conv.shape[1], conv.shape[0])
                        image.scale = 0.025
                        image.setOrigin(0, 0)

                        photons.x = photons.x/image.scale
                        photons.y = photons.y/image.scale

                        siliconsensor.accumulate(photons, image)

                        photons = galsim.PhotonArray.makeFromImage(
                            image, max_flux=1.0)

                        cx0, cy0 = centroid(image.array)
                        ##############################################################
                        ##############################################################
                    ##################################################################
                    ##################################################################

                    photons.x = photons.x-cx0*0.025
                    photons.y = photons.y-cy0*0.025

                    photons.x = photons.x/0.05+gx[ch]
                    photons.y = photons.y/0.05+gy[ch]

                    photons.addTo(final_image[ch])

                    ####################################################
                    ############################################
                    ############################################
                ########################################################################

        print('total los nummber on CCD is: ', nlayccd)

        self.log.info('total los nummber on CCD is = %i' % (nlayccd))

        #################################################################
        tab = Table()
        tab['gal_ra'] = np.asarray(gal_ra)
        tab['gal_dec'] = np.asarray(gal_dec)

        tab['phot_C1'] = np.asarray(phot_C1)
        tab['phot_C2'] = np.asarray(phot_C2)
        tab['phot_C3'] = np.asarray(phot_C3)

        tab['posX_C1'] = np.asarray(posX_C1)
        tab['posX_C2'] = np.asarray(posX_C2)
        tab['posX_C3'] = np.asarray(posX_C3)

        tab['posY_C1'] = np.asarray(posY_C1)
        tab['posY_C2'] = np.asarray(posY_C2)
        tab['posY_C3'] = np.asarray(posY_C3)

        tab['gal_magu'] = np.asarray(gal_magu)
        tab['gal_magg'] = np.asarray(gal_magg)
        tab['gal_magr'] = np.asarray(gal_magr)
        tab['gal_magi'] = np.asarray(gal_magi)
        tab['gal_magz'] = np.asarray(gal_magz)

        tab['gal_ID'] = np.asarray(galID)
        tab['halo_ID'] = np.asarray(haloID)

        tab['gal_new_ra'] = np.asarray(gal_new_ra)
        tab['gal_new_dec'] = np.asarray(gal_new_dec)

        tab['gal_ori_ra'] = np.asarray(gal_ori_ra)
        tab['gal_ori_dec'] = np.asarray(gal_ori_dec)

        tab['gal_rescale'] = np.asarray(gal_rescale)
        tab['gal_szs'] = np.asarray(gal_szs)

        ####################################################################

        file = self.result_path+'/log_Data/gal_info' + \
            '_SN_'+str(self.information['simnumber'])+'.fits'
        tab.write(file, overwrite=True)

        #######################################################################

        fullimg['g'] = final_image['g'].array
        fullimg['r'] = final_image['r'].array
        fullimg['i'] = final_image['i'].array

        return fullimg
#############################################
#################################
###############################################################################
###########
###############################################################################
################

    def generatePRNU(self, ave=1.0, sigma=0.01):
        """
        Creates a PRNU field image with given properties.

        :return: PRNU field image
        :rtype: ndarray
        """
        self.log.info('Generating a flat field...')
        self.log.info(
            'The flat field has mean value of 1 and a given fluctuations, usually either 1 or 2 percent defined by sigma= %d...' % sigma)

        self.PRNU = dict()

        np.random.seed(self.filterP[self.filter_g]['seed'])
        self.PRNU['g'] = np.random.normal(loc=ave, scale=sigma, size=(
            self.information['ysize'], self.information['xsize']))

        np.random.seed(self.filterP[self.filter_r]['seed'])
        self.PRNU['r'] = np.random.normal(loc=ave, scale=sigma, size=(
            self.information['ysize'], self.information['xsize']))

        np.random.seed(self.filterP[self.filter_i]['seed'])
        self.PRNU['i'] = np.random.normal(loc=ave, scale=sigma, size=(
            self.information['ysize'], self.information['xsize']))

        return self.PRNU
###############################################################################
###############################################################################

    def MakeFlatMatrix(self, img, seed):
        ####
        ysize, xsize = img.shape
        np.random.seed(seed)
        r1, r2, r3, r4 = np.random.random(4)
        a1 = -0.5 + 0.2*r1
        a2 = -0.5 + 0.2*r2
        a3 = r3+5
        a4 = r4+5
        xmin, xmax, ymin, ymax = 0, xsize, 0, ysize
        Flty, Fltx = np.mgrid[ymin:ymax, xmin:xmax]
        np.random.seed(seed)
        p1, p2, bg = np.random.poisson(1000, 3)
        Fltz = 1e-6*(a1 * (Fltx-p1) ** 2 + a2 * (Flty-p2)
                     ** 2 - a3*Fltx - a4*Flty) + bg*20
        FlatMat = Fltz/np.mean(Fltz)

        return FlatMat
####################################################################################################

    def applyFlat(self):

        # apply Flat field to image
        self.FlatMat = dict()
        ###
        self.FlatMat['g'] = self.MakeFlatMatrix(
            self.image_g, self.filterP[self.filter_g]['seed'])
        self.image_g *= self.FlatMat['g']
        ###
        self.FlatMat['r'] = self.MakeFlatMatrix(
            self.image_r, self.filterP[self.filter_r]['seed'])
        self.image_r *= self.FlatMat['r']
        ###
        self.FlatMat['i'] = self.MakeFlatMatrix(
            self.image_i, self.filterP[self.filter_i]['seed'])
        self.image_g *= self.FlatMat['i']
        return

######################################################################################################
    def applyPRNUeffect(self):
        """
        Applies multiplicative flat field to emulate pixel-to-pixel non-uniformity.

        Because the pixel-to-pixel non-uniformity effect (i.e. multiplicative) flat fielding takes place
        before CTI and other effects, the flat field file must be the same size as the pixels that see
        the sky."""

        # generate flatfiledfile, with
        flatsigma = self.information['flatsigma']

        self.generatePRNU(ave=1.0, sigma=flatsigma)
        self.image_g *= self.PRNU['g']
        self.image_r *= self.PRNU['r']
        self.image_i *= self.PRNU['i']
        self.log.info('Applied flatfield to images.')

##################################################################################################

##################################################################################################

    def addCosmicRays(self):
        """
        Add cosmic rays to the arrays based on a power-law intensity distribution for tracks.
        Cosmic ray properties (such as location and angle) are chosen from random Uniform distribution.
        For details, see the documentation for the cosmicrays class in the support package.
        """
        self.readCosmicRayInformation()

        # to scale the number of cosmics with exposure time
        self.cr['exptime'] = self.information['exptime']

        # cosmic ray image
        crImage = np.zeros(
            (self.information['ysize'], self.information['xsize']), dtype=float)

        # cosmic ray instance
        cosmics_g = cosmicrays.cosmicrays(
            self.log, crImage, self.information['exptime'], crInfo=self.cr)
        cosmics_r = cosmicrays.cosmicrays(
            self.log, crImage, self.information['exptime'], crInfo=self.cr)
        cosmics_i = cosmicrays.cosmicrays(
            self.log, crImage, self.information['exptime'], crInfo=self.cr)

        CCD_cr_g = cosmics_g.addUpToFraction(
            self.information['coveringfraction']*self.information['exptime']/300.0, limit=None)
        CCD_cr_r = cosmics_r.addUpToFraction(
            self.information['coveringfraction']*self.information['exptime']/300.0, limit=None)
        CCD_cr_i = cosmics_i.addUpToFraction(
            self.information['coveringfraction']*self.information['exptime']/300.0, limit=None)

        # paste the information
        # self.image += CCD_cr
        self.image_g += CCD_cr_g
        self.image_r += CCD_cr_r
        self.image_i += CCD_cr_i

        if self.save_cosmicrays and self.cosmicRays:

            self.log.info('Saved the cosmicRays fits...')
            fits.writeto(self.result_path+'/ori_Sky/cosmicMap_C1_N_'+str(
                self.information['simnumber'])+'.fits', np.int32(CCD_cr_g), overwrite=True)
            fits.writeto(self.result_path+'/ori_Sky/cosmicMap_C2_N_'+str(
                self.information['simnumber'])+'.fits', np.int32(CCD_cr_r), overwrite=True)
            fits.writeto(self.result_path+'/ori_Sky/cosmicMap_C3_N_'+str(
                self.information['simnumber'])+'.fits', np.int32(CCD_cr_i), overwrite=True)

        # count the covering factor
        # area_cr = np.count_nonzero(self.cosmicMap)
        area_cr_g = np.count_nonzero(CCD_cr_g)
        area_cr_r = np.count_nonzero(CCD_cr_r)
        area_cr_i = np.count_nonzero(CCD_cr_i)

        # self.log.info('The cosmic ray covering factor is %i pixels ' % area_cr)
        self.log.info(
            'The cosmic ray in C1 channel covering factor is %i pixels ' % area_cr_g)
        self.log.info(
            'The cosmic ray in C2 channel covering factor is %i pixels ' % area_cr_r)
        self.log.info(
            'The cosmic ray in C3 channel covering factor is %i pixels ' % area_cr_i)

########################################################################################

    def ShutterEffectMat(self,  img, t_exp=300, t_shutter=1.3, dist_bearing=4000, dt=0.001):
        # Generate Shutter-Effect normalized image
        # t_shutter: time of shutter movement
        # dist_bearing: distance between two bearings of shutter leaves
        # dt: delta_t of sampling

        SampleNumb = int(t_shutter/dt+1)

        DistHalf = dist_bearing/2

        t = np.arange(SampleNumb)*dt
        a_arr = 5.84*np.sin(2*math.pi/t_shutter*t)
        v = np.zeros(SampleNumb)
        theta = np.zeros(SampleNumb)
        x = np.arange(SampleNumb)/(SampleNumb-1)*dist_bearing
        s = np.zeros(SampleNumb)
        s1 = np.zeros(SampleNumb)
        s2 = np.zeros(SampleNumb)
        brt = np.zeros(SampleNumb)
        idx = np.arange(SampleNumb)
        sidx = np.zeros(SampleNumb)
        s1idx = np.zeros(SampleNumb)
        s2idx = np.zeros(SampleNumb)

        v[0] = 0
        theta[0] = 0

        for i in range(SampleNumb-1):
            v[i+1] = v[i]+a_arr[i]*dt
            theta[i+1] = theta[i]+v[i]*dt
            s1[i] = DistHalf*np.cos(theta[i])
            s2[i] = dist_bearing-DistHalf*np.cos(theta[i])
            s1idx[i] = int(s1[i]/dist_bearing*(SampleNumb))
            s2idx[i] = int(s2[i]/dist_bearing*(SampleNumb))
            brt[(idx > s1idx[i]) & (idx < s2idx[i])] += dt

        if t_exp > t_shutter*2:
            brt = brt*2+(t_exp-t_shutter*2)
        else:
            brt = brt*2

        x = (x-dist_bearing/2)*100

        intp = interpolate.splrep(x, brt, s=0)

        xmin = 0    #
        xmax = img.shape[1]  # row
        ymin = 0    #
        ymax = img.shape[0]  # column
        if xmin < np.min(x) or xmax > np.max(x):
            raise LookupError("Out of focal-plane bounds in X-direction.")
        if ymin < 0 or ymax > 25331:
            raise LookupError("Out of focal-plane bounds in Y-direction.")

        sizex = xmax-xmin
        sizey = ymax-ymin
        xnewgrid = np.mgrid[xmin:(xmin+sizex)]
        expeffect = interpolate.splev(xnewgrid, intp, der=0)
        expeffect /= np.max(expeffect)
        exparrnormal = np.tile(expeffect, (sizey, 1))

        # Image *= exparrnormal

        shutter = (1-exparrnormal)*t_exp
        shutter = shutter/shutter.max()*(t_shutter*2)

        return shutter

########################################################################################
    def addshuttereffect(self):
        ''' apply shutter effect to image'''
        #
        if self.information['exptime'] > 0:
            self.shutter = self.ShutterEffectMat(self.image_g)

            self.shutterMat = 1-self.shutter/self.information['exptime']

            self.image_g *= self.shutterMat
            self.image_r *= self.shutterMat
            self.image_i *= self.shutterMat
            if self.information['simnumber'] == 1:
                fits.writeto(self.result_path+'/cali_Data/' +
                             'shutter.fits', self.shutter, overwrite=True)

        return

########################################################################################
    def applyDarkCurrent(self):
        """
        Apply dark current. Scales the dark with the exposure time.

        Additionally saves the image without noise to a FITS file.
        """

        # add dark
        dark = self.information['exptime'] * self.information['dark']

        self.image_g += dark
        self.image_r += dark
        self.image_i += dark
        self.log.info('Added dark current = %f' % dark)

    ###########################################################################
    ###########################################################################
    def applyPoissonSkyNoise(self):
        """
        Add Poisson sky noise to the image.
        """
        skynoise_g = self.Sky_Noise['g']*np.ones_like(self.image_g)

        np.random.seed(11*self.information['simnumber'])
        self.image_g = self.image_g + np.random.poisson(lam=skynoise_g)
        self.log.info('Added Poisson sky noise on channel g')

        skynoise_r = self.Sky_Noise['r']*np.ones_like(self.image_r)
        np.random.seed(111*self.information['simnumber'])
        self.image_r = self.image_r + np.random.poisson(lam=skynoise_r)
        self.log.info('Added Poisson sky noise on channel r')

        skynoise_i = self.Sky_Noise['i']*np.ones_like(self.image_i)
        np.random.seed(1111*self.information['simnumber'])
        self.image_i = self.image_i + np.random.poisson(lam=skynoise_i)
        self.log.info('Added Poisson sky noise on channel i')

        return

###############################################################################
###############################################################################
    def applyCosmetics(self):
        """
        Apply cosmetic defects described in the input file.

        #Number of hot and dead pixels from MSSL/Euclid/TR/12003 Issue 2 Draft b

        .. Warning:: This method does not work if the input file has exactly one line.
        """
        #######################################################################
        cosmetics = np.loadtxt(
            self.information['dir_path']+'MCI_inputData/data/Cosmetics_g.txt')

        x = np.round(cosmetics[:, 0]).astype(int)  # row number
        y = np.round(cosmetics[:, 1]).astype(int)  # col number
        value = np.round(cosmetics[:, 2]).astype(int)

        # cosmetics_g=np.zeros((4636,235526))

        self.log.info('Adding cosmetic defects to C1 channel:')
        for xc, yc, val in zip(x, y, value):
            if 0 < xc < 4616 and 27 < (yc % 1499) < 1179:

                self.image_g[xc, yc] = val
                # cosmetics_g[yc,xc]=val
                self.log.info('x=%i, y=%i, value=%f' % (xc, yc, val))
        ######################################################################
        #######################################################################
        cosmetics = np.loadtxt(
            self.information['dir_path']+'MCI_inputData/data/Cosmetics_r.txt')

        x = np.round(cosmetics[:, 0]).astype(int)  # row number
        y = np.round(cosmetics[:, 1]).astype(int)  # col number
        value = np.round(cosmetics[:, 2]).astype(int)

        self.log.info('Adding cosmetic defects to C2 channel:')
        for xc, yc, val in zip(x, y, value):
            if 0 < xc < 4616 and 27 < (yc % 1499) < 1179:

                self.image_r[xc, yc] = val
                # cosmetics_r[yc,xc]=val
                self.log.info('x=%i, y=%i, value=%f' % (xc, yc, val))
##############################################################################
        #######################################################################
        cosmetics = np.loadtxt(
            self.information['dir_path']+'MCI_inputData/data/Cosmetics_i.txt')

        x = np.round(cosmetics[:, 0]).astype(int)  # row number
        y = np.round(cosmetics[:, 1]).astype(int)  # col number
        value = np.round(cosmetics[:, 2]).astype(int)

        # cosmetics_i=np.zeros((4636,235526))

        self.log.info('Adding cosmetic defects to C3 channel:')
        for xc, yc, val in zip(x, y, value):
            if 0 < xc < 4616 and 27 < (yc % 1499) < 1179:

                self.image_i[xc, yc] = val
                # cosmetics_i[yc,xc]=val
                self.log.info('x=%i, y=%i, value=%f' % (xc, yc, val))

        # ######  save cosmetics image  code ####
        # if self.simnumber<2:
        #     #save cosmic ray image map

        #     obsid=self.simnumber
        #     bluefile=self.result_path+'/calibration_Data/IFS_cosmetics'+'_'+str(obsid)+'_B.fits'
        #     redfile =self.result_path+'/calibration_Data/IFS_cosmetics'+'_'+str(obsid)+'_R.fits'

        #     fits.writeto(bluefile, cosmetics_b, overwrite=True)
        #     fits.writeto(redfile,  cosmetics_r, overwrite=True)

        #     #output information to a FITS file

        #     self.log.info('Save cosmetics images fits file to  calibration_Data file ')
#############################################################################################################################
    def applyRadiationDamage(self):
        """
        Applies CDM03 radiation model to the image being constructed.

        .. seealso:: Class :`CDM03`
        """
        # save image without CTI
        self.noCTI_g = self.image_g.copy()

        self.log.debug('Starting to apply radiation damage model...')
        # at this point we can give fake data...
        cti = CDM03bidir(self.information, [], log=self.log)
        # here we need the right input data
        self.image_g = cti.applyRadiationDamage(self.image_g.copy(
        ).transpose(), iquadrant=self.information['quadrant']).transpose()
        self.log.info('Radiation damage added.')

        #################################################

        self.noCTI_r = self.image_r.copy()

        self.log.debug('Starting to apply radiation damage model...')
        # at this point we can give fake data...
        cti = CDM03bidir(self.information, [], log=self.log)
        # here we need the right input data
        self.image_r = cti.applyRadiationDamage(self.image_r.copy(
        ).transpose(), iquadrant=self.information['quadrant']).transpose()
        self.log.info('Radiation damage added.')

        ##################################################

        self.noCTI_i = self.image_i.copy()

        self.log.debug('Starting to apply radiation damage model...')
        # at this point we can give fake data...
        cti = CDM03bidir(self.information, [], log=self.log)
        # here we need the right input data
        self.image_i = cti.applyRadiationDamage(self.image_i.copy(
        ).transpose(), iquadrant=self.information['quadrant']).transpose()
        self.log.info('Radiation damage added.')

    ###########################################################################

    def applyNonlinearity(self):
        """
        Applies a CCD273 non-linearity model to the image being constructed.

        """

        self.log.debug('Starting to apply non-linearity model...')
        self.image_g = CCDnonLinearityModel(self.image_g.copy())
        self.log.info('Non-linearity effects included.')

        ########################################################################
        self.log.debug('Starting to apply non-linearity model...')
        self.image_r = CCDnonLinearityModel(self.image_r.copy())
        self.log.info('Non-linearity effects included.')

        ########################################################################
        self.log.debug('Starting to apply non-linearity model...')
        self.image_i = CCDnonLinearityModel(self.image_i.copy())

        self.log.info('Non-linearity effects included.')

###############################################################################

    def applyReadoutNoise(self):
        """
        Applies readout noise to the image being constructed.

        The noise is drawn from a Normal (Gaussian) distribution with average=0.0 and std=readout noise.
        """
        if self.overscans and self.information['xsize'] == 9216 and self.information['ysize'] == 9232:
            xmin = dict()
            xmax = dict()
            ymin = dict()
            ymax = dict()

            # calculate the non-over subimg of the final matrix of the image
            for k in range(1, 17):
                xmin[k] = 0
                xmax[k] = 4616+self.overscan

                ymin[k] = (1152+self.prescan+self.overscan)*(k-1)
                ymax[k] = (1152+self.prescan+self.overscan)*(k)
                ###############
                np.random.seed(2*self.information['simnumber'])
                noise_g = np.random.normal(loc=0.0, scale=self.information['g_rdnois'+str(
                    k)], size=(4616+self.overscan, 1152+self.overscan+self.prescan))
                self.image_g[xmin[k]:xmax[k], ymin[k]:ymax[k]] += noise_g
                self.log.info('Bias of %i counts were added to the g band image' %
                              self.information['g_rdnois'+str(k)])

                np.random.seed(22*self.information['simnumber'])
                noise_r = np.random.normal(loc=0.0, scale=self.information['r_rdnois'+str(
                    k)], size=(4616+self.overscan, 1152+self.overscan+self.prescan))
                self.image_r[xmin[k]:xmax[k], ymin[k]:ymax[k]] += noise_r
                self.log.info('Bias of %i counts were added to the r band image' %
                              self.information['r_rdnois'+str(k)])

                np.random.seed(222*self.information['simnumber'])
                noise_i = np.random.normal(loc=0.0, scale=self.information['i_rdnois'+str(
                    k)], size=(4616+self.overscan, 1152+self.overscan+self.prescan))
                self.image_i[xmin[k]:xmax[k], ymin[k]:ymax[k]] += noise_i
                self.log.info('Bias of %i counts were added to the i band image' %
                              self.information['i_rdnois'+str(k)])

                # end for

        else:

            np.random.seed(3*self.information['simnumber'])
            noise = np.random.normal(loc=0.0, scale=4, size=self.image_g.shape)
            self.log.info('Sum of readnoise in g channel = %f' % np.sum(noise))
            # add to the image
            self.image_g += noise

            #######################################################
            np.random.seed(33*self.information['simnumber'])
            noise = np.random.normal(loc=0.0, scale=4, size=self.image_r.shape)
            self.log.info('Sum of readnoise in r channel= %f' % np.sum(noise))
            # add to the image
            self.image_r += noise

            ########################################
            np.random.seed(333*self.information['simnumber'])
            noise = np.random.normal(loc=0.0, scale=4, size=self.image_i.shape)
            self.log.info('Sum of readnoise in i channel= %f' % np.sum(noise))
            # add to the image
            self.image_i += noise

        return

##########################################################################################################

    def electrons2ADU(self):
        """
        Convert from electrons to ADUs using the value read from the configuration file.
        """
        #####
        if self.overscans:
            ######################
            xmin = dict()
            xmax = dict()
            ymin = dict()
            ymax = dict()

        # calculate the non-over subimg of the final matrix of the image
            for k in range(1, 17):
                xmin[k] = 0
                xmax[k] = 4616+self.overscan

                ymin[k] = (1152+self.overscan+self.prescan)*(k-1)
                ymax[k] = (1152+self.overscan+self.prescan)*(k)
                ###############

                self.image_g[xmin[k]:xmax[k], ymin[k]:ymax[k]
                             ] /= self.information['g_gain'+str(k)]

                self.image_r[xmin[k]:xmax[k], ymin[k]:ymax[k]
                             ] /= self.information['r_gain'+str(k)]

                self.image_i[xmin[k]:xmax[k], ymin[k]:ymax[k]
                             ] /= self.information['i_gain'+str(k)]

            # end for
            return

##############################################################################

    def applyBias(self):
        """
        Adds a bias level to the image being constructed.

        The value of bias is read from the configure file and stored
        in the information dictionary (key bias).

        """

        xmin = dict()
        xmax = dict()
        ymin = dict()
        ymax = dict()

        # calculate the non-over subimg of the final matrix of the image
        for k in range(1, 17):
            xmin[k] = 0
            xmax[k] = 4616+self.overscan

            ymin[k] = (1152+self.prescan+self.overscan)*(k-1)
            ymax[k] = (1152+self.prescan+self.overscan)*(k)
            ###############
            self.image_g[xmin[k]:xmax[k], ymin[k]:ymax[k]
                         ] += self.information['g_detbia'+str(k)]
            self.log.info('Bias of %i counts were added to the g band image' %
                          self.information['g_detbia'+str(k)])

            self.image_r[xmin[k]:xmax[k], ymin[k]:ymax[k]
                         ] += self.information['r_detbia'+str(k)]
            self.log.info('Bias of %i counts were added to the g band image' %
                          self.information['r_detbia'+str(k)])

            self.image_i[xmin[k]:xmax[k], ymin[k]:ymax[k]
                         ] += self.information['i_detbia'+str(k)]
            self.log.info('Bias of %i counts were added to the g band image' %
                          self.information['i_detbia'+str(k)])

            # end for
        return

##############################################################################
    # def img_fits_save(self, data, filename):
    #     ####

    #     ofd_g = fits.PrimaryHDU()
    #     # World coordinate system and related parameters  #####
    #     hdu_g = fits.ImageHDU(data)

    #     hdu_g.header['WCSAXES'] = (np.int16(2),    'number of WCS axes')
    #     hdu_g.header['CRPIX1'] = (
    #         round(float(self.information['CRPIX1']), 1), 'x-coordinate of reference pixel')
    #     hdu_g.header['CRPIX2'] = (
    #         round(float(self.information['CRPIX2']), 1), 'y-coordinate of reference pixel')

    #     hdu_g.header['CRVAL1'] = (
    #         float(self.information['CRVAL1']), 'first axis value at reference pixel')
    #     hdu_g.header['CRVAL2'] = (
    #         float(self.information['CRVAL2']), 'second axis value at reference pixel')
    #     hdu_g.header['CTYPE1'] = (
    #         'RA---TAN', 'the coordinate type for the first axis')
    #     hdu_g.header['CTYPE2'] = (
    #         'DEC--TAN', 'the coordinate type for the second axis')
    #     hdu_g.header['CD1_1'] = (
    #         float(self.information['CD1_1']), 'partial of first axis coordinate w.r.t. x')
    #     hdu_g.header['CD1_2'] = (
    #         float(self.information['CD1_2']), 'partial of first axis coordinate w.r.t. y')
    #     hdu_g.header['CD2_1'] = (
    #         float(self.information['CD2_1']), 'partial of second axis coordinate w.r.t. x')
    #     hdu_g.header['CD2_2'] = (
    #         float(self.information['CD2_2']), 'partial of second axis coordinate w.r.t. y')

    #     hdulist_g = fits.HDUList([ofd_g, hdu_g])

    #     file_g = self.result_path+'/ori_Sky/'+filename + '.fits'

    #     hdulist_g.writeto(file_g, overwrite=True)
    #     return
###########
########
##############################################################################
######

    def applyBleeding(self, img, direction='not_horizon'):
        """
        Apply bleeding along the CCD readout direction if the number of electrons in a pixel exceeds the full-well capacity.

       direction: this is the CCD readout direction, and default value is horizon direction

        :return: the bleeding image
        """

        data = img.copy()

        if data.max() < self.information['fullwellcapacity']:
            return img

        else:

            if direction == 'horizon':
                self.information['bleding_direction'] = 'Not_horizon'
                # loop over each column, as bleeding is modelled column-wise
                # for i, column in enumerate(data):  # select one solumnn
                #     if column.max() <= self.information['fullwellcapacity']:
                #         continue
                #     sum = 0.
                #     for j, value in enumerate(column):
                #         # first round - from bottom to top (need to half the bleeding)
                #         overload = value - self.information['fullwellcapacity']
                #         if overload > 0.:
                #             overload /= 2.
                #             # self.image[j, i] -= overload
                #             data[i, j] -= overload
                #             sum += overload

                #         elif sum > 0.:
                #             if -overload > sum:
                #                 overload = -sum
                #             # self.image[j, i] -= overload
                #             data[i, j] -= overload
                #             sum += overload
                # ######################################################
                # for i, column in enumerate(data):
                #     if column.max() <= self.information['fullwellcapacity']:
                #         continue
                #     sum = 0.
                #     for j, value in enumerate(column[::-1]):
                #         # second round - from top to bottom (bleeding was half'd already, so now full)
                #         overload = value - self.information['fullwellcapacity']
                #         if overload > 0.:
                #             # self.image[-j-1, i] -= overload
                #             data[i, -j-1] -= overload
                #             sum += overload

                #         elif sum > 0.:
                #             if -overload > sum:
                #                 overload = -sum
                #             # self.image[-j-1, i] -= overload
                #             data[i, -j-1,] -= overload
                #             sum += overload

            else:

                # loop over each column, as bleeding is modelled column-wise
                for i, column in enumerate(data.T):
                    sum = 0.
                    for j, value in enumerate(column):
                        # first round - from bottom to top (need to half the bleeding)
                        overload = value - self.information['fullwellcapacity']
                        if overload > 0.:
                            overload /= 2.
                            # self.image[j, i] -= overload
                            data[j, i] -= overload

                            sum += overload
                        elif sum > 0.:
                            if -overload > sum:
                                overload = -sum
                            # self.image[j, i] -= overload
                            data[j, i] -= overload
                            sum += overload
            ################################
                for i, column in enumerate(data.T):
                    sum = 0.
                    for j, value in enumerate(column[::-1]):
                        # second round - from top to bottom (bleeding was half'd already, so now full)
                        overload = value - self.information['fullwellcapacity']
                        if overload > 0.:
                            # self.image[-j-1, i] -= overload
                            data[-j-1, i] -= overload

                            sum += overload
                        elif sum > 0.:
                            if -overload > sum:
                                overload = -sum
                            # self.image[-j-1, i] -= overload
                            data[-j-1, i] -= overload
                            sum += overload

            # ####print('Applying column bleeding finished.......')
            return data

    ############################################################################

    def discretise(self, max=2**16-1):
        """
        Converts a floating point image array (self.image) to an integer array with max values
        defined by the argument max.

        :param max: maximum value the the integer array may contain [default 65k]
        :type max: float

        :return: None
        """

        # avoid negative numbers in case bias level was not added
        self.image_g[self.image_g < 0.0] = 0.
        # cut of the values larger than max
        self.image_g[self.image_g > max] = max

        self.image_g = np.rint(self.image_g).astype(int)
        self.log.info('Maximum and total values of the image are %i and %i, respectively' % (np.max(self.image_g),
                                                                                             np.sum(self.image_g)))

        # avoid negative numbers in case bias level was not added
        self.image_r[self.image_r < 0.0] = 0.
        # cut of the values larger than max
        self.image_r[self.image_r > max] = max

        self.image_r = np.rint(self.image_r).astype(int)
        self.log.info('Maximum and total values of the image are %i and %i, respectively' % (np.max(self.image_r),
                                                                                             np.sum(self.image_r)))

        ###############################################################################

        # avoid negative numbers in case bias level was not added
        self.image_i[self.image_i < 0.0] = 0.
        # cut of the values larger than max
        self.image_i[self.image_i > max] = max

        self.image_i = np.rint(self.image_i).astype(int)
        self.log.info('Maximum and total values of the image are %i and %i, respectively' % (np.max(self.image_i),
                                                                                             np.sum(self.image_i)))

    ##################################################################################################
    def addScanEffect(self, img):
        # read CCD image to matrix as defined data format
        '''
        function: add prescan and overscan empty zone to the CCD physical image

        return:  img

        '''

        # ## xsize is column, ysize is row

        # horizon direction , columnn
        self.prescan = self.information['prescan']
        self.overscan = self.information['overscan']  # vertical direction, row

        row = self.information['ysize']
        col = self.information['xsize']
        if row != 9232 or col != 9216:
            print('image size is not correct.......')
            sys.exit(1)
        ####
        ###########################################
        xmin = dict()
        xmax = dict()
        ymin = dict()
        ymax = dict()
        # physical CCD zone 1-16
        for n in range(1, 17):

            # physical zone index
            if n < 9:
                xmin[n] = 4616           # row number
                xmax[n] = 4616*2

                ymin[n] = (n-1)*1152     # col number
                ymax[n] = ymin[n]+1152
            else:
                xmin[n] = 0
                xmax[n] = 4616

                ymin[n] = (16-n)*1152
                ymax[n] = ymin[n]+1152
        ###########################################

        temp = np.zeros(
            (int(4616+self.overscan), int((1152+self.overscan+self.prescan)*16)))

        matxmin = dict()
        matxmax = dict()
        matymin = dict()
        matymax = dict()

# get the non-over subimg of the final matrix of the image
        for k in range(1, 17):

            # big new image include prescan and overscan;
            matxmin[k] = 0  # row number
            matxmax[k] = 4616  # row number

            matymin[k] = (1152+self.overscan+self.prescan) * \
                (k-1)+self.prescan  # col number start
            matymax[k] = matymin[k]+1152  # col number end

        ################################

        for k in range(1, 17):
            if k <= 4:  # zos1 zos2 zos3 zos4
                # get k-th subimg from input img
                subimg = img[xmin[k]:xmax[k], ymin[k]:ymax[k]]
                subimg = np.flipud(subimg)  # down to up, up to down

            if k >= 5 and k <= 8:  # zos5 zos6 zos7 zos8
                # get k-th subimg from input img
                subimg = img[xmin[k]:xmax[k], ymin[k]:ymax[k]]
                subimg = np.flipud(subimg)  # down to up, up to down
                subimg = np.fliplr(subimg)  # left to right  ,right to left

            if k >= 9 and k <= 12:  # zos5 zos6 zos7 zos8
                # get k-th subimg from input img
                subimg = img[xmin[k]:xmax[k], ymin[k]:ymax[k]]
                subimg = np.fliplr(subimg)  # left to right  ,right to left

            if k >= 13 and k <= 16:  # zos5 zos6 zos7 zos8
                # get k-th subimg from input img
                subimg = img[xmin[k]:xmax[k], ymin[k]:ymax[k]]

            ######################

            temp[matxmin[k]:matxmax[k], matymin[k]:matymax[k]] = subimg[:, :]
        # end for
        return temp
    ###########################################################################

    #########################################################################################

    def writeOutputs(self, obnum=1):
        """
        Writes out a FITS file using PyFITS and converts the image array to 16bit unsigned integer as
        appropriate for VIS.

        Updates header with the input values and flags used during simulation.
        """

        # Readout information

        HeaderTest = 'no'
        ##########################################################################
        if self.source == 'EXDF':
            obsid = 20100000000+obnum

        if self.source == 'STAR':
            obsid = 20200000000+obnum

        if self.source == 'PIPR':
            obsid = 20300000000+obnum

        if self.source == 'TRNS':
            obsid = 20400000000+obnum

        if self.source == 'COMB':
            obsid = 20500000000+obnum

        if self.source == 'CALI':
            obsid = 20600000000+obnum

        if self.source == 'BIAS':
            obsid = 20700000000+obnum

        if self.source == 'DARK':
            obsid = 20800000000+obnum

        if self.source == 'FLAT':
            obsid = 20900000000+obnum
        #############################

        if self.source == 'OSBS':
            obsid = 21000000000+obnum

        if self.source == 'OSDK':
            obsid = 21100000000+obnum

        if self.source == 'OSFT':
            obsid = 21200000000+obnum

        if self.source == 'FRNG':
            obsid = 21300000000+obnum

        #######################################################################
        exp_starttime = self.dt.strftime("%Y%m%d%H%M%S")

        # exposure end time is t2 ;
        t2 = self.dt+timedelta(seconds=self.information['exptime'])
        exp_endtime = t2.strftime("%Y%m%d%H%M%S")
        # ## data read time is the exposure end time ###
        # data read end time is t3, and this is the write file time;
        t3 = self.dt+timedelta(seconds=self.information['exptime'])+timedelta(
            seconds=self.information['readouttime'])

        if self.filterP[self.filter_g]['number'] > 9:
            filternum = str(self.filterP[self.filter_g]['number'])
        else:
            filternum = '0'+str(self.filterP[self.filter_g]['number'])

        filename_g = 'CSST_MCI_C1_'+self.source+'_'+exp_starttime+'_'+exp_endtime+'_'+str(obsid)+'_' + \
            filternum+'_L0_V01'

        #######################################################################
        #######################################################################
        # create a new FITS file, using HDUList instance
        ofd_g = fits.PrimaryHDU()
        # add primary header
        ofd_g = fits.PrimaryHDU()
        ofd_g.header['SIMPLE'] = (True,   'conforms to FITS standard')
        ofd_g.header['BITPIX'] = (0,   'array data type')
        ofd_g.header['NAXIS'] = (0,   'number of array dimensions')
        ofd_g.header['NEXTEND'] = (np.int16(1),   'number of array dimensions')
        #######################################################################
        ###########################
        temp = t3.utcnow()
        data_time = temp.strftime("%Y-%m-%dT%H:%M:%S.%f")
        ofd_g.header['DATE'] = (
            data_time[:21], 'written date (yyyy-mm-ddThh:mm:ss.s)')
        ofd_g.header['FILENAME'] = (filename_g[:68], '')
        ofd_g.header['FILETYPE'] = (self.source[:12],   'observation type')
        ofd_g.header['TELESCOP'] = ('CSST',   'telescope name')
        ofd_g.header['INSTRUME'] = ('MCI',   'instrument name')
        ofd_g.header['RADECSYS'] = (
            'ICRS',   'coordinate system of the object')
        ofd_g.header['EQUINOX'] = (float(2000.0),   '')
        ofd_g.header['FITSSWV'] = (
            'csst_mci_sim_3.0.0',   'FITS creating software version')
        ##############################################################################
        # ########   Object information  #########################################
        ofd_g.header['OBJECT'] = (
            self.information['name_obj'][:30],   'object name')
        ofd_g.header['TARGET'] = (
            self.information['target'][:15],     'target name (hhmmss.s+ddmmss)')
        #######################################################################
        ofd_g.header['OBSID'] = (str(obsid),   'observation ID')
        ofd_g.header['RA_OBJ'] = (
            float(self.information['ra_obj']),   'object RA (deg)')
        ofd_g.header['DEC_OBJ'] = (
            float(self.information['dec_obj']),   'object Dec (deg)')

        # #######  Telescope information ###############

        ofd_g.header['REFFRAME'] = ('CSSTGSC-1.0',   'guiding catalog version')
        ofd_g.header['DATE-OBS'] = (data_time[:21],
                                    'observation date (yyyy-mm-ddThh:mm:ss.s)')
        ofd_g.header['SATESWV'] = (
            'softwave-1.0',   'satellite software version')
        ofd_g.header['EXPSTART'] = (np.float64(
            time2mjd(self.dt)),   'exposure start time (MJD)')
        ofd_g.header['CABSTART'] = (np.float64(
            time2jd(self.dt)),   'first cabin time after exposure start (MJD)')
        ofd_g.header['SUNANGL0'] = (np.float32(
            0.0),   'angle between the Sun and opt axis at CABSTART')
        ofd_g.header['MOONANG0'] = (np.float32(
            0.0),   'angle between the Moon and opt axis at CABSTART')
        ofd_g.header['TEL_ALT0'] = (np.float64(
            0.0),   'angle between opt axis and Elimb at CABSTART')
        ofd_g.header['POS_ANG0'] = (np.float64(
            0.0),   'angle between y axis and North Pole at CABSTART')
        ofd_g.header['POSI0_X'] = (np.float64(
            self.information['pos_x']),   'orbital position in X at CABSTART (km)')
        ofd_g.header['POSI0_Y'] = (np.float64(
            self.information['pos_y']),   'orbital position in Y at CABSTART (km)')
        ofd_g.header['POSI0_Z'] = (np.float64(
            self.information['pos_z']),   'orbital position in Z at CABSTART (km)')
        ofd_g.header['VELO0_X'] = (np.float64(
            self.information['velocity_x']),   'orbital velocity in X at CABSTART (km/s)')
        ofd_g.header['VELO0_Y'] = (np.float64(
            self.information['velocity_y']),   'orbital velocity in Y at CABSTART (km/s)')
        ofd_g.header['VELO0_Z'] = (np.float64(
            self.information['velocity_z']),   'orbital velocity in Z at CABSTART (km/s)')
        ofd_g.header['Euler0_1'] = (np.float64(
            0.0),   'Euler angle 1 at CABSTART (deg)')
        ofd_g.header['Euler0_2'] = (np.float64(
            0.0),   'Euler angle 2 at CABSTART (deg)')
        ofd_g.header['Euler0_3'] = (np.float64(
            0.0),   'Euler angle 3 at CABSTART (deg)')

        ##############################################################
        #####
        ofd_g.header['RA_PNT0'] = (
            float(self.information['ra_pnt0']),     'pointing RA at CABSTART (deg)')
        ofd_g.header['DEC_PNT0'] = (
            float(self.information['dec_pnt0']),     'pointing Dec at CABSTART (deg)')
        ofd_g.header['EXPEND'] = (float(time2mjd(t2)),
                                  'exposure end time (MJD)')
        ofd_g.header['CABEND'] = (float(time2jd(t2)),
                                  'first cabin time after exposure end (MJD)')
        ofd_g.header['SUNANGL1'] = (np.float32(
            0.0),   'angle between the Sun and opt axis at CABEND')
        ofd_g.header['MOONANG1'] = (np.float32(
            0.0),   'angle between the Moon and opt axis at CABEND')
        ofd_g.header['TEL_ALT1'] = (np.float64(
            0.0),   'angle between opt axis and Elimb at CABEND')
        ofd_g.header['POS_ANG1'] = (
            float(0.0),   'angle between y axis and North Pole at CABEND')

        ##############################################################

        t2jd = time2jd(t2)

        if self.orbit_pars[-1, 0] < t2jd:  # orbit parameters are not in currenct txt file
            self.orbit_file_num = self.orbit_file_num+1
            fn = self.information['dir_path'] + \
                'MCI_inputData/TianCe/orbit20160925/' + \
                str(self.orbit_file_num)+'.txt'
            self.orbit_pars = np.loadtxt(fn)
            self.orbit_exp_num = 0

        for k in range(self.orbit_exp_num, len(self.orbit_pars), 1):

            if t2jd-self.orbit_pars[k, 0] < 0:
                break
        if k == 0:
            deltaT = jd2time(self.orbit_pars[k, 0])-t2
            p1x = self.orbit_pars[k, 1]-(self.orbit_pars[k+1, 1] -
                                         self.orbit_pars[k, 1])*deltaT.seconds/120
            p1y = self.orbit_pars[k, 2]-(self.orbit_pars[k+1, 2] -
                                         self.orbit_pars[k, 2])*deltaT.seconds/120
            p1z = self.orbit_pars[k, 3]-(self.orbit_pars[k+1, 3] -
                                         self.orbit_pars[k, 3])*deltaT.seconds/120

            p1vx = self.orbit_pars[k, 4]-(self.orbit_pars[k+1, 4] -
                                          self.orbit_pars[k, 4])*deltaT.seconds/120
            p1vx = self.orbit_pars[k, 5]-(self.orbit_pars[k+1, 5] -
                                          self.orbit_pars[k, 5])*deltaT.seconds/120
            p1vx = self.orbit_pars[k, 6]-(self.orbit_pars[k+1, 6] -
                                          self.orbit_pars[k, 6])*deltaT.seconds/120

        else:
            deltaT = jd2time(self.orbit_pars[k, 0])-t2
            p1x = self.orbit_pars[k-1, 1]+(self.orbit_pars[k, 1] -
                                           self.orbit_pars[k-1, 1])*deltaT.seconds/120
            p1y = self.orbit_pars[k-1, 2]+(self.orbit_pars[k, 2] -
                                           self.orbit_pars[k-1, 2])*deltaT.seconds/120
            p1z = self.orbit_pars[k-1, 3]+(self.orbit_pars[k, 3] -
                                           self.orbit_pars[k-1, 3])*deltaT.seconds/120

            p1vx = self.orbit_pars[k-1, 4]+(self.orbit_pars[k, 4] -
                                            self.orbit_pars[k-1, 4])*deltaT.seconds/120
            p1vy = self.orbit_pars[k-1, 5]+(self.orbit_pars[k, 5] -
                                            self.orbit_pars[k-1, 5])*deltaT.seconds/120
            p1vz = self.orbit_pars[k-1, 6]+(self.orbit_pars[k, 6] -
                                            self.orbit_pars[k-1, 6])*deltaT.seconds/120

        ofd_g.header['POSI1_X'] = (
            float(p1x),   'orbital position in X at CABEND (km)')
        ofd_g.header['POSI1_Y'] = (
            float(p1y),   'orbital position in Y at CABEND (km)')
        ofd_g.header['POSI1_Z'] = (
            float(p1z),   'orbital position in Z at CABEND (km)')

        ofd_g.header['VELO1_X'] = (
            float(p1vx),   'orbital velocity in X at CABEND (km/s)')
        ofd_g.header['VELO1_Y'] = (
            float(p1vy),   'orbital velocity in Y at CABEND (km/s)')
        ofd_g.header['VELO1_Z'] = (
            float(p1vz),   'orbital velocity in Z at CABEND (km/s)')

        ###################

        ofd_g.header['Euler1_1'] = (np.float64(
            0.0),   'Euler angle 1 at CABEND (deg)')
        ofd_g.header['Euler1_2'] = (np.float64(
            0.0),   'Euler angle 2 at CABEND (deg)')
        ofd_g.header['Euler1_3'] = (np.float64(
            0.0),   'Euler angle 3 at CABEND (deg)')
        ofd_g.header['RA_PNT1'] = (
            float(ofd_g.header['RA_PNT0']),    'pointing RA at CABEND (deg)')
        ofd_g.header['DEC_PNT1'] = (
            float(ofd_g.header['DEC_PNT0']),    'pointing Dec at CABEND (deg)')
        ofd_g.header['EPOCH'] = (np.float32(
            time2jd(t3.utcnow())),  'equinox of pointing RA and Dec')
        ofd_g.header['EXPTIME'] = (np.float32(
            self.information['exptime']),   'exposure time (s)')

        ofd_g.header['CHECKSUM'] = (0,  '0')
        ################################################
        # ######### finish header for 0 layer ####################

        # new image HDU

        if HeaderTest == 'yes':
            hdu_g = fits.ImageHDU()
        else:
            hdu_g = fits.ImageHDU(data=np.uint16(self.image_g))
        #
        # ##  MCI  header for image layer ###
        # need review thie header
        hdu_g.header['XTENSION'] = ('IMAGE',  'extension type')
        hdu_g.header['BITPIX'] = (np.int16(16),  'array data type')
        hdu_g.header['NAXIS'] = (np.int16(2),  'number of array dimensions')

        hdu_g.header['NAXIS1'] = (np.int32(4936),  '')
        hdu_g.header['NAXIS2'] = (np.int32(23984),  '')

        hdu_g.header['PCOUNT'] = (np.int16(0),  'number of parameters')
        hdu_g.header['GCOUNT'] = (np.int16(1),  'number of groups')

        hdu_g.header['BSCALE'] = (int(1), '')
        hdu_g.header['BZERO'] = (int(32768), '')

        hdu_g.header['EXTNAME'] = ('SCI',  '')
        hdu_g.header['EXTVER'] = (np.int16(1), '')

        hdu_g.header['BUNIT'] = ('ADU', 'physical unit of array values')

        # ##### file information  ###############

        temp = t3.utcnow()
        data_time = temp.strftime("%Y-%m-%dT%H:%M:%S.%f")
        hdu_g.header['DATE'] = (
            data_time[:21], 'written date (yyyy-mm-ddThh:mm:ss.s)')
        hdu_g.header['FILENAME'] = (filename_g[:68], '')
        hdu_g.header['FILETYPE'] = (self.source[:12],   'observation type')
        hdu_g.header['RADECSYS'] = (
            'ICRS',   'coordinate system of the object')
        hdu_g.header['EQUINOX'] = (float(2000.1),   '')
        hdu_g.header['FITSSWV'] = (
            'csst_mci_sim_3.0.0',   'FITS creating software version')

        ########################################

        # ### instrument status and object information ######
        hdu_g.header['TELESCOP'] = ('CSST',   'telescope name')
        hdu_g.header['INSTRUME'] = ('MCI',   'instrument name')
        hdu_g.header['CHANNEL'] = ('C1',   'channel number')
        hdu_g.header['FILTERNO'] = (filternum,   'filter number')
        hdu_g.header['DIFFUSER'] = (
            bool(True),  'insert diffuser status for flat calibration')
        hdu_g.header['FLAMP'] = (np.int16(0),   'status of flat lamp')
        hdu_g.header['MCISTAT'] = (
            np.int16(0),   'MCI components status parameter')

        hdu_g.header['DATE-OBS'] = (data_time[:21],
                                    'observation date (yyyy-mm-ddThh:mm:ss.s)')
        hdu_g.header['OBJECT'] = ('MCI_obj',   'object name')

        hdu_g.header['TARGET'] = (
            self.information['target'][:15],     'target name (hhmmss.s+ddmmss)')
        #######################################################################
        hdu_g.header['OBSID'] = (str(obsid),   'observation ID')
        hdu_g.header['RA_OBJ'] = (
            float(self.information['ra_obj']),   'object RA (deg)')
        hdu_g.header['DEC_OBJ'] = (
            float(self.information['dec_obj']),   'object Dec (deg)')

        # ####  detector and  Filter information  #####
        hdu_g.header['FILTER'] = (self.filter_g[:6], 'filter band')
        # ####  Detector information ####
        hdu_g.header['DETSN'] = (
            'E2V-CCD-290-0000000', 'detector serial number')
        hdu_g.header['DETNAME'] = ('uv', 'detector name')
        # need review here

        hdu_g.header['DETTEMP0'] = (np.float32(
            0), 'detector temperature at EXPSTART (K)')
        hdu_g.header['DETTEMP1'] = (np.float32(
            0), 'detector temperature at EXPEND (K)')
        hdu_g.header['DETTEMP2'] = (np.float32(
            0), 'detector temperature at READT1 (K)')

        hdu_g.header['DETSIZE'] = (str(
            self.information['ysize'])+'*'+str(self.information['xsize']), 'detector size')
        hdu_g.header['DATASECT'] = (
            str(self.image_g.shape[0])+'*'+str(self.image_g.shape[1]), 'data section')

        hdu_g.header['PIXSCAL1'] = (
            float(0.05), 'pixel scale for axis 1 (arcsec/pixel)')
        hdu_g.header['PIXSCAL2'] = (
            float(0.05), 'pixel scale for axis 2 (arcsec/pixel)')

        hdu_g.header['PIXSIZE1'] = (int(10), 'pixel size for axis 1 (micron)')
        hdu_g.header['PIXSIZE2'] = (int(10), 'pixel size for axis 2 (micron)')

        hdu_g.header['NCHANNEL'] = (np.int16(16), 'number of readout channels')

        hdu_g.header['PSCAN1'] = (
            np.int32(27), 'horizontal prescan width, per readout channel')
        hdu_g.header['PSCAN2'] = (
            np.int32(0), 'vertical prescan width, per readout channel')

        hdu_g.header['OSCAN1'] = (
            np.int32(320), 'horizontal overscan width, per readout channel')
        hdu_g.header['OSCAN2'] = (
            np.int32(320), 'vertical overscan width, per readout channel')

        hdu_g.header['BIN_X'] = (np.int16(1), 'bin number in X')
        hdu_g.header['BIN_Y'] = (np.int16(1), 'bin number in Y')

        # ###   World coordinate system information  #####

        hdu_g.header['WCSAXES'] = (np.int16(2),    'number of WCS axes')
        hdu_g.header['CRPIX1'] = (
            round(float(self.information['CRPIX1']), 1), 'x-coordinate of reference pixel')
        hdu_g.header['CRPIX2'] = (
            round(float(self.information['CRPIX2']), 1), 'y-coordinate of reference pixel')

        hdu_g.header['CRVAL1'] = (
            float(self.information['CRVAL1']), 'value of reference pixel on axis 1')
        hdu_g.header['CRVAL2'] = (
            float(self.information['CRVAL2']), 'value of reference pixel on axis 2')

        hdu_g.header['CTYPE1'] = ('RA---TAN', 'type of RA WCS projection')
        hdu_g.header['CTYPE2'] = ('DEC--TAN', 'type of Dec WCS projection')

        hdu_g.header['CD1_1'] = (
            float(self.information['CD1_1']), 'transformation matrix element (deg/pix)')
        hdu_g.header['CD1_2'] = (
            float(self.information['CD1_2']), 'transformation matrix element (deg/pix)')
        hdu_g.header['CD2_1'] = (
            float(self.information['CD2_1']), 'transformation matrix element (deg/pix)')
        hdu_g.header['CD2_2'] = (
            float(self.information['CD2_2']), 'transformation matrix element (deg/pix)')

        # #####  Readout information  #######
        hdu_g.header['GAINLVL'] = (np.float32(
            1.5),         'gain level (e-/ADU)')
        hdu_g.header['GAIN01'] = (np.float32(
            self.information['g_gain1']), 'CCD gain [channel 1] (e-/ADU)')
        hdu_g.header['GAIN02'] = (np.float32(
            self.information['g_gain2']), 'CCD gain [channel 2] (e-/ADU)')
        hdu_g.header['GAIN03'] = (np.float32(
            self.information['g_gain3']), 'CCD gain [channel 3] (e-/ADU)')
        hdu_g.header['GAIN04'] = (np.float32(
            self.information['g_gain4']), 'CCD gain [channel 4] (e-/ADU)')
        hdu_g.header['GAIN05'] = (np.float32(
            self.information['g_gain5']), 'CCD gain [channel 5] (e-/ADU)')
        hdu_g.header['GAIN06'] = (np.float32(
            self.information['g_gain6']), 'CCD gain [channel 6] (e-/ADU)')
        hdu_g.header['GAIN07'] = (np.float32(
            self.information['g_gain7']), 'CCD gain [channel 7] (e-/ADU)')
        hdu_g.header['GAIN08'] = (np.float32(
            self.information['g_gain8']), 'CCD gain [channel 8] (e-/ADU)')
        hdu_g.header['GAIN09'] = (np.float32(
            self.information['g_gain9']), 'CCD gain [channel 9] (e-/ADU)')
        hdu_g.header['GAIN10'] = (np.float32(
            self.information['g_gain10']), 'CCD gain [channel 10] (e-/ADU)')
        hdu_g.header['GAIN11'] = (np.float32(
            self.information['g_gain11']), 'CCD gain [channel 11] (e-/ADU)')
        hdu_g.header['GAIN12'] = (np.float32(
            self.information['g_gain12']), 'CCD gain [channel 12] (e-/ADU)')
        hdu_g.header['GAIN13'] = (np.float32(
            self.information['g_gain13']), 'CCD gain [channel 13] (e-/ADU)')
        hdu_g.header['GAIN14'] = (np.float32(
            self.information['g_gain14']), 'CCD gain [channel 14] (e-/ADU)')
        hdu_g.header['GAIN15'] = (np.float32(
            self.information['g_gain15']), 'CCD gain [channel 15] (e-/ADU)')
        hdu_g.header['GAIN16'] = (np.float32(
            self.information['g_gain16']), 'CCD gain [channel 16] (e-/ADU)')
        #######
        hdu_g.header['RON01'] = (np.float32(
            self.information['g_rdnois1']), 'readout noise [channel 01] (e-)')
        hdu_g.header['RON02'] = (np.float32(
            self.information['g_rdnois2']), 'readout noise [channel 02] (e-)')
        hdu_g.header['RON03'] = (np.float32(
            self.information['g_rdnois3']), 'readout noise [channel 03] (e-)')
        hdu_g.header['RON04'] = (np.float32(
            self.information['g_rdnois4']), 'readout noise [channel 04] (e-)')
        hdu_g.header['RON05'] = (np.float32(
            self.information['g_rdnois5']), 'readout noise [channel 05] (e-)')
        hdu_g.header['RON06'] = (np.float32(
            self.information['g_rdnois6']), 'readout noise [channel 06] (e-)')
        hdu_g.header['RON07'] = (np.float32(
            self.information['g_rdnois7']), 'readout noise [channel 07] (e-)')
        hdu_g.header['RON08'] = (np.float32(
            self.information['g_rdnois8']), 'readout noise [channel 08] (e-)')
        hdu_g.header['RON09'] = (np.float32(
            self.information['g_rdnois9']), 'readout noise [channel 09] (e-)')
        hdu_g.header['RON10'] = (np.float32(
            self.information['g_rdnois10']), 'readout noise [channel 10] (e-)')
        hdu_g.header['RON11'] = (np.float32(
            self.information['g_rdnois11']), 'readout noise [channel 11] (e-)')
        hdu_g.header['RON12'] = (np.float32(
            self.information['g_rdnois12']), 'readout noise [channel 12] (e-)')
        hdu_g.header['RON13'] = (np.float32(
            self.information['g_rdnois13']), 'readout noise [channel 13] (e-)')
        hdu_g.header['RON14'] = (np.float32(
            self.information['g_rdnois14']), 'readout noise [channel 14] (e-)')
        hdu_g.header['RON15'] = (np.float32(
            self.information['g_rdnois15']), 'readout noise [channel 15] (e-)')
        hdu_g.header['RON16'] = (np.float32(
            self.information['g_rdnois16']), 'readout noise [channel 16] (e-)')

        #######
        hdu_g.header['DETBIA01'] = (np.float32(
            self.information['g_detbia1']), 'amplifier bias grey value [channel 1] (ADU)')
        hdu_g.header['DETBIA02'] = (np.float32(
            self.information['g_detbia2']), 'amplifier bias grey value [channel 2] (ADU)')
        hdu_g.header['DETBIA03'] = (np.float32(
            self.information['g_detbia3']), 'amplifier bias grey value [channel 3] (ADU)')
        hdu_g.header['DETBIA04'] = (np.float32(
            self.information['g_detbia4']), 'amplifier bias grey value [channel 4] (ADU)')
        hdu_g.header['DETBIA05'] = (np.float32(
            self.information['g_detbia5']), 'amplifier bias grey value [channel 5] (ADU)')
        hdu_g.header['DETBIA06'] = (np.float32(
            self.information['g_detbia6']), 'amplifier bias grey value [channel 6] (ADU)')
        hdu_g.header['DETBIA07'] = (np.float32(
            self.information['g_detbia7']), 'amplifier bias grey value [channel 7] (ADU)')
        hdu_g.header['DETBIA08'] = (np.float32(
            self.information['g_detbia8']), 'amplifier bias grey value [channel 8] (ADU)')
        hdu_g.header['DETBIA09'] = (np.float32(
            self.information['g_detbia9']), 'amplifier bias grey value [channel 9] (ADU)')
        hdu_g.header['DETBIA10'] = (np.float32(
            self.information['g_detbia10']), 'amplifier bias grey value [channel 10] (ADU)')
        hdu_g.header['DETBIA11'] = (np.float32(
            self.information['g_detbia11']), 'amplifier bias grey value [channel 11] (ADU)')
        hdu_g.header['DETBIA12'] = (np.float32(
            self.information['g_detbia12']), 'amplifier bias grey value [channel 12] (ADU)')
        hdu_g.header['DETBIA13'] = (np.float32(
            self.information['g_detbia13']), 'amplifier bias grey value [channel 13] (ADU)')
        hdu_g.header['DETBIA14'] = (np.float32(
            self.information['g_detbia14']), 'amplifier bias grey value [channel 14] (ADU)')
        hdu_g.header['DETBIA15'] = (np.float32(
            self.information['g_detbia15']), 'amplifier bias grey value [channel 15] (ADU)')
        hdu_g.header['DETBIA16'] = (np.float32(
            self.information['g_detbia16']), 'amplifier bias grey value [channel 16] (ADU)')
        hdu_g.header['ROSPEED'] = (np.float32(100), 'readout speed (MHz)')

        ######################################
        # ### exposure and shutter information  #####
        hdu_g.header['EXPSTART'] = (np.float64(
            time2mjd(self.dt)),   'exposure start time (MJD)')
        hdu_g.header['EXPEND'] = (float(time2mjd(t2)),
                                  'exposure end time (MJD)')
        hdu_g.header['EXPTIME'] = (np.float32(
            self.information['exptime']),   'exposure time (s)')
        hdu_g.header['DARKTIME'] = (np.float32(
            self.information['exptime']),   'dark current time (s)')
        hdu_g.header['SHTSTAT'] = (bool(True), 'shutter status')

        # ##### satellite and its allitide information  ##############
        hdu_g.header['REFFRAME'] = ('CSSTGSC-1.0',   'guiding catalog version')
        hdu_g.header['SATESWV'] = (
            'softwave-1.0',   'satellite software version')
        hdu_g.header['CABSTART'] = (np.float64(
            time2jd(self.dt)),   'first cabin time after exposure start (MJD)')
        hdu_g.header['SUNANGL0'] = (np.float32(
            0.0),   'angle between the Sun and opt axis at CABSTART')
        hdu_g.header['MOONANG0'] = (np.float32(
            0.0),   'angle between the Moon and opt axis at CABSTART')
        hdu_g.header['TEL_ALT0'] = (np.float64(
            0.0),   'angle between opt axis and Elimb at CABSTART')
        hdu_g.header['POS_ANG0'] = (np.float64(
            0.0),   'angle between y axis and North Pole at CABSTART')
        hdu_g.header['POSI0_X'] = (np.float64(
            self.information['pos_x']),   'orbital position in X at CABSTART (km)')
        hdu_g.header['POSI0_Y'] = (np.float64(
            self.information['pos_y']),   'orbital position in Y at CABSTART (km)')
        hdu_g.header['POSI0_Z'] = (np.float64(
            self.information['pos_z']),   'orbital position in Z at CABSTART (km)')
        hdu_g.header['VELO0_X'] = (np.float64(
            self.information['velocity_x']),   'orbital velocity in X at CABSTART (km/s)')
        hdu_g.header['VELO0_Y'] = (np.float64(
            self.information['velocity_y']),   'orbital velocity in Y at CABSTART (km/s)')
        hdu_g.header['VELO0_Z'] = (np.float64(
            self.information['velocity_z']),   'orbital velocity in Z at CABSTART (km/s)')
        hdu_g.header['Euler0_1'] = (np.float64(
            0.0),   'Euler angle 1 at CABSTART (deg)')
        hdu_g.header['Euler0_2'] = (np.float64(
            0.0),   'Euler angle 2 at CABSTART (deg)')
        hdu_g.header['Euler0_3'] = (np.float64(
            0.0),   'Euler angle 3 at CABSTART (deg)')
        hdu_g.header['RA_PNT0'] = (
            float(self.information['ra_pnt0']),     'pointing RA at CABSTART (deg)')
        hdu_g.header['DEC_PNT0'] = (
            float(self.information['dec_pnt0']),     'pointing Dec at CABSTART (deg)')
        hdu_g.header['CABEND'] = (float(time2jd(t2)),
                                  'first cabin time after exposure end (MJD)')
        hdu_g.header['SUNANGL1'] = (np.float32(
            0.0),   'angle between the Sun and opt axis at CABEND')
        hdu_g.header['MOONANG1'] = (np.float32(
            0.0),   'angle between the Moon and opt axis at CABEND')
        hdu_g.header['TEL_ALT1'] = (np.float64(
            0.0),   'angle between opt axis and Elimb at CABEND')
        hdu_g.header['POS_ANG1'] = (
            float(0.0),   'angle between y axis and North Pole at CABEND')
        hdu_g.header['POSI1_X'] = (
            float(p1x),   'orbital position in X at CABEND (km)')
        hdu_g.header['POSI1_Y'] = (
            float(p1y),   'orbital position in Y at CABEND (km)')
        hdu_g.header['POSI1_Z'] = (
            float(p1z),   'orbital position in Z at CABEND (km)')
        hdu_g.header['VELO1_X'] = (
            float(p1vx),   'orbital velocity in X at CABEND (km/s)')
        hdu_g.header['VELO1_Y'] = (
            float(p1vy),   'orbital velocity in Y at CABEND (km/s)')
        hdu_g.header['VELO1_Z'] = (
            float(p1vz),   'orbital velocity in Z at CABEND (km/s)')
        hdu_g.header['Euler1_1'] = (np.float64(
            0.0),   'Euler angle 1 at CABEND (deg)')
        hdu_g.header['Euler1_2'] = (np.float64(
            0.0),   'Euler angle 2 at CABEND (deg)')
        hdu_g.header['Euler1_3'] = (np.float64(
            0.0),   'Euler angle 3 at CABEND (deg)')
        hdu_g.header['RA_PNT1'] = (
            float(hdu_g.header['RA_PNT0']),    'pointing RA at CABEND (deg)')
        hdu_g.header['DEC_PNT1'] = (
            float(hdu_g.header['DEC_PNT0']),    'pointing Dec at CABEND (deg)')
        hdu_g.header['EPOCH'] = (np.float32(
            time2jd(t3.utcnow())),  'equinox of pointing RA and Dec')
        hdu_g.header['CHECKSUM'] = (0,  '0')
        ##############################################################

        # finish MCI header for image layer in G channel
        # 333

        hdulist_g = fits.HDUList([ofd_g, hdu_g])
        ####
        ofd_g.header.add_comment(
            '========================================================================', after='FITSSWV')
        ofd_g.header.add_comment('OBJECT INFORMATION', after='FITSSWV')
        ofd_g.header.add_comment(
            '========================================================================', after='FITSSWV')

        ofd_g.header.add_comment(
            '========================================================================', after='DEC_OBJ')
        ofd_g.header.add_comment('TELESCOPE INFORMATION', after='DEC_OBJ')
        ofd_g.header.add_comment(
            '========================================================================', after='DEC_OBJ')

        ofd_g.header.add_comment(
            '========================================================================', after='EXPTIME')
        ofd_g.header.add_comment('VERIFICATION INFORMATION', after='EXPTIME')
        ofd_g.header.add_comment(
            '========================================================================', after='EXPTIME')

        #######################################################################
        hdu_g.header.add_comment(
            '========================================================================', after='BUNIT')
        hdu_g.header.add_comment('FILE INFORMATION', after='BUNIT')
        hdu_g.header.add_comment(
            '========================================================================', after='BUNIT')

        hdu_g.header.add_comment(
            '========================================================================', after='FITSSWV')
        hdu_g.header.add_comment(
            'INSTRUMENT STATUS AND OBJECT INFORMATION', after='FITSSWV')
        hdu_g.header.add_comment(
            '========================================================================', after='FITSSWV')

        hdu_g.header.add_comment(
            '========================================================================', after='DEC_OBJ')
        hdu_g.header.add_comment(
            'DETECTOR AND FILTER INFORMATION', after='DEC_OBJ')
        hdu_g.header.add_comment(
            '========================================================================', after='DEC_OBJ')

        hdu_g.header.add_comment(
            '========================================================================', after='BIN_Y')
        hdu_g.header.add_comment(
            'WORLD COORDINATE SYSTEM INFORMATION', after='BIN_Y')
        hdu_g.header.add_comment(
            '========================================================================', after='BIN_Y')

        hdu_g.header.add_comment(
            '========================================================================', after='CD2_2')
        hdu_g.header.add_comment('READOUT INFORMATION', after='CD2_2')
        hdu_g.header.add_comment(
            '========================================================================', after='CD2_2')

        hdu_g.header.add_comment(
            '========================================================================', after='ROSPEED')
        hdu_g.header.add_comment(
            'EXPOSURE AND SHUTTER INFORMATION', after='ROSPEED')
        hdu_g.header.add_comment(
            '========================================================================', after='ROSPEED')

        hdu_g.header.add_comment(
            '========================================================================', after='SHTSTAT')
        hdu_g.header.add_comment(
            'SATELLITE AND ITS ATTITUDE INFORMATION', after='SHTSTAT')
        hdu_g.header.add_comment(
            '========================================================================', after='SHTSTAT')

        hdu_g.header.add_comment(
            '========================================================================', after='EPOCH')
        hdu_g.header.add_comment('VERIFICATION INFORMATION', after='EPOCH')
        hdu_g.header.add_comment(
            '========================================================================', after='EPOCH')

        #####################################

        if self.source == 'DARK' or self.source == 'FLAT' or self.source == 'BIAS':
            file_g = self.result_path+'/cali_Data/'+filename_g+'.fits'
        else:
            file_g = self.result_path+'/sky_Data/'+filename_g + '.fits'

        hdulist_g.writeto(file_g, checksum=True)

        ###########################################################################
        # r band

        # create a new FITS file, using HDUList instance
        ofd_r = fits.PrimaryHDU()

        if self.filterP[self.filter_r]['number'] > 9:
            filternum = str(self.filterP[self.filter_r]['number'])
        else:
            filternum = '0'+str(self.filterP[self.filter_r]['number'])

        filename_r = 'CSST_MCI_C2_'+self.source+'_'+exp_starttime+'_'+exp_endtime+'_'+str(obsid)+'_' + \
            filternum+'_L0_V01'

        #######################################################################
        #######################################################################
        # create a new FITS file, using HDUList instance
        ofd_r = fits.PrimaryHDU()
        # add primary header

        ##########
        ofd_r.header['SIMPLE'] = (True,   'conforms to FITS standard')
        ofd_r.header['BITPIX'] = (0,   'array data type')
        ofd_r.header['NAXIS'] = (0,   'number of array dimensions')
        ofd_r.header['NEXTEND'] = (np.int16(1),   'number of array dimensions')

        temp = t3.utcnow()
        data_time = temp.strftime("%Y-%m-%dT%H:%M:%S.%f")
        ofd_r.header['DATE'] = (
            data_time[:21], 'written date (yyyy-mm-ddThh:mm:ss.s)')
        ofd_r.header['FILENAME'] = (filename_r[:68], '')
        ofd_r.header['FILETYPE'] = (self.source[:12],   'observation type')
        ofd_r.header['TELESCOP'] = ('CSST',   'telescope name')
        ofd_r.header['INSTRUME'] = ('MCI',   'instrument name')
        ofd_r.header['RADECSYS'] = (
            'ICRS',   'coordinate system of the object')
        ofd_r.header['EQUINOX'] = (float(2000.0),   '')
        ofd_r.header['FITSSWV'] = (
            'csst_mci_sim_3.0.0',   'FITS creating software version')
        # ########   Object information  #########################################
        ofd_r.header['OBJECT'] = (
            self.information['name_obj'][:30],   'object name')
        ofd_r.header['TARGET'] = (
            self.information['target'][:15],     'target name (hhmmss.s+ddmmss)')
        #######################################################################
        ofd_r.header['OBSID'] = (str(obsid),   'observation ID')
        ofd_r.header['RA_OBJ'] = (
            float(self.information['ra_obj']),   'object RA (deg)')
        ofd_r.header['DEC_OBJ'] = (
            float(self.information['dec_obj']),   'object Dec (deg)')

        # #######  Telescope information ###############

        ofd_r.header['REFFRAME'] = ('CSSTGSC-1.0',   'guiding catalog version')
        ofd_r.header['DATE-OBS'] = (data_time[:21],
                                    'observation date (yyyy-mm-ddThh:mm:ss.s)')
        ofd_r.header['SATESWV'] = (
            'softwave-1.0',   'satellite software version')
        ofd_r.header['EXPSTART'] = (np.float64(
            time2mjd(self.dt)),   'exposure start time (MJD)')
        ofd_r.header['CABSTART'] = (np.float64(
            time2jd(self.dt)),   'first cabin time after exposure start (MJD)')
        ofd_r.header['SUNANGL0'] = (np.float32(
            0.0),   'angle between the Sun and opt axis at CABSTART')
        ofd_r.header['MOONANG0'] = (np.float32(
            0.0),   'angle between the Moon and opt axis at CABSTART')
        ofd_r.header['TEL_ALT0'] = (np.float64(
            0.0),   'angle between opt axis and Elimb at CABSTART')
        ofd_r.header['POS_ANG0'] = (np.float64(
            0.0),   'angle between y axis and North Pole at CABSTART')
        ofd_r.header['POSI0_X'] = (np.float64(
            self.information['pos_x']),   'orbital position in X at CABSTART (km)')
        ofd_r.header['POSI0_Y'] = (np.float64(
            self.information['pos_y']),   'orbital position in Y at CABSTART (km)')
        ofd_r.header['POSI0_Z'] = (np.float64(
            self.information['pos_z']),   'orbital position in Z at CABSTART (km)')
        ofd_r.header['VELO0_X'] = (np.float64(
            self.information['velocity_x']),   'orbital velocity in X at CABSTART (km/s)')
        ofd_r.header['VELO0_Y'] = (np.float64(
            self.information['velocity_y']),   'orbital velocity in Y at CABSTART (km/s)')
        ofd_r.header['VELO0_Z'] = (np.float64(
            self.information['velocity_z']),   'orbital velocity in Z at CABSTART (km/s)')
        ofd_r.header['Euler0_1'] = (np.float64(
            0.0),   'Euler angle 1 at CABSTART (deg)')
        ofd_r.header['Euler0_2'] = (np.float64(
            0.0),   'Euler angle 2 at CABSTART (deg)')
        ofd_r.header['Euler0_3'] = (np.float64(
            0.0),   'Euler angle 3 at CABSTART (deg)')

        ##############################################################
        #####
        ofd_r.header['RA_PNT0'] = (
            float(self.information['ra_pnt0']),     'pointing RA at CABSTART (deg)')
        ofd_r.header['DEC_PNT0'] = (
            float(self.information['dec_pnt0']),     'pointing Dec at CABSTART (deg)')
        ofd_r.header['EXPEND'] = (float(time2mjd(t2)),
                                  'exposure end time (MJD)')
        ofd_r.header['CABEND'] = (float(time2jd(t2)),
                                  'first cabin time after exposure end (MJD)')
        ofd_r.header['SUNANGL1'] = (np.float32(
            0.0),   'angle between the Sun and opt axis at CABEND')
        ofd_r.header['MOONANG1'] = (np.float32(
            0.0),   'angle between the Moon and opt axis at CABEND')
        ofd_r.header['TEL_ALT1'] = (np.float64(
            0.0),   'angle between opt axis and Elimb at CABEND')
        ofd_r.header['POS_ANG1'] = (
            float(0.0),   'angle between y axis and North Pole at CABEND')

        ##############################################################

        ofd_r.header['POSI1_X'] = (
            float(p1x),   'orbital position in X at CABEND (km)')
        ofd_r.header['POSI1_Y'] = (
            float(p1y),   'orbital position in Y at CABEND (km)')
        ofd_r.header['POSI1_Z'] = (
            float(p1z),   'orbital position in Z at CABEND (km)')

        ofd_r.header['VELO1_X'] = (
            float(p1vx),   'orbital velocity in X at CABEND (km/s)')
        ofd_r.header['VELO1_Y'] = (
            float(p1vy),   'orbital velocity in Y at CABEND (km/s)')
        ofd_r.header['VELO1_Z'] = (
            float(p1vz),   'orbital velocity in Z at CABEND (km/s)')

        ofd_r.header['Euler1_1'] = (np.float64(
            0.0),   'Euler angle 1 at CABEND (deg)')
        ofd_r.header['Euler1_2'] = (np.float64(
            0.0),   'Euler angle 2 at CABEND (deg)')
        ofd_r.header['Euler1_3'] = (np.float64(
            0.0),   'Euler angle 3 at CABEND (deg)')
        ofd_r.header['RA_PNT1'] = (
            float(ofd_r.header['RA_PNT0']),    'pointing RA at CABEND (deg)')
        ofd_r.header['DEC_PNT1'] = (
            float(ofd_r.header['DEC_PNT0']),    'pointing Dec at CABEND (deg)')
        ofd_r.header['EPOCH'] = (np.float32(
            time2jd(t3.utcnow())),  'equinox of pointing RA and Dec')
        ofd_r.header['EXPTIME'] = (np.float32(
            self.information['exptime']),   'exposure time (s)')

        ofd_r.header['CHECKSUM'] = (0,  '0')
        ################################################
        # ######### finish header for 0 layer ####################
        # ######### finish header for 0 layer ####################
        # finish header for 0 layer

        if HeaderTest == 'yes':
            hdu_r = fits.ImageHDU()
        else:
            hdu_r = fits.ImageHDU(data=np.uint16(self.image_r))
        # ##  MCI  header for image layer ###
        # need review thie header
        hdu_r.header['XTENSION'] = ('IMAGE',  'extension type')
        hdu_r.header['BITPIX'] = (np.int16(16),  'array data type')
        hdu_r.header['NAXIS'] = (np.int16(2),  'number of array dimensions')

        hdu_r.header['NAXIS1'] = (np.int32(4936),  '')
        hdu_r.header['NAXIS2'] = (np.int32(23984),  '')

        hdu_r.header['PCOUNT'] = (np.int16(0),  'number of parameters')
        hdu_r.header['GCOUNT'] = (np.int16(1),  'number of groups')

        hdu_r.header['BSCALE'] = (int(1), '')
        hdu_r.header['BZERO'] = (int(32768), '')

        hdu_r.header['EXTNAME'] = ('SCI',  '')
        hdu_r.header['EXTVER'] = (np.int16(1), '')

        hdu_r.header['BUNIT'] = ('ADU', 'physical unit of array values')

        # ##### file information  ###############

        temp = t3.utcnow()
        data_time = temp.strftime("%Y-%m-%dT%H:%M:%S.%f")
        hdu_r.header['DATE'] = (
            data_time[:21], 'written date (yyyy-mm-ddThh:mm:ss.s)')
        hdu_r.header['FILENAME'] = (filename_r[:68], '')
        hdu_r.header['FILETYPE'] = (self.source[:12],   'observation type')
        hdu_r.header['RADECSYS'] = (
            'ICRS',   'coordinate system of the object')
        hdu_r.header['EQUINOX'] = (float(2000.1),   '')
        hdu_r.header['FITSSWV'] = (
            'csst_mci_sim_3.0.0',   'FITS creating software version')

        ########################################

        # ### instrument status and object information ######
        hdu_r.header['TELESCOP'] = ('CSST',   'telescope name')
        hdu_r.header['INSTRUME'] = ('MCI',   'instrument name')
        hdu_r.header['CHANNEL'] = ('C2',   'channel number')
        hdu_r.header['FILTERNO'] = (filternum,   'filter number')
        hdu_r.header['DIFFUSER'] = (
            bool(True),  'insert diffuser status for flat calibration')
        hdu_r.header['FLAMP'] = (np.int16(0),   'status of flat lamp')
        hdu_r.header['MCISTAT'] = (
            np.int16(0),   'MCI components status parameter')

        hdu_r.header['DATE-OBS'] = (data_time[:21],
                                    'observation date (yyyy-mm-ddThh:mm:ss.s)')
        hdu_r.header['OBJECT'] = ('MCI_obj',   'object name')

        hdu_r.header['TARGET'] = (
            self.information['target'][:15],     'target name (hhmmss.s+ddmmss)')
        #######################################################################
        hdu_r.header['OBSID'] = (str(obsid),   'observation ID')
        hdu_r.header['RA_OBJ'] = (
            float(self.information['ra_obj']),   'object RA (deg)')
        hdu_r.header['DEC_OBJ'] = (
            float(self.information['dec_obj']),   'object Dec (deg)')

        # ####  detector and  Filter information  #####
        hdu_r.header['FILTER'] = (self.filter_r[:6], 'filter band')
        # ####  Detector information ####
        hdu_r.header['DETSN'] = (
            'E2V-CCD-290-0000000', 'detector serial number')
        hdu_r.header['DETNAME'] = ('blue-opt', 'detector name')
        # need review here

        hdu_r.header['DETTEMP0'] = (np.float32(
            0), 'detector temperature at EXPSTART (K)')
        hdu_r.header['DETTEMP1'] = (np.float32(
            0), 'detector temperature at EXPEND (K)')
        hdu_r.header['DETTEMP2'] = (np.float32(
            0), 'detector temperature at READT1 (K)')

        hdu_r.header['DETSIZE'] = (str(
            self.information['ysize'])+'*'+str(self.information['xsize']), 'detector size')
        hdu_r.header['DATASECT'] = (
            str(self.image_g.shape[0])+'*'+str(self.image_g.shape[1]), 'data section')

        hdu_r.header['PIXSCAL1'] = (
            float(0.05), 'pixel scale for axis 1 (arcsec/pixel)')
        hdu_r.header['PIXSCAL2'] = (
            float(0.05), 'pixel scale for axis 2 (arcsec/pixel)')

        hdu_r.header['PIXSIZE1'] = (int(10), 'pixel size for axis 1 (micron)')
        hdu_r.header['PIXSIZE2'] = (int(10), 'pixel size for axis 2 (micron)')

        hdu_r.header['NCHANNEL'] = (np.int16(16), 'number of readout channels')

        hdu_r.header['PSCAN1'] = (
            np.int32(27), 'horizontal prescan width, per readout channel')
        hdu_r.header['PSCAN2'] = (
            np.int32(0), 'vertical prescan width, per readout channel')

        hdu_r.header['OSCAN1'] = (
            np.int32(320), 'horizontal overscan width, per readout channel')
        hdu_r.header['OSCAN2'] = (
            np.int32(320), 'vertical overscan width, per readout channel')

        hdu_r.header['BIN_X'] = (np.int16(1), 'bin number in X')
        hdu_r.header['BIN_Y'] = (np.int16(1), 'bin number in Y')

        # ###   World coordinate system information  #####

        hdu_r.header['WCSAXES'] = (np.int16(2),    'number of WCS axes')
        hdu_r.header['CRPIX1'] = (
            round(float(self.information['CRPIX1']), 1), 'x-coordinate of reference pixel')
        hdu_r.header['CRPIX2'] = (
            round(float(self.information['CRPIX2']), 1), 'y-coordinate of reference pixel')

        hdu_r.header['CRVAL1'] = (
            float(self.information['CRVAL1']), 'value of reference pixel on axis 1')
        hdu_r.header['CRVAL2'] = (
            float(self.information['CRVAL2']), 'value of reference pixel on axis 2')

        hdu_r.header['CTYPE1'] = ('RA---TAN', 'type of RA WCS projection')
        hdu_r.header['CTYPE2'] = ('DEC--TAN', 'type of Dec WCS projection')

        hdu_r.header['CD1_1'] = (
            float(self.information['CD1_1']), 'transformation matrix element (deg/pix)')
        hdu_r.header['CD1_2'] = (
            float(self.information['CD1_2']), 'transformation matrix element (deg/pix)')
        hdu_r.header['CD2_1'] = (
            float(self.information['CD2_1']), 'transformation matrix element (deg/pix)')
        hdu_r.header['CD2_2'] = (
            float(self.information['CD2_2']), 'transformation matrix element (deg/pix)')

        # #####  Readout information  #######
        hdu_r.header['GAINLVL'] = (np.float32(
            1.5),         'gain level (e-/ADU)')
        hdu_r.header['GAIN01'] = (np.float32(
            self.information['r_gain1']), 'CCD gain [channel 1] (e-/ADU)')
        hdu_r.header['GAIN02'] = (np.float32(
            self.information['r_gain2']), 'CCD gain [channel 2] (e-/ADU)')
        hdu_r.header['GAIN03'] = (np.float32(
            self.information['r_gain3']), 'CCD gain [channel 3] (e-/ADU)')
        hdu_r.header['GAIN04'] = (np.float32(
            self.information['r_gain4']), 'CCD gain [channel 4] (e-/ADU)')
        hdu_r.header['GAIN05'] = (np.float32(
            self.information['r_gain5']), 'CCD gain [channel 5] (e-/ADU)')
        hdu_r.header['GAIN06'] = (np.float32(
            self.information['r_gain6']), 'CCD gain [channel 6] (e-/ADU)')
        hdu_r.header['GAIN07'] = (np.float32(
            self.information['r_gain7']), 'CCD gain [channel 7] (e-/ADU)')
        hdu_r.header['GAIN08'] = (np.float32(
            self.information['r_gain8']), 'CCD gain [channel 8] (e-/ADU)')
        hdu_r.header['GAIN09'] = (np.float32(
            self.information['r_gain9']), 'CCD gain [channel 9] (e-/ADU)')
        hdu_r.header['GAIN10'] = (np.float32(
            self.information['r_gain10']), 'CCD gain [channel 10] (e-/ADU)')
        hdu_r.header['GAIN11'] = (np.float32(
            self.information['r_gain11']), 'CCD gain [channel 11] (e-/ADU)')
        hdu_r.header['GAIN12'] = (np.float32(
            self.information['r_gain12']), 'CCD gain [channel 12] (e-/ADU)')
        hdu_r.header['GAIN13'] = (np.float32(
            self.information['r_gain13']), 'CCD gain [channel 13] (e-/ADU)')
        hdu_r.header['GAIN14'] = (np.float32(
            self.information['r_gain14']), 'CCD gain [channel 14] (e-/ADU)')
        hdu_r.header['GAIN15'] = (np.float32(
            self.information['r_gain15']), 'CCD gain [channel 15] (e-/ADU)')
        hdu_r.header['GAIN16'] = (np.float32(
            self.information['r_gain16']), 'CCD gain [channel 16] (e-/ADU)')
        #######
        hdu_r.header['RON01'] = (np.float32(
            self.information['r_rdnois1']), 'readout noise [channel 01] (e-)')
        hdu_r.header['RON02'] = (np.float32(
            self.information['r_rdnois2']), 'readout noise [channel 02] (e-)')
        hdu_r.header['RON03'] = (np.float32(
            self.information['r_rdnois3']), 'readout noise [channel 03] (e-)')
        hdu_r.header['RON04'] = (np.float32(
            self.information['r_rdnois4']), 'readout noise [channel 04] (e-)')
        hdu_r.header['RON05'] = (np.float32(
            self.information['r_rdnois5']), 'readout noise [channel 05] (e-)')
        hdu_r.header['RON06'] = (np.float32(
            self.information['r_rdnois6']), 'readout noise [channel 06] (e-)')
        hdu_r.header['RON07'] = (np.float32(
            self.information['r_rdnois7']), 'readout noise [channel 07] (e-)')
        hdu_r.header['RON08'] = (np.float32(
            self.information['r_rdnois8']), 'readout noise [channel 08] (e-)')
        hdu_r.header['RON09'] = (np.float32(
            self.information['r_rdnois9']), 'readout noise [channel 09] (e-)')
        hdu_r.header['RON10'] = (np.float32(
            self.information['r_rdnois10']), 'readout noise [channel 10] (e-)')
        hdu_r.header['RON11'] = (np.float32(
            self.information['r_rdnois11']), 'readout noise [channel 11] (e-)')
        hdu_r.header['RON12'] = (np.float32(
            self.information['r_rdnois12']), 'readout noise [channel 12] (e-)')
        hdu_r.header['RON13'] = (np.float32(
            self.information['r_rdnois13']), 'readout noise [channel 13] (e-)')
        hdu_r.header['RON14'] = (np.float32(
            self.information['r_rdnois14']), 'readout noise [channel 14] (e-)')
        hdu_r.header['RON15'] = (np.float32(
            self.information['r_rdnois15']), 'readout noise [channel 15] (e-)')
        hdu_r.header['RON16'] = (np.float32(
            self.information['r_rdnois16']), 'readout noise [channel 16] (e-)')

        #######
        hdu_r.header['DETBIA01'] = (np.float32(
            self.information['r_detbia1']), 'amplifier bias grey value [channel 1] (ADU)')
        hdu_r.header['DETBIA02'] = (np.float32(
            self.information['r_detbia2']), 'amplifier bias grey value [channel 2] (ADU)')
        hdu_r.header['DETBIA03'] = (np.float32(
            self.information['r_detbia3']), 'amplifier bias grey value [channel 3] (ADU)')
        hdu_r.header['DETBIA04'] = (np.float32(
            self.information['r_detbia4']), 'amplifier bias grey value [channel 4] (ADU)')
        hdu_r.header['DETBIA05'] = (np.float32(
            self.information['r_detbia5']), 'amplifier bias grey value [channel 5] (ADU)')
        hdu_r.header['DETBIA06'] = (np.float32(
            self.information['r_detbia6']), 'amplifier bias grey value [channel 6] (ADU)')
        hdu_r.header['DETBIA07'] = (np.float32(
            self.information['r_detbia7']), 'amplifier bias grey value [channel 7] (ADU)')
        hdu_r.header['DETBIA08'] = (np.float32(
            self.information['r_detbia8']), 'amplifier bias grey value [channel 8] (ADU)')
        hdu_r.header['DETBIA09'] = (np.float32(
            self.information['r_detbia9']), 'amplifier bias grey value [channel 9] (ADU)')
        hdu_r.header['DETBIA10'] = (np.float32(
            self.information['r_detbia10']), 'amplifier bias grey value [channel 10] (ADU)')
        hdu_r.header['DETBIA11'] = (np.float32(
            self.information['r_detbia11']), 'amplifier bias grey value [channel 11] (ADU)')
        hdu_r.header['DETBIA12'] = (np.float32(
            self.information['r_detbia12']), 'amplifier bias grey value [channel 12] (ADU)')
        hdu_r.header['DETBIA13'] = (np.float32(
            self.information['r_detbia13']), 'amplifier bias grey value [channel 13] (ADU)')
        hdu_r.header['DETBIA14'] = (np.float32(
            self.information['r_detbia14']), 'amplifier bias grey value [channel 14] (ADU)')
        hdu_r.header['DETBIA15'] = (np.float32(
            self.information['r_detbia15']), 'amplifier bias grey value [channel 15] (ADU)')
        hdu_r.header['DETBIA16'] = (np.float32(
            self.information['r_detbia16']), 'amplifier bias grey value [channel 16] (ADU)')
        hdu_r.header['ROSPEED'] = (np.float32(100), 'readout speed (MHz)')

        ######################################
        # ### exposure and shutter information  #####
        hdu_r.header['EXPSTART'] = (np.float64(
            time2mjd(self.dt)),   'exposure start time (MJD)')
        hdu_r.header['EXPEND'] = (float(time2mjd(t2)),
                                  'exposure end time (MJD)')
        hdu_r.header['EXPTIME'] = (np.float32(
            self.information['exptime']),   'exposure time (s)')
        hdu_r.header['DARKTIME'] = (np.float32(
            self.information['exptime']),   'dark current time (s)')
        hdu_r.header['SHTSTAT'] = (bool(True), 'shutter status')

        # ##### satellite and its allitide information  ##############
        hdu_r.header['REFFRAME'] = ('CSSTGSC-1.0',   'guiding catalog version')
        hdu_r.header['SATESWV'] = (
            'softwave-1.0',   'satellite software version')
        hdu_r.header['CABSTART'] = (np.float64(
            time2jd(self.dt)),   'first cabin time after exposure start (MJD)')
        hdu_r.header['SUNANGL0'] = (np.float32(
            0.0),   'angle between the Sun and opt axis at CABSTART')
        hdu_r.header['MOONANG0'] = (np.float32(
            0.0),   'angle between the Moon and opt axis at CABSTART')
        hdu_r.header['TEL_ALT0'] = (np.float64(
            0.0),   'angle between opt axis and Elimb at CABSTART')
        hdu_r.header['POS_ANG0'] = (np.float64(
            0.0),   'angle between y axis and North Pole at CABSTART')
        hdu_r.header['POSI0_X'] = (np.float64(
            self.information['pos_x']),   'orbital position in X at CABSTART (km)')
        hdu_r.header['POSI0_Y'] = (np.float64(
            self.information['pos_y']),   'orbital position in Y at CABSTART (km)')
        hdu_r.header['POSI0_Z'] = (np.float64(
            self.information['pos_z']),   'orbital position in Z at CABSTART (km)')
        hdu_r.header['VELO0_X'] = (np.float64(
            self.information['velocity_x']),   'orbital velocity in X at CABSTART (km/s)')
        hdu_r.header['VELO0_Y'] = (np.float64(
            self.information['velocity_y']),   'orbital velocity in Y at CABSTART (km/s)')
        hdu_r.header['VELO0_Z'] = (np.float64(
            self.information['velocity_z']),   'orbital velocity in Z at CABSTART (km/s)')
        hdu_r.header['Euler0_1'] = (np.float64(
            0.0),   'Euler angle 1 at CABSTART (deg)')
        hdu_r.header['Euler0_2'] = (np.float64(
            0.0),   'Euler angle 2 at CABSTART (deg)')
        hdu_r.header['Euler0_3'] = (np.float64(
            0.0),   'Euler angle 3 at CABSTART (deg)')
        hdu_r.header['RA_PNT0'] = (
            float(self.information['ra_pnt0']),     'pointing RA at CABSTART (deg)')
        hdu_r.header['DEC_PNT0'] = (
            float(self.information['dec_pnt0']),     'pointing Dec at CABSTART (deg)')
        hdu_r.header['CABEND'] = (float(time2jd(t2)),
                                  'first cabin time after exposure end (MJD)')
        hdu_r.header['SUNANGL1'] = (np.float32(
            0.0),   'angle between the Sun and opt axis at CABEND')
        hdu_r.header['MOONANG1'] = (np.float32(
            0.0),   'angle between the Moon and opt axis at CABEND')
        hdu_r.header['TEL_ALT1'] = (np.float64(
            0.0),   'angle between opt axis and Elimb at CABEND')
        hdu_r.header['POS_ANG1'] = (
            float(0.0),   'angle between y axis and North Pole at CABEND')
        hdu_r.header['POSI1_X'] = (
            float(p1x),   'orbital position in X at CABEND (km)')
        hdu_r.header['POSI1_Y'] = (
            float(p1y),   'orbital position in Y at CABEND (km)')
        hdu_r.header['POSI1_Z'] = (
            float(p1z),   'orbital position in Z at CABEND (km)')
        hdu_r.header['VELO1_X'] = (
            float(p1vx),   'orbital velocity in X at CABEND (km/s)')
        hdu_r.header['VELO1_Y'] = (
            float(p1vy),   'orbital velocity in Y at CABEND (km/s)')
        hdu_r.header['VELO1_Z'] = (
            float(p1vz),   'orbital velocity in Z at CABEND (km/s)')
        hdu_r.header['Euler1_1'] = (np.float64(
            0.0),   'Euler angle 1 at CABEND (deg)')
        hdu_r.header['Euler1_2'] = (np.float64(
            0.0),   'Euler angle 2 at CABEND (deg)')
        hdu_r.header['Euler1_3'] = (np.float64(
            0.0),   'Euler angle 3 at CABEND (deg)')
        hdu_r.header['RA_PNT1'] = (
            float(hdu_r.header['RA_PNT0']),    'pointing RA at CABEND (deg)')
        hdu_r.header['DEC_PNT1'] = (
            float(hdu_r.header['DEC_PNT0']),    'pointing Dec at CABEND (deg)')
        hdu_r.header['EPOCH'] = (np.float32(
            time2jd(t3.utcnow())),  'equinox of pointing RA and Dec')
        hdu_r.header['CHECKSUM'] = (0,  '0')
        ##############################################################
        # finish MCI header for image layer in G channel
        # 333

        hdulist_r = fits.HDUList([ofd_r, hdu_r])
        ####
        ofd_r.header.add_comment(
            '========================================================================', after='FITSSWV')
        ofd_r.header.add_comment('OBJECT INFORMATION', after='FITSSWV')
        ofd_r.header.add_comment(
            '========================================================================', after='FITSSWV')

        ofd_r.header.add_comment(
            '========================================================================', after='DEC_OBJ')
        ofd_r.header.add_comment('TELESCOPE INFORMATION', after='DEC_OBJ')
        ofd_r.header.add_comment(
            '========================================================================', after='DEC_OBJ')

        ofd_r.header.add_comment(
            '========================================================================', after='EXPTIME')
        ofd_r.header.add_comment('VERIFICATION INFORMATION', after='EXPTIME')
        ofd_r.header.add_comment(
            '========================================================================', after='EXPTIME')

        #######################################################################
        hdu_r.header.add_comment(
            '========================================================================', after='BUNIT')
        hdu_r.header.add_comment('FILE INFORMATION', after='BUNIT')
        hdu_r.header.add_comment(
            '========================================================================', after='BUNIT')

        hdu_r.header.add_comment(
            '========================================================================', after='FITSSWV')
        hdu_r.header.add_comment(
            'INSTRUMENT STATUS AND OBJECT INFORMATION', after='FITSSWV')
        hdu_r.header.add_comment(
            '========================================================================', after='FITSSWV')

        hdu_r.header.add_comment(
            '========================================================================', after='DEC_OBJ')
        hdu_r.header.add_comment(
            'DETECTOR AND FILTER INFORMATION', after='DEC_OBJ')
        hdu_r.header.add_comment(
            '========================================================================', after='DEC_OBJ')

        hdu_r.header.add_comment(
            '========================================================================', after='BIN_Y')
        hdu_r.header.add_comment(
            'WORLD COORDINATE SYSTEM INFORMATION', after='BIN_Y')
        hdu_r.header.add_comment(
            '========================================================================', after='BIN_Y')

        hdu_r.header.add_comment(
            '========================================================================', after='CD2_2')
        hdu_r.header.add_comment('READOUT INFORMATION', after='CD2_2')
        hdu_r.header.add_comment(
            '========================================================================', after='CD2_2')

        hdu_r.header.add_comment(
            '========================================================================', after='ROSPEED')
        hdu_r.header.add_comment(
            'EXPOSURE AND SHUTTER INFORMATION', after='ROSPEED')
        hdu_r.header.add_comment(
            '========================================================================', after='ROSPEED')

        hdu_r.header.add_comment(
            '========================================================================', after='SHTSTAT')
        hdu_r.header.add_comment(
            'SATELLITE AND ITS ATTITUDE INFORMATION', after='SHTSTAT')
        hdu_r.header.add_comment(
            '========================================================================', after='SHTSTAT')

        hdu_r.header.add_comment(
            '========================================================================', after='EPOCH')
        hdu_r.header.add_comment('VERIFICATION INFORMATION', after='EPOCH')
        hdu_r.header.add_comment(
            '========================================================================', after='EPOCH')

        #####################################
        # finish MCI header for image layer in R channel

        if self.source == 'DARK' or self.source == 'FLAT' or self.source == 'BIAS':
            file_r = self.result_path+'/cali_Data/'+filename_r+'.fits'
        else:
            file_r = self.result_path+'/sky_Data/'+filename_r + '.fits'

        hdulist_r.writeto(file_r, checksum=True)

        print('MCI_r.fits is created ')

        #################################################################################
        # i band

        # create a new FITS file, using HDUList instance
        ofd_i = fits.PrimaryHDU()

        if self.filterP[self.filter_i]['number'] > 9:
            filternum = str(self.filterP[self.filter_i]['number'])
        else:
            filternum = '0'+str(self.filterP[self.filter_i]['number'])

        filename_i = 'CSST_MCI_C3_'+self.source+'_'+exp_starttime+'_'+exp_endtime+'_'+str(obsid)+'_' + \
            filternum+'_L0_V01'

        # add primary header

        ofd_i.header['SIMPLE'] = (True,   'conforms to FITS standard')
        ofd_i.header['BITPIX'] = (0,   'array data type')
        ofd_i.header['NAXIS'] = (0,   'number of array dimensions')
        ofd_i.header['NEXTEND'] = (np.int16(1),   'number of array dimensions')
        ###############################
        temp = t3.utcnow()
        data_time = temp.strftime("%Y-%m-%dT%H:%M:%S.%f")
        ofd_i.header['DATE'] = (
            data_time[:21], 'written date (yyyy-mm-ddThh:mm:ss.s)')
        ofd_i.header['FILENAME'] = (filename_i[:68], '')
        ofd_i.header['FILETYPE'] = (self.source[:12],   'observation type')
        ofd_i.header['TELESCOP'] = ('CSST',   'telescope name')
        ofd_i.header['INSTRUME'] = ('MCI',   'instrument name')
        ofd_i.header['RADECSYS'] = (
            'ICRS',   'coordinate system of the object')
        ofd_i.header['EQUINOX'] = (float(2000.0),   '')
        ofd_i.header['FITSSWV'] = (
            'csst_mci_sim_3.0.0',   'FITS creating software version')
        ##############################################################################
        # ########   Object information  #########################################
        ofd_i.header['OBJECT'] = (
            self.information['name_obj'][:30],   'object name')
        ofd_i.header['TARGET'] = (
            self.information['target'][:15],     'target name (hhmmss.s+ddmmss)')
        #######################################################################
        ofd_i.header['OBSID'] = (str(obsid),   'observation ID')
        ofd_i.header['RA_OBJ'] = (
            float(self.information['ra_obj']),   'object RA (deg)')
        ofd_i.header['DEC_OBJ'] = (
            float(self.information['dec_obj']),   'object Dec (deg)')

        # #######  Telescope information ###############
        ofd_i.header['REFFRAME'] = ('CSSTGSC-1.0',   'guiding catalog version')
        ofd_i.header['DATE-OBS'] = (data_time[:21],
                                    'observation date (yyyy-mm-ddThh:mm:ss.s)')
        ofd_i.header['SATESWV'] = (
            'softwave-1.0',   'satellite software version')
        ofd_i.header['EXPSTART'] = (np.float64(
            time2mjd(self.dt)),   'exposure start time (MJD)')
        ofd_i.header['CABSTART'] = (np.float64(
            time2jd(self.dt)),   'first cabin time after exposure start (MJD)')
        ofd_i.header['SUNANGL0'] = (np.float32(
            0.0),   'angle between the Sun and opt axis at CABSTART')
        ofd_i.header['MOONANG0'] = (np.float32(
            0.0),   'angle between the Moon and opt axis at CABSTART')
        ofd_i.header['TEL_ALT0'] = (np.float64(
            0.0),   'angle between opt axis and Elimb at CABSTART')
        ofd_i.header['POS_ANG0'] = (np.float64(
            0.0),   'angle between y axis and North Pole at CABSTART')
        ofd_i.header['POSI0_X'] = (np.float64(
            self.information['pos_x']),   'orbital position in X at CABSTART (km)')
        ofd_i.header['POSI0_Y'] = (np.float64(
            self.information['pos_y']),   'orbital position in Y at CABSTART (km)')
        ofd_i.header['POSI0_Z'] = (np.float64(
            self.information['pos_z']),   'orbital position in Z at CABSTART (km)')
        ofd_i.header['VELO0_X'] = (np.float64(
            self.information['velocity_x']),   'orbital velocity in X at CABSTART (km/s)')
        ofd_i.header['VELO0_Y'] = (np.float64(
            self.information['velocity_y']),   'orbital velocity in Y at CABSTART (km/s)')
        ofd_i.header['VELO0_Z'] = (np.float64(
            self.information['velocity_z']),   'orbital velocity in Z at CABSTART (km/s)')
        ofd_i.header['Euler0_1'] = (np.float64(
            0.0),   'Euler angle 1 at CABSTART (deg)')
        ofd_i.header['Euler0_2'] = (np.float64(
            0.0),   'Euler angle 2 at CABSTART (deg)')
        ofd_i.header['Euler0_3'] = (np.float64(
            0.0),   'Euler angle 3 at CABSTART (deg)')

        ofd_i.header['RA_PNT0'] = (
            float(self.information['ra_pnt0']),     'pointing RA at CABSTART (deg)')
        ofd_i.header['DEC_PNT0'] = (
            float(self.information['dec_pnt0']),     'pointing Dec at CABSTART (deg)')
        ofd_i.header['EXPEND'] = (float(time2mjd(t2)),
                                  'exposure end time (MJD)')
        ofd_i.header['CABEND'] = (float(time2jd(t2)),
                                  'first cabin time after exposure end (MJD)')
        ofd_i.header['SUNANGL1'] = (np.float32(
            0.0),   'angle between the Sun and opt axis at CABEND')
        ofd_i.header['MOONANG1'] = (np.float32(
            0.0),   'angle between the Moon and opt axis at CABEND')
        ofd_i.header['TEL_ALT1'] = (np.float64(
            0.0),   'angle between opt axis and Elimb at CABEND')
        ofd_i.header['POS_ANG1'] = (
            float(0.0),   'angle between y axis and North Pole at CABEND')

        ##############################################################

        ofd_i.header['POSI1_X'] = (
            float(p1x),   'orbital position in X at CABEND (km)')
        ofd_i.header['POSI1_Y'] = (
            float(p1y),   'orbital position in Y at CABEND (km)')
        ofd_i.header['POSI1_Z'] = (
            float(p1z),   'orbital position in Z at CABEND (km)')

        ofd_i.header['VELO1_X'] = (
            float(p1vx),   'orbital velocity in X at CABEND (km/s)')
        ofd_i.header['VELO1_Y'] = (
            float(p1vy),   'orbital velocity in Y at CABEND (km/s)')
        ofd_i.header['VELO1_Z'] = (
            float(p1vz),   'orbital velocity in Z at CABEND (km/s)')

        ###################

        ofd_i.header['Euler1_1'] = (np.float64(
            0.0),   'Euler angle 1 at CABEND (deg)')
        ofd_i.header['Euler1_2'] = (np.float64(
            0.0),   'Euler angle 2 at CABEND (deg)')
        ofd_i.header['Euler1_3'] = (np.float64(
            0.0),   'Euler angle 3 at CABEND (deg)')
        ofd_i.header['RA_PNT1'] = (
            float(ofd_i.header['RA_PNT0']),    'pointing RA at CABEND (deg)')
        ofd_i.header['DEC_PNT1'] = (
            float(ofd_i.header['DEC_PNT0']),    'pointing Dec at CABEND (deg)')
        ofd_i.header['EPOCH'] = (np.float32(
            time2jd(t3.utcnow())),  'equinox of pointing RA and Dec')
        ofd_i.header['EXPTIME'] = (np.float32(
            self.information['exptime']),   'exposure time (s)')

        ofd_i.header['CHECKSUM'] = (0,  '0')
        ################################################
        # ######### finish header for 0 layer ####################

        # finish header for 0 layer

        # new image HDU
        if HeaderTest == 'yes':
            hdu_i = fits.ImageHDU()
        else:
            hdu_i = fits.ImageHDU(data=np.uint16(self.image_i))

        # need review thie header
        # ##  MCI  header for image layer ###
        # need review thie header
        hdu_i.header['XTENSION'] = ('IMAGE',  'extension type')
        hdu_i.header['BITPIX'] = (np.int16(16),  'array data type')
        hdu_i.header['NAXIS'] = (np.int16(2),  'number of array dimensions')

        hdu_i.header['NAXIS1'] = (np.int32(4936),  '')
        hdu_i.header['NAXIS2'] = (np.int32(23984),  '')

        hdu_i.header['PCOUNT'] = (np.int16(0),  'number of parameters')
        hdu_i.header['GCOUNT'] = (np.int16(1),  'number of groups')

        hdu_i.header['BSCALE'] = (int(1), '')
        hdu_i.header['BZERO'] = (int(32768), '')

        hdu_i.header['EXTNAME'] = ('SCI',  '')
        hdu_i.header['EXTVER'] = (np.int16(1), '')

        hdu_i.header['BUNIT'] = ('ADU', 'physical unit of array values')

        # ##### file information  ###############

        temp = t3.utcnow()
        data_time = temp.strftime("%Y-%m-%dT%H:%M:%S.%f")
        hdu_i.header['DATE'] = (
            data_time[:21], 'written date (yyyy-mm-ddThh:mm:ss.s)')
        hdu_i.header['FILENAME'] = (filename_i[:68], '')
        hdu_i.header['FILETYPE'] = (self.source[:12],   'observation type')
        hdu_i.header['RADECSYS'] = (
            'ICRS',   'coordinate system of the object')
        hdu_i.header['EQUINOX'] = (float(2000.1),   '')
        hdu_i.header['FITSSWV'] = (
            'csst_mci_sim_3.0.0',   'FITS creating software version')

        ########################################

        # ### instrument status and object information ######
        hdu_i.header['TELESCOP'] = ('CSST',   'telescope name')
        hdu_i.header['INSTRUME'] = ('MCI',   'instrument name')
        hdu_i.header['CHANNEL'] = ('C3',   'channel number')
        hdu_i.header['FILTERNO'] = (filternum,   'filter number')
        hdu_i.header['DIFFUSER'] = (
            bool(True),  'insert diffuser status for flat calibration')
        hdu_i.header['FLAMP'] = (np.int16(0),   'status of flat lamp')
        hdu_i.header['MCISTAT'] = (
            np.int16(0),   'MCI components status parameter')

        hdu_i.header['DATE-OBS'] = (data_time[:21],
                                    'observation date (yyyy-mm-ddThh:mm:ss.s)')
        hdu_i.header['OBJECT'] = ('MCI_obj',   'object name')

        hdu_i.header['TARGET'] = (
            self.information['target'][:15],     'target name (hhmmss.s+ddmmss)')
        #######################################################################
        hdu_i.header['OBSID'] = (str(obsid),   'observation ID')
        hdu_i.header['RA_OBJ'] = (
            float(self.information['ra_obj']),   'object RA (deg)')
        hdu_i.header['DEC_OBJ'] = (
            float(self.information['dec_obj']),   'object Dec (deg)')

        # ####  detector and  Filter information  #####
        hdu_i.header['FILTER'] = (self.filter_i[:6], 'filter band')
        # ####  Detector information ####
        hdu_i.header['DETSN'] = (
            'E2V-CCD-290-0000000', 'detector serial number')
        hdu_i.header['DETNAME'] = ('opt-red', 'detector name')
        # need review here

        hdu_i.header['DETTEMP0'] = (np.float32(
            0), 'detector temperature at EXPSTART (K)')
        hdu_i.header['DETTEMP1'] = (np.float32(
            0), 'detector temperature at EXPEND (K)')
        hdu_i.header['DETTEMP2'] = (np.float32(
            0), 'detector temperature at READT1 (K)')

        hdu_i.header['DETSIZE'] = (str(
            self.information['ysize'])+'*'+str(self.information['xsize']), 'detector size')
        hdu_i.header['DATASECT'] = (
            str(self.image_g.shape[0])+'*'+str(self.image_g.shape[1]), 'data section')

        hdu_i.header['PIXSCAL1'] = (
            float(0.05), 'pixel scale for axis 1 (arcsec/pixel)')
        hdu_i.header['PIXSCAL2'] = (
            float(0.05), 'pixel scale for axis 2 (arcsec/pixel)')

        hdu_i.header['PIXSIZE1'] = (int(10), 'pixel size for axis 1 (micron)')
        hdu_i.header['PIXSIZE2'] = (int(10), 'pixel size for axis 2 (micron)')

        hdu_i.header['NCHANNEL'] = (np.int16(16), 'number of readout channels')

        hdu_i.header['PSCAN1'] = (
            np.int32(27), 'horizontal prescan width, per readout channel')
        hdu_i.header['PSCAN2'] = (
            np.int32(0), 'vertical prescan width, per readout channel')

        hdu_i.header['OSCAN1'] = (
            np.int32(320), 'horizontal overscan width, per readout channel')
        hdu_i.header['OSCAN2'] = (
            np.int32(320), 'vertical overscan width, per readout channel')

        hdu_i.header['BIN_X'] = (np.int16(1), 'bin number in X')
        hdu_i.header['BIN_Y'] = (np.int16(1), 'bin number in Y')

        # ###   World coordinate system information  #####

        hdu_i.header['WCSAXES'] = (np.int16(2),    'number of WCS axes')
        hdu_i.header['CRPIX1'] = (
            round(float(self.information['CRPIX1']), 1), 'x-coordinate of reference pixel')
        hdu_i.header['CRPIX2'] = (
            round(float(self.information['CRPIX2']), 1), 'y-coordinate of reference pixel')

        hdu_i.header['CRVAL1'] = (
            float(self.information['CRVAL1']), 'value of reference pixel on axis 1')
        hdu_i.header['CRVAL2'] = (
            float(self.information['CRVAL2']), 'value of reference pixel on axis 2')

        hdu_i.header['CTYPE1'] = ('RA---TAN', 'type of RA WCS projection')
        hdu_i.header['CTYPE2'] = ('DEC--TAN', 'type of Dec WCS projection')

        hdu_i.header['CD1_1'] = (
            float(self.information['CD1_1']), 'transformation matrix element (deg/pix)')
        hdu_i.header['CD1_2'] = (
            float(self.information['CD1_2']), 'transformation matrix element (deg/pix)')
        hdu_i.header['CD2_1'] = (
            float(self.information['CD2_1']), 'transformation matrix element (deg/pix)')
        hdu_i.header['CD2_2'] = (
            float(self.information['CD2_2']), 'transformation matrix element (deg/pix)')

        # #####  Readout information  #######
        hdu_i.header['GAINLVL'] = (np.float32(
            1.5),         'gain level (e-/ADU)')
        hdu_i.header['GAIN01'] = (np.float32(
            self.information['i_gain1']), 'CCD gain [channel 1] (e-/ADU)')
        hdu_i.header['GAIN02'] = (np.float32(
            self.information['i_gain2']), 'CCD gain [channel 2] (e-/ADU)')
        hdu_i.header['GAIN03'] = (np.float32(
            self.information['i_gain3']), 'CCD gain [channel 3] (e-/ADU)')
        hdu_i.header['GAIN04'] = (np.float32(
            self.information['i_gain4']), 'CCD gain [channel 4] (e-/ADU)')
        hdu_i.header['GAIN05'] = (np.float32(
            self.information['i_gain5']), 'CCD gain [channel 5] (e-/ADU)')
        hdu_i.header['GAIN06'] = (np.float32(
            self.information['i_gain6']), 'CCD gain [channel 6] (e-/ADU)')
        hdu_i.header['GAIN07'] = (np.float32(
            self.information['i_gain7']), 'CCD gain [channel 7] (e-/ADU)')
        hdu_i.header['GAIN08'] = (np.float32(
            self.information['i_gain8']), 'CCD gain [channel 8] (e-/ADU)')
        hdu_i.header['GAIN09'] = (np.float32(
            self.information['i_gain9']), 'CCD gain [channel 9] (e-/ADU)')
        hdu_i.header['GAIN10'] = (np.float32(
            self.information['i_gain10']), 'CCD gain [channel 10] (e-/ADU)')
        hdu_i.header['GAIN11'] = (np.float32(
            self.information['i_gain11']), 'CCD gain [channel 11] (e-/ADU)')
        hdu_i.header['GAIN12'] = (np.float32(
            self.information['i_gain12']), 'CCD gain [channel 12] (e-/ADU)')
        hdu_i.header['GAIN13'] = (np.float32(
            self.information['i_gain13']), 'CCD gain [channel 13] (e-/ADU)')
        hdu_i.header['GAIN14'] = (np.float32(
            self.information['i_gain14']), 'CCD gain [channel 14] (e-/ADU)')
        hdu_i.header['GAIN15'] = (np.float32(
            self.information['i_gain15']), 'CCD gain [channel 15] (e-/ADU)')
        hdu_i.header['GAIN16'] = (np.float32(
            self.information['i_gain16']), 'CCD gain [channel 16] (e-/ADU)')
        #######
        hdu_i.header['RON01'] = (np.float32(
            self.information['i_rdnois1']), 'readout noise [channel 01] (e-)')
        hdu_i.header['RON02'] = (np.float32(
            self.information['i_rdnois2']), 'readout noise [channel 02] (e-)')
        hdu_i.header['RON03'] = (np.float32(
            self.information['i_rdnois3']), 'readout noise [channel 03] (e-)')
        hdu_i.header['RON04'] = (np.float32(
            self.information['i_rdnois4']), 'readout noise [channel 04] (e-)')
        hdu_i.header['RON05'] = (np.float32(
            self.information['i_rdnois5']), 'readout noise [channel 05] (e-)')
        hdu_i.header['RON06'] = (np.float32(
            self.information['i_rdnois6']), 'readout noise [channel 06] (e-)')
        hdu_i.header['RON07'] = (np.float32(
            self.information['i_rdnois7']), 'readout noise [channel 07] (e-)')
        hdu_i.header['RON08'] = (np.float32(
            self.information['i_rdnois8']), 'readout noise [channel 08] (e-)')
        hdu_i.header['RON09'] = (np.float32(
            self.information['i_rdnois9']), 'readout noise [channel 09] (e-)')
        hdu_i.header['RON10'] = (np.float32(
            self.information['i_rdnois10']), 'readout noise [channel 10] (e-)')
        hdu_i.header['RON11'] = (np.float32(
            self.information['i_rdnois11']), 'readout noise [channel 11] (e-)')
        hdu_i.header['RON12'] = (np.float32(
            self.information['i_rdnois12']), 'readout noise [channel 12] (e-)')
        hdu_i.header['RON13'] = (np.float32(
            self.information['i_rdnois13']), 'readout noise [channel 13] (e-)')
        hdu_i.header['RON14'] = (np.float32(
            self.information['i_rdnois14']), 'readout noise [channel 14] (e-)')
        hdu_i.header['RON15'] = (np.float32(
            self.information['i_rdnois15']), 'readout noise [channel 15] (e-)')
        hdu_i.header['RON16'] = (np.float32(
            self.information['i_rdnois16']), 'readout noise [channel 16] (e-)')

        #######
        hdu_i.header['DETBIA01'] = (np.float32(
            self.information['i_detbia1']), 'amplifier bias grey value [channel 1] (ADU)')
        hdu_i.header['DETBIA02'] = (np.float32(
            self.information['i_detbia2']), 'amplifier bias grey value [channel 2] (ADU)')
        hdu_i.header['DETBIA03'] = (np.float32(
            self.information['i_detbia3']), 'amplifier bias grey value [channel 3] (ADU)')
        hdu_i.header['DETBIA04'] = (np.float32(
            self.information['i_detbia4']), 'amplifier bias grey value [channel 4] (ADU)')
        hdu_i.header['DETBIA05'] = (np.float32(
            self.information['i_detbia5']), 'amplifier bias grey value [channel 5] (ADU)')
        hdu_i.header['DETBIA06'] = (np.float32(
            self.information['i_detbia6']), 'amplifier bias grey value [channel 6] (ADU)')
        hdu_i.header['DETBIA07'] = (np.float32(
            self.information['i_detbia7']), 'amplifier bias grey value [channel 7] (ADU)')
        hdu_i.header['DETBIA08'] = (np.float32(
            self.information['i_detbia8']), 'amplifier bias grey value [channel 8] (ADU)')
        hdu_i.header['DETBIA09'] = (np.float32(
            self.information['i_detbia9']), 'amplifier bias grey value [channel 9] (ADU)')
        hdu_i.header['DETBIA10'] = (np.float32(
            self.information['i_detbia10']), 'amplifier bias grey value [channel 10] (ADU)')
        hdu_i.header['DETBIA11'] = (np.float32(
            self.information['i_detbia11']), 'amplifier bias grey value [channel 11] (ADU)')
        hdu_i.header['DETBIA12'] = (np.float32(
            self.information['i_detbia12']), 'amplifier bias grey value [channel 12] (ADU)')
        hdu_i.header['DETBIA13'] = (np.float32(
            self.information['i_detbia13']), 'amplifier bias grey value [channel 13] (ADU)')
        hdu_i.header['DETBIA14'] = (np.float32(
            self.information['i_detbia14']), 'amplifier bias grey value [channel 14] (ADU)')
        hdu_i.header['DETBIA15'] = (np.float32(
            self.information['i_detbia15']), 'amplifier bias grey value [channel 15] (ADU)')
        hdu_i.header['DETBIA16'] = (np.float32(
            self.information['i_detbia16']), 'amplifier bias grey value [channel 16] (ADU)')
        hdu_i.header['ROSPEED'] = (np.float32(100), 'readout speed (MHz)')

        ######################################
        # ### exposure and shutter information  #####
        hdu_i.header['EXPSTART'] = (np.float64(
            time2mjd(self.dt)),   'exposure start time (MJD)')
        hdu_i.header['EXPEND'] = (float(time2mjd(t2)),
                                  'exposure end time (MJD)')
        hdu_i.header['EXPTIME'] = (np.float32(
            self.information['exptime']),   'exposure time (s)')
        hdu_i.header['DARKTIME'] = (np.float32(
            self.information['exptime']),   'dark current time (s)')
        hdu_i.header['SHTSTAT'] = (bool(True), 'shutter status')

        # ##### satellite and its allitide information  ##############
        hdu_i.header['REFFRAME'] = ('CSSTGSC-1.0',   'guiding catalog version')
        hdu_i.header['SATESWV'] = (
            'softwave-1.0',   'satellite software version')
        hdu_i.header['CABSTART'] = (np.float64(
            time2jd(self.dt)),   'first cabin time after exposure start (MJD)')
        hdu_i.header['SUNANGL0'] = (np.float32(
            0.0),   'angle between the Sun and opt axis at CABSTART')
        hdu_i.header['MOONANG0'] = (np.float32(
            0.0),   'angle between the Moon and opt axis at CABSTART')
        hdu_i.header['TEL_ALT0'] = (np.float64(
            0.0),   'angle between opt axis and Elimb at CABSTART')
        hdu_i.header['POS_ANG0'] = (np.float64(
            0.0),   'angle between y axis and North Pole at CABSTART')
        hdu_i.header['POSI0_X'] = (np.float64(
            self.information['pos_x']),   'orbital position in X at CABSTART (km)')
        hdu_i.header['POSI0_Y'] = (np.float64(
            self.information['pos_y']),   'orbital position in Y at CABSTART (km)')
        hdu_i.header['POSI0_Z'] = (np.float64(
            self.information['pos_z']),   'orbital position in Z at CABSTART (km)')
        hdu_i.header['VELO0_X'] = (np.float64(
            self.information['velocity_x']),   'orbital velocity in X at CABSTART (km/s)')
        hdu_i.header['VELO0_Y'] = (np.float64(
            self.information['velocity_y']),   'orbital velocity in Y at CABSTART (km/s)')
        hdu_i.header['VELO0_Z'] = (np.float64(
            self.information['velocity_z']),   'orbital velocity in Z at CABSTART (km/s)')
        hdu_i.header['Euler0_1'] = (np.float64(
            0.0),   'Euler angle 1 at CABSTART (deg)')
        hdu_i.header['Euler0_2'] = (np.float64(
            0.0),   'Euler angle 2 at CABSTART (deg)')
        hdu_i.header['Euler0_3'] = (np.float64(
            0.0),   'Euler angle 3 at CABSTART (deg)')
        hdu_i.header['RA_PNT0'] = (
            float(self.information['ra_pnt0']),     'pointing RA at CABSTART (deg)')
        hdu_i.header['DEC_PNT0'] = (
            float(self.information['dec_pnt0']),     'pointing Dec at CABSTART (deg)')
        hdu_i.header['CABEND'] = (float(time2jd(t2)),
                                  'first cabin time after exposure end (MJD)')
        hdu_i.header['SUNANGL1'] = (np.float32(
            0.0),   'angle between the Sun and opt axis at CABEND')
        hdu_i.header['MOONANG1'] = (np.float32(
            0.0),   'angle between the Moon and opt axis at CABEND')
        hdu_i.header['TEL_ALT1'] = (np.float64(
            0.0),   'angle between opt axis and Elimb at CABEND')
        hdu_i.header['POS_ANG1'] = (
            float(0.0),   'angle between y axis and North Pole at CABEND')
        hdu_i.header['POSI1_X'] = (
            float(p1x),   'orbital position in X at CABEND (km)')
        hdu_i.header['POSI1_Y'] = (
            float(p1y),   'orbital position in Y at CABEND (km)')
        hdu_i.header['POSI1_Z'] = (
            float(p1z),   'orbital position in Z at CABEND (km)')
        hdu_i.header['VELO1_X'] = (
            float(p1vx),   'orbital velocity in X at CABEND (km/s)')
        hdu_i.header['VELO1_Y'] = (
            float(p1vy),   'orbital velocity in Y at CABEND (km/s)')
        hdu_i.header['VELO1_Z'] = (
            float(p1vz),   'orbital velocity in Z at CABEND (km/s)')
        hdu_i.header['Euler1_1'] = (np.float64(
            0.0),   'Euler angle 1 at CABEND (deg)')
        hdu_i.header['Euler1_2'] = (np.float64(
            0.0),   'Euler angle 2 at CABEND (deg)')
        hdu_i.header['Euler1_3'] = (np.float64(
            0.0),   'Euler angle 3 at CABEND (deg)')
        hdu_i.header['RA_PNT1'] = (
            float(hdu_i.header['RA_PNT0']),    'pointing RA at CABEND (deg)')
        hdu_i.header['DEC_PNT1'] = (
            float(hdu_i.header['DEC_PNT0']),    'pointing Dec at CABEND (deg)')
        hdu_i.header['EPOCH'] = (np.float32(
            time2jd(t3.utcnow())),  'equinox of pointing RA and Dec')
        hdu_i.header['CHECKSUM'] = (0,  '0')

        ##############################################################
        # finish MCI header for image layer in G channel
        # 333

        hdulist_i = fits.HDUList([ofd_i, hdu_i])
        ####
        ofd_i.header.add_comment(
            '========================================================================', after='FITSSWV')
        ofd_i.header.add_comment('OBJECT INFORMATION', after='FITSSWV')
        ofd_i.header.add_comment(
            '========================================================================', after='FITSSWV')

        ofd_i.header.add_comment(
            '========================================================================', after='DEC_OBJ')
        ofd_i.header.add_comment('TELESCOPE INFORMATION', after='DEC_OBJ')
        ofd_i.header.add_comment(
            '========================================================================', after='DEC_OBJ')

        ofd_i.header.add_comment(
            '========================================================================', after='EXPTIME')
        ofd_i.header.add_comment('VERIFICATION INFORMATION', after='EXPTIME')
        ofd_i.header.add_comment(
            '========================================================================', after='EXPTIME')

        #######################################################################
        hdu_i.header.add_comment(
            '========================================================================', after='BUNIT')
        hdu_i.header.add_comment('FILE INFORMATION', after='BUNIT')
        hdu_i.header.add_comment(
            '========================================================================', after='BUNIT')

        hdu_i.header.add_comment(
            '========================================================================', after='FITSSWV')
        hdu_i.header.add_comment(
            'INSTRUMENT STATUS AND OBJECT INFORMATION', after='FITSSWV')
        hdu_i.header.add_comment(
            '========================================================================', after='FITSSWV')

        hdu_i.header.add_comment(
            '========================================================================', after='DEC_OBJ')
        hdu_i.header.add_comment(
            'DETECTOR AND FILTER INFORMATION', after='DEC_OBJ')
        hdu_i.header.add_comment(
            '========================================================================', after='DEC_OBJ')

        hdu_i.header.add_comment(
            '========================================================================', after='BIN_Y')
        hdu_i.header.add_comment(
            'WORLD COORDINATE SYSTEM INFORMATION', after='BIN_Y')
        hdu_i.header.add_comment(
            '========================================================================', after='BIN_Y')

        hdu_i.header.add_comment(
            '========================================================================', after='CD2_2')
        hdu_i.header.add_comment('READOUT INFORMATION', after='CD2_2')
        hdu_i.header.add_comment(
            '========================================================================', after='CD2_2')

        hdu_i.header.add_comment(
            '========================================================================', after='ROSPEED')
        hdu_i.header.add_comment(
            'EXPOSURE AND SHUTTER INFORMATION', after='ROSPEED')
        hdu_i.header.add_comment(
            '========================================================================', after='ROSPEED')

        hdu_i.header.add_comment(
            '========================================================================', after='SHTSTAT')
        hdu_i.header.add_comment(
            'SATELLITE AND ITS ATTITUDE INFORMATION', after='SHTSTAT')
        hdu_i.header.add_comment(
            '========================================================================', after='SHTSTAT')

        hdu_i.header.add_comment(
            '========================================================================', after='EPOCH')
        hdu_i.header.add_comment('VERIFICATION INFORMATION', after='EPOCH')
        hdu_i.header.add_comment(
            '========================================================================', after='EPOCH')

        # hdu_i.header.add_comment('========================================================================',before='CHECKSUM')
        # hdu_i.header.add_comment('VERIFICATION INFORMATION',before='CHECKSUM')
        # hdu_i.header.add_comment('========================================================================',before='CHECKSUM')

        #####################################
        # finish MCI header for image layer in i channel
        # finish MCI header for image layer
        if self.source == 'DARK' or self.source == 'FLAT' or self.source == 'BIAS':
            file_i = self.result_path+'/cali_Data/'+filename_i+'.fits'
        else:
            file_i = self.result_path+'/sky_Data/'+filename_i + '.fits'

        hdulist_i.writeto(file_i, checksum=True)

#################################################################################################

    def simulate(self, simnumber):
        """
        Create a single simulated image of a quadrant defined by the configuration file.
        Will do all steps defined in the config file sequentially.

        :return: None
        """

        ##############################################################################################
        ##############################################################################################
        sourcelist = ['EXDF', 'STAR', 'PIPR', 'TRNS', 'COMB', 'CALI']

        sim_list = ['FLAT', 'BIAS', 'DARK', 'EXDF',
                    'STAR', 'PIPR', 'TRNS', 'COMB', 'CALI']

        if self.source not in sim_list:
            print('Sourcein is wrong')
            sys.exit(1)

        self._loadGhostModel()
        self.load_filter_PSF()
        self.information['simnumber'] = simnumber

        self.debug = self.information['debug']
        ###########################
        flag = 0

        self.dt_num = int((self.information['simnumber']-1)*(
            self.information['exptime']+self.information['readouttime']+50)/120)
        now_dt = datetime.utcnow()
        now_jd = time2jd(now_dt)

        for k in range(1, 50, 1):

            fn = self.information['dir_path'] + \
                'MCI_inputData/TianCe/orbit20160925/'+str(k)+'.txt'
            d = np.loadtxt(fn)

            for kk in range(len(d[:, 0])):
                if now_jd-d[kk, 0] <= 0:
                    flag = 1
                    break
            if flag == 1:
                print('find time index...... ')
                break

        ################################################################

        if kk + self.dt_num < len(d[:, 0]):

            exptime_start_jd = d[kk+self.dt_num, 0]

            self.orbit_pars = d
            self.orbit_file_num = k
            self.orbit_exp_num = kk

            self.information['pos_x'] = float(
                self.orbit_pars[self.orbit_exp_num, 1])
            self.information['pos_y'] = float(
                self.orbit_pars[self.orbit_exp_num, 2])
            self.information['pos_z'] = float(
                self.orbit_pars[self.orbit_exp_num, 3])
            self.information['velocity_x'] = float(
                self.orbit_pars[self.orbit_exp_num, 4])
            self.information['velocity_y'] = float(
                self.orbit_pars[self.orbit_exp_num, 5])
            self.information['velocity_z'] = float(
                self.orbit_pars[self.orbit_exp_num, 6])

        else:

            fn = self.information['dir_path'] + \
                'MCI_inputData/TianCe/orbit20160925/'+str(k+1)+'.txt'
            d = np.loadtxt(fn)

            self.orbit_pars = d
            self.orbit_file_num = k+1
            self.orbit_exp_num = self.dt_num

            self.information['pos_x'] = float(
                self.orbit_pars[self.orbit_exp_num, 1])
            self.information['pos_y'] = float(
                self.orbit_pars[self.orbit_exp_num, 2])
            self.information['pos_z'] = float(
                self.orbit_pars[self.orbit_exp_num, 3])
            self.information['velocity_x'] = float(
                self.orbit_pars[self.orbit_exp_num, 4])
            self.information['velocity_y'] = float(
                self.orbit_pars[self.orbit_exp_num, 5])
            self.information['velocity_z'] = float(
                self.orbit_pars[self.orbit_exp_num, 6])
            ##########################################

        # ##self.dt=julian.from_jd(exptime_start_jd, fmt='jd')
        self.dt = jd2time(exptime_start_jd)

        # str(self.dt.year)+'-'+str(self.dt.month)+'-'+str(self.dt.day)
        self.TianCe_day = str(self.dt.year)+self.dt.strftime("-%m-%d")

        self.TianCe_exp_start = dt2hmd(self.dt)

        #############################################################

        if self.source in sourcelist:

            # self.earthshine_theta=30.0       # in degree

            ###################################
            if self.sky_shift_rot:

                if self.information['simnumber'] > 1:

                    np.random.seed(self.information['simnumber'])
                    ud = np.random.random()        # Choose a random pointing ra in degree
                    dis_ra = 2*(ud-1)*10/3600.0

                    np.random.seed(10*self.information['simnumber'])
                    ud = np.random.random()        # Choose a random pointing dec in degree
                    dis_dec = 2*(ud-1)*10/3600.0

                    np.random.seed(100*self.information['simnumber'])
                    ud = np.random.random()        # Choose a random telescope rotation in degree
                    # the image will rotate in clockwise rotaion between -10-10 degree
                    rotTelPos = 2*(ud-1)*5.0*galsim.degrees
                    rotSkyPos = 0.0*galsim.degrees

                    ################################
                    # ### test code
                    # rotTelPos = 90.0*galsim.degrees
                    # dis_dec= -200*0.05/3600
                    # dis_ra =  100*0.05/3600

                else:

                    dis_ra = 0     #
                    dis_dec = 0     # T

                    # the image will rotate in clockwise rotaion between -10-10 degree
                    rotTelPos = 0.0*galsim.degrees
                    rotSkyPos = 0.0*galsim.degrees

            else:

                dis_ra = 0     #
                dis_dec = 0     # T

                # the image will rotate in clockwise rotaion between -10-10 degree
                rotTelPos = 0.0*galsim.degrees
                rotSkyPos = 0.0*galsim.degrees

            #####################################################

            # telescope pointing Ra  displacement
            self.information['T_disRa'] = dis_ra
            # telescope pointing Dec displacement
            self.information['T_disDec'] = dis_dec

            # telescope rotation in degree
            self.information['rotTelPos'] = rotTelPos
            # sky rotation in degree
            self.information['rotSkyPos'] = rotSkyPos

            self.information['name_obj'] = 'MCI_obj'

            theta = self.information['rotTelPos'] - \
                self.information['rotSkyPos']

            self.information['rotangle'] = theta.deg
            # print('rotangle',theta.deg )

            self.log.info('dis_ra(in pixel)=%f, dis_dec(in pixel)=%f, sky_rot(in deg)=%f' % (
                dis_ra*3600/0.05, dis_dec*3600/0.05, theta.deg))
###################################################################
##################################################################
# ########   simulate star images from star SED data   ###########

            if self.sim_star:
                ###########################

                starimg = self.cal_StarSED_img()

                if 'starimg' in dir():
                    self.log.info('Generate artifical stars finished...')
                    # fits.writeto(self.result_path+'/ori_Sky/starimg_C1_'+str(simnumber)+'.fits',starimg['g'], overwrite=True)
                    # fits.writeto(self.result_path+'/ori_Sky/starimg_C2_'+str(simnumber)+'.fits',starimg['r'], overwrite=True)
                    # fits.writeto(self.result_path+'/ori_Sky/starimg_C3_'+str(simnumber)+'.fits',starimg['i'], overwrite=True)

                    # self.img_fits_save(
                    #     starimg['g'], 'star_ori_C1_'+str(simnumber))
                    # self.img_fits_save(
                    #     starimg['r'], 'star_ori_C2_'+str(simnumber))
                    # self.img_fits_save(
                    #     starimg['i'], 'star_ori_C3_'+str(simnumber))

                    print('star image finished')

            ##########################################################################
            # # ########################################################################
            # ######   generate galaxy SED  image without Lensing effect    ##########

            if self.sim_galaxy and self.source == 'EXDF':

                losimg = self.cal_Lensing_galaxy_img()

                if 'losimg' in dir():
                    self.log.info('Generate los finished...')
                    print('losimage finished')
                    # fits.writeto(self.result_path+'/ori_Sky/galimg_C1_'+str(simnumber)+'.fits',losimg['g'], overwrite=True)
                    # fits.writeto(self.result_path+'/ori_Sky/galimg_C2_'+str(simnumber)+'.fits',losimg['r'], overwrite=True)
                    # fits.writeto(self.result_path+'/ori_Sky/galimg_C3_'+str(simnumber)+'.fits',losimg['i'], overwrite=True)

                    # self.img_fits_save(
                    #     losimg['g'], 'gal_ori_C1_'+str(simnumber))
                    # self.img_fits_save(
                    #     losimg['r'], 'gal_ori_C2_'+str(simnumber))
                    # self.img_fits_save(
                    #     losimg['i'], 'gal_ori_C3_'+str(simnumber))

            ##################################################################

            # stack the image

            if 'losimg' in dir():
                self.image_g += losimg['g']
                self.image_r += losimg['r']
                self.image_i += losimg['i']

            if 'starimg' in dir():
                self.image_g += starimg['g']
                self.image_r += starimg['r']
                self.image_i += starimg['i']

            ###########################################################################################################

            fits.writeto(self.result_path+'/ori_Sky/original_C1_' +
                         str(simnumber)+'.fits', self.image_g, overwrite=True)
            fits.writeto(self.result_path+'/ori_Sky/original_C2_' +
                         str(simnumber)+'.fits', self.image_r, overwrite=True)
            fits.writeto(self.result_path+'/ori_Sky/original_C3_' +
                         str(simnumber)+'.fits', self.image_i, overwrite=True)

            # self.img_fits_save(self.image_g, 'full_ori_C1_'+str(simnumber))
            # self.img_fits_save(self.image_g, 'full_ori_C2_'+str(simnumber))
            # self.img_fits_save(self.image_g, 'full_ori_C3_'+str(simnumber))

            #########################################################################################################

        # end if source =='SKY'

        elif self.source == 'FLAT':

            theta = 0
            # np.ceil(self.information['simnumber']/2.0)
            self.information['exptime'] = 100*self.information['simnumber']
            self.information['name_obj'] = 'FLAT'

            self.information['rotangle'] = 0

            self.information['CRVAL1'] = 0
            self.information['CRVAL2'] = 0

            self.information['tel_Pra'] = 0
            self.information['tel_Pdec'] = 0

            self.information['CRPIX1'] = self.information['tel_Pra'] * \
                self.information['pixel_size']
            self.information['CRPIX2'] = self.information['tel_Pdec'] * \
                self.information['pixel_size']

            self.information['CD1_1'] = - \
                np.cos(theta)*self.information['pixel_size']
            self.information['CD1_2'] = np.sin(
                theta)*self.information['pixel_size']

            self.information['CD2_1'] = np.sin(
                theta)*self.information['pixel_size']
            self.information['CD2_2'] = np.cos(
                theta)*self.information['pixel_size']

            self.information['ra_obj'] = 0
            self.information['dec_obj'] = 0
            self.information['ra_pnt0'] = 0
            self.information['dec_pnt0'] = 0
            self.information['target'] = 'FLAT'

            self.image_g += 10000*self.information['simnumber']
            self.image_r += 10000*self.information['simnumber']
            self.image_i += 10000*self.information['simnumber']

        elif self.source == 'DARK':

            theta = 0

            self.information['exptime'] = 2*3600*self.information['simnumber']
            self.information['name_obj'] = 'DARK'

            self.information['rotangle'] = 0

            self.information['CRVAL1'] = 0
            self.information['CRVAL2'] = 0

            self.information['tel_Pra'] = 0
            self.information['tel_Pdec'] = 0
            self.information['CRPIX1'] = self.information['tel_Pra'] * \
                self.information['pixel_size']
            self.information['CRPIX2'] = self.information['tel_Pdec'] * \
                self.information['pixel_size']

            self.information['CD1_1'] = - \
                np.cos(theta)*self.information['pixel_size']
            self.information['CD1_2'] = np.sin(
                theta)*self.information['pixel_size']

            self.information['CD2_1'] = np.sin(
                theta)*self.information['pixel_size']
            self.information['CD2_2'] = np.cos(
                theta)*self.information['pixel_size']

            self.information['ra_obj'] = 0
            self.information['dec_obj'] = 0
            self.information['ra_pnt0'] = 0
            self.information['dec_pnt0'] = 0
            self.information['target'] = 'DARK'

            self.image_g = np.zeros(
                (self.information['ysize'], self.information['xsize']), dtype=float)
            self.image_r = np.zeros(
                (self.information['ysize'], self.information['xsize']), dtype=float)
            self.image_i = np.zeros(
                (self.information['ysize'], self.information['xsize']), dtype=float)

        elif self.source == 'BIAS':

            theta = 0

            self.information['exptime'] = 0.0

            self.information['name_obj'] = 'BIAS'

            self.information['rotangle'] = 0

            self.information['CRVAL1'] = 0
            self.information['CRVAL2'] = 0

            self.information['tel_Pra'] = 0
            self.information['tel_Pdec'] = 0

            self.information['CRPIX1'] = self.information['tel_Pra'] * \
                self.information['pixel_size']
            self.information['CRPIX2'] = self.information['tel_Pdec'] * \
                self.information['pixel_size']

            self.information['CD1_1'] = - \
                np.cos(theta)*self.information['pixel_size']
            self.information['CD1_2'] = np.sin(
                theta)*self.information['pixel_size']

            self.information['CD2_1'] = np.sin(
                theta)*self.information['pixel_size']
            self.information['CD2_2'] = np.cos(
                theta)*self.information['pixel_size']

            self.image_g = np.zeros(
                (self.information['ysize'], self.information['xsize']), dtype=float)
            self.image_r = np.zeros(
                (self.information['ysize'], self.information['xsize']), dtype=float)
            self.image_i = np.zeros(
                (self.information['ysize'], self.information['xsize']), dtype=float)
            self.information['target'] = 'BIAS'
            self.information['ra_obj'] = 0
            self.information['dec_obj'] = 0
            self.information['ra_pnt0'] = 0
            self.information['dec_pnt0'] = 0

        else:
            print('Souce is not correct and programe will return')
            sys.exit(1)

###########################################################################
#########################################################################

        # Apply flat-field large scale structure for one chip
        if self.flatfieldM:
            self.applyFlat()
            print('applyFlat')
        ########################################################
        if self.PRNUeffect:
            self.applyPRNUeffect()
            print('applyPRNUeffect')

        ####################################################################
        if self.source in sourcelist:
            if self.cosmicRays:

                self.addCosmicRays()
                print('addCosmicRays finished')

        ##################################################
        if self.skyback:
            if self.source in sourcelist:
                self.applyPoissonSkyNoise()
                print('apply Poisson Sky Noise')
        #################################################

        if self.darknoise:
            self.applyDarkCurrent()
            print('applyDarkCurrent finished')

        #################################################

        if self.shutterEffect and self.information['exptime'] > 2.6:
            self.addshuttereffect()
            print('add shutter effect ')

        #################################################

        if self.bleeding:

            self.image_g = self.applyBleeding(self.image_g.copy())
            self.image_r = self.applyBleeding(self.image_r.copy())
            self.image_i = self.applyBleeding(self.image_i.copy())
            print('apply bleeding effect finished')

        ################################################

        if self.nonlinearity:
            self.applyNonlinearity()
            print('applyNonlinearity')

        # ################################################
        # read CCD image to output matrix as defined

        if self.overscans and self.information['xsize'] == 9216 and self.information['ysize'] == 9232:

            self.image_g = self.addScanEffect(self.image_g.copy())
            self.image_r = self.addScanEffect(self.image_r.copy())
            self.image_i = self.addScanEffect(self.image_i.copy())
            print('apply overscan zone 1-16')

        #################################################################
        if self.source in sourcelist:
            if self.radiationDamage:

                self.applyRadiationDamage()
                print('applyRadiationDamage()')
        #####################################################################
        # #### apply readoutNoise   ######

        if self.readoutNoise:
            self.applyReadoutNoise()
            print('apply readout noise')

        ######################################################################
        if self.information['xsize'] == 9216 and self.information['ysize'] == 9232 and self.overscans:
            self.electrons2ADU()  # apply gain
            print('apply 1-16 zone by different gain ')

        else:
            self.image_g = self.image_g/1.0
            self.image_r = self.image_r/1.0
            self.image_i = self.image_i/1.0
            print('apply gain finished')
        ######################################################################
        # size of the output image array, xsize is column, ysize is row, xsize = 9216,ysize = 9232

        if self.information['xsize'] == 9216 and self.information['ysize'] == 9232 and self.overscans:
            self.applyBias()
            print('apply bias finished')
        else:
            self.image_g = self.image_g+500.0
            self.image_r = self.image_r+500.0
            self.image_i = self.image_i+500.0

        ###################################################################
        if self.cosmetics and self.information['xsize'] == 9216 and self.information['ysize'] == 9232:

            self.applyCosmetics()
            print('apply Cosmetics...')

        ##################################################
        if self.intscale:
            self.discretise()
        ##################################################
        self.writeOutputs(simnumber)

        #######################################################
        self.log.info('Using the following input values:')

        for key, value in self.information.items():
            self.log.info('%s = %s' % (key, value))
        self.log.info('Using the following booleans:')

        for key, value in self.booleans.items():
            self.log.info('%s = %s' % (key, value))
        #######################################################

        self.log.info('Finished the ith_Exposure = %i' % (simnumber))

        print('The iLoop= % d simlaiton finished. ' % simnumber)


################################################################################################

def runMCIsim(sourcein, configfile, dir_path, result_path, debug, iLoop):

    print('Path Test：dir_path', dir_path)

    sim = dict()
    sim[iLoop] = MCIsimulator(configfile)

    sim[iLoop].configure(iLoop, sourcein, dir_path,
                         result_path)    # load the configfile;

    sim[iLoop].information['sourcein'] = sourcein
    sim[iLoop].information['debug'] = debug
    sim[iLoop].information['result_path'] = result_path

    sim[iLoop].simulate(iLoop)

    return

################################################################################################
