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.

Files changed (56) hide show
  1. bluecellulab/__init__.py +10 -3
  2. bluecellulab/analysis/__init__.py +0 -0
  3. bluecellulab/analysis/inject_sequence.py +165 -0
  4. bluecellulab/cell/cell_dict.py +1 -1
  5. bluecellulab/cell/core.py +95 -76
  6. bluecellulab/cell/injector.py +8 -10
  7. bluecellulab/cell/plotting.py +2 -1
  8. bluecellulab/cell/random.py +1 -1
  9. bluecellulab/cell/recording.py +8 -0
  10. bluecellulab/cell/section_distance.py +1 -1
  11. bluecellulab/cell/serialized_sections.py +4 -6
  12. bluecellulab/cell/sonata_proxy.py +1 -1
  13. bluecellulab/cell/stimuli_generator.py +26 -8
  14. bluecellulab/cell/template.py +12 -4
  15. bluecellulab/circuit/circuit_access/bluepy_circuit_access.py +12 -20
  16. bluecellulab/circuit/circuit_access/definition.py +1 -1
  17. bluecellulab/circuit/circuit_access/sonata_circuit_access.py +5 -4
  18. bluecellulab/circuit/config/bluepy_simulation_config.py +1 -1
  19. bluecellulab/circuit/config/definition.py +1 -1
  20. bluecellulab/circuit/config/sections.py +1 -1
  21. bluecellulab/circuit/config/sonata_simulation_config.py +1 -1
  22. bluecellulab/circuit/format.py +1 -1
  23. bluecellulab/circuit/iotools.py +2 -2
  24. bluecellulab/circuit/node_id.py +1 -1
  25. bluecellulab/circuit/simulation_access.py +11 -8
  26. bluecellulab/circuit/synapse_properties.py +25 -9
  27. bluecellulab/circuit/validate.py +1 -1
  28. bluecellulab/{ssim.py → circuit_simulation.py} +36 -34
  29. bluecellulab/connection.py +1 -1
  30. bluecellulab/dendrogram.py +1 -1
  31. bluecellulab/exceptions.py +1 -1
  32. bluecellulab/graph.py +1 -1
  33. bluecellulab/importer.py +6 -8
  34. bluecellulab/neuron_interpreter.py +1 -1
  35. bluecellulab/plotwindow.py +1 -1
  36. bluecellulab/psection.py +11 -16
  37. bluecellulab/psegment.py +4 -4
  38. bluecellulab/rngsettings.py +4 -4
  39. bluecellulab/simulation/__init__.py +0 -1
  40. bluecellulab/simulation/neuron_globals.py +48 -4
  41. bluecellulab/simulation/parallel.py +40 -0
  42. bluecellulab/simulation/simulation.py +28 -32
  43. bluecellulab/stimulus/circuit_stimulus_definitions.py +11 -6
  44. bluecellulab/stimulus/factory.py +537 -102
  45. bluecellulab/synapse/synapse_factory.py +8 -4
  46. bluecellulab/synapse/synapse_types.py +1 -1
  47. bluecellulab/tools.py +20 -13
  48. bluecellulab/type_aliases.py +4 -1
  49. bluecellulab/utils.py +20 -16
  50. {bluecellulab-2.4.0.dist-info → bluecellulab-2.6.37.dist-info}/LICENSE +0 -7
  51. {bluecellulab-2.4.0.dist-info → bluecellulab-2.6.37.dist-info}/METADATA +42 -25
  52. bluecellulab-2.6.37.dist-info/RECORD +70 -0
  53. {bluecellulab-2.4.0.dist-info → bluecellulab-2.6.37.dist-info}/WHEEL +1 -1
  54. bluecellulab-2.4.0.dist-info/RECORD +0 -66
  55. {bluecellulab-2.4.0.dist-info → bluecellulab-2.6.37.dist-info}/AUTHORS.txt +0 -0
  56. {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 * # NOQA
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 .ssim import SSim # NOQA
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
@@ -1,4 +1,4 @@
1
- # Copyright 2012-2024 Blue Brain Project / EPFL
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.
bluecellulab/cell/core.py CHANGED
@@ -1,4 +1,4 @@
1
- # Copyright 2012-2024 Blue Brain Project / EPFL
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 load_hoc_and_mod_files
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
- @load_hoc_and_mod_files
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 | None = emodel_properties.threshold_current
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 = None
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, self.secname_to_psection = init_psections(public_hoc_cell(self.cell))
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 all(self) -> list[NeuronSection]:
152
- return list(public_hoc_cell(self.cell).all)
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, use_random123_stochkv: bool = False) -> None:
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
- if use_random123_stochkv:
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.all:
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.all:
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.all:
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.all:
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: "neuron.h.Section", segx: float = 0.5, dt: Optional[float] = None
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
- var_name = f"neuron.h.{section.name()}({segx})._ref_v"
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: "neuron.h.Section", segx: float = 0.5
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
- recording_name = f"neuron.h.{section.name()}({segx})._ref_v"
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
- all_sections = public_hoc_cell(self.cell).all
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
- all_sections = public_hoc_cell(self.cell).all
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 = public_hoc_cell(self.cell).soma[0](1)._ref_v
450
+ source = sec(1)._ref_v
430
451
  elif location == "AIS":
431
452
  sec = public_hoc_cell(self.cell).axon[1]
432
- source = public_hoc_cell(self.cell).axon[1](0.5)._ref_v
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
- sec = self.get_psection(post_sec_id).hsection
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=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=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
- def getNumberOfSegments(self) -> int:
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.all)
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 self.sonata_proxy.circuit_access.target_contains_cell(stimulus.source, pre_gid):
703
- if pre_gid.id in synapse_spikes:
704
- spikes_of_interest = synapse_spikes[pre_gid.id]
705
- # filter spikes of interest >=stimulus.delay, <=stimulus.duration
706
- spikes_of_interest = spikes_of_interest[
707
- (spikes_of_interest >= stimulus.delay)
708
- & (spikes_of_interest <= stimulus.duration)
709
- ]
710
- connection = bluecellulab.Connection(
711
- synapse,
712
- pre_spiketrain=spikes_of_interest,
713
- pre_cell=None,
714
- stim_dt=self.record_dt,
715
- spike_threshold=spike_threshold,
716
- spike_location=spike_location,
717
- )
718
- logger.debug(
719
- f"Added synapse replay from {pre_gid} to {self.cell_id.id}, {synapse_id}"
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):
@@ -1,4 +1,4 @@
1
- # Copyright 2012-2024 Blue Brain Project / EPFL
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, var, stimulus.decay_time, stimulus.rise_time, cv_square)
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
- pulse = neuron.h.IClamp(segx, sec=section)
447
- self.persistent.extend([pulse, time_vector, current_vector])
444
+ iclamp = neuron.h.IClamp(segx, sec=section)
445
+ self.persistent.extend([iclamp, time_vector, current_vector])
448
446
 
449
- pulse.delay = t_content[0]
450
- pulse.dur = t_content[-1] - t_content[0]
451
- current_vector.play(pulse._ref_amp, time_vector)
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,
@@ -1,4 +1,4 @@
1
- # Copyright 2012-2024 Blue Brain Project / EPFL
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,
@@ -1,4 +1,4 @@
1
- # Copyright 2012-2024 Blue Brain Project / EPFL
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.
@@ -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"
@@ -1,4 +1,4 @@
1
- # Copyright 2012-2024 Blue Brain Project / EPFL
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.