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
dendrotweaks/model.py CHANGED
@@ -6,16 +6,16 @@ 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 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
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
+ 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
@@ -149,7 +149,7 @@ class Model():
149
149
 
150
150
  # Simulator
151
151
  if simulator_name == 'NEURON':
152
- self.simulator = NEURONSimulator()
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 domain, mechs in self.domains_to_mechs.items():
235
- for mech in mechs:
236
- mechs_to_domains[mech].add(domain)
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
 
@@ -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
  """
@@ -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(self.simulator_name)
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, mechs in self.domains_to_mechs.items():
443
- for mech_name in mechs:
444
- self.insert_mechanism(mech_name, domain_name)
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):
@@ -631,8 +632,8 @@ class Model():
631
632
 
632
633
  # Get data to transfer
633
634
  channel = self.mechanisms[channel_name]
634
- channel_domain_names = [domain_name for domain_name, mechs
635
- in self.domains_to_mechs.items() if channel_name in mechs]
635
+ channel_domain_names = [domain_name for domain_name, mech_names
636
+ in self.domains_to_mechs.items() if channel_name in mech_names]
636
637
  gbar_name = f'gbar_{channel_name}'
637
638
  gbar_distributions = self.params[gbar_name]
638
639
  # Kinetic variables cannot be transferred
@@ -709,13 +710,15 @@ class Model():
709
710
  for mech_name in self.domains_to_mechs[old_domain.name]:
710
711
  # TODO: What if section is already in domain? Can't be as
711
712
  # we use a filtered list of sections.
712
- sec.uninsert_mechanism(mech_name)
713
+ mech = self.mechanisms[mech_name]
714
+ sec.uninsert_mechanism(mech)
713
715
 
714
716
 
715
717
  for sec in sections_to_move:
716
718
  domain.add_section(sec)
717
719
  for mech_name in self.domains_to_mechs.get(domain.name, set()):
718
- sec.insert_mechanism(mech_name, distribute=distribute)
720
+ mech = self.mechanisms[mech_name]
721
+ sec.insert_mechanism(mech, distribute=distribute)
719
722
 
720
723
  self._remove_empty()
721
724
 
@@ -797,7 +800,7 @@ class Model():
797
800
  # domain.insert_mechanism(mech)
798
801
  self.domains_to_mechs[domain_name].add(mech.name)
799
802
  for sec in domain.sections:
800
- sec.insert_mechanism(mech.name)
803
+ sec.insert_mechanism(mech)
801
804
  self._add_mechanism_params(mech)
802
805
 
803
806
  # TODO: Redistribute parameters if any group contains this domain
@@ -847,7 +850,7 @@ class Model():
847
850
 
848
851
  # domain.uninsert_mechanism(mech)
849
852
  for sec in domain.sections:
850
- sec.uninsert_mechanism(mech.name)
853
+ sec.uninsert_mechanism(mech)
851
854
  self.domains_to_mechs[domain_name].remove(mech.name)
852
855
 
853
856
  if not self.mechs_to_domains.get(mech.name):
@@ -1287,7 +1290,7 @@ class Model():
1287
1290
  print(f'Recording added to sec {sec} at loc {loc}.')
1288
1291
 
1289
1292
 
1290
- def remove_recording(self, sec, loc):
1293
+ def remove_recording(self, sec, loc, var='v'):
1291
1294
  """
1292
1295
  Remove a recording from the model.
1293
1296
 
@@ -1298,14 +1301,14 @@ class Model():
1298
1301
  loc : float
1299
1302
  The location along the normalized section length to remove the recording from.
1300
1303
  """
1301
- self.simulator.remove_recording(sec, loc)
1304
+ self.simulator.remove_recording(sec, loc, var)
1302
1305
 
1303
1306
 
1304
- def remove_all_recordings(self):
1307
+ def remove_all_recordings(self, var=None):
1305
1308
  """
1306
1309
  Remove all recordings from the model.
1307
1310
  """
1308
- self.simulator.remove_all_recordings()
1311
+ self.simulator.remove_all_recordings(var=var)
1309
1312
 
1310
1313
 
1311
1314
  def run(self, duration=300):
@@ -1402,7 +1405,8 @@ class Model():
1402
1405
  if mech_name == 'Leak':
1403
1406
  continue
1404
1407
  for sec in root.subtree:
1405
- sec.uninsert_mechanism(mech_name)
1408
+ mech = self.mechanisms[mech_name]
1409
+ sec.uninsert_mechanism(mech)
1406
1410
 
1407
1411
  # Disconnect
1408
1412
  root.disconnect_from_parent()
@@ -1434,7 +1438,8 @@ class Model():
1434
1438
  if mech_name == 'Leak':
1435
1439
  continue
1436
1440
  for sec in root.subtree:
1437
- sec.insert_mechanism(mech_name)
1441
+ mech = self.mechanisms[mech_name]
1442
+ sec.insert_mechanism(mech)
1438
1443
 
1439
1444
  # Replace locs with corresponding segs
1440
1445
 
@@ -1466,7 +1471,8 @@ class Model():
1466
1471
 
1467
1472
  # Reinsert active mechanisms after creating the new domain
1468
1473
  for mech_name in inserted_mechs:
1469
- root.insert_mechanism(mech_name)
1474
+ mech = self.mechanisms[mech_name]
1475
+ root.insert_mechanism(mech)
1470
1476
  self.domains_to_mechs[new_reduced_domain_name] = set(inserted_mechs.keys())
1471
1477
 
1472
1478
 
@@ -1598,7 +1604,7 @@ class Model():
1598
1604
  'name': self.name,
1599
1605
  },
1600
1606
  'd_lambda': self.d_lambda,
1601
- 'domains': {domain: list(mechs) for domain, mechs in self.domains_to_mechs.items()},
1607
+ 'domains': {domain: sorted(list(mechs)) for domain, mechs in self.domains_to_mechs.items()},
1602
1608
  'groups': [
1603
1609
  group.to_dict() for group in self._groups
1604
1610
  ],
@@ -1657,9 +1663,9 @@ class Model():
1657
1663
 
1658
1664
 
1659
1665
 
1660
- def export_membrane(self, file_name, **kwargs):
1666
+ def export_biophys(self, file_name, **kwargs):
1661
1667
  """
1662
- Export the membrane properties of the model to a JSON file.
1668
+ Export the biophysical properties of the model to a JSON file.
1663
1669
 
1664
1670
  Parameters
1665
1671
  ----------
@@ -1669,16 +1675,18 @@ class Model():
1669
1675
  Additional keyword arguments to pass to `json.dump`.
1670
1676
  """
1671
1677
 
1672
- path_to_json = self.path_manager.get_file_path('membrane', file_name, extension='json')
1678
+ path_to_json = self.path_manager.get_file_path('biophys', file_name, extension='json')
1679
+ if not kwargs.get('indent'):
1680
+ kwargs['indent'] = 4
1673
1681
 
1674
1682
  data = self.to_dict()
1675
1683
  with open(path_to_json, 'w') as f:
1676
1684
  json.dump(data, f, **kwargs)
1677
1685
 
1678
1686
 
1679
- def load_membrane(self, file_name, recompile=True):
1687
+ def load_biophys(self, file_name, recompile=True):
1680
1688
  """
1681
- Load the membrane properties of the model from a JSON file.
1689
+ Load the biophysical properties of the model from a JSON file.
1682
1690
 
1683
1691
  Parameters
1684
1692
  ----------
@@ -1688,13 +1696,18 @@ class Model():
1688
1696
  Whether to recompile the mechanisms after loading. Default is True.
1689
1697
  """
1690
1698
  self.add_default_mechanisms()
1691
- self.add_mechanisms('mod', recompile=recompile)
1699
+
1692
1700
 
1693
- path_to_json = self.path_manager.get_file_path('membrane', file_name, extension='json')
1701
+ path_to_json = self.path_manager.get_file_path('biophys', file_name, extension='json')
1694
1702
 
1695
1703
  with open(path_to_json, 'r') as f:
1696
1704
  data = json.load(f)
1697
1705
 
1706
+ for mech_name in {mech for mechs in data['domains'].values() for mech in mechs}:
1707
+ if mech_name in ['Leak', 'CaDyn', 'Independent']:
1708
+ continue
1709
+ self.add_mechanism(mech_name, dir_name='mod', recompile=recompile)
1710
+
1698
1711
  self.from_dict(data)
1699
1712
 
1700
1713
 
@@ -1715,6 +1728,14 @@ class Model():
1715
1728
  **self.simulator.to_dict(),
1716
1729
  },
1717
1730
  'stimuli': {
1731
+ 'recordings': [
1732
+ {
1733
+ 'name': f'rec_{i}',
1734
+ 'var': var
1735
+ }
1736
+ for var, recs in self.simulator.recordings.items()
1737
+ for i, _ in enumerate(recs)
1738
+ ],
1718
1739
  'iclamps': [
1719
1740
  {
1720
1741
  'name': f'iclamp_{i}',
@@ -1743,11 +1764,16 @@ class Model():
1743
1764
  """
1744
1765
 
1745
1766
  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],
1767
+ 'type': [],
1768
+ 'idx': [],
1769
+ 'sec_idx': [],
1770
+ 'loc': [],
1750
1771
  }
1772
+ for var, recs in self.simulator.recordings.items():
1773
+ rec_data['type'].extend(['rec'] * len(recs))
1774
+ rec_data['idx'].extend([i for i in range(len(recs))])
1775
+ rec_data['sec_idx'].extend([seg._section.idx for seg in recs])
1776
+ rec_data['loc'].extend([seg.x for seg in recs])
1751
1777
 
1752
1778
  iclamp_data = {
1753
1779
  'type': ['iclamp'] * len(self.iclamps),
@@ -1776,6 +1802,7 @@ class Model():
1776
1802
  pd.DataFrame(iclamp_data),
1777
1803
  pd.DataFrame(synapses_data)
1778
1804
  ], ignore_index=True)
1805
+ df['idx'] = df['idx'].astype(int)
1779
1806
  df['sec_idx'] = df['sec_idx'].astype(int)
1780
1807
  if path_to_csv: df.to_csv(path_to_csv, index=False)
1781
1808
 
@@ -1797,6 +1824,8 @@ class Model():
1797
1824
 
1798
1825
  data = self.stimuli_to_dict()
1799
1826
 
1827
+ if not kwargs.get('indent'):
1828
+ kwargs['indent'] = 4
1800
1829
  with open(path_to_json, 'w') as f:
1801
1830
  json.dump(data, f, **kwargs)
1802
1831
 
@@ -1827,13 +1856,9 @@ class Model():
1827
1856
 
1828
1857
  self.simulator.from_dict(data['simulation'])
1829
1858
 
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
- )
1859
+ # Clear all stimuli and recordings
1860
+ self.remove_all_stimuli()
1861
+ self.remove_all_recordings()
1837
1862
 
1838
1863
  # IClamps -----------------------------------------------------------
1839
1864
 
@@ -1875,6 +1900,20 @@ class Model():
1875
1900
  pop.update_input_params(**pop_data['input_params'])
1876
1901
  self._add_population(pop)
1877
1902
 
1903
+ # Recordings ---------------------------------------------------------
1904
+
1905
+ df_recs = df_stimuli[df_stimuli['type'] == 'rec'].reset_index(drop=True, inplace=False)
1906
+ for i, row in df_recs.iterrows():
1907
+ # TODO: This conditional statement is to account for a recent change
1908
+ # in the JSON structure. It should be removed in the future.
1909
+ if data['stimuli'].get('recordings'):
1910
+ var = data['stimuli']['recordings'][i]['var']
1911
+ else:
1912
+ var = 'v'
1913
+ self.add_recording(
1914
+ self.sec_tree.sections[row['sec_idx']], row['loc'], var
1915
+ )
1916
+
1878
1917
 
1879
1918
  def export_to_NEURON(self, file_name, include_kinetic_params=True):
1880
1919
  """
@@ -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 = Section(idx=i, parent_idx=-1, points=[child])
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 = Segment(
185
- idx=idx, parent_idx=parent_idx, neuron_seg=seg, section=sec)
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