dendrotweaks 0.3.1__py3-none-any.whl → 0.4.1__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.
Files changed (44) hide show
  1. dendrotweaks/__init__.py +3 -3
  2. dendrotweaks/analysis/ephys_analysis.py +12 -8
  3. dendrotweaks/biophys/__init__.py +7 -0
  4. dendrotweaks/{membrane → biophys}/default_templates/NEURON_template.py +5 -3
  5. dendrotweaks/{membrane → biophys}/default_templates/default.py +2 -1
  6. dendrotweaks/{membrane → biophys}/default_templates/standard_channel.mod +5 -1
  7. dendrotweaks/{membrane → biophys}/groups.py +0 -5
  8. dendrotweaks/biophys/io/__init__.py +11 -0
  9. dendrotweaks/{membrane → biophys}/io/ast.py +6 -0
  10. dendrotweaks/{membrane → biophys}/io/code_generators.py +13 -3
  11. dendrotweaks/{membrane → biophys}/io/converter.py +3 -3
  12. dendrotweaks/{membrane → biophys}/io/factories.py +8 -6
  13. dendrotweaks/biophys/io/loader.py +190 -0
  14. dendrotweaks/{membrane → biophys}/io/parser.py +8 -8
  15. dendrotweaks/{membrane → biophys}/mechanisms.py +14 -10
  16. dendrotweaks/model.py +91 -52
  17. dendrotweaks/morphology/__init__.py +2 -2
  18. dendrotweaks/morphology/io/factories.py +5 -5
  19. dendrotweaks/morphology/sec_trees.py +139 -164
  20. dendrotweaks/morphology/seg_trees.py +41 -29
  21. dendrotweaks/path_manager.py +9 -11
  22. dendrotweaks/simulators.py +100 -54
  23. dendrotweaks/stimuli/populations.py +11 -0
  24. {dendrotweaks-0.3.1.dist-info → dendrotweaks-0.4.1.dist-info}/METADATA +2 -2
  25. dendrotweaks-0.4.1.dist-info/RECORD +56 -0
  26. {dendrotweaks-0.3.1.dist-info → dendrotweaks-0.4.1.dist-info}/WHEEL +1 -1
  27. dendrotweaks/membrane/__init__.py +0 -6
  28. dendrotweaks/membrane/io/__init__.py +0 -11
  29. dendrotweaks/membrane/io/loader.py +0 -90
  30. dendrotweaks-0.3.1.dist-info/RECORD +0 -56
  31. /dendrotweaks/{membrane → biophys}/default_mod/AMPA.mod +0 -0
  32. /dendrotweaks/{membrane → biophys}/default_mod/AMPA_NMDA.mod +0 -0
  33. /dendrotweaks/{membrane → biophys}/default_mod/CaDyn.mod +0 -0
  34. /dendrotweaks/{membrane → biophys}/default_mod/GABAa.mod +0 -0
  35. /dendrotweaks/{membrane → biophys}/default_mod/Leak.mod +0 -0
  36. /dendrotweaks/{membrane → biophys}/default_mod/NMDA.mod +0 -0
  37. /dendrotweaks/{membrane → biophys}/default_mod/vecstim.mod +0 -0
  38. /dendrotweaks/{membrane → biophys}/default_templates/template_jaxley.py +0 -0
  39. /dendrotweaks/{membrane → biophys}/default_templates/template_jaxley_new.py +0 -0
  40. /dendrotweaks/{membrane → biophys}/distributions.py +0 -0
  41. /dendrotweaks/{membrane → biophys}/io/grammar.py +0 -0
  42. /dendrotweaks/{membrane → biophys}/io/reader.py +0 -0
  43. {dendrotweaks-0.3.1.dist-info → dendrotweaks-0.4.1.dist-info}/licenses/LICENSE +0 -0
  44. {dendrotweaks-0.3.1.dist-info → dendrotweaks-0.4.1.dist-info}/top_level.txt +0 -0
@@ -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._ref.nseg)
275
- for i in range(1, self._ref.nseg + 1)]) * self._ref.L).tolist()
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._ref.nseg)
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, name: str):
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, name: str):
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._ref.L / self._ref.nseg] * self._ref.nseg
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._ref.L / self.parent._ref.nseg] * self.parent._ref.nseg
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.
@@ -770,7 +745,7 @@ class SectionTree(Tree):
770
745
  """
771
746
 
772
747
  unique_domain_names = set([sec.domain for sec in self.sections])
773
- self.domains = {name: Domain(name) for name in unique_domain_names}
748
+ self.domains = {name: Domain(name) for name in sorted(unique_domain_names)}
774
749
 
775
750
  for sec in self.sections:
776
751
  self.domains[sec.domain].add_section(sec)
@@ -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
- neuron_seg : h.Segment
18
- The NEURON segment.
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 NEURON segment.
27
+ The segment object from a simulator (e.g. NEURON).
28
28
  """
29
29
 
30
- def __init__(self, idx, parent_idx, neuron_seg, section) -> None:
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 = neuron_seg
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.
@@ -26,9 +26,9 @@ class PathManager:
26
26
  'default_mod': os.path.join(self.path_to_data, 'Default'),
27
27
  'templates': os.path.join(self.path_to_data, 'Templates'),
28
28
  'morphology': os.path.join(self.path_to_model, 'morphology'),
29
- 'membrane': os.path.join(self.path_to_model, 'membrane'),
30
- 'mod': os.path.join(self.path_to_model, 'membrane', 'mod'),
31
- 'python': os.path.join(self.path_to_model, 'membrane', 'python'),
29
+ 'biophys': os.path.join(self.path_to_model, 'biophys'),
30
+ 'mod': os.path.join(self.path_to_model, 'biophys', 'mod'),
31
+ 'python': os.path.join(self.path_to_model, 'biophys', 'python'),
32
32
  'stimuli': os.path.join(self.path_to_model, 'stimuli'),
33
33
  }
34
34
  self._ensure_paths_exist()
@@ -61,8 +61,7 @@ class PathManager:
61
61
  """
62
62
  Copy default mod files to the data directory.
63
63
  """
64
- # __file__ + 'membrane' + 'default_mod'
65
- DEFAULT_MOD_DIR = os.path.join(os.path.dirname(__file__), 'membrane', 'default_mod')
64
+ DEFAULT_MOD_DIR = os.path.join(os.path.dirname(__file__), 'biophys', 'default_mod')
66
65
  for file_name in os.listdir(DEFAULT_MOD_DIR):
67
66
  source = os.path.join(DEFAULT_MOD_DIR, file_name)
68
67
  destination = os.path.join(self.paths['default_mod'], file_name)
@@ -72,8 +71,7 @@ class PathManager:
72
71
  """
73
72
  Copy template files to the data directory.
74
73
  """
75
- # __file__ + 'membrane' + 'templates'
76
- TEMPLATES_DIR = os.path.join(os.path.dirname(__file__), 'membrane', 'default_templates')
74
+ TEMPLATES_DIR = os.path.join(os.path.dirname(__file__), 'biophys', 'default_templates')
77
75
  for file_name in os.listdir(TEMPLATES_DIR):
78
76
  source = os.path.join(TEMPLATES_DIR, file_name)
79
77
  destination = os.path.join(self.paths['templates'], file_name)
@@ -169,16 +167,16 @@ class PathManager:
169
167
  return self.list_files('stimuli', extension=extension)
170
168
 
171
169
 
172
- def list_membrane(self):
170
+ def list_biophys(self):
173
171
  """
174
- List all membrane files.
172
+ List all biophysics files.
175
173
 
176
174
  Returns
177
175
  -------
178
176
  List[str]
179
- A list of membrane file
177
+ A list of biophysics file names.
180
178
  """
181
- return self.list_files('membrane', extension='.json')
179
+ return self.list_files('biophys', extension='.json')
182
180
 
183
181
 
184
182
  def print_directory_tree(self, subfolder=None) -> None: