dendrotweaks 0.4.2__py3-none-any.whl → 0.4.4__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 +1 -1
- dendrotweaks/analysis/ephys_analysis.py +4 -2
- dendrotweaks/biophys/distributions.py +2 -2
- dendrotweaks/biophys/mechanisms.py +1 -1
- dendrotweaks/model.py +60 -20
- dendrotweaks/morphology/domains.py +3 -0
- dendrotweaks/morphology/sec_trees.py +60 -26
- dendrotweaks/simulators.py +1 -1
- dendrotweaks/stimuli/populations.py +1 -1
- {dendrotweaks-0.4.2.dist-info → dendrotweaks-0.4.4.dist-info}/METADATA +1 -1
- {dendrotweaks-0.4.2.dist-info → dendrotweaks-0.4.4.dist-info}/RECORD +14 -14
- {dendrotweaks-0.4.2.dist-info → dendrotweaks-0.4.4.dist-info}/WHEEL +1 -1
- {dendrotweaks-0.4.2.dist-info → dendrotweaks-0.4.4.dist-info}/licenses/LICENSE +0 -0
- {dendrotweaks-0.4.2.dist-info → dendrotweaks-0.4.4.dist-info}/top_level.txt +0 -0
dendrotweaks/__init__.py
CHANGED
@@ -24,7 +24,8 @@ def get_somatic_data(model):
|
|
24
24
|
tuple
|
25
25
|
A tuple containing the voltage, time, time step, and injected current.
|
26
26
|
"""
|
27
|
-
|
27
|
+
soma = model.sec_tree.root
|
28
|
+
seg = soma(0.5)
|
28
29
|
iclamp = model.iclamps[seg]
|
29
30
|
|
30
31
|
v = np.array(model.simulator.recordings['v'][seg])
|
@@ -163,7 +164,8 @@ def detect_somatic_spikes(model, **kwargs):
|
|
163
164
|
Returns:
|
164
165
|
dict: A dictionary containing spike metrics.
|
165
166
|
"""
|
166
|
-
|
167
|
+
soma = model.sec_tree.root
|
168
|
+
seg = soma(0.5)
|
167
169
|
|
168
170
|
v = np.array(model.simulator.recordings['v'][seg])
|
169
171
|
t = np.array(model.simulator.t)
|
@@ -20,7 +20,7 @@ def constant(position, value=0):
|
|
20
20
|
The value of the constant function at the given position.
|
21
21
|
"""
|
22
22
|
if isinstance(position, ndarray):
|
23
|
-
return full_like(position, value)
|
23
|
+
return full_like(position, value, dtype=float)
|
24
24
|
else:
|
25
25
|
return value
|
26
26
|
|
@@ -42,7 +42,7 @@ def uniform(position, value=0):
|
|
42
42
|
The value of the constant function at the given position.
|
43
43
|
"""
|
44
44
|
if isinstance(position, ndarray):
|
45
|
-
return full_like(position, value)
|
45
|
+
return full_like(position, value, dtype=float)
|
46
46
|
else:
|
47
47
|
return value
|
48
48
|
|
dendrotweaks/model.py
CHANGED
@@ -473,6 +473,25 @@ class Model():
|
|
473
473
|
# SEGMENTATION
|
474
474
|
# ========================================================================
|
475
475
|
|
476
|
+
# TODO Make a context manager for this
|
477
|
+
def _temp_clear_stimuli(self):
|
478
|
+
"""
|
479
|
+
Temporarily save and clear stimuli.
|
480
|
+
"""
|
481
|
+
self.export_stimuli(file_name='_temp_stimuli')
|
482
|
+
self.remove_all_stimuli()
|
483
|
+
self.remove_all_recordings()
|
484
|
+
|
485
|
+
def _temp_reload_stimuli(self):
|
486
|
+
"""
|
487
|
+
Load stimuli from a temporary file and clean up.
|
488
|
+
"""
|
489
|
+
self.load_stimuli(file_name='_temp_stimuli')
|
490
|
+
for ext in ['json', 'csv']:
|
491
|
+
temp_path = self.path_manager.get_file_path('stimuli', '_temp_stimuli', extension=ext)
|
492
|
+
if os.path.exists(temp_path):
|
493
|
+
os.remove(temp_path)
|
494
|
+
|
476
495
|
def set_segmentation(self, d_lambda=0.1, f=100):
|
477
496
|
"""
|
478
497
|
Set the number of segments in each section based on the geometry.
|
@@ -486,23 +505,26 @@ class Model():
|
|
486
505
|
"""
|
487
506
|
self.d_lambda = d_lambda
|
488
507
|
|
508
|
+
# Temporarily save and clear stimuli
|
509
|
+
self._temp_clear_stimuli()
|
510
|
+
|
489
511
|
# Pre-distribute parameters needed for lambda_f calculation
|
490
512
|
for param_name in ['cm', 'Ra']:
|
491
513
|
self.distribute(param_name)
|
492
514
|
|
493
|
-
# Calculate lambda_f for each section
|
515
|
+
# Calculate lambda_f and set nseg for each section
|
494
516
|
for sec in self.sec_tree.sections:
|
495
517
|
lambda_f = calculate_lambda_f(sec.distances, sec.diameters, sec.Ra, sec.cm, f)
|
496
|
-
nseg = int((sec.L / (d_lambda * lambda_f) + 0.9) / 2) * 2 + 1
|
497
|
-
|
498
|
-
sec._nseg = nseg
|
499
|
-
sec._ref.nseg = nseg
|
500
|
-
# Rebuild the segment tree
|
501
|
-
self.seg_tree = create_segment_tree(self.sec_tree)
|
518
|
+
nseg = max(1, int((sec.L / (d_lambda * lambda_f) + 0.9) / 2) * 2 + 1)
|
519
|
+
sec._nseg = sec._ref.nseg = nseg
|
502
520
|
|
503
|
-
#
|
521
|
+
# Rebuild the segment tree and redistribute parameters
|
522
|
+
self.seg_tree = create_segment_tree(self.sec_tree)
|
504
523
|
self.distribute_all()
|
505
524
|
|
525
|
+
# Reload stimuli and clean up temporary files
|
526
|
+
self._temp_reload_stimuli()
|
527
|
+
|
506
528
|
|
507
529
|
# ========================================================================
|
508
530
|
# MECHANISMS
|
@@ -675,17 +697,26 @@ class Model():
|
|
675
697
|
|
676
698
|
def define_domain(self, domain_name: str, sections, distribute=True):
|
677
699
|
"""
|
678
|
-
Adds a new domain to the
|
679
|
-
the section tree graph.
|
700
|
+
Adds a new domain to the cell and ensures proper partitioning
|
701
|
+
of the section tree graph.
|
702
|
+
|
703
|
+
This method does not automatically insert mechanisms into the newly
|
704
|
+
created domain. It is the user's responsibility to insert mechanisms
|
705
|
+
into the domain after its creation. However, if the domain already
|
706
|
+
exists and is being extended, mechanisms will be inserted automatically
|
707
|
+
into the newly added sections.
|
680
708
|
|
681
709
|
Parameters
|
682
710
|
----------
|
683
711
|
domain_name : str
|
684
|
-
The name of the domain.
|
712
|
+
The name of the domain to be added or extended.
|
685
713
|
sections : list[Section] or Callable
|
686
|
-
The sections to include in the domain. If a callable is provided,
|
687
|
-
it should be a filter function applied to the list of all sections
|
688
|
-
|
714
|
+
The sections to include in the domain. If a callable is provided,
|
715
|
+
it should be a filter function applied to the list of all sections
|
716
|
+
in the model.
|
717
|
+
distribute : bool, optional
|
718
|
+
Whether to re-distribute the parameters after defining the domain.
|
719
|
+
Default is True.
|
689
720
|
"""
|
690
721
|
if isinstance(sections, Callable):
|
691
722
|
sections = self.get_sections(sections)
|
@@ -715,14 +746,22 @@ class Model():
|
|
715
746
|
sec.uninsert_mechanism(mech)
|
716
747
|
|
717
748
|
|
749
|
+
# Add sections to the new domain
|
718
750
|
for sec in sections_to_move:
|
719
751
|
domain.add_section(sec)
|
752
|
+
# Important: here we insert mechanisms only if we extend the domain,
|
753
|
+
# i.e. the domain already exists and has mechanisms.
|
754
|
+
# If the domain is new, we DO NOT insert mechanisms automatically
|
755
|
+
# and leave it to the user to do so.
|
720
756
|
for mech_name in self.domains_to_mechs.get(domain.name, set()):
|
721
757
|
mech = self.mechanisms[mech_name]
|
722
|
-
sec.insert_mechanism(mech
|
758
|
+
sec.insert_mechanism(mech)
|
723
759
|
|
724
760
|
self._remove_empty()
|
725
761
|
|
762
|
+
if distribute:
|
763
|
+
self.distribute_all()
|
764
|
+
|
726
765
|
|
727
766
|
def _add_domain_groups(self, domain_name):
|
728
767
|
"""
|
@@ -1024,7 +1063,6 @@ class Model():
|
|
1024
1063
|
distribution = Distribution(distr_type, **distr_params)
|
1025
1064
|
self.params[param_name][group_name] = distribution
|
1026
1065
|
|
1027
|
-
|
1028
1066
|
def distribute_all(self):
|
1029
1067
|
"""
|
1030
1068
|
Distribute all parameters to the segments.
|
@@ -1471,6 +1509,8 @@ class Model():
|
|
1471
1509
|
|
1472
1510
|
|
1473
1511
|
# Reinsert active mechanisms after creating the new domain
|
1512
|
+
# The new domain by default has no mechanisms. Here we re-insert the
|
1513
|
+
# exact same mechanisms as in the original domain of the root section.
|
1474
1514
|
for mech_name in inserted_mechs:
|
1475
1515
|
mech = self.mechanisms[mech_name]
|
1476
1516
|
root.insert_mechanism(mech)
|
@@ -1889,10 +1929,10 @@ class Model():
|
|
1889
1929
|
segments = [self.sec_tree.sections[sec_idx](loc)
|
1890
1930
|
for sec_idx, loc in zip(df_pop['sec_idx'], df_pop['loc'])]
|
1891
1931
|
|
1892
|
-
pop = Population(i,
|
1893
|
-
segments,
|
1894
|
-
pop_data['N'],
|
1895
|
-
|
1932
|
+
pop = Population(idx=i,
|
1933
|
+
segments=segments,
|
1934
|
+
N=pop_data['N'],
|
1935
|
+
syn_type=syn_type)
|
1896
1936
|
|
1897
1937
|
syn_locs = [(self.sec_tree.sections[sec_idx], loc) for sec_idx, loc in zip(df_pop['sec_idx'].tolist(), df_pop['loc'].tolist())]
|
1898
1938
|
|
@@ -11,6 +11,7 @@ from dendrotweaks.morphology.trees import Node, Tree
|
|
11
11
|
from dendrotweaks.morphology.domains import Domain
|
12
12
|
from dataclasses import dataclass, field
|
13
13
|
from bisect import bisect_left
|
14
|
+
from functools import cached_property
|
14
15
|
|
15
16
|
import warnings
|
16
17
|
|
@@ -271,11 +272,55 @@ class Section(Node):
|
|
271
272
|
seg_values = [seg.get_param_value(param_name) for seg in self.segments]
|
272
273
|
return round(np.mean(seg_values), 16)
|
273
274
|
|
275
|
+
@cached_property
|
276
|
+
def path_distance_to_root(self) -> float:
|
277
|
+
"""
|
278
|
+
Calculate the total distance from the section start to the root.
|
279
|
+
|
280
|
+
Returns
|
281
|
+
-------
|
282
|
+
float
|
283
|
+
The distance from the section start to the root.
|
284
|
+
"""
|
285
|
+
distance = 0
|
286
|
+
node = self.parent # Start from the parent node
|
287
|
+
|
288
|
+
if node is None:
|
289
|
+
return 0
|
290
|
+
|
291
|
+
while node.parent:
|
292
|
+
distance += node.length
|
293
|
+
node = node.parent
|
294
|
+
|
295
|
+
return distance
|
296
|
+
|
297
|
+
@cached_property
|
298
|
+
def path_distance_within_domain(self) -> float:
|
299
|
+
"""
|
300
|
+
Calculate the distance from the section start to the root within the same domain.
|
301
|
+
|
302
|
+
Returns
|
303
|
+
-------
|
304
|
+
float
|
305
|
+
The distance from the section start to the root within the same domain.
|
306
|
+
"""
|
307
|
+
distance = 0
|
308
|
+
node = self.parent # Start from the parent node
|
309
|
+
|
310
|
+
if node is None:
|
311
|
+
return 0
|
274
312
|
|
275
|
-
|
276
|
-
|
313
|
+
while node.parent:
|
314
|
+
if node.domain != self.domain:
|
315
|
+
break
|
316
|
+
distance += node.length
|
317
|
+
node = node.parent
|
318
|
+
|
319
|
+
return distance
|
320
|
+
|
321
|
+
def path_distance(self, relative_position: float = 0, within_domain: bool = False) -> float:
|
277
322
|
"""
|
278
|
-
|
323
|
+
Get the distance from the section to the root at a given relative position.
|
279
324
|
|
280
325
|
Parameters
|
281
326
|
----------
|
@@ -288,29 +333,16 @@ class Section(Node):
|
|
288
333
|
-------
|
289
334
|
float
|
290
335
|
The distance from the section to the root.
|
291
|
-
|
292
|
-
Important
|
293
|
-
---------
|
294
|
-
Assumes that we always attach the 0 end of the child.
|
295
336
|
"""
|
296
337
|
if not (0 <= relative_position <= 1):
|
297
338
|
raise ValueError('Relative position must be between 0 and 1.')
|
298
339
|
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
while node.parent:
|
340
|
+
if self.parent is None: # Soma section
|
341
|
+
# relative_position = abs(relative_position - 0.5)
|
342
|
+
return 0
|
304
343
|
|
305
|
-
|
306
|
-
|
307
|
-
if within_domain and node.parent.domain != node.domain:
|
308
|
-
break
|
309
|
-
|
310
|
-
node = node.parent
|
311
|
-
factor = 1
|
312
|
-
|
313
|
-
return distance
|
344
|
+
base_distance = self.path_distance_within_domain if within_domain else self.path_distance_to_root
|
345
|
+
return base_distance + relative_position * self.length
|
314
346
|
|
315
347
|
|
316
348
|
def disconnect_from_parent(self):
|
@@ -671,11 +703,13 @@ class NeuronSection(Section):
|
|
671
703
|
first_segment = self.segments[0]
|
672
704
|
parent = first_segment.parent
|
673
705
|
|
674
|
-
for
|
675
|
-
|
676
|
-
|
677
|
-
|
678
|
-
|
706
|
+
for seg in new_segments:
|
707
|
+
seg_tree.insert_node_before(seg, first_segment)
|
708
|
+
# for i, seg in enumerate(new_segments[:]):
|
709
|
+
# if i == 0:
|
710
|
+
# seg_tree.insert_node_before(seg, first_segment)
|
711
|
+
# else:
|
712
|
+
# seg_tree.insert_node_after(seg, new_segments[i-1])
|
679
713
|
|
680
714
|
for seg in old_segments:
|
681
715
|
seg_tree.remove_node(seg)
|
dendrotweaks/simulators.py
CHANGED
@@ -178,7 +178,7 @@ class NeuronSimulator(Simulator):
|
|
178
178
|
The location along the normalized section length to remove the recording from.
|
179
179
|
"""
|
180
180
|
seg = sec(loc)
|
181
|
-
if self._recordings[var]
|
181
|
+
if seg in self._recordings[var]:
|
182
182
|
self._recordings[var][seg] = None
|
183
183
|
self._recordings[var].pop(seg)
|
184
184
|
if not self._recordings[var]:
|
@@ -255,7 +255,7 @@ class Population():
|
|
255
255
|
'syn_type': [self.syn_type] * len(flat_synapses),
|
256
256
|
'name': [self.name] * len(flat_synapses),
|
257
257
|
'sec_idx': [syn.sec.idx for syn in flat_synapses],
|
258
|
-
'loc': [syn.loc for syn in flat_synapses],
|
258
|
+
'loc': [round(syn.loc, 8) for syn in flat_synapses],
|
259
259
|
}
|
260
260
|
|
261
261
|
|
@@ -1,16 +1,16 @@
|
|
1
|
-
dendrotweaks/__init__.py,sha256=
|
2
|
-
dendrotweaks/model.py,sha256=
|
1
|
+
dendrotweaks/__init__.py,sha256=Z2JpwnO9nBPrkR1w0u_6gWnCJetIhTx_27PehYlfTRM,384
|
2
|
+
dendrotweaks/model.py,sha256=IjmLrp-Ooh1WLdTb7Xg_9q2KUO7Xuokll26NQyVITlk,70965
|
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=OscZ4H6z9YiNloDtMAgpPx9n2e-9WJyLEWtcSD1YZR8,7411
|
6
6
|
dendrotweaks/utils.py,sha256=jaUJNb39Bsevg3WJByP56bO7CLj1wzlh-uGZl-lxi1I,7131
|
7
7
|
dendrotweaks/analysis/__init__.py,sha256=SEYpoQ5iXiQXyHB20-IAdDHYI-7CR5GYFXIwr-O05Ug,858
|
8
|
-
dendrotweaks/analysis/ephys_analysis.py,sha256=
|
8
|
+
dendrotweaks/analysis/ephys_analysis.py,sha256=PqT3aBCuxQbvdm9jnXjlTJ3R5n7_Vwp4fLrHGgtfoWw,14362
|
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
|
-
dendrotweaks/biophys/distributions.py,sha256=
|
11
|
+
dendrotweaks/biophys/distributions.py,sha256=XGczxBYJ0-vkIfXbfzvlIqlH9OpBuT9J95Kzyd2zL5A,10325
|
12
12
|
dendrotweaks/biophys/groups.py,sha256=Q4kBIqL1-piIgrpsVq6ojinAWHiEJ1GzMjSAQ7Ni_E8,3212
|
13
|
-
dendrotweaks/biophys/mechanisms.py,sha256=
|
13
|
+
dendrotweaks/biophys/mechanisms.py,sha256=IxKcyYftEbupJWPqGAV20Ox5a3uPbnXs3gYSlC3z-E0,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
|
16
16
|
dendrotweaks/biophys/default_mod/CaDyn.mod,sha256=gwc69K_rxu2w_mV7CnOSOnVaCMc8Z-MfdBFf6lAj4kg,1298
|
@@ -33,9 +33,9 @@ dendrotweaks/biophys/io/loader.py,sha256=Wv9ZkEDyA3MkCdV0sMeRnBffg2WAI7yTV3r6C41
|
|
33
33
|
dendrotweaks/biophys/io/parser.py,sha256=boT27lFrn5LYrJnkZFs0SwrZZrkSkwO8efqGPJ4Qj0I,17914
|
34
34
|
dendrotweaks/biophys/io/reader.py,sha256=JWm5WM9illvSfDkhWEmWBcj8Y7PSi8zeZX9j1ARUHVU,6576
|
35
35
|
dendrotweaks/morphology/__init__.py,sha256=JwXmSmdn9e_jqslITEdiU9kWvzxcxT9Aw_kUkXLbm5o,353
|
36
|
-
dendrotweaks/morphology/domains.py,sha256=
|
36
|
+
dendrotweaks/morphology/domains.py,sha256=l57KVR5eo1LlH_fCd1AOMiG_SsYLBPBTGQ5R78BHfdM,2545
|
37
37
|
dendrotweaks/morphology/point_trees.py,sha256=5dUPaQXYPdJbWoD3pFI2DV2XnuFRhB5d0wTBlfmmIeI,21600
|
38
|
-
dendrotweaks/morphology/sec_trees.py,sha256=
|
38
|
+
dendrotweaks/morphology/sec_trees.py,sha256=eKLC-yNhsn_rPdTE7w7p6STa1onYkBTGcpBKBpEWZUI,36957
|
39
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
|
@@ -47,10 +47,10 @@ dendrotweaks/morphology/reduce/reduce.py,sha256=5czZDrG3xsvHn3c_tbYhUOlXgST989-R
|
|
47
47
|
dendrotweaks/morphology/reduce/reduced_cylinder.py,sha256=jGJ4J-amukRr-3DPirVR5pzNO-6H7_sZF1N_X57ZGdw,5132
|
48
48
|
dendrotweaks/stimuli/__init__.py,sha256=bFfSEZhCVpwOVEBgLe65iiY3SdpjKPhyLemC1z5OX9I,153
|
49
49
|
dendrotweaks/stimuli/iclamps.py,sha256=NjkhhwZKJR1f_g3N9BVxMVoO9ubBk5WkQ6h9Bnf9xgA,1681
|
50
|
-
dendrotweaks/stimuli/populations.py,sha256=
|
50
|
+
dendrotweaks/stimuli/populations.py,sha256=vq2NUOaxaltUwlcT7wuCe8z1JWc8rk6HfPmfEd1kK68,8335
|
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.4.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
53
|
+
dendrotweaks-0.4.4.dist-info/METADATA,sha256=2eqzr1zuEY9kyPT0X0ggJ5m_lAqV2_sQKEtSX9TA9PQ,2740
|
54
|
+
dendrotweaks-0.4.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
55
|
+
dendrotweaks-0.4.4.dist-info/top_level.txt,sha256=OzT_2BSI5j5zxC447K6Y-0W-GHbued7iX-_hFGAKMxY,13
|
56
|
+
dendrotweaks-0.4.4.dist-info/RECORD,,
|
File without changes
|
File without changes
|