bluecellulab 2.3.7__py3-none-any.whl → 2.5.0__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/__init__.py +9 -1
- bluecellulab/analysis/__init__.py +0 -0
- bluecellulab/analysis/inject_sequence.py +130 -0
- bluecellulab/cell/core.py +95 -93
- bluecellulab/cell/injector.py +20 -16
- bluecellulab/cell/recording.py +8 -0
- bluecellulab/cell/template.py +38 -62
- bluecellulab/hoc/RNGSettings.hoc +2 -6
- bluecellulab/importer.py +5 -7
- bluecellulab/rngsettings.py +40 -22
- bluecellulab/simulation/__init__.py +0 -1
- bluecellulab/simulation/neuron_globals.py +34 -3
- bluecellulab/simulation/simulation.py +3 -11
- bluecellulab/ssim.py +11 -15
- bluecellulab/synapse/synapse_factory.py +3 -3
- bluecellulab/synapse/synapse_types.py +26 -27
- bluecellulab/tools.py +57 -382
- bluecellulab/type_aliases.py +4 -1
- bluecellulab/utils.py +36 -1
- bluecellulab/verbosity.py +49 -0
- {bluecellulab-2.3.7.dist-info → bluecellulab-2.5.0.dist-info}/METADATA +1 -1
- {bluecellulab-2.3.7.dist-info → bluecellulab-2.5.0.dist-info}/RECORD +26 -22
- {bluecellulab-2.3.7.dist-info → bluecellulab-2.5.0.dist-info}/WHEEL +1 -1
- {bluecellulab-2.3.7.dist-info → bluecellulab-2.5.0.dist-info}/AUTHORS.txt +0 -0
- {bluecellulab-2.3.7.dist-info → bluecellulab-2.5.0.dist-info}/LICENSE +0 -0
- {bluecellulab-2.3.7.dist-info → bluecellulab-2.5.0.dist-info}/top_level.txt +0 -0
bluecellulab/__init__.py
CHANGED
|
@@ -8,8 +8,9 @@ except ImportError:
|
|
|
8
8
|
BLUEPY_AVAILABLE = False
|
|
9
9
|
|
|
10
10
|
from .importer import * # NOQA
|
|
11
|
-
from .
|
|
11
|
+
from .verbosity import *
|
|
12
12
|
from .cell import Cell, create_ball_stick # NOQA
|
|
13
|
+
from .circuit import EmodelProperties
|
|
13
14
|
from .connection import Connection # NOQA
|
|
14
15
|
from .plotwindow import PlotWindow # NOQA
|
|
15
16
|
from .dendrogram import Dendrogram # NOQA
|
|
@@ -18,3 +19,10 @@ from .psegment import PSegment # NOQA
|
|
|
18
19
|
from .simulation import Simulation # NOQA
|
|
19
20
|
from .rngsettings import RNGSettings # NOQA
|
|
20
21
|
from .ssim import SSim # NOQA
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
from .simulation.neuron_globals import NeuronGlobals
|
|
25
|
+
|
|
26
|
+
logger.debug("Loading the hoc files.")
|
|
27
|
+
import_hoc(neuron)
|
|
28
|
+
_ = NeuronGlobals.get_instance() # initiate the singleton
|
|
File without changes
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
"""Module for injecting a sequence of protocols to the cell."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
from enum import Enum, auto
|
|
4
|
+
from typing import NamedTuple, Sequence, Dict
|
|
5
|
+
|
|
6
|
+
import neuron
|
|
7
|
+
import numpy as np
|
|
8
|
+
from bluecellulab.cell.core import Cell
|
|
9
|
+
from bluecellulab.cell.template import TemplateParams
|
|
10
|
+
from bluecellulab.simulation.simulation import Simulation
|
|
11
|
+
from bluecellulab.stimulus.factory import Stimulus, StimulusFactory
|
|
12
|
+
from bluecellulab.utils import IsolatedProcess
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class StimulusName(Enum):
|
|
16
|
+
"""Allowed values for the StimulusName."""
|
|
17
|
+
AP_WAVEFORM = auto()
|
|
18
|
+
IDREST = auto()
|
|
19
|
+
IV = auto()
|
|
20
|
+
FIRE_PATTERN = auto()
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class Recording(NamedTuple):
|
|
24
|
+
"""A tuple of the current, voltage and time recordings."""
|
|
25
|
+
current: np.ndarray
|
|
26
|
+
voltage: np.ndarray
|
|
27
|
+
time: np.ndarray
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
StimulusRecordings = Dict[str, Recording]
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def run_stimulus(
|
|
34
|
+
template_params: TemplateParams,
|
|
35
|
+
stimulus: Stimulus,
|
|
36
|
+
section: str,
|
|
37
|
+
segment: float,
|
|
38
|
+
duration: float,
|
|
39
|
+
) -> Recording:
|
|
40
|
+
"""Creates a cell and stimulates it with a given stimulus.
|
|
41
|
+
|
|
42
|
+
Args:
|
|
43
|
+
template_params: The parameters to create the cell from a template.
|
|
44
|
+
stimulus: The input stimulus to inject into the cell.
|
|
45
|
+
section: Name of the section of cell where the stimulus is to be injected.
|
|
46
|
+
segment: The segment of the section where the stimulus is to be injected.
|
|
47
|
+
duration: The duration for which the simulation is to be run.
|
|
48
|
+
|
|
49
|
+
Returns:
|
|
50
|
+
The voltage-time recording at the specified location.
|
|
51
|
+
|
|
52
|
+
Raises:
|
|
53
|
+
ValueError: If the time and voltage arrays are not the same length.
|
|
54
|
+
"""
|
|
55
|
+
cell = Cell.from_template_parameters(template_params)
|
|
56
|
+
neuron_section = cell.sections[section]
|
|
57
|
+
cell.add_voltage_recording(neuron_section, segment)
|
|
58
|
+
iclamp, _ = cell.inject_current_waveform(
|
|
59
|
+
stimulus.time, stimulus.current, section=neuron_section, segx=segment
|
|
60
|
+
)
|
|
61
|
+
current_vector = neuron.h.Vector()
|
|
62
|
+
current_vector.record(iclamp._ref_i)
|
|
63
|
+
simulation = Simulation(cell)
|
|
64
|
+
simulation.run(duration)
|
|
65
|
+
current = np.array(current_vector.to_python())
|
|
66
|
+
voltage = cell.get_voltage_recording(neuron_section, segment)
|
|
67
|
+
time = cell.get_time()
|
|
68
|
+
if len(time) != len(voltage) or len(time) != len(current):
|
|
69
|
+
raise ValueError("Time, current and voltage arrays are not the same length")
|
|
70
|
+
return Recording(current, voltage, time)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def apply_multiple_step_stimuli(
|
|
74
|
+
cell: Cell,
|
|
75
|
+
stimulus_name: StimulusName,
|
|
76
|
+
amplitudes: Sequence[float],
|
|
77
|
+
duration: float,
|
|
78
|
+
section_name: str | None = None,
|
|
79
|
+
segment: float = 0.5,
|
|
80
|
+
n_processes: int | None = None,
|
|
81
|
+
) -> StimulusRecordings:
|
|
82
|
+
"""Apply multiple stimuli to the cell on isolated processes.
|
|
83
|
+
|
|
84
|
+
Args:
|
|
85
|
+
cell: The cell to which the stimuli are applied.
|
|
86
|
+
stimulus_name: The name of the stimulus to apply.
|
|
87
|
+
amplitudes: The amplitudes of the stimuli to apply.
|
|
88
|
+
duration: The duration for which each stimulus is applied.
|
|
89
|
+
section_name: Section name of the cell where the stimuli are applied.
|
|
90
|
+
If None, the stimuli are applied at the soma[0] of the cell.
|
|
91
|
+
segment: The segment of the section where the stimuli are applied.
|
|
92
|
+
n_processes: The number of processes to use for running the stimuli.
|
|
93
|
+
|
|
94
|
+
Returns:
|
|
95
|
+
A dictionary where the keys are the names of the stimuli and the values
|
|
96
|
+
are the recordings of the cell's response to each stimulus.
|
|
97
|
+
|
|
98
|
+
Raises:
|
|
99
|
+
ValueError: If the stimulus name is not recognized.
|
|
100
|
+
"""
|
|
101
|
+
res: StimulusRecordings = {}
|
|
102
|
+
stim_factory = StimulusFactory(dt=1.0)
|
|
103
|
+
task_args = []
|
|
104
|
+
section_name = section_name if section_name is not None else "soma[0]"
|
|
105
|
+
|
|
106
|
+
# Prepare arguments for each stimulus
|
|
107
|
+
for amplitude in amplitudes:
|
|
108
|
+
if stimulus_name == StimulusName.AP_WAVEFORM:
|
|
109
|
+
stimulus = stim_factory.ap_waveform(threshold_current=cell.threshold, threshold_percentage=amplitude)
|
|
110
|
+
elif stimulus_name == StimulusName.IDREST:
|
|
111
|
+
stimulus = stim_factory.idrest(threshold_current=cell.threshold, threshold_percentage=amplitude)
|
|
112
|
+
elif stimulus_name == StimulusName.IV:
|
|
113
|
+
stimulus = stim_factory.iv(threshold_current=cell.threshold, threshold_percentage=amplitude)
|
|
114
|
+
elif stimulus_name == StimulusName.FIRE_PATTERN:
|
|
115
|
+
stimulus = stim_factory.fire_pattern(threshold_current=cell.threshold, threshold_percentage=amplitude)
|
|
116
|
+
else:
|
|
117
|
+
raise ValueError("Unknown stimulus name.")
|
|
118
|
+
|
|
119
|
+
task_args.append((cell.template_params, stimulus, section_name, segment, duration))
|
|
120
|
+
|
|
121
|
+
with IsolatedProcess(processes=n_processes) as pool:
|
|
122
|
+
# Map expects a function and a list of argument tuples
|
|
123
|
+
results = pool.starmap(run_stimulus, task_args)
|
|
124
|
+
|
|
125
|
+
# Associate each result with a key
|
|
126
|
+
for amplitude, result in zip(amplitudes, results):
|
|
127
|
+
key = f"{stimulus_name}_{amplitude}"
|
|
128
|
+
res[key] = result
|
|
129
|
+
|
|
130
|
+
return res
|
bluecellulab/cell/core.py
CHANGED
|
@@ -27,24 +27,25 @@ import numpy as np
|
|
|
27
27
|
import pandas as pd
|
|
28
28
|
|
|
29
29
|
import bluecellulab
|
|
30
|
+
from bluecellulab.cell.recording import section_to_voltage_recording_str
|
|
30
31
|
from bluecellulab.psection import PSection, init_psections
|
|
31
32
|
from bluecellulab.cell.injector import InjectableMixin
|
|
32
33
|
from bluecellulab.cell.plotting import PlottableMixin
|
|
33
34
|
from bluecellulab.cell.section_distance import EuclideanSectionDistance
|
|
34
35
|
from bluecellulab.cell.sonata_proxy import SonataProxy
|
|
35
|
-
from bluecellulab.cell.template import NeuronTemplate, public_hoc_cell
|
|
36
|
+
from bluecellulab.cell.template import NeuronTemplate, TemplateParams, public_hoc_cell
|
|
36
37
|
from bluecellulab.circuit.config.sections import Conditions
|
|
37
38
|
from bluecellulab.circuit import EmodelProperties, SynapseProperty
|
|
38
39
|
from bluecellulab.circuit.node_id import CellId
|
|
39
40
|
from bluecellulab.circuit.simulation_access import get_synapse_replay_spikes
|
|
40
41
|
from bluecellulab.exceptions import BluecellulabError
|
|
41
|
-
from bluecellulab.importer import
|
|
42
|
+
from bluecellulab.importer import load_mod_files
|
|
42
43
|
from bluecellulab.neuron_interpreter import eval_neuron
|
|
43
44
|
from bluecellulab.rngsettings import RNGSettings
|
|
44
45
|
from bluecellulab.stimulus.circuit_stimulus_definitions import SynapseReplay
|
|
45
46
|
from bluecellulab.synapse import SynapseFactory, Synapse
|
|
46
47
|
from bluecellulab.synapse.synapse_types import SynapseID
|
|
47
|
-
from bluecellulab.type_aliases import HocObjectType, NeuronSection
|
|
48
|
+
from bluecellulab.type_aliases import HocObjectType, NeuronSection, SectionMapping
|
|
48
49
|
|
|
49
50
|
logger = logging.getLogger(__name__)
|
|
50
51
|
|
|
@@ -54,55 +55,77 @@ class Cell(InjectableMixin, PlottableMixin):
|
|
|
54
55
|
|
|
55
56
|
last_id = 0
|
|
56
57
|
|
|
57
|
-
@
|
|
58
|
+
@classmethod
|
|
59
|
+
def from_template_parameters(
|
|
60
|
+
cls, template_params: TemplateParams, cell_id: Optional[CellId] = None,
|
|
61
|
+
record_dt: Optional[float] = None
|
|
62
|
+
) -> Cell:
|
|
63
|
+
"""Create a cell from a TemplateParams object.
|
|
64
|
+
|
|
65
|
+
Useful in isolating runs.
|
|
66
|
+
"""
|
|
67
|
+
return cls(
|
|
68
|
+
template_path=template_params.template_filepath,
|
|
69
|
+
morphology_path=template_params.morph_filepath,
|
|
70
|
+
cell_id=cell_id,
|
|
71
|
+
record_dt=record_dt,
|
|
72
|
+
template_format=template_params.template_format,
|
|
73
|
+
emodel_properties=template_params.emodel_properties,
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
@load_mod_files
|
|
58
77
|
def __init__(self,
|
|
59
78
|
template_path: str | Path,
|
|
60
79
|
morphology_path: str | Path,
|
|
61
80
|
cell_id: Optional[CellId] = None,
|
|
62
81
|
record_dt: Optional[float] = None,
|
|
63
82
|
template_format: str = "v5",
|
|
64
|
-
emodel_properties: Optional[EmodelProperties] = None
|
|
65
|
-
rng_settings: Optional[RNGSettings] = None) -> None:
|
|
83
|
+
emodel_properties: Optional[EmodelProperties] = None) -> None:
|
|
66
84
|
"""Initializes a Cell object.
|
|
67
85
|
|
|
68
86
|
Args:
|
|
69
|
-
template_path:
|
|
87
|
+
template_path: Path to hoc template file.
|
|
70
88
|
morphology_path: Path to morphology file.
|
|
71
|
-
|
|
89
|
+
cell_id: ID of the cell, used in RNG seeds.
|
|
72
90
|
record_dt: Timestep for the recordings.
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
or 'v6_air_scaler'. Defaults to "v5".
|
|
76
|
-
emodel_properties: Properties such as
|
|
77
|
-
threshold_current, holding_current. Defaults to None.
|
|
78
|
-
rng_settings: Random number generation setting
|
|
79
|
-
object used by the Cell. Defaults to None.
|
|
91
|
+
template_format: Cell template format such as 'v5' or 'v6_air_scaler'.
|
|
92
|
+
emodel_properties: Template specific emodel properties.
|
|
80
93
|
"""
|
|
81
94
|
super().__init__()
|
|
95
|
+
self.template_params = TemplateParams(
|
|
96
|
+
template_filepath=template_path,
|
|
97
|
+
morph_filepath=morphology_path,
|
|
98
|
+
template_format=template_format,
|
|
99
|
+
emodel_properties=emodel_properties,
|
|
100
|
+
)
|
|
82
101
|
if cell_id is None:
|
|
83
102
|
cell_id = CellId("", Cell.last_id)
|
|
84
103
|
Cell.last_id += 1
|
|
85
104
|
self.cell_id = cell_id
|
|
86
|
-
# Persistent objects, like clamps, that exist as long
|
|
87
|
-
# as the object exists
|
|
88
|
-
self.persistent: list[HocObjectType] = []
|
|
89
|
-
|
|
90
|
-
self.morphology_path = morphology_path
|
|
91
105
|
|
|
92
106
|
# Load the template
|
|
93
|
-
neuron_template = NeuronTemplate(template_path, morphology_path)
|
|
107
|
+
neuron_template = NeuronTemplate(template_path, morphology_path, template_format, emodel_properties)
|
|
94
108
|
self.template_id = neuron_template.template_name # useful to map NEURON and python objects
|
|
95
|
-
self.cell = neuron_template.get_cell(
|
|
109
|
+
self.cell = neuron_template.get_cell(self.cell_id.id)
|
|
110
|
+
if template_format == 'v6':
|
|
111
|
+
if emodel_properties is None:
|
|
112
|
+
raise BluecellulabError('EmodelProperties must be provided for v6 template')
|
|
113
|
+
self.hypamp: float | None = emodel_properties.holding_current
|
|
114
|
+
self.threshold: float = emodel_properties.threshold_current
|
|
115
|
+
else:
|
|
116
|
+
try:
|
|
117
|
+
self.hypamp = self.cell.getHypAmp()
|
|
118
|
+
except AttributeError:
|
|
119
|
+
self.hypamp = None
|
|
120
|
+
try:
|
|
121
|
+
self.threshold = self.cell.getThreshold()
|
|
122
|
+
except AttributeError:
|
|
123
|
+
self.threshold = 0.0
|
|
96
124
|
self.soma = public_hoc_cell(self.cell).soma[0]
|
|
97
125
|
# WARNING: this finitialize 'must' be here, otherwhise the
|
|
98
126
|
# diameters of the loaded morph are wrong
|
|
99
127
|
neuron.h.finitialize()
|
|
100
128
|
|
|
101
|
-
if rng_settings is None:
|
|
102
|
-
self.rng_settings = RNGSettings("Random123") # SONATA value
|
|
103
|
-
else:
|
|
104
|
-
self.rng_settings = rng_settings
|
|
105
|
-
|
|
106
129
|
self.recordings: dict[str, HocObjectType] = {}
|
|
107
130
|
self.synapses: dict[SynapseID, Synapse] = {}
|
|
108
131
|
self.connections: dict[SynapseID, bluecellulab.Connection] = {}
|
|
@@ -121,23 +144,6 @@ class Cell(InjectableMixin, PlottableMixin):
|
|
|
121
144
|
self.delayed_weights = queue.PriorityQueue() # type: ignore
|
|
122
145
|
self.psections, self.secname_to_psection = init_psections(public_hoc_cell(self.cell))
|
|
123
146
|
|
|
124
|
-
self.emodel_properties = emodel_properties
|
|
125
|
-
if template_format == 'v6':
|
|
126
|
-
if self.emodel_properties is None:
|
|
127
|
-
raise BluecellulabError('EmodelProperties must be provided for v6 template')
|
|
128
|
-
self.hypamp: float | None = self.emodel_properties.holding_current
|
|
129
|
-
self.threshold: float | None = self.emodel_properties.threshold_current
|
|
130
|
-
else:
|
|
131
|
-
try:
|
|
132
|
-
self.hypamp = self.cell.getHypAmp()
|
|
133
|
-
except AttributeError:
|
|
134
|
-
self.hypamp = None
|
|
135
|
-
|
|
136
|
-
try:
|
|
137
|
-
self.threshold = self.cell.getThreshold()
|
|
138
|
-
except AttributeError:
|
|
139
|
-
self.threshold = None
|
|
140
|
-
|
|
141
147
|
# Keep track of when a cell is made passive by make_passive()
|
|
142
148
|
# Used to know when re_init_rng() can be executed
|
|
143
149
|
self.is_made_passive = False
|
|
@@ -145,6 +151,17 @@ class Cell(InjectableMixin, PlottableMixin):
|
|
|
145
151
|
neuron.h.pop_section() # Undoing soma push
|
|
146
152
|
self.sonata_proxy: Optional[SonataProxy] = None
|
|
147
153
|
|
|
154
|
+
# Persistent objects, like clamps, that exist as long
|
|
155
|
+
# as the object exists
|
|
156
|
+
self.persistent: list[HocObjectType] = []
|
|
157
|
+
|
|
158
|
+
def _extract_sections(self, sections) -> SectionMapping:
|
|
159
|
+
res: SectionMapping = {}
|
|
160
|
+
for section in sections:
|
|
161
|
+
key_name = str(section).split(".")[-1]
|
|
162
|
+
res[key_name] = section
|
|
163
|
+
return res
|
|
164
|
+
|
|
148
165
|
@property
|
|
149
166
|
def somatic(self) -> list[NeuronSection]:
|
|
150
167
|
return list(public_hoc_cell(self.cell).somatic)
|
|
@@ -162,8 +179,8 @@ class Cell(InjectableMixin, PlottableMixin):
|
|
|
162
179
|
return list(public_hoc_cell(self.cell).axonal)
|
|
163
180
|
|
|
164
181
|
@property
|
|
165
|
-
def
|
|
166
|
-
return
|
|
182
|
+
def sections(self) -> SectionMapping:
|
|
183
|
+
return self._extract_sections(public_hoc_cell(self.cell).all)
|
|
167
184
|
|
|
168
185
|
def __repr__(self) -> str:
|
|
169
186
|
base_info = f"Cell Object: {super().__repr__()}"
|
|
@@ -174,29 +191,10 @@ class Cell(InjectableMixin, PlottableMixin):
|
|
|
174
191
|
"""Connect this cell to a circuit via sonata proxy."""
|
|
175
192
|
self.sonata_proxy = sonata_proxy
|
|
176
193
|
|
|
177
|
-
def re_init_rng(self
|
|
194
|
+
def re_init_rng(self) -> None:
|
|
178
195
|
"""Reinitialize the random number generator for stochastic channels."""
|
|
179
|
-
|
|
180
196
|
if not self.is_made_passive:
|
|
181
|
-
|
|
182
|
-
channel_id = 0
|
|
183
|
-
for section in self.somatic:
|
|
184
|
-
for seg in section:
|
|
185
|
-
neuron.h.setdata_StochKv(seg.x, sec=section)
|
|
186
|
-
neuron.h.setRNG_StochKv(channel_id, self.cell_id.id)
|
|
187
|
-
channel_id += 1
|
|
188
|
-
for section in self.basal:
|
|
189
|
-
for seg in section:
|
|
190
|
-
neuron.h.setdata_StochKv(seg.x, sec=section)
|
|
191
|
-
neuron.h.setRNG_StochKv(channel_id, self.cell_id.id)
|
|
192
|
-
channel_id += 1
|
|
193
|
-
for section in self.apical:
|
|
194
|
-
for seg in section:
|
|
195
|
-
neuron.h.setdata_StochKv(seg.x, sec=section)
|
|
196
|
-
neuron.h.setRNG_StochKv(channel_id, self.cell_id.id)
|
|
197
|
-
channel_id += 1
|
|
198
|
-
else:
|
|
199
|
-
self.cell.re_init_rng()
|
|
197
|
+
self.cell.re_init_rng()
|
|
200
198
|
|
|
201
199
|
def get_psection(self, section_id: int | str) -> PSection:
|
|
202
200
|
"""Return a python section with the specified section id."""
|
|
@@ -211,7 +209,7 @@ class Cell(InjectableMixin, PlottableMixin):
|
|
|
211
209
|
|
|
212
210
|
def make_passive(self) -> None:
|
|
213
211
|
"""Make the cell passive by deactivating all the active channels."""
|
|
214
|
-
for section in self.
|
|
212
|
+
for section in self.sections.values():
|
|
215
213
|
mech_names = set()
|
|
216
214
|
for seg in section:
|
|
217
215
|
for mech in seg:
|
|
@@ -246,14 +244,14 @@ class Cell(InjectableMixin, PlottableMixin):
|
|
|
246
244
|
|
|
247
245
|
def _default_enable_ttx(self) -> None:
|
|
248
246
|
"""Default enable_ttx implementation."""
|
|
249
|
-
for section in self.
|
|
247
|
+
for section in self.sections.values():
|
|
250
248
|
if not neuron.h.ismembrane("TTXDynamicsSwitch"):
|
|
251
249
|
section.insert('TTXDynamicsSwitch')
|
|
252
250
|
section.ttxo_level_TTXDynamicsSwitch = 1.0
|
|
253
251
|
|
|
254
252
|
def _default_disable_ttx(self) -> None:
|
|
255
253
|
"""Default disable_ttx implementation."""
|
|
256
|
-
for section in self.
|
|
254
|
+
for section in self.sections.values():
|
|
257
255
|
if not neuron.h.ismembrane("TTXDynamicsSwitch"):
|
|
258
256
|
section.insert('TTXDynamicsSwitch')
|
|
259
257
|
section.ttxo_level_TTXDynamicsSwitch = 1e-14
|
|
@@ -261,7 +259,7 @@ class Cell(InjectableMixin, PlottableMixin):
|
|
|
261
259
|
def area(self) -> float:
|
|
262
260
|
"""The total surface area of the cell."""
|
|
263
261
|
area = 0.0
|
|
264
|
-
for section in self.
|
|
262
|
+
for section in self.sections.values():
|
|
265
263
|
x_s = np.arange(1.0 / (2 * section.nseg), 1.0,
|
|
266
264
|
1.0 / (section.nseg))
|
|
267
265
|
for x in x_s:
|
|
@@ -311,7 +309,7 @@ class Cell(InjectableMixin, PlottableMixin):
|
|
|
311
309
|
self.add_recording("self.axonal[1](0.5)._ref_v", dt=dt)
|
|
312
310
|
|
|
313
311
|
def add_voltage_recording(
|
|
314
|
-
self, section:
|
|
312
|
+
self, section: Optional[NeuronSection] = None, segx: float = 0.5, dt: Optional[float] = None
|
|
315
313
|
) -> None:
|
|
316
314
|
"""Add a voltage recording to a certain section at a given segment
|
|
317
315
|
(segx).
|
|
@@ -323,11 +321,13 @@ class Cell(InjectableMixin, PlottableMixin):
|
|
|
323
321
|
dt: Recording time step. If not provided, the recording step will
|
|
324
322
|
default to the simulator's time step.
|
|
325
323
|
"""
|
|
326
|
-
|
|
324
|
+
if section is None:
|
|
325
|
+
section = self.soma
|
|
326
|
+
var_name = section_to_voltage_recording_str(section, segx)
|
|
327
327
|
self.add_recording(var_name, dt)
|
|
328
328
|
|
|
329
329
|
def get_voltage_recording(
|
|
330
|
-
self, section:
|
|
330
|
+
self, section: Optional[NeuronSection] = None, segx: float = 0.5
|
|
331
331
|
) -> np.ndarray:
|
|
332
332
|
"""Get a voltage recording for a certain section at a given segment
|
|
333
333
|
(segx).
|
|
@@ -343,7 +343,9 @@ class Cell(InjectableMixin, PlottableMixin):
|
|
|
343
343
|
Raises:
|
|
344
344
|
BluecellulabError: If voltage recording was not added previously using add_voltage_recording.
|
|
345
345
|
"""
|
|
346
|
-
|
|
346
|
+
if section is None:
|
|
347
|
+
section = self.soma
|
|
348
|
+
recording_name = section_to_voltage_recording_str(section, segx)
|
|
347
349
|
if recording_name in self.recordings:
|
|
348
350
|
return self.get_recording(recording_name)
|
|
349
351
|
else:
|
|
@@ -354,15 +356,13 @@ class Cell(InjectableMixin, PlottableMixin):
|
|
|
354
356
|
|
|
355
357
|
def add_allsections_voltagerecordings(self):
|
|
356
358
|
"""Add a voltage recording to every section of the cell."""
|
|
357
|
-
|
|
358
|
-
for section in all_sections:
|
|
359
|
+
for section in self.sections.values():
|
|
359
360
|
self.add_voltage_recording(section, dt=self.record_dt)
|
|
360
361
|
|
|
361
362
|
def get_allsections_voltagerecordings(self) -> dict[str, np.ndarray]:
|
|
362
363
|
"""Get all the voltage recordings from all the sections."""
|
|
363
364
|
all_section_voltages = {}
|
|
364
|
-
|
|
365
|
-
for section in all_sections:
|
|
365
|
+
for section in self.sections.values():
|
|
366
366
|
recording = self.get_voltage_recording(section)
|
|
367
367
|
all_section_voltages[section.name()] = recording
|
|
368
368
|
return all_section_voltages
|
|
@@ -440,10 +440,10 @@ class Cell(InjectableMixin, PlottableMixin):
|
|
|
440
440
|
"""
|
|
441
441
|
if location == "soma":
|
|
442
442
|
sec = public_hoc_cell(self.cell).soma[0]
|
|
443
|
-
source =
|
|
443
|
+
source = sec(1)._ref_v
|
|
444
444
|
elif location == "AIS":
|
|
445
445
|
sec = public_hoc_cell(self.cell).axon[1]
|
|
446
|
-
source =
|
|
446
|
+
source = sec(0.5)._ref_v
|
|
447
447
|
else:
|
|
448
448
|
raise ValueError("Spike detection location must be soma or AIS")
|
|
449
449
|
netcon = neuron.h.NetCon(source, target, sec=sec)
|
|
@@ -486,7 +486,6 @@ class Cell(InjectableMixin, PlottableMixin):
|
|
|
486
486
|
|
|
487
487
|
sid = synapse_id[1]
|
|
488
488
|
|
|
489
|
-
base_seed = self.rng_settings.base_seed
|
|
490
489
|
weight = syn_description[SynapseProperty.G_SYNX]
|
|
491
490
|
# numpy int to int
|
|
492
491
|
post_sec_id = int(syn_description[SynapseProperty.POST_SECTION_ID])
|
|
@@ -526,9 +525,10 @@ class Cell(InjectableMixin, PlottableMixin):
|
|
|
526
525
|
# NC_SPONTMINI
|
|
527
526
|
self.syn_mini_netcons[synapse_id].weight[nc_type_param] = 1
|
|
528
527
|
|
|
529
|
-
|
|
528
|
+
rng_settings = RNGSettings.get_instance()
|
|
529
|
+
if rng_settings.mode == 'Random123':
|
|
530
530
|
seed2 = source_popid * 65536 + target_popid \
|
|
531
|
-
+
|
|
531
|
+
+ rng_settings.minis_seed
|
|
532
532
|
self.ips[synapse_id].setRNGs(
|
|
533
533
|
sid + 200,
|
|
534
534
|
self.cell_id.id + 250,
|
|
@@ -543,25 +543,26 @@ class Cell(InjectableMixin, PlottableMixin):
|
|
|
543
543
|
uniformrng = neuron.h.Random()
|
|
544
544
|
self.persistent.append(uniformrng)
|
|
545
545
|
|
|
546
|
-
|
|
546
|
+
base_seed = rng_settings.base_seed
|
|
547
|
+
if rng_settings.mode == 'Compatibility':
|
|
547
548
|
exp_seed1 = sid * 100000 + 200
|
|
548
549
|
exp_seed2 = self.cell_id.id + 250 + base_seed + \
|
|
549
|
-
|
|
550
|
+
rng_settings.minis_seed
|
|
550
551
|
uniform_seed1 = sid * 100000 + 300
|
|
551
552
|
uniform_seed2 = self.cell_id.id + 250 + base_seed + \
|
|
552
|
-
|
|
553
|
-
elif
|
|
553
|
+
rng_settings.minis_seed
|
|
554
|
+
elif rng_settings.mode == "UpdatedMCell":
|
|
554
555
|
exp_seed1 = sid * 1000 + 200
|
|
555
556
|
exp_seed2 = source_popid * 16777216 + self.cell_id.id + 250 + \
|
|
556
557
|
base_seed + \
|
|
557
|
-
|
|
558
|
+
rng_settings.minis_seed
|
|
558
559
|
uniform_seed1 = sid * 1000 + 300
|
|
559
560
|
uniform_seed2 = source_popid * 16777216 + self.cell_id.id + 250 \
|
|
560
561
|
+ base_seed + \
|
|
561
|
-
|
|
562
|
+
rng_settings.minis_seed
|
|
562
563
|
else:
|
|
563
564
|
raise ValueError(
|
|
564
|
-
f"Cell: Unknown rng mode: {
|
|
565
|
+
f"Cell: Unknown rng mode: {rng_settings.mode}")
|
|
565
566
|
|
|
566
567
|
exprng.MCellRan4(exp_seed1, exp_seed2)
|
|
567
568
|
exprng.negexp(1.0)
|
|
@@ -695,9 +696,10 @@ class Cell(InjectableMixin, PlottableMixin):
|
|
|
695
696
|
"""Get a vector of AIS voltage."""
|
|
696
697
|
return self.get_recording('self.axonal[1](0.5)._ref_v')
|
|
697
698
|
|
|
698
|
-
|
|
699
|
+
@property
|
|
700
|
+
def n_segments(self) -> int:
|
|
699
701
|
"""Get the number of segments in the cell."""
|
|
700
|
-
return sum(section.nseg for section in self.
|
|
702
|
+
return sum(section.nseg for section in self.sections.values())
|
|
701
703
|
|
|
702
704
|
def add_synapse_replay(
|
|
703
705
|
self, stimulus: SynapseReplay, spike_threshold: float, spike_location: str
|
bluecellulab/cell/injector.py
CHANGED
|
@@ -27,6 +27,7 @@ from bluecellulab.cell.stimuli_generator import (
|
|
|
27
27
|
get_relative_shotnoise_params,
|
|
28
28
|
)
|
|
29
29
|
from bluecellulab.exceptions import BluecellulabError
|
|
30
|
+
from bluecellulab.rngsettings import RNGSettings
|
|
30
31
|
from bluecellulab.stimulus.circuit_stimulus_definitions import (
|
|
31
32
|
ClampMode,
|
|
32
33
|
Hyperpolarizing,
|
|
@@ -191,20 +192,21 @@ class InjectableMixin:
|
|
|
191
192
|
|
|
192
193
|
def _get_noise_step_rand(self, noisestim_count):
|
|
193
194
|
"""Return rng for noise step stimulus."""
|
|
194
|
-
|
|
195
|
+
rng_settings = RNGSettings.get_instance()
|
|
196
|
+
if rng_settings.mode == "Compatibility":
|
|
195
197
|
rng = neuron.h.Random(self.cell_id.id + noisestim_count)
|
|
196
|
-
elif
|
|
198
|
+
elif rng_settings.mode == "UpdatedMCell":
|
|
197
199
|
rng = neuron.h.Random()
|
|
198
200
|
rng.MCellRan4(
|
|
199
201
|
noisestim_count * 10000 + 100,
|
|
200
|
-
|
|
201
|
-
|
|
202
|
+
rng_settings.base_seed +
|
|
203
|
+
rng_settings.stimulus_seed +
|
|
202
204
|
self.cell_id.id * 1000)
|
|
203
|
-
elif
|
|
205
|
+
elif rng_settings.mode == "Random123":
|
|
204
206
|
rng = neuron.h.Random()
|
|
205
207
|
rng.Random123(
|
|
206
208
|
noisestim_count + 100,
|
|
207
|
-
|
|
209
|
+
rng_settings.stimulus_seed + 500,
|
|
208
210
|
self.cell_id.id + 300)
|
|
209
211
|
|
|
210
212
|
self.persistent.append(rng)
|
|
@@ -268,9 +270,10 @@ class InjectableMixin:
|
|
|
268
270
|
|
|
269
271
|
def _get_ornstein_uhlenbeck_rand(self, stim_count, seed):
|
|
270
272
|
"""Return rng for ornstein_uhlenbeck simulation."""
|
|
271
|
-
|
|
273
|
+
rng_settings = RNGSettings.get_instance()
|
|
274
|
+
if rng_settings.mode == "Random123":
|
|
272
275
|
seed1 = stim_count + 2997 # stimulus block
|
|
273
|
-
seed2 =
|
|
276
|
+
seed2 = rng_settings.stimulus_seed + 291204 # stimulus type
|
|
274
277
|
seed3 = self.cell_id.id + 123 if seed is None else seed # GID
|
|
275
278
|
logger.debug("Using ornstein_uhlenbeck process seeds %d %d %d" %
|
|
276
279
|
(seed1, seed2, seed3))
|
|
@@ -284,9 +287,10 @@ class InjectableMixin:
|
|
|
284
287
|
|
|
285
288
|
def _get_shotnoise_step_rand(self, shotnoise_stim_count, seed=None):
|
|
286
289
|
"""Return rng for shot noise step stimulus."""
|
|
287
|
-
|
|
290
|
+
rng_settings = RNGSettings.get_instance()
|
|
291
|
+
if rng_settings.mode == "Random123":
|
|
288
292
|
seed1 = shotnoise_stim_count + 2997
|
|
289
|
-
seed2 =
|
|
293
|
+
seed2 = rng_settings.stimulus_seed + 19216
|
|
290
294
|
seed3 = self.cell_id.id + 123 if seed is None else seed
|
|
291
295
|
logger.debug("Using shot noise seeds %d %d %d" %
|
|
292
296
|
(seed1, seed2, seed3))
|
|
@@ -439,14 +443,14 @@ class InjectableMixin:
|
|
|
439
443
|
time_vector = neuron.h.Vector().from_python(t_content)
|
|
440
444
|
current_vector = neuron.h.Vector().from_python(i_content)
|
|
441
445
|
|
|
442
|
-
|
|
443
|
-
self.persistent.extend([
|
|
446
|
+
iclamp = neuron.h.IClamp(segx, sec=section)
|
|
447
|
+
self.persistent.extend([iclamp, time_vector, current_vector])
|
|
444
448
|
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
current_vector.play(
|
|
449
|
+
iclamp.delay = t_content[0]
|
|
450
|
+
iclamp.dur = t_content[-1] - t_content[0]
|
|
451
|
+
current_vector.play(iclamp._ref_amp, time_vector)
|
|
448
452
|
|
|
449
|
-
return current_vector
|
|
453
|
+
return iclamp, current_vector
|
|
450
454
|
|
|
451
455
|
@deprecated("Use add_sin_current instead.")
|
|
452
456
|
def addSineCurrentInject(self, start_time, stop_time, freq,
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"""Neuron recordings related functions."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
from bluecellulab.type_aliases import NeuronSection
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def section_to_voltage_recording_str(section: NeuronSection, segment=0.5) -> str:
|
|
7
|
+
"""Converts a section and segment to voltage recording string."""
|
|
8
|
+
return f"neuron.h.{section.name()}({segment})._ref_v"
|