Transcranial focused ultrasound induces source localizable cortical activation in resting state humans when applied concurrently with transcranial electric stimulation.
The 9 matches · 2 of them tie a paragraph to a whole file, not to given lines: weak matches, whose lines are not tinted
- [1] § Methods › EEG analysis › Linear mixed effect modeling ↔ lmer.R, the whole file · a weak match · score 0.98 · square root transformed, natural log transformed, classification metric, strictly positive, inclusively bound, ROI activity
- [2] § Methods › EEG analysis › EEG preprocessing ↔ python_utils/preprocessing_utils.py, lines 15–100 · score 0.97 · bandpass filtered, 1–40 Hz, muscle artifacts, peak amplitude, Bad channels, tDCS
- [3] § Methods › EEG analysis › EEG source imaging ↔ python_utils/mne_utils.py, lines 441–509 · score 0.89 · noise covariance matrix, head models, dSPM, EEG electrodes, EEG source imaging, MNE Python
- [4] § Methods › EEG analysis › Connectivity analysis ↔ python_utils/stats_utils.py, lines 252–310 · score 0.81 · power spectral density, Imaginary coherence, ImCoh, Welch, 1 second, window
- [5] § Methods › EEG analysis › EEG source imaging ↔ python_utils/mne_utils.py, lines 625–747 · score 0.65 · bounding box, projecting electrodes, inside, cortex, space, modeling
- [6] § Methods › EEG analysis › Linear mixed effect modeling ↔ lmer.R, the whole file · a weak match · score 0.61 · lme4, ROI activity, recall, variables, linear, precision
- [7] § Results › itFUS, but not tEAS, induces endogenous dissociations between the ROI and deep brain ↔ python_utils/stats_utils.py, lines 252–310 · score 0.55 · imaginary coherence, ImCoh, spectral
- [8] § Methods › EEG analysis › Connectivity analysis ↔ python_utils/stats_utils.py, lines 480–548 · score 0.54 · Granger causality, Toda, Yamamoto, Wald, lag
- [9] § Results › Increased tFUS parameter searches continued to yield no evidence of ROI suprathreshold stimulation ↔ python_utils/mne_utils.py, lines 399–423 · score 0.52 · FSAverage, source morphed, brain model, spaces
Paper
Loaded from Europe PMC by your browser, not stored by OSCR: doi.org · Europe PMC
The paper is loaded when this pane is shown.
The authors' code
Python · 747 lines · 27 KB · MIT · 3 matches
- # -*- coding: utf-8 -*-
- """
- Helper functions for interacting with MNE Python objects.
- Written by Joshua Kosnoff
- [email hidden]
- """
- import mne
- import numpy as np
- import nibabel.freesurfer as fs
- import os
- import platform
- import datetime
- import typing
- import shutil
- mne.set_log_level('CRITICAL')
- def evoked_to_epoch(mne_evoked):
- """
- Convert an instance of an MNE evoked into an MNE Epoch.
- It is common practice to take the average (or evoked) response over
- several trials for EEG analysis. This can be done with Epoch.average() in
- standard MNE usage. However, once averaged into an Evoked structure,
- the data have limited functionality. This will convert an Evoked object
- into an Epoched object to maintain functionality.
- Parameters
- ----------
- mne_evoked : mne.Evoked
- The evoked data structure.
- Returns
- -------
- epoch : mne.Epochs
- The data now within an epoched format.
- """
- data_shape = mne_evoked.get_data().shape
- epoch = mne.EpochsArray(mne_evoked.get_data().reshape(1, data_shape[0], data_shape[1]),
- mne_evoked.info, tmin=mne_evoked.times[0])
- return epoch
- def plot_joint_zscored(mne_epoch, times=[],
- title=None,
- ylims=[None, None],
- topomap_args={}):
- """
- A wrapper function for MNE's plot_joint that correctly scales the axes.
- MNE Python's data structure assumes data are stored in V and wants to plot
- in uV, so plotting functions default to multiplying by 1e6. If data were
- z-scored, they do not need this adjustment. This will pre-emptively
- multiply data by 1e-6 and re-label the y-axis.
- Parameters
- ----------
- mne_epoch : mne.Epochs
- The epoched data.
- times : list
- A list of times to show the spatial topomap distribution. Optional.
- ylims : list.
- A list of [ymin, ymax] for imposing on the final plot. Optional.
- topomap_args : dict
- A dictionary of kwargs to pass to the plot_joint function. Optional.
- Returns
- -------
- None.
- """
- _epochs = mne_epoch.copy()
- _epochs._data *= 1e-6
- fig = _epochs.copy().average().plot_joint(times=times, show=False, title=title,
- topomap_args=topomap_args)
- fig.axes[-3].set_ylabel('Z-Score')
- fig.axes[-3].set_ylim(ylims[0], ylims[1])
- fig.delaxes(fig.axes[-1])
- return fig
- def plot_butterfly_zscored(mne_epoch, ylims=[None, None], title='',
- ax=None):
- """
- A wrapper function for MNE's plot that correctly scales the axes.
- MNE Python's data structure assumes data are stored in V and wants to plot
- in uV, so plotting functions default to multiplying by 1e6. If data were
- z-scored, they do not need this adjustment. This will pre-emptively
- multiply data by 1e-6 and re-label the y-axis.
- Parameters
- ----------
- mne_epoch : mne.Epochs
- The epoched data.
- ylims : list.
- A list of [ymin, ymax] for imposing on the final plot. Optional.
- title : str
- Title for the plot. Optional.
- ax : matplotlib.axes.Axes
- An existing axis to plot the data. Optional.
- Returns
- -------
- None.
- """
- _epochs = mne_epoch.copy()
- _epochs._data *= 1e-6
- fig = _epochs.copy().average().plot(show=False, axes=ax)
- fig.axes[0].set_title(title)
- fig.axes[0].set_ylabel('Z-Score')
- fig.axes[0].set_ylim(ylims[0], ylims[1])
- return
- def get_loc_error(stc, label, src):
- """
- Calculate the localization error between the highest amplitude voxel and the target label.
- Parameters
- ----------
- stc : mne.SourceEstimate
- The subject's source estimate
- label : mne.Label
- The 'ground truth'
- src : mne.SourceSpace
- The subject's source space.
- Returns
- -------
- localization error : np.float
- The minimum distance from the maximum voxel to the ground truth. Units are in m.
- """
- vertices_lh, vertices_rh = stc.vertices
- coords_lh = src[0]["rr"][vertices_lh] # Left hemisphere
- coords_rh = src[1]["rr"][vertices_rh] # Right hemisphere
- # Max Voxel x/y/z
- max_voxel_loc = np.vstack((coords_lh, coords_rh))[
- stc._data.argmax(axis=0)[0], :]
- # Target x/y/z (Take the geometric mean)
- label_verts = label.vertices
- if label.hemi == 'lh':
- target_loc = src[0]["rr"][label_verts]
- elif label.hemi == 'rh':
- target_loc = src[1]["rr"][label_verts]
- return np.linalg.norm(max_voxel_loc - target_loc, axis=1).min()
- def zscore_evoked(mne_epoch, baseline_win=[-0.500, -0.100]):
- """
- Z-score baseline correct an epoch based on the trial average.
- Steps:
- 1. Average all trials into an evoked waveform.
- 2. Z-score the evoked waveform w.r.t. baseline period
- Parameters
- ----------
- mne_epoch : mne.Epochs
- The epoched data.
- baseline_win : list
- The list of times for the start and of the baseline period. Used for
- determine z-score mu and std.
- Returns
- -------
- mne_epoch : mne.Epochs
- The trial_averaged and z-scored data.
- """
- mne_epoch = evoked_to_epoch(mne_epoch.average())
- mne_epoch = mne_epoch.apply_function(
- fun=mne.baseline.rescale,
- times=mne_epoch.times,
- baseline=(baseline_win[0], baseline_win[1]),
- mode='zscore')
- return mne_epoch
- class SourceImage:
- """
- A comprehensive class for EEG source imaging using MNE Python.
- This class provides methods for coregistration, source space setup,
- and source imaging for EEG data.
- """
- def __init__(
- self,
- subject: str,
- subjects_dir: str,
- nasion: str = "NAS",
- surface='pial',
- rpa: str = "RPA",
- lpa: str = "LPA",
- bem_solver: str = "mne",
- src_spacing: str = "oct6",
- n_jobs: int = -1,
- ):
- """
- Initialize the SourceImage object.
- Parameters
- ----------
- subject : str
- The subject's ID.
- subjects_dir : str
- Path to the FreeSurfer processed MRI directory.
- nasion : str, optional
- Landmark key for nasion in digitization file.
- rpa : str, optional
- Landmark key for right preauricular point.
- lpa : str, optional
- Landmark key for left preauricular point.
- bem_solver : str, optional
- BEM algorithm to use ('mne' or 'openmeeg').
- src_spacing : str, optional
- Source space spacing/downsampling.
- n_jobs : int, optional
- Number of parallel processes to use.
- """
- self.subject = subject
- self.subjects_dir = subjects_dir
- self.save_folder = fr'{self.subjects_dir}/{subject}/mne_fifs'
- if not os.path.exists(self.save_folder):
- try:
- os.makedirs(self.save_folder)
- except FileExistsError:
- pass
- self.nasion = nasion
- self.rpa = rpa
- self.lpa = lpa
- self.bem_solver = bem_solver
- self.src_spacing = src_spacing
- self.n_jobs = n_jobs
- # Additional attributes to be set during processing
- self.info = None
- self.src = None
- self.fwd = None
- self.inv = None
- self.surface = surface
- def auto_coregister(
- self,
- save_path: str = "",
- session_id: str = "",
- fiducials: str = "auto",
- ):
- """
- Create a coregistration file for the subject's fiducial points.
- Parameters
- ----------
- save_path : str, optional
- Path to save the coregistration file.
- session_id : str, optional
- Session identifier for file naming.
- fiducials : str, optional
- Fiducial point specification.
- Returns
- -------
- mne.transforms.Transform or None
- The transformation if successful, None otherwise.
- """
- try:
- if not self.info:
- raise ValueError("EEG info must be set before coregistration")
- coreg = mne.coreg.Coregistration(
- self.info,
- self.subject,
- os.path.join(self.subjects_dir, ""),
- fiducials=fiducials,
- )
- coreg = coreg.omit_head_shape_points(
- distance=5.0 / 1000
- ) # distance in meters
- coreg = coreg.fit_icp(n_iterations=100, nasion_weight=100.0,
- rpa_weight=25.0,
- lpa_weight=25.0, verbose=True)
- mne.write_trans(save_path, coreg.trans, overwrite=True)
- except Exception as e:
- print(f"Coregistration error: {e}")
- def setup_source_space(
- self,
- head_model: str,
- session_id: str,
- conductivity: tuple = (0.3, 0.006, 0.3),
- ) -> None:
- """
- Set up source space and forward solution for source imaging.
- Parameters
- ----------
- head_model : str
- MRI file ID to use for the subject.
- session_id : str
- Session identifier.
- conductivity : tuple, optional
- Conductivity values for head model layers.
- """
- # BEM file handling
- # BEM is based on MRI --> don't need to resolve BEM for every session
- bem_fif = os.path.join(self.save_folder, f"{head_model}-bem.fif")
- if os.path.isfile(bem_fif):
- bem = mne.read_bem_solution(bem_fif)
- else:
- print(
- f"Writing BEM file for {os.path.basename(bem_fif).split('-')[0]}"
- )
- model = mne.make_bem_model(
- subject=head_model,
- ico=4,
- conductivity=conductivity,
- subjects_dir=self.subjects_dir,
- )
- bem = mne.make_bem_solution(model, solver=self.bem_solver)
- mne.write_bem_solution(bem_fif, bem)
- # Coregistration file handling
- subject_trans_fif = os.path.join(
- self.save_folder, f"{head_model}_{session_id}-trans.fif"
- )
- if not os.path.isfile(subject_trans_fif):
- self.auto_coregister(
- save_path=subject_trans_fif, session_id=session_id
- )
- # If accessing files via Box, Windows os seem to need assistance in
- # pre-downloading certain files.
- # Download the necessary file from Box drive in order to run
- # This step is only necessary if trying to access models on Box
- p1 = self.subjects_dir + r"/" + head_model + r"/surf/lh.pial"
- p2 = self.subjects_dir + r"/" + head_model + r"/surf/lh.pial.T1"
- try:
- # Try opening the correctly named file
- with open(p1, "r") as _:
- pass
- except FileNotFoundError:
- # If the correct file doesn't exist, check if the incorrectly named file exists
- if os.path.exists(p2):
- # Copy the incorrectly named file to the correct name
- shutil.copy(p2, p1)
- p1 = self.subjects_dir + r"/" + head_model + r"/surf/rh.pial"
- p2 = self.subjects_dir + r"/" + head_model + r"/surf/rh.pial.T1"
- try:
- # Try opening the correctly named file
- with open(p1, "r") as _:
- pass
- except FileNotFoundError:
- # If the correct file doesn't exist, check if the incorrectly named file exists
- if os.path.exists(p2):
- # Copy the incorrectly named file to the correct name
- shutil.copy(p2, p1)
- # Setup source space and forward solution
- self.src = mne.setup_source_space(
- subject=head_model,
- spacing=self.src_spacing,
- surface=self.surface, # formerly inflated?
- add_dist="patch",
- subjects_dir=self.subjects_dir,
- )
- self.fwd = mne.make_forward_solution(
- self.info,
- trans=subject_trans_fif,
- src=self.src,
- bem=bem_fif,
- eeg=True,
- meg=False,
- mindist=5.0,
- n_jobs=self.n_jobs,
- )
- def morph_to_FS(
- self, stc: mne.SourceEstimate, spacing=5
- ) -> mne.SourceEstimate:
- """
- Compute a source-morph of a source estimate to the fsaverage brain model.
- Parameters
- ----------
- stc : mne.SourceEstimate
- The source estimate to source-morph
- Returns
- -------
- fs_morph : mne.SourceEstimate
- The source estimate morphed to the FSAverage brain model.
- """
- fs_morph = mne.compute_source_morph(
- self.inv["src"],
- subject_from=self.subject,
- subjects_dir=self.subjects_dir,
- subject_to="fsaverage",
- verbose="error",
- spacing=spacing,
- ).apply(stc)
- return fs_morph
- def _spatial_smooth_stc(self, stc, smooth):
- """
- Spatially smooth the stc by morphing it to itself.
- """
- stc = mne.compute_source_morph(
- self.inv["src"],
- subject_from=self.subject,
- subjects_dir=self.subjects_dir,
- subject_to=self.subject,
- verbose="error",
- src_to=self.inv["src"],
- smooth=smooth,
- ).apply(stc)
- return stc
- def basic_esi_from_epoch(
- self,
- epoch: mne.Epochs,
- montage: typing.Union[str, mne.channels.DigMontage],
- method: str = "MNE",
- head_model: str = "auto",
- baseline: typing.List[float] = [None, 0],
- lambda2: float = 1 / 9,
- label: typing.Optional[mne.Label] = None,
- zscore_prestim: bool = False,
- noise_cov: typing.Optional[mne.Covariance] = None,
- average: typing.Union[bool, str] = 'mean',
- conductivity: tuple = (0.3, 0.006, 0.3),
- smooth: int = 15,
- return_generator: bool = False,
- pick_ori=None,
- ) -> mne.SourceEstimate:
- """
- Perform EEG Source Imaging on epoched data.
- Cribbed heavily from various MNE Python tutorials. Functionalized by Joshua Kosnoff.
- Parameters
- ----------
- epoch : mne.Epoch
- The epoched data to source image.
- montage : path-like \ str
- The montage to use for the EEG electrodes in leadfield modeling. If using a custom montage, provide the the path to
- the EEG digitization file. Otherwise, indicate the string of the MNE Python premade montage to use. See
- mne.channels.get_builtin_montages() for all the current predefined options. Please note that, currently, this function
- only accepts custom Localite RAS digitization files. However, MNE Python does have additional support for other localization
- software. Please check their documentation and edit the read_dig_localite function accordingly.
- method : str
- The ESI method. MNE Python Currently supports 'MNE', 'dSPM', 'sLORETA', and 'eLORETA'.
- head_model : str
- The name of the MRI file ID to use for the subject. 'auto' will use the subject ID defined in the class initialization. In most
- cases, this will be correct. In some cases, some subjects may not have MRI files, in which case other head models may be specified.
- Defaults to 'auto'.
- baseline : list[float, float]
- The time points (in seconds) to use as the start and end of the epoch baseline period. Example: [0, 10.8]
- Default is [None, 0], which will consider all the prestimulus activity.
- lambda2 : float
- The regularization parameter. Defaults to 1/9 based on the MNE Python tutorials.
- label : mne.Label \ None
- If not None, restrict the source estimate to only the voxels within the label. Default is None, which returns the whole brain.
- zscore_prestim : bool
- Whether to zscore the EEG data based on prestimulus period (up to time 0) before performing ESI calculations.
- Default is False.
- noise_cov: mne.Covariance / None
- The noise covariance of the data (if precomputed). If None, it will calculate a noise covariance matrix based on the
- prestimulus baseline. Default is None.
- average : bool
- If True, will compute the source image on the average of the epochs (aka the evoked waveform), as opposed to on each
- epoch seperately. Defaults to True.
- conductivity : Tuple(float)
- The conductivities to use for each layer of the head model. Defaults are from MNE Python tutorials.
- smooth :
- return_generator : bool
- Only used if average is False. Returns a generator object of stcs and not a list of stcs to preserve memory.
- Returns
- -------
- stc : mne.SourceEstimate
- The source estimate for the subject's experimental condition.
- """
- assert montage in mne.channels.get_builtin_montages() or os.path.exists(
- montage
- ), "Error! Expected 'montage' to either be a path to digitization file or a built-in MNE Python montage."
- # Similar implementation to the original method
- # ... (with added type checking and error handling)
- epoch = epoch.copy()
- if head_model == "auto":
- head_model = self.subject
- # If using a custom head model, the BEM modeling needs to be
- if montage not in mne.channels.get_builtin_montages():
- # If there is a measurement date in the MNE Epoch's meta date, use
- # that for the session ID. If not, use the file creation date for the
- # RAS file.
- # Note that this session ID naming convention does assuming that
- # each subject will only have 1 digitization per day. If this is not
- # the case, will probably want to do some kind of additional constraint.
- dt = epoch.info["meas_date"]
- if dt is None:
- system = platform.system()
- if system == "Windows":
- c_time = os.path.getctime(montage)
- else:
- c_time = os.stat(montage).st_birthtime
- dt = datetime.datetime.fromtimestamp(c_time)
- session_id = dt.strftime("%Y_%m_%d")
- else:
- session_id = montage
- # Load the RAS montage
- # In our lab, we save RPA, LPA, and NAS with those keys. We need to explicitly pass those
- # into the read_dig_localite function.
- if montage not in mne.channels.get_builtin_montages():
- mont = mne.channels.read_dig_localite(
- montage, nasion=self.nasion, lpa=self.lpa, rpa=self.rpa
- )
- epoch.set_montage(mont, match_case=False, on_missing="warn")
- else:
- epoch.set_montage(montage, match_case=False, on_missing="warn")
- self.info = epoch.info
- self.setup_source_space(head_model=head_model, session_id=session_id)
- if zscore_prestim:
- epoch_prestim = epoch.copy().crop(baseline[0], baseline[1])._data
- epoch._data = (
- epoch._data - epoch_prestim.mean(axis=-1, keepdims=True)
- ) / epoch_prestim.std(axis=-1, keepdims=True)
- # Create Common Average Reference (reference is required for ESI)
- epoch.set_eeg_reference("average", projection=True)
- # Compuse noise matrix based on the the stimulus baseline
- # Only do this if there is no passed noise covariance
- if type(noise_cov) == type(None):
- # print("Computing noise covariance . . . ")
- noise = epoch.copy().crop(
- baseline[0], baseline[1], include_tmax=False
- )
- noise_cov = mne.compute_covariance(
- noise, method=["shrunk", "empirical"], rank=None
- )
- # Make the inverse operator
- self.inv = mne.minimum_norm.make_inverse_operator(
- self.info, self.fwd, noise_cov
- )
- # Calculate the time series
- if average:
- if method in ['dSPM', 'MNE', 'sLORETA', 'eLORETA']:
- stc = mne.minimum_norm.apply_inverse(
- epoch.average(method=average),
- self.inv,
- lambda2=lambda2,
- method=method,
- label=label,
- pick_ori=pick_ori,
- )
- elif method == 'gamma':
- stc = mne.inverse_sparse.gamma_map(
- epoch.average(method=average),
- forward=self.fwd,
- noise_cov=noise_cov,
- alpha=lambda2,
- )
- elif method == 'TF-MxNE':
- stc = mne.inverse_sparse.tf_mixed_norm(
- epoch.average(method=average),
- forward=self.fwd,
- noise_cov=noise_cov,
- alpha=40, # lambda2,
- l1_ratio=0.05, # ??
- )
- elif method == 'MxNE':
- stc = mne.inverse_sparse.mixed_norm(
- epoch.average(method=average),
- forward=self.fwd,
- noise_cov=noise_cov,
- alpha=lambda2,
- )
- else:
- stc = mne.minimum_norm.apply_inverse_epochs(
- epoch, self.inv, lambda2=lambda2, method=method, label=label,
- return_generator=return_generator, pick_ori=pick_ori,
- )
- return stc
- def elec_to_inf_label(sensor_info, src, subjects_dir, transfile):
- """
- Creates a label on the inflated brain model based on an electrode bounding box.
- Parameters
- ----------
- sensor_info : mne.Info
- subjects_dir : str
- src : mne.SourceSpace
- transfile: str
- The path to the subject's EEG-MRI coregistration -trans.fif
- """
- subject = src[0].get('subject_his_id', None)
- # Get coordinates in use for passed SRC
- passed_coords = [] # x/y/z spatial info
- passed_verts = [] # corresponding vertex numbers
- for i, hemi in enumerate(['lh', 'rh']):
- # Left hemisphere (array of shape [n_vertices_lh, 3])
- passed_coords.append(
- src[i]['rr'][np.array(src[i]['inuse'], dtype=bool), :])
- passed_verts.append(src[i]['vertno'])
- # Get coordinates in use for the inflated brain model
- inflated_coords = []
- for i, hemi in enumerate(['lh', 'rh']):
- inflated_fname = f'{subjects_dir}/{subject}/surf/{hemi}.inflated'
- _coords = fs.read_geometry(inflated_fname)[0][src[i]['vertno'], :]
- # Inflated brain is a mess and needs to be altered.
- # Right X needs to be fully positive
- # Left X needs to be fully negative
- if i == 0:
- _coords = 1 + _coords - ([_coords.max(axis=0)[0], 0, 0])
- else:
- _coords = -1 + _coords - ([_coords.min(axis=0)[0], 0, 0])
- inflated_coords.append(_coords)
- # project electrodes to the cortex of passed SRC
- proj_info = mne.preprocessing.ieeg.project_sensors_onto_brain(sensor_info,
- trans=mne.read_trans(
- transfile),
- subject=subject,
- subjects_dir=subjects_dir,
- picks=[
- 'eeg'],
- copy=True, verbose=None)
- pos = np.array([proj_info['chs'][proj_info['ch_names'].index(
- ch)]['loc'][:3] for ch in proj_info.ch_names])
- # Compute nearest vertex
- pos_verts = []
- pos_inf_coords = []
- for p in pos:
- pos_verts.append(np.linalg.norm(
- np.vstack(passed_coords) - p, axis=1).argmin())
- pos_inf_coords.append(np.vstack(inflated_coords)[pos_verts[-1], :])
- all_coords = np.vstack(inflated_coords)
- pos_inf_coords = np.vstack(pos_inf_coords)
- # Determine the bounding box by finding min and max for each dimension
- bbox_min = pos_inf_coords.min(axis=0) # [min_x, min_y, min_z]
- bbox_max = pos_inf_coords.max(axis=0) # [max_x, max_y, max_z]
- # We downsampled it later, which might lose some info, so grow it a bit first.
- for i in range(3):
- range_ = bbox_max[i] - bbox_min[i]
- bbox_min[i] -= 0.025 * range_
- bbox_max[i] += 0.025 * range_
- # print(bbox_min)
- # print(bbox_max)
- # print(bbox_min, bbox_max)
- # Find vertices inside the bounding box
- inside_bbox = np.all((all_coords >= bbox_min) &
- (all_coords <= bbox_max), axis=1)
- all_data = np.zeros((all_coords.shape[0], 1))
- all_data[inside_bbox] = 1
- label_data = mne.SourceEstimate(data=all_data,
- vertices=passed_verts,
- subject=subject,
- tmin=0, tstep=1)
- lh_labels, rh_labels = mne.stc_to_label(
- label_data,
- src=src,
- smooth=True,
- subjects_dir=subjects_dir,
- connected=True,
- verbose="error",
- )
- # print(len(src[0]['vertno']), src[0]['vertno'])
- # print(len(np.sum(lh_labels).vertices), np.sum(lh_labels).vertices)
- # Annoyingly, we have to spatially downsample this for the fill() function to work. Not really sure
- # why that is, but unfortunately, we're stuck with it.
- _src = mne.setup_source_space(
- subject=subject, spacing='ico2', subjects_dir=subjects_dir, add_dist=False)
- # lh_labels = np.sum(mne.grow_labels(subject,
- # seeds = np.sum(lh_labels).vertices[::10],
- # extents = 1, hemis = 0, subjects_dir=subjects_dir,
- # surface='inflated', colors = np.array([[1, 1, 1, 1]]),
- # )).restrict(src=_src).fill(src=_src) if lh_labels else None
- lh_labels = np.sum(lh_labels).restrict(
- src=_src).fill(src=_src) if lh_labels else None
- rh_labels = np.sum(rh_labels).restrict(
- src=_src).fill(src=_src) if rh_labels else None
- # all_labels = np.sum([l for l in [lh_labels, rh_labels] if l])
- # all_labels = all_labels.fill(src=src)
- return lh_labels if lh_labels else rh_labels
mne_utils.py at commit 8d48b12, under MIT · at the source
Overview
- Department of Biomedical Engineering, Carnegie Mellon University, Pittsburgh, PA USA
- Neuroscience Institute, Carnegie Mellon University, Pittsburgh, PA USA
Abstract
The abstract is not reproduced here: the paper's license (CC BY-NC-ND) does not allow it. Read it in the paper, at the publisher or on Europe PMC.
Repository
Its files are read in the Code ↔ Paper reader above, with 9 matches between paragraphs and lines of code.
bfinl/tFUS-ESI
8d48b1257d2a331ba89e4320b09df1c30bff3d52, 9 January 2026Availability: 1 check, the latest on 30 September 2026: the link answers
- 30 September 2026: the link answers
8 files
- lmer.R, R, 82 lines, 2 matches
- python_utils/
R_utils.py , Python, 73 lines - python_utils/
__init__.py , Python, 7 lines - python_utils/
mne_utils.py , Python, 747 lines, 3 matches - python_utils/
preprocessing_utils.py , Python, 189 lines, 1 match - python_utils/
stats_utils.py , Python, 548 lines, 3 matches - LICENSE.txt, License, 9 lines
- README.md, Text, 32 lines
Code availability statement
The paper has a code availability statement. Its license (CC BY-NC-ND) does not allow reproducing it here; in short, from what the harvester recognized in it:
- it points to the authors' code: bfinl/
tFUS-ESI
Read it in the paper: doi.org/10.1038/s41467-026-69853-8.
Tracing map
Proposed by the machine: these links were found in the paper and verified at the source, without human review. The map will receive a Zenodo DOI once one of the paper's authors has validated it with their ORCID.
What the map holds:
- 1 repository of the authors' code, each at its verified commit, with its license and how the link was found in the paper;
- 6 scripts, each with its path and the digest of its content;
- 9 matches between paragraphs of the paper and lines of the code (method lexical-v1);
- neither the text of the paper nor the code itself.
Its JSON (tracing-map.json) is deposited on Zenodo with its DOI once the map is validated.
Data
Datasets cited
- figshare:31029691, at figshare; found in “Data availability”
Data availability statement
The paper has a data availability statement. Its license (CC BY-NC-ND) does not allow reproducing it here; in short, from what the harvester recognized in it:
- it points to a dataset: figshare 31029691
Read it in the paper: doi.org/10.1038/s41467-026-69853-8.
Versions
The history of this record: each version stored by the harvester or made by a correction of its authors or of the maintainers of its code, and what changed in its facts. The texts of the paper (its abstract, its availability statements) are not part of it; versions that changed only those are not listed.
Version 1, 30 September 2026: the first record
Recorded: type, language, journal, volume, issue, pages, dates, 7 authors, 5 keywords, 12 MeSH terms, 5 funders, 79 references, 1 RRID.
Cite
This paper
Kosnoff, J., Gonsisko, C., Yu, K., Zhang, J., Ding, Y., Zhang, Y., & He, B. (2026). Transcranial focused ultrasound induces source localizable cortical activation in resting state humans when applied concurrently with transcranial electric stimulation. Nature communications, 17(1), 2023. https://
BibTeX
@article{kosnoff2026tran
author = {Kosnoff, Joshua and Gonsisko, Colton and Yu, Kai and Zhang, Joseph and Ding, Yidan and Zhang, Yisha and He, Bin},
title = {{Transcranial focused ultrasound induces source localizable cortical activation in resting state humans when applied concurrently with transcranial electric stimulation}},
journal = {Nature communications},
year = {2026},
month = mar,
volume = {17},
number = {1},
pages = {2023},
publisher = {Nature Publishing Group},
issn = {2041-1723},
doi = {10.1038/
url = {https://
pmid = {41807379},
pmcid = {PMC12976073}
}
RIS
TY - JOUR
AU - Kosnoff, Joshua
AU - Gonsisko, Colton
AU - Yu, Kai
AU - Zhang, Joseph
AU - Ding, Yidan
AU - Zhang, Yisha
AU - He, Bin
TI - Transcranial focused ultrasound induces source localizable cortical activation in resting state humans when applied concurrently with transcranial electric stimulation
T2 - Nature communications
J2 - Nat Commun
PY - 2026
DA - 2026/
VL - 17
IS - 1
SP - 2023
SN - 2041-1723
PB - Nature Publishing Group
DO - 10.1038/
UR - https://
LA - en
ER -
CSL-JSON
{
"id": "10.1038/
"type": "article-journal",
"title": "Transcranial focused ultrasound induces source localizable cortical activation in resting state humans when applied concurrently with transcranial electric stimulation",
"container-title": "Nature communications",
"author": [
{
"family": "Kosnoff",
"given": "Joshua"
},
{
"family": "Gonsisko",
"given": "Colton"
},
{
"family": "Yu",
"given": "Kai"
},
{
"family": "Zhang",
"given": "Joseph"
},
{
"family": "Ding",
"given": "Yidan"
},
{
"family": "Zhang",
"given": "Yisha"
},
{
"family": "He",
"given": "Bin"
}
],
"container-title-short":
"volume": "17",
"issue": "1",
"page": "2023",
"DOI": "10.1038/
"PMID": "41807379",
"PMCID": "PMC12976073",
"ISSN": "2041-1723",
"publisher": "Nature Publishing Group",
"URL": "https://
"language": "en",
"issued": {
"date-parts": [
[
2026,
3,
10
]
]
}
}
The tracing map gets a citation of its own once an author has validated it and it has a DOI.
Similar papers
The papers with a page that share the most with this one: the tools found in their code, their categories, datasets, cited references and authors, the rarest counting most.
- [1] doi:10.1038/s41467-026-75826-8
- Transcranial focused ultrasound modulates spiking, LFP, and BOLD activity in the primate thalamus.Journal: Nature communicationsIn common: other, 11 references
- [2] doi:10.1038/s41378-026-01391-1
- High pressure transcranial focused ultrasound stimulation induces parameter-dependent cell-type specific effects.Journal: Microsystems & nanoengineeringIn common: other, 7 references, author Bin He
- [3] doi:10.1162/imag.a.1245 [code]
- Towards precision EEG connectomics: Evaluating the benefits of dense sampling.Journal: Imaging neuroscience (Cambridge, Mass.)In common: Pingouin, MNE-Python, lme4, 7 other tools, EEG, 4 references
- [4] doi:10.1038/s41597-026-07350-9 [code]
- An open multi-center MEG-EEG dataset for studying conscious visual perception.Journal: Scientific dataIn common: BayesFactor, Pingouin, car, 8 other tools, EEG, 1 reference
- [5] doi:10.1093/nc/niag029 [code]
- A data-driven approach to identifying and evaluating connectivity-based neural correlates of conscious visual perception.Journal: Neuroscience of consciousnessIn common: BayesFactor, Pingouin, car, 8 other tools, 1 reference
- [6] doi:10.1073/pnas.2510015122 [code]
- Mapping epileptogenic brain using a unified spatial–temporal–spectra
l source imaging framework Journal: n/aIn common: EEG, 2 references, 2 authors - [7] doi:10.1038/s41597-026-07377-y [code]
- An open-access multi-site fMRI dataset for investigating conscious visual perception.Journal: Scientific dataIn common: BayesFactor, Pingouin, car, 8 other tools
- [8] doi:10.1016/j.neuroimage.2026.121861
- Experimental evaluation of an integrated focused ultrasound and electroencephalography approach for developing activation-informed neuroimaging.Journal: NeuroImageIn common: other, EEG, 8 references
- [9] doi:10.1162/imag.a.105 [code]
- Right posterior theta reflects human parahippocampal phase resetting by salient cues during goal-directed navigationJournal: n/aIn common: Pingouin, car, MNE-Python, 8 other tools, EEG
- [10] doi:10.1038/s41467-026-75435-5 [code]
- Sensory-guided human-machine joint learning accelerates the acquisition of motor imagery brain computer interface control.Journal: Nature communicationsIn common: MNE-Python, SciPy, NumPy, EEG, 2 authors
Contribute
The authors of this paper can claim it, correct its record and validate its tracing map, and the maintainers of its code (its owner, or a public member of its organization) correct what it says of their repository; anyone signed in can ask for its removal. Every request goes to OSCR's own machine, which answers it; your account page follows them.
Sign in with ORCID to claim this paper as one of its authors, correct its record or validate its tracing map: when the paper's metadata lists your ORCID iD, you are recognized at once. Maintainers of its code: sign in with GitHub, then claim the repository on your account page.
Claim this paper
Correct its record
Say what each link of this record is, remove the ones that are not the paper's, add the ones that are missing. The correction becomes a new version of the record, in its Versions section.
Validate its tracing map
You validate the map as this page shows it: 1 repository of the authors' code, each at its verified commit and with its license, 6 scripts, and 9 matches between paragraphs and code (see the Code and Map sections). It then receives a DOI on Zenodo, with you (your ORCID iD) and OSCR as its creators; the code itself is not deposited.
The map's fingerprint: sha256:28c8aa8487e65dfd…
Add the badge to its README
The badge links the code to this page. Copy one of these into the README of the paper's code: only you decide where it goes, and nothing is changed for you.
Markdown
[, paste the snippet at the top, then “Commit changes…” and, to review it first, “Create a new branch and start a pull request”. You open the pull request; OSCR asks for no permission.
Request its removal
To ask OSCR to remove this record, the copies of its authors' scripts or its tracing map, use the removal request page: signed in, you say who you are, what to remove and why, then review and confirm the request. Published rules decide every request (how).
Discussion, reproductions, activity
Discussion: questions and error reports about this paper and its code, from signed-in readers and its authors. It opens with sign-in.
Reproductions: reports from readers who ran the authors' code: what they reproduced, with which environment, commit and data. It opens with sign-in.
Activity: what happens around this paper: new versions of its record, its map's validation, discussions and reproductions. It opens with sign-in.
