Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Require passing parameters by name not position in many methods and functions #1333

Merged
merged 12 commits into from
Oct 16, 2023
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ All notable changes to this project will be documented in this file.
time for each time range or the time_array (if there's a time_array and no time_range).

### Changed
- Require keyword arguments rather than allowing for passing arguments by position for
functions and methods with many parameters.
- Only one of `time_array` and `time_range` (and similarly `lst_array` and `lst_range`)
can be set on a UVCal object.
- If `time_range` is set it must be 2D with a shape of (Ntimes, 2) where the first axis
Expand Down
2 changes: 1 addition & 1 deletion docs/uvcal_tutorial.rst
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ data-like arrays as well, filled with zeros.
>>> from pyuvdata.data import DATA_PATH
>>> uvd_file = os.path.join(DATA_PATH, "zen.2458098.45361.HH.uvh5_downselected")
>>> uvd = UVData.from_file(uvd_file, file_type="uvh5", use_future_array_shapes=True)
>>> uvc = UVCal.initialize_from_uvdata(uvd, "multiply", "redundant")
>>> uvc = UVCal.initialize_from_uvdata(uvd, gain_convention="multiply", cal_style="redundant")
>>> print(uvc.ant_array)
[ 0 1 11 12 13 23 24 25]

Expand Down
35 changes: 24 additions & 11 deletions docs/uvdata_tutorial.rst
Original file line number Diff line number Diff line change
Expand Up @@ -601,7 +601,7 @@ Phasing/unphasing data
>>> # center, though it does not need to be unique. We are specifying that the type
>>> # here is "sidereal", which means that the position is represented by a fixed set
>>> # of coordinates in a sidereal coordinate frame (e.g., ICRS, FK5, etc).
>>> uvd.phase(5.23368, 0.710940, epoch="J2000", cat_name='target1', cat_type="sidereal")
>>> uvd.phase(lon=5.23368, lat=0.710940, epoch="J2000", cat_name='target1', cat_type="sidereal")
>>> uvd.print_phase_center_info()
ID Cat Entry Type Az/Lon/RA El/Lat/Dec Frame Epoch
# Name hours deg
Expand All @@ -623,7 +623,7 @@ Phasing/unphasing data
>>> # You can also now phase to "ephem" objects, which
>>> # move with time, e.g. solar system bodies. The phase method has a `lookup_name`
>>> # option which, if set to true, will allow you to search JPL-Horizons for coords
>>> uvd.phase(0, 0, epoch="J2000", cat_name="Sun", lookup_name=True)
>>> uvd.phase(lon=0, lat=0, epoch="J2000", cat_name="Sun", lookup_name=True)
>>> uvd.print_phase_center_info()
ID Cat Entry Type Az/Lon/RA El/Lat/Dec Frame Epoch Ephem Range Dist V_rad
# Name hours deg Start-MJD End-MJD pc km/s
Expand All @@ -640,7 +640,7 @@ Phasing/unphasing data
>>> # used to be designated with phase_type="drift" -- in that it is still phased and
>>> # can be to any azimuth and elevation, not just zenith). Note that we need to
>>> # supply `phase_frame` as "altaz", since driftscans are always in that frame.
>>> uvd.phase(0, pi/2, cat_name="zenith", phase_frame='altaz', cat_type="driftscan", select_mask=select_mask)
>>> uvd.phase(lon=0, lat=pi/2, cat_name="zenith", phase_frame='altaz', cat_type="driftscan", select_mask=select_mask)

>>> # Now when using `print_phase_center_info`, we'll see that there are multiple
>>> # phase centers present in the data
Expand Down Expand Up @@ -857,7 +857,8 @@ a) Getting antenna positions in topocentric frame in units of meters
>>> antpos = uvd.antenna_positions + uvd.telescope_location

>>> # convert to topocentric (East, North, Up or ENU) coords.
>>> antpos = utils.ENU_from_ECEF(antpos, *uvd.telescope_location_lat_lon_alt)
>>> lat, lon, alt = uvd.telescope_location_lat_lon_alt
>>> antpos = utils.ENU_from_ECEF(antpos, latitude=lat, longitude=lon, altitude=alt)

UVData: Selecting data
----------------------
Expand Down Expand Up @@ -1523,13 +1524,25 @@ are written to the appropriate parts of the file on disk.
>>> data_array = 0.5 * uvd2.data_array
>>> flag_array = uvd2.flag_array
>>> nsample_array = uvd2.nsample_array
>>> uvd.write_uvh5_part(partfile, data_array, flag_array, nsample_array, freq_chans=freq_inds1)
>>> uvd.write_uvh5_part(
... partfile,
... data_array=data_array,
... flag_array=flag_array,
... nsample_array=nsample_array,
... freq_chans=freq_inds1
... )

>>> uvd2 = UVData.from_file(filename, freq_chans=freq_inds2, use_future_array_shapes=True)
>>> data_array = 2.0 * uvd2.data_array
>>> flag_array = uvd2.flag_array
>>> nsample_array = uvd2.nsample_array
>>> uvd.write_uvh5_part(partfile, data_array, flag_array, nsample_array, freq_chans=freq_inds2)
>>> uvd.write_uvh5_part(
... partfile,
... data_array=data_array,
... flag_array=flag_array,
... nsample_array=nsample_array,
... freq_chans=freq_inds2
... )


.. _uvdata_sorting_data:
Expand Down Expand Up @@ -1558,7 +1571,7 @@ various conventions (``'ant1<ant2'``, ``'ant2<ant1'``, ``'u<0'``, ``'u>0'``, ``'
>>> print(np.min(uvd.ant_2_array - uvd.ant_1_array) >= 0)
True

>>> uvd2.conjugate_bls(convention='u<0', use_enu=False)
>>> uvd2.conjugate_bls('u<0', use_enu=False)
>>> print(np.max(uvd2.uvw_array[:, 0]) <= 0)
True

Expand Down Expand Up @@ -1587,17 +1600,17 @@ an option to sort the auto visibilities before the cross visibilities (``autos_f

>>> # Explicity sorting by 'time' then 'baseline' gets the same result
>>> uvd2 = uvd.copy()
>>> uvd2.reorder_blts(order='time', minor_order='baseline')
>>> uvd2.reorder_blts('time', minor_order='baseline')
>>> print(uvd == uvd2)
True

>>> uvd.reorder_blts(order='ant1', minor_order='ant2')
>>> uvd.reorder_blts('ant1', minor_order='ant2')
>>> print(np.min(np.diff(uvd.ant_1_array)) >= 0)
True

>>> # You can also sort and conjugate in a single step for the purposes of comparing two objects
>>> uvd.reorder_blts(order='bda', conj_convention='ant1<ant2')
>>> uvd2.reorder_blts(order='bda', conj_convention='ant1<ant2')
>>> uvd.reorder_blts('bda', conj_convention='ant1<ant2')
>>> uvd2.reorder_blts('bda', conj_convention='ant1<ant2')
>>> print(uvd == uvd2)
True

Expand Down
20 changes: 13 additions & 7 deletions pyuvdata/parameter.py
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,7 @@ class UVParameter(object):
def __init__(
self,
name,
*,
required=True,
value=None,
spoof_val=None,
Expand Down Expand Up @@ -293,7 +294,7 @@ def __init__(

self.ignore_eq_none = ignore_eq_none and not required

def __eq__(self, other, silent=False):
def __eq__(self, other, *, silent=False):
"""
Test if classes match and values are within tolerances.

Expand Down Expand Up @@ -562,7 +563,7 @@ def __eq__(self, other, silent=False):

return True

def __ne__(self, other, silent=True):
def __ne__(self, other, *, silent=True):
"""
Test if classes do not match or values are not within tolerances.

Expand Down Expand Up @@ -900,6 +901,7 @@ class LocationParameter(UVParameter):
def __init__(
self,
name,
*,
required=True,
value=None,
spoof_val=None,
Expand Down Expand Up @@ -960,7 +962,10 @@ def set_lat_lon_alt(self, lat_lon_alt):
self.value = None
else:
self.value = utils.XYZ_from_LatLonAlt(
lat_lon_alt[0], lat_lon_alt[1], lat_lon_alt[2], frame=self.frame
latitude=lat_lon_alt[0],
longitude=lat_lon_alt[1],
altitude=lat_lon_alt[2],
frame=self.frame,
)

def lat_lon_alt_degrees(self):
Expand All @@ -987,9 +992,9 @@ def set_lat_lon_alt_degrees(self, lat_lon_alt_degree):
else:
latitude, longitude, altitude = lat_lon_alt_degree
self.value = utils.XYZ_from_LatLonAlt(
latitude * np.pi / 180.0,
longitude * np.pi / 180.0,
altitude,
latitude=latitude * np.pi / 180.0,
longitude=longitude * np.pi / 180.0,
altitude=altitude,
frame=self.frame,
)

Expand Down Expand Up @@ -1095,6 +1100,7 @@ class SkyCoordParameter(UVParameter):
def __init__(
self,
name,
*,
required=True,
value=None,
spoof_val=None,
Expand All @@ -1116,7 +1122,7 @@ def __init__(
tols=(0, radian_tol),
)

def __eq__(self, other, silent=False):
def __eq__(self, other, *, silent=False):
if not issubclass(self.value.__class__, SkyCoord) or not issubclass(
other.value.__class__, SkyCoord
):
Expand Down
Loading
Loading