MockObject.py 16.1 KB
Newer Older
Fang Yuedong's avatar
Fang Yuedong committed
1
2
3
import galsim
import numpy as np
import astropy.constants as cons
Fang Yuedong's avatar
Fang Yuedong committed
4
from astropy import wcs
Fang Yuedong's avatar
Fang Yuedong committed
5
from astropy.table import Table
6

7
from ObservationSim.MockObject._util import magToFlux, VC_A, convolveGaussXorders
8
9
from ObservationSim.MockObject._util import integrate_sed_bandpass, getNormFactorForSpecWithABMAG, getObservedSED, \
    getABMAG
10
from ObservationSim.MockObject.SpecDisperser import SpecDisperser
Fang Yuedong's avatar
Fang Yuedong committed
11

12

Fang Yuedong's avatar
Fang Yuedong committed
13
class MockObject(object):
14
    def __init__(self, param, logger=None):
Fang Yuedong's avatar
Fang Yuedong committed
15
        self.param = param
Fang Yuedong's avatar
Fang Yuedong committed
16
17
18
        for key in self.param:
            setattr(self, key, self.param[key])
        
Fang Yuedong's avatar
Fang Yuedong committed
19
20
21
22
23
24
        if self.param["star"] == 0:
            self.type = "galaxy"
        elif self.param["star"] == 1:
            self.type = "star"
        elif self.param["star"] == 2:
            self.type = "quasar"
Fang Yuedong's avatar
Fang Yuedong committed
25
26
27
28
        ###mock_stamp_START
        elif self.param["star"] == 3:
            self.type = "stamp"
        ###mock_stamp_END
29
        self.sed = None
30
        self.fd_shear = None
31
        # Place holder for outputs
32
        self.additional_output_str = ""
33
34
        self.logger = logger

Fang Yuedong's avatar
Fang Yuedong committed
35
36
37
    def getMagFilter(self, filt):
        if filt.filter_type in ["GI", "GV", "GU"]:
            return self.param["mag_use_normal"]
Fang Yuedong's avatar
Fang Yuedong committed
38
        return self.param["mag_%s" % filt.filter_type.lower()]
39

40
    def getFluxFilter(self, filt):
Fang Yuedong's avatar
Fang Yuedong committed
41
        return self.param["flux_%s" % filt.filter_type.lower()]
Fang Yuedong's avatar
Fang Yuedong committed
42
43

    def getNumPhotons(self, flux, tel, exptime=150.):
44
        pupil_area = tel.pupil_area * (100.) ** 2  # m^2 to cm^2
Fang Yuedong's avatar
Fang Yuedong committed
45
46
47
        return flux * pupil_area * exptime

    def getElectronFluxFilt(self, filt, tel, exptime=150.):
48
49
50
51
52
53
        # photonEnergy = filt.getPhotonE()
        # flux = magToFlux(self.getMagFilter(filt))
        # factor = 1.0e4 * flux/photonEnergy * VC_A * (1.0/filt.blue_limit - 1.0/filt.red_limit)
        # return factor * filt.efficiency * tel.pupil_area * exptime
        flux = self.getFluxFilter(filt)
        return flux * tel.pupil_area * exptime
Fang Yuedong's avatar
Fang Yuedong committed
54
55
56
57

    def getPosWorld(self):
        ra = self.param["ra"]
        dec = self.param["dec"]
58
        return galsim.CelestialCoord(ra=ra * galsim.degrees, dec=dec * galsim.degrees)
Fang Yuedong's avatar
Fang Yuedong committed
59

60
    def getPosImg_Offset_WCS(self, img, fdmodel=None, chip=None, verbose=True, chip_wcs=None, img_header=None):
Fang Yuedong's avatar
Fang Yuedong committed
61
62
        self.posImg = img.wcs.toImage(self.getPosWorld())
        self.localWCS = img.wcs.local(self.posImg)
63
        # Apply field distortion model
Fang Yuedong's avatar
Fang Yuedong committed
64
65
66
67
        if (fdmodel is not None) and (chip is not None):
            if verbose:
                print("\n")
                print("Before field distortion:\n")
68
                print("x = %.2f, y = %.2f\n" % (self.posImg.x, self.posImg.y), flush=True)
Fang Yuedong's avatar
Fang Yuedong committed
69
            self.posImg, self.fd_shear = fdmodel.get_distorted(chip=chip, pos_img=self.posImg)
Fang Yuedong's avatar
Fang Yuedong committed
70
71
            if verbose:
                print("After field distortion:\n")
72
                print("x = %.2f, y = %.2f\n" % (self.posImg.x, self.posImg.y), flush=True)
73
        
Fang Yuedong's avatar
Fang Yuedong committed
74
75
76
77
78
79
        x, y = self.posImg.x + 0.5, self.posImg.y + 0.5
        self.x_nominal = int(np.floor(x + 0.5))
        self.y_nominal = int(np.floor(y + 0.5))
        dx = x - self.x_nominal
        dy = y - self.y_nominal
        self.offset = galsim.PositionD(dx, dy)
80
        
81
        # Deal with chip rotation
82
        if chip_wcs is not None:
83
            self.chip_wcs = chip_wcs
84
        elif img_header is not None:
85
            self.chip_wcs = galsim.FitsWCS(header=img_header)
xin's avatar
xin committed
86
        else:
87
            self.chip_wcs = None
xin's avatar
xin committed
88

89
        return self.posImg, self.offset, self.localWCS, self.chip_wcs, self.fd_shear
xin's avatar
xin committed
90

91
    def getRealPos(self, img, global_x=0., global_y=0., img_real_wcs=None):
xin's avatar
xin committed
92
93
94
95
96
        img_global_pos = galsim.PositionD(global_x, global_y)
        cel_pos = img.wcs.toWorld(img_global_pos)
        realPos = img_real_wcs.toImage(cel_pos)
        return realPos

97
    def drawObj_multiband(self, tel, pos_img, psf_model, bandpass_list, filt, chip, nphotons_tot=None, g1=0, g2=0,
Fang Yuedong's avatar
Fang Yuedong committed
98
                          exptime=150., fd_shear=None):
Fang Yuedong's avatar
Fang Yuedong committed
99
100
101
102
103
104
105
106
        if nphotons_tot == None:
            nphotons_tot = self.getElectronFluxFilt(filt, tel, exptime)
        # print("nphotons_tot = ", nphotons_tot)

        try:
            full = integrate_sed_bandpass(sed=self.sed, bandpass=filt.bandpass_full)
        except Exception as e:
            print(e)
107
108
            if self.logger:
                self.logger.error(e)
Fang Yuedong's avatar
Fang Yuedong committed
109
            return 2, None
Fang Yuedong's avatar
Fang Yuedong committed
110

111
        # Set Galsim Parameters
Fang Yuedong's avatar
Fang Yuedong committed
112
113
114
115
116
        if self.getMagFilter(filt) <= 15:
            folding_threshold = 5.e-4
        else:
            folding_threshold = 5.e-3
        gsp = galsim.GSParams(folding_threshold=folding_threshold)
117
118
        
        # Get real image position of object (deal with chip rotation w.r.t its center)
119
        self.real_pos = self.getRealPos(chip.img, global_x=self.posImg.x, global_y=self.posImg.y,
120
                                        img_real_wcs=self.chip_wcs)
xin's avatar
xin committed
121
        x, y = self.real_pos.x + 0.5, self.real_pos.y + 0.5
xin's avatar
xin committed
122
123
124
125
126
        x_nominal = int(np.floor(x + 0.5))
        y_nominal = int(np.floor(y + 0.5))
        dx = x - x_nominal
        dy = y - y_nominal
        offset = galsim.PositionD(dx, dy)
127
128
129
        # Get real local wcs of object (deal with chip rotation w.r.t its center)
        chip_wcs_local = self.chip_wcs.local(self.real_pos)
        is_updated = 0
xin's avatar
xin committed
130

131
        # Loop over all sub-bandpasses
Fang Yuedong's avatar
Fang Yuedong committed
132
133
134
135
136
137
        for i in range(len(bandpass_list)):
            bandpass = bandpass_list[i]
            try:
                sub = integrate_sed_bandpass(sed=self.sed, bandpass=bandpass)
            except Exception as e:
                print(e)
138
139
                if self.logger:
                    self.logger.error(e)
Fang Yuedong's avatar
Fang Yuedong committed
140
                continue
141
            ratio = sub / full
Fang Yuedong's avatar
Fang Yuedong committed
142
143
144
145
            if not (ratio == -1 or (ratio != ratio)):
                nphotons = ratio * nphotons_tot
            else:
                continue
146
147

            # nphotons_sum += nphotons
Fang Yuedong's avatar
Fang Yuedong committed
148
            # print("nphotons_sub-band_%d = %.2f"%(i, nphotons))
149
150
            
            # Get PSF model
151
152
            psf, pos_shear = psf_model.get_PSF(chip=chip, pos_img=pos_img, bandpass=bandpass,
                                               folding_threshold=folding_threshold)
Fang Yuedong's avatar
Fang Yuedong committed
153
154
155
156
            star = galsim.DeltaFunction(gsparams=gsp)
            star = star.withFlux(nphotons)
            star = galsim.Convolve(psf, star)

157
158
159
160
161
162
163
164
165
166
167
            stamp = star.drawImage(wcs=chip_wcs_local, offset=offset)
            if np.sum(np.isnan(stamp.array)) > 0:
                continue
            stamp.setCenter(x_nominal, y_nominal)
            bounds = stamp.bounds & galsim.BoundsI(0, chip.npix_x - 1, 0, chip.npix_y - 1)
            if bounds.area() > 0:
                chip.img.setOrigin(0, 0)
                chip.img[bounds] += stamp[bounds]
                is_updated = 1
                chip.img.setOrigin(chip.bound.xmin, chip.bound.ymin)
                del stamp
Fang Yuedong's avatar
Fang Yuedong committed
168
        
169
170
        if is_updated == 0:
            # Return code 0: object has missed this detector
Fang Yuedong's avatar
Fang Yuedong committed
171
            print("obj %s missed"%(self.id))
172
173
            if self.logger:
                self.logger.info("obj %s missed"%(self.id))
Fang Yuedong's avatar
Fang Yuedong committed
174
175
            return 0, pos_shear
        return 1, pos_shear # Return code 1: draw sucesss
Fang Yuedong's avatar
Fang Yuedong committed
176

177
    def addSLStoChipImage(self, sdp=None, chip=None, xOrderSigPlus=None, local_wcs=None):
Xin Zhang's avatar
Xin Zhang committed
178
179
180
        spec_orders = sdp.compute_spec_orders()
        for k, v in spec_orders.items():
            img_s = v[0]
Fang Yuedong's avatar
Fang Yuedong committed
181
182
183
184
185
186
187
188
189
            #########################################################
            # DEBUG
            #########################################################
            # print("before convolveGaussXorders, img_s:", img_s)
            nan_ids = np.isnan(img_s)
            if img_s[nan_ids].shape[0] > 0:
                # img_s[nan_ids] = 0
                print("DEBUG: before convolveGaussXorders specImg nan num is", img_s[nan_ids].shape[0])
            #########################################################
Xin Zhang's avatar
Xin Zhang committed
190
191
192
            img_s, orig_off = convolveGaussXorders(img_s, xOrderSigPlus[k])
            origin_order_x = v[1] - orig_off
            origin_order_y = v[2] - orig_off
Fang Yuedong's avatar
Fang Yuedong committed
193
194
195
196
197
198
199
200
201
202

            #########################################################
            # DEBUG
            #########################################################
            # print("DEBUG: orig_off is", orig_off)
            nan_ids = np.isnan(img_s)
            if img_s[nan_ids].shape[0] > 0:
                img_s[nan_ids] = 0
                print("DEBUG: specImg nan num is", img_s[nan_ids].shape[0])
            #########################################################
Xin Zhang's avatar
Xin Zhang committed
203
204
205
206
207
208
209
210
            specImg = galsim.ImageF(img_s)
            photons = galsim.PhotonArray.makeFromImage(specImg)
            photons.x += origin_order_x
            photons.y += origin_order_y

            xlen_imf = int(specImg.xmax - specImg.xmin + 1)
            ylen_imf = int(specImg.ymax - specImg.ymin + 1)
            stamp = galsim.ImageF(xlen_imf, ylen_imf)
xin's avatar
xin committed
211
            stamp.wcs = local_wcs
Xin Zhang's avatar
Xin Zhang committed
212
213
            stamp.setOrigin(origin_order_x, origin_order_y)

214
            bounds = stamp.bounds & galsim.BoundsI(0, chip.npix_x - 1, 0, chip.npix_y - 1)
Xin Zhang's avatar
Xin Zhang committed
215
216
            if bounds.area() == 0:
                continue
xin's avatar
xin committed
217
            chip.img.setOrigin(0, 0)
Xin Zhang's avatar
Xin Zhang committed
218
219
220
            stamp[bounds] = chip.img[bounds]
            chip.sensor.accumulate(photons, stamp)
            chip.img[bounds] = stamp[bounds]
xin's avatar
xin committed
221
            chip.img.setOrigin(chip.bound.xmin, chip.bound.ymin)
Xin Zhang's avatar
Xin Zhang committed
222
223
224
            del stamp
        del spec_orders

Fang Yuedong's avatar
Fang Yuedong committed
225
    def drawObj_slitless(self, tel, pos_img, psf_model, bandpass_list, filt, chip, nphotons_tot=None, g1=0, g2=0,
Fang Yuedong's avatar
Fang Yuedong committed
226
227
228
229
230
231
232
233
234
235
236
237
                         exptime=150., normFilter=None, grating_split_pos=3685, fd_shear=None):

        if normFilter is not None:
            norm_thr_rang_ids = normFilter['SENSITIVITY'] > 0.001
            sedNormFactor = getNormFactorForSpecWithABMAG(ABMag=self.param['mag_use_normal'], spectrum=self.sed,
                                                          norm_thr=normFilter,
                                                          sWave=np.floor(normFilter[norm_thr_rang_ids][0][0]),
                                                          eWave=np.ceil(normFilter[norm_thr_rang_ids][-1][0]))
            if sedNormFactor == 0:
                return 2, None
        else:
            sedNormFactor = 1.
Fang Yuedong's avatar
Fang Yuedong committed
238
239
240
241
242
243
244
245
246
247

        if self.getMagFilter(filt) <= 15:
            folding_threshold = 5.e-4
        else:
            folding_threshold = 5.e-3
        gsp = galsim.GSParams(folding_threshold=folding_threshold)

        normalSED = Table(np.array([self.sed['WAVELENGTH'], self.sed['FLUX'] * sedNormFactor]).T,
                          names=('WAVELENGTH', 'FLUX'))

248
        self.real_pos = self.getRealPos(chip.img, global_x=self.posImg.x, global_y=self.posImg.y,
249
                                        img_real_wcs=self.chip_wcs)
xin's avatar
xin committed
250
251
252
253
254
255
256
257

        x, y = self.real_pos.x + 0.5, self.real_pos.y + 0.5
        x_nominal = int(np.floor(x + 0.5))
        y_nominal = int(np.floor(y + 0.5))
        dx = x - x_nominal
        dy = y - y_nominal
        offset = galsim.PositionD(dx, dy)

258
        chip_wcs_local = self.chip_wcs.local(self.real_pos)
xin's avatar
xin committed
259

260
        flat_cube = chip.flat_cube
Zhang Xin's avatar
Zhang Xin committed
261

262
263
        xOrderSigPlus = {'A': 1.3909419820029296, 'B': 1.4760376591236062, 'C': 4.035447379743442,
                         'D': 5.5684364343742825, 'E': 16.260021029735388}
xin's avatar
xin committed
264
        grating_split_pos_chip = 0 + grating_split_pos
Fang Yuedong's avatar
Fang Yuedong committed
265
266
        for i in range(len(bandpass_list)):
            bandpass = bandpass_list[i]
267
268
            psf, pos_shear = psf_model.get_PSF(chip=chip, pos_img=pos_img, bandpass=bandpass,
                                               folding_threshold=folding_threshold)
Fang Yuedong's avatar
Fang Yuedong committed
269
270
271
            star = galsim.DeltaFunction(gsparams=gsp)
            star = star.withFlux(tel.pupil_area * exptime)
            star = galsim.Convolve(psf, star)
Fang Yuedong's avatar
Fang Yuedong committed
272
            
273
            starImg = star.drawImage(nx=100, ny=100, wcs=chip_wcs_local, offset=offset)
Fang Yuedong's avatar
Fang Yuedong committed
274

xin's avatar
xin committed
275
276
            origin_star = [y_nominal - (starImg.center.y - starImg.ymin),
                           x_nominal - (starImg.center.x - starImg.xmin)]
Fang Yuedong's avatar
Fang Yuedong committed
277
            starImg.setOrigin(0,0)
Xin Zhang's avatar
Xin Zhang committed
278
279
280
281
282
283
284
285
286
287
            gal_origin = [origin_star[0], origin_star[1]]
            gal_end = [origin_star[0] + starImg.array.shape[0] - 1, origin_star[1] + starImg.array.shape[1] - 1]

            if gal_origin[1] < grating_split_pos_chip < gal_end[1]:
                subSlitPos = int(grating_split_pos_chip - gal_origin[1] + 1)
                ## part img disperse

                subImg_p1 = starImg.array[:, 0:subSlitPos]
                star_p1 = galsim.Image(subImg_p1)
                origin_p1 = origin_star
Fang Yuedong's avatar
Fang Yuedong committed
288
                star_p1.setOrigin(0,0)
289
290
                xcenter_p1 = min(x_nominal, grating_split_pos_chip - 1) - 0
                ycenter_p1 = y_nominal - 0
Xin Zhang's avatar
Xin Zhang committed
291
292

                sdp_p1 = SpecDisperser(orig_img=star_p1, xcenter=xcenter_p1,
293
294
295
296
297
298
                                       ycenter=ycenter_p1, origin=origin_p1,
                                       tar_spec=normalSED,
                                       band_start=bandpass.blue_limit * 10, band_end=bandpass.red_limit * 10,
                                       conf=chip.sls_conf[0],
                                       isAlongY=0,
                                       flat_cube=flat_cube)
Xin Zhang's avatar
Xin Zhang committed
299

300
                self.addSLStoChipImage(sdp=sdp_p1, chip=chip, xOrderSigPlus=xOrderSigPlus, local_wcs=chip_wcs_local)
Xin Zhang's avatar
Xin Zhang committed
301

302
                subImg_p2 = starImg.array[:, subSlitPos + 1:starImg.array.shape[1]]
Xin Zhang's avatar
Xin Zhang committed
303
                star_p2 = galsim.Image(subImg_p2)
Fang Yuedong's avatar
Fang Yuedong committed
304
                star_p2.setOrigin(0, 0)
Xin Zhang's avatar
Xin Zhang committed
305
                origin_p2 = [origin_star[0], grating_split_pos_chip]
xin's avatar
xin committed
306
307
                xcenter_p2 = max(x_nominal, grating_split_pos_chip - 1) - 0
                ycenter_p2 = y_nominal - 0
Xin Zhang's avatar
Xin Zhang committed
308
309
310
311
312
313

                sdp_p2 = SpecDisperser(orig_img=star_p2, xcenter=xcenter_p2,
                                       ycenter=ycenter_p2, origin=origin_p2,
                                       tar_spec=normalSED,
                                       band_start=bandpass.blue_limit * 10, band_end=bandpass.red_limit * 10,
                                       conf=chip.sls_conf[1],
314
315
                                       isAlongY=0,
                                       flat_cube=flat_cube)
Xin Zhang's avatar
Xin Zhang committed
316

317
                self.addSLStoChipImage(sdp=sdp_p2, chip=chip, xOrderSigPlus=xOrderSigPlus, local_wcs=chip_wcs_local)
Xin Zhang's avatar
Xin Zhang committed
318
319
320

                del sdp_p1
                del sdp_p2
321
            elif grating_split_pos_chip <= gal_origin[1]:
xin's avatar
xin committed
322
323
                sdp = SpecDisperser(orig_img=starImg, xcenter=x_nominal - 0,
                                    ycenter=y_nominal - 0, origin=origin_star,
Xin Zhang's avatar
Xin Zhang committed
324
325
326
                                    tar_spec=normalSED,
                                    band_start=bandpass.blue_limit * 10, band_end=bandpass.red_limit * 10,
                                    conf=chip.sls_conf[1],
327
328
                                    isAlongY=0,
                                    flat_cube=flat_cube)
329
                self.addSLStoChipImage(sdp=sdp, chip=chip, xOrderSigPlus=xOrderSigPlus, local_wcs=chip_wcs_local)
Xin Zhang's avatar
Xin Zhang committed
330
                del sdp
331
            elif grating_split_pos_chip >= gal_end[1]:
xin's avatar
xin committed
332
333
                sdp = SpecDisperser(orig_img=starImg, xcenter=x_nominal - 0,
                                    ycenter=y_nominal - 0, origin=origin_star,
Xin Zhang's avatar
Xin Zhang committed
334
335
336
                                    tar_spec=normalSED,
                                    band_start=bandpass.blue_limit * 10, band_end=bandpass.red_limit * 10,
                                    conf=chip.sls_conf[0],
337
338
                                    isAlongY=0,
                                    flat_cube=flat_cube)
339
                self.addSLStoChipImage(sdp=sdp, chip=chip, xOrderSigPlus=xOrderSigPlus, local_wcs=chip_wcs_local)
Xin Zhang's avatar
Xin Zhang committed
340
                del sdp
Fang Yuedong's avatar
Fang Yuedong committed
341
            del psf
Fang Yuedong's avatar
Fang Yuedong committed
342
        return 1, pos_shear
Fang Yuedong's avatar
Fang Yuedong committed
343
344
345
346
347
348
349
350
351
352

    def SNRestimate(self, img_obj, flux, noise_level=0.0, seed=31415):
        img_flux = img_obj.added_flux
        stamp = img_obj.copy() * 0.0
        rng = galsim.BaseDeviate(seed)
        gaussianNoise = galsim.GaussianNoise(rng, sigma=noise_level)
        stamp.addNoise(gaussianNoise)
        sig_obj = np.std(stamp.array)
        snr_obj = img_flux / sig_obj
        return snr_obj