def test_apply_bda(fake_data): """Generate test data and make sure that BDA is applied correctly.""" uvd = fake_data # define parameters max_decorr = 0.1 pre_fs_int_time = 0.1 * units.s corr_fov_angle = Angle(20.0, units.deg) max_time = 30 * units.s uvd2 = bda_tools.apply_bda( uvd, max_decorr, pre_fs_int_time, corr_fov_angle, max_time, ) # make sure that things are correct assert uvd2.Ntimes == 3 assert uvd2.Nbls == 3 assert uvd2.Nblts == 5 # baselines (0, 1) and (0, 2) were all averaged together bl1_inds, _, _ = uvd._key2inds((0, 1)) bl2_inds, _, _ = uvd2._key2inds((0, 1)) target = uvd2.data_array[bl2_inds] # multiply in factor of 3 to account for 3 time samples being averaged avg_data = 3.0 * np.average(uvd.data_array[bl1_inds], axis=0)[np.newaxis, :, :, :] # there are small differences to account for phasing considerations, but the # results are still "close" for the times and baseline lengths chosen assert np.allclose(target, avg_data, atol=1e-3) return
def test_apply_bda_non_increasing_error(fake_data): """Test error when times in object are not monotonically increasing.""" # mess with fake data uvd = fake_data uvd.time_array = uvd.time_array[::-1] max_decorr = 0.1 pre_fs_int_time = 0.1 * units.s corr_fov_angle = Angle(20.0, units.degree) max_time = 30 * units.s with pytest.raises(AssertionError) as cm: bda_tools.apply_bda( uvd, max_decorr, pre_fs_int_time, corr_fov_angle, max_time, ) assert str(cm.value).startswith( "times of uvdata object are not monotonically increasing") return
def test_apply_bda_max_decorr_error(): """Test error for supplying an invalid max_decorr value.""" # define parameters uvd = UVData() max_decorr = -0.1 pre_fs_int_time = 0.1 * units.s corr_fov_angle = Angle(20.0, units.degree) max_time = 30 * units.s corr_int_time = 2 * units.s # use a bad max_decorr value with pytest.raises(ValueError) as cm: bda_tools.apply_bda( uvd, max_decorr, pre_fs_int_time, corr_fov_angle, max_time, corr_int_time, ) assert str(cm.value).startswith("max_decorr must be between 0 and 1") return
def test_apply_bda_ind2_key_error(fake_data): """Test error when the length of ind2 is non-zero.""" # mess with fake data uvd = fake_data uvd.ant_1_array[1] = 1 uvd.ant_2_array[1] = 0 max_decorr = 0.1 pre_fs_int_time = 0.1 * units.s corr_fov_angle = Angle(20.0, units.degree) max_time = 30 * units.s corr_int_time = 2.0 * units.s with pytest.raises(AssertionError) as cm: bda_tools.apply_bda( uvd, max_decorr, pre_fs_int_time, corr_fov_angle, max_time, corr_int_time, ) assert str(cm.value).startswith("ind2 from _key2inds() is not 0--exiting") return
def test_apply_bda_phased_error(fake_data): """Test error when applying bda to phased data.""" # convert input data to phased uvd = fake_data uvd.phase_to_time(uvd.time_array[0]) max_decorr = 0.1 pre_fs_int_time = 0.1 * units.s corr_fov_angle = Angle(20.0, units.degree) max_time = 30 * units.s corr_int_time = 2.0 * units.s with pytest.raises(ValueError) as cm: bda_tools.apply_bda( uvd, max_decorr, pre_fs_int_time, corr_fov_angle, max_time, corr_int_time, ) assert str(cm.value).startswith( "UVData object must be in drift mode to apply BDA") return
def test_apply_bda_corr_int_time_bad_quantity_error(): """Test error for using an incompatible quantity for corr_int_time.""" # define parameters uvd = UVData() max_decorr = 0.1 pre_fs_int_time = 0.1 * units.s corr_fov_angle = Angle(20.0, units.degree) max_time = 30 * units.s corr_int_time = 2.0 * units.m # use a bad quantity for corr_int_time with pytest.raises(ValueError) as cm: bda_tools.apply_bda( uvd, max_decorr, pre_fs_int_time, corr_fov_angle, max_time, corr_int_time, ) assert str(cm.value).startswith( "corr_int_time must be a Quantity with units of time") return
def test_apply_bda_corr_int_time_float_error(): """Test error for using corr_int_time as a float.""" # define parameters uvd = UVData() max_decorr = 0.1 pre_fs_int_time = 0.1 * units.s corr_fov_angle = Angle(20.0, units.degree) max_time = 30 * units.s corr_int_time = 2.0 # use a plain float for corr_int_time with pytest.raises(ValueError) as cm: bda_tools.apply_bda( uvd, max_decorr, pre_fs_int_time, corr_fov_angle, max_time, corr_int_time, ) assert str( cm.value).startswith("corr_int_time must be an astropy.units.Quantity") return
def test_apply_bda_pre_fs_int_time_float_error(): """Test error for not using a Quantity for pre_fs_int_time.""" # define parameters uvd = UVData() max_decorr = 0.1 pre_fs_int_time = 0.1 corr_fov_angle = Angle(20.0, units.degree) max_time = 30 * units.s corr_int_time = 2 * units.s # pass in pre_fs_int_time as a float instead of a Quantity with pytest.raises(ValueError) as cm: bda_tools.apply_bda( uvd, max_decorr, pre_fs_int_time, corr_fov_angle, max_time, corr_int_time, ) assert str(cm.value).startswith( "pre_fs_int_time must be an astropy.units.Quantity") return
def test_apply_bda_non_angle_error(): """Test error for not using an Angle for corr_fov_angle.""" # define parameters uvd = UVData() max_decorr = 0.1 pre_fs_int_time = 0.1 * units.s corr_fov_angle = "foo" max_time = 30 * units.s corr_int_time = 2 * units.s # test using something besides an angle for corr_fov_angle with pytest.raises(ValueError) as cm: bda_tools.apply_bda( uvd, max_decorr, pre_fs_int_time, corr_fov_angle, max_time, corr_int_time, ) assert str(cm.value).startswith( "corr_fov_angle must be an Angle object from astropy.coordinates") return
def test_apply_bda_non_uvd_error(): """Test error for not using a UVData object.""" # define parameters uvd = "foo" max_decorr = 0.1 pre_fs_int_time = 0.1 * units.s corr_fov_angle = Angle(20.0, units.degree) max_time = 30 * units.s corr_int_time = 2 * units.s # test using something besides a UVData object with pytest.raises(ValueError) as cm: bda_tools.apply_bda( uvd, max_decorr, pre_fs_int_time, corr_fov_angle, max_time, corr_int_time, ) assert str(cm.value).startswith( "apply_bda must be passed a UVData object as its first argument") return
args = ap.parse_args() if os.path.exists(args.file_out) and args.overwrite is False: print("{} exists. Use --overwrite to overwrite the file.".format( args.file_out)) sys.exit(0) # check that output filetype is valid if args.filetype not in ("uvh5", "uvfits", "miriad"): print("filetype must be one of uvh5, uvfits, or miriad") sys.exit(0) # read in file uv = UVData() uv.read(args.file_in) # apply BDA pre_fs_int_time = args.pre_fs_int_time * units.s corr_fov_angle = Angle(args.corr_fov_angle, units.deg) max_time = args.max_time * units.s uv2 = bda_tools.apply_bda(uv, args.max_decorr, pre_fs_int_time, corr_fov_angle, max_time, args.corr_int_time) # write out file if args.filetype == "uvh5": uv2.write_uvh5(args.file_out, clobber=True) if args.filetype == "uvfits": uv2.write_uvfits(args.file_out, spoof_nonessential=True, force_phase=True) if args.filetype == "miriad": uv2.write_miriad(args.file_out, clobber=True)
def run(input, outfile, verbose, save_all, clobber): """Run a full simulation with systematics. """ if verbose: print("Loading configuration file...") # load in config with open(input, 'r') as fl: yaml_contents = yaml.load(fl.read(), Loader=yaml.FullLoader) # figure out whether or not to do BDA bda_params = yaml_contents.get("bda", {}) # make sure bda is installed if the user wants to do BDA if bda_params and bda is None: raise ImportError("You have defined BDA parameters but do not have " "bda installed. Please install bda to proceed.") if verbose: print("Checking validity of filing parameters...") # extract parameters for saving to disk filing_params = yaml_contents.get("filing", {}) # construct outfile name if not passed from command line if outfile is None: outfile = os.path.join(filing_params["outdir"], filing_params["outfile_name"]) # get the filing format fmt = filing_params.get("output_format", None) assert fmt is not None, \ "The output file format must be specified in the configuration file " \ "under the 'filing' section." # assume miriad files have the extension "uv"; others are same as name fmt_to_ext = {"miriad": "uv", "uvfits": "uvfits", "uvh5": "uvh5"} # make sure the output format is supported; only miriad, uvfits, uvh5 # are currently supported by UVData objects supported_fmts = tuple(fmt_to_ext.keys()) assert fmt in supported_fmts, \ "UVData objects currently only support writing to the following " \ "datatypes: {}".format(supported_fmts) # add appropriate extension if not specified already, but allow custom ext if os.path.splitext(outfile)[1] == '': outfile += ".%s" % fmt_to_ext[fmt] # add clobber to filing parameters if it's not already in there # also choose to clobber if told to do so from command line if filing_params.get("clobber", None) is None or clobber: filing_params["clobber"] = clobber if os.path.exists(outfile) and not filing_params['clobber']: print("Nothing to do: %s already exists and clobber=False" % outfile) return if verbose: print("Determining whether to use a default configuration...") # determine whether to use season defaults defaults = yaml_contents.get("defaults", {}) if defaults: # this assertion is made to keep the configuration file as neat as # possible; it is confusing to have a full configuration nested # inside another configuration assert isinstance(defaults["default_config"], str), \ "If a default configuration is set with the default_config " \ "option in the configuration YAML, then it must be specified " \ "by a string which is either an absolute path to a config " \ "file compatible with hera_sim.defaults, or one of the season " \ "configuration keywords." hera_sim.defaults.set(defaults["default_config"]) if verbose: print("Constructing Simulator object...") # extract instrument parameters if isinstance(yaml_contents["telescope"]["array_layout"], str): # assume it's an antenna layout csv antennas = _parse_layout_csv( yaml_contents["telescope"]["array_layout"]) else: # assume it's constructed using antpos and the YAML tag !antpos antennas = yaml_contents["telescope"]["array_layout"] instrument_params = {"antennas": antennas} for parameter in ( "freq", "time", ): for key, value in yaml_contents[parameter].items(): instrument_params[key] = value sim = hera_sim.Simulator(**instrument_params) # for this application, we only want the sky temperature model and # beam size; this will need to be updated in the future when the # defaults handling is changed, but we'll leave that to the version # 1 release config_params = {} # look for Tsky_mdl, omega_p, and integration_time; extract these # note that this may not be an exhaustive list for content in yaml_contents.values(): if "Tsky_mdl" in content.keys(): config_params["Tsky_mdl"] = content["Tsky_mdl"] if "omega_p" in content.keys(): config_params["omega_p"] = content["omega_p"] if "integration_time" in content.keys(): config_params["inttime"] = content["integration_time"] # warn the user if both defaults and configuration parameters specified if defaults and config_params: warnings.warn("You have chosen to use a default configuration in " "addition to listing configuration parameters. The " "configuration parameters will override any default " "parameters that show up in both places.") if config_params: hera_sim.defaults.set(config_params) if not config_params and not defaults: warnings.warn("You have specified neither defaults nor configuration " "parameters. This may result in the simulation erroring " "at some point.") if verbose: print("Extracting simulation parameters...") # extract the simulation parameters from the configuration file # the configuration file should only specify any particular parameter once # i.e. the same sky temperature model should be used throughout sim_params = {} sim_details = yaml_contents["simulation"] if verbose and save_all: print("Running simulation...") for component in sim_details["components"]: for content in yaml_contents.values(): if component in content.keys(): for model, params in content[component].items(): if model in sim_details["exclude"]: continue if save_all: # we need to do this piecemeal sim_params = {model: params} # make sure new component is returned sim_params[model]["ret_vis"] = True # make a copy of the Simulator object sim_copy = copy.deepcopy(sim) # write component vis to copy's data array vis = sim.run_sim(**sim_params)[0][1] sim_copy.data.data_array = vis # update the history to only note this component sim_copy.data.history = \ sim.data.history.replace(sim_copy.data.history, '') # update the filename base, ext = os.path.splitext(outfile) copy_out = '.'.join((base, model)) + ext # save the component sim_copy.write_data( copy_out, file_type=filing_params["output_format"], **filing_params['kwargs']) else: sim_params[model] = params continue if verbose and not save_all: print("Running simulation...") if not save_all: sim.run_sim(**sim_params) # if the user wants to do BDA, then apply BDA if bda_params: # convert corr_FoV_angle to an Angle object bda_params["corr_FoV_angle"] = Angle(bda_params["corr_FoV_angle"]) if verbose: print("Performing BDA...") sim.data = bda_tools.apply_bda(sim.data, **bda_params) # save the simulation # note that we may want to allow the functionality for the user to choose some # kwargs to pass to the write method if verbose: print("Writing simulation results to disk...") # before writing to disk, update the history to note the config file used sim.data.history += "\nSimulation from configuration file: {cfg}".format( cfg=input) sim.write_data(outfile, file_type=filing_params["output_format"], **filing_params["kwargs"]) if verbose: print("Simulation complete.") return