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] compute yscale from cell points #327
Conversation
hnn_core/cell.py
Outdated
@@ -293,18 +306,28 @@ def move_to_pos(self): | |||
# 2. a list needs to be created with a Dipole (Point Process) in each | |||
# section at position 1 | |||
# In Cell() and not Pyr() for future possibilities | |||
def insert_dipole(self, yscale): | |||
def insert_dipole(self, p_secs, dipole_sec): |
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 becoming clear that p_secs
should be an attribute of Cell
but I'm holding off on that in this PR.
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 see why p_secs
needs to be an attribute of Cell
since it isn't used anywhere outside Cell.build()
and here. Since Cell.insert_dipole()
is called after build()
, why not just access the points via the NEURON Section objects?
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'm imagining that plot_cell_morphology
will move from Network
to Cell
and then self.p_secs
will be used for the plot. I'm a little wary of relying on NEURON objects because of : 1) numerical issues we saw in the past 2) unpicklability. Since p_secs
is picklable we can make it accessible to the user to inspect/modify etc.
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.
Fair enough!
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 agree that to enable Cell.plot
we should keep HOC-objects out of ours for as long as possible.
Codecov Report
@@ Coverage Diff @@
## master #327 +/- ##
==========================================
+ Coverage 90.31% 90.33% +0.01%
==========================================
Files 13 13
Lines 2416 2421 +5
==========================================
+ Hits 2182 2187 +5
Misses 234 234
Continue to review full report at Codecov.
|
hnn_core/cell.py
Outdated
from neuron import h, nrn | ||
|
||
# Units for e: mV | ||
# Units for gbar: S/cm^2 | ||
|
||
|
||
def _get_yscale(p_secs, dipole_sec): | ||
"""Get cos theta to compute dipole in z direction.""" | ||
a = (np.array(p_secs[dipole_sec]['sec_pts'][1]) - |
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.
Here we have the hard assumption that our cells live in the xy-plane. Not much to do about it, just a note.
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.
Not really. This just projects along the apical dendrite which may or may not be oriented along the y direction (Neuron coordinates ... z direction in physical coordinates).
There are the following two alternatives which I considered and that does assume the xy-plane:
y_long = (h.y3d(1, sec=sect) - h.y3d(0, sec=sect)) * pos_all
and
y_long = (p_secs[sec_name]['sec_pts'][1][1] - p_secs['apical_trunk']['sec_pts'][0][1]) * pos_all
and get rid of yscale
altogether. That didn't work though because of numerical issues I suspect. The code uses a mix of Neuron and Python. At some point we should figure out what kind of rounding/precision Neuron actually uses. We could deliberately typecast in Python then to make things match and then remove it when we're confident it's correct
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.
Not really. This just projects along the apical dendrite which may or may not be oriented along the y direction
But isn't index 1
in p_secs[dipole_sec]['sec_pts'][1]
explicitly the y-coordinate? And 0
on the next line x. That's what I mean by "hard assumptions"---we're doing 2D projection, implicitly ignoring the z-orientation, which is assumed to be degenerate (flat).
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.
But isn't index 1 in p_secs[dipole_sec]['sec_pts'][1] explicitly the y-coordinate?
actually no, the default Neuron has only two points and this is the second point. To get the y-coordinate of the second point you would have to do:
p_secs[dipole_sec]['sec_pts'][1][1]
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.
Ah, my bad. All clear now!
hnn_core/cell.py
Outdated
@@ -293,18 +306,28 @@ def move_to_pos(self): | |||
# 2. a list needs to be created with a Dipole (Point Process) in each | |||
# section at position 1 | |||
# In Cell() and not Pyr() for future possibilities | |||
def insert_dipole(self, yscale): | |||
def insert_dipole(self, p_secs, dipole_sec): |
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 agree that to enable Cell.plot
we should keep HOC-objects out of ours for as long as possible.
hnn_core/cell.py
Outdated
The properties of syn are specified in p_syn. | ||
mech is a dict with keys as the mechanism names. The | ||
values are dictionaries with properties of the mechanism. | ||
dipole_sec : 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.
Along the lines of my other comments, could this be 'apical_section'
?
The name of a section defining the apical orientation, onto which dipole moments are projected.
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.
isn't "apical" specific to the default neuron? I like the use of term "projection" though. I'll try to incorporate it
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 my mind, "apical" refers to the single large dendrite sprouting from the top of the pyramid-formed soma (from the "apex" of the pyramid). Some L2/3 pyramidal cells have a bifurcation very close to the soma, but even then the two apical dendrites run in parallel, towards L1. The "textbook answer" is that the apical dendrite(s) is a defining characteristic of all neocortical (indeed, also hippocampal) pyramidal cells. The terms 'proximal' and 'distal' are much more ambiguous in comparison.
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.
humm ... so are you saying that you'd always want to compute the dipole along an apical dendrite? I'll change the name in that case
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. Even if we one day included a more morphologically detailed model, the dipole moment would still be calculated along the primary symmetry axis defined by the apical dendrite. You know how in MNE we have the "loose orientation constraint"? The "looseness" is fudge factor to cover for coreg and surface recon errors. The underlying strong assumption is that there's one dominant dipole orientation per source space point. And this is the normal to the cortical (WM) surface because of the apical dendrites.
hnn_core/cell.py
Outdated
sect_name = sect.name().split('_', 1)[1] | ||
y_scale = (yscale[sect_name] * sect.L) * pos_all | ||
# y_long = (h.y3d(1, sec=sect) - h.y3d(0, sec=sect)) * pos | ||
pos_proj = sect.L * cos_thetas[sect_name] * pos_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 scaling is super mysterious to me. We're doing:
diff(section length * position vector of segment)
= section length * segment lengths
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.
Yeah I was trying to make sense of that too... I think it's very ugly shorthand. The L and theta are constant, so the diff is effectively on the position, which is in units of section length ([0, 1]
). This may have something to do with calculating the dipole moment of current, but it all depends on what Dipole.mod
and Dipole_pp.mod
are actually doing... I'm digging into the details of HOC (trying to figure out my LFP-woes). I have plans to visit the "Dipole"-calculations, too, hoping this part of our code will make more sense!
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.
oh I get it now. sect.L * np.diff(pos_all)
is the fractional length (in units of um) of that segment. Multiply that by the current in the segment and you get the dipole moment of that 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.
Indeed, but that's what I mean by having to understand the MOD-files better. Because then they must not be calculating the dipole moment, right? But rather the primary current density...
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 think you're right. I'm guessing Qsum
is the the sum of all radial current entering the dendrite at a given point in a nrn section?
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.
Final nitpicks, good to merge!
hnn_core/cell.py
Outdated
"""Get cos(theta) to compute dipole along the apical dendrite.""" | ||
a = (np.array(p_secs[sec_name_apical]['sec_pts'][1]) - | ||
np.array(p_secs[sec_name_apical]['sec_pts'][0])) | ||
yscales = dict() |
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.
yscales = dict() | |
cos_theta = dict() |
hnn_core/cell.py
Outdated
yscales = dict() | ||
for sec_name, p_sec in p_secs.items(): | ||
b = np.array(p_sec['sec_pts'][1]) - np.array(p_sec['sec_pts'][0]) | ||
yscales[sec_name] = np.dot(a, b) / (norm(a) * norm(b)) |
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.
yscales[sec_name] = np.dot(a, b) / (norm(a) * norm(b)) | |
cos_theta[sec_name] = np.dot(a, b) / (norm(a) * norm(b)) |
hnn_core/cell.py
Outdated
for sec_name, p_sec in p_secs.items(): | ||
b = np.array(p_sec['sec_pts'][1]) - np.array(p_sec['sec_pts'][0]) | ||
yscales[sec_name] = np.dot(a, b) / (norm(a) * norm(b)) | ||
return yscales |
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.
return yscales | |
return cos_theta |
hnn_core/cell.py
Outdated
mech is a dict with keys as the mechanism names. The | ||
values are dictionaries with properties of the mechanism. | ||
sec_name_apical : str | ||
The name of the section along which dipole is to |
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.
The name of the section along which dipole is to | |
The name of the section along which dipole moment is calculated. |
WDYT?
Addressed! Thanks for the careful reviews :) |
Thanks for the much more readable code @jasmainak, merged after green light from @rythorpe |
closes #326
I tried not to touch too much so others can continue with their PRs