MIRI PSF Photometry with Photutils#

Author: Ori Fox

Submitted: November, 2023
Updated: November, 2023

Use case: PSF Photometry using Photutils. The purpose here is to illustrate the workflow and runtime for using Photutils in a variety of use cases.

Generally, PSF photometry for data from a space telescope is most accurately performed on pre-mosaiced data. The mosaic process changes the inherent PSF, blurring it both due to resampling and mixing PSFs at different detector positions and rotations. Additionally, accurate theoretical PSF models (e.g., from STPSF) are not available for mosaiced data. While an empirical PSF could be constructed (e.g., using Photutils ePSFBuilder) for mosaiced data, the results will generally not be as accurate as performing PSF photometry on the pre-mosaiced data.

NOTE: A companion notebook exists that illustrates how to use perform PSF photometry on both Level 2 and Level 3 data using a new software program called space_phot.
Data: MIRI Data PID 1028 (Calibration Program; Single Star Visit 006 A5V dwarf 2MASSJ17430448+6655015) and MIRI Data PID 1171 (LMC; Multiple Stars).
Tools: photutils, stpsf, jwst
Cross-Instrument: MIRI
Documentation: This notebook is part of a STScI’s larger post-pipeline Data Analysis Tools Ecosystem and can be downloaded directly from the JDAT Notebook Github directory.

Table of contents#

  1. Introduction
    1.1 Python Imports
    1.2 Set up STPSF and Synphot

  2. Download JWST MIRI Data

  3. Single Bright Object
    3.1 Single Level 2 File
    3.2 Generate empirical PSF grid for MIRI F770W using STPSF
    3.3 PSF Photometry

  4. Faint/Upper Limit, Single Object
    4.1 Multiple, Level2 Files

  5. Stellar Field (LMC)
    5.1 Multiple Stars, Single Level 2 File
    5.2 Generate empirical PSF grid for MIRI F560W using STPSF
    5.3 PSF Photometry

1. Introduction #

GOALS:

Perform PSF photometry on JWST MIRI images with the Photutils PSF Photometry tools using a grid of empirical PSF models from STPSF.

The notebook shows how to:

Data:

MIRI Data PID 1028 (Calibration Program), F770W
MIRI Data PID 1171 (LMC), F560W/F770W

1.1 Python Imports #

import glob
import os
import shutil
import tarfile
from pandas import DataFrame

import matplotlib.pyplot as plt
import numpy as np
import stpsf
from urllib.parse import urlparse
import requests
import astropy.units as u
from astropy.coordinates import SkyCoord
from astropy.io import fits
from astropy.nddata import extract_array
from astropy.table import Table
from astropy.visualization import simple_norm
from astroquery.mast import Observations
from jwst.datamodels import ImageModel
from photutils.aperture import CircularAperture
from photutils.background import LocalBackground, MADStdBackgroundRMS, MMMBackground
from photutils.detection import DAOStarFinder
from photutils.psf import GriddedPSFModel, PSFPhotometry

1.2 Download and Set up Required Data for STPSF and Synphot #

# Set environmental variables
os.environ["STPSF_PATH"] = "./stpsf-data/stpsf-data"
os.environ["PYSYN_CDBS"] = "./grp/redcat/trds/"

# required stpsf data
boxlink = 'https://stsci.box.com/shared/static/kqfolg2bfzqc4mjkgmujo06d3iaymahv.gz'
boxfile = './stpsf-data-LATEST.tar.gz'
synphot_url = 'http://ssb.stsci.edu/trds/tarfiles/synphot5.tar.gz'
synphot_file = './synphot5.tar.gz'

stpsf_folder = './stpsf-data'
synphot_folder = './grp'


def download_file(url, dest_path, timeout=60):
    parsed_url = urlparse(url)
    if parsed_url.scheme not in ["http", "https"]:
        raise ValueError(f"Unsupported URL scheme: {parsed_url.scheme}")

    response = requests.get(url, stream=True, timeout=timeout)
    response.raise_for_status()
    with open(dest_path, "wb") as f:
        for chunk in response.iter_content(chunk_size=8192):
            f.write(chunk)


# Gather stpsf files
psfExist = os.path.exists(stpsf_folder)
if not psfExist:
    os.makedirs(stpsf_folder)
    download_file(boxlink, boxfile)
    gzf = tarfile.open(boxfile)
    gzf.extractall(stpsf_folder, filter='data')

# Gather synphot files
synExist = os.path.exists(synphot_folder)
if not synExist:
    os.makedirs(synphot_folder)
    download_file(synphot_url, synphot_file)
    gzf = tarfile.open(synphot_file)
    gzf.extractall('./', filter='data')

2. Download JWST MIRI Data #

# Download Proposal ID 1028 F770W data

# Define source and destination directories
source_dir = 'mastDownload/JWST/'
destination_dir = 'mast/01028/'

if os.path.isdir(destination_dir):
    print(f'Data already downloaded to {os.path.abspath(destination_dir)}')
else:
    # Query the MAST (Mikulski Archive for Space Telescopes) database for observations
    # with proposal ID 1028 and the F770W filter
    obs = Observations.query_criteria(proposal_id=1028, filters=['F770W'])
    
    # Get a list of products associated with the located observation
    plist = Observations.get_product_list(obs)
    
    # Filter the product list to include only specific product subgroups
    fplist = Observations.filter_products(plist, productSubGroupDescription=['CAL', 'I2D', 'ASN'])
    
    # Download the selected products from the MAST database
    Observations.download_products(fplist)
    
    # Create the destination directory
    os.makedirs(destination_dir)
    
    # Use glob to find all files matching the pattern
    files_to_copy = glob.glob(os.path.join(source_dir, 'j*/jw01028*'))

    # Copy the matching files to the destination directory
    for file_path in files_to_copy:
        shutil.copy(file_path, destination_dir)
Downloading URL https://mast.stsci.edu/api/v0.1/Download/file?uri=mast:JWST/product/jw01028-o006_20250617t142750_image2_00013_asn.json to ./mastDownload/JWST/jw01028006001_02101_00004_mirimage/jw01028-o006_20250617t142750_image2_00013_asn.json ...
 [Done]
Downloading URL https://mast.stsci.edu/api/v0.1/Download/file?uri=mast:JWST/product/jw01028-o006_20250617t142750_image2_00014_asn.json to ./mastDownload/JWST/jw01028006001_02101_00003_mirimage/jw01028-o006_20250617t142750_image2_00014_asn.json ...
 [Done]
Downloading URL https://mast.stsci.edu/api/v0.1/Download/file?uri=mast:JWST/product/jw01028-o006_20250617t142750_image2_00015_asn.json to ./mastDownload/JWST/jw01028006001_02101_00002_mirimage/jw01028-o006_20250617t142750_image2_00015_asn.json ...
 [Done]
Downloading URL https://mast.stsci.edu/api/v0.1/Download/file?uri=mast:JWST/product/jw01028-o006_20250617t142750_image2_00016_asn.json to ./mastDownload/JWST/jw01028006001_02101_00001_mirimage/jw01028-o006_20250617t142750_image2_00016_asn.json ...
 [Done]
Downloading URL https://mast.stsci.edu/api/v0.1/Download/file?uri=mast:JWST/product/jw01028-o006_20250617t142750_image3_00004_asn.json to ./mastDownload/JWST/jw01028-o006_t001_miri_f770w/jw01028-o006_20250617t142750_image3_00004_asn.json ...
 [Done]
Downloading URL https://mast.stsci.edu/api/v0.1/Download/file?uri=mast:JWST/product/jw01028-o006_t001_miri_f770w_i2d.fits to ./mastDownload/JWST/jw01028-o006_t001_miri_f770w/jw01028-o006_t001_miri_f770w_i2d.fits ...
 [Done]
Downloading URL https://mast.stsci.edu/api/v0.1/Download/file?uri=mast:JWST/product/jw01028006001_02101_00001_mirimage_cal.fits to ./mastDownload/JWST/jw01028006001_02101_00001_mirimage/jw01028006001_02101_00001_mirimage_cal.fits ...
 [Done]
Downloading URL https://mast.stsci.edu/api/v0.1/Download/file?uri=mast:JWST/product/jw01028006001_02101_00001_mirimage_i2d.fits to ./mastDownload/JWST/jw01028006001_02101_00001_mirimage/jw01028006001_02101_00001_mirimage_i2d.fits ...
 [Done]
Downloading URL https://mast.stsci.edu/api/v0.1/Download/file?uri=mast:JWST/product/jw01028006001_02101_00002_mirimage_cal.fits to ./mastDownload/JWST/jw01028006001_02101_00002_mirimage/jw01028006001_02101_00002_mirimage_cal.fits ...
 [Done]
Downloading URL https://mast.stsci.edu/api/v0.1/Download/file?uri=mast:JWST/product/jw01028006001_02101_00002_mirimage_i2d.fits to ./mastDownload/JWST/jw01028006001_02101_00002_mirimage/jw01028006001_02101_00002_mirimage_i2d.fits ...
 [Done]
Downloading URL https://mast.stsci.edu/api/v0.1/Download/file?uri=mast:JWST/product/jw01028006001_02101_00003_mirimage_cal.fits to ./mastDownload/JWST/jw01028006001_02101_00003_mirimage/jw01028006001_02101_00003_mirimage_cal.fits ...
 [Done]
Downloading URL https://mast.stsci.edu/api/v0.1/Download/file?uri=mast:JWST/product/jw01028006001_02101_00003_mirimage_i2d.fits to ./mastDownload/JWST/jw01028006001_02101_00003_mirimage/jw01028006001_02101_00003_mirimage_i2d.fits ...
 [Done]
Downloading URL https://mast.stsci.edu/api/v0.1/Download/file?uri=mast:JWST/product/jw01028006001_02101_00004_mirimage_cal.fits to ./mastDownload/JWST/jw01028006001_02101_00004_mirimage/jw01028006001_02101_00004_mirimage_cal.fits ...
 [Done]
Downloading URL https://mast.stsci.edu/api/v0.1/Download/file?uri=mast:JWST/product/jw01028006001_02101_00004_mirimage_i2d.fits to ./mastDownload/JWST/jw01028006001_02101_00004_mirimage/jw01028006001_02101_00004_mirimage_i2d.fits ...
 [Done]
# Download Proposal ID 1171 F550W and F770W data

# Define source and destination directories
source_dir = 'mastDownload/JWST/'
destination_dir = 'mast/01171/'

if os.path.isdir(destination_dir):
    print(f'Data already downloaded to {os.path.abspath(destination_dir)}')
else:
    # Query the MAST (Mikulski Archive for Space Telescopes) database for observations
    # with proposal ID 1171 and the F550W and F770W filters
    obs = Observations.query_criteria(proposal_id=1171, filters=['F560W', 'F770W'])
    
    # Get a list of products associated with the located observation
    plist = Observations.get_product_list(obs)
    
    # Filter the product list to include only specific product subgroups
    fplist = Observations.filter_products(plist, productSubGroupDescription=['CAL', 'I2D', 'ASN'])
    
    # Download the selected products from the MAST database
    Observations.download_products(fplist)
    
    # Create the destination directory
    os.makedirs(destination_dir)
    
    # Use glob to find all files matching the pattern
    files_to_copy = glob.glob(os.path.join(source_dir, 'j*/jw01171*'))
    
    # Copy the matching files to the destination directory
    for file_path in files_to_copy:
        shutil.copy(file_path, destination_dir)
Downloading URL https://mast.stsci.edu/api/v0.1/Download/file?uri=mast:JWST/product/jw01171-o001_20231102t191130_image2_00005_asn.json to ./mastDownload/JWST/jw01171001001_02101_00004_mirimage/jw01171-o001_20231102t191130_image2_00005_asn.json ...
 [Done]
Downloading URL https://mast.stsci.edu/api/v0.1/Download/file?uri=mast:JWST/product/jw01171-o001_20231102t191130_image2_00006_asn.json to ./mastDownload/JWST/jw01171001001_02101_00003_mirimage/jw01171-o001_20231102t191130_image2_00006_asn.json ...
 [Done]
Downloading URL https://mast.stsci.edu/api/v0.1/Download/file?uri=mast:JWST/product/jw01171-o001_20231102t191130_image2_00007_asn.json to ./mastDownload/JWST/jw01171001001_02101_00002_mirimage/jw01171-o001_20231102t191130_image2_00007_asn.json ...
 [Done]
Downloading URL https://mast.stsci.edu/api/v0.1/Download/file?uri=mast:JWST/product/jw01171-o001_20231102t191130_image2_00008_asn.json to ./mastDownload/JWST/jw01171001001_02101_00001_mirimage/jw01171-o001_20231102t191130_image2_00008_asn.json ...
 [Done]
Downloading URL https://mast.stsci.edu/api/v0.1/Download/file?uri=mast:JWST/product/jw01171-o001_20231102t191130_image3_00002_asn.json to ./mastDownload/JWST/jw01171-o001_t001_miri_f770w/jw01171-o001_20231102t191130_image3_00002_asn.json ...
 [Done]
Downloading URL https://mast.stsci.edu/api/v0.1/Download/file?uri=mast:JWST/product/jw01171-o001_t001_miri_f770w_i2d.fits to ./mastDownload/JWST/jw01171-o001_t001_miri_f770w/jw01171-o001_t001_miri_f770w_i2d.fits ...
 [Done]
Downloading URL https://mast.stsci.edu/api/v0.1/Download/file?uri=mast:JWST/product/jw01171-o002_20231102t191130_image2_00005_asn.json to ./mastDownload/JWST/jw01171002001_02101_00004_mirimage/jw01171-o002_20231102t191130_image2_00005_asn.json ...
 [Done]
Downloading URL https://mast.stsci.edu/api/v0.1/Download/file?uri=mast:JWST/product/jw01171-o002_20231102t191130_image2_00006_asn.json to ./mastDownload/JWST/jw01171002001_02101_00003_mirimage/jw01171-o002_20231102t191130_image2_00006_asn.json ...
 [Done]
Downloading URL https://mast.stsci.edu/api/v0.1/Download/file?uri=mast:JWST/product/jw01171-o002_20231102t191130_image2_00007_asn.json to ./mastDownload/JWST/jw01171002001_02101_00002_mirimage/jw01171-o002_20231102t191130_image2_00007_asn.json ...
 [Done]
Downloading URL https://mast.stsci.edu/api/v0.1/Download/file?uri=mast:JWST/product/jw01171-o002_20231102t191130_image2_00008_asn.json to ./mastDownload/JWST/jw01171002001_02101_00001_mirimage/jw01171-o002_20231102t191130_image2_00008_asn.json ...
 [Done]
Downloading URL https://mast.stsci.edu/api/v0.1/Download/file?uri=mast:JWST/product/jw01171-o002_20231102t191130_image3_00002_asn.json to ./mastDownload/JWST/jw01171-o002_t001_miri_f770w/jw01171-o002_20231102t191130_image3_00002_asn.json ...
 [Done]
Downloading URL https://mast.stsci.edu/api/v0.1/Download/file?uri=mast:JWST/product/jw01171-o002_t001_miri_f770w_i2d.fits to ./mastDownload/JWST/jw01171-o002_t001_miri_f770w/jw01171-o002_t001_miri_f770w_i2d.fits ...
 [Done]
Downloading URL https://mast.stsci.edu/api/v0.1/Download/file?uri=mast:JWST/product/jw01171-o003_20231102t191130_image2_00005_asn.json to ./mastDownload/JWST/jw01171003001_02101_00004_mirimage/jw01171-o003_20231102t191130_image2_00005_asn.json ...
 [Done]
Downloading URL https://mast.stsci.edu/api/v0.1/Download/file?uri=mast:JWST/product/jw01171-o003_20231102t191130_image2_00006_asn.json to ./mastDownload/JWST/jw01171003001_02101_00003_mirimage/jw01171-o003_20231102t191130_image2_00006_asn.json ...
 [Done]
Downloading URL https://mast.stsci.edu/api/v0.1/Download/file?uri=mast:JWST/product/jw01171-o003_20231102t191130_image2_00007_asn.json to ./mastDownload/JWST/jw01171003001_02101_00002_mirimage/jw01171-o003_20231102t191130_image2_00007_asn.json ...
 [Done]
Downloading URL https://mast.stsci.edu/api/v0.1/Download/file?uri=mast:JWST/product/jw01171-o003_20231102t191130_image2_00008_asn.json to ./mastDownload/JWST/jw01171003001_02101_00001_mirimage/jw01171-o003_20231102t191130_image2_00008_asn.json ...
 [Done]
Downloading URL https://mast.stsci.edu/api/v0.1/Download/file?uri=mast:JWST/product/jw01171-o003_20231102t191130_image3_00002_asn.json to ./mastDownload/JWST/jw01171-o003_t001_miri_f770w/jw01171-o003_20231102t191130_image3_00002_asn.json ...
 [Done]
Downloading URL https://mast.stsci.edu/api/v0.1/Download/file?uri=mast:JWST/product/jw01171-o003_t001_miri_f770w_i2d.fits to ./mastDownload/JWST/jw01171-o003_t001_miri_f770w/jw01171-o003_t001_miri_f770w_i2d.fits ...
 [Done]
Downloading URL https://mast.stsci.edu/api/v0.1/Download/file?uri=mast:JWST/product/jw01171-o004_20231102t191130_image2_00005_asn.json to ./mastDownload/JWST/jw01171004001_02101_00004_mirimage/jw01171-o004_20231102t191130_image2_00005_asn.json ...
 [Done]
Downloading URL https://mast.stsci.edu/api/v0.1/Download/file?uri=mast:JWST/product/jw01171-o004_20231102t191130_image2_00006_asn.json to ./mastDownload/JWST/jw01171004001_02101_00003_mirimage/jw01171-o004_20231102t191130_image2_00006_asn.json ...
 [Done]
Downloading URL https://mast.stsci.edu/api/v0.1/Download/file?uri=mast:JWST/product/jw01171-o004_20231102t191130_image2_00007_asn.json to ./mastDownload/JWST/jw01171004001_02101_00002_mirimage/jw01171-o004_20231102t191130_image2_00007_asn.json ...
 [Done]
Downloading URL https://mast.stsci.edu/api/v0.1/Download/file?uri=mast:JWST/product/jw01171-o004_20231102t191130_image2_00008_asn.json to ./mastDownload/JWST/jw01171004001_02101_00001_mirimage/jw01171-o004_20231102t191130_image2_00008_asn.json ...
 [Done]
Downloading URL https://mast.stsci.edu/api/v0.1/Download/file?uri=mast:JWST/product/jw01171-o004_20231102t191130_image3_00002_asn.json to ./mastDownload/JWST/jw01171-o004_t001_miri_f560w/jw01171-o004_20231102t191130_image3_00002_asn.json ...
 [Done]
Downloading URL https://mast.stsci.edu/api/v0.1/Download/file?uri=mast:JWST/product/jw01171-o004_t001_miri_f560w_i2d.fits to ./mastDownload/JWST/jw01171-o004_t001_miri_f560w/jw01171-o004_t001_miri_f560w_i2d.fits ...
 [Done]
Downloading URL https://mast.stsci.edu/api/v0.1/Download/file?uri=mast:JWST/product/jw01171-o005_20231102t191130_image2_00005_asn.json to ./mastDownload/JWST/jw01171005001_02101_00004_mirimage/jw01171-o005_20231102t191130_image2_00005_asn.json ...
 [Done]
Downloading URL https://mast.stsci.edu/api/v0.1/Download/file?uri=mast:JWST/product/jw01171-o005_20231102t191130_image2_00006_asn.json to ./mastDownload/JWST/jw01171005001_02101_00003_mirimage/jw01171-o005_20231102t191130_image2_00006_asn.json ...
 [Done]
Downloading URL https://mast.stsci.edu/api/v0.1/Download/file?uri=mast:JWST/product/jw01171-o005_20231102t191130_image2_00007_asn.json to ./mastDownload/JWST/jw01171005001_02101_00002_mirimage/jw01171-o005_20231102t191130_image2_00007_asn.json ...
 [Done]
Downloading URL https://mast.stsci.edu/api/v0.1/Download/file?uri=mast:JWST/product/jw01171-o005_20231102t191130_image2_00008_asn.json to ./mastDownload/JWST/jw01171005001_02101_00001_mirimage/jw01171-o005_20231102t191130_image2_00008_asn.json ...
 [Done]
Downloading URL https://mast.stsci.edu/api/v0.1/Download/file?uri=mast:JWST/product/jw01171-o005_20231102t191130_image3_00002_asn.json to ./mastDownload/JWST/jw01171-o005_t001_miri_f770w/jw01171-o005_20231102t191130_image3_00002_asn.json ...
 [Done]
Downloading URL https://mast.stsci.edu/api/v0.1/Download/file?uri=mast:JWST/product/jw01171-o005_t001_miri_f770w_i2d.fits to ./mastDownload/JWST/jw01171-o005_t001_miri_f770w/jw01171-o005_t001_miri_f770w_i2d.fits ...
 [Done]
Downloading URL https://mast.stsci.edu/api/v0.1/Download/file?uri=mast:JWST/product/jw01171001001_02101_00001_mirimage_cal.fits to ./mastDownload/JWST/jw01171001001_02101_00001_mirimage/jw01171001001_02101_00001_mirimage_cal.fits ...
 [Done]
Downloading URL https://mast.stsci.edu/api/v0.1/Download/file?uri=mast:JWST/product/jw01171001001_02101_00001_mirimage_i2d.fits to ./mastDownload/JWST/jw01171001001_02101_00001_mirimage/jw01171001001_02101_00001_mirimage_i2d.fits ...
 [Done]
Downloading URL https://mast.stsci.edu/api/v0.1/Download/file?uri=mast:JWST/product/jw01171001001_02101_00002_mirimage_cal.fits to ./mastDownload/JWST/jw01171001001_02101_00002_mirimage/jw01171001001_02101_00002_mirimage_cal.fits ...
 [Done]
Downloading URL https://mast.stsci.edu/api/v0.1/Download/file?uri=mast:JWST/product/jw01171001001_02101_00002_mirimage_i2d.fits to ./mastDownload/JWST/jw01171001001_02101_00002_mirimage/jw01171001001_02101_00002_mirimage_i2d.fits ...
 [Done]
Downloading URL https://mast.stsci.edu/api/v0.1/Download/file?uri=mast:JWST/product/jw01171001001_02101_00003_mirimage_cal.fits to ./mastDownload/JWST/jw01171001001_02101_00003_mirimage/jw01171001001_02101_00003_mirimage_cal.fits ...
 [Done]
Downloading URL https://mast.stsci.edu/api/v0.1/Download/file?uri=mast:JWST/product/jw01171001001_02101_00003_mirimage_i2d.fits to ./mastDownload/JWST/jw01171001001_02101_00003_mirimage/jw01171001001_02101_00003_mirimage_i2d.fits ...
 [Done]
Downloading URL https://mast.stsci.edu/api/v0.1/Download/file?uri=mast:JWST/product/jw01171001001_02101_00004_mirimage_cal.fits to ./mastDownload/JWST/jw01171001001_02101_00004_mirimage/jw01171001001_02101_00004_mirimage_cal.fits ...
 [Done]
Downloading URL https://mast.stsci.edu/api/v0.1/Download/file?uri=mast:JWST/product/jw01171001001_02101_00004_mirimage_i2d.fits to ./mastDownload/JWST/jw01171001001_02101_00004_mirimage/jw01171001001_02101_00004_mirimage_i2d.fits ...
 [Done]
Downloading URL https://mast.stsci.edu/api/v0.1/Download/file?uri=mast:JWST/product/jw01171002001_02101_00001_mirimage_cal.fits to ./mastDownload/JWST/jw01171002001_02101_00001_mirimage/jw01171002001_02101_00001_mirimage_cal.fits ...
 [Done]
Downloading URL https://mast.stsci.edu/api/v0.1/Download/file?uri=mast:JWST/product/jw01171002001_02101_00001_mirimage_i2d.fits to ./mastDownload/JWST/jw01171002001_02101_00001_mirimage/jw01171002001_02101_00001_mirimage_i2d.fits ...
 [Done]
Downloading URL https://mast.stsci.edu/api/v0.1/Download/file?uri=mast:JWST/product/jw01171002001_02101_00002_mirimage_cal.fits to ./mastDownload/JWST/jw01171002001_02101_00002_mirimage/jw01171002001_02101_00002_mirimage_cal.fits ...
 [Done]
Downloading URL https://mast.stsci.edu/api/v0.1/Download/file?uri=mast:JWST/product/jw01171002001_02101_00002_mirimage_i2d.fits to ./mastDownload/JWST/jw01171002001_02101_00002_mirimage/jw01171002001_02101_00002_mirimage_i2d.fits ...
 [Done]
Downloading URL https://mast.stsci.edu/api/v0.1/Download/file?uri=mast:JWST/product/jw01171002001_02101_00003_mirimage_cal.fits to ./mastDownload/JWST/jw01171002001_02101_00003_mirimage/jw01171002001_02101_00003_mirimage_cal.fits ...
 [Done]
Downloading URL https://mast.stsci.edu/api/v0.1/Download/file?uri=mast:JWST/product/jw01171002001_02101_00003_mirimage_i2d.fits to ./mastDownload/JWST/jw01171002001_02101_00003_mirimage/jw01171002001_02101_00003_mirimage_i2d.fits ...
 [Done]
Downloading URL https://mast.stsci.edu/api/v0.1/Download/file?uri=mast:JWST/product/jw01171002001_02101_00004_mirimage_cal.fits to ./mastDownload/JWST/jw01171002001_02101_00004_mirimage/jw01171002001_02101_00004_mirimage_cal.fits ...
 [Done]
Downloading URL https://mast.stsci.edu/api/v0.1/Download/file?uri=mast:JWST/product/jw01171002001_02101_00004_mirimage_i2d.fits to ./mastDownload/JWST/jw01171002001_02101_00004_mirimage/jw01171002001_02101_00004_mirimage_i2d.fits ...
 [Done]
Downloading URL https://mast.stsci.edu/api/v0.1/Download/file?uri=mast:JWST/product/jw01171003001_02101_00001_mirimage_cal.fits to ./mastDownload/JWST/jw01171003001_02101_00001_mirimage/jw01171003001_02101_00001_mirimage_cal.fits ...
 [Done]
Downloading URL https://mast.stsci.edu/api/v0.1/Download/file?uri=mast:JWST/product/jw01171003001_02101_00001_mirimage_i2d.fits to ./mastDownload/JWST/jw01171003001_02101_00001_mirimage/jw01171003001_02101_00001_mirimage_i2d.fits ...
 [Done]
Downloading URL https://mast.stsci.edu/api/v0.1/Download/file?uri=mast:JWST/product/jw01171003001_02101_00002_mirimage_cal.fits to ./mastDownload/JWST/jw01171003001_02101_00002_mirimage/jw01171003001_02101_00002_mirimage_cal.fits ...
 [Done]
Downloading URL https://mast.stsci.edu/api/v0.1/Download/file?uri=mast:JWST/product/jw01171003001_02101_00002_mirimage_i2d.fits to ./mastDownload/JWST/jw01171003001_02101_00002_mirimage/jw01171003001_02101_00002_mirimage_i2d.fits ...
 [Done]
Downloading URL https://mast.stsci.edu/api/v0.1/Download/file?uri=mast:JWST/product/jw01171003001_02101_00003_mirimage_cal.fits to ./mastDownload/JWST/jw01171003001_02101_00003_mirimage/jw01171003001_02101_00003_mirimage_cal.fits ...
 [Done]
Downloading URL https://mast.stsci.edu/api/v0.1/Download/file?uri=mast:JWST/product/jw01171003001_02101_00003_mirimage_i2d.fits to ./mastDownload/JWST/jw01171003001_02101_00003_mirimage/jw01171003001_02101_00003_mirimage_i2d.fits ...
 [Done]
Downloading URL https://mast.stsci.edu/api/v0.1/Download/file?uri=mast:JWST/product/jw01171003001_02101_00004_mirimage_cal.fits to ./mastDownload/JWST/jw01171003001_02101_00004_mirimage/jw01171003001_02101_00004_mirimage_cal.fits ...
 [Done]
Downloading URL https://mast.stsci.edu/api/v0.1/Download/file?uri=mast:JWST/product/jw01171003001_02101_00004_mirimage_i2d.fits to ./mastDownload/JWST/jw01171003001_02101_00004_mirimage/jw01171003001_02101_00004_mirimage_i2d.fits ...
 [Done]
Downloading URL https://mast.stsci.edu/api/v0.1/Download/file?uri=mast:JWST/product/jw01171004001_02101_00001_mirimage_cal.fits to ./mastDownload/JWST/jw01171004001_02101_00001_mirimage/jw01171004001_02101_00001_mirimage_cal.fits ...
 [Done]
Downloading URL https://mast.stsci.edu/api/v0.1/Download/file?uri=mast:JWST/product/jw01171004001_02101_00001_mirimage_i2d.fits to ./mastDownload/JWST/jw01171004001_02101_00001_mirimage/jw01171004001_02101_00001_mirimage_i2d.fits ...
 [Done]
Downloading URL https://mast.stsci.edu/api/v0.1/Download/file?uri=mast:JWST/product/jw01171004001_02101_00002_mirimage_cal.fits to ./mastDownload/JWST/jw01171004001_02101_00002_mirimage/jw01171004001_02101_00002_mirimage_cal.fits ...
 [Done]
Downloading URL https://mast.stsci.edu/api/v0.1/Download/file?uri=mast:JWST/product/jw01171004001_02101_00002_mirimage_i2d.fits to ./mastDownload/JWST/jw01171004001_02101_00002_mirimage/jw01171004001_02101_00002_mirimage_i2d.fits ...
 [Done]
Downloading URL https://mast.stsci.edu/api/v0.1/Download/file?uri=mast:JWST/product/jw01171004001_02101_00003_mirimage_cal.fits to ./mastDownload/JWST/jw01171004001_02101_00003_mirimage/jw01171004001_02101_00003_mirimage_cal.fits ...
 [Done]
Downloading URL https://mast.stsci.edu/api/v0.1/Download/file?uri=mast:JWST/product/jw01171004001_02101_00003_mirimage_i2d.fits to ./mastDownload/JWST/jw01171004001_02101_00003_mirimage/jw01171004001_02101_00003_mirimage_i2d.fits ...
 [Done]
Downloading URL https://mast.stsci.edu/api/v0.1/Download/file?uri=mast:JWST/product/jw01171004001_02101_00004_mirimage_cal.fits to ./mastDownload/JWST/jw01171004001_02101_00004_mirimage/jw01171004001_02101_00004_mirimage_cal.fits ...
 [Done]
Downloading URL https://mast.stsci.edu/api/v0.1/Download/file?uri=mast:JWST/product/jw01171004001_02101_00004_mirimage_i2d.fits to ./mastDownload/JWST/jw01171004001_02101_00004_mirimage/jw01171004001_02101_00004_mirimage_i2d.fits ...
 [Done]
Downloading URL https://mast.stsci.edu/api/v0.1/Download/file?uri=mast:JWST/product/jw01171005001_02101_00001_mirimage_cal.fits to ./mastDownload/JWST/jw01171005001_02101_00001_mirimage/jw01171005001_02101_00001_mirimage_cal.fits ...
 [Done]
Downloading URL https://mast.stsci.edu/api/v0.1/Download/file?uri=mast:JWST/product/jw01171005001_02101_00001_mirimage_i2d.fits to ./mastDownload/JWST/jw01171005001_02101_00001_mirimage/jw01171005001_02101_00001_mirimage_i2d.fits ...
 [Done]
Downloading URL https://mast.stsci.edu/api/v0.1/Download/file?uri=mast:JWST/product/jw01171005001_02101_00002_mirimage_cal.fits to ./mastDownload/JWST/jw01171005001_02101_00002_mirimage/jw01171005001_02101_00002_mirimage_cal.fits ...
 [Done]
Downloading URL https://mast.stsci.edu/api/v0.1/Download/file?uri=mast:JWST/product/jw01171005001_02101_00002_mirimage_i2d.fits to ./mastDownload/JWST/jw01171005001_02101_00002_mirimage/jw01171005001_02101_00002_mirimage_i2d.fits ...
 [Done]
Downloading URL https://mast.stsci.edu/api/v0.1/Download/file?uri=mast:JWST/product/jw01171005001_02101_00003_mirimage_cal.fits to ./mastDownload/JWST/jw01171005001_02101_00003_mirimage/jw01171005001_02101_00003_mirimage_cal.fits ...
 [Done]
Downloading URL https://mast.stsci.edu/api/v0.1/Download/file?uri=mast:JWST/product/jw01171005001_02101_00003_mirimage_i2d.fits to ./mastDownload/JWST/jw01171005001_02101_00003_mirimage/jw01171005001_02101_00003_mirimage_i2d.fits ...
 [Done]
Downloading URL https://mast.stsci.edu/api/v0.1/Download/file?uri=mast:JWST/product/jw01171005001_02101_00004_mirimage_cal.fits to ./mastDownload/JWST/jw01171005001_02101_00004_mirimage/jw01171005001_02101_00004_mirimage_cal.fits ...
 [Done]
Downloading URL https://mast.stsci.edu/api/v0.1/Download/file?uri=mast:JWST/product/jw01171005001_02101_00004_mirimage_i2d.fits to ./mastDownload/JWST/jw01171005001_02101_00004_mirimage/jw01171005001_02101_00004_mirimage_i2d.fits ...
 [Done]

3. Single Bright Star #

The purpose of this section is to illustrate how to perform PSF photometry on a single bright star. While aperture photometry is feasible in isolated cases, the user may find PSF photometry preferable in crowded fields or complicated backgrounds.

3.1 Single Level 2 File #

In this example, we fit a single, bright source in a single Level 2 images. For a collection of Level 2 images, we could fit each Level 2 image individually and then average the measured fluxes.

Useful references:
HST Documentation on PSF Photometry: https://www.stsci.edu/hst/instrumentation/wfc3/data-analysis/psf
WFPC2 Stellar Photometry with HSTPHOT: https://ui.adsabs.harvard.edu/abs/2000PASP..112.1383D/abstract
Photutils PSF Fitting Photometry: https://photutils.readthedocs.io/en/stable/user_guide/psf.html

# get the level 2 filenames
path = "./mast/01028/"
level2 = sorted(glob.glob(os.path.join(path, '*cal.fits')))
level2
['./mast/01028/jw01028006001_02101_00001_mirimage_cal.fits',
 './mast/01028/jw01028006001_02101_00002_mirimage_cal.fits',
 './mast/01028/jw01028006001_02101_00003_mirimage_cal.fits',
 './mast/01028/jw01028006001_02101_00004_mirimage_cal.fits']
# display the first level-2 image
data = fits.getdata(level2[0])
norm = simple_norm(data, 'sqrt', percent=99)

fig, ax = plt.subplots(figsize=(20, 12))
im = ax.imshow(data, origin='lower', norm=norm, cmap='gray')
clb = plt.colorbar(im, label='MJy/sr')
ax.set_xlabel('Pixels')
ax.set_ylabel('Pixels')
plt.show()
../../../_images/56f1b85051ea1d00693f320e05330e1d1acfb15835a42ec8f1b2def0704ef46b.png
# Change all DQ flagged pixels to NaN.
# Here, we'll overwrite the original CAL file.
# Reference for JWST DQ Flag Definitions: https://jwst-pipeline.readthedocs.io/en/latest/jwst/references_general/references_general.html
# In this case, we choose all DQ > 10, but users are encouraged to choose their own values accordingly.
filename = level2[0]
with ImageModel(filename) as model:
    model.data[model.dq >= 10] = np.nan
    model.save(filename)
# Re-display the image
data = fits.getdata(level2[0])
norm = simple_norm(data, 'sqrt', percent=99)

fig, ax = plt.subplots(figsize=(20, 12))
im = ax.imshow(data, origin='lower', norm=norm, cmap='gray')
clb = plt.colorbar(im, label='MJy/sr')
ax.set_xlabel('Pixels')
ax.set_ylabel('Pixels')
plt.show()
../../../_images/56f1b85051ea1d00693f320e05330e1d1acfb15835a42ec8f1b2def0704ef46b.png
# Zoom in to see the source. In this case, our source is from MIRI Program ID #1028, a Calibration Program.
# We are using Visit 006, which targets the A5V dwarf 2MASSJ17430448+6655015
# Reference Link: http://simbad.cds.unistra.fr/simbad/sim-basic?Ident=2MASSJ17430448%2B6655015&submit=SIMBAD+search
source_location = SkyCoord('17:43:04.4879', '+66:55:01.837', unit=(u.hourangle, u.deg))
with ImageModel(filename) as model:
    x, y = model.meta.wcs.world_to_pixel(source_location)

cutout = extract_array(data, (21, 21), (y, x))

fig, ax = plt.subplots(figsize=(8, 8))
norm2 = simple_norm(cutout, 'log', percent=99)
im = ax.imshow(cutout, origin='lower', norm=norm2, cmap='gray')
clb = plt.colorbar(im, label='MJy/sr', shrink=0.8)
ax.set_title('PID1028, Obs006')

ax.set_xlabel('Pixels')
ax.set_ylabel('Pixels')
plt.show()
../../../_images/f67b936859aa9dfcba7d877bcb984410c0f7f41c1d36ddc1071efb5e14927fc0.png

3.2 Generate empirical PSF grid for MIRI F770W using STPSF #

Let’s now use STPSF to generate an empirical grid of ePSF models for MIRI F770W. The output will be a Photutils GriddedPSFModel containing a 2x2 grid of detector-position-dependent empirical PSFs, each oversampled by a factor of 4. Note that we save the PSF grid to a FITS file (via save=True) called miri_mirim_f770w_fovp101_samp4_npsf4.fits. To save time in future runs, we load this FITS file directly into a GriddedPSFModel object:

psfgrid_filename = 'miri_mirim_f770w_fovp101_samp4_npsf4.fits'

if not os.path.exists(psfgrid_filename):
    miri = stpsf.MIRI()
    miri.filter = 'F770W'
    miri.detector = 'MIRIM'
    psf_model = miri.psf_grid(num_psfs=4, all_detectors=False, verbose=True, save=True)
else:
    psf_model = GriddedPSFModel.read(psfgrid_filename)

psf_model
Running instrument: MIRI, filter: F770W
  Running detector: MIRIM
    Position 1/4: (0, 0) pixels
    Position 1/4 centroid: (np.float64(201.4023624749013), np.float64(201.47046390597333))
    Position 2/4: (0, 1023) pixels
    Position 2/4 centroid: (np.float64(201.30690755776234), np.float64(201.3328394993336))
    Position 3/4: (1023, 0) pixels
    Position 3/4 centroid: (np.float64(201.46001999611337), np.float64(201.57406698621335))
    Position 4/4: (1023, 1023) pixels
    Position 4/4 centroid: (np.float64(201.71435554877664), np.float64(201.48197003471248))
  Saving file: miri_mirim_f770w_fovp101_samp4_npsf4.fits
<GriddedPSFModel(flux=1., x_0=0., y_0=0.)>
# display the PSF grid
psf_model.plot_grid()
../../../_images/dfb069bcd239792acc29ae1a438fe7a7d38b623379164ac02e8351a5f1c44a03.png ../../../_images/dfb069bcd239792acc29ae1a438fe7a7d38b623379164ac02e8351a5f1c44a03.png
# display the PSF grid deltas from the mean ePSF
psf_model.plot_grid(deltas=True)
../../../_images/7e41036467798516be1d984f31715b93831bfd2c840bd01139482f3d45835961.png ../../../_images/7e41036467798516be1d984f31715b93831bfd2c840bd01139482f3d45835961.png

3.3 PSF Photometry #

Now let’s use our gridded PSF model to perform PSF photometry.

# load data and convert units from MJy/sr to uJy
with ImageModel(filename) as model:
    unit = u.Unit(model.meta.bunit_data)
    data = model.data << unit
    error = model.err << unit

    # use pixel area map because of geometric distortion in level-2 data 
    pixel_area = model.area * model.meta.photometry.pixelarea_steradians * u.sr
    data *= pixel_area
    error *= pixel_area
    
    data = data.to(u.uJy)
    error = error.to(u.uJy)

data.unit, error.unit
(Unit("uJy"), Unit("uJy"))

To perform photometry on a single source we can input a Table containing its (x, y) position.

init_params = Table()
init_params['x'] = [x]
init_params['y'] = [y]
init_params
Table length=1
xy
float64float64
691.0126389413533512.5838903334229
# we turn off the finder because we input the source position
fit_shape = 5
localbkg_estimator = LocalBackground(5, 10, bkg_estimator=MMMBackground())
psfphot = PSFPhotometry(psf_model, fit_shape, finder=None, aperture_radius=fit_shape, 
                        localbkg_estimator=localbkg_estimator, progress_bar=True)
phot = psfphot(data, error=error, init_params=init_params)
phot
QTable length=1
idgroup_idgroup_sizelocal_bkgx_inity_initflux_initx_fity_fitflux_fitx_erry_errflux_errnpixfitqfitcfitflags
uJyuJyuJyuJy
int64int64int64float64float64float64float64float64float64float64float64float64float64int64float64float64int64
1111.4397251162293783691.0126389413533512.5838903334229475.7955370966203691.1798603021881512.9107570382915465.116852324490250.00101164897130539850.0010247664683714710.5138472957774761250.7057654379338035-0.0170561282657910320
# convert fit flux from uJy to ABmag
flux = phot['flux_fit']
flux_err = phot['flux_err']
mag = phot['flux_fit'].to(u.ABmag)
magerr = 2.5 * np.log10(1.0 + (flux_err / flux))
magerr = magerr.value * u.ABmag
mag, magerr
(<Magnitude [17.23109481] mag(AB)>, <Magnitude [0.00119883] mag(AB)>)
fig, ax = plt.subplots(ncols=3, figsize=(12, 4))

shape = (21, 21)
cutout1 = extract_array(data.value, shape, (y, x))
norm = simple_norm(cutout1, 'log', percent=98)
im1 = ax[0].imshow(cutout1, origin='lower', norm=norm)
ax[0].set_title(r'Data ($\mu$Jy)')
plt.colorbar(im1, shrink=0.7)

model = psfphot.make_model_image(data.shape, psf_shape=shape)
cutout2 = extract_array(model, shape, (y, x))
im2 = ax[1].imshow(cutout2, origin='lower', norm=norm)
ax[1].set_title('Fit PSF Model')
plt.colorbar(im2, shrink=0.7)

resid = psfphot.make_residual_image(data, psf_shape=shape)
cutout3 = extract_array(resid, shape, (y, x))
norm3 = simple_norm(cutout3.value, 'sqrt', percent=99)
im3 = ax[2].imshow(cutout3.value, origin='lower', norm=norm3)
ax[2].set_title('Residual')
plt.colorbar(im3, shrink=0.7)
plt.show()
../../../_images/e806b737002fe41882b3fcfd18367b2a5a61834da0d499e6cbb518ade341e8ea.png

4. Faint/Upper Limit, Single Object #

The purpose of this section is to illustrate how to calculate an upper limit at a fixed (x, y) position using forced PSF photometry a blank part of the sky.

We’ll use the same data as Section 3.

# load data and convert units from MJy/sr to uJy
with ImageModel(filename) as model:
    unit = u.Unit(model.meta.bunit_data)
    data = model.data << unit
    error = model.err << unit
    
    pixel_area = pixel_area = model.meta.photometry.pixelarea_steradians * u.sr
    data *= pixel_area
    error *= pixel_area
    
    data = data.to(u.uJy)
    error = error.to(u.uJy)

source_location = SkyCoord('17:43:00.0332', '+66:54:42.677', unit=(u.hourangle, u.deg))
with ImageModel(filename) as model:
    x, y = model.meta.wcs.world_to_pixel(source_location)

cutout = extract_array(data.value, (21, 21), (y, x))

fig, ax = plt.subplots()
norm = simple_norm(cutout, 'sqrt', percent=95)
im = ax.imshow(cutout, origin='lower', norm=norm, cmap='gray')
clb = plt.colorbar(im, label=r'$\mu$Jy')
ax.set_title('PID1028, Obs006')

ax.set_xlabel('Pixels')
ax.set_ylabel('Pixels')
plt.show()
../../../_images/ec98cf79a1dd29444c256720d7733385ae386343e35c05d98a4ad49bf4a2de11.png
# to perform forced photometry, we set the (x, y) source position
# AND we fix the PSF model position so that it does not vary in the fit
# (only flux will be fit)
init_params = Table()
init_params['x'] = [x]
init_params['y'] = [y]

# This requires photutils 1.11.0
psf_model_forced = psf_model.copy()
psf_model_forced.x_0.fixed = True
psf_model_forced.y_0.fixed = True
psf_model_forced.fixed
{'flux': False, 'x_0': True, 'y_0': True}
fit_shape = 5
localbkg_estimator = LocalBackground(5, 10, bkg_estimator=MMMBackground())
psfphot = PSFPhotometry(psf_model_forced, fit_shape, finder=None, aperture_radius=fit_shape, 
                        localbkg_estimator=localbkg_estimator, progress_bar=True)

phot = psfphot(data, error=error, init_params=init_params)
phot
QTable length=1
idgroup_idgroup_sizelocal_bkgx_inity_initflux_initx_fity_fitflux_fitx_erry_errflux_errnpixfitqfitcfitflags
uJyuJyuJyuJy
int64int64int64float64float64float64float64float64float64float64float64float64float64int64float64float64int64
1111.2926821657735057559.0832487866767772.861580973197899.69285815735192559.0832487866767772.8615809731978-0.38089014331448917nannan0.0634904164668550925-93.326726058071730.40995783182664194
# To calculate upper limit, multiply the flux_err by your desired sigma
sigma = 3.0
limit = sigma * phot['flux_err']
limit.to(u.ABmag)
\[[25.700426] \; \mathrm{mag}$$\mathrm{\left( \mathrm{AB} \right)}\]

Note: you can go significantly deeper with the Level 3 combined data product#

5. Stellar Field (LMC) #

In this case, we are going to do the same steps as in Section 3, but for multiple stars. The purpose is to illustrate the workflow and runtime for using Photutils on a large number of stars.

5.1 Multiple Stars, Single Level 2 File #

# Find stars in Level 3 File
path = './mast/01171/'
level3 = os.path.join(path, 'jw01171-o004_t001_miri_f560w_i2d.fits')
level3
'./mast/01171/jw01171-o004_t001_miri_f560w_i2d.fits'
# Get rough estimate of background (there are better ways to do background subtraction)
bkgrms = MADStdBackgroundRMS()
mmm_bkg = MMMBackground()

with ImageModel(level3) as model:
    wcs_l3 = model.meta.wcs
    std = bkgrms(model.data)
    bkg = mmm_bkg(model.data)
    data_bkgsub = model.data.copy()
    data_bkgsub -= bkg 

# Find stars
# F560W FWHM = 1.882 pix
fwhm_psf = 1.882
threshold = 5.0
daofind = DAOStarFinder(threshold=threshold * std, fwhm=fwhm_psf, exclude_border=True, min_separation=10)
found_stars = daofind(data_bkgsub)
found_stars.pprint_all(max_lines=10)
 id     xcentroid          ycentroid          sharpness       roundness1      roundness2      npix     peak       flux    mag      daofind_mag     
--- ------------------ ------------------ ------------------ ----------- -------------------- ---- ----------- ---------- --- ---------------------
  1  546.2615248590856  5.400907067001771 0.7041937380599614  0.46773428  0.04273256330788263   25  -0.8653275 -50.092262 nan   -0.5413295965257243
  2  789.0983970730795  6.312157835017224 0.6691237944354219 -0.17942303  -0.8079555096758687   25 -0.57612133  -34.95916 nan   -0.1035249124958943
  3  845.9799634503643  3.675797141070524 0.6829976607805458 0.043498766 -0.42254093349077243   25  -1.1719772 -46.178497 nan    -0.179222451244508
...                ...                ...                ...         ...                  ...  ...         ...        ... ...                   ...
788 1005.1187213604594 1062.3368573824348 0.6862059219157299 -0.44935676  -0.8216906677208756   25 -0.69887817 -38.484135 nan -0.037972898002874234
789  399.3766175963356 1064.9409739772366 0.3060837128458461  -0.1559235 -0.21203561885367186   25 -0.74871683 -34.341244 nan -0.024118448038772944
790  566.9700693080139  1065.048267845045 0.5650444893283237 -0.37038124  -0.5505482861764636   25  0.51578784 -21.200844 nan   -0.5359357398718912
Length = 790 rows
# plot the found stars
norm = simple_norm(data_bkgsub, 'sqrt', percent=99)
fig, ax = plt.subplots(figsize=(10, 10))
ax.imshow(data_bkgsub, origin='lower', norm=norm)

xypos = zip(found_stars['xcentroid'], found_stars['ycentroid'])
aper = CircularAperture(xypos, r=10)
aper.plot(ax, color='red')
plt.show()
../../../_images/c59a6b92721970b1a9fdbf1922eccd86a86d99204c54fbc6a55d8e3499215b37.png
fig, ax = plt.subplots(nrows=2, figsize=(10, 8))

ax[0].scatter(found_stars['mag'], found_stars['sharpness'], s=10, color='k')
ax[0].set_xlabel('mag')
ax[0].set_ylabel('sharpness')

ax[1].scatter(found_stars['mag'], found_stars['roundness2'], s=10, color='k')
ax[1].set_xlabel('mag')
ax[1].set_ylabel('roundness')

mag0 = -3.0
mag1 = -5.0
for ax_ in ax:
    ax_.axvline(mag0, color='red', linestyle='dashed')
    ax_.axvline(mag1, color='red', linestyle='dashed')

sh0 = 0.40
sh1 = 0.82
ax[0].axhline(sh0, color='red', linestyle='dashed')
ax[0].axhline(sh1, color='red', linestyle='dashed')

rnd0 = -0.40
rnd1 = 0.40
ax[1].axhline(rnd0, color='red', linestyle='dashed')
ax[1].axhline(rnd1, color='red', linestyle='dashed')
plt.show()
../../../_images/6bcb66e8bd7af3de0a7d51294516291a741c8e22b50a1f2e462cdee3832d4f44.png
mask = ((found_stars['mag'] < mag0) & (found_stars['mag'] > mag1) & (found_stars['roundness2'] > rnd0)
        & (found_stars['roundness2'] < rnd1) & (found_stars['sharpness'] > sh0) 
        & (found_stars['sharpness'] < sh1) & (found_stars['xcentroid'] > 100) & (found_stars['xcentroid'] < 700)
        & (found_stars['ycentroid'] > 100) & (found_stars['ycentroid'] < 700))

found_stars_sel = found_stars[mask]

print('Number of stars found originally:', len(found_stars))
print('Number of stars in final selection:', len(found_stars_sel))
Number of stars found originally: 790
Number of stars in final selection: 63
# plot the selected stars
norm = simple_norm(data_bkgsub, 'sqrt', percent=99)
fig, ax = plt.subplots(figsize=(10, 10))
ax.imshow(data_bkgsub, origin='lower', norm=norm)

xypos = zip(found_stars_sel['xcentroid'], found_stars_sel['ycentroid'])
aper = CircularAperture(xypos, r=10)
aper.plot(ax, color='red')
plt.show()
../../../_images/38921486e4c60d5cbc54007253dbc8d6834ebfabaa488c100700dcf56f9f0464.png

5.2 Generate empirical PSF grid for MIRI F560W using STPSF #

psfgrid_filename = 'miri_mirim_f560w_fovp101_samp4_npsf4.fits'

if not os.path.exists(psfgrid_filename):
    miri = stpsf.MIRI()
    miri.filter = 'F560W'
    miri.detector = 'MIRIM'
    psf_model = miri.psf_grid(num_psfs=4, all_detectors=False, verbose=True, save=True)
else:
    psf_model = GriddedPSFModel.read(psfgrid_filename)

psf_model
Running instrument: MIRI, filter: F560W
  Running detector: MIRIM
    Position 1/4: (0, 0) pixels
    Position 1/4 centroid: (np.float64(201.39504365744267), np.float64(201.46837962104274))
    Position 2/4: (0, 1023) pixels
    Position 2/4 centroid: (np.float64(201.30209346068543), np.float64(201.31984343070144))
    Position 3/4: (1023, 0) pixels
    Position 3/4 centroid: (np.float64(201.42830769164874), np.float64(201.59926840411683))
    Position 4/4: (1023, 1023) pixels
    Position 4/4 centroid: (np.float64(201.75498594600967), np.float64(201.4970454286808))
  Saving file: miri_mirim_f560w_fovp101_samp4_npsf4.fits
<GriddedPSFModel(flux=1., x_0=0., y_0=0.)>
psf_model.plot_grid()
../../../_images/bc74157d6af7253113b9830b414ebbc3fc74f89be3492710e716663969403cb1.png ../../../_images/bc74157d6af7253113b9830b414ebbc3fc74f89be3492710e716663969403cb1.png
# get the level 2 image
# here, we'll use the PID 1171 files
path = "./mast/01171/"
level2 = sorted(glob.glob(os.path.join(path, 'jw01171004*cal.fits')))
filename = level2[0]
print(filename)

# load data and convert units from MJy/sr to uJy
with ImageModel(filename) as model:
    unit = u.Unit(model.meta.bunit_data)
    model.data[model.dq >= 10] = np.nan
    data = model.data << unit
    error = model.err << unit
    
    pixel_area = pixel_area = model.meta.photometry.pixelarea_steradians * u.sr
    data *= pixel_area
    error *= pixel_area
    
    data = data.to(u.uJy)
    error = error.to(u.uJy)

    wcs = model.meta.wcs

data.unit, error.unit
./mast/01171/jw01171004001_02101_00001_mirimage_cal.fits
(Unit("uJy"), Unit("uJy"))

5.3 PSF Photometry #

# translate (x, y) positions from the level 3 image to the level 2 image
xc = found_stars_sel['xcentroid']
yc = found_stars_sel['ycentroid']
sc = wcs_l3.pixel_to_world(xc, yc)

x, y = wcs.world_to_pixel(sc)
init_params = Table()
init_params['x'] = x
init_params['y'] = y

# we need to remove stars in the masked region of
# the level-2 data
mask = x > 404
init_params = init_params[mask]
mask
array([ True, False,  True,  True,  True,  True,  True,  True,  True,
       False,  True,  True,  True,  True,  True,  True,  True, False,
       False,  True,  True, False,  True,  True,  True, False, False,
       False,  True, False,  True, False, False,  True,  True,  True,
       False,  True,  True, False,  True,  True,  True,  True,  True,
       False,  True,  True, False,  True,  True,  True,  True, False,
        True, False,  True,  True,  True, False,  True,  True, False])
# plot the selected stars
norm = simple_norm(data.value, 'sqrt', percent=99)
fig, ax = plt.subplots(figsize=(10, 10))
ax.imshow(data.value, origin='lower', norm=norm)

xypos = zip(init_params['x'], init_params['y'])
aper = CircularAperture(xypos, r=10)
aper.plot(ax, color='red')
plt.show()
../../../_images/9b28ffe12ba3fc511b7d9fa5701510d92c7a6d5b8cae8ef03c780c892a81d250.png
fit_shape = 5
localbkg_estimator = LocalBackground(5, 10, bkg_estimator=MMMBackground())
psfphot = PSFPhotometry(psf_model, fit_shape, finder=None, aperture_radius=fit_shape, 
                        localbkg_estimator=localbkg_estimator, progress_bar=True)
phot = psfphot(data, error=error, init_params=init_params)
phot
QTable length=44
idgroup_idgroup_sizelocal_bkgx_inity_initflux_initx_fity_fitflux_fitx_erry_errflux_errnpixfitqfitcfitflags
uJyuJyuJyuJy
int64int64int64float64float64float64float64float64float64float64float64float64float64int64float64float64int64
1110.5917736126746966583.695341436043357.2468701021954364.37585446860986583.627142605562257.2552777003193922.0863819605096620.0098815502341313840.0099970620158883850.22361477408075406251.592554525858295-0.0080290932031348590
2210.6100208181015989630.187092084485677.5032493593316866.95979612654335630.132255059323577.3547092151910826.079898099100720.0092063231202812270.0098031112434340090.26646535979914693251.80084729791472590.050081104179486850
3310.5691058405479137545.15556726562778.0177541507890267.61121880086363545.108578309271578.0094349551779430.202903859416520.0077708145318987950.008798925212455360.27799187555778915251.38696618011187960.089393262549077730
4410.593688857401901514.464730227235779.1773166195533863.13382471169785514.361841954661379.2300847685002524.2948782769461080.0092386701713419710.0094316164342726030.23037622229648375241.42718143660313260.002544004819200131
5510.6032650537579973526.081043124956280.8659614510502367.36284381417863525.971211382839680.8962482210831426.6170282816455630.008579420954881350.0091399971222032220.2580218388975531251.47705149884605860.060736247626977280
6610.5452195879137307453.146773623292684.1205724415652961.86177209688229453.079826268620484.0261879496028124.4530465754203870.0082297811017829040.0088080010672269130.2213714474830289252.29219257941446750.174023571613774320
7710.6332202497470021636.506798065965493.2769254717192166.69100788310536636.458386121412293.080529182659723.580115007438610.0097318808765939280.0095596583001602040.2490521394977686252.04198264143679430.17290300831545310
8810.6027188777403019656.149458255486499.9766827036358488.83133597743618656.252364357897899.9586584565887554.486555316687560.005244162402045330.0055995663812747720.32797720995728463250.74255765843190040.079867124301421770
9910.547520673274994481.54703925766387118.9239209027718456.459257597781644481.48800673642165118.9276436443860119.17824918881760.0105218432389612010.0105795454255588720.20213058094361303252.4125262460750530.086554178565214750
...................................................
363610.5620971093618923412.2166106708058492.047101962824368.8067629570188412.2467288371631492.029597107380632.608786911330450.0069848112477665810.0070541540098616440.2329917311819081251.20447956011119330.071562299570546450
373710.5360806073094238603.7650329383015516.331035521901857.06107361620856603.8016419674484516.455897259548821.676026707551860.008874410825653380.0092413229833216270.19719255380655115251.87264102787228340.082250425663954410
383810.5449298500494144438.43239918428213517.685856132844863.63512050110673438.36556392354873517.810299494736628.615681302854680.0077819947128868950.0078725590120994910.23308950425313937251.62923143387816260.06840339722557970
393910.5434614263082806557.926455966104531.950248611796461.31849260874046558.0156371212586531.957579045869620.787861467922040.009119023017883790.0095142143049680420.20148472945846096251.42697039158700380.030154364930656660
404010.5319761503161045626.789868315454541.406086684874755.3254156791539626.815640367721541.360854099085515.8395983493667210.011388193318084320.0123614042893083340.18182948752514208252.4757249193214790.15412629844420950
414110.5237871754813839628.1186930495217550.984864814687453.318885866652614628.111638030972551.007739539424516.064387451179860.0114503873598606670.0116723885120443710.18770635400852514252.20064633487740120.0268989283425036250
424210.561615267280857493.2502114845369581.882794112347759.53039625260884493.2556683895723581.936857357872421.7361538789219360.0090749982925519630.0092878895348714050.1985728294800299252.2458853662752520.139609448512822670
434310.5316955510606156597.327038102174587.682966273043961.735091743228274597.4619912123752587.744812070565825.3435376258597780.008368048126274310.0085212050452416860.21284260819942533252.016375429048198-0.0058450872299451110
444410.5456375295271254433.97407354382995606.00108887163858.52389361531568433.9640921192637606.00778030585619.2337960643760760.0098718269188991460.010199207489140340.191789929450615251.6630678103751408-0.0162459949107794340
# convert fit flux from uJy to ABmag
flux = phot['flux_fit']
flux_err = phot['flux_err']
mag = phot['flux_fit'].to(u.ABmag)
magerr = 2.5 * np.log10(1.0 + (flux_err / flux))
magerr = magerr.value * u.ABmag
mag, magerr
(<Magnitude [20.53968855, 20.35923527, 20.19987825, 20.43621318,
             20.33710108, 20.42916756, 20.4686352 , 19.55927662,
             20.69297761, 20.65126457, 19.32377519, 20.04306398,
             20.40416966, 19.70731474, 20.11691711, 20.29396076,
             20.44007513, 20.47336615, 20.48695953, 20.12105398,
             20.2501405 , 21.16800096, 20.79285496, 20.02802598,
             20.33465086, 20.34476846, 19.47693267, 20.36948414,
             20.43489364, 20.47643891, 20.41885784, 20.39525343,
             20.31342508, 20.14212217, 20.36406716, 20.11666339,
             20.56005081, 20.25848977, 20.60547547, 20.90063959,
             20.88533957, 20.55704325, 20.39033191, 20.68983748] mag(AB)>,
 <Magnitude [0.01093732, 0.01103697, 0.00994756, 0.01024699, 0.01047429,
             0.00978486, 0.01140736, 0.00651591, 0.01138331, 0.01133476,
             0.0065102 , 0.00798444, 0.00939263, 0.00744215, 0.00802427,
             0.00869982, 0.01027089, 0.00948007, 0.00967182, 0.00842352,
             0.00898325, 0.01438394, 0.01198467, 0.00790335, 0.00930763,
             0.00886982, 0.00599879, 0.0093634 , 0.00927861, 0.00958065,
             0.00929012, 0.00903172, 0.00896606, 0.00792852, 0.00901809,
             0.00773007, 0.00983257, 0.00880806, 0.01047274, 0.01239263,
             0.01261288, 0.00987382, 0.00908026, 0.0107728 ] mag(AB)>)
# Write to File
df = DataFrame({"RA": sc.ra.deg[mask], "DEC": sc.dec.deg[mask], "Mag": mag.value, "Mag Err": magerr.value})
df.to_csv('miri_photometry_photutils.txt', index=False)  

Space Telescope Logo