def Idealtov2v3(XIdl, YIdl, apername, **kwargs): import pysiaf if ('basepath' in kwargs): siaf = pysiaf.Siaf('MIRI', basepath=kwargs['basepath']) else: siaf = pysiaf.Siaf('MIRI') thisentry = siaf[apername] v2ref, v3ref = thisentry.V2Ref, thisentry.V3Ref angle, parity = thisentry.V3IdlYAngle, thisentry.VIdlParity # Per Colin Cox: # V2 = V2Ref + VIdlParity*XIdl*cos(a) + YIdl*sin(a) # V3 = V3Ref - VIdlParity*XIdl*sin(a) + YIdl*cos(a) # Do the math rpd = math.pi / 180. # Radians per degree v2 = v2ref + parity * XIdl * math.cos(angle * rpd) + YIdl * math.sin( angle * rpd) v3 = v3ref - parity * XIdl * math.sin(angle * rpd) + YIdl * math.cos( angle * rpd) return v2, v3
def Idealtov2v3(XIdl, YIdl, apername, **kwargs): import pysiaf if ('instr' in kwargs): instrument = kwargs['instr'] else: instrument = 'MIRI' if ('basepath' in kwargs): siaf = pysiaf.Siaf(instrument, basepath=kwargs['basepath']) else: siaf = pysiaf.Siaf(instrument) print('SIAF version: ', pysiaf.JWST_PRD_VERSION) print(apername) thisentry = siaf[apername] v2ref, v3ref = thisentry.V2Ref, thisentry.V3Ref angle, parity = thisentry.V3IdlYAngle, thisentry.VIdlParity # Per Colin Cox: # V2 = V2Ref + VIdlParity*XIdl*cos(a) + YIdl*sin(a) # V3 = V3Ref - VIdlParity*XIdl*sin(a) + YIdl*cos(a) # Do the math rpd = math.pi / 180. # Radians per degree v2 = v2ref + parity * XIdl * math.cos(angle * rpd) + YIdl * math.sin( angle * rpd) v3 = v3ref - parity * XIdl * math.sin(angle * rpd) + YIdl * math.cos( angle * rpd) return v2, v3
def v2v3toIdeal(v2, v3, apername, **kwargs): import pysiaf if ('basepath' in kwargs): siaf = pysiaf.Siaf('MIRI', basepath=kwargs['basepath']) else: siaf = pysiaf.Siaf('MIRI') thisentry = siaf[apername] v2ref, v3ref = thisentry.V2Ref, thisentry.V3Ref angle, parity = thisentry.V3IdlYAngle, thisentry.VIdlParity # Inverting the above equations we get # XIdl = VIdlParity*(V2-V2Ref)*cos(a) - VIdlParity*(V3-V3REF)*sin(a) # YIdl = (V2-V2Ref)*sin(a) + (V3-V3Ref)*cos(a) # Do the math rpd = math.pi / 180. # Radians per degree XIdl = parity * (v2 - v2ref) * math.cos( angle * rpd) - parity * (v3 - v3ref) * math.sin(angle * rpd) YIdl = (v2 - v2ref) * math.sin(angle * rpd) + (v3 - v3ref) * math.cos( angle * rpd) return XIdl, YIdl
def test_specific_day_of_year_background_spectrum(): """Test this function using specific inputs and compare to the ETC output values""" sw_gain = MEAN_GAIN_VALUES['nircam']['swa'] lw_gain = MEAN_GAIN_VALUES['nircam']['lwa'] lw_etc = 2.26 / lw_gain # 2.26 e/s/pix divided by gain 2.19 e/ADU, FOR LWA sw_etc = 0.20 / sw_gain # 0.20 e/s/pix divided by gain 2.44 e/ADU for SWA # Use the NIRISS Focus Field ra = 85.22458 dec = -69.5225 obs_date = '2021-10-04' lw_filter_file = os.path.join( CONFIG_DIR, 'F444W_CLEAR_nircam_plus_ote_throughput_moda_sorted.txt') #lw_photflam = 7.7190e-22 # FLAM in cgs #lw_pivot = 4.3849 # microns lw_siaf = pysiaf.Siaf('nircam')['NRCA5_FULL'] # Here: etc is 1.03, mirage is 0.84. This may be due to a bug in the ETC. sw_filter_file = os.path.join( CONFIG_DIR, 'F090W_CLEAR_nircam_plus_ote_throughput_moda_sorted.txt') #sw_photflam = 3.3895e-20 # FLAM in cgs #sw_pivot = 0.9034 # microns sw_siaf = pysiaf.Siaf('nircam')['NRCA2_FULL'] waves, signals = backgrounds.day_of_year_background_spectrum( ra, dec, obs_date) sw_bkgd = backgrounds.calculate_background(ra, dec, sw_filter_file, True, sw_gain, sw_siaf, back_wave=waves, back_sig=signals) lw_bkgd = backgrounds.calculate_background(ra, dec, lw_filter_file, True, lw_gain, lw_siaf, back_wave=waves, back_sig=signals) assert np.isclose(sw_bkgd, sw_etc, atol=0, rtol=0.15) assert np.isclose(lw_bkgd, lw_etc, atol=0, rtol=0.25)
def get_v2v3_limits(pupil=None, border=10, return_corners=False, **kwargs): """ V2/V3 Limits for a given module stored within an dictionary border : float Extend a border by some number of arcsec. return_corners : bool Return the actualy aperture corners. Otherwise, values are chosen to be a square in V2/V3. """ siaf = pysiaf.Siaf('NIRCam') siaf.generate_toc() names_dict = { 'SW' : 'NRCALL_FULL', 'LW' : 'NRCALL_FULL', 'SWA': 'NRCAS_FULL', 'SWB': 'NRCBS_FULL', 'LWA': 'NRCA5_FULL', 'LWB': 'NRCB5_FULL', } v2v3_limits = {} for name in names_dict.keys(): apname = names_dict[name] # Do all four apertures for each SWA & SWB ap = siaf[apname] if ('S_' in apname) or ('ALL_' in apname): v2_ref, v3_ref = ap.corners('tel', False) else: xsci, ysci = ap.corners('sci', False) v2_ref, v3_ref = ap.sci_to_tel(xsci, ysci) # Offset by 50" if coronagraphy if (pupil is not None) and ('LYOT' in pupil): v2_ref -= 2.1 v3_ref += 47.7 # Add border margin v2_avg = np.mean(v2_ref) v2_ref[v2_ref<v2_avg] -= border v2_ref[v2_ref>v2_avg] += border v3_avg = np.mean(v3_ref) v3_ref[v3_ref<v3_avg] -= border v3_ref[v3_ref>v3_avg] += border if return_corners: v2v3_limits[name] = {'V2': v2_ref / 60., 'V3': v3_ref / 60.} else: v2_minmax = np.array([v2_ref.min(), v2_ref.max()]) v3_minmax = np.array([v3_ref.min(), v3_ref.max()]) v2v3_limits[name] = {'V2': v2_minmax / 60., 'V3': v3_minmax / 60.} return v2v3_limits
def checkheaders(model): # check that the data have the correct header keywords # input is a JWST datamodel if model.meta.visit.tsovisit != True: model.meta.visit.tsovisit = True #print('Setting TSOVISIT keyword') # check that CRPIX1 and CRPIX2 are set to the center of the siaf aperture for the array being used. array_cfg = model.meta.subarray.name #print('Data uses {0}'.format(array_cfg)) siaf_ap = 'MIRIM_' + array_cfg siaf = pysiaf.Siaf('MIRI') ap = siaf[siaf_ap] # Now set the crpix keywords to the right value we take from the Siaf model.meta.wcsinfo.crpix1 = ap.XSciRef model.meta.wcsinfo.crpix2 = ap.YSciRef #print(model.meta.wcsinfo.crpix1, model.meta.wcsinfo.crpix2) # also set these coordinates in another attribute of the model model.meta.wcsinfo.siaf_xref_sci = ap.XSciRef model.meta.wcsinfo.siaf_yref_sci = ap.YSciRef # we also need to add a couple of attributes for the TSO photometry step. nints = model.meta.exposure.nints model.meta.exposure.integration_start = 1 model.meta.exposure.integration_end = nints return model
def test_specific_low_medium_high_background_value(): """Test specific cases of this function and compare to ETC outputs """ # etc = {'low': 0.24, 'medium': 0.26, 'high': 0.27} # MJy/sr from webform etc = { 'low': 1.246, 'medium': 1.352, 'high': 1.402 } # e-/sec/pixel measured from output image # Use the NIRISS Focus Field ra = 85.22458 dec = -69.5225 siaf = pysiaf.Siaf('niriss')['NIS_CEN'] filter_throughput_file = os.path.join(CONFIG_DIR, 'f150w_niriss_throughput1.txt') filter_waves, filter_thru = file_io.read_filter_throughput( filter_throughput_file) bkgd_high = backgrounds.low_medium_high_background_value( ra, dec, "high", filter_waves, filter_thru, siaf) bkgd_med = backgrounds.low_medium_high_background_value( ra, dec, "medium", filter_waves, filter_thru, siaf) bkgd_low = backgrounds.low_medium_high_background_value( ra, dec, "low", filter_waves, filter_thru, siaf) assert np.isclose(bkgd_high, etc['high'], atol=0., rtol=0.05) assert np.isclose(bkgd_med, etc['medium'], atol=0., rtol=0.05) assert np.isclose(bkgd_low, etc['low'], atol=0., rtol=0.05)
def _get_default_siaf(instrument, aper_name): """ Create instance of pysiaf for the input instrument and aperture to be used later to pull SIAF values like distortion polynomial coefficients and rotation. Parameters ---------- instrument : str The name of the instrument aper_name : str The name of the specific instrument aperture Returns ------- aper : instance of pysiaf """ # Create new naming because SIAF requires special capitalization if instrument == "NIRCAM": siaf_name = "NIRCam" elif instrument == "NIRSPEC": siaf_name = "NIRSpec" else: siaf_name = instrument # Select a single SIAF aperture siaf = pysiaf.Siaf(siaf_name) aper = siaf.apertures[aper_name] return aper
def get_angle(instrument, aperture, angle_name): """Get angle requested by user Parameters ---------- instrument : JWST instrument of interest type : str aperture : instrument observing aperture type : str angle_name : angle of interest type : str Returns ------- angle : the angle obtained from the SIAF. type : float """ siaf = pysiaf.Siaf(instrument) meta = siaf[aperture] angle = getattr(meta, angle_name) return angle
def RADec_To_XY(ra, dec, array_name, attitude_matrix): """Translate backwards, RA, Dec to V2, V3. If a distortion reference file is provided, use that. Otherwise fall back to pysiaf. Parameters: ----------- ra : float Right ascention value, in degrees, to be translated. dec : float Declination value, in degrees, to be translated. Returns: -------- pixelx : float X coordinate value in the aperture corresponding to the input location pixely : float Y coordinate value in the aperture corresponding to the input location """ siaf = pysiaf.Siaf('nircam')[array_name] loc_v2, loc_v3 = pysiaf.utils.rotations.getv2v3(attitude_matrix, ra, dec) pixelx, pixely = siaf.tel_to_sci(loc_v2, loc_v3) # Subtract 1 from SAIF-derived results since SIAF works in a 1-indexed coord system pixelx -= 1 pixely -= 1 return pixelx, pixely
def read_aperture(mode='slit', verbose=True): ''' Function that loads and returns the right SIAF aperture for LRS as specified in the 'mode' parameter: 'slit' or 'slitless' ''' if verbose: print("Pysiaf uses PRD version {}".format(pysiaf.JWST_PRD_VERSION)) # check that the mode is a valid option assert (mode in ['slit', 'slitless' ]), "Mode not supported. Please use 'slit' or 'slitless'." # Read in the SIAF file using PySiaf instrument = 'MIRI' siaf = pysiaf.Siaf(instrument) # Load in the aperture corresponding to the specified mode: if (mode == 'slit'): ap = siaf['MIRIM_SLIT'] elif (mode == 'slitless'): ap = siaf['MIRIM_SLITLESSPRISM'] else: raise IOError('Mode not supported!') return ap
def XY_To_RADec(pixelx, pixely, array_name, attitude_matrix): """Translate a given x, y location on the detector to RA, Dec. If a distortion reference file is provided, use that. Otherwise fall back to using pysiaf. Parameters: ----------- pixelx : float X coordinate value in the aperture pixely : float Y coordinate value in the aperture Returns: -------- ra : float Right ascention value in degrees dec : float Declination value in degrees ra_str : str Right ascention value in HH:MM:SS dec_str : str Declination value in DD:MM:SS """ siaf = pysiaf.Siaf('nircam')[array_name] # Use SIAF to do the calculations #In this case, add 1 to the input pixel values # since SIAF works in a 1-indexed coordinate system. loc_v2, loc_v3 = siaf.sci_to_tel(pixelx + 1, pixely + 1) ra, dec = pysiaf.utils.rotations.pointing(attitude_matrix, loc_v2, loc_v3) return ra, dec
def v2v3_to_pixel(ap_obs, v2_obj, v3_obj, frame='det'): """V2/V3 to pixel coordinates Convert object V2/V3 coordinates into detector pixel position. Parameters ========== ap_obs : str Name of observed aperture (e.g., NRCA5_FULL) v2_obj : ndarray V2 locations of stellar sources. v3_obj : ndarray V3 locations of stellar soruces. Keywords ======== frame : str 'det' or 'sci' coordinate frame. 'det' is always full frame reference. 'sci' is relative to subarray size if not a full frame aperture. """ # xpix and ypix locations siaf = pysiaf.Siaf('NIRCAM') ap_siaf = siaf[ap_obs] if frame=='det': xpix, ypix = ap_siaf.tel_to_det(v2_obj, v3_obj) elif frame=='sci': xpix, ypix = ap_siaf.tel_to_sci(v2_obj, v3_obj) else: raise ValueError("Do not recognize frame keyword value: {}".format(frame)) return (xpix, ypix)
def __init__(self, RA, DEC, INSTRUMENT): self.ra = RA self.dec = DEC self.instrument = INSTRUMENT if 'NIRCam' in self.instrument: siaf = pysiaf.Siaf('NIRCam') dimX, dimY = 51, 1343 rad = 2.5 pixel_scale = siaf. xSweet, ySweet = siaf. , siaf. elif 'MIRI' in self.instrument: siaf = pysiaf.Siaf('MIRI') dimX, dimY = 51, 1343 rad = 2.5 pixel_scale = siaf. xSweet, ySweet = siaf. , siaf.
def test_get_zernike_coeffs_from_smif(): """ Test that the OTE SM Influence function returns expected Hexike coefficients. """ # Create an instance of the OTE linear model otelm = webbpsf.opds.OTE_Linear_Model_WSS() # Case 1: otelm.v2v3 is None, should return None otelm._apply_field_dependence_model() assert ( otelm._apply_field_dependence_model() is None) # Case 2: check coefficient at control point; should return zeros. assert(np.allclose(otelm._get_hexike_coeffs_from_smif(0., 0.), np.asarray([0.] * 9))) # Case 3: dx=1, dy=1, SM Poses all equal to 1 um telfer_zern = [-0.055279643, -0.037571947, -0.80840763, -0.035680581, -0.0036747300, 0.0033910640] # Taken from Telfer's tool # Convert Telfer's Zernikes to Hexikes: hexikes = [-telfer_zern[1], 2.*telfer_zern[0] - (60984./69531.)*telfer_zern[5], telfer_zern[2], (33./25)*telfer_zern[3], (-33./25)*telfer_zern[4], (1386./860.)*telfer_zern[5]] otelm.segment_state[-1, :] = 1.0 assert (np.allclose(otelm._get_hexike_coeffs_from_smif(1.0, 1.0)[3:], hexikes, rtol=1e-3)) # Case 4: test at MIRIM_FP1MIMF field point otelm.ote_ctrl_pt = pysiaf.Siaf('NIRCAM')['NRCA3_FP1'].reference_point('tel') *u.arcsec otelm.v2v3 = pysiaf.Siaf('MIRI')['MIRIM_FP1MIMF'].reference_point('tel') *u.arcsec telfer_zern_mirim_fp1mimf = np.asarray( [-0.25066019, 0.22840080, -0.53545999, -0.024227464, -0.0025191352, 0.00050082553]) # Taken from Telfer's tool # Convert Telfer's Zernikes to Hexikes: hexikes = hexikes = [-telfer_zern_mirim_fp1mimf[1], 2.*telfer_zern_mirim_fp1mimf[0] - (60984./69531.)*telfer_zern_mirim_fp1mimf[5], telfer_zern_mirim_fp1mimf[2], (33./25)*telfer_zern_mirim_fp1mimf[3], (-33./25)*telfer_zern_mirim_fp1mimf[4], (1386./860.)*telfer_zern_mirim_fp1mimf[5]] otelm.segment_state[-1, :] = [300., 400., 100., 200., 5., 0.] dx =-(otelm.v2v3[0] - otelm.ote_ctrl_pt[0]).to(u.rad).value dy = (otelm.v2v3[1] - otelm.ote_ctrl_pt[1]).to(u.rad).value assert (np.allclose(otelm._get_hexike_coeffs_from_smif(dx, dy)[3:], hexikes, rtol=1e-3))
def get_siaf_info(self, instrument_name, aperture_name): """Get v2,v3 reference values and y3yangle for a given aperture Parameters ---------- instrument_name : str JWST instrument name aperture_name : str Aperture name to use in pysiaf (e.g. 'NRCA1_FULL') """ self.siaf = pysiaf.Siaf(instrument_name)[aperture_name]
def make_photom(self): """MAIN FUNCTION""" # Check inputs if self.imaging_throughput_files is None: raise ValueError( "No imaging throughput files specified! Quitting.") if self.detector is None: raise ValueError("No detector specified! Quitting.") self.siaf = pysiaf.Siaf('nircam')['NRC{}_FULL'.format(self.detector)] # Lists of polynomial coefficients from SIAF self.xcoeffs, self.ycoeffs = self.get_coefficients() # Find the nominal pixel area from the SIAF self.pixel_area_a2 = self.get_pixel_area() # Required header keyword outputs sterrad_per_arcsec2 = (1. / 3600. * np.pi / 180.)**2 self.str_per_detector = ( self.detector_width * self.detector_length * self.pixel_area_a2) * sterrad_per_arcsec2 * u.sr self.pixel_area_sr = self.pixel_area_a2.to(u.sr) # Calculate the appropriate gain value to use #self.find_gain() self.find_gain_pre_launch() # Calculations for imaging filters img_tab = self.imaging_calibrations(self.imaging_throughput_files) # Now do the calculations for the grisms if self.grism_throughput_files is not None: gfiles = self.read_listfile(self.grism_throughput_files) grism_table = self.grism_cal(gfiles) # Combine the imaging and grism photom tables print(grism_table.shape, grism_table[0].shape) print(img_tab.shape, img_tab[0].shape) photom_table = np.append(img_tab, grism_table) print(photom_table.shape, photom_table[0].shape) else: photom_table = img_tab # Get module name from the detector name input self.module = self.detector[0].upper() # Now you should be ready to write out the reference file if self.photom_outfile is None: self.photom_outfile = 'NIRCam_{}_photom.fits'.format(self.detector) self.save_photom_model(photom_table, self.photom_outfile)
def get_instance(instrument): """Return an instance of a pysiaf.Siaf object for the given instrument Parameters ---------- instrument : str Name of instrument Returns ------- siaf : pysiaf.Siaf Siaf object for the requested instrument """ if instrument.lower() == 'nircam': print("NOTE: Using pre-delivery SIAF data for {}".format(instrument)) siaf_instrument = 'NIRCam' pre_delivery_dir = os.path.join(JWST_DELIVERY_DATA_ROOT, 'NIRCam') siaf = pysiaf.Siaf(siaf_instrument, basepath=pre_delivery_dir) else: siaf_instrument = instrument siaf = pysiaf.Siaf(siaf_instrument) return siaf
def Tel2Sci_info(channel, coords, output="Sci"): """Telescope coords converted to Science coords Returns the detector name and position associated with input coordinates. Parameters ---------- channel : str 'SW' or 'LW' coords : tuple Telescope coordinates (V2,V3) in arcsec. output : str Type of desired output coordinates. * Det: pixels, in raw detector read out axes orientation * Sci: pixels, in conventional DMS axes orientation * Idl: arcsecs relative to aperture reference location. * Tel: arcsecs V2,V3 """ V2, V3 = coords # Figure out the detector and pixel position for some (V2,V3) coord # mysiaf = webbpsf.webbpsf_core.SIAF('NIRCam') mysiaf = pysiaf.Siaf('NIRCam') swa = ['A1', 'A2', 'A3', 'A4'] swb = ['B1', 'B2', 'B3', 'B4'] lwa = ['A5'] lwb = ['B5'] detnames = swa + swb if 'SW' in channel else lwa + lwb apnames = ['NRC'+det+'_FULL' for det in detnames] # Find center positions for each apname cens = [] for apname in apnames: ap = mysiaf[apname] cens.append(ap.Tel2Sci(V2, V3)) cens = np.array(cens) # Select that with the closest position dist = np.sqrt((cens[:,0]-1024)**2 + (cens[:,1]-1024)**2) ind = np.where(dist==dist.min())[0][0] # Find detector "science" coordinates detector = detnames[ind] apname = apnames[ind] ap = mysiaf[apname] detector_position = ap.convert(V2, V3, frame_from='Tel', frame_to=output) return detector, detector_position
def radec_to_v2v3(coord_objs, siaf_ref_name, coord_ref, pa_ref, base_off=(0, 0), dith_off=(0, 0)): """RA/Dec to V2/V3 Convert a series of RA/Dec positions to telescope V2/V3 coordinates (in arcsec). Parameters ---------- coord_objs : tuple (RA, Dec) positions (deg), where RA and Dec are numpy arrays. siaf_ref_name : str Reference SIAF name (e.g., 'NRCALL_FULL') coord_ref : list or tuple RA and Dec towards which reference SIAF points pa_ref : float Position angle of reference SIAF Keywords -------- base_off : list or tuple X/Y offset of overall aperture offset (see APT pointing file) dither_off : list or tuple Additional offset from dithering (see APT pointing file) """ # SIAF object setup nrc_siaf = pysiaf.Siaf('NIRCam') siaf_ref = nrc_siaf[siaf_ref_name] # RA and Dec of ap ref location and the objects in the field ra_ref, dec_ref = coord_ref ra_obj, dec_obj = coord_objs # Field offset as specified in APT Special Requirements x_off, y_off = (base_off[0] + dith_off[0], base_off[1] + dith_off[1]) # V2/V3 reference location aligned with RA/Dec reference v2_ref, v3_ref = np.array(siaf_ref.reference_point('tel')) # Attitude correction matrix relative to NRCALL_FULL aperture att = pysiaf.utils.rotations.attitude(v2_ref - x_off, v3_ref + y_off, ra_ref, dec_ref, pa_ref) # Convert all RA/Dec coordinates into V2/V3 positions for objects v2_obj, v3_obj = pysiaf.utils.rotations.getv2v3(att, ra_obj, dec_obj) return (v2_obj, v3_obj)
def aperture_size_check(mast_dicts, instrument_name, aperture_name): """Check that the aperture size in a science file is consistent with what is listed in the SUBARRAY header keyword. The motivation for this check comes from NIRCam ASIC Tuning data, where file apertures are listed as FULL, but the data are in fact SUBSTRIPE256. Note that at the moment this function will only work for a subset of apertures, because the mapping of SUBARRAY header keyword value to pysiaf-recognized aperture name is not always straightforward. Initially, this function is being built only to support checking files listed as full frame. Parameters ---------- mast_dicts : list List of file metadata dictionaries, as returned from a MAST query instrument_name : str JWST instrument name aperture_name : str Name of the aperture, in order to load the proper SIAF information Returns ------- consistent_files : list List of metadata dictionaries where the array size in the metadata matches that retrieved from SIAF """ consistent_files = [] siaf = pysiaf.Siaf(instrument_name) # If the basic formula for aperture name does not produce a name recognized by # pysiaf, then skip the check and assume the file is ok. This should only be the # case for lesser-used apertures. For our purposes here, where we are focusing # on full frame apertures, it should be ok. try: siaf_ap = siaf[aperture_name] except KeyError: consistent_files.extend(mast_dicts) return consistent_files # Most cases will end up here. Compare SIAF aperture size to that in the metadata for mast_dict in mast_dicts: array_size_y, array_size_x = mast_dict['subsize2'], mast_dict[ 'subsize1'] if ((array_size_y == siaf_ap.YSciSize) & (array_size_x == siaf_ap.XSciSize)): consistent_files.append(mast_dict) return consistent_files
def get_instance(instrument): """Return an instance of a pysiaf.Siaf object for the given instrument Parameters ---------- instrument : str Name of instrument Returns ------- siaf : pysiaf.Siaf Siaf object for the requested instrument """ siaf = pysiaf.Siaf(instrument) return siaf
def fromsiaf(apername): siaf=pysiaf.Siaf('MIRI') thissiaf=siaf[apername] v2ref,v3ref=thissiaf.V2Ref,thissiaf.V3Ref xvert=np.array([thissiaf.XIdlVert1,thissiaf.XIdlVert2,thissiaf.XIdlVert3,thissiaf.XIdlVert4]) yvert=np.array([thissiaf.YIdlVert1,thissiaf.YIdlVert2,thissiaf.YIdlVert3,thissiaf.YIdlVert4]) # Convert the Ideal frame corner coordinates to v2,v3 corner coordinates v2vert,v3vert=mt.Idealtov2v3(xvert,yvert,apername) # Convert from SIAF order (lower-left, lower-right, upper-right, upper-left) to # lower-left, upper-left, upper-right, lower-right v2vert=v2vert[[0,3,2,1]] v3vert=v3vert[[0,3,2,1]] return v2vert,v3vert
def _get_default_siaf(instrument, aper_name): """ Store the default SIAF values for distortion and rotation """ # Create new naming because SIAF requires special capitalization if instrument == "NIRCAM": siaf_name = "NIRCam" elif instrument == "NIRSPEC": siaf_name = "NIRSpec" else: siaf_name = instrument # Select a single SIAF aperture siaf = pysiaf.Siaf(siaf_name) aper = siaf.apertures[aper_name] return aper
def test_low_medium_high_background_value(): """Test that the proper integrated background value is calculated for a given filter and level""" ra = 57.2 dec = -27.6 filt_waves = np.array([1., 2., 2.5, 2.6, 2.7, 2.8, 2.9, 3.0, 3.1, 4., 5.]) filt_thru = np.array([0., 0., 0., 1., 1., 1., 1., 1., 0., 0., 0.]) siaf = pysiaf.Siaf('nircam')['NRCA1_FULL'] bkgd_high = backgrounds.low_medium_high_background_value( ra, dec, "high", filt_waves, filt_thru, siaf) bkgd_med = backgrounds.low_medium_high_background_value( ra, dec, "medium", filt_waves, filt_thru, siaf) bkgd_low = backgrounds.low_medium_high_background_value( ra, dec, "low", filt_waves, filt_thru, siaf) assert bkgd_high > bkgd_med assert bkgd_med > bkgd_low
def get_wcs(self, aperture, useafter): """ Query the SIAF database file and get WCS values. Given an ``APERTURE_NAME`` and a ``USEAFTER`` date query the SIAF database and extract the following keywords: ``V2Ref``, ``V3Ref``, ``V3IdlYAngle``, ``VIdlParity``, ``XSciRef``, ``YSciRef``, ``XSciScale``, ``YSciScale``, ``XIdlVert1``, ``XIdlVert2``, ``XIdlVert3``, ``XIdlVert4``, ``YIdlVert1``, ``YIdlVert2``, ``YIdlVert3``, ``YIdlVert4`` Parameters ---------- aperture : str The name of the aperture to retrieve. useafter : str The date of observation (``model.meta.date``) Returns ------- siaf : namedtuple The SIAF namedtuple with values from the PRD database. """ instrument = INSTRUMENT_MAP[aperture[:3].lower()] siaf = pysiaf.Siaf(instrument, basepath=self._source) aperture = siaf[aperture.upper()] # Fill out the Siaf vertices = tuple(getattr(aperture, key) for key in SIAF_VERTICIES) siaf = SIAF(v2_ref=aperture.V2Ref, v3_ref=aperture.V3Ref, v3yangle=aperture.V3IdlYAngle, vparity=aperture.VIdlParity, crpix1=aperture.XSciRef, crpix2=aperture.YSciRef, cdelt1=aperture.XSciScale, cdelt2=aperture.YSciScale, vertices_idl=vertices) return siaf
"""Script adaptation of make_nircam_siaf_figures.ipynb that also includes saving figures to PDF. """ import os import matplotlib.pyplot as pl import pysiaf show_plot = True save_plot = True pl.close('all') plot_dir = os.environ['HOME'] # Load NIRCam SIAF instrument = 'NIRCam' siaf = pysiaf.Siaf(instrument) #################################################################################################### # Create a plot that shows the SUB160 apertures on Module B. # Figure setup fig = pl.figure(figsize=(8, 8), facecolor='w', edgecolor='k') # Plot the outline of each aperture, with reference points marked (plus symbol is default). # Plotting blue and red lines separately, blue for short wavelength and red for long wavelength. for aperture_name in [ 'NRCB1_SUB160', 'NRCB2_SUB160', 'NRCB3_SUB160', 'NRCB4_SUB160' ]: aperture = siaf[aperture_name] aperture.plot(color='b', fill_color='b', fill_alpha=0.3, mark_ref=True) for aperture_name in ['NRCB5_SUB160']: aperture = siaf[aperture_name] aperture.plot(color='r', fill_color='r', fill_alpha=0.3, mark_ref=True)
def ap_radec(ap_obs, ap_ref, coord_ref, pa_ref, base_off=(0,0), dith_off=(0,0), get_cenpos=True, get_vert=False): """Aperture reference point(s) RA/Dec Return RA/Dec associated with the reference point (usually center) of a specific aperture. Parameters ---------- ap_obs : str Name of observed aperture (e.g., NRCA5_FULL) ap_ref : str Name of reference aperture (e.g., NRCALL_FULL) coord_ref : tuple or list Center position of reference aperture (RA/Dec deg) pa_ref : float Position angle of ap_ref Keywords -------- base_off : list or tuple X/Y offset of overall aperture offset (see APT pointing file) dither_off : list or tuple Additional offset from dithering (see APT pointing file) get_cenpos : bool Return aperture reference location coordinates? get_vert: bool Return closed polygon vertices (useful for plotting)? """ if (get_cenpos==False) and (get_vert==False): _log.warning("Neither get_cenpos nor get_vert were set to True. Nothing to return.") return nrc_siaf = pysiaf.Siaf('NIRCAM') ap_siaf = nrc_siaf[ap_ref] ap_siaf_obs = nrc_siaf[ap_obs] # RA and Dec of ap ref location and the objects in the field ra_ref, dec_ref = coord_ref # Field offset as specified in APT Special Requirements x_off, y_off = (base_off[0] + dith_off[0], base_off[1] + dith_off[1]) # V2/V3 reference location aligned with RA/Dec reference v2_ref, v3_ref = np.array(ap_siaf.reference_point('tel')) # Attitude correction matrix relative to reference aperture att = pysiaf.utils.rotations.attitude(v2_ref-x_off, v3_ref+y_off, ra_ref, dec_ref, pa_ref) # Get V2/V3 position of observed SIAF aperture and convert to RA/Dec if get_cenpos==True: v2_obs, v3_obs = ap_siaf_obs.reference_point('tel') ra_obs, dec_obs = pysiaf.utils.rotations.pointing(att, v2_obs, v3_obs) cen_obs = (ra_obs, dec_obs) # Get V2/V3 vertices of observed SIAF aperture and convert to RA/Dec if get_vert==True: v2_vert, v3_vert = ap_siaf_obs.closed_polygon_points('tel', rederive=False) ra_vert, dec_vert = pysiaf.utils.rotations.pointing(att, v2_vert, v3_vert) vert_obs = (ra_vert, dec_vert) if (get_cenpos==True) and (get_vert==True): return cen_obs, vert_obs elif get_cenpos==True: return cen_obs elif get_vert==True: return vert_obs else: _log.warning("Neither get_cenpos nor get_vert were set to True. Nothing to return.") return
aperture_collection = pysiaf.ApertureCollection(aperture_dict) emulate_delivery = True if emulate_delivery: pre_delivery_dir = os.path.join(JWST_DELIVERY_DATA_ROOT, instrument) if not os.path.isdir(pre_delivery_dir): os.makedirs(pre_delivery_dir) # write the SIAF files to disk filenames = pysiaf.iando.write.write_jwst_siaf(aperture_collection, basepath=pre_delivery_dir, file_format=['xml', 'xlsx']) pre_delivery_siaf = pysiaf.Siaf(instrument, basepath=pre_delivery_dir) # pre_delivery_siaf = pysiaf.Siaf(instrument, filename=filenames[0]) compare_against_prd = True compare_against_cdp7b = True for compare_to in [pysiaf.JWST_PRD_VERSION, 'outdated pre-delivery']: if compare_to == 'outdated pre-delivery': ref_siaf = pysiaf.Siaf(instrument, filename=os.path.join( pre_delivery_dir, 'NIRCam_SIAF_outdated.xml')) else: # compare new SIAF with PRD version ref_siaf = pysiaf.Siaf(instrument)
def contamVerify(RA, DEC, INSTRUMENT, APAlist, binComp=[], PDF='', web=False): """ Generates a PDF file of figures displaying a simulation of the science image for any given observation using the parameters provided. Parameter(s) ------------ RA : str The Right Ascension of your target in HH:MM:SS DEC : str The Declination of your target in DD:MM:SS INSTRUMENT : str The instrument you are observing with (case-sensitive). The software currently supports: 'MIRI', 'NIRISS', 'NIRCam F322W2', 'NIRCam F444W' APAlist : list A list of Aperture Position Angle(s). Element(s) must be in integers. Example 1: [1, 25, 181, 205] Example 2: [25] binComp : list A list containing parameters of a missing companion that is not in the 2MASS IRSA point-source catalog. The format is: [RA (arcseconds), DEC (arcseconds), J mag, H mag, K mag] [string, string, integer, integer, integer] PDF : string The path to where the PDF file will be saved. If left blank, the PDF file will be saved in your current working directory. Example: 'path/to/my/file.pdf' web : boolean Makes it easier to integrate it onto the website. Leave this as false, unless you're running this in app_exoctk.py Returns ------- A .PDF file containing a simulation of the FOV of your target in the science coordinate system. Some things to consider when reading the figures: 1. The target is circled in red 2. Stellar temperatures of all sources are plotted by color 3. The gray region oulined in blue represents the aperture for the given instrument. 4. The blue square represents the readout region, or the "origin" """ print('Generating FOV...') # Decimal degrees --> HMSDMS for Irsa.query_region() targetcrd = crd.SkyCoord(ra=RA, dec=DEC, unit='deg').to_string('hmsdms') targetRA, targetDEC = RA, DEC # Querying for neighbors with 2MASS IRSA's fp_psc (point-source catalog) rad = 2.5 print('Querying for point-sources within {} arcminutes...'.format( str(rad))) info = Irsa.query_region(targetcrd, catalog='fp_psc', spatial='Cone', radius=rad * u.arcmin) # Coordinates of all stars in FOV, including target allRA = info['ra'].data.data allDEC = info['dec'].data.data # Initiating a dictionary to hold all relevant star information stars = {} stars['RA'], stars['DEC'] = allRA, allDEC print('Total point-sources found in region: {}'.format(len(stars['RA']))) # Finding the target using relative distances sindRA = (targetRA - stars['RA']) * np.cos(targetDEC) cosdRA = targetDEC - stars['DEC'] distance = np.sqrt(sindRA**2 + cosdRA**2) targetIndex = np.argmin(distance) # Appending missing companion to the above lists (if any) if binComp != []: print('Adding missing companion...') bb = binComp[0] / 3600 / np.cos(allDEC[targetIndex] * deg2rad) allRA = np.append(allRA, (allRA[targetIndex] + bb)) allDEC = np.append(allDEC, (allDEC[targetIndex] + binComp[1] / 3600)) Jmag = np.append(Jmag, binComp[2]) Hmag = np.append(Kmag, binComp[3]) Kmag = np.append(Kmag, binComp[4]) J_Hobs = Jmag - Hmag H_Kobs = Hmag - Kmag # Restoring model parameters modelParam = readsav(os.path.join(TRACES_PATH, 'NIRISS', 'modelsInfo.sav'), verbose=False) models = modelParam['models'] modelPadX = modelParam['modelpadx'] modelPadY = modelParam['modelpady'] dimXmod = modelParam['dimxmod'] dimYmod = modelParam['dimymod'] jhMod = modelParam['jhmod'] hkMod = modelParam['hkmod'] teffMod = modelParam['teffmod'] # JHK bands of all stars in FOV, including target Jmag = info['j_m'].data.data Hmag = info['h_m'].data.data Kmag = info['k_m'].data.data # J-H band, H-K band. This will be used to derive the stellar Temps later J_Hobs = Jmag - Hmag H_Kobs = Hmag - Kmag # Number of stars nStars = stars['RA'].size # Find/assign Teff of each star print('Calculating effective temperatures...') starsT = np.empty(nStars) for j in range(nStars): color_separation = (J_Hobs[j] - jhMod)**2 + (H_Kobs[j] - hkMod)**2 min_separation_ind = np.argmin(color_separation) starsT[j] = teffMod[min_separation_ind] # Record keeping stars['Temp'] = starsT # Initiating a dictionary for customizability apertures = {} apertures['NIRISS'] = ['NIS_SOSSFULL', 'NIS_SOSSFULL'] apertures['NIRCam F444W'] = ['NRCA5_GRISM256_F444W', 'NRCA5_FULL'] apertures['NIRCam F322W2'] = ['NRCA5_GRISM256_F322W2', 'NRCA5_FULL'] apertures['MIRI'] = ['MIRIM_SLITLESSPRISM', 'MIRIM_FULL'] # Instantiate SIAF object siaf = pysiaf.Siaf(INSTRUMENT.split(' ')[0]) aper = siaf.apertures[apertures[INSTRUMENT][0]] full = siaf.apertures[apertures[INSTRUMENT][1]] # DET_targ -> TEL_targ -> get attitude matrix for target # -> TEL_neighbor -> DET_neighbor -> SCI_neighbor print('Converting Sky --> Science coordinates...') xSweet, ySweet = aper.reference_point('det') v2targ, v3targ = aper.det_to_tel(xSweet, ySweet) contam = {} if not web: filename = 'contam_{}_{}_{}.pdf'.format(RA, DEC, INSTRUMENT) defaultPDF = os.path.join(os.getcwd(), filename).replace(' ', '_') PDF = defaultPDF if PDF == '' else PDF elif web: filename = 'contam_{}_{}_{}.pdf'.format(RA, DEC, INSTRUMENT) PDF = os.path.join(TRACES_PATH, filename) print('Saving figures to: {}'.format(PDF)) print('This will take a second...') pdfobj = PdfPages(PDF) for APA in APAlist: attitude = pysiaf.utils.rotations.attitude_matrix( v2targ, v3targ, targetRA, targetDEC, APA) xdet, ydet = [], [] xsci, ysci = [], [] for starRA, starDEC in zip(stars['RA'], stars['DEC']): # Get the TEL coordinates of each star using the attitude # matrix of the target V2, V3 = pysiaf.utils.rotations.sky_to_tel(attitude, starRA, starDEC) # Convert to arcsec and turn to a float V2, V3 = V2.to(u.arcsec).value, V3.to(u.arcsec).value XDET, YDET = aper.tel_to_det(V2, V3) XSCI, YSCI = aper.det_to_sci(XDET, YDET) xdet.append(XDET) ydet.append(YDET) xsci.append(XSCI) ysci.append(YSCI) XDET, YDET = np.array(xdet), np.array(ydet) XSCI, YSCI = np.array(xsci), np.array(ysci) starsAPA = {'xdet': XDET, 'ydet': YDET, 'xsci': XSCI, 'ysci': YSCI} # Finding indexes of neighbor sources that land on detector rows, cols = full.corners('det') minrow, maxrow = rows.min(), rows.max() mincol, maxcol = cols.min(), cols.max() inFOV = [] for star in range(0, nStars): x, y = starsAPA['xdet'][star], starsAPA['ydet'][star] if (mincol < x) & (x < maxcol) & (minrow < y) & (y < maxrow): inFOV.append(star) inFOV = np.array(inFOV) # Making final plot fig = plt.figure(figsize=(15, 15)) aper.plot(frame='sci', fill_color='gray', color='blue') plt.scatter(XSCI[targetIndex], YSCI[targetIndex], s=400, lw=1.5, facecolor='gray', edgecolor='red') plotTemps(starsT[inFOV], XSCI[inFOV], YSCI[inFOV]) aper.plot_frame_origin(frame='sci', which='sci') # Fine-tune trace lengths start, stop = traceLength(INSTRUMENT) # Plotting the trace footprints for x, y in zip(XSCI[inFOV], YSCI[inFOV]): if 'F322W2' in INSTRUMENT: plt.plot([x - stop, x + start], [y, y], lw=40, color='white', alpha=0.2) plt.plot([x - stop, x + start], [y, y], lw=2., color='white') elif 'F444W' in INSTRUMENT: plt.plot([x - start, x + stop], [y, y], lw=40, color='white', alpha=0.2) plt.plot([x - start, x + stop], [y, y], lw=2., color='white') else: plt.plot([x, x], [y - stop, y + start], lw=40, color='white', alpha=0.2) plt.plot([x, x], [y - stop, y + start], lw=2., color='white') # Labeling aperstr = str(aper.AperName.replace('_', ' ')) tx, ty = str(round(XSCI[targetIndex])), str(round(YSCI[targetIndex])) plt.title( 'The FOV in SCIENCE coordinates at APA {}$^o$'.format(str(APA)) + '\n' + '{}'.format(aperstr) + '\n' + 'Target (X,Y): {}, {}'.format(tx, ty), fontsize=20) # Adding to PDF pdfobj.savefig(fig, bbox_inches='tight') pdfobj.close() if web: return PDF