diff --git a/ci/pyuvdata_linting.yml b/ci/pyuvdata_linting.yml index 17e3b4b90d..935553c02f 100644 --- a/ci/pyuvdata_linting.yml +++ b/ci/pyuvdata_linting.yml @@ -10,4 +10,5 @@ dependencies: - flake8-comprehensions - flake8-docstrings - flake8-pytest + - flake8-rst-docstrings - pep8-naming diff --git a/docs/developer_docs.rst b/docs/developer_docs.rst index 5cc7c5d0c2..c31ef2869b 100644 --- a/docs/developer_docs.rst +++ b/docs/developer_docs.rst @@ -36,39 +36,56 @@ specific code. The read and write methods on the user classes convert between the user classes and the file-specific classes automatically as needed, so users generally do not need to interact with these classes, but developers may need to. -.. autoclass:: pyuvdata.uvdata.uvfits.UVFITS +.. autoclass:: pyuvdata.uvdata.fhd.FHD :members: .. autoclass:: pyuvdata.uvdata.miriad.Miriad :members: -.. autoclass:: pyuvdata.uvdata.fhd.FHD +.. autoclass:: pyuvdata.uvdata.ms.MS :members: -.. autoclass:: pyuvdata.uvdata.ms.MS +.. autoclass:: pyuvdata.uvdata.mwa_corr_fits.MWACorrFITS :members: -.. autoclass:: pyuvdata.uvdata.uvh5.UVH5 +.. autoclass:: pyuvdata.uvdata.uvfits.UVFITS :members: -.. autoclass:: pyuvdata.uvcal.fhd_cal.FHDCal +.. autoclass:: pyuvdata.uvdata.uvh5.UVH5 :members: .. autoclass:: pyuvdata.uvcal.calfits.CALFITS :members: +.. autoclass:: pyuvdata.uvcal.fhd_cal.FHDCal + :members: + .. autoclass:: pyuvdata.uvbeam.beamfits.BeamFITS :members: .. autoclass:: pyuvdata.uvbeam.cst_beam.CSTBeam :members: +.. autoclass:: pyuvdata.uvbeam.mwa_beam.MWABeam + :members: + Functions ---------- .. autofunction:: pyuvdata.uvdata.fhd.get_fhd_history +.. autofunction:: pyuvdata.uvbeam.mwa_beam.P1sin + +.. autofunction:: pyuvdata.uvbeam.mwa_beam.P1sin_array + +.. autofunction:: pyuvdata.uvflag.uvflag.and_rows_cols + +.. autofunction:: pyuvdata.uvflag.uvflag.lst_from_uv + +.. autofunction:: pyuvdata.uvflag.uvflag.flags2waterfall + + aipy extracts _____________ diff --git a/pyuvdata/utils.py b/pyuvdata/utils.py index 24a2c9251c..93fbb063bc 100644 --- a/pyuvdata/utils.py +++ b/pyuvdata/utils.py @@ -1208,6 +1208,7 @@ def mean_collapse(arr, weights=None, axis=None, return_weights=False, Whether to return sum of weights. return_weights_square: bool Whether to return the sum of the square of the weights. Default is False. + """ arr = copy.deepcopy(arr) # avoid changing outside if weights is None: @@ -1277,6 +1278,7 @@ def quadmean_collapse(arr, weights=None, axis=None, return_weights=False, Whether to return sum of weights. return_weights_square: bool whether to return the sum of the squares of the weights. Default is False. + """ out = mean_collapse(np.abs(arr)**2, weights=weights, axis=axis, return_weights=return_weights, @@ -1307,6 +1309,7 @@ def or_collapse(arr, weights=None, axis=None, return_weights=False, NOTE: the dummy weights will simply be an array of ones return_weights_square: bool NOT USED, but kept for symmetry with other collapsing functions. + """ if arr.dtype != np.bool: raise ValueError('Input to or_collapse function must be boolean array') @@ -1337,6 +1340,7 @@ def and_collapse(arr, weights=None, axis=None, return_weights=False, NOTE: the dummy weights will simply be an array of ones return_weights_square: bool NOT USED, but kept for symmetry with other collapsing functions. + """ if arr.dtype != np.bool: raise ValueError('Input to and_collapse function must be boolean array') @@ -1371,6 +1375,7 @@ def collapse(arr, alg, weights=None, axis=None, return_weights=False, Whether to return sum of weights. return_weights_square: bool Whether to return the sum of the squares of the weights. Default is False. + """ collapse_dict = {'mean': mean_collapse, 'absmean': absmean_collapse, 'quadmean': quadmean_collapse, 'or': or_collapse, @@ -1391,9 +1396,11 @@ def uvcalibrate(uvdata, uvcal, inplace=True, prop_flags=True, flag_missing=True, Parameters ---------- - uvdata: UVData object - uvcal: UVCal object - inplace: bool, optional + uvdata : UVData object + UVData object to calibrate. + uvcal : UVCal object + UVCal object containing the calibration. + inplace : bool, optional if True edit uvdata in place, else deepcopy prop_flags : bool, optional if True, propagate calibration flags to data flags @@ -1416,6 +1423,7 @@ def uvcalibrate(uvdata, uvcal, inplace=True, prop_flags=True, flag_missing=True, ------- UVData, optional Returns if not inplace + """ # deepcopy for not inplace if not inplace: diff --git a/pyuvdata/uvbase.py b/pyuvdata/uvbase.py index 37da3e7c59..a0af30edbd 100644 --- a/pyuvdata/uvbase.py +++ b/pyuvdata/uvbase.py @@ -32,17 +32,20 @@ class UVBase(object): defined. The init method of this base class creates properties (named using UVParameter.name) from all the UVParameter attributes on the subclass. AngleParameter and LocationParameter attributes also have extra convenience - properties defined:\n - AngleParameter:\n - UVParameter.name+'_degrees'\n - LocationParameter:\n - UVParameter.name+'_lat_lon_alt'\n - UVParameter.name+'_lat_lon_alt_degrees' + properties defined: + + AngleParameter: + + UVParameter.name+'_degrees' + + LocationParameter: + + UVParameter.name+'_lat_lon_alt' + UVParameter.name+'_lat_lon_alt_degrees' """ def __init__(self): """Create properties from UVParameter attributes.""" - warnings.formatwarning = _warning # set any UVParameter attributes to be properties @@ -68,7 +71,6 @@ def __init__(self): def prop_fget(self, param_name): """Getter method for UVParameter properties.""" - def fget(self): this_param = getattr(self, param_name) return this_param.value @@ -76,7 +78,6 @@ def fget(self): def prop_fset(self, param_name): """Setter method for UVParameter properties.""" - def fset(self, value): this_param = getattr(self, param_name) this_param.value = value @@ -85,7 +86,6 @@ def fset(self, value): def degree_prop_fget(self, param_name): """Degree getter method for AngleParameter properties.""" - def fget(self): this_param = getattr(self, param_name) return this_param.degrees() @@ -93,7 +93,6 @@ def fget(self): def degree_prop_fset(self, param_name): """Degree setter method for AngleParameter properties.""" - def fset(self, value): this_param = getattr(self, param_name) this_param.set_degrees(value) @@ -102,7 +101,6 @@ def fset(self, value): def lat_lon_alt_prop_fget(self, param_name): """Lat/lon/alt getter method for LocationParameter properties.""" - def fget(self): this_param = getattr(self, param_name) return this_param.lat_lon_alt() @@ -110,7 +108,6 @@ def fget(self): def lat_lon_alt_prop_fset(self, param_name): """Lat/lon/alt setter method for LocationParameter properties.""" - def fset(self, value): this_param = getattr(self, param_name) this_param.set_lat_lon_alt(value) @@ -119,7 +116,6 @@ def fset(self, value): def lat_lon_alt_degrees_prop_fget(self, param_name): """Lat/lon/alt degree getter method for LocationParameter properties.""" - def fget(self): this_param = getattr(self, param_name) return this_param.lat_lon_alt_degrees() @@ -127,7 +123,6 @@ def fget(self): def lat_lon_alt_degrees_prop_fset(self, param_name): """Lat/lon/alt degree setter method for LocationParameter properties.""" - def fset(self, value): this_param = getattr(self, param_name) this_param.set_lat_lon_alt_degrees(value) @@ -135,7 +130,7 @@ def fset(self, value): return fset def __iter__(self): - """Iterator for all UVParameter attributes.""" + """Iterate over all UVParameter attributes.""" attribute_list = [a for a in dir(self) if not a.startswith('__') and not callable(getattr(self, a))] param_list = [] @@ -147,7 +142,7 @@ def __iter__(self): yield a def required(self): - """Iterator for all required UVParameter attributes.""" + """Iterate over all required UVParameter attributes.""" attribute_list = [a for a in dir(self) if not a.startswith('__') and not callable(getattr(self, a))] required_list = [] @@ -160,7 +155,7 @@ def required(self): yield a def extra(self): - """Iterator for all non-required UVParameter attributes.""" + """Iterate over all non-required UVParameter attributes.""" attribute_list = [a for a in dir(self) if not a.startswith('__') and not callable(getattr(self, a))] extra_list = [] @@ -230,16 +225,21 @@ def __ne__(self, other): def check(self, check_extra=True, run_check_acceptability=True, ignore_requirements=False): """ Check that required parameters exist and have the correct shapes. + Optionally, check that the values are acceptable. - Args: - check_extra: If true, check shapes and values on all parameters, - otherwise only check required parameters. - run_check_acceptability: Option to check if values in parameters - are acceptable. Default is True. - ignore_requirements: Do not error if a required parameter isn't set. - Default is False. This allows the user to run the shape/acceptability checks - on parameters in a partially-defined UVData object. + Parameters + ---------- + check_extra : bool + If true, check shapes and values on all parameters, + otherwise only check required parameters. + run_check_acceptability : bool + Option to check if values in parameters are acceptable. + ignore_requirements : bool + Do not error if a required parameter isn't set. + This allows the user to run the shape/acceptability checks + on parameters in a partially-defined UVData object. + """ if check_extra: p_check = list(self.required()) + list(self.extra()) diff --git a/pyuvdata/uvbeam/mwa_beam.py b/pyuvdata/uvbeam/mwa_beam.py index 4ca4fc8a52..af1ee67079 100644 --- a/pyuvdata/uvbeam/mwa_beam.py +++ b/pyuvdata/uvbeam/mwa_beam.py @@ -46,9 +46,9 @@ def P1sin(nmax, theta): Returns ------- P_sin : array of float - P_{n}^{|m|}(cos(theta))/sin(theta) with FEKO order M,N. Shape (nmax ** 2 + 2 * nmax). + P_{n}^{abs(m)}(cos(theta))/sin(theta) with FEKO order M,N. Shape (nmax ** 2 + 2 * nmax). P1 : array of float - P_{n}^{|m|+1}(cos(theta)) with FEKO order M,N. Shape (nmax ** 2 + 2 * nmax). + P_{n}^{abs(m)+1}(cos(theta)) with FEKO order M,N. Shape (nmax ** 2 + 2 * nmax). """ # initialize for nmax, we have 2(1+...+nmax)+nmax=nmax^2+2*nmax long array @@ -62,7 +62,7 @@ def P1sin(nmax, theta): # step from 1 to nmax for n in range(1, nmax + 1): - # legendre P_{n}^{|m|=0...n} (cos_th) + # legendre P_{n}^{abs(m)=0...n} (cos_th) orders = np.arange(0, n + 1) orders = orders.reshape(n + 1, 1) P = lpmv(orders, n, cos_th) @@ -74,11 +74,11 @@ def P1sin(nmax, theta): # a=a.reshape(3,1) # lpmv(b,2,np.arange(0,0.3,0.1)) - # P_{n}^{|m|+1} (cos_th) + # P_{n}^{abs(m)+1} (cos_th) Pm1 = np.append(P[1::], 0) Pm1 = Pm1.reshape(len(Pm1), 1) - # P_{n}^{|m|}(cos_th)/sin_th + # P_{n}^{abs(m)}(cos_th)/sin_th Pm_sin = np.zeros((n + 1, 1)) # initialize if cos_th == 1: @@ -115,7 +115,7 @@ def P1sin(nmax, theta): def P1sin_array(nmax, theta): """ - Calculate P^|m|_n(cos(theta))/sin(theta) and P^(|m|+1)_n(cos(theta)). + Calculate P^abs(m)_n(cos(theta))/sin(theta) and P^(abs(m)+1)_n(cos(theta)). Similar to the "P1sin" function, but calculates for all theta in one go. At the end of the function, patches are made using the original P1sin function @@ -133,9 +133,9 @@ def P1sin_array(nmax, theta): Returns ------- P_sin : array of float - P_{n}^{|m|}(cos(theta))/sin(theta) with FEKO order M,N. Shape (nmax ** 2 + 2 * nmax, theta.size). + P_{n}^{abs(m)}(cos(theta))/sin(theta) with FEKO order M,N. Shape (nmax ** 2 + 2 * nmax, theta.size). P1 : array of float - P_{n}^{|m|+1}(cos(theta)) with FEKO order M,N. Shape (nmax ** 2 + 2 * nmax, theta.size). + P_{n}^{abs(m)+1}(cos(theta)) with FEKO order M,N. Shape (nmax ** 2 + 2 * nmax, theta.size). """ cos_th = np.cos(theta) @@ -149,7 +149,7 @@ def P1sin_array(nmax, theta): P_sin = np.zeros((nmax ** 2 + 2 * nmax, np.size(theta))) P1 = np.zeros((nmax ** 2 + 2 * nmax, np.size(theta))) for n in range(1, nmax + 1): - # legendre P_{n}^{|m|=0...n} (cos_th) + # legendre P_{n}^{abs(m)=0...n} (cos_th) orders = np.arange(0, n + 1) orders = orders.reshape(n + 1, 1) @@ -157,10 +157,10 @@ def P1sin_array(nmax, theta): # in theory, fetching for all n in one go should also be possible P = lpmv(orders, n, cos_th) - # P_{n}^{|m|+1} (cos_th) + # P_{n}^{abs(m)+1} (cos_th) Pm1 = np.vstack([P[1::, :], np.zeros((1, np.size(theta)))]) - # P_{n}^{|m|}(u)/sin_th + # P_{n}^{abs(m)}(u)/sin_th Pm_sin = P / sin_theta # accumulate Psin and P1 for the m values @@ -390,7 +390,7 @@ def _get_response(self, freqs_hz, pol_names, beam_modes, phi_arr, theta_arr): # calculate equation C_mn from equation 4 of # pyuvdata/docs/references/Far_field_spherical_FEKO_draft2.pdf # These are the normalization factors for the associated - # Legendre function of order n and rank |m| + # Legendre function of order n and rank abs(m) C_MN = (0.5 * (2 * N + 1) * factorial(N - abs(M)) / factorial(N + abs(M))) ** 0.5 # 1 for M<=0, -1 for odd M>0 diff --git a/pyuvdata/uvbeam/uvbeam.py b/pyuvdata/uvbeam/uvbeam.py index 2bb244926a..9e53fa0da1 100644 --- a/pyuvdata/uvbeam/uvbeam.py +++ b/pyuvdata/uvbeam/uvbeam.py @@ -2335,7 +2335,9 @@ def read_cst_beam(self, filename, beam_type='power', feed_pol=None, rotate_pol=N Either a settings yaml file or a cst text file or list of cst text files to read from. If a list is passed, the files are combined along the appropriate axes. + Settings yaml files must include the following keywords: + | - telescope_name (str) | - feed_name (str) | - feed_version (str) @@ -2347,6 +2349,7 @@ def read_cst_beam(self, filename, beam_type='power', feed_pol=None, rotate_pol=N | - feed_pol (str) or (list(str)) and they may include the following optional keywords: + | - x_orientation (str): Optional but strongly encouraged! | - ref_imp (float): beam model reference impedance | - sim_beam_type (str): e.g. 'E-farfield' diff --git a/pyuvdata/uvdata/ms.py b/pyuvdata/uvdata/ms.py index fc25228301..b947d7f2bc 100644 --- a/pyuvdata/uvdata/ms.py +++ b/pyuvdata/uvdata/ms.py @@ -40,6 +40,9 @@ class MS(UVData): """ Defines a class for reading and writing casa measurement sets. + This class should not be interacted with directly, instead use the read_ms + method on the UVData class. + Attributes ---------- ms_required_extra : list of str diff --git a/pyuvdata/uvdata/tests/test_uvh5.py b/pyuvdata/uvdata/tests/test_uvh5.py index 2bd6ccaff2..aed1296678 100644 --- a/pyuvdata/uvdata/tests/test_uvh5.py +++ b/pyuvdata/uvdata/tests/test_uvh5.py @@ -2272,7 +2272,7 @@ def test_uvh5_partial_write_ints_irregular_multi4(uv_uvh5): def test_antenna_names_not_list(uv_uvfits): - """Test if antenna_names is cast to an array, dimensions are preserved in np.string_ call during uvh5 write.""" + """Test if antenna_names is cast to an array, dimensions are preserved in ``np.string_`` call during uvh5 write.""" uv_in = uv_uvfits uv_out = UVData() testfile = os.path.join(DATA_PATH, 'test', 'outtest_uvfits_ant_names.uvh5') diff --git a/pyuvdata/uvdata/uvdata.py b/pyuvdata/uvdata/uvdata.py index e82f153fd1..f681f91135 100644 --- a/pyuvdata/uvdata/uvdata.py +++ b/pyuvdata/uvdata/uvdata.py @@ -2181,9 +2181,9 @@ def __add__(self, other, phase_center_radec=None, If other is not a UVData object, self and other are not compatible or if data in self and other overlap. One way they can not be compatible if if they have different phasing, in that case set - `unphase_to_drift` or `phase_center_radec`to (un)phase them so they + `unphase_to_drift` or `phase_center_radec` to (un)phase them so they are compatible. - If phase_center_radec is not None and is not length 2. + If `phase_center_radec` is not None and is not length 2. """ if inplace: diff --git a/pyuvdata/uvflag/uvflag.py b/pyuvdata/uvflag/uvflag.py index 9353fe5b88..0aa1b52cc9 100644 --- a/pyuvdata/uvflag/uvflag.py +++ b/pyuvdata/uvflag/uvflag.py @@ -74,6 +74,7 @@ def flags2waterfall(uv, flag_array=None, keep_pol=False): Averages over baselines and polarizations (in the case of visibility data), or antennas and jones parameters (in case of calibrationd data). + Parameters ---------- uv : A UVData or UVCal object @@ -2065,7 +2066,7 @@ def write(self, filename, clobber=False, data_compression='lzf'): The file to write to. clobber : bool Option to overwrite the file if it already exists. - data_compression : str + data_compression : str HDF5 filter to apply when writing the data_array. If no compression is wanted, set to None. diff --git a/setup.cfg b/setup.cfg index 93556b6c5b..ca8f7e5c18 100644 --- a/setup.cfg +++ b/setup.cfg @@ -18,8 +18,7 @@ per-file-ignores = # remove the A from following lines as builtin shadowing is fixed: # remove the N802, N803 from following lines as functions and input variables are renamed to pep8 style: pyuvdata/uvdata/aipy_extracts.py: D,A - pyuvdata/uvbase.py: D - pyuvdata/uvcal/uvcal.py: D + pyuvdata/uvcal/uvcal.py: D,RST pyuvdata/uvflag/uvflag.py: D,A pyuvdata/uvflag/tests/test_uvflag.py: D,A pyuvdata/uvdata/uvdata.py: N802 @@ -28,7 +27,7 @@ per-file-ignores = pyuvdata/tests/test_utils.py: D,N802 pyuvdata/tests/__init__.py: D,N802 docstring-convention = numpy -select = C,E,W,T4,B9,F,D,A,N +select = C,E,W,T4,B9,F,D,A,N,RST # List of other checks to consider adding: # move to a max line length rather than ignoring E501 # max-line-length = 88 @@ -38,7 +37,6 @@ select = C,E,W,T4,B9,F,D,A,N # flake8-black # flake8-eradicate # flake8-isort -# flake8-rst-docstrings # flake8-rst # darglint # flake8-copyright