-
Notifications
You must be signed in to change notification settings - Fork 52
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
MRG: Improve cells #200
MRG: Improve cells #200
Conversation
hnn_core/pyramidal.py
Outdated
geometry : dict | str | None | ||
The parameters dictionary containing geometry. | ||
If it's a string, then load the geometry from | ||
a .geo file. If None, load the default geometry |
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 was mulling over this. @rythorpe let me know your thoughts before I go too deep trying to implement it. Does this kind of API make sense? Or should it be rather a parameter of set_geometry
? Looking at these .geo files (e.g., here) it seems they only define the dendrite locations and the connections. The synapses would still have to be manually defined by the user. Note I am trying to go for something that is relatively easy to implement but also serves a practical purpose ...
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 really good actually because of the modular flexibility it will lend to pyramidal cells. I hadn't heard of .geo files before this but I'm assuming they are the standard file type for storing dendritic geometry in nrn hoc?
I'm assuming the geometry
argument will be passed down from higher-level geometry
arguments of the Network
and NetworkBuilder
classes in order to make it accessible to the user?
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 hadn't heard of .geo files before this but I'm assuming they are the standard file type for storing dendritic geometry in nrn hoc?
I think so, but even if they are not ... I think the modularization would pave the way for other loaders and should be rather straigthforward. A quick google search also revealed swc files. It should also be straightforward to implement once we have this in place.
I'm assuming the geometry argument will be passed down from higher-level geometry arguments of the Network and NetworkBuilder classes in order to make it accessible to the user?
You asked the tough question :) I don't think it would be enough to pass down a geometry
argument from Network
or NetworkBuilder
because a user would still need to define mechanisms, synapses, and how other cells connect to this cell. What might work is allowing the user to define the Pyramidal cell class/objects, passing them to Network
/NetworkBuilder
and cloning them to create a network. Wdyt?
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 might work is allowing the user to define the Pyramidal cell class/objects, passing them to Network/NetworkBuilder and cloning them to create a network. Wdyt?
That's an interesting idea, making the child cell classes (i.e., L2Pyr
, L5Pyr
, and BasketSingle
) accessible to the user. If we do that we'll need to refactor the cell object constructor and other methods so that nrn objects related to the network are only called after the user passes them into Network
/NetworkBuilder
.
hnn_core/pyramidal.py
Outdated
@@ -372,10 +402,8 @@ def topol(self): | |||
self.dends['basal_2'].connect(self.dends['basal_1'], 1, 0) | |||
self.dends['basal_3'].connect(self.dends['basal_1'], 1, 0) | |||
|
|||
def _biophys_soma(self): | |||
def _biophysics(self): |
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 makes it consistent with basket cells. Also I think it's just nicer to have all the biophysics defined in one place.
@@ -430,11 +465,11 @@ class L5Pyr(Pyr): | |||
List of dendrites. | |||
""" | |||
|
|||
def __init__(self, gid=-1, pos=-1, p={}): |
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.
defining p={}
is an anti-pattern. You can get surprising results. See here
secloc : float (0. to 1.0) | ||
The section location | ||
secloc : instance of nrn.Segment | ||
The section location. E.g., soma(0.5). |
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.
Describing nrn objects with floats and ints shows up in several docstrings. Is this just referencing the numerical argument that instantiates the object?
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 might be documented wrongly :-) fix is always welcome!
btw, soma(0.5)
just means the middle of the soma
... I guess you know that already but putting it out there in case that was not clear.
Codecov Report
@@ Coverage Diff @@
## master #200 +/- ##
==========================================
- Coverage 66.89% 66.83% -0.07%
==========================================
Files 20 20
Lines 2039 1975 -64
==========================================
- Hits 1364 1320 -44
+ Misses 675 655 -20
Continue to review full report at Codecov.
|
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.
@rythorpe @ntolley @blakecaldwell this is good to go for me. I backtracked a bit on loading arbitrary cells. It seems we would benefit more from clarifying and strengthening the object structure. Here is a summary of the changes:
- consolidate the init of L2Pyr and L5Pyr in the parent class
- remove get_sectnames method and replace with hard-coded dictionary in Cell.secs(). This has the advantage of not depending on Neuron.
- Remove list_all as an attribute of cell class. We should not introduce arbitrary attributes. They should only be introduced in the init
- Remove p_all as an attribute to avoid dependencies in the code on default shape
- Move hard coded topology into a nested list structure. All the geometry related information is going into Cell.secs() method. The data structures are not final but moving them to one place helps separate hard-coded values.
- Finally, there are a number of related yet confusingly similar attributes ... some give you dendrite names, some section names, some Neuron sections etc. We might want to carefully think and consolidate them only into ones that we need. But I leave that for another PR.
@@ -49,30 +73,36 @@ def __init__(self, gid, soma_props): | |||
self.sect_loc = dict() | |||
# for legacy use with L5Pyr | |||
self.list_dend = [] |
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.
In the spirit of cleaning up cell class attributes, is Pyr.list_dend
necessary given that we have the keys in Pyr.dends
?
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 guess we can save this for another PR as you mentioned above....
# XXX: risky to use self.soma as default. Unfortunately there isn't | ||
# a dictionary with all the sections (including 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.
Given that self.create_soma()
is called prior, why is this risky?
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.
maybe not risky right now ... but if we loaded a geo
or swc
cell from the internet and it somehow was missing a section named soma
, it could lead to a crash and a cryptic error. Unlikely but probable? I can remove the comment if you think it's more confusing than helpful
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.
Good point. Let's keep the comment for now until we can implement input validation for .geo or .swc cell files.
hnn_core/pyramidal.py
Outdated
def __biophys_soma(self): | ||
# set soma biophysics specified in Pyr | ||
# self.pyr_biophys_soma() | ||
def _biophysics(self, p_all): |
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 is nitpicky, but could we rename this to _set_biophysics()
or something along that line just to clarify that this modifies object attributes? Also, should we consider some sort of explicit or implicit method-naming convention that indicates that calls to neuron.h
are occurring within (e.g., I think we created a similar convention for NetworkBuilder.build()
)?
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.
totally, I made it set_biophysics
so it mirrored set_geometry
. I am imagining that after clean up, we will be left with one or two methods which make Neuron calls. Next, we'll need to make sure that no Neuron related attributes are instantiated when the class is instantiated. Getting closer to picklable cell class :)
Also, do we need the |
actually it seems it has no impact on the results and it unnecessarily makes more Neuron calls. So I got rid of this. Okay for you? |
@rythorpe comments addressed |
Oh I see, so the only time cell positions are set are in the cell constructors during network instantiation in |
e649944
to
93b57b3
Compare
I take back my words ... the tests were failing, not sure why. Would need to dig in. But for now, I put it back with the consolidated method you suggested. |
|
||
def move_to_pos(self): | ||
"""Move cell to position.""" | ||
self.translate_to(self.pos[0] * 100, self.pos[2], self.pos[1] * 100) |
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 don't quite get this ... why pos[2]
for y
and why not pos[2] * 100
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 is confusing. Why pos[2]
instead of pos[1]
for the y-coordinate?
Is the factor of 100
supposed to represent the actual distance between cells (i.e., scales the integer positions within the coordinate grid)?
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 honestly don't know ... I raised an issue #202. Keeping it unchanged for now. Just reorganized the code.
65efea1
to
93b57b3
Compare
Anyone else want to review or are we good to merge? |
Feel free to merge if nobody pitches in a day or two :) |
Merged! Thanks @jasmainak! |
related to #174 and #165