@@ -50,6 +50,7 @@ def simulate_roman_image(
5050 dec = None ,
5151 date = datetime .datetime (year = 2027 , month = 7 , day = 7 , hour = 0 , minute = 0 , second = 0 ),
5252 psf_directory = None ,
53+ precomputed_background = None ,
5354):
5455 """Creates a Roman-simulated image of a selected lens with noise, using
5556 galsim's noise settings and PSFs from STPSF.
@@ -101,6 +102,9 @@ def simulate_roman_image(
101102 Otherwise, the psf will be generated by stpsf on the fly, which is very slow.
102103 See the note in the ``get_psf`` method's docstring for details on the PSF file naming convention.
103104 :type psf_directory: str
105+ :param precomputed_background: A 2-D numpy array of background counts, shape (num_pix+6, num_pix+6), to be added to the image before noise realizations. This can be computed by calling precompute_roman_background with the same band, num_pix, oversample, exposure_time, and date parameters as passed into this function. If None, the background will be computed on the fly.
106+ :type precomputed_background: numpy.ndarray, optional
107+
104108 :return: simulated image in units of flux per second.
105109 :rtype: numpy.ndarray
106110 """
@@ -182,18 +186,25 @@ def simulate_roman_image(
182186 image_no_noise = convolved .drawImage (im )
183187
184188 if add_noise :
185- # Obtain sky and thermal background corresponding to certain band and add it to the image
186- # Poisson noise realization is not handled until later
187- image_with_background = add_sky_plus_thermal_background (
188- image_no_noise ,
189- band ,
190- detector ,
191- num_pix ,
192- _exposure_time ,
193- ra ,
194- dec ,
195- date ,
196- )
189+ if precomputed_background is not None :
190+ # Convert precomputed numpy array to a galsim Image with the same
191+ # scale and origin as image_no_noise so the addition is element-wise.
192+ bg = galsim .ImageF (precomputed_background .astype (np .float32 ), scale = 0.11 )
193+ bg .setOrigin (0 , 0 )
194+ image_with_background = image_no_noise + bg
195+ else :
196+ # Obtain sky and thermal background corresponding to certain band and add it to the image
197+ # Poisson noise realization is not handled until later
198+ image_with_background = add_sky_plus_thermal_background (
199+ image_no_noise ,
200+ band ,
201+ detector ,
202+ num_pix ,
203+ _exposure_time ,
204+ ra ,
205+ dec ,
206+ date ,
207+ )
197208
198209 # Add noise realizations and detector effects
199210 # Need to handle each exposure separately to properly take into account read noise and persistence
@@ -420,3 +431,56 @@ def _get_wcs_dict(ra, dec, date):
420431
421432 # NB targ_pos indicates the position to observe at the center of the focal plane array
422433 return roman .getWCS (world_pos = targ_pos , date = date )
434+
435+
436+ def precompute_roman_background (
437+ band ,
438+ num_pix ,
439+ exposure_time ,
440+ detector = None ,
441+ detector_pos = None ,
442+ ra = None ,
443+ dec = None ,
444+ date = datetime .datetime (year = 2027 , month = 7 , day = 7 ),
445+ oversample = 3 ,
446+ ):
447+ """Precompute the Roman sky + thermal background as a numpy array.
448+
449+ Calls add_sky_plus_thermal_background with a zero image so no lens
450+ computation is involved. Pass the returned array to
451+ simulate_roman_image via precomputed_background to skip the WCS /
452+ sky-level computation on every call.
453+
454+ :param band: imaging band (e.g. 'F106').
455+ :param num_pix: pixels per axis — must match simulate_roman_image.
456+ :param exposure_time: seconds — must match simulate_roman_image
457+ internally.
458+ :param detector: WFI detector (1–18). Random if None.
459+ :param detector_pos: (x, y) pixel position. Random if None.
460+ :param ra: RA in degrees. Random if None.
461+ :param dec: Dec in degrees. Random if None.
462+ :param date: observation date; affects zodiacal light level.
463+ :param oversample: must match simulate_roman_image.
464+ :return: 2-D numpy array of background counts, shape (num_pix+6,
465+ num_pix+6).
466+ """
467+ if detector is None :
468+ detector = random .randint (1 , 18 )
469+ if detector_pos is None :
470+ x_pos = random .randint (4 + num_pix * oversample , 4092 - num_pix * oversample )
471+ y_pos = random .randint (4 + num_pix * oversample , 4092 - num_pix * oversample )
472+ detector_pos = (x_pos , y_pos )
473+ if ra is None :
474+ ra = random .uniform (5 , 60 )
475+ if dec is None :
476+ dec = random .uniform (- 40 , - 20 )
477+
478+ num_pix_buffered = num_pix + 6
479+
480+ zero_image = galsim .ImageF (num_pix_buffered , num_pix_buffered , scale = 0.11 )
481+ zero_image .setOrigin (0 , 0 )
482+
483+ background_image = add_sky_plus_thermal_background (
484+ zero_image , band , detector , num_pix_buffered , exposure_time , ra , dec , date
485+ )
486+ return background_image .array
0 commit comments