""" .. _ex-opm-somatosensory: Optically pumped magnetometer (OPM) data ======================================== In this dataset, electrical median nerve stimulation was delivered to the left wrist of the subject. Somatosensory evoked fields were measured using nine QuSpin SERF OPMs placed over the right-hand side somatomotor area. Here we demonstrate how to localize these custom OPM data in MNE. """ # Authors: The MNE-Python contributors. # License: BSD-3-Clause # Copyright the MNE-Python contributors. # sphinx_gallery_thumbnail_number = 4 import numpy as np import mne data_path = mne.datasets.opm.data_path() subject = "OPM_sample" subjects_dir = data_path / "subjects" raw_fname = data_path / "MEG" / "OPM" / "OPM_SEF_raw.fif" bem_fname = subjects_dir / subject / "bem" / f"{subject}-5120-5120-5120-bem-sol.fif" fwd_fname = data_path / "MEG" / "OPM" / "OPM_sample-fwd.fif" coil_def_fname = data_path / "MEG" / "OPM" / "coil_def.dat" # %% # Prepare data for localization # ----------------------------- # First we filter and epoch the data: raw = mne.io.read_raw_fif(raw_fname, preload=True) raw.filter(None, 90, h_trans_bandwidth=10.0) raw.notch_filter(50.0, notch_widths=1) # Set epoch rejection threshold a bit larger than for SQUIDs reject = dict(mag=2e-10) tmin, tmax = -0.5, 1 # Find median nerve stimulator trigger event_id = dict(Median=257) events = mne.find_events(raw, stim_channel="STI101", mask=257, mask_type="and") picks = mne.pick_types(raw.info, meg=True, eeg=False) # We use verbose='error' to suppress warning about decimation causing aliasing, # ideally we would low-pass and then decimate instead epochs = mne.Epochs( raw, events, event_id, tmin, tmax, verbose="error", reject=reject, picks=picks, proj=False, decim=10, preload=True, ) evoked = epochs.average() evoked.plot() cov = mne.compute_covariance(epochs, tmax=0.0) del epochs, raw # %% # Examine our coordinate alignment for source localization and compute a # forward operator: # # .. note:: The Head<->MRI transform is an identity matrix, as the # co-registration method used equates the two coordinate # systems. This mis-defines the head coordinate system # (which should be based on the LPA, Nasion, and RPA) # but should be fine for these analyses. bem = mne.read_bem_solution(bem_fname) trans = mne.transforms.Transform("head", "mri") # identity transformation # To compute the forward solution, we must # provide our temporary/custom coil definitions, which can be done as:: # # with mne.use_coil_def(coil_def_fname): # fwd = mne.make_forward_solution( # raw.info, trans, src, bem, eeg=False, mindist=5.0, # n_jobs=None, verbose=True) fwd = mne.read_forward_solution(fwd_fname) # use fixed orientation here just to save memory later mne.convert_forward_solution(fwd, force_fixed=True, copy=False) with mne.use_coil_def(coil_def_fname): fig = mne.viz.plot_alignment( evoked.info, trans=trans, subject=subject, subjects_dir=subjects_dir, surfaces=("head", "pial"), bem=bem, ) mne.viz.set_3d_view( figure=fig, azimuth=45, elevation=60, distance=0.4, focalpoint=(0.02, 0, 0.04) ) # %% # Perform dipole fitting # ---------------------- # Fit dipoles on a subset of time points with mne.use_coil_def(coil_def_fname): dip_opm, _ = mne.fit_dipole( evoked.copy().crop(0.040, 0.080), cov, bem, trans, verbose=True ) idx = np.argmax(dip_opm.gof) print( f"Best dipole at t={1000 * dip_opm.times[idx]:0.1f} ms with " f"{dip_opm.gof[idx]:0.1f}% GOF" ) # Plot N20m dipole as an example dip_opm.plot_locations(trans, subject, subjects_dir, mode="orthoview", idx=idx) # %% # Perform minimum-norm localization # --------------------------------- # Due to the small number of sensors, there will be some leakage of activity # to areas with low/no sensitivity. Constraining the source space to # areas we are sensitive to might be a good idea. inverse_operator = mne.minimum_norm.make_inverse_operator( evoked.info, fwd, cov, loose=0.0, depth=None ) del fwd, cov method = "MNE" snr = 3.0 lambda2 = 1.0 / snr**2 stc = mne.minimum_norm.apply_inverse( evoked, inverse_operator, lambda2, method=method, pick_ori=None, verbose=True ) # Plot source estimate at time of best dipole fit brain = stc.plot( hemi="rh", views="lat", subjects_dir=subjects_dir, initial_time=dip_opm.times[idx], clim=dict(kind="percent", lims=[99, 99.9, 99.99]), size=(400, 300), background="w", )