-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathmorphologyFunctions.py
320 lines (289 loc) · 14.8 KB
/
morphologyFunctions.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
import numpy as np
from neuron import h
from . import neuronFunctions as nfx
def findSites(soma, dist, method='hoc', dends=None, incDiam=False):
"""
Take top level section (usually soma) and find all sections connected to it at a certain dist from soma.
If method = 'hoc', uses soma.subtree(), and finds all hoc sections connected to soma
If method = 'struct', requires 4th input dends, which is a list of connections to soma (that must be in soma.subtree())
- just look for sections in dends that are connected at a certain distance
Return:
list of section names
segment value that's the requested distance (i.e. if section is 90-110µm and requested distance is 100µm, return 0.5)
actual distance (this is based off of how many segments, will be within smallest dx of segment, set elsewhere)
"""
# Get subtree
if method=='hoc':
# Use generic hoc method
tree = soma.subtree()
elif method=='struct':
# Use structure method (requires 4th input)
tree = dends
else:
print('Did not recognize method')
return None
# Find sections with requested distance from soma, then return segment number for specific distance
N = len(tree)
isDistance = np.zeros(N,dtype='bool')
nSegment = np.zeros(N)
prevDistance = np.zeros(N) # length of dendrite from previous branch point
postDistance = np.zeros(N) # length of dendrite after site requested
distFromBranch = np.zeros(N) # distance of site from previous branch point
diamAtBranch = np.zeros(N) # diameter at previous branch point
for n in range(N):
proxDist = h.distance(tree[n](0),soma(1))
L = tree[n].L
if (proxDist < dist) & (proxDist+L >= dist):
isDistance[n]=True # record that this one is a valid distance from the soma
nSegment[n] = (dist-proxDist)/L # record segment at requested distance
distFromBranch[n] = h.distance(tree[n](nSegment[n]),tree[n](0)) # distance from previous branch point
diamAtBranch[n] = tree[n].diam
# Measure total dendritic length after site
currentTree = tree[n].subtree()
for ct in currentTree:
if ct == tree[n]:
diamAdjustment = 1
if incDiam: diamAdjustment = np.pi*ct.diam
postDistance[n] += diamAdjustment * (L - h.distance(tree[n](nSegment[n]),tree[n](0))) # add only the distance after the requested site
else:
diamAdjustment = 1
if incDiam: diamAdjustment = np.pi*ct.diam
postDistance[n] += ct.L * diamAdjustment # add all children distance
# Measure total dendritic length after previous branch point (only works rn because the parents are always parents of sisters)
parentTree = tree[n].parentseg().sec.subtree()
for ct in parentTree:
if ct!=tree[n].parentseg().sec: # don't include the actual parent section (which is included in the subtree)
diamAdjustment = 1
if incDiam: diamAdjustment = np.pi*ct.diam
prevDistance[n] += ct.L * diamAdjustment
outSection = [tree[sec] for sec in np.where(isDistance)[0]]
outSegment = nSegment[isDistance]
outPost = postDistance[isDistance]
outPre = prevDistance[isDistance]
outDistBranch = distFromBranch[isDistance]
outDiam = diamAtBranch[isDistance]
return outSection, outSegment, outPost, outPre, outDistBranch, outDiam
def measurePrePostDistance(section,segment):
N = len(section)
prevDistance = np.zeros(N)
postDistance = np.zeros(N)
for n in range(N):
cSection = section[n]
cSegment = segment[n]
cTree = cSection.subtree()
# Measure dendritic length after site
for ct in cTree:
postDistance[n] += ct.L
if ct == cSection:
postDistance[n] -= (ct.L - h.distance(ct(cSegment),ct(0))) # discount everything proximal to ROI
# Measure dendritic length after previous branch point
parentTree = cSection.parentseg().sec.subtree()
for ct in parentTree:
if ct!=cSection.parentseg().sec: # don't include parent section (which is included in the subtree)
prevDistance[n] += ct.L
return postDistance, prevDistance
def recordSites(section,segment,recordVariable='_ref_v'):
"""
Takes a list of section names and segments within each section, and sets up recording vectors for each
Section & segment must be registered with one another...
recordVariable determines what to measure from each, (default is membrane voltage)
"""
tv = h.Vector() # Time stamp vector
tv.record(h._ref_t)
vsection = [] # list of hoc vectors for each section
for sec,seg in zip(section, segment):
vsection.append(h.Vector())
vsection[-1].record(getattr(sec(seg),recordVariable)) # record voltage... eventually make this a dynamic attribute name
return vsection,tv
def injectSites(section,segment,stim=None,amplitude=-0.1):
# Always record time stamps
tv = h.Vector()
tv.record(h._ref_t)
# Inject and record voltage in each segment
N = len(section)
i = 0
vrecord = h.Vector()
vsection = []
for sec,seg in zip(section,segment):
i += 1
#print('Working on section {0}, {1}/{2}'.format(sec,i,N))
vrecord.record(sec(seg)._ref_v)
stim = nfx.attachCC(section=sec, delay=50, dur=50, amp=amplitude, loc=seg)
nfx.simulate(tstop=101,v_init=-76,celsius=37)
vsection.append(np.array(vrecord))
return vsection,tv,stim
def injectAlphaSites(section,segment,syn=None,onset=5,tau=2,gmax=0.1,tstop=25):
# Always record time stamps
tv = h.Vector()
tv.record(h._ref_t)
# Inject and record voltage in each segment
N = len(section)
i = 0
vrecord = h.Vector()
vsomaRec = h.Vector()
vsection = []
vsoma = []
for sec,seg in zip(section,segment):
i += 1
#print('Working on section {0}, {1}/{2}'.format(sec,i,N))
vrecord.record(sec(seg)._ref_v)
vsomaRec.record(h.soma(0.5)._ref_v)
syn = nfx.attachAlpha(section=sec, seg=seg, onset=onset, tau=tau, gmax=gmax)
nfx.simulate(tstop=tstop,v_init=-76,celsius=35)
vsection.append(np.array(vrecord))
vsoma.append(np.array(vsomaRec))
return vsection,vsoma,tv,syn
def recordBranchPointDivision(section):
tv = h.Vector()
tv.record(h._ref_t)
targetBranch = []
sisterBranch = []
for sec in section:
targetBranch.append([h.Vector(), h.Vector(), 0.0])
sisterBranch.append([h.Vector(), h.Vector(), 0.0])
parentRef = h.SectionRef(sec=sec)
# Record at first and second segment of target branch immediately after previous branch point
targetBranch[-1][0].record(sec(nfx.returnSegment(sec.nseg,1))._ref_v)
targetBranch[-1][1].record(sec(nfx.returnSegment(sec.nseg,2))._ref_v)
# Resistance!
targetAxialResistance = 4*sec.Ra*1e4 / (np.pi * sec.diam**2)
targetLength = sec.L/sec.nseg
targetBranch[-1][2] = 1e-6 * targetAxialResistance * targetLength
# Record at 1st/2nd segment of sister branch
sref = h.SectionRef(sec=sec.parentseg().sec)
if sref.nchild()!=2:
print('The parent of {0} had more than 2 children! Exiting prematurely.'.format(sec))
return
childIdx = 0
if sref.child[childIdx]==sec:
childIdx=1
sisterSection = sref.child[childIdx]
sisterBranch[-1][0].record(sisterSection(nfx.returnSegment(sisterSection.nseg,1))._ref_v)
sisterBranch[-1][1].record(sisterSection(nfx.returnSegment(sisterSection.nseg,2))._ref_v)
# Resistance!
sisterAxialResistance = 4*sisterSection.Ra*1e4 / (np.pi * sisterSection.diam**2)
sisterLength = sisterSection.L / sisterSection.nseg
sisterBranch[-1][2] = 1e-6 * sisterAxialResistance * sisterLength
return tv,targetBranch,sisterBranch
def measureConvolvedBranching(section, segment, lengthConstant):
convolvedLength = []
for sec, seg in zip(section, segment):
thisSecOffsets = [] # keep track of distance to branch points
thisSecLengths = [] # keep track of distance after branch points
# Start by measuring distance after ROI itself
thisSecOffsets.append(0.0) # no distance between ROI and itself
currentTree = sec.subtree()
currentPost = 0# -h.distance(sec(seg),sec(0)) # start by subtracting distance from previous branch point to ROI (which will be included and offset in following loop)
for ct in currentTree:
currentPost += ct.L
if ct==sec: currentPost -= h.distance(sec(seg),sec(0))
thisSecLengths.append(currentPost)
# Next, measure distance after each previous branch point (including soma)
currentSec = sec
while True:
dist2branch = h.distance(sec(seg),currentSec(0)) # measure distance from ROI
thisSecOffsets.append(dist2branch)
currentTree = currentSec.subtree()
currentPost = 0.0
for ct in currentTree:
currentPost += ct.L
thisSecLengths.append(currentPost)
currentSec = currentSec.parentseg().sec
if currentSec.parentseg() is None: break
# Now, compute weighted average using exponential decay as convolutional filter
expPoints = np.exp(-(np.array(thisSecOffsets))/lengthConstant)
convolvedLength.append(np.dot(thisSecLengths,expPoints)/np.sum(expPoints))
return convolvedLength
def measureLocalBranching(section,segment,soma,lengthConstant):
localBranching = []
somaTree = soma.subtree()
for sec,seg in zip(section,segment):
currentDistance = 0.0
for ct in somaTree:
if ct==sec:
distalLength = h.distance(sec(seg),sec(1))
multFactor = lengthConstant/distalLength * (np.exp(-0/lengthConstant) - np.exp(-distalLength/lengthConstant))
currentDistance += distalLength * multFactor
proxLength = h.distance(sec(seg),sec(0))
multFactor = lengthConstant/proxLength * (np.exp(-0/lengthConstant) - np.exp(-proxLength/lengthConstant))
currentDistance += proxLength * multFactor
elif ct!=soma:
idx1 = h.distance(sec(seg),ct(1))
idx2 = h.distance(sec(seg),ct(0))
further = np.max([idx1,idx2])
closer = np.min([idx1,idx2])
cLength = further - closer
multFactor = lengthConstant/(further - closer) * (np.exp(-closer/lengthConstant) - np.exp(-further/lengthConstant))
currentDistance += cLength * multFactor
else:
None
# Don't add soma...
localBranching.append(currentDistance)
return localBranching
def measureDiscountedMorphRatio(section,lengthConstant,method='exponential'):
discountedLength = []
for sec in section:
discountedLength.append([0.0,0.0])
parentSec = sec.parentseg().sec
parentSecRef = h.SectionRef(sec=parentSec) # Get parent section reference
if parentSecRef.nchild()!=2:
print('The parent of {0} had more than 2 children! Exiting prematurely.'.format(sec))
return
childIdx = 0 # Try idx 0
if parentSecRef.child[childIdx]==sec:
childIdx=1 # Set idx to sister branch
sisSec = parentSecRef.child[childIdx]
# Measure total dendritic length, discounted exponentially
targetTree = sec.subtree()
for tt in targetTree:
idx1 = h.distance(parentSec(1),tt(0))
idx2 = h.distance(parentSec(1),tt(1))
if method=='exponential':
# mult factor is integral of exponential between start and end distance of the current section
multFactor = lengthConstant/(idx2-idx1)*(np.exp(-idx1/lengthConstant) - np.exp(-idx2/lengthConstant)) # average of exponential evaluated between idx1 & idx2
elif method=='linear':
multFactor = 1/(idx2-idx1)*1/(2*lengthConstant) * (idx2**2 - idx1**2)
elif method=='sigmoid':
if len(lengthConstant)!=3:
print('For sigmoid, must provide 3 terms, exiting now.')
return
mainTerm = lambda x: lengthConstant[0] / (lengthConstant[0] + np.exp(-lengthConstant[1]*x - lengthConstant[2]))
multFactor = 1/(idx2-idx1) * (1/lengthConstant[1]) * (np.log(mainTerm(idx2)) - np.log(mainTerm(idx1)))
elif method=='order':
if len(lengthConstant)!=3:
print('For order, must provide 3 terms, exiting now.')
return
order = 0
currSec = tt
while currSec!=sec:
order += 1
currSec = currSec.parentseg().sec
multFactor = 1 - 1/(1+lengthConstant[0]*np.exp(lengthConstant[1]*order+lengthConstant[2]))
discountedLength[-1][0] += tt.L * multFactor
sisterTree = sisSec.subtree()
for st in sisterTree:
idx1 = h.distance(parentSec(1),st(0))
idx2 = h.distance(parentSec(1),st(1))
if method=='exponential':
# mult factor is integral of exponential between start and end distance of the current section
multFactor = lengthConstant/(idx2-idx1)*(np.exp(-idx1/lengthConstant) - np.exp(-idx2/lengthConstant)) # average of exponential evaluated between idx1 & idx2
elif method=='linear':
multFactor = 1/(idx2-idx1)*1/(2*lengthConstant) * (idx2**2 - idx1**2)
elif method=='sigmoid':
if len(lengthConstant)!=3:
print('For sigmoid, must provide 3 terms, exiting now.')
return
mainTerm = lambda x: lengthConstant[0] / (lengthConstant[0] + np.exp(-lengthConstant[1]*x - lengthConstant[2]))
multFactor = 1/(idx2-idx1) * (1/lengthConstant[1]) * (np.log(mainTerm(idx2)) - np.log(mainTerm(idx1)))
elif method=='order':
if len(lengthConstant)!=3:
print('For order, must provide 3 terms, exiting now.')
return
order = 0
currSec = st
while currSec!=sisSec:
order += 1
currSec = currSec.parentseg().sec
multFactor = 1 - 1/(1+lengthConstant[0]*np.exp(lengthConstant[1]*order+lengthConstant[2]))
discountedLength[-1][1] += st.L * multFactor
return discountedLength