STIPS Tutorials

The Space Telescope Imaging Product Simulator (STIPS) is a versatile tool designed to simulate Level 2 (L2) images from the Wide Field Instrument (WFI) for Roman. This article presents a series of tutorials demonstrating the range of functionalities, allowing users to develop simulations tailored to their scientific goals



Tutorial #1: Basic STIPS Usage

The following tutorial describes a straightforward use of the STIPS software that may be used as the basis for other use cases. The Basic STIPS Usage Tutorial builds on the concepts introduced in the  STIPS Overview article and is designed to walk-through the phases of using STIPS at the most introductory level. A STIPS observation will be designed, a small scene developed, and then a simulated image generated.

Initial Observation Setup

First we make an obs dictionary with the properties of the observation and then we generate an observation module that contains the details of the observation. 

Observation Dictionary

An example  Input Parameter obs dictionary is specified in the code block below. In this example, the imaging filter F129 is used in combination with the WFI01 Sensor Chip Assembly (SCA) and an exposure time of 300 seconds.

Observation Dictionary
from stips.observation_module import ObservationModule

# Build observation parameters
obs = {'instrument'       : 'WFI',
       'filters'          : ['F129'],
       'detectors'        : 1,
       'background'       : 'pandeia',
       'observations_id'  : 42,
       'exptime'          : 300,
       'offsets'          : [{'offset_id'    : 1    ,
                              'offset_centre': False,
                              'offset_ra'    : 0.0  ,
                              'offset_dec'   : 0.0  ,
                              'offset_pa'    : 0.0  }]}

Observation Object

An observation object combines the Input Parameter obs dictionary with the central coordinates (RA, Dec), orientation angle, and computational parameters for the simulation. Then, the setup can be initialized.

Initialization of the Observation Module
# Create observation object
obm = ObservationModule(obs,
                        ra    = 90,
                        dec   = 30,
                        pa    = 0,
                        seed  = 42,
                        cores = 6)

Generating a Simple Astronomical Scene

Next, the Input Parameter catalog contains the coordinates of the sources, the observed flux, and any necessary shape parameters. The code block below provides an example of the required input information to generate a Input Parameter catalog containing two point sources. The catalog is saved to a file called File Name catalog.fits for later use.

Example STIPS Input Catalog
from astropy.io import fits

cols=[]
cols.append(fits.Column(name='id'   ,array=[1,2]            , format='K' )) # Object ID
cols.append(fits.Column(name='ra'   ,array=[90.02,90.03]    , format='E' )) # RA in degrees
cols.append(fits.Column(name='dec'  ,array=[29.98,29.97]    , format='D' )) # DEC in degrees
cols.append(fits.Column(name='flux' ,array=[0.00023,0.0004] , format='D' )) # Flux in `units`
cols.append(fits.Column(name='type' ,array=['point','point'], format='8A')) # `point` or `sersic`
cols.append(fits.Column(name='n'    ,array=[0,0]            , format='D' )) # Sersic profile index
cols.append(fits.Column(name='re'   ,array=[0,0]            , format='D' )) # Half-light radius in pixels
cols.append(fits.Column(name='phi'  ,array=[0,0]            , format='D' )) # Angle of PA in degrees
cols.append(fits.Column(name='ratio',array=[0,0]            , format='D' )) # Axial Ratio
cols.append(fits.Column(name='notes',array=['','']          , format='8A')) # Notes
cols.append(fits.Column(name='units',array=['j','j']        , format='8A')) # Units, 'j' for jansky

# Create output fits table
hdut = fits.BinTableHDU.from_columns(cols)
hdut.header['TYPE']='mixed'
hdut.header['FILTER']='F129'

# Write to disk
hdut.writeto('catalog.fits',overwrite=True)

Generating a Simulated Image

The code block below combines the of inputs required for STIPS that were generated above and finalizes the  ObservationModule to run a simulation. The final simulated image will be saved under the name of the  Input Parameter fits_file variable and the result is shown at the right.

Full Inputs for STIPS
# Initialize the local instrument
obm.nextObservation()

# Add catalog with sources
cat_name = obm.addCatalogue('catalog.fits')

# Add error to image
psf_file = obm.addError()

# Call the final method
fits_file, mosaic_file, params = obm.finalize(mosaic=False)
print("Output FITS file is {}".format(fits_file))

Result of STIPS Basic Use Tutorial

This output FITS image was simulated with STIPS using this tutorial. 


Tutorial #2: Generating a Scene from a User-Generated Catalog

There are two ways to make more complex astronomical scenes: (1) inputting pre-existing catalogs in the supported formats described in Catalogs formatting on readthedocs or (2) using built in modules to generate catalogs of stars or galaxies using input parameters. The user must set the parameters for the stellar and galactic populations in the scene – STIPS then generates catalogs that will be used to populate the scene.

Stellar Source Catalog

Below is an example of generating a stellar population defined by stellar_parameters

Code Generating a Stellar Population
obs_prefix = 'notebook_example'
obs_ra = 150.0
obs_dec = -2.5

from stips.scene_module import SceneModule

scm = SceneModule(out_prefix=obs_prefix, ra=obs_ra, dec=obs_dec)

stellar_parameters = {
                      'n_stars': 100,
                      'age_low': 7.5e12, 
                      'age_high': 7.5e12,
                      'z_low': -2.0, 
                      'z_high': -2.0,
                      'imf': 'salpeter', 
                      'alpha': -2.35,
                      'binary_fraction': 0.1,
                      'clustered': True,
                      'distribution': 'invpow',
                      'radius': 100.0, 
                      'radius_units': 'pc',
                      'distance_low': 20.0, 
                      'distance_high': 20.0,
                      'offset_ra': 0.0, 
                      'offset_dec': 0.0
                     }

stellar_cat_file = scm.CreatePopulation(stellar_parameters)
print("Stellar population saved to file {}".format(stellar_cat_file))

Galaxy Source Catalog

Below is an example of generating a galaxy population defined by galaxy_parameters


Code Generating a Background Galaxy Population
obs_prefix = 'notebook_example'
obs_ra = 150.0
obs_dec = -2.5

from stips.scene_module import SceneModule

scm = SceneModule(out_prefix=obs_prefix, ra=obs_ra, dec=obs_dec)
 
galaxy_parameters = {
                     'n_gals': 10,
                     'z_low': 0.0, 
                     'z_high': 0.2,
                     'rad_low': 0.01, 
                     'rad_high': 2.0,
                     'sb_v_low': 30.0, 
                     'sb_v_high': 25.0,
                     'distribution': 'uniform', 
                     'clustered': False,
                     'radius': 200.0, 
                     'radius_units': 'arcsec',
                     'offset_ra': 0.0, 
                     'offset_dec': 0.0,
                    }

galaxy_cat_file = scm.CreateGalaxies(galaxy_parameters)
print("Galaxy population saved to file {}".format(galaxy_cat_file))

Generating the Scene 

The stellar source catalog can now be observed. in the F129 filter on the SCA WFI03 using an exptime of 1500 s and a uniform background.

Code to Generate the Astronomical Scene in Example #2
from stips.observation_module import ObservationModule

offset_1 = {
          'offset_id': 1,
          'offset_centre': False,
          'offset_ra': 2.0,
          'offset_dec': 0.0,
          'offset_pa': 0.5
          }

residuals_1 = {
             'residual_flat': True,
             'residual_dark': True,
             'residual_cosmic': False,
             'residual_poisson': False,
             'residual_readnoise': False
            }

observation_parameters_1 = {
                          'instrument': 'WFI',
                          'filters': ['F129'],
                          'detectors': 3,
                          'distortion': True,
                          'background': 0.24,
                          'observations_id': 1,
                          'exptime': 1500,
                          'offsets': [offset]
                          }

obm_1 = ObservationModule(observation_parameters_1, out_prefix=obs_prefix, ra=obs_ra, dec=obs_dec, residual=residuals_1)

obm_1.nextObservation()

Result of the Astronomical Scene Tutorial


Result of Tutorial #2.




Tutorial #3: Modifying Observation Parameters 

We can use the results of Tutorial #2 as the first of several observations that are dithered. The function nextObservation()is required to move between different observations. This function must be called at least once, in order to initialize the observation module to the first of the observations before catalogs are added.  The Input Parameter obs_prefix , Input Parameter obs_ra , and Input Parameter obs_dec values are set during initial setup of  STIPS . Then we can modify the offsets such that the same underlying astronomical scene is observed with different parameters. The code below adds an offset of 10 degrees in the RA and a rotation of 27 degrees, and with a read noise residual instead of a dark current residual. This process could be repeated to generate sets of observations obtained with different telescope orientations to either construct complex mosaics or compare results with different noise residuals.

Example Code with Different Observation Parameters
offset_2 = {
          'offset_id': 1,
          'offset_centre': False,
          'offset_ra': 10.0,
          'offset_dec': 0.0,
          'offset_pa': 27
          }

residuals_2 = {
             'residual_flat': True,
             'residual_dark': False,
             'residual_cosmic': False,
             'residual_poisson': False,
             'residual_readnoise': True
            }

observation_parameters_2 = {
                          'instrument': 'WFI',
                          'filters': ['F129'],
                          'detectors': 3,
                          'distortion': True,
                          'background': 0.24,
                          'observations_id': 1,
                          'exptime': 1500,
                          'offsets': [offset]
                          }

obm_2 = ObservationModule(observation_parameters_2, out_prefix=obs_prefix, ra=obs_ra, dec=obs_dec, residual=residuals_2)

obm_2.nextObservation()



Tutorial #4: PSFs and Adding Sources 

STIPS offers a variety of customizable options through the use of utility functions.  As an example, in this section we describe the use of the makePSF module.


STIPS also allows the user to manually produce PSF models for a given set detector positions and use them to insert point sources into an existing scene. PSFs at specific locations can be generated by providing the input PSF library and detector positions to the makePSF module within STIPS . Generated PSFs are returned as a 2D array.

STIPS performs two types of interpolation to obtain the exact PSF model at the user-specified locations. First, it applies bi-linear interpolation of the 3x3 PSF library array to compute the best PSF at the specified integer SCA pixels; then it performs bicubic interpolations over the supersampled pixel grid of this PSF to generate the best PSF at the specified SCA sub-pixel positions. The PSF models can then be used to create new scenes or to add sources to existing scenes.

The file File Name psf_WFI_2.0.0_F129_sca01.fits contains the library PSF for filter F129 and SCA 1. This file is available in the notebooks directory of the STIPS GitHub repository.

The library PSF can be loaded using the following code:

Code to Access the Library PSF
with fits.open('psf_WFI_2.0.0_F129_sca01.fits') as hdul:
	test_psf = stips.utilities.makePSF.make_epsf(hdul[0].data)

Visualization of the Example PSF

Visualization of one of the library PSF from the FITS file File Name psf_WFI_2.0.0_F129_sca01.fits

Adding an Artificial Point Source

We will now insert this PSF into the astronomical scene defined in Example #2. The collapsed code box below repeats the code required to generate the astronomical scene from Example #2 for completeness. 

STIPS Code from Example#2
from stips.observation_module import ObservationModule

offset = {
          'offset_id': 1,
          'offset_centre': False,
          'offset_ra': 0.0,
          'offset_dec': 0.0,
          'offset_pa': 0.0
         }

observation_parameters = {
                          'instrument': 'WFI',
                          'filters': ['F129'],
                          'detectors': 1,
                          'distortion': False,
                          'background': 0.15,
                          'observations_id': 1,
                          'exptime': 1000,
                          'offsets': [offset]
                         }

obm = ObservationModule(observation_parameters, out_prefix=obs_prefix, ra=obs_ra, dec=obs_dec)

# nextObservation is called to move between different combinations of offset and filter.
# It must be called once in order to initialize the observation module to the first observation before adding catalogs.
obm.nextObservation()

output_stellar_catalogs = obm.addCatalogue(stellar_cat_file)
output_galaxy_catalogs = obm.addCatalogue(galaxy_cat_file)

print("Output Catalogs are {} and {}.".format(output_stellar_catalogs, output_galaxy_catalogs))

psf_file = obm.addError()

print("PSF File is {}".format(psf_file))

fits_file, mosaic_file, params = obm.finalize(mosaic=False)

print("Output FITS file is {}".format(fits_file))
print("Output Mosaic File is {}".format(mosaic_file))
print("Observation Parameters are {}".format(params))
 

The library PSF can now be added to the simulated image, by specifying the source addition parameters, Location (px) and Flux, as demonstrated in the code below. The result of this code on the scene from Example #2 is given at right.

STIPS Code to Add an Artifical Point Source
# Image crated in the example above
with fits.open('notebook_example_1_0.fits') as result_file:
    result_data = result_file[1].data

# Open library PSF generated earlier in this section
with fits.open('psf_WFI_2.0.0_F129_sca01.fits') as hdul:
	test_psf = stips.utilities.makePSF.make_epsf(hdul[0].data)

# Default upscaling factor for STIPS
PSF_UPSCALE = 4

# Calculate center and size
psf_middle = int(np.round((test_psf[0].shape[0]-1) / 2))
boxsize = int(np.floor(psf_middle/PSF_UPSCALE))

added_source = stips.utilities.makePSF.place_source(2000, 2000, 3000, result_data, test_psf, boxsize=boxsize, psf_center=psf_middle)

Note that the default PSF_UPSCALE is 4.

Result of Tutorial #4 to Add an Artificial Source 

 

Comparison of the astronomical scene before and after adding the PSF as described in Tutorial #4. 




For additional questions not answered in this article, please contact the Roman Help Desk at STScI.



Latest Update

 

Updated Tutorials to with STIPS v2.1
Publication

 

Initial publication of the article.