def xyideal_to_v2v3(xi, yi, **kwargs): if ('siaf1A' in kwargs): siaf1A = kwargs['siaf1A'] else: # Import locally to this function so that pysiaf isn't required for everything in mrs_tools import miricoord.mrs.makesiaf.makesiaf_mrs as makesiaf siaf1A = makesiaf.create_siaf_oneband('1A') v2ref, v3ref = siaf1A['inscr_v2ref'], siaf1A['inscr_v3ref'] v2 = -(xi - v2ref) v3 = yi + v3ref return v2, v3
def v2v3_to_xyideal(v2, v3, **kwargs): if ('siaf1A' in kwargs): siaf1A = kwargs['siaf1A'] else: # Import locally to this function so that pysiaf isn't required for everything in mrs_tools import miricoord.mrs.makesiaf.makesiaf_mrs as makesiaf siaf1A = makesiaf.create_siaf_oneband('1A') v2ref, v3ref = siaf1A['inscr_v2ref'], siaf1A['inscr_v3ref'] xidl = -(v2 - v2ref) yidl = v3 - v3ref return xidl, yidl
def main(detband, dithers, psftot, extval, scan=False, writearea=False): # Set the distortion solution to use mt.set_toolversion('cdp8b') # Define the bands to use left, right = 'N/A', 'N/A' if ((detband == '1A') or (detband == '12A')): left = '1A' if ((detband == '2A') or (detband == '12A')): right = '2A' if ((detband == '1B') or (detband == '12B')): left = '1B' if ((detband == '2B') or (detband == '12B')): right = '2B' if ((detband == '1C') or (detband == '12C')): left = '1C' if ((detband == '2C') or (detband == '12C')): right = '2C' if ((detband == '3A') or (detband == '34A')): right = '3A' if ((detband == '4A') or (detband == '34A')): left = '4A' if ((detband == '3B') or (detband == '34B')): right = '3B' if ((detband == '4B') or (detband == '34B')): left = '4B' if ((detband == '3C') or (detband == '34C')): right = '3C' if ((detband == '4C') or (detband == '34C')): left = '4C' ######################################################### print('Setting up the dithers') # Normal CDP-8b distortion solution dithers from PRDOPSSOC-M-026 if (scan == False): dxidl = -np.array([ 0., 1.094458, -1.012049, 0.988069, -1.117844, 0.102213, -0.127945, 0.008080, -0.034015 ]) dyidl = -np.array([ 0., -0.385616, 0.296642, -0.311605, 0.371771, -0.485776, 0.467512, -0.499923, 0.481275 ]) # Select desired combination of dither positions # Warning, this will fail if we have bad input! dxidl = dxidl[dithers] dyidl = dyidl[dithers] nexp = len(dxidl) # If the 'scan' option was True, then override the setup to create a scanning grid to sample # the field for a given channel. Note that this also will only populate a single detector at a time! if (scan == True): if ((detband == '12A') or (detband == '12B') or (detband == '12C') or (detband == '34A') or (detband == '34B') or (detband == '34C')): print('Cannot use scan with selected band!') # What is the field for this channel? chinfo = mrssiaf.create_siaf_oneband(detband) minalpha, maxalpha = np.min(chinfo['inscr_alpha_corners']), np.max( chinfo['inscr_alpha_corners']) minbeta, maxbeta = np.min(chinfo['inscr_beta_corners']), np.max( chinfo['inscr_beta_corners']) # And the slice width sw = mt.slicewidth(detband) # Number of slices nslice = ((maxbeta - minbeta) / sw).astype(int) # PSF FWHM for spacing from detector edges for sim points fwhm = 2 * rough_fwhm(detband) # First seven points are the center, corners, and sides alpha1 = np.array([ 0, minalpha + fwhm, maxalpha - fwhm, minalpha + fwhm, maxalpha - fwhm, minalpha + fwhm, maxalpha - fwhm ]) beta1 = np.array([ 0, maxbeta - fwhm, maxbeta - fwhm, 0, 0, minbeta + fwhm, minbeta + fwhm ]) # Next set of points is a scan up alpha=0 for every slice alpha2 = np.zeros(nslice) beta2 = np.arange(nslice) * sw + minbeta + sw / 2. # Concatenate arrays alpha = np.concatenate((alpha1, alpha2)) beta = np.concatenate((beta1, beta2)) # Convert to v2,v3 v2, v3 = mt.abtov2v3(alpha, beta, detband) # Now convert to Ideal coordinate offsets relative to Ch1a reference point dxidl, dyidl = mt.v2v3_to_xyideal(v2, v3) # Now flip them, because we're moving the source not the telescope dxidl, dyidl = -dxidl, -dyidl nexp = len(dxidl) # Print the points to a file for reference pointfile = 'simpoints' + detband + '.txt' print('# alpha beta', file=open(pointfile, "w")) for ii in range(0, nexp): print(alpha[ii], beta[ii], file=open(pointfile, "a")) # Plot where the points were for reference plotname = 'qaplot' + detband + '.png' plot_qascan(chinfo, detband, v2, v3, filename=plotname) ######################################################### print('Ndither = ', nexp) # MRS reference location is DEFINED for 1A regardless of band in use v2ref, v3ref = mt.abtov2v3(0., 0., '1A') # Define source coordinates (decl=0 makes life easier) raobj = 45.0 decobj = 0.0 # Make life easier by assuming that telescope roll exactly places # slices along R.A. for Ch1A (will not be quite as good for other channels) # Compute what that roll is a1, b1 = 0., 0. a2, b2 = 2., 0. # A location along alpha axis v2_1, v3_1 = mt.abtov2v3(a1, b1, '1A') v2_2, v3_2 = mt.abtov2v3(a2, b2, '1A') ra_1, dec_1, _ = tt.jwst_v2v3toradec([v2_1], [v3_1], v2ref=v2ref, v3ref=v3ref, raref=raobj, decref=decobj, rollref=0.) ra_2, dec_2, _ = tt.jwst_v2v3toradec([v2_2], [v3_2], v2ref=v2ref, v3ref=v3ref, raref=raobj, decref=decobj, rollref=0.) dra = (ra_2 - ra_1) * 3600. ddec = (dec_2 - dec_1) * 3600. roll = -(np.arctan2(dra, ddec) * 180. / np.pi - 90.0) # Compute the corresponding raref, decref, rollref of the dither positions. raref = np.zeros(nexp) decref = np.zeros(nexp) rollref = np.zeros(nexp) for ii in range(0, nexp): temp1, temp2, temp3 = tt.jwst_v2v3toradec([v2ref] - dxidl[ii], [v3ref] + dyidl[ii], v2ref=v2ref, v3ref=v3ref, raref=raobj, decref=decobj, rollref=roll) raref[ii] = temp1 decref[ii] = temp2 rollref[ii] = temp3 # Values for each exposure allexp = np.zeros([nexp, 1024, 1032]) allarea = np.zeros([nexp, 1024, 1032]) # Do left half of detector print('Working on left half of detector') roi = rough_fwhm(left) * 3 if (left != 'N/A'): allexp, allarea = setvalues(allexp, allarea, left, roi, raobj, decobj, raref, decref, rollref, dxidl, dyidl, psftot, extval) # Do right half of detector print('Working on right half of detector') roi = rough_fwhm(right) * 3 if (right != 'N/A'): allexp, allarea = setvalues(allexp, allarea, right, roi, raobj, decobj, raref, decref, rollref, dxidl, dyidl, psftot, extval) # Write the exposures to disk print('Writing files') basefile = get_template(detband) for ii in range(0, nexp): thisexp = allexp[ii, :, :] thisarea = allarea[ii, :, :] # Ensure two-digit sim format code strii = str(ii) if (ii <= 9): strii = '0' + str(ii) newfile = 'mock' + detband + '-' + strii + '.fits' newareafile = 'mockarea' + detband + '-' + strii + '.fits' hdu = fits.open(basefile) # Hack header WCS primheader = hdu[0].header primheader['TARG_RA'] = raobj primheader['TARG_DEC'] = decobj header = hdu['SCI'].header header['V2_REF'] = v2ref header['V3_REF'] = v3ref header['RA_REF'] = raref[ii] header['DEC_REF'] = decref[ii] header['ROLL_REF'] = rollref[ii] hdu['SCI'].header = header hdu['SCI'].data = thisexp # Overwrite any old DQ problems hdu['DQ'].data[:] = 0 hdu.writeto(newfile, overwrite=True) if (writearea == True): hdu['SCI'].data = thisarea hdu.writeto(newareafile, overwrite=True) print('Done!')
def make_x1d_fromdict(now, cdp_dir, outplot): meta = {} meta['telescope'] = 'JWST' meta['pedigree'] = 'GROUND' meta['description'] = 'Default MIRI MRS Extract1d parameters' meta['date'] = now.value meta['reftype'] = 'EXTRACT1D' meta['exposure'] = {'type': 'MIR_MRS'} meta['useafter'] = '2000-01-01T00:00:00' meta['version'] = int(now.mjd) meta['author'] = 'D. Law' meta['origin'] = 'STSCI' meta['model_type'] = 'Extract1dIFUModel' meta['history'] = '1D Extraction defaults' meta['history'] += ' DOCUMENT: TBD' meta[ 'history'] += ' SOFTWARE: https://github.com/STScI-MIRI/miri3d/tree/master/miri3d/x1d/make_x1d.py' meta['history'] += ' DATA USED: CDP-7' meta['history'] += ' Updated 4/26/21 to decrease background annulus size' meta['instrument'] = {'name': 'MIRI'} meta['region_type'] = 'target' meta['subtract_background'] = True meta['method'] = 'subpixel' meta['subpixels'] = 10 print('Figuring out wavelength ranges') wmin1A, _ = mc.waveminmax('1A') _, wmax4C = mc.waveminmax('4C') print('Building tables') # Set up placeholder vectors waves = np.arange(wmin1A, wmax4C, 0.01, dtype='float32') nwave = len(waves) radius = np.ones(nwave, dtype='float32') inbkg = np.zeros(nwave, dtype='float32') outbkg = np.zeros(nwave, dtype='float32') axratio = np.ones(nwave, dtype='float32') axangle = np.zeros(nwave, dtype='float32') # Populate real values # Read in the CDP files files = [ 'MIRI_FM_MIRIFUSHORT_1SHORT_APERCORR_07.00.00.fits', 'MIRI_FM_MIRIFUSHORT_1MEDIUM_APERCORR_07.00.00.fits', 'MIRI_FM_MIRIFUSHORT_1LONG_APERCORR_07.00.00.fits', 'MIRI_FM_MIRIFUSHORT_2SHORT_APERCORR_07.00.00.fits', 'MIRI_FM_MIRIFUSHORT_2MEDIUM_APERCORR_07.00.00.fits', 'MIRI_FM_MIRIFUSHORT_2LONG_APERCORR_07.00.00.fits', 'MIRI_FM_MIRIFULONG_3SHORT_APERCORR_07.00.00.fits', 'MIRI_FM_MIRIFULONG_3MEDIUM_APERCORR_07.00.00.fits', 'MIRI_FM_MIRIFULONG_3LONG_APERCORR_07.00.00.fits', 'MIRI_FM_MIRIFULONG_4SHORT_APERCORR_07.00.00.fits', 'MIRI_FM_MIRIFULONG_4MEDIUM_APERCORR_07.00.00.fits', 'MIRI_FM_MIRIFULONG_4LONG_APERCORR_07.00.00.fits' ] inwave = [] inap = [] for file in files: fullfile = os.path.join(cdp_dir, file) hdu = fits.open(fullfile) data = hdu[1].data inwave.append(data['wavelength']) inap.append(data['a_aperture']) # Compile into big vectors # Simple polynomial fit to the aperture thefit = np.polyfit(np.array(inwave).ravel(), np.array(inap).ravel(), 1) poly = np.poly1d(thefit) radius = poly(waves) # Background annulus # Note that Ch1 can be much more generous than Ch4; FWHM increases # by a factor of 5 from Ch1 to Ch4 but FOV only by a factor of 2. # We also should not apply any sudden steps in the annulus size # between channels, otherwise that will manifest as a step in the required # aperture correction between channels, and we're assuming that it can be # smooth with wavelength so everything interpolates from the same table. # Therefore, we'll make annuli that shrink linearly (relative to FWHM) # with wavelength in1, in2 = np.min(radius) * 2.5, np.max(radius) * 1.02 out1, out2 = np.min(radius) * 3.0, np.max(radius) * 1.5 inbkg = np.float32( np.interp(waves, np.array([np.min(waves), np.max(waves)]), np.array([in1, in2]))) outbkg = np.float32( np.interp(waves, np.array([np.min(waves), np.max(waves)]), np.array([out1, out2]))) # QA plot that our aperture and annuli look reasonable fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2, figsize=(5, 5), dpi=150) tband = ['1A', '2A', '3A', '4A'] # Bands to test twave = [7.5, 11.75, 18, 28] # Wavelengths to test ax = [ax1, ax2, ax3, ax4] for ii in range(0, len(tband)): siaf = mksiaf.create_siaf_oneband(tband[ii]) indx = np.argmin(np.abs(waves - twave[ii])) ax[ii].plot(siaf['inscr_v2_corners'], siaf['inscr_v3_corners'], color='#000000', linewidth=2) # Circle showing FWHM circle = mpl.patches.Circle((siaf['inscr_v2ref'], siaf['inscr_v3ref']), 0.31 * twave[ii] / 8. / 2, linewidth=1, edgecolor='black', facecolor=(0, 0, 0, .0125)) ax[ii].add_artist(circle) # Circle showing extraction radius circle = mpl.patches.Circle((siaf['inscr_v2ref'], siaf['inscr_v3ref']), radius[indx], linewidth=1, edgecolor='r', facecolor=(0, 0, 0, .0125)) ax[ii].add_artist(circle) # Circles showing annulus circle = mpl.patches.Circle((siaf['inscr_v2ref'], siaf['inscr_v3ref']), inbkg[indx], linewidth=1, edgecolor='b', facecolor=(0, 0, 0, .0125)) ax[ii].add_artist(circle) circle = mpl.patches.Circle((siaf['inscr_v2ref'], siaf['inscr_v3ref']), outbkg[indx], linewidth=1, edgecolor='b', facecolor=(0, 0, 0, .0125)) ax[ii].add_artist(circle) ax[ii].set_xlim(-508, -499) ax[ii].set_ylim(-324, -315) ax[ii].set_xlabel('V2 (arcsec)') ax[ii].set_ylabel('V3 (arcsec)') ax[ii].set_title(tband[ii]) plt.tight_layout() plt.savefig(str.replace(outplot, '.png', 'FOV.png')) plt.close() # QA plot of final values plt.plot(inwave, inap, '.') plt.plot(waves, radius) plt.plot(waves, inbkg, color='red') plt.plot(waves, outbkg, color='red') plt.grid() plt.xlabel('Wavelength (micron)') plt.ylabel('Extraction Radius (arcsec)') plt.savefig(outplot) plt.close() data = { 'wavelength': waves, 'wavelength_units': 'micron', 'radius': radius, 'radius_units': 'arcsec', 'inner_bkg': inbkg, 'inner_bkg_units': 'arcsec', 'outer_bkg': outbkg, 'outer_bkg_units': 'arcsec', 'axis_ratio': axratio, 'axis_pa': axangle, 'axis_pa_units': 'degrees', } tree = {'meta': meta, 'data': data} ff = asdf.AsdfFile(tree) return ff
def assess_dith(rootdir=False, siafdir=False, write_result=False, channel='1A', dith=False, wave=False, da=None, db=None): #set root and siaf directories if rootdir == False: rootdir = os.getenv('MIRICOORD_DATA_DIR') if siafdir == False: siafdir = os.getenv('MIRICOORD_DATA_DIR') # if dith == False: dith = [1, 2] ndith = len(dith) #read in list of dithers dithers = pd.read_csv('mrs_dithers.txt', header=2, sep='\s+', index_col=0) #convert dithers to ra/dec dra = dithers.dXIdeal[dith].array / 3600. ddec = dithers.dYIdeal[dith].array / 3600. siaf = makesiaf.create_siaf_oneband(channel) #define shape of channel in alpha/beta maxalpha = siaf['inscr_alpha_corners'][0] * 2 minalpha = siaf['inscr_alpha_corners'][2] * 2 maxbeta = siaf['inscr_beta_corners'][0] * 2 minbeta = siaf['inscr_beta_corners'][2] * 2 racen = 45. decen = 0. # WARNING- this code will fail if DEC != 0 !!!! dtheta = 0.02 #number of pix in ra/dec dims nra = int(np.abs(maxalpha - minalpha) / dtheta) ndec = int(np.abs(maxbeta - minbeta) / dtheta) #define the wavelength on each pixel if wave != False: lam = np.ones((nra * ndec)) * wave else: lam = np.ones((nra * ndec)) * -1 if (da != None) and (db != None): v2, v3 = mmrs.abtov2v3(da, db, channel) zpv2, zpv3 = mmrs.abtov2v3(0, 0, channel) zpx, zpy = mmrs.v2v3_to_xyideal(zpv2, zpv3) tempx, tempy = mmrs.v2v3_to_xyideal(v2, v3) dra = (tempx - zpx) / 3600. ddec = (tempy - zpy) / 3600. #define actual ra/dec range ra = (np.arange(nra) * dtheta + minalpha) / 3600. + racen dec = (np.arange(ndec) * dtheta + minbeta) / 3600. + decen #redefine center racen = (min(ra) + max(ra)) / 2. decen = (min(dec) + max(dec)) / 2. #create a cube of ra/dec coordinates skyloc = np.zeros((ndith, ndec, nra)) for i in range(0, nra - 1): skyloc[0, :, i] = ra[i] for i in range(0, ndec - 1): skyloc[1, i, :] = dec[i] rall = skyloc[0, :, :].reshape(nra * ndec) deall = skyloc[1, :, :].reshape(nra * ndec) # construct phase images slice_phase, pixel_phase, wave_phase = make_phase_im( rall, deall, racen, decen, dra, ddec, nra, ndec, lam, ndith, channel) temp = np.sum(wave_phase, axis=0) goodval = np.where(temp > -10) #construct coverage map covmap = make_covmap(goodval, wave_phase, pixel_phase) temp = covmap[0, :, :] indx = np.where(temp != 0) nindx = len(indx[0]) print('Coverage area: ' + str(nindx * dtheta * dtheta) + ' arcsec^2') offsets = np.sqrt((dra - dra[0])**2 + (ddec - ddec[0])**2) maxoffset = max(offsets) * 3600. if write_result == True: output_result(maxoffset, channel, phase_pix, phase_slice, covmap) return wave_phase, pixel_phase, covmap