Repository: RadioAstronomySoftwareGroup/pyuvdata
Pull request #803 (2020-04-14 02:02:47)

az_za spline opts
<!--- Provide a general summary of your changes in the Title above -->

## Description
<!--- Describe your changes in detail -->
Allow options to `RectBivariateSpline` to be passed from the interp method in UVBeam.

## Motivation and Context
<!--- Why is this change required? What problem does it solve? -->
Allows the user more control over the spatial interpolation function.

<!--- If it fixes an open issue, please link to the issue here. If this PR closes an issue, put the word 'closes' before the issue link to auto-close the issue when the PR is merged. -->

## Types of changes
<!--- What types of changes does your code introduce? Put an `x` in all the boxes that apply: -->
- [ ] Bug fix (non-breaking change which fixes an issue)
- [x] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to change)
- [ ] Documentation change (documentation changes only)
- [ ] Version change
- [ ] Build or continuous integration change


## Checklist:
<!--- You may remove the checklists that don't apply to your change type(s) or just leave them empty -->
<!--- Go over all the following points, and replace the space with an `x` in all the boxes that apply. -->
<!--- If you're unsure about any of these, don't hesitate to ask. We're here to help! -->
- [x] I have read the [contribution guide](https://github.com/RadioAstronomySoftwareGroup/pyuvdata/blob/master/.github/CONTRIBUTING.md).
- [x] My code follows the code style of this project.

New feature checklist:
- [x] I have added or updated the docstrings associated with my feature using the [numpy docstring format](https://numpydoc.readthedocs.io/en/latest/format.html).
- [ ] I have updated the tutorial to highlight my new feature (if appropriate).
- [x] I have added tests to cover my new feature.
- [x] All new and existing tests pass.
- [x] I have updated the [CHANGELOG](https://github.com/RadioAstronomySoftwareGroup/pyuvdata/blob/master/CHANGELOG.md).

bhazelton: @aelanman looks like there are some linting issues. If you install the pyuvdata pre-commit hook it will prevent you from committing files that won't pass linting.
codecov[bot]: # [Codecov](https://codecov.io/gh/RadioAstronomySoftwareGroup/pyuvdata/pull/803?src=pr&el=h1) Report
> Merging [#803](https://codecov.io/gh/RadioAstronomySoftwareGroup/pyuvdata/pull/803?src=pr&el=desc) into [master](https://codecov.io/gh/RadioAstronomySoftwareGroup/pyuvdata/commit/070b74372fec7ce5bdd21411039f7eb9e18fa1b6&el=desc) will **increase** coverage by `0.00%`.
> The diff coverage is `100.00%`.

[![Impacted file tree graph](https://codecov.io/gh/RadioAstronomySoftwareGroup/pyuvdata/pull/803/graphs/tree.svg?width=650&height=150&src=pr&token=4QykcCeB1H)](https://codecov.io/gh/RadioAstronomySoftwareGroup/pyuvdata/pull/803?src=pr&el=tree)

```diff
@@           Coverage Diff           @@
##           master     #803   +/-   ##
=======================================
  Coverage   98.95%   98.95%           
=======================================
  Files          27       27           
  Lines        9566     9569    +3     
=======================================
+ Hits         9466     9469    +3     
  Misses        100      100           
```


| [Impacted Files](https://codecov.io/gh/RadioAstronomySoftwareGroup/pyuvdata/pull/803?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [pyuvdata/uvbeam/uvbeam.py](https://codecov.io/gh/RadioAstronomySoftwareGroup/pyuvdata/pull/803/diff?src=pr&el=tree#diff-cHl1dmRhdGEvdXZiZWFtL3V2YmVhbS5weQ==) | `99.32% <100.00%> (+<0.01%)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/RadioAstronomySoftwareGroup/pyuvdata/pull/803?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/RadioAstronomySoftwareGroup/pyuvdata/pull/803?src=pr&el=footer). Last update [070b743...7bc7f92](https://codecov.io/gh/RadioAstronomySoftwareGroup/pyuvdata/pull/803?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index eb877fc3..be4cca65 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,6 +10,7 @@ All notable changes to this project will be documented in this file.
 - Handling of strings in UVFlag files has been made more widely compatible.
 
 ### Added
+- Option to provide parameters for RectBivariatespline through interp
 - `weights_square_array` (optional) parameter on UVFlag - stores sum of squares of weights when converting to waterfall
 - `frequency_average` method on UVData to average data along the frequency axis.
 
diff --git a/pyuvdata/uvbeam/uvbeam.py b/pyuvdata/uvbeam/uvbeam.py
index 6d200cf6..663fb2ea 100644
--- a/pyuvdata/uvbeam/uvbeam.py
+++ b/pyuvdata/uvbeam/uvbeam.py
@@ -1169,6 +1169,7 @@ class UVBeam(UVBase):
         freq_interp_tol=1.0,
         polarizations=None,
         reuse_spline=False,
+        spline_opts=None,
     ):
         """
         Interpolate in az_za coordinate system with a simple spline.
@@ -1191,6 +1192,8 @@ class UVBeam(UVBase):
             Default is all polarizations in self.polarization_array.
         reuse_spline : bool
             Save the interpolation functions for reuse.
+        spline_opts : dict
+            Options (kx, ky, s) for numpy.RectBivariateSpline.
 
         Returns
         -------
@@ -1330,6 +1333,8 @@ class UVBeam(UVBase):
         data_shape = (self.Naxes_vec, self.Nspws, Npol_feeds, input_nfreqs, npoints)
         interp_data = np.zeros(data_shape, dtype=data_type)
 
+        if spline_opts is None or not isinstance(spline_opts, dict):
+            spline_opts = {}
         if reuse_spline and not hasattr(self, "saved_interp_functions"):
             int_dict = {}
             self.saved_interp_functions = int_dict
@@ -1352,11 +1357,13 @@ class UVBeam(UVBase):
                                     theta_use[:, 0],
                                     phi_use[0, :],
                                     data_use[index0, index1, index2, index3, :].real,
+                                    **spline_opts,
                                 )
                                 imag_lut = interpolate.RectBivariateSpline(
                                     theta_use[:, 0],
                                     phi_use[0, :],
                                     data_use[index0, index1, index2, index3, :].imag,
+                                    **spline_opts,
                                 )
                                 lut = get_lambda(real_lut, imag_lut)
                             else:
@@ -1364,6 +1371,7 @@ class UVBeam(UVBase):
                                     theta_use[:, 0],
                                     phi_use[0, :],
                                     data_use[index0, index1, index2, index3, :],
+                                    **spline_opts,
                                 )
                                 lut = get_lambda(lut)
                             if reuse_spline:
@@ -1571,6 +1579,7 @@ class UVBeam(UVBase):
         polarizations=None,
         return_bandpass=False,
         reuse_spline=False,
+        spline_opts=None,
         new_object=False,
         run_check=True,
         check_extra=True,
@@ -1608,13 +1617,17 @@ class UVBeam(UVBase):
             polarizations to interpolate if beam_type is 'power'.
             Default is all polarizations in self.polarization_array.
         new_object : bool
-            Option to return a new UVData object with the interpolated data,
+            Option to return a new UVBeam object with the interpolated data,
             if possible. Note that this is only possible for Healpix pixels or
             if az_za_grid is True and `az_array` and `za_array` are evenly spaced
             or for frequency only interpolation.
         reuse_spline : bool
             Save the interpolation functions for reuse. Only applies for
             `az_za_simple` interpolation.
+        spline_opts : dict
+            Provide options to numpy.RectBivariateSpline. This includes spline
+            order parameters `kx` and `ky`, and smoothing parameter `s`.
+            Only applies for `az_za_simple` interpolation.
         run_check : bool
             Only used if new_object is True. Option to check for the existence
             and proper shapes of required parameters on the new object.
@@ -1707,6 +1720,7 @@ class UVBeam(UVBase):
         extra_keyword_dict = {}
         if interp_func == "_interp_az_za_rect_spline":
             extra_keyword_dict["reuse_spline"] = reuse_spline
+            extra_keyword_dict["spline_opts"] = spline_opts
 
         interp_data, interp_basis_vector, interp_bandpass = getattr(self, interp_func)(
             az_array_use,
@@ -1714,7 +1728,7 @@ class UVBeam(UVBase):
             freq_array,
             freq_interp_kind=kind_use,
             polarizations=polarizations,
-            **extra_keyword_dict
+            **extra_keyword_dict,
         )
 
         # return just the interpolated arrays


Test patch:
diff --git a/pyuvdata/uvbeam/tests/test_uvbeam.py b/pyuvdata/uvbeam/tests/test_uvbeam.py
index 11002cdc..d82bb15f 100644
--- a/pyuvdata/uvbeam/tests/test_uvbeam.py
+++ b/pyuvdata/uvbeam/tests/test_uvbeam.py
@@ -961,6 +961,19 @@ def test_efield_spatial_interpolation():
 
     assert np.all(reused_data_array == orig_data_array)
 
+    # test passing spline options
+    spline_opts = {"kx": 4, "ky": 4}
+    quartic_data_array, interp_basis_vector = efield_beam.interp(
+        az_array=az_interp_vals,
+        za_array=za_interp_vals,
+        freq_array=freq_interp_vals,
+        spline_opts=spline_opts,
+    )
+
+    # slightly different interpolation, so not identical.
+    assert np.allclose(quartic_data_array, orig_data_array, atol=1e-10)
+    assert not np.all(quartic_data_array == orig_data_array)
+
     select_data_array_orig, interp_basis_vector = efield_beam.interp(
         az_array=az_interp_vals[0:1],
         za_array=za_interp_vals[0:1],