Repository: mne-tools/mne-python

Issue:
apply_inverse_raw() calls _check_reference() even though no EEG channels are used
This bug report is based on [this discussion](https://mne.discourse.group/t/why-does-mne-minimum-norm-apply-inverse-raw-call-check-reference-when-using-only-meg-channels/5321) in the MNE group.

I am trying to compute the inverse solution for continuous raw data. I am only interested in MEG channels so I set eeg to False during the computation of the inverse operator. However, apply_inverse_raw() still calls _check_reference().

Unfortunately, the flag custom_ref_applied was set to True for the HCP data (see discussion for details), which caused _check_reference() to raise an error despite me not even using EEG channels.

apply_inverse_raw() should either only call _check_reference() if EEG channels are being used or _check_reference() should check whether EEG channels are being used before raising an error. Personally, I think the second option is better since this seems relevant for other methods calling _check_reference() as well. A solution could look like this:

```
def _check_reference(inst, ch_names=None):
    """Check for EEG ref."""
    info = inst.info
    if ch_names is not None:
        picks = [ci for ci, ch_name in enumerate(info['ch_names'])
                 if ch_name in ch_names]
        info = pick_info(info, sel=picks)
    if 'eeg' not in info.get_channel_types():
        return
    if _needs_eeg_average_ref_proj(info):
        raise ValueError(
            'EEG average reference (using a projector) is mandatory for '
            'modeling, use the method set_eeg_reference(projection=True)')
    if info.get('custom_ref_applied', False):
        raise ValueError('Custom EEG reference is not allowed for inverse '
                         'modeling.')
```

Also as a side question, why does apply_inverse_epochs() not call _check_reference()?


Discussion:

Resolving patch:
diff --git a/mne/minimum_norm/inverse.py b/mne/minimum_norm/inverse.py
--- a/mne/minimum_norm/inverse.py
+++ b/mne/minimum_norm/inverse.py
@@ -1163,6 +1163,7 @@ def _apply_inverse_epochs_gen(epochs, inverse_operator, lambda2, method='dSPM',
                               use_cps=True, verbose=None):
     """Generate inverse solutions for epochs. Used in apply_inverse_epochs."""
     _validate_type(epochs, BaseEpochs, 'epochs')
+    _check_reference(epochs, inverse_operator['info']['ch_names'])
     _check_option('method', method, INVERSE_METHODS)
     _check_ori(pick_ori, inverse_operator['source_ori'],
                inverse_operator['src'])


Test patch:
diff --git a/mne/minimum_norm/tests/test_inverse.py b/mne/minimum_norm/tests/test_inverse.py
--- a/mne/minimum_norm/tests/test_inverse.py
+++ b/mne/minimum_norm/tests/test_inverse.py
@@ -15,7 +15,7 @@
 from mne.datasets import testing
 from mne.label import read_label, label_sign_flip
 from mne.event import read_events
-from mne.epochs import Epochs, EpochsArray
+from mne.epochs import Epochs, EpochsArray, make_fixed_length_epochs
 from mne.forward import restrict_forward_to_stc, apply_forward, is_fixed_orient
 from mne.source_estimate import read_source_estimate, VolSourceEstimate
 from mne.source_space import _get_src_nn
@@ -206,15 +206,35 @@ def _compare_io(inv_op, *, out_file_ext='.fif', tempdir):
 def test_warn_inverse_operator(evoked, noise_cov):
     """Test MNE inverse warning without average EEG projection."""
     bad_info = evoked.info
+    data = evoked.data
+    tmax = evoked.tmax
+    del evoked
     with bad_info._unlock():
         bad_info['projs'] = list()
+    assert bad_info['bads'] == ['MEG 2443', 'EEG 053']
     fwd_op = convert_forward_solution(read_forward_solution(fname_fwd),
                                       surf_ori=True, copy=False)
     with pytest.raises(ValueError, match='greater than or'):
         make_inverse_operator(bad_info, fwd_op, noise_cov, depth=-0.1)
     noise_cov['projs'].pop(-1)  # get rid of avg EEG ref proj
     with pytest.warns(RuntimeWarning, match='reference'):
-        make_inverse_operator(bad_info, fwd_op, noise_cov)
+        inv = make_inverse_operator(bad_info, fwd_op, noise_cov)
+    # Create MEG-only forward, create inverse (should not warn)
+    fwd_meg = pick_channels_forward(fwd_op, bad_info['ch_names'][:306])
+    inv_meg = make_inverse_operator(bad_info, fwd_meg, noise_cov)
+    # Create MEG-only inverse, apply to M/EEG data (raw, epochs, evoked)
+    raw = mne.io.RawArray(data, bad_info)
+    epochs = make_fixed_length_epochs(raw, duration=tmax).load_data()
+    assert len(epochs) == 1
+    evoked = epochs.average()
+    assert 'eeg' in raw
+    assert 'meg' in raw
+    for (func, inst) in ((apply_inverse_raw, raw),
+                         (apply_inverse_epochs, epochs),
+                         (apply_inverse, evoked)):
+        with pytest.raises(ValueError, match='reference'):
+            func(inst, inv, 1. / 9.)
+        func(inst, inv_meg, 1. / 9.)  # no warning
 
 
 @pytest.mark.slowtest