bsb-neuron 7.6.0__tar.gz → 8.0.1__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.
- {bsb_neuron-7.6.0 → bsb_neuron-8.0.1}/PKG-INFO +5 -7
- {bsb_neuron-7.6.0 → bsb_neuron-8.0.1}/bsb_neuron/adapter.py +96 -32
- {bsb_neuron-7.6.0 → bsb_neuron-8.0.1}/bsb_neuron/connection.py +81 -19
- {bsb_neuron-7.6.0 → bsb_neuron-8.0.1}/bsb_neuron/devices/current_clamp.py +13 -10
- bsb_neuron-8.0.1/bsb_neuron/devices/synapse_recorder.py +59 -0
- {bsb_neuron-7.6.0 → bsb_neuron-8.0.1}/bsb_neuron/devices/voltage_clamp.py +25 -19
- {bsb_neuron-7.6.0 → bsb_neuron-8.0.1}/bsb_neuron/devices/voltage_recorder.py +12 -10
- {bsb_neuron-7.6.0 → bsb_neuron-8.0.1}/pyproject.toml +22 -9
- bsb_neuron-7.6.0/bsb_neuron/devices/synapse_recorder.py +0 -37
- {bsb_neuron-7.6.0 → bsb_neuron-8.0.1}/LICENSE +0 -0
- {bsb_neuron-7.6.0 → bsb_neuron-8.0.1}/README.md +0 -0
- {bsb_neuron-7.6.0 → bsb_neuron-8.0.1}/bsb_neuron/__init__.py +0 -0
- {bsb_neuron-7.6.0 → bsb_neuron-8.0.1}/bsb_neuron/_util.py +0 -0
- {bsb_neuron-7.6.0 → bsb_neuron-8.0.1}/bsb_neuron/cell.py +0 -0
- {bsb_neuron-7.6.0 → bsb_neuron-8.0.1}/bsb_neuron/device.py +0 -0
- {bsb_neuron-7.6.0 → bsb_neuron-8.0.1}/bsb_neuron/devices/__init__.py +0 -0
- {bsb_neuron-7.6.0 → bsb_neuron-8.0.1}/bsb_neuron/devices/spike_generator.py +0 -0
- {bsb_neuron-7.6.0 → bsb_neuron-8.0.1}/bsb_neuron/simulation.py +2 -2
|
@@ -1,17 +1,15 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: bsb-neuron
|
|
3
|
-
Version:
|
|
3
|
+
Version: 8.0.1
|
|
4
4
|
Summary: NEURON simulator adapter for the BSB framework.
|
|
5
5
|
Author-email: Robin De Schepper <robin@alexandria.sc>, Dimitri Rodarie <dimitri.rodarie@unipv.it>
|
|
6
|
-
Requires-Python: >=3.
|
|
6
|
+
Requires-Python: >=3.11,<4
|
|
7
7
|
Description-Content-Type: text/markdown
|
|
8
8
|
Classifier: License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
|
|
9
9
|
License-File: LICENSE
|
|
10
|
-
Requires-Dist: bsb-core~=
|
|
11
|
-
Requires-Dist: nrn-patch~=
|
|
12
|
-
Requires-Dist: arborize[neuron]~=
|
|
13
|
-
Requires-Dist: bsb-core[parallel] ; extra == "parallel"
|
|
14
|
-
Provides-Extra: parallel
|
|
10
|
+
Requires-Dist: bsb-core~=8.0
|
|
11
|
+
Requires-Dist: nrn-patch~=8.0 ; sys_platform != 'win32'
|
|
12
|
+
Requires-Dist: arborize[neuron]~=8.0 ; sys_platform != 'win32'
|
|
15
13
|
|
|
16
14
|
[](https://github.com/dbbs-lab/bsb/actions/workflows/main.yml)
|
|
17
15
|
[](https://bsb-neuron.readthedocs.io/en/latest/?badge=latest)
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import contextlib
|
|
2
1
|
import itertools
|
|
3
2
|
|
|
4
3
|
import numpy as np
|
|
@@ -6,10 +5,12 @@ from bsb import (
|
|
|
6
5
|
AdapterError,
|
|
7
6
|
Chunk,
|
|
8
7
|
DatasetNotFoundError,
|
|
8
|
+
ResultsError,
|
|
9
9
|
SimulationData,
|
|
10
10
|
SimulationError,
|
|
11
11
|
SimulationResult,
|
|
12
12
|
SimulatorAdapter,
|
|
13
|
+
multiplex_channels,
|
|
13
14
|
report,
|
|
14
15
|
)
|
|
15
16
|
from neo import AnalogSignal
|
|
@@ -26,35 +27,94 @@ class NeuronSimulationData(SimulationData):
|
|
|
26
27
|
|
|
27
28
|
|
|
28
29
|
class NeuronResult(SimulationResult):
|
|
29
|
-
def
|
|
30
|
+
def __init__(self, *args, **kwargs):
|
|
31
|
+
super().__init__(*args, **kwargs)
|
|
32
|
+
# A device's channels, recorded across however many calls to `record` it
|
|
33
|
+
# makes, one flush of one multiplexed signal apiece: keyed on the device, not
|
|
34
|
+
# drawn per call, so every point/synapse of a device lands in the one signal.
|
|
35
|
+
self._channels = {}
|
|
36
|
+
|
|
37
|
+
def record(self, obj, *, device, target: dict, name: str, units: str):
|
|
38
|
+
"""
|
|
39
|
+
Record a NEURON variable every time step, as a recording of a device.
|
|
40
|
+
|
|
41
|
+
Every call for the same device joins one multiplexed signal, flushed as a
|
|
42
|
+
single ``AnalogSignal`` with one channel per call, rather than one signal per
|
|
43
|
+
call: cheap to write and to merge across ranks, at the scale of a device
|
|
44
|
+
rather than the scale of a cell.
|
|
45
|
+
|
|
46
|
+
:param obj: Reference to the NEURON variable to record, such as ``seg._ref_v``.
|
|
47
|
+
:param device: The device the recording belongs to.
|
|
48
|
+
:type device: bsb_neuron.device.NeuronDevice
|
|
49
|
+
:param target: What is recorded, as the annotations of
|
|
50
|
+
:func:`~bsb.simulation.results.cell_annotations`,
|
|
51
|
+
:func:`~bsb.simulation.results.point_annotations` or
|
|
52
|
+
:func:`~bsb.simulation.results.synapse_annotations` give them.
|
|
53
|
+
:param name: What the recorded variable measures, such as ``v`` or ``i``. Has
|
|
54
|
+
to agree with every other call for the same device.
|
|
55
|
+
:param units: Units of the recorded variable. Has to agree with every other
|
|
56
|
+
call for the same device.
|
|
57
|
+
"""
|
|
30
58
|
from patch import p
|
|
31
|
-
from quantities import ms
|
|
32
59
|
|
|
33
|
-
|
|
60
|
+
if "bsb_recording_kind" not in target or "bsb_direction" not in target:
|
|
61
|
+
raise AdapterError(
|
|
62
|
+
f"Device '{device.name}' has to say what it records: pass the "
|
|
63
|
+
"annotations of `cell_annotations`, `point_annotations` or "
|
|
64
|
+
"`synapse_annotations` as `target`."
|
|
65
|
+
)
|
|
66
|
+
entry = self._channels.get(device)
|
|
67
|
+
if entry is None:
|
|
68
|
+
entry = self._channels[device] = {
|
|
69
|
+
"name": name,
|
|
70
|
+
"units": units,
|
|
71
|
+
"channels": [],
|
|
72
|
+
}
|
|
73
|
+
self.create_recorder(
|
|
74
|
+
lambda segment: self._flush_device(segment, device), device=device
|
|
75
|
+
)
|
|
76
|
+
elif entry["name"] != name or entry["units"] != units:
|
|
77
|
+
raise AdapterError(
|
|
78
|
+
f"Device '{device.name}' recorded '{entry['name']}' in "
|
|
79
|
+
f"'{entry['units']}' before, and now '{name}' in '{units}': every "
|
|
80
|
+
"channel of a device has to measure the same thing, in the same units."
|
|
81
|
+
)
|
|
82
|
+
entry["channels"].append((p.record(obj), target))
|
|
83
|
+
|
|
84
|
+
def _flush_device(self, segment, device):
|
|
85
|
+
"""Combine every channel a device recorded since the last flush into one
|
|
86
|
+
multiplexed ``AnalogSignal``, and free the NEURON vectors that held them."""
|
|
87
|
+
from patch import p
|
|
88
|
+
from quantities import ms
|
|
34
89
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
90
|
+
entry = self._channels[device]
|
|
91
|
+
channels = entry["channels"]
|
|
92
|
+
lengths = {v.size() for v, _ in channels}
|
|
93
|
+
if len(lengths) > 1:
|
|
94
|
+
raise ResultsError(
|
|
95
|
+
f"Device '{device.name}' recorded channels of different lengths "
|
|
96
|
+
f"{sorted(lengths)} this checkpoint; they cannot be combined into one "
|
|
97
|
+
"signal."
|
|
40
98
|
)
|
|
41
|
-
|
|
99
|
+
data = np.array([list(v) for v, _ in channels]).T
|
|
100
|
+
baseline, array_annotations = multiplex_channels(
|
|
101
|
+
[target for _, target in channels]
|
|
102
|
+
)
|
|
103
|
+
segment.analogsignals.append(
|
|
104
|
+
AnalogSignal(
|
|
105
|
+
data,
|
|
106
|
+
sampling_period=p.dt * ms,
|
|
107
|
+
name=entry["name"],
|
|
108
|
+
units=entry["units"],
|
|
109
|
+
array_annotations=array_annotations,
|
|
110
|
+
**baseline,
|
|
111
|
+
)
|
|
112
|
+
)
|
|
113
|
+
# Free the memory
|
|
114
|
+
for v, _ in channels:
|
|
42
115
|
if v.size():
|
|
43
116
|
v.remove(0, v.size() - 1)
|
|
44
117
|
|
|
45
|
-
self.create_recorder(flush)
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
@contextlib.contextmanager
|
|
49
|
-
def fill_parameter_data(parameters, data):
|
|
50
|
-
for param in parameters:
|
|
51
|
-
if hasattr(param, "load_data"):
|
|
52
|
-
param.load_data(*data)
|
|
53
|
-
yield
|
|
54
|
-
for param in parameters:
|
|
55
|
-
if hasattr(param, "load_data"):
|
|
56
|
-
param.drop_data()
|
|
57
|
-
|
|
58
118
|
|
|
59
119
|
class NeuronAdapter(SimulatorAdapter):
|
|
60
120
|
initial = -65
|
|
@@ -70,7 +130,7 @@ class NeuronAdapter(SimulatorAdapter):
|
|
|
70
130
|
|
|
71
131
|
return engine
|
|
72
132
|
|
|
73
|
-
def prepare(self, simulation):
|
|
133
|
+
def prepare(self, simulation, filename=None):
|
|
74
134
|
"""
|
|
75
135
|
Prepare the simulation environment and data structures for running a NEURON
|
|
76
136
|
simulation.
|
|
@@ -90,7 +150,13 @@ class NeuronAdapter(SimulatorAdapter):
|
|
|
90
150
|
"""
|
|
91
151
|
|
|
92
152
|
self.simdata[simulation] = NeuronSimulationData(
|
|
93
|
-
simulation,
|
|
153
|
+
simulation,
|
|
154
|
+
result=NeuronResult(
|
|
155
|
+
simulation,
|
|
156
|
+
filename=filename,
|
|
157
|
+
comm=self.comm,
|
|
158
|
+
simulation_id=self.new_run_id(),
|
|
159
|
+
),
|
|
94
160
|
)
|
|
95
161
|
try:
|
|
96
162
|
report("Preparing simulation", level=2)
|
|
@@ -170,10 +236,9 @@ class NeuronAdapter(SimulatorAdapter):
|
|
|
170
236
|
self._allocate_transmitters(simulation)
|
|
171
237
|
for conn_model in simulation.connection_models.values():
|
|
172
238
|
cs = simulation.scaffold.get_connectivity_set(conn_model.name)
|
|
173
|
-
|
|
174
|
-
simdata
|
|
175
|
-
|
|
176
|
-
)
|
|
239
|
+
simdata.connections[conn_model] = conn_model.create_connections(
|
|
240
|
+
simulation, simdata, cs
|
|
241
|
+
)
|
|
177
242
|
|
|
178
243
|
def _allocate_transmitters(self, simulation):
|
|
179
244
|
simdata = self.simdata[simulation]
|
|
@@ -282,9 +347,8 @@ class NeuronAdapter(SimulatorAdapter):
|
|
|
282
347
|
except DatasetNotFoundError:
|
|
283
348
|
data.append(itertools.repeat(None))
|
|
284
349
|
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
simdata.populations[cell_model] = NeuronPopulation(cell_model, instances)
|
|
350
|
+
instances = cell_model.create_instances(len(ps), *data)
|
|
351
|
+
simdata.populations[cell_model] = NeuronPopulation(cell_model, instances)
|
|
288
352
|
|
|
289
353
|
|
|
290
354
|
class NeuronPopulation(list):
|
|
@@ -1,5 +1,8 @@
|
|
|
1
|
+
import dataclasses
|
|
2
|
+
import typing
|
|
3
|
+
|
|
1
4
|
import numpy as np
|
|
2
|
-
from bsb import AdapterError, ConnectionModel,
|
|
5
|
+
from bsb import AdapterError, ConnectionModel, config, types
|
|
3
6
|
|
|
4
7
|
from bsb_neuron._util import ignore_arborize_proxy_warnings
|
|
5
8
|
|
|
@@ -42,16 +45,46 @@ class SynapseSpec:
|
|
|
42
45
|
"""Name of the synapse model."""
|
|
43
46
|
weight = config.attr(type=float, default=0.004)
|
|
44
47
|
"""Weight of the connection between the presynaptic and the postsynaptic cells."""
|
|
45
|
-
delay = config.attr(type=float, default=
|
|
46
|
-
"""
|
|
47
|
-
|
|
48
|
-
|
|
48
|
+
delay = config.attr(type=float, default=1.0)
|
|
49
|
+
"""
|
|
50
|
+
Delay of the transmission between the presynaptic and the postsynaptic cells, in
|
|
51
|
+
milliseconds. Defaults to NEURON's own ``NetCon`` delay of 1 ms.
|
|
52
|
+
|
|
53
|
+
The delays of a network set NEURON's ``mindelay``, which
|
|
54
|
+
``ParallelContext.set_maxstep`` requires to be strictly positive and at least one
|
|
55
|
+
:attr:`~bsb_neuron.simulation.NeuronSimulation.resolution` step. A delay of 0
|
|
56
|
+
therefore aborts the simulation with ``usable mindelay is 0``.
|
|
57
|
+
"""
|
|
49
58
|
|
|
50
59
|
def __init__(self, synapse_name=None, /, **kwargs):
|
|
51
60
|
if synapse_name is not None:
|
|
52
61
|
self._synapse = synapse_name
|
|
53
62
|
|
|
54
63
|
|
|
64
|
+
@dataclasses.dataclass(frozen=True, slots=True)
|
|
65
|
+
class Receiver:
|
|
66
|
+
"""
|
|
67
|
+
A synapse a connection makes on a cell, and the connection it belongs to.
|
|
68
|
+
|
|
69
|
+
Every synapse a connection model creates keeps its receiver as ``receiver``.
|
|
70
|
+
"""
|
|
71
|
+
|
|
72
|
+
#: The transmitter gid the synapse receives from.
|
|
73
|
+
gid: int
|
|
74
|
+
#: The postsynaptic cell.
|
|
75
|
+
cell: typing.Any
|
|
76
|
+
#: Branch and point of the synapse on the postsynaptic cell.
|
|
77
|
+
location: tuple[int, int]
|
|
78
|
+
#: The synapse specification.
|
|
79
|
+
spec: SynapseSpec
|
|
80
|
+
#: The cell model of the presynaptic cell.
|
|
81
|
+
pre_model: typing.Any
|
|
82
|
+
#: Cell id, branch and point where the connection starts on the presynaptic cell.
|
|
83
|
+
pre: tuple[int, int, int]
|
|
84
|
+
#: Name of the connectivity set the connection is in.
|
|
85
|
+
connectivity_set: str
|
|
86
|
+
|
|
87
|
+
|
|
55
88
|
@config.node
|
|
56
89
|
class TransceiverModel(NeuronConnection, classmap_entry="transceiver"):
|
|
57
90
|
synapses = config.list(
|
|
@@ -59,8 +92,6 @@ class TransceiverModel(NeuronConnection, classmap_entry="transceiver"):
|
|
|
59
92
|
required=True,
|
|
60
93
|
)
|
|
61
94
|
"""List of synapse models to use for a connection."""
|
|
62
|
-
parameters = config.list(type=Parameter)
|
|
63
|
-
"""List of parameters to assign to the connection."""
|
|
64
95
|
source = config.attr(type=str)
|
|
65
96
|
"""Source variable to assign to the connection."""
|
|
66
97
|
|
|
@@ -101,24 +132,55 @@ class TransceiverModel(NeuronConnection, classmap_entry="transceiver"):
|
|
|
101
132
|
:type simdata: bsb_neuron.simulation.NeuronSimulationData
|
|
102
133
|
:type cs: bsb.storage.interfaces.ConnectivitySet
|
|
103
134
|
"""
|
|
104
|
-
for
|
|
105
|
-
|
|
135
|
+
for receiver in self.iter_receivers(simdata, cs):
|
|
136
|
+
receiver.cell.insert_receiver(
|
|
137
|
+
receiver.gid,
|
|
138
|
+
receiver.spec.synapse,
|
|
139
|
+
receiver.location,
|
|
140
|
+
source=self.source,
|
|
141
|
+
weight=receiver.spec.weight,
|
|
142
|
+
delay=receiver.spec.delay,
|
|
143
|
+
)
|
|
144
|
+
# The receiver's synapse is the one it just appended to its section.
|
|
145
|
+
location = receiver.cell.get_location(receiver.location)
|
|
146
|
+
location.section.synapses[-1].receiver = receiver
|
|
147
|
+
|
|
148
|
+
def iter_receivers(self, simdata, cs):
|
|
149
|
+
"""
|
|
150
|
+
Iterate the synapses the connectivity set makes on the cells of this rank, in
|
|
151
|
+
the order :meth:`create_receivers` inserts them.
|
|
152
|
+
|
|
153
|
+
:type simdata: bsb_neuron.simulation.NeuronSimulationData
|
|
154
|
+
:type cs: bsb.storage.interfaces.ConnectivitySet
|
|
155
|
+
:returns: The receivers, one per synapse.
|
|
156
|
+
:rtype: typing.Iterator[Receiver]
|
|
157
|
+
"""
|
|
158
|
+
for post_model, post_pop in simdata.populations.items(): # noqa: B007
|
|
159
|
+
if post_model.cell_type == cs.post_type:
|
|
106
160
|
break
|
|
107
161
|
else:
|
|
108
162
|
raise AdapterError(f"No pop found for {cs.pre_type.name}")
|
|
109
|
-
|
|
163
|
+
pre_model = next(
|
|
164
|
+
(cm for cm in simdata.populations if cm.cell_type == cs.pre_type), None
|
|
165
|
+
)
|
|
166
|
+
query = cs.load_connections().incoming().to(simdata.chunks)
|
|
167
|
+
pre, post = query.all()
|
|
168
|
+
# The same connections with the presynaptic cells as placement set ids, which
|
|
169
|
+
# is how a synapse names the cell it receives from.
|
|
170
|
+
pre_globals = query.as_globals().all()[0]
|
|
110
171
|
transmitters = simdata.transmap[self]["receivers"]
|
|
111
|
-
for pre_loc, post_loc in zip(pre
|
|
112
|
-
gid = transmitters[tuple(pre_loc)]
|
|
172
|
+
for pre_loc, pre_global, post_loc in zip(pre, pre_globals, post, strict=True):
|
|
173
|
+
gid = transmitters[tuple(pre_loc[:2])]
|
|
113
174
|
cell = post_pop[post_loc[0]]
|
|
114
175
|
for spec in self.synapses:
|
|
115
|
-
|
|
116
|
-
gid,
|
|
117
|
-
|
|
118
|
-
post_loc[1:],
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
176
|
+
yield Receiver(
|
|
177
|
+
gid=gid,
|
|
178
|
+
cell=cell,
|
|
179
|
+
location=tuple(int(i) for i in post_loc[1:]),
|
|
180
|
+
spec=spec,
|
|
181
|
+
pre_model=pre_model,
|
|
182
|
+
pre=tuple(int(i) for i in pre_global),
|
|
183
|
+
connectivity_set=cs.tag,
|
|
122
184
|
)
|
|
123
185
|
|
|
124
186
|
def __lt__(self, other):
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from bsb import LocationTargetting, config, warn
|
|
1
|
+
from bsb import LocationTargetting, config, point_annotations, warn
|
|
2
2
|
|
|
3
3
|
from .._util import ignore_arborize_proxy_warnings
|
|
4
4
|
from ..device import NeuronDevice
|
|
@@ -24,19 +24,22 @@ class CurrentClamp(NeuronDevice, classmap_entry="current_clamp"):
|
|
|
24
24
|
for location in self.locations.get_locations(target):
|
|
25
25
|
if clamped:
|
|
26
26
|
warn(f"Multiple current clamps placed on {target}")
|
|
27
|
-
self._add_clamp(
|
|
28
|
-
simdata,
|
|
29
|
-
location,
|
|
30
|
-
name=self.name,
|
|
31
|
-
cell_type=target.cell_model.name,
|
|
32
|
-
cell_id=target.id,
|
|
33
|
-
)
|
|
27
|
+
self._add_clamp(simdata, target, location)
|
|
34
28
|
clamped = True
|
|
35
29
|
|
|
36
30
|
@ignore_arborize_proxy_warnings()
|
|
37
|
-
def _add_clamp(self, simdata,
|
|
31
|
+
def _add_clamp(self, simdata, target, location):
|
|
38
32
|
sx = location.arc(0.5)
|
|
39
33
|
clamp = location.section.iclamp(
|
|
40
34
|
x=sx, delay=self.before, duration=self.duration, amplitude=self.amplitude
|
|
41
35
|
)
|
|
42
|
-
|
|
36
|
+
# The clamp records the current it injects.
|
|
37
|
+
simdata.result.record(
|
|
38
|
+
clamp._ref_i,
|
|
39
|
+
device=self,
|
|
40
|
+
target=point_annotations(
|
|
41
|
+
target.cell_model, target.id, *location._loc, sx, "stimulate"
|
|
42
|
+
),
|
|
43
|
+
name="i",
|
|
44
|
+
units="nA",
|
|
45
|
+
)
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
from bsb import LocationTargetting, config, synapse_annotations, warn
|
|
2
|
+
|
|
3
|
+
from .._util import ignore_arborize_proxy_warnings
|
|
4
|
+
from ..device import NeuronDevice
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@config.node
|
|
8
|
+
class SynapseRecorder(NeuronDevice, classmap_entry="synapse_recorder"):
|
|
9
|
+
locations = config.attr(type=LocationTargetting, required=True)
|
|
10
|
+
"""Location of the synapse recorder on the section"""
|
|
11
|
+
synapse_types = config.list()
|
|
12
|
+
"""List of synaptic types"""
|
|
13
|
+
|
|
14
|
+
@ignore_arborize_proxy_warnings()
|
|
15
|
+
def implement(self, adapter, simulation, simdata):
|
|
16
|
+
for _model, pop in self.targetting.get_targets(
|
|
17
|
+
adapter, simulation, simdata
|
|
18
|
+
).items():
|
|
19
|
+
for target in pop:
|
|
20
|
+
# Locations on the same section share its synapses, so each synapse
|
|
21
|
+
# is recorded once however many of the targeted locations hold it.
|
|
22
|
+
synapses = {
|
|
23
|
+
id(synapse): synapse
|
|
24
|
+
for location in self.locations.get_locations(target)
|
|
25
|
+
for synapse in location.section.synapses
|
|
26
|
+
if not self.synapse_types
|
|
27
|
+
or synapse.synapse_name in self.synapse_types
|
|
28
|
+
}
|
|
29
|
+
for synapse in synapses.values():
|
|
30
|
+
self._record_synaptic_current(simdata.result, target, synapse)
|
|
31
|
+
|
|
32
|
+
def _record_synaptic_current(self, result, target, synapse):
|
|
33
|
+
receiver = getattr(synapse, "receiver", None)
|
|
34
|
+
if receiver is None:
|
|
35
|
+
warn(
|
|
36
|
+
f"Device '{self.name}' cannot record a '{synapse.synapse_name}' synapse "
|
|
37
|
+
f"of cell {target.id}: it was not made by a connection, so where it is "
|
|
38
|
+
"and what it connects is unknown."
|
|
39
|
+
)
|
|
40
|
+
return
|
|
41
|
+
pre_id, pre_branch, pre_point = receiver.pre
|
|
42
|
+
location = target.get_location(receiver.location)
|
|
43
|
+
result.record(
|
|
44
|
+
synapse._pp._ref_i,
|
|
45
|
+
device=self,
|
|
46
|
+
name="i",
|
|
47
|
+
units="nA",
|
|
48
|
+
target=synapse_annotations(
|
|
49
|
+
(target.cell_model, target.id, *receiver.location, location.arc(0.5)),
|
|
50
|
+
synapse.synapse_name,
|
|
51
|
+
"record",
|
|
52
|
+
pre=(
|
|
53
|
+
None
|
|
54
|
+
if receiver.pre_model is None
|
|
55
|
+
else (receiver.pre_model, pre_id, pre_branch, pre_point)
|
|
56
|
+
),
|
|
57
|
+
connectivity_set=receiver.connectivity_set,
|
|
58
|
+
),
|
|
59
|
+
)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import warnings
|
|
2
2
|
|
|
3
|
-
from bsb import LocationTargetting, config, types
|
|
3
|
+
from bsb import LocationTargetting, config, point_annotations, types
|
|
4
4
|
|
|
5
5
|
from .._util import ignore_arborize_proxy_warnings
|
|
6
6
|
from ..device import NeuronDevice
|
|
@@ -25,25 +25,22 @@ class VoltageClamp(NeuronDevice, classmap_entry="vclamp"):
|
|
|
25
25
|
"""Voltage value in the `before` and `after` delays"""
|
|
26
26
|
|
|
27
27
|
def implement(self, adapter, simulation, simdata):
|
|
28
|
-
for
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
cell_id=target.id,
|
|
42
|
-
)
|
|
43
|
-
clamped = True
|
|
28
|
+
for _model, pop in self.targetting.get_targets(
|
|
29
|
+
adapter, simulation, simdata
|
|
30
|
+
).items():
|
|
31
|
+
for target in pop:
|
|
32
|
+
clamped = False
|
|
33
|
+
for location in self.locations.get_locations(target):
|
|
34
|
+
if clamped:
|
|
35
|
+
warnings.warn(
|
|
36
|
+
f"Multiple voltage clamps placed on {target}",
|
|
37
|
+
stacklevel=2,
|
|
38
|
+
)
|
|
39
|
+
self._add_clamp(simdata.result, target, location)
|
|
40
|
+
clamped = True
|
|
44
41
|
|
|
45
42
|
@ignore_arborize_proxy_warnings()
|
|
46
|
-
def _add_clamp(self, results,
|
|
43
|
+
def _add_clamp(self, results, target, location):
|
|
47
44
|
sx = location.arc(0.5)
|
|
48
45
|
clamp = location.section.vclamp(
|
|
49
46
|
voltage=self.voltage,
|
|
@@ -54,4 +51,13 @@ class VoltageClamp(NeuronDevice, classmap_entry="vclamp"):
|
|
|
54
51
|
if (v := getattr(self, k)) is not None
|
|
55
52
|
},
|
|
56
53
|
)
|
|
57
|
-
|
|
54
|
+
# The clamp records the current it injects to hold the voltage.
|
|
55
|
+
results.record(
|
|
56
|
+
clamp._ref_i,
|
|
57
|
+
device=self,
|
|
58
|
+
target=point_annotations(
|
|
59
|
+
target.cell_model, target.id, *location._loc, sx, "stimulate"
|
|
60
|
+
),
|
|
61
|
+
name="i",
|
|
62
|
+
units="nA",
|
|
63
|
+
)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from bsb import LocationTargetting, config
|
|
1
|
+
from bsb import LocationTargetting, config, point_annotations
|
|
2
2
|
|
|
3
3
|
from .._util import ignore_arborize_proxy_warnings
|
|
4
4
|
from ..device import NeuronDevice
|
|
@@ -17,16 +17,18 @@ class VoltageRecorder(NeuronDevice, classmap_entry="voltage_recorder"):
|
|
|
17
17
|
).items():
|
|
18
18
|
for target in pop:
|
|
19
19
|
for location in self.locations.get_locations(target):
|
|
20
|
-
self._add_voltage_recorder(
|
|
21
|
-
simdata.result,
|
|
22
|
-
location,
|
|
23
|
-
name=self.name,
|
|
24
|
-
cell_type=target.cell_model.name,
|
|
25
|
-
cell_id=target.id,
|
|
26
|
-
)
|
|
20
|
+
self._add_voltage_recorder(simdata.result, target, location)
|
|
27
21
|
|
|
28
22
|
@ignore_arborize_proxy_warnings()
|
|
29
|
-
def _add_voltage_recorder(self, results,
|
|
23
|
+
def _add_voltage_recorder(self, results, target, location):
|
|
30
24
|
section = location.section
|
|
31
25
|
x = location.arc(0)
|
|
32
|
-
results.record(
|
|
26
|
+
results.record(
|
|
27
|
+
section(x)._ref_v,
|
|
28
|
+
device=self,
|
|
29
|
+
target=point_annotations(
|
|
30
|
+
target.cell_model, target.id, *location._loc, x, "record"
|
|
31
|
+
),
|
|
32
|
+
name="v",
|
|
33
|
+
units="mV",
|
|
34
|
+
)
|
|
@@ -6,18 +6,19 @@ build-backend = "flit_core.buildapi"
|
|
|
6
6
|
|
|
7
7
|
[project]
|
|
8
8
|
name = "bsb-neuron"
|
|
9
|
-
version = "
|
|
9
|
+
version = "8.0.1"
|
|
10
10
|
readme = "README.md"
|
|
11
|
-
requires-python = ">=3.
|
|
11
|
+
requires-python = ">=3.11,<4"
|
|
12
12
|
dynamic = [ "description" ]
|
|
13
13
|
classifiers = [
|
|
14
14
|
"License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)"
|
|
15
15
|
]
|
|
16
16
|
dependencies = [
|
|
17
|
-
"bsb-core~=
|
|
18
|
-
"nrn-patch~=
|
|
19
|
-
"arborize[neuron]~=
|
|
17
|
+
"bsb-core~=8.0",
|
|
18
|
+
"nrn-patch~=8.0 ; sys_platform != 'win32'",
|
|
19
|
+
"arborize[neuron]~=8.0 ; sys_platform != 'win32'"
|
|
20
20
|
]
|
|
21
|
+
optional-dependencies = { }
|
|
21
22
|
|
|
22
23
|
[[project.authors]]
|
|
23
24
|
name = "Robin De Schepper"
|
|
@@ -30,15 +31,27 @@ dependencies = [
|
|
|
30
31
|
[project.license]
|
|
31
32
|
file = "LICENSE"
|
|
32
33
|
|
|
33
|
-
[project.optional-dependencies]
|
|
34
|
-
parallel = [ "bsb-core[parallel]" ]
|
|
35
|
-
|
|
36
34
|
[project.entry-points."bsb.simulation_backends"]
|
|
37
35
|
neuron = "bsb_neuron"
|
|
38
36
|
|
|
39
37
|
[tool.uv]
|
|
40
38
|
default-groups = [ "dev", "docs", "test" ]
|
|
41
|
-
|
|
39
|
+
|
|
40
|
+
[tool.uv.sources.bsb-hdf5]
|
|
41
|
+
path = "../bsb-hdf5"
|
|
42
|
+
editable = true
|
|
43
|
+
|
|
44
|
+
[tool.uv.sources.bsb-test]
|
|
45
|
+
path = "../bsb-test"
|
|
46
|
+
editable = true
|
|
47
|
+
|
|
48
|
+
[tool.uv.sources.bsb-otel]
|
|
49
|
+
path = "../bsb-otel"
|
|
50
|
+
editable = true
|
|
51
|
+
|
|
52
|
+
[tool.uv.sources.sphinxext-bsb]
|
|
53
|
+
path = "../../libs/sphinxext-bsb"
|
|
54
|
+
editable = true
|
|
42
55
|
|
|
43
56
|
[tool.flit.module]
|
|
44
57
|
name = "bsb_neuron"
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
from bsb import LocationTargetting, config
|
|
2
|
-
|
|
3
|
-
from .._util import ignore_arborize_proxy_warnings
|
|
4
|
-
from ..device import NeuronDevice
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
@config.node
|
|
8
|
-
class SynapseRecorder(NeuronDevice, classmap_entry="synapse_recorder"):
|
|
9
|
-
locations = config.attr(type=LocationTargetting, required=True)
|
|
10
|
-
"""Location of the synapse recorder on the section"""
|
|
11
|
-
synapse_types = config.list()
|
|
12
|
-
"""List of synaptic types"""
|
|
13
|
-
|
|
14
|
-
@ignore_arborize_proxy_warnings()
|
|
15
|
-
def implement(self, adapter, simulation, simdata):
|
|
16
|
-
for _model, pop in self.targetting.get_targets(
|
|
17
|
-
adapter, simulation, simdata
|
|
18
|
-
).items():
|
|
19
|
-
for target in pop:
|
|
20
|
-
for location in self.locations.get_locations(target):
|
|
21
|
-
for synapse in location.section.synapses:
|
|
22
|
-
if (
|
|
23
|
-
not self.synapse_types
|
|
24
|
-
or synapse.synapse_name in self.synapse_types
|
|
25
|
-
):
|
|
26
|
-
_record_synaptic_current(
|
|
27
|
-
simdata.result,
|
|
28
|
-
synapse,
|
|
29
|
-
name=self.name,
|
|
30
|
-
cell_type=target.cell_model.name,
|
|
31
|
-
cell_id=target.id,
|
|
32
|
-
synapse_type=synapse.synapse_name,
|
|
33
|
-
)
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
def _record_synaptic_current(result, synapse, **annotations):
|
|
37
|
-
result.record(synapse._pp._ref_i, **annotations, units="nA")
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -11,10 +11,10 @@ class NeuronSimulation(Simulation):
|
|
|
11
11
|
Interface between the scaffold model and the NEURON simulator.
|
|
12
12
|
"""
|
|
13
13
|
|
|
14
|
-
initial = config.attr(type=float, default=-65.0)
|
|
15
|
-
"""Initial membrane potential for all neurons."""
|
|
16
14
|
resolution = config.attr(type=types.float(min=0.0), default=0.1)
|
|
17
15
|
"""Simulation time step size in milliseconds."""
|
|
16
|
+
initial = config.attr(type=float, default=-65.0)
|
|
17
|
+
"""Initial membrane potential for all neurons."""
|
|
18
18
|
temperature = config.attr(type=float, required=True)
|
|
19
19
|
"""Temperature of the circuit during simulation."""
|
|
20
20
|
cell_models: config._attrs.cfgdict[NeuronCell] = config.dict(
|