bluecellulab 2.4.0__py3-none-any.whl → 2.6.37__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 +10 -3
- bluecellulab/analysis/__init__.py +0 -0
- bluecellulab/analysis/inject_sequence.py +165 -0
- bluecellulab/cell/cell_dict.py +1 -1
- bluecellulab/cell/core.py +95 -76
- bluecellulab/cell/injector.py +8 -10
- bluecellulab/cell/plotting.py +2 -1
- bluecellulab/cell/random.py +1 -1
- bluecellulab/cell/recording.py +8 -0
- bluecellulab/cell/section_distance.py +1 -1
- bluecellulab/cell/serialized_sections.py +4 -6
- bluecellulab/cell/sonata_proxy.py +1 -1
- bluecellulab/cell/stimuli_generator.py +26 -8
- bluecellulab/cell/template.py +12 -4
- bluecellulab/circuit/circuit_access/bluepy_circuit_access.py +12 -20
- bluecellulab/circuit/circuit_access/definition.py +1 -1
- bluecellulab/circuit/circuit_access/sonata_circuit_access.py +5 -4
- bluecellulab/circuit/config/bluepy_simulation_config.py +1 -1
- bluecellulab/circuit/config/definition.py +1 -1
- bluecellulab/circuit/config/sections.py +1 -1
- bluecellulab/circuit/config/sonata_simulation_config.py +1 -1
- bluecellulab/circuit/format.py +1 -1
- bluecellulab/circuit/iotools.py +2 -2
- bluecellulab/circuit/node_id.py +1 -1
- bluecellulab/circuit/simulation_access.py +11 -8
- bluecellulab/circuit/synapse_properties.py +25 -9
- bluecellulab/circuit/validate.py +1 -1
- bluecellulab/{ssim.py → circuit_simulation.py} +36 -34
- bluecellulab/connection.py +1 -1
- bluecellulab/dendrogram.py +1 -1
- bluecellulab/exceptions.py +1 -1
- bluecellulab/graph.py +1 -1
- bluecellulab/importer.py +6 -8
- bluecellulab/neuron_interpreter.py +1 -1
- bluecellulab/plotwindow.py +1 -1
- bluecellulab/psection.py +11 -16
- bluecellulab/psegment.py +4 -4
- bluecellulab/rngsettings.py +4 -4
- bluecellulab/simulation/__init__.py +0 -1
- bluecellulab/simulation/neuron_globals.py +48 -4
- bluecellulab/simulation/parallel.py +40 -0
- bluecellulab/simulation/simulation.py +28 -32
- bluecellulab/stimulus/circuit_stimulus_definitions.py +11 -6
- bluecellulab/stimulus/factory.py +537 -102
- bluecellulab/synapse/synapse_factory.py +8 -4
- bluecellulab/synapse/synapse_types.py +1 -1
- bluecellulab/tools.py +20 -13
- bluecellulab/type_aliases.py +4 -1
- bluecellulab/utils.py +20 -16
- {bluecellulab-2.4.0.dist-info → bluecellulab-2.6.37.dist-info}/LICENSE +0 -7
- {bluecellulab-2.4.0.dist-info → bluecellulab-2.6.37.dist-info}/METADATA +42 -25
- bluecellulab-2.6.37.dist-info/RECORD +70 -0
- {bluecellulab-2.4.0.dist-info → bluecellulab-2.6.37.dist-info}/WHEEL +1 -1
- bluecellulab-2.4.0.dist-info/RECORD +0 -66
- {bluecellulab-2.4.0.dist-info → bluecellulab-2.6.37.dist-info}/AUTHORS.txt +0 -0
- {bluecellulab-2.4.0.dist-info → bluecellulab-2.6.37.dist-info}/top_level.txt +0 -0
bluecellulab/__init__.py
CHANGED
|
@@ -7,10 +7,10 @@ try:
|
|
|
7
7
|
except ImportError:
|
|
8
8
|
BLUEPY_AVAILABLE = False
|
|
9
9
|
|
|
10
|
-
from .importer import
|
|
11
|
-
from .tools import * # NOQA
|
|
10
|
+
from bluecellulab.importer import import_hoc
|
|
12
11
|
from .verbosity import *
|
|
13
12
|
from .cell import Cell, create_ball_stick # NOQA
|
|
13
|
+
from .circuit import EmodelProperties
|
|
14
14
|
from .connection import Connection # NOQA
|
|
15
15
|
from .plotwindow import PlotWindow # NOQA
|
|
16
16
|
from .dendrogram import Dendrogram # NOQA
|
|
@@ -18,4 +18,11 @@ from .psection import PSection # NOQA
|
|
|
18
18
|
from .psegment import PSegment # NOQA
|
|
19
19
|
from .simulation import Simulation # NOQA
|
|
20
20
|
from .rngsettings import RNGSettings # NOQA
|
|
21
|
-
from .
|
|
21
|
+
from .circuit_simulation import CircuitSimulation # NOQA
|
|
22
|
+
import neuron
|
|
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,165 @@
|
|
|
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.parallel import IsolatedProcess
|
|
11
|
+
from bluecellulab.simulation.simulation import Simulation
|
|
12
|
+
from bluecellulab.stimulus.circuit_stimulus_definitions import Hyperpolarizing
|
|
13
|
+
from bluecellulab.stimulus.factory import Stimulus, StimulusFactory
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class StimulusName(Enum):
|
|
17
|
+
"""Allowed values for the StimulusName."""
|
|
18
|
+
AP_WAVEFORM = auto()
|
|
19
|
+
IDREST = auto()
|
|
20
|
+
IV = auto()
|
|
21
|
+
FIRE_PATTERN = auto()
|
|
22
|
+
POS_CHEOPS = auto()
|
|
23
|
+
NEG_CHEOPS = auto()
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class Recording(NamedTuple):
|
|
27
|
+
"""A tuple of the current, voltage and time recordings."""
|
|
28
|
+
current: np.ndarray
|
|
29
|
+
voltage: np.ndarray
|
|
30
|
+
time: np.ndarray
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
StimulusRecordings = Dict[str, Recording]
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def run_stimulus(
|
|
37
|
+
template_params: TemplateParams,
|
|
38
|
+
stimulus: Stimulus,
|
|
39
|
+
section: str,
|
|
40
|
+
segment: float,
|
|
41
|
+
cvode: bool = True,
|
|
42
|
+
add_hypamp: bool = True,
|
|
43
|
+
) -> Recording:
|
|
44
|
+
"""Creates a cell and stimulates it with a given stimulus.
|
|
45
|
+
|
|
46
|
+
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.
|
|
52
|
+
|
|
53
|
+
Returns:
|
|
54
|
+
The voltage-time recording at the specified location.
|
|
55
|
+
|
|
56
|
+
Raises:
|
|
57
|
+
ValueError: If the time and voltage arrays are not the same length.
|
|
58
|
+
"""
|
|
59
|
+
cell = Cell.from_template_parameters(template_params)
|
|
60
|
+
neuron_section = cell.sections[section]
|
|
61
|
+
if add_hypamp:
|
|
62
|
+
hyp_stim = Hyperpolarizing(target="", delay=0.0, duration=stimulus.stimulus_time)
|
|
63
|
+
cell.add_replay_hypamp(hyp_stim)
|
|
64
|
+
cell.add_voltage_recording(neuron_section, segment)
|
|
65
|
+
iclamp, _ = cell.inject_current_waveform(
|
|
66
|
+
stimulus.time, stimulus.current, section=neuron_section, segx=segment
|
|
67
|
+
)
|
|
68
|
+
current_vector = neuron.h.Vector()
|
|
69
|
+
current_vector.record(iclamp._ref_i)
|
|
70
|
+
simulation = Simulation(cell)
|
|
71
|
+
simulation.run(stimulus.stimulus_time, cvode=cvode)
|
|
72
|
+
current = np.array(current_vector.to_python())
|
|
73
|
+
voltage = cell.get_voltage_recording(neuron_section, segment)
|
|
74
|
+
time = cell.get_time()
|
|
75
|
+
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)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def apply_multiple_stimuli(
|
|
81
|
+
cell: Cell,
|
|
82
|
+
stimulus_name: StimulusName,
|
|
83
|
+
amplitudes: Sequence[float],
|
|
84
|
+
threshold_based: bool = True,
|
|
85
|
+
section_name: str | None = None,
|
|
86
|
+
segment: float = 0.5,
|
|
87
|
+
n_processes: int | None = None,
|
|
88
|
+
cvode: bool = True,
|
|
89
|
+
add_hypamp: bool = True,
|
|
90
|
+
) -> StimulusRecordings:
|
|
91
|
+
"""Apply multiple stimuli to the cell on isolated processes.
|
|
92
|
+
|
|
93
|
+
Args:
|
|
94
|
+
cell: The cell to which the stimuli are applied.
|
|
95
|
+
stimulus_name: The name of the stimulus to apply.
|
|
96
|
+
amplitudes: The amplitudes of the stimuli to apply.
|
|
97
|
+
threshold_based: Whether to consider amplitudes to be
|
|
98
|
+
threshold percentages or to be raw amplitudes.
|
|
99
|
+
section_name: Section name of the cell where the stimuli are applied.
|
|
100
|
+
If None, the stimuli are applied at the soma[0] of the cell.
|
|
101
|
+
segment: The segment of the section where the stimuli are applied.
|
|
102
|
+
n_processes: The number of processes to use for running the stimuli.
|
|
103
|
+
cvode: True to use variable time-steps. False for fixed time-steps.
|
|
104
|
+
add_hypamp: True to add the cell's holding current stimulus
|
|
105
|
+
|
|
106
|
+
Returns:
|
|
107
|
+
A dictionary where the keys are the names of the stimuli and the values
|
|
108
|
+
are the recordings of the cell's response to each stimulus.
|
|
109
|
+
|
|
110
|
+
Raises:
|
|
111
|
+
ValueError: If the stimulus name is not recognized.
|
|
112
|
+
"""
|
|
113
|
+
res: StimulusRecordings = {}
|
|
114
|
+
stim_factory = StimulusFactory(dt=1.0)
|
|
115
|
+
task_args = []
|
|
116
|
+
section_name = section_name if section_name is not None else "soma[0]"
|
|
117
|
+
|
|
118
|
+
# Prepare arguments for each stimulus
|
|
119
|
+
for amplitude in amplitudes:
|
|
120
|
+
if threshold_based:
|
|
121
|
+
thres_perc = amplitude
|
|
122
|
+
amp = None
|
|
123
|
+
else:
|
|
124
|
+
thres_perc = None
|
|
125
|
+
amp = amplitude
|
|
126
|
+
|
|
127
|
+
if stimulus_name == StimulusName.AP_WAVEFORM:
|
|
128
|
+
stimulus = stim_factory.ap_waveform(
|
|
129
|
+
threshold_current=cell.threshold, threshold_percentage=thres_perc, amplitude=amp
|
|
130
|
+
)
|
|
131
|
+
elif stimulus_name == StimulusName.IDREST:
|
|
132
|
+
stimulus = stim_factory.idrest(
|
|
133
|
+
threshold_current=cell.threshold, threshold_percentage=thres_perc, amplitude=amp
|
|
134
|
+
)
|
|
135
|
+
elif stimulus_name == StimulusName.IV:
|
|
136
|
+
stimulus = stim_factory.iv(
|
|
137
|
+
threshold_current=cell.threshold, threshold_percentage=thres_perc, amplitude=amp
|
|
138
|
+
)
|
|
139
|
+
elif stimulus_name == StimulusName.FIRE_PATTERN:
|
|
140
|
+
stimulus = stim_factory.fire_pattern(
|
|
141
|
+
threshold_current=cell.threshold, threshold_percentage=thres_perc, amplitude=amp
|
|
142
|
+
)
|
|
143
|
+
elif stimulus_name == StimulusName.POS_CHEOPS:
|
|
144
|
+
stimulus = stim_factory.pos_cheops(
|
|
145
|
+
threshold_current=cell.threshold, threshold_percentage=thres_perc, amplitude=amp
|
|
146
|
+
)
|
|
147
|
+
elif stimulus_name == StimulusName.NEG_CHEOPS:
|
|
148
|
+
stimulus = stim_factory.neg_cheops(
|
|
149
|
+
threshold_current=cell.threshold, threshold_percentage=thres_perc, amplitude=amp
|
|
150
|
+
)
|
|
151
|
+
else:
|
|
152
|
+
raise ValueError("Unknown stimulus name.")
|
|
153
|
+
|
|
154
|
+
task_args.append((cell.template_params, stimulus, section_name, segment, cvode, add_hypamp))
|
|
155
|
+
|
|
156
|
+
with IsolatedProcess(processes=n_processes) as pool:
|
|
157
|
+
# Map expects a function and a list of argument tuples
|
|
158
|
+
results = pool.starmap(run_stimulus, task_args)
|
|
159
|
+
|
|
160
|
+
# Associate each result with a key
|
|
161
|
+
for amplitude, result in zip(amplitudes, results):
|
|
162
|
+
key = f"{stimulus_name}_{amplitude}"
|
|
163
|
+
res[key] = result
|
|
164
|
+
|
|
165
|
+
return res
|
bluecellulab/cell/cell_dict.py
CHANGED
bluecellulab/cell/core.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright
|
|
1
|
+
# Copyright 2023-2024 Blue Brain Project / EPFL
|
|
2
2
|
|
|
3
3
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
4
|
# you may not use this file except in compliance with the License.
|
|
@@ -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,7 +55,25 @@ 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,
|
|
@@ -73,6 +92,12 @@ class Cell(InjectableMixin, PlottableMixin):
|
|
|
73
92
|
emodel_properties: Template specific emodel properties.
|
|
74
93
|
"""
|
|
75
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
|
+
)
|
|
76
101
|
if cell_id is None:
|
|
77
102
|
cell_id = CellId("", Cell.last_id)
|
|
78
103
|
Cell.last_id += 1
|
|
@@ -86,17 +111,16 @@ class Cell(InjectableMixin, PlottableMixin):
|
|
|
86
111
|
if emodel_properties is None:
|
|
87
112
|
raise BluecellulabError('EmodelProperties must be provided for v6 template')
|
|
88
113
|
self.hypamp: float | None = emodel_properties.holding_current
|
|
89
|
-
self.threshold: float
|
|
114
|
+
self.threshold: float = emodel_properties.threshold_current
|
|
90
115
|
else:
|
|
91
116
|
try:
|
|
92
117
|
self.hypamp = self.cell.getHypAmp()
|
|
93
118
|
except AttributeError:
|
|
94
119
|
self.hypamp = None
|
|
95
|
-
|
|
96
120
|
try:
|
|
97
121
|
self.threshold = self.cell.getThreshold()
|
|
98
122
|
except AttributeError:
|
|
99
|
-
self.threshold =
|
|
123
|
+
self.threshold = 0.0
|
|
100
124
|
self.soma = public_hoc_cell(self.cell).soma[0]
|
|
101
125
|
# WARNING: this finitialize 'must' be here, otherwhise the
|
|
102
126
|
# diameters of the loaded morph are wrong
|
|
@@ -118,7 +142,8 @@ class Cell(InjectableMixin, PlottableMixin):
|
|
|
118
142
|
dt=self.record_dt)
|
|
119
143
|
|
|
120
144
|
self.delayed_weights = queue.PriorityQueue() # type: ignore
|
|
121
|
-
self.psections,
|
|
145
|
+
self.psections: dict[int, PSection] = {}
|
|
146
|
+
self.secname_to_psection: dict[str, PSection] = {}
|
|
122
147
|
|
|
123
148
|
# Keep track of when a cell is made passive by make_passive()
|
|
124
149
|
# Used to know when re_init_rng() can be executed
|
|
@@ -131,6 +156,18 @@ class Cell(InjectableMixin, PlottableMixin):
|
|
|
131
156
|
# as the object exists
|
|
132
157
|
self.persistent: list[HocObjectType] = []
|
|
133
158
|
|
|
159
|
+
def _init_psections(self) -> None:
|
|
160
|
+
"""Initialize the psections of the cell."""
|
|
161
|
+
if not self.psections:
|
|
162
|
+
self.psections, self.secname_to_psection = init_psections(public_hoc_cell(self.cell))
|
|
163
|
+
|
|
164
|
+
def _extract_sections(self, sections) -> SectionMapping:
|
|
165
|
+
res: SectionMapping = {}
|
|
166
|
+
for section in sections:
|
|
167
|
+
key_name = str(section).split(".")[-1]
|
|
168
|
+
res[key_name] = section
|
|
169
|
+
return res
|
|
170
|
+
|
|
134
171
|
@property
|
|
135
172
|
def somatic(self) -> list[NeuronSection]:
|
|
136
173
|
return list(public_hoc_cell(self.cell).somatic)
|
|
@@ -148,8 +185,8 @@ class Cell(InjectableMixin, PlottableMixin):
|
|
|
148
185
|
return list(public_hoc_cell(self.cell).axonal)
|
|
149
186
|
|
|
150
187
|
@property
|
|
151
|
-
def
|
|
152
|
-
return
|
|
188
|
+
def sections(self) -> SectionMapping:
|
|
189
|
+
return self._extract_sections(public_hoc_cell(self.cell).all)
|
|
153
190
|
|
|
154
191
|
def __repr__(self) -> str:
|
|
155
192
|
base_info = f"Cell Object: {super().__repr__()}"
|
|
@@ -160,32 +197,14 @@ class Cell(InjectableMixin, PlottableMixin):
|
|
|
160
197
|
"""Connect this cell to a circuit via sonata proxy."""
|
|
161
198
|
self.sonata_proxy = sonata_proxy
|
|
162
199
|
|
|
163
|
-
def re_init_rng(self
|
|
200
|
+
def re_init_rng(self) -> None:
|
|
164
201
|
"""Reinitialize the random number generator for stochastic channels."""
|
|
165
|
-
|
|
166
202
|
if not self.is_made_passive:
|
|
167
|
-
|
|
168
|
-
channel_id = 0
|
|
169
|
-
for section in self.somatic:
|
|
170
|
-
for seg in section:
|
|
171
|
-
neuron.h.setdata_StochKv(seg.x, sec=section)
|
|
172
|
-
neuron.h.setRNG_StochKv(channel_id, self.cell_id.id)
|
|
173
|
-
channel_id += 1
|
|
174
|
-
for section in self.basal:
|
|
175
|
-
for seg in section:
|
|
176
|
-
neuron.h.setdata_StochKv(seg.x, sec=section)
|
|
177
|
-
neuron.h.setRNG_StochKv(channel_id, self.cell_id.id)
|
|
178
|
-
channel_id += 1
|
|
179
|
-
for section in self.apical:
|
|
180
|
-
for seg in section:
|
|
181
|
-
neuron.h.setdata_StochKv(seg.x, sec=section)
|
|
182
|
-
neuron.h.setRNG_StochKv(channel_id, self.cell_id.id)
|
|
183
|
-
channel_id += 1
|
|
184
|
-
else:
|
|
185
|
-
self.cell.re_init_rng()
|
|
203
|
+
self.cell.re_init_rng()
|
|
186
204
|
|
|
187
205
|
def get_psection(self, section_id: int | str) -> PSection:
|
|
188
206
|
"""Return a python section with the specified section id."""
|
|
207
|
+
self._init_psections()
|
|
189
208
|
if isinstance(section_id, int):
|
|
190
209
|
return self.psections[section_id]
|
|
191
210
|
elif isinstance(section_id, str):
|
|
@@ -197,7 +216,7 @@ class Cell(InjectableMixin, PlottableMixin):
|
|
|
197
216
|
|
|
198
217
|
def make_passive(self) -> None:
|
|
199
218
|
"""Make the cell passive by deactivating all the active channels."""
|
|
200
|
-
for section in self.
|
|
219
|
+
for section in self.sections.values():
|
|
201
220
|
mech_names = set()
|
|
202
221
|
for seg in section:
|
|
203
222
|
for mech in seg:
|
|
@@ -232,14 +251,14 @@ class Cell(InjectableMixin, PlottableMixin):
|
|
|
232
251
|
|
|
233
252
|
def _default_enable_ttx(self) -> None:
|
|
234
253
|
"""Default enable_ttx implementation."""
|
|
235
|
-
for section in self.
|
|
254
|
+
for section in self.sections.values():
|
|
236
255
|
if not neuron.h.ismembrane("TTXDynamicsSwitch"):
|
|
237
256
|
section.insert('TTXDynamicsSwitch')
|
|
238
257
|
section.ttxo_level_TTXDynamicsSwitch = 1.0
|
|
239
258
|
|
|
240
259
|
def _default_disable_ttx(self) -> None:
|
|
241
260
|
"""Default disable_ttx implementation."""
|
|
242
|
-
for section in self.
|
|
261
|
+
for section in self.sections.values():
|
|
243
262
|
if not neuron.h.ismembrane("TTXDynamicsSwitch"):
|
|
244
263
|
section.insert('TTXDynamicsSwitch')
|
|
245
264
|
section.ttxo_level_TTXDynamicsSwitch = 1e-14
|
|
@@ -247,7 +266,7 @@ class Cell(InjectableMixin, PlottableMixin):
|
|
|
247
266
|
def area(self) -> float:
|
|
248
267
|
"""The total surface area of the cell."""
|
|
249
268
|
area = 0.0
|
|
250
|
-
for section in self.
|
|
269
|
+
for section in self.sections.values():
|
|
251
270
|
x_s = np.arange(1.0 / (2 * section.nseg), 1.0,
|
|
252
271
|
1.0 / (section.nseg))
|
|
253
272
|
for x in x_s:
|
|
@@ -297,7 +316,7 @@ class Cell(InjectableMixin, PlottableMixin):
|
|
|
297
316
|
self.add_recording("self.axonal[1](0.5)._ref_v", dt=dt)
|
|
298
317
|
|
|
299
318
|
def add_voltage_recording(
|
|
300
|
-
self, section:
|
|
319
|
+
self, section: Optional[NeuronSection] = None, segx: float = 0.5, dt: Optional[float] = None
|
|
301
320
|
) -> None:
|
|
302
321
|
"""Add a voltage recording to a certain section at a given segment
|
|
303
322
|
(segx).
|
|
@@ -309,11 +328,13 @@ class Cell(InjectableMixin, PlottableMixin):
|
|
|
309
328
|
dt: Recording time step. If not provided, the recording step will
|
|
310
329
|
default to the simulator's time step.
|
|
311
330
|
"""
|
|
312
|
-
|
|
331
|
+
if section is None:
|
|
332
|
+
section = self.soma
|
|
333
|
+
var_name = section_to_voltage_recording_str(section, segx)
|
|
313
334
|
self.add_recording(var_name, dt)
|
|
314
335
|
|
|
315
336
|
def get_voltage_recording(
|
|
316
|
-
self, section:
|
|
337
|
+
self, section: Optional[NeuronSection] = None, segx: float = 0.5
|
|
317
338
|
) -> np.ndarray:
|
|
318
339
|
"""Get a voltage recording for a certain section at a given segment
|
|
319
340
|
(segx).
|
|
@@ -329,7 +350,9 @@ class Cell(InjectableMixin, PlottableMixin):
|
|
|
329
350
|
Raises:
|
|
330
351
|
BluecellulabError: If voltage recording was not added previously using add_voltage_recording.
|
|
331
352
|
"""
|
|
332
|
-
|
|
353
|
+
if section is None:
|
|
354
|
+
section = self.soma
|
|
355
|
+
recording_name = section_to_voltage_recording_str(section, segx)
|
|
333
356
|
if recording_name in self.recordings:
|
|
334
357
|
return self.get_recording(recording_name)
|
|
335
358
|
else:
|
|
@@ -340,15 +363,13 @@ class Cell(InjectableMixin, PlottableMixin):
|
|
|
340
363
|
|
|
341
364
|
def add_allsections_voltagerecordings(self):
|
|
342
365
|
"""Add a voltage recording to every section of the cell."""
|
|
343
|
-
|
|
344
|
-
for section in all_sections:
|
|
366
|
+
for section in self.sections.values():
|
|
345
367
|
self.add_voltage_recording(section, dt=self.record_dt)
|
|
346
368
|
|
|
347
369
|
def get_allsections_voltagerecordings(self) -> dict[str, np.ndarray]:
|
|
348
370
|
"""Get all the voltage recordings from all the sections."""
|
|
349
371
|
all_section_voltages = {}
|
|
350
|
-
|
|
351
|
-
for section in all_sections:
|
|
372
|
+
for section in self.sections.values():
|
|
352
373
|
recording = self.get_voltage_recording(section)
|
|
353
374
|
all_section_voltages[section.name()] = recording
|
|
354
375
|
return all_section_voltages
|
|
@@ -426,10 +447,10 @@ class Cell(InjectableMixin, PlottableMixin):
|
|
|
426
447
|
"""
|
|
427
448
|
if location == "soma":
|
|
428
449
|
sec = public_hoc_cell(self.cell).soma[0]
|
|
429
|
-
source =
|
|
450
|
+
source = sec(1)._ref_v
|
|
430
451
|
elif location == "AIS":
|
|
431
452
|
sec = public_hoc_cell(self.cell).axon[1]
|
|
432
|
-
source =
|
|
453
|
+
source = sec(0.5)._ref_v
|
|
433
454
|
else:
|
|
434
455
|
raise ValueError("Spike detection location must be soma or AIS")
|
|
435
456
|
netcon = neuron.h.NetCon(source, target, sec=sec)
|
|
@@ -476,10 +497,6 @@ class Cell(InjectableMixin, PlottableMixin):
|
|
|
476
497
|
# numpy int to int
|
|
477
498
|
post_sec_id = int(syn_description[SynapseProperty.POST_SECTION_ID])
|
|
478
499
|
|
|
479
|
-
location = SynapseFactory.determine_synapse_location(
|
|
480
|
-
syn_description, self
|
|
481
|
-
)
|
|
482
|
-
|
|
483
500
|
weight_scalar = connection_modifiers.get('Weight', 1.0)
|
|
484
501
|
exc_mini_frequency, inh_mini_frequency = mini_frequencies \
|
|
485
502
|
if mini_frequencies is not None else (None, None)
|
|
@@ -495,13 +512,15 @@ class Cell(InjectableMixin, PlottableMixin):
|
|
|
495
512
|
spont_minis_rate = inh_mini_frequency
|
|
496
513
|
|
|
497
514
|
if spont_minis_rate is not None and spont_minis_rate > 0:
|
|
498
|
-
|
|
515
|
+
synapse_hoc_args = SynapseFactory.determine_synapse_location(
|
|
516
|
+
syn_description, self
|
|
517
|
+
)
|
|
499
518
|
# add the *minis*: spontaneous synaptic events
|
|
500
519
|
self.ips[synapse_id] = neuron.h.\
|
|
501
|
-
InhPoissonStim(location, sec=
|
|
520
|
+
InhPoissonStim(synapse_hoc_args.location, sec=synapse_hoc_args.section)
|
|
502
521
|
|
|
503
522
|
self.syn_mini_netcons[synapse_id] = neuron.h.\
|
|
504
|
-
NetCon(self.ips[synapse_id], synapse.hsynapse, sec=
|
|
523
|
+
NetCon(self.ips[synapse_id], synapse.hsynapse, sec=synapse_hoc_args.section)
|
|
505
524
|
self.syn_mini_netcons[synapse_id].delay = 0.1
|
|
506
525
|
self.syn_mini_netcons[synapse_id].weight[0] = weight * weight_scalar
|
|
507
526
|
# set netcon type
|
|
@@ -682,9 +701,10 @@ class Cell(InjectableMixin, PlottableMixin):
|
|
|
682
701
|
"""Get a vector of AIS voltage."""
|
|
683
702
|
return self.get_recording('self.axonal[1](0.5)._ref_v')
|
|
684
703
|
|
|
685
|
-
|
|
704
|
+
@property
|
|
705
|
+
def n_segments(self) -> int:
|
|
686
706
|
"""Get the number of segments in the cell."""
|
|
687
|
-
return sum(section.nseg for section in self.
|
|
707
|
+
return sum(section.nseg for section in self.sections.values())
|
|
688
708
|
|
|
689
709
|
def add_synapse_replay(
|
|
690
710
|
self, stimulus: SynapseReplay, spike_threshold: float, spike_location: str
|
|
@@ -699,27 +719,26 @@ class Cell(InjectableMixin, PlottableMixin):
|
|
|
699
719
|
pre_gid = CellId(
|
|
700
720
|
source_population, int(synapse.syn_description[SynapseProperty.PRE_GID])
|
|
701
721
|
)
|
|
702
|
-
if
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
spikes_of_interest
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
self.connections[synapse_id] = connection
|
|
722
|
+
if pre_gid.id in synapse_spikes:
|
|
723
|
+
spikes_of_interest = synapse_spikes[pre_gid.id]
|
|
724
|
+
# filter spikes of interest >=stimulus.delay, <=stimulus.duration
|
|
725
|
+
spikes_of_interest = spikes_of_interest[
|
|
726
|
+
(spikes_of_interest >= stimulus.delay)
|
|
727
|
+
& (spikes_of_interest <= stimulus.duration)
|
|
728
|
+
]
|
|
729
|
+
connection = bluecellulab.Connection(
|
|
730
|
+
synapse,
|
|
731
|
+
pre_spiketrain=spikes_of_interest,
|
|
732
|
+
pre_cell=None,
|
|
733
|
+
stim_dt=self.record_dt,
|
|
734
|
+
spike_threshold=spike_threshold,
|
|
735
|
+
spike_location=spike_location,
|
|
736
|
+
)
|
|
737
|
+
logger.debug(
|
|
738
|
+
f"Added synapse replay from {pre_gid} to {self.cell_id.id}, {synapse_id}"
|
|
739
|
+
)
|
|
740
|
+
|
|
741
|
+
self.connections[synapse_id] = connection
|
|
723
742
|
|
|
724
743
|
@property
|
|
725
744
|
def info_dict(self):
|
bluecellulab/cell/injector.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright
|
|
1
|
+
# Copyright 2023-2024 Blue Brain Project / EPFL
|
|
2
2
|
|
|
3
3
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
4
|
# you may not use this file except in compliance with the License.
|
|
@@ -360,17 +360,15 @@ class InjectableMixin:
|
|
|
360
360
|
stimulus: RelativeShotNoise,
|
|
361
361
|
shotnoise_stim_count=0):
|
|
362
362
|
"""Add a replay relative shot noise stimulus."""
|
|
363
|
-
cv_square = stimulus.amp_cv**2
|
|
364
363
|
|
|
365
364
|
stim_mode = stimulus.mode
|
|
366
365
|
rel_prop = self.relativity_proportion(stim_mode)
|
|
367
366
|
|
|
368
367
|
mean = stimulus.mean_percent / 100 * rel_prop
|
|
369
368
|
sd = stimulus.sd_percent / 100 * rel_prop
|
|
370
|
-
var = sd * sd
|
|
371
369
|
|
|
372
370
|
rate, amp_mean, amp_var = get_relative_shotnoise_params(
|
|
373
|
-
mean,
|
|
371
|
+
mean, sd, stimulus.decay_time, stimulus.rise_time, stimulus.relative_skew)
|
|
374
372
|
|
|
375
373
|
rng = self._get_shotnoise_step_rand(shotnoise_stim_count, stimulus.seed)
|
|
376
374
|
tvec, svec = gen_shotnoise_signal(stimulus.decay_time, stimulus.rise_time, rate, amp_mean,
|
|
@@ -443,14 +441,14 @@ class InjectableMixin:
|
|
|
443
441
|
time_vector = neuron.h.Vector().from_python(t_content)
|
|
444
442
|
current_vector = neuron.h.Vector().from_python(i_content)
|
|
445
443
|
|
|
446
|
-
|
|
447
|
-
self.persistent.extend([
|
|
444
|
+
iclamp = neuron.h.IClamp(segx, sec=section)
|
|
445
|
+
self.persistent.extend([iclamp, time_vector, current_vector])
|
|
448
446
|
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
current_vector.play(
|
|
447
|
+
iclamp.delay = t_content[0]
|
|
448
|
+
iclamp.dur = t_content[-1] - t_content[0]
|
|
449
|
+
current_vector.play(iclamp._ref_amp, time_vector)
|
|
452
450
|
|
|
453
|
-
return current_vector
|
|
451
|
+
return iclamp, current_vector
|
|
454
452
|
|
|
455
453
|
@deprecated("Use add_sin_current instead.")
|
|
456
454
|
def addSineCurrentInject(self, start_time, stop_time, freq,
|
bluecellulab/cell/plotting.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright
|
|
1
|
+
# Copyright 2023-2024 Blue Brain Project / EPFL
|
|
2
2
|
|
|
3
3
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
4
|
# you may not use this file except in compliance with the License.
|
|
@@ -60,6 +60,7 @@ class PlottableMixin:
|
|
|
60
60
|
scale_bar_size=10.0,
|
|
61
61
|
fig_title=None):
|
|
62
62
|
"""Show a dendrogram of the cell."""
|
|
63
|
+
self._init_psections()
|
|
63
64
|
cell_dendrogram = bluecellulab.Dendrogram(
|
|
64
65
|
self.psections,
|
|
65
66
|
variable=variable,
|
bluecellulab/cell/random.py
CHANGED
|
@@ -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"
|