bluecellulab 2.6.47__py3-none-any.whl → 2.6.48__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.
Potentially problematic release.
This version of bluecellulab might be problematic. Click here for more details.
- bluecellulab/analysis/analysis.py +47 -11
- bluecellulab/analysis/inject_sequence.py +96 -24
- bluecellulab/analysis/plotting.py +47 -4
- bluecellulab/stimulus/factory.py +104 -74
- bluecellulab/tools.py +3 -4
- bluecellulab/validation/validation.py +401 -0
- {bluecellulab-2.6.47.dist-info → bluecellulab-2.6.48.dist-info}/METADATA +1 -1
- {bluecellulab-2.6.47.dist-info → bluecellulab-2.6.48.dist-info}/RECORD +12 -11
- {bluecellulab-2.6.47.dist-info → bluecellulab-2.6.48.dist-info}/WHEEL +1 -1
- {bluecellulab-2.6.47.dist-info → bluecellulab-2.6.48.dist-info}/licenses/AUTHORS.txt +0 -0
- {bluecellulab-2.6.47.dist-info → bluecellulab-2.6.48.dist-info}/licenses/LICENSE +0 -0
- {bluecellulab-2.6.47.dist-info → bluecellulab-2.6.48.dist-info}/top_level.txt +0 -0
|
@@ -5,10 +5,10 @@ except ImportError:
|
|
|
5
5
|
efel = None
|
|
6
6
|
import numpy as np
|
|
7
7
|
|
|
8
|
-
from bluecellulab.stimulus import StimulusFactory
|
|
9
|
-
from bluecellulab.tools import calculate_rheobase
|
|
10
8
|
from bluecellulab.analysis.inject_sequence import run_stimulus
|
|
11
9
|
from bluecellulab.analysis.plotting import plot_iv_curve, plot_fi_curve
|
|
10
|
+
from bluecellulab.stimulus import StimulusFactory
|
|
11
|
+
from bluecellulab.tools import calculate_rheobase
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
def compute_plot_iv_curve(cell,
|
|
@@ -19,8 +19,13 @@ def compute_plot_iv_curve(cell,
|
|
|
19
19
|
stim_start=100.0,
|
|
20
20
|
duration=500.0,
|
|
21
21
|
post_delay=100.0,
|
|
22
|
-
threshold_voltage=-
|
|
23
|
-
nb_bins=11
|
|
22
|
+
threshold_voltage=-20,
|
|
23
|
+
nb_bins=11,
|
|
24
|
+
rheobase=None,
|
|
25
|
+
show_figure=True,
|
|
26
|
+
save_figure=False,
|
|
27
|
+
output_dir="./",
|
|
28
|
+
output_fname="iv_curve.pdf"):
|
|
24
29
|
"""Compute and plot the Current-Voltage (I-V) curve for a given cell by
|
|
25
30
|
injecting a range of currents.
|
|
26
31
|
|
|
@@ -46,6 +51,12 @@ def compute_plot_iv_curve(cell,
|
|
|
46
51
|
response. Default is -30 mV.
|
|
47
52
|
nb_bins (int, optional): The number of discrete current levels between 0 and the maximum current.
|
|
48
53
|
Default is 11.
|
|
54
|
+
rheobase (float, optional): The rheobase current (in nA) for the cell. If not provided, it will
|
|
55
|
+
be calculated using the `calculate_rheobase` function.
|
|
56
|
+
show_figure (bool): Whether to display the figure. Default is True.
|
|
57
|
+
save_figure (bool): Whether to save the figure. Default is False.
|
|
58
|
+
output_dir (str): The directory to save the figure if save_figure is True. Default is "./".
|
|
59
|
+
output_fname (str): The filename to save the figure as if save_figure is True. Default is "iv_curve.png".
|
|
49
60
|
|
|
50
61
|
Returns:
|
|
51
62
|
tuple: A tuple containing:
|
|
@@ -57,7 +68,8 @@ def compute_plot_iv_curve(cell,
|
|
|
57
68
|
ValueError: If the cell object is invalid, the specified sections/segments are not found, or if
|
|
58
69
|
the simulation results are inconsistent.
|
|
59
70
|
"""
|
|
60
|
-
rheobase
|
|
71
|
+
if rheobase is None:
|
|
72
|
+
rheobase = calculate_rheobase(cell=cell, section=injecting_section, segx=injecting_segment)
|
|
61
73
|
|
|
62
74
|
list_amp = np.linspace(rheobase - 2, rheobase - 0.1, nb_bins) # [nA]
|
|
63
75
|
|
|
@@ -89,7 +101,7 @@ def compute_plot_iv_curve(cell,
|
|
|
89
101
|
'stim_end': [stim_start + duration]
|
|
90
102
|
}
|
|
91
103
|
features_results = efel.get_feature_values([trace], ['steady_state_voltage_stimend'])
|
|
92
|
-
steady_state = features_results[0]['steady_state_voltage_stimend']
|
|
104
|
+
steady_state = features_results[0]['steady_state_voltage_stimend'][0]
|
|
93
105
|
steady_states.append(steady_state)
|
|
94
106
|
|
|
95
107
|
plot_iv_curve(list_amp,
|
|
@@ -97,7 +109,11 @@ def compute_plot_iv_curve(cell,
|
|
|
97
109
|
injecting_section=injecting_section,
|
|
98
110
|
injecting_segment=injecting_segment,
|
|
99
111
|
recording_section=recording_section,
|
|
100
|
-
recording_segment=recording_segment
|
|
112
|
+
recording_segment=recording_segment,
|
|
113
|
+
show_figure=show_figure,
|
|
114
|
+
save_figure=save_figure,
|
|
115
|
+
output_dir=output_dir,
|
|
116
|
+
output_fname=output_fname)
|
|
101
117
|
|
|
102
118
|
return np.array(list_amp), np.array(steady_states)
|
|
103
119
|
|
|
@@ -111,7 +127,13 @@ def compute_plot_fi_curve(cell,
|
|
|
111
127
|
duration=500.0,
|
|
112
128
|
post_delay=100.0,
|
|
113
129
|
max_current=0.8,
|
|
114
|
-
|
|
130
|
+
threshold_voltage=-20,
|
|
131
|
+
nb_bins=11,
|
|
132
|
+
rheobase=None,
|
|
133
|
+
show_figure=True,
|
|
134
|
+
save_figure=False,
|
|
135
|
+
output_dir="./",
|
|
136
|
+
output_fname="fi_curve.pdf"):
|
|
115
137
|
"""Compute and plot the Frequency-Current (F-I) curve for a given cell by
|
|
116
138
|
injecting a range of currents.
|
|
117
139
|
|
|
@@ -135,8 +157,16 @@ def compute_plot_fi_curve(cell,
|
|
|
135
157
|
(in ms). Default is 100.0 ms.
|
|
136
158
|
max_current (float, optional): The maximum amplitude of the injected current (in nA).
|
|
137
159
|
Default is 0.8 nA.
|
|
160
|
+
threshold_voltage (float, optional): The voltage threshold (in mV) for detecting a steady-state
|
|
161
|
+
response. Default is -30 mV.
|
|
138
162
|
nb_bins (int, optional): The number of discrete current levels between 0 and `max_current`.
|
|
139
163
|
Default is 11.
|
|
164
|
+
rheobase (float, optional): The rheobase current (in nA) for the cell. If not provided, it will
|
|
165
|
+
be calculated using the `calculate_rheobase` function.
|
|
166
|
+
show_figure (bool): Whether to display the figure. Default is True.
|
|
167
|
+
save_figure (bool): Whether to save the figure. Default is False.
|
|
168
|
+
output_dir (str): The directory to save the figure if save_figure is True. Default is "./".
|
|
169
|
+
output_fname (str): The filename to save the figure as if save_figure is True. Default is "iv_curve.png".
|
|
140
170
|
|
|
141
171
|
Returns:
|
|
142
172
|
tuple: A tuple containing:
|
|
@@ -146,7 +176,8 @@ def compute_plot_fi_curve(cell,
|
|
|
146
176
|
Raises:
|
|
147
177
|
ValueError: If the cell object is invalid or the specified sections/segments are not found.
|
|
148
178
|
"""
|
|
149
|
-
rheobase
|
|
179
|
+
if rheobase is None:
|
|
180
|
+
rheobase = calculate_rheobase(cell=cell, section=injecting_section, segx=injecting_segment)
|
|
150
181
|
|
|
151
182
|
list_amp = np.linspace(rheobase, max_current, nb_bins) # [nA]
|
|
152
183
|
steps = []
|
|
@@ -161,7 +192,8 @@ def compute_plot_fi_curve(cell,
|
|
|
161
192
|
segment=injecting_segment,
|
|
162
193
|
recording_section=recording_section,
|
|
163
194
|
recording_segment=recording_segment,
|
|
164
|
-
enable_spike_detection=True
|
|
195
|
+
enable_spike_detection=True,
|
|
196
|
+
threshold_spike_detection=threshold_voltage)
|
|
165
197
|
steps.append(step_stimulus)
|
|
166
198
|
spikes.append(recording.spike)
|
|
167
199
|
|
|
@@ -172,6 +204,10 @@ def compute_plot_fi_curve(cell,
|
|
|
172
204
|
injecting_section=injecting_section,
|
|
173
205
|
injecting_segment=injecting_segment,
|
|
174
206
|
recording_section=recording_section,
|
|
175
|
-
recording_segment=recording_segment
|
|
207
|
+
recording_segment=recording_segment,
|
|
208
|
+
show_figure=show_figure,
|
|
209
|
+
save_figure=save_figure,
|
|
210
|
+
output_dir=output_dir,
|
|
211
|
+
output_fname=output_fname)
|
|
176
212
|
|
|
177
213
|
return np.array(list_amp), np.array(spike_count)
|
|
@@ -36,18 +36,17 @@ class Recording(NamedTuple):
|
|
|
36
36
|
StimulusRecordings = Dict[str, Recording]
|
|
37
37
|
|
|
38
38
|
|
|
39
|
-
def
|
|
39
|
+
def run_multirecordings_stimulus(
|
|
40
40
|
template_params: TemplateParams,
|
|
41
41
|
stimulus: Stimulus,
|
|
42
42
|
section: str,
|
|
43
43
|
segment: float,
|
|
44
44
|
cvode: bool = True,
|
|
45
45
|
add_hypamp: bool = True,
|
|
46
|
-
|
|
47
|
-
recording_segment: float = 0.5,
|
|
46
|
+
recording_locations: list[tuple[str, float]] = [("soma[0]", 0.5)],
|
|
48
47
|
enable_spike_detection: bool = False,
|
|
49
|
-
threshold_spike_detection: float = -
|
|
50
|
-
) -> Recording:
|
|
48
|
+
threshold_spike_detection: float = -20.0,
|
|
49
|
+
) -> list[Recording]:
|
|
51
50
|
"""Creates a cell from template parameters, applies a stimulus, and records
|
|
52
51
|
the response.
|
|
53
52
|
|
|
@@ -66,16 +65,17 @@ def run_stimulus(
|
|
|
66
65
|
cvode (bool, optional): Whether to use variable time-step integration. Defaults to True.
|
|
67
66
|
add_hypamp (bool, optional): If True, adds a hyperpolarizing stimulus before applying
|
|
68
67
|
the main stimulus. Defaults to True.
|
|
69
|
-
|
|
70
|
-
|
|
68
|
+
recording_location (list): List of tuples containing the name of the section of the cell
|
|
69
|
+
where voltage is recorded and the normalized position (0.0 to 1.0) along the recording
|
|
71
70
|
section where voltage is recorded.
|
|
71
|
+
(e.g. [("soma[0]", 0.5), ("dend[0]", 0.5)])
|
|
72
72
|
enable_spike_detection (bool, optional): If True, enables spike detection at the
|
|
73
73
|
recording location. Defaults to False.
|
|
74
74
|
threshold_spike_detection (float, optional): The voltage threshold (mV) for spike detection.
|
|
75
|
-
Defaults to -
|
|
75
|
+
Defaults to -20 mV.
|
|
76
76
|
|
|
77
77
|
Returns:
|
|
78
|
-
Recording:
|
|
78
|
+
list[Recording]: `Recording` objects containing the following:
|
|
79
79
|
- `current` (np.ndarray): The injected current waveform (nA).
|
|
80
80
|
- `voltage` (np.ndarray): The recorded membrane potential (mV) over time.
|
|
81
81
|
- `time` (np.ndarray): The simulation time points (ms).
|
|
@@ -88,19 +88,22 @@ def run_stimulus(
|
|
|
88
88
|
cell = Cell.from_template_parameters(template_params)
|
|
89
89
|
|
|
90
90
|
validate_section_and_segment(cell, section, segment)
|
|
91
|
-
|
|
91
|
+
for recording_section, recording_segment in recording_locations:
|
|
92
|
+
validate_section_and_segment(cell, recording_section, recording_segment)
|
|
92
93
|
|
|
93
94
|
if add_hypamp:
|
|
94
95
|
hyp_stim = Hyperpolarizing(target="", delay=0.0, duration=stimulus.stimulus_time)
|
|
95
96
|
cell.add_replay_hypamp(hyp_stim)
|
|
96
97
|
|
|
97
|
-
|
|
98
|
+
for recording_section, recording_segment in recording_locations:
|
|
99
|
+
cell.add_voltage_recording(cell.sections[recording_section], recording_segment)
|
|
98
100
|
|
|
99
101
|
# Set up spike detection if enabled
|
|
100
102
|
spikes: Optional[np.ndarray] = None
|
|
101
103
|
if enable_spike_detection:
|
|
102
|
-
|
|
103
|
-
|
|
104
|
+
for recording_section, recording_segment in recording_locations:
|
|
105
|
+
recording_location = f"{recording_section}({str(recording_segment)})"
|
|
106
|
+
cell.start_recording_spikes(None, location=recording_location, threshold=threshold_spike_detection)
|
|
104
107
|
|
|
105
108
|
# Inject the stimulus and run the simulation
|
|
106
109
|
iclamp, _ = cell.inject_current_waveform(
|
|
@@ -113,21 +116,90 @@ def run_stimulus(
|
|
|
113
116
|
simulation.run(stimulus.stimulus_time, cvode=cvode)
|
|
114
117
|
|
|
115
118
|
# Retrieve simulation results
|
|
119
|
+
recordings = []
|
|
116
120
|
current = np.array(current_vector.to_python())
|
|
117
|
-
|
|
118
|
-
|
|
121
|
+
for recording_section, recording_segment in recording_locations:
|
|
122
|
+
recording_location = f"{recording_section}({str(recording_segment)})"
|
|
123
|
+
voltage = cell.get_voltage_recording(cell.sections[recording_section], recording_segment)
|
|
124
|
+
time = cell.get_time()
|
|
119
125
|
|
|
120
|
-
|
|
121
|
-
|
|
126
|
+
if len(time) != len(voltage) or len(time) != len(current):
|
|
127
|
+
raise ValueError("Time, current, and voltage arrays are not the same length")
|
|
122
128
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
+
if enable_spike_detection:
|
|
130
|
+
results = cell.get_recorded_spikes(location=recording_location, threshold=threshold_spike_detection)
|
|
131
|
+
if results is not None:
|
|
132
|
+
spikes = np.array(results)
|
|
133
|
+
else:
|
|
134
|
+
spikes = None
|
|
135
|
+
|
|
136
|
+
recordings.append(
|
|
137
|
+
Recording(current=current, voltage=voltage, time=time, spike=spikes)
|
|
138
|
+
)
|
|
129
139
|
|
|
130
|
-
return
|
|
140
|
+
return recordings
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def run_stimulus(
|
|
144
|
+
template_params: TemplateParams,
|
|
145
|
+
stimulus: Stimulus,
|
|
146
|
+
section: str,
|
|
147
|
+
segment: float,
|
|
148
|
+
cvode: bool = True,
|
|
149
|
+
add_hypamp: bool = True,
|
|
150
|
+
recording_section: str = "soma[0]",
|
|
151
|
+
recording_segment: float = 0.5,
|
|
152
|
+
enable_spike_detection: bool = False,
|
|
153
|
+
threshold_spike_detection: float = -20.0,
|
|
154
|
+
) -> Recording:
|
|
155
|
+
"""Creates a cell from template parameters, applies a stimulus, and records
|
|
156
|
+
the response.
|
|
157
|
+
|
|
158
|
+
This function simulates the electrical activity of a neuronal cell model by injecting
|
|
159
|
+
a stimulus into a specified section and segment, recording the voltage response, and
|
|
160
|
+
optionally detecting spikes.
|
|
161
|
+
|
|
162
|
+
Args:
|
|
163
|
+
template_params (TemplateParams): Parameters required to create the cell from a
|
|
164
|
+
specified template, including morphology and mechanisms.
|
|
165
|
+
stimulus (Stimulus): The stimulus waveform to inject, defined by time and current arrays.
|
|
166
|
+
section (str): Name of the section of the cell where the stimulus is applied.
|
|
167
|
+
(e.g. soma[0])
|
|
168
|
+
segment (float): The normalized position (0.0 to 1.0) along the injecting
|
|
169
|
+
section where the stimulus is applied.
|
|
170
|
+
cvode (bool, optional): Whether to use variable time-step integration. Defaults to True.
|
|
171
|
+
add_hypamp (bool, optional): If True, adds a hyperpolarizing stimulus before applying
|
|
172
|
+
the main stimulus. Defaults to True.
|
|
173
|
+
recording_section (str): Name of the section of the cell where voltage is recorded.
|
|
174
|
+
recording_segment (float): The normalized position (0.0 to 1.0) along the recording
|
|
175
|
+
section where voltage is recorded.
|
|
176
|
+
enable_spike_detection (bool, optional): If True, enables spike detection at the
|
|
177
|
+
recording location. Defaults to False.
|
|
178
|
+
threshold_spike_detection (float, optional): The voltage threshold (mV) for spike detection.
|
|
179
|
+
Defaults to -20 mV.
|
|
180
|
+
|
|
181
|
+
Returns:
|
|
182
|
+
Recording: A `Recording` object containing the following:
|
|
183
|
+
- `current` (np.ndarray): The injected current waveform (nA).
|
|
184
|
+
- `voltage` (np.ndarray): The recorded membrane potential (mV) over time.
|
|
185
|
+
- `time` (np.ndarray): The simulation time points (ms).
|
|
186
|
+
- `spike` (np.ndarray or None): The detected spikes, if spike detection is enabled.
|
|
187
|
+
|
|
188
|
+
Raises:
|
|
189
|
+
ValueError: If the time, current, and voltage arrays do not have the same length,
|
|
190
|
+
or if the specified sections or segments are not found in the cell model.
|
|
191
|
+
"""
|
|
192
|
+
return run_multirecordings_stimulus(
|
|
193
|
+
template_params=template_params,
|
|
194
|
+
stimulus=stimulus,
|
|
195
|
+
section=section,
|
|
196
|
+
segment=segment,
|
|
197
|
+
cvode=cvode,
|
|
198
|
+
add_hypamp=add_hypamp,
|
|
199
|
+
recording_locations=[(recording_section, recording_segment)],
|
|
200
|
+
enable_spike_detection=enable_spike_detection,
|
|
201
|
+
threshold_spike_detection=threshold_spike_detection,
|
|
202
|
+
)[0]
|
|
131
203
|
|
|
132
204
|
|
|
133
205
|
def apply_multiple_stimuli(
|
|
@@ -1,9 +1,21 @@
|
|
|
1
1
|
"""Module for plotting analysis results of cell simulations."""
|
|
2
2
|
|
|
3
3
|
import matplotlib.pyplot as plt
|
|
4
|
+
import pathlib
|
|
4
5
|
|
|
5
6
|
|
|
6
|
-
def plot_iv_curve(
|
|
7
|
+
def plot_iv_curve(
|
|
8
|
+
currents,
|
|
9
|
+
voltages,
|
|
10
|
+
injecting_section,
|
|
11
|
+
injecting_segment,
|
|
12
|
+
recording_section,
|
|
13
|
+
recording_segment,
|
|
14
|
+
show_figure=True,
|
|
15
|
+
save_figure=False,
|
|
16
|
+
output_dir="./",
|
|
17
|
+
output_fname="iv_curve.pdf",
|
|
18
|
+
):
|
|
7
19
|
"""Plots the IV curve.
|
|
8
20
|
|
|
9
21
|
Args:
|
|
@@ -13,6 +25,10 @@ def plot_iv_curve(currents, voltages, injecting_section, injecting_segment, reco
|
|
|
13
25
|
injecting_segment (float): The segment position (0.0 to 1.0) where the current was injected.
|
|
14
26
|
recording_section (str): The section in the cell where spikes were recorded.
|
|
15
27
|
recording_segment (float): The segment position (0.0 to 1.0) where spikes were recorded.
|
|
28
|
+
show_figure (bool): Whether to display the figure. Default is True.
|
|
29
|
+
save_figure (bool): Whether to save the figure. Default is False.
|
|
30
|
+
output_dir (str): The directory to save the figure if save_figure is True. Default is "./".
|
|
31
|
+
output_fname (str): The filename to save the figure as if save_figure is True. Default is "iv_curve.pdf".
|
|
16
32
|
|
|
17
33
|
Raises:
|
|
18
34
|
ValueError: If the lengths of currents and voltages do not match.
|
|
@@ -27,10 +43,27 @@ def plot_iv_curve(currents, voltages, injecting_section, injecting_segment, reco
|
|
|
27
43
|
plt.ylabel(f"Steady state voltage [mV] at {recording_section}({recording_segment:.2f})")
|
|
28
44
|
plt.grid(True)
|
|
29
45
|
plt.tight_layout()
|
|
30
|
-
|
|
46
|
+
if show_figure:
|
|
47
|
+
plt.show()
|
|
31
48
|
|
|
49
|
+
if save_figure:
|
|
50
|
+
pathlib.Path(output_dir).mkdir(parents=True, exist_ok=True)
|
|
51
|
+
plt.savefig(pathlib.Path(output_dir) / output_fname, format='pdf')
|
|
52
|
+
plt.close()
|
|
32
53
|
|
|
33
|
-
|
|
54
|
+
|
|
55
|
+
def plot_fi_curve(
|
|
56
|
+
currents,
|
|
57
|
+
spike_count,
|
|
58
|
+
injecting_section,
|
|
59
|
+
injecting_segment,
|
|
60
|
+
recording_section,
|
|
61
|
+
recording_segment,
|
|
62
|
+
show_figure=True,
|
|
63
|
+
save_figure=False,
|
|
64
|
+
output_dir="./",
|
|
65
|
+
output_fname="fi_curve.pdf",
|
|
66
|
+
):
|
|
34
67
|
"""Plots the F-I (Frequency-Current) curve.
|
|
35
68
|
|
|
36
69
|
Args:
|
|
@@ -40,6 +73,10 @@ def plot_fi_curve(currents, spike_count, injecting_section, injecting_segment, r
|
|
|
40
73
|
injecting_segment (float): The segment position (0.0 to 1.0) where the current was injected.
|
|
41
74
|
recording_section (str): The section in the cell where spikes were recorded.
|
|
42
75
|
recording_segment (float): The segment position (0.0 to 1.0) where spikes were recorded.
|
|
76
|
+
show_figure (bool): Whether to display the figure. Default is True.
|
|
77
|
+
save_figure (bool): Whether to save the figure. Default is False.
|
|
78
|
+
output_dir (str): The directory to save the figure if save_figure is True. Default is "./".
|
|
79
|
+
output_fname (str): The filename to save the figure as if save_figure is True. Default is "fi_curve.pdf".
|
|
43
80
|
|
|
44
81
|
Raises:
|
|
45
82
|
ValueError: If the lengths of currents and spike counts do not match.
|
|
@@ -54,4 +91,10 @@ def plot_fi_curve(currents, spike_count, injecting_section, injecting_segment, r
|
|
|
54
91
|
plt.ylabel(f"Spike Count recorded at {recording_section}({recording_segment:.2f})")
|
|
55
92
|
plt.grid(True)
|
|
56
93
|
plt.tight_layout()
|
|
57
|
-
|
|
94
|
+
if show_figure:
|
|
95
|
+
plt.show()
|
|
96
|
+
|
|
97
|
+
if save_figure:
|
|
98
|
+
pathlib.Path(output_dir).mkdir(parents=True, exist_ok=True)
|
|
99
|
+
plt.savefig(pathlib.Path(output_dir) / output_fname, format='pdf')
|
|
100
|
+
plt.close()
|
bluecellulab/stimulus/factory.py
CHANGED
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
# limitations under the License.
|
|
15
15
|
|
|
16
16
|
from __future__ import annotations
|
|
17
|
+
from enum import Enum
|
|
17
18
|
from typing import Optional
|
|
18
19
|
import logging
|
|
19
20
|
from bluecellulab.stimulus.stimulus import DelayedZap, Empty, Ramp, Slope, Step, StepNoise, Stimulus, OrnsteinUhlenbeck, ShotNoise, Sinusoidal, Pulse
|
|
@@ -21,6 +22,61 @@ from bluecellulab.stimulus.stimulus import DelayedZap, Empty, Ramp, Slope, Step,
|
|
|
21
22
|
logger = logging.getLogger(__name__)
|
|
22
23
|
|
|
23
24
|
|
|
25
|
+
class APWaveformTimings(Enum):
|
|
26
|
+
"""APWaveform timings."""
|
|
27
|
+
PRE_DELAY = 250.0
|
|
28
|
+
DURATION = 50.0
|
|
29
|
+
POST_DELAY = 250.0
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class IDRestTimings(Enum):
|
|
33
|
+
"""IDRest timings."""
|
|
34
|
+
PRE_DELAY = 250.0
|
|
35
|
+
DURATION = 1350.0
|
|
36
|
+
POST_DELAY = 250.0
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class IVTimings(Enum):
|
|
40
|
+
"""IV timings."""
|
|
41
|
+
PRE_DELAY = 250.0
|
|
42
|
+
DURATION = 3000.0
|
|
43
|
+
POST_DELAY = 250.0
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class FirePatternTimings(Enum):
|
|
47
|
+
"""FirePattern timings."""
|
|
48
|
+
PRE_DELAY = 250.0
|
|
49
|
+
DURATION = 3600.0
|
|
50
|
+
POST_DELAY = 250.0
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class PosCheopsTimings(Enum):
|
|
54
|
+
"""PosCheops timings."""
|
|
55
|
+
PRE_DELAY = 250.0
|
|
56
|
+
RAMP1_DURATION = 4000.0
|
|
57
|
+
RAMP2_DURATION = 2000.0
|
|
58
|
+
RAMP3_DURATION = 1333.0
|
|
59
|
+
INTER_DELAY = 2000.0
|
|
60
|
+
POST_DELAY = 250.0
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class NegCheopsTimings(Enum):
|
|
64
|
+
"""NegCheops timings."""
|
|
65
|
+
PRE_DELAY = 1750.0
|
|
66
|
+
RAMP1_DURATION = 3333.0
|
|
67
|
+
RAMP2_DURATION = 1666.0
|
|
68
|
+
RAMP3_DURATION = 1111.0
|
|
69
|
+
INTER_DELAY = 2000.0
|
|
70
|
+
POST_DELAY = 250.0
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class SineSpecTimings(Enum):
|
|
74
|
+
"""SineSpec timings."""
|
|
75
|
+
PRE_DELAY = 0
|
|
76
|
+
DURATION = 5000.0
|
|
77
|
+
POST_DELAY = 0
|
|
78
|
+
|
|
79
|
+
|
|
24
80
|
class StimulusFactory:
|
|
25
81
|
def __init__(self, dt: float):
|
|
26
82
|
self.dt = dt
|
|
@@ -84,9 +140,6 @@ class StimulusFactory:
|
|
|
84
140
|
threshold_percentage: Percentage of desired threshold_current amplification.
|
|
85
141
|
amplitude: Raw amplitude of input current.
|
|
86
142
|
"""
|
|
87
|
-
pre_delay = 250.0
|
|
88
|
-
duration = 50.0
|
|
89
|
-
post_delay = 250.0
|
|
90
143
|
|
|
91
144
|
if amplitude is not None:
|
|
92
145
|
if threshold_current is not None and threshold_current != 0 and threshold_percentage is not None:
|
|
@@ -96,18 +149,18 @@ class StimulusFactory:
|
|
|
96
149
|
)
|
|
97
150
|
return Step.amplitude_based(
|
|
98
151
|
self.dt,
|
|
99
|
-
pre_delay=
|
|
100
|
-
duration=
|
|
101
|
-
post_delay=
|
|
152
|
+
pre_delay=APWaveformTimings.PRE_DELAY.value,
|
|
153
|
+
duration=APWaveformTimings.DURATION.value,
|
|
154
|
+
post_delay=APWaveformTimings.POST_DELAY.value,
|
|
102
155
|
amplitude=amplitude,
|
|
103
156
|
)
|
|
104
157
|
|
|
105
158
|
if threshold_current is not None and threshold_current != 0 and threshold_percentage is not None:
|
|
106
159
|
return Step.threshold_based(
|
|
107
160
|
self.dt,
|
|
108
|
-
pre_delay=
|
|
109
|
-
duration=
|
|
110
|
-
post_delay=
|
|
161
|
+
pre_delay=APWaveformTimings.PRE_DELAY.value,
|
|
162
|
+
duration=APWaveformTimings.DURATION.value,
|
|
163
|
+
post_delay=APWaveformTimings.POST_DELAY.value,
|
|
111
164
|
threshold_current=threshold_current,
|
|
112
165
|
threshold_percentage=threshold_percentage,
|
|
113
166
|
)
|
|
@@ -127,9 +180,6 @@ class StimulusFactory:
|
|
|
127
180
|
threshold_percentage: Percentage of desired threshold_current amplification.
|
|
128
181
|
amplitude: Raw amplitude of input current.
|
|
129
182
|
"""
|
|
130
|
-
pre_delay = 250.0
|
|
131
|
-
duration = 1350.0
|
|
132
|
-
post_delay = 250.0
|
|
133
183
|
|
|
134
184
|
if amplitude is not None:
|
|
135
185
|
if threshold_current is not None and threshold_current != 0 and threshold_percentage is not None:
|
|
@@ -139,18 +189,18 @@ class StimulusFactory:
|
|
|
139
189
|
)
|
|
140
190
|
return Step.amplitude_based(
|
|
141
191
|
self.dt,
|
|
142
|
-
pre_delay=
|
|
143
|
-
duration=
|
|
144
|
-
post_delay=
|
|
192
|
+
pre_delay=IDRestTimings.PRE_DELAY.value,
|
|
193
|
+
duration=IDRestTimings.DURATION.value,
|
|
194
|
+
post_delay=IDRestTimings.POST_DELAY.value,
|
|
145
195
|
amplitude=amplitude,
|
|
146
196
|
)
|
|
147
197
|
|
|
148
198
|
if threshold_current is not None and threshold_current != 0 and threshold_percentage is not None:
|
|
149
199
|
return Step.threshold_based(
|
|
150
200
|
self.dt,
|
|
151
|
-
pre_delay=
|
|
152
|
-
duration=
|
|
153
|
-
post_delay=
|
|
201
|
+
pre_delay=IDRestTimings.PRE_DELAY.value,
|
|
202
|
+
duration=IDRestTimings.DURATION.value,
|
|
203
|
+
post_delay=IDRestTimings.POST_DELAY.value,
|
|
154
204
|
threshold_current=threshold_current,
|
|
155
205
|
threshold_percentage=threshold_percentage,
|
|
156
206
|
)
|
|
@@ -170,9 +220,6 @@ class StimulusFactory:
|
|
|
170
220
|
threshold_percentage: Percentage of desired threshold_current amplification.
|
|
171
221
|
amplitude: Raw amplitude of input current.
|
|
172
222
|
"""
|
|
173
|
-
pre_delay = 250.0
|
|
174
|
-
duration = 3000.0
|
|
175
|
-
post_delay = 250.0
|
|
176
223
|
|
|
177
224
|
if amplitude is not None:
|
|
178
225
|
if threshold_current is not None and threshold_current != 0 and threshold_percentage is not None:
|
|
@@ -182,18 +229,18 @@ class StimulusFactory:
|
|
|
182
229
|
)
|
|
183
230
|
return Step.amplitude_based(
|
|
184
231
|
self.dt,
|
|
185
|
-
pre_delay=
|
|
186
|
-
duration=
|
|
187
|
-
post_delay=
|
|
232
|
+
pre_delay=IVTimings.PRE_DELAY.value,
|
|
233
|
+
duration=IVTimings.DURATION.value,
|
|
234
|
+
post_delay=IVTimings.POST_DELAY.value,
|
|
188
235
|
amplitude=amplitude,
|
|
189
236
|
)
|
|
190
237
|
|
|
191
238
|
if threshold_current is not None and threshold_current != 0 and threshold_percentage is not None:
|
|
192
239
|
return Step.threshold_based(
|
|
193
240
|
self.dt,
|
|
194
|
-
pre_delay=
|
|
195
|
-
duration=
|
|
196
|
-
post_delay=
|
|
241
|
+
pre_delay=IVTimings.PRE_DELAY.value,
|
|
242
|
+
duration=IVTimings.DURATION.value,
|
|
243
|
+
post_delay=IVTimings.POST_DELAY.value,
|
|
197
244
|
threshold_current=threshold_current,
|
|
198
245
|
threshold_percentage=threshold_percentage,
|
|
199
246
|
)
|
|
@@ -213,9 +260,6 @@ class StimulusFactory:
|
|
|
213
260
|
threshold_percentage: Percentage of desired threshold_current amplification.
|
|
214
261
|
amplitude: Raw amplitude of input current.
|
|
215
262
|
"""
|
|
216
|
-
pre_delay = 250.0
|
|
217
|
-
duration = 3600.0
|
|
218
|
-
post_delay = 250.0
|
|
219
263
|
|
|
220
264
|
if amplitude is not None:
|
|
221
265
|
if threshold_current is not None and threshold_current != 0 and threshold_percentage is not None:
|
|
@@ -225,18 +269,18 @@ class StimulusFactory:
|
|
|
225
269
|
)
|
|
226
270
|
return Step.amplitude_based(
|
|
227
271
|
self.dt,
|
|
228
|
-
pre_delay=
|
|
229
|
-
duration=
|
|
230
|
-
post_delay=
|
|
272
|
+
pre_delay=FirePatternTimings.PRE_DELAY.value,
|
|
273
|
+
duration=FirePatternTimings.DURATION.value,
|
|
274
|
+
post_delay=FirePatternTimings.POST_DELAY.value,
|
|
231
275
|
amplitude=amplitude,
|
|
232
276
|
)
|
|
233
277
|
|
|
234
278
|
if threshold_current is not None and threshold_current != 0 and threshold_percentage is not None:
|
|
235
279
|
return Step.threshold_based(
|
|
236
280
|
self.dt,
|
|
237
|
-
pre_delay=
|
|
238
|
-
duration=
|
|
239
|
-
post_delay=
|
|
281
|
+
pre_delay=FirePatternTimings.PRE_DELAY.value,
|
|
282
|
+
duration=FirePatternTimings.DURATION.value,
|
|
283
|
+
post_delay=FirePatternTimings.POST_DELAY.value,
|
|
240
284
|
threshold_current=threshold_current,
|
|
241
285
|
threshold_percentage=threshold_percentage,
|
|
242
286
|
)
|
|
@@ -257,12 +301,6 @@ class StimulusFactory:
|
|
|
257
301
|
threshold_percentage: Percentage of desired threshold_current amplification.
|
|
258
302
|
amplitude: Raw amplitude of input current.
|
|
259
303
|
"""
|
|
260
|
-
delay = 250.0
|
|
261
|
-
ramp1_duration = 4000.0
|
|
262
|
-
ramp2_duration = 2000.0
|
|
263
|
-
ramp3_duration = 1333.0
|
|
264
|
-
inter_delay = 2000.0
|
|
265
|
-
post_delay = 250.0
|
|
266
304
|
|
|
267
305
|
if amplitude is None:
|
|
268
306
|
if threshold_current is None or threshold_current == 0 or threshold_percentage is None:
|
|
@@ -274,16 +312,16 @@ class StimulusFactory:
|
|
|
274
312
|
" Will only keep amplitude value."
|
|
275
313
|
)
|
|
276
314
|
result = (
|
|
277
|
-
Empty(self.dt, duration=
|
|
278
|
-
+ Slope(self.dt, duration=
|
|
279
|
-
+ Slope(self.dt, duration=
|
|
280
|
-
+ Empty(self.dt, duration=
|
|
281
|
-
+ Slope(self.dt, duration=
|
|
282
|
-
+ Slope(self.dt, duration=
|
|
283
|
-
+ Empty(self.dt, duration=
|
|
284
|
-
+ Slope(self.dt, duration=
|
|
285
|
-
+ Slope(self.dt, duration=
|
|
286
|
-
+ Empty(self.dt, duration=
|
|
315
|
+
Empty(self.dt, duration=PosCheopsTimings.PRE_DELAY.value)
|
|
316
|
+
+ Slope(self.dt, duration=PosCheopsTimings.RAMP1_DURATION.value, amplitude_start=0.0, amplitude_end=amplitude)
|
|
317
|
+
+ Slope(self.dt, duration=PosCheopsTimings.RAMP1_DURATION.value, amplitude_start=amplitude, amplitude_end=0.0)
|
|
318
|
+
+ Empty(self.dt, duration=PosCheopsTimings.INTER_DELAY.value)
|
|
319
|
+
+ Slope(self.dt, duration=PosCheopsTimings.RAMP2_DURATION.value, amplitude_start=0.0, amplitude_end=amplitude)
|
|
320
|
+
+ Slope(self.dt, duration=PosCheopsTimings.RAMP2_DURATION.value, amplitude_start=amplitude, amplitude_end=0.0)
|
|
321
|
+
+ Empty(self.dt, duration=PosCheopsTimings.INTER_DELAY.value)
|
|
322
|
+
+ Slope(self.dt, duration=PosCheopsTimings.RAMP3_DURATION.value, amplitude_start=0.0, amplitude_end=amplitude)
|
|
323
|
+
+ Slope(self.dt, duration=PosCheopsTimings.RAMP3_DURATION.value, amplitude_start=amplitude, amplitude_end=0.0)
|
|
324
|
+
+ Empty(self.dt, duration=PosCheopsTimings.POST_DELAY.value)
|
|
287
325
|
)
|
|
288
326
|
return result
|
|
289
327
|
|
|
@@ -301,12 +339,6 @@ class StimulusFactory:
|
|
|
301
339
|
threshold_percentage: Percentage of desired threshold_current amplification.
|
|
302
340
|
amplitude: Raw amplitude of input current.
|
|
303
341
|
"""
|
|
304
|
-
delay = 1750.0
|
|
305
|
-
ramp1_duration = 3333.0
|
|
306
|
-
ramp2_duration = 1666.0
|
|
307
|
-
ramp3_duration = 1111.0
|
|
308
|
-
inter_delay = 2000.0
|
|
309
|
-
post_delay = 250.0
|
|
310
342
|
|
|
311
343
|
if amplitude is None:
|
|
312
344
|
if threshold_current is None or threshold_current == 0 or threshold_percentage is None:
|
|
@@ -318,16 +350,16 @@ class StimulusFactory:
|
|
|
318
350
|
" Will only keep amplitude value."
|
|
319
351
|
)
|
|
320
352
|
result = (
|
|
321
|
-
Empty(self.dt, duration=
|
|
322
|
-
+ Slope(self.dt, duration=
|
|
323
|
-
+ Slope(self.dt, duration=
|
|
324
|
-
+ Empty(self.dt, duration=
|
|
325
|
-
+ Slope(self.dt, duration=
|
|
326
|
-
+ Slope(self.dt, duration=
|
|
327
|
-
+ Empty(self.dt, duration=
|
|
328
|
-
+ Slope(self.dt, duration=
|
|
329
|
-
+ Slope(self.dt, duration=
|
|
330
|
-
+ Empty(self.dt, duration=
|
|
353
|
+
Empty(self.dt, duration=NegCheopsTimings.PRE_DELAY.value)
|
|
354
|
+
+ Slope(self.dt, duration=NegCheopsTimings.RAMP1_DURATION.value, amplitude_start=0.0, amplitude_end=amplitude)
|
|
355
|
+
+ Slope(self.dt, duration=NegCheopsTimings.RAMP1_DURATION.value, amplitude_start=amplitude, amplitude_end=0.0)
|
|
356
|
+
+ Empty(self.dt, duration=NegCheopsTimings.INTER_DELAY.value)
|
|
357
|
+
+ Slope(self.dt, duration=NegCheopsTimings.RAMP2_DURATION.value, amplitude_start=0.0, amplitude_end=amplitude)
|
|
358
|
+
+ Slope(self.dt, duration=NegCheopsTimings.RAMP2_DURATION.value, amplitude_start=amplitude, amplitude_end=0.0)
|
|
359
|
+
+ Empty(self.dt, duration=NegCheopsTimings.INTER_DELAY.value)
|
|
360
|
+
+ Slope(self.dt, duration=NegCheopsTimings.RAMP3_DURATION.value, amplitude_start=0.0, amplitude_end=amplitude)
|
|
361
|
+
+ Slope(self.dt, duration=NegCheopsTimings.RAMP3_DURATION.value, amplitude_start=amplitude, amplitude_end=0.0)
|
|
362
|
+
+ Empty(self.dt, duration=NegCheopsTimings.POST_DELAY.value)
|
|
331
363
|
)
|
|
332
364
|
return result
|
|
333
365
|
|
|
@@ -346,8 +378,6 @@ class StimulusFactory:
|
|
|
346
378
|
amplitude: Raw amplitude of input current.
|
|
347
379
|
pre_delay: delay before the start of the stimulus
|
|
348
380
|
"""
|
|
349
|
-
duration = 5000.0
|
|
350
|
-
post_delay = 0
|
|
351
381
|
|
|
352
382
|
if amplitude is not None:
|
|
353
383
|
if threshold_current is not None and threshold_current != 0 and threshold_percentage is not None:
|
|
@@ -358,8 +388,8 @@ class StimulusFactory:
|
|
|
358
388
|
return DelayedZap.amplitude_based(
|
|
359
389
|
self.dt,
|
|
360
390
|
pre_delay=pre_delay,
|
|
361
|
-
duration=
|
|
362
|
-
post_delay=
|
|
391
|
+
duration=SineSpecTimings.DURATION.value,
|
|
392
|
+
post_delay=SineSpecTimings.POST_DELAY.value,
|
|
363
393
|
amplitude=amplitude,
|
|
364
394
|
)
|
|
365
395
|
|
|
@@ -367,8 +397,8 @@ class StimulusFactory:
|
|
|
367
397
|
return DelayedZap.threshold_based(
|
|
368
398
|
self.dt,
|
|
369
399
|
pre_delay=pre_delay,
|
|
370
|
-
duration=
|
|
371
|
-
post_delay=
|
|
400
|
+
duration=SineSpecTimings.DURATION.value,
|
|
401
|
+
post_delay=SineSpecTimings.POST_DELAY.value,
|
|
372
402
|
threshold_current=threshold_current,
|
|
373
403
|
threshold_percentage=threshold_percentage,
|
|
374
404
|
)
|
bluecellulab/tools.py
CHANGED
|
@@ -437,18 +437,17 @@ def calculate_max_thresh_current(cell: Cell,
|
|
|
437
437
|
|
|
438
438
|
|
|
439
439
|
def calculate_rheobase(cell: Cell,
|
|
440
|
-
threshold_voltage: float = -
|
|
440
|
+
threshold_voltage: float = -20.0,
|
|
441
441
|
threshold_search_stim_start: float = 300.0,
|
|
442
442
|
threshold_search_stim_stop: float = 1000.0,
|
|
443
443
|
section: str = "soma[0]",
|
|
444
|
-
segx: float = 0.5
|
|
445
|
-
step_thresh: float = -20.) -> float:
|
|
444
|
+
segx: float = 0.5) -> float:
|
|
446
445
|
"""Calculate the rheobase by first computing the upper bound threshold
|
|
447
446
|
current.
|
|
448
447
|
|
|
449
448
|
Args:
|
|
450
449
|
cell (bluecellulab.cell.Cell): The initialized cell model.
|
|
451
|
-
threshold_voltage (float, optional): Voltage threshold for spike detection. Default is -
|
|
450
|
+
threshold_voltage (float, optional): Voltage threshold for spike detection. Default is -20.0 mV.
|
|
452
451
|
threshold_search_stim_start (float, optional): Start time for threshold search stimulation (in ms). Default is 300.0 ms.
|
|
453
452
|
threshold_search_stim_stop (float, optional): Stop time for threshold search stimulation (in ms). Default is 1000.0 ms.
|
|
454
453
|
section (str, optional): The section where current is injected.
|
|
@@ -0,0 +1,401 @@
|
|
|
1
|
+
# Copyright 2025 Open Brain Institute
|
|
2
|
+
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
import logging
|
|
16
|
+
import matplotlib.pyplot as plt
|
|
17
|
+
import numpy
|
|
18
|
+
import pathlib
|
|
19
|
+
|
|
20
|
+
import efel
|
|
21
|
+
|
|
22
|
+
from bluecellulab.analysis.analysis import compute_plot_fi_curve
|
|
23
|
+
from bluecellulab.analysis.analysis import compute_plot_iv_curve
|
|
24
|
+
from bluecellulab.analysis.inject_sequence import run_multirecordings_stimulus
|
|
25
|
+
from bluecellulab.analysis.inject_sequence import run_stimulus
|
|
26
|
+
from bluecellulab.stimulus.factory import IDRestTimings
|
|
27
|
+
from bluecellulab.stimulus.factory import StimulusFactory
|
|
28
|
+
from bluecellulab.tools import calculate_input_resistance
|
|
29
|
+
from bluecellulab.tools import calculate_rheobase
|
|
30
|
+
|
|
31
|
+
logger = logging.getLogger(__name__)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def plot_trace(recording, out_dir, fname, title):
|
|
35
|
+
"""Plot a trace with inout current given a recording."""
|
|
36
|
+
outpath = out_dir / fname
|
|
37
|
+
fig, ax1 = plt.subplots(figsize=(10, 6))
|
|
38
|
+
plt.plot(recording.time, recording.voltage, color="black")
|
|
39
|
+
current_axis = ax1.twinx()
|
|
40
|
+
current_axis.plot(recording.time, recording.current, color="gray", alpha=0.6)
|
|
41
|
+
current_axis.set_ylabel("Stimulus Current [nA]")
|
|
42
|
+
fig.suptitle(title)
|
|
43
|
+
ax1.set_xlabel("Time [ms]")
|
|
44
|
+
ax1.set_ylabel("Voltage [mV]")
|
|
45
|
+
fig.tight_layout()
|
|
46
|
+
fig.savefig(outpath)
|
|
47
|
+
|
|
48
|
+
return outpath
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def plot_traces(recordings, out_dir, fname, title, labels=None, xlim=None):
|
|
52
|
+
"""Plot a trace with inout current given a recording."""
|
|
53
|
+
outpath = out_dir / fname
|
|
54
|
+
fig, ax1 = plt.subplots(figsize=(10, 6))
|
|
55
|
+
prop_cycle = plt.rcParams["axes.prop_cycle"]
|
|
56
|
+
colors = prop_cycle.by_key()["color"]
|
|
57
|
+
N_colors = len(colors)
|
|
58
|
+
for i, recording in enumerate(recordings):
|
|
59
|
+
if i == 0:
|
|
60
|
+
color = "black"
|
|
61
|
+
else:
|
|
62
|
+
color = colors[(i - 1) % N_colors]
|
|
63
|
+
label = labels[i] if labels is not None else None
|
|
64
|
+
plt.plot(recording.time, recording.voltage, color=color, label=label)
|
|
65
|
+
current_axis = ax1.twinx()
|
|
66
|
+
current_axis.plot(recordings[0].time, recordings[0].current, color="gray", alpha=0.6)
|
|
67
|
+
current_axis.set_ylabel("Stimulus Current [nA]")
|
|
68
|
+
fig.suptitle(title)
|
|
69
|
+
ax1.set_xlabel("Time [ms]")
|
|
70
|
+
ax1.set_ylabel("Voltage [mV]")
|
|
71
|
+
if labels is not None:
|
|
72
|
+
ax1.legend()
|
|
73
|
+
if xlim is not None:
|
|
74
|
+
ax1.set_xlim(xlim)
|
|
75
|
+
fig.tight_layout()
|
|
76
|
+
fig.savefig(outpath)
|
|
77
|
+
|
|
78
|
+
return outpath
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def spiking_test(cell, rheobase, out_dir, spike_threshold_voltage=-30.):
|
|
82
|
+
"""Spiking test: cell should spike."""
|
|
83
|
+
stim_factory = StimulusFactory(dt=1.0)
|
|
84
|
+
step_stimulus = stim_factory.idrest(threshold_current=rheobase, threshold_percentage=200)
|
|
85
|
+
recording = run_stimulus(
|
|
86
|
+
cell.template_params,
|
|
87
|
+
step_stimulus,
|
|
88
|
+
"soma[0]",
|
|
89
|
+
0.5,
|
|
90
|
+
add_hypamp=True,
|
|
91
|
+
enable_spike_detection=True,
|
|
92
|
+
threshold_spike_detection=spike_threshold_voltage,
|
|
93
|
+
)
|
|
94
|
+
passed = recording.spike is not None and len(recording.spike) > 0
|
|
95
|
+
|
|
96
|
+
# plotting
|
|
97
|
+
outpath = plot_trace(
|
|
98
|
+
recording,
|
|
99
|
+
out_dir,
|
|
100
|
+
fname="spiking_test.pdf",
|
|
101
|
+
title="Spiking Test - Step at 200% of Rheobase",
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
return {
|
|
105
|
+
"skipped": False,
|
|
106
|
+
"passed": passed,
|
|
107
|
+
"figures": [outpath],
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def depolarization_block_test(cell, rheobase, out_dir):
|
|
112
|
+
"""Depolarization block test: no depolarization block should be detected."""
|
|
113
|
+
# Run the stimulus
|
|
114
|
+
stim_factory = StimulusFactory(dt=1.0)
|
|
115
|
+
step_stimulus = stim_factory.idrest(threshold_current=rheobase, threshold_percentage=200)
|
|
116
|
+
recording = run_stimulus(
|
|
117
|
+
cell.template_params,
|
|
118
|
+
step_stimulus,
|
|
119
|
+
"soma[0]",
|
|
120
|
+
0.5,
|
|
121
|
+
add_hypamp=True,
|
|
122
|
+
enable_spike_detection=False,
|
|
123
|
+
)
|
|
124
|
+
# Check for depolarization block
|
|
125
|
+
trace = {
|
|
126
|
+
"T": recording.time,
|
|
127
|
+
"V": recording.voltage,
|
|
128
|
+
"stim_start": [IDRestTimings.PRE_DELAY.value],
|
|
129
|
+
"stim_end": [IDRestTimings.PRE_DELAY.value + IDRestTimings.DURATION.value],
|
|
130
|
+
}
|
|
131
|
+
features_results = efel.get_feature_values([trace], ["depol_block_bool"])
|
|
132
|
+
depol_block = bool(features_results[0]["depol_block_bool"][0])
|
|
133
|
+
|
|
134
|
+
# plotting
|
|
135
|
+
outpath = plot_trace(
|
|
136
|
+
recording,
|
|
137
|
+
out_dir,
|
|
138
|
+
fname="depolarization_block_test.pdf",
|
|
139
|
+
title="Depolarization Block Test - Step at 200% of Rheobase",
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
return {
|
|
143
|
+
"skipped": False,
|
|
144
|
+
"passed": not depol_block,
|
|
145
|
+
"figures": [outpath],
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def ais_spiking_test(cell, rheobase, out_dir, spike_threshold_voltage=-30.):
|
|
150
|
+
"""AIS spiking test: axon should spike before soma."""
|
|
151
|
+
# Check that the cell has an axon
|
|
152
|
+
if len(cell.axonal) == 0:
|
|
153
|
+
return {
|
|
154
|
+
"skipped": True,
|
|
155
|
+
"passed": False,
|
|
156
|
+
"figures": [],
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
# Run the stimulus
|
|
160
|
+
stim_factory = StimulusFactory(dt=1.0)
|
|
161
|
+
step_stimulus = stim_factory.idrest(threshold_current=rheobase, threshold_percentage=200)
|
|
162
|
+
recordings = run_multirecordings_stimulus(
|
|
163
|
+
cell.template_params,
|
|
164
|
+
step_stimulus,
|
|
165
|
+
"soma[0]",
|
|
166
|
+
0.5,
|
|
167
|
+
add_hypamp=True,
|
|
168
|
+
recording_locations=[("axon[0]", 0.5), ("soma[0]", 0.5)],
|
|
169
|
+
enable_spike_detection=True,
|
|
170
|
+
threshold_spike_detection=spike_threshold_voltage,
|
|
171
|
+
)
|
|
172
|
+
axon_recording, soma_recording = recordings
|
|
173
|
+
|
|
174
|
+
# plotting
|
|
175
|
+
outpath1 = plot_traces(
|
|
176
|
+
recordings,
|
|
177
|
+
out_dir,
|
|
178
|
+
fname="ais_spiking_test.pdf",
|
|
179
|
+
title="AIS Spiking Test - Step at 200% of Rheobase",
|
|
180
|
+
labels=["axon[0]", "soma[0]"],
|
|
181
|
+
)
|
|
182
|
+
outpath2 = plot_traces(
|
|
183
|
+
recordings,
|
|
184
|
+
out_dir,
|
|
185
|
+
fname="ais_spiking_test_zoomed.pdf",
|
|
186
|
+
title="AIS Spiking Test - Step at 200% of Rheobase (zoomed)",
|
|
187
|
+
labels=["axon[0]", "soma[0]"],
|
|
188
|
+
xlim=(IDRestTimings.PRE_DELAY.value, IDRestTimings.PRE_DELAY.value + 100),
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
# Check for spiking
|
|
192
|
+
for recording in recordings:
|
|
193
|
+
if recording.spike is None or len(recording.spike) == 0:
|
|
194
|
+
return {
|
|
195
|
+
"skipped": False,
|
|
196
|
+
"passed": False,
|
|
197
|
+
"figures": [outpath1, outpath2],
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
# Check if axon spike happens before soma spike
|
|
201
|
+
passed = bool(axon_recording.spike[0] < soma_recording.spike[0])
|
|
202
|
+
return {
|
|
203
|
+
"skipped": False,
|
|
204
|
+
"passed": passed,
|
|
205
|
+
"figures": [outpath1, outpath2],
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
def hyperpolarization_test(cell, rheobase, out_dir):
|
|
210
|
+
"""Hyperpolarization test: hyperpolarized voltage should be lower than RMP."""
|
|
211
|
+
# Run the stimulus
|
|
212
|
+
stim_factory = StimulusFactory(dt=1.0)
|
|
213
|
+
step_stimulus = stim_factory.iv(threshold_current=rheobase, threshold_percentage=-40)
|
|
214
|
+
recording = run_stimulus(
|
|
215
|
+
cell.template_params,
|
|
216
|
+
step_stimulus,
|
|
217
|
+
"soma[0]",
|
|
218
|
+
0.5,
|
|
219
|
+
add_hypamp=True,
|
|
220
|
+
enable_spike_detection=False,
|
|
221
|
+
)
|
|
222
|
+
|
|
223
|
+
# plotting
|
|
224
|
+
outpath = plot_trace(
|
|
225
|
+
recording,
|
|
226
|
+
out_dir,
|
|
227
|
+
fname="hyperpolarization_test.pdf",
|
|
228
|
+
title="Hyperpolarization Test - Step at -40% of Rheobase",
|
|
229
|
+
)
|
|
230
|
+
|
|
231
|
+
# Check for hyperpolarization
|
|
232
|
+
trace = {
|
|
233
|
+
"T": recording.time,
|
|
234
|
+
"V": recording.voltage,
|
|
235
|
+
"stim_start": [IDRestTimings.PRE_DELAY.value],
|
|
236
|
+
"stim_end": [IDRestTimings.PRE_DELAY.value + IDRestTimings.DURATION.value],
|
|
237
|
+
}
|
|
238
|
+
features_results = efel.get_feature_values([trace], ["voltage_base", "steady_state_voltage_stimend"])
|
|
239
|
+
rmp = features_results[0]["voltage_base"][0]
|
|
240
|
+
ss_voltage = features_results[0]["steady_state_voltage_stimend"][0]
|
|
241
|
+
if rmp is None or ss_voltage is None:
|
|
242
|
+
return {
|
|
243
|
+
"skipped": False,
|
|
244
|
+
"passed": False,
|
|
245
|
+
"figures": [outpath],
|
|
246
|
+
}
|
|
247
|
+
hyperpol_bool = bool(ss_voltage < rmp)
|
|
248
|
+
|
|
249
|
+
return {
|
|
250
|
+
"skipped": False,
|
|
251
|
+
"passed": hyperpol_bool,
|
|
252
|
+
"figures": [outpath],
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
def rin_test(rin):
|
|
257
|
+
"""Rin should have an acceptable biological range (< 1000 MOhm)"""
|
|
258
|
+
passed = bool(rin < 1000)
|
|
259
|
+
|
|
260
|
+
return {
|
|
261
|
+
"skipped": False,
|
|
262
|
+
"passed": passed,
|
|
263
|
+
"figures": [],
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
def iv_test(cell, rheobase, out_dir, spike_threshold_voltage=-30.):
|
|
268
|
+
"""IV curve should have a positive slope."""
|
|
269
|
+
amps, steady_states = compute_plot_iv_curve(
|
|
270
|
+
cell,
|
|
271
|
+
rheobase=rheobase,
|
|
272
|
+
threshold_voltage=spike_threshold_voltage,
|
|
273
|
+
nb_bins=5,
|
|
274
|
+
show_figure=False,
|
|
275
|
+
save_figure=True,
|
|
276
|
+
output_dir=out_dir,
|
|
277
|
+
output_fname="iv_curve.pdf")
|
|
278
|
+
|
|
279
|
+
outpath = pathlib.Path(out_dir) / "iv_curve.pdf"
|
|
280
|
+
|
|
281
|
+
# Check for positive slope
|
|
282
|
+
if len(amps) < 2 or len(steady_states) < 2:
|
|
283
|
+
return {
|
|
284
|
+
"skipped": False,
|
|
285
|
+
"passed": False,
|
|
286
|
+
"figures": [outpath],
|
|
287
|
+
}
|
|
288
|
+
slope = numpy.polyfit(amps, steady_states, 1)[0]
|
|
289
|
+
passed = bool(slope > 0)
|
|
290
|
+
return {
|
|
291
|
+
"skipped": False,
|
|
292
|
+
"passed": passed,
|
|
293
|
+
"figures": [outpath],
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
def fi_test(cell, rheobase, out_dir, spike_threshold_voltage=-30.):
|
|
298
|
+
"""FI curve should have a positive slope."""
|
|
299
|
+
amps, spike_counts = compute_plot_fi_curve(
|
|
300
|
+
cell,
|
|
301
|
+
rheobase=rheobase,
|
|
302
|
+
threshold_voltage=spike_threshold_voltage,
|
|
303
|
+
nb_bins=5,
|
|
304
|
+
show_figure=False,
|
|
305
|
+
save_figure=True,
|
|
306
|
+
output_dir=out_dir,
|
|
307
|
+
output_fname="fi_curve.pdf")
|
|
308
|
+
|
|
309
|
+
outpath = pathlib.Path(out_dir) / "fi_curve.pdf"
|
|
310
|
+
|
|
311
|
+
# Check for positive slope
|
|
312
|
+
if len(amps) < 2 or len(spike_counts) < 2:
|
|
313
|
+
return {
|
|
314
|
+
"skipped": False,
|
|
315
|
+
"passed": False,
|
|
316
|
+
"figures": [outpath],
|
|
317
|
+
}
|
|
318
|
+
slope = numpy.polyfit(amps, spike_counts, 1)[0]
|
|
319
|
+
passed = bool(slope > 0)
|
|
320
|
+
return {
|
|
321
|
+
"skipped": False,
|
|
322
|
+
"passed": passed,
|
|
323
|
+
"figures": [outpath],
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
def run_validations(cell, cell_name, spike_threshold_voltage=-30):
|
|
328
|
+
"""Run all the validations on the cell.
|
|
329
|
+
|
|
330
|
+
Args:
|
|
331
|
+
cell (Cell): The cell to validate.
|
|
332
|
+
cell_name (str): The name of the cell, used in the output directory.
|
|
333
|
+
spike_threshold_voltage (float): The voltage threshold for spike detection.
|
|
334
|
+
"""
|
|
335
|
+
out_dir = pathlib.Path("memodel_validation_figures") / cell_name
|
|
336
|
+
out_dir.mkdir(parents=True, exist_ok=True)
|
|
337
|
+
|
|
338
|
+
# cell = Cell.from_template_parameters(template_params)
|
|
339
|
+
# get me-model properties
|
|
340
|
+
holding_current = cell.hypamp if cell.hypamp else 0.0
|
|
341
|
+
if cell.threshold:
|
|
342
|
+
rheobase = cell.threshold
|
|
343
|
+
else:
|
|
344
|
+
rheobase = calculate_rheobase(
|
|
345
|
+
cell=cell, section="soma[0]", segx=0.5, threshold_voltage=spike_threshold_voltage
|
|
346
|
+
)
|
|
347
|
+
rin = calculate_input_resistance(
|
|
348
|
+
template_path=cell.template_params.template_filepath,
|
|
349
|
+
morphology_path=cell.template_params.morph_filepath,
|
|
350
|
+
template_format=cell.template_params.template_format,
|
|
351
|
+
emodel_properties=cell.template_params.emodel_properties,
|
|
352
|
+
)
|
|
353
|
+
|
|
354
|
+
# Validation 1: Spiking Test
|
|
355
|
+
logger.debug("Running spiking test")
|
|
356
|
+
spiking_test_result = spiking_test(cell, rheobase, out_dir, spike_threshold_voltage)
|
|
357
|
+
|
|
358
|
+
# Validation 2: Depolarization Block Test
|
|
359
|
+
logger.debug("Running depolarization block test")
|
|
360
|
+
depolarization_block_result = depolarization_block_test(cell, rheobase, out_dir)
|
|
361
|
+
|
|
362
|
+
# Validation 3: Backpropagating AP Test
|
|
363
|
+
# logger.debug("Running backpropagating AP test")
|
|
364
|
+
|
|
365
|
+
# Validation 4: Postsynaptic Potential Test
|
|
366
|
+
# logger.debug("Running postsynaptic potential test")
|
|
367
|
+
|
|
368
|
+
# Validation 5: AIS Spiking Test
|
|
369
|
+
logger.debug("Running AIS spiking test")
|
|
370
|
+
ais_spiking_test_result = ais_spiking_test(cell, rheobase, out_dir, spike_threshold_voltage)
|
|
371
|
+
|
|
372
|
+
# Validation 6: Hyperpolarization Test
|
|
373
|
+
logger.debug("Running hyperpolarization test")
|
|
374
|
+
hyperpolarization_result = hyperpolarization_test(cell, rheobase, out_dir)
|
|
375
|
+
|
|
376
|
+
# Validation 7: Rin Test
|
|
377
|
+
logger.debug("Running Rin test")
|
|
378
|
+
rin_result = rin_test(rin)
|
|
379
|
+
|
|
380
|
+
# Validation 8: IV Test
|
|
381
|
+
logger.debug("Running IV test")
|
|
382
|
+
iv_test_result = iv_test(cell, rheobase, out_dir, spike_threshold_voltage)
|
|
383
|
+
|
|
384
|
+
# Validation 9: FI Test
|
|
385
|
+
logger.debug("Running FI test")
|
|
386
|
+
fi_test_result = fi_test(cell, rheobase, out_dir, spike_threshold_voltage)
|
|
387
|
+
|
|
388
|
+
return {
|
|
389
|
+
"memodel_properties": {
|
|
390
|
+
"holding_current": holding_current,
|
|
391
|
+
"rheobase": rheobase,
|
|
392
|
+
"rin": rin,
|
|
393
|
+
},
|
|
394
|
+
"spiking_test": spiking_test_result,
|
|
395
|
+
"depolarization_block_test": depolarization_block_result,
|
|
396
|
+
"ais_spiking_test": ais_spiking_test_result,
|
|
397
|
+
"hyperpolarization_test": hyperpolarization_result,
|
|
398
|
+
"rin_test": rin_result,
|
|
399
|
+
"iv_test": iv_test_result,
|
|
400
|
+
"fi_test": fi_test_result,
|
|
401
|
+
}
|
|
@@ -10,14 +10,14 @@ bluecellulab/plotwindow.py,sha256=ePU-EegZ1Sqk0SoYYiQ6k24ZO4s3Hgfpx10uePiJ5xM,27
|
|
|
10
10
|
bluecellulab/psection.py,sha256=FSOwRNuOTyB469BM-jPEf9l1J59FamXmzrQgBI6cnP4,6174
|
|
11
11
|
bluecellulab/psegment.py,sha256=PTgoGLqM4oFIdF_8QHFQCU59j-8TQmtq6PakiGUQhIo,3138
|
|
12
12
|
bluecellulab/rngsettings.py,sha256=2Ykb4Ylk3XTs58x1UIxjg8XJqjSpnCgKRZ8avXCDpxk,4237
|
|
13
|
-
bluecellulab/tools.py,sha256=
|
|
13
|
+
bluecellulab/tools.py,sha256=hF1HJcera0oggetsfN5PNTRYCpFS0sQiZlVxw4jRd1g,17968
|
|
14
14
|
bluecellulab/type_aliases.py,sha256=DvgjERv2Ztdw_sW63JrZTQGpJ0x5uMTFB5hcBHDb0WA,441
|
|
15
15
|
bluecellulab/utils.py,sha256=SbOOkzw1YGjCKV3qOw0zpabNEy7V9BRtgMLsQJiFRq4,1526
|
|
16
16
|
bluecellulab/verbosity.py,sha256=T0IgX7DrRo19faxrT4Xzb27gqxzoILQ8FzYKxvUeaPM,1342
|
|
17
17
|
bluecellulab/analysis/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
18
|
-
bluecellulab/analysis/analysis.py,sha256=
|
|
19
|
-
bluecellulab/analysis/inject_sequence.py,sha256=
|
|
20
|
-
bluecellulab/analysis/plotting.py,sha256
|
|
18
|
+
bluecellulab/analysis/analysis.py,sha256=3gDJRau5SL6wOhAUJ16vqP7aYLtoAZ7f1qZbSUVoUU8,10818
|
|
19
|
+
bluecellulab/analysis/inject_sequence.py,sha256=uU6Q6y0ErMDDEgdsTZBXCJ4LgwkATY7G8Fa6mmjpL98,12523
|
|
20
|
+
bluecellulab/analysis/plotting.py,sha256=PqRoaZz33ULMw8A9YnZXXrxcUd84M_dwlYMTFhG7YT4,3999
|
|
21
21
|
bluecellulab/cell/__init__.py,sha256=Sbc0QOsJ8E7tSwf3q7fsXuE_SevBN6ZmoCVyyU5zfII,208
|
|
22
22
|
bluecellulab/cell/cell_dict.py,sha256=VE7pi-NsMVRSmo-PSdbiLYmolDOu0Gc6JxFBkuQpFdk,1346
|
|
23
23
|
bluecellulab/cell/core.py,sha256=BualY0WOtC0arKmvKzQ3sQ0fhc2jrtd6wqSd9M-e25E,33797
|
|
@@ -60,14 +60,15 @@ bluecellulab/simulation/parallel.py,sha256=oQ_oV2EKr8gP4yF2Dq8q9MiblDyi89_wBgLzQ
|
|
|
60
60
|
bluecellulab/simulation/simulation.py,sha256=VhftOMYU1Rfrphvud6f0U4kvbUivSviQ5TlVljuTb88,6486
|
|
61
61
|
bluecellulab/stimulus/__init__.py,sha256=DgIgVaSyR-URf3JZzvO6j-tjCerzvktuK-ep8pjMRPQ,37
|
|
62
62
|
bluecellulab/stimulus/circuit_stimulus_definitions.py,sha256=dTpwfRxH4c4yJDYyKrO6X-2Nqdy-QT3GxGQjfsRhNVY,17158
|
|
63
|
-
bluecellulab/stimulus/factory.py,sha256=
|
|
63
|
+
bluecellulab/stimulus/factory.py,sha256=4fvVFFjOGHSqBidLe_W1zQozfMEeePXWO6yYCs30-SM,30780
|
|
64
64
|
bluecellulab/stimulus/stimulus.py,sha256=a_hKJUtZmIgjiFjbJf6RzUPokELqn0IHCgIWGw5XLm8,30322
|
|
65
65
|
bluecellulab/synapse/__init__.py,sha256=RW8XoAMXOvK7OG1nHl_q8jSEKLj9ZN4oWf2nY9HAwuk,192
|
|
66
66
|
bluecellulab/synapse/synapse_factory.py,sha256=NHwRMYMrnRVm_sHmyKTJ1bdoNmWZNU4UPOGu7FCi-PE,6987
|
|
67
67
|
bluecellulab/synapse/synapse_types.py,sha256=zs_yBvGTH4QrbQF3nEViidyq1WM_ZcTSFdjUxB3khW0,16871
|
|
68
|
-
bluecellulab
|
|
69
|
-
bluecellulab-2.6.
|
|
70
|
-
bluecellulab-2.6.
|
|
71
|
-
bluecellulab-2.6.
|
|
72
|
-
bluecellulab-2.6.
|
|
73
|
-
bluecellulab-2.6.
|
|
68
|
+
bluecellulab/validation/validation.py,sha256=iGOgtgpZ3Yid1VlxsCycS14a0qRkP42zhj4WFPCBX4Q,13017
|
|
69
|
+
bluecellulab-2.6.48.dist-info/licenses/AUTHORS.txt,sha256=EDs3H-2HXBojbma10psixk3C2rFiOCTIREi2ZAbXYNQ,179
|
|
70
|
+
bluecellulab-2.6.48.dist-info/licenses/LICENSE,sha256=dAMAR2Sud4Nead1wGFleKiwTZfkTNZbzmuGfcTKb3kg,11335
|
|
71
|
+
bluecellulab-2.6.48.dist-info/METADATA,sha256=a7gi6zv56VtPk9dN6g2qWN926WnR3VdX-_7T1SvT2h4,8236
|
|
72
|
+
bluecellulab-2.6.48.dist-info/WHEEL,sha256=Nw36Djuh_5VDukK0H78QzOX-_FQEo6V37m3nkm96gtU,91
|
|
73
|
+
bluecellulab-2.6.48.dist-info/top_level.txt,sha256=VSyEP8w9l3pXdRkyP_goeMwiNA8KWwitfAqUkveJkdQ,13
|
|
74
|
+
bluecellulab-2.6.48.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|