Neuropixels Opto: combining high-resolution electrophysiology and optogenetics.
The 5 matches · 1 of them tie a paragraph to a whole file, not to given lines: a weak match, whose lines are not tinted
- [1] § Methods › Activating local neural populations › Data processing ↔ spks/sorting.py, lines 74–133 · score 0.62 · phase shift, bandpass filter, Kilosort
- [2] § Methods › Spike sorting and quality metrics ↔ spks/clusters.py, lines 240–323 · score 0.61 · amplitude cutoff, presence ratio, ISI, clusters, metrics, spike
- [3] § Results › Optotagging nearby neurons ↔ spks/clusters.py, lines 240–323 · score 0.59 · amplitude cutoff, presence ratio, firing rates, ISI, metrics, waveforms
- [4] § Methods › Optotagging nearby neurons (AI) › Data processing ↔ spks/sorting.py, lines 74–133 · score 0.57 · phase shift, Kilosort, pipeline, preprocessing, raw, filter
- [5] § Methods › Optotagging nearby neurons (AI) › Data processing ↔ spks/postprocess.py, the whole file · a weak match · score 0.50 · waveform amplitudes, Kilosort, peak, location, spike
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 · 514 lines · 24 KB · no license · 2 matches
- # Run various spike sorters using spike interface precompiled docker images
- # This should allow running it on a fast disk and copying only the relevant files over
- # The functions and parameters should be exposed and contained in this module.
- from .utils import *
- from .raw import *
- def get_probename(filename):
- # get the probe name from a file (only works with spikeglx?)
- probename = re.search(r'\s*imec[0-9]*[a-z]?\s*',str(filename))
- if not probename is None:
- return str(probename.group()).strip('/').strip('.')
- else:
- return 'probe0'
- def get_sorting_folder_path(filename,
- sorting_results_path_rules = ['..','..','{sortname}','{probename}'],
- sorting_folder_dictionary = dict(sortname = 'sorting',
- probename = 'probe0')):
- '''
- Gets the sorting folder path from a defined rule.
- foldername = get_sorting_folder_path(filename)
- '''
- filename = Path(filename)
- if filename.is_file():
- foldername = filename.parent
- sorting_folder_dictionary['probename'] = get_probename(filename)
- sorting_results_path = foldername #FIXME: not defined if filename isn't file
- for f in sorting_results_path_rules:
- if f == '..':
- foldername = foldername.parent
- else:
- foldername = foldername.joinpath(f.format(**sorting_folder_dictionary))
- return foldername
- def move_sorting_results(
- scratch_folder,
- original_session_path,
- move_filtered_data = False,
- sorting_results_path_rules = ['..','..','{sortname}','{probename}'],
- sorting_folder_dictionary = dict(
- sortname = 'sorting',
- probename = 'probe0')):
- '''
- Move spike sorting results to a standardized folder
- '''
- sorting_results_path = get_sorting_folder_path(
- filename = original_session_path,
- sorting_folder_dictionary=sorting_folder_dictionary,
- sorting_results_path_rules=sorting_results_path_rules)
- files_to_copy = []
- extensions = ['.npy','.tsv','.hdf','.m','.mat','.py','.log']
- if move_filtered_data:
- extensions += ['filtered_recording.*.bin']
- for name in extensions:
- files_to_copy += scratch_folder.glob(f'*{name}')
- if not sorting_results_path.exists():
- sorting_results_path.mkdir(parents=True, exist_ok=True)
- for f in tqdm(files_to_copy,desc = 'Moving files'):
- if os.path.exists(sorting_results_path/f.name):
- os.remove(sorting_results_path/f.name) #need to delete files before moving to avoid weird permission error
- try:
- shutil.move(f,sorting_results_path/f.name)
- except Exception as err:
- print(err)
- print(f'FAILED to move {f} to {sorting_results_path}')
- return sorting_results_path
- def run_kilosort(sessionfiles = [],
- channels = None,
- foldername = None,
- temporary_folder = 'temporary',
- version = '4.0',
- sorting_results_path_rules = ['..','..','{sortname}','{probename}'],
- sorting_folder_dictionary = dict(sortname = None, probename = 'probe0'),
- do_post_processing = False,
- device = 'cuda',
- gpu_index = 0,
- split_shanks = False,
- motion_correction = True,
- dredge_motion_correction = False,
- thresholds = None,
- batch_size = int(60000),
- lowpass = 300.,
- highpass = 13000.,
- filter_pipeline_par = [dict(function = 'bandpass_filter_gpu',
- sampling_rate = 30000,
- lowpass = 300,
- highpass = 13000,
- return_gpu = True),
- dict(function = 'phase_shift_gpu',
- sample_shifts = None,
- return_gpu = True),
- dict(function = 'global_car_gpu',
- return_gpu = False)]):
- '''
- Runs kilosort given binary files, returns a folder with the results.
- '''
- using_scratch = False
- if foldername is None:
- foldername = create_temporary_folder(temporary_folder, prefix=f'ks{version}_sorting')
- using_scratch = True
- for f in filter_pipeline_par:
- if 'lowpass' in f.keys() and not lowpass is None:
- f['lowpass'] = lowpass
- if 'highpass' in f.keys() and not highpass is None:
- f['highpass'] = highpass
- tt = RawRecording(sessionfiles,device = device,
- filter_pipeline_par = filter_pipeline_par,
- return_preprocessed = True)
- dtype = 'int16'
- binaryfilepath = pjoin(foldername,'filtered_recording.{probename}.bin'.format(
- **sorting_folder_dictionary))
- output_folder = os.path.dirname(binaryfilepath)
- if not os.path.exists(output_folder):
- os.makedirs(output_folder)
- print(f'Exporting binary to {binaryfilepath}')
- if channels is None:
- channels = tt.channel_info.channel_idx.values
- print(tt.offsets,len(sessionfiles),len(sessionfiles)>1)
- print(sessionfiles)
- binaryfile,metadata = tt.to_binary(binaryfilepath,
- filter_pipeline_par = filter_pipeline_par,
- channels = channels)
- if dredge_motion_correction:
- from .raw import dredge_motion_correct_binary_file,dredge_motion_correct_across_sessions
- n_jobs = 10
- print(len(sessionfiles),len(sessionfiles)>1)
- if len(sessionfiles)>1:
- print('Using multisession dredge.')
- binaryfilepath = dredge_motion_correct_across_sessions(binaryfilepath=binaryfilepath,
- metadata = metadata,
- n_jobs = n_jobs)
- else:
- print('Using single session dredge.')
- binaryfilepath = dredge_motion_correct_binary_file(binaryfilepath,
- nchannels = metadata['nchannels'],
- sampling_rate = metadata['sampling_rate'],
- channel_coords = metadata['channel_coords'],
- channel_shank = metadata['channel_shank'],
- output_folder = foldername,
- overwrite = True,
- output_dtype = 'int16',
- n_jobs = n_jobs)
- dtype = 'int16'
- print('Motion corrected done with dredge so skipping kilosort motion.')
- motion_correction = 0
- del tt # RawRecording no longer needed
- channelmappath = pjoin(os.path.dirname(binaryfilepath),'chanMap.mat')
- opspath = pjoin(os.path.dirname(binaryfilepath),'ops.mat')
- if lowpass is None:
- lowpass = 300.
- nchannels = metadata['nchannels']
- coords = np.stack(metadata['channel_coords'])
- # make the channelmap file
- chanMap = dict(Nchannels = nchannels,
- connected = np.ones(nchannels,dtype=bool).T,
- xcoords = coords[:,0].astype(float),
- ycoords = coords[:,1].astype(float),
- chanMap = np.array(metadata['channel_idx'],dtype=np.int64)+1,
- chanMap0ind = np.array(metadata['channel_idx'],dtype=np.int64),
- kcoords = np.array(metadata['channel_shank'],dtype=float).T+1,
- fs = metadata['sampling_rate'])
- from scipy.io import savemat
- savemat(channelmappath, chanMap,appendmat = False)
- if version == '2.5':
- compiled_name = 'kilosort2_5'
- if thresholds is None:
- thresholds = [9.,3.]
- ops = dict(ops = dict(default_ks25_ops,
- NchanTOT=float(metadata['nchannels']),
- Nchan = float(len(metadata['channel_idx'])),
- fbinary = binaryfilepath,
- fproc = pjoin(output_folder,'temp_wh.dat'),
- chanMap = channelmappath,
- fs = metadata['sampling_rate'],
- doCorrection = int(motion_correction),
- fshigh = lowpass,
- Th = thresholds,
- GPU = gpu_index + 1)) # indices are one based ...
- matlabcommand = kilosort25_matlabcommand
- elif version == '3.0':
- compiled_name = 'kilosort3_0'
- if thresholds is None:
- thresholds = [9.,9.]
- ops = dict(ops = dict(default_ks30_ops,
- NchanTOT=float(metadata['nchannels']),
- Nchan = float(len(metadata['channel_idx'])),
- fbinary = binaryfilepath,
- fproc = pjoin(output_folder,'temp_wh.dat'),
- chanMap = channelmappath,
- fs = metadata['sampling_rate'],
- doCorrection = int(motion_correction),
- fshigh = lowpass,
- Th = thresholds,
- GPU = gpu_index + 1)) # indices are one based ...
- if version in ['2.5','3.0']:
- # save the files
- savemat(opspath, ops,appendmat = False)
- import shutil
- if not shutil.which(compiled_name) is None:
- os.system(f'{compiled_name} {output_folder}') # easier to kill than subprocess?
- else: # just run using a local installation..
- matlabfile = pjoin(output_folder,'run_ks.m')
- with open(matlabfile,'w') as f:
- f.write(matlabcommand.format(output_folder = output_folder))
- cmd = """matlab -nodisplay -nosplash -r "run('{0}');" """.format(matlabfile)
- os.system(cmd) # easier to kill than subprocess?
- elif version == '4.0':
- if thresholds is None:
- thresholds = [9.,8.]
- res = run_kilosort4(
- device = device,
- foldername = foldername,
- binaryfilepath = binaryfilepath,
- metadata = metadata,
- motion_correction = motion_correction,
- thresholds = thresholds,
- dtype = dtype,
- batch_size = batch_size)
- else:
- raise(OSError('Undefined version {version}'))
- if do_post_processing:
- foldername = kilosort_post_processing(
- foldername,
- sessionfiles,
- move = using_scratch,
- binaryfilepath = binaryfilepath,
- sorting_results_path_rules = sorting_results_path_rules,
- sorting_folder_dictionary = sorting_folder_dictionary)
- return foldername
- def run_kilosort4(device, foldername, binaryfilepath,
- metadata,
- dtype = 'int16',
- thresholds = None, # pass a list if needed
- do_car = False,
- batch_size = int(60000),
- motion_correction = 1):
- nchannels = metadata['nchannels']
- coords = np.stack(metadata['channel_coords'])
- yc = coords[:,1].astype(np.float32)
- xc = coords[:,0].astype(np.float32)
- fix_shanks = False # flag to fix the phy coords # this is now fixed on v4.04
- if fix_shanks:
- # lets stack the shanks... because kilosort 4.0 can not handle multiple shanks..
- previous = 0
- for shank in np.unique(metadata['channel_shank']):
- idx = np.where(metadata['channel_shank'] == shank)
- offset = np.max(yc[idx])
- yc[idx] = (yc[idx]-np.min(yc[idx])) + previous
- xc[idx] = (xc[idx]-np.min(xc[idx]))
- previous += 100 + offset
- probe = dict(n_chan = nchannels,
- xc = xc,
- yc = yc,
- chanMap = np.array(metadata['channel_idx'], dtype=int),
- kcoords = np.array(metadata['channel_shank'], dtype=int).T)
- from kilosort import run_kilosort
- settings = dict(fs = metadata['sampling_rate'],
- n_chan_bin = nchannels,
- batch_size = batch_size,
- data_dir = foldername)
- settings['nblocks'] = int(motion_correction)
- if not thresholds is None:
- settings['Th_universal'] = thresholds[0]
- settings['Th_learned'] = thresholds[1]
- res = run_kilosort(filename = binaryfilepath,
- results_dir = foldername,
- settings = settings,
- data_dtype = dtype,
- probe = probe,
- device = device,
- do_CAR = do_car,
- save_preprocessed_copy = False,
- save_extra_vars = True) # save pc_features
- if fix_shanks:
- ops = res[0]
- st = res[1]
- tF = res[3]
- ops['xc'],ops['yc'] = coords.astype(np.float32).T # recompute the spike positions
- from kilosort.postprocessing import compute_spike_positions
- positions = compute_spike_positions(st,tF,ops)
- np.save(Path(foldername)/'spike_positions.npy',np.vstack(positions).T) # overwrite spike positions
- np.save(Path(foldername)/'channel_positions.npy',coords.astype(float)) # save the correct channel positions
- del res
- free_gpu()
- return True
- def kilosort_post_processing(resultsfolder,
- sessionfolder,
- move = False,
- binaryfilepath = None,
- sorting_results_path_rules = ['..','..','{sortname}','{probename}'],
- sorting_folder_dictionary = dict(
- sortname = 'kilosort',
- probename = 'probe0'),
- max_n_spikes = 1000):
- '''
- Post processing for kilosort results
- 1. remove duplicates
- 2. compute_waveforms
- 3. store a sample of 1000 waveforms to disk
- 4. move the files to a new folder and if so delete the scratch folder
- '''
- # 1. remove duplicates
- from .clusters import Clusters
- resultsfolder = Path(resultsfolder)
- sp = Clusters(resultsfolder,get_metrics = False,get_waveforms=False,load_template_features = True)
- sp.remove_duplicate_spikes(overwrite_phy=True)
- del sp
- # 2. compute_waveforms and store to disk
- sp = Clusters(resultsfolder,get_metrics = False,get_waveforms=False)
- meta = load_dict_from_h5(list(resultsfolder.glob('filtered_recording.*.metadata.hdf'))[0])
- from .io import map_binary
- if binaryfilepath is None:
- binaryfilepath = list(resultsfolder.glob('filtered_recording.*.bin'))[0]
- data = map_binary(binaryfilepath,meta['nchannels'])
- # don't filter the waveforms because it was done before.
- sp.extract_waveforms(data,np.arange(meta['nchannels']),
- max_n_spikes=max_n_spikes,
- save_folder_path = resultsfolder,filter_par = None)
- del sp
- # Compute metrics and mean waveforms
- sp = Clusters(resultsfolder,get_metrics = True, get_waveforms=True, load_template_features = True)
- del sp # close so it can move
- # 4. move the files to a new folder
- if move:
- if type(sessionfolder) in [list]:
- folder = sessionfolder[0]
- print(f'Saving to {folder}')
- else:
- folder = sessionfolder
- outputfolder = move_sorting_results(
- resultsfolder,
- folder,
- sorting_results_path_rules = sorting_results_path_rules,
- sorting_folder_dictionary = sorting_folder_dictionary)
- # 5. delete the scratch
- shutil.rmtree(resultsfolder)
- resultsfolder = outputfolder
- return resultsfolder # Clusters(resultsfolder) to open
- from .io import list_spikeglx_binary_paths
- def sort_multiprobe_sessions(sessions,
- temporary_folder = '/scratch',
- method = 'kilosort2.5',
- sorting_results_path_rules = ['..','..','{sortname}','{probename}'],
- sorting_folder_dictionary = dict(
- sortname = 'kilosort', probename = 'probe0'),
- do_post_processing = True,
- move = True,
- device = 'cuda',
- gpu_index = 0,
- motion_correction = True):
- '''
- Sort multiprobe neuropixels recordings (will concatenate multiple sessions if a list is passed).
- '''
- if not type(sessions) is list:
- sessions = [sessions]
- tmp = [list_spikeglx_binary_paths(s) for s in sessions]
- all_probe_dirs = []
- for iprobe in range(len(tmp[0])):
- all_probe_dirs.append([t[iprobe][0] for t in tmp])
- results = []
- for probepath in all_probe_dirs:
- print('Running {1} on sessions {0}'.format(' ,'.join(probepath),method))
- sorting_folder_dictionary['probename'] = get_probename(probepath)
- results_folder = run_kilosort(sessionfiles = probepath,
- version = method.strip('kilosort'),
- temporary_folder = temporary_folder,
- sorting_results_path_rules = sorting_results_path_rules,
- sorting_folder_dictionary = sorting_folder_dictionary,
- do_post_processing = False,
- motion_correction = motion_correction,
- device=device, gpu_index = gpu_index)
- print('Completed {1} Results folder: {0}'.format(results_folder,method))
- if do_post_processing:
- results_folder = kilosort_post_processing(
- results_folder,
- probepath,
- sorting_results_path_rules = sorting_results_path_rules,
- sorting_folder_dictionary = sorting_folder_dictionary,
- move = move)
- print('Completed sorting for results folder: {0}'.format(results_folder))
- results.append(results_folder)
- return results
- kilosort25_matlabcommand = '''
- load(fullfile('{output_folder}','ops.mat'))
- load(fullfile('{output_folder}','chanMap.mat'))
- % This assumes kilosort 2.5 and all are installed and on the path
- % preprocess data to create temp_wh.dat
- rez = preprocessDataSub(ops);
- rez = datashift2(rez, ops.doCorrection); % last input is for shifting data
- % ORDER OF BATCHES IS NOW RANDOM, controlled by random number generator
- iseed = 1;
- rez = learnAndSolve8b(rez, iseed); % main tracking and template matching algorithm
- % OPTIONAL: remove double-counted spikes - solves issue in which individual spikes are assigned to multiple templates.
- % See issue 29: https://github.com/MouseLand/Kilosort/issues/29
- % rez = remove_ks2_duplicate_spikes(rez);
- rez = find_merges(rez, 1); % final merges
- rez = splitAllClusters(rez, 1); % final splits by SVD
- rez = set_cutoff(rez); % decide on cutoff
- rez.good = get_good_units(rez); % eliminate widely spread waveforms (likely noise)
- fprintf('found %d good units', sum(rez.good>0))
- fprintf('Saving results to Phy ')
- rezToPhy(rez, '{output_folder}'); % write to Phy
- exit(1);
- '''
- default_ks25_ops = dict(
- datatype = 'dat',
- trange = [0.,np.inf],
- CAR = 1.,
- nblocks = 5.,
- sig = 20.,
- lam = 10.,
- AUCsplit = 0.9,
- minFR = 1./50,
- momentum = [20.,400.],
- sigmaMask = 30.,
- ThPre = 8.,
- spkTh = -6.,
- reorder = 1.,
- nskip = 25.,
- nfilt_factor = 4.,
- ntbuff = 64.,
- NT = 65600.,
- whiteningRange = 32.,
- nSkipCov = 25.,
- scaleproc = 200.,
- nPCs = 3,
- useRam = 0,
- nt0 = 61.)
- default_ks30_ops = dict(
- datatype = 'dat',
- trange = [0.,np.inf],
- CAR = 1.,
- nblocks = 5.,
- sig = 20.,
- lam = 20.,
- AUCsplit = 0.8,
- minFR = 1./50,
- momentum = [20.,400.],
- sigmaMask = 30.,
- ThPre = 8.,
- spkTh = -6.,
- reorder = 1.,
- nskip = 25.,
- nfilt_factor = 4.,
- ntbuff = 64.,
- NT = 65600.,
- whiteningRange = 32.,
- nSkipCov = 25.,
- scaleproc = 200.,
- nPCs = 3,
- useRam = 0,
- nt0 = 61.)
- class SpikeSorting(object):
- def __init__(raw_files, output_folder,
- filter_pipeline_par = [dict(function = 'bandpass_filter_gpu',
- sampling_rate = 30000,
- lowpass = 300,
- highpass = 10000,
- return_gpu = False),
- dict(function = 'global_car_gpu',
- return_gpu = True)],
- temporary_folder = None, motion_correction = True, **kwargs):
- ''' Run a spike sorter.
- 1) Creates the output folder.
- 2) Concatenates the input files.
- 3) Writes a json file with the onsets and offsets of each file, the channelmap
- 4) Downloads a sorter image and runs it in the temporaty folder
- 5) Copies the files to the output folder and cleans the temporary folder
- THIS IS A PLACEHOLDER FOR NOW.
- '''
- pass
sorting.py at commit 451691a, no license · at the source
Overview
and 9 other authors
Michael Häusser8, Christof Koch9, Jonathan T. Ting3,7, Barundeb Dutta6, Timothy D. Harris4,5, Nicholas A. Steinmetz3, Karel Svoboda1,4, Joshua H. Siegle1, Matteo Carandini2- Allen Institute for Neural Dynamics,Seattle, WA USA
- UCL Institute of Ophthalmology, University College London,London, UK
- Department of Neurobiology and Biophysics, University of Washington,Seattle, WA USA
- Janelia Research Campus, Howard Hughes Medical Institute,Ashburn, VA USA
- Department of Biomedical Engineering, Johns Hopkins University,Baltimore, MD USA
- IMEC,Leuven, Belgium
- Allen Institute for Brain Science,Seattle, WA USA
- Wolfson Institute for Biomedical Research, University College London,London, UK
- Allen Institute MindScope Program,Seattle, WA USA
Abstract
High-resolution extracellular electrophysiology is the gold standard for recording spikes from distributed neural populations and is especially powerful when combined with optogenetics for manipulation of specific cell types with high temporal resolution. We integrated these approaches into prototype Neuropixels Opto probes, which combine electronic and photonic circuits. These devices pack 960 electrical recording sites and two sets of 14 light emitters onto a 70-μm-wide, 1-cm-long shank, allowing spatially addressable optogenetic stimulation with blue and red light. In mouse cortex, Neuropixels Opto probes delivered high-quality recordings together with spatially addressable optogenetics, differentially activating or silencing neurons at distinct cortical depths. In the mouse striatum and other deep structures, Neuropixels Opto probes delivered efficient optotagging, facilitating the identification of two cell types in parallel. Neuropixels Opto probes represent a promising tool for recording, identifying and manipulating neuronal populations.
Reproduced under the paper's license (CC BY), from the paper cited above.
Repositories
Its files are read in the Code ↔ Paper reader above, with 5 matches between paragraphs and lines of code.
spkware/spks
451691a558650c80696a6871377f33986f3abb2f, 18 March 2026Availability: 1 check, the latest on 27 September 2026: the link answers
- 27 September 2026: the link answers
20 files
- notebooks/
load_raw_waveforms.ipynb , Jupyter, 89 lines - notebooks/
plot_drift_map.ipynb , Jupyter, 46 lines - notebooks/
plot_footprints.ipynb , Jupyter, 50 lines - notebooks/
sort_dataset.ipynb , Jupyter, 282 lines - setup.py, Python, 25 lines
- spks/
__init__.py , Python, 10 lines - spks/
clusters.py , Python, 685 lines, 2 matches - spks/
event_aligned.py , Python, 150 lines - spks/
io.py , Python, 252 lines - spks/
metrics.py , Python, 272 lines - spks/
phy_utils.py , Python, 183 lines - spks/
postprocess.py , Python, 95 lines, 1 match - spks/
raw.py , Python, 615 lines - spks/
sorting.py , Python, 514 lines, 2 matches - spks/
spikeglx_utils.py , Python, 290 lines - spks/
sync.py , Python, 124 lines - spks/
utils.py , Python, 432 lines - spks/
viz.py , Python, 273 lines - spks/
waveforms.py , Python, 353 lines - README.md, Text, 38 lines
AllenInstitute/MIES
5e6410391e0a92988c37df39c3ea435bd96af058, 17 September 2026Availability: 1 check, the latest on 27 September 2026: the link answers
- 27 September 2026: the link answers
49 files
- Packages/
doc/ , Python, 80 linesconf.py - tools/
autorun-test.sh , Shell, 69 lines - tools/
build-documentation.sh , Shell, 156 lines - tools/
check-code.sh , Shell, 272 lines - tools/
clean_mies_installation. , Shell, 174 linessh - tools/
cleanup-dotfiles.sh , Shell, 26 lines - tools/
create-changelog.sh , Shell, 57 lines - tools/
create-installer.sh , Shell, 128 lines - tools/
create-release.sh , Shell, 85 lines - tools/
documentation/ , Shell, 23 linesrun.sh - tools/
ftp-upload/ , Shell, 18 linescompress-files.sh - tools/
ftp-upload/ , Shell, 88 linesdownload-files.sh - tools/
ftp-upload/ , Shell, 45 linesflatten-files.sh - tools/
ftp-upload/ , Shell, 63 linesupload-files.sh - tools/
functionprofiling.sh , Shell, 31 lines - tools/
gather-logfiles-and-cras , Shell, 34 lineshdumps.sh - tools/
get-changed-controls.sh , Shell, 35 lines - tools/
get-igor-path.sh , Shell, 49 lines - tools/
initial-repo-config.sh , Shell, 48 lines - tools/
installer/ , C++, 1,444 linesAccessControl/ Contrib/ AccessControl/ AccessControl.cpp - tools/
installer/ , C/C++, 83 linesAccessControl/ Contrib/ AccessControl/ nsis_ansi/ api.h - tools/
installer/ , C/C++, 74 linesAccessControl/ Contrib/ AccessControl/ nsis_ansi/ pluginapi.h - tools/
installer/ , C/C++, 83 linesAccessControl/ Contrib/ AccessControl/ nsis_unicode/ api.h - tools/
installer/ , C/C++, 214 linesAccessControl/ Contrib/ AccessControl/ nsis_unicode/ nsis_tchar.h - tools/
installer/ , C/C++, 101 linesAccessControl/ Contrib/ AccessControl/ nsis_unicode/ pluginapi.h - tools/
installer/ , C/C++, 14 linesAccessControl/ Contrib/ AccessControl/ resource.h - tools/
installer/ , C++, 348 linesShelllink/ Contrib/ ShellLink/ ShellLink.cpp - tools/
installer/ , C/C++, 83 linesShelllink/ Contrib/ ShellLink/ nsis_ansi/ api.h - tools/
installer/ , C/C++, 74 linesShelllink/ Contrib/ ShellLink/ nsis_ansi/ pluginapi.h - tools/
installer/ , C/C++, 83 linesShelllink/ Contrib/ ShellLink/ nsis_unicode/ api.h - tools/
installer/ , C/C++, 214 linesShelllink/ Contrib/ ShellLink/ nsis_unicode/ nsis_tchar.h - tools/
installer/ , C/C++, 101 linesShelllink/ Contrib/ ShellLink/ nsis_unicode/ pluginapi.h - tools/
installer/ , C, 42 linesnsis/ Examples/ Plugin/ exdll.c - tools/
installer/ , C/C++, 85 linesnsis/ Examples/ Plugin/ nsis/ api.h - tools/
installer/ , C/C++, 229 linesnsis/ Examples/ Plugin/ nsis/ nsis_tchar.h - tools/
installer/ , C/C++, 108 linesnsis/ Examples/ Plugin/ nsis/ pluginapi.h - tools/
nextFreePRNumber.sh , Shell, 30 lines - tools/
nwb-read-tests/ , Python, 119 linesnwbv2-read-test.py - tools/
nwb-read-tests/ , Shell, 29 linesrun.sh - tools/
pre-commit/ , Shell, 36 linesrun.sh - tools/
publish-docs-on-github.s , Shell, 70 linesh - tools/
report-generator/ , Shell, 119 linesbuild.sh - tools/
run-ipt.sh , Shell, 29 lines - tools/
sign-installer.sh , Shell, 98 lines - tools/
upload-github-release-as , Shell, 146 linesset-helper.sh - tools/
upload-github-release-as , Shell, 81 linesset.sh - tools/
verify-junit-files.sh , Shell, 32 lines - LICENSE, License, 32 lines
- README.md, Text, 100 lines
codeocean.allenneuraldynamics.org/capsule/1593868
Availability: 1 check, the latest on 27 September 2026: the link answers (HTTP 200)
- 27 September 2026: the link answers (HTTP 200)
Code availability
The code used in this study is available in open repositories under open access licenses. Code for the electrical and optical characterizations (Fig. 2 and Extended Data Figs. 2b–e and 3a,b) is available via Figshare at 10.5522/
Reproduced under the paper's license (CC BY), from the paper cited above.
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:
- 3 repositories of the authors' code, each at its verified commit, with its license and how the link was found in the paper;
- 66 scripts, each with its path and the digest of its content;
- 5 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:31271761, at figshare; found in “Data availability”
- zenodo:18461445, at Zenodo; found in “Data availability”
Data availability
The data from this study are available in open repositories under open access licenses. Data from the electrical and optical characterizations (Fig. 2 and Extended Data Figs. 2b–e and 3a,b) are available via Figshare at 10.5522/
Reproduced under the paper's license (CC BY), from the paper cited above.
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 2, 28 September 2026
- Publisher: n/a → Nature Portfolio
Version 1, 27 September 2026: the first record
Recorded: type, language, journal, volume, issue, pages, dates, 29 authors, 3 keywords, 5 MeSH terms, 7 funders, 94 references, 4 RRIDs.
Cite
This paper
Lakunina, A. A., Socha, K. Z., Ladd, A. E., Bowen, A. J., Chen, S., Colonell, J., Doshi, A., Karsh, B., Krumin, M., Kulik, P., Li, A. J., Neutens, P., O’Callaghan, J., Olsen, M., Putzeys, J., Bai Reddy, C., Tilmans, H. A. C., Vargas, S., Welkenhuysen, M., . . . Carandini, M. (2026). Neuropixels Opto: combining high-resolution electrophysiology and optogenetics. Nature methods, 23(6), 1207-1216. https://
BibTeX
@article{lakunina2026neu
author = {Lakunina, Anna A. and Socha, Karolina Z. and Ladd, Alexander E. and Bowen, Anna J. and Chen, Susu and Colonell, Jennifer and Doshi, Anjal and Karsh, Bill and Krumin, Michael and Kulik, Pavel and Li, Anna J. and Neutens, Pieter and O’Callaghan, John and Olsen, Meghan and Putzeys, Jan and Bai Reddy, Charu and Tilmans, Harrie A. C. and Vargas, Sara and Welkenhuysen, Marleen and Ye, Zhiwen and Häusser, Michael and Koch, Christof and Ting, Jonathan T. and Dutta, Barundeb and Harris, Timothy D. and Steinmetz, Nicholas A. and Svoboda, Karel and Siegle, Joshua H. and Carandini, Matteo},
title = {{Neuropixels Opto: combining high-resolution electrophysiology and optogenetics}},
journal = {Nature methods},
year = {2026},
month = jun,
volume = {23},
number = {6},
pages = {1207--1216},
publisher = {Nature Portfolio},
issn = {1548-7091},
doi = {10.1038/
url = {https://
pmid = {42225964},
pmcid = {PMC13259958}
}
RIS
TY - JOUR
AU - Lakunina, Anna A.
AU - Socha, Karolina Z.
AU - Ladd, Alexander E.
AU - Bowen, Anna J.
AU - Chen, Susu
AU - Colonell, Jennifer
AU - Doshi, Anjal
AU - Karsh, Bill
AU - Krumin, Michael
AU - Kulik, Pavel
AU - Li, Anna J.
AU - Neutens, Pieter
AU - O’Callaghan, John
AU - Olsen, Meghan
AU - Putzeys, Jan
AU - Bai Reddy, Charu
AU - Tilmans, Harrie A. C.
AU - Vargas, Sara
AU - Welkenhuysen, Marleen
AU - Ye, Zhiwen
AU - Häusser, Michael
AU - Koch, Christof
AU - Ting, Jonathan T.
AU - Dutta, Barundeb
AU - Harris, Timothy D.
AU - Steinmetz, Nicholas A.
AU - Svoboda, Karel
AU - Siegle, Joshua H.
AU - Carandini, Matteo
TI - Neuropixels Opto: combining high-resolution electrophysiology and optogenetics
T2 - Nature methods
J2 - Nat Methods
PY - 2026
DA - 2026/
VL - 23
IS - 6
SP - 1207
EP - 1216
SN - 1548-7091
PB - Nature Portfolio
DO - 10.1038/
UR - https://
LA - en
ER -
CSL-JSON
{
"id": "10.1038/
"type": "article-journal",
"title": "Neuropixels Opto: combining high-resolution electrophysiology and optogenetics",
"container-title": "Nature methods",
"author": [
{
"family": "Lakunina",
"given": "Anna A."
},
{
"family": "Socha",
"given": "Karolina Z."
},
{
"family": "Ladd",
"given": "Alexander E."
},
{
"family": "Bowen",
"given": "Anna J."
},
{
"family": "Chen",
"given": "Susu"
},
{
"family": "Colonell",
"given": "Jennifer"
},
{
"family": "Doshi",
"given": "Anjal"
},
{
"family": "Karsh",
"given": "Bill"
},
{
"family": "Krumin",
"given": "Michael"
},
{
"family": "Kulik",
"given": "Pavel"
},
{
"family": "Li",
"given": "Anna J."
},
{
"family": "Neutens",
"given": "Pieter"
},
{
"family": "O’Callaghan",
"given": "John"
},
{
"family": "Olsen",
"given": "Meghan"
},
{
"family": "Putzeys",
"given": "Jan"
},
{
"family": "Bai Reddy",
"given": "Charu"
},
{
"family": "Tilmans",
"given": "Harrie A. C."
},
{
"family": "Vargas",
"given": "Sara"
},
{
"family": "Welkenhuysen",
"given": "Marleen"
},
{
"family": "Ye",
"given": "Zhiwen"
},
{
"family": "Häusser",
"given": "Michael"
},
{
"family": "Koch",
"given": "Christof"
},
{
"family": "Ting",
"given": "Jonathan T."
},
{
"family": "Dutta",
"given": "Barundeb"
},
{
"family": "Harris",
"given": "Timothy D."
},
{
"family": "Steinmetz",
"given": "Nicholas A."
},
{
"family": "Svoboda",
"given": "Karel"
},
{
"family": "Siegle",
"given": "Joshua H."
},
{
"family": "Carandini",
"given": "Matteo"
}
],
"container-title-short":
"volume": "23",
"issue": "6",
"page": "1207-1216",
"DOI": "10.1038/
"PMID": "42225964",
"PMCID": "PMC13259958",
"ISSN": "1548-7091",
"publisher": "Nature Portfolio",
"URL": "https://
"language": "en",
"issued": {
"date-parts": [
[
2026,
6,
1
]
]
}
}
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.7554/elife.110170 [code]
- Efficient and reproducible pipelines for spike sorting large-scale electrophysiology data.Journal: eLifeIn common: SpikeInterface, Neurodata Without Borders (PyNWB, MatNWB), pandas, 1 other tool, extracellular electrophysiology (units, LFP), mouse, 7 references, 2 authors
- [2] doi:10.1038/s41467-026-71331-0 [code]
- A multimodal approach for visualizing and identifying electrophysiological cell types in vivo.Journal: Nature communicationsIn common: PyTorch, pandas, SciPy, 2 other tools, extracellular electrophysiology (units, LFP), mouse, 5 references, 2 authors
- [3] doi:10.7554/elife.110588 [code]
- Opening the black box toward a modular approach to spike sorting.Journal: eLifeIn common: Kilosort, SpikeInterface, Neurodata Without Borders (PyNWB, MatNWB), 6 other tools, extracellular electrophysiology (units, LFP), mouse, 5 references
- [4] doi:10.1038/s41593-026-02232-0 [code]
- Entorhinal cortex represents task-relevant remote locations independently of CA1.Journal: Nature neuroscienceIn common: Kilosort, SpikeInterface, Neurodata Without Borders (PyNWB, MatNWB), 6 other tools, systems, mouse, 2 references
- [5] doi:10.1016/j.patter.2026.101590 [code]
- Density-based longitudinal neuron tracking in high-density electrophysiological recordings.Journal: Patterns (New York, N.Y.)In common: Kilosort, SpikeInterface, h5py, 5 other tools, extracellular electrophysiology (units, LFP), 4 references
- [6] doi:10.1016/j.crmeth.2026.101421 [code]
- EthoPy provides an accessible platform for reproducible behavioral neuroscience.Journal: Cell reports methodsIn common: Neurodata Without Borders (PyNWB, MatNWB), h5py, pandas, 3 other tools, mouse, 7 references
- [7] doi:10.1038/s41467-026-71664-w [code]
- Dorsal prefrontal cortex drives perseverative behavior in mice.Journal: Nature communicationsIn common: Kilosort, NumPy, systems, mouse, 8 references
- [8] doi:10.7554/elife.109717 [code]
- Retrosplenial cortex enables context-dependent goal-directed sensorimotor transformation.Journal: eLifeIn common: Kilosort, Neurodata Without Borders (PyNWB, MatNWB), h5py, 4 other tools, systems, mouse, 3 references
- [9] doi:10.1038/s41467-026-76581-6 [code]
- Thalamocortical bursts encode reward contingencies and drive associative learning.Journal: Nature communicationsIn common: Kilosort, h5py, pandas, 3 other tools, systems, mouse, 4 references
- [10] doi:10.1016/j.celrep.2026.117420 [code]
- Neural population dynamics of direct electrical stimulation of neocortex.Journal: Cell reportsIn common: Neurodata Without Borders (PyNWB, MatNWB), pandas, SciPy, 2 other tools, extracellular electrophysiology (units, LFP), systems, mouse, 4 references
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: 3 repositories of the authors' code, each at its verified commit and with its license, 66 scripts, and 5 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:c97b06f9b1033f83…
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.
