Repository: mne-tools/mne-python

Issue:
find_bad_channels_maxwell breaks if during some chunk all channels are flat
Hello everyone!

I faced with a problem using `mne.preprocessing.find_bad_channels_maxwell()`. At some point it returns an error:
```
        Interval 103:  510.000 -  514.999
            Flat ( 2): MEG1243 MEG1532
        Interval 104:  515.000 -  519.999
Traceback (most recent call last):
  File "<input>", line 172, in <module>
  File "<decorator-gen-415>", line 24, in find_bad_channels_maxwell
  File "E:\Egor_Levchenko\Software\Miniconda\envs\MCCA-MEG\lib\site-packages\mne\preprocessing\maxwell.py", line 2190, in find_bad_channels_maxwell
    delta = chunk_raw.get_data(these_picks)
  File "<decorator-gen-170>", line 22, in get_data
  File "E:\Egor_Levchenko\Software\Miniconda\envs\MCCA-MEG\lib\site-packages\mne\io\base.py", line 819, in get_data
    picks = _picks_to_idx(self.info, picks, 'all', exclude=())
  File "E:\Egor_Levchenko\Software\Miniconda\envs\MCCA-MEG\lib\site-packages\mne\io\pick.py", line 1058, in _picks_to_idx
    '(%r)' % (orig_picks,))
ValueError: No appropriate channels found for the given picks ([])
```

I checked the code and my data. I think the problem is that my data is corrupted a little bit and I have all flat channels for around  20 seconds. After detecting the flat channels the code takes only `good_meg_picks` which are not in `chunk_flats`. As a result, `these_picks` appears to be empty if all channels are in `chunk_flats`:

https://github.com/mne-tools/mne-python/blob/8b9616dfbc377ce468a6d52b43f7a973eac47cab/mne/preprocessing/maxwell.py#L2167-L2175
https://github.com/mne-tools/mne-python/blob/8b9616dfbc377ce468a6d52b43f7a973eac47cab/mne/preprocessing/maxwell.py#L2187



You can see a snapshot of raw data below:

![image](https://user-images.githubusercontent.com/18507220/122130189-f4304e00-ce3f-11eb-8702-2749c13ecaa5.png)


I think this is more a problem of my data, but I guess it should be nice to ignore "all flat" chunks with a warning or something like that. Correct me please if I am wrong and thank you for any comments!

System properties:
```
OS: Windows Server 2012 R2
python=3.7.7
mne=0.22.0
```


Discussion:
can you try
https://mne.tools/stable/generated/mne.preprocessing.annotate_flat.html
before running maxfilter?
it should make maxfilter ignore the flat segment

Thank you! 
I tried running it, but it is strange that flat segments are not fully detected:
```
>>> annot, bads = mne.preprocessing.annotate_flat(raw, picks='meg')

Finding flat segments
100%|██████████| Channels : 306/306 [00:04<00:00,   62.14it/s]
Marking 0.44% of time points (371 segments) and 0/306 channels bad
```

It detects only specific pieces in one part of the big flat segment:
![image](https://user-images.githubusercontent.com/18507220/122206548-2d9da380-ceaa-11eb-8110-a173f58278af.png)

I've uploaded the data sample [here](https://drive.google.com/drive/folders/1QTtMJIeBDsi5JlauglL0ZkkOOQeSbOn3?usp=sharing).

Looks like there are likely bugs with `annotate_flat` *and* `find_bad_channels_maxwell`, I'll look into it!
> > mne=0.22.0

> I tried running it, but it is strange that flat segments are not fully detected:

I cannot reproduce on 0.22.0:
```
import mne
raw = mne.io.read_raw_fif('sub-1011_ses-20190430_task-vid2_meg.fif').load_data()
annot, bads = mne.preprocessing.annotate_flat(raw, picks='meg', bad_percent=100, verbose=True)
raw.set_annotations(annot)
raw.plot()
```
outputs:
```
Marking 8.85% of time points (5 segments) and 0/306 channels bad
```
Either way, though, I recommend updating to the latest release 0.23 as 0.22 is no longer supported. There I get the same thing:

![Screenshot from 2021-07-06 14-36-31](https://user-images.githubusercontent.com/2365790/124650330-99e74380-de67-11eb-874c-f90425c4044c.png)

Can you try on 0.23? If you have the same problem with the code snippet above I'll boot into Windows to see if I can replicate there.

Resolving patch:
diff --git a/mne/preprocessing/maxwell.py b/mne/preprocessing/maxwell.py
--- a/mne/preprocessing/maxwell.py
+++ b/mne/preprocessing/maxwell.py
@@ -2173,6 +2173,14 @@ def find_bad_channels_maxwell(
         chunk_flats = sorted(all_flats)
         these_picks = [pick for pick in good_meg_picks
                        if raw.ch_names[pick] not in chunk_flats]
+        if len(these_picks) == 0:
+            logger.info(f'            Flat ({len(chunk_flats):2d}): <all>')
+            warn('All-flat segment detected, all channels will be marked as '
+                 f'flat and processing will stop (t={t[0]:0.3f}). '
+                 'Consider using annotate_flat before calling this function '
+                 'with skip_by_annotation="bad_flat" (or similar) to properly '
+                 'process all segments.')
+            break  # no reason to continue
         # Bad pass
         chunk_noisy = list()
         params['st_duration'] = int(round(


Test patch:
diff --git a/mne/preprocessing/tests/test_maxwell.py b/mne/preprocessing/tests/test_maxwell.py
--- a/mne/preprocessing/tests/test_maxwell.py
+++ b/mne/preprocessing/tests/test_maxwell.py
@@ -23,11 +23,12 @@
 from mne.io import (read_raw_fif, read_info, read_raw_bti, read_raw_kit,
                     BaseRaw, read_raw_ctf)
 from mne.io.constants import FIFF
+from mne.preprocessing import (maxwell_filter, find_bad_channels_maxwell,
+                               annotate_flat, compute_maxwell_basis)
 from mne.preprocessing.maxwell import (
-    maxwell_filter, _get_n_moments, _sss_basis_basic, _sh_complex_to_real,
+    _get_n_moments, _sss_basis_basic, _sh_complex_to_real,
     _sh_real_to_complex, _sh_negate, _bases_complex_to_real, _trans_sss_basis,
-    _bases_real_to_complex, _prep_mf_coils, find_bad_channels_maxwell,
-    compute_maxwell_basis)
+    _bases_real_to_complex, _prep_mf_coils)
 from mne.rank import _get_rank_sss, _compute_rank_int, compute_rank
 from mne.utils import (assert_meg_snr, catch_logging,
                        object_diff, buggy_mkl_svd, use_log_level)
@@ -1369,6 +1370,49 @@ def test_find_bad_channels_maxwell(fname, bads, annot, add_ch, ignore_ref,
         assert bads == set(want_bads)
 
 
+def test_find_bads_maxwell_flat():
+    """Test find_bads_maxwell when there are flat channels."""
+    # See gh-9479
+    raw = mne.io.read_raw_fif(raw_small_fname).load_data()
+    assert_allclose(raw.times[-1], 23.97, atol=1e-2)
+    noisy, flat = find_bad_channels_maxwell(raw, min_count=1)
+    assert noisy == ['MEG 1032', 'MEG 2313', 'MEG 2443']
+    assert flat == []
+    n = int(round(raw.info['sfreq'] * 10))
+    assert (len(raw.times) - n) / raw.info['sfreq'] > 10  # at least 10 sec
+    with catch_logging() as log:
+        want_noisy, want_flat = find_bad_channels_maxwell(
+            raw.copy().crop(n / raw.info['sfreq'], None), min_count=1,
+            verbose='debug')
+    log = log.getvalue()
+    assert 'in 2 intervals ' in log
+    assert want_noisy == ['MEG 2313', 'MEG 2443']
+    assert want_flat == []
+    raw._data[:, :n] = 0
+    with pytest.warns(RuntimeWarning, match='All-flat segment detected'):
+        with catch_logging() as log:
+            noisy, flat = find_bad_channels_maxwell(
+                raw, min_count=1, verbose='debug')
+    log = log.getvalue()
+    assert ' in 4 intervals ' in log
+    assert flat == raw.ch_names[:306]
+    assert noisy == []  # none found because all flat
+    # now do what we suggest in the warning
+    annot, _ = annotate_flat(raw, bad_percent=100, min_duration=1.)
+    assert_allclose(annot.duration, 10., atol=1e-2)  # not even divisor sfreq
+    raw.info['bads'] = []
+    raw.set_annotations(annot)
+    data_good = raw.get_data(reject_by_annotation='omit')
+    assert data_good.shape[1] / raw.info['sfreq'] / 5. > 2  # at least 10 sec
+    with catch_logging() as log:
+        noisy, flat = find_bad_channels_maxwell(
+            raw, min_count=1, skip_by_annotation='bad_flat', verbose='debug')
+    log = log.getvalue()
+    assert ' in 2 intervals ' in log, log
+    assert flat == want_flat
+    assert noisy == want_noisy
+
+
 @pytest.mark.parametrize('regularize, n', [
     (None, 80),
     ('in', 71),