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
dictionary is specified in the code block below. In this example, the imaging filter
obs
F129
is used in combination with the WFI01
Sensor Chip Assembly (SCA) and an exposure time of 300 seconds.
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 dictionary with the central coordinates (RA, Dec), orientation angle, and computational parameters for the simulation. Then, the setup can be initialized. obs
# Create observation object obm = ObservationModule(obs, ra = 90, dec = 30, pa = 0, seed = 42, cores = 6)
Generating a Simple Astronomical Scene
Next, the 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 catalog containing two point sources. The catalog is saved to a file called catalog for later use. catalog.fits
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
variable and the result is shown at the right.
fits_file
# 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))
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
.
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
.
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.
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()
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
,
obs_prefix
, and
obs_ra
values are set during initial setup of
obs_dec
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.
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
contains the library PSF for filter F129 and SCA 1. This file is available in the
psf_WFI_2.0.0_F129_sca01.fits
notebooks directory of the STIPS GitHub repository.
The library PSF can be loaded using the following code:
with fits.open('psf_WFI_2.0.0_F129_sca01.fits') as hdul: test_psf = stips.utilities.makePSF.make_epsf(hdul[0].data)
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.
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.
# 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.