Skip to content

Commit

Permalink
test cleanup and improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
bhazelton committed Feb 25, 2024
1 parent 64ee930 commit ddcd646
Show file tree
Hide file tree
Showing 23 changed files with 776 additions and 608 deletions.
6 changes: 4 additions & 2 deletions pyuvsim/analyticbeam.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,9 @@ def interp(
interp_basis_vector = None
elif self.type == 'gaussian':
if (self.diameter is None) and (self.sigma is None):
raise ValueError("Dish diameter needed for gaussian beam -- units: meters")
raise ValueError(
"Antenna diameter (meters) or sigma (radians) needed for gaussian beams."
)
interp_data = np.zeros((2, 2, freq_array.size, az_array.size), dtype=float)
# gaussian beam only depends on Zenith Angle (symmetric is azimuth)
# standard deviation of sigma is referring to the standard deviation of e-field beam!
Expand All @@ -191,7 +193,7 @@ def interp(
interp_basis_vector = None
elif self.type == 'airy':
if self.diameter is None:
raise ValueError("Dish diameter needed for airy beam -- units: meters")
raise ValueError("Antenna diameter needed for airy beam -- units: meters")
interp_data = np.zeros((2, 2, freq_array.size, az_array.size), dtype=float)
za_grid, f_grid = np.meshgrid(za_array, freq_array)
xvals = self.diameter / 2. * np.sin(za_grid) * 2. * np.pi * f_grid / c_ms
Expand Down
1 change: 0 additions & 1 deletion pyuvsim/antenna.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,6 @@ def get_beam_jones(
jones_matrix = np.zeros((2, 2, Ncomponents), dtype=complex)

# first axis is feed, second axis is theta, phi (opposite order of beam!)
print(interp_data.shape)
jones_matrix[0, 0] = interp_data[1, 0, 0, :]
jones_matrix[1, 1] = interp_data[0, 1, 0, :]
jones_matrix[0, 1] = interp_data[0, 0, 0, :]
Expand Down
8 changes: 4 additions & 4 deletions pyuvsim/data/28m_triangle_10time_10chan.uvfits

Large diffs are not rendered by default.

Binary file added pyuvsim/data/gleam_triangle_flat.uvh5
Binary file not shown.
Binary file added pyuvsim/data/gleam_triangle_spectral_index.uvh5
Binary file not shown.
Binary file added pyuvsim/data/gleam_triangle_subband.uvh5
Binary file not shown.
2 changes: 1 addition & 1 deletion pyuvsim/data/test_config/28m_triangle_10time_10chan.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@ beam_paths:
type: airy
diameter: 14.6
telescope_location: (-30.721527777777847, 21.428305555555557, 1073.0000000046566)
telescope_name: HERA
telescope_name: Triangle
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@ beam_paths:
3:
type: gaussian
telescope_location: (-30.721527777777847, 21.428305555555557, 1073.0000000046566)
telescope_name: HERA
telescope_name: Triangle
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,4 @@ beam_paths:
3:
type: airy
telescope_location: (-30.721527777777847, 21.428305555555557, 1073.0000000046566)
telescope_name: HERA
telescope_name: Triangle
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@ beam_paths:
type: airy
diameter: 14.6
telescope_location: (-30.721527777777847, 21.428305555555557, 1073.0000000046566)
telescope_name: HERA
telescope_name: Triangle
Binary file modified pyuvsim/data/testfile_singlesource.uvh5
Binary file not shown.
2 changes: 1 addition & 1 deletion pyuvsim/profiling.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ def set_profiler(func_list=default_profile_funcs, rank=0, outfile_prefix='time_p
return

prof = LineProfiler()
if mpi is None or prof is None: # pragma: no cover
if mpi is None or prof is None:
raise ImportError("You need mpi4py and line_profiler to use the "
"profiling module. Install them both by running pip "
"install pyuvsim[all].")
Expand Down
20 changes: 14 additions & 6 deletions pyuvsim/simsetup.py
Original file line number Diff line number Diff line change
Expand Up @@ -856,16 +856,16 @@ def initialize_catalog_from_params(
Returns
-------
skydata: :class:`pyradiosky.SkyModel`
sky: :class:`pyradiosky.SkyModel`
Source catalog filled with data.
source_list_name: str
Catalog identifier for metadata. Only returned if return_catname is True.
"""
if return_catname is None:
warnings.warn(
"The return_catname parameter currently defaults to True, but starting in"
"version 1.4 it will default to False.",
"The return_catname parameter currently defaults to True, but "
"starting in version 1.4 it will default to False.",
DeprecationWarning
)
return_catname = True
Expand Down Expand Up @@ -1218,12 +1218,13 @@ def parse_telescope_params(tele_params, config_path='', freq_range=None, force_b
if 'array_layout' not in tele_params:
raise KeyError('array_layout must be provided.')
array_layout = tele_params.pop('array_layout')
assert isinstance(array_layout, (str, dict)), "array_layout must be a string or a dict"
if isinstance(array_layout, str):
# Interpet as file path to layout csv file.
layout_csv = array_layout
# if array layout is a str, parse it as .csv filepath
if isinstance(layout_csv, str):
if not os.path.exists(layout_csv):
if not os.path.exists(layout_csv) and isinstance(config_path, str):
layout_csv = os.path.join(config_path, layout_csv)
if not os.path.exists(layout_csv):
raise ValueError(
Expand Down Expand Up @@ -2015,9 +2016,16 @@ def initialize_uvdata_from_keywords(
arrfile = antenna_layout_filepath is not None
outfile = output_layout_filename is not None

if write_files:
if array_layout is None and antenna_layout_filepath is None:
raise ValueError(
"Either array_layout or antenna_layout_filepath must be passed."
)

if array_layout is None or isinstance(array_layout, str) or write_files:
if path_out is None:
path_out = '.'

if write_files:
if not outfile:
if not arrfile:
output_layout_filename = 'antenna_layout.csv'
Expand Down Expand Up @@ -2057,7 +2065,7 @@ def initialize_uvdata_from_keywords(
)

if array_layout is None:
if outfile:
if write_files and output_layout_filename is not None:
array_layout = output_layout_filename
else:
array_layout = antenna_layout_filepath
Expand Down
72 changes: 42 additions & 30 deletions pyuvsim/tests/test_analyticbeam.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
# Licensed under the 3-clause BSD License

import os
import re

import numpy as np
import pytest
Expand Down Expand Up @@ -48,13 +49,13 @@ def test_uniform_beam(heratext_posfreq):
nsrcs = az_vals.size
n_freqs = freqs.size

interpolated_beam, interp_basis_vector = beam.interp(
interpolated_beam, _ = beam.interp(
az_array=az_vals, za_array=za_vals, freq_array=freqs
)
expected_data = np.zeros((2, 2, n_freqs, nsrcs), dtype=float)
expected_data[1, 0, :, :] = 1
expected_data[0, 1, :, :] = 1
assert np.allclose(interpolated_beam, expected_data)
np.testing.assert_allclose(interpolated_beam, expected_data)


def test_airy_beam_values(heratext_posfreq):
Expand All @@ -64,7 +65,7 @@ def test_airy_beam_values(heratext_posfreq):

az_vals, za_vals, freq_vals = heratext_posfreq

interpolated_beam, interp_basis_vector = beam.interp(
interpolated_beam, _ = beam.interp(
az_array=az_vals, za_array=za_vals, freq_array=freq_vals
)

Expand All @@ -79,7 +80,7 @@ def test_airy_beam_values(heratext_posfreq):
expected_data[1, 0, :, :] = airy_values
expected_data[0, 1, :, :] = airy_values

assert np.allclose(interpolated_beam, expected_data)
np.testing.assert_allclose(interpolated_beam, expected_data)


def test_interp_errors(heratext_posfreq):
Expand Down Expand Up @@ -164,7 +165,7 @@ def test_achromatic_gaussian_beam(heratext_posfreq):
expected_data[1, 0, :, :] = gaussian_vals
expected_data[0, 1, :, :] = gaussian_vals

assert np.allclose(interpolated_beam, expected_data)
np.testing.assert_allclose(interpolated_beam, expected_data)


@pytest.mark.filterwarnings("ignore:UVW orientation appears to be flipped")
Expand All @@ -186,7 +187,7 @@ def test_gaussbeam_values():

time = Time(hera_uv.time_array[0], scale='utc', format='jd')

catalog, mock_keywords = pyuvsim.create_mock_catalog(
catalog, _ = pyuvsim.create_mock_catalog(
time=time, arrangement='long-line', Nsrcs=41, min_alt=80., array_location=array_location
)

Expand Down Expand Up @@ -237,16 +238,11 @@ def test_chromatic_gaussian():
az = np.zeros(Npix)
za = np.linspace(0, np.pi / 2., Npix)

# Error if trying to define chromatic beam without a reference frequency
with pytest.raises(ValueError,
match='ref_freq must be set for nonzero gaussian beam spectral index'):
pyuvsim.AnalyticBeam('gaussian', sigma=sigma, spectral_index=alpha)

A = pyuvsim.AnalyticBeam('gaussian', sigma=sigma, ref_freq=freqs[0], spectral_index=alpha)
gauss = pyuvsim.AnalyticBeam('gaussian', sigma=sigma, ref_freq=freqs[0], spectral_index=alpha)

# Get the widths at each frequency.

vals, _ = A.interp(az, za, freqs)
vals, _ = gauss.interp(az, za, freqs)

vals = vals[0, 1]

Expand All @@ -256,6 +252,13 @@ def test_chromatic_gaussian():
assert np.isclose(sig_f, 2 * hwhm / 2.355, atol=1e-3)


def test_chromatic_gaussian_error():
# Error if trying to define chromatic beam without a reference frequency
with pytest.raises(ValueError,
match='ref_freq must be set for nonzero gaussian beam spectral index'):
pyuvsim.AnalyticBeam('gaussian', sigma=np.radians(15.0), spectral_index=-1.5)


def test_power_analytic_beam():
# Check that power beam evaluation matches electric field amp**2 for analytic beams.
freqs = np.arange(120e6, 160e6, 4e6)
Expand All @@ -271,12 +274,7 @@ def test_power_analytic_beam():
pb.efield_to_power()
evals = eb.interp(az, za, freqs)[0][0, 1]
pvals = pb.interp(az, za, freqs)[0][0, 0]
assert np.allclose(evals**2, pvals)

# Ensure uniform beam works
pb = pyuvsim.AnalyticBeam('uniform')
pb.efield_to_power()
pb.interp(az, za, freqs)
np.testing.assert_allclose(evals**2, pvals)


def test_comparison():
Expand All @@ -292,25 +290,39 @@ def test_comparison():
assert beam2 != beam1


def test_beamerrs():
def test_beam_init_errs():
"""
Error cases.
"""
with pytest.raises(ValueError, match='type not recognized'):
pyuvsim.AnalyticBeam('unsupported_type')

beam = pyuvsim.AnalyticBeam('gaussian')

@pytest.mark.parametrize(
("beam_type", "error_msg"),
[
(
"gaussian",
re.escape(
"Antenna diameter (meters) or sigma (radians) needed for gaussian beams."
),
),
("airy", "Antenna diameter needed for airy beam"),
("noninterpolable", "no interp for this type: noninterpolable"),
]
)
def test_beam_interp_errs(beam_type, error_msg):
if beam_type == "noninterpolable":
beam = pyuvsim.AnalyticBeam("uniform")
beam.type = "noninterpolable"
else:
beam = pyuvsim.AnalyticBeam(beam_type)
az, za = np.random.uniform(0.0, np.pi, (2, 5))
freq_arr = np.linspace(1e8, 1.5e8, 10)
with pytest.raises(ValueError, match='Dish diameter needed for gaussian beam'):
beam.interp(az, za, freq_arr)

beam.type = 'airy'
with pytest.raises(ValueError, match='Dish diameter needed for airy beam'):
beam.interp(az, za, freq_arr)

beam.type = 'noninterpolable'
with pytest.raises(ValueError, match='no interp for this type: noninterpolable'):
with pytest.raises(
ValueError,
match=error_msg
):
beam.interp(az, za, freq_arr)


Expand Down
50 changes: 49 additions & 1 deletion pyuvsim/tests/test_antenna.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# -*- mode: python; coding: utf-8 -*
# Copyright (c) 2021 Radio Astronomy Software Group
# Licensed under the 3-clause BSD License
import copy
import os

import numpy as np
Expand Down Expand Up @@ -47,7 +48,38 @@ def test_jones_set_spline(cst_beam, hera_loc):
altaz[:, 0] = alts.flatten()
altaz[:, 1] = azs.flatten()

antenna.get_beam_jones(array, altaz, 150e6, interpolation_function='az_za_simple')
jones_matrix = antenna.get_beam_jones(
array, altaz, 150e6, interpolation_function='az_za_simple'
)

# These are just values from a run, so this just tests for unexpected changes.
expected_jones = np.array(
[
[
[
-4.57061296e-04 - 3.88626249e-04j,
-7.28285993e-05 - 1.45479743e-04j,
-7.28285993e-05 - 1.45479743e-04j
], [
-3.35886569e-02 - 1.83636844e-02j,
-3.36205621e-02 - 1.84105336e-02j,
-3.36205621e-02 - 1.84105336e-02j
],
], [
[
-1.04000784e-02 - 1.11629186e-02j,
-1.03973090e-02 - 1.11516998e-02j,
-1.03973090e-02 - 1.11516998e-02j
], [
5.32870283e-04 + 1.16831373e-04j,
1.26946128e-06 - 1.22843330e-06j,
1.26946128e-06 - 1.22843330e-06j
],
],
]
)

np.testing.assert_allclose(expected_jones, jones_matrix)


def test_jones_set_interp(cst_beam, hera_loc):
Expand Down Expand Up @@ -102,3 +134,19 @@ def test_set_interps(cst_beam, hera_loc):

if interp_function_attr:
assert beam.interpolation_function == 'az_za_simple'


def test_ant_comparison():
antenna1 = pyuvsim.Antenna('ant1', 1, np.array([0, 10, 0]), 1)
antenna2 = pyuvsim.Antenna('ant2', 2, np.array([0, 20, 0]), 1)

ant1_copy = copy.deepcopy(antenna1)

assert antenna1 < antenna2
assert antenna1 <= antenna2
assert antenna1 <= antenna1
assert antenna1 == ant1_copy

assert antenna2 > antenna1
assert antenna2 >= antenna2
assert antenna1 >= antenna1
27 changes: 27 additions & 0 deletions pyuvsim/tests/test_baseline.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# -*- mode: python; coding: utf-8 -*
# Copyright (c) 2024 Radio Astronomy Software Group
# Licensed under the 3-clause BSD License
import copy

import numpy as np

import pyuvsim


def test_baseline_comparison():
antenna1 = pyuvsim.Antenna('ant1', 1, np.array([0, 10, 0]), 1)
antenna2 = pyuvsim.Antenna('ant2', 2, np.array([0, 20, 0]), 1)

baseline12 = pyuvsim.Baseline(antenna1, antenna2)
baseline21 = pyuvsim.Baseline(antenna2, antenna1)

bl12_copy = copy.deepcopy(baseline12)

assert baseline12 < baseline21
assert baseline12 <= baseline21
assert baseline12 <= baseline12
assert baseline12 == bl12_copy

assert baseline21 > baseline12
assert baseline21 >= baseline21
assert baseline21 >= baseline12
Loading

0 comments on commit ddcd646

Please sign in to comment.