MockObject.py 17.6 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
19
        
        for key in self.param:
            setattr(self, key, self.param[key])
        
Fang Yuedong's avatar
Fang Yuedong committed
20
21
22
23
24
25
        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
26
27
28
29
        ###mock_stamp_START
        elif self.param["star"] == 3:
            self.type = "stamp"
        ###mock_stamp_END
30

31
        self.sed = None
Fang Yuedong's avatar
Fang Yuedong committed
32

33
        # Place holder for outputs
34
        self.additional_output_str = ""
35
        
Fang Yuedong's avatar
Fang Yuedong committed
36
        self.fd_shear = None
37

38
39
        self.logger = logger

Fang Yuedong's avatar
Fang Yuedong committed
40
41
42
    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
43
        return self.param["mag_%s" % filt.filter_type.lower()]
44

45
    def getFluxFilter(self, filt):
Fang Yuedong's avatar
Fang Yuedong committed
46
        return self.param["flux_%s" % filt.filter_type.lower()]
Fang Yuedong's avatar
Fang Yuedong committed
47
48

    def getNumPhotons(self, flux, tel, exptime=150.):
49
        pupil_area = tel.pupil_area * (100.) ** 2  # m^2 to cm^2
Fang Yuedong's avatar
Fang Yuedong committed
50
51
52
        return flux * pupil_area * exptime

    def getElectronFluxFilt(self, filt, tel, exptime=150.):
53
54
55
56
57
58
        # 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
59
60
61
62

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

65
    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
66
67
68
69
70
71
        self.posImg = img.wcs.toImage(self.getPosWorld())
        self.localWCS = img.wcs.local(self.posImg)
        if (fdmodel is not None) and (chip is not None):
            if verbose:
                print("\n")
                print("Before field distortion:\n")
72
                print("x = %.2f, y = %.2f\n" % (self.posImg.x, self.posImg.y), flush=True)
Fang Yuedong's avatar
Fang Yuedong committed
73
            self.posImg, self.fd_shear = fdmodel.get_distorted(chip=chip, pos_img=self.posImg)
Fang Yuedong's avatar
Fang Yuedong committed
74
75
            if verbose:
                print("After field distortion:\n")
76
                print("x = %.2f, y = %.2f\n" % (self.posImg.x, self.posImg.y), flush=True)
Fang Yuedong's avatar
Fang Yuedong committed
77
78
79
80
81
82
        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)
83
84
85
86
        
        if chip_wcs is not None:
            self.real_wcs = chip_wcs
        elif img_header is not None:
xin's avatar
xin committed
87
88
89
90
            self.real_wcs = galsim.FitsWCS(header=img_header)
        else:
            self.real_wcs = None

Fang Yuedong's avatar
Fang Yuedong committed
91
        return self.posImg, self.offset, self.localWCS, self.real_wcs, self.fd_shear
xin's avatar
xin committed
92

93
    def getRealPos(self, img, global_x=0., global_y=0., img_real_wcs=None):
xin's avatar
xin committed
94
95
96
97
98
99
        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

Fang Yuedong's avatar
Fang Yuedong committed
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
    def drawObject(self, img, final, flux=None, filt=None, tel=None, exptime=150.):
        """ Draw (point like) object on img.
        Should be overided for extended source, e.g. galaxy...
        Paramter:
            img: the "canvas"
            final: final (after shear, PSF etc.) GSObject
        Return:
            img: the image with the GSObject added (or discarded)
            isUpdated: is the "canvas" been updated? (a flag for updating statistcs)
        """
        isUpdated = True

        # Draw with FFT
        # stamp = final.drawImage(wcs=self.localWCS, offset=self.offset)

        # Draw with Photon Shoot
        stamp = final.drawImage(wcs=self.localWCS, method='phot', offset=self.offset)
117

Fang Yuedong's avatar
Fang Yuedong committed
118
119
120
121
122
123
124
125
126
127
        stamp.setCenter(self.x_nominal, self.y_nominal)
        if np.sum(np.isnan(stamp.array)) >= 1:
            stamp.setZero()
        bounds = stamp.bounds & img.bounds
        if bounds.area() == 0:
            isUpdated = False
        else:
            img[bounds] += stamp[bounds]
        return img, stamp, isUpdated

128
    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
129
                          exptime=150., fd_shear=None):
Fang Yuedong's avatar
Fang Yuedong committed
130
131
132
133
134
135
136
137
        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)
138
139
            if self.logger:
                self.logger.error(e)
Fang Yuedong's avatar
Fang Yuedong committed
140
            return 2, None
Fang Yuedong's avatar
Fang Yuedong committed
141
142
143
144
145
146
147
148
149
150
151
152

        nphotons_sum = 0
        photons_list = []
        xmax, ymax = 0, 0

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

153
154
        self.real_pos = self.getRealPos(chip.img, global_x=self.posImg.x, global_y=self.posImg.y,
                                        img_real_wcs=self.real_wcs)
xin's avatar
xin committed
155

xin's avatar
xin committed
156
        x, y = self.real_pos.x + 0.5, self.real_pos.y + 0.5
xin's avatar
xin committed
157
158
159
160
161
162
        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)

xin's avatar
xin committed
163
        real_wcs_local = self.real_wcs.local(self.real_pos)
xin's avatar
xin committed
164

Fang Yuedong's avatar
Fang Yuedong committed
165
166
167
168
169
170
        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)
171
172
                if self.logger:
                    self.logger.error(e)
Fang Yuedong's avatar
Fang Yuedong committed
173
174
                # return False
                continue
175
176

            ratio = sub / full
Fang Yuedong's avatar
Fang Yuedong committed
177
178
179
180
181
182
183
184

            if not (ratio == -1 or (ratio != ratio)):
                nphotons = ratio * nphotons_tot
            else:
                # return False
                continue
            nphotons_sum += nphotons
            # print("nphotons_sub-band_%d = %.2f"%(i, nphotons))
185
186
            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
187
188
189
190
            star = galsim.DeltaFunction(gsparams=gsp)
            star = star.withFlux(nphotons)
            star = galsim.Convolve(psf, star)

xin's avatar
xin committed
191
            stamp = star.drawImage(wcs=real_wcs_local, method='phot', offset=offset, save_photons=True)
Fang Yuedong's avatar
Fang Yuedong committed
192
193
194
            xmax = max(xmax, stamp.xmax)
            ymax = max(ymax, stamp.ymax)
            photons = stamp.photons
xin's avatar
xin committed
195
196
            photons.x += x_nominal
            photons.y += y_nominal
Fang Yuedong's avatar
Fang Yuedong committed
197
198
            photons_list.append(photons)

xin's avatar
xin committed
199
200
201
        stamp = galsim.ImageF(int(xmax * 1.1), int(ymax * 1.1))
        stamp.wcs = real_wcs_local
        stamp.setCenter(x_nominal, y_nominal)
202
        bounds = stamp.bounds & galsim.BoundsI(0, chip.npix_x - 1, 0, chip.npix_y - 1)
Fang Yuedong's avatar
Fang Yuedong committed
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
        
        # # (DEBUG)
        # print("stamp bounds: ", stamp.bounds)
        # print(bounds)
        if bounds.area() > 0:
            chip.img.setOrigin(0, 0)
            stamp[bounds] = chip.img[bounds]
            for i in range(len(photons_list)):
                if i == 0:
                    chip.sensor.accumulate(photons_list[i], stamp)
                else:
                    chip.sensor.accumulate(photons_list[i], stamp, resume=True)

            chip.img[bounds] = stamp[bounds]

            chip.img.setOrigin(chip.bound.xmin, chip.bound.ymin)
        else:
            # Return code 0: object photons missed this detector
            print("obj %s missed"%(self.id))
222
223
            if self.logger:
                self.logger.info("obj %s missed"%(self.id))
Fang Yuedong's avatar
Fang Yuedong committed
224
225
            return 0, pos_shear

Fang Yuedong's avatar
Fang Yuedong committed
226
227
        del photons_list
        del stamp
Fang Yuedong's avatar
Fang Yuedong committed
228
        return 1, pos_shear # Return code 1: draw sucesss
Fang Yuedong's avatar
Fang Yuedong committed
229

230
    def addSLStoChipImage(self, sdp=None, chip=None, xOrderSigPlus=None, local_wcs=None):
Xin Zhang's avatar
Xin Zhang committed
231
232
233
        spec_orders = sdp.compute_spec_orders()
        for k, v in spec_orders.items():
            img_s = v[0]
Fang Yuedong's avatar
Fang Yuedong committed
234
235
236
237
238
239
240
241
242
            #########################################################
            # 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
243
244
245
            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
246
247
248
249
250
251
252
253
254
255

            #########################################################
            # 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
256
257
258
259
260
261
262
263
            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
264
            stamp.wcs = local_wcs
Xin Zhang's avatar
Xin Zhang committed
265
266
            stamp.setOrigin(origin_order_x, origin_order_y)

267
            bounds = stamp.bounds & galsim.BoundsI(0, chip.npix_x - 1, 0, chip.npix_y - 1)
Xin Zhang's avatar
Xin Zhang committed
268
269
            if bounds.area() == 0:
                continue
xin's avatar
xin committed
270
            chip.img.setOrigin(0, 0)
Xin Zhang's avatar
Xin Zhang committed
271
272
273
            stamp[bounds] = chip.img[bounds]
            chip.sensor.accumulate(photons, stamp)
            chip.img[bounds] = stamp[bounds]
xin's avatar
xin committed
274
            chip.img.setOrigin(chip.bound.xmin, chip.bound.ymin)
Xin Zhang's avatar
Xin Zhang committed
275
276
277
            del stamp
        del spec_orders

Fang Yuedong's avatar
Fang Yuedong committed
278
    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
279
280
281
282
283
284
285
286
287
288
289
290
                         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
291
292
293
294
295
296
297
298
299
300

        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'))

301
302
        self.real_pos = self.getRealPos(chip.img, global_x=self.posImg.x, global_y=self.posImg.y,
                                        img_real_wcs=self.real_wcs)
xin's avatar
xin committed
303
304
305
306
307
308
309
310
311
312

        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)

        real_wcs_local = self.real_wcs.local(self.real_pos)

313
        flat_cube = chip.flat_cube
Zhang Xin's avatar
Zhang Xin committed
314

315
316
        xOrderSigPlus = {'A': 1.3909419820029296, 'B': 1.4760376591236062, 'C': 4.035447379743442,
                         'D': 5.5684364343742825, 'E': 16.260021029735388}
xin's avatar
xin committed
317
        grating_split_pos_chip = 0 + grating_split_pos
Fang Yuedong's avatar
Fang Yuedong committed
318
319
        for i in range(len(bandpass_list)):
            bandpass = bandpass_list[i]
320
321
            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
322
323
324
            star = galsim.DeltaFunction(gsparams=gsp)
            star = star.withFlux(tel.pupil_area * exptime)
            star = galsim.Convolve(psf, star)
Fang Yuedong's avatar
Fang Yuedong committed
325
326
            
            starImg = star.drawImage(nx=100, ny=100, wcs=real_wcs_local, offset=offset)
Fang Yuedong's avatar
Fang Yuedong committed
327

xin's avatar
xin committed
328
329
            origin_star = [y_nominal - (starImg.center.y - starImg.ymin),
                           x_nominal - (starImg.center.x - starImg.xmin)]
Fang Yuedong's avatar
Fang Yuedong committed
330
            starImg.setOrigin(0,0)
Xin Zhang's avatar
Xin Zhang committed
331
332
333
334
335
336
337
338
339
340
            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
341
                star_p1.setOrigin(0,0)
342
343
                xcenter_p1 = min(x_nominal, grating_split_pos_chip - 1) - 0
                ycenter_p1 = y_nominal - 0
Xin Zhang's avatar
Xin Zhang committed
344
345

                sdp_p1 = SpecDisperser(orig_img=star_p1, xcenter=xcenter_p1,
346
347
348
349
350
351
                                       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
352

353
                self.addSLStoChipImage(sdp=sdp_p1, chip=chip, xOrderSigPlus=xOrderSigPlus, local_wcs=real_wcs_local)
Xin Zhang's avatar
Xin Zhang committed
354

355
                subImg_p2 = starImg.array[:, subSlitPos + 1:starImg.array.shape[1]]
Xin Zhang's avatar
Xin Zhang committed
356
                star_p2 = galsim.Image(subImg_p2)
Fang Yuedong's avatar
Fang Yuedong committed
357
                star_p2.setOrigin(0, 0)
Xin Zhang's avatar
Xin Zhang committed
358
                origin_p2 = [origin_star[0], grating_split_pos_chip]
xin's avatar
xin committed
359
360
                xcenter_p2 = max(x_nominal, grating_split_pos_chip - 1) - 0
                ycenter_p2 = y_nominal - 0
Xin Zhang's avatar
Xin Zhang committed
361
362
363
364
365
366

                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],
367
368
                                       isAlongY=0,
                                       flat_cube=flat_cube)
Xin Zhang's avatar
Xin Zhang committed
369

370
                self.addSLStoChipImage(sdp=sdp_p2, chip=chip, xOrderSigPlus=xOrderSigPlus, local_wcs=real_wcs_local)
Xin Zhang's avatar
Xin Zhang committed
371
372
373

                del sdp_p1
                del sdp_p2
374
            elif grating_split_pos_chip <= gal_origin[1]:
xin's avatar
xin committed
375
376
                sdp = SpecDisperser(orig_img=starImg, xcenter=x_nominal - 0,
                                    ycenter=y_nominal - 0, origin=origin_star,
Xin Zhang's avatar
Xin Zhang committed
377
378
379
                                    tar_spec=normalSED,
                                    band_start=bandpass.blue_limit * 10, band_end=bandpass.red_limit * 10,
                                    conf=chip.sls_conf[1],
380
381
382
                                    isAlongY=0,
                                    flat_cube=flat_cube)
                self.addSLStoChipImage(sdp=sdp, chip=chip, xOrderSigPlus=xOrderSigPlus, local_wcs=real_wcs_local)
Xin Zhang's avatar
Xin Zhang committed
383
                del sdp
384
            elif grating_split_pos_chip >= gal_end[1]:
xin's avatar
xin committed
385
386
                sdp = SpecDisperser(orig_img=starImg, xcenter=x_nominal - 0,
                                    ycenter=y_nominal - 0, origin=origin_star,
Xin Zhang's avatar
Xin Zhang committed
387
388
389
                                    tar_spec=normalSED,
                                    band_start=bandpass.blue_limit * 10, band_end=bandpass.red_limit * 10,
                                    conf=chip.sls_conf[0],
390
391
392
                                    isAlongY=0,
                                    flat_cube=flat_cube)
                self.addSLStoChipImage(sdp=sdp, chip=chip, xOrderSigPlus=xOrderSigPlus, local_wcs=real_wcs_local)
Xin Zhang's avatar
Xin Zhang committed
393
                del sdp
Fang Yuedong's avatar
Fang Yuedong committed
394
            del psf
Fang Yuedong's avatar
Fang Yuedong committed
395
        return 1, pos_shear
Fang Yuedong's avatar
Fang Yuedong committed
396
397
398
399
400
401
402
403
404
405

    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