Skip to content

Commit a074b05

Browse files
committed
Interferometer simulator now live in auto array
1 parent ceb5e4d commit a074b05

7 files changed

Lines changed: 233 additions & 18 deletions

File tree

autoarray/operators/fourier_transform.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,25 +7,25 @@
77
class Transformer(object):
88
def __init__(self, uv_wavelengths, grid_radians, preload_transform=True):
99

10-
self.uv_wavelengths = uv_wavelengths
11-
self.grid_radians = grid_radians
10+
self.uv_wavelengths = uv_wavelengths.astype('float')
11+
self.grid_radians = grid_radians.in_1d_binned
1212

1313
self.total_visibilities = uv_wavelengths.shape[0]
14-
self.total_image_pixels = grid_radians.shape[0]
14+
self.total_image_pixels = grid_radians.shape_1d
1515

1616
self.preload_transform = preload_transform
1717

1818
if preload_transform:
1919

2020
self.preload_real_transforms = self.preload_real_transforms(
21-
grid_radians=grid_radians,
22-
uv_wavelengths=uv_wavelengths,
21+
grid_radians=self.grid_radians,
22+
uv_wavelengths=self.uv_wavelengths,
2323
total_image_pixels=self.total_image_pixels,
2424
)
2525

2626
self.preload_imag_transforms = self.preload_imag_transforms(
27-
grid_radians=grid_radians,
28-
uv_wavelengths=uv_wavelengths,
27+
grid_radians=self.grid_radians,
28+
uv_wavelengths=self.uv_wavelengths,
2929
total_image_pixels=self.total_image_pixels,
3030
)
3131

autoarray/simulator/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
from .simulator import ImagingSimulator as imaging
2+
from .simulator import InterferometerSimulator as interferometer
8.44 KB
Binary file not shown.

autoarray/simulator/simulator.py

Lines changed: 148 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1-
import autoarray as aa
2-
3-
from autoarray.dataset import imaging
1+
import os
42

3+
from autoarray.util import array_util
4+
from autoarray.structures import grids, kernel
5+
from autoarray.dataset import imaging, interferometer
6+
from autoarray.operators import fourier_transform
57

68
class ImagingSimulator(object):
79
def __init__(
@@ -66,7 +68,7 @@ def lsst(
6668
"""Default settings for an observation with the Large Synotpic Survey Telescope.
6769
6870
This can be customized by over-riding the default input values."""
69-
psf = aa.kernel.from_gaussian(
71+
psf = kernel.Kernel.from_gaussian(
7072
shape_2d=psf_shape_2d, sigma=psf_sigma, pixel_scales=pixel_scales
7173
)
7274
return ImagingSimulator(
@@ -98,7 +100,7 @@ def euclid(
98100
"""Default settings for an observation with the Euclid space satellite.
99101
100102
This can be customized by over-riding the default input values."""
101-
psf = aa.kernel.from_gaussian(
103+
psf = kernel.Kernel.from_gaussian(
102104
shape_2d=psf_shape_2d, sigma=psf_sigma, pixel_scales=pixel_scales
103105
)
104106
return ImagingSimulator(
@@ -130,7 +132,7 @@ def hst(
130132
"""Default settings for an observation with the Hubble Space Telescope.
131133
132134
This can be customized by over-riding the default input values."""
133-
psf = aa.kernel.from_gaussian(
135+
psf = kernel.Kernel.from_gaussian(
134136
shape_2d=psf_shape_2d, sigma=psf_sigma, pixel_scales=pixel_scales
135137
)
136138
return ImagingSimulator(
@@ -163,7 +165,7 @@ def hst_up_sampled(
163165
pixel-scale to better sample the PSF.
164166
165167
This can be customized by over-riding the default input values."""
166-
psf = aa.kernel.from_gaussian(
168+
psf = kernel.Kernel.from_gaussian(
167169
shape_2d=psf_shape_2d, sigma=psf_sigma, pixel_scales=pixel_scales
168170
)
169171
return ImagingSimulator(
@@ -195,7 +197,7 @@ def keck_adaptive_optics(
195197
"""Default settings for an observation using Keck Adaptive Optics imaging.
196198
197199
This can be customized by over-riding the default input values."""
198-
psf = aa.kernel.from_gaussian(
200+
psf = kernel.Kernel.from_gaussian(
199201
shape_2d=psf_shape_2d, sigma=psf_sigma, pixel_scales=pixel_scales
200202
)
201203
return ImagingSimulator(
@@ -212,7 +214,7 @@ def keck_adaptive_optics(
212214

213215
@property
214216
def grid(self):
215-
return aa.grid.uniform(
217+
return grids.Grid.uniform(
216218
shape_2d=self.shape_2d,
217219
pixel_scales=self.pixel_scales,
218220
sub_size=self.sub_size,
@@ -252,3 +254,140 @@ def from_image(self, image, name=None):
252254
noise_seed=self.noise_seed,
253255
name=name,
254256
)
257+
258+
259+
class InterferometerSimulator(object):
260+
def __init__(
261+
self,
262+
real_space_shape_2d,
263+
real_space_pixel_scales,
264+
uv_wavelengths,
265+
sub_size,
266+
exposure_time,
267+
background_sky_level,
268+
primary_beam=None,
269+
noise_sigma=0.1,
270+
noise_if_add_noise_false=0.1,
271+
noise_seed=-1,
272+
origin=(0.0, 0.0),
273+
):
274+
"""A class representing a Imaging observation, using the shape of the image, the pixel scale,
275+
psf, exposure time, etc.
276+
277+
Parameters
278+
----------
279+
real_space_shape_2d : (int, int)
280+
The shape of the observation. Note that we do not simulator a full Imaging frame (e.g. 2000 x 2000 pixels for \
281+
Hubble imaging), but instead just a cut-out around the strong lens.
282+
real_space_pixel_scales : float
283+
The size of each pixel in arc seconds.
284+
psf : PSF
285+
An arrays describing the PSF kernel of the image.
286+
exposure_time : float
287+
The exposure time of an observation using this data_type.
288+
background_sky_level : float
289+
The level of the background sky of an observationg using this data_type.
290+
"""
291+
292+
if type(real_space_pixel_scales) is float:
293+
real_space_pixel_scales = (real_space_pixel_scales, real_space_pixel_scales)
294+
295+
self.real_space_shape_2d = real_space_shape_2d
296+
self.real_space_pixel_scales = real_space_pixel_scales
297+
self.uv_wavelengths = uv_wavelengths
298+
self.sub_size = sub_size
299+
self.origin = origin
300+
self.transformer = fourier_transform.Transformer(uv_wavelengths=self.uv_wavelengths, grid_radians=self.grid.in_1d_binned.in_radians)
301+
self.exposure_time = exposure_time
302+
self.background_sky_level = background_sky_level
303+
self.primary_beam = primary_beam
304+
self.noise_sigma = noise_sigma
305+
self.noise_if_add_noise_false = noise_if_add_noise_false
306+
self.noise_seed = noise_seed
307+
308+
@property
309+
def grid(self):
310+
return grids.Grid.uniform(
311+
shape_2d=self.real_space_shape_2d,
312+
pixel_scales=self.real_space_pixel_scales,
313+
sub_size=self.sub_size,
314+
origin=self.origin,
315+
)
316+
317+
@classmethod
318+
def sma(
319+
cls,
320+
real_space_shape_2d=(151, 151),
321+
real_space_pixel_scales=(0.05, 0.05),
322+
sub_size=8,
323+
primary_beam_shape_2d=None,
324+
primary_beam_sigma=None,
325+
exposure_time=100.0,
326+
background_sky_level=1.0,
327+
noise_sigma=0.1,
328+
noise_if_add_noise_false=0.1,
329+
noise_seed=-1,
330+
):
331+
"""Default settings for an observation with the Large Synotpic Survey Telescope.
332+
333+
This can be customized by over-riding the default input values."""
334+
335+
uv_wavelengths_path = "{}/dataset/sma_uv_wavelengths.fits".format(
336+
os.path.dirname(os.path.realpath(__file__)))
337+
338+
uv_wavelengths = array_util.numpy_array_1d_from_fits(file_path=uv_wavelengths_path, hdu=0)
339+
340+
if primary_beam_shape_2d is not None and primary_beam_sigma is not None:
341+
primary_beam = kernel.Kernel.from_gaussian(
342+
shape_2d=primary_beam_shape_2d, sigma=primary_beam_sigma, pixel_scales=real_space_pixel_scales
343+
)
344+
else:
345+
primary_beam = None
346+
347+
return InterferometerSimulator(
348+
real_space_shape_2d=real_space_shape_2d,
349+
real_space_pixel_scales=real_space_pixel_scales,
350+
uv_wavelengths=uv_wavelengths,
351+
sub_size=sub_size,
352+
primary_beam=primary_beam,
353+
exposure_time=exposure_time,
354+
background_sky_level=background_sky_level,
355+
noise_sigma=noise_sigma,
356+
noise_if_add_noise_false=noise_if_add_noise_false,
357+
noise_seed=noise_seed,
358+
)
359+
360+
def from_real_space_image(self, real_space_image):
361+
"""
362+
Create a realistic simulated image by applying effects to a plain simulated image.
363+
364+
Parameters
365+
----------
366+
name
367+
real_space_image : ndarray
368+
The image before simulating (e.g. the lens and source galaxies before optics blurring and Imaging read-out).
369+
pixel_scales: float
370+
The scale of each pixel in arc seconds
371+
exposure_time_map : ndarray
372+
An arrays representing the effective exposure time of each pixel.
373+
psf: PSF
374+
An arrays describing the PSF the simulated image is blurred with.
375+
background_sky_map : ndarray
376+
The value of background sky in every image pixel (electrons per second).
377+
add_noise: Bool
378+
If True poisson noise_maps is simulated and added to the image, based on the total counts in each image
379+
pixel
380+
noise_seed: int
381+
A seed for random noise_maps generation
382+
"""
383+
return interferometer.SimulatedInterferometer.simulate(
384+
real_space_image=real_space_image,
385+
real_space_pixel_scales=self.real_space_pixel_scales,
386+
exposure_time=self.exposure_time,
387+
transformer=self.transformer,
388+
primary_beam=self.primary_beam,
389+
background_sky_level=self.background_sky_level,
390+
noise_sigma=self.noise_sigma,
391+
noise_if_add_noise_false=self.noise_if_add_noise_false,
392+
noise_seed=self.noise_seed,
393+
)
8.44 KB
Binary file not shown.

test_autoarray/unit/simulator/test_simulator.py

Lines changed: 76 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import autoarray as aa
22

3+
import pytest
34
import numpy as np
45
import os
56

@@ -8,7 +9,7 @@
89
)
910

1011

11-
class TestImaging:
12+
class TestImagingSimulator:
1213
def test__simulator_grid_is_uniform_grid_with_same_inputs(self):
1314

1415
grid = aa.grid.uniform(
@@ -146,3 +147,77 @@ def test__from_image_same_as_manual_image_input(self):
146147
assert (
147148
imaging_simulated.exposure_time_map == imaging_manual.exposure_time_map
148149
).all()
150+
151+
152+
class TestInterferometerSimulator:
153+
def test__simulator_grid_is_uniform_grid_with_same_inputs(self):
154+
155+
grid = aa.grid.uniform(
156+
shape_2d=(31, 31), pixel_scales=0.05, sub_size=1, origin=(0.1, 0.1)
157+
)
158+
159+
simulator = aa.simulator.interferometer(
160+
real_space_shape_2d=(31, 31),
161+
real_space_pixel_scales=0.05,
162+
uv_wavelengths=np.ones((7,2)),
163+
sub_size=1,
164+
origin=(0.1, 0.1),
165+
exposure_time=20.0,
166+
background_sky_level=10.0,
167+
)
168+
169+
assert (simulator.grid == grid).all()
170+
171+
def test__constructor_and_specific_instrument_class_methods(self):
172+
173+
sma = aa.simulator.interferometer.sma()
174+
175+
uv_wavelengths_path = "{}/dataset/sma_uv_wavelengths.fits".format(
176+
os.path.dirname(os.path.realpath(__file__)))
177+
178+
sma_uv_wavelengths = aa.util.array.numpy_array_1d_from_fits(file_path=uv_wavelengths_path, hdu=0)
179+
180+
assert sma.real_space_shape_2d == (151, 151)
181+
assert sma.real_space_pixel_scales == (0.05, 0.05)
182+
assert (sma.uv_wavelengths == sma_uv_wavelengths).all()
183+
assert sma.uv_wavelengths[0] == pytest.approx([184584.953125, -16373.30566406], 1.0e-4)
184+
assert sma.exposure_time == 100.0
185+
assert sma.background_sky_level == 1.0
186+
187+
def test__from_real_space_image_same_as_manual_image_input(self):
188+
189+
primary_beam = aa.kernel.manual_2d(
190+
array=np.array([[0.0, 1.0, 0.0], [1.0, 2.0, 1.0], [0.0, 1.0, 0.0]]),
191+
pixel_scales=1.0,
192+
)
193+
194+
real_space_image = aa.array.manual_2d([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]])
195+
196+
simulator = aa.simulator.interferometer(
197+
real_space_shape_2d=(2, 3),
198+
real_space_pixel_scales=0.05,
199+
uv_wavelengths=np.ones((7,2)),
200+
sub_size=1,
201+
primary_beam=primary_beam,
202+
exposure_time=10000.0,
203+
background_sky_level=100.0,
204+
noise_sigma=0.1,
205+
noise_seed=1,
206+
)
207+
208+
interferometer_simulated = simulator.from_real_space_image(real_space_image=real_space_image)
209+
210+
interferometer_manual = aa.interferometer.simulate(
211+
real_space_image=real_space_image,
212+
real_space_pixel_scales=0.05,
213+
transformer=simulator.transformer,
214+
exposure_time=10000.0,
215+
primary_beam=primary_beam,
216+
background_sky_level=100.0,
217+
noise_sigma=0.1,
218+
noise_seed=1,
219+
)
220+
221+
assert (interferometer_simulated.visibilities == interferometer_manual.visibilities).all()
222+
assert (interferometer_simulated.noise_map == interferometer_manual.noise_map).all()
223+
assert (interferometer_simulated.primary_beam.in_2d == interferometer_manual.primary_beam.in_2d).all()

test_autoarray/unit/test_files/config/general.ini

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ assert_pickle_matches = False
1313

1414
[numba]
1515
nopython = True
16-
cache = True
16+
cache = False
1717
parallel = False
1818

1919
[inversion]

0 commit comments

Comments
 (0)