diff --git a/CHANGELOG.md b/CHANGELOG.md index d288a79e..53832fa8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## [Unreleased] +## [1.4.0] - 2024-10-31 + ### Changed - Use new pyuvdata analytic beams and remove the analytic beams defined in this package. - Major restructuring of the BeamList object to use shared memory and remove the diff --git a/src/pyuvsim/data/bl_lite_mixed.yaml b/src/pyuvsim/data/bl_lite_mixed.yaml index b2645beb..ed36527c 100644 --- a/src/pyuvsim/data/bl_lite_mixed.yaml +++ b/src/pyuvsim/data/bl_lite_mixed.yaml @@ -1,23 +1,27 @@ beam_paths: - 0: hera.beamfits + 0: + filename: hera.beamfits 1: type: airy diameter: 16 2: type: gaussian sigma: 0.03 - 3: airy + 3: + type: airy + diameter: 12 4: type: gaussian diameter: 14 - 5: gaussian + 5: + type: gaussian + diameter: 12 6: filename: mwa_full_EE_test.h5 pixels_per_deg: 1 freq_range: [100.e+6, 200.e+6] 7: type: short_dipole -diameter: 12 spline_interp_opts: kx: 4 ky: 4 diff --git a/src/pyuvsim/simsetup.py b/src/pyuvsim/simsetup.py index 9d18b64e..179cd9da 100644 --- a/src/pyuvsim/simsetup.py +++ b/src/pyuvsim/simsetup.py @@ -102,14 +102,6 @@ def get_rank(): } -weird_beamfits_extension_warning = ( - "This beamfits file does not have a '.fits' or '.beamfits' extension, so " - "UVBeam does not recognize it as a beamfits file. Either change the file " - "extension or specify the beam_type. This is currently handled with a " - "try/except clause in pyuvsim, but this will become an error in version 1.4" -) - - def _parse_layout_csv(layout_csv): """ Interpret the layout csv file. @@ -902,7 +894,7 @@ def _sky_select_calc_rise_set(sky, source_params, telescope_lat_deg=None): def initialize_catalog_from_params( - obs_params, input_uv=None, filetype=None, return_catname=None + obs_params, input_uv=None, filetype=None, return_catname=False ): """ Make catalog from parameter file specifications. @@ -919,8 +911,7 @@ def initialize_catalog_from_params( One of ['skyh5', 'gleam', 'vot', 'text', 'fhd'] or None. If None, the code attempts to guess what the file type is. return_catname: bool - Return the catalog name. Defaults to True currently but will default to False - in version 1.4. + Return the catalog name. Default is False. Returns ------- @@ -930,14 +921,6 @@ def initialize_catalog_from_params( 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.", - DeprecationWarning, - ) - return_catname = True - if input_uv is not None and not isinstance(input_uv, UVData): raise TypeError("input_uv must be UVData object") @@ -1036,12 +1019,7 @@ def initialize_catalog_from_params( ) if detect_gleam and "spectral_type" not in source_params: - warnings.warn( - "No spectral_type specified for GLEAM, using 'flat'. In version 1.4 " - "this default will change to 'subband' to match pyradiosky's default.", - DeprecationWarning, - ) - read_params["spectral_type"] = "flat" + read_params["spectral_type"] = "subband" source_list_name = os.path.basename(catalog) sky = SkyModel.from_file(catalog, **read_params) @@ -1100,6 +1078,12 @@ def _construct_beam_list( Range of frequencies to keep in the beam object, shape (2,). Should include all frequencies in the simulation with a buffer to support interpolation. + + Returns + ------- + BeamList + The constructed beam list object. + """ beam_list = [] assert isinstance( @@ -1118,11 +1102,10 @@ def _construct_beam_list( # possible global shape options if "diameter" in telconfig or "sigma" in telconfig: - warnings.warn( + raise ValueError( "Beam shape options diameter and sigma should be specified per beamID " "in the 'beam_paths' section not as globals. For examples see the " - "parameter_files documentation. This will become an error in version 1.4", - DeprecationWarning, + "parameter_files documentation." ) select = telconfig.pop("select", {}) @@ -1145,9 +1128,10 @@ def _construct_beam_list( raise ValueError(f"beam_id {beam_id} is not listed in the telconfig.") beam_model = telconfig["beam_paths"][beam_id] - if not isinstance(beam_model, str | dict | AnalyticBeam | UVBeam): + if not isinstance(beam_model, dict | AnalyticBeam | UVBeam): raise ValueError( "Beam model is not properly specified in telescope config file." + f"It is specified as {beam_model}" ) if not isinstance(beam_model, AnalyticBeam | UVBeam) and not ( @@ -1157,9 +1141,8 @@ def _construct_beam_list( "Entries in 'beam_paths' should be specified using either the " "UVBeam or AnalyticBeam constructors or using a dict syntax for " "UVBeams. For examples see the parameter_files documentation. " - "Specifying simple strings will cause an error in version 1.4, " - "specifying analytic beam without the AnalyticBeam constructors " - "will cause an error in version 1.5", + "Specifying analytic beam without the AnalyticBeam constructors " + "will cause an error in version 1.6", DeprecationWarning, ) @@ -1174,20 +1157,10 @@ def _construct_beam_list( select["freq_chans"] = freq_chans beam_model.select(**select) beam_list.append(beam_model) - elif ( - isinstance(beam_model, str) - and beam_model not in analytic_beams - or isinstance(beam_model, dict) - and "filename" in beam_model - ): - if isinstance(beam_model, str): - # this is the path to a UVBeam readable file - beam_file = beam_model - read_kwargs = {} - else: - # this a UVBeam readable file with (possibly) some read kwargs - beam_file = beam_model.pop("filename") - read_kwargs = beam_model + elif isinstance(beam_model, dict) and "filename" in beam_model: + # this a UVBeam readable file with (possibly) some read kwargs + beam_file = beam_model.pop("filename") + read_kwargs = beam_model beam_file = _check_uvbeam_file(beam_file) uvb = UVBeam() @@ -1195,19 +1168,11 @@ def _construct_beam_list( if "freq_range" in select: read_kwargs["freq_range"] = select["freq_range"] - try: - uvb.read(beam_file, **read_kwargs) - except ValueError: - # If file type is not recognized, assume beamfits, - # which was originally the only option. - uvb.read_beamfits(beam_file, **read_kwargs) - warnings.warn(weird_beamfits_extension_warning, DeprecationWarning) + uvb.read(beam_file, **read_kwargs) beam_list.append(uvb) else: - if isinstance(beam_model, str): - beam_type = beam_model - elif "type" in beam_model: + if "type" in beam_model: beam_type = beam_model["type"] else: raise ValueError( @@ -1239,10 +1204,6 @@ def _construct_beam_list( for opt in shape_opts: shape_opts[opt] = this_beam_opts.get(opt) - if all(v is None for v in shape_opts.values()): - for opt in shape_opts: - shape_opts[opt] = telconfig.get(opt) - ab = analytic_beams[beam_type](**shape_opts) beam_list.append(ab) @@ -1919,7 +1880,7 @@ def set_ordering(uv_obj, param_dict, reorder_blt_kw): def initialize_uvdata_from_params( - obs_params, return_beams=None, reorder_blt_kw=None, check_kw=None + obs_params, return_beams=False, reorder_blt_kw=None, check_kw=None ): """ Construct a :class:`pyuvdata.UVData` object from parameters in a valid yaml file. @@ -1938,8 +1899,7 @@ def initialize_uvdata_from_params( Either an obs_param file name or a dictionary of parameters read in. Additional :class:`pyuvdata.UVData` parameters may be passed in through here. return_beams : bool - Option to return the beam_list and beam_dict. Currently defaults to True, but - will default to False starting in version 1.4. + Option to return the beam_list and beam_dict. Default is False. reorder_blt_kw : dict (optional) Deprecated. Use obs_param['ordering']['blt_order'] instead. Keyword arguments to send to the ``uvdata.reorder_blts`` method at the end of @@ -1961,14 +1921,6 @@ def initialize_uvdata_from_params( """ logger.info("Starting SimSetup") - if return_beams is None: - warnings.warn( - "The return_beams parameter currently defaults to True, but starting in" - "version 1.4 it will default to False.", - DeprecationWarning, - ) - return_beams = True - if reorder_blt_kw is not None: warnings.warn( "The reorder_blt_kw parameter is deprecated in favor of setting " diff --git a/tests/test_run.py b/tests/test_run.py index a7b797c3..bb860ff6 100644 --- a/tests/test_run.py +++ b/tests/test_run.py @@ -17,7 +17,6 @@ from astropy.time import Time from pyradiosky.utils import jy_to_ksr, stokes_to_coherency from pyuvdata import ShortDipoleBeam, UniformBeam, UVData -from pyuvdata.testing import check_warnings import pyuvsim from pyuvsim.data import DATA_PATH as SIM_DATA_PATH @@ -236,28 +235,14 @@ def test_run_paramdict_uvsim(rename_beamfits, tmp_path): shutil.copyfile(source_file, new_source_file) beamfits_file = os.path.join(SIM_DATA_PATH, "HERA_NicCST.beamfits") - new_beam_file = os.path.join(tmp_path, "test_config", "HERA_NicCST.uvbeam") + new_beam_file = os.path.join(tmp_path, "test_config", "HERA_NicCST.beamfits") shutil.copyfile(beamfits_file, new_beam_file) - # change the beam file name to .uvbeam - with open(new_telescope_param_file) as pfile: - tele_param_dict = yaml.safe_load(pfile) - tele_param_dict["beam_paths"][0] = {"filename": new_beam_file} - - with open(new_telescope_param_file, "w") as yfile: - yaml.dump(tele_param_dict, yfile, default_flow_style=False) - - warn_type = [DeprecationWarning] - msg = [pyuvsim.simsetup.weird_beamfits_extension_warning] - params = pyuvsim.simsetup._config_str_to_dict(new_param_file) else: - warn_type = None - msg = "" params = pyuvsim.simsetup._config_str_to_dict(param_file) - with check_warnings(warn_type, match=msg): - pyuvsim.run_uvsim(params, return_uv=True) + pyuvsim.run_uvsim(params, return_uv=True) @pytest.mark.parametrize("spectral_type", ["flat", "subband", "spectral_index"]) diff --git a/tests/test_simsetup.py b/tests/test_simsetup.py index b1fc8935..abbdf9a3 100644 --- a/tests/test_simsetup.py +++ b/tests/test_simsetup.py @@ -292,7 +292,7 @@ def test_mock_diffuse_maps(modname, modkwargs, hera_loc, apollo_loc, location): @pytest.mark.parametrize( ("horizon_buffer", "pass_time", "pass_array_loc", "pass_uv", "return_catname"), [ - (True, True, True, False, None), + (True, True, True, False, True), (False, False, False, True, False), (True, True, True, True, False), ], @@ -330,13 +330,6 @@ def test_initialize_catalog_from_params( warn_type += [UserWarning] warn_str += ["No array_location specified. Defaulting to the HERA site."] - if return_catname is None: - warn_type += [DeprecationWarning] - warn_str += [ - "The return_catname parameter currently defaults to True, but " - "starting in version 1.4 it will default to False." - ] - if len(warn_type) == 0: warn_type = None warn_str = "" @@ -346,8 +339,9 @@ def test_initialize_catalog_from_params( {"sources": source_dict}, input_uv=uv_use, return_catname=return_catname ) - if return_catname is not False: - catalog_uv = catalog_uv[0] + if return_catname: + catalog_uv, cat_name = catalog_uv + assert cat_name.startswith("mock") exp_cat, _ = simsetup.create_mock_catalog( uv_in.time_array[0], arrangement="zenith", array_location=hera_loc, Nsrcs=5 @@ -392,7 +386,7 @@ def test_initialize_catalog_from_params_errors( pytest.raises(err_type, match=err_msg), ): simsetup.initialize_catalog_from_params( - {"sources": source_dict}, input_uv=input_uv, return_catname=False + {"sources": source_dict}, input_uv=input_uv ) @@ -406,7 +400,7 @@ def test_vot_catalog(use_filetype): if use_filetype: filetype = "vot" vot_catalog = simsetup.initialize_catalog_from_params( - vot_param_filename, filetype=filetype, return_catname=False + vot_param_filename, filetype=filetype ) if use_filetype: @@ -415,7 +409,7 @@ def test_vot_catalog(use_filetype): SIM_DATA_PATH, "test_config", "param_1time_1src_testcat.yaml" ) txt_catalog = simsetup.initialize_catalog_from_params( - txt_param_filename, filetype=filetype, return_catname=False + txt_param_filename, filetype=filetype ) assert vot_catalog == txt_catalog @@ -426,15 +420,13 @@ def test_vot_catalog_errors(): SIM_DATA_PATH, "test_config", "param_1time_1src_testvot.yaml" ) with pytest.raises(ValueError, match="Invalid filetype. Filetype options are:"): - simsetup.initialize_catalog_from_params( - vot_param_filename, filetype="foo", return_catname=False - ) + simsetup.initialize_catalog_from_params(vot_param_filename, filetype="foo") @pytest.mark.parametrize(("flux_cut", "filetype"), [(True, "gleam"), (False, None)]) def test_gleam_catalog(filetype, flux_cut): if flux_cut: - expected_ncomp = 23 + expected_ncomp = 9 params_use = gleam_param_file else: expected_ncomp = 50 @@ -445,15 +437,9 @@ def test_gleam_catalog(filetype, flux_cut): param_dict["sources"].pop("max_flux") params_use = param_dict - warn_messages = [ - "No spectral_type specified for GLEAM, using 'flat'. In version 1.4 " - "this default will change to 'subband' to match pyradiosky's default." - ] - warnings = [DeprecationWarning] - with check_warnings(warnings, match=warn_messages): - gleam_catalog = simsetup.initialize_catalog_from_params( - params_use, filetype=filetype, return_catname=False - ) + gleam_catalog = simsetup.initialize_catalog_from_params( + params_use, filetype=filetype + ) assert gleam_catalog.Ncomponents == expected_ncomp @@ -487,7 +473,7 @@ def test_skyh5_catalog(use_filetype, yaml_filetype, tmp_path): if use_filetype: filetype = "skyh5" skyh5_catalog = simsetup.initialize_catalog_from_params( - param_filename, filetype=filetype, return_catname=False + param_filename, filetype=filetype ) assert skyh5_catalog == skyobj @@ -500,7 +486,7 @@ def test_healpix_catalog(): sky = SkyModel.from_file(path) params = {"sources": {"catalog": path}} - hpx_sky = simsetup.initialize_catalog_from_params(params, return_catname=False) + hpx_sky = simsetup.initialize_catalog_from_params(params) assert hpx_sky == sky @@ -513,9 +499,7 @@ def test_gleam_catalog_spectral_type(spectral_type): param_dict["sources"].pop("max_flux") param_dict["sources"]["spectral_type"] = spectral_type - gleam_catalog = simsetup.initialize_catalog_from_params( - param_dict, return_catname=False - ) + gleam_catalog = simsetup.initialize_catalog_from_params(param_dict) assert gleam_catalog.spectral_type == spectral_type assert gleam_catalog.Ncomponents == 50 @@ -574,10 +558,8 @@ def test_param_reader(telparam_in_obsparam, tmpdir): # Check default configuration with check_warnings( - [DeprecationWarning, DeprecationWarning, DeprecationWarning], + [DeprecationWarning, DeprecationWarning], match=[ - "The return_beams parameter currently defaults to True, but starting in" - "version 1.4 it will default to False.", "The reorder_blt_kw parameter is deprecated in favor of setting " "obs_param['ordering']['blt_order']. This will become an error in " "version 1.5", @@ -589,6 +571,7 @@ def test_param_reader(telparam_in_obsparam, tmpdir): new_param_file, reorder_blt_kw={"order": "time", "minor_order": "baseline"}, check_kw={"run_check_acceptability": True}, + return_beams=True, ) assert uv_obj.telescope.x_orientation == "east" if telparam_in_obsparam: @@ -1638,7 +1621,6 @@ def test_direct_fname(tmpdir): """ beam_paths: 0: 1.35 - diameter: 12 spline_interp_opts: kx: 4 ky: 4 @@ -1654,7 +1636,6 @@ def test_direct_fname(tmpdir): beam_paths: 0: diameter: 12 - diameter: 12 spline_interp_opts: kx: 4 ky: 4 @@ -1672,7 +1653,6 @@ def test_direct_fname(tmpdir): 0: type: unsupported_type diameter: 12 - diameter: 12 spline_interp_opts: kx: 4 ky: 4 @@ -1689,7 +1669,6 @@ def test_direct_fname(tmpdir): 0: !AnalyticBeam class: AiryBeam diameter: 12 - diameter: 12 spline_interp_opts: kx: 4 ky: 4 @@ -1700,32 +1679,50 @@ def test_direct_fname(tmpdir): 1, "beam_id 1 is not listed in the telconfig.", ), + ( + """ + beam_paths: + 0: !AnalyticBeam + class: AiryBeam + diameter: 12 + diameter: 12 + spline_interp_opts: + kx: 4 + ky: 4 + freq_interp_kind: 'cubic' + telescope_location: (-30.72152777777791, 21.428305555555557, 1073.0000000093132) + telescope_name: BLLITE + """, + 0, + "Beam shape options diameter and sigma should be specified per " + "beamID in the 'beam_paths' section not as globals. For examples " + "see the parameter_files documentation.", + ), ], ) def test_beamlist_init_errors(input_yaml, beam_id, err_msg): telconfig = yaml.safe_load(input_yaml) - warn_msg = [ - "Beam shape options diameter and sigma should be specified per " - "beamID in the 'beam_paths' section not as globals. For examples " - "see the parameter_files documentation. This will become an " - "error in version 1.4" - ] - if beam_id == 0 and not isinstance(telconfig["beam_paths"][0], float): + warn_msg = [] + if ("!AnalyticBeam" not in input_yaml) and not isinstance( + telconfig["beam_paths"][0], float + ): warn_msg += [ "Entries in 'beam_paths' should be specified using either the UVBeam " "or AnalyticBeam constructors or using a dict syntax for UVBeams. " "For examples see the parameter_files documentation. Specifying " - "simple strings will cause an error in version 1.4, specifying " "analytic beam without the AnalyticBeam constructors will cause an " - "error in version 1.5" + "error in version 1.6" ] + warn_type = DeprecationWarning + if len(warn_msg) == 0: + warn_type = None Nfreqs = 10 freqs = np.linspace(100, 130, Nfreqs) * 1e6 * units.Hz with ( - check_warnings(DeprecationWarning, match=warn_msg), + check_warnings(warn_type, match=warn_msg), pytest.raises(ValueError, match=err_msg), ): simsetup._construct_beam_list([beam_id], telconfig, freq_array=freqs) @@ -1748,27 +1745,21 @@ def test_beamlist_init_spline(): with check_warnings( DeprecationWarning, match=[ - "Beam shape options diameter and sigma should be specified per " - "beamID in the 'beam_paths' section not as globals. For examples see " - "the parameter_files documentation. This will become an error in " - "version 1.4" - ] - + [ "Entries in 'beam_paths' should be specified using either the UVBeam " "or AnalyticBeam constructors or using a dict syntax for UVBeams. " "For examples see the parameter_files documentation. Specifying " - "simple strings will cause an error in version 1.4, specifying " "analytic beam without the AnalyticBeam constructors will cause an " - "error in version 1.5" + "error in version 1.6" ] * 5, ): beam_list = simsetup._construct_beam_list(beam_ids, telconfig, freq_array=freqs) + assert isinstance(beam_list, pyuvsim.BeamList) assert beam_list.spline_interp_opts == {"kx": 2, "ky": 2} @pytest.mark.parametrize( - ("rename_beamfits", "pass_beam_type"), [(True, False), (True, True), (False, False)] + ("rename_beamfits", "pass_beam_type"), [(False, True), (True, True), (False, False)] ) def test_beamlist_init(rename_beamfits, pass_beam_type, tmp_path): telescope_config_name = os.path.join(SIM_DATA_PATH, "bl_lite_mixed.yaml") @@ -1781,9 +1772,9 @@ def test_beamlist_init(rename_beamfits, pass_beam_type, tmp_path): new_beam_file = os.path.join(tmp_path, "HERA_NicCST.uvbeam") shutil.copyfile(beamfits_file, new_beam_file) - telconfig["beam_paths"][0] = new_beam_file + telconfig["beam_paths"][0] = {"filename": new_beam_file} else: - telconfig["beam_paths"][0] = "HERA_NicCST.beamfits" + telconfig["beam_paths"][0] = {"filename": "HERA_NicCST.beamfits"} if pass_beam_type: beam_file = beamfits_file @@ -1794,36 +1785,20 @@ def test_beamlist_init(rename_beamfits, pass_beam_type, tmp_path): ) nbeams = len(telconfig["beam_paths"]) - entries_warnings = nbeams - 1 - if pass_beam_type: - entries_warnings -= 1 + entries_warnings = 6 warn_list = [ - "Beam shape options diameter and sigma should be specified per " - "beamID in the 'beam_paths' section not as globals. For examples see " - "the parameter_files documentation. This will become an error in " - "version 1.4" - ] + [ "Entries in 'beam_paths' should be specified using either the UVBeam " "or AnalyticBeam constructors or using a dict syntax for UVBeams. " "For examples see the parameter_files documentation. Specifying " - "simple strings will cause an error in version 1.4, specifying " "analytic beam without the AnalyticBeam constructors will cause an " - "error in version 1.5" + "error in version 1.6" ] * entries_warnings warn_types = DeprecationWarning Nfreqs = 10 freqs = np.linspace(100, 130, Nfreqs) * 1e6 * units.Hz - if rename_beamfits and not pass_beam_type: - warn_list.append( - "This beamfits file does not have a '.fits' or '.beamfits' extension, " - "so UVBeam does not recognize it as a beamfits file. Either change the " - "file extension or specify the beam_type. This is currently handled " - "with a try/except clause in pyuvsim, but this will become an error in " - "version 1.4" - ) with check_warnings(warn_types, match=warn_list): beam_list = simsetup._construct_beam_list( np.arange(nbeams), telconfig, freq_array=freqs @@ -1851,7 +1826,9 @@ def test_beamlist_init_freqrange(sel_type): with open(telescope_config_name) as yf: telconfig = yaml.safe_load(yf) - telconfig["beam_paths"][0] = os.path.join(SIM_DATA_PATH, "HERA_NicCST.beamfits") + telconfig["beam_paths"][0] = { + "filename": os.path.join(SIM_DATA_PATH, "HERA_NicCST.beamfits") + } Nfreqs = 10 freqs = np.linspace(120, 145, Nfreqs) * 1e6 * units.Hz @@ -1869,20 +1846,13 @@ def test_beamlist_init_freqrange(sel_type): with check_warnings( DeprecationWarning, match=[ - "Beam shape options diameter and sigma should be specified per " - "beamID in the 'beam_paths' section not as globals. For examples see " - "the parameter_files documentation. This will become an error in " - "version 1.4" - ] - + [ "Entries in 'beam_paths' should be specified using either the UVBeam " "or AnalyticBeam constructors or using a dict syntax for UVBeams. " "For examples see the parameter_files documentation. Specifying " - "simple strings will cause an error in version 1.4, specifying " "analytic beam without the AnalyticBeam constructors will cause an " - "error in version 1.5" + "error in version 1.6" ] - * 6, + * 5, ): beam_list = simsetup._construct_beam_list( np.arange(6), telconfig, freq_range=freq_range_pass, freq_array=freqs