{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "\n\n# Optically pumped magnetometer (OPM) data\n\nIn this dataset, electrical median nerve stimulation was delivered to the\nleft wrist of the subject. Somatosensory evoked fields were measured using\nnine QuSpin SERF OPMs placed over the right-hand side somatomotor area. Here\nwe demonstrate how to localize these custom OPM data in MNE.\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "# Authors: The MNE-Python contributors.\n# License: BSD-3-Clause\n# Copyright the MNE-Python contributors.\n\n\nimport numpy as np\n\nimport mne\n\ndata_path = mne.datasets.opm.data_path()\nsubject = \"OPM_sample\"\nsubjects_dir = data_path / \"subjects\"\nraw_fname = data_path / \"MEG\" / \"OPM\" / \"OPM_SEF_raw.fif\"\nbem_fname = subjects_dir / subject / \"bem\" / f\"{subject}-5120-5120-5120-bem-sol.fif\"\nfwd_fname = data_path / \"MEG\" / \"OPM\" / \"OPM_sample-fwd.fif\"\ncoil_def_fname = data_path / \"MEG\" / \"OPM\" / \"coil_def.dat\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Prepare data for localization\nFirst we filter and epoch the data:\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "raw = mne.io.read_raw_fif(raw_fname, preload=True)\nraw.filter(None, 90, h_trans_bandwidth=10.0)\nraw.notch_filter(50.0, notch_widths=1)\n\n\n# Set epoch rejection threshold a bit larger than for SQUIDs\nreject = dict(mag=2e-10)\ntmin, tmax = -0.5, 1\n\n# Find median nerve stimulator trigger\nevent_id = dict(Median=257)\nevents = mne.find_events(raw, stim_channel=\"STI101\", mask=257, mask_type=\"and\")\npicks = mne.pick_types(raw.info, meg=True, eeg=False)\n# We use verbose='error' to suppress warning about decimation causing aliasing,\n# ideally we would low-pass and then decimate instead\nepochs = mne.Epochs(\n raw,\n events,\n event_id,\n tmin,\n tmax,\n verbose=\"error\",\n reject=reject,\n picks=picks,\n proj=False,\n decim=10,\n preload=True,\n)\nevoked = epochs.average()\nevoked.plot()\ncov = mne.compute_covariance(epochs, tmax=0.0)\ndel epochs, raw" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Examine our coordinate alignment for source localization and compute a\nforward operator:\n\n

Note

The Head<->MRI transform is an identity matrix, as the\n co-registration method used equates the two coordinate\n systems. This mis-defines the head coordinate system\n (which should be based on the LPA, Nasion, and RPA)\n but should be fine for these analyses.

\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "bem = mne.read_bem_solution(bem_fname)\ntrans = mne.transforms.Transform(\"head\", \"mri\") # identity transformation\n\n# To compute the forward solution, we must\n# provide our temporary/custom coil definitions, which can be done as::\n#\n# with mne.use_coil_def(coil_def_fname):\n# fwd = mne.make_forward_solution(\n# raw.info, trans, src, bem, eeg=False, mindist=5.0,\n# n_jobs=None, verbose=True)\n\nfwd = mne.read_forward_solution(fwd_fname)\n# use fixed orientation here just to save memory later\nmne.convert_forward_solution(fwd, force_fixed=True, copy=False)\n\nwith mne.use_coil_def(coil_def_fname):\n fig = mne.viz.plot_alignment(\n evoked.info,\n trans=trans,\n subject=subject,\n subjects_dir=subjects_dir,\n surfaces=(\"head\", \"pial\"),\n bem=bem,\n )\n\nmne.viz.set_3d_view(\n figure=fig, azimuth=45, elevation=60, distance=0.4, focalpoint=(0.02, 0, 0.04)\n)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Perform dipole fitting\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "# Fit dipoles on a subset of time points\nwith mne.use_coil_def(coil_def_fname):\n dip_opm, _ = mne.fit_dipole(\n evoked.copy().crop(0.040, 0.080), cov, bem, trans, verbose=True\n )\nidx = np.argmax(dip_opm.gof)\nprint(\n f\"Best dipole at t={1000 * dip_opm.times[idx]:0.1f} ms with \"\n f\"{dip_opm.gof[idx]:0.1f}% GOF\"\n)\n\n# Plot N20m dipole as an example\ndip_opm.plot_locations(trans, subject, subjects_dir, mode=\"orthoview\", idx=idx)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Perform minimum-norm localization\nDue to the small number of sensors, there will be some leakage of activity\nto areas with low/no sensitivity. Constraining the source space to\nareas we are sensitive to might be a good idea.\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "inverse_operator = mne.minimum_norm.make_inverse_operator(\n evoked.info, fwd, cov, loose=0.0, depth=None\n)\ndel fwd, cov\n\nmethod = \"MNE\"\nsnr = 3.0\nlambda2 = 1.0 / snr**2\nstc = mne.minimum_norm.apply_inverse(\n evoked, inverse_operator, lambda2, method=method, pick_ori=None, verbose=True\n)\n\n# Plot source estimate at time of best dipole fit\nbrain = stc.plot(\n hemi=\"rh\",\n views=\"lat\",\n subjects_dir=subjects_dir,\n initial_time=dip_opm.times[idx],\n clim=dict(kind=\"percent\", lims=[99, 99.9, 99.99]),\n size=(400, 300),\n background=\"w\",\n)" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.12.2" } }, "nbformat": 4, "nbformat_minor": 0 }