codecov[bot]: # [Codecov](https://codecov.io/gh/RadioAstronomySoftwareGroup/pyuvdata/pull/732?src=pr&el=h1) Report
> Merging [#732](https://codecov.io/gh/RadioAstronomySoftwareGroup/pyuvdata/pull/732?src=pr&el=desc) into [master](https://codecov.io/gh/RadioAstronomySoftwareGroup/pyuvdata/commit/5406e7fd30526b124d9755f0b14e8c9d1af88c01?src=pr&el=desc) will **decrease** coverage by `<.01%`.
> The diff coverage is `100%`.

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

```diff
@@            Coverage Diff             @@
##           master     #732      +/-   ##
==========================================
- Coverage   99.03%   99.02%   -0.01%     
==========================================
  Files          21       21              
  Lines        9410     9374      -36     
==========================================
- Hits         9319     9283      -36     
  Misses         91       91
```


| [Impacted Files](https://codecov.io/gh/RadioAstronomySoftwareGroup/pyuvdata/pull/732?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [pyuvdata/utils.py](https://codecov.io/gh/RadioAstronomySoftwareGroup/pyuvdata/pull/732/diff?src=pr&el=tree#diff-cHl1dmRhdGEvdXRpbHMucHk=) | `100% <100%> (ø)` | :arrow_up: |
| [pyuvdata/uvdata.py](https://codecov.io/gh/RadioAstronomySoftwareGroup/pyuvdata/pull/732/diff?src=pr&el=tree#diff-cHl1dmRhdGEvdXZkYXRhLnB5) | `100% <0%> (ø)` | :arrow_up: |
| [pyuvdata/uvflag.py](https://codecov.io/gh/RadioAstronomySoftwareGroup/pyuvdata/pull/732/diff?src=pr&el=tree#diff-cHl1dmRhdGEvdXZmbGFnLnB5) | `100% <0%> (ø)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/RadioAstronomySoftwareGroup/pyuvdata/pull/732?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/732?src=pr&el=footer). Last update [5406e7f...c606f2c](https://codecov.io/gh/RadioAstronomySoftwareGroup/pyuvdata/pull/732?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).

bhazelton: can you rebase this? The coverage report is crazy and I think it's because it's not based off the current master.
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 342f2bd4..aa6baeda 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,6 +10,7 @@ All notable changes to this project will be documented in this file.
 - `utils.apply_uvflag` for applying UVFlag objects to UVData objects
 
 ### Fixed
+- Redundancy finder will now error if any baselines appear in multiple groups.
 - A bug in `UVCal` objects that prevented them from properly getting data with `ee`/`nn`-style polarizations.
 - a bug in `UVFlag` where `x_orientation` was not set during initialization.
 - A bug in `UVCal.read_fhd_cal` that caused calibration solutions to be approximately doubled.
diff --git a/pyuvdata/utils.py b/pyuvdata/utils.py
index ef9e2795..1cebc572 100644
--- a/pyuvdata/utils.py
+++ b/pyuvdata/utils.py
@@ -1389,6 +1389,9 @@ def get_baseline_redundancies(baselines, baseline_vecs, tol=1.0, with_conjugates
         vec_bin_centers[gi] = np.mean(baseline_vecs[inds, :], axis=0)
 
     lens = np.sqrt(np.sum(vec_bin_centers**2, axis=1))
+    if np.sum([len(bg) for bg in bl_gps]) > Nbls:
+        raise ValueError("Some baselines are falling into multiple"
+                         " redundant groups. Lower the tolerance to resolve ambiguity.")
 
     return bl_gps, vec_bin_centers, lens
 

Make sure find redundancy methods only put each baseline in one redundant group
Should be asserts to enforce that if it's already the case.
diff --git a/pyuvdata/tests/test_utils.py b/pyuvdata/tests/test_utils.py
index 337bfa8a..eae93e5f 100644
--- a/pyuvdata/tests/test_utils.py
+++ b/pyuvdata/tests/test_utils.py
@@ -570,6 +570,27 @@ def test_redundancy_finder():
                               lens[gi], atol=tol)
 
 
+def test_high_tolerance_redundancy_error():
+    """
+    Confirm that an error is raised if the redundancy tolerance is set too high,
+    such that baselines end up in multiple
+    """
+    uvd = pyuvdata.UVData()
+    uvd.read_uvfits(os.path.join(DATA_PATH, 'fewant_randsrc_airybeam_Nsrc100_10MHz.uvfits'))
+
+    uvd.select(times=uvd.time_array[0])
+    uvd.unphase_to_drift(use_ant_pos=True)   # uvw_array is now equivalent to baseline positions
+    uvd.conjugate_bls(convention='ant1<ant2', use_enu=True)
+    bl_positions = uvd.uvw_array
+
+    tol = 20.05  # meters
+
+    with pytest.raises(ValueError) as cm:
+        baseline_groups, vec_bin_centers, lens, conjugates = uvutils.get_baseline_redundancies(
+            uvd.baseline_array, bl_positions, tol=tol, with_conjugates=True)
+    assert "Some baselines are falling into" in str(cm.value)
+
+
 def test_redundancy_conjugates():
     # Check that the correct baselines are flipped when returning redundancies with conjugates.
 
diff --git a/pyuvdata/tests/test_uvdata.py b/pyuvdata/tests/test_uvdata.py
index 67e686db..ae710852 100644
--- a/pyuvdata/tests/test_uvdata.py
+++ b/pyuvdata/tests/test_uvdata.py
@@ -3858,7 +3858,7 @@ def test_redundancy_contract_expand_nblts_not_nbls_times_ntimes():
 def test_compress_redundancy_metadata_only():
     uv0 = UVData()
     uv0.read_uvfits(os.path.join(DATA_PATH, 'fewant_randsrc_airybeam_Nsrc100_10MHz.uvfits'))
-    tol = 0.01
+    tol = 0.05
 
     # Assign identical data to each redundant group:
     red_gps, centers, lengths = uv0.get_redundancies(tol=tol, use_antpos=True, conjugate_bls=True)

pip install -e .
parse_log_pytest
requirements.txt
coverage pytest-cov pytest
apt-get update apt-get install -y gcc g++
3.6
requirements/base.txt
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
astropy==4.1
attrs==22.2.0
cached-property==1.5.2
certifi==2021.5.30
coverage==6.2
h5py==3.1.0
importlib-metadata==4.8.3
iniconfig==1.1.1
numpy==1.19.5
packaging==21.3
pluggy==1.0.0
py==1.11.0
pyparsing==3.1.4
pytest==7.0.1
pytest-cov==4.0.0
-e git+https://github.com/RadioAstronomySoftwareGroup/pyuvdata.git@5406e7fd30526b124d9755f0b14e8c9d1af88c01#egg=pyuvdata
scipy==1.5.4
six==1.17.0
tomli==1.2.3
typing_extensions==4.1.1
zipp==3.6.0

name: pyuvdata
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - ca-certificates=2025.2.25=h06a4308_0
  - certifi=2021.5.30=py36h06a4308_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.3=he6710b0_2
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=1.1.1w=h7f8727e_0
  - pip=21.2.2=py36h06a4308_0
  - python=3.6.13=h12debd9_1
  - readline=8.2=h5eee18b_0
  - setuptools=58.0.4=py36h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - wheel=0.37.1=pyhd3eb1b0_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - astropy==4.1
      - attrs==22.2.0
      - cached-property==1.5.2
      - coverage==6.2
      - h5py==3.1.0
      - importlib-metadata==4.8.3
      - iniconfig==1.1.1
      - numpy==1.19.5
      - packaging==21.3
      - pluggy==1.0.0
      - py==1.11.0
      - pyparsing==3.1.4
      - pytest==7.0.1
      - pytest-cov==4.0.0
      - scipy==1.5.4
      - six==1.17.0
      - tomli==1.2.3
      - typing-extensions==4.1.1
      - zipp==3.6.0
prefix: /opt/conda/envs/pyuvdata

pyuvdata/tests/test_utils.py::test_high_tolerance_redundancy_error
pyuvdata/tests/test_uvdata.py::test_add_drift pyuvdata/tests/test_uvdata.py::test_break_add pyuvdata/tests/test_uvdata.py::test_set_uvws_from_antenna_pos pyuvdata/tests/test_uvdata.py::test_downsample_in_time_errors pyuvdata/tests/test_uvdata.py::test_downsample_in_time_int_time_mismatch_warning pyuvdata/tests/test_uvdata.py::test_downsample_in_time_varying_integration_time pyuvdata/tests/test_uvdata.py::test_downsample_in_time_varying_integration_time_warning
pyuvdata/tests/test_utils.py::test_XYZ_from_LatLonAlt pyuvdata/tests/test_utils.py::test_LatLonAlt_from_XYZ pyuvdata/tests/test_utils.py::test_ENU_tofrom_ECEF pyuvdata/tests/test_utils.py::test_mwa_ecef_conversion pyuvdata/tests/test_utils.py::test_phasing_funcs pyuvdata/tests/test_utils.py::test_pol_funcs pyuvdata/tests/test_utils.py::test_pol_funcs_x_orientation pyuvdata/tests/test_utils.py::test_jones_num_funcs pyuvdata/tests/test_utils.py::test_jones_num_funcs_x_orientation pyuvdata/tests/test_utils.py::test_conj_pol pyuvdata/tests/test_utils.py::test_deprecated_funcs pyuvdata/tests/test_utils.py::test_redundancy_finder pyuvdata/tests/test_utils.py::test_redundancy_conjugates pyuvdata/tests/test_utils.py::test_redundancy_finder_fully_redundant_array pyuvdata/tests/test_utils.py::test_reraise_context pyuvdata/tests/test_utils.py::test_str_to_bytes pyuvdata/tests/test_utils.py::test_bytes_to_str pyuvdata/tests/test_utils.py::test_reorder_conj_pols_non_list pyuvdata/tests/test_utils.py::test_reorder_conj_pols_strings pyuvdata/tests/test_utils.py::test_reorder_conj_pols_ints pyuvdata/tests/test_utils.py::test_reorder_conj_pols_missing_conj pyuvdata/tests/test_utils.py::test_collapse_mean_no_return_no_weights pyuvdata/tests/test_utils.py::test_collapse_mean_returned_no_weights pyuvdata/tests/test_utils.py::test_collapse_mean_returned_with_weights pyuvdata/tests/test_utils.py::test_collapse_absmean_no_return_no_weights pyuvdata/tests/test_utils.py::test_collapse_quadmean_no_return_no_weights pyuvdata/tests/test_utils.py::test_collapse_or_no_return_no_weights pyuvdata/tests/test_utils.py::test_collapse_and_no_return_no_weights pyuvdata/tests/test_utils.py::test_collapse_error pyuvdata/tests/test_utils.py::test_mean_no_weights pyuvdata/tests/test_utils.py::test_mean_weights pyuvdata/tests/test_utils.py::test_mean_infs pyuvdata/tests/test_utils.py::test_absmean pyuvdata/tests/test_utils.py::test_quadmean pyuvdata/tests/test_utils.py::test_or_collapse pyuvdata/tests/test_utils.py::test_or_collapse_weights pyuvdata/tests/test_utils.py::test_or_collapse_errors pyuvdata/tests/test_utils.py::test_and_collapse pyuvdata/tests/test_utils.py::test_and_collapse_weights pyuvdata/tests/test_utils.py::test_and_collapse_errors pyuvdata/tests/test_utils.py::test_uvcalibrate_apply_gains pyuvdata/tests/test_utils.py::test_uvcalibrate_flag_propagation pyuvdata/tests/test_utils.py::test_apply_uvflag pyuvdata/tests/test_utils.py::test_upos_tol_reds pyuvdata/tests/test_uvdata.py::test_parameter_iter pyuvdata/tests/test_uvdata.py::test_required_parameter_iter pyuvdata/tests/test_uvdata.py::test_extra_parameter_iter pyuvdata/tests/test_uvdata.py::test_unexpected_parameters pyuvdata/tests/test_uvdata.py::test_unexpected_attributes pyuvdata/tests/test_uvdata.py::test_properties pyuvdata/tests/test_uvdata.py::test_baseline_to_antnums pyuvdata/tests/test_uvdata.py::test_baseline_to_antnums_vectorized pyuvdata/tests/test_uvdata.py::test_antnums_to_baselines pyuvdata/tests/test_uvdata.py::test_known_telescopes pyuvdata/tests/test_uvdata.py::test_HERA_diameters pyuvdata/tests/test_uvdata.py::test_generic_read pyuvdata/tests/test_uvdata.py::test_phase_unphaseHERA[phase_kwargs0] pyuvdata/tests/test_uvdata.py::test_phase_unphaseHERA[phase_kwargs1] pyuvdata/tests/test_uvdata.py::test_phase_unphaseHERA[phase_kwargs2] pyuvdata/tests/test_uvdata.py::test_phase_unphaseHERA_one_bl pyuvdata/tests/test_uvdata.py::test_phase_unphaseHERA_antpos pyuvdata/tests/test_uvdata.py::test_phase_unphaseHERA_zenith_timestamp pyuvdata/tests/test_uvdata.py::test_phase_to_time_jd_input pyuvdata/tests/test_uvdata.py::test_phase_to_time_error pyuvdata/tests/test_uvdata.py::test_unphase_drift_data_error pyuvdata/tests/test_uvdata.py::test_unknown_phase_unphaseHERA_errors[unphase_to_drift-phase_kwargs0-The pyuvdata/tests/test_uvdata.py::test_unknown_phase_unphaseHERA_errors[phase-phase_kwargs1-The pyuvdata/tests/test_uvdata.py::test_unknown_phase_unphaseHERA_errors[phase_to_time-phase_kwargs2-The pyuvdata/tests/test_uvdata.py::test_phase_rephaseHERA_errors[phase-phase_kwargs0-The pyuvdata/tests/test_uvdata.py::test_phase_rephaseHERA_errors[phase_to_time-phase_kwargs1-The pyuvdata/tests/test_uvdata.py::test_phase_unphaseHERA_bad_frame pyuvdata/tests/test_uvdata.py::test_phasing pyuvdata/tests/test_uvdata.py::test_set_phase_unknown pyuvdata/tests/test_uvdata.py::test_select_blts pyuvdata/tests/test_uvdata.py::test_select_antennas pyuvdata/tests/test_uvdata.py::test_select_bls pyuvdata/tests/test_uvdata.py::test_select_times pyuvdata/tests/test_uvdata.py::test_select_frequencies pyuvdata/tests/test_uvdata.py::test_select_freq_chans pyuvdata/tests/test_uvdata.py::test_select_polarizations pyuvdata/tests/test_uvdata.py::test_select pyuvdata/tests/test_uvdata.py::test_select_not_inplace pyuvdata/tests/test_uvdata.py::test_conjugate_bls pyuvdata/tests/test_uvdata.py::test_reorder_pols pyuvdata/tests/test_uvdata.py::test_reorder_blts pyuvdata/tests/test_uvdata.py::test_sum_vis pyuvdata/tests/test_uvdata.py::test_add pyuvdata/tests/test_uvdata.py::test_fast_concat pyuvdata/tests/test_uvdata.py::test_fast_concat_errors pyuvdata/tests/test_uvdata.py::test_key2inds pyuvdata/tests/test_uvdata.py::test_key2inds_conj_all_pols pyuvdata/tests/test_uvdata.py::test_key2inds_conj_all_pols_fringe pyuvdata/tests/test_uvdata.py::test_key2inds_conj_all_pols_bl_fringe pyuvdata/tests/test_uvdata.py::test_key2inds_conj_all_pols_missing_data pyuvdata/tests/test_uvdata.py::test_key2inds_conj_all_pols_bls pyuvdata/tests/test_uvdata.py::test_key2inds_conj_all_pols_missing_data_bls pyuvdata/tests/test_uvdata.py::test_smart_slicing pyuvdata/tests/test_uvdata.py::test_get_data pyuvdata/tests/test_uvdata.py::test_get_flags pyuvdata/tests/test_uvdata.py::test_get_nsamples pyuvdata/tests/test_uvdata.py::test_antpair2ind pyuvdata/tests/test_uvdata.py::test_get_times pyuvdata/tests/test_uvdata.py::test_antpairpol_iter pyuvdata/tests/test_uvdata.py::test_get_ants pyuvdata/tests/test_uvdata.py::test_get_ENU_antpos pyuvdata/tests/test_uvdata.py::test_telescope_loc_XYZ_check pyuvdata/tests/test_uvdata.py::test_get_pols pyuvdata/tests/test_uvdata.py::test_get_pols_x_orientation pyuvdata/tests/test_uvdata.py::test_deprecated_x_orientation pyuvdata/tests/test_uvdata.py::test_get_feedpols pyuvdata/tests/test_uvdata.py::test_parse_ants pyuvdata/tests/test_uvdata.py::test_select_with_ant_str pyuvdata/tests/test_uvdata.py::test_deprecated_redundancy_funcs pyuvdata/tests/test_uvdata.py::test_get_antenna_redundancies pyuvdata/tests/test_uvdata.py::test_redundancy_contract_expand pyuvdata/tests/test_uvdata.py::test_redundancy_contract_expand_nblts_not_nbls_times_ntimes pyuvdata/tests/test_uvdata.py::test_compress_redundancy_metadata_only pyuvdata/tests/test_uvdata.py::test_redundancy_missing_groups pyuvdata/tests/test_uvdata.py::test_quick_redundant_vs_redundant_test_array pyuvdata/tests/test_uvdata.py::test_redundancy_finder_when_nblts_not_nbls_times_ntimes pyuvdata/tests/test_uvdata.py::test_overlapping_data_add pyuvdata/tests/test_uvdata.py::test_lsts_from_time_with_only_unique pyuvdata/tests/test_uvdata.py::test_copy pyuvdata/tests/test_uvdata.py::test_upsample_in_time pyuvdata/tests/test_uvdata.py::test_upsample_in_time_with_flags pyuvdata/tests/test_uvdata.py::test_upsample_in_time_noninteger_resampling pyuvdata/tests/test_uvdata.py::test_upsample_in_time_errors pyuvdata/tests/test_uvdata.py::test_upsample_in_time_summing_correlator_mode pyuvdata/tests/test_uvdata.py::test_upsample_in_time_summing_correlator_mode_with_flags pyuvdata/tests/test_uvdata.py::test_upsample_in_time_summing_correlator_mode_nonint_resampling pyuvdata/tests/test_uvdata.py::test_partial_upsample_in_time pyuvdata/tests/test_uvdata.py::test_upsample_in_time_drift pyuvdata/tests/test_uvdata.py::test_upsample_in_time_drift_no_phasing pyuvdata/tests/test_uvdata.py::test_downsample_in_time pyuvdata/tests/test_uvdata.py::test_downsample_in_time_partial_flags pyuvdata/tests/test_uvdata.py::test_downsample_in_time_totally_flagged pyuvdata/tests/test_uvdata.py::test_downsample_in_time_uneven_samples pyuvdata/tests/test_uvdata.py::test_downsample_in_time_uneven_samples_discard_ragged pyuvdata/tests/test_uvdata.py::test_downsample_in_time_summing_correlator_mode pyuvdata/tests/test_uvdata.py::test_downsample_in_time_summing_correlator_mode_partial_flags pyuvdata/tests/test_uvdata.py::test_downsample_in_time_summing_correlator_mode_totally_flagged pyuvdata/tests/test_uvdata.py::test_downsample_in_time_summing_correlator_mode_uneven_samples pyuvdata/tests/test_uvdata.py::test_downsample_in_time_summing_correlator_mode_uneven_samples_drop_ragged pyuvdata/tests/test_uvdata.py::test_partial_downsample_in_time pyuvdata/tests/test_uvdata.py::test_downsample_in_time_drift pyuvdata/tests/test_uvdata.py::test_downsample_in_time_drift_no_phasing pyuvdata/tests/test_uvdata.py::test_upsample_downsample_in_time pyuvdata/tests/test_uvdata.py::test_upsample_downsample_in_time_odd_resample pyuvdata/tests/test_uvdata.py::test_upsample_downsample_in_time_metadata_only pyuvdata/tests/test_uvdata.py::test_resample_in_time pyuvdata/tests/test_uvdata.py::test_resample_in_time_downsample_only pyuvdata/tests/test_uvdata.py::test_resample_in_time_only_upsample
pyuvdata/utils.py CHANGELOG.md
pyuvdata/utils.py CHANGELOG.md