Repository: mne-tools/mne-python

Issue:
BUG, VIZ: raw.plot_psd fmax param ignored when plotting
the `fmax` parameter should be used to set the right-hand xlim of the plot, but it is not. In the plots below, the right-hand xlim is determined by the `sfreq` even though `fmax=350` is specified for both plots.

```
import os
import mne
sample_data_folder = mne.datasets.sample.data_path()
sample_data_raw_file = os.path.join(sample_data_folder, 'MEG', 'sample',
                                    'sample_audvis_raw.fif')
raw = mne.io.read_raw_fif(sample_data_raw_file)
raw.crop(0, 60).load_data() 

raw_downsampled = raw.copy().resample(sfreq=100)

for data in (raw, raw_downsampled):
    fig = data.plot_psd(fmax=350)
```
![Figure_1](https://user-images.githubusercontent.com/1810515/65967765-045b8c00-e417-11e9-9b8c-e32950f093c4.png)

![Figure_2](https://user-images.githubusercontent.com/1810515/65967773-07ef1300-e417-11e9-9be3-fc1a6aac8b5f.png)






Discussion:
Somewhere there could be a `min(fmax, freqs[-1])` that makes it so that it uses the min of the actual freqs present and what you pass. But we can undo this / make it work.
I think it should rather crash if you ask for an fmax > sfreq / 2 as you go beyond nyquist cut-off frequency. There is nothing to see beyond.

Resolving patch:
diff --git a/mne/viz/epochs.py b/mne/viz/epochs.py
--- a/mne/viz/epochs.py
+++ b/mne/viz/epochs.py
@@ -31,7 +31,8 @@
                     _compute_scalings, DraggableColorbar, _setup_cmap,
                     _handle_decim, _setup_plot_projector, _set_ax_label_style,
                     _set_title_multiple_electrodes, _make_combine_callable,
-                    _get_figsize_from_config, _toggle_scrollbars)
+                    _get_figsize_from_config, _toggle_scrollbars,
+                    _check_psd_fmax)
 from .misc import _handle_event_colors
 
 
@@ -919,6 +920,7 @@ def plot_epochs_psd(epochs, fmin=0, fmax=np.inf, tmin=None, tmax=None,
     fig, picks_list, titles_list, units_list, scalings_list, ax_list, \
         make_label = _set_psd_plot_params(epochs.info, proj, picks, ax,
                                           area_mode)
+    _check_psd_fmax(epochs, fmax)
     del ax
     psd_list = list()
     for picks in picks_list:
diff --git a/mne/viz/raw.py b/mne/viz/raw.py
--- a/mne/viz/raw.py
+++ b/mne/viz/raw.py
@@ -27,7 +27,8 @@
                     _radio_clicked, _set_radio_button, _handle_topomap_bads,
                     _change_channel_group, _plot_annotations, _setup_butterfly,
                     _handle_decim, _setup_plot_projector, _check_cov,
-                    _set_ax_label_style, _draw_vert_line, _simplify_float)
+                    _set_ax_label_style, _draw_vert_line, _simplify_float,
+                    _check_psd_fmax)
 
 
 def _plot_update_raw_proj(params, bools):
@@ -618,6 +619,7 @@ def plot_raw_psd(raw, fmin=0, fmax=np.inf, tmin=None, tmax=None, proj=False,
     fig, picks_list, titles_list, units_list, scalings_list, ax_list, \
         make_label = _set_psd_plot_params(
             raw.info, proj, picks, ax, area_mode)
+    _check_psd_fmax(raw, fmax)
     del ax
     psd_list = list()
     if n_fft is None:
diff --git a/mne/viz/utils.py b/mne/viz/utils.py
--- a/mne/viz/utils.py
+++ b/mne/viz/utils.py
@@ -3077,6 +3077,14 @@ def _convert_psds(psds, dB, estimate, scaling, unit, ch_names):
     return ylabel
 
 
+def _check_psd_fmax(inst, fmax):
+    """Make sure requested fmax does not exceed Nyquist frequency."""
+    if np.isfinite(fmax) and (fmax > inst.info['sfreq'] / 2):
+        raise ValueError('Requested fmax ({} Hz) must not exceed one half '
+                         'the sampling frequency of the data ({}).'
+                         .format(fmax, 0.5 * inst.info['sfreq']))
+
+
 def _plot_psd(inst, fig, freqs, psd_list, picks_list, titles_list,
               units_list, scalings_list, ax_list, make_label, color, area_mode,
               area_alpha, dB, estimate, average, spatial_colors, xscale,


Test patch:
diff --git a/mne/viz/tests/test_raw.py b/mne/viz/tests/test_raw.py
--- a/mne/viz/tests/test_raw.py
+++ b/mne/viz/tests/test_raw.py
@@ -411,6 +411,10 @@ def test_plot_raw_psd():
     raw.plot_psd(reject_by_annotation=True)
     raw.plot_psd(reject_by_annotation=False)
 
+    # test fmax value checking
+    with pytest.raises(ValueError, match='not exceed one half the sampling'):
+        raw.plot_psd(fmax=50000)
+
     # gh-5046
     raw = read_raw_fif(raw_fname, preload=True).crop(0, 1)
     picks = pick_types(raw.info)