33import numpy as np
44
55from ibllib .atlas import AllenAtlas
6- from ibllib .atlas .regions import BrainRegions
76from ibllib .pipes import histology
87from ibllib .pipes .ephys_alignment import EphysAlignment
98from ibllib .qc import base
109from ibllib .oneibl .patcher import FTPPatcher
1110from ibllib .qc .base import CRITERIA as CRITERIA_BASE
11+ from ibllib .io .spikeglx import _geometry_from_meta , read_meta_data
12+ from ibllib .ephys .neuropixel import trace_header
1213
1314_log = logging .getLogger ('ibllib' )
1415CRITERIA = {"PASS" : 0.8 }
@@ -37,7 +38,7 @@ def __init__(self, probe_id, one=None, brain_atlas=None, channels=True, collecti
3738 # Get the brain atlas
3839 self .brain_atlas = brain_atlas or AllenAtlas (25 )
3940 # Flag for uploading channels to alyx. For testing purposes
40- self .channels = channels
41+ self .channels_flag = channels
4142
4243 self .insertion = self .one .alyx .get (f'/insertions/{ self .eid } ' , clobber = True )
4344 self .resolved = (self .insertion .get ('json' , {'temp' : 0 }).get ('extended_qc' ).
@@ -66,14 +67,14 @@ def load_data(self, prev_alignments=None, xyz_picks=None, depths=None, cluster_c
6667 align_keys = [* self .alignments .keys ()]
6768 self .align_keys_sorted = sorted (align_keys , reverse = True )
6869
69- if len (self .alignments ) < 2 :
70- return
71-
7270 if not np .any (xyz_picks ):
7371 self .xyz_picks = np .array (self .insertion ['json' ]['xyz_picks' ]) / 1e6
7472 else :
7573 self .xyz_picks = xyz_picks
7674
75+ if len (self .alignments ) < 2 :
76+ return
77+
7778 if not self .probe_collection :
7879 all_collections = self .one .list_collections (self .insertion ['session' ])
7980 if f'alf/{ self .insertion ["name" ]} /pykilosort' in all_collections :
@@ -182,7 +183,11 @@ def resolve_manual(self, align_key, update=True, upload_alyx=True, upload_flatir
182183 f"set 'force=True'" )
183184 file_paths = []
184185 else :
185- results = self .compute_alignment_status ()
186+ if self .sim_matrix .shape [0 ] > 1 :
187+ results = self .compute_alignment_status ()
188+ else :
189+ results = {'alignment_count' : self .sim_matrix .shape [0 ]}
190+
186191 results ['alignment_resolved' ] = True
187192 results ['alignment_stored' ] = align_key
188193 results ['alignment_resolved_by' ] = 'experimenter'
@@ -203,7 +208,7 @@ def compute_similarity_matrix(self):
203208 parent brain region
204209 """
205210
206- r = BrainRegions ()
211+ r = self . brain_atlas . regions
207212
208213 clusters = dict ()
209214 for iK , key in enumerate (self .align_keys_sorted ):
@@ -248,7 +253,7 @@ def compute_alignment_status(self):
248253 """
249254 # Set diagonals to zero so we don't use those to find max
250255 np .fill_diagonal (self .sim_matrix , 0 )
251- # self.sim_matrix[self.sim_matrix == 1] = 0
256+
252257 max_sim = np .max (self .sim_matrix )
253258
254259 results = {'alignment_qc' : max_sim ,
@@ -260,14 +265,10 @@ def compute_alignment_status(self):
260265 results .update ({'alignment_resolved' : True })
261266 results .update ({'alignment_resolved_by' : 'qc' })
262267
263- # outcome = 'PASS'
264-
265268 else :
266269 results .update ({'alignment_stored' : self .align_keys_sorted [0 ]})
267270 results .update ({'alignment_resolved' : False })
268271
269- # outcome = 'WARNING'
270-
271272 return results
272273
273274 def upload_channels (self , alignment_key , upload_alyx , upload_flatiron ):
@@ -277,73 +278,89 @@ def upload_channels(self, alignment_key, upload_alyx, upload_flatiron):
277278
278279 feature = np .array (self .alignments [alignment_key ][0 ])
279280 track = np .array (self .alignments [alignment_key ][1 ])
280- ephysalign = EphysAlignment (self .xyz_picks , self .depths ,
281+
282+ try :
283+ meta_dset = self .one .list_datasets (self .insertion ['session' ], '*ap.meta' ,
284+ collection = f'raw_ephys_data/{ self .insertion ["name" ]} ' )
285+
286+ meta_file = self .one .load_dataset (self .insertion ['session' ], meta_dset [0 ].split ('/' )[- 1 ],
287+ collection = f'raw_ephys_data/{ self .insertion ["name" ]} ' ,
288+ download_only = True )
289+ geometry = _geometry_from_meta (read_meta_data (meta_file ))
290+ chns = np .c_ [geometry ['x' ], geometry ['y' ]]
291+ except Exception as err :
292+ self .log .warning (f"Could not compute channel locations from meta file, errored with message: { err } . "
293+ f"Will use default Neuropixel 1 channels" )
294+ geometry = trace_header (version = 1 )
295+ chns = np .c_ [geometry ['x' ], geometry ['y' ]]
296+
297+ ephysalign = EphysAlignment (self .xyz_picks , chns [:, 1 ],
281298 track_prev = track ,
282299 feature_prev = feature ,
283300 brain_atlas = self .brain_atlas )
284-
285- # Find the channels
286301 channels_mlapdv = np .int32 (ephysalign .get_channel_locations (feature , track ) * 1e6 )
287- channels_brainID = ephysalign .get_brain_locations (channels_mlapdv / 1e6 )['id' ]
302+ channels_atlas_id = ephysalign .get_brain_locations (channels_mlapdv / 1e6 )['id' ]
288303
289- # Find the clusters
290- r = BrainRegions ()
291- clusters_mlapdv = channels_mlapdv [self .cluster_chns ]
292- clusters_brainID = channels_brainID [self .cluster_chns ]
293- clusters_brainAcro = r .get (ids = clusters_brainID ).acronym
304+ # Need to change channels stored on alyx as well as the stored key is not the same as the latest key
305+ if upload_alyx :
306+ if alignment_key != self .align_keys_sorted [0 ]:
307+ histology .register_aligned_track (self .eid , channels_mlapdv / 1e6 ,
308+ chn_coords = chns , one = self .one ,
309+ overwrite = True , channels = self .channels_flag )
310+
311+ ephys_traj = self .one .alyx .get (f'/trajectories?&probe_insertion={ self .eid } '
312+ '&provenance=Ephys aligned histology track' ,
313+ clobber = True )
314+ patch_dict = {'json' : self .alignments }
315+ self .one .alyx .rest ('trajectories' , 'partial_update' , id = ephys_traj [0 ]['id' ],
316+ data = patch_dict )
294317
295- # upload datasets to flatiron
296318 files_to_register = []
297319 if upload_flatiron :
298320 ftp_patcher = FTPPatcher (one = self .one )
299- insertion = self . one . alyx . get ( f'/insertions/ { self . eid } ' , clobber = True )
300- alf_path = self .one .eid2path (insertion ['session' ]).joinpath (self .probe_collection )
321+
322+ alf_path = self .one .eid2path (self . insertion ['session' ]).joinpath ('alf' , self .insertion [ "name" ] )
301323 alf_path .mkdir (exist_ok = True , parents = True )
302324
303- # Make the channels.mlapdv dataset
304- f_name = alf_path .joinpath ('channels.mlapdv.npy' )
325+ f_name = alf_path .joinpath ('electrodeSites.mlapdv.npy' )
305326 np .save (f_name , channels_mlapdv )
306327 files_to_register .append (f_name )
307328
308- # Make the channels.brainLocationIds dataset
309- f_name = alf_path .joinpath ('channels.brainLocationIds_ccf_2017.npy' )
310- np .save (f_name , channels_brainID )
329+ f_name = alf_path .joinpath ('electrodeSites.brainLocationIds_ccf_2017.npy' )
330+ np .save (f_name , channels_atlas_id )
311331 files_to_register .append (f_name )
312332
313- # Make the clusters.mlapdv dataset
314- f_name = alf_path .joinpath ('clusters.mlapdv.npy' )
315- np .save (f_name , clusters_mlapdv )
333+ f_name = alf_path .joinpath ('electrodeSites.localCoordinates.npy' )
334+ np .save (f_name , chns )
316335 files_to_register .append (f_name )
317336
318- # Make the clusters.brainLocationIds dataset
319- f_name = alf_path .joinpath ('clusters.brainLocationIds_ccf_2017.npy' )
320- np .save (f_name , clusters_brainID )
321- files_to_register .append (f_name )
337+ probe_collections = self .one .list_collections (self .insertion ['session' ], filename = 'channels*' ,
338+ collection = f'alf/{ self .insertion ["name" ]} *' )
322339
323- # Make the clusters.brainLocationAcronym dataset
324- f_name = alf_path .joinpath ('clusters.brainLocationAcronyms_ccf_2017.npy' )
325- np .save (f_name , clusters_brainAcro )
326- files_to_register .append (f_name )
340+ for collection in probe_collections :
341+ chns = self .one .load_dataset (self .insertion ['session' ], 'channels.localCoordinates' , collection = collection )
342+ ephysalign = EphysAlignment (self .xyz_picks , chns [:, 1 ],
343+ track_prev = track ,
344+ feature_prev = feature ,
345+ brain_atlas = self .brain_atlas )
346+ channels_mlapdv = np .int32 (ephysalign .get_channel_locations (feature , track ) * 1e6 )
347+ channels_atlas_id = ephysalign .get_brain_locations (channels_mlapdv / 1e6 )['id' ]
348+
349+ alf_path = self .one .eid2path (self .insertion ['session' ]).joinpath (collection )
350+ alf_path .mkdir (exist_ok = True , parents = True )
351+
352+ f_name = alf_path .joinpath ('channels.mlapdv.npy' )
353+ np .save (f_name , channels_mlapdv )
354+ files_to_register .append (f_name )
355+
356+ f_name = alf_path .joinpath ('channels.brainLocationIds_ccf_2017.npy' )
357+ np .save (f_name , channels_atlas_id )
358+ files_to_register .append (f_name )
327359
328360 self .log .info ("Writing datasets to FlatIron" )
329361 ftp_patcher .create_dataset (path = files_to_register ,
330362 created_by = self .one .alyx .user )
331363
332- # Need to change channels stored on alyx as well as the stored key is not the same as the
333- # latest key
334- if upload_alyx :
335- if alignment_key != self .align_keys_sorted [0 ]:
336- histology .register_aligned_track (self .eid , channels_mlapdv / 1e6 ,
337- chn_coords = self .chn_coords , one = self .one ,
338- overwrite = True , channels = self .channels )
339-
340- ephys_traj = self .one .alyx .get (f'/trajectories?&probe_insertion={ self .eid } '
341- '&provenance=Ephys aligned histology track' ,
342- clobber = True )
343- patch_dict = {'json' : self .alignments }
344- self .one .alyx .rest ('trajectories' , 'partial_update' , id = ephys_traj [0 ]['id' ],
345- data = patch_dict )
346-
347364 return files_to_register
348365
349366 def update_experimenter_evaluation (self , prev_alignments = None , override = False ):
0 commit comments