Source code for lenstronomy.SimulationAPI.ObservationConfig.Roman

# %%
"""Provisional Roman instrument and observational settings."""

import lenstronomy.Util.util as util
import os
import astropy.io.fits as pyfits

# magnitude_zero_point: table 3 from https://www.stsci.edu/files/live/sites/www/files/home/roman/documentation/technical-documentation/_documents/Roman-STScI%E2%80%93000825.pdf
# ccd_gain found right under table 1 in https://iopscience.iop.org/article/10.3847/1538-4357/aac08b/pdf
# seeing, read_noise, pixel_scale (labelled as plate_scale on website): https://roman.gsfc.nasa.gov/science/WFI_technical.html
# sky brightness calculated using count rates per pixel at minimum Zodiacal light given in website above
# For microlensing survey mode: exposure time, number of exposures for F146 and F087: table 1 with the mission design of WFIRST Cycle 7 from https://iopscience.iop.org/article/10.3847/1538-4365/aafb69/meta#apjsaafb69t1fnd
# For wide area survey mode: exposure time and number of exposures for relevant filters set as given in https://roman-docs.stsci.edu/roman-community-defined-surveys/high-latitude-wide-area-survey
# For time domain survey mode: exposure time and number of exposures for relevant filters set as given in https://roman-docs.stsci.edu/roman-community-defined-surveys/high-latitude-time-domain-survey

__all__ = ["Roman"]

# magnitude zero points updated on 3/10/2026
# seeing, or PSF FWHM in arcseconds, updated on 3/10/2026

F062_band_obs = {
    "sky_brightness": 23.19,
    "magnitude_zero_point": 26.6179,
    "seeing": 0.058,
}

F087_band_obs = {
    "sky_brightness": 22.93,
    "magnitude_zero_point": 26.3023,
    "seeing": 0.073,
}

F106_band_obs = {
    "sky_brightness": 22.99,
    "magnitude_zero_point": 26.3546,
    "seeing": 0.087,
}

F129_band_obs = {
    "sky_brightness": 22.99,
    "magnitude_zero_point": 26.3531,
    "seeing": 0.106,
}

F158_band_obs = {
    "sky_brightness": 23.10,
    "magnitude_zero_point": 26.3760,
    "seeing": 0.128,
}

F184_band_obs = {
    "sky_brightness": 23.22,
    "magnitude_zero_point": 25.9124,
    "seeing": 0.146,
}

F146_band_obs = {
    "sky_brightness": 22.03,
    "magnitude_zero_point": 27.5842,
    "seeing": 0.105,
}

# - keyword sky_brightness: sky brightness (in magnitude per square arcsecond)
# - keyword magnitude_zero_point: magnitude in which 1 count (e-) per second per arcsecond square is registered
# - keyword seeing: Full-Width-at-Half-Maximum (FWHM) of PSF
# - keyword psf_type: string, type of PSF ('GAUSSIAN' supported)


[docs] class Roman(object): """Class contains Roman instrument and observation configurations."""
[docs] def __init__(self, band="F106", psf_type="GAUSSIAN", survey_mode="wide_area"): """ :param band: string, 'F062', 'F087', 'F106', 'F129', 'F158', 'F184', or 'F146' supported. Determines obs dictionary. :param psf_type: string, type of PSF ('GAUSSIAN', 'PIXEL' supported). :param survey_mode: string, survey mode ('wide_area', 'time_domain_wide', 'time_domain_deep', or 'microlensing' supported). Determines exposure time and number of exposures. """ if band == "F062": self.obs = F062_band_obs elif band == "F087": self.obs = F087_band_obs elif band == "F106": self.obs = F106_band_obs elif band == "F129": self.obs = F129_band_obs elif band == "F158": self.obs = F158_band_obs elif band == "F184": self.obs = F184_band_obs elif band == "F146": self.obs = F146_band_obs else: raise ValueError( "band %s not supported! Choose 'F062', 'F087', 'F106', 'F129', 'F158' , 'F184' or 'F146'" % band ) # NOTE: Updated on 1/13/2026 if survey_mode == "wide_area": # medium tier if band in ["F106", "F129", "F158"]: # 107s per dither, 3 dithers, 2 passes for a total of 6 exposures self.obs.update({"exposure_time": 107, "num_exposures": 6}) else: raise ValueError( "band %s is not supported with the wide_area medium tier survey mode! Choose 'F106', 'F129', or 'F158'" % band ) # NOTE: Updated on 1/13/2026 elif survey_mode == "time_domain_wide": bands = ["F062", "F087", "F106", "F129", "F158"] exposure_times = [60, 85, 95, 152, 294] if band in bands: index = bands.index(band) exposure_time = exposure_times[index] else: raise ValueError( "band %s is not supported with the time_domain_wide survey mode! Choose 'F062', 'F087', 'F106', 'F129', or 'F158'" % band ) self.obs.update({"exposure_time": exposure_time, "num_exposures": 1}) # NOTE: Updated on 1/13/2026 elif survey_mode == "time_domain_deep": bands = ["F087", "F106", "F129", "F158", "F184"] exposure_times = [193, 294, 307, 420, 409] num_exposures_list = [1, 1, 1, 1, 4] if band in bands: index = bands.index(band) exposure_time = exposure_times[index] num_exposures = num_exposures_list[index] else: raise ValueError( "band %s is not supported with the time_domain_deep survey mode! Choose 'F087', 'F106', 'F129', 'F158', or 'F184'" % band ) self.obs.update( {"exposure_time": exposure_time, "num_exposures": num_exposures} ) elif survey_mode == "microlensing": if band == "F146": # These are the exposure times and number of exposures for the primary filter, F146 self.obs.update({"exposure_time": 46.8, "num_exposures": 41000}) elif band == "F087": # These are the exposure times and number of exposures for the secondary filter, F087 self.obs.update({"exposure_time": 286.0, "num_exposures": 860}) else: raise ValueError( "band %s is not supported with the microlensing survey mode! Choose 'F146' or 'F087'" % band ) else: raise ValueError( "survey mode %s not supported! Choose 'wide_area', 'time_domain_wide', 'time_domain_deep', or 'microlensing'" % survey_mode ) if psf_type == "PIXEL": import lenstronomy module_path = os.path.dirname(lenstronomy.__file__) psf_filename = os.path.join( module_path, "SimulationAPI/ObservationConfig/PSF_models/{}.fits".format(band), ) kernel = pyfits.getdata(psf_filename) self.obs.update({"psf_type": "PIXEL", "kernel_point_source": kernel}) elif psf_type == "GAUSSIAN": self.obs.update({"psf_type": "GAUSSIAN"}) else: raise ValueError( "psf_type %s not supported! Choose 'GAUSSIAN' or 'PIXEL'" % psf_type ) self.camera = {"read_noise": 8.5, "pixel_scale": 0.11, "ccd_gain": 1}
# - keyword read_noise: std of noise generated by read-out (in units of electrons) # - keyword pixel_scale: scale (in arcseconds) of pixels # - keyword ccd_gain: electrons/ADU (analog-to-digital unit)
[docs] def kwargs_single_band(self): """ :return: merged kwargs from camera and obs dicts """ kwargs = util.merge_dicts(self.camera, self.obs) return kwargs