def test_decimate(): """Test decimation of digitizer headshapes with too many points.""" # load headshape and convert to meters hsp_mm = _get_ico_surface(5)['rr'] * 100 hsp_m = hsp_mm / 1000. # save headshape to a file in mm in temporary directory tempdir = _TempDir() sphere_hsp_path = op.join(tempdir, 'test_sphere.txt') np.savetxt(sphere_hsp_path, hsp_mm) # read in raw data using spherical hsp, and extract new hsp with warnings.catch_warnings(record=True) as w: raw = read_raw_kit(sqd_path, mrk_path, elp_txt_path, sphere_hsp_path) assert_true(any('more than' in str(ww.message) for ww in w)) # collect headshape from raw (should now be in m) hsp_dec = np.array([dig['r'] for dig in raw.info['dig']])[8:] # with 10242 points and _decimate_points set to resolution of 5 mm, hsp_dec # should be a bit over 5000 points. If not, something is wrong or # decimation resolution has been purposefully changed assert_true(len(hsp_dec) > 5000) # should have similar size, distance from center dist = np.sqrt(np.sum((hsp_m - np.mean(hsp_m, axis=0))**2, axis=1)) dist_dec = np.sqrt(np.sum((hsp_dec - np.mean(hsp_dec, axis=0))**2, axis=1)) hsp_rad = np.mean(dist) hsp_dec_rad = np.mean(dist_dec) assert_almost_equal(hsp_rad, hsp_dec_rad, places=3)
def test_decimate(tmpdir): """Test decimation of digitizer headshapes with too many points.""" # load headshape and convert to meters hsp_mm = _get_ico_surface(5)['rr'] * 100 hsp_m = hsp_mm / 1000. # save headshape to a file in mm in temporary directory tempdir = str(tmpdir) sphere_hsp_path = op.join(tempdir, 'test_sphere.txt') np.savetxt(sphere_hsp_path, hsp_mm) # read in raw data using spherical hsp, and extract new hsp with pytest.warns(RuntimeWarning, match='was automatically downsampled .* FastScan'): raw = read_raw_kit(sqd_path, mrk_path, elp_txt_path, sphere_hsp_path) # collect headshape from raw (should now be in m) hsp_dec = np.array([dig['r'] for dig in raw.info['dig']])[8:] # with 10242 points and _decimate_points set to resolution of 5 mm, hsp_dec # should be a bit over 5000 points. If not, something is wrong or # decimation resolution has been purposefully changed assert len(hsp_dec) > 5000 # should have similar size, distance from center dist = np.sqrt(np.sum((hsp_m - np.mean(hsp_m, axis=0))**2, axis=1)) dist_dec = np.sqrt(np.sum((hsp_dec - np.mean(hsp_dec, axis=0))**2, axis=1)) hsp_rad = np.mean(dist) hsp_dec_rad = np.mean(dist_dec) assert_array_almost_equal(hsp_rad, hsp_dec_rad, decimal=3)
def test_decimate(tmpdir): """Test decimation of digitizer headshapes with too many points.""" # load headshape and convert to meters hsp_mm = _get_ico_surface(5)['rr'] * 100 hsp_m = hsp_mm / 1000. # save headshape to a file in mm in temporary directory tempdir = str(tmpdir) sphere_hsp_path = op.join(tempdir, 'test_sphere.txt') np.savetxt(sphere_hsp_path, hsp_mm) # read in raw data using spherical hsp, and extract new hsp with pytest.warns(RuntimeWarning, match='was automatically downsampled .* FastScan'): raw = read_raw_kit(sqd_path, mrk_path, elp_txt_path, sphere_hsp_path) # collect headshape from raw (should now be in m) hsp_dec = np.array([dig['r'] for dig in raw.info['dig']])[8:] # with 10242 points and _decimate_points set to resolution of 5 mm, hsp_dec # should be a bit over 5000 points. If not, something is wrong or # decimation resolution has been purposefully changed assert len(hsp_dec) > 5000 # should have similar size, distance from center dist = np.sqrt(np.sum((hsp_m - np.mean(hsp_m, axis=0))**2, axis=1)) dist_dec = np.sqrt(np.sum((hsp_dec - np.mean(hsp_dec, axis=0))**2, axis=1)) hsp_rad = np.mean(dist) hsp_dec_rad = np.mean(dist_dec) assert_array_almost_equal(hsp_rad, hsp_dec_rad, decimal=3)
def test_sensors_inside_bem(): """Test that sensors inside the BEM are problematic.""" rr = _get_ico_surface(1)['rr'] rr /= np.linalg.norm(rr, axis=1, keepdims=True) rr *= 0.1 assert len(rr) == 42 info = create_info(len(rr), 1000., 'mag') info['dev_head_t'] = Transform('meg', 'head', np.eye(4)) for ii, ch in enumerate(info['chs']): ch['loc'][:] = np.concatenate((rr[ii], np.eye(3).ravel())) trans = Transform('head', 'mri', np.eye(4)) trans['trans'][2, 3] = 0.03 sphere_noshell = make_sphere_model((0., 0., 0.), None) sphere = make_sphere_model((0., 0., 0.), 1.01) with pytest.raises(RuntimeError, match='.* 15 MEG.*inside the scalp.*'): make_forward_solution(info, trans, fname_src, fname_bem) make_forward_solution(info, trans, fname_src, fname_bem_meg) # okay make_forward_solution(info, trans, fname_src, sphere_noshell) # okay with pytest.raises(RuntimeError, match='.* 42 MEG.*outermost sphere sh.*'): make_forward_solution(info, trans, fname_src, sphere) sphere = make_sphere_model((0., 0., 2.0), 1.01) # weird, but okay make_forward_solution(info, trans, fname_src, sphere) for ch in info['chs']: ch['loc'][:3] *= 0.1 with pytest.raises(RuntimeError, match='.* 42 MEG.*the inner skull.*'): make_forward_solution(info, trans, fname_src, fname_bem_meg)
def test_decimate(): """Test decimation of digitizer headshapes with too many points.""" # load headshape and convert to meters hsp_mm = _get_ico_surface(5)['rr'] * 100 hsp_m = hsp_mm / 1000. # save headshape to a file in mm in temporary directory tempdir = _TempDir() sphere_hsp_path = op.join(tempdir, 'test_sphere.txt') np.savetxt(sphere_hsp_path, hsp_mm) # read in raw data using spherical hsp, and extract new hsp with warnings.catch_warnings(record=True) as w: raw = read_raw_kit(sqd_path, mrk_path, elp_txt_path, sphere_hsp_path) assert_true(any('more than' in str(ww.message) for ww in w)) # collect headshape from raw (should now be in m) hsp_dec = np.array([dig['r'] for dig in raw.info['dig']])[8:] # with 10242 points and _decimate_points set to resolution of 5 mm, hsp_dec # should be a bit over 5000 points. If not, something is wrong or # decimation resolution has been purposefully changed assert_true(len(hsp_dec) > 5000) # should have similar size, distance from center dist = np.sqrt(np.sum((hsp_m - np.mean(hsp_m, axis=0))**2, axis=1)) dist_dec = np.sqrt(np.sum((hsp_dec - np.mean(hsp_dec, axis=0))**2, axis=1)) hsp_rad = np.mean(dist) hsp_dec_rad = np.mean(dist_dec) assert_almost_equal(hsp_rad, hsp_dec_rad, places=3)
def test_make_scalp_surfaces_topology(tmp_path, monkeypatch): """Test topology checks for make_scalp_surfaces.""" pytest.importorskip('pyvista') subjects_dir = tmp_path subject = 'test' surf_dir = subjects_dir / subject / 'surf' makedirs(surf_dir) surf = _get_ico_surface(2) surf['rr'] *= 100 # mm write_surface(surf_dir / 'lh.seghead', surf['rr'], surf['tris']) # make it so that decimation really messes up the mesh just by deleting # the last N tris def _decimate_surface(points, triangles, n_triangles): assert len(triangles) >= n_triangles return points, triangles[:n_triangles] monkeypatch.setattr(mne.bem, 'decimate_surface', _decimate_surface) # TODO: These two errors should probably have the same class... # Not enough neighbors monkeypatch.setattr(mne.bem, '_tri_levels', dict(sparse=315)) with pytest.raises(ValueError, match='.*have fewer than three.*'): make_scalp_surfaces(subject, subjects_dir, force=False, verbose=True) monkeypatch.setattr(mne.bem, '_tri_levels', dict(sparse=319)) # Incomplete surface (sum of solid angles) with pytest.raises(RuntimeError, match='.*is not complete.*'): make_scalp_surfaces(subject, subjects_dir, force=False, verbose=True, overwrite=True) bem_dir = subjects_dir / subject / 'bem' sparse_path = (bem_dir / f'{subject}-head-sparse.fif') assert not sparse_path.is_file() # These are ignorable monkeypatch.setattr(mne.bem, '_tri_levels', dict(sparse=315)) with pytest.warns(RuntimeWarning, match='.*have fewer than three.*'): make_scalp_surfaces(subject, subjects_dir, force=True, overwrite=True) surf, = read_bem_surfaces(sparse_path, on_defects='ignore') assert len(surf['tris']) == 315 monkeypatch.setattr(mne.bem, '_tri_levels', dict(sparse=319)) with pytest.warns(RuntimeWarning, match='.*is not complete.*'): make_scalp_surfaces(subject, subjects_dir, force=True, overwrite=True) surf, = read_bem_surfaces(sparse_path, on_defects='ignore') assert len(surf['tris']) == 319
def test_iterable(): """Test iterable support for simulate_raw.""" raw = read_raw_fif(raw_fname_short).load_data() raw.pick_channels(raw.ch_names[:10] + ['STI 014']) src = setup_volume_source_space( pos=dict(rr=[[-0.05, 0, 0], [0.1, 0, 0]], nn=[[0, 1., 0], [0, 1., 0]])) assert src.kind == 'discrete' trans = None sphere = make_sphere_model(head_radius=None, info=raw.info) tstep = 1. / raw.info['sfreq'] rng = np.random.RandomState(0) vertices = np.array([1]) data = rng.randn(1, 2) stc = VolSourceEstimate(data, vertices, 0, tstep) assert isinstance(stc.vertices, np.ndarray) with pytest.raises(ValueError, match='at least three time points'): simulate_raw(raw.info, stc, trans, src, sphere, None) data = rng.randn(1, 1000) n_events = (len(raw.times) - 1) // 1000 + 1 stc = VolSourceEstimate(data, vertices, 0, tstep) assert isinstance(stc.vertices, np.ndarray) with catch_logging() as log: with pytest.deprecated_call(): raw_sim = simulate_raw(raw, stc, trans, src, sphere, None, verbose=True) log = log.getvalue() assert 'Making 15 copies of STC' in log assert_allclose(raw.times, raw_sim.times) events = find_events(raw_sim, initial_event=True) assert len(events) == n_events assert_array_equal(events[:, 2], 1) # Degenerate STCs with pytest.raises(RuntimeError, match=r'Iterable did not provide stc\[0\]'): simulate_raw(raw.info, [], trans, src, sphere, None) with pytest.raises(RuntimeError, match=r'Iterable did not provide stc\[2\].*duration'): with pytest.deprecated_call(): simulate_raw(raw, [stc, stc], trans, src, sphere, None) # tuple with ndarray event_data = np.zeros(len(stc.times), int) event_data[0] = 3 raw_new = simulate_raw(raw.info, [(stc, event_data)] * 15, trans, src, sphere, None, first_samp=raw.first_samp) assert raw_new.n_times == 15000 raw_new.crop(0, raw_sim.times[-1]) _assert_iter_sim(raw_sim, raw_new, 3) with pytest.raises(ValueError, match='event data had shape .* but need'): simulate_raw(raw.info, [(stc, event_data[:-1])], trans, src, sphere, None) with pytest.raises(ValueError, match='stim_data in a stc tuple .* int'): simulate_raw(raw.info, [(stc, event_data * 1.)], trans, src, sphere, None) # iterable def stc_iter(): stim_data = np.zeros(len(stc.times), int) stim_data[0] = 4 ii = 0 while ii < 100: ii += 1 yield (stc, stim_data) with pytest.deprecated_call(): raw_new = simulate_raw(raw, stc_iter(), trans, src, sphere, None) _assert_iter_sim(raw_sim, raw_new, 4) def stc_iter_bad(): ii = 0 while ii < 100: ii += 1 yield (stc, 4, 3) with pytest.raises(ValueError, match='stc, if tuple, must be length'): simulate_raw(raw.info, stc_iter_bad(), trans, src, sphere, None) _assert_iter_sim(raw_sim, raw_new, 4) def stc_iter_bad(): ii = 0 while ii < 100: ii += 1 stc_new = stc.copy() stc_new.vertices = np.array([ii % 2]) yield stc_new with pytest.raises(RuntimeError, match=r'Vertex mismatch for stc\[1\]'): simulate_raw(raw.info, stc_iter_bad(), trans, src, sphere, None) # Forward omission vertices = np.array([0, 1]) data = rng.randn(2, 1000) stc = VolSourceEstimate(data, vertices, 0, tstep) assert isinstance(stc.vertices, np.ndarray) # XXX eventually we should support filtering based on sphere radius, too, # by refactoring the code in source_space.py that does it! surf = _get_ico_surface(3) surf['rr'] *= 60 # mm model = _surfaces_to_bem([surf], [FIFF.FIFFV_BEM_SURF_ID_BRAIN], [0.3]) bem = make_bem_solution(model) with pytest.warns(RuntimeWarning, match='1 of 2 SourceEstimate vertices'): simulate_raw(raw, stc, trans, src, bem, None)
def test_iterable(): """Test iterable support for simulate_raw.""" raw = read_raw_fif(raw_fname_short).load_data() raw.pick_channels(raw.ch_names[:10] + ['STI 014']) src = setup_volume_source_space( pos=dict(rr=[[-0.05, 0, 0], [0.1, 0, 0]], nn=[[0, 1., 0], [0, 1., 0]])) assert src.kind == 'discrete' trans = None sphere = make_sphere_model(head_radius=None, info=raw.info) tstep = 1. / raw.info['sfreq'] rng = np.random.RandomState(0) vertices = np.array([1]) data = rng.randn(1, 2) stc = VolSourceEstimate(data, vertices, 0, tstep) assert isinstance(stc.vertices, np.ndarray) with pytest.raises(ValueError, match='at least three time points'): simulate_raw(raw.info, stc, trans, src, sphere, None) data = rng.randn(1, 1000) n_events = (len(raw.times) - 1) // 1000 + 1 stc = VolSourceEstimate(data, vertices, 0, tstep) assert isinstance(stc.vertices, np.ndarray) with catch_logging() as log: with pytest.deprecated_call(): raw_sim = simulate_raw(raw, stc, trans, src, sphere, None, verbose=True) log = log.getvalue() assert 'Making 15 copies of STC' in log assert_allclose(raw.times, raw_sim.times) events = find_events(raw_sim, initial_event=True) assert len(events) == n_events assert_array_equal(events[:, 2], 1) # Degenerate STCs with pytest.raises(RuntimeError, match=r'Iterable did not provide stc\[0\]'): simulate_raw(raw.info, [], trans, src, sphere, None) with pytest.raises(RuntimeError, match=r'Iterable did not provide stc\[2\].*duration'): with pytest.deprecated_call(): simulate_raw(raw, [stc, stc], trans, src, sphere, None) # tuple with ndarray event_data = np.zeros(len(stc.times), int) event_data[0] = 3 raw_new = simulate_raw(raw.info, [(stc, event_data)] * 15, trans, src, sphere, None, first_samp=raw.first_samp) assert raw_new.n_times == 15000 raw_new.crop(0, raw_sim.times[-1]) _assert_iter_sim(raw_sim, raw_new, 3) with pytest.raises(ValueError, match='event data had shape .* but need'): simulate_raw(raw.info, [(stc, event_data[:-1])], trans, src, sphere, None) with pytest.raises(ValueError, match='stim_data in a stc tuple .* int'): simulate_raw(raw.info, [(stc, event_data * 1.)], trans, src, sphere, None) # iterable def stc_iter(): stim_data = np.zeros(len(stc.times), int) stim_data[0] = 4 ii = 0 while ii < 100: ii += 1 yield (stc, stim_data) with pytest.deprecated_call(): raw_new = simulate_raw(raw, stc_iter(), trans, src, sphere, None) _assert_iter_sim(raw_sim, raw_new, 4) def stc_iter_bad(): ii = 0 while ii < 100: ii += 1 yield (stc, 4, 3) with pytest.raises(ValueError, match='stc, if tuple, must be length'): simulate_raw(raw.info, stc_iter_bad(), trans, src, sphere, None) _assert_iter_sim(raw_sim, raw_new, 4) def stc_iter_bad(): ii = 0 while ii < 100: ii += 1 stc_new = stc.copy() stc_new.vertices = np.array([ii % 2]) yield stc_new with pytest.raises(RuntimeError, match=r'Vertex mismatch for stc\[1\]'): simulate_raw(raw.info, stc_iter_bad(), trans, src, sphere, None) # Forward omission vertices = np.array([0, 1]) data = rng.randn(2, 1000) stc = VolSourceEstimate(data, vertices, 0, tstep) assert isinstance(stc.vertices, np.ndarray) # XXX eventually we should support filtering based on sphere radius, too, # by refactoring the code in source_space.py that does it! surf = _get_ico_surface(3) surf['rr'] *= 60 # mm model = _surfaces_to_bem([surf], [FIFF.FIFFV_BEM_SURF_ID_BRAIN], [0.3]) bem = make_bem_solution(model) with pytest.warns(RuntimeWarning, match='1 of 2 SourceEstimate vertices'): simulate_raw(raw, stc, trans, src, bem, None)