From 97ef82a3c3b5f8098cf2c18af60e800251093c02 Mon Sep 17 00:00:00 2001 From: Christian O'Reilly Date: Fri, 14 Oct 2022 14:30:05 -0400 Subject: [PATCH 1/7] NEURON compatible morphologies from ArrayMorphology. --- neuroml/arraymorph.py | 138 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 128 insertions(+), 10 deletions(-) diff --git a/neuroml/arraymorph.py b/neuroml/arraymorph.py index 3e2123ee..84692502 100644 --- a/neuroml/arraymorph.py +++ b/neuroml/arraymorph.py @@ -2,8 +2,39 @@ Prototype for object model backend for the libNeuroML project """ + import numpy as np import neuroml +from pyneuroml.pynml import print_function # type: ignore + + +neuro_lex_ids = { + 'axon': "GO:0030424", + 'dend': "GO:0030425", + 'soma': "GO:0043025", +} + + +def get_seg_group_by_id(sg_id, morph): + # type (str, Cell) -> SegmentGroup + """Return the SegmentGroup object for the specified segment group id. + + :param sg_id: id of segment group to find + :type sg_id: str + :param cell: cell to look for segment group in + :type cell: Cell + :returns: SegmentGroup object of specified ID or None if not found + + """ + if not sg_id or not morph: + print_function("Please specify both a segment group id and a Morphology") + return None + + for sg in morph.segment_groups: + if sg.id == sg_id: + return sg + + return None class ArrayMorphology(neuroml.Morphology): @@ -44,7 +75,11 @@ def __init__( self.connectivity = np.array(connectivity) self.vertices = np.array(vertices) - self.id = id + if id is None: + self.id = name + else: + self.id = id + self.name = name if np.any(physical_mask): self.physical_mask = np.array(physical_mask) @@ -171,19 +206,45 @@ def pop(self, index): k += 1 pass - def to_neuroml_morphology(self, id=""): + def to_neuroml_morphology(self, id="", add_color_prop=False): morphology = neuroml.Morphology() - morphology.id = id + if id: + morphology.id = id + else: + morphology.id = self.id + + seg_group_all = neuroml.SegmentGroup(id='all') + seg_group_soma = neuroml.SegmentGroup(id='soma_group', + neuro_lex_id=neuro_lex_ids["soma"], + notes="Default soma segment group for the cell") + seg_group_axon = neuroml.SegmentGroup(id='axon_group', + neuro_lex_id=neuro_lex_ids["axon"], + notes="Default axon segment group for the cell") + seg_group_dend = neuroml.SegmentGroup(id='dendrite_group', + neuro_lex_id=neuro_lex_ids["dend"], + notes="Default dendrite segment group for the cell") + + if add_color_prop: + seg_group_soma.properties.append(neuroml.Property(tag="color", value="0.8 0 0")) + seg_group_axon.properties.append(neuroml.Property(tag="color", value="0 0.8 0")) + seg_group_dend.properties.append(neuroml.Property(tag="color", value="0 0 0.8")) + + morphology.segment_groups.append(seg_group_all) + morphology.segment_groups.append(seg_group_soma) + morphology.segment_groups.append(seg_group_axon) + morphology.segment_groups.append(seg_group_dend) # need to traverse the tree: - for index in range(self.num_vertices - 1): - seg = self.segment_from_vertex_index(index) + for index in range(self.num_vertices): + seg = self.segment_from_vertex_index(index, morphology) morphology.segments.append(seg) + self.group_segments(morphology) + return morphology - def segment_from_vertex_index(self, index): + def segment_from_vertex_index(self, index, morphology): parent_index = self.connectivity[index] node_x = self.vertices[index][0] @@ -196,19 +257,76 @@ def segment_from_vertex_index(self, index): parent_z = self.vertices[parent_index][2] parent_d = self.vertices[parent_index][3] + p = neuroml.Point3DWithDiam(x=node_x, y=node_y, z=node_z, diameter=node_d) - d = neuroml.Point3DWithDiam( - x=parent_x, y=parent_y, z=parent_z, diameter=parent_d - ) + d = neuroml.Point3DWithDiam(x=parent_x, y=parent_y, + z=parent_z, diameter=parent_d) seg = neuroml.Segment(proximal=p, distal=d, id=index) - if index > 1: + if parent_index != -1: parent = neuroml.SegmentParent(segments=parent_index) seg.parent = parent return seg + def group_segments(self, morphology): + + non_branching_lex_id = 'sao864921383' + + N = len(self.connectivity) + ind_root = np.where(self.connectivity == -1)[0] + assert(len(ind_root) == 1) + assert(not np.any(np.arange(N) == self.connectivity)) + ind_root = ind_root[0] + + nodes, counts = np.unique(self.connectivity, return_counts=True) + nodes = nodes[counts > 1] + + parent_trunks = np.arange(N) + # parent_trunks are the children of parent_nodes. + # They are the trunk of non-branching sections + for ind, parent_node in enumerate(self.connectivity): + if ind == ind_root: + continue + while parent_node not in nodes: + # Go one step back + parent_trunks[ind] = parent_node + parent_node = self.connectivity[parent_node] + + seg_prefixes = {1: "soma", 2: "axon", 3: "dend", 4: "apical"} + default_groups = {1: get_seg_group_by_id("soma_group", morphology), + 2: get_seg_group_by_id("axon_group", morphology), + 3: get_seg_group_by_id("dendrite_group", morphology), + 4: get_seg_group_by_id("dendrite_group", morphology), + } + + trunks = np.unique(parent_trunks) + for trunk in trunks: + if self.node_types[trunk] in seg_prefixes: + trunk_type = seg_prefixes[self.node_types[trunk]] + else: + trunk_type = "other" + + group_id = f"{trunk_type}_{trunk}" + seg_group = neuroml.SegmentGroup(id=group_id, + neuro_lex_id=non_branching_lex_id) + for ind in np.arange(N)[parent_trunks == trunk]: + seg_group.members.append(neuroml.Member(segments=morphology.segments[ind].id)) + + # Add the segment group to the morphology + morphology.segment_groups.insert(0, seg_group) + + # Include the segment group in the corresponding anatomical group + default_group = default_groups[self.node_types[trunk]] + default_group.includes.append(neuroml.Include(segment_groups=group_id)) + + # Add to the all group + seg_group_all = get_seg_group_by_id("all", morphology) + seg_group_all.includes.append(neuroml.Include(segment_groups=group_id)) + + + class SegmentList(object): """ From c3532cc43fadb4b1f758511c65f6326014e5c13c Mon Sep 17 00:00:00 2001 From: Christian O'Reilly Date: Tue, 29 Nov 2022 14:50:45 -0500 Subject: [PATCH 2/7] Fixing problematic import of print_function --- neuroml/arraymorph.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/neuroml/arraymorph.py b/neuroml/arraymorph.py index 84692502..5b696b8b 100644 --- a/neuroml/arraymorph.py +++ b/neuroml/arraymorph.py @@ -5,7 +5,7 @@ import numpy as np import neuroml -from pyneuroml.pynml import print_function # type: ignore +from __future__ import print_function # type: ignore neuro_lex_ids = { From 79238cda99746873777137c0b672fe63d40cebfa Mon Sep 17 00:00:00 2001 From: Christian O'Reilly Date: Tue, 29 Nov 2022 15:05:30 -0500 Subject: [PATCH 3/7] Update arraymorph.py --- neuroml/arraymorph.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/neuroml/arraymorph.py b/neuroml/arraymorph.py index 5b696b8b..19cec3e2 100644 --- a/neuroml/arraymorph.py +++ b/neuroml/arraymorph.py @@ -5,7 +5,6 @@ import numpy as np import neuroml -from __future__ import print_function # type: ignore neuro_lex_ids = { @@ -27,7 +26,7 @@ def get_seg_group_by_id(sg_id, morph): """ if not sg_id or not morph: - print_function("Please specify both a segment group id and a Morphology") + print("Please specify both a segment group id and a Morphology") return None for sg in morph.segment_groups: From 256b91d8da093cbdcfe1e8b24bf507f64b71cce2 Mon Sep 17 00:00:00 2001 From: Christian O'Reilly Date: Fri, 14 Oct 2022 14:30:05 -0400 Subject: [PATCH 4/7] NEURON compatible morphologies from ArrayMorphology. --- neuroml/arraymorph.py | 138 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 128 insertions(+), 10 deletions(-) diff --git a/neuroml/arraymorph.py b/neuroml/arraymorph.py index 3e2123ee..84692502 100644 --- a/neuroml/arraymorph.py +++ b/neuroml/arraymorph.py @@ -2,8 +2,39 @@ Prototype for object model backend for the libNeuroML project """ + import numpy as np import neuroml +from pyneuroml.pynml import print_function # type: ignore + + +neuro_lex_ids = { + 'axon': "GO:0030424", + 'dend': "GO:0030425", + 'soma': "GO:0043025", +} + + +def get_seg_group_by_id(sg_id, morph): + # type (str, Cell) -> SegmentGroup + """Return the SegmentGroup object for the specified segment group id. + + :param sg_id: id of segment group to find + :type sg_id: str + :param cell: cell to look for segment group in + :type cell: Cell + :returns: SegmentGroup object of specified ID or None if not found + + """ + if not sg_id or not morph: + print_function("Please specify both a segment group id and a Morphology") + return None + + for sg in morph.segment_groups: + if sg.id == sg_id: + return sg + + return None class ArrayMorphology(neuroml.Morphology): @@ -44,7 +75,11 @@ def __init__( self.connectivity = np.array(connectivity) self.vertices = np.array(vertices) - self.id = id + if id is None: + self.id = name + else: + self.id = id + self.name = name if np.any(physical_mask): self.physical_mask = np.array(physical_mask) @@ -171,19 +206,45 @@ def pop(self, index): k += 1 pass - def to_neuroml_morphology(self, id=""): + def to_neuroml_morphology(self, id="", add_color_prop=False): morphology = neuroml.Morphology() - morphology.id = id + if id: + morphology.id = id + else: + morphology.id = self.id + + seg_group_all = neuroml.SegmentGroup(id='all') + seg_group_soma = neuroml.SegmentGroup(id='soma_group', + neuro_lex_id=neuro_lex_ids["soma"], + notes="Default soma segment group for the cell") + seg_group_axon = neuroml.SegmentGroup(id='axon_group', + neuro_lex_id=neuro_lex_ids["axon"], + notes="Default axon segment group for the cell") + seg_group_dend = neuroml.SegmentGroup(id='dendrite_group', + neuro_lex_id=neuro_lex_ids["dend"], + notes="Default dendrite segment group for the cell") + + if add_color_prop: + seg_group_soma.properties.append(neuroml.Property(tag="color", value="0.8 0 0")) + seg_group_axon.properties.append(neuroml.Property(tag="color", value="0 0.8 0")) + seg_group_dend.properties.append(neuroml.Property(tag="color", value="0 0 0.8")) + + morphology.segment_groups.append(seg_group_all) + morphology.segment_groups.append(seg_group_soma) + morphology.segment_groups.append(seg_group_axon) + morphology.segment_groups.append(seg_group_dend) # need to traverse the tree: - for index in range(self.num_vertices - 1): - seg = self.segment_from_vertex_index(index) + for index in range(self.num_vertices): + seg = self.segment_from_vertex_index(index, morphology) morphology.segments.append(seg) + self.group_segments(morphology) + return morphology - def segment_from_vertex_index(self, index): + def segment_from_vertex_index(self, index, morphology): parent_index = self.connectivity[index] node_x = self.vertices[index][0] @@ -196,19 +257,76 @@ def segment_from_vertex_index(self, index): parent_z = self.vertices[parent_index][2] parent_d = self.vertices[parent_index][3] + p = neuroml.Point3DWithDiam(x=node_x, y=node_y, z=node_z, diameter=node_d) - d = neuroml.Point3DWithDiam( - x=parent_x, y=parent_y, z=parent_z, diameter=parent_d - ) + d = neuroml.Point3DWithDiam(x=parent_x, y=parent_y, + z=parent_z, diameter=parent_d) seg = neuroml.Segment(proximal=p, distal=d, id=index) - if index > 1: + if parent_index != -1: parent = neuroml.SegmentParent(segments=parent_index) seg.parent = parent return seg + def group_segments(self, morphology): + + non_branching_lex_id = 'sao864921383' + + N = len(self.connectivity) + ind_root = np.where(self.connectivity == -1)[0] + assert(len(ind_root) == 1) + assert(not np.any(np.arange(N) == self.connectivity)) + ind_root = ind_root[0] + + nodes, counts = np.unique(self.connectivity, return_counts=True) + nodes = nodes[counts > 1] + + parent_trunks = np.arange(N) + # parent_trunks are the children of parent_nodes. + # They are the trunk of non-branching sections + for ind, parent_node in enumerate(self.connectivity): + if ind == ind_root: + continue + while parent_node not in nodes: + # Go one step back + parent_trunks[ind] = parent_node + parent_node = self.connectivity[parent_node] + + seg_prefixes = {1: "soma", 2: "axon", 3: "dend", 4: "apical"} + default_groups = {1: get_seg_group_by_id("soma_group", morphology), + 2: get_seg_group_by_id("axon_group", morphology), + 3: get_seg_group_by_id("dendrite_group", morphology), + 4: get_seg_group_by_id("dendrite_group", morphology), + } + + trunks = np.unique(parent_trunks) + for trunk in trunks: + if self.node_types[trunk] in seg_prefixes: + trunk_type = seg_prefixes[self.node_types[trunk]] + else: + trunk_type = "other" + + group_id = f"{trunk_type}_{trunk}" + seg_group = neuroml.SegmentGroup(id=group_id, + neuro_lex_id=non_branching_lex_id) + for ind in np.arange(N)[parent_trunks == trunk]: + seg_group.members.append(neuroml.Member(segments=morphology.segments[ind].id)) + + # Add the segment group to the morphology + morphology.segment_groups.insert(0, seg_group) + + # Include the segment group in the corresponding anatomical group + default_group = default_groups[self.node_types[trunk]] + default_group.includes.append(neuroml.Include(segment_groups=group_id)) + + # Add to the all group + seg_group_all = get_seg_group_by_id("all", morphology) + seg_group_all.includes.append(neuroml.Include(segment_groups=group_id)) + + + class SegmentList(object): """ From 66ec55f11c4e356adde84dff006cbb6c184dc215 Mon Sep 17 00:00:00 2001 From: Christian O'Reilly Date: Tue, 29 Nov 2022 14:50:45 -0500 Subject: [PATCH 5/7] Fixing problematic import of print_function --- neuroml/arraymorph.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/neuroml/arraymorph.py b/neuroml/arraymorph.py index 84692502..5b696b8b 100644 --- a/neuroml/arraymorph.py +++ b/neuroml/arraymorph.py @@ -5,7 +5,7 @@ import numpy as np import neuroml -from pyneuroml.pynml import print_function # type: ignore +from __future__ import print_function # type: ignore neuro_lex_ids = { From 3a370585ff70839abbaf89c3d50bb094507bc97a Mon Sep 17 00:00:00 2001 From: Christian O'Reilly Date: Tue, 29 Nov 2022 15:05:30 -0500 Subject: [PATCH 6/7] Update arraymorph.py --- neuroml/arraymorph.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/neuroml/arraymorph.py b/neuroml/arraymorph.py index 5b696b8b..19cec3e2 100644 --- a/neuroml/arraymorph.py +++ b/neuroml/arraymorph.py @@ -5,7 +5,6 @@ import numpy as np import neuroml -from __future__ import print_function # type: ignore neuro_lex_ids = { @@ -27,7 +26,7 @@ def get_seg_group_by_id(sg_id, morph): """ if not sg_id or not morph: - print_function("Please specify both a segment group id and a Morphology") + print("Please specify both a segment group id and a Morphology") return None for sg in morph.segment_groups: From a64056efcbfd1d490137f35e07b88acb936a0350 Mon Sep 17 00:00:00 2001 From: Christian O'Reilly Date: Tue, 29 Nov 2022 15:33:19 -0500 Subject: [PATCH 7/7] Silence option for loaders. --- neuroml/loaders.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/neuroml/loaders.py b/neuroml/loaders.py index 45db658f..9149dd22 100644 --- a/neuroml/loaders.py +++ b/neuroml/loaders.py @@ -25,8 +25,8 @@ def print_(text, verbose=True): class NeuroMLLoader(object): @classmethod - def load(cls, src): - doc = cls.__nml2_doc(src) + def load(cls, src, silence=True): + doc = cls.__nml2_doc(src, silence) if isinstance(doc, neuroml.nml.nml.NeuroMLDocument): return doc else: @@ -37,12 +37,13 @@ def load(cls, src): ) @classmethod - def __nml2_doc(cls, file_name): + def __nml2_doc(cls, file_name, silence=True): + try: if supressGeneratedsWarnings: warnings.simplefilter("ignore") - nml2_doc = nmlparse(file_name, silence=True) + nml2_doc = nmlparse(file_name, silence=silence) if supressGeneratedsWarnings: warnings.resetwarnings() except Exception as e: @@ -97,7 +98,7 @@ class SWCLoader(object): """ @classmethod - def load_swc_single(cls, src, name=None): + def load_swc_single(cls, src, id=None): import numpy as np from neuroml import arraymorph @@ -147,7 +148,7 @@ def load_swc_single(cls, src, name=None): vertices=vertices, connectivity=connection_indices, node_types=section_types, - name=name, + id=id, )