bluecellulab 2.6.40__tar.gz → 2.6.42__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of bluecellulab might be problematic. Click here for more details.

Files changed (107) hide show
  1. {bluecellulab-2.6.40 → bluecellulab-2.6.42}/PKG-INFO +2 -1
  2. {bluecellulab-2.6.40 → bluecellulab-2.6.42}/README.rst +1 -0
  3. bluecellulab-2.6.42/bluecellulab/analysis/analysis.py +177 -0
  4. {bluecellulab-2.6.40 → bluecellulab-2.6.42}/bluecellulab/analysis/inject_sequence.py +69 -16
  5. bluecellulab-2.6.42/bluecellulab/analysis/plotting.py +57 -0
  6. {bluecellulab-2.6.40 → bluecellulab-2.6.42}/bluecellulab/cell/core.py +64 -9
  7. {bluecellulab-2.6.40 → bluecellulab-2.6.42}/bluecellulab/tools.py +105 -13
  8. {bluecellulab-2.6.40 → bluecellulab-2.6.42}/bluecellulab.egg-info/PKG-INFO +2 -1
  9. bluecellulab-2.6.40/bluecellulab/analysis/analysis.py +0 -63
  10. bluecellulab-2.6.40/bluecellulab/analysis/plotting.py +0 -25
  11. {bluecellulab-2.6.40 → bluecellulab-2.6.42}/.compile_mod.sh +0 -0
  12. {bluecellulab-2.6.40 → bluecellulab-2.6.42}/.gitattributes +0 -0
  13. {bluecellulab-2.6.40 → bluecellulab-2.6.42}/.github/dependabot.yml +0 -0
  14. {bluecellulab-2.6.40 → bluecellulab-2.6.42}/.github/workflows/release.yml +0 -0
  15. {bluecellulab-2.6.40 → bluecellulab-2.6.42}/.github/workflows/test.yml +0 -0
  16. {bluecellulab-2.6.40 → bluecellulab-2.6.42}/.gitignore +0 -0
  17. {bluecellulab-2.6.40 → bluecellulab-2.6.42}/.gitlab-ci.yml +0 -0
  18. {bluecellulab-2.6.40 → bluecellulab-2.6.42}/.readthedocs.yml +0 -0
  19. {bluecellulab-2.6.40 → bluecellulab-2.6.42}/.zenodo.json +0 -0
  20. {bluecellulab-2.6.40 → bluecellulab-2.6.42}/AUTHORS.txt +0 -0
  21. {bluecellulab-2.6.40 → bluecellulab-2.6.42}/CHANGELOG.rst +0 -0
  22. {bluecellulab-2.6.40 → bluecellulab-2.6.42}/CITATION.cff +0 -0
  23. {bluecellulab-2.6.40 → bluecellulab-2.6.42}/CONTRIBUTING.rst +0 -0
  24. {bluecellulab-2.6.40 → bluecellulab-2.6.42}/LICENSE +0 -0
  25. {bluecellulab-2.6.40 → bluecellulab-2.6.42}/MANIFEST.in +0 -0
  26. {bluecellulab-2.6.40 → bluecellulab-2.6.42}/Makefile +0 -0
  27. {bluecellulab-2.6.40 → bluecellulab-2.6.42}/bluecellulab/__init__.py +0 -0
  28. {bluecellulab-2.6.40 → bluecellulab-2.6.42}/bluecellulab/analysis/__init__.py +0 -0
  29. {bluecellulab-2.6.40 → bluecellulab-2.6.42}/bluecellulab/cell/__init__.py +0 -0
  30. {bluecellulab-2.6.40 → bluecellulab-2.6.42}/bluecellulab/cell/ballstick/__init__.py +0 -0
  31. {bluecellulab-2.6.40 → bluecellulab-2.6.42}/bluecellulab/cell/ballstick/emodel.hoc +0 -0
  32. {bluecellulab-2.6.40 → bluecellulab-2.6.42}/bluecellulab/cell/ballstick/morphology.asc +0 -0
  33. {bluecellulab-2.6.40 → bluecellulab-2.6.42}/bluecellulab/cell/cell_dict.py +0 -0
  34. {bluecellulab-2.6.40 → bluecellulab-2.6.42}/bluecellulab/cell/injector.py +0 -0
  35. {bluecellulab-2.6.40 → bluecellulab-2.6.42}/bluecellulab/cell/plotting.py +0 -0
  36. {bluecellulab-2.6.40 → bluecellulab-2.6.42}/bluecellulab/cell/random.py +0 -0
  37. {bluecellulab-2.6.40 → bluecellulab-2.6.42}/bluecellulab/cell/recording.py +0 -0
  38. {bluecellulab-2.6.40 → bluecellulab-2.6.42}/bluecellulab/cell/section_distance.py +0 -0
  39. {bluecellulab-2.6.40 → bluecellulab-2.6.42}/bluecellulab/cell/serialized_sections.py +0 -0
  40. {bluecellulab-2.6.40 → bluecellulab-2.6.42}/bluecellulab/cell/sonata_proxy.py +0 -0
  41. {bluecellulab-2.6.40 → bluecellulab-2.6.42}/bluecellulab/cell/stimuli_generator.py +0 -0
  42. {bluecellulab-2.6.40 → bluecellulab-2.6.42}/bluecellulab/cell/template.py +0 -0
  43. {bluecellulab-2.6.40 → bluecellulab-2.6.42}/bluecellulab/circuit/__init__.py +0 -0
  44. {bluecellulab-2.6.40 → bluecellulab-2.6.42}/bluecellulab/circuit/circuit_access/__init__.py +0 -0
  45. {bluecellulab-2.6.40 → bluecellulab-2.6.42}/bluecellulab/circuit/circuit_access/bluepy_circuit_access.py +0 -0
  46. {bluecellulab-2.6.40 → bluecellulab-2.6.42}/bluecellulab/circuit/circuit_access/definition.py +0 -0
  47. {bluecellulab-2.6.40 → bluecellulab-2.6.42}/bluecellulab/circuit/circuit_access/sonata_circuit_access.py +0 -0
  48. {bluecellulab-2.6.40 → bluecellulab-2.6.42}/bluecellulab/circuit/config/__init__.py +0 -0
  49. {bluecellulab-2.6.40 → bluecellulab-2.6.42}/bluecellulab/circuit/config/bluepy_simulation_config.py +0 -0
  50. {bluecellulab-2.6.40 → bluecellulab-2.6.42}/bluecellulab/circuit/config/definition.py +0 -0
  51. {bluecellulab-2.6.40 → bluecellulab-2.6.42}/bluecellulab/circuit/config/sections.py +0 -0
  52. {bluecellulab-2.6.40 → bluecellulab-2.6.42}/bluecellulab/circuit/config/sonata_simulation_config.py +0 -0
  53. {bluecellulab-2.6.40 → bluecellulab-2.6.42}/bluecellulab/circuit/format.py +0 -0
  54. {bluecellulab-2.6.40 → bluecellulab-2.6.42}/bluecellulab/circuit/iotools.py +0 -0
  55. {bluecellulab-2.6.40 → bluecellulab-2.6.42}/bluecellulab/circuit/node_id.py +0 -0
  56. {bluecellulab-2.6.40 → bluecellulab-2.6.42}/bluecellulab/circuit/simulation_access.py +0 -0
  57. {bluecellulab-2.6.40 → bluecellulab-2.6.42}/bluecellulab/circuit/synapse_properties.py +0 -0
  58. {bluecellulab-2.6.40 → bluecellulab-2.6.42}/bluecellulab/circuit/validate.py +0 -0
  59. {bluecellulab-2.6.40 → bluecellulab-2.6.42}/bluecellulab/circuit_simulation.py +0 -0
  60. {bluecellulab-2.6.40 → bluecellulab-2.6.42}/bluecellulab/connection.py +0 -0
  61. {bluecellulab-2.6.40 → bluecellulab-2.6.42}/bluecellulab/dendrogram.py +0 -0
  62. {bluecellulab-2.6.40 → bluecellulab-2.6.42}/bluecellulab/exceptions.py +0 -0
  63. {bluecellulab-2.6.40 → bluecellulab-2.6.42}/bluecellulab/graph.py +0 -0
  64. {bluecellulab-2.6.40 → bluecellulab-2.6.42}/bluecellulab/hoc/Cell.hoc +0 -0
  65. {bluecellulab-2.6.40 → bluecellulab-2.6.42}/bluecellulab/hoc/RNGSettings.hoc +0 -0
  66. {bluecellulab-2.6.40 → bluecellulab-2.6.42}/bluecellulab/hoc/TDistFunc.hoc +0 -0
  67. {bluecellulab-2.6.40 → bluecellulab-2.6.42}/bluecellulab/hoc/TStim.hoc +0 -0
  68. {bluecellulab-2.6.40 → bluecellulab-2.6.42}/bluecellulab/hoc/fileUtils.hoc +0 -0
  69. {bluecellulab-2.6.40 → bluecellulab-2.6.42}/bluecellulab/importer.py +0 -0
  70. {bluecellulab-2.6.40 → bluecellulab-2.6.42}/bluecellulab/neuron_interpreter.py +0 -0
  71. {bluecellulab-2.6.40 → bluecellulab-2.6.42}/bluecellulab/plotwindow.py +0 -0
  72. {bluecellulab-2.6.40 → bluecellulab-2.6.42}/bluecellulab/psection.py +0 -0
  73. {bluecellulab-2.6.40 → bluecellulab-2.6.42}/bluecellulab/psegment.py +0 -0
  74. {bluecellulab-2.6.40 → bluecellulab-2.6.42}/bluecellulab/rngsettings.py +0 -0
  75. {bluecellulab-2.6.40 → bluecellulab-2.6.42}/bluecellulab/simulation/__init__.py +0 -0
  76. {bluecellulab-2.6.40 → bluecellulab-2.6.42}/bluecellulab/simulation/neuron_globals.py +0 -0
  77. {bluecellulab-2.6.40 → bluecellulab-2.6.42}/bluecellulab/simulation/parallel.py +0 -0
  78. {bluecellulab-2.6.40 → bluecellulab-2.6.42}/bluecellulab/simulation/simulation.py +0 -0
  79. {bluecellulab-2.6.40 → bluecellulab-2.6.42}/bluecellulab/stimulus/__init__.py +0 -0
  80. {bluecellulab-2.6.40 → bluecellulab-2.6.42}/bluecellulab/stimulus/circuit_stimulus_definitions.py +0 -0
  81. {bluecellulab-2.6.40 → bluecellulab-2.6.42}/bluecellulab/stimulus/factory.py +0 -0
  82. {bluecellulab-2.6.40 → bluecellulab-2.6.42}/bluecellulab/synapse/__init__.py +0 -0
  83. {bluecellulab-2.6.40 → bluecellulab-2.6.42}/bluecellulab/synapse/synapse_factory.py +0 -0
  84. {bluecellulab-2.6.40 → bluecellulab-2.6.42}/bluecellulab/synapse/synapse_types.py +0 -0
  85. {bluecellulab-2.6.40 → bluecellulab-2.6.42}/bluecellulab/type_aliases.py +0 -0
  86. {bluecellulab-2.6.40 → bluecellulab-2.6.42}/bluecellulab/utils.py +0 -0
  87. {bluecellulab-2.6.40 → bluecellulab-2.6.42}/bluecellulab/verbosity.py +0 -0
  88. {bluecellulab-2.6.40 → bluecellulab-2.6.42}/bluecellulab.egg-info/SOURCES.txt +0 -0
  89. {bluecellulab-2.6.40 → bluecellulab-2.6.42}/bluecellulab.egg-info/dependency_links.txt +0 -0
  90. {bluecellulab-2.6.40 → bluecellulab-2.6.42}/bluecellulab.egg-info/requires.txt +0 -0
  91. {bluecellulab-2.6.40 → bluecellulab-2.6.42}/bluecellulab.egg-info/top_level.txt +0 -0
  92. {bluecellulab-2.6.40 → bluecellulab-2.6.42}/docs/Makefile +0 -0
  93. {bluecellulab-2.6.40 → bluecellulab-2.6.42}/docs/images/voltage-readme.png +0 -0
  94. {bluecellulab-2.6.40 → bluecellulab-2.6.42}/docs/make.bat +0 -0
  95. {bluecellulab-2.6.40 → bluecellulab-2.6.42}/docs/requirements_docs.txt +0 -0
  96. {bluecellulab-2.6.40 → bluecellulab-2.6.42}/docs/source/_static/.gitkeep +0 -0
  97. {bluecellulab-2.6.40 → bluecellulab-2.6.42}/docs/source/api.rst +0 -0
  98. {bluecellulab-2.6.40 → bluecellulab-2.6.42}/docs/source/changelog.rst +0 -0
  99. {bluecellulab-2.6.40 → bluecellulab-2.6.42}/docs/source/compiling-mechanisms.rst +0 -0
  100. {bluecellulab-2.6.40 → bluecellulab-2.6.42}/docs/source/conf.py +0 -0
  101. {bluecellulab-2.6.40 → bluecellulab-2.6.42}/docs/source/contributing.rst +0 -0
  102. {bluecellulab-2.6.40 → bluecellulab-2.6.42}/docs/source/index.rst +0 -0
  103. {bluecellulab-2.6.40 → bluecellulab-2.6.42}/docs/source/list_of_stim.rst +0 -0
  104. {bluecellulab-2.6.40 → bluecellulab-2.6.42}/docs/source/logo/BlueCelluLabBanner.jpg +0 -0
  105. {bluecellulab-2.6.40 → bluecellulab-2.6.42}/pyproject.toml +0 -0
  106. {bluecellulab-2.6.40 → bluecellulab-2.6.42}/setup.cfg +0 -0
  107. {bluecellulab-2.6.40 → bluecellulab-2.6.42}/tox.ini +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: bluecellulab
3
- Version: 2.6.40
3
+ Version: 2.6.42
4
4
  Summary: Biologically detailed neural network simulations and analysis.
5
5
  Author: Blue Brain Project, EPFL
6
6
  License: Apache2.0
@@ -166,6 +166,7 @@ Copyright
166
166
  =========
167
167
 
168
168
  Copyright (c) 2023-2024 Blue Brain Project/EPFL
169
+
169
170
  Copyright (c) 2025 Open Brain Institute
170
171
 
171
172
  This work is licensed under `Apache 2.0 <https://www.apache.org/licenses/LICENSE-2.0.html>`_
@@ -136,6 +136,7 @@ Copyright
136
136
  =========
137
137
 
138
138
  Copyright (c) 2023-2024 Blue Brain Project/EPFL
139
+
139
140
  Copyright (c) 2025 Open Brain Institute
140
141
 
141
142
  This work is licensed under `Apache 2.0 <https://www.apache.org/licenses/LICENSE-2.0.html>`_
@@ -0,0 +1,177 @@
1
+ """Module for analyzing cell simulation results."""
2
+ try:
3
+ import efel
4
+ except ImportError:
5
+ efel = None
6
+ import numpy as np
7
+
8
+ from bluecellulab.stimulus import StimulusFactory
9
+ from bluecellulab.tools import calculate_rheobase
10
+ from bluecellulab.analysis.inject_sequence import run_stimulus
11
+ from bluecellulab.analysis.plotting import plot_iv_curve, plot_fi_curve
12
+
13
+
14
+ def compute_plot_iv_curve(cell,
15
+ injecting_section="soma[0]",
16
+ injecting_segment=0.5,
17
+ recording_section="soma[0]",
18
+ recording_segment=0.5,
19
+ stim_start=100.0,
20
+ duration=500.0,
21
+ post_delay=100.0,
22
+ threshold_voltage=-30,
23
+ nb_bins=11):
24
+ """Compute and plot the Current-Voltage (I-V) curve for a given cell by
25
+ injecting a range of currents.
26
+
27
+ This function evaluates the relationship between the injected current amplitude and the resulting
28
+ steady-state membrane potential of a neuronal cell model. Currents are injected at a specified section
29
+ and segment, and the steady-state voltage at the recording location is used to construct the I-V curve.
30
+
31
+ Args:
32
+ cell (bluecellulab.cell.Cell): The initialized BlueCelluLab cell model.
33
+ injecting_section (str, optional): The name of the section where the stimulus is injected.
34
+ Default is "soma[0]".
35
+ injecting_segment (float, optional): The position along the injecting section (0.0 to 1.0)
36
+ where the stimulus is applied. Default is 0.5.
37
+ recording_section (str, optional): The name of the section where the voltage is recorded.
38
+ Default is "soma[0]".
39
+ recording_segment (float, optional): The position along the recording section (0.0 to 1.0)
40
+ where the voltage is recorded. Default is 0.5.
41
+ stim_start (float, optional): The start time of the current injection (in ms). Default is 100.0 ms.
42
+ duration (float, optional): The duration of the current injection (in ms). Default is 500.0 ms.
43
+ post_delay (float, optional): The delay after the stimulation ends before the simulation stops
44
+ (in ms). Default is 100.0 ms.
45
+ threshold_voltage (float, optional): The voltage threshold (in mV) for detecting a steady-state
46
+ response. Default is -30 mV.
47
+ nb_bins (int, optional): The number of discrete current levels between 0 and the maximum current.
48
+ Default is 11.
49
+
50
+ Returns:
51
+ tuple: A tuple containing:
52
+ - list_amp (np.ndarray): The injected current amplitudes (nA).
53
+ - steady_states (np.ndarray): The corresponding steady-state voltages (mV) recorded at the
54
+ specified location.
55
+
56
+ Raises:
57
+ ValueError: If the cell object is invalid, the specified sections/segments are not found, or if
58
+ the simulation results are inconsistent.
59
+ """
60
+ rheobase = calculate_rheobase(cell=cell, section=injecting_section, segx=injecting_segment)
61
+
62
+ list_amp = np.linspace(rheobase - 2, rheobase - 0.1, nb_bins) # [nA]
63
+
64
+ steps = []
65
+ times = []
66
+ voltages = []
67
+ # inject step current and record voltage response
68
+ stim_factory = StimulusFactory(dt=0.1)
69
+ for amp in list_amp:
70
+ step_stimulus = stim_factory.step(pre_delay=stim_start, duration=duration, post_delay=post_delay, amplitude=amp)
71
+ recording = run_stimulus(cell.template_params,
72
+ step_stimulus,
73
+ section=injecting_section,
74
+ segment=injecting_segment,
75
+ recording_section=recording_section,
76
+ recording_segment=recording_segment)
77
+ steps.append(step_stimulus)
78
+ times.append(recording.time)
79
+ voltages.append(recording.voltage)
80
+
81
+ steady_states = []
82
+ # compute steady state response
83
+ efel.set_setting('Threshold', threshold_voltage)
84
+ for voltage, t in zip(voltages, times):
85
+ trace = {
86
+ 'T': t,
87
+ 'V': voltage,
88
+ 'stim_start': [stim_start],
89
+ 'stim_end': [stim_start + duration]
90
+ }
91
+ features_results = efel.get_feature_values([trace], ['steady_state_voltage_stimend'])
92
+ steady_state = features_results[0]['steady_state_voltage_stimend']
93
+ steady_states.append(steady_state)
94
+
95
+ plot_iv_curve(list_amp,
96
+ steady_states,
97
+ injecting_section=injecting_section,
98
+ injecting_segment=injecting_segment,
99
+ recording_section=recording_section,
100
+ recording_segment=recording_segment)
101
+
102
+ return np.array(list_amp), np.array(steady_states)
103
+
104
+
105
+ def compute_plot_fi_curve(cell,
106
+ injecting_section="soma[0]",
107
+ injecting_segment=0.5,
108
+ recording_section="soma[0]",
109
+ recording_segment=0.5,
110
+ stim_start=100.0,
111
+ duration=500.0,
112
+ post_delay=100.0,
113
+ max_current=0.8,
114
+ nb_bins=11):
115
+ """Compute and plot the Frequency-Current (F-I) curve for a given cell by
116
+ injecting a range of currents.
117
+
118
+ This function evaluates the relationship between injected current amplitude and the firing rate
119
+ of a neuronal cell model. Currents are injected at a specified section and segment, and the number
120
+ of spikes recorded in the specified recording location is used to construct the F-I curve.
121
+
122
+ Args:
123
+ cell (bluecellulab.cell.Cell): The initialized BlueCelluLab cell model.
124
+ injecting_section (str, optional): The name of the section where the stimulus is injected.
125
+ Default is "soma[0]".
126
+ injecting_segment (float, optional): The position along the injecting section (0.0 to 1.0)
127
+ where the stimulus is applied. Default is 0.5.
128
+ recording_section (str, optional): The name of the section where spikes are recorded.
129
+ Default is "soma[0]".
130
+ recording_segment (float, optional): The position along the recording section (0.0 to 1.0)
131
+ where spikes are recorded. Default is 0.5.
132
+ stim_start (float, optional): The start time of the current injection (in ms). Default is 100.0 ms.
133
+ duration (float, optional): The duration of the current injection (in ms). Default is 500.0 ms.
134
+ post_delay (float, optional): The delay after the stimulation ends before the simulation stops
135
+ (in ms). Default is 100.0 ms.
136
+ max_current (float, optional): The maximum amplitude of the injected current (in nA).
137
+ Default is 0.8 nA.
138
+ nb_bins (int, optional): The number of discrete current levels between 0 and `max_current`.
139
+ Default is 11.
140
+
141
+ Returns:
142
+ tuple: A tuple containing:
143
+ - list_amp (np.ndarray): The injected current amplitudes (nA).
144
+ - spike_count (np.ndarray): The corresponding spike counts for each current amplitude.
145
+
146
+ Raises:
147
+ ValueError: If the cell object is invalid or the specified sections/segments are not found.
148
+ """
149
+ rheobase = calculate_rheobase(cell=cell, section=injecting_section, segx=injecting_segment)
150
+
151
+ list_amp = np.linspace(rheobase, max_current, nb_bins) # [nA]
152
+ steps = []
153
+ spikes = []
154
+ # inject step current and record spike response
155
+ stim_factory = StimulusFactory(dt=0.1)
156
+ for amp in list_amp:
157
+ step_stimulus = stim_factory.step(pre_delay=stim_start, duration=duration, post_delay=post_delay, amplitude=amp)
158
+ recording = run_stimulus(cell.template_params,
159
+ step_stimulus,
160
+ section=injecting_section,
161
+ segment=injecting_segment,
162
+ recording_section=recording_section,
163
+ recording_segment=recording_segment,
164
+ enable_spike_detection=True)
165
+ steps.append(step_stimulus)
166
+ spikes.append(recording.spike)
167
+
168
+ spike_count = [len(spike) for spike in spikes]
169
+
170
+ plot_fi_curve(list_amp,
171
+ spike_count,
172
+ injecting_section=injecting_section,
173
+ injecting_segment=injecting_segment,
174
+ recording_section=recording_section,
175
+ recording_segment=recording_segment)
176
+
177
+ return np.array(list_amp), np.array(spike_count)
@@ -1,7 +1,7 @@
1
1
  """Module for injecting a sequence of protocols to the cell."""
2
2
  from __future__ import annotations
3
3
  from enum import Enum, auto
4
- from typing import NamedTuple, Sequence, Dict
4
+ from typing import NamedTuple, Sequence, Dict, Optional
5
5
 
6
6
  import neuron
7
7
  import numpy as np
@@ -11,6 +11,7 @@ from bluecellulab.simulation.parallel import IsolatedProcess
11
11
  from bluecellulab.simulation.simulation import Simulation
12
12
  from bluecellulab.stimulus.circuit_stimulus_definitions import Hyperpolarizing
13
13
  from bluecellulab.stimulus.factory import Stimulus, StimulusFactory
14
+ from bluecellulab.tools import validate_section_and_segment
14
15
 
15
16
 
16
17
  class StimulusName(Enum):
@@ -24,10 +25,12 @@ class StimulusName(Enum):
24
25
 
25
26
 
26
27
  class Recording(NamedTuple):
27
- """A tuple of the current, voltage and time recordings."""
28
+ """A tuple of the current, voltage and time recordings with optional spike
29
+ recordings."""
28
30
  current: np.ndarray
29
31
  voltage: np.ndarray
30
32
  time: np.ndarray
33
+ spike: np.ndarray | None
31
34
 
32
35
 
33
36
  StimulusRecordings = Dict[str, Recording]
@@ -40,41 +43,91 @@ def run_stimulus(
40
43
  segment: float,
41
44
  cvode: bool = True,
42
45
  add_hypamp: bool = True,
46
+ recording_section: str = "soma[0]",
47
+ recording_segment: float = 0.5,
48
+ enable_spike_detection: bool = False,
49
+ threshold_spike_detection: float = -30,
43
50
  ) -> Recording:
44
- """Creates a cell and stimulates it with a given stimulus.
51
+ """Creates a cell from template parameters, applies a stimulus, and records
52
+ the response.
53
+
54
+ This function simulates the electrical activity of a neuronal cell model by injecting
55
+ a stimulus into a specified section and segment, recording the voltage response, and
56
+ optionally detecting spikes.
45
57
 
46
58
  Args:
47
- template_params: The parameters to create the cell from a template.
48
- stimulus: The input stimulus to inject into the cell.
49
- section: Name of the section of cell where the stimulus is to be injected.
50
- segment: The segment of the section where the stimulus is to be injected.
51
- cvode: True to use variable time-steps. False for fixed time-steps.
59
+ template_params (TemplateParams): Parameters required to create the cell from a
60
+ specified template, including morphology and mechanisms.
61
+ stimulus (Stimulus): The stimulus waveform to inject, defined by time and current arrays.
62
+ section (str): Name of the section of the cell where the stimulus is applied.
63
+ (e.g. soma[0])
64
+ segment (float): The normalized position (0.0 to 1.0) along the injecting
65
+ section where the stimulus is applied.
66
+ cvode (bool, optional): Whether to use variable time-step integration. Defaults to True.
67
+ add_hypamp (bool, optional): If True, adds a hyperpolarizing stimulus before applying
68
+ 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
71
+ section where voltage is recorded.
72
+ enable_spike_detection (bool, optional): If True, enables spike detection at the
73
+ recording location. Defaults to False.
74
+ threshold_spike_detection (float, optional): The voltage threshold (mV) for spike detection.
75
+ Defaults to -30 mV.
52
76
 
53
77
  Returns:
54
- The voltage-time recording at the specified location.
78
+ Recording: A `Recording` object containing the following:
79
+ - `current` (np.ndarray): The injected current waveform (nA).
80
+ - `voltage` (np.ndarray): The recorded membrane potential (mV) over time.
81
+ - `time` (np.ndarray): The simulation time points (ms).
82
+ - `spike` (np.ndarray or None): The detected spikes, if spike detection is enabled.
55
83
 
56
84
  Raises:
57
- ValueError: If the time and voltage arrays are not the same length.
85
+ ValueError: If the time, current, and voltage arrays do not have the same length,
86
+ or if the specified sections or segments are not found in the cell model.
58
87
  """
59
88
  cell = Cell.from_template_parameters(template_params)
60
- neuron_section = cell.sections[section]
89
+
90
+ validate_section_and_segment(cell, section, segment)
91
+ validate_section_and_segment(cell, recording_section, recording_segment)
92
+
61
93
  if add_hypamp:
62
94
  hyp_stim = Hyperpolarizing(target="", delay=0.0, duration=stimulus.stimulus_time)
63
95
  cell.add_replay_hypamp(hyp_stim)
64
- cell.add_voltage_recording(neuron_section, segment)
96
+
97
+ cell.add_voltage_recording(cell.sections[recording_section], recording_segment)
98
+
99
+ # Set up spike detection if enabled
100
+ spikes: Optional[np.ndarray] = None
101
+ 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
+
105
+ # Inject the stimulus and run the simulation
65
106
  iclamp, _ = cell.inject_current_waveform(
66
- stimulus.time, stimulus.current, section=neuron_section, segx=segment
107
+ stimulus.time, stimulus.current, section=cell.sections[section], segx=segment
67
108
  )
68
109
  current_vector = neuron.h.Vector()
69
110
  current_vector.record(iclamp._ref_i)
111
+
70
112
  simulation = Simulation(cell)
71
113
  simulation.run(stimulus.stimulus_time, cvode=cvode)
114
+
115
+ # Retrieve simulation results
72
116
  current = np.array(current_vector.to_python())
73
- voltage = cell.get_voltage_recording(neuron_section, segment)
117
+ voltage = cell.get_voltage_recording(cell.sections[recording_section], recording_segment)
74
118
  time = cell.get_time()
119
+
75
120
  if len(time) != len(voltage) or len(time) != len(current):
76
- raise ValueError("Time, current and voltage arrays are not the same length")
77
- return Recording(current, voltage, time)
121
+ raise ValueError("Time, current, and voltage arrays are not the same length")
122
+
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
+
130
+ return Recording(current=current, voltage=voltage, time=time, spike=spikes)
78
131
 
79
132
 
80
133
  def apply_multiple_stimuli(
@@ -0,0 +1,57 @@
1
+ """Module for plotting analysis results of cell simulations."""
2
+
3
+ import matplotlib.pyplot as plt
4
+
5
+
6
+ def plot_iv_curve(currents, voltages, injecting_section, injecting_segment, recording_section, recording_segment):
7
+ """Plots the IV curve.
8
+
9
+ Args:
10
+ currents (list): The injected current levels (nA).
11
+ voltages (list): The corresponding steady-state voltages (mV).
12
+ injecting_section (str): The section in the cell where the current was injected.
13
+ injecting_segment (float): The segment position (0.0 to 1.0) where the current was injected.
14
+ recording_section (str): The section in the cell where spikes were recorded.
15
+ recording_segment (float): The segment position (0.0 to 1.0) where spikes were recorded.
16
+
17
+ Raises:
18
+ ValueError: If the lengths of currents and voltages do not match.
19
+ """
20
+ if len(currents) != len(voltages):
21
+ raise ValueError("currents and voltages must have the same length")
22
+
23
+ plt.figure(figsize=(10, 6))
24
+ plt.plot(currents, voltages, marker='o', linestyle='-', color='b')
25
+ plt.title("I-V Curve")
26
+ plt.xlabel(f"Injected Current [nA] at {injecting_section}({injecting_segment:.2f})")
27
+ plt.ylabel(f"Steady state voltage [mV] at {recording_section}({recording_segment:.2f})")
28
+ plt.grid(True)
29
+ plt.tight_layout()
30
+ plt.show()
31
+
32
+
33
+ def plot_fi_curve(currents, spike_count, injecting_section, injecting_segment, recording_section, recording_segment):
34
+ """Plots the F-I (Frequency-Current) curve.
35
+
36
+ Args:
37
+ currents (list): The injected current levels (nA).
38
+ spike_count (list): The number of spikes recorded for each current level.
39
+ injecting_section (str): The section in the cell where the current was injected.
40
+ injecting_segment (float): The segment position (0.0 to 1.0) where the current was injected.
41
+ recording_section (str): The section in the cell where spikes were recorded.
42
+ recording_segment (float): The segment position (0.0 to 1.0) where spikes were recorded.
43
+
44
+ Raises:
45
+ ValueError: If the lengths of currents and spike counts do not match.
46
+ """
47
+ if len(currents) != len(spike_count):
48
+ raise ValueError("currents and spike count must have the same length")
49
+
50
+ plt.figure(figsize=(10, 6))
51
+ plt.plot(currents, spike_count, marker='o')
52
+ plt.title("F-I Curve")
53
+ plt.xlabel(f"Injected Current [nA] at {injecting_section}({injecting_segment:.2f})")
54
+ plt.ylabel(f"Spike Count recorded at {recording_section}({recording_segment:.2f})")
55
+ plt.grid(True)
56
+ plt.tight_layout()
57
+ plt.show()
@@ -25,6 +25,7 @@ from typing_extensions import deprecated
25
25
  import neuron
26
26
  import numpy as np
27
27
  import pandas as pd
28
+ import re
28
29
 
29
30
  import bluecellulab
30
31
  from bluecellulab.cell.recording import section_to_voltage_recording_str
@@ -376,7 +377,11 @@ class Cell(InjectableMixin, PlottableMixin):
376
377
 
377
378
  def get_recording(self, var_name: str) -> np.ndarray:
378
379
  """Get recorded values."""
379
- return np.array(self.recordings[var_name].to_python())
380
+ try:
381
+ res = np.array(self.recordings[var_name].to_python())
382
+ except KeyError as e:
383
+ raise ValueError(f"No recording for '{var_name}' was found.") from e
384
+ return res
380
385
 
381
386
  def add_replay_synapse(self,
382
387
  synapse_id: SynapseID,
@@ -432,19 +437,42 @@ class Cell(InjectableMixin, PlottableMixin):
432
437
  def create_netcon_spikedetector(self, target: HocObjectType, location: str, threshold: float = -30.0) -> HocObjectType:
433
438
  """Add and return a spikedetector.
434
439
 
435
- This is a NetCon that detects spike in the current cell, and that
436
- connects to target
440
+ This function creates a NetCon object that detects spikes at a specific
441
+ location in the current cell and connects to the provided target point process.
442
+ The location can be specified as a predefined site ('soma' or 'AIS') or as a
443
+ custom location in the format `section[index](position)`. Custom locations
444
+ allow fine-grained control of the spike detection site within the cell's sections.
437
445
 
438
446
  Args:
439
- target: target point process
440
- location: the spike detection location
441
- threshold: spike detection threshold
447
+ target: A NEURON point process object (e.g., synapse) that the NetCon connects to.
448
+ location: The spike detection location. Acceptable formats include:
449
+
450
+ - `"soma"`: Detect spikes in the soma section at the distal end.
451
+
452
+ - `"AIS"`: Detect spikes in the axon initial segment at the midpoint.
442
453
 
443
- Returns: Neuron netcon object
454
+ - `"section[index](position)"`: Custom location specifying:
455
+
456
+ - `section`: The name of the section (e.g., 'soma', 'axon').
457
+ - `[index]` (optional): Segment index within a section array (e.g., 'soma[0]').
458
+ - `(position)` (optional): Normalized position along the section length (0 to 1).
459
+ Defaults to 0.5 if not provided.
460
+
461
+ threshold: The voltage threshold for spike detection (default: -30.0 mV).
462
+
463
+ Returns:
464
+ A NEURON `NetCon` object configured for spike detection at the specified location.
444
465
 
445
466
  Raises:
446
- ValueError: If the spike detection location is not 'soma' or 'AIS'.
467
+ ValueError: If:
468
+
469
+ - The `location` is not 'soma', 'AIS', or a valid custom format.
470
+
471
+ - The specified section or segment index does not exist.
472
+
473
+ - The position is out of bounds (e.g., negative or greater than 1.0).
447
474
  """
475
+
448
476
  if location == "soma":
449
477
  sec = public_hoc_cell(self.cell).soma[0]
450
478
  source = sec(1)._ref_v
@@ -452,7 +480,34 @@ class Cell(InjectableMixin, PlottableMixin):
452
480
  sec = public_hoc_cell(self.cell).axon[1]
453
481
  source = sec(0.5)._ref_v
454
482
  else:
455
- raise ValueError("Spike detection location must be soma or AIS")
483
+ # Parse custom location (e.g., 'soma[0](0.3)')
484
+ pattern = r'^([a-zA-Z_]+)(?:\[(\d+)\])?(?:\((-?\d+\.\d+)\))?$'
485
+ match = re.search(pattern, location)
486
+
487
+ # Extract the value if a match is found
488
+ if match:
489
+ section_name = match.group(1)
490
+ segment_index = match.group(2)
491
+ pos = match.group(3)
492
+ if pos is None:
493
+ pos = 0.5
494
+ else:
495
+ pos = float(pos)
496
+
497
+ else:
498
+ raise ValueError(f"Invalid location format: {location}")
499
+
500
+ try:
501
+ # Handle section arrays (e.g., soma[0])
502
+ if segment_index is not None:
503
+ sec = getattr(public_hoc_cell(self.cell), section_name)[int(segment_index)]
504
+ else:
505
+ sec = getattr(public_hoc_cell(self.cell), section_name)
506
+
507
+ source = sec(pos)._ref_v
508
+ except (AttributeError, ValueError, IndexError) as e:
509
+ raise ValueError(f"Invalid spike detection location: {location}") from e
510
+
456
511
  netcon = neuron.h.NetCon(source, target, sec=sec)
457
512
  netcon.threshold = threshold
458
513
  return netcon