def test_LatLonAlt_from_XYZ(): """Test conversion from ECEF xyz to lat/lon/alt with reference values.""" out_latlonalt = uvutils.LatLonAlt_from_XYZ(ref_xyz) # Got reference by forcing http://www.oc.nps.edu/oc2902w/coord/llhxyz.htm # to give additional precision. assert np.allclose(ref_latlonalt, out_latlonalt, rtol=0, atol=1e-3) pytest.raises(ValueError, uvutils.LatLonAlt_from_XYZ, ref_latlonalt) # test passing multiple values xyz_mult = np.stack((np.array(ref_xyz), np.array(ref_xyz))) lat_vec, lon_vec, alt_vec = uvutils.LatLonAlt_from_XYZ(xyz_mult) assert np.allclose(ref_latlonalt, (lat_vec[1], lon_vec[1], alt_vec[1]), rtol=0, atol=1e-3) # check warning if array transposed uvtest.checkWarnings(uvutils.LatLonAlt_from_XYZ, [xyz_mult.T], message='The expected shape of ECEF xyz array', category=DeprecationWarning) # check warning if 3 x 3 array xyz_3 = np.stack((np.array(ref_xyz), np.array(ref_xyz), np.array(ref_xyz))) uvtest.checkWarnings(uvutils.LatLonAlt_from_XYZ, [xyz_3], message='The xyz array in LatLonAlt_from_XYZ is', category=DeprecationWarning) # check error if only 2 coordinates pytest.raises(ValueError, uvutils.LatLonAlt_from_XYZ, xyz_mult[:, 0:2]) # test error checking pytest.raises(ValueError, uvutils.LatLonAlt_from_XYZ, ref_xyz[0:1])
def test_phasing_funcs(): # these tests are based on a notebook where I tested against the mwa_tools phasing code ra_hrs = 12.1 dec_degs = -42.3 mjd = 55780.1 array_center_xyz = np.array([-2559454.08, 5095372.14, -2849057.18]) lat_lon_alt = uvutils.LatLonAlt_from_XYZ(array_center_xyz) obs_time = Time(mjd, format='mjd', location=(lat_lon_alt[1], lat_lon_alt[0])) icrs_coord = SkyCoord(ra=Angle(ra_hrs, unit='hr'), dec=Angle(dec_degs, unit='deg'), obstime=obs_time) gcrs_coord = icrs_coord.transform_to('gcrs') # in east/north/up frame (relative to array center) in meters: (Nants, 3) ants_enu = np.array([-101.94, 0156.41, 0001.24]) ant_xyz_abs = uvutils.ECEF_from_ENU(ants_enu, lat_lon_alt[0], lat_lon_alt[1], lat_lon_alt[2]) ant_xyz_rel_itrs = ant_xyz_abs - array_center_xyz ant_xyz_rel_rot = uvutils.rotECEF_from_ECEF(ant_xyz_rel_itrs, lat_lon_alt[1]) array_center_coord = SkyCoord(x=array_center_xyz[0] * units.m, y=array_center_xyz[1] * units.m, z=array_center_xyz[2] * units.m, frame='itrs', obstime=obs_time) itrs_coord = SkyCoord(x=ant_xyz_abs[0] * units.m, y=ant_xyz_abs[1] * units.m, z=ant_xyz_abs[2] * units.m, frame='itrs', obstime=obs_time) gcrs_array_center = array_center_coord.transform_to('gcrs') gcrs_from_itrs_coord = itrs_coord.transform_to('gcrs') gcrs_rel = (gcrs_from_itrs_coord.cartesian - gcrs_array_center.cartesian).get_xyz().T gcrs_uvw = uvutils.phase_uvw(gcrs_coord.ra.rad, gcrs_coord.dec.rad, gcrs_rel.value) mwa_tools_calcuvw_u = -97.122828 mwa_tools_calcuvw_v = 50.388281 mwa_tools_calcuvw_w = -151.27976 assert np.allclose(gcrs_uvw[0, 0], mwa_tools_calcuvw_u, atol=1e-3) assert np.allclose(gcrs_uvw[0, 1], mwa_tools_calcuvw_v, atol=1e-3) assert np.allclose(gcrs_uvw[0, 2], mwa_tools_calcuvw_w, atol=1e-3) # also test unphasing temp2 = uvutils.unphase_uvw(gcrs_coord.ra.rad, gcrs_coord.dec.rad, np.squeeze(gcrs_uvw)) assert np.allclose(gcrs_rel.value, temp2)
def test_reraise_context(): with pytest.raises(ValueError) as cm: try: uvutils.LatLonAlt_from_XYZ(ref_xyz[0:1]) except ValueError: uvutils._reraise_context('Add some info') assert 'Add some info: xyz values should be ECEF x, y, z coordinates in meters' in str( cm.value) with pytest.raises(ValueError) as cm: try: uvutils.LatLonAlt_from_XYZ(ref_xyz[0:1]) except ValueError: uvutils._reraise_context('Add some info %s', 'and then more') assert 'Add some info and then more: xyz values should be ECEF x, y, z coordinates in meters' in str( cm.value) with pytest.raises(EnvironmentError) as cm: try: raise EnvironmentError(1, 'some bad problem') except EnvironmentError: uvutils._reraise_context('Add some info') assert 'Add some info: some bad problem' in str(cm.value)
def test_mwa_ecef_conversion(): ''' Test based on comparing the antenna locations in a Cotter uvfits file to the antenna locations in MWA_tools. ''' test_data_file = os.path.join(DATA_PATH, 'mwa128_ant_layouts.npz') f = np.load(test_data_file) # From the STABXYZ table in a cotter-generated uvfits file, obsid = 1066666832 xyz = f['stabxyz'] # From the East/North/Height columns in a cotter-generated metafits file, obsid = 1066666832 enh = f['ENH'] # From a text file antenna_locations.txt in MWA_Tools/scripts txt_topo = f['txt_topo'] # From the unphased uvw coordinates of obsid 1066666832, positions relative to antenna 0 # these aren't used in the current test, but are interesting and might help with phasing diagnosis in the future uvw_topo = f['uvw_topo'] # Sky coordinates are flipped for uvw derived values uvw_topo = -uvw_topo uvw_topo += txt_topo[0] # transpose these arrays to get them into the right shape txt_topo = txt_topo.T uvw_topo = uvw_topo.T enh = enh.T # ARRAYX, ARRAYY, ARRAYZ in ECEF frame from Cotter file arrcent = f['arrcent'] lat, lon, alt = uvutils.LatLonAlt_from_XYZ(arrcent) # The STABXYZ coordinates are defined with X through the local meridian, # so rotate back to the prime meridian new_xyz = uvutils.ECEF_from_rotECEF(xyz.T, lon) # add in array center to get real ECEF ecef_xyz = new_xyz + arrcent enu = uvutils.ENU_from_ECEF(ecef_xyz.T, lat, lon, alt) nt.assert_true(np.allclose(enu, enh)) # test other direction of ECEF rotation rot_xyz = uvutils.rotECEF_from_ECEF(new_xyz, lon) nt.assert_true(np.allclose(rot_xyz.T, xyz))
Nprocs = int(os.environ['SLURM_CPUS_PER_TASK'])# * int(os.environ['SLURM_NTASKS']) elif 'Nprocs' in param_dict: Nprocs = int(param_dict['Nprocs']) else: Nprocs = 1 sjob_id = None if 'SLURM_JOB_ID' in os.environ: sjob_id = os.environ['SLURM_JOB_ID'] print("Nprocs: ", Nprocs) sys.stdout.flush() # --------------------------- # Observatory # --------------------------- lat, lon, alt = uvutils.LatLonAlt_from_XYZ(tele_dict['telescope_location']) antpos = tele_dict['antenna_positions'] enu = uvutils.ENU_from_ECEF(tele_dict['antenna_positions'] + tele_dict['telescope_location'], lat, lon, alt) anums = tele_dict['antenna_numbers'] antnames = tele_dict['antenna_names'] Nants = tele_dict['Nants_data'] uv_obj = UVData() uv_obj.telescope_location = tele_dict['telescope_location'] uv_obj.telescope_location_lat_lon_alt = (lat, lon, alt) uv_obj.telescope_location_lat_lon_alt_degrees = (np.degrees(lat), np.degrees(lon), alt) uv_obj.antenna_numbers = anums uv_obj.antenna_names = antnames uv_obj.antenna_positions = antpos uv_obj.Nants_telescope = Nants uv_obj.Ntimes = time_dict['Ntimes']
def setup_uvdata( array_layout=None, telescope_location=None, telescope_name=None, Nfreqs=None, start_freq=None, bandwidth=None, freq_array=None, Ntimes=None, time_cadence=None, start_time=None, time_array=None, bls=None, anchor_ant=None, antenna_nums=None, no_autos=True, pols=["xx"], make_full=False, redundancy=None, run_check=True, ): """ Setup a UVData object for simulating. Args: array_layout : str Filepath to array layout in ENU coordinates [meters] telescope_location : len-3 tuple Telescope location on Earth in LatLonAlt coordinates [deg, deg, meters] telescope_name : str Name of telescope Nfreqs : int Number of frequency channels start_freq : float Starting frequency [Hz] bandwidth : float Total frequency bandwidth of spectral window [Hz] freq_array : ndarray frequency array [Hz], cannot be specified if start_freq, Nfreqs or bandwidth is specified Ntimes : int Number of integration bins time_cadence : float Cadence of time bins [seconds] start_time : float Time of the first integration bin [Julian Date] time_array : ndarray time array [Julian Date], cannot be specified if start_time, Ntimes and time_cadence is specified bls : list List of antenna-pair tuples for baseline selection anchor_ant: int Selects baselines such that one of the pair is a specified antenna number redundancy: float Redundant baseline selection tolerance for selection antenna_nums : list List of antenna numbers to keep in array make_full : Generate the full UVData object, includes arrays of length Nblts. Default behavior creates an invalid UVData object, where baseline_array has length Nbls, etc. This is to avoid excessive memory usage up front when it's not necessary. Returns: UVData object with zeroed data_array """ # get antenna information tele_dict = parse_telescope_params( dict( array_layout=array_layout, telescope_location=telescope_location, telescope_name=telescope_name, )) lat, lon, alt = uvutils.LatLonAlt_from_XYZ(tele_dict["telescope_location"]) antpos = tele_dict["antenna_positions"] enu = uvutils.ENU_from_ECEF( tele_dict["antenna_positions"] + tele_dict["telescope_location"], lat, lon, alt) anums = tele_dict["antenna_numbers"] antnames = tele_dict["antenna_names"] Nants = tele_dict["Nants_data"] # setup object and fill in basic info uv_obj = UVData() uv_obj.telescope_location = tele_dict["telescope_location"] uv_obj.telescope_location_lat_lon_alt = (lat, lon, alt) uv_obj.telescope_location_lat_lon_alt_degrees = ( np.degrees(lat), np.degrees(lon), alt, ) uv_obj.antenna_numbers = anums uv_obj.antenna_names = antnames uv_obj.antenna_positions = antpos uv_obj.Nants_telescope = Nants # fill in frequency and time info: wait to fill in len-Nblts arrays until later if freq_array is not None: if Nfreqs is not None or start_freq is not None or bandwidth is not None: raise ValueError( "Cannot specify freq_array as well as Nfreqs, start_freq or bandwidth" ) if freq_array.ndim == 1: freq_array = freq_array.reshape(1, -1) Nfreqs = freq_array.size else: freq_dict = parse_frequency_params( dict(Nfreqs=Nfreqs, start_freq=start_freq, bandwidth=bandwidth)) freq_array = freq_dict["freq_array"] if time_array is not None: if Ntimes is not None or start_time is not None or time_cadence is not None: raise ValueError( "Cannot specify time_array as well as Ntimes, start_time or time_cadence" ) Ntimes = time_array.size else: time_dict = parse_time_params( dict(Ntimes=Ntimes, start_time=start_time, time_cadence=time_cadence)) time_array = time_dict["time_array"] uv_obj.freq_array = freq_array uv_obj.Nfreqs = Nfreqs uv_obj.Ntimes = Ntimes # fill in other attributes uv_obj.spw_array = np.array([0], dtype=int) uv_obj.Nspws = 1 uv_obj.polarization_array = np.array( [uvutils.polstr2num(pol) for pol in pols], dtype=int) uv_obj.Npols = uv_obj.polarization_array.size if uv_obj.Nfreqs > 1: uv_obj.channel_width = np.diff(uv_obj.freq_array[0])[0] else: uv_obj.channel_width = 1.0 uv_obj._set_drift() uv_obj.telescope_name = tele_dict["telescope_name"] uv_obj.instrument = "simulator" uv_obj.object_name = "zenith" uv_obj.vis_units = "Jy" uv_obj.history = "" if redundancy is not None: red_tol = redundancy reds, vec_bin_centers, lengths = uvutils.get_antenna_redundancies( anums, enu, tol=red_tol, include_autos=not bool(no_autos)) bls = [r[0] for r in reds] bls = [uvutils.baseline_to_antnums(bl_ind, Nants) for bl_ind in bls] # Setup array and implement antenna/baseline selections. bl_array = [] _bls = [(a1, a2) for a1 in anums for a2 in anums if a1 <= a2] if bls is not None: if isinstance(bls, str): bls = ast.literal_eval(bls) bls = [bl for bl in _bls if bl in bls] else: bls = _bls if anchor_ant is not None: bls = [bl for bl in bls if anchor_ant in bl] if bool(no_autos): bls = [bl for bl in bls if bl[0] != bl[1]] if antenna_nums is not None: if isinstance(antenna_nums, str): antenna_nums = ast.literal_eval(antenna_nums) if isinstance(antenna_nums, int): antenna_nums = [antenna_nums] bls = [(a1, a2) for (a1, a2) in bls if a1 in antenna_nums or a2 in antenna_nums] bls = sorted(bls) for (a1, a2) in bls: bl_array.append(uvutils.antnums_to_baseline(a1, a2, 1)) bl_array = np.asarray(bl_array) print("Nbls: {}".format(bl_array.size)) if bl_array.size == 0: raise ValueError("No baselines selected.") uv_obj.time_array = time_array # Keep length Ntimes uv_obj.baseline_array = bl_array # Length Nbls if make_full: uv_obj = complete_uvdata(uv_obj, run_check=run_check) return uv_obj
def write_vis(fname, data, lst_array, freq_array, antpos, time_array=None, flags=None, nsamples=None, filetype='miriad', write_file=True, outdir="./", overwrite=False, verbose=True, history=" ", return_uvd=False, longitude=21.42830, start_jd=None, instrument="HERA", telescope_name="HERA", object_name='EOR', vis_units='uncalib', dec=-30.72152, telescope_location=np.array( [5109325.85521063, 2005235.09142983, -3239928.42475395]), integration_time=None, **kwargs): """ Take DataContainer dictionary, export to UVData object and write to file. See pyuvdata.UVdata documentation for more info on these attributes. Parameters: ----------- fname : type=str, output filename of visibliity data data : type=DataContainer, holds complex visibility data. lst_array : type=float ndarray, contains unique LST time bins [radians] of data (center of integration). freq_array : type=ndarray, contains frequency bins of data [Hz]. antpos : type=dictionary, antenna position dictionary. keys are antenna integers and values are position vectors in meters in ENU (TOPO) frame. time_array : type=ndarray, contains unique Julian Date time bins of data (center of integration). flags : type=DataContainer, holds data flags, matching data in shape. nsamples : type=DataContainer, holds number of points averaged into each bin in data (if applicable). filetype : type=str, filetype to write-out, options=['miriad']. write_file : type=boolean, write UVData to file if True. outdir : type=str, output directory for output file. overwrite : type=boolean, if True, overwrite output files. verbose : type=boolean, if True, report feedback to stdout. history : type=str, history string for UVData object return_uvd : type=boolean, if True return UVData instance. longitude : type=float, longitude of observer in degrees East start_jd : type=float, starting integer Julian Date of time_array if time_array is None. instrument : type=str, instrument name. telescope_name : type=str, telescope name. object_name : type=str, observing object name. vis_unit : type=str, visibility units. dec : type=float, declination of observer in degrees North. telescope_location : type=ndarray, telescope location in xyz in ITRF (earth-centered frame). integration_time : type=float, integration duration in seconds for data_array. This does not necessarily have to be equal to the diff(time_array): for the case of LST-binning, this is not the duration of the LST-bin but the integration time of the pre-binned data. kwargs : type=dictionary, additional parameters to set in UVData object. Output: ------- if return_uvd: return UVData instance """ ## configure UVData parameters # get pols pols = np.unique(map(lambda k: k[-1], data.keys())) Npols = len(pols) polarization_array = np.array(map(lambda p: polstr2num[p], pols)) # get times if time_array is None: if start_jd is None: raise AttributeError( "if time_array is not fed, start_jd must be fed") time_array = hc.utils.LST2JD(lst_array, start_jd, longitude=longitude) Ntimes = len(time_array) if integration_time is None: integration_time = np.median(np.diff(time_array)) * 24 * 3600. # get freqs Nfreqs = len(freq_array) channel_width = np.median(np.diff(freq_array)) freq_array = freq_array.reshape(1, -1) spw_array = np.array([0]) Nspws = 1 # get baselines keys bls = sorted(data.bls()) Nbls = len(bls) Nblts = Nbls * Ntimes # reconfigure time_array and lst_array time_array = np.repeat(time_array[np.newaxis], Nbls, axis=0).ravel() lst_array = np.repeat(lst_array[np.newaxis], Nbls, axis=0).ravel() # get data array data_array = np.moveaxis( map(lambda p: map(lambda bl: data[str(p)][bl], bls), pols), 0, -1) # resort time and baseline axes data_array = data_array.reshape(Nblts, 1, Nfreqs, Npols) if nsamples is None: nsample_array = np.ones_like(data_array, np.float) else: nsample_array = np.moveaxis( map(lambda p: map(lambda bl: nsamples[str(p)][bl], bls), pols), 0, -1) nsample_array = nsample_array.reshape(Nblts, 1, Nfreqs, Npols) # flags if flags is None: flag_array = np.zeros_like(data_array, np.float).astype(np.bool) else: flag_array = np.moveaxis( map( lambda p: map(lambda bl: flags[str(p)][bl].astype(np.bool), bls ), pols), 0, -1) flag_array = flag_array.reshape(Nblts, 1, Nfreqs, Npols) # configure baselines bls = np.repeat(np.array(bls), Ntimes, axis=0) # get ant_1_array, ant_2_array ant_1_array = bls[:, 0] ant_2_array = bls[:, 1] # get baseline array baseline_array = 2048 * (ant_1_array + 1) + (ant_2_array + 1) + 2**16 # get antennas in data data_ants = np.unique(np.concatenate([ant_1_array, ant_2_array])) Nants_data = len(data_ants) # get telescope ants antenna_numbers = np.unique(antpos.keys()) Nants_telescope = len(antenna_numbers) antenna_names = map(lambda a: "HH{}".format(a), antenna_numbers) # set uvw assuming drift phase i.e. phase center is zenith uvw_array = np.array( [antpos[k[1]] - antpos[k[0]] for k in zip(ant_1_array, ant_2_array)]) # get antenna positions in ITRF frame tel_lat_lon_alt = uvutils.LatLonAlt_from_XYZ(telescope_location) antenna_positions = np.array(map(lambda k: antpos[k], antenna_numbers)) antenna_positions = uvutils.ECEF_from_ENU( antenna_positions.T, *tel_lat_lon_alt).T - telescope_location # get zenith location: can only write drift phase phase_type = 'drift' zenith_dec_degrees = np.ones_like(baseline_array) * dec zenith_ra_degrees = hc.utils.JD2RA(time_array, longitude) zenith_dec = zenith_dec_degrees * np.pi / 180 zenith_ra = zenith_ra_degrees * np.pi / 180 # instantiate object uvd = UVData() # assign parameters params = [ 'Nants_data', 'Nants_telescope', 'Nbls', 'Nblts', 'Nfreqs', 'Npols', 'Nspws', 'Ntimes', 'ant_1_array', 'ant_2_array', 'antenna_names', 'antenna_numbers', 'baseline_array', 'channel_width', 'data_array', 'flag_array', 'freq_array', 'history', 'instrument', 'integration_time', 'lst_array', 'nsample_array', 'object_name', 'phase_type', 'polarization_array', 'spw_array', 'telescope_location', 'telescope_name', 'time_array', 'uvw_array', 'vis_units', 'antenna_positions', 'zenith_dec', 'zenith_ra' ] local_params = locals() # overwrite paramters by kwargs local_params.update(kwargs) # set parameters in uvd for p in params: uvd.__setattr__(p, local_params[p]) # write to file if write_file: if filetype == 'miriad': # check output fname = os.path.join(outdir, fname) if os.path.exists(fname) and overwrite is False: if verbose: print("{} exists, not overwriting".format(fname)) else: if verbose: print("saving {}".format(fname)) uvd.write_miriad(fname, clobber=True) else: raise AttributeError( "didn't recognize filetype: {}".format(filetype)) if return_uvd: return uvd