dendrotweaks 0.3.1__py3-none-any.whl → 0.4.0__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 (41) hide show
  1. dendrotweaks/__init__.py +2 -2
  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 +2 -2
  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 +7 -1
  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 +65 -32
  17. dendrotweaks/morphology/sec_trees.py +1 -1
  18. dendrotweaks/path_manager.py +9 -11
  19. dendrotweaks/simulators.py +82 -48
  20. dendrotweaks/stimuli/populations.py +11 -0
  21. {dendrotweaks-0.3.1.dist-info → dendrotweaks-0.4.0.dist-info}/METADATA +2 -2
  22. dendrotweaks-0.4.0.dist-info/RECORD +56 -0
  23. {dendrotweaks-0.3.1.dist-info → dendrotweaks-0.4.0.dist-info}/WHEEL +1 -1
  24. dendrotweaks/membrane/__init__.py +0 -6
  25. dendrotweaks/membrane/io/__init__.py +0 -11
  26. dendrotweaks/membrane/io/loader.py +0 -90
  27. dendrotweaks-0.3.1.dist-info/RECORD +0 -56
  28. /dendrotweaks/{membrane → biophys}/default_mod/AMPA.mod +0 -0
  29. /dendrotweaks/{membrane → biophys}/default_mod/AMPA_NMDA.mod +0 -0
  30. /dendrotweaks/{membrane → biophys}/default_mod/CaDyn.mod +0 -0
  31. /dendrotweaks/{membrane → biophys}/default_mod/GABAa.mod +0 -0
  32. /dendrotweaks/{membrane → biophys}/default_mod/Leak.mod +0 -0
  33. /dendrotweaks/{membrane → biophys}/default_mod/NMDA.mod +0 -0
  34. /dendrotweaks/{membrane → biophys}/default_mod/vecstim.mod +0 -0
  35. /dendrotweaks/{membrane → biophys}/default_templates/template_jaxley.py +0 -0
  36. /dendrotweaks/{membrane → biophys}/default_templates/template_jaxley_new.py +0 -0
  37. /dendrotweaks/{membrane → biophys}/distributions.py +0 -0
  38. /dendrotweaks/{membrane → biophys}/io/grammar.py +0 -0
  39. /dendrotweaks/{membrane → biophys}/io/reader.py +0 -0
  40. {dendrotweaks-0.3.1.dist-info → dendrotweaks-0.4.0.dist-info}/licenses/LICENSE +0 -0
  41. {dendrotweaks-0.3.1.dist-info → dendrotweaks-0.4.0.dist-info}/top_level.txt +0 -0
dendrotweaks/model.py CHANGED
@@ -9,13 +9,13 @@ from dendrotweaks.morphology.point_trees import PointTree
9
9
  from dendrotweaks.morphology.sec_trees import Section, SectionTree, Domain
10
10
  from dendrotweaks.morphology.seg_trees import Segment, SegmentTree
11
11
  from dendrotweaks.simulators import NEURONSimulator
12
- from dendrotweaks.membrane.groups import SegmentGroup
13
- from dendrotweaks.membrane.mechanisms import Mechanism, LeakChannel, CaDynamics
14
- from dendrotweaks.membrane.io import create_channel, standardize_channel, create_standard_channel
15
- from dendrotweaks.membrane.io import MODFileLoader
12
+ from dendrotweaks.biophys.groups import SegmentGroup
13
+ from dendrotweaks.biophys.mechanisms import Mechanism, LeakChannel, CaDynamics
14
+ from dendrotweaks.biophys.io import create_channel, standardize_channel, create_standard_channel
15
+ from dendrotweaks.biophys.io import MODFileLoader
16
16
  from dendrotweaks.morphology.io import create_point_tree, create_section_tree, create_segment_tree
17
17
  from dendrotweaks.stimuli.iclamps import IClamp
18
- from dendrotweaks.membrane.distributions import Distribution
18
+ from dendrotweaks.biophys.distributions import Distribution
19
19
  from dendrotweaks.stimuli.populations import Population
20
20
  from dendrotweaks.utils import calculate_lambda_f, dynamic_import
21
21
  from dendrotweaks.utils import get_domain_color, timeit
@@ -343,11 +343,11 @@ class Model():
343
343
  """
344
344
  return self.path_manager.list_files('morphology', extension=extension)
345
345
 
346
- def list_membrane_configs(self, extension='json'):
346
+ def list_biophys(self, extension='json'):
347
347
  """
348
- List the membrane configurations available for the model.
348
+ List the biophysical configurations available for the model.
349
349
  """
350
- return self.path_manager.list_files('membrane', extension=extension)
350
+ return self.path_manager.list_files('biophys', extension=extension)
351
351
 
352
352
  def list_mechanisms(self, extension='mod'):
353
353
  """
@@ -355,7 +355,7 @@ class Model():
355
355
  """
356
356
  return self.path_manager.list_files('mod', extension=extension)
357
357
 
358
- def list_stimuli_configs(self, extension='json'):
358
+ def list_stimuli(self, extension='json'):
359
359
  """
360
360
  List the stimuli configurations available for the model.
361
361
  """
@@ -1287,7 +1287,7 @@ class Model():
1287
1287
  print(f'Recording added to sec {sec} at loc {loc}.')
1288
1288
 
1289
1289
 
1290
- def remove_recording(self, sec, loc):
1290
+ def remove_recording(self, sec, loc, var='v'):
1291
1291
  """
1292
1292
  Remove a recording from the model.
1293
1293
 
@@ -1298,14 +1298,14 @@ class Model():
1298
1298
  loc : float
1299
1299
  The location along the normalized section length to remove the recording from.
1300
1300
  """
1301
- self.simulator.remove_recording(sec, loc)
1301
+ self.simulator.remove_recording(sec, loc, var)
1302
1302
 
1303
1303
 
1304
- def remove_all_recordings(self):
1304
+ def remove_all_recordings(self, var=None):
1305
1305
  """
1306
1306
  Remove all recordings from the model.
1307
1307
  """
1308
- self.simulator.remove_all_recordings()
1308
+ self.simulator.remove_all_recordings(var=var)
1309
1309
 
1310
1310
 
1311
1311
  def run(self, duration=300):
@@ -1598,7 +1598,7 @@ class Model():
1598
1598
  'name': self.name,
1599
1599
  },
1600
1600
  'd_lambda': self.d_lambda,
1601
- 'domains': {domain: list(mechs) for domain, mechs in self.domains_to_mechs.items()},
1601
+ 'domains': {domain: sorted(list(mechs)) for domain, mechs in self.domains_to_mechs.items()},
1602
1602
  'groups': [
1603
1603
  group.to_dict() for group in self._groups
1604
1604
  ],
@@ -1657,9 +1657,9 @@ class Model():
1657
1657
 
1658
1658
 
1659
1659
 
1660
- def export_membrane(self, file_name, **kwargs):
1660
+ def export_biophys(self, file_name, **kwargs):
1661
1661
  """
1662
- Export the membrane properties of the model to a JSON file.
1662
+ Export the biophysical properties of the model to a JSON file.
1663
1663
 
1664
1664
  Parameters
1665
1665
  ----------
@@ -1669,16 +1669,18 @@ class Model():
1669
1669
  Additional keyword arguments to pass to `json.dump`.
1670
1670
  """
1671
1671
 
1672
- path_to_json = self.path_manager.get_file_path('membrane', file_name, extension='json')
1672
+ path_to_json = self.path_manager.get_file_path('biophys', file_name, extension='json')
1673
+ if not kwargs.get('indent'):
1674
+ kwargs['indent'] = 4
1673
1675
 
1674
1676
  data = self.to_dict()
1675
1677
  with open(path_to_json, 'w') as f:
1676
1678
  json.dump(data, f, **kwargs)
1677
1679
 
1678
1680
 
1679
- def load_membrane(self, file_name, recompile=True):
1681
+ def load_biophys(self, file_name, recompile=True):
1680
1682
  """
1681
- Load the membrane properties of the model from a JSON file.
1683
+ Load the biophysical properties of the model from a JSON file.
1682
1684
 
1683
1685
  Parameters
1684
1686
  ----------
@@ -1688,13 +1690,18 @@ class Model():
1688
1690
  Whether to recompile the mechanisms after loading. Default is True.
1689
1691
  """
1690
1692
  self.add_default_mechanisms()
1691
- self.add_mechanisms('mod', recompile=recompile)
1693
+
1692
1694
 
1693
- path_to_json = self.path_manager.get_file_path('membrane', file_name, extension='json')
1695
+ path_to_json = self.path_manager.get_file_path('biophys', file_name, extension='json')
1694
1696
 
1695
1697
  with open(path_to_json, 'r') as f:
1696
1698
  data = json.load(f)
1697
1699
 
1700
+ for mech_name in {mech for mechs in data['domains'].values() for mech in mechs}:
1701
+ if mech_name in ['Leak', 'CaDyn', 'Independent']:
1702
+ continue
1703
+ self.add_mechanism(mech_name, dir_name='mod', recompile=recompile)
1704
+
1698
1705
  self.from_dict(data)
1699
1706
 
1700
1707
 
@@ -1715,6 +1722,14 @@ class Model():
1715
1722
  **self.simulator.to_dict(),
1716
1723
  },
1717
1724
  'stimuli': {
1725
+ 'recordings': [
1726
+ {
1727
+ 'name': f'rec_{i}',
1728
+ 'var': var
1729
+ }
1730
+ for var, recs in self.simulator.recordings.items()
1731
+ for i, _ in enumerate(recs)
1732
+ ],
1718
1733
  'iclamps': [
1719
1734
  {
1720
1735
  'name': f'iclamp_{i}',
@@ -1743,11 +1758,16 @@ class Model():
1743
1758
  """
1744
1759
 
1745
1760
  rec_data = {
1746
- 'type': ['recording'] * len(self.recordings),
1747
- 'idx': [i for i in range(len(self.recordings))],
1748
- 'sec_idx': [seg._section.idx for seg in self.recordings],
1749
- 'loc': [seg.x for seg in self.recordings],
1761
+ 'type': [],
1762
+ 'idx': [],
1763
+ 'sec_idx': [],
1764
+ 'loc': [],
1750
1765
  }
1766
+ for var, recs in self.simulator.recordings.items():
1767
+ rec_data['type'].extend(['rec'] * len(recs))
1768
+ rec_data['idx'].extend([i for i in range(len(recs))])
1769
+ rec_data['sec_idx'].extend([seg._section.idx for seg in recs])
1770
+ rec_data['loc'].extend([seg.x for seg in recs])
1751
1771
 
1752
1772
  iclamp_data = {
1753
1773
  'type': ['iclamp'] * len(self.iclamps),
@@ -1776,6 +1796,7 @@ class Model():
1776
1796
  pd.DataFrame(iclamp_data),
1777
1797
  pd.DataFrame(synapses_data)
1778
1798
  ], ignore_index=True)
1799
+ df['idx'] = df['idx'].astype(int)
1779
1800
  df['sec_idx'] = df['sec_idx'].astype(int)
1780
1801
  if path_to_csv: df.to_csv(path_to_csv, index=False)
1781
1802
 
@@ -1797,6 +1818,8 @@ class Model():
1797
1818
 
1798
1819
  data = self.stimuli_to_dict()
1799
1820
 
1821
+ if not kwargs.get('indent'):
1822
+ kwargs['indent'] = 4
1800
1823
  with open(path_to_json, 'w') as f:
1801
1824
  json.dump(data, f, **kwargs)
1802
1825
 
@@ -1827,13 +1850,9 @@ class Model():
1827
1850
 
1828
1851
  self.simulator.from_dict(data['simulation'])
1829
1852
 
1830
- # Recordings ---------------------------------------------------------
1831
-
1832
- df_recs = df_stimuli[df_stimuli['type'] == 'recording']
1833
- for i, row in df_recs.iterrows():
1834
- self.add_recording(
1835
- self.sec_tree.sections[row['sec_idx']], row['loc']
1836
- )
1853
+ # Clear all stimuli and recordings
1854
+ self.remove_all_stimuli()
1855
+ self.remove_all_recordings()
1837
1856
 
1838
1857
  # IClamps -----------------------------------------------------------
1839
1858
 
@@ -1875,6 +1894,20 @@ class Model():
1875
1894
  pop.update_input_params(**pop_data['input_params'])
1876
1895
  self._add_population(pop)
1877
1896
 
1897
+ # Recordings ---------------------------------------------------------
1898
+
1899
+ df_recs = df_stimuli[df_stimuli['type'] == 'rec'].reset_index(drop=True, inplace=False)
1900
+ for i, row in df_recs.iterrows():
1901
+ # TODO: This conditional statement is to account for a recent change
1902
+ # in the JSON structure. It should be removed in the future.
1903
+ if data['stimuli'].get('recordings'):
1904
+ var = data['stimuli']['recordings'][i]['var']
1905
+ else:
1906
+ var = 'v'
1907
+ self.add_recording(
1908
+ self.sec_tree.sections[row['sec_idx']], row['loc'], var
1909
+ )
1910
+
1878
1911
 
1879
1912
  def export_to_NEURON(self, file_name, include_kinetic_params=True):
1880
1913
  """
@@ -770,7 +770,7 @@ class SectionTree(Tree):
770
770
  """
771
771
 
772
772
  unique_domain_names = set([sec.domain for sec in self.sections])
773
- self.domains = {name: Domain(name) for name in unique_domain_names}
773
+ self.domains = {name: Domain(name) for name in sorted(unique_domain_names)}
774
774
 
775
775
  for sec in self.sections:
776
776
  self.domains[sec.domain].add_section(sec)
@@ -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:
@@ -1,5 +1,6 @@
1
1
  from collections import defaultdict
2
2
  import warnings
3
+ from functools import cached_property
3
4
 
4
5
  import matplotlib.pyplot as plt
5
6
  import neuron
@@ -35,26 +36,47 @@ class Simulator:
35
36
  A generic simulator class.
36
37
  """
37
38
  def __init__(self):
38
- self.vs = None
39
- self.t = None
39
+ self._t = None
40
40
  self.dt = None
41
- self.recordings = {}
41
+ self._recordings = {'v': {}}
42
42
 
43
- def plot_voltage(self, ax=None, segments=None, **kwargs):
43
+ def plot_var(self, var='v', ax=None, segments=None, **kwargs):
44
+ if self._t is None:
45
+ raise ValueError('Simulation has not been run yet.')
46
+ if var not in self.recordings:
47
+ raise ValueError(f'Variable {var} not recorded.')
44
48
  if ax is None:
45
49
  fig, ax = plt.subplots()
46
50
  if segments is None:
47
- segments = self.recordings.keys()
48
- for seg, v in self.vs.items():
51
+ segments = self.recordings[var].keys()
52
+ for seg, x in self.recordings[var].items():
49
53
  if segments and seg not in segments:
50
54
  continue
51
- ax.plot(self.t, v, label=f'{seg.domain} {seg.idx}', **kwargs)
52
-
55
+ ax.plot(self.t, x, label=f'{var} {seg.domain} {seg.idx}', **kwargs)
53
56
  if len(segments) < 10:
54
57
  ax.legend()
55
- # ax.set_ylim(-100, 60)
56
58
  ax.set_xlabel('Time (ms)')
57
- ax.set_ylabel('Voltage (mV)')
59
+ if var == 'v':
60
+ ax.set_ylabel('Voltage (mV)')
61
+ elif var.startswith('i_'):
62
+ ax.set_ylabel('Current (nA)')
63
+ return ax
64
+
65
+ def plot_voltage(self, **kwargs):
66
+ """
67
+ Plot the recorded voltages.
68
+ """
69
+ self.plot_var('v', **kwargs)
70
+
71
+ def plot_currents(self, **kwargs):
72
+ """
73
+ Plot the recorded currents.
74
+ """
75
+ ax = kwargs.pop('ax', None)
76
+ for var in self.recordings:
77
+ if var.startswith('i_'):
78
+ ax = self.plot_var(var, ax=ax, **kwargs)
79
+
58
80
 
59
81
 
60
82
  class NEURONSimulator(Simulator):
@@ -88,10 +110,33 @@ class NEURONSimulator(Simulator):
88
110
  self.temperature = temperature
89
111
  self.v_init = v_init * mV
90
112
  self._duration = 300
113
+
91
114
 
92
115
  self.dt = dt
93
116
  self._cvode = cvode
94
117
 
118
+ @cached_property
119
+ def recordings(self):
120
+ return {
121
+ var:{ seg: vec.to_python() for seg, vec in recs.items() }
122
+ for var, recs in self._recordings.items()
123
+ }
124
+
125
+ @cached_property
126
+ def t(self):
127
+ return self._t.to_python()
128
+
129
+ def _clean_cache(self):
130
+ """
131
+ Clean the cache of the simulator.
132
+ """
133
+ try:
134
+ del self.recordings
135
+ del self.t
136
+ except AttributeError:
137
+ # Property hasn't been accessed yet, so no need to delete
138
+ pass
139
+
95
140
 
96
141
  def add_recording(self, sec, loc, var='v'):
97
142
  """
@@ -107,11 +152,16 @@ class NEURONSimulator(Simulator):
107
152
  The variable to record. Default is 'v' (voltage).
108
153
  """
109
154
  seg = sec(loc)
110
- if self.recordings.get(seg):
111
- self.remove_recording(sec, loc)
112
- self.recordings[seg] = h.Vector().record(getattr(seg._ref, f'_ref_{var}'))
113
-
114
- def remove_recording(self, sec, loc):
155
+ if not hasattr(seg._ref, f'_ref_{var}'):
156
+ raise ValueError(f'Segment {seg} does not have variable {var}.')
157
+ if self._recordings.get(var, {}).get(seg):
158
+ self.remove_recording(sec, loc, var)
159
+ if var not in self._recordings:
160
+ self._recordings[var] = {}
161
+ self._recordings[var][seg] = h.Vector().record(getattr(seg._ref, f'_ref_{var}'))
162
+ self._clean_cache()
163
+
164
+ def remove_recording(self, sec, loc, var='v'):
115
165
  """
116
166
  Remove a recording from the simulator.
117
167
 
@@ -123,20 +173,23 @@ class NEURONSimulator(Simulator):
123
173
  The location along the normalized section length to remove the recording from.
124
174
  """
125
175
  seg = sec(loc)
126
- if self.recordings.get(seg):
127
- self.recordings[seg] = None
128
- self.recordings.pop(seg)
129
-
130
- def remove_all_recordings(self):
176
+ if self._recordings[var].get(seg):
177
+ self._recordings[var][seg] = None
178
+ self._recordings[var].pop(seg)
179
+ if not self._recordings[var]:
180
+ self._recordings.pop(var)
181
+ self._clean_cache()
182
+
183
+ def remove_all_recordings(self, var=None):
131
184
  """
132
185
  Remove all recordings from the simulator.
133
186
  """
134
- for seg in list(self.recordings.keys()):
135
- sec, loc = seg._section, seg.x
136
- self.remove_recording(sec, loc)
137
- if self.recordings:
138
- warnings.warn(f'Not all recordings were removed: {self.recordings}')
139
- self.recordings = {}
187
+ variables = [var] if var else list(self._recordings.keys())
188
+ for variable in variables:
189
+ for seg in list(self._recordings.get(variable, {}).keys()):
190
+ self.remove_recording(seg._section, seg.x, variable)
191
+ if self._recordings.get(variable):
192
+ warnings.warn(f'Not all recordings were removed for variable {variable}: {self._recordings}')
140
193
 
141
194
 
142
195
  def _init_simulation(self):
@@ -161,35 +214,16 @@ class NEURONSimulator(Simulator):
161
214
  duration : float
162
215
  The duration of the simulation in milliseconds.
163
216
  """
164
- self._duration = duration
165
-
166
-
167
- # vs = list(self.recordings.values())
168
- Is = []
169
217
 
170
- # for v in self.recordings.values():
171
- # # v = h.Vector().record(seg._ref_v)
172
- # vs.append(v)
218
+ self._clean_cache()
173
219
 
174
- t = h.Vector().record(h._ref_t)
220
+ self._duration = duration
175
221
 
176
- # if self.ch is None:
177
- # pass
178
- # else:
179
- # for seg in self.recordings.keys():
180
- # if getattr(seg, f'_ref_i_{self.ch.suffix}', None) is None:
181
- # logger.warning(
182
- # f'No current recorded for {self.ch.suffix} at {seg}. Make i a RANGE variable in mod file.')
183
- # continue
184
- # I = h.Vector().record(getattr(seg, f'_ref_i_{self.ch.suffix}'))
185
- # Is.append(I)
222
+ self._t = h.Vector().record(h._ref_t)
186
223
 
187
224
  self._init_simulation()
188
225
 
189
226
  h.continuerun(duration * ms)
190
-
191
- self.t = t.to_python()
192
- self.vs = {seg: v.to_python() for seg, v in self.recordings.items()}
193
227
 
194
228
 
195
229
  def to_dict(self):
@@ -123,6 +123,17 @@ class Population():
123
123
  spike_times[syn].extend(syn.spike_times)
124
124
  return dict(spike_times)
125
125
 
126
+ @property
127
+ def n_per_seg(self):
128
+ """
129
+ Return the number of synapses per segment.
130
+ """
131
+ n_per_seg = {seg: 0 for seg in self.segments}
132
+ for (sec, loc), syns in self.synapses.items():
133
+ seg = sec(loc)
134
+ n_per_seg[seg] += len(syns)
135
+ return dict(n_per_seg)
136
+
126
137
 
127
138
  def update_kinetic_params(self, **params):
128
139
  """
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dendrotweaks
3
- Version: 0.3.1
3
+ Version: 0.4.0
4
4
  Summary: A toolbox for exploring dendritic dynamics
5
5
  Home-page: https://dendrotweaks.dendrites.gr
6
6
  Author: Roman Makarov
@@ -15,7 +15,7 @@ Classifier: Operating System :: OS Independent
15
15
  Requires-Python: >=3.9
16
16
  Description-Content-Type: text/markdown
17
17
  License-File: LICENSE
18
- Requires-Dist: neuron>=8.2.6
18
+ Requires-Dist: neuron>=8.2.6; sys_platform == "linux" or sys_platform == "darwin"
19
19
  Requires-Dist: neuron-reduce==0.0.7
20
20
  Requires-Dist: numpy<2.0.0
21
21
  Requires-Dist: pandas
@@ -0,0 +1,56 @@
1
+ dendrotweaks/__init__.py,sha256=qmlQDMHQGSD70naZme_cEMgale9T5OH6VqjdxhznouM,384
2
+ dendrotweaks/model.py,sha256=XX4WPaHPYUzm9CF4S6lUCDc_a1mORJaEbwtLexqEKCQ,68796
3
+ dendrotweaks/model_io.py,sha256=xwXKMcUle-Y0HoWFYVZu3G8v4pdQXmeaDfl2Xi65eHw,2137
4
+ dendrotweaks/path_manager.py,sha256=dai5o6UA0nk-ubwKWRu4LFdDBO77zW_SsMf6k0MLBiI,8703
5
+ dendrotweaks/simulators.py,sha256=ADiXPqcWZ7oLQfMMnhgQNpgCW-NE6lBb5hZCQACSIkM,7237
6
+ dendrotweaks/utils.py,sha256=jaUJNb39Bsevg3WJByP56bO7CLj1wzlh-uGZl-lxi1I,7131
7
+ dendrotweaks/analysis/__init__.py,sha256=SEYpoQ5iXiQXyHB20-IAdDHYI-7CR5GYFXIwr-O05Ug,858
8
+ dendrotweaks/analysis/ephys_analysis.py,sha256=Caiww27p9dnL5_27OmZ95AZ_OmefvImAPyItsJMSdmA,14320
9
+ dendrotweaks/analysis/morphometric_analysis.py,sha256=5zohjGssyx-wezI-yY3Q-kYM_wzAQLLFBJ9Xk950_JY,3571
10
+ dendrotweaks/biophys/__init__.py,sha256=k0o2xwyoaJUb1lfO9OHtqxheNP6R-Ya5o0g-bJOdCZg,360
11
+ dendrotweaks/biophys/distributions.py,sha256=ADPFPA-CN7AbRJj0Ry4TxFZJhdYXJm87iIGWZSDr5vI,10299
12
+ dendrotweaks/biophys/groups.py,sha256=Kze8ft8EYFXWW6zJhhbpWQUUt82L47g2IbB-5WpsWrY,3481
13
+ dendrotweaks/biophys/mechanisms.py,sha256=j9uygcwkK6Z_08hpTHax40Wn-eV4V_k_on_KyPDnO90,18520
14
+ dendrotweaks/biophys/default_mod/AMPA.mod,sha256=HY_pWzYvaSDV-w7qruenG2mnll8v79s40HFHjUCIi4U,980
15
+ dendrotweaks/biophys/default_mod/AMPA_NMDA.mod,sha256=ztv2ePUiEQZ93-23FTkGO2DC91rehQuqo0NUIbHZ368,2318
16
+ dendrotweaks/biophys/default_mod/CaDyn.mod,sha256=gwc69K_rxu2w_mV7CnOSOnVaCMc8Z-MfdBFf6lAj4kg,1298
17
+ dendrotweaks/biophys/default_mod/GABAa.mod,sha256=jdGRid-Wzw4y9kHvq74oSMogLhSiS-Ac2DDaLOrxVi8,983
18
+ dendrotweaks/biophys/default_mod/Leak.mod,sha256=u0lwMYGgl5kNZN5W4N6YHgRSeVxb-z2oM9fqou5rCV8,420
19
+ dendrotweaks/biophys/default_mod/NMDA.mod,sha256=tT4Q5UPoeztXcQ45uZc2PUO3-8OkDLCmrS7WDsn1yQQ,1185
20
+ dendrotweaks/biophys/default_mod/vecstim.mod,sha256=iSpJgR96O2Z3pLNUFIsZ7YJ529ncKUBaZqDJvA0_oV0,965
21
+ dendrotweaks/biophys/default_templates/NEURON_template.py,sha256=MWSv2fLKGJxdt2zfSO0b74peuBC8U9j_S6AwM5URXts,14945
22
+ dendrotweaks/biophys/default_templates/default.py,sha256=7HEbR2GJEOhgiox1QtZUEuHi5ihNAHDLsXQiQk980tI,2201
23
+ dendrotweaks/biophys/default_templates/standard_channel.mod,sha256=sw80c-JyqfXNA7c7v7pZGLY-0MgFUvd3bPvJcAGXNSk,2923
24
+ dendrotweaks/biophys/default_templates/template_jaxley.py,sha256=t-GsCSUyQ7rDoaLmyuWd9bIxB8W3bCqJdnikD59EVvI,3676
25
+ dendrotweaks/biophys/default_templates/template_jaxley_new.py,sha256=I62KhnOYNV1bT-nPsDTxjIISYmDcso2X8rnsos28nYs,3631
26
+ dendrotweaks/biophys/io/__init__.py,sha256=kkmQ4L0SatI3lWd3qE8KqOIKd7x3G2OnqAAW93sWWCU,575
27
+ dendrotweaks/biophys/io/ast.py,sha256=7x_Kxz1qoQHZeIjovUNyVuKgUo4vAFKm-bd4hn9n1CI,6078
28
+ dendrotweaks/biophys/io/code_generators.py,sha256=yg0Do1XLM_rIXk4i_FibJGfkWOt-GN0lq5dOCD4D3pk,11702
29
+ dendrotweaks/biophys/io/converter.py,sha256=5yrPJhyZbuwV7tTGoacnNOvmRdVgXPIyGfiR0PyOVzg,3371
30
+ dendrotweaks/biophys/io/factories.py,sha256=j1Hi2u-NTFFL8ElRYlgGVNHRcfKWH6o5GfKvraMTlwM,5020
31
+ dendrotweaks/biophys/io/grammar.py,sha256=TJLTDlr8Ajp3J9DJ4IvulOCcpUkYr7HnoI0TGnNuEPc,11677
32
+ dendrotweaks/biophys/io/loader.py,sha256=Wv9ZkEDyA3MkCdV0sMeRnBffg2WAI7yTV3r6C412GiY,6378
33
+ dendrotweaks/biophys/io/parser.py,sha256=boT27lFrn5LYrJnkZFs0SwrZZrkSkwO8efqGPJ4Qj0I,17914
34
+ dendrotweaks/biophys/io/reader.py,sha256=JWm5WM9illvSfDkhWEmWBcj8Y7PSi8zeZX9j1ARUHVU,6576
35
+ dendrotweaks/morphology/__init__.py,sha256=aqJTQOpRVOYcbWqZ2q4e-Oy735r9_ubW-uE_5cFZVtI,323
36
+ dendrotweaks/morphology/domains.py,sha256=Y4txcGdBdl2aK1DfbTRziNtDyd6bChczwpCWE7lTFzg,2391
37
+ dendrotweaks/morphology/point_trees.py,sha256=5dUPaQXYPdJbWoD3pFI2DV2XnuFRhB5d0wTBlfmmIeI,21600
38
+ dendrotweaks/morphology/sec_trees.py,sha256=4EU8YbYsi1Pr_n7vkFuKQtIIWbJwP-8Q_epmioEXAIw,36902
39
+ dendrotweaks/morphology/seg_trees.py,sha256=uwL1X9qeFNyJVHua1I3rhp0fLSRrS2TAVyb1Fnw4RwQ,3595
40
+ dendrotweaks/morphology/trees.py,sha256=NrNvPMR-U0clt63eqwVJqU0H8NJgY53QGA_BkdcwkQI,16033
41
+ dendrotweaks/morphology/io/__init__.py,sha256=gAZqZdf5VKPb6ksK8Lwt7MbTAq8TDP8uq3Vs_ebNFEY,324
42
+ dendrotweaks/morphology/io/factories.py,sha256=NngNINw73diGK7fud314JzWVhxv2aYLuA9wUuQA0Diw,6344
43
+ dendrotweaks/morphology/io/reader.py,sha256=hW3c541WtG1rNag_YreEhvrLzm8-OTtw0fQREeHDthM,1913
44
+ dendrotweaks/morphology/io/validation.py,sha256=lVkYw9y9yG5QpRh_N0YQ3FbZwuSUsQfSqJTMumMcDdc,7872
45
+ dendrotweaks/morphology/reduce/__init__.py,sha256=p6Mg3KDHxTt8S4DtI0m7L7MqV6dS2pdIYAwB7B-toVw,921
46
+ dendrotweaks/morphology/reduce/reduce.py,sha256=5czZDrG3xsvHn3c_tbYhUOlXgST989-RS-ntbhlvvA0,6361
47
+ dendrotweaks/morphology/reduce/reduced_cylinder.py,sha256=jGJ4J-amukRr-3DPirVR5pzNO-6H7_sZF1N_X57ZGdw,5132
48
+ dendrotweaks/stimuli/__init__.py,sha256=bFfSEZhCVpwOVEBgLe65iiY3SdpjKPhyLemC1z5OX9I,153
49
+ dendrotweaks/stimuli/iclamps.py,sha256=NjkhhwZKJR1f_g3N9BVxMVoO9ubBk5WkQ6h9Bnf9xgA,1681
50
+ dendrotweaks/stimuli/populations.py,sha256=y85v8smiMifINIqXm1O3mOINAlDTz-SPGLS78alhX5A,8325
51
+ dendrotweaks/stimuli/synapses.py,sha256=g4MgWTske2TZ2i9FIIOE8-KXNx_3dWa3zEhB2rcqYig,5470
52
+ dendrotweaks-0.4.0.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
53
+ dendrotweaks-0.4.0.dist-info/METADATA,sha256=9AAcxk2ZzIvqUOQg7hNf0Tw1an2EYJLl76gSI21WAKM,2740
54
+ dendrotweaks-0.4.0.dist-info/WHEEL,sha256=lTU6B6eIfYoiQJTZNc-fyaR6BpL6ehTzU3xGYxn2n8k,91
55
+ dendrotweaks-0.4.0.dist-info/top_level.txt,sha256=OzT_2BSI5j5zxC447K6Y-0W-GHbued7iX-_hFGAKMxY,13
56
+ dendrotweaks-0.4.0.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (77.0.3)
2
+ Generator: setuptools (78.1.1)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,6 +0,0 @@
1
- from dendrotweaks.membrane.mechanisms import Mechanism, IonChannel, CaDynamics, StandardIonChannel, LeakChannel
2
- from dendrotweaks.membrane.mechanisms import LeakChannel
3
- from dendrotweaks.membrane.groups import SegmentGroup
4
- from dendrotweaks.membrane.distributions import Distribution
5
-
6
- import dendrotweaks.membrane.io as io
@@ -1,11 +0,0 @@
1
- from dendrotweaks.membrane.io.loader import MODFileLoader
2
- from dendrotweaks.membrane.io.converter import MODFileConverter
3
-
4
- from dendrotweaks.membrane.io.reader import MODFileReader
5
- from dendrotweaks.membrane.io.parser import MODFileParser
6
- from dendrotweaks.membrane.io.code_generators import PythonCodeGenerator
7
- from dendrotweaks.membrane.io.code_generators import NMODLCodeGenerator
8
-
9
- from dendrotweaks.membrane.io.factories import create_channel
10
- from dendrotweaks.membrane.io.factories import create_standard_channel
11
- from dendrotweaks.membrane.io.factories import standardize_channel