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.

@@ -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=-30,
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 = calculate_rheobase(cell=cell, section=injecting_section, segx=injecting_segment)
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
- nb_bins=11):
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 = calculate_rheobase(cell=cell, section=injecting_section, segx=injecting_segment)
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 run_stimulus(
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
- recording_section: str = "soma[0]",
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 = -30.0,
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
- recording_section (str): Name of the section of the cell where voltage is recorded.
70
- recording_segment (float): The normalized position (0.0 to 1.0) along the recording
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 -30 mV.
75
+ Defaults to -20 mV.
76
76
 
77
77
  Returns:
78
- Recording: A `Recording` object containing the following:
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
- validate_section_and_segment(cell, recording_section, recording_segment)
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
- cell.add_voltage_recording(cell.sections[recording_section], recording_segment)
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
- recording_location = f"{recording_section}({str(recording_segment)})"
103
- cell.start_recording_spikes(None, location=recording_location, threshold=threshold_spike_detection)
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
- voltage = cell.get_voltage_recording(cell.sections[recording_section], recording_segment)
118
- time = cell.get_time()
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
- if len(time) != len(voltage) or len(time) != len(current):
121
- raise ValueError("Time, current, and voltage arrays are not the same length")
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
- if enable_spike_detection:
124
- results = cell.get_recorded_spikes(location=recording_location, threshold=threshold_spike_detection)
125
- if results is not None:
126
- spikes = np.array(results)
127
- else:
128
- spikes = None
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 Recording(current=current, voltage=voltage, time=time, spike=spikes)
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(currents, voltages, injecting_section, injecting_segment, recording_section, recording_segment):
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
- plt.show()
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
- def plot_fi_curve(currents, spike_count, injecting_section, injecting_segment, recording_section, recording_segment):
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
- plt.show()
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()
@@ -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=pre_delay,
100
- duration=duration,
101
- post_delay=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=pre_delay,
109
- duration=duration,
110
- post_delay=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=pre_delay,
143
- duration=duration,
144
- post_delay=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=pre_delay,
152
- duration=duration,
153
- post_delay=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=pre_delay,
186
- duration=duration,
187
- post_delay=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=pre_delay,
195
- duration=duration,
196
- post_delay=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=pre_delay,
229
- duration=duration,
230
- post_delay=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=pre_delay,
238
- duration=duration,
239
- post_delay=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=delay)
278
- + Slope(self.dt, duration=ramp1_duration, amplitude_start=0.0, amplitude_end=amplitude)
279
- + Slope(self.dt, duration=ramp1_duration, amplitude_start=amplitude, amplitude_end=0.0)
280
- + Empty(self.dt, duration=inter_delay)
281
- + Slope(self.dt, duration=ramp2_duration, amplitude_start=0.0, amplitude_end=amplitude)
282
- + Slope(self.dt, duration=ramp2_duration, amplitude_start=amplitude, amplitude_end=0.0)
283
- + Empty(self.dt, duration=inter_delay)
284
- + Slope(self.dt, duration=ramp3_duration, amplitude_start=0.0, amplitude_end=amplitude)
285
- + Slope(self.dt, duration=ramp3_duration, amplitude_start=amplitude, amplitude_end=0.0)
286
- + Empty(self.dt, duration=post_delay)
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=delay)
322
- + Slope(self.dt, duration=ramp1_duration, amplitude_start=0.0, amplitude_end=amplitude)
323
- + Slope(self.dt, duration=ramp1_duration, amplitude_start=amplitude, amplitude_end=0.0)
324
- + Empty(self.dt, duration=inter_delay)
325
- + Slope(self.dt, duration=ramp2_duration, amplitude_start=0.0, amplitude_end=amplitude)
326
- + Slope(self.dt, duration=ramp2_duration, amplitude_start=amplitude, amplitude_end=0.0)
327
- + Empty(self.dt, duration=inter_delay)
328
- + Slope(self.dt, duration=ramp3_duration, amplitude_start=0.0, amplitude_end=amplitude)
329
- + Slope(self.dt, duration=ramp3_duration, amplitude_start=amplitude, amplitude_end=0.0)
330
- + Empty(self.dt, duration=post_delay)
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=duration,
362
- post_delay=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=duration,
371
- post_delay=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 = -30.0,
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 -30.0 mV.
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
+ }
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bluecellulab
3
- Version: 2.6.47
3
+ Version: 2.6.48
4
4
  Summary: Biologically detailed neural network simulations and analysis.
5
5
  Author: Blue Brain Project, EPFL
6
6
  License: Apache2.0
@@ -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=yy2_XBANaCQEqBeCIRiQfWJUidth9_igldVOSuSp5l4,18018
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=ItRtUeXeIoYN6gkSZh-U5lBv-plClNZECU6y6YlaNCI,8615
19
- bluecellulab/analysis/inject_sequence.py,sha256=ehYSXFgz7do9-zQjiGS2WVAiEmcjM24mWQFQty9pWDw,9012
20
- bluecellulab/analysis/plotting.py,sha256=-ArjWHZsAlUoNSu_t0HQjQmFhGI1tCQtdAoICgwstFs,2588
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=K8d8krPr12tchYcqGrKzm-QOdOZh9QKwcml1Mk90M1M,29359
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-2.6.47.dist-info/licenses/AUTHORS.txt,sha256=EDs3H-2HXBojbma10psixk3C2rFiOCTIREi2ZAbXYNQ,179
69
- bluecellulab-2.6.47.dist-info/licenses/LICENSE,sha256=dAMAR2Sud4Nead1wGFleKiwTZfkTNZbzmuGfcTKb3kg,11335
70
- bluecellulab-2.6.47.dist-info/METADATA,sha256=rydGOCvn6Y2v-KNPtssf3Au4IxaW2B_mo5nT5hf1QXA,8236
71
- bluecellulab-2.6.47.dist-info/WHEEL,sha256=0CuiUZ_p9E4cD6NyLD6UG80LBXYyiSYZOKDm5lp32xk,91
72
- bluecellulab-2.6.47.dist-info/top_level.txt,sha256=VSyEP8w9l3pXdRkyP_goeMwiNA8KWwitfAqUkveJkdQ,13
73
- bluecellulab-2.6.47.dist-info/RECORD,,
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,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.3.1)
2
+ Generator: setuptools (80.7.1)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5