Chip.py 45.2 KB
Newer Older
Fang Yuedong's avatar
Fang Yuedong committed
1
2
3
import galsim
import os
import numpy as np
Fang Yuedong's avatar
Fang Yuedong committed
4
5
import pickle
import json
Fang Yuedong's avatar
Fang Yuedong committed
6
7
from astropy.table import Table
from numpy.random import Generator, PCG64
Fang Yuedong's avatar
Fang Yuedong committed
8
9
from astropy.io import fits
from datetime import datetime
Fang Yuedong's avatar
Fang Yuedong committed
10

11
12
13
from ObservationSim.Instrument.Chip import Effects as effects
from ObservationSim.Instrument.FocalPlane import FocalPlane
from ObservationSim.Config.Header import generatePrimaryHeader, generateExtensionHeader
Fang Yuedong's avatar
Fang Yuedong committed
14
from ObservationSim.Instrument._util import rotate_conterclockwise
15

Fang Yuedong's avatar
Fang Yuedong committed
16
17
18
19
20
21
try:
    import importlib.resources as pkg_resources
except ImportError:
    # Try backported to PY<37 'importlib_resources'
    import importlib_resources as pkg_resources

Fang Yuedong's avatar
Fang Yuedong committed
22
class Chip(FocalPlane):
23
    def __init__(self, chipID, ccdEffCurve_dir=None, CRdata_dir=None, sls_dir=None, config=None, treering_func=None, logger=None):
Fang Yuedong's avatar
Fang Yuedong committed
24
25
26
        # Get focal plane (instance of paraent class) info
        # TODO: use chipID to config individual chip?
        super().__init__()
Fang Yuedong's avatar
Fang Yuedong committed
27
28
29
        # self.npix_x = 9216
        # self.npix_y = 9232
        # self.pix_scale  = 0.074 # pixel scale
30
31
32
        self.nsecy = 2
        self.nsecx = 8
        self.gain_channel = np.ones(self.nsecy* self.nsecx)
Fang Yuedong's avatar
Fang Yuedong committed
33
        self._set_attributes_from_config(config)
Fang Yuedong's avatar
Fang Yuedong committed
34

35
36
        self.logger = logger

Fang Yuedong's avatar
Fang Yuedong committed
37
38
        # A chip ID must be assigned
        self.chipID = int(chipID)
Fang Yuedong's avatar
Fang Yuedong committed
39
        self.chip_name = str(chipID).rjust(2, '0')
Fang Yuedong's avatar
Fang Yuedong committed
40
41
42
43
44

        # Get corresponding filter info
        self.filter_id, self.filter_type = self.getChipFilter()
        self.survey_type = self._getSurveyType()

Fang Yuedong's avatar
Fang Yuedong committed
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
        # [TODO]
        if self.filter_type != "FGS":
            self._getChipRowCol()

        # Set the relavent specs for FGS detectors
        # [TODO]
        try:
            with pkg_resources.files('ObservationSim.Instrument.data.ccd').joinpath("chip_definition.json") as chip_definition:
                with open(chip_definition, "r") as f:
                    chip_dict = json.load(f)[str(self.chipID)]
        except AttributeError:
            with pkg_resources.path('ObservationSim.Instrument.data.ccd', "chip_definition.json") as chip_definition:
                with open(chip_definition, "r") as f:
                    chip_dict = json.load(f)[str(self.chipID)]
        for key in chip_dict:
            setattr(self, key, chip_dict[key])
        if self.filter_type == "FGS":
62
            if ("field_dist" in config) and (config["ins_effects"]["field_dist"]) == False:
Fang Yuedong's avatar
Fang Yuedong committed
63
64
65
66
67
68
69
70
71
72
73
74
75
                self.fdModel = None
            else:
                fgs_name = self.chip_name[0:4]
                try:
                    with pkg_resources.files('ObservationSim.Instrument.data.field_distortion').joinpath("FieldDistModelGlobal_pr4_%s.pickle"%(fgs_name.lower())) as field_distortion:
                        with open(field_distortion, "rb") as f:
                            self.fdModel = pickle.load(f)
                except AttributeError:
                    with pkg_resources.path('ObservationSim.Instrument.data.field_distortion', "FieldDistModelGlobal_pr4_%s.pickle"%(fgs_name.lower())) as field_distortion:
                        with open(field_distortion, "rb") as f:
                            self.fdModel = pickle.load(f)
        else:
            # Get the corresponding field distortion model
76
            if ("field_dist" in config) and (config["ins_effects"]["field_dist"] == False):
Fang Yuedong's avatar
Fang Yuedong committed
77
78
79
80
81
82
83
84
85
86
87
                self.fdModel = None
            else:
                try:
                    with pkg_resources.files('ObservationSim.Instrument.data.field_distortion').joinpath("FieldDistModelGlobal_mainFP_v1.0.pickle") as field_distortion:
                        with open(field_distortion, "rb") as f:
                            self.fdModel = pickle.load(f)
                except AttributeError:
                    with pkg_resources.path('ObservationSim.Instrument.data.field_distortion', "FieldDistModelGlobal_mainFP_v1.0.pickle") as field_distortion:
                        with open(field_distortion, "rb") as f:
                            self.fdModel = pickle.load(f)

Fang Yuedong's avatar
Fang Yuedong committed
88
89
90
91
92
93
        # Get boundary (in pix)
        self.bound = self.getChipLim()
        self.ccdEffCurve_dir = ccdEffCurve_dir
        self.CRdata_dir = CRdata_dir
        slsconfs = self.getChipSLSConf()
        if np.size(slsconfs) == 1:
94
95
96
97
98
99
            try:
                with pkg_resources.files('ObservationSim.Instrument.data.sls_conf').joinpath(slsconfs) as conf_path:
                    self.sls_conf = str(conf_path)
            except AttributeError:
                with pkg_resources.path('ObservationSim.Instrument.data.sls_conf', slsconfs) as conf_path:
                    self.sls_conf = str(conf_path)
Fang Yuedong's avatar
Fang Yuedong committed
100
        else:
Fang Yuedong's avatar
Fang Yuedong committed
101
102
            # self.sls_conf = [os.path.join(self.sls_dir, slsconfs[0]), os.path.join(self.sls_dir, slsconfs[1])]
            self.sls_conf = []
103
104
105
106
107
108
109
110
111
112
113
114
            try:
                with pkg_resources.files('ObservationSim.Instrument.data.sls_conf').joinpath(slsconfs[0]) as conf_path:
                    self.sls_conf.append(str(conf_path))
            except AttributeError:
                with pkg_resources.path('ObservationSim.Instrument.data.sls_conf', slsconfs[0]) as conf_path:
                    self.sls_conf.append(str(conf_path))
            try:
                with pkg_resources.files('ObservationSim.Instrument.data.sls_conf').joinpath(slsconfs[1]) as conf_path:
                    self.sls_conf.append(str(conf_path))
            except AttributeError:
                with pkg_resources.path('ObservationSim.Instrument.data.sls_conf', slsconfs[1]) as conf_path:
                    self.sls_conf.append(str(conf_path))
Fang Yuedong's avatar
Fang Yuedong committed
115
116
117
118
        
        self.effCurve = self._getChipEffCurve(self.filter_type)
        self._getCRdata()

Fang Yuedong's avatar
Fang Yuedong committed
119
        # Define the sensor model
Fang Yuedong's avatar
Fang Yuedong committed
120
        if config["ins_effects"]["bright_fatter"] == True and self.survey_type == "photometric":
Fang Yuedong's avatar
Fang Yuedong committed
121
            self.sensor = galsim.SiliconSensor(strength=self.df_strength, treering_func=treering_func)
Fang Yuedong's avatar
Fang Yuedong committed
122
123
124
        else:
            self.sensor = galsim.Sensor()

125
126
        self.flat_cube = None # for spectroscopic flat field cube simulation

Fang Yuedong's avatar
Fang Yuedong committed
127
128
129
130
131
132
133
134
135
136
137
    def _set_attributes_from_config(self, config):
        # Default setting
        self.read_noise = 5.0   # e/pix
        self.dark_noise = 0.02  # e/pix/s
        self.rotate_angle = 0.
        self.overscan   = 1000
        # Override default values
        for key in ["gain", "bias_level, dark_exptime", "flat_exptime", "readout_time", "full_well", "read_noise", "dark_noise", "overscan"]:
            if key in config["ins_effects"]:
                setattr(self, key, config["ins_effects"][key])

Fang Yuedong's avatar
Fang Yuedong committed
138
139
140
141
142
143
144
145
146
147
148
    def _getChipRowCol(self):
        self.rowID, self.colID = self.getChipRowCol(self.chipID)

    def getChipRowCol(self, chipID):
        rowID = ((chipID - 1) % 5) + 1
        colID = 6 - ((chipID - 1) // 5)
        return rowID, colID

    def _getSurveyType(self):
        if self.filter_type in ["GI", "GV", "GU"]:
            return "spectroscopic"
Fang Yuedong's avatar
Fang Yuedong committed
149
        elif self.filter_type in ["nuv", "u", "g", 'r', 'i', 'z', 'y', 'FGS']:
Fang Yuedong's avatar
Fang Yuedong committed
150
            return "photometric"
Fang Yuedong's avatar
Fang Yuedong committed
151
152
        # elif self.filter_type in ["FGS"]:
        #     return "FGS"
Fang Yuedong's avatar
Fang Yuedong committed
153
154
155
156

    def _getChipEffCurve(self, filter_type):
        # CCD efficiency curves
        if filter_type in ['nuv', 'u', 'GU']: filename = 'UV0.txt'
Fang Yuedong's avatar
Fang Yuedong committed
157
        if filter_type in ['g', 'r', 'GV', 'FGS']: filename = 'Astro_MB.txt' # TODO, need to switch to the right efficiency curvey for FGS CMOS
Fang Yuedong's avatar
Fang Yuedong committed
158
159
        if filter_type in ['i', 'z', 'y', 'GI']: filename = 'Basic_NIR.txt'
        # Mirror efficiency:
160
161
162
163
        # if filter_type == 'nuv': mirror_eff = 0.54
        # if filter_type == 'u': mirror_eff = 0.68
        # if filter_type in ['g', 'r', 'i', 'z', 'y']: mirror_eff = 0.8
        # if filter_type in ['GU', 'GV', 'GI']: mirror_eff = 1. # Not sure if this is right
Fang Yuedong's avatar
Fang Yuedong committed
164
165
        # path = os.path.join(self.ccdEffCurve_dir, filename)
        # table = Table.read(path, format='ascii')
166
167
168
169
170
171
        try:
            with pkg_resources.files('ObservationSim.Instrument.data.ccd').joinpath(filename) as ccd_path:
                table = Table.read(ccd_path, format='ascii')
        except AttributeError:
            with pkg_resources.path('ObservationSim.Instrument.data.ccd', filename) as ccd_path:
                table = Table.read(ccd_path, format='ascii')
172
        throughput = galsim.LookupTable(x=table['col1'], f=table['col2'], interpolant='linear')
Fang Yuedong's avatar
Fang Yuedong committed
173
174
175
176
        bandpass = galsim.Bandpass(throughput, wave_type='nm')
        return bandpass

    def _getCRdata(self):
177
178
179
180
181
182
        try:
            with pkg_resources.files('ObservationSim.Instrument.data').joinpath("wfc-cr-attachpixel.dat") as cr_path:
                self.attachedSizes = np.loadtxt(cr_path)
        except AttributeError:
            with pkg_resources.path('ObservationSim.Instrument.data', "wfc-cr-attachpixel.dat") as cr_path:
                self.attachedSizes = np.loadtxt(cr_path)
Fang Yuedong's avatar
Fang Yuedong committed
183
184
185
186

    def getChipFilter(self, chipID=None, filter_layout=None):
        """Return the filter index and type for a given chip #(chipID)
        """
Fang Yuedong's avatar
Fang Yuedong committed
187
        filter_type_list = ["nuv","u", "g", "r", "i","z","y","GU", "GV", "GI", "FGS"]
Fang Yuedong's avatar
Fang Yuedong committed
188
189
190
191
192
193
        if filter_layout is not None:
            return filter_layout[chipID][0], filter_layout[chipID][1]
        if chipID == None:
            chipID = self.chipID

        # updated configurations
Fang Yuedong's avatar
Fang Yuedong committed
194
195
        if chipID>42 or chipID<1: raise ValueError("!!! Chip ID: [1,42]")
        if chipID in [6, 15, 16, 25]:  filter_type = "y"
Fang Yuedong's avatar
Fang Yuedong committed
196
        if chipID in [11, 20]:         filter_type = "z"
Fang Yuedong's avatar
Fang Yuedong committed
197
        if chipID in [7, 24]:          filter_type = "i"
Fang Yuedong's avatar
Fang Yuedong committed
198
        if chipID in [14, 17]:         filter_type = "u"
Fang Yuedong's avatar
Fang Yuedong committed
199
200
201
202
203
204
205
        if chipID in [9, 22]:          filter_type = "r"
        if chipID in [12, 13, 18, 19]: filter_type = "nuv"
        if chipID in [8, 23]:          filter_type = "g"
        if chipID in [1, 10, 21, 30]:  filter_type = "GI"
        if chipID in [2, 5, 26, 29]:   filter_type = "GV"
        if chipID in [3, 4, 27, 28]:   filter_type = "GU"
        if chipID in range(31, 43):    filter_type = 'FGS'
Fang Yuedong's avatar
Fang Yuedong committed
206
207
208
209
210
211
        filter_id = filter_type_list.index(filter_type)

        return filter_id, filter_type

    def getChipLim(self, chipID=None):
        """Calculate the edges in pixel for a given CCD chip on the focal plane
Fang Yuedong's avatar
Fang Yuedong committed
212
        NOTE: There are 5*4 CCD chips in the focus plane for photometric / spectroscopic observation.
Fang Yuedong's avatar
Fang Yuedong committed
213
214
215
216
217
218
        Parameters:
            chipID:         int
                            the index of the chip
        Returns:
            A galsim BoundsD object
        """
Fang Yuedong's avatar
Fang Yuedong committed
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
        if ((chipID is not None) and (int(chipID) <= 30)) or (self.chipID <= 30):
            # [TODO]
            if chipID == None:
                chipID = self.chipID
                rowID, colID = self.rowID, self.colID
            else:
                rowID, colID = self.getChipRowCol(chipID)
            gx1, gx2 = self.npix_gap_x
            gy = self.npix_gap_y

            # xlim of a given CCD chip
            xrem = 2*(colID - 1) - (self.nchip_x - 1)
            xcen = (self.npix_x//2 + gx1//2) * xrem
            if chipID >= 26 or chipID == 21:
                xcen = (self.npix_x//2 + gx1//2) * xrem - (gx2-gx1)
            if chipID <= 5 or chipID == 10:
                xcen = (self.npix_x//2 + gx1//2) * xrem + (gx2-gx1)
            nx0 = xcen - self.npix_x//2 + 1
            nx1 = xcen + self.npix_x//2

            # ylim of a given CCD chip
            yrem = (rowID - 1) - self.nchip_y // 2
            ycen = (self.npix_y + gy) * yrem
            ny0 = ycen - self.npix_y//2 + 1
            ny1 = ycen + self.npix_y//2
            return galsim.BoundsD(nx0-1, nx1-1, ny0-1, ny1-1)
Fang Yuedong's avatar
Fang Yuedong committed
245
        else:
Fang Yuedong's avatar
Fang Yuedong committed
246
247
248
249
250
251
252
253
254
255
256
257
            xmin, xmax, ymin, ymax = 1e10, -1e10, 1e10, -1e10
            xcen = self.x_cen / self.pix_size
            ycen = self.y_cen / self.pix_size
            sign_x = [-1., 1., -1., 1.]
            sign_y = [-1., -1., 1., 1.]
            for i in range(4):
                x = xcen + sign_x[i] * self.npix_x / 2.
                y = ycen + sign_y[i] * self.npix_y / 2.
                x, y = rotate_conterclockwise(x0=xcen, y0=ycen, x=x, y=y, angle=self.rotate_angle)
                xmin, xmax = min(xmin, x), max(xmax, x)
                ymin, ymax = min(ymin, y), max(ymax, y)
            return galsim.BoundsD(xmin, xmax, ymin, ymax)
Fang Yuedong's avatar
Fang Yuedong committed
258
259
260
261



    def getSkyCoverage(self, wcs):
Fang Yuedong's avatar
Fang Yuedong committed
262
        # print("In getSkyCoverage: xmin = %.3f, xmax = %.3f, ymin = %.3f, ymax = %.3f"%(self.bound.xmin, self.bound.xmax, self.bound.ymin, self.bound.ymax))
Fang Yuedong's avatar
Fang Yuedong committed
263
264
265
266
267
268
269
270
271
272
        return super().getSkyCoverage(wcs, self.bound.xmin, self.bound.xmax, self.bound.ymin, self.bound.ymax)


    def getSkyCoverageEnlarged(self, wcs, margin=0.5):
        """The enlarged sky coverage of the chip
        """
        margin /= 60.0
        bound = self.getSkyCoverage(wcs)
        return galsim.BoundsD(bound.xmin - margin, bound.xmax + margin, bound.ymin - margin, bound.ymax + margin)

Fang Yuedong's avatar
Fang Yuedong committed
273
    def isContainObj(self, ra_obj=None, dec_obj=None, x_image=None, y_image=None, wcs=None, margin=1):
Fang Yuedong's avatar
Fang Yuedong committed
274
        # magin in number of pix
Fang Yuedong's avatar
Fang Yuedong committed
275
276
277
278
279
280
281
282
        if (ra_obj is not None) and (dec_obj is not None):
            if wcs is None:
                wcs = self.img.wcs
            pos_obj = wcs.toImage(galsim.CelestialCoord(ra=ra_obj*galsim.degrees, dec=dec_obj*galsim.degrees))
            x_image, y_image = pos_obj.x, pos_obj.y
        elif (x_image is None) or (y_image is None):
            raise ValueError("Either (ra_obj, dec_obj) or (x_image, y_image) should be given")

Fang Yuedong's avatar
Fang Yuedong committed
283
284
        xmin, xmax = self.bound.xmin - margin, self.bound.xmax + margin
        ymin, ymax = self.bound.ymin - margin, self.bound.ymax + margin
Fang Yuedong's avatar
Fang Yuedong committed
285
        if (x_image - xmin) * (x_image - xmax) > 0.0 or (y_image - ymin) * (y_image - ymax) > 0.0:
Fang Yuedong's avatar
Fang Yuedong committed
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
            return False
        return True

    def getChipNoise(self, exptime=150.0):
        noise = self.dark_noise * exptime + self.read_noise**2
        return noise

    def getChipSLSConf(self):
        confFile = ''
        if self.chipID == 1: confFile = ['CSST_GI2.conf', 'CSST_GI1.conf']
        if self.chipID == 2: confFile = ['CSST_GV4.conf', 'CSST_GV3.conf']
        if self.chipID == 3: confFile = ['CSST_GU2.conf', 'CSST_GU1.conf']
        if self.chipID == 4: confFile = ['CSST_GU4.conf', 'CSST_GU3.conf']
        if self.chipID == 5: confFile = ['CSST_GV2.conf', 'CSST_GV1.conf']
        if self.chipID == 10: confFile = ['CSST_GI4.conf', 'CSST_GI3.conf']
        if self.chipID == 21: confFile = ['CSST_GI6.conf', 'CSST_GI5.conf']
        if self.chipID == 26: confFile = ['CSST_GV8.conf', 'CSST_GV7.conf']
        if self.chipID == 27: confFile = ['CSST_GU6.conf', 'CSST_GU5.conf']
        if self.chipID == 28: confFile = ['CSST_GU8.conf', 'CSST_GU7.conf']
        if self.chipID == 29: confFile = ['CSST_GV6.conf', 'CSST_GV5.conf']
        if self.chipID == 30: confFile = ['CSST_GI8.conf', 'CSST_GI7.conf']
        return confFile

309
310
311
312
    def generateHeader(self, ra_cen, dec_cen, img_rot, im_type, pointing_ID, exptime=150., timestamp = 1621915200):
        datetime_obs = datetime.utcfromtimestamp(timestamp)
        date_obs = datetime_obs.strftime("%y%m%d")
        time_obs = datetime_obs.strftime("%H%M%S")
Fang Yuedong's avatar
Fang Yuedong committed
313
314
315
316
317
318
        h_prim = generatePrimaryHeader(
            xlen=self.npix_x, 
            ylen=self.npix_y, 
            pointNum = str(pointing_ID),
            ra=ra_cen, 
            dec=dec_cen, 
Fang Yuedong's avatar
Fang Yuedong committed
319
            pixel_scale=self.pix_scale,
Fang Yuedong's avatar
Fang Yuedong committed
320
321
322
            date=date_obs,
            time_obs=time_obs,
            im_type = im_type,
Fang Yuedong's avatar
Fang Yuedong committed
323
324
            exptime=exptime,
            chip_name=str(self.chipID).rjust(2, '0')
Fang Yuedong's avatar
Fang Yuedong committed
325
326
            )
        h_ext = generateExtensionHeader(
Fang Yuedong's avatar
Fang Yuedong committed
327
            chip=self,
Fang Yuedong's avatar
Fang Yuedong committed
328
329
330
            xlen=self.npix_x, 
            ylen=self.npix_y, 
            ra=ra_cen, 
Fang Yuedong's avatar
Fang Yuedong committed
331
            dec=dec_cen,
Fang Yuedong's avatar
Fang Yuedong committed
332
333
334
335
336
            pa=img_rot.deg, 
            gain=self.gain, 
            readout=self.read_noise, 
            dark=self.dark_noise, 
            saturation=90000, 
Fang Yuedong's avatar
Fang Yuedong committed
337
338
339
340
            pixel_scale=self.pix_scale, 
            pixel_size=self.pix_size,
            xcen=self.x_cen,
            ycen=self.y_cen,
341
342
343
344
            extName='SCI',
            timestamp = timestamp,
            exptime = exptime,
            readoutTime = 40.)
Fang Yuedong's avatar
Fang Yuedong committed
345
346
        return h_prim, h_ext

347
    def outputCal(self, img, ra_cen, dec_cen, img_rot, im_type, pointing_ID, output_dir, exptime=150., timestamp = 1621915200):
Fang Yuedong's avatar
Fang Yuedong committed
348
349
350
351
352
353
        h_prim, h_ext = self.generateHeader(
            ra_cen=ra_cen,
            dec_cen=dec_cen,
            img_rot=img_rot,
            im_type=im_type,
            pointing_ID=pointing_ID,
354
355
            exptime=exptime,
            timestamp = timestamp)
Fang Yuedong's avatar
Fang Yuedong committed
356
        hdu1 = fits.PrimaryHDU(header=h_prim)
Fang Yuedong's avatar
Fang Yuedong committed
357
        hdu1.add_checksum()
358
359
        hdu1.header.comments['CHECKSUM'] = 'HDU checksum'
        hdu1.header.comments['DATASUM'] = 'data unit checksum'
Fang Yuedong's avatar
Fang Yuedong committed
360
        hdu2 = fits.ImageHDU(img.array, header=h_ext)
Fang Yuedong's avatar
Fang Yuedong committed
361
        hdu2.add_checksum()
362
363
364
        hdu2.header.comments['XTENSION'] = 'extension type'
        hdu2.header.comments['CHECKSUM'] = 'HDU checksum'
        hdu2.header.comments['DATASUM'] = 'data unit checksum'
Fang Yuedong's avatar
Fang Yuedong committed
365
366
367
368
        hdu1 = fits.HDUList([hdu1, hdu2])
        fname = os.path.join(output_dir, h_prim['FILENAME']+'.fits')
        hdu1.writeto(fname, output_verify='ignore', overwrite=True)

369
    def addEffects(self, config, img, chip_output, filt, ra_cen, dec_cen, img_rot, exptime=150., pointing_ID=0, timestamp_obs=1621915200, pointing_type='MS', sky_map=None, tel=None, logger=None):
Fang Yuedong's avatar
Fang Yuedong committed
370
371
372
373
374
375
        SeedGainNonuni=int(config["random_seeds"]["seed_gainNonUniform"])
        SeedBiasNonuni=int(config["random_seeds"]["seed_biasNonUniform"])
        SeedRnNonuni = int(config["random_seeds"]["seed_rnNonUniform"])
        SeedBadColumns = int(config["random_seeds"]["seed_badcolumns"])
        SeedDefective = int(config["random_seeds"]["seed_defective"])
        SeedCosmicRay = int(config["random_seeds"]["seed_CR"])
Fang Yuedong's avatar
Fang Yuedong committed
376
        fullwell = int(self.full_well)
Fang Yuedong's avatar
Fang Yuedong committed
377
        if config["ins_effects"]["add_hotpixels"] == True:
Fang Yuedong's avatar
Fang Yuedong committed
378
379
380
            BoolHotPix = True
        else:
            BoolHotPix = False
Fang Yuedong's avatar
Fang Yuedong committed
381
        if config["ins_effects"]["add_deadpixels"]== True:
Fang Yuedong's avatar
Fang Yuedong committed
382
383
384
            BoolDeadPix = True
        else:
            BoolDeadPix = False
385
        self.logger = logger
Fang Yuedong's avatar
Fang Yuedong committed
386

387
388
389
390
391
        # Get Poisson noise generator
        seed = int(config["random_seeds"]["seed_poisson"]) + pointing_ID*30 + self.chipID
        rng_poisson = galsim.BaseDeviate(seed)
        poisson_noise = galsim.PoissonNoise(rng_poisson, sky_level=0.)

392
        # Add sky background
Zhang Xin's avatar
Zhang Xin committed
393
        if sky_map is None:
Fang Yuedong's avatar
Fang Yuedong committed
394
            sky_map = filt.getSkyNoise(exptime=exptime)
395
396
397
398
399
400
401
402
            sky_map = sky_map * np.ones_like(img.array)
            sky_map = galsim.Image(array=sky_map)
            # Apply Poisson noise to the sky map
            # (NOTE): only for photometric chips
            # since it utilize the photon shooting
            # to draw stamps
            if self.survey_type == "photometric":
                sky_map.addNoise(poisson_noise)
403
404
405
        elif img.array.shape != sky_map.shape:
            raise ValueError("The shape img and sky_map must be equal.")
        elif tel is not None: # If sky_map is given in flux
Fang Yuedong's avatar
Fang Yuedong committed
406
            sky_map = sky_map * tel.pupil_area * exptime
Fang Yuedong's avatar
Fang Yuedong committed
407
        if config["ins_effects"]["add_back"] == True:
408
409
410
            img += sky_map
        del sky_map

Fang Yuedong's avatar
Fang Yuedong committed
411
        # Apply flat-field large scale structure for one chip
Fang Yuedong's avatar
Fang Yuedong committed
412
        if config["ins_effects"]["flat_fielding"] == True:
413
414
415
416
417
418
419
            if self.logger is not None:
                self.logger.info("  Creating and applying Flat-Fielding")
                msg = str(img.bounds)
                self.logger.info(msg)
            else:
                print("  Creating and applying Flat-Fielding", flush=True)
                print(img.bounds, flush=True)
Fang Yuedong's avatar
Fang Yuedong committed
420
421
            flat_img = effects.MakeFlatSmooth(
                img.bounds, 
Fang Yuedong's avatar
Fang Yuedong committed
422
                int(config["random_seeds"]["seed_flat"]))
Fang Yuedong's avatar
Fang Yuedong committed
423
            flat_normal = flat_img / np.mean(flat_img.array)
424
425
            if self.survey_type == "photometric":
                img *= flat_normal
Fang Yuedong's avatar
Fang Yuedong committed
426
            del flat_normal
Fang Yuedong's avatar
Fang Yuedong committed
427
            if config["output_setting"]["flat_output"] == False:
Fang Yuedong's avatar
Fang Yuedong committed
428
429
430
                del flat_img

        # Apply Shutter-effect for one chip
Fang Yuedong's avatar
Fang Yuedong committed
431
        if config["ins_effects"]["shutter_effect"] == True:
432
433
434
435
            if self.logger is not None:
                self.logger.info("  Apply shutter effect")
            else:
                print("  Apply shutter effect", flush=True)
Fang Yuedong's avatar
Fang Yuedong committed
436
            shuttimg = effects.ShutterEffectArr(img, t_shutter=1.3, dist_bearing=735, dt=1E-3)    # shutter effect normalized image for this chip
437
438
            if self.survey_type == "photometric":
                img *= shuttimg
Fang Yuedong's avatar
Fang Yuedong committed
439
            if config["output_setting"]["shutter_output"] == True:    # output 16-bit shutter effect image with pixel value <=65535
Fang Yuedong's avatar
Fang Yuedong committed
440
441
442
443
444
                shutt_gsimg = galsim.ImageUS(shuttimg*6E4)
                shutt_gsimg.write("%s/ShutterEffect_%s_1.fits" % (chip_output.subdir, self.chipID))
                del shutt_gsimg
            del shuttimg

445
446
447
448
449
        # Add Poisson noise to the resulting images
        # (NOTE): this can only applied to the slitless image
        # since it dose not use photon shooting to draw stamps
        if self.survey_type == "spectroscopic":
            img.addNoise(poisson_noise)
Fang Yuedong's avatar
Fang Yuedong committed
450
451

        # Add cosmic-rays
Fang Yuedong's avatar
Fang Yuedong committed
452
        if config["ins_effects"]["cosmic_ray"] == True and pointing_type=='MS':
453
454
455
456
            if self.logger is not None:
                self.logger.info(("  Adding Cosmic-Ray"))
            else:
                print("  Adding Cosmic-Ray", flush=True)
Fang Yuedong's avatar
Fang Yuedong committed
457
            cr_map, cr_event_num = effects.produceCR_Map(
Fang Yuedong's avatar
Fang Yuedong committed
458
                xLen=self.npix_x, yLen=self.npix_y, 
Fang Yuedong's avatar
Fang Yuedong committed
459
460
                exTime=exptime+0.5*self.readout_time, 
                cr_pixelRatio=0.003*(exptime+0.5*self.readout_time)/600.,
Fang Yuedong's avatar
Fang Yuedong committed
461
462
                gain=self.gain, 
                attachedSizes=self.attachedSizes,
Fang Yuedong's avatar
Fang Yuedong committed
463
                seed=SeedCosmicRay+pointing_ID*30+self.chipID)   # seed: obj-imaging:+0; bias:+1; dark:+2; flat:+3;
Fang Yuedong's avatar
Fang Yuedong committed
464
465
466
467
            img += cr_map
            cr_map[cr_map > 65535] = 65535
            cr_map[cr_map < 0] = 0
            crmap_gsimg = galsim.Image(cr_map, dtype=np.uint16)
Fang Yuedong's avatar
Fang Yuedong committed
468
            del cr_map
Fang Yuedong's avatar
Fang Yuedong committed
469
            # crmap_gsimg.write("%s/CosmicRay_%s_1.fits" % (chip_output.subdir, self.chipID))
Fang Yuedong's avatar
Fang Yuedong committed
470
            # crmap_gsimg.write("%s/CosmicRay_%s.fits" % (chip_output.subdir, self.chipID))
471
472
473
            # datetime_obs = datetime.utcfromtimestamp(timestamp_obs)
            # date_obs = datetime_obs.strftime("%y%m%d")
            # time_obs = datetime_obs.strftime("%H%M%S")
Fang Yuedong's avatar
Fang Yuedong committed
474
475
476
477
478
479
480
481
            self.outputCal(
                img=crmap_gsimg,
                ra_cen=ra_cen,
                dec_cen=dec_cen,
                img_rot=img_rot,
                im_type='CRS',
                pointing_ID=pointing_ID,
                output_dir=chip_output.subdir,
482
483
                exptime=exptime,
                timestamp=timestamp_obs)
Fang Yuedong's avatar
Fang Yuedong committed
484
485
            del crmap_gsimg

486
        # Apply PRNU effect and output PRNU flat file:
Fang Yuedong's avatar
Fang Yuedong committed
487
        if config["ins_effects"]["prnu_effect"] == True:
488
489
490
491
            if self.logger is not None:
                self.logger.info("  Applying PRNU effect")
            else:
                print("  Applying PRNU effect", flush=True)
492
493
494
495
            prnu_img = effects.PRNU_Img(
                xsize=self.npix_x, 
                ysize=self.npix_y, 
                sigma=0.01, 
Fang Yuedong's avatar
Fang Yuedong committed
496
                seed=int(config["random_seeds"]["seed_prnu"]+self.chipID))
497
            img *= prnu_img
Fang Yuedong's avatar
Fang Yuedong committed
498
            if config["output_setting"]["prnu_output"] == True:
499
                prnu_img.write("%s/FlatImg_PRNU_%s.fits" % (chip_output.subdir,self.chipID))
Fang Yuedong's avatar
Fang Yuedong committed
500
            if config["output_setting"]["flat_output"] == False:
501
502
503
                del prnu_img

        # Add dark current
Fang Yuedong's avatar
Fang Yuedong committed
504
        if config["ins_effects"]["add_dark"] == True:
Fang Yuedong's avatar
Fang Yuedong committed
505
            dark_noise = galsim.DeviateNoise(galsim.PoissonDeviate(rng_poisson, self.dark_noise*(exptime+0.5*self.readout_time)))
506
507
508
509
510
511
512
513
            img.addNoise(dark_noise)

        # Add Hot Pixels or/and Dead Pixels
        rgbadpix = Generator(PCG64(int(SeedDefective+self.chipID)))
        badfraction = 5E-5*(rgbadpix.random()*0.5+0.7)
        img = effects.DefectivePixels(img, IfHotPix=BoolHotPix, IfDeadPix=BoolDeadPix, fraction=badfraction, seed=SeedDefective+self.chipID, biaslevel=0)

        # Apply Bad lines 
Fang Yuedong's avatar
Fang Yuedong committed
514
        if config["ins_effects"]["add_badcolumns"] == True:
515
            img = effects.BadColumns(img, seed=SeedBadColumns, chipid=self.chipID, logger=self.logger)
516

Fang Yuedong's avatar
Fang Yuedong committed
517
        # Add Bias level
Fang Yuedong's avatar
Fang Yuedong committed
518
        if config["ins_effects"]["add_bias"] == True:
519
520
521
522
            if self.logger is not None:
                self.logger.info("  Adding Bias level and 16-channel non-uniformity")
            else:
                print("  Adding Bias level and 16-channel non-uniformity")
523
524
            if config["ins_effects"]["bias_16channel"] == True:
                img = effects.AddBiasNonUniform16(img, 
Fang Yuedong's avatar
Fang Yuedong committed
525
                    bias_level=float(self.bias_level), 
526
527
528
529
530
                    nsecy = 2, nsecx=8, 
                    seed=SeedBiasNonuni+self.chipID,
                    logger=self.logger)
            elif config["ins_effects"]["bias_16channel"] == False:
                img += self.bias_level
Fang Yuedong's avatar
Fang Yuedong committed
531

532
        # Apply Nonlinearity on the chip image
Fang Yuedong's avatar
Fang Yuedong committed
533
        if config["ins_effects"]["non_linear"] == True:
534
535
536
537
            if self.logger is not None:
                self.logger.info("  Applying Non-Linearity on the chip image")
            else:
                print("  Applying Non-Linearity on the chip image", flush=True)
538
539
540
            img = effects.NonLinearity(GSImage=img, beta1=5.e-7, beta2=0)

        # Apply CCD Saturation & Blooming
Fang Yuedong's avatar
Fang Yuedong committed
541
        if config["ins_effects"]["saturbloom"] == True:
542
543
544
545
            if self.logger is not None:
                self.logger.info("  Applying CCD Saturation & Blooming")
            else:
                print("  Applying CCD Saturation & Blooming")
546
547
548
            img = effects.SaturBloom(GSImage=img, nsect_x=1, nsect_y=1, fullwell=fullwell)

        # Apply CTE Effect
Fang Yuedong's avatar
Fang Yuedong committed
549
        if config["ins_effects"]["cte_trail"] == True:
550
551
552
553
            if self.logger is not None:
                self.logger.info("  Apply CTE Effect")
            else:
                print("  Apply CTE Effect")
554
555
556
            img = effects.CTE_Effect(GSImage=img, threshold=27)

        # Add Read-out Noise
Fang Yuedong's avatar
Fang Yuedong committed
557
558
        if config["ins_effects"]["add_readout"] == True:
            seed = int(config["random_seeds"]["seed_readout"]) + pointing_ID*30 + self.chipID
559
560
561
562
563
            rng_readout = galsim.BaseDeviate(seed)
            readout_noise = galsim.GaussianNoise(rng=rng_readout, sigma=self.read_noise)
            img.addNoise(readout_noise)

        # Apply Gain & Quantization
564
565
566
567
        if self.logger is not None:
            self.logger.info("  Applying Gain (and 16 channel non-uniformity) & Quantization")
        else:
            print("  Applying Gain (and 16 channel non-uniformity) & Quantization", flush=True)
568
        if config["ins_effects"]["gain_16channel"] == True:
569
            img, self.gain_channel = effects.ApplyGainNonUniform16(
570
571
572
573
574
575
576
                img, gain=self.gain, 
                nsecy = 2, nsecx=8, 
                seed=SeedGainNonuni+self.chipID,
                logger=self.logger)
        elif config["ins_effects"]["gain_16channel"] == False:
            img /= self.gain
            
577
578
579
580
581
582
583
        img.array[img.array > 65535] = 65535
        img.replaceNegative(replace_value=0)
        img.quantize()

        ######################################################################################
        # Output images for calibration pointing
        ######################################################################################
Fang Yuedong's avatar
Fang Yuedong committed
584
        # Bias output
585
        if config["ins_effects"]["add_bias"] == True and config["output_setting"]["bias_output"] == True and pointing_type=='CAL':
586
587
588
589
            if self.logger is not None:
                self.logger.info("  Output N frame Bias files")
            else:
                print("  Output N frame Bias files", flush=True)
Fang Yuedong's avatar
Fang Yuedong committed
590
            NBias = int(config["output_setting"]["NBias"])
Fang Yuedong's avatar
Fang Yuedong committed
591
592
593
            for i in range(NBias):
                BiasCombImg, BiasTag = effects.MakeBiasNcomb(
                    self.npix_x, self.npix_y, 
Fang Yuedong's avatar
Fang Yuedong committed
594
                    bias_level=float(self.bias_level), 
Fang Yuedong's avatar
Fang Yuedong committed
595
                    ncombine=1, read_noise=self.read_noise, gain=1,
596
597
                    seed=SeedBiasNonuni+self.chipID,
                    logger=self.logger)
598
                # Readout noise for Biases is not generated with random seeds. So readout noise for bias images can't be reproduced.
Fang Yuedong's avatar
Fang Yuedong committed
599
600
601
                if config["ins_effects"]["cosmic_ray"] == True:
                    if config["ins_effects"]["cray_differ"] == True:
                        cr_map, cr_event_num = effects.produceCR_Map(
Fang Yuedong's avatar
Fang Yuedong committed
602
603
                            xLen=self.npix_x, yLen=self.npix_y, 
                            exTime=0.01, 
Fang Yuedong's avatar
Fang Yuedong committed
604
                            cr_pixelRatio=0.003*(0.01+0.5*self.readout_time)/150., 
Fang Yuedong's avatar
Fang Yuedong committed
605
606
607
608
609
610
611
                            gain=self.gain, 
                            attachedSizes=self.attachedSizes,
                            seed=SeedCosmicRay+pointing_ID*30+self.chipID+1)
                            # seed: obj-imaging:+0; bias:+1; dark:+2; flat:+3;
                    BiasCombImg += cr_map
                    del cr_map

Fang Yuedong's avatar
Fang Yuedong committed
612
                # Non-Linearity for Bias
Fang Yuedong's avatar
Fang Yuedong committed
613
                if config["ins_effects"]["non_linear"] == True:
614
615
616
617
                    if self.logger is not None:
                        self.logger.info("  Applying Non-Linearity on the Bias image")
                    else:
                        print("  Applying Non-Linearity on the Bias image", flush=True)
618
619
620
                    BiasCombImg = effects.NonLinearity(GSImage=BiasCombImg, beta1=5.e-7, beta2=0)

                # Apply Bad lines 
Fang Yuedong's avatar
Fang Yuedong committed
621
                if config["ins_effects"]["add_badcolumns"] == True:
Fang Yuedong's avatar
Fang Yuedong committed
622
                    BiasCombImg = effects.BadColumns(BiasCombImg-float(self.bias_level)+5, seed=SeedBadColumns, chipid=self.chipID, logger=self.logger) + float(self.bias_level)-5
Fang Yuedong's avatar
Fang Yuedong committed
623

624
                BiasCombImg, self.gain_channel = effects.ApplyGainNonUniform16(BiasCombImg, gain=self.gain,
Fang Yuedong's avatar
Fang Yuedong committed
625
                    nsecy = 2, nsecx=8, 
626
627
                    seed=SeedGainNonuni+self.chipID,
                    logger=self.logger)
Fang Yuedong's avatar
Fang Yuedong committed
628
629
                # BiasCombImg = effects.AddOverscan(
                #     BiasCombImg, 
Fang Yuedong's avatar
Fang Yuedong committed
630
                #     overscan=float(config["ins_effects"]["bias_level"])-2, gain=self.gain, 
Fang Yuedong's avatar
Fang Yuedong committed
631
632
633
634
                #     widthl=27, widthr=27, widtht=8, widthb=8)
                BiasCombImg.replaceNegative(replace_value=0)
                BiasCombImg.quantize()
                BiasCombImg = galsim.ImageUS(BiasCombImg)
Fang Yuedong's avatar
Fang Yuedong committed
635
                # BiasCombImg.write("%s/BiasImg_%s_%s_%s.fits" % (chip_output.subdir, BiasTag, self.chipID, i+1))
636
637
638
                # datetime_obs = datetime.utcfromtimestamp(timestamp_obs)
                # date_obs = datetime_obs.strftime("%y%m%d")
                # time_obs = datetime_obs.strftime("%H%M%S")
639
                timestamp_obs += 10 * 60
Fang Yuedong's avatar
Fang Yuedong committed
640
641
642
643
644
                self.outputCal(
                    img=BiasCombImg,
                    ra_cen=ra_cen,
                    dec_cen=dec_cen,
                    img_rot=img_rot,
Xin Zhang's avatar
Xin Zhang committed
645
                    im_type='BIAS',
Fang Yuedong's avatar
Fang Yuedong committed
646
647
                    pointing_ID=pointing_ID,
                    output_dir=chip_output.subdir,
648
649
                    exptime=0.0,
                    timestamp=timestamp_obs)
Fang Yuedong's avatar
Fang Yuedong committed
650
651
652
            del BiasCombImg

        # Export combined (ncombine, Vignetting + PRNU) & single vignetting flat-field file
653
        if config["ins_effects"]["flat_fielding"] == True and config["output_setting"]["flat_output"] == True and pointing_type=='CAL':
654
655
656
657
            if self.logger is not None:
                self.logger.info("  Output N frame Flat-Field files")
            else:
                print("  Output N frame Flat-Field files", flush=True)
Fang Yuedong's avatar
Fang Yuedong committed
658
            NFlat = int(config["output_setting"]["NFlat"])
Fang Yuedong's avatar
Fang Yuedong committed
659
            if config["ins_effects"]["add_bias"] == True:
Fang Yuedong's avatar
Fang Yuedong committed
660
661
                biaslevel = self.bias_level
                overscan = biaslevel-2
662
            elif config["ins_effects"]["add_bias"] == False:
Fang Yuedong's avatar
Fang Yuedong committed
663
664
                biaslevel = 0
                overscan = 0
665
            darklevel = self.dark_noise*(self.flat_exptime+0.5*self.readout_time)
Fang Yuedong's avatar
Fang Yuedong committed
666
667
668
669
670
671
672
673
            for i in range(NFlat):
                FlatSingle = flat_img * prnu_img + darklevel
                FlatCombImg, FlatTag = effects.MakeFlatNcomb(
                    flat_single_image=FlatSingle, 
                    ncombine=1, 
                    read_noise=self.read_noise,
                    gain=1, 
                    overscan=overscan, 
674
                    biaslevel=0,
675
676
                    seed_bias=SeedDefective+self.chipID,
                    logger=self.logger
Fang Yuedong's avatar
Fang Yuedong committed
677
                    )
Fang Yuedong's avatar
Fang Yuedong committed
678
679
680
                if config["ins_effects"]["cosmic_ray"] == True:
                    if config["ins_effects"]["cray_differ"] == True:
                        cr_map, cr_event_num = effects.produceCR_Map(
Fang Yuedong's avatar
Fang Yuedong committed
681
                            xLen=self.npix_x, yLen=self.npix_y, 
682
                            exTime=self.flat_exptime+0.5*self.readout_time, 
Fang Yuedong's avatar
Fang Yuedong committed
683
                            cr_pixelRatio=0.003*(self.flat_exptime+0.5*self.readout_time)/150., 
Fang Yuedong's avatar
Fang Yuedong committed
684
685
686
687
                            gain=self.gain, 
                            attachedSizes=self.attachedSizes,
                            seed=SeedCosmicRay+pointing_ID*30+self.chipID+3)
                            # seed: obj-imaging:+0; bias:+1; dark:+2; flat:+3;
Fang Yuedong's avatar
Fang Yuedong committed
688
                    FlatCombImg += cr_map
Fang Yuedong's avatar
Fang Yuedong committed
689
                    del cr_map
Fang Yuedong's avatar
Fang Yuedong committed
690

Fang Yuedong's avatar
Fang Yuedong committed
691
                if config["ins_effects"]["non_linear"] == True:
692
693
694
695
                    if self.logger is not None:
                        self.logger.info("  Applying Non-Linearity on the Flat image")
                    else:
                        print("  Applying Non-Linearity on the Flat image", flush=True)
696
                    FlatCombImg = effects.NonLinearity(GSImage=FlatCombImg, beta1=5.e-7, beta2=0)
Fang Yuedong's avatar
Fang Yuedong committed
697

Fang Yuedong's avatar
Fang Yuedong committed
698
                if config["ins_effects"]["cte_trail"] == True:
Fang Yuedong's avatar
Fang Yuedong committed
699
700
                    FlatCombImg = effects.CTE_Effect(GSImage=FlatCombImg, threshold=3)

701
702
703
704
705
                # Add Hot Pixels or/and Dead Pixels
                rgbadpix = Generator(PCG64(int(SeedDefective+self.chipID)))
                badfraction = 5E-5*(rgbadpix.random()*0.5+0.7)
                FlatCombImg = effects.DefectivePixels(FlatCombImg, IfHotPix=BoolHotPix, IfDeadPix=BoolDeadPix, fraction=badfraction, seed=SeedDefective+self.chipID, biaslevel=0)

Fang Yuedong's avatar
Fang Yuedong committed
706
                # Apply Bad lines 
Fang Yuedong's avatar
Fang Yuedong committed
707
                if config["ins_effects"]["add_badcolumns"] == True:
708
                    FlatCombImg = effects.BadColumns(FlatCombImg, seed=SeedBadColumns, chipid=self.chipID, logger=self.logger)
Fang Yuedong's avatar
Fang Yuedong committed
709

710
                # Add Bias level
Fang Yuedong's avatar
Fang Yuedong committed
711
                if config["ins_effects"]["add_bias"] == True:
712
713
714
715
                    if self.logger is not None:
                        self.logger.info("  Adding Bias level and 16-channel non-uniformity")
                    else:
                        print("  Adding Bias level and 16-channel non-uniformity")
Fang Yuedong's avatar
Fang Yuedong committed
716
                    # img += float(config["ins_effects"]["bias_level"])
717
718
719
                    FlatCombImg = effects.AddBiasNonUniform16(FlatCombImg, 
                        bias_level=biaslevel, 
                        nsecy = 2, nsecx=8, 
720
721
                        seed=SeedBiasNonuni+self.chipID,
                        logger=self.logger)
722
723
                
                # Add Read-out Noise
Fang Yuedong's avatar
Fang Yuedong committed
724
                if config["ins_effects"]["add_readout"] == True:
725
                    seed = int(config["random_seeds"]["seed_readout"]) + pointing_ID*30 + self.chipID + 3
726
727
728
                    rng_readout = galsim.BaseDeviate(seed)
                    readout_noise = galsim.GaussianNoise(rng=rng_readout, sigma=self.read_noise)
                    FlatCombImg.addNoise(readout_noise)
Fang Yuedong's avatar
Fang Yuedong committed
729

730
                FlatCombImg, self.gain_channel = effects.ApplyGainNonUniform16(FlatCombImg, gain=self.gain,
Fang Yuedong's avatar
Fang Yuedong committed
731
                    nsecy = 2, nsecx=8, 
732
733
                    seed=SeedGainNonuni+self.chipID,
                    logger=self.logger)
Fang Yuedong's avatar
Fang Yuedong committed
734
735
736
737
                # FlatCombImg = effects.AddOverscan(FlatCombImg, overscan=overscan, gain=self.gain, widthl=27, widthr=27, widtht=8, widthb=8)
                FlatCombImg.replaceNegative(replace_value=0)
                FlatCombImg.quantize()
                FlatCombImg = galsim.ImageUS(FlatCombImg)
Fang Yuedong's avatar
Fang Yuedong committed
738
                # FlatCombImg.write("%s/FlatImg_%s_%s_%s.fits" % (chip_output.subdir, FlatTag, self.chipID, i+1))
739
740
741
                # datetime_obs = datetime.utcfromtimestamp(timestamp_obs)
                # date_obs = datetime_obs.strftime("%y%m%d")
                # time_obs = datetime_obs.strftime("%H%M%S")
742
                timestamp_obs += 10 * 60
Fang Yuedong's avatar
Fang Yuedong committed
743
744
745
746
747
                self.outputCal(
                    img=FlatCombImg,
                    ra_cen=ra_cen,
                    dec_cen=dec_cen,
                    img_rot=img_rot,
Xin Zhang's avatar
Xin Zhang committed
748
                    im_type='FLAT',
Fang Yuedong's avatar
Fang Yuedong committed
749
750
                    pointing_ID=pointing_ID,
                    output_dir=chip_output.subdir,
751
752
                    exptime=self.flat_exptime,
                    timestamp=timestamp_obs)
Fang Yuedong's avatar
Fang Yuedong committed
753

Fang Yuedong's avatar
Fang Yuedong committed
754
755
756
757
758
759
760
            del FlatCombImg, FlatSingle, prnu_img
            # flat_img.replaceNegative(replace_value=0)
            # flat_img.quantize()
            # galsim.ImageUS(flat_img).write("%s/FlatImg_Vignette_%s.fits" % (chip_output.subdir, self.chipID))
            del flat_img

        # Export Dark current images
761
        if config["ins_effects"]["add_dark"] == True and config["output_setting"]["dark_output"] == True and pointing_type=='CAL':
762
763
764
765
            if self.logger is not None:
                self.logger.info("  Output N frame Dark Current files")
            else:
                print("  Output N frame Dark Current files", flush=True)
Fang Yuedong's avatar
Fang Yuedong committed
766
            NDark = int(config["output_setting"]["NDark"])
Fang Yuedong's avatar
Fang Yuedong committed
767
            if config["ins_effects"]["add_bias"] == True:
Fang Yuedong's avatar
Fang Yuedong committed
768
769
                biaslevel = self.bias_level
                overscan = biaslevel-2
770
            elif config["ins_effects"]["add_bias"] == False:
Fang Yuedong's avatar
Fang Yuedong committed
771
772
773
774
775
                biaslevel = 0
                overscan = 0
            for i in range(NDark):
                DarkCombImg, DarkTag = effects.MakeDarkNcomb(
                    self.npix_x, self.npix_y, 
776
                    overscan=overscan, bias_level=0, darkpsec=0.02, exptime=self.dark_exptime+0.5*self.readout_time,
Fang Yuedong's avatar
Fang Yuedong committed
777
                    ncombine=1, read_noise=self.read_noise, 
778
779
                    gain=1, seed_bias=SeedBiasNonuni+self.chipID,
                    logger=self.logger)
Fang Yuedong's avatar
Fang Yuedong committed
780
781
782
                if config["ins_effects"]["cosmic_ray"] == True:
                    if config["ins_effects"]["cray_differ"] == True:
                        cr_map, cr_event_num = effects.produceCR_Map(
Fang Yuedong's avatar
Fang Yuedong committed
783
                            xLen=self.npix_x, yLen=self.npix_y, 
784
                            exTime=self.dark_exptime+0.5*self.readout_time, 
Fang Yuedong's avatar
Fang Yuedong committed
785
                            cr_pixelRatio=0.003*(self.dark_exptime+0.5*self.readout_time)/150., 
Fang Yuedong's avatar
Fang Yuedong committed
786
787
788
789
                            gain=self.gain, 
                            attachedSizes=self.attachedSizes,
                            seed=SeedCosmicRay+pointing_ID*30+self.chipID+2)
                            # seed: obj-imaging:+0; bias:+1; dark:+2; flat:+3;
Fang Yuedong's avatar
Fang Yuedong committed
790
                    DarkCombImg += cr_map
Fang Yuedong's avatar
Fang Yuedong committed
791
792
793
794
                    cr_map[cr_map > 65535] = 65535
                    cr_map[cr_map < 0] = 0
                    crmap_gsimg = galsim.Image(cr_map, dtype=np.uint16)
                    del cr_map
795
796
797
                    # datetime_obs = datetime.utcfromtimestamp(timestamp_obs)
                    # date_obs = datetime_obs.strftime("%y%m%d")
                    # time_obs = datetime_obs.strftime("%H%M%S")
Fang Yuedong's avatar
Fang Yuedong committed
798
799
800
801
802
803
804
805
                    self.outputCal(
                        img=crmap_gsimg,
                        ra_cen=ra_cen,
                        dec_cen=dec_cen,
                        img_rot=img_rot,
                        im_type='CRD',
                        pointing_ID=pointing_ID,
                        output_dir=chip_output.subdir,
806
807
                        exptime=self.dark_exptime,
                        timestamp=timestamp_obs)
Fang Yuedong's avatar
Fang Yuedong committed
808
                    del crmap_gsimg
Fang Yuedong's avatar
Fang Yuedong committed
809
810

                # Non-Linearity for Dark
Fang Yuedong's avatar
Fang Yuedong committed
811
                if config["ins_effects"]["non_linear"] == True:
812
813
814
815
                    if self.logger is not None:
                        self.logger.info("  Applying Non-Linearity on the Dark image")
                    else:
                        print("  Applying Non-Linearity on the Dark image", flush=True)
816
                    DarkCombImg = effects.NonLinearity(GSImage=DarkCombImg, beta1=5.e-7, beta2=0)
Fang Yuedong's avatar
Fang Yuedong committed
817

Fang Yuedong's avatar
Fang Yuedong committed
818
                if config["ins_effects"]["cte_trail"] == True:
Fang Yuedong's avatar
Fang Yuedong committed
819
820
                    DarkCombImg = effects.CTE_Effect(GSImage=DarkCombImg, threshold=3)

821
822
823
824
825
                # Add Hot Pixels or/and Dead Pixels
                rgbadpix = Generator(PCG64(int(SeedDefective+self.chipID)))
                badfraction = 5E-5*(rgbadpix.random()*0.5+0.7)
                DarkCombImg = effects.DefectivePixels(DarkCombImg, IfHotPix=BoolHotPix, IfDeadPix=BoolDeadPix, fraction=badfraction, seed=SeedDefective+self.chipID, biaslevel=0)

Fang Yuedong's avatar
Fang Yuedong committed
826
                # Apply Bad lines 
Fang Yuedong's avatar
Fang Yuedong committed
827
                if config["ins_effects"]["add_badcolumns"] == True:
828
                    DarkCombImg = effects.BadColumns(DarkCombImg, seed=SeedBadColumns, chipid=self.chipID, logger=self.logger)
Fang Yuedong's avatar
Fang Yuedong committed
829

830
                # Add Bias level
Fang Yuedong's avatar
Fang Yuedong committed
831
                if config["ins_effects"]["add_bias"] == True:
832
833
834
835
                    if self.logger is not None:
                        self.logger.info("  Adding Bias level and 16-channel non-uniformity")
                    else:
                        print("  Adding Bias level and 16-channel non-uniformity")
Fang Yuedong's avatar
Fang Yuedong committed
836
                    # img += float(config["ins_effects"]["bias_level"])
837
838
839
                    DarkCombImg = effects.AddBiasNonUniform16(DarkCombImg, 
                        bias_level=biaslevel, 
                        nsecy = 2, nsecx=8, 
840
841
                        seed=SeedBiasNonuni+self.chipID,
                        logger=self.logger)
842
843

                # Add Read-out Noise
Fang Yuedong's avatar
Fang Yuedong committed
844
                if config["ins_effects"]["add_readout"] == True:
845
                    seed = int(config["random_seeds"]["seed_readout"]) + pointing_ID*30 + self.chipID + 2
846
847
848
                    rng_readout = galsim.BaseDeviate(seed)
                    readout_noise = galsim.GaussianNoise(rng=rng_readout, sigma=self.read_noise)
                    DarkCombImg.addNoise(readout_noise)
Fang Yuedong's avatar
Fang Yuedong committed
849

850
                DarkCombImg, self.gain_channel = effects.ApplyGainNonUniform16(
Fang Yuedong's avatar
Fang Yuedong committed
851
852
                    DarkCombImg, gain=self.gain, 
                    nsecy = 2, nsecx=8, 
853
854
                    seed=SeedGainNonuni+self.chipID,
                    logger=self.logger)
Fang Yuedong's avatar
Fang Yuedong committed
855
856
857
858
859
860
861
                # DarkCombImg = effects.AddOverscan(
                #     DarkCombImg, 
                #     overscan=overscan, gain=self.gain, 
                #     widthl=27, widthr=27, widtht=8, widthb=8)
                DarkCombImg.replaceNegative(replace_value=0)
                DarkCombImg.quantize()
                DarkCombImg = galsim.ImageUS(DarkCombImg)
Fang Yuedong's avatar
Fang Yuedong committed
862
                # DarkCombImg.write("%s/DarkImg_%s_%s_%s.fits" % (chip_output.subdir, DarkTag, self.chipID, i+1))
863
864
865
                # datetime_obs = datetime.utcfromtimestamp(timestamp_obs)
                # date_obs = datetime_obs.strftime("%y%m%d")
                # time_obs = datetime_obs.strftime("%H%M%S")
866
                timestamp_obs += 10 * 60
Fang Yuedong's avatar
Fang Yuedong committed
867
868
869
870
871
                self.outputCal(
                    img=DarkCombImg,
                    ra_cen=ra_cen,
                    dec_cen=dec_cen,
                    img_rot=img_rot,
Xin Zhang's avatar
Xin Zhang committed
872
                    im_type='DARK',
Fang Yuedong's avatar
Fang Yuedong committed
873
874
                    pointing_ID=pointing_ID,
                    output_dir=chip_output.subdir,
875
876
                    exptime=self.dark_exptime,
                    timestamp = timestamp_obs)
Fang Yuedong's avatar
Fang Yuedong committed
877
878
879
880
            del DarkCombImg
        # img = galsim.ImageUS(img)

        # # 16 output channel, with each a single image file
Fang Yuedong's avatar
Fang Yuedong committed
881
        # if config["ins_effects"]["readout16"] == True:
Fang Yuedong's avatar
Fang Yuedong committed
882
883
884
885
886
887
888
889
890
891
892
893
        #     print("  16 Output Channel simulation")
        #     for coli in [0, 1]:
        #         for rowi in range(8):
        #             sub_img = effects.readout16(
        #                 GSImage=img, 
        #                 rowi=rowi, 
        #                 coli=coli, 
        #                 overscan_value=self.overscan)
        #             rowcoltag = str(rowi) + str(coli)
        #             img_name_root = chip_output.img_name.split(".")[0]
        #             sub_img.write("%s/%s_%s.fits" % (chip_output.subdir, img_name_root, rowcoltag))
        #     del sub_img
Zhang Xin's avatar
Zhang Xin committed
894
        return img
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911

    def loadSLSFLATCUBE(self, flat_fn='flat_cube.fits'):
        from astropy.io import fits
        try:
            with pkg_resources.files('ObservationSim.Instrument.data').joinpath(flat_fn) as data_path:
                flat_fits = fits.open(data_path, ignore_missing_simple=True)
        except AttributeError:
            with pkg_resources.path('ObservationSim.Instrument.data', flat_fn) as data_path:
                flat_fits = fits.open(data_path, ignore_missing_simple=True)

        fl = len(flat_fits)
        fl_sh = flat_fits[0].data.shape
        assert fl == 4, 'FLAT Field Cube is Not 4 layess!!!!!!!'
        self.flat_cube = np.zeros([fl, fl_sh[0], fl_sh[1]])
        for i in np.arange(0, fl, 1):
            self.flat_cube[i] = flat_fits[i].data