Repository: RadioAstronomySoftwareGroup/pyuvsim

Issue:
Beam parameters assigned to beam_ids
Currently, it is not possible to assign, say, different diameters to different beam_ids within a telescope_config yaml. For instance,
```
beam_paths:
  0: 'airy'
  1: 'gaussian'
sigma: 0.3
diameter=14.0
```

This will assign diameter=14m to both the airy and gaussian beams. There's currently no way to specify which analytic beam parameters go to which beams.

One way to solve this is to change the syntax so that each beam path is a list:

```
beam_paths:
  0: 'airy', diameter=15.0
  1: 'gaussian', diameter=14.0
```

Discussion:

Patch:
diff --git a/.travis.yml b/.travis.yml
index 308d9fa..38e62cb 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -3,7 +3,6 @@ dist: trusty
 language: python
 python:
   # We don't actually use the Travis Python, but this keeps it organized.
-  - "2.7"
   - "3.6"
 env:
   global:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4f71628..bfd643c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,9 +2,17 @@
 
 ## [Unreleased]
 
+### Added
+- Support for individual shape parameters for analytic beams.
+
+### Changed
+
+### Fixed
+- Removed warning catch for astropy GLEAM read in tests -- new versions don't raise the warning.
+
 ## [1.1.1] - 2019-11-14
 
-## Added
+### Added
 - A function for checking the memory usage on each Node
 
 ### Changed
diff --git a/docs/example_configs/bl_lite_mixed.yaml b/docs/example_configs/bl_lite_mixed.yaml
index a8113a5..b11957e 100644
--- a/docs/example_configs/bl_lite_mixed.yaml
+++ b/docs/example_configs/bl_lite_mixed.yaml
@@ -1,6 +1,8 @@
 beam_paths:
   0: 'hera.uvbeam'
-  1: 'airy'
-diameter: 16
+  1: 'airy', diameter=16
+  2: 'gaussian', sigma=0.03
+  3: 'airy'
+diameter: 12
 telescope_location: (-30.72152777777791, 21.428305555555557, 1073.0000000093132)
 telescope_name: BLLITE
diff --git a/docs/parameter_files.rst b/docs/parameter_files.rst
index 491ebe6..fcae5ad 100644
--- a/docs/parameter_files.rst
+++ b/docs/parameter_files.rst
@@ -139,7 +139,7 @@ Telescope Configuration
 
     .. literalinclude:: example_configs/bl_lite_mixed.yaml
 
-    This yaml file provides the telescope name, location in latitude/longitude/altitude in degrees/degrees/meters (respectively), and the `beam dictionary`. In this case, beam_id == 0 is the UVBeam file written out to hera.uvbeam, and beam_id == 1 is an Airy beam with diameter 16m. The dictionary only needs to be as long as the number of unique beams used in the array, while the layout file specifies which antennas will use which beam type. This allows for a mixture of beams to be used, as in this example.
+    This yaml file provides the telescope name, location in latitude/longitude/altitude in degrees/degrees/meters (respectively), and the `beam dictionary`. In this case, beam_id == 0 is the UVBeam file hera.uvbeam, beam_id == 1 is an Airy disk with diameter 14 m, beam_id == 2 is a Gaussian beam with sigma 0.03, and beam_id == 3 is another Airy beam. When no shape parameter is written inline in the beam_dictionary (as with 3), pyuvsim will look for a default parameter below. So in this case, the beam_id == 3 ends up with a diameter of 12 m. The dictionary only needs to be as long as the number of unique beams used in the array, while the layout file specifies which antennas will use which beam type. This allows for a mixture of beams to be used, as in this example. Unassigned beams will be ignored (the given layout file does not use beams 2 or 3).
 
     Analytic beams may require additional parameters.
 
@@ -147,7 +147,7 @@ Telescope Configuration
     - gaussian = Gaussian function shaped beam. Requires either an antenna diameter (in meters) or a standard deviation sigma (in radians). This standard deviation sets the width of the beam in zenith angle. Note that defining gaussian beams via `sigma` will be deprecated in the future.
     - airy = Airy disk (ie, diffraction pattern of a circular aperture). Requires an antenna diameter.
 
-    These extra keywords should be included after the beam dictionary in the telescope config file. Note that beams defined with an antenna diameter will be chromatic. That is, their widths on the sky will change with frequency.
+    Note that beams defined with an antenna diameter will be chromatic (their widths on the sky will change with frequency).
 
     The figure below shows the array created by these configurations, with beam type indicated by color.
 
diff --git a/pyuvsim/analyticbeam.py b/pyuvsim/analyticbeam.py
index a9d2f90..d446919 100644
--- a/pyuvsim/analyticbeam.py
+++ b/pyuvsim/analyticbeam.py
@@ -79,7 +79,7 @@ class AnalyticBeam(object):
 
         self.sigma = sigma
         if self.type == 'gaussian' and self.sigma is not None:
-            warnings.warn("Achromatic gaussian beams will not be supported in the future."
+            warnings.warn("Achromatic gaussian beams will not be supported in the future. "
                           + "Define your gaussian beam by a dish diameter from now on.",
                           PendingDeprecationWarning)
 
diff --git a/pyuvsim/simsetup.py b/pyuvsim/simsetup.py
index eadf8ea..33fbfe6 100644
--- a/pyuvsim/simsetup.py
+++ b/pyuvsim/simsetup.py
@@ -67,15 +67,17 @@ def _parse_layout_csv(layout_csv):
                          dtype=dt.dtype)
 
 
-def _write_layout_csv(filepath, antpos_enu, antenna_names, antenna_numbers):
+def _write_layout_csv(filepath, antpos_enu, antenna_names, antenna_numbers, beam_ids=None):
     col_width = max([len(name) for name in antenna_names])
     header = ("{:" + str(col_width) + "} {:8} {:8} {:10} {:10} {:10}\n").format(
         "Name", "Number", "BeamID", "E", "N", "U"
     )
+    if beam_ids is None:
+        beam_ids = np.zeros(len(antenna_names), dtype=int)
     with open(filepath, 'w') as lfile:
         lfile.write(header + '\n')
         for i, (e, n, u) in enumerate(antpos_enu):
-            beam_id = 0
+            beam_id = beam_ids[i]
             name = antenna_names[i]
             num = antenna_numbers[i]
             line = (
@@ -615,34 +617,105 @@ def initialize_catalog_from_params(obs_params, input_uv=None):
     return np.asarray(catalog), source_list_name
 
 
+def _construct_beam_list(beam_ids, telconfig):
+    beam_list = []
+    for beamID in beam_ids:
+        beam_model = telconfig['beam_paths'][beamID]
+
+        # First, check to see if the string specifies a beam path.
+        altpath = os.path.join(SIM_DATA_PATH, beam_model)
+        if os.path.exists(beam_model):
+            beam_list.append(beam_model)
+        elif os.path.exists(altpath):
+            beam_list.append(altpath)
+        # Failing that, try to parse the beam string as an analytic beam.
+        else:
+            find_type = [t in beam_model for t in AnalyticBeam.supported_types]
+            if np.sum(find_type) > 1:
+                raise ValueError("Ambiguous beam specification: {}".format(beam_model))
+            if np.sum(find_type) == 0:
+                raise ValueError("Undefined beam model: {}".format(beam_model))
+
+            beam_type = AnalyticBeam.supported_types[find_type.index(True)]
+
+            beam_model = "".join(beam_model.split())    # Remove whitespace
+            mod_list = beam_model.split(',')
+            inline_beam_opts = {}
+            for opt in mod_list:
+                if '=' not in opt:
+                    continue
+                k, v = opt.split('=')
+                inline_beam_opts[k] = v
+
+            # Gaussian beam requires either diameter or sigma
+            # Airy beam requires diameter
+
+            # Default to None for diameter and sigma.
+            # Values in the "beam_paths" override globally-defined options.
+            beam_opts = {'diameter': None, 'sigma': None}
+            for opt in beam_opts.keys():
+                val = telconfig.get(opt, None)
+                val = inline_beam_opts.get(opt, val)
+                beam_opts[opt] = val
+
+            diameter = beam_opts['diameter']
+            sigma = beam_opts['sigma']
+
+            if beam_type == 'uniform':
+                beam_model = 'analytic_uniform'
+
+            if beam_type == 'gaussian':
+                if diameter is not None:
+                    beam_model = '_'.join(['analytic_gaussian', 'diam', str(diameter)])
+                elif sigma is not None:
+                    beam_model = '_'.join(['analytic_gaussian', 'sig', str(sigma)])
+                else:
+                    raise KeyError("Missing shape parameter for gaussian beam (diameter or sigma).")
+            if beam_type == 'airy':
+                if diameter is not None:
+                    beam_model = '_'.join(['analytic_airy', 'diam', str(diameter)])
+                else:
+                    raise KeyError("Missing diameter for airy beam.")
+
+            beam_list.append(beam_model)
+    return beam_list
+
+
 def parse_telescope_params(tele_params, config_path=''):
     """
     Parse the "telescope" section of obsparam.
 
-    Args:
-        tele_params: Dictionary of telescope parameters
-            See pyuvsim documentation for allowable keys.
-            https://pyuvsim.readthedocs.io/en/latest/parameter_files.html#telescope-configuration
-        config_path: str
-            path to directory holding configuration and layout files.
+    Parameters
+    ----------
+    tele_params : dict
+        Telescope parameters
+        See pyuvsim documentation for allowable keys.
+        https://pyuvsim.readthedocs.io/en/latest/parameter_files.html#telescope-configuration
+    config_path : str
+        Path to directory holding configuration and layout files.
 
-    Returns:
-        param_dict : dict
-            * `Nants_data`: Number of antennas
-            * `Nants_telescope`: Number of antennas
-            *  `antenna_names`: list of antenna names
-            * `antenna_numbers`: corresponding list of antenna numbers
-            * `antenna_positions`: Array of ECEF antenna positions
-            * `telescope_location`: ECEF array center location
-            * `telescope_location_lat_lon_alt`: Lat Lon Alt array center location
-            * `telescope_config_file`: Path to configuration yaml file
-            * `antenna_location_file`: Path to csv layout file
-            * `telescope_name`: observatory name
-
-        beam_list : list of :class:`pyuvdata.UVBeam` or `pyuvsim.AnalyticBeam`
-            Beam models in the configuration.
-        beam_dict : dict
-            Antenna numbers to beam indices
+    Returns
+    -------
+    param_dict : dict
+        Parameters related to the telescope and antenna layout, to be included in the
+        UVData object.
+
+        * `Nants_data`: Number of antennas
+        * `Nants_telescope`: Number of antennas
+        *  `antenna_names`: list of antenna names
+        * `antenna_numbers`: corresponding list of antenna numbers
+        * `antenna_positions`: Array of ECEF antenna positions
+        * `telescope_location`: ECEF array center location
+        * `telescope_location_lat_lon_alt`: Lat Lon Alt array center location
+        * `telescope_config_file`: Path to configuration yaml file
+        * `antenna_location_file`: Path to csv layout file
+        * `telescope_name`: observatory name
+    beam_list : list of str
+        Beam models in the configuration.
+        The strings provide either paths to beamfits files or the specifications to make
+        a :class:`pyuvsim.AnalyticBeam`.
+    beam_dict : dict
+        Antenna numbers to beam indices
     """
     tele_params = copy.deepcopy(tele_params)
     # check for telescope config
@@ -742,41 +815,11 @@ def parse_telescope_params(tele_params, config_path=''):
     beam_dict = {}
 
     for beamID in np.unique(beam_ids):
-        beam_model = telconfig['beam_paths'][beamID]
         which_ants = antnames[np.where(beam_ids == beamID)]
         for a in which_ants:
             beam_dict[a] = beamID
-        if beam_model in AnalyticBeam.supported_types:
-            # Gaussian beam requires either diameter or sigma
-            # Airy beam requires diameter
-            if beam_model == 'uniform':
-                beam_model = 'analytic_uniform'
-            if beam_model == 'gaussian':
-                try:
-                    diam = telconfig['diameter']
-                    beam_model = '_'.join(['analytic', beam_model, 'diam', str(diam)])
-                except KeyError:
-                    try:
-                        sigma = telconfig['sigma']
-                        beam_model = '_'.join(['analytic', beam_model, 'sig', str(sigma)])
-                    except KeyError:
-                        raise KeyError(
-                            "Missing shape parameter for gaussian beam (diameter or sigma)."
-                        )
-            if beam_model == 'airy':
-                try:
-                    diam = telconfig['diameter']
-                    beam_model = '_'.join(['analytic', beam_model, 'diam', str(diam)])
-                except KeyError:
-                    raise KeyError("Missing diameter for airy beam.")
-        else:
-            # If not analytic, it's a beamfits file path.
-            if not os.path.exists(beam_model):
-                path = os.path.join(SIM_DATA_PATH, beam_model)
-                beam_model = path
-                if not os.path.exists(path):
-                    raise OSError("Could not find file " + beam_model)
-        beam_list.append(beam_model)
+
+    beam_list = _construct_beam_list(np.unique(beam_ids), telconfig)
 
     return return_dict, beam_list, beam_dict
 
@@ -1187,14 +1230,13 @@ def initialize_uvdata_from_params(obs_params):
                 bls = ast.literal_eval(bls)
                 select_params['bls'] = bls
         if len(select_params) > 0:
-            select_params['metadata_only'] = True
             uv_obj.select(**select_params)
 
         if no_autos:
-            uv_obj.select(ant_str='cross', metadata_only=True)
+            uv_obj.select(ant_str='cross')
 
         if redundant_threshold is not None:
-            uv_obj.compress_by_redundancy(tol=redundant_threshold, metadata_only=True)
+            uv_obj.compress_by_redundancy(tol=redundant_threshold)
 
     return uv_obj, beam_list, beam_dict
 


Test patch:
diff --git a/pyuvsim/tests/conftest.py b/pyuvsim/tests/conftest.py
index 1553958..1eb0dda 100644
--- a/pyuvsim/tests/conftest.py
+++ b/pyuvsim/tests/conftest.py
@@ -7,6 +7,7 @@ from __future__ import absolute_import, division, print_function
 
 import os
 import shutil
+import warnings
 
 import pytest
 import six.moves.urllib as urllib
@@ -41,3 +42,9 @@ def setup_and_teardown_package():
     # clean up the test directory after
     if os.path.exists(testdir):
         shutil.rmtree(testdir)
+
+
+@pytest.fixture(autouse=True)
+def ignore_center_deprecation():
+    warnings.filterwarnings('ignore', message='The default for the `center` keyword has changed.',
+                            category=DeprecationWarning)
diff --git a/pyuvsim/tests/test_mpi_uvsim.py b/pyuvsim/tests/test_mpi_uvsim.py
index fa3767c..fd4c3eb 100644
--- a/pyuvsim/tests/test_mpi_uvsim.py
+++ b/pyuvsim/tests/test_mpi_uvsim.py
@@ -15,7 +15,6 @@ import time
 mpi4py.rc.initialize = False  # noqa
 import pytest
 from mpi4py import MPI
-import astropy
 
 from pyuvdata import UVData
 from pyuvdata.data import DATA_PATH
@@ -78,9 +77,8 @@ def test_run_paramfile_uvsim():
     tempfilename = params_dict['filing']['outfile_name']
 
     # This test obsparam file has "single_source.txt" as its catalog.
-    uvtest.checkWarnings(pyuvsim.uvsim.run_uvsim, [param_filename], nwarnings=1,
-                         message=['The default for the `center` keyword'],
-                         category=[DeprecationWarning])
+    pyuvsim.uvsim.run_uvsim(param_filename)
+
     uv_new_txt = UVData()
     uvtest.checkWarnings(uv_new_txt.read_uvfits, [tempfilename],
                          message='antenna_diameters is not set')
@@ -88,12 +86,7 @@ def test_run_paramfile_uvsim():
     os.remove(tempfilename)
 
     param_filename = os.path.join(SIM_DATA_PATH, 'test_config', 'param_1time_1src_testvot.yaml')
-    uvtest.checkWarnings(
-        pyuvsim.uvsim.run_uvsim, [param_filename],
-        nwarnings=11,
-        message=[SIM_DATA_PATH] * 10 + ['The default for the `center` keyword has changed'],
-        category=[astropy.io.votable.exceptions.W50] * 10 + [DeprecationWarning]
-    )
+    pyuvsim.uvsim.run_uvsim(param_filename)
 
     uv_new_vot = UVData()
     uvtest.checkWarnings(uv_new_vot.read_uvfits, [tempfilename], nwarnings=1,
diff --git a/pyuvsim/tests/test_simsetup.py b/pyuvsim/tests/test_simsetup.py
index 680bb28..d3ba5e7 100644
--- a/pyuvsim/tests/test_simsetup.py
+++ b/pyuvsim/tests/test_simsetup.py
@@ -8,7 +8,6 @@ import copy
 import os
 import shutil
 
-import astropy
 import numpy as np
 import pytest
 import pyuvdata.tests as uvtest
@@ -144,12 +143,7 @@ def test_catalog_from_params():
 def test_param_flux_cuts():
     # Check that min/max flux limits in test params work.
 
-    gleam_path = os.path.join(SIM_DATA_PATH, 'test_config', '..', 'gleam_50srcs.vot')
-    catalog, srclistname = uvtest.checkWarnings(
-        pyuvsim.simsetup.initialize_catalog_from_params, [gleam_param_file],
-        message=gleam_path, nwarnings=11,
-        category=[astropy.io.votable.exceptions.W27] + [astropy.io.votable.exceptions.W50] * 10
-    )
+    catalog, srclistname = pyuvsim.simsetup.initialize_catalog_from_params(gleam_param_file)
 
     catalog = pyuvsim.simsetup.array_to_skymodel(catalog)
     for sI in catalog.stokes[0, 0, :]:
@@ -159,9 +153,8 @@ def test_param_flux_cuts():
 # parametrize will loop over all the give values
 @pytest.mark.parametrize("config_num", list([0, 2]))
 def test_param_reader(config_num):
-    """
-        Part of test_param_reader
-    """
+
+    # Reading in various configuration files
 
     param_filename = param_filenames[config_num]
     hera_uv = UVData()
@@ -187,10 +180,7 @@ def test_param_reader(config_num):
     Ntasks = hera_uv.Nblts * hera_uv.Nfreqs
     taskiter = pyuvsim.uvdata_to_task_iter(range(Ntasks), hera_uv, sources,
                                            beam_list, beam_dict=beam_dict)
-    expected_uvtask_list = uvtest.checkWarnings(
-        list, [taskiter], message='The default for the `center` keyword has changed',
-        category=DeprecationWarning
-    )
+    expected_uvtask_list = list(taskiter)
 
     # Check error conditions:
     if config_num == 0:
@@ -242,7 +232,7 @@ def test_param_reader(config_num):
         params_bad['telescope']['telescope_config_name'] = os.path.join(
             SIM_DATA_PATH, 'test_config', '28m_triangle_10time_10chan_nofile.yaml'
         )
-        simtest.assert_raises_message(OSError, 'Could not find file',
+        simtest.assert_raises_message(ValueError, 'Undefined beam model',
                                       pyuvsim.initialize_uvdata_from_params, params_bad)
 
     # Check default configuration
@@ -268,10 +258,7 @@ def test_param_reader(config_num):
     taskiter = pyuvsim.uvdata_to_task_iter(
         range(Ntasks), hera_uv, sources, beam_list, beam_dict=beam_dict
     )
-    uvtask_list = uvtest.checkWarnings(
-        list, [taskiter], message='The default for the `center` keyword has changed',
-        category=DeprecationWarning
-    )
+    uvtask_list = list(taskiter)
 
     # Tasks are not ordered in UVTask lists, so need to sort them.
     uvtask_list = sorted(uvtask_list)
@@ -535,7 +522,7 @@ def test_param_select_cross():
     # test only keeping cross pols
     param_dict['select'] = {'ant_str': 'cross'}
     uv_obj_cross, new_beam_list, new_beam_dict = pyuvsim.initialize_uvdata_from_params(param_dict)
-    uv_obj_cross2 = uv_obj_full.select(ant_str='cross', inplace=False, metadata_only=True)
+    uv_obj_cross2 = uv_obj_full.select(ant_str='cross', inplace=False)
 
     assert uv_obj_cross == uv_obj_cross2
 
@@ -550,7 +537,7 @@ def test_param_select_bls():
     uv_obj_bls, new_beam_list, new_beam_dict = pyuvsim.initialize_uvdata_from_params(param_dict)
 
     uv_obj_bls2 = uv_obj_full.select(
-        bls=[(40, 41), (42, 43), (44, 45)], inplace=False, metadata_only=True
+        bls=[(40, 41), (42, 43), (44, 45)], inplace=False
     )
     uv_obj_bls.history, uv_obj_bls2.history = '', ''
     assert uv_obj_bls == uv_obj_bls2
@@ -568,7 +555,7 @@ def test_param_select_redundant():
     # test only keeping one baseline per redundant group
     param_dict['select'] = {'redundant_threshold': 0.1}
     uv_obj_red, new_beam_list, new_beam_dict = pyuvsim.initialize_uvdata_from_params(param_dict)
-    uv_obj_red2 = uv_obj_full.compress_by_redundancy(tol=0.1, inplace=False, metadata_only=True)
+    uv_obj_red2 = uv_obj_full.compress_by_redundancy(tol=0.1, inplace=False)
     uv_obj_red.history, uv_obj_red2.history = '', ''
 
     assert uv_obj_red == uv_obj_red2
@@ -799,7 +786,7 @@ def test_point_catalog_reader():
 def test_flux_cuts():
     uv_in, beam_list, beam_dict = pyuvsim.simsetup.initialize_uvdata_from_params(manytimes_config)
     Nsrcs = 20
-    uv_in.select(times=np.unique(uv_in.time_array)[:50], bls=[(0, 1)], metadata_only=True)
+    uv_in.select(times=np.unique(uv_in.time_array)[:50], bls=[(0, 1)])
 
     dt = np.format_parser(
         ['U10', 'f8', 'f8', 'f8', 'f8'],
@@ -869,22 +856,15 @@ def test_circumpolar_nonrising():
 
 
 def test_read_gleam():
-    sourcelist = uvtest.checkWarnings(
-        pyuvsim.simsetup.read_votable_catalog, [GLEAM_vot],
-        message=GLEAM_vot, nwarnings=11,
-        category=[astropy.io.votable.exceptions.W27] + [astropy.io.votable.exceptions.W50] * 10
-    )
+    sourcelist = pyuvsim.simsetup.read_votable_catalog(GLEAM_vot)
 
     assert sourcelist.Ncomponents == 50
 
     # Check cuts
     source_select_kwds = {'min_flux': 1.0}
-    catalog = uvtest.checkWarnings(
-        pyuvsim.simsetup.read_votable_catalog, [GLEAM_vot],
-        dict(source_select_kwds=source_select_kwds, return_table=True),
-        message=GLEAM_vot, nwarnings=11,
-        category=[astropy.io.votable.exceptions.W27] + [astropy.io.votable.exceptions.W50] * 10
-    )
+    catalog = pyuvsim.simsetup.read_votable_catalog(GLEAM_vot,
+                                                    source_select_kwds=source_select_kwds,
+                                                    return_table=True)
 
     assert len(catalog) < sourcelist.Ncomponents
 
@@ -971,11 +951,7 @@ def test_keyword_param_loop():
 
 
 def test_array_to_skymodel_loop():
-    sky = uvtest.checkWarnings(
-        pyuvsim.simsetup.read_votable_catalog, [GLEAM_vot],
-        message=GLEAM_vot, nwarnings=11,
-        category=[astropy.io.votable.exceptions.W27] + [astropy.io.votable.exceptions.W50] * 10
-    )
+    sky = pyuvsim.simsetup.read_votable_catalog(GLEAM_vot)
     sky.ra = Angle(sky.ra.rad, 'rad')
     sky.dec = Angle(sky.dec.rad, 'rad')
     arr = pyuvsim.simsetup.skymodel_to_array(sky)
@@ -983,3 +959,54 @@ def test_array_to_skymodel_loop():
 
     assert np.allclose((sky.ra - sky2.ra).rad, 0.0)
     assert np.allclose((sky.dec - sky2.dec).rad, 0.0)
+
+
+def test_multi_analytic_beams():
+    # Test inline definitions of beam attributes.
+    # eg. (in beam configuration file):
+    #
+    # beam_paths:
+    #   0 : airy, diameter=14
+    #   1 : airy, diameter=20
+    #   2 : gaussian, sigma=0.5
+
+    par_fname = os.path.join(simtest.TESTDATA_PATH, 'test_teleconfig.yaml')
+    layout_fname = os.path.join(simtest.TESTDATA_PATH, 'test_layout_5ant.csv')
+
+    telescope_location = (-30.72152777777791, 21.428305555555557, 1073.0000000093132)
+    telescope_name = 'SKA'
+    beam_specs = {0: 'airy, diameter=14', 1: 'airy, diameter=20', 2: 'gaussian, sigma=0.5'}
+    expected = ['analytic_airy_diam_14', 'analytic_airy_diam_20', 'analytic_gaussian_sig_0.5']
+
+    Nants = 5
+    antenna_numbers = np.arange(Nants)
+    antpos = np.zeros((Nants, 3))
+    antpos[:, 0] = np.arange(Nants)
+    names = antenna_numbers.astype(str)
+    beam_ids = [0, 1, 2, 2, 0]
+    pyuvsim.simsetup._write_layout_csv(layout_fname, antpos, names, antenna_numbers, beam_ids)
+
+    # Write tele config to file.
+    pdict = dict(telescope_location=str(telescope_location),
+                 telescope_name=telescope_name, beam_paths=beam_specs)
+    with open(par_fname, 'w') as yfile:
+        yaml.dump(pdict, yfile, default_flow_style=False)
+
+    # Check error condition:
+    ambig_beam_specs = copy.copy(beam_specs)
+    ambig_beam_specs[0] = 'airy, gaussian, diameter=14'
+    bad_pdict = copy.copy(pdict)
+    bad_pdict['beam_paths'] = ambig_beam_specs
+
+    simtest.assert_raises_message(ValueError, "Ambiguous beam specification",
+                                  pyuvsim.simsetup._construct_beam_list, beam_ids, bad_pdict)
+
+    param_dict = {'telescope_config_name': par_fname, 'array_layout': layout_fname}
+
+    pdict, beam_list, beam_dict = pyuvsim.simsetup.parse_telescope_params(
+        param_dict, simtest.TESTDATA_PATH)
+
+    for i, nm in enumerate(names):
+        bid = beam_ids[i]
+        assert beam_dict[nm] == bid
+        assert beam_list[bid] == expected[bid]
diff --git a/pyuvsim/tests/test_uvsim.py b/pyuvsim/tests/test_uvsim.py
index 4ffea32..d880e2c 100644
--- a/pyuvsim/tests/test_uvsim.py
+++ b/pyuvsim/tests/test_uvsim.py
@@ -339,7 +339,7 @@ def test_offzenith_source_multibl_uvfits():
     # get antennas positions into ENU
     antpos, ants = uvtest.checkWarnings(
         hera_uv.get_ENU_antpos, category=[DeprecationWarning],
-        message=['The default for the `center` keyword has changed',
+        message=['The default for the `center` ',
                  'The xyz array in ENU_from_ECEF is being interpreted as (Npts, 3)'],
         nwarnings=2
     )
@@ -417,10 +417,7 @@ def test_file_to_tasks():
     taskiter = pyuvsim.uvdata_to_task_iter(
         np.arange(Ntasks), hera_uv, sources, beam_list, beam_dict
     )
-    uvtask_list = uvtest.checkWarnings(
-        list, [taskiter], message=['The default for the `center` keyword has changed'],
-        category=DeprecationWarning
-    )
+    uvtask_list = list(taskiter)
 
     tlist = copy.deepcopy(uvtask_list)
     # Test task comparisons
@@ -497,10 +494,7 @@ def test_gather():
     beam_dict = dict(zip(hera_uv.antenna_names, [0] * hera_uv.Nants_data))
     taskiter = pyuvsim.uvdata_to_task_iter(np.arange(Ntasks), hera_uv, sources, beam_list,
                                            beam_dict)
-    uvtask_list = uvtest.checkWarnings(
-        list, [taskiter], message=['The default for the `center` keyword has changed'],
-        category=DeprecationWarning
-    )
+    uvtask_list = list(taskiter)
 
     uv_out = pyuvsim.simsetup._complete_uvdata(hera_uv, inplace=False)
     for task in uvtask_list:
@@ -544,10 +538,8 @@ def test_local_task_gen():
     taskiter = pyuvsim.uvdata_to_task_iter(
         np.arange(Ntasks), hera_uv, sources, beam_list, beam_dict
     )
-    uvtask_list = uvtest.checkWarnings(
-        list, [taskiter], message=['The default for the `center` keyword has changed'],
-        category=DeprecationWarning
-    )
+    uvtask_list = list(taskiter)
+
     uvtask_iter = pyuvsim.uvdata_to_task_iter(
         np.arange(Ntasks), hera_uv, copy.deepcopy(sources),
         copy.deepcopy(beam_list), beam_dict
@@ -680,10 +672,7 @@ def test_source_splitting():
         np.arange(Ntasks), hera_uv, sources, beam_list, beam_dict, Nsky_parts=Nsky_parts
     )
 
-    uvtask_list = uvtest.checkWarnings(
-        list, [taskiter], message='The default for the `center` keyword has changed.',
-        category=DeprecationWarning
-    )
+    uvtask_list = list(taskiter)
 
     assert pyuvsim.estimate_skymodel_memory_usage(partsize, 1) * Npus_node < mem_avail