-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathbluecellulab_evaluator.py
300 lines (253 loc) · 9.33 KB
/
bluecellulab_evaluator.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
"""Compute the threshold and holding current using bluecellulab, adapted from BluePyThresh."""
import logging
import os
from copy import copy
from pathlib import Path
import bluecellulab
import efel
from bluecellulab.simulation.neuron_globals import NeuronGlobals
from bluepyparallel.evaluator import evaluate
from emodel_generalisation.utils import isolate
logger = logging.getLogger(__name__)
AXON_LOC = "self.axonal[1](0.5)._ref_v"
def calculate_threshold_current(cell, config, holding_current):
"""Calculate threshold current"""
min_current_spike_count = run_spike_sim(
cell,
config,
holding_current,
config["min_threshold_current"],
)
logger.debug("min %s", min_current_spike_count)
if min_current_spike_count > 0:
logger.debug("Cell is firing spontaneously at min current, we divide by 2")
if config["min_threshold_current"] == 0:
return None
config["max_threshold_current"] = copy(config["min_threshold_current"])
config["min_threshold_current"] /= 2.0
return calculate_threshold_current(cell, config, holding_current)
max_current_spike_count = run_spike_sim(
cell,
config,
holding_current,
config["max_threshold_current"],
)
logger.debug("max %s", max_current_spike_count)
if max_current_spike_count < 1:
logger.debug("Cell is not firing at max current, we multiply by 2")
config["min_threshold_current"] = copy(config["max_threshold_current"])
config["max_threshold_current"] *= 1.2
return calculate_threshold_current(cell, config, holding_current)
return binsearch_threshold_current(
cell,
config,
holding_current,
config["min_threshold_current"],
config["max_threshold_current"],
)
def binsearch_threshold_current(cell, config, holding_current, min_current, max_current):
"""Binary search for threshold currents"""
mid_current = (min_current + max_current) / 2
if abs(max_current - min_current) < config.get("threshold_current_precision", 0.001):
spike_count = run_spike_sim(
cell,
config,
holding_current,
max_current,
)
return max_current
spike_count = run_spike_sim(
cell,
config,
holding_current,
mid_current,
)
if spike_count == 0:
return binsearch_threshold_current(cell, config, holding_current, mid_current, max_current)
return binsearch_threshold_current(cell, config, holding_current, min_current, mid_current)
def run_spike_sim(cell, config, holding_current, step_current):
"""Run simulation on a cell and compute number of spikes."""
cell.add_step(0, config["step_stop"], holding_current)
cell.add_step(config["step_start"], config["step_stop"], step_current)
if config["spike_at_ais"]:
cell.add_recordings(["neuron.h._ref_t", AXON_LOC], dt=cell.record_dt)
sim = bluecellulab.Simulation()
ng = NeuronGlobals.get_instance()
ng.v_init = config["v_init"]
ng.temperature = config["celsius"]
sim.run(
config["step_stop"],
cvode=config.get("deterministic", True),
dt=config.get("dt", 0.025),
)
time = cell.get_time()
if config["spike_at_ais"]:
voltage = cell.get_recording(AXON_LOC)
else:
voltage = cell.get_soma_voltage()
if len(voltage) < 2:
raise Exception("No voltage trace!")
efel.reset()
efel.setIntSetting("strict_stiminterval", True)
spike_count_array = efel.getFeatureValues(
[
{
"T": time,
"V": voltage,
"stim_start": [config["step_start"]],
"stim_end": [config["step_stop"]],
}
],
["Spikecount"],
)[0]["Spikecount"]
cell.persistent = [] # remove the step protocols for next run
if spike_count_array is None or len(spike_count_array) != 1:
raise Exception("Error during spike count calculation")
return spike_count_array[0]
def set_cell_deterministic(cell, deterministic):
"""Disable stochasticity in ion channels"""
deterministic = True
for section in cell.cell.all:
for compartment in section:
for mech in compartment:
mech_name = mech.name()
if "Stoch" in mech_name:
if not deterministic:
deterministic = False
setattr(
section,
f"deterministic_{mech_name}",
1 if deterministic else 0,
)
def calculate_rmp_and_rin(cell, config):
"""Calculate rmp and input resistance from rmp."""
cell.add_step(0, config["rin"]["step_stop"], 0)
cell.add_step(
config["rin"]["step_start"], config["rin"]["step_stop"], config["rin"]["step_amp"]
)
if config["rin"]["with_ttx"]:
cell.enable_ttx()
sim = bluecellulab.Simulation()
ng = NeuronGlobals.get_instance()
ng.v_init = config["v_init"]
ng.temperature = config["celsius"]
sim.run(
config["rin"]["step_stop"],
cvode=config.get("deterministic", True),
dt=config.get("dt", 0.025),
)
time = cell.get_time()
voltage = cell.get_soma_voltage()
efel.reset()
efel.setIntSetting("strict_stiminterval", True)
trace = {
"T": time,
"V": voltage,
"stim_start": [config["rin"]["step_start"]],
"stim_end": [config["rin"]["step_stop"]],
"stimulus_current": [config["rin"]["step_amp"]],
}
features = efel.getFeatureValues(
[trace], ["spike_count", "voltage_base", "ohmic_input_resistance_vb_ssse"]
)[0]
rmp = 0
if features["voltage_base"] is not None:
rmp = features["voltage_base"][0]
rin = -1.0
if features["ohmic_input_resistance_vb_ssse"] is not None:
rin = features["ohmic_input_resistance_vb_ssse"][0]
if features["spike_count"] > 0:
logger.warning("SPIKES! %s, %s", rmp, rin)
return 0.0, 0.0
return rmp, rin
def calculate_holding_current(cell, config):
"""Calculate holding current.
adapted from: bluecellulab.tools.holding_current_subprocess,
"""
vclamp = bluecellulab.neuron.h.SEClamp(0.5, sec=cell.soma)
vclamp.rs = 0.01
vclamp.dur1 = 2000
vclamp.amp1 = config["holding_voltage"]
simulation = bluecellulab.Simulation()
ng = NeuronGlobals.get_instance()
ng.v_init = config["v_init"]
simulation.run(1000, cvode=config.get("deterministic", True), dt=config.get("dt", 0.025))
return vclamp.i
def _current_evaluation(
combo,
protocol_config,
emodels_hoc_dir,
morphology_path="morphology_path",
template_format="v6",
only_rin=False,
):
"""Compute the threshold and holding currents."""
cell_kwargs = {
"template_path": str(Path(emodels_hoc_dir) / f"{combo['emodel']}.hoc"),
"morphology_path": combo[morphology_path],
"template_format": template_format,
"emodel_properties": bluecellulab.circuit.EmodelProperties(
holding_current=0,
threshold_current=0,
AIS_scaler=combo.get("@dynamics:AIS_scaler", None),
soma_scaler=combo.get("@dynamics:soma_scaler", None),
),
}
cell = bluecellulab.Cell(**cell_kwargs)
set_cell_deterministic(cell, protocol_config["deterministic"])
if not only_rin:
holding_current = calculate_holding_current(cell, protocol_config)
threshold_current = calculate_threshold_current(cell, protocol_config, holding_current)
rmp, rin = calculate_rmp_and_rin(cell, protocol_config)
cell.delete()
results = {"resting_potential": rmp, "input_resistance": rin}
if not only_rin:
results["holding_current"] = holding_current
results["threshold_current"] = threshold_current
return results
def _isolated_current_evaluation(*args, **kwargs):
"""Isolate current evaluation for full safety."""
timeout = kwargs.pop("timeout", None)
res = isolate(_current_evaluation, timeout=timeout)(*args, **kwargs)
if res is None:
res = {
"resting_potential": 0.0,
"input_resistance": -1.0,
}
if not kwargs.get("only_rin", False):
res["holding_current"] = 0.0
res["threshold_current"] = 0.0
return res
def evaluate_currents(
morphs_combos_df,
protocol_config,
emodels_hoc_dir,
morphology_path="path",
resume=False,
db_url="eval_db.sql",
parallel_factory=None,
template_format="v6",
timeout=1000,
only_rin=False,
):
"""Compute the threshold and holding currents using bluecellulab."""
new_columns = [["resting_potential", 0.0], ["input_resistance", 0.0]]
if not only_rin:
new_columns += [["holding_current", 0.0], ["threshold_current", 0.0]]
return evaluate(
morphs_combos_df,
_isolated_current_evaluation,
new_columns=new_columns,
resume=resume,
parallel_factory=parallel_factory,
db_url=db_url,
func_kwargs={
"protocol_config": protocol_config,
"emodels_hoc_dir": emodels_hoc_dir,
"morphology_path": morphology_path,
"template_format": template_format,
"timeout": timeout,
"only_rin": only_rin,
},
progress_bar=not os.environ.get("NO_PROGRESS", False),
)