Repository: mne-tools/mne-python

Issue:
BUG: morph.as_volume does not check subject `src`
@jhouck is able to do this for `inv` defined for `sample`:
```
stc_fsaverage.as_volume(inverse_operator['src'], dest='mri', mri_resolution=False, format='nifti1')
```
Assuming the `inv` has a properly defined `src[0]['subject_his_id']` this should not be possible. If that field is not defined, we should probably update the `sample` dataset.


Discussion:
I did double-check, and for that file (sample_audvis-meg-vol-7-meg-inv.fif) `inverse_operator['src'][0]['subject_his_id']` is None.  
Ahh okay. So maybe it does check. But we should probably update the `sample` dataset.
Maybe also a warning if you load/create an inverse operator when its subject is None?
That's probably a reasonable thing to do, too.
agreed. We should have a warning and update the sample dataset.
FYI I just tried recreating the source space with MNE-C and it looks like it does not actually write `subject_his_id` for volume source spaces. I'll set it manually, save the file, and update the dataset. (Our Python `setup_volume_source_space` does set `subject_his_id` properly.)

Resolving patch:
diff --git a/mne/datasets/utils.py b/mne/datasets/utils.py
--- a/mne/datasets/utils.py
+++ b/mne/datasets/utils.py
@@ -257,7 +257,7 @@ def _data_path(path=None, force_update=False, update_path=True, download=True,
              'datasets/foo.tgz',
         misc='https://codeload.github.com/mne-tools/mne-misc-data/'
              'tar.gz/%s' % releases['misc'],
-        sample='https://osf.io/86qa2/download?version=4',
+        sample='https://osf.io/86qa2/download?version=5',
         somato='https://osf.io/tp4sg/download?version=5',
         spm='https://osf.io/je4s8/download?version=2',
         testing='https://codeload.github.com/mne-tools/mne-testing-data/'
@@ -320,7 +320,7 @@ def _data_path(path=None, force_update=False, update_path=True, download=True,
             bst_resting='70fc7bf9c3b97c4f2eab6260ee4a0430'),
         fake='3194e9f7b46039bb050a74f3e1ae9908',
         misc='84e606998ac379ef53029b3b1cf37918',
-        sample='fc2d5b9eb0a144b1d6ba84dc3b983602',
+        sample='12b75d1cb7df9dfb4ad73ed82f61094f',
         somato='f08f17924e23c57a751b3bed4a05fe02',
         spm='9f43f67150e3b694b523a21eb929ea75',
         testing='e825eec08cfd75e85f33ed2b06fc82f1',
diff --git a/mne/morph.py b/mne/morph.py
--- a/mne/morph.py
+++ b/mne/morph.py
@@ -19,7 +19,7 @@
 from .surface import read_morph_map, mesh_edges, read_surface, _compute_nearest
 from .utils import (logger, verbose, check_version, get_subjects_dir,
                     warn as warn_, fill_doc, _check_option, _validate_type,
-                    BunchConst, wrapped_stdout, _check_fname)
+                    BunchConst, wrapped_stdout, _check_fname, warn)
 from .externals.h5io import read_hdf5, write_hdf5
 
 
@@ -525,6 +525,10 @@ def _check_subject_src(subject, src, name='subject_from', src_name='src'):
         subject_check = subject
     else:
         subject_check = src._subject
+        if subject_check is None:
+            warn('The source space does not contain the subject name, we '
+                 'recommend regenerating the source space (and forward / '
+                 'inverse if applicable) for better code reliability')
     if subject is None:
         subject = subject_check
     elif subject_check is not None and subject != subject_check:


Test patch:
diff --git a/mne/tests/test_morph.py b/mne/tests/test_morph.py
--- a/mne/tests/test_morph.py
+++ b/mne/tests/test_morph.py
@@ -241,7 +241,8 @@ def test_volume_source_morph(tmpdir):
     assert src._subject is None  # already None on disk (old!)
 
     with pytest.raises(ValueError, match='subject_from could not be inferred'):
-        compute_source_morph(src=src, subjects_dir=subjects_dir)
+        with pytest.warns(RuntimeWarning, match='recommend regenerating'):
+            compute_source_morph(src=src, subjects_dir=subjects_dir)
 
     # check infer subject_from from src[0]['subject_his_id']
     src[0]['subject_his_id'] = 'sample'
@@ -255,9 +256,10 @@ def test_volume_source_morph(tmpdir):
     # terrible quality buts fast
     zooms = 20
     kwargs = dict(zooms=zooms, niter_sdr=(1,), niter_affine=(1,))
-    source_morph_vol = compute_source_morph(
-        subjects_dir=subjects_dir, src=fname_inv_vol, subject_from='sample',
-        **kwargs)
+    with pytest.warns(RuntimeWarning, match='recommend regenerating'):
+        source_morph_vol = compute_source_morph(
+            subjects_dir=subjects_dir, src=fname_inv_vol,
+            subject_from='sample', **kwargs)
     shape = (13,) * 3  # for the given zooms
 
     assert source_morph_vol.subject_from == 'sample'
@@ -275,6 +277,7 @@ def test_volume_source_morph(tmpdir):
     assert source_morph_vol.src_data['src_shape_full'] == mri_size
 
     fwd = read_forward_solution(fname_fwd_vol)
+    fwd['src'][0]['subject_his_id'] = 'sample'  # avoid further warnings
     source_morph_vol = compute_source_morph(
         fwd['src'], 'sample', 'sample', subjects_dir=subjects_dir,
         **kwargs)
@@ -380,12 +383,12 @@ def test_volume_source_morph(tmpdir):
     with pytest.raises(ValueError, match='If src_to is provided, zooms shoul'):
         source_morph_vol = compute_source_morph(
             fwd['src'], subject_from='sample', src_to=fwd['src'],
-            subjects_dir=subjects_dir, **kwargs)
+            subject_to='sample', subjects_dir=subjects_dir, **kwargs)
     # hack the src_to "zooms" to make it seem like a pos=20. source space
     fwd['src'][0]['src_mri_t']['trans'][:3, :3] = 0.02 * np.eye(3)
     source_morph_vol = compute_source_morph(
         fwd['src'], subject_from='sample', src_to=fwd['src'],
-        subjects_dir=subjects_dir, **kwargs)
+        subject_to='sample', subjects_dir=subjects_dir, **kwargs)
     stc_vol_2 = source_morph_vol.apply(stc_vol)
     # new way, verts match
     assert_array_equal(stc_vol.vertices, stc_vol_2.vertices)
diff --git a/mne/viz/tests/test_3d.py b/mne/viz/tests/test_3d.py
--- a/mne/viz/tests/test_3d.py
+++ b/mne/viz/tests/test_3d.py
@@ -540,6 +540,7 @@ def test_plot_volume_source_estimates_morph():
     n_time = 2
     data = np.random.RandomState(0).rand(n_verts, n_time)
     stc = VolSourceEstimate(data, vertices, 1, 1)
+    sample_src[0]['subject_his_id'] = 'sample'  # old src
     morph = compute_source_morph(sample_src, 'sample', 'fsaverage', zooms=5,
                                  subjects_dir=subjects_dir)
     initial_pos = (-0.05, -0.01, -0.006)