bluecellulab 2.6.49__tar.gz → 2.6.51__tar.gz
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.
Potentially problematic release.
This version of bluecellulab might be problematic. Click here for more details.
- {bluecellulab-2.6.49 → bluecellulab-2.6.51}/PKG-INFO +2 -1
- bluecellulab-2.6.51/bluecellulab/analysis/analysis.py +502 -0
- bluecellulab-2.6.51/bluecellulab/analysis/utils.py +7 -0
- {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/circuit/circuit_access/bluepy_circuit_access.py +1 -1
- {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/utils.py +26 -0
- {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/validation/validation.py +169 -55
- {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab.egg-info/PKG-INFO +2 -1
- {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab.egg-info/SOURCES.txt +1 -0
- {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab.egg-info/requires.txt +1 -0
- {bluecellulab-2.6.49 → bluecellulab-2.6.51}/pyproject.toml +1 -0
- bluecellulab-2.6.49/bluecellulab/analysis/analysis.py +0 -213
- {bluecellulab-2.6.49 → bluecellulab-2.6.51}/.compile_mod.sh +0 -0
- {bluecellulab-2.6.49 → bluecellulab-2.6.51}/.gitattributes +0 -0
- {bluecellulab-2.6.49 → bluecellulab-2.6.51}/.github/dependabot.yml +0 -0
- {bluecellulab-2.6.49 → bluecellulab-2.6.51}/.github/workflows/release.yml +0 -0
- {bluecellulab-2.6.49 → bluecellulab-2.6.51}/.github/workflows/test.yml +0 -0
- {bluecellulab-2.6.49 → bluecellulab-2.6.51}/.gitignore +0 -0
- {bluecellulab-2.6.49 → bluecellulab-2.6.51}/.gitlab-ci.yml +0 -0
- {bluecellulab-2.6.49 → bluecellulab-2.6.51}/.readthedocs.yml +0 -0
- {bluecellulab-2.6.49 → bluecellulab-2.6.51}/.zenodo.json +0 -0
- {bluecellulab-2.6.49 → bluecellulab-2.6.51}/AUTHORS.txt +0 -0
- {bluecellulab-2.6.49 → bluecellulab-2.6.51}/CHANGELOG.rst +0 -0
- {bluecellulab-2.6.49 → bluecellulab-2.6.51}/CITATION.cff +0 -0
- {bluecellulab-2.6.49 → bluecellulab-2.6.51}/CONTRIBUTING.rst +0 -0
- {bluecellulab-2.6.49 → bluecellulab-2.6.51}/LICENSE +0 -0
- {bluecellulab-2.6.49 → bluecellulab-2.6.51}/MANIFEST.in +0 -0
- {bluecellulab-2.6.49 → bluecellulab-2.6.51}/Makefile +0 -0
- {bluecellulab-2.6.49 → bluecellulab-2.6.51}/README.rst +0 -0
- {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/__init__.py +0 -0
- {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/analysis/__init__.py +0 -0
- {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/analysis/inject_sequence.py +0 -0
- {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/analysis/plotting.py +0 -0
- {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/cell/__init__.py +0 -0
- {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/cell/ballstick/__init__.py +0 -0
- {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/cell/ballstick/emodel.hoc +0 -0
- {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/cell/ballstick/morphology.asc +0 -0
- {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/cell/cell_dict.py +0 -0
- {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/cell/core.py +0 -0
- {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/cell/injector.py +0 -0
- {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/cell/plotting.py +0 -0
- {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/cell/random.py +0 -0
- {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/cell/recording.py +0 -0
- {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/cell/section_distance.py +0 -0
- {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/cell/serialized_sections.py +0 -0
- {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/cell/sonata_proxy.py +0 -0
- {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/cell/stimuli_generator.py +0 -0
- {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/cell/template.py +0 -0
- {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/circuit/__init__.py +0 -0
- {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/circuit/circuit_access/__init__.py +0 -0
- {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/circuit/circuit_access/definition.py +0 -0
- {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/circuit/circuit_access/sonata_circuit_access.py +0 -0
- {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/circuit/config/__init__.py +0 -0
- {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/circuit/config/bluepy_simulation_config.py +0 -0
- {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/circuit/config/definition.py +0 -0
- {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/circuit/config/sections.py +0 -0
- {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/circuit/config/sonata_simulation_config.py +0 -0
- {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/circuit/format.py +0 -0
- {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/circuit/iotools.py +0 -0
- {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/circuit/node_id.py +0 -0
- {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/circuit/simulation_access.py +0 -0
- {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/circuit/synapse_properties.py +0 -0
- {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/circuit/validate.py +0 -0
- {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/circuit_simulation.py +0 -0
- {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/connection.py +0 -0
- {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/dendrogram.py +0 -0
- {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/exceptions.py +0 -0
- {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/graph.py +0 -0
- {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/hoc/Cell.hoc +0 -0
- {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/hoc/RNGSettings.hoc +0 -0
- {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/hoc/TDistFunc.hoc +0 -0
- {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/hoc/TStim.hoc +0 -0
- {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/hoc/fileUtils.hoc +0 -0
- {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/importer.py +0 -0
- {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/neuron_interpreter.py +0 -0
- {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/plotwindow.py +0 -0
- {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/psection.py +0 -0
- {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/psegment.py +0 -0
- {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/rngsettings.py +0 -0
- {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/simulation/__init__.py +0 -0
- {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/simulation/neuron_globals.py +0 -0
- {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/simulation/parallel.py +0 -0
- {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/simulation/simulation.py +0 -0
- {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/stimulus/__init__.py +0 -0
- {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/stimulus/circuit_stimulus_definitions.py +0 -0
- {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/stimulus/factory.py +0 -0
- {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/stimulus/stimulus.py +0 -0
- {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/synapse/__init__.py +0 -0
- {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/synapse/synapse_factory.py +0 -0
- {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/synapse/synapse_types.py +0 -0
- {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/tools.py +0 -0
- {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/type_aliases.py +0 -0
- {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab/verbosity.py +0 -0
- {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab.egg-info/dependency_links.txt +0 -0
- {bluecellulab-2.6.49 → bluecellulab-2.6.51}/bluecellulab.egg-info/top_level.txt +0 -0
- {bluecellulab-2.6.49 → bluecellulab-2.6.51}/docs/Makefile +0 -0
- {bluecellulab-2.6.49 → bluecellulab-2.6.51}/docs/images/voltage-readme.png +0 -0
- {bluecellulab-2.6.49 → bluecellulab-2.6.51}/docs/make.bat +0 -0
- {bluecellulab-2.6.49 → bluecellulab-2.6.51}/docs/requirements_docs.txt +0 -0
- {bluecellulab-2.6.49 → bluecellulab-2.6.51}/docs/source/_static/.gitkeep +0 -0
- {bluecellulab-2.6.49 → bluecellulab-2.6.51}/docs/source/api.rst +0 -0
- {bluecellulab-2.6.49 → bluecellulab-2.6.51}/docs/source/changelog.rst +0 -0
- {bluecellulab-2.6.49 → bluecellulab-2.6.51}/docs/source/compiling-mechanisms.rst +0 -0
- {bluecellulab-2.6.49 → bluecellulab-2.6.51}/docs/source/conf.py +0 -0
- {bluecellulab-2.6.49 → bluecellulab-2.6.51}/docs/source/contributing.rst +0 -0
- {bluecellulab-2.6.49 → bluecellulab-2.6.51}/docs/source/index.rst +0 -0
- {bluecellulab-2.6.49 → bluecellulab-2.6.51}/docs/source/list_of_stim.rst +0 -0
- {bluecellulab-2.6.49 → bluecellulab-2.6.51}/docs/source/logo/BlueCelluLabBanner.jpg +0 -0
- {bluecellulab-2.6.49 → bluecellulab-2.6.51}/setup.cfg +0 -0
- {bluecellulab-2.6.49 → bluecellulab-2.6.51}/tox.ini +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: bluecellulab
|
|
3
|
-
Version: 2.6.
|
|
3
|
+
Version: 2.6.51
|
|
4
4
|
Summary: Biologically detailed neural network simulations and analysis.
|
|
5
5
|
Author: Blue Brain Project, EPFL
|
|
6
6
|
License: Apache2.0
|
|
@@ -27,6 +27,7 @@ Requires-Dist: pydantic<3.0.0,>=2.5.2
|
|
|
27
27
|
Requires-Dist: typing-extensions>=4.8.0
|
|
28
28
|
Requires-Dist: networkx>=3.1
|
|
29
29
|
Requires-Dist: h5py>=3.8.0
|
|
30
|
+
Requires-Dist: seaborn
|
|
30
31
|
Dynamic: license-file
|
|
31
32
|
|
|
32
33
|
|banner|
|
|
@@ -0,0 +1,502 @@
|
|
|
1
|
+
"""Module for analyzing cell simulation results."""
|
|
2
|
+
try:
|
|
3
|
+
import efel
|
|
4
|
+
except ImportError:
|
|
5
|
+
efel = None
|
|
6
|
+
from itertools import islice
|
|
7
|
+
from itertools import repeat
|
|
8
|
+
import logging
|
|
9
|
+
from matplotlib.collections import LineCollection
|
|
10
|
+
import matplotlib.pyplot as plt
|
|
11
|
+
from multiprocessing import Pool
|
|
12
|
+
import neuron
|
|
13
|
+
import numpy as np
|
|
14
|
+
import pathlib
|
|
15
|
+
import seaborn as sns
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
from bluecellulab import Cell
|
|
19
|
+
from bluecellulab.analysis.inject_sequence import run_stimulus
|
|
20
|
+
from bluecellulab.analysis.plotting import plot_iv_curve, plot_fi_curve
|
|
21
|
+
from bluecellulab.analysis.utils import exp_decay
|
|
22
|
+
from bluecellulab.simulation import Simulation
|
|
23
|
+
from bluecellulab.stimulus import StimulusFactory
|
|
24
|
+
from bluecellulab.stimulus.circuit_stimulus_definitions import Hyperpolarizing
|
|
25
|
+
from bluecellulab.tools import calculate_rheobase
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
logger = logging.getLogger(__name__)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def compute_plot_iv_curve(cell,
|
|
32
|
+
injecting_section="soma[0]",
|
|
33
|
+
injecting_segment=0.5,
|
|
34
|
+
recording_section="soma[0]",
|
|
35
|
+
recording_segment=0.5,
|
|
36
|
+
stim_start=100.0,
|
|
37
|
+
duration=500.0,
|
|
38
|
+
post_delay=100.0,
|
|
39
|
+
threshold_voltage=-20,
|
|
40
|
+
nb_bins=11,
|
|
41
|
+
rheobase=None,
|
|
42
|
+
show_figure=True,
|
|
43
|
+
save_figure=False,
|
|
44
|
+
output_dir="./",
|
|
45
|
+
output_fname="iv_curve.pdf"):
|
|
46
|
+
"""Compute and plot the Current-Voltage (I-V) curve for a given cell by
|
|
47
|
+
injecting a range of currents.
|
|
48
|
+
|
|
49
|
+
This function evaluates the relationship between the injected current amplitude and the resulting
|
|
50
|
+
steady-state membrane potential of a neuronal cell model. Currents are injected at a specified section
|
|
51
|
+
and segment, and the steady-state voltage at the recording location is used to construct the I-V curve.
|
|
52
|
+
|
|
53
|
+
Args:
|
|
54
|
+
cell (bluecellulab.cell.Cell): The initialized BlueCelluLab cell model.
|
|
55
|
+
injecting_section (str, optional): The name of the section where the stimulus is injected.
|
|
56
|
+
Default is "soma[0]".
|
|
57
|
+
injecting_segment (float, optional): The position along the injecting section (0.0 to 1.0)
|
|
58
|
+
where the stimulus is applied. Default is 0.5.
|
|
59
|
+
recording_section (str, optional): The name of the section where the voltage is recorded.
|
|
60
|
+
Default is "soma[0]".
|
|
61
|
+
recording_segment (float, optional): The position along the recording section (0.0 to 1.0)
|
|
62
|
+
where the voltage is recorded. Default is 0.5.
|
|
63
|
+
stim_start (float, optional): The start time of the current injection (in ms). Default is 100.0 ms.
|
|
64
|
+
duration (float, optional): The duration of the current injection (in ms). Default is 500.0 ms.
|
|
65
|
+
post_delay (float, optional): The delay after the stimulation ends before the simulation stops
|
|
66
|
+
(in ms). Default is 100.0 ms.
|
|
67
|
+
threshold_voltage (float, optional): The voltage threshold (in mV) for detecting a steady-state
|
|
68
|
+
response. Default is -20 mV.
|
|
69
|
+
nb_bins (int, optional): The number of discrete current levels between 0 and the maximum current.
|
|
70
|
+
Default is 11.
|
|
71
|
+
rheobase (float, optional): The rheobase current (in nA) for the cell. If not provided, it will
|
|
72
|
+
be calculated using the `calculate_rheobase` function.
|
|
73
|
+
show_figure (bool): Whether to display the figure. Default is True.
|
|
74
|
+
save_figure (bool): Whether to save the figure. Default is False.
|
|
75
|
+
output_dir (str): The directory to save the figure if save_figure is True. Default is "./".
|
|
76
|
+
output_fname (str): The filename to save the figure as if save_figure is True. Default is "iv_curve.png".
|
|
77
|
+
|
|
78
|
+
Returns:
|
|
79
|
+
tuple: A tuple containing:
|
|
80
|
+
- list_amp (np.ndarray): The injected current amplitudes (nA).
|
|
81
|
+
- steady_states (np.ndarray): The corresponding steady-state voltages (mV) recorded at the
|
|
82
|
+
specified location.
|
|
83
|
+
|
|
84
|
+
Raises:
|
|
85
|
+
ValueError: If the cell object is invalid, the specified sections/segments are not found, or if
|
|
86
|
+
the simulation results are inconsistent.
|
|
87
|
+
"""
|
|
88
|
+
if rheobase is None:
|
|
89
|
+
rheobase = calculate_rheobase(cell=cell, section=injecting_section, segx=injecting_segment)
|
|
90
|
+
|
|
91
|
+
list_amp = np.linspace(rheobase - 2, rheobase - 0.1, nb_bins) # [nA]
|
|
92
|
+
|
|
93
|
+
# inject step current and record voltage response
|
|
94
|
+
stim_factory = StimulusFactory(dt=0.1)
|
|
95
|
+
steps = [
|
|
96
|
+
stim_factory.step(pre_delay=stim_start, duration=duration, post_delay=post_delay, amplitude=amp)
|
|
97
|
+
for amp in list_amp
|
|
98
|
+
]
|
|
99
|
+
|
|
100
|
+
with Pool(len(steps)) as p:
|
|
101
|
+
recordings = p.starmap(
|
|
102
|
+
run_stimulus,
|
|
103
|
+
zip(
|
|
104
|
+
repeat(cell.template_params),
|
|
105
|
+
steps,
|
|
106
|
+
repeat(injecting_section),
|
|
107
|
+
repeat(injecting_segment),
|
|
108
|
+
repeat(True), # cvode
|
|
109
|
+
repeat(True), # add_hypamp
|
|
110
|
+
repeat(recording_section),
|
|
111
|
+
repeat(recording_segment),
|
|
112
|
+
)
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
steady_states = []
|
|
116
|
+
# compute steady state response
|
|
117
|
+
efel.set_setting('Threshold', threshold_voltage)
|
|
118
|
+
for recording in recordings:
|
|
119
|
+
trace = {
|
|
120
|
+
'T': recording.time,
|
|
121
|
+
'V': recording.voltage,
|
|
122
|
+
'stim_start': [stim_start],
|
|
123
|
+
'stim_end': [stim_start + duration]
|
|
124
|
+
}
|
|
125
|
+
features_results = efel.get_feature_values([trace], ['steady_state_voltage_stimend'])
|
|
126
|
+
steady_state = features_results[0]['steady_state_voltage_stimend'][0]
|
|
127
|
+
steady_states.append(steady_state)
|
|
128
|
+
|
|
129
|
+
plot_iv_curve(list_amp,
|
|
130
|
+
steady_states,
|
|
131
|
+
injecting_section=injecting_section,
|
|
132
|
+
injecting_segment=injecting_segment,
|
|
133
|
+
recording_section=recording_section,
|
|
134
|
+
recording_segment=recording_segment,
|
|
135
|
+
show_figure=show_figure,
|
|
136
|
+
save_figure=save_figure,
|
|
137
|
+
output_dir=output_dir,
|
|
138
|
+
output_fname=output_fname)
|
|
139
|
+
|
|
140
|
+
return np.array(list_amp), np.array(steady_states)
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def compute_plot_fi_curve(cell,
|
|
144
|
+
injecting_section="soma[0]",
|
|
145
|
+
injecting_segment=0.5,
|
|
146
|
+
recording_section="soma[0]",
|
|
147
|
+
recording_segment=0.5,
|
|
148
|
+
stim_start=100.0,
|
|
149
|
+
duration=500.0,
|
|
150
|
+
post_delay=100.0,
|
|
151
|
+
max_current=0.8,
|
|
152
|
+
threshold_voltage=-20,
|
|
153
|
+
nb_bins=11,
|
|
154
|
+
rheobase=None,
|
|
155
|
+
show_figure=True,
|
|
156
|
+
save_figure=False,
|
|
157
|
+
output_dir="./",
|
|
158
|
+
output_fname="fi_curve.pdf"):
|
|
159
|
+
"""Compute and plot the Frequency-Current (F-I) curve for a given cell by
|
|
160
|
+
injecting a range of currents.
|
|
161
|
+
|
|
162
|
+
This function evaluates the relationship between injected current amplitude and the firing rate
|
|
163
|
+
of a neuronal cell model. Currents are injected at a specified section and segment, and the number
|
|
164
|
+
of spikes recorded in the specified recording location is used to construct the F-I curve.
|
|
165
|
+
|
|
166
|
+
Args:
|
|
167
|
+
cell (bluecellulab.cell.Cell): The initialized BlueCelluLab cell model.
|
|
168
|
+
injecting_section (str, optional): The name of the section where the stimulus is injected.
|
|
169
|
+
Default is "soma[0]".
|
|
170
|
+
injecting_segment (float, optional): The position along the injecting section (0.0 to 1.0)
|
|
171
|
+
where the stimulus is applied. Default is 0.5.
|
|
172
|
+
recording_section (str, optional): The name of the section where spikes are recorded.
|
|
173
|
+
Default is "soma[0]".
|
|
174
|
+
recording_segment (float, optional): The position along the recording section (0.0 to 1.0)
|
|
175
|
+
where spikes are recorded. Default is 0.5.
|
|
176
|
+
stim_start (float, optional): The start time of the current injection (in ms). Default is 100.0 ms.
|
|
177
|
+
duration (float, optional): The duration of the current injection (in ms). Default is 500.0 ms.
|
|
178
|
+
post_delay (float, optional): The delay after the stimulation ends before the simulation stops
|
|
179
|
+
(in ms). Default is 100.0 ms.
|
|
180
|
+
max_current (float, optional): The maximum amplitude of the injected current (in nA).
|
|
181
|
+
Default is 0.8 nA.
|
|
182
|
+
threshold_voltage (float, optional): The voltage threshold (in mV) for detecting a steady-state
|
|
183
|
+
response. Default is -20 mV.
|
|
184
|
+
nb_bins (int, optional): The number of discrete current levels between 0 and `max_current`.
|
|
185
|
+
Default is 11.
|
|
186
|
+
rheobase (float, optional): The rheobase current (in nA) for the cell. If not provided, it will
|
|
187
|
+
be calculated using the `calculate_rheobase` function.
|
|
188
|
+
show_figure (bool): Whether to display the figure. Default is True.
|
|
189
|
+
save_figure (bool): Whether to save the figure. Default is False.
|
|
190
|
+
output_dir (str): The directory to save the figure if save_figure is True. Default is "./".
|
|
191
|
+
output_fname (str): The filename to save the figure as if save_figure is True. Default is "iv_curve.png".
|
|
192
|
+
|
|
193
|
+
Returns:
|
|
194
|
+
tuple: A tuple containing:
|
|
195
|
+
- list_amp (np.ndarray): The injected current amplitudes (nA).
|
|
196
|
+
- spike_count (np.ndarray): The corresponding spike counts for each current amplitude.
|
|
197
|
+
|
|
198
|
+
Raises:
|
|
199
|
+
ValueError: If the cell object is invalid or the specified sections/segments are not found.
|
|
200
|
+
"""
|
|
201
|
+
if rheobase is None:
|
|
202
|
+
rheobase = calculate_rheobase(cell=cell, section=injecting_section, segx=injecting_segment)
|
|
203
|
+
|
|
204
|
+
list_amp = np.linspace(rheobase, max_current, nb_bins) # [nA]
|
|
205
|
+
stim_factory = StimulusFactory(dt=0.1)
|
|
206
|
+
steps = [
|
|
207
|
+
stim_factory.step(pre_delay=stim_start, duration=duration, post_delay=post_delay, amplitude=amp)
|
|
208
|
+
for amp in list_amp
|
|
209
|
+
]
|
|
210
|
+
|
|
211
|
+
with Pool(len(steps)) as p:
|
|
212
|
+
recordings = p.starmap(
|
|
213
|
+
run_stimulus,
|
|
214
|
+
zip(
|
|
215
|
+
repeat(cell.template_params),
|
|
216
|
+
steps,
|
|
217
|
+
repeat(injecting_section),
|
|
218
|
+
repeat(injecting_segment),
|
|
219
|
+
repeat(True), # cvode
|
|
220
|
+
repeat(True), # add_hypamp
|
|
221
|
+
repeat(recording_section),
|
|
222
|
+
repeat(recording_segment),
|
|
223
|
+
repeat(True), # enable_spike_detection
|
|
224
|
+
repeat(threshold_voltage), # threshold_spike_detection
|
|
225
|
+
)
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
spike_count = [len(recording.spike) for recording in recordings]
|
|
229
|
+
|
|
230
|
+
plot_fi_curve(list_amp,
|
|
231
|
+
spike_count,
|
|
232
|
+
injecting_section=injecting_section,
|
|
233
|
+
injecting_segment=injecting_segment,
|
|
234
|
+
recording_section=recording_section,
|
|
235
|
+
recording_segment=recording_segment,
|
|
236
|
+
show_figure=show_figure,
|
|
237
|
+
save_figure=save_figure,
|
|
238
|
+
output_dir=output_dir,
|
|
239
|
+
output_fname=output_fname)
|
|
240
|
+
|
|
241
|
+
return np.array(list_amp), np.array(spike_count)
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
class BPAP:
|
|
245
|
+
# taken from the examples
|
|
246
|
+
|
|
247
|
+
def __init__(self, cell: Cell) -> None:
|
|
248
|
+
self.cell = cell
|
|
249
|
+
self.dt = 0.025
|
|
250
|
+
self.stim_start = 1000
|
|
251
|
+
self.stim_duration = 3
|
|
252
|
+
self.basal_cmap = sns.color_palette("crest", as_cmap=True)
|
|
253
|
+
self.apical_cmap = sns.color_palette("YlOrBr_r", as_cmap=True)
|
|
254
|
+
|
|
255
|
+
@property
|
|
256
|
+
def start_index(self) -> int:
|
|
257
|
+
"""Get the index of the start of the stimulus."""
|
|
258
|
+
return int(self.stim_start / self.dt)
|
|
259
|
+
|
|
260
|
+
@property
|
|
261
|
+
def end_index(self) -> int:
|
|
262
|
+
"""Get the index of the end of the stimulus."""
|
|
263
|
+
return int((self.stim_start + self.stim_duration) / self.dt)
|
|
264
|
+
|
|
265
|
+
def get_recordings(self):
|
|
266
|
+
"""Get the soma, basal and apical recordings."""
|
|
267
|
+
all_recordings = self.cell.get_allsections_voltagerecordings()
|
|
268
|
+
soma_rec = None
|
|
269
|
+
dend_rec = {}
|
|
270
|
+
apic_rec = {}
|
|
271
|
+
for key, value in all_recordings.items():
|
|
272
|
+
if "soma" in key:
|
|
273
|
+
soma_rec = value
|
|
274
|
+
elif "dend" in key:
|
|
275
|
+
dend_rec[key] = value
|
|
276
|
+
elif "apic" in key:
|
|
277
|
+
apic_rec[key] = value
|
|
278
|
+
|
|
279
|
+
return soma_rec, dend_rec, apic_rec
|
|
280
|
+
|
|
281
|
+
def run(self, duration: float, amplitude: float) -> None:
|
|
282
|
+
"""Apply depolarization and hyperpolarization at the same time."""
|
|
283
|
+
sim = Simulation()
|
|
284
|
+
sim.add_cell(self.cell)
|
|
285
|
+
self.cell.add_allsections_voltagerecordings()
|
|
286
|
+
self.cell.add_step(start_time=self.stim_start, stop_time=self.stim_start + self.stim_duration, level=amplitude)
|
|
287
|
+
hyperpolarizing = Hyperpolarizing("single-cell", delay=0, duration=duration)
|
|
288
|
+
self.cell.add_replay_hypamp(hyperpolarizing)
|
|
289
|
+
sim.run(duration, dt=self.dt, cvode=False)
|
|
290
|
+
|
|
291
|
+
def amplitudes(self, recs) -> list[float]:
|
|
292
|
+
"""Return amplitude across given sections."""
|
|
293
|
+
efel_feature_name = "maximum_voltage_from_voltagebase"
|
|
294
|
+
traces = [
|
|
295
|
+
{
|
|
296
|
+
'T': self.cell.get_time(),
|
|
297
|
+
'V': rec,
|
|
298
|
+
'stim_start': [self.stim_start],
|
|
299
|
+
'stim_end': [self.stim_start + self.stim_duration]
|
|
300
|
+
}
|
|
301
|
+
for rec in recs.values()
|
|
302
|
+
]
|
|
303
|
+
features_results = efel.get_feature_values(traces, [efel_feature_name])
|
|
304
|
+
amps = [feat_res[efel_feature_name][0] for feat_res in features_results]
|
|
305
|
+
|
|
306
|
+
return amps
|
|
307
|
+
|
|
308
|
+
def distances_to_soma(self, recs) -> list[float]:
|
|
309
|
+
"""Return the distance to the soma for each section."""
|
|
310
|
+
res = []
|
|
311
|
+
soma = self.cell.soma
|
|
312
|
+
for key in recs.keys():
|
|
313
|
+
section_name = key.rsplit(".")[-1].split("[")[0] # e.g. "dend"
|
|
314
|
+
section_idx = int(key.rsplit(".")[-1].split("[")[1].split("]")[0]) # e.g. 0
|
|
315
|
+
attribute_value = getattr(self.cell.cell.getCell(), section_name)
|
|
316
|
+
section = next(islice(attribute_value, section_idx, None))
|
|
317
|
+
# section e.g. cADpyr_L2TPC_bluecellulab_x[0].dend[0]
|
|
318
|
+
res.append(neuron.h.distance(soma(0.5), section(0.5)))
|
|
319
|
+
return res
|
|
320
|
+
|
|
321
|
+
def get_amplitudes_and_distances(self):
|
|
322
|
+
soma_rec, dend_rec, apic_rec = self.get_recordings()
|
|
323
|
+
soma_amp = self.amplitudes({"soma": soma_rec})[0]
|
|
324
|
+
dend_amps = None
|
|
325
|
+
dend_dist = None
|
|
326
|
+
apic_amps = None
|
|
327
|
+
apic_dist = None
|
|
328
|
+
if dend_rec:
|
|
329
|
+
dend_amps = self.amplitudes(dend_rec)
|
|
330
|
+
dend_dist = self.distances_to_soma(dend_rec)
|
|
331
|
+
if apic_rec:
|
|
332
|
+
apic_amps = self.amplitudes(apic_rec)
|
|
333
|
+
apic_dist = self.distances_to_soma(apic_rec)
|
|
334
|
+
|
|
335
|
+
return soma_amp, dend_amps, dend_dist, apic_amps, apic_dist
|
|
336
|
+
|
|
337
|
+
def fit(self, soma_amp, dend_amps, dend_dist, apic_amps, apic_dist):
|
|
338
|
+
"""Fit the amplitudes vs distances to an exponential decay function."""
|
|
339
|
+
from scipy.optimize import curve_fit
|
|
340
|
+
|
|
341
|
+
popt_dend = None
|
|
342
|
+
if dend_amps and dend_dist:
|
|
343
|
+
dist = [0] + dend_dist # add soma distance
|
|
344
|
+
amps = [soma_amp] + dend_amps # add soma amplitude
|
|
345
|
+
popt_dend, _ = curve_fit(exp_decay, dist, amps)
|
|
346
|
+
|
|
347
|
+
popt_apic = None
|
|
348
|
+
if apic_amps and apic_dist:
|
|
349
|
+
dist = [0] + apic_dist # add soma distance
|
|
350
|
+
amps = [soma_amp] + apic_amps # add soma amplitude
|
|
351
|
+
popt_apic, _ = curve_fit(exp_decay, dist, amps)
|
|
352
|
+
|
|
353
|
+
return popt_dend, popt_apic
|
|
354
|
+
|
|
355
|
+
def validate(self, soma_amp, dend_amps, dend_dist, apic_amps, apic_dist):
|
|
356
|
+
"""Check that the exponential fit is decaying."""
|
|
357
|
+
validated = True
|
|
358
|
+
notes = ""
|
|
359
|
+
popt_dend, popt_apic = self.fit(soma_amp, dend_amps, dend_dist, apic_amps, apic_dist)
|
|
360
|
+
if popt_dend is None:
|
|
361
|
+
logger.debug("No dendritic recordings found.")
|
|
362
|
+
notes += "No dendritic recordings found.\n"
|
|
363
|
+
elif popt_dend[1] <= 0:
|
|
364
|
+
logger.debug("Dendritic fit is not decaying.")
|
|
365
|
+
validated = False
|
|
366
|
+
notes += "Dendritic fit is not decaying.\n"
|
|
367
|
+
else:
|
|
368
|
+
notes += "Dendritic validation passed: dendritic amplitude is decaying with distance relative to soma.\n"
|
|
369
|
+
if popt_apic is None:
|
|
370
|
+
logger.debug("No apical recordings found.")
|
|
371
|
+
notes += "No apical recordings found.\n"
|
|
372
|
+
elif popt_apic[1] <= 0:
|
|
373
|
+
logger.debug("Apical fit is not decaying.")
|
|
374
|
+
validated = False
|
|
375
|
+
notes += "Apical fit is not decaying.\n"
|
|
376
|
+
else:
|
|
377
|
+
notes += "Apical validation passed: apical amplitude is decaying with distance relative to soma.\n"
|
|
378
|
+
|
|
379
|
+
return validated, notes
|
|
380
|
+
|
|
381
|
+
def plot_amp_vs_dist(
|
|
382
|
+
self,
|
|
383
|
+
soma_amp,
|
|
384
|
+
dend_amps,
|
|
385
|
+
dend_dist,
|
|
386
|
+
apic_amps,
|
|
387
|
+
apic_dist,
|
|
388
|
+
show_figure=True,
|
|
389
|
+
save_figure=False,
|
|
390
|
+
output_dir="./",
|
|
391
|
+
output_fname="bpap.pdf",
|
|
392
|
+
):
|
|
393
|
+
"""Plot the results of the BPAP analysis."""
|
|
394
|
+
popt_dend, popt_apic = self.fit(soma_amp, dend_amps, dend_dist, apic_amps, apic_dist)
|
|
395
|
+
|
|
396
|
+
outpath = pathlib.Path(output_dir) / output_fname
|
|
397
|
+
fig, ax1 = plt.subplots(figsize=(10, 6))
|
|
398
|
+
ax1.scatter([0], [soma_amp], marker="^", color='black', label='Soma')
|
|
399
|
+
if dend_amps and dend_dist:
|
|
400
|
+
ax1.scatter(
|
|
401
|
+
dend_dist,
|
|
402
|
+
dend_amps,
|
|
403
|
+
c=dend_dist,
|
|
404
|
+
cmap=self.basal_cmap,
|
|
405
|
+
label='Basal Dendrites',
|
|
406
|
+
)
|
|
407
|
+
if popt_dend is not None:
|
|
408
|
+
x = np.linspace(0, max(dend_dist), 100)
|
|
409
|
+
y = exp_decay(x, *popt_dend)
|
|
410
|
+
ax1.plot(x, y, color='darkgreen', linestyle='--', label='Basal Dendritic Fit')
|
|
411
|
+
if apic_amps and apic_dist:
|
|
412
|
+
ax1.scatter(
|
|
413
|
+
apic_dist,
|
|
414
|
+
apic_amps,
|
|
415
|
+
c=apic_dist,
|
|
416
|
+
cmap=self.apical_cmap,
|
|
417
|
+
label='Apical Dendrites'
|
|
418
|
+
)
|
|
419
|
+
if popt_apic is not None:
|
|
420
|
+
x = np.linspace(0, max(apic_dist), 100)
|
|
421
|
+
y = exp_decay(x, *popt_apic)
|
|
422
|
+
ax1.plot(x, y, color='goldenrod', linestyle='--', label='Apical Fit')
|
|
423
|
+
ax1.set_xlabel('Distance to Soma (um)')
|
|
424
|
+
ax1.set_ylabel('Amplitude (mV)')
|
|
425
|
+
ax1.legend()
|
|
426
|
+
fig.suptitle('Back-propagating Action Potential Analysis')
|
|
427
|
+
fig.tight_layout()
|
|
428
|
+
if save_figure:
|
|
429
|
+
fig.savefig(outpath)
|
|
430
|
+
if show_figure:
|
|
431
|
+
plt.show()
|
|
432
|
+
|
|
433
|
+
return outpath
|
|
434
|
+
|
|
435
|
+
def plot_one_axis_recordings(self, fig, ax, rec_list, dist, cmap):
|
|
436
|
+
"""Plot the soma and dendritic recordings on one axis.
|
|
437
|
+
|
|
438
|
+
Args:
|
|
439
|
+
fig (matplotlib.figure.Figure): The figure to plot on.
|
|
440
|
+
ax (matplotlib.axes.Axes): The axis to plot on.
|
|
441
|
+
rec_list (list): List of recordings to plot.
|
|
442
|
+
dist (list): List of distances from the soma for each recording.
|
|
443
|
+
cmap (matplotlib.colors.Colormap): Colormap to use for the recordings.
|
|
444
|
+
"""
|
|
445
|
+
time = self.cell.get_time()
|
|
446
|
+
line_collection = LineCollection(
|
|
447
|
+
[np.column_stack([time, rec]) for rec in rec_list],
|
|
448
|
+
array=dist,
|
|
449
|
+
cmap=cmap,
|
|
450
|
+
)
|
|
451
|
+
ax.set_xlim(
|
|
452
|
+
self.stim_start - 0.1,
|
|
453
|
+
self.stim_start + 30
|
|
454
|
+
)
|
|
455
|
+
ax.set_ylim(
|
|
456
|
+
min([min(rec[self.start_index:]) for rec in rec_list]) - 2,
|
|
457
|
+
max([max(rec[self.start_index:]) for rec in rec_list]) + 2
|
|
458
|
+
)
|
|
459
|
+
ax.add_collection(line_collection)
|
|
460
|
+
fig.colorbar(line_collection, label="soma distance (um)", ax=ax)
|
|
461
|
+
|
|
462
|
+
def plot_recordings(
|
|
463
|
+
self,
|
|
464
|
+
show_figure=True,
|
|
465
|
+
save_figure=False,
|
|
466
|
+
output_dir="./",
|
|
467
|
+
output_fname="bpap_recordings.pdf",
|
|
468
|
+
):
|
|
469
|
+
"""Plot the recordings from all dendrites."""
|
|
470
|
+
soma_rec, dend_rec, apic_rec = self.get_recordings()
|
|
471
|
+
dend_dist = []
|
|
472
|
+
apic_dist = []
|
|
473
|
+
if dend_rec:
|
|
474
|
+
dend_dist = self.distances_to_soma(dend_rec)
|
|
475
|
+
if apic_rec:
|
|
476
|
+
apic_dist = self.distances_to_soma(apic_rec)
|
|
477
|
+
# add soma_rec to the lists
|
|
478
|
+
dend_rec_list = [soma_rec] + list(dend_rec.values())
|
|
479
|
+
dend_dist = [0] + dend_dist
|
|
480
|
+
apic_rec_list = [soma_rec] + list(apic_rec.values())
|
|
481
|
+
apic_dist = [0] + apic_dist
|
|
482
|
+
|
|
483
|
+
outpath = pathlib.Path(output_dir) / output_fname
|
|
484
|
+
fig, (ax1, ax2) = plt.subplots(figsize=(10, 12), nrows=2, sharex=True)
|
|
485
|
+
|
|
486
|
+
self.plot_one_axis_recordings(fig, ax1, dend_rec_list, dend_dist, self.basal_cmap)
|
|
487
|
+
self.plot_one_axis_recordings(fig, ax2, apic_rec_list, apic_dist, self.apical_cmap)
|
|
488
|
+
|
|
489
|
+
# plt.setp(ax1.get_xticklabels(), visible=False)
|
|
490
|
+
ax1.set_title('Basal Dendritic Recordings')
|
|
491
|
+
ax2.set_title('Apical Dendritic Recordings')
|
|
492
|
+
ax1.set_ylabel('Voltage (mV)')
|
|
493
|
+
ax2.set_ylabel('Voltage (mV)')
|
|
494
|
+
ax2.set_xlabel('Time (ms)')
|
|
495
|
+
fig.suptitle('Back-propagating Action Potential Recordings')
|
|
496
|
+
fig.tight_layout()
|
|
497
|
+
if save_figure:
|
|
498
|
+
fig.savefig(outpath)
|
|
499
|
+
if show_figure:
|
|
500
|
+
plt.show()
|
|
501
|
+
|
|
502
|
+
return outpath
|
|
@@ -233,7 +233,7 @@ class BluepyCircuitAccess:
|
|
|
233
233
|
source_popid, target_popid = zip(*pop_ids)
|
|
234
234
|
|
|
235
235
|
result = result.assign(
|
|
236
|
-
source_popid=source_popid, target_popid=target_popid
|
|
236
|
+
source_popid=pd.Series(source_popid), target_popid=pd.Series(target_popid)
|
|
237
237
|
)
|
|
238
238
|
|
|
239
239
|
if result.empty:
|
|
@@ -4,6 +4,8 @@ from __future__ import annotations
|
|
|
4
4
|
import contextlib
|
|
5
5
|
import io
|
|
6
6
|
import json
|
|
7
|
+
import multiprocessing
|
|
8
|
+
from multiprocessing import pool
|
|
7
9
|
|
|
8
10
|
import numpy as np
|
|
9
11
|
|
|
@@ -56,3 +58,27 @@ class NumpyEncoder(json.JSONEncoder):
|
|
|
56
58
|
elif isinstance(obj, np.ndarray):
|
|
57
59
|
return obj.tolist()
|
|
58
60
|
return json.JSONEncoder.default(self, obj)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class NoDaemonProcess(multiprocessing.Process):
|
|
64
|
+
"""Class that represents a non-daemon process."""
|
|
65
|
+
|
|
66
|
+
# pylint: disable=dangerous-default-value
|
|
67
|
+
|
|
68
|
+
def __init__(self, group=None, target=None, name=None, args=(), kwargs={}):
|
|
69
|
+
"""Ensures group=None, for macosx."""
|
|
70
|
+
super().__init__(group=None, target=target, name=name, args=args, kwargs=kwargs)
|
|
71
|
+
|
|
72
|
+
@property
|
|
73
|
+
def daemon(self):
|
|
74
|
+
return False
|
|
75
|
+
|
|
76
|
+
@daemon.setter
|
|
77
|
+
def daemon(self, val):
|
|
78
|
+
pass
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class NestedPool(pool.Pool): # pylint: disable=abstract-method
|
|
82
|
+
"""Class that represents a MultiProcessing nested pool."""
|
|
83
|
+
|
|
84
|
+
Process = NoDaemonProcess
|