MockObject.py 17.4 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 = ""
Fang Yuedong's avatar
Fang Yuedong committed
35
        self.fd_shear = None
36

37
38
        self.logger = logger

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

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

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

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

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

xin's avatar
xin committed
64
    def getPosImg_Offset_WCS(self, img, fdmodel=None, chip=None, verbose=True, img_header=None):
Fang Yuedong's avatar
Fang Yuedong committed
65
66
67
68
69
70
        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")
71
                print("x = %.2f, y = %.2f\n" % (self.posImg.x, self.posImg.y), flush=True)
Fang Yuedong's avatar
Fang Yuedong committed
72
            self.posImg, self.fd_shear = fdmodel.get_distorted(chip=chip, pos_img=self.posImg)
Fang Yuedong's avatar
Fang Yuedong committed
73
74
            if verbose:
                print("After field distortion:\n")
75
                print("x = %.2f, y = %.2f\n" % (self.posImg.x, self.posImg.y), flush=True)
Fang Yuedong's avatar
Fang Yuedong committed
76
77
78
79
80
81
        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)
xin's avatar
xin committed
82
83
84
85
86
87

        if img_header is not None:
            self.real_wcs = galsim.FitsWCS(header=img_header)
        else:
            self.real_wcs = None

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

90
    def getRealPos(self, img, global_x=0., global_y=0., img_real_wcs=None):
xin's avatar
xin committed
91
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

Fang Yuedong's avatar
Fang Yuedong committed
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
    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)
114

Fang Yuedong's avatar
Fang Yuedong committed
115
116
117
118
119
120
121
122
123
124
        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

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

        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)

149
150
        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
151

xin's avatar
xin committed
152
        x, y = self.real_pos.x + 0.5, self.real_pos.y + 0.5
xin's avatar
xin committed
153
154
155
156
157
158
        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
159
        real_wcs_local = self.real_wcs.local(self.real_pos)
xin's avatar
xin committed
160

Fang Yuedong's avatar
Fang Yuedong committed
161
162
163
164
165
166
        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)
167
                self.logger.error(e)
Fang Yuedong's avatar
Fang Yuedong committed
168
169
                # return False
                continue
170
171

            ratio = sub / full
Fang Yuedong's avatar
Fang Yuedong committed
172
173
174
175
176
177
178
179

            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))
180
181
            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
182
183
184
185
            star = galsim.DeltaFunction(gsparams=gsp)
            star = star.withFlux(nphotons)
            star = galsim.Convolve(psf, star)

xin's avatar
xin committed
186
            stamp = star.drawImage(wcs=real_wcs_local, method='phot', offset=offset, save_photons=True)
Fang Yuedong's avatar
Fang Yuedong committed
187
188
189
            xmax = max(xmax, stamp.xmax)
            ymax = max(ymax, stamp.ymax)
            photons = stamp.photons
xin's avatar
xin committed
190
191
            photons.x += x_nominal
            photons.y += y_nominal
Fang Yuedong's avatar
Fang Yuedong committed
192
193
            photons_list.append(photons)

xin's avatar
xin committed
194
195
196
        stamp = galsim.ImageF(int(xmax * 1.1), int(ymax * 1.1))
        stamp.wcs = real_wcs_local
        stamp.setCenter(x_nominal, y_nominal)
197
        bounds = stamp.bounds & galsim.BoundsI(0, chip.npix_x - 1, 0, chip.npix_y - 1)
Fang Yuedong's avatar
Fang Yuedong committed
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
        
        # # (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))
            self.logger.info("obj %s missed"%(self.id))
            return 0, pos_shear

Fang Yuedong's avatar
Fang Yuedong committed
220
221
        del photons_list
        del stamp
Fang Yuedong's avatar
Fang Yuedong committed
222
        return 1, pos_shear # Return code 1: draw sucesss
Fang Yuedong's avatar
Fang Yuedong committed
223

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

            #########################################################
            # 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
250
251
252
253
254
255
256
257
            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
258
            stamp.wcs = local_wcs
Xin Zhang's avatar
Xin Zhang committed
259
260
            stamp.setOrigin(origin_order_x, origin_order_y)

261
            bounds = stamp.bounds & galsim.BoundsI(0, chip.npix_x - 1, 0, chip.npix_y - 1)
Xin Zhang's avatar
Xin Zhang committed
262
263
            if bounds.area() == 0:
                continue
xin's avatar
xin committed
264
            chip.img.setOrigin(0, 0)
Xin Zhang's avatar
Xin Zhang committed
265
266
267
            stamp[bounds] = chip.img[bounds]
            chip.sensor.accumulate(photons, stamp)
            chip.img[bounds] = stamp[bounds]
xin's avatar
xin committed
268
            chip.img.setOrigin(chip.bound.xmin, chip.bound.ymin)
Xin Zhang's avatar
Xin Zhang committed
269
270
271
            del stamp
        del spec_orders

Fang Yuedong's avatar
Fang Yuedong committed
272
    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
273
274
275
276
277
278
279
280
281
282
283
284
                         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
285
286
287
288
289
290
291
292
293
294

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

295
296
        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
297
298
299
300
301
302
303
304
305
306

        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)

307
        flat_cube = chip.flat_cube
Zhang Xin's avatar
Zhang Xin committed
308

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

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

                sdp_p1 = SpecDisperser(orig_img=star_p1, xcenter=xcenter_p1,
340
341
342
343
344
345
                                       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
346

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

349
                subImg_p2 = starImg.array[:, subSlitPos + 1:starImg.array.shape[1]]
Xin Zhang's avatar
Xin Zhang committed
350
                star_p2 = galsim.Image(subImg_p2)
Fang Yuedong's avatar
Fang Yuedong committed
351
                star_p2.setOrigin(0, 0)
Xin Zhang's avatar
Xin Zhang committed
352
                origin_p2 = [origin_star[0], grating_split_pos_chip]
xin's avatar
xin committed
353
354
                xcenter_p2 = max(x_nominal, grating_split_pos_chip - 1) - 0
                ycenter_p2 = y_nominal - 0
Xin Zhang's avatar
Xin Zhang committed
355
356
357
358
359
360

                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],
361
362
                                       isAlongY=0,
                                       flat_cube=flat_cube)
Xin Zhang's avatar
Xin Zhang committed
363

364
                self.addSLStoChipImage(sdp=sdp_p2, chip=chip, xOrderSigPlus=xOrderSigPlus, local_wcs=real_wcs_local)
Xin Zhang's avatar
Xin Zhang committed
365
366
367

                del sdp_p1
                del sdp_p2
368
            elif grating_split_pos_chip <= gal_origin[1]:
xin's avatar
xin committed
369
370
                sdp = SpecDisperser(orig_img=starImg, xcenter=x_nominal - 0,
                                    ycenter=y_nominal - 0, origin=origin_star,
Xin Zhang's avatar
Xin Zhang committed
371
372
373
                                    tar_spec=normalSED,
                                    band_start=bandpass.blue_limit * 10, band_end=bandpass.red_limit * 10,
                                    conf=chip.sls_conf[1],
374
375
376
                                    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
377
                del sdp
378
            elif grating_split_pos_chip >= gal_end[1]:
xin's avatar
xin committed
379
380
                sdp = SpecDisperser(orig_img=starImg, xcenter=x_nominal - 0,
                                    ycenter=y_nominal - 0, origin=origin_star,
Xin Zhang's avatar
Xin Zhang committed
381
382
383
                                    tar_spec=normalSED,
                                    band_start=bandpass.blue_limit * 10, band_end=bandpass.red_limit * 10,
                                    conf=chip.sls_conf[0],
384
385
386
                                    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
387
                del sdp
Fang Yuedong's avatar
Fang Yuedong committed
388
            del psf
Fang Yuedong's avatar
Fang Yuedong committed
389
        return 1, pos_shear
Fang Yuedong's avatar
Fang Yuedong committed
390
391
392
393
394
395
396
397
398
399

    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