Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

IRSA Tutorials

Using Firefly to Explore OpenUniverse2024 Simulated Roman and Rubin Images

Learning Goals

By the end of this tutorial, you will:

Introduction

The purpose of this tutorial is to become familiar with the simulated Roman and Rubin data published through OpenUniverse2024, and to become familiar with the Firefly JupyterLab Extension for visualizing astronomical data products.

OpenUniverse2024 is a project to simulate spatially overlapping imaging surveys to be carried out by the Nancy Grace Roman Telescope and the Vera C. Rubin Observatory. The simulations were carried out on Argonne’s Theta cluster and consist of:

This tutorial works with the full simulation, which covers the entire survey footprint. More information about the dataset can be found at IRSA’s holding of this dataset, and the OpenUniverse2024 paper describes how the simulation was produced.

Firefly is an open-source web-based UI library for astronomical data archive access and visualization developed at Caltech and used by multiple space- and ground-based astrophysics archives. In addition to being used to make web applications, Firefly can be used from Python via Firefly Python client. The Firefly JupyterLab Extension makes it particularly easy to use Firefly to efficiently visualize cloud-hosted astronomical data using JupyterLab instances running locally or on cloud.

If you are new to OpenUniverse2024, the Quickstart tutorial introduces the directory layout, the parquet catalogs, and the SIA image search that this notebook builds on.

Imports

1. Learn where the OpenUniverse2024 data are hosted in the cloud

The OpenUniverse2024 data are hosted in the cloud via Amazon Web Services (AWS). To access these data, you need to create a client to read data from Amazon’s Simple Storage Service (s3) buckets, and you need to know some information about those buckets. OpenUniverse2024 contains simulations of the Roman Wide-Area Survey (WAS) and the Roman Time Domain Survey (TDS). In this tutorial, we will focus on the WAS.

We also configure IRSA’s Simple Image Access (SIA) service here, which later sections use to locate the Roman and Rubin images covering a coordinate.

2. Roman Images

The Nancy Grace Roman Space Telescope will carry out a wide-area survey (WAS) in the near infrared. OpenUniverse2024 simulates that survey as individual exposures, each covering one detector’s worth of sky. Bands include F184, H158, J129, K213, W146, Y106.

The survey stores those exposures by pointing and detector rather than by sky position, so in this section we define a function that asks the image search which exposure covers a position, and then reads it.

Define a function that retrieves a Roman simulated image given a sky position and filter.

The Roman collection holds both the Wide-Area Survey and the Time Domain Survey, so we keep only the WAS exposures and then only the band we want. Many exposures cover any given position, so we sort by observation time and take the earliest.

Having found the file, we prefix its path with s3:// and open it with astropy. The science image sits in the first extension, and we take the WCS from the same header as an astropy.wcs.WCS object. The function returns a dictionary of both, along with the header itself so we can look at it later, and the path so that later cells can reuse it instead of searching a second time.

Notebook Cell

Inspect a simulated Roman image

Choose a filter and a position within the survey footprint

Retrieve the data and header information from the simulated Roman image corresponding to the chosen position and filter.

Understand the size of a simulated Roman image.

(4088, 4088)
[<Quantity 3.04137746e-05 deg>, <Quantity 3.00150219e-05 deg>]
[<Quantity 447.59343814 arcsec>, <Quantity 441.72507487 arcsec>]

The field of view of a Roman exposure is ~450 arcsec, at a pixel scale of ~0.11 arcsec.

Look at everything else the image file contains

So far we have read the science image, but each exposure carries more than that. Listing the extensions shows the full picture.

Filename: <class 's3fs.core.S3File'>
No.    Name      Ver    Type      Cards   Dimensions   Format
  0  PRIMARY       1 PrimaryHDU      63   ()      
  1  SCI           1 ImageHDU        68   (4088, 4088)   float64   
  2  ERR           1 ImageHDU        68   (4088, 4088)   float32   
  3  DQ            1 ImageHDU        70   (4088, 4088)   int32 (rescales to uint32)   

Each file holds a primary header carrying the observation metadata but no pixels, followed by three 4088x4088 planes:

SCI = the simulated science image, in electrons per second

ERR = the estimated uncertainty on each science pixel

DQ = the data quality mask, flagging pixels that should not be trusted

Use the WCS from the Roman simulated header to convert the specified coordinate into a pixel position.

Notebook Cell
(array(2318), array(3029))

Use matplotlib imshow to create a static visualization of the Roman simulated image and overplot the selected position.

Each telescope points independently, so neither survey’s images arrive with north pointing up: this Roman exposure is turned about 170 degrees from north, and the Rubin visit in the next section about 160 degrees. Every figure in this notebook is therefore resampled onto a north-up grid before plotting, so that all of them, and both telescopes, can be read the same way. The target grid is a plain gnomonic (TAN) one, so the SIP distortion carried by the exposure headers is resampled away along with the rotation. The rotation leaves the frame tilted inside a slightly larger box, with empty wedges in the corners.

Notebook Cell
(np.float64(-0.5), np.float64(1225.5), np.float64(-0.5), np.float64(1225.5))
<Figure size 640x480 with 1 Axes>

3. Rubin Images

OpenUniverse2024 includes simulated Rubin images in the following filters: u, g, r, i, z, y. As with Roman, these are stored by visit and detector rather than by sky position, so we again let the SIA service tell us which images cover our position. In this section, we define functions that retrieve a Rubin image for a chosen position and filter, returning data in the same structure as the function we defined above for Roman.

Retrieve Rubin images

The image search returns one row per image, with the cloud location of each tucked inside a cloud_access JSON string. Many visits cover any given position, so we sort by observation time and take the earliest.

Notebook Cell
Notebook Cell

Inspect a simulated Rubin image

Choose a filter and retrieve the data and header information from the simulated Rubin image covering our position.

Understand the size of a simulated Rubin image.

(4004, 4096)
[<Quantity 5.55815653e-05 deg>, <Quantity 5.5575227e-05 deg>]
[<Quantity 801.17491466 arcsec>, <Quantity 819.49006765 arcsec>]

The field of view of a Rubin detector is ~800 arcsec, at a pixel scale of ~0.2 arcsec. A single Rubin detector therefore covers nearly twice the sky of one Roman exposure, at roughly twice the pixel scale.

Use matplotlib imshow to create a static visualization of the Rubin simulated image and overplot the selected position.

The Rubin figure below covers a different patch of sky than the Roman figure in Section 2. Each is centered on its own exposure and sized to its own field of view, so the Rubin panel is nearly twice as wide on the sky as the Roman one. What they share is the marked position, which falls inside both.

(np.float64(-0.5), np.float64(1228.5), np.float64(-0.5), np.float64(1228.5))
<Figure size 640x480 with 1 Axes>

4. Compare simulated Roman and Rubin cutouts

To compare the two telescopes’ images on equal terms we trim each full frame down to the same patch of sky with Cutout2D, which extracts a rectangular region centered on a sky position and returns an updated WCS along with the pixels, so the trimmed image still knows where it sits on the sky.

Create the cutouts

Rotate both cutouts so that north points up

The full frames above were rotated for display; here we do the same to the cutouts, using the same helper. The two telescopes are turned about 10 degrees apart from each other, so without this the panels would not line up. Each keeps its own pixel scale, so Roman still shows the finer sampling.

Plot static side-by-side comparisons of the cutouts using matplotlib

<Figure size 1200x600 with 2 Axes>

Both panels cover the same patch of sky, at the same size and with north up in each. The difference is stark: the simulated Roman exposure resolves a field full of galaxies, while the same patch in the simulated Rubin image is close to blank. A single Rubin visit is short, so only the brightest sources here rise above its noise, and from the ground its point spread function is broad enough to blend close neighbors together. Roman’s finer pixels and sharper point spread function keep those sources separate. Rubin reaches its full depth by stacking many visits, which is not what we are looking at.

5. Use Firefly to interactively explore the images

Static cutouts only help once you know where to look. Choosing a position worth comparing means exploring the image interactively, and for that we hand it to Firefly.

Launch and initialize Firefly

There are two ways to initialize a Firefly client from Python, depending on whether you’re running the notebook in JupyterLab or not. Assuming you have jupyter-firefly-extensions set up in your environment as explained here, you can use make_lab_client() in JupyterLab, which will open the Firefly viewer in a new tab within the Lab. Otherwise, you can use make_client() in a Jupyter Notebook (or even a Python shell), which will open the Firefly viewer in a new web browser tab.

You also need a Firefly server to communicate with your Firefly Python client. In this notebook, we use a public Firefly server: https://irsacloud.ipac.caltech.edu/firefly. However, you can also run a local Firefly server via a Firefly Docker image and access it at http://localhost:8080/firefly. The URL of the Firefly server is read by both make_client() and make_lab_client() through the environment variable FIREFLY_URL. However, make_client() also allows you to pass the URL directly as the url parameter.

Loading...
{'success': True}

Define a function that returns the URL for a given S3 filepath

Since the OpenUniverse2024 data are in a public S3 bucket, any file can also be reached over HTTPS. Firefly loads images and catalogs by URL rather than by S3 path, so we use this helper throughout the rest of the notebook.

Notebook Cell

Let’s generate URL for the Rubin image we plotted above. Clicking on the returned URL will allow you to download this image locally, but this is not required for the rest of the tutorial.

'https://nasa-irsa-simulations.s3.amazonaws.com/openuniverse2024/rubin/full/u/descdm/step1_000_w_2024_22/20240609T004129Z/calexp/20250706/r/r_57/5025070601048/calexp_LSSTCam_r_r_57_5025070601048_R23_S11_u_descdm_step1_000_w_2024_22_20240609T004129Z.fits'

Send the simulated Rubin image to Firefly using show_fits_image

For displaying the FITS image of the Rubin visit in Firefly, we use show_fits_image:

{'success': True}

Use ds9 region syntax to outline the Roman exposure on the interactive display

The Firefly client includes several methods related to controlling ds9 region overlays. To overlay a region layer on the loaded FITS images, we can use overlay_region_layer.

Region data is defined in ds9 region syntax that can be found here.

The Rubin visit on display covers more sky than the Roman exposure does, so it is worth seeing where the Roman footprint falls. The corners of the Roman exposure come straight from its WCS, and we draw them as a polygon.

array([[ 9.69558016, -43.96362143], [ 9.72505074, -44.08444863], [ 9.55408697, -44.10379702], [ 9.52495478, -43.98292638]])
{'success': True}

Send the simulated Roman image to Firefly and align it with Rubin

Now let’s pull up the Roman exposure itself, using the same show_fits_image method:

{'success': True}

Also align & lock it with the Rubin display by WCS using align_images so that panning or zooming one moves the other in sync.

{'success': True}

You can pan and zoom around the image display using the same controls as Google Maps or similar map apps (scroll/pinch to zoom, drag to pan, or the magnifying glass icons in the upper left). Try zooming out from the outlined Roman region and see how it sits within the wider Rubin field around it.

6. Use Firefly to overlay sources on the images

Let’s inspect the properties of sources within the Roman footprint we outlined above. For this we will use the input truth files present in S3 bucket.

OpenUniverse2024 includes the input truth files that were used to create the simulated images. These files are in Parquet and HDF5 format, and include information about the properties of galaxies, stars, and transients.

The catalogs are split by HEALPix sky region (nside=32, RING ordering) and the region index appears in each filename, so we can convert our sky position into that index with hpgeom rather than hunting through the directory listing. This is the same approach the Quickstart tutorial takes.

Each region comes as two separate catalogs: a point source catalog holding the stars, and a galaxy catalog. We load both below and overlay each on the image.

HEALPix region for our position: 10307
'nasa-irsa-simulations/openuniverse2024/roman/full/roman_rubin_cats_v1.1.2_faint/pointsource_10307.parquet'
'nasa-irsa-simulations/openuniverse2024/roman/full/roman_rubin_cats_v1.1.2_faint/galaxy_10307.parquet'

Use Firefly’s show_table to overlay the catalogs on the interactive image

Each catalog spans a whole HEALPix region, which is much larger than the image we are looking at. So before displaying the table, we filter it down to the sky area the Roman exposure actually covers. (Note: you can remove filters through the table UI if you wish to see the entire data)

['("ra" >= 9.524954784568889 AND "ra" <= 9.725050735278336)', '("dec" >= -44.10379702171619 AND "dec" <= -43.963621429049894)']

You can visualize catalogs interactively with Firefly using show_table. This capability can take many parameters. Here we will simply send our catalog to Firefly so that we can (a) see an interactive table; (b) see this table plotted over the image that we’ve already sent; and (c) use the GUI to quickly create exploratory plots. See if you can use the GUI to quickly determine approximately how many galaxies cover the Roman exposure and what the redshift distribution of these galaxies is.

{'success': True}
{'success': True}

For each row in the table you can notice a marker in the image. Selecting a row or marker changes the corresponding marker or row, respectively. You can click on “Details” tab in the UI to show properties of each source selected in image/table.

Use Firefly’s apply_table_filters to show sources of interest

Let’s say the apparent physical size of a galaxy’s disk component is what interests us. So we filter the galaxy catalog table we sent to Firefly down to the galaxies with a disk half-light radius bigger than 1 arcsec. You can use whatever properties you are interested in to filter the table. Notice how the table display, the image overlay, and the active chart all change as the sample shrinks. You can remove this filter or add new ones through the GUI.

For filtering, we will use apply_table_filters method on the galaxy table we loaded above.

{'success': True}

You can play with the filters directly from the UI as well. Try removing or adding more filters in the tables and see how markers change.

7. Identify a region of interest to compare cutouts

Sections 5 and 6 gave us two complementary ways to explore the data interactively: panning and zooming around the displayed images, and filtering the catalog table by whatever properties catch our interest. In this section we put both to use to land on a single region worth a closer look, copy its coordinates into the notebook, mark it with a region overlay, and then see how Roman and Rubin compare in that region.

Use Firefly’s pan/zoom/align capabilities to locate a region of interest

You can view the coordinates of your mouse pointer at the bottom left of the display window. To copy the coordinates for a specific coordinate:

Copy the coordinates from the coordinate display to the Python notebook

Paste the position you copied from the display here. We picked a large disk-shaped galaxy that the Roman image resolves clearly, but that looks like just a fuzzy blob in the Rubin image. Its disk half-light radius of ~2 arcsec is well above the threshold we filtered on in Section 6, which is what caught our eye while panning around.

<SkyCoord (ICRS): (ra, dec) in deg (9.65075, -44.00341667)>

Let’s also define the size of the region we are interested in, which will be used to create cutouts from the images:

Use ds9 region syntax to overplot the selected position

For this we use the id of the region layer we defined above, and add more region data using add_region_data.

{'success': True}

You should now see a circular region around your target of interest in both the Rubin and Roman images in Firefly.

Plot static side-by-side comparisons of the cutouts using matplotlib

Now that interactive exploration has narrowed things down to a specific region of interest, rather than the arbitrary region we started with in Section 2, let’s compare Roman and Rubin cutouts of it the same way we did in Section 4.

<Figure size 1200x600 with 2 Axes>

8. Use Firefly to visualize your region in 3-color

Firefly can also combine multiple bands into a false-color image. Since a single Rubin visit doesn’t detect faint sources at all, we make this 3-color image from the Roman exposures instead. We use the following bands:

Retrieve closely-overlapping Roman images in the 3 bands

image_roman is the H158 exposure covering coords_of_interest from Section 7. Roman’s WAS filters aren’t tiled identically, so rather than searching each RGB filter around coords_of_interest again, we search around the center of this exposure itself, which keeps the 3 filters closely aligned.

Notebook Cell

Let’s print the S3 paths of the Roman exposures in the 3 bands that cover our region of interest.

{'H158': 'nasa-irsa-simulations/openuniverse2024/roman/full/RomanWAS/images/simple_model/H158/773/Roman_WAS_simple_model_H158_773_4.fits.gz', 'J129': 'nasa-irsa-simulations/openuniverse2024/roman/full/RomanWAS/images/simple_model/J129/15948/Roman_WAS_simple_model_J129_15948_12.fits.gz', 'Y106': 'nasa-irsa-simulations/openuniverse2024/roman/full/RomanWAS/images/simple_model/Y106/9361/Roman_WAS_simple_model_Y106_9361_9.fits.gz'}

Verify that the H158 exposure we retrieved in Section 2 is the same as the one we found here.

True

Use Firefly’s show_fits_3color to create a 3 color image

For this we will use show_fits_3color method:

{'success': True}

Use Firefly’s pan/zoom/align methods to highlight the region of interest

Now, let’s pan & zoom to the region of interest. Also align & lock all images being displayed by WCS. For these operations we use set_pan, set_zoom and align_images.

{'success': True}

Use Firefly’s set_stretch method to change the stretch of the image display via Python

Even zoomed in on our source of interest, the background still looks noisy. You can use the Firefly GUI to change the stretch of the image display. We identify that linear stretch from -1 to 30 sigma highlights the colors of our source better. You can also use the Firefly client’s set_stretch to do this via Python. This is helpful for reproducibility and for scaling up to many images.

{'success': True, 'rv_string': '92,-1.000000,92,30.000000,NaN,2.000000,44,25,600,120,0,NaN,1.000000'}

About This Notebook

Updated: 2026-09-02

Contact: the IRSA Helpdesk with questions or reporting problems.

References
  1. Alarcon, A., Aldoroty, L., Beltz-Mohrmann, G., Bera, A., Blazek, J., Bogart, J., Braeunlich, G., Broughton, A., Cao, K., Chiang, J., Chisari, N. E., Desai, V., Fang, Y., Galbany, L., Hearin, A., Heitmann, K., Hirata, C., Hounsell, R., Jain, B., … Zhang, T. (2025). OpenUniverse2024: a shared, simulated view of the sky for the next generation of cosmological surveys. Monthly Notices of the Royal Astronomical Society, 544(4), 3799–3823. 10.1093/mnras/staf1833