def test_offset_residuals(): """ Use same affine object for simulation and analysis """ pixelscale_as = 0.0656 arcsec2rad = u.arcsec.to(u.rad) #psf_offsets = ((0,0), (1.0,0), ) #psf_offsets = ((0,0), (1.0,0), (0, 1.0), (1.0,1.0)) bandpass = np.array([ (1.0, 4.3e-6), ]) # Loop over psf_offsets jw = NRM_Model(mask='jwst', holeshape='hex') jw.set_pixelscale(pixelscale_as * arcsec2rad) jw.simulate(fov=35, bandpass=bandpass, over=oversample, psf_offset=psf_offsets) fits.writeto(fitsimdir + "offset_data.fits", jw.psf, overwrite=True) jw.make_model(fov=35, bandpass=bandpass, over=oversample, psf_offset=psf_offsets) jw.fit_image(jw.psf, modelin=jw.model) fits.writeto(fitsimdir + "residual_offset.fits", jw.residual, overwrite=True)
class Affine2dMakeModelRotTestCase(unittest.TestCase): def setUp(self): allholes = ('b4', 'c2', 'b5', 'b2', 'c1', 'b6', 'c6') b4, c2, b5, b2, c1, b6, c6 = allholes self.hc = (b2, b6, b5) # holechoices self.hstr = holes2string(self.hc) self.holeshape = "hex" # directory containing the test data data_dir = os.path.join(os.path.dirname(__file__), 'test_data/affine2d_makemodel_rot') self.data_dir = data_dir print(data_dir) if not os.path.exists(data_dir): os.makedirs(data_dir) # expects strings of % (self.npix, self.holeshape, self.wave/um, rot, self.hstr) self.fnfmt = data_dir + '/psf_nrm_{2:.1f}_{0}_{1}_{3:.0f}_{4:s}.fits' self.fmt = " ({0:+.3f}, {1:+.3f}) -> ({2:+.3f}, {3:+.3f})" self.pixel = 0.0656 self.npix = 87 self.wave = 4.3e-6 # m self.over = 11 mx, my = 1.0, 1.0 sx, sy = 0.0, 0.0 xo, yo = 0.0, 0.0 affine_ideal = Affine2d(mx=mx, my=my, sx=sx, sy=sy, xo=xo, yo=yo, name="Ideal") rots = (0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90, 95) rots = ( 0.000, 10.000, ) for rot in rots: rot = avoidhexsingularity(rot) # in utils affine_rot = Affine2d(rotradccw=np.pi * rot / 180.0, name="{0:.0f}".format(rot)) # in utils aff = affine_rot # holeshape is hex or circ g7s6 jwst # because model_array requires # a primary beam for one slice of the model self.jw = NRM_Model( mask='jwst', holeshape=self.holeshape, #chooseholes=self.hc, affine2d=aff) self.jw.set_pixelscale(self.pixel * arcsec2rad) self.jw.simulate(fov=self.npix, bandpass=self.wave, over=self.over) # write psf psffn = self.fnfmt.format(self.npix, self.holeshape, self.wave / um, rot, self.hstr) fits.writeto(psffn, self.jw.psf, overwrite=True) header = fits.getheader(psffn) header = affinepars2header(header, aff) fits.update(psffn, self.jw.psf, header=header) print("test: psf shape", self.jw.psf.shape) modelslices = self.jw.make_model(fov=self.npix, bandpass=self.wave, over=self.over) print("test: modelslices type", type(modelslices)) print("test: modelslices shape", modelslices.shape) modelfn = psffn.replace("psf_nrm", "model") # write model model_for_fitsfile = np.zeros( (modelslices.shape[2], modelslices.shape[0], modelslices.shape[1])) for sl in range(modelslices.shape[2]): model_for_fitsfile[sl, :, :] = modelslices[:, :, sl] print("test: model_for_fitsfile type", type(model_for_fitsfile)) print("test: model_for_fitsfile shape", model_for_fitsfile.shape) fits.writeto(modelfn, model_for_fitsfile[6, :, :], overwrite=True) header = fits.getheader(modelfn) header = affinepars2header(header, aff) fits.update(modelfn, model_for_fitsfile[6, :, :], header=header) del self.jw del aff del affine_rot def test_psf(self): """ sanity check and code development tool than a routine test. """ self.assertTrue(0.0 < 1e-15, 'error: test_affine2d failed')
def fit_fringes_single_integration(args): self = args["object"] slc = args["slc"] # indexes each slice of 3D stack of images id_tag = args["slc"] nrm = NRM_Model(mask=self.instrument_data.mask, pixscale=self.instrument_data.pscale_rad, holeshape=self.instrument_data.holeshape, affine2d=self.instrument_data.affine2d, over=self.oversample) # for image data from single filter, this is the filter bandpass. # otherwise it's the IFU wavelength slice. nrm.bandpass = self.instrument_data.wls[slc] if self.npix == 'default': self.npix = self.scidata[slc, :, :].shape[0] # New or modified in LG++ # center the image on its peak pixel: # AS subtract 1 from "r" below for testing >1/2 pixel offsets # AG 03-2019 -- is above comment still relevant? # Where appropriate, the slice under consideration is centered, and processed if self.instrument_data.arrname == "jwst_g7s6c": # get the cropped image and identically-cropped bad pixel data: self.ctrd, self.dqslice = utils.center_imagepeak( self.scidata[slc, :, :], dqm=self.dqmask[slc, :, :]) else: self.ctrd = utils.center_imagepeak(self.scidata[slc, :, :]) # store the 2D cropped image centered on the brightest pixel, # bad pixels smoothed over if self.psf_offset_ff is None: # returned values have offsets x-y flipped: # Finding centroids the Fourier way assumes no bad pixels case # - Fourier domain mean slope # offsets from brightest pixel ctr centroid = utils.find_centroid(self.ctrd, self.instrument_data.threshold) # use flipped centroids to update centroid of image for JWST # pixel coordinates: - note the flip of [0] and [1] to match DS9 view image_center = utils.centerpoint(self.ctrd.shape) + \ np.array((centroid[1], centroid[0])) # info only, unused nrm.xpos = centroid[1] # flip 0 and 1 to convert nrm.ypos = centroid[0] # flip 0 and 1 nrm.psf_offset = nrm.xpos, nrm.ypos # renamed .bestcenter to .psf_offset if self.debug: print( "nrm.core.fit_fringes_single_integration: utils.find_centroid() -> nrm.psf_offset" ) else: # user-provided psf_offset python-style offsets from array center are here. nrm.psf_offset = self.psf_offset_ff nrm.make_model(fov=self.ctrd.shape[0], bandpass=nrm.bandpass, over=self.oversample, psf_offset=nrm.psf_offset, pixscale=nrm.pixel) # again, fit just one slice... if self.instrument_data.arrname == "jwst_g7s6c": nrm.fit_image(self.ctrd, modelin=nrm.model, psf_offset=nrm.psf_offset, dqm=self.dqslice, weighted=self.weighted) else: nrm.fit_image(self.ctrd, modelin=nrm.model, psf_offset=nrm.psf_offset, weighted=self.weighted) """ Attributes now stored in nrm object: ----------------------------------------------------------------------------- soln --- resulting sin/cos coefficients from least squares fitting fringephase --- baseline phases in radians fringeamp --- baseline amplitudes (flux normalized) redundant_cps --- closure phases in radians redundant_cas --- closure amplitudes residual --- fit residuals [data - model solution] cond --- matrix condition for inversion fringepistons --- zero-mean piston opd in radians on each hole (eigenphases) ----------------------------------------------------------------------------- For jwst_g7s6 cropped-to-match-data bad pixel array 'ctrb' is also stored """ self.save_output(slc, nrm) # Please elucidate what this is for self.nrm = nrm # store extracted values here return None
def verify_pistons(arg): """ Create simulated data with pistons, 1. analyze the data by calling fit_image Do not use fringefitter since the data is perfecrly centered (the if loop). Check if input and output pistons match. 2. analyze the data by calling fit_image via fringefitter Fringefitter finds the bestcenter and makes model with bestcenter. Since the data is perfecrly centered fringefitter should not affect output pistons. Check if input and output pistons match. """ jw = NRM_Model(mask='jwst', holeshape="hex") pixelscale_as = 0.0656 arcsec2rad = u.arcsec.to(u.rad) jw.set_pixelscale(pixelscale_as * arcsec2rad) np.random.seed(100) #lambda/14 ~ 80% strehl ratio phi = np.random.normal(0, 1.0, 7) / 14.0 # waves print("phi", phi, "varphi", phi.var(), "waves") phi = phi - phi.mean() print("phi_nb stdev/w", phi.std()) print("phi_nb stdev/r", phi.std() * 2 * np.pi) print("phi_nb mean/r", phi.mean() * 2 * np.pi) pistons = phi * 4.3e-6 #OR print("/=====input pistons/m=======/\n", pistons) print("/=====input pistons/r=======/\n", pistons * (2 * np.pi) / 4.3e-6) jw.set_pistons(pistons) jw.simulate(fov=81, bandpass=4.3e-6, over=oversample) fits.PrimaryHDU(data=jw.psf).writeto( "implaneia_tests/perfect_wpistons.fits", overwrite=True) if arg == ("no_fringefitter"): jw.make_model(fov=81, bandpass=4.3e-6, over=oversample) jw.fit_image(jw.psf, modelin=jw.model) pos = np.get_printoptions() np.set_printoptions(precision=4, formatter={'float': lambda x: '{:+.4e}'.format(x)}, linewidth=80) print("Residual/psfpeak array center:", jw.psf.shape) print((jw.residual / jw.psf.max())[36:-36, 36:-36]) np.set_printoptions(pos) fits.PrimaryHDU(data=jw.residual).writeto( "residual_pistons_no_ff.fits", overwrite=True) #return jw #print("output pistons/r",jw.fringepistons) #print("output pistons/w",jw.fringepistons/(2*np.pi)) #print("output pistons/m",jw.fringepistons*4.3e-6/(2*np.pi)) #print("input pistons/m ",jw.phi) elif arg == ("use_fringefitter"): fits.PrimaryHDU(data=jw.psf).writeto(datadir + "/perfect_wpistons.fits", overwrite=True) amisim2mirage(datadir, ("perfect_wpistons", ), mirexample, filt) niriss = InstrumentData.NIRISS(filt) ff_t = nrm_core.FringeFitter(niriss, datadir=datadir, savedir=datadir, oversample=oversample, interactive=False) print(test_tar) ff_t.fit_fringes(test_tar) print("Residual:") #print(ff_t.nrm.residual) print("Residual/psfpeak array center:", ff_t.nrm.reference.shape) pos = np.get_printoptions() np.set_printoptions(precision=3, formatter={'float': lambda x: '{:+.3e}'.format(x)}, linewidth=80) print((ff_t.nrm.residual / jw.psf.max())[36:-36, 36:-36]) np.set_printoptions(pos) fits.PrimaryHDU(data=ff_t.nrm.residual).writeto(datadir+\ "/residual_pistons_with_ff.fits",overwrite=True) utils.compare_pistons(jw.phi * 2 * np.pi / 4.3e-6, ff_t.nrm.fringepistons, str="ff_t")