Forward modelling EEG from simulated sources

Hi all,

I want to forwad model EEG data from simulated source activity. I have two questions

  1. My intuition would be to average-reference the simulated EEG but in your tutorial you apply set_eeg_reference(projection=True), which I believe makes the simulated EEG reference-free. Is there a particular reason why you’d do that?
  2. My simulation has 1000 cortical regions. So far, I have assigned the same simulated cortical activity to several sources per region and fixed the dipole orientation. Testing this looks fine but the simulate_raw() function takes ages, which makes me wonder if there’s a good way to reduce my source space to 1000 simulated regions/sources?

This is how I prepare the forward model:

# Download fsaverage files
fs_dir = Path(mne.datasets.fetch_fsaverage(verbose=True))

trans = "fsaverage"
src = fs_dir / "bem" / "fsaverage-ico-5-src.fif"
bem = fs_dir / "bem" / "fsaverage-5120-5120-5120-bem-sol.fif"

# get the montage 
easycap_montage = mne.channels.make_standard_montage("easycap-M1")

# Create info of EEG data
info = mne.create_info(easycap_montage.ch_names, sfreq=sfreq, ch_types='eeg')
info.set_montage(easycap_montage)

# Get regions from parcellation
subject_path = os.path.join(project_root, derivatives_path, 'structural_connectivity/Schaefer1000')             

regions = np.array(mne.read_labels_from_annot('fsaverage', 'Schaefer2018_1000Parcels_17Networks_order', 'both', surf_name='pial', subjects_dir=subject_path, sort=False))
regions = list(regions[["background" not in r.name.lower() for r in regions]])  # remove background

# Create forward model
fwd = mne.make_forward_solution(info, trans=trans, src=src, bem=bem, eeg=True, n_jobs=None)

fwd = mne.forward.restrict_forward_to_label(fwd, regions)

mne.write_forward_solution(os.path.join(project_root, fwd_path), fwd, overwrite=True)

This is how I use it for the simulations:

# Get montage
easycap_montage = mne.channels.make_standard_montage(montage)

# Create info for simulated EEG data
info = mne.create_info(easycap_montage.ch_names, sfreq=sfreq, ch_types='eeg')
info.set_montage(easycap_montage)

# Prepare forward model
fwd = mne.read_forward_solution(fwd_path)
fwd = mne.convert_forward_solution(fwd, force_fixed=True, surf_ori=True, use_cps=True)

subject_path = os.path.join(project_root, derivatives_path, 'structural_connectivity/Schaefer1000')             

regions = np.array(mne.read_labels_from_annot('fsaverage', 'Schaefer2018_1000Parcels_17Networks_order', 'both', surf_name='pial', subjects_dir=subject_path, sort=False))
regions = list(regions[["background" not in r.name.lower() for r in regions]])  # remove medial wall

# Prepare source space
src_space = fwd['src']

# Assign simulations to source space
source_simulator = mne.simulation.SourceSimulator(src_space, tstep=time_step_in_seconds)
events = np.array([[   0,    0,    1]]) 

for i, s in enumerate(regions):
    source_simulator.add_data(s, simulated_lfp[i], events)

# Simulate raw EEG
info["dev_head_t"] = fwd["info"]["dev_head_t"] # throws error if they don't match
raw = mne.simulation.simulate_raw(info, source_simulator, forward=fwd, n_jobs=1)
raw.set_eeg_reference(projection=True)

Thanks for your help!

Setup
MNE 1.11.0
MacOS 15.6