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.
- dendrotweaks/__init__.py +3 -3
- dendrotweaks/analysis/ephys_analysis.py +12 -8
- dendrotweaks/biophys/__init__.py +7 -0
- dendrotweaks/{membrane → biophys}/default_templates/NEURON_template.py +5 -3
- dendrotweaks/{membrane → biophys}/default_templates/default.py +2 -1
- dendrotweaks/{membrane → biophys}/default_templates/standard_channel.mod +5 -1
- dendrotweaks/{membrane → biophys}/groups.py +0 -5
- dendrotweaks/biophys/io/__init__.py +11 -0
- dendrotweaks/{membrane → biophys}/io/ast.py +6 -0
- dendrotweaks/{membrane → biophys}/io/code_generators.py +13 -3
- dendrotweaks/{membrane → biophys}/io/converter.py +3 -3
- dendrotweaks/{membrane → biophys}/io/factories.py +8 -6
- dendrotweaks/biophys/io/loader.py +190 -0
- dendrotweaks/{membrane → biophys}/io/parser.py +8 -8
- dendrotweaks/{membrane → biophys}/mechanisms.py +14 -10
- dendrotweaks/model.py +91 -52
- dendrotweaks/morphology/__init__.py +2 -2
- dendrotweaks/morphology/io/factories.py +5 -5
- dendrotweaks/morphology/sec_trees.py +139 -164
- dendrotweaks/morphology/seg_trees.py +41 -29
- dendrotweaks/path_manager.py +9 -11
- dendrotweaks/simulators.py +100 -54
- dendrotweaks/stimuli/populations.py +11 -0
- {dendrotweaks-0.3.1.dist-info → dendrotweaks-0.4.1.dist-info}/METADATA +2 -2
- dendrotweaks-0.4.1.dist-info/RECORD +56 -0
- {dendrotweaks-0.3.1.dist-info → dendrotweaks-0.4.1.dist-info}/WHEEL +1 -1
- dendrotweaks/membrane/__init__.py +0 -6
- dendrotweaks/membrane/io/__init__.py +0 -11
- dendrotweaks/membrane/io/loader.py +0 -90
- dendrotweaks-0.3.1.dist-info/RECORD +0 -56
- /dendrotweaks/{membrane → biophys}/default_mod/AMPA.mod +0 -0
- /dendrotweaks/{membrane → biophys}/default_mod/AMPA_NMDA.mod +0 -0
- /dendrotweaks/{membrane → biophys}/default_mod/CaDyn.mod +0 -0
- /dendrotweaks/{membrane → biophys}/default_mod/GABAa.mod +0 -0
- /dendrotweaks/{membrane → biophys}/default_mod/Leak.mod +0 -0
- /dendrotweaks/{membrane → biophys}/default_mod/NMDA.mod +0 -0
- /dendrotweaks/{membrane → biophys}/default_mod/vecstim.mod +0 -0
- /dendrotweaks/{membrane → biophys}/default_templates/template_jaxley.py +0 -0
- /dendrotweaks/{membrane → biophys}/default_templates/template_jaxley_new.py +0 -0
- /dendrotweaks/{membrane → biophys}/distributions.py +0 -0
- /dendrotweaks/{membrane → biophys}/io/grammar.py +0 -0
- /dendrotweaks/{membrane → biophys}/io/reader.py +0 -0
- {dendrotweaks-0.3.1.dist-info → dendrotweaks-0.4.1.dist-info}/licenses/LICENSE +0 -0
- {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
|
12
|
-
from dendrotweaks.
|
13
|
-
from dendrotweaks.
|
14
|
-
from dendrotweaks.
|
15
|
-
from dendrotweaks.
|
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.
|
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 =
|
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
|
|
@@ -343,11 +343,11 @@ class Model():
|
|
343
343
|
"""
|
344
344
|
return self.path_manager.list_files('morphology', extension=extension)
|
345
345
|
|
346
|
-
def
|
346
|
+
def list_biophys(self, extension='json'):
|
347
347
|
"""
|
348
|
-
List the
|
348
|
+
List the biophysical configurations available for the model.
|
349
349
|
"""
|
350
|
-
return self.path_manager.list_files('
|
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
|
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(
|
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):
|
@@ -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,
|
635
|
-
in self.domains_to_mechs.items() if channel_name in
|
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
|
-
|
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
|
-
|
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
|
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
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
1666
|
+
def export_biophys(self, file_name, **kwargs):
|
1661
1667
|
"""
|
1662
|
-
Export the
|
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('
|
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
|
1687
|
+
def load_biophys(self, file_name, recompile=True):
|
1680
1688
|
"""
|
1681
|
-
Load the
|
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
|
-
|
1699
|
+
|
1692
1700
|
|
1693
|
-
path_to_json = self.path_manager.get_file_path('
|
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': [
|
1747
|
-
'idx': [
|
1748
|
-
'sec_idx': [
|
1749
|
-
'loc': [
|
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
|
-
#
|
1831
|
-
|
1832
|
-
|
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 =
|
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
|
|