dendrotweaks 0.4.0__py3-none-any.whl → 0.4.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- dendrotweaks/__init__.py +2 -2
- dendrotweaks/biophys/groups.py +0 -5
- dendrotweaks/biophys/io/code_generators.py +6 -2
- dendrotweaks/model.py +28 -21
- dendrotweaks/morphology/__init__.py +2 -2
- dendrotweaks/morphology/io/factories.py +5 -5
- dendrotweaks/morphology/sec_trees.py +138 -163
- dendrotweaks/morphology/seg_trees.py +41 -29
- dendrotweaks/simulators.py +18 -6
- {dendrotweaks-0.4.0.dist-info → dendrotweaks-0.4.2.dist-info}/METADATA +1 -1
- {dendrotweaks-0.4.0.dist-info → dendrotweaks-0.4.2.dist-info}/RECORD +14 -14
- {dendrotweaks-0.4.0.dist-info → dendrotweaks-0.4.2.dist-info}/WHEEL +1 -1
- {dendrotweaks-0.4.0.dist-info → dendrotweaks-0.4.2.dist-info}/licenses/LICENSE +0 -0
- {dendrotweaks-0.4.0.dist-info → dendrotweaks-0.4.2.dist-info}/top_level.txt +0 -0
dendrotweaks/__init__.py
CHANGED
@@ -1,7 +1,7 @@
|
|
1
|
-
__version__ = "0.4.
|
1
|
+
__version__ = "0.4.2"
|
2
2
|
|
3
3
|
from dendrotweaks.model import Model
|
4
|
-
from dendrotweaks.simulators import
|
4
|
+
from dendrotweaks.simulators import NeuronSimulator
|
5
5
|
from dendrotweaks.biophys.distributions import Distribution
|
6
6
|
from dendrotweaks.path_manager import PathManager
|
7
7
|
from dendrotweaks.stimuli import Synapse, Population, IClamp
|
dendrotweaks/biophys/groups.py
CHANGED
@@ -1,10 +1,5 @@
|
|
1
1
|
from typing import List, Callable, Dict
|
2
2
|
|
3
|
-
from dendrotweaks.morphology.trees import Node
|
4
|
-
from dendrotweaks.morphology.sec_trees import Section
|
5
|
-
from dendrotweaks.morphology.seg_trees import Segment
|
6
|
-
from dendrotweaks.biophys.mechanisms import Mechanism
|
7
|
-
from dendrotweaks.biophys.distributions import Distribution
|
8
3
|
from dendrotweaks.utils import timeit
|
9
4
|
from dataclasses import dataclass, field, asdict
|
10
5
|
from typing import List, Tuple, Dict, Optional
|
@@ -134,7 +134,8 @@ class PythonCodeGenerator(CodeGenerator):
|
|
134
134
|
# Generate the signature
|
135
135
|
signature_str = self._generate_signature(procedure.signature,
|
136
136
|
is_method=True,
|
137
|
-
extra_params=['celsius']
|
137
|
+
extra_params=['celsius'],
|
138
|
+
default_params=True)
|
138
139
|
|
139
140
|
# Generate the body
|
140
141
|
body_str = self._generate_body(procedure.statements)
|
@@ -160,7 +161,7 @@ class PythonCodeGenerator(CodeGenerator):
|
|
160
161
|
|
161
162
|
return procedures
|
162
163
|
|
163
|
-
def _generate_signature(self, signature, is_method=True, extra_params=None):
|
164
|
+
def _generate_signature(self, signature, is_method=True, extra_params=None, default_params=False):
|
164
165
|
"""
|
165
166
|
Generate the signature string for a function using a Jinja2 template.
|
166
167
|
The function AST representation is used to retrieve the function name
|
@@ -180,6 +181,9 @@ class PythonCodeGenerator(CodeGenerator):
|
|
180
181
|
template = Template(signature_template)
|
181
182
|
name = signature['name']
|
182
183
|
params = [param['name'] for param in signature.get('params', [])]
|
184
|
+
if params == [] and default_params:
|
185
|
+
print(f"Warning: Procedure {name} has no parameters! Expected 'v' or 'cai'. Defaulting to 'v'.")
|
186
|
+
params = ['v']
|
183
187
|
if is_method:
|
184
188
|
params = ['self'] + params
|
185
189
|
|
dendrotweaks/model.py
CHANGED
@@ -6,9 +6,9 @@ import numpy as np
|
|
6
6
|
import quantities as pq
|
7
7
|
|
8
8
|
from dendrotweaks.morphology.point_trees import PointTree
|
9
|
-
from dendrotweaks.morphology.sec_trees import Section, SectionTree, Domain
|
10
|
-
from dendrotweaks.morphology.seg_trees import Segment, SegmentTree
|
11
|
-
from dendrotweaks.simulators import
|
9
|
+
from dendrotweaks.morphology.sec_trees import NeuronSection, Section, SectionTree, Domain
|
10
|
+
from dendrotweaks.morphology.seg_trees import NeuronSegment, Segment, SegmentTree
|
11
|
+
from dendrotweaks.simulators import NeuronSimulator
|
12
12
|
from dendrotweaks.biophys.groups import SegmentGroup
|
13
13
|
from dendrotweaks.biophys.mechanisms import Mechanism, LeakChannel, CaDynamics
|
14
14
|
from dendrotweaks.biophys.io import create_channel, standardize_channel, create_standard_channel
|
@@ -149,7 +149,7 @@ class Model():
|
|
149
149
|
|
150
150
|
# Simulator
|
151
151
|
if simulator_name == 'NEURON':
|
152
|
-
self.simulator =
|
152
|
+
self.simulator = NeuronSimulator()
|
153
153
|
elif simulator_name == 'Jaxley':
|
154
154
|
self.simulator = JaxleySimulator()
|
155
155
|
else:
|
@@ -231,9 +231,9 @@ class Model():
|
|
231
231
|
The dictionary mapping mechanisms to domains where they are inserted.
|
232
232
|
"""
|
233
233
|
mechs_to_domains = defaultdict(set)
|
234
|
-
for
|
235
|
-
for
|
236
|
-
mechs_to_domains[
|
234
|
+
for domain_name, mech_names in self.domains_to_mechs.items():
|
235
|
+
for mech_name in mech_names:
|
236
|
+
mechs_to_domains[mech_name].add(domain_name)
|
237
237
|
return dict(mechs_to_domains)
|
238
238
|
|
239
239
|
|
@@ -418,7 +418,7 @@ class Model():
|
|
418
418
|
"""
|
419
419
|
if self.verbose: print(f'Building sections in {self.simulator_name}...')
|
420
420
|
for sec in self.sec_tree.sections:
|
421
|
-
sec.create_and_reference(
|
421
|
+
sec.create_and_reference()
|
422
422
|
n_sec = len([sec._ref for sec in self.sec_tree.sections
|
423
423
|
if sec._ref is not None])
|
424
424
|
if self.verbose: print(f'{n_sec} sections created.')
|
@@ -439,9 +439,10 @@ class Model():
|
|
439
439
|
# TODO: Check that domains match
|
440
440
|
if not domain_name in self.domains_to_mechs:
|
441
441
|
self.domains_to_mechs[domain_name] = set()
|
442
|
-
for domain_name,
|
443
|
-
for mech_name in
|
444
|
-
self.
|
442
|
+
for domain_name, mech_names in self.domains_to_mechs.items():
|
443
|
+
for mech_name in mech_names:
|
444
|
+
mech = self.mechanisms[mech_name]
|
445
|
+
self.insert_mechanism(mech, domain_name)
|
445
446
|
|
446
447
|
|
447
448
|
def get_sections(self, filter_function):
|
@@ -493,7 +494,8 @@ class Model():
|
|
493
494
|
for sec in self.sec_tree.sections:
|
494
495
|
lambda_f = calculate_lambda_f(sec.distances, sec.diameters, sec.Ra, sec.cm, f)
|
495
496
|
nseg = int((sec.L / (d_lambda * lambda_f) + 0.9) / 2) * 2 + 1
|
496
|
-
# TODO: Set sec.
|
497
|
+
# TODO: Set sec.nseg instead
|
498
|
+
sec._nseg = nseg
|
497
499
|
sec._ref.nseg = nseg
|
498
500
|
# Rebuild the segment tree
|
499
501
|
self.seg_tree = create_segment_tree(self.sec_tree)
|
@@ -631,8 +633,8 @@ class Model():
|
|
631
633
|
|
632
634
|
# Get data to transfer
|
633
635
|
channel = self.mechanisms[channel_name]
|
634
|
-
channel_domain_names = [domain_name for domain_name,
|
635
|
-
in self.domains_to_mechs.items() if channel_name in
|
636
|
+
channel_domain_names = [domain_name for domain_name, mech_names
|
637
|
+
in self.domains_to_mechs.items() if channel_name in mech_names]
|
636
638
|
gbar_name = f'gbar_{channel_name}'
|
637
639
|
gbar_distributions = self.params[gbar_name]
|
638
640
|
# Kinetic variables cannot be transferred
|
@@ -709,13 +711,15 @@ class Model():
|
|
709
711
|
for mech_name in self.domains_to_mechs[old_domain.name]:
|
710
712
|
# TODO: What if section is already in domain? Can't be as
|
711
713
|
# we use a filtered list of sections.
|
712
|
-
|
714
|
+
mech = self.mechanisms[mech_name]
|
715
|
+
sec.uninsert_mechanism(mech)
|
713
716
|
|
714
717
|
|
715
718
|
for sec in sections_to_move:
|
716
719
|
domain.add_section(sec)
|
717
720
|
for mech_name in self.domains_to_mechs.get(domain.name, set()):
|
718
|
-
|
721
|
+
mech = self.mechanisms[mech_name]
|
722
|
+
sec.insert_mechanism(mech, distribute=distribute)
|
719
723
|
|
720
724
|
self._remove_empty()
|
721
725
|
|
@@ -797,7 +801,7 @@ class Model():
|
|
797
801
|
# domain.insert_mechanism(mech)
|
798
802
|
self.domains_to_mechs[domain_name].add(mech.name)
|
799
803
|
for sec in domain.sections:
|
800
|
-
sec.insert_mechanism(mech
|
804
|
+
sec.insert_mechanism(mech)
|
801
805
|
self._add_mechanism_params(mech)
|
802
806
|
|
803
807
|
# TODO: Redistribute parameters if any group contains this domain
|
@@ -847,7 +851,7 @@ class Model():
|
|
847
851
|
|
848
852
|
# domain.uninsert_mechanism(mech)
|
849
853
|
for sec in domain.sections:
|
850
|
-
sec.uninsert_mechanism(mech
|
854
|
+
sec.uninsert_mechanism(mech)
|
851
855
|
self.domains_to_mechs[domain_name].remove(mech.name)
|
852
856
|
|
853
857
|
if not self.mechs_to_domains.get(mech.name):
|
@@ -1402,7 +1406,8 @@ class Model():
|
|
1402
1406
|
if mech_name == 'Leak':
|
1403
1407
|
continue
|
1404
1408
|
for sec in root.subtree:
|
1405
|
-
|
1409
|
+
mech = self.mechanisms[mech_name]
|
1410
|
+
sec.uninsert_mechanism(mech)
|
1406
1411
|
|
1407
1412
|
# Disconnect
|
1408
1413
|
root.disconnect_from_parent()
|
@@ -1434,7 +1439,8 @@ class Model():
|
|
1434
1439
|
if mech_name == 'Leak':
|
1435
1440
|
continue
|
1436
1441
|
for sec in root.subtree:
|
1437
|
-
|
1442
|
+
mech = self.mechanisms[mech_name]
|
1443
|
+
sec.insert_mechanism(mech)
|
1438
1444
|
|
1439
1445
|
# Replace locs with corresponding segs
|
1440
1446
|
|
@@ -1466,7 +1472,8 @@ class Model():
|
|
1466
1472
|
|
1467
1473
|
# Reinsert active mechanisms after creating the new domain
|
1468
1474
|
for mech_name in inserted_mechs:
|
1469
|
-
|
1475
|
+
mech = self.mechanisms[mech_name]
|
1476
|
+
root.insert_mechanism(mech)
|
1470
1477
|
self.domains_to_mechs[new_reduced_domain_name] = set(inserted_mechs.keys())
|
1471
1478
|
|
1472
1479
|
|
@@ -1,5 +1,5 @@
|
|
1
1
|
from dendrotweaks.morphology.trees import Node, Tree
|
2
2
|
from dendrotweaks.morphology.point_trees import Point, PointTree
|
3
|
-
from dendrotweaks.morphology.sec_trees import Section, SectionTree, Domain
|
4
|
-
from dendrotweaks.morphology.seg_trees import Segment, SegmentTree
|
3
|
+
from dendrotweaks.morphology.sec_trees import NeuronSection, Section, SectionTree, Domain
|
4
|
+
from dendrotweaks.morphology.seg_trees import NeuronSegment, Segment, SegmentTree
|
5
5
|
from dendrotweaks.morphology.io.validation import validate_tree
|
@@ -1,7 +1,7 @@
|
|
1
1
|
from dendrotweaks.morphology.trees import Node, Tree
|
2
2
|
from dendrotweaks.morphology.point_trees import Point, PointTree
|
3
|
-
from dendrotweaks.morphology.sec_trees import Section, SectionTree
|
4
|
-
from dendrotweaks.morphology.seg_trees import Segment, SegmentTree
|
3
|
+
from dendrotweaks.morphology.sec_trees import NeuronSection, Section, SectionTree
|
4
|
+
from dendrotweaks.morphology.seg_trees import NeuronSegment, Segment, SegmentTree
|
5
5
|
|
6
6
|
from dendrotweaks.morphology.io.reader import SWCReader
|
7
7
|
|
@@ -86,7 +86,7 @@ def _split_to_sections(point_tree: PointTree) -> List[Section]:
|
|
86
86
|
|
87
87
|
# Assign a section to each bifurcation child
|
88
88
|
for i, child in enumerate(bifurcation_children):
|
89
|
-
section =
|
89
|
+
section = NeuronSection(idx=i, parent_idx=-1, points=[child])
|
90
90
|
sections.append(section)
|
91
91
|
child._section = section
|
92
92
|
# Propagate the section to the children until the next
|
@@ -181,8 +181,8 @@ def _create_segments(sec_tree) -> List[Segment]:
|
|
181
181
|
segs = {seg: idx + idx_counter for idx, seg in enumerate(sec._ref)}
|
182
182
|
sec.segments = []
|
183
183
|
for seg, idx in segs.items():
|
184
|
-
segment =
|
185
|
-
idx=idx, parent_idx=parent_idx,
|
184
|
+
segment = NeuronSegment(
|
185
|
+
idx=idx, parent_idx=parent_idx, sim_seg=seg, section=sec)
|
186
186
|
segments.append(segment)
|
187
187
|
sec.segments.append(segment)
|
188
188
|
|
@@ -54,7 +54,9 @@ class Section(Node):
|
|
54
54
|
self.points = points
|
55
55
|
self.segments = []
|
56
56
|
self._ref = None
|
57
|
+
self._nseg = None
|
57
58
|
self._domain = self.points[0].domain
|
59
|
+
self._cell = None
|
58
60
|
|
59
61
|
if not all(pt.domain == self._domain for pt in points):
|
60
62
|
raise ValueError('All points in a section must belong to the same domain.')
|
@@ -125,105 +127,6 @@ class Section(Node):
|
|
125
127
|
'parent_idx': [self.parent_idx]})
|
126
128
|
|
127
129
|
|
128
|
-
# TODO: Figure out why this is different from NEURON's diam
|
129
|
-
# Update: In NEURON, sec.diam returns the diameter of the segment at the center of the section
|
130
|
-
# In this implementation, sec.diam returns the average diameter of the section
|
131
|
-
# @property
|
132
|
-
# def diam(self):
|
133
|
-
# """
|
134
|
-
# Average diameter of the section calculated from
|
135
|
-
# the radii and distances of the points.
|
136
|
-
# """
|
137
|
-
# distances = self.distances # Cumulative distances
|
138
|
-
# radii = self.radii # Corresponding radii
|
139
|
-
# total_length = distances[-1] # Total section length
|
140
|
-
|
141
|
-
# if total_length == 0:
|
142
|
-
# return 0 # Avoid division by zero for zero-length sections
|
143
|
-
|
144
|
-
# segment_lengths = np.diff(distances) # Lengths of frusta segments
|
145
|
-
# segment_diameters = 2 * (np.array(radii[:-1]) + np.array(radii[1:])) / 2 # Mean diameter per segment
|
146
|
-
|
147
|
-
# # Length-weighted average
|
148
|
-
# avg_diameter = np.sum(segment_diameters * segment_lengths) / total_length
|
149
|
-
|
150
|
-
# return avg_diameter
|
151
|
-
|
152
|
-
@property
|
153
|
-
def diam(self):
|
154
|
-
"""
|
155
|
-
Diameter of the central segment of the section (from NEURON).
|
156
|
-
"""
|
157
|
-
return self._ref.diam
|
158
|
-
|
159
|
-
|
160
|
-
@property
|
161
|
-
def L(self):
|
162
|
-
"""
|
163
|
-
Length of the section (from NEURON).
|
164
|
-
"""
|
165
|
-
return self._ref.L
|
166
|
-
|
167
|
-
|
168
|
-
@property
|
169
|
-
def cm(self):
|
170
|
-
"""
|
171
|
-
Specific membrane capacitance of the section (from NEURON).
|
172
|
-
"""
|
173
|
-
return self._ref.cm
|
174
|
-
|
175
|
-
|
176
|
-
@property
|
177
|
-
def Ra(self):
|
178
|
-
"""
|
179
|
-
Axial resistance of the section (from NEURON).
|
180
|
-
"""
|
181
|
-
return self._ref.Ra
|
182
|
-
|
183
|
-
|
184
|
-
@property
|
185
|
-
def nseg(self):
|
186
|
-
"""
|
187
|
-
Number of segments in the section (from NEURON).
|
188
|
-
"""
|
189
|
-
return self._ref.nseg
|
190
|
-
|
191
|
-
@nseg.setter
|
192
|
-
def nseg(self, value):
|
193
|
-
if value < 1:
|
194
|
-
raise ValueError('Number of segments must be at least 1.')
|
195
|
-
if value % 2 == 0:
|
196
|
-
raise ValueError('Number of segments must be odd.')
|
197
|
-
# Set the number in NEURON
|
198
|
-
self._ref.nseg = value
|
199
|
-
# Get the new NEURON segments
|
200
|
-
nrnsegs = [seg for seg in self._ref]
|
201
|
-
|
202
|
-
# Create new DendroTweaks segments
|
203
|
-
from dendrotweaks.morphology.seg_trees import Segment
|
204
|
-
old_segments = self.segments
|
205
|
-
new_segments = [Segment(idx=0, parent_idx=0, neuron_seg=seg, section=self)
|
206
|
-
for seg in nrnsegs]
|
207
|
-
|
208
|
-
seg_tree = self._tree._seg_tree
|
209
|
-
first_segment = self.segments[0]
|
210
|
-
parent = first_segment.parent
|
211
|
-
|
212
|
-
for i, seg in enumerate(new_segments[:]):
|
213
|
-
if i == 0:
|
214
|
-
seg_tree.insert_node_before(seg, first_segment)
|
215
|
-
else:
|
216
|
-
seg_tree.insert_node_before(seg, new_segments[i-1])
|
217
|
-
|
218
|
-
for seg in old_segments:
|
219
|
-
seg_tree.remove_node(seg)
|
220
|
-
|
221
|
-
# Sort the tree
|
222
|
-
self._tree._seg_tree.sort()
|
223
|
-
# Update the section's segments
|
224
|
-
self.segments = new_segments
|
225
|
-
|
226
|
-
|
227
130
|
@property
|
228
131
|
def radii(self):
|
229
132
|
"""
|
@@ -271,8 +174,8 @@ class Section(Node):
|
|
271
174
|
"""
|
272
175
|
if self._ref is None:
|
273
176
|
raise ValueError('Section is not referenced in NEURON.')
|
274
|
-
return (np.array([(2*i - 1) / (2 * self.
|
275
|
-
for i in range(1, self.
|
177
|
+
return (np.array([(2*i - 1) / (2 * self.nseg)
|
178
|
+
for i in range(1, self.nseg + 1)]) * self.L).tolist()
|
276
179
|
|
277
180
|
|
278
181
|
@property
|
@@ -282,7 +185,7 @@ class Section(Node):
|
|
282
185
|
"""
|
283
186
|
if self._ref is None:
|
284
187
|
raise ValueError('Section is not referenced in NEURON.')
|
285
|
-
nseg = int(self.
|
188
|
+
nseg = int(self.nseg)
|
286
189
|
return [i / nseg for i in range(nseg + 1)]
|
287
190
|
|
288
191
|
|
@@ -322,80 +225,27 @@ class Section(Node):
|
|
322
225
|
areas = [np.pi * (r1 + r2) * np.sqrt((r1 - r2)**2 + h**2) for r1, r2, h in zip(self.radii[:-1], self.radii[1:], np.diff(self.distances))]
|
323
226
|
return sum(areas)
|
324
227
|
|
325
|
-
def has_mechanism(mech_name):
|
326
|
-
"""
|
327
|
-
Check if the section has a mechanism inserted.
|
328
|
-
|
329
|
-
Parameters
|
330
|
-
----------
|
331
|
-
mech_name : str
|
332
|
-
The name of the mechanism to check.
|
333
|
-
"""
|
334
|
-
return self._ref.has_membrane(mech_name)
|
335
|
-
|
336
|
-
|
337
|
-
# REFERENCING METHODS
|
338
|
-
|
339
|
-
def create_and_reference(self, simulator_name='NEURON'):
|
340
|
-
"""
|
341
|
-
Add a reference to the section in the simulator.
|
342
|
-
|
343
|
-
Parameters
|
344
|
-
----------
|
345
|
-
simulator_name : str
|
346
|
-
The name of the simulator to create the section in.
|
347
|
-
"""
|
348
|
-
if simulator_name == 'NEURON':
|
349
|
-
self.create_NEURON_section()
|
350
|
-
elif simulator_name == 'JAXLEY':
|
351
|
-
self.create_JAXLEY_section()
|
352
|
-
|
353
|
-
|
354
|
-
def create_NEURON_section(self):
|
355
|
-
"""
|
356
|
-
Create a NEURON section.
|
357
|
-
"""
|
358
|
-
self._ref = h.Section() # name=f'Sec_{self.idx}'
|
359
|
-
if self.parent is not None:
|
360
|
-
# TODO: Attaching basal to soma 0
|
361
|
-
if self.parent.parent is None: # if parent is soma
|
362
|
-
self._ref.connect(self.parent._ref(0.5))
|
363
|
-
else:
|
364
|
-
self._ref.connect(self.parent._ref(1))
|
365
|
-
# Add 3D points to the section
|
366
|
-
self._ref.pt3dclear()
|
367
|
-
for pt in self.points:
|
368
|
-
diam = 2*pt.r
|
369
|
-
diam = round(diam, 16)
|
370
|
-
self._ref.pt3dadd(pt.x, pt.y, pt.z, diam)
|
371
|
-
|
372
|
-
def create_JAXLEY_section(self):
|
373
|
-
"""
|
374
|
-
Create a JAXLEY section.
|
375
|
-
"""
|
376
|
-
raise NotImplementedError
|
377
|
-
|
378
228
|
|
379
229
|
# MECHANISM METHODS
|
380
230
|
|
381
|
-
def insert_mechanism(self,
|
231
|
+
def insert_mechanism(self, mech):
|
382
232
|
"""
|
383
233
|
Inserts a mechanism in the section if
|
384
234
|
it is not already inserted.
|
385
235
|
"""
|
386
|
-
if self._ref.has_membrane(name):
|
236
|
+
if self._ref.has_membrane(mech.name):
|
387
237
|
return
|
388
|
-
self._ref.insert(name)
|
238
|
+
self._ref.insert(mech.name)
|
389
239
|
|
390
240
|
|
391
|
-
def uninsert_mechanism(self,
|
241
|
+
def uninsert_mechanism(self, mech):
|
392
242
|
"""
|
393
243
|
Uninserts a mechanism in the section if
|
394
244
|
it was inserted.
|
395
245
|
"""
|
396
|
-
if not self._ref.has_membrane(name):
|
246
|
+
if not self._ref.has_membrane(mech.name):
|
397
247
|
return
|
398
|
-
self._ref.uninsert(name)
|
248
|
+
self._ref.uninsert(mech.name)
|
399
249
|
|
400
250
|
|
401
251
|
# PARAMETER METHODS
|
@@ -636,7 +486,7 @@ class Section(Node):
|
|
636
486
|
seg_radii = np.array([seg.diam / 2 for seg in self._ref])
|
637
487
|
|
638
488
|
# Use the specified bar width calculation from original code
|
639
|
-
bar_width = [self.
|
489
|
+
bar_width = [self.L / self._nseg] * self._nseg
|
640
490
|
|
641
491
|
# Plot segment radii as bars
|
642
492
|
ax.bar(normalized_seg_centers, seg_radii, width=bar_width,
|
@@ -666,7 +516,7 @@ class Section(Node):
|
|
666
516
|
parent_seg_radii = np.array([seg.diam / 2 for seg in self.parent._ref])
|
667
517
|
|
668
518
|
# Use the specified bar width calculation for parent
|
669
|
-
parent_bar_width = [self.parent.
|
519
|
+
parent_bar_width = [self.parent.L / self.parent._nseg] * self.parent._nseg
|
670
520
|
|
671
521
|
# Plot parent segment radii as bars
|
672
522
|
ax.bar(normalized_parent_seg_centers, parent_seg_radii,
|
@@ -738,6 +588,131 @@ class Section(Node):
|
|
738
588
|
return ax
|
739
589
|
|
740
590
|
|
591
|
+
|
592
|
+
# --------------------------------------------------------------
|
593
|
+
# NEURON SECTION
|
594
|
+
# --------------------------------------------------------------
|
595
|
+
|
596
|
+
class NeuronSection(Section):
|
597
|
+
|
598
|
+
def __init__(self, idx, parent_idx, points) -> None:
|
599
|
+
super().__init__(idx, parent_idx, points)
|
600
|
+
|
601
|
+
@property
|
602
|
+
def diam(self):
|
603
|
+
"""
|
604
|
+
Diameter of the central segment of the section (from NEURON).
|
605
|
+
"""
|
606
|
+
return self._ref.diam
|
607
|
+
|
608
|
+
|
609
|
+
@property
|
610
|
+
def L(self):
|
611
|
+
"""
|
612
|
+
Length of the section (from NEURON).
|
613
|
+
"""
|
614
|
+
return self._ref.L
|
615
|
+
|
616
|
+
|
617
|
+
@property
|
618
|
+
def cm(self):
|
619
|
+
"""
|
620
|
+
Specific membrane capacitance of the section (from NEURON).
|
621
|
+
"""
|
622
|
+
return self._ref.cm
|
623
|
+
|
624
|
+
|
625
|
+
@property
|
626
|
+
def Ra(self):
|
627
|
+
"""
|
628
|
+
Axial resistance of the section (from NEURON).
|
629
|
+
"""
|
630
|
+
return self._ref.Ra
|
631
|
+
|
632
|
+
|
633
|
+
def has_mechanism(mech_name):
|
634
|
+
"""
|
635
|
+
Check if the section has a mechanism inserted.
|
636
|
+
|
637
|
+
Parameters
|
638
|
+
----------
|
639
|
+
mech_name : str
|
640
|
+
The name of the mechanism to check.
|
641
|
+
"""
|
642
|
+
return self._ref.has_membrane(mech_name)
|
643
|
+
|
644
|
+
|
645
|
+
@property
|
646
|
+
def nseg(self):
|
647
|
+
"""
|
648
|
+
Number of segments in the section (from NEURON).
|
649
|
+
"""
|
650
|
+
return self._nseg
|
651
|
+
|
652
|
+
|
653
|
+
@nseg.setter
|
654
|
+
def nseg(self, value):
|
655
|
+
if value < 1:
|
656
|
+
raise ValueError('Number of segments must be at least 1.')
|
657
|
+
if value % 2 == 0:
|
658
|
+
raise ValueError('Number of segments must be odd.')
|
659
|
+
# Set the number in NEURON
|
660
|
+
self._nseg = self._ref.nseg = value
|
661
|
+
# Get the new NEURON segments
|
662
|
+
nrnsegs = [seg for seg in self._ref]
|
663
|
+
|
664
|
+
# Create new DendroTweaks segments
|
665
|
+
from dendrotweaks.morphology.seg_trees import NeuronSegment
|
666
|
+
old_segments = self.segments
|
667
|
+
new_segments = [NeuronSegment(idx=0, parent_idx=0, sim_seg=seg, section=self)
|
668
|
+
for seg in nrnsegs]
|
669
|
+
|
670
|
+
seg_tree = self._tree._seg_tree
|
671
|
+
first_segment = self.segments[0]
|
672
|
+
parent = first_segment.parent
|
673
|
+
|
674
|
+
for i, seg in enumerate(new_segments[:]):
|
675
|
+
if i == 0:
|
676
|
+
seg_tree.insert_node_before(seg, first_segment)
|
677
|
+
else:
|
678
|
+
seg_tree.insert_node_before(seg, new_segments[i-1])
|
679
|
+
|
680
|
+
for seg in old_segments:
|
681
|
+
seg_tree.remove_node(seg)
|
682
|
+
|
683
|
+
# Sort the tree
|
684
|
+
self._tree._seg_tree.sort()
|
685
|
+
# Update the section's segments
|
686
|
+
self.segments = new_segments
|
687
|
+
|
688
|
+
|
689
|
+
# REFERENCING METHODS
|
690
|
+
|
691
|
+
def create_and_reference(self):
|
692
|
+
"""
|
693
|
+
Create a NEURON section.
|
694
|
+
"""
|
695
|
+
self._ref = h.Section() # name=f'Sec_{self.idx}'
|
696
|
+
self._nseg = self._ref.nseg
|
697
|
+
if self.parent is not None:
|
698
|
+
# TODO: Attaching basal to soma 0
|
699
|
+
if self.parent.parent is None: # if parent is soma
|
700
|
+
self._ref.connect(self.parent._ref(0.5))
|
701
|
+
else:
|
702
|
+
self._ref.connect(self.parent._ref(1))
|
703
|
+
# Add 3D points to the section
|
704
|
+
self._ref.pt3dclear()
|
705
|
+
for pt in self.points:
|
706
|
+
diam = 2*pt.r
|
707
|
+
diam = round(diam, 16)
|
708
|
+
self._ref.pt3dadd(pt.x, pt.y, pt.z, diam)
|
709
|
+
|
710
|
+
|
711
|
+
|
712
|
+
# ========================================================================
|
713
|
+
# SECTION TREE
|
714
|
+
# ========================================================================
|
715
|
+
|
741
716
|
class SectionTree(Tree):
|
742
717
|
"""
|
743
718
|
A class representing a tree graph of sections in a neuron morphology.
|
@@ -14,8 +14,8 @@ class Segment(Node):
|
|
14
14
|
The index of the segment.
|
15
15
|
parent_idx : int
|
16
16
|
The index of the parent segment.
|
17
|
-
|
18
|
-
The
|
17
|
+
sim_seg : h.Segment
|
18
|
+
The segment object from a simulator (e.g. NEURON).
|
19
19
|
section : Section
|
20
20
|
The section to which the segment belongs.
|
21
21
|
|
@@ -24,13 +24,13 @@ class Segment(Node):
|
|
24
24
|
_section : Section
|
25
25
|
The section to which the segment belongs.
|
26
26
|
_ref : h.Segment
|
27
|
-
The
|
27
|
+
The segment object from a simulator (e.g. NEURON).
|
28
28
|
"""
|
29
29
|
|
30
|
-
def __init__(self, idx, parent_idx,
|
30
|
+
def __init__(self, idx, parent_idx, sim_seg, section) -> None:
|
31
31
|
super().__init__(idx, parent_idx)
|
32
32
|
self._section = section
|
33
|
-
self._ref =
|
33
|
+
self._ref = sim_seg
|
34
34
|
|
35
35
|
|
36
36
|
# PROPERTIES
|
@@ -43,30 +43,6 @@ class Segment(Node):
|
|
43
43
|
return self._section.domain
|
44
44
|
|
45
45
|
|
46
|
-
@property
|
47
|
-
def x(self):
|
48
|
-
"""
|
49
|
-
The position of the segment along the normalized section length (from NEURON).
|
50
|
-
"""
|
51
|
-
return self._ref.x
|
52
|
-
|
53
|
-
|
54
|
-
@property
|
55
|
-
def area(self):
|
56
|
-
"""
|
57
|
-
The area of the segment (from NEURON).
|
58
|
-
"""
|
59
|
-
return self._ref.area()
|
60
|
-
|
61
|
-
|
62
|
-
@property
|
63
|
-
def diam(self):
|
64
|
-
"""
|
65
|
-
The diameter of the segment (from NEURON).
|
66
|
-
"""
|
67
|
-
return self._ref.diam
|
68
|
-
|
69
|
-
|
70
46
|
@property
|
71
47
|
def subtree_size(self):
|
72
48
|
"""
|
@@ -137,6 +113,42 @@ class Segment(Node):
|
|
137
113
|
return np.nan
|
138
114
|
|
139
115
|
|
116
|
+
|
117
|
+
# -------------------------------------------------------------------
|
118
|
+
# NEURON SEGMENT
|
119
|
+
# -------------------------------------------------------------------
|
120
|
+
|
121
|
+
class NeuronSegment(Segment):
|
122
|
+
"""
|
123
|
+
A class representing a segment for the Jaxley simulator.
|
124
|
+
"""
|
125
|
+
|
126
|
+
def __init__(self, idx, parent_idx, sim_seg, section) -> None:
|
127
|
+
super().__init__(idx, parent_idx, sim_seg, section)
|
128
|
+
|
129
|
+
@property
|
130
|
+
def x(self):
|
131
|
+
"""
|
132
|
+
The position of the segment along the normalized section length (from NEURON).
|
133
|
+
"""
|
134
|
+
return self._ref.x
|
135
|
+
|
136
|
+
|
137
|
+
@property
|
138
|
+
def area(self):
|
139
|
+
"""
|
140
|
+
The area of the segment (from NEURON).
|
141
|
+
"""
|
142
|
+
return self._ref.area()
|
143
|
+
|
144
|
+
|
145
|
+
@property
|
146
|
+
def diam(self):
|
147
|
+
"""
|
148
|
+
The diameter of the segment (from NEURON).
|
149
|
+
"""
|
150
|
+
return self._ref.diam
|
151
|
+
|
140
152
|
class SegmentTree(Tree):
|
141
153
|
"""
|
142
154
|
A class representing a tree graph of segments.
|
dendrotweaks/simulators.py
CHANGED
@@ -10,6 +10,7 @@ h.load_file('stdrun.hoc')
|
|
10
10
|
# h.load_file('import3d.hoc')
|
11
11
|
# h.load_file('nrngui.hoc')
|
12
12
|
# h.load_file('import3d')
|
13
|
+
import numpy as np
|
13
14
|
|
14
15
|
import contextlib
|
15
16
|
|
@@ -31,6 +32,10 @@ def reset_neuron():
|
|
31
32
|
|
32
33
|
reset_neuron()
|
33
34
|
|
35
|
+
# -------------------------------------------------------
|
36
|
+
# SIMULATOR
|
37
|
+
# -------------------------------------------------------
|
38
|
+
|
34
39
|
class Simulator:
|
35
40
|
"""
|
36
41
|
A generic simulator class.
|
@@ -79,7 +84,7 @@ class Simulator:
|
|
79
84
|
|
80
85
|
|
81
86
|
|
82
|
-
class
|
87
|
+
class NeuronSimulator(Simulator):
|
83
88
|
"""
|
84
89
|
A class to represent the NEURON simulator.
|
85
90
|
|
@@ -193,18 +198,25 @@ class NEURONSimulator(Simulator):
|
|
193
198
|
|
194
199
|
|
195
200
|
def _init_simulation(self):
|
196
|
-
|
201
|
+
|
197
202
|
h.celsius = self.temperature
|
198
|
-
|
199
|
-
|
200
|
-
|
203
|
+
|
204
|
+
if self._cvode:
|
205
|
+
h.cvode.active(1)
|
206
|
+
else:
|
207
|
+
h.cvode.active(0)
|
208
|
+
h.dt = self.dt
|
209
|
+
|
201
210
|
h.finitialize(self.v_init)
|
202
|
-
|
211
|
+
|
212
|
+
if self._cvode:
|
203
213
|
h.cvode.re_init()
|
204
214
|
else:
|
205
215
|
h.fcurrent()
|
216
|
+
|
206
217
|
h.frecord_init()
|
207
218
|
|
219
|
+
|
208
220
|
def run(self, duration=300):
|
209
221
|
"""
|
210
222
|
Run a simulation.
|
@@ -1,15 +1,15 @@
|
|
1
|
-
dendrotweaks/__init__.py,sha256=
|
2
|
-
dendrotweaks/model.py,sha256=
|
1
|
+
dendrotweaks/__init__.py,sha256=9L85_UrXGRtxCoSzemZUlzdq_FWZPX0Hjz7HUtyqepg,384
|
2
|
+
dendrotweaks/model.py,sha256=rMvS0BnqQ4ozIbM65t1jgydm5IeFo1QH0FAXEQNM-bs,69141
|
3
3
|
dendrotweaks/model_io.py,sha256=xwXKMcUle-Y0HoWFYVZu3G8v4pdQXmeaDfl2Xi65eHw,2137
|
4
4
|
dendrotweaks/path_manager.py,sha256=dai5o6UA0nk-ubwKWRu4LFdDBO77zW_SsMf6k0MLBiI,8703
|
5
|
-
dendrotweaks/simulators.py,sha256=
|
5
|
+
dendrotweaks/simulators.py,sha256=Vg6ToTkDZ4OGcANYwYtamGnasyLNbyP8pfPHl9PnMnM,7413
|
6
6
|
dendrotweaks/utils.py,sha256=jaUJNb39Bsevg3WJByP56bO7CLj1wzlh-uGZl-lxi1I,7131
|
7
7
|
dendrotweaks/analysis/__init__.py,sha256=SEYpoQ5iXiQXyHB20-IAdDHYI-7CR5GYFXIwr-O05Ug,858
|
8
8
|
dendrotweaks/analysis/ephys_analysis.py,sha256=Caiww27p9dnL5_27OmZ95AZ_OmefvImAPyItsJMSdmA,14320
|
9
9
|
dendrotweaks/analysis/morphometric_analysis.py,sha256=5zohjGssyx-wezI-yY3Q-kYM_wzAQLLFBJ9Xk950_JY,3571
|
10
10
|
dendrotweaks/biophys/__init__.py,sha256=k0o2xwyoaJUb1lfO9OHtqxheNP6R-Ya5o0g-bJOdCZg,360
|
11
11
|
dendrotweaks/biophys/distributions.py,sha256=ADPFPA-CN7AbRJj0Ry4TxFZJhdYXJm87iIGWZSDr5vI,10299
|
12
|
-
dendrotweaks/biophys/groups.py,sha256=
|
12
|
+
dendrotweaks/biophys/groups.py,sha256=Q4kBIqL1-piIgrpsVq6ojinAWHiEJ1GzMjSAQ7Ni_E8,3212
|
13
13
|
dendrotweaks/biophys/mechanisms.py,sha256=j9uygcwkK6Z_08hpTHax40Wn-eV4V_k_on_KyPDnO90,18520
|
14
14
|
dendrotweaks/biophys/default_mod/AMPA.mod,sha256=HY_pWzYvaSDV-w7qruenG2mnll8v79s40HFHjUCIi4U,980
|
15
15
|
dendrotweaks/biophys/default_mod/AMPA_NMDA.mod,sha256=ztv2ePUiEQZ93-23FTkGO2DC91rehQuqo0NUIbHZ368,2318
|
@@ -25,21 +25,21 @@ dendrotweaks/biophys/default_templates/template_jaxley.py,sha256=t-GsCSUyQ7rDoaL
|
|
25
25
|
dendrotweaks/biophys/default_templates/template_jaxley_new.py,sha256=I62KhnOYNV1bT-nPsDTxjIISYmDcso2X8rnsos28nYs,3631
|
26
26
|
dendrotweaks/biophys/io/__init__.py,sha256=kkmQ4L0SatI3lWd3qE8KqOIKd7x3G2OnqAAW93sWWCU,575
|
27
27
|
dendrotweaks/biophys/io/ast.py,sha256=7x_Kxz1qoQHZeIjovUNyVuKgUo4vAFKm-bd4hn9n1CI,6078
|
28
|
-
dendrotweaks/biophys/io/code_generators.py,sha256=
|
28
|
+
dendrotweaks/biophys/io/code_generators.py,sha256=RX0nw5-0CyWR3KOrTZUabKuPAQg2ysQ_nQi2iu9TxiE,11978
|
29
29
|
dendrotweaks/biophys/io/converter.py,sha256=5yrPJhyZbuwV7tTGoacnNOvmRdVgXPIyGfiR0PyOVzg,3371
|
30
30
|
dendrotweaks/biophys/io/factories.py,sha256=j1Hi2u-NTFFL8ElRYlgGVNHRcfKWH6o5GfKvraMTlwM,5020
|
31
31
|
dendrotweaks/biophys/io/grammar.py,sha256=TJLTDlr8Ajp3J9DJ4IvulOCcpUkYr7HnoI0TGnNuEPc,11677
|
32
32
|
dendrotweaks/biophys/io/loader.py,sha256=Wv9ZkEDyA3MkCdV0sMeRnBffg2WAI7yTV3r6C412GiY,6378
|
33
33
|
dendrotweaks/biophys/io/parser.py,sha256=boT27lFrn5LYrJnkZFs0SwrZZrkSkwO8efqGPJ4Qj0I,17914
|
34
34
|
dendrotweaks/biophys/io/reader.py,sha256=JWm5WM9illvSfDkhWEmWBcj8Y7PSi8zeZX9j1ARUHVU,6576
|
35
|
-
dendrotweaks/morphology/__init__.py,sha256=
|
35
|
+
dendrotweaks/morphology/__init__.py,sha256=JwXmSmdn9e_jqslITEdiU9kWvzxcxT9Aw_kUkXLbm5o,353
|
36
36
|
dendrotweaks/morphology/domains.py,sha256=Y4txcGdBdl2aK1DfbTRziNtDyd6bChczwpCWE7lTFzg,2391
|
37
37
|
dendrotweaks/morphology/point_trees.py,sha256=5dUPaQXYPdJbWoD3pFI2DV2XnuFRhB5d0wTBlfmmIeI,21600
|
38
|
-
dendrotweaks/morphology/sec_trees.py,sha256=
|
39
|
-
dendrotweaks/morphology/seg_trees.py,sha256
|
38
|
+
dendrotweaks/morphology/sec_trees.py,sha256=fj-1kBj7InkqB27Ux-jPidGQXts7FAPXa1m2LdzekNY,35816
|
39
|
+
dendrotweaks/morphology/seg_trees.py,sha256=-XeSJuD7ZixBJYQDzvmSEiNvOWbVmX_DanyAPkkR-NA,4042
|
40
40
|
dendrotweaks/morphology/trees.py,sha256=NrNvPMR-U0clt63eqwVJqU0H8NJgY53QGA_BkdcwkQI,16033
|
41
41
|
dendrotweaks/morphology/io/__init__.py,sha256=gAZqZdf5VKPb6ksK8Lwt7MbTAq8TDP8uq3Vs_ebNFEY,324
|
42
|
-
dendrotweaks/morphology/io/factories.py,sha256=
|
42
|
+
dendrotweaks/morphology/io/factories.py,sha256=DCE37QCloiYVro5HGihJbxPz91BB3y5NNf-oRaQ-M2g,6383
|
43
43
|
dendrotweaks/morphology/io/reader.py,sha256=hW3c541WtG1rNag_YreEhvrLzm8-OTtw0fQREeHDthM,1913
|
44
44
|
dendrotweaks/morphology/io/validation.py,sha256=lVkYw9y9yG5QpRh_N0YQ3FbZwuSUsQfSqJTMumMcDdc,7872
|
45
45
|
dendrotweaks/morphology/reduce/__init__.py,sha256=p6Mg3KDHxTt8S4DtI0m7L7MqV6dS2pdIYAwB7B-toVw,921
|
@@ -49,8 +49,8 @@ dendrotweaks/stimuli/__init__.py,sha256=bFfSEZhCVpwOVEBgLe65iiY3SdpjKPhyLemC1z5O
|
|
49
49
|
dendrotweaks/stimuli/iclamps.py,sha256=NjkhhwZKJR1f_g3N9BVxMVoO9ubBk5WkQ6h9Bnf9xgA,1681
|
50
50
|
dendrotweaks/stimuli/populations.py,sha256=y85v8smiMifINIqXm1O3mOINAlDTz-SPGLS78alhX5A,8325
|
51
51
|
dendrotweaks/stimuli/synapses.py,sha256=g4MgWTske2TZ2i9FIIOE8-KXNx_3dWa3zEhB2rcqYig,5470
|
52
|
-
dendrotweaks-0.4.
|
53
|
-
dendrotweaks-0.4.
|
54
|
-
dendrotweaks-0.4.
|
55
|
-
dendrotweaks-0.4.
|
56
|
-
dendrotweaks-0.4.
|
52
|
+
dendrotweaks-0.4.2.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
53
|
+
dendrotweaks-0.4.2.dist-info/METADATA,sha256=RHDsvzirnDTlrF2wl4IqLgSLw-40UOJ8_VfNtoezyYw,2740
|
54
|
+
dendrotweaks-0.4.2.dist-info/WHEEL,sha256=Nw36Djuh_5VDukK0H78QzOX-_FQEo6V37m3nkm96gtU,91
|
55
|
+
dendrotweaks-0.4.2.dist-info/top_level.txt,sha256=OzT_2BSI5j5zxC447K6Y-0W-GHbued7iX-_hFGAKMxY,13
|
56
|
+
dendrotweaks-0.4.2.dist-info/RECORD,,
|
File without changes
|
File without changes
|