def _load_itm_library(library_file): """Load ITM FITS file Parameters ---------- library_path : str Path pointing to the location of the PSF library Returns ------- library : photutils.griddedPSFModel Object containing PSF library """ data = fits.getdata(library_file) hdr = fits.getheader(library_file) if data.shape == (2048, 2048): # Normalize the data data /= np.sum(data) # Add PSF location and oversampling keywords hdr['DET_YX0'] = ('(1023, 1023)', "The #0 PSF's (y,x) detector pixel position") hdr['OVERSAMP'] = (1, 'Oversampling factor for FFTs in computation') # Convert to HDUList and create library phdu = fits.PrimaryHDU(data, hdr) hdulist = fits.HDUList(phdu) library = to_griddedpsfmodel(hdulist) return library else: raise ValueError( 'Expecting ITM data of size (2048, 2048), not {}'.format( data.shape))
def get_gridded_segment_psf_library_list(instrument, detector, filtername, library_path, pupilname="CLEAR"): """Find the filenames for the appropriate gridded segment PSF libraries and read them into griddedPSFModel objects Parameters ---------- instrument : str Name of instrument the PSFs are from detector : str Name of the detector within ```instrument``` filtername : str Name of filter used for PSF library creation library_path : str Path pointing to the location of the PSF library pupilname : str, optional Name of pupil wheel element used for PSF library creation. Default is "CLEAR". Returns: -------- libraries : list of photutils.griddedPSFModel List of object containing segment PSF libraries """ logger = logging.getLogger( 'mirage.psf.segment_psfs.get_gridded_segment_psf_library_list') library_list = get_segment_library_list(instrument, detector, filtername, library_path, pupil=pupilname) logger.info("Segment PSFs will be generated using:") for filename in library_list: logger.info(os.path.basename(filename)) libraries = [] for filename in library_list: with fits.open(filename) as hdulist: # hdr = hdulist[0].header # d = hdulist[0].data # # data = d[0][0] # phdu = fits.PrimaryHDU(data, header=hdr) # hdulist = fits.HDUList(phdu) lib_model = to_griddedpsfmodel(hdulist) libraries.append(lib_model) return libraries
def test_to_gridded_psfmodel(test_library_file): """Test that the example library file can be correctly loaded as a GriddedPSFModel using the webbpsf.utils.to_griddedpsfmodel function. Note that this is more a test of webbpsf than of MIRaGe, but it is required for MIRaGe to work! """ with fits.open(test_library_file) as hdulist: lib_model = to_griddedpsfmodel(hdulist) assert isinstance(lib_model, photutils.psf.models.GriddedPSFModel), \ 'Segment PSF library not created correctly' assert lib_model.grid_xypos == [(1023.5, 1023.5)], \ 'Segment PSF library not created correctly' assert lib_model.oversampling == 1, \ 'Segment PSF library not created correctly' for k in ['segid', 'segname', 'xtilt', 'ytilt']: assert k in list(lib_model.meta.keys()), \ 'Segment PSF library not created correctly' assert lib_model.meta['segid'][0] == 12, \ 'Segment PSF library not created correctly' assert lib_model.data.shape == (1, 1024, 1024), \ 'Segment PSF library not created correctly'
def get_gridded_psf_library(instrument, detector, filtername, pupilname, wavefront_error, wavefront_error_group, library_path): """Find the filename for the appropriate gridded PSF library and read it in to a griddedPSFModel Parameters ---------- instrument : str Name of instrument the PSFs are from detector : str Name of the detector within ```instrument``` filtername : str Name of filter used for PSF library creation pupilname : str Name of pupil wheel element used for PSF library creation wavefront_error : str Wavefront error. Can be 'predicted' or 'requirements' wavefront_error_group : int Wavefront error realization group. Must be an integer from 0 - 9. library_path : str Path pointing to the location of the PSF library Returns: -------- library : photutils.griddedPSFModel Object containing PSF library """ logger = logging.getLogger( 'mirage.psf.psf_selection.get_gridded_psf_library') # First, as a way to save time, let's assume a file naming convention # and search for the appropriate file that way. If we find a match, # confirm the properties of the file via the header. This way we don't # need to open and examine every file in the gridded library, which # saves at least a handful of seconds. if instrument.lower() == 'fgs': default_file_pattern = '{}_{}_fovp*_samp*_npsf*_{}_realization{}.fits'.format( instrument.lower(), detector.lower(), wavefront_error.lower(), wavefront_error_group) else: # NIRISS gridded library names don't follow standard filter/pupil rules. # The filenames are all <filter>_<clear>, where <clear> is clear if it # is in the filter wheel and clearp if in the pupil wheel. if instrument.lower() == 'niriss': if filtername.lower() == 'clear': filename_filter = pupilname filename_pupil = filtername elif pupilname.lower() == 'clearp': filename_filter = filtername filename_pupil = pupilname # filter=clear, pupil=nrm is currently not allowed if pupilname.lower() == 'nrm': filename_filter = filtername filename_pupil = 'mask_nrm' elif instrument.lower() == 'nircam': filename_filter = filtername filename_pupil = pupilname if 'GDHS' not in pupilname else 'clear' # for WFSC team practice purposes we don't produce DHS "PSFs" default_file_pattern = '{}_{}_{}_{}_fovp*_samp*_npsf*_{}_realization{}.fits'.format( instrument.lower(), detector.lower(), filename_filter.lower(), filename_pupil.lower(), wavefront_error.lower(), wavefront_error_group) default_matches = glob(os.path.join(library_path, default_file_pattern)) library_file = None if len(default_matches) == 1: library_file = confirm_gridded_properties( default_matches[0], instrument, detector, filtername, pupilname, wavefront_error, wavefront_error_group, library_path) # If the above search found no matching files, or multiple matching # files (based only on filename), or if the matching file's gridded # PSF model properties don't match what's expected, then resort to # opening and examining all files in the library. if library_file is None: library_file = get_library_file(instrument, detector, filtername, pupilname, wavefront_error, wavefront_error_group, library_path) logger.info("PSFs will be generated using: {}".format( os.path.abspath(library_file))) lib_head = fits.getheader(library_file) itm_sim = lib_head.get('ORIGIN', '') == 'ITM' if not itm_sim: try: library = to_griddedpsfmodel(library_file) except OSError: logger.error("OSError: Unable to open {}.".format(library_file)) else: # Handle input ITM images library = _load_itm_library(library_file) # Check that the gridded PSF library is normalized as expected correct_norm, reason = check_normalization(library) if correct_norm: return library else: raise ValueError( ("Gridded PSF library in {} appears to be improperly normalized." "The total signal in a PSF is {}".format(library_file, reason)))
def get_gridded_psf_library(instrument, detector, filtername, pupilname, wavefront_error, wavefront_error_group, library_path): """Find the filename for the appropriate gridded PSF library and read it in to a griddedPSFModel Parameters ---------- instrument : str Name of instrument the PSFs are from detector : str Name of the detector within ```instrument``` filtername : str Name of filter used for PSF library creation pupilname : str Name of pupil wheel element used for PSF library creation wavefront_error : str Wavefront error. Can be 'predicted' or 'requirements' wavefront_error_group : int Wavefront error realization group. Must be an integer from 0 - 9. library_path : str Path pointing to the location of the PSF library Returns: -------- library : photutils.griddedPSFModel Object containing PSF library """ # First, as a way to save time, let's assume a file naming convention # and search for the appropriate file that way. If we find a match, # confirm the properties of the file via the header. This way we don't # need to open and examine every file in the gridded library, which # saves at least a handful of seconds. if instrument.lower() == 'fgs': default_file_pattern = '{}_{}_fovp*_samp*_npsf*_{}_realization{}.fits'.format( instrument.lower(), detector.lower(), wavefront_error.lower(), wavefront_error_group) else: default_file_pattern = '{}_{}_{}_{}_fovp*_samp*_npsf*_{}_realization{}.fits'.format( instrument.lower(), detector.lower(), filtername.lower(), pupilname.lower(), wavefront_error.lower(), wavefront_error_group) default_matches = glob(os.path.join(library_path, default_file_pattern)) library_file = None if len(default_matches) == 1: library_file = confirm_gridded_properties( default_matches[0], instrument, detector, filtername, pupilname, wavefront_error, wavefront_error_group, library_path) # If the above search found no matching files, or multiple matching # files (based only on filename), or if the matching file's gridded # PSF model properties don't match what's expected, then resort to # opening and examining all files in the library. if library_file is None: library_file = get_library_file(instrument, detector, filtername, pupilname, wavefront_error, wavefront_error_group, library_path) print("PSFs will be generated using: {}".format( os.path.abspath(library_file))) try: library = to_griddedpsfmodel(library_file) except KeyError: # Handle input ITM images itm_sim = fits.getval(library_file, 'ORIGIN') if itm_sim: library = _load_itm_library(library_file) except OSError: print("OSError: Unable to open {}.".format(library_file)) return library
def get_gridded_psf_library(instrument, detector, filtername, pupilname, wavefront_error, wavefront_error_group, library_path): """Find the filename for the appropriate gridded PSF library and read it in to a griddedPSFModel Parameters ---------- instrument : str Name of instrument the PSFs are from detector : str Name of the detector within ```instrument``` filtername : str Name of filter used for PSF library creation pupilname : str Name of pupil wheel element used for PSF library creation wavefront_error : str Wavefront error. Can be 'predicted' or 'requirements' wavefront_error_group : int Wavefront error realization group. Must be an integer from 0 - 9. library_path : str Path pointing to the location of the PSF library Returns: -------- library : photutils.griddedPSFModel Object containing PSF library """ logger = logging.getLogger( 'mirage.psf.psf_selection.get_gridded_psf_library') # In the default case, we expect the (total PSF signal)/ (oversample factor**2) # to be close to 1.0. In certain cases for NIRISS, this expectation is lower by # some factor. Here we set the defaul factor to lower expectations to 1.0 # (i.e. don't lower expectations). grid_min_factor = 1.0 # First, as a way to save time, let's assume a file naming convention # and search for the appropriate file that way. If we find a match, # confirm the properties of the file via the header. This way we don't # need to open and examine every file in the gridded library, which # saves at least a handful of seconds. if instrument.lower() == 'fgs': default_file_pattern = '{}_{}_fovp*_samp*_npsf*_{}_realization{}.fits'.format( instrument.lower(), detector.lower(), wavefront_error.lower(), wavefront_error_group) else: # NIRISS gridded library names don't follow standard filter/pupil rules. # The filenames are all <filter>_<clear>, where <clear> is clear if it # is in the filter wheel and clearp if in the pupil wheel. if instrument.lower() == 'niriss': if filtername.lower() == 'clear': filename_filter = pupilname filename_pupil = filtername elif pupilname.lower() == 'clearp': # If CLEARP is used, we need to alert the normalization check # below to expect PSFs with a total signal/oversamp**2 lower # than otherwise expected by a factor of ~0.84 filename_filter = filtername filename_pupil = pupilname grid_min_factor = NIRISS_CLEARP_PSF_THROUGHPUT_REDUCTION # filter=clear, pupil=nrm is currently not allowed if pupilname.lower() == 'nrm': # In NRM mode, the ~0.15 throughput factor associated with the # NRM mask is baked into the PSFs from Webbpsf, so we need to # adjust the expectations of the normalization check below. filename_filter = filtername filename_pupil = 'mask_nrm' grid_min_factor = NIRISS_NRM_PSF_THROUGHPUT_REDUCTION elif instrument.lower() == 'nircam': # In the case of a WLP4+WLP8 observation, the gridded PSF library field # of view is small enough (and the PSF is large enough) that a significant # fraction of the flux is outside the field of view (of the PSF "core" library. # In this case, we need to loosen then requirement for the minimum normalized # flux in the array. if ((filtername.lower() == 'wlp4') & (pupilname.lower() == 'wlp8')): grid_min_factor = NIRCAM_WLP12_PSF_THROUGHPUT_REDUCTION filename_filter = filtername filename_pupil = pupilname if 'GDHS' not in pupilname else 'clear' # for WFSC team practice purposes we don't produce DHS "PSFs" default_file_pattern = '{}_{}_{}_{}_fovp*_samp*_npsf*_{}_realization{}.fits'.format( instrument.lower(), detector.lower(), filename_filter.lower(), filename_pupil.lower(), wavefront_error.lower(), wavefront_error_group) default_matches = glob(os.path.join(library_path, default_file_pattern)) library_file = None if len(default_matches) == 1: library_file = confirm_gridded_properties( default_matches[0], instrument, detector, filtername, pupilname, wavefront_error, wavefront_error_group, library_path) # If the above search found no matching files, or multiple matching # files (based only on filename), or if the matching file's gridded # PSF model properties don't match what's expected, then resort to # opening and examining all files in the library. if library_file is None: logger.info(( "No matching gridded PSF library file found based on filename pattern. Checking " "library files' metadata. This will be slower.")) library_file = get_library_file(instrument, detector, filtername, pupilname, wavefront_error, wavefront_error_group, library_path) logger.info("PSFs will be generated using: {}".format( os.path.abspath(library_file))) lib_head = fits.getheader(library_file) itm_sim = lib_head.get('ORIGIN', '') == 'ITM' if not itm_sim: try: library = to_griddedpsfmodel(library_file) except OSError: logger.error("OSError: Unable to open {}.".format(library_file)) else: # Handle input ITM images library = _load_itm_library(library_file) # Check that the gridded PSF library is normalized as expected check_max = PSF_NORM_MAX * grid_min_factor check_min = PSF_NORM_MIN * grid_min_factor correct_norm, reason = check_normalization(library, lower_limit=check_min, upper_limit=check_max, renorm_psfs_above_1=True) if correct_norm: return library else: raise ValueError( ("Gridded PSF library in {} appears to be improperly normalized." "The total signal in a PSF is {}".format(library_file, reason)))