dendrotweaks 0.3.1__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.
- dendrotweaks/__init__.py +10 -0
- dendrotweaks/analysis/__init__.py +11 -0
- dendrotweaks/analysis/ephys_analysis.py +482 -0
- dendrotweaks/analysis/morphometric_analysis.py +106 -0
- dendrotweaks/membrane/__init__.py +6 -0
- dendrotweaks/membrane/default_mod/AMPA.mod +65 -0
- dendrotweaks/membrane/default_mod/AMPA_NMDA.mod +100 -0
- dendrotweaks/membrane/default_mod/CaDyn.mod +54 -0
- dendrotweaks/membrane/default_mod/GABAa.mod +65 -0
- dendrotweaks/membrane/default_mod/Leak.mod +27 -0
- dendrotweaks/membrane/default_mod/NMDA.mod +72 -0
- dendrotweaks/membrane/default_mod/vecstim.mod +76 -0
- dendrotweaks/membrane/default_templates/NEURON_template.py +354 -0
- dendrotweaks/membrane/default_templates/default.py +73 -0
- dendrotweaks/membrane/default_templates/standard_channel.mod +87 -0
- dendrotweaks/membrane/default_templates/template_jaxley.py +108 -0
- dendrotweaks/membrane/default_templates/template_jaxley_new.py +108 -0
- dendrotweaks/membrane/distributions.py +324 -0
- dendrotweaks/membrane/groups.py +103 -0
- dendrotweaks/membrane/io/__init__.py +11 -0
- dendrotweaks/membrane/io/ast.py +201 -0
- dendrotweaks/membrane/io/code_generators.py +312 -0
- dendrotweaks/membrane/io/converter.py +108 -0
- dendrotweaks/membrane/io/factories.py +144 -0
- dendrotweaks/membrane/io/grammar.py +417 -0
- dendrotweaks/membrane/io/loader.py +90 -0
- dendrotweaks/membrane/io/parser.py +499 -0
- dendrotweaks/membrane/io/reader.py +212 -0
- dendrotweaks/membrane/mechanisms.py +574 -0
- dendrotweaks/model.py +1916 -0
- dendrotweaks/model_io.py +75 -0
- dendrotweaks/morphology/__init__.py +5 -0
- dendrotweaks/morphology/domains.py +100 -0
- dendrotweaks/morphology/io/__init__.py +5 -0
- dendrotweaks/morphology/io/factories.py +212 -0
- dendrotweaks/morphology/io/reader.py +66 -0
- dendrotweaks/morphology/io/validation.py +212 -0
- dendrotweaks/morphology/point_trees.py +681 -0
- dendrotweaks/morphology/reduce/__init__.py +16 -0
- dendrotweaks/morphology/reduce/reduce.py +155 -0
- dendrotweaks/morphology/reduce/reduced_cylinder.py +129 -0
- dendrotweaks/morphology/sec_trees.py +1112 -0
- dendrotweaks/morphology/seg_trees.py +157 -0
- dendrotweaks/morphology/trees.py +567 -0
- dendrotweaks/path_manager.py +261 -0
- dendrotweaks/simulators.py +235 -0
- dendrotweaks/stimuli/__init__.py +3 -0
- dendrotweaks/stimuli/iclamps.py +73 -0
- dendrotweaks/stimuli/populations.py +265 -0
- dendrotweaks/stimuli/synapses.py +203 -0
- dendrotweaks/utils.py +239 -0
- dendrotweaks-0.3.1.dist-info/METADATA +70 -0
- dendrotweaks-0.3.1.dist-info/RECORD +56 -0
- dendrotweaks-0.3.1.dist-info/WHEEL +5 -0
- dendrotweaks-0.3.1.dist-info/licenses/LICENSE +674 -0
- dendrotweaks-0.3.1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,574 @@
|
|
1
|
+
from typing import Dict
|
2
|
+
import numpy as np
|
3
|
+
import matplotlib.pyplot as plt
|
4
|
+
|
5
|
+
|
6
|
+
|
7
|
+
class Mechanism():
|
8
|
+
"""
|
9
|
+
A class representing a mechanism in a neuron model.
|
10
|
+
|
11
|
+
A mechanism is a set of differential equations that
|
12
|
+
describe the kinetics of a channel
|
13
|
+
or a pump in the neuron membrane
|
14
|
+
|
15
|
+
Parameters
|
16
|
+
----------
|
17
|
+
name : str
|
18
|
+
The name of the mechanism.
|
19
|
+
|
20
|
+
Attributes
|
21
|
+
----------
|
22
|
+
name : str
|
23
|
+
The name of the mechanism.
|
24
|
+
params : dict
|
25
|
+
A dictionary of the parameters of the mechanism.
|
26
|
+
range_params : dict
|
27
|
+
A dictionary of the range parameters of the mechanism added
|
28
|
+
under the RANGE statement in the MOD file.
|
29
|
+
"""
|
30
|
+
|
31
|
+
def __init__(self, name):
|
32
|
+
self.name = name
|
33
|
+
self.params = {}
|
34
|
+
self.range_params = {}
|
35
|
+
|
36
|
+
@property
|
37
|
+
def params_with_suffix(self):
|
38
|
+
"""
|
39
|
+
The parameters of the mechanism with the suffix
|
40
|
+
— the name of the mechanism.
|
41
|
+
|
42
|
+
Returns
|
43
|
+
-------
|
44
|
+
dict
|
45
|
+
A dictionary of the parameters of the mechanism with the suffix and their values.
|
46
|
+
"""
|
47
|
+
return {f"{param}_{self.name}":value for param, value in self.params.items()}
|
48
|
+
|
49
|
+
@property
|
50
|
+
def range_params_with_suffix(self):
|
51
|
+
"""
|
52
|
+
The range parameters of the mechanism with the suffix
|
53
|
+
— the name of the mechanism. The range parameters are the parameters
|
54
|
+
defined in the RANGE block of the NMODL file.
|
55
|
+
|
56
|
+
Returns
|
57
|
+
-------
|
58
|
+
dict
|
59
|
+
A dictionary of the range parameters of the mechanism with the suffix and their values.
|
60
|
+
"""
|
61
|
+
return {f"{param}_{self.name}":value for param, value in self.range_params.items()}
|
62
|
+
|
63
|
+
def to_dict(self):
|
64
|
+
"""
|
65
|
+
Return the mechanism as a dictionary.
|
66
|
+
"""
|
67
|
+
return {
|
68
|
+
'name': self.name,
|
69
|
+
'params': self.params
|
70
|
+
}
|
71
|
+
|
72
|
+
def __repr__(self):
|
73
|
+
return f"<Mechnaism({self.name})>"
|
74
|
+
|
75
|
+
|
76
|
+
|
77
|
+
|
78
|
+
class IonChannel(Mechanism):
|
79
|
+
"""
|
80
|
+
A class representing an ion channel in a neuron model.
|
81
|
+
|
82
|
+
Parameters
|
83
|
+
----------
|
84
|
+
name : str
|
85
|
+
The name of the channel.
|
86
|
+
|
87
|
+
Attributes
|
88
|
+
----------
|
89
|
+
independent_var_name : str
|
90
|
+
The name of the independent variable for the channel kinetics e.g. 'v', 'cai'.
|
91
|
+
params : dict
|
92
|
+
A dictionary of the parameters of the channel kinetics and distribution.
|
93
|
+
range_params : dict
|
94
|
+
A dictionary of the range parameters of the channel kinetics added
|
95
|
+
under the RANGE statement in the MOD file.
|
96
|
+
temperature : float
|
97
|
+
The temperature in degrees Celsius.
|
98
|
+
tadj : float
|
99
|
+
The temperature adjustment factor for the channel kinetics.
|
100
|
+
"""
|
101
|
+
|
102
|
+
def __init__(self, name):
|
103
|
+
super().__init__(name)
|
104
|
+
self.tadj = 1
|
105
|
+
|
106
|
+
def set_tadj(self, temperature):
|
107
|
+
"""
|
108
|
+
Set the temperature adjustment factor for the channel kinetics.
|
109
|
+
|
110
|
+
Parameters
|
111
|
+
----------
|
112
|
+
temperature : float
|
113
|
+
The temperature in degrees Celsius.
|
114
|
+
|
115
|
+
Notes
|
116
|
+
-----
|
117
|
+
The temperature adjustment factor is calculated as:
|
118
|
+
tadj = q10 ** ((temperature - reference_temp) / 10)
|
119
|
+
where q10 is the temperature coefficient and reference_temp is the
|
120
|
+
temperature at which the channel kinetics were measured.
|
121
|
+
"""
|
122
|
+
q10 = self.params.get("q10", 2.3)
|
123
|
+
reference_temp = self.params.get("temp", temperature)
|
124
|
+
self.tadj = q10 ** ((temperature - reference_temp) / 10)
|
125
|
+
|
126
|
+
def get_data(self, x=None, temperature: float = 37, verbose=True) -> Dict[str, Dict[str, float]]:
|
127
|
+
"""
|
128
|
+
Get the data for the channel kinetics as a dictionary. The data
|
129
|
+
includes the steady state values and time constants of the channel,
|
130
|
+
as well as the independent variable values.
|
131
|
+
|
132
|
+
Parameters
|
133
|
+
----------
|
134
|
+
x : np.array, optional
|
135
|
+
The independent variable for the channel kinetics. If None, the
|
136
|
+
default values will be used. The default is None.
|
137
|
+
temperature : float, optional
|
138
|
+
The temperature in degrees Celsius. The default is 37.
|
139
|
+
|
140
|
+
Returns
|
141
|
+
-------
|
142
|
+
Dict[str, Dict[str, np.ndarray]]
|
143
|
+
A dictionary of states with their steady state values and time constants:
|
144
|
+
{
|
145
|
+
'state1': {'inf': np.array, 'tau': np.array},
|
146
|
+
'state2': {'inf': np.array, 'tau': np.array},
|
147
|
+
...
|
148
|
+
'x': np.array
|
149
|
+
}
|
150
|
+
"""
|
151
|
+
|
152
|
+
if x is None:
|
153
|
+
if self.independent_var_name == 'v':
|
154
|
+
x = np.linspace(-100, 100, 100)
|
155
|
+
elif self.independent_var_name == 'cai':
|
156
|
+
x = np.logspace(-6, 2, 100)
|
157
|
+
|
158
|
+
self.set_tadj(temperature)
|
159
|
+
self.temperature = temperature
|
160
|
+
states = self.compute_kinetic_variables(x)
|
161
|
+
# TODO: Fix the issue with returning state as a constant
|
162
|
+
# for some channels (e.g. tau in Poirazi Na_soma)
|
163
|
+
data = {
|
164
|
+
state_name: {
|
165
|
+
'inf': np.full_like(x, states[i]) if np.isscalar(states[i]) else states[i],
|
166
|
+
'tau': np.full_like(x, states[i + 1]) if np.isscalar(states[i + 1]) else states[i + 1]
|
167
|
+
}
|
168
|
+
for i, state_name in zip(range(0, len(states), 2),
|
169
|
+
self.states)
|
170
|
+
}
|
171
|
+
data.update({'x': x})
|
172
|
+
if verbose: print(f'Got data for {self.independent_var_name} '
|
173
|
+
f'in range {x[0]} to {x[-1]} at {temperature}°C')
|
174
|
+
return data
|
175
|
+
|
176
|
+
def plot_kinetics(self, ax=None, linestyle='solid', **kwargs) -> None:
|
177
|
+
"""
|
178
|
+
Plot the kinetics of the channel.
|
179
|
+
|
180
|
+
Parameters
|
181
|
+
----------
|
182
|
+
ax : matplotlib.axes.Axes, optional
|
183
|
+
The axes to plot the kinetics on. If None, a new figure
|
184
|
+
will be created. The default is None.
|
185
|
+
linestyle : str, optional
|
186
|
+
The line style for the plots. The default is 'solid'.
|
187
|
+
**kwargs : dict
|
188
|
+
Additional keyword arguments to pass to the get_data method.
|
189
|
+
"""
|
190
|
+
|
191
|
+
if ax is None:
|
192
|
+
fig, ax = plt.subplots(1, 2, figsize=(10, 5))
|
193
|
+
|
194
|
+
data = self.get_data(**kwargs)
|
195
|
+
x = data.pop('x')
|
196
|
+
|
197
|
+
for state_name, state in data.items():
|
198
|
+
ax[0].plot(x, state['inf'], label=f'{state_name}Inf', linestyle=linestyle)
|
199
|
+
ax[1].plot(x, state['tau'], label=f'{state_name}Tau', linestyle=linestyle)
|
200
|
+
|
201
|
+
ax[0].set_title('Steady state')
|
202
|
+
ax[1].set_title('Time constant')
|
203
|
+
ax[0].set_xlabel('Voltage (mV)' if self.independent_var_name == 'v' else 'Ca2+ concentration (mM)')
|
204
|
+
ax[1].set_xlabel('Voltage (mV)' if self.independent_var_name == 'v' else 'Ca2+ concentration (mM)')
|
205
|
+
ax[0].set_ylabel('Open probability (1)')
|
206
|
+
ax[1].set_ylabel('Time constant (ms)')
|
207
|
+
ax[0].legend()
|
208
|
+
ax[1].legend()
|
209
|
+
|
210
|
+
|
211
|
+
|
212
|
+
|
213
|
+
class StandardIonChannel(IonChannel):
|
214
|
+
"""
|
215
|
+
A class representing a voltage-gated ion channel with a standard
|
216
|
+
set of kinetic parameters and equations grounded in the transition-state
|
217
|
+
theory. The model is based on the Hodgkin-Huxley formalism.
|
218
|
+
|
219
|
+
Parameters
|
220
|
+
----------
|
221
|
+
name : str
|
222
|
+
The name of the channel.
|
223
|
+
state_powers : dict
|
224
|
+
A dictionary of the state variables and their powers in the
|
225
|
+
differential equations of the channel kinetics.
|
226
|
+
ion : str, optional
|
227
|
+
The ion that the channel is permeable to. The default is None.
|
228
|
+
|
229
|
+
|
230
|
+
Attributes
|
231
|
+
----------
|
232
|
+
ion : str
|
233
|
+
The ion that the channel is permeable to e.g. 'na', 'k'.
|
234
|
+
independent_var_name : str
|
235
|
+
The name of the independent variable for the channel kinetics e.g. 'v', 'cai'.
|
236
|
+
params : dict
|
237
|
+
A dictionary of the parameters of the channel kinetics and distribution.
|
238
|
+
range_params : dict
|
239
|
+
A dictionary of the range parameters of the channel kinetics added
|
240
|
+
under the RANGE statement in the MOD file.
|
241
|
+
temperature : float
|
242
|
+
The temperature in degrees Celsius.
|
243
|
+
"""
|
244
|
+
|
245
|
+
STANDARD_PARAMS = [
|
246
|
+
'vhalf', 'sigma', 'k', 'delta', 'tau0'
|
247
|
+
]
|
248
|
+
|
249
|
+
@staticmethod
|
250
|
+
def steady_state(v, vhalf, sigma):
|
251
|
+
"""
|
252
|
+
Compute the steady state value of the channel.
|
253
|
+
|
254
|
+
Parameters
|
255
|
+
----------
|
256
|
+
v : np.array
|
257
|
+
The voltage values to compute the steady state value for.
|
258
|
+
vhalf : float
|
259
|
+
The half-activation voltage.
|
260
|
+
sigma : float
|
261
|
+
The slope factor.
|
262
|
+
|
263
|
+
Returns
|
264
|
+
-------
|
265
|
+
np.array
|
266
|
+
The steady state value of the channel at the given voltage values.
|
267
|
+
"""
|
268
|
+
return 1 / (1 + np.exp(-(v - vhalf) / sigma))
|
269
|
+
|
270
|
+
def time_constant(self, v, vhalf, sigma, k, delta, tau0):
|
271
|
+
"""
|
272
|
+
Compute the time constant of the channel.
|
273
|
+
|
274
|
+
Parameters
|
275
|
+
----------
|
276
|
+
v : np.array
|
277
|
+
The voltage values to compute the time constant for.
|
278
|
+
vhalf : float
|
279
|
+
The half-activation voltage.
|
280
|
+
sigma : float
|
281
|
+
The slope factor.
|
282
|
+
k : float
|
283
|
+
The maximum rate parameter.
|
284
|
+
delta : float
|
285
|
+
The skew parameter of the time constant curve (unitless)
|
286
|
+
tau0 : float
|
287
|
+
The rate-limiting factor (minimum time constant)
|
288
|
+
|
289
|
+
Returns
|
290
|
+
-------
|
291
|
+
np.array
|
292
|
+
The time constant of the channel at the given voltage values.
|
293
|
+
"""
|
294
|
+
return 1 / (self.alpha_prime(v, vhalf, sigma, k, delta) + self.beta_prime(v, vhalf, sigma, k, delta)) + tau0
|
295
|
+
|
296
|
+
@staticmethod
|
297
|
+
def alpha_prime(v, vhalf, sigma, k, delta):
|
298
|
+
return k * np.exp(delta * (v - vhalf) / sigma)
|
299
|
+
|
300
|
+
@staticmethod
|
301
|
+
def beta_prime(v, vhalf, sigma, k, delta):
|
302
|
+
return k * np.exp(-(1 - delta) * (v - vhalf) / sigma)
|
303
|
+
|
304
|
+
@staticmethod
|
305
|
+
def t_adj(temperature, q10=2.3, reference_temp=23):
|
306
|
+
"""
|
307
|
+
Compute the temperature adjustment factor for the channel kinetics.
|
308
|
+
|
309
|
+
Parameters
|
310
|
+
----------
|
311
|
+
temperature : float
|
312
|
+
The temperature in degrees Celsius.
|
313
|
+
q10 : float, optional
|
314
|
+
The temperature coefficient. The default is 2.3.
|
315
|
+
reference_temp : float, optional
|
316
|
+
The reference temperature at which the channel kinetics were measured.
|
317
|
+
The default is 23.
|
318
|
+
|
319
|
+
Returns
|
320
|
+
-------
|
321
|
+
float
|
322
|
+
The temperature adjustment factor.
|
323
|
+
"""
|
324
|
+
return q10 ** ((temperature - reference_temp) / 10)
|
325
|
+
|
326
|
+
def compute_state(self, v, vhalf, sigma, k, delta, tau0, tadj=1):
|
327
|
+
"""
|
328
|
+
Compute the steady state value and time constant of the channel
|
329
|
+
for the given voltage values.
|
330
|
+
|
331
|
+
Parameters
|
332
|
+
----------
|
333
|
+
v : np.array
|
334
|
+
The voltage values to compute the channel kinetics for.
|
335
|
+
vhalf : float
|
336
|
+
The half-activation voltage.
|
337
|
+
sigma : float
|
338
|
+
The slope factor.
|
339
|
+
k : float
|
340
|
+
The maximum rate parameter.
|
341
|
+
delta : float
|
342
|
+
The skew parameter of the time constant curve (unitless)
|
343
|
+
tau0 : float
|
344
|
+
The rate-limiting factor (minimum time constant)
|
345
|
+
tadj : float, optional
|
346
|
+
The temperature adjustment factor. The default is 1.
|
347
|
+
|
348
|
+
Returns
|
349
|
+
-------
|
350
|
+
np.array
|
351
|
+
A list of steady state values and time constants for the channel.
|
352
|
+
"""
|
353
|
+
inf = self.steady_state(v, vhalf, sigma)
|
354
|
+
tau = self.time_constant(v, vhalf, sigma, k, delta, tau0) / tadj
|
355
|
+
return inf, tau
|
356
|
+
|
357
|
+
|
358
|
+
def __init__(self, name, state_powers, ion=None):
|
359
|
+
super().__init__(name)
|
360
|
+
|
361
|
+
self.ion = ion
|
362
|
+
self.independent_var_name = 'v'
|
363
|
+
|
364
|
+
self._state_powers = state_powers
|
365
|
+
|
366
|
+
# self.range_params = [f'{param}_{state}' for state in state_powers
|
367
|
+
# for param in self.STANDARD_PARAMS]
|
368
|
+
|
369
|
+
self.params = {
|
370
|
+
f'{param}_{state}': None
|
371
|
+
for state in state_powers
|
372
|
+
for param in self.STANDARD_PARAMS
|
373
|
+
}
|
374
|
+
self.params.update({
|
375
|
+
'gbar': 0.0,
|
376
|
+
})
|
377
|
+
self.range_params = self.params.copy()
|
378
|
+
|
379
|
+
self.temperature = 37
|
380
|
+
|
381
|
+
|
382
|
+
@property
|
383
|
+
def states(self):
|
384
|
+
"""
|
385
|
+
A list of state variable names of the channel.
|
386
|
+
"""
|
387
|
+
return [state for state in self._state_powers]
|
388
|
+
|
389
|
+
|
390
|
+
def compute_kinetic_variables(self, v):
|
391
|
+
"""
|
392
|
+
Compute the steady state values and time constants of the channel
|
393
|
+
for the given voltage values.
|
394
|
+
|
395
|
+
Parameters
|
396
|
+
----------
|
397
|
+
v : np.array
|
398
|
+
The voltage values to compute the channel kinetics for.
|
399
|
+
|
400
|
+
Returns
|
401
|
+
-------
|
402
|
+
list
|
403
|
+
A list of steady state values and time constants for each state
|
404
|
+
of the channel.
|
405
|
+
"""
|
406
|
+
|
407
|
+
results = []
|
408
|
+
|
409
|
+
for state in self.states:
|
410
|
+
|
411
|
+
vhalf = self.params[f'vhalf_{state}']
|
412
|
+
sigma = self.params[f'sigma_{state}']
|
413
|
+
k = self.params[f'k_{state}']
|
414
|
+
delta = self.params[f'delta_{state}']
|
415
|
+
tau0 = self.params[f'tau0_{state}']
|
416
|
+
|
417
|
+
inf = self.steady_state(v, vhalf, sigma)
|
418
|
+
tau = self.time_constant(v, vhalf, sigma, k, delta, tau0) / self.tadj
|
419
|
+
|
420
|
+
results.extend([inf, tau])
|
421
|
+
|
422
|
+
return results
|
423
|
+
|
424
|
+
|
425
|
+
def fit(self, data, prioritized_inf=True, round_params=3):
|
426
|
+
"""
|
427
|
+
Fit the standardized set of parameters of the model to the data
|
428
|
+
of the channel kinetics.
|
429
|
+
|
430
|
+
Parameters
|
431
|
+
----------
|
432
|
+
data : dict
|
433
|
+
A dictionary containing the data for the channel kinetics. The
|
434
|
+
dictionary should have the following structure:
|
435
|
+
{
|
436
|
+
'x': np.array, # The independent variable
|
437
|
+
'state1': {'inf': np.array, 'tau': np.array},
|
438
|
+
'state2': {'inf': np.array, 'tau': np.array},
|
439
|
+
...
|
440
|
+
}
|
441
|
+
prioritized_inf : bool, optional
|
442
|
+
Whether to prioritize the fit to the 'inf' data. If True, an
|
443
|
+
additional fit will be performed to the 'inf' data only. The
|
444
|
+
default is True.
|
445
|
+
round_params : int, optional
|
446
|
+
The number of decimal places to round the fitted parameters to.
|
447
|
+
The default is 3.
|
448
|
+
"""
|
449
|
+
from symfit import exp, variables, parameters, Model, Fit
|
450
|
+
|
451
|
+
x = data.pop('x')
|
452
|
+
|
453
|
+
for state, state_data in data.items():
|
454
|
+
v, inf, tau = variables('v, inf, tau')
|
455
|
+
initial_values = [1, 0.5, 0, 10, 0] if state_data['inf'][0] < state_data['inf'][-1] else [1, 0.5, 0, -10, 0]
|
456
|
+
k, delta, vhalf, sigma, tau0 = parameters('k, delta, vhalf, sigma, tau0', value=initial_values)
|
457
|
+
|
458
|
+
model = Model({
|
459
|
+
inf: 1 / (1 + exp(-(v - vhalf) / sigma)),
|
460
|
+
tau: 1 / (k * exp(delta * (v - vhalf) / sigma) + k * exp(-(1 - delta) * (v - vhalf) / sigma)) + tau0,
|
461
|
+
})
|
462
|
+
|
463
|
+
fit = Fit(model, v=x, inf=state_data['inf'], tau=state_data['tau'])
|
464
|
+
fit_result = fit.execute()
|
465
|
+
|
466
|
+
if prioritized_inf:
|
467
|
+
vhalf.value, sigma.value = fit_result.params['vhalf'], fit_result.params['sigma']
|
468
|
+
model_inf = Model({inf: 1 / (1 + exp(-(v - vhalf) / sigma))})
|
469
|
+
fit_inf = Fit(model_inf, v=x, inf=state_data['inf'])
|
470
|
+
fit_result_inf = fit_inf.execute()
|
471
|
+
fit_result.params.update(fit_result_inf.params)
|
472
|
+
|
473
|
+
if round_params:
|
474
|
+
fit_result.params = {key: round(value, round_params) for key, value in fit_result.params.items()}
|
475
|
+
|
476
|
+
for param in ['k', 'delta', 'tau0', 'vhalf', 'sigma']:
|
477
|
+
self.params[f'{param}_{state}'] = fit_result.params[param]
|
478
|
+
|
479
|
+
self.range_params = self.params.copy()
|
480
|
+
|
481
|
+
|
482
|
+
def to_dict(self):
|
483
|
+
"""
|
484
|
+
Return the mechanism as a dictionary.
|
485
|
+
"""
|
486
|
+
return {
|
487
|
+
'suffix': self.name,
|
488
|
+
'ion': self.ion,
|
489
|
+
'range_params': [
|
490
|
+
(param, self.params[param], get_unit(param))
|
491
|
+
for param in self.params
|
492
|
+
],
|
493
|
+
'state_vars': {
|
494
|
+
var: power for var, power in self._state_powers.items()
|
495
|
+
},
|
496
|
+
}
|
497
|
+
|
498
|
+
@staticmethod
|
499
|
+
def get_unit(param):
|
500
|
+
"""
|
501
|
+
Get the unit of a parameter based on its name.
|
502
|
+
|
503
|
+
Parameters
|
504
|
+
----------
|
505
|
+
param : str
|
506
|
+
The name of the parameter.
|
507
|
+
|
508
|
+
Returns
|
509
|
+
-------
|
510
|
+
str
|
511
|
+
The unit of the parameter.
|
512
|
+
"""
|
513
|
+
if param.startswith('vhalf_'): return 'mV'
|
514
|
+
elif param.startswith('sigma_'): return 'mV'
|
515
|
+
elif param.startswith('k_'): return '1/ms'
|
516
|
+
elif param.startswith('delta_'): return '1'
|
517
|
+
elif param.startswith('tau0_'): return 'ms'
|
518
|
+
|
519
|
+
|
520
|
+
class LeakChannel(Mechanism):
|
521
|
+
"""
|
522
|
+
A class representing a leak channel in a neuron model.
|
523
|
+
|
524
|
+
Parameters
|
525
|
+
----------
|
526
|
+
name : str
|
527
|
+
The name of the channel.
|
528
|
+
|
529
|
+
Attributes
|
530
|
+
----------
|
531
|
+
params : dict
|
532
|
+
A dictionary of the parameters of the channel kinetics and distribution.
|
533
|
+
range_params : dict
|
534
|
+
A dictionary of the range parameters of the channel kinetics added
|
535
|
+
under the RANGE statement in the MOD file.
|
536
|
+
"""
|
537
|
+
|
538
|
+
def __init__(self):
|
539
|
+
super().__init__(name='Leak')
|
540
|
+
self.params = {'gbar': 0.0, 'e': -70}
|
541
|
+
self.range_params = {'gbar': 0.0, 'e': -70}
|
542
|
+
|
543
|
+
|
544
|
+
class CaDynamics(Mechanism):
|
545
|
+
"""
|
546
|
+
A class representing a calcium dynamics mechanism in a neuron model.
|
547
|
+
|
548
|
+
Attributes
|
549
|
+
----------
|
550
|
+
params : dict
|
551
|
+
A dictionary of the parameters of the calcium dynamics mechanism.
|
552
|
+
range_params : dict
|
553
|
+
A dictionary of the range parameters of the calcium dynamics mechanism
|
554
|
+
added under the RANGE statement in the MOD file.
|
555
|
+
"""
|
556
|
+
|
557
|
+
def __init__(self):
|
558
|
+
super().__init__('CaDyn')
|
559
|
+
self.params = {
|
560
|
+
'depth': 0.1, # um: Depth of calcium shell
|
561
|
+
'taur': 80, # ms: Time constant for calcium removal
|
562
|
+
'cainf': 1e-4, # mM: Steady-state calcium concentration
|
563
|
+
'gamma': 0.05,
|
564
|
+
'kt': 0.0,
|
565
|
+
'kd': 0.0
|
566
|
+
}
|
567
|
+
self.range_params = {
|
568
|
+
'depth': 0.1,
|
569
|
+
'taur': 80,
|
570
|
+
'cainf': 1e-4,
|
571
|
+
'gamma': 0.05,
|
572
|
+
'kt': 0.0,
|
573
|
+
'kd': 0.0
|
574
|
+
}
|