-
Notifications
You must be signed in to change notification settings - Fork 31
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feat/export swc:- This aims to add a file which exports SWC file into neuroml. #407
Feat/export swc:- This aims to add a file which exports SWC file into neuroml. #407
Conversation
Hi @sanjayankur31 I have refactored the code. Can you give it a look if time permits? |
Cool, will have a look at it today or tomorrow hopefully. |
Did we add any unit tests for the writer @AdityaBITMESRA ? (Not seeing them in this PR) |
No, I will add them soon |
I wanted to ask how should I structure my test file like I should build a mock swc data for all the cases? |
Yeh, you can build mock data, or you can even copy the case files from the CVApp if you want, or you can only paste their contents into a Python string in the test? Whatever you think works best. |
…to feat/ExportSWC
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks very good. Getting there. A few notes/queries/suggestions there.
pyneuroml/swc/ExportNML.py
Outdated
|
||
|
||
class NeuroMLWriter: | ||
def __init__(self, swc_graph: "SWCGraph"): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
does SWCGraph
need to be in quotes here?
pyneuroml/swc/ExportNML.py
Outdated
start_point = self.find_start_point() | ||
|
||
print(f"Cell name: {cell_name}") | ||
print(f"Start point: {start_point}") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if you're using these for debugging, use logging
so that you can use logger.debug
and so on. Only stuff that is definitely needed to be printed to the user should use print
.
pyneuroml/swc/ExportNML.py
Outdated
:param this_point: The current SWCNode. | ||
:param parent_point: The parent SWCNode. | ||
:param cable_id: The ID of the current cable. | ||
:param version: The NeuroML version being used. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, we should've mentioned this---NeuroMLv1 is deprecated and not to be used at all. So, no need to have special cases supporting it. We only export to NeuroMLv2.
pyneuroml/swc/ExportNML.py
Outdated
self.next_segment_id += 1 | ||
self.point_indices_vs_seg_ids[this_point.id] = seg_id | ||
|
||
segment = [f' <segment id="{seg_id}" name="Seg_{seg_id}"'] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now that this is all neuroml specific, is there a reason we're not creating objects using the libneuroml API? (Segment
and SegmentGroup
)? It's usually better than creating xml by hand.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can I get some more ideas on how to proceed with this? I am bit confused about using the libneuroml API.
@sanjayankur31
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, see this example for a start:
https://docs.neuroml.org/Userdocs/MultiCompartmentOLMexample.html#declaring-the-cell
When users use NeuroML, they don't write XML---that's tedious and error prone. Instead, they use the libNeuroML API:
cell = neuroml.Cell(...)
cell.add_segment(...)
..
it's basically a Python object model of the NeuroML schema, so a Cell contains Morphology, which contains Segments and SegmentGroups.
That's a better way of generating the NeuroML than writing XML by hand.
Let me know if this isn't clear and we can have a chat where I can explain it to you more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thankyou I understand it now...will try to implement this
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do take a look at the various "utility functions" in the Cell class, they should help with lots of things (hopefully):
https://libneuroml.readthedocs.io/en/latest/userdocs/coreclasses.html#cell
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've just remembered: if you just add all the segments with their correct parents to the cell, you should be able to use the create_unbranched_segment_group_branches
to automatically create all the unbranched segment groups. It creates the adjacency matrix and then traverses the whole cell detecting branches:
pyneuroml/swc/ExportNML.py
Outdated
], | ||
} | ||
groups.extend( | ||
type_groups.get(p.type, [f"SWC_group_{p.type}", "dendrite_group"]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what are we doing here? if a group for the type is not found, we're using SWC_group_{type}
and dendrite_group
? Not sure if this is correct. If it doesn't belong to the standard types, it shouldn't be valid SWC 🤔
pyneuroml/swc/ExportNML.py
Outdated
self.group_content.append(f" <meta:group>{group}</meta:group>") | ||
self.group_content.append(" </cable>") | ||
else: | ||
for group in self.get_groups_for_type(this_point): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if this is the first point of a new branch, it should also create a new segment group.
ALL "cables" also have to have their own segment groups, in addition to the standard soma/dendrite etc groups. Is this being done?
Thankyou for all the comments @sanjayankur31 I will update after addressing the comments |
Hi @sanjayankur31 I have tried to address all the comments so can you have a look if time permits...it is still not complete but I wanted to know if I am going right or not |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's looking good and you're on the right track. Well done. 👍
Dropped a few comments about things I wasn't completely clear on.
pyneuroml/swc/ExportNML.py
Outdated
Initialize the NeuroMLWriter. | ||
|
||
:param swc_graph: The graph representation of the SWC file. | ||
:type swc_graph: Any # Replace with the actual type of swc_graph if known |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should probably be your SWCGraph
class?
pyneuroml/swc/ExportNML.py
Outdated
|
||
|
||
class NeuroMLWriter: | ||
def __init__(self, swc_graph): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please also remember to add the type hint here.
pyneuroml/swc/ExportNML.py
Outdated
logger.info("Initializing NeuroMLWriter") | ||
self.swc_graph = swc_graph | ||
self.points: List[SWCNode] = swc_graph.nodes | ||
self.section_types: List[str] = [ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
are these not already defined in the other module? Can we re-use that so that the same bits aren't defined in two different locations?
pyneuroml/swc/ExportNML.py
Outdated
self.next_segment_id += 1 | ||
|
||
segment = Segment(id=seg_id, name=f"Seg_{seg_id}") | ||
segment.type = this_point.type |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
neuroml.Segment
does not have a type
attribute
diameter=2 * this_point.radius, | ||
) | ||
|
||
self.cell.morphology.segments.append(segment) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why not also place the segment in the default group here when it has been created? All segments should go into the all
group, and then here since you already check what the segment type is, you can also at least place it in the soma/dendrite/axon group?
Otherwise you're iterating over all segments again in the create_segment_group
pyneuroml/swc/ExportNML.py
Outdated
""" | ||
logger.info("Creating segment groups") | ||
|
||
def get_groups_for_type(segment_type: int) -> List[str]: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
see above comment, can this be done while creating the segment so it's done in the same pass, instead of doing a second pass over all the segments? (if you need information about all segments to do this, then a second pass is required, that's OK too).
pyneuroml/swc/ExportNML.py
Outdated
|
||
if this_point.type == SWCNode.SOMA: | ||
self.handle_soma(this_point, parent_point, cable_id, version, new_cell) | ||
elif this_point.type != SWCNode.SOMA and parent_point.type == SWCNode.SOMA: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hrm, I'm unclear on his---the first point of a dendrite will have a soma point as parent, no?
This is tricky to understand. Maybe add a comment before the conditional explaining the three cases so that others reading the code are clear on what's happening here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thankyou for pointing this out...I will add comments to make it more clear
pyneuroml/swc/ExportNML.py
Outdated
logger.info("Creating Cell object") | ||
cell_name = self.get_cell_name() | ||
|
||
self.cell = Cell(id=cell_name) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd maybe use the component_factory
here:
from neuroml.utils import component_factory
cell = component_factory("Cell", id="cell_name", notes="something")
(the advantage of the component factory is that it validates things as we go)
pyneuroml/swc/ExportNML.py
Outdated
|
||
logger.info("Segment groups created successfully") | ||
|
||
def print_soma_segments(self) -> None: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is this for debugging?
you can use cell.get_segment_group("soma_group")
to get all segments in the segment group.
See help(neuroml.Cell)
in a python interpreter to see all the methods of the Cell
class.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes I added this for debugging...will go through the recomendation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cool, I'd also suggest writing unit tests for testing that each part of your code is doing what you expect it to do :)
I have added a unittest file and will make it more perfect with time |
import tempfile | ||
from typing import Dict, List, Optional, Set | ||
|
||
import neuroml.writers as writers |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks good, well done. I think the file should be test_ExportNML.py
, just for consistency, though.
…NeuroML into feat/ExportSWC
Hi @sanjayankur31 I have addressed the issues...Can you give it a look? |
I was running with this swc file@sanjayankur31 |
I went through the neuroml file in the soma segment 70 the proximal is set to the previous segment distal which is creating the problem. When I am manually writing the proximal to that of point id 1 which is its parent according to the swc data I am getting the exact morphology like the cvapp |
Cool, I think I know what's going on. I'll look into it soon. |
Working on this here: #421 (This will be automatically merged when that one gets merged, so leaving this open) |
This draft PR aims to add a file which exports SWC into neuroml for visualisation