Skip to content

Commit 819ff19

Browse files
author
Thinh Nguyen
committed
first prototype for pykilosort
1 parent df599fb commit 819ff19

File tree

2 files changed

+105
-34
lines changed

2 files changed

+105
-34
lines changed

element_array_ephys/ephys_no_curation.py

Lines changed: 79 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -583,53 +583,61 @@ def make(self, key):
583583
* ClusteringParamSet & key).fetch1(
584584
'acq_software', 'clustering_method', 'params')
585585

586-
if clustering_method.startswith('kilosort'):
586+
if 'kilosort' in clustering_method:
587587
from element_array_ephys.readers import kilosort_triggering
588588

589+
# add additional probe-recording and channels details into `params`
590+
params = {**params, **get_recording_channels_details(key)}
591+
589592
if acq_software == 'SpikeGLX':
590593
spikeglx_meta_filepath = get_spikeglx_meta_filepath(key)
591594
spikeglx_recording = spikeglx.SpikeGLX(spikeglx_meta_filepath.parent)
592595
spikeglx_recording.validate_file('ap')
593596

594-
run_kilosort = kilosort_triggering.SGLXKilosortPipeline(
595-
npx_input_dir=spikeglx_meta_filepath.parent,
596-
ks_output_dir=kilosort_dir,
597-
params=params,
598-
KS2ver=f'{Decimal(clustering_method.replace("kilosort", "")):.1f}',
599-
run_CatGT=False)
600-
run_kilosort.run_modules()
597+
if clustering_method.startswith('pykilosort'):
598+
kilosort_triggering.run_pykilosort(
599+
continuous_file=spikeglx_recording.root_dir / (
600+
spikeglx_recording.root_name + '.ap.bin'),
601+
kilosort_output_directory=kilosort_dir,
602+
channel_ind=params.pop('channel_ind'),
603+
x_coords=params.pop('x_coords'),
604+
y_coords=params.pop('y_coords'),
605+
shank_ind=params.pop('shank_ind'),
606+
connected=params.pop('connected'),
607+
sample_rate=params.pop('sample_rate'),
608+
params=params)
609+
else:
610+
run_kilosort = kilosort_triggering.SGLXKilosortPipeline(
611+
npx_input_dir=spikeglx_meta_filepath.parent,
612+
ks_output_dir=kilosort_dir,
613+
params=params,
614+
KS2ver=f'{Decimal(clustering_method.replace("kilosort", "")):.1f}',
615+
run_CatGT=False)
616+
run_kilosort.run_modules()
601617
elif acq_software == 'Open Ephys':
602618
oe_probe = get_openephys_probe_data(key)
603619

604620
assert len(oe_probe.recording_info['recording_files']) == 1
605621

606-
# add additional probe-recording settings into `params`
607-
probe_type = (ProbeInsertion * probe.Probe & key).fetch1('probe_type')
608-
params['probe_type'] = {'neuropixels 1.0 - 3A': '3A',
609-
'neuropixels 1.0 - 3B': 'NP1',
610-
'neuropixels UHD': 'NP1100',
611-
'neuropixels 2.0 - SS': 'NP21',
612-
'neuropixels 2.0 - MS': 'NP24'}[probe_type]
613-
params['sample_rate'] = oe_probe.ap_meta['sample_rate']
614-
params['num_channels'] = oe_probe.ap_meta['num_channels']
615-
params['uVPerBit'] = oe_probe.ap_meta['channels_gains'][0]
616-
617-
# add additional electrodes information into `params`
618-
electrode_config_key = (probe.ElectrodeConfig * EphysRecording & key).fetch1('KEY')
619-
params['channel_ind'], params['x_coords'], params['y_coords'], params['shank_ind'] = (
620-
probe.ElectrodeConfig.Electrode * probe.ProbeType.Electrode
621-
& electrode_config_key).fetch('electrode', 'x_coord', 'y_coord', 'shank')
622-
params['connected'] = np.array([int(v == 1)
623-
for c, v in oe_probe.channel_status.items()
624-
if c in params['channel_ind']])
625-
626622
# run kilosort
627-
run_kilosort = kilosort_triggering.OpenEphysKilosortPipeline(
628-
npx_input_dir=oe_probe.recording_info['recording_files'][0],
629-
ks_output_dir=kilosort_dir,
630-
params=params,
631-
KS2ver=f'{Decimal(clustering_method.replace("kilosort", "")):.1f}')
632-
run_kilosort.run_modules()
623+
if clustering_method.startswith('pykilosort'):
624+
kilosort_triggering.run_pykilosort(
625+
continuous_file=pathlib.Path(oe_probe.recording_info['recording_files'][0]) / 'continuous.dat',
626+
kilosort_output_directory=kilosort_dir,
627+
channel_ind=params.pop('channel_ind'),
628+
x_coords=params.pop('x_coords'),
629+
y_coords=params.pop('y_coords'),
630+
shank_ind=params.pop('shank_ind'),
631+
connected=params.pop('connected'),
632+
sample_rate=params.pop('sample_rate'),
633+
params=params)
634+
else:
635+
run_kilosort = kilosort_triggering.OpenEphysKilosortPipeline(
636+
npx_input_dir=oe_probe.recording_info['recording_files'][0],
637+
ks_output_dir=kilosort_dir,
638+
params=params,
639+
KS2ver=f'{Decimal(clustering_method.replace("kilosort", "")):.1f}')
640+
run_kilosort.run_modules()
633641
else:
634642
raise NotImplementedError(f'Automatic triggering of {clustering_method}'
635643
f' clustering analysis is not yet supported')
@@ -929,3 +937,40 @@ def generate_electrode_config(probe_type: str, electrodes: list):
929937

930938
return electrode_config_key
931939

940+
941+
def get_recording_channels_details(ephys_recording_key):
942+
channels_details = {}
943+
944+
acq_software, sample_rate = (EphysRecording & ephys_recording_key).fetch1('acq_software',
945+
'sampling_rate')
946+
947+
probe_type = (ProbeInsertion * probe.Probe & ephys_recording_key).fetch1('probe_type')
948+
channels_details['probe_type'] = {'neuropixels 1.0 - 3A': '3A',
949+
'neuropixels 1.0 - 3B': 'NP1',
950+
'neuropixels UHD': 'NP1100',
951+
'neuropixels 2.0 - SS': 'NP21',
952+
'neuropixels 2.0 - MS': 'NP24'}[probe_type]
953+
954+
electrode_config_key = (probe.ElectrodeConfig * EphysRecording & ephys_recording_key).fetch1('KEY')
955+
channels_details['channel_ind'], channels_details['x_coords'], channels_details[
956+
'y_coords'], channels_details['shank_ind'] = (
957+
probe.ElectrodeConfig.Electrode * probe.ProbeType.Electrode
958+
& electrode_config_key).fetch('electrode', 'x_coord', 'y_coord', 'shank')
959+
channels_details['sample_rate'] = sample_rate
960+
channels_details['num_channels'] = len(channels_details['channel_ind'])
961+
962+
if acq_software == 'SpikeGLX':
963+
spikeglx_meta_filepath = get_spikeglx_meta_filepath(ephys_recording_key)
964+
spikeglx_recording = spikeglx.SpikeGLX(spikeglx_meta_filepath.parent)
965+
channels_details['uVPerBit'] = spikeglx_recording.get_channel_bit_volts('ap')[0]
966+
channels_details['connected'] = np.array(
967+
[v for *_, v in spikeglx_recording.apmeta.shankmap['data']])
968+
elif acq_software == 'Open Ephys':
969+
oe_probe = get_openephys_probe_data(ephys_recording_key)
970+
channels_details['uVPerBit'] = oe_probe.ap_meta['channels_gains'][0]
971+
channels_details['connected'] = np.array([int(v == 1)
972+
for c, v in
973+
oe_probe.channel_status.items()
974+
if c in channels_details['channel_ind']])
975+
976+
return channels_details

element_array_ephys/readers/kilosort_triggering.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,12 @@
2020
except Exception as e:
2121
print(f'Error in loading "ecephys_spike_sorting" package - {str(e)}')
2222

23+
# import pykilosort package
24+
try:
25+
import pykilosort
26+
except Exception as e:
27+
print(f'Error in loading "pykilosort" package - {str(e)}')
28+
2329

2430
class SGLXKilosortPipeline:
2531
"""
@@ -391,6 +397,26 @@ def _get_module_status(self, module):
391397
return {'start_time': None, 'completion_time': None, 'duration': None}
392398

393399

400+
def run_pykilosort(continuous_file, kilosort_output_directory, params,
401+
channel_ind, x_coords, y_coords, shank_ind, connected, sample_rate):
402+
dat_path = pathlib.Path(continuous_file)
403+
404+
probe = pykilosort.Bunch()
405+
channel_count = len(channel_ind)
406+
probe.Nchan = channel_count
407+
probe.chanMap = np.arange(0, channel_count, dtype='float64')
408+
probe.xc = x_coords
409+
probe.yc = y_coords
410+
probe.kcoords = shank_ind
411+
412+
pykilosort.run(dat_path=continuous_file,
413+
dir_path=dat_path.parent,
414+
output_dir=kilosort_output_directory,
415+
probe=probe,
416+
params=params,
417+
n_channels=385, dtype=np.int16, sample_rate=sample_rate)
418+
419+
394420
def _get_kilosort_repository(KS2ver):
395421
"""
396422
Get the path to where the kilosort package is installed at, assuming it can be found

0 commit comments

Comments
 (0)