
Slitlessutils Cookbook: Spectral Extraction for WFC3/UVIS Full-Frame#
This notebook contains a step-by-step guide for performing spectral extractions with Slitlessutils for G280 full-frame data from WFC3/UVIS.
The original source for this notebook is the WFC3 folder on the spacetelescope/hst_notebooks GitHub repository.
Learning Goals#
In this tutorial, you will:
Configure
slitlessutilsDownload data
Preprocess data for extraction
Extract 1-D spectra with a simple boxcar method
Table of Contents#
1. Introduction
1.1 Environment
1.2 Imports
1.3 Slitlessutils Configuration
2. Define Global Variables
3. Download Data
3.1 Create Data Directories and Organize
4. Check the WCS
5. Preprocess the G280 Grism Images
6. Preprocess the F200LP Direct Images
6.1 Display Drizzle Image and Overlay Segmentation Map
7. Extract the Spectra from the Grism Data
7.1 Display a Region File
7.2 Plot the Spectrum
8. Conclusions
Additional Resources
About this Notebook
Citations
1. Introduction#
Slitlessutils is an official STScI-supported Python package that provides spectral extraction processes for HST grism and
prism data. The slitlessutils software package has formally replaced the HSTaXe Python package, which will remain
on the spacetelescope GitHub organization, but will no longer be maintained or developed.
Below, we show an example workflow of spectral extraction using WFC3/UVIS G280 grism data and F200LP direct image data.
The example data were taken as part of WFC3 GO program 12524 (PI Quimby) and are downloaded from the Mikulski Archive for
Space Telescopes (MAST) via astroquery. The images are all full-frame exposures. Users with subarray data will need to employ
the embedding utility within slitlessutils before processing and extracting the data. See the UVIS subarray cookbook for
a tutorial on using slitlessutils with subarray data. This tutorial is intended to run continuously without requiring any edits
to the cells.
Running this notebook requires creating a conda environment from the provided requirements file in this notebook’s sub-folder
in the GitHub repository. For more details about creating the necessary environment, see the next section.
To run slitlessutils, we must download the configuration reference files. These files are instrument-specific and include
sensitivity curves, trace and dispersion files, flat fields, and more. Slitlessutils has a Config class with an associated
function that will download the necessary reference files from a Box folder. Section 1.3 shows how to use slitlessutils
to retrieve the files. Once downloaded, slitlessutils will use them for the different processes.
1.1 Environment#
This notebook requires users to install the packages listed in the requirements.txt file located in the notebook’s
sub-folder on the GitHub repository. We will use the conda package manager to build the necessary virtual environment.
For more information about installing and getting started with Conda (or Mamba), please see this page.
First, we will make sure the conda-forge channel is added so that conda can correctly find the various packages.
$ conda config --add channels conda-forge
Next, we will create a new environment called slitlessutils and initialize it with python:
$ conda create --name slitlessutils "python==3.12"
Wait for conda to solve for the environment and confirm the packages/libraries with the y key when prompted.
Once completed, you can activate the new environment with:
$ conda activate slitlessutils
With the new environment activated, we can install the remaining packages from the requirements.txt file with pip:
$ pip install -r requirements.txt
Note, you may encounter an error installing llvmlite. To fix it, run the below command before installing slitlessutils:
$ conda install -c conda-forge llvmlite
We have now successfully installed all the necessary packages for running this notebook. Launch Jupyter and begin:
$ jupyter-lab
1.2 Imports#
For this notebook we import:
| Package | Purpose |
|---|---|
glob |
File handling |
matplotlib.pyplot |
Displaying images and plotting spectrum |
numpy |
Handling arrays |
os |
System commands |
shutil |
File and directory clean up |
astropy.io.fits |
Reading and modifying/creating FITS files |
astropy.visualization |
Various tools for display FITS images |
astropy.wcs.WCS |
Handling WCS objects |
astroquery.mast.Observations |
Downloading RAW and FLT data from MASTs |
drizzlepac.astrodrizzle |
Creating a mosaic for the direct image filter |
pyregions |
Overlaying DS9-formatted regions |
slitlessutils |
Handling preprocessing and spectral extraction |
%matplotlib widget
import glob
import matplotlib.pyplot as plt
import numpy as np
import os
import shutil
from astropy.io import fits
from astropy.visualization import ZScaleInterval, ImageNormalize, LogStretch
from astropy.wcs import WCS
from astroquery.mast import Observations
from drizzlepac import astrodrizzle
import pyregion
import slitlessutils as su
zscale = ZScaleInterval()
INFO MainProcess> Logger (slitlessutils) with level=10 at 2026-04-10 14:16:26.883642
1.3 Slitlessutils Configuration#
In order to extract or simulate grism spectra with slitlessutils, you must have the necessary reference files.
Below, we provide a table of file descriptions, example filenames, and file types for the different reference files
required by slitlessutils.
| File Description | Example Filename | File Type |
|---|---|---|
| Sensitivity curves for the different spectral orders | WFC3.UVIS.G280.CCD2.p1.sens.2021.fits |
FITS table |
| Config files with trace and dispersion coefficients | WFC3.UVIS.G280.CHIP2.V3.0.conf |
Text / ASCII |
| Instrument configuration parameters | hst_wfc3uvis.yaml |
YAML |
| Normalized sky images | wfc3uvis_g280_sky.fits |
FITS image |
| Filter throughput curves | hst_wfc3_f200lp.fits |
FITS table |
| Flat fields | WFC3.UVIS.flat.CH2.fits |
FITS image |
These reference files that are required for spectral extraction and modeling with slitlessutils must reside in a
dot-directory within the user’s home directory, {$HOME}/.slitlessutils. Upon initialization, the Config()
object verifies the existence of this directory and the presence of valid reference files. If the directory does not
exist, it is created; if the reference files are missing, the most recent versions are automatically retrieved from a
public Box directory. Once the files are downloaded, slitlessutils will apply them automatically, relieving the
user from calling them manually. For more information about configuring slitlessutils, please see the
documentation.
In the following code cell, we initialize the configuration with su.config.Config(). As stated above, if this is your
first time initializing slitlessutils configuration, su.config.Config() will download the most recent reference
files. In the future, if a new reference file version is released, it can be retrieved with cfg.retrieve_reffiles(update=True).
# Initialize configuration
cfg = su.config.Config()
# Download latest reffile version
cfg.retrieve_reffiles(update=True)
INFO MainProcess> Using reference path: /home/runner/.slitlessutils/1.0.8/
INFO MainProcess> Retrieving remote file https://data.science.stsci.edu/redirect/slitlessutils/slitlessutils_config_v1.0.8.tar.gz to /home/runner/.slitlessutils
INFO MainProcess> Using reference path: /home/runner/.slitlessutils/1.0.8/
'https://data.science.stsci.edu/redirect/slitlessutils/slitlessutils_config_v1.0.8.tar.gz'
2. Define Global Variables#
These variables will be used throughout the notebook and should only be updated if you are processing different datasets.
The table below lists all the global variables used in the notebook, along with a brief description and the section(s) where
they’re used.
| Variable | Description |
|---|---|
FILTER |
The direct image filter. Used throughout entire notebook for handling files, I/O, creating a sub- directory, and needed for slitlessutils. E.g. F200LP, F300X. |
GRATING |
The grism image filter. Used throughout entire notebook for handling files, I/O, creating a sub- directory, and needed for slitlessutils. E.g. G280. |
INSTRUMENT |
The science instrument that took the data. Needed for slitlessutils. Used in preprocess_direct() in Section 6. E.g. WFC3. |
TELESCOPE |
The observatory. Needed for slitlessutils. Used in download() and preprocess_direct() in Sections 3 and 6, respectively. E.g. HST. |
DATASETS |
A dictionary of grism and direct image rootnames. Used throughout entire notebook. |
RA |
The right ascension of the target in the direct image. Used in preprocess_direct() in Section 6 when creating the segmentation map needed for slitlessutils. Must be in degrees. |
DEC |
The declination of the target in the direct image. Used in preprocess_direct() in Section 6 when creating the segmentation map needed for slitlessutils. Must be in degrees. |
RAD |
The radius for the segemntations in the map. Used in preprocess_direct() in Section 6. Must be in arcsec. E.g. 0.5. |
SCALE |
The pixel scale used in preprocess_direct() in Section 6 for creating the drizzle image and then the segmentation map. Must be in arcsec/pixel. E.g. 0.04. |
ROOT |
The output file name for the drizzle image, segmentation map, and x1d spectrum file. Needed by slitlessutils. Used in Sections 6 and 7. |
ZEROPOINT |
The AB mag zeropoint of the direct image filter. Used by slitlessutils for estimating source flux and/or for normalization when simulating data. In this tutorial, the zeropoint value will not affect the resulting spectrum. Used in extract_single() in Section 7. |
# the observations
FILTER = 'F200LP' # direct image filter
GRATING = 'G280' # grism image filter
INSTRUMENT = 'WFC3'
TELESCOPE = 'HST'
# datasets to process
DATASETS = {GRATING: ["ibr502icq", "ibr502i9q", "ibr502ibq"],
FILTER: ["ibr502i6q"]}
RA = 216.19252 # degrees
DEC = 46.23011 # degrees
RAD = 0.4 # arcsec; for seg map
SCALE = 0.03962 # driz image pix scale; arcsec/pix
ROOT = 'WFC3_UVIS_PTF12dam' # output rootname for driz and SU
ZEROPOINT = 27.423 # f200lp AB MAG -- obsmode = 'wfc3, uvis2, f200lp, mjd#56073, aper#6'
3. Download Data#
Here, we download the example images via astroquery. For more information, please look at the documentation for
Astroquery, Astroquery.mast, and CAOM Field Descriptions, which are used for the obstab variable below. Additionally,
you may download the data from MAST using either the HST MAST Search Engine or the more general MAST Portal.
We download G280 and F200LP _flc.fits images of the super-luminous supernova (SLSN), PTF12dam, from GO
program 12524 (PI Quimby). This target is one of the closest (z=0.108) and best-studied SLSN, and the G280 spectrum
is published in Quimby et al. (2018). After downloading the images, we move them to a sub-directory within the current
working directory.
def download(datasets, telescope):
"""
Function to download FLC files from MAST with astroquery.
`datasets` contains ROOTNAMEs i.e. `ipppssoot`.
Parameters
----------
datasets : dict
Dictionary of grism and direct rootnames
telescope : str
Used for MAST; e.g. HST
Output
-------
flt FITS files in current working dir
ipppssoot_flt.fits
"""
# get user inputted datasets create list of rootname
obs_ids = tuple(d.lower() for k, v in datasets.items() for d in v)
# Query for all images with iels01*
all_obs_table = Observations.query_criteria(obs_id=obs_ids[0][:6]+'*', obs_collection=telescope)
# Get all the products
products_table = Observations.get_product_list(all_obs_table)
# Filter product list for user inputted obs_ids under variable DATASETS
products_table = products_table[np.isin(products_table['obs_id'], obs_ids)]
# Download the FLT files
kwargs = {'productSubGroupDescription': 'FLC',
'extension': 'fits',
'project': 'CALWF3',
'cache': False}
download_table = Observations.download_products(products_table, **kwargs)
# Move the files to the cwd
for local in download_table['Local Path']:
local = str(local)
f = os.path.basename(local)
if f.startswith(obs_ids):
shutil.copy2(local, '.')
download(DATASETS, TELESCOPE)
Downloading URL https://mast.stsci.edu/api/v0.1/Download/file?uri=mast:HST/product/ibr502i6q_flc.fits to ./mastDownload/HST/ibr502i6q/ibr502i6q_flc.fits ...
[Done]
Downloading URL https://mast.stsci.edu/api/v0.1/Download/file?uri=mast:HST/product/ibr502i9q_flc.fits to ./mastDownload/HST/ibr502i9q/ibr502i9q_flc.fits ...
[Done]
Downloading URL https://mast.stsci.edu/api/v0.1/Download/file?uri=mast:HST/product/ibr502ibq_flc.fits to ./mastDownload/HST/ibr502ibq/ibr502ibq_flc.fits ...
[Done]
Downloading URL https://mast.stsci.edu/api/v0.1/Download/file?uri=mast:HST/product/ibr502icq_flc.fits to ./mastDownload/HST/ibr502icq/ibr502icq_flc.fits ...
[Done]
3.1 Create Data Directories and Organize#
Below, we define the current working directory, cwd, and create directories, g280/, f200lp/, and output/, that will be used
throughout the notebook. After the directories are created, we move the downloaded FLC files into them.
Note, the cell below will remove any subdirectories named g280/, f200lp/, and output/ and their contents. If you would like
to run the notebook multiple times and keep past results, please rename the subdirectories.
cwd = os.getcwd()
print(f'The current directory is: {cwd}')
The current directory is: /home/runner/work/hst_notebooks/hst_notebooks/notebooks/WFC3/slitlessutils_UVIS_fullframe_extraction
dirs = [GRATING.lower(), FILTER.lower(), 'output']
for dirr in dirs:
if os.path.isdir(dirr):
print(f'Removing {dirr}/')
shutil.rmtree(dirr)
print(f'Creating {dirr}/')
os.mkdir(dirr)
Removing g280/
Creating g280/
Removing f200lp/
Creating f200lp/
Removing output/
Creating output/
for file in glob.glob('*flc.fits'):
filt = fits.getval(file, 'filter').lower()
print(f"Moving {file} to {filt}/")
shutil.move(file, filt)
Moving ibr502i9q_flc.fits to g280/
Moving ibr502i6q_flc.fits to f200lp/
Moving ibr502ibq_flc.fits to g280/
Moving ibr502icq_flc.fits to g280/
4. Check the World Coordinate System (WCS)#
It is likely that the active WCS in the direct and grism images differ. Having a consistent astrometric reference for the direct
and grism images is crucial for successful spectral extraction. In this section, we will investigate the active WCS (header
keyword WCSNAME) and ultimately use slitlessutils to change the WCS of the direct images. We start by using
slitlessutils to display the active WCS of the direct and grism images.
For more information about slitlessutils astrometry, see the documentation. For a more technical and detailed understanding
of HST WCS and improved absolute astrometry please see WFC3 ISR 2022-06 (Mack et al.). A general overview is also available on
Outerspace.
su.core.preprocess.astrometry.list_wcs(f'{GRATING.lower()}/i*flc.fits')
su.core.preprocess.astrometry.list_wcs(f'{FILTER.lower()}/i*flc.fits')
g280/ibr502i9q_flc.fits G280
+----------+--------------------------------+--------------------------------+
| WCSVERS | 1 | 2 |
+----------+--------------------------------+--------------------------------+
| WCSNAME | IDC_2731450pi-GSC240 | IDC_2731450pi-GSC240 |
| WCSNAMEA | IDC_2731450pi | IDC_2731450pi |
| WCSNAMEO | OPUS | OPUS |
+----------+--------------------------------+--------------------------------+
g280/ibr502ibq_flc.fits G280
+----------+--------------------------------+--------------------------------+
| WCSVERS | 1 | 2 |
+----------+--------------------------------+--------------------------------+
| WCSNAME | IDC_2731450pi-GSC240 | IDC_2731450pi-GSC240 |
| WCSNAMEA | IDC_2731450pi | IDC_2731450pi |
| WCSNAMEO | OPUS | OPUS |
+----------+--------------------------------+--------------------------------+
g280/ibr502icq_flc.fits G280
+----------+--------------------------------+--------------------------------+
| WCSVERS | 1 | 2 |
+----------+--------------------------------+--------------------------------+
| WCSNAME | IDC_2731450pi-GSC240 | IDC_2731450pi-GSC240 |
| WCSNAMEA | IDC_2731450pi | IDC_2731450pi |
| WCSNAMEO | OPUS | OPUS |
+----------+--------------------------------+--------------------------------+
f200lp/ibr502i6q_flc.fits F200LP
+----------+--------------------------------+--------------------------------+
| WCSVERS | 1 | 2 |
+----------+--------------------------------+--------------------------------+
| WCSNAME | IDC_2731450pi-HSC30 | IDC_2731450pi-HSC30 |
| WCSNAMEA | IDC_2731450pi | IDC_2731450pi |
| WCSNAMEO | OPUS | OPUS |
+----------+--------------------------------+--------------------------------+
As shown above, the F200LP active WCS (IDC_2731450pi-HSC30) is different from the G280 WCS (IDC_2731450pi-GSC240).
Both are an undistorted a priori solution where guide star coordinates are corrected from the original reference frame to a catalog.
However, the F200LP WCS solution uses the Hubble Source Catalog v3.0 (HSC30), while the G280 solutions use the Gaia DR1-
based Guide Star Catalog v2.4.0 (GSC240)
To fix the discrepancy, we will use slitlessutils to “downgrade” the WCS of the direct and grism images. The WCS solution
we’re going to use, IDC_w3m18525i, is encoded in the header keyword WCSNAMEA of the G280 and F200LP images. For this tutorial,
we are not concerned with precise absolute astrometry. The key requirement is that the images have a consistent reference, which
preserves relative source positions.
files = glob.glob(f'{FILTER.lower()}/i*_flc.fits')+glob.glob(f'{GRATING.lower()}/i*_flc.fits')
for img in files:
su.core.preprocess.astrometry.downgrade_wcs(img, key='A', inplace=True) # `key` corresponds to WCS in WCSNAMEA
INFO MainProcess> downgrading WCS in f200lp/ibr502i6q_flc.fits to WCSNAMEA
INFO MainProcess> downgrading WCS in g280/ibr502i9q_flc.fits to WCSNAMEA
INFO MainProcess> downgrading WCS in g280/ibr502ibq_flc.fits to WCSNAMEA
INFO MainProcess> downgrading WCS in g280/ibr502icq_flc.fits to WCSNAMEA
Finally, we print the active WCS names one last time to verify the upgrade worked as intended.
wcsnames = []
files = sorted(glob.glob(f'{GRATING.lower()}/i*flc.fits')+glob.glob(f'{FILTER.lower()}/i*flc.fits'))
for f in files:
wcsname = fits.getval(f, 'WCSNAME', ext=1)
wcsnames.append(wcsname)
print(f, wcsname)
if len(set(wcsnames)) == 1:
print("\nSuccess!\n")
else:
print("\nThere is still more than one active WCS\n")
f200lp/ibr502i6q_flc.fits IDC_2731450pi
g280/ibr502i9q_flc.fits IDC_2731450pi
g280/ibr502ibq_flc.fits IDC_2731450pi
g280/ibr502icq_flc.fits IDC_2731450pi
Success!
5. Preprocess the G280 Grism Images#
The function below performs preprocessing steps to prepare the grism images for optimal spectral extraction.
This function completes two task, (1) background subtraction via a global-sky image, and (2) cosmic ray
(CR) flagging via DrizzlePac.
In slitless spectroscopy, the background is inherently complex because every patch of sky is dispersed onto
the detector, and each spectral order has a different response and geometric footprint. This complexity is
further amplified by wavelength-dependent flat fields, resulting in a highly structured two-dimensional
background that cannot be treated like standard imaging backgrounds.
Slitlessutils handles this by subtracting a global-sky image, which is built from many science and
calibration exposures with sources removed, leaving only the instrumental and sky background structure.
This sky image is scaled to each individual exposure by fitting the sky-dominated pixels and is subtracted
before any further reduction steps. For more information about the G280 sky image, see WFC3 ISR 2023-06
(Pagul et al.), and for details about slitlessutils background subtraction, see the documentation.
Slitlessutils does not interpolate over CRs, and expects affected pixels to be flagged in the data quality
array. There are two methods in slitlessutils for identifying and flagging CRs: a Laplacian edge-detection
technique that identifies sharp, high-contrast features in individual images, and a drizzle-based approach that
uses AstroDrizzle to compare multiple aligned exposures and flag discrepant pixels. The Laplace method works
on single exposures using image gradients, while the drizzle method groups and aligns images before identifying
CRs from their inconsistencies.
Below, we flag CRs using the drizzle method. For reliable CR flagging, drizzle must be run on grism images
taken at a single orient (position angle). Slitlessutils has the ability to group the data by visit or position angle
before processing. We do not employ any grouping in our example since the data are from the same orient. For more
information about CR flagging with slitlessutils see the documentation.
def preprocess_grism(datasets, grating):
"""
Function to perform bkg subtraction with global-sky image and CR flagging with drizzle
Parameters
----------
dataset : dict
A dictionary of grating and filter rootnames
grating : str
The grism image filter associated with datasets; e.g. G280
"""
# Create a list of all G280 files
grismfiles = [f'{grismdset}_flc.fits' for grismdset in datasets[grating]]
# Subtract background via global-sky
# Background subtraction should happen on original subarray images before embedding
# Subtract sky before CR flagging
back = su.core.preprocess.background.Background()
for grism in grismfiles:
back.image(grism, inplace=True)
# Process FLTs through drizzle for CR flagging
grismfiles = su.core.preprocess.crrej.drizzle(grismfiles, grouping=None, outdir='./')
Note, calling Background() may produce a WARNING about the sky file’s normalization. This warning is expected can be safely ignored.
os.chdir(GRATING.lower())
preprocess_grism(DATASETS, GRATING)
WARNING MainProcess> The global sky image (/home/runner/.slitlessutils/1.0.8/instruments/WFC3UVIS/wfc3uvis_g280_sky.fits) is unnormalized 1.050017745214092. Results will be fine, but the values may be suspect.
WARNING MainProcess> The global sky image (/home/runner/.slitlessutils/1.0.8/instruments/WFC3UVIS/wfc3uvis_g280_sky.fits) is unnormalized 1.0356415439738356. Results will be fine, but the values may be suspect.
WARNING MainProcess> The global sky image (/home/runner/.slitlessutils/1.0.8/instruments/WFC3UVIS/wfc3uvis_g280_sky.fits) is unnormalized 1.050017745214092. Results will be fine, but the values may be suspect.
WARNING MainProcess> The global sky image (/home/runner/.slitlessutils/1.0.8/instruments/WFC3UVIS/wfc3uvis_g280_sky.fits) is unnormalized 1.0356415439738356. Results will be fine, but the values may be suspect.
WARNING MainProcess> The global sky image (/home/runner/.slitlessutils/1.0.8/instruments/WFC3UVIS/wfc3uvis_g280_sky.fits) is unnormalized 1.050017745214092. Results will be fine, but the values may be suspect.
WARNING MainProcess> The global sky image (/home/runner/.slitlessutils/1.0.8/instruments/WFC3UVIS/wfc3uvis_g280_sky.fits) is unnormalized 1.0356415439738356. Results will be fine, but the values may be suspect.
Setting up logfile : astrodrizzle.log
AstroDrizzle log file: astrodrizzle.log
AstroDrizzle Version 3.10.0 started at: 14:19:10.786 (10/04/2026)
==== Processing Step Initialization started at 14:19:10.787 (10/04/2026)
WCS Keywords
Number of WCS axes: 2
CTYPE : 'RA---TAN' 'DEC--TAN'
CUNIT : 'deg' 'deg'
CRVAL : 216.197450145954 46.24051523167477
CRPIX : 2062.5 2193.0
CD1_1 CD1_2 : -1.043184104214553e-05 3.5069860095649007e-06
CD2_1 CD2_2 : 3.5069860095649007e-06 1.043184104214553e-05
NAXIS : 4125 4386
********************************************************************************
*
* Estimated memory usage: up to 1084 Mb.
* Output image size: 4125 X 4386 pixels.
* Output image file: ~ 207 Mb.
* Cores available: 4
*
********************************************************************************
==== Processing Step Initialization finished at 14:19:11.525 (10/04/2026)
==== Processing Step Static Mask started at 14:19:11.52 (10/04/2026)
==== Processing Step Static Mask finished at 14:19:12.295 (10/04/2026)
==== Processing Step Subtract Sky started at 14:19:12.296 (10/04/2026)
***** skymatch started on 2026-04-10 14:19:12.437211
Version 1.0.12
'skymatch' task will apply computed sky differences to input image file(s).
NOTE: Computed sky values WILL NOT be subtracted from image data ('subtractsky'=False).
'MDRIZSKY' header keyword will represent sky value *computed* from data.
----- User specified keywords: -----
Sky Value Keyword: 'MDRIZSKY'
Data Units Keyword: 'BUNIT'
----- Input file list: -----
** Input image: 'ibr502icq_flc.fits'
EXT: 'SCI',1; MASK: ibr502icq_skymatch_mask_sci1.fits[0]
EXT: 'SCI',2; MASK: ibr502icq_skymatch_mask_sci2.fits[0]
** Input image: 'ibr502i9q_flc.fits'
EXT: 'SCI',1; MASK: ibr502i9q_skymatch_mask_sci1.fits[0]
EXT: 'SCI',2; MASK: ibr502i9q_skymatch_mask_sci2.fits[0]
** Input image: 'ibr502ibq_flc.fits'
EXT: 'SCI',1; MASK: ibr502ibq_skymatch_mask_sci1.fits[0]
EXT: 'SCI',2; MASK: ibr502ibq_skymatch_mask_sci2.fits[0]
----- Sky statistics parameters: -----
statistics function: 'median'
lower = None
upper = None
nclip = 5
lsigma = 4.0
usigma = 4.0
binwidth = 0.1
----- Data->Brightness conversion parameters for input files: -----
* Image: ibr502icq_flc.fits
EXT = 'SCI',1
Data units type: COUNTS
EXPTIME: 816.0 [s]
Conversion factor (data->brightness): 0.7806940686126805
EXT = 'SCI',2
Data units type: COUNTS
EXPTIME: 816.0 [s]
Conversion factor (data->brightness): 0.7806940686126805
* Image: ibr502i9q_flc.fits
EXT = 'SCI',1
Data units type: COUNTS
EXPTIME: 816.0 [s]
Conversion factor (data->brightness): 0.7806940686126805
EXT = 'SCI',2
Data units type: COUNTS
EXPTIME: 816.0 [s]
Conversion factor (data->brightness): 0.7806940686126805
* Image: ibr502ibq_flc.fits
EXT = 'SCI',1
Data units type: COUNTS
EXPTIME: 816.0 [s]
Conversion factor (data->brightness): 0.7806940686126805
EXT = 'SCI',2
Data units type: COUNTS
EXPTIME: 816.0 [s]
Conversion factor (data->brightness): 0.7806940686126805
----- Computing sky values requested image extensions (detector chips): -----
* Image: 'ibr502icq_flc.fits['SCI',1,2]' -- SKY = 1.1016696691513062 (brightness units)
Sky change (data units):
- EXT = 'SCI',1 delta(MDRIZSKY) = 1.41114 NEW MDRIZSKY = 1.41114
- EXT = 'SCI',2 delta(MDRIZSKY) = 1.41114 NEW MDRIZSKY = 1.41114
* Image: 'ibr502i9q_flc.fits['SCI',1,2]' -- SKY = 1.138963222503662 (brightness units)
Sky change (data units):
- EXT = 'SCI',1 delta(MDRIZSKY) = 1.45891 NEW MDRIZSKY = 1.45891
- EXT = 'SCI',2 delta(MDRIZSKY) = 1.45891 NEW MDRIZSKY = 1.45891
* Image: 'ibr502ibq_flc.fits['SCI',1,2]' -- SKY = 1.0578856468200684 (brightness units)
Sky change (data units):
- EXT = 'SCI',1 delta(MDRIZSKY) = 1.35506 NEW MDRIZSKY = 1.35506
- EXT = 'SCI',2 delta(MDRIZSKY) = 1.35506 NEW MDRIZSKY = 1.35506
***** skymatch ended on 2026-04-10 14:19:14.153852
TOTAL RUN TIME: 0:00:01.716641
==== Processing Step Subtract Sky finished at 14:19:14.237 (10/04/2026)
==== Processing Step Separate Drizzle started at 14:19:14.23 (10/04/2026)
WCS Keywords
Number of WCS axes: 2
CTYPE : 'RA---TAN' 'DEC--TAN'
CUNIT : 'deg' 'deg'
CRVAL : 216.197450145954 46.24051523167477
CRPIX : 2062.5 2193.0
CD1_1 CD1_2 : -1.043184104214553e-05 3.5069860095649007e-06
CD2_1 CD2_2 : 3.5069860095649007e-06 1.043184104214553e-05
NAXIS : 4125 4386
-Generating simple FITS output: ibr502i9q_single_sci.fits
-Generating simple FITS output: ibr502ibq_single_sci.fits
Writing out image to disk: ibr502i9q_single_sci.fits
Writing out image to disk: ibr502ibq_single_sci.fits
Writing out image to disk: ibr502i9q_single_wht.fits
Writing out image to disk: ibr502ibq_single_wht.fits
-Generating simple FITS output: ibr502icq_single_sci.fits
Writing out image to disk: ibr502icq_single_sci.fits
Writing out image to disk: ibr502icq_single_wht.fits
==== Processing Step Separate Drizzle finished at 14:19:16.164 (10/04/2026)
==== Processing Step Create Median started at 14:19:16.165 (10/04/2026)
reference sky value for image 'ibr502icq_flc.fits' is 1.411141276359558
reference sky value for image 'ibr502i9q_flc.fits' is 1.4589110612869263
reference sky value for image 'ibr502ibq_flc.fits' is 1.3550578355789185
Saving output median image to: 'su_drizzle_med.fits'
==== Processing Step Create Median finished at 14:19:18.684 (10/04/2026)
==== Processing Step Blot started at 14:19:18.685 (10/04/2026)
Blot: creating blotted image: ibr502icq_flc.fits[sci,1]
Using default C-based coordinate transformation...
-Generating simple FITS output: ibr502icq_sci1_blt.fits
Writing out image to disk: ibr502icq_sci1_blt.fits
Blot: creating blotted image: ibr502icq_flc.fits[sci,2]
Using default C-based coordinate transformation...
-Generating simple FITS output: ibr502icq_sci2_blt.fits
Writing out image to disk: ibr502icq_sci2_blt.fits
Blot: creating blotted image: ibr502i9q_flc.fits[sci,1]
Using default C-based coordinate transformation...
-Generating simple FITS output: ibr502i9q_sci1_blt.fits
Writing out image to disk: ibr502i9q_sci1_blt.fits
Blot: creating blotted image: ibr502i9q_flc.fits[sci,2]
Using default C-based coordinate transformation...
-Generating simple FITS output: ibr502i9q_sci2_blt.fits
Writing out image to disk: ibr502i9q_sci2_blt.fits
Blot: creating blotted image: ibr502ibq_flc.fits[sci,1]
Using default C-based coordinate transformation...
-Generating simple FITS output: ibr502ibq_sci1_blt.fits
Writing out image to disk: ibr502ibq_sci1_blt.fits
Blot: creating blotted image: ibr502ibq_flc.fits[sci,2]
Using default C-based coordinate transformation...
-Generating simple FITS output: ibr502ibq_sci2_blt.fits
Writing out image to disk: ibr502ibq_sci2_blt.fits
==== Processing Step Blot finished at 14:19:25.009 (10/04/2026)
==== Processing Step Driz_CR started at 14:19:25.010 (10/04/2026)
Creating output: ibr502i9q_sci1_crmask.fits
Creating output: ibr502icq_sci1_crmask.fits
Creating output: ibr502ibq_sci1_crmask.fits
Creating output: ibr502i9q_sci2_crmask.fits
Creating output: ibr502icq_sci2_crmask.fits
Creating output: ibr502ibq_sci2_crmask.fits
==== Processing Step Driz_CR finished at 14:19:27.867 (10/04/2026)
==== Processing Step Final Drizzle started at 14:19:27.88 (10/04/2026)
WCS Keywords
Number of WCS axes: 2
CTYPE : 'RA---TAN' 'DEC--TAN'
CUNIT : 'deg' 'deg'
CRVAL : 216.197450145954 46.24051523167477
CRPIX : 2062.5 2193.0
CD1_1 CD1_2 : -1.043184104214553e-05 3.5069860095649007e-06
CD2_1 CD2_2 : 3.5069860095649007e-06 1.043184104214553e-05
NAXIS : 4125 4386
-Generating simple FITS output: su_drizzle_drc_sci.fits
Writing out image to disk: su_drizzle_drc_sci.fits
Writing out image to disk: su_drizzle_drc_wht.fits
Writing out image to disk: su_drizzle_drc_ctx.fits
==== Processing Step Final Drizzle finished at 14:19:39.088 (10/04/2026)
AstroDrizzle Version 3.10.0 is finished processing at 14:19:39.08 (10/04/2026).
-------------------- --------------------
Step Elapsed time
-------------------- --------------------
Initialization 0.7375 sec.
Static Mask 0.7685 sec.
Subtract Sky 1.9412 sec.
Separate Drizzle 1.9256 sec.
Create Median 2.5192 sec.
Blot 6.3233 sec.
Driz_CR 2.8571 sec.
Final Drizzle 11.2066 sec.
==================== ====================
Total 28.2790 sec.
Trailer file written to: astrodrizzle.log
Next, we display one of the grism images for visual inspection.
fig, axs = plt.subplots(1, 1, figsize=(9, 7))
img1 = fits.getdata(f'{DATASETS[GRATING][0]}_flc.fits', ext=4)
img2 = fits.getdata(f'{DATASETS[GRATING][0]}_flc.fits', ext=1)
img = np.concatenate([img2, img1])
z1, z2 = zscale.get_limits(img)
inorm = ImageNormalize(img, vmin=z1, vmax=z2*100, stretch=LogStretch())
im1 = axs.imshow(img, origin='lower', cmap='Greys_r', norm=inorm)
fig.colorbar(im1, ax=axs, pad=0.02).set_label(label='e$^-$', size=12)
axs.set_title(f'{GRATING} {DATASETS[GRATING][0]}_flc.fits')
fig.tight_layout()
plt.show()
6. Preprocess the F200LP Direct Images#
With the grism data calibrated, data embedded, and a consistent WCS for all images, we can proceed to the preprocessing of the
direct image. The two main steps we need to complete are (1) drizzling the direct image and (2) creating a segmentation map.
Information about DrizzlePac can be found on the STScI website, and AstroDrizzle
tutorials are hosted in the hst_notebooks
GitHub repo.
def preprocess_direct(datasets, filt, root, scale, ra, dec, rad, telescope, instrument):
"""
Function to create drizzle image and segmentation map for direct image data.
Parameters
----------
datasets : dict
A dictionary of grating and filter rootnames
filt : str
The direct image filter associated with datasets
root : str
The filename for the drizzle image and seg. map
scale : float
Pixel (plate) scale for drizzle image and seg. map (arcsec/pix)
ra : float
The Right Ascension of the source in the direct image (degrees)
dec : float
The Declination of the source in the direct image (degrees)
rad : float
The radius of the segmentation size (arcsec)
telescope : str
The name of the observatory; e.g. HST
instrument : str
The name of the instrument; e.g. WFC3
Outputs
-------
Drizzle image : FITS file
<root>_drc_sci.fits
Segmentation map : FITS file
<root>_drc_seg.fits
"""
# list of direct image data
files = [f'{imgdset}_flc.fits' for imgdset in datasets[filt]]
# mosaic data via astrodrizzle
astrodrizzle.AstroDrizzle(files, output=root, build=False,
static=False, skysub=False, driz_separate=False,
median=False, blot=False, driz_cr=False,
driz_combine=True, final_wcs=True,
final_rot=0., final_scale=scale,
final_pixfrac=1.0,
overwrite=True, final_fillval=0.0)
# Must use memmap=False to force close all handles and allow file overwrite
with fits.open(f'{root}_drc_sci.fits', memmap=False) as hdulist:
img = hdulist['PRIMARY'].data
hdr = hdulist['PRIMARY'].header
wcs = WCS(hdr)
x, y = wcs.all_world2pix(ra, dec, 0)
xx, yy = np.meshgrid(np.arange(hdr['NAXIS1']),
np.arange(hdr['NAXIS2']))
rr = np.hypot(xx-x, yy-y)
seg = rr < (rad/scale)
# add some keywords for SU
hdr['TELESCOP'] = telescope
hdr['INSTRUME'] = instrument
hdr['FILTER'] = filt
# write the files to disk
fits.writeto(f'{root}_drc_sci.fits', img, hdr, overwrite=True)
fits.writeto(f'{root}_drc_seg.fits', seg.astype(int), hdr, overwrite=True)
os.chdir('../'+FILTER.lower())
preprocess_direct(DATASETS, FILTER, ROOT, SCALE, RA, DEC, RAD, TELESCOPE, INSTRUMENT)
Setting up logfile : astrodrizzle.log
AstroDrizzle log file: astrodrizzle.log
AstroDrizzle Version 3.10.0 started at: 14:19:40.137 (10/04/2026)
==== Processing Step Initialization started at 14:19:40.138 (10/04/2026)
Forcibly archiving original of: ibr502i6q_flc.fits as OrIg_files/ibr502i6q_flc.fits
Turning OFF "preserve" and "restore" actions...
WCS Keywords
Number of WCS axes: 2
CTYPE : 'RA---TAN' 'DEC--TAN'
CUNIT : 'deg' 'deg'
CRVAL : 216.1974497492579 46.24051553429139
CRPIX : 2653.9807710041614 2735.7746060539325
CD1_1 CD1_2 : -1.1005555555555558e-05 5.044470758428284e-22
CD2_1 CD2_2 : 2.155411493130141e-22 1.1005555555555558e-05
NAXIS : 5308 5472
********************************************************************************
*
* Estimated memory usage: up to 396 Mb.
* Output image size: 5308 X 5472 pixels.
* Output image file: ~ 332 Mb.
* Cores available: 1
*
********************************************************************************
==== Processing Step Initialization finished at 14:19:40.427 (10/04/2026)
==== Processing Step Static Mask started at 14:19:40.429 (10/04/2026)
==== Processing Step Static Mask finished at 14:19:40.430 (10/04/2026)
==== Processing Step Subtract Sky started at 14:19:40.431 (10/04/2026)
==== Processing Step Subtract Sky finished at 14:19:40.469 (10/04/2026)
==== Processing Step Separate Drizzle started at 14:19:40.470 (10/04/2026)
==== Processing Step Separate Drizzle finished at 14:19:40.471 (10/04/2026)
==== Processing Step Create Median started at 14:19:40.472 (10/04/2026)
==== Processing Step Blot started at 14:19:40.474 (10/04/2026)
==== Processing Step Blot finished at 14:19:40.475 (10/04/2026)
==== Processing Step Driz_CR started at 14:19:40.476 (10/04/2026)
==== Processing Step Final Drizzle started at 14:19:40.477 (10/04/2026)
WCS Keywords
Number of WCS axes: 2
CTYPE : 'RA---TAN' 'DEC--TAN'
CUNIT : 'deg' 'deg'
CRVAL : 216.1974497492579 46.24051553429139
CRPIX : 2653.9807710041614 2735.7746060539325
CD1_1 CD1_2 : -1.1005555555555558e-05 5.044470758428284e-22
CD2_1 CD2_2 : 2.155411493130141e-22 1.1005555555555558e-05
NAXIS : 5308 5472
-Generating simple FITS output: WFC3_UVIS_PTF12dam_drc_sci.fits
Writing out image to disk: WFC3_UVIS_PTF12dam_drc_sci.fits
Writing out image to disk: WFC3_UVIS_PTF12dam_drc_wht.fits
Writing out image to disk: WFC3_UVIS_PTF12dam_drc_ctx.fits
==== Processing Step Final Drizzle finished at 14:19:44.901 (10/04/2026)
AstroDrizzle Version 3.10.0 is finished processing at 14:19:44.902 (10/04/2026).
-------------------- --------------------
Step Elapsed time
-------------------- --------------------
Initialization 0.2887 sec.
Static Mask 0.0015 sec.
Subtract Sky 0.0381 sec.
Separate Drizzle 0.0014 sec.
Create Median 0.0000 sec.
Blot 0.0015 sec.
Driz_CR 0.0000 sec.
Final Drizzle 4.4238 sec.
==================== ====================
Total 4.7549 sec.
Trailer file written to: astrodrizzle.log
6.1 Display Drizzle Image and Overlay Segmentation Map#
Next, we display the F200LP drizzle image created in the cell above. We also overplot the segmentation map by getting all
the x and y pixels where the segmentation array is True. Visually inspect the drizzle science image (and other drizzle
products if needed) for accurate image alignment and combination, as well as the source location as defined by the
segmentation map.
# get drizzle data
d = fits.getdata(f'{ROOT}_drc_sci.fits')
# get seg map coordinates
y, x = np.where(fits.getdata(f'{ROOT}_drc_seg.fits') == 1)
# display the image and seg map coords
fig, axs = plt.subplots(1, 1, figsize=(10, 8))
z1, z2 = zscale.get_limits(d)
inorm = ImageNormalize(d, vmin=z1, vmax=z2*100, stretch=LogStretch())
im1 = axs.imshow(d, origin='lower', cmap='Greys_r', norm=inorm)
axs.scatter(x, y, 15, marker='x', c='limegreen', alpha=0.2, label='segmentation')
fig.colorbar(im1, ax=axs, pad=0.02).set_label(label='e$^-$', size=12)
axs.set_title(f'{ROOT} {FILTER}')
axs.legend()
fig.tight_layout()
plt.show()
7. Extract the Spectrum from the Grism Data#
We are now finished with all the preprocessing steps and ready to extract the 1D spectrum. The function below, extract_single(),
demonstrates a complete single-orientation spectral extraction workflow using slitlessutils, starting from calibrated WFC3/UVIS
grism exposures and a direct-image-based source catalog (segmentation map). Below, we list each of the core slitlessutils tasks
used in extract_single() and provide a brief description of what the task does.
WFSSCollectionserves as a container for the grism FLT exposures used in the extraction. Grouping them into a collection allows
the software to treat the set of exposures as a unified dataset when projecting sources, building pixel‑dispersion tables (PDTs), and
performing extraction. For more information aboutWFSSCollection, see the documentation.SourceCollectionis a similar container data structure that defines the sources from the inputted segmentation map and the
drizzled science image; both made from the associated direct filter data. For more information aboutSourceCollection, see
the documentation.Regionis a module that creates visual outlines of where each source’s dispersed light appears on the grism images. It does this
by reading the PDTs and distilling them into DS9‑compatible region files that trace the footprint of each spectral order across the
detector. These region files are not required for extraction, but are useful for quickly inspecting which parts of the grism images
contribute to each object’s spectrum. More information aboutRegioncan be found in the documentation.Tabulateperforms the forward‑modeling step that connects the direct image to the grism frames. For every relevant direct‑image
pixel, it computes the trace, wavelength mapping, and fractional pixel coverage and stores the results in a PDT. Because this calculation
is computationally expensive,slitlessutilsonly computes PDTs when requested and then saves them in hierarchical data-format
5 (HDF5) files inside thesu_tables/directory for later use. The PDT files only capture the geometric mapping of the scene.
Instrumental effects and the actual astrophysical signal are added later in the workflow, so the PDTs depend solely on the WCS and its
calibration. See theTabulatedocumentation for more information.Singleis the class that carries out the one‑dimensional extraction for a single telescope orientation (position angle) and spectral
order (here, the +1 order). It uses the PDTs to forward‑model how each direct‑image pixel contributes to the dispersed spectrum and
assembles these contributions into a 1D spectrum. The method is conceptually similar toHSTaXe, but with a more flexible contamination
model and a full forward‑modeling approach that can support optimization, such as SED‑fitting workflows. This extraction mode is
intended for relatively simple, isolated sources where self‑contamination is not severe. A given spectral order should be extracted one
at a time. Note, the software will overwrite thex1d.fitsfile, so users must use unique file names withroot. For additional details,
see theSingledocumentation.
Note, the AB mag zeropoint for the direct image filter (here, F200LP) is needed for SourceCollection. It is used by slitlessutils
to measure the magnitude within the aperture, which allows users to reject sources that are too faint. The value used for zeropoint will not
affect the resulting spectrum. Zeropoints for WFC3/UVIS filters can be found on the WFC3/UVIS Photometric Calibration page. For convenience,
a table of zeropoints for common filters is provided below. These zeropoints were created using the stsynphot Python package with an
obsmode of 'wfc3, uvis2, {filter}, mjd#56073, aper#6'. For the most accurate and time-dependent zeropoints, we recommend
using stsynphot. A tutorial for calculating WFC3 time-dependent zeropoints with stsynphot is available as a Jupyter notebook (see
Section 5 of that tutorial).
| Filter | Approximate UVIS2 AB Mag Zeropint |
|---|---|
| F200LP | 27.423 |
| F300X | 25.055 |
def extract_single(grating, filt, root, zeropoint, tabulate=False):
"""
Basic single orient extraction similar to HSTaXe
Parameters
----------
grating : str
The grism image filter associated with datasets; e.g. G280
filt : str
The direct image filter associated with datasets e.g. F200LP
root : str
The rootname of the drz and seg FITS file; will also be the 1d spectrum output file rootname
zeropoint : float
The AB mag zeropoint of the direct image filter; needed when simulating or rejecting faint sources
tabulate : Bool
If true, SU computes the pixel‑dispersion tables. Only need once per `data` and `sources`
Output
------
1D spectrum : FITS file
root_x1d.fits
"""
# load grism data into slitlessutils
data = su.wfss.WFSSCollection.from_glob(f'../{grating.lower()}/*_flc.fits')
# load the sources from direct image into slitlessutils
sources = su.sources.SourceCollection(f'../{filt.lower()}/{root}_drc_seg.fits',
f'../{filt.lower()}/{root}_drc_sci.fits',
local_back=False,
zeropoint=zeropoint)
# create region files for spectral orders
# regions are NOT required for spectral extraction
reg = su.modules.Region(ncpu=1, orders=['+1', '+2', '-1', '-2'])
reg(data, sources)
# project the sources onto the grism images; output to `su_tables/`
if tabulate:
tab = su.modules.Tabulate(ncpu=1, orders=['+1', '+2', '-1', '-2'])
tab(data, sources)
# run a single-orient extraction on the +1 order
ext = su.modules.Single('+1', mskorders=None, root=ROOT, nsigmaclip=2, maxiters=10, ncpu=1)
ext(data, sources, width=15)
Note, you may see a WARNING that the “filename does not contain suffix”. This warning is expected can be safely ignored.
os.chdir('../output')
extract_single(GRATING, FILTER, ROOT, ZEROPOINT, tabulate=True)
INFO MainProcess> Loading from glob: ../g280/*_flc.fits
INFO MainProcess> Loading WFSS data from python list
INFO MainProcess> loading throughput from keys: ('hst', 'wfc3', 'f200lp')
INFO MainProcess> Loading a classic segmentation map: ../f200lp/WFC3_UVIS_PTF12dam_drc_seg.fits
INFO MainProcess> Serial processing
Regions: 0%| | 0/3 [00:00<?, ?it/s]
WARNING MainProcess> Filename does not contain suffix.
WARNING MainProcess> Filename does not contain suffix.
Regions: 33%|███▎ | 1/3 [00:01<00:02, 1.05s/it]
WARNING MainProcess> Filename does not contain suffix.
WARNING MainProcess> Filename does not contain suffix.
Regions: 67%|██████▋ | 2/3 [00:01<00:00, 1.62it/s]
WARNING MainProcess> Filename does not contain suffix.
WARNING MainProcess> Filename does not contain suffix.
Regions: 100%|██████████| 3/3 [00:01<00:00, 2.09it/s]
Regions: 100%|██████████| 3/3 [00:01<00:00, 1.79it/s]
INFO MainProcess> Serial processing
Tabulating: 0%| | 0/3 [00:00<?, ?it/s]
WARNING MainProcess> Filename does not contain suffix.
Tabulating: 33%|███▎ | 1/3 [00:32<01:04, 32.00s/it]
WARNING MainProcess> Filename does not contain suffix.
Tabulating: 67%|██████▋ | 2/3 [01:05<00:32, 32.61s/it]
WARNING MainProcess> Filename does not contain suffix.
Tabulating: 100%|██████████| 3/3 [01:38<00:00, 32.84s/it]
Tabulating: 100%|██████████| 3/3 [01:38<00:00, 32.71s/it]
INFO MainProcess> Serial processing
Extracting (single): 0%| | 0/3 [00:00<?, ?it/s]
WARNING MainProcess> Filename does not contain suffix.
INFO MainProcess> Building contamination model for ibr502i9q_flc.fits/UVIS1
INFO MainProcess> Building contamination model for ibr502i9q_flc.fits/UVIS2
Extracting (single): 33%|███▎ | 1/3 [00:03<00:06, 3.21s/it]
WARNING MainProcess> Filename does not contain suffix.
INFO MainProcess> Building contamination model for ibr502ibq_flc.fits/UVIS1
INFO MainProcess> Building contamination model for ibr502ibq_flc.fits/UVIS2
Extracting (single): 67%|██████▋ | 2/3 [00:06<00:03, 3.21s/it]
WARNING MainProcess> Filename does not contain suffix.
INFO MainProcess> Building contamination model for ibr502icq_flc.fits/UVIS1
INFO MainProcess> Building contamination model for ibr502icq_flc.fits/UVIS2
Extracting (single): 100%|██████████| 3/3 [00:09<00:00, 3.21s/it]
Extracting (single): 100%|██████████| 3/3 [00:09<00:00, 3.21s/it]
INFO MainProcess> Combining the 1d spectra
INFO MainProcess> Writing: /home/runner/work/hst_notebooks/hst_notebooks/notebooks/WFC3/slitlessutils_UVIS_fullframe_extraction/output/WFC3_UVIS_PTF12dam_x1d.fits
WARNING: VerifyWarning: Card is too long, comment will be truncated. [astropy.io.fits.card]
7.1 Display a Region File#
Below, we display a grism FLT file and the associated region file. The region files are useful for quickly inspecting which part of
the grism image contribute to each object’s spectrum. The +1, +2, -1, and -2 spectral orders for the observation are outlined in
dark blue, dark orange, light blue, and light orange, respectively. More information about the slitlessutils region files can
be found in the documentation.
Note: if the figure below does not display correctly, try switching to %matplotlib inline.
# %matplotlib inline
i = 0 # which image to display
fig, axs = plt.subplots(1, 1, figsize=(9, 6))
# Display a grism FLC
flc = fits.getdata(f'../{GRATING.lower()}/{DATASETS[GRATING][i]}_flc.fits')
z1, z2 = zscale.get_limits(flc)
inorm = ImageNormalize(flc, vmin=z1, vmax=z2*100, stretch=LogStretch())
im1 = axs.imshow(flc, origin='lower', cmap='Greys_r', norm=inorm)
axs.set_title(f'{DATASETS[GRATING][i]}_flc.fits')
cbar = fig.colorbar(im1, ax=axs, pad=0.1, orientation='horizontal', label='e$^-$')
for t in cbar.ax.get_xticklabels():
t.set_rotation(45)
# Display the corresponding region file
region = pyregion.open(f'{DATASETS[GRATING][i]}_flc.fits_UVIS2.reg')
patch_list, _ = region.get_mpl_patches_texts()
orders = ['+1', '+2', '-1', '-2']
# Add patches
for idx, p in enumerate(patch_list):
p.set_linewidth(2)
p.set_label(orders[idx])
axs.add_patch(p)
axs.legend(title="Spectral Orders", ncol=4, loc=0)
fig.tight_layout()
plt.show()
7.2 Plot the Spectrum#
Finally, what we’ve all been waiting for(!), plotting the extracted spectrum. The Single module produces a single spectrum for each
source and grism image. Those spectra are then combined into a single one-dimensional spectrum for each source and saved as a
x1d.fits file. In this example, we only have one source, so there is a single spectrum.
The x1d file is a binary table with columns for the wavelength (WAVELENGTH), flux (FLUX), uncertainty (UNCERTAINTY), contamination
(CONTAMINATION), and number of pixels (NMEASUREMENTS). By default configuration, the flux and uncertainty values are in units of
erg s-1 cm-2 · Å-1 and scaled by 10-17 (see cfg.fluxunits and cfg.fluxscale below).
For reference, the G280 spectrum was published in Quimby et al. (2018). In this tutorial, we’ve analyzed data taken of PTF12dam on
May 26, 2012, which corresponds to a light curve phase of -18.4 days before peak (+44.2 days after explosion), and illustrated in
Figure 18 in Quimby et al. (2018).
cfg.fluxunits, cfg.fluxscale
('erg / (s * cm**2 * Angstrom)', 1e-17)
fig, ax = plt.subplots(1, 1, figsize=(10, 6))
ax.grid(alpha=0.5)
dat = fits.getdata(f'{ROOT}_x1d.fits')
lrange = (1900, 3500)
wav = (lrange[0] < dat['WAVELENGTH']) & (dat['WAVELENGTH'] < lrange[1])
ax.errorbar(dat['WAVELENGTH'][wav],
dat['FLUX'][wav]*cfg.fluxscale/1e-15,
yerr=dat['UNCERTAINTY'][wav]*cfg.fluxscale/1e-15,
marker='.', markersize=5)
# matplotlib formatting
ax.minorticks_on()
ax.yaxis.set_ticks_position('both'), ax.xaxis.set_ticks_position('both')
ax.tick_params(axis='both', which='minor', direction='in', labelsize=12, length=3)
ax.tick_params(axis='both', which='major', direction='in', labelsize=12, length=5)
ax.set_xlabel(r'Wavelength ($\mathrm{\AA}$)', size=13)
ax.set_ylabel(r'Flux (10$^{-15}$ $erg\ cm^{-2}\ s^{-1}\ \AA^{-1}$)', size=13)
ax.set_title(f"{ROOT.replace('_', ' ')} {GRATING} Spectrum", size=14)
plt.show()
8. Conclusions#
Thank you for going through this notebook. You should now have all the necessary tools to extract a 1D spectrum from WFC3
UVIS grism data using slitlessutils. After completing this notebook, you should be more familiar with:
Downloading data from MAST.
Preprocessing images for
slitlessutils.Creating and displaying region files.
Extracting and plotting a source’s spectrum.
Additional Resources#
Below are some additional resources that may be helpful. Please send any questions through the HST Help Desk.
About this Notebook#
Author: Benjamin Kuhn, WFC3 Team
Last Updated: 2026-03-27
Created 2026-01-20
Citations#
If you use Python packages for published research, please cite the authors.
Follow these links for more information about citing packages used in this notebook:
