Repository: mne-tools/mne-python

Issue:
BUG: Error with single-trial epoch input to compute_source_psd_epochs
Here is the script from Sebastian to reproduce the error discussed here (https://mail.nmr.mgh.harvard.edu/pipermail//mne_analysis/2014-August/002299.html) using the MNE sample dataset:

``` python
import mne
from mne.minimum_norm import read_inverse_operator, compute_source_psd_epochs
from mne.datasets import sample

data_path = sample.data_path()
subjects_dir = data_path + '/subjects'
fname_raw = data_path + '/MEG/sample/sample_audvis_filt-0-40_raw.fif'
fname_inv = data_path + '/MEG/sample/sample_audvis-meg-oct-6-meg-inv.fif'
fname_event = data_path + '/MEG/sample/sample_audvis_filt-0-40_raw-eve.fif'

raw = mne.io.Raw(fname_raw)
inverse_operator = read_inverse_operator(fname_inv)
events = mne.read_events(fname_event)

event_id, tmin, tmax = 3, 0, .1

epochs = mne.Epochs(raw=raw, events=events, event_id=event_id, tmin=tmin,
                    tmax=tmax)

for ii in range(len(epochs.events)):
    stcs_gen = compute_source_psd_epochs(epochs=epochs[ii],
                                         inverse_operator=inverse_operator,
                                         lambda2=1. / 9,
                                         method="dSPM", fmin=15.0,
                                         fmax=25.0,
                                         pick_ori=None, pca=True,
                                         bandwidth=.01, n_jobs=1,
                                         low_bias=False,
                                         return_generator=True)
    print(list(stcs_gen))
```

cc @agramfort 



Discussion:
the pb occurs in the dpss windows function that is called with n_tapers_max = 0

```
523     half_nbw = float(bandwidth) * n_times / (2 * sfreq)
524     n_tapers_max = int(2 * half_nbw)
```

leads to n_tapers_max = 0

@mluessi or @Eric89GXL do you have time to have a look?


Resolving patch:
diff --git a/mne/minimum_norm/time_frequency.py b/mne/minimum_norm/time_frequency.py
--- a/mne/minimum_norm/time_frequency.py
+++ b/mne/minimum_norm/time_frequency.py
@@ -7,6 +7,7 @@
 
 import numpy as np
 from scipy import linalg, signal, fftpack
+import warnings
 
 from ..io.constants import FIFF
 from ..source_estimate import _make_stc
@@ -521,6 +522,9 @@ def _compute_source_psd_epochs(epochs, inverse_operator, lambda2=1. / 9.,
 
     # compute standardized half-bandwidth
     half_nbw = float(bandwidth) * n_times / (2 * sfreq)
+    if half_nbw < 0.5:
+        warnings.warn('Bandwidth too small, using minimum (normalized 0.5)')
+        half_nbw = 0.5
     n_tapers_max = int(2 * half_nbw)
 
     dpss, eigvals = dpss_windows(n_times, half_nbw, n_tapers_max,
diff --git a/mne/time_frequency/multitaper.py b/mne/time_frequency/multitaper.py
--- a/mne/time_frequency/multitaper.py
+++ b/mne/time_frequency/multitaper.py
@@ -6,6 +6,7 @@
 
 import numpy as np
 from scipy import fftpack, linalg, interpolate
+import warnings
 
 from ..parallel import parallel_func
 from ..utils import verbose, sum_squared
@@ -239,8 +240,12 @@ def dpss_windows(N, half_nbw, Kmax, low_bias=True, interp_from=None,
 
     if low_bias:
         idx = (eigvals > 0.9)
+        if not idx.any():
+            warnings.warn('Could not properly use low_bias, '
+                          'keeping lowest-bias taper')
+            idx = [np.argmax(eigvals)]
         dpss, eigvals = dpss[idx], eigvals[idx]
-
+    assert len(dpss) > 0  # should never happen
     return dpss, eigvals
 
 


Test patch:
diff --git a/mne/minimum_norm/tests/test_time_frequency.py b/mne/minimum_norm/tests/test_time_frequency.py
--- a/mne/minimum_norm/tests/test_time_frequency.py
+++ b/mne/minimum_norm/tests/test_time_frequency.py
@@ -3,6 +3,7 @@
 import numpy as np
 from numpy.testing import assert_array_almost_equal
 from nose.tools import assert_true
+import warnings
 
 from mne.datasets import sample
 from mne import io, find_events, Epochs, pick_types
@@ -23,6 +24,7 @@
 fname_data = op.join(data_path, 'MEG', 'sample',
                      'sample_audvis_raw.fif')
 fname_label = op.join(data_path, 'MEG', 'sample', 'labels', 'Aud-lh.label')
+warnings.simplefilter('always')
 
 
 @sample.requires_sample_data
@@ -159,3 +161,22 @@ def test_source_psd_epochs():
 
     assert_array_almost_equal(psd, stc_psd.data)
     assert_array_almost_equal(freqs, stc_psd.times)
+
+    # Check corner cases caused by tiny bandwidth
+    with warnings.catch_warnings(record=True) as w:
+        warnings.simplefilter('always')
+        compute_source_psd_epochs(one_epochs, inverse_operator,
+                                  lambda2=lambda2, method=method,
+                                  pick_ori="normal", label=label,
+                                  bandwidth=0.01, low_bias=True,
+                                  fmin=fmin, fmax=fmax,
+                                  return_generator=False)
+        compute_source_psd_epochs(one_epochs, inverse_operator,
+                                  lambda2=lambda2, method=method,
+                                  pick_ori="normal", label=label,
+                                  bandwidth=0.01, low_bias=False,
+                                  fmin=fmin, fmax=fmax,
+                                  return_generator=False)
+    assert_true(len(w) >= 2)
+    assert_true(any('not properly use' in str(ww.message) for ww in w))
+    assert_true(any('Bandwidth too small' in str(ww.message) for ww in w))