Repository: mne-tools/mne-python

Issue:
Epochs.plot_psd_topomap() doesn't apply unit scaling
`Epochs.plot_psd` does scale the measurement units while `Epochs.plot_psd_topomap` doesn't, [causing confusion](https://mne.discourse.group/t/psd-different-units/3809/15)

```python
import os.path as op
import mne
from mne.datasets import somato

data_path = somato.data_path()
subject = '01'
task = 'somato'
raw_fname = op.join(data_path, 'sub-{}'.format(subject), 'meg',
                    'sub-{}_task-{}_meg.fif'.format(subject, task))

raw = mne.io.read_raw_fif(raw_fname)
events = mne.find_events(raw, stim_channel='STI 014')

picks = mne.pick_types(raw.info, meg='grad', eeg=False, eog=True, stim=False)
event_id, tmin, tmax = 1, -1., 3.
baseline = (None, 0)
epochs = mne.Epochs(raw, events, event_id, tmin, tmax, picks=picks,
                    baseline=baseline, reject=dict(grad=4000e-13, eog=350e-6),
                    preload=True)

epochs.resample(200., npad='auto')  # resample to reduce computation time

fmin = 0.1
fmax = 40

epochs.plot_psd(fmin=fmin, fmax=fmax, average=True, dB=True, area_mode=None)
epochs.plot_psd_topomap(dB=True)
```

![a](https://user-images.githubusercontent.com/2046265/137897334-aa71a546-e7b2-4086-9d7c-d516b4b0f4ec.png)

![b](https://user-images.githubusercontent.com/2046265/137897379-9d64af29-1670-4201-9624-db696d707739.png)

The displayed data  have a scale difference of precisely 13 orders of magnitude.

cc @cbrnr @guiomar


Discussion:
I guess the fix is that topomaps should also use (fT/cm)^2/Hz? Also, the correct unit would be 10 * log10((fT/cm)^2/Hz) if we don't want to use dB (that's also what EEGLAB shows). A bit unwieldy though, for me it would be OK to just use dB and mention the details in the docstring.
In any case, the currently displayed label at the colorbar is wrong, as the data have not been scaled
> Also, the correct unit would be 10 * log10((fT/cm)^2/Hz) if we don't want to use dB (that's also what EEGLAB shows)

Why wouldn't we want to use dB? Are you referring to the comment I made on the forum? :) 
> Why wouldn't we want to use dB? Are you referring to the comment I made on the forum? :)

Yes. Currently we only show (fT/cm)^2/Hz, which is not correct. Either show the explicit unit with the log, or dB.
> Currently we only show (fT/cm)^2/Hz,

No, we show `(fT/cm)^2/Hz (dB)` 😅

But +1 for changing it to `Power (dB)`?
> But +1 for changing it to Power (dB)?

I'd put "Power Spectral Density (dB)" in the title, and 10 * log10((fT/cm)^2/Hz) as the y-label.
> I'd put "Power Spectral Density (dB)" in the title, and 10 * log10((fT/cm)^2/Hz) as the y-label.

Works for me!
👍 
Let's not forget about the more pressuring bug here: we should scale the data before plotting :)

Resolving patch:
diff --git a/mne/viz/topomap.py b/mne/viz/topomap.py
--- a/mne/viz/topomap.py
+++ b/mne/viz/topomap.py
@@ -1893,7 +1893,9 @@ def plot_epochs_psd_topomap(epochs, bands=None,
     """
     ch_type = _get_ch_type(epochs, ch_type)
     units = _handle_default('units', None)
+    scalings = _handle_default('scalings', None)
     unit = units[ch_type]
+    scaling = scalings[ch_type]
 
     picks, pos, merge_channels, names, ch_type, sphere, clip_origin = \
         _prepare_topomap_plot(epochs, ch_type, sphere=sphere)
@@ -1905,6 +1907,7 @@ def plot_epochs_psd_topomap(epochs, bands=None,
                                  normalization=normalization, picks=picks,
                                  proj=proj, n_jobs=n_jobs)
     psds = np.mean(psds, axis=0)
+    psds *= scaling**2
 
     if merge_channels:
         psds, names = _merge_ch_data(psds, ch_type, names, method='mean')
diff --git a/tutorials/time-freq/20_sensors_time_frequency.py b/tutorials/time-freq/20_sensors_time_frequency.py
--- a/tutorials/time-freq/20_sensors_time_frequency.py
+++ b/tutorials/time-freq/20_sensors_time_frequency.py
@@ -63,18 +63,27 @@
 epochs.plot_psd(fmin=2., fmax=40., average=True, spatial_colors=False)
 
 # %%
-# Now let's take a look at the spatial distributions of the PSD.
-epochs.plot_psd_topomap(ch_type='grad', normalize=True)
+# Now, let's take a look at the spatial distributions of the PSD, averaged
+# across epochs and frequency bands.
+epochs.plot_psd_topomap(ch_type='grad', normalize=False)
 
 # %%
-# Alternatively, you can also create PSDs from Epochs objects with functions
+# Alternatively, you can also create PSDs from `~mne.Epochs` with functions
 # that start with ``psd_`` such as
 # :func:`mne.time_frequency.psd_multitaper` and
 # :func:`mne.time_frequency.psd_welch`.
+#
+# .. note::
+#    In contrast to the methods for visualization, those ``psd_*`` functions do
+#    **not** scale the data from SI units to more "convenient" values. So when
+#    e.g. calculating the PSD of gradiometers via
+#    :func:`~mne.time_frequency.psd_multitaper`, you will get the power as
+#    ``(T/m)²/Hz`` (instead of ``(fT/cm)²/Hz`` via
+#    :meth:`~mne.Epochs.plot_psd`).
 
 f, ax = plt.subplots()
 psds, freqs = psd_multitaper(epochs, fmin=2, fmax=40, n_jobs=1)
-psds = 10. * np.log10(psds)
+psds = 10 * np.log10(psds)  # convert to dB
 psds_mean = psds.mean(0).mean(0)
 psds_std = psds.mean(0).std(0)
 


Test patch: