
Slitlessutils Cookbook: Spectral Extraction for WFC3/UVIS Subarrays#
This notebook contains a step-by-step guide for performing spectral extractions with Slitlessutils for G280 subarray 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
Embed subarray images
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 World Coordinate System (WCS)
5. Preprocess the G280 Grism Images
6. Embed Subarray Data
7. Preprocess the F200LP Direct Images
7.1 Display Drizzle Image and Overlay Segmentation Map
8. Extract the Spectra from the Grism Data
8.1 Display a Region File
8.2 Plot the Spectrum
9. 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 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 CAL program 16581 and are downloaded from the Mikulski Archive for Space
Telescopes (MAST) via astroquery. The images are all subarray exposures, which require special preprocessing steps,
mainly embedding them into a full-chip image. There is an embedding utility within slitlessutils that we will use before
extracting the 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:14:40.077007
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 by slitlessutils. Used in preprocess_direct() in Section 7. E.g. WFC3. |
TELESCOPE |
The observatory. Needed for slitlessutils. Used in download() and preprocess_direct() in Sections 3 and 7, 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 7 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 7 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 7. Must be in arcsec. E.g. 0.5. |
SCALE |
The pixel scale used in preprocess_direct() in Section 7 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 for slitlessutils. Used in Sections 7 and 8. |
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: ["iels01aaq", "iels01abq", "iels01acq"],
FILTER: ["iels01a8q"]}
RA = 133.73161 # degrees
DEC = -47.57176 # degrees
RAD = 0.5 # arcsec; for seg map
SCALE = 0.03962 # driz image pix scale
ROOT = 'WFC3_UVIS_WR14' # output rootname for driz and later SU
ZEROPOINT = 27.409 # f200lp AB MAG -- obsmode = 'wfc3, uvis2, f200lp, mjd#60833, 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 _flt.fits images of the Wolf-Rayet star, WR14, from CAL program 16581. This target
is a well-established calibration source routinely used by the WFC3 instrument team to derive and validate the wavelength
solution for the UVIS grism (WFC3 ISR 2020-09).
After downloading the images, we move them to a sub-directory within
the current working directory.
def download(datasets, telescope):
"""
Function to download FLT 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': 'FLT',
'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/iels01acq_flt.fits to ./mastDownload/HST/iels01acq/iels01acq_flt.fits ...
[Done]
Downloading URL https://mast.stsci.edu/api/v0.1/Download/file?uri=mast:HST/product/iels01a8q_flt.fits to ./mastDownload/HST/iels01a8q/iels01a8q_flt.fits ...
[Done]
Downloading URL https://mast.stsci.edu/api/v0.1/Download/file?uri=mast:HST/product/iels01abq_flt.fits to ./mastDownload/HST/iels01abq/iels01abq_flt.fits ...
[Done]
Downloading URL https://mast.stsci.edu/api/v0.1/Download/file?uri=mast:HST/product/iels01aaq_flt.fits to ./mastDownload/HST/iels01aaq/iels01aaq_flt.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 FLT 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_subarray_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('*.fits'):
filt = fits.getval(file, 'filter').lower()
print(f"Moving {file} to {filt}/")
shutil.move(file, filt)
Moving iels01acq_flt.fits to g280/
Moving iels01a8q_flt.fits to f200lp/
Moving iels01aaq_flt.fits to g280/
Moving iels01abq_flt.fits to g280/
4. Check the World Coordinate System (WCS)#
It is likely that the WCS in the direct and grism images differ. In this section, we will use slitlessutils to view the active WCS.
To address any disagreement in WCS solutions, users can call the upgrade_wcs or downgrade_wcs functions within slitlessutils.
Regardless of upgrading or downgrading, having a consistent astrometric reference for the direct and grism images is crucial for
successful spectral extraction.
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*flt.fits')
su.core.preprocess.astrometry.list_wcs(f'{FILTER.lower()}/i*flt.fits')
g280/iels01acq_flt.fits G280
+----------+--------------------------------+--------------------------------+
| WCSVERS | 1 |
+----------+--------------------------------+--------------------------------+
| WCSNAME | IDC_2731450pi-GSC240 |
| WCSNAMEA | IDC_2731450pi |
| WCSNAMEB | IDC_2731450pi-GSC240 |
| WCSNAMEO | OPUS |
+----------+--------------------------------+--------------------------------+
g280/iels01aaq_flt.fits G280
+----------+--------------------------------+--------------------------------+
| WCSVERS | 1 |
+----------+--------------------------------+--------------------------------+
| WCSNAME | IDC_2731450pi-GSC240 |
| WCSNAMEA | IDC_2731450pi |
| WCSNAMEB | IDC_2731450pi-GSC240 |
| WCSNAMEO | OPUS |
+----------+--------------------------------+--------------------------------+
g280/iels01abq_flt.fits G280
+----------+--------------------------------+--------------------------------+
| WCSVERS | 1 |
+----------+--------------------------------+--------------------------------+
| WCSNAME | IDC_2731450pi-GSC240 |
| WCSNAMEA | IDC_2731450pi |
| WCSNAMEB | IDC_2731450pi-GSC240 |
| WCSNAMEO | OPUS |
+----------+--------------------------------+--------------------------------+
f200lp/iels01a8q_flt.fits F200LP
+----------+--------------------------------+--------------------------------+
| WCSVERS | 1 |
+----------+--------------------------------+--------------------------------+
| WCSNAME | IDC_2731450pi-GSC240 |
| WCSNAMEA | IDC_2731450pi |
| WCSNAMEB | IDC_2731450pi-GSC240 |
| WCSNAMEO | OPUS |
+----------+--------------------------------+--------------------------------+
As shown above, the F200LP active WCS (IDC_2731450pi-GSC240) is the same as the G280 WCS (IDC_2731450pi-GSC240).
This is an undistorted a priori solution where guide star coordinates are corrected from the original reference frame to the Gaia
DR1-based Guide Star Catalog v2.4.0 (GSC240).
If the active WCS solutions differ between images, one can use slitlessutils to downgrade or upgrade the WCS.
For example, downgrade the WCS with slitlessutils using:
su.core.preprocess.astrometry.downgrade_wcs(img_to_downgrade, key='A', inplace=True)
and upgrade the WCS with:
su.core.preprocess.astrometry.upgrade_wcs(direct_ref_img, girsm_img_to_upgrade)
Where direct_ref_img, girsm_img_to_upgrade, and img_to_downgrade are the images’ paths as a string.
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 here. Users with WFC3/IR
G102/G141 subarray data who wish to complete background subtraction should consider using WFC3
Backsub before embedding. The WFC3/IR slitlessutils notebook shows how to run WFC3 Backsub.
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 here.
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}_flt.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 KNOWN ISSUE and a WARNING about using a subarray and the sky file’s normalization. Both are expected and can be safely ignored.
os.chdir(GRATING.lower())
preprocess_grism(DATASETS, GRATING)
KNOWN ISSUE (logger:132)> Slitlessutils does not fully support subarray data: iels01aaq_flt.fits
See: https://slitlessutils.readthedocs.io/en/latest/knownissues.html
KNOWN ISSUE (logger:132)> Global-sky subtraction may give poor results for subarraydata, especially for small subarrays: iels01aaq_flt.fits
See: https://slitlessutils.readthedocs.io/en/latest/knownissues.html
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.
KNOWN ISSUE (logger:132)> Slitlessutils does not fully support subarray data: iels01abq_flt.fits
See: https://slitlessutils.readthedocs.io/en/latest/knownissues.html
KNOWN ISSUE (logger:132)> Global-sky subtraction may give poor results for subarraydata, especially for small subarrays: iels01abq_flt.fits
See: https://slitlessutils.readthedocs.io/en/latest/knownissues.html
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.
KNOWN ISSUE (logger:132)> Slitlessutils does not fully support subarray data: iels01acq_flt.fits
See: https://slitlessutils.readthedocs.io/en/latest/knownissues.html
KNOWN ISSUE (logger:132)> Global-sky subtraction may give poor results for subarraydata, especially for small subarrays: iels01acq_flt.fits
See: https://slitlessutils.readthedocs.io/en/latest/knownissues.html
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.
Setting up logfile : astrodrizzle.log
AstroDrizzle log file: astrodrizzle.log
AstroDrizzle Version 3.10.0 started at: 14:15:37.190 (10/04/2026)
==== Processing Step Initialization started at 14:15:37.19 (10/04/2026)
WCS Keywords
Number of WCS axes: 2
CTYPE : 'RA---TAN' 'DEC--TAN'
CUNIT : 'deg' 'deg'
CRVAL : 133.7545389447977 -47.56567448769588
CRPIX : 2061.5 509.5
CD1_1 CD1_2 : -9.908616642537148e-06 -4.789735829631489e-06
CD2_1 CD2_2 : -4.789735829631489e-06 9.908616642537148e-06
NAXIS : 4123 1019
********************************************************************************
*
* Estimated memory usage: up to 216 Mb.
* Output image size: 4123 X 1019 pixels.
* Output image file: ~ 48 Mb.
* Cores available: 3
*
********************************************************************************
==== Processing Step Initialization finished at 14:15:37.487 (10/04/2026)
==== Processing Step Static Mask started at 14:15:37.489 (10/04/2026)
==== Processing Step Static Mask finished at 14:15:37.660 (10/04/2026)
==== Processing Step Subtract Sky started at 14:15:37.66 (10/04/2026)
***** skymatch started on 2026-04-10 14:15:37.708300
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: 'iels01aaq_flt.fits'
EXT: 'SCI',1; MASK: iels01aaq_skymatch_mask_sci1.fits[0]
** Input image: 'iels01abq_flt.fits'
EXT: 'SCI',1; MASK: iels01abq_skymatch_mask_sci1.fits[0]
** Input image: 'iels01acq_flt.fits'
EXT: 'SCI',1; MASK: iels01acq_skymatch_mask_sci1.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: iels01aaq_flt.fits
EXT = 'SCI',1
Data units type: COUNTS
EXPTIME: 2.5 [s]
Conversion factor (data->brightness): 254.81855517369576
* Image: iels01abq_flt.fits
EXT = 'SCI',1
Data units type: COUNTS
EXPTIME: 2.5 [s]
Conversion factor (data->brightness): 254.81855517369576
* Image: iels01acq_flt.fits
EXT = 'SCI',1
Data units type: COUNTS
EXPTIME: 2.5 [s]
Conversion factor (data->brightness): 254.81855517369576
----- Computing sky values requested image extensions (detector chips): -----
* Image: 'iels01aaq_flt.fits['SCI',1]' -- SKY = 66.75874328613281 (brightness units)
Sky change (data units):
- EXT = 'SCI',1 delta(MDRIZSKY) = 0.261985 NEW MDRIZSKY = 0.261985
* Image: 'iels01abq_flt.fits['SCI',1]' -- SKY = 112.52604675292969 (brightness units)
Sky change (data units):
- EXT = 'SCI',1 delta(MDRIZSKY) = 0.441593 NEW MDRIZSKY = 0.441593
* Image: 'iels01acq_flt.fits['SCI',1]' -- SKY = 93.95782470703125 (brightness units)
Sky change (data units):
- EXT = 'SCI',1 delta(MDRIZSKY) = 0.368724 NEW MDRIZSKY = 0.368724
***** skymatch ended on 2026-04-10 14:15:38.118959
TOTAL RUN TIME: 0:00:00.410659
==== Processing Step Subtract Sky finished at 14:15:38.154 (10/04/2026)
==== Processing Step Separate Drizzle started at 14:15:38.155 (10/04/2026)
WCS Keywords
Number of WCS axes: 2
CTYPE : 'RA---TAN' 'DEC--TAN'
CUNIT : 'deg' 'deg'
CRVAL : 133.7545389447977 -47.56567448769588
CRPIX : 2061.5 509.5
CD1_1 CD1_2 : -9.908616642537148e-06 -4.789735829631489e-06
CD2_1 CD2_2 : -4.789735829631489e-06 9.908616642537148e-06
NAXIS : 4123 1019
-Generating simple FITS output: iels01abq_single_sci.fits
Writing out image to disk: iels01abq_single_sci.fits
Writing out image to disk: iels01abq_single_wht.fits
-Generating simple FITS output: iels01aaq_single_sci.fits
-Generating simple FITS output: iels01acq_single_sci.fits
Writing out image to disk: iels01aaq_single_sci.fits
Writing out image to disk: iels01acq_single_sci.fits
Writing out image to disk: iels01aaq_single_wht.fits
Writing out image to disk: iels01acq_single_wht.fits
==== Processing Step Separate Drizzle finished at 14:15:39.197 (10/04/2026)
==== Processing Step Create Median started at 14:15:39.198 (10/04/2026)
reference sky value for image 'iels01aaq_flt.fits' is 0.2619853913784027
reference sky value for image 'iels01abq_flt.fits' is 0.441592812538147
reference sky value for image 'iels01acq_flt.fits' is 0.36872440576553345
Saving output median image to: 'su_drizzle_med.fits'
==== Processing Step Create Median finished at 14:15:40.023 (10/04/2026)
==== Processing Step Blot started at 14:15:40.024 (10/04/2026)
Blot: creating blotted image: iels01aaq_flt.fits[sci,1]
Using default C-based coordinate transformation...
-Generating simple FITS output: iels01aaq_sci1_blt.fits
Writing out image to disk: iels01aaq_sci1_blt.fits
Blot: creating blotted image: iels01abq_flt.fits[sci,1]
Using default C-based coordinate transformation...
-Generating simple FITS output: iels01abq_sci1_blt.fits
Writing out image to disk: iels01abq_sci1_blt.fits
Blot: creating blotted image: iels01acq_flt.fits[sci,1]
Using default C-based coordinate transformation...
-Generating simple FITS output: iels01acq_sci1_blt.fits
Writing out image to disk: iels01acq_sci1_blt.fits
==== Processing Step Blot finished at 14:15:41.381 (10/04/2026)
==== Processing Step Driz_CR started at 14:15:41.382 (10/04/2026)
Creating output: iels01acq_sci1_crmask.fits
Creating output: iels01aaq_sci1_crmask.fits
Creating output: iels01abq_sci1_crmask.fits
==== Processing Step Driz_CR finished at 14:15:42.507 (10/04/2026)
==== Processing Step Final Drizzle started at 14:15:42.525 (10/04/2026)
WCS Keywords
Number of WCS axes: 2
CTYPE : 'RA---TAN' 'DEC--TAN'
CUNIT : 'deg' 'deg'
CRVAL : 133.7545389447977 -47.56567448769588
CRPIX : 2061.5 509.5
CD1_1 CD1_2 : -9.908616642537148e-06 -4.789735829631489e-06
CD2_1 CD2_2 : -4.789735829631489e-06 9.908616642537148e-06
NAXIS : 4123 1019
-Generating simple FITS output: su_drizzle_drz_sci.fits
Writing out image to disk: su_drizzle_drz_sci.fits
Writing out image to disk: su_drizzle_drz_wht.fits
Writing out image to disk: su_drizzle_drz_ctx.fits
==== Processing Step Final Drizzle finished at 14:15:45.198 (10/04/2026)
AstroDrizzle Version 3.10.0 is finished processing at 14:15:45.200 (10/04/2026).
-------------------- --------------------
Step Elapsed time
-------------------- --------------------
Initialization 0.2958 sec.
Static Mask 0.1710 sec.
Subtract Sky 0.4935 sec.
Separate Drizzle 1.0417 sec.
Create Median 0.8250 sec.
Blot 1.3571 sec.
Driz_CR 1.1244 sec.
Final Drizzle 2.6733 sec.
==================== ====================
Total 7.9819 sec.
Trailer file written to: astrodrizzle.log
Next, we display one of the grism images and overlay the CR flags from the Data Qualty array for visual inspection.
fig, axs = plt.subplots(1, 1, figsize=(9, 4))
img = fits.getdata(f'{DATASETS[GRATING][0]}_flt.fits', ext=1)
dq = fits.getdata(f'{DATASETS[GRATING][0]}_flt.fits', ext=3)
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)
y, x = np.where(np.bitwise_and(dq, 4096) == 4096)
axs.scatter(x, y, 3, marker='.', label='CR flag')
axs.set_title(f'{GRATING.lower()}/{DATASETS[GRATING][0]}_flt.fits')
cbar = fig.colorbar(im1, ax=axs, orientation='horizontal', pad=0.15, label='e$^-$')
plt.legend()
for t in cbar.ax.get_xticklabels():
t.set_rotation(55)
fig.tight_layout()
plt.show()
6. Embed Subarray Data#
In order to perform spectral extraction on subarray data with slitlessutils, the images must be embedded first.
To embed the subarray data, we use the built-in utility, embedsub_full_chip, within slitlessutils. This function
takes the subarray data and embeds it into a full-chip image, which for UVIS has a shape of 2051x4069. The emnedding utility
works on WFC3 (UVIS/IR) and ACS (WFC) subarrays and saves the original files as ipppssoot_flt_original.fits.
The embedded files will have the LTV*, NAXIS*, CRPIX*, and SUBARRAY header keywords modified. The embedding
utility saves the original values at the end of the HISTORY section in the primary header. The old (original) values are
not needed, but were added for convenience and bookkeeping.
Below, we call the embedding function on the grism and direct images, and then create a subarray/ directory for
the original subarray files. Finally, we display an original and embedded grism image to verify that it looks as expected.
We also show how the original header keywords are appended to the HISTORY section of an embedded file.
os.chdir(cwd)
# loop over all direct and grism images and embed subarray into full-chip image
for file in glob.glob(f"{FILTER.lower()}/*flt.fits")+glob.glob(f"{GRATING.lower()}/*flt.fits"):
dirr = file.split('/')[0]
su.core.utilities.embedding.embedsub_full_chip(file, instrument='UVIS', output_dir=dirr)
# loop over all original direct and grism subarray images and move to new dir
for file in glob.glob(f"{FILTER.lower()}/*original.fits")+glob.glob(f"{GRATING.lower()}/*original.fits"):
dirr = file.split('/')[0]
dst = dirr+'/subarray'
# create subarray directory
if not os.path.isdir(dst):
os.mkdir(dst)
print(f"Moving {file} to {dst}/")
shutil.move(f"{os.path.abspath('./')}/{file}",
f"{os.path.abspath('./')}/{dst}/{file.split('/')[-1]}")
Moving f200lp/iels01a8q_flt_original.fits to f200lp/subarray/
Moving g280/iels01abq_flt_original.fits to g280/subarray/
Moving g280/iels01aaq_flt_original.fits to g280/subarray/
Moving g280/iels01acq_flt_original.fits to g280/subarray/
embed_file = f'{GRATING.lower()}/{DATASETS[GRATING][0]}_flt.fits'
orig_file = f'{GRATING.lower()}/subarray/{DATASETS[GRATING][0]}_flt_original.fits'
fig, [ax1, ax2] = plt.subplots(2, 1, figsize=(9, 7))
embed_img = fits.getdata(embed_file, ext=1)
orig_img = fits.getdata(orig_file, ext=1)
z1, z2 = zscale.get_limits(orig_img)
norm_embed = ImageNormalize(embed_img, vmin=z1, vmax=z2*100, stretch=LogStretch())
norm_orig = ImageNormalize(orig_img, vmin=z1, vmax=z2*100, stretch=LogStretch())
im1 = ax1.imshow(orig_img, origin='lower', cmap='Greys_r', norm=norm_orig)
im2 = ax2.imshow(embed_img, origin='lower', cmap='Greys_r', norm=norm_embed)
cbar2 = fig.colorbar(im2, ax=ax2, orientation='horizontal', pad=0.2, label='e$^-$')
ax1.set_title(f"{orig_file.replace('/', ' ')}")
ax2.set_title(f"{embed_file.replace('/', ' ')} embedded")
for t in cbar2.ax.get_xticklabels():
t.set_rotation(64)
fig.tight_layout()
plt.show()
# the embedding function adds original keywords to HISTORY
fits.getval(embed_file, "HISTORY")[-5:]
This file was created with slitlessutils function, `embedsub_full_chip`
As a result, some header keywords have been modified:
- CRPIX1 and CRPIX2 were originally: 2048.0, -339.0
- LTV1 and LTV2 were originally: 0.0, -589.0
- NAXIS1 and NAXIS2 were originally: 4096, 768
7. Preprocess the F200LP Direct Images#
With the grism data calibrated, data embedded, and consistent WCS for all images, we can continue 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)
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>_drz_sci.fits
Segmentation map : FITS file
<root>_drz_seg.fits
"""
# list of direct image data
files = [f'{imgdset}_flt.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}_drz_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}_drz_sci.fits', img, hdr, overwrite=True)
fits.writeto(f'{root}_drz_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:15:46.705 (10/04/2026)
==== Processing Step Initialization started at 14:15:46.707 (10/04/2026)
Forcibly archiving original of: iels01a8q_flt.fits as OrIg_files/iels01a8q_flt.fits
Turning OFF "preserve" and "restore" actions...
WCS Keywords
Number of WCS axes: 2
CTYPE : 'RA---TAN' 'DEC--TAN'
CUNIT : 'deg' 'deg'
CRVAL : 133.75414297030696 -47.56511821922862
CRPIX : 2358.1227654981353 1935.0681345149071
CD1_1 CD1_2 : -1.1005555555555558e-05 1.3190207845836313e-22
CD2_1 CD2_2 : -4.678572665168487e-24 1.1005555555555558e-05
NAXIS : 4717 3870
********************************************************************************
*
* Estimated memory usage: up to 273 Mb.
* Output image size: 4717 X 3870 pixels.
* Output image file: ~ 208 Mb.
* Cores available: 1
*
********************************************************************************
==== Processing Step Initialization finished at 14:15:46.84 (10/04/2026)
==== Processing Step Static Mask started at 14:15:46.84 (10/04/2026)
==== Processing Step Static Mask finished at 14:15:46.845 (10/04/2026)
==== Processing Step Subtract Sky started at 14:15:46.846 (10/04/2026)
==== Processing Step Subtract Sky finished at 14:15:46.867 (10/04/2026)
==== Processing Step Separate Drizzle started at 14:15:46.868 (10/04/2026)
==== Processing Step Separate Drizzle finished at 14:15:46.870 (10/04/2026)
==== Processing Step Create Median started at 14:15:46.870 (10/04/2026)
==== Processing Step Blot started at 14:15:46.872 (10/04/2026)
==== Processing Step Blot finished at 14:15:46.873 (10/04/2026)
==== Processing Step Driz_CR started at 14:15:46.873 (10/04/2026)
==== Processing Step Final Drizzle started at 14:15:46.875 (10/04/2026)
WCS Keywords
Number of WCS axes: 2
CTYPE : 'RA---TAN' 'DEC--TAN'
CUNIT : 'deg' 'deg'
CRVAL : 133.75414297030696 -47.56511821922862
CRPIX : 2358.1227654981353 1935.0681345149071
CD1_1 CD1_2 : -1.1005555555555558e-05 1.3190207845836313e-22
CD2_1 CD2_2 : -4.678572665168487e-24 1.1005555555555558e-05
NAXIS : 4717 3870
-Generating simple FITS output: WFC3_UVIS_WR14_drz_sci.fits
Writing out image to disk: WFC3_UVIS_WR14_drz_sci.fits
Writing out image to disk: WFC3_UVIS_WR14_drz_wht.fits
Writing out image to disk: WFC3_UVIS_WR14_drz_ctx.fits
==== Processing Step Final Drizzle finished at 14:15:49.481 (10/04/2026)
AstroDrizzle Version 3.10.0 is finished processing at 14:15:49.483 (10/04/2026).
-------------------- --------------------
Step Elapsed time
-------------------- --------------------
Initialization 0.1363 sec.
Static Mask 0.0012 sec.
Subtract Sky 0.0212 sec.
Separate Drizzle 0.0015 sec.
Create Median 0.0000 sec.
Blot 0.0012 sec.
Driz_CR 0.0000 sec.
Final Drizzle 2.6066 sec.
==================== ====================
Total 2.7680 sec.
Trailer file written to: astrodrizzle.log
7.1 Display Drizzle Image and Overlay Segmentation Map#
Next, we display the F105W 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}_drz_sci.fits')
# get seg map coordinates
y, x = np.where(fits.getdata(f'{ROOT}_drz_seg.fits') == 1)
# display the image and seg map coords
fig, axs = plt.subplots(1, 1, figsize=(9, 6))
z1, z2 = zscale.get_limits(d[1100:1600, 3400:4200])
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()
8. 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#60833, 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.409 |
| F300X | 25.049 |
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()}/*_flt.fits')
# load the sources from direct image into slitlessutils
sources = su.sources.SourceCollection(f'../{filt.lower()}/{root}_drz_seg.fits',
f'../{filt.lower()}/{root}_drz_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, ncpu=1)
ext(data, sources, width=15)
os.chdir('../output')
extract_single(GRATING, FILTER, ROOT, ZEROPOINT, tabulate=True)
INFO MainProcess> Loading from glob: ../g280/*_flt.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_WR14_drz_seg.fits
INFO MainProcess> Serial processing
Regions: 0%| | 0/3 [00:00<?, ?it/s]
Regions: 33%|███▎ | 1/3 [00:01<00:02, 1.04s/it]
Regions: 67%|██████▋ | 2/3 [00:01<00:00, 1.66it/s]
Regions: 100%|██████████| 3/3 [00:01<00:00, 2.16it/s]
Regions: 100%|██████████| 3/3 [00:01<00:00, 1.84it/s]
INFO MainProcess> Serial processing
Tabulating: 0%| | 0/3 [00:00<?, ?it/s]
Tabulating: 33%|███▎ | 1/3 [00:33<01:07, 33.53s/it]
Tabulating: 67%|██████▋ | 2/3 [01:08<00:34, 34.50s/it]
Tabulating: 100%|██████████| 3/3 [01:43<00:00, 34.85s/it]
Tabulating: 100%|██████████| 3/3 [01:43<00:00, 34.65s/it]
INFO MainProcess> Serial processing
Extracting (single): 0%| | 0/3 [00:00<?, ?it/s]
INFO MainProcess> Building contamination model for iels01acq/UVIS2
Extracting (single): 33%|███▎ | 1/3 [00:04<00:09, 4.63s/it]
INFO MainProcess> Building contamination model for iels01aaq/UVIS2
Extracting (single): 67%|██████▋ | 2/3 [00:09<00:04, 4.58s/it]
INFO MainProcess> Building contamination model for iels01abq/UVIS2
Extracting (single): 100%|██████████| 3/3 [00:13<00:00, 4.56s/it]
Extracting (single): 100%|██████████| 3/3 [00:13<00:00, 4.57s/it]
INFO MainProcess> Combining the 1d spectra
INFO MainProcess> Writing: /home/runner/work/hst_notebooks/hst_notebooks/notebooks/WFC3/slitlessutils_UVIS_subarray_extraction/output/WFC3_UVIS_WR14_x1d.fits
WARNING: VerifyWarning: Card is too long, comment will be truncated. [astropy.io.fits.card]
8.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 FLT image to display
fig, axs = plt.subplots(1, 1, figsize=(9, 5))
# Display the corresponding region file
region = pyregion.open(f'{DATASETS[GRATING][i]}_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='lower right')
axs.set_ylim(600, 1355)
axs.set_xlim(2048, 4096)
# Display a grism FLT
flt = fits.getdata(f'../{GRATING.lower()}/{DATASETS[GRATING][i]}_flt.fits')
z1, z2 = zscale.get_limits(flt)
inorm = ImageNormalize(flt, vmin=z1, vmax=z2*100, stretch=LogStretch())
im1 = axs.imshow(flt, origin='lower', cmap='Greys_r', norm=inorm)
axs.set_title(f'{DATASETS[GRATING][i]}_flt.fits')
cbar = fig.colorbar(im1, ax=axs, shrink=0.9, pad=0.1, orientation='horizontal', label='e$^-$')
for t in cbar.ax.get_xticklabels():
t.set_rotation(45)
fig.tight_layout()
plt.show()
8.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).
There are a number of emission lines in WR14’s spectrum. A list of fiducial emission lines is provided in Table 2 of WFC3 ISR 2020-09
(Pirzkal), which has been copied over to this notebook in the cell below. It is commented out by default because it makes the plot very
busy, but please uncomment if you would like to see all the locations of all the emission lines used in the G280 wavelength calibration.
Finally, Figure 9 in WFC3 ISR 2020-09 illustrates the +1st-order spectrum of WR14, which can be used for comparison with the extraction
performed here.
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, 7500)
wav = (lrange[0] < dat['WAVELENGTH']) & (dat['WAVELENGTH'] < lrange[1])
ax.errorbar(dat['WAVELENGTH'][wav],
dat['FLUX'][wav]*cfg.fluxscale/1e-12,
yerr=dat['UNCERTAINTY'][wav]*cfg.fluxscale/1e-12,
marker='.', markersize=5)
# overlay emission lines; C, N, Ne, He, and more; from WFC3 ISR 2020-09
# for eline in [2299.2, 2402.6, 2527.4, 2906.0, 3067.0, 3400.0,
# 3704.8, 3932.9, 4073.7, 4334.2, 4444.0, 4654.6,
# 5131.1, 5813.4, 6569.7, 6744.9, 7059.7, 7218.8]:
# ax.axvline(eline, ls='-', color='C0', lw=1, alpha=0.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$^{-12}$ $erg\ cm^{-2}\ s^{-1}\ \AA^{-1}$)', size=13)
ax.set_title(f"{ROOT.replace('_', ' ')} {GRATING} Spectrum", size=14)
plt.show()
9. Conclusions#
Thank you for going through this notebook. You should now have all the necessary tools for extracting a 1D spectrum from WFC3
UVIS grism data with slitlessutils. After completing this notebook, you should be more familiar with:
Downloading data from MAST.
Preprocessing images for
slitlessutils.Embedding subarray images.
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:
