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.
Files changed (56) hide show
  1. dendrotweaks/__init__.py +10 -0
  2. dendrotweaks/analysis/__init__.py +11 -0
  3. dendrotweaks/analysis/ephys_analysis.py +482 -0
  4. dendrotweaks/analysis/morphometric_analysis.py +106 -0
  5. dendrotweaks/membrane/__init__.py +6 -0
  6. dendrotweaks/membrane/default_mod/AMPA.mod +65 -0
  7. dendrotweaks/membrane/default_mod/AMPA_NMDA.mod +100 -0
  8. dendrotweaks/membrane/default_mod/CaDyn.mod +54 -0
  9. dendrotweaks/membrane/default_mod/GABAa.mod +65 -0
  10. dendrotweaks/membrane/default_mod/Leak.mod +27 -0
  11. dendrotweaks/membrane/default_mod/NMDA.mod +72 -0
  12. dendrotweaks/membrane/default_mod/vecstim.mod +76 -0
  13. dendrotweaks/membrane/default_templates/NEURON_template.py +354 -0
  14. dendrotweaks/membrane/default_templates/default.py +73 -0
  15. dendrotweaks/membrane/default_templates/standard_channel.mod +87 -0
  16. dendrotweaks/membrane/default_templates/template_jaxley.py +108 -0
  17. dendrotweaks/membrane/default_templates/template_jaxley_new.py +108 -0
  18. dendrotweaks/membrane/distributions.py +324 -0
  19. dendrotweaks/membrane/groups.py +103 -0
  20. dendrotweaks/membrane/io/__init__.py +11 -0
  21. dendrotweaks/membrane/io/ast.py +201 -0
  22. dendrotweaks/membrane/io/code_generators.py +312 -0
  23. dendrotweaks/membrane/io/converter.py +108 -0
  24. dendrotweaks/membrane/io/factories.py +144 -0
  25. dendrotweaks/membrane/io/grammar.py +417 -0
  26. dendrotweaks/membrane/io/loader.py +90 -0
  27. dendrotweaks/membrane/io/parser.py +499 -0
  28. dendrotweaks/membrane/io/reader.py +212 -0
  29. dendrotweaks/membrane/mechanisms.py +574 -0
  30. dendrotweaks/model.py +1916 -0
  31. dendrotweaks/model_io.py +75 -0
  32. dendrotweaks/morphology/__init__.py +5 -0
  33. dendrotweaks/morphology/domains.py +100 -0
  34. dendrotweaks/morphology/io/__init__.py +5 -0
  35. dendrotweaks/morphology/io/factories.py +212 -0
  36. dendrotweaks/morphology/io/reader.py +66 -0
  37. dendrotweaks/morphology/io/validation.py +212 -0
  38. dendrotweaks/morphology/point_trees.py +681 -0
  39. dendrotweaks/morphology/reduce/__init__.py +16 -0
  40. dendrotweaks/morphology/reduce/reduce.py +155 -0
  41. dendrotweaks/morphology/reduce/reduced_cylinder.py +129 -0
  42. dendrotweaks/morphology/sec_trees.py +1112 -0
  43. dendrotweaks/morphology/seg_trees.py +157 -0
  44. dendrotweaks/morphology/trees.py +567 -0
  45. dendrotweaks/path_manager.py +261 -0
  46. dendrotweaks/simulators.py +235 -0
  47. dendrotweaks/stimuli/__init__.py +3 -0
  48. dendrotweaks/stimuli/iclamps.py +73 -0
  49. dendrotweaks/stimuli/populations.py +265 -0
  50. dendrotweaks/stimuli/synapses.py +203 -0
  51. dendrotweaks/utils.py +239 -0
  52. dendrotweaks-0.3.1.dist-info/METADATA +70 -0
  53. dendrotweaks-0.3.1.dist-info/RECORD +56 -0
  54. dendrotweaks-0.3.1.dist-info/WHEEL +5 -0
  55. dendrotweaks-0.3.1.dist-info/licenses/LICENSE +674 -0
  56. dendrotweaks-0.3.1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,354 @@
1
+ """
2
+ This file was automatically generated by the DendroTweaks toolbox (https://dendrotweaks.dendrites.gr/).
3
+ It provides a plain (Python) NEURON code representation of a biophysical model of a single neuron
4
+ built using DendroTweaks, which can be run independently of DendroTweaks.
5
+
6
+ Classes:
7
+ Cell: Represents a biophysical NEURON model of a single neuron with methods to:
8
+ - Load morphology from an SWC file.
9
+ - Distribute passive properties.
10
+ - Insert ion channel mechanisms.
11
+ - Distribute parameters across the neuron.
12
+ - Set the number of segments for each section.
13
+ - Add stimuli and recordings.
14
+
15
+ Functions:
16
+ load_mechanisms: Load NEURON mechanisms from a specified directory.
17
+ init_simulation: Initialize a NEURON simulation with specified parameters.
18
+ run: Run a NEURON simulation for a given duration.
19
+ get_domain: Retrieve the domain of a given segment.
20
+ linear: Compute a linear function based on distance.
21
+ sinusoidal: Compute a sinusoidal function based on distance.
22
+ exponential: Compute an exponential function based on distance.
23
+ sigmoid: Compute a sigmoid function based on distance.
24
+ gaussian: Compute a Gaussian function based on distance.
25
+ step: Compute a step function based on distance.
26
+ inherit: Inherit a parameter value from the parent segment.
27
+ """
28
+
29
+ import os
30
+
31
+ import numpy as np
32
+ from numpy import polyval
33
+
34
+ import neuron
35
+ from neuron import h
36
+ h.load_file('stdrun.hoc')
37
+
38
+
39
+ class Cell():
40
+ """
41
+ A class representing a biophysical NEURON model of a single neuron.
42
+ """
43
+
44
+ def __init__(self, path_to_swc_file: str):
45
+ self.name = os.path.basename(path_to_swc_file).replace('.swc', '')
46
+ self.load_morphology(path_to_swc_file)
47
+ self.distribute_passive()
48
+ self.set_geom_nseg()
49
+ self.insert_mechanisms()
50
+ self.distribute_parameters()
51
+
52
+ @property
53
+ def all_segments(self):
54
+ return [seg for sec in self.all for seg in sec]
55
+
56
+ ### Morphology methods ###
57
+
58
+ def load_morphology(self, path_to_swc_file: str) -> None:
59
+ if path_to_swc_file.endswith('.swc'):
60
+ self._load_swc(path_to_swc_file)
61
+ elif path_to_swc_file.endswith('.asc'):
62
+ self._load_asc(path_to_swc_file)
63
+ else:
64
+ raise ValueError(f"File type not supported: {path_to_swc_file}")
65
+
66
+ def _load_swc(self, path_to_swc_file: str) -> None:
67
+ h.load_file('import3d.hoc')
68
+ swc_importer = h.Import3d_SWC_read()
69
+ swc_importer.input(path_to_swc_file)
70
+ imported_cell = h.Import3d_GUI(swc_importer, False)
71
+ imported_cell.instantiate(self)
72
+
73
+ def _load_asc(self, path_to_asc_file: str) -> None:
74
+ h.load_file('import3d.hoc')
75
+ asc_importer = h.Import3d_Neurolucida3()
76
+ asc_importer.input(path_to_asc_file)
77
+ imported_cell = h.Import3d_GUI(asc_importer, False)
78
+ imported_cell.instantiate(self)
79
+
80
+ def distance(self, seg, from_seg=None):
81
+ if from_seg is None:
82
+ from_seg = self.soma[0](0.5)
83
+ return h.distance(from_seg, seg)
84
+
85
+ def domain_distance(self, seg):
86
+ parent = self._find_parent_with_different_domain(seg.sec)
87
+ if parent:
88
+ return h.distance(parent(1), seg)
89
+ return 0
90
+
91
+ def _find_parent_with_different_domain(self, sec):
92
+ parentseg = sec.parentseg()
93
+ if not parentseg:
94
+ return None
95
+ parent = parentseg.sec
96
+ while parent:
97
+ if get_domain(parent(0.5)) != get_domain(sec(0.5)):
98
+ return parent
99
+ parentseg = parent.parentseg()
100
+ if not parentseg:
101
+ return None
102
+ parent = parentseg.sec
103
+ return None
104
+
105
+ ### Segmentation methods ###
106
+
107
+ def set_geom_nseg(self, d_lambda:float=0.1, f:float=100):
108
+ for sec in self.all:
109
+ sec.nseg = int((sec.L/(d_lambda*h.lambda_f(f, sec=sec)) + 0.9)/2)*2 + 1
110
+
111
+ ### Mechanism methods ###
112
+
113
+ def insert_mechanisms(self):
114
+ {% for domain, mechanisms in domains_to_mechs.items() %}
115
+ for sec in self.{{ domains_to_NEURON[domain] }}:
116
+ {% for mechanism in mechanisms %}
117
+ sec.insert('{{ mechanism }}')
118
+ {%- endfor %}
119
+ {% endfor %}
120
+
121
+ ### Parameter distribution methods ###
122
+
123
+ def set_param(self, seg, param: str, mech: str, value: float) -> None:
124
+ if param == 'Ra':
125
+ setattr(seg.sec, param, value)
126
+ if param == 'cm':
127
+ setattr(seg, param, value)
128
+ else:
129
+ if seg.sec.has_membrane(mech):
130
+ setattr(seg, param, value)
131
+ else:
132
+ if param in ['ena', 'ek', 'eca']:
133
+ if hasattr(seg, param):
134
+ setattr(seg, param, value)
135
+
136
+ def distribute_passive(self):
137
+
138
+ for seg in self.all_segments:
139
+
140
+ domain = get_domain(seg)
141
+ distance = self.distance(seg)
142
+ domain_distance = self.domain_distance(seg)
143
+ diam = seg.diam
144
+ section_diam = seg.sec.diam
145
+
146
+ {% for param, mech in params_to_mechs.items() -%}
147
+ {% if param in ['cm', 'Ra']%}
148
+ {% set groups = param_dict[param] -%}
149
+ {% for group_name, distribution in groups.items() -%}
150
+ {% set group = groups_dict[group_name] -%}
151
+ if domain in {{ params_to_valid_domains[param][group_name] }}:
152
+ {% if group.select_by -%}
153
+ {% set min_val = group.min_value if group.min_value is not none else 0 -%}
154
+ {% if group.max_value is not none -%}
155
+ if {{ min_val }} < {{ group.select_by }} <= {{ group.max_value }}:
156
+ {% else -%}
157
+ if {{ min_val }} < {{ group.select_by }}:
158
+ {% endif %}
159
+ {% if distribution.function_name == "constant" -%}
160
+ self.set_param(seg, "{{ param }}", "{{ mech }}", {{ distribution.parameters.value }})
161
+ {% elif distribution.function_name == "linear" -%}
162
+ self.set_param(seg, "{{ param }}", "{{ mech }}", linear(distance, slope={{ distribution.parameters.slope }}, intercept={{ distribution.parameters.intercept }}))
163
+ {% elif distribution.function_name == "polynomial" -%}
164
+ self.set_param(seg, "{{ param }}", "{{ mech }}", polyval([{% for coeff in distribution.parameters.coeffs %}{{ coeff }}{% if not loop.last %}, {% endif %}{% endfor %}], distance))
165
+ {% else -%}
166
+ self.set_param(seg, "{{ param }}", "{{ mech }}", {{ distribution.function_name }}(distance, **{{ distribution.parameters }}))
167
+ {% endif %}
168
+ {% else -%}
169
+ {% if distribution.function_name == "constant" -%}
170
+ self.set_param(seg, "{{ param }}", "{{ mech }}", {{ distribution.parameters.value }})
171
+ {% elif distribution.function_name == "linear" -%}
172
+ self.set_param(seg, "{{ param }}", "{{ mech }}", linear(distance, slope={{ distribution.parameters.slope }}, intercept={{ distribution.parameters.intercept }}))
173
+ {% elif distribution.function_name == "polynomial" -%}
174
+ self.set_param(seg, "{{ param }}", "{{ mech }}", polyval([{% for coeff in distribution.parameters.coeffs %}{{ coeff }}{% if not loop.last %}, {% endif %}{% endfor %}], distance))
175
+ {% else -%}
176
+ self.set_param(seg, "{{ param }}", "{{ mech }}", {{ distribution.function_name }}(distance, **{{ distribution.parameters }}))
177
+ {% endif %}
178
+ {% endif %}
179
+ {% endfor -%}
180
+ {% endif -%}
181
+ {% endfor %}
182
+
183
+ def distribute_parameters(self):
184
+
185
+ for seg in self.all_segments:
186
+
187
+ domain = get_domain(seg)
188
+ distance = self.distance(seg)
189
+ domain_distance = self.domain_distance(seg)
190
+ diam = seg.diam
191
+ section_diam = seg.sec.diam
192
+
193
+ {% for param, mech in params_to_mechs.items() -%}
194
+ {% if param not in ['cm', 'Ra']%}
195
+ {% set groups = param_dict[param] -%}
196
+ {% for group_name, distribution in groups.items() -%}
197
+ {% set group = groups_dict[group_name] -%}
198
+ if domain in {{ params_to_valid_domains[param][group_name] }}:
199
+ {% if group.select_by -%}
200
+ {% set min_val = group.min_value if group.min_value is not none else 0 -%}
201
+ {% if group.max_value is not none -%}
202
+ if {{ min_val }} < {{ group.select_by }} <= {{ group.max_value }}:
203
+ {% else -%}
204
+ if {{ min_val }} < {{ group.select_by }}:
205
+ {% endif %}
206
+ {% if distribution.function_name == "constant" -%}
207
+ self.set_param(seg, "{{ param }}", "{{ mech }}", {{ distribution.parameters.value }})
208
+ {% elif distribution.function_name == "linear" -%}
209
+ self.set_param(seg, "{{ param }}", "{{ mech }}", linear(distance, slope={{ distribution.parameters.slope }}, intercept={{ distribution.parameters.intercept }}))
210
+ {% elif distribution.function_name == "polynomial" -%}
211
+ self.set_param(seg, "{{ param }}", "{{ mech }}", polyval([{% for coeff in distribution.parameters.coeffs %}{{ coeff }}{% if not loop.last %}, {% endif %}{% endfor %}], distance))
212
+ {% else -%}
213
+ self.set_param(seg, "{{ param }}", "{{ mech }}", {{ distribution.function_name }}(distance, **{{ distribution.parameters }}))
214
+ {% endif %}
215
+ {% else -%}
216
+ {% if distribution.function_name == "constant" -%}
217
+ self.set_param(seg, "{{ param }}", "{{ mech }}", {{ distribution.parameters.value }})
218
+ {% elif distribution.function_name == "linear" -%}
219
+ self.set_param(seg, "{{ param }}", "{{ mech }}", linear(distance, slope={{ distribution.parameters.slope }}, intercept={{ distribution.parameters.intercept }}))
220
+ {% elif distribution.function_name == "polynomial" -%}
221
+ self.set_param(seg, "{{ param }}", "{{ mech }}", polyval([{% for coeff in distribution.parameters.coeffs %}{{ coeff }}{% if not loop.last %}, {% endif %}{% endfor %}], distance))
222
+ {% else -%}
223
+ self.set_param(seg, "{{ param }}", "{{ mech }}", {{ distribution.function_name }}(distance, **{{ distribution.parameters }}))
224
+ {% endif %}
225
+ {% endif %}
226
+ {% endfor -%}
227
+ {% endif -%}
228
+ {% endfor %}
229
+
230
+ ### Stimulation methods ###
231
+
232
+ def add_stimuli(self):
233
+ self.add_iclamps()
234
+ self.add_synapses()
235
+
236
+ def add_recordings(self):
237
+ recordings = []
238
+ {% for seg, rec in recordings.items() %}
239
+ rec = h.Vector()
240
+ rec.record(self.{{seg._section.domain}}[{{seg._section.domain_idx}}]({{seg.x}})._ref_v)
241
+ recordings.append(rec)
242
+ {% endfor %}
243
+ return recordings
244
+
245
+ def add_iclamps(self):
246
+ iclamps = []
247
+ {% for seg, iclamp in iclamps.items() %}
248
+ iclamp = h.IClamp(self.{{seg._section.domain}}[{{seg._section.domain_idx}}]({{seg.x}}))
249
+ iclamp.delay = {{ iclamp.delay }}
250
+ iclamp.dur = {{ iclamp.dur }}
251
+ iclamp.amp = {{ iclamp.amp }}
252
+ iclamps.append(iclamp)
253
+ {% endfor %}
254
+ return iclamps
255
+
256
+
257
+ # =================================================================================================
258
+ # SIMULATION FUNCTIONS
259
+ # =================================================================================================
260
+
261
+ def load_mechanisms(path_to_mod: str, recompile:bool=False) -> None:
262
+ """
263
+ Load NEURON mechanisms from a directory.
264
+
265
+ Parameters
266
+ ----------
267
+ path_to_mod : str
268
+ The path to the directory containing the mod files.
269
+ recompile : bool
270
+ Whether to recompile the mod files before loading
271
+ """
272
+
273
+ if recompile:
274
+ cwd = os.getcwd()
275
+ os.chdir(path_to_mod)
276
+ os.system('nrnivmodl')
277
+ os.chdir(cwd)
278
+ print(f'Compiled mod files in "{path_to_mod}"')
279
+
280
+ neuron.load_mechanisms(path_to_mod)
281
+ print(f'Loaded mod files from "{path_to_mod}"')
282
+
283
+ def init_simulation(temperature:float=37, dt:float=0.025, v_init:float=-65, cvode:bool=False) -> None:
284
+ """
285
+ Initialize a NEURON simulation.
286
+
287
+ Parameters
288
+ ----------
289
+ temperature : float
290
+ The temperature in degrees Celsius.
291
+ dt : float
292
+ The time step of the simulation in ms.
293
+ v_init : float
294
+ The initial membrane potential in mV.
295
+ cvode : bool
296
+ Whether to use the CVode variable time step integrator.
297
+ """
298
+ h.CVode().active(cvode)
299
+ h.celsius = temperature
300
+ h.dt = dt
301
+ h.stdinit()
302
+ h.init()
303
+ h.finitialize(v_init)
304
+ if h.cvode.active():
305
+ h.cvode.re_init()
306
+ else:
307
+ h.fcurrent()
308
+ h.frecord_init()
309
+
310
+ def run(duration:float=300, **kwargs) -> None:
311
+ """
312
+ Run a NEURON simulation.
313
+
314
+ Parameters
315
+ ----------
316
+ duration : float
317
+ The duration of the simulation in ms.
318
+ """
319
+ init_simulation(**kwargs)
320
+ h.continuerun(duration)
321
+
322
+ # =================================================================================================
323
+ # HELPER FUNCTIONS
324
+ # =================================================================================================
325
+
326
+ def get_domain(seg) -> str:
327
+ sec = seg.sec
328
+ sec_name = sec.name()
329
+ domain = sec_name.split('.')[-1].split('[')[0]
330
+ return domain
331
+
332
+ def linear(distance: float, slope:float = 0, intercept: float = 0) -> float:
333
+ return slope * distance + intercept
334
+
335
+ def sinusoidal(distance: float, amplitude: float = 0, frequency: float = 1, phase: float = 0) -> float:
336
+ return amplitude * np.sin(frequency * distance + phase)
337
+
338
+ def exponential(distance: float, vertical_shift:float = 0, scale_factor: float =1, growth_rate: float=1, horizontal_shift: float = 0) -> float:
339
+ return vertical_shift + scale_factor * np.exp(growth_rate * (distance - horizontal_shift))
340
+
341
+ def sigmoid(distance: float, vertical_shift: float=0, scale_factor: float=1, growth_rate: float=1, horizontal_shift: float=0) -> float:
342
+ return vertical_shift + scale_factor / (1 + np.exp(-growth_rate*(distance - horizontal_shift)))
343
+
344
+ def gaussian(distance: float, amplitude: float, mean: float, std: float) -> float:
345
+ return amplitude * np.exp(-((distance - mean) ** 2) / (2 * std ** 2))
346
+
347
+ def step(distance: float, max_value: float, min_value: float, start: float, end: float) -> float:
348
+ if start < distance < end:
349
+ return max_value
350
+ else:
351
+ return min_value
352
+
353
+ def inherit(seg, param: str) -> float:
354
+ return getattr(seg.sec.parentseg(), param, np.nan)
@@ -0,0 +1,73 @@
1
+ # This Python channel class was automatically generated from a MOD file
2
+ # using DendroTweaks toolbox, dendrotweaks.dendrites.gr
3
+
4
+ import sys
5
+
6
+ from dendrotweaks.membrane.mechanisms import IonChannel
7
+ import numpy as np
8
+
9
+ class {{ class_name }}(IonChannel):
10
+ """
11
+ {{ title }}
12
+ """
13
+
14
+ def __init__(self, name="{{ class_name }}"):
15
+ super().__init__(name=name)
16
+ self.params = {
17
+ {% for param, value in channel_params.items() -%}
18
+ "{{ param }}": {{ value }}
19
+ {%- if not loop.last -%},
20
+ {%- endif %}
21
+ {% endfor -%}
22
+ }
23
+ self.range_params = {
24
+ {% for param, value in range_params.items() -%}
25
+ "{{ param }}": {{ value }}
26
+ {%- if not loop.last -%},
27
+ {%- endif %}
28
+ {% endfor -%}
29
+ }
30
+ self.states = {
31
+ {% for state in state_vars -%}
32
+ "{{ state }}": 0.0
33
+ {%- if not loop.last %},
34
+ {%- endif %}
35
+ {% endfor -%}
36
+ }
37
+ self._state_powers = {
38
+ {% for state, power in state_vars.items() -%}
39
+ "{{ state }}": {{ power }}
40
+ {%- if not loop.last %},
41
+ {%- endif %}
42
+ {% endfor -%}
43
+ }
44
+ self.ion = "{{ ion }}"
45
+ self.current_name = "i_{{ ion }}"
46
+ self.independent_var_name = "{{ independent_var_name }}"
47
+ self.temperature = 37
48
+
49
+ def __getitem__(self, item):
50
+ return self.params[item]
51
+
52
+ def __setitem__(self, item, value):
53
+ self.params[item] = value
54
+
55
+ {% for procedure in procedures %}
56
+ {{ procedure['signature'] }}
57
+ {% for param in procedure['params'] -%}
58
+ {{ param }} = self.params["{{ param }}"]
59
+ {% endfor %}
60
+ {{ procedure['body'] }}
61
+ {%- if not loop.last %}
62
+ {% endif %}{% endfor %}
63
+
64
+ {% for function in functions %}
65
+ {{ function['signature'] }}
66
+ {% for param in function['params'] -%}
67
+ {{ param }} = self.params["{{ param }}"]
68
+ {% endfor %}
69
+ {{ function['body'] }}
70
+ {%- if not loop.last %}
71
+ {% endif %}{% endfor -%}
72
+
73
+
@@ -0,0 +1,87 @@
1
+ TITLE standardized {{ suffix }} channel
2
+
3
+ COMMENT
4
+ Standardized and templated by DendroTweaks.
5
+ This NMODL file defines a model for a {{ ion }} ion channel.
6
+ ENDCOMMENT
7
+
8
+ NEURON {
9
+ SUFFIX {{ suffix }}
10
+ {% if ion is none %}
11
+ NONSPECIFIC_CURRENT i
12
+ {% else %}
13
+ USEION {{ ion }} READ e{{ ion }} WRITE i{{ ion }}
14
+ {% endif %}
15
+ RANGE gbar, i{% for param, _, _ in range_params %}, {{ param }}{% endfor %}
16
+ }
17
+
18
+ UNITS {
19
+ (mA) = (milliamp)
20
+ (mV) = (millivolt)
21
+ (S) = (siemens)
22
+ (um) = (micron)
23
+ }
24
+
25
+ PARAMETER {
26
+ {% for key, value, unit in range_params %}{{ "%-7s"|format(key) }} = {{ value }} ({{ unit }}){% if not loop.last %}
27
+ {% endif %}{% endfor %}
28
+ }
29
+
30
+ ASSIGNED {
31
+ v (mV) : membrane voltage
32
+ i (mA/cm2) : current density
33
+ i{{ "%-7s"|format(ion) }} (mA/cm2) : current density of {{ ion }} ion
34
+ g{{ "%-7s"|format(ion) }} (S/cm2) : conductance of {{ ion }} ion
35
+ e{{ "%-7s"|format(ion) }} (mV) : reversal potential of {{ ion }} ion
36
+ {% for state in state_vars %}
37
+ {{ state }}_inf (1) : steady state value of {{ state }}
38
+ tau_{{ state }} (ms) : time constant of {{ state }}
39
+ {% endfor %}
40
+ tadj (1) : temperature adjustment factor
41
+ celsius (degC) : simulation temperature in celsius
42
+ }
43
+
44
+ STATE { {% for variable in state_vars %}{{ variable }}{% if not loop.last %} {% endif %}{% endfor %} }
45
+
46
+ BREAKPOINT {
47
+ SOLVE states METHOD cnexp
48
+ g{{ ion }} = tadj * gbar{% for state, power in state_vars.items() %} * {% if power > 1 %}pow({{ state }}, {{ power }}){% else %}{{ state }}{% endif %}{% endfor %}
49
+ i = g{{ ion }} * (v - e{{ ion }})
50
+ i{{ ion }} = i
51
+ }
52
+
53
+ DERIVATIVE states {
54
+ rates(v)
55
+ {% for state in state_vars %}{{ state }}' = ({{ state }}_inf - {{ state }}) / tau_{{ state }}
56
+ {% endfor %}
57
+ }
58
+
59
+ INITIAL {
60
+ tadj = q10^((celsius - temp)/10(degC))
61
+ rates(v)
62
+ {% for state in state_vars %}{{ state }} = {{ state }}_inf
63
+ {% endfor %}
64
+ }
65
+
66
+
67
+ FUNCTION alpha_prime(v (mV), k (1/ms), delta (1), vhalf (mV), sigma (mV)) (1/ms) {
68
+ alpha_prime = k * exp(delta * (v - vhalf) / sigma)
69
+ }
70
+
71
+ FUNCTION beta_prime(v (mV), k (1/ms), delta (1), vhalf (mV), sigma (mV)) (1/ms) {
72
+ beta_prime = k * exp(-(1 - delta) * (v - vhalf) / sigma)
73
+ }
74
+
75
+ PROCEDURE rates(v(mV)) {
76
+ LOCAL {% for state in state_vars %}alpha_{{ state }}, beta_{{ state }}{% if not loop.last %}, {% endif %}{% endfor %}
77
+
78
+ {% for state in state_vars %}
79
+ {{ state }}_inf = 1 / (1 + exp(-(v - vhalf_{{ state }}) / sigma_{{ state }}))
80
+ alpha_{{ state }} = alpha_prime(v, k_{{ state }}, delta_{{ state }}, vhalf_{{ state }}, sigma_{{ state }})
81
+ beta_{{ state }} = beta_prime(v, k_{{ state }}, delta_{{ state }}, vhalf_{{ state }}, sigma_{{ state }})
82
+ tau_{{ state }} = (1 / (alpha_{{ state }} + beta_{{ state }}) + tau0_{{ state }}) / tadj
83
+ {% endfor %}
84
+
85
+ }
86
+
87
+
@@ -0,0 +1,108 @@
1
+ # This Python channel class was automatically generated from a MOD file
2
+ # using DendroTweaks toolbox, dendrotweaks.dendrites.gr
3
+
4
+ from jaxley.channels import Channel
5
+ from jaxley.solver_gate import exponential_euler
6
+ import jax.numpy as jn
7
+
8
+ class {{ class_name }}(Channel):
9
+ """
10
+ {{ title }}
11
+ """
12
+
13
+ def __init__(self, name="{{ class_name }}"):
14
+ super().__init__(name=name)
15
+ self.channel_params = {
16
+ {% for param, value in channel_params.items() -%}
17
+ "{{ class_name }}_{{ param }}": {{ value }}
18
+ {%- if not loop.last -%},
19
+ {%- endif %}
20
+ {% endfor -%}
21
+ }
22
+ self.channel_states = {
23
+ {% for state in state_vars -%}
24
+ "{{class_name}}_{{ state }}": 0.0
25
+ {%- if not loop.last %},
26
+ {%- endif %}
27
+ {% endfor -%}
28
+ }
29
+ self._state_powers = {
30
+ {% for state, power in state_vars.items() -%}
31
+ "{{class_name}}_{{ state }}": {{ power }}
32
+ {%- if not loop.last %},
33
+ {%- endif %}
34
+ {% endfor -%}
35
+ }
36
+ self.ion = "{{ ion }}"
37
+ self.current_name = "i_{{ ion }}"
38
+
39
+ self.independent_var_name = "{{ independent_var_name }}"
40
+
41
+ # @property
42
+ # def tadj(self):
43
+ # return self.tadj = q10 ** ((celsius - temp) / 10)
44
+
45
+ def __getitem__(self, item):
46
+ return self.channel_params[item]
47
+
48
+ def __setitem__(self, item, value):
49
+ self.channel_params[item] = value
50
+
51
+ {% for function in functions %}
52
+ {{ function['signature'] }}
53
+ {%- for param in function['params'] -%}
54
+ {{ param }} = self.channel_params.get("{{ class_name }}_{{ param }}", 1)
55
+ {% endfor %}
56
+ {{ function['body'] }}
57
+ {% if not loop.last %}
58
+ {% endif %}{% endfor -%}
59
+ {% for procedure in procedures %}
60
+ {{ procedure['signature'] }}
61
+ {% for param in procedure['params'] -%}
62
+ {{ param }} = self.channel_params.get("{{ class_name }}_{{ param }}", 1)
63
+ {% endfor %}
64
+ {{ procedure['body'] }}
65
+ {%- if not loop.last %}
66
+ {% endif %}{% endfor %}
67
+
68
+ def update_states(self, states, dt, v, params):
69
+ {% for state, state_params in state_vars.items() -%}
70
+ {{state}} = states['{{class_name}}_{{state}}']
71
+ {%- if not loop.last %}
72
+ {%- endif %}
73
+ {% endfor -%}
74
+ {{- procedure_calls}}
75
+ {% for state in state_vars.keys() %}new_{{state}} = exponential_euler({{state}}, dt, {{state}}Inf, {{state}}Tau){% if not loop.last %}
76
+ {% endif %}{% endfor %}
77
+ return {
78
+ {% for state in state_vars -%}
79
+ "{{class_name}}_{{state}}": new_{{state}}
80
+ {%- if not loop.last %},
81
+ {%- endif %}
82
+ {% endfor -%}
83
+ }
84
+
85
+ def compute_current(self, states, v, params):
86
+ {% for state in state_vars.keys() -%}
87
+ {{state}} = states['{{class_name}}_{{state}}']
88
+ {%- if not loop.last %}
89
+ {%- endif %}
90
+ {% endfor -%}
91
+ gbar = params["{{class_name}}_gbar"]
92
+ # E = params["E_{{ ion }}"]
93
+ E = {{ E_ion }}
94
+ {{ procedure_calls}}
95
+ g = self.tadj * gbar *{% for state, power in state_vars.items()%} {{state}}**{{power}} {% if not loop.last %}*{% endif %}{% endfor %}* 1000
96
+ return g * (v - E)
97
+
98
+ def init_state(self, states, v, params, delta_t):
99
+ {{ procedure_calls}}
100
+ return {
101
+ {% for state in state_vars.keys() -%}
102
+ "{{class_name}}_{{state}}": {{state}}Inf
103
+ {%- if not loop.last %},
104
+ {%- endif %}
105
+ {% endfor -%}
106
+ }
107
+
108
+