def test_make_forward_dipole(): """Test forward-projecting dipoles.""" rng = np.random.RandomState(0) evoked = read_evokeds(fname_evo)[0] cov = read_cov(fname_cov) dip_c = read_dipole(fname_dip) # Only use magnetometers for speed! picks = pick_types(evoked.info, meg='mag', eeg=False) evoked.pick_channels([evoked.ch_names[p] for p in picks]) info = evoked.info # Make new Dipole object with n_test_dipoles picked from the dipoles # in the test dataset. n_test_dipoles = 3 # minimum 3 needed to get uneven sampling in time dipsel = np.sort(rng.permutation(np.arange(len(dip_c)))[:n_test_dipoles]) dip_test = Dipole(times=dip_c.times[dipsel], pos=dip_c.pos[dipsel], amplitude=dip_c.amplitude[dipsel], ori=dip_c.ori[dipsel], gof=dip_c.gof[dipsel]) sphere = make_sphere_model(head_radius=0.1) # Warning emitted due to uneven sampling in time with warnings.catch_warnings(record=True) as w: fwd, stc = make_forward_dipole(dip_test, sphere, info, trans=fname_trans) assert_true(issubclass(w[-1].category, RuntimeWarning)) # stc is list of VolSourceEstimate's assert_true(isinstance(stc, list)) for nd in range(n_test_dipoles): assert_true(isinstance(stc[nd], VolSourceEstimate)) # Now simulate evoked responses for each of the test dipoles, # and fit dipoles to them (sphere model, MEG and EEG) times, pos, amplitude, ori, gof = [], [], [], [], [] snr = 20. # add a tiny amount of noise to the simulated evokeds for s in stc: evo_test = simulate_evoked(fwd, s, info, cov, snr=snr, random_state=rng) # evo_test.add_proj(make_eeg_average_ref_proj(evo_test.info)) dfit, resid = fit_dipole(evo_test, cov, sphere, None) times += dfit.times.tolist() pos += dfit.pos.tolist() amplitude += dfit.amplitude.tolist() ori += dfit.ori.tolist() gof += dfit.gof.tolist() # Create a new Dipole object with the dipole fits dip_fit = Dipole(times, pos, amplitude, ori, gof) # check that true (test) dipoles and fits are "close" # cf. mne/tests/test_dipole.py diff = dip_test.pos - dip_fit.pos corr = np.corrcoef(dip_test.pos.ravel(), dip_fit.pos.ravel())[0, 1] dist = np.sqrt(np.mean(np.sum(diff * diff, axis=1))) gc_dist = 180 / np.pi * \ np.mean(np.arccos(np.sum(dip_test.ori * dip_fit.ori, axis=1))) amp_err = np.sqrt(np.mean((dip_test.amplitude - dip_fit.amplitude)**2)) # Make sure each coordinate is close to reference # NB tolerance should be set relative to snr of simulated evoked! assert_allclose(dip_fit.pos, dip_test.pos, rtol=0, atol=1e-2, err_msg='position mismatch') assert_true(dist < 1e-2, 'dist: %s' % dist) # within 1 cm assert_true(corr > 1 - 1e-2, 'corr: %s' % corr) assert_true(gc_dist < 20, 'gc_dist: %s' % gc_dist) # less than 20 degrees assert_true(amp_err < 10e-9, 'amp_err: %s' % amp_err) # within 10 nAm # Make sure rejection works with BEM: one dipole at z=1m # NB _make_forward.py:_prepare_for_forward will raise a RuntimeError # if no points are left after min_dist exclusions, hence 2 dips here! dip_outside = Dipole(times=[0., 0.001], pos=[[0., 0., 1.0], [0., 0., 0.040]], amplitude=[100e-9, 100e-9], ori=[[1., 0., 0.], [1., 0., 0.]], gof=1) assert_raises(ValueError, make_forward_dipole, dip_outside, fname_bem, info, fname_trans) # if we get this far, can safely assume the code works with BEMs too # -> use sphere again below for speed # Now make an evenly sampled set of dipoles, some simultaneous, # should return a VolSourceEstimate regardless times = [0., 0., 0., 0.001, 0.001, 0.002] pos = np.random.rand(6, 3) * 0.020 + \ np.array([0., 0., 0.040])[np.newaxis, :] amplitude = np.random.rand(6) * 100e-9 ori = np.eye(6, 3) + np.eye(6, 3, -3) gof = np.arange(len(times)) / len(times) # arbitrary dip_even_samp = Dipole(times, pos, amplitude, ori, gof) fwd, stc = make_forward_dipole(dip_even_samp, sphere, info, trans=fname_trans) assert_true(isinstance, VolSourceEstimate) assert_allclose(stc.times, np.arange(0., 0.003, 0.001))
def test_make_forward_dipole(tmp_path): """Test forward-projecting dipoles.""" rng = np.random.RandomState(0) evoked = read_evokeds(fname_evo)[0] cov = read_cov(fname_cov) cov['projs'] = [] # avoid proj warning dip_c = read_dipole(fname_dip) # Only use magnetometers for speed! picks = pick_types(evoked.info, meg='mag', eeg=False)[::8] evoked.pick_channels([evoked.ch_names[p] for p in picks]) evoked.info.normalize_proj() info = evoked.info # Make new Dipole object with n_test_dipoles picked from the dipoles # in the test dataset. n_test_dipoles = 3 # minimum 3 needed to get uneven sampling in time dipsel = np.sort(rng.permutation(np.arange(len(dip_c)))[:n_test_dipoles]) dip_test = Dipole(times=dip_c.times[dipsel], pos=dip_c.pos[dipsel], amplitude=dip_c.amplitude[dipsel], ori=dip_c.ori[dipsel], gof=dip_c.gof[dipsel]) sphere = make_sphere_model(head_radius=0.1) # Warning emitted due to uneven sampling in time with pytest.warns(RuntimeWarning, match='unevenly spaced'): fwd, stc = make_forward_dipole(dip_test, sphere, info, trans=fname_trans) # stc is list of VolSourceEstimate's assert isinstance(stc, list) for n_dip in range(n_test_dipoles): assert isinstance(stc[n_dip], VolSourceEstimate) # Now simulate evoked responses for each of the test dipoles, # and fit dipoles to them (sphere model, MEG and EEG) times, pos, amplitude, ori, gof = [], [], [], [], [] nave = 200 # add a tiny amount of noise to the simulated evokeds for s in stc: evo_test = simulate_evoked(fwd, s, info, cov, nave=nave, random_state=rng) # evo_test.add_proj(make_eeg_average_ref_proj(evo_test.info)) dfit, resid = fit_dipole(evo_test, cov, sphere, None) times += dfit.times.tolist() pos += dfit.pos.tolist() amplitude += dfit.amplitude.tolist() ori += dfit.ori.tolist() gof += dfit.gof.tolist() # Create a new Dipole object with the dipole fits dip_fit = Dipole(times, pos, amplitude, ori, gof) # check that true (test) dipoles and fits are "close" # cf. mne/tests/test_dipole.py diff = dip_test.pos - dip_fit.pos corr = np.corrcoef(dip_test.pos.ravel(), dip_fit.pos.ravel())[0, 1] dist = np.sqrt(np.mean(np.sum(diff * diff, axis=1))) gc_dist = 180 / np.pi * \ np.mean(np.arccos(np.sum(dip_test.ori * dip_fit.ori, axis=1))) amp_err = np.sqrt(np.mean((dip_test.amplitude - dip_fit.amplitude)**2)) # Make sure each coordinate is close to reference # NB tolerance should be set relative to snr of simulated evoked! assert_allclose(dip_fit.pos, dip_test.pos, rtol=0, atol=1e-2, err_msg='position mismatch') assert dist < 1e-2 # within 1 cm assert corr > 0.985 assert gc_dist < 20 # less than 20 degrees assert amp_err < 10e-9 # within 10 nAm # Make sure rejection works with BEM: one dipole at z=1m # NB _make_forward.py:_prepare_for_forward will raise a RuntimeError # if no points are left after min_dist exclusions, hence 2 dips here! dip_outside = Dipole(times=[0., 0.001], pos=[[0., 0., 1.0], [0., 0., 0.040]], amplitude=[100e-9, 100e-9], ori=[[1., 0., 0.], [1., 0., 0.]], gof=1) with pytest.raises(ValueError, match='outside the inner skull'): make_forward_dipole(dip_outside, fname_bem, info, fname_trans) # if we get this far, can safely assume the code works with BEMs too # -> use sphere again below for speed # Now make an evenly sampled set of dipoles, some simultaneous, # should return a VolSourceEstimate regardless times = [0., 0., 0., 0.001, 0.001, 0.002] pos = np.random.rand(6, 3) * 0.020 + \ np.array([0., 0., 0.040])[np.newaxis, :] amplitude = np.random.rand(6) * 100e-9 ori = np.eye(6, 3) + np.eye(6, 3, -3) gof = np.arange(len(times)) / len(times) # arbitrary dip_even_samp = Dipole(times, pos, amplitude, ori, gof) # I/O round-trip fname = str(tmp_path / 'test-fwd.fif') with pytest.warns(RuntimeWarning, match='free orientation'): write_forward_solution(fname, fwd) fwd_read = convert_forward_solution(read_forward_solution(fname), force_fixed=True) assert_forward_allclose(fwd, fwd_read, rtol=1e-6) fwd, stc = make_forward_dipole(dip_even_samp, sphere, info, trans=fname_trans) assert isinstance(stc, VolSourceEstimate) assert_allclose(stc.times, np.arange(0., 0.003, 0.001)) # Test passing a list of Dipoles instead of a single Dipole object fwd2, stc2 = make_forward_dipole([dip_even_samp[0], dip_even_samp[1:]], sphere, info, trans=fname_trans) assert_array_equal(fwd['sol']['data'], fwd2['sol']['data']) assert_array_equal(stc.data, stc2.data)