Focus Diverse ePSFs for ACS/WFC#

This notebook currently fails to execute, use as reference only

This notebook highlights how to use the focus_diverse_psfs module within acstools to retrieve empirical, focus-diverse ePSFs for ACS/WFC data. Please see the webtool, ACS ISR 2018-08, and ACS ISR 2023-06 for more details.

Imports#

import os

from astropy.io import fits
from astroquery.mast import Observations

import ipywidgets as widgets
import matplotlib.colors as colors
import matplotlib.pyplot as plt

# import the focus_diverse_psfs functions from acstools 
from acstools.focus_diverse_epsfs import psf_retriever, multi_psf_retriever, interp_epsf

Downloading and Examining a Single Focus-Diverse ePSF file#

Let’s begin with downloading the focus-diverse ePSF FITS file that matches a single observation of our choosing. For this example, we will aim to retrieve the ePSF file for the observation rootname “jds408jsq”, from GO-15445 (PI W. Keel).

Please note that only IPPPSSOOT formats will work (e.g. jds408jsq), and the tool does not support inputs in the form of association IDs or product names (e.g. jds408010 or jds408011).

Make sure to change the variable “download_location” to an existing folder on your local machine.

%%time 

# example of a single retrieval


# Set the download location to the current working directory
download_location = os.path.join(os.getcwd(), 'downloads')
os.makedirs(download_location, exist_ok=True)

# call the psf_retriever function with observation rootname
retrieved_download = psf_retriever('jds408jsq', download_location) 

The direct return of the psf_retriever function is the retrieved file name. The name has the format %ROOTNAME-STDPBF_ACSWFC_%FILTER_%SM_%F.fits, where the variables are:


We can now examine the retrieved FITS file.

# give path to downloaded file
retrieved_filepath = os.path.abspath(retrieved_download)

if not os.path.isfile(retrieved_filepath):
    raise FileNotFoundError(f"Expected file not found at {retrieved_filepath}")

# open the file with astropy.io
with fits.open(retrieved_filepath) as hdu:
    hdu.info() # Display basic information about the file

With the output above, we can see that the dimensions of the file are 101 x 101 x 90. This corresponds to a total of 90 ePSFs, each one with x,y dimensions of 101 x 101. The 90 ePSFs span the range of the two WFC chips and correspond to a 9 x 10 spatial grid (see Figure 1 in ACS ISR 2018-08).

We show these ePSFs below in a widget which allows you to quickly scroll through and get a sense of the spatial differences.

# # First, lets grab the image data from the retrieved FITS file
ePSFs = fits.getdata(retrieved_filepath, ext=0)


def show_ePSF(grid_index):
    plt.imshow(ePSFs[grid_index], cmap='viridis', norm=colors.LogNorm(vmin=1e-4), origin='lower')
    cbar = plt.colorbar()
    cbar.set_label('Fractional Energy')
    
    
widgets.interact(show_ePSF, grid_index=(0, 89, 1))

Batch Downloads#

Here we show how to perform batch downloads for a large set of input rootnames, via two separate methods.


via an Input Text File#

With an input text file of one rootname per line, we can use the multi_psf_retriever function with the “fromTextFile” parameter set to True to batch download the desired focus-diverse ePSFs.

# specify the name of the input text file (with one rootname per line),
# as well as the desired download location for our focus-diverse ePSFs

input_list = 'input_ipsoots.txt'
%%time 

# use the multi_psf_retriever function to retrieve our ePSFs
retrieved_downloads = multi_psf_retriever(input_list, download_location)

print('# of matching files: ', len(retrieved_downloads))

via astroquery#

Alternatively, we can use astroquery to simply grab the rootnames for all ACS/WFC images within a given HST proposal and retrieve the corresponding focus-diverse ePSFs.

Here we provide an example of using astroquery and the multi_psf_retriever function to grab all matching focus-diverse ePSFs for observations of the Leo P galaxy from GO-13376 (PI K. McQuinn).

# # use astroquery to grab ACS/WFC observations from GO-13376 (PI K. McQuinn)
obsTable = Observations.query_criteria(obs_collection='HST', proposal_id="13376", 
                                       instrument_name="ACS/WFC", provenance_name="CALACS")

# retrieve the data products for the above observations
dataProducts = Observations.get_product_list(obsTable)

# filter the data products for just the FLC files from HST, and not the Hubble Advanced Products (HAP) project
dataProducts = dataProducts[(dataProducts['productSubGroupDescription'] == 'FLC') &
                            (dataProducts['type'] == 'S')]

# create a list of corresponding rootnames
obs_rootnames = list(dataProducts['obs_id'])
%%time

# use the multi_psf_retriever function to retrieve our ePSFs 
retrieved_downloads = multi_psf_retriever(obs_rootnames, download_location)

print('# of matching files: ', len(retrieved_downloads))

Further Spatial Interpolations#


Users may be interested in further interpolating the provided ePSF array to any arbitrary (x,y) coordinate. The function interp_epsf() allows us to retrieve this (using bi-linear interpolation).

For example, we can retrieve the ePSF loaded from its FITS file above and interpolate to x,y = (2000,2000) on WFC1, which is near the middle of the detector along the x-axis, and near the top of the WFC1 chip (and the detector overall).

x = 2000
y = 2000
chip = "WFC1"

# get interpolated ePSF in supersampled space
P = interp_epsf(ePSFs, x, y, chip)
plt.imshow(P, cmap='viridis', norm=colors.LogNorm(vmin=1e-4), origin='lower')
plt.title('jds408jsq at x,y = (2000,2000) on WFC1')
plt.colorbar()

By default, the ePSFs come with 4x supersampling. We can output them in detector space by setting the “pixel_space” = True.

# get interpolated ePSF in detector space
P = interp_epsf(ePSFs, x, y, chip,
                pixel_space=True)
plt.imshow(P, cmap='viridis', norm=colors.LogNorm(vmin=1e-4), origin='lower')
plt.title('(2000,2000) on WFC1 (Detector Space)')
plt.colorbar()

Lastly, we can shift the ePSF to any sub-pixel phase by specifying the individual x and y subpixel offsets. The code uses bi-cubic interpolation to perform these sub-pixel phase shifts.

Note that pixel_space must be set to True to use these subpixel offsets. Also note that the code only supports this bi-cubic interpolation to the second decimal place (e.g. subpixel_x = 0.77 and subpixel_y = 0.33)

# # get interpolated ePSF in detector space with specified sub-pixel shifts
P = interp_epsf(ePSFs, x, y, chip,
                pixel_space=True, 
                subpixel_x=0.77, subpixel_y=0.33)

plt.imshow(P, cmap='viridis', norm=colors.LogNorm(vmin=1e-4), origin='lower')
plt.title('(2000.77,2000.33) on WFC1 (Detector Space)')
plt.colorbar()