Repository: mne-tools/mne-python

Issue:
pick_types(csd=True) does not return csd type channels but fails with a ValueError
Hi all,
I am pretty sure, this is not as it is supposed to be, but would be fixed quite easily.

#### Describe the bug
When working on an instance with [CSD transformed](https://mne.tools/stable/auto_examples/preprocessing/eeg_csd.html?highlight=csd) data, using `.pick_types(csd=True)` returns an error complaining about "no channels match the selection".

This is due to the `csd=True` kwarg not being passed to `pick_types` in the according method of `mne.channels.channels.UpdateChannelsMixin` - [see here](https://github.com/mne-tools/mne-python/blob/maint/1.0/mne/channels/channels.py#L671-L676)

#### Steps to reproduce
```python
import mne
from pathlib import Path
from mne.datasets import sample

data_path = sample.data_path()
meg_path = Path(data_path) / 'MEG' / 'sample'
raw = mne.io.read_raw_fif(meg_path / 'sample_audvis_raw.fif')
raw = raw.pick_types(meg=False, eeg=True, eog=True, ecg=True, stim=True,
                     exclude=raw.info['bads']).load_data()
raw.set_eeg_reference(projection=True).apply_proj()
raw_csd = mne.preprocessing.compute_current_source_density(raw)

raw_csd.pick_types(csd=True)
```

#### Expected results
The instance object filtered for the 59 CSD transformed channels. See info:
```python
In [22]: raw_csd.info
Out[22]:
<Info | 21 non-empty values
 acq_pars: ACQch001 110113 ACQch002 110112 ACQch003 110111 ACQch004 110122 ...
 bads: []
 ch_names: STI 001, STI 002, STI 003, STI 004, STI 005, STI 006, STI 014, ...
 chs: 9 Stimulus, 59 Current source density, 1 EOG
```
#### Actual results
The error:
```python
ValueError: No channels match the selection.
```



Discussion:
Hello! 👋 Thanks for opening your first issue here! ❤️ We will try to get back to you soon. 🚴🏽‍♂️

thanks for the detailed bug report. WOuld you have time to submit a PR to
fix this?

thanks

> Message ID: ***@***.***>
>

Sure, I will create a PR tomorrow.

Resolving patch:
diff --git a/mne/channels/channels.py b/mne/channels/channels.py
--- a/mne/channels/channels.py
+++ b/mne/channels/channels.py
@@ -672,8 +672,8 @@ def pick_types(self, meg=False, eeg=False, stim=False, eog=False,
             self.info, meg=meg, eeg=eeg, stim=stim, eog=eog, ecg=ecg, emg=emg,
             ref_meg=ref_meg, misc=misc, resp=resp, chpi=chpi, exci=exci,
             ias=ias, syst=syst, seeg=seeg, dipole=dipole, gof=gof, bio=bio,
-            ecog=ecog, fnirs=fnirs, dbs=dbs, include=include, exclude=exclude,
-            selection=selection)
+            ecog=ecog, fnirs=fnirs, csd=csd, dbs=dbs, include=include,
+            exclude=exclude, selection=selection)
 
         self._pick_drop_channels(idx)
 
diff --git a/mne/preprocessing/_csd.py b/mne/preprocessing/_csd.py
--- a/mne/preprocessing/_csd.py
+++ b/mne/preprocessing/_csd.py
@@ -15,7 +15,7 @@
 import numpy as np
 
 from .. import pick_types
-from ..utils import _validate_type, _ensure_int, _check_preload
+from ..utils import _validate_type, _ensure_int, _check_preload, verbose
 from ..io import BaseRaw
 from ..io.constants import FIFF
 from ..epochs import BaseEpochs
@@ -51,9 +51,10 @@ def _compute_csd(G_precomputed, H, radius):
     return X
 
 
+@verbose
 def compute_current_source_density(inst, sphere='auto', lambda2=1e-5,
                                    stiffness=4, n_legendre_terms=50,
-                                   copy=True):
+                                   copy=True, *, verbose=None):
     """Get the current source density (CSD) transformation.
 
     Transformation based on spherical spline surface Laplacian
@@ -75,6 +76,7 @@ def compute_current_source_density(inst, sphere='auto', lambda2=1e-5,
         Number of Legendre terms to evaluate.
     copy : bool
         Whether to overwrite instance data or create a copy.
+    %(verbose)s
 
     Returns
     -------


Test patch:
diff --git a/mne/io/tests/test_pick.py b/mne/io/tests/test_pick.py
--- a/mne/io/tests/test_pick.py
+++ b/mne/io/tests/test_pick.py
@@ -11,6 +11,8 @@
 from mne import __file__ as _root_init_fname
 from mne.io import (read_raw_fif, RawArray, read_raw_bti, read_raw_kit,
                     read_info)
+from mne.channels import make_standard_montage
+from mne.preprocessing import compute_current_source_density
 from mne.io.pick import (channel_indices_by_type, channel_type,
                          pick_types_forward, _picks_by_type, _picks_to_idx,
                          _contains_ch_type, pick_channels_cov,
@@ -599,6 +601,23 @@ def test_pick_types_meg():
     assert list(pick_types(info2, eeg=True)) == [0, 1]
 
 
+def test_pick_types_csd():
+    """Test pick_types(csd=True)."""
+    # info with laplacian/CSD channels at indices 1, 2
+    names = ['F1', 'F2', 'C1', 'C2', 'A1', 'A2', 'misc1', 'CSD1']
+    info1 = create_info(names, 256, ["eeg", "eeg", "eeg", "eeg", "mag",
+                                     "mag", 'misc', 'csd'])
+    raw = RawArray(np.zeros((8, 512)), info1)
+    raw.set_montage(make_standard_montage('standard_1020'), verbose='error')
+    raw_csd = compute_current_source_density(raw, verbose='error')
+
+    assert_array_equal(pick_types(info1, csd=True), [7])
+
+    # pick from the raw object
+    assert raw_csd.copy().pick_types(csd=True).ch_names == [
+        'F1', 'F2', 'C1', 'C2', 'CSD1']
+
+
 @pytest.mark.parametrize('meg', [True, False, 'grad', 'mag'])
 @pytest.mark.parametrize('eeg', [True, False])
 @pytest.mark.parametrize('ordered', [True, False])