Repository: mne-tools/mne-python

Issue:
VIZ, BUG: plot_compare_evokeds(..., axes='topo') fails for CSD channel types
probably related to mne.channels.layout.generate_2d_layout.  The plot succeeds, but all the axes are squished into the lower left corner and the figure is mostly empty space.  To reproduce:

```python
import mne

data_path = mne.datasets.sample.data_path()

raw = mne.io.read_raw_fif(data_path + '/MEG/sample/sample_audvis_raw.fif',
                          preload=True)
raw.pick_types(meg=False, eeg=True, eog=True, ecg=True, stim=True, exclude='bads')
events = mne.find_events(raw)
raw.set_eeg_reference(projection=True).apply_proj()

event_id = {'auditory/left': 1, 'auditory/right': 2, 'visual/left': 3,
            'visual/right': 4, 'smiley': 5, 'button': 32}
epochs = mne.Epochs(raw, events, event_id=event_id, tmin=-0.2, tmax=0.5,
                    preload=True)
evoked = epochs['auditory'].average()

evoked_csd = mne.preprocessing.compute_current_source_density(evoked)

mne.viz.plot_compare_evokeds(evoked_csd, picks='csd', axes='topo')
```


Discussion:

Resolving patch:
diff --git a/mne/channels/channels.py b/mne/channels/channels.py
--- a/mne/channels/channels.py
+++ b/mne/channels/channels.py
@@ -1262,7 +1262,7 @@ def find_ch_adjacency(info, ch_type):
     (has_vv_mag, has_vv_grad, is_old_vv, has_4D_mag, ctf_other_types,
      has_CTF_grad, n_kit_grads, has_any_meg, has_eeg_coils,
      has_eeg_coils_and_meg, has_eeg_coils_only,
-     has_neuromag_122_grad) = _get_ch_info(info)
+     has_neuromag_122_grad, has_csd_coils) = _get_ch_info(info)
     conn_name = None
     if has_vv_mag and ch_type == 'mag':
         conn_name = 'neuromag306mag'
@@ -1452,10 +1452,13 @@ def _get_ch_info(info):
                      FIFF.FIFFV_EEG_CH in channel_types)
     has_eeg_coils_and_meg = has_eeg_coils and has_any_meg
     has_eeg_coils_only = has_eeg_coils and not has_any_meg
+    has_csd_coils = (FIFF.FIFFV_COIL_EEG_CSD in coil_types and
+                     FIFF.FIFFV_EEG_CH in channel_types)
 
     return (has_vv_mag, has_vv_grad, is_old_vv, has_4D_mag, ctf_other_types,
             has_CTF_grad, n_kit_grads, has_any_meg, has_eeg_coils,
-            has_eeg_coils_and_meg, has_eeg_coils_only, has_neuromag_122_grad)
+            has_eeg_coils_and_meg, has_eeg_coils_only, has_neuromag_122_grad,
+            has_csd_coils)
 
 
 def make_1020_channel_selections(info, midline="z"):
diff --git a/mne/channels/layout.py b/mne/channels/layout.py
--- a/mne/channels/layout.py
+++ b/mne/channels/layout.py
@@ -214,7 +214,8 @@ def read_layout(kind, path=None, scale=True):
     return Layout(box=box, pos=pos, names=names, kind=kind, ids=ids)
 
 
-def make_eeg_layout(info, radius=0.5, width=None, height=None, exclude='bads'):
+def make_eeg_layout(info, radius=0.5, width=None, height=None, exclude='bads',
+                    csd=False):
     """Create .lout file from EEG electrode digitization.
 
     Parameters
@@ -232,6 +233,8 @@ def make_eeg_layout(info, radius=0.5, width=None, height=None, exclude='bads'):
     exclude : list of str | str
         List of channels to exclude. If empty do not exclude any.
         If 'bads', exclude channels in info['bads'] (default).
+    csd : bool
+        Whether the channels contain current-source-density-transformed data.
 
     Returns
     -------
@@ -249,8 +252,10 @@ def make_eeg_layout(info, radius=0.5, width=None, height=None, exclude='bads'):
     if height is not None and not (0 <= height <= 1.0):
         raise ValueError('The height parameter should be between 0 and 1.')
 
-    picks = pick_types(info, meg=False, eeg=True, ref_meg=False,
-                       exclude=exclude)
+    pick_kwargs = dict(meg=False, eeg=True, ref_meg=False, exclude=exclude)
+    if csd:
+        pick_kwargs.update(csd=True, eeg=False)
+    picks = pick_types(info, **pick_kwargs)
     loc2d = _find_topomap_coords(info, picks)
     names = [info['chs'][i]['ch_name'] for i in picks]
 
@@ -383,12 +388,13 @@ def find_layout(info, ch_type=None, exclude='bads'):
     layout : Layout instance | None
         None if layout not found.
     """
-    _check_option('ch_type', ch_type, [None, 'mag', 'grad', 'meg', 'eeg'])
+    _check_option('ch_type', ch_type, [None, 'mag', 'grad', 'meg', 'eeg',
+                                       'csd'])
 
     (has_vv_mag, has_vv_grad, is_old_vv, has_4D_mag, ctf_other_types,
      has_CTF_grad, n_kit_grads, has_any_meg, has_eeg_coils,
      has_eeg_coils_and_meg, has_eeg_coils_only,
-     has_neuromag_122_grad) = _get_ch_info(info)
+     has_neuromag_122_grad, has_csd_coils) = _get_ch_info(info)
     has_vv_meg = has_vv_mag and has_vv_grad
     has_vv_only_mag = has_vv_mag and not has_vv_grad
     has_vv_only_grad = has_vv_grad and not has_vv_mag
@@ -417,6 +423,8 @@ def find_layout(info, ch_type=None, exclude='bads'):
             raise RuntimeError('Cannot make EEG layout, no measurement info '
                                'was passed to `find_layout`')
         return make_eeg_layout(info, exclude=exclude)
+    elif has_csd_coils and ch_type in [None, 'csd']:
+        return make_eeg_layout(info, exclude=exclude, csd=True)
     elif has_4D_mag:
         layout_name = 'magnesWH3600'
     elif has_CTF_grad:
@@ -792,8 +800,7 @@ def _pair_grad_sensors(info, layout=None, topomap_coords=True, exclude='bads',
     pairs = defaultdict(list)
     grad_picks = pick_types(info, meg='grad', ref_meg=False, exclude=exclude)
 
-    (_, has_vv_grad, _, _, _, _, _, _, _, _, _, has_neuromag_122_grad) = \
-        _get_ch_info(info)
+    _, has_vv_grad, *_, has_neuromag_122_grad, _ = _get_ch_info(info)
 
     for i in grad_picks:
         ch = info['chs'][i]


Test patch:
diff --git a/mne/viz/tests/test_evoked.py b/mne/viz/tests/test_evoked.py
--- a/mne/viz/tests/test_evoked.py
+++ b/mne/viz/tests/test_evoked.py
@@ -42,7 +42,7 @@
 sel = [0, 7]
 
 
-def _get_epochs():
+def _get_epochs(picks=picks):
     """Get epochs."""
     raw = read_raw_fif(raw_fname)
     raw.add_proj([], remove_existing=True)
@@ -323,6 +323,12 @@ def test_plot_compare_evokeds():
     figs = plot_compare_evokeds(evoked_dict, axes='topo', legend=True)
     for fig in figs:
         assert len(fig.axes[0].lines) == len(evoked_dict)
+    # test with (fake) CSD data
+    csd = _get_epochs(picks=np.arange(315, 320)).average()  # 5 EEG chs
+    for entry in csd.info['chs']:
+        entry['coil_type'] = FIFF.FIFFV_COIL_EEG_CSD
+        entry['unit'] = FIFF.FIFF_UNIT_V_M2
+    plot_compare_evokeds(csd, picks='csd', axes='topo')
     # old tests
     red.info['chs'][0]['loc'][:2] = 0  # test plotting channel at zero
     plot_compare_evokeds([red, blue], picks=[0],